From a0ef2ac28ba16cd818898289ab418bbfc13b2f06 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Wed, 12 Apr 2017 20:26:06 -0300 Subject: [PATCH] Add Depth compare / shadow sampler support. Officially this is only supported in glsl3 shaders. - Add Texture:setDepthSampleMode(comparemode). Only works on textures with depth pixel formats. A texture with the depth sample mode set will only work with a depth sampler. - Add DepthImage, DepthArrayImage, and DepthCubeImage sampler keywords to glsl3. --HG-- branch : minor --- CMakeLists.txt | 2 + .../xcode/liblove.xcodeproj/project.pbxproj | 8 ++ src/common/Optional.h | 9 +- src/common/pixelformat.cpp | 6 ++ src/common/pixelformat.h | 7 +- src/modules/graphics/Graphics.cpp | 55 ++--------- src/modules/graphics/Graphics.h | 38 +------ src/modules/graphics/Shader.cpp | 21 +++- src/modules/graphics/Shader.h | 4 +- src/modules/graphics/Texture.cpp | 11 +++ src/modules/graphics/Texture.h | 7 ++ src/modules/graphics/depthstencil.cpp | 99 +++++++++++++++++++ src/modules/graphics/depthstencil.h | 68 +++++++++++++ src/modules/graphics/opengl/Canvas.cpp | 41 ++++++++ src/modules/graphics/opengl/Canvas.h | 1 + src/modules/graphics/opengl/Graphics.cpp | 40 +------- src/modules/graphics/opengl/Image.cpp | 10 ++ src/modules/graphics/opengl/OpenGL.cpp | 30 ++++++ src/modules/graphics/opengl/OpenGL.h | 3 + src/modules/graphics/opengl/Shader.cpp | 72 +++++++++++--- src/modules/graphics/opengl/Shader.h | 1 + src/modules/graphics/opengl/Text.cpp | 2 +- src/modules/graphics/wrap_Graphics.cpp | 12 +-- src/modules/graphics/wrap_Graphics.lua | 10 ++ src/modules/graphics/wrap_Texture.cpp | 38 +++++++ 25 files changed, 444 insertions(+), 151 deletions(-) create mode 100644 src/modules/graphics/depthstencil.cpp create mode 100644 src/modules/graphics/depthstencil.h diff --git a/CMakeLists.txt b/CMakeLists.txt index e212fa074..c5f9970cf 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -475,6 +475,8 @@ set(LOVE_SRC_MODULE_GRAPHICS_ROOT src/modules/graphics/Canvas.cpp src/modules/graphics/Canvas.h src/modules/graphics/Color.h + src/modules/graphics/depthstencil.cpp + src/modules/graphics/depthstencil.h src/modules/graphics/Drawable.cpp src/modules/graphics/Drawable.h src/modules/graphics/Font.cpp diff --git a/platform/xcode/liblove.xcodeproj/project.pbxproj b/platform/xcode/liblove.xcodeproj/project.pbxproj index 544206209..63064c01a 100644 --- a/platform/xcode/liblove.xcodeproj/project.pbxproj +++ b/platform/xcode/liblove.xcodeproj/project.pbxproj @@ -1132,6 +1132,8 @@ FAF140DB1E20934C00F898D2 /* InitializeDll.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAF1403B1E20934C00F898D2 /* InitializeDll.cpp */; }; FAF140DC1E20934C00F898D2 /* InitializeDll.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAF1403B1E20934C00F898D2 /* InitializeDll.cpp */; }; FAF140DD1E20934C00F898D2 /* InitializeDll.h in Headers */ = {isa = PBXBuildFile; fileRef = FAF1403C1E20934C00F898D2 /* InitializeDll.h */; }; + FAF1889F1E9DBC4B008C1479 /* depthstencil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAF1889E1E9DBC4B008C1479 /* depthstencil.cpp */; }; + FAF188A01E9DBC4B008C1479 /* depthstencil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAF1889E1E9DBC4B008C1479 /* depthstencil.cpp */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -1937,6 +1939,8 @@ FAF1403B1E20934C00F898D2 /* InitializeDll.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InitializeDll.cpp; sourceTree = ""; }; FAF1403C1E20934C00F898D2 /* InitializeDll.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InitializeDll.h; sourceTree = ""; }; FAF1889C1E9DA834008C1479 /* Optional.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Optional.h; sourceTree = ""; }; + FAF1889D1E9DBBC8008C1479 /* depthstencil.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = depthstencil.h; sourceTree = ""; }; + FAF1889E1E9DBC4B008C1479 /* depthstencil.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = depthstencil.cpp; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -2663,6 +2667,8 @@ FA1BA0A51E16F20600AA2803 /* Canvas.cpp */, FA1BA0A61E16F20600AA2803 /* Canvas.h */, FA0B7B881A95902C000E1D17 /* Color.h */, + FAF1889E1E9DBC4B008C1479 /* depthstencil.cpp */, + FAF1889D1E9DBBC8008C1479 /* depthstencil.h */, FA9D8DDC1DEF842A002CD881 /* Drawable.cpp */, FA0B7B891A95902C000E1D17 /* Drawable.h */, FA1BA09B1E16CFCE00AA2803 /* Font.cpp */, @@ -4185,6 +4191,7 @@ FA0B7DDD1A95902C000E1D17 /* wrap_BezierCurve.cpp in Sources */, FA0B7A2A1A958EA3000E1D17 /* b2BroadPhase.cpp in Sources */, FA0B7A811A958EA3000E1D17 /* b2EdgeAndCircleContact.cpp in Sources */, + FAF188A01E9DBC4B008C1479 /* depthstencil.cpp in Sources */, FA0B7D071A95902C000E1D17 /* wrap_File.cpp in Sources */, FAD19A181DFF8CA200D5398A /* ImageDataBase.cpp in Sources */, FA0B7AD01A958EA3000E1D17 /* peer.c in Sources */, @@ -4545,6 +4552,7 @@ FA0B7DDC1A95902C000E1D17 /* wrap_BezierCurve.cpp in Sources */, FA0B7A951A958EA3000E1D17 /* b2Joint.cpp in Sources */, FA0B7D061A95902C000E1D17 /* wrap_File.cpp in Sources */, + FAF1889F1E9DBC4B008C1479 /* depthstencil.cpp in Sources */, FA0B7A4E1A958EA3000E1D17 /* b2Draw.cpp in Sources */, FAD19A171DFF8CA200D5398A /* ImageDataBase.cpp in Sources */, FA27B3C01B4985BF008A9DCE /* wrap_VideoStream.cpp in Sources */, diff --git a/src/common/Optional.h b/src/common/Optional.h index aa40b3cd6..7f16fec5b 100644 --- a/src/common/Optional.h +++ b/src/common/Optional.h @@ -23,7 +23,7 @@ namespace love { -// Currently only meant for simple types. +// Currently only meant for simple and small types. template struct Optional { @@ -40,8 +40,11 @@ struct Optional , value(val) {} -private: - + void set(T val) + { + hasValue = true; + value = val; + } }; typedef Optional OptionalBool; diff --git a/src/common/pixelformat.cpp b/src/common/pixelformat.cpp index a6334aa54..365812432 100644 --- a/src/common/pixelformat.cpp +++ b/src/common/pixelformat.cpp @@ -125,6 +125,12 @@ bool isPixelFormatDepthStencil(PixelFormat format) return iformat >= (int) PIXELFORMAT_STENCIL8 && iformat <= (int) PIXELFORMAT_DEPTH32F_STENCIL8; } +bool isPixelFormatDepth(PixelFormat format) +{ + int iformat = (int) format; + return iformat >= (int) PIXELFORMAT_DEPTH16 && iformat <= (int) PIXELFORMAT_DEPTH32F_STENCIL8; +} + size_t getPixelFormatSize(PixelFormat format) { switch (format) diff --git a/src/common/pixelformat.h b/src/common/pixelformat.h index 37dde8a00..a293185d9 100644 --- a/src/common/pixelformat.h +++ b/src/common/pixelformat.h @@ -115,10 +115,15 @@ bool getConstant(const char *in, PixelFormat &out); bool isPixelFormatCompressed(PixelFormat format); /** - * Gets whether the specified pixel format is a depth/stencil type. + * Gets whether the specified pixel format is a depth or stencil type. **/ bool isPixelFormatDepthStencil(PixelFormat format); +/** + * Gets whether the specified pixel format is a depth type. + **/ +bool isPixelFormatDepth(PixelFormat format); + /** * Gets the size in bytes of the specified pixel format. * NOTE: Currently returns 0 for compressed formats. diff --git a/src/modules/graphics/Graphics.cpp b/src/modules/graphics/Graphics.cpp index 704505530..1171ad491 100644 --- a/src/modules/graphics/Graphics.cpp +++ b/src/modules/graphics/Graphics.cpp @@ -589,6 +589,9 @@ Graphics::StreamVertexData Graphics::requestStreamDraw(const StreamDrawRequest & StreamBufferState &state = streamBufferState; + if (state.vertexCount == 0 && Shader::current != nullptr && req.texture != nullptr) + Shader::current->checkMainTexture(req.texture); + bool shouldflush = false; bool shouldresize = false; @@ -706,6 +709,13 @@ Graphics::StreamVertexData Graphics::requestStreamDraw(const StreamDrawRequest & return d; } +void Graphics::flushStreamDrawsGlobal() +{ + Graphics *instance = getInstance(M_GRAPHICS); + if (instance != nullptr) + instance->flushStreamDraws(); +} + /** * Drawing **/ @@ -1266,26 +1276,6 @@ bool Graphics::getConstant(LineJoin in, const char *&out) return lineJoins.find(in, out); } -bool Graphics::getConstant(const char *in, StencilAction &out) -{ - return stencilActions.find(in, out); -} - -bool Graphics::getConstant(StencilAction in, const char *&out) -{ - return stencilActions.find(in, out); -} - -bool Graphics::getConstant(const char *in, CompareMode &out) -{ - return compareModes.find(in, out); -} - -bool Graphics::getConstant(CompareMode in, const char *&out) -{ - return compareModes.find(in, out); -} - bool Graphics::getConstant(const char *in, Feature &out) { return features.find(in, out); @@ -1373,31 +1363,6 @@ StringMap::Entry Graphics::lin StringMap Graphics::lineJoins(Graphics::lineJoinEntries, sizeof(Graphics::lineJoinEntries)); -StringMap::Entry Graphics::stencilActionEntries[] = -{ - { "replace", STENCIL_REPLACE }, - { "increment", STENCIL_INCREMENT }, - { "decrement", STENCIL_DECREMENT }, - { "incrementwrap", STENCIL_INCREMENT_WRAP }, - { "decrementwrap", STENCIL_DECREMENT_WRAP }, - { "invert", STENCIL_INVERT }, -}; - -StringMap Graphics::stencilActions(Graphics::stencilActionEntries, sizeof(Graphics::stencilActionEntries)); - -StringMap::Entry Graphics::compareModeEntries[] = -{ - { "less", COMPARE_LESS }, - { "lequal", COMPARE_LEQUAL }, - { "equal", COMPARE_EQUAL }, - { "gequal", COMPARE_GEQUAL }, - { "greater", COMPARE_GREATER }, - { "notequal", COMPARE_NOTEQUAL }, - { "always", COMPARE_ALWAYS }, -}; - -StringMap Graphics::compareModes(Graphics::compareModeEntries, sizeof(Graphics::compareModeEntries)); - StringMap::Entry Graphics::featureEntries[] = { { "multicanvasformats", FEATURE_MULTI_CANVAS_FORMATS }, diff --git a/src/modules/graphics/Graphics.h b/src/modules/graphics/Graphics.h index 323393bb5..e8f35bc24 100644 --- a/src/modules/graphics/Graphics.h +++ b/src/modules/graphics/Graphics.h @@ -37,6 +37,7 @@ #include "Quad.h" #include "Mesh.h" #include "Image.h" +#include "depthstencil.h" #include "math/Transform.h" #include "font/Rasterizer.h" #include "video/VideoStream.h" @@ -150,29 +151,6 @@ public: LINE_JOIN_MAX_ENUM }; - enum StencilAction - { - STENCIL_REPLACE, - STENCIL_INCREMENT, - STENCIL_DECREMENT, - STENCIL_INCREMENT_WRAP, - STENCIL_DECREMENT_WRAP, - STENCIL_INVERT, - STENCIL_MAX_ENUM - }; - - enum CompareMode - { - COMPARE_LESS, - COMPARE_LEQUAL, - COMPARE_EQUAL, - COMPARE_GEQUAL, - COMPARE_GREATER, - COMPARE_NOTEQUAL, - COMPARE_ALWAYS, - COMPARE_MAX_ENUM - }; - enum Feature { FEATURE_MULTI_CANVAS_FORMATS, @@ -761,6 +739,8 @@ public: virtual void flushStreamDraws() = 0; StreamVertexData requestStreamDraw(const StreamDrawRequest &request); + static void flushStreamDrawsGlobal(); + virtual Shader::Language getShaderLanguageTarget() const = 0; const Shader::ShaderSource &getCurrentDefaultShaderCode() const; @@ -793,12 +773,6 @@ public: static bool getConstant(const char *in, LineJoin &out); static bool getConstant(LineJoin in, const char *&out); - static bool getConstant(const char *in, StencilAction &out); - static bool getConstant(StencilAction in, const char *&out); - - static bool getConstant(const char *in, CompareMode &out); - static bool getConstant(CompareMode in, const char *&out); - static bool getConstant(const char *in, Feature &out); static bool getConstant(Feature in, const char *&out); @@ -932,12 +906,6 @@ private: static StringMap::Entry lineJoinEntries[]; static StringMap lineJoins; - static StringMap::Entry stencilActionEntries[]; - static StringMap stencilActions; - - static StringMap::Entry compareModeEntries[]; - static StringMap compareModes; - static StringMap::Entry featureEntries[]; static StringMap features; diff --git a/src/modules/graphics/Shader.cpp b/src/modules/graphics/Shader.cpp index 8b5938eba..1fe2ae3a8 100644 --- a/src/modules/graphics/Shader.cpp +++ b/src/modules/graphics/Shader.cpp @@ -192,11 +192,14 @@ TextureType Shader::getMainTextureType() const return info != nullptr ? info->textureType : TEXTURE_MAX_ENUM; } -void Shader::checkMainTextureType(TextureType textype) const +void Shader::checkMainTextureType(TextureType textype, bool isDepthSampler) const { const UniformInfo *info = getUniformInfo(BUILTIN_TEXTURE_MAIN); - if (info != nullptr && info->textureType != TEXTURE_MAX_ENUM && info->textureType != textype) + if (info == nullptr) + return; + + if (info->textureType != TEXTURE_MAX_ENUM && info->textureType != textype) { const char *textypestr = "unknown"; const char *shadertextypestr = "unknown"; @@ -204,14 +207,22 @@ void Shader::checkMainTextureType(TextureType textype) const Texture::getConstant(info->textureType, shadertextypestr); throw love::Exception("Texture's type (%s) must match the type of the shader's main texture type (%s).", textypestr, shadertextypestr); } + + if (info->isDepthSampler != isDepthSampler) + { + if (info->isDepthSampler) + throw love::Exception("Depth comparison samplers in shaders can only be used with depth textures which have depth comparison set."); + else + throw love::Exception("Depth textures which have depth comparison set can only be used with depth/shadow samplers in shaders."); + } } -void Shader::checkMainTexture(Texture *texture) const +void Shader::checkMainTexture(Texture *tex) const { - if (!texture->isReadable()) + if (!tex->isReadable()) throw love::Exception("Textures with non-readable formats cannot be sampled from in a shader."); - checkMainTextureType(texture->getTextureType()); + checkMainTextureType(tex->getTextureType(), tex->getDepthSampleMode().hasValue); } bool Shader::validate(Graphics *gfx, bool gles, const ShaderSource &source, bool checkWithDefaults, std::string &err) diff --git a/src/modules/graphics/Shader.h b/src/modules/graphics/Shader.h index f1703b34b..31ff693b7 100644 --- a/src/modules/graphics/Shader.h +++ b/src/modules/graphics/Shader.h @@ -128,7 +128,7 @@ public: UniformType baseType; TextureType textureType; - bool isDepthTexture; + bool isDepthSampler; std::string name; union @@ -190,7 +190,7 @@ public: virtual void setVideoTextures(Texture *ytexture, Texture *cbtexture, Texture *crtexture) = 0; TextureType getMainTextureType() const; - void checkMainTextureType(TextureType textype) const; + void checkMainTextureType(TextureType textype, bool isDepthSampler) const; void checkMainTexture(Texture *texture) const; virtual ptrdiff_t getHandle() const = 0; diff --git a/src/modules/graphics/Texture.cpp b/src/modules/graphics/Texture.cpp index df49fe057..e9e8d62c9 100644 --- a/src/modules/graphics/Texture.cpp +++ b/src/modules/graphics/Texture.cpp @@ -230,6 +230,17 @@ float Texture::getMipmapSharpness() const return mipmapSharpness; } +void Texture::setDepthSampleMode(Optional mode) +{ + if (mode.hasValue && (!readable || !isPixelFormatDepthStencil(format))) + throw love::Exception("Only readable depth textures can have a depth sample compare mode."); +} + +Optional Texture::getDepthSampleMode() const +{ + return depthCompareMode; +} + Quad *Texture::getQuad() const { return quad; diff --git a/src/modules/graphics/Texture.h b/src/modules/graphics/Texture.h index e91a94826..af5e10d25 100644 --- a/src/modules/graphics/Texture.h +++ b/src/modules/graphics/Texture.h @@ -26,9 +26,11 @@ #include "common/math.h" #include "common/pixelformat.h" #include "common/Exception.h" +#include "common/Optional.h" #include "Drawable.h" #include "Quad.h" #include "vertex.h" +#include "depthstencil.h" // C #include @@ -143,6 +145,9 @@ public: virtual bool setMipmapSharpness(float sharpness) = 0; float getMipmapSharpness() const; + virtual void setDepthSampleMode(Optional mode = Optional()); + Optional getDepthSampleMode() const; + Quad *getQuad() const; virtual ptrdiff_t getHandle() const = 0; @@ -185,6 +190,8 @@ protected: float mipmapSharpness; + Optional depthCompareMode; + StrongRef quad; private: diff --git a/src/modules/graphics/depthstencil.cpp b/src/modules/graphics/depthstencil.cpp new file mode 100644 index 000000000..e94413aaa --- /dev/null +++ b/src/modules/graphics/depthstencil.cpp @@ -0,0 +1,99 @@ +/** + * Copyright (c) 2006-2017 LOVE Development Team + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + **/ + +#include "depthstencil.h" +#include "common/StringMap.h" + +namespace love +{ +namespace graphics +{ + +CompareMode getReversedCompareMode(CompareMode mode) +{ + switch (mode) + { + case COMPARE_LESS: + return COMPARE_GREATER; + case COMPARE_LEQUAL: + return COMPARE_GEQUAL; + case COMPARE_EQUAL: + return COMPARE_EQUAL; + case COMPARE_GEQUAL: + return COMPARE_LEQUAL; + case COMPARE_GREATER: + return COMPARE_LESS; + case COMPARE_NOTEQUAL: + return COMPARE_NOTEQUAL; + case COMPARE_ALWAYS: + return COMPARE_ALWAYS; + case COMPARE_MAX_ENUM: + default: + return COMPARE_MAX_ENUM; + } +} + +static StringMap::Entry stencilActionEntries[] = +{ + { "replace", STENCIL_REPLACE }, + { "increment", STENCIL_INCREMENT }, + { "decrement", STENCIL_DECREMENT }, + { "incrementwrap", STENCIL_INCREMENT_WRAP }, + { "decrementwrap", STENCIL_DECREMENT_WRAP }, + { "invert", STENCIL_INVERT }, +}; + +static StringMap stencilActions(stencilActionEntries, sizeof(stencilActionEntries)); + +static StringMap::Entry compareModeEntries[] = +{ + { "less", COMPARE_LESS }, + { "lequal", COMPARE_LEQUAL }, + { "equal", COMPARE_EQUAL }, + { "gequal", COMPARE_GEQUAL }, + { "greater", COMPARE_GREATER }, + { "notequal", COMPARE_NOTEQUAL }, + { "always", COMPARE_ALWAYS }, +}; + +static StringMap compareModes(compareModeEntries, sizeof(compareModeEntries)); + +bool getConstant(const char *in, StencilAction &out) +{ + return stencilActions.find(in, out); +} + +bool getConstant(StencilAction in, const char *&out) +{ + return stencilActions.find(in, out); +} + +bool getConstant(const char *in, CompareMode &out) +{ + return compareModes.find(in, out); +} + +bool getConstant(CompareMode in, const char *&out) +{ + return compareModes.find(in, out); +} + +} // graphics +} // love diff --git a/src/modules/graphics/depthstencil.h b/src/modules/graphics/depthstencil.h new file mode 100644 index 000000000..a251c192e --- /dev/null +++ b/src/modules/graphics/depthstencil.h @@ -0,0 +1,68 @@ +/** + * Copyright (c) 2006-2017 LOVE Development Team + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + **/ + +#pragma once + +namespace love +{ +namespace graphics +{ + +enum StencilAction +{ + STENCIL_REPLACE, + STENCIL_INCREMENT, + STENCIL_DECREMENT, + STENCIL_INCREMENT_WRAP, + STENCIL_DECREMENT_WRAP, + STENCIL_INVERT, + STENCIL_MAX_ENUM +}; + +enum CompareMode +{ + COMPARE_LESS, + COMPARE_LEQUAL, + COMPARE_EQUAL, + COMPARE_GEQUAL, + COMPARE_GREATER, + COMPARE_NOTEQUAL, + COMPARE_ALWAYS, + COMPARE_MAX_ENUM +}; + +/** + * GPU APIs do the comparison in the opposite way of what makes sense for some + * of love's APIs. For example in OpenGL if the compare function is GL_GREATER, + * then the stencil test will pass if the reference value is greater than the + * value in the stencil buffer. With our stencil API it's more intuitive to + * assume that setStencilTest(COMPARE_GREATER, 4) will make it pass if the + * stencil buffer has a value greater than 4. + **/ +CompareMode getReversedCompareMode(CompareMode mode); + +bool getConstant(const char *in, StencilAction &out); +bool getConstant(StencilAction in, const char *&out); + +bool getConstant(const char *in, CompareMode &out); +bool getConstant(CompareMode in, const char *&out); + +} // graphics +} // love diff --git a/src/modules/graphics/opengl/Canvas.cpp b/src/modules/graphics/opengl/Canvas.cpp index 5249dd193..96c0eda37 100644 --- a/src/modules/graphics/opengl/Canvas.cpp +++ b/src/modules/graphics/opengl/Canvas.cpp @@ -277,6 +277,7 @@ bool Canvas::loadVolatile() setFilter(filter); setWrap(wrap); + setDepthSampleMode(depthCompareMode); while (glGetError() != GL_NO_ERROR) /* Clear the error buffer. */; @@ -358,6 +359,8 @@ void Canvas::setFilter(const Texture::Filter &f) if (!validateFilter(f, false)) throw love::Exception("Invalid texture filter."); + Graphics::flushStreamDrawsGlobal(); + filter = f; gl.bindTextureToUnit(this, 0, false); gl.setTextureFilter(texType, filter); @@ -365,6 +368,8 @@ void Canvas::setFilter(const Texture::Filter &f) bool Canvas::setWrap(const Texture::Wrap &w) { + Graphics::flushStreamDrawsGlobal(); + bool success = true; bool forceclamp = texType == TEXTURE_CUBE; wrap = w; @@ -402,6 +407,42 @@ bool Canvas::setMipmapSharpness(float /*sharpness*/) return false; } +void Canvas::setDepthSampleMode(Optional mode) +{ + Texture::setDepthSampleMode(mode); + + bool supported = gl.isDepthCompareSampleSupported(); + + if (mode.hasValue && !supported) + throw love::Exception("Depth comparison sampling in shaders is not supported on this system."); + + if (mode.hasValue) + { + Graphics::flushStreamDrawsGlobal(); + + gl.bindTextureToUnit(texType, texture, 0, false); + GLenum gltextype = OpenGL::getGLTextureType(texType); + + // See the comment in depthstencil.h + GLenum glmode = OpenGL::getGLCompareMode(getReversedCompareMode(mode.value)); + + glTexParameteri(gltextype, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); + glTexParameteri(gltextype, GL_TEXTURE_COMPARE_FUNC, glmode); + + } + else if (isPixelFormatDepth(format) && supported) + { + Graphics::flushStreamDrawsGlobal(); + + gl.bindTextureToUnit(texType, texture, 0, false); + GLenum gltextype = OpenGL::getGLTextureType(texType); + + glTexParameteri(gltextype, GL_TEXTURE_COMPARE_MODE, GL_NONE); + } + + depthCompareMode = mode; +} + ptrdiff_t Canvas::getHandle() const { return texture; diff --git a/src/modules/graphics/opengl/Canvas.h b/src/modules/graphics/opengl/Canvas.h index 3faea6230..bc97abe08 100644 --- a/src/modules/graphics/opengl/Canvas.h +++ b/src/modules/graphics/opengl/Canvas.h @@ -50,6 +50,7 @@ public: void setFilter(const Texture::Filter &f) override; bool setWrap(const Texture::Wrap &w) override; bool setMipmapSharpness(float sharpness) override; + void setDepthSampleMode(Optional mode) override; ptrdiff_t getHandle() const override; love::image::ImageData *newImageData(love::image::Image *module, int slice, int x, int y, int w, int h) override; diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index 655a35f70..fcf4a13fc 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/src/modules/graphics/opengl/Graphics.cpp @@ -354,12 +354,6 @@ void Graphics::flushStreamDraws() prevdefaultshader = Shader::current; Shader::standardShaders[Shader::STANDARD_ARRAY]->attach(); } - - if (!sbstate.texture->isReadable()) - throw love::Exception("Textures with non-readable formats cannot be drawn."); - - if (Shader::current) - Shader::current->checkMainTextureType(textype); } OpenGL::TempDebugGroup debuggroup("Stream vertices flush and draw"); @@ -1236,45 +1230,15 @@ void Graphics::setStencilTest(CompareMode compare, int value) return; } - GLenum glcompare = GL_EQUAL; - /** - * Q: Why are some of the compare modes inverted (e.g. COMPARE_LESS becomes - * GL_GREATER)? - * - * A: OpenGL / GPUs do the comparison in the opposite way that makes sense + * OpenGL / GPUs do the comparison in the opposite way that makes sense * for this API. For example, if the compare function is GL_GREATER then the * stencil test will pass if the reference value is greater than the value * in the stencil buffer. With our API it's more intuitive to assume that * setStencilTest(COMPARE_GREATER, 4) will make it pass if the stencil * buffer has a value greater than 4. **/ - - switch (compare) - { - case COMPARE_LESS: - glcompare = GL_GREATER; - break; - case COMPARE_LEQUAL: - glcompare = GL_GEQUAL; - break; - case COMPARE_EQUAL: - default: - glcompare = GL_EQUAL; - break; - case COMPARE_GEQUAL: - glcompare = GL_LEQUAL; - break; - case COMPARE_GREATER: - glcompare = GL_LESS; - break; - case COMPARE_NOTEQUAL: - glcompare = GL_NOTEQUAL; - break; - case COMPARE_ALWAYS: - glcompare = GL_ALWAYS; - break; - } + GLenum glcompare = OpenGL::getGLCompareMode(getReversedCompareMode(compare)); glEnable(GL_STENCIL_TEST); glStencilFunc(glcompare, value, 0xFFFFFFFF); diff --git a/src/modules/graphics/opengl/Image.cpp b/src/modules/graphics/opengl/Image.cpp index 1b1022aec..1ba7191e4 100644 --- a/src/modules/graphics/opengl/Image.cpp +++ b/src/modules/graphics/opengl/Image.cpp @@ -380,6 +380,8 @@ void Image::replacePixels(love::image::ImageDataBase *d, int slice, int mipmap, if (w != oldd->getWidth() || h != oldd->getHeight()) throw love::Exception("Dimensions must match the texture's dimensions for the specified mipmap level."); + Graphics::flushStreamDrawsGlobal(); + d->retain(); oldd->release(); @@ -397,6 +399,8 @@ void Image::replacePixels(love::image::ImageDataBase *d, int slice, int mipmap, void Image::replacePixels(const void *data, size_t size, const Rect &rect, int slice, int mipmap, bool reloadmipmaps) { + Graphics::flushStreamDrawsGlobal(); + OpenGL::TempDebugGroup debuggroup("Image replace pixels"); gl.bindTextureToUnit(this, 0, false); @@ -422,6 +426,8 @@ void Image::setFilter(const Texture::Filter &f) throw love::Exception("Invalid texture filter."); } + Graphics::flushStreamDrawsGlobal(); + filter = f; if (!OpenGL::hasTextureFilteringSupport(getPixelFormat())) @@ -445,6 +451,8 @@ void Image::setFilter(const Texture::Filter &f) bool Image::setWrap(const Texture::Wrap &w) { + Graphics::flushStreamDrawsGlobal(); + bool success = true; bool forceclamp = texType == TEXTURE_CUBE; wrap = w; @@ -483,6 +491,8 @@ bool Image::setMipmapSharpness(float sharpness) if (!GLAD_VERSION_1_4) return false; + Graphics::flushStreamDrawsGlobal(); + // LOD bias has the range (-maxbias, maxbias) mipmapSharpness = std::min(std::max(sharpness, -maxMipmapSharpness + 0.01f), maxMipmapSharpness - 0.01f); diff --git a/src/modules/graphics/opengl/OpenGL.cpp b/src/modules/graphics/opengl/OpenGL.cpp index 3ae813a53..3b82e142c 100644 --- a/src/modules/graphics/opengl/OpenGL.cpp +++ b/src/modules/graphics/opengl/OpenGL.cpp @@ -987,6 +987,29 @@ GLint OpenGL::getGLWrapMode(Texture::WrapMode wmode) } +GLint OpenGL::getGLCompareMode(CompareMode mode) +{ + switch (mode) + { + case COMPARE_LESS: + return GL_LESS; + case COMPARE_LEQUAL: + return GL_LEQUAL; + case COMPARE_EQUAL: + return GL_EQUAL; + case COMPARE_GEQUAL: + return GL_GEQUAL; + case COMPARE_GREATER: + return GL_GREATER; + case COMPARE_NOTEQUAL: + return GL_NOTEQUAL; + case COMPARE_ALWAYS: + return GL_ALWAYS; + default: + return GL_NEVER; + } +} + void OpenGL::setTextureWrap(TextureType target, const graphics::Texture::Wrap &w) { glTexParameteri(getGLTextureType(target), GL_TEXTURE_WRAP_S, getGLWrapMode(w.s)); @@ -1108,6 +1131,13 @@ bool OpenGL::isInstancingSupported() const || GLAD_ARB_instanced_arrays || GLAD_EXT_instanced_arrays || GLAD_ANGLE_instanced_arrays; } +bool OpenGL::isDepthCompareSampleSupported() const +{ + // Our official API only supports this in GLSL3 shaders, but unofficially + // the requirements are more lax. + return GLAD_VERSION_2_0 || GLAD_ES_VERSION_3_0 || GLAD_EXT_shadow_samplers; +} + int OpenGL::getMax2DTextureSize() const { return std::max(max2DTextureSize, 1); diff --git a/src/modules/graphics/opengl/OpenGL.h b/src/modules/graphics/opengl/OpenGL.h index 5a8dbc189..661620304 100644 --- a/src/modules/graphics/opengl/OpenGL.h +++ b/src/modules/graphics/opengl/OpenGL.h @@ -28,6 +28,7 @@ #include "graphics/Color.h" #include "graphics/Texture.h" #include "graphics/vertex.h" +#include "graphics/depthstencil.h" #include "common/Matrix.h" // GLAD @@ -326,6 +327,7 @@ public: bool isClampZeroTextureWrapSupported() const; bool isPixelShaderHighpSupported() const; bool isInstancingSupported() const; + bool isDepthCompareSampleSupported() const; /** * Returns the maximum supported width or height of a texture. @@ -379,6 +381,7 @@ public: static GLenum getGLBufferUsage(vertex::Usage usage); static GLenum getGLTextureType(TextureType type); static GLint getGLWrapMode(Texture::WrapMode wmode); + static GLint getGLCompareMode(CompareMode mode); static TextureFormat convertPixelFormat(PixelFormat pixelformat, bool renderbuffer, bool &isSRGB); static bool isTexStorageSupported(); diff --git a/src/modules/graphics/opengl/Shader.cpp b/src/modules/graphics/opengl/Shader.cpp index 3cb094faf..3c946d91d 100644 --- a/src/modules/graphics/opengl/Shader.cpp +++ b/src/modules/graphics/opengl/Shader.cpp @@ -173,6 +173,7 @@ void Shader::mapActiveUniforms() u.location = glGetUniformLocation(program, u.name.c_str()); u.baseType = getUniformBaseType(gltype); u.textureType = getUniformTextureType(gltype); + u.isDepthSampler = isDepthTextureType(gltype); if (u.baseType == UNIFORM_MATRIX) u.matrix = getMatrixSize(gltype); @@ -675,22 +676,51 @@ void Shader::sendTextures(const UniformInfo *info, Texture **textures, int count // Bind the textures to the texture units. for (int i = 0; i < count; i++) { - if (textures[i] != nullptr) - { - if (textures[i]->getTextureType() != info->textureType || !textures[i]->isReadable()) - continue; + Texture *tex = textures[i]; - textures[i]->retain(); + if (tex != nullptr) + { + if (!tex->isReadable()) + { + if (internalUpdate) + continue; + else + throw love::Exception("Textures with non-readable formats cannot be sampled from in a shader."); + } + else if (info->isDepthSampler != tex->getDepthSampleMode().hasValue) + { + if (internalUpdate) + continue; + else if (info->isDepthSampler) + throw love::Exception("Depth comparison samplers in shaders can only be used with depth textures which have depth comparison set."); + else + throw love::Exception("Depth textures which have depth comparison set can only be used with depth/shadow samplers in shaders."); + } + else if (tex->getTextureType() != info->textureType) + { + if (internalUpdate) + continue; + else + { + const char *textypestr = "unknown"; + const char *shadertextypestr = "unknown"; + Texture::getConstant(tex->getTextureType(), textypestr); + Texture::getConstant(info->textureType, shadertextypestr); + throw love::Exception("Texture's type (%s) must match the type of %s (%s).", textypestr, info->name.c_str(), shadertextypestr); + } + } + + tex->retain(); } if (info->textures[i] != nullptr) info->textures[i]->release(); - info->textures[i] = textures[i]; + info->textures[i] = tex; GLuint gltex = 0; if (textures[i] != nullptr) - gltex = (GLuint) textures[i]->getHandle(); + gltex = (GLuint) tex->getHandle(); else gltex = gl.getDefaultTexture(info->textureType); @@ -707,11 +737,7 @@ void Shader::sendTextures(const UniformInfo *info, Texture **textures, int count void Shader::flushStreamDraws() const { if (current == this) - { - auto gfx = Module::getInstance(Module::M_GRAPHICS); - if (gfx != nullptr) - gfx->flushStreamDraws(); - } + Graphics::flushStreamDrawsGlobal(); } bool Shader::hasUniform(const std::string &name) const @@ -1035,7 +1061,7 @@ TextureType Shader::getUniformTextureType(GLenum type) const // 1D-typed textures are not supported. return TEXTURE_MAX_ENUM; case GL_SAMPLER_2D: - //case GL_SAMPLER_2D_SHADOW: + case GL_SAMPLER_2D_SHADOW: return TEXTURE_2D; case GL_SAMPLER_2D_MULTISAMPLE: case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: @@ -1046,12 +1072,12 @@ TextureType Shader::getUniformTextureType(GLenum type) const // Rectangle textures are not supported. return TEXTURE_MAX_ENUM; case GL_SAMPLER_2D_ARRAY: - //case GL_SAMPLER_2D_ARRAY_SHADOW: + case GL_SAMPLER_2D_ARRAY_SHADOW: return TEXTURE_2D_ARRAY; case GL_SAMPLER_3D: return TEXTURE_VOLUME; case GL_SAMPLER_CUBE: - //case GL_SAMPLER_CUBE_SHADOW: + case GL_SAMPLER_CUBE_SHADOW: return TEXTURE_CUBE; case GL_SAMPLER_CUBE_MAP_ARRAY: case GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW: @@ -1062,6 +1088,22 @@ TextureType Shader::getUniformTextureType(GLenum type) const } } +bool Shader::isDepthTextureType(GLenum type) const +{ + switch (type) + { + case GL_SAMPLER_1D_SHADOW: + case GL_SAMPLER_1D_ARRAY_SHADOW: + case GL_SAMPLER_2D_SHADOW: + case GL_SAMPLER_2D_ARRAY_SHADOW: + case GL_SAMPLER_CUBE_SHADOW: + case GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW: + return true; + default: + return false; + } +} + } // opengl } // graphics } // love diff --git a/src/modules/graphics/opengl/Shader.h b/src/modules/graphics/opengl/Shader.h index ac554ba73..b2ac4950d 100644 --- a/src/modules/graphics/opengl/Shader.h +++ b/src/modules/graphics/opengl/Shader.h @@ -94,6 +94,7 @@ private: MatrixSize getMatrixSize(GLenum type) const; UniformType getUniformBaseType(GLenum type) const; TextureType getUniformTextureType(GLenum type) const; + bool isDepthTextureType(GLenum type) const; GLuint compileCode(ShaderStage stage, const std::string &code); diff --git a/src/modules/graphics/opengl/Text.cpp b/src/modules/graphics/opengl/Text.cpp index d9995498b..17a7e07bd 100644 --- a/src/modules/graphics/opengl/Text.cpp +++ b/src/modules/graphics/opengl/Text.cpp @@ -46,7 +46,7 @@ void Text::draw(Graphics *gfx, const Matrix4 &m) return; if (Shader::current) - Shader::current->checkMainTextureType(TEXTURE_2D); + Shader::current->checkMainTextureType(TEXTURE_2D, false); gfx->flushStreamDraws(); diff --git a/src/modules/graphics/wrap_Graphics.cpp b/src/modules/graphics/wrap_Graphics.cpp index 20877bd3b..36e54305e 100644 --- a/src/modules/graphics/wrap_Graphics.cpp +++ b/src/modules/graphics/wrap_Graphics.cpp @@ -489,12 +489,12 @@ int w_stencil(lua_State *L) { luaL_checktype(L, 1, LUA_TFUNCTION); - Graphics::StencilAction action = Graphics::STENCIL_REPLACE; + StencilAction action = STENCIL_REPLACE; if (!lua_isnoneornil(L, 2)) { const char *actionstr = luaL_checkstring(L, 2); - if (!Graphics::getConstant(actionstr, action)) + if (!getConstant(actionstr, action)) return luaL_error(L, "Invalid stencil draw action: %s", actionstr); } @@ -522,13 +522,13 @@ int w_stencil(lua_State *L) int w_setStencilTest(lua_State *L) { // COMPARE_ALWAYS effectively disables stencil testing. - Graphics::CompareMode compare = Graphics::COMPARE_ALWAYS; + CompareMode compare = COMPARE_ALWAYS; int comparevalue = 0; if (!lua_isnoneornil(L, 1)) { const char *comparestr = luaL_checkstring(L, 1); - if (!Graphics::getConstant(comparestr, compare)) + if (!getConstant(comparestr, compare)) return luaL_error(L, "Invalid compare mode: %s", comparestr); comparevalue = (int) luaL_checknumber(L, 2); @@ -540,13 +540,13 @@ int w_setStencilTest(lua_State *L) int w_getStencilTest(lua_State *L) { - Graphics::CompareMode compare = Graphics::COMPARE_ALWAYS; + CompareMode compare = COMPARE_ALWAYS; int comparevalue = 1; instance()->getStencilTest(compare, comparevalue); const char *comparestr; - if (!Graphics::getConstant(compare, comparestr)) + if (!getConstant(compare, comparestr)) return luaL_error(L, "Unknown compare mode."); lua_pushstring(L, comparestr); diff --git a/src/modules/graphics/wrap_Graphics.lua b/src/modules/graphics/wrap_Graphics.lua index cae05a576..bb9f3c64f 100644 --- a/src/modules/graphics/wrap_Graphics.lua +++ b/src/modules/graphics/wrap_Graphics.lua @@ -50,6 +50,11 @@ GLSL.SYNTAX = [[ #define ArrayImage sampler2DArray #define CubeImage samplerCube #define VolumeImage sampler3D +#if __VERSION__ >= 300 && !defined(LOVE_GLSL1_ON_GLSL3) + #define DepthImage sampler2DShadow + #define DepthArrayImage sampler2DArrayShadow + #define DepthCubeImage samplerCubeShadow +#endif #define extern uniform #ifdef GL_EXT_texture_array #extension GL_EXT_texture_array : enable @@ -78,6 +83,11 @@ GLSL.FUNCTIONS = [[ #if __VERSION__ >= 300 || defined(GL_OES_texture_3D) precision lowp sampler3D; #endif + #if __VERSION__ >= 300 + precision lowp sampler2DShadow; + precision lowp samplerCubeShadow; + precision lowp sampler2DArrayShadow; + #endif #endif #if __VERSION__ >= 130 && !defined(LOVE_GLSL1_ON_GLSL3) diff --git a/src/modules/graphics/wrap_Texture.cpp b/src/modules/graphics/wrap_Texture.cpp index 4c9cf26b7..ae2761323 100644 --- a/src/modules/graphics/wrap_Texture.cpp +++ b/src/modules/graphics/wrap_Texture.cpp @@ -244,6 +244,42 @@ int w_Texture_isReadable(lua_State *L) return 1; } +int w_Texture_setDepthSampleMode(lua_State *L) +{ + Texture *t = luax_checktexture(L, 1); + + Optional mode; + if (!lua_isnoneornil(L, 2)) + { + const char *str = luaL_checkstring(L, 2); + + mode.hasValue = true; + if (!getConstant(str, mode.value)) + return luaL_error(L, "Invalid compare mode: %s", str); + } + + luax_catchexcept(L, [&]() { t->setDepthSampleMode(mode); }); + return 0; +} + +int w_Texture_getDepthSampleMode(lua_State *L) +{ + Texture *t = luax_checktexture(L, 1); + Optional mode = t->getDepthSampleMode(); + + if (mode.hasValue) + { + const char *str = nullptr; + if (!getConstant(mode.value, str)) + return luaL_error(L, "Unknown compare mode."); + lua_pushstring(L, str); + } + else + lua_pushnil(L); + + return 1; +} + const luaL_Reg w_Texture_functions[] = { { "getTextureType", w_Texture_getTextureType }, @@ -265,6 +301,8 @@ const luaL_Reg w_Texture_functions[] = { "getWrap", w_Texture_getWrap }, { "getFormat", w_Texture_getFormat }, { "isReadable", w_Texture_isReadable }, + { "getDepthSampleMode", w_Texture_getDepthSampleMode }, + { "setDepthSampleMode", w_Texture_setDepthSampleMode }, { 0, 0 } };