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>
8 #include "framebuffer.h"
10 #include "renderbuffer.h"
11 #include "texture2d.h"
12 #include "texture3d.h"
19 void operator<<(LexicalConverter &conv, FramebufferStatus status)
23 case FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
24 conv.result("incomplete attachment");
26 case FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
27 conv.result("missing attachment");
29 case FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
30 conv.result("mismatched attachment dimensions");
32 case FRAMEBUFFER_INCOMPLETE_FORMATS:
33 conv.result("mismatched attachment formats");
35 case FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
36 conv.result("missing draw buffer attachment");
38 case FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
39 conv.result("missing read buffer attachment");
41 case FRAMEBUFFER_UNSUPPORTED:
42 conv.result("unsupported");
45 conv.result(lexical_cast<string, unsigned>(status, "%#x"));
50 framebuffer_incomplete::framebuffer_incomplete(FramebufferStatus status):
51 runtime_error(lexical_cast<string>(status))
55 Framebuffer::Framebuffer(unsigned i):
60 throw invalid_argument("System framebuffer must have id 0");
62 glGetIntegerv(GL_VIEWPORT, &view.left);
67 Framebuffer::Framebuffer():
72 static Require _req(EXT_framebuffer_object);
74 if(ARB_direct_state_access)
75 glCreateFramebuffers(1, &id);
77 glGenFramebuffers(1, &id);
80 Framebuffer::~Framebuffer()
83 glDeleteFramebuffers(1, &id);
88 void Framebuffer::update_attachment(unsigned mask) const
90 if(!ARB_direct_state_access && current()!=this)
96 std::vector<GLenum> color_bufs;
97 color_bufs.reserve(attachments.size());
98 for(unsigned i=0; i<attachments.size(); ++i)
100 const Attachment &attch = attachments[i];
103 if(attch.type==GL_RENDERBUFFER)
105 if(ARB_direct_state_access)
106 glNamedFramebufferRenderbuffer(id, attch.attachment, GL_RENDERBUFFER, attch.rbuf->get_id());
108 glFramebufferRenderbuffer(GL_FRAMEBUFFER, attch.attachment, GL_RENDERBUFFER, attch.rbuf->get_id());
110 else if(attch.type==GL_TEXTURE_2D)
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);
116 glFramebufferTexture2D(GL_FRAMEBUFFER, attch.attachment, attch.type, attch.tex->get_id(), attch.level);
118 else if(attch.type==GL_TEXTURE_3D || attch.type==GL_TEXTURE_2D_ARRAY)
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);
126 glFramebufferTexture3D(GL_FRAMEBUFFER, attch.attachment, attch.type, attch.tex->get_id(), attch.level, attch.layer);
128 else if(attch.type==GL_TEXTURE_CUBE_MAP)
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);
134 glFramebufferTexture2D(GL_FRAMEBUFFER, attch.attachment, TextureCube::enumerate_faces(attch.layer), attch.tex->get_id(), attch.level);
136 else if(ARB_direct_state_access)
137 glNamedFramebufferRenderbuffer(id, attch.attachment, 0, 0);
139 glFramebufferRenderbuffer(GL_FRAMEBUFFER, attch.attachment, 0, 0);
142 if(attch.attachment>=COLOR_ATTACHMENT0 && attch.attachment<=COLOR_ATTACHMENT3)
143 color_bufs.push_back(attch.attachment);
146 if(color_bufs.size()>1)
147 static Require _req(ARB_draw_buffers);
149 GLenum first_buffer = (color_bufs.empty() ? GL_NONE : color_bufs.front());
150 if(ARB_direct_state_access)
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);
160 glDrawBuffers(color_bufs.size(), &color_bufs[0]);
161 else if(MSP_draw_buffer)
162 glDrawBuffer(first_buffer);
165 glReadBuffer(first_buffer);
169 void Framebuffer::check_size()
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)
175 if(i->type==GL_RENDERBUFFER)
177 width = i->rbuf->get_width();
178 height = i->rbuf->get_height();
180 else if(i->type==GL_TEXTURE_2D)
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);
186 else if(i->type==GL_TEXTURE_3D || i->type==GL_TEXTURE_2D_ARRAY)
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);
192 else if(i->type==GL_TEXTURE_CUBE_MAP)
194 width = max(static_cast<TextureCube *>(i->tex)->get_size()>>i->level, 1U);
203 unsigned Framebuffer::get_attachment_index(FramebufferAttachment attch)
205 for(unsigned i=0; i<attachments.size(); ++i)
206 if(attachments[i].attachment==attch)
208 attachments.push_back(Attachment(attch));
209 return attachments.size()-1;
212 void Framebuffer::attach(FramebufferAttachment attch, Renderbuffer &rbuf)
215 throw invalid_operation("Framebuffer::attach");
217 unsigned i = get_attachment_index(attch);
218 attachments[i].set(rbuf);
219 update_attachment(1<<i);
223 void Framebuffer::attach(FramebufferAttachment attch, Texture2D &tex, unsigned level)
226 throw invalid_operation("Framebuffer::attach");
228 unsigned i = get_attachment_index(attch);
229 attachments[i].set(tex, level, 0);
230 update_attachment(1<<i);
234 void Framebuffer::attach(FramebufferAttachment attch, Texture3D &tex, unsigned layer, unsigned level)
237 throw invalid_operation("Framebuffer::attach");
239 unsigned i = get_attachment_index(attch);
240 attachments[i].set(tex, level, layer);
241 update_attachment(1<<i);
245 void Framebuffer::attach(FramebufferAttachment attch, TextureCube &tex, TextureCubeFace face, unsigned level)
248 throw invalid_operation("Framebuffer::attach");
250 unsigned i = get_attachment_index(attch);
251 attachments[i].set(tex, level, TextureCube::get_face_index(face));
252 update_attachment(1<<i);
256 void Framebuffer::detach(FramebufferAttachment attch)
259 throw invalid_operation("Framebuffer::detach");
261 unsigned i = get_attachment_index(attch);
262 attachments[i].clear();
263 update_attachment(1<<i);
267 FramebufferStatus Framebuffer::check_status() const
269 if(ARB_direct_state_access)
270 return static_cast<FramebufferStatus>(glCheckNamedFramebufferStatus(id, GL_FRAMEBUFFER));
273 BindRestore _bind(this);
274 return static_cast<FramebufferStatus>(glCheckFramebufferStatus(GL_FRAMEBUFFER));
278 void Framebuffer::require_complete() const
280 FramebufferStatus status = check_status();
281 if(status!=FRAMEBUFFER_COMPLETE)
282 throw framebuffer_incomplete(status);
285 void Framebuffer::viewport(int l, int b, unsigned w, unsigned h)
293 glViewport(view.left, view.bottom, view.width, view.height);
296 void Framebuffer::reset_viewport()
298 viewport(0, 0, width, height);
301 void Framebuffer::clear(BufferBits bits)
303 BindRestore _bind(this);
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)
309 static Require _req(EXT_framebuffer_blit);
311 if(ARB_direct_state_access)
313 glBlitNamedFramebuffer(other.id, id, sx0, sy0, sx1, sy1, dx0, dy0, dx1, dy1, bits, (filter ? GL_LINEAR : GL_NEAREST));
317 const Framebuffer *old = current();
318 if(set_current(this))
320 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, id);
323 update_attachment(dirty);
328 glBindFramebuffer(GL_READ_FRAMEBUFFER, other.id);
330 glBlitFramebuffer(sx0, sy0, sx1, sy1, dx0, dy0, dx1, dy1, bits, (filter ? GL_LINEAR : GL_NEAREST));
333 glBindFramebuffer(GL_FRAMEBUFFER, (old ? old->id : 0));
336 void Framebuffer::blit_from(const Framebuffer &other, int sx, int sy, unsigned wd, unsigned ht, int dx, int dy, BufferBits bits)
338 blit_from(other, sx, sy, sx+wd, sy+ht, dx, dy, dx+wd, dy+ht, bits, false);
341 void Framebuffer::blit_from(const Framebuffer &other, BufferBits bits, bool filter)
343 blit_from(other, 0, 0, other.width, other.height, 0, 0, width, height, bits, filter);
346 void Framebuffer::bind() const
348 if(set_current(this))
350 glBindFramebuffer(GL_FRAMEBUFFER, id);
353 update_attachment(dirty);
358 glViewport(view.left, view.bottom, view.width, view.height);
362 const Framebuffer *Framebuffer::current()
369 void Framebuffer::unbind()
374 Framebuffer &Framebuffer::system()
376 static Framebuffer sys_framebuf(0);
381 Framebuffer::Attachment::Attachment(FramebufferAttachment a):
388 void Framebuffer::Attachment::set(Renderbuffer &r)
390 type = GL_RENDERBUFFER;
396 void Framebuffer::Attachment::set(Texture &t, unsigned l, unsigned z)
398 type = t.get_target();
404 void Framebuffer::Attachment::clear()
410 Framebuffer::Viewport::Viewport():