blob: 2586835868477a00d682887eb67470dacf08ae9e [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() {
20 GrGpu* gpu = fContext->contextPriv().getGpu();
21
22 if (fBackendTexture.isValid()) {
23 gpu->deleteTestingOnlyBackendTexture(fBackendTexture);
24 }
25}
26
27///////////////////////////////////////////////////////////////////////////////////////////////////
28
Robert Phillipse8e2bb12018-09-27 14:26:47 -040029DDLPromiseImageHelper::~DDLPromiseImageHelper() {}
30
Robert Phillips96601082018-05-29 16:13:26 -040031sk_sp<SkData> DDLPromiseImageHelper::deflateSKP(const SkPicture* inputPicture) {
32 SkSerialProcs procs;
33
34 procs.fImageCtx = this;
35 procs.fImageProc = [](SkImage* image, void* ctx) -> sk_sp<SkData> {
36 auto helper = static_cast<DDLPromiseImageHelper*>(ctx);
37
38 int id = helper->findOrDefineImage(image);
39 if (id >= 0) {
40 SkASSERT(helper->isValidID(id));
41 return SkData::MakeWithCopy(&id, sizeof(id));
42 }
43
44 return nullptr;
45 };
46
47 return inputPicture->serialize(&procs);
48}
49
Jim Van Verth60ac5d02018-12-06 13:11:53 -050050// needed until we have SkRG_88_ColorType;
51static GrBackendTexture create_yuva_texture(GrGpu* gpu, const SkPixmap& pm,
52 const SkYUVAIndex yuvaIndices[4], int texIndex) {
53 SkASSERT(texIndex >= 0 && texIndex <= 3);
54 int channelCount = 0;
55 for (int i = 0; i < SkYUVAIndex::kIndexCount; ++i) {
56 if (yuvaIndices[i].fIndex == texIndex) {
57 ++channelCount;
58 }
59 }
60 // Need to create an RG texture for two-channel planes
61 GrBackendTexture tex;
62 if (2 == channelCount) {
63 SkASSERT(kRGBA_8888_SkColorType == pm.colorType());
64 SkAutoTMalloc<char> pixels(2 * pm.width()*pm.height());
65 char* currPixel = pixels;
66 for (int y = 0; y < pm.height(); ++y) {
67 for (int x = 0; x < pm.width(); ++x) {
68 SkColor color = pm.getColor(x, y);
69 currPixel[0] = SkColorGetR(color);
70 currPixel[1] = SkColorGetG(color);
71 currPixel += 2;
72 }
73 }
74 tex = gpu->createTestingOnlyBackendTexture(
75 pixels,
76 pm.width(),
77 pm.height(),
78 GrColorType::kRG_88,
79 false,
80 GrMipMapped::kNo,
81 2 * pm.width());
82 } else {
83 tex = gpu->createTestingOnlyBackendTexture(
84 pm.addr(),
85 pm.width(),
86 pm.height(),
87 pm.colorType(),
88 false,
89 GrMipMapped::kNo,
90 pm.rowBytes());
91 }
92 return tex;
93}
94
Robert Phillips96601082018-05-29 16:13:26 -040095void DDLPromiseImageHelper::uploadAllToGPU(GrContext* context) {
96 GrGpu* gpu = context->contextPriv().getGpu();
97 SkASSERT(gpu);
98
99 for (int i = 0; i < fImageInfo.count(); ++i) {
Robert Phillips96601082018-05-29 16:13:26 -0400100 const PromiseImageInfo& info = fImageInfo[i];
101
102 // DDL TODO: how can we tell if we need mipmapping!
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400103 if (info.isYUV()) {
Jim Van Verth8f11e432018-10-18 14:36:59 -0400104 int numPixmaps;
105 SkAssertResult(SkYUVAIndex::AreValidIndices(info.yuvaIndices(), &numPixmaps));
106 for (int j = 0; j < numPixmaps; ++j) {
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400107 const SkPixmap& yuvPixmap = info.yuvPixmap(j);
Robert Phillips96601082018-05-29 16:13:26 -0400108
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400109 sk_sp<PromiseImageCallbackContext> callbackContext(
110 new PromiseImageCallbackContext(context));
Jim Van Verth60ac5d02018-12-06 13:11:53 -0500111
112 callbackContext->setBackendTexture(create_yuva_texture(gpu, yuvPixmap,
113 info.yuvaIndices(), j));
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400114 SkAssertResult(callbackContext->backendTexture().isValid());
115
116 fImageInfo[i].setCallbackContext(j, std::move(callbackContext));
117 }
118 } else {
119 sk_sp<PromiseImageCallbackContext> callbackContext(
120 new PromiseImageCallbackContext(context));
121
122 const SkBitmap& bm = info.normalBitmap();
123
124 callbackContext->setBackendTexture(gpu->createTestingOnlyBackendTexture(
125 bm.getPixels(),
126 bm.width(),
127 bm.height(),
128 bm.colorType(),
129 false, GrMipMapped::kNo,
130 bm.rowBytes()));
131 // The GMs sometimes request too large an image
132 //SkAssertResult(callbackContext->backendTexture().isValid());
133
134 fImageInfo[i].setCallbackContext(0, std::move(callbackContext));
135 }
136
Robert Phillips96601082018-05-29 16:13:26 -0400137 }
138}
139
140sk_sp<SkPicture> DDLPromiseImageHelper::reinflateSKP(
141 SkDeferredDisplayListRecorder* recorder,
142 SkData* compressedPictureData,
143 SkTArray<sk_sp<SkImage>>* promiseImages) const {
144 PerRecorderContext perRecorderContext { recorder, this, promiseImages };
145
146 SkDeserialProcs procs;
147 procs.fImageCtx = (void*) &perRecorderContext;
148 procs.fImageProc = PromiseImageCreator;
149
150 return SkPicture::MakeFromData(compressedPictureData, &procs);
151}
152
153// This generates promise images to replace the indices in the compressed picture. This
154// reconstitution is performed separately in each thread so we end up with multiple
155// promise images referring to the same GrBackendTexture.
156sk_sp<SkImage> DDLPromiseImageHelper::PromiseImageCreator(const void* rawData,
157 size_t length, void* ctxIn) {
158 PerRecorderContext* perRecorderContext = static_cast<PerRecorderContext*>(ctxIn);
159 const DDLPromiseImageHelper* helper = perRecorderContext->fHelper;
160 SkDeferredDisplayListRecorder* recorder = perRecorderContext->fRecorder;
161
162 SkASSERT(length == sizeof(int));
163
164 const int* indexPtr = static_cast<const int*>(rawData);
165 SkASSERT(helper->isValidID(*indexPtr));
166
167 const DDLPromiseImageHelper::PromiseImageInfo& curImage = helper->getInfo(*indexPtr);
168
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400169 if (!curImage.backendTexture(0).isValid()) {
170 SkASSERT(!curImage.isYUV());
Robert Phillips96601082018-05-29 16:13:26 -0400171 // We weren't able to make a backend texture for this SkImage. In this case we create
172 // a separate bitmap-backed image for each thread.
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400173 SkASSERT(curImage.normalBitmap().isImmutable());
174 return SkImage::MakeFromBitmap(curImage.normalBitmap());
Robert Phillips96601082018-05-29 16:13:26 -0400175 }
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400176 SkASSERT(curImage.index() == *indexPtr);
Robert Phillips96601082018-05-29 16:13:26 -0400177
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400178 sk_sp<SkImage> image;
179 if (curImage.isYUV()) {
Jim Van Verthe24b5872018-10-29 16:26:02 -0400180 GrBackendFormat backendFormats[SkYUVASizeInfo::kMaxCount];
181 void* contexts[SkYUVASizeInfo::kMaxCount] = { nullptr, nullptr, nullptr, nullptr };
182 SkISize sizes[SkYUVASizeInfo::kMaxCount];
Jim Van Verth8f11e432018-10-18 14:36:59 -0400183 // TODO: store this value somewhere?
184 int textureCount;
185 SkAssertResult(SkYUVAIndex::AreValidIndices(curImage.yuvaIndices(), &textureCount));
186 for (int i = 0; i < textureCount; ++i) {
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400187 const GrBackendTexture& backendTex = curImage.backendTexture(i);
Brian Salomonf391d0f2018-12-14 09:18:50 -0500188 backendFormats[i] = backendTex.getBackendFormat();
189 SkASSERT(backendFormats[i].isValid());
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400190 contexts[i] = curImage.refCallbackContext(i).release();
Jim Van Verthf9f07352018-10-24 10:32:20 -0400191 sizes[i].set(curImage.yuvPixmap(i).width(), curImage.yuvPixmap(i).height());
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400192 }
Jim Van Verthe24b5872018-10-29 16:26:02 -0400193 for (int i = textureCount; i < SkYUVASizeInfo::kMaxCount; ++i) {
Jim Van Verthf9f07352018-10-24 10:32:20 -0400194 sizes[i] = SkISize::MakeEmpty();
Jim Van Verth8f11e432018-10-18 14:36:59 -0400195 }
Jim Van Verthf99a6742018-10-18 16:13:18 +0000196
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400197 image = recorder->makeYUVAPromiseTexture(curImage.yuvColorSpace(),
198 backendFormats,
Jim Van Verthf9f07352018-10-24 10:32:20 -0400199 sizes,
Jim Van Verth8f11e432018-10-18 14:36:59 -0400200 curImage.yuvaIndices(),
Jim Van Verth21bd60d2018-10-12 15:00:20 -0400201 curImage.overallWidth(),
202 curImage.overallHeight(),
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400203 GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin,
204 curImage.refOverallColorSpace(),
205 DDLPromiseImageHelper::PromiseImageFulfillProc,
206 DDLPromiseImageHelper::PromiseImageReleaseProc,
207 DDLPromiseImageHelper::PromiseImageDoneProc,
208 contexts);
209
210 } else {
211 const GrBackendTexture& backendTex = curImage.backendTexture(0);
Brian Salomonf391d0f2018-12-14 09:18:50 -0500212 GrBackendFormat backendFormat = backendTex.getBackendFormat();
213 SkASSERT(backendFormat.isValid());
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400214
215 // Each DDL recorder gets its own ref on the promise callback context for the
216 // promise images it creates.
217 // DDL TODO: sort out mipmapping
218 image = recorder->makePromiseTexture(backendFormat,
219 curImage.overallWidth(),
220 curImage.overallHeight(),
221 GrMipMapped::kNo,
222 GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin,
223 curImage.overallColorType(),
224 curImage.overallAlphaType(),
225 curImage.refOverallColorSpace(),
226 DDLPromiseImageHelper::PromiseImageFulfillProc,
227 DDLPromiseImageHelper::PromiseImageReleaseProc,
228 DDLPromiseImageHelper::PromiseImageDoneProc,
229 (void*) curImage.refCallbackContext(0).release());
230 }
Robert Phillips96601082018-05-29 16:13:26 -0400231 perRecorderContext->fPromiseImages->push_back(image);
232 SkASSERT(image);
233 return image;
234}
235
236int DDLPromiseImageHelper::findImage(SkImage* image) const {
237 for (int i = 0; i < fImageInfo.count(); ++i) {
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400238 if (fImageInfo[i].originalUniqueID() == image->uniqueID()) { // trying to dedup here
239 SkASSERT(fImageInfo[i].index() == i);
240 SkASSERT(this->isValidID(i) && this->isValidID(fImageInfo[i].index()));
Robert Phillips96601082018-05-29 16:13:26 -0400241 return i;
242 }
243 }
244 return -1;
245}
246
247int DDLPromiseImageHelper::addImage(SkImage* image) {
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400248 SkImage_Base* ib = as_IB(image);
Robert Phillips96601082018-05-29 16:13:26 -0400249
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400250 SkImageInfo overallII = SkImageInfo::Make(image->width(), image->height(),
251 image->colorType(), image->alphaType(),
252 image->refColorSpace());
Robert Phillips96601082018-05-29 16:13:26 -0400253
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400254 PromiseImageInfo& newImageInfo = fImageInfo.emplace_back(fImageInfo.count(),
255 image->uniqueID(),
256 overallII);
Robert Phillips96601082018-05-29 16:13:26 -0400257
Jim Van Verthe24b5872018-10-29 16:26:02 -0400258 SkYUVASizeInfo yuvaSizeInfo;
Jim Van Verth8f11e432018-10-18 14:36:59 -0400259 SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount];
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400260 SkYUVColorSpace yuvColorSpace;
Jim Van Verthe24b5872018-10-29 16:26:02 -0400261 const void* planes[SkYUVASizeInfo::kMaxCount];
Jim Van Verth8f11e432018-10-18 14:36:59 -0400262 sk_sp<SkCachedData> yuvData = ib->getPlanes(&yuvaSizeInfo, yuvaIndices, &yuvColorSpace, planes);
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400263 if (yuvData) {
Jim Van Verth8f11e432018-10-18 14:36:59 -0400264 newImageInfo.setYUVData(std::move(yuvData), yuvaIndices, yuvColorSpace);
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400265
Jim Van Verthb7f0b9c2018-10-22 14:12:03 -0400266 // determine colortypes from index data
267 // for testing we only ever use A8 or RGBA8888
Jim Van Verthe24b5872018-10-29 16:26:02 -0400268 SkColorType colorTypes[SkYUVASizeInfo::kMaxCount] = {
Jim Van Verthb7f0b9c2018-10-22 14:12:03 -0400269 kUnknown_SkColorType, kUnknown_SkColorType,
270 kUnknown_SkColorType, kUnknown_SkColorType
271 };
272 for (int yuvIndex = 0; yuvIndex < SkYUVAIndex::kIndexCount; ++yuvIndex) {
273 int texIdx = yuvaIndices[yuvIndex].fIndex;
274 if (texIdx < 0) {
275 SkASSERT(SkYUVAIndex::kA_Index == yuvIndex);
276 continue;
277 }
278 if (kUnknown_SkColorType == colorTypes[texIdx]) {
279 colorTypes[texIdx] = kAlpha_8_SkColorType;
280 } else {
281 colorTypes[texIdx] = kRGBA_8888_SkColorType;
282 }
283 }
284
Jim Van Verthe24b5872018-10-29 16:26:02 -0400285 for (int i = 0; i < SkYUVASizeInfo::kMaxCount; ++i) {
Jim Van Verthb7f0b9c2018-10-22 14:12:03 -0400286 if (yuvaSizeInfo.fSizes[i].isEmpty()) {
287 SkASSERT(!yuvaSizeInfo.fWidthBytes[i] && kUnknown_SkColorType == colorTypes[i]);
Jim Van Verth8f11e432018-10-18 14:36:59 -0400288 continue;
289 }
290
291 SkImageInfo planeII = SkImageInfo::Make(yuvaSizeInfo.fSizes[i].fWidth,
292 yuvaSizeInfo.fSizes[i].fHeight,
Jim Van Verthb7f0b9c2018-10-22 14:12:03 -0400293 colorTypes[i],
Jim Van Verth8f11e432018-10-18 14:36:59 -0400294 kUnpremul_SkAlphaType);
295 newImageInfo.addYUVPlane(i, planeII, planes[i], yuvaSizeInfo.fWidthBytes[i]);
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400296 }
297 } else {
298 sk_sp<SkImage> rasterImage = image->makeRasterImage(); // force decoding of lazy images
299
300 SkBitmap tmp;
301 tmp.allocPixels(overallII);
302
303 if (!rasterImage->readPixels(tmp.pixmap(), 0, 0)) {
304 return -1;
305 }
306
307 tmp.setImmutable();
308 newImageInfo.setNormalBitmap(tmp);
Robert Phillips96601082018-05-29 16:13:26 -0400309 }
Robert Phillipse8e2bb12018-09-27 14:26:47 -0400310 // In either case newImageInfo's PromiseImageCallbackContext is filled in by uploadAllToGPU
Robert Phillips96601082018-05-29 16:13:26 -0400311
312 return fImageInfo.count()-1;
313}
314
315int DDLPromiseImageHelper::findOrDefineImage(SkImage* image) {
316 int preExistingID = this->findImage(image);
317 if (preExistingID >= 0) {
318 SkASSERT(this->isValidID(preExistingID));
319 return preExistingID;
320 }
321
322 int newID = this->addImage(image);
323 SkASSERT(this->isValidID(newID));
324 return newID;
325}