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