diff options
-rw-r--r-- | dm/DM.cpp | 8 | ||||
-rw-r--r-- | dm/DMSrcSink.cpp | 158 | ||||
-rw-r--r-- | dm/DMSrcSink.h | 12 | ||||
-rw-r--r-- | gn/core.gni | 4 | ||||
-rw-r--r-- | include/core/SkDeferredDisplayListRecorder.h | 52 | ||||
-rw-r--r-- | include/core/SkSurface.h | 17 | ||||
-rw-r--r-- | include/private/SkDeferredDisplayList.h | 43 | ||||
-rw-r--r-- | include/private/SkSurfaceCharacterization.h | 54 | ||||
-rw-r--r-- | src/core/SkDeferredDisplayListRecorder.cpp | 46 | ||||
-rw-r--r-- | src/image/SkSurface.cpp | 8 | ||||
-rw-r--r-- | src/image/SkSurface_Base.h | 3 | ||||
-rw-r--r-- | src/image/SkSurface_Gpu.cpp | 31 | ||||
-rw-r--r-- | src/image/SkSurface_Gpu.h | 3 | ||||
-rw-r--r-- | tools/flags/SkCommonFlags.h | 1 |
14 files changed, 427 insertions, 13 deletions
@@ -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<SKPSrc>(FLAGS_skps, "skp"); + if (FLAGS_ddl) { + gather_file_srcs<DDLSKPSrc>(FLAGS_skps, "skp"); + } else { + gather_file_srcs<SKPSrc>(FLAGS_skps, "skp"); + } gather_file_srcs<MSKPSrc>(FLAGS_mskps, "mskp"); #if defined(SK_XML) gather_file_srcs<SVGSrc>(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 <cmath> #include <functional> @@ -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<SkStream> stream = SkStream::MakeFromFile(fPath.c_str()); +static sk_sp<SkPicture> read_skp(const char* path) { + std::unique_ptr<SkStream> stream = SkStream::MakeFromFile(path); if (!stream) { - return SkStringPrintf("Couldn't read %s.", fPath.c_str()); + return nullptr; } sk_sp<SkPicture> 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<SkPicture> 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<SkStream> stream = SkStream::MakeFromFile(fPath.c_str()); +static SkRect get_cull_rect_for_skp(const char* path) { + std::unique_ptr<SkStream> 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(); @@ -1179,6 +1196,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<SkSurface> 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<SkImage> img = fSurface->makeImageSnapshot(); + dst->save(); + dst->clipRect(SkRect::Make(fClip)); + dst->drawImage(std::move(img), fClip.fLeft, fClip.fTop); + dst->restore(); + } + + private: + sk_sp<SkSurface> fSurface; + SkIRect fClip; // in the device space of the destination canvas + std::unique_ptr<SkDeferredDisplayList> fDisplayList; + SkSurfaceCharacterization fCharacterization; + }; + + SkTArray<TileData> tileData; + tileData.reserve(16); + + sk_sp<SkPicture> 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. static const SkSize kDefaultSVGSize = {1000, 1000}; 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<SkDeferredDisplayList> detach(); + +private: + SkSurfaceCharacterization fCharacterization; + + sk_sp<SkSurface> 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<SkImage> 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<SkImage> 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<SkDeferredDisplayList> SkDeferredDisplayListRecorder::detach() { + sk_sp<SkImage> img = fSurface->makeImageSnapshot(); + fSurface.reset(); + + // TODO: need to wrap the opLists associated with the deferred draws + // in the SkDeferredDisplayList. + return std::unique_ptr<SkDeferredDisplayList>( + 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<SkSurface*>(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<SkImage> 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); |