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/core/SkBitmap.h"
13 #include "include/core/SkImage.h"
14 #include "include/gpu/GrBackendSurface.h"
15 #include "include/gpu/GrDirectContext.h"
16 #include "src/gpu/GrDirectContextPriv.h"
17 #include "src/gpu/GrProxyProvider.h"
18 #include "src/gpu/GrRecordingContextPriv.h"
19 #include "src/gpu/GrResourceCache.h"
20 #include "src/gpu/GrResourceProvider.h"
21 #include "src/gpu/GrTexture.h"
22 #include "src/gpu/GrTextureProxy.h"
23 #include "src/gpu/SkGr.h"
24 #include "tools/gpu/ManagedBackendTexture.h"
25
26 #ifdef SK_DAWN
27 #include "src/gpu/dawn/GrDawnGpu.h"
28 #endif
29
numUniqueKeyProxies_TestOnly() const30 int GrProxyProvider::numUniqueKeyProxies_TestOnly() const {
31 return fUniquelyKeyedProxies.count();
32 }
33
34 static constexpr auto kColorType = GrColorType::kRGBA_8888;
35 static constexpr auto kSize = SkISize::Make(64, 64);
36
37 ///////////////////////////////////////////////////////////////////////////////////////////////////
38 // Basic test
39
deferred_tex(skiatest::Reporter * reporter,GrRecordingContext * rContext,GrProxyProvider * proxyProvider,SkBackingFit fit)40 static sk_sp<GrTextureProxy> deferred_tex(skiatest::Reporter* reporter,
41 GrRecordingContext* rContext,
42 GrProxyProvider* proxyProvider,
43 SkBackingFit fit) {
44 const GrCaps* caps = rContext->priv().caps();
45
46 GrBackendFormat format = caps->getDefaultBackendFormat(kColorType, GrRenderable::kNo);
47
48 sk_sp<GrTextureProxy> proxy =
49 proxyProvider->createProxy(format, kSize, GrRenderable::kNo, 1, GrMipmapped::kNo, fit,
50 SkBudgeted::kYes, GrProtected::kNo);
51 // Only budgeted & wrapped external proxies get to carry uniqueKeys
52 REPORTER_ASSERT(reporter, !proxy->getUniqueKey().isValid());
53 return proxy;
54 }
55
deferred_texRT(skiatest::Reporter * reporter,GrRecordingContext * rContext,GrProxyProvider * proxyProvider,SkBackingFit fit)56 static sk_sp<GrTextureProxy> deferred_texRT(skiatest::Reporter* reporter,
57 GrRecordingContext* rContext,
58 GrProxyProvider* proxyProvider,
59 SkBackingFit fit) {
60 const GrCaps* caps = rContext->priv().caps();
61
62 GrBackendFormat format = caps->getDefaultBackendFormat(kColorType, GrRenderable::kYes);
63
64 sk_sp<GrTextureProxy> proxy =
65 proxyProvider->createProxy(format, kSize, GrRenderable::kYes, 1, GrMipmapped::kNo, fit,
66 SkBudgeted::kYes, GrProtected::kNo);
67 // Only budgeted & wrapped external proxies get to carry uniqueKeys
68 REPORTER_ASSERT(reporter, !proxy->getUniqueKey().isValid());
69 return proxy;
70 }
71
wrapped(skiatest::Reporter * reporter,GrRecordingContext *,GrProxyProvider * proxyProvider,SkBackingFit fit)72 static sk_sp<GrTextureProxy> wrapped(skiatest::Reporter* reporter, GrRecordingContext*,
73 GrProxyProvider* proxyProvider, SkBackingFit fit) {
74 sk_sp<GrTextureProxy> proxy = proxyProvider->testingOnly_createInstantiatedProxy(
75 kSize, kColorType, GrRenderable::kNo, 1, fit, 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,GrRecordingContext *,GrProxyProvider * proxyProvider,SkBackingFit fit)81 static sk_sp<GrTextureProxy> wrapped_with_key(skiatest::Reporter* reporter, GrRecordingContext*,
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, fit, SkBudgeted::kYes, GrProtected::kNo);
95 SkAssertResult(proxyProvider->assignUniqueKeyToProxy(key, proxy.get()));
96 REPORTER_ASSERT(reporter, proxy->getUniqueKey().isValid());
97 return proxy;
98 }
99
create_wrapped_backend(GrDirectContext * dContext)100 static sk_sp<GrTextureProxy> create_wrapped_backend(GrDirectContext* dContext) {
101 auto mbet = sk_gpu_test::ManagedBackendTexture::MakeWithoutData(
102 dContext,
103 kSize.width(),
104 kSize.height(),
105 GrColorTypeToSkColorType(kColorType),
106 GrMipmapped::kNo,
107 GrRenderable::kNo,
108 GrProtected::kNo);
109 if (!mbet) {
110 return nullptr;
111 }
112 GrProxyProvider* proxyProvider = dContext->priv().proxyProvider();
113 return proxyProvider->wrapBackendTexture(mbet->texture(),
114 kBorrow_GrWrapOwnership,
115 GrWrapCacheable::kYes,
116 kRead_GrIOType,
117 mbet->refCountedCallback());
118 }
119
120 // This tests the basic capabilities of the uniquely keyed texture proxies. Does assigning
121 // and looking them up work, etc.
basic_test(GrDirectContext * dContext,skiatest::Reporter * reporter,sk_sp<GrTextureProxy> proxy,int cacheEntriesPerProxy)122 static void basic_test(GrDirectContext* dContext,
123 skiatest::Reporter* reporter,
124 sk_sp<GrTextureProxy> proxy,
125 int cacheEntriesPerProxy) {
126 static int id = 1;
127
128 GrResourceProvider* resourceProvider = dContext->priv().resourceProvider();
129 GrProxyProvider* proxyProvider = dContext->priv().proxyProvider();
130 GrResourceCache* cache = dContext->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(key));
154 REPORTER_ASSERT(reporter, 1 == proxyProvider->numUniqueKeyProxies_TestOnly());
155
156 int expectedCacheCount = startCacheCount + (proxy->isInstantiated() ? 0 : cacheEntriesPerProxy);
157
158 // Once instantiated, the backing resource should have the same key
159 SkAssertResult(proxy->instantiate(resourceProvider));
160 const GrUniqueKey texKey = proxy->peekSurface()->getUniqueKey();
161 REPORTER_ASSERT(reporter, texKey.isValid());
162 REPORTER_ASSERT(reporter, key == texKey);
163
164 // An Unbudgeted-cacheable resource will not get purged when a proxy with the same key is
165 // deleted.
166 bool expectResourceToOutliveProxy = proxy->peekSurface()->resourcePriv().budgetedType() ==
167 GrBudgetedType::kUnbudgetedCacheable;
168
169 // An Unbudgeted-uncacheable resource is never kept alive if it's ref cnt reaches zero even if
170 // it has a key.
171 bool expectDeletingProxyToDeleteResource =
172 proxy->peekSurface()->resourcePriv().budgetedType() ==
173 GrBudgetedType::kUnbudgetedUncacheable;
174
175 // deleting the proxy should delete it from the hash but not the cache
176 proxy = nullptr;
177 if (expectDeletingProxyToDeleteResource) {
178 expectedCacheCount -= cacheEntriesPerProxy;
179 }
180 REPORTER_ASSERT(reporter, 0 == proxyProvider->numUniqueKeyProxies_TestOnly());
181 REPORTER_ASSERT(reporter, expectedCacheCount == cache->getResourceCount());
182
183 // If the proxy was cached refinding it should bring it back to life
184 proxy = proxyProvider->findOrCreateProxyByUniqueKey(key);
185 REPORTER_ASSERT(reporter, proxy);
186 REPORTER_ASSERT(reporter, 1 == proxyProvider->numUniqueKeyProxies_TestOnly());
187 REPORTER_ASSERT(reporter, expectedCacheCount == cache->getResourceCount());
188
189 // Mega-purging it should remove it from both the hash and the cache
190 proxy = nullptr;
191 cache->purgeUnlockedResources();
192 if (!expectResourceToOutliveProxy) {
193 expectedCacheCount -= cacheEntriesPerProxy;
194 }
195 REPORTER_ASSERT(reporter, expectedCacheCount == cache->getResourceCount());
196
197 // If the texture was deleted then the proxy should no longer be findable. Otherwise, it should
198 // be.
199 proxy = proxyProvider->findOrCreateProxyByUniqueKey(key);
200 REPORTER_ASSERT(reporter, expectResourceToOutliveProxy ? (bool)proxy : !proxy);
201 REPORTER_ASSERT(reporter, expectedCacheCount == cache->getResourceCount());
202
203 if (expectResourceToOutliveProxy) {
204 proxy.reset();
205 GrUniqueKeyInvalidatedMessage msg(texKey, dContext->priv().contextID());
206 SkMessageBus<GrUniqueKeyInvalidatedMessage, uint32_t>::Post(msg);
207 cache->purgeAsNeeded();
208 expectedCacheCount -= cacheEntriesPerProxy;
209 proxy = proxyProvider->findOrCreateProxyByUniqueKey(key);
210 REPORTER_ASSERT(reporter, !proxy);
211 REPORTER_ASSERT(reporter, expectedCacheCount == cache->getResourceCount());
212 }
213 }
214
215 ///////////////////////////////////////////////////////////////////////////////////////////////////
216 // Invalidation test
217
218 // Test if invalidating unique ids operates as expected for texture proxies.
invalidation_test(GrDirectContext * dContext,skiatest::Reporter * reporter,int cacheEntriesPerProxy)219 static void invalidation_test(GrDirectContext* dContext,
220 skiatest::Reporter* reporter,
221 int cacheEntriesPerProxy) {
222
223 GrProxyProvider* proxyProvider = dContext->priv().proxyProvider();
224 GrResourceCache* cache = dContext->priv().getResourceCache();
225 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
226
227 sk_sp<SkImage> rasterImg;
228
229 {
230 SkImageInfo ii = SkImageInfo::Make(64, 64, kRGBA_8888_SkColorType, kOpaque_SkAlphaType);
231
232 SkBitmap bm;
233 bm.allocPixels(ii);
234
235 rasterImg = bm.asImage();
236 REPORTER_ASSERT(reporter, 0 == proxyProvider->numUniqueKeyProxies_TestOnly());
237 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
238 }
239
240 // Some of our backends use buffers to do uploads that will live in our resource cache. So we
241 // need to account for those extra resources here.
242 int bufferResources = 0;
243 if (dContext->backend() == GrBackendApi::kDawn ||
244 dContext->backend() == GrBackendApi::kVulkan ||
245 dContext->backend() == GrBackendApi::kDirect3D ||
246 dContext->backend() == GrBackendApi::kMetal) {
247 bufferResources = 1;
248 }
249
250 sk_sp<SkImage> textureImg = rasterImg->makeTextureImage(dContext);
251 REPORTER_ASSERT(reporter, 0 == proxyProvider->numUniqueKeyProxies_TestOnly());
252 REPORTER_ASSERT(reporter, cacheEntriesPerProxy + bufferResources == cache->getResourceCount());
253
254 rasterImg = nullptr; // this invalidates the uniqueKey
255
256 // this forces the cache to respond to the inval msg
257 size_t maxBytes = dContext->getResourceCacheLimit();
258 dContext->setResourceCacheLimit(maxBytes-1);
259
260 REPORTER_ASSERT(reporter, 0 == proxyProvider->numUniqueKeyProxies_TestOnly());
261 REPORTER_ASSERT(reporter, cacheEntriesPerProxy + bufferResources == cache->getResourceCount());
262
263 textureImg = nullptr;
264
265 // For backends that use buffers to upload lets make sure that work has been submit and done
266 // before we try to purge all resources.
267 dContext->submit(true);
268
269 #ifdef SK_DAWN
270 // The forced cpu sync in dawn doesn't actually mean the async map will finish thus we may
271 // still have a ref on the GrGpuBuffer and it will not get purged by the call below. We dig
272 // deep into the dawn gpu to make sure we wait for the async map to finish.
273 if (dContext->backend() == GrBackendApi::kDawn) {
274 GrDawnGpu* gpu = static_cast<GrDawnGpu*>(dContext->priv().getGpu());
275 gpu->waitOnAllBusyStagingBuffers();
276 }
277 #endif
278
279 dContext->priv().getResourceCache()->purgeUnlockedResources();
280
281 REPORTER_ASSERT(reporter, 0 == proxyProvider->numUniqueKeyProxies_TestOnly());
282 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
283 }
284
285 // Test if invalidating unique ids prior to instantiating operates as expected
invalidation_and_instantiation_test(GrDirectContext * dContext,skiatest::Reporter * reporter,int cacheEntriesPerProxy)286 static void invalidation_and_instantiation_test(GrDirectContext* dContext,
287 skiatest::Reporter* reporter,
288 int cacheEntriesPerProxy) {
289 GrProxyProvider* proxyProvider = dContext->priv().proxyProvider();
290 GrResourceProvider* resourceProvider = dContext->priv().resourceProvider();
291 GrResourceCache* cache = dContext->priv().getResourceCache();
292 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
293
294 static GrUniqueKey::Domain d = GrUniqueKey::GenerateDomain();
295 GrUniqueKey key;
296 GrUniqueKey::Builder builder(&key, d, 1, nullptr);
297 builder[0] = 0;
298 builder.finish();
299
300 // Create proxy, assign unique key
301 sk_sp<GrTextureProxy> proxy = deferred_tex(reporter, dContext, proxyProvider,
302 SkBackingFit::kExact);
303 SkAssertResult(proxyProvider->assignUniqueKeyToProxy(key, proxy.get()));
304
305 // Send an invalidation message, which will be sitting in the cache's inbox
306 SkMessageBus<GrUniqueKeyInvalidatedMessage, uint32_t>::Post(
307 GrUniqueKeyInvalidatedMessage(key, dContext->priv().contextID()));
308
309 REPORTER_ASSERT(reporter, 1 == proxyProvider->numUniqueKeyProxies_TestOnly());
310 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
311
312 // Instantiate the proxy. This will trigger the message to be processed, so the resulting
313 // texture should *not* have the unique key on it!
314 SkAssertResult(proxy->instantiate(resourceProvider));
315
316 REPORTER_ASSERT(reporter, !proxy->getUniqueKey().isValid());
317 REPORTER_ASSERT(reporter, !proxy->peekTexture()->getUniqueKey().isValid());
318 REPORTER_ASSERT(reporter, 0 == proxyProvider->numUniqueKeyProxies_TestOnly());
319 REPORTER_ASSERT(reporter, cacheEntriesPerProxy == cache->getResourceCount());
320
321 proxy = nullptr;
322 dContext->priv().getResourceCache()->purgeUnlockedResources();
323
324 REPORTER_ASSERT(reporter, 0 == proxyProvider->numUniqueKeyProxies_TestOnly());
325 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
326 }
327
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(TextureProxyTest,reporter,ctxInfo)328 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(TextureProxyTest, reporter, ctxInfo) {
329 auto direct = ctxInfo.directContext();
330 GrProxyProvider* proxyProvider = direct->priv().proxyProvider();
331 GrResourceCache* cache = direct->priv().getResourceCache();
332
333 REPORTER_ASSERT(reporter, !proxyProvider->numUniqueKeyProxies_TestOnly());
334 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
335
336 // As we transition to using attachments instead of GrTextures and GrRenderTargets individual
337 // proxy instansiations may add multiple things to the cache. There would be an entry for the
338 // GrTexture/GrRenderTarget and entries for one or more attachments.
339 int cacheEntriesPerProxy = 1;
340 // We currently only have attachments on the vulkan and metal backends
341 if (direct->backend() == GrBackend::kVulkan || direct->backend() == GrBackend::kMetal) {
342 cacheEntriesPerProxy++;
343 // If we ever have a test with multisamples this would have an additional attachment as
344 // well.
345 }
346
347 for (auto fit : { SkBackingFit::kExact, SkBackingFit::kApprox }) {
348 for (auto create : { deferred_tex, deferred_texRT, wrapped, wrapped_with_key }) {
349 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
350 basic_test(direct, reporter, create(reporter, direct, proxyProvider, fit),
351 cacheEntriesPerProxy);
352 }
353
354 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
355 cache->purgeUnlockedResources();
356 }
357
358 basic_test(direct, reporter, create_wrapped_backend(direct), cacheEntriesPerProxy);
359
360 invalidation_test(direct, reporter, cacheEntriesPerProxy);
361 invalidation_and_instantiation_test(direct, reporter, cacheEntriesPerProxy);
362 }
363