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