Add more tests for texture corruption: msaa and depth stencil formats

2D array textures with non-color formats like depth/stencil formats
are always fine. Workaround is not needed.

Multisample textures are treated as array textures from the
perspective of texture memory layout on Intel Gen12 and each sample
acts like a layer. However, multisample textures are fine.
Workaround is not needed.

Bug: dawn:1507

Change-Id: I1e5cd6a4e46503f67e4c1ffe2133e2e8fb121016
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/113740
Reviewed-by: Austin Eng <enga@chromium.org>
Kokoro: Austin Eng <enga@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Austin Eng <enga@chromium.org>
This commit is contained in:
Yunchao He 2022-12-15 00:52:53 +00:00 committed by Dawn LUCI CQ
parent 87a1724368
commit 69b7f4a61c
8 changed files with 139 additions and 40 deletions

View File

@ -283,12 +283,12 @@ static constexpr ToggleEnumAndInfoList kToggleNameAndInfoList = {{
"Toggle is enabled by default on the D3D12 platforms where CastingFullyTypedFormatSupported "
"is false.",
"https://crbug.com/dawn/1276"}},
{Toggle::D3D12AllocateExtraMemoryFor2DArrayTexture,
{"d3d12_allocate_extra_memory_for_2d_array_texture",
"Memory allocation for 2D array texture may be smaller than it should be on D3D12 on some "
"Intel devices. So texture access can be out-of-bound, which may cause critical security "
"issue. We can workaround this security issue via allocating extra memory and limiting its "
"access in itself.",
{Toggle::D3D12AllocateExtraMemoryFor2DArrayColorTexture,
{"d3d12_allocate_extra_memory_for_2d_array_color_texture",
"Memory allocation for 2D array color texture may be smaller than it should be on D3D12 on "
"some Intel devices. So texture access can be out-of-bound, which may cause critical "
"security issue. We can workaround this security issue via allocating extra memory and "
"limiting its access in itself.",
"https://crbug.com/dawn/949"}},
{Toggle::D3D12UseTempBufferInDepthStencilTextureAndBufferCopyWithNonZeroBufferOffset,
{"d3d12_use_temp_buffer_in_depth_stencil_texture_and_buffer_copy_with_non_zero_buffer_offset",

View File

@ -76,7 +76,7 @@ enum class Toggle {
D3D12ForceClearCopyableDepthStencilTextureOnCreation,
D3D12DontSetClearValueOnDepthTextureCreation,
D3D12AlwaysUseTypelessFormatsForCastableTexture,
D3D12AllocateExtraMemoryFor2DArrayTexture,
D3D12AllocateExtraMemoryFor2DArrayColorTexture,
D3D12UseTempBufferInDepthStencilTextureAndBufferCopyWithNonZeroBufferOffset,
ApplyClearBigIntegerColorValueWithDraw,
MetalUseMockBlitEncoderForWriteTimestamp,

View File

@ -724,7 +724,7 @@ void Device::InitTogglesFromDriver() {
const gpu_info::DriverVersion kFixedDriverVersion = {30, 0, 101, 1692};
if (gpu_info::CompareWindowsDriverVersion(vendorId, GetAdapter()->GetDriverVersion(),
kFixedDriverVersion) == -1) {
SetToggle(Toggle::D3D12AllocateExtraMemoryFor2DArrayTexture, true);
SetToggle(Toggle::D3D12AllocateExtraMemoryFor2DArrayColorTexture, true);
}
}

View File

@ -229,7 +229,7 @@ uint32_t ComputeExtraArraySizeForIntelGen12(uint32_t width,
uint32_t arrayLayerCount,
uint32_t mipLevelCount,
uint32_t sampleCount,
uint32_t formatBytesPerBlock) {
uint32_t colorFormatBytesPerBlock) {
// For details about texture memory layout on Intel Gen12 GPU, read
// https://01.org/sites/default/files/documentation/intel-gfx-prm-osrc-tgl-vol05-memory_data_formats.pdf.
// - Texture memory layout: from <Surface Memory Organizations> to
@ -243,9 +243,9 @@ uint32_t ComputeExtraArraySizeForIntelGen12(uint32_t width,
// around the bug.
constexpr uint32_t kTileSize = 16 * kPageSize;
// Tile's width and height vary according to format bit-wise (formatBytesPerBlock)
// Tile's width and height vary according to format bit-wise (colorFormatBytesPerBlock)
uint32_t tileHeight = 0;
switch (formatBytesPerBlock) {
switch (colorFormatBytesPerBlock) {
case 1:
tileHeight = 256;
break;
@ -270,7 +270,7 @@ uint32_t ComputeExtraArraySizeForIntelGen12(uint32_t width,
uint32_t columnPitch = GetColumnPitch(height, mipLevelCount);
uint64_t totalWidth = width * formatBytesPerBlock;
uint64_t totalWidth = width * colorFormatBytesPerBlock;
uint64_t totalHeight = columnPitch * layerxSamples;
// Texture should be aligned on both tile width (512 bytes) and tile height (128 rows) on Intel
@ -280,17 +280,17 @@ uint32_t ComputeExtraArraySizeForIntelGen12(uint32_t width,
uint64_t mainTileCount = mainTileCols * mainTileRows;
// There is a bug in Intel old drivers to compute the auxiliary memory size (auxSize) of the
// texture, which is calculated from the main memory size (mainSize) of the texture. Note that
// memory allocation for mainSize itself is correct. But during memory allocation for auxSize,
// it re-caculated mainSize and did it in a wrong way. The incorrect algorithm doesn't respect
// alignment requirements from tile-based texture memory layout. It just simple aligned to a
// constant value (16K) for each sample and layer.
// color texture, which is calculated from the main memory size (mainSize) of the texture. Note
// that memory allocation for mainSize itself is correct. But during memory allocation for
// auxSize, it re-caculated mainSize and did it in a wrong way. The incorrect algorithm doesn't
// respect alignment requirements from tile-based texture memory layout. It just simple aligned
// to a constant value (16K) for each sample and layer.
uint64_t expectedMainSize = mainTileCount * kTileSize;
uint64_t actualMainSize = Align(columnPitch * totalWidth, kLinearAlignment) * layerxSamples;
// If the incorrect mainSize calculation lead to less-than-expected auxSize, texture corruption
// is very likely to happen for any texture access like texture copy, rendering, sampling, etc.
// So we have to allocate a few more extra layers to offset the less-than-expected auxSize.
// is very likely to happen for any color texture access like texture copy, rendering, sampling,
// etc. So we have to allocate a few more extra layers to offset the less-than-expected auxSize.
// However, it is fine if the incorrect mainSize calculation doesn't introduce less auxSize. For
// example, if correct mainSize is 3.8M, it requires 4 pages of auxSize (16K). Any incorrect
// mainSize between 3.0+ M and 4.0M also requires 16K auxSize according to the calculation:
@ -344,7 +344,7 @@ ResultOrError<ResourceHeapAllocation> ResourceAllocatorManager::AllocateMemory(
D3D12_HEAP_TYPE heapType,
const D3D12_RESOURCE_DESC& resourceDescriptor,
D3D12_RESOURCE_STATES initialUsage,
uint32_t formatBytesPerBlock,
uint32_t colorFormatBytesPerBlock,
bool forceAllocateAsCommittedResource) {
// In order to suppress a warning in the D3D12 debug layer, we need to specify an
// optimized clear value. As there are no negative consequences when picking a mismatched
@ -357,16 +357,20 @@ ResultOrError<ResourceHeapAllocation> ResourceAllocatorManager::AllocateMemory(
optimizedClearValue = &zero;
}
// If we are allocating memory for a 2D array texture on D3D12 backend, we need to allocate
// extra layers on some Intel Gen12 devices, see crbug.com/dawn/949 for details.
// If we are allocating memory for a 2D array texture with a color format on D3D12 backend,
// we need to allocate extra layers on some Intel Gen12 devices, see crbug.com/dawn/949
// for details.
D3D12_RESOURCE_DESC revisedDescriptor = resourceDescriptor;
if (mDevice->IsToggleEnabled(Toggle::D3D12AllocateExtraMemoryFor2DArrayTexture) &&
if (mDevice->IsToggleEnabled(Toggle::D3D12AllocateExtraMemoryFor2DArrayColorTexture) &&
resourceDescriptor.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE2D &&
resourceDescriptor.DepthOrArraySize > 1) {
resourceDescriptor.DepthOrArraySize > 1 && colorFormatBytesPerBlock > 0) {
// Multisample textures have one layer at most. Only non-multisample textures need the
// workaround.
ASSERT(revisedDescriptor.SampleDesc.Count <= 1);
revisedDescriptor.DepthOrArraySize += ComputeExtraArraySizeForIntelGen12(
resourceDescriptor.Width, resourceDescriptor.Height,
resourceDescriptor.DepthOrArraySize, resourceDescriptor.MipLevels,
resourceDescriptor.SampleDesc.Count, formatBytesPerBlock);
resourceDescriptor.SampleDesc.Count, colorFormatBytesPerBlock);
}
// TODO(crbug.com/dawn/849): Conditionally disable sub-allocation.
@ -614,7 +618,7 @@ uint64_t ResourceAllocatorManager::GetResourcePadding(
const D3D12_RESOURCE_DESC& resourceDescriptor) const {
// If we are allocating memory for a 2D array texture on D3D12 backend, we need to allocate
// extra memory on some devices, see crbug.com/dawn/949 for details.
if (mDevice->IsToggleEnabled(Toggle::D3D12AllocateExtraMemoryFor2DArrayTexture) &&
if (mDevice->IsToggleEnabled(Toggle::D3D12AllocateExtraMemoryFor2DArrayColorTexture) &&
resourceDescriptor.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE2D &&
resourceDescriptor.DepthOrArraySize > 1) {
return kExtraMemoryToMitigateTextureCorruption;

View File

@ -65,7 +65,7 @@ class ResourceAllocatorManager {
D3D12_HEAP_TYPE heapType,
const D3D12_RESOURCE_DESC& resourceDescriptor,
D3D12_RESOURCE_STATES initialUsage,
uint32_t formatBytesPerBlock,
uint32_t colorFormatBytesPerBlock,
bool forceAllocateAsCommittedResource = false);
void DeallocateMemory(ResourceHeapAllocation& allocation);

View File

@ -1627,6 +1627,32 @@ testing::AssertionResult CheckImpl<float, uint16_t>(const float& expected,
} // namespace
template <typename T>
ExpectConstant<T>::ExpectConstant(T constant) : mConstant(constant) {}
template <typename T>
uint32_t ExpectConstant<T>::DataSize() {
return sizeof(T);
}
template <typename T>
testing::AssertionResult ExpectConstant<T>::Check(const void* data, size_t size) {
DAWN_ASSERT(size % DataSize() == 0 && size > 0);
const T* actual = static_cast<const T*>(data);
for (size_t i = 0; i < size / DataSize(); ++i) {
if (actual[i] != mConstant) {
return testing::AssertionFailure()
<< "Expected data[" << i << "] to match constant value " << mConstant
<< ", actual " << actual[i] << std::endl;
}
}
return testing::AssertionSuccess();
}
template class ExpectConstant<float>;
template <typename T, typename U>
testing::AssertionResult ExpectEq<T, U>::Check(const void* data, size_t size) {
DAWN_ASSERT(size == sizeof(U) * mExpected.size());

View File

@ -134,6 +134,8 @@ namespace detail {
class Expectation;
class CustomTextureExpectation;
template <typename T>
class ExpectConstant;
template <typename T, typename U = T>
class ExpectEq;
template <typename T>
@ -780,6 +782,19 @@ class Expectation {
virtual testing::AssertionResult Check(const void* data, size_t size) = 0;
};
template <typename T>
class ExpectConstant : public Expectation {
public:
explicit ExpectConstant(T constant);
uint32_t DataSize();
testing::AssertionResult Check(const void* data, size_t size) override;
private:
T mConstant;
};
extern template class ExpectConstant<float>;
// Expectation that checks the data is equal to some expected values.
// T - expected value Type
// U - actual value Type (defaults = T)

View File

@ -42,6 +42,7 @@ constexpr wgpu::TextureFormat kDefaultFormat = wgpu::TextureFormat::RGBA8Unorm;
constexpr uint32_t kDefaultHeight = 100u;
constexpr uint32_t kDefaultArrayLayerCount = 2u;
constexpr uint32_t kDefaultMipLevelCount = 1u;
constexpr uint32_t kDefaultSampleCount = 1u;
constexpr WriteType kDefaultWriteType = WriteType::B2TCopy;
std::ostream& operator<<(std::ostream& o, WriteType writeType) {
@ -73,6 +74,7 @@ using TextureWidth = uint32_t;
using TextureHeight = uint32_t;
using ArrayLayerCount = uint32_t;
using MipLevelCount = uint32_t;
using SampleCount = uint32_t;
DAWN_TEST_PARAM_STRUCT(TextureCorruptionTestsParams,
TextureFormat,
@ -80,18 +82,20 @@ DAWN_TEST_PARAM_STRUCT(TextureCorruptionTestsParams,
TextureHeight,
ArrayLayerCount,
MipLevelCount,
SampleCount,
WriteType);
} // namespace
class TextureCorruptionTests : public DawnTestWithParams<TextureCorruptionTestsParams> {
protected:
std::ostringstream& DoSingleTest(wgpu::Texture texture,
const wgpu::Extent3D textureSize,
uint32_t depthOrArrayLayer,
uint32_t mipLevel,
uint32_t srcValue,
wgpu::TextureFormat format) {
virtual std::ostringstream& DoSingleTest(wgpu::Texture texture,
const wgpu::Extent3D textureSize,
uint32_t depthOrArrayLayer,
uint32_t mipLevel,
uint32_t sampleCount,
uint32_t srcValue,
wgpu::TextureFormat format) {
wgpu::Extent3D levelSize = textureSize;
if (mipLevel > 0) {
levelSize.width = std::max(textureSize.width >> mipLevel, 1u);
@ -172,7 +176,7 @@ class TextureCorruptionTests : public DawnTestWithParams<TextureCorruptionTestsP
ASSERT(format == wgpu::TextureFormat::RGBA8Unorm);
wgpu::TextureView tempView;
if (type != WriteType::RenderConstant) {
wgpu::Texture tempTexture = Create2DTexture(copySize, format, 1);
wgpu::Texture tempTexture = Create2DTexture(copySize, format, 1, 1);
wgpu::ImageCopyTexture imageCopyTempTexture =
utils::CreateImageCopyTexture(tempTexture, 0, {0, 0, 0});
wgpu::TextureDataLayout textureDataLayout =
@ -274,12 +278,14 @@ class TextureCorruptionTests : public DawnTestWithParams<TextureCorruptionTestsP
wgpu::Texture Create2DTexture(const wgpu::Extent3D size,
wgpu::TextureFormat format,
uint32_t mipLevelCount) {
uint32_t mipLevelCount,
uint32_t sampleCount) {
wgpu::TextureDescriptor texDesc = {};
texDesc.dimension = wgpu::TextureDimension::e2D;
texDesc.size = size;
texDesc.mipLevelCount = mipLevelCount;
texDesc.format = format;
texDesc.sampleCount = sampleCount;
texDesc.usage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc |
wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::TextureBinding;
return device.CreateTexture(&texDesc);
@ -291,6 +297,7 @@ class TextureCorruptionTests : public DawnTestWithParams<TextureCorruptionTestsP
uint32_t height = GetParam().mTextureHeight;
uint32_t depthOrArrayLayerCount = GetParam().mArrayLayerCount;
uint32_t mipLevelCount = GetParam().mMipLevelCount;
uint32_t sampleCount = GetParam().mSampleCount;
wgpu::Extent3D textureSize = {width, height, depthOrArrayLayerCount};
// Pre-allocate textures. The incorrect write type may corrupt neighboring textures or
@ -299,13 +306,20 @@ class TextureCorruptionTests : public DawnTestWithParams<TextureCorruptionTestsP
wgpu::TextureFormat format = GetParam().mTextureFormat;
uint32_t texNum = 2;
for (uint32_t i = 0; i < texNum; ++i) {
textures.push_back(Create2DTexture(textureSize, format, mipLevelCount));
textures.push_back(Create2DTexture(textureSize, format, mipLevelCount, sampleCount));
}
// Multisample textures have only 1 layer, while other textures being tested have 2 layers
// at least.
std::vector<uint32_t> testedLayers = {0};
if (sampleCount == 1) {
testedLayers.push_back(1);
}
// 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) {
ASSERT(sampleCount == 1);
uint32_t divider = 4;
for (uint32_t i = 1; i <= divider; ++i) {
int32_t testedLayer = depthOrArrayLayerCount * i / divider - 1;
@ -320,7 +334,8 @@ class TextureCorruptionTests : public DawnTestWithParams<TextureCorruptionTestsP
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)
DoSingleTest(textures[i], textureSize, testedLayers[j], k, sampleCount,
srcValue, format)
<< "texNum: " << i << ", layer: " << j << ", mip level: " << k;
srcValue += 100000000;
}
@ -339,12 +354,14 @@ DAWN_INSTANTIATE_TEST_P(TextureCorruptionTests_Format,
{D3D12Backend()},
{wgpu::TextureFormat::R8Unorm, wgpu::TextureFormat::RG8Unorm,
wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureFormat::RGBA16Uint,
wgpu::TextureFormat::RGBA32Uint},
wgpu::TextureFormat::RGBA32Uint, wgpu::TextureFormat::Depth16Unorm,
wgpu::TextureFormat::Stencil8},
{100u, 600u, 1200u, 2400u, 4800u},
{kDefaultHeight},
{kDefaultArrayLayerCount},
{kDefaultMipLevelCount},
{kDefaultWriteType});
{kDefaultSampleCount},
{WriteType::ClearTexture});
class TextureCorruptionTests_WidthAndHeight : public TextureCorruptionTests {};
@ -359,6 +376,7 @@ DAWN_INSTANTIATE_TEST_P(TextureCorruptionTests_WidthAndHeight,
{100u, 200u},
{kDefaultArrayLayerCount},
{kDefaultMipLevelCount},
{kDefaultSampleCount},
{kDefaultWriteType});
class TextureCorruptionTests_ArrayLayer : public TextureCorruptionTests {};
@ -374,6 +392,7 @@ DAWN_INSTANTIATE_TEST_P(TextureCorruptionTests_ArrayLayer,
{kDefaultHeight},
{6u, 12u, 40u, 256u},
{kDefaultMipLevelCount},
{kDefaultSampleCount},
{kDefaultWriteType});
class TextureCorruptionTests_Mipmap : public TextureCorruptionTests {};
@ -389,8 +408,42 @@ DAWN_INSTANTIATE_TEST_P(TextureCorruptionTests_Mipmap,
{kDefaultHeight},
{kDefaultArrayLayerCount},
{2u, 6u},
{kDefaultSampleCount},
{kDefaultWriteType});
class TextureCorruptionTests_Multisample : public TextureCorruptionTests {
protected:
std::ostringstream& DoSingleTest(wgpu::Texture texture,
const wgpu::Extent3D textureSize,
uint32_t depthOrArrayLayer,
uint32_t mipLevel,
uint32_t sampleCount,
uint32_t srcValue,
wgpu::TextureFormat format) override {
ASSERT(depthOrArrayLayer == 0);
ASSERT(mipLevel == 0);
uint32_t bytesPerTexel = utils::GetTexelBlockSizeInBytes(format);
return ExpectMultisampledFloatData(texture, textureSize.width, textureSize.height,
bytesPerTexel, sampleCount, 0, mipLevel,
new detail::ExpectConstant<float>(0));
}
};
TEST_P(TextureCorruptionTests_Multisample, Tests) {
DoTest();
}
DAWN_INSTANTIATE_TEST_P(TextureCorruptionTests_Multisample,
{D3D12Backend()},
{kDefaultFormat},
{100u, 200u, 300u, 400u, 500u, 600u, 700u, 800u, 900u, 1000u, 1200u},
{100u, 200u},
{1u},
{kDefaultMipLevelCount},
{4u},
{WriteType::ClearTexture});
class TextureCorruptionTests_WriteType : public TextureCorruptionTests {};
TEST_P(TextureCorruptionTests_WriteType, Tests) {
@ -404,6 +457,7 @@ DAWN_INSTANTIATE_TEST_P(TextureCorruptionTests_WriteType,
{kDefaultHeight},
{kDefaultArrayLayerCount},
{kDefaultMipLevelCount},
{kDefaultSampleCount},
{WriteType::ClearTexture, WriteType::WriteTexture, WriteType::B2TCopy,
WriteType::RenderConstant, WriteType::RenderFromTextureSample,
WriteType::RenderFromTextureLoad});