Cleaned up the Canvas code.

--HG--
branch : minor
This commit is contained in:
Alex Szpakowski
2015-02-26 17:06:09 -04:00
parent a66a7b7ce9
commit f83c2db471
5 changed files with 205 additions and 524 deletions
+157 -499
View File
@@ -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() {}
// get currently bound fbo to reset to it later
GLint current_fbo;
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &current_fbo);
/// 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;
}
glGenFramebuffers(1, &framebuffer);
gl.bindFramebuffer(GL_FRAMEBUFFER, framebuffer);
/// 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;
}
if (texture != 0)
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
/// 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;
}
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
/// 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) {}
// unbind framebuffer
gl.bindFramebuffer(GL_FRAMEBUFFER, (GLuint) current_fbo);
/// attach additional canvases to the active framebuffer for rendering
/**
* @param[in] canvases List of canvases to attach
**/
virtual void setAttachments(const std::vector<Canvas *> &) {}
return status;
}
/// stop using all additional attached canvases
virtual void setAttachments() {}
};
struct FramebufferStrategyCorePacked : public FramebufferStrategy
static GLenum createMSAABuffer(int width, int height, int &samples, GLenum iformat, GLuint &buffer)
{
virtual GLenum createFBO(GLuint &framebuffer, GLuint texture)
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);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE)
{
// get currently bound fbo to reset to it later
GLint current_fbo;
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &current_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;
glDeleteRenderbuffers(1, &buffer);
buffer = 0;
}
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, &current_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);
if (glCheckFramebufferStatusEXT(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
{
glDeleteRenderbuffersEXT(1, &buffer);
buffer = 0;
return false;
}
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);
}
virtual void bindFBO(GLuint framebuffer)
{
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer);
++Canvas::switchCount;
}
virtual void setAttachments()
{
// set a single render target
glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
}
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]);
}
};
struct FramebufferStrategyEXT : public FramebufferStrategyPackedEXT
{
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);
if (samples > 1)
{
glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, samples,
GL_STENCIL_INDEX, width, height);
}
else
{
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_STENCIL_INDEX,
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;
}
bool isSupported()
{
GLuint fb = 0, stencil = 0;
GLenum status = createFBO(fb, 0);
deleteFBO(fb, stencil, 0);
return status == GL_FRAMEBUFFER_COMPLETE;
}
};
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;
glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
strategy->bindFBO(previous);
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);
+1 -4
View File
@@ -113,8 +113,6 @@ public:
return actual_samples;
}
bool resolveMSAA();
static bool isSupported();
static bool isMultiCanvasSupported();
static bool isFormatSupported(Format format);
@@ -127,7 +125,6 @@ public:
// Whether the main screen should have linear -> sRGB conversions enabled.
static bool screenHasSRGB;
static int switchCount;
static int canvasCount;
static bool getConstant(const char *in, Format &out);
@@ -136,6 +133,7 @@ public:
private:
bool createMSAAFBO(GLenum internalformat);
bool resolveMSAA(bool restoreprev);
static Format getSizedFormat(Format format);
static void convertFormat(Format format, GLenum &internalformat, GLenum &externalformat, GLenum &type);
@@ -156,7 +154,6 @@ private:
int requested_samples;
int actual_samples;
bool msaa_dirty;
size_t texture_memory;
+2 -2
View File
@@ -471,7 +471,7 @@ void Graphics::present()
// Reset the per-frame stat counts.
gl.stats.drawCalls = 0;
Canvas::switchCount = 0;
gl.stats.framebufferBinds = 0;
}
int Graphics::getWidth() const
@@ -1267,7 +1267,7 @@ Graphics::Stats Graphics::getStats() const
Stats stats;
stats.drawCalls = gl.stats.drawCalls;
stats.canvasSwitches = Canvas::switchCount;
stats.canvasSwitches = gl.stats.framebufferBinds;
stats.canvases = Canvas::canvasCount;
stats.images = Image::imageCount;
stats.fonts = Font::fontCount;
+39 -19
View File
@@ -196,17 +196,42 @@ void OpenGL::initVendor()
void OpenGL::initOpenGLFunctions()
{
// Alias extension-suffixed framebuffer functions to core versions since
// there are so many different-named extensions that do the same things...
if (!(GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object))
{
// There are like 100 different suffixed versions of glRenderbufferStorageMultisample,
// so we'll just assign all the ones we use to the core function pointer.
if (GLAD_APPLE_framebuffer_multisample)
fp_glRenderbufferStorageMultisample = (pfn_glRenderbufferStorageMultisample) fp_glRenderbufferStorageMultisampleAPPLE;
else if (GLAD_ANGLE_framebuffer_multisample)
fp_glRenderbufferStorageMultisample = (pfn_glRenderbufferStorageMultisample) fp_glRenderbufferStorageMultisampleANGLE;
if (GLAD_VERSION_1_0 && GLAD_EXT_framebuffer_object)
{
fp_glBindRenderbuffer = fp_glBindRenderbufferEXT;
fp_glDeleteRenderbuffers = fp_glDeleteRenderbuffersEXT;
fp_glGenRenderbuffers = fp_glGenRenderbuffersEXT;
fp_glRenderbufferStorage = fp_glRenderbufferStorageEXT;
fp_glGetRenderbufferParameteriv = fp_glGetRenderbufferParameterivEXT;
fp_glBindFramebuffer = fp_glBindFramebufferEXT;
fp_glDeleteFramebuffers = fp_glDeleteFramebuffersEXT;
fp_glGenFramebuffers = fp_glGenFramebuffersEXT;
fp_glCheckFramebufferStatus = fp_glCheckFramebufferStatusEXT;
fp_glFramebufferTexture2D = fp_glFramebufferTexture2DEXT;
fp_glFramebufferRenderbuffer = fp_glFramebufferRenderbufferEXT;
fp_glGetFramebufferAttachmentParameteriv = fp_glGetFramebufferAttachmentParameterivEXT;
fp_glGenerateMipmap = fp_glGenerateMipmapEXT;
}
if (GLAD_ANGLE_framebuffer_blit)
fp_glBlitFramebuffer = (pfn_glBlitFramebuffer) fp_glBlitFramebufferANGLE;
if (GLAD_EXT_framebuffer_blit)
fp_glBlitFramebuffer = fp_glBlitFramebufferEXT;
else if (GLAD_ANGLE_framebuffer_blit)
fp_glBlitFramebuffer = fp_glBlitFramebufferANGLE;
else if (GLAD_NV_framebuffer_blit)
fp_glBlitFramebuffer = fp_glBlitFramebufferNV;
if (GLAD_EXT_framebuffer_multisample)
fp_glRenderbufferStorageMultisample = fp_glRenderbufferStorageMultisampleEXT;
else if (GLAD_APPLE_framebuffer_multisample)
fp_glRenderbufferStorageMultisample = fp_glRenderbufferStorageMultisampleAPPLE;
else if (GLAD_ANGLE_framebuffer_multisample)
fp_glRenderbufferStorageMultisample = fp_glRenderbufferStorageMultisampleANGLE;
else if (GLAD_NV_framebuffer_multisample)
fp_glRenderbufferStorageMultisample = fp_glRenderbufferStorageMultisampleNV;
}
}
@@ -297,17 +322,6 @@ void OpenGL::prepareDraw()
// Make sure the active shader has the correct values for its
// love-provided uniforms.
shader->checkSetScreenParams();
// We need to make sure antialiased Canvases are properly resolved
// before sampling from their textures in a shader.
// This is kind of a big hack. :(
for (auto &r : shader->getBoundRetainables())
{
// Even bigger hack! D:
Canvas *canvas = dynamic_cast<Canvas *>(r.second);
if (canvas != nullptr)
canvas->resolveMSAA();
}
}
const Matrix &curproj = matrices.projection.back();
@@ -445,6 +459,12 @@ float OpenGL::getPointSize() const
return state.pointSize;
}
void OpenGL::bindFramebuffer(GLenum target, GLuint framebuffer)
{
glBindFramebuffer(target, framebuffer);
++stats.framebufferBinds;
}
GLuint OpenGL::getDefaultFBO() const
{
#ifdef LOVE_IOS
+6
View File
@@ -148,6 +148,7 @@ public:
{
size_t textureMemory;
int drawCalls;
int framebufferBinds;
} stats;
OpenGL();
@@ -250,6 +251,11 @@ public:
**/
float getPointSize() const;
/**
* Binds a Framebuffer Object to the specified target.
**/
void bindFramebuffer(GLenum target, GLuint framebuffer);
/**
* This will usually be 0 (system drawable), but some platforms require a
* non-zero FBO for rendering.