mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
[Perf] (MG_Backend/DirectVulkan, MG_IntegrationTest/Scenarios): defer window clears while a window pass is open
- A whole-window clear with a window pass open ends that pass and folds into the next one, instead of running a load-clear-store pass of its own - A present drops the depth/stencil half of a pending window clear
This commit is contained in:
@@ -16,12 +16,15 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
// Present has its own submit path. Tag its last wire pass before that
|
// Present has its own submit path. Tag its last wire pass before that
|
||||||
// submission, rather than retaining it until a draw in the next frame.
|
// submission, rather than retaining it until a draw in the next frame.
|
||||||
RetireWireDrawPass();
|
RetireWireDrawPass();
|
||||||
// A window clear no pass drew after still has to reach the presented image; a frame that
|
// A window color clear no pass drew after still has to reach the presented image. Its
|
||||||
// cannot be presented drops it with the rest of the frame.
|
// depth/stencil half is dropped: a swap leaves those undefined (see the content flags
|
||||||
if (m_swapchainObject.GetHandle() == VK_NULL_HANDLE || m_presentSuspended)
|
// reset below). A frame that cannot be presented drops the whole clear.
|
||||||
|
if (m_swapchainObject.GetHandle() == VK_NULL_HANDLE || m_presentSuspended) {
|
||||||
m_defaultWirePendingClear = {};
|
m_defaultWirePendingClear = {};
|
||||||
else
|
} else {
|
||||||
|
m_defaultWirePendingClear.depthStencil = 0;
|
||||||
MaterializeDefaultWirePendingClear();
|
MaterializeDefaultWirePendingClear();
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
if (m_swapchainObject.GetHandle() == VK_NULL_HANDLE || m_presentSuspended) {
|
if (m_swapchainObject.GetHandle() == VK_NULL_HANDLE || m_presentSuspended) {
|
||||||
// No usable swapchain: the window was zero-area at initialization, or
|
// No usable swapchain: the window was zero-area at initialization, or
|
||||||
|
|||||||
@@ -1407,18 +1407,17 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
return variant;
|
return variant;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The default framebuffer's half of the deferred clears: a whole-window glClear with no pass
|
// The default framebuffer's half of the deferred clears: a whole-window glClear waits for the
|
||||||
// open on the window waits for the next pass that draws into it, which loads it with
|
// next pass that draws into the window, which loads it with LOAD_OP_CLEAR
|
||||||
// LOAD_OP_CLEAR (BeginWireDrawPassFolding) instead of storing a cleared window and loading it
|
// (BeginWireDrawPassFolding) instead of storing a cleared window and loading it back. A pass
|
||||||
// back. Returns the buffers it did not take.
|
// open on the window ends first, since the in-pass clear does not take the window
|
||||||
|
// (MagmaInPassClearDefault); a present drops the depth/stencil half (Present). Returns the
|
||||||
|
// buffers it did not take.
|
||||||
GLbitfield VulkanRenderer::DeferDefaultWireClear(const MG_Pipe::MGPFramebufferState& fbo,
|
GLbitfield VulkanRenderer::DeferDefaultWireClear(const MG_Pipe::MGPFramebufferState& fbo,
|
||||||
const ClearAttachmentPayload& payload, GLint drawbuffer) {
|
const ClearAttachmentPayload& payload, GLint drawbuffer) {
|
||||||
GLbitfield remaining = payload.mask & (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
GLbitfield remaining = payload.mask & (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
||||||
if (!remaining || !fbo.IsDefault || drawbuffer >= 0 || m_wirePreparationDepth != 0) return remaining;
|
if (!remaining || !fbo.IsDefault || drawbuffer >= 0 || m_wirePreparationDepth != 0) return remaining;
|
||||||
if (GLScissorTestClips()) return remaining;
|
if (GLScissorTestClips()) return remaining;
|
||||||
// A pass open on the window takes the clear itself (ClearWireAttachmentsOfOpenPass).
|
|
||||||
if (VkRenderPassManager::GetActiveRenderPass() != nullptr && m_wireDrawPass && m_wireDrawPassKey.isDefault)
|
|
||||||
return remaining;
|
|
||||||
GLbitfield handled = 0;
|
GLbitfield handled = 0;
|
||||||
Bool color = false;
|
Bool color = false;
|
||||||
VkImageAspectFlags depthStencil = 0;
|
VkImageAspectFlags depthStencil = 0;
|
||||||
@@ -1452,6 +1451,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!color && depthStencil == 0) return remaining & ~handled;
|
if (!color && depthStencil == 0) return remaining & ~handled;
|
||||||
|
if (VkRenderPassManager::GetActiveRenderPass() != nullptr && m_wireDrawPass && m_wireDrawPassKey.isDefault)
|
||||||
|
RetireWireDrawPass();
|
||||||
// The index the next drawing pass on the window will address (WireDraw.cpp's pass key).
|
// The index the next drawing pass on the window will address (WireDraw.cpp's pass key).
|
||||||
const Uint32 index = DefaultFramebufferWriteIndex(true);
|
const Uint32 index = DefaultFramebufferWriteIndex(true);
|
||||||
auto& pending = m_defaultWirePendingClear;
|
auto& pending = m_defaultWirePendingClear;
|
||||||
|
|||||||
@@ -357,4 +357,35 @@ void main() { o0 = texture(uPalette, vec2(0.5, 0.5)); }
|
|||||||
glDeleteProgram(paletteProgram);
|
glDeleteProgram(paletteProgram);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A depth clear of the DEFAULT framebuffer while a pass is open on it (a game clearing depth
|
||||||
|
// before its hand or GUI): the in-pass clear does not take the window, so the clear ends the
|
||||||
|
// pass and becomes the load op of the next one, which the farther draw must see.
|
||||||
|
TEST_F(InPassClearScenario, ADefaultFramebufferDepthClearBetweenTestedDrawsDecidesTheSecondDraw) {
|
||||||
|
if (!Ready()) return;
|
||||||
|
HeadlessGL& gl = Gl();
|
||||||
|
const int width = gl.Width(), height = gl.Height();
|
||||||
|
Build();
|
||||||
|
for (int frame = 0; frame < 2; ++frame) {
|
||||||
|
BindDefaultFramebuffer();
|
||||||
|
glViewport(0, 0, width, height);
|
||||||
|
glDisable(GL_SCISSOR_TEST);
|
||||||
|
glDisable(GL_BLEND);
|
||||||
|
glDepthMask(GL_TRUE);
|
||||||
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
|
glClearDepth(1.0);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
glDepthFunc(GL_LESS);
|
||||||
|
Quad(1.0f, 0.0f, 0.0f, 1.0f, -0.5f);
|
||||||
|
glClear(GL_DEPTH_BUFFER_BIT);
|
||||||
|
Quad(0.0f, 1.0f, 0.0f, 1.0f, 0.0f);
|
||||||
|
glDisable(GL_DEPTH_TEST);
|
||||||
|
EXPECT_EQ(FirstGLError(), 0u);
|
||||||
|
const Image image = ReadPixels(width, height);
|
||||||
|
EXPECT_TRUE(Near(image.At(width / 2, height / 2), 0, 255, 0, 255))
|
||||||
|
<< "frame " << frame << ": the depth clear between the draws let the farther draw pass";
|
||||||
|
gl.EndFrame();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace MGITest
|
} // namespace MGITest
|
||||||
|
|||||||
Reference in New Issue
Block a user