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