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