#include "platform/TraceEvent.h"
#include "platform/graphics/Canvas2DLayerManager.h"
#include "platform/graphics/GraphicsLayer.h"
-#include "platform/graphics/gpu/SharedGraphicsContext3D.h"
#include "public/platform/Platform.h"
#include "public/platform/WebCompositorSupport.h"
#include "public/platform/WebGraphicsContext3D.h"
+#include "public/platform/WebGraphicsContext3DProvider.h"
using blink::WebExternalTextureLayer;
using blink::WebGraphicsContext3D;
+namespace {
+enum {
+ InvalidMailboxIndex = -1,
+};
+}
+
namespace WebCore {
-static PassRefPtr<SkSurface> createSkSurface(GraphicsContext3D* context3D, const IntSize& size, int msaaSampleCount)
+static PassRefPtr<SkSurface> createSkSurface(GrContext* gr, const IntSize& size, int msaaSampleCount = 0)
{
- ASSERT(!context3D->webContext()->isContextLost());
- GrContext* gr = context3D->grContext();
if (!gr)
return 0;
gr->resetContext();
PassRefPtr<Canvas2DLayerBridge> Canvas2DLayerBridge::create(const IntSize& size, OpacityMode opacityMode, int msaaSampleCount)
{
TRACE_EVENT_INSTANT0("test_gpu", "Canvas2DLayerBridgeCreation");
- RefPtr<GraphicsContext3D> context = SharedGraphicsContext3D::get();
- RefPtr<SkSurface> surface(createSkSurface(context.get(), size, msaaSampleCount));
+ OwnPtr<blink::WebGraphicsContext3DProvider> contextProvider = adoptPtr(blink::Platform::current()->createSharedOffscreenGraphicsContext3DProvider());
+ if (!contextProvider)
+ return 0;
+ RefPtr<SkSurface> surface(createSkSurface(contextProvider->grContext(), size, msaaSampleCount));
if (!surface)
return 0;
RefPtr<Canvas2DLayerBridge> layerBridge;
OwnPtr<SkDeferredCanvas> canvas = adoptPtr(SkDeferredCanvas::Create(surface.get()));
- layerBridge = adoptRef(new Canvas2DLayerBridge(context, canvas.release(), msaaSampleCount, opacityMode));
+ layerBridge = adoptRef(new Canvas2DLayerBridge(contextProvider.release(), canvas.release(), msaaSampleCount, opacityMode));
return layerBridge.release();
}
-Canvas2DLayerBridge::Canvas2DLayerBridge(PassRefPtr<GraphicsContext3D> context, PassOwnPtr<SkDeferredCanvas> canvas, int msaaSampleCount, OpacityMode opacityMode)
+Canvas2DLayerBridge::Canvas2DLayerBridge(PassOwnPtr<blink::WebGraphicsContext3DProvider> contextProvider, PassOwnPtr<SkDeferredCanvas> canvas, int msaaSampleCount, OpacityMode opacityMode)
: m_canvas(canvas)
- , m_context(context)
+ , m_contextProvider(contextProvider)
, m_msaaSampleCount(msaaSampleCount)
, m_bytesAllocated(0)
, 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_releasedMailboxInfoIndex(InvalidMailboxIndex)
{
ASSERT(m_canvas);
+ ASSERT(m_contextProvider);
// Used by browser tests to detect the use of a Canvas2DLayerBridge.
TRACE_EVENT_INSTANT0("test_gpu", "Canvas2DLayerBridgeCreation");
m_layer = adoptPtr(blink::Platform::current()->compositorSupport()->createExternalTextureLayer(this));
{
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();
}
{
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;
flush();
}
}
+ ++m_framesSinceMailboxRelease;
+ if (releasedMailboxHasExpired()) {
+ freeReleasedMailbox();
+ }
}
void Canvas2DLayerBridge::prepareForDraw()
{
- ASSERT(!m_destructionInProgress);
ASSERT(m_layer);
if (!isValid()) {
if (m_canvas) {
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);
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 hasReleasedMailbox() && m_framesSinceMailboxRelease > 2;
+}
+
+Canvas2DLayerBridge::MailboxInfo* Canvas2DLayerBridge::releasedMailboxInfo()
+{
+ return hasReleasedMailbox() ? &m_mailboxes[m_releasedMailboxInfoIndex] : 0;
+}
+
+bool Canvas2DLayerBridge::hasReleasedMailbox() const
+{
+ return m_releasedMailboxInfoIndex != InvalidMailboxIndex;
+}
+
+void Canvas2DLayerBridge::freeReleasedMailbox()
+{
+ MailboxInfo* mailboxInfo = releasedMailboxInfo();
+ if (!mailboxInfo)
+ return;
+
+ ASSERT(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.
+ if (mailboxInfo->m_image) {
+ if (isHidden() || releasedMailboxHasExpired())
+ mailboxInfo->m_image->getTexture()->resetFlag(static_cast<GrTextureFlags>(GrTexture::kReturnToCache_FlagBit));
+ mailboxInfo->m_image->getTexture()->invalidateCachedState();
+ mailboxInfo->m_image.clear();
+ }
+ mailboxInfo->m_status = MailboxAvailable;
+ m_releasedMailboxInfoIndex = InvalidMailboxIndex;
+ Canvas2DLayerManager::get().layerTransientResourceAllocationChanged(this);
+}
+
blink::WebGraphicsContext3D* Canvas2DLayerBridge::context()
{
// Check on m_layer is necessary because context() may be called during
if (m_layer) {
isValid(); // To ensure rate limiter is disabled if context is lost.
}
- return m_context->webContext();
+ return m_contextProvider->context3d();
}
bool Canvas2DLayerBridge::isValid()
ASSERT(m_layer);
if (m_destructionInProgress)
return false;
- if (m_context->webContext()->isContextLost() || !m_surfaceIsValid) {
+ if (m_contextProvider->context3d()->isContextLost() || !m_surfaceIsValid) {
// Attempt to recover.
+ blink::WebGraphicsContext3D* sharedContext = 0;
m_layer->clearTexture();
m_mailboxes.clear();
- RefPtr<GraphicsContext3D> sharedContext = SharedGraphicsContext3D::get();
- if (!sharedContext || sharedContext->webContext()->isContextLost()) {
+ m_releasedMailboxInfoIndex = InvalidMailboxIndex;
+ m_contextProvider = adoptPtr(blink::Platform::current()->createSharedOffscreenGraphicsContext3DProvider());
+ if (m_contextProvider)
+ sharedContext = m_contextProvider->context3d();
+
+ if (!sharedContext || sharedContext->isContextLost()) {
m_surfaceIsValid = false;
} else {
- m_context = sharedContext;
IntSize size(m_canvas->getTopDevice()->width(), m_canvas->getTopDevice()->height());
- RefPtr<SkSurface> surface(createSkSurface(m_context.get(), size, m_msaaSampleCount));
+ RefPtr<SkSurface> surface(createSkSurface(m_contextProvider->grContext(), size, m_msaaSampleCount));
if (surface.get()) {
m_canvas->setSurface(surface.get());
m_surfaceIsValid = true;
// 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.
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.
- m_context->grContext()->resetContext(kTextureBinding_GrGLBackendState);
+ m_contextProvider->grContext()->resetContext(kTextureBinding_GrGLBackendState);
// set m_parentLayerBridge to make sure 'this' stays alive as long as it has
// live mailboxes
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))) {
// Trigger Canvas2DLayerBridge self-destruction if this is the
// last live mailbox and the layer bridge is not externally
// referenced.
+ m_releasedMailboxInfoIndex = mailboxInfo - m_mailboxes.begin();
+ 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;
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;
}