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