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