]> git.tdb.fi Git - libs/gl.git/blob - source/framebuffer.cpp
Rework exceptions
[libs/gl.git] / source / framebuffer.cpp
1 #include "error.h"
2 #include "extension.h"
3 #include "ext_framebuffer_blit.h"
4 #include "ext_framebuffer_object.h"
5 #include "framebuffer.h"
6 #include "misc.h"
7 #include "renderbuffer.h"
8 #include "texture2d.h"
9
10 using namespace std;
11
12 namespace Msp {
13 namespace GL {
14
15 Framebuffer::Framebuffer(unsigned i):
16         id(i),
17         dirty(0)
18 {
19         if(id)
20                 throw invalid_argument("System framebuffer must have id 0");
21
22         int viewport[4];
23         glGetIntegerv(GL_VIEWPORT, viewport);
24         width = viewport[2];
25         height = viewport[3];
26 }
27
28 Framebuffer::Framebuffer():
29         width(0),
30         height(0),
31         dirty(0)
32 {
33         static RequireExtension _ext("GL_EXT_framebuffer_object");
34
35         glGenFramebuffersEXT(1, &id);
36 }
37
38 Framebuffer::~Framebuffer()
39 {
40         if(id)
41                 glDeleteFramebuffersEXT(1, &id);
42         if(current()==this)
43                 unbind();
44 }
45
46 void Framebuffer::update_attachment(unsigned mask) const
47 {
48         if(current()==this)
49         {
50                 GLenum color_buf = GL_NONE;
51                 for(unsigned i=0; i<attachments.size(); ++i)
52                 {
53                         const Attachment &attch = attachments[i];
54                         if(mask&(1<<i))
55                         {
56                                 if(attch.type==GL_RENDERBUFFER_EXT)
57                                         glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, attch.attachment, GL_RENDERBUFFER_EXT, attch.rbuf->get_id());
58                                 else if(attch.type==GL_TEXTURE_2D)
59                                 {
60                                         static_cast<Texture2D *>(attch.tex)->allocate(attch.level);
61                                         glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attch.attachment, attch.type, attch.tex->get_id(), attch.level);
62                                 }
63                                 else
64                                         glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, attch.attachment, 0, 0);
65                         }
66
67                         if(attch.attachment>=COLOR_ATTACHMENT0 && attch.attachment<=COLOR_ATTACHMENT3)
68                                 color_buf = attch.attachment;
69                 }
70
71                 glDrawBuffer(color_buf);
72         }
73         else
74                 dirty |= mask;
75 }
76
77 void Framebuffer::check_size()
78 {
79         for(vector<Attachment>::iterator i=attachments.begin(); i!=attachments.end(); ++i)
80                 if(i->type)
81                 {
82                         if(i->type==GL_RENDERBUFFER_EXT)
83                         {
84                                 width = i->rbuf->get_width();
85                                 height = i->rbuf->get_height();
86                         }
87                         else if(i->type==GL_TEXTURE_2D)
88                         {
89                                 Texture2D *tex = static_cast<Texture2D *>(i->tex);
90                                 width = tex->get_width();
91                                 height = tex->get_height();
92                         }
93                         if(current()==this)
94                                 glViewport(0, 0, width, height);
95                         break;
96                 }
97 }
98
99 void Framebuffer::attach(FramebufferAttachment attch, Renderbuffer &rbuf)
100 {
101         if(!id)
102                 throw invalid_operation("Framebuffer::attach");
103
104         unsigned i = get_attachment_index(attch);
105         attachments[i].set(rbuf);
106         update_attachment(1<<i);
107         check_size();
108 }
109
110 void Framebuffer::attach(FramebufferAttachment attch, Texture2D &tex, unsigned level)
111 {
112         if(!id)
113                 throw invalid_operation("Framebuffer::attach");
114
115         unsigned i = get_attachment_index(attch);
116         attachments[i].set(tex, level);
117         update_attachment(1<<i);
118         check_size();
119 }
120
121 void Framebuffer::detach(FramebufferAttachment attch)
122 {
123         if(!id)
124                 throw invalid_operation("Framebuffer::detach");
125
126         unsigned i = get_attachment_index(attch);
127         attachments[i].clear();
128         update_attachment(1<<i);
129         check_size();
130 }
131
132 FramebufferStatus Framebuffer::check_status() const
133 {
134         Bind _bind(this, true);
135         return static_cast<FramebufferStatus>(glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT));
136 }
137
138 void Framebuffer::clear(BufferBits bits)
139 {
140         Bind _bind(this, true);
141         glClear(bits);
142 }
143
144 void Framebuffer::blit_from(const Framebuffer &other, int sx0, int sy0, int sx1, int sy1, int dx0, int dy0, int dx1, int dy1, BufferBits bits, bool filter)
145 {
146         static RequireExtension _ext("GL_EXT_framebuffer_blit");
147
148         const Framebuffer *old = current();
149         if(set_current(this))
150         {
151                 glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, id);
152                 if(dirty)
153                 {
154                         update_attachment(dirty);
155                         dirty = 0;
156                 }
157         }
158         if(old!=&other)
159                 glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, other.id);
160
161         glBlitFramebufferEXT(sx0, sy0, sx1, sy1, dx0, dy0, dx1, dy1, bits, (filter ? GL_LINEAR : GL_NEAREST));
162
163         set_current(old);
164         glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, (old ? old->id : 0));
165 }
166
167 void Framebuffer::blit_from(const Framebuffer &other, int sx, int sy, unsigned wd, unsigned ht, int dx, int dy, BufferBits bits)
168 {
169         blit_from(other, sx, sy, sx+wd, sy+ht, dx, dy, dx+wd, dy+ht, bits, false);
170 }
171
172 void Framebuffer::blit_from(const Framebuffer &other, BufferBits bits, bool filter)
173 {
174         blit_from(other, 0, 0, other.width, other.height, 0, 0, width, height, bits, filter);
175 }
176
177 void Framebuffer::bind() const
178 {
179         if(set_current(this))
180         {
181                 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, id);
182                 if(dirty)
183                 {
184                         update_attachment(dirty);
185                         dirty = 0;
186                 }
187                 if(width && height)
188                         glViewport(0, 0, width, height);
189         }
190 }
191
192 const Framebuffer *Framebuffer::current()
193 {       
194         if(!cur_obj)
195                 cur_obj = &system();
196         return cur_obj;
197 }
198
199 void Framebuffer::unbind()
200 {
201         system().bind();
202 }
203
204 Framebuffer &Framebuffer::system()
205 {
206         static Framebuffer sys_framebuf(0);
207         return sys_framebuf;
208 }
209
210 unsigned Framebuffer::get_attachment_index(FramebufferAttachment attch)
211 {
212         for(unsigned i=0; i<attachments.size(); ++i)
213                 if(attachments[i].attachment==attch)
214                         return i;
215         attachments.push_back(Attachment(attch));
216         return attachments.size()-1;
217 }
218
219
220 Framebuffer::Attachment::Attachment(FramebufferAttachment a):
221         attachment(a),
222         type(0),
223         level(0)
224 { }
225
226 void Framebuffer::Attachment::set(Renderbuffer &r)
227 {
228         type = GL_RENDERBUFFER_EXT;
229         rbuf = &r;
230         level = 0;
231 }
232
233 void Framebuffer::Attachment::set(Texture &t, unsigned l)
234 {
235         type = t.get_target();
236         tex = &t;
237         level = l;
238 }
239
240 void Framebuffer::Attachment::clear()
241 {
242         type = 0;
243 }
244
245 } // namespace GL
246 } // namespace Msp