1 // Copyright 2020 The Dawn Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <gtest/gtest.h>
16
17 #include "dawn_native/SubresourceStorage.h"
18
19 #include "common/Log.h"
20
21 using namespace dawn_native;
22
23 // A fake class that replicates the behavior of SubresourceStorage but without any compression and
24 // is used to compare the results of operations on SubresourceStorage against the "ground truth" of
25 // FakeStorage.
26 template <typename T>
27 struct FakeStorage {
FakeStorageFakeStorage28 FakeStorage(Aspect aspects,
29 uint32_t arrayLayerCount,
30 uint32_t mipLevelCount,
31 T initialValue = {})
32 : mAspects(aspects),
33 mArrayLayerCount(arrayLayerCount),
34 mMipLevelCount(mipLevelCount),
35 mData(GetAspectCount(aspects) * arrayLayerCount * mipLevelCount, initialValue) {
36 }
37
38 template <typename F>
UpdateFakeStorage39 void Update(const SubresourceRange& range, F&& updateFunc) {
40 for (Aspect aspect : IterateEnumMask(range.aspects)) {
41 for (uint32_t layer = range.baseArrayLayer;
42 layer < range.baseArrayLayer + range.layerCount; layer++) {
43 for (uint32_t level = range.baseMipLevel;
44 level < range.baseMipLevel + range.levelCount; level++) {
45 SubresourceRange range = SubresourceRange::MakeSingle(aspect, layer, level);
46 updateFunc(range, &mData[GetDataIndex(aspect, layer, level)]);
47 }
48 }
49 }
50 }
51
52 template <typename U, typename F>
MergeFakeStorage53 void Merge(const SubresourceStorage<U>& other, F&& mergeFunc) {
54 for (Aspect aspect : IterateEnumMask(mAspects)) {
55 for (uint32_t layer = 0; layer < mArrayLayerCount; layer++) {
56 for (uint32_t level = 0; level < mMipLevelCount; level++) {
57 SubresourceRange range = SubresourceRange::MakeSingle(aspect, layer, level);
58 mergeFunc(range, &mData[GetDataIndex(aspect, layer, level)],
59 other.Get(aspect, layer, level));
60 }
61 }
62 }
63 }
64
GetFakeStorage65 const T& Get(Aspect aspect, uint32_t arrayLayer, uint32_t mipLevel) const {
66 return mData[GetDataIndex(aspect, arrayLayer, mipLevel)];
67 }
68
GetDataIndexFakeStorage69 size_t GetDataIndex(Aspect aspect, uint32_t layer, uint32_t level) const {
70 uint32_t aspectIndex = GetAspectIndex(aspect);
71 return level + mMipLevelCount * (layer + mArrayLayerCount * aspectIndex);
72 }
73
74 // Method that checks that this and real have exactly the same content. It does so via looping
75 // on all subresources and calling Get() (hence testing Get()). It also calls Iterate()
76 // checking that every subresource is mentioned exactly once and that its content is correct
77 // (hence testing Iterate()).
78 // Its implementation requires the RangeTracker below that itself needs FakeStorage<int> so it
79 // cannot be define inline with the other methods.
80 void CheckSameAs(const SubresourceStorage<T>& real);
81
82 Aspect mAspects;
83 uint32_t mArrayLayerCount;
84 uint32_t mMipLevelCount;
85
86 std::vector<T> mData;
87 };
88
89 // Track a set of ranges that have been seen and can assert that in aggregate they make exactly
90 // a single range (and that each subresource was seen only once).
91 struct RangeTracker {
92 template <typename T>
RangeTrackerRangeTracker93 RangeTracker(const SubresourceStorage<T>& s)
94 : mTracked(s.GetAspectsForTesting(),
95 s.GetArrayLayerCountForTesting(),
96 s.GetMipLevelCountForTesting(),
97 0) {
98 }
99
TrackRangeTracker100 void Track(const SubresourceRange& range) {
101 // Add +1 to the subresources tracked.
102 mTracked.Update(range, [](const SubresourceRange&, uint32_t* counter) {
103 ASSERT_EQ(*counter, 0u);
104 *counter += 1;
105 });
106 }
107
CheckTrackedExactlyRangeTracker108 void CheckTrackedExactly(const SubresourceRange& range) {
109 // Check that all subresources in the range were tracked once and set the counter back to 0.
110 mTracked.Update(range, [](const SubresourceRange&, uint32_t* counter) {
111 ASSERT_EQ(*counter, 1u);
112 *counter = 0;
113 });
114
115 // Now all subresources should be at 0.
116 for (int counter : mTracked.mData) {
117 ASSERT_EQ(counter, 0);
118 }
119 }
120
121 FakeStorage<uint32_t> mTracked;
122 };
123
124 template <typename T>
CheckSameAs(const SubresourceStorage<T> & real)125 void FakeStorage<T>::CheckSameAs(const SubresourceStorage<T>& real) {
126 EXPECT_EQ(real.GetAspectsForTesting(), mAspects);
127 EXPECT_EQ(real.GetArrayLayerCountForTesting(), mArrayLayerCount);
128 EXPECT_EQ(real.GetMipLevelCountForTesting(), mMipLevelCount);
129
130 RangeTracker tracker(real);
131 real.Iterate([&](const SubresourceRange& range, const T& data) {
132 // Check that the range is sensical.
133 EXPECT_TRUE(IsSubset(range.aspects, mAspects));
134
135 EXPECT_LT(range.baseArrayLayer, mArrayLayerCount);
136 EXPECT_LE(range.baseArrayLayer + range.layerCount, mArrayLayerCount);
137
138 EXPECT_LT(range.baseMipLevel, mMipLevelCount);
139 EXPECT_LE(range.baseMipLevel + range.levelCount, mMipLevelCount);
140
141 for (Aspect aspect : IterateEnumMask(range.aspects)) {
142 for (uint32_t layer = range.baseArrayLayer;
143 layer < range.baseArrayLayer + range.layerCount; layer++) {
144 for (uint32_t level = range.baseMipLevel;
145 level < range.baseMipLevel + range.levelCount; level++) {
146 EXPECT_EQ(data, Get(aspect, layer, level));
147 EXPECT_EQ(data, real.Get(aspect, layer, level));
148 }
149 }
150 }
151
152 tracker.Track(range);
153 });
154
155 tracker.CheckTrackedExactly(
156 SubresourceRange::MakeFull(mAspects, mArrayLayerCount, mMipLevelCount));
157 }
158
159 template <typename T>
CheckAspectCompressed(const SubresourceStorage<T> & s,Aspect aspect,bool expected)160 void CheckAspectCompressed(const SubresourceStorage<T>& s, Aspect aspect, bool expected) {
161 ASSERT(HasOneBit(aspect));
162
163 uint32_t levelCount = s.GetMipLevelCountForTesting();
164 uint32_t layerCount = s.GetArrayLayerCountForTesting();
165
166 bool seen = false;
167 s.Iterate([&](const SubresourceRange& range, const T&) {
168 if (range.aspects == aspect && range.layerCount == layerCount &&
169 range.levelCount == levelCount && range.baseArrayLayer == 0 &&
170 range.baseMipLevel == 0) {
171 seen = true;
172 }
173 });
174
175 ASSERT_EQ(seen, expected);
176
177 // Check that the internal state of SubresourceStorage matches what we expect.
178 // If an aspect is compressed, all its layers should be internally tagged as compressed.
179 ASSERT_EQ(s.IsAspectCompressedForTesting(aspect), expected);
180 if (expected) {
181 for (uint32_t layer = 0; layer < s.GetArrayLayerCountForTesting(); layer++) {
182 ASSERT_TRUE(s.IsLayerCompressedForTesting(aspect, layer));
183 }
184 }
185 }
186
187 template <typename T>
CheckLayerCompressed(const SubresourceStorage<T> & s,Aspect aspect,uint32_t layer,bool expected)188 void CheckLayerCompressed(const SubresourceStorage<T>& s,
189 Aspect aspect,
190 uint32_t layer,
191 bool expected) {
192 ASSERT(HasOneBit(aspect));
193
194 uint32_t levelCount = s.GetMipLevelCountForTesting();
195
196 bool seen = false;
197 s.Iterate([&](const SubresourceRange& range, const T&) {
198 if (range.aspects == aspect && range.layerCount == 1 && range.levelCount == levelCount &&
199 range.baseArrayLayer == layer && range.baseMipLevel == 0) {
200 seen = true;
201 }
202 });
203
204 ASSERT_EQ(seen, expected);
205 ASSERT_EQ(s.IsLayerCompressedForTesting(aspect, layer), expected);
206 }
207
208 struct SmallData {
209 uint32_t value = 0xF00;
210 };
211
operator ==(const SmallData & a,const SmallData & b)212 bool operator==(const SmallData& a, const SmallData& b) {
213 return a.value == b.value;
214 }
215
216 // Test that the default value is correctly set.
TEST(SubresourceStorageTest,DefaultValue)217 TEST(SubresourceStorageTest, DefaultValue) {
218 // Test setting no default value for a primitive type.
219 {
220 SubresourceStorage<int> s(Aspect::Color, 3, 5);
221 EXPECT_EQ(s.Get(Aspect::Color, 1, 2), 0);
222
223 FakeStorage<int> f(Aspect::Color, 3, 5);
224 f.CheckSameAs(s);
225 }
226
227 // Test setting a default value for a primitive type.
228 {
229 SubresourceStorage<int> s(Aspect::Color, 3, 5, 42);
230 EXPECT_EQ(s.Get(Aspect::Color, 1, 2), 42);
231
232 FakeStorage<int> f(Aspect::Color, 3, 5, 42);
233 f.CheckSameAs(s);
234 }
235
236 // Test setting no default value for a type with a default constructor.
237 {
238 SubresourceStorage<SmallData> s(Aspect::Color, 3, 5);
239 EXPECT_EQ(s.Get(Aspect::Color, 1, 2).value, 0xF00u);
240
241 FakeStorage<SmallData> f(Aspect::Color, 3, 5);
242 f.CheckSameAs(s);
243 }
244 // Test setting a default value for a type with a default constructor.
245 {
246 SubresourceStorage<SmallData> s(Aspect::Color, 3, 5, {007u});
247 EXPECT_EQ(s.Get(Aspect::Color, 1, 2).value, 007u);
248
249 FakeStorage<SmallData> f(Aspect::Color, 3, 5, {007u});
250 f.CheckSameAs(s);
251 }
252 }
253
254 // The tests for Update() all follow the same pattern of setting up a real and a fake storage then
255 // performing one or multiple Update()s on them and checking:
256 // - They have the same content.
257 // - The Update() range was correct.
258 // - The aspects and layers have the expected "compressed" status.
259
260 // Calls Update both on the read storage and the fake storage but intercepts the call to updateFunc
261 // done by the real storage to check their ranges argument aggregate to exactly the update range.
262 template <typename T, typename F>
CallUpdateOnBoth(SubresourceStorage<T> * s,FakeStorage<T> * f,const SubresourceRange & range,F && updateFunc)263 void CallUpdateOnBoth(SubresourceStorage<T>* s,
264 FakeStorage<T>* f,
265 const SubresourceRange& range,
266 F&& updateFunc) {
267 RangeTracker tracker(*s);
268
269 s->Update(range, [&](const SubresourceRange& range, T* data) {
270 tracker.Track(range);
271 updateFunc(range, data);
272 });
273 f->Update(range, updateFunc);
274
275 tracker.CheckTrackedExactly(range);
276 f->CheckSameAs(*s);
277 }
278
279 // Test updating a single subresource on a single-aspect storage.
TEST(SubresourceStorageTest,SingleSubresourceUpdateSingleAspect)280 TEST(SubresourceStorageTest, SingleSubresourceUpdateSingleAspect) {
281 SubresourceStorage<int> s(Aspect::Color, 5, 7);
282 FakeStorage<int> f(Aspect::Color, 5, 7);
283
284 // Update a single subresource.
285 SubresourceRange range = SubresourceRange::MakeSingle(Aspect::Color, 3, 2);
286 CallUpdateOnBoth(&s, &f, range, [](const SubresourceRange&, int* data) { *data += 1; });
287
288 CheckAspectCompressed(s, Aspect::Color, false);
289 CheckLayerCompressed(s, Aspect::Color, 2, true);
290 CheckLayerCompressed(s, Aspect::Color, 3, false);
291 CheckLayerCompressed(s, Aspect::Color, 4, true);
292 }
293
294 // Test updating a single subresource on a multi-aspect storage.
TEST(SubresourceStorageTest,SingleSubresourceUpdateMultiAspect)295 TEST(SubresourceStorageTest, SingleSubresourceUpdateMultiAspect) {
296 SubresourceStorage<int> s(Aspect::Depth | Aspect::Stencil, 5, 3);
297 FakeStorage<int> f(Aspect::Depth | Aspect::Stencil, 5, 3);
298
299 SubresourceRange range = SubresourceRange::MakeSingle(Aspect::Stencil, 1, 2);
300 CallUpdateOnBoth(&s, &f, range, [](const SubresourceRange&, int* data) { *data += 1; });
301
302 CheckAspectCompressed(s, Aspect::Depth, true);
303 CheckAspectCompressed(s, Aspect::Stencil, false);
304 CheckLayerCompressed(s, Aspect::Stencil, 0, true);
305 CheckLayerCompressed(s, Aspect::Stencil, 1, false);
306 CheckLayerCompressed(s, Aspect::Stencil, 2, true);
307 }
308
309 // Test updating as a stipple pattern on one of two aspects then updating it completely.
TEST(SubresourceStorageTest,UpdateStipple)310 TEST(SubresourceStorageTest, UpdateStipple) {
311 const uint32_t kLayers = 10;
312 const uint32_t kLevels = 7;
313 SubresourceStorage<int> s(Aspect::Depth | Aspect::Stencil, kLayers, kLevels);
314 FakeStorage<int> f(Aspect::Depth | Aspect::Stencil, kLayers, kLevels);
315
316 // Update with a stipple.
317 for (uint32_t layer = 0; layer < kLayers; layer++) {
318 for (uint32_t level = 0; level < kLevels; level++) {
319 if ((layer + level) % 2 == 0) {
320 SubresourceRange range = SubresourceRange::MakeSingle(Aspect::Depth, layer, level);
321 CallUpdateOnBoth(&s, &f, range,
322 [](const SubresourceRange&, int* data) { *data += 17; });
323 }
324 }
325 }
326
327 // The depth should be fully uncompressed while the stencil stayed compressed.
328 CheckAspectCompressed(s, Aspect::Stencil, true);
329 CheckAspectCompressed(s, Aspect::Depth, false);
330 for (uint32_t layer = 0; layer < kLayers; layer++) {
331 CheckLayerCompressed(s, Aspect::Depth, layer, false);
332 }
333
334 // Update completely with a single value. Recompression should happen!
335 {
336 SubresourceRange fullRange =
337 SubresourceRange::MakeFull(Aspect::Depth | Aspect::Stencil, kLayers, kLevels);
338 CallUpdateOnBoth(&s, &f, fullRange, [](const SubresourceRange&, int* data) { *data = 31; });
339 }
340
341 CheckAspectCompressed(s, Aspect::Depth, true);
342 CheckAspectCompressed(s, Aspect::Stencil, true);
343 }
344
345 // Test updating as a crossing band pattern:
346 // - The first band is full layers [2, 3] on both aspects
347 // - The second band is full mips [5, 6] on one aspect.
348 // Then updating completely.
TEST(SubresourceStorageTest,UpdateTwoBand)349 TEST(SubresourceStorageTest, UpdateTwoBand) {
350 const uint32_t kLayers = 5;
351 const uint32_t kLevels = 9;
352 SubresourceStorage<int> s(Aspect::Depth | Aspect::Stencil, kLayers, kLevels);
353 FakeStorage<int> f(Aspect::Depth | Aspect::Stencil, kLayers, kLevels);
354
355 // Update the two bands
356 {
357 SubresourceRange range(Aspect::Depth | Aspect::Stencil, {2, 2}, {0, kLevels});
358 CallUpdateOnBoth(&s, &f, range, [](const SubresourceRange&, int* data) { *data += 3; });
359 }
360
361 // The layers were fully updated so they should stay compressed.
362 CheckLayerCompressed(s, Aspect::Depth, 2, true);
363 CheckLayerCompressed(s, Aspect::Depth, 3, true);
364 CheckLayerCompressed(s, Aspect::Stencil, 2, true);
365 CheckLayerCompressed(s, Aspect::Stencil, 3, true);
366
367 {
368 SubresourceRange range(Aspect::Depth, {0, kLayers}, {5, 2});
369 CallUpdateOnBoth(&s, &f, range, [](const SubresourceRange&, int* data) { *data *= 3; });
370 }
371
372 // The layers had to be decompressed in depth
373 CheckLayerCompressed(s, Aspect::Depth, 2, false);
374 CheckLayerCompressed(s, Aspect::Depth, 3, false);
375 CheckLayerCompressed(s, Aspect::Stencil, 2, true);
376 CheckLayerCompressed(s, Aspect::Stencil, 3, true);
377
378 // Update completely. Without a single value recompression shouldn't happen.
379 {
380 SubresourceRange fullRange =
381 SubresourceRange::MakeFull(Aspect::Depth | Aspect::Stencil, kLayers, kLevels);
382 CallUpdateOnBoth(&s, &f, fullRange,
383 [](const SubresourceRange&, int* data) { *data += 12; });
384 }
385
386 CheckAspectCompressed(s, Aspect::Depth, false);
387 CheckAspectCompressed(s, Aspect::Stencil, false);
388 }
389
390 // Test updating with extremal subresources
391 // - Then half of the array layers in full.
392 // - Then updating completely.
TEST(SubresourceStorageTest,UpdateExtremas)393 TEST(SubresourceStorageTest, UpdateExtremas) {
394 const uint32_t kLayers = 6;
395 const uint32_t kLevels = 4;
396 SubresourceStorage<int> s(Aspect::Color, kLayers, kLevels);
397 FakeStorage<int> f(Aspect::Color, kLayers, kLevels);
398
399 // Update the two extrema
400 {
401 SubresourceRange range = SubresourceRange::MakeSingle(Aspect::Color, 0, kLevels - 1);
402 CallUpdateOnBoth(&s, &f, range, [](const SubresourceRange&, int* data) { *data += 3; });
403 }
404 {
405 SubresourceRange range = SubresourceRange::MakeSingle(Aspect::Color, kLayers - 1, 0);
406 CallUpdateOnBoth(&s, &f, range, [](const SubresourceRange&, int* data) { *data *= 3; });
407 }
408
409 CheckLayerCompressed(s, Aspect::Color, 0, false);
410 CheckLayerCompressed(s, Aspect::Color, 1, true);
411 CheckLayerCompressed(s, Aspect::Color, kLayers - 2, true);
412 CheckLayerCompressed(s, Aspect::Color, kLayers - 1, false);
413
414 // Update half of the layers in full with constant values. Some recompression should happen.
415 {
416 SubresourceRange range(Aspect::Color, {0, kLayers / 2}, {0, kLevels});
417 CallUpdateOnBoth(&s, &f, range, [](const SubresourceRange&, int* data) { *data = 123; });
418 }
419
420 CheckLayerCompressed(s, Aspect::Color, 0, true);
421 CheckLayerCompressed(s, Aspect::Color, 1, true);
422 CheckLayerCompressed(s, Aspect::Color, kLayers - 1, false);
423
424 // Update completely. Recompression should happen!
425 {
426 SubresourceRange fullRange = SubresourceRange::MakeFull(Aspect::Color, kLayers, kLevels);
427 CallUpdateOnBoth(&s, &f, fullRange, [](const SubresourceRange&, int* data) { *data = 35; });
428 }
429
430 CheckAspectCompressed(s, Aspect::Color, true);
431 }
432
433 // A regression test for an issue found while reworking the implementation where RecompressAspect
434 // didn't correctly check that each each layer was compressed but only that their 0th value was
435 // the same.
TEST(SubresourceStorageTest,UpdateLevel0sHappenToMatch)436 TEST(SubresourceStorageTest, UpdateLevel0sHappenToMatch) {
437 SubresourceStorage<int> s(Aspect::Color, 2, 2);
438 FakeStorage<int> f(Aspect::Color, 2, 2);
439
440 // Update 0th mip levels to some value, it should decompress the aspect and both layers.
441 {
442 SubresourceRange range(Aspect::Color, {0, 2}, {0, 1});
443 CallUpdateOnBoth(&s, &f, range, [](const SubresourceRange&, int* data) { *data = 17; });
444 }
445
446 CheckAspectCompressed(s, Aspect::Color, false);
447 CheckLayerCompressed(s, Aspect::Color, 0, false);
448 CheckLayerCompressed(s, Aspect::Color, 1, false);
449
450 // Update the whole resource by doing +1. The aspects and layers should stay decompressed.
451 {
452 SubresourceRange range = SubresourceRange::MakeFull(Aspect::Color, 2, 2);
453 CallUpdateOnBoth(&s, &f, range, [](const SubresourceRange&, int* data) { *data += 1; });
454 }
455
456 CheckAspectCompressed(s, Aspect::Color, false);
457 CheckLayerCompressed(s, Aspect::Color, 0, false);
458 CheckLayerCompressed(s, Aspect::Color, 1, false);
459 }
460
461 // The tests for Merge() all follow the same as the Update() tests except that they use Update()
462 // to set up the test storages.
463
464 // Similar to CallUpdateOnBoth but for Merge
465 template <typename T, typename U, typename F>
CallMergeOnBoth(SubresourceStorage<T> * s,FakeStorage<T> * f,const SubresourceStorage<U> & other,F && mergeFunc)466 void CallMergeOnBoth(SubresourceStorage<T>* s,
467 FakeStorage<T>* f,
468 const SubresourceStorage<U>& other,
469 F&& mergeFunc) {
470 RangeTracker tracker(*s);
471
472 s->Merge(other, [&](const SubresourceRange& range, T* data, const U& otherData) {
473 tracker.Track(range);
474 mergeFunc(range, data, otherData);
475 });
476 f->Merge(other, mergeFunc);
477
478 tracker.CheckTrackedExactly(
479 SubresourceRange::MakeFull(f->mAspects, f->mArrayLayerCount, f->mMipLevelCount));
480 f->CheckSameAs(*s);
481 }
482
483 // Test merging two fully compressed single-aspect resources.
TEST(SubresourceStorageTest,MergeFullWithFullSingleAspect)484 TEST(SubresourceStorageTest, MergeFullWithFullSingleAspect) {
485 SubresourceStorage<int> s(Aspect::Color, 4, 6);
486 FakeStorage<int> f(Aspect::Color, 4, 6);
487
488 // Merge the whole resource in a single call.
489 SubresourceStorage<bool> other(Aspect::Color, 4, 6, true);
490 CallMergeOnBoth(&s, &f, other, [](const SubresourceRange&, int* data, bool other) {
491 if (other) {
492 *data = 13;
493 }
494 });
495
496 CheckAspectCompressed(s, Aspect::Color, true);
497 }
498
499 // Test merging two fully compressed multi-aspect resources.
TEST(SubresourceStorageTest,MergeFullWithFullMultiAspect)500 TEST(SubresourceStorageTest, MergeFullWithFullMultiAspect) {
501 SubresourceStorage<int> s(Aspect::Depth | Aspect::Stencil, 6, 7);
502 FakeStorage<int> f(Aspect::Depth | Aspect::Stencil, 6, 7);
503
504 // Merge the whole resource in a single call.
505 SubresourceStorage<bool> other(Aspect::Depth | Aspect::Stencil, 6, 7, true);
506 CallMergeOnBoth(&s, &f, other, [](const SubresourceRange&, int* data, bool other) {
507 if (other) {
508 *data = 13;
509 }
510 });
511
512 CheckAspectCompressed(s, Aspect::Depth, true);
513 CheckAspectCompressed(s, Aspect::Stencil, true);
514 }
515
516 // Test merging a fully compressed resource in a resource with the "cross band" pattern.
517 // - The first band is full layers [2, 3] on both aspects
518 // - The second band is full mips [5, 6] on one aspect.
519 // This provides coverage of using a single piece of data from `other` to update all of `s`
TEST(SubresourceStorageTest,MergeFullInTwoBand)520 TEST(SubresourceStorageTest, MergeFullInTwoBand) {
521 const uint32_t kLayers = 5;
522 const uint32_t kLevels = 9;
523 SubresourceStorage<int> s(Aspect::Depth | Aspect::Stencil, kLayers, kLevels);
524 FakeStorage<int> f(Aspect::Depth | Aspect::Stencil, kLayers, kLevels);
525
526 // Update the two bands
527 {
528 SubresourceRange range(Aspect::Depth | Aspect::Stencil, {2, 2}, {0, kLevels});
529 CallUpdateOnBoth(&s, &f, range, [](const SubresourceRange&, int* data) { *data += 3; });
530 }
531 {
532 SubresourceRange range(Aspect::Depth, {0, kLayers}, {5, 2});
533 CallUpdateOnBoth(&s, &f, range, [](const SubresourceRange&, int* data) { *data += 5; });
534 }
535
536 // Merge the fully compressed resource.
537 SubresourceStorage<int> other(Aspect::Depth | Aspect::Stencil, kLayers, kLevels, 17);
538 CallMergeOnBoth(&s, &f, other,
539 [](const SubresourceRange&, int* data, int other) { *data += other; });
540
541 // The layers traversed by the mip band are still uncompressed.
542 CheckLayerCompressed(s, Aspect::Depth, 1, false);
543 CheckLayerCompressed(s, Aspect::Depth, 2, false);
544 CheckLayerCompressed(s, Aspect::Depth, 3, false);
545 CheckLayerCompressed(s, Aspect::Depth, 4, false);
546
547 // Stencil is decompressed but all its layers are still compressed because there wasn't the mip
548 // band.
549 CheckAspectCompressed(s, Aspect::Stencil, false);
550 CheckLayerCompressed(s, Aspect::Stencil, 1, true);
551 CheckLayerCompressed(s, Aspect::Stencil, 2, true);
552 CheckLayerCompressed(s, Aspect::Stencil, 3, true);
553 CheckLayerCompressed(s, Aspect::Stencil, 4, true);
554 }
555 // Test the reverse, mergign two-bands in a full resource. This provides coverage for decompressing
556 // aspects / and partilly layers to match the compression of `other`
TEST(SubresourceStorageTest,MergeTwoBandInFull)557 TEST(SubresourceStorageTest, MergeTwoBandInFull) {
558 const uint32_t kLayers = 5;
559 const uint32_t kLevels = 9;
560 SubresourceStorage<int> s(Aspect::Depth | Aspect::Stencil, kLayers, kLevels, 75);
561 FakeStorage<int> f(Aspect::Depth | Aspect::Stencil, kLayers, kLevels, 75);
562
563 // Update the two bands
564 SubresourceStorage<int> other(Aspect::Depth | Aspect::Stencil, kLayers, kLevels);
565 {
566 SubresourceRange range(Aspect::Depth | Aspect::Stencil, {2, 2}, {0, kLevels});
567 other.Update(range, [](const SubresourceRange&, int* data) { *data += 3; });
568 }
569 {
570 SubresourceRange range(Aspect::Depth, {0, kLayers}, {5, 2});
571 other.Update(range, [](const SubresourceRange&, int* data) { *data += 5; });
572 }
573
574 // Merge the fully compressed resource.
575 CallMergeOnBoth(&s, &f, other,
576 [](const SubresourceRange&, int* data, int other) { *data += other; });
577
578 // The layers traversed by the mip band are still uncompressed.
579 CheckLayerCompressed(s, Aspect::Depth, 1, false);
580 CheckLayerCompressed(s, Aspect::Depth, 2, false);
581 CheckLayerCompressed(s, Aspect::Depth, 3, false);
582 CheckLayerCompressed(s, Aspect::Depth, 4, false);
583
584 // Stencil is decompressed but all its layers are still compressed because there wasn't the mip
585 // band.
586 CheckAspectCompressed(s, Aspect::Stencil, false);
587 CheckLayerCompressed(s, Aspect::Stencil, 1, true);
588 CheckLayerCompressed(s, Aspect::Stencil, 2, true);
589 CheckLayerCompressed(s, Aspect::Stencil, 3, true);
590 CheckLayerCompressed(s, Aspect::Stencil, 4, true);
591 }
592
593 // Test merging storage with a layer band in a stipple patterned storage. This provide coverage
594 // for the code path that uses the same layer data for other multiple times.
TEST(SubresourceStorageTest,MergeLayerBandInStipple)595 TEST(SubresourceStorageTest, MergeLayerBandInStipple) {
596 const uint32_t kLayers = 3;
597 const uint32_t kLevels = 5;
598
599 SubresourceStorage<int> s(Aspect::Color, kLayers, kLevels);
600 FakeStorage<int> f(Aspect::Color, kLayers, kLevels);
601 SubresourceStorage<int> other(Aspect::Color, kLayers, kLevels);
602
603 for (uint32_t layer = 0; layer < kLayers; layer++) {
604 for (uint32_t level = 0; level < kLevels; level++) {
605 if ((layer + level) % 2 == 0) {
606 SubresourceRange range = SubresourceRange::MakeSingle(Aspect::Color, layer, level);
607 CallUpdateOnBoth(&s, &f, range,
608 [](const SubresourceRange&, int* data) { *data += 17; });
609 }
610 }
611 if (layer % 2 == 0) {
612 other.Update({Aspect::Color, {layer, 1}, {0, kLevels}},
613 [](const SubresourceRange&, int* data) { *data += 8; });
614 }
615 }
616
617 // Merge the band in the stipple.
618 CallMergeOnBoth(&s, &f, other,
619 [](const SubresourceRange&, int* data, int other) { *data += other; });
620
621 // None of the resulting layers are compressed.
622 CheckLayerCompressed(s, Aspect::Color, 0, false);
623 CheckLayerCompressed(s, Aspect::Color, 1, false);
624 CheckLayerCompressed(s, Aspect::Color, 2, false);
625 }
626
627 // Regression test for a missing check that layer 0 is compressed when recompressing.
TEST(SubresourceStorageTest,Layer0NotCompressedBlocksAspectRecompression)628 TEST(SubresourceStorageTest, Layer0NotCompressedBlocksAspectRecompression) {
629 const uint32_t kLayers = 2;
630 const uint32_t kLevels = 2;
631 SubresourceStorage<int> s(Aspect::Color, kLayers, kLevels);
632 FakeStorage<int> f(Aspect::Color, kLayers, kLevels);
633
634 // Set up s with zeros except (0, 1) which is garbage.
635 {
636 SubresourceRange range = SubresourceRange::MakeSingle(Aspect::Color, 0, 1);
637 CallUpdateOnBoth(&s, &f, range, [](const SubresourceRange&, int* data) { *data += 0xABC; });
638 }
639
640 // Other is 2x2 of zeroes
641 SubresourceStorage<int> other(Aspect::Color, kLayers, kLevels);
642
643 // Fake updating F with other which is fully compressed and will trigger recompression.
644 CallMergeOnBoth(&s, &f, other, [](const SubresourceRange&, int*, int) {});
645
646 // The Color aspect should not have been recompressed.
647 CheckAspectCompressed(s, Aspect::Color, false);
648 CheckLayerCompressed(s, Aspect::Color, 0, false);
649 }
650
651 // Regression test for aspect decompression not copying to layer 0
TEST(SubresourceStorageTest,AspectDecompressionUpdatesLayer0)652 TEST(SubresourceStorageTest, AspectDecompressionUpdatesLayer0) {
653 const uint32_t kLayers = 2;
654 const uint32_t kLevels = 2;
655 SubresourceStorage<int> s(Aspect::Color, kLayers, kLevels, 3);
656 FakeStorage<int> f(Aspect::Color, kLayers, kLevels, 3);
657
658 // Cause decompression by writing to a single subresource.
659 {
660 SubresourceRange range = SubresourceRange::MakeSingle(Aspect::Color, 1, 1);
661 CallUpdateOnBoth(&s, &f, range, [](const SubresourceRange&, int* data) { *data += 0xABC; });
662 }
663
664 // Check that the aspect's value of 3 was correctly decompressed in layer 0.
665 CheckLayerCompressed(s, Aspect::Color, 0, true);
666 EXPECT_EQ(3, s.Get(Aspect::Color, 0, 0));
667 EXPECT_EQ(3, s.Get(Aspect::Color, 0, 1));
668 }
669
670 // Bugs found while testing:
671 // - mLayersCompressed not initialized to true.
672 // - DecompressLayer setting Compressed to true instead of false.
673 // - Get() checking for !compressed instead of compressed for the early exit.
674 // - ASSERT in RecompressLayers was inverted.
675 // - Two != being converted to == during a rework.
676 // - (with ASSERT) that RecompressAspect didn't check that aspect 0 was compressed.
677 // - Missing decompression of layer 0 after introducing mInlineAspectData.
678