Commit b3545b42 by Tim Van Patten Committed by Commit Bot

Get storage buffers/images from ProgramExecutable

Update StateCache::updateActiveShaderStorageBufferIndices() and StateCache::updateActiveImageUnitIndices to get the storage buffers and image bindings from the ProgramExecutable, respectively. This requires updating the ProgramPipeline's ProgramExecutable to build up its vector of buffers/images from each Program's ProgramExecutable. Bug: angleproject:4869 Test: VertexAttributeTestES31.UsePpoComputeShaderToUpdateVertexBuffer Test: SimpleStateChangeTestES31.InvalidateThenStorageWriteThenBlendPpo Change-Id: I68b7d23eedda910c3dcbf5f9c50b74b5e80134d8 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2327701 Commit-Queue: Tim Van Patten <timvp@google.com> Reviewed-by: 's avatarShahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: 's avatarCharlie Lao <cclao@google.com>
parent 50442fac
......@@ -5678,6 +5678,10 @@ void Context::dispatchCompute(GLuint numGroupsX, GLuint numGroupsY, GLuint numGr
angle::Result result =
mImplementation->dispatchCompute(this, numGroupsX, numGroupsY, numGroupsZ);
// This must be called before convertPpoToComputeOrDraw() so it uses the PPO's compute values
// before convertPpoToComputeOrDraw() reverts the PPO back to graphics.
MarkShaderStorageUsage(this);
// We always assume PPOs are used for draws, until they aren't. If we just executed a dispatch
// with a PPO, we need to convert it back to a "draw"-type.
convertPpoToComputeOrDraw(false);
......@@ -5686,8 +5690,6 @@ void Context::dispatchCompute(GLuint numGroupsX, GLuint numGroupsY, GLuint numGr
{
return;
}
MarkShaderStorageUsage(this);
}
void Context::convertPpoToComputeOrDraw(bool isCompute)
......@@ -5700,6 +5702,10 @@ void Context::convertPpoToComputeOrDraw(bool isCompute)
pipeline->setDirtyBit(ProgramPipeline::DirtyBitType::DIRTY_BIT_DRAW_DISPATCH_CHANGE);
mState.mDirtyObjects.set(State::DIRTY_OBJECT_PROGRAM_PIPELINE);
mState.mDirtyBits.set(State::DirtyBitType::DIRTY_BIT_PROGRAM_EXECUTABLE);
// The PPO's isCompute() has changed, so its ProgramExecutable will produce different
// results for things like getShaderStorageBlocks() or getImageBindings().
mStateCache.onProgramExecutableChange(this);
}
}
......@@ -8826,10 +8832,10 @@ void StateCache::updateVertexAttribTypesValidation(Context *context)
void StateCache::updateActiveShaderStorageBufferIndices(Context *context)
{
mCachedActiveShaderStorageBufferIndices.reset();
Program *program = context->getState().getProgram();
if (program)
const ProgramExecutable *executable = context->getState().getProgramExecutable();
if (executable)
{
for (const InterfaceBlock &block : program->getState().getShaderStorageBlocks())
for (const InterfaceBlock &block : executable->getShaderStorageBlocks())
{
mCachedActiveShaderStorageBufferIndices.set(block.binding);
}
......@@ -8839,10 +8845,10 @@ void StateCache::updateActiveShaderStorageBufferIndices(Context *context)
void StateCache::updateActiveImageUnitIndices(Context *context)
{
mCachedActiveImageUnitIndices.reset();
Program *program = context->getState().getProgram();
if (program)
const ProgramExecutable *executable = context->getState().getProgramExecutable();
if (executable)
{
for (const ImageBinding &imageBinding : program->getState().getImageBindings())
for (const ImageBinding &imageBinding : executable->getImageBindings())
{
if (imageBinding.unreferenced)
{
......
......@@ -1464,7 +1464,7 @@ angle::Result Program::linkImpl(const Context *context)
{
mState.mExecutable->mResources.reset(new ProgramLinkedResources(
0, PackMode::ANGLE_RELAXED, &mState.mExecutable->mUniformBlocks,
&mState.mExecutable->mUniforms, &mState.mExecutable->mShaderStorageBlocks,
&mState.mExecutable->mUniforms, &mState.mExecutable->mComputeShaderStorageBlocks,
&mState.mBufferVariables, &mState.mExecutable->mAtomicCounterBuffers));
GLuint combinedImageUniforms = 0u;
......@@ -1521,7 +1521,7 @@ angle::Result Program::linkImpl(const Context *context)
mState.mExecutable->mResources.reset(new ProgramLinkedResources(
static_cast<GLuint>(data.getCaps().maxVaryingVectors), packMode,
&mState.mExecutable->mUniformBlocks, &mState.mExecutable->mUniforms,
&mState.mExecutable->mShaderStorageBlocks, &mState.mBufferVariables,
&mState.mExecutable->mGraphicsShaderStorageBlocks, &mState.mBufferVariables,
&mState.mExecutable->mAtomicCounterBuffers));
if (!linkAttributes(context, infoLog))
......@@ -1649,9 +1649,9 @@ void Program::resolveLinkImpl(const Context *context)
ASSERT(mLinked);
// Mark implementation-specific unreferenced uniforms as ignored.
std::vector<ImageBinding> *imageBindings = getExecutable().getImageBindings();
mProgram->markUnusedUniformLocations(&mState.mUniformLocations,
&mState.mExecutable->mSamplerBindings,
&mState.mExecutable->mImageBindings);
&mState.mExecutable->mSamplerBindings, imageBindings);
// Must be called after markUnusedUniformLocations.
postResolveLink(context);
......@@ -2908,8 +2908,9 @@ GLuint Program::getImageUniformBinding(const VariableLocation &uniformLocation)
{
ASSERT(!mLinkingState);
GLuint imageIndex = mState.getImageIndexFromUniformIndex(uniformLocation.index);
const std::vector<GLuint> &boundImageUnits =
mState.mExecutable->mImageBindings[imageIndex].boundImageUnits;
const std::vector<ImageBinding> &imageBindings = getExecutable().getImageBindings();
const std::vector<GLuint> &boundImageUnits = imageBindings[imageIndex].boundImageUnits;
return boundImageUnits[uniformLocation.arrayIndex];
}
......@@ -3698,6 +3699,11 @@ void Program::linkSamplerAndImageBindings(GLuint *combinedImageUniforms)
mState.mExecutable->mImageUniformRange = RangeUI(low, high);
*combinedImageUniforms = 0u;
// The Program is still linking, so getExecutable().isCompute() isn't accurate yet.
bool hasComputeShader = mState.mAttachedShaders[ShaderType::Compute] != nullptr;
std::vector<ImageBinding> &imageBindings = hasComputeShader
? mState.mExecutable->mComputeImageBindings
: mState.mExecutable->mGraphicsImageBindings;
// If uniform is a image type, insert it into the mImageBindings array.
for (unsigned int imageIndex : mState.mExecutable->getImageUniformRange())
{
......@@ -3708,12 +3714,11 @@ void Program::linkSamplerAndImageBindings(GLuint *combinedImageUniforms)
auto &imageUniform = mState.mExecutable->getUniforms()[imageIndex];
if (imageUniform.binding == -1)
{
mState.mExecutable->mImageBindings.emplace_back(
ImageBinding(imageUniform.getBasicTypeElementCount()));
imageBindings.emplace_back(ImageBinding(imageUniform.getBasicTypeElementCount()));
}
else
{
mState.mExecutable->mImageBindings.emplace_back(
imageBindings.emplace_back(
ImageBinding(imageUniform.binding, imageUniform.getBasicTypeElementCount(), false));
}
......@@ -5110,6 +5115,8 @@ angle::Result Program::serialize(const Context *context, angle::MemoryBuffer *bi
stream.writeInt(0);
}
mState.mExecutable->save(&stream);
const auto &computeLocalSize = mState.getComputeShaderLocalSize();
stream.writeInt(computeLocalSize[0]);
......@@ -5270,8 +5277,6 @@ angle::Result Program::serialize(const Context *context, angle::MemoryBuffer *bi
stream.writeInt(mState.getAtomicCounterUniformRange().low());
stream.writeInt(mState.getAtomicCounterUniformRange().high());
mState.mExecutable->save(&stream);
mProgram->save(context, &stream);
ASSERT(binaryOut);
......@@ -5307,6 +5312,8 @@ angle::Result Program::deserialize(const Context *context,
return angle::Result::Incomplete;
}
mState.mExecutable->load(&stream);
mState.mComputeShaderLocalSize[0] = stream.readInt<int>();
mState.mComputeShaderLocalSize[1] = stream.readInt<int>();
mState.mComputeShaderLocalSize[2] = stream.readInt<int>();
......@@ -5393,7 +5400,14 @@ angle::Result Program::deserialize(const Context *context,
{
InterfaceBlock shaderStorageBlock;
LoadInterfaceBlock(&stream, &shaderStorageBlock);
mState.mExecutable->mShaderStorageBlocks.push_back(shaderStorageBlock);
if (getExecutable().isCompute())
{
mState.mExecutable->mComputeShaderStorageBlocks.push_back(shaderStorageBlock);
}
else
{
mState.mExecutable->mGraphicsShaderStorageBlocks.push_back(shaderStorageBlock);
}
}
unsigned int atomicCounterBufferCount = stream.readInt<unsigned int>();
......@@ -5509,7 +5523,14 @@ angle::Result Program::deserialize(const Context *context,
{
imageBinding.boundImageUnits[i] = stream.readInt<unsigned int>();
}
mState.mExecutable->mImageBindings.emplace_back(imageBinding);
if (getExecutable().isCompute())
{
mState.mExecutable->mComputeImageBindings.emplace_back(imageBinding);
}
else
{
mState.mExecutable->mGraphicsImageBindings.emplace_back(imageBinding);
}
}
unsigned int atomicCounterRangeLow = stream.readInt<unsigned int>();
......@@ -5524,8 +5545,6 @@ angle::Result Program::deserialize(const Context *context,
mState.updateTransformFeedbackStrides();
}
mState.mExecutable->load(&stream);
postResolveLink(context);
mState.mExecutable->updateCanDrawWith();
......
......@@ -264,7 +264,7 @@ class ProgramState final : angle::NonCopyable
}
const std::vector<ImageBinding> &getImageBindings() const
{
return mExecutable->getImageBindings();
return getExecutable().getImageBindings();
}
const sh::WorkGroupSize &getComputeShaderLocalSize() const { return mComputeShaderLocalSize; }
const RangeUI &getDefaultUniformRange() const { return mExecutable->getDefaultUniformRange(); }
......@@ -740,7 +740,7 @@ class Program final : public LabeledObject, public angle::Subject
const std::vector<ImageBinding> &getImageBindings() const
{
ASSERT(!mLinkingState);
return mState.mExecutable->getImageBindings();
return getExecutable().getImageBindings();
}
const sh::WorkGroupSize &getComputeShaderLocalSize() const;
PrimitiveMode getGeometryShaderInputPrimitiveType() const;
......
......@@ -72,7 +72,8 @@ ProgramExecutable::ProgramExecutable(const ProgramExecutable &other)
mUniformBlocks(other.mUniformBlocks),
mAtomicCounterBuffers(other.mAtomicCounterBuffers),
mImageUniformRange(other.mImageUniformRange),
mShaderStorageBlocks(other.mShaderStorageBlocks),
mComputeShaderStorageBlocks(other.mComputeShaderStorageBlocks),
mGraphicsShaderStorageBlocks(other.mGraphicsShaderStorageBlocks),
mPipelineHasGraphicsUniformBuffers(other.mPipelineHasGraphicsUniformBuffers),
mPipelineHasComputeUniformBuffers(other.mPipelineHasComputeUniformBuffers),
mPipelineHasGraphicsStorageBuffers(other.mPipelineHasGraphicsStorageBuffers),
......@@ -111,12 +112,14 @@ void ProgramExecutable::reset()
mLinkedTransformFeedbackVaryings.clear();
mUniforms.clear();
mUniformBlocks.clear();
mShaderStorageBlocks.clear();
mComputeShaderStorageBlocks.clear();
mGraphicsShaderStorageBlocks.clear();
mAtomicCounterBuffers.clear();
mOutputVariables.clear();
mOutputLocations.clear();
mSamplerBindings.clear();
mImageBindings.clear();
mComputeImageBindings.clear();
mGraphicsImageBindings.clear();
mPipelineHasGraphicsUniformBuffers = false;
mPipelineHasComputeUniformBuffers = false;
......@@ -233,8 +236,17 @@ bool ProgramExecutable::hasUniformBuffers() const
bool ProgramExecutable::hasStorageBuffers() const
{
return !getShaderStorageBlocks().empty() ||
(isCompute() ? mPipelineHasComputeStorageBuffers : mPipelineHasGraphicsStorageBuffers);
return (isCompute() ? hasComputeStorageBuffers() : hasGraphicsStorageBuffers());
}
bool ProgramExecutable::hasGraphicsStorageBuffers() const
{
return !mGraphicsShaderStorageBlocks.empty() || mPipelineHasGraphicsStorageBuffers;
}
bool ProgramExecutable::hasComputeStorageBuffers() const
{
return !mComputeShaderStorageBlocks.empty() || mPipelineHasComputeStorageBuffers;
}
bool ProgramExecutable::hasAtomicCounterBuffers() const
......@@ -246,8 +258,17 @@ bool ProgramExecutable::hasAtomicCounterBuffers() const
bool ProgramExecutable::hasImages() const
{
return !getImageBindings().empty() ||
(isCompute() ? mPipelineHasComputeImages : mPipelineHasGraphicsImages);
return (isCompute() ? hasComputeImages() : hasGraphicsImages());
}
bool ProgramExecutable::hasGraphicsImages() const
{
return !mGraphicsImageBindings.empty() || mPipelineHasGraphicsImages;
}
bool ProgramExecutable::hasComputeImages() const
{
return !mComputeImageBindings.empty() || mPipelineHasComputeImages;
}
GLuint ProgramExecutable::getUniformIndexFromImageIndex(GLuint imageIndex) const
......@@ -295,9 +316,10 @@ void ProgramExecutable::updateActiveSamplers(const ProgramState &programState)
void ProgramExecutable::updateActiveImages(const ProgramExecutable &executable)
{
for (uint32_t imageIndex = 0; imageIndex < mImageBindings.size(); ++imageIndex)
const std::vector<ImageBinding> *imageBindings = getImageBindings();
for (uint32_t imageIndex = 0; imageIndex < imageBindings->size(); ++imageIndex)
{
const gl::ImageBinding &imageBinding = mImageBindings[imageIndex];
const gl::ImageBinding &imageBinding = imageBindings->at(imageIndex);
if (imageBinding.unreferenced)
{
continue;
......
......@@ -194,8 +194,12 @@ class ProgramExecutable final : public angle::Subject
bool hasTextures() const;
bool hasUniformBuffers() const;
bool hasStorageBuffers() const;
bool hasGraphicsStorageBuffers() const;
bool hasComputeStorageBuffers() const;
bool hasAtomicCounterBuffers() const;
bool hasImages() const;
bool hasGraphicsImages() const;
bool hasComputeImages() const;
bool hasTransformFeedbackOutput() const
{
return !getLinkedTransformFeedbackVaryings().empty();
......@@ -216,7 +220,14 @@ class ProgramExecutable final : public angle::Subject
const std::vector<LinkedUniform> &getUniforms() const { return mUniforms; }
const std::vector<InterfaceBlock> &getUniformBlocks() const { return mUniformBlocks; }
const std::vector<SamplerBinding> &getSamplerBindings() const { return mSamplerBindings; }
const std::vector<ImageBinding> &getImageBindings() const { return mImageBindings; }
const std::vector<ImageBinding> &getImageBindings() const
{
return isCompute() ? mComputeImageBindings : mGraphicsImageBindings;
}
std::vector<ImageBinding> *getImageBindings()
{
return isCompute() ? &mComputeImageBindings : &mGraphicsImageBindings;
}
const RangeUI &getDefaultUniformRange() const { return mDefaultUniformRange; }
const RangeUI &getSamplerUniformRange() const { return mSamplerUniformRange; }
const RangeUI &getImageUniformRange() const { return mImageUniformRange; }
......@@ -232,8 +243,10 @@ class ProgramExecutable final : public angle::Subject
}
GLuint getShaderStorageBlockBinding(GLuint blockIndex) const
{
ASSERT(blockIndex < mShaderStorageBlocks.size());
return mShaderStorageBlocks[blockIndex].binding;
ASSERT((isCompute() && (blockIndex < mComputeShaderStorageBlocks.size())) ||
(!isCompute() && (blockIndex < mGraphicsShaderStorageBlocks.size())));
return isCompute() ? mComputeShaderStorageBlocks[blockIndex].binding
: mGraphicsShaderStorageBlocks[blockIndex].binding;
}
const std::vector<GLsizei> &getTransformFeedbackStrides() const
{
......@@ -245,7 +258,7 @@ class ProgramExecutable final : public angle::Subject
}
const std::vector<InterfaceBlock> &getShaderStorageBlocks() const
{
return mShaderStorageBlocks;
return isCompute() ? mComputeShaderStorageBlocks : mGraphicsShaderStorageBlocks;
}
const LinkedUniform &getUniformByIndex(GLuint index) const
{
......@@ -265,7 +278,9 @@ class ProgramExecutable final : public angle::Subject
ANGLE_INLINE GLuint getActiveShaderStorageBlockCount() const
{
return static_cast<GLuint>(mShaderStorageBlocks.size());
size_t shaderStorageBlocksSize =
isCompute() ? mComputeShaderStorageBlocks.size() : mGraphicsShaderStorageBlocks.size();
return static_cast<GLuint>(shaderStorageBlocksSize);
}
GLuint getUniformIndexFromImageIndex(GLuint imageIndex) const;
......@@ -351,13 +366,15 @@ class ProgramExecutable final : public angle::Subject
std::vector<InterfaceBlock> mUniformBlocks;
std::vector<AtomicCounterBuffer> mAtomicCounterBuffers;
RangeUI mImageUniformRange;
std::vector<InterfaceBlock> mShaderStorageBlocks;
std::vector<InterfaceBlock> mComputeShaderStorageBlocks;
std::vector<InterfaceBlock> mGraphicsShaderStorageBlocks;
// An array of the samplers that are used by the program
std::vector<SamplerBinding> mSamplerBindings;
// An array of the images that are used by the program
std::vector<ImageBinding> mImageBindings;
std::vector<ImageBinding> mComputeImageBindings;
std::vector<ImageBinding> mGraphicsImageBindings;
// TODO: http://anglebug.com/3570: Remove mPipelineHas*UniformBuffers once PPO's have valid data
// in mUniformBlocks
......
......@@ -263,6 +263,75 @@ void ProgramPipeline::updateTransformFeedbackMembers()
vertexExecutable.mLinkedTransformFeedbackVaryings;
}
void ProgramPipeline::updateShaderStorageBlocks()
{
mState.mExecutable->mComputeShaderStorageBlocks.clear();
mState.mExecutable->mGraphicsShaderStorageBlocks.clear();
// Only copy the storage blocks from each Program in the PPO once, since each Program could
// contain multiple shader stages.
ShaderBitSet handledStages;
for (const gl::ShaderType shaderType : kAllGraphicsShaderTypes)
{
const Program *shaderProgram = getShaderProgram(shaderType);
if (shaderProgram && !handledStages.test(shaderType))
{
// Only add each Program's blocks once.
handledStages |= shaderProgram->getExecutable().getLinkedShaderStages();
for (const InterfaceBlock &block :
shaderProgram->getExecutable().getShaderStorageBlocks())
{
mState.mExecutable->mGraphicsShaderStorageBlocks.emplace_back(block);
}
}
}
const Program *computeProgram = getShaderProgram(ShaderType::Compute);
if (computeProgram)
{
for (const InterfaceBlock &block : computeProgram->getExecutable().getShaderStorageBlocks())
{
mState.mExecutable->mComputeShaderStorageBlocks.emplace_back(block);
}
}
}
void ProgramPipeline::updateImageBindings()
{
mState.mExecutable->mComputeImageBindings.clear();
mState.mExecutable->mGraphicsImageBindings.clear();
// Only copy the storage blocks from each Program in the PPO once, since each Program could
// contain multiple shader stages.
ShaderBitSet handledStages;
for (const gl::ShaderType shaderType : kAllGraphicsShaderTypes)
{
const Program *shaderProgram = getShaderProgram(shaderType);
if (shaderProgram && !handledStages.test(shaderType))
{
// Only add each Program's blocks once.
handledStages |= shaderProgram->getExecutable().getLinkedShaderStages();
for (const ImageBinding &imageBinding : shaderProgram->getState().getImageBindings())
{
mState.mExecutable->mGraphicsImageBindings.emplace_back(imageBinding);
}
}
}
const Program *computeProgram = getShaderProgram(ShaderType::Compute);
if (computeProgram)
{
for (const ImageBinding &imageBinding : computeProgram->getState().getImageBindings())
{
mState.mExecutable->mComputeImageBindings.emplace_back(imageBinding);
}
}
}
void ProgramPipeline::updateHasBooleans()
{
// Need to check all of the shader stages, not just linked, so we handle Compute correctly.
......@@ -277,7 +346,7 @@ void ProgramPipeline::updateHasBooleans()
{
mState.mExecutable->mPipelineHasGraphicsUniformBuffers = true;
}
if (executable.hasStorageBuffers())
if (executable.hasGraphicsStorageBuffers())
{
mState.mExecutable->mPipelineHasGraphicsStorageBuffers = true;
}
......@@ -293,7 +362,7 @@ void ProgramPipeline::updateHasBooleans()
{
mState.mExecutable->mPipelineHasGraphicsTextures = true;
}
if (executable.hasImages())
if (executable.hasGraphicsImages())
{
mState.mExecutable->mPipelineHasGraphicsImages = true;
}
......@@ -309,7 +378,7 @@ void ProgramPipeline::updateHasBooleans()
{
mState.mExecutable->mPipelineHasComputeUniformBuffers = true;
}
if (executable.hasStorageBuffers())
if (executable.hasComputeStorageBuffers())
{
mState.mExecutable->mPipelineHasComputeStorageBuffers = true;
}
......@@ -325,7 +394,7 @@ void ProgramPipeline::updateHasBooleans()
{
mState.mExecutable->mPipelineHasComputeTextures = true;
}
if (executable.hasImages())
if (executable.hasComputeImages())
{
mState.mExecutable->mPipelineHasComputeImages = true;
}
......@@ -339,9 +408,13 @@ void ProgramPipeline::updateExecutable()
// Vertex Shader ProgramExecutable properties
updateExecutableAttributes();
updateTransformFeedbackMembers();
updateShaderStorageBlocks();
updateImageBindings();
// All Shader ProgramExecutable properties
mState.updateExecutableTextures();
// Must be last, since it queries things updated by earlier functions
updateHasBooleans();
}
......
......@@ -161,6 +161,8 @@ class ProgramPipeline final : public RefCountObject<ProgramPipelineID>,
void updateLinkedShaderStages();
void updateExecutableAttributes();
void updateTransformFeedbackMembers();
void updateShaderStorageBlocks();
void updateImageBindings();
void updateHasBooleans();
void updateExecutable();
......
......@@ -2803,6 +2803,70 @@ void main()
blendAndVerifyColor(GLColor32F(1.0f, 0.0f, 0.0f, 0.5f), GLColor(127, 127, 127, 191));
}
// Tests that invalidate then compute write works inside PPO
TEST_P(SimpleStateChangeTestES31, InvalidateThenStorageWriteThenBlendPpo)
{
// Fails on AMD OpenGL Windows. This configuration isn't maintained.
ANGLE_SKIP_TEST_IF(IsWindows() && IsAMD() && IsOpenGL());
// PPOs are only supported in the Vulkan backend
ANGLE_SKIP_TEST_IF(!isVulkanRenderer());
constexpr char kCS[] = R"(#version 310 es
layout(local_size_x=1, local_size_y=1) in;
layout (rgba8, binding = 1) writeonly uniform highp image2D dstImage;
void main()
{
imageStore(dstImage, ivec2(gl_GlobalInvocationID.xy), vec4(0.0f, 1.0f, 1.0f, 1.0f));
})";
GLProgramPipeline pipeline;
ANGLE_GL_COMPUTE_PROGRAM(program, kCS);
glProgramParameteri(program, GL_PROGRAM_SEPARABLE, GL_TRUE);
glUseProgramStages(pipeline, GL_COMPUTE_SHADER_BIT, program);
EXPECT_GL_NO_ERROR();
glBindProgramPipeline(pipeline);
EXPECT_GL_NO_ERROR();
glUseProgram(0);
// Create the framebuffer texture
GLTexture renderTarget;
glBindTexture(GL_TEXTURE_2D, renderTarget);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1);
glBindImageTexture(1, renderTarget, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA8);
// Write to the texture with compute once. In the Vulkan backend, this will make sure the image
// is already created with STORAGE usage and avoids recreate later.
glDispatchCompute(1, 1, 1);
EXPECT_GL_NO_ERROR();
// Create the framebuffer that will be invalidated
GLFramebuffer drawFBO;
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, drawFBO);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, renderTarget,
0);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_DRAW_FRAMEBUFFER);
EXPECT_GL_NO_ERROR();
// Clear the framebuffer and invalidate it.
glClearColor(1.0f, 1.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
GLenum invalidateAttachment = GL_COLOR_ATTACHMENT0;
glInvalidateFramebuffer(GL_DRAW_FRAMEBUFFER, 1, &invalidateAttachment);
EXPECT_GL_NO_ERROR();
// Write to it with a compute shader
glDispatchCompute(1, 1, 1);
EXPECT_GL_NO_ERROR();
glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT);
// Blend into the framebuffer, then verify that the framebuffer should have had cyan.
glBindFramebuffer(GL_READ_FRAMEBUFFER, drawFBO);
blendAndVerifyColor(GLColor32F(1.0f, 0.0f, 0.0f, 0.5f), GLColor(127, 127, 127, 191));
}
// Tests that sub-invalidate then draw works.
TEST_P(SimpleStateChangeTestES3, SubInvalidateThenDraw)
{
......
......@@ -1846,6 +1846,152 @@ void main()
checkPixelsUnEqual();
}
TEST_P(VertexAttributeTestES31, UsePpoComputeShaderToUpdateVertexBuffer)
{
// PPOs are only supported in the Vulkan backend
ANGLE_SKIP_TEST_IF(!isVulkanRenderer());
initTest();
constexpr char kComputeShader[] =
R"(#version 310 es
layout(local_size_x=24) in;
layout(std430, binding = 0) buffer buf {
uint outData[24];
};
void main()
{
outData[gl_LocalInvocationIndex] = gl_LocalInvocationIndex;
})";
glUseProgram(mProgram);
GLuint mid = std::numeric_limits<GLuint>::max() >> 1;
GLuint hi = std::numeric_limits<GLuint>::max();
std::array<GLuint, kVertexCount> inputData = {
{0, 1, 2, 3, 254, 255, 256, mid - 1, mid, mid + 1, hi - 2, hi - 1, hi}};
std::array<GLfloat, kVertexCount> expectedData;
for (size_t i = 0; i < kVertexCount; i++)
{
expectedData[i] = Normalize(inputData[i]);
}
// Normalized unsigned int attribute will be classified as translated static attribute.
TestData data(GL_UNSIGNED_INT, GL_TRUE, Source::BUFFER, inputData.data(), expectedData.data());
GLint typeSize = 4;
GLsizei dataSize = kVertexCount * TypeStride(data.type);
GLBuffer testBuffer;
glBindBuffer(GL_ARRAY_BUFFER, testBuffer);
glBufferData(GL_ARRAY_BUFFER, dataSize, data.inputData, GL_STATIC_DRAW);
glVertexAttribPointer(mTestAttrib, typeSize, data.type, data.normalized, 0,
reinterpret_cast<void *>(data.bufferOffset));
glEnableVertexAttribArray(mTestAttrib);
glBindBuffer(GL_ARRAY_BUFFER, mExpectedBuffer);
glBufferData(GL_ARRAY_BUFFER, dataSize, data.expectedData, GL_STATIC_DRAW);
glVertexAttribPointer(mExpectedAttrib, typeSize, GL_FLOAT, GL_FALSE, 0, nullptr);
// Draw twice to make sure that all static attributes dirty bits are synced.
glDrawArrays(GL_TRIANGLES, 0, 6);
glDrawArrays(GL_TRIANGLES, 0, 6);
checkPixels();
// Modify the testBuffer using a raw buffer
GLProgramPipeline pipeline;
ANGLE_GL_COMPUTE_PROGRAM(computeProgram, kComputeShader);
glProgramParameteri(computeProgram, GL_PROGRAM_SEPARABLE, GL_TRUE);
glUseProgramStages(pipeline, GL_COMPUTE_SHADER_BIT, computeProgram);
EXPECT_GL_NO_ERROR();
glBindProgramPipeline(pipeline);
EXPECT_GL_NO_ERROR();
glUseProgram(0);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, testBuffer);
glDispatchCompute(1, 1, 1);
glMemoryBarrier(GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT);
// Draw again to verify that testBuffer has been changed.
glUseProgram(mProgram);
glDrawArrays(GL_TRIANGLES, 0, 6);
EXPECT_GL_NO_ERROR();
checkPixelsUnEqual();
}
TEST_P(VertexAttributeTestES31, UseComputeShaderToUpdateVertexBufferSamePpo)
{
// PPOs are only supported in the Vulkan backend
ANGLE_SKIP_TEST_IF(!isVulkanRenderer());
initTest();
constexpr char kComputeShader[] =
R"(#version 310 es
layout(local_size_x=24) in;
layout(std430, binding = 0) buffer buf {
uint outData[24];
};
void main()
{
outData[gl_LocalInvocationIndex] = gl_LocalInvocationIndex;
})";
// Mark the program separable and re-link it so it can be bound to the PPO.
glProgramParameteri(mProgram, GL_PROGRAM_SEPARABLE, GL_TRUE);
glLinkProgram(mProgram);
mProgram = CheckLinkStatusAndReturnProgram(mProgram, true);
GLProgramPipeline pipeline;
EXPECT_GL_NO_ERROR();
glBindProgramPipeline(pipeline);
glUseProgramStages(pipeline, GL_VERTEX_SHADER_BIT | GL_FRAGMENT_SHADER_BIT, mProgram);
EXPECT_GL_NO_ERROR();
glUseProgram(0);
GLuint mid = std::numeric_limits<GLuint>::max() >> 1;
GLuint hi = std::numeric_limits<GLuint>::max();
std::array<GLuint, kVertexCount> inputData = {
{0, 1, 2, 3, 254, 255, 256, mid - 1, mid, mid + 1, hi - 2, hi - 1, hi}};
std::array<GLfloat, kVertexCount> expectedData;
for (size_t i = 0; i < kVertexCount; i++)
{
expectedData[i] = Normalize(inputData[i]);
}
// Normalized unsigned int attribute will be classified as translated static attribute.
TestData data(GL_UNSIGNED_INT, GL_TRUE, Source::BUFFER, inputData.data(), expectedData.data());
GLint typeSize = 4;
GLsizei dataSize = kVertexCount * TypeStride(data.type);
GLBuffer testBuffer;
glBindBuffer(GL_ARRAY_BUFFER, testBuffer);
glBufferData(GL_ARRAY_BUFFER, dataSize, data.inputData, GL_STATIC_DRAW);
glVertexAttribPointer(mTestAttrib, typeSize, data.type, data.normalized, 0,
reinterpret_cast<void *>(data.bufferOffset));
glEnableVertexAttribArray(mTestAttrib);
glBindBuffer(GL_ARRAY_BUFFER, mExpectedBuffer);
glBufferData(GL_ARRAY_BUFFER, dataSize, data.expectedData, GL_STATIC_DRAW);
glVertexAttribPointer(mExpectedAttrib, typeSize, GL_FLOAT, GL_FALSE, 0, nullptr);
// Draw twice to make sure that all static attributes dirty bits are synced.
glDrawArrays(GL_TRIANGLES, 0, 6);
glDrawArrays(GL_TRIANGLES, 0, 6);
checkPixels();
// Modify the testBuffer using a raw buffer
ANGLE_GL_COMPUTE_PROGRAM(computeProgram, kComputeShader);
glProgramParameteri(computeProgram, GL_PROGRAM_SEPARABLE, GL_TRUE);
glUseProgramStages(pipeline, GL_COMPUTE_SHADER_BIT, computeProgram);
EXPECT_GL_NO_ERROR();
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, testBuffer);
glDispatchCompute(1, 1, 1);
glMemoryBarrier(GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT);
// Draw again to verify that testBuffer has been changed.
glUseProgram(mProgram);
glDrawArrays(GL_TRIANGLES, 0, 6);
EXPECT_GL_NO_ERROR();
checkPixelsUnEqual();
}
// Verify that using VertexAttribBinding after VertexAttribPointer won't mess up the draw.
TEST_P(VertexAttributeTestES31, ChangeAttribBindingAfterVertexAttribPointer)
{
......
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