Chromium Code Reviews| Index: Source/platform/graphics/Canvas2DLayerBridge.cpp |
| diff --git a/Source/platform/graphics/Canvas2DLayerBridge.cpp b/Source/platform/graphics/Canvas2DLayerBridge.cpp |
| index 7767b6b184ff7e3eb1814eb4cad69e56f45d5ce4..df8c062d4fbb06ace5847b25e93d9e8f1c1e5970 100644 |
| --- a/Source/platform/graphics/Canvas2DLayerBridge.cpp |
| +++ b/Source/platform/graphics/Canvas2DLayerBridge.cpp |
| @@ -43,7 +43,7 @@ using blink::WebGraphicsContext3D; |
| namespace WebCore { |
| -static PassRefPtr<SkSurface> createSkSurface(GraphicsContext3D* context3D, const IntSize& size, int msaaSampleCount) |
| +static PassRefPtr<SkSurface> createSkSurface(GraphicsContext3D* context3D, const IntSize& size, int msaaSampleCount = 0) |
| { |
| ASSERT(!context3D->webContext()->isContextLost()); |
| GrContext* gr = context3D->grContext(); |
| @@ -79,11 +79,14 @@ Canvas2DLayerBridge::Canvas2DLayerBridge(PassRefPtr<GraphicsContext3D> context, |
| , m_didRecordDrawCommand(false) |
| , m_surfaceIsValid(true) |
| , m_framesPending(0) |
| + , m_framesSinceMailboxRelease(0) |
| , m_destructionInProgress(false) |
| , m_rateLimitingEnabled(false) |
| + , m_isHidden(false) |
| , m_next(0) |
| , m_prev(0) |
| , m_lastImageId(0) |
| + , m_releasedMailboxInfo(0) |
| { |
| ASSERT(m_canvas); |
| // Used by browser tests to detect the use of a Canvas2DLayerBridge. |
| @@ -100,18 +103,14 @@ Canvas2DLayerBridge::~Canvas2DLayerBridge() |
| { |
| ASSERT(m_destructionInProgress); |
| m_layer.clear(); |
| + freeReleasedMailbox(); |
| +#if !ASSERT_DISABLED |
| Vector<MailboxInfo>::iterator mailboxInfo; |
| for (mailboxInfo = m_mailboxes.begin(); mailboxInfo < m_mailboxes.end(); ++mailboxInfo) { |
| ASSERT(mailboxInfo->m_status != MailboxInUse); |
| - if (mailboxInfo->m_status == MailboxReleased) { |
| - if (mailboxInfo->m_mailbox.syncPoint) { |
| - context()->waitSyncPoint(mailboxInfo->m_mailbox.syncPoint); |
| - mailboxInfo->m_mailbox.syncPoint = 0; |
| - } |
| - // Invalidate texture state in case the compositor altered it since the copy-on-write. |
| - mailboxInfo->m_image->getTexture()->invalidateCachedState(); |
| - } |
| + ASSERT(mailboxInfo->m_status != MailboxReleased); |
| } |
| +#endif |
| m_mailboxes.clear(); |
| } |
| @@ -119,19 +118,49 @@ void Canvas2DLayerBridge::beginDestruction() |
| { |
| ASSERT(!m_destructionInProgress); |
| m_destructionInProgress = true; |
| + freeTransientResources(); |
| + setIsHidden(true); |
| GraphicsLayer::unregisterContentsLayer(m_layer->layer()); |
| m_canvas->setNotificationClient(0); |
| m_layer->clearTexture(); |
| - Canvas2DLayerManager::get().layerToBeDestroyed(this); |
| // Orphaning the layer is required to trigger the recration of a new layer |
| // in the case where destruction is caused by a canvas resize. Test: |
| // virtual/gpu/fast/canvas/canvas-resize-after-paint-without-layout.html |
| m_layer->layer()->removeFromParent(); |
| } |
| +void Canvas2DLayerBridge::setIsHidden(bool hidden) |
| +{ |
| + bool newHiddenValue = hidden || m_destructionInProgress; |
| + if (m_isHidden == newHiddenValue) |
| + return; |
| + |
| + m_isHidden = newHiddenValue; |
| + if (isHidden()) { |
| + freeTransientResources(); |
| + } |
| +} |
| + |
| +void Canvas2DLayerBridge::freeTransientResources() |
| +{ |
| + freeReleasedMailbox(); |
| + flush(); |
| + freeMemoryIfPossible(bytesAllocated()); |
| + ASSERT(!hasTransientResources()); |
| +} |
| + |
| +bool Canvas2DLayerBridge::hasTransientResources() const |
| +{ |
| + return hasReleasedMailbox() || bytesAllocated(); |
| +} |
| + |
| void Canvas2DLayerBridge::limitPendingFrames() |
| { |
| ASSERT(!m_destructionInProgress); |
| + if (isHidden()) { |
| + freeTransientResources(); |
| + return; |
| + } |
| if (m_didRecordDrawCommand) { |
| m_framesPending++; |
| m_didRecordDrawCommand = false; |
| @@ -144,11 +173,14 @@ void Canvas2DLayerBridge::limitPendingFrames() |
| flush(); |
| } |
| } |
| + ++m_framesSinceMailboxRelease; |
| + if (releasedMailboxHasExpired()) { |
| + freeReleasedMailbox(); |
| + } |
| } |
| void Canvas2DLayerBridge::prepareForDraw() |
| { |
| - ASSERT(!m_destructionInProgress); |
| ASSERT(m_layer); |
| if (!isValid()) { |
| if (m_canvas) { |
| @@ -162,28 +194,24 @@ void Canvas2DLayerBridge::prepareForDraw() |
| void Canvas2DLayerBridge::storageAllocatedForRecordingChanged(size_t bytesAllocated) |
| { |
| - ASSERT(!m_destructionInProgress); |
| intptr_t delta = (intptr_t)bytesAllocated - (intptr_t)m_bytesAllocated; |
| m_bytesAllocated = bytesAllocated; |
| - Canvas2DLayerManager::get().layerAllocatedStorageChanged(this, delta); |
| + Canvas2DLayerManager::get().layerTransientResourceAllocationChanged(this, delta); |
| } |
| size_t Canvas2DLayerBridge::storageAllocatedForRecording() |
| { |
| - ASSERT(!m_destructionInProgress); |
| return m_canvas->storageAllocatedForRecording(); |
| } |
| void Canvas2DLayerBridge::flushedDrawCommands() |
| { |
| - ASSERT(!m_destructionInProgress); |
| storageAllocatedForRecordingChanged(storageAllocatedForRecording()); |
| m_framesPending = 0; |
| } |
| void Canvas2DLayerBridge::skippedPendingDrawCommands() |
| { |
| - ASSERT(!m_destructionInProgress); |
| // Stop triggering the rate limiter if SkDeferredCanvas is detecting |
| // and optimizing overdraw. |
| setRateLimitingEnabled(false); |
| @@ -201,23 +229,51 @@ void Canvas2DLayerBridge::setRateLimitingEnabled(bool enabled) |
| size_t Canvas2DLayerBridge::freeMemoryIfPossible(size_t bytesToFree) |
| { |
| - ASSERT(!m_destructionInProgress); |
| size_t bytesFreed = m_canvas->freeMemoryIfPossible(bytesToFree); |
| - if (bytesFreed) |
| - Canvas2DLayerManager::get().layerAllocatedStorageChanged(this, -((intptr_t)bytesFreed)); |
| m_bytesAllocated -= bytesFreed; |
| + if (bytesFreed) |
| + Canvas2DLayerManager::get().layerTransientResourceAllocationChanged(this, -((intptr_t)bytesFreed)); |
| return bytesFreed; |
| } |
| void Canvas2DLayerBridge::flush() |
| { |
| - ASSERT(!m_destructionInProgress); |
| if (m_canvas->hasPendingCommands()) { |
| TRACE_EVENT0("cc", "Canvas2DLayerBridge::flush"); |
| + freeReleasedMailbox(); // To avoid unnecessary triple-buffering |
| m_canvas->flush(); |
| } |
| } |
| +bool Canvas2DLayerBridge::releasedMailboxHasExpired() |
| +{ |
| + // This heuristic indicates that the canvas is not being |
| + // acitvely presented by the compositor, suggesting that it does |
|
Stephen White
2014/01/27 18:55:14
Nit: acitvely -> actively
Perhaps we should expla
|
| + // not require double buffering. |
| + return m_releasedMailboxInfo && m_framesSinceMailboxRelease > 2; |
| +} |
| + |
| +void Canvas2DLayerBridge::freeReleasedMailbox() |
| +{ |
| + if (m_releasedMailboxInfo) { |
| + ASSERT(m_releasedMailboxInfo->m_status == MailboxReleased); |
| + if (m_releasedMailboxInfo->m_mailbox.syncPoint) { |
| + context()->waitSyncPoint(m_releasedMailboxInfo->m_mailbox.syncPoint); |
| + m_releasedMailboxInfo->m_mailbox.syncPoint = 0; |
| + } |
| + // Invalidate texture state in case the compositor altered it since the copy-on-write. |
| + if (m_releasedMailboxInfo->m_image) { |
| + if (isHidden() || releasedMailboxHasExpired()) |
| + m_releasedMailboxInfo->m_image->getTexture()->resetFlag(static_cast<GrTextureFlags>(GrTexture::kReturnToCache_FlagBit)); |
| + m_releasedMailboxInfo->m_image->getTexture()->invalidateCachedState(); |
| + m_releasedMailboxInfo->m_image.clear(); |
| + } |
| + m_releasedMailboxInfo->m_status = MailboxAvailable; |
| + m_releasedMailboxInfo = 0; |
| + Canvas2DLayerManager::get().layerTransientResourceAllocationChanged(this); |
| + } |
| +} |
| + |
| blink::WebGraphicsContext3D* Canvas2DLayerBridge::context() |
| { |
| // Check on m_layer is necessary because context() may be called during |
| @@ -280,28 +336,18 @@ bool Canvas2DLayerBridge::prepareMailbox(blink::WebExternalTextureMailbox* outMa |
| // order to cap maximum gpu memory consumption. |
| webContext->makeContextCurrent(); |
| flush(); |
| - Vector<MailboxInfo>::iterator mailboxInfo; |
| - for (mailboxInfo = m_mailboxes.begin(); mailboxInfo < m_mailboxes.end(); ++mailboxInfo) { |
| - if (mailboxInfo->m_status == MailboxReleased) { |
| - if (mailboxInfo->m_mailbox.syncPoint) { |
| - webContext->waitSyncPoint(mailboxInfo->m_mailbox.syncPoint); |
| - mailboxInfo->m_mailbox.syncPoint = 0; |
| - } |
| - // Invalidate texture state in case the compositor altered it since the copy-on-write. |
| - mailboxInfo->m_image->getTexture()->invalidateCachedState(); |
| - mailboxInfo->m_image.reset(0); |
| - mailboxInfo->m_status = MailboxAvailable; |
| - } |
| - } |
| - SkAutoTUnref<SkImage> image(m_canvas->newImageSnapshot()); |
| + |
| + RefPtr<SkImage> image = adoptRef(m_canvas->newImageSnapshot()); |
| + |
| // Early exit if canvas was not drawn to since last prepareMailbox |
| if (image->uniqueID() == m_lastImageId) |
| return false; |
| m_lastImageId = image->uniqueID(); |
| - mailboxInfo = createMailboxInfo(); |
| + MailboxInfo* mailboxInfo = createMailboxInfo(); |
| mailboxInfo->m_status = MailboxInUse; |
| - mailboxInfo->m_image.swap(&image); |
| + mailboxInfo->m_image = image; |
| + |
| // Because of texture sharing with the compositor, we must invalidate |
| // the state cached in skia so that the deferred copy on write |
| // in SkSurface_Gpu does not make any false assumptions. |
| @@ -317,8 +363,14 @@ bool Canvas2DLayerBridge::prepareMailbox(blink::WebExternalTextureMailbox* outMa |
| webContext->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| webContext->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| webContext->produceTextureCHROMIUM(GL_TEXTURE_2D, mailboxInfo->m_mailbox.name); |
| - webContext->flush(); |
| - mailboxInfo->m_mailbox.syncPoint = webContext->insertSyncPoint(); |
| + if (isHidden()) { |
| + // With hidden canvases, we release the SkImage immediately because |
| + // there is no need for animations to be double buffered. |
| + mailboxInfo->m_image.clear(); |
| + } else { |
| + webContext->flush(); |
| + mailboxInfo->m_mailbox.syncPoint = webContext->insertSyncPoint(); |
| + } |
| webContext->bindTexture(GL_TEXTURE_2D, 0); |
| // Because we are changing the texture binding without going through skia, |
| // we must dirty the context. |
| @@ -358,6 +410,7 @@ Canvas2DLayerBridge::MailboxInfo* Canvas2DLayerBridge::createMailboxInfo() { |
| void Canvas2DLayerBridge::mailboxReleased(const blink::WebExternalTextureMailbox& mailbox) |
| { |
| + freeReleasedMailbox(); // Never have more than one mailbox in the released state. |
| Vector<MailboxInfo>::iterator mailboxInfo; |
| for (mailboxInfo = m_mailboxes.begin(); mailboxInfo < m_mailboxes.end(); ++mailboxInfo) { |
| if (!memcmp(mailboxInfo->m_mailbox.name, mailbox.name, sizeof(mailbox.name))) { |
| @@ -367,6 +420,14 @@ void Canvas2DLayerBridge::mailboxReleased(const blink::WebExternalTextureMailbox |
| // Trigger Canvas2DLayerBridge self-destruction if this is the |
| // last live mailbox and the layer bridge is not externally |
| // referenced. |
| + m_releasedMailboxInfo = mailboxInfo; |
| + m_framesSinceMailboxRelease = 0; |
| + if (isHidden()) { |
| + freeReleasedMailbox(); |
| + } else { |
| + ASSERT(!m_destructionInProgress); |
| + Canvas2DLayerManager::get().layerTransientResourceAllocationChanged(this); |
| + } |
| ASSERT(mailboxInfo->m_parentLayerBridge.get() == this); |
| mailboxInfo->m_parentLayerBridge.clear(); |
| return; |
| @@ -404,10 +465,10 @@ Platform3DObject Canvas2DLayerBridge::getBackingTexture() |
| Canvas2DLayerBridge::MailboxInfo::MailboxInfo(const MailboxInfo& other) { |
| // This copy constructor should only be used for Vector reallocation |
| - // Assuming 'other' is to be destroyed, we swap m_image ownership |
| + // Assuming 'other' is to be destroyed, we transfer m_image ownership |
| // rather than do a refcount dance. |
| memcpy(&m_mailbox, &other.m_mailbox, sizeof(m_mailbox)); |
| - m_image.swap(const_cast<SkAutoTUnref<SkImage>*>(&other.m_image)); |
| + m_image = const_cast<MailboxInfo*>(&other)->m_image.release(); |
| m_status = other.m_status; |
| } |