Commit 05af7590 by Le Hoang Quyen Committed by Commit Bot

Metal: Fix FramebufferMtl's read-after-delete

Due to late verification, ContextMtl might call onFinishedDrawingToFrameBuffer() on a deleted framebuffer object inside syncState() Fix: - Switch to call onStartedDrawingToFrameBuffer() on new FBO instead of calling onFinishedDrawingToFrameBuffer() on old (and possibly deleted) FBO. - Also discard framebuffer only takes effect per render pass. The discard flag will be reset when render pass starts. Bug: angleproject:4144 Change-Id: I7c6c96862892f1c241ce4af3b61862fa4b710a94 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1924101 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarJonah Ryan-Davis <jonahr@google.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent 7c95d081
...@@ -363,6 +363,12 @@ class ContextMtl : public ContextImpl, public mtl::Context ...@@ -363,6 +363,12 @@ class ContextMtl : public ContextImpl, public mtl::Context
VertexArrayMtl *mVertexArray = nullptr; VertexArrayMtl *mVertexArray = nullptr;
ProgramMtl *mProgram = nullptr; ProgramMtl *mProgram = nullptr;
// Special flag to indicate current draw framebuffer is default framebuffer.
// We need this instead of calling mDrawFramebuffer->getState().isDefault() because
// mDrawFramebuffer might point to a deleted object, ContextMtl only knows about this very late,
// only during syncState() function call.
bool mDrawFramebufferIsDefault = true;
using DirtyBits = angle::BitSet<DIRTY_BIT_MAX>; using DirtyBits = angle::BitSet<DIRTY_BIT_MAX>;
gl::AttributesMask mDirtyDefaultAttribsMask; gl::AttributesMask mDirtyDefaultAttribsMask;
......
...@@ -663,7 +663,7 @@ ProgramImpl *ContextMtl::createProgram(const gl::ProgramState &state) ...@@ -663,7 +663,7 @@ ProgramImpl *ContextMtl::createProgram(const gl::ProgramState &state)
// Framebuffer creation // Framebuffer creation
FramebufferImpl *ContextMtl::createFramebuffer(const gl::FramebufferState &state) FramebufferImpl *ContextMtl::createFramebuffer(const gl::FramebufferState &state)
{ {
return new FramebufferMtl(state, false, false); return new FramebufferMtl(state, false);
} }
// Texture creation // Texture creation
...@@ -937,9 +937,16 @@ void ContextMtl::present(const gl::Context *context, id<CAMetalDrawable> present ...@@ -937,9 +937,16 @@ void ContextMtl::present(const gl::Context *context, id<CAMetalDrawable> present
{ {
ensureCommandBufferValid(); ensureCommandBufferValid();
if (hasStartedRenderPass(mDrawFramebuffer)) // Always discard default FBO's depth stencil buffers at the end of the frame:
if (mDrawFramebufferIsDefault && hasStartedRenderPass(mDrawFramebuffer))
{ {
mDrawFramebuffer->onFinishedDrawingToFrameBuffer(context, &mRenderEncoder); constexpr GLenum dsAttachments[] = {GL_DEPTH, GL_STENCIL};
(void)mDrawFramebuffer->invalidate(context, 2, dsAttachments);
endEncoding(false);
// Reset discard flag by notify framebuffer that a new render pass has started.
mDrawFramebuffer->onStartedDrawingToFrameBuffer(context);
} }
endEncoding(false); endEncoding(false);
...@@ -1172,14 +1179,10 @@ void ContextMtl::updateDrawFrameBufferBinding(const gl::Context *context) ...@@ -1172,14 +1179,10 @@ void ContextMtl::updateDrawFrameBufferBinding(const gl::Context *context)
{ {
const gl::State &glState = getState(); const gl::State &glState = getState();
auto oldFrameBuffer = mDrawFramebuffer; mDrawFramebuffer = mtl::GetImpl(glState.getDrawFramebuffer());
mDrawFramebufferIsDefault = mDrawFramebuffer->getState().isDefault();
mDrawFramebuffer = mtl::GetImpl(glState.getDrawFramebuffer());
if (oldFrameBuffer && hasStartedRenderPass(oldFrameBuffer->getRenderPassDesc(this))) mDrawFramebuffer->onStartedDrawingToFrameBuffer(context);
{
oldFrameBuffer->onFinishedDrawingToFrameBuffer(context, &mRenderEncoder);
}
onDrawFrameBufferChange(context, mDrawFramebuffer); onDrawFrameBufferChange(context, mDrawFramebuffer);
} }
......
...@@ -24,9 +24,7 @@ class SurfaceMtl; ...@@ -24,9 +24,7 @@ class SurfaceMtl;
class FramebufferMtl : public FramebufferImpl class FramebufferMtl : public FramebufferImpl
{ {
public: public:
explicit FramebufferMtl(const gl::FramebufferState &state, explicit FramebufferMtl(const gl::FramebufferState &state, bool flipY);
bool flipY,
bool alwaysDiscardDepthStencil);
~FramebufferMtl() override; ~FramebufferMtl() override;
void destroy(const gl::Context *context) override; void destroy(const gl::Context *context) override;
...@@ -91,9 +89,8 @@ class FramebufferMtl : public FramebufferImpl ...@@ -91,9 +89,8 @@ class FramebufferMtl : public FramebufferImpl
const mtl::RenderPassDesc &getRenderPassDesc(ContextMtl *context); const mtl::RenderPassDesc &getRenderPassDesc(ContextMtl *context);
// Call this to notify FramebufferMtl whenever its render pass has ended. // Call this to notify FramebufferMtl whenever its render pass has started.
void onFinishedDrawingToFrameBuffer(const gl::Context *context, void onStartedDrawingToFrameBuffer(const gl::Context *context);
mtl::RenderCommandEncoder *encoder);
// The actual area will be adjusted based on framebuffer flipping property. // The actual area will be adjusted based on framebuffer flipping property.
gl::Rectangle getReadPixelArea(const gl::Rectangle &glArea); gl::Rectangle getReadPixelArea(const gl::Rectangle &glArea);
...@@ -140,13 +137,9 @@ class FramebufferMtl : public FramebufferImpl ...@@ -140,13 +137,9 @@ class FramebufferMtl : public FramebufferImpl
// depth & stencil attachments as of now. Separate depth & stencil could be useful to // depth & stencil attachments as of now. Separate depth & stencil could be useful to
// save spaces on iOS devices. See doc/PackedDepthStencilSupport.md. // save spaces on iOS devices. See doc/PackedDepthStencilSupport.md.
std::array<RenderTargetMtl *, mtl::kMaxRenderTargets> mColorRenderTargets; std::array<RenderTargetMtl *, mtl::kMaxRenderTargets> mColorRenderTargets;
std::array<bool, mtl::kMaxRenderTargets> mDiscardColors;
RenderTargetMtl *mDepthRenderTarget = nullptr; RenderTargetMtl *mDepthRenderTarget = nullptr;
bool mDiscardDepth = false;
RenderTargetMtl *mStencilRenderTarget = nullptr; RenderTargetMtl *mStencilRenderTarget = nullptr;
bool mDiscardStencil = false;
mtl::RenderPassDesc mRenderPassDesc; mtl::RenderPassDesc mRenderPassDesc;
const bool mAlwaysDiscardDepthStencil;
const bool mFlipY = false; const bool mFlipY = false;
}; };
} // namespace rx } // namespace rx
......
...@@ -274,7 +274,7 @@ egl::Error SurfaceMtl::initialize(const egl::Display *display) ...@@ -274,7 +274,7 @@ egl::Error SurfaceMtl::initialize(const egl::Display *display)
FramebufferImpl *SurfaceMtl::createDefaultFramebuffer(const gl::Context *context, FramebufferImpl *SurfaceMtl::createDefaultFramebuffer(const gl::Context *context,
const gl::FramebufferState &state) const gl::FramebufferState &state)
{ {
auto fbo = new FramebufferMtl(state, /* flipY */ true, /* alwaysDiscard */ true); auto fbo = new FramebufferMtl(state, /* flipY */ true);
return fbo; return fbo;
} }
......
...@@ -236,6 +236,8 @@ class RenderCommandEncoder final : public CommandEncoder ...@@ -236,6 +236,8 @@ class RenderCommandEncoder final : public CommandEncoder
RenderCommandEncoder &setDepthStencilStoreAction(MTLStoreAction depthStoreAction, RenderCommandEncoder &setDepthStencilStoreAction(MTLStoreAction depthStoreAction,
MTLStoreAction stencilStoreAction); MTLStoreAction stencilStoreAction);
RenderCommandEncoder &setDepthStoreAction(MTLStoreAction action);
RenderCommandEncoder &setStencilStoreAction(MTLStoreAction action);
const RenderPassDesc &renderPassDesc() const { return mRenderPassDesc; } const RenderPassDesc &renderPassDesc() const { return mRenderPassDesc; }
......
...@@ -753,6 +753,22 @@ RenderCommandEncoder &RenderCommandEncoder::setDepthStencilStoreAction( ...@@ -753,6 +753,22 @@ RenderCommandEncoder &RenderCommandEncoder::setDepthStencilStoreAction(
return *this; return *this;
} }
RenderCommandEncoder &RenderCommandEncoder::setDepthStoreAction(MTLStoreAction action)
{
// We only store the options, will defer the actual setting until the encoder finishes
mRenderPassDesc.depthAttachment.storeAction = action;
return *this;
}
RenderCommandEncoder &RenderCommandEncoder::setStencilStoreAction(MTLStoreAction action)
{
// We only store the options, will defer the actual setting until the encoder finishes
mRenderPassDesc.stencilAttachment.storeAction = action;
return *this;
}
// BlitCommandEncoder // BlitCommandEncoder
BlitCommandEncoder::BlitCommandEncoder(CommandBuffer *cmdBuffer) : CommandEncoder(cmdBuffer, BLIT) BlitCommandEncoder::BlitCommandEncoder(CommandBuffer *cmdBuffer) : CommandEncoder(cmdBuffer, BLIT)
{} {}
......
...@@ -26,7 +26,10 @@ class DiscardFramebufferEXTTest : public ANGLETest ...@@ -26,7 +26,10 @@ class DiscardFramebufferEXTTest : public ANGLETest
TEST_P(DiscardFramebufferEXTTest, DefaultFramebuffer) TEST_P(DiscardFramebufferEXTTest, DefaultFramebuffer)
{ {
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("EXT_discard_framebuffer")); ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_discard_framebuffer"));
// TODO: fix crash issue. http://anglebug.com/4141
ANGLE_SKIP_TEST_IF(IsD3D11());
// These should succeed on the default framebuffer // These should succeed on the default framebuffer
const GLenum discards1[] = {GL_COLOR_EXT}; const GLenum discards1[] = {GL_COLOR_EXT};
...@@ -61,7 +64,7 @@ TEST_P(DiscardFramebufferEXTTest, DefaultFramebuffer) ...@@ -61,7 +64,7 @@ TEST_P(DiscardFramebufferEXTTest, DefaultFramebuffer)
TEST_P(DiscardFramebufferEXTTest, NonDefaultFramebuffer) TEST_P(DiscardFramebufferEXTTest, NonDefaultFramebuffer)
{ {
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("EXT_discard_framebuffer")); ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_discard_framebuffer"));
GLuint tex2D; GLuint tex2D;
GLuint framebuffer; GLuint framebuffer;
......
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