Commit 8fde1151 by Shahbaz Youssefi Committed by Commit Bot

Vulkan: fix default uniform descriptor rebind on program change

When the program binding changes, we set the descriptor sets binding dirty bit if the program had any textures, UBOs, SSBOs, images or atomic counters. The check for default uniforms was missing. So if the two programs had no resources and were only using default uniforms, then drawing with one after the other didn't update the descriptor set binding of the default uniforms for the second draw. Bug: angleproject:4277 Change-Id: I631a1619658ee713484cfaee99fe1e39987e16e7 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1993408 Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: 's avatarCody Northrop <cnorthrop@google.com>
parent 5aed7c74
......@@ -1059,6 +1059,7 @@ ProgramState::ProgramState()
mAttachedShaders{},
mTransformFeedbackBufferMode(GL_INTERLEAVED_ATTRIBS),
mMaxActiveAttribLocation(0),
mDefaultUniformRange(0, 0),
mSamplerUniformRange(0, 0),
mImageUniformRange(0, 0),
mAtomicCounterUniformRange(0, 0),
......@@ -3614,6 +3615,9 @@ void Program::linkSamplerAndImageBindings(GLuint *combinedImageUniforms)
SamplerFormat format = samplerUniform.typeInfo->samplerFormat;
mState.mSamplerBindings.emplace_back(textureType, format, elementCount, false);
}
// Whatever is left constitutes the default uniforms.
mState.mDefaultUniformRange = RangeUI(0, low);
}
bool Program::linkAtomicCounterBuffers()
......@@ -5150,6 +5154,9 @@ void Program::serialize(const Context *context, angle::MemoryBuffer *binaryOut)
stream.writeInt(static_cast<int>(mState.mDrawBufferTypeMask.to_ulong()));
stream.writeInt(static_cast<int>(mState.mActiveOutputVariables.to_ulong()));
stream.writeInt(mState.getDefaultUniformRange().low());
stream.writeInt(mState.getDefaultUniformRange().high());
stream.writeInt(mState.getSamplerUniformRange().low());
stream.writeInt(mState.getSamplerUniformRange().high());
......@@ -5388,6 +5395,10 @@ angle::Result Program::deserialize(const Context *context,
mState.mDrawBufferTypeMask = gl::ComponentTypeMask(stream.readInt<uint32_t>());
mState.mActiveOutputVariables = stream.readInt<gl::DrawBufferMask>();
unsigned int defaultUniformRangeLow = stream.readInt<unsigned int>();
unsigned int defaultUniformRangeHigh = stream.readInt<unsigned int>();
mState.mDefaultUniformRange = RangeUI(defaultUniformRangeLow, defaultUniformRangeHigh);
unsigned int samplerRangeLow = stream.readInt<unsigned int>();
unsigned int samplerRangeHigh = stream.readInt<unsigned int>();
mState.mSamplerUniformRange = RangeUI(samplerRangeLow, samplerRangeHigh);
......
......@@ -346,6 +346,7 @@ class ProgramState final : angle::NonCopyable
const std::vector<SamplerBinding> &getSamplerBindings() const { return mSamplerBindings; }
const std::vector<ImageBinding> &getImageBindings() const { return mImageBindings; }
const sh::WorkGroupSize &getComputeShaderLocalSize() const { return mComputeShaderLocalSize; }
const RangeUI &getDefaultUniformRange() const { return mDefaultUniformRange; }
const RangeUI &getSamplerUniformRange() const { return mSamplerUniformRange; }
const RangeUI &getImageUniformRange() const { return mImageUniformRange; }
const RangeUI &getAtomicCounterUniformRange() const { return mAtomicCounterUniformRange; }
......@@ -455,6 +456,7 @@ class ProgramState final : angle::NonCopyable
std::vector<BufferVariable> mBufferVariables;
std::vector<InterfaceBlock> mShaderStorageBlocks;
std::vector<AtomicCounterBuffer> mAtomicCounterBuffers;
RangeUI mDefaultUniformRange;
RangeUI mSamplerUniformRange;
RangeUI mImageUniformRange;
RangeUI mAtomicCounterUniformRange;
......
......@@ -2445,6 +2445,7 @@ angle::Result ContextVk::syncState(const gl::Context *context,
break;
case gl::State::DIRTY_BIT_PROGRAM_EXECUTABLE:
{
invalidateCurrentDefaultUniforms();
invalidateCurrentTextures();
invalidateCurrentShaderResources();
if (glState.getProgram()->isCompute())
......@@ -2699,6 +2700,16 @@ OverlayImpl *ContextVk::createOverlay(const gl::OverlayState &state)
return new OverlayVk(state);
}
void ContextVk::invalidateCurrentDefaultUniforms()
{
ASSERT(mProgram);
if (mProgram->hasDefaultUniforms())
{
mGraphicsDirtyBits.set(DIRTY_BIT_DESCRIPTOR_SETS);
mComputeDirtyBits.set(DIRTY_BIT_DESCRIPTOR_SETS);
}
}
void ContextVk::invalidateCurrentTextures()
{
ASSERT(mProgram);
......
......@@ -585,6 +585,7 @@ class ContextVk : public ContextImpl, public vk::Context, public vk::RenderPassO
mCurrentComputePipeline = nullptr;
}
void invalidateCurrentDefaultUniforms();
void invalidateCurrentTextures();
void invalidateCurrentShaderResources();
void invalidateGraphicsDriverUniforms();
......
......@@ -119,6 +119,7 @@ class ProgramVk : public ProgramImpl
const vk::PipelineLayout &getPipelineLayout() const { return mPipelineLayout.get(); }
bool hasDefaultUniforms() const { return !mState.getDefaultUniformRange().empty(); }
bool hasTextures() const { return !mState.getSamplerBindings().empty(); }
bool hasUniformBuffers() const { return !mState.getUniformBlocks().empty(); }
bool hasStorageBuffers() const { return !mState.getShaderStorageBlocks().empty(); }
......
......@@ -4553,6 +4553,67 @@ void main()
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
}
// Test that switching between programs that only contain default uniforms is correct.
TEST_P(SimpleStateChangeTest, TwoProgramsWithOnlyDefaultUniforms)
{
constexpr char kVS[] = R"(attribute vec4 a_position;
varying float v_attrib;
uniform float u_value;
void main()
{
v_attrib = u_value;
gl_Position = a_position;
})";
constexpr char kFS[] = R"(precision mediump float;
varying float v_attrib;
void main()
{
gl_FragColor = vec4(v_attrib, 0, 0, 1);
})";
ANGLE_GL_PROGRAM(program1, kVS, kFS);
ANGLE_GL_PROGRAM(program2, kVS, kFS);
// Don't use drawQuad so there's no state changes between the draw calls other than the program
// binding.
constexpr size_t kProgramCount = 2;
GLuint programs[kProgramCount] = {program1, program2};
for (size_t i = 0; i < kProgramCount; ++i)
{
glUseProgram(programs[i]);
GLint uniformLoc = glGetUniformLocation(programs[i], "u_value");
ASSERT_NE(uniformLoc, -1);
glUniform1f(uniformLoc, static_cast<float>(i + 1) / static_cast<float>(kProgramCount));
// Ensure position is at location 0 in both programs.
GLint positionLocation = glGetAttribLocation(programs[i], "a_position");
ASSERT_EQ(positionLocation, 0);
}
ASSERT_GL_NO_ERROR();
std::array<Vector3, 6> quadVertices = GetQuadVertices();
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, quadVertices.data());
glEnableVertexAttribArray(0);
// Draw once with each so their uniforms are updated.
// The first draw will clear the screen to 255, 0, 0, 255
glUseProgram(program2);
glDrawArrays(GL_TRIANGLES, 0, 6);
// The second draw will clear the screen to 127, 0, 0, 255
glUseProgram(program1);
glDrawArrays(GL_TRIANGLES, 0, 6);
// Draw with the previous program again, to make sure its default uniforms are bound again.
glUseProgram(program2);
glDrawArrays(GL_TRIANGLES, 0, 6);
// Verify red was drawn
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
}
// Validates GL_RASTERIZER_DISCARD state is tracked correctly
TEST_P(SimpleStateChangeTestES3, RasterizerDiscardState)
{
......
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