| 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;
|
| }
|
|
|
|
|