| /* | 
 |  * 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 PromiseImageHelper_DEFINED | 
 | #define PromiseImageHelper_DEFINED | 
 |  | 
 | #include "include/core/SkBitmap.h" | 
 | #include "include/core/SkDeferredDisplayListRecorder.h" | 
 | #include "include/core/SkPromiseImageTexture.h" | 
 | #include "include/core/SkYUVAIndex.h" | 
 | #include "include/core/SkYUVAPixmaps.h" | 
 | #include "include/core/SkYUVASizeInfo.h" | 
 | #include "include/gpu/GrBackendSurface.h" | 
 | #include "include/private/SkTArray.h" | 
 | #include "src/core/SkCachedData.h" | 
 | #include "src/core/SkTLazy.h" | 
 |  | 
 | class GrContext; | 
 | class SkImage; | 
 | class SkMipmap; | 
 | class SkPicture; | 
 | class SkTaskGroup; | 
 | struct SkYUVAIndex; | 
 |  | 
 | // This class acts as a proxy for a GrBackendTexture that backs an image. | 
 | // Whenever a promise image is created for the image, the promise image receives a ref to | 
 | // 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"). | 
 | class PromiseImageCallbackContext : public SkRefCnt { | 
 | public: | 
 |     PromiseImageCallbackContext(GrDirectContext* direct, GrBackendFormat backendFormat) | 
 |             : fContext(direct) | 
 |             , fBackendFormat(backendFormat) {} | 
 |  | 
 |     ~PromiseImageCallbackContext() override; | 
 |  | 
 |     const GrBackendFormat& backendFormat() const { return fBackendFormat; } | 
 |  | 
 |     void setBackendTexture(const GrBackendTexture& backendTexture); | 
 |  | 
 |     void destroyBackendTexture(); | 
 |  | 
 |     sk_sp<SkPromiseImageTexture> fulfill() { | 
 |         SkASSERT(fUnreleasedFulfills >= 0); | 
 |         ++fUnreleasedFulfills; | 
 |         ++fTotalFulfills; | 
 |         return fPromiseImageTexture; | 
 |     } | 
 |  | 
 |     void release() { | 
 |         SkASSERT(fUnreleasedFulfills > 0); | 
 |         --fUnreleasedFulfills; | 
 |         ++fTotalReleases; | 
 |     } | 
 |  | 
 |     void done() { | 
 |         ++fDoneCnt; | 
 |         SkASSERT(fDoneCnt <= fNumImages); | 
 |     } | 
 |  | 
 |     void wasAddedToImage() { fNumImages++; } | 
 |  | 
 |     const SkPromiseImageTexture* promiseImageTexture() const { | 
 |         return fPromiseImageTexture.get(); | 
 |     } | 
 |  | 
 |     static sk_sp<SkPromiseImageTexture> PromiseImageFulfillProc(void* textureContext) { | 
 |         auto callbackContext = static_cast<PromiseImageCallbackContext*>(textureContext); | 
 |         return callbackContext->fulfill(); | 
 |     } | 
 |  | 
 |     static void PromiseImageReleaseProc(void* textureContext) { | 
 |         auto callbackContext = static_cast<PromiseImageCallbackContext*>(textureContext); | 
 |         callbackContext->release(); | 
 |     } | 
 |  | 
 |     static void PromiseImageDoneProc(void* textureContext) { | 
 |         auto callbackContext = static_cast<PromiseImageCallbackContext*>(textureContext); | 
 |         callbackContext->done(); | 
 |         callbackContext->unref(); | 
 |     } | 
 |  | 
 | private: | 
 |     GrDirectContext*             fContext; | 
 |     GrBackendFormat              fBackendFormat; | 
 |     sk_sp<SkPromiseImageTexture> fPromiseImageTexture; | 
 |     int                          fNumImages = 0; | 
 |     int                          fTotalFulfills = 0; | 
 |     int                          fTotalReleases = 0; | 
 |     int                          fUnreleasedFulfills = 0; | 
 |     int                          fDoneCnt = 0; | 
 |  | 
 |     using INHERITED = SkRefCnt; | 
 | }; | 
 |  | 
 | // This class consolidates tracking & extraction of the original image data from an skp, | 
 | // 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 (via deflateSKP) | 
 | // | 
 | //    Prior to replaying in threads, all the images stored in this class are uploaded to the | 
 | //       gpu and PromiseImageCallbackContexts are created for them (via uploadAllToGPU) | 
 | // | 
 | //    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) (via reinflateSKP). | 
 | // | 
 | //    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 | 
 | // | 
 | // Note: if DDLs are going to be replayed multiple times, the reset call can be delayed until | 
 | // all the replaying is complete. This will pin the GrBackendTextures in VRAM. | 
 | class DDLPromiseImageHelper { | 
 | public: | 
 |     DDLPromiseImageHelper(const SkYUVAPixmapInfo::SupportedDataTypes& supportedYUVADataTypes) | 
 |             : fSupportedYUVADataTypes(supportedYUVADataTypes) {} | 
 |     ~DDLPromiseImageHelper() = default; | 
 |  | 
 |     // Convert the SkPicture into SkData replacing all the SkImages with an index. | 
 |     sk_sp<SkData> deflateSKP(const SkPicture* inputPicture); | 
 |  | 
 |     void createCallbackContexts(GrDirectContext*); | 
 |  | 
 |     void uploadAllToGPU(SkTaskGroup*, GrDirectContext*); | 
 |     void deleteAllFromGPU(SkTaskGroup*, GrDirectContext*); | 
 |  | 
 |     // reinflate a deflated SKP, replacing all the indices with promise images. | 
 |     sk_sp<SkPicture> reinflateSKP(SkDeferredDisplayListRecorder*, | 
 |                                   SkData* compressedPicture, | 
 |                                   SkTArray<sk_sp<SkImage>>* promiseImages) const; | 
 |  | 
 |     // Remove this class' refs on the PromiseImageCallbackContexts | 
 |     void reset() { fImageInfo.reset(); } | 
 |  | 
 | private: | 
 |     // 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: | 
 |         PromiseImageInfo(int index, uint32_t originalUniqueID, const SkImageInfo& ii); | 
 |         PromiseImageInfo(PromiseImageInfo&& other); | 
 |         ~PromiseImageInfo(); | 
 |  | 
 |         int index() const { return fIndex; } | 
 |         uint32_t originalUniqueID() const { return fOriginalUniqueID; } | 
 |         bool isYUV() const { return fYUVAPixmaps.isValid(); } | 
 |  | 
 |         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<SkColorSpace> refOverallColorSpace() const { return fImageInfo.refColorSpace(); } | 
 |  | 
 |         int numYUVAPlanes() const { | 
 |             SkASSERT(this->isYUV()); | 
 |             return fYUVAPixmaps.yuvaInfo().numPlanes(); | 
 |         } | 
 |         SkYUVColorSpace yuvColorSpace() const { | 
 |             SkASSERT(this->isYUV()); | 
 |             return fYUVAPixmaps.yuvaInfo().yuvColorSpace(); | 
 |         } | 
 |         const SkYUVAIndex* yuvaIndices() const { | 
 |             SkASSERT(this->isYUV()); | 
 |             SkASSERT(fYUVAIndices[SkYUVAIndex::kY_Index].fIndex >= 0); | 
 |             return fYUVAIndices; | 
 |         } | 
 |         const SkPixmap& yuvPixmap(int index) const { | 
 |             SkASSERT(this->isYUV()); | 
 |             return fYUVAPixmaps.planes()[index]; | 
 |         } | 
 |  | 
 |         const SkBitmap& baseLevel() const { | 
 |             SkASSERT(!this->isYUV()); | 
 |             return fBaseLevel; | 
 |         } | 
 |         // This returns an array of all the available mipLevels - suitable for passing into | 
 |         // createBackendTexture. | 
 |         std::unique_ptr<SkPixmap[]> normalMipLevels() const; | 
 |         int numMipLevels() const; | 
 |  | 
 |         void setCallbackContext(int index, sk_sp<PromiseImageCallbackContext> callbackContext) { | 
 |             SkASSERT(index >= 0 && index < (this->isYUV() ? SkYUVASizeInfo::kMaxCount : 1)); | 
 |             fCallbackContexts[index] = callbackContext; | 
 |         } | 
 |         PromiseImageCallbackContext* callbackContext(int index) const { | 
 |             SkASSERT(index >= 0 && index < (this->isYUV() ? SkYUVASizeInfo::kMaxCount : 1)); | 
 |             return fCallbackContexts[index].get(); | 
 |         } | 
 |         sk_sp<PromiseImageCallbackContext> refCallbackContext(int index) const { | 
 |             SkASSERT(index >= 0 && index < (this->isYUV() ? SkYUVASizeInfo::kMaxCount : 1)); | 
 |             return fCallbackContexts[index]; | 
 |         } | 
 |  | 
 |         GrMipmapped mipMapped(int index) const { | 
 |             if (this->isYUV()) { | 
 |                 return GrMipmapped::kNo; | 
 |             } | 
 |             return fMipLevels ? GrMipmapped::kYes : GrMipmapped::kNo; | 
 |         } | 
 |         const GrBackendFormat& backendFormat(int index) const { | 
 |             SkASSERT(index >= 0 && index < (this->isYUV() ? SkYUVASizeInfo::kMaxCount : 1)); | 
 |             return fCallbackContexts[index]->backendFormat(); | 
 |         } | 
 |         const SkPromiseImageTexture* promiseTexture(int index) const { | 
 |             SkASSERT(index >= 0 && index < (this->isYUV() ? SkYUVASizeInfo::kMaxCount : 1)); | 
 |             return fCallbackContexts[index]->promiseImageTexture(); | 
 |         } | 
 |  | 
 |         void setMipLevels(const SkBitmap& baseLevel, std::unique_ptr<SkMipmap> mipLevels); | 
 |  | 
 |         /** Takes ownership of the plane data. */ | 
 |         void setYUVPlanes(SkYUVAPixmaps yuvaPixmaps) { fYUVAPixmaps = std::move(yuvaPixmaps); } | 
 |  | 
 |         /** Call after setYUVPlanes() and callback contexts have been installed.  */ | 
 |         void initYUVAIndices(); | 
 |  | 
 |     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 mipmap levels | 
 |         SkBitmap                           fBaseLevel; | 
 |         std::unique_ptr<SkMipmap>          fMipLevels; | 
 |  | 
 |         // CPU-side cache of a YUV SkImage's contents | 
 |         SkYUVAPixmaps                      fYUVAPixmaps; | 
 |         SkYUVAIndex                        fYUVAIndices[SkYUVAIndex::kIndexCount] = {}; | 
 |  | 
 |         // Up to SkYUVASizeInfo::kMaxCount for a YUVA image. Only one for a normal image. | 
 |         sk_sp<PromiseImageCallbackContext> fCallbackContexts[SkYUVASizeInfo::kMaxCount]; | 
 |     }; | 
 |  | 
 |     // 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 DDLPromiseImageHelper*   fHelper; | 
 |         SkTArray<sk_sp<SkImage>>*      fPromiseImages; | 
 |     }; | 
 |  | 
 |     static void CreateBETexturesForPromiseImage(GrDirectContext*, PromiseImageInfo*); | 
 |     static void DeleteBETexturesForPromiseImage(GrDirectContext*, PromiseImageInfo*); | 
 |  | 
 |     static sk_sp<SkImage> CreatePromiseImages(const void* rawData, size_t length, void* ctxIn); | 
 |  | 
 |     bool isValidID(int id) const { return id >= 0 && id < fImageInfo.count(); } | 
 |     const PromiseImageInfo& getInfo(int id) const { return fImageInfo[id]; } | 
 |     void uploadImage(GrDirectContext*, PromiseImageInfo*); | 
 |  | 
 |     // returns -1 if not found | 
 |     int findImage(SkImage* image) const; | 
 |  | 
 |     // returns -1 on failure | 
 |     int addImage(SkImage* image); | 
 |  | 
 |     // returns -1 on failure | 
 |     int findOrDefineImage(SkImage* image); | 
 |  | 
 |     SkYUVAPixmapInfo::SupportedDataTypes fSupportedYUVADataTypes; | 
 |     SkTArray<PromiseImageInfo> fImageInfo; | 
 | }; | 
 |  | 
 | #endif |