blob: 77db2f18b9964eacb3aaa333e50d7f2468361264 [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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "tools/DDLPromiseImageHelper.h"
Robert Phillips96601082018-05-29 16:13:26 -04009
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#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"
Robert Phillips96601082018-05-29 16:13:26 -040019
20DDLPromiseImageHelper::PromiseImageCallbackContext::~PromiseImageCallbackContext() {
Brian Salomoncdd8a0a2019-01-10 12:09:52 -050021 SkASSERT(fDoneCnt == fNumImages);
22 SkASSERT(!fUnreleasedFulfills);
23 SkASSERT(fTotalReleases == fTotalFulfills);
24 SkASSERT(!fTotalFulfills || fDoneCnt);
Robert Phillips96601082018-05-29 16:13:26 -040025
Brian Salomon3f4cd772019-01-11 16:03:19 -050026 if (fPromiseImageTexture) {
Robert Phillips9da87e02019-02-04 13:26:26 -050027 GrGpu* gpu = fContext->priv().getGpu();
Brian Salomon3f4cd772019-01-11 16:03:19 -050028 gpu->deleteTestingOnlyBackendTexture(fPromiseImageTexture->backendTexture());
Robert Phillips96601082018-05-29 16:13:26 -040029 }
30}
31
Brian Salomoncdd8a0a2019-01-10 12:09:52 -050032void DDLPromiseImageHelper::PromiseImageCallbackContext::setBackendTexture(
33 const GrBackendTexture& backendTexture) {
Brian Salomon7d88f312019-02-28 10:03:03 -050034 SkASSERT(!fPromiseImageTexture);
Brian Salomon3f4cd772019-01-11 16:03:19 -050035 fPromiseImageTexture = SkPromiseImageTexture::Make(backendTexture);
Brian Salomoncdd8a0a2019-01-10 12:09:52 -050036}
37
Robert Phillips96601082018-05-29 16:13:26 -040038///////////////////////////////////////////////////////////////////////////////////////////////////
39
40sk_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
Jim Van Verth60ac5d02018-12-06 13:11:53 -050059// needed until we have SkRG_88_ColorType;
60static 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
Robert Phillips96601082018-05-29 16:13:26 -0400104void DDLPromiseImageHelper::uploadAllToGPU(GrContext* context) {
Robert Phillips9da87e02019-02-04 13:26:26 -0500105 GrGpu* gpu = context->priv().getGpu();
Robert Phillips96601082018-05-29 16:13:26 -0400106 SkASSERT(gpu);
107
Brian Salomon426ba462019-01-10 16:33:06 +0000108 for (int i = 0; i < fImageInfo.count(); ++i) {
Robert Phillips96601082018-05-29 16:13:26 -0400109 const PromiseImageInfo& info = fImageInfo[i];
110
111 // DDL TODO: how can we tell if we need mipmapping!
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400112 if (info.isYUV()) {
Jim Van Verth8f11e432018-10-18 14:36:59 -0400113 int numPixmaps;
114 SkAssertResult(SkYUVAIndex::AreValidIndices(info.yuvaIndices(), &numPixmaps));
115 for (int j = 0; j < numPixmaps; ++j) {
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400116 const SkPixmap& yuvPixmap = info.yuvPixmap(j);
Robert Phillips96601082018-05-29 16:13:26 -0400117
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400118 sk_sp<PromiseImageCallbackContext> callbackContext(
119 new PromiseImageCallbackContext(context));
Jim Van Verth60ac5d02018-12-06 13:11:53 -0500120
121 callbackContext->setBackendTexture(create_yuva_texture(gpu, yuvPixmap,
122 info.yuvaIndices(), j));
Brian Salomon3f4cd772019-01-11 16:03:19 -0500123 SkASSERT(callbackContext->promiseImageTexture());
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400124
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 }
Brian Salomoncdd8a0a2019-01-10 12:09:52 -0500145 }
146}
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400147
Robert Phillips96601082018-05-29 16:13:26 -0400148sk_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.
164sk_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
Brian Salomon3f4cd772019-01-11 16:03:19 -0500177 if (!curImage.promiseTexture(0)) {
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400178 SkASSERT(!curImage.isYUV());
Robert Phillips96601082018-05-29 16:13:26 -0400179 // 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.
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400181 SkASSERT(curImage.normalBitmap().isImmutable());
182 return SkImage::MakeFromBitmap(curImage.normalBitmap());
Robert Phillips96601082018-05-29 16:13:26 -0400183 }
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400184 SkASSERT(curImage.index() == *indexPtr);
Robert Phillips96601082018-05-29 16:13:26 -0400185
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400186 sk_sp<SkImage> image;
187 if (curImage.isYUV()) {
Jim Van Verthe24b5872018-10-29 16:26:02 -0400188 GrBackendFormat backendFormats[SkYUVASizeInfo::kMaxCount];
189 void* contexts[SkYUVASizeInfo::kMaxCount] = { nullptr, nullptr, nullptr, nullptr };
190 SkISize sizes[SkYUVASizeInfo::kMaxCount];
Jim Van Verth8f11e432018-10-18 14:36:59 -0400191 // TODO: store this value somewhere?
192 int textureCount;
193 SkAssertResult(SkYUVAIndex::AreValidIndices(curImage.yuvaIndices(), &textureCount));
194 for (int i = 0; i < textureCount; ++i) {
Brian Salomon3f4cd772019-01-11 16:03:19 -0500195 const GrBackendTexture& backendTex = curImage.promiseTexture(i)->backendTexture();
Brian Salomonf391d0f2018-12-14 09:18:50 -0500196 backendFormats[i] = backendTex.getBackendFormat();
197 SkASSERT(backendFormats[i].isValid());
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400198 contexts[i] = curImage.refCallbackContext(i).release();
Jim Van Verthf9f07352018-10-24 10:32:20 -0400199 sizes[i].set(curImage.yuvPixmap(i).width(), curImage.yuvPixmap(i).height());
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400200 }
Jim Van Verthe24b5872018-10-29 16:26:02 -0400201 for (int i = textureCount; i < SkYUVASizeInfo::kMaxCount; ++i) {
Jim Van Verthf9f07352018-10-24 10:32:20 -0400202 sizes[i] = SkISize::MakeEmpty();
Jim Van Verth8f11e432018-10-18 14:36:59 -0400203 }
Jim Van Verthf99a6742018-10-18 16:13:18 +0000204
Brian Salomon0cc57542019-03-08 13:28:46 -0500205 image = recorder->makeYUVAPromiseTexture(
206 curImage.yuvColorSpace(),
207 backendFormats,
208 sizes,
209 curImage.yuvaIndices(),
210 curImage.overallWidth(),
211 curImage.overallHeight(),
212 GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin,
213 curImage.refOverallColorSpace(),
214 DDLPromiseImageHelper::PromiseImageFulfillProc,
215 DDLPromiseImageHelper::PromiseImageReleaseProc,
216 DDLPromiseImageHelper::PromiseImageDoneProc,
217 contexts,
218 SkDeferredDisplayListRecorder::PromiseImageApiVersion::kNew);
Brian Salomoncdd8a0a2019-01-10 12:09:52 -0500219 for (int i = 0; i < textureCount; ++i) {
220 curImage.callbackContext(i)->wasAddedToImage();
221 }
Robert Phillips193c4212019-03-04 12:18:53 -0500222
223#ifdef SK_DEBUG
224 {
225 // By the peekProxy contract this image should not have a single backing proxy so
226 // should return null. The call should also not trigger the conversion to RGBA.
227 SkImage_GpuYUVA* yuva = reinterpret_cast<SkImage_GpuYUVA*>(image.get());
228 SkASSERT(!yuva->peekProxy());
229 SkASSERT(!yuva->peekProxy()); // the first call didn't force a conversion to RGBA
230 }
231#endif
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400232 } else {
Brian Salomon3f4cd772019-01-11 16:03:19 -0500233 const GrBackendTexture& backendTex = curImage.promiseTexture(0)->backendTexture();
Brian Salomonf391d0f2018-12-14 09:18:50 -0500234 GrBackendFormat backendFormat = backendTex.getBackendFormat();
235 SkASSERT(backendFormat.isValid());
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400236
237 // Each DDL recorder gets its own ref on the promise callback context for the
238 // promise images it creates.
239 // DDL TODO: sort out mipmapping
Brian Salomon0cc57542019-03-08 13:28:46 -0500240 image = recorder->makePromiseTexture(
241 backendFormat,
242 curImage.overallWidth(),
243 curImage.overallHeight(),
244 GrMipMapped::kNo,
245 GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin,
246 curImage.overallColorType(),
247 curImage.overallAlphaType(),
248 curImage.refOverallColorSpace(),
249 DDLPromiseImageHelper::PromiseImageFulfillProc,
250 DDLPromiseImageHelper::PromiseImageReleaseProc,
251 DDLPromiseImageHelper::PromiseImageDoneProc,
252 (void*)curImage.refCallbackContext(0).release(),
253 SkDeferredDisplayListRecorder::PromiseImageApiVersion::kNew);
Brian Salomoncdd8a0a2019-01-10 12:09:52 -0500254 curImage.callbackContext(0)->wasAddedToImage();
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400255 }
Robert Phillips96601082018-05-29 16:13:26 -0400256 perRecorderContext->fPromiseImages->push_back(image);
257 SkASSERT(image);
258 return image;
259}
260
261int DDLPromiseImageHelper::findImage(SkImage* image) const {
262 for (int i = 0; i < fImageInfo.count(); ++i) {
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400263 if (fImageInfo[i].originalUniqueID() == image->uniqueID()) { // trying to dedup here
264 SkASSERT(fImageInfo[i].index() == i);
265 SkASSERT(this->isValidID(i) && this->isValidID(fImageInfo[i].index()));
Robert Phillips96601082018-05-29 16:13:26 -0400266 return i;
267 }
268 }
269 return -1;
270}
271
272int DDLPromiseImageHelper::addImage(SkImage* image) {
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400273 SkImage_Base* ib = as_IB(image);
Robert Phillips96601082018-05-29 16:13:26 -0400274
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400275 SkImageInfo overallII = SkImageInfo::Make(image->width(), image->height(),
276 image->colorType(), image->alphaType(),
277 image->refColorSpace());
Robert Phillips96601082018-05-29 16:13:26 -0400278
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400279 PromiseImageInfo& newImageInfo = fImageInfo.emplace_back(fImageInfo.count(),
280 image->uniqueID(),
281 overallII);
Robert Phillips96601082018-05-29 16:13:26 -0400282
Jim Van Verthe24b5872018-10-29 16:26:02 -0400283 SkYUVASizeInfo yuvaSizeInfo;
Jim Van Verth8f11e432018-10-18 14:36:59 -0400284 SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount];
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400285 SkYUVColorSpace yuvColorSpace;
Jim Van Verthe24b5872018-10-29 16:26:02 -0400286 const void* planes[SkYUVASizeInfo::kMaxCount];
Jim Van Verth8f11e432018-10-18 14:36:59 -0400287 sk_sp<SkCachedData> yuvData = ib->getPlanes(&yuvaSizeInfo, yuvaIndices, &yuvColorSpace, planes);
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400288 if (yuvData) {
Jim Van Verth8f11e432018-10-18 14:36:59 -0400289 newImageInfo.setYUVData(std::move(yuvData), yuvaIndices, yuvColorSpace);
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400290
Jim Van Verthb7f0b9c2018-10-22 14:12:03 -0400291 // determine colortypes from index data
292 // for testing we only ever use A8 or RGBA8888
Jim Van Verthe24b5872018-10-29 16:26:02 -0400293 SkColorType colorTypes[SkYUVASizeInfo::kMaxCount] = {
Jim Van Verthb7f0b9c2018-10-22 14:12:03 -0400294 kUnknown_SkColorType, kUnknown_SkColorType,
295 kUnknown_SkColorType, kUnknown_SkColorType
296 };
297 for (int yuvIndex = 0; yuvIndex < SkYUVAIndex::kIndexCount; ++yuvIndex) {
298 int texIdx = yuvaIndices[yuvIndex].fIndex;
299 if (texIdx < 0) {
300 SkASSERT(SkYUVAIndex::kA_Index == yuvIndex);
301 continue;
302 }
303 if (kUnknown_SkColorType == colorTypes[texIdx]) {
304 colorTypes[texIdx] = kAlpha_8_SkColorType;
305 } else {
306 colorTypes[texIdx] = kRGBA_8888_SkColorType;
307 }
308 }
309
Jim Van Verthe24b5872018-10-29 16:26:02 -0400310 for (int i = 0; i < SkYUVASizeInfo::kMaxCount; ++i) {
Jim Van Verthb7f0b9c2018-10-22 14:12:03 -0400311 if (yuvaSizeInfo.fSizes[i].isEmpty()) {
312 SkASSERT(!yuvaSizeInfo.fWidthBytes[i] && kUnknown_SkColorType == colorTypes[i]);
Jim Van Verth8f11e432018-10-18 14:36:59 -0400313 continue;
314 }
315
316 SkImageInfo planeII = SkImageInfo::Make(yuvaSizeInfo.fSizes[i].fWidth,
317 yuvaSizeInfo.fSizes[i].fHeight,
Jim Van Verthb7f0b9c2018-10-22 14:12:03 -0400318 colorTypes[i],
Jim Van Verth8f11e432018-10-18 14:36:59 -0400319 kUnpremul_SkAlphaType);
320 newImageInfo.addYUVPlane(i, planeII, planes[i], yuvaSizeInfo.fWidthBytes[i]);
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400321 }
322 } else {
323 sk_sp<SkImage> rasterImage = image->makeRasterImage(); // force decoding of lazy images
324
325 SkBitmap tmp;
326 tmp.allocPixels(overallII);
327
328 if (!rasterImage->readPixels(tmp.pixmap(), 0, 0)) {
329 return -1;
330 }
331
332 tmp.setImmutable();
333 newImageInfo.setNormalBitmap(tmp);
Robert Phillips96601082018-05-29 16:13:26 -0400334 }
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400335 // In either case newImageInfo's PromiseImageCallbackContext is filled in by uploadAllToGPU
Robert Phillips96601082018-05-29 16:13:26 -0400336
337 return fImageInfo.count()-1;
338}
339
340int DDLPromiseImageHelper::findOrDefineImage(SkImage* image) {
341 int preExistingID = this->findImage(image);
342 if (preExistingID >= 0) {
343 SkASSERT(this->isValidID(preExistingID));
344 return preExistingID;
345 }
346
347 int newID = this->addImage(image);
348 SkASSERT(this->isValidID(newID));
349 return newID;
350}