Flutter Engine
The Flutter Engine
DDLPromiseImageHelper.cpp
Go to the documentation of this file.
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
9
19#include "src/core/SkMipmap.h"
25
26DDLPromiseImageHelper::PromiseImageInfo::PromiseImageInfo(int index,
27 uint32_t originalUniqueID,
28 const SkImageInfo& ii)
29 : fIndex(index)
30 , fOriginalUniqueID(originalUniqueID)
31 , fImageInfo(ii) {
32}
33
34DDLPromiseImageHelper::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
46DDLPromiseImageHelper::PromiseImageInfo::~PromiseImageInfo() {}
47
48std::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
62int DDLPromiseImageHelper::PromiseImageInfo::numMipLevels() const {
63 SkASSERT(!this->isYUV());
64 return fMipLevels ? fMipLevels->countLevels()+1 : 1;
65}
66
67void DDLPromiseImageHelper::PromiseImageInfo::setMipLevels(const SkBitmap& baseLevel,
68 std::unique_ptr<SkMipmap> mipLevels) {
69 fBaseLevel = baseLevel;
70 fMipLevels = std::move(mipLevels);
71}
72
73///////////////////////////////////////////////////////////////////////////////////////////////////
75 SkASSERT(fDoneCnt == fNumImages);
76 SkASSERT(!fTotalFulfills || fDoneCnt);
77
78 if (fPromiseImageTexture) {
79 fContext->deleteBackendTexture(fPromiseImageTexture->backendTexture());
80 }
81}
82
84 SkASSERT(!fPromiseImageTexture);
85 SkASSERT(fBackendFormat == backendTexture.getBackendFormat());
86 fPromiseImageTexture = GrPromiseImageTexture::Make(backendTexture);
87}
88
90 SkASSERT(!fPromiseImageTexture || fPromiseImageTexture->unique());
91
92 if (fPromiseImageTexture) {
93 fContext->deleteBackendTexture(fPromiseImageTexture->backendTexture());
94 }
95 fPromiseImageTexture = nullptr;
96}
97
98///////////////////////////////////////////////////////////////////////////////////////////////////
99
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
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,
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 */
154void DDLPromiseImageHelper::CreateBETexturesForPromiseImage(GrDirectContext* direct,
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(),
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
199void 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
221void 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(),
236
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(),
253 if (!caps->isFormatTexturable(backendFormat, GrTextureType::k2D)) {
254 continue;
255 }
256
258 new PromiseImageCallbackContext(direct, backendFormat));
259
260 info.setCallbackContext(0, std::move(callbackContext));
261 }
262 }
263}
264
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
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
293sk_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.
306sk_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
331 if (curImage.isYUV()) {
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);
343 procContext->fThreadSafeProxy,
344 yuvaBackendTextures,
345 curImage.refOverallColorSpace(),
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),
365 curImage.overallColorType(),
366 curImage.overallAlphaType(),
367 curImage.refOverallColorSpace(),
370 (void*)curImage.refCallbackContext(0).release());
371 curImage.callbackContext(0)->wasAddedToImage();
372 }
373 helper->fPromiseImages.push_back(image);
375 return image;
376}
377
378int 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
389int DDLPromiseImageHelper::addImage(SkImage* image) {
390 SkImage_Base* ib = as_IB(image);
391
395 : image->colorType(),
396 image->alphaType(),
398
399 PromiseImageInfo& newImageInfo = fImageInfo.emplace_back(fImageInfo.size(),
400 image->uniqueID(),
401 overallII);
402
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
440int 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}
static GrBackendTexture create_yuva_texture(GrDirectContext *direct, const SkPixmap &pm, int texIndex)
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
@ kTopLeft_GrSurfaceOrigin
Definition: GrTypes.h:148
#define SkASSERT(cond)
Definition: SkAssert.h:116
@ kBGRA_8888_SkColorType
pixel with 8 bits for blue, green, red, alpha; in 32-bit word
Definition: SkColorType.h:26
@ kRGBA_8888_SkColorType
pixel with 8 bits for red, green, blue, alpha; in 32-bit word
Definition: SkColorType.h:24
static SkImage_Base * as_IB(SkImage *image)
Definition: SkImage_Base.h:201
sk_sp< SkPicture > recreateSKP(GrDirectContext *, SkPicture *)
void deleteAllFromGPU(SkTaskGroup *, GrDirectContext *)
void uploadAllToGPU(SkTaskGroup *, GrDirectContext *)
bool isValid() const
GrBackendFormat getBackendFormat() const
const GrCaps * caps() const
Definition: GrCaps.h:57
virtual bool isFormatTexturable(const GrBackendFormat &, GrTextureType) const =0
int maxTextureSize() const
Definition: GrCaps.h:229
SK_API GrBackendFormat defaultBackendFormat(SkColorType, GrRenderable) const
void checkAsyncWorkCompletion()
bool submit(GrSyncCpu sync=GrSyncCpu::kNo)
void deleteBackendTexture(const GrBackendTexture &)
GrBackendTexture createBackendTexture(int width, int height, const GrBackendFormat &, skgpu::Mipmapped, GrRenderable, GrProtected=GrProtected::kNo, std::string_view label={})
sk_sp< GrContextThreadSafeProxy > threadSafeProxy()
GrDirectContextPriv priv()
GrBackendTexture backendTexture() const
static sk_sp< GrPromiseImageTexture > Make(const GrBackendTexture &backendTexture)
static sk_sp< GrPromiseImageTexture > PromiseImageFulfillProc(void *textureContext)
void setBackendTexture(const GrBackendTexture &backendTexture)
static void PromiseImageReleaseProc(void *textureContext)
const GrPromiseImageTexture * promiseImageTexture() const
void allocPixels(const SkImageInfo &info, size_t rowBytes)
Definition: SkBitmap.cpp:258
void setImmutable()
Definition: SkBitmap.cpp:400
int width() const
Definition: SkBitmap.h:149
const SkPixmap & pixmap() const
Definition: SkBitmap.h:133
SkColorType colorType() const
Definition: SkBitmap.h:160
int height() const
Definition: SkBitmap.h:158
static std::unique_ptr< SkImageGenerator > MakeFromEncodedCodec(sk_sp< SkData >, std::optional< SkAlphaType >=std::nullopt)
Definition: SkData.h:25
static sk_sp< SkData > MakeWithCopy(const void *data, size_t length)
Definition: SkData.cpp:111
sk_sp< SkImage > makeRasterImage(GrDirectContext *, CachingHint cachingHint=kDisallow_CachingHint) const
Definition: SkImage.cpp:267
bool readPixels(GrDirectContext *context, const SkImageInfo &dstInfo, void *dstPixels, size_t dstRowBytes, int srcX, int srcY, CachingHint cachingHint=kAllow_CachingHint) const
Definition: SkImage.cpp:42
uint32_t uniqueID() const
Definition: SkImage.h:311
SkAlphaType alphaType() const
Definition: SkImage.cpp:154
int width() const
Definition: SkImage.h:285
SkColorType colorType() const
Definition: SkImage.cpp:152
int height() const
Definition: SkImage.h:291
sk_sp< SkData > refEncodedData() const
Definition: SkImage.cpp:214
sk_sp< SkColorSpace > refColorSpace() const
Definition: SkImage.cpp:158
static SkMipmap * Build(const SkPixmap &src, SkDiscardableFactoryProc, bool computeContents=true)
Definition: SkMipmap.cpp:45
bool unique() const
Definition: SkRefCnt.h:175
sk_sp< SkData > serialize(const SkSerialProcs *procs=nullptr) const
Definition: SkPicture.cpp:249
static sk_sp< SkPicture > MakeFromData(const SkData *data, const SkDeserialProcs *procs=nullptr)
Definition: SkPicture.cpp:160
SkColorType colorType() const
Definition: SkPixmap.h:173
void add(std::function< void(void)> fn)
Definition: SkTaskGroup.cpp:16
int numPlanes() const
Definition: SkYUVAInfo.h:204
static constexpr int kMaxPlanes
Definition: SkYUVAInfo.h:98
static SkYUVAPixmaps Allocate(const SkYUVAPixmapInfo &yuvaPixmapInfo)
T * get() const
Definition: SkRefCnt.h:303
int size() const
Definition: SkTArray.h:421
T & emplace_back(Args &&... args)
Definition: SkTArray.h:248
static float max(float r, float g, float b)
Definition: hsl.cpp:49
size_t length
SK_API sk_sp< SkImage > PromiseTextureFrom(skgpu::graphite::Recorder *, SkISize dimensions, const skgpu::graphite::TextureInfo &, const SkColorInfo &, skgpu::Origin origin, skgpu::graphite::Volatile, GraphitePromiseTextureFulfillProc, GraphitePromiseImageReleaseProc, GraphitePromiseTextureReleaseProc, GraphitePromiseImageContext, std::string_view label={})
SK_API sk_sp< SkImage > PromiseTextureFromYUVA(skgpu::graphite::Recorder *, const skgpu::graphite::YUVABackendTextureInfo &, sk_sp< SkColorSpace > imageColorSpace, skgpu::graphite::Volatile, GraphitePromiseTextureFulfillProc, GraphitePromiseImageReleaseProc, GraphitePromiseTextureReleaseProc, GraphitePromiseImageContext imageContext, GraphitePromiseTextureFulfillContext planeContexts[], std::string_view label={})
sk_sp< const SkImage > image
Definition: SkRecords.h:269
Definition: ref_ptr.h:256
SkDeserialImageProc fImageProc
static SkImageInfo Make(int width, int height, SkColorType ct, SkAlphaType at)
SkPixmap fPixmap
Definition: SkMipmap.h:71
void * fImageCtx
Definition: SkSerialProcs.h:91
SkSerialImageProc fImageProc
Definition: SkSerialProcs.h:90