From ff505e9019a6a29f0223c5f299feeacb13f2db52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Leclercq?= Date: Fri, 28 May 2021 22:58:14 +0200 Subject: [PATCH] Renderer: Implement and fix front face (winding order) between Vulkan / OpenGL --- examples/GraphicsTest/main.cpp | 1 + examples/RenderTest/main.cpp | 1 + .../OpenGLRenderer/OpenGLRenderPipeline.hpp | 6 ++---- include/Nazara/OpenGLRenderer/Utils.hpp | 3 ++- include/Nazara/OpenGLRenderer/Utils.inl | 20 +++++++++++++++---- .../Nazara/OpenGLRenderer/Wrapper/Context.hpp | 2 +- .../OpenGLRenderer/Wrapper/CoreFunctions.hpp | 1 + include/Nazara/Renderer/RenderStates.hpp | 1 + include/Nazara/Utility/Enums.hpp | 8 ++++++++ include/Nazara/VulkanRenderer/Utils.hpp | 1 + include/Nazara/VulkanRenderer/Utils.inl | 14 ++++++++++++- .../OpenGLRenderer/OpenGLCommandBuffer.cpp | 5 +---- .../OpenGLRenderer/OpenGLRenderPipeline.cpp | 18 +++++++---------- src/Nazara/OpenGLRenderer/Wrapper/Context.cpp | 16 +++++++++++++-- .../VulkanRenderer/VulkanRenderPipeline.cpp | 4 ++-- 15 files changed, 71 insertions(+), 30 deletions(-) diff --git a/examples/GraphicsTest/main.cpp b/examples/GraphicsTest/main.cpp index 64bcd4fdd..6960a0b75 100644 --- a/examples/GraphicsTest/main.cpp +++ b/examples/GraphicsTest/main.cpp @@ -63,6 +63,7 @@ int main() std::shared_ptr material = std::make_shared(Nz::BasicMaterial::GetSettings()); material->EnableDepthBuffer(true); + material->EnableFaceCulling(true); Nz::BasicMaterial basicMat(*material); basicMat.EnableAlphaTest(true); diff --git a/examples/RenderTest/main.cpp b/examples/RenderTest/main.cpp index 49e122978..86f8f610d 100644 --- a/examples/RenderTest/main.cpp +++ b/examples/RenderTest/main.cpp @@ -190,6 +190,7 @@ int main() Nz::RenderPipelineInfo pipelineInfo; pipelineInfo.pipelineLayout = renderPipelineLayout; + pipelineInfo.faceCulling = true; pipelineInfo.depthBuffer = true; pipelineInfo.shaderModules.emplace_back(fragVertShader); diff --git a/include/Nazara/OpenGLRenderer/OpenGLRenderPipeline.hpp b/include/Nazara/OpenGLRenderer/OpenGLRenderPipeline.hpp index 6745b8c5f..fa3b227ee 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLRenderPipeline.hpp +++ b/include/Nazara/OpenGLRenderer/OpenGLRenderPipeline.hpp @@ -22,9 +22,7 @@ namespace Nz OpenGLRenderPipeline(OpenGLDevice& device, RenderPipelineInfo pipelineInfo); ~OpenGLRenderPipeline() = default; - void Apply(const GL::Context& context) const; - - void FlipY(bool shouldFlipY) const; + void Apply(const GL::Context& context, bool flipViewport) const; inline const RenderPipelineInfo& GetPipelineInfo() const override; @@ -32,7 +30,7 @@ namespace Nz RenderPipelineInfo m_pipelineInfo; GL::Program m_program; GLint m_flipYUniformLocation; - mutable bool m_isYFlipped; + mutable bool m_isViewportFlipped; }; } diff --git a/include/Nazara/OpenGLRenderer/Utils.hpp b/include/Nazara/OpenGLRenderer/Utils.hpp index 9e808839d..a51cafc93 100644 --- a/include/Nazara/OpenGLRenderer/Utils.hpp +++ b/include/Nazara/OpenGLRenderer/Utils.hpp @@ -31,7 +31,8 @@ namespace Nz inline GLenum ToOpenGL(BlendEquation blendEquation); inline GLenum ToOpenGL(BlendFunc blendFunc); - inline GLenum ToOpenGL(FaceSide filter); + inline GLenum ToOpenGL(FaceSide side); + inline GLenum ToOpenGL(FrontFace face); inline GLenum ToOpenGL(PrimitiveMode primitiveMode); inline GLenum ToOpenGL(SamplerFilter filter); inline GLenum ToOpenGL(SamplerFilter minFilter, SamplerMipmapMode mipmapFilter); diff --git a/include/Nazara/OpenGLRenderer/Utils.inl b/include/Nazara/OpenGLRenderer/Utils.inl index 739bda857..47faba72b 100644 --- a/include/Nazara/OpenGLRenderer/Utils.inl +++ b/include/Nazara/OpenGLRenderer/Utils.inl @@ -72,9 +72,9 @@ namespace Nz return {}; } - inline GLenum ToOpenGL(FaceSide filter) + inline GLenum ToOpenGL(FaceSide side) { - switch (filter) + switch (side) { case FaceSide::None: break; @@ -84,11 +84,23 @@ namespace Nz case FaceSide::FrontAndBack: return GL_FRONT_AND_BACK; } - NazaraError("Unhandled FaceSide 0x" + NumberToString(UnderlyingCast(filter), 16)); + NazaraError("Unhandled FaceSide 0x" + NumberToString(UnderlyingCast(side), 16)); return {}; } - GLenum ToOpenGL(PrimitiveMode primitiveMode) + inline GLenum ToOpenGL(FrontFace face) + { + switch (face) + { + case FrontFace::Clockwise: return GL_CW; + case FrontFace::CounterClockwise: return GL_CCW; + } + + NazaraError("Unhandled FrontFace 0x" + NumberToString(UnderlyingCast(face), 16)); + return {}; + } + + inline GLenum ToOpenGL(PrimitiveMode primitiveMode) { switch (primitiveMode) { diff --git a/include/Nazara/OpenGLRenderer/Wrapper/Context.hpp b/include/Nazara/OpenGLRenderer/Wrapper/Context.hpp index e80c51eec..dee916a58 100644 --- a/include/Nazara/OpenGLRenderer/Wrapper/Context.hpp +++ b/include/Nazara/OpenGLRenderer/Wrapper/Context.hpp @@ -144,7 +144,7 @@ namespace Nz::GL virtual void SwapBuffers() = 0; - void UpdateStates(const RenderStates& renderStates) const; + void UpdateStates(const RenderStates& renderStates, bool isViewportFlipped) const; #define NAZARA_OPENGLRENDERER_FUNC(name, sig) sig name = nullptr; NAZARA_OPENGLRENDERER_FOREACH_GLES_FUNC(NAZARA_OPENGLRENDERER_FUNC, NAZARA_OPENGLRENDERER_FUNC) diff --git a/include/Nazara/OpenGLRenderer/Wrapper/CoreFunctions.hpp b/include/Nazara/OpenGLRenderer/Wrapper/CoreFunctions.hpp index 5058b424a..851d482e4 100644 --- a/include/Nazara/OpenGLRenderer/Wrapper/CoreFunctions.hpp +++ b/include/Nazara/OpenGLRenderer/Wrapper/CoreFunctions.hpp @@ -76,6 +76,7 @@ typedef void (GL_APIENTRYP PFNGLSPECIALIZESHADERARBPROC) (GLuint shader, const G cb(glFramebufferRenderbuffer, PFNGLFRAMEBUFFERRENDERBUFFERPROC) \ cb(glFramebufferTexture2D, PFNGLFRAMEBUFFERTEXTURE2DPROC) \ cb(glFramebufferTextureLayer, PFNGLFRAMEBUFFERTEXTURELAYERPROC) \ + cb(glFrontFace, PFNGLFRONTFACEPROC) \ cb(glGenBuffers, PFNGLGENBUFFERSPROC) \ cb(glGenFramebuffers, PFNGLGENFRAMEBUFFERSPROC) \ cb(glGenQueries, PFNGLGENQUERIESPROC) \ diff --git a/include/Nazara/Renderer/RenderStates.hpp b/include/Nazara/Renderer/RenderStates.hpp index 978ab9777..0119f7c99 100644 --- a/include/Nazara/Renderer/RenderStates.hpp +++ b/include/Nazara/Renderer/RenderStates.hpp @@ -21,6 +21,7 @@ namespace Nz { FaceFilling faceFilling = FaceFilling::Fill; FaceSide cullingSide = FaceSide::Back; + FrontFace frontFace = FrontFace::Clockwise; RendererComparison depthCompare = RendererComparison::Less; PrimitiveMode primitiveMode = PrimitiveMode::TriangleList; diff --git a/include/Nazara/Utility/Enums.hpp b/include/Nazara/Utility/Enums.hpp index 7f586b630..2b1c92e65 100644 --- a/include/Nazara/Utility/Enums.hpp +++ b/include/Nazara/Utility/Enums.hpp @@ -149,6 +149,14 @@ namespace Nz Max = FrontAndBack }; + enum class FrontFace + { + Clockwise, + CounterClockwise, + + Max = CounterClockwise + }; + enum class ImageType { E1D, diff --git a/include/Nazara/VulkanRenderer/Utils.hpp b/include/Nazara/VulkanRenderer/Utils.hpp index 88ec90b05..77f553d30 100644 --- a/include/Nazara/VulkanRenderer/Utils.hpp +++ b/include/Nazara/VulkanRenderer/Utils.hpp @@ -24,6 +24,7 @@ namespace Nz inline VkFormat ToVulkan(ComponentType componentType); inline VkCullModeFlagBits ToVulkan(FaceSide faceSide); inline VkPolygonMode ToVulkan(FaceFilling faceFilling); + inline VkFrontFace ToVulkan(FrontFace frontFace); inline VkAccessFlagBits ToVulkan(MemoryAccess memoryAccess); inline VkAccessFlags ToVulkan(MemoryAccessFlags memoryAccessFlags); inline VkPipelineStageFlagBits ToVulkan(PipelineStage pipelineStage); diff --git a/include/Nazara/VulkanRenderer/Utils.inl b/include/Nazara/VulkanRenderer/Utils.inl index 244eb76a5..e280a1202 100644 --- a/include/Nazara/VulkanRenderer/Utils.inl +++ b/include/Nazara/VulkanRenderer/Utils.inl @@ -153,7 +153,19 @@ namespace Nz NazaraError("Unhandled FaceFilling 0x" + NumberToString(UnderlyingCast(faceFilling), 16)); return VK_POLYGON_MODE_FILL; } - + + inline VkFrontFace ToVulkan(FrontFace frontFace) + { + switch (frontFace) + { + case FrontFace::Clockwise: return VK_FRONT_FACE_CLOCKWISE; + case FrontFace::CounterClockwise: return VK_FRONT_FACE_COUNTER_CLOCKWISE; + } + + NazaraError("Unhandled FrontFace 0x" + NumberToString(UnderlyingCast(frontFace), 16)); + return {}; + } + inline VkAccessFlagBits ToVulkan(MemoryAccess memoryAccess) { switch (memoryAccess) diff --git a/src/Nazara/OpenGLRenderer/OpenGLCommandBuffer.cpp b/src/Nazara/OpenGLRenderer/OpenGLCommandBuffer.cpp index d99c8e4a7..7066d8384 100644 --- a/src/Nazara/OpenGLRenderer/OpenGLCommandBuffer.cpp +++ b/src/Nazara/OpenGLRenderer/OpenGLCommandBuffer.cpp @@ -241,10 +241,7 @@ namespace Nz void OpenGLCommandBuffer::ApplyStates(const GL::Context& context, const DrawStates& states) { - states.pipeline->Apply(context); - - states.pipeline->FlipY(states.shouldFlipY); - + states.pipeline->Apply(context, states.shouldFlipY); states.shaderBindings->Apply(context); if (states.scissorRegion) diff --git a/src/Nazara/OpenGLRenderer/OpenGLRenderPipeline.cpp b/src/Nazara/OpenGLRenderer/OpenGLRenderPipeline.cpp index f6703391f..cda8d32f1 100644 --- a/src/Nazara/OpenGLRenderer/OpenGLRenderPipeline.cpp +++ b/src/Nazara/OpenGLRenderer/OpenGLRenderPipeline.cpp @@ -16,7 +16,7 @@ namespace Nz { OpenGLRenderPipeline::OpenGLRenderPipeline(OpenGLDevice& device, RenderPipelineInfo pipelineInfo) : m_pipelineInfo(std::move(pipelineInfo)), - m_isYFlipped(false) + m_isViewportFlipped(false) { if (!m_program.Create(device)) throw std::runtime_error("failed to create program"); @@ -39,18 +39,14 @@ namespace Nz m_program.Uniform(m_flipYUniformLocation, 1.f); } - void OpenGLRenderPipeline::Apply(const GL::Context& context) const + void OpenGLRenderPipeline::Apply(const GL::Context& context, bool flipViewport) const { - context.UpdateStates(m_pipelineInfo); - context.BindProgram(m_program.GetObjectId()); //< Bind program after states - } - - void OpenGLRenderPipeline::FlipY(bool shouldFlipY) const - { - if (m_isYFlipped != shouldFlipY) + context.UpdateStates(m_pipelineInfo, flipViewport); + context.BindProgram(m_program.GetObjectId()); //< Bind program after states (for shader caching) + if (m_isViewportFlipped != flipViewport) { - m_program.Uniform(m_flipYUniformLocation, (shouldFlipY) ? -1.f : 1.f); - m_isYFlipped = shouldFlipY; + m_program.Uniform(m_flipYUniformLocation, (flipViewport) ? -1.f : 1.f); + m_isViewportFlipped = flipViewport; } } } diff --git a/src/Nazara/OpenGLRenderer/Wrapper/Context.cpp b/src/Nazara/OpenGLRenderer/Wrapper/Context.cpp index ea512dad2..dc55efcce 100644 --- a/src/Nazara/OpenGLRenderer/Wrapper/Context.cpp +++ b/src/Nazara/OpenGLRenderer/Wrapper/Context.cpp @@ -363,6 +363,8 @@ namespace Nz::GL glGetIntegerv(GL_VIEWPORT, res.data()); m_state.viewport = { res[0], res[1], res[2], res[3] }; + m_state.renderStates.frontFace = FrontFace::CounterClockwise; //< OpenGL default front face is counter-clockwise + EnableVerticalSync(false); return true; @@ -435,7 +437,7 @@ namespace Nz::GL } } - void Context::UpdateStates(const RenderStates& renderStates) const + void Context::UpdateStates(const RenderStates& renderStates, bool isViewportFlipped) const { if (!SetCurrentContext(this)) throw std::runtime_error("failed to activate context"); @@ -482,11 +484,21 @@ namespace Nz::GL { if (m_state.renderStates.cullingSide != renderStates.cullingSide) { - glCullFace(ToOpenGL(m_state.renderStates.cullingSide)); + glCullFace(ToOpenGL(renderStates.cullingSide)); m_state.renderStates.cullingSide = renderStates.cullingSide; } } + FrontFace targetFrontFace = renderStates.frontFace; + if (!isViewportFlipped) + targetFrontFace = (targetFrontFace == FrontFace::Clockwise) ? FrontFace::CounterClockwise : FrontFace::Clockwise; + + if (m_state.renderStates.frontFace != targetFrontFace) + { + glFrontFace(ToOpenGL(targetFrontFace)); + m_state.renderStates.frontFace = targetFrontFace; + } + /* TODO: Use glPolyonMode if available (OpenGL) if (m_state.renderStates.faceFilling != renderStates.faceFilling) diff --git a/src/Nazara/VulkanRenderer/VulkanRenderPipeline.cpp b/src/Nazara/VulkanRenderer/VulkanRenderPipeline.cpp index 399ae88c5..2e6498d15 100644 --- a/src/Nazara/VulkanRenderer/VulkanRenderPipeline.cpp +++ b/src/Nazara/VulkanRenderer/VulkanRenderPipeline.cpp @@ -140,8 +140,8 @@ namespace Nz VkPipelineRasterizationStateCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; createInfo.polygonMode = ToVulkan(pipelineInfo.faceFilling); - createInfo.cullMode = ToVulkan(pipelineInfo.cullingSide); - createInfo.frontFace = VK_FRONT_FACE_CLOCKWISE; //< TODO + createInfo.cullMode = (pipelineInfo.faceCulling) ? ToVulkan(pipelineInfo.cullingSide) : VK_CULL_MODE_NONE; + createInfo.frontFace = ToVulkan(pipelineInfo.frontFace); createInfo.lineWidth = pipelineInfo.lineWidth; return createInfo;