diff --git a/gn/core.gni b/gn/core.gni index f8d7fdf4f9..e006cbac96 100644 --- a/gn/core.gni +++ b/gn/core.gni @@ -91,6 +91,7 @@ skia_core_sources = [ "$_src/core/SkData.cpp", "$_src/core/SkDataTable.cpp", "$_src/core/SkDebug.cpp", + "$_src/core/SkDeferredDisplayListPriv.h", "$_src/core/SkDeferredDisplayList.cpp", "$_src/core/SkDeferredDisplayListRecorder.cpp", "$_src/core/SkDeque.cpp", diff --git a/include/core/SkDeferredDisplayListRecorder.h b/include/core/SkDeferredDisplayListRecorder.h index 0acf598f96..fcd63784fd 100644 --- a/include/core/SkDeferredDisplayListRecorder.h +++ b/include/core/SkDeferredDisplayListRecorder.h @@ -22,6 +22,7 @@ class GrContext; class SkCanvas; class SkImage; class SkSurface; +struct SkYUVAIndex; /* * This class is intended to be used as: @@ -76,7 +77,7 @@ public: In other words we will never call textureFulfillProc or textureReleaseProc multiple times for the same textureContext before calling the other. - We we call the promiseDoneProc when we will no longer call the textureFulfillProc again. We + We call the promiseDoneProc when we will no longer call the textureFulfillProc again. We pass in the textureContext as a parameter to the promiseDoneProc. We also guarantee that there will be no outstanding textureReleaseProcs that still need to be called when we call the textureDoneProc. Thus when the textureDoneProc gets called the client is able to cleanup @@ -115,6 +116,24 @@ public: PromiseDoneProc promiseDoneProc, TextureContext textureContext); + /** + This entry point operates the same as 'makePromiseTexture' except that its + textureFulfillProc can be called up to four times to fetch the required YUVA + planes (passing a different textureContext to each call). So, if the 'yuvaIndices' + indicate that only the first two backend textures are used, 'textureFulfillProc' will + be called with the first two 'textureContexts'. + */ + sk_sp makeYUVAPromiseTexture(SkYUVColorSpace yuvColorSpace, + const GrBackendFormat yuvaFormats[], + const SkYUVAIndex yuvaIndices[4], + int imageWidth, + int imageHeight, + GrSurfaceOrigin imageOrigin, + sk_sp imageColorSpace, + TextureFulfillProc textureFulfillProc, + TextureReleaseProc textureReleaseProc, + PromiseDoneProc promiseDoneProc, + TextureContext textureContexts[]); private: bool init(); diff --git a/include/private/SkDeferredDisplayList.h b/include/private/SkDeferredDisplayList.h index a236c01b35..4285910e1b 100644 --- a/include/private/SkDeferredDisplayList.h +++ b/include/private/SkDeferredDisplayList.h @@ -17,8 +17,8 @@ #include #endif +class SkDeferredDisplayListPriv; class SkSurface; - /* * This class contains pre-processed gpu operations that can be replayed into * an SkSurface via draw(SkDeferredDisplayList*). @@ -51,9 +51,14 @@ public: return fCharacterization; } + // Provides access to functions that aren't part of the public API. + SkDeferredDisplayListPriv priv(); + const SkDeferredDisplayListPriv priv() const; + private: friend class GrDrawingManager; // for access to 'fOpLists' and 'fLazyProxyData' friend class SkDeferredDisplayListRecorder; // for access to 'fLazyProxyData' + friend class SkDeferredDisplayListPriv; const SkSurfaceCharacterization fCharacterization; diff --git a/src/core/SkDeferredDisplayListPriv.h b/src/core/SkDeferredDisplayListPriv.h new file mode 100644 index 0000000000..12f03190cb --- /dev/null +++ b/src/core/SkDeferredDisplayListPriv.h @@ -0,0 +1,48 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkDeferredDisplayListPriv_DEFINED +#define SkDeferredDisplayListPriv_DEFINED + +#include "SkDeferredDisplayList.h" + +/** Class that adds methods to SkDeferredDisplayList that are only intended for use internal to Skia. + This class is purely a privileged window into SkDeferredDisplayList. It should never have + additional data members or virtual methods. */ +class SkDeferredDisplayListPriv { +public: + int numOpLists() const { +#if SK_SUPPORT_GPU + return fDDL->fOpLists.count(); +#else + return 0; +#endif + } + +private: + explicit SkDeferredDisplayListPriv(SkDeferredDisplayList* ddl) : fDDL(ddl) {} + SkDeferredDisplayListPriv(const SkDeferredDisplayListPriv&); // unimpl + SkDeferredDisplayListPriv& operator=(const SkDeferredDisplayListPriv&); // unimpl + + // No taking addresses of this type. + const SkDeferredDisplayListPriv* operator&() const; + SkDeferredDisplayListPriv* operator&(); + + SkDeferredDisplayList* fDDL; + + friend class SkDeferredDisplayList; // to construct/copy this type. +}; + +inline SkDeferredDisplayListPriv SkDeferredDisplayList::priv() { + return SkDeferredDisplayListPriv(this); +} + +inline const SkDeferredDisplayListPriv SkDeferredDisplayList::priv () const { + return SkDeferredDisplayListPriv(const_cast(this)); +} + +#endif diff --git a/src/core/SkDeferredDisplayListRecorder.cpp b/src/core/SkDeferredDisplayListRecorder.cpp index 82ab3e447f..84f31c93df 100644 --- a/src/core/SkDeferredDisplayListRecorder.cpp +++ b/src/core/SkDeferredDisplayListRecorder.cpp @@ -38,6 +38,21 @@ sk_sp SkDeferredDisplayListRecorder::makePromiseTexture( return nullptr; } +sk_sp SkDeferredDisplayListRecorder::makeYUVAPromiseTexture( + SkYUVColorSpace yuvColorSpace, + const GrBackendFormat yuvaFormats[], + const SkYUVAIndex yuvaIndices[4], + int imageWidth, + int imageHeight, + GrSurfaceOrigin imageOrigin, + sk_sp imageColorSpace, + TextureFulfillProc textureFulfillProc, + TextureReleaseProc textureReleaseProc, + PromiseDoneProc promiseDoneProc, + TextureContext textureContexts[]) { + return nullptr; +} + #else #include "GrContextPriv.h" @@ -211,4 +226,34 @@ sk_sp SkDeferredDisplayListRecorder::makePromiseTexture( textureContext); } +sk_sp SkDeferredDisplayListRecorder::makeYUVAPromiseTexture( + SkYUVColorSpace yuvColorSpace, + const GrBackendFormat yuvaFormats[], + const SkYUVAIndex yuvaIndices[4], + int imageWidth, + int imageHeight, + GrSurfaceOrigin imageOrigin, + sk_sp imageColorSpace, + TextureFulfillProc textureFulfillProc, + TextureReleaseProc textureReleaseProc, + PromiseDoneProc promiseDoneProc, + TextureContext textureContexts[]) { + if (!fContext) { + return nullptr; + } + + return SkImage_Gpu::MakePromiseYUVATexture(fContext.get(), + yuvColorSpace, + yuvaFormats, + yuvaIndices, + imageWidth, + imageHeight, + imageOrigin, + std::move(imageColorSpace), + textureFulfillProc, + textureReleaseProc, + promiseDoneProc, + textureContexts); +} + #endif diff --git a/src/core/SkImagePriv.h b/src/core/SkImagePriv.h index 9172dd4c2c..1c104b531c 100644 --- a/src/core/SkImagePriv.h +++ b/src/core/SkImagePriv.h @@ -105,7 +105,7 @@ enum SkImageSourceChannel { /** Describes the alpha channel; */ kA_SkImageSourceChannel, - /** Describes the alpha channel; */ + /** Utility value */ kLastEnum_SkImageSourceChannel = kA_SkImageSourceChannel, }; diff --git a/tools/DDLPromiseImageHelper.cpp b/tools/DDLPromiseImageHelper.cpp index 0d4e0ff9eb..51225b32de 100644 --- a/tools/DDLPromiseImageHelper.cpp +++ b/tools/DDLPromiseImageHelper.cpp @@ -10,7 +10,11 @@ #include "GrContext.h" #include "GrContextPriv.h" #include "GrGpu.h" +#include "SkCachedData.h" #include "SkDeferredDisplayListRecorder.h" +#include "SkImage_Base.h" +#include "SkImagePriv.h" +#include "SkYUVSizeInfo.h" DDLPromiseImageHelper::PromiseImageCallbackContext::~PromiseImageCallbackContext() { GrGpu* gpu = fContext->contextPriv().getGpu(); @@ -26,6 +30,8 @@ const GrCaps* DDLPromiseImageHelper::PromiseImageCallbackContext::caps() const { /////////////////////////////////////////////////////////////////////////////////////////////////// +DDLPromiseImageHelper::~DDLPromiseImageHelper() {} + sk_sp DDLPromiseImageHelper::deflateSKP(const SkPicture* inputPicture) { SkSerialProcs procs; @@ -50,23 +56,45 @@ void DDLPromiseImageHelper::uploadAllToGPU(GrContext* context) { 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(), - false, GrMipMapped::kNo)); - // The GMs sometimes request too large an image - //SkAssertResult(callbackContext->backendTexture().isValid()); + if (info.isYUV()) { + for (int j = 0; j < 3; ++j) { + const SkPixmap& yuvPixmap = info.yuvPixmap(j); + + sk_sp callbackContext( + new PromiseImageCallbackContext(context)); + callbackContext->setBackendTexture(gpu->createTestingOnlyBackendTexture( + yuvPixmap.addr(), + yuvPixmap.width(), + yuvPixmap.height(), + yuvPixmap.colorType(), + false, GrMipMapped::kNo, + yuvPixmap.rowBytes())); + SkAssertResult(callbackContext->backendTexture().isValid()); + + fImageInfo[i].setCallbackContext(j, std::move(callbackContext)); + } + } else { + sk_sp callbackContext( + new PromiseImageCallbackContext(context)); + + const SkBitmap& bm = info.normalBitmap(); + + callbackContext->setBackendTexture(gpu->createTestingOnlyBackendTexture( + bm.getPixels(), + bm.width(), + bm.height(), + bm.colorType(), + false, GrMipMapped::kNo, + bm.rowBytes())); + // The GMs sometimes request too large an image + //SkAssertResult(callbackContext->backendTexture().isValid()); + + fImageInfo[i].setCallbackContext(0, std::move(callbackContext)); + } - // The fImageInfo array gets the creation ref - fImageInfo[i].fCallbackContext = std::move(callbackContext); } } @@ -99,34 +127,71 @@ sk_sp DDLPromiseImageHelper::PromiseImageCreator(const void* rawData, const DDLPromiseImageHelper::PromiseImageInfo& curImage = helper->getInfo(*indexPtr); - if (!curImage.fCallbackContext->backendTexture().isValid()) { + if (!curImage.backendTexture(0).isValid()) { + SkASSERT(!curImage.isYUV()); // 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. - SkASSERT(curImage.fBitmap.isImmutable()); - return SkImage::MakeFromBitmap(curImage.fBitmap); + SkASSERT(curImage.normalBitmap().isImmutable()); + return SkImage::MakeFromBitmap(curImage.normalBitmap()); } - SkASSERT(curImage.fIndex == *indexPtr); + SkASSERT(curImage.index() == *indexPtr); - const GrCaps* caps = curImage.fCallbackContext->caps(); - const GrBackendTexture& backendTex = curImage.fCallbackContext->backendTexture(); - GrBackendFormat backendFormat = caps->createFormatFromBackendTexture(backendTex); + const GrCaps* caps = curImage.caps(); - // 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(), - DDLPromiseImageHelper::PromiseImageFulfillProc, - DDLPromiseImageHelper::PromiseImageReleaseProc, - DDLPromiseImageHelper::PromiseImageDoneProc, - (void*) SkSafeRef(curImage.fCallbackContext.get())); + sk_sp image; + if (curImage.isYUV()) { + GrBackendFormat backendFormats[4]; + void* contexts[4] = { nullptr, nullptr, nullptr, nullptr }; + + for (int i = 0; i < 3; ++i) { + const GrBackendTexture& backendTex = curImage.backendTexture(i); + backendFormats[i] = caps->createFormatFromBackendTexture(backendTex); + + contexts[i] = curImage.refCallbackContext(i).release(); + } + + SkYUVAIndex yuvaIndices[4] = { + SkYUVAIndex{0, SkImageSourceChannel::kA_SkImageSourceChannel}, + SkYUVAIndex{1, SkImageSourceChannel::kA_SkImageSourceChannel}, + SkYUVAIndex{2, SkImageSourceChannel::kA_SkImageSourceChannel}, + SkYUVAIndex{-1, SkImageSourceChannel::kA_SkImageSourceChannel} + }; + + int tempWidth = curImage.backendTexture(0).width(); + int tempHeight = curImage.backendTexture(0).height(); + + image = recorder->makeYUVAPromiseTexture(curImage.yuvColorSpace(), + backendFormats, + yuvaIndices, + tempWidth, //curImage.overallWidth(), + tempHeight, //curImage.overallHeight(), + GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin, + curImage.refOverallColorSpace(), + DDLPromiseImageHelper::PromiseImageFulfillProc, + DDLPromiseImageHelper::PromiseImageReleaseProc, + DDLPromiseImageHelper::PromiseImageDoneProc, + contexts); + + } else { + const GrBackendTexture& backendTex = curImage.backendTexture(0); + GrBackendFormat backendFormat = caps->createFormatFromBackendTexture(backendTex); + + // Each DDL recorder gets its own ref on the promise callback context for the + // promise images it creates. + // DDL TODO: sort out mipmapping + image = recorder->makePromiseTexture(backendFormat, + curImage.overallWidth(), + curImage.overallHeight(), + GrMipMapped::kNo, + GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin, + curImage.overallColorType(), + curImage.overallAlphaType(), + curImage.refOverallColorSpace(), + DDLPromiseImageHelper::PromiseImageFulfillProc, + DDLPromiseImageHelper::PromiseImageReleaseProc, + DDLPromiseImageHelper::PromiseImageDoneProc, + (void*) curImage.refCallbackContext(0).release()); + } perRecorderContext->fPromiseImages->push_back(image); SkASSERT(image); return image; @@ -134,9 +199,9 @@ sk_sp DDLPromiseImageHelper::PromiseImageCreator(const void* rawData, int DDLPromiseImageHelper::findImage(SkImage* image) const { for (int i = 0; i < fImageInfo.count(); ++i) { - if (fImageInfo[i].fOriginalUniqueID == image->uniqueID()) { // trying to dedup here - SkASSERT(fImageInfo[i].fIndex == i); - SkASSERT(this->isValidID(i) && this->isValidID(fImageInfo[i].fIndex)); + if (fImageInfo[i].originalUniqueID() == image->uniqueID()) { // trying to dedup here + SkASSERT(fImageInfo[i].index() == i); + SkASSERT(this->isValidID(i) && this->isValidID(fImageInfo[i].index())); return i; } } @@ -144,26 +209,42 @@ int DDLPromiseImageHelper::findImage(SkImage* image) const { } int DDLPromiseImageHelper::addImage(SkImage* image) { - sk_sp rasterImage = image->makeRasterImage(); // force decoding of lazy images + SkImage_Base* ib = as_IB(image); - SkImageInfo ii = SkImageInfo::Make(rasterImage->width(), rasterImage->height(), - rasterImage->colorType(), rasterImage->alphaType(), - rasterImage->refColorSpace()); + SkImageInfo overallII = SkImageInfo::Make(image->width(), image->height(), + image->colorType(), image->alphaType(), + image->refColorSpace()); - SkBitmap bm; - bm.allocPixels(ii); + PromiseImageInfo& newImageInfo = fImageInfo.emplace_back(fImageInfo.count(), + image->uniqueID(), + overallII); - if (!rasterImage->readPixels(bm.pixmap(), 0, 0)) { - return -1; + SkYUVSizeInfo yuvSizeInfo; + SkYUVColorSpace yuvColorSpace; + const void* planes[3]; + sk_sp yuvData = ib->getPlanes(&yuvSizeInfo, &yuvColorSpace, planes); + if (yuvData) { + newImageInfo.setYUVData(std::move(yuvData), yuvColorSpace); + + for (int i = 0; i < 3; ++i) { + SkImageInfo planeII = SkImageInfo::MakeA8(yuvSizeInfo.fSizes[i].fWidth, + yuvSizeInfo.fSizes[i].fHeight); + newImageInfo.addYUVPlane(i, planeII, planes[i], yuvSizeInfo.fWidthBytes[i]); + } + } else { + sk_sp rasterImage = image->makeRasterImage(); // force decoding of lazy images + + SkBitmap tmp; + tmp.allocPixels(overallII); + + if (!rasterImage->readPixels(tmp.pixmap(), 0, 0)) { + return -1; + } + + tmp.setImmutable(); + newImageInfo.setNormalBitmap(tmp); } - - bm.setImmutable(); - - PromiseImageInfo& newImageInfo = fImageInfo.push_back(); - newImageInfo.fIndex = fImageInfo.count()-1; - newImageInfo.fOriginalUniqueID = image->uniqueID(); - newImageInfo.fBitmap = bm; - /* fCallbackContext is filled in by uploadAllToGPU */ + // In either case newImageInfo's PromiseImageCallbackContext is filled in by uploadAllToGPU return fImageInfo.count()-1; } diff --git a/tools/DDLPromiseImageHelper.h b/tools/DDLPromiseImageHelper.h index e9520b2d48..4c6c1638d2 100644 --- a/tools/DDLPromiseImageHelper.h +++ b/tools/DDLPromiseImageHelper.h @@ -12,6 +12,8 @@ #include "SkTArray.h" #include "GrBackendSurface.h" +#include "SkCachedData.h" +#include "SkYUVSizeInfo.h" class GrContext; class SkDeferredDisplayListRecorder; @@ -35,7 +37,7 @@ class SkPicture; // 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 +// are done, the PromiseImageCallbackContext is freed and its GrBackendTexture removed // from VRAM // // Note: if DDLs are going to be replayed multiple times, the reset call can be delayed until @@ -43,6 +45,7 @@ class SkPicture; class DDLPromiseImageHelper { public: DDLPromiseImageHelper() { } + ~DDLPromiseImageHelper(); // Convert the SkPicture into SkData replacing all the SkImages with an index. sk_sp deflateSKP(const SkPicture* inputPicture); @@ -58,10 +61,10 @@ public: void reset() { fImageInfo.reset(); } private: - // This class acts as a proxy for the single GrBackendTexture representing an image. + // This class acts as a proxy for a GrBackendTexture that is part of 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. + // potentially several of these objects. 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"). @@ -72,6 +75,7 @@ private: ~PromiseImageCallbackContext(); void setBackendTexture(const GrBackendTexture& backendTexture) { + SkASSERT(!fBackendTexture.isValid()); fBackendTexture = backendTexture; } @@ -91,10 +95,85 @@ private: // 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; + PromiseImageInfo(int index, uint32_t originalUniqueID, const SkImageInfo& ii) + : fIndex(index) + , fOriginalUniqueID(originalUniqueID) + , fImageInfo(ii) { + } + ~PromiseImageInfo() {} + + int index() const { return fIndex; } + uint32_t originalUniqueID() const { return fOriginalUniqueID; } + bool isYUV() const { return SkToBool(fYUVData.get()); } + + int overallWidth() const { return fImageInfo.width(); } + int overallHeight() const { return fImageInfo.height(); } + SkColorType overallColorType() const { return fImageInfo.colorType(); } + SkAlphaType overallAlphaType() const { return fImageInfo.alphaType(); } + sk_sp refOverallColorSpace() const { return fImageInfo.refColorSpace(); } + + SkYUVColorSpace yuvColorSpace() const { + SkASSERT(this->isYUV()); + return fYUVColorSpace; + } + const SkPixmap& yuvPixmap(int index) const { + SkASSERT(this->isYUV()); + SkASSERT(index >= 0 && index < 3); + return fYUVPlanes[index]; + } + const SkBitmap& normalBitmap() const { + SkASSERT(!this->isYUV()); + return fBitmap; + } + + void setCallbackContext(int index, sk_sp callbackContext) { + SkASSERT(index >= 0 && index < (this->isYUV() ? 3 : 1)); + fCallbackContexts[index] = callbackContext; + } + PromiseImageCallbackContext* callbackContext(int index) { + SkASSERT(index >= 0 && index < (this->isYUV() ? 3 : 1)); + return fCallbackContexts[index].get(); + } + sk_sp refCallbackContext(int index) const { + SkASSERT(index >= 0 && index < (this->isYUV() ? 3 : 1)); + return fCallbackContexts[index]; + } + + const GrCaps* caps() const { return fCallbackContexts[0]->caps(); } + + const GrBackendTexture& backendTexture(int index) const { + SkASSERT(index >= 0 && index < (this->isYUV() ? 3 : 1)); + return fCallbackContexts[index]->backendTexture(); + } + + void setNormalBitmap(const SkBitmap& bm) { fBitmap = bm; } + + void setYUVData(sk_sp yuvData, SkYUVColorSpace cs) { + fYUVData = yuvData; + fYUVColorSpace = cs; + } + void addYUVPlane(int index, const SkImageInfo& ii, const void* plane, size_t widthBytes) { + SkASSERT(this->isYUV()); + SkASSERT(index >= 0 && index < 3); + fYUVPlanes[index].reset(ii, plane, widthBytes); + } + + private: + const int fIndex; // index in the 'fImageInfo' array + const uint32_t fOriginalUniqueID; // original ID for deduping + + const SkImageInfo fImageInfo; // info for the overarching image + + // CPU-side cache of a normal SkImage's contents + SkBitmap fBitmap; + + // CPU-side cache of a YUV SkImage's contents + sk_sp fYUVData; // when !null, this is a YUV image + SkYUVColorSpace fYUVColorSpace = kJPEG_SkYUVColorSpace; + SkPixmap fYUVPlanes[3]; + + // Up to 3 for a YUV image. Only one for a normal image. + sk_sp fCallbackContexts[3]; }; // This stack-based context allows each thread to re-inflate the image indices into diff --git a/tools/DDLTileHelper.cpp b/tools/DDLTileHelper.cpp index 2403b43fc3..b651c34221 100644 --- a/tools/DDLTileHelper.cpp +++ b/tools/DDLTileHelper.cpp @@ -9,6 +9,7 @@ #include "DDLPromiseImageHelper.h" #include "SkCanvas.h" +#include "SkDeferredDisplayListPriv.h" #include "SkDeferredDisplayListRecorder.h" #include "SkImage_Gpu.h" #include "SkPicture.h" @@ -31,10 +32,17 @@ void DDLTileHelper::TileData::createTileSpecificSKP(SkData* compressedPictureDat SkDeferredDisplayListRecorder recorder(fCharacterization); fReconstitutedPicture = helper.reinflateSKP(&recorder, compressedPictureData, &fPromiseImages); + + std::unique_ptr ddl = recorder.detach(); + if (ddl.get()->priv().numOpLists()) { + // TODO: remove this once skbug.com/8424 is fixed. If the DDL resulting from the + // reinflation of the SKPs contains opLists that means some image subset operation + // created a draw. + fReconstitutedPicture.reset(); + } } void DDLTileHelper::TileData::createDDL() { - SkASSERT(fReconstitutedPicture); SkASSERT(!fDisplayList); SkDeferredDisplayListRecorder recorder(fCharacterization); @@ -60,7 +68,9 @@ void DDLTileHelper::TileData::createDDL() { // 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(fReconstitutedPicture); + if (fReconstitutedPicture) { + subCanvas->drawPicture(fReconstitutedPicture); + } fDisplayList = recorder.detach(); }