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 "SkImage_GpuYUVA.h"
17 #include "SkYUVAIndex.h"
18 #include "SkYUVASizeInfo.h"
19
~PromiseImageCallbackContext()20 DDLPromiseImageHelper::PromiseImageCallbackContext::~PromiseImageCallbackContext() {
21 SkASSERT(fDoneCnt == fNumImages);
22 SkASSERT(!fUnreleasedFulfills);
23 SkASSERT(fTotalReleases == fTotalFulfills);
24 SkASSERT(!fTotalFulfills || fDoneCnt);
25
26 if (fPromiseImageTexture) {
27 GrGpu* gpu = fContext->priv().getGpu();
28 gpu->deleteTestingOnlyBackendTexture(fPromiseImageTexture->backendTexture());
29 }
30 }
31
setBackendTexture(const GrBackendTexture & backendTexture)32 void DDLPromiseImageHelper::PromiseImageCallbackContext::setBackendTexture(
33 const GrBackendTexture& backendTexture) {
34 SkASSERT(!fPromiseImageTexture);
35 fPromiseImageTexture = SkPromiseImageTexture::Make(backendTexture);
36 }
37
38 ///////////////////////////////////////////////////////////////////////////////////////////////////
39
deflateSKP(const SkPicture * inputPicture)40 sk_sp<SkData> DDLPromiseImageHelper::deflateSKP(const SkPicture* inputPicture) {
41 SkSerialProcs procs;
42
43 procs.fImageCtx = this;
44 procs.fImageProc = [](SkImage* image, void* ctx) -> sk_sp<SkData> {
45 auto helper = static_cast<DDLPromiseImageHelper*>(ctx);
46
47 int id = helper->findOrDefineImage(image);
48 if (id >= 0) {
49 SkASSERT(helper->isValidID(id));
50 return SkData::MakeWithCopy(&id, sizeof(id));
51 }
52
53 return nullptr;
54 };
55
56 return inputPicture->serialize(&procs);
57 }
58
59 // needed until we have SkRG_88_ColorType;
create_yuva_texture(GrGpu * gpu,const SkPixmap & pm,const SkYUVAIndex yuvaIndices[4],int texIndex)60 static GrBackendTexture create_yuva_texture(GrGpu* gpu, const SkPixmap& pm,
61 const SkYUVAIndex yuvaIndices[4], int texIndex) {
62 SkASSERT(texIndex >= 0 && texIndex <= 3);
63 int channelCount = 0;
64 for (int i = 0; i < SkYUVAIndex::kIndexCount; ++i) {
65 if (yuvaIndices[i].fIndex == texIndex) {
66 ++channelCount;
67 }
68 }
69 // Need to create an RG texture for two-channel planes
70 GrBackendTexture tex;
71 if (2 == channelCount) {
72 SkASSERT(kRGBA_8888_SkColorType == pm.colorType());
73 SkAutoTMalloc<char> pixels(2 * pm.width()*pm.height());
74 char* currPixel = pixels;
75 for (int y = 0; y < pm.height(); ++y) {
76 for (int x = 0; x < pm.width(); ++x) {
77 SkColor color = pm.getColor(x, y);
78 currPixel[0] = SkColorGetR(color);
79 currPixel[1] = SkColorGetG(color);
80 currPixel += 2;
81 }
82 }
83 tex = gpu->createTestingOnlyBackendTexture(
84 pixels,
85 pm.width(),
86 pm.height(),
87 GrColorType::kRG_88,
88 false,
89 GrMipMapped::kNo,
90 2 * pm.width());
91 } else {
92 tex = gpu->createTestingOnlyBackendTexture(
93 pm.addr(),
94 pm.width(),
95 pm.height(),
96 pm.colorType(),
97 false,
98 GrMipMapped::kNo,
99 pm.rowBytes());
100 }
101 return tex;
102 }
103
uploadAllToGPU(GrContext * context)104 void DDLPromiseImageHelper::uploadAllToGPU(GrContext* context) {
105 GrGpu* gpu = context->priv().getGpu();
106 SkASSERT(gpu);
107
108 for (int i = 0; i < fImageInfo.count(); ++i) {
109 const PromiseImageInfo& info = fImageInfo[i];
110
111 // DDL TODO: how can we tell if we need mipmapping!
112 if (info.isYUV()) {
113 int numPixmaps;
114 SkAssertResult(SkYUVAIndex::AreValidIndices(info.yuvaIndices(), &numPixmaps));
115 for (int j = 0; j < numPixmaps; ++j) {
116 const SkPixmap& yuvPixmap = info.yuvPixmap(j);
117
118 sk_sp<PromiseImageCallbackContext> callbackContext(
119 new PromiseImageCallbackContext(context));
120
121 callbackContext->setBackendTexture(create_yuva_texture(gpu, yuvPixmap,
122 info.yuvaIndices(), j));
123 SkASSERT(callbackContext->promiseImageTexture());
124
125 fImageInfo[i].setCallbackContext(j, std::move(callbackContext));
126 }
127 } else {
128 sk_sp<PromiseImageCallbackContext> callbackContext(
129 new PromiseImageCallbackContext(context));
130
131 const SkBitmap& bm = info.normalBitmap();
132
133 callbackContext->setBackendTexture(gpu->createTestingOnlyBackendTexture(
134 bm.getPixels(),
135 bm.width(),
136 bm.height(),
137 bm.colorType(),
138 false, GrMipMapped::kNo,
139 bm.rowBytes()));
140 // The GMs sometimes request too large an image
141 //SkAssertResult(callbackContext->backendTexture().isValid());
142
143 fImageInfo[i].setCallbackContext(0, std::move(callbackContext));
144 }
145 }
146 }
147
reinflateSKP(SkDeferredDisplayListRecorder * recorder,SkData * compressedPictureData,SkTArray<sk_sp<SkImage>> * promiseImages) const148 sk_sp<SkPicture> DDLPromiseImageHelper::reinflateSKP(
149 SkDeferredDisplayListRecorder* recorder,
150 SkData* compressedPictureData,
151 SkTArray<sk_sp<SkImage>>* promiseImages) const {
152 PerRecorderContext perRecorderContext { recorder, this, promiseImages };
153
154 SkDeserialProcs procs;
155 procs.fImageCtx = (void*) &perRecorderContext;
156 procs.fImageProc = PromiseImageCreator;
157
158 return SkPicture::MakeFromData(compressedPictureData, &procs);
159 }
160
161 // This generates promise images to replace the indices in the compressed picture. This
162 // reconstitution is performed separately in each thread so we end up with multiple
163 // promise images referring to the same GrBackendTexture.
PromiseImageCreator(const void * rawData,size_t length,void * ctxIn)164 sk_sp<SkImage> DDLPromiseImageHelper::PromiseImageCreator(const void* rawData,
165 size_t length, void* ctxIn) {
166 PerRecorderContext* perRecorderContext = static_cast<PerRecorderContext*>(ctxIn);
167 const DDLPromiseImageHelper* helper = perRecorderContext->fHelper;
168 SkDeferredDisplayListRecorder* recorder = perRecorderContext->fRecorder;
169
170 SkASSERT(length == sizeof(int));
171
172 const int* indexPtr = static_cast<const int*>(rawData);
173 SkASSERT(helper->isValidID(*indexPtr));
174
175 const DDLPromiseImageHelper::PromiseImageInfo& curImage = helper->getInfo(*indexPtr);
176
177 if (!curImage.promiseTexture(0)) {
178 SkASSERT(!curImage.isYUV());
179 // We weren't able to make a backend texture for this SkImage. In this case we create
180 // a separate bitmap-backed image for each thread.
181 SkASSERT(curImage.normalBitmap().isImmutable());
182 return SkImage::MakeFromBitmap(curImage.normalBitmap());
183 }
184 SkASSERT(curImage.index() == *indexPtr);
185
186 sk_sp<SkImage> image;
187 if (curImage.isYUV()) {
188 GrBackendFormat backendFormats[SkYUVASizeInfo::kMaxCount];
189 void* contexts[SkYUVASizeInfo::kMaxCount] = { nullptr, nullptr, nullptr, nullptr };
190 SkISize sizes[SkYUVASizeInfo::kMaxCount];
191 // TODO: store this value somewhere?
192 int textureCount;
193 SkAssertResult(SkYUVAIndex::AreValidIndices(curImage.yuvaIndices(), &textureCount));
194 for (int i = 0; i < textureCount; ++i) {
195 const GrBackendTexture& backendTex = curImage.promiseTexture(i)->backendTexture();
196 backendFormats[i] = backendTex.getBackendFormat();
197 SkASSERT(backendFormats[i].isValid());
198 contexts[i] = curImage.refCallbackContext(i).release();
199 sizes[i].set(curImage.yuvPixmap(i).width(), curImage.yuvPixmap(i).height());
200 }
201 for (int i = textureCount; i < SkYUVASizeInfo::kMaxCount; ++i) {
202 sizes[i] = SkISize::MakeEmpty();
203 }
204
205 image = recorder->makeYUVAPromiseTexture(curImage.yuvColorSpace(),
206 backendFormats,
207 sizes,
208 curImage.yuvaIndices(),
209 curImage.overallWidth(),
210 curImage.overallHeight(),
211 GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin,
212 curImage.refOverallColorSpace(),
213 DDLPromiseImageHelper::PromiseImageFulfillProc,
214 DDLPromiseImageHelper::PromiseImageReleaseProc,
215 DDLPromiseImageHelper::PromiseImageDoneProc,
216 contexts);
217 for (int i = 0; i < textureCount; ++i) {
218 curImage.callbackContext(i)->wasAddedToImage();
219 }
220
221 #ifdef SK_DEBUG
222 {
223 // By the peekProxy contract this image should not have a single backing proxy so
224 // should return null. The call should also not trigger the conversion to RGBA.
225 SkImage_GpuYUVA* yuva = reinterpret_cast<SkImage_GpuYUVA*>(image.get());
226 SkASSERT(!yuva->peekProxy());
227 SkASSERT(!yuva->peekProxy()); // the first call didn't force a conversion to RGBA
228 }
229 #endif
230 } else {
231 const GrBackendTexture& backendTex = curImage.promiseTexture(0)->backendTexture();
232 GrBackendFormat backendFormat = backendTex.getBackendFormat();
233 SkASSERT(backendFormat.isValid());
234
235 // Each DDL recorder gets its own ref on the promise callback context for the
236 // promise images it creates.
237 // DDL TODO: sort out mipmapping
238 image = recorder->makePromiseTexture(backendFormat,
239 curImage.overallWidth(),
240 curImage.overallHeight(),
241 GrMipMapped::kNo,
242 GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin,
243 curImage.overallColorType(),
244 curImage.overallAlphaType(),
245 curImage.refOverallColorSpace(),
246 DDLPromiseImageHelper::PromiseImageFulfillProc,
247 DDLPromiseImageHelper::PromiseImageReleaseProc,
248 DDLPromiseImageHelper::PromiseImageDoneProc,
249 (void*)curImage.refCallbackContext(0).release());
250 curImage.callbackContext(0)->wasAddedToImage();
251 }
252 perRecorderContext->fPromiseImages->push_back(image);
253 SkASSERT(image);
254 return image;
255 }
256
findImage(SkImage * image) const257 int DDLPromiseImageHelper::findImage(SkImage* image) const {
258 for (int i = 0; i < fImageInfo.count(); ++i) {
259 if (fImageInfo[i].originalUniqueID() == image->uniqueID()) { // trying to dedup here
260 SkASSERT(fImageInfo[i].index() == i);
261 SkASSERT(this->isValidID(i) && this->isValidID(fImageInfo[i].index()));
262 return i;
263 }
264 }
265 return -1;
266 }
267
addImage(SkImage * image)268 int DDLPromiseImageHelper::addImage(SkImage* image) {
269 SkImage_Base* ib = as_IB(image);
270
271 SkImageInfo overallII = SkImageInfo::Make(image->width(), image->height(),
272 image->colorType(), image->alphaType(),
273 image->refColorSpace());
274
275 PromiseImageInfo& newImageInfo = fImageInfo.emplace_back(fImageInfo.count(),
276 image->uniqueID(),
277 overallII);
278
279 SkYUVASizeInfo yuvaSizeInfo;
280 SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount];
281 SkYUVColorSpace yuvColorSpace;
282 const void* planes[SkYUVASizeInfo::kMaxCount];
283 sk_sp<SkCachedData> yuvData = ib->getPlanes(&yuvaSizeInfo, yuvaIndices, &yuvColorSpace, planes);
284 if (yuvData) {
285 newImageInfo.setYUVData(std::move(yuvData), yuvaIndices, yuvColorSpace);
286
287 // determine colortypes from index data
288 // for testing we only ever use A8 or RGBA8888
289 SkColorType colorTypes[SkYUVASizeInfo::kMaxCount] = {
290 kUnknown_SkColorType, kUnknown_SkColorType,
291 kUnknown_SkColorType, kUnknown_SkColorType
292 };
293 for (int yuvIndex = 0; yuvIndex < SkYUVAIndex::kIndexCount; ++yuvIndex) {
294 int texIdx = yuvaIndices[yuvIndex].fIndex;
295 if (texIdx < 0) {
296 SkASSERT(SkYUVAIndex::kA_Index == yuvIndex);
297 continue;
298 }
299 if (kUnknown_SkColorType == colorTypes[texIdx]) {
300 colorTypes[texIdx] = kAlpha_8_SkColorType;
301 } else {
302 colorTypes[texIdx] = kRGBA_8888_SkColorType;
303 }
304 }
305
306 for (int i = 0; i < SkYUVASizeInfo::kMaxCount; ++i) {
307 if (yuvaSizeInfo.fSizes[i].isEmpty()) {
308 SkASSERT(!yuvaSizeInfo.fWidthBytes[i] && kUnknown_SkColorType == colorTypes[i]);
309 continue;
310 }
311
312 SkImageInfo planeII = SkImageInfo::Make(yuvaSizeInfo.fSizes[i].fWidth,
313 yuvaSizeInfo.fSizes[i].fHeight,
314 colorTypes[i],
315 kUnpremul_SkAlphaType);
316 newImageInfo.addYUVPlane(i, planeII, planes[i], yuvaSizeInfo.fWidthBytes[i]);
317 }
318 } else {
319 sk_sp<SkImage> rasterImage = image->makeRasterImage(); // force decoding of lazy images
320
321 SkBitmap tmp;
322 tmp.allocPixels(overallII);
323
324 if (!rasterImage->readPixels(tmp.pixmap(), 0, 0)) {
325 return -1;
326 }
327
328 tmp.setImmutable();
329 newImageInfo.setNormalBitmap(tmp);
330 }
331 // In either case newImageInfo's PromiseImageCallbackContext is filled in by uploadAllToGPU
332
333 return fImageInfo.count()-1;
334 }
335
findOrDefineImage(SkImage * image)336 int DDLPromiseImageHelper::findOrDefineImage(SkImage* image) {
337 int preExistingID = this->findImage(image);
338 if (preExistingID >= 0) {
339 SkASSERT(this->isValidID(preExistingID));
340 return preExistingID;
341 }
342
343 int newID = this->addImage(image);
344 SkASSERT(this->isValidID(newID));
345 return newID;
346 }
347