2 * Copyright 2015 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
9 #include "SkBitmapCache.h"
10 #include "SkColorSpace_Base.h"
11 #include "SkImage_Base.h"
12 #include "SkImageCacherator.h"
13 #include "SkMallocPixelRef.h"
15 #include "SkPixelRef.h"
16 #include "SkResourceCache.h"
19 #include "GrContext.h"
20 #include "GrGpuResourcePriv.h"
21 #include "GrImageTextureMaker.h"
22 #include "GrResourceKey.h"
23 #include "GrSamplerParams.h"
24 #include "GrYUVProvider.h"
29 // Until we actually have codecs/etc. that can contain/support a GPU texture format
30 // skip this step, since for some generators, returning their encoded data as a SkData
31 // can be somewhat expensive, and this call doesn't indicate to the generator that we're
32 // only interested in GPU datas...
33 // see skbug.com/ 4971, 5128, ...
34 //#define SK_SUPPORT_COMPRESSED_TEXTURES_IN_CACHERATOR
36 // Helper for exclusive access to a shared generator.
37 class SkImageCacherator::ScopedGenerator {
39 ScopedGenerator(const sk_sp<SharedGenerator>& gen)
40 : fSharedGenerator(gen)
41 , fAutoAquire(gen->fMutex) {}
43 SkImageGenerator* operator->() const {
44 fSharedGenerator->fMutex.assertHeld();
45 return fSharedGenerator->fGenerator.get();
48 operator SkImageGenerator*() const {
49 fSharedGenerator->fMutex.assertHeld();
50 return fSharedGenerator->fGenerator.get();
54 const sk_sp<SharedGenerator>& fSharedGenerator;
55 SkAutoExclusive fAutoAquire;
58 SkImageCacherator::Validator::Validator(sk_sp<SharedGenerator> gen, const SkIRect* subset)
59 : fSharedGenerator(std::move(gen)) {
61 if (!fSharedGenerator) {
65 // The following generator accessors are safe without acquiring the mutex (const getters).
66 // TODO: refactor to use a ScopedGenerator instead, for clarity.
67 const SkImageInfo& info = fSharedGenerator->fGenerator->getInfo();
69 fSharedGenerator.reset();
73 fUniqueID = fSharedGenerator->fGenerator->uniqueID();
74 const SkIRect bounds = SkIRect::MakeWH(info.width(), info.height());
76 if (!bounds.contains(*subset)) {
77 fSharedGenerator.reset();
80 if (*subset != bounds) {
81 // we need a different uniqueID since we really are a subset of the raw generator
82 fUniqueID = SkNextID::ImageID();
88 fInfo = info.makeWH(subset->width(), subset->height());
89 fOrigin = SkIPoint::Make(subset->x(), subset->y());
91 // If the encoded data is in a strange color space (it's not an XYZ matrix space), we won't be
92 // able to preserve the gamut of the encoded data when we decode it. Instead, we'll have to
93 // decode to a known color space (linear sRGB is a good choice). But we need to adjust the
94 // stored color space, because drawing code will ask the SkImage for its color space, which
95 // will in turn ask the cacherator. If we return the A2B color space, then we will be unable to
96 // construct a source-to-dest gamut transformation matrix.
97 if (fInfo.colorSpace() &&
98 SkColorSpace_Base::Type::kXYZ != as_CSB(fInfo.colorSpace())->type()) {
99 fInfo = fInfo.makeColorSpace(SkColorSpace::MakeNamed(SkColorSpace::kSRGBLinear_Named));
103 SkImageCacherator* SkImageCacherator::NewFromGenerator(SkImageGenerator* gen,
104 const SkIRect* subset) {
105 Validator validator(SharedGenerator::Make(gen), subset);
107 return validator ? new SkImageCacherator(&validator) : nullptr;
110 SkImageCacherator::SkImageCacherator(Validator* validator)
111 : fSharedGenerator(std::move(validator->fSharedGenerator)) // we take ownership
112 , fInfo(validator->fInfo)
113 , fOrigin(validator->fOrigin)
115 fUniqueIDs[kLegacy_CachedFormat] = validator->fUniqueID;
116 for (int i = 1; i < kNumCachedFormats; ++i) {
117 // We lazily allocate IDs for non-default caching cases
118 fUniqueIDs[i] = kNeedNewImageUniqueID;
120 SkASSERT(fSharedGenerator);
123 SkImageCacherator::~SkImageCacherator() {}
125 SkData* SkImageCacherator::refEncoded(GrContext* ctx) {
126 ScopedGenerator generator(fSharedGenerator);
127 return generator->refEncodedData(ctx);
130 static bool check_output_bitmap(const SkBitmap& bitmap, uint32_t expectedID) {
131 SkASSERT(bitmap.getGenerationID() == expectedID);
132 SkASSERT(bitmap.isImmutable());
133 SkASSERT(bitmap.getPixels());
137 // Note, this returns a new, mutable, bitmap, with a new genID.
138 // If you want the immutable bitmap with the same ID as our cacherator, call tryLockAsBitmap()
140 bool SkImageCacherator::generateBitmap(SkBitmap* bitmap, const SkImageInfo& decodeInfo) {
141 SkBitmap::Allocator* allocator = SkResourceCache::GetAllocator();
143 ScopedGenerator generator(fSharedGenerator);
144 const SkImageInfo& genInfo = generator->getInfo();
145 if (decodeInfo.dimensions() == genInfo.dimensions()) {
146 SkASSERT(fOrigin.x() == 0 && fOrigin.y() == 0);
147 // fast-case, no copy needed
148 return generator->tryGenerateBitmap(bitmap, decodeInfo, allocator);
150 // need to handle subsetting, so we first generate the full size version, and then
151 // "read" from it to get our subset. See https://bug.skia.org/4213
154 if (!generator->tryGenerateBitmap(&full,
155 decodeInfo.makeWH(genInfo.width(), genInfo.height()),
159 if (!bitmap->tryAllocPixels(decodeInfo, nullptr, full.getColorTable())) {
162 return full.readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(),
163 fOrigin.x(), fOrigin.y());
167 bool SkImageCacherator::directGeneratePixels(const SkImageInfo& info, void* pixels, size_t rb,
168 int srcX, int srcY) {
169 ScopedGenerator generator(fSharedGenerator);
170 const SkImageInfo& genInfo = generator->getInfo();
171 // Currently generators do not natively handle subsets, so check that first.
172 if (srcX || srcY || genInfo.width() != info.width() || genInfo.height() != info.height()) {
175 return generator->getPixels(info, pixels, rb);
178 bool SkImageCacherator::directAccessScaledImage(const SkRect& srcRect,
179 const SkMatrix& totalMatrix,
181 SkImageGenerator::ScaledImageRec* rec) {
182 return ScopedGenerator(fSharedGenerator)->accessScaledImage(srcRect, totalMatrix, fq, rec);
185 //////////////////////////////////////////////////////////////////////////////////////////////////
187 bool SkImageCacherator::lockAsBitmapOnlyIfAlreadyCached(SkBitmap* bitmap, CachedFormat format) {
188 return kNeedNewImageUniqueID != fUniqueIDs[format] &&
189 SkBitmapCache::Find(fUniqueIDs[format], bitmap) &&
190 check_output_bitmap(*bitmap, fUniqueIDs[format]);
193 bool SkImageCacherator::tryLockAsBitmap(SkBitmap* bitmap, const SkImage* client,
194 SkImage::CachingHint chint, CachedFormat format,
195 const SkImageInfo& info) {
196 if (this->lockAsBitmapOnlyIfAlreadyCached(bitmap, format)) {
199 if (!this->generateBitmap(bitmap, info)) {
203 if (kNeedNewImageUniqueID == fUniqueIDs[format]) {
204 fUniqueIDs[format] = SkNextID::ImageID();
206 bitmap->pixelRef()->setImmutableWithID(fUniqueIDs[format]);
207 if (SkImage::kAllow_CachingHint == chint) {
208 SkBitmapCache::Add(fUniqueIDs[format], *bitmap);
210 as_IB(client)->notifyAddedToCache();
216 bool SkImageCacherator::lockAsBitmap(SkBitmap* bitmap, const SkImage* client,
217 SkColorSpace* dstColorSpace,
218 SkImage::CachingHint chint) {
219 CachedFormat format = this->chooseCacheFormat(dstColorSpace);
220 SkImageInfo cacheInfo = this->buildCacheInfo(format);
222 if (kNeedNewImageUniqueID == fUniqueIDs[format]) {
223 fUniqueIDs[format] = SkNextID::ImageID();
226 if (this->tryLockAsBitmap(bitmap, client, chint, format, cacheInfo)) {
227 return check_output_bitmap(*bitmap, fUniqueIDs[format]);
231 // Try to get a texture and read it back to raster (and then cache that with our ID)
232 sk_sp<GrTexture> tex;
235 ScopedGenerator generator(fSharedGenerator);
236 SkIRect subset = SkIRect::MakeXYWH(fOrigin.x(), fOrigin.y(),
237 cacheInfo.width(), cacheInfo.height());
238 tex.reset(generator->generateTexture(nullptr, &subset));
245 if (!bitmap->tryAllocPixels(cacheInfo)) {
250 const uint32_t pixelOpsFlags = 0;
251 if (!tex->readPixels(0, 0, bitmap->width(), bitmap->height(),
252 SkImageInfo2GrPixelConfig(cacheInfo, *tex->getContext()->caps()),
253 bitmap->getPixels(), bitmap->rowBytes(), pixelOpsFlags)) {
258 bitmap->pixelRef()->setImmutableWithID(fUniqueIDs[format]);
259 if (SkImage::kAllow_CachingHint == chint) {
260 SkBitmapCache::Add(fUniqueIDs[format], *bitmap);
262 as_IB(client)->notifyAddedToCache();
265 return check_output_bitmap(*bitmap, fUniqueIDs[format]);
271 //////////////////////////////////////////////////////////////////////////////////////////////////
273 // Abstraction of GrCaps that handles the cases where we don't have a caps pointer (because
274 // we're in raster mode), or where GPU support is entirely missing. In theory, we only need the
275 // chosen format to be texturable, but that lets us choose F16 on GLES implemenations where we
276 // won't be able to read the texture back. We'd like to ensure that SkImake::makeNonTextureImage
277 // works, so we require that the formats we choose are renderable (as a proxy for being readable).
279 CacheCaps(const GrCaps* caps) : fCaps(caps) {}
282 bool supportsHalfFloat() const {
284 (fCaps->isConfigTexturable(kRGBA_half_GrPixelConfig) &&
285 fCaps->isConfigRenderable(kRGBA_half_GrPixelConfig, false));
288 bool supportsSRGB() const {
290 (fCaps->srgbSupport() && fCaps->isConfigTexturable(kSRGBA_8888_GrPixelConfig));
293 bool supportsSBGR() const {
294 return !fCaps || fCaps->srgbSupport();
297 bool supportsHalfFloat() const { return true; }
298 bool supportsSRGB() const { return true; }
299 bool supportsSBGR() const { return true; }
305 SkImageCacherator::CachedFormat SkImageCacherator::chooseCacheFormat(SkColorSpace* dstColorSpace,
306 const GrCaps* grCaps) {
307 SkColorSpace* cs = fInfo.colorSpace();
308 if (!cs || !dstColorSpace) {
309 return kLegacy_CachedFormat;
312 CacheCaps caps(grCaps);
313 switch (fInfo.colorType()) {
314 case kUnknown_SkColorType:
315 case kAlpha_8_SkColorType:
316 case kRGB_565_SkColorType:
317 case kARGB_4444_SkColorType:
318 // We don't support color space on these formats, so always decode in legacy mode:
319 // TODO: Ask the codec to decode these to something else (at least sRGB 8888)?
320 return kLegacy_CachedFormat;
322 case kIndex_8_SkColorType:
323 // We can't draw from indexed textures with a color space, so ask the codec to expand
324 if (cs->gammaCloseToSRGB()) {
325 if (caps.supportsSRGB()) {
326 return kSRGB8888_CachedFormat;
327 } else if (caps.supportsHalfFloat()) {
328 return kLinearF16_CachedFormat;
330 return kLegacy_CachedFormat;
333 if (caps.supportsHalfFloat()) {
334 return kLinearF16_CachedFormat;
335 } else if (caps.supportsSRGB()) {
336 return kSRGB8888_CachedFormat;
338 return kLegacy_CachedFormat;
342 case kGray_8_SkColorType:
343 // TODO: What do we do with grayscale sources that have strange color spaces attached?
344 // The codecs and color space xform don't handle this correctly (yet), so drop it on
345 // the floor. (Also, inflating by a factor of 8 is going to be unfortunate).
346 // As it is, we don't directly support sRGB grayscale, so ask the codec to convert
347 // it for us. This bypasses some really sketchy code GrUploadPixmapToTexture.
348 if (cs->gammaCloseToSRGB() && caps.supportsSRGB()) {
349 return kSRGB8888_CachedFormat;
351 return kLegacy_CachedFormat;
354 case kRGBA_8888_SkColorType:
355 if (cs->gammaCloseToSRGB()) {
356 if (caps.supportsSRGB()) {
357 return kAsIs_CachedFormat;
358 } else if (caps.supportsHalfFloat()) {
359 return kLinearF16_CachedFormat;
361 return kLegacy_CachedFormat;
364 if (caps.supportsHalfFloat()) {
365 return kLinearF16_CachedFormat;
366 } else if (caps.supportsSRGB()) {
367 return kSRGB8888_CachedFormat;
369 return kLegacy_CachedFormat;
373 case kBGRA_8888_SkColorType:
374 // Odd case. sBGRA isn't a real thing, so we may not have this texturable.
375 if (caps.supportsSBGR()) {
376 if (cs->gammaCloseToSRGB()) {
377 return kAsIs_CachedFormat;
378 } else if (caps.supportsHalfFloat()) {
379 return kLinearF16_CachedFormat;
380 } else if (caps.supportsSRGB()) {
381 return kSRGB8888_CachedFormat;
383 // sBGRA support without sRGBA is highly unlikely (impossible?) Nevertheless.
384 return kLegacy_CachedFormat;
387 if (cs->gammaCloseToSRGB()) {
388 if (caps.supportsSRGB()) {
389 return kSRGB8888_CachedFormat;
390 } else if (caps.supportsHalfFloat()) {
391 return kLinearF16_CachedFormat;
393 return kLegacy_CachedFormat;
396 if (caps.supportsHalfFloat()) {
397 return kLinearF16_CachedFormat;
398 } else if (caps.supportsSRGB()) {
399 return kSRGB8888_CachedFormat;
401 return kLegacy_CachedFormat;
406 case kRGBA_F16_SkColorType:
407 if (!caps.supportsHalfFloat()) {
408 if (caps.supportsSRGB()) {
409 return kSRGB8888_CachedFormat;
411 return kLegacy_CachedFormat;
413 } else if (cs->gammaIsLinear()) {
414 return kAsIs_CachedFormat;
416 return kLinearF16_CachedFormat;
419 SkDEBUGFAIL("Unreachable");
420 return kLegacy_CachedFormat;
423 SkImageInfo SkImageCacherator::buildCacheInfo(CachedFormat format) {
425 case kLegacy_CachedFormat:
426 return fInfo.makeColorSpace(nullptr);
427 case kAsIs_CachedFormat:
429 case kLinearF16_CachedFormat:
431 .makeColorType(kRGBA_F16_SkColorType)
432 .makeColorSpace(as_CSB(fInfo.colorSpace())->makeLinearGamma());
433 case kSRGB8888_CachedFormat:
435 .makeColorType(kRGBA_8888_SkColorType)
436 .makeColorSpace(as_CSB(fInfo.colorSpace())->makeSRGBGamma());
438 SkDEBUGFAIL("Invalid cached format");
443 //////////////////////////////////////////////////////////////////////////////////////////////////
447 void SkImageCacherator::makeCacheKeyFromOrigKey(const GrUniqueKey& origKey, CachedFormat format,
448 GrUniqueKey* cacheKey) {
449 SkASSERT(!cacheKey->isValid());
450 if (origKey.isValid()) {
451 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
452 GrUniqueKey::Builder builder(cacheKey, origKey, kDomain, 1);
457 #ifdef SK_SUPPORT_COMPRESSED_TEXTURES_IN_CACHERATOR
458 static GrTexture* load_compressed_into_texture(GrContext* ctx, SkData* data, GrSurfaceDesc desc) {
459 const void* rawStart;
460 GrPixelConfig config = GrIsCompressedTextureDataSupported(ctx, data, desc.fWidth, desc.fHeight,
462 if (kUnknown_GrPixelConfig == config) {
466 desc.fConfig = config;
467 return ctx->textureProvider()->createTexture(desc, SkBudgeted::kYes, rawStart, 0);
471 class Generator_GrYUVProvider : public GrYUVProvider {
472 SkImageGenerator* fGen;
475 Generator_GrYUVProvider(SkImageGenerator* gen) : fGen(gen) {}
477 uint32_t onGetID() override { return fGen->uniqueID(); }
478 bool onQueryYUV8(SkYUVSizeInfo* sizeInfo, SkYUVColorSpace* colorSpace) const override {
479 return fGen->queryYUV8(sizeInfo, colorSpace);
481 bool onGetYUV8Planes(const SkYUVSizeInfo& sizeInfo, void* planes[3]) override {
482 return fGen->getYUV8Planes(sizeInfo, planes);
486 static GrTexture* set_key_and_return(GrTexture* tex, const GrUniqueKey& key) {
488 tex->resourcePriv().setUniqueKey(key);
493 sk_sp<SkColorSpace> SkImageCacherator::getColorSpace(GrContext* ctx, SkColorSpace* dstColorSpace) {
494 // TODO: This isn't always correct. Picture generator currently produces textures in N32,
495 // and will (soon) emit them in an arbitrary (destination) space. We will need to stash that
496 // information in/on the key so we can return the correct space in case #1 of lockTexture.
497 CachedFormat format = this->chooseCacheFormat(dstColorSpace, ctx->caps());
498 SkImageInfo cacheInfo = this->buildCacheInfo(format);
499 return sk_ref_sp(cacheInfo.colorSpace());
503 * We have a 5 ways to try to return a texture (in sorted order)
505 * 1. Check the cache for a pre-existing one
506 * 2. Ask the generator to natively create one
507 * 3. Ask the generator to return a compressed form that the GPU might support
508 * 4. Ask the generator to return YUV planes, which the GPU can convert
509 * 5. Ask the generator to return RGB(A) data, which the GPU can convert
511 GrTexture* SkImageCacherator::lockTexture(GrContext* ctx, const GrUniqueKey& origKey,
512 const SkImage* client, SkImage::CachingHint chint,
513 bool willBeMipped, SkColorSpace* dstColorSpace) {
514 // Values representing the various texture lock paths we can take. Used for logging the path
515 // taken to a histogram.
516 enum LockTexturePath {
517 kFailure_LockTexturePath,
518 kPreExisting_LockTexturePath,
519 kNative_LockTexturePath,
520 kCompressed_LockTexturePath,
521 kYUV_LockTexturePath,
522 kRGBA_LockTexturePath,
525 enum { kLockTexturePathCount = kRGBA_LockTexturePath + 1 };
527 // Determine which cached format we're going to use (which may involve decoding to a different
528 // info than the generator provides).
529 CachedFormat format = this->chooseCacheFormat(dstColorSpace, ctx->caps());
531 // Fold the cache format into our texture key
533 this->makeCacheKeyFromOrigKey(origKey, format, &key);
535 // 1. Check the cache for a pre-existing one
537 if (GrTexture* tex = ctx->textureProvider()->findAndRefTextureByUniqueKey(key)) {
538 SK_HISTOGRAM_ENUMERATION("LockTexturePath", kPreExisting_LockTexturePath,
539 kLockTexturePathCount);
544 // 2. Ask the generator to natively create one
546 ScopedGenerator generator(fSharedGenerator);
547 SkIRect subset = SkIRect::MakeXYWH(fOrigin.x(), fOrigin.y(), fInfo.width(), fInfo.height());
548 if (GrTexture* tex = generator->generateTexture(ctx, &subset)) {
549 SK_HISTOGRAM_ENUMERATION("LockTexturePath", kNative_LockTexturePath,
550 kLockTexturePathCount);
551 return set_key_and_return(tex, key);
555 // The CachedFormat is both an index for which cache "slot" we'll use to store this particular
556 // decoded variant of the encoded data, and also a recipe for how to transform the original
557 // info to get the one that we're going to decode to.
558 SkImageInfo cacheInfo = this->buildCacheInfo(format);
560 const GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(cacheInfo, *ctx->caps());
562 #ifdef SK_SUPPORT_COMPRESSED_TEXTURES_IN_CACHERATOR
563 // 3. Ask the generator to return a compressed form that the GPU might support
564 sk_sp<SkData> data(this->refEncoded(ctx));
566 GrTexture* tex = load_compressed_into_texture(ctx, data, desc);
568 SK_HISTOGRAM_ENUMERATION("LockTexturePath", kCompressed_LockTexturePath,
569 kLockTexturePathCount);
570 return set_key_and_return(tex, key);
575 // 4. Ask the generator to return YUV planes, which the GPU can convert
577 ScopedGenerator generator(fSharedGenerator);
578 Generator_GrYUVProvider provider(generator);
579 sk_sp<GrTexture> tex = provider.refAsTexture(ctx, desc, true);
581 SK_HISTOGRAM_ENUMERATION("LockTexturePath", kYUV_LockTexturePath,
582 kLockTexturePathCount);
583 return set_key_and_return(tex.release(), key);
587 // 5. Ask the generator to return RGB(A) data, which the GPU can convert
589 if (this->tryLockAsBitmap(&bitmap, client, chint, format, cacheInfo)) {
590 GrTexture* tex = nullptr;
592 tex = GrGenerateMipMapsAndUploadToTexture(ctx, bitmap, dstColorSpace);
595 tex = GrUploadBitmapToTexture(ctx, bitmap);
598 SK_HISTOGRAM_ENUMERATION("LockTexturePath", kRGBA_LockTexturePath,
599 kLockTexturePathCount);
600 return set_key_and_return(tex, key);
603 SK_HISTOGRAM_ENUMERATION("LockTexturePath", kFailure_LockTexturePath,
604 kLockTexturePathCount);
608 ///////////////////////////////////////////////////////////////////////////////////////////////////
610 GrTexture* SkImageCacherator::lockAsTexture(GrContext* ctx, const GrSamplerParams& params,
611 SkColorSpace* dstColorSpace,
612 sk_sp<SkColorSpace>* texColorSpace,
613 const SkImage* client, SkImage::CachingHint chint) {
618 return GrImageTextureMaker(ctx, this, client, chint).refTextureForParams(params, dstColorSpace,
624 GrTexture* SkImageCacherator::lockAsTexture(GrContext* ctx, const GrSamplerParams&,
625 SkColorSpace* dstColorSpace,
626 sk_sp<SkColorSpace>* texColorSpace,
627 const SkImage* client, SkImage::CachingHint) {