diff --git a/src/modules/graphics/Texture.cpp b/src/modules/graphics/Texture.cpp index 0a8d85541..477eb33cb 100644 --- a/src/modules/graphics/Texture.cpp +++ b/src/modules/graphics/Texture.cpp @@ -109,16 +109,6 @@ bool Texture::getConstant(WrapMode in, const char *&out) return wrapModes.find(in, out); } -bool Texture::getConstant(const char *in, Format &out) -{ - return formats.find(in, out); -} - -bool Texture::getConstant(Format in, const char *&out) -{ - return formats.find(in, out); -} - StringMap::Entry Texture::filterModeEntries[] = { { "linear", Texture::FILTER_LINEAR }, @@ -135,15 +125,5 @@ StringMap::Entry Texture::wrapModeEnt StringMap Texture::wrapModes(Texture::wrapModeEntries, sizeof(Texture::wrapModeEntries)); -StringMap::Entry Texture::formatEntries[] = -{ - {"normal", Texture::FORMAT_NORMAL}, - {"hdr", Texture::FORMAT_HDR}, - {"srgb", Texture::FORMAT_SRGB}, -}; - -StringMap Texture::formats(Texture::formatEntries, sizeof(Texture::formatEntries)); - - } // graphics } // love diff --git a/src/modules/graphics/Texture.h b/src/modules/graphics/Texture.h index d9c83171e..7b5a525a4 100644 --- a/src/modules/graphics/Texture.h +++ b/src/modules/graphics/Texture.h @@ -55,14 +55,6 @@ public: FILTER_MAX_ENUM }; - enum Format - { - FORMAT_NORMAL, - FORMAT_HDR, - FORMAT_SRGB, - FORMAT_MAX_ENUM - }; - struct Filter { Filter(); @@ -119,9 +111,6 @@ public: static bool getConstant(const char *in, WrapMode &out); static bool getConstant(WrapMode in, const char *&out); - static bool getConstant(const char *in, Format &out); - static bool getConstant(Format in, const char *&out); - protected: int width; @@ -143,9 +132,6 @@ private: static StringMap::Entry wrapModeEntries[]; static StringMap wrapModes; - static StringMap::Entry formatEntries[]; - static StringMap formats; - }; // Texture } // graphics diff --git a/src/modules/graphics/opengl/Canvas.cpp b/src/modules/graphics/opengl/Canvas.cpp index 58ee616c4..6cb2f7e14 100644 --- a/src/modules/graphics/opengl/Canvas.cpp +++ b/src/modules/graphics/opengl/Canvas.cpp @@ -423,7 +423,7 @@ static void getStrategy() } } -Canvas::Canvas(int width, int height, Texture::Format format, int fsaa) +Canvas::Canvas(int width, int height, Format format, int fsaa) : fbo(0) , resolve_fbo(0) , texture(0) @@ -525,39 +525,33 @@ bool Canvas::loadVolatile() return false; } + if (!isFormatSupported(format)) + { + status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; + return false; + } + glGenTextures(1, &texture); gl.bindTexture(texture); setFilter(filter); setWrap(wrap); - GLint internalformat; - GLenum textype; - switch (format) - { - case Texture::FORMAT_HDR: - internalformat = GL_RGBA16F; - textype = GL_FLOAT; - break; - case Texture::FORMAT_SRGB: - internalformat = GL_SRGB8_ALPHA8; - textype = GL_UNSIGNED_BYTE; - break; - case Texture::FORMAT_NORMAL: - default: - internalformat = GL_RGBA8; - textype = GL_UNSIGNED_BYTE; - } + GLenum internalformat = GL_RGBA; + GLenum externalformat = GL_RGBA; + GLenum textype = GL_UNSIGNED_BYTE; + + convertFormat(format, internalformat, externalformat, textype); while (glGetError() != GL_NO_ERROR) /* Clear the error buffer. */; glTexImage2D(GL_TEXTURE_2D, 0, - internalformat, + (GLint) internalformat, width, height, 0, - GL_RGBA, + externalformat, textype, nullptr); @@ -981,41 +975,149 @@ bool Canvas::resolveMSAA() return true; } +Canvas::Format Canvas::getSizedFormat(Canvas::Format format) +{ + switch (format) + { + case FORMAT_NORMAL: + return FORMAT_RGBA8; + case FORMAT_HDR: + return FORMAT_RGBA16F; + default: + return format; + } +} + +void Canvas::convertFormat(Canvas::Format format, GLenum &internalformat, GLenum &externalformat, GLenum &type) +{ + format = getSizedFormat(format); + externalformat = GL_RGBA; + + switch (format) + { + case FORMAT_RGBA8: + default: + internalformat = GL_RGBA8; + type = GL_UNSIGNED_BYTE; + break; + case FORMAT_RGBA4: + internalformat = GL_RGBA4; + type = GL_UNSIGNED_SHORT_4_4_4_4; + break; + case FORMAT_RGB5A1: + internalformat = GL_RGB5_A1; + type = GL_UNSIGNED_SHORT_5_5_5_1; + break; + case FORMAT_RGB565: + internalformat = GL_RGB565; + externalformat = GL_RGB; + type = GL_UNSIGNED_SHORT_5_6_5; + break; + case FORMAT_RGB10A2: + internalformat = GL_RGB10_A2; + type = GL_UNSIGNED_INT_10_10_10_2; + break; + case FORMAT_RGB9E5: + internalformat = GL_RGB9_E5; + externalformat = GL_RGB; + type = GL_RGB9_E5; + case FORMAT_RG11B10F: + internalformat = GL_R11F_G11F_B10F; + externalformat = GL_RGB; + type = GL_UNSIGNED_INT_10F_11F_11F_REV; + case FORMAT_RGBA16F: + internalformat = GL_RGBA16F; + type = GL_FLOAT; + break; + case FORMAT_RGBA32F: + internalformat = GL_RGBA32F; + type = GL_FLOAT; + break; + case FORMAT_SRGB: + internalformat = GL_SRGB8_ALPHA8; + type = GL_UNSIGNED_BYTE; + break; + } +} + bool Canvas::isSupported() { getStrategy(); return (strategy != &strategyNone); } -bool Canvas::isHDRSupported() -{ - return GLEE_VERSION_3_0 || (isSupported() && GLEE_ARB_texture_float); -} - -bool Canvas::isSRGBSupported() -{ - if (GLEE_VERSION_3_0) - return true; - - if (!isSupported()) - return false; - - return (GLEE_ARB_framebuffer_sRGB || GLEE_EXT_framebuffer_sRGB) - && GLEE_EXT_texture_sRGB; -} - bool Canvas::isMultiCanvasSupported() { // system must support at least 4 simultanious active canvases. return gl.getMaxRenderTargets() >= 4; } +bool Canvas::isFormatSupported(Canvas::Format format) +{ + if (!isSupported()) + return false; + + format = getSizedFormat(format); + + switch (format) + { + case FORMAT_RGBA8: + case FORMAT_RGBA4: + case FORMAT_RGB5A1: + case FORMAT_RGB10A2: + return true; + case FORMAT_RGB565: + return GLEE_VERSION_4_2 || GLEE_ARB_ES2_compatibility; + case FORMAT_RGB9E5: + return GLEE_VERSION_3_0 || GLEE_EXT_texture_shared_exponent; + case FORMAT_RG11B10F: + return GLEE_VERSION_3_0 || (GLEE_ARB_texture_float && GLEE_ARB_color_buffer_float + && GLEE_EXT_packed_float); + case FORMAT_RGBA16F: + case FORMAT_RGBA32F: + return GLEE_VERSION_3_0 || (GLEE_ARB_texture_float && GLEE_ARB_color_buffer_float); + case FORMAT_SRGB: + return GLEE_VERSION_3_0 || ((GLEE_ARB_framebuffer_sRGB || GLEE_EXT_framebuffer_sRGB) + && (GLEE_VERSION_2_1 || GLEE_EXT_texture_sRGB)); + default: + return false; + } +} + void Canvas::bindDefaultCanvas() { if (current != nullptr) current->stopGrab(); } +bool Canvas::getConstant(const char *in, Format &out) +{ + return formats.find(in, out); +} + +bool Canvas::getConstant(Format in, const char *&out) +{ + return formats.find(in, out); +} + +StringMap::Entry Canvas::formatEntries[] = +{ + {"normal", Canvas::FORMAT_NORMAL}, + {"hdr", Canvas::FORMAT_HDR}, + {"rgba8", Canvas::FORMAT_RGBA8}, + {"rgba4", Canvas::FORMAT_RGBA4}, + {"rgb5a1", Canvas::FORMAT_RGB5A1}, + {"rgb565", Canvas::FORMAT_RGB565}, + {"rgb10a2", Canvas::FORMAT_RGB10A2}, + {"rgb9e5", Canvas::FORMAT_RGB9E5}, + {"rg11b10f", Canvas::FORMAT_RG11B10F}, + {"rgba16f", Canvas::FORMAT_RGBA16F}, + {"rgba32f", Canvas::FORMAT_RGBA32F}, + {"srgb", Canvas::FORMAT_SRGB}, +}; + +StringMap Canvas::formats(Canvas::formatEntries, sizeof(Canvas::formatEntries)); + } // opengl } // graphics } // love diff --git a/src/modules/graphics/opengl/Canvas.h b/src/modules/graphics/opengl/Canvas.h index 2d87b0bbc..41fb3b1fa 100644 --- a/src/modules/graphics/opengl/Canvas.h +++ b/src/modules/graphics/opengl/Canvas.h @@ -25,6 +25,7 @@ #include "image/Image.h" #include "image/ImageData.h" #include "common/Matrix.h" +#include "common/StringMap.h" #include "Texture.h" #include "OpenGL.h" @@ -39,7 +40,25 @@ class Canvas : public Texture { public: - Canvas(int width, int height, Texture::Format format = Texture::FORMAT_NORMAL, int fsaa = 0); + // Different Canvas render target formats. + enum Format + { + FORMAT_NORMAL, + FORMAT_HDR, + FORMAT_RGBA8, + FORMAT_RGBA4, + FORMAT_RGB5A1, + FORMAT_RGB565, + FORMAT_RGB10A2, + FORMAT_RGB9E5, + FORMAT_RG11B10F, + FORMAT_RGBA16F, + FORMAT_RGBA32F, + FORMAT_SRGB, + FORMAT_MAX_ENUM + }; + + Canvas(int width, int height, Format format = FORMAT_NORMAL, int fsaa = 0); virtual ~Canvas(); // Implements Volatile. @@ -85,7 +104,7 @@ public: return status; } - inline Texture::Format getTextureFormat() const + inline Format getTextureFormat() const { return format; } @@ -98,9 +117,8 @@ public: bool resolveMSAA(); static bool isSupported(); - static bool isHDRSupported(); - static bool isSRGBSupported(); static bool isMultiCanvasSupported(); + static bool isFormatSupported(Format format); static Canvas *current; static void bindDefaultCanvas(); @@ -111,10 +129,16 @@ public: // Whether the main screen should have linear -> sRGB conversions enabled. static bool screenHasSRGB; + static bool getConstant(const char *in, Format &out); + static bool getConstant(Format in, const char *&out); + private: bool createFSAAFBO(GLenum internalformat); + static Format getSizedFormat(Format format); + static void convertFormat(Format format, GLenum &internalformat, GLenum &externalformat, GLenum &type); + GLuint fbo; GLuint resolve_fbo; @@ -134,6 +158,9 @@ private: void setupGrab(); void drawv(const Matrix &t, const Vertex *v); + static StringMap::Entry formatEntries[]; + static StringMap formats; + }; // Canvas } // opengl diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index 68b71f379..a78908736 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/src/modules/graphics/opengl/Graphics.cpp @@ -409,7 +409,7 @@ void Graphics::discardStencil() activeStencil = false; } -Image *Graphics::newImage(love::image::ImageData *data, Texture::Format format) +Image *Graphics::newImage(love::image::ImageData *data, Image::Format format) { // Create the image. Image *image = new Image(data, format); @@ -436,7 +436,7 @@ Image *Graphics::newImage(love::image::ImageData *data, Texture::Format format) return image; } -Image *Graphics::newImage(love::image::CompressedData *cdata, Texture::Format format) +Image *Graphics::newImage(love::image::CompressedData *cdata, Image::Format format) { // Create the image. Image *image = new Image(cdata, format); @@ -483,13 +483,14 @@ ParticleSystem *Graphics::newParticleSystem(Texture *texture, int size) return new ParticleSystem(texture, size); } -Canvas *Graphics::newCanvas(int width, int height, Texture::Format format, int fsaa) +Canvas *Graphics::newCanvas(int width, int height, Canvas::Format format, int fsaa) { - if (format == Texture::FORMAT_HDR && !Canvas::isHDRSupported()) - throw Exception("HDR Canvases are not supported by your OpenGL implementation"); - - if (format == Texture::FORMAT_SRGB && !Canvas::isSRGBSupported()) - throw Exception("sRGB Canvases are not supported by your OpenGL implementation"); + if (!Canvas::isFormatSupported(format)) + { + const char *fstr = "rgba8"; + Canvas::getConstant(format, fstr); + throw love::Exception("The %s canvas format is not supported by your OpenGL implementation.", fstr); + } if (width > gl.getMaxTextureSize()) throw Exception("Cannot create canvas: width of %d pixels is too large for this system.", width); @@ -540,8 +541,8 @@ Canvas *Graphics::newCanvas(int width, int height, Texture::Format format, int f } canvas->release(); - throw Exception(error_string.str().c_str()); - return NULL; // never reached + throw Exception("%s", error_string.str().c_str()); + return nullptr; // never reached } Shader *Graphics::newShader(const Shader::ShaderSources &sources) diff --git a/src/modules/graphics/opengl/Graphics.h b/src/modules/graphics/opengl/Graphics.h index 7ab7560cd..4e295eedf 100644 --- a/src/modules/graphics/opengl/Graphics.h +++ b/src/modules/graphics/opengl/Graphics.h @@ -194,8 +194,8 @@ public: /** * Creates an Image object with padding and/or optimization. **/ - Image *newImage(love::image::ImageData *data, Texture::Format format = Texture::FORMAT_NORMAL); - Image *newImage(love::image::CompressedData *cdata, Texture::Format format = Texture::FORMAT_NORMAL); + Image *newImage(love::image::ImageData *data, Image::Format format = Image::FORMAT_NORMAL); + Image *newImage(love::image::CompressedData *cdata, Image::Format format = Image::FORMAT_NORMAL); Quad *newQuad(Quad::Viewport v, float sw, float sh); @@ -208,7 +208,7 @@ public: ParticleSystem *newParticleSystem(Texture *texture, int size); - Canvas *newCanvas(int width, int height, Texture::Format format = Texture::FORMAT_NORMAL, int fsaa = 0); + Canvas *newCanvas(int width, int height, Canvas::Format format = Canvas::FORMAT_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 2166fd4a5..b7a37977d 100644 --- a/src/modules/graphics/opengl/Image.cpp +++ b/src/modules/graphics/opengl/Image.cpp @@ -36,7 +36,7 @@ float Image::maxMipmapSharpness = 0.0f; Texture::FilterMode Image::defaultMipmapFilter = Texture::FILTER_NONE; float Image::defaultMipmapSharpness = 0.0f; -Image::Image(love::image::ImageData *data, Texture::Format format) +Image::Image(love::image::ImageData *data, Format format) : data(data) , cdata(nullptr) , paddedWidth(width) @@ -55,7 +55,7 @@ Image::Image(love::image::ImageData *data, Texture::Format format) preload(); } -Image::Image(love::image::CompressedData *cdata, Texture::Format format) +Image::Image(love::image::CompressedData *cdata, Format format) : data(nullptr) , cdata(cdata) , paddedWidth(width) @@ -504,7 +504,7 @@ bool Image::refresh() return true; } -Texture::Format Image::getFormat() const +Image::Format Image::getFormat() const { return format; } @@ -663,6 +663,24 @@ bool Image::hasSRGBSupport() return GLEE_VERSION_2_1 || GLEE_EXT_texture_sRGB; } +bool Image::getConstant(const char *in, Format &out) +{ + return formats.find(in, out); +} + +bool Image::getConstant(Format in, const char *&out) +{ + return formats.find(in, out); +} + +StringMap::Entry Image::formatEntries[] = +{ + {"normal", Image::FORMAT_NORMAL}, + {"srgb", Image::FORMAT_SRGB}, +}; + +StringMap Image::formats(Image::formatEntries, sizeof(Image::formatEntries)); + } // opengl } // graphics } // love diff --git a/src/modules/graphics/opengl/Image.h b/src/modules/graphics/opengl/Image.h index d23f385ec..6e24b17c6 100644 --- a/src/modules/graphics/opengl/Image.h +++ b/src/modules/graphics/opengl/Image.h @@ -25,6 +25,7 @@ #include "common/config.h" #include "common/Matrix.h" #include "common/Vector.h" +#include "common/StringMap.h" #include "common/math.h" #include "image/ImageData.h" #include "image/CompressedData.h" @@ -50,20 +51,27 @@ class Image : public Texture { public: + enum Format + { + FORMAT_NORMAL, + FORMAT_SRGB, + FORMAT_MAX_ENUM + }; + /** * Creates a new Image. Not that anything is ready to use * before load is called. * * @param data The data from which to load the image. **/ - Image(love::image::ImageData *data, Texture::Format format = Texture::FORMAT_NORMAL); + Image(love::image::ImageData *data, Format format = FORMAT_NORMAL); /** * Creates a new Image with compressed image data. * * @param cdata The compressed data from which to load the image. **/ - Image(love::image::CompressedData *cdata, Texture::Format format = Texture::FORMAT_NORMAL); + Image(love::image::CompressedData *cdata, Format format = FORMAT_NORMAL); /** * Destructor. Deletes the hardware texture and other resources. @@ -120,7 +128,7 @@ public: **/ bool refresh(); - Texture::Format getFormat() const; + Format getFormat() const; static void setDefaultMipmapSharpness(float sharpness); static float getDefaultMipmapSharpness(); @@ -137,6 +145,9 @@ public: static bool hasSRGBSupport(); + static bool getConstant(const char *in, Format &out); + static bool getConstant(Format in, const char *&out); + private: void uploadDefaultTexture(); @@ -166,8 +177,8 @@ private: // Whether this Image is using a compressed texture. bool compressed; - // The format to interpret the texture's data as. - Texture::Format format; + // The format to interpret the image's data as. + Format format; // True if the image wasn't able to be properly created and it had to fall // back to a default texture. @@ -189,6 +200,9 @@ private: GLenum getCompressedFormat(image::CompressedData::Format cformat) const; + static StringMap::Entry formatEntries[]; + static StringMap formats; + }; // Image } // opengl diff --git a/src/modules/graphics/opengl/wrap_Canvas.cpp b/src/modules/graphics/opengl/wrap_Canvas.cpp index a3c31b7f9..4efc610e1 100644 --- a/src/modules/graphics/opengl/wrap_Canvas.cpp +++ b/src/modules/graphics/opengl/wrap_Canvas.cpp @@ -113,10 +113,10 @@ int w_Canvas_clear(lua_State *L) int w_Canvas_getFormat(lua_State *L) { Canvas *canvas = luax_checkcanvas(L, 1); - Texture::Format format = canvas->getTextureFormat(); + Canvas::Format format = canvas->getTextureFormat(); const char *str; - if (!Texture::getConstant(format, str)) - return luaL_error(L, "Unknown texture format."); + if (!Canvas::getConstant(format, str)) + return luaL_error(L, "Unknown Canvas format."); lua_pushstring(L, str); return 1; diff --git a/src/modules/graphics/opengl/wrap_Graphics.cpp b/src/modules/graphics/opengl/wrap_Graphics.cpp index 243c9e908..1814cb2ef 100644 --- a/src/modules/graphics/opengl/wrap_Graphics.cpp +++ b/src/modules/graphics/opengl/wrap_Graphics.cpp @@ -155,14 +155,11 @@ int w_newImage(lua_State *L) love::image::ImageData *data = nullptr; love::image::CompressedData *cdata = nullptr; - Texture::Format format = Texture::FORMAT_NORMAL; + Image::Format format = Image::FORMAT_NORMAL; const char *fstr = lua_isnoneornil(L, 2) ? nullptr : luaL_checkstring(L, 2); - if (fstr != nullptr && !Texture::getConstant(fstr, format)) - return luaL_error(L, "Invalid texture format: %s", fstr); - - if (format == Texture::FORMAT_HDR) // For now... - return luaL_error(L, "HDR images are not supported."); + if (fstr != nullptr && !Image::getConstant(fstr, format)) + return luaL_error(L, "Invalid Image format: %s", fstr); // Convert to FileData, if necessary. if (lua_isstring(L, 1) || luax_istype(L, 1, FILESYSTEM_FILE_T)) @@ -334,9 +331,9 @@ int w_newCanvas(lua_State *L) const char *str = luaL_optstring(L, 3, "normal"); int fsaa = luaL_optint(L, 4, 0); - Texture::Format format; - if (!Texture::getConstant(str, format)) - return luaL_error(L, "Invalid texture format: %s", str); + Canvas::Format format; + if (!Canvas::getConstant(str, format)) + return luaL_error(L, "Invalid Canvas format: %s", str); Canvas *canvas = nullptr; EXCEPT_GUARD(canvas = instance->newCanvas(width, height, format, fsaa);) @@ -985,7 +982,7 @@ int w_isSupported(lua_State *L) supported = false; break; case Graphics::SUPPORT_HDR_CANVAS: - if (!Canvas::isHDRSupported()) + if (!Canvas::isFormatSupported(Canvas::FORMAT_HDR)) supported = false; break; case Graphics::SUPPORT_MULTI_CANVAS: @@ -1021,7 +1018,7 @@ int w_isSupported(lua_State *L) supported = false; break; case Graphics::SUPPORT_SRGB: - if (!Canvas::isSRGBSupported()) + if (!Canvas::isFormatSupported(Canvas::FORMAT_SRGB)) supported = false; break; default: @@ -1034,6 +1031,18 @@ int w_isSupported(lua_State *L) return 1; } +int w_hasCanvasFormat(lua_State *L) +{ + const char *str = luaL_checkstring(L, 1); + Canvas::Format format; + + if (!Canvas::getConstant(str, format)) + return luaL_error(L, "Invalid canvas format: %s", str); + + luax_pushboolean(L, Canvas::isFormatSupported(format)); + return 1; +} + int w_getRendererInfo(lua_State *L) { std::string name, version, vendor, device; @@ -1410,6 +1419,7 @@ static const luaL_Reg functions[] = { "getShader", w_getShader }, { "isSupported", w_isSupported }, + { "hasCanvasFormat", w_hasCanvasFormat }, { "getRendererInfo", w_getRendererInfo }, { "getSystemLimit", w_getSystemLimit }, diff --git a/src/modules/graphics/opengl/wrap_Graphics.h b/src/modules/graphics/opengl/wrap_Graphics.h index 85977cca6..9a18bb7f3 100644 --- a/src/modules/graphics/opengl/wrap_Graphics.h +++ b/src/modules/graphics/opengl/wrap_Graphics.h @@ -93,6 +93,7 @@ int w_getCanvas(lua_State *L); int w_setShader(lua_State *L); int w_getShader(lua_State *L); int w_isSupported(lua_State *L); +int w_hasCanvasFormat(lua_State *L); int w_getRendererInfo(lua_State *L); int w_getSystemLimit(lua_State *L); int w_draw(lua_State *L);