• 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 #include "DDLPromiseImageHelper.h"
9 
10 #include "GrContext.h"
11 #include "GrContextPriv.h"
12 #include "GrGpu.h"
13 #include "SkCachedData.h"
14 #include "SkDeferredDisplayListRecorder.h"
15 #include "SkImage_Base.h"
16 #include "SkImage_GpuYUVA.h"
17 #include "SkYUVAIndex.h"
18 #include "SkYUVASizeInfo.h"
19 
~PromiseImageCallbackContext()20 DDLPromiseImageHelper::PromiseImageCallbackContext::~PromiseImageCallbackContext() {
21     SkASSERT(fDoneCnt == fNumImages);
22     SkASSERT(!fUnreleasedFulfills);
23     SkASSERT(fTotalReleases == fTotalFulfills);
24     SkASSERT(!fTotalFulfills || fDoneCnt);
25 
26     if (fPromiseImageTexture) {
27         GrGpu* gpu = fContext->priv().getGpu();
28         gpu->deleteTestingOnlyBackendTexture(fPromiseImageTexture->backendTexture());
29     }
30 }
31 
setBackendTexture(const GrBackendTexture & backendTexture)32 void DDLPromiseImageHelper::PromiseImageCallbackContext::setBackendTexture(
33         const GrBackendTexture& backendTexture) {
34     SkASSERT(!fPromiseImageTexture);
35     fPromiseImageTexture = SkPromiseImageTexture::Make(backendTexture);
36 }
37 
38 ///////////////////////////////////////////////////////////////////////////////////////////////////
39 
deflateSKP(const SkPicture * inputPicture)40 sk_sp<SkData> DDLPromiseImageHelper::deflateSKP(const SkPicture* inputPicture) {
41     SkSerialProcs procs;
42 
43     procs.fImageCtx = this;
44     procs.fImageProc = [](SkImage* image, void* ctx) -> sk_sp<SkData> {
45         auto helper = static_cast<DDLPromiseImageHelper*>(ctx);
46 
47         int id = helper->findOrDefineImage(image);
48         if (id >= 0) {
49             SkASSERT(helper->isValidID(id));
50             return SkData::MakeWithCopy(&id, sizeof(id));
51         }
52 
53         return nullptr;
54     };
55 
56     return inputPicture->serialize(&procs);
57 }
58 
59 // needed until we have SkRG_88_ColorType;
create_yuva_texture(GrGpu * gpu,const SkPixmap & pm,const SkYUVAIndex yuvaIndices[4],int texIndex)60 static GrBackendTexture create_yuva_texture(GrGpu* gpu, const SkPixmap& pm,
61                                             const SkYUVAIndex yuvaIndices[4], int texIndex) {
62     SkASSERT(texIndex >= 0 && texIndex <= 3);
63     int channelCount = 0;
64     for (int i = 0; i < SkYUVAIndex::kIndexCount; ++i) {
65         if (yuvaIndices[i].fIndex == texIndex) {
66             ++channelCount;
67         }
68     }
69     // Need to create an RG texture for two-channel planes
70     GrBackendTexture tex;
71     if (2 == channelCount) {
72         SkASSERT(kRGBA_8888_SkColorType == pm.colorType());
73         SkAutoTMalloc<char> pixels(2 * pm.width()*pm.height());
74         char* currPixel = pixels;
75         for (int y = 0; y < pm.height(); ++y) {
76             for (int x = 0; x < pm.width(); ++x) {
77                 SkColor color = pm.getColor(x, y);
78                 currPixel[0] = SkColorGetR(color);
79                 currPixel[1] = SkColorGetG(color);
80                 currPixel += 2;
81             }
82         }
83         tex = gpu->createTestingOnlyBackendTexture(
84             pixels,
85             pm.width(),
86             pm.height(),
87             GrColorType::kRG_88,
88             false,
89             GrMipMapped::kNo,
90             2 * pm.width());
91     } else {
92         tex = gpu->createTestingOnlyBackendTexture(
93             pm.addr(),
94             pm.width(),
95             pm.height(),
96             pm.colorType(),
97             false,
98             GrMipMapped::kNo,
99             pm.rowBytes());
100     }
101     return tex;
102 }
103 
uploadAllToGPU(GrContext * context)104 void DDLPromiseImageHelper::uploadAllToGPU(GrContext* context) {
105     GrGpu* gpu = context->priv().getGpu();
106     SkASSERT(gpu);
107 
108     for (int i = 0; i < fImageInfo.count(); ++i) {
109         const PromiseImageInfo& info = fImageInfo[i];
110 
111         // DDL TODO: how can we tell if we need mipmapping!
112         if (info.isYUV()) {
113             int numPixmaps;
114             SkAssertResult(SkYUVAIndex::AreValidIndices(info.yuvaIndices(), &numPixmaps));
115             for (int j = 0; j < numPixmaps; ++j) {
116                 const SkPixmap& yuvPixmap = info.yuvPixmap(j);
117 
118                 sk_sp<PromiseImageCallbackContext> callbackContext(
119                                                         new PromiseImageCallbackContext(context));
120 
121                 callbackContext->setBackendTexture(create_yuva_texture(gpu, yuvPixmap,
122                                                                        info.yuvaIndices(), j));
123                 SkASSERT(callbackContext->promiseImageTexture());
124 
125                 fImageInfo[i].setCallbackContext(j, std::move(callbackContext));
126             }
127         } else {
128             sk_sp<PromiseImageCallbackContext> callbackContext(
129                                                     new PromiseImageCallbackContext(context));
130 
131             const SkBitmap& bm = info.normalBitmap();
132 
133             callbackContext->setBackendTexture(gpu->createTestingOnlyBackendTexture(
134                                                                 bm.getPixels(),
135                                                                 bm.width(),
136                                                                 bm.height(),
137                                                                 bm.colorType(),
138                                                                 false, GrMipMapped::kNo,
139                                                                 bm.rowBytes()));
140             // The GMs sometimes request too large an image
141             //SkAssertResult(callbackContext->backendTexture().isValid());
142 
143             fImageInfo[i].setCallbackContext(0, std::move(callbackContext));
144         }
145     }
146 }
147 
reinflateSKP(SkDeferredDisplayListRecorder * recorder,SkData * compressedPictureData,SkTArray<sk_sp<SkImage>> * promiseImages) const148 sk_sp<SkPicture> DDLPromiseImageHelper::reinflateSKP(
149                                                    SkDeferredDisplayListRecorder* recorder,
150                                                    SkData* compressedPictureData,
151                                                    SkTArray<sk_sp<SkImage>>* promiseImages) const {
152     PerRecorderContext perRecorderContext { recorder, this, promiseImages };
153 
154     SkDeserialProcs procs;
155     procs.fImageCtx = (void*) &perRecorderContext;
156     procs.fImageProc = PromiseImageCreator;
157 
158     return SkPicture::MakeFromData(compressedPictureData, &procs);
159 }
160 
161 // This generates promise images to replace the indices in the compressed picture. This
162 // reconstitution is performed separately in each thread so we end up with multiple
163 // promise images referring to the same GrBackendTexture.
PromiseImageCreator(const void * rawData,size_t length,void * ctxIn)164 sk_sp<SkImage> DDLPromiseImageHelper::PromiseImageCreator(const void* rawData,
165                                                           size_t length, void* ctxIn) {
166     PerRecorderContext* perRecorderContext = static_cast<PerRecorderContext*>(ctxIn);
167     const DDLPromiseImageHelper* helper = perRecorderContext->fHelper;
168     SkDeferredDisplayListRecorder* recorder = perRecorderContext->fRecorder;
169 
170     SkASSERT(length == sizeof(int));
171 
172     const int* indexPtr = static_cast<const int*>(rawData);
173     SkASSERT(helper->isValidID(*indexPtr));
174 
175     const DDLPromiseImageHelper::PromiseImageInfo& curImage = helper->getInfo(*indexPtr);
176 
177     if (!curImage.promiseTexture(0)) {
178         SkASSERT(!curImage.isYUV());
179         // We weren't able to make a backend texture for this SkImage. In this case we create
180         // a separate bitmap-backed image for each thread.
181         SkASSERT(curImage.normalBitmap().isImmutable());
182         return SkImage::MakeFromBitmap(curImage.normalBitmap());
183     }
184     SkASSERT(curImage.index() == *indexPtr);
185 
186     sk_sp<SkImage> image;
187     if (curImage.isYUV()) {
188         GrBackendFormat backendFormats[SkYUVASizeInfo::kMaxCount];
189         void* contexts[SkYUVASizeInfo::kMaxCount] = { nullptr, nullptr, nullptr, nullptr };
190         SkISize sizes[SkYUVASizeInfo::kMaxCount];
191         // TODO: store this value somewhere?
192         int textureCount;
193         SkAssertResult(SkYUVAIndex::AreValidIndices(curImage.yuvaIndices(), &textureCount));
194         for (int i = 0; i < textureCount; ++i) {
195             const GrBackendTexture& backendTex = curImage.promiseTexture(i)->backendTexture();
196             backendFormats[i] = backendTex.getBackendFormat();
197             SkASSERT(backendFormats[i].isValid());
198             contexts[i] = curImage.refCallbackContext(i).release();
199             sizes[i].set(curImage.yuvPixmap(i).width(), curImage.yuvPixmap(i).height());
200         }
201         for (int i = textureCount; i < SkYUVASizeInfo::kMaxCount; ++i) {
202             sizes[i] = SkISize::MakeEmpty();
203         }
204 
205         image = recorder->makeYUVAPromiseTexture(curImage.yuvColorSpace(),
206                                                  backendFormats,
207                                                  sizes,
208                                                  curImage.yuvaIndices(),
209                                                  curImage.overallWidth(),
210                                                  curImage.overallHeight(),
211                                                  GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin,
212                                                  curImage.refOverallColorSpace(),
213                                                  DDLPromiseImageHelper::PromiseImageFulfillProc,
214                                                  DDLPromiseImageHelper::PromiseImageReleaseProc,
215                                                  DDLPromiseImageHelper::PromiseImageDoneProc,
216                                                  contexts);
217         for (int i = 0; i < textureCount; ++i) {
218             curImage.callbackContext(i)->wasAddedToImage();
219         }
220 
221 #ifdef SK_DEBUG
222         {
223             // By the peekProxy contract this image should not have a single backing proxy so
224             // should return null. The call should also not trigger the conversion to RGBA.
225             SkImage_GpuYUVA* yuva = reinterpret_cast<SkImage_GpuYUVA*>(image.get());
226             SkASSERT(!yuva->peekProxy());
227             SkASSERT(!yuva->peekProxy());  // the first call didn't force a conversion to RGBA
228         }
229 #endif
230     } else {
231         const GrBackendTexture& backendTex = curImage.promiseTexture(0)->backendTexture();
232         GrBackendFormat backendFormat = backendTex.getBackendFormat();
233         SkASSERT(backendFormat.isValid());
234 
235         // Each DDL recorder gets its own ref on the promise callback context for the
236         // promise images it creates.
237         // DDL TODO: sort out mipmapping
238         image = recorder->makePromiseTexture(backendFormat,
239                                              curImage.overallWidth(),
240                                              curImage.overallHeight(),
241                                              GrMipMapped::kNo,
242                                              GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin,
243                                              curImage.overallColorType(),
244                                              curImage.overallAlphaType(),
245                                              curImage.refOverallColorSpace(),
246                                              DDLPromiseImageHelper::PromiseImageFulfillProc,
247                                              DDLPromiseImageHelper::PromiseImageReleaseProc,
248                                              DDLPromiseImageHelper::PromiseImageDoneProc,
249                                              (void*)curImage.refCallbackContext(0).release());
250         curImage.callbackContext(0)->wasAddedToImage();
251     }
252     perRecorderContext->fPromiseImages->push_back(image);
253     SkASSERT(image);
254     return image;
255 }
256 
findImage(SkImage * image) const257 int DDLPromiseImageHelper::findImage(SkImage* image) const {
258     for (int i = 0; i < fImageInfo.count(); ++i) {
259         if (fImageInfo[i].originalUniqueID() == image->uniqueID()) { // trying to dedup here
260             SkASSERT(fImageInfo[i].index() == i);
261             SkASSERT(this->isValidID(i) && this->isValidID(fImageInfo[i].index()));
262             return i;
263         }
264     }
265     return -1;
266 }
267 
addImage(SkImage * image)268 int DDLPromiseImageHelper::addImage(SkImage* image) {
269     SkImage_Base* ib = as_IB(image);
270 
271     SkImageInfo overallII = SkImageInfo::Make(image->width(), image->height(),
272                                               image->colorType(), image->alphaType(),
273                                               image->refColorSpace());
274 
275     PromiseImageInfo& newImageInfo = fImageInfo.emplace_back(fImageInfo.count(),
276                                                              image->uniqueID(),
277                                                              overallII);
278 
279     SkYUVASizeInfo yuvaSizeInfo;
280     SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount];
281     SkYUVColorSpace yuvColorSpace;
282     const void* planes[SkYUVASizeInfo::kMaxCount];
283     sk_sp<SkCachedData> yuvData = ib->getPlanes(&yuvaSizeInfo, yuvaIndices, &yuvColorSpace, planes);
284     if (yuvData) {
285         newImageInfo.setYUVData(std::move(yuvData), yuvaIndices, yuvColorSpace);
286 
287         // determine colortypes from index data
288         // for testing we only ever use A8 or RGBA8888
289         SkColorType colorTypes[SkYUVASizeInfo::kMaxCount] = {
290             kUnknown_SkColorType, kUnknown_SkColorType,
291             kUnknown_SkColorType, kUnknown_SkColorType
292         };
293         for (int yuvIndex = 0; yuvIndex < SkYUVAIndex::kIndexCount; ++yuvIndex) {
294             int texIdx = yuvaIndices[yuvIndex].fIndex;
295             if (texIdx < 0) {
296                 SkASSERT(SkYUVAIndex::kA_Index == yuvIndex);
297                 continue;
298             }
299             if (kUnknown_SkColorType == colorTypes[texIdx]) {
300                 colorTypes[texIdx] = kAlpha_8_SkColorType;
301             } else {
302                 colorTypes[texIdx] = kRGBA_8888_SkColorType;
303             }
304         }
305 
306         for (int i = 0; i < SkYUVASizeInfo::kMaxCount; ++i) {
307             if (yuvaSizeInfo.fSizes[i].isEmpty()) {
308                 SkASSERT(!yuvaSizeInfo.fWidthBytes[i] && kUnknown_SkColorType == colorTypes[i]);
309                 continue;
310             }
311 
312             SkImageInfo planeII = SkImageInfo::Make(yuvaSizeInfo.fSizes[i].fWidth,
313                                                     yuvaSizeInfo.fSizes[i].fHeight,
314                                                     colorTypes[i],
315                                                     kUnpremul_SkAlphaType);
316             newImageInfo.addYUVPlane(i, planeII, planes[i], yuvaSizeInfo.fWidthBytes[i]);
317         }
318     } else {
319         sk_sp<SkImage> rasterImage = image->makeRasterImage(); // force decoding of lazy images
320 
321         SkBitmap tmp;
322         tmp.allocPixels(overallII);
323 
324         if (!rasterImage->readPixels(tmp.pixmap(), 0, 0)) {
325             return -1;
326         }
327 
328         tmp.setImmutable();
329         newImageInfo.setNormalBitmap(tmp);
330     }
331     // In either case newImageInfo's PromiseImageCallbackContext is filled in by uploadAllToGPU
332 
333     return fImageInfo.count()-1;
334 }
335 
findOrDefineImage(SkImage * image)336 int DDLPromiseImageHelper::findOrDefineImage(SkImage* image) {
337     int preExistingID = this->findImage(image);
338     if (preExistingID >= 0) {
339         SkASSERT(this->isValidID(preExistingID));
340         return preExistingID;
341     }
342 
343     int newID = this->addImage(image);
344     SkASSERT(this->isValidID(newID));
345     return newID;
346 }
347