aboutsummaryrefslogtreecommitdiffhomepage
path: root/dm
diff options
context:
space:
mode:
authorGravatar Robert Phillips <robertphillips@google.com>2018-05-29 16:13:26 -0400
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2018-05-30 10:12:08 +0000
commit96601084b3f7b108c1faf12a2ea12eb7ea8688a0 (patch)
tree222f8ec851bd0156b32a382f458226170f7b33c0 /dm
parent16f558ddeed33c816f3d3dad03997b2ea523c5b9 (diff)
Add DDL to SKPBench
Most of this CL is just repackaging the promise image and tile code from ViaDDL for reuse by SKPBench. Change-Id: Ie5003c36fe85cc5be9639552f9488b8e92dcdbbf Reviewed-on: https://skia-review.googlesource.com/129805 Reviewed-by: Chris Dalton <csmartdalton@google.com> Reviewed-by: Greg Daniel <egdaniel@google.com> Commit-Queue: Robert Phillips <robertphillips@google.com>
Diffstat (limited to 'dm')
-rw-r--r--dm/DMSrcSink.cpp373
-rw-r--r--dm/DMSrcSink.h3
2 files changed, 18 insertions, 358 deletions
diff --git a/dm/DMSrcSink.cpp b/dm/DMSrcSink.cpp
index 9def615f2b..ffe01dbd97 100644
--- a/dm/DMSrcSink.cpp
+++ b/dm/DMSrcSink.cpp
@@ -9,6 +9,8 @@
#include <cmath>
#include <functional>
#include "../src/jumper/SkJumper.h"
+#include "DDLPromiseImageHelper.h"
+#include "DDLTileHelper.h"
#include "Resources.h"
#include "SkAndroidCodec.h"
#include "SkAutoMalloc.h"
@@ -2019,302 +2021,6 @@ ViaDDL::ViaDDL(int numDivisions, Sink* sink)
, fNumDivisions(numDivisions) {
}
-// This class consolidates tracking & extraction of the original image data from the sources,
-// the upload of said data to the GPU and the fulfillment of promise images.
-//
-// The way this works is:
-// the original skp is converted to SkData and all its image info is extracted into this
-// class and only indices into this class are left in the SkData
-// Prior to replaying in threads, all the images stored in this class are uploaded to the
-// gpu and PromiseImageCallbackContexts are created for them
-// Each thread reinflates the SkData into an SkPicture replacing all the indices w/
-// promise images (all using the same GrBackendTexture and getting a ref to the
-// appropriate PromiseImageCallbackContext) and then creates a DDL.
-// This class is then reset - dropping all of its refs on the PromiseImageCallbackContexts
-// Each done callback unrefs its PromiseImageCallbackContext so, once all the promise images
-// are done the PromiseImageCallbackContext is freed and its GrBackendTexture removed
-// from VRAM
-//
-class ViaDDL::PromiseImageHelper {
-public:
- // This class acts as a proxy for the single GrBackendTexture representing an image.
- // Whenever a promise image is created for the image the promise image receives a ref to
- // this object. Once all the promise images receive their done callbacks this object
- // is deleted - removing the GrBackendTexture from VRAM.
- // Note that while the DDLs are being created in the threads, the PromiseImageHelper holds
- // a ref on all the PromiseImageCallbackContexts. However, once all the threads are done
- // it drops all of its refs (via "reset").
- class PromiseImageCallbackContext : public SkRefCnt {
- public:
- PromiseImageCallbackContext(GrContext* context) : fContext(context) {}
-
- ~PromiseImageCallbackContext() {
- GrGpu* gpu = fContext->contextPriv().getGpu();
-
- if (fBackendTexture.isValid()) {
- gpu->deleteTestingOnlyBackendTexture(fBackendTexture);
- }
- }
-
- void setBackendTexture(const GrBackendTexture& backendTexture) {
- fBackendTexture = backendTexture;
- }
-
- const GrBackendTexture& backendTexture() const { return fBackendTexture; }
-
- private:
- GrContext* fContext;
- GrBackendTexture fBackendTexture;
-
- typedef SkRefCnt INHERITED;
- };
-
- // This is the information extracted into this class from the parsing of the skp file.
- // Once it has all been uploaded to the GPU and distributed to the promise images, it
- // is all dropped via "reset".
- class PromiseImageInfo {
- public:
- int fIndex; // index in the 'fImageInfo' array
- uint32_t fOriginalUniqueID; // original ID for deduping
- SkBitmap fBitmap; // CPU-side cache of the contents
- sk_sp<PromiseImageCallbackContext> fCallbackContext;
- };
-
- PromiseImageHelper() { }
-
- void reset() { fImageInfo.reset(); }
-
- bool isValidID(int id) const {
- return id >= 0 && id < fImageInfo.count();
- }
-
- const PromiseImageInfo& getInfo(int id) const {
- return fImageInfo[id];
- }
-
- // returns -1 on failure
- int findOrDefineImage(SkImage* image) {
- int preExistingID = this->findImage(image);
- if (preExistingID >= 0) {
- SkASSERT(this->isValidID(preExistingID));
- return preExistingID;
- }
-
- int newID = this->addImage(image);
- SkASSERT(this->isValidID(newID));
- return newID;
- }
-
- void uploadAllToGPU(GrContext* context) {
- GrGpu* gpu = context->contextPriv().getGpu();
- SkASSERT(gpu);
-
- for (int i = 0; i < fImageInfo.count(); ++i) {
- sk_sp<PromiseImageCallbackContext> callbackContext(
- new PromiseImageCallbackContext(context));
-
- const PromiseImageInfo& info = fImageInfo[i];
-
- // DDL TODO: how can we tell if we need mipmapping!
- callbackContext->setBackendTexture(gpu->createTestingOnlyBackendTexture(
- info.fBitmap.getPixels(),
- info.fBitmap.width(),
- info.fBitmap.height(),
- info.fBitmap.colorType(),
- info.fBitmap.colorSpace(),
- false, GrMipMapped::kNo));
- // The GMs sometimes request too large an image
- //SkAssertResult(callbackContext->backendTexture().isValid());
-
- // The fImageInfo array gets the creation ref
- fImageInfo[i].fCallbackContext = std::move(callbackContext);
- }
- }
-
- static void PromiseImageFulfillProc(void* textureContext, GrBackendTexture* outTexture) {
- auto callbackContext = static_cast<PromiseImageCallbackContext*>(textureContext);
- SkASSERT(callbackContext->backendTexture().isValid());
- *outTexture = callbackContext->backendTexture();
- }
-
- static void PromiseImageReleaseProc(void* textureContext) {
-#ifdef SK_DEBUG
- auto callbackContext = static_cast<PromiseImageCallbackContext*>(textureContext);
- SkASSERT(callbackContext->backendTexture().isValid());
-#endif
- }
-
- static void PromiseImageDoneProc(void* textureContext) {
- auto callbackContext = static_cast<PromiseImageCallbackContext*>(textureContext);
- callbackContext->unref();
- }
-
-private:
- // returns -1 if not found
- int findImage(SkImage* image) const {
- for (int i = 0; i < fImageInfo.count(); ++i) {
- if (fImageInfo[i].fOriginalUniqueID == image->uniqueID()) {
- SkASSERT(fImageInfo[i].fIndex == i);
- SkASSERT(this->isValidID(i) && this->isValidID(fImageInfo[i].fIndex));
- return i;
- }
- }
- return -1;
- }
-
- // returns -1 on failure
- int addImage(SkImage* image) {
- sk_sp<SkImage> rasterImage = image->makeRasterImage(); // force decoding of lazy images
-
- SkImageInfo ii = SkImageInfo::Make(rasterImage->width(), rasterImage->height(),
- rasterImage->colorType(), rasterImage->alphaType(),
- rasterImage->refColorSpace());
-
- SkBitmap bm;
- bm.allocPixels(ii);
-
- if (!rasterImage->readPixels(bm.pixmap(), 0, 0)) {
- return -1;
- }
-
- bm.setImmutable();
-
- PromiseImageInfo newImageInfo;
- newImageInfo.fIndex = fImageInfo.count();
- newImageInfo.fOriginalUniqueID = image->uniqueID();
- newImageInfo.fBitmap = bm;
- /* fCallbackContext is filled in by uploadAllToGPU */
-
- fImageInfo.push_back(newImageInfo);
- SkASSERT(newImageInfo.fIndex == fImageInfo.count()-1);
- return fImageInfo.count()-1;
- }
-
- SkTArray<PromiseImageInfo> fImageInfo;
-};
-
-// TileData class encapsulates the information and behavior for a single tile/thread in
-// a DDL rendering.
-class ViaDDL::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
- // In each thread we will reconvert the compressedPictureData into an SkPicture
- // replacing each image-index with a promise image.
- void preprocess(SkData* compressedPictureData, const PromiseImageHelper& helper) {
-
- SkDeferredDisplayListRecorder recorder(fCharacterization);
-
- // DDL TODO: the DDLRecorder's GrContext isn't initialized until getCanvas is called.
- // Maybe set it up in the ctor?
- SkCanvas* subCanvas = recorder.getCanvas();
-
- sk_sp<SkPicture> reconstitutedPicture;
-
- {
- PerRecorderContext perRecorderContext { &recorder, &helper };
-
- SkDeserialProcs procs;
- procs.fImageCtx = (void*) &perRecorderContext;
- procs.fImageProc = PromiseImageCreator;
-
- reconstitutedPicture = SkPicture::MakeFromData(compressedPictureData, &procs);
- if (!reconstitutedPicture) {
- return;
- }
- }
-
- 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(reconstitutedPicture);
-
- fDisplayList = recorder.detach();
- }
-
- // This method operates serially and replays the recorded DDL into the tile surface.
- void draw() {
- fSurface->draw(fDisplayList.get());
- }
-
- // This method also operates serially and composes the results of replaying the DDL into
- // the final destination surface.
- 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:
- // This stack-based context allows each thread to re-inflate the image indices into
- // promise images while still using the same GrBackendTexture.
- struct PerRecorderContext {
- SkDeferredDisplayListRecorder* fRecorder;
- const PromiseImageHelper* fHelper;
- };
-
- // This generates promise images to replace the indices in the compressed picture. This
- // reconstitution is performed separately in each thread so we end up with multiple
- // promise images referring to the same GrBackendTexture.
- static sk_sp<SkImage> PromiseImageCreator(const void* rawData, size_t length, void* ctxIn) {
- PerRecorderContext* perRecorderContext = static_cast<PerRecorderContext*>(ctxIn);
- const PromiseImageHelper* helper = perRecorderContext->fHelper;
- SkDeferredDisplayListRecorder* recorder = perRecorderContext->fRecorder;
-
- SkASSERT(length == sizeof(int));
-
- const int* indexPtr = static_cast<const int*>(rawData);
- SkASSERT(helper->isValidID(*indexPtr));
-
- const PromiseImageHelper::PromiseImageInfo& curImage = helper->getInfo(*indexPtr);
-
- if (!curImage.fCallbackContext->backendTexture().isValid()) {
- // We weren't able to make a backend texture for this SkImage. In this case we create
- // a separate bitmap-backed image for each thread.
- // Note: we would like to share the same bitmap between all the threads but
- // SkBitmap is not thread-safe.
- return SkImage::MakeRasterCopy(curImage.fBitmap.pixmap());
- }
- SkASSERT(curImage.fIndex == *indexPtr);
-
- GrBackendFormat backendFormat = curImage.fCallbackContext->backendTexture().format();
-
- // Each DDL recorder gets its own ref on the promise callback context for the
- // promise images it creates.
- // DDL TODO: sort out mipmapping
- sk_sp<SkImage> image = recorder->makePromiseTexture(
- backendFormat,
- curImage.fBitmap.width(),
- curImage.fBitmap.height(),
- GrMipMapped::kNo,
- GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin,
- curImage.fBitmap.colorType(),
- curImage.fBitmap.alphaType(),
- curImage.fBitmap.refColorSpace(),
- PromiseImageHelper::PromiseImageFulfillProc,
- PromiseImageHelper::PromiseImageReleaseProc,
- PromiseImageHelper::PromiseImageDoneProc,
- (void*) SkSafeRef(curImage.fCallbackContext.get()));
- SkASSERT(image);
- return image;
- }
-
- sk_sp<SkSurface> fSurface;
- SkIRect fClip; // in the device space of the dest canvas
- std::unique_ptr<SkDeferredDisplayList> fDisplayList;
- SkSurfaceCharacterization fCharacterization;
-};
-
Error ViaDDL::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const {
auto size = src.size();
SkPictureRecorder recorder;
@@ -2328,30 +2034,10 @@ Error ViaDDL::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString
// this is our ultimate final drawing area/rect
SkIRect viewport = SkIRect::MakeWH(size.fWidth, size.fHeight);
- PromiseImageHelper helper;
- sk_sp<SkData> compressedPictureData;
-
- // Convert the SkPicture into SkData replacing all the SkImages with an index.
- {
- SkSerialProcs procs;
-
- procs.fImageCtx = &helper;
- procs.fImageProc = [](SkImage* image, void* ctx) -> sk_sp<SkData> {
- auto helper = static_cast<PromiseImageHelper*>(ctx);
-
- int id = helper->findOrDefineImage(image);
- if (id >= 0) {
- SkASSERT(helper->isValidID(id));
- return SkData::MakeWithCopy(&id, sizeof(id));
- }
-
- return nullptr;
- };
-
- compressedPictureData = inputPicture->serialize(&procs);
- if (!compressedPictureData) {
- return SkStringPrintf("ViaDDL: Couldn't deflate SkPicture");
- }
+ DDLPromiseImageHelper promiseImageHelper;
+ sk_sp<SkData> compressedPictureData = promiseImageHelper.deflateSKP(inputPicture.get());
+ if (!compressedPictureData) {
+ return SkStringPrintf("ViaDDL: Couldn't deflate SkPicture");
}
return draw_to_canvas(fSink.get(), bitmap, stream, log, size,
@@ -2362,52 +2048,29 @@ Error ViaDDL::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString
}
// This is here bc this is the first point where we have access to the context
- helper.uploadAllToGPU(context);
-
- int xTileSize = viewport.width()/fNumDivisions;
- int yTileSize = viewport.height()/fNumDivisions;
-
- SkTArray<TileData> tileData;
- tileData.reserve(fNumDivisions*fNumDivisions);
-
- // First, create the destination tiles
- for (int y = 0, yOff = 0; y < fNumDivisions; ++y, yOff += yTileSize) {
- int ySize = (y < fNumDivisions-1) ? yTileSize : viewport.height()-yOff;
-
- for (int x = 0, xOff = 0; x < fNumDivisions; ++x, xOff += xTileSize) {
- int xSize = (x < fNumDivisions-1) ? xTileSize : viewport.width()-xOff;
+ promiseImageHelper.uploadAllToGPU(context);
- SkIRect clip = SkIRect::MakeXYWH(xOff, yOff, xSize, ySize);
+ // First, create all the tiles (including their individual dest surfaces)
+ DDLTileHelper tiles(canvas, viewport, fNumDivisions);
- SkASSERT(viewport.contains(clip));
+ // Second, reinflate the compressed picture individually for each thread
+ tiles.createSKPPerTile(compressedPictureData.get(), promiseImageHelper);
- SkImageInfo tileII = SkImageInfo::MakeN32Premul(xSize, ySize);
+ // Third, create the DDLs in parallel
+ tiles.createDDLsInParallel();
- tileData.push_back(TileData(canvas->makeSurface(tileII), clip));
- }
- }
-
- // Second, run the cpu pre-processing in threads
- SkTaskGroup().batch(tileData.count(), [&](int i) {
- tileData[i].preprocess(compressedPictureData.get(), helper);
- });
+ // This drops the promiseImageHelper's refs on all the promise images
+ promiseImageHelper.reset();
- // This drops the helper's refs on all the promise images
- helper.reset();
-
- // Third, synchronously render the display lists into the dest tiles
+ // Fourth, 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 and composing to the final surface
- for (int i = 0; i < tileData.count(); ++i) {
- tileData[i].draw();
- }
+ tiles.drawAllTilesAndFlush(context, false);
// 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);
- }
+ tiles.composeAllTiles(canvas);
context->flush();
return "";
diff --git a/dm/DMSrcSink.h b/dm/DMSrcSink.h
index cffca6e760..982040469c 100644
--- a/dm/DMSrcSink.h
+++ b/dm/DMSrcSink.h
@@ -529,9 +529,6 @@ public:
Error draw(const Src&, SkBitmap*, SkWStream*, SkString*) const override;
private:
#if SK_SUPPORT_GPU
- class PromiseImageHelper;
- class TileData;
-
const int fNumDivisions;
#endif
};