Commit 0de40b8a authored by junov@chromium.org's avatar junov@chromium.org

Fix crash in Canvas2DLayerBridge after a GPU resource is lost

BUG=411864
TEST=blink unit test Canvas2DLayerBridgeTest.testPrepareMailboxAndLoseResource

Review URL: https://codereview.chromium.org/552423002

git-svn-id: svn://svn.chromium.org/blink/trunk@181679 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent 3751b045
...@@ -424,6 +424,17 @@ bool Canvas2DLayerBridge::prepareMailbox(WebExternalTextureMailbox* outMailbox, ...@@ -424,6 +424,17 @@ bool Canvas2DLayerBridge::prepareMailbox(WebExternalTextureMailbox* outMailbox,
ASSERT(mailboxInfo->m_mailbox.syncPoint == 0); ASSERT(mailboxInfo->m_mailbox.syncPoint == 0);
ASSERT(mailboxInfo->m_image.get()); ASSERT(mailboxInfo->m_image.get());
// set m_parentLayerBridge to make sure 'this' stays alive as long as it has
// live mailboxes
ASSERT(!mailboxInfo->m_parentLayerBridge);
mailboxInfo->m_parentLayerBridge = this;
*outMailbox = mailboxInfo->m_mailbox;
GrContext* grContext = m_contextProvider->grContext();
if (!grContext)
return true; // for testing: skip gl stuff when using a mock graphics context.
ASSERT(mailboxInfo->m_image->getTexture()); ASSERT(mailboxInfo->m_image->getTexture());
// Because of texture sharing with the compositor, we must invalidate // Because of texture sharing with the compositor, we must invalidate
...@@ -448,13 +459,7 @@ bool Canvas2DLayerBridge::prepareMailbox(WebExternalTextureMailbox* outMailbox, ...@@ -448,13 +459,7 @@ bool Canvas2DLayerBridge::prepareMailbox(WebExternalTextureMailbox* outMailbox,
webContext->bindTexture(GL_TEXTURE_2D, 0); webContext->bindTexture(GL_TEXTURE_2D, 0);
// Because we are changing the texture binding without going through skia, // Because we are changing the texture binding without going through skia,
// we must dirty the context. // we must dirty the context.
m_contextProvider->grContext()->resetContext(kTextureBinding_GrGLBackendState); grContext->resetContext(kTextureBinding_GrGLBackendState);
// set m_parentLayerBridge to make sure 'this' stays alive as long as it has
// live mailboxes
ASSERT(!mailboxInfo->m_parentLayerBridge);
mailboxInfo->m_parentLayerBridge = this;
*outMailbox = mailboxInfo->m_mailbox;
return true; return true;
} }
...@@ -505,16 +510,29 @@ void Canvas2DLayerBridge::mailboxReleased(const WebExternalTextureMailbox& mailb ...@@ -505,16 +510,29 @@ void Canvas2DLayerBridge::mailboxReleased(const WebExternalTextureMailbox& mailb
// texture and remove the mailbox from list to avoid reusing it // texture and remove the mailbox from list to avoid reusing it
// in future. // in future.
if (mailboxInfo->m_image) { if (mailboxInfo->m_image) {
mailboxInfo->m_image->getTexture()->resetFlag( GrTexture* texture = mailboxInfo->m_image->getTexture();
static_cast<GrTextureFlags>(GrTexture::kReturnToCache_FlagBit)); if (texture) {
mailboxInfo->m_image->getTexture()->textureParamsModified(); texture->resetFlag(static_cast<GrTextureFlags>(GrTexture::kReturnToCache_FlagBit));
texture->textureParamsModified();
}
mailboxInfo->m_image.clear(); mailboxInfo->m_image.clear();
} }
size_t i = mailboxInfo - m_mailboxes.begin(); if (m_destructionInProgress) {
m_mailboxes.remove(i); mailboxInfo->m_status = MailboxAvailable; // To satisfy assert in destructor
Canvas2DLayerManager::get().layerTransientResourceAllocationChanged(this);
// Here we need to return early since mailboxInfo removal would // The following line may trigger self destruction. We do not care about
// also clear m_parentLayerBridge reference. // not cleaning up m_mailboxes during destruction sequence because
// mailboxes will not be recycled after this point. Calling remove()
// could trigger a memory use after free, so we just clear the self
// reference to be safe, and we let the Canvas2DLayerBridge destructor
// take care of freeing m_mailboxes.
mailboxInfo->m_parentLayerBridge.clear();
} else {
size_t i = mailboxInfo - m_mailboxes.begin();
m_mailboxes.remove(i); // indirectly clears mailboxInfo->m_parentLayerBridge
Canvas2DLayerManager::get().layerTransientResourceAllocationChanged(this);
}
// mailboxInfo is not valid from this point, so we return immediately.
return; return;
} else { } else {
mailboxInfo->m_status = MailboxReleased; mailboxInfo->m_status = MailboxReleased;
......
...@@ -181,6 +181,40 @@ protected: ...@@ -181,6 +181,40 @@ protected:
bridge->prepareMailbox(0, &bitmap); bridge->prepareMailbox(0, &bitmap);
EXPECT_EQ(0u, bridge->m_lastImageId); EXPECT_EQ(0u, bridge->m_lastImageId);
} }
void prepareMailboxAndLoseResourceTest()
{
MockCanvasContext mainMock;
RefPtr<SkSurface> surface = adoptRef(SkSurface::NewRasterPMColor(300, 150));
bool lostResource = true;
// Prepare a mailbox, then report the resource as lost.
// This test passes by not crashing and not triggering assertions.
{
WebExternalTextureMailbox mailbox;
OwnPtr<SkDeferredCanvas> canvas = adoptPtr(SkDeferredCanvas::Create(surface.get()));
OwnPtr<MockWebGraphicsContext3DProvider> mainMockProvider = adoptPtr(new MockWebGraphicsContext3DProvider(&mainMock));
Canvas2DLayerBridgePtr bridge(adoptRef(new Canvas2DLayerBridge(mainMockProvider.release(), canvas.release(), surface, 0, NonOpaque)));
bridge->prepareMailbox(&mailbox, 0);
bridge->mailboxReleased(mailbox, lostResource);
}
// Retry with mailbox released while bridge destruction is in progress
{
OwnPtr<SkDeferredCanvas> canvas = adoptPtr(SkDeferredCanvas::Create(surface.get()));
OwnPtr<MockWebGraphicsContext3DProvider> mainMockProvider = adoptPtr(new MockWebGraphicsContext3DProvider(&mainMock));
WebExternalTextureMailbox mailbox;
Canvas2DLayerBridge* rawBridge;
{
Canvas2DLayerBridgePtr bridge(adoptRef(new Canvas2DLayerBridge(mainMockProvider.release(), canvas.release(), surface, 0, NonOpaque)));
bridge->prepareMailbox(&mailbox, 0);
rawBridge = bridge.get();
} // bridge goes out of scope, but object is kept alive by self references
// before fixing crbug.com/411864, the following line you cause a memory use after free
// that sometimes causes a crash in normal builds and crashes consistently with ASAN.
rawBridge->mailboxReleased(mailbox, lostResource); // This should self-destruct the bridge.
}
}
}; };
namespace { namespace {
...@@ -195,9 +229,14 @@ TEST_F(Canvas2DLayerBridgeTest, testNoDrawOnContextLost) ...@@ -195,9 +229,14 @@ TEST_F(Canvas2DLayerBridgeTest, testNoDrawOnContextLost)
noDrawOnContextLostTest(); noDrawOnContextLostTest();
} }
TEST_F(Canvas2DLayerBridgeTest, prepareMailboxWithBitmapTest) TEST_F(Canvas2DLayerBridgeTest, testPrepareMailboxWithBitmap)
{ {
prepareMailboxWithBitmapTest(); prepareMailboxWithBitmapTest();
} }
TEST_F(Canvas2DLayerBridgeTest, testPrepareMailboxAndLoseResource)
{
prepareMailboxAndLoseResourceTest();
}
} // namespace } // 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