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 "SkTypes.h"
9 
10 #include "Test.h"
11 
12 #include "GrContextPriv.h"
13 #include "GrDeinstantiateProxyTracker.h"
14 #include "GrGpu.h"
15 #include "GrProxyProvider.h"
16 #include "GrResourceAllocator.h"
17 #include "GrResourceProvider.h"
18 #include "GrSurfaceProxyPriv.h"
19 #include "GrTexture.h"
20 #include "GrTextureProxy.h"
21 
22 #include "SkSurface.h"
23 
24 struct ProxyParams {
25     int             fSize;
26     bool            fIsRT;
27     SkColorType     fColorType;
28     SkBackingFit    fFit;
29     int             fSampleCnt;
30     GrSurfaceOrigin fOrigin;
31     // TODO: do we care about mipmapping
32 };
33 
make_deferred(GrProxyProvider * proxyProvider,const GrCaps * caps,const ProxyParams & p)34 static GrSurfaceProxy* make_deferred(GrProxyProvider* proxyProvider, const GrCaps* caps,
35                                      const ProxyParams& p) {
36     GrColorType grCT = SkColorTypeToGrColorType(p.fColorType);
37     GrPixelConfig config = GrColorTypeToPixelConfig(grCT, GrSRGBEncoded::kNo);
38 
39     GrSurfaceDesc desc;
40     desc.fFlags = p.fIsRT ? kRenderTarget_GrSurfaceFlag : kNone_GrSurfaceFlags;
41     desc.fWidth  = p.fSize;
42     desc.fHeight = p.fSize;
43     desc.fConfig = config;
44     desc.fSampleCnt = p.fSampleCnt;
45 
46     const GrBackendFormat format = caps->getBackendFormatFromColorType(p.fColorType);
47 
48     auto tmp = proxyProvider->createProxy(format, desc, p.fOrigin, p.fFit, SkBudgeted::kNo);
49     if (!tmp) {
50         return nullptr;
51     }
52     GrSurfaceProxy* ret = tmp.release();
53 
54     // Add a read to keep the proxy around but unref it so its backing surfaces can be recycled
55     ret->addPendingRead();
56     ret->unref();
57     return ret;
58 }
59 
make_backend(GrContext * context,const ProxyParams & p,GrBackendTexture * backendTex)60 static GrSurfaceProxy* make_backend(GrContext* context, const ProxyParams& p,
61                                     GrBackendTexture* backendTex) {
62     GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider();
63     GrGpu* gpu = context->contextPriv().getGpu();
64 
65     *backendTex = gpu->createTestingOnlyBackendTexture(nullptr, p.fSize, p.fSize,
66                                                        p.fColorType, false,
67                                                        GrMipMapped::kNo);
68     if (!backendTex->isValid()) {
69         return nullptr;
70     }
71 
72     auto tmp = proxyProvider->wrapBackendTexture(*backendTex, p.fOrigin, kBorrow_GrWrapOwnership,
73                                                  GrWrapCacheable::kNo, kRead_GrIOType);
74     if (!tmp) {
75         return nullptr;
76     }
77     GrSurfaceProxy* ret = tmp.release();
78 
79     // Add a read to keep the proxy around but unref it so its backing surfaces can be recycled
80     ret->addPendingRead();
81     ret->unref();
82     return ret;
83 }
84 
cleanup_backend(GrContext * context,const GrBackendTexture & backendTex)85 static void cleanup_backend(GrContext* context, const GrBackendTexture& backendTex) {
86     context->contextPriv().getGpu()->deleteTestingOnlyBackendTexture(backendTex);
87 }
88 
89 // Basic test that two proxies with overlapping intervals and compatible descriptors are
90 // assigned different GrSurfaces.
overlap_test(skiatest::Reporter * reporter,GrResourceProvider * resourceProvider,GrSurfaceProxy * p1,GrSurfaceProxy * p2,bool expectedResult)91 static void overlap_test(skiatest::Reporter* reporter, GrResourceProvider* resourceProvider,
92                          GrSurfaceProxy* p1, GrSurfaceProxy* p2, bool expectedResult) {
93     GrDeinstantiateProxyTracker deinstantiateTracker;
94     GrResourceAllocator alloc(resourceProvider, &deinstantiateTracker);
95 
96     alloc.addInterval(p1, 0, 4);
97     alloc.addInterval(p2, 1, 2);
98     alloc.markEndOfOpList(0);
99 
100     int startIndex, stopIndex;
101     GrResourceAllocator::AssignError error;
102     alloc.assign(&startIndex, &stopIndex, &error);
103     REPORTER_ASSERT(reporter, GrResourceAllocator::AssignError::kNoError == error);
104 
105     REPORTER_ASSERT(reporter, p1->peekSurface());
106     REPORTER_ASSERT(reporter, p2->peekSurface());
107     bool doTheBackingStoresMatch = p1->underlyingUniqueID() == p2->underlyingUniqueID();
108     REPORTER_ASSERT(reporter, expectedResult == doTheBackingStoresMatch);
109 }
110 
111 // Test various cases when two proxies do not have overlapping intervals.
112 // This mainly acts as a test of the ResourceAllocator's free pool.
non_overlap_test(skiatest::Reporter * reporter,GrResourceProvider * resourceProvider,GrSurfaceProxy * p1,GrSurfaceProxy * p2,bool expectedResult)113 static void non_overlap_test(skiatest::Reporter* reporter, GrResourceProvider* resourceProvider,
114                              GrSurfaceProxy* p1, GrSurfaceProxy* p2,
115                              bool expectedResult) {
116     GrDeinstantiateProxyTracker deinstantiateTracker;
117     GrResourceAllocator alloc(resourceProvider, &deinstantiateTracker);
118 
119     alloc.addInterval(p1, 0, 2);
120     alloc.addInterval(p2, 3, 5);
121     alloc.markEndOfOpList(0);
122 
123     int startIndex, stopIndex;
124     GrResourceAllocator::AssignError error;
125     alloc.assign(&startIndex, &stopIndex, &error);
126     REPORTER_ASSERT(reporter, GrResourceAllocator::AssignError::kNoError == error);
127 
128     REPORTER_ASSERT(reporter, p1->peekSurface());
129     REPORTER_ASSERT(reporter, p2->peekSurface());
130     bool doTheBackingStoresMatch = p1->underlyingUniqueID() == p2->underlyingUniqueID();
131     REPORTER_ASSERT(reporter, expectedResult == doTheBackingStoresMatch);
132 }
133 
testingOnly_setExplicitlyAllocateGPUResources(bool newValue)134 bool GrResourceProvider::testingOnly_setExplicitlyAllocateGPUResources(bool newValue) {
135     bool oldValue = fExplicitlyAllocateGPUResources;
136     fExplicitlyAllocateGPUResources = newValue;
137     return oldValue;
138 }
139 
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ResourceAllocatorTest,reporter,ctxInfo)140 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ResourceAllocatorTest, reporter, ctxInfo) {
141     const GrCaps* caps = ctxInfo.grContext()->contextPriv().caps();
142     GrProxyProvider* proxyProvider = ctxInfo.grContext()->contextPriv().proxyProvider();
143     GrResourceProvider* resourceProvider = ctxInfo.grContext()->contextPriv().resourceProvider();
144 
145     bool orig = resourceProvider->testingOnly_setExplicitlyAllocateGPUResources(true);
146 
147     struct TestCase {
148         ProxyParams   fP1;
149         ProxyParams   fP2;
150         bool          fExpectation;
151     };
152 
153     constexpr bool kRT = true;
154     constexpr bool kNotRT = false;
155 
156     constexpr bool kShare = true;
157     constexpr bool kDontShare = false;
158     // Non-RT GrSurfaces are never recycled on some platforms.
159     bool kConditionallyShare = resourceProvider->caps()->reuseScratchTextures();
160 
161     const SkColorType kRGBA = kRGBA_8888_SkColorType;
162     const SkColorType kBGRA = kBGRA_8888_SkColorType;
163 
164     const SkBackingFit kE = SkBackingFit::kExact;
165     const SkBackingFit kA = SkBackingFit::kApprox;
166 
167     const GrSurfaceOrigin kTL = kTopLeft_GrSurfaceOrigin;
168     const GrSurfaceOrigin kBL = kBottomLeft_GrSurfaceOrigin;
169 
170     //--------------------------------------------------------------------------------------------
171     TestCase gOverlappingTests[] = {
172         //----------------------------------------------------------------------------------------
173         // Two proxies with overlapping intervals and compatible descriptors should never share
174         // RT version
175         { { 64,    kRT, kRGBA, kA, 0, kTL }, { 64,    kRT, kRGBA, kA, 0, kTL }, kDontShare },
176         // non-RT version
177         { { 64, kNotRT, kRGBA, kA, 0, kTL }, { 64, kNotRT, kRGBA, kA, 0, kTL }, kDontShare },
178     };
179 
180     for (auto test : gOverlappingTests) {
181         GrSurfaceProxy* p1 = make_deferred(proxyProvider, caps, test.fP1);
182         GrSurfaceProxy* p2 = make_deferred(proxyProvider, caps, test.fP2);
183         overlap_test(reporter, resourceProvider, p1, p2, test.fExpectation);
184         p1->completedRead();
185         p2->completedRead();
186     }
187 
188     int k2 = ctxInfo.grContext()->contextPriv().caps()->getRenderTargetSampleCount(
189                                                                     2, kRGBA_8888_GrPixelConfig);
190     int k4 = ctxInfo.grContext()->contextPriv().caps()->getRenderTargetSampleCount(
191                                                                     4, kRGBA_8888_GrPixelConfig);
192 
193     //--------------------------------------------------------------------------------------------
194     TestCase gNonOverlappingTests[] = {
195         //----------------------------------------------------------------------------------------
196         // Two non-overlapping intervals w/ compatible proxies should share
197         // both same size & approx
198         { { 64,    kRT, kRGBA, kA, 0, kTL }, { 64,    kRT, kRGBA, kA, 0, kTL }, kShare },
199         { { 64, kNotRT, kRGBA, kA, 0, kTL }, { 64, kNotRT, kRGBA, kA, 0, kTL }, kConditionallyShare },
200         // diffs sizes but still approx
201         { { 64,    kRT, kRGBA, kA, 0, kTL }, { 50,    kRT, kRGBA, kA, 0, kTL }, kShare },
202         { { 64, kNotRT, kRGBA, kA, 0, kTL }, { 50, kNotRT, kRGBA, kA, 0, kTL }, kConditionallyShare },
203         // sames sizes but exact
204         { { 64,    kRT, kRGBA, kE, 0, kTL }, { 64,    kRT, kRGBA, kE, 0, kTL }, kShare },
205         { { 64, kNotRT, kRGBA, kE, 0, kTL }, { 64, kNotRT, kRGBA, kE, 0, kTL }, kConditionallyShare },
206         //----------------------------------------------------------------------------------------
207         // Two non-overlapping intervals w/ different exact sizes should not share
208         { { 56,    kRT, kRGBA, kE, 0, kTL }, { 54,    kRT, kRGBA, kE, 0, kTL }, kDontShare },
209         // Two non-overlapping intervals w/ _very different_ approx sizes should not share
210         { { 255,   kRT, kRGBA, kA, 0, kTL }, { 127,   kRT, kRGBA, kA, 0, kTL }, kDontShare },
211         // Two non-overlapping intervals w/ different MSAA sample counts should not share
212         { { 64,    kRT, kRGBA, kA, k2, kTL },{ 64,    kRT, kRGBA, kA, k4, kTL}, k2 == k4 },
213         // Two non-overlapping intervals w/ different configs should not share
214         { { 64,    kRT, kRGBA, kA, 0, kTL }, { 64,    kRT, kBGRA, kA, 0, kTL }, kDontShare },
215         // Two non-overlapping intervals w/ different RT classifications should never share
216         { { 64,    kRT, kRGBA, kA, 0, kTL }, { 64, kNotRT, kRGBA, kA, 0, kTL }, kDontShare },
217         { { 64, kNotRT, kRGBA, kA, 0, kTL }, { 64,    kRT, kRGBA, kA, 0, kTL }, kDontShare },
218         // Two non-overlapping intervals w/ different origins should share
219         { { 64,    kRT, kRGBA, kA, 0, kTL }, { 64,    kRT, kRGBA, kA, 0, kBL }, kShare },
220     };
221 
222     for (auto test : gNonOverlappingTests) {
223         GrSurfaceProxy* p1 = make_deferred(proxyProvider, caps, test.fP1);
224         GrSurfaceProxy* p2 = make_deferred(proxyProvider, caps, test.fP2);
225 
226         if (!p1 || !p2) {
227             continue; // creation can fail (i.e., for msaa4 on iOS)
228         }
229 
230         non_overlap_test(reporter, resourceProvider, p1, p2, test.fExpectation);
231 
232         p1->completedRead();
233         p2->completedRead();
234     }
235 
236     {
237         // Wrapped backend textures should never be reused
238         TestCase t[1] = {
239             { { 64, kNotRT, kRGBA, kE, 0, kTL }, { 64, kNotRT, kRGBA, kE, 0, kTL }, kDontShare }
240         };
241 
242         GrBackendTexture backEndTex;
243         GrSurfaceProxy* p1 = make_backend(ctxInfo.grContext(), t[0].fP1, &backEndTex);
244         GrSurfaceProxy* p2 = make_deferred(proxyProvider, caps, t[0].fP2);
245 
246         non_overlap_test(reporter, resourceProvider, p1, p2, t[0].fExpectation);
247 
248         p1->completedRead();
249         p2->completedRead();
250 
251         cleanup_backend(ctxInfo.grContext(), backEndTex);
252     }
253 
254     resourceProvider->testingOnly_setExplicitlyAllocateGPUResources(orig);
255 }
256 
draw(GrContext * context)257 static void draw(GrContext* context) {
258     SkImageInfo ii = SkImageInfo::Make(1024, 1024, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
259 
260     sk_sp<SkSurface> s = SkSurface::MakeRenderTarget(context, SkBudgeted::kYes,
261                                                      ii, 1, kTopLeft_GrSurfaceOrigin, nullptr);
262 
263     SkCanvas* c = s->getCanvas();
264 
265     c->clear(SK_ColorBLACK);
266 }
267 
268 
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ResourceAllocatorStressTest,reporter,ctxInfo)269 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ResourceAllocatorStressTest, reporter, ctxInfo) {
270     GrContext* context = ctxInfo.grContext();
271     GrResourceProvider* resourceProvider = ctxInfo.grContext()->contextPriv().resourceProvider();
272 
273     int maxNum;
274     size_t maxBytes;
275     context->getResourceCacheLimits(&maxNum, &maxBytes);
276 
277     bool orig = resourceProvider->testingOnly_setExplicitlyAllocateGPUResources(true);
278     context->setResourceCacheLimits(0, 0); // We'll always be overbudget
279 
280     draw(context);
281     draw(context);
282     draw(context);
283     draw(context);
284     context->flush();
285 
286     context->setResourceCacheLimits(maxNum, maxBytes);
287     resourceProvider->testingOnly_setExplicitlyAllocateGPUResources(orig);
288 }
289 
make_lazy(GrProxyProvider * proxyProvider,const GrCaps * caps,const ProxyParams & p,bool deinstantiate)290 sk_sp<GrSurfaceProxy> make_lazy(GrProxyProvider* proxyProvider, const GrCaps* caps,
291                                 const ProxyParams& p, bool deinstantiate) {
292     GrColorType grCT = SkColorTypeToGrColorType(p.fColorType);
293     GrPixelConfig config = GrColorTypeToPixelConfig(grCT, GrSRGBEncoded::kNo);
294 
295     GrSurfaceDesc desc;
296     desc.fFlags = p.fIsRT ? kRenderTarget_GrSurfaceFlag : kNone_GrSurfaceFlags;
297     desc.fWidth = p.fSize;
298     desc.fHeight = p.fSize;
299     desc.fConfig = config;
300     desc.fSampleCnt = p.fSampleCnt;
301 
302     SkBackingFit fit = p.fFit;
303     auto callback = [fit, desc](GrResourceProvider* resourceProvider) -> sk_sp<GrSurface> {
304         if (!resourceProvider) {
305             return nullptr;
306         }
307         if (fit == SkBackingFit::kApprox) {
308             return resourceProvider->createApproxTexture(desc, GrResourceProvider::Flags::kNone);
309         } else {
310             return resourceProvider->createTexture(desc, SkBudgeted::kNo);
311         }
312     };
313     const GrBackendFormat format = caps->getBackendFormatFromColorType(p.fColorType);
314     auto lazyType = deinstantiate ? GrSurfaceProxy::LazyInstantiationType ::kDeinstantiate
315                                   : GrSurfaceProxy::LazyInstantiationType ::kSingleUse;
316     GrInternalSurfaceFlags flags = GrInternalSurfaceFlags::kNone;
317     return proxyProvider->createLazyProxy(callback, format, desc, p.fOrigin, GrMipMapped::kNo,
318                                           flags, p.fFit, SkBudgeted::kNo, lazyType);
319 }
320 
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(LazyDeinstantiation,reporter,ctxInfo)321 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(LazyDeinstantiation, reporter, ctxInfo) {
322     GrContext* context = ctxInfo.grContext();
323     GrResourceProvider* resourceProvider = ctxInfo.grContext()->contextPriv().resourceProvider();
324     for (auto explicitlyAllocating : {false, true}) {
325         resourceProvider->testingOnly_setExplicitlyAllocateGPUResources(explicitlyAllocating);
326         ProxyParams texParams;
327         texParams.fFit = SkBackingFit::kExact;
328         texParams.fOrigin = kTopLeft_GrSurfaceOrigin;
329         texParams.fColorType = kRGBA_8888_SkColorType;
330         texParams.fIsRT = false;
331         texParams.fSampleCnt = 1;
332         texParams.fSize = 100;
333         ProxyParams rtParams = texParams;
334         rtParams.fIsRT = true;
335         auto proxyProvider = context->contextPriv().proxyProvider();
336         auto caps = context->contextPriv().caps();
337         auto p0 = make_lazy(proxyProvider, caps, texParams, true);
338         auto p1 = make_lazy(proxyProvider, caps, texParams, false);
339         texParams.fFit = rtParams.fFit = SkBackingFit::kApprox;
340         auto p2 = make_lazy(proxyProvider, caps, rtParams, true);
341         auto p3 = make_lazy(proxyProvider, caps, rtParams, false);
342 
343         GrDeinstantiateProxyTracker deinstantiateTracker;
344         {
345             GrResourceAllocator alloc(resourceProvider, &deinstantiateTracker);
346             alloc.addInterval(p0.get(), 0, 1);
347             alloc.addInterval(p1.get(), 0, 1);
348             alloc.addInterval(p2.get(), 0, 1);
349             alloc.addInterval(p3.get(), 0, 1);
350             alloc.markEndOfOpList(0);
351             int startIndex, stopIndex;
352             GrResourceAllocator::AssignError error;
353             alloc.assign(&startIndex, &stopIndex, &error);
354         }
355         deinstantiateTracker.deinstantiateAllProxies();
356         REPORTER_ASSERT(reporter, !p0->isInstantiated());
357         REPORTER_ASSERT(reporter, p1->isInstantiated());
358         REPORTER_ASSERT(reporter, !p2->isInstantiated());
359         REPORTER_ASSERT(reporter, p3->isInstantiated());
360     }
361 }
362