diff --git a/MobileGL/MG_Backend/Diligent/Renderer/DiligentRenderer.cpp b/MobileGL/MG_Backend/Diligent/Renderer/DiligentRenderer.cpp index 5f019232..f436c218 100644 --- a/MobileGL/MG_Backend/Diligent/Renderer/DiligentRenderer.cpp +++ b/MobileGL/MG_Backend/Diligent/Renderer/DiligentRenderer.cpp @@ -1282,9 +1282,12 @@ void main() if (glViewport.z() <= 0 || glViewport.w() <= 0) { glViewport = IntVec4(0, 0, static_cast(m_width), static_cast(m_height)); } + // Diligent uses the top-left window origin (Direct3D convention); OpenGL uses + // a bottom-left origin. Translate the GL viewport rect into Diligent's space. + const Float viewportY = static_cast(m_height) - (glViewport.w() + glViewport.y()); ::Diligent::Viewport viewport{ static_cast(glViewport.x()), - static_cast(glViewport.y()), + viewportY, static_cast(glViewport.z()), static_cast(glViewport.w()), 0.0f, 1.0f}; @@ -1292,11 +1295,12 @@ void main() if (MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::ScissorTest)) { const auto scissor = MG_State::pGLContext->GetScissorBox(); + const Int32 scissorTop = static_cast(m_height) - (scissor.w() + scissor.y()); ::Diligent::Rect rect{ scissor.x(), - scissor.y(), + scissorTop, scissor.x() + scissor.z(), - scissor.y() + scissor.w()}; + scissorTop + scissor.w()}; m_pContext->SetScissorRects(1, &rect, m_width, m_height); } else { ::Diligent::Rect rect{0, 0, static_cast<::Diligent::Int32>(m_width), @@ -1508,8 +1512,10 @@ void main() graphicsPipeline.RasterizerDesc.CullMode = cullEnabled ? ConvertCullMode(MG_State::pGLContext->GetCullFaceMode()) : ::Diligent::CULL_MODE_NONE; + // The viewport Y conversion mirrors GL's bottom-left origin to Diligent's + // top-left origin, so the front-face winding must be inverted to match GL. graphicsPipeline.RasterizerDesc.FrontCounterClockwise = - MG_State::pGLContext->GetFrontFaceMode() == FrontFaceMode::CounterClockwise; + MG_State::pGLContext->GetFrontFaceMode() == FrontFaceMode::Clockwise; graphicsPipeline.DepthStencilDesc.DepthEnable = depthEnabled; graphicsPipeline.DepthStencilDesc.DepthWriteEnable = MG_State::pGLContext->GetDepthMask(); graphicsPipeline.DepthStencilDesc.DepthFunc = ConvertDepthFunc(MG_State::pGLContext->GetDepthFunc()); diff --git a/MobileGL/MG_Test/Backend/Diligent/SanityTest.cpp b/MobileGL/MG_Test/Backend/Diligent/SanityTest.cpp index 8ba71376..2c9fdb34 100644 --- a/MobileGL/MG_Test/Backend/Diligent/SanityTest.cpp +++ b/MobileGL/MG_Test/Backend/Diligent/SanityTest.cpp @@ -1120,3 +1120,73 @@ void main() { Color = vec4(1.0, 0.0, 0.0, 1.0); } EXPECT_GT(center[0], 200) << "center should be red from baseVertex-selected triangle"; EXPECT_LT(center[1], 50) << "center should not be green"; } + + +TEST(DiligentVulkanBackend, DrawsTopHalfTriangleFromMobileGLState) { + 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* fsSrc = R"(#version 330 core +out vec4 Color; +void main() { Color = vec4(1.0, 0.0, 0.0, 1.0); } +)"; + + const GLuint vs = CreateShader(GL_VERTEX_SHADER); + ShaderSource(vs, 1, &vsSrc, nullptr); + CompileShader(vs); + const GLuint fs = CreateShader(GL_FRAGMENT_SHADER); + ShaderSource(fs, 1, &fsSrc, nullptr); + CompileShader(fs); + const GLuint program = CreateProgram(); + AttachShader(program, vs); + AttachShader(program, fs); + LinkProgram(program); + UseProgram(program); + Viewport(0, 0, 256, 256); + BindFramebuffer(GL_FRAMEBUFFER, 0); + ReadBuffer(GL_BACK); + Disable(GL_DEPTH_TEST); + Disable(GL_BLEND); + Disable(GL_SCISSOR_TEST); + Disable(GL_STENCIL_TEST); + + // Triangle occupies the upper half of NDC (positive Y). + const float vertices[] = { + -0.5f, 0.1f, + 0.5f, 0.1f, + 0.0f, 1.0f, + }; + 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); + + DiligentBackend::BackendObject_Diligent backend; + backend.Initialize(); + auto* renderer = backend.GetRenderer(); + if (renderer == nullptr) { + GTEST_SKIP() << "No Vulkan adapter available; skipping y-orientation test"; + } + + renderer->Clear(0.0f, 1.0f, 0.0f, 1.0f); + renderer->DrawFromState(GL_TRIANGLES, 0, 3, 0, nullptr); + renderer->Present(); + + std::uint8_t upper[4] = {}; + renderer->ReadPixels(128, 64, 1, 1, upper); + EXPECT_GT(upper[0], 200) << "upper half should be red"; + + std::uint8_t lower[4] = {}; + renderer->ReadPixels(128, 192, 1, 1, lower); + EXPECT_GT(lower[1], 200) << "lower half should remain green"; + EXPECT_LT(lower[0], 50) << "lower half should not be red"; +}