DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ZeroSizedProxyTest, reporter, ctxInfo) { GrProxyProvider* provider = ctxInfo.grContext()->priv().proxyProvider(); for (auto flags : { kRenderTarget_GrSurfaceFlag, kNone_GrSurfaceFlags }) { for (auto fit : { SkBackingFit::kExact, SkBackingFit::kApprox }) { for (int width : { 0, 100 }) { for (int height : { 0, 100}) { if (width && height) { continue; // not zero-sized } GrSurfaceDesc desc; desc.fFlags = flags; desc.fWidth = width; desc.fHeight = height; desc.fConfig = kRGBA_8888_GrPixelConfig; desc.fSampleCnt = 1; const GrBackendFormat format = ctxInfo.grContext()->priv().caps()->getBackendFormatFromColorType( kRGBA_8888_SkColorType); sk_sp<GrTextureProxy> proxy = provider->createProxy( format, desc, kBottomLeft_GrSurfaceOrigin, fit, SkBudgeted::kNo); REPORTER_ASSERT(reporter, !proxy); } } } } }
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(TextureProxyTest, reporter, ctxInfo) { GrContext* context = ctxInfo.grContext(); GrProxyProvider* proxyProvider = context->priv().proxyProvider(); GrResourceCache* cache = context->priv().getResourceCache(); REPORTER_ASSERT(reporter, !proxyProvider->numUniqueKeyProxies_TestOnly()); REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); for (auto fit : { SkBackingFit::kExact, SkBackingFit::kApprox }) { for (auto create : { deferred_tex, deferred_texRT, wrapped, wrapped_with_key }) { REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); basic_test(context, reporter, create(reporter, context, proxyProvider, fit)); } REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); sk_sp<GrTexture> backingTex; sk_sp<GrTextureProxy> proxy = create_wrapped_backend(context, fit, &backingTex); basic_test(context, reporter, std::move(proxy)); backingTex = nullptr; cache->purgeAllUnlocked(); } invalidation_test(context, reporter); invalidation_and_instantiation_test(context, reporter); }
sk_sp<GrTextureProxy> GrCopyBaseMipMapToTextureProxy(GrContext* ctx, GrTextureProxy* baseProxy) { SkASSERT(baseProxy); if (!ctx->caps()->isConfigCopyable(baseProxy->config())) { return nullptr; } GrProxyProvider* proxyProvider = ctx->contextPriv().proxyProvider(); GrSurfaceDesc desc; desc.fFlags = kNone_GrSurfaceFlags; desc.fOrigin = baseProxy->origin(); desc.fWidth = baseProxy->width(); desc.fHeight = baseProxy->height(); desc.fConfig = baseProxy->config(); desc.fSampleCnt = 1; sk_sp<GrTextureProxy> proxy = proxyProvider->createMipMapProxy(desc, SkBudgeted::kYes); if (!proxy) { return nullptr; } // Copy the base layer to our proxy sk_sp<SkColorSpace> colorSpace; if (GrPixelConfigIsSRGB(proxy->config())) { colorSpace = SkColorSpace::MakeSRGB(); } sk_sp<GrSurfaceContext> sContext = ctx->contextPriv().makeWrappedSurfaceContext(proxy, std::move(colorSpace)); SkASSERT(sContext); SkAssertResult(sContext->copy(baseProxy)); return proxy; }
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 onDraw(SkCanvas* canvas) override { GrRenderTargetContext* renderTargetContext = canvas->internal_private_accessTopLayerRenderTargetContext(); if (!renderTargetContext) { skiagm::GM::DrawGpuOnlyMessage(canvas); return; } GrContext* context = canvas->getGrContext(); if (!context) { return; } GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider(); sk_sp<GrTextureProxy> proxy[3]; for (int i = 0; i < 3; ++i) { int index = (0 == i) ? 0 : 1; GrSurfaceDesc desc; desc.fWidth = fBmp[index].width(); desc.fHeight = fBmp[index].height(); desc.fConfig = SkImageInfo2GrPixelConfig(fBmp[index].info(), *context->caps()); SkASSERT(kUnknown_GrPixelConfig != desc.fConfig); proxy[i] = proxyProvider->createTextureProxy( desc, SkBudgeted::kYes, fBmp[index].getPixels(), fBmp[index].rowBytes()); if (!proxy[i]) { return; } } constexpr SkScalar kDrawPad = 10.f; constexpr SkScalar kTestPad = 10.f; constexpr SkScalar kColorSpaceOffset = 36.f; SkISize sizes[3] = {{YSIZE, YSIZE}, {USIZE, USIZE}, {VSIZE, VSIZE}}; for (int space = kJPEG_SkYUVColorSpace; space <= kLastEnum_SkYUVColorSpace; ++space) { SkRect renderRect = SkRect::MakeWH(SkIntToScalar(fBmp[0].width()), SkIntToScalar(fBmp[0].height())); renderRect.outset(kDrawPad, kDrawPad); SkScalar y = kDrawPad + kTestPad + space * kColorSpaceOffset; SkScalar x = kDrawPad + kTestPad; GrPaint grPaint; grPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc)); auto fp = GrYUVtoRGBEffect::Make(proxy[0], proxy[1], proxy[2], sizes, static_cast<SkYUVColorSpace>(space), true); if (fp) { SkMatrix viewMatrix; viewMatrix.setTranslate(x, y); grPaint.addColorFragmentProcessor(std::move(fp)); std::unique_ptr<GrDrawOp> op(GrRectOpFactory::MakeNonAAFill( std::move(grPaint), viewMatrix, renderRect, GrAAType::kNone)); renderTargetContext->priv().testingOnly_addDrawOp(std::move(op)); } } }
static sk_sp<GrSurfaceProxy> make_backend(GrContext* context, const ProxyParams& p, GrBackendTexture* backendTex) { GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider(); GrGpu* gpu = context->contextPriv().getGpu(); *backendTex = gpu->createTestingOnlyBackendTexture(nullptr, p.fSize, p.fSize, p.fConfig, false, GrMipMapped::kNo); return proxyProvider->createWrappedTextureProxy(*backendTex, p.fOrigin); }
static sk_sp<GrTextureProxy> create_wrapped_backend(GrContext* context, SkBackingFit fit, sk_sp<GrTexture>* backingSurface) { GrProxyProvider* proxyProvider = context->priv().proxyProvider(); GrResourceProvider* resourceProvider = context->priv().resourceProvider(); const GrSurfaceDesc desc = make_desc(kNone_GrSurfaceFlags); *backingSurface = resourceProvider->createTexture(desc, SkBudgeted::kNo, GrResourceProvider::Flags::kNoPendingIO); if (!(*backingSurface)) { return nullptr; } GrBackendTexture backendTex = (*backingSurface)->getBackendTexture(); backendTex.setPixelConfig(desc.fConfig); return proxyProvider->wrapBackendTexture(backendTex, kBottomLeft_GrSurfaceOrigin, kBorrow_GrWrapOwnership, GrWrapCacheable::kYes, kRead_GrIOType); }
// Test if invalidating unique ids operates as expected for texture proxies. static void invalidation_test(GrContext* context, skiatest::Reporter* reporter) { GrProxyProvider* proxyProvider = context->priv().proxyProvider(); GrResourceCache* cache = context->priv().getResourceCache(); REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); sk_sp<SkImage> rasterImg; { SkImageInfo ii = SkImageInfo::Make(64, 64, kRGBA_8888_SkColorType, kOpaque_SkAlphaType); SkBitmap bm; bm.allocPixels(ii); rasterImg = SkImage::MakeFromBitmap(bm); REPORTER_ASSERT(reporter, 0 == proxyProvider->numUniqueKeyProxies_TestOnly()); REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); } sk_sp<SkImage> textureImg = rasterImg->makeTextureImage(context, nullptr); REPORTER_ASSERT(reporter, 1 == proxyProvider->numUniqueKeyProxies_TestOnly()); REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); rasterImg = nullptr; // this invalidates the uniqueKey // this forces the cache to respond to the inval msg int maxNum; size_t maxBytes; context->getResourceCacheLimits(&maxNum, &maxBytes); context->setResourceCacheLimits(maxNum-1, maxBytes); REPORTER_ASSERT(reporter, 0 == proxyProvider->numUniqueKeyProxies_TestOnly()); REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); textureImg = nullptr; context->priv().testingOnly_purgeAllUnlockedResources(); REPORTER_ASSERT(reporter, 0 == proxyProvider->numUniqueKeyProxies_TestOnly()); REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); }
// Test if invalidating unique ids prior to instantiating operates as expected static void invalidation_and_instantiation_test(GrContext* context, skiatest::Reporter* reporter) { GrProxyProvider* proxyProvider = context->priv().proxyProvider(); GrResourceProvider* resourceProvider = context->priv().resourceProvider(); GrResourceCache* cache = context->priv().getResourceCache(); REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); static GrUniqueKey::Domain d = GrUniqueKey::GenerateDomain(); GrUniqueKey key; GrUniqueKey::Builder builder(&key, d, 1, nullptr); builder[0] = 0; builder.finish(); // Create proxy, assign unique key sk_sp<GrTextureProxy> proxy = deferred_tex(reporter, context, proxyProvider, SkBackingFit::kExact); SkAssertResult(proxyProvider->assignUniqueKeyToProxy(key, proxy.get())); // Send an invalidation message, which will be sitting in the cache's inbox SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post( GrUniqueKeyInvalidatedMessage(key, context->priv().contextID())); REPORTER_ASSERT(reporter, 1 == proxyProvider->numUniqueKeyProxies_TestOnly()); REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); // Instantiate the proxy. This will trigger the message to be processed, so the resulting // texture should *not* have the unique key on it! SkAssertResult(proxy->instantiate(resourceProvider)); REPORTER_ASSERT(reporter, !proxy->getUniqueKey().isValid()); REPORTER_ASSERT(reporter, !proxy->peekTexture()->getUniqueKey().isValid()); REPORTER_ASSERT(reporter, 0 == proxyProvider->numUniqueKeyProxies_TestOnly()); REPORTER_ASSERT(reporter, 1 == cache->getResourceCount()); proxy = nullptr; context->priv().testingOnly_purgeAllUnlockedResources(); REPORTER_ASSERT(reporter, 0 == proxyProvider->numUniqueKeyProxies_TestOnly()); REPORTER_ASSERT(reporter, 0 == cache->getResourceCount()); }
bool GrDrawingManager::ProgramUnitTest(GrContext* context, int maxStages, int maxLevels) { GrDrawingManager* drawingManager = context->contextPriv().drawingManager(); GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider(); sk_sp<GrTextureProxy> proxies[2]; // setup dummy textures { GrSurfaceDesc dummyDesc; dummyDesc.fFlags = kRenderTarget_GrSurfaceFlag; dummyDesc.fWidth = 34; dummyDesc.fHeight = 18; dummyDesc.fConfig = kRGBA_8888_GrPixelConfig; proxies[0] = proxyProvider->createProxy(dummyDesc, kBottomLeft_GrSurfaceOrigin, SkBackingFit::kExact, SkBudgeted::kNo); } { GrSurfaceDesc dummyDesc; dummyDesc.fFlags = kNone_GrSurfaceFlags; dummyDesc.fWidth = 16; dummyDesc.fHeight = 22; dummyDesc.fConfig = kAlpha_8_GrPixelConfig; proxies[1] = proxyProvider->createProxy(dummyDesc, kTopLeft_GrSurfaceOrigin, SkBackingFit::kExact, SkBudgeted::kNo); } if (!proxies[0] || !proxies[1]) { SkDebugf("Could not allocate dummy textures"); return false; } // dummy scissor state GrScissorState scissor; SkRandom random; static const int NUM_TESTS = 1024; for (int t = 0; t < NUM_TESTS; t++) { // setup random render target(can fail) sk_sp<GrRenderTargetContext> renderTargetContext(random_render_target_context( context, &random, context->caps())); if (!renderTargetContext) { SkDebugf("Could not allocate renderTargetContext"); return false; } GrPaint paint; GrProcessorTestData ptd(&random, context, renderTargetContext.get(), proxies); set_random_color_coverage_stages(&paint, &ptd, maxStages, maxLevels); set_random_xpf(&paint, &ptd); set_random_state(&paint, &random); GrDrawRandomOp(&random, renderTargetContext.get(), std::move(paint)); } // Flush everything, test passes if flush is successful(ie, no asserts are hit, no crashes) drawingManager->flush(nullptr); // Validate that GrFPs work correctly without an input. sk_sp<GrRenderTargetContext> renderTargetContext( context->contextPriv().makeDeferredRenderTargetContext(SkBackingFit::kExact, kRenderTargetWidth, kRenderTargetHeight, kRGBA_8888_GrPixelConfig, nullptr)); if (!renderTargetContext) { SkDebugf("Could not allocate a renderTargetContext"); return false; } int fpFactoryCnt = GrFragmentProcessorTestFactory::Count(); for (int i = 0; i < fpFactoryCnt; ++i) { // Since FP factories internally randomize, call each 10 times. for (int j = 0; j < 10; ++j) { GrProcessorTestData ptd(&random, context, renderTargetContext.get(), proxies); GrPaint paint; paint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc)); auto fp = GrFragmentProcessorTestFactory::MakeIdx(i, &ptd); auto blockFP = BlockInputFragmentProcessor::Make(std::move(fp)); paint.addColorFragmentProcessor(std::move(blockFP)); GrDrawRandomOp(&random, renderTargetContext.get(), std::move(paint)); drawingManager->flush(nullptr); } } return true; }
/* * 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; }
DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(RectangleTexture, reporter, ctxInfo) { GrContext* context = ctxInfo.grContext(); GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider(); sk_gpu_test::GLTestContext* glContext = ctxInfo.glContext(); static const int kWidth = 13; static const int kHeight = 13; GrColor pixels[kWidth * kHeight]; for (int y = 0; y < kHeight; ++y) { for (int x = 0; x < kWidth; ++x) { pixels[y * kWidth + x] = y * kWidth + x; } } for (auto origin : { kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin }) { bool useBLOrigin = kBottomLeft_GrSurfaceOrigin == origin; GrGLuint rectTexID = glContext->createTextureRectangle(kWidth, kHeight, GR_GL_RGBA, GR_GL_RGBA, GR_GL_UNSIGNED_BYTE, pixels); if (!rectTexID) { return; } // Let GrContext know that we messed with the GL context directly. context->resetContext(); // Wrap the rectangle texture ID in a GrTexture GrGLTextureInfo rectangleInfo; rectangleInfo.fID = rectTexID; rectangleInfo.fTarget = GR_GL_TEXTURE_RECTANGLE; GrBackendTexture rectangleTex(kWidth, kHeight, kRGBA_8888_GrPixelConfig, rectangleInfo); GrColor refPixels[kWidth * kHeight]; for (int y = 0; y < kHeight; ++y) { for (int x = 0; x < kWidth; ++x) { int y0 = useBLOrigin ? kHeight - y - 1 : y; refPixels[y * kWidth + x] = pixels[y0 * kWidth + x]; } } sk_sp<GrTextureProxy> rectProxy = proxyProvider->wrapBackendTexture(rectangleTex, origin); if (!rectProxy) { ERRORF(reporter, "Error creating proxy for rectangle texture."); GR_GL_CALL(glContext->gl(), DeleteTextures(1, &rectTexID)); continue; } SkASSERT(rectProxy->texPriv().doesNotSupportMipMaps()); SkASSERT(rectProxy->priv().peekTexture()->surfacePriv().doesNotSupportMipMaps()); SkASSERT(rectProxy->texPriv().isClampOnly()); SkASSERT(rectProxy->priv().peekTexture()->surfacePriv().isClampOnly()); test_basic_draw_as_src(reporter, context, rectProxy, refPixels); // Test copy to both a texture and RT test_copy_from_surface(reporter, context, rectProxy.get(), refPixels, false, "RectangleTexture-copy-from"); sk_sp<GrSurfaceContext> rectContext = context->contextPriv().makeWrappedSurfaceContext( std::move(rectProxy)); SkASSERT(rectContext); test_read_pixels(reporter, rectContext.get(), refPixels, "RectangleTexture-read"); test_copy_to_surface(reporter, context->contextPriv().proxyProvider(), rectContext.get(), "RectangleTexture-copy-to"); test_write_pixels(reporter, rectContext.get(), true, "RectangleTexture-write"); test_clear(reporter, rectContext.get()); GR_GL_CALL(glContext->gl(), DeleteTextures(1, &rectTexID)); } }
// 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()); } }
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(CopySurface, reporter, ctxInfo) { GrContext* context = ctxInfo.grContext(); GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider(); static const int kW = 10; static const int kH = 10; static const size_t kRowBytes = sizeof(uint32_t) * kW; GrSurfaceDesc baseDesc; baseDesc.fConfig = kRGBA_8888_GrPixelConfig; baseDesc.fWidth = kW; baseDesc.fHeight = kH; SkAutoTMalloc<uint32_t> srcPixels(kW * kH); for (int i = 0; i < kW * kH; ++i) { srcPixels.get()[i] = i; } SkAutoTMalloc<uint32_t> dstPixels(kW * kH); for (int i = 0; i < kW * kH; ++i) { dstPixels.get()[i] = ~i; } static const SkIRect kSrcRects[] { { 0, 0, kW , kH }, {-1, -1, kW+1, kH+1}, { 1, 1, kW-1, kH-1}, { 5, 5, 6 , 6 }, }; static const SkIPoint kDstPoints[] { { 0 , 0 }, { 1 , 1 }, { kW/2, kH/4}, { kW-1, kH-1}, { kW , kH }, { kW+1, kH+2}, {-1 , -1 }, }; const SkImageInfo ii = SkImageInfo::Make(kW, kH, kRGBA_8888_SkColorType, kPremul_SkAlphaType); SkAutoTMalloc<uint32_t> read(kW * kH); for (auto sOrigin : {kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin}) { for (auto dOrigin : {kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin}) { for (auto sFlags: {kRenderTarget_GrSurfaceFlag, kNone_GrSurfaceFlags}) { for (auto dFlags: {kRenderTarget_GrSurfaceFlag, kNone_GrSurfaceFlags}) { for (auto srcRect : kSrcRects) { for (auto dstPoint : kDstPoints) { GrSurfaceDesc srcDesc = baseDesc; srcDesc.fOrigin = sOrigin; srcDesc.fFlags = sFlags; GrSurfaceDesc dstDesc = baseDesc; dstDesc.fOrigin = dOrigin; dstDesc.fFlags = dFlags; sk_sp<GrTextureProxy> src = proxyProvider->createTextureProxy( srcDesc, SkBudgeted::kNo, srcPixels.get(), kRowBytes); sk_sp<GrTextureProxy> dst = proxyProvider->createTextureProxy( dstDesc, SkBudgeted::kNo, dstPixels.get(), kRowBytes); if (!src || !dst) { ERRORF(reporter, "Could not create surfaces for copy surface test."); continue; } sk_sp<GrSurfaceContext> dstContext = context->contextPriv().makeWrappedSurfaceContext(std::move(dst)); bool result = dstContext->copy(src.get(), srcRect, dstPoint); bool expectedResult = true; SkIPoint dstOffset = { dstPoint.fX - srcRect.fLeft, dstPoint.fY - srcRect.fTop }; SkIRect copiedDstRect = SkIRect::MakeXYWH(dstPoint.fX, dstPoint.fY, srcRect.width(), srcRect.height()); SkIRect copiedSrcRect; if (!copiedSrcRect.intersect(srcRect, SkIRect::MakeWH(kW, kH))) { expectedResult = false; } else { // If the src rect was clipped, apply same clipping to each side of // copied dst rect. copiedDstRect.fLeft += copiedSrcRect.fLeft - srcRect.fLeft; copiedDstRect.fTop += copiedSrcRect.fTop - srcRect.fTop; copiedDstRect.fRight -= copiedSrcRect.fRight - srcRect.fRight; copiedDstRect.fBottom -= copiedSrcRect.fBottom - srcRect.fBottom; } if (copiedDstRect.isEmpty() || !copiedDstRect.intersect(SkIRect::MakeWH(kW, kH))) { expectedResult = false; } // To make the copied src rect correct we would apply any dst clipping // back to the src rect, but we don't use it again so don't bother. if (expectedResult != result) { ERRORF(reporter, "Expected return value %d from copySurface, got " "%d.", expectedResult, result); continue; } if (!expectedResult || !result) { continue; } sk_memset32(read.get(), 0, kW * kH); if (!dstContext->readPixels(ii, read.get(), kRowBytes, 0, 0)) { ERRORF(reporter, "Error calling readPixels"); continue; } bool abort = false; // Validate that pixels inside copiedDstRect received the correct value // from src and that those outside were not modified. for (int y = 0; y < kH && !abort; ++y) { for (int x = 0; x < kW; ++x) { uint32_t r = read.get()[y * kW + x]; if (copiedDstRect.contains(x, y)) { int sx = x - dstOffset.fX; int sy = y - dstOffset.fY; uint32_t s = srcPixels.get()[sy * kW + sx]; if (s != r) { ERRORF(reporter, "Expected dst %d,%d to contain " "0x%08x copied from src location %d,%d. Got " "0x%08x", x, y, s, sx, sy, r); abort = true; break; } } else { uint32_t d = dstPixels.get()[y * kW + x]; if (d != r) { ERRORF(reporter, "Expected dst %d,%d to be unmodified (" "0x%08x). Got 0x%08x", x, y, d, r); abort = true; break; } } } } } } } } } } }
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(WrappedProxyTest, reporter, ctxInfo) { GrProxyProvider* proxyProvider = ctxInfo.grContext()->priv().proxyProvider(); GrContext* context = ctxInfo.grContext(); GrResourceProvider* resourceProvider = context->priv().resourceProvider(); GrGpu* gpu = context->priv().getGpu(); const GrCaps& caps = *context->priv().caps(); static const int kWidthHeight = 100; for (auto origin : { kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin }) { for (auto colorType : { kAlpha_8_SkColorType, kRGBA_8888_SkColorType, kRGBA_1010102_SkColorType }) { // External on-screen render target. // Tests wrapBackendRenderTarget with a GrBackendRenderTarget // Our test-only function that creates a backend render target doesn't currently support // sample counts :(. if (ctxInfo.grContext()->colorTypeSupportedAsSurface(colorType)) { GrBackendRenderTarget backendRT = gpu->createTestingOnlyBackendRenderTarget( kWidthHeight, kWidthHeight, SkColorTypeToGrColorType(colorType)); sk_sp<GrSurfaceProxy> sProxy( proxyProvider->wrapBackendRenderTarget(backendRT, origin, nullptr, nullptr)); check_surface(reporter, sProxy.get(), origin, kWidthHeight, kWidthHeight, backendRT.pixelConfig(), SkBudgeted::kNo); static constexpr int kExpectedNumSamples = 1; check_rendertarget(reporter, caps, resourceProvider, sProxy->asRenderTargetProxy(), kExpectedNumSamples, SkBackingFit::kExact, caps.maxWindowRectangles()); gpu->deleteTestingOnlyBackendRenderTarget(backendRT); } for (auto numSamples : {1, 4}) { GrPixelConfig config = SkColorType2GrPixelConfig(colorType); SkASSERT(kUnknown_GrPixelConfig != config); int supportedNumSamples = caps.getRenderTargetSampleCount(numSamples, config); if (!supportedNumSamples) { continue; } // Test wrapping FBO 0 (with made up properties). This tests sample count and the // special case where FBO 0 doesn't support window rectangles. if (GrBackendApi::kOpenGL == ctxInfo.backend()) { GrGLFramebufferInfo fboInfo; fboInfo.fFBOID = 0; fboInfo.fFormat = GR_GL_RGBA8; static constexpr int kStencilBits = 8; GrBackendRenderTarget backendRT(kWidthHeight, kWidthHeight, numSamples, kStencilBits, fboInfo); backendRT.setPixelConfig(config); sk_sp<GrSurfaceProxy> sProxy( proxyProvider->wrapBackendRenderTarget(backendRT, origin, nullptr, nullptr)); check_surface(reporter, sProxy.get(), origin, kWidthHeight, kWidthHeight, backendRT.pixelConfig(), SkBudgeted::kNo); check_rendertarget(reporter, caps, resourceProvider, sProxy->asRenderTargetProxy(), supportedNumSamples, SkBackingFit::kExact, 0); } // Tests wrapBackendRenderTarget with a GrBackendTexture { GrBackendTexture backendTex = context->createBackendTexture(kWidthHeight, kWidthHeight, colorType, GrMipMapped::kNo, GrRenderable::kYes); sk_sp<GrSurfaceProxy> sProxy = proxyProvider->wrapBackendTextureAsRenderTarget( backendTex, origin, supportedNumSamples); if (!sProxy) { context->deleteBackendTexture(backendTex); continue; // This can fail on Mesa } check_surface(reporter, sProxy.get(), origin, kWidthHeight, kWidthHeight, backendTex.pixelConfig(), SkBudgeted::kNo); check_rendertarget(reporter, caps, resourceProvider, sProxy->asRenderTargetProxy(), supportedNumSamples, SkBackingFit::kExact, caps.maxWindowRectangles()); context->deleteBackendTexture(backendTex); } // Tests wrapBackendTexture that is only renderable { GrBackendTexture backendTex = context->createBackendTexture(kWidthHeight, kWidthHeight, colorType, GrMipMapped::kNo, GrRenderable::kYes); sk_sp<GrSurfaceProxy> sProxy = proxyProvider->wrapRenderableBackendTexture( backendTex, origin, supportedNumSamples, kBorrow_GrWrapOwnership, GrWrapCacheable::kNo, nullptr, nullptr); if (!sProxy) { context->deleteBackendTexture(backendTex); continue; // This can fail on Mesa } check_surface(reporter, sProxy.get(), origin, kWidthHeight, kWidthHeight, backendTex.pixelConfig(), SkBudgeted::kNo); check_rendertarget(reporter, caps, resourceProvider, sProxy->asRenderTargetProxy(), supportedNumSamples, SkBackingFit::kExact, caps.maxWindowRectangles()); context->deleteBackendTexture(backendTex); } // Tests wrapBackendTexture that is only textureable { // Internal offscreen texture GrBackendTexture backendTex = context->createBackendTexture(kWidthHeight, kWidthHeight, colorType, GrMipMapped::kNo, GrRenderable::kNo); sk_sp<GrSurfaceProxy> sProxy = proxyProvider->wrapBackendTexture( backendTex, origin, kBorrow_GrWrapOwnership, GrWrapCacheable::kNo, kRead_GrIOType); if (!sProxy) { context->deleteBackendTexture(backendTex); continue; } check_surface(reporter, sProxy.get(), origin, kWidthHeight, kWidthHeight, backendTex.pixelConfig(), SkBudgeted::kNo); check_texture(reporter, resourceProvider, sProxy->asTextureProxy(), SkBackingFit::kExact); context->deleteBackendTexture(backendTex); } } } } }
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DeferredProxyTest, reporter, ctxInfo) { GrProxyProvider* proxyProvider = ctxInfo.grContext()->priv().proxyProvider(); GrResourceProvider* resourceProvider = ctxInfo.grContext()->priv().resourceProvider(); const GrCaps& caps = *ctxInfo.grContext()->priv().caps(); int attempt = 0; // useful for debugging for (auto origin : { kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin }) { for (auto widthHeight : { 100, 128, 1048576 }) { for (auto config : { kAlpha_8_GrPixelConfig, kRGB_565_GrPixelConfig, kRGBA_8888_GrPixelConfig, kRGBA_1010102_GrPixelConfig, kRGB_ETC1_GrPixelConfig }) { for (auto fit : { SkBackingFit::kExact, SkBackingFit::kApprox }) { for (auto budgeted : { SkBudgeted::kYes, SkBudgeted::kNo }) { for (auto numSamples : {1, 4, 16, 128}) { // We don't have recycling support for compressed textures if (GrPixelConfigIsCompressed(config) && SkBackingFit::kApprox == fit) { continue; } GrSurfaceDesc desc; desc.fFlags = kRenderTarget_GrSurfaceFlag; desc.fWidth = widthHeight; desc.fHeight = widthHeight; desc.fConfig = config; desc.fSampleCnt = numSamples; GrSRGBEncoded srgbEncoded; GrColorType colorType = GrPixelConfigToColorTypeAndEncoding(config, &srgbEncoded); const GrBackendFormat format = caps.getBackendFormatFromGrColorType(colorType, srgbEncoded); { sk_sp<GrTexture> tex; if (SkBackingFit::kApprox == fit) { tex = resourceProvider->createApproxTexture( desc, GrResourceProvider::Flags::kNoPendingIO); } else { tex = resourceProvider->createTexture( desc, budgeted, GrResourceProvider::Flags::kNoPendingIO); } sk_sp<GrTextureProxy> proxy = proxyProvider->createProxy(format, desc, origin, fit, budgeted); REPORTER_ASSERT(reporter, SkToBool(tex) == SkToBool(proxy)); if (proxy) { REPORTER_ASSERT(reporter, proxy->asRenderTargetProxy()); // This forces the proxy to compute and cache its // pre-instantiation size guess. Later, when it is actually // instantiated, it checks that the instantiated size is <= to // the pre-computation. If the proxy never computed its // pre-instantiation size then the check is skipped. proxy->gpuMemorySize(); check_surface(reporter, proxy.get(), origin, widthHeight, widthHeight, config, budgeted); int supportedSamples = caps.getRenderTargetSampleCount(numSamples, config); check_rendertarget(reporter, caps, resourceProvider, proxy->asRenderTargetProxy(), supportedSamples, fit, caps.maxWindowRectangles()); } } desc.fFlags = kNone_GrSurfaceFlags; { sk_sp<GrTexture> tex; if (SkBackingFit::kApprox == fit) { tex = resourceProvider->createApproxTexture( desc, GrResourceProvider::Flags::kNoPendingIO); } else { tex = resourceProvider->createTexture( desc, budgeted, GrResourceProvider::Flags::kNoPendingIO); } sk_sp<GrTextureProxy> proxy( proxyProvider->createProxy(format, desc, origin, fit, budgeted)); REPORTER_ASSERT(reporter, SkToBool(tex) == SkToBool(proxy)); if (proxy) { // This forces the proxy to compute and cache its // pre-instantiation size guess. Later, when it is actually // instantiated, it checks that the instantiated size is <= to // the pre-computation. If the proxy never computed its // pre-instantiation size then the check is skipped. proxy->gpuMemorySize(); check_surface(reporter, proxy.get(), origin, widthHeight, widthHeight, config, budgeted); check_texture(reporter, resourceProvider, proxy->asTextureProxy(), fit); } } attempt++; } } } } } } }