From 41c27e15ec2740850700f1b82038ce0f7a632481 Mon Sep 17 00:00:00 2001 From: reed Date: Wed, 6 Jul 2016 09:29:16 -0700 Subject: Experiment: add flag for finish-recording to return null BUG=skia:5495 GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2106843004 Review-Url: https://codereview.chromium.org/2106843004 --- include/core/SkPictureRecorder.h | 15 ++++++---- src/core/SkPictureRecorder.cpp | 25 +++++++++++++---- src/core/SkRecordOpts.cpp | 2 +- tests/PictureTest.cpp | 60 ++++++++++++++++++++++++++++++++++++++++ tests/RecordReplaceDrawTest.cpp | 5 ++++ 5 files changed, 96 insertions(+), 11 deletions(-) diff --git a/include/core/SkPictureRecorder.h b/include/core/SkPictureRecorder.h index 3f0cbcc801..419c01bc88 100644 --- a/include/core/SkPictureRecorder.h +++ b/include/core/SkPictureRecorder.h @@ -33,11 +33,15 @@ public: enum RecordFlags { // This flag indicates that, if some BHH is being computed, saveLayer // information should also be extracted at the same time. - kComputeSaveLayerInfo_RecordFlag = 0x01, + kComputeSaveLayerInfo_RecordFlag = 1 << 0, // If you call drawPicture() or drawDrawable() on the recording canvas, this flag forces // that object to playback its contents immediately rather than reffing the object. - kPlaybackDrawPicture_RecordFlag = 0x02, + kPlaybackDrawPicture_RecordFlag = 1 << 1, + }; + + enum FinishFlags { + kReturnNullForEmpty_FinishFlag = 1 << 0, // no draw-ops will return nullptr }; /** Returns the canvas that records the drawing commands. @@ -72,7 +76,7 @@ public: * reflect their current state, but will not contain a live reference to the drawables * themselves. */ - sk_sp finishRecordingAsPicture(); + sk_sp finishRecordingAsPicture(uint32_t endFlags = 0); /** * Signal that the caller is done recording, and update the cull rect to use for bounding @@ -83,7 +87,8 @@ public: * and subsequent culling operations. * @return the picture containing the recorded content. */ - sk_sp finishRecordingAsPictureWithCull(const SkRect& cullRect); + sk_sp finishRecordingAsPictureWithCull(const SkRect& cullRect, + uint32_t endFlags = 0); /** * Signal that the caller is done recording. This invalidates the canvas returned by @@ -95,7 +100,7 @@ public: * and therefore this drawable will reflect the current state of those nested drawables anytime * it is drawn or a new picture is snapped from it (by calling drawable->newPictureSnapshot()). */ - sk_sp finishRecordingAsDrawable(); + sk_sp finishRecordingAsDrawable(uint32_t endFlags = 0); #ifdef SK_SUPPORT_LEGACY_PICTURE_PTR SkPicture* SK_WARN_UNUSED_RESULT endRecordingAsPicture() { diff --git a/src/core/SkPictureRecorder.cpp b/src/core/SkPictureRecorder.cpp index a157d0dfe0..8ce770efce 100644 --- a/src/core/SkPictureRecorder.cpp +++ b/src/core/SkPictureRecorder.cpp @@ -51,17 +51,26 @@ SkCanvas* SkPictureRecorder::getRecordingCanvas() { return fActivelyRecording ? fRecorder.get() : nullptr; } -sk_sp SkPictureRecorder::finishRecordingAsPicture() { +sk_sp SkPictureRecorder::finishRecordingAsPicture(uint32_t finishFlags) { fActivelyRecording = false; fRecorder->restoreToCount(1); // If we were missing any restores, add them now. if (fRecord->count() == 0) { + if (finishFlags & kReturnNullForEmpty_FinishFlag) { + return nullptr; + } return fMiniRecorder.detachAsPicture(fCullRect); } // TODO: delay as much of this work until just before first playback? SkRecordOptimize(fRecord); + if (fRecord->count() == 0) { + if (finishFlags & kReturnNullForEmpty_FinishFlag) { + return nullptr; + } + } + SkAutoTUnref saveLayerData; if (fBBH && (fFlags & kComputeSaveLayerInfo_RecordFlag)) { @@ -97,9 +106,10 @@ sk_sp SkPictureRecorder::finishRecordingAsPicture() { saveLayerData.release(), subPictureBytes); } -sk_sp SkPictureRecorder::finishRecordingAsPictureWithCull(const SkRect& cullRect) { +sk_sp SkPictureRecorder::finishRecordingAsPictureWithCull(const SkRect& cullRect, + uint32_t finishFlags) { fCullRect = cullRect; - return this->finishRecordingAsPicture(); + return this->finishRecordingAsPicture(finishFlags); } @@ -118,14 +128,19 @@ void SkPictureRecorder::partialReplay(SkCanvas* canvas) const { SkRecordDraw(*fRecord, canvas, nullptr, drawables, drawableCount, nullptr/*bbh*/, nullptr/*callback*/); } -sk_sp SkPictureRecorder::finishRecordingAsDrawable() { +sk_sp SkPictureRecorder::finishRecordingAsDrawable(uint32_t finishFlags) { fActivelyRecording = false; fRecorder->flushMiniRecorder(); fRecorder->restoreToCount(1); // If we were missing any restores, add them now. - // TODO: delay as much of this work until just before first playback? SkRecordOptimize(fRecord); + if (fRecord->count() == 0) { + if (finishFlags & kReturnNullForEmpty_FinishFlag) { + return nullptr; + } + } + if (fBBH.get()) { SkAutoTMalloc bounds(fRecord->count()); SkRecordFillBounds(fCullRect, *fRecord, bounds); diff --git a/src/core/SkRecordOpts.cpp b/src/core/SkRecordOpts.cpp index ea19d6eaa5..aa07facb19 100644 --- a/src/core/SkRecordOpts.cpp +++ b/src/core/SkRecordOpts.cpp @@ -276,7 +276,7 @@ void SkRecordOptimize(SkRecord* record) { // out junk for other optimization passes. Right now, nothing needs it, // and the bounding box hierarchy will do the work of skipping no-op // Save-NoDraw-Restore sequences better than we can here. - //SkRecordNoopSaveRestores(record); + SkRecordNoopSaveRestores(record); SkRecordNoopSaveLayerDrawRestores(record); SkRecordMergeSvgOpacityAndFilterLayers(record); diff --git a/tests/PictureTest.cpp b/tests/PictureTest.cpp index f109482311..eb0c6a8f0e 100644 --- a/tests/PictureTest.cpp +++ b/tests/PictureTest.cpp @@ -1447,3 +1447,63 @@ DEF_TEST(PictureGpuAnalyzer, r) { } #endif // SK_SUPPORT_GPU + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +static void empty_ops(SkCanvas* canvas) { +} +static void clip_ops(SkCanvas* canvas) { + canvas->save(); + canvas->clipRect(SkRect::MakeWH(20, 20)); + canvas->restore(); +} +static void matrix_ops(SkCanvas* canvas) { + canvas->save(); + canvas->scale(2, 3); + canvas->restore(); +} +static void matrixclip_ops(SkCanvas* canvas) { + canvas->save(); + canvas->scale(2, 3); + canvas->clipRect(SkRect::MakeWH(20, 20)); + canvas->restore(); +} +typedef void (*CanvasProc)(SkCanvas*); + +// Test the kReturnNullForEmpty_FinishFlag option when recording +// +DEF_TEST(Picture_RecordEmpty, r) { + const SkRect cull = SkRect::MakeWH(100, 100); + + CanvasProc procs[] { empty_ops, clip_ops, matrix_ops, matrixclip_ops }; + + for (auto proc : procs) { + { + SkPictureRecorder rec; + proc(rec.beginRecording(cull)); + sk_sp pic = rec.finishRecordingAsPicture(0); + REPORTER_ASSERT(r, pic.get()); + REPORTER_ASSERT(r, pic->approximateOpCount() == 0); + } + { + SkPictureRecorder rec; + proc(rec.beginRecording(cull)); + sk_sp pic = rec.finishRecordingAsPicture( + SkPictureRecorder::kReturnNullForEmpty_FinishFlag); + REPORTER_ASSERT(r, !pic.get()); + } + { + SkPictureRecorder rec; + proc(rec.beginRecording(cull)); + sk_sp dr = rec.finishRecordingAsDrawable(0); + REPORTER_ASSERT(r, dr.get()); + } + { + SkPictureRecorder rec; + proc(rec.beginRecording(cull)); + sk_sp dr = rec.finishRecordingAsDrawable( + SkPictureRecorder::kReturnNullForEmpty_FinishFlag); + REPORTER_ASSERT(r, !dr.get()); + } + } +} diff --git a/tests/RecordReplaceDrawTest.cpp b/tests/RecordReplaceDrawTest.cpp index 0dd804ec9d..388bee8d7b 100644 --- a/tests/RecordReplaceDrawTest.cpp +++ b/tests/RecordReplaceDrawTest.cpp @@ -78,6 +78,11 @@ DEF_TEST(RecordReplaceDraw_Unbalanced, r) { canvas->save(); canvas->scale(2, 2); pic = recorder.finishRecordingAsPicture(); + + // we may have optimized everything away. If so, just return + if (pic->approximateOpCount() == 0) { + return; + } } SkRecord rerecord; -- cgit v1.2.3