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