Commit 47f6dd0d by Qin Jiajia Committed by Commit Bot

Implement robust initialization for image texture

This change fixes a bug that the image texture initialization time is not correct. It happens when we use compute shader to write data to an uninitialized texture, then use readPixels to read the result which results that texture initialization falls behind image store and covers the real result. Bug: angleproject:2766 Change-Id: I4e986972096857afc975c40dfa4d559a2f31194c Reviewed-on: https://chromium-review.googlesource.com/1170569 Commit-Queue: Jiajia Qin <jiajia.qin@intel.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent 4e877ad3
......@@ -770,10 +770,11 @@ ProgramBindings::const_iterator ProgramBindings::end() const
}
// ImageBinding implementation.
ImageBinding::ImageBinding(size_t count) : boundImageUnits(count, 0)
ImageBinding::ImageBinding(size_t count) : boundImageUnits(count, 0), unreferenced(false)
{
}
ImageBinding::ImageBinding(GLuint imageUnit, size_t count)
ImageBinding::ImageBinding(GLuint imageUnit, size_t count, bool unreferenced)
: unreferenced(unreferenced)
{
for (size_t index = 0; index < count; ++index)
{
......@@ -861,6 +862,17 @@ GLuint ProgramState::getSamplerIndexFromUniformIndex(GLuint uniformIndex) const
return uniformIndex - mSamplerUniformRange.low();
}
bool ProgramState::isImageUniformIndex(GLuint index) const
{
return mImageUniformRange.contains(index);
}
GLuint ProgramState::getImageIndexFromUniformIndex(GLuint uniformIndex) const
{
ASSERT(isImageUniformIndex(uniformIndex));
return uniformIndex - mImageUniformRange.low();
}
GLuint ProgramState::getAttributeLocation(const std::string &name) const
{
for (const sh::Attribute &attribute : mAttributes)
......@@ -1291,10 +1303,12 @@ void Program::resolveLinkImpl()
updateLinkedShaderStages();
// Mark implementation-specific unreferenced uniforms as ignored.
mProgram->markUnusedUniformLocations(&mState.mUniformLocations, &mState.mSamplerBindings);
mProgram->markUnusedUniformLocations(&mState.mUniformLocations, &mState.mSamplerBindings,
&mState.mImageBindings);
// Must be called after markUnusedUniformLocations.
mState.updateActiveSamplers();
mState.updateActiveImages();
setUniformValuesFromBindingQualifiers();
......@@ -1361,6 +1375,20 @@ void ProgramState::updateActiveSamplers()
}
}
void ProgramState::updateActiveImages()
{
for (ImageBinding &imageBinding : mImageBindings)
{
if (imageBinding.unreferenced)
continue;
for (GLint imageUnit : imageBinding.boundImageUnits)
{
mActiveImagesMask.set(imageUnit);
}
}
}
// Returns the program object to an unlinked state, before re-linking, or at destruction
void Program::unlink()
{
......@@ -1383,6 +1411,7 @@ void Program::unlink()
mState.mComputeShaderLocalSize.fill(1);
mState.mSamplerBindings.clear();
mState.mImageBindings.clear();
mState.mActiveImagesMask.reset();
mState.mNumViews = -1;
mState.mGeometryShaderInputPrimitiveType = PrimitiveMode::Triangles;
mState.mGeometryShaderOutputPrimitiveType = PrimitiveMode::TriangleStrip;
......@@ -2860,7 +2889,7 @@ void Program::linkSamplerAndImageBindings(GLuint *combinedImageUniforms)
else
{
mState.mImageBindings.emplace_back(
ImageBinding(imageUniform.binding, imageUniform.getBasicTypeElementCount()));
ImageBinding(imageUniform.binding, imageUniform.getBasicTypeElementCount(), false));
}
GLuint arraySize = imageUniform.isArray() ? imageUniform.arraySizes[0] : 1u;
......
......@@ -264,11 +264,14 @@ struct TransformFeedbackVarying : public sh::Varying
struct ImageBinding
{
ImageBinding(size_t count);
ImageBinding(GLuint imageUnit, size_t count);
ImageBinding(GLuint imageUnit, size_t count, bool unreferenced);
ImageBinding(const ImageBinding &other);
~ImageBinding();
std::vector<GLuint> boundImageUnits;
// A note if this image unit is an unreferenced uniform.
bool unreferenced;
};
class ProgramState final : angle::NonCopyable
......@@ -338,6 +341,8 @@ class ProgramState final : angle::NonCopyable
Optional<GLuint> getSamplerIndex(GLint location) const;
bool isSamplerUniformIndex(GLuint index) const;
GLuint getSamplerIndexFromUniformIndex(GLuint uniformIndex) const;
bool isImageUniformIndex(GLuint index) const;
GLuint getImageIndexFromUniformIndex(GLuint uniformIndex) const;
GLuint getAttributeLocation(const std::string &name) const;
GLuint getBufferVariableIndexFromName(const std::string &name) const;
......@@ -355,6 +360,7 @@ class ProgramState final : angle::NonCopyable
void updateTransformFeedbackStrides();
void updateActiveSamplers();
void updateActiveImages();
// Scans the sampler bindings for type conflicts with sampler 'textureUnitIndex'.
TextureType getSamplerUniformTextureType(size_t textureUnitIndex) const;
......@@ -437,6 +443,9 @@ class ProgramState final : angle::NonCopyable
ActiveTextureMask mActiveSamplersMask;
ActiveTextureArray<uint32_t> mActiveSamplerRefCounts;
ActiveTextureArray<TextureType> mActiveSamplerTypes;
// Cached mask of active images.
ActiveTextureMask mActiveImagesMask;
};
class ProgramBindings final : angle::NonCopyable
......@@ -748,6 +757,7 @@ class Program final : angle::NonCopyable, public LabeledObject
const std::vector<GLsizei> &getTransformFeedbackStrides() const;
const ActiveTextureMask &getActiveSamplersMask() const { return mState.mActiveSamplersMask; }
const ActiveTextureMask &getActiveImagesMask() const { return mState.mActiveImagesMask; }
const ActiveTextureArray<TextureType> &getActiveSamplerTypes() const
{
......
......@@ -132,6 +132,7 @@ State::State(bool debug,
mActiveSampler(0),
mActiveTexturesCache{},
mCachedTexturesInitState(InitState::MayNeedInit),
mCachedImageTexturesInitState(InitState::MayNeedInit),
mPrimitiveRestart(false),
mDebug(debug),
mMultiSampling(false),
......@@ -239,6 +240,7 @@ void State::initialize(Context *context)
}
mCompleteTextureBindings.reserve(caps.maxCombinedTextureImageUnits);
mCachedTexturesInitState = InitState::MayNeedInit;
mCachedImageTexturesInitState = InitState::MayNeedInit;
for (uint32_t textureIndex = 0; textureIndex < caps.maxCombinedTextureImageUnits;
++textureIndex)
{
......@@ -2701,6 +2703,7 @@ Error State::syncProgramTextures(const Context *context)
// Initialize to the 'Initialized' state and set to 'MayNeedInit' if any texture is not
// initialized.
mCachedTexturesInitState = InitState::Initialized;
mCachedImageTexturesInitState = InitState::Initialized;
const ActiveTextureMask &activeTextures = mProgram->getActiveSamplersMask();
const ActiveTextureArray<TextureType> &textureTypes = mProgram->getActiveSamplerTypes();
......@@ -2756,6 +2759,23 @@ Error State::syncProgramTextures(const Context *context)
}
}
for (size_t imageUnitIndex : mProgram->getActiveImagesMask())
{
Texture *texture = mImageUnits[imageUnitIndex].texture.get();
if (!texture)
{
continue;
}
if (!mDrawFramebuffer->hasTextureAttachment(texture))
{
ANGLE_TRY(texture->syncState(context));
}
if (texture->initState() == InitState::MayNeedInit)
{
mCachedImageTexturesInitState = InitState::MayNeedInit;
}
}
return NoError();
}
......@@ -2880,28 +2900,35 @@ void State::onUniformBufferStateChange(size_t uniformBufferIndex)
Error State::clearUnclearedActiveTextures(const Context *context)
{
ASSERT(mRobustResourceInit);
if (mCachedTexturesInitState == InitState::Initialized)
{
return NoError();
}
ASSERT(!mDirtyObjects[DIRTY_OBJECT_PROGRAM_TEXTURES]);
if (!mProgram)
return NoError();
for (auto textureIndex : mProgram->getActiveSamplersMask())
if (mCachedTexturesInitState != InitState::Initialized)
{
Texture *texture = mActiveTexturesCache[textureIndex];
if (texture)
for (size_t textureUnitIndex : mProgram->getActiveSamplersMask())
{
ANGLE_TRY(texture->ensureInitialized(context));
Texture *texture = mActiveTexturesCache[textureUnitIndex];
if (texture)
{
ANGLE_TRY(texture->ensureInitialized(context));
}
}
mCachedTexturesInitState = InitState::Initialized;
}
if (mCachedImageTexturesInitState != InitState::Initialized)
{
for (size_t imageUnitIndex : mProgram->getActiveImagesMask())
{
Texture *texture = mImageUnits[imageUnitIndex].texture.get();
if (texture)
{
ANGLE_TRY(texture->ensureInitialized(context));
}
}
mCachedImageTexturesInitState = InitState::Initialized;
}
mCachedTexturesInitState = InitState::Initialized;
return NoError();
}
......
......@@ -564,6 +564,8 @@ class State : angle::NonCopyable
std::vector<angle::ObserverBinding> mCompleteTextureBindings;
InitState mCachedTexturesInitState;
InitState mCachedImageTexturesInitState;
using SamplerBindingVector = std::vector<BindingPointer<Sampler>>;
SamplerBindingVector mSamplers;
......
......@@ -120,7 +120,8 @@ class ProgramImpl : angle::NonCopyable
// perform more extensive analysis and ignore some locations that ANGLE doesn't detect as
// unreferenced. This method is not required to be overriden by a back-end.
virtual void markUnusedUniformLocations(std::vector<gl::VariableLocation> *uniformLocations,
std::vector<gl::SamplerBinding> *samplerBindings)
std::vector<gl::SamplerBinding> *samplerBindings,
std::vector<gl::ImageBinding> *imageBindings)
{
}
......
......@@ -890,7 +890,8 @@ void ProgramGL::getUniformuiv(const gl::Context *context, GLint location, GLuint
}
void ProgramGL::markUnusedUniformLocations(std::vector<gl::VariableLocation> *uniformLocations,
std::vector<gl::SamplerBinding> *samplerBindings)
std::vector<gl::SamplerBinding> *samplerBindings,
std::vector<gl::ImageBinding> *imageBindings)
{
GLint maxLocation = static_cast<GLint>(uniformLocations->size());
for (GLint location = 0; location < maxLocation; ++location)
......@@ -903,6 +904,11 @@ void ProgramGL::markUnusedUniformLocations(std::vector<gl::VariableLocation> *un
GLuint samplerIndex = mState.getSamplerIndexFromUniformIndex(locationRef.index);
(*samplerBindings)[samplerIndex].unreferenced = true;
}
else if (mState.isImageUniformIndex(locationRef.index))
{
GLuint imageIndex = mState.getImageIndexFromUniformIndex(locationRef.index);
(*imageBindings)[imageIndex].unreferenced = true;
}
locationRef.markUnused();
}
}
......
......@@ -75,7 +75,8 @@ class ProgramGL : public ProgramImpl
const GLfloat *coeffs) override;
void markUnusedUniformLocations(std::vector<gl::VariableLocation> *uniformLocations,
std::vector<gl::SamplerBinding> *samplerBindings) override;
std::vector<gl::SamplerBinding> *samplerBindings,
std::vector<gl::ImageBinding> *imageBindings) override;
GLuint getProgramID() const;
......
......@@ -905,23 +905,20 @@ void StateManagerGL::setGenericShaderState(const gl::Context *context)
// TODO(xinghua.cao@intel.com): Track image units state with dirty bits to
// avoid update every draw call.
ASSERT(context->getClientVersion() >= gl::ES_3_1 || program->getImageBindings().size() == 0);
for (const gl::ImageBinding &imageUniform : program->getImageBindings())
for (size_t imageUnitIndex : program->getActiveImagesMask())
{
for (GLuint imageUnitIndex : imageUniform.boundImageUnits)
const gl::ImageUnit &imageUnit = glState.getImageUnit(imageUnitIndex);
const TextureGL *textureGL = SafeGetImplAs<TextureGL>(imageUnit.texture.get());
if (textureGL)
{
const gl::ImageUnit &imageUnit = glState.getImageUnit(imageUnitIndex);
const TextureGL *textureGL = SafeGetImplAs<TextureGL>(imageUnit.texture.get());
if (textureGL)
{
bindImageTexture(imageUnitIndex, textureGL->getTextureID(), imageUnit.level,
imageUnit.layered, imageUnit.layer, imageUnit.access,
imageUnit.format);
}
else
{
bindImageTexture(imageUnitIndex, 0, imageUnit.level, imageUnit.layered,
imageUnit.layer, imageUnit.access, imageUnit.format);
}
bindImageTexture(imageUnitIndex, textureGL->getTextureID(), imageUnit.level,
imageUnit.layered, imageUnit.layer, imageUnit.access,
imageUnit.format);
}
else
{
bindImageTexture(imageUnitIndex, 0, imageUnit.level, imageUnit.layered, imageUnit.layer,
imageUnit.access, imageUnit.format);
}
}
......
......@@ -297,6 +297,10 @@ class RobustResourceInitTestES3 : public RobustResourceInitTest
GLenum type);
};
class RobustResourceInitTestES31 : public RobustResourceInitTest
{
};
// Robust resource initialization is not based on hardware support or native extensions, check that
// it only works on the implemented renderers
TEST_P(RobustResourceInitTest, ExpectedRendererSupport)
......@@ -1004,6 +1008,44 @@ TEST_P(RobustResourceInitTestES3, TextureInit_IntRGB32)
testIntegerTextureInit<int32_t>("i", GL_RGBA32I, GL_RGB32I, GL_INT);
}
// Test that uninitialized image texture works well.
TEST_P(RobustResourceInitTestES31, ImageTextureInit_R32UI)
{
ANGLE_SKIP_TEST_IF(!hasGLExtension());
const std::string csSource =
R"(#version 310 es
layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
layout(r32ui, binding = 1) writeonly uniform highp uimage2D writeImage;
void main()
{
imageStore(writeImage, ivec2(gl_LocalInvocationID.xy), uvec4(200u));
})";
GLTexture texture;
// Don't upload data to texture.
glBindTexture(GL_TEXTURE_2D, texture);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32UI, 1, 1);
EXPECT_GL_NO_ERROR();
ANGLE_GL_COMPUTE_PROGRAM(program, csSource);
glUseProgram(program.get());
glBindImageTexture(1, texture, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R32UI);
glDispatchCompute(1, 1, 1);
EXPECT_GL_NO_ERROR();
GLFramebuffer framebuffer;
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
GLuint outputValue;
glReadPixels(0, 0, 1, 1, GL_RED_INTEGER, GL_UNSIGNED_INT, &outputValue);
EXPECT_GL_NO_ERROR();
EXPECT_EQ(200u, outputValue);
}
// Basic test that renderbuffers are initialized correctly.
TEST_P(RobustResourceInitTest, Renderbuffer)
{
......@@ -1697,4 +1739,6 @@ ANGLE_INSTANTIATE_TEST(RobustResourceInitTest,
ANGLE_INSTANTIATE_TEST(RobustResourceInitTestES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
ANGLE_INSTANTIATE_TEST(RobustResourceInitTestES31, ES31_OPENGL(), ES31_D3D11());
} // 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