|
@@ -33,423 +33,54 @@ namespace graphics
|
|
|
namespace opengl
|
|
|
{
|
|
|
|
|
|
-// strategy for fbo creation, interchangable at runtime:
|
|
|
-// none, opengl >= 3.0, extensions
|
|
|
-struct FramebufferStrategy
|
|
|
+static GLenum createFBO(GLuint &framebuffer, GLuint texture)
|
|
|
{
|
|
|
- virtual ~FramebufferStrategy() {}
|
|
|
-
|
|
|
- /// create a new framebuffer and texture
|
|
|
- /**
|
|
|
- * @param[out] framebuffer Framebuffer name
|
|
|
- * @param[in] texture Texture name
|
|
|
- * @return Creation status
|
|
|
- */
|
|
|
- virtual GLenum createFBO(GLuint &, GLuint)
|
|
|
- {
|
|
|
- return GL_FRAMEBUFFER_UNSUPPORTED;
|
|
|
- }
|
|
|
-
|
|
|
- /// Create a stencil buffer and attach it to the active framebuffer object
|
|
|
- /**
|
|
|
- * @param[in] width Width of the stencil buffer
|
|
|
- * @param[in] height Height of the stencil buffer
|
|
|
- * @param[in] samples Number of samples to use
|
|
|
- * @param[out] stencil Name for stencil buffer
|
|
|
- * @return Whether the stencil buffer was successfully created
|
|
|
- **/
|
|
|
- virtual bool createStencil(int, int, int, GLuint &)
|
|
|
- {
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- /// Create a MSAA renderbuffer and attach it to the active FBO.
|
|
|
- /**
|
|
|
- * @param[in] width Width of the MSAA buffer
|
|
|
- * @param[in] height Height of the MSAA buffer
|
|
|
- * @param[inout] samples Number of samples to use
|
|
|
- * @param[in] internalformat The internal format to use for the buffer
|
|
|
- * @param[out] buffer Name for the MSAA buffer
|
|
|
- * @return Whether the MSAA buffer was successfully created and attached
|
|
|
- **/
|
|
|
- virtual bool createMSAABuffer(int, int, int &, GLenum, GLuint &)
|
|
|
- {
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- /// remove objects
|
|
|
- /**
|
|
|
- * @param[in] framebuffer Framebuffer name
|
|
|
- * @param[in] depth_stencil Name for packed depth and stencil buffer
|
|
|
- */
|
|
|
- virtual void deleteFBO(GLuint, GLuint, GLuint) {}
|
|
|
- virtual void bindFBO(GLuint) {}
|
|
|
-
|
|
|
- /// attach additional canvases to the active framebuffer for rendering
|
|
|
- /**
|
|
|
- * @param[in] canvases List of canvases to attach
|
|
|
- **/
|
|
|
- virtual void setAttachments(const std::vector<Canvas *> &) {}
|
|
|
-
|
|
|
- /// stop using all additional attached canvases
|
|
|
- virtual void setAttachments() {}
|
|
|
-};
|
|
|
-
|
|
|
-struct FramebufferStrategyCorePacked : public FramebufferStrategy
|
|
|
-{
|
|
|
- virtual GLenum createFBO(GLuint &framebuffer, GLuint texture)
|
|
|
- {
|
|
|
- // get currently bound fbo to reset to it later
|
|
|
- GLint current_fbo;
|
|
|
- glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤t_fbo);
|
|
|
-
|
|
|
- // create framebuffer
|
|
|
- glGenFramebuffers(1, &framebuffer);
|
|
|
- glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
|
|
|
-
|
|
|
- if (texture != 0)
|
|
|
- {
|
|
|
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
|
|
- GL_TEXTURE_2D, texture, 0);
|
|
|
- }
|
|
|
-
|
|
|
- // check status
|
|
|
- GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
|
|
-
|
|
|
- // unbind framebuffer
|
|
|
- glBindFramebuffer(GL_FRAMEBUFFER, (GLuint) current_fbo);
|
|
|
- return status;
|
|
|
- }
|
|
|
-
|
|
|
- virtual bool createStencil(int width, int height, int samples, GLuint &stencil)
|
|
|
- {
|
|
|
- // create combined depth/stencil buffer
|
|
|
- glDeleteRenderbuffers(1, &stencil);
|
|
|
- glGenRenderbuffers(1, &stencil);
|
|
|
- glBindRenderbuffer(GL_RENDERBUFFER, stencil);
|
|
|
-
|
|
|
- if (samples > 1)
|
|
|
- glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, GL_DEPTH_STENCIL, width, height);
|
|
|
- else
|
|
|
- glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_STENCIL, width, height);
|
|
|
-
|
|
|
- glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
|
|
|
- GL_RENDERBUFFER, stencil);
|
|
|
-
|
|
|
- glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
|
|
-
|
|
|
- // check status
|
|
|
- if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
|
|
- {
|
|
|
- glDeleteRenderbuffers(1, &stencil);
|
|
|
- stencil = 0;
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- virtual bool createMSAABuffer(int width, int height, int &samples, GLenum iformat, GLuint &buffer)
|
|
|
- {
|
|
|
- glGenRenderbuffers(1, &buffer);
|
|
|
- glBindRenderbuffer(GL_RENDERBUFFER, buffer);
|
|
|
- glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, iformat, width, height);
|
|
|
- glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
|
|
- GL_RENDERBUFFER, buffer);
|
|
|
- glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
|
|
|
-
|
|
|
- glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
|
|
-
|
|
|
- if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
|
|
- {
|
|
|
- glDeleteRenderbuffers(1, &buffer);
|
|
|
- buffer = 0;
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- virtual void deleteFBO(GLuint framebuffer, GLuint depth_stencil, GLuint msaa_buffer)
|
|
|
- {
|
|
|
- if (depth_stencil != 0)
|
|
|
- glDeleteRenderbuffers(1, &depth_stencil);
|
|
|
- if (msaa_buffer != 0)
|
|
|
- glDeleteRenderbuffers(1, &msaa_buffer);
|
|
|
- if (framebuffer != 0)
|
|
|
- glDeleteFramebuffers(1, &framebuffer);
|
|
|
- }
|
|
|
-
|
|
|
- virtual void bindFBO(GLuint framebuffer)
|
|
|
- {
|
|
|
- glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
|
|
|
- ++Canvas::switchCount;
|
|
|
- }
|
|
|
-
|
|
|
- virtual void setAttachments()
|
|
|
- {
|
|
|
- // set a single render target
|
|
|
- glDrawBuffer(GL_COLOR_ATTACHMENT0);
|
|
|
- }
|
|
|
-
|
|
|
- virtual void setAttachments(const std::vector<Canvas *> &canvases)
|
|
|
- {
|
|
|
- if (canvases.size() == 0)
|
|
|
- {
|
|
|
- setAttachments();
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- std::vector<GLenum> drawbuffers;
|
|
|
- drawbuffers.push_back(GL_COLOR_ATTACHMENT0);
|
|
|
-
|
|
|
- // Attach the canvas textures to the currently bound framebuffer.
|
|
|
- for (int i = 0; i < (int) canvases.size(); i++)
|
|
|
- {
|
|
|
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1 + i,
|
|
|
- GL_TEXTURE_2D, canvases[i]->getGLTexture(), 0);
|
|
|
- drawbuffers.push_back(GL_COLOR_ATTACHMENT1 + i);
|
|
|
- }
|
|
|
-
|
|
|
- // set up multiple render targets
|
|
|
- glDrawBuffers((int) drawbuffers.size(), &drawbuffers[0]);
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-struct FramebufferStrategyCore : public FramebufferStrategyCorePacked
|
|
|
-{
|
|
|
- virtual bool createStencil(int width, int height, int samples, GLuint &stencil)
|
|
|
- {
|
|
|
- // create stencil buffer
|
|
|
- glDeleteRenderbuffers(1, &stencil);
|
|
|
- glGenRenderbuffers(1, &stencil);
|
|
|
- glBindRenderbuffer(GL_RENDERBUFFER, stencil);
|
|
|
-
|
|
|
- if (samples > 1)
|
|
|
- glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, GL_STENCIL_INDEX8, width, height);
|
|
|
- else
|
|
|
- glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, width, height);
|
|
|
-
|
|
|
- glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
|
|
|
- GL_RENDERBUFFER, stencil);
|
|
|
-
|
|
|
- glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
|
|
-
|
|
|
- // check status
|
|
|
- if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
|
|
- {
|
|
|
- glDeleteRenderbuffers(1, &stencil);
|
|
|
- stencil = 0;
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- return true;
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-struct FramebufferStrategyPackedEXT : public FramebufferStrategy
|
|
|
-{
|
|
|
- virtual GLenum createFBO(GLuint &framebuffer, GLuint texture)
|
|
|
- {
|
|
|
- GLint current_fbo;
|
|
|
- glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING_EXT, ¤t_fbo);
|
|
|
-
|
|
|
- // create framebuffer
|
|
|
- glGenFramebuffersEXT(1, &framebuffer);
|
|
|
- glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer);
|
|
|
-
|
|
|
- glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
|
|
|
- GL_TEXTURE_2D, texture, 0);
|
|
|
-
|
|
|
- // check status
|
|
|
- GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
|
|
|
-
|
|
|
- // unbind framebuffer
|
|
|
- glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, (GLuint) current_fbo);
|
|
|
- return status;
|
|
|
- }
|
|
|
-
|
|
|
- virtual bool createStencil(int width, int height, int samples, GLuint &stencil)
|
|
|
- {
|
|
|
- // create combined depth/stencil buffer
|
|
|
- glDeleteRenderbuffersEXT(1, &stencil);
|
|
|
- glGenRenderbuffersEXT(1, &stencil);
|
|
|
- glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, stencil);
|
|
|
-
|
|
|
- if (samples > 1)
|
|
|
- {
|
|
|
- glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, samples,
|
|
|
- GL_DEPTH_STENCIL, width, height);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_STENCIL_EXT,
|
|
|
- width, height);
|
|
|
- }
|
|
|
-
|
|
|
- glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT,
|
|
|
- GL_RENDERBUFFER_EXT, stencil);
|
|
|
-
|
|
|
- glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
|
|
|
-
|
|
|
- // check status
|
|
|
- if (glCheckFramebufferStatusEXT(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
|
|
- {
|
|
|
- glDeleteRenderbuffersEXT(1, &stencil);
|
|
|
- stencil = 0;
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- virtual bool createMSAABuffer(int width, int height, int &samples, GLenum internalformat, GLuint &buffer)
|
|
|
- {
|
|
|
- if (!GLAD_EXT_framebuffer_multisample)
|
|
|
- return false;
|
|
|
-
|
|
|
- glGenRenderbuffersEXT(1, &buffer);
|
|
|
- glBindRenderbufferEXT(GL_RENDERBUFFER, buffer);
|
|
|
- glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, samples,
|
|
|
- internalformat, width, height);
|
|
|
- glFramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
|
|
- GL_RENDERBUFFER, buffer);
|
|
|
- glGetRenderbufferParameterivEXT(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
|
|
|
-
|
|
|
- glBindRenderbufferEXT(GL_RENDERBUFFER, 0);
|
|
|
+ // get currently bound fbo to reset to it later
|
|
|
+ GLint current_fbo;
|
|
|
+ glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤t_fbo);
|
|
|
|
|
|
- if (glCheckFramebufferStatusEXT(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
|
|
- {
|
|
|
- glDeleteRenderbuffersEXT(1, &buffer);
|
|
|
- buffer = 0;
|
|
|
- return false;
|
|
|
- }
|
|
|
+ glGenFramebuffers(1, &framebuffer);
|
|
|
+ gl.bindFramebuffer(GL_FRAMEBUFFER, framebuffer);
|
|
|
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- virtual void deleteFBO(GLuint framebuffer, GLuint depth_stencil, GLuint msaa_buffer)
|
|
|
- {
|
|
|
- if (depth_stencil != 0)
|
|
|
- glDeleteRenderbuffersEXT(1, &depth_stencil);
|
|
|
- if (msaa_buffer != 0)
|
|
|
- glDeleteRenderbuffersEXT(1, &msaa_buffer);
|
|
|
- if (framebuffer != 0)
|
|
|
- glDeleteFramebuffersEXT(1, &framebuffer);
|
|
|
- }
|
|
|
+ if (texture != 0)
|
|
|
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
|
|
|
|
|
|
- virtual void bindFBO(GLuint framebuffer)
|
|
|
- {
|
|
|
- glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer);
|
|
|
- ++Canvas::switchCount;
|
|
|
- }
|
|
|
+ GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
|
|
|
|
|
- virtual void setAttachments()
|
|
|
- {
|
|
|
- // set a single render target
|
|
|
- glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
|
|
|
- }
|
|
|
+ // unbind framebuffer
|
|
|
+ gl.bindFramebuffer(GL_FRAMEBUFFER, (GLuint) current_fbo);
|
|
|
|
|
|
- virtual void setAttachments(const std::vector<Canvas *> &canvases)
|
|
|
- {
|
|
|
- if (canvases.size() == 0)
|
|
|
- {
|
|
|
- setAttachments();
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- std::vector<GLenum> drawbuffers;
|
|
|
- drawbuffers.push_back(GL_COLOR_ATTACHMENT0_EXT);
|
|
|
-
|
|
|
- // Attach the canvas textures to the currently bound framebuffer.
|
|
|
- for (int i = 0; i < (int) canvases.size(); i++)
|
|
|
- {
|
|
|
- glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1_EXT + i,
|
|
|
- GL_TEXTURE_2D, canvases[i]->getGLTexture(), 0);
|
|
|
- drawbuffers.push_back(GL_COLOR_ATTACHMENT1_EXT + i);
|
|
|
- }
|
|
|
-
|
|
|
- // set up multiple render targets
|
|
|
- glDrawBuffers((int) drawbuffers.size(), &drawbuffers[0]);
|
|
|
- }
|
|
|
-};
|
|
|
+ return status;
|
|
|
+}
|
|
|
|
|
|
-struct FramebufferStrategyEXT : public FramebufferStrategyPackedEXT
|
|
|
+static GLenum createMSAABuffer(int width, int height, int &samples, GLenum iformat, GLuint &buffer)
|
|
|
{
|
|
|
- virtual bool createStencil(int width, int height, int samples, GLuint &stencil)
|
|
|
- {
|
|
|
- // create stencil buffer
|
|
|
- glDeleteRenderbuffersEXT(1, &stencil);
|
|
|
- glGenRenderbuffersEXT(1, &stencil);
|
|
|
- glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, stencil);
|
|
|
+ glGenRenderbuffers(1, &buffer);
|
|
|
+ glBindRenderbuffer(GL_RENDERBUFFER, buffer);
|
|
|
|
|
|
- if (samples > 1)
|
|
|
- {
|
|
|
- glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, samples,
|
|
|
- GL_STENCIL_INDEX, width, height);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_STENCIL_INDEX,
|
|
|
- width, height);
|
|
|
- }
|
|
|
+ glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, iformat, width, height);
|
|
|
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, buffer);
|
|
|
|
|
|
- glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT,
|
|
|
- GL_RENDERBUFFER_EXT, stencil);
|
|
|
+ glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
|
|
|
|
|
|
- glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
|
|
|
+ glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
|
|
|
|
|
- // check status
|
|
|
- if (glCheckFramebufferStatusEXT(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
|
|
- {
|
|
|
- glDeleteRenderbuffersEXT(1, &stencil);
|
|
|
- stencil = 0;
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- return true;
|
|
|
- }
|
|
|
+ GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
|
|
|
|
|
- bool isSupported()
|
|
|
+ if (status != GL_FRAMEBUFFER_COMPLETE)
|
|
|
{
|
|
|
- GLuint fb = 0, stencil = 0;
|
|
|
- GLenum status = createFBO(fb, 0);
|
|
|
- deleteFBO(fb, stencil, 0);
|
|
|
- return status == GL_FRAMEBUFFER_COMPLETE;
|
|
|
+ glDeleteRenderbuffers(1, &buffer);
|
|
|
+ buffer = 0;
|
|
|
}
|
|
|
-};
|
|
|
-
|
|
|
-FramebufferStrategy *strategy = nullptr;
|
|
|
|
|
|
-FramebufferStrategy strategyNone;
|
|
|
-FramebufferStrategyCorePacked strategyCorePacked;
|
|
|
-FramebufferStrategyCore strategyCore;
|
|
|
-FramebufferStrategyPackedEXT strategyPackedEXT;
|
|
|
-FramebufferStrategyEXT strategyEXT;
|
|
|
+ return status;
|
|
|
+}
|
|
|
|
|
|
Canvas *Canvas::current = nullptr;
|
|
|
OpenGL::Viewport Canvas::systemViewport = OpenGL::Viewport();
|
|
|
bool Canvas::screenHasSRGB = false;
|
|
|
-int Canvas::switchCount = 0;
|
|
|
int Canvas::canvasCount = 0;
|
|
|
|
|
|
-static void getStrategy()
|
|
|
-{
|
|
|
- if (!strategy)
|
|
|
- {
|
|
|
- if (GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object)
|
|
|
- strategy = &strategyCorePacked;
|
|
|
- else if (GLAD_ES_VERSION_2_0)
|
|
|
- strategy = &strategyCore;
|
|
|
- else if (GLAD_EXT_framebuffer_object && GLAD_EXT_packed_depth_stencil)
|
|
|
- strategy = &strategyPackedEXT;
|
|
|
- else if (GLAD_EXT_framebuffer_object && strategyEXT.isSupported())
|
|
|
- strategy = &strategyEXT;
|
|
|
- else
|
|
|
- strategy = &strategyNone;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
Canvas::Canvas(int width, int height, Format format, int msaa)
|
|
|
: fbo(0)
|
|
|
, resolve_fbo(0)
|
|
@@ -459,7 +90,6 @@ Canvas::Canvas(int width, int height, Format format, int msaa)
|
|
|
, format(format)
|
|
|
, requested_samples(msaa)
|
|
|
, actual_samples(0)
|
|
|
- , msaa_dirty(false)
|
|
|
, texture_memory(0)
|
|
|
{
|
|
|
this->width = width;
|
|
@@ -488,8 +118,6 @@ Canvas::Canvas(int width, int height, Format format, int msaa)
|
|
|
vertices[3].s = 1;
|
|
|
vertices[3].t = 0;
|
|
|
|
|
|
- getStrategy();
|
|
|
-
|
|
|
loadVolatile();
|
|
|
|
|
|
++canvasCount;
|
|
@@ -517,7 +145,7 @@ bool Canvas::createMSAAFBO(GLenum internalformat)
|
|
|
}
|
|
|
|
|
|
// Create our FBO without a texture.
|
|
|
- status = strategy->createFBO(fbo, 0);
|
|
|
+ status = createFBO(fbo, 0);
|
|
|
|
|
|
GLuint previous = gl.getDefaultFBO();
|
|
|
if (current != this)
|
|
@@ -525,30 +153,28 @@ bool Canvas::createMSAAFBO(GLenum internalformat)
|
|
|
if (current != nullptr)
|
|
|
previous = current->fbo;
|
|
|
|
|
|
- strategy->bindFBO(fbo);
|
|
|
+ gl.bindFramebuffer(GL_FRAMEBUFFER, fbo);
|
|
|
}
|
|
|
|
|
|
// Create and attach the MSAA buffer for our FBO.
|
|
|
- if (strategy->createMSAABuffer(width, height, actual_samples, internalformat, msaa_buffer))
|
|
|
- status = GL_FRAMEBUFFER_COMPLETE;
|
|
|
- else
|
|
|
- status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
|
|
|
+ status = createMSAABuffer(width, height, actual_samples, internalformat, msaa_buffer);
|
|
|
|
|
|
// Create the FBO used for the MSAA resolve, and attach the texture.
|
|
|
if (status == GL_FRAMEBUFFER_COMPLETE)
|
|
|
- status = strategy->createFBO(resolve_fbo, texture);
|
|
|
+ status = createFBO(resolve_fbo, texture);
|
|
|
|
|
|
if (status != GL_FRAMEBUFFER_COMPLETE)
|
|
|
{
|
|
|
// Clean up.
|
|
|
- strategy->deleteFBO(fbo, 0, msaa_buffer);
|
|
|
- strategy->deleteFBO(resolve_fbo, 0, 0);
|
|
|
+ glDeleteFramebuffers(1, &fbo);
|
|
|
+ glDeleteFramebuffers(1, &resolve_fbo);
|
|
|
+ glDeleteRenderbuffers(1, &msaa_buffer);
|
|
|
fbo = msaa_buffer = resolve_fbo = 0;
|
|
|
actual_samples = 0;
|
|
|
}
|
|
|
|
|
|
if (current != this)
|
|
|
- strategy->bindFBO(previous);
|
|
|
+ gl.bindFramebuffer(GL_FRAMEBUFFER, previous);
|
|
|
|
|
|
return status == GL_FRAMEBUFFER_COMPLETE;
|
|
|
}
|
|
@@ -566,6 +192,8 @@ bool Canvas::loadVolatile()
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
+ // getMaxRenderbufferSamples will be 0 on systems that don't support
|
|
|
+ // multisampled renderbuffers / don't export FBO multisample extensions.
|
|
|
requested_samples = std::min(requested_samples, gl.getMaxRenderbufferSamples());
|
|
|
requested_samples = std::max(requested_samples, 0);
|
|
|
|
|
@@ -606,13 +234,13 @@ bool Canvas::loadVolatile()
|
|
|
// Try to create a MSAA FBO if requested. On failure (or no requested MSAA),
|
|
|
// fall back to a regular FBO.
|
|
|
if (!createMSAAFBO(internalformat))
|
|
|
- status = strategy->createFBO(fbo, texture);
|
|
|
+ status = createFBO(fbo, texture);
|
|
|
|
|
|
if (status != GL_FRAMEBUFFER_COMPLETE)
|
|
|
{
|
|
|
if (fbo != 0)
|
|
|
{
|
|
|
- strategy->deleteFBO(fbo, 0, 0);
|
|
|
+ glDeleteFramebuffers(1, &fbo);
|
|
|
fbo = 0;
|
|
|
}
|
|
|
return false;
|
|
@@ -620,8 +248,6 @@ bool Canvas::loadVolatile()
|
|
|
|
|
|
clear(Color(0, 0, 0, 0));
|
|
|
|
|
|
- msaa_dirty = (msaa_buffer != 0);
|
|
|
-
|
|
|
size_t prevmemsize = texture_memory;
|
|
|
|
|
|
texture_memory = (getFormatBitsPerPixel(format) * width * height) / 8;
|
|
@@ -635,13 +261,19 @@ bool Canvas::loadVolatile()
|
|
|
|
|
|
void Canvas::unloadVolatile()
|
|
|
{
|
|
|
- strategy->deleteFBO(fbo, depth_stencil, msaa_buffer);
|
|
|
- strategy->deleteFBO(resolve_fbo, 0, 0);
|
|
|
+ glDeleteFramebuffers(1, &fbo);
|
|
|
+ glDeleteFramebuffers(1, &resolve_fbo);
|
|
|
+
|
|
|
+ glDeleteRenderbuffers(1, &depth_stencil);
|
|
|
+ glDeleteRenderbuffers(1, &msaa_buffer);
|
|
|
|
|
|
gl.deleteTexture(texture);
|
|
|
|
|
|
- fbo = depth_stencil = texture = 0;
|
|
|
- resolve_fbo = msaa_buffer = 0;
|
|
|
+ fbo = 0;
|
|
|
+ resolve_fbo = 0;
|
|
|
+ depth_stencil = 0;
|
|
|
+ msaa_buffer = 0;
|
|
|
+ texture = 0;
|
|
|
|
|
|
attachedCanvases.clear();
|
|
|
|
|
@@ -725,10 +357,6 @@ GLuint Canvas::getGLTexture() const
|
|
|
|
|
|
void Canvas::predraw()
|
|
|
{
|
|
|
- // We need to make sure the texture is up-to-date by resolving the MSAA
|
|
|
- // buffer (which we render to when the canvas is active) to it.
|
|
|
- resolveMSAA();
|
|
|
-
|
|
|
gl.bindTexture(texture);
|
|
|
}
|
|
|
|
|
@@ -751,7 +379,7 @@ void Canvas::setupGrab()
|
|
|
current = this;
|
|
|
|
|
|
// bind the framebuffer object.
|
|
|
- strategy->bindFBO(fbo);
|
|
|
+ gl.bindFramebuffer(GL_FRAMEBUFFER, fbo);
|
|
|
gl.setViewport(OpenGL::Viewport(0, 0, width, height));
|
|
|
|
|
|
// Set up the projection matrix
|
|
@@ -766,9 +394,6 @@ void Canvas::setupGrab()
|
|
|
glDisable(GL_FRAMEBUFFER_SRGB);
|
|
|
}
|
|
|
|
|
|
- if (msaa_buffer != 0)
|
|
|
- msaa_dirty = true;
|
|
|
-
|
|
|
// We discard the stencil buffer in stopGrab, so we should clear it here
|
|
|
// to prevent the discarded (invalid) contents from being accidentally used.
|
|
|
if (depth_stencil != 0 && (GLAD_ES_VERSION_3_0 || GLAD_EXT_discard_framebuffer))
|
|
@@ -815,7 +440,22 @@ void Canvas::startGrab(const std::vector<Canvas *> &canvases)
|
|
|
return;
|
|
|
|
|
|
// Attach the canvas textures to the active FBO and set up MRTs.
|
|
|
- strategy->setAttachments(canvases);
|
|
|
+ std::vector<GLenum> drawbuffers;
|
|
|
+ drawbuffers.reserve(canvases.size() + 1);
|
|
|
+
|
|
|
+ drawbuffers.push_back(GL_COLOR_ATTACHMENT0);
|
|
|
+
|
|
|
+ // Attach the canvas textures to the currently bound framebuffer.
|
|
|
+ for (int i = 0; i < (int) canvases.size(); i++)
|
|
|
+ {
|
|
|
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1 + i,
|
|
|
+ GL_TEXTURE_2D, canvases[i]->getGLTexture(), 0);
|
|
|
+
|
|
|
+ drawbuffers.push_back(GL_COLOR_ATTACHMENT1 + i);
|
|
|
+ }
|
|
|
+
|
|
|
+ // set up multiple render targets
|
|
|
+ glDrawBuffers((int) drawbuffers.size(), &drawbuffers[0]);
|
|
|
|
|
|
// We want to avoid reference cycles, so we don't retain the attached
|
|
|
// Canvases here. The code in Graphics::setCanvas retains them.
|
|
@@ -830,8 +470,8 @@ void Canvas::startGrab()
|
|
|
if (attachedCanvases.size() == 0)
|
|
|
return;
|
|
|
|
|
|
- // make sure the FBO is only using a single canvas
|
|
|
- strategy->setAttachments();
|
|
|
+ // make sure the FBO is only using a single draw buffer
|
|
|
+ glDrawBuffer(GL_COLOR_ATTACHMENT0);
|
|
|
|
|
|
attachedCanvases.clear();
|
|
|
}
|
|
@@ -853,6 +493,9 @@ void Canvas::stopGrab(bool switchingToOtherCanvas)
|
|
|
glDiscardFramebufferEXT(GL_FRAMEBUFFER, 2, attachments);
|
|
|
}
|
|
|
|
|
|
+ // Make sure the canvas texture is up to date if we're using MSAA.
|
|
|
+ resolveMSAA(false);
|
|
|
+
|
|
|
gl.matrices.projection.pop_back();
|
|
|
|
|
|
if (switchingToOtherCanvas)
|
|
@@ -866,7 +509,7 @@ void Canvas::stopGrab(bool switchingToOtherCanvas)
|
|
|
else
|
|
|
{
|
|
|
// bind system framebuffer.
|
|
|
- strategy->bindFBO(gl.getDefaultFBO());
|
|
|
+ gl.bindFramebuffer(GL_FRAMEBUFFER, gl.getDefaultFBO());
|
|
|
current = nullptr;
|
|
|
gl.setViewport(systemViewport);
|
|
|
|
|
@@ -882,9 +525,6 @@ void Canvas::stopGrab(bool switchingToOtherCanvas)
|
|
|
|
|
|
void Canvas::clear(Color c)
|
|
|
{
|
|
|
- if (strategy == &strategyNone)
|
|
|
- return;
|
|
|
-
|
|
|
GLuint previous = gl.getDefaultFBO();
|
|
|
|
|
|
if (current != this)
|
|
@@ -892,7 +532,7 @@ void Canvas::clear(Color c)
|
|
|
if (current != nullptr)
|
|
|
previous = current->fbo;
|
|
|
|
|
|
- strategy->bindFBO(fbo);
|
|
|
+ gl.bindFramebuffer(GL_FRAMEBUFFER, fbo);
|
|
|
}
|
|
|
|
|
|
GLfloat glcolor[] = {c.r/255.f, c.g/255.f, c.b/255.f, c.a/255.f};
|
|
@@ -911,28 +551,40 @@ void Canvas::clear(Color c)
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- // glClear will clear all active draw buffers, so we need to temporarily
|
|
|
- // detach any other canvases (when MRT is being used.)
|
|
|
+ // glClear will clear all active draw buffers, so should make sure only
|
|
|
+ // the draw buffer corresponding to our canvas' texture is active.
|
|
|
if (attachedCanvases.size() > 0)
|
|
|
- strategy->setAttachments();
|
|
|
+ glDrawBuffer(GL_COLOR_ATTACHMENT0);
|
|
|
|
|
|
// Don't use the state-shadowed gl.setClearColor because we want to save
|
|
|
// the previous clear color.
|
|
|
glClearColor(glcolor[0], glcolor[1], glcolor[2], glcolor[3]);
|
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
|
|
+ // Restore the draw buffers for rendering to multiple textures.
|
|
|
if (attachedCanvases.size() > 0)
|
|
|
- strategy->setAttachments(attachedCanvases);
|
|
|
+ {
|
|
|
+ std::vector<GLenum> drawbuffers;
|
|
|
+ drawbuffers.reserve(attachedCanvases.size() + 1);
|
|
|
+
|
|
|
+ for (int i = 0; i < (int) attachedCanvases.size() + 1; i++)
|
|
|
+ drawbuffers.push_back(GL_COLOR_ATTACHMENT0 + i);
|
|
|
+
|
|
|
+ glDrawBuffers((GLsizei) drawbuffers.size(), &drawbuffers[0]);
|
|
|
+ }
|
|
|
|
|
|
// Restore the global clear color.
|
|
|
gl.setClearColor(gl.getClearColor());
|
|
|
}
|
|
|
|
|
|
if (current != this)
|
|
|
- strategy->bindFBO(previous);
|
|
|
+ {
|
|
|
+ // If we're not the active canvas, we need to do a MSAA resolve after
|
|
|
+ // clearing the FBO since the resolve texture might be used afterward.
|
|
|
+ resolveMSAA(false);
|
|
|
|
|
|
- if (msaa_buffer != 0)
|
|
|
- msaa_dirty = true;
|
|
|
+ gl.bindFramebuffer(GL_FRAMEBUFFER, previous);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
bool Canvas::checkCreateStencil()
|
|
@@ -942,20 +594,47 @@ bool Canvas::checkCreateStencil()
|
|
|
return true;
|
|
|
|
|
|
if (current != this)
|
|
|
- strategy->bindFBO(fbo);
|
|
|
+ gl.bindFramebuffer(GL_FRAMEBUFFER, fbo);
|
|
|
|
|
|
- bool success = strategy->createStencil(width, height, requested_samples, depth_stencil);
|
|
|
+ GLenum format = GL_STENCIL_INDEX8;
|
|
|
+ GLenum attachment = GL_STENCIL_ATTACHMENT;
|
|
|
|
|
|
- if (success)
|
|
|
+ // Prefer a combined depth/stencil buffer.
|
|
|
+ if (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object
|
|
|
+ || GLAD_EXT_packed_depth_stencil || GLAD_OES_packed_depth_stencil)
|
|
|
{
|
|
|
- // We don't want the stencil buffer filled with garbage.
|
|
|
+ format = GL_DEPTH_STENCIL;
|
|
|
+ attachment = GL_DEPTH_STENCIL_ATTACHMENT;
|
|
|
+ }
|
|
|
+
|
|
|
+ glGenRenderbuffers(1, &depth_stencil);
|
|
|
+ glBindRenderbuffer(GL_RENDERBUFFER, depth_stencil);
|
|
|
+
|
|
|
+ if (requested_samples > 1)
|
|
|
+ glRenderbufferStorageMultisample(GL_RENDERBUFFER, requested_samples, format, width, height);
|
|
|
+ else
|
|
|
+ glRenderbufferStorage(GL_RENDERBUFFER, format, width, height);
|
|
|
+
|
|
|
+ // Attach the stencil buffer to the framebuffer object.
|
|
|
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, depth_stencil);
|
|
|
+
|
|
|
+ glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
|
|
+
|
|
|
+ bool success = glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE;
|
|
|
+
|
|
|
+ // We don't want the stencil buffer filled with garbage.
|
|
|
+ if (success)
|
|
|
glClear(GL_STENCIL_BUFFER_BIT);
|
|
|
+ else
|
|
|
+ {
|
|
|
+ glDeleteRenderbuffers(1, &depth_stencil);
|
|
|
+ depth_stencil = 0;
|
|
|
}
|
|
|
|
|
|
if (current && current != this)
|
|
|
- strategy->bindFBO(current->fbo);
|
|
|
+ gl.bindFramebuffer(GL_FRAMEBUFFER, current->fbo);
|
|
|
else if (!current)
|
|
|
- strategy->bindFBO(gl.getDefaultFBO());
|
|
|
+ gl.bindFramebuffer(GL_FRAMEBUFFER, gl.getDefaultFBO());
|
|
|
|
|
|
return success;
|
|
|
}
|
|
@@ -965,8 +644,6 @@ love::image::ImageData *Canvas::newImageData(love::image::Image *image, int x, i
|
|
|
if (x < 0 || y < 0 || w <= 0 || h <= 0 || (x + w) > width || (y + h) > height)
|
|
|
throw love::Exception("Invalid ImageData rectangle dimensions.");
|
|
|
|
|
|
- resolveMSAA();
|
|
|
-
|
|
|
int row = 4 * w;
|
|
|
int size = row * h;
|
|
|
GLubyte *pixels = nullptr;
|
|
@@ -979,68 +656,50 @@ love::image::ImageData *Canvas::newImageData(love::image::Image *image, int x, i
|
|
|
throw love::Exception("Out of memory.");
|
|
|
}
|
|
|
|
|
|
+ // Make sure the canvas texture is up to date if we're using MSAA.
|
|
|
+ if (current == this)
|
|
|
+ resolveMSAA(false);
|
|
|
+
|
|
|
// Our texture is attached to 'resolve_fbo' when we use MSAA.
|
|
|
- if (resolve_fbo != 0 && (GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object))
|
|
|
- glBindFramebuffer(GL_READ_FRAMEBUFFER, resolve_fbo);
|
|
|
- else if (resolve_fbo != 0 && GLAD_EXT_framebuffer_multisample)
|
|
|
- glBindFramebufferEXT(GL_READ_FRAMEBUFFER, resolve_fbo);
|
|
|
+ if (resolve_fbo != 0)
|
|
|
+ gl.bindFramebuffer(GL_READ_FRAMEBUFFER, resolve_fbo);
|
|
|
else
|
|
|
- strategy->bindFBO(fbo);
|
|
|
+ gl.bindFramebuffer(GL_FRAMEBUFFER, fbo);
|
|
|
|
|
|
glReadPixels(x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
|
|
|
|
|
- if (current)
|
|
|
- strategy->bindFBO(current->fbo);
|
|
|
- else
|
|
|
- strategy->bindFBO(gl.getDefaultFBO());
|
|
|
+ GLuint prevfbo = current ? current->fbo : gl.getDefaultFBO();
|
|
|
+ gl.bindFramebuffer(GL_FRAMEBUFFER, prevfbo);
|
|
|
|
|
|
// The new ImageData now owns the pixel data, so we don't delete it here.
|
|
|
return image->newImageData(w, h, pixels, true);
|
|
|
}
|
|
|
|
|
|
-bool Canvas::resolveMSAA()
|
|
|
+bool Canvas::resolveMSAA(bool restoreprev)
|
|
|
{
|
|
|
if (resolve_fbo == 0 || msaa_buffer == 0)
|
|
|
return false;
|
|
|
|
|
|
- if (!msaa_dirty)
|
|
|
- return true;
|
|
|
-
|
|
|
- GLuint previous = gl.getDefaultFBO();
|
|
|
- if (current != nullptr)
|
|
|
- previous = current->fbo;
|
|
|
+ GLint w = width;
|
|
|
+ GLint h = height;
|
|
|
|
|
|
// Do the MSAA resolve by blitting the MSAA renderbuffer to the texture.
|
|
|
- // For many of the extensions that add suffixes to the functions, we assign
|
|
|
- // function pointers in OpenGL.cpp so we can call the core functions.
|
|
|
- if (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object
|
|
|
- || GLAD_ANGLE_framebuffer_multisample)
|
|
|
- {
|
|
|
- glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
|
|
|
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolve_fbo);
|
|
|
- glBlitFramebuffer(0, 0, width, height, 0, 0, width, height,
|
|
|
- GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
|
|
- }
|
|
|
- else if (GLAD_EXT_framebuffer_multisample && GLAD_EXT_framebuffer_blit)
|
|
|
- {
|
|
|
- glBindFramebufferEXT(GL_READ_FRAMEBUFFER, fbo);
|
|
|
- glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, resolve_fbo);
|
|
|
- glBlitFramebufferEXT(0, 0, width, height, 0, 0, width, height,
|
|
|
- GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
|
|
- }
|
|
|
- else if (GLAD_APPLE_framebuffer_multisample)
|
|
|
- {
|
|
|
- glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
|
|
|
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolve_fbo);
|
|
|
+ // For many of the MSAA extensions that add suffixes to the functions, we
|
|
|
+ // assign function pointers in OpenGL.cpp so we can call the core functions.
|
|
|
+
|
|
|
+ gl.bindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
|
|
|
+ gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, resolve_fbo);
|
|
|
+
|
|
|
+ if (GLAD_APPLE_framebuffer_multisample)
|
|
|
glResolveMultisampleFramebufferAPPLE();
|
|
|
- }
|
|
|
else
|
|
|
- return false;
|
|
|
-
|
|
|
- strategy->bindFBO(previous);
|
|
|
+ glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
|
|
|
|
|
- if (current != this)
|
|
|
- msaa_dirty = false;
|
|
|
+ if (restoreprev)
|
|
|
+ {
|
|
|
+ GLuint fbo = current ? current->fbo : gl.getDefaultFBO();
|
|
|
+ gl.bindFramebuffer(GL_FRAMEBUFFER, fbo);
|
|
|
+ }
|
|
|
|
|
|
return true;
|
|
|
}
|
|
@@ -1141,8 +800,7 @@ size_t Canvas::getFormatBitsPerPixel(Format format)
|
|
|
|
|
|
bool Canvas::isSupported()
|
|
|
{
|
|
|
- getStrategy();
|
|
|
- return (strategy != &strategyNone);
|
|
|
+ return GLAD_ES_VERSION_2_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object || GLAD_EXT_framebuffer_object;
|
|
|
}
|
|
|
|
|
|
bool Canvas::isMultiCanvasSupported()
|
|
@@ -1237,8 +895,8 @@ bool Canvas::isFormatSupported(Canvas::Format format)
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, internalformat, 2, 2, 0, externalformat, textype, nullptr);
|
|
|
|
|
|
GLuint fbo = 0;
|
|
|
- supported = (strategy->createFBO(fbo, texture) == GL_FRAMEBUFFER_COMPLETE);
|
|
|
- strategy->deleteFBO(fbo, 0, 0);
|
|
|
+ supported = (createFBO(fbo, texture) == GL_FRAMEBUFFER_COMPLETE);
|
|
|
+ glDeleteFramebuffers(1, &fbo);
|
|
|
|
|
|
gl.deleteTexture(texture);
|
|
|
|