diff --git a/src/modules/graphics/Graphics.cpp b/src/modules/graphics/Graphics.cpp index 97d8773f8..7c06e156e 100644 --- a/src/modules/graphics/Graphics.cpp +++ b/src/modules/graphics/Graphics.cpp @@ -847,6 +847,7 @@ void Graphics::restoreState(const DisplayState &s) setStencilState(s.stencil); setDepthMode(s.depthTest, s.depthWrite); + setDepthClamp(s.depthClampEnable); setColorMask(s.colorMask); setWireframe(s.wireframe); @@ -926,6 +927,9 @@ void Graphics::restoreStateChecked(const DisplayState &s) if (s.depthTest != cur.depthTest || s.depthWrite != cur.depthWrite) setDepthMode(s.depthTest, s.depthWrite); + if(s.depthClampEnable != cur.depthClampEnable) + setDepthClamp(s.depthClampEnable); + if (s.colorMask != cur.colorMask) setColorMask(s.colorMask); diff --git a/src/modules/graphics/Graphics.h b/src/modules/graphics/Graphics.h index 73be91f1e..c8c272ace 100644 --- a/src/modules/graphics/Graphics.h +++ b/src/modules/graphics/Graphics.h @@ -163,6 +163,7 @@ class Graphics : public Module FEATURE_TEXEL_BUFFER, FEATURE_COPY_TEXTURE_TO_BUFFER, FEATURE_INDIRECT_DRAW, + FEATURE_DEPTH_CLAMP, FEATURE_MAX_ENUM }; @@ -621,6 +622,12 @@ class Graphics : public Module void setDepthMode(); void getDepthMode(CompareMode &compare, bool &write) const; + /** + * Clamps the depth valies to [0, 1] range instead of clipping geomentry + * away at near and far planes + */ + virtual void setDepthClamp(bool enable) = 0; + void setMeshCullMode(CullMode cull); CullMode getMeshCullMode() const; @@ -948,6 +955,7 @@ class Graphics : public Module CompareMode depthTest = COMPARE_ALWAYS; bool depthWrite = false; + bool depthClampEnable = false; CullMode meshCullMode = CULL_NONE; Winding winding = WINDING_CCW; diff --git a/src/modules/graphics/metal/Graphics.h b/src/modules/graphics/metal/Graphics.h index 4652f6628..ba69df536 100644 --- a/src/modules/graphics/metal/Graphics.h +++ b/src/modules/graphics/metal/Graphics.h @@ -96,6 +96,8 @@ class Graphics final : public love::graphics::Graphics void setDepthMode(CompareMode compare, bool write) override; + void setDepthClamp(bool enable) override; + void setFrontFaceWinding(Winding winding) override; void setColorMask(ColorChannelMask mask) override; diff --git a/src/modules/graphics/metal/Graphics.mm b/src/modules/graphics/metal/Graphics.mm index ce267668c..41fac38b8 100644 --- a/src/modules/graphics/metal/Graphics.mm +++ b/src/modules/graphics/metal/Graphics.mm @@ -1036,6 +1036,9 @@ static bool isClampOne(SamplerState::WrapMode w) id mtlstate = getCachedDepthStencilState(depth, state.stencil); [encoder setDepthStencilState:mtlstate]; + + // Also set the depth clamping (depth state change is tirggered for both deptstate and clamp state changes) + [encoder setDepthClipMode: state.depthClampEnable ? MTLDepthClipModeClamp : MTLDepthClipModeClip]; } if (dirtyState & STATEBIT_STENCIL) @@ -1842,6 +1845,18 @@ static inline void advanceVertexOffsets(const VertexAttributes &attributes, Buff } } +void Graphics::setDepthClamp(bool enable) +{ + DisplayState &state = states.back(); + + if(state.depthClampEnable != enable) + { + flushBatchedDraws(); + state.depthClampEnable = enable; + dirtyRenderState |= STATEBIT_DEPTH; + } +} + void Graphics::setFrontFaceWinding(Winding winding) { if (states.back().winding != winding) @@ -2266,7 +2281,9 @@ static inline void advanceVertexOffsets(const VertexAttributes &attributes, Buff else capabilities.features[FEATURE_INDIRECT_DRAW] = false; - static_assert(FEATURE_MAX_ENUM == 13, "Graphics::initCapabilities must be updated when adding a new graphics feature!"); + capabilities.features[FEATURE_DEPTH_CLAMP] = true; + + static_assert(FEATURE_MAX_ENUM == 14, "Graphics::initCapabilities must be updated when adding a new graphics feature!"); // https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf capabilities.limits[LIMIT_POINT_SIZE] = 511; diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index 24378c29d..cc2f4831b 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/src/modules/graphics/opengl/Graphics.cpp @@ -1453,6 +1453,19 @@ void Graphics::setDepthMode(CompareMode compare, bool write) } } +void Graphics::setDepthClamp(bool enable) +{ + if(capabilities.features[FEATURE_DEPTH_CLAMP]) { + DisplayState &state = states.back(); + + if(state.depthClampEnable != enable) + flushBatchedDraws(); + + gl.setEnableState(OpenGL::ENABLE_DEPTH_CLAMP, enable); + state.depthClampEnable = enable; + } +} + void Graphics::setFrontFaceWinding(Winding winding) { DisplayState &state = states.back(); @@ -1609,7 +1622,12 @@ void Graphics::initCapabilities() capabilities.features[FEATURE_TEXEL_BUFFER] = gl.isBufferUsageSupported(BUFFERUSAGE_TEXEL); capabilities.features[FEATURE_COPY_TEXTURE_TO_BUFFER] = gl.isCopyTextureToBufferSupported(); capabilities.features[FEATURE_INDIRECT_DRAW] = capabilities.features[FEATURE_GLSL4]; - static_assert(FEATURE_MAX_ENUM == 13, "Graphics::initCapabilities must be updated when adding a new graphics feature!"); + capabilities.features[FEATURE_DEPTH_CLAMP] = hasExtension("GL_EXT_depth_clamp") || + hasExtension("GL_NV_depth_clamp") || + hasExtension("GL_AMD_depth_clamp_separate") || + hasExtension("GL_ARB_depth_clamp") || + GLAD_VERSION_3_2; + static_assert(FEATURE_MAX_ENUM == 14, "Graphics::initCapabilities must be updated when adding a new graphics feature!"); capabilities.limits[LIMIT_POINT_SIZE] = gl.getMaxPointSize(); capabilities.limits[LIMIT_TEXTURE_SIZE] = gl.getMax2DTextureSize(); @@ -1738,6 +1756,23 @@ bool Graphics::isPixelFormatSupported(PixelFormat format, uint32 usage) return (usage & pixelFormatUsage[format][readable ? 1 : 0]) == usage; } +bool Graphics::hasExtension(const char* name) +{ + GLint numExts = 0; + glGetIntegerv(GL_NUM_EXTENSIONS, &numExts); + for (GLint i = 0; i < numExts; ++i) { + const char* ext = (const char*)glGetStringi(GL_EXTENSIONS, i); + ::printf("extension: %s \n", ext); + if (strcmp(ext, name) == 0) return true; + } + // fallback for older contexts + const char* all = (const char*)glGetString(GL_EXTENSIONS); + if (all) { + return strstr(all, name) != nullptr; + } + return false; +} + } // opengl } // graphics } // love diff --git a/src/modules/graphics/opengl/Graphics.h b/src/modules/graphics/opengl/Graphics.h index aea7a85ca..5a0278a44 100644 --- a/src/modules/graphics/opengl/Graphics.h +++ b/src/modules/graphics/opengl/Graphics.h @@ -92,6 +92,8 @@ class Graphics final : public love::graphics::Graphics void setDepthMode(CompareMode compare, bool write) override; + void setDepthClamp(bool enable) override; + void setFrontFaceWinding(Winding winding) override; void setColorMask(ColorChannelMask mask) override; @@ -144,6 +146,8 @@ class Graphics final : public love::graphics::Graphics void setRenderTargetsInternal(const RenderTargets &rts, int pixelw, int pixelh, bool hasSRGBtexture) override; void initCapabilities() override; void getAPIStats(int &shaderswitches) const override; + + bool hasExtension(const char* name); void endPass(bool presenting); GLuint bindCachedFBO(const RenderTargets &targets); diff --git a/src/modules/graphics/opengl/OpenGL.cpp b/src/modules/graphics/opengl/OpenGL.cpp index 9bac11128..bca9e3f20 100644 --- a/src/modules/graphics/opengl/OpenGL.cpp +++ b/src/modules/graphics/opengl/OpenGL.cpp @@ -830,6 +830,9 @@ void OpenGL::setEnableState(EnableState enablestate, bool enable) case ENABLE_FRAMEBUFFER_SRGB: glstate = GL_FRAMEBUFFER_SRGB; break; + case ENABLE_DEPTH_CLAMP: + glstate = GL_DEPTH_CLAMP; + break; case ENABLE_MAX_ENUM: break; } diff --git a/src/modules/graphics/opengl/OpenGL.h b/src/modules/graphics/opengl/OpenGL.h index 2ba17e351..227cb00db 100644 --- a/src/modules/graphics/opengl/OpenGL.h +++ b/src/modules/graphics/opengl/OpenGL.h @@ -100,6 +100,7 @@ class OpenGL ENABLE_SCISSOR_TEST, ENABLE_FACE_CULL, ENABLE_FRAMEBUFFER_SRGB, + ENABLE_DEPTH_CLAMP, ENABLE_MAX_ENUM }; diff --git a/src/modules/graphics/vulkan/Graphics.cpp b/src/modules/graphics/vulkan/Graphics.cpp index 7d4c926c7..620f3d585 100644 --- a/src/modules/graphics/vulkan/Graphics.cpp +++ b/src/modules/graphics/vulkan/Graphics.cpp @@ -1104,6 +1104,18 @@ void Graphics::setDepthMode(CompareMode compare, bool write) states.back().depthWrite = write; } +void Graphics::setDepthClamp(bool enable) +{ + flushBatchedDraws(); + + if (optionalDeviceExtensions.extendedDynamicState3) + { + vkCmdSetDepthClampEnableEXT(commandBuffers.at(currentFrame), Vulkan::getBool(enable)); + } + + states.back().depthClampEnable = enable; +} + void Graphics::setWireframe(bool enable) { flushBatchedDraws(); @@ -1357,6 +1369,11 @@ void Graphics::initDynamicState() vkCmdSetFrontFaceEXT( commandBuffers.at(currentFrame), Vulkan::getFrontFace(states.back().winding)); } + + if (optionalDeviceExtensions.extendedDynamicState3) + { + vkCmdSetDepthClampEnableEXT(commandBuffers.at(currentFrame), Vulkan::getBool(states.back().depthClampEnable)); + } } void Graphics::beginSwapChainFrame() @@ -1782,6 +1799,8 @@ static void findOptionalDeviceExtensions(VkPhysicalDevice physicalDevice, Option { if (strcmp(extension.extensionName, VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME) == 0) optionalDeviceExtensions.extendedDynamicState = true; + if (strcmp(extension.extensionName, VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME) == 0) + optionalDeviceExtensions.extendedDynamicState3 = true; if (strcmp(extension.extensionName, VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME) == 0) optionalDeviceExtensions.memoryRequirements2 = true; if (strcmp(extension.extensionName, VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME) == 0) @@ -1844,6 +1863,8 @@ void Graphics::createLogicalDevice() std::vector enabledExtensions(deviceExtensions.begin(), deviceExtensions.end()); if (optionalDeviceExtensions.extendedDynamicState) enabledExtensions.push_back(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); + if (optionalDeviceExtensions.extendedDynamicState3) + enabledExtensions.push_back(VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME); if (optionalDeviceExtensions.memoryRequirements2) enabledExtensions.push_back(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME); if (optionalDeviceExtensions.dedicatedAllocation) @@ -2609,13 +2630,23 @@ void Graphics::prepareDraw(VertexAttributesID attributesID, const BufferBindings if (optionalDeviceExtensions.extendedDynamicState) { vkCmdSetCullModeEXT(commandBuffers.at(currentFrame), Vulkan::getCullMode(cullmode)); - pipeline = s->getCachedGraphicsPipeline(this, configuration.core); + if (!optionalDeviceExtensions.extendedDynamicState3) + { + // If extended dynamic state 3 is not supported, we need to set the depth clamp enable manually via no dynamic state. + // But the depth clamp enable is not part of the core dynamic state, so we need to use noDynamicState only for depth clamp enable. + // Rest of the dynamic state is still set via extended dynamic state. + configuration.noDynamicState.depthClampEnable = states.back().depthClampEnable; + pipeline = s->getCachedGraphicsPipeline(this, configuration); + } + else + pipeline = s->getCachedGraphicsPipeline(this, configuration.core); } else { configuration.noDynamicState.winding = states.back().winding; configuration.noDynamicState.depthState.compare = states.back().depthTest; configuration.noDynamicState.depthState.write = states.back().depthWrite; + configuration.noDynamicState.depthClampEnable = states.back().depthClampEnable; configuration.noDynamicState.stencilAction = states.back().stencil.action; configuration.noDynamicState.stencilCompare = states.back().stencil.compare; configuration.noDynamicState.cullmode = cullmode; @@ -3012,7 +3043,6 @@ VkPipeline Graphics::createGraphicsPipeline(Shader *shader, const GraphicsPipeli VkPipelineRasterizationStateCreateInfo rasterizer{}; rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; - rasterizer.depthClampEnable = VK_FALSE; rasterizer.rasterizerDiscardEnable = VK_FALSE; rasterizer.polygonMode = Vulkan::getPolygonMode(configuration.wireFrame); rasterizer.lineWidth = 1.0f; @@ -3022,6 +3052,9 @@ VkPipeline Graphics::createGraphicsPipeline(Shader *shader, const GraphicsPipeli rasterizer.frontFace = Vulkan::getFrontFace(noDynamicStateConfiguration->winding); } + if (!optionalDeviceExtensions.extendedDynamicState3) + rasterizer.depthClampEnable = Vulkan::getBool(noDynamicStateConfiguration->depthClampEnable); + rasterizer.depthBiasEnable = VK_FALSE; rasterizer.depthBiasConstantFactor = 0.0f; rasterizer.depthBiasClamp = 0.0f; @@ -3098,7 +3131,7 @@ VkPipeline Graphics::createGraphicsPipeline(Shader *shader, const GraphicsPipeli std::vector dynamicStates; - if (optionalDeviceExtensions.extendedDynamicState) + if (optionalDeviceExtensions.extendedDynamicState3) dynamicStates = { VK_DYNAMIC_STATE_SCISSOR, VK_DYNAMIC_STATE_VIEWPORT, @@ -3111,7 +3144,22 @@ VkPipeline Graphics::createGraphicsPipeline(Shader *shader, const GraphicsPipeli VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE_EXT, VK_DYNAMIC_STATE_DEPTH_COMPARE_OP_EXT, VK_DYNAMIC_STATE_STENCIL_OP_EXT, - }; + VK_DYNAMIC_STATE_DEPTH_CLAMP_ENABLE_EXT, + }; + else if (optionalDeviceExtensions.extendedDynamicState) + dynamicStates = { + VK_DYNAMIC_STATE_SCISSOR, + VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, + VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK, + VK_DYNAMIC_STATE_STENCIL_REFERENCE, + + VK_DYNAMIC_STATE_CULL_MODE_EXT, + VK_DYNAMIC_STATE_FRONT_FACE_EXT, + VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE_EXT, + VK_DYNAMIC_STATE_DEPTH_COMPARE_OP_EXT, + VK_DYNAMIC_STATE_STENCIL_OP_EXT, + }; else dynamicStates = { VK_DYNAMIC_STATE_SCISSOR, @@ -3119,7 +3167,7 @@ VkPipeline Graphics::createGraphicsPipeline(Shader *shader, const GraphicsPipeli VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE, - }; + }; VkPipelineDynamicStateCreateInfo dynamicState{}; dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; diff --git a/src/modules/graphics/vulkan/Graphics.h b/src/modules/graphics/vulkan/Graphics.h index 43169cad9..717337c0d 100644 --- a/src/modules/graphics/vulkan/Graphics.h +++ b/src/modules/graphics/vulkan/Graphics.h @@ -161,6 +161,9 @@ struct OptionalDeviceExtensions // VK_EXT_extended_dynamic_state bool extendedDynamicState = false; + // VK_EXT_extended_dynamic_state3 + bool extendedDynamicState3 = false; + // VK_KHR_get_memory_requirements2 bool memoryRequirements2 = false; @@ -251,6 +254,7 @@ class Graphics final : public love::graphics::Graphics void setScissor() override; void setStencilState(const StencilState &s) override; void setDepthMode(CompareMode compare, bool write) override; + void setDepthClamp(bool enable) override; void setFrontFaceWinding(Winding winding) override; void setColorMask(ColorChannelMask mask) override; void setBlendState(const BlendState &blend) override; diff --git a/src/modules/graphics/vulkan/Shader.h b/src/modules/graphics/vulkan/Shader.h index 547825769..ba58fdd76 100644 --- a/src/modules/graphics/vulkan/Shader.h +++ b/src/modules/graphics/vulkan/Shader.h @@ -84,6 +84,7 @@ struct GraphicsPipelineConfigurationNoDynamicState StencilAction stencilAction = STENCIL_MAX_ENUM; CompareMode stencilCompare = COMPARE_MAX_ENUM; DepthState depthState{}; + bool depthClampEnable = false; }; struct GraphicsPipelineConfigurationFull diff --git a/src/modules/graphics/wrap_Graphics.cpp b/src/modules/graphics/wrap_Graphics.cpp index 825e66c55..79b5ecee2 100644 --- a/src/modules/graphics/wrap_Graphics.cpp +++ b/src/modules/graphics/wrap_Graphics.cpp @@ -2891,6 +2891,12 @@ int w_getDepthMode(lua_State *L) return 2; } +int w_setDepthClamp(lua_State *L) +{ + instance()->setDepthClamp(luax_checkboolean(L, 1)); + return 0; +} + int w_setMeshCullMode(lua_State *L) { const char *str = luaL_checkstring(L, 1); @@ -4081,6 +4087,7 @@ static const luaL_Reg functions[] = { "getPointSize", w_getPointSize }, { "setDepthMode", w_setDepthMode }, { "getDepthMode", w_getDepthMode }, + { "setDepthClamp", w_setDepthClamp }, { "setMeshCullMode", w_setMeshCullMode }, { "getMeshCullMode", w_getMeshCullMode }, { "setFrontFaceWinding", w_setFrontFaceWinding },