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 "tools/DDLPromiseImageHelper.h"
9 
10 #include "include/core/SkDeferredDisplayListRecorder.h"
11 #include "include/core/SkPicture.h"
12 #include "include/core/SkSerialProcs.h"
13 #include "include/core/SkYUVAIndex.h"
14 #include "include/core/SkYUVASizeInfo.h"
15 #include "include/gpu/GrDirectContext.h"
16 #include "include/gpu/GrYUVABackendTextures.h"
17 #include "src/codec/SkCodecImageGenerator.h"
18 #include "src/core/SkCachedData.h"
19 #include "src/core/SkMipmap.h"
20 #include "src/core/SkTaskGroup.h"
21 #include "src/gpu/GrDirectContextPriv.h"
22 #include "src/image/SkImage_Base.h"
23 #include "src/image/SkImage_GpuYUVA.h"
24 
PromiseImageInfo(int index,uint32_t originalUniqueID,const SkImageInfo & ii)25 DDLPromiseImageHelper::PromiseImageInfo::PromiseImageInfo(int index,
26                                                           uint32_t originalUniqueID,
27                                                           const SkImageInfo& ii)
28         : fIndex(index)
29         , fOriginalUniqueID(originalUniqueID)
30         , fImageInfo(ii) {
31 }
32 
PromiseImageInfo(PromiseImageInfo && other)33 DDLPromiseImageHelper::PromiseImageInfo::PromiseImageInfo(PromiseImageInfo&& other)
34         : fIndex(other.fIndex)
35         , fOriginalUniqueID(other.fOriginalUniqueID)
36         , fImageInfo(other.fImageInfo)
37         , fBaseLevel(other.fBaseLevel)
38         , fMipLevels(std::move(other.fMipLevels))
39         , fYUVAPixmaps(std::move(other.fYUVAPixmaps)) {
40     for (int i = 0; i < SkYUVASizeInfo::kMaxCount; ++i) {
41         fCallbackContexts[i] = std::move(other.fCallbackContexts[i]);
42     }
43 }
44 
~PromiseImageInfo()45 DDLPromiseImageHelper::PromiseImageInfo::~PromiseImageInfo() {}
46 
normalMipLevels() const47 std::unique_ptr<SkPixmap[]> DDLPromiseImageHelper::PromiseImageInfo::normalMipLevels() const {
48     SkASSERT(!this->isYUV());
49     std::unique_ptr<SkPixmap[]> pixmaps(new SkPixmap[this->numMipLevels()]);
50     pixmaps[0] = fBaseLevel.pixmap();
51     if (fMipLevels) {
52         for (int i = 0; i < fMipLevels->countLevels(); ++i) {
53             SkMipmap::Level mipLevel;
54             fMipLevels->getLevel(i, &mipLevel);
55             pixmaps[i+1] = mipLevel.fPixmap;
56         }
57     }
58     return pixmaps;
59 }
60 
numMipLevels() const61 int DDLPromiseImageHelper::PromiseImageInfo::numMipLevels() const {
62     SkASSERT(!this->isYUV());
63     return fMipLevels ? fMipLevels->countLevels()+1 : 1;
64 }
65 
setMipLevels(const SkBitmap & baseLevel,std::unique_ptr<SkMipmap> mipLevels)66 void DDLPromiseImageHelper::PromiseImageInfo::setMipLevels(const SkBitmap& baseLevel,
67                                                            std::unique_ptr<SkMipmap> mipLevels) {
68     fBaseLevel = baseLevel;
69     fMipLevels = std::move(mipLevels);
70 }
71 
72 ///////////////////////////////////////////////////////////////////////////////////////////////////
~PromiseImageCallbackContext()73 PromiseImageCallbackContext::~PromiseImageCallbackContext() {
74     SkASSERT(fDoneCnt == fNumImages);
75     SkASSERT(!fTotalFulfills || fDoneCnt);
76 
77     if (fPromiseImageTexture) {
78         fContext->deleteBackendTexture(fPromiseImageTexture->backendTexture());
79     }
80 }
81 
setBackendTexture(const GrBackendTexture & backendTexture)82 void PromiseImageCallbackContext::setBackendTexture(const GrBackendTexture& backendTexture) {
83     SkASSERT(!fPromiseImageTexture);
84     SkASSERT(fBackendFormat == backendTexture.getBackendFormat());
85     fPromiseImageTexture = SkPromiseImageTexture::Make(backendTexture);
86 }
87 
destroyBackendTexture()88 void PromiseImageCallbackContext::destroyBackendTexture() {
89     SkASSERT(!fPromiseImageTexture || fPromiseImageTexture->unique());
90 
91     if (fPromiseImageTexture) {
92         fContext->deleteBackendTexture(fPromiseImageTexture->backendTexture());
93     }
94     fPromiseImageTexture = nullptr;
95 }
96 
97 ///////////////////////////////////////////////////////////////////////////////////////////////////
98 
deflateSKP(const SkPicture * inputPicture)99 sk_sp<SkData> DDLPromiseImageHelper::deflateSKP(const SkPicture* inputPicture) {
100     SkSerialProcs procs;
101 
102     procs.fImageCtx = this;
103     procs.fImageProc = [](SkImage* image, void* ctx) -> sk_sp<SkData> {
104         auto helper = static_cast<DDLPromiseImageHelper*>(ctx);
105 
106         int id = helper->findOrDefineImage(image);
107 
108         // Even if 'id' is invalid (i.e., -1) write it to the SKP
109         return SkData::MakeWithCopy(&id, sizeof(id));
110     };
111 
112     return inputPicture->serialize(&procs);
113 }
114 
create_yuva_texture(GrDirectContext * direct,const SkPixmap & pm,int texIndex)115 static GrBackendTexture create_yuva_texture(GrDirectContext* direct,
116                                             const SkPixmap& pm,
117                                             int texIndex) {
118     SkASSERT(texIndex >= 0 && texIndex <= 3);
119 
120     bool finishedBECreate = false;
121     auto markFinished = [](void* context) {
122         *(bool*)context = true;
123     };
124     auto beTex = direct->createBackendTexture(&pm, 1, GrRenderable::kNo, GrProtected::kNo,
125                                               markFinished, &finishedBECreate);
126     if (beTex.isValid()) {
127         direct->submit();
128         while (!finishedBECreate) {
129             direct->checkAsyncWorkCompletion();
130         }
131     }
132     return beTex;
133 }
134 
135 /*
136  * Create backend textures and upload data to them for all the textures required to satisfy
137  * a single promise image.
138  * For YUV textures this will result in up to 4 actual textures.
139  */
CreateBETexturesForPromiseImage(GrDirectContext * direct,PromiseImageInfo * info)140 void DDLPromiseImageHelper::CreateBETexturesForPromiseImage(GrDirectContext* direct,
141                                                             PromiseImageInfo* info) {
142     if (info->isYUV()) {
143         int numPixmaps = info->yuvaInfo().numPlanes();
144         for (int j = 0; j < numPixmaps; ++j) {
145             const SkPixmap& yuvPixmap = info->yuvPixmap(j);
146 
147             PromiseImageCallbackContext* callbackContext = info->callbackContext(j);
148             SkASSERT(callbackContext);
149 
150             // DDL TODO: what should we do with mipmapped YUV images
151             callbackContext->setBackendTexture(create_yuva_texture(direct, yuvPixmap, j));
152             SkASSERT(callbackContext->promiseImageTexture());
153         }
154     } else {
155         PromiseImageCallbackContext* callbackContext = info->callbackContext(0);
156         if (!callbackContext) {
157             // This texture would've been too large to fit on the GPU
158             return;
159         }
160 
161         std::unique_ptr<SkPixmap[]> mipLevels = info->normalMipLevels();
162 
163         bool finishedBECreate = false;
164         auto markFinished = [](void* context) {
165             *(bool*)context = true;
166         };
167         auto backendTex = direct->createBackendTexture(mipLevels.get(), info->numMipLevels(),
168                                                        GrRenderable::kNo, GrProtected::kNo,
169                                                        markFinished, &finishedBECreate);
170         SkASSERT(backendTex.isValid());
171         direct->submit();
172         while (!finishedBECreate) {
173             direct->checkAsyncWorkCompletion();
174         }
175 
176         callbackContext->setBackendTexture(backendTex);
177     }
178 }
179 
DeleteBETexturesForPromiseImage(PromiseImageInfo * info)180 void DDLPromiseImageHelper::DeleteBETexturesForPromiseImage(PromiseImageInfo* info) {
181     if (info->isYUV()) {
182         int numPixmaps = info->yuvaInfo().numPlanes();
183         for (int j = 0; j < numPixmaps; ++j) {
184             PromiseImageCallbackContext* callbackContext = info->callbackContext(j);
185             SkASSERT(callbackContext);
186 
187             callbackContext->destroyBackendTexture();
188             SkASSERT(!callbackContext->promiseImageTexture());
189         }
190     } else {
191         PromiseImageCallbackContext* callbackContext = info->callbackContext(0);
192         if (!callbackContext) {
193             // This texture would've been too large to fit on the GPU
194             return;
195         }
196 
197         callbackContext->destroyBackendTexture();
198         SkASSERT(!callbackContext->promiseImageTexture());
199     }
200 }
201 
createCallbackContexts(GrDirectContext * direct)202 void DDLPromiseImageHelper::createCallbackContexts(GrDirectContext* direct) {
203     const GrCaps* caps = direct->priv().caps();
204     const int maxDimension = caps->maxTextureSize();
205 
206     for (int i = 0; i < fImageInfo.count(); ++i) {
207         PromiseImageInfo& info = fImageInfo[i];
208 
209         if (info.isYUV()) {
210             int numPixmaps = info.yuvaInfo().numPlanes();
211 
212             for (int j = 0; j < numPixmaps; ++j) {
213                 const SkPixmap& yuvPixmap = info.yuvPixmap(j);
214 
215                 GrBackendFormat backendFormat = direct->defaultBackendFormat(yuvPixmap.colorType(),
216                                                                              GrRenderable::kNo);
217 
218                 sk_sp<PromiseImageCallbackContext> callbackContext(
219                     new PromiseImageCallbackContext(direct, backendFormat));
220 
221                 info.setCallbackContext(j, std::move(callbackContext));
222             }
223         } else {
224             const SkBitmap& baseLevel = info.baseLevel();
225 
226             // TODO: explicitly mark the PromiseImageInfo as too big and check in uploadAllToGPU
227             if (maxDimension < std::max(baseLevel.width(), baseLevel.height())) {
228                 // This won't fit on the GPU. Fallback to a raster-backed image per tile.
229                 continue;
230             }
231 
232             GrBackendFormat backendFormat = direct->defaultBackendFormat(baseLevel.colorType(),
233                                                                          GrRenderable::kNo);
234             if (!caps->isFormatTexturable(backendFormat)) {
235                 continue;
236             }
237 
238             sk_sp<PromiseImageCallbackContext> callbackContext(
239                 new PromiseImageCallbackContext(direct, backendFormat));
240 
241             info.setCallbackContext(0, std::move(callbackContext));
242         }
243     }
244 }
245 
uploadAllToGPU(SkTaskGroup * taskGroup,GrDirectContext * direct)246 void DDLPromiseImageHelper::uploadAllToGPU(SkTaskGroup* taskGroup, GrDirectContext* direct) {
247     if (taskGroup) {
248         for (int i = 0; i < fImageInfo.count(); ++i) {
249             PromiseImageInfo* info = &fImageInfo[i];
250 
251             taskGroup->add([direct, info]() { CreateBETexturesForPromiseImage(direct, info); });
252         }
253     } else {
254         for (int i = 0; i < fImageInfo.count(); ++i) {
255             CreateBETexturesForPromiseImage(direct, &fImageInfo[i]);
256         }
257     }
258 }
259 
deleteAllFromGPU(SkTaskGroup * taskGroup,GrDirectContext * direct)260 void DDLPromiseImageHelper::deleteAllFromGPU(SkTaskGroup* taskGroup, GrDirectContext* direct) {
261     if (taskGroup) {
262         for (int i = 0; i < fImageInfo.count(); ++i) {
263             PromiseImageInfo* info = &fImageInfo[i];
264 
265             taskGroup->add([info]() { DeleteBETexturesForPromiseImage(info); });
266         }
267     } else {
268         for (int i = 0; i < fImageInfo.count(); ++i) {
269             DeleteBETexturesForPromiseImage(&fImageInfo[i]);
270         }
271     }
272 }
273 
reinflateSKP(SkDeferredDisplayListRecorder * recorder,SkData * compressedPictureData,SkTArray<sk_sp<SkImage>> * promiseImages) const274 sk_sp<SkPicture> DDLPromiseImageHelper::reinflateSKP(
275                                                    SkDeferredDisplayListRecorder* recorder,
276                                                    SkData* compressedPictureData,
277                                                    SkTArray<sk_sp<SkImage>>* promiseImages) const {
278     PerRecorderContext perRecorderContext { recorder, this, promiseImages };
279 
280     SkDeserialProcs procs;
281     procs.fImageCtx = (void*) &perRecorderContext;
282     procs.fImageProc = CreatePromiseImages;
283 
284     return SkPicture::MakeFromData(compressedPictureData, &procs);
285 }
286 
287 // This generates promise images to replace the indices in the compressed picture. This
288 // reconstitution is performed separately in each thread so we end up with multiple
289 // promise images referring to the same GrBackendTexture.
CreatePromiseImages(const void * rawData,size_t length,void * ctxIn)290 sk_sp<SkImage> DDLPromiseImageHelper::CreatePromiseImages(const void* rawData,
291                                                           size_t length, void* ctxIn) {
292     PerRecorderContext* perRecorderContext = static_cast<PerRecorderContext*>(ctxIn);
293     const DDLPromiseImageHelper* helper = perRecorderContext->fHelper;
294     SkDeferredDisplayListRecorder* recorder = perRecorderContext->fRecorder;
295 
296     SkASSERT(length == sizeof(int));
297 
298     const int* indexPtr = static_cast<const int*>(rawData);
299     if (!helper->isValidID(*indexPtr)) {
300         return nullptr;
301     }
302 
303     const DDLPromiseImageHelper::PromiseImageInfo& curImage = helper->getInfo(*indexPtr);
304 
305     // If there is no callback context that means 'createCallbackContexts' determined the
306     // texture wouldn't fit on the GPU. Create a separate bitmap-backed image for each thread.
307     if (!curImage.isYUV() && !curImage.callbackContext(0)) {
308         SkASSERT(curImage.baseLevel().isImmutable());
309         return SkImage::MakeFromBitmap(curImage.baseLevel());
310     }
311 
312     SkASSERT(curImage.index() == *indexPtr);
313 
314     sk_sp<SkImage> image;
315     if (curImage.isYUV()) {
316         GrBackendFormat backendFormats[SkYUVASizeInfo::kMaxCount];
317         const SkYUVAInfo& yuvaInfo = curImage.yuvaInfo();
318         void* contexts[SkYUVASizeInfo::kMaxCount] = { nullptr, nullptr, nullptr, nullptr };
319         int textureCount = yuvaInfo.numPlanes();
320         for (int i = 0; i < textureCount; ++i) {
321             backendFormats[i] = curImage.backendFormat(i);
322             contexts[i] = curImage.refCallbackContext(i).release();
323         }
324         GrYUVABackendTextureInfo yuvaBackendTextures(yuvaInfo,
325                                                      backendFormats,
326                                                      GrMipmapped::kNo,
327                                                      kTopLeft_GrSurfaceOrigin);
328 
329         image = recorder->makeYUVAPromiseTexture(
330                 yuvaBackendTextures,
331                 curImage.refOverallColorSpace(),
332                 PromiseImageCallbackContext::PromiseImageFulfillProc,
333                 PromiseImageCallbackContext::PromiseImageReleaseProc,
334                 contexts);
335         for (int i = 0; i < textureCount; ++i) {
336             curImage.callbackContext(i)->wasAddedToImage();
337         }
338 
339 #ifdef SK_DEBUG
340         {
341             // By the peekProxy contract this image should not have a single backing proxy so
342             // should return null. The call should also not trigger the conversion to RGBA.
343             SkImage_GpuYUVA* yuva = reinterpret_cast<SkImage_GpuYUVA*>(image.get());
344             SkASSERT(!yuva->peekProxy());
345             SkASSERT(!yuva->peekProxy());  // the first call didn't force a conversion to RGBA
346         }
347 #endif
348     } else {
349         const GrBackendFormat& backendFormat = curImage.backendFormat(0);
350         SkASSERT(backendFormat.isValid());
351 
352         // Each DDL recorder gets its own ref on the promise callback context for the
353         // promise images it creates.
354         image = recorder->makePromiseTexture(backendFormat,
355                                              curImage.overallWidth(),
356                                              curImage.overallHeight(),
357                                              curImage.mipMapped(0),
358                                              GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin,
359                                              curImage.overallColorType(),
360                                              curImage.overallAlphaType(),
361                                              curImage.refOverallColorSpace(),
362                                              PromiseImageCallbackContext::PromiseImageFulfillProc,
363                                              PromiseImageCallbackContext::PromiseImageReleaseProc,
364                                              (void*)curImage.refCallbackContext(0).release());
365         curImage.callbackContext(0)->wasAddedToImage();
366     }
367     perRecorderContext->fPromiseImages->push_back(image);
368     SkASSERT(image);
369     return image;
370 }
371 
findImage(SkImage * image) const372 int DDLPromiseImageHelper::findImage(SkImage* image) const {
373     for (int i = 0; i < fImageInfo.count(); ++i) {
374         if (fImageInfo[i].originalUniqueID() == image->uniqueID()) { // trying to dedup here
375             SkASSERT(fImageInfo[i].index() == i);
376             SkASSERT(this->isValidID(i) && this->isValidID(fImageInfo[i].index()));
377             return i;
378         }
379     }
380     return -1;
381 }
382 
addImage(SkImage * image)383 int DDLPromiseImageHelper::addImage(SkImage* image) {
384     SkImage_Base* ib = as_IB(image);
385 
386     SkImageInfo overallII = SkImageInfo::Make(image->width(), image->height(),
387                                               image->colorType() == kBGRA_8888_SkColorType
388                                                         ? kRGBA_8888_SkColorType
389                                                         : image->colorType(),
390                                               image->alphaType(),
391                                               image->refColorSpace());
392 
393     PromiseImageInfo& newImageInfo = fImageInfo.emplace_back(fImageInfo.count(),
394                                                              image->uniqueID(),
395                                                              overallII);
396 
397     auto codec = SkCodecImageGenerator::MakeFromEncodedCodec(ib->refEncodedData());
398     SkYUVAPixmapInfo yuvaInfo;
399     if (codec && codec->queryYUVAInfo(fSupportedYUVADataTypes, &yuvaInfo)) {
400         auto yuvaPixmaps = SkYUVAPixmaps::Allocate(yuvaInfo);
401         SkAssertResult(codec->getYUVAPlanes(yuvaPixmaps));
402         SkASSERT(yuvaPixmaps.isValid());
403         newImageInfo.setYUVPlanes(std::move(yuvaPixmaps));
404     } else {
405         sk_sp<SkImage> rasterImage = image->makeRasterImage(); // force decoding of lazy images
406         if (!rasterImage) {
407             return -1;
408         }
409 
410         SkBitmap tmp;
411         tmp.allocPixels(overallII);
412 
413         if (!rasterImage->readPixels(nullptr, tmp.pixmap(), 0, 0)) {
414             return -1;
415         }
416 
417         tmp.setImmutable();
418 
419         // Given how the DDL testing harness works (i.e., only modifying the SkImages w/in an
420         // SKP) we don't know if a given SkImage will require mipmapping. To work around this
421         // we just try to create all the backend textures as mipmapped but, failing that, fall
422         // back to un-mipped.
423         std::unique_ptr<SkMipmap> mipmaps(SkMipmap::Build(tmp.pixmap(), nullptr));
424 
425         newImageInfo.setMipLevels(tmp, std::move(mipmaps));
426     }
427     // In either case newImageInfo's PromiseImageCallbackContext is filled in by uploadAllToGPU
428 
429     return fImageInfo.count()-1;
430 }
431 
findOrDefineImage(SkImage * image)432 int DDLPromiseImageHelper::findOrDefineImage(SkImage* image) {
433     int preExistingID = this->findImage(image);
434     if (preExistingID >= 0) {
435         SkASSERT(this->isValidID(preExistingID));
436         return preExistingID;
437     }
438 
439     int newID = this->addImage(image);
440     return newID;
441 }
442