xref: /aosp_15_r20/external/skia/tests/TextureProxyTest.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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