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 "DDLPromiseImageHelper.h"
9
10 #include "GrContext.h"
11 #include "GrContextPriv.h"
12 #include "GrGpu.h"
13 #include "SkCachedData.h"
14 #include "SkDeferredDisplayListRecorder.h"
15 #include "SkImage_Base.h"
16 #include "SkYUVAIndex.h"
17 #include "SkYUVASizeInfo.h"
18
~PromiseImageCallbackContext()19 DDLPromiseImageHelper::PromiseImageCallbackContext::~PromiseImageCallbackContext() {
20 SkASSERT(fDoneCnt == fNumImages);
21 SkASSERT(!fUnreleasedFulfills);
22 SkASSERT(fTotalReleases == fTotalFulfills);
23 SkASSERT(!fTotalFulfills || fDoneCnt);
24
25 if (fPromiseImageTexture) {
26 GrGpu* gpu = fContext->contextPriv().getGpu();
27 gpu->deleteTestingOnlyBackendTexture(fPromiseImageTexture->backendTexture());
28 }
29 }
30
setBackendTexture(const GrBackendTexture & backendTexture)31 void DDLPromiseImageHelper::PromiseImageCallbackContext::setBackendTexture(
32 const GrBackendTexture& backendTexture) {
33 SkASSERT(!fUnreleasedFulfills);
34 if (fPromiseImageTexture) {
35 GrGpu* gpu = fContext->contextPriv().getGpu();
36 gpu->deleteTestingOnlyBackendTexture(fPromiseImageTexture->backendTexture());
37 }
38 fPromiseImageTexture = SkPromiseImageTexture::Make(backendTexture);
39 }
40
41 ///////////////////////////////////////////////////////////////////////////////////////////////////
42
~DDLPromiseImageHelper()43 DDLPromiseImageHelper::~DDLPromiseImageHelper() {}
44
deflateSKP(const SkPicture * inputPicture)45 sk_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
64 // needed until we have SkRG_88_ColorType;
create_yuva_texture(GrGpu * gpu,const SkPixmap & pm,const SkYUVAIndex yuvaIndices[4],int texIndex)65 static 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
uploadAllToGPU(GrContext * context)109 void DDLPromiseImageHelper::uploadAllToGPU(GrContext* context) {
110 GrGpu* gpu = context->contextPriv().getGpu();
111 SkASSERT(gpu);
112
113 for (int i = 0; i < fImageInfo.count(); ++i) {
114 const PromiseImageInfo& info = fImageInfo[i];
115
116 // DDL TODO: how can we tell if we need mipmapping!
117 if (info.isYUV()) {
118 int numPixmaps;
119 SkAssertResult(SkYUVAIndex::AreValidIndices(info.yuvaIndices(), &numPixmaps));
120 for (int j = 0; j < numPixmaps; ++j) {
121 const SkPixmap& yuvPixmap = info.yuvPixmap(j);
122
123 sk_sp<PromiseImageCallbackContext> callbackContext(
124 new PromiseImageCallbackContext(context));
125
126 callbackContext->setBackendTexture(create_yuva_texture(gpu, yuvPixmap,
127 info.yuvaIndices(), j));
128 SkASSERT(callbackContext->promiseImageTexture());
129
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 }
150 }
151 }
152
replaceEveryOtherPromiseTexture(GrContext * context)153 void DDLPromiseImageHelper::replaceEveryOtherPromiseTexture(GrContext* context) {
154 GrGpu* gpu = context->contextPriv().getGpu();
155 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));
168 SkASSERT(info.callbackContext(j)->promiseImageTexture());
169 }
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 }
178 }
179 }
180
reinflateSKP(SkDeferredDisplayListRecorder * recorder,SkData * compressedPictureData,SkTArray<sk_sp<SkImage>> * promiseImages) const181 sk_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.
PromiseImageCreator(const void * rawData,size_t length,void * ctxIn)197 sk_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
210 if (!curImage.promiseTexture(0)) {
211 SkASSERT(!curImage.isYUV());
212 // 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.
214 SkASSERT(curImage.normalBitmap().isImmutable());
215 return SkImage::MakeFromBitmap(curImage.normalBitmap());
216 }
217 SkASSERT(curImage.index() == *indexPtr);
218
219 sk_sp<SkImage> image;
220 if (curImage.isYUV()) {
221 GrBackendFormat backendFormats[SkYUVASizeInfo::kMaxCount];
222 void* contexts[SkYUVASizeInfo::kMaxCount] = { nullptr, nullptr, nullptr, nullptr };
223 SkISize sizes[SkYUVASizeInfo::kMaxCount];
224 // TODO: store this value somewhere?
225 int textureCount;
226 SkAssertResult(SkYUVAIndex::AreValidIndices(curImage.yuvaIndices(), &textureCount));
227 for (int i = 0; i < textureCount; ++i) {
228 const GrBackendTexture& backendTex = curImage.promiseTexture(i)->backendTexture();
229 backendFormats[i] = backendTex.getBackendFormat();
230 SkASSERT(backendFormats[i].isValid());
231 contexts[i] = curImage.refCallbackContext(i).release();
232 sizes[i].set(curImage.yuvPixmap(i).width(), curImage.yuvPixmap(i).height());
233 }
234 for (int i = textureCount; i < SkYUVASizeInfo::kMaxCount; ++i) {
235 sizes[i] = SkISize::MakeEmpty();
236 }
237
238 image = recorder->makeYUVAPromiseTexture(curImage.yuvColorSpace(),
239 backendFormats,
240 sizes,
241 curImage.yuvaIndices(),
242 curImage.overallWidth(),
243 curImage.overallHeight(),
244 GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin,
245 curImage.refOverallColorSpace(),
246 DDLPromiseImageHelper::PromiseImageFulfillProc,
247 DDLPromiseImageHelper::PromiseImageReleaseProc,
248 DDLPromiseImageHelper::PromiseImageDoneProc,
249 contexts,
250 helper->fDelayReleaseCallback);
251 for (int i = 0; i < textureCount; ++i) {
252 curImage.callbackContext(i)->wasAddedToImage();
253 }
254 } else {
255 const GrBackendTexture& backendTex = curImage.promiseTexture(0)->backendTexture();
256 GrBackendFormat backendFormat = backendTex.getBackendFormat();
257 SkASSERT(backendFormat.isValid());
258
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,
273 (void*)curImage.refCallbackContext(0).release(),
274 helper->fDelayReleaseCallback);
275 curImage.callbackContext(0)->wasAddedToImage();
276 }
277 perRecorderContext->fPromiseImages->push_back(image);
278 SkASSERT(image);
279 return image;
280 }
281
findImage(SkImage * image) const282 int DDLPromiseImageHelper::findImage(SkImage* image) const {
283 for (int i = 0; i < fImageInfo.count(); ++i) {
284 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()));
287 return i;
288 }
289 }
290 return -1;
291 }
292
addImage(SkImage * image)293 int DDLPromiseImageHelper::addImage(SkImage* image) {
294 SkImage_Base* ib = as_IB(image);
295
296 SkImageInfo overallII = SkImageInfo::Make(image->width(), image->height(),
297 image->colorType(), image->alphaType(),
298 image->refColorSpace());
299
300 PromiseImageInfo& newImageInfo = fImageInfo.emplace_back(fImageInfo.count(),
301 image->uniqueID(),
302 overallII);
303
304 SkYUVASizeInfo yuvaSizeInfo;
305 SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount];
306 SkYUVColorSpace yuvColorSpace;
307 const void* planes[SkYUVASizeInfo::kMaxCount];
308 sk_sp<SkCachedData> yuvData = ib->getPlanes(&yuvaSizeInfo, yuvaIndices, &yuvColorSpace, planes);
309 if (yuvData) {
310 newImageInfo.setYUVData(std::move(yuvData), yuvaIndices, yuvColorSpace);
311
312 // determine colortypes from index data
313 // for testing we only ever use A8 or RGBA8888
314 SkColorType colorTypes[SkYUVASizeInfo::kMaxCount] = {
315 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
331 for (int i = 0; i < SkYUVASizeInfo::kMaxCount; ++i) {
332 if (yuvaSizeInfo.fSizes[i].isEmpty()) {
333 SkASSERT(!yuvaSizeInfo.fWidthBytes[i] && kUnknown_SkColorType == colorTypes[i]);
334 continue;
335 }
336
337 SkImageInfo planeII = SkImageInfo::Make(yuvaSizeInfo.fSizes[i].fWidth,
338 yuvaSizeInfo.fSizes[i].fHeight,
339 colorTypes[i],
340 kUnpremul_SkAlphaType);
341 newImageInfo.addYUVPlane(i, planeII, planes[i], yuvaSizeInfo.fWidthBytes[i]);
342 }
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);
355 }
356 // In either case newImageInfo's PromiseImageCallbackContext is filled in by uploadAllToGPU
357
358 return fImageInfo.count()-1;
359 }
360
findOrDefineImage(SkImage * image)361 int 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 }
372