1 /*
2  * Copyright 2018 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #ifndef PromiseImageHelper_DEFINED
9 #define PromiseImageHelper_DEFINED
10 
11 #include "include/core/SkBitmap.h"
12 #include "include/core/SkDeferredDisplayListRecorder.h"
13 #include "include/core/SkPromiseImageTexture.h"
14 #include "include/core/SkYUVAIndex.h"
15 #include "include/core/SkYUVAPixmaps.h"
16 #include "include/core/SkYUVASizeInfo.h"
17 #include "include/gpu/GrBackendSurface.h"
18 #include "include/private/SkTArray.h"
19 #include "src/core/SkCachedData.h"
20 #include "src/core/SkTLazy.h"
21 
22 class GrDirectContext;
23 class SkImage;
24 class SkMipmap;
25 class SkPicture;
26 class SkTaskGroup;
27 struct SkYUVAIndex;
28 
29 // This class acts as a proxy for a GrBackendTexture that backs an image.
30 // Whenever a promise image is created for the image, the promise image receives a ref to
31 // potentially several of these objects. Once all the promise images receive their done
32 // callbacks this object is deleted - removing the GrBackendTexture from VRAM.
33 // Note that while the DDLs are being created in the threads, the PromiseImageHelper holds
34 // a ref on all the PromiseImageCallbackContexts. However, once all the threads are done
35 // it drops all of its refs (via "reset").
36 class PromiseImageCallbackContext : public SkRefCnt {
37 public:
PromiseImageCallbackContext(GrDirectContext * direct,GrBackendFormat backendFormat)38     PromiseImageCallbackContext(GrDirectContext* direct, GrBackendFormat backendFormat)
39             : fContext(direct)
40             , fBackendFormat(backendFormat) {}
41 
42     ~PromiseImageCallbackContext() override;
43 
backendFormat()44     const GrBackendFormat& backendFormat() const { return fBackendFormat; }
45 
46     void setBackendTexture(const GrBackendTexture& backendTexture);
47 
48     void destroyBackendTexture();
49 
fulfill()50     sk_sp<SkPromiseImageTexture> fulfill() {
51         ++fTotalFulfills;
52         return fPromiseImageTexture;
53     }
54 
release()55     void release() {
56         ++fDoneCnt;
57         SkASSERT(fDoneCnt <= fNumImages);
58     }
59 
wasAddedToImage()60     void wasAddedToImage() { fNumImages++; }
61 
promiseImageTexture()62     const SkPromiseImageTexture* promiseImageTexture() const {
63         return fPromiseImageTexture.get();
64     }
65 
PromiseImageFulfillProc(void * textureContext)66     static sk_sp<SkPromiseImageTexture> PromiseImageFulfillProc(void* textureContext) {
67         auto callbackContext = static_cast<PromiseImageCallbackContext*>(textureContext);
68         return callbackContext->fulfill();
69     }
70 
PromiseImageReleaseProc(void * textureContext)71     static void PromiseImageReleaseProc(void* textureContext) {
72         auto callbackContext = static_cast<PromiseImageCallbackContext*>(textureContext);
73         callbackContext->release();
74         callbackContext->unref();
75     }
76 
77 private:
78     GrDirectContext*             fContext;
79     GrBackendFormat              fBackendFormat;
80     sk_sp<SkPromiseImageTexture> fPromiseImageTexture;
81     int                          fNumImages = 0;
82     int                          fTotalFulfills = 0;
83     int                          fDoneCnt = 0;
84 
85     using INHERITED = SkRefCnt;
86 };
87 
88 // This class consolidates tracking & extraction of the original image data from an skp,
89 // the upload of said data to the GPU and the fulfillment of promise images.
90 //
91 // The way this works is:
92 //    the original skp is converted to SkData and all its image info is extracted into this
93 //       class and only indices into this class are left in the SkData (via deflateSKP)
94 //
95 //    Prior to replaying in threads, all the images stored in this class are uploaded to the
96 //       gpu and PromiseImageCallbackContexts are created for them (via uploadAllToGPU)
97 //
98 //    Each thread reinflates the SkData into an SkPicture replacing all the indices w/
99 //       promise images (all using the same GrBackendTexture and getting a ref to the
100 //       appropriate PromiseImageCallbackContext) (via reinflateSKP).
101 //
102 //    This class is then reset - dropping all of its refs on the PromiseImageCallbackContexts
103 //
104 //    Each done callback unrefs its PromiseImageCallbackContext so, once all the promise images
105 //       are done, the PromiseImageCallbackContext is freed and its GrBackendTexture removed
106 //       from VRAM
107 //
108 // Note: if DDLs are going to be replayed multiple times, the reset call can be delayed until
109 // all the replaying is complete. This will pin the GrBackendTextures in VRAM.
110 class DDLPromiseImageHelper {
111 public:
DDLPromiseImageHelper(const SkYUVAPixmapInfo::SupportedDataTypes & supportedYUVADataTypes)112     DDLPromiseImageHelper(const SkYUVAPixmapInfo::SupportedDataTypes& supportedYUVADataTypes)
113             : fSupportedYUVADataTypes(supportedYUVADataTypes) {}
114     ~DDLPromiseImageHelper() = default;
115 
116     // Convert the SkPicture into SkData replacing all the SkImages with an index.
117     sk_sp<SkData> deflateSKP(const SkPicture* inputPicture);
118 
119     void createCallbackContexts(GrDirectContext*);
120 
121     void uploadAllToGPU(SkTaskGroup*, GrDirectContext*);
122     void deleteAllFromGPU(SkTaskGroup*, GrDirectContext*);
123 
124     // reinflate a deflated SKP, replacing all the indices with promise images.
125     sk_sp<SkPicture> reinflateSKP(SkDeferredDisplayListRecorder*,
126                                   SkData* compressedPicture,
127                                   SkTArray<sk_sp<SkImage>>* promiseImages) const;
128 
129     // Remove this class' refs on the PromiseImageCallbackContexts
reset()130     void reset() { fImageInfo.reset(); }
131 
132 private:
133     // This is the information extracted into this class from the parsing of the skp file.
134     // Once it has all been uploaded to the GPU and distributed to the promise images, it
135     // is all dropped via "reset".
136     class PromiseImageInfo {
137     public:
138         PromiseImageInfo(int index, uint32_t originalUniqueID, const SkImageInfo& ii);
139         PromiseImageInfo(PromiseImageInfo&& other);
140         ~PromiseImageInfo();
141 
index()142         int index() const { return fIndex; }
originalUniqueID()143         uint32_t originalUniqueID() const { return fOriginalUniqueID; }
isYUV()144         bool isYUV() const { return fYUVAPixmaps.isValid(); }
145 
overallWidth()146         int overallWidth() const { return fImageInfo.width(); }
overallHeight()147         int overallHeight() const { return fImageInfo.height(); }
overallColorType()148         SkColorType overallColorType() const { return fImageInfo.colorType(); }
overallAlphaType()149         SkAlphaType overallAlphaType() const { return fImageInfo.alphaType(); }
refOverallColorSpace()150         sk_sp<SkColorSpace> refOverallColorSpace() const { return fImageInfo.refColorSpace(); }
151 
yuvaInfo()152         const SkYUVAInfo& yuvaInfo() const { return fYUVAPixmaps.yuvaInfo(); }
153 
yuvPixmap(int index)154         const SkPixmap& yuvPixmap(int index) const {
155             SkASSERT(this->isYUV());
156             return fYUVAPixmaps.planes()[index];
157         }
158 
baseLevel()159         const SkBitmap& baseLevel() const {
160             SkASSERT(!this->isYUV());
161             return fBaseLevel;
162         }
163         // This returns an array of all the available mipLevels - suitable for passing into
164         // createBackendTexture.
165         std::unique_ptr<SkPixmap[]> normalMipLevels() const;
166         int numMipLevels() const;
167 
setCallbackContext(int index,sk_sp<PromiseImageCallbackContext> callbackContext)168         void setCallbackContext(int index, sk_sp<PromiseImageCallbackContext> callbackContext) {
169             SkASSERT(index >= 0 && index < (this->isYUV() ? SkYUVASizeInfo::kMaxCount : 1));
170             fCallbackContexts[index] = callbackContext;
171         }
callbackContext(int index)172         PromiseImageCallbackContext* callbackContext(int index) const {
173             SkASSERT(index >= 0 && index < (this->isYUV() ? SkYUVASizeInfo::kMaxCount : 1));
174             return fCallbackContexts[index].get();
175         }
refCallbackContext(int index)176         sk_sp<PromiseImageCallbackContext> refCallbackContext(int index) const {
177             SkASSERT(index >= 0 && index < (this->isYUV() ? SkYUVASizeInfo::kMaxCount : 1));
178             return fCallbackContexts[index];
179         }
180 
mipMapped(int index)181         GrMipmapped mipMapped(int index) const {
182             if (this->isYUV()) {
183                 return GrMipmapped::kNo;
184             }
185             return fMipLevels ? GrMipmapped::kYes : GrMipmapped::kNo;
186         }
backendFormat(int index)187         const GrBackendFormat& backendFormat(int index) const {
188             SkASSERT(index >= 0 && index < (this->isYUV() ? SkYUVASizeInfo::kMaxCount : 1));
189             return fCallbackContexts[index]->backendFormat();
190         }
promiseTexture(int index)191         const SkPromiseImageTexture* promiseTexture(int index) const {
192             SkASSERT(index >= 0 && index < (this->isYUV() ? SkYUVASizeInfo::kMaxCount : 1));
193             return fCallbackContexts[index]->promiseImageTexture();
194         }
195 
196         void setMipLevels(const SkBitmap& baseLevel, std::unique_ptr<SkMipmap> mipLevels);
197 
198         /** Takes ownership of the plane data. */
setYUVPlanes(SkYUVAPixmaps yuvaPixmaps)199         void setYUVPlanes(SkYUVAPixmaps yuvaPixmaps) { fYUVAPixmaps = std::move(yuvaPixmaps); }
200 
201     private:
202         const int                          fIndex;                // index in the 'fImageInfo' array
203         const uint32_t                     fOriginalUniqueID;     // original ID for deduping
204 
205         const SkImageInfo                  fImageInfo;            // info for the overarching image
206 
207         // CPU-side cache of a normal SkImage's mipmap levels
208         SkBitmap                           fBaseLevel;
209         std::unique_ptr<SkMipmap>          fMipLevels;
210 
211         // CPU-side cache of a YUV SkImage's contents
212         SkYUVAPixmaps                      fYUVAPixmaps;
213 
214         // Up to SkYUVASizeInfo::kMaxCount for a YUVA image. Only one for a normal image.
215         sk_sp<PromiseImageCallbackContext> fCallbackContexts[SkYUVASizeInfo::kMaxCount];
216     };
217 
218     // This stack-based context allows each thread to re-inflate the image indices into
219     // promise images while still using the same GrBackendTexture.
220     struct PerRecorderContext {
221         SkDeferredDisplayListRecorder* fRecorder;
222         const DDLPromiseImageHelper*   fHelper;
223         SkTArray<sk_sp<SkImage>>*      fPromiseImages;
224     };
225 
226     static void CreateBETexturesForPromiseImage(GrDirectContext*, PromiseImageInfo*);
227     static void DeleteBETexturesForPromiseImage(PromiseImageInfo*);
228 
229     static sk_sp<SkImage> CreatePromiseImages(const void* rawData, size_t length, void* ctxIn);
230 
isValidID(int id)231     bool isValidID(int id) const { return id >= 0 && id < fImageInfo.count(); }
getInfo(int id)232     const PromiseImageInfo& getInfo(int id) const { return fImageInfo[id]; }
233     void uploadImage(GrDirectContext*, PromiseImageInfo*);
234 
235     // returns -1 if not found
236     int findImage(SkImage* image) const;
237 
238     // returns -1 on failure
239     int addImage(SkImage* image);
240 
241     // returns -1 on failure
242     int findOrDefineImage(SkImage* image);
243 
244     SkYUVAPixmapInfo::SupportedDataTypes fSupportedYUVADataTypes;
245     SkTArray<PromiseImageInfo> fImageInfo;
246 };
247 
248 #endif
249