aboutsummaryrefslogtreecommitdiffhomepage
path: root/dm
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 /dm
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>
Diffstat (limited to 'dm')
-rw-r--r--dm/DM.cpp8
-rw-r--r--dm/DMSrcSink.cpp158
-rw-r--r--dm/DMSrcSink.h12
3 files changed, 165 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