Index: Source/platform/graphics/Canvas2DLayerBridge.cpp |
diff --git a/Source/platform/graphics/Canvas2DLayerBridge.cpp b/Source/platform/graphics/Canvas2DLayerBridge.cpp |
index 7767b6b184ff7e3eb1814eb4cad69e56f45d5ce4..3a4e64f04192c2b2431712601a76081f395d4b36 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 |
+ // actively presented by the compositor (3 frames rendered since |
+ // last mailbox release), suggesting that double buffering is not required. |
+ 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; |
} |