• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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