GrTexture* GrTextureMaker::refTextureForParams(const GrTextureParams& params, SkSourceGammaTreatment gammaTreatment) { CopyParams copyParams; bool willBeMipped = params.filterMode() == GrTextureParams::kMipMap_FilterMode; if (!fContext->caps()->mipMapSupport()) { willBeMipped = false; } if (!fContext->getGpu()->makeCopyForTextureParams(this->width(), this->height(), params, ©Params)) { return this->refOriginalTexture(willBeMipped, gammaTreatment); } GrUniqueKey copyKey; this->makeCopyKey(copyParams, ©Key); if (copyKey.isValid()) { GrTexture* result = fContext->textureProvider()->findAndRefTextureByUniqueKey(copyKey); if (result) { return result; } } GrTexture* result = this->generateTextureForParams(copyParams, willBeMipped, gammaTreatment); if (!result) { return nullptr; } if (copyKey.isValid()) { fContext->textureProvider()->assignUniqueKeyToTexture(copyKey, result); this->didCacheCopy(copyKey); } return result; }
GrTexture* GrTextureMaker::refCachedTexture(GrContext* ctx, const GrTextureParams* params) { SkGrStretch stretch; get_stretch(ctx, this->width(), this->height(), params, &stretch); if (SkGrStretch::kNone_Type == stretch.fType) { return this->onRefUnstretchedTexture(ctx); } GrUniqueKey stretchedKey; if (this->onMakeStretchedKey(stretch, &stretchedKey)) { GrTexture* result = ctx->textureProvider()->findAndRefTextureByUniqueKey(stretchedKey); if (result) { return result; } } GrTexture* result = this->onGenerateStretchedTexture(ctx, stretch); if (!result) { return nullptr; } if (stretchedKey.isValid()) { ctx->textureProvider()->assignUniqueKeyToTexture(stretchedKey, result); this->onNotifyStretchCached(stretchedKey); } return result; }
GrTexture* GrTextureMaker::refTextureForParams(const GrTextureParams& params) { CopyParams copyParams; if (!fContext->getGpu()->makeCopyForTextureParams(this->width(), this->height(), params, ©Params)) { return this->refOriginalTexture(); } GrUniqueKey copyKey; this->makeCopyKey(copyParams, ©Key); if (copyKey.isValid()) { GrTexture* result = fContext->textureProvider()->findAndRefTextureByUniqueKey(copyKey); if (result) { return result; } } GrTexture* result = this->generateTextureForParams(copyParams); if (!result) { return nullptr; } if (copyKey.isValid()) { fContext->textureProvider()->assignUniqueKeyToTexture(copyKey, result); this->didCacheCopy(copyKey); } return result; }
sk_sp<GrTextureProxy> GrTextureAdjuster::refTextureProxyCopy(const CopyParams& copyParams, bool willBeMipped) { GrProxyProvider* proxyProvider = fContext->contextPriv().proxyProvider(); GrUniqueKey key; this->makeCopyKey(copyParams, &key, nullptr); if (key.isValid()) { sk_sp<GrTextureProxy> cachedCopy = proxyProvider->findOrCreateProxyByUniqueKey(key, this->originalProxy()->origin()); if (cachedCopy) { return cachedCopy; } } sk_sp<GrTextureProxy> proxy = this->originalProxyRef(); sk_sp<GrTextureProxy> copy = CopyOnGpu(fContext, std::move(proxy), copyParams, willBeMipped); if (copy) { if (key.isValid()) { SkASSERT(copy->origin() == this->originalProxy()->origin()); proxyProvider->assignUniqueKeyToProxy(key, copy.get()); this->didCacheCopy(key); } } return copy; }
void draw(Target* target, const GrGeometryProcessor* gp) const { GrResourceProvider* rp = target->resourceProvider(); SkScalar screenSpaceTol = GrPathUtils::kDefaultTolerance; SkScalar tol = GrPathUtils::scaleToleranceToSrc(screenSpaceTol, fViewMatrix, fShape.bounds()); SkPath path; fShape.asPath(&path); bool inverseFill = path.isInverseFillType(); // construct a cache key from the path's genID and the view matrix static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain(); GrUniqueKey key; static constexpr int kClipBoundsCnt = sizeof(fClipBounds) / sizeof(uint32_t); int shapeKeyDataCnt = fShape.unstyledKeySize(); SkASSERT(shapeKeyDataCnt >= 0); GrUniqueKey::Builder builder(&key, kDomain, shapeKeyDataCnt + kClipBoundsCnt); fShape.writeUnstyledKey(&builder[0]); // For inverse fills, the tessellation is dependent on clip bounds. if (inverseFill) { memcpy(&builder[shapeKeyDataCnt], &fClipBounds, sizeof(fClipBounds)); } else { memset(&builder[shapeKeyDataCnt], 0, sizeof(fClipBounds)); } builder.finish(); SkAutoTUnref<GrBuffer> cachedVertexBuffer(rp->findAndRefTByUniqueKey<GrBuffer>(key)); int actualCount; if (cache_match(cachedVertexBuffer.get(), tol, &actualCount)) { this->drawVertices(target, gp, cachedVertexBuffer.get(), 0, actualCount); return; } bool isLinear; bool canMapVB = GrCaps::kNone_MapFlags != target->caps().mapBufferFlags(); StaticVertexAllocator allocator(rp, canMapVB); int count = GrTessellator::PathToTriangles(path, tol, fClipBounds, &allocator, &isLinear); if (count == 0) { return; } this->drawVertices(target, gp, allocator.vertexBuffer(), 0, count); TessInfo info; info.fTolerance = isLinear ? 0 : tol; info.fCount = count; SkAutoTUnref<SkData> data(SkData::NewWithCopy(&info, sizeof(info))); key.setCustomData(data.get()); rp->assignUniqueKeyToResource(key, allocator.vertexBuffer()); }
void SkImage_Lazy::makeCacheKeyFromOrigKey(const GrUniqueKey& origKey, CachedFormat format, GrUniqueKey* cacheKey) { SkASSERT(!cacheKey->isValid()); if (origKey.isValid()) { static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain(); GrUniqueKey::Builder builder(cacheKey, origKey, kDomain, 1, "Image"); builder[0] = format; } }
GrTexture* GrTextureAdjuster::refCopy(const CopyParams& copyParams) { GrTexture* texture = this->originalTexture(); GrContext* context = texture->getContext(); const SkIRect* contentArea = this->contentAreaOrNull(); GrUniqueKey key; this->makeCopyKey(copyParams, &key); if (key.isValid()) { GrTexture* cachedCopy = context->textureProvider()->findAndRefTextureByUniqueKey(key); if (cachedCopy) { return cachedCopy; } } GrTexture* copy = copy_on_gpu(texture, contentArea, copyParams); if (copy) { if (key.isValid()) { copy->resourcePriv().setUniqueKey(key); this->didCacheCopy(key); } } return copy; }
static bool make_stretched_key(const GrUniqueKey& origKey, Stretch stretch, GrUniqueKey* stretchedKey) { if (origKey.isValid() && kNo_Stretch != stretch) { static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain(); GrUniqueKey::Builder builder(stretchedKey, origKey, kDomain, 1); builder[0] = stretch; builder.finish(); return true; } SkASSERT(!stretchedKey->isValid()); return false; }
sk_sp<GrTextureProxy> GrMakeCachedImageProxy(GrProxyProvider* proxyProvider, sk_sp<SkImage> srcImage, SkBackingFit fit) { sk_sp<GrTextureProxy> proxy; GrUniqueKey originalKey; create_unique_key_for_image(srcImage.get(), &originalKey); if (originalKey.isValid()) { proxy = proxyProvider->findOrCreateProxyByUniqueKey(originalKey, kTopLeft_GrSurfaceOrigin); } if (!proxy) { proxy = proxyProvider->createTextureProxy(std::move(srcImage), kNone_GrSurfaceFlags, kTopLeft_GrSurfaceOrigin, 1, SkBudgeted::kYes, fit); if (proxy && originalKey.isValid()) { proxyProvider->assignUniqueKeyToProxy(originalKey, proxy.get()); } } return proxy; }
bool GrMakeStretchedKey(const GrUniqueKey& origKey, const SkGrStretch& stretch, GrUniqueKey* stretchedKey) { if (origKey.isValid() && SkGrStretch::kNone_Type != stretch.fType) { uint32_t width = SkToU16(stretch.fWidth); uint32_t height = SkToU16(stretch.fHeight); static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain(); GrUniqueKey::Builder builder(stretchedKey, origKey, kDomain, 2); builder[0] = stretch.fType; builder[1] = width | (height << 16); builder.finish(); return true; } SkASSERT(!stretchedKey->isValid()); return false; }
void GrGpuResource::setUniqueKey(const GrUniqueKey& key) { SkASSERT(this->internalHasRef()); SkASSERT(key.isValid()); // Wrapped and uncached resources can never have a unique key. if (SkBudgeted::kNo == this->resourcePriv().isBudgeted()) { return; } if (this->wasDestroyed()) { return; } get_resource_cache(fGpu)->resourceAccess().changeUniqueKey(this, key); }
static void set_key_on_proxy(GrProxyProvider* proxyProvider, GrTextureProxy* proxy, GrTextureProxy* originalProxy, const GrUniqueKey& key) { if (key.isValid()) { SkASSERT(proxy->origin() == kTopLeft_GrSurfaceOrigin); if (originalProxy && originalProxy->getUniqueKey().isValid()) { SkASSERT(originalProxy->getUniqueKey() == key); SkASSERT(GrMipMapped::kYes == proxy->mipMapped() && GrMipMapped::kNo == originalProxy->mipMapped()); // If we had an originalProxy with a valid key, that means there already is a proxy in // the cache which matches the key, but it does not have mip levels and we require them. // Thus we must remove the unique key from that proxy. proxyProvider->removeUniqueKeyFromProxy(key, originalProxy); } proxyProvider->assignUniqueKeyToProxy(key, proxy); } }
/* * We have 4 ways to try to return a texture (in sorted order) * * 1. Check the cache for a pre-existing one * 2. Ask the generator to natively create one * 3. Ask the generator to return YUV planes, which the GPU can convert * 4. Ask the generator to return RGB(A) data, which the GPU can convert */ sk_sp<GrTextureProxy> SkImage_Lazy::lockTextureProxy(GrContext* ctx, const GrUniqueKey& origKey, SkImage::CachingHint chint, bool willBeMipped, SkColorSpace* dstColorSpace, GrTextureMaker::AllowedTexGenType genType) { // Values representing the various texture lock paths we can take. Used for logging the path // taken to a histogram. enum LockTexturePath { kFailure_LockTexturePath, kPreExisting_LockTexturePath, kNative_LockTexturePath, kCompressed_LockTexturePath, // Deprecated kYUV_LockTexturePath, kRGBA_LockTexturePath, }; enum { kLockTexturePathCount = kRGBA_LockTexturePath + 1 }; // Determine which cached format we're going to use (which may involve decoding to a different // info than the generator provides). CachedFormat format = this->chooseCacheFormat(dstColorSpace, ctx->caps()); // Fold the cache format into our texture key GrUniqueKey key; this->makeCacheKeyFromOrigKey(origKey, format, &key); GrProxyProvider* proxyProvider = ctx->contextPriv().proxyProvider(); sk_sp<GrTextureProxy> proxy; // 1. Check the cache for a pre-existing one if (key.isValid()) { proxy = proxyProvider->findOrCreateProxyByUniqueKey(key, kTopLeft_GrSurfaceOrigin); if (proxy) { SK_HISTOGRAM_ENUMERATION("LockTexturePath", kPreExisting_LockTexturePath, kLockTexturePathCount); if (!willBeMipped || GrMipMapped::kYes == proxy->mipMapped()) { return proxy; } } } // The CachedFormat is both an index for which cache "slot" we'll use to store this particular // decoded variant of the encoded data, and also a recipe for how to transform the original // info to get the one that we're going to decode to. const SkImageInfo cacheInfo = this->buildCacheInfo(format); SkImageInfo genPixelsInfo = cacheInfo; SkTransferFunctionBehavior behavior = getGeneratorBehaviorAndInfo(&genPixelsInfo); // 2. Ask the generator to natively create one if (!proxy) { ScopedGenerator generator(fSharedGenerator); if (GrTextureMaker::AllowedTexGenType::kCheap == genType && SkImageGenerator::TexGenType::kCheap != generator->onCanGenerateTexture()) { return nullptr; } if ((proxy = generator->generateTexture(ctx, genPixelsInfo, fOrigin, behavior, willBeMipped))) { SK_HISTOGRAM_ENUMERATION("LockTexturePath", kNative_LockTexturePath, kLockTexturePathCount); set_key_on_proxy(proxyProvider, proxy.get(), nullptr, key); if (!willBeMipped || GrMipMapped::kYes == proxy->mipMapped()) { return proxy; } } } // 3. Ask the generator to return YUV planes, which the GPU can convert. If we will be mipping // the texture we fall through here and have the CPU generate the mip maps for us. if (!proxy && !willBeMipped && !ctx->contextPriv().disableGpuYUVConversion()) { const GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(cacheInfo, *ctx->caps()); ScopedGenerator generator(fSharedGenerator); Generator_GrYUVProvider provider(generator); // The pixels in the texture will be in the generator's color space. If onMakeColorSpace // has been called then this will not match this image's color space. To correct this, apply // a color space conversion from the generator's color space to this image's color space. const SkColorSpace* generatorColorSpace = fSharedGenerator->fGenerator->getInfo().colorSpace(); const SkColorSpace* thisColorSpace = fInfo.colorSpace(); // TODO: Update to create the mipped surface in the YUV generator and draw the base layer // directly into the mipped surface. proxy = provider.refAsTextureProxy(ctx, desc, generatorColorSpace, thisColorSpace); if (proxy) { SK_HISTOGRAM_ENUMERATION("LockTexturePath", kYUV_LockTexturePath, kLockTexturePathCount); set_key_on_proxy(proxyProvider, proxy.get(), nullptr, key); return proxy; } } // 4. Ask the generator to return RGB(A) data, which the GPU can convert SkBitmap bitmap; if (!proxy && this->lockAsBitmap(&bitmap, chint, format, genPixelsInfo, behavior)) { if (willBeMipped) { proxy = proxyProvider->createMipMapProxyFromBitmap(bitmap, dstColorSpace); } if (!proxy) { proxy = GrUploadBitmapToTextureProxy(proxyProvider, bitmap, dstColorSpace); } if (proxy && (!willBeMipped || GrMipMapped::kYes == proxy->mipMapped())) { SK_HISTOGRAM_ENUMERATION("LockTexturePath", kRGBA_LockTexturePath, kLockTexturePathCount); set_key_on_proxy(proxyProvider, proxy.get(), nullptr, key); return proxy; } } if (proxy) { // We need a mipped proxy, but we either found a proxy earlier that wasn't mipped, generated // a native non mipped proxy, or generated a non-mipped yuv proxy. Thus we generate a new // mipped surface and copy the original proxy into the base layer. We will then let the gpu // generate the rest of the mips. SkASSERT(willBeMipped); SkASSERT(GrMipMapped::kNo == proxy->mipMapped()); if (auto mippedProxy = GrCopyBaseMipMapToTextureProxy(ctx, proxy.get())) { set_key_on_proxy(proxyProvider, mippedProxy.get(), proxy.get(), key); return mippedProxy; } // We failed to make a mipped proxy with the base copied into it. This could have // been from failure to make the proxy or failure to do the copy. Thus we will fall // back to just using the non mipped proxy; See skbug.com/7094. return proxy; } SK_HISTOGRAM_ENUMERATION("LockTexturePath", kFailure_LockTexturePath, kLockTexturePathCount); return nullptr; }
void draw(Target* target, const GrGeometryProcessor* gp) const { GrResourceProvider* rp = target->resourceProvider(); SkScalar screenSpaceTol = GrPathUtils::kDefaultTolerance; SkScalar tol = GrPathUtils::scaleToleranceToSrc(screenSpaceTol, fViewMatrix, fPath.getBounds()); SkScalar styleScale = SK_Scalar1; if (fStyle.applies()) { styleScale = GrStyle::MatrixToScaleFactor(fViewMatrix); } // construct a cache key from the path's genID and the view matrix static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain(); GrUniqueKey key; int clipBoundsCnt = fPath.isInverseFillType() ? sizeof(fClipBounds) / sizeof(uint32_t) : 0; int styleDataCnt = GrStyle::KeySize(fStyle, GrStyle::Apply::kPathEffectAndStrokeRec); if (styleDataCnt >= 0) { GrUniqueKey::Builder builder(&key, kDomain, 2 + clipBoundsCnt + styleDataCnt); builder[0] = fPath.getGenerationID(); builder[1] = fPath.getFillType(); // For inverse fills, the tessellation is dependent on clip bounds. if (fPath.isInverseFillType()) { memcpy(&builder[2], &fClipBounds, sizeof(fClipBounds)); } if (styleDataCnt) { GrStyle::WriteKey(&builder[2 + clipBoundsCnt], fStyle, GrStyle::Apply::kPathEffectAndStrokeRec, styleScale); } builder.finish(); SkAutoTUnref<GrBuffer> cachedVertexBuffer(rp->findAndRefTByUniqueKey<GrBuffer>(key)); int actualCount; if (cache_match(cachedVertexBuffer.get(), tol, &actualCount)) { this->drawVertices(target, gp, cachedVertexBuffer.get(), 0, actualCount); return; } } SkPath path; if (fStyle.applies()) { SkStrokeRec::InitStyle fill; SkAssertResult(fStyle.applyToPath(&path, &fill, fPath, styleScale)); SkASSERT(SkStrokeRec::kFill_InitStyle == fill); } else { path = fPath; } bool isLinear; bool canMapVB = GrCaps::kNone_MapFlags != target->caps().mapBufferFlags(); StaticVertexAllocator allocator(rp, canMapVB); int count = GrTessellator::PathToTriangles(path, tol, fClipBounds, &allocator, &isLinear); if (count == 0) { return; } this->drawVertices(target, gp, allocator.vertexBuffer(), 0, count); if (!fPath.isVolatile() && styleDataCnt >= 0) { TessInfo info; info.fTolerance = isLinear ? 0 : tol; info.fCount = count; SkAutoTUnref<SkData> data(SkData::NewWithCopy(&info, sizeof(info))); key.setCustomData(data.get()); rp->assignUniqueKeyToResource(key, allocator.vertexBuffer()); SkPathPriv::AddGenIDChangeListener(fPath, new PathInvalidator(key)); } }
// This tests the basic capabilities of the uniquely keyed texture proxies. Does assigning // and looking them up work, etc. static void basic_test(GrContext* context, skiatest::Reporter* reporter, sk_sp<GrTextureProxy> proxy) { static int id = 1; GrResourceProvider* resourceProvider = context->priv().resourceProvider(); GrProxyProvider* proxyProvider = context->priv().proxyProvider(); GrResourceCache* cache = context->priv().getResourceCache(); int startCacheCount = cache->getResourceCount(); GrUniqueKey key; if (proxy->getUniqueKey().isValid()) { key = proxy->getUniqueKey(); } else { GrMakeKeyFromImageID(&key, id, SkIRect::MakeWH(64, 64)); ++id; // Assigning the uniqueKey adds the proxy to the hash but doesn't force instantiation REPORTER_ASSERT(reporter, !proxyProvider->numUniqueKeyProxies_TestOnly()); SkAssertResult(proxyProvider->assignUniqueKeyToProxy(key, proxy.get())); } REPORTER_ASSERT(reporter, 1 == proxyProvider->numUniqueKeyProxies_TestOnly()); REPORTER_ASSERT(reporter, startCacheCount == cache->getResourceCount()); // setUniqueKey had better stick REPORTER_ASSERT(reporter, key == proxy->getUniqueKey()); // We just added it, surely we can find it REPORTER_ASSERT(reporter, proxyProvider->findOrCreateProxyByUniqueKey( key, kBottomLeft_GrSurfaceOrigin)); REPORTER_ASSERT(reporter, 1 == proxyProvider->numUniqueKeyProxies_TestOnly()); int expectedCacheCount = startCacheCount + (proxy->isInstantiated() ? 0 : 1); // Once instantiated, the backing resource should have the same key SkAssertResult(proxy->instantiate(resourceProvider)); const GrUniqueKey texKey = proxy->peekSurface()->getUniqueKey(); REPORTER_ASSERT(reporter, texKey.isValid()); REPORTER_ASSERT(reporter, key == texKey); // An Unbudgeted-cacheable resource will not get purged when a proxy with the same key is // deleted. bool expectResourceToOutliveProxy = proxy->peekSurface()->resourcePriv().budgetedType() == GrBudgetedType::kUnbudgetedCacheable; // An Unbudgeted-uncacheable resource is never kept alive if it's ref cnt reaches zero even if // it has a key. bool expectDeletingProxyToDeleteResource = proxy->peekSurface()->resourcePriv().budgetedType() == GrBudgetedType::kUnbudgetedUncacheable; // deleting the proxy should delete it from the hash but not the cache proxy = nullptr; if (expectDeletingProxyToDeleteResource) { expectedCacheCount -= 1; } REPORTER_ASSERT(reporter, 0 == proxyProvider->numUniqueKeyProxies_TestOnly()); REPORTER_ASSERT(reporter, expectedCacheCount == cache->getResourceCount()); // If the proxy was cached refinding it should bring it back to life proxy = proxyProvider->findOrCreateProxyByUniqueKey(key, kBottomLeft_GrSurfaceOrigin); REPORTER_ASSERT(reporter, proxy); REPORTER_ASSERT(reporter, 1 == proxyProvider->numUniqueKeyProxies_TestOnly()); REPORTER_ASSERT(reporter, expectedCacheCount == cache->getResourceCount()); // Mega-purging it should remove it from both the hash and the cache proxy = nullptr; cache->purgeAllUnlocked(); if (!expectResourceToOutliveProxy) { expectedCacheCount--; } REPORTER_ASSERT(reporter, expectedCacheCount == cache->getResourceCount()); // If the texture was deleted then the proxy should no longer be findable. Otherwise, it should // be. proxy = proxyProvider->findOrCreateProxyByUniqueKey(key, kBottomLeft_GrSurfaceOrigin); REPORTER_ASSERT(reporter, expectResourceToOutliveProxy ? (bool)proxy : !proxy); REPORTER_ASSERT(reporter, expectedCacheCount == cache->getResourceCount()); if (expectResourceToOutliveProxy) { proxy.reset(); GrUniqueKeyInvalidatedMessage msg(texKey, context->priv().contextID()); SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(msg); cache->purgeAsNeeded(); expectedCacheCount--; proxy = proxyProvider->findOrCreateProxyByUniqueKey(key, kBottomLeft_GrSurfaceOrigin); REPORTER_ASSERT(reporter, !proxy); REPORTER_ASSERT(reporter, expectedCacheCount == cache->getResourceCount()); } }