From cffd4859760ca9bd3279601c2a38730cbeb5858b Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Thu, 12 Aug 2021 17:43:33 -0300 Subject: [PATCH] Allow rendering to integer textures. --- src/common/Color.h | 1 + src/modules/graphics/Graphics.cpp | 6 --- src/modules/graphics/Graphics.h | 6 +-- src/modules/graphics/Shader.cpp | 37 ++++++++++++++++++ src/modules/graphics/Shader.h | 2 + src/modules/graphics/opengl/Graphics.cpp | 48 ++++++++++++++++++------ src/modules/graphics/opengl/Graphics.h | 4 +- src/modules/graphics/opengl/Shader.cpp | 34 ----------------- src/modules/graphics/wrap_Graphics.cpp | 18 ++++----- 9 files changed, 90 insertions(+), 66 deletions(-) diff --git a/src/common/Color.h b/src/common/Color.h index ff5f74f55..5da99c7a8 100644 --- a/src/common/Color.h +++ b/src/common/Color.h @@ -137,6 +137,7 @@ ColorT operator/(const ColorT &a, T s) typedef ColorT Color32; typedef ColorT Colorf; +typedef ColorT ColorD; inline Color32 toColor32(Colorf cf) { diff --git a/src/modules/graphics/Graphics.cpp b/src/modules/graphics/Graphics.cpp index 74f939932..719acaa51 100644 --- a/src/modules/graphics/Graphics.cpp +++ b/src/modules/graphics/Graphics.cpp @@ -693,9 +693,6 @@ void Graphics::setRenderTargets(const RenderTargets &rts) if (!firsttex->isValidSlice(firsttarget.slice)) throw love::Exception("Invalid slice index: %d.", firsttarget.slice + 1); - if (isPixelFormatInteger(firstcolorformat)) - throw love::Exception("Textures with integer pixel formats cannot be rendered to, currently."); - bool hasSRGBtexture = firstcolorformat == PIXELFORMAT_sRGBA8_UNORM; int pixelw = firsttex->getPixelWidth(firsttarget.mipmap); int pixelh = firsttex->getPixelHeight(firsttarget.mipmap); @@ -729,9 +726,6 @@ void Graphics::setRenderTargets(const RenderTargets &rts) if (isPixelFormatDepthStencil(format)) throw love::Exception("Depth/stencil format textures must be used with the 'depthstencil' field of the table passed into setRenderTargets."); - if (isPixelFormatInteger(format)) - throw love::Exception("Textures with integer pixel formats cannot be rendered to, currently."); - if (format == PIXELFORMAT_sRGBA8_UNORM) hasSRGBtexture = true; } diff --git a/src/modules/graphics/Graphics.h b/src/modules/graphics/Graphics.h index 31d3b2502..92e6639e2 100644 --- a/src/modules/graphics/Graphics.h +++ b/src/modules/graphics/Graphics.h @@ -61,7 +61,7 @@ class Text; class Video; class Buffer; -typedef Optional OptionalColorf; +typedef Optional OptionalColorD; const int MAX_COLOR_RENDER_TARGETS = 8; @@ -457,8 +457,8 @@ public: **/ void reset(); - virtual void clear(OptionalColorf color, OptionalInt stencil, OptionalDouble depth) = 0; - virtual void clear(const std::vector &colors, OptionalInt stencil, OptionalDouble depth) = 0; + virtual void clear(OptionalColorD color, OptionalInt stencil, OptionalDouble depth) = 0; + virtual void clear(const std::vector &colors, OptionalInt stencil, OptionalDouble depth) = 0; virtual void discard(const std::vector &colorbuffers, bool depthstencil) = 0; diff --git a/src/modules/graphics/Shader.cpp b/src/modules/graphics/Shader.cpp index 82b62d161..fee230eb2 100644 --- a/src/modules/graphics/Shader.cpp +++ b/src/modules/graphics/Shader.cpp @@ -640,6 +640,40 @@ const Shader::UniformInfo *Shader::getMainTextureInfo() const return getUniformInfo(BUILTIN_TEXTURE_MAIN); } +DataBaseType Shader::getDataBaseType(PixelFormat format) +{ + switch (getPixelFormatInfo(format).dataType) + { + case PIXELFORMATTYPE_UNORM: + return DATA_BASETYPE_UNORM; + case PIXELFORMATTYPE_SNORM: + return DATA_BASETYPE_SNORM; + case PIXELFORMATTYPE_UFLOAT: + case PIXELFORMATTYPE_SFLOAT: + return DATA_BASETYPE_FLOAT; + case PIXELFORMATTYPE_SINT: + return DATA_BASETYPE_INT; + case PIXELFORMATTYPE_UINT: + return DATA_BASETYPE_UINT; + default: + return DATA_BASETYPE_FLOAT; + } +} + +bool Shader::isResourceBaseTypeCompatible(DataBaseType a, DataBaseType b) +{ + if (a == DATA_BASETYPE_FLOAT || a == DATA_BASETYPE_UNORM || a == DATA_BASETYPE_SNORM) + return b == DATA_BASETYPE_FLOAT || b == DATA_BASETYPE_UNORM || b == DATA_BASETYPE_SNORM; + + if (a == DATA_BASETYPE_INT && b == DATA_BASETYPE_INT) + return true; + + if (a == DATA_BASETYPE_UINT && b == DATA_BASETYPE_UINT) + return true; + + return false; +} + void Shader::validateDrawState(PrimitiveType primtype, Texture *maintex) const { if ((primtype == PRIMITIVE_POINTS) != validationReflection.usesPointSize) @@ -672,6 +706,9 @@ void Shader::validateDrawState(PrimitiveType primtype, Texture *maintex) const throw love::Exception("Texture's type (%s) must match the type of the shader's main texture type (%s).", textypestr, shadertextypestr); } + if (!isResourceBaseTypeCompatible(info->dataBaseType, getDataBaseType(maintex->getPixelFormat()))) + throw love::Exception("Texture's data format base type must match the uniform variable declared in the shader (float, int, or uint)."); + if (info->isDepthSampler != maintex->getSamplerState().depthSampleMode.hasValue) { if (info->isDepthSampler) diff --git a/src/modules/graphics/Shader.h b/src/modules/graphics/Shader.h index 543478f01..b9b497969 100644 --- a/src/modules/graphics/Shader.h +++ b/src/modules/graphics/Shader.h @@ -259,6 +259,8 @@ protected: }; static bool validateInternal(StrongRef stages[], std::string& err, ValidationReflection &reflection); + static DataBaseType getDataBaseType(PixelFormat format); + static bool isResourceBaseTypeCompatible(DataBaseType a, DataBaseType b); StrongRef stages[SHADERSTAGE_MAX_ENUM]; diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index 2c87282ff..0fd4a6411 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/src/modules/graphics/opengl/Graphics.cpp @@ -835,7 +835,7 @@ void Graphics::endPass() } } -void Graphics::clear(OptionalColorf c, OptionalInt stencil, OptionalDouble depth) +void Graphics::clear(OptionalColorD c, OptionalInt stencil, OptionalDouble depth) { if (c.hasValue || stencil.hasValue || depth.hasValue) flushBatchedDraws(); @@ -844,8 +844,9 @@ void Graphics::clear(OptionalColorf c, OptionalInt stencil, OptionalDouble depth if (c.hasValue) { - gammaCorrectColor(c.value); - glClearColor(c.value.r, c.value.g, c.value.b, c.value.a); + Colorf cf((float)c.value.r, (float)c.value.g, (float)c.value.b, (float)c.value.b); + gammaCorrectColor(cf); + glClearColor(cf.r, cf.g, cf.b, cf.a); flags |= GL_COLOR_BUFFER_BIT; } @@ -881,17 +882,19 @@ void Graphics::clear(OptionalColorf c, OptionalInt stencil, OptionalDouble depth } } -void Graphics::clear(const std::vector &colors, OptionalInt stencil, OptionalDouble depth) +void Graphics::clear(const std::vector &colors, OptionalInt stencil, OptionalDouble depth) { if (colors.size() == 0 && !stencil.hasValue && !depth.hasValue) return; - int ncolorRTs = (int) states.back().renderTargets.colors.size(); + const auto &rts = states.back().renderTargets.colors; + + int ncolorRTs = (int) rts.size(); int ncolors = (int) colors.size(); - if (ncolors <= 1 && ncolorRTs <= 1) + if (ncolors <= 1 && (ncolorRTs == 0 || (ncolorRTs == 1 && rts[0].texture != nullptr && !isPixelFormatInteger(rts[0].texture->getPixelFormat())))) { - clear(ncolors > 0 ? colors[0] : OptionalColorf(), stencil, depth); + clear(ncolors > 0 ? colors[0] : OptionalColorD(), stencil, depth); return; } @@ -905,18 +908,39 @@ void Graphics::clear(const std::vector &colors, OptionalInt sten if (!colors[i].hasValue) continue; - Colorf c = colors[i].value; - gammaCorrectColor(c); + PixelFormatType datatype = PIXELFORMATTYPE_UNORM; + if (rts[i].texture != nullptr) + datatype = getPixelFormatInfo(rts[i].texture->getPixelFormat()).dataType; + + ColorD c = colors[i].value; if (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0) { - const GLfloat carray[] = {c.r, c.g, c.b, c.a}; - glClearBufferfv(GL_COLOR, i, carray); + if (datatype == PIXELFORMATTYPE_SINT) + { + const GLint carray[] = {(GLint)c.r, (GLint)c.g, (GLint)c.b, (GLint)c.a}; + glClearBufferiv(GL_COLOR, i, carray); + } + else if (datatype == PIXELFORMATTYPE_UINT) + { + const GLuint carray[] = {(GLuint)c.r, (GLuint)c.g, (GLuint)c.b, (GLuint)c.a}; + glClearBufferuiv(GL_COLOR, i, carray); + } + else + { + Colorf cf((float)c.r, (float)c.g, (float)c.b, (float)c.a); + gammaCorrectColor(cf); + const GLfloat carray[] = {cf.r, cf.g, cf.b, cf.a}; + glClearBufferfv(GL_COLOR, i, carray); + } } else { + Colorf cf((float)c.r, (float)c.g, (float)c.b, (float)c.a); + gammaCorrectColor(cf); + glDrawBuffer(GL_COLOR_ATTACHMENT0 + i); - glClearColor(c.r, c.g, c.b, c.a); + glClearColor(cf.r, cf.g, cf.b, cf.a); glClear(GL_COLOR_BUFFER_BIT); drawbuffersmodified = true; diff --git a/src/modules/graphics/opengl/Graphics.h b/src/modules/graphics/opengl/Graphics.h index fc03d24ea..b568c4ebb 100644 --- a/src/modules/graphics/opengl/Graphics.h +++ b/src/modules/graphics/opengl/Graphics.h @@ -74,8 +74,8 @@ public: void draw(const DrawIndexedCommand &cmd) override; void drawQuads(int start, int count, const VertexAttributes &attributes, const BufferBindings &buffers, love::graphics::Texture *texture) override; - void clear(OptionalColorf color, OptionalInt stencil, OptionalDouble depth) override; - void clear(const std::vector &colors, OptionalInt stencil, OptionalDouble depth) override; + void clear(OptionalColorD color, OptionalInt stencil, OptionalDouble depth) override; + void clear(const std::vector &colors, OptionalInt stencil, OptionalDouble depth) override; void discard(const std::vector &colorbuffers, bool depthstencil) override; diff --git a/src/modules/graphics/opengl/Shader.cpp b/src/modules/graphics/opengl/Shader.cpp index c22b932d7..eea214d57 100644 --- a/src/modules/graphics/opengl/Shader.cpp +++ b/src/modules/graphics/opengl/Shader.cpp @@ -710,40 +710,6 @@ void Shader::updateUniform(const UniformInfo *info, int count, bool internalupda } } -static bool isResourceBaseTypeCompatible(DataBaseType a, DataBaseType b) -{ - if (a == DATA_BASETYPE_FLOAT || a == DATA_BASETYPE_UNORM || a == DATA_BASETYPE_SNORM) - return b == DATA_BASETYPE_FLOAT || b == DATA_BASETYPE_UNORM || b == DATA_BASETYPE_SNORM; - - if (a == DATA_BASETYPE_INT && b == DATA_BASETYPE_INT) - return true; - - if (a == DATA_BASETYPE_UINT && b == DATA_BASETYPE_UINT) - return true; - - return false; -} - -static DataBaseType getDataBaseType(PixelFormat format) -{ - switch (getPixelFormatInfo(format).dataType) - { - case PIXELFORMATTYPE_UNORM: - return DATA_BASETYPE_UNORM; - case PIXELFORMATTYPE_SNORM: - return DATA_BASETYPE_SNORM; - case PIXELFORMATTYPE_UFLOAT: - case PIXELFORMATTYPE_SFLOAT: - return DATA_BASETYPE_FLOAT; - case PIXELFORMATTYPE_SINT: - return DATA_BASETYPE_INT; - case PIXELFORMATTYPE_UINT: - return DATA_BASETYPE_UINT; - default: - return DATA_BASETYPE_FLOAT; - } -} - void Shader::sendTextures(const UniformInfo *info, love::graphics::Texture **textures, int count) { Shader::sendTextures(info, textures, count, false); diff --git a/src/modules/graphics/wrap_Graphics.cpp b/src/modules/graphics/wrap_Graphics.cpp index 07526e9a5..18196a67e 100644 --- a/src/modules/graphics/wrap_Graphics.cpp +++ b/src/modules/graphics/wrap_Graphics.cpp @@ -68,8 +68,8 @@ int w_reset(lua_State *) int w_clear(lua_State *L) { - OptionalColorf color(Colorf(0.0f, 0.0f, 0.0f, 0.0f)); - std::vector colors; + OptionalColorD color(ColorD(0.0, 0.0, 0.0, 0.0)); + std::vector colors; OptionalInt stencil(0); OptionalDouble depth(1.0); @@ -93,19 +93,19 @@ int w_clear(lua_State *L) } else if (argtype == LUA_TNIL || argtype == LUA_TNONE || luax_objlen(L, i + 1) == 0) { - colors.push_back(OptionalColorf()); + colors.push_back(OptionalColorD()); continue; } for (int j = 1; j <= 4; j++) lua_rawgeti(L, i + 1, j); - OptionalColorf c; + OptionalColorD c; c.hasValue = true; - c.value.r = (float) luaL_checknumber(L, -4); - c.value.g = (float) luaL_checknumber(L, -3); - c.value.b = (float) luaL_checknumber(L, -2); - c.value.a = (float) luaL_optnumber(L, -1, 1.0); + c.value.r = luaL_checknumber(L, -4); + c.value.g = luaL_checknumber(L, -3); + c.value.b = luaL_checknumber(L, -2); + c.value.a = luaL_optnumber(L, -1, 1.0); colors.push_back(c); lua_pop(L, 4); @@ -627,7 +627,7 @@ int w_stencil(lua_State *L) luaL_checktype(L, 4, LUA_TBOOLEAN); if (stencilclear.hasValue) - instance()->clear(OptionalColorf(), stencilclear, OptionalDouble()); + instance()->clear(OptionalColorD(), stencilclear, OptionalDouble()); luax_catchexcept(L, [&](){ instance()->drawToStencilBuffer(action, stencilvalue); });