blob: 8e56c8cbbc3773f9a79283f8e1c7c272ebca019d [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "platform/graphics/RecordingImageBufferSurface.h"
#include "platform/Histogram.h"
#include "platform/graphics/CanvasMetrics.h"
#include "platform/graphics/ExpensiveCanvasHeuristicParameters.h"
#include "platform/graphics/GraphicsContext.h"
#include "platform/graphics/ImageBuffer.h"
#include "platform/graphics/paint/PaintRecorder.h"
#include "wtf/PassRefPtr.h"
#include "wtf/PtrUtil.h"
#include <memory>
namespace blink {
RecordingImageBufferSurface::RecordingImageBufferSurface(
const IntSize& size,
std::unique_ptr<RecordingImageBufferFallbackSurfaceFactory> fallbackFactory,
OpacityMode opacityMode,
sk_sp<SkColorSpace> colorSpace,
SkColorType colorType)
: ImageBufferSurface(size, opacityMode, std::move(colorSpace), colorType),
m_imageBuffer(0),
m_currentFramePixelCount(0),
m_previousFramePixelCount(0),
m_frameWasCleared(true),
m_didRecordDrawCommandsInCurrentFrame(false),
m_currentFrameHasExpensiveOp(false),
m_previousFrameHasExpensiveOp(false),
m_fallbackFactory(std::move(fallbackFactory)) {
initializeCurrentFrame();
}
RecordingImageBufferSurface::~RecordingImageBufferSurface() {}
void RecordingImageBufferSurface::initializeCurrentFrame() {
static SkRTreeFactory rTreeFactory;
m_currentFrame = WTF::wrapUnique(new PaintRecorder);
PaintCanvas* canvas = m_currentFrame->beginRecording(
size().width(), size().height(), &rTreeFactory);
// Always save an initial frame, to support resetting the top level matrix
// and clip.
canvas->save();
if (m_imageBuffer) {
m_imageBuffer->resetCanvas(canvas);
}
m_didRecordDrawCommandsInCurrentFrame = false;
m_currentFrameHasExpensiveOp = false;
m_currentFramePixelCount = 0;
}
void RecordingImageBufferSurface::setImageBuffer(ImageBuffer* imageBuffer) {
m_imageBuffer = imageBuffer;
if (m_currentFrame && m_imageBuffer) {
m_imageBuffer->resetCanvas(m_currentFrame->getRecordingCanvas());
}
if (m_fallbackSurface) {
m_fallbackSurface->setImageBuffer(imageBuffer);
}
}
bool RecordingImageBufferSurface::writePixels(const SkImageInfo& origInfo,
const void* pixels,
size_t rowBytes,
int x,
int y) {
if (!m_fallbackSurface) {
if (x <= 0 && y <= 0 && x + origInfo.width() >= size().width() &&
y + origInfo.height() >= size().height()) {
willOverwriteCanvas();
}
fallBackToRasterCanvas(FallbackReasonWritePixels);
}
return m_fallbackSurface->writePixels(origInfo, pixels, rowBytes, x, y);
}
void RecordingImageBufferSurface::fallBackToRasterCanvas(
FallbackReason reason) {
DCHECK(m_fallbackFactory);
CHECK(reason != FallbackReasonUnknown);
if (m_fallbackSurface) {
DCHECK(!m_currentFrame);
return;
}
DEFINE_THREAD_SAFE_STATIC_LOCAL(
EnumerationHistogram, canvasFallbackHistogram,
new EnumerationHistogram("Canvas.DisplayListFallbackReason",
FallbackReasonCount));
canvasFallbackHistogram.count(reason);
m_fallbackSurface = m_fallbackFactory->createSurface(
size(), getOpacityMode(), colorSpace(), colorType());
m_fallbackSurface->setImageBuffer(m_imageBuffer);
if (m_previousFrame) {
m_previousFrame->playback(m_fallbackSurface->canvas());
m_previousFrame.reset();
}
if (m_currentFrame) {
sk_sp<PaintRecord> record = m_currentFrame->finishRecordingAsPicture();
if (record)
record->playback(m_fallbackSurface->canvas());
m_currentFrame.reset();
}
if (m_imageBuffer) {
m_imageBuffer->resetCanvas(m_fallbackSurface->canvas());
}
CanvasMetrics::countCanvasContextUsage(
CanvasMetrics::DisplayList2DCanvasFallbackToRaster);
}
static RecordingImageBufferSurface::FallbackReason
snapshotReasonToFallbackReason(SnapshotReason reason) {
switch (reason) {
case SnapshotReasonUnknown:
return RecordingImageBufferSurface::FallbackReasonUnknown;
case SnapshotReasonGetImageData:
return RecordingImageBufferSurface::FallbackReasonSnapshotForGetImageData;
case SnapshotReasonPaint:
return RecordingImageBufferSurface::FallbackReasonSnapshotForPaint;
case SnapshotReasonToDataURL:
return RecordingImageBufferSurface::FallbackReasonSnapshotForToDataURL;
case SnapshotReasonToBlob:
return RecordingImageBufferSurface::FallbackReasonSnapshotForToBlob;
case SnapshotReasonCanvasListenerCapture:
return RecordingImageBufferSurface::
FallbackReasonSnapshotForCanvasListenerCapture;
case SnapshotReasonDrawImage:
return RecordingImageBufferSurface::FallbackReasonSnapshotForDrawImage;
case SnapshotReasonCreatePattern:
return RecordingImageBufferSurface::
FallbackReasonSnapshotForCreatePattern;
case SnapshotReasonTransferToImageBitmap:
return RecordingImageBufferSurface::
FallbackReasonSnapshotForTransferToImageBitmap;
case SnapshotReasonUnitTests:
return RecordingImageBufferSurface::FallbackReasonSnapshotForUnitTests;
case SnapshotReasonGetCopiedImage:
return RecordingImageBufferSurface::FallbackReasonSnapshotGetCopiedImage;
case SnapshotReasonWebGLDrawImageIntoBuffer:
return RecordingImageBufferSurface::
FallbackReasonSnapshotWebGLDrawImageIntoBuffer;
case SnapshotReasonWebGLTexImage2D:
return RecordingImageBufferSurface::
FallbackReasonSnapshotForWebGLTexImage2D;
case SnapshotReasonWebGLTexSubImage2D:
return RecordingImageBufferSurface::
FallbackReasonSnapshotForWebGLTexSubImage2D;
case SnapshotReasonWebGLTexImage3D:
return RecordingImageBufferSurface::
FallbackReasonSnapshotForWebGLTexImage3D;
case SnapshotReasonWebGLTexSubImage3D:
return RecordingImageBufferSurface::
FallbackReasonSnapshotForWebGLTexSubImage3D;
case SnapshotReasonCopyToClipboard:
return RecordingImageBufferSurface::
FallbackReasonSnapshotForCopyToClipboard;
case SnapshotReasonCreateImageBitmap:
return RecordingImageBufferSurface::
FallbackReasonSnapshotForCreateImageBitmap;
}
NOTREACHED();
return RecordingImageBufferSurface::FallbackReasonUnknown;
}
sk_sp<SkImage> RecordingImageBufferSurface::newImageSnapshot(
AccelerationHint hint,
SnapshotReason reason) {
if (!m_fallbackSurface)
fallBackToRasterCanvas(snapshotReasonToFallbackReason(reason));
return m_fallbackSurface->newImageSnapshot(hint, reason);
}
PaintCanvas* RecordingImageBufferSurface::canvas() {
if (m_fallbackSurface)
return m_fallbackSurface->canvas();
DCHECK(m_currentFrame->getRecordingCanvas());
return m_currentFrame->getRecordingCanvas();
}
static RecordingImageBufferSurface::FallbackReason
disableDeferralReasonToFallbackReason(DisableDeferralReason reason) {
switch (reason) {
case DisableDeferralReasonUnknown:
return RecordingImageBufferSurface::FallbackReasonUnknown;
case DisableDeferralReasonExpensiveOverdrawHeuristic:
return RecordingImageBufferSurface::
FallbackReasonExpensiveOverdrawHeuristic;
case DisableDeferralReasonUsingTextureBackedPattern:
return RecordingImageBufferSurface::FallbackReasonTextureBackedPattern;
case DisableDeferralReasonDrawImageOfVideo:
return RecordingImageBufferSurface::FallbackReasonDrawImageOfVideo;
case DisableDeferralReasonDrawImageOfAnimated2dCanvas:
return RecordingImageBufferSurface::
FallbackReasonDrawImageOfAnimated2dCanvas;
case DisableDeferralReasonSubPixelTextAntiAliasingSupport:
return RecordingImageBufferSurface::
FallbackReasonSubPixelTextAntiAliasingSupport;
case DisableDeferralDrawImageWithTextureBackedSourceImage:
return RecordingImageBufferSurface::
FallbackReasonDrawImageWithTextureBackedSourceImage;
case DisableDeferralReasonCount:
NOTREACHED();
break;
}
NOTREACHED();
return RecordingImageBufferSurface::FallbackReasonUnknown;
}
void RecordingImageBufferSurface::disableDeferral(
DisableDeferralReason reason) {
if (!m_fallbackSurface)
fallBackToRasterCanvas(disableDeferralReasonToFallbackReason(reason));
}
sk_sp<PaintRecord> RecordingImageBufferSurface::getRecord() {
if (m_fallbackSurface)
return nullptr;
FallbackReason fallbackReason = FallbackReasonUnknown;
bool canUseRecord = finalizeFrameInternal(&fallbackReason);
DCHECK(canUseRecord || m_fallbackFactory);
if (canUseRecord) {
return m_previousFrame;
}
if (!m_fallbackSurface)
fallBackToRasterCanvas(fallbackReason);
return nullptr;
}
void RecordingImageBufferSurface::finalizeFrame() {
if (m_fallbackSurface) {
m_fallbackSurface->finalizeFrame();
return;
}
FallbackReason fallbackReason = FallbackReasonUnknown;
if (!finalizeFrameInternal(&fallbackReason))
fallBackToRasterCanvas(fallbackReason);
}
void RecordingImageBufferSurface::doPaintInvalidation(
const FloatRect& dirtyRect) {
if (m_fallbackSurface) {
m_fallbackSurface->doPaintInvalidation(dirtyRect);
}
}
static RecordingImageBufferSurface::FallbackReason flushReasonToFallbackReason(
FlushReason reason) {
switch (reason) {
case FlushReasonUnknown:
return RecordingImageBufferSurface::FallbackReasonUnknown;
case FlushReasonInitialClear:
return RecordingImageBufferSurface::FallbackReasonFlushInitialClear;
case FlushReasonDrawImageOfWebGL:
return RecordingImageBufferSurface::
FallbackReasonFlushForDrawImageOfWebGL;
}
NOTREACHED();
return RecordingImageBufferSurface::FallbackReasonUnknown;
}
void RecordingImageBufferSurface::flush(FlushReason reason) {
if (!m_fallbackSurface)
fallBackToRasterCanvas(flushReasonToFallbackReason(reason));
m_fallbackSurface->flush(reason);
}
void RecordingImageBufferSurface::willOverwriteCanvas() {
m_frameWasCleared = true;
m_previousFrame.reset();
m_previousFrameHasExpensiveOp = false;
m_previousFramePixelCount = 0;
if (m_didRecordDrawCommandsInCurrentFrame) {
// Discard previous draw commands
m_currentFrame->finishRecordingAsPicture();
initializeCurrentFrame();
}
}
void RecordingImageBufferSurface::didDraw(const FloatRect& rect) {
m_didRecordDrawCommandsInCurrentFrame = true;
IntRect pixelBounds = enclosingIntRect(rect);
m_currentFramePixelCount += pixelBounds.width() * pixelBounds.height();
}
bool RecordingImageBufferSurface::finalizeFrameInternal(
FallbackReason* fallbackReason) {
CHECK(!m_fallbackSurface);
CHECK(m_currentFrame);
DCHECK(m_currentFrame->getRecordingCanvas());
DCHECK(fallbackReason);
DCHECK(*fallbackReason == FallbackReasonUnknown);
if (!m_didRecordDrawCommandsInCurrentFrame) {
if (!m_previousFrame) {
// Create an initial blank frame
m_previousFrame = m_currentFrame->finishRecordingAsPicture();
initializeCurrentFrame();
}
CHECK(m_currentFrame);
return true;
}
if (!m_frameWasCleared) {
*fallbackReason = FallbackReasonCanvasNotClearedBetweenFrames;
return false;
}
if (m_fallbackFactory &&
m_currentFrame->getRecordingCanvas()->getSaveCount() - 1 >
ExpensiveCanvasHeuristicParameters::ExpensiveRecordingStackDepth) {
// (getSaveCount() decremented to account for the intial recording canvas
// save frame.)
*fallbackReason = FallbackReasonRunawayStateStack;
return false;
}
m_previousFrame = m_currentFrame->finishRecordingAsPicture();
m_previousFrameHasExpensiveOp = m_currentFrameHasExpensiveOp;
m_previousFramePixelCount = m_currentFramePixelCount;
initializeCurrentFrame();
m_frameWasCleared = false;
return true;
}
void RecordingImageBufferSurface::draw(GraphicsContext& context,
const FloatRect& destRect,
const FloatRect& srcRect,
SkBlendMode op) {
if (m_fallbackSurface) {
m_fallbackSurface->draw(context, destRect, srcRect, op);
return;
}
sk_sp<PaintRecord> record = getRecord();
if (record) {
context.compositeRecord(std::move(record), destRect, srcRect, op);
} else {
ImageBufferSurface::draw(context, destRect, srcRect, op);
}
}
bool RecordingImageBufferSurface::isExpensiveToPaint() {
if (m_fallbackSurface)
return m_fallbackSurface->isExpensiveToPaint();
if (m_didRecordDrawCommandsInCurrentFrame) {
if (m_currentFrameHasExpensiveOp)
return true;
if (m_currentFramePixelCount >=
(size().width() * size().height() *
ExpensiveCanvasHeuristicParameters::ExpensiveOverdrawThreshold))
return true;
if (m_frameWasCleared)
return false; // early exit because previous frame is overdrawn
}
if (m_previousFrame) {
if (m_previousFrameHasExpensiveOp)
return true;
if (m_previousFramePixelCount >=
(size().width() * size().height() *
ExpensiveCanvasHeuristicParameters::ExpensiveOverdrawThreshold))
return true;
}
return false;
}
// Fallback passthroughs
bool RecordingImageBufferSurface::restore() {
if (m_fallbackSurface)
return m_fallbackSurface->restore();
return ImageBufferSurface::restore();
}
WebLayer* RecordingImageBufferSurface::layer() const {
if (m_fallbackSurface)
return m_fallbackSurface->layer();
return ImageBufferSurface::layer();
}
bool RecordingImageBufferSurface::isAccelerated() const {
if (m_fallbackSurface)
return m_fallbackSurface->isAccelerated();
return ImageBufferSurface::isAccelerated();
}
void RecordingImageBufferSurface::setIsHidden(bool hidden) {
if (m_fallbackSurface)
m_fallbackSurface->setIsHidden(hidden);
else
ImageBufferSurface::setIsHidden(hidden);
}
} // namespace blink