Skip to content

Commit

Permalink
Vulkan: fix crash when clearing stencil with ClearBuffer
Browse files Browse the repository at this point in the history
Follow up to [1] which fixed a crash with glClear, but the bug remained
with glClearBufferiv.  This change refactors the "is stencil write
masked out" query to always take the framebuffer's stencil bit count
into account (practically always 8), which also happens to make the rest
of the code checking this query more accurate in the presence of
nonsense masks where the bottom 8 bits are 0.

[1]: https://chromium-review.googlesource.com/c/angle/angle/+/3315158

Bug: chromium:40207259
Bug: angleproject:42266334
Change-Id: I68a6b0b75c67ed2cdc8c4d03b243efe5495efce1
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/5889788
Reviewed-by: Geoff Lang <[email protected]>
Auto-Submit: Shahbaz Youssefi <[email protected]>
Commit-Queue: Geoff Lang <[email protected]>
  • Loading branch information
ShabbyX authored and Angle LUCI CQ committed Sep 30, 2024
1 parent 163e24e commit 7c81171
Show file tree
Hide file tree
Showing 13 changed files with 80 additions and 67 deletions.
17 changes: 9 additions & 8 deletions src/libANGLE/Context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4714,10 +4714,8 @@ void Context::clear(GLbitfield mask)
}

// If all stencil bits are masked, don't attempt to clear stencil.
if (mState.getDrawFramebuffer()->getStencilAttachment() == nullptr ||
(angle::BitMask<uint32_t>(
mState.getDrawFramebuffer()->getStencilAttachment()->getStencilSize()) &
mState.getDepthStencilState().stencilWritemask) == 0)
if (mState.getDepthStencilState().isStencilMaskedOut(
mState.getDrawFramebuffer()->getStencilBitCount()))
{
mask &= ~GL_STENCIL_BUFFER_BIT;
}
Expand All @@ -4733,7 +4731,9 @@ void Context::clear(GLbitfield mask)
ANGLE_CONTEXT_TRY(mState.getDrawFramebuffer()->clear(this, mask));
}

bool Context::isClearBufferMaskedOut(GLenum buffer, GLint drawbuffer) const
bool Context::isClearBufferMaskedOut(GLenum buffer,
GLint drawbuffer,
GLuint framebufferStencilSize) const
{
switch (buffer)
{
Expand All @@ -4742,10 +4742,10 @@ bool Context::isClearBufferMaskedOut(GLenum buffer, GLint drawbuffer) const
case GL_DEPTH:
return mState.getDepthStencilState().isDepthMaskedOut();
case GL_STENCIL:
return mState.getDepthStencilState().isStencilMaskedOut();
return mState.getDepthStencilState().isStencilMaskedOut(framebufferStencilSize);
case GL_DEPTH_STENCIL:
return mState.getDepthStencilState().isDepthMaskedOut() &&
mState.getDepthStencilState().isStencilMaskedOut();
mState.getDepthStencilState().isStencilMaskedOut(framebufferStencilSize);
default:
UNREACHABLE();
return true;
Expand All @@ -4757,7 +4757,8 @@ bool Context::noopClearBuffer(GLenum buffer, GLint drawbuffer) const
Framebuffer *framebufferObject = mState.getDrawFramebuffer();

return !IsClearBufferEnabled(framebufferObject->getState(), buffer, drawbuffer) ||
mState.isRasterizerDiscardEnabled() || isClearBufferMaskedOut(buffer, drawbuffer);
mState.isRasterizerDiscardEnabled() ||
isClearBufferMaskedOut(buffer, drawbuffer, framebufferObject->getStencilBitCount());
}

void Context::clearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat *values)
Expand Down
4 changes: 3 additions & 1 deletion src/libANGLE/Context.h
Original file line number Diff line number Diff line change
Expand Up @@ -801,7 +801,9 @@ class Context final : public egl::LabeledObject, angle::NonCopyable, public angl
bool noopDrawInstanced(PrimitiveMode mode, GLsizei count, GLsizei instanceCount) const;
bool noopMultiDraw(GLsizei drawcount) const;

bool isClearBufferMaskedOut(GLenum buffer, GLint drawbuffer) const;
bool isClearBufferMaskedOut(GLenum buffer,
GLint drawbuffer,
GLuint framebufferStencilSize) const;
bool noopClearBuffer(GLenum buffer, GLint drawbuffer) const;

void addRef() const { mRefCount++; }
Expand Down
44 changes: 13 additions & 31 deletions src/libANGLE/Framebuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -740,25 +740,30 @@ bool FramebufferState::colorAttachmentsAreUniqueImages() const

bool FramebufferState::hasDepth() const
{
return (mDepthAttachment.isAttached() && mDepthAttachment.getDepthSize() > 0);
return mDepthAttachment.isAttached() && mDepthAttachment.getDepthSize() > 0;
}

bool FramebufferState::hasStencil() const
{
return (mStencilAttachment.isAttached() && mStencilAttachment.getStencilSize() > 0);
return mStencilAttachment.isAttached() && mStencilAttachment.getStencilSize() > 0;
}

GLuint FramebufferState::getStencilBitCount() const
{
return mStencilAttachment.isAttached() ? mStencilAttachment.getStencilSize() : 0;
}

bool FramebufferState::hasExternalTextureAttachment() const
{
// External textures can only be bound to color attachment 0
return (mColorAttachments[0].isAttached() && mColorAttachments[0].isExternalTexture());
return mColorAttachments[0].isAttached() && mColorAttachments[0].isExternalTexture();
}

bool FramebufferState::hasYUVAttachment() const
{
// The only attachments that can be YUV are external textures and surfaces, both are attached at
// color attachment 0.
return (mColorAttachments[0].isAttached() && mColorAttachments[0].isYUV());
return mColorAttachments[0].isAttached() && mColorAttachments[0].isYUV();
}

bool FramebufferState::isMultiview() const
Expand Down Expand Up @@ -1248,31 +1253,6 @@ void Framebuffer::setReadBuffer(GLenum buffer)
}
}

size_t Framebuffer::getNumColorAttachments() const
{
return mState.mColorAttachments.size();
}

bool Framebuffer::hasDepth() const
{
return mState.hasDepth();
}

bool Framebuffer::hasStencil() const
{
return mState.hasStencil();
}

bool Framebuffer::hasExternalTextureAttachment() const
{
return mState.hasExternalTextureAttachment();
}

bool Framebuffer::hasYUVAttachment() const
{
return mState.hasYUVAttachment();
}

void Framebuffer::invalidateCompletenessCache()
{
if (!isDefault())
Expand Down Expand Up @@ -2495,7 +2475,8 @@ angle::Result Framebuffer::ensureClearAttachmentsInitialized(const Context *cont

bool color = (mask & GL_COLOR_BUFFER_BIT) != 0 && !glState.allActiveDrawBufferChannelsMasked();
bool depth = (mask & GL_DEPTH_BUFFER_BIT) != 0 && !depthStencil.isDepthMaskedOut();
bool stencil = (mask & GL_STENCIL_BUFFER_BIT) != 0 && !depthStencil.isStencilMaskedOut();
bool stencil = (mask & GL_STENCIL_BUFFER_BIT) != 0 &&
!depthStencil.isStencilMaskedOut(getStencilBitCount());

if (!color && !depth && !stencil)
{
Expand Down Expand Up @@ -2523,7 +2504,8 @@ angle::Result Framebuffer::ensureClearBufferAttachmentsInitialized(const Context
{
if (!context->isRobustResourceInitEnabled() ||
context->getState().isRasterizerDiscardEnabled() ||
context->isClearBufferMaskedOut(buffer, drawbuffer) || mState.mResourceNeedsInit.none())
context->isClearBufferMaskedOut(buffer, drawbuffer, getStencilBitCount()) ||
mState.mResourceNeedsInit.none())
{
return angle::Result::Continue;
}
Expand Down
12 changes: 7 additions & 5 deletions src/libANGLE/Framebuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ class FramebufferState final : angle::NonCopyable

bool hasDepth() const;
bool hasStencil() const;
GLuint getStencilBitCount() const;

bool hasExternalTextureAttachment() const;
bool hasYUVAttachment() const;
Expand Down Expand Up @@ -285,12 +286,13 @@ class Framebuffer final : public angle::ObserverInterface,
GLenum getReadBufferState() const;
void setReadBuffer(GLenum buffer);

size_t getNumColorAttachments() const;
bool hasDepth() const;
bool hasStencil() const;
size_t getNumColorAttachments() const { return mState.mColorAttachments.size(); }
bool hasDepth() const { return mState.hasDepth(); }
bool hasStencil() const { return mState.hasStencil(); }
GLuint getStencilBitCount() const { return mState.getStencilBitCount(); }

bool hasExternalTextureAttachment() const;
bool hasYUVAttachment() const;
bool hasExternalTextureAttachment() const { return mState.hasExternalTextureAttachment(); }
bool hasYUVAttachment() const { return mState.hasYUVAttachment(); }

// This method calls checkStatus.
int getSamples(const Context *context) const;
Expand Down
10 changes: 7 additions & 3 deletions src/libANGLE/State.h
Original file line number Diff line number Diff line change
Expand Up @@ -334,10 +334,11 @@ class PrivateState : angle::NonCopyable

// Stencil state maniupulation
bool isStencilTestEnabled() const { return mDepthStencil.stencilTest; }
bool isStencilWriteEnabled() const
bool isStencilWriteEnabled(GLuint framebufferStencilSize) const
{
return mDepthStencil.stencilTest &&
!(mDepthStencil.isStencilNoOp() && mDepthStencil.isStencilBackNoOp());
!(mDepthStencil.isStencilNoOp(framebufferStencilSize) &&
mDepthStencil.isStencilBackNoOp(framebufferStencilSize));
}
void setStencilTest(bool enabled);
void setStencilParams(GLenum stencilFunc, GLint stencilRef, GLuint stencilMask);
Expand Down Expand Up @@ -1305,7 +1306,10 @@ class State : angle::NonCopyable
{
return mPrivateState.isBlendAdvancedCoherentEnabled();
}
bool isStencilWriteEnabled() const { return mPrivateState.isStencilWriteEnabled(); }
bool isStencilWriteEnabled(GLuint framebufferStencilSize) const
{
return mPrivateState.isStencilWriteEnabled(framebufferStencilSize);
}
GLint getStencilRef() const { return mPrivateState.getStencilRef(); }
GLint getStencilBackRef() const { return mPrivateState.getStencilBackRef(); }
PolygonMode getPolygonMode() const { return mPrivateState.getPolygonMode(); }
Expand Down
19 changes: 12 additions & 7 deletions src/libANGLE/angletypes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ namespace gl
{
namespace
{
bool IsStencilWriteMaskedOut(GLuint stencilWritemask, GLuint framebufferStencilSize)
{
const GLuint framebufferMask = angle::BitMask<GLuint>(framebufferStencilSize);
return (stencilWritemask & framebufferMask) == 0;
}

bool IsStencilNoOp(GLenum stencilFunc,
GLenum stencilFail,
GLenum stencilPassDepthFail,
Expand Down Expand Up @@ -165,21 +171,20 @@ bool DepthStencilState::isDepthMaskedOut() const
return !depthMask;
}

bool DepthStencilState::isStencilMaskedOut() const
bool DepthStencilState::isStencilMaskedOut(GLuint framebufferStencilSize) const
{
return stencilWritemask == 0;
return IsStencilWriteMaskedOut(stencilWritemask, framebufferStencilSize);
}

bool DepthStencilState::isStencilNoOp() const
bool DepthStencilState::isStencilNoOp(GLuint framebufferStencilSize) const
{
return isStencilMaskedOut() ||
return isStencilMaskedOut(framebufferStencilSize) ||
IsStencilNoOp(stencilFunc, stencilFail, stencilPassDepthFail, stencilPassDepthPass);
}

bool DepthStencilState::isStencilBackNoOp() const
bool DepthStencilState::isStencilBackNoOp(GLuint framebufferStencilSize) const
{
const bool isStencilBackMaskedOut = stencilBackWritemask == 0;
return isStencilBackMaskedOut ||
return IsStencilWriteMaskedOut(stencilBackWritemask, framebufferStencilSize) ||
IsStencilNoOp(stencilBackFunc, stencilBackFail, stencilBackPassDepthFail,
stencilBackPassDepthPass);
}
Expand Down
6 changes: 3 additions & 3 deletions src/libANGLE/angletypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -301,9 +301,9 @@ struct DepthStencilState final
DepthStencilState &operator=(const DepthStencilState &other);

bool isDepthMaskedOut() const;
bool isStencilMaskedOut() const;
bool isStencilNoOp() const;
bool isStencilBackNoOp() const;
bool isStencilMaskedOut(GLuint framebufferStencilSize) const;
bool isStencilNoOp(GLuint framebufferStencilSize) const;
bool isStencilBackNoOp(GLuint framebufferStencilSize) const;

bool depthTest;
GLenum depthFunc;
Expand Down
18 changes: 12 additions & 6 deletions src/libANGLE/renderer/vulkan/ContextVk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,7 @@ vk::ResourceAccess GetDepthAccess(const gl::DepthStencilState &dsState,
}

vk::ResourceAccess GetStencilAccess(const gl::DepthStencilState &dsState,
GLuint framebufferStencilSize,
UpdateDepthFeedbackLoopReason reason)
{
// Skip if depth/stencil not actually accessed.
Expand All @@ -344,8 +345,10 @@ vk::ResourceAccess GetStencilAccess(const gl::DepthStencilState &dsState,
return vk::ResourceAccess::Unused;
}

return dsState.isStencilNoOp() && dsState.isStencilBackNoOp() ? vk::ResourceAccess::ReadOnly
: vk::ResourceAccess::ReadWrite;
return dsState.isStencilNoOp(framebufferStencilSize) &&
dsState.isStencilBackNoOp(framebufferStencilSize)
? vk::ResourceAccess::ReadOnly
: vk::ResourceAccess::ReadWrite;
}

egl::ContextPriority GetContextPriority(const gl::State &state)
Expand Down Expand Up @@ -2304,7 +2307,8 @@ angle::Result ContextVk::switchOutReadOnlyDepthStencilMode(

const gl::DepthStencilState &dsState = mState.getDepthStencilState();
vk::ResourceAccess depthAccess = GetDepthAccess(dsState, depthReason);
vk::ResourceAccess stencilAccess = GetStencilAccess(dsState, stencilReason);
vk::ResourceAccess stencilAccess =
GetStencilAccess(dsState, mState.getDrawFramebuffer()->getStencilBitCount(), stencilReason);

if ((HasResourceWriteAccess(depthAccess) &&
mDepthStencilAttachmentFlags[vk::RenderPassUsage::DepthReadOnlyAttachment]) ||
Expand Down Expand Up @@ -2450,7 +2454,8 @@ angle::Result ContextVk::handleDirtyGraphicsDepthStencilAccess(
const gl::DepthStencilState &dsState = mState.getDepthStencilState();
vk::ResourceAccess depthAccess = GetDepthAccess(dsState, UpdateDepthFeedbackLoopReason::Draw);
vk::ResourceAccess stencilAccess =
GetStencilAccess(dsState, UpdateDepthFeedbackLoopReason::Draw);
GetStencilAccess(dsState, mState.getDrawFramebuffer()->getStencilBitCount(),
UpdateDepthFeedbackLoopReason::Draw);
mRenderPassCommands->onDepthAccess(depthAccess);
mRenderPassCommands->onStencilAccess(stencilAccess);

Expand Down Expand Up @@ -4552,7 +4557,8 @@ angle::Result ContextVk::optimizeRenderPassForPresent(vk::ImageViewHelper *color
mRenderPassCommands->invalidateRenderPassDepthAttachment(
dsState, mRenderPassCommands->getRenderArea());
mRenderPassCommands->invalidateRenderPassStencilAttachment(
dsState, mRenderPassCommands->getRenderArea());
dsState, mState.getDrawFramebuffer()->getStencilBitCount(),
mRenderPassCommands->getRenderArea());
}

// Use finalLayout instead of extra barrier for layout change to present
Expand Down Expand Up @@ -8569,7 +8575,7 @@ angle::Result ContextVk::switchToReadOnlyDepthStencilMode(gl::Texture *texture,

if (isStencilTexture)
{
if (mState.isStencilWriteEnabled())
if (mState.isStencilWriteEnabled(mState.getDrawFramebuffer()->getStencilBitCount()))
{
// This looks like a feedback loop, but we don't issue a warning because the application
// may have correctly used BASE and MAX levels to avoid it. ANGLE doesn't track that.
Expand Down
2 changes: 1 addition & 1 deletion src/libANGLE/renderer/vulkan/FramebufferVk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2217,7 +2217,7 @@ angle::Result FramebufferVk::invalidateImpl(ContextVk *contextVk,
if (invalidateStencilBuffer)
{
contextVk->getStartedRenderPassCommands().invalidateRenderPassStencilAttachment(
dsState, invalidateArea);
dsState, mState.getStencilBitCount(), invalidateArea);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ egl::Error HardwareBufferImageSiblingVkAndroid::ValidateHardwareBuffer(
{
return egl::EglBadAccess()
<< "EGL_PROTECTED_CONTENT_EXT attribute does not match protected state "
"of EGLCleintBuffer.";
"of EGLClientBuffer.";
}
}

Expand Down
4 changes: 3 additions & 1 deletion src/libANGLE/renderer/vulkan/vk_helpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3045,10 +3045,12 @@ void RenderPassCommandBufferHelper::invalidateRenderPassDepthAttachment(

void RenderPassCommandBufferHelper::invalidateRenderPassStencilAttachment(
const gl::DepthStencilState &dsState,
GLuint framebufferStencilSize,
const gl::Rectangle &invalidateArea)
{
const bool isStencilWriteEnabled =
dsState.stencilTest && (!dsState.isStencilNoOp() || !dsState.isStencilBackNoOp());
dsState.stencilTest && (!dsState.isStencilNoOp(framebufferStencilSize) ||
!dsState.isStencilBackNoOp(framebufferStencilSize));
mStencilAttachment.invalidate(invalidateArea, isStencilWriteEnabled,
getRenderPassWriteCommandCount());
}
Expand Down
1 change: 1 addition & 0 deletions src/libANGLE/renderer/vulkan/vk_helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -1833,6 +1833,7 @@ class RenderPassCommandBufferHelper final : public CommandBufferHelperCommon
void invalidateRenderPassDepthAttachment(const gl::DepthStencilState &dsState,
const gl::Rectangle &invalidateArea);
void invalidateRenderPassStencilAttachment(const gl::DepthStencilState &dsState,
GLuint framebufferStencilSize,
const gl::Rectangle &invalidateArea);

void updateRenderPassColorClear(PackedAttachmentIndex colorIndexVk,
Expand Down
8 changes: 8 additions & 0 deletions src/tests/gl_tests/ClearTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3006,6 +3006,14 @@ TEST_P(ClearTestES3, ClearStencilZeroFirstByteMask)
glClear(GL_STENCIL_BUFFER_BIT);
}

// Same test as ClearStencilZeroFirstByteMask, but using glClearBufferiv.
TEST_P(ClearTestES3, ClearBufferStencilZeroFirstByteMask)
{
glStencilMask(0xe7d6a900);
const GLint kStencilClearValue = 0x55;
glClearBufferiv(GL_STENCIL, 0, &kStencilClearValue);
}

// Test that mid render pass clear after draw sets the render pass size correctly.
TEST_P(ClearTestES3, ScissoredDrawThenFullClear)
{
Expand Down

0 comments on commit 7c81171

Please sign in to comment.