/* * Copyright 2017 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "Test.h" #if SK_SUPPORT_GPU #include "GrClip.h" #include "GrContextPriv.h" #include "GrProxyProvider.h" #include "GrOnFlushResourceProvider.h" #include "GrRenderTargetContext.h" #include "GrRenderTargetContextPriv.h" #include "GrSurfaceProxy.h" #include "GrSurfaceProxyPriv.h" #include "GrTexture.h" #include "GrTextureProxy.h" #include "GrTextureProxyPriv.h" #include "SkMakeUnique.h" #include "SkRectPriv.h" #include "mock/GrMockTypes.h" // This test verifies that lazy proxy callbacks get invoked during flush, after onFlush callbacks, // but before Ops are executed. It also ensures that lazy proxy callbacks are invoked both for // regular Ops and for clips. class LazyProxyTest final : public GrOnFlushCallbackObject { public: LazyProxyTest(skiatest::Reporter* reporter) : fReporter(reporter) , fHasOpTexture(false) , fHasClipTexture(false) { } ~LazyProxyTest() override { REPORTER_ASSERT(fReporter, fHasOpTexture); REPORTER_ASSERT(fReporter, fHasClipTexture); } void preFlush(GrOnFlushResourceProvider*, const uint32_t*, int, SkTArray>*) override { REPORTER_ASSERT(fReporter, !fHasOpTexture); REPORTER_ASSERT(fReporter, !fHasClipTexture); } void postFlush(GrDeferredUploadToken, const uint32_t* opListIDs, int numOpListIDs) override { REPORTER_ASSERT(fReporter, fHasOpTexture); REPORTER_ASSERT(fReporter, fHasClipTexture); } class Op final : public GrDrawOp { public: DEFINE_OP_CLASS_ID Op(GrProxyProvider* proxyProvider, LazyProxyTest* test, bool nullTexture) : GrDrawOp(ClassID()), fTest(test) { fProxy = proxyProvider->createFullyLazyProxy([this, nullTexture]( GrResourceProvider* rp, GrSurfaceOrigin* origin) { if (!rp) { return sk_sp(); } REPORTER_ASSERT(fTest->fReporter, !fTest->fHasOpTexture); fTest->fHasOpTexture = true; *origin = kTopLeft_GrSurfaceOrigin; if (nullTexture) { return sk_sp(); } else { GrSurfaceDesc desc; desc.fWidth = 1234; desc.fHeight = 567; desc.fOrigin = kTopLeft_GrSurfaceOrigin; desc.fConfig = kRGB_565_GrPixelConfig; sk_sp texture = rp->createTexture(desc, SkBudgeted::kYes); REPORTER_ASSERT(fTest->fReporter, texture); return texture; } }, GrProxyProvider::Renderable::kNo, kRGB_565_GrPixelConfig); this->setBounds(SkRectPriv::MakeLargest(), GrOp::HasAABloat::kNo, GrOp::IsZeroArea::kNo); } void visitProxies(const VisitProxyFunc& func) const override { func(fProxy.get()); } void onExecute(GrOpFlushState*) override { REPORTER_ASSERT(fTest->fReporter, fTest->fHasOpTexture); REPORTER_ASSERT(fTest->fReporter, fTest->fHasClipTexture); } private: const char* name() const override { return "LazyProxyTest::Op"; } FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; } RequiresDstTexture finalize(const GrCaps&, const GrAppliedClip*, GrPixelConfigIsClamped) override { return RequiresDstTexture::kNo; } void wasRecorded(GrRenderTargetOpList*) override {} bool onCombineIfPossible(GrOp* other, const GrCaps& caps) override { return false; } void onPrepare(GrOpFlushState*) override {} LazyProxyTest* const fTest; sk_sp fProxy; }; class ClipFP : public GrFragmentProcessor { public: ClipFP(GrProxyProvider* proxyProvider, LazyProxyTest* test, GrTextureProxy* atlas) : GrFragmentProcessor(kTestFP_ClassID, kNone_OptimizationFlags) , fProxyProvider(proxyProvider) , fTest(test) , fAtlas(atlas) { fLazyProxy = proxyProvider->createFullyLazyProxy([this](GrResourceProvider* rp, GrSurfaceOrigin* origin) { if (!rp) { return sk_sp(); } REPORTER_ASSERT(fTest->fReporter, !fTest->fHasClipTexture); fTest->fHasClipTexture = true; *origin = kBottomLeft_GrSurfaceOrigin; fAtlas->instantiate(rp); return sk_ref_sp(fAtlas->priv().peekTexture()); }, GrProxyProvider::Renderable::kYes, kAlpha_half_GrPixelConfig); fAccess.reset(fLazyProxy, GrSamplerState::Filter::kNearest, GrSamplerState::WrapMode::kClamp, kFragment_GrShaderFlag); this->addTextureSampler(&fAccess); } private: const char* name() const override { return "LazyProxyTest::ClipFP"; } std::unique_ptr clone() const override { return skstd::make_unique(fProxyProvider, fTest, fAtlas); } GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { return nullptr; } void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {} bool onIsEqual(const GrFragmentProcessor&) const override { return false; } GrProxyProvider* const fProxyProvider; LazyProxyTest* const fTest; GrTextureProxy* const fAtlas; sk_sp fLazyProxy; TextureSampler fAccess; }; class Clip : public GrClip { public: Clip(LazyProxyTest* test, GrTextureProxy* atlas) : fTest(test) , fAtlas(atlas) {} private: bool apply(GrContext* context, GrRenderTargetContext*, bool, bool, GrAppliedClip* out, SkRect* bounds) const override { GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider(); out->addCoverageFP(skstd::make_unique(proxyProvider, fTest, fAtlas)); return true; } bool quickContains(const SkRect&) const final { return false; } bool isRRect(const SkRect& rtBounds, SkRRect* rr, GrAA*) const final { return false; } void getConservativeBounds(int width, int height, SkIRect* rect, bool* iior) const final { rect->set(0, 0, width, height); if (iior) { *iior = false; } } LazyProxyTest* const fTest; GrTextureProxy* fAtlas; }; private: skiatest::Reporter* fReporter; bool fHasOpTexture; bool fHasClipTexture; }; DEF_GPUTEST(LazyProxyTest, reporter, /* options */) { GrMockOptions mockOptions; mockOptions.fConfigOptions[kAlpha_half_GrPixelConfig].fRenderability = GrMockOptions::ConfigOptions::Renderability::kNonMSAA; mockOptions.fConfigOptions[kAlpha_half_GrPixelConfig].fTexturable = true; sk_sp ctx = GrContext::MakeMock(&mockOptions, GrContextOptions()); GrProxyProvider* proxyProvider = ctx->contextPriv().proxyProvider(); for (bool nullTexture : {false, true}) { LazyProxyTest test(reporter); ctx->contextPriv().addOnFlushCallbackObject(&test); sk_sp rtc = ctx->makeDeferredRenderTargetContext(SkBackingFit::kExact, 100, 100, kRGBA_8888_GrPixelConfig, nullptr); REPORTER_ASSERT(reporter, rtc); sk_sp mockAtlas = ctx->makeDeferredRenderTargetContext(SkBackingFit::kExact, 10, 10, kAlpha_half_GrPixelConfig, nullptr); REPORTER_ASSERT(reporter, mockAtlas); rtc->priv().testingOnly_addDrawOp(LazyProxyTest::Clip(&test, mockAtlas->asTextureProxy()), skstd::make_unique(proxyProvider, &test, nullTexture)); ctx->contextPriv().testingOnly_flushAndRemoveOnFlushCallbackObject(&test); } } static const int kSize = 16; DEF_GPUTEST(LazyProxyReleaseTest, reporter, /* options */) { GrMockOptions mockOptions; sk_sp ctx = GrContext::MakeMock(&mockOptions, GrContextOptions()); auto proxyProvider = ctx->contextPriv().proxyProvider(); GrSurfaceDesc desc; desc.fWidth = kSize; desc.fHeight = kSize; desc.fConfig = kRGBA_8888_GrPixelConfig; using LazyInstantiationType = GrSurfaceProxy::LazyInstantiationType; for (bool doInstantiate : {true, false}) { for (auto lazyType : {LazyInstantiationType::kSingleUse, LazyInstantiationType::kMultipleUse}) { int testCount = 0; int* testCountPtr = &testCount; sk_sp proxy = proxyProvider->createLazyProxy( [testCountPtr](GrResourceProvider* resourceProvider, GrSurfaceOrigin* /*outOrigin*/) { if (!resourceProvider) { *testCountPtr = -1; return sk_sp(); } *testCountPtr = 1; return sk_sp(); }, desc, GrMipMapped::kNo, SkBackingFit::kExact, SkBudgeted::kNo); proxy->priv().testingOnly_setLazyInstantiationType(lazyType); REPORTER_ASSERT(reporter, 0 == testCount); if (doInstantiate) { proxy->priv().doLazyInstantiation(ctx->contextPriv().resourceProvider()); if (LazyInstantiationType::kSingleUse == proxy->priv().lazyInstantiationType()) { // In SingleUse we will call the cleanup and delete the callback in the // doLazyInstantiationCall. REPORTER_ASSERT(reporter, -1 == testCount); } else { REPORTER_ASSERT(reporter, 1 == testCount); } proxy.reset(); REPORTER_ASSERT(reporter, -1 == testCount); } else { proxy.reset(); REPORTER_ASSERT(reporter, -1 == testCount); } } } } class LazyFailedInstantiationTestOp : public GrDrawOp { public: DEFINE_OP_CLASS_ID LazyFailedInstantiationTestOp(GrProxyProvider* proxyProvider, int* testExecuteValue, bool shouldFailInstantiation) : INHERITED(ClassID()) , fTestExecuteValue(testExecuteValue) { GrSurfaceDesc desc; desc.fWidth = kSize; desc.fHeight = kSize; desc.fConfig = kRGBA_8888_GrPixelConfig; fLazyProxy = proxyProvider->createLazyProxy( [testExecuteValue, shouldFailInstantiation, desc] ( GrResourceProvider* rp, GrSurfaceOrigin* /*origin*/) { if (!rp) { return sk_sp(); } if (shouldFailInstantiation) { *testExecuteValue = 1; return sk_sp(); } return rp->createTexture(desc, SkBudgeted::kNo); }, desc, GrMipMapped::kNo, SkBackingFit::kExact, SkBudgeted::kNo); this->setBounds(SkRect::MakeIWH(kSize, kSize), HasAABloat::kNo, IsZeroArea::kNo); } void visitProxies(const VisitProxyFunc& func) const override { func(fLazyProxy.get()); } private: const char* name() const override { return "LazyFailedInstantiationTestOp"; } FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; } RequiresDstTexture finalize(const GrCaps&, const GrAppliedClip*, GrPixelConfigIsClamped) override { return RequiresDstTexture::kNo; } bool onCombineIfPossible(GrOp* other, const GrCaps& caps) override { return false; } void onPrepare(GrOpFlushState*) override {} void onExecute(GrOpFlushState* state) override { *fTestExecuteValue = 2; } int* fTestExecuteValue; sk_sp fLazyProxy; typedef GrDrawOp INHERITED; }; // Test that when a lazy proxy fails to instantiate during flush that we drop the Op that it was // associated with. DEF_GPUTEST(LazyProxyFailedInstantiationTest, reporter, /* options */) { GrMockOptions mockOptions; sk_sp ctx = GrContext::MakeMock(&mockOptions, GrContextOptions()); GrResourceProvider* resourceProvider = ctx->contextPriv().resourceProvider(); GrProxyProvider* proxyProvider = ctx->contextPriv().proxyProvider(); for (bool failInstantiation : {false, true}) { sk_sp rtc = ctx->makeDeferredRenderTargetContext(SkBackingFit::kExact, 100, 100, kRGBA_8888_GrPixelConfig, nullptr); REPORTER_ASSERT(reporter, rtc); rtc->clear(nullptr, 0xbaaaaaad, GrRenderTargetContext::CanClearFullscreen::kYes); int executeTestValue = 0; rtc->priv().testingOnly_addDrawOp( skstd::make_unique(proxyProvider, &executeTestValue, failInstantiation)); ctx->flush(); if (failInstantiation) { if (resourceProvider->explicitlyAllocateGPUResources()) { REPORTER_ASSERT(reporter, 1 == executeTestValue); } else { // When we disable explicit gpu resource allocation we don't throw away ops that // have uninstantiated proxies. REPORTER_ASSERT(reporter, 2 == executeTestValue); } } else { REPORTER_ASSERT(reporter, 2 == executeTestValue); } } } #endif