From a479f96dafa94549ed3b5b72d1d25bacd9d18b16 Mon Sep 17 00:00:00 2001 From: Robert Phillips Date: Tue, 10 Apr 2018 11:45:40 -0400 Subject: Get ViaDDL working with Vulkan Change-Id: Iab5ab689529227d2c8a6dbea89e555b73622a99c Reviewed-on: https://skia-review.googlesource.com/118989 Reviewed-by: Greg Daniel Commit-Queue: Robert Phillips --- dm/DMSrcSink.cpp | 188 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 115 insertions(+), 73 deletions(-) (limited to 'dm') diff --git a/dm/DMSrcSink.cpp b/dm/DMSrcSink.cpp index 4ddebb7e4c..f0b329c5b9 100644 --- a/dm/DMSrcSink.cpp +++ b/dm/DMSrcSink.cpp @@ -2007,29 +2007,75 @@ ViaDDL::ViaDDL(int numDivisions, Sink* sink) // 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 - GrBackendTexture fBackendTexture; // GPU-side version + 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() : fLocked(false) { } + PromiseImageHelper() { } - // This class will hand out pointers to its PromiseImageInfo. This is just some insurance - // we won't be moving them around. - void lock() { fLocked = true; } + void reset() { fImageInfo.reset(); } bool isValidID(int id) const { return id >= 0 && id < fImageInfo.count(); } - const PromiseImageInfo* getInfo(int id) const { - SkASSERT(fLocked); - return &fImageInfo[id]; + const PromiseImageInfo& getInfo(int id) const { + return fImageInfo[id]; } // returns -1 on failure @@ -2050,39 +2096,40 @@ public: SkASSERT(gpu); for (int i = 0; i < fImageInfo.count(); ++i) { + sk_sp callbackContext( + new PromiseImageCallbackContext(context)); + // DDL TODO: how can we tell if we need mipmapping! - fImageInfo[i].fBackendTexture = gpu->createTestingOnlyBackendTexture( + callbackContext->setBackendTexture(gpu->createTestingOnlyBackendTexture( fImageInfo[i].fBitmap.getPixels(), fImageInfo[i].fBitmap.width(), fImageInfo[i].fBitmap.height(), fImageInfo[i].fBitmap.colorType(), - false, GrMipMapped::kNo); - SkAssertResult(fImageInfo[i].fBackendTexture.isValid()); - } - } + false, GrMipMapped::kNo)); + // The GMs sometimes request too large an image + //SkAssertResult(callbackContext->backendTexture().isValid()); - void cleanUpVRAM(GrContext* context) { - GrGpu* gpu = context->contextPriv().getGpu(); - SkASSERT(gpu); - - for (int i = 0; i < fImageInfo.count(); ++i) { - gpu->deleteTestingOnlyBackendTexture(fImageInfo[i].fBackendTexture); + // The fImageInfo array gets the creation ref + fImageInfo[i].fCallbackContext = std::move(callbackContext); } } static void PromiseImageFulfillProc(void* textureContext, GrBackendTexture* outTexture) { - auto imgInfo = static_cast(textureContext); - - SkASSERT(imgInfo->fBackendTexture.isValid()); - *outTexture = imgInfo->fBackendTexture; + auto callbackContext = static_cast(textureContext); + SkASSERT(callbackContext->backendTexture().isValid()); + *outTexture = callbackContext->backendTexture(); } static void PromiseImageReleaseProc(void* textureContext) { - // Do nothing. We free all the backend textures at the end in cleanUpVRAM. +#ifdef SK_DEBUG + auto callbackContext = static_cast(textureContext); + SkASSERT(callbackContext->backendTexture().isValid()); +#endif } static void PromiseImageDoneProc(void* textureContext) { - // Do nothing. + auto callbackContext = static_cast(textureContext); + callbackContext->unref(); } private: @@ -2100,8 +2147,6 @@ private: // returns -1 on failure int addImage(SkImage* image) { - SkASSERT(!fLocked); - sk_sp rasterImage = image->makeRasterImage(); // force decoding of lazy images SkImageInfo ii = SkImageInfo::Make(rasterImage->width(), rasterImage->height(), @@ -2121,7 +2166,7 @@ private: newImageInfo.fIndex = fImageInfo.count(); newImageInfo.fOriginalUniqueID = image->uniqueID(); newImageInfo.fBitmap = bm; - /* fBackendTexture is filled in by uploadAllToGPU */ + /* fCallbackContext is filled in by uploadAllToGPU */ fImageInfo.push_back(newImageInfo); SkASSERT(newImageInfo.fIndex == fImageInfo.count()-1); @@ -2129,7 +2174,6 @@ private: } SkTArray fImageInfo; - bool fLocked; // are additions still allowed }; // TileData class encapsulates the information and behavior for a single tile/thread in @@ -2147,6 +2191,7 @@ public: // 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. @@ -2156,10 +2201,10 @@ public: sk_sp reconstitutedPicture; { - PromiseImageCallbackContext callbackCtx = { &helper, &recorder }; + PerRecorderContext perRecorderContext { &recorder, &helper }; SkDeserialProcs procs; - procs.fImageCtx = &callbackCtx; + procs.fImageCtx = (void*) &perRecorderContext; procs.fImageProc = PromiseImageCreator; reconstitutedPicture = SkPicture::MakeFromData(compressedPictureData, &procs); @@ -2194,49 +2239,52 @@ public: } private: - // This class lets us pass the collected image information and the DDLRecorder to the - // promise_image_creator callback when reconstituting a deflated SKP for a particular tile - // (i.e., in a thread). - class PromiseImageCallbackContext { - public: - const PromiseImageHelper* fHelper; - SkDeferredDisplayListRecorder* fRecorder; + // 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 of with multiple - // promise image referring to the same GrBackendTexture. - // DDL TODO: Having multiple promise images using the same GrBackendTexture won't work in - // Vulkan! Move creation of the promise images to the main thread & SkImage. + // 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) { - PromiseImageCallbackContext* ctx = static_cast(ctxIn); - const PromiseImageHelper* helper = ctx->fHelper; - SkDeferredDisplayListRecorder* recorder = ctx->fRecorder; + 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); - SkASSERT(curImage->fIndex == *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 + return nullptr; + } + SkASSERT(curImage.fIndex == *indexPtr); - GrBackendFormat backendFormat = curImage->fBackendTexture.format(); + 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*) curImage); + 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; } @@ -2286,8 +2334,6 @@ Error ViaDDL::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString } } - helper.lock(); // after this point no more images should be added to the helper - return draw_to_canvas(fSink.get(), bitmap, stream, log, size, [&](SkCanvas* canvas) -> Error { GrContext* context = canvas->getGrContext(); @@ -2295,6 +2341,7 @@ Error ViaDDL::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString return SkStringPrintf("DDLs are GPU only"); } + // This is here bc this is the first point where we have access to the context helper.uploadAllToGPU(context); int xTileSize = viewport.width()/fNumDivisions; @@ -2325,6 +2372,9 @@ Error ViaDDL::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString tileData[i].preprocess(compressedPictureData.get(), helper); }); + // This drops the helper's refs on all the promise images + helper.reset(); + // 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 and composing to the final surface @@ -2339,15 +2389,7 @@ Error ViaDDL::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString tileData[i].compose(canvas); } - // All promise images need to be fulfilled before leaving this method since we - // are about to delete their backing GrBackendTextures - // DDL TODO: remove the cleanUpVRAM method and use the release & done - // callbacks. - GrGpu* gpu = context->contextPriv().getGpu(); context->flush(); - gpu->testingOnly_flushGpuAndSync(); - - helper.cleanUpVRAM(context); return ""; }); } -- cgit v1.2.3