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 // This is a GPU-backend specific test.
9 
10 #include "Test.h"
11 
12 #include "GrBackendSurface.h"
13 #include "GrContextPriv.h"
14 #include "GrResourceCache.h"
15 #include "GrProxyProvider.h"
16 #include "GrResourceProvider.h"
17 #include "GrTexture.h"
18 #include "GrTextureProxy.h"
19 
20 #include "SkGr.h"
21 #include "SkImage.h"
22 
numUniqueKeyProxies_TestOnly() const23 int GrProxyProvider::numUniqueKeyProxies_TestOnly() const {
24     return fUniquelyKeyedProxies.count();
25 }
26 
make_desc(GrSurfaceDescFlags descFlags)27 static GrSurfaceDesc make_desc(GrSurfaceDescFlags descFlags) {
28     GrSurfaceDesc desc;
29     desc.fFlags = descFlags;
30     desc.fWidth = 64;
31     desc.fHeight = 64;
32     desc.fConfig = kRGBA_8888_GrPixelConfig;
33     desc.fSampleCnt = 1;
34 
35     return desc;
36 }
37 
38 ///////////////////////////////////////////////////////////////////////////////////////////////////
39 // Basic test
40 
deferred_tex(skiatest::Reporter * reporter,GrContext * ctx,GrProxyProvider * proxyProvider,SkBackingFit fit)41 static sk_sp<GrTextureProxy> deferred_tex(skiatest::Reporter* reporter, GrContext* ctx,
42                                           GrProxyProvider* proxyProvider, SkBackingFit fit) {
43     const GrSurfaceDesc desc = make_desc(kNone_GrSurfaceFlags);
44     GrBackendFormat format =
45             ctx->priv().caps()->getBackendFormatFromColorType(kRGBA_8888_SkColorType);
46 
47     sk_sp<GrTextureProxy> proxy =
48             proxyProvider->createProxy(format, desc, kBottomLeft_GrSurfaceOrigin, fit,
49                                        SkBudgeted::kYes);
50     // Only budgeted & wrapped external proxies get to carry uniqueKeys
51     REPORTER_ASSERT(reporter, !proxy->getUniqueKey().isValid());
52     return proxy;
53 }
54 
deferred_texRT(skiatest::Reporter * reporter,GrContext * ctx,GrProxyProvider * proxyProvider,SkBackingFit fit)55 static sk_sp<GrTextureProxy> deferred_texRT(skiatest::Reporter* reporter, GrContext* ctx,
56                                             GrProxyProvider* proxyProvider, SkBackingFit fit) {
57     const GrSurfaceDesc desc = make_desc(kRenderTarget_GrSurfaceFlag);
58     GrBackendFormat format =
59             ctx->priv().caps()->getBackendFormatFromColorType(kRGBA_8888_SkColorType);
60 
61     sk_sp<GrTextureProxy> proxy =
62             proxyProvider->createProxy(format, desc, kBottomLeft_GrSurfaceOrigin, fit, SkBudgeted::kYes);
63     // Only budgeted & wrapped external proxies get to carry uniqueKeys
64     REPORTER_ASSERT(reporter, !proxy->getUniqueKey().isValid());
65     return proxy;
66 }
67 
wrapped(skiatest::Reporter * reporter,GrContext * ctx,GrProxyProvider * proxyProvider,SkBackingFit fit)68 static sk_sp<GrTextureProxy> wrapped(skiatest::Reporter* reporter, GrContext* ctx,
69                                      GrProxyProvider* proxyProvider, SkBackingFit fit) {
70     const GrSurfaceDesc desc = make_desc(kNone_GrSurfaceFlags);
71 
72     sk_sp<GrTextureProxy> proxy = proxyProvider->testingOnly_createInstantiatedProxy(
73             desc, kBottomLeft_GrSurfaceOrigin, fit, SkBudgeted::kYes);
74     // Only budgeted & wrapped external proxies get to carry uniqueKeys
75     REPORTER_ASSERT(reporter, !proxy->getUniqueKey().isValid());
76     return proxy;
77 }
78 
wrapped_with_key(skiatest::Reporter * reporter,GrContext * ctx,GrProxyProvider * proxyProvider,SkBackingFit fit)79 static sk_sp<GrTextureProxy> wrapped_with_key(skiatest::Reporter* reporter, GrContext* ctx,
80                                               GrProxyProvider* proxyProvider, SkBackingFit fit) {
81     static GrUniqueKey::Domain d = GrUniqueKey::GenerateDomain();
82     static int kUniqueKeyData = 0;
83 
84     GrUniqueKey key;
85 
86     GrUniqueKey::Builder builder(&key, d, 1, nullptr);
87     builder[0] = kUniqueKeyData++;
88     builder.finish();
89 
90     const GrSurfaceDesc desc = make_desc(kNone_GrSurfaceFlags);
91 
92     // Only budgeted & wrapped external proxies get to carry uniqueKeys
93     sk_sp<GrTextureProxy> proxy = proxyProvider->testingOnly_createInstantiatedProxy(
94             desc, kBottomLeft_GrSurfaceOrigin, fit, SkBudgeted::kYes);
95     SkAssertResult(proxyProvider->assignUniqueKeyToProxy(key, proxy.get()));
96     REPORTER_ASSERT(reporter, proxy->getUniqueKey().isValid());
97     return proxy;
98 }
99 
create_wrapped_backend(GrContext * context,SkBackingFit fit,sk_sp<GrTexture> * backingSurface)100 static sk_sp<GrTextureProxy> create_wrapped_backend(GrContext* context, SkBackingFit fit,
101                                                     sk_sp<GrTexture>* backingSurface) {
102     GrProxyProvider* proxyProvider = context->priv().proxyProvider();
103     GrResourceProvider* resourceProvider = context->priv().resourceProvider();
104 
105     const GrSurfaceDesc desc = make_desc(kNone_GrSurfaceFlags);
106 
107     *backingSurface = resourceProvider->createTexture(desc, SkBudgeted::kNo);
108     if (!(*backingSurface)) {
109         return nullptr;
110     }
111 
112     GrBackendTexture backendTex = (*backingSurface)->getBackendTexture();
113     backendTex.setPixelConfig(desc.fConfig);
114 
115     return proxyProvider->wrapBackendTexture(backendTex, kBottomLeft_GrSurfaceOrigin,
116                                              kBorrow_GrWrapOwnership, GrWrapCacheable::kYes,
117                                              kRead_GrIOType);
118 }
119 
120 
121 // This tests the basic capabilities of the uniquely keyed texture proxies. Does assigning
122 // and looking them up work, etc.
basic_test(GrContext * context,skiatest::Reporter * reporter,sk_sp<GrTextureProxy> proxy)123 static void basic_test(GrContext* context,
124                        skiatest::Reporter* reporter,
125                        sk_sp<GrTextureProxy> proxy) {
126     static int id = 1;
127 
128     GrResourceProvider* resourceProvider = context->priv().resourceProvider();
129     GrProxyProvider* proxyProvider = context->priv().proxyProvider();
130     GrResourceCache* cache = context->priv().getResourceCache();
131 
132     int startCacheCount = cache->getResourceCount();
133 
134     GrUniqueKey key;
135     if (proxy->getUniqueKey().isValid()) {
136         key = proxy->getUniqueKey();
137     } else {
138         GrMakeKeyFromImageID(&key, id, SkIRect::MakeWH(64, 64));
139         ++id;
140 
141         // Assigning the uniqueKey adds the proxy to the hash but doesn't force instantiation
142         REPORTER_ASSERT(reporter, !proxyProvider->numUniqueKeyProxies_TestOnly());
143         SkAssertResult(proxyProvider->assignUniqueKeyToProxy(key, proxy.get()));
144     }
145 
146     REPORTER_ASSERT(reporter, 1 == proxyProvider->numUniqueKeyProxies_TestOnly());
147     REPORTER_ASSERT(reporter, startCacheCount == cache->getResourceCount());
148 
149     // setUniqueKey had better stick
150     REPORTER_ASSERT(reporter, key == proxy->getUniqueKey());
151 
152     // We just added it, surely we can find it
153     REPORTER_ASSERT(reporter, proxyProvider->findOrCreateProxyByUniqueKey(
154                                                                 key, kBottomLeft_GrSurfaceOrigin));
155     REPORTER_ASSERT(reporter, 1 == proxyProvider->numUniqueKeyProxies_TestOnly());
156 
157     int expectedCacheCount = startCacheCount + (proxy->isInstantiated() ? 0 : 1);
158 
159     // Once instantiated, the backing resource should have the same key
160     SkAssertResult(proxy->instantiate(resourceProvider));
161     const GrUniqueKey texKey = proxy->peekSurface()->getUniqueKey();
162     REPORTER_ASSERT(reporter, texKey.isValid());
163     REPORTER_ASSERT(reporter, key == texKey);
164 
165     // An Unbudgeted-cacheable resource will not get purged when a proxy with the same key is
166     // deleted.
167     bool expectResourceToOutliveProxy = proxy->peekSurface()->resourcePriv().budgetedType() ==
168                                         GrBudgetedType::kUnbudgetedCacheable;
169 
170     // An Unbudgeted-uncacheable resource is never kept alive if it's ref cnt reaches zero even if
171     // it has a key.
172     bool expectDeletingProxyToDeleteResource =
173             proxy->peekSurface()->resourcePriv().budgetedType() ==
174             GrBudgetedType::kUnbudgetedUncacheable;
175 
176     // deleting the proxy should delete it from the hash but not the cache
177     proxy = nullptr;
178     if (expectDeletingProxyToDeleteResource) {
179         expectedCacheCount -= 1;
180     }
181     REPORTER_ASSERT(reporter, 0 == proxyProvider->numUniqueKeyProxies_TestOnly());
182     REPORTER_ASSERT(reporter, expectedCacheCount == cache->getResourceCount());
183 
184     // If the proxy was cached refinding it should bring it back to life
185     proxy = proxyProvider->findOrCreateProxyByUniqueKey(key, kBottomLeft_GrSurfaceOrigin);
186     REPORTER_ASSERT(reporter, proxy);
187     REPORTER_ASSERT(reporter, 1 == proxyProvider->numUniqueKeyProxies_TestOnly());
188     REPORTER_ASSERT(reporter, expectedCacheCount == cache->getResourceCount());
189 
190     // Mega-purging it should remove it from both the hash and the cache
191     proxy = nullptr;
192     cache->purgeAllUnlocked();
193     if (!expectResourceToOutliveProxy) {
194         expectedCacheCount--;
195     }
196     REPORTER_ASSERT(reporter, expectedCacheCount == cache->getResourceCount());
197 
198     // If the texture was deleted then the proxy should no longer be findable. Otherwise, it should
199     // be.
200     proxy = proxyProvider->findOrCreateProxyByUniqueKey(key, kBottomLeft_GrSurfaceOrigin);
201     REPORTER_ASSERT(reporter, expectResourceToOutliveProxy ? (bool)proxy : !proxy);
202     REPORTER_ASSERT(reporter, expectedCacheCount == cache->getResourceCount());
203 
204     if (expectResourceToOutliveProxy) {
205         proxy.reset();
206         GrUniqueKeyInvalidatedMessage msg(texKey, context->priv().contextID());
207         SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(msg);
208         cache->purgeAsNeeded();
209         expectedCacheCount--;
210         proxy = proxyProvider->findOrCreateProxyByUniqueKey(key, kBottomLeft_GrSurfaceOrigin);
211         REPORTER_ASSERT(reporter, !proxy);
212         REPORTER_ASSERT(reporter, expectedCacheCount == cache->getResourceCount());
213     }
214 }
215 
216 ///////////////////////////////////////////////////////////////////////////////////////////////////
217 // Invalidation test
218 
219 // Test if invalidating unique ids operates as expected for texture proxies.
invalidation_test(GrContext * context,skiatest::Reporter * reporter)220 static void invalidation_test(GrContext* context, skiatest::Reporter* reporter) {
221 
222     GrProxyProvider* proxyProvider = context->priv().proxyProvider();
223     GrResourceCache* cache = context->priv().getResourceCache();
224     REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
225 
226     sk_sp<SkImage> rasterImg;
227 
228     {
229         SkImageInfo ii = SkImageInfo::Make(64, 64, kRGBA_8888_SkColorType, kOpaque_SkAlphaType);
230 
231         SkBitmap bm;
232         bm.allocPixels(ii);
233 
234         rasterImg = SkImage::MakeFromBitmap(bm);
235         REPORTER_ASSERT(reporter, 0 == proxyProvider->numUniqueKeyProxies_TestOnly());
236         REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
237     }
238 
239     sk_sp<SkImage> textureImg = rasterImg->makeTextureImage(context, nullptr);
240     REPORTER_ASSERT(reporter, 1 == proxyProvider->numUniqueKeyProxies_TestOnly());
241     REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
242 
243     rasterImg = nullptr;        // this invalidates the uniqueKey
244 
245     // this forces the cache to respond to the inval msg
246     int maxNum;
247     size_t maxBytes;
248     context->getResourceCacheLimits(&maxNum, &maxBytes);
249     context->setResourceCacheLimits(maxNum-1, maxBytes);
250 
251     REPORTER_ASSERT(reporter, 0 == proxyProvider->numUniqueKeyProxies_TestOnly());
252     REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
253 
254     textureImg = nullptr;
255     context->priv().testingOnly_purgeAllUnlockedResources();
256 
257     REPORTER_ASSERT(reporter, 0 == proxyProvider->numUniqueKeyProxies_TestOnly());
258     REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
259 }
260 
261 // Test if invalidating unique ids prior to instantiating operates as expected
invalidation_and_instantiation_test(GrContext * context,skiatest::Reporter * reporter)262 static void invalidation_and_instantiation_test(GrContext* context, skiatest::Reporter* reporter) {
263     GrProxyProvider* proxyProvider = context->priv().proxyProvider();
264     GrResourceProvider* resourceProvider = context->priv().resourceProvider();
265     GrResourceCache* cache = context->priv().getResourceCache();
266     REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
267 
268     static GrUniqueKey::Domain d = GrUniqueKey::GenerateDomain();
269     GrUniqueKey key;
270     GrUniqueKey::Builder builder(&key, d, 1, nullptr);
271     builder[0] = 0;
272     builder.finish();
273 
274     // Create proxy, assign unique key
275     sk_sp<GrTextureProxy> proxy = deferred_tex(reporter, context, proxyProvider,
276                                                SkBackingFit::kExact);
277     SkAssertResult(proxyProvider->assignUniqueKeyToProxy(key, proxy.get()));
278 
279     // Send an invalidation message, which will be sitting in the cache's inbox
280     SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(
281             GrUniqueKeyInvalidatedMessage(key, context->priv().contextID()));
282 
283     REPORTER_ASSERT(reporter, 1 == proxyProvider->numUniqueKeyProxies_TestOnly());
284     REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
285 
286     // Instantiate the proxy. This will trigger the message to be processed, so the resulting
287     // texture should *not* have the unique key on it!
288     SkAssertResult(proxy->instantiate(resourceProvider));
289 
290     REPORTER_ASSERT(reporter, !proxy->getUniqueKey().isValid());
291     REPORTER_ASSERT(reporter, !proxy->peekTexture()->getUniqueKey().isValid());
292     REPORTER_ASSERT(reporter, 0 == proxyProvider->numUniqueKeyProxies_TestOnly());
293     REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
294 
295     proxy = nullptr;
296     context->priv().testingOnly_purgeAllUnlockedResources();
297 
298     REPORTER_ASSERT(reporter, 0 == proxyProvider->numUniqueKeyProxies_TestOnly());
299     REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
300 }
301 
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(TextureProxyTest,reporter,ctxInfo)302 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(TextureProxyTest, reporter, ctxInfo) {
303     GrContext* context = ctxInfo.grContext();
304     GrProxyProvider* proxyProvider = context->priv().proxyProvider();
305     GrResourceCache* cache = context->priv().getResourceCache();
306 
307     REPORTER_ASSERT(reporter, !proxyProvider->numUniqueKeyProxies_TestOnly());
308     REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
309 
310     for (auto fit : { SkBackingFit::kExact, SkBackingFit::kApprox }) {
311         for (auto create : { deferred_tex, deferred_texRT, wrapped, wrapped_with_key }) {
312             REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
313             basic_test(context, reporter, create(reporter, context, proxyProvider, fit));
314         }
315 
316         REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
317         sk_sp<GrTexture> backingTex;
318         sk_sp<GrTextureProxy> proxy = create_wrapped_backend(context, fit, &backingTex);
319         basic_test(context, reporter, std::move(proxy));
320 
321         backingTex = nullptr;
322         cache->purgeAllUnlocked();
323     }
324 
325     invalidation_test(context, reporter);
326     invalidation_and_instantiation_test(context, reporter);
327 }
328