[Fix] (Diligent): fix y inversion and front-face winding in viewport mapping

- Translate OpenGL bottom-left viewport/scissor rects to Diligent top-left origin
- Invert FrontCounterClockwise to compensate for the Y conversion
- Add DrawsTopHalfTriangleFromMobileGLState y-orientation test
This commit is contained in:
BZLZHH
2026-08-23 12:25:41 +08:00
parent ca7878bf3a
commit 24dfbb41f9
2 changed files with 80 additions and 4 deletions
@@ -1282,9 +1282,12 @@ void main()
if (glViewport.z() <= 0 || glViewport.w() <= 0) {
glViewport = IntVec4(0, 0, static_cast<Int32>(m_width), static_cast<Int32>(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<Float>(m_height) - (glViewport.w() + glViewport.y());
::Diligent::Viewport viewport{
static_cast<Float>(glViewport.x()),
static_cast<Float>(glViewport.y()),
viewportY,
static_cast<Float>(glViewport.z()),
static_cast<Float>(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<Int32>(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());
@@ -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";
}