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