diff --git a/HANDOFF_DILIGENT.md b/HANDOFF_DILIGENT.md index 7beb2b92..fcacc632 100644 --- a/HANDOFF_DILIGENT.md +++ b/HANDOFF_DILIGENT.md @@ -105,6 +105,7 @@ Working tree is clean. - `DrawsWithBlendFromMobileGLState` - `DrawsWithDepthTestFromMobileGLState` - `DrawsNamedUniformBlockFromMobileGLState` + - `DrawsWithStencilTestFromMobileGLState` --- @@ -128,6 +129,7 @@ Verified locally on Turnip Adreno 750: - Textured draw test passes - Render state: - Blend enable/factors/equations + - Stencil clear + test enabled on a D24S8 default depth/stencil target - Depth test enable/func/write mask - Cull face enable/mode/front-face winding - Stencil test enable/masks/ops/func/ref @@ -156,7 +158,7 @@ Verified locally on Turnip Adreno 750: - Local test result: ``` -[ PASSED ] 12 tests +[ PASSED ] 13 tests ``` --- @@ -248,7 +250,7 @@ Notes: - [x] Texture filtering / sampler state test - [x] framebuffer offscreen render-to-texture test - [x] Depth test visual test - - [ ] Stencil test + - [x] Stencil test --- diff --git a/MobileGL/MG_Backend/Diligent/BackendObject_Diligent.cpp b/MobileGL/MG_Backend/Diligent/BackendObject_Diligent.cpp index 2f75c7b4..21638b3c 100644 --- a/MobileGL/MG_Backend/Diligent/BackendObject_Diligent.cpp +++ b/MobileGL/MG_Backend/Diligent/BackendObject_Diligent.cpp @@ -47,6 +47,9 @@ namespace MobileGL::MG_Backend::DiligentBackend { if ((mask & GL_DEPTH_BUFFER_BIT) != 0) { renderer->ClearDepth(MG_State::pGLContext->GetClearDepth()); } + if ((mask & GL_STENCIL_BUFFER_BIT) != 0) { + renderer->ClearStencil(MG_State::pGLContext->GetClearStencil()); + } } void DrawArrays(GLenum mode, GLint first, GLsizei count) { diff --git a/MobileGL/MG_Backend/Diligent/Renderer/DiligentRenderer.cpp b/MobileGL/MG_Backend/Diligent/Renderer/DiligentRenderer.cpp index accde107..d2690a42 100644 --- a/MobileGL/MG_Backend/Diligent/Renderer/DiligentRenderer.cpp +++ b/MobileGL/MG_Backend/Diligent/Renderer/DiligentRenderer.cpp @@ -406,7 +406,7 @@ void main() ::Diligent::TextureDesc depthDesc; depthDesc.Name = "MobileGL Diligent offscreen depth target"; depthDesc.Type = ::Diligent::RESOURCE_DIM_TEX_2D; - depthDesc.Format = ::Diligent::TEX_FORMAT_D32_FLOAT; + depthDesc.Format = ::Diligent::TEX_FORMAT_D24_UNORM_S8_UINT; depthDesc.Width = m_width; depthDesc.Height = m_height; depthDesc.MipLevels = 1; @@ -535,6 +535,21 @@ void main() ::Diligent::RESOURCE_STATE_TRANSITION_MODE_TRANSITION); } + void DiligentRenderer::ClearStencil(Uint32 stencil) { + if (!m_initialized || !m_pContext) { + return; + } + Vector<::Diligent::ITextureView*> rtvs; + ::Diligent::ITextureView* dsv = nullptr; + if (!ResolveCurrentRenderTargets(rtvs, dsv) || dsv == nullptr) { + return; + } + m_pContext->SetRenderTargets(static_cast<::Diligent::Uint32>(rtvs.size()), rtvs.data(), dsv, + ::Diligent::RESOURCE_STATE_TRANSITION_MODE_TRANSITION); + m_pContext->ClearDepthStencil(dsv, ::Diligent::CLEAR_STENCIL_FLAG, 0, stencil, + ::Diligent::RESOURCE_STATE_TRANSITION_MODE_TRANSITION); + } + void DiligentRenderer::DrawTriangle() { if (!m_initialized || !m_pContext || !m_pPSO) { return; @@ -1315,7 +1330,7 @@ void main() psoDesc.ResourceLayout.DefaultVariableType = ::Diligent::SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC; graphicsPipeline.NumRenderTargets = 1; graphicsPipeline.RTVFormats[0] = ::Diligent::TEX_FORMAT_RGBA8_UNORM; - graphicsPipeline.DSVFormat = ::Diligent::TEX_FORMAT_D32_FLOAT; + graphicsPipeline.DSVFormat = ::Diligent::TEX_FORMAT_D24_UNORM_S8_UINT; switch (mode) { case GL_POINTS: graphicsPipeline.PrimitiveTopology = ::Diligent::PRIMITIVE_TOPOLOGY_POINT_LIST; diff --git a/MobileGL/MG_Backend/Diligent/Renderer/DiligentRenderer.h b/MobileGL/MG_Backend/Diligent/Renderer/DiligentRenderer.h index 4847ca02..b886360b 100644 --- a/MobileGL/MG_Backend/Diligent/Renderer/DiligentRenderer.h +++ b/MobileGL/MG_Backend/Diligent/Renderer/DiligentRenderer.h @@ -51,6 +51,7 @@ namespace MobileGL::MG_Backend::DiligentBackend { Bool Initialize(Uint32 width, Uint32 height); void Clear(Float r, Float g, Float b, Float a); void ClearDepth(Float depth); + void ClearStencil(Uint32 stencil); void DrawTriangle(); void DrawVertices(const Float* vertices, Uint32 vertexCount); // Creates a simple 2D RGBA8 texture from CPU data and makes it available diff --git a/MobileGL/MG_Test/Backend/Diligent/SanityTest.cpp b/MobileGL/MG_Test/Backend/Diligent/SanityTest.cpp index 1703945b..2f26150c 100644 --- a/MobileGL/MG_Test/Backend/Diligent/SanityTest.cpp +++ b/MobileGL/MG_Test/Backend/Diligent/SanityTest.cpp @@ -798,3 +798,90 @@ void main() { Color = u_color; } EXPECT_LT(center[1], 50) << "center should not be green"; EXPECT_GT(center[2], 200) << "center should be blue from the named uniform block"; } + + +TEST(DiligentVulkanBackend, DrawsWithStencilTestFromMobileGLState) { + MobileGL::Initialize(); + + const char* vsSrc = R"(#version 330 core +layout(location = 0) in vec2 Position; +void main() { gl_Position = vec4(Position, 0.0, 1.0); } +)"; + const char* redFsSrc = R"(#version 330 core +out vec4 Color; +void main() { Color = vec4(1.0, 0.0, 0.0, 1.0); } +)"; + const char* blueFsSrc = R"(#version 330 core +out vec4 Color; +void main() { Color = vec4(0.0, 0.0, 1.0, 1.0); } +)"; + + const GLuint vs = CreateShader(GL_VERTEX_SHADER); + ShaderSource(vs, 1, &vsSrc, nullptr); + CompileShader(vs); + const GLuint redFs = CreateShader(GL_FRAGMENT_SHADER); + ShaderSource(redFs, 1, &redFsSrc, nullptr); + CompileShader(redFs); + const GLuint redProgram = CreateProgram(); + AttachShader(redProgram, vs); + AttachShader(redProgram, redFs); + LinkProgram(redProgram); + const GLuint blueFs = CreateShader(GL_FRAGMENT_SHADER); + ShaderSource(blueFs, 1, &blueFsSrc, nullptr); + CompileShader(blueFs); + const GLuint blueProgram = CreateProgram(); + AttachShader(blueProgram, vs); + AttachShader(blueProgram, blueFs); + LinkProgram(blueProgram); + + const float vertices[] = { + -0.5f, -0.5f, + 0.5f, -0.5f, + 0.0f, 0.5f, + }; + GLuint vbo = 0; + GenBuffers(1, &vbo); + BindBuffer(GL_ARRAY_BUFFER, vbo); + BufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + GLuint vao = 0; + GenVertexArrays(1, &vao); + BindVertexArray(vao); + EnableVertexAttribArray(0); + VertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, nullptr); + + BindFramebuffer(GL_FRAMEBUFFER, 0); + Disable(GL_DEPTH_TEST); + Disable(GL_BLEND); + Disable(GL_SCISSOR_TEST); + Viewport(0, 0, 256, 256); + + DiligentBackend::BackendObject_Diligent backend; + backend.Initialize(); + auto* renderer = backend.GetRenderer(); + if (renderer == nullptr) { + GTEST_SKIP() << "No Vulkan adapter available; skipping stencil state test"; + } + + renderer->Clear(0.0f, 1.0f, 0.0f, 1.0f); + renderer->ClearStencil(0); + + Enable(GL_STENCIL_TEST); + StencilMask(0xFF); + StencilFunc(GL_ALWAYS, 1, 0xFF); + StencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); + UseProgram(redProgram); + renderer->DrawFromState(GL_TRIANGLES, 0, 3, 0, nullptr); + + // Second draw should fail the stencil test (ref 2 != stencil value 1). + StencilFunc(GL_EQUAL, 2, 0xFF); + StencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + UseProgram(blueProgram); + renderer->DrawFromState(GL_TRIANGLES, 0, 3, 0, nullptr); + renderer->Present(); + + std::uint8_t center[4] = {}; + renderer->ReadPixels(128, 128, 1, 1, center); + EXPECT_GT(center[0], 200) << "center should remain red after failing stencil draw"; + EXPECT_LT(center[2], 50) << "center should not become blue"; +}