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 "include/core/SkAlphaType.h"
11 #include "include/core/SkBitmap.h"
12 #include "include/core/SkColorType.h"
13 #include "include/core/SkImage.h"
14 #include "include/core/SkImageInfo.h"
15 #include "include/core/SkRect.h"
16 #include "include/core/SkRefCnt.h"
17 #include "include/core/SkSize.h"
18 #include "include/core/SkTypes.h"
19 #include "include/gpu/GpuTypes.h"
20 #include "include/gpu/ganesh/GrBackendSurface.h"
21 #include "include/gpu/ganesh/GrDirectContext.h"
22 #include "include/gpu/ganesh/GrRecordingContext.h"
23 #include "include/gpu/ganesh/GrTypes.h"
24 #include "include/gpu/ganesh/SkImageGanesh.h"
25 #include "include/private/gpu/ganesh/GrTypesPriv.h"
26 #include "src/core/SkMessageBus.h"
27 #include "src/gpu/ResourceKey.h"
28 #include "src/gpu/SkBackingFit.h"
29 #include "src/gpu/ganesh/GrCaps.h"
30 #include "src/gpu/ganesh/GrDirectContextPriv.h"
31 #include "src/gpu/ganesh/GrGpuResourcePriv.h"
32 #include "src/gpu/ganesh/GrProxyProvider.h"
33 #include "src/gpu/ganesh/GrRecordingContextPriv.h"
34 #include "src/gpu/ganesh/GrResourceCache.h"
35 #include "src/gpu/ganesh/GrSurface.h"
36 #include "src/gpu/ganesh/GrTexture.h"
37 #include "src/gpu/ganesh/GrTextureProxy.h"
38 #include "src/gpu/ganesh/SkGr.h"
39 #include "tests/CtsEnforcement.h"
40 #include "tests/Test.h"
41 #include "tools/gpu/ManagedBackendTexture.h"
42
43 #include <cstddef>
44 #include <cstdint>
45 #include <initializer_list>
46
47 class GrResourceProvider;
48 struct GrContextOptions;
49
numUniqueKeyProxies_TestOnly() const50 int GrProxyProvider::numUniqueKeyProxies_TestOnly() const {
51 return fUniquelyKeyedProxies.count();
52 }
53
54 static constexpr auto kColorType = GrColorType::kRGBA_8888;
55 static constexpr auto kSize = SkISize::Make(64, 64);
56
57 ///////////////////////////////////////////////////////////////////////////////////////////////////
58 // Basic test
59
deferred_tex(skiatest::Reporter * reporter,GrRecordingContext * rContext,GrProxyProvider * proxyProvider,SkBackingFit fit)60 static sk_sp<GrTextureProxy> deferred_tex(skiatest::Reporter* reporter,
61 GrRecordingContext* rContext,
62 GrProxyProvider* proxyProvider,
63 SkBackingFit fit) {
64 using namespace skgpu;
65
66 const GrCaps* caps = rContext->priv().caps();
67
68 Protected isProtected = Protected(caps->supportsProtectedContent());
69
70 GrBackendFormat format = caps->getDefaultBackendFormat(kColorType, GrRenderable::kNo);
71
72 sk_sp<GrTextureProxy> proxy = proxyProvider->createProxy(format,
73 kSize,
74 GrRenderable::kNo,
75 1,
76 Mipmapped::kNo,
77 fit,
78 Budgeted::kYes,
79 isProtected,
80 /*label=*/{});
81 // Only budgeted & wrapped external proxies get to carry uniqueKeys
82 REPORTER_ASSERT(reporter, !proxy->getUniqueKey().isValid());
83 return proxy;
84 }
85
deferred_texRT(skiatest::Reporter * reporter,GrRecordingContext * rContext,GrProxyProvider * proxyProvider,SkBackingFit fit)86 static sk_sp<GrTextureProxy> deferred_texRT(skiatest::Reporter* reporter,
87 GrRecordingContext* rContext,
88 GrProxyProvider* proxyProvider,
89 SkBackingFit fit) {
90 using namespace skgpu;
91
92 const GrCaps* caps = rContext->priv().caps();
93
94 Protected isProtected = Protected(caps->supportsProtectedContent());
95
96 GrBackendFormat format = caps->getDefaultBackendFormat(kColorType, GrRenderable::kYes);
97
98 sk_sp<GrTextureProxy> proxy = proxyProvider->createProxy(format,
99 kSize,
100 GrRenderable::kYes,
101 1,
102 Mipmapped::kNo,
103 fit,
104 Budgeted::kYes,
105 isProtected,
106 /*label=*/{});
107 // Only budgeted & wrapped external proxies get to carry uniqueKeys
108 REPORTER_ASSERT(reporter, !proxy->getUniqueKey().isValid());
109 return proxy;
110 }
111
wrapped(skiatest::Reporter * reporter,GrRecordingContext * rContext,GrProxyProvider * proxyProvider,SkBackingFit fit)112 static sk_sp<GrTextureProxy> wrapped(skiatest::Reporter* reporter, GrRecordingContext* rContext,
113 GrProxyProvider* proxyProvider, SkBackingFit fit) {
114 using namespace skgpu;
115
116 Protected isProtected = Protected(rContext->priv().caps()->supportsProtectedContent());
117
118 sk_sp<GrTextureProxy> proxy = proxyProvider->testingOnly_createInstantiatedProxy(
119 kSize, kColorType, GrRenderable::kNo, 1, fit, Budgeted::kYes, isProtected);
120 // Only budgeted & wrapped external proxies get to carry uniqueKeys
121 REPORTER_ASSERT(reporter, !proxy->getUniqueKey().isValid());
122 return proxy;
123 }
124
wrapped_with_key(skiatest::Reporter * reporter,GrRecordingContext * rContext,GrProxyProvider * proxyProvider,SkBackingFit fit)125 static sk_sp<GrTextureProxy> wrapped_with_key(skiatest::Reporter* reporter,
126 GrRecordingContext* rContext,
127 GrProxyProvider* proxyProvider,
128 SkBackingFit fit) {
129 using namespace skgpu;
130
131 Protected isProtected = Protected(rContext->priv().caps()->supportsProtectedContent());
132
133 static UniqueKey::Domain d = UniqueKey::GenerateDomain();
134 static int kUniqueKeyData = 0;
135
136 UniqueKey key;
137
138 UniqueKey::Builder builder(&key, d, 1, nullptr);
139 builder[0] = kUniqueKeyData++;
140 builder.finish();
141
142 // Only budgeted & wrapped external proxies get to carry uniqueKeys
143 sk_sp<GrTextureProxy> proxy = proxyProvider->testingOnly_createInstantiatedProxy(
144 kSize, kColorType, GrRenderable::kNo, 1, fit, Budgeted::kYes, isProtected);
145 SkAssertResult(proxyProvider->assignUniqueKeyToProxy(key, proxy.get()));
146 REPORTER_ASSERT(reporter, proxy->getUniqueKey().isValid());
147 return proxy;
148 }
149
create_wrapped_backend(GrDirectContext * dContext)150 static sk_sp<GrTextureProxy> create_wrapped_backend(GrDirectContext* dContext) {
151 using namespace skgpu;
152
153 Protected isProtected = Protected(dContext->priv().caps()->supportsProtectedContent());
154
155 auto mbet = sk_gpu_test::ManagedBackendTexture::MakeWithoutData(
156 dContext,
157 kSize.width(),
158 kSize.height(),
159 GrColorTypeToSkColorType(kColorType),
160 Mipmapped::kNo,
161 GrRenderable::kNo,
162 isProtected);
163 if (!mbet) {
164 return nullptr;
165 }
166 GrProxyProvider* proxyProvider = dContext->priv().proxyProvider();
167 return proxyProvider->wrapBackendTexture(mbet->texture(),
168 kBorrow_GrWrapOwnership,
169 GrWrapCacheable::kYes,
170 kRead_GrIOType,
171 mbet->refCountedCallback());
172 }
173
174 // This tests the basic capabilities of the uniquely keyed texture proxies. Does assigning
175 // and looking them up work, etc.
basic_test(GrDirectContext * dContext,skiatest::Reporter * reporter,sk_sp<GrTextureProxy> proxy,int cacheEntriesPerProxy)176 static void basic_test(GrDirectContext* dContext,
177 skiatest::Reporter* reporter,
178 sk_sp<GrTextureProxy> proxy,
179 int cacheEntriesPerProxy) {
180 static int id = 1;
181
182 GrResourceProvider* resourceProvider = dContext->priv().resourceProvider();
183 GrProxyProvider* proxyProvider = dContext->priv().proxyProvider();
184 GrResourceCache* cache = dContext->priv().getResourceCache();
185
186 int startCacheCount = cache->getResourceCount();
187
188 skgpu::UniqueKey key;
189 if (proxy->getUniqueKey().isValid()) {
190 key = proxy->getUniqueKey();
191 } else {
192 GrMakeKeyFromImageID(&key, id, SkIRect::MakeWH(64, 64));
193 ++id;
194
195 // Assigning the uniqueKey adds the proxy to the hash but doesn't force instantiation
196 REPORTER_ASSERT(reporter, !proxyProvider->numUniqueKeyProxies_TestOnly());
197 SkAssertResult(proxyProvider->assignUniqueKeyToProxy(key, proxy.get()));
198 }
199
200 REPORTER_ASSERT(reporter, 1 == proxyProvider->numUniqueKeyProxies_TestOnly());
201 REPORTER_ASSERT(reporter, startCacheCount == cache->getResourceCount());
202
203 // setUniqueKey had better stick
204 REPORTER_ASSERT(reporter, key == proxy->getUniqueKey());
205
206 // We just added it, surely we can find it
207 REPORTER_ASSERT(reporter, proxyProvider->findOrCreateProxyByUniqueKey(key));
208 REPORTER_ASSERT(reporter, 1 == proxyProvider->numUniqueKeyProxies_TestOnly());
209
210 int expectedCacheCount = startCacheCount + (proxy->isInstantiated() ? 0 : cacheEntriesPerProxy);
211
212 // Once instantiated, the backing resource should have the same key
213 SkAssertResult(proxy->instantiate(resourceProvider));
214 const skgpu::UniqueKey texKey = proxy->peekSurface()->getUniqueKey();
215 REPORTER_ASSERT(reporter, texKey.isValid());
216 REPORTER_ASSERT(reporter, key == texKey);
217
218 // An Unbudgeted-cacheable resource will not get purged when a proxy with the same key is
219 // deleted.
220 bool expectResourceToOutliveProxy = proxy->peekSurface()->resourcePriv().budgetedType() ==
221 GrBudgetedType::kUnbudgetedCacheable;
222
223 // An Unbudgeted-uncacheable resource is never kept alive if it's ref cnt reaches zero even if
224 // it has a key.
225 bool expectDeletingProxyToDeleteResource =
226 proxy->peekSurface()->resourcePriv().budgetedType() ==
227 GrBudgetedType::kUnbudgetedUncacheable;
228
229 // deleting the proxy should delete it from the hash but not the cache
230 proxy = nullptr;
231 if (expectDeletingProxyToDeleteResource) {
232 expectedCacheCount -= cacheEntriesPerProxy;
233 }
234 REPORTER_ASSERT(reporter, 0 == proxyProvider->numUniqueKeyProxies_TestOnly());
235 REPORTER_ASSERT(reporter, expectedCacheCount == cache->getResourceCount());
236
237 // If the proxy was cached refinding it should bring it back to life
238 proxy = proxyProvider->findOrCreateProxyByUniqueKey(key);
239 REPORTER_ASSERT(reporter, proxy);
240 REPORTER_ASSERT(reporter, 1 == proxyProvider->numUniqueKeyProxies_TestOnly());
241 REPORTER_ASSERT(reporter, expectedCacheCount == cache->getResourceCount());
242
243 // Mega-purging it should remove it from both the hash and the cache
244 proxy = nullptr;
245 cache->purgeUnlockedResources(GrPurgeResourceOptions::kAllResources);
246 if (!expectResourceToOutliveProxy) {
247 expectedCacheCount -= cacheEntriesPerProxy;
248 }
249 REPORTER_ASSERT(reporter, expectedCacheCount == cache->getResourceCount());
250
251 // If the texture was deleted then the proxy should no longer be findable. Otherwise, it should
252 // be.
253 proxy = proxyProvider->findOrCreateProxyByUniqueKey(key);
254 REPORTER_ASSERT(reporter, expectResourceToOutliveProxy ? (bool)proxy : !proxy);
255 REPORTER_ASSERT(reporter, expectedCacheCount == cache->getResourceCount());
256
257 if (expectResourceToOutliveProxy) {
258 proxy.reset();
259 skgpu::UniqueKeyInvalidatedMessage msg(texKey, dContext->priv().contextID());
260 SkMessageBus<skgpu::UniqueKeyInvalidatedMessage, uint32_t>::Post(msg);
261 cache->purgeAsNeeded();
262 expectedCacheCount -= cacheEntriesPerProxy;
263 proxy = proxyProvider->findOrCreateProxyByUniqueKey(key);
264 REPORTER_ASSERT(reporter, !proxy);
265 REPORTER_ASSERT(reporter, expectedCacheCount == cache->getResourceCount());
266 }
267 }
268
269 ///////////////////////////////////////////////////////////////////////////////////////////////////
270 // Invalidation test
271
272 // Test if invalidating unique ids operates as expected for texture proxies.
invalidation_test(GrDirectContext * dContext,skiatest::Reporter * reporter,int cacheEntriesPerProxy)273 static void invalidation_test(GrDirectContext* dContext,
274 skiatest::Reporter* reporter,
275 int cacheEntriesPerProxy) {
276
277 GrProxyProvider* proxyProvider = dContext->priv().proxyProvider();
278 GrResourceCache* cache = dContext->priv().getResourceCache();
279 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
280
281 sk_sp<SkImage> rasterImg;
282
283 {
284 SkImageInfo ii = SkImageInfo::Make(64, 64, kRGBA_8888_SkColorType, kOpaque_SkAlphaType);
285
286 SkBitmap bm;
287 bm.allocPixels(ii);
288
289 rasterImg = bm.asImage();
290 REPORTER_ASSERT(reporter, 0 == proxyProvider->numUniqueKeyProxies_TestOnly());
291 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
292 }
293
294 // Some of our backends use buffers to do uploads that will live in our resource cache. So we
295 // need to account for those extra resources here.
296 int bufferResources = 0;
297 if (dContext->backend() == GrBackendApi::kVulkan ||
298 dContext->backend() == GrBackendApi::kDirect3D ||
299 dContext->backend() == GrBackendApi::kMetal) {
300 bufferResources = 1;
301 }
302
303 sk_sp<SkImage> textureImg = SkImages::TextureFromImage(dContext, rasterImg);
304 REPORTER_ASSERT(reporter, 0 == proxyProvider->numUniqueKeyProxies_TestOnly());
305 REPORTER_ASSERT(reporter, cacheEntriesPerProxy + bufferResources == cache->getResourceCount());
306
307 rasterImg = nullptr; // this invalidates the uniqueKey
308
309 // this forces the cache to respond to the inval msg
310 size_t maxBytes = dContext->getResourceCacheLimit();
311 dContext->setResourceCacheLimit(maxBytes-1);
312
313 REPORTER_ASSERT(reporter, 0 == proxyProvider->numUniqueKeyProxies_TestOnly());
314 REPORTER_ASSERT(reporter, cacheEntriesPerProxy + bufferResources == cache->getResourceCount());
315
316 textureImg = nullptr;
317
318 // For backends that use buffers to upload lets make sure that work has been submit and done
319 // before we try to purge all resources.
320 dContext->submit(GrSyncCpu::kYes);
321 dContext->priv().getResourceCache()->purgeUnlockedResources(
322 GrPurgeResourceOptions::kAllResources);
323
324 REPORTER_ASSERT(reporter, 0 == proxyProvider->numUniqueKeyProxies_TestOnly());
325 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
326 }
327
328 // Test if invalidating unique ids prior to instantiating operates as expected
invalidation_and_instantiation_test(GrDirectContext * dContext,skiatest::Reporter * reporter,int cacheEntriesPerProxy)329 static void invalidation_and_instantiation_test(GrDirectContext* dContext,
330 skiatest::Reporter* reporter,
331 int cacheEntriesPerProxy) {
332 GrProxyProvider* proxyProvider = dContext->priv().proxyProvider();
333 GrResourceProvider* resourceProvider = dContext->priv().resourceProvider();
334 GrResourceCache* cache = dContext->priv().getResourceCache();
335 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
336
337 static skgpu::UniqueKey::Domain d = skgpu::UniqueKey::GenerateDomain();
338 skgpu::UniqueKey key;
339 skgpu::UniqueKey::Builder builder(&key, d, 1, nullptr);
340 builder[0] = 0;
341 builder.finish();
342
343 // Create proxy, assign unique key
344 sk_sp<GrTextureProxy> proxy = deferred_tex(reporter, dContext, proxyProvider,
345 SkBackingFit::kExact);
346 SkAssertResult(proxyProvider->assignUniqueKeyToProxy(key, proxy.get()));
347
348 // Send an invalidation message, which will be sitting in the cache's inbox
349 SkMessageBus<skgpu::UniqueKeyInvalidatedMessage, uint32_t>::Post(
350 skgpu::UniqueKeyInvalidatedMessage(key, dContext->priv().contextID()));
351
352 REPORTER_ASSERT(reporter, 1 == proxyProvider->numUniqueKeyProxies_TestOnly());
353 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
354
355 // Instantiate the proxy. This will trigger the message to be processed, so the resulting
356 // texture should *not* have the unique key on it!
357 SkAssertResult(proxy->instantiate(resourceProvider));
358
359 REPORTER_ASSERT(reporter, !proxy->getUniqueKey().isValid());
360 REPORTER_ASSERT(reporter, !proxy->peekTexture()->getUniqueKey().isValid());
361 REPORTER_ASSERT(reporter, 0 == proxyProvider->numUniqueKeyProxies_TestOnly());
362 REPORTER_ASSERT(reporter, cacheEntriesPerProxy == cache->getResourceCount());
363
364 proxy = nullptr;
365 dContext->priv().getResourceCache()->purgeUnlockedResources(
366 GrPurgeResourceOptions::kAllResources);
367
368 REPORTER_ASSERT(reporter, 0 == proxyProvider->numUniqueKeyProxies_TestOnly());
369 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
370 }
371
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(TextureProxyTest,reporter,ctxInfo,CtsEnforcement::kApiLevel_T)372 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(TextureProxyTest,
373 reporter,
374 ctxInfo,
375 CtsEnforcement::kApiLevel_T) {
376 auto direct = ctxInfo.directContext();
377 GrProxyProvider* proxyProvider = direct->priv().proxyProvider();
378 GrResourceCache* cache = direct->priv().getResourceCache();
379
380 REPORTER_ASSERT(reporter, !proxyProvider->numUniqueKeyProxies_TestOnly());
381 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
382
383 // As we transition to using attachments instead of GrTextures and GrRenderTargets individual
384 // proxy instansiations may add multiple things to the cache. There would be an entry for the
385 // GrTexture/GrRenderTarget and entries for one or more attachments.
386 int cacheEntriesPerProxy = 1;
387 // We currently only have attachments on the vulkan and metal backends
388 if (direct->backend() == GrBackend::kVulkan || direct->backend() == GrBackend::kMetal) {
389 cacheEntriesPerProxy++;
390 // If we ever have a test with multisamples this would have an additional attachment as
391 // well.
392 }
393
394 for (auto fit : { SkBackingFit::kExact, SkBackingFit::kApprox }) {
395 for (auto create : { deferred_tex, deferred_texRT, wrapped, wrapped_with_key }) {
396 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
397 basic_test(direct, reporter, create(reporter, direct, proxyProvider, fit),
398 cacheEntriesPerProxy);
399 }
400
401 REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
402 cache->purgeUnlockedResources(GrPurgeResourceOptions::kAllResources);
403 }
404
405 basic_test(direct, reporter, create_wrapped_backend(direct), cacheEntriesPerProxy);
406
407 invalidation_test(direct, reporter, cacheEntriesPerProxy);
408 invalidation_and_instantiation_test(direct, reporter, cacheEntriesPerProxy);
409 }
410