Commit 89d2a96a by Jamie Madill Committed by Commit Bot

Vulkan: Add test for UBO descriptor allocations.

This performance counter test verifies that re-binding the same two buffers repeatedly doesn't allocate new descriptor sets. Currently the test fails because we don't cache descriptor sets for UBOs. Covers equivalent code patterns in Asphalt 9. Reorganizes the perf counters collected for the program objects. Now they are per-frame reset instead of cumulative. This tracking is now consistent for the different counter types. In the future we can add cumulative tracking for all per-object and global perf counters. Bug: angleproject:5736 Change-Id: I23d04b6453e38af1cf4af7274d24382d136efad3 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2746176Reviewed-by: 's avatarShahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: 's avatarTim Van Patten <timvp@google.com> Commit-Queue: Jamie Madill <jmadill@chromium.org>
parent 4d7ea943
...@@ -1778,11 +1778,43 @@ angle::Result ContextVk::handleDirtyDescriptorSetsImpl(vk::CommandBuffer *comman ...@@ -1778,11 +1778,43 @@ angle::Result ContextVk::handleDirtyDescriptorSetsImpl(vk::CommandBuffer *comman
return mExecutable->updateDescriptorSets(this, commandBuffer); return mExecutable->updateDescriptorSets(this, commandBuffer);
} }
void ContextVk::syncObjectPerfCounters()
{
uint32_t descriptorSetAllocations = 0;
// ContextVk's descriptor set allocations
for (const uint32_t count : mObjectPerfCounters.descriptorSetsAllocated)
{
descriptorSetAllocations += count;
}
// UtilsVk's descriptor set allocations
descriptorSetAllocations += mUtils.getObjectPerfCounters().descriptorSetsAllocated;
// ProgramExecutableVk's descriptor set allocations
const gl::State &state = getState();
const gl::ShaderProgramManager &shadersAndPrograms = state.getShaderProgramManagerForCapture();
const gl::ResourceMap<gl::Program, gl::ShaderProgramID> &programs =
shadersAndPrograms.getProgramsForCaptureAndPerf();
for (const std::pair<GLuint, gl::Program *> &resource : programs)
{
ProgramVk *programVk = vk::GetImpl(resource.second);
ProgramExecutablePerfCounters progPerfCounters =
programVk->getExecutable().getAndResetObjectPerfCounters();
for (const uint32_t count : progPerfCounters.descriptorSetsAllocated)
{
descriptorSetAllocations += count;
}
}
mPerfCounters.descriptorSetAllocations = descriptorSetAllocations;
}
void ContextVk::updateOverlayOnPresent() void ContextVk::updateOverlayOnPresent()
{ {
const gl::OverlayType *overlay = mState.getOverlay(); const gl::OverlayType *overlay = mState.getOverlay();
ASSERT(overlay->isEnabled()); ASSERT(overlay->isEnabled());
syncObjectPerfCounters();
// Update overlay if active. // Update overlay if active.
{ {
gl::RunningGraphWidget *renderPassCount = gl::RunningGraphWidget *renderPassCount =
...@@ -1801,39 +1833,10 @@ void ContextVk::updateOverlayOnPresent() ...@@ -1801,39 +1833,10 @@ void ContextVk::updateOverlayOnPresent()
} }
{ {
uint32_t descriptorSetAllocations = 0;
// ContextVk's descriptor set allocations
for (const uint32_t count : mObjectPerfCounters.descriptorSetsAllocated)
{
descriptorSetAllocations += count;
}
// UtilsVk's descriptor set allocations
descriptorSetAllocations += mUtils.getObjectPerfCounters().descriptorSetsAllocated;
// ProgramExecutableVk's descriptor set allocations
const gl::State &state = getState();
const gl::ShaderProgramManager &shadersAndPrograms =
state.getShaderProgramManagerForCapture();
const gl::ResourceMap<gl::Program, gl::ShaderProgramID> &programs =
shadersAndPrograms.getProgramsForCaptureAndPerf();
for (const std::pair<GLuint, gl::Program *> &resource : programs)
{
ProgramVk *programVk = vk::GetImpl(resource.second);
ProgramExecutableVk::PerfCounters progPerfCounters =
programVk->getExecutable().getObjectPerfCounters();
for (const uint32_t count : progPerfCounters.descriptorSetsAllocated)
{
descriptorSetAllocations += count;
}
}
gl::RunningGraphWidget *descriptorSetAllocationCount = gl::RunningGraphWidget *descriptorSetAllocationCount =
overlay->getRunningGraphWidget(gl::WidgetId::VulkanDescriptorSetAllocations); overlay->getRunningGraphWidget(gl::WidgetId::VulkanDescriptorSetAllocations);
descriptorSetAllocationCount->add(descriptorSetAllocations - descriptorSetAllocationCount->add(mPerfCounters.descriptorSetAllocations);
mPerfCounters.descriptorSetAllocations);
descriptorSetAllocationCount->next(); descriptorSetAllocationCount->next();
mPerfCounters.descriptorSetAllocations = descriptorSetAllocations;
} }
{ {
......
...@@ -561,6 +561,7 @@ class ContextVk : public ContextImpl, public vk::Context, public MultisampleText ...@@ -561,6 +561,7 @@ class ContextVk : public ContextImpl, public vk::Context, public MultisampleText
// Used by QueryVk to share query helpers between transform feedback queries. // Used by QueryVk to share query helpers between transform feedback queries.
QueryVk *getActiveRenderPassQuery(gl::QueryType queryType) const; QueryVk *getActiveRenderPassQuery(gl::QueryType queryType) const;
void syncObjectPerfCounters();
void updateOverlayOnPresent(); void updateOverlayOnPresent();
void addOverlayUsedBuffersCount(vk::CommandBufferHelper *commandBuffer); void addOverlayUsedBuffersCount(vk::CommandBufferHelper *commandBuffer);
......
...@@ -1828,4 +1828,11 @@ void ProgramExecutableVk::outputCumulativePerfCounters() ...@@ -1828,4 +1828,11 @@ void ProgramExecutableVk::outputCumulativePerfCounters()
} }
} }
ProgramExecutablePerfCounters ProgramExecutableVk::getAndResetObjectPerfCounters()
{
ProgramExecutablePerfCounters counters = mObjectPerfCounters;
mObjectPerfCounters.descriptorSetsAllocated = {};
return counters;
}
} // namespace rx } // namespace rx
...@@ -101,6 +101,14 @@ struct DefaultUniformBlock final : private angle::NonCopyable ...@@ -101,6 +101,14 @@ struct DefaultUniformBlock final : private angle::NonCopyable
std::vector<sh::BlockMemberInfo> uniformLayout; std::vector<sh::BlockMemberInfo> uniformLayout;
}; };
// Performance and resource counters.
using DescriptorSetCountList = std::array<uint32_t, DescriptorSetIndex::EnumCount>;
struct ProgramExecutablePerfCounters
{
DescriptorSetCountList descriptorSetsAllocated;
};
class ProgramExecutableVk class ProgramExecutableVk
{ {
public: public:
...@@ -175,14 +183,7 @@ class ProgramExecutableVk ...@@ -175,14 +183,7 @@ class ProgramExecutableVk
mProgramPipeline = pipeline; mProgramPipeline = pipeline;
} }
using DescriptorSetCountList = std::array<uint32_t, DescriptorSetIndex::EnumCount>; ProgramExecutablePerfCounters getAndResetObjectPerfCounters();
// Performance and resource counters.
struct PerfCounters
{
DescriptorSetCountList descriptorSetsAllocated;
};
const PerfCounters getObjectPerfCounters() const { return mObjectPerfCounters; }
private: private:
friend class ProgramVk; friend class ProgramVk;
...@@ -282,7 +283,7 @@ class ProgramExecutableVk ...@@ -282,7 +283,7 @@ class ProgramExecutableVk
ProgramVk *mProgram; ProgramVk *mProgram;
ProgramPipelineVk *mProgramPipeline; ProgramPipelineVk *mProgramPipeline;
PerfCounters mObjectPerfCounters; ProgramExecutablePerfCounters mObjectPerfCounters;
}; };
} // namespace rx } // namespace rx
......
...@@ -43,7 +43,10 @@ class VulkanPerformanceCounterTest : public ANGLETest ...@@ -43,7 +43,10 @@ class VulkanPerformanceCounterTest : public ANGLETest
{ {
// Hack the angle! // Hack the angle!
const gl::Context *context = static_cast<const gl::Context *>(getEGLWindow()->getContext()); const gl::Context *context = static_cast<const gl::Context *>(getEGLWindow()->getContext());
return rx::GetImplAs<const rx::ContextVk>(context)->getPerfCounters(); rx::ContextVk *contextVk = rx::GetImplAs<rx::ContextVk>(context);
// This will be implicitly called when using the extension.
contextVk->syncObjectPerfCounters();
return contextVk->getPerfCounters();
} }
static constexpr GLsizei kInvalidateTestSize = 16; static constexpr GLsizei kInvalidateTestSize = 16;
...@@ -2919,6 +2922,129 @@ TEST_P(VulkanPerformanceCounterTest, ScissorDoesNotBreakRenderPass) ...@@ -2919,6 +2922,129 @@ TEST_P(VulkanPerformanceCounterTest, ScissorDoesNotBreakRenderPass)
GLColor::transparentBlack); GLColor::transparentBlack);
} }
// Tests that changing UBO bindings does not allocate new descriptor sets.
TEST_P(VulkanPerformanceCounterTest, ChangingUBOsHitsDescriptorSetCache)
{
// Set up two UBOs, one filled with "1" and the second with "2".
constexpr GLsizei kCount = 64;
std::vector<GLint> data1(kCount, 1);
std::vector<GLint> data2(kCount, 2);
GLBuffer ubo1;
glBindBuffer(GL_UNIFORM_BUFFER, ubo1);
glBufferData(GL_UNIFORM_BUFFER, kCount * sizeof(data1[0]), data1.data(), GL_STATIC_DRAW);
GLBuffer ubo2;
glBindBuffer(GL_UNIFORM_BUFFER, ubo2);
glBufferData(GL_UNIFORM_BUFFER, kCount * sizeof(data2[0]), data2.data(), GL_STATIC_DRAW);
// Set up a program that verifies the contents of uniform blocks.
constexpr char kVS[] = R"(#version 300 es
precision mediump float;
in vec4 position;
void main()
{
gl_Position = position;
})";
constexpr char kFS[] = R"(#version 300 es
precision mediump float;
uniform buf {
int data[64/4];
};
uniform int checkValue;
out vec4 outColor;
void main()
{
for (int i = 0; i < 64/4; ++i) {
if (data[i] != checkValue) {
outColor = vec4(1, 0, 0, 1);
return;
}
}
outColor = vec4(0, 1, 0, 1);
})";
ANGLE_GL_PROGRAM(program, kVS, kFS);
glUseProgram(program);
ASSERT_GL_NO_ERROR();
GLint uniLoc = glGetUniformLocation(program, "checkValue");
ASSERT_NE(-1, uniLoc);
GLuint blockIndex = glGetUniformBlockIndex(program, "buf");
ASSERT_NE(blockIndex, GL_INVALID_INDEX);
glUniformBlockBinding(program, blockIndex, 0);
ASSERT_GL_NO_ERROR();
// Set up the rest of the GL state.
auto quadVerts = GetQuadVertices();
GLBuffer vertexBuffer;
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, quadVerts.size() * sizeof(quadVerts[0]), quadVerts.data(),
GL_STATIC_DRAW);
GLint posLoc = glGetAttribLocation(program, "position");
ASSERT_NE(-1, posLoc);
glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(posLoc);
// Draw a few times with each UBO. Stream out one pixel for post-render verification.
constexpr int kIterations = 5;
constexpr GLsizei kPackBufferSize = sizeof(GLColor) * kIterations * 2;
GLBuffer packBuffer;
glBindBuffer(GL_PIXEL_PACK_BUFFER, packBuffer);
glBufferData(GL_PIXEL_PACK_BUFFER, kPackBufferSize, nullptr, GL_STREAM_READ);
GLsizei offset = 0;
uint32_t descriptorSetAllocationsBefore = 0;
for (int iteration = 0; iteration < kIterations; ++iteration)
{
glUniform1i(uniLoc, 1);
glBindBufferBase(GL_UNIFORM_BUFFER, 0, ubo1);
glDrawArrays(GL_TRIANGLES, 0, 6);
glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, reinterpret_cast<GLvoid *>(offset));
offset += sizeof(GLColor);
glUniform1i(uniLoc, 2);
glBindBufferBase(GL_UNIFORM_BUFFER, 0, ubo2);
glDrawArrays(GL_TRIANGLES, 0, 6);
glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, reinterpret_cast<GLvoid *>(offset));
offset += sizeof(GLColor);
// Capture the allocations counter after the first run.
if (iteration == 0)
{
descriptorSetAllocationsBefore = hackANGLE().descriptorSetAllocations;
}
}
ASSERT_GL_NO_ERROR();
// Verify correctness first.
std::vector<GLColor> expectedData(kIterations * 2, GLColor::green);
std::vector<GLColor> actualData(kIterations * 2, GLColor::black);
void *mapPtr = glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, kPackBufferSize, GL_MAP_READ_BIT);
ASSERT_NE(nullptr, mapPtr);
memcpy(actualData.data(), mapPtr, kPackBufferSize);
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
EXPECT_EQ(expectedData, actualData);
// Check for unnecessary descriptor set allocations.
uint32_t descriptorSetAllocationsAfter = hackANGLE().descriptorSetAllocations;
// TODO(jmadill): http://anglebug.com/5736 change to EXPECT_EQ.
EXPECT_NE(descriptorSetAllocationsBefore, descriptorSetAllocationsAfter);
}
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