• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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