• 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/Constants.h"
19 #include "common/Math.h"
20 #include "utils/ComboRenderPipelineDescriptor.h"
21 #include "utils/DawnHelpers.h"
22 
23 // Create a 2D texture for sampling in the tests.
Create2DTexture(dawn::Device device,dawn::TextureFormat format,uint32_t width,uint32_t height,uint32_t arrayLayerCount=1,uint32_t mipLevelCount=1,dawn::TextureUsageBit usage=dawn::TextureUsageBit::Sampled|dawn::TextureUsageBit::CopyDst)24 dawn::Texture Create2DTexture(dawn::Device device,
25                               dawn::TextureFormat format,
26                               uint32_t width,
27                               uint32_t height,
28                               uint32_t arrayLayerCount = 1,
29                               uint32_t mipLevelCount = 1,
30                               dawn::TextureUsageBit usage = dawn::TextureUsageBit::Sampled |
31                                                             dawn::TextureUsageBit::CopyDst) {
32     dawn::TextureDescriptor descriptor;
33     descriptor.dimension = dawn::TextureDimension::e2D;
34     descriptor.format = format;
35     descriptor.size.width = width;
36     descriptor.size.height = height;
37     descriptor.size.depth = 1;
38     descriptor.arrayLayerCount = arrayLayerCount;
39     descriptor.sampleCount = 1;
40     descriptor.mipLevelCount = mipLevelCount;
41     descriptor.usage = usage;
42     return device.CreateTexture(&descriptor);
43 }
44 
45 // The helper struct to configure the copies between buffers and textures.
46 struct CopyConfig {
47     dawn::TextureFormat format;
48     uint32_t textureWidthLevel0;
49     uint32_t textureHeightLevel0;
50     dawn::Extent3D copyExtent3D;
51     dawn::Origin3D copyOrigin3D = {0, 0, 0};
52     uint32_t arrayLayerCount = 1;
53     uint32_t mipmapLevelCount = 1;
54     uint32_t baseMipmapLevel = 0;
55     uint32_t baseArrayLayer = 0;
56     uint32_t bufferOffset = 0;
57     uint32_t rowPitchAlignment = kTextureRowPitchAlignment;
58     uint32_t imageHeight = 0;
59 };
60 
61 class CompressedTextureBCFormatTest : public DawnTest {
62   protected:
SetUp()63     void SetUp() override {
64         DawnTest::SetUp();
65         mBindGroupLayout = utils::MakeBindGroupLayout(
66             device, {{0, dawn::ShaderStageBit::Fragment, dawn::BindingType::Sampler},
67                      {1, dawn::ShaderStageBit::Fragment, dawn::BindingType::SampledTexture}});
68     }
69 
70     // Copy the compressed texture data into the destination texture as is specified in copyConfig.
CopyDataIntoCompressedTexture(dawn::Texture bcCompressedTexture,const CopyConfig & copyConfig)71     void CopyDataIntoCompressedTexture(dawn::Texture bcCompressedTexture,
72                                        const CopyConfig& copyConfig) {
73         // Compute the upload buffer size with rowPitchAlignment and the copy region.
74         uint32_t actualWidthAtLevel = copyConfig.textureWidthLevel0 >> copyConfig.baseMipmapLevel;
75         uint32_t actualHeightAtLevel = copyConfig.textureHeightLevel0 >> copyConfig.baseMipmapLevel;
76         uint32_t copyWidthInBlockAtLevel =
77             (actualWidthAtLevel + kBCBlockWidthInTexels - 1) / kBCBlockWidthInTexels;
78         uint32_t copyHeightInBlockAtLevel =
79             (actualHeightAtLevel + kBCBlockHeightInTexels - 1) / kBCBlockHeightInTexels;
80         uint32_t bufferRowPitchInBytes = 0;
81         if (copyConfig.rowPitchAlignment != 0) {
82             bufferRowPitchInBytes = copyConfig.rowPitchAlignment;
83         } else {
84             bufferRowPitchInBytes =
85                 copyWidthInBlockAtLevel * CompressedFormatBlockSizeInBytes(copyConfig.format);
86         }
87         uint32_t uploadBufferSize =
88             copyConfig.bufferOffset + bufferRowPitchInBytes * copyHeightInBlockAtLevel;
89 
90         // Fill uploadData with the pre-prepared one-block compressed texture data.
91         std::vector<uint8_t> uploadData(uploadBufferSize, 0);
92         std::vector<uint8_t> oneBlockCompressedTextureData =
93             GetOneBlockBCFormatTextureData(copyConfig.format);
94         for (uint32_t h = 0; h < copyHeightInBlockAtLevel; ++h) {
95             for (uint32_t w = 0; w < copyWidthInBlockAtLevel; ++w) {
96                 uint32_t uploadBufferOffset = copyConfig.bufferOffset + bufferRowPitchInBytes * h +
97                                               oneBlockCompressedTextureData.size() * w;
98                 std::memcpy(&uploadData[uploadBufferOffset], oneBlockCompressedTextureData.data(),
99                             oneBlockCompressedTextureData.size() * sizeof(uint8_t));
100             }
101         }
102 
103         // Copy texture data from a staging buffer to the destination texture.
104         dawn::Buffer stagingBuffer = utils::CreateBufferFromData(
105             device, uploadData.data(), uploadBufferSize, dawn::BufferUsageBit::CopySrc);
106         dawn::BufferCopyView bufferCopyView =
107             utils::CreateBufferCopyView(stagingBuffer, copyConfig.bufferOffset,
108                                         copyConfig.rowPitchAlignment, copyConfig.imageHeight);
109         dawn::TextureCopyView textureCopyView =
110             utils::CreateTextureCopyView(bcCompressedTexture, copyConfig.baseMipmapLevel,
111                                          copyConfig.baseArrayLayer, copyConfig.copyOrigin3D);
112 
113         dawn::CommandEncoder encoder = device.CreateCommandEncoder();
114         encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, &copyConfig.copyExtent3D);
115         dawn::CommandBuffer copy = encoder.Finish();
116         queue.Submit(1, &copy);
117     }
118 
119     // Create the bind group that includes a BC texture and a sampler.
CreateBindGroupForTest(dawn::Texture bcCompressedTexture,dawn::TextureFormat bcFormat,uint32_t baseArrayLayer=0,uint32_t baseMipLevel=0)120     dawn::BindGroup CreateBindGroupForTest(dawn::Texture bcCompressedTexture,
121                                            dawn::TextureFormat bcFormat,
122                                            uint32_t baseArrayLayer = 0,
123                                            uint32_t baseMipLevel = 0) {
124         dawn::SamplerDescriptor samplerDesc = utils::GetDefaultSamplerDescriptor();
125         samplerDesc.minFilter = dawn::FilterMode::Nearest;
126         samplerDesc.magFilter = dawn::FilterMode::Nearest;
127         dawn::Sampler sampler = device.CreateSampler(&samplerDesc);
128 
129         dawn::TextureViewDescriptor textureViewDescriptor;
130         textureViewDescriptor.format = bcFormat;
131         textureViewDescriptor.dimension = dawn::TextureViewDimension::e2D;
132         textureViewDescriptor.baseMipLevel = baseMipLevel;
133         textureViewDescriptor.baseArrayLayer = baseArrayLayer;
134         textureViewDescriptor.arrayLayerCount = 1;
135         textureViewDescriptor.mipLevelCount = 1;
136         dawn::TextureView bcTextureView = bcCompressedTexture.CreateView(&textureViewDescriptor);
137 
138         return utils::MakeBindGroup(device, mBindGroupLayout, {{0, sampler}, {1, bcTextureView}});
139     }
140 
141     // Create a render pipeline for sampling from a BC texture and rendering into the render target.
CreateRenderPipelineForTest()142     dawn::RenderPipeline CreateRenderPipelineForTest() {
143         dawn::PipelineLayout pipelineLayout =
144             utils::MakeBasicPipelineLayout(device, &mBindGroupLayout);
145 
146         utils::ComboRenderPipelineDescriptor renderPipelineDescriptor(device);
147         dawn::ShaderModule vsModule =
148             utils::CreateShaderModule(device, utils::ShaderStage::Vertex, R"(
149             #version 450
150             layout(location=0) out vec2 texCoord;
151             void main() {
152                 const vec2 pos[3] = vec2[3](
153                     vec2(-3.0f, -1.0f),
154                     vec2( 3.0f, -1.0f),
155                     vec2( 0.0f,  2.0f)
156                 );
157                 gl_Position = vec4(pos[gl_VertexIndex], 0.0f, 1.0f);
158                 texCoord = gl_Position.xy / 2.0f + vec2(0.5f);
159             })");
160         dawn::ShaderModule fsModule =
161             utils::CreateShaderModule(device, utils::ShaderStage::Fragment, R"(
162             #version 450
163             layout(set = 0, binding = 0) uniform sampler sampler0;
164             layout(set = 0, binding = 1) uniform texture2D texture0;
165             layout(location = 0) in vec2 texCoord;
166             layout(location = 0) out vec4 fragColor;
167 
168             void main() {
169                 fragColor = texture(sampler2D(texture0, sampler0), texCoord);
170             })");
171         renderPipelineDescriptor.cVertexStage.module = vsModule;
172         renderPipelineDescriptor.cFragmentStage.module = fsModule;
173         renderPipelineDescriptor.layout = pipelineLayout;
174         renderPipelineDescriptor.cColorStates[0]->format =
175             utils::BasicRenderPass::kDefaultColorFormat;
176         return device.CreateRenderPipeline(&renderPipelineDescriptor);
177     }
178 
179     // Run the given render pipeline and bind group and verify the pixels in the render target.
VerifyCompressedTexturePixelValues(dawn::RenderPipeline renderPipeline,dawn::BindGroup bindGroup,const dawn::Extent3D & renderTargetSize,const dawn::Origin3D & expectedOrigin,const dawn::Extent3D & expectedExtent,const std::vector<RGBA8> & expected)180     void VerifyCompressedTexturePixelValues(dawn::RenderPipeline renderPipeline,
181                                             dawn::BindGroup bindGroup,
182                                             const dawn::Extent3D& renderTargetSize,
183                                             const dawn::Origin3D& expectedOrigin,
184                                             const dawn::Extent3D& expectedExtent,
185                                             const std::vector<RGBA8>& expected) {
186         ASSERT(expected.size() == renderTargetSize.width * renderTargetSize.height);
187         utils::BasicRenderPass renderPass =
188             utils::CreateBasicRenderPass(device, renderTargetSize.width, renderTargetSize.height);
189 
190         dawn::CommandEncoder encoder = device.CreateCommandEncoder();
191         {
192             dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
193             pass.SetPipeline(renderPipeline);
194             pass.SetBindGroup(0, bindGroup, 0, nullptr);
195             pass.Draw(6, 1, 0, 0);
196             pass.EndPass();
197         }
198 
199         dawn::CommandBuffer commands = encoder.Finish();
200         queue.Submit(1, &commands);
201 
202         EXPECT_TEXTURE_RGBA8_EQ(expected.data(), renderPass.color, expectedOrigin.x,
203                                 expectedOrigin.y, expectedExtent.width, expectedExtent.height, 0,
204                                 0);
205     }
206 
207     // Run the tests that copies pre-prepared BC format data into a BC texture and verifies if we
208     // can render correctly with the pixel values sampled from the BC texture.
TestCopyRegionIntoBCFormatTextures(const CopyConfig & config)209     void TestCopyRegionIntoBCFormatTextures(const CopyConfig& config) {
210         dawn::Texture bcTexture = Create2DTexture(device, config.format, config.textureWidthLevel0,
211                                                   config.textureHeightLevel0,
212                                                   config.arrayLayerCount, config.mipmapLevelCount);
213         CopyDataIntoCompressedTexture(bcTexture, config);
214 
215         dawn::BindGroup bindGroup = CreateBindGroupForTest(
216             bcTexture, config.format, config.baseArrayLayer, config.baseMipmapLevel);
217         dawn::RenderPipeline renderPipeline = CreateRenderPipelineForTest();
218 
219         dawn::Extent3D virtualSizeAtLevel = GetVirtualSizeAtLevel(config);
220 
221         // The copy region may exceed the subresource size because of the required paddings for BC
222         // blocks, so we should limit the size of the expectedData to make it match the real size
223         // of the render target.
224         dawn::Extent3D noPaddingExtent3D = config.copyExtent3D;
225         if (config.copyOrigin3D.x + config.copyExtent3D.width > virtualSizeAtLevel.width) {
226             noPaddingExtent3D.width = virtualSizeAtLevel.width - config.copyOrigin3D.x;
227         }
228         if (config.copyOrigin3D.y + config.copyExtent3D.height > virtualSizeAtLevel.height) {
229             noPaddingExtent3D.height = virtualSizeAtLevel.height - config.copyOrigin3D.y;
230         }
231 
232         std::vector<RGBA8> expectedData = GetExpectedData(config.format, virtualSizeAtLevel);
233         VerifyCompressedTexturePixelValues(renderPipeline, bindGroup, virtualSizeAtLevel,
234                                            config.copyOrigin3D, noPaddingExtent3D, expectedData);
235     }
236 
237     // Return the BC block size in bytes.
CompressedFormatBlockSizeInBytes(dawn::TextureFormat format)238     static uint32_t CompressedFormatBlockSizeInBytes(dawn::TextureFormat format) {
239         switch (format) {
240             case dawn::TextureFormat::BC1RGBAUnorm:
241             case dawn::TextureFormat::BC1RGBAUnormSrgb:
242             case dawn::TextureFormat::BC4RSnorm:
243             case dawn::TextureFormat::BC4RUnorm:
244                 return 8;
245             case dawn::TextureFormat::BC2RGBAUnorm:
246             case dawn::TextureFormat::BC2RGBAUnormSrgb:
247             case dawn::TextureFormat::BC3RGBAUnorm:
248             case dawn::TextureFormat::BC3RGBAUnormSrgb:
249             case dawn::TextureFormat::BC5RGSnorm:
250             case dawn::TextureFormat::BC5RGUnorm:
251             case dawn::TextureFormat::BC6HRGBSfloat:
252             case dawn::TextureFormat::BC6HRGBUfloat:
253             case dawn::TextureFormat::BC7RGBAUnorm:
254             case dawn::TextureFormat::BC7RGBAUnormSrgb:
255                 return 16;
256             default:
257                 UNREACHABLE();
258                 return 0;
259         }
260     }
261 
262     // Return the pre-prepared one-block BC texture data.
GetOneBlockBCFormatTextureData(dawn::TextureFormat bcFormat)263     static std::vector<uint8_t> GetOneBlockBCFormatTextureData(dawn::TextureFormat bcFormat) {
264         switch (bcFormat) {
265             // The expected data represents 4x4 pixel images with the left side dark red and the
266             // right side dark green. We specify the same compressed data in both sRGB and non-sRGB
267             // tests, but the rendering result should be different because for sRGB formats, the
268             // red, green, and blue components are converted from an sRGB color space to a linear
269             // color space as part of filtering.
270             case dawn::TextureFormat::BC1RGBAUnorm:
271             case dawn::TextureFormat::BC1RGBAUnormSrgb:
272                 return {0x0, 0xC0, 0x60, 0x6, 0x50, 0x50, 0x50, 0x50};
273             case dawn::TextureFormat::BC7RGBAUnorm:
274             case dawn::TextureFormat::BC7RGBAUnormSrgb:
275                 return {0x50, 0x18, 0xfc, 0xf, 0x0,  0x30, 0xe3, 0xe1,
276                         0xe1, 0xe1, 0xc1, 0xf, 0xfc, 0xc0, 0xf,  0xfc};
277 
278             // The expected data represents 4x4 pixel images with the left side dark red and the
279             // right side dark green. The pixels in the left side of the block all have an alpha
280             // value equal to 0x88. We specify the same compressed data in both sRGB and non-sRGB
281             // tests, but the rendering result should be different because for sRGB formats, the
282             // red, green, and blue components are converted from an sRGB color space to a linear
283             // color space as part of filtering, and any alpha component is left unchanged.
284             case dawn::TextureFormat::BC2RGBAUnorm:
285             case dawn::TextureFormat::BC2RGBAUnormSrgb:
286                 return {0x88, 0xFF, 0x88, 0xFF, 0x88, 0xFF, 0x88, 0xFF,
287                         0x0,  0xC0, 0x60, 0x6,  0x50, 0x50, 0x50, 0x50};
288             case dawn::TextureFormat::BC3RGBAUnorm:
289             case dawn::TextureFormat::BC3RGBAUnormSrgb:
290                 return {0x88, 0xFF, 0x40, 0x2, 0x24, 0x40, 0x2,  0x24,
291                         0x0,  0xC0, 0x60, 0x6, 0x50, 0x50, 0x50, 0x50};
292 
293             // The expected data represents 4x4 pixel images with the left side red and the
294             // right side black.
295             case dawn::TextureFormat::BC4RSnorm:
296                 return {0x7F, 0x0, 0x40, 0x2, 0x24, 0x40, 0x2, 0x24};
297             case dawn::TextureFormat::BC4RUnorm:
298                 return {0xFF, 0x0, 0x40, 0x2, 0x24, 0x40, 0x2, 0x24};
299 
300             // The expected data represents 4x4 pixel images with the left side red and the right
301             // side green and was encoded with DirectXTex from Microsoft.
302             case dawn::TextureFormat::BC5RGSnorm:
303                 return {0x7f, 0x81, 0x40, 0x2,  0x24, 0x40, 0x2,  0x24,
304                         0x7f, 0x81, 0x9,  0x90, 0x0,  0x9,  0x90, 0x0};
305             case dawn::TextureFormat::BC5RGUnorm:
306                 return {0xff, 0x0, 0x40, 0x2,  0x24, 0x40, 0x2,  0x24,
307                         0xff, 0x0, 0x9,  0x90, 0x0,  0x9,  0x90, 0x0};
308             case dawn::TextureFormat::BC6HRGBSfloat:
309                 return {0xe3, 0x1f, 0x0, 0x0,  0x0, 0xe0, 0x1f, 0x0,
310                         0x0,  0xff, 0x0, 0xff, 0x0, 0xff, 0x0,  0xff};
311             case dawn::TextureFormat::BC6HRGBUfloat:
312                 return {0xe3, 0x3d, 0x0, 0x0,  0x0, 0xe0, 0x3d, 0x0,
313                         0x0,  0xff, 0x0, 0xff, 0x0, 0xff, 0x0,  0xff};
314 
315             default:
316                 UNREACHABLE();
317                 return {};
318         }
319     }
320 
321     // Return the texture data that is decoded from the result of GetOneBlockBCFormatTextureData in
322     // RGBA8 formats.
GetExpectedData(dawn::TextureFormat bcFormat,const dawn::Extent3D & testRegion)323     static std::vector<RGBA8> GetExpectedData(dawn::TextureFormat bcFormat,
324                                               const dawn::Extent3D& testRegion) {
325         constexpr RGBA8 kRed(255, 0, 0, 255);
326         constexpr RGBA8 kGreen(0, 255, 0, 255);
327         constexpr RGBA8 kBlack(0, 0, 0, 255);
328         constexpr RGBA8 kDarkRed(198, 0, 0, 255);
329         constexpr RGBA8 kDarkGreen(0, 207, 0, 255);
330         constexpr RGBA8 kDarkRedSRGB(144, 0, 0, 255);
331         constexpr RGBA8 kDarkGreenSRGB(0, 159, 0, 255);
332 
333         constexpr uint8_t kLeftAlpha = 0x88;
334         constexpr uint8_t kRightAlpha = 0xFF;
335 
336         switch (bcFormat) {
337             case dawn::TextureFormat::BC1RGBAUnorm:
338             case dawn::TextureFormat::BC7RGBAUnorm:
339                 return FillExpectedData(testRegion, kDarkRed, kDarkGreen);
340 
341             case dawn::TextureFormat::BC2RGBAUnorm:
342             case dawn::TextureFormat::BC3RGBAUnorm: {
343                 constexpr RGBA8 kLeftColor = RGBA8(kDarkRed.r, 0, 0, kLeftAlpha);
344                 constexpr RGBA8 kRightColor = RGBA8(0, kDarkGreen.g, 0, kRightAlpha);
345                 return FillExpectedData(testRegion, kLeftColor, kRightColor);
346             }
347 
348             case dawn::TextureFormat::BC1RGBAUnormSrgb:
349             case dawn::TextureFormat::BC7RGBAUnormSrgb:
350                 return FillExpectedData(testRegion, kDarkRedSRGB, kDarkGreenSRGB);
351 
352             case dawn::TextureFormat::BC2RGBAUnormSrgb:
353             case dawn::TextureFormat::BC3RGBAUnormSrgb: {
354                 constexpr RGBA8 kLeftColor = RGBA8(kDarkRedSRGB.r, 0, 0, kLeftAlpha);
355                 constexpr RGBA8 kRightColor = RGBA8(0, kDarkGreenSRGB.g, 0, kRightAlpha);
356                 return FillExpectedData(testRegion, kLeftColor, kRightColor);
357             }
358 
359             case dawn::TextureFormat::BC4RSnorm:
360             case dawn::TextureFormat::BC4RUnorm:
361                 return FillExpectedData(testRegion, kRed, kBlack);
362 
363             case dawn::TextureFormat::BC5RGSnorm:
364             case dawn::TextureFormat::BC5RGUnorm:
365             case dawn::TextureFormat::BC6HRGBSfloat:
366             case dawn::TextureFormat::BC6HRGBUfloat:
367                 return FillExpectedData(testRegion, kRed, kGreen);
368 
369             default:
370                 UNREACHABLE();
371                 return {};
372         }
373     }
374 
FillExpectedData(const dawn::Extent3D & testRegion,RGBA8 leftColorInBlock,RGBA8 rightColorInBlock)375     static std::vector<RGBA8> FillExpectedData(const dawn::Extent3D& testRegion,
376                                                RGBA8 leftColorInBlock,
377                                                RGBA8 rightColorInBlock) {
378         ASSERT(testRegion.depth == 1);
379 
380         std::vector<RGBA8> expectedData(testRegion.width * testRegion.height, leftColorInBlock);
381         for (uint32_t y = 0; y < testRegion.height; ++y) {
382             for (uint32_t x = 0; x < testRegion.width; ++x) {
383                 if (x % kBCBlockWidthInTexels >= kBCBlockWidthInTexels / 2) {
384                     expectedData[testRegion.width * y + x] = rightColorInBlock;
385                 }
386             }
387         }
388         return expectedData;
389     }
390 
GetVirtualSizeAtLevel(const CopyConfig & config)391     static dawn::Extent3D GetVirtualSizeAtLevel(const CopyConfig& config) {
392         return {config.textureWidthLevel0 >> config.baseMipmapLevel,
393                 config.textureHeightLevel0 >> config.baseMipmapLevel, 1};
394     }
395 
GetPhysicalSizeAtLevel(const CopyConfig & config)396     static dawn::Extent3D GetPhysicalSizeAtLevel(const CopyConfig& config) {
397         dawn::Extent3D sizeAtLevel = GetVirtualSizeAtLevel(config);
398         sizeAtLevel.width = (sizeAtLevel.width + kBCBlockWidthInTexels - 1) /
399                             kBCBlockWidthInTexels * kBCBlockWidthInTexels;
400         sizeAtLevel.height = (sizeAtLevel.height + kBCBlockHeightInTexels - 1) /
401                              kBCBlockHeightInTexels * kBCBlockHeightInTexels;
402         return sizeAtLevel;
403     }
404 
405     const std::array<dawn::TextureFormat, 14> kBCFormats = {
406         dawn::TextureFormat::BC1RGBAUnorm,  dawn::TextureFormat::BC1RGBAUnormSrgb,
407         dawn::TextureFormat::BC2RGBAUnorm,  dawn::TextureFormat::BC2RGBAUnormSrgb,
408         dawn::TextureFormat::BC3RGBAUnorm,  dawn::TextureFormat::BC3RGBAUnormSrgb,
409         dawn::TextureFormat::BC4RSnorm,     dawn::TextureFormat::BC4RUnorm,
410         dawn::TextureFormat::BC5RGSnorm,    dawn::TextureFormat::BC5RGUnorm,
411         dawn::TextureFormat::BC6HRGBSfloat, dawn::TextureFormat::BC6HRGBUfloat,
412         dawn::TextureFormat::BC7RGBAUnorm,  dawn::TextureFormat::BC7RGBAUnormSrgb};
413 
414     // Tthe block width and height in texels are 4 for all BC formats.
415     static constexpr uint32_t kBCBlockWidthInTexels = 4;
416     static constexpr uint32_t kBCBlockHeightInTexels = 4;
417 
418     dawn::BindGroupLayout mBindGroupLayout;
419 };
420 
421 // Test copying into the whole BC texture with 2x2 blocks and sampling from it.
TEST_P(CompressedTextureBCFormatTest,Basic)422 TEST_P(CompressedTextureBCFormatTest, Basic) {
423     CopyConfig config;
424     config.textureWidthLevel0 = 8;
425     config.textureHeightLevel0 = 8;
426     config.copyExtent3D = {config.textureWidthLevel0, config.textureHeightLevel0, 1};
427 
428     for (dawn::TextureFormat format : kBCFormats) {
429         config.format = format;
430         TestCopyRegionIntoBCFormatTextures(config);
431     }
432 }
433 
434 // Test copying into a sub-region of a texture with BC formats works correctly.
TEST_P(CompressedTextureBCFormatTest,CopyIntoSubRegion)435 TEST_P(CompressedTextureBCFormatTest, CopyIntoSubRegion) {
436     CopyConfig config;
437     config.textureHeightLevel0 = 8;
438     config.textureWidthLevel0 = 8;
439     config.rowPitchAlignment = kTextureRowPitchAlignment;
440 
441     const dawn::Origin3D kOrigin = {4, 4, 0};
442     const dawn::Extent3D kExtent3D = {4, 4, 1};
443     config.copyOrigin3D = kOrigin;
444     config.copyExtent3D = kExtent3D;
445 
446     for (dawn::TextureFormat format : kBCFormats) {
447         config.format = format;
448         TestCopyRegionIntoBCFormatTextures(config);
449     }
450 }
451 
452 // Test using rowPitch == 0 in the copies with BC formats works correctly.
TEST_P(CompressedTextureBCFormatTest,CopyWithZeroRowPitch)453 TEST_P(CompressedTextureBCFormatTest, CopyWithZeroRowPitch) {
454     CopyConfig config;
455     config.textureHeightLevel0 = 8;
456 
457     config.rowPitchAlignment = 0;
458 
459     for (dawn::TextureFormat format : kBCFormats) {
460         config.format = format;
461         config.textureWidthLevel0 = kTextureRowPitchAlignment /
462                                     CompressedFormatBlockSizeInBytes(config.format) *
463                                     kBCBlockWidthInTexels;
464         config.copyExtent3D = {config.textureWidthLevel0, config.textureHeightLevel0, 1};
465         TestCopyRegionIntoBCFormatTextures(config);
466     }
467 }
468 
469 // Test copying into the non-zero layer of a 2D array texture with BC formats works correctly.
TEST_P(CompressedTextureBCFormatTest,CopyIntoNonZeroArrayLayer)470 TEST_P(CompressedTextureBCFormatTest, CopyIntoNonZeroArrayLayer) {
471     CopyConfig config;
472     config.textureHeightLevel0 = 8;
473     config.textureWidthLevel0 = 8;
474     config.copyExtent3D = {config.textureWidthLevel0, config.textureHeightLevel0, 1};
475     config.rowPitchAlignment = kTextureRowPitchAlignment;
476 
477     constexpr uint32_t kArrayLayerCount = 3;
478     config.arrayLayerCount = kArrayLayerCount;
479     config.baseArrayLayer = kArrayLayerCount - 1;
480 
481     for (dawn::TextureFormat format : kBCFormats) {
482         config.format = format;
483         TestCopyRegionIntoBCFormatTextures(config);
484     }
485 }
486 
487 // Test copying into a non-zero mipmap level of a texture with BC texture formats.
TEST_P(CompressedTextureBCFormatTest,CopyBufferIntoNonZeroMipmapLevel)488 TEST_P(CompressedTextureBCFormatTest, CopyBufferIntoNonZeroMipmapLevel) {
489     CopyConfig config;
490     config.textureHeightLevel0 = 60;
491     config.textureWidthLevel0 = 60;
492     config.rowPitchAlignment = kTextureRowPitchAlignment;
493 
494     constexpr uint32_t kMipmapLevelCount = 3;
495     config.mipmapLevelCount = kMipmapLevelCount;
496     config.baseMipmapLevel = kMipmapLevelCount - 1;
497 
498     // The actual size of the texture at mipmap level == 2 is not a multiple of 4, paddings are
499     // required in the copies.
500     const uint32_t kActualWidthAtLevel = config.textureWidthLevel0 >> config.baseMipmapLevel;
501     const uint32_t kActualHeightAtLevel = config.textureHeightLevel0 >> config.baseMipmapLevel;
502     ASSERT(kActualWidthAtLevel % kBCBlockWidthInTexels != 0);
503     ASSERT(kActualHeightAtLevel % kBCBlockHeightInTexels != 0);
504 
505     const uint32_t kCopyWidthAtLevel = (kActualWidthAtLevel + kBCBlockWidthInTexels - 1) /
506                                        kBCBlockWidthInTexels * kBCBlockWidthInTexels;
507     const uint32_t kCopyHeightAtLevel = (kActualHeightAtLevel + kBCBlockHeightInTexels - 1) /
508                                         kBCBlockHeightInTexels * kBCBlockHeightInTexels;
509 
510     config.copyExtent3D = {kCopyWidthAtLevel, kCopyHeightAtLevel, 1};
511 
512     for (dawn::TextureFormat format : kBCFormats) {
513         config.format = format;
514         TestCopyRegionIntoBCFormatTextures(config);
515     }
516 }
517 
518 // Test texture-to-texture whole-size copies with BC formats.
TEST_P(CompressedTextureBCFormatTest,CopyWholeTextureSubResourceIntoNonZeroMipmapLevel)519 TEST_P(CompressedTextureBCFormatTest, CopyWholeTextureSubResourceIntoNonZeroMipmapLevel) {
520     // TODO(cwallez@chromium.org): This consistently fails on with the 12th pixel being opaque black
521     // instead of opaque red on Win10 FYI Release (NVIDIA GeForce GTX 1660). See
522     // https://bugs.chromium.org/p/chromium/issues/detail?id=981393
523     DAWN_SKIP_TEST_IF(IsWindows() && IsVulkan() && IsNvidia());
524 
525     CopyConfig config;
526     config.textureHeightLevel0 = 60;
527     config.textureWidthLevel0 = 60;
528     config.rowPitchAlignment = kTextureRowPitchAlignment;
529 
530     constexpr uint32_t kMipmapLevelCount = 3;
531     config.mipmapLevelCount = kMipmapLevelCount;
532     config.baseMipmapLevel = kMipmapLevelCount - 1;
533 
534     // The actual size of the texture at mipmap level == 2 is not a multiple of 4, paddings are
535     // required in the copies.
536     const dawn::Extent3D kVirtualSize = GetVirtualSizeAtLevel(config);
537     const dawn::Extent3D kPhysicalSize = GetPhysicalSizeAtLevel(config);
538     ASSERT(kVirtualSize.width % kBCBlockWidthInTexels != 0);
539     ASSERT(kVirtualSize.height % kBCBlockHeightInTexels != 0);
540 
541     config.copyExtent3D = kPhysicalSize;
542     for (dawn::TextureFormat format : kBCFormats) {
543         // Create bcTextureSrc as the source texture and initialize it with pre-prepared BC
544         // compressed data.
545         config.format = format;
546         dawn::Texture bcTextureSrc = Create2DTexture(
547             device, config.format, config.textureWidthLevel0, config.textureHeightLevel0,
548             config.arrayLayerCount, config.mipmapLevelCount,
549             dawn::TextureUsageBit::CopySrc | dawn::TextureUsageBit::CopyDst);
550         CopyDataIntoCompressedTexture(bcTextureSrc, config);
551 
552         // Create bcTexture and copy from the content in bcTextureSrc into it.
553         dawn::Texture bcTexture = Create2DTexture(device, config.format, config.textureWidthLevel0,
554                                                   config.textureHeightLevel0,
555                                                   config.arrayLayerCount, config.mipmapLevelCount);
556         dawn::TextureCopyView textureCopyViewSrc = utils::CreateTextureCopyView(
557             bcTextureSrc, config.baseMipmapLevel, config.baseArrayLayer, config.copyOrigin3D);
558         dawn::TextureCopyView textureCopyViewDst = utils::CreateTextureCopyView(
559             bcTexture, config.baseMipmapLevel, config.baseArrayLayer, config.copyOrigin3D);
560         dawn::CommandEncoder encoder = device.CreateCommandEncoder();
561         encoder.CopyTextureToTexture(&textureCopyViewSrc, &textureCopyViewDst,
562                                      &config.copyExtent3D);
563         dawn::CommandBuffer copy = encoder.Finish();
564         queue.Submit(1, &copy);
565 
566         // Verify if we can use bcTexture as sampled textures correctly.
567         dawn::BindGroup bindGroup = CreateBindGroupForTest(
568             bcTexture, config.format, config.baseArrayLayer, config.baseMipmapLevel);
569         dawn::RenderPipeline renderPipeline = CreateRenderPipelineForTest();
570 
571         std::vector<RGBA8> expectedData = GetExpectedData(config.format, kVirtualSize);
572         VerifyCompressedTexturePixelValues(renderPipeline, bindGroup, kVirtualSize,
573                                            config.copyOrigin3D, kVirtualSize, expectedData);
574     }
575 }
576 
577 // Test BC format texture-to-texture partial copies.
TEST_P(CompressedTextureBCFormatTest,CopyPartofTextureSubResourceIntoNonZeroMipmapLevel)578 TEST_P(CompressedTextureBCFormatTest, CopyPartofTextureSubResourceIntoNonZeroMipmapLevel) {
579     // TODO(jiawei.shao@intel.com): add workaround on the T2T copies where Extent3D fits in one
580     // subresource and does not fit in another one on Vulkan. Currently this test causes an error if
581     // Vulkan validation layer is enabled.
582     DAWN_SKIP_TEST_IF(IsVulkan());
583 
584     CopyConfig srcConfig;
585     srcConfig.textureHeightLevel0 = 60;
586     srcConfig.textureWidthLevel0 = 60;
587     srcConfig.rowPitchAlignment = kTextureRowPitchAlignment;
588     srcConfig.mipmapLevelCount = 1;
589     srcConfig.baseMipmapLevel = srcConfig.mipmapLevelCount - 1;
590 
591     const dawn::Extent3D kSrcVirtualSize = GetVirtualSizeAtLevel(srcConfig);
592 
593     CopyConfig dstConfig;
594     dstConfig.textureHeightLevel0 = 60;
595     dstConfig.textureWidthLevel0 = 60;
596     dstConfig.rowPitchAlignment = kTextureRowPitchAlignment;
597 
598     constexpr uint32_t kMipmapLevelCount = 3;
599     dstConfig.mipmapLevelCount = kMipmapLevelCount;
600     dstConfig.baseMipmapLevel = kMipmapLevelCount - 1;
601 
602     // The actual size of the texture at mipmap level == 2 is not a multiple of 4, paddings are
603     // required in the copies.
604     const dawn::Extent3D kDstVirtualSize = GetVirtualSizeAtLevel(dstConfig);
605     ASSERT(kDstVirtualSize.width % kBCBlockWidthInTexels != 0);
606     ASSERT(kDstVirtualSize.height % kBCBlockHeightInTexels != 0);
607 
608     const dawn::Extent3D kDstPhysicalSize = GetPhysicalSizeAtLevel(dstConfig);
609 
610     srcConfig.copyExtent3D = dstConfig.copyExtent3D = kDstPhysicalSize;
611     ASSERT(srcConfig.copyOrigin3D.x + srcConfig.copyExtent3D.width < kSrcVirtualSize.width);
612     ASSERT(srcConfig.copyOrigin3D.y + srcConfig.copyExtent3D.height < kSrcVirtualSize.height);
613 
614     for (dawn::TextureFormat format : kBCFormats) {
615         // Create bcTextureSrc as the source texture and initialize it with pre-prepared BC
616         // compressed data.
617         srcConfig.format = format;
618         dawn::Texture bcTextureSrc = Create2DTexture(
619             device, srcConfig.format, srcConfig.textureWidthLevel0, srcConfig.textureHeightLevel0,
620             srcConfig.arrayLayerCount, srcConfig.mipmapLevelCount,
621             dawn::TextureUsageBit::CopySrc | dawn::TextureUsageBit::CopyDst);
622         CopyDataIntoCompressedTexture(bcTextureSrc, srcConfig);
623         dawn::TextureCopyView textureCopyViewSrc =
624             utils::CreateTextureCopyView(bcTextureSrc, srcConfig.baseMipmapLevel,
625                                          srcConfig.baseArrayLayer, srcConfig.copyOrigin3D);
626 
627         // Create bcTexture and copy from the content in bcTextureSrc into it.
628         dstConfig.format = format;
629         dawn::Texture bcTexture = Create2DTexture(
630             device, dstConfig.format, dstConfig.textureWidthLevel0, dstConfig.textureHeightLevel0,
631             dstConfig.arrayLayerCount, dstConfig.mipmapLevelCount);
632         dawn::TextureCopyView textureCopyViewDst = utils::CreateTextureCopyView(
633             bcTexture, dstConfig.baseMipmapLevel, dstConfig.baseArrayLayer, dstConfig.copyOrigin3D);
634 
635         dawn::CommandEncoder encoder = device.CreateCommandEncoder();
636         encoder.CopyTextureToTexture(&textureCopyViewSrc, &textureCopyViewDst,
637                                      &dstConfig.copyExtent3D);
638         dawn::CommandBuffer copy = encoder.Finish();
639         queue.Submit(1, &copy);
640 
641         // Verify if we can use bcTexture as sampled textures correctly.
642         dawn::BindGroup bindGroup = CreateBindGroupForTest(
643             bcTexture, dstConfig.format, dstConfig.baseArrayLayer, dstConfig.baseMipmapLevel);
644         dawn::RenderPipeline renderPipeline = CreateRenderPipelineForTest();
645 
646         std::vector<RGBA8> expectedData = GetExpectedData(dstConfig.format, kDstVirtualSize);
647         VerifyCompressedTexturePixelValues(renderPipeline, bindGroup, kDstVirtualSize,
648                                            dstConfig.copyOrigin3D, kDstVirtualSize, expectedData);
649     }
650 }
651 
652 // Test the special case of the B2T copies on the D3D12 backend that the buffer offset and texture
653 // extent exactly fit the RowPitch.
TEST_P(CompressedTextureBCFormatTest,BufferOffsetAndExtentFitRowPitch)654 TEST_P(CompressedTextureBCFormatTest, BufferOffsetAndExtentFitRowPitch) {
655     CopyConfig config;
656     config.textureWidthLevel0 = 8;
657     config.textureHeightLevel0 = 8;
658     config.rowPitchAlignment = kTextureRowPitchAlignment;
659     config.copyExtent3D = {config.textureWidthLevel0, config.textureHeightLevel0, 1};
660 
661     const uint32_t blockCountPerRow = config.textureWidthLevel0 / kBCBlockWidthInTexels;
662 
663     for (dawn::TextureFormat format : kBCFormats) {
664         config.format = format;
665 
666         const uint32_t blockSizeInBytes = CompressedFormatBlockSizeInBytes(format);
667         const uint32_t blockCountPerRowPitch = config.rowPitchAlignment / blockSizeInBytes;
668 
669         config.bufferOffset = (blockCountPerRowPitch - blockCountPerRow) * blockSizeInBytes;
670 
671         TestCopyRegionIntoBCFormatTextures(config);
672     }
673 }
674 
675 // Test the special case of the B2T copies on the D3D12 backend that the buffer offset exceeds the
676 // slice pitch (slicePitch = rowPitch * (imageHeightInTexels / blockHeightInTexels)). On D3D12
677 // backend the texelOffset.y will be greater than 0 after calcuting the texelOffset in the function
678 // ComputeTexelOffsets().
TEST_P(CompressedTextureBCFormatTest,BufferOffsetExceedsSlicePitch)679 TEST_P(CompressedTextureBCFormatTest, BufferOffsetExceedsSlicePitch) {
680     CopyConfig config;
681     config.textureWidthLevel0 = 8;
682     config.textureHeightLevel0 = 8;
683     config.rowPitchAlignment = kTextureRowPitchAlignment;
684     config.copyExtent3D = {config.textureWidthLevel0, config.textureHeightLevel0, 1};
685 
686     const uint32_t blockCountPerRow = config.textureWidthLevel0 / kBCBlockWidthInTexels;
687     const uint32_t slicePitchInBytes =
688         config.rowPitchAlignment * (config.textureHeightLevel0 / kBCBlockHeightInTexels);
689 
690     for (dawn::TextureFormat format : kBCFormats) {
691         config.format = format;
692 
693         const uint32_t blockSizeInBytes = CompressedFormatBlockSizeInBytes(format);
694         const uint32_t blockCountPerRowPitch = config.rowPitchAlignment / blockSizeInBytes;
695 
696         config.bufferOffset = (blockCountPerRowPitch - blockCountPerRow) * blockSizeInBytes +
697                               config.rowPitchAlignment + slicePitchInBytes;
698 
699         TestCopyRegionIntoBCFormatTextures(config);
700     }
701 }
702 
703 // Test the special case of the B2T copies on the D3D12 backend that the buffer offset and texture
704 // extent exceed the RowPitch. On D3D12 backend two copies are required for this case.
TEST_P(CompressedTextureBCFormatTest,CopyWithBufferOffsetAndExtentExceedRowPitch)705 TEST_P(CompressedTextureBCFormatTest, CopyWithBufferOffsetAndExtentExceedRowPitch) {
706     CopyConfig config;
707     config.textureWidthLevel0 = 8;
708     config.textureHeightLevel0 = 8;
709     config.rowPitchAlignment = kTextureRowPitchAlignment;
710     config.copyExtent3D = {config.textureWidthLevel0, config.textureHeightLevel0, 1};
711 
712     const uint32_t blockCountPerRow = config.textureWidthLevel0 / kBCBlockWidthInTexels;
713 
714     constexpr uint32_t kExceedRowBlockCount = 1;
715 
716     for (dawn::TextureFormat format : kBCFormats) {
717         config.format = format;
718 
719         const uint32_t blockSizeInBytes = CompressedFormatBlockSizeInBytes(format);
720         const uint32_t blockCountPerRowPitch = config.rowPitchAlignment / blockSizeInBytes;
721         config.bufferOffset =
722             (blockCountPerRowPitch - blockCountPerRow + kExceedRowBlockCount) * blockSizeInBytes;
723 
724         TestCopyRegionIntoBCFormatTextures(config);
725     }
726 }
727 
728 // Test the special case of the B2T copies on the D3D12 backend that the slicePitch is equal to the
729 // rowPitch. On D3D12 backend the texelOffset.z will be greater than 0 after calcuting the
730 // texelOffset in the function ComputeTexelOffsets().
TEST_P(CompressedTextureBCFormatTest,RowPitchEqualToSlicePitch)731 TEST_P(CompressedTextureBCFormatTest, RowPitchEqualToSlicePitch) {
732     CopyConfig config;
733     config.textureWidthLevel0 = 8;
734     config.textureHeightLevel0 = kBCBlockHeightInTexels;
735     config.rowPitchAlignment = kTextureRowPitchAlignment;
736     config.copyExtent3D = {config.textureWidthLevel0, config.textureHeightLevel0, 1};
737 
738     const uint32_t blockCountPerRow = config.textureWidthLevel0 / kBCBlockWidthInTexels;
739     const uint32_t slicePitchInBytes = config.rowPitchAlignment;
740 
741     for (dawn::TextureFormat format : kBCFormats) {
742         config.format = format;
743 
744         const uint32_t blockSizeInBytes = CompressedFormatBlockSizeInBytes(format);
745         const uint32_t blockCountPerRowPitch = config.rowPitchAlignment / blockSizeInBytes;
746 
747         config.bufferOffset =
748             (blockCountPerRowPitch - blockCountPerRow) * blockSizeInBytes + slicePitchInBytes;
749 
750         TestCopyRegionIntoBCFormatTextures(config);
751     }
752 }
753 
754 // Test the workaround in the B2T copies when (bufferSize - bufferOffset < bytesPerImage *
755 // copyExtent.depth) on Metal backends. As copyExtent.depth can only be 1 for BC formats, on Metal
756 // backend we will use two copies to implement such copy.
TEST_P(CompressedTextureBCFormatTest,LargeImageHeight)757 TEST_P(CompressedTextureBCFormatTest, LargeImageHeight) {
758     CopyConfig config;
759     config.textureWidthLevel0 = 8;
760     config.textureHeightLevel0 = 8;
761     config.copyExtent3D = {config.textureWidthLevel0, config.textureHeightLevel0, 1};
762 
763     config.imageHeight = config.textureHeightLevel0 * 2;
764 
765     for (dawn::TextureFormat format : kBCFormats) {
766         config.format = format;
767         TestCopyRegionIntoBCFormatTextures(config);
768     }
769 }
770 
771 // Test the workaround in the B2T copies when (bufferSize - bufferOffset < bytesPerImage *
772 // copyExtent.depth) and copyExtent needs to be clamped.
TEST_P(CompressedTextureBCFormatTest,LargeImageHeightAndClampedCopyExtent)773 TEST_P(CompressedTextureBCFormatTest, LargeImageHeightAndClampedCopyExtent) {
774     CopyConfig config;
775     config.textureHeightLevel0 = 56;
776     config.textureWidthLevel0 = 56;
777     config.rowPitchAlignment = kTextureRowPitchAlignment;
778 
779     constexpr uint32_t kMipmapLevelCount = 3;
780     config.mipmapLevelCount = kMipmapLevelCount;
781     config.baseMipmapLevel = kMipmapLevelCount - 1;
782 
783     // The actual size of the texture at mipmap level == 2 is not a multiple of 4, paddings are
784     // required in the copies.
785     const uint32_t kActualWidthAtLevel = config.textureWidthLevel0 >> config.baseMipmapLevel;
786     const uint32_t kActualHeightAtLevel = config.textureHeightLevel0 >> config.baseMipmapLevel;
787     ASSERT(kActualWidthAtLevel % kBCBlockWidthInTexels != 0);
788     ASSERT(kActualHeightAtLevel % kBCBlockHeightInTexels != 0);
789 
790     const uint32_t kCopyWidthAtLevel = (kActualWidthAtLevel + kBCBlockWidthInTexels - 1) /
791                                        kBCBlockWidthInTexels * kBCBlockWidthInTexels;
792     const uint32_t kCopyHeightAtLevel = (kActualHeightAtLevel + kBCBlockHeightInTexels - 1) /
793                                         kBCBlockHeightInTexels * kBCBlockHeightInTexels;
794 
795     config.copyExtent3D = {kCopyWidthAtLevel, kCopyHeightAtLevel, 1};
796 
797     config.imageHeight = kCopyHeightAtLevel * 2;
798 
799     for (dawn::TextureFormat format : kBCFormats) {
800         config.format = format;
801         TestCopyRegionIntoBCFormatTextures(config);
802     }
803 }
804 
805 // TODO(jiawei.shao@intel.com): support BC formats on OpenGL backend
806 DAWN_INSTANTIATE_TEST(CompressedTextureBCFormatTest, D3D12Backend, MetalBackend, VulkanBackend);
807