aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Robert Phillips <robertphillips@google.com>2017-08-30 12:06:35 -0400
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2017-08-30 19:25:47 +0000
commitad8a43f7698071f00ce024a935b0bc04a4b19a41 (patch)
treee9d501a363e4ec0f9ff42a15dd0c1c7c717b15e7
parentf1942de288ad593b1348029aefaad2cf99ad57ea (diff)
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 <bsalomon@google.com> Commit-Queue: Robert Phillips <robertphillips@google.com>
-rw-r--r--dm/DM.cpp8
-rw-r--r--dm/DMSrcSink.cpp158
-rw-r--r--dm/DMSrcSink.h12
-rw-r--r--gn/core.gni4
-rw-r--r--include/core/SkDeferredDisplayListRecorder.h52
-rw-r--r--include/core/SkSurface.h17
-rw-r--r--include/private/SkDeferredDisplayList.h43
-rw-r--r--include/private/SkSurfaceCharacterization.h54
-rw-r--r--src/core/SkDeferredDisplayListRecorder.cpp46
-rw-r--r--src/image/SkSurface.cpp8
-rw-r--r--src/image/SkSurface_Base.h3
-rw-r--r--src/image/SkSurface_Gpu.cpp31
-rw-r--r--src/image/SkSurface_Gpu.h3
-rw-r--r--tools/flags/SkCommonFlags.h1
14 files changed, 427 insertions, 13 deletions
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<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);