Added MRT support to canvases via love.graphics.setCanvases(c1, c2, ...), and an 'effects' callback function in shaders.

- Simultanious rendering to multiple canvases requires all canvases to be the same size (limitation of pre-GL3.0 framebuffers)
- love.graphics.isSupported("mrtcanvas") was added
- love_Canvases[n] (0-based array of active canvases) can be written to in pixel shaders in the 'effects' callback function
- everything drawn to multiple canvases at once will be duplicated to all canvases if no shader is active or the standard 'effect' shader function is used

--HG--
branch : MRTs
This commit is contained in:
Alex Szpakowski
2013-03-24 02:20:15 -03:00
parent 2009124cf9
commit 2ea065c68b
9 changed files with 377 additions and 87 deletions
+130 -2
View File
@@ -59,6 +59,12 @@ struct FramebufferStrategy
*/
virtual void deleteFBO(GLuint, GLuint, GLuint) {}
virtual void bindFBO(GLuint) {}
/// attach additional canvases to this framebuffer for rendering
/**
* @param[in] canvases List of canvases to attach
**/
virtual void setAttachments(const std::vector<Canvas *> &canvases) {}
};
struct FramebufferStrategyGL3 : public FramebufferStrategy
@@ -127,6 +133,35 @@ struct FramebufferStrategyGL3 : public FramebufferStrategy
{
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
}
virtual void setAttachments(const std::vector<Canvas *> &canvases)
{
if (canvases.size() == 0)
{
// set a single render target
glDrawBuffer(GL_COLOR_ATTACHMENT0);
return;
}
std::vector<GLenum> drawbuffers;
drawbuffers.push_back(GL_COLOR_ATTACHMENT0);
// attach the canvas framebuffer textures to the currently bound framebuffer
for (int i = 0; i < canvases.size(); i++)
{
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1 + i,
GL_TEXTURE_2D, canvases[i]->getTextureName(), 0);
drawbuffers.push_back(GL_COLOR_ATTACHMENT1 + i);
}
// set up multiple render targets
if (GLEE_VERSION_2_0)
glDrawBuffers(drawbuffers.size(), &drawbuffers[0]);
else if (GLEE_ARB_draw_buffers)
glDrawBuffersARB(drawbuffers.size(), &drawbuffers[0]);
else if (GLEE_ATI_draw_buffers)
glDrawBuffersATI(drawbuffers.size(), &drawbuffers[0]);
}
};
struct FramebufferStrategyPackedEXT : public FramebufferStrategy
@@ -196,6 +231,35 @@ struct FramebufferStrategyPackedEXT : public FramebufferStrategy
{
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer);
}
virtual void setAttachments(const std::vector<Canvas *> &canvases)
{
if (canvases.size() == 0)
{
// set a single render target
glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
return;
}
std::vector<GLenum> drawbuffers;
drawbuffers.push_back(GL_COLOR_ATTACHMENT0_EXT);
// attach the canvas framebuffer textures to the currently bound framebuffer
for (int i = 0; i < canvases.size(); i++)
{
glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1_EXT + i,
GL_TEXTURE_2D, canvases[i]->getTextureName(), 0);
drawbuffers.push_back(GL_COLOR_ATTACHMENT1_EXT + i);
}
// set up multiple render targets
if (GLEE_VERSION_2_0)
glDrawBuffers(drawbuffers.size(), &drawbuffers[0]);
else if (GLEE_ARB_draw_buffers)
glDrawBuffersARB(drawbuffers.size(), &drawbuffers[0]);
else if (GLEE_ATI_draw_buffers)
glDrawBuffersATI(drawbuffers.size(), &drawbuffers[0]);
}
};
struct FramebufferStrategyEXT : public FramebufferStrategyPackedEXT
@@ -288,6 +352,9 @@ static void getStrategy()
}
}
static int maxFBOColorAttachments = 0;
static int maxDrawBuffers = 0;
Canvas::Canvas(int width, int height, TextureType texture_type)
: width(width)
, height(height)
@@ -338,11 +405,26 @@ bool Canvas::isSupported()
return (strategy != &strategyNone);
}
bool Canvas::isHdrSupported()
bool Canvas::isHDRSupported()
{
return GLEE_VERSION_3_0 || GLEE_ARB_texture_float;
}
bool Canvas::isMRTSupported()
{
if (!(isSupported() && (GLEE_VERSION_2_0 || GLEE_ARB_draw_buffers || GLEE_ATI_draw_buffers)))
return false;
if (maxFBOColorAttachments == 0 || maxDrawBuffers == 0)
{
glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &maxFBOColorAttachments);
glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers);
}
// system must support at least 4 simultanious render targets
return maxFBOColorAttachments >= 4 && maxDrawBuffers >= 4;
}
void Canvas::bindDefaultCanvas()
{
if (current != NULL)
@@ -379,6 +461,42 @@ void Canvas::startGrab()
current = this;
}
void Canvas::startGrab(const std::vector<Canvas *> &canvases)
{
if (canvases.size() > 0)
{
if (!isMRTSupported())
throw love::Exception("Multiple render targets are not supported on this system.");
if (canvases.size() + 1 > maxDrawBuffers || canvases.size() + 1 > maxFBOColorAttachments)
throw love::Exception("This system can't support %d simultanious render targets.", canvases.size() + 1);
}
for (size_t i = 0; i < canvases.size(); i++)
{
if (canvases[i]->getWidth() != width || canvases[i]->getHeight() != height)
throw love::Exception("All canvas arguments must have the same dimensions.");
}
startGrab();
// don't attach anything if there's nothing to attach/detach
if (canvases.size() == 0 && attachedCanvases.size() == 0)
return;
strategy->setAttachments(canvases);
// retain newly attached canvases
for (size_t i = 0; i < canvases.size(); i++)
canvases[i]->retain();
// release previously attached canvases
for (size_t i = 0; i < attachedCanvases.size(); i++)
attachedCanvases[i]->release();
attachedCanvases = canvases;
}
void Canvas::stopGrab()
{
// i am not grabbing. leave me alone
@@ -393,7 +511,6 @@ void Canvas::stopGrab()
current = NULL;
}
void Canvas::clear(const Color &c)
{
GLuint previous = 0;
@@ -472,6 +589,11 @@ void Canvas::getPixel(unsigned char* pixel_rgba, int x, int y)
strategy->bindFBO( 0 );
}
const std::vector<Canvas *> &Canvas::getAttachedCanvases() const
{
return attachedCanvases;
}
void Canvas::setFilter(const Image::Filter &f)
{
bindTexture(img);
@@ -515,6 +637,12 @@ void Canvas::unloadVolatile()
settings.filter = getFilter();
settings.wrap = getWrap();
strategy->deleteFBO(fbo, depth_stencil, img);
// release attached canvases
for (size_t i = 0; i < attachedCanvases.size(); i++)
attachedCanvases[i]->release();
attachedCanvases.clear();
}
int Canvas::getWidth()