| // Copyright 2019 The Dawn & Tint Authors | |
| // | |
| // Redistribution and use in source and binary forms, with or without | |
| // modification, are permitted provided that the following conditions are met: | |
| // | |
| // 1. Redistributions of source code must retain the above copyright notice, this | |
| // list of conditions and the following disclaimer. | |
| // | |
| // 2. Redistributions in binary form must reproduce the above copyright notice, | |
| // this list of conditions and the following disclaimer in the documentation | |
| // and/or other materials provided with the distribution. | |
| // | |
| // 3. Neither the name of the copyright holder nor the names of its | |
| // contributors may be used to endorse or promote products derived from | |
| // this software without specific prior written permission. | |
| // | |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
| // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
| // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | |
| // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
| // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |
| // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |
| // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| #include <vector> | |
| #include "src/dawn/common/Constants.h" | |
| #include "src/dawn/common/Math.h" | |
| #include "src/dawn/tests/DawnTest.h" | |
| #include "src/dawn/utils/ComboRenderPipelineDescriptor.h" | |
| #include "src/dawn/utils/TestUtils.h" | |
| #include "src/dawn/utils/TextureUtils.h" | |
| #include "src/dawn/utils/WGPUHelpers.h" | |
| #include "src/utils/assert.h" | |
| #include "src/utils/compiler.h" | |
| namespace dawn { | |
| namespace { | |
| // The helper struct to configure the copies between buffers and textures. | |
| struct CopyConfig { | |
| wgpu::TextureDescriptor textureDescriptor; | |
| wgpu::Extent3D copyExtent3D; | |
| wgpu::Origin3D copyOrigin3D = {0, 0, 0}; | |
| uint32_t viewMipmapLevel = 0; | |
| uint32_t bufferOffset = 0; | |
| uint32_t bytesPerRowAlignment = kTextureBytesPerRowAlignment; | |
| uint32_t rowsPerImage = wgpu::kCopyStrideUndefined; | |
| }; | |
| using TextureFormat = wgpu::TextureFormat; | |
| DAWN_TEST_PARAM_STRUCT(CompressedTextureFormatTestParams, TextureFormat); | |
| class CompressedTextureFormatTest : public DawnTestWithParams<CompressedTextureFormatTestParams> { | |
| protected: | |
| void SetUp() override { | |
| DawnTestWithParams<CompressedTextureFormatTestParams>::SetUp(); | |
| // TODO(crbug.com/518857263): Produces incorrect result on Pixel 10. | |
| DAWN_SUPPRESS_TEST_IF(IsAndroid() && IsImgTec() && IsVulkan()); | |
| } | |
| std::vector<wgpu::FeatureName> GetRequiredFeatures() override { | |
| const wgpu::TextureFormat format = GetParam().mTextureFormat; | |
| if (utils::IsBCTextureFormat(format) && | |
| SupportsFeatures({wgpu::FeatureName::TextureCompressionBC})) { | |
| mIsFormatSupported = true; | |
| return {wgpu::FeatureName::TextureCompressionBC}; | |
| } | |
| if (utils::IsETC2TextureFormat(format) && | |
| SupportsFeatures({wgpu::FeatureName::TextureCompressionETC2})) { | |
| mIsFormatSupported = true; | |
| return {wgpu::FeatureName::TextureCompressionETC2}; | |
| } | |
| if (utils::IsASTCTextureFormat(format) && | |
| SupportsFeatures({wgpu::FeatureName::TextureCompressionASTC})) { | |
| mIsFormatSupported = true; | |
| return {wgpu::FeatureName::TextureCompressionASTC}; | |
| } | |
| return {}; | |
| } | |
| bool IsFormatSupported() const { return mIsFormatSupported; } | |
| uint32_t BlockWidthInTexels() const { | |
| DAWN_ASSERT(IsFormatSupported()); | |
| return utils::GetTextureFormatBlockWidth(GetParam().mTextureFormat); | |
| } | |
| uint32_t BlockHeightInTexels() const { | |
| DAWN_ASSERT(IsFormatSupported()); | |
| return utils::GetTextureFormatBlockHeight(GetParam().mTextureFormat); | |
| } | |
| // Compute the upload data for the copyConfig. | |
| std::vector<uint8_t> UploadData(const CopyConfig& copyConfig) { | |
| uint32_t copyWidthInBlock = copyConfig.copyExtent3D.width / BlockWidthInTexels(); | |
| uint32_t copyHeightInBlock = copyConfig.copyExtent3D.height / BlockHeightInTexels(); | |
| uint32_t copyBytesPerRow = 0; | |
| if (copyConfig.bytesPerRowAlignment != 0) { | |
| copyBytesPerRow = copyConfig.bytesPerRowAlignment; | |
| } else { | |
| copyBytesPerRow = copyWidthInBlock * | |
| utils::GetTexelBlockSizeInBytes(copyConfig.textureDescriptor.format); | |
| } | |
| uint32_t copyRowsPerImage = copyConfig.rowsPerImage; | |
| if (copyRowsPerImage == wgpu::kCopyStrideUndefined) { | |
| copyRowsPerImage = copyHeightInBlock; | |
| } | |
| uint32_t copyBytesPerImage = copyBytesPerRow * copyRowsPerImage; | |
| uint32_t uploadBufferSize = copyConfig.bufferOffset + | |
| copyBytesPerImage * copyConfig.copyExtent3D.depthOrArrayLayers; | |
| // Fill data with the pre-prepared one-block compressed texture data. | |
| std::vector<uint8_t> data(uploadBufferSize, 0); | |
| std::vector<uint8_t> oneBlockCompressedTextureData = GetOneBlockFormatTextureData(); | |
| for (uint32_t layer = 0; layer < copyConfig.copyExtent3D.depthOrArrayLayers; ++layer) { | |
| for (uint32_t h = 0; h < copyHeightInBlock; ++h) { | |
| for (uint32_t w = 0; w < copyWidthInBlock; ++w) { | |
| uint32_t uploadBufferOffset = copyConfig.bufferOffset + | |
| copyBytesPerImage * layer + copyBytesPerRow * h + | |
| oneBlockCompressedTextureData.size() * w; | |
| DAWN_UNSAFE_TODO( | |
| std::memcpy(&data[uploadBufferOffset], oneBlockCompressedTextureData.data(), | |
| oneBlockCompressedTextureData.size() * sizeof(uint8_t))); | |
| } | |
| } | |
| } | |
| return data; | |
| } | |
| // Copy the compressed texture data into the destination texture as is specified in | |
| // copyConfig. | |
| void InitializeDataInCompressedTexture(wgpu::Texture compressedTexture, | |
| const CopyConfig& copyConfig) { | |
| DAWN_ASSERT(IsFormatSupported()); | |
| std::vector<uint8_t> data = UploadData(copyConfig); | |
| // Copy texture data from a staging buffer to the destination texture. | |
| wgpu::Buffer stagingBuffer = utils::CreateBufferFromData(device, data.data(), data.size(), | |
| wgpu::BufferUsage::CopySrc); | |
| wgpu::TexelCopyBufferInfo texelCopyBufferInfo = utils::CreateTexelCopyBufferInfo( | |
| stagingBuffer, copyConfig.bufferOffset, copyConfig.bytesPerRowAlignment, | |
| copyConfig.rowsPerImage); | |
| wgpu::TexelCopyTextureInfo texelCopyTextureInfo = utils::CreateTexelCopyTextureInfo( | |
| compressedTexture, copyConfig.viewMipmapLevel, copyConfig.copyOrigin3D); | |
| wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); | |
| encoder.CopyBufferToTexture(&texelCopyBufferInfo, &texelCopyTextureInfo, | |
| ©Config.copyExtent3D); | |
| wgpu::CommandBuffer copy = encoder.Finish(); | |
| queue.Submit(1, ©); | |
| } | |
| // Create the bind group that includes a texture and a sampler. | |
| wgpu::BindGroup CreateBindGroupForTest(wgpu::BindGroupLayout bindGroupLayout, | |
| wgpu::Texture compressedTexture, | |
| uint32_t baseArrayLayer = 0, | |
| uint32_t baseMipLevel = 0) { | |
| DAWN_ASSERT(IsFormatSupported()); | |
| wgpu::SamplerDescriptor samplerDesc; | |
| samplerDesc.minFilter = wgpu::FilterMode::Nearest; | |
| samplerDesc.magFilter = wgpu::FilterMode::Nearest; | |
| wgpu::Sampler sampler = device.CreateSampler(&samplerDesc); | |
| wgpu::TextureViewDescriptor textureViewDescriptor; | |
| textureViewDescriptor.format = GetParam().mTextureFormat; | |
| textureViewDescriptor.dimension = wgpu::TextureViewDimension::e2D; | |
| textureViewDescriptor.baseMipLevel = baseMipLevel; | |
| textureViewDescriptor.baseArrayLayer = baseArrayLayer; | |
| textureViewDescriptor.arrayLayerCount = 1; | |
| textureViewDescriptor.mipLevelCount = 1; | |
| wgpu::TextureView textureView = compressedTexture.CreateView(&textureViewDescriptor); | |
| return utils::MakeBindGroup(device, bindGroupLayout, {{0, sampler}, {1, textureView}}); | |
| } | |
| // Create a render pipeline for sampling from a texture 2d and rendering into the render target. | |
| wgpu::RenderPipeline CreateRenderPipelineForTestTex2D() { | |
| DAWN_ASSERT(IsFormatSupported()); | |
| utils::ComboRenderPipelineDescriptor renderPipelineDescriptor; | |
| wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"( | |
| struct VertexOut { | |
| @location(0) texCoord : vec2 <f32>, | |
| @builtin(position) position : vec4f, | |
| } | |
| @vertex | |
| fn main(@builtin(vertex_index) VertexIndex : u32) -> VertexOut { | |
| var pos = array( | |
| vec2f(-3.0, 1.0), | |
| vec2f( 3.0, 1.0), | |
| vec2f( 0.0, -2.0) | |
| ); | |
| var output : VertexOut; | |
| output.position = vec4f(pos[VertexIndex], 0.0, 1.0); | |
| output.texCoord = vec2f(output.position.x / 2.0, -output.position.y / 2.0) + vec2f(0.5, 0.5); | |
| return output; | |
| })"); | |
| wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"( | |
| @group(0) @binding(0) var sampler0 : sampler; | |
| @group(0) @binding(1) var texture0 : texture_2d<f32>; | |
| @fragment | |
| fn main(@location(0) texCoord : vec2f) -> @location(0) vec4f { | |
| return textureSample(texture0, sampler0, texCoord); | |
| })"); | |
| renderPipelineDescriptor.vertex.module = vsModule; | |
| renderPipelineDescriptor.cFragment.module = fsModule; | |
| renderPipelineDescriptor.cTargets[0].format = utils::BasicRenderPass::kDefaultColorFormat; | |
| return device.CreateRenderPipeline(&renderPipelineDescriptor); | |
| } | |
| // Create a render pipeline for sampling from a texture cube and rendering into the render | |
| // target. Used for compatibility mode as cube texture cannot be bound as texture_2d_array. | |
| wgpu::RenderPipeline CreateRenderPipelineForTestCube() { | |
| utils::ComboRenderPipelineDescriptor renderPipelineDescriptor; | |
| wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"( | |
| struct VertexOut { | |
| @location(0) texCoord : vec2 <f32>, | |
| @builtin(position) position : vec4f, | |
| } | |
| @vertex | |
| fn main(@builtin(vertex_index) VertexIndex : u32) -> VertexOut { | |
| var pos = array( | |
| vec2f(-3.0, 1.0), | |
| vec2f( 3.0, 1.0), | |
| vec2f( 0.0, -2.0) | |
| ); | |
| var output : VertexOut; | |
| output.position = vec4f(pos[VertexIndex], 0.0, 1.0); | |
| output.texCoord = vec2f(output.position.x / 2.0, -output.position.y / 2.0) + vec2f(0.5, 0.5); | |
| return output; | |
| })"); | |
| wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"( | |
| @group(0) @binding(0) var sampler0 : sampler; | |
| @group(0) @binding(1) var texture0 : texture_cube<f32>; | |
| @group(0) @binding(2) var<uniform> layer : u32; | |
| @fragment | |
| fn main(@location(0) texCoord : vec2f) -> @location(0) vec4f { | |
| var st: vec2f = texCoord; | |
| st.y = 1. - st.y; | |
| st = st * 2. - 1.; | |
| var coords: vec3f; | |
| switch(layer) { | |
| case 0u: { coords = vec3f(1., st.y, -st.x); } // Positive X | |
| case 1u: { coords = vec3f(-1., st.y, st.x); } // Negative X | |
| case 2u: { coords = vec3f(st.x, 1., -st.y); } // Positive Y | |
| case 3u: { coords = vec3f(st.x, -1., st.y); } // Negative Y | |
| case 4u: { coords = vec3f(st.x, st.y, 1.); } // Positive Z | |
| case 5u: { coords = vec3f(-st.x, st.y, -1.);} // Negative Z | |
| default: { return vec4f(0.); } // Unreachable | |
| } | |
| return textureSample(texture0, sampler0, coords); | |
| })"); | |
| renderPipelineDescriptor.vertex.module = vsModule; | |
| renderPipelineDescriptor.cFragment.module = fsModule; | |
| renderPipelineDescriptor.cTargets[0].format = utils::BasicRenderPass::kDefaultColorFormat; | |
| return device.CreateRenderPipeline(&renderPipelineDescriptor); | |
| } | |
| // Run the given render pipeline and bind group and verify the pixels in the render target. | |
| void VerifyCompressedTexturePixelValues(wgpu::RenderPipeline renderPipeline, | |
| wgpu::BindGroup bindGroup, | |
| const wgpu::Extent3D& renderTargetSize, | |
| const wgpu::Origin3D& expectedOrigin, | |
| const wgpu::Extent3D& expectedExtent, | |
| const std::vector<utils::RGBA8>& expected) { | |
| DAWN_ASSERT(IsFormatSupported()); | |
| utils::BasicRenderPass renderPass = | |
| utils::CreateBasicRenderPass(device, renderTargetSize.width, renderTargetSize.height); | |
| wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); | |
| { | |
| wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); | |
| pass.SetPipeline(renderPipeline); | |
| pass.SetBindGroup(0, bindGroup); | |
| pass.Draw(6); | |
| pass.End(); | |
| } | |
| wgpu::CommandBuffer commands = encoder.Finish(); | |
| queue.Submit(1, &commands); | |
| // Some mobile chipsets decode the compressed texture values with a lower precision, leading | |
| // to color channels that are off by one from the expected result. This check is given a | |
| // little bit of tolerance to account for it. See dawn:1562. | |
| EXPECT_TEXTURE_EQ(expected.data(), renderPass.color, {expectedOrigin.x, expectedOrigin.y}, | |
| {expectedExtent.width, expectedExtent.height}, | |
| /* level */ 0, /* aspect */ wgpu::TextureAspect::All, | |
| /* bytesPerRow */ 0, /* Tolerance */ utils::RGBA8(1, 1, 1, 1)); | |
| } | |
| // Run the tests that copies pre-prepared format data into a texture and verifies if we can | |
| // render correctly with the pixel values sampled from the texture. | |
| void TestCopyRegionIntoFormatTextures(const CopyConfig& config) { | |
| DAWN_ASSERT(IsFormatSupported()); | |
| wgpu::Texture texture = CreateTextureWithCompressedData(config); | |
| VerifyTexture(config, texture); | |
| } | |
| void VerifyTexture(const CopyConfig& config, wgpu::Texture texture) { | |
| wgpu::RenderPipeline renderPipeline = CreateRenderPipelineForTestTex2D(); | |
| wgpu::Extent3D virtualSizeAtLevel = GetVirtualSizeAtLevel(config); | |
| // The copy region may exceed the subresource size because of the required paddings, so we | |
| // should limit the size of the expectedData to make it match the real size of the render | |
| // target. | |
| wgpu::Extent3D noPaddingExtent3D = config.copyExtent3D; | |
| if (config.copyOrigin3D.x + config.copyExtent3D.width > virtualSizeAtLevel.width) { | |
| noPaddingExtent3D.width = virtualSizeAtLevel.width - config.copyOrigin3D.x; | |
| } | |
| if (config.copyOrigin3D.y + config.copyExtent3D.height > virtualSizeAtLevel.height) { | |
| noPaddingExtent3D.height = virtualSizeAtLevel.height - config.copyOrigin3D.y; | |
| } | |
| noPaddingExtent3D.depthOrArrayLayers = 1u; | |
| std::vector<utils::RGBA8> expectedData = GetExpectedData(noPaddingExtent3D); | |
| wgpu::Origin3D firstLayerCopyOrigin = {config.copyOrigin3D.x, config.copyOrigin3D.y, 0}; | |
| for (uint32_t layer = config.copyOrigin3D.z; | |
| layer < config.copyOrigin3D.z + config.copyExtent3D.depthOrArrayLayers; ++layer) { | |
| wgpu::BindGroup bindGroup = CreateBindGroupForTest( | |
| renderPipeline.GetBindGroupLayout(0), texture, layer, config.viewMipmapLevel); | |
| VerifyCompressedTexturePixelValues(renderPipeline, bindGroup, virtualSizeAtLevel, | |
| firstLayerCopyOrigin, noPaddingExtent3D, | |
| expectedData); | |
| } | |
| } | |
| // Create a texture and initialize it with the pre-prepared compressed texture data. | |
| wgpu::Texture CreateTextureWithCompressedData(CopyConfig config) { | |
| wgpu::Texture texture = device.CreateTexture(&config.textureDescriptor); | |
| InitializeDataInCompressedTexture(texture, config); | |
| return texture; | |
| } | |
| // Record a texture-to-texture copy command into command encoder without finishing the | |
| // encoding. | |
| void RecordTextureToTextureCopy(wgpu::CommandEncoder encoder, | |
| wgpu::Texture srcTexture, | |
| wgpu::Texture dstTexture, | |
| CopyConfig srcConfig, | |
| CopyConfig dstConfig) { | |
| wgpu::TexelCopyTextureInfo texelCopyTextureInfoSrc = utils::CreateTexelCopyTextureInfo( | |
| srcTexture, srcConfig.viewMipmapLevel, srcConfig.copyOrigin3D); | |
| wgpu::TexelCopyTextureInfo texelCopyTextureInfoDst = utils::CreateTexelCopyTextureInfo( | |
| dstTexture, dstConfig.viewMipmapLevel, dstConfig.copyOrigin3D); | |
| encoder.CopyTextureToTexture(&texelCopyTextureInfoSrc, &texelCopyTextureInfoDst, | |
| &dstConfig.copyExtent3D); | |
| } | |
| wgpu::Texture CreateTextureFromTexture(wgpu::Texture srcTexture, | |
| CopyConfig srcConfig, | |
| CopyConfig dstConfig) { | |
| wgpu::Texture dstTexture = device.CreateTexture(&dstConfig.textureDescriptor); | |
| wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); | |
| RecordTextureToTextureCopy(encoder, srcTexture, dstTexture, srcConfig, dstConfig); | |
| wgpu::CommandBuffer copy = encoder.Finish(); | |
| queue.Submit(1, ©); | |
| return dstTexture; | |
| } | |
| // Return the pre-prepared one-block texture data. | |
| std::vector<uint8_t> GetOneBlockFormatTextureData() { | |
| switch (GetParam().mTextureFormat) { | |
| // The expected data represents 4x4 pixel images with the left side dark red and the | |
| // right side dark green. We specify the same compressed data in both sRGB and | |
| // non-sRGB tests, but the rendering result should be different because for sRGB | |
| // formats, the red, green, and blue components are converted from an sRGB color | |
| // space to a linear color space as part of filtering. | |
| case wgpu::TextureFormat::BC1RGBAUnorm: | |
| case wgpu::TextureFormat::BC1RGBAUnormSrgb: | |
| return {0x0, 0xC0, 0x60, 0x6, 0x50, 0x50, 0x50, 0x50}; | |
| case wgpu::TextureFormat::BC7RGBAUnorm: | |
| case wgpu::TextureFormat::BC7RGBAUnormSrgb: | |
| return {0x50, 0x18, 0xfc, 0xf, 0x0, 0x30, 0xe3, 0xe1, | |
| 0xe1, 0xe1, 0xc1, 0xf, 0xfc, 0xc0, 0xf, 0xfc}; | |
| // The expected data represents 4x4 pixel images with the left side dark red and the | |
| // right side dark green. The pixels in the left side of the block all have an alpha | |
| // value equal to 0x88. We specify the same compressed data in both sRGB and | |
| // non-sRGB tests, but the rendering result should be different because for sRGB | |
| // formats, the red, green, and blue components are converted from an sRGB color | |
| // space to a linear color space as part of filtering, and any alpha component is | |
| // left unchanged. | |
| case wgpu::TextureFormat::BC2RGBAUnorm: | |
| case wgpu::TextureFormat::BC2RGBAUnormSrgb: | |
| return {0x88, 0xFF, 0x88, 0xFF, 0x88, 0xFF, 0x88, 0xFF, | |
| 0x0, 0xC0, 0x60, 0x6, 0x50, 0x50, 0x50, 0x50}; | |
| case wgpu::TextureFormat::BC3RGBAUnorm: | |
| case wgpu::TextureFormat::BC3RGBAUnormSrgb: | |
| return {0x88, 0xFF, 0x40, 0x2, 0x24, 0x40, 0x2, 0x24, | |
| 0x0, 0xC0, 0x60, 0x6, 0x50, 0x50, 0x50, 0x50}; | |
| // The expected data represents 4x4 pixel images with the left side red and the | |
| // right side black. | |
| case wgpu::TextureFormat::BC4RSnorm: | |
| return {0x7F, 0x0, 0x40, 0x2, 0x24, 0x40, 0x2, 0x24}; | |
| case wgpu::TextureFormat::BC4RUnorm: | |
| return {0xFF, 0x0, 0x40, 0x2, 0x24, 0x40, 0x2, 0x24}; | |
| // The expected data represents 4x4 pixel images with the left side red and the | |
| // right side green and was encoded with DirectXTex from Microsoft. | |
| case wgpu::TextureFormat::BC5RGSnorm: | |
| return {0x7f, 0x81, 0x40, 0x2, 0x24, 0x40, 0x2, 0x24, | |
| 0x7f, 0x81, 0x9, 0x90, 0x0, 0x9, 0x90, 0x0}; | |
| case wgpu::TextureFormat::BC5RGUnorm: | |
| return {0xff, 0x0, 0x40, 0x2, 0x24, 0x40, 0x2, 0x24, | |
| 0xff, 0x0, 0x9, 0x90, 0x0, 0x9, 0x90, 0x0}; | |
| case wgpu::TextureFormat::BC6HRGBFloat: | |
| return {0xe3, 0x1f, 0x0, 0x0, 0x0, 0xe0, 0x1f, 0x0, | |
| 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff}; | |
| case wgpu::TextureFormat::BC6HRGBUfloat: | |
| return {0xe3, 0x3d, 0x0, 0x0, 0x0, 0xe0, 0x3d, 0x0, | |
| 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff}; | |
| // The expected data represents 4x4 pixel images with the left side dark red and the | |
| // right side dark green. We specify the same compressed data in both sRGB and | |
| // non-sRGB tests, but the rendering result should be different because for sRGB | |
| // formats, the red, green, and blue components are converted from an sRGB color | |
| // space to a linear color space as part of filtering. | |
| case wgpu::TextureFormat::ETC2RGB8Unorm: | |
| case wgpu::TextureFormat::ETC2RGB8UnormSrgb: | |
| case wgpu::TextureFormat::ETC2RGB8A1Unorm: | |
| case wgpu::TextureFormat::ETC2RGB8A1UnormSrgb: | |
| return {0x4, 0xc0, 0xc0, 0x2, 0x0, 0xff, 0x0, 0x0}; | |
| // The expected data represents 4x4 pixel images with the left side dark red and the | |
| // right side dark green. The pixels in the left side of the block all have an alpha | |
| // value equal to 0x88. We specify the same compressed data in both sRGB and | |
| // non-sRGB tests, but the rendering result should be different because for sRGB | |
| // formats, the red, green, and blue components are converted from an sRGB color | |
| // space to a linear color space as part of filtering, and any alpha component is | |
| // left unchanged. | |
| case wgpu::TextureFormat::ETC2RGBA8Unorm: | |
| case wgpu::TextureFormat::ETC2RGBA8UnormSrgb: | |
| return {0xc0, 0x78, 0x49, 0x24, 0x92, 0xff, 0xff, 0xff, | |
| 0x4, 0xc0, 0xc0, 0x2, 0x0, 0xff, 0x0, 0x0}; | |
| // The expected data represents 4x4 pixel image with the left side red and the right | |
| // side black. | |
| case wgpu::TextureFormat::EACR11Unorm: | |
| return {0x84, 0x90, 0xff, 0xff, 0xff, 0x6d, 0xb6, 0xdb}; | |
| case wgpu::TextureFormat::EACR11Snorm: | |
| return {0x2, 0x90, 0xff, 0xff, 0xff, 0x6d, 0xb6, 0xdb}; | |
| // The expected data represents 4x4 pixel image with the left side red and the right | |
| // side green. | |
| case wgpu::TextureFormat::EACRG11Unorm: | |
| return {0x84, 0x90, 0xff, 0xff, 0xff, 0x6d, 0xb6, 0xdb, | |
| 0x84, 0x90, 0x6d, 0xb6, 0xdb, 0xff, 0xff, 0xff}; | |
| case wgpu::TextureFormat::EACRG11Snorm: | |
| return {0x2, 0x90, 0xff, 0xff, 0xff, 0x6d, 0xb6, 0xdb, | |
| 0x2, 0x90, 0x6d, 0xb6, 0xdb, 0xff, 0xff, 0xff}; | |
| // The expected data is a texel block of the corresponding size where the left width / 2 | |
| // pixels are dark red with an alpha of 0x80 and the remaining (width - width / 2) | |
| // pixels are dark green. We specify the same compressed data in both sRGB and non-sRGB | |
| // tests, but the rendering result should be different because for sRGB formats, the | |
| // red, green, and blue components are converted from an sRGB color space to a linear | |
| // color space as part of filtering, and any alpha component is left unchanged. | |
| case wgpu::TextureFormat::ASTC4x4Unorm: | |
| case wgpu::TextureFormat::ASTC4x4UnormSrgb: | |
| return {0x13, 0x80, 0xe9, 0x1, 0x0, 0xe8, 0x1, 0x0, | |
| 0x0, 0xff, 0x1, 0x0, 0x0, 0x3f, 0xf0, 0x3}; | |
| case wgpu::TextureFormat::ASTC5x4Unorm: | |
| case wgpu::TextureFormat::ASTC5x4UnormSrgb: | |
| case wgpu::TextureFormat::ASTC5x5Unorm: | |
| case wgpu::TextureFormat::ASTC5x5UnormSrgb: | |
| return {0x83, 0x80, 0xe9, 0x1, 0x0, 0xe8, 0x1, 0x0, | |
| 0x0, 0xff, 0x1, 0x0, 0x80, 0x14, 0x90, 0x2}; | |
| case wgpu::TextureFormat::ASTC6x5Unorm: | |
| case wgpu::TextureFormat::ASTC6x5UnormSrgb: | |
| case wgpu::TextureFormat::ASTC6x6Unorm: | |
| case wgpu::TextureFormat::ASTC6x6UnormSrgb: | |
| return {0x2, 0x81, 0xe9, 0x1, 0x0, 0xe8, 0x1, 0x0, | |
| 0x0, 0xff, 0x1, 0x0, 0x0, 0x3f, 0xf0, 0x3}; | |
| case wgpu::TextureFormat::ASTC8x5Unorm: | |
| case wgpu::TextureFormat::ASTC8x5UnormSrgb: | |
| case wgpu::TextureFormat::ASTC8x6Unorm: | |
| case wgpu::TextureFormat::ASTC8x6UnormSrgb: | |
| case wgpu::TextureFormat::ASTC8x8Unorm: | |
| case wgpu::TextureFormat::ASTC8x8UnormSrgb: | |
| return {0x6, 0x80, 0xe9, 0x1, 0x0, 0xe8, 0x1, 0x0, | |
| 0x0, 0xff, 0x1, 0x0, 0xff, 0x0, 0xff, 0x0}; | |
| case wgpu::TextureFormat::ASTC10x5Unorm: | |
| case wgpu::TextureFormat::ASTC10x5UnormSrgb: | |
| case wgpu::TextureFormat::ASTC10x6Unorm: | |
| case wgpu::TextureFormat::ASTC10x6UnormSrgb: | |
| case wgpu::TextureFormat::ASTC10x8Unorm: | |
| case wgpu::TextureFormat::ASTC10x8UnormSrgb: | |
| case wgpu::TextureFormat::ASTC10x10Unorm: | |
| case wgpu::TextureFormat::ASTC10x10UnormSrgb: | |
| return {0x6, 0x81, 0xe9, 0x1, 0x0, 0xe8, 0x1, 0x0, | |
| 0x0, 0xff, 0x1, 0xff, 0x3, 0xf0, 0x3f, 0x0}; | |
| case wgpu::TextureFormat::ASTC12x10Unorm: | |
| case wgpu::TextureFormat::ASTC12x10UnormSrgb: | |
| case wgpu::TextureFormat::ASTC12x12Unorm: | |
| case wgpu::TextureFormat::ASTC12x12UnormSrgb: | |
| return {0x4, 0x80, 0xe9, 0x1, 0x0, 0xe8, 0x1, 0x0, | |
| 0x0, 0xff, 0x1, 0x0, 0x0, 0x3f, 0xf0, 0x3}; | |
| default: | |
| DAWN_UNREACHABLE(); | |
| return {}; | |
| } | |
| } | |
| // Return the texture data that is decoded from the result of GetOneBlockFormatTextureData | |
| // in RGBA8 formats. Since some compression methods may be lossy, we may use different colors | |
| // to test different formats. | |
| std::vector<utils::RGBA8> GetExpectedData(const wgpu::Extent3D& testRegion) { | |
| constexpr uint8_t kLeftAlpha = 0x88; | |
| constexpr uint8_t kRightAlpha = 0xFF; | |
| constexpr utils::RGBA8 kBCDarkRed(198, 0, 0, 255); | |
| constexpr utils::RGBA8 kBCDarkGreen(0, 207, 0, 255); | |
| constexpr utils::RGBA8 kBCDarkRedSRGB(144, 0, 0, 255); | |
| constexpr utils::RGBA8 kBCDarkGreenSRGB(0, 159, 0, 255); | |
| constexpr utils::RGBA8 kETC2DarkRed(204, 0, 0, 255); | |
| constexpr utils::RGBA8 kETC2DarkGreen(0, 204, 0, 255); | |
| constexpr utils::RGBA8 kETC2DarkRedSRGB(154, 0, 0, 255); | |
| constexpr utils::RGBA8 kETC2DarkGreenSRGB(0, 154, 0, 255); | |
| constexpr utils::RGBA8 kASTCDarkRed(244, 0, 0, 128); | |
| constexpr utils::RGBA8 kASTCDarkGreen(0, 244, 0, 255); | |
| constexpr utils::RGBA8 kASTCDarkRedSRGB(231, 0, 0, 128); | |
| constexpr utils::RGBA8 kASTCDarkGreenSRGB(0, 231, 0, 255); | |
| switch (GetParam().mTextureFormat) { | |
| case wgpu::TextureFormat::BC1RGBAUnorm: | |
| case wgpu::TextureFormat::BC7RGBAUnorm: | |
| return FillExpectedData(testRegion, kBCDarkRed, kBCDarkGreen); | |
| case wgpu::TextureFormat::BC2RGBAUnorm: | |
| case wgpu::TextureFormat::BC3RGBAUnorm: { | |
| constexpr utils::RGBA8 kLeftColor = utils::RGBA8(kBCDarkRed.r, 0, 0, kLeftAlpha); | |
| constexpr utils::RGBA8 kRightColor = | |
| utils::RGBA8(0, kBCDarkGreen.g, 0, kRightAlpha); | |
| return FillExpectedData(testRegion, kLeftColor, kRightColor); | |
| } | |
| case wgpu::TextureFormat::BC1RGBAUnormSrgb: | |
| case wgpu::TextureFormat::BC7RGBAUnormSrgb: | |
| return FillExpectedData(testRegion, kBCDarkRedSRGB, kBCDarkGreenSRGB); | |
| case wgpu::TextureFormat::BC2RGBAUnormSrgb: | |
| case wgpu::TextureFormat::BC3RGBAUnormSrgb: { | |
| constexpr utils::RGBA8 kLeftColor = | |
| utils::RGBA8(kBCDarkRedSRGB.r, 0, 0, kLeftAlpha); | |
| constexpr utils::RGBA8 kRightColor = | |
| utils::RGBA8(0, kBCDarkGreenSRGB.g, 0, kRightAlpha); | |
| return FillExpectedData(testRegion, kLeftColor, kRightColor); | |
| } | |
| case wgpu::TextureFormat::BC4RSnorm: | |
| case wgpu::TextureFormat::BC4RUnorm: | |
| return FillExpectedData(testRegion, utils::RGBA8::kRed, utils::RGBA8::kBlack); | |
| case wgpu::TextureFormat::BC5RGSnorm: | |
| case wgpu::TextureFormat::BC5RGUnorm: | |
| case wgpu::TextureFormat::BC6HRGBFloat: | |
| case wgpu::TextureFormat::BC6HRGBUfloat: | |
| return FillExpectedData(testRegion, utils::RGBA8::kRed, utils::RGBA8::kGreen); | |
| case wgpu::TextureFormat::ETC2RGB8Unorm: | |
| case wgpu::TextureFormat::ETC2RGB8A1Unorm: | |
| return FillExpectedData(testRegion, kETC2DarkRed, kETC2DarkGreen); | |
| case wgpu::TextureFormat::ETC2RGB8UnormSrgb: | |
| case wgpu::TextureFormat::ETC2RGB8A1UnormSrgb: | |
| return FillExpectedData(testRegion, kETC2DarkRedSRGB, kETC2DarkGreenSRGB); | |
| case wgpu::TextureFormat::ETC2RGBA8Unorm: { | |
| constexpr utils::RGBA8 kLeftColor = utils::RGBA8(kETC2DarkRed.r, 0, 0, kLeftAlpha); | |
| constexpr utils::RGBA8 kRightColor = | |
| utils::RGBA8(0, kETC2DarkGreen.g, 0, kRightAlpha); | |
| return FillExpectedData(testRegion, kLeftColor, kRightColor); | |
| } | |
| case wgpu::TextureFormat::ETC2RGBA8UnormSrgb: { | |
| constexpr utils::RGBA8 kLeftColor = | |
| utils::RGBA8(kETC2DarkRedSRGB.r, 0, 0, kLeftAlpha); | |
| constexpr utils::RGBA8 kRightColor = | |
| utils::RGBA8(0, kETC2DarkGreenSRGB.g, 0, kRightAlpha); | |
| return FillExpectedData(testRegion, kLeftColor, kRightColor); | |
| } | |
| case wgpu::TextureFormat::EACR11Unorm: | |
| case wgpu::TextureFormat::EACR11Snorm: | |
| return FillExpectedData(testRegion, utils::RGBA8::kRed, utils::RGBA8::kBlack); | |
| case wgpu::TextureFormat::EACRG11Unorm: | |
| case wgpu::TextureFormat::EACRG11Snorm: | |
| return FillExpectedData(testRegion, utils::RGBA8::kRed, utils::RGBA8::kGreen); | |
| case wgpu::TextureFormat::ASTC4x4Unorm: | |
| case wgpu::TextureFormat::ASTC5x4Unorm: | |
| case wgpu::TextureFormat::ASTC5x5Unorm: | |
| case wgpu::TextureFormat::ASTC6x5Unorm: | |
| case wgpu::TextureFormat::ASTC6x6Unorm: | |
| case wgpu::TextureFormat::ASTC8x5Unorm: | |
| case wgpu::TextureFormat::ASTC8x6Unorm: | |
| case wgpu::TextureFormat::ASTC8x8Unorm: | |
| case wgpu::TextureFormat::ASTC10x5Unorm: | |
| case wgpu::TextureFormat::ASTC10x6Unorm: | |
| case wgpu::TextureFormat::ASTC10x8Unorm: | |
| case wgpu::TextureFormat::ASTC10x10Unorm: | |
| case wgpu::TextureFormat::ASTC12x10Unorm: | |
| case wgpu::TextureFormat::ASTC12x12Unorm: | |
| return FillExpectedData(testRegion, kASTCDarkRed, kASTCDarkGreen); | |
| case wgpu::TextureFormat::ASTC4x4UnormSrgb: | |
| case wgpu::TextureFormat::ASTC5x4UnormSrgb: | |
| case wgpu::TextureFormat::ASTC5x5UnormSrgb: | |
| case wgpu::TextureFormat::ASTC6x5UnormSrgb: | |
| case wgpu::TextureFormat::ASTC6x6UnormSrgb: | |
| case wgpu::TextureFormat::ASTC8x5UnormSrgb: | |
| case wgpu::TextureFormat::ASTC8x6UnormSrgb: | |
| case wgpu::TextureFormat::ASTC8x8UnormSrgb: | |
| case wgpu::TextureFormat::ASTC10x5UnormSrgb: | |
| case wgpu::TextureFormat::ASTC10x6UnormSrgb: | |
| case wgpu::TextureFormat::ASTC10x8UnormSrgb: | |
| case wgpu::TextureFormat::ASTC10x10UnormSrgb: | |
| case wgpu::TextureFormat::ASTC12x10UnormSrgb: | |
| case wgpu::TextureFormat::ASTC12x12UnormSrgb: | |
| return FillExpectedData(testRegion, kASTCDarkRedSRGB, kASTCDarkGreenSRGB); | |
| default: | |
| DAWN_UNREACHABLE(); | |
| return {}; | |
| } | |
| } | |
| std::vector<utils::RGBA8> FillExpectedData(const wgpu::Extent3D& testRegion, | |
| utils::RGBA8 leftColorInBlock, | |
| utils::RGBA8 rightColorInBlock) { | |
| DAWN_ASSERT(testRegion.depthOrArrayLayers == 1); | |
| std::vector<utils::RGBA8> expectedData( | |
| static_cast<size_t>(testRegion.width) * testRegion.height, leftColorInBlock); | |
| for (uint32_t y = 0; y < testRegion.height; ++y) { | |
| for (uint32_t x = 0; x < testRegion.width; ++x) { | |
| if (x % BlockWidthInTexels() >= BlockWidthInTexels() / 2) { | |
| expectedData[static_cast<size_t>(testRegion.width) * y + x] = rightColorInBlock; | |
| } | |
| } | |
| } | |
| return expectedData; | |
| } | |
| // Returns a texture size given the number of texel blocks that should be tiled. For example, | |
| // if a texel block size is 5x4, then GetTextureSizeFromBlocks(2, 2) -> {10, 8, 1}. | |
| wgpu::Extent3D GetTextureSizeWithNumBlocks(uint32_t numBlockWidth, | |
| uint32_t numBlockHeight, | |
| uint32_t depthOrArrayLayers = 1) const { | |
| return {numBlockWidth * BlockWidthInTexels(), numBlockHeight * BlockHeightInTexels(), | |
| depthOrArrayLayers}; | |
| } | |
| CopyConfig GetDefaultFullConfig(uint32_t depthOrArrayLayers = 1) const { | |
| DAWN_ASSERT(IsFormatSupported()); | |
| CopyConfig config; | |
| config.textureDescriptor.format = GetParam().mTextureFormat; | |
| config.textureDescriptor.usage = kDefaultFormatTextureUsage; | |
| config.textureDescriptor.size = GetTextureSizeWithNumBlocks( | |
| kUnalignedBlockSize, kUnalignedBlockSize, depthOrArrayLayers); | |
| config.textureDescriptor.mipLevelCount = kMipmapLevelCount; | |
| config.viewMipmapLevel = kMipmapLevelCount - 1; | |
| const wgpu::Extent3D virtualSize = GetVirtualSizeAtLevel(config); | |
| DAWN_ASSERT(virtualSize.width % BlockWidthInTexels() != 0u); | |
| DAWN_ASSERT(virtualSize.height % BlockHeightInTexels() != 0u); | |
| return config; | |
| } | |
| CopyConfig GetDefaultSmallConfig(uint32_t depthOrArrayLayers = 1) const { | |
| DAWN_ASSERT(IsFormatSupported()); | |
| CopyConfig config; | |
| config.textureDescriptor.format = GetParam().mTextureFormat; | |
| config.textureDescriptor.usage = kDefaultFormatTextureUsage; | |
| config.textureDescriptor.size = GetTextureSizeWithNumBlocks(2, 2, depthOrArrayLayers); | |
| return config; | |
| } | |
| CopyConfig GetDefaultSubresourceConfig(uint32_t depthOrArrayLayers = 1) const { | |
| DAWN_ASSERT(IsFormatSupported()); | |
| CopyConfig config; | |
| config.textureDescriptor.format = GetParam().mTextureFormat; | |
| config.textureDescriptor.usage = kDefaultFormatTextureUsage; | |
| config.textureDescriptor.size = | |
| GetPhysicalSizeAtLevel(GetDefaultFullConfig(depthOrArrayLayers)); | |
| config.viewMipmapLevel = config.textureDescriptor.mipLevelCount - 1; | |
| return config; | |
| } | |
| // Note: Compressed formats are only valid with 2D (array) textures. | |
| static wgpu::Extent3D GetVirtualSizeAtLevel(const CopyConfig& config) { | |
| return {config.textureDescriptor.size.width >> config.viewMipmapLevel, | |
| config.textureDescriptor.size.height >> config.viewMipmapLevel, | |
| config.textureDescriptor.size.depthOrArrayLayers}; | |
| } | |
| wgpu::Extent3D GetPhysicalSizeAtLevel(const CopyConfig& config) const { | |
| wgpu::Extent3D sizeAtLevel = GetVirtualSizeAtLevel(config); | |
| sizeAtLevel.width = (sizeAtLevel.width + BlockWidthInTexels() - 1) / BlockWidthInTexels() * | |
| BlockWidthInTexels(); | |
| sizeAtLevel.height = (sizeAtLevel.height + BlockHeightInTexels() - 1) / | |
| BlockHeightInTexels() * BlockHeightInTexels(); | |
| return sizeAtLevel; | |
| } | |
| static constexpr wgpu::TextureUsage kDefaultFormatTextureUsage = | |
| wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::CopyDst; | |
| // We choose a prime that is greater than the current max texel dimension size as a multiplier | |
| // to compute test texture sizes so that we can be certain that its level 2 mipmap (x4) | |
| // cannot be a multiple of the dimension. This is useful for testing padding at the edges of | |
| // the mipmaps. | |
| static constexpr uint32_t kUnalignedBlockSize = 13; | |
| static constexpr uint32_t kMipmapLevelCount = 3; | |
| bool mIsFormatSupported = false; | |
| }; | |
| // Test copying into the whole texture with 2x2 blocks and sampling from it. | |
| TEST_P(CompressedTextureFormatTest, Basic) { | |
| DAWN_TEST_UNSUPPORTED_IF(!IsFormatSupported()); | |
| CopyConfig config = GetDefaultSmallConfig(); | |
| config.copyExtent3D = config.textureDescriptor.size; | |
| TestCopyRegionIntoFormatTextures(config); | |
| } | |
| // Test copying into the whole cube texture with 2x2 blocks and sampling from it. | |
| // Made for compatibility mode. | |
| TEST_P(CompressedTextureFormatTest, Cube) { | |
| DAWN_TEST_UNSUPPORTED_IF(!IsFormatSupported()); | |
| const wgpu::TextureFormat format = GetParam().mTextureFormat; | |
| // TODO(crbug.com/362762192): diagnose this failure on GLES with ASTC and ETC2 texture formats | |
| DAWN_SUPPRESS_TEST_IF( | |
| IsOpenGLES() && (utils::IsASTCTextureFormat(format) || utils::IsETC2TextureFormat(format))); | |
| constexpr uint32_t kLayers = 6; | |
| CopyConfig config = GetDefaultSmallConfig(kLayers); | |
| config.copyExtent3D = config.textureDescriptor.size; | |
| config.bytesPerRowAlignment = Align( | |
| config.copyExtent3D.width / BlockWidthInTexels() * utils::GetTexelBlockSizeInBytes(format), | |
| kTextureBytesPerRowAlignment); | |
| config.rowsPerImage = kLayers; | |
| wgpu::TextureBindingViewDimension textureBindingViewDimensionDesc; | |
| if (IsCompatibilityMode()) { | |
| textureBindingViewDimensionDesc.textureBindingViewDimension = | |
| wgpu::TextureViewDimension::Cube; | |
| config.textureDescriptor.nextInChain = &textureBindingViewDimensionDesc; | |
| wgpu::Texture texture = CreateTextureWithCompressedData(config); | |
| wgpu::RenderPipeline renderPipeline = CreateRenderPipelineForTestCube(); | |
| wgpu::Extent3D virtualSizeAtLevel = GetVirtualSizeAtLevel(config); | |
| // The copy region may exceed the subresource size because of the required paddings, so we | |
| // should limit the size of the expectedData to make it match the real size of the render | |
| // target. | |
| wgpu::Extent3D noPaddingExtent3D = config.copyExtent3D; | |
| if (config.copyOrigin3D.x + config.copyExtent3D.width > virtualSizeAtLevel.width) { | |
| noPaddingExtent3D.width = virtualSizeAtLevel.width - config.copyOrigin3D.x; | |
| } | |
| if (config.copyOrigin3D.y + config.copyExtent3D.height > virtualSizeAtLevel.height) { | |
| noPaddingExtent3D.height = virtualSizeAtLevel.height - config.copyOrigin3D.y; | |
| } | |
| noPaddingExtent3D.depthOrArrayLayers = 1u; | |
| std::vector<utils::RGBA8> expectedData = GetExpectedData(noPaddingExtent3D); | |
| wgpu::Origin3D firstLayerCopyOrigin = {config.copyOrigin3D.x, config.copyOrigin3D.y, 0}; | |
| wgpu::SamplerDescriptor samplerDesc = {}; | |
| wgpu::Sampler sampler = device.CreateSampler(&samplerDesc); | |
| wgpu::TextureViewDescriptor textureViewDescriptor = {}; | |
| textureViewDescriptor.format = GetParam().mTextureFormat; | |
| textureViewDescriptor.dimension = wgpu::TextureViewDimension::Cube; | |
| textureViewDescriptor.baseMipLevel = config.viewMipmapLevel; | |
| textureViewDescriptor.mipLevelCount = 1; | |
| wgpu::TextureView textureView = texture.CreateView(&textureViewDescriptor); | |
| for (uint32_t layer = config.copyOrigin3D.z; | |
| layer < config.copyOrigin3D.z + config.copyExtent3D.depthOrArrayLayers; ++layer) { | |
| wgpu::Buffer uniformBuffer = utils::CreateBufferFromData( | |
| device, &layer, sizeof(uint32_t), wgpu::BufferUsage::Uniform); | |
| wgpu::BindGroup bindGroup = | |
| utils::MakeBindGroup(device, renderPipeline.GetBindGroupLayout(0), | |
| { | |
| {0, sampler}, | |
| {1, textureView}, | |
| {2, uniformBuffer}, | |
| }); | |
| VerifyCompressedTexturePixelValues(renderPipeline, bindGroup, virtualSizeAtLevel, | |
| firstLayerCopyOrigin, noPaddingExtent3D, | |
| expectedData); | |
| } | |
| } else { | |
| TestCopyRegionIntoFormatTextures(config); | |
| } | |
| } | |
| // Test copying into a sub-region of a texture works correctly. | |
| TEST_P(CompressedTextureFormatTest, CopyIntoSubRegion) { | |
| // TODO(crbug.com/dawn/976): Failing on Linux Intel OpenGL drivers. | |
| DAWN_SUPPRESS_TEST_IF(IsIntel() && IsOpenGL() && IsLinux()); | |
| DAWN_TEST_UNSUPPORTED_IF(!IsFormatSupported()); | |
| CopyConfig config = GetDefaultSmallConfig(); | |
| config.copyOrigin3D = {BlockWidthInTexels(), BlockHeightInTexels(), 0}; | |
| config.copyExtent3D = {BlockWidthInTexels(), BlockHeightInTexels(), 1}; | |
| TestCopyRegionIntoFormatTextures(config); | |
| } | |
| // Test copying into the non-zero layer of a 2D array texture works correctly. | |
| TEST_P(CompressedTextureFormatTest, CopyIntoNonZeroArrayLayer) { | |
| DAWN_TEST_UNSUPPORTED_IF(!IsFormatSupported()); | |
| // Compatibility mode cannot bind a 2d-array texture as 2d. | |
| DAWN_TEST_UNSUPPORTED_IF(IsCompatibilityMode()); | |
| constexpr uint32_t kArrayLayerCount = 3; | |
| CopyConfig config = GetDefaultSmallConfig(kArrayLayerCount); | |
| config.copyExtent3D = config.textureDescriptor.size; | |
| config.copyExtent3D.depthOrArrayLayers = 1; | |
| config.copyOrigin3D.z = kArrayLayerCount - 1; | |
| TestCopyRegionIntoFormatTextures(config); | |
| } | |
| // Test copying into a non-zero mipmap level of a texture. | |
| TEST_P(CompressedTextureFormatTest, CopyBufferIntoNonZeroMipmapLevel) { | |
| DAWN_TEST_UNSUPPORTED_IF(!IsFormatSupported()); | |
| // TODO(crbug.com/dawn/0000): diagnose this failure on QualComm OpenGL ES. | |
| DAWN_SUPPRESS_TEST_IF(IsAndroid() && IsOpenGLES() && IsQualcomm()); | |
| CopyConfig config = GetDefaultFullConfig(); | |
| // The virtual size of the texture at mipmap level == 2 is not a multiple of the texel | |
| // dimensions so paddings are required in the copies. | |
| config.copyExtent3D = GetPhysicalSizeAtLevel(config); | |
| TestCopyRegionIntoFormatTextures(config); | |
| } | |
| // Test texture-to-texture whole-size copies. | |
| TEST_P(CompressedTextureFormatTest, CopyWholeTextureSubResourceIntoNonZeroMipmapLevel) { | |
| DAWN_TEST_UNSUPPORTED_IF(!IsFormatSupported()); | |
| // Compatibility mode does not support T2T copies of compressed textures. | |
| DAWN_TEST_UNSUPPORTED_IF(IsCompatibilityMode()); | |
| // TODO(crbug.com/dawn/816): This consistently fails on with the 12th pixel being opaque | |
| // black instead of opaque red on Win10 FYI Release (NVIDIA GeForce GTX 1660). | |
| DAWN_SUPPRESS_TEST_IF(IsWindows() && IsVulkan() && IsNvidia()); | |
| CopyConfig config = GetDefaultFullConfig(); | |
| // Add the usage bit for both source and destination textures so that we don't need to | |
| // create two copy configs. | |
| config.textureDescriptor.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst | | |
| wgpu::TextureUsage::TextureBinding; | |
| // The virtual size of the texture at mipmap level == 2 is not a multiple of the texel | |
| // dimensions so paddings are required in the copies. | |
| const wgpu::Extent3D kVirtualSize = GetVirtualSizeAtLevel(config); | |
| config.copyExtent3D = GetPhysicalSizeAtLevel(config); | |
| wgpu::Texture textureSrc = CreateTextureWithCompressedData(config); | |
| // Create textureDst and copy from the content in textureSrc into it. | |
| wgpu::Texture textureDst = CreateTextureFromTexture(textureSrc, config, config); | |
| // Verify if we can use texture as sampled textures correctly. | |
| wgpu::RenderPipeline renderPipeline = CreateRenderPipelineForTestTex2D(); | |
| wgpu::BindGroup bindGroup = | |
| CreateBindGroupForTest(renderPipeline.GetBindGroupLayout(0), textureDst, | |
| config.copyOrigin3D.z, config.viewMipmapLevel); | |
| std::vector<utils::RGBA8> expectedData = GetExpectedData(kVirtualSize); | |
| VerifyCompressedTexturePixelValues(renderPipeline, bindGroup, kVirtualSize, config.copyOrigin3D, | |
| kVirtualSize, expectedData); | |
| } | |
| // Test texture-to-texture partial copies where the physical size of the destination subresource is | |
| // different from its virtual size. | |
| TEST_P(CompressedTextureFormatTest, CopyIntoSubresourceWithPhysicalSizeNotEqualToVirtualSize) { | |
| DAWN_TEST_UNSUPPORTED_IF(!IsFormatSupported()); | |
| // Compatibility mode does not support T2T copies of compressed textures. | |
| DAWN_TEST_UNSUPPORTED_IF(IsCompatibilityMode()); | |
| // TODO(crbug.com/dawn/817): add workaround on the T2T copies where Extent3D fits in one | |
| // subresource and does not fit in another one on OpenGL. | |
| DAWN_SUPPRESS_TEST_IF(IsOpenGL()); | |
| CopyConfig srcConfig = GetDefaultSubresourceConfig(); | |
| srcConfig.textureDescriptor.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst; | |
| CopyConfig dstConfig = GetDefaultFullConfig(); | |
| // The virtual size of the texture at mipmap level == 2 is not a multiple of the texel | |
| // dimensions so paddings are required in the copies. | |
| const wgpu::Extent3D kDstVirtualSize = GetVirtualSizeAtLevel(dstConfig); | |
| const wgpu::Extent3D kDstPhysicalSize = GetPhysicalSizeAtLevel(dstConfig); | |
| srcConfig.copyExtent3D = dstConfig.copyExtent3D = kDstPhysicalSize; | |
| // Create textureSrc as the source texture and initialize it with pre-prepared compressed | |
| // data. | |
| wgpu::Texture textureSrc = CreateTextureWithCompressedData(srcConfig); | |
| wgpu::TexelCopyTextureInfo texelCopyTextureInfoSrc = utils::CreateTexelCopyTextureInfo( | |
| textureSrc, srcConfig.viewMipmapLevel, srcConfig.copyOrigin3D); | |
| // Create textureDst and copy from the content in textureSrc into it. | |
| wgpu::Texture textureDst = CreateTextureFromTexture(textureSrc, srcConfig, dstConfig); | |
| // Verify if we can use texture as sampled textures correctly. | |
| wgpu::RenderPipeline renderPipeline = CreateRenderPipelineForTestTex2D(); | |
| wgpu::BindGroup bindGroup = | |
| CreateBindGroupForTest(renderPipeline.GetBindGroupLayout(0), textureDst, | |
| dstConfig.copyOrigin3D.z, dstConfig.viewMipmapLevel); | |
| std::vector<utils::RGBA8> expectedData = GetExpectedData(kDstVirtualSize); | |
| VerifyCompressedTexturePixelValues(renderPipeline, bindGroup, kDstVirtualSize, | |
| dstConfig.copyOrigin3D, kDstVirtualSize, expectedData); | |
| } | |
| // Test texture-to-texture partial copies where the physical size of the source subresource is | |
| // different from its virtual size. | |
| TEST_P(CompressedTextureFormatTest, CopyFromSubresourceWithPhysicalSizeNotEqualToVirtualSize) { | |
| DAWN_TEST_UNSUPPORTED_IF(!IsFormatSupported()); | |
| // Compatibility mode does not support T2T copies of compressed textures. | |
| DAWN_TEST_UNSUPPORTED_IF(IsCompatibilityMode()); | |
| // TODO(crbug.com/dawn/817): add workaround on the T2T copies where Extent3D fits in one | |
| // subresource and does not fit in another one on OpenGL. | |
| DAWN_SUPPRESS_TEST_IF(IsOpenGL()); | |
| CopyConfig srcConfig = GetDefaultFullConfig(); | |
| srcConfig.textureDescriptor.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst; | |
| CopyConfig dstConfig = GetDefaultSubresourceConfig(); | |
| // The virtual size of the texture at mipmap level == 2 is not a multiple of the texel | |
| // dimensions so paddings are required in the copies. | |
| const wgpu::Extent3D kSrcVirtualSize = GetVirtualSizeAtLevel(srcConfig); | |
| const wgpu::Extent3D kDstVirtualSize = GetVirtualSizeAtLevel(dstConfig); | |
| srcConfig.copyExtent3D = dstConfig.copyExtent3D = kDstVirtualSize; | |
| ASSERT_GT(srcConfig.copyOrigin3D.x + srcConfig.copyExtent3D.width, kSrcVirtualSize.width); | |
| ASSERT_GT(srcConfig.copyOrigin3D.y + srcConfig.copyExtent3D.height, kSrcVirtualSize.height); | |
| // Create textureSrc as the source texture and initialize it with pre-prepared compressed | |
| // data. | |
| wgpu::Texture textureSrc = CreateTextureWithCompressedData(srcConfig); | |
| // Create textureDst and copy from the content in textureSrc into it. | |
| wgpu::Texture textureDst = CreateTextureFromTexture(textureSrc, srcConfig, dstConfig); | |
| // Verify if we can use texture as sampled textures correctly. | |
| wgpu::RenderPipeline renderPipeline = CreateRenderPipelineForTestTex2D(); | |
| wgpu::BindGroup bindGroup = | |
| CreateBindGroupForTest(renderPipeline.GetBindGroupLayout(0), textureDst, | |
| dstConfig.copyOrigin3D.z, dstConfig.viewMipmapLevel); | |
| std::vector<utils::RGBA8> expectedData = GetExpectedData(kDstVirtualSize); | |
| VerifyCompressedTexturePixelValues(renderPipeline, bindGroup, kDstVirtualSize, | |
| dstConfig.copyOrigin3D, kDstVirtualSize, expectedData); | |
| } | |
| // Test recording two texture-to-texture partial copies where the physical size of the source | |
| // subresource is different from its virtual size into one command buffer. | |
| TEST_P(CompressedTextureFormatTest, MultipleCopiesWithPhysicalSizeNotEqualToVirtualSize) { | |
| DAWN_TEST_UNSUPPORTED_IF(!IsFormatSupported()); | |
| // Compatibility mode does not support T2T copies of compressed textures. | |
| DAWN_TEST_UNSUPPORTED_IF(IsCompatibilityMode()); | |
| // TODO(crbug.com/dawn/817): add workaround on the T2T copies where Extent3D fits in one | |
| // subresource and does not fit in another one on OpenGL. | |
| DAWN_SUPPRESS_TEST_IF(IsOpenGL()); | |
| constexpr uint32_t kTotalCopyCount = 2; | |
| std::array<CopyConfig, kTotalCopyCount> srcConfigs; | |
| std::array<CopyConfig, kTotalCopyCount> dstConfigs; | |
| srcConfigs[0] = GetDefaultFullConfig(); | |
| srcConfigs[0].textureDescriptor.usage = | |
| wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst; | |
| dstConfigs[0] = GetDefaultSubresourceConfig(); | |
| srcConfigs[0].copyExtent3D = dstConfigs[0].copyExtent3D = GetVirtualSizeAtLevel(dstConfigs[0]); | |
| srcConfigs[1] = GetDefaultSubresourceConfig(); | |
| srcConfigs[1].textureDescriptor.usage = | |
| wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst; | |
| dstConfigs[1] = GetDefaultFullConfig(); | |
| srcConfigs[1].copyExtent3D = dstConfigs[1].copyExtent3D = GetVirtualSizeAtLevel(srcConfigs[1]); | |
| std::array<wgpu::Extent3D, kTotalCopyCount> dstVirtualSizes; | |
| for (uint32_t i = 0; i < kTotalCopyCount; ++i) { | |
| dstVirtualSizes[i] = GetVirtualSizeAtLevel(dstConfigs[i]); | |
| } | |
| std::array<wgpu::Texture, kTotalCopyCount> srcTextures; | |
| std::array<wgpu::Texture, kTotalCopyCount> dstTextures; | |
| wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); | |
| for (uint32_t i = 0; i < kTotalCopyCount; ++i) { | |
| // Create srcTextures as the source textures and initialize them with pre-prepared | |
| // compressed data. | |
| srcTextures[i] = CreateTextureWithCompressedData(srcConfigs[i]); | |
| dstTextures[i] = device.CreateTexture(&dstConfigs[i].textureDescriptor); | |
| RecordTextureToTextureCopy(encoder, srcTextures[i], dstTextures[i], srcConfigs[i], | |
| dstConfigs[i]); | |
| } | |
| wgpu::CommandBuffer commandBuffer = encoder.Finish(); | |
| queue.Submit(1, &commandBuffer); | |
| wgpu::RenderPipeline renderPipeline = CreateRenderPipelineForTestTex2D(); | |
| for (uint32_t i = 0; i < kTotalCopyCount; ++i) { | |
| // Verify if we can use dstTextures as sampled textures correctly. | |
| wgpu::BindGroup bindGroup0 = | |
| CreateBindGroupForTest(renderPipeline.GetBindGroupLayout(0), dstTextures[i], | |
| dstConfigs[i].copyOrigin3D.z, dstConfigs[i].viewMipmapLevel); | |
| std::vector<utils::RGBA8> expectedData = GetExpectedData(dstVirtualSizes[i]); | |
| VerifyCompressedTexturePixelValues(renderPipeline, bindGroup0, dstVirtualSizes[i], | |
| dstConfigs[i].copyOrigin3D, dstVirtualSizes[i], | |
| expectedData); | |
| } | |
| } | |
| // A regression test for a bug for the toggle UseTemporaryBufferInCompressedTextureToTextureCopy on | |
| // Vulkan backend: test texture-to-texture partial copies with multiple array layers where the | |
| // physical size of the source subresource is different from its virtual size. | |
| TEST_P(CompressedTextureFormatTest, CopyWithMultipleLayerAndPhysicalSizeNotEqualToVirtualSize) { | |
| DAWN_TEST_UNSUPPORTED_IF(!IsFormatSupported()); | |
| // Compatibility mode cannot bind a 2d-array texture as 2d. | |
| DAWN_TEST_UNSUPPORTED_IF(IsCompatibilityMode()); | |
| // TODO(crbug.com/dawn/817): add workaround on the T2T copies where Extent3D fits in one | |
| // subresource and does not fit in another one on OpenGL. | |
| DAWN_SUPPRESS_TEST_IF(IsOpenGL()); | |
| constexpr uint32_t kArrayLayerCount = 5; | |
| CopyConfig srcConfig = GetDefaultFullConfig(kArrayLayerCount); | |
| srcConfig.textureDescriptor.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst; | |
| CopyConfig dstConfig = GetDefaultSubresourceConfig(kArrayLayerCount); | |
| // The virtual size of the texture at mipmap level == 2 is not a multiple of the texel | |
| // dimensions so paddings are required in the copies. | |
| const wgpu::Extent3D kSrcVirtualSize = GetVirtualSizeAtLevel(srcConfig); | |
| const wgpu::Extent3D kDstVirtualSize = GetVirtualSizeAtLevel(dstConfig); | |
| srcConfig.copyExtent3D = dstConfig.copyExtent3D = kDstVirtualSize; | |
| srcConfig.rowsPerImage = srcConfig.copyExtent3D.height / BlockHeightInTexels(); | |
| ASSERT_GT(srcConfig.copyOrigin3D.x + srcConfig.copyExtent3D.width, kSrcVirtualSize.width); | |
| ASSERT_GT(srcConfig.copyOrigin3D.y + srcConfig.copyExtent3D.height, kSrcVirtualSize.height); | |
| const wgpu::TextureFormat format = GetParam().mTextureFormat; | |
| srcConfig.textureDescriptor.format = dstConfig.textureDescriptor.format = format; | |
| srcConfig.bytesPerRowAlignment = Align(srcConfig.copyExtent3D.width / BlockWidthInTexels() * | |
| utils::GetTexelBlockSizeInBytes(format), | |
| kTextureBytesPerRowAlignment); | |
| dstConfig.textureDescriptor.usage = kDefaultFormatTextureUsage; | |
| // Create textureSrc as the source texture and initialize it with pre-prepared compressed | |
| // data. | |
| wgpu::Texture textureSrc = CreateTextureWithCompressedData(srcConfig); | |
| // Create textureDst and copy from the content in textureSrc into it. | |
| wgpu::Texture textureDst = CreateTextureFromTexture(textureSrc, srcConfig, dstConfig); | |
| // We use the render pipeline to test if each layer can be correctly sampled with the | |
| // expected data. | |
| wgpu::RenderPipeline renderPipeline = CreateRenderPipelineForTestTex2D(); | |
| const wgpu::Extent3D kExpectedDataRegionPerLayer = {kDstVirtualSize.width, | |
| kDstVirtualSize.height, 1u}; | |
| std::vector<utils::RGBA8> kExpectedDataPerLayer = GetExpectedData(kExpectedDataRegionPerLayer); | |
| const wgpu::Origin3D kCopyOriginPerLayer = {dstConfig.copyOrigin3D.x, dstConfig.copyOrigin3D.y, | |
| 0}; | |
| for (uint32_t copyLayer = 0; copyLayer < kArrayLayerCount; ++copyLayer) { | |
| wgpu::BindGroup bindGroup = | |
| CreateBindGroupForTest(renderPipeline.GetBindGroupLayout(0), textureDst, | |
| dstConfig.copyOrigin3D.z + copyLayer, dstConfig.viewMipmapLevel); | |
| VerifyCompressedTexturePixelValues(renderPipeline, bindGroup, kExpectedDataRegionPerLayer, | |
| kCopyOriginPerLayer, kExpectedDataRegionPerLayer, | |
| kExpectedDataPerLayer); | |
| } | |
| } | |
| // Test the special case of the B2T copies on the D3D12 backend that the buffer offset and texture | |
| // extent exactly fit the RowPitch. | |
| TEST_P(CompressedTextureFormatTest, BufferOffsetAndExtentFitRowPitch) { | |
| DAWN_TEST_UNSUPPORTED_IF(!IsFormatSupported()); | |
| CopyConfig config = GetDefaultSmallConfig(); | |
| config.copyExtent3D = config.textureDescriptor.size; | |
| const wgpu::TextureFormat format = GetParam().mTextureFormat; | |
| const uint32_t blockCountPerRow = config.textureDescriptor.size.width / BlockWidthInTexels(); | |
| const uint32_t blockSizeInBytes = utils::GetTexelBlockSizeInBytes(format); | |
| const uint32_t blockCountPerRowPitch = config.bytesPerRowAlignment / blockSizeInBytes; | |
| config.bufferOffset = (blockCountPerRowPitch - blockCountPerRow) * blockSizeInBytes; | |
| TestCopyRegionIntoFormatTextures(config); | |
| } | |
| // Test the special case of the B2T copies on the D3D12 backend that the buffer offset exceeds the | |
| // slice pitch (slicePitch = bytesPerRow * (rowsPerImage / blockHeightInTexels)). On D3D12 | |
| // backend the texelOffset.y will be greater than 0 after calcuting the texelOffset in the function | |
| // ComputeTexelOffsets(). | |
| TEST_P(CompressedTextureFormatTest, BufferOffsetExceedsSlicePitch) { | |
| DAWN_TEST_UNSUPPORTED_IF(!IsFormatSupported()); | |
| CopyConfig config = GetDefaultSmallConfig(); | |
| config.copyExtent3D = config.textureDescriptor.size; | |
| const wgpu::TextureFormat format = GetParam().mTextureFormat; | |
| const wgpu::Extent3D textureSizeLevel = config.textureDescriptor.size; | |
| const uint32_t blockCountPerRow = textureSizeLevel.width / BlockWidthInTexels(); | |
| const uint32_t slicePitchInBytes = | |
| config.bytesPerRowAlignment * (textureSizeLevel.height / BlockHeightInTexels()); | |
| const uint32_t blockSizeInBytes = utils::GetTexelBlockSizeInBytes(format); | |
| const uint32_t blockCountPerRowPitch = config.bytesPerRowAlignment / blockSizeInBytes; | |
| config.bufferOffset = (blockCountPerRowPitch - blockCountPerRow) * blockSizeInBytes + | |
| config.bytesPerRowAlignment + slicePitchInBytes; | |
| TestCopyRegionIntoFormatTextures(config); | |
| } | |
| // Test the special case of the B2T copies on the D3D12 backend that the buffer offset and texture | |
| // extent exceed the RowPitch. On D3D12 backend two copies are required for this case. | |
| TEST_P(CompressedTextureFormatTest, CopyWithBufferOffsetAndExtentExceedRowPitch) { | |
| DAWN_TEST_UNSUPPORTED_IF(!IsFormatSupported()); | |
| constexpr uint32_t kExceedRowBlockCount = 1; | |
| CopyConfig config = GetDefaultSmallConfig(); | |
| config.copyExtent3D = config.textureDescriptor.size; | |
| const wgpu::TextureFormat format = GetParam().mTextureFormat; | |
| const uint32_t blockCountPerRow = config.textureDescriptor.size.width / BlockWidthInTexels(); | |
| const uint32_t blockSizeInBytes = utils::GetTexelBlockSizeInBytes(format); | |
| const uint32_t blockCountPerRowPitch = config.bytesPerRowAlignment / blockSizeInBytes; | |
| config.bufferOffset = | |
| (blockCountPerRowPitch - blockCountPerRow + kExceedRowBlockCount) * blockSizeInBytes; | |
| TestCopyRegionIntoFormatTextures(config); | |
| } | |
| // Test the special case of the B2T copies on the D3D12 backend that the slicePitch is equal to the | |
| // bytesPerRow. On D3D12 backend the texelOffset.z will be greater than 0 after calcuting the | |
| // texelOffset in the function ComputeTexelOffsets(). | |
| TEST_P(CompressedTextureFormatTest, RowPitchEqualToSlicePitch) { | |
| DAWN_TEST_UNSUPPORTED_IF(!IsFormatSupported()); | |
| CopyConfig config = GetDefaultSmallConfig(); | |
| config.textureDescriptor.size = GetTextureSizeWithNumBlocks(2, 1); | |
| config.copyExtent3D = config.textureDescriptor.size; | |
| const wgpu::TextureFormat format = GetParam().mTextureFormat; | |
| const uint32_t blockCountPerRow = config.textureDescriptor.size.width / BlockWidthInTexels(); | |
| const uint32_t slicePitchInBytes = config.bytesPerRowAlignment; | |
| const uint32_t blockSizeInBytes = utils::GetTexelBlockSizeInBytes(format); | |
| const uint32_t blockCountPerRowPitch = config.bytesPerRowAlignment / blockSizeInBytes; | |
| config.bufferOffset = | |
| (blockCountPerRowPitch - blockCountPerRow) * blockSizeInBytes + slicePitchInBytes; | |
| TestCopyRegionIntoFormatTextures(config); | |
| } | |
| // Test the workaround in the B2T copies when (bufferSize - bufferOffset < bytesPerImage * | |
| // copyExtent.depthOrArrayLayers) on Metal backends. As copyExtent.depthOrArrayLayers can only be 1 | |
| // for compressed formats, on Metal backend we will use two copies to implement such copy. | |
| TEST_P(CompressedTextureFormatTest, LargeImageHeight) { | |
| DAWN_TEST_UNSUPPORTED_IF(!IsFormatSupported()); | |
| CopyConfig config = GetDefaultSmallConfig(); | |
| config.copyExtent3D = config.textureDescriptor.size; | |
| config.rowsPerImage = config.textureDescriptor.size.height * 2 / BlockHeightInTexels(); | |
| TestCopyRegionIntoFormatTextures(config); | |
| } | |
| // Test the workaround in the B2T copies when (bufferSize - bufferOffset < bytesPerImage * | |
| // copyExtent.depthOrArrayLayers) and copyExtent needs to be clamped. | |
| TEST_P(CompressedTextureFormatTest, LargeImageHeightAndClampedCopyExtent) { | |
| DAWN_TEST_UNSUPPORTED_IF(!IsFormatSupported()); | |
| // TODO(crbug.com/dawn/0000): diagnose this failure on QualComm OpenGL ES. | |
| DAWN_SUPPRESS_TEST_IF(IsAndroid() && IsOpenGLES() && IsQualcomm()); | |
| CopyConfig config = GetDefaultFullConfig(); | |
| // The virtual size of the texture at mipmap level == 2 is not a multiple of the texel | |
| // dimensions so paddings are required in the copies. | |
| const wgpu::Extent3D kPhysicalSize = GetPhysicalSizeAtLevel(config); | |
| config.copyExtent3D = kPhysicalSize; | |
| config.rowsPerImage = kPhysicalSize.height * 2 / BlockHeightInTexels(); | |
| TestCopyRegionIntoFormatTextures(config); | |
| } | |
| // Test copying a whole 2D array texture with array layer count > 1 in one copy command works with | |
| // compressed formats. | |
| TEST_P(CompressedTextureFormatTest, CopyWhole2DArrayTexture) { | |
| DAWN_TEST_UNSUPPORTED_IF(!IsFormatSupported()); | |
| // Compatibility mode does not support T2T copies of compressed textures. | |
| DAWN_TEST_UNSUPPORTED_IF(IsCompatibilityMode()); | |
| constexpr uint32_t kArrayLayerCount = 3; | |
| CopyConfig config = GetDefaultSmallConfig(kArrayLayerCount); | |
| config.rowsPerImage = 8; | |
| config.copyExtent3D = config.textureDescriptor.size; | |
| config.copyExtent3D.depthOrArrayLayers = kArrayLayerCount; | |
| TestCopyRegionIntoFormatTextures(config); | |
| } | |
| // Test copying a multiple 2D texture array layers in one copy command works. | |
| TEST_P(CompressedTextureFormatTest, CopyMultiple2DArrayLayers) { | |
| DAWN_TEST_UNSUPPORTED_IF(!IsFormatSupported()); | |
| // Compatibility mode cannot bind a 2d-array texture as 2d. | |
| DAWN_TEST_UNSUPPORTED_IF(IsCompatibilityMode()); | |
| constexpr uint32_t kArrayLayerCount = 3; | |
| CopyConfig config = GetDefaultSmallConfig(kArrayLayerCount); | |
| config.rowsPerImage = 8; | |
| constexpr uint32_t kCopyBaseArrayLayer = 1; | |
| constexpr uint32_t kCopyLayerCount = 2; | |
| config.copyOrigin3D = {0, 0, kCopyBaseArrayLayer}; | |
| config.copyExtent3D = config.textureDescriptor.size; | |
| config.copyExtent3D.depthOrArrayLayers = kCopyLayerCount; | |
| TestCopyRegionIntoFormatTextures(config); | |
| } | |
| DAWN_INSTANTIATE_TEST_P(CompressedTextureFormatTest, | |
| {D3D11Backend(), D3D12Backend(), MetalBackend(), OpenGLBackend(), | |
| OpenGLESBackend(), VulkanBackend(), | |
| VulkanBackend({"use_temporary_buffer_in_texture_to_texture_copy"}), | |
| WebGPUBackend()}, | |
| std::vector<wgpu::TextureFormat>(utils::kCompressedFormats.begin(), | |
| utils::kCompressedFormats.end())); | |
| // Suite of regression tests that target specific compression types. | |
| class CompressedTextureFormatSpecificTest : public DawnTest { | |
| protected: | |
| std::vector<wgpu::FeatureName> GetRequiredFeatures() override { | |
| mIsBCFormatSupported = SupportsFeatures({wgpu::FeatureName::TextureCompressionBC}); | |
| std::vector<wgpu::FeatureName> features; | |
| if (mIsBCFormatSupported) { | |
| features.emplace_back(wgpu::FeatureName::TextureCompressionBC); | |
| } | |
| return features; | |
| } | |
| bool IsBCFormatSupported() const { return mIsBCFormatSupported; } | |
| bool mIsBCFormatSupported = false; | |
| }; | |
| // Testing a special code path: clearing a non-renderable texture when DynamicUploader | |
| // is unaligned doesn't throw validation errors. | |
| TEST_P(CompressedTextureFormatSpecificTest, BC1RGBAUnorm_UnalignedDynamicUploader) { | |
| // Compatibility mode cannot bind a 2d-array texture as 2d. | |
| DAWN_TEST_UNSUPPORTED_IF(IsCompatibilityMode()); | |
| // CopyT2B for compressed texture formats is unimplemented on OpenGL. | |
| DAWN_TEST_UNSUPPORTED_IF(IsOpenGL()); | |
| DAWN_TEST_UNSUPPORTED_IF(!IsBCFormatSupported()); | |
| utils::UnalignDynamicUploader(device); | |
| wgpu::TextureDescriptor textureDescriptor = {}; | |
| textureDescriptor.size = {4, 4, 1}; | |
| textureDescriptor.format = wgpu::TextureFormat::BC1RGBAUnorm; | |
| textureDescriptor.usage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc; | |
| wgpu::Texture texture = device.CreateTexture(&textureDescriptor); | |
| wgpu::BufferDescriptor bufferDescriptor; | |
| bufferDescriptor.size = 8; | |
| bufferDescriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; | |
| wgpu::Buffer buffer = device.CreateBuffer(&bufferDescriptor); | |
| wgpu::TexelCopyTextureInfo texelCopyTextureInfo = | |
| utils::CreateTexelCopyTextureInfo(texture, 0, {0, 0, 0}); | |
| wgpu::TexelCopyBufferInfo texelCopyBufferInfo = | |
| utils::CreateTexelCopyBufferInfo(buffer, 0, 256); | |
| wgpu::Extent3D copyExtent = {4, 4, 1}; | |
| wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); | |
| encoder.CopyTextureToBuffer(&texelCopyTextureInfo, &texelCopyBufferInfo, ©Extent); | |
| wgpu::CommandBuffer commands = encoder.Finish(); | |
| queue.Submit(1, &commands); | |
| } | |
| DAWN_INSTANTIATE_TEST(CompressedTextureFormatSpecificTest, | |
| D3D11Backend(), | |
| D3D12Backend(), | |
| MetalBackend(), | |
| OpenGLBackend(), | |
| OpenGLESBackend(), | |
| VulkanBackend(), | |
| VulkanBackend({"use_temporary_buffer_in_texture_to_texture_copy"}), | |
| WebGPUBackend()); | |
| class CompressedTextureWriteTextureTest : public CompressedTextureFormatTest { | |
| protected: | |
| void SetUp() override { | |
| CompressedTextureFormatTest::SetUp(); | |
| DAWN_TEST_UNSUPPORTED_IF(!IsFormatSupported()); | |
| } | |
| // Write the compressed texture data into the destination texture as is specified in | |
| // copyConfig. | |
| void WriteToCompressedTexture(wgpu::Texture compressedTexture, const CopyConfig& copyConfig) { | |
| DAWN_ASSERT(IsFormatSupported()); | |
| std::vector<uint8_t> data = UploadData(copyConfig); | |
| wgpu::TexelCopyBufferLayout texelCopyBufferLayout = utils::CreateTexelCopyBufferLayout( | |
| copyConfig.bufferOffset, copyConfig.bytesPerRowAlignment, copyConfig.rowsPerImage); | |
| wgpu::TexelCopyTextureInfo texelCopyTextureInfo = utils::CreateTexelCopyTextureInfo( | |
| compressedTexture, copyConfig.viewMipmapLevel, copyConfig.copyOrigin3D); | |
| queue.WriteTexture(&texelCopyTextureInfo, data.data(), data.size(), &texelCopyBufferLayout, | |
| ©Config.copyExtent3D); | |
| } | |
| // Run the tests that write pre-prepared format data into a texture and verifies if we can | |
| // render correctly with the pixel values sampled from the texture. | |
| void TestWriteRegionIntoFormatTextures(const CopyConfig& config) { | |
| DAWN_ASSERT(IsFormatSupported()); | |
| wgpu::Texture texture = device.CreateTexture(&config.textureDescriptor); | |
| WriteToCompressedTexture(texture, config); | |
| VerifyTexture(config, texture); | |
| } | |
| }; | |
| // Test WriteTexture to a 2D texture with all parameters non-defaults. | |
| TEST_P(CompressedTextureWriteTextureTest, Basic) { | |
| // TODO(crbug.com/dawn/976): Failing on Linux Intel OpenGL drivers. | |
| DAWN_SUPPRESS_TEST_IF(IsIntel() && IsOpenGL() && IsLinux()); | |
| constexpr uint32_t kSizeWidthMultiplier = 5; | |
| constexpr uint32_t kSizeHeightMultiplier = 6; | |
| constexpr uint32_t kOriginWidthMultiplier = 1; | |
| constexpr uint32_t kOriginHeightMultiplier = 2; | |
| constexpr uint32_t kExtentWidthMultiplier = 3; | |
| constexpr uint32_t kExtentHeightMultiplier = 4; | |
| CopyConfig config; | |
| config.textureDescriptor.usage = kDefaultFormatTextureUsage; | |
| config.textureDescriptor.size = {BlockWidthInTexels() * kSizeWidthMultiplier, | |
| BlockHeightInTexels() * kSizeHeightMultiplier, 1}; | |
| config.copyOrigin3D = {BlockWidthInTexels() * kOriginWidthMultiplier, | |
| BlockHeightInTexels() * kOriginHeightMultiplier, 0}; | |
| config.copyExtent3D = {BlockWidthInTexels() * kExtentWidthMultiplier, | |
| BlockHeightInTexels() * kExtentHeightMultiplier, 1}; | |
| config.bytesPerRowAlignment = 511; | |
| config.rowsPerImage = 5; | |
| config.textureDescriptor.format = GetParam().mTextureFormat; | |
| TestWriteRegionIntoFormatTextures(config); | |
| } | |
| // Test writing to multiple 2D texture array layers. | |
| TEST_P(CompressedTextureWriteTextureTest, WriteMultiple2DArrayLayers) { | |
| // Compatibility mode cannot bind a 2d-array texture as 2d. | |
| DAWN_TEST_UNSUPPORTED_IF(IsCompatibilityMode()); | |
| // TODO(crbug.com/dawn/976): Failing on Linux Intel OpenGL drivers. | |
| DAWN_SUPPRESS_TEST_IF(IsIntel() && IsOpenGL() && IsLinux()); | |
| // TODO(crbug.com/459848484): Fails on Win/Snapdragon X Elite. | |
| DAWN_SUPPRESS_TEST_IF(IsWindows() && IsQualcomm() && IsD3D11()); | |
| // TODO(b/198674734): Width multiplier set to 7 because 5 results in square size for ASTC6x5. | |
| constexpr uint32_t kSizeWidthMultiplier = 7; | |
| constexpr uint32_t kSizeHeightMultiplier = 6; | |
| constexpr uint32_t kOriginWidthMultiplier = 1; | |
| constexpr uint32_t kOriginHeightMultiplier = 2; | |
| constexpr uint32_t kExtentWidthMultiplier = 3; | |
| constexpr uint32_t kExtentHeightMultiplier = 4; | |
| CopyConfig config; | |
| config.textureDescriptor.usage = kDefaultFormatTextureUsage; | |
| config.textureDescriptor.size = {BlockWidthInTexels() * kSizeWidthMultiplier, | |
| BlockHeightInTexels() * kSizeHeightMultiplier, 9}; | |
| config.copyOrigin3D = {BlockWidthInTexels() * kOriginWidthMultiplier, | |
| BlockHeightInTexels() * kOriginHeightMultiplier, 3}; | |
| config.copyExtent3D = {BlockWidthInTexels() * kExtentWidthMultiplier, | |
| BlockHeightInTexels() * kExtentHeightMultiplier, 6}; | |
| config.bytesPerRowAlignment = 511; | |
| config.rowsPerImage = 5; | |
| config.textureDescriptor.format = GetParam().mTextureFormat; | |
| TestWriteRegionIntoFormatTextures(config); | |
| } | |
| // Test writing textures where the physical size of the destination subresource is different from | |
| // its virtual size. | |
| TEST_P(CompressedTextureWriteTextureTest, | |
| WriteIntoSubresourceWithPhysicalSizeNotEqualToVirtualSize) { | |
| DAWN_SUPPRESS_TEST_IF(IsWARP()); | |
| // TODO(crbug.com/dawn/976): Failing on Linux Intel OpenGL drivers. | |
| DAWN_SUPPRESS_TEST_IF(IsIntel() && IsOpenGL() && IsLinux()); | |
| // TODO(crbug.com/dawn/0000): diagnose this failure on QualComm OpenGL ES. | |
| DAWN_SUPPRESS_TEST_IF(IsAndroid() && IsOpenGLES() && IsQualcomm()); | |
| // TODO(https://issues.chromium.org/issues/41479545): Triggers some VVL sync error on the | |
| // textures used as an attachment when verifying the contents of the compressed texture. | |
| // It complains that the CmdPipelineBarrier to attachment doesn't include the previous call | |
| // to CmdCopyBufferToTexture in its source, but that texture is never used in such a call! | |
| DAWN_SUPPRESS_TEST_IF(IsVulkan() && IsBackendValidationEnabled()); | |
| CopyConfig config = GetDefaultFullConfig(); | |
| // The virtual size of the texture at mipmap level == 2 is not a multiple of the texel | |
| // dimensions so paddings are required in the copies. We then test against the expected | |
| // physical size and a valid size smaller than the physical size for verification. | |
| const wgpu::Extent3D kPhysicalSize = GetPhysicalSizeAtLevel(config); | |
| for (unsigned int w : {kPhysicalSize.width - BlockWidthInTexels(), kPhysicalSize.width}) { | |
| for (unsigned int h : | |
| {kPhysicalSize.height - BlockHeightInTexels(), kPhysicalSize.height}) { | |
| config.copyExtent3D = {w, h, 1}; | |
| TestWriteRegionIntoFormatTextures(config); | |
| } | |
| } | |
| } | |
| DAWN_INSTANTIATE_TEST_P(CompressedTextureWriteTextureTest, | |
| {D3D11Backend(), D3D12Backend(), MetalBackend(), OpenGLBackend(), | |
| OpenGLESBackend(), VulkanBackend(), WebGPUBackend()}, | |
| std::vector<wgpu::TextureFormat>(utils::kCompressedFormats.begin(), | |
| utils::kCompressedFormats.end())); | |
| class UnalignedCompressedTextureFormatTest : public CompressedTextureFormatTest { | |
| void SetUp() override { | |
| CompressedTextureFormatTest::SetUp(); | |
| DAWN_TEST_UNSUPPORTED_IF( | |
| !device.HasFeature(wgpu::FeatureName::TextureCompressionUnaligned)); | |
| } | |
| std::vector<wgpu::FeatureName> GetRequiredFeatures() override { | |
| std::vector<wgpu::FeatureName> features = | |
| CompressedTextureFormatTest::GetRequiredFeatures(); | |
| if (SupportsFeatures({wgpu::FeatureName::TextureCompressionUnaligned})) { | |
| features.push_back(wgpu::FeatureName::TextureCompressionUnaligned); | |
| } | |
| return features; | |
| } | |
| }; | |
| // Test copying to and from a 1x1 compressed texture. | |
| TEST_P(UnalignedCompressedTextureFormatTest, 1x1) { | |
| DAWN_TEST_UNSUPPORTED_IF(!IsFormatSupported()); | |
| CopyConfig config = { | |
| .textureDescriptor = | |
| { | |
| .usage = kDefaultFormatTextureUsage, | |
| .size = {1, 1}, | |
| .format = GetParam().mTextureFormat, | |
| }, | |
| .copyExtent3D = GetTextureSizeWithNumBlocks(1, 1), | |
| }; | |
| TestCopyRegionIntoFormatTextures(config); | |
| } | |
| DAWN_INSTANTIATE_TEST_P(UnalignedCompressedTextureFormatTest, | |
| {D3D11Backend(), D3D12Backend(), MetalBackend(), OpenGLBackend(), | |
| OpenGLESBackend(), VulkanBackend(), | |
| VulkanBackend({"use_temporary_buffer_in_texture_to_texture_copy"}), | |
| WebGPUBackend()}, | |
| std::vector<wgpu::TextureFormat>(utils::kCompressedFormats.begin(), | |
| utils::kCompressedFormats.end())); | |
| } // anonymous namespace | |
| } // namespace dawn |