Infrastructure changes to support pull-saveLayers-forward task

This is split out of (First pass at pre-rendering saveLayers for GPU - https://codereview.chromium.org/261663003/).

It mainly:

	Moves NeedsDeepCopy to somewhere more accessible (so GrPictureUtils.cpp can use it)
	Moves ComputeAccelDataKey somewhere more accessible (so GPUPicture test can use it)
	Adds unit test for picture saveLayer analysis (done in EXPERIMENTAL_optimize)
        Adds new fields to SaveLayerInfo that are needed to pull forward layers

Committed: http://code.google.com/p/skia/source/detail?r=14571

R=bsalomon@google.com

Author: robertphillips@google.com

Review URL: https://codereview.chromium.org/266203003

git-svn-id: http://skia.googlecode.com/svn/trunk@14586 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
commit-bot@chromium.org 2014-05-06 12:02:22 +00:00
parent c9c5c42bbb
commit 0205aba7d5
7 changed files with 246 additions and 72 deletions

View File

@ -76,3 +76,24 @@ bool isPaintOpaque(const SkPaint* paint,
}
return false;
}
bool NeedsDeepCopy(const SkPaint& paint) {
/*
* These fields are known to be immutable, and so can be shallow-copied
*
* getTypeface()
* getAnnotation()
* paint.getColorFilter()
* getXfermode()
* getPathEffect()
* getMaskFilter()
*/
return paint.getShader() ||
#ifdef SK_SUPPORT_LEGACY_LAYERRASTERIZER_API
paint.getRasterizer() ||
#endif
paint.getLooper() || // needs to hide its addLayer...
paint.getImageFilter();
}

View File

@ -22,4 +22,11 @@ class SkPaint;
*/
bool isPaintOpaque(const SkPaint* paint,
const SkBitmap* bmpReplacesShader = NULL);
/** Returns true if the provided paint has fields which are not
immutable (and will thus require deep copying).
@param paint the paint to be analyzed
@return true if the paint requires a deep copy
*/
bool NeedsDeepCopy(const SkPaint& paint);
#endif

View File

@ -15,6 +15,7 @@
#include "SkBitmapDevice.h"
#include "SkCanvas.h"
#include "SkChunkAlloc.h"
#include "SkPaintPriv.h"
#include "SkPicture.h"
#include "SkRegion.h"
#include "SkStream.h"
@ -217,26 +218,6 @@ SkPicture* SkPicture::clone() const {
return clonedPicture;
}
static bool needs_deep_copy(const SkPaint& paint) {
/*
* These fields are known to be immutable, and so can be shallow-copied
*
* getTypeface()
* getAnnotation()
* paint.getColorFilter()
* getXfermode()
* getPathEffect()
* getMaskFilter()
*/
return paint.getShader() ||
#ifdef SK_SUPPORT_LEGACY_LAYERRASTERIZER_API
paint.getRasterizer() ||
#endif
paint.getLooper() || // needs to hide its addLayer...
paint.getImageFilter();
}
void SkPicture::clone(SkPicture* pictures, int count) const {
SkPictCopyInfo copyInfo;
SkPictInfo info;
@ -282,7 +263,7 @@ void SkPicture::clone(SkPicture* pictures, int count) const {
SkDEBUGCODE(int heapSize = SafeCount(fPlayback->fBitmapHeap.get());)
for (int i = 0; i < paintCount; i++) {
if (needs_deep_copy(fPlayback->fPaints->at(i))) {
if (NeedsDeepCopy(fPlayback->fPaints->at(i))) {
copyInfo.paintData[i] =
SkFlatData::Create<SkPaint::FlatteningTraits>(&copyInfo.controller,
fPlayback->fPaints->at(i), 0);

View File

@ -7,6 +7,14 @@
#include "GrPictureUtils.h"
#include "SkDevice.h"
#include "SkDraw.h"
#include "SkPaintPriv.h"
SkPicture::AccelData::Key GPUAccelData::ComputeAccelDataKey() {
static const SkPicture::AccelData::Key gGPUID = SkPicture::AccelData::GenerateDomain();
return gGPUID;
}
// The GrGather device performs GPU-backend-specific preprocessing on
// a picture. The results are stored in a GPUAccelData.
@ -20,12 +28,17 @@ class GrGatherDevice : public SkBaseDevice {
public:
SK_DECLARE_INST_COUNT(GrGatherDevice)
GrGatherDevice(int width, int height, SkPicture* picture, GPUAccelData* accelData) {
GrGatherDevice(int width, int height, SkPicture* picture, GPUAccelData* accelData,
int saveLayerDepth) {
fPicture = picture;
fSaveLayerDepth = saveLayerDepth;
fInfo.fValid = true;
fInfo.fSize.set(width, height);
fInfo.fPaint = NULL;
fInfo.fSaveLayerOpID = fPicture->EXPERIMENTAL_curOpID();
fInfo.fRestoreOpID = 0;
fInfo.fHasNestedLayers = false;
fInfo.fIsNested = (2 == fSaveLayerDepth);
fEmptyBitmap.setConfig(SkImageInfo::Make(fInfo.fSize.fWidth,
fInfo.fSize.fHeight,
@ -110,7 +123,8 @@ protected:
const SkPaint& paint) SK_OVERRIDE {
}
virtual void drawDevice(const SkDraw& draw, SkBaseDevice* deviceIn, int x, int y,
const SkPaint&) SK_OVERRIDE {
const SkPaint& paint) SK_OVERRIDE {
// deviceIn is the one that is being "restored" back to its parent
GrGatherDevice* device = static_cast<GrGatherDevice*>(deviceIn);
if (device->fAlreadyDrawn) {
@ -118,6 +132,29 @@ protected:
}
device->fInfo.fRestoreOpID = fPicture->EXPERIMENTAL_curOpID();
device->fInfo.fCTM = *draw.fMatrix;
device->fInfo.fCTM.postTranslate(SkIntToScalar(-device->getOrigin().fX),
SkIntToScalar(-device->getOrigin().fY));
// We need the x & y values that will yield 'getOrigin' when transformed
// by 'draw.fMatrix'.
device->fInfo.fOffset.iset(device->getOrigin());
SkMatrix invMatrix;
if (draw.fMatrix->invert(&invMatrix)) {
invMatrix.mapPoints(&device->fInfo.fOffset, 1);
} else {
device->fInfo.fValid = false;
}
if (NeedsDeepCopy(paint)) {
// This NULL acts as a signal that the paint was uncopyable (for now)
device->fInfo.fPaint = NULL;
device->fInfo.fValid = false;
} else {
device->fInfo.fPaint = SkNEW_ARGS(SkPaint, (paint));
}
fAccelData->addSaveLayerInfo(device->fInfo);
device->fAlreadyDrawn = true;
}
@ -158,6 +195,9 @@ private:
// The information regarding the saveLayer call this device represents.
GPUAccelData::SaveLayerInfo fInfo;
// The depth of this device in the saveLayer stack
int fSaveLayerDepth;
virtual void replaceBitmapBackendForRasterSurface(const SkBitmap&) SK_OVERRIDE {
NotSupported();
}
@ -167,7 +207,8 @@ private:
SkASSERT(kSaveLayer_Usage == usage);
fInfo.fHasNestedLayers = true;
return SkNEW_ARGS(GrGatherDevice, (info.width(), info.height(), fPicture, fAccelData));
return SkNEW_ARGS(GrGatherDevice, (info.width(), info.height(), fPicture,
fAccelData, fSaveLayerDepth+1));
}
virtual void flush() SK_OVERRIDE {}
@ -239,7 +280,7 @@ void GatherGPUInfo(SkPicture* pict, GPUAccelData* accelData) {
return ;
}
GrGatherDevice device(pict->width(), pict->height(), pict, accelData);
GrGatherDevice device(pict->width(), pict->height(), pict, accelData, 0);
GrGatherCanvas canvas(&device, pict);
canvas.gather();

View File

@ -17,8 +17,21 @@ class GPUAccelData : public SkPicture::AccelData {
public:
// Information about a given saveLayer in an SkPicture
struct SaveLayerInfo {
// True if the SaveLayerInfo is valid. False if either 'fOffset' is
// invalid (due to a non-invertible CTM) or 'fPaint' is NULL (due
// to a non-copyable paint).
bool fValid;
// The size of the saveLayer
SkISize fSize;
// The CTM in which this layer's draws must occur. It already incorporates
// the translation needed to map the layer's top-left point to the origin.
SkMatrix fCTM;
// The offset that needs to be passed to drawBitmap to correctly
// position the pre-rendered layer.
SkPoint fOffset;
// The paint to use on restore. NULL if the paint was not copyable (and
// thus that this layer should not be pulled forward).
const SkPaint* fPaint;
// The ID of this saveLayer in the picture. 0 is an invalid ID.
size_t fSaveLayerOpID;
// The ID of the matching restore in the picture. 0 is an invalid ID.
@ -26,6 +39,8 @@ public:
// True if this saveLayer has at least one other saveLayer nested within it.
// False otherwise.
bool fHasNestedLayers;
// True if this saveLayer is nested within another. False otherwise.
bool fIsNested;
};
GPUAccelData(Key key) : INHERITED(key) { }
@ -43,6 +58,10 @@ public:
return fSaveLayerInfo[index];
}
// We may, in the future, need to pass in the GPUDevice in order to
// incorporate the clip and matrix state into the key
static SkPicture::AccelData::Key ComputeAccelDataKey();
protected:
SkTDArray<SaveLayerInfo> fSaveLayerInfo;

View File

@ -1910,16 +1910,8 @@ SkSurface* SkGpuDevice::newSurface(const SkImageInfo& info) {
return SkSurface::NewRenderTarget(fContext, info, fRenderTarget->numSamples());
}
// In the future this may not be a static method if we need to incorporate the
// clip and matrix state into the key
SkPicture::AccelData::Key SkGpuDevice::ComputeAccelDataKey() {
static const SkPicture::AccelData::Key gGPUID = SkPicture::AccelData::GenerateDomain();
return gGPUID;
}
void SkGpuDevice::EXPERIMENTAL_optimize(SkPicture* picture) {
SkPicture::AccelData::Key key = ComputeAccelDataKey();
SkPicture::AccelData::Key key = GPUAccelData::ComputeAccelDataKey();
GPUAccelData* data = SkNEW_ARGS(GPUAccelData, (key));
@ -1934,7 +1926,7 @@ void SkGpuDevice::EXPERIMENTAL_purge(SkPicture* picture) {
bool SkGpuDevice::EXPERIMENTAL_drawPicture(SkCanvas* canvas, SkPicture* picture) {
SkPicture::AccelData::Key key = ComputeAccelDataKey();
SkPicture::AccelData::Key key = GPUAccelData::ComputeAccelDataKey();
const SkPicture::AccelData* data = picture->EXPERIMENTAL_getAccelData(key);
if (NULL == data) {
@ -1943,27 +1935,6 @@ bool SkGpuDevice::EXPERIMENTAL_drawPicture(SkCanvas* canvas, SkPicture* picture)
const GPUAccelData *gpuData = static_cast<const GPUAccelData*>(data);
//#define SK_PRINT_PULL_FORWARD_INFO 1
#ifdef SK_PRINT_PULL_FORWARD_INFO
static bool gPrintedAccelData = false;
if (!gPrintedAccelData) {
for (int i = 0; i < gpuData->numSaveLayers(); ++i) {
const GPUAccelData::SaveLayerInfo& info = gpuData->saveLayerInfo(i);
SkDebugf("%d: Width: %d Height: %d SL: %d R: %d hasNestedLayers: %s\n",
i,
info.fSize.fWidth,
info.fSize.fHeight,
info.fSaveLayerOpID,
info.fRestoreOpID,
info.fHasNestedLayers ? "T" : "F");
}
gPrintedAccelData = true;
}
#endif
SkAutoTArray<bool> pullForward(gpuData->numSaveLayers());
for (int i = 0; i < gpuData->numSaveLayers(); ++i) {
pullForward[i] = false;
@ -1984,10 +1955,6 @@ bool SkGpuDevice::EXPERIMENTAL_drawPicture(SkCanvas* canvas, SkPicture* picture)
const SkPicture::OperationList& ops = picture->EXPERIMENTAL_getActiveOps(clip);
#ifdef SK_PRINT_PULL_FORWARD_INFO
SkDebugf("rect: %d %d %d %d\n", clip.fLeft, clip.fTop, clip.fRight, clip.fBottom);
#endif
for (int i = 0; i < ops.numOps(); ++i) {
for (int j = 0; j < gpuData->numSaveLayers(); ++j) {
const GPUAccelData::SaveLayerInfo& info = gpuData->saveLayerInfo(j);
@ -1998,17 +1965,5 @@ bool SkGpuDevice::EXPERIMENTAL_drawPicture(SkCanvas* canvas, SkPicture* picture)
}
}
#ifdef SK_PRINT_PULL_FORWARD_INFO
SkDebugf("Need SaveLayers: ");
for (int i = 0; i < gpuData->numSaveLayers(); ++i) {
if (pullForward[i]) {
const GrCachedLayer* layer = fContext->getLayerCache()->findLayerOrCreate(picture, i);
SkDebugf("%d (%d), ", i, layer->layerID());
}
}
SkDebugf("\n");
#endif
return false;
}

View File

@ -6,12 +6,18 @@
*/
#include "SkBitmapDevice.h"
#if SK_SUPPORT_GPU
#include "SkBlurImageFilter.h"
#endif
#include "SkCanvas.h"
#include "SkColorPriv.h"
#include "SkDashPathEffect.h"
#include "SkData.h"
#include "SkDecodingImageGenerator.h"
#include "SkError.h"
#if SK_SUPPORT_GPU
#include "SkGpuDevice.h"
#endif
#include "SkImageEncoder.h"
#include "SkImageGenerator.h"
#include "SkPaint.h"
@ -22,6 +28,12 @@
#include "SkRandom.h"
#include "SkShader.h"
#include "SkStream.h"
#if SK_SUPPORT_GPU
#include "SkSurface.h"
#include "GrContextFactory.h"
#include "GrPictureUtils.h"
#endif
#include "Test.h"
static const int gColorScale = 30;
@ -765,6 +777,138 @@ static void test_gpu_veto(skiatest::Reporter* reporter) {
// hairline stroked AA concave paths are fine for GPU rendering
REPORTER_ASSERT(reporter, picture->suitableForGpuRasterization(NULL));
}
static void test_gpu_picture_optimization(skiatest::Reporter* reporter,
GrContextFactory* factory) {
GrContext* context = factory->get(GrContextFactory::kNative_GLContextType);
static const int kWidth = 100;
static const int kHeight = 100;
SkAutoTUnref<SkPicture> pict;
// create a picture with the structure:
// 1)
// SaveLayer
// Restore
// 2)
// SaveLayer
// Translate
// SaveLayer w/ bound
// Restore
// Restore
// 3)
// SaveLayer w/ copyable paint
// Restore
// 4)
// SaveLayer w/ non-copyable paint
// Restore
{
SkPictureRecorder recorder;
SkCanvas* c = recorder.beginRecording(kWidth, kHeight, NULL, 0);
// 1)
c->saveLayer(NULL, NULL);
c->restore();
// 2)
c->saveLayer(NULL, NULL);
c->translate(kWidth/2, kHeight/2);
SkRect r = SkRect::MakeXYWH(0, 0, kWidth/2, kHeight/2);
c->saveLayer(&r, NULL);
c->restore();
c->restore();
// 3)
{
SkPaint p;
p.setColor(SK_ColorRED);
c->saveLayer(NULL, &p);
c->restore();
}
// 4)
// TODO: this case will need to be removed once the paint's are immutable
{
SkPaint p;
SkBitmap bmp;
bmp.allocN32Pixels(10, 10);
bmp.eraseColor(SK_ColorGREEN);
bmp.setAlphaType(kOpaque_SkAlphaType);
SkShader* shader = SkShader::CreateBitmapShader(bmp,
SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
p.setShader(shader)->unref();
c->saveLayer(NULL, &p);
c->restore();
}
pict.reset(recorder.endRecording());
}
// Now test out the SaveLayer extraction
{
SkImageInfo info = SkImageInfo::MakeN32Premul(kWidth, kHeight);
SkAutoTUnref<SkSurface> surface(SkSurface::NewScratchRenderTarget(context, info));
SkCanvas* canvas = surface->getCanvas();
canvas->EXPERIMENTAL_optimize(pict);
SkPicture::AccelData::Key key = GPUAccelData::ComputeAccelDataKey();
const SkPicture::AccelData* data = pict->EXPERIMENTAL_getAccelData(key);
REPORTER_ASSERT(reporter, NULL != data);
const GPUAccelData *gpuData = static_cast<const GPUAccelData*>(data);
REPORTER_ASSERT(reporter, 5 == gpuData->numSaveLayers());
const GPUAccelData::SaveLayerInfo& info0 = gpuData->saveLayerInfo(0);
// The parent/child layer appear in reverse order
const GPUAccelData::SaveLayerInfo& info1 = gpuData->saveLayerInfo(2);
const GPUAccelData::SaveLayerInfo& info2 = gpuData->saveLayerInfo(1);
const GPUAccelData::SaveLayerInfo& info3 = gpuData->saveLayerInfo(3);
const GPUAccelData::SaveLayerInfo& info4 = gpuData->saveLayerInfo(4);
REPORTER_ASSERT(reporter, info0.fValid);
REPORTER_ASSERT(reporter, kWidth == info0.fSize.fWidth && kHeight == info0.fSize.fHeight);
REPORTER_ASSERT(reporter, info0.fCTM.isIdentity());
REPORTER_ASSERT(reporter, 0 == info0.fOffset.fX && 0 == info0.fOffset.fY);
REPORTER_ASSERT(reporter, NULL != info0.fPaint);
REPORTER_ASSERT(reporter, !info0.fIsNested && !info0.fHasNestedLayers);
REPORTER_ASSERT(reporter, info1.fValid);
REPORTER_ASSERT(reporter, kWidth == info1.fSize.fWidth && kHeight == info1.fSize.fHeight);
REPORTER_ASSERT(reporter, info1.fCTM.isIdentity());
REPORTER_ASSERT(reporter, 0 == info1.fOffset.fX && 0 == info1.fOffset.fY);
REPORTER_ASSERT(reporter, NULL != info1.fPaint);
REPORTER_ASSERT(reporter, !info1.fIsNested && info1.fHasNestedLayers); // has a nested SL
REPORTER_ASSERT(reporter, info2.fValid);
REPORTER_ASSERT(reporter, kWidth/2 == info2.fSize.fWidth &&
kHeight/2 == info2.fSize.fHeight); // bound reduces size
REPORTER_ASSERT(reporter, info2.fCTM.isIdentity()); // translated
REPORTER_ASSERT(reporter, 0 == info2.fOffset.fX && 0 == info2.fOffset.fY);
REPORTER_ASSERT(reporter, NULL != info1.fPaint);
REPORTER_ASSERT(reporter, info2.fIsNested && !info2.fHasNestedLayers); // is nested
REPORTER_ASSERT(reporter, info3.fValid);
REPORTER_ASSERT(reporter, kWidth == info3.fSize.fWidth && kHeight == info3.fSize.fHeight);
REPORTER_ASSERT(reporter, info3.fCTM.isIdentity());
REPORTER_ASSERT(reporter, 0 == info3.fOffset.fX && 0 == info3.fOffset.fY);
REPORTER_ASSERT(reporter, NULL != info3.fPaint);
REPORTER_ASSERT(reporter, !info3.fIsNested && !info3.fHasNestedLayers);
REPORTER_ASSERT(reporter, !info4.fValid); // paint is/was uncopyable
REPORTER_ASSERT(reporter, kWidth == info4.fSize.fWidth && kHeight == info4.fSize.fHeight);
REPORTER_ASSERT(reporter, 0 == info4.fOffset.fX && 0 == info4.fOffset.fY);
REPORTER_ASSERT(reporter, info4.fCTM.isIdentity());
REPORTER_ASSERT(reporter, NULL == info4.fPaint); // paint is/was uncopyable
REPORTER_ASSERT(reporter, !info4.fIsNested && !info4.fHasNestedLayers);
}
}
#endif
static void set_canvas_to_save_count_4(SkCanvas* canvas) {
@ -1284,6 +1428,12 @@ DEF_TEST(Picture, reporter) {
test_gen_id(reporter);
}
#if SK_SUPPORT_GPU
DEF_GPUTEST(GPUPicture, reporter, factory) {
test_gpu_picture_optimization(reporter, factory);
}
#endif
static void draw_bitmaps(const SkBitmap bitmap, SkCanvas* canvas) {
const SkPaint paint;
const SkRect rect = { 5.0f, 5.0f, 8.0f, 8.0f };