Chromium Code Reviews| Index: Source/platform/graphics/Canvas2DLayerBridge.cpp |
| diff --git a/Source/platform/graphics/Canvas2DLayerBridge.cpp b/Source/platform/graphics/Canvas2DLayerBridge.cpp |
| index c498d131a8b8c39a76412af22823aea8b11e5280..0d3f292a6ece0c56b10276c93ea0ef29efdecadc 100644 |
| --- a/Source/platform/graphics/Canvas2DLayerBridge.cpp |
| +++ b/Source/platform/graphics/Canvas2DLayerBridge.cpp |
| @@ -58,13 +58,20 @@ namespace blink { |
| static PassRefPtr<SkSurface> createSkSurface(GrContext* gr, const IntSize& size, int msaaSampleCount, OpacityMode opacityMode) |
| { |
| - 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) |
| + surface = adoptRef(SkSurface::NewRenderTarget(gr, SkSurface::kNo_Budgeted, info, msaaSampleCount, Opaque == opacityMode ? 0 : &disableLCDProps)); |
| + |
| + if (!surface) |
|
Stephen White
2015/09/16 18:19:32
IWBN if we had a test for this.
|
| + surface = adoptRef(SkSurface::NewRaster(info, Opaque == opacityMode ? 0 : &disableLCDProps)); |
| + |
| if (surface) { |
| if (opacityMode == Opaque) { |
| surface->getCanvas()->clear(SK_ColorBLACK); |
| @@ -75,23 +82,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 +107,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 +141,61 @@ 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 accelerate = shouldAccelerate(hint); |
| + |
| + if (accelerate && !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); |
| + } |
| + |
| + m_surface = createSkSurface(accelerate ? m_contextProvider->grContext() : nullptr, m_size, m_msaaSampleCount, m_opacityMode); |
| + } |
| + 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 +217,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 +235,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 +278,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 +289,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 +310,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 +323,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 +352,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 +375,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(); |
| @@ -331,7 +388,6 @@ bool Canvas2DLayerBridge::restoreSurface() |
| RefPtr<SkSurface> surface(createSkSurface(grCtx, m_size, m_msaaSampleCount, m_opacityMode)); |
| if (surface.get()) { |
| m_surface = surface.release(); |
| - m_initialSurfaceSaveCount = m_surface->getCanvas()->getSaveCount(); |
| // FIXME: draw sad canvas picture into new buffer crbug.com/243842 |
| } |
| } |
| @@ -341,6 +397,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 +420,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 +491,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 +505,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 +563,11 @@ void Canvas2DLayerBridge::didDraw(const FloatRect& rect) |
| } |
| } |
| +void Canvas2DLayerBridge::prepareSurfaceForPaintingIfNeeded() |
| +{ |
| + getOrCreateSurface(PreferAcceleration); |
| +} |
| + |
| void Canvas2DLayerBridge::finalizeFrame(const FloatRect &dirtyRect) |
| { |
| ASSERT(!m_destructionInProgress); |
| @@ -551,16 +609,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()); |
| } |