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