Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(4)

Unified Diff: third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridge.cpp

Issue 1507863005: [2D Canvas] Send GPU resources into hibernation when page is not visible (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fix tests Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridge.cpp
diff --git a/third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridge.cpp b/third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridge.cpp
index 3f16b1f996f41bf9fa54050e0a78f9de1c3e0fd3..7444dc4d4e56fece40ff27a6af1dc4db9a9727a5 100644
--- a/third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridge.cpp
+++ b/third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridge.cpp
@@ -37,6 +37,8 @@
#include "public/platform/WebCompositorSupport.h"
#include "public/platform/WebGraphicsContext3D.h"
#include "public/platform/WebGraphicsContext3DProvider.h"
+#include "public/platform/WebScheduler.h"
+#include "public/platform/WebTraceLocation.h"
#include "third_party/skia/include/core/SkData.h"
#include "third_party/skia/include/core/SkPictureRecorder.h"
#include "third_party/skia/include/core/SkSurface.h"
@@ -56,7 +58,8 @@ WTF::RefCountedLeakCounter& canvas2DLayerBridgeInstanceCounter()
return staticCanvas2DLayerBridgeInstanceCounter;
}
#endif
-}
+
+} // unnamed
namespace blink {
@@ -103,6 +106,8 @@ PassRefPtr<Canvas2DLayerBridge> Canvas2DLayerBridge::create(const IntSize& size,
Canvas2DLayerBridge::Canvas2DLayerBridge(PassOwnPtr<WebGraphicsContext3DProvider> contextProvider, const IntSize& size, int msaaSampleCount, OpacityMode opacityMode, AccelerationMode accelerationMode)
: m_contextProvider(contextProvider)
+ , m_logger(adoptPtr(new Logger))
+ , m_weakPtrFactory(this)
, m_imageBuffer(0)
, m_msaaSampleCount(msaaSampleCount)
, m_bytesAllocated(0)
@@ -113,6 +118,7 @@ Canvas2DLayerBridge::Canvas2DLayerBridge(PassOwnPtr<WebGraphicsContext3DProvider
, m_isDeferralEnabled(true)
, m_isRegisteredTaskObserver(false)
, m_renderingTaskCompletedForCurrentFrame(false)
+ , m_softwareRenderingWhileHidden(false)
, m_lastImageId(0)
, m_lastFilter(GL_LINEAR)
, m_accelerationMode(accelerationMode)
@@ -149,10 +155,17 @@ void Canvas2DLayerBridge::startRecording()
m_recordingPixelCount = 0;
}
+void Canvas2DLayerBridge::setLoggerForTesting(PassOwnPtr<Logger> logger)
+{
+ m_logger = logger;
+}
+
bool Canvas2DLayerBridge::shouldAccelerate(AccelerationHint hint) const
{
bool accelerate;
- if (m_accelerationMode == ForceAccelerationForTesting)
+ if (m_softwareRenderingWhileHidden)
+ accelerate = false;
+ else if (m_accelerationMode == ForceAccelerationForTesting)
accelerate = true;
else if (m_accelerationMode == DisableAcceleration)
accelerate = false;
@@ -166,6 +179,10 @@ bool Canvas2DLayerBridge::shouldAccelerate(AccelerationHint hint) const
bool Canvas2DLayerBridge::isAccelerated() const
{
+ if (isHibernating())
+ return false;
+ if (m_softwareRenderingWhileHidden)
+ return false;
if (m_layer) // We don't check m_surface, so this returns true if context was lost (m_surface is null) with restoration pending.
return true;
if (m_surface) // && !m_layer is implied
@@ -177,25 +194,99 @@ bool Canvas2DLayerBridge::isAccelerated() const
return shouldAccelerate(PreferAcceleration);
}
+static void hibernateWrapper(WeakPtr<Canvas2DLayerBridge> bridge, double /*idleDeadline*/)
+{
+ if (bridge) {
+ bridge->hibernate();
+ } else {
+ Canvas2DLayerBridge::Logger localLogger;
+ localLogger.reportHibernationEvent(Canvas2DLayerBridge::HibernationAbortedDueToDestructionWhileHibernatePending);
+ }
+}
+
+void Canvas2DLayerBridge::hibernate()
+{
+ ASSERT(!isHibernating());
+ if (m_destructionInProgress) {
+ m_logger->reportHibernationEvent(HibernationAbortedDueToPendingDestruction);
+ return;
+ }
+
+ if (!isHidden()) {
+ m_logger->reportHibernationEvent(HibernationAbortedDueToVisibilityChange);
+ return;
+ }
+
+ if (!checkSurfaceValid()) {
+ m_logger->reportHibernationEvent(HibernationAbortedDueGpuContextLoss);
+ return;
+ }
+
+ if (!isAccelerated()) {
+ m_logger->reportHibernationEvent(HibernationAbortedDueToSwitchToUnacceleratedRendering);
+ return;
+ }
+
+ TRACE_EVENT0("cc", "Canvas2DLayerBridge::hibernate");
+ RefPtr<SkSurface> tempHibernationSurface = adoptRef(SkSurface::NewRasterN32Premul(m_size.width(), m_size.height()));
+ if (tempHibernationSurface) {
+ // No HibernationEvent reported on success. This is on purppose to avoid
+ // non-complementary stats. Each HibernationScheduled event is paired with
+ // exactly one failure or exit event.
+ flushRecordingOnly();
+ SkPaint copyPaint;
+ copyPaint.setXfermodeMode(SkXfermode::kSrc_Mode);
+ m_surface->draw(tempHibernationSurface->getCanvas(), 0, 0, &copyPaint); // GPU readback
+ m_hibernationImage = adoptRef(tempHibernationSurface->newImageSnapshot());
+ m_surface.clear(); // destroy the GPU-backed buffer
+ m_layer->clearTexture();
+ m_logger->didStartHibernating();
+ } else {
+ m_logger->reportHibernationEvent(HibernationAbortedDueToAllocationFailure);
+ }
+}
+
SkSurface* Canvas2DLayerBridge::getOrCreateSurface(AccelerationHint hint)
{
- if (!m_surface) {
- if (m_layer)
- return nullptr; // recreation will happen through restore()
+ if (m_surface)
+ return m_surface.get();
- bool wantAccelerated = shouldAccelerate(hint);
- bool surfaceIsAccelerated;
+ if (m_layer && !isHibernating())
+ return nullptr; // re-creation will happen through restore()
- m_surface = createSkSurface(wantAccelerated ? m_contextProvider->grContext() : nullptr, m_size, m_msaaSampleCount, m_opacityMode, &surfaceIsAccelerated);
+ bool wantAcceleration = shouldAccelerate(hint);
+ bool surfaceIsAccelerated;
- if (m_surface && surfaceIsAccelerated && !m_layer) {
- m_layer = adoptPtr(Platform::current()->compositorSupport()->createExternalTextureLayer(this));
- m_layer->setOpaque(m_opacityMode == Opaque);
- m_layer->setBlendBackgroundColor(m_opacityMode != Opaque);
- GraphicsLayer::registerContentsLayer(m_layer->layer());
- m_layer->setNearestNeighbor(m_filterQuality == kNone_SkFilterQuality);
+ if (isHidden() && wantAcceleration) {
+ wantAcceleration = false;
+ m_softwareRenderingWhileHidden = true;
+ }
+
+ m_surface = createSkSurface(wantAcceleration ? m_contextProvider->grContext() : nullptr, m_size, m_msaaSampleCount, m_opacityMode, &surfaceIsAccelerated);
+
+ if (m_surface && surfaceIsAccelerated && !m_layer) {
+ m_layer = adoptPtr(Platform::current()->compositorSupport()->createExternalTextureLayer(this));
+ m_layer->setOpaque(m_opacityMode == Opaque);
+ m_layer->setBlendBackgroundColor(m_opacityMode != Opaque);
+ GraphicsLayer::registerContentsLayer(m_layer->layer());
+ m_layer->setNearestNeighbor(m_filterQuality == kNone_SkFilterQuality);
+ }
+
+ if (m_surface && isHibernating()) {
+ if (surfaceIsAccelerated) {
+ m_logger->reportHibernationEvent(HibernationEndedNormally);
+ } else {
+ if (isHidden())
+ m_logger->reportHibernationEvent(HibernationEndedWithSwitchToBackgroundRendering);
+ else
+ m_logger->reportHibernationEvent(HibernationEndedWithFallbackToSW);
}
+ SkPaint copyPaint;
+ copyPaint.setXfermodeMode(SkXfermode::kSrc_Mode);
+ m_surface->getCanvas()->drawImage(m_hibernationImage.get(), 0, 0, &copyPaint);
+ m_hibernationImage.clear();
+
if (m_imageBuffer)
m_imageBuffer->updateGPUMemoryUsage();
}
@@ -229,7 +320,8 @@ void Canvas2DLayerBridge::disableDeferral()
flushRecordingOnly();
m_recorder.clear();
// install the current matrix/clip stack onto the immediate canvas
- m_imageBuffer->resetCanvas(getOrCreateSurface()->getCanvas());
+ if (m_imageBuffer)
+ m_imageBuffer->resetCanvas(getOrCreateSurface()->getCanvas());
}
void Canvas2DLayerBridge::setImageBuffer(ImageBuffer* imageBuffer)
@@ -242,7 +334,11 @@ void Canvas2DLayerBridge::setImageBuffer(ImageBuffer* imageBuffer)
void Canvas2DLayerBridge::beginDestruction()
{
- ASSERT(!m_destructionInProgress);
+ if (m_destructionInProgress)
+ return;
+ if (isHibernating())
+ m_logger->reportHibernationEvent(HibernationEndedWithTeardown);
+ m_hibernationImage.clear();
m_recorder.clear();
m_imageBuffer = nullptr;
m_destructionInProgress = true;
@@ -259,6 +355,7 @@ void Canvas2DLayerBridge::beginDestruction()
// virtual/gpu/fast/canvas/canvas-resize-after-paint-without-layout.html
m_layer->layer()->removeFromParent();
}
+
ASSERT(!m_bytesAllocated);
}
@@ -285,8 +382,26 @@ void Canvas2DLayerBridge::setIsHidden(bool hidden)
return;
m_isHidden = newHiddenValue;
- if (isHidden() && !m_destructionInProgress)
- flush();
+ if (m_surface && isHidden() && !m_destructionInProgress) {
+ if (m_layer)
+ m_layer->clearTexture();
+ m_logger->reportHibernationEvent(HibernationScheduled);
+ Platform::current()->currentThread()->scheduler()->postIdleTask(BLINK_FROM_HERE, WTF::bind<double>(&hibernateWrapper, m_weakPtrFactory.createWeakPtr()));
+ }
+ if (!isHidden() && m_softwareRenderingWhileHidden) {
+ flushRecordingOnly();
+ SkPaint copyPaint;
+ copyPaint.setXfermodeMode(SkXfermode::kSrc_Mode);
+ RefPtr<SkSurface> oldSurface = m_surface.release();
+ m_softwareRenderingWhileHidden = false;
+ SkSurface* newSurface = getOrCreateSurface();
+ if (newSurface) {
+ oldSurface->draw(newSurface->getCanvas(), 0, 0, &copyPaint);
+ }
+ }
+ if (!isHidden() && isHibernating()) {
+ getOrCreateSurface(); // Rude awakening
+ }
}
bool Canvas2DLayerBridge::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes, int x, int y)
@@ -417,7 +532,6 @@ bool Canvas2DLayerBridge::restoreSurface()
bool Canvas2DLayerBridge::prepareMailbox(WebExternalTextureMailbox* outMailbox, WebExternalBitmap* bitmap)
{
- ASSERT(isAccelerated());
if (m_destructionInProgress) {
// It can be hit in the following sequence.
// 1. Canvas draws something.
@@ -426,6 +540,13 @@ bool Canvas2DLayerBridge::prepareMailbox(WebExternalTextureMailbox* outMailbox,
// 4. Here.
return false;
}
+ ASSERT(isAccelerated() || isHibernating());
+
+ // if hibernating but not hidden, we want to wake up from
+ // hibernation
+ if (isHibernating() && isHidden())
+ return false;
+
if (bitmap) {
// Using accelerated 2d canvas with software renderer, which
// should only happen in tests that use fake graphics contexts
@@ -511,8 +632,8 @@ bool Canvas2DLayerBridge::prepareMailbox(WebExternalTextureMailbox* outMailbox,
void Canvas2DLayerBridge::mailboxReleased(const WebExternalTextureMailbox& mailbox, bool lostResource)
{
- ASSERT(isAccelerated());
- bool contextLost = !m_surface || m_contextProvider->context3d()->isContextLost();
+ ASSERT(isAccelerated() || isHibernating());
+ bool contextLost = !isHibernating() && (!m_surface || m_contextProvider->context3d()->isContextLost());
ASSERT(m_mailboxes.last().m_parentLayerBridge.get() == this);
// Mailboxes are typically released in FIFO order, so we iterate
@@ -656,4 +777,9 @@ Canvas2DLayerBridge::MailboxInfo::MailboxInfo(const MailboxInfo& other)
m_parentLayerBridge = other.m_parentLayerBridge;
}
+void Canvas2DLayerBridge::Logger::reportHibernationEvent(HibernationEvent event)
+{
+ blink::Platform::current()->histogramEnumeration("Canvas.HibernationEvents", event, HibernationEventCount);
+}
+
} // namespace blink

Powered by Google App Engine
This is Rietveld 408576698