Commit 22e1f3e2 by Charlie Lao Committed by Commit Bot

Vulkan: Let all shader stages share one buffer for default uniform

Right now each shader stage has its own vk::DynamicBuffer for default uniform storage. This is less efficient than just share one buffer. This CL moves the storage from per shader stage into its ProgramVk object. Bug: b/159457348 Change-Id: If47248ea23c4e48407d3b211583ae2b048d4d10f Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2265281 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 4e7a6a69
...@@ -1472,7 +1472,8 @@ angle::Result ContextVk::handleDirtyGraphicsTransformFeedbackBuffersEmulation( ...@@ -1472,7 +1472,8 @@ angle::Result ContextVk::handleDirtyGraphicsTransformFeedbackBuffersEmulation(
// TODO(http://anglebug.com/3570): Need to update to handle Program Pipelines // TODO(http://anglebug.com/3570): Need to update to handle Program Pipelines
return mProgram->getExecutable().updateTransformFeedbackDescriptorSet( return mProgram->getExecutable().updateTransformFeedbackDescriptorSet(
mProgram->getState(), mProgram->getDefaultUniformBlocks(), this); mProgram->getState(), mProgram->getDefaultUniformBlocks(),
mProgram->getDefaultUniformBuffer(), this);
} }
angle::Result ContextVk::handleDirtyGraphicsTransformFeedbackBuffersExtension( angle::Result ContextVk::handleDirtyGraphicsTransformFeedbackBuffersExtension(
......
...@@ -853,6 +853,7 @@ angle::Result ProgramExecutableVk::createPipelineLayout(const gl::Context *glCon ...@@ -853,6 +853,7 @@ angle::Result ProgramExecutableVk::createPipelineLayout(const gl::Context *glCon
void ProgramExecutableVk::updateDefaultUniformsDescriptorSet( void ProgramExecutableVk::updateDefaultUniformsDescriptorSet(
const gl::ShaderType shaderType, const gl::ShaderType shaderType,
gl::ShaderMap<DefaultUniformBlock> &defaultUniformBlocks, gl::ShaderMap<DefaultUniformBlock> &defaultUniformBlocks,
vk::BufferHelper *defaultUniformBuffer,
ContextVk *contextVk) ContextVk *contextVk)
{ {
const std::string uniformBlockName = kDefaultUniformNames[shaderType]; const std::string uniformBlockName = kDefaultUniformNames[shaderType];
...@@ -868,9 +869,8 @@ void ProgramExecutableVk::updateDefaultUniformsDescriptorSet( ...@@ -868,9 +869,8 @@ void ProgramExecutableVk::updateDefaultUniformsDescriptorSet(
if (!uniformBlock.uniformData.empty()) if (!uniformBlock.uniformData.empty())
{ {
vk::BufferHelper *bufferHelper = uniformBlock.storage.getCurrentBuffer(); bufferInfo.buffer = defaultUniformBuffer->getBuffer().getHandle();
bufferInfo.buffer = bufferHelper->getBuffer().getHandle(); mDescriptorBuffersCache.emplace_back(defaultUniformBuffer);
mDescriptorBuffersCache.emplace_back(bufferHelper);
} }
else else
{ {
...@@ -1186,6 +1186,7 @@ angle::Result ProgramExecutableVk::updateShaderResourcesDescriptorSet( ...@@ -1186,6 +1186,7 @@ angle::Result ProgramExecutableVk::updateShaderResourcesDescriptorSet(
angle::Result ProgramExecutableVk::updateTransformFeedbackDescriptorSet( angle::Result ProgramExecutableVk::updateTransformFeedbackDescriptorSet(
const gl::ProgramState &programState, const gl::ProgramState &programState,
gl::ShaderMap<DefaultUniformBlock> &defaultUniformBlocks, gl::ShaderMap<DefaultUniformBlock> &defaultUniformBlocks,
vk::BufferHelper *defaultUniformBuffer,
ContextVk *contextVk) ContextVk *contextVk)
{ {
const gl::ProgramExecutable &executable = programState.getExecutable(); const gl::ProgramExecutable &executable = programState.getExecutable();
...@@ -1196,7 +1197,8 @@ angle::Result ProgramExecutableVk::updateTransformFeedbackDescriptorSet( ...@@ -1196,7 +1197,8 @@ angle::Result ProgramExecutableVk::updateTransformFeedbackDescriptorSet(
mDescriptorBuffersCache.clear(); mDescriptorBuffersCache.clear();
for (const gl::ShaderType shaderType : executable.getLinkedShaderStages()) for (const gl::ShaderType shaderType : executable.getLinkedShaderStages())
{ {
updateDefaultUniformsDescriptorSet(shaderType, defaultUniformBlocks, contextVk); updateDefaultUniformsDescriptorSet(shaderType, defaultUniformBlocks, defaultUniformBuffer,
contextVk);
} }
updateTransformFeedbackDescriptorSetImpl(programState, contextVk); updateTransformFeedbackDescriptorSetImpl(programState, contextVk);
......
...@@ -86,8 +86,6 @@ struct DefaultUniformBlock final : private angle::NonCopyable ...@@ -86,8 +86,6 @@ struct DefaultUniformBlock final : private angle::NonCopyable
DefaultUniformBlock(); DefaultUniformBlock();
~DefaultUniformBlock(); ~DefaultUniformBlock();
vk::DynamicBuffer storage;
// Shadow copies of the shader uniform data. // Shadow copies of the shader uniform data.
angle::MemoryBuffer uniformData; angle::MemoryBuffer uniformData;
...@@ -147,6 +145,7 @@ class ProgramExecutableVk ...@@ -147,6 +145,7 @@ class ProgramExecutableVk
angle::Result updateTransformFeedbackDescriptorSet( angle::Result updateTransformFeedbackDescriptorSet(
const gl::ProgramState &programState, const gl::ProgramState &programState,
gl::ShaderMap<DefaultUniformBlock> &defaultUniformBlocks, gl::ShaderMap<DefaultUniformBlock> &defaultUniformBlocks,
vk::BufferHelper *defaultUniformBuffer,
ContextVk *contextVk); ContextVk *contextVk);
angle::Result updateDescriptorSets(ContextVk *contextVk, vk::CommandBuffer *commandBuffer); angle::Result updateDescriptorSets(ContextVk *contextVk, vk::CommandBuffer *commandBuffer);
...@@ -190,6 +189,7 @@ class ProgramExecutableVk ...@@ -190,6 +189,7 @@ class ProgramExecutableVk
void updateDefaultUniformsDescriptorSet( void updateDefaultUniformsDescriptorSet(
const gl::ShaderType shaderType, const gl::ShaderType shaderType,
gl::ShaderMap<DefaultUniformBlock> &defaultUniformBlocks, gl::ShaderMap<DefaultUniformBlock> &defaultUniformBlocks,
vk::BufferHelper *defaultUniformBuffer,
ContextVk *contextVk); ContextVk *contextVk);
void updateTransformFeedbackDescriptorSetImpl(const gl::ProgramState &programState, void updateTransformFeedbackDescriptorSetImpl(const gl::ProgramState &programState,
ContextVk *contextVk); ContextVk *contextVk);
......
...@@ -123,7 +123,8 @@ angle::Result ProgramPipelineVk::updateUniforms(ContextVk *contextVk) ...@@ -123,7 +123,8 @@ angle::Result ProgramPipelineVk::updateUniforms(ContextVk *contextVk)
if (programVk) if (programVk)
{ {
mExecutable.updateDefaultUniformsDescriptorSet( mExecutable.updateDefaultUniformsDescriptorSet(
shaderType, programVk->getDefaultUniformBlocks(), contextVk); shaderType, programVk->getDefaultUniformBlocks(),
programVk->getDefaultUniformBuffer(), contextVk);
mExecutable.updateTransformFeedbackDescriptorSetImpl(programVk->getState(), mExecutable.updateTransformFeedbackDescriptorSetImpl(programVk->getState(),
contextVk); contextVk);
} }
......
...@@ -181,10 +181,7 @@ void ProgramVk::reset(ContextVk *contextVk) ...@@ -181,10 +181,7 @@ void ProgramVk::reset(ContextVk *contextVk)
mOriginalShaderInfo.release(contextVk); mOriginalShaderInfo.release(contextVk);
for (auto &uniformBlock : mDefaultUniformBlocks) mDefaultUniformStorage.release(renderer);
{
uniformBlock.storage.release(renderer);
}
GlslangWrapperVk::ResetGlslangProgramInterfaceInfo(&mGlslangProgramInterfaceInfo); GlslangWrapperVk::ResetGlslangProgramInterfaceInfo(&mGlslangProgramInterfaceInfo);
...@@ -426,12 +423,6 @@ angle::Result ProgramVk::resizeUniformBlockMemory(ContextVk *contextVk, ...@@ -426,12 +423,6 @@ angle::Result ProgramVk::resizeUniformBlockMemory(ContextVk *contextVk,
{ {
ANGLE_VK_CHECK(contextVk, false, VK_ERROR_OUT_OF_HOST_MEMORY); ANGLE_VK_CHECK(contextVk, false, VK_ERROR_OUT_OF_HOST_MEMORY);
} }
size_t minAlignment = static_cast<size_t>(
renderer->getPhysicalDeviceProperties().limits.minUniformBufferOffsetAlignment);
mDefaultUniformBlocks[shaderType].storage.init(
renderer, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
minAlignment, kUniformBlockDynamicBufferMinSize, true);
// Initialize uniform buffer memory to zero by default. // Initialize uniform buffer memory to zero by default.
mDefaultUniformBlocks[shaderType].uniformData.fill(0); mDefaultUniformBlocks[shaderType].uniformData.fill(0);
...@@ -439,6 +430,12 @@ angle::Result ProgramVk::resizeUniformBlockMemory(ContextVk *contextVk, ...@@ -439,6 +430,12 @@ angle::Result ProgramVk::resizeUniformBlockMemory(ContextVk *contextVk,
} }
} }
size_t minAlignment = static_cast<size_t>(
renderer->getPhysicalDeviceProperties().limits.minUniformBufferOffsetAlignment);
mDefaultUniformStorage.init(
renderer, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
minAlignment, kUniformBlockDynamicBufferMinSize, true);
return angle::Result::Continue; return angle::Result::Continue;
} }
...@@ -739,7 +736,7 @@ angle::Result ProgramVk::updateShaderUniforms(ContextVk *contextVk, ...@@ -739,7 +736,7 @@ angle::Result ProgramVk::updateShaderUniforms(ContextVk *contextVk,
if (mDefaultUniformBlocksDirty[shaderType]) if (mDefaultUniformBlocksDirty[shaderType])
{ {
bool bufferModified = false; bool bufferModified = false;
ANGLE_TRY(SyncDefaultUniformBlock(contextVk, &uniformBlock.storage, ANGLE_TRY(SyncDefaultUniformBlock(contextVk, &mDefaultUniformStorage,
uniformBlock.uniformData, outOffset, &bufferModified)); uniformBlock.uniformData, outOffset, &bufferModified));
mDefaultUniformBlocksDirty.reset(shaderType); mDefaultUniformBlocksDirty.reset(shaderType);
...@@ -752,22 +749,75 @@ angle::Result ProgramVk::updateShaderUniforms(ContextVk *contextVk, ...@@ -752,22 +749,75 @@ angle::Result ProgramVk::updateShaderUniforms(ContextVk *contextVk,
return angle::Result::Continue; return angle::Result::Continue;
} }
size_t ProgramVk::calcUniformUpdateRequiredSpace(ContextVk *contextVk,
const gl::ProgramExecutable &glExecutable,
gl::ShaderMap<VkDeviceSize> &uniformOffsets) const
{
size_t requiredSpace = 0;
for (const gl::ShaderType shaderType : glExecutable.getLinkedShaderStages())
{
if (mDefaultUniformBlocksDirty[shaderType])
{
uniformOffsets[shaderType] = requiredSpace;
requiredSpace += getDefaultUniformAlignedSize(contextVk, shaderType);
}
}
return requiredSpace;
}
angle::Result ProgramVk::updateUniforms(ContextVk *contextVk) angle::Result ProgramVk::updateUniforms(ContextVk *contextVk)
{ {
ASSERT(dirtyUniforms()); ASSERT(dirtyUniforms());
bool anyNewBufferAllocated = false; bool anyNewBufferAllocated = false;
uint8_t *bufferData = nullptr;
VkDeviceSize bufferOffset = 0;
uint32_t offsetIndex = 0; uint32_t offsetIndex = 0;
const gl::ProgramExecutable &glExecutable = mState.getExecutable(); const gl::ProgramExecutable &glExecutable = mState.getExecutable();
gl::ShaderMap<VkDeviceSize> offsets;
size_t requiredSpace;
// We usually only update uniform data for shader stages that are actually dirty. But when the
// buffer for uniform data have switched, because all shader stages are using the same buffer,
// we then must update uniform data for all shader stages to keep all shader stages' uniform
// data in the same buffer.
requiredSpace = calcUniformUpdateRequiredSpace(contextVk, glExecutable, offsets);
ASSERT(requiredSpace > 0);
// Allocate space from dynamicBuffer. Always try to allocate from the current buffer first.
// If that failed, we deal with fall out and try again.
if (!mDefaultUniformStorage.allocateFromCurrentBuffer(requiredSpace, &bufferData,
&bufferOffset))
{
for (const gl::ShaderType shaderType : glExecutable.getLinkedShaderStages())
{
if (!mDefaultUniformBlocks[shaderType].uniformData.empty())
{
mDefaultUniformBlocksDirty.set(shaderType);
}
}
mDefaultUniformStorage.releaseInFlightBuffersToResourceUseList(contextVk);
requiredSpace = calcUniformUpdateRequiredSpace(contextVk, glExecutable, offsets);
ANGLE_TRY(mDefaultUniformStorage.allocate(contextVk, requiredSpace, &bufferData, nullptr,
&bufferOffset, &anyNewBufferAllocated));
}
// Update buffer memory by immediate mapping. This immediate update only works once. // Update buffer memory by immediate mapping. This immediate update only works once.
for (const gl::ShaderType shaderType : glExecutable.getLinkedShaderStages()) for (const gl::ShaderType shaderType : glExecutable.getLinkedShaderStages())
{ {
ANGLE_TRY(updateShaderUniforms(contextVk, shaderType, if (mDefaultUniformBlocksDirty[shaderType])
&mExecutable.mDynamicBufferOffsets[offsetIndex], {
&anyNewBufferAllocated)); const angle::MemoryBuffer &uniformData = mDefaultUniformBlocks[shaderType].uniformData;
memcpy(&bufferData[offsets[shaderType]], uniformData.data(), uniformData.size());
mExecutable.mDynamicBufferOffsets[offsetIndex] =
static_cast<uint32_t>(bufferOffset + offsets[shaderType]);
mDefaultUniformBlocksDirty.reset(shaderType);
}
++offsetIndex; ++offsetIndex;
} }
ANGLE_TRY(mDefaultUniformStorage.flush(contextVk));
if (anyNewBufferAllocated) if (anyNewBufferAllocated)
{ {
...@@ -778,10 +828,11 @@ angle::Result ProgramVk::updateUniforms(ContextVk *contextVk) ...@@ -778,10 +828,11 @@ angle::Result ProgramVk::updateUniforms(ContextVk *contextVk)
mExecutable.mDescriptorBuffersCache.clear(); mExecutable.mDescriptorBuffersCache.clear();
for (const gl::ShaderType shaderType : glExecutable.getLinkedShaderStages()) for (const gl::ShaderType shaderType : glExecutable.getLinkedShaderStages())
{ {
mExecutable.updateDefaultUniformsDescriptorSet(shaderType, mDefaultUniformBlocks, mExecutable.updateDefaultUniformsDescriptorSet(
shaderType, mDefaultUniformBlocks, mDefaultUniformStorage.getCurrentBuffer(),
contextVk); contextVk);
mExecutable.updateTransformFeedbackDescriptorSetImpl(mState, contextVk);
} }
mExecutable.updateTransformFeedbackDescriptorSetImpl(mState, contextVk);
} }
return angle::Result::Continue; return angle::Result::Continue;
...@@ -789,10 +840,7 @@ angle::Result ProgramVk::updateUniforms(ContextVk *contextVk) ...@@ -789,10 +840,7 @@ angle::Result ProgramVk::updateUniforms(ContextVk *contextVk)
void ProgramVk::setDefaultUniformBlocksMinSizeForTesting(size_t minSize) void ProgramVk::setDefaultUniformBlocksMinSizeForTesting(size_t minSize)
{ {
for (DefaultUniformBlock &block : mDefaultUniformBlocks) mDefaultUniformStorage.setMinimumSizeForTesting(minSize);
{
block.storage.setMinimumSizeForTesting(minSize);
}
} }
} // namespace rx } // namespace rx
...@@ -123,6 +123,21 @@ class ProgramVk : public ProgramImpl ...@@ -123,6 +123,21 @@ class ProgramVk : public ProgramImpl
ProgramExecutableVk &getExecutable() { return mExecutable; } ProgramExecutableVk &getExecutable() { return mExecutable; }
gl::ShaderMap<DefaultUniformBlock> &getDefaultUniformBlocks() { return mDefaultUniformBlocks; } gl::ShaderMap<DefaultUniformBlock> &getDefaultUniformBlocks() { return mDefaultUniformBlocks; }
vk::BufferHelper *getDefaultUniformBuffer() const
{
return mDefaultUniformStorage.getCurrentBuffer();
}
size_t getDefaultUniformAlignedSize(ContextVk *contextVk, const gl::ShaderType shaderType) const
{
RendererVk *renderer = contextVk->getRenderer();
size_t alignment = static_cast<size_t>(
renderer->getPhysicalDeviceProperties().limits.minUniformBufferOffsetAlignment);
return roundUp(mDefaultUniformBlocks[shaderType].uniformData.size(), alignment);
}
size_t calcUniformUpdateRequiredSpace(ContextVk *contextVk,
const gl::ProgramExecutable &glExecutable,
gl::ShaderMap<VkDeviceSize> &uniformOffsets) const;
ANGLE_INLINE angle::Result initGraphicsShaderProgram(ContextVk *contextVk, ANGLE_INLINE angle::Result initGraphicsShaderProgram(ContextVk *contextVk,
const gl::ShaderType shaderType, const gl::ShaderType shaderType,
...@@ -193,6 +208,7 @@ class ProgramVk : public ProgramImpl ...@@ -193,6 +208,7 @@ class ProgramVk : public ProgramImpl
gl::ShaderMap<DefaultUniformBlock> mDefaultUniformBlocks; gl::ShaderMap<DefaultUniformBlock> mDefaultUniformBlocks;
gl::ShaderBitSet mDefaultUniformBlocksDirty; gl::ShaderBitSet mDefaultUniformBlocksDirty;
vk::DynamicBuffer mDefaultUniformStorage;
// We keep the SPIR-V code to use for draw call pipeline creation. // We keep the SPIR-V code to use for draw call pipeline creation.
ShaderInfo mOriginalShaderInfo; ShaderInfo mOriginalShaderInfo;
......
...@@ -972,6 +972,32 @@ angle::Result DynamicBuffer::allocateNewBuffer(ContextVk *contextVk) ...@@ -972,6 +972,32 @@ angle::Result DynamicBuffer::allocateNewBuffer(ContextVk *contextVk)
return angle::Result::Continue; return angle::Result::Continue;
} }
bool DynamicBuffer::allocateFromCurrentBuffer(size_t sizeInBytes,
uint8_t **ptrOut,
VkDeviceSize *offsetOut)
{
ASSERT(ptrOut);
ASSERT(offsetOut);
size_t sizeToAllocate = roundUp(sizeInBytes, mAlignment);
angle::base::CheckedNumeric<size_t> checkedNextWriteOffset = mNextAllocationOffset;
checkedNextWriteOffset += sizeToAllocate;
if (!checkedNextWriteOffset.IsValid() || checkedNextWriteOffset.ValueOrDie() >= mSize)
{
return false;
}
ASSERT(mBuffer != nullptr);
ASSERT(mHostVisible);
ASSERT(mBuffer->getMappedMemory());
*ptrOut = mBuffer->getMappedMemory() + mNextAllocationOffset;
*offsetOut = static_cast<VkDeviceSize>(mNextAllocationOffset);
mNextAllocationOffset += static_cast<uint32_t>(sizeToAllocate);
return true;
}
angle::Result DynamicBuffer::allocate(ContextVk *contextVk, angle::Result DynamicBuffer::allocate(ContextVk *contextVk,
size_t sizeInBytes, size_t sizeInBytes,
uint8_t **ptrOut, uint8_t **ptrOut,
......
...@@ -79,6 +79,11 @@ class DynamicBuffer : angle::NonCopyable ...@@ -79,6 +79,11 @@ class DynamicBuffer : angle::NonCopyable
size_t initialSize, size_t initialSize,
VkMemoryPropertyFlags memoryProperty); VkMemoryPropertyFlags memoryProperty);
// This call will allocate a new region at the end of the current buffer. If it can't find
// enough space in the current buffer, it returns false. This gives caller a chance to deal with
// buffer switch that may occur with allocate call.
bool allocateFromCurrentBuffer(size_t sizeInBytes, uint8_t **ptrOut, VkDeviceSize *offsetOut);
// This call will allocate a new region at the end of the buffer. It internally may trigger // This call will allocate a new region at the end of the buffer. It internally may trigger
// a new buffer to be created (which is returned in the optional parameter // a new buffer to be created (which is returned in the optional parameter
// `newBufferAllocatedOut`). The new region will be in the returned buffer at given offset. If // `newBufferAllocatedOut`). The new region will be in the returned buffer at given offset. If
...@@ -108,7 +113,7 @@ class DynamicBuffer : angle::NonCopyable ...@@ -108,7 +113,7 @@ class DynamicBuffer : angle::NonCopyable
// This frees resources immediately. // This frees resources immediately.
void destroy(RendererVk *renderer); void destroy(RendererVk *renderer);
BufferHelper *getCurrentBuffer() { return mBuffer; } BufferHelper *getCurrentBuffer() const { return mBuffer; }
// **Accumulate** an alignment requirement. A dynamic buffer is used as the staging buffer for // **Accumulate** an alignment requirement. A dynamic buffer is used as the staging buffer for
// image uploads, which can contain updates to unrelated mips, possibly with different formats. // image uploads, which can contain updates to unrelated mips, possibly with different formats.
...@@ -702,7 +707,7 @@ class BufferHelper final : public Resource ...@@ -702,7 +707,7 @@ class BufferHelper final : public Resource
bool valid() const { return mBuffer.valid(); } bool valid() const { return mBuffer.valid(); }
const Buffer &getBuffer() const { return mBuffer; } const Buffer &getBuffer() const { return mBuffer; }
VkDeviceSize getSize() const { return mSize; } VkDeviceSize getSize() const { return mSize; }
const uint8_t *getMappedMemory() const uint8_t *getMappedMemory() const
{ {
ASSERT(isMapped()); ASSERT(isMapped());
return mMappedMemory; return mMappedMemory;
......
...@@ -475,6 +475,228 @@ TEST_P(VulkanUniformUpdatesTest, TextureStagingBufferRecycling) ...@@ -475,6 +475,228 @@ TEST_P(VulkanUniformUpdatesTest, TextureStagingBufferRecycling)
} }
} }
// This test tries to create a situation that VS and FS's uniform data might get placed in
// different buffers and verify uniforms not getting stale data.
TEST_P(VulkanUniformUpdatesTest, UpdateAfterNewBufferIsAllocated)
{
ASSERT_TRUE(IsVulkan());
constexpr char kPositionUniformVertexShader[] = R"(attribute vec2 position;
uniform float uniformVS;
varying vec4 outVS;
void main()
{
outVS = vec4(uniformVS, uniformVS, uniformVS, uniformVS);
gl_Position = vec4(position, 0, 1);
})";
constexpr char kColorUniformFragmentShader[] = R"(precision mediump float;
varying vec4 outVS;
uniform float uniformFS;
void main()
{
if(outVS[0] > uniformFS)
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
else
gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
})";
ANGLE_GL_PROGRAM(program, kPositionUniformVertexShader, kColorUniformFragmentShader);
glUseProgram(program);
limitMaxSets(program);
rx::ProgramVk *programVk = hackProgram(program);
// Set a really small min size so that every uniform update actually allocates a new buffer.
programVk->setDefaultUniformBlocksMinSizeForTesting(128);
GLint uniformVSLocation = glGetUniformLocation(program, "uniformVS");
ASSERT_NE(uniformVSLocation, -1);
GLint uniformFSLocation = glGetUniformLocation(program, "uniformFS");
ASSERT_NE(uniformFSLocation, -1);
glUniform1f(uniformVSLocation, 10.0);
glUniform1f(uniformFSLocation, 11.0);
drawQuad(program, "position", 0.5f, 1.0f);
ASSERT_GL_NO_ERROR();
const GLsizei kHalfX = getWindowWidth() / 2;
const GLsizei kHalfY = getWindowHeight() / 2;
const GLsizei xoffset = kHalfX;
const GLsizei yoffset = kHalfY;
// 10.0f < 11.0f, should see green
EXPECT_PIXEL_RECT_EQ(xoffset, yoffset, kHalfX, kHalfY, GLColor::green);
// Now only update FS's uniform
for (int i = 0; i < 3; i++)
{
glUniform1f(uniformFSLocation, 1.0f + i / 10.0f);
drawQuad(program, "position", 0.5f, 1.0f);
ASSERT_GL_NO_ERROR();
}
// 10.0f > 9.0f, should see red
EXPECT_PIXEL_RECT_EQ(xoffset, yoffset, kHalfX, kHalfY, GLColor::red);
// 10.0f < 11.0f, should see green again
glUniform1f(uniformFSLocation, 11.0f);
drawQuad(program, "position", 0.5f, 1.0f);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_RECT_EQ(xoffset, yoffset, kHalfX, kHalfY, GLColor::green);
// Now only update VS's uniform and flush the draw and readback and verify for every iteration.
// This will ensure the old buffers are finished and possibly recycled.
for (int i = 0; i < 100; i++)
{
// Make VS uniform value ping pong across FS uniform value
float vsUniformValue = (i % 2) == 0 ? (11.0 + (i - 50)) : (11.0 - (i - 50));
GLColor expectedColor = vsUniformValue > 11.0f ? GLColor::red : GLColor::green;
glUniform1f(uniformVSLocation, vsUniformValue);
drawQuad(program, "position", 0.5f, 1.0f);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_RECT_EQ(xoffset, yoffset, kHalfX, kHalfY, expectedColor);
}
}
ANGLE_INSTANTIATE_TEST(VulkanUniformUpdatesTest, ES2_VULKAN(), ES3_VULKAN()); ANGLE_INSTANTIATE_TEST(VulkanUniformUpdatesTest, ES2_VULKAN(), ES3_VULKAN());
// This test tries to test uniform data update while switching between PPO and monolithic program.
// The uniform data update occurred on one should carry over to the other. Also buffers are hacked
// to smallest size to force updates occur in the new buffer so that any bug related to buffer
// recycling will be exposed.
class PipelineProgramUniformUpdatesTest : public VulkanUniformUpdatesTest
{};
TEST_P(PipelineProgramUniformUpdatesTest, ToggleBetweenPPOAndProgramVKWithUniformUpdate)
{
ASSERT_TRUE(IsVulkan());
ANGLE_SKIP_TEST_IF(true && "Known bug. Expected to be fixed by http://b/161391337");
const GLchar *kPositionUniformVertexShader = R"(attribute vec2 position;
uniform float uniformVS;
varying vec4 outVS;
void main()
{
outVS = vec4(uniformVS, uniformVS, uniformVS, uniformVS);
gl_Position = vec4(position, 0, 1);
})";
const GLchar *kColorUniformFragmentShader = R"(precision mediump float;
varying vec4 outVS;
uniform float uniformFS;
void main()
{
if(outVS[0] > uniformFS)
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
else
gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
})";
// Compile and link a separable vertex shader
GLShader vertShader(GL_VERTEX_SHADER);
glShaderSource(vertShader, 1, &kPositionUniformVertexShader, nullptr);
glCompileShader(vertShader);
GLShader fragShader(GL_FRAGMENT_SHADER);
glShaderSource(fragShader, 1, &kColorUniformFragmentShader, nullptr);
glCompileShader(fragShader);
GLuint program = glCreateProgram();
glProgramParameteri(program, GL_PROGRAM_SEPARABLE, GL_TRUE);
glAttachShader(program, vertShader);
glAttachShader(program, fragShader);
glLinkProgram(program);
EXPECT_GL_NO_ERROR();
glUseProgram(program);
limitMaxSets(program);
// Set a really small min size so that every uniform update actually allocates a new buffer.
rx::ProgramVk *programVk = hackProgram(program);
programVk->setDefaultUniformBlocksMinSizeForTesting(128);
// Setup vertices
std::array<Vector3, 6> quadVertices = ANGLETestBase::GetQuadVertices();
GLint positionLocation = glGetAttribLocation(program, "position");
glBindBuffer(GL_ARRAY_BUFFER, 0);
glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, quadVertices.data());
glEnableVertexAttribArray(positionLocation);
GLint uniformVSLocation = glGetUniformLocation(program, "uniformVS");
ASSERT_NE(uniformVSLocation, -1);
GLint uniformFSLocation = glGetUniformLocation(program, "uniformFS");
ASSERT_NE(uniformFSLocation, -1);
glUseProgram(0);
// Generate a pipeline program out of the monolithic program
GLuint pipeline;
glGenProgramPipelines(1, &pipeline);
EXPECT_GL_NO_ERROR();
glUseProgramStages(pipeline, GL_VERTEX_SHADER_BIT | GL_FRAGMENT_SHADER_BIT, program);
EXPECT_GL_NO_ERROR();
glBindProgramPipeline(pipeline);
EXPECT_GL_NO_ERROR();
// First use monolithic program and update uniforms
glUseProgram(program);
glUniform1f(uniformVSLocation, 10.0);
glUniform1f(uniformFSLocation, 11.0);
glDrawArrays(GL_TRIANGLES, 0, 6);
ASSERT_GL_NO_ERROR();
const GLsizei kHalfX = getWindowWidth() / 2;
const GLsizei kHalfY = getWindowHeight() / 2;
const GLsizei xoffset = kHalfX;
const GLsizei yoffset = kHalfY;
// 10.0f < 11.0f, should see green
EXPECT_PIXEL_RECT_EQ(xoffset, yoffset, kHalfX, kHalfY, GLColor::green);
// Now use PPO and only update FS's uniform
glUseProgram(0);
for (int i = 0; i < 3; i++)
{
glActiveShaderProgram(pipeline, program);
glUniform1f(uniformFSLocation, 1.0f + i / 10.0f);
glDrawArrays(GL_TRIANGLES, 0, 6);
ASSERT_GL_NO_ERROR();
}
// 10.0f > 9.0f, should see red
EXPECT_PIXEL_RECT_EQ(xoffset, yoffset, kHalfX, kHalfY, GLColor::red);
// Now switch back to monolithic program and only update FS's uniform.
// 10.0f < 11.0f, should see green again
glUseProgram(program);
glUniform1f(uniformFSLocation, 11.0f);
glDrawArrays(GL_TRIANGLES, 0, 6);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_RECT_EQ(xoffset, yoffset, kHalfX, kHalfY, GLColor::green);
// Now only update VS's uniform and flush the draw and readback and verify for every iteration.
// This will ensure the old buffers are finished and possibly recycled.
for (int i = 0; i < 100; i++)
{
bool iteration_even = (i % 2) == 0 ? true : false;
float vsUniformValue;
// Make VS uniform value ping pong across FS uniform value and also pin pong between
// monolithic program and PPO
if (iteration_even)
{
vsUniformValue = 11.0 + (i - 50);
glUseProgram(program);
glUniform1f(uniformVSLocation, vsUniformValue);
}
else
{
vsUniformValue = 11.0 - (i - 50);
glUseProgram(0);
glActiveShaderProgram(pipeline, program);
glUniform1f(uniformVSLocation, vsUniformValue);
}
GLColor expectedColor = vsUniformValue > 11.0f ? GLColor::red : GLColor::green;
glDrawArrays(GL_TRIANGLES, 0, 6);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_RECT_EQ(xoffset, yoffset, kHalfX, kHalfY, expectedColor);
}
}
ANGLE_INSTANTIATE_TEST(PipelineProgramUniformUpdatesTest, ES31_VULKAN());
} // anonymous namespace } // anonymous namespace
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