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

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

Issue 1307143005: Make 2D canvas smarter about chosing whether or not to use GPU acceleration (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: migrated to chromium repo Created 5 years, 3 months 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 c498d131a8b8c39a76412af22823aea8b11e5280..7cf921e2f37dec1a80885e884860f5dc1a268c5a 100644
--- a/third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridge.cpp
+++ b/third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridge.cpp
@@ -56,15 +56,26 @@ DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, canvas2DLayerBridgeInstance
namespace blink {
-static PassRefPtr<SkSurface> createSkSurface(GrContext* gr, const IntSize& size, int msaaSampleCount, OpacityMode opacityMode)
+static PassRefPtr<SkSurface> createSkSurface(GrContext* gr, const IntSize& size, int msaaSampleCount, OpacityMode opacityMode, bool* surfaceIsAccelerated)
{
- if (!gr)
- return nullptr;
- gr->resetContext();
- SkImageInfo info = SkImageInfo::MakeN32Premul(size.width(), size.height());
+ if (gr)
+ gr->resetContext();
+
+ SkAlphaType alphaType = (Opaque == opacityMode) ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
+ SkImageInfo info = SkImageInfo::MakeN32(size.width(), size.height(), alphaType);
SkSurfaceProps disableLCDProps(0, kUnknown_SkPixelGeometry);
- RefPtr<SkSurface> surface = adoptRef(SkSurface::NewRenderTarget(gr, SkSurface::kNo_Budgeted, info, msaaSampleCount,
- Opaque == opacityMode ? 0 : &disableLCDProps));
+ RefPtr<SkSurface> surface;
+
+ if (gr) {
+ *surfaceIsAccelerated = true;
+ surface = adoptRef(SkSurface::NewRenderTarget(gr, SkSurface::kNo_Budgeted, info, msaaSampleCount, Opaque == opacityMode ? 0 : &disableLCDProps));
+ }
+
+ if (!surface) {
+ *surfaceIsAccelerated = false;
+ surface = adoptRef(SkSurface::NewRaster(info, Opaque == opacityMode ? 0 : &disableLCDProps));
+ }
+
if (surface) {
if (opacityMode == Opaque) {
surface->getCanvas()->clear(SK_ColorBLACK);
@@ -75,23 +86,19 @@ static PassRefPtr<SkSurface> createSkSurface(GrContext* gr, const IntSize& size,
return surface;
}
-PassRefPtr<Canvas2DLayerBridge> Canvas2DLayerBridge::create(const IntSize& size, OpacityMode opacityMode, int msaaSampleCount)
+PassRefPtr<Canvas2DLayerBridge> Canvas2DLayerBridge::create(const IntSize& size, int msaaSampleCount, OpacityMode opacityMode, AccelerationMode accelerationMode)
{
TRACE_EVENT_INSTANT0("test_gpu", "Canvas2DLayerBridgeCreation", TRACE_EVENT_SCOPE_GLOBAL);
OwnPtr<WebGraphicsContext3DProvider> contextProvider = adoptPtr(Platform::current()->createSharedOffscreenGraphicsContext3DProvider());
if (!contextProvider)
return nullptr;
- RefPtr<SkSurface> surface(createSkSurface(contextProvider->grContext(), size, msaaSampleCount, opacityMode));
- if (!surface)
- return nullptr;
RefPtr<Canvas2DLayerBridge> layerBridge;
- layerBridge = adoptRef(new Canvas2DLayerBridge(contextProvider.release(), surface.release(), msaaSampleCount, opacityMode));
+ layerBridge = adoptRef(new Canvas2DLayerBridge(contextProvider.release(), size, msaaSampleCount, opacityMode, accelerationMode));
return layerBridge.release();
}
-Canvas2DLayerBridge::Canvas2DLayerBridge(PassOwnPtr<WebGraphicsContext3DProvider> contextProvider, PassRefPtr<SkSurface> surface, int msaaSampleCount, OpacityMode opacityMode)
- : m_surface(surface)
- , m_contextProvider(contextProvider)
+Canvas2DLayerBridge::Canvas2DLayerBridge(PassOwnPtr<WebGraphicsContext3DProvider> contextProvider, const IntSize& size, int msaaSampleCount, OpacityMode opacityMode, AccelerationMode accelerationMode)
+ : m_contextProvider(contextProvider)
, m_imageBuffer(0)
, m_msaaSampleCount(msaaSampleCount)
, m_bytesAllocated(0)
@@ -104,19 +111,13 @@ Canvas2DLayerBridge::Canvas2DLayerBridge(PassOwnPtr<WebGraphicsContext3DProvider
, m_renderingTaskCompletedForCurrentFrame(false)
, m_lastImageId(0)
, m_lastFilter(GL_LINEAR)
+ , m_accelerationMode(accelerationMode)
, m_opacityMode(opacityMode)
- , m_size(m_surface->width(), m_surface->height())
+ , m_size(size)
{
- ASSERT(m_surface);
ASSERT(m_contextProvider);
- m_initialSurfaceSaveCount = m_surface->getCanvas()->getSaveCount();
// Used by browser tests to detect the use of a Canvas2DLayerBridge.
TRACE_EVENT_INSTANT0("test_gpu", "Canvas2DLayerBridgeCreation", TRACE_EVENT_SCOPE_GLOBAL);
- m_layer = adoptPtr(Platform::current()->compositorSupport()->createExternalTextureLayer(this));
- m_layer->setOpaque(opacityMode == Opaque);
- m_layer->setBlendBackgroundColor(opacityMode != Opaque);
- GraphicsLayer::registerContentsLayer(m_layer->layer());
- m_layer->setNearestNeighbor(m_filterQuality == kNone_SkFilterQuality);
startRecording();
#ifndef NDEBUG
canvas2DLayerBridgeInstanceCounter.increment();
@@ -144,10 +145,62 @@ void Canvas2DLayerBridge::startRecording()
m_recordingPixelCount = 0;
}
+bool Canvas2DLayerBridge::shouldAccelerate(AccelerationHint hint) const
+{
+ bool accelerate;
+ if (m_accelerationMode == ForceAccelerationForTesting)
+ accelerate = true;
+ else if (m_accelerationMode == DisableAcceleration)
+ accelerate = false;
+ else
+ accelerate = hint == PreferAcceleration;
+
+ if (accelerate && (!m_contextProvider || m_contextProvider->context3d()->isContextLost()))
+ accelerate = false;
+ return accelerate;
+}
+
+bool Canvas2DLayerBridge::isAccelerated() const
+{
+ 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
+ return false;
+
+ // Whether or not to accelerate is not yet resolved, determine whether immediate presentation
+ // of the canvas would result in the canvas being accelerated. Presentation is assumed to be
+ // a 'PreferAcceleration' operation.
+ return shouldAccelerate(PreferAcceleration);
+}
+
+SkSurface* Canvas2DLayerBridge::getOrCreateSurface(AccelerationHint hint)
+{
+ if (!m_surface) {
+ if (m_layer)
+ return nullptr; // recreation will happen through restore()
+
+ bool wantAccelerated = shouldAccelerate(hint);
+ bool surfaceIsAccelerated;
+
+ m_surface = createSkSurface(wantAccelerated ? 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);
+ }
+ }
+ return m_surface.get();
+}
+
SkCanvas* Canvas2DLayerBridge::canvas()
{
- if (!m_isDeferralEnabled)
- return m_surface->getCanvas();
+ if (!m_isDeferralEnabled) {
+ SkSurface* s = getOrCreateSurface();
+ return s ? s->getCanvas() : nullptr;
+ }
return m_recorder->getRecordingCanvas();
}
@@ -169,7 +222,7 @@ void Canvas2DLayerBridge::disableDeferral()
flushRecordingOnly();
m_recorder.clear();
// install the current matrix/clip stack onto the immediate canvas
- m_imageBuffer->resetCanvas(m_surface->getCanvas());
+ m_imageBuffer->resetCanvas(getOrCreateSurface()->getCanvas());
}
void Canvas2DLayerBridge::setImageBuffer(ImageBuffer* imageBuffer)
@@ -187,16 +240,18 @@ void Canvas2DLayerBridge::beginDestruction()
m_imageBuffer = nullptr;
m_destructionInProgress = true;
setIsHidden(true);
- GraphicsLayer::unregisterContentsLayer(m_layer->layer());
m_surface.clear();
- m_layer->clearTexture();
- // 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();
- // To anyone who ever hits this assert: Please update crbug.com/344666
- // with repro steps.
+
unregisterTaskObserver();
+
+ if (m_layer) {
+ GraphicsLayer::unregisterContentsLayer(m_layer->layer());
+ m_layer->clearTexture();
+ // 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();
+ }
ASSERT(!m_bytesAllocated);
}
@@ -228,7 +283,7 @@ void Canvas2DLayerBridge::setIsHidden(bool hidden)
bool Canvas2DLayerBridge::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes, int x, int y)
{
- if (!m_surface)
+ if (!getOrCreateSurface())
return false;
if (x <= 0 && y <= 0 && x + origInfo.width() >= m_size.width() && y + origInfo.height() >= m_size.height()) {
skipQueuedDrawCommands();
@@ -239,7 +294,7 @@ bool Canvas2DLayerBridge::writePixels(const SkImageInfo& origInfo, const void* p
// call write pixels on the surface, not the recording canvas.
// No need to call beginDirectSurfaceAccessModeIfNeeded() because writePixels
// ignores the matrix and clip state.
- return m_surface->getCanvas()->writePixels(origInfo, pixels, rowBytes, x, y);
+ return getOrCreateSurface()->getCanvas()->writePixels(origInfo, pixels, rowBytes, x, y);
}
void Canvas2DLayerBridge::skipQueuedDrawCommands()
@@ -260,9 +315,11 @@ void Canvas2DLayerBridge::skipQueuedDrawCommands()
void Canvas2DLayerBridge::flushRecordingOnly()
{
ASSERT(!m_destructionInProgress);
- if (m_haveRecordedDrawCommands && m_surface) {
+
+ if (m_haveRecordedDrawCommands && getOrCreateSurface()) {
+ TRACE_EVENT0("cc", "Canvas2DLayerBridge::flushRecordingOnly");
RefPtr<SkPicture> picture = adoptRef(m_recorder->endRecording());
- picture->playback(m_surface->getCanvas());
+ picture->playback(getOrCreateSurface()->getCanvas());
if (m_isDeferralEnabled)
startRecording();
m_haveRecordedDrawCommands = false;
@@ -271,15 +328,16 @@ void Canvas2DLayerBridge::flushRecordingOnly()
void Canvas2DLayerBridge::flush()
{
- if (!m_surface)
+ if (!getOrCreateSurface())
return;
TRACE_EVENT0("cc", "Canvas2DLayerBridge::flush");
flushRecordingOnly();
- m_surface->getCanvas()->flush();
+ getOrCreateSurface()->getCanvas()->flush();
}
void Canvas2DLayerBridge::flushGpu()
{
+ TRACE_EVENT0("cc", "Canvas2DLayerBridge::flushGpu");
flush();
WebGraphicsContext3D* webContext = context();
if (isAccelerated() && webContext)
@@ -299,7 +357,11 @@ WebGraphicsContext3D* Canvas2DLayerBridge::context()
bool Canvas2DLayerBridge::checkSurfaceValid()
{
ASSERT(!m_destructionInProgress);
- if (m_destructionInProgress || !m_surface)
+ if (m_destructionInProgress)
+ return false;
+ if (!m_layer)
+ return true;
+ if (!m_surface)
return false;
if (m_contextProvider->context3d()->isContextLost()) {
m_surface.clear();
@@ -318,7 +380,7 @@ bool Canvas2DLayerBridge::restoreSurface()
ASSERT(!m_destructionInProgress);
if (m_destructionInProgress)
return false;
- ASSERT(m_layer && !m_surface);
+ ASSERT(isAccelerated() && !m_surface);
WebGraphicsContext3D* sharedContext = 0;
m_layer->clearTexture();
@@ -328,10 +390,13 @@ bool Canvas2DLayerBridge::restoreSurface()
if (sharedContext && !sharedContext->isContextLost()) {
GrContext* grCtx = m_contextProvider->grContext();
- RefPtr<SkSurface> surface(createSkSurface(grCtx, m_size, m_msaaSampleCount, m_opacityMode));
- if (surface.get()) {
+ bool surfaceIsAccelerated;
+ RefPtr<SkSurface> surface(createSkSurface(grCtx, m_size, m_msaaSampleCount, m_opacityMode, &surfaceIsAccelerated));
+ // Current paradigm does support switching from accelerated to non-accelerated, which would be tricky
+ // due to changes to the layer tree, which can only happen at specific times during the document lifecycle.
+ // Therefore, we can only accept the restored surface if it is accelerated.
+ if (surface.get() && surfaceIsAccelerated) {
m_surface = surface.release();
- m_initialSurfaceSaveCount = m_surface->getCanvas()->getSaveCount();
// FIXME: draw sad canvas picture into new buffer crbug.com/243842
}
}
@@ -341,6 +406,7 @@ 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.
@@ -363,7 +429,7 @@ bool Canvas2DLayerBridge::prepareMailbox(WebExternalTextureMailbox* outMailbox,
WebGraphicsContext3D* webContext = context();
- RefPtr<SkImage> image = newImageSnapshot();
+ RefPtr<SkImage> image = newImageSnapshot(PreferAcceleration);
// Early exit if canvas was not drawn to since last prepareMailbox
GLenum filter = m_filterQuality == kNone_SkFilterQuality ? GL_NEAREST : GL_LINEAR;
@@ -434,6 +500,7 @@ bool Canvas2DLayerBridge::prepareMailbox(WebExternalTextureMailbox* outMailbox,
void Canvas2DLayerBridge::mailboxReleased(const WebExternalTextureMailbox& mailbox, bool lostResource)
{
+ ASSERT(isAccelerated());
bool contextLost = !m_surface || m_contextProvider->context3d()->isContextLost();
ASSERT(m_mailboxes.last().m_parentLayerBridge.get() == this);
@@ -447,12 +514,7 @@ void Canvas2DLayerBridge::mailboxReleased(const WebExternalTextureMailbox& mailb
if (nameEquals(releasedMailboxInfo->m_mailbox, mailbox)) {
break;
}
- if (releasedMailboxInfo == firstMailbox) {
- // Reached last entry without finding a match, should never happen.
- // FIXME: This used to be an ASSERT, and was (temporarily?) changed to a
- // CRASH to facilitate the investigation of crbug.com/443898.
- CRASH();
- }
+ ASSERT(releasedMailboxInfo == firstMailbox);
}
if (!contextLost) {
@@ -510,6 +572,11 @@ void Canvas2DLayerBridge::didDraw(const FloatRect& rect)
}
}
+void Canvas2DLayerBridge::prepareSurfaceForPaintingIfNeeded()
+{
+ getOrCreateSurface(PreferAcceleration);
+}
+
void Canvas2DLayerBridge::finalizeFrame(const FloatRect &dirtyRect)
{
ASSERT(!m_destructionInProgress);
@@ -551,16 +618,17 @@ void Canvas2DLayerBridge::willProcessTask()
ASSERT_NOT_REACHED();
}
-PassRefPtr<SkImage> Canvas2DLayerBridge::newImageSnapshot()
+PassRefPtr<SkImage> Canvas2DLayerBridge::newImageSnapshot(AccelerationHint hint)
{
if (!checkSurfaceValid())
return nullptr;
+ getOrCreateSurface(hint);
flush();
// A readback operation may alter the texture parameters, which may affect
// the compositor's behavior. Therefore, we must trigger copy-on-write
// even though we are not technically writing to the texture, only to its
// parameters.
- m_surface->notifyContentWillChange(SkSurface::kRetain_ContentChangeMode);
+ getOrCreateSurface()->notifyContentWillChange(SkSurface::kRetain_ContentChangeMode);
return adoptRef(m_surface->newImageSnapshot());
}

Powered by Google App Engine
This is Rietveld 408576698