• 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 "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