mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-12-20 10:25:28 +00:00
Polish 2D texture copy splitter on D3D12
In every copy region, its bufferSize.height minus rowsPerImage can be texture format's blockInfo.height (it is 1 for uncompressed formats) at most, and it appears only if bytesPerRow is 256 and copySize on height is a full copy. This change will benefit 3D texture copy splitter via removing unwanted empty rows issues. Because empty rows in 3D copy splitter may lead to recompute/modify copy regions and there might be new copy regions added. The removed empty row situations are: 1) Partial copy on height: copySize.height < rowsPerImage * blockInfo.height 2) bytesPerRow is greater than 512. For example, if bytesPerRow is 512 and data in one row straddles two rows and there is no empty row at the first part. The second part will have a fake empty row if we don't recompute its alignedOffset. 3) There are two empty rows in a copy region. For example: if data in one row straddles two rows and there is an empty row in the first copy region. Then there will be two empty rows in the copy region of the second part if we don't recompute the alignedOffset. This change also fixes an issue found by Corentin that copy related argument "rowsPerImage" should not take effect when we are copying one single depth or array slice. Bug: dawn:547 Change-Id: I603291d559de1d05e420e5ed1f4cabf53de5a93f Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/52680 Reviewed-by: Corentin Wallez <cwallez@google.com> Reviewed-by: Austin Eng <enga@chromium.org> Commit-Queue: Yunchao He <yunchao.he@intel.com>
This commit is contained in:
@@ -863,6 +863,47 @@ TEST_P(CopyTests_T2B, StrideSpecialCases) {
|
||||
}
|
||||
}
|
||||
|
||||
// Test copying a single slice with rowsPerImage larger than copy height and rowsPerImage will not
|
||||
// take effect. If rowsPerImage takes effect, it looks like the copy may go past the end of the
|
||||
// buffer.
|
||||
TEST_P(CopyTests_T2B, RowsPerImageShouldNotCauseBufferOOBIfDepthOrArrayLayersIsOne) {
|
||||
// Check various offsets to cover each code path in the 2D split code in TextureCopySplitter.
|
||||
for (uint32_t offset : {0, 4, 64}) {
|
||||
constexpr uint32_t kWidth = 250;
|
||||
constexpr uint32_t kHeight = 3;
|
||||
|
||||
TextureSpec textureSpec;
|
||||
textureSpec.textureSize = {kWidth, kHeight, 1};
|
||||
|
||||
BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kHeight);
|
||||
bufferSpec.rowsPerImage = 2 * kHeight;
|
||||
bufferSpec.offset = offset;
|
||||
bufferSpec.size += offset;
|
||||
DoTest(textureSpec, bufferSpec, {kWidth, kHeight, 1});
|
||||
DoTest(textureSpec, bufferSpec, {kWidth, kHeight, 1}, wgpu::TextureDimension::e3D);
|
||||
}
|
||||
}
|
||||
|
||||
// Test copying a single row with bytesPerRow larger than copy width and bytesPerRow will not
|
||||
// take effect. If bytesPerRow takes effect, it looks like the copy may go past the end of the
|
||||
// buffer.
|
||||
TEST_P(CopyTests_T2B, BytesPerRowShouldNotCauseBufferOOBIfCopyHeightIsOne) {
|
||||
// Check various offsets to cover each code path in the 2D split code in TextureCopySplitter.
|
||||
for (uint32_t offset : {0, 4, 100}) {
|
||||
constexpr uint32_t kWidth = 250;
|
||||
|
||||
TextureSpec textureSpec;
|
||||
textureSpec.textureSize = {kWidth, 1, 1};
|
||||
|
||||
BufferSpec bufferSpec = MinimumBufferSpec(kWidth, 1);
|
||||
bufferSpec.bytesPerRow = 1280; // the default bytesPerRow is 1024.
|
||||
bufferSpec.offset = offset;
|
||||
bufferSpec.size += offset;
|
||||
DoTest(textureSpec, bufferSpec, {kWidth, 1, 1});
|
||||
DoTest(textureSpec, bufferSpec, {kWidth, 1, 1}, wgpu::TextureDimension::e3D);
|
||||
}
|
||||
}
|
||||
|
||||
// Test that copying whole texture 2D array layers in one texture-to-buffer-copy works.
|
||||
TEST_P(CopyTests_T2B, Texture2DArrayFull) {
|
||||
constexpr uint32_t kWidth = 256;
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "dawn_native/Format.h"
|
||||
#include "dawn_native/d3d12/TextureCopySplitter.h"
|
||||
#include "dawn_native/d3d12/d3d12_platform.h"
|
||||
#include "utils/TestUtils.h"
|
||||
|
||||
using namespace dawn_native::d3d12;
|
||||
|
||||
@@ -31,7 +32,7 @@ namespace {
|
||||
uint32_t z;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t depth;
|
||||
uint32_t depthOrArrayLayers;
|
||||
uint32_t texelBlockSizeInBytes;
|
||||
uint32_t blockWidth = 1;
|
||||
uint32_t blockHeight = 1;
|
||||
@@ -44,13 +45,50 @@ namespace {
|
||||
};
|
||||
|
||||
// Check that each copy region fits inside the buffer footprint
|
||||
void ValidateFootprints(const TextureCopySubresource& copySplit) {
|
||||
void ValidateFootprints(const TextureSpec& textureSpec,
|
||||
const BufferSpec& bufferSpec,
|
||||
const TextureCopySubresource& copySplit) {
|
||||
for (uint32_t i = 0; i < copySplit.count; ++i) {
|
||||
const auto& copy = copySplit.copies[i];
|
||||
ASSERT_LE(copy.bufferOffset.x + copy.copySize.width, copy.bufferSize.width);
|
||||
ASSERT_LE(copy.bufferOffset.y + copy.copySize.height, copy.bufferSize.height);
|
||||
ASSERT_LE(copy.bufferOffset.z + copy.copySize.depthOrArrayLayers,
|
||||
copy.bufferSize.depthOrArrayLayers);
|
||||
|
||||
// If there are multiple layers, 2D texture splitter actually splits each layer
|
||||
// independently. See the details in Compute2DTextureCopySplits(). As a result,
|
||||
// if we simply expand a copy region generated by 2D texture splitter to all
|
||||
// layers, the copy region might be OOB. But that is not the approach that the current
|
||||
// 2D texture splitter is doing, although Compute2DTextureCopySubresource forwards
|
||||
// "copySize.depthOrArrayLayers" to the copy region it generated. So skip the test
|
||||
// below for 2D textures with multiple layers.
|
||||
// TODO(yunchao.he@intel.com): add the test below for 3D texture with multiple slices.
|
||||
if (textureSpec.depthOrArrayLayers <= 1) {
|
||||
uint32_t widthInBlocks = textureSpec.width / textureSpec.blockWidth;
|
||||
uint32_t heightInBlocks = textureSpec.height / textureSpec.blockHeight;
|
||||
uint64_t minimumRequiredBufferSize =
|
||||
bufferSpec.offset +
|
||||
utils::RequiredBytesInCopy(bufferSpec.bytesPerRow, bufferSpec.rowsPerImage,
|
||||
widthInBlocks, heightInBlocks,
|
||||
textureSpec.depthOrArrayLayers,
|
||||
textureSpec.texelBlockSizeInBytes);
|
||||
|
||||
ASSERT(copy.bufferSize.width % textureSpec.blockWidth == 0);
|
||||
uint32_t widthInBlocksForFootprint = copy.bufferSize.width / textureSpec.blockWidth;
|
||||
ASSERT(copy.bufferSize.height % textureSpec.blockHeight == 0);
|
||||
uint32_t heightInBlocksForFootprint =
|
||||
copy.bufferSize.height / textureSpec.blockHeight;
|
||||
uint64_t bufferSizeForFootprint =
|
||||
copy.alignedOffset + utils::RequiredBytesInCopy(
|
||||
bufferSpec.bytesPerRow, bufferSpec.rowsPerImage,
|
||||
widthInBlocksForFootprint, heightInBlocksForFootprint,
|
||||
copy.bufferSize.depthOrArrayLayers,
|
||||
textureSpec.texelBlockSizeInBytes);
|
||||
|
||||
// The buffer footprint for copy should not exceed the minimum required buffer
|
||||
// size. Otherwise, pixels accessed by copy may be OOB.
|
||||
ASSERT_LE(bufferSizeForFootprint, minimumRequiredBufferSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,7 +153,7 @@ namespace {
|
||||
ASSERT_EQ(minZ, textureSpec.z);
|
||||
ASSERT_EQ(maxX, textureSpec.x + textureSpec.width);
|
||||
ASSERT_EQ(maxY, textureSpec.y + textureSpec.height);
|
||||
ASSERT_EQ(maxZ, textureSpec.z + textureSpec.depth);
|
||||
ASSERT_EQ(maxZ, textureSpec.z + textureSpec.depthOrArrayLayers);
|
||||
}
|
||||
|
||||
// Validate that the number of pixels copied is exactly equal to the number of pixels in the
|
||||
@@ -127,7 +165,7 @@ namespace {
|
||||
const auto& copy = copySplit.copies[i];
|
||||
count += copy.copySize.width * copy.copySize.height * copy.copySize.depthOrArrayLayers;
|
||||
}
|
||||
ASSERT_EQ(count, textureSpec.width * textureSpec.height * textureSpec.depth);
|
||||
ASSERT_EQ(count, textureSpec.width * textureSpec.height * textureSpec.depthOrArrayLayers);
|
||||
}
|
||||
|
||||
// Check that every buffer offset is at the correct pixel location
|
||||
@@ -149,6 +187,7 @@ namespace {
|
||||
copy.bufferOffset.x / textureSpec.blockWidth * texelsPerBlock +
|
||||
copy.bufferOffset.y / textureSpec.blockHeight * bytesPerRowInTexels;
|
||||
|
||||
ASSERT_LE(copy.bufferOffset.y, textureSpec.blockHeight);
|
||||
ASSERT_EQ(copy.bufferOffset.z, 0u);
|
||||
|
||||
ASSERT(absoluteTexelOffset >=
|
||||
@@ -170,7 +209,7 @@ namespace {
|
||||
void ValidateCopySplit(const TextureSpec& textureSpec,
|
||||
const BufferSpec& bufferSpec,
|
||||
const TextureCopySubresource& copySplit) {
|
||||
ValidateFootprints(copySplit);
|
||||
ValidateFootprints(textureSpec, bufferSpec, copySplit);
|
||||
ValidateOffset(copySplit);
|
||||
ValidateDisjoint(copySplit);
|
||||
ValidateTextureBounds(textureSpec, copySplit);
|
||||
@@ -181,8 +220,8 @@ namespace {
|
||||
std::ostream& operator<<(std::ostream& os, const TextureSpec& textureSpec) {
|
||||
os << "TextureSpec("
|
||||
<< "[(" << textureSpec.x << ", " << textureSpec.y << ", " << textureSpec.z << "), ("
|
||||
<< textureSpec.width << ", " << textureSpec.height << ", " << textureSpec.depth << ")], "
|
||||
<< textureSpec.texelBlockSizeInBytes << ")";
|
||||
<< textureSpec.width << ", " << textureSpec.height << ", "
|
||||
<< textureSpec.depthOrArrayLayers << ")], " << textureSpec.texelBlockSizeInBytes << ")";
|
||||
return os;
|
||||
}
|
||||
|
||||
@@ -212,10 +251,19 @@ namespace {
|
||||
constexpr TextureSpec kBaseTextureSpecs[] = {
|
||||
{0, 0, 0, 1, 1, 1, 4},
|
||||
{0, 0, 0, 64, 1, 1, 4},
|
||||
{0, 0, 0, 128, 1, 1, 4},
|
||||
{0, 0, 0, 192, 1, 1, 4},
|
||||
{31, 16, 0, 1, 1, 1, 4},
|
||||
{64, 16, 0, 1, 1, 1, 4},
|
||||
{64, 16, 8, 1, 1, 1, 4},
|
||||
|
||||
{0, 0, 0, 64, 2, 1, 4},
|
||||
{0, 0, 0, 64, 2, 2, 4},
|
||||
{0, 0, 0, 128, 2, 1, 4},
|
||||
{0, 0, 0, 128, 2, 2, 4},
|
||||
{0, 0, 0, 192, 2, 1, 4},
|
||||
{0, 0, 0, 192, 2, 2, 4},
|
||||
|
||||
{0, 0, 0, 1024, 1024, 1, 4},
|
||||
{256, 512, 0, 1024, 1024, 1, 4},
|
||||
{64, 48, 0, 1024, 1024, 1, 4},
|
||||
@@ -246,7 +294,7 @@ namespace {
|
||||
|
||||
// Define base buffer sizes to work with: some offsets aligned, some unaligned. bytesPerRow is
|
||||
// the minimum required
|
||||
std::array<BufferSpec, 14> BaseBufferSpecs(const TextureSpec& textureSpec) {
|
||||
std::array<BufferSpec, 15> BaseBufferSpecs(const TextureSpec& textureSpec) {
|
||||
uint32_t bytesPerRow = Align(textureSpec.texelBlockSizeInBytes * textureSpec.width,
|
||||
kTextureBytesPerRowAlignment);
|
||||
|
||||
@@ -257,6 +305,8 @@ namespace {
|
||||
return {
|
||||
BufferSpec{alignNonPow2(0, textureSpec.texelBlockSizeInBytes), bytesPerRow,
|
||||
textureSpec.height},
|
||||
BufferSpec{alignNonPow2(256, textureSpec.texelBlockSizeInBytes), bytesPerRow,
|
||||
textureSpec.height},
|
||||
BufferSpec{alignNonPow2(512, textureSpec.texelBlockSizeInBytes), bytesPerRow,
|
||||
textureSpec.height},
|
||||
BufferSpec{alignNonPow2(1024, textureSpec.texelBlockSizeInBytes), bytesPerRow,
|
||||
@@ -307,8 +357,8 @@ class CopySplitTest : public testing::Test {
|
||||
blockInfo.byteSize = textureSpec.texelBlockSizeInBytes;
|
||||
TextureCopySubresource copySplit = Compute2DTextureCopySubresource(
|
||||
{textureSpec.x, textureSpec.y, textureSpec.z},
|
||||
{textureSpec.width, textureSpec.height, textureSpec.depth}, blockInfo,
|
||||
bufferSpec.offset, bufferSpec.bytesPerRow, bufferSpec.rowsPerImage);
|
||||
{textureSpec.width, textureSpec.height, textureSpec.depthOrArrayLayers}, blockInfo,
|
||||
bufferSpec.offset, bufferSpec.bytesPerRow);
|
||||
ValidateCopySplit(textureSpec, bufferSpec, copySplit);
|
||||
return copySplit;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user