Commit c03c4490 by Charlie Lao Committed by Commit Bot

Vulkan: Defer glFlush issued in middle of renderpass to endRenderpass

Manhattan is calling glFlush in middle of a renderpass. This CL defers the flush that issued in the middle of renderpass to the end of renderpass. Bug: b/166475273 Change-Id: I6baa3898d5efc456e2205c44e13c64f3d79d1464 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2381942 Commit-Queue: Charlie Lao <cclao@google.com> Reviewed-by: 's avatarShahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarTim Van Patten <timvp@google.com>
parent 7fc325cc
...@@ -361,6 +361,15 @@ struct FeaturesVk : FeatureSetBase ...@@ -361,6 +361,15 @@ struct FeaturesVk : FeatureSetBase
"preferred_large_heap_block_size_4M", FeatureCategory::VulkanWorkarounds, "preferred_large_heap_block_size_4M", FeatureCategory::VulkanWorkarounds,
"Use 4 MB preferred large heap block size with AMD allocator", &members, "Use 4 MB preferred large heap block size with AMD allocator", &members,
"http://anglebug.com/4995"}; "http://anglebug.com/4995"};
// Manhattan is calling glFlush in the middle of renderpass which breaks renderpass and hurts
// performance on tile based GPU. When this is enabled, we will defer the glFlush call made in
// the middle of renderpass to the end of renderpass.
// https://issuetracker.google.com/issues/166475273
Feature deferFlushUntilEndRenderPass = {
"defer_flush_until_endrenderpass", FeatureCategory::VulkanWorkarounds,
"Allow glFlush to be deferred until renderpass ends", &members,
"https://issuetracker.google.com/issues/166475273"};
}; };
inline FeaturesVk::FeaturesVk() = default; inline FeaturesVk::FeaturesVk() = default;
......
...@@ -671,6 +671,8 @@ ContextVk::ContextVk(const gl::State &state, gl::ErrorSet *errorSet, RendererVk ...@@ -671,6 +671,8 @@ ContextVk::ContextVk(const gl::State &state, gl::ErrorSet *errorSet, RendererVk
mRenderPassCommands(nullptr), mRenderPassCommands(nullptr),
mHasPrimaryCommands(false), mHasPrimaryCommands(false),
mGpuEventsEnabled(false), mGpuEventsEnabled(false),
mSyncObjectPendingFlush(false),
mDeferredFlushCount(0),
mGpuClockSync{std::numeric_limits<double>::max(), std::numeric_limits<double>::max()}, mGpuClockSync{std::numeric_limits<double>::max(), std::numeric_limits<double>::max()},
mGpuEventTimestampOrigin(0), mGpuEventTimestampOrigin(0),
mPerfCounters{}, mPerfCounters{},
...@@ -973,6 +975,17 @@ angle::Result ContextVk::startPrimaryCommandBuffer() ...@@ -973,6 +975,17 @@ angle::Result ContextVk::startPrimaryCommandBuffer()
angle::Result ContextVk::flush(const gl::Context *context) angle::Result ContextVk::flush(const gl::Context *context)
{ {
// If we are in middle of renderpass and it is not a shared context, then we will defer the
// glFlush call here until the renderpass ends. If sync object has been used, we must respect
// glFlush call, otherwise we a wait for sync object without GL_SYNC_FLUSH_COMMANDS_BIT may
// never come back.
if (mRenderer->getFeatures().deferFlushUntilEndRenderPass.enabled && !context->isShared() &&
!mSyncObjectPendingFlush && hasStartedRenderPass())
{
mDeferredFlushCount++;
return angle::Result::Continue;
}
return flushImpl(nullptr); return flushImpl(nullptr);
} }
...@@ -4078,11 +4091,17 @@ angle::Result ContextVk::flushImpl(const vk::Semaphore *signalSemaphore) ...@@ -4078,11 +4091,17 @@ angle::Result ContextVk::flushImpl(const vk::Semaphore *signalSemaphore)
bool hasPendingSemaphore = signalSemaphore || !mWaitSemaphores.empty(); bool hasPendingSemaphore = signalSemaphore || !mWaitSemaphores.empty();
if (!hasRecordedCommands() && !hasPendingSemaphore && !mGpuEventsEnabled) if (!hasRecordedCommands() && !hasPendingSemaphore && !mGpuEventsEnabled)
{ {
ASSERT(!mDeferredFlushCount);
return angle::Result::Continue; return angle::Result::Continue;
} }
ANGLE_TRACE_EVENT0("gpu.angle", "ContextVk::flush"); ANGLE_TRACE_EVENT0("gpu.angle", "ContextVk::flush");
// We must set this to zero before calling flushCommandsAndEndRenderPass to prevent it from
// calling back to flushImpl.
mDeferredFlushCount = 0;
mSyncObjectPendingFlush = false;
ANGLE_TRY(flushCommandsAndEndRenderPass()); ANGLE_TRY(flushCommandsAndEndRenderPass());
if (mIsAnyHostVisibleBufferWritten) if (mIsAnyHostVisibleBufferWritten)
...@@ -4589,6 +4608,7 @@ angle::Result ContextVk::flushCommandsAndEndRenderPass() ...@@ -4589,6 +4608,7 @@ angle::Result ContextVk::flushCommandsAndEndRenderPass()
if (!mRenderPassCommands->started()) if (!mRenderPassCommands->started())
{ {
ASSERT(!mDeferredFlushCount);
onRenderPassFinished(); onRenderPassFinished();
return angle::Result::Continue; return angle::Result::Continue;
} }
...@@ -4657,6 +4677,12 @@ angle::Result ContextVk::flushCommandsAndEndRenderPass() ...@@ -4657,6 +4677,12 @@ angle::Result ContextVk::flushCommandsAndEndRenderPass()
ANGLE_TRY(flushOutsideRenderPassCommands()); ANGLE_TRY(flushOutsideRenderPassCommands());
} }
if (mDeferredFlushCount > 0)
{
// If we have deferred glFlush call in the middle of renderpass, flush them now.
ANGLE_TRY(flushImpl(nullptr));
}
return angle::Result::Continue; return angle::Result::Continue;
} }
......
...@@ -634,6 +634,8 @@ class ContextVk : public ContextImpl, public vk::Context ...@@ -634,6 +634,8 @@ class ContextVk : public ContextImpl, public vk::Context
const vk::PerfCounters &getPerfCounters() const { return mPerfCounters; } const vk::PerfCounters &getPerfCounters() const { return mPerfCounters; }
vk::PerfCounters &getPerfCounters() { return mPerfCounters; } vk::PerfCounters &getPerfCounters() { return mPerfCounters; }
void onSyncHelperInitialize() { mSyncObjectPendingFlush = true; }
private: private:
// Dirty bits. // Dirty bits.
enum DirtyBitType : size_t enum DirtyBitType : size_t
...@@ -1077,6 +1079,11 @@ class ContextVk : public ContextImpl, public vk::Context ...@@ -1077,6 +1079,11 @@ class ContextVk : public ContextImpl, public vk::Context
// A list of gpu events since the last clock sync. // A list of gpu events since the last clock sync.
std::vector<GpuEvent> mGpuEvents; std::vector<GpuEvent> mGpuEvents;
// Track SyncHelper object been added into secondary command buffer that has not been flushed to
// vulkan.
bool mSyncObjectPendingFlush;
uint32_t mDeferredFlushCount;
// Semaphores that must be waited on in the next submission. // Semaphores that must be waited on in the next submission.
std::vector<VkSemaphore> mWaitSemaphores; std::vector<VkSemaphore> mWaitSemaphores;
std::vector<VkPipelineStageFlags> mWaitSemaphoreStageMasks; std::vector<VkPipelineStageFlags> mWaitSemaphoreStageMasks;
......
...@@ -1868,6 +1868,8 @@ void RendererVk::initFeatures(DisplayVk *displayVk, const ExtensionNameList &dev ...@@ -1868,6 +1868,8 @@ void RendererVk::initFeatures(DisplayVk *displayVk, const ExtensionNameList &dev
ANGLE_FEATURE_CONDITION(&mFeatures, preferredLargeHeapBlockSize4MB, !isQualcomm); ANGLE_FEATURE_CONDITION(&mFeatures, preferredLargeHeapBlockSize4MB, !isQualcomm);
ANGLE_FEATURE_CONDITION(&mFeatures, deferFlushUntilEndRenderPass, true);
angle::PlatformMethods *platform = ANGLEPlatformCurrent(); angle::PlatformMethods *platform = ANGLEPlatformCurrent();
platform->overrideFeaturesVk(platform, &mFeatures); platform->overrideFeaturesVk(platform, &mFeatures);
......
...@@ -56,6 +56,8 @@ angle::Result SyncHelper::initialize(ContextVk *contextVk) ...@@ -56,6 +56,8 @@ angle::Result SyncHelper::initialize(ContextVk *contextVk)
commandBuffer.setEvent(mEvent.getHandle(), VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT); commandBuffer.setEvent(mEvent.getHandle(), VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT);
retain(&contextVk->getResourceUseList()); retain(&contextVk->getResourceUseList());
contextVk->onSyncHelperInitialize();
return angle::Result::Continue; return angle::Result::Continue;
} }
......
...@@ -225,8 +225,9 @@ TEST_P(OcclusionQueriesTest, MultiQueries) ...@@ -225,8 +225,9 @@ TEST_P(OcclusionQueriesTest, MultiQueries)
EXPECT_GL_NO_ERROR(); EXPECT_GL_NO_ERROR();
// A flush shound't clear the query result // Due to implementation might skip in-renderpass flush, we are using glFinish here to force a
glFlush(); // flush. A flush shound't clear the query result.
glFinish();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
drawQuad(mProgram, essl1_shaders::PositionAttrib(), -2, 0.25f); // this quad should be occluded drawQuad(mProgram, essl1_shaders::PositionAttrib(), -2, 0.25f); // this quad should be occluded
...@@ -248,7 +249,7 @@ TEST_P(OcclusionQueriesTest, MultiQueries) ...@@ -248,7 +249,7 @@ TEST_P(OcclusionQueriesTest, MultiQueries)
0.5f); // this quad should not be occluded 0.5f); // this quad should not be occluded
glEndQueryEXT(GL_ANY_SAMPLES_PASSED_EXT); glEndQueryEXT(GL_ANY_SAMPLES_PASSED_EXT);
// ------------ // ------------
glFlush(); glFinish();
glViewport(0, 0, getWindowWidth() / 2, getWindowHeight()); glViewport(0, 0, getWindowWidth() / 2, getWindowHeight());
glScissor(0, 0, getWindowWidth() / 2, getWindowHeight()); glScissor(0, 0, getWindowWidth() / 2, getWindowHeight());
......
...@@ -1242,6 +1242,33 @@ TEST_P(VulkanPerformanceCounterTest, DrawbufferChangeWithAllColorMaskDisabled) ...@@ -1242,6 +1242,33 @@ TEST_P(VulkanPerformanceCounterTest, DrawbufferChangeWithAllColorMaskDisabled)
EXPECT_EQ(expectedRenderPassCount, actualRenderPassCount); EXPECT_EQ(expectedRenderPassCount, actualRenderPassCount);
} }
// Tests the optimization that a glFlush call issued inside a renderpass will be skipped.
TEST_P(VulkanPerformanceCounterTest, InRenderpassFlushShouldBotBreakRenderpass)
{
const rx::vk::PerfCounters &counters = hackANGLE();
uint32_t expectedRenderPassCount = counters.renderPasses + 1;
GLTexture texture;
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
GLFramebuffer framebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
ASSERT_GL_NO_ERROR();
ANGLE_GL_PROGRAM(redProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
drawQuad(redProgram, essl1_shaders::PositionAttrib(), 0.5f);
glFlush();
ANGLE_GL_PROGRAM(greenProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
drawQuad(greenProgram, essl1_shaders::PositionAttrib(), 0.5f);
ASSERT_GL_NO_ERROR();
uint32_t actualRenderPassCount = counters.renderPasses;
EXPECT_EQ(expectedRenderPassCount, actualRenderPassCount);
}
ANGLE_INSTANTIATE_TEST(VulkanPerformanceCounterTest, ES3_VULKAN()); ANGLE_INSTANTIATE_TEST(VulkanPerformanceCounterTest, ES3_VULKAN());
ANGLE_INSTANTIATE_TEST(VulkanPerformanceCounterTest_ES31, ES31_VULKAN()); ANGLE_INSTANTIATE_TEST(VulkanPerformanceCounterTest_ES31, ES31_VULKAN());
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment