mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-12 06:08:30 +09:00
[Fix, Test] (MG_Backend/DirectVulkan): depth blits to or from the default framebuffer convert their rect out of GL's bottom-origin space, like the colour blit does
This commit is contained in:
@@ -7766,7 +7766,15 @@ void main() {
|
|||||||
MOBILEGL_ASSERT(ok, "%s: failed to transition depth destination image", __func__);
|
MOBILEGL_ASSERT(ok, "%s: failed to transition depth destination image", __func__);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (depthBlitScales) {
|
// The default framebuffer is stored display-side-up, so a rect aimed at it (or read
|
||||||
|
// from it) has to be converted out of GL's bottom-origin space - the same conversion
|
||||||
|
// the colour blit below applies. vkCmdCopyImage cannot express it (it has no second
|
||||||
|
// offset to invert), so a default-framebuffer side forces the vkCmdBlitImage form even
|
||||||
|
// at equal size. Without this a scissored depth blit into the default framebuffer
|
||||||
|
// wrote the MIRRORED band: KHR-GL*.framebuffer_blit.scissor_blit clips to the lower
|
||||||
|
// left quadrant, and the depth landed in the upper one.
|
||||||
|
const Bool depthBlitNeedsOrientation = readIsDefaultFbo || drawIsDefaultFbo;
|
||||||
|
if (depthBlitScales || depthBlitNeedsOrientation) {
|
||||||
// vkCmdCopyImage cannot resize; NEAREST is the only filter Vulkan allows for a
|
// vkCmdCopyImage cannot resize; NEAREST is the only filter Vulkan allows for a
|
||||||
// depth/stencil blit anyway, and the GL front end already rejects the others.
|
// depth/stencil blit anyway, and the GL front end already rejects the others.
|
||||||
VkImageBlit blitRegion{};
|
VkImageBlit blitRegion{};
|
||||||
@@ -7782,6 +7790,14 @@ void main() {
|
|||||||
blitRegion.dstSubresource.layerCount = dstBinding.layerCount;
|
blitRegion.dstSubresource.layerCount = dstBinding.layerCount;
|
||||||
blitRegion.dstOffsets[0] = {dstX0, dstY0, 0};
|
blitRegion.dstOffsets[0] = {dstX0, dstY0, 0};
|
||||||
blitRegion.dstOffsets[1] = {dstX1, dstY1, 1};
|
blitRegion.dstOffsets[1] = {dstX1, dstY1, 1};
|
||||||
|
if (readIsDefaultFbo) {
|
||||||
|
ApplyNativeBlitDefaultFramebufferSourceTransform(m_swapchainObject.GetPreTransform(), srcBinding,
|
||||||
|
blitRegion);
|
||||||
|
}
|
||||||
|
if (drawIsDefaultFbo) {
|
||||||
|
ApplyNativeBlitDefaultFramebufferTransform(m_swapchainObject.GetPreTransform(), dstBinding,
|
||||||
|
blitRegion);
|
||||||
|
}
|
||||||
vkCmdBlitImage(frame.commandBuffer,
|
vkCmdBlitImage(frame.commandBuffer,
|
||||||
srcBinding.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
srcBinding.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||||
dstBinding.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
dstBinding.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||||
|
|||||||
@@ -179,6 +179,75 @@ namespace MGITest {
|
|||||||
gl.EndFrame();
|
gl.EndFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A depth blit INTO the default framebuffer has to convert its rect out of GL's bottom-origin
|
||||||
|
// space, exactly as the colour blit does. The colour path had that conversion and the
|
||||||
|
// depth path did not, so a scissored depth blit landed in the mirrored band - which is the
|
||||||
|
// whole of KHR-GL*.framebuffer_blit.scissor_blit once the readback above works well enough to
|
||||||
|
// see it (before that the test died on the poison values and never reached the blit).
|
||||||
|
TEST_F(DepthStencilReadbackScenario, AScissoredDepthBlitIntoTheDefaultFramebufferLandsInTheScissorBox) {
|
||||||
|
if (!Ready()) return;
|
||||||
|
if (!BackendReadsDepthStencil()) {
|
||||||
|
GTEST_SKIP() << "backend " << Gl().BackendName() << " has no depth readback path";
|
||||||
|
}
|
||||||
|
HeadlessGL& gl = Gl();
|
||||||
|
const int width = gl.Width();
|
||||||
|
const int height = gl.Height();
|
||||||
|
ASSERT_GE(width, 8);
|
||||||
|
ASSERT_GE(height, 8);
|
||||||
|
|
||||||
|
// Source: a user framebuffer whose depth is uniformly 0.75.
|
||||||
|
GLuint fbo = 0, colorTex = 0, depthTex = 0;
|
||||||
|
glGenFramebuffers(1, &fbo);
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||||
|
glGenTextures(1, &colorTex);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, colorTex);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||||
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTex, 0);
|
||||||
|
glGenTextures(1, &depthTex);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, depthTex);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, width, height, 0, GL_DEPTH_STENCIL,
|
||||||
|
GL_UNSIGNED_INT_24_8, nullptr);
|
||||||
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, depthTex, 0);
|
||||||
|
ASSERT_EQ(glCheckFramebufferStatus(GL_FRAMEBUFFER), GLenum(GL_FRAMEBUFFER_COMPLETE));
|
||||||
|
glViewport(0, 0, width, height);
|
||||||
|
glDisable(GL_SCISSOR_TEST);
|
||||||
|
glDepthMask(GL_TRUE);
|
||||||
|
glClearDepth(0.75);
|
||||||
|
glClear(GL_DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
|
// Destination: the default framebuffer, depth 0 everywhere.
|
||||||
|
BindDefaultFramebuffer();
|
||||||
|
glViewport(0, 0, width, height);
|
||||||
|
glClearDepth(0.0);
|
||||||
|
glClear(GL_DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
|
// Blit the whole rect, but scissored to the BOTTOM-LEFT quadrant in GL coordinates.
|
||||||
|
glEnable(GL_SCISSOR_TEST);
|
||||||
|
glScissor(0, 0, width / 2, height / 2);
|
||||||
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
|
||||||
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||||
|
glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_DEPTH_BUFFER_BIT, GL_NEAREST);
|
||||||
|
glDisable(GL_SCISSOR_TEST);
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
|
EXPECT_EQ(FirstGLError(), 0u);
|
||||||
|
|
||||||
|
const float inside = ReadDepthAt(width / 4, height / 4);
|
||||||
|
const float above = ReadDepthAt(width / 4, height - 1 - height / 4);
|
||||||
|
EXPECT_EQ(FirstGLError(), 0u);
|
||||||
|
EXPECT_NEAR(inside, 0.75f, 1.0f / 4096.0f)
|
||||||
|
<< "GL (" << (width / 4) << ", " << (height / 4) << ") is inside the scissor box and should hold the "
|
||||||
|
<< "blitted 0.75, but read back " << inside;
|
||||||
|
EXPECT_NEAR(above, 0.0f, 1.0f / 4096.0f)
|
||||||
|
<< "GL (" << (width / 4) << ", " << (height - 1 - height / 4)
|
||||||
|
<< ") is ABOVE the scissor box and must still hold the cleared 0.0, but read back " << above
|
||||||
|
<< " (0.75 there means the depth blit landed in the mirrored band)";
|
||||||
|
|
||||||
|
glDeleteTextures(1, &depthTex);
|
||||||
|
glDeleteTextures(1, &colorTex);
|
||||||
|
glDeleteFramebuffers(1, &fbo);
|
||||||
|
gl.EndFrame();
|
||||||
|
}
|
||||||
|
|
||||||
// The control: the same read against a user framebuffer, which never went through the
|
// The control: the same read against a user framebuffer, which never went through the
|
||||||
// declined path. It is what makes a failure above specific to the default framebuffer.
|
// declined path. It is what makes a failure above specific to the default framebuffer.
|
||||||
TEST_F(DepthStencilReadbackScenario, UserFramebufferDepthClearIsVisibleToReadPixels) {
|
TEST_F(DepthStencilReadbackScenario, UserFramebufferDepthClearIsVisibleToReadPixels) {
|
||||||
|
|||||||
Reference in New Issue
Block a user