]> git.tdb.fi Git - libs/gl.git/blob - source/framebuffer.cpp
c3639b8bb378848f4c9dda1e27f7654109c75af6
[libs/gl.git] / source / framebuffer.cpp
1 #include <msp/gl/extensions/arb_draw_buffers.h>
2 #include <msp/gl/extensions/arb_direct_state_access.h>
3 #include <msp/gl/extensions/ext_framebuffer_blit.h>
4 #include <msp/gl/extensions/ext_framebuffer_object.h>
5 #include <msp/gl/extensions/ext_texture_array.h>
6 #include <msp/gl/extensions/ext_texture3d.h>
7 #include <msp/gl/extensions/msp_draw_buffer.h>
8 #include "error.h"
9 #include "framebuffer.h"
10 #include "misc.h"
11 #include "renderbuffer.h"
12 #include "texture2d.h"
13 #include "texture3d.h"
14
15 using namespace std;
16
17 namespace Msp {
18 namespace GL {
19
20 void operator<<(LexicalConverter &conv, FramebufferStatus status)
21 {
22         switch(status)
23         {
24         case FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
25                 conv.result("incomplete attachment");
26                 break;
27         case FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
28                 conv.result("missing attachment");
29                 break;
30         case FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
31                 conv.result("mismatched attachment dimensions");
32                 break;
33         case FRAMEBUFFER_INCOMPLETE_FORMATS:
34                 conv.result("mismatched attachment formats");
35                 break;
36         case FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
37                 conv.result("missing draw buffer attachment");
38                 break;
39         case FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
40                 conv.result("missing read buffer attachment");
41                 break;
42         case FRAMEBUFFER_UNSUPPORTED:
43                 conv.result("unsupported");
44                 break;
45         default:
46                 conv.result(lexical_cast<string, unsigned>(status, "%#x"));
47                 break;
48         }
49 }
50
51 framebuffer_incomplete::framebuffer_incomplete(FramebufferStatus status):
52         runtime_error(lexical_cast<string>(status))
53 { }
54
55
56 Framebuffer::Framebuffer(unsigned i):
57         id(i),
58         dirty(0)
59 {
60         if(id)
61                 throw invalid_argument("System framebuffer must have id 0");
62
63         glGetIntegerv(GL_VIEWPORT, &view.left);
64         width = view.width;
65         height = view.height;
66 }
67
68 Framebuffer::Framebuffer():
69         width(0),
70         height(0),
71         dirty(0)
72 {
73         static Require _req(EXT_framebuffer_object);
74
75         if(ARB_direct_state_access)
76                 glCreateFramebuffers(1, &id);
77         else
78                 glGenFramebuffers(1, &id);
79 }
80
81 Framebuffer::~Framebuffer()
82 {
83         if(id)
84                 glDeleteFramebuffers(1, &id);
85         if(current()==this)
86                 unbind();
87 }
88
89 void Framebuffer::update_attachment(unsigned mask) const
90 {
91         if(!ARB_direct_state_access && current()!=this)
92         {
93                 dirty |= mask;
94                 return;
95         }
96
97         std::vector<GLenum> color_bufs;
98         color_bufs.reserve(attachments.size());
99         for(unsigned i=0; i<attachments.size(); ++i)
100         {
101                 const Attachment &attch = attachments[i];
102                 if(mask&(1<<i))
103                 {
104                         if(attch.type==GL_RENDERBUFFER)
105                         {
106                                 if(ARB_direct_state_access)
107                                         glNamedFramebufferRenderbuffer(id, attch.attachment, GL_RENDERBUFFER, attch.rbuf->get_id());
108                                 else
109                                         glFramebufferRenderbuffer(GL_FRAMEBUFFER, attch.attachment, GL_RENDERBUFFER, attch.rbuf->get_id());
110                         }
111                         else if(attch.type==GL_TEXTURE_2D)
112                         {
113                                 static_cast<Texture2D *>(attch.tex)->allocate(attch.level);
114                                 if(ARB_direct_state_access)
115                                         glNamedFramebufferTexture(id, attch.attachment, attch.tex->get_id(), attch.level);
116                                 else
117                                         glFramebufferTexture2D(GL_FRAMEBUFFER, attch.attachment, attch.type, attch.tex->get_id(), attch.level);
118                         }
119                         else if(attch.type==GL_TEXTURE_3D || attch.type==GL_TEXTURE_2D_ARRAY)
120                         {
121                                 static_cast<Texture3D *>(attch.tex)->allocate(attch.level);
122                                 if(ARB_direct_state_access)
123                                         glNamedFramebufferTextureLayer(id, attch.attachment, attch.tex->get_id(), attch.level, attch.layer);
124                                 else if(attch.type==GL_TEXTURE_2D_ARRAY)
125                                         glFramebufferTextureLayer(GL_FRAMEBUFFER, attch.attachment, attch.tex->get_id(), attch.level, attch.layer);
126                                 else
127                                         glFramebufferTexture3D(GL_FRAMEBUFFER, attch.attachment, attch.type, attch.tex->get_id(), attch.level, attch.layer);
128                         }
129                         else if(attch.type==GL_TEXTURE_CUBE_MAP)
130                         {
131                                 static_cast<TextureCube *>(attch.tex)->allocate(attch.level);
132                                 if(ARB_direct_state_access)
133                                         glNamedFramebufferTextureLayer(id, attch.attachment, attch.tex->get_id(), attch.level, attch.layer);
134                                 else
135                                         glFramebufferTexture2D(GL_FRAMEBUFFER, attch.attachment, TextureCube::enumerate_faces(attch.layer), attch.tex->get_id(), attch.level);
136                         }
137                         else if(ARB_direct_state_access)
138                                 glNamedFramebufferRenderbuffer(id, attch.attachment, 0, 0);
139                         else
140                                 glFramebufferRenderbuffer(GL_FRAMEBUFFER, attch.attachment, 0, 0);
141                 }
142
143                 if(attch.attachment>=COLOR_ATTACHMENT0 && attch.attachment<=COLOR_ATTACHMENT3)
144                         color_bufs.push_back(attch.attachment);
145         }
146
147         if(color_bufs.size()>1)
148                 static Require _req(ARB_draw_buffers);
149
150         GLenum first_buffer = (color_bufs.empty() ? GL_NONE : color_bufs.front());
151         if(ARB_direct_state_access)
152         {
153                 /* ARB_direct_state_access ties the availability of these functions to
154                 framebuffers themselves, so no further checks are needed. */
155                 glNamedFramebufferDrawBuffers(id, color_bufs.size(), &color_bufs[0]);
156                 glNamedFramebufferReadBuffer(id, first_buffer);
157         }
158         else
159         {
160                 if(ARB_draw_buffers)
161                         glDrawBuffers(color_bufs.size(), &color_bufs[0]);
162                 else if(MSP_draw_buffer)
163                         glDrawBuffer(first_buffer);
164
165                 if(MSP_draw_buffer)
166                         glReadBuffer(first_buffer);
167         }
168 }
169
170 void Framebuffer::check_size()
171 {
172         bool full_viewport = (view.left==0 && view.bottom==0 && view.width==width && view.height==height);
173         for(vector<Attachment>::iterator i=attachments.begin(); i!=attachments.end(); ++i)
174                 if(i->type)
175                 {
176                         if(i->type==GL_RENDERBUFFER)
177                         {
178                                 width = i->rbuf->get_width();
179                                 height = i->rbuf->get_height();
180                         }
181                         else if(i->type==GL_TEXTURE_2D)
182                         {
183                                 Texture2D *tex = static_cast<Texture2D *>(i->tex);
184                                 width = max(tex->get_width()>>i->level, 1U);
185                                 height = max(tex->get_height()>>i->level, 1U);
186                         }
187                         else if(i->type==GL_TEXTURE_3D || i->type==GL_TEXTURE_2D_ARRAY)
188                         {
189                                 Texture3D *tex = static_cast<Texture3D *>(i->tex);
190                                 width = max(tex->get_width()>>i->level, 1U);
191                                 height = max(tex->get_height()>>i->level, 1U);
192                         }
193                         else if(i->type==GL_TEXTURE_CUBE_MAP)
194                         {
195                                 width = max(static_cast<TextureCube *>(i->tex)->get_size()>>i->level, 1U);
196                                 height = width;
197                         }
198                         if(full_viewport)
199                                 reset_viewport();
200                         break;
201                 }
202 }
203
204 unsigned Framebuffer::get_attachment_index(FramebufferAttachment attch)
205 {
206         for(unsigned i=0; i<attachments.size(); ++i)
207                 if(attachments[i].attachment==attch)
208                         return i;
209         attachments.push_back(Attachment(attch));
210         return attachments.size()-1;
211 }
212
213 void Framebuffer::attach(FramebufferAttachment attch, Renderbuffer &rbuf)
214 {
215         if(!id)
216                 throw invalid_operation("Framebuffer::attach");
217
218         unsigned i = get_attachment_index(attch);
219         attachments[i].set(rbuf);
220         update_attachment(1<<i);
221         check_size();
222 }
223
224 void Framebuffer::attach(FramebufferAttachment attch, Texture2D &tex, unsigned level)
225 {
226         if(!id)
227                 throw invalid_operation("Framebuffer::attach");
228
229         unsigned i = get_attachment_index(attch);
230         attachments[i].set(tex, level, 0);
231         update_attachment(1<<i);
232         check_size();
233 }
234
235 void Framebuffer::attach(FramebufferAttachment attch, Texture3D &tex, unsigned layer, unsigned level)
236 {
237         if(!id)
238                 throw invalid_operation("Framebuffer::attach");
239
240         unsigned i = get_attachment_index(attch);
241         attachments[i].set(tex, level, layer);
242         update_attachment(1<<i);
243         check_size();
244 }
245
246 void Framebuffer::attach(FramebufferAttachment attch, TextureCube &tex, TextureCubeFace face, unsigned level)
247 {
248         if(!id)
249                 throw invalid_operation("Framebuffer::attach");
250
251         unsigned i = get_attachment_index(attch);
252         attachments[i].set(tex, level, TextureCube::get_face_index(face));
253         update_attachment(1<<i);
254         check_size();
255 }
256
257 void Framebuffer::detach(FramebufferAttachment attch)
258 {
259         if(!id)
260                 throw invalid_operation("Framebuffer::detach");
261
262         unsigned i = get_attachment_index(attch);
263         attachments[i].clear();
264         update_attachment(1<<i);
265         check_size();
266 }
267
268 FramebufferStatus Framebuffer::check_status() const
269 {
270         if(ARB_direct_state_access)
271                 return static_cast<FramebufferStatus>(glCheckNamedFramebufferStatus(id, GL_FRAMEBUFFER));
272         else
273         {
274                 BindRestore _bind(this);
275                 return static_cast<FramebufferStatus>(glCheckFramebufferStatus(GL_FRAMEBUFFER));
276         }
277 }
278
279 void Framebuffer::require_complete() const
280 {
281         FramebufferStatus status = check_status();
282         if(status!=FRAMEBUFFER_COMPLETE)
283                 throw framebuffer_incomplete(status);
284 }
285
286 void Framebuffer::viewport(int l, int b, unsigned w, unsigned h)
287 {
288         view.left = l;
289         view.bottom = b;
290         view.width = w;
291         view.height = h;
292
293         if(current()==this)
294                 glViewport(view.left, view.bottom, view.width, view.height);
295 }
296
297 void Framebuffer::reset_viewport()
298 {
299         viewport(0, 0, width, height);
300 }
301
302 void Framebuffer::clear(BufferBits bits)
303 {
304         BindRestore _bind(this);
305         glClear(bits);
306 }
307
308 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)
309 {
310         static Require _req(EXT_framebuffer_blit);
311
312         if(ARB_direct_state_access)
313         {
314                 glBlitNamedFramebuffer(other.id, id, sx0, sy0, sx1, sy1, dx0, dy0, dx1, dy1, bits, (filter ? GL_LINEAR : GL_NEAREST));
315                 return;
316         }
317
318         const Framebuffer *old = current();
319         if(set_current(this))
320         {
321                 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, id);
322                 if(dirty)
323                 {
324                         update_attachment(dirty);
325                         dirty = 0;
326                 }
327         }
328         if(old!=&other)
329                 glBindFramebuffer(GL_READ_FRAMEBUFFER, other.id);
330
331         glBlitFramebuffer(sx0, sy0, sx1, sy1, dx0, dy0, dx1, dy1, bits, (filter ? GL_LINEAR : GL_NEAREST));
332
333         set_current(old);
334         glBindFramebuffer(GL_FRAMEBUFFER, (old ? old->id : 0));
335 }
336
337 void Framebuffer::blit_from(const Framebuffer &other, int sx, int sy, unsigned wd, unsigned ht, int dx, int dy, BufferBits bits)
338 {
339         blit_from(other, sx, sy, sx+wd, sy+ht, dx, dy, dx+wd, dy+ht, bits, false);
340 }
341
342 void Framebuffer::blit_from(const Framebuffer &other, BufferBits bits, bool filter)
343 {
344         blit_from(other, 0, 0, other.width, other.height, 0, 0, width, height, bits, filter);
345 }
346
347 void Framebuffer::bind() const
348 {
349         if(set_current(this))
350         {
351                 glBindFramebuffer(GL_FRAMEBUFFER, id);
352                 if(dirty)
353                 {
354                         update_attachment(dirty);
355                         dirty = 0;
356                 }
357
358                 if(width && height)
359                         glViewport(view.left, view.bottom, view.width, view.height);
360         }
361 }
362
363 const Framebuffer *Framebuffer::current()
364 {
365         if(!cur_obj)
366                 cur_obj = &system();
367         return cur_obj;
368 }
369
370 void Framebuffer::unbind()
371 {
372         system().bind();
373 }
374
375 Framebuffer &Framebuffer::system()
376 {
377         static Framebuffer sys_framebuf(0);
378         return sys_framebuf;
379 }
380
381
382 Framebuffer::Attachment::Attachment(FramebufferAttachment a):
383         attachment(a),
384         type(0),
385         level(0),
386         layer(0)
387 { }
388
389 void Framebuffer::Attachment::set(Renderbuffer &r)
390 {
391         type = GL_RENDERBUFFER;
392         rbuf = &r;
393         level = 0;
394         layer = 0;
395 }
396
397 void Framebuffer::Attachment::set(Texture &t, unsigned l, unsigned z)
398 {
399         type = t.get_target();
400         tex = &t;
401         level = l;
402         layer = z;
403 }
404
405 void Framebuffer::Attachment::clear()
406 {
407         type = 0;
408 }
409
410
411 Framebuffer::Viewport::Viewport():
412         left(0),
413         bottom(0),
414         width(0),
415         height(0)
416 { }
417
418 } // namespace GL
419 } // namespace Msp