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/gpu/GrDirectContext.h"
14 #include "include/gpu/GrYUVABackendTextures.h"
15 #include "src/codec/SkCodecImageGenerator.h"
16 #include "src/core/SkCachedData.h"
17 #include "src/core/SkMipmap.h"
18 #include "src/core/SkTaskGroup.h"
19 #include "src/gpu/ganesh/GrCaps.h"
20 #include "src/gpu/ganesh/GrDirectContextPriv.h"
21 #include "src/image/SkImage_Base.h"
22 #include "src/image/SkImage_GpuYUVA.h"
23
PromiseImageInfo(int index,uint32_t originalUniqueID,const SkImageInfo & ii)24 DDLPromiseImageHelper::PromiseImageInfo::PromiseImageInfo(int index,
25 uint32_t originalUniqueID,
26 const SkImageInfo& ii)
27 : fIndex(index)
28 , fOriginalUniqueID(originalUniqueID)
29 , fImageInfo(ii) {
30 }
31
PromiseImageInfo(PromiseImageInfo && other)32 DDLPromiseImageHelper::PromiseImageInfo::PromiseImageInfo(PromiseImageInfo&& other)
33 : fIndex(other.fIndex)
34 , fOriginalUniqueID(other.fOriginalUniqueID)
35 , fImageInfo(other.fImageInfo)
36 , fBaseLevel(other.fBaseLevel)
37 , fMipLevels(std::move(other.fMipLevels))
38 , fYUVAPixmaps(std::move(other.fYUVAPixmaps)) {
39 for (int i = 0; i < SkYUVAInfo::kMaxPlanes; ++i) {
40 fCallbackContexts[i] = std::move(other.fCallbackContexts[i]);
41 }
42 }
43
~PromiseImageInfo()44 DDLPromiseImageHelper::PromiseImageInfo::~PromiseImageInfo() {}
45
normalMipLevels() const46 std::unique_ptr<SkPixmap[]> DDLPromiseImageHelper::PromiseImageInfo::normalMipLevels() const {
47 SkASSERT(!this->isYUV());
48 std::unique_ptr<SkPixmap[]> pixmaps(new SkPixmap[this->numMipLevels()]);
49 pixmaps[0] = fBaseLevel.pixmap();
50 if (fMipLevels) {
51 for (int i = 0; i < fMipLevels->countLevels(); ++i) {
52 SkMipmap::Level mipLevel;
53 fMipLevels->getLevel(i, &mipLevel);
54 pixmaps[i+1] = mipLevel.fPixmap;
55 }
56 }
57 return pixmaps;
58 }
59
numMipLevels() const60 int DDLPromiseImageHelper::PromiseImageInfo::numMipLevels() const {
61 SkASSERT(!this->isYUV());
62 return fMipLevels ? fMipLevels->countLevels()+1 : 1;
63 }
64
setMipLevels(const SkBitmap & baseLevel,std::unique_ptr<SkMipmap> mipLevels)65 void DDLPromiseImageHelper::PromiseImageInfo::setMipLevels(const SkBitmap& baseLevel,
66 std::unique_ptr<SkMipmap> mipLevels) {
67 fBaseLevel = baseLevel;
68 fMipLevels = std::move(mipLevels);
69 }
70
71 ///////////////////////////////////////////////////////////////////////////////////////////////////
~PromiseImageCallbackContext()72 PromiseImageCallbackContext::~PromiseImageCallbackContext() {
73 SkASSERT(fDoneCnt == fNumImages);
74 SkASSERT(!fTotalFulfills || fDoneCnt);
75
76 if (fPromiseImageTexture) {
77 fContext->deleteBackendTexture(fPromiseImageTexture->backendTexture());
78 }
79 }
80
setBackendTexture(const GrBackendTexture & backendTexture)81 void PromiseImageCallbackContext::setBackendTexture(const GrBackendTexture& backendTexture) {
82 SkASSERT(!fPromiseImageTexture);
83 SkASSERT(fBackendFormat == backendTexture.getBackendFormat());
84 fPromiseImageTexture = SkPromiseImageTexture::Make(backendTexture);
85 }
86
destroyBackendTexture()87 void PromiseImageCallbackContext::destroyBackendTexture() {
88 SkASSERT(!fPromiseImageTexture || fPromiseImageTexture->unique());
89
90 if (fPromiseImageTexture) {
91 fContext->deleteBackendTexture(fPromiseImageTexture->backendTexture());
92 }
93 fPromiseImageTexture = nullptr;
94 }
95
96 ///////////////////////////////////////////////////////////////////////////////////////////////////
97
recreateSKP(GrDirectContext * dContext,SkPicture * inputPicture)98 sk_sp<SkPicture> DDLPromiseImageHelper::recreateSKP(GrDirectContext* dContext,
99 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 sk_sp<SkData> compressedPictureData = inputPicture->serialize(&procs);
113 if (!compressedPictureData) {
114 return nullptr;
115 }
116
117 this->createCallbackContexts(dContext);
118
119 return this->reinflateSKP(dContext->threadSafeProxy(), compressedPictureData.get());
120 }
121
create_yuva_texture(GrDirectContext * direct,const SkPixmap & pm,int texIndex)122 static GrBackendTexture create_yuva_texture(GrDirectContext* direct,
123 const SkPixmap& pm,
124 int texIndex) {
125 SkASSERT(texIndex >= 0 && texIndex <= 3);
126
127 bool finishedBECreate = false;
128 auto markFinished = [](void* context) {
129 *(bool*)context = true;
130 };
131 auto beTex = direct->createBackendTexture(pm,
132 kTopLeft_GrSurfaceOrigin,
133 GrRenderable::kNo,
134 GrProtected::kNo,
135 markFinished,
136 &finishedBECreate,
137 /*label=*/"CreateYuvaTexture");
138 if (beTex.isValid()) {
139 direct->submit();
140 while (!finishedBECreate) {
141 direct->checkAsyncWorkCompletion();
142 }
143 }
144 return beTex;
145 }
146
147 /*
148 * Create backend textures and upload data to them for all the textures required to satisfy
149 * a single promise image.
150 * For YUV textures this will result in up to 4 actual textures.
151 */
CreateBETexturesForPromiseImage(GrDirectContext * direct,PromiseImageInfo * info)152 void DDLPromiseImageHelper::CreateBETexturesForPromiseImage(GrDirectContext* direct,
153 PromiseImageInfo* info) {
154 if (info->isYUV()) {
155 int numPixmaps = info->yuvaInfo().numPlanes();
156 for (int j = 0; j < numPixmaps; ++j) {
157 const SkPixmap& yuvPixmap = info->yuvPixmap(j);
158
159 PromiseImageCallbackContext* callbackContext = info->callbackContext(j);
160 SkASSERT(callbackContext);
161
162 // DDL TODO: what should we do with mipmapped YUV images
163 callbackContext->setBackendTexture(create_yuva_texture(direct, yuvPixmap, j));
164 SkASSERT(callbackContext->promiseImageTexture());
165 }
166 } else {
167 PromiseImageCallbackContext* callbackContext = info->callbackContext(0);
168 if (!callbackContext) {
169 // This texture would've been too large to fit on the GPU
170 return;
171 }
172
173 std::unique_ptr<SkPixmap[]> mipLevels = info->normalMipLevels();
174
175 bool finishedBECreate = false;
176 auto markFinished = [](void* context) {
177 *(bool*)context = true;
178 };
179 auto backendTex = direct->createBackendTexture(mipLevels.get(),
180 info->numMipLevels(),
181 kTopLeft_GrSurfaceOrigin,
182 GrRenderable::kNo,
183 GrProtected::kNo,
184 markFinished,
185 &finishedBECreate,
186 /*label=*/"CreateBETexturesForPromiseImage");
187 SkASSERT(backendTex.isValid());
188 direct->submit();
189 while (!finishedBECreate) {
190 direct->checkAsyncWorkCompletion();
191 }
192
193 callbackContext->setBackendTexture(backendTex);
194 }
195 }
196
DeleteBETexturesForPromiseImage(PromiseImageInfo * info)197 void DDLPromiseImageHelper::DeleteBETexturesForPromiseImage(PromiseImageInfo* info) {
198 if (info->isYUV()) {
199 int numPixmaps = info->yuvaInfo().numPlanes();
200 for (int j = 0; j < numPixmaps; ++j) {
201 PromiseImageCallbackContext* callbackContext = info->callbackContext(j);
202 SkASSERT(callbackContext);
203
204 callbackContext->destroyBackendTexture();
205 SkASSERT(!callbackContext->promiseImageTexture());
206 }
207 } else {
208 PromiseImageCallbackContext* callbackContext = info->callbackContext(0);
209 if (!callbackContext) {
210 // This texture would've been too large to fit on the GPU
211 return;
212 }
213
214 callbackContext->destroyBackendTexture();
215 SkASSERT(!callbackContext->promiseImageTexture());
216 }
217 }
218
createCallbackContexts(GrDirectContext * direct)219 void DDLPromiseImageHelper::createCallbackContexts(GrDirectContext* direct) {
220 const GrCaps* caps = direct->priv().caps();
221 const int maxDimension = caps->maxTextureSize();
222
223 for (int i = 0; i < fImageInfo.size(); ++i) {
224 PromiseImageInfo& info = fImageInfo[i];
225
226 if (info.isYUV()) {
227 int numPixmaps = info.yuvaInfo().numPlanes();
228
229 for (int j = 0; j < numPixmaps; ++j) {
230 const SkPixmap& yuvPixmap = info.yuvPixmap(j);
231
232 GrBackendFormat backendFormat = direct->defaultBackendFormat(yuvPixmap.colorType(),
233 GrRenderable::kNo);
234
235 sk_sp<PromiseImageCallbackContext> callbackContext(
236 new PromiseImageCallbackContext(direct, backendFormat));
237
238 info.setCallbackContext(j, std::move(callbackContext));
239 }
240 } else {
241 const SkBitmap& baseLevel = info.baseLevel();
242
243 // TODO: explicitly mark the PromiseImageInfo as too big and check in uploadAllToGPU
244 if (maxDimension < std::max(baseLevel.width(), baseLevel.height())) {
245 // This won't fit on the GPU. Fallback to a raster-backed image per tile.
246 continue;
247 }
248
249 GrBackendFormat backendFormat = direct->defaultBackendFormat(baseLevel.colorType(),
250 GrRenderable::kNo);
251 if (!caps->isFormatTexturable(backendFormat, GrTextureType::k2D)) {
252 continue;
253 }
254
255 sk_sp<PromiseImageCallbackContext> callbackContext(
256 new PromiseImageCallbackContext(direct, backendFormat));
257
258 info.setCallbackContext(0, std::move(callbackContext));
259 }
260 }
261 }
262
uploadAllToGPU(SkTaskGroup * taskGroup,GrDirectContext * direct)263 void DDLPromiseImageHelper::uploadAllToGPU(SkTaskGroup* taskGroup, GrDirectContext* direct) {
264 if (taskGroup) {
265 for (int i = 0; i < fImageInfo.size(); ++i) {
266 PromiseImageInfo* info = &fImageInfo[i];
267
268 taskGroup->add([direct, info]() { CreateBETexturesForPromiseImage(direct, info); });
269 }
270 } else {
271 for (int i = 0; i < fImageInfo.size(); ++i) {
272 CreateBETexturesForPromiseImage(direct, &fImageInfo[i]);
273 }
274 }
275 }
276
deleteAllFromGPU(SkTaskGroup * taskGroup,GrDirectContext * direct)277 void DDLPromiseImageHelper::deleteAllFromGPU(SkTaskGroup* taskGroup, GrDirectContext* direct) {
278 if (taskGroup) {
279 for (int i = 0; i < fImageInfo.size(); ++i) {
280 PromiseImageInfo* info = &fImageInfo[i];
281
282 taskGroup->add([info]() { DeleteBETexturesForPromiseImage(info); });
283 }
284 } else {
285 for (int i = 0; i < fImageInfo.size(); ++i) {
286 DeleteBETexturesForPromiseImage(&fImageInfo[i]);
287 }
288 }
289 }
290
reinflateSKP(sk_sp<GrContextThreadSafeProxy> threadSafeProxy,SkData * compressedPictureData)291 sk_sp<SkPicture> DDLPromiseImageHelper::reinflateSKP(
292 sk_sp<GrContextThreadSafeProxy> threadSafeProxy,
293 SkData* compressedPictureData) {
294 DeserialImageProcContext procContext { std::move(threadSafeProxy), this };
295
296 SkDeserialProcs procs;
297 procs.fImageCtx = (void*) &procContext;
298 procs.fImageProc = CreatePromiseImages;
299
300 return SkPicture::MakeFromData(compressedPictureData, &procs);
301 }
302
303 // This generates promise images to replace the indices in the compressed picture.
CreatePromiseImages(const void * rawData,size_t length,void * ctxIn)304 sk_sp<SkImage> DDLPromiseImageHelper::CreatePromiseImages(const void* rawData,
305 size_t length,
306 void* ctxIn) {
307 DeserialImageProcContext* procContext = static_cast<DeserialImageProcContext*>(ctxIn);
308 DDLPromiseImageHelper* helper = procContext->fHelper;
309
310 SkASSERT(length == sizeof(int));
311
312 const int* indexPtr = static_cast<const int*>(rawData);
313 if (!helper->isValidID(*indexPtr)) {
314 return nullptr;
315 }
316
317 const DDLPromiseImageHelper::PromiseImageInfo& curImage = helper->getInfo(*indexPtr);
318
319 // If there is no callback context that means 'createCallbackContexts' determined the
320 // texture wouldn't fit on the GPU. Create a bitmap-backed image.
321 if (!curImage.isYUV() && !curImage.callbackContext(0)) {
322 SkASSERT(curImage.baseLevel().isImmutable());
323 return curImage.baseLevel().asImage();
324 }
325
326 SkASSERT(curImage.index() == *indexPtr);
327
328 sk_sp<SkImage> image;
329 if (curImage.isYUV()) {
330 GrBackendFormat backendFormats[SkYUVAInfo::kMaxPlanes];
331 const SkYUVAInfo& yuvaInfo = curImage.yuvaInfo();
332 void* contexts[SkYUVAInfo::kMaxPlanes] = {nullptr, nullptr, nullptr, nullptr};
333 int textureCount = yuvaInfo.numPlanes();
334 for (int i = 0; i < textureCount; ++i) {
335 backendFormats[i] = curImage.backendFormat(i);
336 contexts[i] = curImage.refCallbackContext(i).release();
337 }
338 GrYUVABackendTextureInfo yuvaBackendTextures(yuvaInfo,
339 backendFormats,
340 GrMipmapped::kNo,
341 kTopLeft_GrSurfaceOrigin);
342 image = SkImage::MakePromiseYUVATexture(
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 = SkImage::MakePromiseTexture(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