From 96601084b3f7b108c1faf12a2ea12eb7ea8688a0 Mon Sep 17 00:00:00 2001 From: Robert Phillips Date: Tue, 29 May 2018 16:13:26 -0400 Subject: 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 Reviewed-by: Greg Daniel Commit-Queue: Robert Phillips --- dm/DMSrcSink.cpp | 373 +++---------------------------------------------------- dm/DMSrcSink.h | 3 - 2 files changed, 18 insertions(+), 358 deletions(-) (limited to 'dm') 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 #include #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 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 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(textureContext); - SkASSERT(callbackContext->backendTexture().isValid()); - *outTexture = callbackContext->backendTexture(); - } - - static void PromiseImageReleaseProc(void* textureContext) { -#ifdef SK_DEBUG - auto callbackContext = static_cast(textureContext); - SkASSERT(callbackContext->backendTexture().isValid()); -#endif - } - - static void PromiseImageDoneProc(void* textureContext) { - auto callbackContext = static_cast(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 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 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 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 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 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 PromiseImageCreator(const void* rawData, size_t length, void* ctxIn) { - PerRecorderContext* perRecorderContext = static_cast(ctxIn); - const PromiseImageHelper* helper = perRecorderContext->fHelper; - SkDeferredDisplayListRecorder* recorder = perRecorderContext->fRecorder; - - SkASSERT(length == sizeof(int)); - - const int* indexPtr = static_cast(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 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 fSurface; - SkIRect fClip; // in the device space of the dest canvas - std::unique_ptr 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 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 { - auto helper = static_cast(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 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.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 }; -- cgit v1.2.3