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