From ad8a43f7698071f00ce024a935b0bc04a4b19a41 Mon Sep 17 00:00:00 2001 From: Robert Phillips Date: Wed, 30 Aug 2017 12:06:35 -0400 Subject: DeferredDisplayList API proposal Chrome would like to perform cpu-side preprocessing for gpu draws in parallel. They do not want to go through a picture (since they have their own display list format). The general idea is that we add a new SkDeferredDisplayListRecorder class to perform all of Ganesh's cpu-side preprocessing ahead of time and in parallel. The SkDDLRecorder operates like SkPictureRecorder. The user can get an SkCanvas from the SkDDLRecorder and feed it draw operations. Once finished, the user calls 'detach' to get an SkDeferredDisplayList. All the work up to and including the 'detach' call can be done in parallel and will not touch the GPU. To actually get pixels the client must call SkSurface::draw(SkDDL) on an SkSurface that is "compatible" with the surface characterization initially given to the SkDDLMaker. The surface characterization contains the minimum amount of information Ganesh needs to know about the ultimate destination in order to perform its cpu-side work (i.e., caps, width, height, config). Change-Id: I75faa483ab5a6b779c8de56ea56b9d90b990f43a Reviewed-on: https://skia-review.googlesource.com/30140 Reviewed-by: Brian Salomon Commit-Queue: Robert Phillips --- dm/DM.cpp | 8 +- dm/DMSrcSink.cpp | 158 +++++++++++++++++++++++++-- dm/DMSrcSink.h | 12 ++ gn/core.gni | 4 + include/core/SkDeferredDisplayListRecorder.h | 52 +++++++++ include/core/SkSurface.h | 17 +++ include/private/SkDeferredDisplayList.h | 43 ++++++++ include/private/SkSurfaceCharacterization.h | 54 +++++++++ src/core/SkDeferredDisplayListRecorder.cpp | 46 ++++++++ src/image/SkSurface.cpp | 8 ++ src/image/SkSurface_Base.h | 3 + src/image/SkSurface_Gpu.cpp | 31 ++++++ src/image/SkSurface_Gpu.h | 3 + tools/flags/SkCommonFlags.h | 1 + 14 files changed, 427 insertions(+), 13 deletions(-) create mode 100644 include/core/SkDeferredDisplayListRecorder.h create mode 100644 include/private/SkDeferredDisplayList.h create mode 100644 include/private/SkSurfaceCharacterization.h create mode 100644 src/core/SkDeferredDisplayListRecorder.cpp diff --git a/dm/DM.cpp b/dm/DM.cpp index 1c89ae0fdd..044fea5907 100644 --- a/dm/DM.cpp +++ b/dm/DM.cpp @@ -92,6 +92,8 @@ DEFINE_int32(shard, 0, "Which shard do I run?"); DEFINE_string(mskps, "", "Directory to read mskps from, or a single mskp file."); DEFINE_bool(forceRasterPipeline, false, "sets gSkForceRasterPipelineBlitter"); +DEFINE_bool(ddl, false, "If true, use DeferredDisplayLists for GPU SKP rendering."); + #if SK_SUPPORT_GPU DEFINE_pathrenderer_flag; #endif @@ -773,7 +775,11 @@ static bool gather_srcs() { push_src("gm", "", new GMSrc(r->factory())); } - gather_file_srcs(FLAGS_skps, "skp"); + if (FLAGS_ddl) { + gather_file_srcs(FLAGS_skps, "skp"); + } else { + gather_file_srcs(FLAGS_skps, "skp"); + } gather_file_srcs(FLAGS_mskps, "mskp"); #if defined(SK_XML) gather_file_srcs(FLAGS_svgs, "svg"); diff --git a/dm/DMSrcSink.cpp b/dm/DMSrcSink.cpp index e14e1f9963..dc8a913fa1 100644 --- a/dm/DMSrcSink.cpp +++ b/dm/DMSrcSink.cpp @@ -19,6 +19,7 @@ #include "SkData.h" #include "SkDebugCanvas.h" #include "SkDeferredCanvas.h" +#include "SkDeferredDisplayListRecorder.h" #include "SkDocument.h" #include "SkExecutor.h" #include "SkImageGenerator.h" @@ -39,9 +40,11 @@ #include "SkRandom.h" #include "SkRecordDraw.h" #include "SkRecorder.h" +#include "SkSurfaceCharacterization.h" #include "SkSVGCanvas.h" #include "SkStream.h" #include "SkSwizzler.h" +#include "SkTaskGroup.h" #include "SkTLogic.h" #include #include @@ -1140,37 +1143,51 @@ Name ColorCodecSrc::name() const { /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ -static const SkRect kSKPViewport = {0,0, 1000,1000}; +static const SkRect kSKPViewport = {0, 0, 1000, 1000}; -SKPSrc::SKPSrc(Path path) : fPath(path) {} +SKPSrc::SKPSrc(Path path) : fPath(path) { } -Error SKPSrc::draw(SkCanvas* canvas) const { - std::unique_ptr stream = SkStream::MakeFromFile(fPath.c_str()); +static sk_sp read_skp(const char* path) { + std::unique_ptr stream = SkStream::MakeFromFile(path); if (!stream) { - return SkStringPrintf("Couldn't read %s.", fPath.c_str()); + return nullptr; } sk_sp pic(SkPicture::MakeFromStream(stream.get())); if (!pic) { - return SkStringPrintf("Couldn't decode %s as a picture.", fPath.c_str()); + return nullptr; } stream = nullptr; // Might as well drop this when we're done with it. + return pic; +} + +Error SKPSrc::draw(SkCanvas* canvas) const { + sk_sp pic = read_skp(fPath.c_str()); + if (!pic) { + return SkStringPrintf("Couldn't read %s.", fPath.c_str()); + } + canvas->clipRect(kSKPViewport); canvas->drawPicture(pic); return ""; } -SkISize SKPSrc::size() const { - std::unique_ptr stream = SkStream::MakeFromFile(fPath.c_str()); +static SkRect get_cull_rect_for_skp(const char* path) { + std::unique_ptr stream = SkStream::MakeFromFile(path); if (!stream) { - return {0, 0}; + return SkRect::MakeEmpty(); } SkPictInfo info; if (!SkPicture::InternalOnly_StreamIsSKP(stream.get(), &info)) { - return {0, 0}; + return SkRect::MakeEmpty(); } - SkRect viewport = kSKPViewport; - if (!viewport.intersect(info.fCullRect)) { + + return info.fCullRect; +} + +SkISize SKPSrc::size() const { + SkRect viewport = get_cull_rect_for_skp(fPath.c_str()); + if (!viewport.intersect(kSKPViewport)) { return {0, 0}; } return viewport.roundOut().size(); @@ -1178,6 +1195,123 @@ SkISize SKPSrc::size() const { Name SKPSrc::name() const { return SkOSPath::Basename(fPath.c_str()); } +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +static const int kNumDDLXTiles = 4; +static const int kNumDDLYTiles = 4; +static const int kDDLTileSize = 1024; +static const SkRect kDDLSKPViewport = { 0, 0, + kNumDDLXTiles * kDDLTileSize, + kNumDDLYTiles * kDDLTileSize }; + +DDLSKPSrc::DDLSKPSrc(Path path) : fPath(path) { } + +Error DDLSKPSrc::draw(SkCanvas* canvas) const { + class TileData { + public: + // Note: we could just pass in surface characterization + TileData(sk_sp surf, const SkIRect& clip) + : fSurface(std::move(surf)) + , fClip(clip) { + SkAssertResult(fSurface->characterize(&fCharacterization)); + } + + // This method operates in parallel + void preprocess(SkPicture* pic) { + SkDeferredDisplayListRecorder recorder(fCharacterization); + + SkCanvas* subCanvas = recorder.getCanvas(); + + subCanvas->clipRect(SkRect::MakeWH(fClip.width(), fClip.height())); + subCanvas->translate(-fClip.fLeft, -fClip.fTop); + + // Note: in this use case we only render a picture to the deferred canvas + // but, more generally, clients will use arbitrary draw calls. + subCanvas->drawPicture(pic); + + fDisplayList = recorder.detach(); + } + + // This method operates serially + void draw() { + fSurface->draw(fDisplayList.get()); + } + + // This method also operates serially + void compose(SkCanvas* dst) { + sk_sp img = fSurface->makeImageSnapshot(); + dst->save(); + dst->clipRect(SkRect::Make(fClip)); + dst->drawImage(std::move(img), fClip.fLeft, fClip.fTop); + dst->restore(); + } + + private: + sk_sp fSurface; + SkIRect fClip; // in the device space of the destination canvas + std::unique_ptr fDisplayList; + SkSurfaceCharacterization fCharacterization; + }; + + SkTArray tileData; + tileData.reserve(16); + + sk_sp pic = read_skp(fPath.c_str()); + if (!pic) { + return SkStringPrintf("Couldn't read %s.", fPath.c_str()); + } + + const SkRect cullRect = pic->cullRect(); + + // All the destination tiles are the same size + const SkImageInfo tileII = SkImageInfo::MakeN32Premul(kDDLTileSize, kDDLTileSize); + + // First, create the destination tiles + for (int y = 0; y < kNumDDLYTiles; ++y) { + for (int x = 0; x < kNumDDLXTiles; ++x) { + SkRect clip = SkRect::MakeXYWH(x * kDDLTileSize, y * kDDLTileSize, + kDDLTileSize, kDDLTileSize); + + if (!clip.intersect(cullRect)) { + continue; + } + + tileData.push_back(TileData(canvas->makeSurface(tileII), clip.roundOut())); + } + } + + // Second, run the cpu pre-processing in threads + SkTaskGroup().batch(tileData.count(), [&](int i) { + tileData[i].preprocess(pic.get()); + }); + + // Third, synchronously render the display lists into the dest tiles + // TODO: it would be cool to not wait until all the tiles are drawn to begin + // drawing to the GPU + for (int i = 0; i < tileData.count(); ++i) { + tileData[i].draw(); + } + + // Finally, compose the drawn tiles into the result + // Note: the separation between the tiles and the final composition better + // matches Chrome but costs us a copy + for (int i = 0; i < tileData.count(); ++i) { + tileData[i].compose(canvas); + } + + return ""; +} + +SkISize DDLSKPSrc::size() const { + SkRect viewport = get_cull_rect_for_skp(fPath.c_str()); + if (!viewport.intersect(kDDLSKPViewport)) { + return {0, 0}; + } + return viewport.roundOut().size(); +} + +Name DDLSKPSrc::name() const { return SkOSPath::Basename(fPath.c_str()); } + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ #if defined(SK_XML) // Used when the image doesn't have an intrinsic size. diff --git a/dm/DMSrcSink.h b/dm/DMSrcSink.h index c1af4d01b5..26c32f59b7 100644 --- a/dm/DMSrcSink.h +++ b/dm/DMSrcSink.h @@ -248,6 +248,18 @@ private: Path fPath; }; +// DeferredDisplayList flavor +class DDLSKPSrc : public Src { +public: + explicit DDLSKPSrc(Path path); + + Error draw(SkCanvas*) const override; + SkISize size() const override; + Name name() const override; +private: + Path fPath; +}; + #if defined(SK_XML) } // namespace DM diff --git a/gn/core.gni b/gn/core.gni index 5cb3e094b2..862bdd0b9a 100644 --- a/gn/core.gni +++ b/gn/core.gni @@ -96,6 +96,7 @@ skia_core_sources = [ "$_src/core/SkData.cpp", "$_src/core/SkDataTable.cpp", "$_src/core/SkDebug.cpp", + "$_src/core/SkDeferredDisplayListRecorder.cpp", "$_src/core/SkDeque.cpp", "$_src/core/SkDescriptor.h", "$_src/core/SkDevice.cpp", @@ -371,6 +372,7 @@ skia_core_sources = [ "$_include/core/SkColorFilter.h", "$_include/core/SkColorPriv.h", "$_include/core/SkData.h", + "$_include/core/SkDeferredDisplayListRecorder.h", "$_include/core/SkDeque.h", "$_include/core/SkDrawable.h", "$_include/core/SkDrawFilter.h", @@ -429,6 +431,7 @@ skia_core_sources = [ # private "$_include/private/SkAtomics.h", "$_include/private/SkChecksum.h", + "$_include/private/SkDeferredDisplayList.h", "$_include/private/SkFixed.h", "$_include/private/SkFloatBits.h", "$_include/private/SkFloatingPoint.h", @@ -440,6 +443,7 @@ skia_core_sources = [ "$_include/private/SkSemaphore.h", "$_include/private/SkShadowFlags.h", "$_include/private/SkSpinlock.h", + "$_include/private/SkSurfaceCharacterization.h", "$_include/private/SkTemplates.h", "$_include/private/SkTArray.h", "$_include/private/SkTDArray.h", diff --git a/include/core/SkDeferredDisplayListRecorder.h b/include/core/SkDeferredDisplayListRecorder.h new file mode 100644 index 0000000000..185adfa99b --- /dev/null +++ b/include/core/SkDeferredDisplayListRecorder.h @@ -0,0 +1,52 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkDeferredDisplayListMaker_DEFINED +#define SkDeferredDisplayListMaker_DEFINED + +#include "SkRefCnt.h" + +#include "../private/SkDeferredDisplayList.h" +#include "../private/SkSurfaceCharacterization.h" + +class SkCanvas; +class SkSurface; // TODO: remove + +/* + * This class is intended to be used as: + * Get an SkSurfaceCharacterization from the ultimate intended gpu-backed destination SkSurface + * Create one of these (an SkDDLMaker) on the stack + * Get the canvas and render into it + * Snap off and hold on to an SkDeferredDisplayList + * Once your app actually needs the pixels, call SkSurface::draw(SkDeferredDisplayList*) + * + * This class never accesses the GPU but performs all the cpu work it can. It + * is thread-safe (i.e., one can break a scene into tiles and perform their cpu-side + * work in parallel ahead of time). + */ +class SkDeferredDisplayListRecorder { +public: + SkDeferredDisplayListRecorder(const SkSurfaceCharacterization&); + + const SkSurfaceCharacterization& characterization() const { + return fCharacterization; + } + + // The backing canvas will become invalid (and this entry point will return + // null) once 'detach' is called. + // Note: ownership of the SkCanvas is not transfered via this call. + SkCanvas* getCanvas(); + + std::unique_ptr detach(); + +private: + SkSurfaceCharacterization fCharacterization; + + sk_sp fSurface; // temporary until we have a real implementation +}; + +#endif diff --git a/include/core/SkSurface.h b/include/core/SkSurface.h index eaf55050b8..890532f983 100644 --- a/include/core/SkSurface.h +++ b/include/core/SkSurface.h @@ -15,7 +15,9 @@ #include "GrTypes.h" class SkCanvas; +class SkDeferredDisplayList; class SkPaint; +class SkSurfaceCharacterization; class GrBackendRenderTarget; class GrBackendSemaphore; class GrContext; @@ -335,6 +337,21 @@ public: */ bool wait(int numSemaphores, const GrBackendSemaphore* waitSemaphores); + /** + * This creates a characterization of this SkSurface's properties that can + * be used to perform gpu-backend preprocessing in a separate thread (via + * the SkDeferredDisplayListRecorder). + * It will return false on failure (e.g., if the SkSurface is cpu-backed). + */ + bool characterize(SkSurfaceCharacterization* characterization) const; + + /** + * Draw a deferred display list (created via SkDeferredDisplayListRecorder). + * The draw will be skipped if the characterization stored in the display list + * isn't compatible with this surface. + */ + void draw(SkDeferredDisplayList* deferredDisplayList); + protected: SkSurface(int width, int height, const SkSurfaceProps*); SkSurface(const SkImageInfo&, const SkSurfaceProps*); diff --git a/include/private/SkDeferredDisplayList.h b/include/private/SkDeferredDisplayList.h new file mode 100644 index 0000000000..37e792ff20 --- /dev/null +++ b/include/private/SkDeferredDisplayList.h @@ -0,0 +1,43 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkDeferredDisplayList_DEFINED +#define SkDeferredDisplayList_DEFINED + +#include "SkSurfaceCharacterization.h" + +class SkImage; // TODO: rm this + +/* + * This class contains pre-processed gpu operations that can be replayed into + * an SkSurface via draw(SkDeferredDisplayList*). + * + * TODO: we probably need to expose this class so users can query it for memory usage. + */ +class SkDeferredDisplayList { +public: + SkDeferredDisplayList(const SkSurfaceCharacterization& characterization, + sk_sp image) // TODO rm this parameter + : fCharacterization(characterization) + , fImage(std::move(image)) { + } + + const SkSurfaceCharacterization& characterization() const { + return fCharacterization; + } + + // TODO: remove this. It is just scaffolding to get something up & running + void draw(SkSurface*); + +private: + SkSurfaceCharacterization fCharacterization; + + // TODO: actually store the GPU opLists + sk_sp fImage; +}; + +#endif diff --git a/include/private/SkSurfaceCharacterization.h b/include/private/SkSurfaceCharacterization.h new file mode 100644 index 0000000000..f3acb01236 --- /dev/null +++ b/include/private/SkSurfaceCharacterization.h @@ -0,0 +1,54 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkSurfaceCharacterization_DEFINED +#define SkSurfaceCharacterization_DEFINED + +#include "GrTypes.h" + +class SkSurface; + +// This class captures all the pertinent data about an SkSurface required +// to perform cpu-preprocessing for gpu-rendering. +class SkSurfaceCharacterization { +public: + SkSurfaceCharacterization() + : fOrigin(kBottomLeft_GrSurfaceOrigin) + , fWidth(0) + , fHeight(0) + , fConfig(kRGBA_8888_GrPixelConfig) + , fSampleCnt(0) { + } + + void set(GrSurfaceOrigin origin, + int width, int height, + GrPixelConfig config, + int sampleCnt) { + fOrigin = origin; + fWidth = width; + fHeight = height; + fConfig = config; + fSampleCnt = sampleCnt; + } + + GrSurfaceOrigin origin() const { return fOrigin; } + int width() const { return fWidth; } + int height() const { return fHeight; } + GrPixelConfig config() const { return fConfig; } + int sampleCount() const { return fSampleCnt; } + +private: + GrSurfaceOrigin fOrigin; + int fWidth; + int fHeight; + GrPixelConfig fConfig; + int fSampleCnt; + // TODO: need to include caps! + // Maybe use GrContextThreadSafeProxy (it has the caps & the unique Context ID already) +}; + +#endif diff --git a/src/core/SkDeferredDisplayListRecorder.cpp b/src/core/SkDeferredDisplayListRecorder.cpp new file mode 100644 index 0000000000..c47a79a77d --- /dev/null +++ b/src/core/SkDeferredDisplayListRecorder.cpp @@ -0,0 +1,46 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkDeferredDisplayListRecorder.h" + +#include "SkCanvas.h" // TODO: remove +#include "SkDeferredDisplayList.h" +#include "SkSurface.h" // TODO: remove + +SkDeferredDisplayListRecorder::SkDeferredDisplayListRecorder( + const SkSurfaceCharacterization& characterization) + : fCharacterization(characterization) { +} + +SkCanvas* SkDeferredDisplayListRecorder::getCanvas() { + if (!fSurface) { + SkImageInfo ii = SkImageInfo::MakeN32(fCharacterization.width(), + fCharacterization.height(), + kOpaque_SkAlphaType); + + // Use raster right now to allow threading + fSurface = SkSurface::MakeRaster(ii, nullptr); + } + + return fSurface->getCanvas(); +} + +std::unique_ptr SkDeferredDisplayListRecorder::detach() { + sk_sp img = fSurface->makeImageSnapshot(); + fSurface.reset(); + + // TODO: need to wrap the opLists associated with the deferred draws + // in the SkDeferredDisplayList. + return std::unique_ptr( + new SkDeferredDisplayList(fCharacterization, std::move(img))); +} + +// Placeholder. Ultimately, the SkSurface_Gpu will pass the wrapped opLists to its +// renderTargetContext. +void SkDeferredDisplayList::draw(SkSurface* surface) { + surface->getCanvas()->drawImage(fImage.get(), 0, 0); +} diff --git a/src/image/SkSurface.cpp b/src/image/SkSurface.cpp index a196145c99..1de7b609e3 100644 --- a/src/image/SkSurface.cpp +++ b/src/image/SkSurface.cpp @@ -209,6 +209,14 @@ bool SkSurface::wait(int numSemaphores, const GrBackendSemaphore* waitSemaphores return asSB(this)->onWait(numSemaphores, waitSemaphores); } +bool SkSurface::characterize(SkSurfaceCharacterization* characterization) const { + return asSB(const_cast(this))->onCharacterize(characterization); +} + +void SkSurface::draw(SkDeferredDisplayList* ddl) { + return asSB(this)->onDraw(ddl); +} + ////////////////////////////////////////////////////////////////////////////////////// #include "SkNoDrawCanvas.h" diff --git a/src/image/SkSurface_Base.h b/src/image/SkSurface_Base.h index 8cf75b60e4..e05d371cd8 100644 --- a/src/image/SkSurface_Base.h +++ b/src/image/SkSurface_Base.h @@ -94,6 +94,9 @@ public: return false; } + virtual bool onCharacterize(SkSurfaceCharacterization*) const { return false; } + virtual void onDraw(SkDeferredDisplayList*) { } + inline SkCanvas* getCachedCanvas(); inline sk_sp refCachedImage(); diff --git a/src/image/SkSurface_Gpu.cpp b/src/image/SkSurface_Gpu.cpp index a5acf4f95a..042fc32a67 100644 --- a/src/image/SkSurface_Gpu.cpp +++ b/src/image/SkSurface_Gpu.cpp @@ -15,11 +15,13 @@ #include "SkCanvas.h" #include "SkColorSpace_Base.h" +#include "SkDeferredDisplayList.h" #include "SkGpuDevice.h" #include "SkImage_Base.h" #include "SkImage_Gpu.h" #include "SkImagePriv.h" #include "SkSurface_Base.h" +#include "SkSurfaceCharacterization.h" #if SK_SUPPORT_GPU @@ -158,6 +160,35 @@ bool SkSurface_Gpu::onWait(int numSemaphores, const GrBackendSemaphore* waitSema return fDevice->wait(numSemaphores, waitSemaphores); } +bool SkSurface_Gpu::onCharacterize(SkSurfaceCharacterization* data) const { + GrRenderTargetContext* rtc = fDevice->accessRenderTargetContext(); + + data->set(rtc->origin(), rtc->width(), rtc->height(), + rtc->config(), rtc->numColorSamples()); + return true; +} + +bool SkSurface_Gpu::isCompatible(const SkSurfaceCharacterization& data) const { + GrRenderTargetContext* rtc = fDevice->accessRenderTargetContext(); + + return data.origin() == rtc->origin() && + data.width() == rtc->width() && + data.height() == rtc->height() && + data.config() == rtc->config() && + data.sampleCount() == rtc->numColorSamples(); +} + +void SkSurface_Gpu::onDraw(SkDeferredDisplayList* dl) { + if (!this->isCompatible(dl->characterization())) { + return; + } + + // Ultimately need to pass opLists from the DeferredDisplayList on to the + // SkGpuDevice's renderTargetContext. + dl->draw(this); +} + + /////////////////////////////////////////////////////////////////////////////// bool SkSurface_Gpu::Valid(const SkImageInfo& info) { diff --git a/src/image/SkSurface_Gpu.h b/src/image/SkSurface_Gpu.h index ebf7d4ed44..c95966370c 100644 --- a/src/image/SkSurface_Gpu.h +++ b/src/image/SkSurface_Gpu.h @@ -29,6 +29,9 @@ public: GrSemaphoresSubmitted onFlush(int numSemaphores, GrBackendSemaphore signalSemaphores[]) override; bool onWait(int numSemaphores, const GrBackendSemaphore* waitSemaphores) override; + bool onCharacterize(SkSurfaceCharacterization*) const override; + bool isCompatible(const SkSurfaceCharacterization&) const; + void onDraw(SkDeferredDisplayList*) override; SkGpuDevice* getDevice() { return fDevice.get(); } diff --git a/tools/flags/SkCommonFlags.h b/tools/flags/SkCommonFlags.h index d35e9a8d51..ef1897b7bc 100644 --- a/tools/flags/SkCommonFlags.h +++ b/tools/flags/SkCommonFlags.h @@ -25,6 +25,7 @@ DECLARE_bool(preAbandonGpuContext); DECLARE_bool(abandonGpuContext); DECLARE_bool(releaseAndAbandonGpuContext); DECLARE_string(skps); +DECLARE_bool(ddl); DECLARE_string(svgs); DECLARE_int32(threads); DECLARE_string(resourcePath); -- cgit v1.2.3