}
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();
+}
+
*/
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
#include "SkBitmapDevice.h"
#include "SkCanvas.h"
#include "SkChunkAlloc.h"
+#include "SkPaintPriv.h"
#include "SkPicture.h"
#include "SkRegion.h"
#include "SkStream.h"
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;
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>(©Info.controller,
fPlayback->fPaints->at(i), 0);
#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.
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,
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) {
}
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;
}
// 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();
}
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 {}
return ;
}
- GrGatherDevice device(pict->width(), pict->height(), pict, accelData);
+ GrGatherDevice device(pict->width(), pict->height(), pict, accelData, 0);
GrGatherCanvas canvas(&device, pict);
canvas.gather();
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.
// 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) { }
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;
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));
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) {
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;
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);
}
}
-#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;
}
*/
#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"
#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;
// 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) {
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 };