Commit 248119b3 by Shahbaz Youssefi Committed by Commit Bot

Vulkan: Fix render target's tracking of content defined

Imagine the following scenario: 1. Clear draw framebuffer 2. Invalidate draw framebuffer 3. Update texture attached to draw framebuffer 4. Draw again into draw framebuffer Step 3 could be a number of things, such as glCopyTex[Sub]Image, glBlitFramebuffer, glTex[Sub]Image2D, glGenerateMipmap etc. In the above scenario, at step 2, the framebuffer's render target remembers it being invalidated (mContentDefined = false). This is used to set the loadOp of the next render pass to DONT_CARE. However, mContentDefined was implemented for a very specific optimization regarding the swapchain's depth buffer. The reuse of this variable for glInvalidateFramebuffer was erroneous as this variable didn't track whether the contents are defined for the general case. With this change, mContentDefined is set to true during FramebufferVk::syncState for each render target whose contents are marked dirty. This change additionally makes glBlitFramebuffer signal the contents of the blit targets as dirty, as well as textures that are used as storage images. Bug: angleproject:4859 Change-Id: I68c829f75ff4a3d03bb293ec72c609384983026d Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2309110 Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarCharlie Lao <cclao@google.com>
parent 2c3ff0a0
......@@ -2245,7 +2245,7 @@ void Context::drawArraysInstanced(PrimitiveMode mode,
ANGLE_CONTEXT_TRY(
mImplementation->drawArraysInstanced(this, mode, first, count, instanceCount));
MarkTransformFeedbackBufferUsage(this, count, instanceCount);
MarkShaderStorageBufferUsage(this);
MarkShaderStorageUsage(this);
}
void Context::drawElementsInstanced(PrimitiveMode mode,
......@@ -2263,7 +2263,7 @@ void Context::drawElementsInstanced(PrimitiveMode mode,
ANGLE_CONTEXT_TRY(prepareForDraw(mode));
ANGLE_CONTEXT_TRY(
mImplementation->drawElementsInstanced(this, mode, count, type, indices, instances));
MarkShaderStorageBufferUsage(this);
MarkShaderStorageUsage(this);
}
void Context::drawElementsBaseVertex(PrimitiveMode mode,
......@@ -2281,7 +2281,7 @@ void Context::drawElementsBaseVertex(PrimitiveMode mode,
ANGLE_CONTEXT_TRY(prepareForDraw(mode));
ANGLE_CONTEXT_TRY(
mImplementation->drawElementsBaseVertex(this, mode, count, type, indices, basevertex));
MarkShaderStorageBufferUsage(this);
MarkShaderStorageUsage(this);
}
void Context::drawElementsInstancedBaseVertex(PrimitiveMode mode,
......@@ -2300,7 +2300,7 @@ void Context::drawElementsInstancedBaseVertex(PrimitiveMode mode,
ANGLE_CONTEXT_TRY(prepareForDraw(mode));
ANGLE_CONTEXT_TRY(mImplementation->drawElementsInstancedBaseVertex(
this, mode, count, type, indices, instancecount, basevertex));
MarkShaderStorageBufferUsage(this);
MarkShaderStorageUsage(this);
}
void Context::drawRangeElements(PrimitiveMode mode,
......@@ -2319,7 +2319,7 @@ void Context::drawRangeElements(PrimitiveMode mode,
ANGLE_CONTEXT_TRY(prepareForDraw(mode));
ANGLE_CONTEXT_TRY(
mImplementation->drawRangeElements(this, mode, start, end, count, type, indices));
MarkShaderStorageBufferUsage(this);
MarkShaderStorageUsage(this);
}
void Context::drawRangeElementsBaseVertex(PrimitiveMode mode,
......@@ -2339,21 +2339,21 @@ void Context::drawRangeElementsBaseVertex(PrimitiveMode mode,
ANGLE_CONTEXT_TRY(prepareForDraw(mode));
ANGLE_CONTEXT_TRY(mImplementation->drawRangeElementsBaseVertex(this, mode, start, end, count,
type, indices, basevertex));
MarkShaderStorageBufferUsage(this);
MarkShaderStorageUsage(this);
}
void Context::drawArraysIndirect(PrimitiveMode mode, const void *indirect)
{
ANGLE_CONTEXT_TRY(prepareForDraw(mode));
ANGLE_CONTEXT_TRY(mImplementation->drawArraysIndirect(this, mode, indirect));
MarkShaderStorageBufferUsage(this);
MarkShaderStorageUsage(this);
}
void Context::drawElementsIndirect(PrimitiveMode mode, DrawElementsType type, const void *indirect)
{
ANGLE_CONTEXT_TRY(prepareForDraw(mode));
ANGLE_CONTEXT_TRY(mImplementation->drawElementsIndirect(this, mode, type, indirect));
MarkShaderStorageBufferUsage(this);
MarkShaderStorageUsage(this);
}
void Context::flush()
......@@ -5686,7 +5686,7 @@ void Context::dispatchCompute(GLuint numGroupsX, GLuint numGroupsY, GLuint numGr
return;
}
MarkShaderStorageBufferUsage(this);
MarkShaderStorageUsage(this);
}
void Context::convertPpoToComputeOrDraw(bool isCompute)
......@@ -5707,7 +5707,7 @@ void Context::dispatchComputeIndirect(GLintptr indirect)
ANGLE_CONTEXT_TRY(prepareForDispatch());
ANGLE_CONTEXT_TRY(mImplementation->dispatchComputeIndirect(this, indirect));
MarkShaderStorageBufferUsage(this);
MarkShaderStorageUsage(this);
}
void Context::texStorage2D(TextureType target,
......@@ -8507,6 +8507,7 @@ void StateCache::onProgramExecutableChange(Context *context)
updateBasicDrawStatesError();
updateValidDrawModes(context);
updateActiveShaderStorageBufferIndices(context);
updateActiveImageUnitIndices(context);
updateCanDraw(context);
}
......@@ -8772,6 +8773,26 @@ void StateCache::updateActiveShaderStorageBufferIndices(Context *context)
}
}
void StateCache::updateActiveImageUnitIndices(Context *context)
{
mCachedActiveImageUnitIndices.reset();
Program *program = context->getState().getProgram();
if (program)
{
for (const ImageBinding &imageBinding : program->getState().getImageBindings())
{
if (imageBinding.unreferenced)
{
continue;
}
for (GLuint binding : imageBinding.boundImageUnits)
{
mCachedActiveImageUnitIndices.set(binding);
}
}
}
}
void StateCache::updateCanDraw(Context *context)
{
mCachedCanDraw = (context->isGLES1() ||
......
......@@ -258,6 +258,10 @@ class StateCache final : angle::NonCopyable
return mCachedActiveShaderStorageBufferIndices;
}
// Places that can trigger updateActiveImageUnitIndices:
// 1. onProgramExecutableChange.
ImageUnitMask getActiveImageUnitIndices() const { return mCachedActiveImageUnitIndices; }
// Places that can trigger updateCanDraw:
// 1. onProgramExecutableChange.
bool getCanDraw() const { return mCachedCanDraw; }
......@@ -295,6 +299,7 @@ class StateCache final : angle::NonCopyable
void updateTransformFeedbackActiveUnpaused(Context *context);
void updateVertexAttribTypesValidation(Context *context);
void updateActiveShaderStorageBufferIndices(Context *context);
void updateActiveImageUnitIndices(Context *context);
void updateCanDraw(Context *context);
void setValidDrawModes(bool pointsOK, bool linesOK, bool trisOK, bool lineAdjOK, bool triAdjOK);
......@@ -314,6 +319,7 @@ class StateCache final : angle::NonCopyable
mutable intptr_t mCachedBasicDrawElementsError;
bool mCachedTransformFeedbackActiveUnpaused;
StorageBuffersMask mCachedActiveShaderStorageBufferIndices;
ImageUnitMask mCachedActiveImageUnitIndices;
// Reserve an extra slot at the end of these maps for invalid enum.
angle::PackedEnumMap<PrimitiveMode, bool, angle::EnumSize<PrimitiveMode>() + 1>
......
......@@ -46,16 +46,26 @@ ANGLE_INLINE void MarkTransformFeedbackBufferUsage(const Context *context,
}
}
ANGLE_INLINE void MarkShaderStorageBufferUsage(const Context *context)
ANGLE_INLINE void MarkShaderStorageUsage(const Context *context)
{
for (size_t index : context->getStateCache().getActiveShaderStorageBufferIndices())
{
gl::Buffer *buffer = context->getState().getIndexedShaderStorageBuffer(index).get();
Buffer *buffer = context->getState().getIndexedShaderStorageBuffer(index).get();
if (buffer)
{
buffer->onDataChanged();
}
}
for (size_t index : context->getStateCache().getActiveImageUnitIndices())
{
const ImageUnit &imageUnit = context->getState().getImageUnit(index);
const Texture *texture = imageUnit.texture.get();
if (texture)
{
texture->onStateChange(angle::SubjectMessage::ContentsChanged);
}
}
}
// Return true if the draw is a no-op, else return false.
......
......@@ -1551,7 +1551,27 @@ angle::Result Framebuffer::blit(const Context *context,
{
ASSERT(mask != 0);
return mImpl->blit(context, sourceArea, destArea, mask, filter);
ANGLE_TRY(mImpl->blit(context, sourceArea, destArea, mask, filter));
// Mark the contents of the attachments dirty
if ((mask & GL_COLOR_BUFFER_BIT) != 0)
{
for (size_t colorIndex : mState.mEnabledDrawBuffers)
{
mDirtyBits.set(DIRTY_BIT_COLOR_BUFFER_CONTENTS_0 + colorIndex);
}
}
if ((mask & GL_DEPTH_BUFFER_BIT) != 0)
{
mDirtyBits.set(DIRTY_BIT_DEPTH_BUFFER_CONTENTS);
}
if ((mask & GL_STENCIL_BUFFER_BIT) != 0)
{
mDirtyBits.set(DIRTY_BIT_STENCIL_BUFFER_CONTENTS);
}
onStateChange(angle::SubjectMessage::DirtyBitsFlagged);
return angle::Result::Continue;
}
int Framebuffer::getSamples(const Context *context) const
......
......@@ -526,7 +526,7 @@ angle::Result Context11::drawElementsIndirect(const gl::Context *context,
ASSERT(counts[drawID] > 0); \
DRAW_CALL(drawType, instanced, bvbi); \
ANGLE_MARK_TRANSFORM_FEEDBACK_USAGE(instanced); \
gl::MarkShaderStorageBufferUsage(context); \
gl::MarkShaderStorageUsage(context); \
}
angle::Result Context11::multiDrawArrays(const gl::Context *context,
......
......@@ -968,7 +968,7 @@ void GetSamplePosition(GLsizei sampleCount, size_t index, GLfloat *xy)
ANGLE_SET_BASE_INSTANCE_UNIFORM(hasBaseInstance)(baseInstances[drawID]); \
ANGLE_TRY(DRAW_CALL(drawType, instanced, bvbi)); \
ANGLE_MARK_TRANSFORM_FEEDBACK_USAGE(instanced); \
gl::MarkShaderStorageBufferUsage(context); \
gl::MarkShaderStorageUsage(context); \
}
angle::Result MultiDrawArraysGeneral(ContextImpl *contextImpl,
......
......@@ -172,8 +172,12 @@ vk::ImageHelper &RenderTargetVk::getImageForWrite() const
angle::Result RenderTargetVk::flushStagedUpdates(ContextVk *contextVk,
vk::ClearValuesArray *deferredClears,
uint32_t deferredClearIndex) const
uint32_t deferredClearIndex)
{
// This function is called when the framebuffer is notified of an update to the attachment's
// contents. Therefore, set mContentDefined so that the next render pass will have loadOp=LOAD.
mContentDefined = true;
// Note that the layer index for 3D textures is always zero according to Vulkan.
uint32_t layerIndex = mLayerIndex;
if (mImage->getType() == VK_IMAGE_TYPE_3D)
......
......@@ -81,7 +81,7 @@ class RenderTargetVk final : public FramebufferAttachmentRenderTarget
angle::Result flushStagedUpdates(ContextVk *contextVk,
vk::ClearValuesArray *deferredClears,
uint32_t deferredClearIndex) const;
uint32_t deferredClearIndex);
void retainImageViews(ContextVk *contextVk) const;
......
......@@ -1430,9 +1430,31 @@ class SimpleStateChangeTest : public ANGLETest
};
class SimpleStateChangeTestES3 : public SimpleStateChangeTest
{};
{
protected:
void blendAndVerifyColor(const GLColor32F blendColor, const GLColor expectedColor)
{
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
EXPECT_GL_NO_ERROR();
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
glUseProgram(program);
class SimpleStateChangeTestES31 : public SimpleStateChangeTest
GLint colorUniformLocation =
glGetUniformLocation(program, angle::essl1_shaders::ColorUniform());
ASSERT_NE(colorUniformLocation, -1);
glUniform4f(colorUniformLocation, blendColor.R, blendColor.G, blendColor.B, blendColor.A);
EXPECT_GL_NO_ERROR();
drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
EXPECT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_NEAR(0, 0, expectedColor, 1);
}
};
class SimpleStateChangeTestES31 : public SimpleStateChangeTestES3
{};
class SimpleStateChangeTestComputeES31 : public SimpleStateChangeTest
......@@ -2529,6 +2551,258 @@ TEST_P(SimpleStateChangeTestES3, ReadFramebufferDrawFramebufferDifferentAttachme
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
}
// Tests that invalidate then copy then blend works.
TEST_P(SimpleStateChangeTestES3, InvalidateThenCopyThenBlend)
{
// Create a framebuffer as the source of copy
const GLColor kSrcData = GLColor::cyan;
GLTexture copySrc;
glBindTexture(GL_TEXTURE_2D, copySrc);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &kSrcData);
GLFramebuffer readFBO;
glBindFramebuffer(GL_READ_FRAMEBUFFER, readFBO);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, copySrc, 0);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_READ_FRAMEBUFFER);
// Create the framebuffer that will be invalidated
GLTexture renderTarget;
glBindTexture(GL_TEXTURE_2D, renderTarget);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
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();
// Copy into the framebuffer's texture. The framebuffer should now be cyan.
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 1, 1);
EXPECT_GL_NO_ERROR();
// 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 invalidate then blit then blend works.
TEST_P(SimpleStateChangeTestES3, InvalidateThenBlitThenBlend)
{
// Create a framebuffer as the source of blit
const GLColor kSrcData = GLColor::cyan;
GLTexture blitSrc;
glBindTexture(GL_TEXTURE_2D, blitSrc);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &kSrcData);
GLFramebuffer readFBO;
glBindFramebuffer(GL_READ_FRAMEBUFFER, readFBO);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, blitSrc, 0);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_READ_FRAMEBUFFER);
// Create the framebuffer that will be invalidated
GLTexture renderTarget;
glBindTexture(GL_TEXTURE_2D, renderTarget);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
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();
// Blit into the framebuffer. The framebuffer should now be cyan.
glBlitFramebuffer(0, 0, 1, 1, 0, 0, 1, 1, GL_COLOR_BUFFER_BIT, GL_NEAREST);
EXPECT_GL_NO_ERROR();
// 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 invalidate then generate mipmaps works
TEST_P(SimpleStateChangeTestES3, InvalidateThenGenerateMipmapsThenBlend)
{
// Create a texture on which generate mipmaps would be called
const GLColor kMip0Data[4] = {GLColor::cyan, GLColor::cyan, GLColor::cyan, GLColor::cyan};
const GLColor kMip1Data = GLColor::blue;
GLTexture texture;
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, kMip0Data);
glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &kMip1Data);
// Create the framebuffer that will be invalidated
GLFramebuffer drawFBO;
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, drawFBO);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 1);
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();
// Generate mipmaps
glGenerateMipmap(GL_TEXTURE_2D);
// 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 invalidate then upload works
TEST_P(SimpleStateChangeTestES3, InvalidateThenUploadThenBlend)
{
// http://anglebug.com/4870
ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES());
// Create the framebuffer that will be invalidated
GLTexture renderTarget;
glBindTexture(GL_TEXTURE_2D, renderTarget);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
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();
// Upload data to it
const GLColor kUploadColor = GLColor::cyan;
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &kUploadColor);
// 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 invalidate then sub upload works
TEST_P(SimpleStateChangeTestES3, InvalidateThenSubUploadThenBlend)
{
// http://anglebug.com/4870
ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES());
// Create the framebuffer that will be invalidated
GLTexture renderTarget;
glBindTexture(GL_TEXTURE_2D, renderTarget);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
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();
// Upload data to it
const GLColor kUploadColor = GLColor::cyan;
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &kUploadColor);
// 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 invalidate then compute write works
TEST_P(SimpleStateChangeTestES31, InvalidateThenStorageWriteThenBlend)
{
// Fails on AMD OpenGL Windows. This configuration isn't maintained.
ANGLE_SKIP_TEST_IF(IsWindows() && IsAMD() && IsOpenGL());
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));
})";
ANGLE_GL_COMPUTE_PROGRAM(program, kCS);
glUseProgram(program);
EXPECT_GL_NO_ERROR();
// 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 deleting a Framebuffer that is in use.
TEST_P(SimpleStateChangeTest, DeleteFramebufferInUse)
{
......
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