From d27151d333ac7b724982b36f8592236cbe04afec Mon Sep 17 00:00:00 2001 From: Yunchao He Date: Wed, 19 Oct 2022 15:19:13 +0000 Subject: [PATCH] D3D12 Intel: Improve the workaround for array texture corruption issue This change adds more tests to exercise the code of the workaround for array texture corruption issue. Because texture memory layout and tile alignment vary accordingly if array textures have mipmaps, and/or different dimensions, etc. It also does some slight changes in the workaround itself for array textures with non-32-or-16-bit-wise formats. Bug: dawn: 949, dawn: 1507 Change-Id: I22e87830ba59f2a2814e6786aa9a1a55a15c95cb Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/105241 Kokoro: Kokoro Reviewed-by: Austin Eng Commit-Queue: Yunchao He --- .../d3d12/ResourceAllocatorManagerD3D12.cpp | 31 ++- .../tests/end2end/TextureCorruptionTests.cpp | 231 ++++++++++++++---- 2 files changed, 204 insertions(+), 58 deletions(-) diff --git a/src/dawn/native/d3d12/ResourceAllocatorManagerD3D12.cpp b/src/dawn/native/d3d12/ResourceAllocatorManagerD3D12.cpp index 4db54aaf7e..c63e348a1f 100644 --- a/src/dawn/native/d3d12/ResourceAllocatorManagerD3D12.cpp +++ b/src/dawn/native/d3d12/ResourceAllocatorManagerD3D12.cpp @@ -236,11 +236,32 @@ uint32_t ComputeExtraArraySizeForIntelGen12(uint32_t width, // . // - Tile-based memory: the entire section of
. constexpr uint32_t kPageSize = 4 * 1024; - constexpr uint32_t kTileSize = 16 * kPageSize; - constexpr uint32_t kTileHeight = 128; - constexpr uint32_t kTileWidth = kTileSize / kTileHeight; constexpr uint32_t kLinearAlignment = 4 * kPageSize; + // There are two tile modes: TileYS (64KB per tile) and TileYf (4KB per tile). TileYS is used + // here because it may have more paddings and therefore requires more extra layers to work + // around the bug. + constexpr uint32_t kTileSize = 16 * kPageSize; + + // Tile's width and height vary according to format bit-wise (formatBytesPerBlock) + uint32_t tileHeight = 0; + switch (formatBytesPerBlock) { + case 1: + tileHeight = 256; + break; + case 2: + case 4: + tileHeight = 128; + break; + case 8: + case 16: + tileHeight = 64; + break; + default: + UNREACHABLE(); + } + uint32_t tileWidth = kTileSize / tileHeight; + uint64_t layerxSamples = arrayLayerCount * sampleCount; if (layerxSamples <= 1) { @@ -254,8 +275,8 @@ uint32_t ComputeExtraArraySizeForIntelGen12(uint32_t width, // Texture should be aligned on both tile width (512 bytes) and tile height (128 rows) on Intel // Gen12 GPU - uint32_t mainTileCols = Align(totalWidth, kTileWidth) / kTileWidth; - uint32_t mainTileRows = Align(totalHeight, kTileHeight) / kTileHeight; + uint32_t mainTileCols = Align(totalWidth, tileWidth) / tileWidth; + uint32_t mainTileRows = Align(totalHeight, tileHeight) / tileHeight; uint64_t mainTileCount = mainTileCols * mainTileRows; // There is a bug in Intel old drivers to compute the auxiliary memory size (auxSize) of the diff --git a/src/dawn/tests/end2end/TextureCorruptionTests.cpp b/src/dawn/tests/end2end/TextureCorruptionTests.cpp index 5e51c7dae3..f08446af04 100644 --- a/src/dawn/tests/end2end/TextureCorruptionTests.cpp +++ b/src/dawn/tests/end2end/TextureCorruptionTests.cpp @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include #include #include "dawn/common/Math.h" @@ -24,8 +25,6 @@ // tested texture is written via different methods, then read back from the texture and verify the // data. -constexpr wgpu::TextureFormat kFormat = wgpu::TextureFormat::RGBA8Unorm; - namespace { enum class WriteType { ClearTexture, @@ -39,6 +38,12 @@ enum class WriteType { // writing the loaded data }; +constexpr wgpu::TextureFormat kDefaultFormat = wgpu::TextureFormat::RGBA8Unorm; +constexpr uint32_t kDefaultHeight = 100u; +constexpr uint32_t kDefaultArrayLayerCount = 2u; +constexpr uint32_t kDefaultMipLevelCount = 1u; +constexpr WriteType kDefaultWriteType = WriteType::B2TCopy; + std::ostream& operator<<(std::ostream& o, WriteType writeType) { switch (writeType) { case WriteType::ClearTexture: @@ -66,20 +71,35 @@ std::ostream& operator<<(std::ostream& o, WriteType writeType) { using TextureFormat = wgpu::TextureFormat; using TextureWidth = uint32_t; using TextureHeight = uint32_t; +using ArrayLayerCount = uint32_t; +using MipLevelCount = uint32_t; -DAWN_TEST_PARAM_STRUCT(TextureCorruptionTestsParams, TextureWidth, TextureHeight, WriteType); +DAWN_TEST_PARAM_STRUCT(TextureCorruptionTestsParams, + TextureFormat, + TextureWidth, + TextureHeight, + ArrayLayerCount, + MipLevelCount, + WriteType); } // namespace class TextureCorruptionTests : public DawnTestWithParams { protected: - std::ostringstream& DoTest(wgpu::Texture texture, - const wgpu::Extent3D textureSize, - uint32_t depthOrArrayLayer, - uint32_t srcValue) { - uint32_t bytesPerTexel = utils::GetTexelBlockSizeInBytes(kFormat); - uint32_t bytesPerRow = Align(textureSize.width * bytesPerTexel, 256); - uint64_t bufferSize = bytesPerRow * textureSize.height; + std::ostringstream& DoSingleTest(wgpu::Texture texture, + const wgpu::Extent3D textureSize, + uint32_t depthOrArrayLayer, + uint32_t mipLevel, + uint32_t srcValue, + wgpu::TextureFormat format) { + wgpu::Extent3D levelSize = textureSize; + if (mipLevel > 0) { + levelSize.width = std::max(textureSize.width >> mipLevel, 1u); + levelSize.height = std::max(textureSize.height >> mipLevel, 1u); + } + uint32_t bytesPerTexel = utils::GetTexelBlockSizeInBytes(format); + uint32_t bytesPerRow = Align(levelSize.width * bytesPerTexel, 256); + uint64_t bufferSize = bytesPerRow * levelSize.height; wgpu::BufferDescriptor descriptor; descriptor.size = bufferSize; descriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; @@ -87,7 +107,7 @@ class TextureCorruptionTests : public DawnTestWithParams= sizeof(uint32_t)) { + elementNumPerTexel = bytesPerTexel / sizeof(uint32_t); + } else { + copyWidth = copyWidth * bytesPerTexel / sizeof(uint32_t); + } + uint32_t elementNumPerRow = bytesPerRow / sizeof(uint32_t); uint32_t elementNumInTotal = bufferSize / sizeof(uint32_t); std::vector data(elementNumInTotal, 0); for (uint32_t i = 0; i < copySize.height; ++i) { - for (uint32_t j = 0; j < copySize.width; ++j) { - if (type == WriteType::RenderFromTextureSample || - type == WriteType::RenderConstant) { - // Fill a simple and constant value (0xFFFFFFFF) in the whole buffer for - // texture sampling and rendering because either sampling operation will - // lead to precision loss or rendering a solid color is easier to implement and - // compare. - data[i * elementNumPerRow + j] = 0xFFFFFFFF; - } else if (type != WriteType::ClearTexture) { - data[i * elementNumPerRow + j] = srcValue; - srcValue++; + for (uint32_t j = 0; j < copyWidth; ++j) { + for (uint32_t k = 0; k < elementNumPerTexel; ++k) { + if (type == WriteType::RenderFromTextureSample || + type == WriteType::RenderConstant) { + // Fill a simple and constant value (0xFFFFFFFF) in the whole buffer for + // texture sampling and rendering because either sampling operation will + // lead to precision loss or rendering a solid color is easier to implement + // and compare. + ASSERT(elementNumPerTexel == 1); + data[i * elementNumPerRow + j] = 0xFFFFFFFF; + } else if (type != WriteType::ClearTexture) { + data[i * elementNumPerRow + j * elementNumPerTexel + k] = srcValue; + srcValue++; + } } } } @@ -139,9 +169,10 @@ class TextureCorruptionTests : public DawnTestWithParams textures; - uint32_t texNum = 2; - for (uint32_t i = 0; i < texNum; ++i) { - textures.push_back(Create2DTexture(textureSize)); - } + // Pre-allocate textures. The incorrect write type may corrupt neighboring textures or + // layers. + std::vector textures; + wgpu::TextureFormat format = GetParam().mTextureFormat; + uint32_t texNum = 2; + for (uint32_t i = 0; i < texNum; ++i) { + textures.push_back(Create2DTexture(textureSize, format, mipLevelCount)); + } - // Write data and verify the result one by one for every layer of every texture - uint32_t srcValue = 100000000; - for (uint32_t i = 0; i < texNum; ++i) { - for (uint32_t j = 0; j < depthOrArrayLayerCount; ++j) { - DoTest(textures[i], textureSize, j, srcValue) << "texNum: " << i << ", layer: " << j; - srcValue += 100000000; + // Most 2d-array textures being tested have only 2 layers. But if the texture has a lot of + // layers, select a few layers to test. + std::vector testedLayers = {0, 1}; + if (depthOrArrayLayerCount > 2) { + uint32_t divider = 4; + for (uint32_t i = 1; i <= divider; ++i) { + int32_t testedLayer = depthOrArrayLayerCount * i / divider - 1; + if (testedLayer > 1) { + testedLayers.push_back(testedLayer); + } + } + } + + // Write data and verify the result one by one for every layer of every texture + uint32_t srcValue = 100000000; + for (uint32_t i = 0; i < texNum; ++i) { + for (uint32_t j = 0; j < testedLayers.size(); ++j) { + for (uint32_t k = 0; k < mipLevelCount; ++k) { + DoSingleTest(textures[i], textureSize, testedLayers[j], k, srcValue, format) + << "texNum: " << i << ", layer: " << j << ", mip level: " << k; + srcValue += 100000000; + } + } } } +}; + +class TextureCorruptionTests_Format : public TextureCorruptionTests {}; + +TEST_P(TextureCorruptionTests_Format, Tests) { + DoTest(); } -DAWN_INSTANTIATE_TEST_P(TextureCorruptionTests, +DAWN_INSTANTIATE_TEST_P(TextureCorruptionTests_Format, {D3D12Backend()}, + {wgpu::TextureFormat::R8Unorm, wgpu::TextureFormat::RG8Unorm, + wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureFormat::RGBA16Uint, + wgpu::TextureFormat::RGBA32Uint}, + {100u, 600u, 1200u, 2400u, 4800u}, + {kDefaultHeight}, + {kDefaultArrayLayerCount}, + {kDefaultMipLevelCount}, + {kDefaultWriteType}); + +class TextureCorruptionTests_WidthAndHeight : public TextureCorruptionTests {}; + +TEST_P(TextureCorruptionTests_WidthAndHeight, Tests) { + DoTest(); +} + +DAWN_INSTANTIATE_TEST_P(TextureCorruptionTests_WidthAndHeight, + {D3D12Backend()}, + {kDefaultFormat}, {100u, 200u, 300u, 400u, 500u, 600u, 700u, 800u, 900u, 1000u, 1200u}, {100u, 200u}, + {kDefaultArrayLayerCount}, + {kDefaultMipLevelCount}, + {kDefaultWriteType}); + +class TextureCorruptionTests_ArrayLayer : public TextureCorruptionTests {}; + +TEST_P(TextureCorruptionTests_ArrayLayer, Tests) { + DoTest(); +} + +DAWN_INSTANTIATE_TEST_P(TextureCorruptionTests_ArrayLayer, + {D3D12Backend()}, + {kDefaultFormat}, + {100u, 600u, 1200u}, + {kDefaultHeight}, + {6u, 12u, 40u, 256u}, + {kDefaultMipLevelCount}, + {kDefaultWriteType}); + +class TextureCorruptionTests_Mipmap : public TextureCorruptionTests {}; + +TEST_P(TextureCorruptionTests_Mipmap, Tests) { + DoTest(); +} + +DAWN_INSTANTIATE_TEST_P(TextureCorruptionTests_Mipmap, + {D3D12Backend()}, + {kDefaultFormat}, + {100u, 600u, 1200u}, + {kDefaultHeight}, + {kDefaultArrayLayerCount}, + {2u, 6u}, + {kDefaultWriteType}); + +class TextureCorruptionTests_WriteType : public TextureCorruptionTests {}; + +TEST_P(TextureCorruptionTests_WriteType, Tests) { + DoTest(); +} + +DAWN_INSTANTIATE_TEST_P(TextureCorruptionTests_WriteType, + {D3D12Backend()}, + {kDefaultFormat}, + {100u, 600u, 1200u}, + {kDefaultHeight}, + {kDefaultArrayLayerCount}, + {kDefaultMipLevelCount}, {WriteType::ClearTexture, WriteType::WriteTexture, WriteType::B2TCopy, WriteType::RenderConstant, WriteType::RenderFromTextureSample, WriteType::RenderFromTextureLoad});