1/*
2 * Copyright 2017 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 "Test.h"
9
10#if SK_SUPPORT_GPU
11
12#include "GrClip.h"
13#include "GrContextPriv.h"
14#include "GrProxyProvider.h"
15#include "GrOnFlushResourceProvider.h"
16#include "GrRenderTargetContext.h"
17#include "GrRenderTargetContextPriv.h"
18#include "GrSurfaceProxy.h"
19#include "GrSurfaceProxyPriv.h"
20#include "GrTexture.h"
21#include "GrTextureProxy.h"
22#include "GrTextureProxyPriv.h"
23#include "SkMakeUnique.h"
24#include "SkRectPriv.h"
25#include "mock/GrMockTypes.h"
26
27// This test verifies that lazy proxy callbacks get invoked during flush, after onFlush callbacks,
28// but before Ops are executed. It also ensures that lazy proxy callbacks are invoked both for
29// regular Ops and for clips.
30class LazyProxyTest final : public GrOnFlushCallbackObject {
31public:
32    LazyProxyTest(skiatest::Reporter* reporter)
33            : fReporter(reporter)
34            , fHasOpTexture(false)
35            , fHasClipTexture(false) {
36    }
37
38    ~LazyProxyTest() override {
39        REPORTER_ASSERT(fReporter, fHasOpTexture);
40        REPORTER_ASSERT(fReporter, fHasClipTexture);
41    }
42
43    void preFlush(GrOnFlushResourceProvider*, const uint32_t*, int,
44                  SkTArray<sk_sp<GrRenderTargetContext>>*) override {
45        REPORTER_ASSERT(fReporter, !fHasOpTexture);
46        REPORTER_ASSERT(fReporter, !fHasClipTexture);
47    }
48
49    void postFlush(GrDeferredUploadToken, const uint32_t* opListIDs, int numOpListIDs) override {
50        REPORTER_ASSERT(fReporter, fHasOpTexture);
51        REPORTER_ASSERT(fReporter, fHasClipTexture);
52    }
53
54    class Op final : public GrDrawOp {
55    public:
56        DEFINE_OP_CLASS_ID
57
58        Op(GrProxyProvider* proxyProvider, LazyProxyTest* test, bool nullTexture)
59                    : GrDrawOp(ClassID()), fTest(test) {
60            fProxy = proxyProvider->createFullyLazyProxy([this, nullTexture](
61                                        GrResourceProvider* rp, GrSurfaceOrigin* origin) {
62                if (!rp) {
63                    return sk_sp<GrTexture>();
64                }
65                REPORTER_ASSERT(fTest->fReporter, !fTest->fHasOpTexture);
66                fTest->fHasOpTexture = true;
67                *origin = kTopLeft_GrSurfaceOrigin;
68                if (nullTexture) {
69                    return sk_sp<GrTexture>();
70                } else {
71                    GrSurfaceDesc desc;
72                    desc.fWidth = 1234;
73                    desc.fHeight = 567;
74                    desc.fOrigin = kTopLeft_GrSurfaceOrigin;
75                    desc.fConfig = kRGB_565_GrPixelConfig;
76                    sk_sp<GrTexture> texture = rp->createTexture(desc, SkBudgeted::kYes);
77                    REPORTER_ASSERT(fTest->fReporter, texture);
78                    return texture;
79                }
80            }, GrProxyProvider::Renderable::kNo, kRGB_565_GrPixelConfig);
81            this->setBounds(SkRectPriv::MakeLargest(), GrOp::HasAABloat::kNo, GrOp::IsZeroArea::kNo);
82        }
83
84        void visitProxies(const VisitProxyFunc& func) const override {
85            func(fProxy.get());
86        }
87
88        void onExecute(GrOpFlushState*) override {
89            REPORTER_ASSERT(fTest->fReporter, fTest->fHasOpTexture);
90            REPORTER_ASSERT(fTest->fReporter, fTest->fHasClipTexture);
91        }
92
93    private:
94        const char* name() const override { return "LazyProxyTest::Op"; }
95        FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
96        RequiresDstTexture finalize(const GrCaps&, const GrAppliedClip*,
97                                    GrPixelConfigIsClamped) override {
98            return RequiresDstTexture::kNo;
99        }
100        void wasRecorded(GrRenderTargetOpList*) override {}
101        bool onCombineIfPossible(GrOp* other, const GrCaps& caps) override { return false; }
102        void onPrepare(GrOpFlushState*) override {}
103
104        LazyProxyTest* const fTest;
105        sk_sp<GrTextureProxy> fProxy;
106    };
107
108    class ClipFP : public GrFragmentProcessor {
109    public:
110        ClipFP(GrProxyProvider* proxyProvider, LazyProxyTest* test, GrTextureProxy* atlas)
111                : GrFragmentProcessor(kTestFP_ClassID, kNone_OptimizationFlags)
112                , fProxyProvider(proxyProvider)
113                , fTest(test)
114                , fAtlas(atlas) {
115            fLazyProxy = proxyProvider->createFullyLazyProxy([this](GrResourceProvider* rp,
116                                                                    GrSurfaceOrigin* origin) {
117                if (!rp) {
118                    return sk_sp<GrTexture>();
119                }
120                REPORTER_ASSERT(fTest->fReporter, !fTest->fHasClipTexture);
121                fTest->fHasClipTexture = true;
122                *origin = kBottomLeft_GrSurfaceOrigin;
123                fAtlas->instantiate(rp);
124                return sk_ref_sp(fAtlas->priv().peekTexture());
125            }, GrProxyProvider::Renderable::kYes, kAlpha_half_GrPixelConfig);
126            fAccess.reset(fLazyProxy, GrSamplerState::Filter::kNearest,
127                          GrSamplerState::WrapMode::kClamp, kFragment_GrShaderFlag);
128            this->addTextureSampler(&fAccess);
129        }
130
131    private:
132        const char* name() const override { return "LazyProxyTest::ClipFP"; }
133        std::unique_ptr<GrFragmentProcessor> clone() const override {
134            return skstd::make_unique<ClipFP>(fProxyProvider, fTest, fAtlas);
135        }
136        GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { return nullptr; }
137        void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {}
138        bool onIsEqual(const GrFragmentProcessor&) const override { return false; }
139
140        GrProxyProvider* const fProxyProvider;
141        LazyProxyTest* const fTest;
142        GrTextureProxy* const fAtlas;
143        sk_sp<GrTextureProxy> fLazyProxy;
144        TextureSampler fAccess;
145    };
146
147
148    class Clip : public GrClip {
149    public:
150        Clip(LazyProxyTest* test, GrTextureProxy* atlas)
151                : fTest(test)
152                , fAtlas(atlas) {}
153
154    private:
155        bool apply(GrContext* context, GrRenderTargetContext*, bool, bool, GrAppliedClip* out,
156                   SkRect* bounds) const override {
157            GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider();
158            out->addCoverageFP(skstd::make_unique<ClipFP>(proxyProvider, fTest, fAtlas));
159            return true;
160        }
161        bool quickContains(const SkRect&) const final { return false; }
162        bool isRRect(const SkRect& rtBounds, SkRRect* rr, GrAA*) const final { return false; }
163        void getConservativeBounds(int width, int height, SkIRect* rect, bool* iior) const final {
164            rect->set(0, 0, width, height);
165            if (iior) {
166                *iior = false;
167            }
168        }
169
170        LazyProxyTest* const fTest;
171        GrTextureProxy* fAtlas;
172    };
173
174private:
175    skiatest::Reporter* fReporter;
176    bool fHasOpTexture;
177    bool fHasClipTexture;
178};
179
180DEF_GPUTEST(LazyProxyTest, reporter, /* options */) {
181    GrMockOptions mockOptions;
182    mockOptions.fConfigOptions[kAlpha_half_GrPixelConfig].fRenderable[0] = true;
183    mockOptions.fConfigOptions[kAlpha_half_GrPixelConfig].fTexturable = true;
184    sk_sp<GrContext> ctx = GrContext::MakeMock(&mockOptions, GrContextOptions());
185    GrProxyProvider* proxyProvider = ctx->contextPriv().proxyProvider();
186    for (bool nullTexture : {false, true}) {
187        LazyProxyTest test(reporter);
188        ctx->contextPriv().addOnFlushCallbackObject(&test);
189        sk_sp<GrRenderTargetContext> rtc =
190                ctx->makeDeferredRenderTargetContext(SkBackingFit::kExact, 100, 100,
191                                                     kRGBA_8888_GrPixelConfig, nullptr);
192        REPORTER_ASSERT(reporter, rtc);
193        sk_sp<GrRenderTargetContext> mockAtlas =
194                ctx->makeDeferredRenderTargetContext(SkBackingFit::kExact, 10, 10,
195                                                     kAlpha_half_GrPixelConfig, nullptr);
196        REPORTER_ASSERT(reporter, mockAtlas);
197        rtc->priv().testingOnly_addDrawOp(LazyProxyTest::Clip(&test, mockAtlas->asTextureProxy()),
198                        skstd::make_unique<LazyProxyTest::Op>(proxyProvider, &test, nullTexture));
199        ctx->contextPriv().testingOnly_flushAndRemoveOnFlushCallbackObject(&test);
200    }
201}
202
203static const int kSize = 16;
204
205DEF_GPUTEST(LazyProxyReleaseTest, reporter, /* options */) {
206    GrMockOptions mockOptions;
207    sk_sp<GrContext> ctx = GrContext::MakeMock(&mockOptions, GrContextOptions());
208    auto proxyProvider = ctx->contextPriv().proxyProvider();
209
210    GrSurfaceDesc desc;
211    desc.fWidth = kSize;
212    desc.fHeight = kSize;
213    desc.fConfig = kRGBA_8888_GrPixelConfig;
214
215    for (bool doInstantiate : {true, false}) {
216        int testCount = 0;
217        int* testCountPtr = &testCount;
218        sk_sp<GrTextureProxy> proxy = proxyProvider->createLazyProxy(
219                [testCountPtr](GrResourceProvider* resourceProvider, GrSurfaceOrigin* outOrigin) {
220                    if (!resourceProvider) {
221                        *testCountPtr = -1;
222                        return sk_sp<GrTexture>();
223                    }
224                    *testCountPtr = 1;
225                    return sk_sp<GrTexture>();
226                }, desc, GrMipMapped::kNo, SkBackingFit::kExact, SkBudgeted::kNo);
227
228        REPORTER_ASSERT(reporter, 0 == testCount);
229
230        if (doInstantiate) {
231            proxy->priv().doLazyInstantiation(ctx->contextPriv().resourceProvider());
232            REPORTER_ASSERT(reporter, 1 == testCount);
233            proxy.reset();
234            REPORTER_ASSERT(reporter, -1 == testCount);
235        } else {
236            proxy.reset();
237            REPORTER_ASSERT(reporter, -1 == testCount);
238        }
239    }
240}
241
242class LazyFailedInstantiationTestOp : public GrDrawOp {
243public:
244    DEFINE_OP_CLASS_ID
245
246    LazyFailedInstantiationTestOp(GrProxyProvider* proxyProvider, int* testExecuteValue,
247                                  bool shouldFailInstantiation)
248            : INHERITED(ClassID())
249            , fTestExecuteValue(testExecuteValue) {
250        GrSurfaceDesc desc;
251        desc.fWidth = kSize;
252        desc.fHeight = kSize;
253        desc.fConfig = kRGBA_8888_GrPixelConfig;
254
255        fLazyProxy = proxyProvider->createLazyProxy(
256                [testExecuteValue, shouldFailInstantiation, desc] (
257                        GrResourceProvider* rp, GrSurfaceOrigin* /*origin*/) {
258                    if (!rp) {
259                        return sk_sp<GrTexture>();
260                    }
261                    if (shouldFailInstantiation) {
262                        *testExecuteValue = 1;
263                        return sk_sp<GrTexture>();
264                    }
265                    return rp->createTexture(desc, SkBudgeted::kNo);
266                }, desc, GrMipMapped::kNo, SkBackingFit::kExact, SkBudgeted::kNo);
267
268        this->setBounds(SkRect::MakeIWH(kSize, kSize),
269                        HasAABloat::kNo, IsZeroArea::kNo);
270    }
271
272    void visitProxies(const VisitProxyFunc& func) const override {
273        func(fLazyProxy.get());
274    }
275
276private:
277    const char* name() const override { return "LazyFailedInstantiationTestOp"; }
278    FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
279    RequiresDstTexture finalize(const GrCaps&, const GrAppliedClip*,
280                                GrPixelConfigIsClamped) override {
281        return RequiresDstTexture::kNo;
282    }
283    bool onCombineIfPossible(GrOp* other, const GrCaps& caps) override { return false; }
284    void onPrepare(GrOpFlushState*) override {}
285    void onExecute(GrOpFlushState* state) override {
286        *fTestExecuteValue = 2;
287    }
288
289    int* fTestExecuteValue;
290    sk_sp<GrSurfaceProxy> fLazyProxy;
291
292    typedef GrDrawOp INHERITED;
293};
294
295// Test that when a lazy proxy fails to instantiate during flush that we drop the Op that it was
296// associated with.
297DEF_GPUTEST(LazyProxyFailedInstantiationTest, reporter, /* options */) {
298    GrMockOptions mockOptions;
299    sk_sp<GrContext> ctx = GrContext::MakeMock(&mockOptions, GrContextOptions());
300    GrProxyProvider* proxyProvider = ctx->contextPriv().proxyProvider();
301    for (bool failInstantiation : {false, true}) {
302        sk_sp<GrRenderTargetContext> rtc =
303                ctx->makeDeferredRenderTargetContext(SkBackingFit::kExact, 100, 100,
304                                                     kRGBA_8888_GrPixelConfig, nullptr);
305        REPORTER_ASSERT(reporter, rtc);
306
307        rtc->clear(nullptr, 0xbaaaaaad, GrRenderTargetContext::CanClearFullscreen::kYes);
308
309        int executeTestValue = 0;
310        rtc->priv().testingOnly_addDrawOp(
311                skstd::make_unique<LazyFailedInstantiationTestOp>(proxyProvider, &executeTestValue,
312                                                                  failInstantiation));
313        ctx->flush();
314
315        if (failInstantiation) {
316#ifdef SK_DISABLE_EXPLICIT_GPU_RESOURCE_ALLOCATION
317            // When we disable explicit gpu resource allocation we don't throw away ops that have
318            // uninstantiated proxies.
319            REPORTER_ASSERT(reporter, 2 == executeTestValue);
320#else
321            REPORTER_ASSERT(reporter, 1 == executeTestValue);
322#endif
323        } else {
324            REPORTER_ASSERT(reporter, 2 == executeTestValue);
325        }
326    }
327
328}
329
330#endif
331