Add more tests to RequiredBufferSizeInCopyTests

The new tests reveal that this incorrect calculation of buffer size
requirement on D3D12 exist only when:
  - the texture dimension is 3D, and
  - there are rowsPerImage paddings (imageLayout.rowsPerImage >
  copySize.height), and
  - it's copying multiple depth images (copySize.depthOrArrayLayers > 1).

The new tests can guide and narrow down the workaround for this issue.

Bug: dawn:1289
Change-Id: I4136d6e3aa3680a65739bfdd402dc0d46debfd8d
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/87162
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Yunchao He <yunchao.he@intel.com>
This commit is contained in:
Yunchao He 2022-04-19 21:57:45 +00:00 committed by Dawn LUCI CQ
parent 799420d347
commit 8f28d21176
1 changed files with 67 additions and 30 deletions

View File

@ -17,11 +17,9 @@
#include "dawn/utils/TestUtils.h" #include "dawn/utils/TestUtils.h"
#include "dawn/utils/WGPUHelpers.h" #include "dawn/utils/WGPUHelpers.h"
constexpr static wgpu::Extent3D kCopySize = {1, 1, 2}; constexpr static wgpu::Extent3D kCopySize = {1, 1};
constexpr static uint64_t kOffset = 0; constexpr static uint64_t kOffset = 0;
constexpr static uint64_t kBytesPerRow = 256; constexpr static uint64_t kBytesPerRow = 256;
constexpr static uint64_t kRowsPerImagePadding = 1;
constexpr static uint64_t kRowsPerImage = kRowsPerImagePadding + kCopySize.height;
constexpr static wgpu::TextureFormat kFormat = wgpu::TextureFormat::RGBA8Unorm; constexpr static wgpu::TextureFormat kFormat = wgpu::TextureFormat::RGBA8Unorm;
constexpr static uint32_t kBytesPerBlock = 4; constexpr static uint32_t kBytesPerBlock = 4;
@ -40,7 +38,14 @@ namespace {
return o; return o;
} }
DAWN_TEST_PARAM_STRUCT(RequiredBufferSizeInCopyTestsParams, Type); using TextureDimension = wgpu::TextureDimension;
using CopyDepth = uint32_t;
using ExtraRowsPerImage = uint64_t;
DAWN_TEST_PARAM_STRUCT(RequiredBufferSizeInCopyTestsParams,
Type,
TextureDimension,
CopyDepth,
ExtraRowsPerImage);
} // namespace } // namespace
// Tests in this file are used to expose an error on D3D12 about required minimum buffer size. // Tests in this file are used to expose an error on D3D12 about required minimum buffer size.
@ -67,19 +72,24 @@ namespace {
// It looks like D3D12 requires unnecessary buffer storage for rowsPerImagePadding in the last // It looks like D3D12 requires unnecessary buffer storage for rowsPerImagePadding in the last
// image. It does respect bytesPerRowPadding in the last row and doesn't require storage for // image. It does respect bytesPerRowPadding in the last row and doesn't require storage for
// that part, though. // that part, though.
//
// Further tests reveal that this incorrect calculation exist only when the texture dimension
// is 3D, and there are rowsPerImage paddings, and there are multiple depth images.
class RequiredBufferSizeInCopyTests class RequiredBufferSizeInCopyTests
: public DawnTestWithParams<RequiredBufferSizeInCopyTestsParams> { : public DawnTestWithParams<RequiredBufferSizeInCopyTestsParams> {
protected: protected:
void DoTest(const uint64_t bufferSize) { void DoTest(const uint64_t bufferSize,
const wgpu::Extent3D copySize,
const uint64_t rowsPerImage) {
wgpu::BufferDescriptor descriptor; wgpu::BufferDescriptor descriptor;
descriptor.size = bufferSize; descriptor.size = bufferSize;
descriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; descriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst;
wgpu::Buffer buffer = device.CreateBuffer(&descriptor); wgpu::Buffer buffer = device.CreateBuffer(&descriptor);
wgpu::TextureDescriptor texDesc = {}; wgpu::TextureDescriptor texDesc = {};
texDesc.dimension = wgpu::TextureDimension::e3D; texDesc.dimension = GetParam().mTextureDimension;
texDesc.size = kCopySize; texDesc.size = copySize;
texDesc.format = kFormat; texDesc.format = kFormat;
texDesc.usage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc; texDesc.usage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc;
wgpu::Texture texture = device.CreateTexture(&texDesc); wgpu::Texture texture = device.CreateTexture(&texDesc);
@ -87,19 +97,19 @@ class RequiredBufferSizeInCopyTests
wgpu::ImageCopyTexture imageCopyTexture = wgpu::ImageCopyTexture imageCopyTexture =
utils::CreateImageCopyTexture(texture, 0, {0, 0, 0}); utils::CreateImageCopyTexture(texture, 0, {0, 0, 0});
wgpu::ImageCopyBuffer imageCopyBuffer = wgpu::ImageCopyBuffer imageCopyBuffer =
utils::CreateImageCopyBuffer(buffer, kOffset, kBytesPerRow, kRowsPerImage); utils::CreateImageCopyBuffer(buffer, kOffset, kBytesPerRow, rowsPerImage);
// Initialize copied data and set expected data for buffer and texture. // Initialize copied data and set expected data for buffer and texture.
ASSERT(sizeof(uint32_t) == kBytesPerBlock); ASSERT(sizeof(uint32_t) == kBytesPerBlock);
uint32_t numOfBufferElements = bufferSize / kBytesPerBlock; uint32_t numOfBufferElements = bufferSize / kBytesPerBlock;
std::vector<uint32_t> data(numOfBufferElements, 1); std::vector<uint32_t> data(numOfBufferElements, 1);
std::vector<uint32_t> expectedBufferData(numOfBufferElements, 0); std::vector<uint32_t> expectedBufferData(numOfBufferElements, 0);
std::vector<uint32_t> expectedTextureData(kCopySize.depthOrArrayLayers, 0); std::vector<uint32_t> expectedTextureData(copySize.depthOrArrayLayers, 0);
// Initialize the first element on every image to be 0x80808080 // Initialize the first element on every image to be 0x80808080
uint64_t imageSize = kBytesPerRow * kRowsPerImage; uint64_t imageSize = kBytesPerRow * rowsPerImage;
ASSERT(bufferSize >= (imageSize * (kCopySize.depthOrArrayLayers - 1) + kBytesPerBlock)); ASSERT(bufferSize >= (imageSize * (copySize.depthOrArrayLayers - 1) + kBytesPerBlock));
uint32_t numOfImageElements = imageSize / kBytesPerBlock; uint32_t numOfImageElements = imageSize / kBytesPerBlock;
for (uint32_t i = 0; i < kCopySize.depthOrArrayLayers; ++i) { for (uint32_t i = 0; i < copySize.depthOrArrayLayers; ++i) {
data[i * numOfImageElements] = 0x80808080; data[i * numOfImageElements] = 0x80808080;
expectedBufferData[i * numOfImageElements] = 0x80808080; expectedBufferData[i * numOfImageElements] = 0x80808080;
expectedTextureData[i] = 0x80808080; expectedTextureData[i] = 0x80808080;
@ -110,17 +120,17 @@ class RequiredBufferSizeInCopyTests
switch (GetParam().mType) { switch (GetParam().mType) {
case Type::T2BCopy: { case Type::T2BCopy: {
wgpu::TextureDataLayout textureDataLayout = wgpu::TextureDataLayout textureDataLayout =
utils::CreateTextureDataLayout(kOffset, kBytesPerRow, kRowsPerImage); utils::CreateTextureDataLayout(kOffset, kBytesPerRow, rowsPerImage);
queue.WriteTexture(&imageCopyTexture, data.data(), bufferSize, &textureDataLayout, queue.WriteTexture(&imageCopyTexture, data.data(), bufferSize, &textureDataLayout,
&kCopySize); &copySize);
encoder.CopyTextureToBuffer(&imageCopyTexture, &imageCopyBuffer, &kCopySize); encoder.CopyTextureToBuffer(&imageCopyTexture, &imageCopyBuffer, &copySize);
break; break;
} }
case Type::B2TCopy: case Type::B2TCopy:
queue.WriteBuffer(buffer, 0, data.data(), bufferSize); queue.WriteBuffer(buffer, 0, data.data(), bufferSize);
encoder.CopyBufferToTexture(&imageCopyBuffer, &imageCopyTexture, &kCopySize); encoder.CopyBufferToTexture(&imageCopyBuffer, &imageCopyTexture, &copySize);
break; break;
} }
wgpu::CommandBuffer commands = encoder.Finish(); wgpu::CommandBuffer commands = encoder.Finish();
@ -132,7 +142,7 @@ class RequiredBufferSizeInCopyTests
EXPECT_BUFFER_U32_RANGE_EQ(expectedBufferData.data(), buffer, 0, bufferSize / 4); EXPECT_BUFFER_U32_RANGE_EQ(expectedBufferData.data(), buffer, 0, bufferSize / 4);
break; break;
case Type::B2TCopy: case Type::B2TCopy:
EXPECT_TEXTURE_EQ(expectedTextureData.data(), texture, {0, 0, 0}, kCopySize); EXPECT_TEXTURE_EQ(expectedTextureData.data(), texture, {0, 0, 0}, copySize);
break; break;
} }
} }
@ -140,36 +150,63 @@ class RequiredBufferSizeInCopyTests
// The buffer contains full data on the last image and has storage for all kinds of paddings. // The buffer contains full data on the last image and has storage for all kinds of paddings.
TEST_P(RequiredBufferSizeInCopyTests, AbundantBufferSize) { TEST_P(RequiredBufferSizeInCopyTests, AbundantBufferSize) {
uint64_t size = kOffset + kBytesPerRow * kRowsPerImage * kCopySize.depthOrArrayLayers; wgpu::Extent3D copySize = kCopySize;
DoTest(size); copySize.depthOrArrayLayers = GetParam().mCopyDepth;
const uint64_t extraRowsPerImage = GetParam().mExtraRowsPerImage;
const uint64_t rowsPerImage = extraRowsPerImage + copySize.height;
uint64_t size = kOffset + kBytesPerRow * rowsPerImage * copySize.depthOrArrayLayers;
DoTest(size, copySize, rowsPerImage);
} }
// The buffer has storage for rowsPerImage paddings on the last image but not bytesPerRow // The buffer has storage for rowsPerImage paddings on the last image but not bytesPerRow
// paddings on the last row, which is exactly what D3D12 requires. See the comments at the // paddings on the last row, which is exactly what D3D12 requires. See the comments at the
// beginning of class RequiredBufferSizeInCopyTests for details. // beginning of class RequiredBufferSizeInCopyTests for details.
TEST_P(RequiredBufferSizeInCopyTests, BufferSizeOnBoundary) { TEST_P(RequiredBufferSizeInCopyTests, BufferSizeOnBoundary) {
uint64_t size = kOffset + kBytesPerRow * kRowsPerImage * (kCopySize.depthOrArrayLayers - 1) + wgpu::Extent3D copySize = kCopySize;
kBytesPerRow * (kRowsPerImage - 1) + kBytesPerBlock * kCopySize.width; copySize.depthOrArrayLayers = GetParam().mCopyDepth;
DoTest(size); const uint64_t extraRowsPerImage = GetParam().mExtraRowsPerImage;
// If there are no rowsPerImage paddings, buffer size required by D3D12 will be exactly the
// same with the minimum size required by WebGPU spec, which can be covered by tests below
// at MinimunBufferSize.
if (extraRowsPerImage > 0) {
const uint64_t rowsPerImage = extraRowsPerImage + copySize.height;
// TODO(crbug.com/dawn/1278, 1288, 1289): Required buffer size for copy is wrong on D3D12. uint64_t size = kOffset + kBytesPerRow * rowsPerImage * (copySize.depthOrArrayLayers - 1) +
DAWN_SUPPRESS_TEST_IF(IsD3D12()); kBytesPerRow * (rowsPerImage - 1) + kBytesPerBlock * copySize.width;
size -= kBytesPerBlock; DoTest(size, copySize, rowsPerImage);
DoTest(size);
// TODO(crbug.com/dawn/1278, 1288, 1289): Required buffer size for copy is wrong on D3D12.
bool mayNeedWorkaround = GetParam().mTextureDimension == wgpu::TextureDimension::e3D &&
extraRowsPerImage > 0 && copySize.depthOrArrayLayers > 1;
DAWN_SUPPRESS_TEST_IF(IsD3D12() && mayNeedWorkaround);
size -= kBytesPerBlock;
DoTest(size, copySize, rowsPerImage);
}
} }
// The buffer doesn't have storage for any paddings on the last image. WebGPU spec doesn't require // The buffer doesn't have storage for any paddings on the last image. WebGPU spec doesn't require
// storage for these paddings, and the copy operation will never access to these paddings. So it // storage for these paddings, and the copy operation will never access to these paddings. So it
// should work. // should work.
TEST_P(RequiredBufferSizeInCopyTests, MininumBufferSize) { TEST_P(RequiredBufferSizeInCopyTests, MininumBufferSize) {
wgpu::Extent3D copySize = kCopySize;
copySize.depthOrArrayLayers = GetParam().mCopyDepth;
const uint64_t extraRowsPerImage = GetParam().mExtraRowsPerImage;
const uint64_t rowsPerImage = extraRowsPerImage + copySize.height;
// TODO(crbug.com/dawn/1278, 1288, 1289): Required buffer size for copy is wrong on D3D12. // TODO(crbug.com/dawn/1278, 1288, 1289): Required buffer size for copy is wrong on D3D12.
DAWN_SUPPRESS_TEST_IF(IsD3D12()); bool mayNeedWorkaround = GetParam().mTextureDimension == wgpu::TextureDimension::e3D &&
extraRowsPerImage > 0 && copySize.depthOrArrayLayers > 1;
DAWN_SUPPRESS_TEST_IF(IsD3D12() && mayNeedWorkaround);
uint64_t size = uint64_t size =
kOffset + utils::RequiredBytesInCopy(kBytesPerRow, kRowsPerImage, kCopySize, kFormat); kOffset + utils::RequiredBytesInCopy(kBytesPerRow, rowsPerImage, copySize, kFormat);
DoTest(size); DoTest(size, copySize, rowsPerImage);
} }
DAWN_INSTANTIATE_TEST_P(RequiredBufferSizeInCopyTests, DAWN_INSTANTIATE_TEST_P(RequiredBufferSizeInCopyTests,
{D3D12Backend(), MetalBackend(), OpenGLBackend(), OpenGLESBackend(), {D3D12Backend(), MetalBackend(), OpenGLBackend(), OpenGLESBackend(),
VulkanBackend()}, VulkanBackend()},
{Type::T2BCopy, Type::B2TCopy}); {Type::T2BCopy, Type::B2TCopy},
{wgpu::TextureDimension::e3D, wgpu::TextureDimension::e2D},
{2u, 1u},
{1u, 0u});