From 7556fde5e691d98be06f73050d5a99ce231156ba Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Mon, 27 Jan 2014 01:31:41 -0400 Subject: [PATCH] Added antialiasing (MSAA) support to Canvases via a new optional parameter. Added Canvas:getFSAA. --- src/modules/graphics/Drawable.h | 2 +- src/modules/graphics/Texture.h | 2 +- src/modules/graphics/opengl/Canvas.cpp | 269 ++++++++++++++++-- src/modules/graphics/opengl/Canvas.h | 25 +- src/modules/graphics/opengl/Graphics.cpp | 4 +- src/modules/graphics/opengl/Graphics.h | 2 +- src/modules/graphics/opengl/Image.cpp | 10 +- src/modules/graphics/opengl/Image.h | 10 +- src/modules/graphics/opengl/Mesh.cpp | 2 +- src/modules/graphics/opengl/Mesh.h | 2 +- src/modules/graphics/opengl/OpenGL.cpp | 12 + .../graphics/opengl/ParticleSystem.cpp | 2 +- src/modules/graphics/opengl/ParticleSystem.h | 2 +- src/modules/graphics/opengl/Shader.cpp | 5 + src/modules/graphics/opengl/Shader.h | 2 + src/modules/graphics/opengl/SpriteBatch.cpp | 2 +- src/modules/graphics/opengl/SpriteBatch.h | 2 +- src/modules/graphics/opengl/Texture.h | 4 +- src/modules/graphics/opengl/wrap_Canvas.cpp | 8 + src/modules/graphics/opengl/wrap_Canvas.h | 1 + src/modules/graphics/opengl/wrap_Graphics.cpp | 7 +- 21 files changed, 321 insertions(+), 54 deletions(-) diff --git a/src/modules/graphics/Drawable.h b/src/modules/graphics/Drawable.h index aea03b340..a4cd7e2ff 100644 --- a/src/modules/graphics/Drawable.h +++ b/src/modules/graphics/Drawable.h @@ -55,7 +55,7 @@ public: * @param kx Shear along the x-axis. * @param ky Shear along the y-axis. **/ - virtual void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const = 0; + virtual void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) = 0; }; } // graphics diff --git a/src/modules/graphics/Texture.h b/src/modules/graphics/Texture.h index e87c432f4..7b5a525a4 100644 --- a/src/modules/graphics/Texture.h +++ b/src/modules/graphics/Texture.h @@ -88,7 +88,7 @@ public: * @param kx Shear along the x-axis. * @param ky Shear along the y-axis. **/ - virtual void drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const = 0; + virtual void drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) = 0; virtual int getWidth() const; virtual int getHeight() const; diff --git a/src/modules/graphics/opengl/Canvas.cpp b/src/modules/graphics/opengl/Canvas.cpp index 855830dd1..334f16103 100644 --- a/src/modules/graphics/opengl/Canvas.cpp +++ b/src/modules/graphics/opengl/Canvas.cpp @@ -63,12 +63,26 @@ struct FramebufferStrategy 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) {} + virtual void deleteFBO(GLuint, GLuint, GLuint) {} virtual void bindFBO(GLuint) {} /// attach additional canvases to the active framebuffer for rendering @@ -93,8 +107,11 @@ struct FramebufferStrategyGL3 : public FramebufferStrategy glGenFramebuffers(1, &framebuffer); glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, texture, 0); + if (texture != 0) + { + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, texture, 0); + } // check status GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); @@ -118,13 +135,44 @@ struct FramebufferStrategyGL3 : public FramebufferStrategy glBindRenderbuffer(GL_RENDERBUFFER, 0); // check status - return glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE; + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + { + glDeleteRenderbuffers(1, &stencil); + stencil = 0; + return false; + } + + return true; } - virtual void deleteFBO(GLuint framebuffer, GLuint depth_stencil) + virtual bool createMSAABuffer(int width, int height, int &samples, GLenum internalformat, GLuint &buffer) + { + glGenRenderbuffers(1, &buffer); + glBindRenderbuffer(GL_RENDERBUFFER, buffer); + glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, internalformat, + 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); } @@ -192,7 +240,7 @@ struct FramebufferStrategyPackedEXT : public FramebufferStrategy virtual bool createStencil(int width, int height, GLuint &stencil) { // create combined depth/stencil buffer - glDeleteRenderbuffers(1, &stencil); + glDeleteRenderbuffersEXT(1, &stencil); glGenRenderbuffersEXT(1, &stencil); glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, stencil); glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_STENCIL_EXT, @@ -203,13 +251,47 @@ struct FramebufferStrategyPackedEXT : public FramebufferStrategy glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); // check status - return glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) == GL_FRAMEBUFFER_COMPLETE_EXT; + if (glCheckFramebufferStatusEXT(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + { + glDeleteRenderbuffersEXT(1, &stencil); + stencil = 0; + return false; + } + + return true; } - virtual void deleteFBO(GLuint framebuffer, GLuint depth_stencil) + virtual bool createMSAABuffer(int width, int height, int &samples, GLenum internalformat, GLuint &buffer) + { + if (!GLEE_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); } @@ -257,7 +339,7 @@ struct FramebufferStrategyEXT : public FramebufferStrategyPackedEXT virtual bool createStencil(int width, int height, GLuint &stencil) { // create stencil buffer - glDeleteRenderbuffers(1, &stencil); + glDeleteRenderbuffersEXT(1, &stencil); glGenRenderbuffersEXT(1, &stencil); glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, stencil); glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_STENCIL_INDEX, @@ -268,14 +350,21 @@ struct FramebufferStrategyEXT : public FramebufferStrategyPackedEXT glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); // check status - return glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) == GL_FRAMEBUFFER_COMPLETE_EXT; + 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); + deleteFBO(fb, stencil, 0); return status == GL_FRAMEBUFFER_COMPLETE; } }; @@ -311,11 +400,15 @@ static void getStrategy() static int maxFBOColorAttachments = 0; static int maxDrawBuffers = 0; -Canvas::Canvas(int width, int height, TextureType texture_type) +Canvas::Canvas(int width, int height, TextureType texture_type, int fsaa) : fbo(0) + , resolve_fbo(0) , texture(0) + , fsaa_buffer(0) , depth_stencil(0) , texture_type(texture_type) + , fsaa_samples(fsaa) + , fsaa_dirty(false) { this->width = width; this->height = height; @@ -357,9 +450,50 @@ Canvas::~Canvas() unloadVolatile(); } +bool Canvas::createFSAAFBO(GLenum internalformat) +{ + // Create our FBO without a texture. + status = strategy->createFBO(fbo, 0); + + GLuint previous = 0; + if (current != this) + { + if (current != nullptr) + previous = current->fbo; + + strategy->bindFBO(fbo); + } + + // Create and attach the MSAA buffer for our FBO. + if (strategy->createMSAABuffer(width, height, fsaa_samples, internalformat, fsaa_buffer)) + status = GL_FRAMEBUFFER_COMPLETE; + else + status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; + + // Create the FBO used for the MSAA resolve, and attach the texture. + if (status == GL_FRAMEBUFFER_COMPLETE) + status = strategy->createFBO(resolve_fbo, texture); + + if (status != GL_FRAMEBUFFER_COMPLETE) + { + // Clean up. + strategy->deleteFBO(fbo, 0, fsaa_buffer); + strategy->deleteFBO(resolve_fbo, 0, 0); + fbo = fsaa_buffer = resolve_fbo = 0; + fsaa_samples = 0; + } + + if (current != this) + strategy->bindFBO(previous); + + return status == GL_FRAMEBUFFER_COMPLETE; +} + bool Canvas::loadVolatile() { fbo = depth_stencil = texture = 0; + resolve_fbo = fsaa_buffer = 0; + status = GL_FRAMEBUFFER_COMPLETE; // glTexImage2D is guaranteed to error in this case. if (width > gl.getMaxTextureSize() || height > gl.getMaxTextureSize()) @@ -406,7 +540,25 @@ bool Canvas::loadVolatile() return false; } - status = strategy->createFBO(fbo, texture); + int max_samples = 0; + if (GLEE_VERSION_3_0 || GLEE_ARB_framebuffer_object + || GLEE_EXT_framebuffer_multisample) + { + glGetIntegerv(GL_MAX_SAMPLES, &max_samples); + } + + if (fsaa_samples > max_samples) + fsaa_samples = max_samples; + + // Try to create a FSAA FBO if requested. + bool fsaasuccess = false; + if (fsaa_samples > 1) + fsaasuccess = createFSAAFBO(internalformat); + + // On failure (or no requested FSAA), fall back to a regular FBO. + if (!fsaasuccess) + status = strategy->createFBO(fbo, texture); + if (status != GL_FRAMEBUFFER_COMPLETE) return false; @@ -416,11 +568,13 @@ bool Canvas::loadVolatile() void Canvas::unloadVolatile() { - strategy->deleteFBO(fbo, depth_stencil); + strategy->deleteFBO(fbo, depth_stencil, fsaa_buffer); + strategy->deleteFBO(resolve_fbo, 0, 0); gl.deleteTexture(texture); fbo = depth_stencil = texture = 0; + resolve_fbo = fsaa_buffer = 0; for (size_t i = 0; i < attachedCanvases.size(); i++) attachedCanvases[i]->release(); @@ -428,13 +582,12 @@ void Canvas::unloadVolatile() attachedCanvases.clear(); } -void Canvas::drawv(const Matrix &t, const Vertex *v) const +void Canvas::drawv(const Matrix &t, const Vertex *v) { glPushMatrix(); - glMultMatrixf((const GLfloat *)t.getElements()); - gl.bindTexture(texture); + predraw(); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); @@ -447,11 +600,13 @@ void Canvas::drawv(const Matrix &t, const Vertex *v) const glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); + + postdraw(); glPopMatrix(); } -void Canvas::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const +void Canvas::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) { static Matrix t; t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky); @@ -459,7 +614,7 @@ void Canvas::draw(float x, float y, float angle, float sx, float sy, float ox, f drawv(t, vertices); } -void Canvas::drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const +void Canvas::drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) { static Matrix t; t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky); @@ -487,8 +642,12 @@ GLuint Canvas::getGLTexture() const return texture; } -void Canvas::predraw() 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); } @@ -520,6 +679,9 @@ void Canvas::setupGrab() // indicate we are using this fbo current = this; + + if (fsaa_buffer != 0) + fsaa_dirty = true; } void Canvas::startGrab(const std::vector &canvases) @@ -535,6 +697,9 @@ void Canvas::startGrab(const std::vector &canvases) if (canvases.size()+1 > size_t(maxDrawBuffers) || canvases.size()+1 > size_t(maxFBOColorAttachments)) throw love::Exception("This system can't simultaniously render to %d canvases.", canvases.size()+1); + + if (fsaa_samples != 0) + throw love::Exception("Multi-canvas rendering is not supported with FSAA."); } for (size_t i = 0; i < canvases.size(); i++) @@ -545,6 +710,9 @@ void Canvas::startGrab(const std::vector &canvases) if (canvases[i]->getTextureType() != texture_type) throw love::Exception("All canvas arguments must have the same texture type."); + if (canvases[i]->getFSAA() != 0) + throw love::Exception("Multi-canvas rendering is not supported with FSAA."); + if (!canvaseschanged && canvases[i] != attachedCanvases[i]) canvaseschanged = true; } @@ -674,12 +842,22 @@ bool Canvas::checkCreateStencil() love::image::ImageData *Canvas::getImageData(love::image::Image *image) { + resolveMSAA(); + int row = 4 * width; int size = row * height; GLubyte *pixels = new GLubyte[size]; - strategy->bindFBO(fbo); + // Our texture is attached to 'resolve_fbo' when we use MSAA. + if (fsaa_samples > 1 && (GLEE_VERSION_3_0 || GLEE_ARB_framebuffer_object)) + glBindFramebuffer(GL_READ_FRAMEBUFFER, resolve_fbo); + else if (fsaa_samples > 1 && GLEE_EXT_framebuffer_multisample) + glBindFramebufferEXT(GL_READ_FRAMEBUFFER, resolve_fbo); + else + strategy->bindFBO(fbo); + glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + if (current) strategy->bindFBO(current->fbo); else @@ -692,10 +870,17 @@ love::image::ImageData *Canvas::getImageData(love::image::Image *image) void Canvas::getPixel(unsigned char* pixel_rgba, int x, int y) { - if (current != this) + resolveMSAA(); + + // Our texture is attached to 'resolve_fbo' when we use MSAA. + if (fsaa_samples > 1 && (GLEE_VERSION_3_0 || GLEE_ARB_framebuffer_object)) + glBindFramebuffer(GL_READ_FRAMEBUFFER, resolve_fbo); + else if (fsaa_samples > 1 && GLEE_EXT_framebuffer_multisample) + glBindFramebufferEXT(GL_READ_FRAMEBUFFER, resolve_fbo); + else if (current != this) strategy->bindFBO(fbo); - glReadPixels(x, height - y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel_rgba); + glReadPixels(x, y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel_rgba); if (current && current != this) strategy->bindFBO(current->fbo); @@ -703,6 +888,44 @@ void Canvas::getPixel(unsigned char* pixel_rgba, int x, int y) strategy->bindFBO(0); } +bool Canvas::resolveMSAA() +{ + if (resolve_fbo == 0 || fsaa_buffer == 0) + return false; + + if (!fsaa_dirty) + return true; + + GLuint previous = 0; + if (current != nullptr) + previous = current->fbo; + + // Do the MSAA resolve by blitting the MSAA renderbuffer to the texture. + if (GLEE_VERSION_3_0 || GLEE_ARB_framebuffer_object) + { + 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 (GLEE_EXT_framebuffer_multisample && GLEE_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 + return false; + + strategy->bindFBO(previous); + + if (current != this) + fsaa_dirty = false; + + return true; +} + bool Canvas::isSupported() { getStrategy(); diff --git a/src/modules/graphics/opengl/Canvas.h b/src/modules/graphics/opengl/Canvas.h index e9e9f7ca0..f643294fc 100644 --- a/src/modules/graphics/opengl/Canvas.h +++ b/src/modules/graphics/opengl/Canvas.h @@ -46,7 +46,7 @@ public: TYPE_MAX_ENUM }; - Canvas(int width, int height, TextureType texture_type = TYPE_NORMAL); + Canvas(int width, int height, TextureType texture_type = TYPE_NORMAL, int fsaa = 0); virtual ~Canvas(); // Implements Volatile. @@ -54,14 +54,14 @@ public: virtual void unloadVolatile(); // Implements Drawable. - virtual void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const; + virtual void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky); // Implements Texture. - virtual void drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const; + virtual void drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky); virtual void setFilter(const Texture::Filter &f); virtual void setWrap(const Texture::Wrap &w); virtual GLuint getGLTexture() const; - virtual void predraw() const; + virtual void predraw(); /** * @param canvases A list of other canvases to temporarily attach to this one, @@ -97,6 +97,13 @@ public: return texture_type; } + inline int getFSAA() const + { + return fsaa_samples; + } + + bool resolveMSAA(); + static bool isSupported(); static bool isHDRSupported(); static bool isMultiCanvasSupported(); @@ -112,8 +119,13 @@ public: private: + bool createFSAAFBO(GLenum internalformat); + GLuint fbo; + GLuint resolve_fbo; + GLuint texture; + GLuint fsaa_buffer; GLuint depth_stencil; TextureType texture_type; @@ -122,8 +134,11 @@ private: std::vector attachedCanvases; + int fsaa_samples; + bool fsaa_dirty; + void setupGrab(); - void drawv(const Matrix &t, const Vertex *v) const; + void drawv(const Matrix &t, const Vertex *v); static StringMap::Entry textureTypeEntries[]; static StringMap textureTypes; diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index e6469a512..a8144c15c 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/src/modules/graphics/opengl/Graphics.cpp @@ -392,7 +392,7 @@ ParticleSystem *Graphics::newParticleSystem(Texture *texture, int size) return new ParticleSystem(texture, size); } -Canvas *Graphics::newCanvas(int width, int height, Canvas::TextureType texture_type) +Canvas *Graphics::newCanvas(int width, int height, Canvas::TextureType texture_type, int fsaa) { if (texture_type == Canvas::TYPE_HDR && !Canvas::isHDRSupported()) throw Exception("HDR Canvases are not supported by your OpenGL implementation"); @@ -405,7 +405,7 @@ Canvas *Graphics::newCanvas(int width, int height, Canvas::TextureType texture_t while (GL_NO_ERROR != glGetError()) /* clear opengl error flag */; - Canvas *canvas = new Canvas(width, height, texture_type); + Canvas *canvas = new Canvas(width, height, texture_type, fsaa); GLenum err = canvas->getStatus(); // everything ok, return canvas (early out) diff --git a/src/modules/graphics/opengl/Graphics.h b/src/modules/graphics/opengl/Graphics.h index 54e90e9e7..b63ae801a 100644 --- a/src/modules/graphics/opengl/Graphics.h +++ b/src/modules/graphics/opengl/Graphics.h @@ -208,7 +208,7 @@ public: ParticleSystem *newParticleSystem(Texture *texture, int size); - Canvas *newCanvas(int width, int height, Canvas::TextureType texture_type = Canvas::TYPE_NORMAL); + Canvas *newCanvas(int width, int height, Canvas::TextureType texture_type = Canvas::TYPE_NORMAL, int fsaa = 0); Shader *newShader(const Shader::ShaderSources &sources); diff --git a/src/modules/graphics/opengl/Image.cpp b/src/modules/graphics/opengl/Image.cpp index 2d7719656..e3ef771dd 100644 --- a/src/modules/graphics/opengl/Image.cpp +++ b/src/modules/graphics/opengl/Image.cpp @@ -91,7 +91,7 @@ love::image::CompressedData *Image::getCompressedData() const return cdata; } -void Image::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const +void Image::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) { Matrix t; t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky); @@ -99,7 +99,7 @@ void Image::draw(float x, float y, float angle, float sx, float sy, float ox, fl drawv(t, vertices); } -void Image::drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const +void Image::drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) { Matrix t; t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky); @@ -107,7 +107,7 @@ void Image::drawq(Quad *quad, float x, float y, float angle, float sx, float sy, drawv(t, quad->getVertices()); } -void Image::predraw() const +void Image::predraw() { bind(); @@ -121,7 +121,7 @@ void Image::predraw() const } } -void Image::postdraw() const +void Image::postdraw() { if (width != paddedWidth || height != paddedHeight) { @@ -511,7 +511,7 @@ void Image::uploadDefaultTexture() glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, px); } -void Image::drawv(const Matrix &t, const Vertex *v) const +void Image::drawv(const Matrix &t, const Vertex *v) { predraw(); diff --git a/src/modules/graphics/opengl/Image.h b/src/modules/graphics/opengl/Image.h index 0622fb8dc..865c5f3bd 100644 --- a/src/modules/graphics/opengl/Image.h +++ b/src/modules/graphics/opengl/Image.h @@ -76,20 +76,20 @@ public: /** * @copydoc Drawable::draw() **/ - void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const; + void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky); /** * @copydoc Texture::drawq() **/ - void drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const; + void drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky); /** * Call before using this Image's texture to draw. Binds the texture, * globally scales texture coordinates if the Image has NPOT dimensions and * NPOT isn't supported, etc. **/ - virtual void predraw() const; - virtual void postdraw() const; + virtual void predraw(); + virtual void postdraw(); virtual GLuint getGLTexture() const; @@ -137,7 +137,7 @@ private: void uploadDefaultTexture(); - void drawv(const Matrix &t, const Vertex *v) const; + void drawv(const Matrix &t, const Vertex *v); // The ImageData from which the texture is created. May be null if // Compressed image data was used to create the texture. diff --git a/src/modules/graphics/opengl/Mesh.cpp b/src/modules/graphics/opengl/Mesh.cpp index becae3596..d440bc786 100644 --- a/src/modules/graphics/opengl/Mesh.cpp +++ b/src/modules/graphics/opengl/Mesh.cpp @@ -288,7 +288,7 @@ bool Mesh::isWireframe() const return wireframe; } -void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const +void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) { const size_t pos_offset = offsetof(Vertex, x); const size_t tex_offset = offsetof(Vertex, s); diff --git a/src/modules/graphics/opengl/Mesh.h b/src/modules/graphics/opengl/Mesh.h index 7719c1c58..3a5029a4d 100644 --- a/src/modules/graphics/opengl/Mesh.h +++ b/src/modules/graphics/opengl/Mesh.h @@ -172,7 +172,7 @@ public: bool isWireframe() const; // Implements Drawable. - void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const; + void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky); static bool getConstant(const char *in, DrawMode &out); static bool getConstant(DrawMode in, const char *&out); diff --git a/src/modules/graphics/opengl/OpenGL.cpp b/src/modules/graphics/opengl/OpenGL.cpp index cae8c88b9..6b3ca1d5f 100644 --- a/src/modules/graphics/opengl/OpenGL.cpp +++ b/src/modules/graphics/opengl/OpenGL.cpp @@ -230,6 +230,18 @@ void OpenGL::prepareDraw() glVertexAttrib1f((GLuint) ATTRIB_PSEUDO_INSTANCE_ID, 0.0f); state.lastPseudoInstanceID = 0; } + + // 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. :( + const std::map &r = shader->getBoundRetainables(); + for (auto it = r.begin(); it != r.end(); ++it) + { + // Even bigger hack! D: + Canvas *canvas = dynamic_cast(it->second); + if (canvas != nullptr) + canvas->resolveMSAA(); + } } } diff --git a/src/modules/graphics/opengl/ParticleSystem.cpp b/src/modules/graphics/opengl/ParticleSystem.cpp index 3f176a00b..04bdbee87 100644 --- a/src/modules/graphics/opengl/ParticleSystem.cpp +++ b/src/modules/graphics/opengl/ParticleSystem.cpp @@ -780,7 +780,7 @@ bool ParticleSystem::isFull() const return activeParticles == maxParticles; } -void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const +void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) { uint32 pCount = getCount(); if (pCount == 0 || texture == nullptr || pMem == nullptr || particleVerts == nullptr) diff --git a/src/modules/graphics/opengl/ParticleSystem.h b/src/modules/graphics/opengl/ParticleSystem.h index 14b37a1c4..1314578a9 100644 --- a/src/modules/graphics/opengl/ParticleSystem.h +++ b/src/modules/graphics/opengl/ParticleSystem.h @@ -470,7 +470,7 @@ public: * @param x The x-coordinate. * @param y The y-coordinate. **/ - virtual void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const; + virtual void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky); /** * Updates the particle system. diff --git a/src/modules/graphics/opengl/Shader.cpp b/src/modules/graphics/opengl/Shader.cpp index b14ea7a74..365a52569 100644 --- a/src/modules/graphics/opengl/Shader.cpp +++ b/src/modules/graphics/opengl/Shader.cpp @@ -719,6 +719,11 @@ void Shader::checkSetScreenParams() lastCanvas = Canvas::current; } +const std::map &Shader::getBoundRetainables() const +{ + return boundRetainables; +} + std::string Shader::getGLSLVersion() { const char *tmp = nullptr; diff --git a/src/modules/graphics/opengl/Shader.h b/src/modules/graphics/opengl/Shader.h index ab32bffd7..937d79308 100644 --- a/src/modules/graphics/opengl/Shader.h +++ b/src/modules/graphics/opengl/Shader.h @@ -143,6 +143,8 @@ public: bool sendBuiltinFloat(BuiltinExtern builtin, int size, const GLfloat *m, int count); void checkSetScreenParams(); + const std::map &getBoundRetainables() const; + static std::string getGLSLVersion(); static bool isSupported(); diff --git a/src/modules/graphics/opengl/SpriteBatch.cpp b/src/modules/graphics/opengl/SpriteBatch.cpp index 7b583c4b8..032e8769e 100644 --- a/src/modules/graphics/opengl/SpriteBatch.cpp +++ b/src/modules/graphics/opengl/SpriteBatch.cpp @@ -263,7 +263,7 @@ int SpriteBatch::getBufferSize() const return size; } -void SpriteBatch::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const +void SpriteBatch::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) { const size_t vertex_offset = offsetof(Vertex, x); const size_t texel_offset = offsetof(Vertex, s); diff --git a/src/modules/graphics/opengl/SpriteBatch.h b/src/modules/graphics/opengl/SpriteBatch.h index de14bf099..ae79db8d6 100644 --- a/src/modules/graphics/opengl/SpriteBatch.h +++ b/src/modules/graphics/opengl/SpriteBatch.h @@ -109,7 +109,7 @@ public: int getBufferSize() const; // Implements Drawable. - void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const; + void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky); static bool getConstant(const char *in, UsageHint &out); static bool getConstant(UsageHint in, const char *&out); diff --git a/src/modules/graphics/opengl/Texture.h b/src/modules/graphics/opengl/Texture.h index 6dee55350..3a02decbd 100644 --- a/src/modules/graphics/opengl/Texture.h +++ b/src/modules/graphics/opengl/Texture.h @@ -48,12 +48,12 @@ public: * Any setup the texture might need to do before drawing, e.g. binding * the OpenGL texture for use. **/ - virtual void predraw() const {} + virtual void predraw() {} /** * Any cleanup the texture might need to do directly after drawing. **/ - virtual void postdraw() const {} + virtual void postdraw() {} }; // Texture diff --git a/src/modules/graphics/opengl/wrap_Canvas.cpp b/src/modules/graphics/opengl/wrap_Canvas.cpp index 9a4669563..cbc9b6ed9 100644 --- a/src/modules/graphics/opengl/wrap_Canvas.cpp +++ b/src/modules/graphics/opengl/wrap_Canvas.cpp @@ -120,6 +120,13 @@ int w_Canvas_getType(lua_State *L) return 1; } +int w_Canvas_getFSAA(lua_State *L) +{ + Canvas *canvas = luax_checkcanvas(L, 1); + lua_pushinteger(L, canvas->getFSAA()); + return 1; +} + static const luaL_Reg functions[] = { // From wrap_Texture. @@ -136,6 +143,7 @@ static const luaL_Reg functions[] = { "getPixel", w_Canvas_getPixel }, { "clear", w_Canvas_clear }, { "getType", w_Canvas_getType }, + { "getFSAA", w_Canvas_getFSAA }, { 0, 0 } }; diff --git a/src/modules/graphics/opengl/wrap_Canvas.h b/src/modules/graphics/opengl/wrap_Canvas.h index 4a6bea87b..89c7952f2 100644 --- a/src/modules/graphics/opengl/wrap_Canvas.h +++ b/src/modules/graphics/opengl/wrap_Canvas.h @@ -40,6 +40,7 @@ int w_Canvas_getImageData(lua_State *L); int w_Canvas_getPixel(lua_State * L); int w_Canvas_clear(lua_State *L); int w_Canvas_getType(lua_State *L); +int w_Canvas_getFSAA(lua_State *L); extern "C" int luaopen_canvas(lua_State *L); } // opengl diff --git a/src/modules/graphics/opengl/wrap_Graphics.cpp b/src/modules/graphics/opengl/wrap_Graphics.cpp index da0aac721..3118fb5e2 100644 --- a/src/modules/graphics/opengl/wrap_Graphics.cpp +++ b/src/modules/graphics/opengl/wrap_Graphics.cpp @@ -323,15 +323,16 @@ int w_newCanvas(lua_State *L) int width = luaL_optint(L, 1, instance->getWidth()); int height = luaL_optint(L, 2, instance->getHeight()); const char *str = luaL_optstring(L, 3, "normal"); + int fsaa = luaL_optint(L, 4, 0); Canvas::TextureType texture_type; if (!Canvas::getConstant(str, texture_type)) return luaL_error(L, "Invalid canvas type: %s", str); - Canvas *canvas = 0; - EXCEPT_GUARD(canvas = instance->newCanvas(width, height, texture_type);) + Canvas *canvas = nullptr; + EXCEPT_GUARD(canvas = instance->newCanvas(width, height, texture_type, fsaa);) - if (canvas == 0) + if (canvas == nullptr) return luaL_error(L, "Canvas not created, but no error thrown. I don't even..."); luax_pushtype(L, "Canvas", GRAPHICS_CANVAS_T, canvas);