• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 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 "tests/DawnTest.h"
16 
17 #include "common/Assert.h"
18 #include "common/Math.h"
19 #include "utils/ComboRenderPipelineDescriptor.h"
20 #include "utils/TextureUtils.h"
21 #include "utils/WGPUHelpers.h"
22 
23 #include <cmath>
24 #include <type_traits>
25 
26 // An expectation for float buffer content that can correctly compare different NaN values and
27 // supports a basic tolerance for comparison of finite values.
28 class ExpectFloatWithTolerance : public detail::Expectation {
29   public:
ExpectFloatWithTolerance(std::vector<float> expected,float tolerance)30     ExpectFloatWithTolerance(std::vector<float> expected, float tolerance)
31         : mExpected(std::move(expected)), mTolerance(tolerance) {
32     }
33 
Check(const void * data,size_t size)34     testing::AssertionResult Check(const void* data, size_t size) override {
35         ASSERT(size == sizeof(float) * mExpected.size());
36 
37         const float* actual = static_cast<const float*>(data);
38 
39         for (size_t i = 0; i < mExpected.size(); ++i) {
40             float expectedValue = mExpected[i];
41             float actualValue = actual[i];
42 
43             if (!FloatsMatch(expectedValue, actualValue)) {
44                 testing::AssertionResult result = testing::AssertionFailure()
45                                                   << "Expected data[" << i << "] to be close to "
46                                                   << expectedValue << ", actual " << actualValue
47                                                   << std::endl;
48                 return result;
49             }
50         }
51         return testing::AssertionSuccess();
52     }
53 
54   private:
FloatsMatch(float expected,float actual)55     bool FloatsMatch(float expected, float actual) {
56         if (std::isnan(expected)) {
57             return std::isnan(actual);
58         }
59 
60         if (std::isinf(expected)) {
61             return std::isinf(actual) && std::signbit(expected) == std::signbit(actual);
62         }
63 
64         if (mTolerance == 0.0f) {
65             return expected == actual;
66         }
67 
68         float error = std::abs(expected - actual);
69         return error < mTolerance;
70     }
71 
72     std::vector<float> mExpected;
73     float mTolerance;
74 };
75 
76 // An expectation for float16 buffers that can correctly compare NaNs (all NaNs are equivalent).
77 class ExpectFloat16 : public detail::Expectation {
78   public:
ExpectFloat16(std::vector<uint16_t> expected)79     ExpectFloat16(std::vector<uint16_t> expected) : mExpected(std::move(expected)) {
80     }
81 
Check(const void * data,size_t size)82     testing::AssertionResult Check(const void* data, size_t size) override {
83         ASSERT(size == sizeof(uint16_t) * mExpected.size());
84 
85         const uint16_t* actual = static_cast<const uint16_t*>(data);
86 
87         for (size_t i = 0; i < mExpected.size(); ++i) {
88             uint16_t expectedValue = mExpected[i];
89             uint16_t actualValue = actual[i];
90 
91             if (!Floats16Match(expectedValue, actualValue)) {
92                 testing::AssertionResult result = testing::AssertionFailure()
93                                                   << "Expected data[" << i << "] to be "
94                                                   << expectedValue << ", actual " << actualValue
95                                                   << std::endl;
96                 return result;
97             }
98         }
99         return testing::AssertionSuccess();
100     }
101 
102   private:
Floats16Match(float expected,float actual)103     bool Floats16Match(float expected, float actual) {
104         if (IsFloat16NaN(expected)) {
105             return IsFloat16NaN(actual);
106         }
107 
108         return expected == actual;
109     }
110 
111     std::vector<uint16_t> mExpected;
112 };
113 
114 class TextureFormatTest : public DawnTest {
115   protected:
116     // Structure containing all the information that tests need to know about the format.
117     struct FormatTestInfo {
118         wgpu::TextureFormat format;
119         uint32_t texelByteSize;
120         wgpu::TextureComponentType type;
121         uint32_t componentCount;
122     };
123 
124     // Returns a reprensentation of a format that can be used to contain the "uncompressed" values
125     // of the format. That the equivalent format with all channels 32bit-sized.
GetUncompressedFormatInfo(FormatTestInfo formatInfo)126     FormatTestInfo GetUncompressedFormatInfo(FormatTestInfo formatInfo) {
127         switch (formatInfo.type) {
128             case wgpu::TextureComponentType::Float:
129                 return {wgpu::TextureFormat::RGBA32Float, 16, formatInfo.type, 4};
130             case wgpu::TextureComponentType::Sint:
131                 return {wgpu::TextureFormat::RGBA32Sint, 16, formatInfo.type, 4};
132             case wgpu::TextureComponentType::Uint:
133                 return {wgpu::TextureFormat::RGBA32Uint, 16, formatInfo.type, 4};
134             default:
135                 UNREACHABLE();
136         }
137     }
138 
139     // Return a pipeline that can be used in a full-texture draw to sample from the texture in the
140     // bindgroup and output its decompressed values to the render target.
CreateSamplePipeline(FormatTestInfo sampleFormatInfo,FormatTestInfo renderFormatInfo)141     wgpu::RenderPipeline CreateSamplePipeline(FormatTestInfo sampleFormatInfo,
142                                               FormatTestInfo renderFormatInfo) {
143         utils::ComboRenderPipelineDescriptor desc;
144 
145         wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
146             [[stage(vertex)]]
147             fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
148                 var pos = array<vec2<f32>, 3>(
149                     vec2<f32>(-3.0, -1.0),
150                     vec2<f32>( 3.0, -1.0),
151                     vec2<f32>( 0.0,  2.0));
152 
153                 return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
154             })");
155 
156         // Compute the WGSL type of the texture's data.
157         const char* type = utils::GetWGSLColorTextureComponentType(sampleFormatInfo.format);
158 
159         std::ostringstream fsSource;
160         fsSource << "[[group(0), binding(0)]] var myTexture : texture_2d<" << type << ">;\n";
161         fsSource << "struct FragmentOut {\n";
162         fsSource << "   [[location(0)]] color : vec4<" << type << ">;\n";
163         fsSource << R"(};
164             [[stage(fragment)]]
165             fn main([[builtin(position)]] FragCoord : vec4<f32>) -> FragmentOut {
166                 var output : FragmentOut;
167                 output.color = textureLoad(myTexture, vec2<i32>(FragCoord.xy), 0);
168                 return output;
169             })";
170 
171         wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, fsSource.str().c_str());
172 
173         desc.vertex.module = vsModule;
174         desc.cFragment.module = fsModule;
175         desc.cTargets[0].format = renderFormatInfo.format;
176 
177         return device.CreateRenderPipeline(&desc);
178     }
179 
180     // The sampling test uploads the sample data in a texture with the sampleFormatInfo.format.
181     // It then samples from it and renders the results in a texture with the
182     // renderFormatInfo.format format. Finally it checks that the data rendered matches
183     // expectedRenderData, using the cutom expectation if present.
DoSampleTest(FormatTestInfo sampleFormatInfo,const void * sampleData,size_t sampleDataSize,FormatTestInfo renderFormatInfo,const void * expectedRenderData,size_t expectedRenderDataSize,detail::Expectation * customExpectation)184     void DoSampleTest(FormatTestInfo sampleFormatInfo,
185                       const void* sampleData,
186                       size_t sampleDataSize,
187                       FormatTestInfo renderFormatInfo,
188                       const void* expectedRenderData,
189                       size_t expectedRenderDataSize,
190                       detail::Expectation* customExpectation) {
191         // The input data should contain an exact number of texels
192         ASSERT(sampleDataSize % sampleFormatInfo.texelByteSize == 0);
193         uint32_t width = sampleDataSize / sampleFormatInfo.texelByteSize;
194 
195         // The input data must be a multiple of 4 byte in length for WriteBuffer
196         ASSERT(sampleDataSize % 4 == 0);
197         ASSERT(expectedRenderDataSize % 4 == 0);
198 
199         // Create the texture we will sample from
200         wgpu::TextureDescriptor sampleTextureDesc;
201         sampleTextureDesc.usage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::TextureBinding;
202         sampleTextureDesc.size = {width, 1, 1};
203         sampleTextureDesc.format = sampleFormatInfo.format;
204         wgpu::Texture sampleTexture = device.CreateTexture(&sampleTextureDesc);
205 
206         wgpu::Buffer uploadBuffer = utils::CreateBufferFromData(device, sampleData, sampleDataSize,
207                                                                 wgpu::BufferUsage::CopySrc);
208 
209         // Create the texture that we will render results to
210         ASSERT(expectedRenderDataSize == width * renderFormatInfo.texelByteSize);
211 
212         wgpu::TextureDescriptor renderTargetDesc;
213         renderTargetDesc.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::RenderAttachment;
214         renderTargetDesc.size = {width, 1, 1};
215         renderTargetDesc.format = renderFormatInfo.format;
216 
217         wgpu::Texture renderTarget = device.CreateTexture(&renderTargetDesc);
218 
219         // Create the readback buffer for the data in renderTarget
220         wgpu::BufferDescriptor readbackBufferDesc;
221         readbackBufferDesc.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::CopySrc;
222         readbackBufferDesc.size = expectedRenderDataSize;
223         wgpu::Buffer readbackBuffer = device.CreateBuffer(&readbackBufferDesc);
224 
225         // Prepare objects needed to sample from texture in the renderpass
226         wgpu::RenderPipeline pipeline = CreateSamplePipeline(sampleFormatInfo, renderFormatInfo);
227         wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
228                                                          {{0, sampleTexture.CreateView()}});
229 
230         // Encode commands for the test that fill texture, sample it to render to renderTarget then
231         // copy renderTarget in a buffer so we can read it easily.
232         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
233 
234         {
235             wgpu::ImageCopyBuffer bufferView = utils::CreateImageCopyBuffer(uploadBuffer, 0, 256);
236             wgpu::ImageCopyTexture textureView =
237                 utils::CreateImageCopyTexture(sampleTexture, 0, {0, 0, 0});
238             wgpu::Extent3D extent{width, 1, 1};
239             encoder.CopyBufferToTexture(&bufferView, &textureView, &extent);
240         }
241 
242         utils::ComboRenderPassDescriptor renderPassDesc({renderTarget.CreateView()});
243         wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&renderPassDesc);
244         renderPass.SetPipeline(pipeline);
245         renderPass.SetBindGroup(0, bindGroup);
246         renderPass.Draw(3);
247         renderPass.EndPass();
248 
249         {
250             wgpu::ImageCopyBuffer bufferView = utils::CreateImageCopyBuffer(readbackBuffer, 0, 256);
251             wgpu::ImageCopyTexture textureView =
252                 utils::CreateImageCopyTexture(renderTarget, 0, {0, 0, 0});
253             wgpu::Extent3D extent{width, 1, 1};
254             encoder.CopyTextureToBuffer(&textureView, &bufferView, &extent);
255         }
256 
257         wgpu::CommandBuffer commands = encoder.Finish();
258         queue.Submit(1, &commands);
259 
260         // For floats use a special expectation that understands how to compare NaNs and support a
261         // tolerance.
262         if (customExpectation != nullptr) {
263             AddBufferExpectation(__FILE__, __LINE__, readbackBuffer, 0, expectedRenderDataSize,
264                                  customExpectation);
265         } else {
266             EXPECT_BUFFER_U32_RANGE_EQ(static_cast<const uint32_t*>(expectedRenderData),
267                                        readbackBuffer, 0,
268                                        expectedRenderDataSize / sizeof(uint32_t));
269         }
270     }
271 
272     template <typename Data>
ExpandDataTo4Component(const std::vector<Data> & originalData,uint32_t originalComponentCount,const std::array<Data,4> & defaultValues)273     std::vector<Data> ExpandDataTo4Component(const std::vector<Data>& originalData,
274                                              uint32_t originalComponentCount,
275                                              const std::array<Data, 4>& defaultValues) {
276         std::vector<Data> result;
277 
278         for (size_t i = 0; i < originalData.size() / originalComponentCount; i++) {
279             for (size_t component = 0; component < 4; component++) {
280                 if (component < originalComponentCount) {
281                     result.push_back(originalData[i * originalComponentCount + component]);
282                 } else {
283                     result.push_back(defaultValues[component]);
284                 }
285             }
286         }
287 
288         return result;
289     }
290 
291     // Helper functions used to run tests that convert the typeful test objects to typeless void*
292 
293     template <typename TextureData, typename RenderData>
DoFormatSamplingTest(FormatTestInfo formatInfo,const std::vector<TextureData> & textureData,const std::vector<RenderData> & expectedRenderData,detail::Expectation * customExpectation=nullptr)294     void DoFormatSamplingTest(FormatTestInfo formatInfo,
295                               const std::vector<TextureData>& textureData,
296                               const std::vector<RenderData>& expectedRenderData,
297                               detail::Expectation* customExpectation = nullptr) {
298         FormatTestInfo renderFormatInfo = GetUncompressedFormatInfo(formatInfo);
299 
300         // Expand the expected data to be 4 component wide with the default sampling values of
301         // (0, 0, 0, 1)
302         std::array<RenderData, 4> defaultValues = {RenderData(0), RenderData(0), RenderData(0),
303                                                    RenderData(1)};
304         std::vector<RenderData> expandedRenderData =
305             ExpandDataTo4Component(expectedRenderData, formatInfo.componentCount, defaultValues);
306 
307         DoSampleTest(formatInfo, textureData.data(), textureData.size() * sizeof(TextureData),
308                      renderFormatInfo, expandedRenderData.data(),
309                      expandedRenderData.size() * sizeof(RenderData), customExpectation);
310     }
311 
312     template <typename TextureData>
DoFloatFormatSamplingTest(FormatTestInfo formatInfo,const std::vector<TextureData> & textureData,const std::vector<float> & expectedRenderData,float floatTolerance=0.0f)313     void DoFloatFormatSamplingTest(FormatTestInfo formatInfo,
314                                    const std::vector<TextureData>& textureData,
315                                    const std::vector<float>& expectedRenderData,
316                                    float floatTolerance = 0.0f) {
317         // Expand the expected data to be 4 component wide with the default sampling values of
318         // (0, 0, 0, 1)
319         std::array<float, 4> defaultValues = {0.0f, 0.0f, 0.0f, 1.0f};
320         std::vector<float> expandedRenderData =
321             ExpandDataTo4Component(expectedRenderData, formatInfo.componentCount, defaultValues);
322 
323         // Use a special expectation that understands how to compare NaNs and supports a tolerance.
324         DoFormatSamplingTest(formatInfo, textureData, expectedRenderData,
325                              new ExpectFloatWithTolerance(expandedRenderData, floatTolerance));
326     }
327 
328     template <typename TextureData, typename RenderData>
DoFormatRenderingTest(FormatTestInfo formatInfo,const std::vector<TextureData> & textureData,const std::vector<RenderData> & expectedRenderData,detail::Expectation * customExpectation=nullptr)329     void DoFormatRenderingTest(FormatTestInfo formatInfo,
330                                const std::vector<TextureData>& textureData,
331                                const std::vector<RenderData>& expectedRenderData,
332                                detail::Expectation* customExpectation = nullptr) {
333         FormatTestInfo sampleFormatInfo = GetUncompressedFormatInfo(formatInfo);
334 
335         // Expand the sampling texture data to contain garbage data for unused components to check
336         // that they don't influence the rendering result.
337         std::array<TextureData, 4> garbageValues;
338         garbageValues.fill(13);
339         std::vector<TextureData> expandedTextureData =
340             ExpandDataTo4Component(textureData, formatInfo.componentCount, garbageValues);
341 
342         DoSampleTest(sampleFormatInfo, expandedTextureData.data(),
343                      expandedTextureData.size() * sizeof(TextureData), formatInfo,
344                      expectedRenderData.data(), expectedRenderData.size() * sizeof(RenderData),
345                      customExpectation);
346     }
347 
348     // Below are helper functions for types that are very similar to one another so the logic is
349     // shared.
350 
351     template <typename T>
DoUnormTest(FormatTestInfo formatInfo)352     void DoUnormTest(FormatTestInfo formatInfo) {
353         static_assert(!std::is_signed<T>::value && std::is_integral<T>::value, "");
354         ASSERT(sizeof(T) * formatInfo.componentCount == formatInfo.texelByteSize);
355         ASSERT(formatInfo.type == wgpu::TextureComponentType::Float);
356 
357         T maxValue = std::numeric_limits<T>::max();
358         std::vector<T> textureData = {0, 1, maxValue, maxValue};
359         std::vector<float> uncompressedData = {0.0f, 1.0f / maxValue, 1.0f, 1.0f};
360 
361         DoFormatSamplingTest(formatInfo, textureData, uncompressedData);
362         DoFormatRenderingTest(formatInfo, uncompressedData, textureData);
363     }
364 
365     template <typename T>
DoSnormTest(FormatTestInfo formatInfo)366     void DoSnormTest(FormatTestInfo formatInfo) {
367         static_assert(std::is_signed<T>::value && std::is_integral<T>::value, "");
368         ASSERT(sizeof(T) * formatInfo.componentCount == formatInfo.texelByteSize);
369         ASSERT(formatInfo.type == wgpu::TextureComponentType::Float);
370 
371         T maxValue = std::numeric_limits<T>::max();
372         T minValue = std::numeric_limits<T>::min();
373         std::vector<T> textureData = {0, 1, -1, maxValue, minValue, T(minValue + 1), 0, 0};
374         std::vector<float> uncompressedData = {
375             0.0f, 1.0f / maxValue, -1.0f / maxValue, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f};
376 
377         DoFloatFormatSamplingTest(formatInfo, textureData, uncompressedData, 0.0001f / maxValue);
378         // Snorm formats aren't renderable because they are not guaranteed renderable in Vulkan
379     }
380 
381     template <typename T>
DoUintTest(FormatTestInfo formatInfo)382     void DoUintTest(FormatTestInfo formatInfo) {
383         static_assert(!std::is_signed<T>::value && std::is_integral<T>::value, "");
384         ASSERT(sizeof(T) * formatInfo.componentCount == formatInfo.texelByteSize);
385         ASSERT(formatInfo.type == wgpu::TextureComponentType::Uint);
386 
387         T maxValue = std::numeric_limits<T>::max();
388         std::vector<T> textureData = {0, 1, maxValue, maxValue};
389         std::vector<uint32_t> uncompressedData = {0, 1, maxValue, maxValue};
390 
391         DoFormatSamplingTest(formatInfo, textureData, uncompressedData);
392         DoFormatRenderingTest(formatInfo, uncompressedData, textureData);
393     }
394 
395     template <typename T>
DoSintTest(FormatTestInfo formatInfo)396     void DoSintTest(FormatTestInfo formatInfo) {
397         static_assert(std::is_signed<T>::value && std::is_integral<T>::value, "");
398         ASSERT(sizeof(T) * formatInfo.componentCount == formatInfo.texelByteSize);
399         ASSERT(formatInfo.type == wgpu::TextureComponentType::Sint);
400 
401         T maxValue = std::numeric_limits<T>::max();
402         T minValue = std::numeric_limits<T>::min();
403         std::vector<T> textureData = {0, 1, maxValue, minValue};
404         std::vector<int32_t> uncompressedData = {0, 1, maxValue, minValue};
405 
406         DoFormatSamplingTest(formatInfo, textureData, uncompressedData);
407         DoFormatRenderingTest(formatInfo, uncompressedData, textureData);
408     }
409 
DoFloat32Test(FormatTestInfo formatInfo)410     void DoFloat32Test(FormatTestInfo formatInfo) {
411         ASSERT(sizeof(float) * formatInfo.componentCount == formatInfo.texelByteSize);
412         ASSERT(formatInfo.type == wgpu::TextureComponentType::Float);
413 
414         std::vector<float> textureData = {+0.0f,   -0.0f, 1.0f,     1.0e-29f,
415                                           1.0e29f, NAN,   INFINITY, -INFINITY};
416 
417         DoFloatFormatSamplingTest(formatInfo, textureData, textureData);
418         DoFormatRenderingTest(formatInfo, textureData, textureData,
419                               new ExpectFloatWithTolerance(textureData, 0.0f));
420     }
421 
DoFloat16Test(FormatTestInfo formatInfo)422     void DoFloat16Test(FormatTestInfo formatInfo) {
423         ASSERT(sizeof(int16_t) * formatInfo.componentCount == formatInfo.texelByteSize);
424         ASSERT(formatInfo.type == wgpu::TextureComponentType::Float);
425 
426         std::vector<float> uncompressedData = {+0.0f,  -0.0f, 1.0f,     1.01e-4f,
427                                                1.0e4f, NAN,   INFINITY, -INFINITY};
428         std::vector<uint16_t> textureData;
429         for (float value : uncompressedData) {
430             textureData.push_back(Float32ToFloat16(value));
431         }
432 
433         DoFloatFormatSamplingTest(formatInfo, textureData, uncompressedData, 1.0e-5f);
434 
435         // Use a special expectation that knows that all Float16 NaNs are equivalent.
436         DoFormatRenderingTest(formatInfo, uncompressedData, textureData,
437                               new ExpectFloat16(textureData));
438     }
439 };
440 
441 // Test the R8Unorm format
TEST_P(TextureFormatTest,R8Unorm)442 TEST_P(TextureFormatTest, R8Unorm) {
443     DoUnormTest<uint8_t>({wgpu::TextureFormat::R8Unorm, 1, wgpu::TextureComponentType::Float, 1});
444 }
445 
446 // Test the RG8Unorm format
TEST_P(TextureFormatTest,RG8Unorm)447 TEST_P(TextureFormatTest, RG8Unorm) {
448     DoUnormTest<uint8_t>({wgpu::TextureFormat::RG8Unorm, 2, wgpu::TextureComponentType::Float, 2});
449 }
450 
451 // Test the RGBA8Unorm format
TEST_P(TextureFormatTest,RGBA8Unorm)452 TEST_P(TextureFormatTest, RGBA8Unorm) {
453     DoUnormTest<uint8_t>(
454         {wgpu::TextureFormat::RGBA8Unorm, 4, wgpu::TextureComponentType::Float, 4});
455 }
456 
457 // Test the BGRA8Unorm format
TEST_P(TextureFormatTest,BGRA8Unorm)458 TEST_P(TextureFormatTest, BGRA8Unorm) {
459     // TODO(crbug.com/dawn/596): BGRA is unsupported on OpenGL ES; add workaround or validation
460     DAWN_SUPPRESS_TEST_IF(IsOpenGLES());
461     uint8_t maxValue = std::numeric_limits<uint8_t>::max();
462     std::vector<uint8_t> textureData = {maxValue, 1, 0, maxValue};
463     std::vector<float> uncompressedData = {0.0f, 1.0f / maxValue, 1.0f, 1.0f};
464     DoFormatSamplingTest({wgpu::TextureFormat::BGRA8Unorm, 4, wgpu::TextureComponentType::Float, 4},
465                          textureData, uncompressedData);
466     DoFormatRenderingTest(
467         {wgpu::TextureFormat::BGRA8Unorm, 4, wgpu::TextureComponentType::Float, 4},
468         uncompressedData, textureData);
469 }
470 
471 // Test the R8Snorm format
TEST_P(TextureFormatTest,R8Snorm)472 TEST_P(TextureFormatTest, R8Snorm) {
473     DoSnormTest<int8_t>({wgpu::TextureFormat::R8Snorm, 1, wgpu::TextureComponentType::Float, 1});
474 }
475 
476 // Test the RG8Snorm format
TEST_P(TextureFormatTest,RG8Snorm)477 TEST_P(TextureFormatTest, RG8Snorm) {
478     DoSnormTest<int8_t>({wgpu::TextureFormat::RG8Snorm, 2, wgpu::TextureComponentType::Float, 2});
479 }
480 
481 // Test the RGBA8Snorm format
TEST_P(TextureFormatTest,RGBA8Snorm)482 TEST_P(TextureFormatTest, RGBA8Snorm) {
483     DoSnormTest<int8_t>({wgpu::TextureFormat::RGBA8Snorm, 4, wgpu::TextureComponentType::Float, 4});
484 }
485 
486 // Test the R8Uint format
TEST_P(TextureFormatTest,R8Uint)487 TEST_P(TextureFormatTest, R8Uint) {
488     DoUintTest<uint8_t>({wgpu::TextureFormat::R8Uint, 1, wgpu::TextureComponentType::Uint, 1});
489 }
490 
491 // Test the RG8Uint format
TEST_P(TextureFormatTest,RG8Uint)492 TEST_P(TextureFormatTest, RG8Uint) {
493     DoUintTest<uint8_t>({wgpu::TextureFormat::RG8Uint, 2, wgpu::TextureComponentType::Uint, 2});
494 }
495 
496 // Test the RGBA8Uint format
TEST_P(TextureFormatTest,RGBA8Uint)497 TEST_P(TextureFormatTest, RGBA8Uint) {
498     DoUintTest<uint8_t>({wgpu::TextureFormat::RGBA8Uint, 4, wgpu::TextureComponentType::Uint, 4});
499 }
500 
501 // Test the R16Uint format
TEST_P(TextureFormatTest,R16Uint)502 TEST_P(TextureFormatTest, R16Uint) {
503     DoUintTest<uint16_t>({wgpu::TextureFormat::R16Uint, 2, wgpu::TextureComponentType::Uint, 1});
504 }
505 
506 // Test the RG16Uint format
TEST_P(TextureFormatTest,RG16Uint)507 TEST_P(TextureFormatTest, RG16Uint) {
508     DoUintTest<uint16_t>({wgpu::TextureFormat::RG16Uint, 4, wgpu::TextureComponentType::Uint, 2});
509 }
510 
511 // Test the RGBA16Uint format
TEST_P(TextureFormatTest,RGBA16Uint)512 TEST_P(TextureFormatTest, RGBA16Uint) {
513     DoUintTest<uint16_t>({wgpu::TextureFormat::RGBA16Uint, 8, wgpu::TextureComponentType::Uint, 4});
514 }
515 
516 // Test the R32Uint format
TEST_P(TextureFormatTest,R32Uint)517 TEST_P(TextureFormatTest, R32Uint) {
518     DoUintTest<uint32_t>({wgpu::TextureFormat::R32Uint, 4, wgpu::TextureComponentType::Uint, 1});
519 }
520 
521 // Test the RG32Uint format
TEST_P(TextureFormatTest,RG32Uint)522 TEST_P(TextureFormatTest, RG32Uint) {
523     DoUintTest<uint32_t>({wgpu::TextureFormat::RG32Uint, 8, wgpu::TextureComponentType::Uint, 2});
524 }
525 
526 // Test the RGBA32Uint format
TEST_P(TextureFormatTest,RGBA32Uint)527 TEST_P(TextureFormatTest, RGBA32Uint) {
528     DoUintTest<uint32_t>(
529         {wgpu::TextureFormat::RGBA32Uint, 16, wgpu::TextureComponentType::Uint, 4});
530 }
531 
532 // Test the R8Sint format
TEST_P(TextureFormatTest,R8Sint)533 TEST_P(TextureFormatTest, R8Sint) {
534     DoSintTest<int8_t>({wgpu::TextureFormat::R8Sint, 1, wgpu::TextureComponentType::Sint, 1});
535 }
536 
537 // Test the RG8Sint format
TEST_P(TextureFormatTest,RG8Sint)538 TEST_P(TextureFormatTest, RG8Sint) {
539     DoSintTest<int8_t>({wgpu::TextureFormat::RG8Sint, 2, wgpu::TextureComponentType::Sint, 2});
540 }
541 
542 // Test the RGBA8Sint format
TEST_P(TextureFormatTest,RGBA8Sint)543 TEST_P(TextureFormatTest, RGBA8Sint) {
544     DoSintTest<int8_t>({wgpu::TextureFormat::RGBA8Sint, 4, wgpu::TextureComponentType::Sint, 4});
545 }
546 
547 // Test the R16Sint format
TEST_P(TextureFormatTest,R16Sint)548 TEST_P(TextureFormatTest, R16Sint) {
549     DoSintTest<int16_t>({wgpu::TextureFormat::R16Sint, 2, wgpu::TextureComponentType::Sint, 1});
550 }
551 
552 // Test the RG16Sint format
TEST_P(TextureFormatTest,RG16Sint)553 TEST_P(TextureFormatTest, RG16Sint) {
554     DoSintTest<int16_t>({wgpu::TextureFormat::RG16Sint, 4, wgpu::TextureComponentType::Sint, 2});
555 }
556 
557 // Test the RGBA16Sint format
TEST_P(TextureFormatTest,RGBA16Sint)558 TEST_P(TextureFormatTest, RGBA16Sint) {
559     DoSintTest<int16_t>({wgpu::TextureFormat::RGBA16Sint, 8, wgpu::TextureComponentType::Sint, 4});
560 }
561 
562 // Test the R32Sint format
TEST_P(TextureFormatTest,R32Sint)563 TEST_P(TextureFormatTest, R32Sint) {
564     DoSintTest<int32_t>({wgpu::TextureFormat::R32Sint, 4, wgpu::TextureComponentType::Sint, 1});
565 }
566 
567 // Test the RG32Sint format
TEST_P(TextureFormatTest,RG32Sint)568 TEST_P(TextureFormatTest, RG32Sint) {
569     DoSintTest<int32_t>({wgpu::TextureFormat::RG32Sint, 8, wgpu::TextureComponentType::Sint, 2});
570 }
571 
572 // Test the RGBA32Sint format
TEST_P(TextureFormatTest,RGBA32Sint)573 TEST_P(TextureFormatTest, RGBA32Sint) {
574     DoSintTest<int32_t>({wgpu::TextureFormat::RGBA32Sint, 16, wgpu::TextureComponentType::Sint, 4});
575 }
576 
577 // Test the R32Float format
TEST_P(TextureFormatTest,R32Float)578 TEST_P(TextureFormatTest, R32Float) {
579     DoFloat32Test({wgpu::TextureFormat::R32Float, 4, wgpu::TextureComponentType::Float, 1});
580 }
581 
582 // Test the RG32Float format
TEST_P(TextureFormatTest,RG32Float)583 TEST_P(TextureFormatTest, RG32Float) {
584     DoFloat32Test({wgpu::TextureFormat::RG32Float, 8, wgpu::TextureComponentType::Float, 2});
585 }
586 
587 // Test the RGBA32Float format
TEST_P(TextureFormatTest,RGBA32Float)588 TEST_P(TextureFormatTest, RGBA32Float) {
589     DoFloat32Test({wgpu::TextureFormat::RGBA32Float, 16, wgpu::TextureComponentType::Float, 4});
590 }
591 
592 // Test the R16Float format
TEST_P(TextureFormatTest,R16Float)593 TEST_P(TextureFormatTest, R16Float) {
594     // TODO(https://crbug.com/swiftshader/147) Rendering INFINITY isn't handled correctly by
595     // swiftshader
596     DAWN_SUPPRESS_TEST_IF(IsVulkan() && IsSwiftshader() || IsANGLE());
597 
598     DoFloat16Test({wgpu::TextureFormat::R16Float, 2, wgpu::TextureComponentType::Float, 1});
599 }
600 
601 // Test the RG16Float format
TEST_P(TextureFormatTest,RG16Float)602 TEST_P(TextureFormatTest, RG16Float) {
603     // TODO(https://crbug.com/swiftshader/147) Rendering INFINITY isn't handled correctly by
604     // swiftshader
605     DAWN_SUPPRESS_TEST_IF(IsVulkan() && IsSwiftshader() || IsANGLE());
606 
607     DoFloat16Test({wgpu::TextureFormat::RG16Float, 4, wgpu::TextureComponentType::Float, 2});
608 }
609 
610 // Test the RGBA16Float format
TEST_P(TextureFormatTest,RGBA16Float)611 TEST_P(TextureFormatTest, RGBA16Float) {
612     // TODO(https://crbug.com/swiftshader/147) Rendering INFINITY isn't handled correctly by
613     // swiftshader
614     DAWN_SUPPRESS_TEST_IF(IsVulkan() && IsSwiftshader() || IsANGLE());
615 
616     DoFloat16Test({wgpu::TextureFormat::RGBA16Float, 8, wgpu::TextureComponentType::Float, 4});
617 }
618 
619 // Test the RGBA8Unorm format
TEST_P(TextureFormatTest,RGBA8UnormSrgb)620 TEST_P(TextureFormatTest, RGBA8UnormSrgb) {
621     uint8_t maxValue = std::numeric_limits<uint8_t>::max();
622     std::vector<uint8_t> textureData = {0, 1, maxValue, 64, 35, 68, 152, 168};
623 
624     std::vector<float> uncompressedData;
625     for (size_t i = 0; i < textureData.size(); i += 4) {
626         uncompressedData.push_back(SRGBToLinear(textureData[i + 0] / float(maxValue)));
627         uncompressedData.push_back(SRGBToLinear(textureData[i + 1] / float(maxValue)));
628         uncompressedData.push_back(SRGBToLinear(textureData[i + 2] / float(maxValue)));
629         // Alpha is linear for sRGB formats
630         uncompressedData.push_back(textureData[i + 3] / float(maxValue));
631     }
632 
633     DoFloatFormatSamplingTest(
634         {wgpu::TextureFormat::RGBA8UnormSrgb, 4, wgpu::TextureComponentType::Float, 4}, textureData,
635         uncompressedData, 1.0e-3);
636     DoFormatRenderingTest(
637         {wgpu::TextureFormat::RGBA8UnormSrgb, 4, wgpu::TextureComponentType::Float, 4},
638         uncompressedData, textureData);
639 }
640 
641 // Test the BGRA8UnormSrgb format
TEST_P(TextureFormatTest,BGRA8UnormSrgb)642 TEST_P(TextureFormatTest, BGRA8UnormSrgb) {
643     // TODO(cwallez@chromium.org): This format doesn't exist in OpenGL, emulate it using
644     // RGBA8UnormSrgb and swizzling / shader twiddling
645     DAWN_SUPPRESS_TEST_IF(IsOpenGL() || IsOpenGLES());
646 
647     uint8_t maxValue = std::numeric_limits<uint8_t>::max();
648     std::vector<uint8_t> textureData = {0, 1, maxValue, 64, 35, 68, 152, 168};
649 
650     std::vector<float> uncompressedData;
651     for (size_t i = 0; i < textureData.size(); i += 4) {
652         // Note that R and B are swapped
653         uncompressedData.push_back(SRGBToLinear(textureData[i + 2] / float(maxValue)));
654         uncompressedData.push_back(SRGBToLinear(textureData[i + 1] / float(maxValue)));
655         uncompressedData.push_back(SRGBToLinear(textureData[i + 0] / float(maxValue)));
656         // Alpha is linear for sRGB formats
657         uncompressedData.push_back(textureData[i + 3] / float(maxValue));
658     }
659 
660     DoFloatFormatSamplingTest(
661         {wgpu::TextureFormat::BGRA8UnormSrgb, 4, wgpu::TextureComponentType::Float, 4}, textureData,
662         uncompressedData, 1.0e-3);
663     DoFormatRenderingTest(
664         {wgpu::TextureFormat::BGRA8UnormSrgb, 4, wgpu::TextureComponentType::Float, 4},
665         uncompressedData, textureData);
666 }
667 
668 // Test the RGB10A2Unorm format
TEST_P(TextureFormatTest,RGB10A2Unorm)669 TEST_P(TextureFormatTest, RGB10A2Unorm) {
670     auto MakeRGB10A2 = [](uint32_t r, uint32_t g, uint32_t b, uint32_t a) -> uint32_t {
671         ASSERT((r & 0x3FF) == r);
672         ASSERT((g & 0x3FF) == g);
673         ASSERT((b & 0x3FF) == b);
674         ASSERT((a & 0x3) == a);
675         return r | g << 10 | b << 20 | a << 30;
676     };
677 
678     std::vector<uint32_t> textureData = {MakeRGB10A2(0, 0, 0, 0), MakeRGB10A2(1023, 1023, 1023, 1),
679                                          MakeRGB10A2(243, 576, 765, 2), MakeRGB10A2(0, 0, 0, 3)};
680     // clang-format off
681     std::vector<float> uncompressedData = {
682        0.0f, 0.0f, 0.0f, 0.0f,
683        1.0f, 1.0f, 1.0f, 1 / 3.0f,
684         243 / 1023.0f, 576 / 1023.0f, 765 / 1023.0f, 2 / 3.0f,
685        0.0f, 0.0f, 0.0f, 1.0f
686     };
687     // clang-format on
688 
689     DoFloatFormatSamplingTest(
690         {wgpu::TextureFormat::RGB10A2Unorm, 4, wgpu::TextureComponentType::Float, 4}, textureData,
691         uncompressedData, 1.0e-5);
692     DoFormatRenderingTest(
693         {wgpu::TextureFormat::RGB10A2Unorm, 4, wgpu::TextureComponentType::Float, 4},
694         uncompressedData, textureData);
695 }
696 
697 // Test the RG11B10Ufloat format
TEST_P(TextureFormatTest,RG11B10Ufloat)698 TEST_P(TextureFormatTest, RG11B10Ufloat) {
699     constexpr uint32_t kFloat11Zero = 0;
700     constexpr uint32_t kFloat11Infinity = 0x7C0;
701     constexpr uint32_t kFloat11Nan = 0x7C1;
702     constexpr uint32_t kFloat11One = 0x3C0;
703 
704     constexpr uint32_t kFloat10Zero = 0;
705     constexpr uint32_t kFloat10Infinity = 0x3E0;
706     constexpr uint32_t kFloat10Nan = 0x3E1;
707     constexpr uint32_t kFloat10One = 0x1E0;
708 
709     auto MakeRG11B10 = [](uint32_t r, uint32_t g, uint32_t b) {
710         ASSERT((r & 0x7FF) == r);
711         ASSERT((g & 0x7FF) == g);
712         ASSERT((b & 0x3FF) == b);
713         return r | g << 11 | b << 22;
714     };
715 
716     // Test each of (0, 1, INFINITY, NaN) for each component but never two with the same value at a
717     // time.
718     std::vector<uint32_t> textureData = {
719         MakeRG11B10(kFloat11Zero, kFloat11Infinity, kFloat10Nan),
720         MakeRG11B10(kFloat11Infinity, kFloat11Nan, kFloat10One),
721         MakeRG11B10(kFloat11Nan, kFloat11One, kFloat10Zero),
722         MakeRG11B10(kFloat11One, kFloat11Zero, kFloat10Infinity),
723     };
724 
725     // This is one of the only 3-channel formats, so we don't have specific testing for them. Alpha
726     // should always be sampled as 1
727     // clang-format off
728     std::vector<float> uncompressedData = {
729         0.0f,     INFINITY, NAN,      1.0f,
730         INFINITY, NAN,      1.0f,     1.0f,
731         NAN,      1.0f,     0.0f,     1.0f,
732         1.0f,     0.0f,     INFINITY, 1.0f
733     };
734     // clang-format on
735 
736     DoFloatFormatSamplingTest(
737         {wgpu::TextureFormat::RG11B10Ufloat, 4, wgpu::TextureComponentType::Float, 4}, textureData,
738         uncompressedData);
739     // This format is not renderable.
740 }
741 
742 // Test the RGB9E5Ufloat format
TEST_P(TextureFormatTest,RGB9E5Ufloat)743 TEST_P(TextureFormatTest, RGB9E5Ufloat) {
744     // RGB9E5 is different from other floating point formats because the mantissa doesn't index in
745     // the window defined by the exponent but is instead treated as a pure multiplier. There is
746     // also no Infinity or NaN. The OpenGL 4.6 spec has the best explanation I've found in section
747     // 8.25 "Shared Exponent Texture Color Conversion":
748     //
749     //   red = reduint * 2^(expuint - B - N) = reduint * 2^(expuint - 24)
750     //
751     // Where reduint and expuint are the integer values when considering the E5 as a 5bit uint, and
752     // the r9 as a 9bit uint. B the number of bits of the mantissa (9), and N the offset for the
753     // exponent (15).
754 
755     float smallestExponent = std::pow(2.0f, -24.0f);
756     float largestExponent = std::pow(2.0f, float(31 - 24));
757 
758     auto MakeRGB9E5 = [](uint32_t r, uint32_t g, uint32_t b, uint32_t e) {
759         ASSERT((r & 0x1FF) == r);
760         ASSERT((g & 0x1FF) == g);
761         ASSERT((b & 0x1FF) == b);
762         ASSERT((e & 0x1F) == e);
763         return r | g << 9 | b << 18 | e << 27;
764     };
765 
766     // Test the smallest largest, and "1" exponents
767     std::vector<uint32_t> textureData = {
768         MakeRGB9E5(0, 1, 2, 0b00000),
769         MakeRGB9E5(2, 1, 0, 0b11111),
770         MakeRGB9E5(0, 1, 2, 0b11000),
771     };
772 
773     // This is one of the only 3-channel formats, so we don't have specific testing for them. Alpha
774     // should always be sampled as 1
775     // clang-format off
776     std::vector<float> uncompressedData = {
777         0.0f, smallestExponent, 2.0f * smallestExponent, 1.0f,
778         2.0f * largestExponent, largestExponent, 0.0f, 1.0f,
779         0.0f, 1.0f, 2.0f, 1.0f,
780     };
781     // clang-format on
782 
783     DoFloatFormatSamplingTest(
784         {wgpu::TextureFormat::RGB9E5Ufloat, 4, wgpu::TextureComponentType::Float, 4}, textureData,
785         uncompressedData);
786     // This format is not renderable.
787 }
788 
789 DAWN_INSTANTIATE_TEST(TextureFormatTest,
790                       D3D12Backend(),
791                       MetalBackend(),
792                       OpenGLBackend(),
793                       OpenGLESBackend(),
794                       VulkanBackend());
795