Line data Source code
1 : /*
2 : * Copyright 2015 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 "SkBitmap.h"
9 : #include "SkBitmapCache.h"
10 : #include "SkColorSpace_Base.h"
11 : #include "SkImage_Base.h"
12 : #include "SkImageCacherator.h"
13 : #include "SkMallocPixelRef.h"
14 : #include "SkNextID.h"
15 : #include "SkPixelRef.h"
16 : #include "SkResourceCache.h"
17 :
18 : #if SK_SUPPORT_GPU
19 : #include "GrContext.h"
20 : #include "GrContextPriv.h"
21 : #include "GrGpuResourcePriv.h"
22 : #include "GrImageTextureMaker.h"
23 : #include "GrResourceKey.h"
24 : #include "GrResourceProvider.h"
25 : #include "GrSamplerParams.h"
26 : #include "GrYUVProvider.h"
27 : #include "SkGr.h"
28 : #endif
29 :
30 : // Until we actually have codecs/etc. that can contain/support a GPU texture format
31 : // skip this step, since for some generators, returning their encoded data as a SkData
32 : // can be somewhat expensive, and this call doesn't indicate to the generator that we're
33 : // only interested in GPU datas...
34 : // see skbug.com/ 4971, 5128, ...
35 : //#define SK_SUPPORT_COMPRESSED_TEXTURES_IN_CACHERATOR
36 :
37 : // Helper for exclusive access to a shared generator.
38 0 : class SkImageCacherator::ScopedGenerator {
39 : public:
40 0 : ScopedGenerator(const sk_sp<SharedGenerator>& gen)
41 0 : : fSharedGenerator(gen)
42 0 : , fAutoAquire(gen->fMutex) {}
43 :
44 0 : SkImageGenerator* operator->() const {
45 0 : fSharedGenerator->fMutex.assertHeld();
46 0 : return fSharedGenerator->fGenerator.get();
47 : }
48 :
49 0 : operator SkImageGenerator*() const {
50 0 : fSharedGenerator->fMutex.assertHeld();
51 0 : return fSharedGenerator->fGenerator.get();
52 : }
53 :
54 : private:
55 : const sk_sp<SharedGenerator>& fSharedGenerator;
56 : SkAutoExclusive fAutoAquire;
57 : };
58 :
59 0 : SkImageCacherator::Validator::Validator(sk_sp<SharedGenerator> gen, const SkIRect* subset)
60 0 : : fSharedGenerator(std::move(gen)) {
61 :
62 0 : if (!fSharedGenerator) {
63 0 : return;
64 : }
65 :
66 : // The following generator accessors are safe without acquiring the mutex (const getters).
67 : // TODO: refactor to use a ScopedGenerator instead, for clarity.
68 0 : const SkImageInfo& info = fSharedGenerator->fGenerator->getInfo();
69 0 : if (info.isEmpty()) {
70 0 : fSharedGenerator.reset();
71 0 : return;
72 : }
73 :
74 0 : fUniqueID = fSharedGenerator->fGenerator->uniqueID();
75 0 : const SkIRect bounds = SkIRect::MakeWH(info.width(), info.height());
76 0 : if (subset) {
77 0 : if (!bounds.contains(*subset)) {
78 0 : fSharedGenerator.reset();
79 0 : return;
80 : }
81 0 : if (*subset != bounds) {
82 : // we need a different uniqueID since we really are a subset of the raw generator
83 0 : fUniqueID = SkNextID::ImageID();
84 : }
85 : } else {
86 0 : subset = &bounds;
87 : }
88 :
89 0 : fInfo = info.makeWH(subset->width(), subset->height());
90 0 : fOrigin = SkIPoint::Make(subset->x(), subset->y());
91 :
92 : // colortables are poorly to not-at-all supported in our resourcecache, so we
93 : // bully them into N32 (the generator will perform the up-sample)
94 0 : if (fInfo.colorType() == kIndex_8_SkColorType) {
95 0 : fInfo = fInfo.makeColorType(kN32_SkColorType);
96 : }
97 : }
98 :
99 0 : SkImageCacherator* SkImageCacherator::NewFromGenerator(std::unique_ptr<SkImageGenerator> gen,
100 : const SkIRect* subset) {
101 0 : Validator validator(SharedGenerator::Make(std::move(gen)), subset);
102 :
103 0 : return validator ? new SkImageCacherator(&validator) : nullptr;
104 : }
105 :
106 0 : SkImageCacherator::SkImageCacherator(Validator* validator)
107 0 : : fSharedGenerator(std::move(validator->fSharedGenerator)) // we take ownership
108 : , fInfo(validator->fInfo)
109 0 : , fOrigin(validator->fOrigin)
110 : {
111 0 : SkASSERT(fSharedGenerator);
112 0 : SkASSERT(fInfo.colorType() != kIndex_8_SkColorType);
113 : // We explicit set the legacy format slot, but leave the others uninitialized (via SkOnce)
114 : // and only resolove them to IDs as needed (by calling getUniqueID()).
115 0 : fIDRecs[kLegacy_CachedFormat].fOnce([this, validator] {
116 0 : fIDRecs[kLegacy_CachedFormat].fUniqueID = validator->fUniqueID;
117 0 : });
118 0 : }
119 :
120 0 : SkImageCacherator::~SkImageCacherator() {}
121 :
122 0 : uint32_t SkImageCacherator::getUniqueID(CachedFormat format) const {
123 0 : IDRec* rec = &fIDRecs[format];
124 0 : rec->fOnce([rec] {
125 0 : rec->fUniqueID = SkNextID::ImageID();
126 0 : });
127 0 : return rec->fUniqueID;
128 : }
129 :
130 0 : SkData* SkImageCacherator::refEncoded(GrContext* ctx) {
131 0 : ScopedGenerator generator(fSharedGenerator);
132 0 : return generator->refEncodedData(ctx);
133 : }
134 :
135 0 : static bool check_output_bitmap(const SkBitmap& bitmap, uint32_t expectedID) {
136 0 : SkASSERT(bitmap.getGenerationID() == expectedID);
137 0 : SkASSERT(bitmap.isImmutable());
138 0 : SkASSERT(bitmap.getPixels());
139 0 : return true;
140 : }
141 :
142 0 : bool SkImageCacherator::directGeneratePixels(const SkImageInfo& info, void* pixels, size_t rb,
143 : int srcX, int srcY,
144 : SkTransferFunctionBehavior behavior) {
145 0 : ScopedGenerator generator(fSharedGenerator);
146 0 : const SkImageInfo& genInfo = generator->getInfo();
147 : // Currently generators do not natively handle subsets, so check that first.
148 0 : if (srcX || srcY || genInfo.width() != info.width() || genInfo.height() != info.height()) {
149 0 : return false;
150 : }
151 :
152 0 : SkImageGenerator::Options opts;
153 0 : opts.fBehavior = behavior;
154 0 : return generator->getPixels(info, pixels, rb, &opts);
155 : }
156 :
157 : //////////////////////////////////////////////////////////////////////////////////////////////////
158 :
159 0 : bool SkImageCacherator::lockAsBitmapOnlyIfAlreadyCached(SkBitmap* bitmap, CachedFormat format) {
160 0 : uint32_t uniqueID = this->getUniqueID(format);
161 0 : return SkBitmapCache::Find(SkBitmapCacheDesc::Make(uniqueID,
162 0 : fInfo.width(), fInfo.height()), bitmap) &&
163 0 : check_output_bitmap(*bitmap, uniqueID);
164 : }
165 :
166 0 : static bool generate_pixels(SkImageGenerator* gen, const SkPixmap& pmap, int originX, int originY) {
167 0 : const int genW = gen->getInfo().width();
168 0 : const int genH = gen->getInfo().height();
169 0 : const SkIRect srcR = SkIRect::MakeWH(genW, genH);
170 0 : const SkIRect dstR = SkIRect::MakeXYWH(originX, originY, pmap.width(), pmap.height());
171 0 : if (!srcR.contains(dstR)) {
172 0 : return false;
173 : }
174 :
175 : // If they are requesting a subset, we have to have a temp allocation for full image, and
176 : // then copy the subset into their allocation
177 0 : SkBitmap full;
178 0 : SkPixmap fullPM;
179 0 : const SkPixmap* dstPM = &pmap;
180 0 : if (srcR != dstR) {
181 0 : if (!full.tryAllocPixels(pmap.info().makeWH(genW, genH))) {
182 0 : return false;
183 : }
184 0 : if (!full.peekPixels(&fullPM)) {
185 0 : return false;
186 : }
187 0 : dstPM = &fullPM;
188 : }
189 :
190 0 : if (!gen->getPixels(dstPM->info(), dstPM->writable_addr(), dstPM->rowBytes())) {
191 0 : return false;
192 : }
193 :
194 0 : if (srcR != dstR) {
195 0 : if (!full.readPixels(pmap, originX, originY)) {
196 0 : return false;
197 : }
198 : }
199 0 : return true;
200 : }
201 :
202 0 : bool SkImageCacherator::tryLockAsBitmap(SkBitmap* bitmap, const SkImage* client,
203 : SkImage::CachingHint chint, CachedFormat format,
204 : const SkImageInfo& info) {
205 0 : if (this->lockAsBitmapOnlyIfAlreadyCached(bitmap, format)) {
206 0 : return true;
207 : }
208 :
209 0 : uint32_t uniqueID = this->getUniqueID(format);
210 :
211 0 : SkBitmap tmpBitmap;
212 0 : SkBitmapCache::RecPtr cacheRec;
213 0 : SkPixmap pmap;
214 0 : if (SkImage::kAllow_CachingHint == chint) {
215 0 : auto desc = SkBitmapCacheDesc::Make(uniqueID, info.width(), info.height());
216 0 : cacheRec = SkBitmapCache::Alloc(desc, info, &pmap);
217 0 : if (!cacheRec) {
218 0 : return false;
219 : }
220 : } else {
221 0 : if (!tmpBitmap.tryAllocPixels(info)) {
222 0 : return false;
223 : }
224 0 : if (!tmpBitmap.peekPixels(&pmap)) {
225 0 : return false;
226 : }
227 : }
228 :
229 0 : ScopedGenerator generator(fSharedGenerator);
230 0 : if (!generate_pixels(generator, pmap, fOrigin.x(), fOrigin.y())) {
231 0 : return false;
232 : }
233 :
234 0 : if (cacheRec) {
235 0 : SkBitmapCache::Add(std::move(cacheRec), bitmap);
236 0 : SkASSERT(bitmap->getPixels()); // we're locked
237 0 : SkASSERT(bitmap->isImmutable());
238 0 : SkASSERT(bitmap->getGenerationID() == uniqueID);
239 0 : if (client) {
240 0 : as_IB(client)->notifyAddedToCache();
241 : }
242 : } else {
243 0 : *bitmap = tmpBitmap;
244 0 : bitmap->lockPixels();
245 0 : bitmap->pixelRef()->setImmutableWithID(uniqueID);
246 : }
247 0 : return true;
248 : }
249 :
250 0 : bool SkImageCacherator::lockAsBitmap(GrContext* context, SkBitmap* bitmap, const SkImage* client,
251 : SkColorSpace* dstColorSpace,
252 : SkImage::CachingHint chint) {
253 0 : CachedFormat format = this->chooseCacheFormat(dstColorSpace);
254 0 : SkImageInfo cacheInfo = this->buildCacheInfo(format);
255 0 : const uint32_t uniqueID = this->getUniqueID(format);
256 :
257 0 : if (this->tryLockAsBitmap(bitmap, client, chint, format, cacheInfo)) {
258 0 : return check_output_bitmap(*bitmap, uniqueID);
259 : }
260 :
261 : #if SK_SUPPORT_GPU
262 0 : if (!context) {
263 0 : bitmap->reset();
264 0 : return false;
265 : }
266 :
267 : // Try to get a texture and read it back to raster (and then cache that with our ID)
268 0 : sk_sp<GrTextureProxy> proxy;
269 :
270 : {
271 0 : ScopedGenerator generator(fSharedGenerator);
272 0 : proxy = generator->generateTexture(context, cacheInfo, fOrigin);
273 : }
274 0 : if (!proxy) {
275 0 : bitmap->reset();
276 0 : return false;
277 : }
278 :
279 0 : const auto desc = SkBitmapCacheDesc::Make(uniqueID, fInfo.width(), fInfo.height());
280 0 : SkBitmapCache::RecPtr rec;
281 0 : SkPixmap pmap;
282 0 : if (SkImage::kAllow_CachingHint == chint) {
283 0 : rec = SkBitmapCache::Alloc(desc, cacheInfo, &pmap);
284 0 : if (!rec) {
285 0 : bitmap->reset();
286 0 : return false;
287 : }
288 : } else {
289 0 : if (!bitmap->tryAllocPixels(cacheInfo)) {
290 0 : bitmap->reset();
291 0 : return false;
292 : }
293 : }
294 :
295 0 : sk_sp<GrSurfaceContext> sContext(context->contextPriv().makeWrappedSurfaceContext(
296 : proxy,
297 0 : fInfo.refColorSpace())); // src colorSpace
298 0 : if (!sContext) {
299 0 : bitmap->reset();
300 0 : return false;
301 : }
302 :
303 0 : if (!sContext->readPixels(pmap.info(), pmap.writable_addr(), pmap.rowBytes(), 0, 0)) {
304 0 : bitmap->reset();
305 0 : return false;
306 : }
307 :
308 0 : if (rec) {
309 0 : SkBitmapCache::Add(std::move(rec), bitmap);
310 0 : if (client) {
311 0 : as_IB(client)->notifyAddedToCache();
312 : }
313 : }
314 0 : return check_output_bitmap(*bitmap, uniqueID);
315 : #else
316 : return false;
317 : #endif
318 : }
319 :
320 : //////////////////////////////////////////////////////////////////////////////////////////////////
321 :
322 : // Abstraction of GrCaps that handles the cases where we don't have a caps pointer (because
323 : // we're in raster mode), or where GPU support is entirely missing. In theory, we only need the
324 : // chosen format to be texturable, but that lets us choose F16 on GLES implemenations where we
325 : // won't be able to read the texture back. We'd like to ensure that SkImake::makeNonTextureImage
326 : // works, so we require that the formats we choose are renderable (as a proxy for being readable).
327 : struct CacheCaps {
328 0 : CacheCaps(const GrCaps* caps) : fCaps(caps) {}
329 :
330 : #if SK_SUPPORT_GPU
331 0 : bool supportsHalfFloat() const {
332 0 : return !fCaps ||
333 0 : (fCaps->isConfigTexturable(kRGBA_half_GrPixelConfig) &&
334 0 : fCaps->isConfigRenderable(kRGBA_half_GrPixelConfig, false));
335 : }
336 :
337 0 : bool supportsSRGB() const {
338 0 : return !fCaps ||
339 0 : (fCaps->srgbSupport() && fCaps->isConfigTexturable(kSRGBA_8888_GrPixelConfig));
340 : }
341 :
342 0 : bool supportsSBGR() const {
343 0 : return !fCaps || fCaps->srgbSupport();
344 : }
345 : #else
346 : bool supportsHalfFloat() const { return true; }
347 : bool supportsSRGB() const { return true; }
348 : bool supportsSBGR() const { return true; }
349 : #endif
350 :
351 : const GrCaps* fCaps;
352 : };
353 :
354 0 : SkImageCacherator::CachedFormat SkImageCacherator::chooseCacheFormat(SkColorSpace* dstColorSpace,
355 : const GrCaps* grCaps) {
356 0 : SkColorSpace* cs = fInfo.colorSpace();
357 0 : if (!cs || !dstColorSpace) {
358 0 : return kLegacy_CachedFormat;
359 : }
360 :
361 0 : CacheCaps caps(grCaps);
362 0 : switch (fInfo.colorType()) {
363 : case kUnknown_SkColorType:
364 : case kAlpha_8_SkColorType:
365 : case kRGB_565_SkColorType:
366 : case kARGB_4444_SkColorType:
367 : // We don't support color space on these formats, so always decode in legacy mode:
368 : // TODO: Ask the codec to decode these to something else (at least sRGB 8888)?
369 0 : return kLegacy_CachedFormat;
370 :
371 : case kIndex_8_SkColorType:
372 0 : SkDEBUGFAIL("Index_8 should have been remapped at construction time.");
373 0 : return kLegacy_CachedFormat;
374 :
375 : case kGray_8_SkColorType:
376 : // TODO: What do we do with grayscale sources that have strange color spaces attached?
377 : // The codecs and color space xform don't handle this correctly (yet), so drop it on
378 : // the floor. (Also, inflating by a factor of 8 is going to be unfortunate).
379 : // As it is, we don't directly support sRGB grayscale, so ask the codec to convert
380 : // it for us. This bypasses some really sketchy code GrUploadPixmapToTexture.
381 0 : if (cs->gammaCloseToSRGB() && caps.supportsSRGB()) {
382 0 : return kSRGB8888_CachedFormat;
383 : } else {
384 0 : return kLegacy_CachedFormat;
385 : }
386 :
387 : case kRGBA_8888_SkColorType:
388 0 : if (cs->gammaCloseToSRGB()) {
389 0 : if (caps.supportsSRGB()) {
390 0 : return kSRGB8888_CachedFormat;
391 0 : } else if (caps.supportsHalfFloat()) {
392 0 : return kLinearF16_CachedFormat;
393 : } else {
394 0 : return kLegacy_CachedFormat;
395 : }
396 : } else {
397 0 : if (caps.supportsHalfFloat()) {
398 0 : return kLinearF16_CachedFormat;
399 0 : } else if (caps.supportsSRGB()) {
400 0 : return kSRGB8888_CachedFormat;
401 : } else {
402 0 : return kLegacy_CachedFormat;
403 : }
404 : }
405 :
406 : case kBGRA_8888_SkColorType:
407 : // Odd case. sBGRA isn't a real thing, so we may not have this texturable.
408 0 : if (caps.supportsSBGR()) {
409 0 : if (cs->gammaCloseToSRGB()) {
410 0 : return kSBGR8888_CachedFormat;
411 0 : } else if (caps.supportsHalfFloat()) {
412 0 : return kLinearF16_CachedFormat;
413 0 : } else if (caps.supportsSRGB()) {
414 0 : return kSRGB8888_CachedFormat;
415 : } else {
416 : // sBGRA support without sRGBA is highly unlikely (impossible?) Nevertheless.
417 0 : return kLegacy_CachedFormat;
418 : }
419 : } else {
420 0 : if (cs->gammaCloseToSRGB()) {
421 0 : if (caps.supportsSRGB()) {
422 0 : return kSRGB8888_CachedFormat;
423 0 : } else if (caps.supportsHalfFloat()) {
424 0 : return kLinearF16_CachedFormat;
425 : } else {
426 0 : return kLegacy_CachedFormat;
427 : }
428 : } else {
429 0 : if (caps.supportsHalfFloat()) {
430 0 : return kLinearF16_CachedFormat;
431 0 : } else if (caps.supportsSRGB()) {
432 0 : return kSRGB8888_CachedFormat;
433 : } else {
434 0 : return kLegacy_CachedFormat;
435 : }
436 : }
437 : }
438 :
439 : case kRGBA_F16_SkColorType:
440 0 : if (caps.supportsHalfFloat()) {
441 0 : return kLinearF16_CachedFormat;
442 0 : } else if (caps.supportsSRGB()) {
443 0 : return kSRGB8888_CachedFormat;
444 : } else {
445 0 : return kLegacy_CachedFormat;
446 : }
447 : }
448 0 : SkDEBUGFAIL("Unreachable");
449 0 : return kLegacy_CachedFormat;
450 : }
451 :
452 0 : SkImageInfo SkImageCacherator::buildCacheInfo(CachedFormat format) {
453 0 : switch (format) {
454 : case kLegacy_CachedFormat:
455 0 : return fInfo.makeColorSpace(nullptr);
456 : case kLinearF16_CachedFormat:
457 0 : return fInfo.makeColorType(kRGBA_F16_SkColorType)
458 0 : .makeColorSpace(as_CSB(fInfo.colorSpace())->makeLinearGamma());
459 : case kSRGB8888_CachedFormat:
460 : // If the transfer function is nearly (but not exactly) sRGB, we don't want the codec
461 : // to bother trans-coding. It would be slow, and do more harm than good visually,
462 : // so we make sure to leave the colorspace as-is.
463 0 : if (fInfo.colorSpace()->gammaCloseToSRGB()) {
464 0 : return fInfo.makeColorType(kRGBA_8888_SkColorType);
465 : } else {
466 0 : return fInfo.makeColorType(kRGBA_8888_SkColorType)
467 0 : .makeColorSpace(as_CSB(fInfo.colorSpace())->makeSRGBGamma());
468 : }
469 : case kSBGR8888_CachedFormat:
470 : // See note above about not-quite-sRGB transfer functions.
471 0 : if (fInfo.colorSpace()->gammaCloseToSRGB()) {
472 0 : return fInfo.makeColorType(kBGRA_8888_SkColorType);
473 : } else {
474 0 : return fInfo.makeColorType(kBGRA_8888_SkColorType)
475 0 : .makeColorSpace(as_CSB(fInfo.colorSpace())->makeSRGBGamma());
476 : }
477 : default:
478 0 : SkDEBUGFAIL("Invalid cached format");
479 0 : return fInfo;
480 : }
481 : }
482 :
483 : //////////////////////////////////////////////////////////////////////////////////////////////////
484 :
485 : #if SK_SUPPORT_GPU
486 :
487 0 : void SkImageCacherator::makeCacheKeyFromOrigKey(const GrUniqueKey& origKey, CachedFormat format,
488 : GrUniqueKey* cacheKey) {
489 0 : SkASSERT(!cacheKey->isValid());
490 0 : if (origKey.isValid()) {
491 0 : static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
492 0 : GrUniqueKey::Builder builder(cacheKey, origKey, kDomain, 1);
493 0 : builder[0] = format;
494 : }
495 0 : }
496 :
497 : #ifdef SK_SUPPORT_COMPRESSED_TEXTURES_IN_CACHERATOR
498 : static GrTexture* load_compressed_into_texture(GrContext* ctx, SkData* data, GrSurfaceDesc desc) {
499 : const void* rawStart;
500 : GrPixelConfig config = GrIsCompressedTextureDataSupported(ctx, data, desc.fWidth, desc.fHeight,
501 : &rawStart);
502 : if (kUnknown_GrPixelConfig == config) {
503 : return nullptr;
504 : }
505 :
506 : desc.fConfig = config;
507 : return ctx->resourceProvider()->createTexture(desc, SkBudgeted::kYes, rawStart, 0);
508 : }
509 : #endif
510 :
511 0 : class Generator_GrYUVProvider : public GrYUVProvider {
512 : SkImageGenerator* fGen;
513 :
514 : public:
515 0 : Generator_GrYUVProvider(SkImageGenerator* gen) : fGen(gen) {}
516 :
517 0 : uint32_t onGetID() override { return fGen->uniqueID(); }
518 0 : bool onQueryYUV8(SkYUVSizeInfo* sizeInfo, SkYUVColorSpace* colorSpace) const override {
519 0 : return fGen->queryYUV8(sizeInfo, colorSpace);
520 : }
521 0 : bool onGetYUV8Planes(const SkYUVSizeInfo& sizeInfo, void* planes[3]) override {
522 0 : return fGen->getYUV8Planes(sizeInfo, planes);
523 : }
524 : };
525 :
526 0 : static void set_key_on_proxy(GrResourceProvider* resourceProvider,
527 : GrTextureProxy* proxy, const GrUniqueKey& key) {
528 0 : if (key.isValid()) {
529 0 : resourceProvider->assignUniqueKeyToProxy(key, proxy);
530 : }
531 0 : }
532 :
533 0 : sk_sp<SkColorSpace> SkImageCacherator::getColorSpace(GrContext* ctx, SkColorSpace* dstColorSpace) {
534 : // TODO: This isn't always correct. Picture generator currently produces textures in N32,
535 : // and will (soon) emit them in an arbitrary (destination) space. We will need to stash that
536 : // information in/on the key so we can return the correct space in case #1 of lockTexture.
537 0 : CachedFormat format = this->chooseCacheFormat(dstColorSpace, ctx->caps());
538 0 : SkImageInfo cacheInfo = this->buildCacheInfo(format);
539 0 : return sk_ref_sp(cacheInfo.colorSpace());
540 : }
541 :
542 : /*
543 : * We have a 5 ways to try to return a texture (in sorted order)
544 : *
545 : * 1. Check the cache for a pre-existing one
546 : * 2. Ask the generator to natively create one
547 : * 3. Ask the generator to return a compressed form that the GPU might support
548 : * 4. Ask the generator to return YUV planes, which the GPU can convert
549 : * 5. Ask the generator to return RGB(A) data, which the GPU can convert
550 : */
551 0 : sk_sp<GrTextureProxy> SkImageCacherator::lockTextureProxy(GrContext* ctx,
552 : const GrUniqueKey& origKey,
553 : const SkImage* client,
554 : SkImage::CachingHint chint,
555 : bool willBeMipped,
556 : SkColorSpace* dstColorSpace) {
557 : // Values representing the various texture lock paths we can take. Used for logging the path
558 : // taken to a histogram.
559 : enum LockTexturePath {
560 : kFailure_LockTexturePath,
561 : kPreExisting_LockTexturePath,
562 : kNative_LockTexturePath,
563 : kCompressed_LockTexturePath,
564 : kYUV_LockTexturePath,
565 : kRGBA_LockTexturePath,
566 : };
567 :
568 : enum { kLockTexturePathCount = kRGBA_LockTexturePath + 1 };
569 :
570 : // Determine which cached format we're going to use (which may involve decoding to a different
571 : // info than the generator provides).
572 0 : CachedFormat format = this->chooseCacheFormat(dstColorSpace, ctx->caps());
573 :
574 : // Fold the cache format into our texture key
575 0 : GrUniqueKey key;
576 0 : this->makeCacheKeyFromOrigKey(origKey, format, &key);
577 :
578 : // 1. Check the cache for a pre-existing one
579 0 : if (key.isValid()) {
580 0 : if (sk_sp<GrTextureProxy> proxy = ctx->resourceProvider()->findProxyByUniqueKey(key)) {
581 : SK_HISTOGRAM_ENUMERATION("LockTexturePath", kPreExisting_LockTexturePath,
582 : kLockTexturePathCount);
583 0 : return proxy;
584 : }
585 : }
586 :
587 : // The CachedFormat is both an index for which cache "slot" we'll use to store this particular
588 : // decoded variant of the encoded data, and also a recipe for how to transform the original
589 : // info to get the one that we're going to decode to.
590 0 : SkImageInfo cacheInfo = this->buildCacheInfo(format);
591 :
592 : // 2. Ask the generator to natively create one
593 : {
594 0 : ScopedGenerator generator(fSharedGenerator);
595 0 : if (sk_sp<GrTextureProxy> proxy = generator->generateTexture(ctx, cacheInfo, fOrigin)) {
596 : SK_HISTOGRAM_ENUMERATION("LockTexturePath", kNative_LockTexturePath,
597 : kLockTexturePathCount);
598 0 : set_key_on_proxy(ctx->resourceProvider(), proxy.get(), key);
599 0 : return proxy;
600 : }
601 : }
602 :
603 0 : const GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(cacheInfo, *ctx->caps());
604 :
605 : #ifdef SK_SUPPORT_COMPRESSED_TEXTURES_IN_CACHERATOR
606 : // 3. Ask the generator to return a compressed form that the GPU might support
607 : sk_sp<SkData> data(this->refEncoded(ctx));
608 : if (data) {
609 : GrTexture* tex = load_compressed_into_texture(ctx, data, desc);
610 : if (tex) {
611 : SK_HISTOGRAM_ENUMERATION("LockTexturePath", kCompressed_LockTexturePath,
612 : kLockTexturePathCount);
613 : return set_key_and_return(tex, key);
614 : }
615 : }
616 : #endif
617 :
618 : // 4. Ask the generator to return YUV planes, which the GPU can convert
619 0 : if (!ctx->contextPriv().disableGpuYUVConversion()) {
620 0 : ScopedGenerator generator(fSharedGenerator);
621 0 : Generator_GrYUVProvider provider(generator);
622 0 : if (sk_sp<GrTextureProxy> proxy = provider.refAsTextureProxy(ctx, desc, true)) {
623 : SK_HISTOGRAM_ENUMERATION("LockTexturePath", kYUV_LockTexturePath,
624 : kLockTexturePathCount);
625 0 : set_key_on_proxy(ctx->resourceProvider(), proxy.get(), key);
626 0 : return proxy;
627 : }
628 : }
629 :
630 : // 5. Ask the generator to return RGB(A) data, which the GPU can convert
631 0 : SkBitmap bitmap;
632 0 : if (this->tryLockAsBitmap(&bitmap, client, chint, format, cacheInfo)) {
633 0 : sk_sp<GrTextureProxy> proxy;
634 0 : if (willBeMipped) {
635 0 : proxy = GrGenerateMipMapsAndUploadToTextureProxy(ctx, bitmap, dstColorSpace);
636 : }
637 0 : if (!proxy) {
638 0 : proxy = GrUploadBitmapToTextureProxy(ctx->resourceProvider(), bitmap);
639 : }
640 0 : if (proxy) {
641 : SK_HISTOGRAM_ENUMERATION("LockTexturePath", kRGBA_LockTexturePath,
642 : kLockTexturePathCount);
643 0 : set_key_on_proxy(ctx->resourceProvider(), proxy.get(), key);
644 0 : return proxy;
645 : }
646 : }
647 : SK_HISTOGRAM_ENUMERATION("LockTexturePath", kFailure_LockTexturePath,
648 : kLockTexturePathCount);
649 0 : return nullptr;
650 : }
651 :
652 : ///////////////////////////////////////////////////////////////////////////////////////////////////
653 :
654 0 : sk_sp<GrTextureProxy> SkImageCacherator::lockAsTextureProxy(GrContext* ctx,
655 : const GrSamplerParams& params,
656 : SkColorSpace* dstColorSpace,
657 : sk_sp<SkColorSpace>* texColorSpace,
658 : const SkImage* client,
659 : SkScalar scaleAdjust[2],
660 : SkImage::CachingHint chint) {
661 0 : if (!ctx) {
662 0 : return nullptr;
663 : }
664 :
665 0 : return GrImageTextureMaker(ctx, this, client, chint).refTextureProxyForParams(params,
666 : dstColorSpace,
667 : texColorSpace,
668 0 : scaleAdjust);
669 : }
670 :
671 : #endif
|