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