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 <noreply+kokoro@google.com>
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Yunchao He <yunchao.he@intel.com>
This commit is contained in:
Yunchao He 2022-10-19 15:19:13 +00:00 committed by Dawn LUCI CQ
parent f30e8dbe31
commit d27151d333
2 changed files with 204 additions and 58 deletions

View File

@ -236,11 +236,32 @@ uint32_t ComputeExtraArraySizeForIntelGen12(uint32_t width,
// <Surface Padding Requirement>.
// - Tile-based memory: the entire section of <Address Tiling Function Introduction>.
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

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <algorithm>
#include <vector>
#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<TextureCorruptionTestsParams> {
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<TextureCorruptionTestsP
wgpu::Buffer resultBuffer = device.CreateBuffer(&descriptor);
wgpu::ImageCopyTexture imageCopyTexture =
utils::CreateImageCopyTexture(texture, 0, {0, 0, depthOrArrayLayer});
utils::CreateImageCopyTexture(texture, mipLevel, {0, 0, depthOrArrayLayer});
wgpu::ImageCopyBuffer imageCopyBuffer =
utils::CreateImageCopyBuffer(buffer, 0, bytesPerRow);
wgpu::ImageCopyBuffer imageCopyResult =
@ -96,26 +116,36 @@ class TextureCorruptionTests : public DawnTestWithParams<TextureCorruptionTestsP
WriteType type = GetParam().mWriteType;
// Fill data into a buffer
wgpu::Extent3D copySize = {textureSize.width, textureSize.height, 1};
wgpu::Extent3D copySize = {levelSize.width, levelSize.height, 1};
// Data is stored in a uint32_t vector, so a single texel may require multiple vector
// elements for some formats
ASSERT(bytesPerTexel = sizeof(uint32_t));
// elements for some formats or multiple texels may be combined into one vector element.
uint32_t elementNumPerTexel = 1;
uint32_t copyWidth = copySize.width;
if (bytesPerTexel >= 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<uint32_t> 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<TextureCorruptionTestsP
case WriteType::RenderFromTextureSample:
case WriteType::RenderFromTextureLoad: {
// Write data into a single layer temp texture and read from this texture if needed
ASSERT(format == wgpu::TextureFormat::RGBA8Unorm);
wgpu::TextureView tempView;
if (type != WriteType::RenderConstant) {
wgpu::Texture tempTexture = Create2DTexture(copySize);
wgpu::Texture tempTexture = Create2DTexture(copySize, format, 1);
wgpu::ImageCopyTexture imageCopyTempTexture =
utils::CreateImageCopyTexture(tempTexture, 0, {0, 0, 0});
wgpu::TextureDataLayout textureDataLayout =
@ -153,13 +184,14 @@ class TextureCorruptionTests : public DawnTestWithParams<TextureCorruptionTestsP
// Write into the specified layer of a 2D array texture
wgpu::TextureViewDescriptor viewDesc;
viewDesc.format = kFormat;
viewDesc.format = format;
viewDesc.dimension = wgpu::TextureViewDimension::e2D;
viewDesc.baseMipLevel = 0;
viewDesc.mipLevelCount = 1;
viewDesc.baseArrayLayer = depthOrArrayLayer;
viewDesc.arrayLayerCount = 1;
CreatePipelineAndRender(texture.CreateView(&viewDesc), tempView, encoder, type);
CreatePipelineAndRender(texture.CreateView(&viewDesc), tempView, encoder, type,
format);
break;
}
default:
@ -176,9 +208,10 @@ class TextureCorruptionTests : public DawnTestWithParams<TextureCorruptionTestsP
void CreatePipelineAndRender(wgpu::TextureView renderView,
wgpu::TextureView samplerView,
wgpu::CommandEncoder encoder,
WriteType type) {
WriteType type,
wgpu::TextureFormat format) {
utils::ComboRenderPipelineDescriptor pipelineDescriptor;
pipelineDescriptor.cTargets[0].format = kFormat;
pipelineDescriptor.cTargets[0].format = format;
// Draw the whole texture (a rectangle) via two triangles
pipelineDescriptor.vertex.module = utils::CreateShaderModule(device, R"(
@ -239,46 +272,138 @@ class TextureCorruptionTests : public DawnTestWithParams<TextureCorruptionTestsP
pass.End();
}
wgpu::Texture Create2DTexture(const wgpu::Extent3D size) {
wgpu::Texture Create2DTexture(const wgpu::Extent3D size,
wgpu::TextureFormat format,
uint32_t mipLevelCount) {
wgpu::TextureDescriptor texDesc = {};
texDesc.dimension = wgpu::TextureDimension::e2D;
texDesc.size = size;
texDesc.mipLevelCount = 1;
texDesc.format = kFormat;
texDesc.mipLevelCount = mipLevelCount;
texDesc.format = format;
texDesc.usage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc |
wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::TextureBinding;
return device.CreateTexture(&texDesc);
}
};
TEST_P(TextureCorruptionTests, Tests) {
DAWN_SUPPRESS_TEST_IF(IsWARP());
uint32_t width = GetParam().mTextureWidth;
uint32_t height = GetParam().mTextureHeight;
uint32_t depthOrArrayLayerCount = 2;
wgpu::Extent3D textureSize = {width, height, depthOrArrayLayerCount};
void DoTest() {
DAWN_SUPPRESS_TEST_IF(IsWARP());
uint32_t width = GetParam().mTextureWidth;
uint32_t height = GetParam().mTextureHeight;
uint32_t depthOrArrayLayerCount = GetParam().mArrayLayerCount;
uint32_t mipLevelCount = GetParam().mMipLevelCount;
wgpu::Extent3D textureSize = {width, height, depthOrArrayLayerCount};
// Pre-allocate textures. The incorrect write type may corrupt neighboring textures or layers.
std::vector<wgpu::Texture> 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<wgpu::Texture> 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<uint32_t> 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});