--- /dev/null
+#include <msp/gl/extensions/arb_draw_buffers.h>
+#include <msp/gl/extensions/arb_direct_state_access.h>
+#include <msp/gl/extensions/arb_geometry_shader4.h>
+#include <msp/gl/extensions/arb_internalformat_query.h>
+#include <msp/gl/extensions/arb_internalformat_query2.h>
+#include <msp/gl/extensions/ext_framebuffer_object.h>
+#include <msp/gl/extensions/ext_texture_array.h>
+#include <msp/gl/extensions/ext_texture3d.h>
+#include <msp/gl/extensions/msp_buffer_control.h>
+#include <msp/gl/extensions/khr_debug.h>
+#include <msp/strings/format.h>
+#include "framebuffer.h"
+#include "framebuffer_backend.h"
+#include "gl.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+OpenGLFramebuffer::OpenGLFramebuffer(bool is_system):
+ id(0),
+ status(is_system ? GL_FRAMEBUFFER_COMPLETE : GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT)
+{
+ if(!is_system)
+ {
+ static Require _req(EXT_framebuffer_object);
+
+ if(ARB_direct_state_access)
+ glCreateFramebuffers(1, &id);
+ else
+ glGenFramebuffers(1, &id);
+ }
+}
+
+OpenGLFramebuffer::~OpenGLFramebuffer()
+{
+ if(id)
+ glDeleteFramebuffers(1, &id);
+}
+
+FrameFormat OpenGLFramebuffer::get_system_format()
+{
+ FrameFormat format;
+
+ if(EXT_framebuffer_object)
+ {
+ int value;
+ glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_BACK, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &value);
+ if(value==GL_NONE)
+ glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_FRONT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &value);
+ if(value!=GL_NONE)
+ format = (format,COLOR_ATTACHMENT);
+
+ glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &value);
+ if(value!=GL_NONE)
+ format = (format,DEPTH_ATTACHMENT);
+
+ glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_STENCIL, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &value);
+ if(value!=GL_NONE)
+ format = (format,STENCIL_ATTACHMENT);
+ }
+
+ return format;
+}
+
+void OpenGLFramebuffer::get_system_size(unsigned &width, unsigned &height)
+{
+ int view[4];
+ glGetIntegerv(GL_VIEWPORT, view);
+ width = view[2];
+ height = view[3];
+}
+
+bool OpenGLFramebuffer::is_format_supported(const FrameFormat &fmt)
+{
+ // Pretend everything is supported if we can't check
+ if(!ARB_internalformat_query || !ARB_internalformat_query2)
+ return true;
+
+ unsigned target = (fmt.get_samples()>1 ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D);
+ for(FrameAttachment a: fmt)
+ {
+ unsigned pf = get_gl_pixelformat(get_attachment_pixelformat(a));
+ int supported = 0;
+ glGetInternalformativ(target, pf, GL_FRAMEBUFFER_RENDERABLE, 1, &supported);
+ if(supported!=GL_FULL_SUPPORT)
+ return false;
+ }
+
+ return true;
+}
+
+void OpenGLFramebuffer::require_layered()
+{
+ static Require _req(ARB_geometry_shader4);
+}
+
+void OpenGLFramebuffer::update(unsigned mask) const
+{
+ const FrameFormat &format = static_cast<const Framebuffer *>(this)->format;
+ vector<GLenum> color_bufs;
+ color_bufs.reserve(format.size());
+ unsigned i = 0;
+ for(FrameAttachment a: format)
+ {
+ GLenum gl_attach_point = get_gl_attachment(a);
+ if(mask&(1<<i))
+ {
+ const Framebuffer::Attachment &attch = static_cast<const Framebuffer *>(this)->attachments[i];
+ if(attch.tex)
+ {
+ if(ARB_direct_state_access)
+ {
+ if(attch.tex->target==GL_TEXTURE_2D || attch.tex->target==GL_TEXTURE_2D_MULTISAMPLE || attch.layer<0)
+ glNamedFramebufferTexture(id, gl_attach_point, attch.tex->id, attch.level);
+ else
+ glNamedFramebufferTextureLayer(id, gl_attach_point, attch.tex->id, attch.level, attch.layer);
+ }
+ else if(attch.tex->target==GL_TEXTURE_2D || attch.tex->target==GL_TEXTURE_2D_MULTISAMPLE)
+ glFramebufferTexture2D(GL_FRAMEBUFFER, gl_attach_point, attch.tex->target, attch.tex->id, attch.level);
+ else if(attch.layer<0)
+ glFramebufferTexture(GL_FRAMEBUFFER, gl_attach_point, attch.tex->id, attch.level);
+ else if(attch.tex->target==GL_TEXTURE_2D_ARRAY)
+ glFramebufferTextureLayer(GL_FRAMEBUFFER, gl_attach_point, attch.tex->id, attch.level, attch.layer);
+ else if(attch.tex->target==GL_TEXTURE_3D)
+ glFramebufferTexture3D(GL_FRAMEBUFFER, gl_attach_point, attch.tex->target, attch.tex->id, attch.level, attch.layer);
+ else if(attch.tex->target==GL_TEXTURE_CUBE_MAP)
+ glFramebufferTexture2D(GL_FRAMEBUFFER, gl_attach_point, get_gl_cube_face(static_cast<TextureCubeFace>(attch.layer)), attch.tex->id, attch.level);
+ }
+ else if(ARB_direct_state_access)
+ glNamedFramebufferTexture(id, gl_attach_point, 0, 0);
+ else
+ glFramebufferTexture2D(GL_FRAMEBUFFER, gl_attach_point, GL_TEXTURE_2D, 0, 0);
+ }
+
+ if(gl_attach_point!=GL_DEPTH_ATTACHMENT && gl_attach_point!=GL_STENCIL_ATTACHMENT)
+ color_bufs.push_back(gl_attach_point);
+
+ ++i;
+ }
+
+ if(color_bufs.size()>1)
+ static Require _req(ARB_draw_buffers);
+
+ GLenum first_buffer = (color_bufs.empty() ? GL_NONE : color_bufs.front());
+ if(ARB_direct_state_access)
+ {
+ /* ARB_direct_state_access ties the availability of these functions to
+ framebuffers themselves, so no further checks are needed. */
+ glNamedFramebufferDrawBuffers(id, color_bufs.size(), &color_bufs[0]);
+ glNamedFramebufferReadBuffer(id, first_buffer);
+ }
+ else
+ {
+ if(ARB_draw_buffers)
+ glDrawBuffers(color_bufs.size(), &color_bufs[0]);
+ else if(MSP_buffer_control)
+ glDrawBuffer(first_buffer);
+
+ if(MSP_buffer_control)
+ glReadBuffer(first_buffer);
+ }
+
+ if(ARB_direct_state_access)
+ status = glCheckNamedFramebufferStatus(id, GL_FRAMEBUFFER);
+ else
+ status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+}
+
+void OpenGLFramebuffer::require_complete() const
+{
+ if(status==GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT)
+ throw framebuffer_incomplete("incomplete or unsupported attachment");
+ if(status==GL_FRAMEBUFFER_UNSUPPORTED)
+ throw framebuffer_incomplete("unsupported configuration");
+ if(status!=GL_FRAMEBUFFER_COMPLETE)
+ throw framebuffer_incomplete(Msp::format("incomplete (%#x)", status));
+}
+
+void OpenGLFramebuffer::set_debug_name(const string &name)
+{
+#ifdef DEBUG
+ if(KHR_debug)
+ glObjectLabel(GL_FRAMEBUFFER, id, name.size(), name.c_str());
+#else
+ (void)name;
+#endif
+}
+
+} // namespace GL
+} // namespace Msp