| 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());
|
| }
|
|
|
|
|