| 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, ©Paint); // 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, ©Paint);
|
| + 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, ©Paint);
|
| + }
|
| + }
|
| + 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
|
|
|