blob: 6d9b318c7cd3b0334dffa211e4b7143bb8d2cc86 [file] [log] [blame]
Robert Phillips96601082018-05-29 16:13:26 -04001/*
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 "DDLPromiseImageHelper.h"
9
Robert Phillips96601082018-05-29 16:13:26 -040010#include "GrContext.h"
11#include "GrContextPriv.h"
12#include "GrGpu.h"
Robert Phillipse8e2bb12018-09-27 14:26:47 -040013#include "SkCachedData.h"
Robert Phillips96601082018-05-29 16:13:26 -040014#include "SkDeferredDisplayListRecorder.h"
Robert Phillipse8e2bb12018-09-27 14:26:47 -040015#include "SkImage_Base.h"
Robert Phillips66a97342018-10-04 09:10:29 -040016#include "SkYUVAIndex.h"
Jim Van Verthe24b5872018-10-29 16:26:02 -040017#include "SkYUVASizeInfo.h"
Robert Phillips96601082018-05-29 16:13:26 -040018
19DDLPromiseImageHelper::PromiseImageCallbackContext::~PromiseImageCallbackContext() {
Brian Salomoncdd8a0a2019-01-10 12:09:52 -050020 SkASSERT(fDoneCnt == fNumImages);
21 SkASSERT(!fUnreleasedFulfills);
22 SkASSERT(fTotalReleases == fTotalFulfills);
23 SkASSERT(!fTotalFulfills || fDoneCnt);
Robert Phillips96601082018-05-29 16:13:26 -040024
Brian Salomon3f4cd772019-01-11 16:03:19 -050025 if (fPromiseImageTexture) {
Robert Phillips9da87e02019-02-04 13:26:26 -050026 GrGpu* gpu = fContext->priv().getGpu();
Brian Salomon3f4cd772019-01-11 16:03:19 -050027 gpu->deleteTestingOnlyBackendTexture(fPromiseImageTexture->backendTexture());
Robert Phillips96601082018-05-29 16:13:26 -040028 }
29}
30
Brian Salomoncdd8a0a2019-01-10 12:09:52 -050031void DDLPromiseImageHelper::PromiseImageCallbackContext::setBackendTexture(
32 const GrBackendTexture& backendTexture) {
33 SkASSERT(!fUnreleasedFulfills);
Brian Salomon3f4cd772019-01-11 16:03:19 -050034 if (fPromiseImageTexture) {
Robert Phillips9da87e02019-02-04 13:26:26 -050035 GrGpu* gpu = fContext->priv().getGpu();
Brian Salomon3f4cd772019-01-11 16:03:19 -050036 gpu->deleteTestingOnlyBackendTexture(fPromiseImageTexture->backendTexture());
Brian Salomoncdd8a0a2019-01-10 12:09:52 -050037 }
Brian Salomon3f4cd772019-01-11 16:03:19 -050038 fPromiseImageTexture = SkPromiseImageTexture::Make(backendTexture);
Brian Salomoncdd8a0a2019-01-10 12:09:52 -050039}
40
Robert Phillips96601082018-05-29 16:13:26 -040041///////////////////////////////////////////////////////////////////////////////////////////////////
42
Robert Phillipse8e2bb12018-09-27 14:26:47 -040043DDLPromiseImageHelper::~DDLPromiseImageHelper() {}
44
Robert Phillips96601082018-05-29 16:13:26 -040045sk_sp<SkData> DDLPromiseImageHelper::deflateSKP(const SkPicture* inputPicture) {
46 SkSerialProcs procs;
47
48 procs.fImageCtx = this;
49 procs.fImageProc = [](SkImage* image, void* ctx) -> sk_sp<SkData> {
50 auto helper = static_cast<DDLPromiseImageHelper*>(ctx);
51
52 int id = helper->findOrDefineImage(image);
53 if (id >= 0) {
54 SkASSERT(helper->isValidID(id));
55 return SkData::MakeWithCopy(&id, sizeof(id));
56 }
57
58 return nullptr;
59 };
60
61 return inputPicture->serialize(&procs);
62}
63
Jim Van Verth60ac5d02018-12-06 13:11:53 -050064// needed until we have SkRG_88_ColorType;
65static GrBackendTexture create_yuva_texture(GrGpu* gpu, const SkPixmap& pm,
66 const SkYUVAIndex yuvaIndices[4], int texIndex) {
67 SkASSERT(texIndex >= 0 && texIndex <= 3);
68 int channelCount = 0;
69 for (int i = 0; i < SkYUVAIndex::kIndexCount; ++i) {
70 if (yuvaIndices[i].fIndex == texIndex) {
71 ++channelCount;
72 }
73 }
74 // Need to create an RG texture for two-channel planes
75 GrBackendTexture tex;
76 if (2 == channelCount) {
77 SkASSERT(kRGBA_8888_SkColorType == pm.colorType());
78 SkAutoTMalloc<char> pixels(2 * pm.width()*pm.height());
79 char* currPixel = pixels;
80 for (int y = 0; y < pm.height(); ++y) {
81 for (int x = 0; x < pm.width(); ++x) {
82 SkColor color = pm.getColor(x, y);
83 currPixel[0] = SkColorGetR(color);
84 currPixel[1] = SkColorGetG(color);
85 currPixel += 2;
86 }
87 }
88 tex = gpu->createTestingOnlyBackendTexture(
89 pixels,
90 pm.width(),
91 pm.height(),
92 GrColorType::kRG_88,
93 false,
94 GrMipMapped::kNo,
95 2 * pm.width());
96 } else {
97 tex = gpu->createTestingOnlyBackendTexture(
98 pm.addr(),
99 pm.width(),
100 pm.height(),
101 pm.colorType(),
102 false,
103 GrMipMapped::kNo,
104 pm.rowBytes());
105 }
106 return tex;
107}
108
Robert Phillips96601082018-05-29 16:13:26 -0400109void DDLPromiseImageHelper::uploadAllToGPU(GrContext* context) {
Robert Phillips9da87e02019-02-04 13:26:26 -0500110 GrGpu* gpu = context->priv().getGpu();
Robert Phillips96601082018-05-29 16:13:26 -0400111 SkASSERT(gpu);
112
Brian Salomon426ba462019-01-10 16:33:06 +0000113 for (int i = 0; i < fImageInfo.count(); ++i) {
Robert Phillips96601082018-05-29 16:13:26 -0400114 const PromiseImageInfo& info = fImageInfo[i];
115
116 // DDL TODO: how can we tell if we need mipmapping!
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400117 if (info.isYUV()) {
Jim Van Verth8f11e432018-10-18 14:36:59 -0400118 int numPixmaps;
119 SkAssertResult(SkYUVAIndex::AreValidIndices(info.yuvaIndices(), &numPixmaps));
120 for (int j = 0; j < numPixmaps; ++j) {
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400121 const SkPixmap& yuvPixmap = info.yuvPixmap(j);
Robert Phillips96601082018-05-29 16:13:26 -0400122
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400123 sk_sp<PromiseImageCallbackContext> callbackContext(
124 new PromiseImageCallbackContext(context));
Jim Van Verth60ac5d02018-12-06 13:11:53 -0500125
126 callbackContext->setBackendTexture(create_yuva_texture(gpu, yuvPixmap,
127 info.yuvaIndices(), j));
Brian Salomon3f4cd772019-01-11 16:03:19 -0500128 SkASSERT(callbackContext->promiseImageTexture());
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400129
130 fImageInfo[i].setCallbackContext(j, std::move(callbackContext));
131 }
132 } else {
133 sk_sp<PromiseImageCallbackContext> callbackContext(
134 new PromiseImageCallbackContext(context));
135
136 const SkBitmap& bm = info.normalBitmap();
137
138 callbackContext->setBackendTexture(gpu->createTestingOnlyBackendTexture(
139 bm.getPixels(),
140 bm.width(),
141 bm.height(),
142 bm.colorType(),
143 false, GrMipMapped::kNo,
144 bm.rowBytes()));
145 // The GMs sometimes request too large an image
146 //SkAssertResult(callbackContext->backendTexture().isValid());
147
148 fImageInfo[i].setCallbackContext(0, std::move(callbackContext));
149 }
Brian Salomoncdd8a0a2019-01-10 12:09:52 -0500150 }
151}
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400152
Brian Salomoncdd8a0a2019-01-10 12:09:52 -0500153void DDLPromiseImageHelper::replaceEveryOtherPromiseTexture(GrContext* context) {
Robert Phillips9da87e02019-02-04 13:26:26 -0500154 GrGpu* gpu = context->priv().getGpu();
Brian Salomoncdd8a0a2019-01-10 12:09:52 -0500155 SkASSERT(gpu);
156
157 for (int i = 0; i < fImageInfo.count(); i += 2) {
158 PromiseImageInfo& info = fImageInfo[i];
159
160 // DDL TODO: how can we tell if we need mipmapping!
161 if (info.isYUV()) {
162 int numPixmaps;
163 SkAssertResult(SkYUVAIndex::AreValidIndices(info.yuvaIndices(), &numPixmaps));
164 for (int j = 0; j < numPixmaps; ++j) {
165 const SkPixmap& yuvPixmap = info.yuvPixmap(j);
166 info.callbackContext(j)->setBackendTexture(
167 create_yuva_texture(gpu, yuvPixmap, info.yuvaIndices(), j));
Brian Salomon3f4cd772019-01-11 16:03:19 -0500168 SkASSERT(info.callbackContext(j)->promiseImageTexture());
Brian Salomoncdd8a0a2019-01-10 12:09:52 -0500169 }
170 } else {
171 const SkBitmap& bm = info.normalBitmap();
172 info.callbackContext(0)->setBackendTexture(gpu->createTestingOnlyBackendTexture(
173 bm.getPixels(), bm.width(), bm.height(), bm.colorType(), false,
174 GrMipMapped::kNo, bm.rowBytes()));
175 // The GMs sometimes request too large an image
176 // SkAssertResult(callbackContext->backendTexture().isValid());
177 }
Robert Phillips96601082018-05-29 16:13:26 -0400178 }
179}
180
181sk_sp<SkPicture> DDLPromiseImageHelper::reinflateSKP(
182 SkDeferredDisplayListRecorder* recorder,
183 SkData* compressedPictureData,
184 SkTArray<sk_sp<SkImage>>* promiseImages) const {
185 PerRecorderContext perRecorderContext { recorder, this, promiseImages };
186
187 SkDeserialProcs procs;
188 procs.fImageCtx = (void*) &perRecorderContext;
189 procs.fImageProc = PromiseImageCreator;
190
191 return SkPicture::MakeFromData(compressedPictureData, &procs);
192}
193
194// This generates promise images to replace the indices in the compressed picture. This
195// reconstitution is performed separately in each thread so we end up with multiple
196// promise images referring to the same GrBackendTexture.
197sk_sp<SkImage> DDLPromiseImageHelper::PromiseImageCreator(const void* rawData,
198 size_t length, void* ctxIn) {
199 PerRecorderContext* perRecorderContext = static_cast<PerRecorderContext*>(ctxIn);
200 const DDLPromiseImageHelper* helper = perRecorderContext->fHelper;
201 SkDeferredDisplayListRecorder* recorder = perRecorderContext->fRecorder;
202
203 SkASSERT(length == sizeof(int));
204
205 const int* indexPtr = static_cast<const int*>(rawData);
206 SkASSERT(helper->isValidID(*indexPtr));
207
208 const DDLPromiseImageHelper::PromiseImageInfo& curImage = helper->getInfo(*indexPtr);
209
Brian Salomon3f4cd772019-01-11 16:03:19 -0500210 if (!curImage.promiseTexture(0)) {
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400211 SkASSERT(!curImage.isYUV());
Robert Phillips96601082018-05-29 16:13:26 -0400212 // We weren't able to make a backend texture for this SkImage. In this case we create
213 // a separate bitmap-backed image for each thread.
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400214 SkASSERT(curImage.normalBitmap().isImmutable());
215 return SkImage::MakeFromBitmap(curImage.normalBitmap());
Robert Phillips96601082018-05-29 16:13:26 -0400216 }
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400217 SkASSERT(curImage.index() == *indexPtr);
Robert Phillips96601082018-05-29 16:13:26 -0400218
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400219 sk_sp<SkImage> image;
220 if (curImage.isYUV()) {
Jim Van Verthe24b5872018-10-29 16:26:02 -0400221 GrBackendFormat backendFormats[SkYUVASizeInfo::kMaxCount];
222 void* contexts[SkYUVASizeInfo::kMaxCount] = { nullptr, nullptr, nullptr, nullptr };
223 SkISize sizes[SkYUVASizeInfo::kMaxCount];
Jim Van Verth8f11e432018-10-18 14:36:59 -0400224 // TODO: store this value somewhere?
225 int textureCount;
226 SkAssertResult(SkYUVAIndex::AreValidIndices(curImage.yuvaIndices(), &textureCount));
227 for (int i = 0; i < textureCount; ++i) {
Brian Salomon3f4cd772019-01-11 16:03:19 -0500228 const GrBackendTexture& backendTex = curImage.promiseTexture(i)->backendTexture();
Brian Salomonf391d0f2018-12-14 09:18:50 -0500229 backendFormats[i] = backendTex.getBackendFormat();
230 SkASSERT(backendFormats[i].isValid());
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400231 contexts[i] = curImage.refCallbackContext(i).release();
Jim Van Verthf9f07352018-10-24 10:32:20 -0400232 sizes[i].set(curImage.yuvPixmap(i).width(), curImage.yuvPixmap(i).height());
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400233 }
Jim Van Verthe24b5872018-10-29 16:26:02 -0400234 for (int i = textureCount; i < SkYUVASizeInfo::kMaxCount; ++i) {
Jim Van Verthf9f07352018-10-24 10:32:20 -0400235 sizes[i] = SkISize::MakeEmpty();
Jim Van Verth8f11e432018-10-18 14:36:59 -0400236 }
Jim Van Verthf99a6742018-10-18 16:13:18 +0000237
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400238 image = recorder->makeYUVAPromiseTexture(curImage.yuvColorSpace(),
239 backendFormats,
Jim Van Verthf9f07352018-10-24 10:32:20 -0400240 sizes,
Jim Van Verth8f11e432018-10-18 14:36:59 -0400241 curImage.yuvaIndices(),
Jim Van Verth21bd60d2018-10-12 15:00:20 -0400242 curImage.overallWidth(),
243 curImage.overallHeight(),
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400244 GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin,
245 curImage.refOverallColorSpace(),
246 DDLPromiseImageHelper::PromiseImageFulfillProc,
247 DDLPromiseImageHelper::PromiseImageReleaseProc,
248 DDLPromiseImageHelper::PromiseImageDoneProc,
Brian Salomonf55e8d52019-01-30 17:28:20 -0500249 contexts,
250 helper->fDelayReleaseCallback);
Brian Salomoncdd8a0a2019-01-10 12:09:52 -0500251 for (int i = 0; i < textureCount; ++i) {
252 curImage.callbackContext(i)->wasAddedToImage();
253 }
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400254 } else {
Brian Salomon3f4cd772019-01-11 16:03:19 -0500255 const GrBackendTexture& backendTex = curImage.promiseTexture(0)->backendTexture();
Brian Salomonf391d0f2018-12-14 09:18:50 -0500256 GrBackendFormat backendFormat = backendTex.getBackendFormat();
257 SkASSERT(backendFormat.isValid());
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400258
259 // Each DDL recorder gets its own ref on the promise callback context for the
260 // promise images it creates.
261 // DDL TODO: sort out mipmapping
262 image = recorder->makePromiseTexture(backendFormat,
263 curImage.overallWidth(),
264 curImage.overallHeight(),
265 GrMipMapped::kNo,
266 GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin,
267 curImage.overallColorType(),
268 curImage.overallAlphaType(),
269 curImage.refOverallColorSpace(),
270 DDLPromiseImageHelper::PromiseImageFulfillProc,
271 DDLPromiseImageHelper::PromiseImageReleaseProc,
272 DDLPromiseImageHelper::PromiseImageDoneProc,
Brian Salomonf55e8d52019-01-30 17:28:20 -0500273 (void*)curImage.refCallbackContext(0).release(),
274 helper->fDelayReleaseCallback);
Brian Salomoncdd8a0a2019-01-10 12:09:52 -0500275 curImage.callbackContext(0)->wasAddedToImage();
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400276 }
Robert Phillips96601082018-05-29 16:13:26 -0400277 perRecorderContext->fPromiseImages->push_back(image);
278 SkASSERT(image);
279 return image;
280}
281
282int DDLPromiseImageHelper::findImage(SkImage* image) const {
283 for (int i = 0; i < fImageInfo.count(); ++i) {
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400284 if (fImageInfo[i].originalUniqueID() == image->uniqueID()) { // trying to dedup here
285 SkASSERT(fImageInfo[i].index() == i);
286 SkASSERT(this->isValidID(i) && this->isValidID(fImageInfo[i].index()));
Robert Phillips96601082018-05-29 16:13:26 -0400287 return i;
288 }
289 }
290 return -1;
291}
292
293int DDLPromiseImageHelper::addImage(SkImage* image) {
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400294 SkImage_Base* ib = as_IB(image);
Robert Phillips96601082018-05-29 16:13:26 -0400295
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400296 SkImageInfo overallII = SkImageInfo::Make(image->width(), image->height(),
297 image->colorType(), image->alphaType(),
298 image->refColorSpace());
Robert Phillips96601082018-05-29 16:13:26 -0400299
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400300 PromiseImageInfo& newImageInfo = fImageInfo.emplace_back(fImageInfo.count(),
301 image->uniqueID(),
302 overallII);
Robert Phillips96601082018-05-29 16:13:26 -0400303
Jim Van Verthe24b5872018-10-29 16:26:02 -0400304 SkYUVASizeInfo yuvaSizeInfo;
Jim Van Verth8f11e432018-10-18 14:36:59 -0400305 SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount];
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400306 SkYUVColorSpace yuvColorSpace;
Jim Van Verthe24b5872018-10-29 16:26:02 -0400307 const void* planes[SkYUVASizeInfo::kMaxCount];
Jim Van Verth8f11e432018-10-18 14:36:59 -0400308 sk_sp<SkCachedData> yuvData = ib->getPlanes(&yuvaSizeInfo, yuvaIndices, &yuvColorSpace, planes);
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400309 if (yuvData) {
Jim Van Verth8f11e432018-10-18 14:36:59 -0400310 newImageInfo.setYUVData(std::move(yuvData), yuvaIndices, yuvColorSpace);
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400311
Jim Van Verthb7f0b9c2018-10-22 14:12:03 -0400312 // determine colortypes from index data
313 // for testing we only ever use A8 or RGBA8888
Jim Van Verthe24b5872018-10-29 16:26:02 -0400314 SkColorType colorTypes[SkYUVASizeInfo::kMaxCount] = {
Jim Van Verthb7f0b9c2018-10-22 14:12:03 -0400315 kUnknown_SkColorType, kUnknown_SkColorType,
316 kUnknown_SkColorType, kUnknown_SkColorType
317 };
318 for (int yuvIndex = 0; yuvIndex < SkYUVAIndex::kIndexCount; ++yuvIndex) {
319 int texIdx = yuvaIndices[yuvIndex].fIndex;
320 if (texIdx < 0) {
321 SkASSERT(SkYUVAIndex::kA_Index == yuvIndex);
322 continue;
323 }
324 if (kUnknown_SkColorType == colorTypes[texIdx]) {
325 colorTypes[texIdx] = kAlpha_8_SkColorType;
326 } else {
327 colorTypes[texIdx] = kRGBA_8888_SkColorType;
328 }
329 }
330
Jim Van Verthe24b5872018-10-29 16:26:02 -0400331 for (int i = 0; i < SkYUVASizeInfo::kMaxCount; ++i) {
Jim Van Verthb7f0b9c2018-10-22 14:12:03 -0400332 if (yuvaSizeInfo.fSizes[i].isEmpty()) {
333 SkASSERT(!yuvaSizeInfo.fWidthBytes[i] && kUnknown_SkColorType == colorTypes[i]);
Jim Van Verth8f11e432018-10-18 14:36:59 -0400334 continue;
335 }
336
337 SkImageInfo planeII = SkImageInfo::Make(yuvaSizeInfo.fSizes[i].fWidth,
338 yuvaSizeInfo.fSizes[i].fHeight,
Jim Van Verthb7f0b9c2018-10-22 14:12:03 -0400339 colorTypes[i],
Jim Van Verth8f11e432018-10-18 14:36:59 -0400340 kUnpremul_SkAlphaType);
341 newImageInfo.addYUVPlane(i, planeII, planes[i], yuvaSizeInfo.fWidthBytes[i]);
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400342 }
343 } else {
344 sk_sp<SkImage> rasterImage = image->makeRasterImage(); // force decoding of lazy images
345
346 SkBitmap tmp;
347 tmp.allocPixels(overallII);
348
349 if (!rasterImage->readPixels(tmp.pixmap(), 0, 0)) {
350 return -1;
351 }
352
353 tmp.setImmutable();
354 newImageInfo.setNormalBitmap(tmp);
Robert Phillips96601082018-05-29 16:13:26 -0400355 }
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400356 // In either case newImageInfo's PromiseImageCallbackContext is filled in by uploadAllToGPU
Robert Phillips96601082018-05-29 16:13:26 -0400357
358 return fImageInfo.count()-1;
359}
360
361int DDLPromiseImageHelper::findOrDefineImage(SkImage* image) {
362 int preExistingID = this->findImage(image);
363 if (preExistingID >= 0) {
364 SkASSERT(this->isValidID(preExistingID));
365 return preExistingID;
366 }
367
368 int newID = this->addImage(image);
369 SkASSERT(this->isValidID(newID));
370 return newID;
371}