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