Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
ProxyCacheTest.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2023 Google LLC
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#include "tests/Test.h"
9
20#include "tools/DecodeUtils.h"
21#include "tools/Resources.h"
23
24#include <thread>
25
26namespace skgpu::graphite {
27
28// This test exercises the basic MessageBus behavior of the ProxyCache by manually inserting an
29// SkBitmap into the proxy cache and then changing its contents. This simple test should create
30// an IDChangeListener that will remove the entry in the cache when the bitmap is changed and
31// the resulting message processed.
33 std::unique_ptr<Recorder> recorder = context->makeRecorder();
34 ProxyCache* proxyCache = recorder->priv().proxyCache();
35
37 bool success = ToolUtils::GetResourceAsBitmap("images/mandrill_128.png", &bitmap);
38 REPORTER_ASSERT(r, success);
39 if (!success) {
40 return;
41 }
42
43 REPORTER_ASSERT(r, proxyCache->numCached() == 0);
44
45 sk_sp<TextureProxy> proxy = proxyCache->findOrCreateCachedProxy(recorder.get(), bitmap,
46 Mipmapped::kNo);
47
48 REPORTER_ASSERT(r, proxyCache->numCached() == 1);
49
50 bitmap.eraseColor(SK_ColorBLACK);
51
52 proxyCache->forceProcessInvalidKeyMsgs();
53
54 REPORTER_ASSERT(r, proxyCache->numCached() == 0);
55}
56
57// This test checks that, if the same bitmap is added to two separate ProxyCaches, when it is
58// changed, both of the ProxyCaches will receive the message.
60 std::unique_ptr<Recorder> recorder1 = context->makeRecorder();
61 ProxyCache* proxyCache1 = recorder1->priv().proxyCache();
62 std::unique_ptr<Recorder> recorder2 = context->makeRecorder();
63 ProxyCache* proxyCache2 = recorder2->priv().proxyCache();
64
66 bool success = ToolUtils::GetResourceAsBitmap("images/mandrill_128.png", &bitmap);
67 REPORTER_ASSERT(r, success);
68 if (!success) {
69 return;
70 }
71
72 REPORTER_ASSERT(r, proxyCache1->numCached() == 0);
73 REPORTER_ASSERT(r, proxyCache2->numCached() == 0);
74
75 sk_sp<TextureProxy> proxy1 = proxyCache1->findOrCreateCachedProxy(recorder1.get(), bitmap,
76 Mipmapped::kNo);
77 sk_sp<TextureProxy> proxy2 = proxyCache2->findOrCreateCachedProxy(recorder2.get(), bitmap,
78 Mipmapped::kNo);
79
80 REPORTER_ASSERT(r, proxyCache1->numCached() == 1);
81 REPORTER_ASSERT(r, proxyCache2->numCached() == 1);
82
83 bitmap.eraseColor(SK_ColorBLACK);
84
85 proxyCache1->forceProcessInvalidKeyMsgs();
86 proxyCache2->forceProcessInvalidKeyMsgs();
87
88 REPORTER_ASSERT(r, proxyCache1->numCached() == 0);
89 REPORTER_ASSERT(r, proxyCache2->numCached() == 0);
90}
91
92// This test exercises mipmap selectivity of the ProxyCache. Mipmapped and non-mipmapped version
93// of the same bitmap are keyed differently. Requesting a non-mipmapped version will
94// return a mipmapped version, if it exists.
96 std::unique_ptr<Recorder> recorder = context->makeRecorder();
97 ProxyCache* proxyCache = recorder->priv().proxyCache();
98
100 bool success = ToolUtils::GetResourceAsBitmap("images/mandrill_128.png", &bitmap);
101 REPORTER_ASSERT(r, success);
102 if (!success) {
103 return;
104 }
105
106 REPORTER_ASSERT(r, proxyCache->numCached() == 0);
107
108 sk_sp<TextureProxy> nonMipmapped = proxyCache->findOrCreateCachedProxy(recorder.get(), bitmap,
109 Mipmapped::kNo);
110 REPORTER_ASSERT(r, nonMipmapped->mipmapped() == Mipmapped::kNo);
111 REPORTER_ASSERT(r, proxyCache->numCached() == 1);
112
113 sk_sp<TextureProxy> test = proxyCache->findOrCreateCachedProxy(recorder.get(), bitmap,
114 Mipmapped::kNo);
115 REPORTER_ASSERT(r, nonMipmapped == test);
116 REPORTER_ASSERT(r, proxyCache->numCached() == 1);
117
118 sk_sp<TextureProxy> mipmapped = proxyCache->findOrCreateCachedProxy(recorder.get(), bitmap,
119 Mipmapped::kYes);
120 REPORTER_ASSERT(r, mipmapped->mipmapped() == Mipmapped::kYes);
121 REPORTER_ASSERT(r, mipmapped != nonMipmapped);
122
123 REPORTER_ASSERT(r, proxyCache->numCached() == 2);
124
125 test = proxyCache->findOrCreateCachedProxy(recorder.get(), bitmap, Mipmapped::kNo);
126 REPORTER_ASSERT(r, mipmapped == test);
127
128 REPORTER_ASSERT(r, proxyCache->numCached() == 2);
129
130 test = proxyCache->findOrCreateCachedProxy(recorder.get(), bitmap, Mipmapped::kYes);
131 REPORTER_ASSERT(r, mipmapped == test);
132
133 REPORTER_ASSERT(r, proxyCache->numCached() == 2);
134}
135
136namespace {
137
138struct ProxyCacheSetup {
139 bool valid() const {
140 return !fBitmap1.empty() && !fBitmap2.empty() && fProxy1 && fProxy2;
141 }
142
147
148 skgpu::StdSteadyClock::time_point fTimeBetweenProxyCreation;
149 skgpu::StdSteadyClock::time_point fTimeAfterAllProxyCreation;
150};
151
152ProxyCacheSetup setup_test(Context* context,
154 Recorder* recorder,
156 ProxyCache* proxyCache = recorder->priv().proxyCache();
157
158 ProxyCacheSetup setup;
159
160 bool success1 = ToolUtils::GetResourceAsBitmap("images/mandrill_32.png", &setup.fBitmap1);
161 bool success2 = ToolUtils::GetResourceAsBitmap("images/mandrill_64.png", &setup.fBitmap2);
162 if (!success1 || !success2) {
163 return {};
164 }
165
166 REPORTER_ASSERT(r, proxyCache->numCached() == 0);
167
168 setup.fProxy1 = proxyCache->findOrCreateCachedProxy(recorder, setup.fBitmap1, Mipmapped::kNo);
169 REPORTER_ASSERT(r, proxyCache->numCached() == 1);
170
171 {
172 // Ensure proxy1's Texture is created (and timestamped) at this time
173 auto recording = recorder->snap();
174 context->insertRecording({ recording.get() });
175 context->submit(SyncToCpu::kYes);
176 }
177
178 std::this_thread::sleep_for(std::chrono::milliseconds(2));
179 setup.fTimeBetweenProxyCreation = skgpu::StdSteadyClock::now();
180 std::this_thread::sleep_for(std::chrono::milliseconds(2));
181
182 setup.fProxy2 = proxyCache->findOrCreateCachedProxy(recorder, setup.fBitmap2, Mipmapped::kNo);
183 REPORTER_ASSERT(r, proxyCache->numCached() == 2);
184
185 {
186 // Ensure proxy2's Texture is created (and timestamped) at this time
187 auto recording = recorder->snap();
188 context->insertRecording({ recording.get() });
189 testContext->syncedSubmit(context);
190 }
191
192 std::this_thread::sleep_for(std::chrono::milliseconds(2));
193 setup.fTimeAfterAllProxyCreation = skgpu::StdSteadyClock::now();
194 std::this_thread::sleep_for(std::chrono::milliseconds(2));
195
196 return setup;
197}
198
199} // anonymous namespace
200
201// This test exercises the ProxyCache's freeUniquelyHeld method.
203 r,
204 context,
205 testContext,
206 true,
208 std::unique_ptr<Recorder> recorder = context->makeRecorder();
209 ProxyCache* proxyCache = recorder->priv().proxyCache();
210
211 ProxyCacheSetup setup = setup_test(context, testContext, recorder.get(), r);
212 REPORTER_ASSERT(r, setup.valid());
213 if (!setup.valid()) {
214 return;
215 }
216
217 proxyCache->forceFreeUniquelyHeld();
218 REPORTER_ASSERT(r, proxyCache->numCached() == 2);
219
220 setup.fProxy1.reset();
221 proxyCache->forceFreeUniquelyHeld();
222 REPORTER_ASSERT(r, proxyCache->numCached() == 1);
223
224 setup.fProxy2.reset();
225 proxyCache->forceFreeUniquelyHeld();
226 REPORTER_ASSERT(r, proxyCache->numCached() == 0);
227}
228
229// This test exercises the ProxyCache's purgeProxiesNotUsedSince method.
231 r,
232 context,
233 testContext,
234 true,
236 std::unique_ptr<Recorder> recorder = context->makeRecorder();
237 ProxyCache* proxyCache = recorder->priv().proxyCache();
238
239 ProxyCacheSetup setup = setup_test(context, testContext, recorder.get(), r);
240 REPORTER_ASSERT(r, setup.valid());
241 if (!setup.valid()) {
242 return;
243 }
244
245 REPORTER_ASSERT(r, !setup.fProxy1->texture()->testingShouldDeleteASAP());
246 REPORTER_ASSERT(r, !setup.fProxy2->texture()->testingShouldDeleteASAP());
247
248 proxyCache->forcePurgeProxiesNotUsedSince(setup.fTimeBetweenProxyCreation);
249 REPORTER_ASSERT(r, proxyCache->numCached() == 1);
250 REPORTER_ASSERT(r, setup.fProxy1->texture()->testingShouldDeleteASAP());
251 REPORTER_ASSERT(r, !setup.fProxy2->texture()->testingShouldDeleteASAP());
252
253 sk_sp<TextureProxy> test = proxyCache->find(setup.fBitmap1, Mipmapped::kNo);
254 REPORTER_ASSERT(r, !test); // proxy1 should've been purged
255
256 proxyCache->forcePurgeProxiesNotUsedSince(setup.fTimeAfterAllProxyCreation);
257 REPORTER_ASSERT(r, proxyCache->numCached() == 0);
258 REPORTER_ASSERT(r, setup.fProxy1->texture()->testingShouldDeleteASAP());
259 REPORTER_ASSERT(r, setup.fProxy2->texture()->testingShouldDeleteASAP());
260}
261
262// This test simply verifies that the ProxyCache is correctly updating the Resource's
263// last access time stamp.
265 r,
266 context,
267 testContext,
268 true,
270 std::unique_ptr<Recorder> recorder = context->makeRecorder();
271 ProxyCache* proxyCache = recorder->priv().proxyCache();
272
273 ProxyCacheSetup setup = setup_test(context, testContext, recorder.get(), r);
274 REPORTER_ASSERT(r, setup.valid());
275 if (!setup.valid()) {
276 return;
277 }
278
279 REPORTER_ASSERT(r, !setup.fProxy1->texture()->testingShouldDeleteASAP());
280 REPORTER_ASSERT(r, !setup.fProxy2->texture()->testingShouldDeleteASAP());
281
282 // update proxy1's timestamp
283 sk_sp<TextureProxy> test = proxyCache->findOrCreateCachedProxy(recorder.get(), setup.fBitmap1,
284 Mipmapped::kNo);
285 REPORTER_ASSERT(r, test == setup.fProxy1);
286
287 std::this_thread::sleep_for(std::chrono::milliseconds(2));
288 auto timeAfterProxy1Update = skgpu::StdSteadyClock::now();
289
290 proxyCache->forcePurgeProxiesNotUsedSince(setup.fTimeBetweenProxyCreation);
291 REPORTER_ASSERT(r, proxyCache->numCached() == 2);
292 REPORTER_ASSERT(r, !setup.fProxy1->texture()->testingShouldDeleteASAP());
293 REPORTER_ASSERT(r, !setup.fProxy2->texture()->testingShouldDeleteASAP());
294
295 proxyCache->forcePurgeProxiesNotUsedSince(setup.fTimeAfterAllProxyCreation);
296 REPORTER_ASSERT(r, proxyCache->numCached() == 1);
297 REPORTER_ASSERT(r, !setup.fProxy1->texture()->testingShouldDeleteASAP());
298 REPORTER_ASSERT(r, setup.fProxy2->texture()->testingShouldDeleteASAP());
299
300 test = proxyCache->find(setup.fBitmap2, Mipmapped::kNo);
301 REPORTER_ASSERT(r, !test); // proxy2 should've been purged
302
303 proxyCache->forcePurgeProxiesNotUsedSince(timeAfterProxy1Update);
304 REPORTER_ASSERT(r, proxyCache->numCached() == 0);
305 REPORTER_ASSERT(r, setup.fProxy1->texture()->testingShouldDeleteASAP());
306 REPORTER_ASSERT(r, setup.fProxy2->texture()->testingShouldDeleteASAP());
307}
308
309// Verify that the ProxyCache's purgeProxiesNotUsedSince method can clear out multiple proxies.
311 r,
312 context,
313 testContext,
314 true,
316 std::unique_ptr<Recorder> recorder = context->makeRecorder();
317 ProxyCache* proxyCache = recorder->priv().proxyCache();
318
319 ProxyCacheSetup setup = setup_test(context, testContext, recorder.get(), r);
320 REPORTER_ASSERT(r, setup.valid());
321 if (!setup.valid()) {
322 return;
323 }
324
325 REPORTER_ASSERT(r, !setup.fProxy1->texture()->testingShouldDeleteASAP());
326 REPORTER_ASSERT(r, !setup.fProxy2->texture()->testingShouldDeleteASAP());
327
328 proxyCache->forcePurgeProxiesNotUsedSince(setup.fTimeAfterAllProxyCreation);
329 REPORTER_ASSERT(r, proxyCache->numCached() == 0);
330 REPORTER_ASSERT(r, setup.fProxy1->texture()->testingShouldDeleteASAP());
331 REPORTER_ASSERT(r, setup.fProxy2->texture()->testingShouldDeleteASAP());
332}
333
334// Verify that the ProxyCache's freeUniquelyHeld behavior is working in the ResourceCache.
336 r,
337 context,
338 testContext,
339 true,
341 std::unique_ptr<Recorder> recorder = context->makeRecorder();
342 ResourceCache* resourceCache = recorder->priv().resourceCache();
343 ProxyCache* proxyCache = recorder->priv().proxyCache();
344
345 resourceCache->setMaxBudget(0);
346
347 ProxyCacheSetup setup = setup_test(context, testContext, recorder.get(), r);
348 REPORTER_ASSERT(r, setup.valid());
349 if (!setup.valid()) {
350 return;
351 }
352
353 resourceCache->forcePurgeAsNeeded();
354
355 REPORTER_ASSERT(r, proxyCache->numCached() == 2);
356
357 setup.fProxy1.reset();
358 proxyCache->forceProcessInvalidKeyMsgs();
359
360 // unreffing fProxy1 and forcing message processing shouldn't purge proxy1 from the cache
361 sk_sp<TextureProxy> test = proxyCache->find(setup.fBitmap1, Mipmapped::kNo);
363 test.reset();
364
365 resourceCache->forcePurgeAsNeeded();
366
367 REPORTER_ASSERT(r, proxyCache->numCached() == 1);
368 test = proxyCache->find(setup.fBitmap1, Mipmapped::kNo);
369 REPORTER_ASSERT(r, !test); // proxy1 should've been purged
370
371 setup.fProxy2.reset();
372 proxyCache->forceProcessInvalidKeyMsgs();
373
374 // unreffing fProxy2 and forcing message processing shouldn't purge proxy2 from the cache
375 test = proxyCache->find(setup.fBitmap2, Mipmapped::kNo);
377 test.reset();
378
379 resourceCache->forcePurgeAsNeeded();
380
381 REPORTER_ASSERT(r, proxyCache->numCached() == 0);
382}
383
384// Verify that the ProxyCache's purgeProxiesNotUsedSince behavior is working when triggered from
385// ResourceCache.
387 r,
388 context,
389 testContext,
390 true,
392 std::unique_ptr<Recorder> recorder = context->makeRecorder();
393 ResourceCache* resourceCache = recorder->priv().resourceCache();
394 ProxyCache* proxyCache = recorder->priv().proxyCache();
395
396 ProxyCacheSetup setup = setup_test(context, testContext, recorder.get(), r);
397 REPORTER_ASSERT(r, setup.valid());
398 if (!setup.valid()) {
399 return;
400 }
401
402 REPORTER_ASSERT(r, setup.fProxy1->isInstantiated());
403 REPORTER_ASSERT(r, setup.fProxy2->isInstantiated());
404
405 if (!setup.fProxy1->texture() || !setup.fProxy2->texture()) {
406 return;
407 }
408
409 // Clear out resources used to setup bitmap proxies so we can track things easier.
410 resourceCache->setMaxBudget(0);
411 resourceCache->setMaxBudget(256 * (1 << 20));
412
413 REPORTER_ASSERT(r, proxyCache->numCached() == 2);
414 int baselineResourceCount = resourceCache->getResourceCount();
415 // When buffer maps are async it can take extra time for buffers to be returned to the cache.
416 if (context->priv().caps()->bufferMapsAreAsync()) {
417 // We expect at least 2 textures (and possibly buffers).
418 REPORTER_ASSERT(r, baselineResourceCount >= 2);
419 } else {
420 REPORTER_ASSERT(r, baselineResourceCount == 2);
421 }
422 // Force a command buffer ref on the second proxy in the cache so it can't be purged immediately
423 setup.fProxy2->texture()->refCommandBuffer();
424
425 Resource* proxy2ResourcePtr = setup.fProxy2->texture();
426
427 setup.fProxy1.reset();
428 setup.fProxy2.reset();
429 REPORTER_ASSERT(r, proxyCache->numCached() == 2);
430
431 auto timeAfterProxyCreation = skgpu::StdSteadyClock::now();
432
433 // This should trigger both proxies to be purged from the ProxyCache. The first proxy should
434 // immediately be purged from the ResourceCache as well since it has not other refs. The second
435 // proxy will not be purged from the ResourceCache since it still has a command buffer ref.
436 // However, that resource should have its deleteASAP flag set.
437 resourceCache->purgeResourcesNotUsedSince(timeAfterProxyCreation);
438
439 REPORTER_ASSERT(r, proxyCache->numCached() == 0);
440 REPORTER_ASSERT(r, resourceCache->getResourceCount() == baselineResourceCount - 1);
441 REPORTER_ASSERT(r, resourceCache->topOfPurgeableQueue() == nullptr);
442 REPORTER_ASSERT(r, proxy2ResourcePtr->testingShouldDeleteASAP());
443
444 // Removing the command buffer ref and returning proxy2Resource to the cache should cause it to
445 // immediately get deleted without going in the purgeable queue.
446 proxy2ResourcePtr->unrefCommandBuffer();
447 resourceCache->forceProcessReturnedResources();
448
449 REPORTER_ASSERT(r, proxyCache->numCached() == 0);
450 REPORTER_ASSERT(r, resourceCache->getResourceCount() == baselineResourceCount - 2);
451 REPORTER_ASSERT(r, resourceCache->topOfPurgeableQueue() == nullptr);
452}
453
454} // namespace skgpu::graphite
sk_sp< TextureProxy > fProxy2
SkBitmap fBitmap1
SkBitmap fBitmap2
skgpu::StdSteadyClock::time_point fTimeAfterAllProxyCreation
skgpu::StdSteadyClock::time_point fTimeBetweenProxyCreation
sk_sp< TextureProxy > fProxy1
constexpr SkColor SK_ColorBLACK
Definition SkColor.h:103
#define DEF_GRAPHITE_TEST_FOR_ALL_CONTEXTS(name, reporter, graphite_ctx, ctsEnforcement)
Definition Test.h:373
#define DEF_CONDITIONAL_GRAPHITE_TEST_FOR_ALL_CONTEXTS(name, reporter, graphite_ctx, test_ctx, cond, ctsEnforcement)
Definition Test.h:352
#define REPORTER_ASSERT(r, cond,...)
Definition Test.h:286
bool empty() const
Definition SkBitmap.h:210
sk_sp< TextureProxy > findOrCreateCachedProxy(Recorder *, const SkBitmap &, Mipmapped)
std::unique_ptr< Recording > snap()
Definition Recorder.cpp:149
void purgeResourcesNotUsedSince(StdSteadyClock::time_point purgeTime)
void unrefCommandBuffer() const
Definition Resource.h:79
void syncedSubmit(skgpu::graphite::Context *)
bool GetResourceAsBitmap(const char *resource, SkBitmap *dst)
Definition DecodeUtils.h:21
Definition setup.py:1