From 8067d42df29543ba7e582cc9e051ea5c681a4aa0 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Tue, 20 Jan 2015 15:13:59 -0400 Subject: [PATCH] Canvases now have OpenGL ES support, updated Texture:setWrap to return false for repeat wrap modes in non-power-of-two textures if OpenGL ES 2 is used and the implementation doesn't have support for repeat on NPOT textures. --HG-- branch : minor --- src/modules/graphics/Texture.h | 2 +- src/modules/graphics/opengl/Canvas.cpp | 98 +++++++++++++++---- src/modules/graphics/opengl/Canvas.h | 2 +- src/modules/graphics/opengl/Graphics.cpp | 14 ++- src/modules/graphics/opengl/Image.cpp | 37 ++++++- src/modules/graphics/opengl/Image.h | 2 +- src/modules/graphics/opengl/Texture.h | 8 ++ src/modules/graphics/opengl/wrap_Graphics.cpp | 16 +-- src/modules/graphics/opengl/wrap_Texture.cpp | 4 +- 9 files changed, 136 insertions(+), 47 deletions(-) diff --git a/src/modules/graphics/Texture.h b/src/modules/graphics/Texture.h index ce43c0127..75e8c6311 100644 --- a/src/modules/graphics/Texture.h +++ b/src/modules/graphics/Texture.h @@ -95,7 +95,7 @@ public: virtual void setFilter(const Filter &f) = 0; virtual const Filter &getFilter() const; - virtual void setWrap(const Wrap &w) = 0; + virtual bool setWrap(const Wrap &w) = 0; virtual const Wrap &getWrap() const; virtual const Vertex *getVertices() const; diff --git a/src/modules/graphics/opengl/Canvas.cpp b/src/modules/graphics/opengl/Canvas.cpp index 747eb6cac..33e1d019e 100644 --- a/src/modules/graphics/opengl/Canvas.cpp +++ b/src/modules/graphics/opengl/Canvas.cpp @@ -571,12 +571,17 @@ bool Canvas::loadVolatile() convertFormat(format, internalformat, externalformat, textype); + // in GLES2, the internalformat and format params of TexImage have to match. + GLint iformat = (GLint) internalformat; + if (GLAD_ES_VERSION_2_0 && !GLAD_ES_VERSION_3_0) + iformat = (GLint) externalformat; + while (glGetError() != GL_NO_ERROR) /* Clear the error buffer. */; glTexImage2D(GL_TEXTURE_2D, 0, - (GLint) internalformat, + iformat, width, height, 0, externalformat, @@ -691,11 +696,24 @@ void Canvas::setFilter(const Texture::Filter &f) gl.setTextureFilter(filter); } -void Canvas::setWrap(const Texture::Wrap &w) +bool Canvas::setWrap(const Texture::Wrap &w) { + bool success = true; wrap = w; + + if (hasLimitedNpot() && (width != next_p2(width) || height != next_p2(height))) + { + if (wrap.s != WRAP_CLAMP || wrap.t != WRAP_CLAMP) + success = false; + + // If we only have limited NPOT support then the wrap mode must be CLAMP. + wrap.s = wrap.t = WRAP_CLAMP; + } + gl.bindTexture(texture); gl.setTextureWrap(wrap); + + return success; } GLuint Canvas::getGLTexture() const @@ -738,10 +756,13 @@ void Canvas::setupGrab() gl.matrices.projection.push_back(Matrix::ortho(0.0, width, 0.0, height)); // Make sure the correct sRGB setting is used when drawing to the canvas. - if (format == FORMAT_SRGB) - glEnable(GL_FRAMEBUFFER_SRGB); - else if (screenHasSRGB) - glDisable(GL_FRAMEBUFFER_SRGB); + if (GLAD_VERSION_1_0 || GLAD_EXT_sRGB_write_control) + { + if (format == FORMAT_SRGB) + glEnable(GL_FRAMEBUFFER_SRGB); + else if (screenHasSRGB) + glDisable(GL_FRAMEBUFFER_SRGB); + } if (msaa_buffer != 0) msaa_dirty = true; @@ -829,8 +850,11 @@ void Canvas::stopGrab(bool switchingToOtherCanvas) if (switchingToOtherCanvas) { - if (format == FORMAT_SRGB) - glDisable(GL_FRAMEBUFFER_SRGB); + if (GLAD_VERSION_1_0 || GLAD_EXT_sRGB_write_control) + { + if (format == FORMAT_SRGB) + glDisable(GL_FRAMEBUFFER_SRGB); + } } else { @@ -839,10 +863,13 @@ void Canvas::stopGrab(bool switchingToOtherCanvas) current = nullptr; gl.setViewport(systemViewport); - if (format == FORMAT_SRGB && !screenHasSRGB) - glDisable(GL_FRAMEBUFFER_SRGB); - else if (format != FORMAT_SRGB && screenHasSRGB) - glEnable(GL_FRAMEBUFFER_SRGB); + if (GLAD_VERSION_1_0 || GLAD_EXT_sRGB_write_control) + { + if (format == FORMAT_SRGB && !screenHasSRGB) + glDisable(GL_FRAMEBUFFER_SRGB); + else if (format != FORMAT_SRGB && screenHasSRGB) + glEnable(GL_FRAMEBUFFER_SRGB); + } } } @@ -865,7 +892,7 @@ void Canvas::clear(Color c) // We don't need to worry about multiple FBO attachments or global clear // color state when OpenGL 3.0+ is supported. - if (GLAD_VERSION_3_0) + if (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0) { glClearBufferfv(GL_COLOR, 0, glcolor); @@ -1020,7 +1047,11 @@ Canvas::Format Canvas::getSizedFormat(Canvas::Format format) switch (format) { case FORMAT_NORMAL: - return FORMAT_RGBA8; + // 32-bit render targets don't have guaranteed support on OpenGL ES 2. + if (GLAD_ES_VERSION_2_0 && !(GLAD_ES_VERSION_3_0 || GLAD_OES_rgb8_rgba8 || GLAD_ARM_rgba8)) + return FORMAT_RGBA4; + else + return FORMAT_RGBA8; case FORMAT_HDR: return FORMAT_RGBA16F; default: @@ -1055,7 +1086,7 @@ void Canvas::convertFormat(Canvas::Format format, GLenum &internalformat, GLenum break; case FORMAT_RGB10A2: internalformat = GL_RGB10_A2; - type = GL_UNSIGNED_INT_10_10_10_2; + type = GL_UNSIGNED_INT_2_10_10_10_REV; break; case FORMAT_RG11B10F: internalformat = GL_R11F_G11F_B10F; @@ -1064,7 +1095,12 @@ void Canvas::convertFormat(Canvas::Format format, GLenum &internalformat, GLenum break; case FORMAT_RGBA16F: internalformat = GL_RGBA16F; - type = GL_FLOAT; + if (GLAD_OES_texture_float) + type = GL_HALF_FLOAT_OES; + else if (GLAD_VERSION_1_0) + type = GL_FLOAT; + else + type = GL_HALF_FLOAT; break; case FORMAT_RGBA32F: internalformat = GL_RGBA32F; @@ -1073,6 +1109,8 @@ void Canvas::convertFormat(Canvas::Format format, GLenum &internalformat, GLenum case FORMAT_SRGB: internalformat = GL_SRGB8_ALPHA8; type = GL_UNSIGNED_BYTE; + if (GLAD_ES_VERSION_2_0 && !GLAD_ES_VERSION_3_0) + externalformat = GL_SRGB_ALPHA; break; } } @@ -1106,7 +1144,7 @@ bool Canvas::isSupported() bool Canvas::isMultiCanvasSupported() { - // system must support at least 4 simultanious active canvases. + // system must support at least 4 simultaneous active canvases. return gl.getMaxRenderTargets() >= 4; } @@ -1124,24 +1162,38 @@ bool Canvas::isFormatSupported(Canvas::Format format) switch (format) { case FORMAT_RGBA8: + supported = GLAD_VERSION_1_0 || GLAD_ES_VERSION_3_0 || GLAD_OES_rgb8_rgba8 || GLAD_ARM_rgba8; + break; case FORMAT_RGBA4: case FORMAT_RGB5A1: - case FORMAT_RGB10A2: supported = true; break; case FORMAT_RGB565: supported = GLAD_ES_VERSION_2_0 || GLAD_VERSION_4_2 || GLAD_ARB_ES2_compatibility; break; + case FORMAT_RGB10A2: + supported = GLAD_ES_VERSION_3_0 || GLAD_VERSION_1_0; + break; case FORMAT_RG11B10F: - supported = GLAD_VERSION_3_0 || GLAD_EXT_packed_float; + supported = GLAD_VERSION_3_0 || GLAD_EXT_packed_float /*|| GLAD_APPLE_color_buffer_packed_float*/; break; case FORMAT_RGBA16F: + if (GLAD_VERSION_1_0) + supported = GLAD_VERSION_3_0 || GLAD_ARB_texture_float; + else if (GLAD_ES_VERSION_2_0) + supported = GLAD_EXT_color_buffer_half_float && (GLAD_ES_VERSION_3_0 || GLAD_OES_texture_half_float); + break; case FORMAT_RGBA32F: supported = GLAD_VERSION_3_0 || GLAD_ARB_texture_float; break; case FORMAT_SRGB: - supported = GLAD_VERSION_3_0 || ((GLAD_ARB_framebuffer_sRGB || GLAD_EXT_framebuffer_sRGB) - && (GLAD_VERSION_2_1 || GLAD_EXT_texture_sRGB)); + if (GLAD_VERSION_1_0) + { + supported = GLAD_VERSION_3_0 || ((GLAD_ARB_framebuffer_sRGB || GLAD_EXT_framebuffer_sRGB) + && (GLAD_VERSION_2_1 || GLAD_EXT_texture_sRGB)); + } + else + supported = GLAD_ES_VERSION_3_0 || GLAD_EXT_sRGB; break; default: supported = false; @@ -1164,6 +1216,10 @@ bool Canvas::isFormatSupported(Canvas::Format format) GLenum textype = GL_UNSIGNED_BYTE; convertFormat(format, internalformat, externalformat, textype); + // in GLES2, the internalformat and format params of TexImage have to match. + if (GLAD_ES_VERSION_2_0 && !GLAD_ES_VERSION_3_0) + internalformat = externalformat; + GLuint texture = 0; glGenTextures(1, &texture); gl.bindTexture(texture); diff --git a/src/modules/graphics/opengl/Canvas.h b/src/modules/graphics/opengl/Canvas.h index a3875990f..46ad5a9dc 100644 --- a/src/modules/graphics/opengl/Canvas.h +++ b/src/modules/graphics/opengl/Canvas.h @@ -71,7 +71,7 @@ public: // 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); virtual void setFilter(const Texture::Filter &f); - virtual void setWrap(const Texture::Wrap &w); + virtual bool setWrap(const Texture::Wrap &w); virtual GLuint getGLTexture() const; virtual void predraw(); diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index a5b8dbd78..1447745e4 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/src/modules/graphics/opengl/Graphics.cpp @@ -254,12 +254,16 @@ bool Graphics::setMode(int width, int height, bool &sRGB) glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // Set whether drawing converts input from linear -> sRGB colorspace. - if (GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_sRGB || GLAD_EXT_framebuffer_sRGB) + if (GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_sRGB || GLAD_EXT_framebuffer_sRGB + || GLAD_ES_VERSION_3_0 || GLAD_EXT_sRGB) { - if (sRGB) - glEnable(GL_FRAMEBUFFER_SRGB); - else - glDisable(GL_FRAMEBUFFER_SRGB); + if (GLAD_VERSION_1_0 || GLAD_EXT_sRGB_write_control) + { + if (sRGB) + glEnable(GL_FRAMEBUFFER_SRGB); + else + glDisable(GL_FRAMEBUFFER_SRGB); + } } else sRGB = false; diff --git a/src/modules/graphics/opengl/Image.cpp b/src/modules/graphics/opengl/Image.cpp index 8670f5ec3..42f4be65d 100644 --- a/src/modules/graphics/opengl/Image.cpp +++ b/src/modules/graphics/opengl/Image.cpp @@ -155,12 +155,24 @@ void Image::setFilter(const Texture::Filter &f) gl.setTextureFilter(filter); } -void Image::setWrap(const Texture::Wrap &w) +bool Image::setWrap(const Texture::Wrap &w) { + bool success = true; wrap = w; + if (hasLimitedNpot() && (width != next_p2(width) || height != next_p2(height))) + { + if (wrap.s != WRAP_CLAMP || wrap.t != WRAP_CLAMP) + success = false; + + // If we only have limited NPOT support then the wrap mode must be CLAMP. + wrap.s = wrap.t = WRAP_CLAMP; + } + bind(); gl.setTextureWrap(w); + + return success; } void Image::setMipmapSharpness(float sharpness) @@ -263,10 +275,18 @@ void Image::loadTextureFromCompressedData() void Image::loadTextureFromImageData() { GLenum iformat = flags.sRGB ? GL_SRGB8_ALPHA8 : GL_RGBA8; + GLenum format = GL_RGBA; + + // in GLES2, the internalformat and format params of TexImage have to match. + if (GLAD_ES_VERSION_2_0 && !GLAD_ES_VERSION_3_0) + { + format = flags.sRGB ? GL_SRGB_ALPHA : GL_RGBA; + iformat = format; + } { love::thread::Lock lock(data->getMutex()); - glTexImage2D(GL_TEXTURE_2D, 0, iformat, width, height, 0, GL_RGBA, + glTexImage2D(GL_TEXTURE_2D, 0, iformat, width, height, 0, format, GL_UNSIGNED_BYTE, data->getData()); } @@ -292,14 +312,21 @@ bool Image::loadVolatile() throw love::Exception("sRGB images are not supported on this system."); } + // NPOT textures don't support mipmapping without full NPOT support. + if (hasLimitedNpot() && (width != next_p2(width) || height != next_p2(height))) + { + flags.mipmaps = false; + filter.mipmap = FILTER_NONE; + } + if (maxMipmapSharpness == 0.0f && GLAD_VERSION_1_4) glGetFloatv(GL_MAX_TEXTURE_LOD_BIAS, &maxMipmapSharpness); glGenTextures(1, &texture); gl.bindTexture(texture); - gl.setTextureFilter(filter); - gl.setTextureWrap(wrap); + setFilter(filter); + setWrap(wrap); setMipmapSharpness(mipmapSharpness); // Use a default texture if the size is too big for the system. @@ -616,7 +643,7 @@ bool Image::hasCompressedTextureSupport(image::CompressedData::Format format, bo bool Image::hasSRGBSupport() { - return GLAD_VERSION_2_1 || GLAD_EXT_texture_sRGB; + return GLAD_ES_VERSION_3_0 || GLAD_EXT_sRGB || GLAD_VERSION_2_1 || GLAD_EXT_texture_sRGB; } bool Image::getConstant(const char *in, FlagType &out) diff --git a/src/modules/graphics/opengl/Image.h b/src/modules/graphics/opengl/Image.h index 769d936ad..c4eb643c1 100644 --- a/src/modules/graphics/opengl/Image.h +++ b/src/modules/graphics/opengl/Image.h @@ -108,7 +108,7 @@ public: virtual GLuint getGLTexture() const; virtual void setFilter(const Texture::Filter &f); - virtual void setWrap(const Texture::Wrap &w); + virtual bool setWrap(const Texture::Wrap &w); void setMipmapSharpness(float sharpness); float getMipmapSharpness() const; diff --git a/src/modules/graphics/opengl/Texture.h b/src/modules/graphics/opengl/Texture.h index e30de6960..eea72f571 100644 --- a/src/modules/graphics/opengl/Texture.h +++ b/src/modules/graphics/opengl/Texture.h @@ -55,6 +55,14 @@ public: **/ virtual void postdraw() {} + /** + * Unextended OpenGL ES 2 doesn't support non-clamp wrap modes or mipmapping + * with non-power-of-two textures. + **/ + static bool hasLimitedNpot() + { + return GLAD_ES_VERSION_2_0 && !(GLAD_ES_VERSION_3_0 || GLAD_OES_texture_npot); + } }; // Texture diff --git a/src/modules/graphics/opengl/wrap_Graphics.cpp b/src/modules/graphics/opengl/wrap_Graphics.cpp index d49b6fd34..31a2c8fca 100644 --- a/src/modules/graphics/opengl/wrap_Graphics.cpp +++ b/src/modules/graphics/opengl/wrap_Graphics.cpp @@ -713,26 +713,20 @@ int w_getBlendMode(lua_State *L) int w_setDefaultFilter(lua_State *L) { - Texture::FilterMode min; - Texture::FilterMode mag; + Texture::Filter f; const char *minstr = luaL_checkstring(L, 1); const char *magstr = luaL_optstring(L, 2, minstr); - if (!Texture::getConstant(minstr, min)) + if (!Texture::getConstant(minstr, f.min)) return luaL_error(L, "Invalid filter mode: %s", minstr); - if (!Texture::getConstant(magstr, mag)) + if (!Texture::getConstant(magstr, f.mag)) return luaL_error(L, "Invalid filter mode: %s", magstr); - float anisotropy = (float) luaL_optnumber(L, 3, 1.0); - - Texture::Filter f; - f.min = min; - f.mag = mag; - f.anisotropy = anisotropy; + f.anisotropy = (float) luaL_optnumber(L, 3, 1.0); instance()->setDefaultFilter(f); - + return 0; } diff --git a/src/modules/graphics/opengl/wrap_Texture.cpp b/src/modules/graphics/opengl/wrap_Texture.cpp index 7d6a92dad..a318a3b1d 100644 --- a/src/modules/graphics/opengl/wrap_Texture.cpp +++ b/src/modules/graphics/opengl/wrap_Texture.cpp @@ -105,8 +105,8 @@ int w_Texture_setWrap(lua_State *L) if (!Texture::getConstant(tstr, w.t)) return luaL_error(L, "Invalid wrap mode, %s", tstr); - t->setWrap(w); - return 0; + luax_pushboolean(L, t->setWrap(w)); + return 1; } int w_Texture_getWrap(lua_State *L)