dawn-cmake/src/tests/unittests/d3d12/CopySplitTests.cpp
Yunchao He 5b6c1ebce7 Enable CopySplitTests unittests for 3D texture copy splitter on D3D12
CopySplitTests unittests were added for 2D texture copy splitter.
Now that we have added 3D texture copy splitter recently, it's time
to enable these unittests for 3D texture copy splitter.

This change enables the CopySplitTests unittests for 3D texture copy
splitter, with some fixes:
  - Footprint depends on bufferOffset and copySize, not bufferSize,
  - Copy regions overlap validation incorrectly added 1 extra pixel,
  - For 2D texture copy regions, there is one empty row at most in
    a copy region. However, it is not true for 3D texture copy
    regions when we are copying the last row of each slice.

It also adds a few more tests for 3D texture copy splitter when the
copySize.height is 1 and there is an empty row in the copy region.

Bug: dawn:547

Change-Id: I5381993ed5c656da0f82395e7c7bccc54d784767
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/54500
Commit-Queue: Jiawei Shao <jiawei.shao@intel.com>
Reviewed-by: Jiawei Shao <jiawei.shao@intel.com>
2021-06-17 03:22:48 +00:00

530 lines
23 KiB
C++

// Copyright 2017 The Dawn Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <gtest/gtest.h>
#include "common/Assert.h"
#include "common/Constants.h"
#include "common/Math.h"
#include "dawn/webgpu_cpp_print.h"
#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;
namespace {
struct TextureSpec {
uint32_t x;
uint32_t y;
uint32_t z;
uint32_t width;
uint32_t height;
uint32_t depthOrArrayLayers;
uint32_t texelBlockSizeInBytes;
uint32_t blockWidth = 1;
uint32_t blockHeight = 1;
};
struct BufferSpec {
uint64_t offset;
uint32_t bytesPerRow;
uint32_t rowsPerImage;
};
// Check that each copy region fits inside the buffer footprint
void ValidateFootprints(const TextureSpec& textureSpec,
const BufferSpec& bufferSpec,
const TextureCopySubresource& copySplit,
wgpu::TextureDimension dimension) {
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.
if (textureSpec.depthOrArrayLayers <= 1 || dimension == wgpu::TextureDimension::e3D) {
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);
// The last pixel (buffer footprint) of each copy region depends on its bufferOffset
// and copySize. It is not the last pixel where the bufferSize ends.
ASSERT_EQ(copy.bufferOffset.x % textureSpec.blockWidth, 0u);
ASSERT_EQ(copy.copySize.width % textureSpec.blockWidth, 0u);
uint32_t footprintWidth = copy.bufferOffset.x + copy.copySize.width;
ASSERT_EQ(footprintWidth % textureSpec.blockWidth, 0u);
uint32_t footprintWidthInBlocks = footprintWidth / textureSpec.blockWidth;
ASSERT_EQ(copy.bufferOffset.y % textureSpec.blockHeight, 0u);
ASSERT_EQ(copy.copySize.height % textureSpec.blockHeight, 0u);
uint32_t footprintHeight = copy.bufferOffset.y + copy.copySize.height;
ASSERT_EQ(footprintHeight % textureSpec.blockHeight, 0u);
uint32_t footprintHeightInBlocks = footprintHeight / textureSpec.blockHeight;
uint64_t bufferSizeForFootprint =
copy.alignedOffset +
utils::RequiredBytesInCopy(bufferSpec.bytesPerRow, copy.bufferSize.height,
footprintWidthInBlocks, footprintHeightInBlocks,
copy.bufferSize.depthOrArrayLayers,
textureSpec.texelBlockSizeInBytes);
// The buffer footprint of each copy region should not exceed the minimum required
// buffer size. Otherwise, pixels accessed by copy may be OOB.
ASSERT_LE(bufferSizeForFootprint, minimumRequiredBufferSize);
}
}
}
// Check that the offset is aligned
void ValidateOffset(const TextureCopySubresource& copySplit) {
for (uint32_t i = 0; i < copySplit.count; ++i) {
ASSERT_TRUE(
Align(copySplit.copies[i].alignedOffset, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT) ==
copySplit.copies[i].alignedOffset);
}
}
bool InclusiveRangesOverlap(uint32_t minA, uint32_t maxA, uint32_t minB, uint32_t maxB) {
return (minA <= minB && minB <= maxA) || (minB <= minA && minA <= maxB);
}
// Check that no pair of copy regions intersect each other
void ValidateDisjoint(const TextureCopySubresource& copySplit) {
for (uint32_t i = 0; i < copySplit.count; ++i) {
const auto& a = copySplit.copies[i];
for (uint32_t j = i + 1; j < copySplit.count; ++j) {
const auto& b = copySplit.copies[j];
// If textureOffset.x is 0, and copySize.width is 2, we are copying pixel 0 and
// 1. We never touch pixel 2 on x-axis. So the copied range on x-axis should be
// [textureOffset.x, textureOffset.x + copySize.width - 1] and both ends are
// included.
bool overlapX = InclusiveRangesOverlap(
a.textureOffset.x, a.textureOffset.x + a.copySize.width - 1, b.textureOffset.x,
b.textureOffset.x + b.copySize.width - 1);
bool overlapY = InclusiveRangesOverlap(
a.textureOffset.y, a.textureOffset.y + a.copySize.height - 1, b.textureOffset.y,
b.textureOffset.y + b.copySize.height - 1);
bool overlapZ = InclusiveRangesOverlap(
a.textureOffset.z, a.textureOffset.z + a.copySize.depthOrArrayLayers - 1,
b.textureOffset.z, b.textureOffset.z + b.copySize.depthOrArrayLayers - 1);
ASSERT_TRUE(!overlapX || !overlapY || !overlapZ);
}
}
}
// Check that the union of the copy regions exactly covers the texture region
void ValidateTextureBounds(const TextureSpec& textureSpec,
const TextureCopySubresource& copySplit) {
ASSERT_TRUE(copySplit.count > 0);
uint32_t minX = copySplit.copies[0].textureOffset.x;
uint32_t minY = copySplit.copies[0].textureOffset.y;
uint32_t minZ = copySplit.copies[0].textureOffset.z;
uint32_t maxX = copySplit.copies[0].textureOffset.x + copySplit.copies[0].copySize.width;
uint32_t maxY = copySplit.copies[0].textureOffset.y + copySplit.copies[0].copySize.height;
uint32_t maxZ =
copySplit.copies[0].textureOffset.z + copySplit.copies[0].copySize.depthOrArrayLayers;
for (uint32_t i = 1; i < copySplit.count; ++i) {
const auto& copy = copySplit.copies[i];
minX = std::min(minX, copy.textureOffset.x);
minY = std::min(minY, copy.textureOffset.y);
minZ = std::min(minZ, copy.textureOffset.z);
maxX = std::max(maxX, copy.textureOffset.x + copy.copySize.width);
maxY = std::max(maxY, copy.textureOffset.y + copy.copySize.height);
maxZ = std::max(maxZ, copy.textureOffset.z + copy.copySize.depthOrArrayLayers);
}
ASSERT_EQ(minX, textureSpec.x);
ASSERT_EQ(minY, textureSpec.y);
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.depthOrArrayLayers);
}
// Validate that the number of pixels copied is exactly equal to the number of pixels in the
// texture region
void ValidatePixelCount(const TextureSpec& textureSpec,
const TextureCopySubresource& copySplit) {
uint32_t count = 0;
for (uint32_t i = 0; i < copySplit.count; ++i) {
const auto& copy = copySplit.copies[i];
uint32_t copiedPixels =
copy.copySize.width * copy.copySize.height * copy.copySize.depthOrArrayLayers;
ASSERT_GT(copiedPixels, 0u);
count += copiedPixels;
}
ASSERT_EQ(count, textureSpec.width * textureSpec.height * textureSpec.depthOrArrayLayers);
}
// Check that every buffer offset is at the correct pixel location
void ValidateBufferOffset(const TextureSpec& textureSpec,
const BufferSpec& bufferSpec,
const TextureCopySubresource& copySplit,
wgpu::TextureDimension dimension) {
ASSERT_TRUE(copySplit.count > 0);
uint32_t texelsPerBlock = textureSpec.blockWidth * textureSpec.blockHeight;
for (uint32_t i = 0; i < copySplit.count; ++i) {
const auto& copy = copySplit.copies[i];
uint32_t bytesPerRowInTexels =
bufferSpec.bytesPerRow / textureSpec.texelBlockSizeInBytes * texelsPerBlock;
uint32_t slicePitchInTexels =
bytesPerRowInTexels * (bufferSpec.rowsPerImage / textureSpec.blockHeight);
uint32_t absoluteTexelOffset =
copy.alignedOffset / textureSpec.texelBlockSizeInBytes * texelsPerBlock +
copy.bufferOffset.x / textureSpec.blockWidth * texelsPerBlock +
copy.bufferOffset.y / textureSpec.blockHeight * bytesPerRowInTexels;
// There is one empty row at most in a 2D copy region. However, it is not true for
// a 3D texture copy region when we are copying the last row of each slice. We may
// need to offset a lot rows and copy.bufferOffset.y may be big.
if (dimension == wgpu::TextureDimension::e2D) {
ASSERT_LE(copy.bufferOffset.y, textureSpec.blockHeight);
}
ASSERT_EQ(copy.bufferOffset.z, 0u);
ASSERT_GE(absoluteTexelOffset,
bufferSpec.offset / textureSpec.texelBlockSizeInBytes * texelsPerBlock);
uint32_t relativeTexelOffset =
absoluteTexelOffset -
bufferSpec.offset / textureSpec.texelBlockSizeInBytes * texelsPerBlock;
uint32_t z = relativeTexelOffset / slicePitchInTexels;
uint32_t y = (relativeTexelOffset % slicePitchInTexels) / bytesPerRowInTexels;
uint32_t x = relativeTexelOffset % bytesPerRowInTexels;
ASSERT_EQ(copy.textureOffset.x - textureSpec.x, x);
ASSERT_EQ(copy.textureOffset.y - textureSpec.y, y);
ASSERT_EQ(copy.textureOffset.z - textureSpec.z, z);
}
}
void ValidateCopySplit(const TextureSpec& textureSpec,
const BufferSpec& bufferSpec,
const TextureCopySubresource& copySplit,
wgpu::TextureDimension dimension) {
ValidateFootprints(textureSpec, bufferSpec, copySplit, dimension);
ValidateOffset(copySplit);
ValidateDisjoint(copySplit);
ValidateTextureBounds(textureSpec, copySplit);
ValidatePixelCount(textureSpec, copySplit);
ValidateBufferOffset(textureSpec, bufferSpec, copySplit, dimension);
}
std::ostream& operator<<(std::ostream& os, const TextureSpec& textureSpec) {
os << "TextureSpec("
<< "[(" << textureSpec.x << ", " << textureSpec.y << ", " << textureSpec.z << "), ("
<< textureSpec.width << ", " << textureSpec.height << ", "
<< textureSpec.depthOrArrayLayers << ")], " << textureSpec.texelBlockSizeInBytes << ")";
return os;
}
std::ostream& operator<<(std::ostream& os, const BufferSpec& bufferSpec) {
os << "BufferSpec(" << bufferSpec.offset << ", " << bufferSpec.bytesPerRow << ", "
<< bufferSpec.rowsPerImage << ")";
return os;
}
std::ostream& operator<<(std::ostream& os, const TextureCopySubresource& copySplit) {
os << "CopySplit" << std::endl;
for (uint32_t i = 0; i < copySplit.count; ++i) {
const auto& copy = copySplit.copies[i];
os << " " << i << ": Texture at (" << copy.textureOffset.x << ", "
<< copy.textureOffset.y << ", " << copy.textureOffset.z << "), size ("
<< copy.copySize.width << ", " << copy.copySize.height << ", "
<< copy.copySize.depthOrArrayLayers << ")" << std::endl;
os << " " << i << ": Buffer at (" << copy.bufferOffset.x << ", " << copy.bufferOffset.y
<< ", " << copy.bufferOffset.z << "), footprint (" << copy.bufferSize.width << ", "
<< copy.bufferSize.height << ", " << copy.bufferSize.depthOrArrayLayers << ")"
<< std::endl;
}
return os;
}
// Define base texture sizes and offsets to test with: some aligned, some unaligned
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, 1, 2, 4},
{0, 0, 0, 64, 2, 2, 4},
{0, 0, 0, 128, 2, 1, 4},
{0, 0, 0, 128, 1, 2, 4},
{0, 0, 0, 128, 2, 2, 4},
{0, 0, 0, 192, 2, 1, 4},
{0, 0, 0, 192, 1, 2, 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},
{64, 48, 16, 1024, 1024, 1024, 4},
{0, 0, 0, 257, 31, 1, 4},
{0, 0, 0, 17, 93, 1, 4},
{59, 13, 0, 257, 31, 1, 4},
{17, 73, 0, 17, 93, 1, 4},
{17, 73, 59, 17, 93, 99, 4},
{0, 0, 0, 4, 4, 1, 8, 4, 4},
{64, 16, 0, 4, 4, 1, 8, 4, 4},
{64, 16, 8, 4, 4, 1, 8, 4, 4},
{0, 0, 0, 4, 4, 1, 16, 4, 4},
{64, 16, 0, 4, 4, 1, 16, 4, 4},
{64, 16, 8, 4, 4, 1, 16, 4, 4},
{0, 0, 0, 1024, 1024, 1, 8, 4, 4},
{256, 512, 0, 1024, 1024, 1, 8, 4, 4},
{64, 48, 0, 1024, 1024, 1, 8, 4, 4},
{64, 48, 16, 1024, 1024, 1, 8, 4, 4},
{0, 0, 0, 1024, 1024, 1, 16, 4, 4},
{256, 512, 0, 1024, 1024, 1, 16, 4, 4},
{64, 48, 0, 1024, 1024, 1, 4, 16, 4},
{64, 48, 16, 1024, 1024, 1, 16, 4, 4},
};
// Define base buffer sizes to work with: some offsets aligned, some unaligned. bytesPerRow is
// the minimum required
std::array<BufferSpec, 15> BaseBufferSpecs(const TextureSpec& textureSpec) {
uint32_t bytesPerRow = Align(textureSpec.texelBlockSizeInBytes * textureSpec.width,
kTextureBytesPerRowAlignment);
auto alignNonPow2 = [](uint32_t value, uint32_t size) -> uint32_t {
return value == 0 ? 0 : ((value - 1) / size + 1) * size;
};
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,
textureSpec.height},
BufferSpec{alignNonPow2(1024, textureSpec.texelBlockSizeInBytes), bytesPerRow,
textureSpec.height * 2},
BufferSpec{alignNonPow2(32, textureSpec.texelBlockSizeInBytes), bytesPerRow,
textureSpec.height},
BufferSpec{alignNonPow2(64, textureSpec.texelBlockSizeInBytes), bytesPerRow,
textureSpec.height},
BufferSpec{alignNonPow2(64, textureSpec.texelBlockSizeInBytes), bytesPerRow,
textureSpec.height * 2},
BufferSpec{alignNonPow2(31, textureSpec.texelBlockSizeInBytes), bytesPerRow,
textureSpec.height},
BufferSpec{alignNonPow2(257, textureSpec.texelBlockSizeInBytes), bytesPerRow,
textureSpec.height},
BufferSpec{alignNonPow2(384, textureSpec.texelBlockSizeInBytes), bytesPerRow,
textureSpec.height},
BufferSpec{alignNonPow2(511, textureSpec.texelBlockSizeInBytes), bytesPerRow,
textureSpec.height},
BufferSpec{alignNonPow2(513, textureSpec.texelBlockSizeInBytes), bytesPerRow,
textureSpec.height},
BufferSpec{alignNonPow2(1023, textureSpec.texelBlockSizeInBytes), bytesPerRow,
textureSpec.height},
BufferSpec{alignNonPow2(1023, textureSpec.texelBlockSizeInBytes), bytesPerRow,
textureSpec.height * 2},
};
}
// Define a list of values to set properties in the spec structs
constexpr uint32_t kCheckValues[] = {1, 2, 3, 4, 5, 6, 7, 8, // small values
16, 32, 64, 128, 256, 512, 1024, 2048, // powers of 2
15, 31, 63, 127, 257, 511, 1023, 2047, // misalignments
17, 33, 65, 129, 257, 513, 1025, 2049};
} // namespace
class CopySplitTest : public testing::TestWithParam<wgpu::TextureDimension> {
protected:
void DoTest(const TextureSpec& textureSpec, const BufferSpec& bufferSpec) {
ASSERT(textureSpec.width % textureSpec.blockWidth == 0 &&
textureSpec.height % textureSpec.blockHeight == 0);
wgpu::TextureDimension dimension = GetParam();
TextureCopySubresource copySplit;
switch (dimension) {
case wgpu::TextureDimension::e2D: {
copySplit = Compute2DTextureCopySubresource(
{textureSpec.x, textureSpec.y, textureSpec.z},
{textureSpec.width, textureSpec.height, textureSpec.depthOrArrayLayers},
{textureSpec.texelBlockSizeInBytes, textureSpec.blockWidth,
textureSpec.blockHeight},
bufferSpec.offset, bufferSpec.bytesPerRow);
break;
}
case wgpu::TextureDimension::e3D: {
copySplit = Compute3DTextureCopySplits(
{textureSpec.x, textureSpec.y, textureSpec.z},
{textureSpec.width, textureSpec.height, textureSpec.depthOrArrayLayers},
{textureSpec.texelBlockSizeInBytes, textureSpec.blockWidth,
textureSpec.blockHeight},
bufferSpec.offset, bufferSpec.bytesPerRow, bufferSpec.rowsPerImage);
break;
}
default:
UNREACHABLE();
break;
}
ValidateCopySplit(textureSpec, bufferSpec, copySplit, dimension);
if (HasFatalFailure()) {
std::ostringstream message;
message << "Failed generating splits: " << textureSpec << ", " << bufferSpec
<< std::endl
<< dimension << " " << copySplit << std::endl;
FAIL() << message.str();
}
}
};
TEST_P(CopySplitTest, General) {
for (TextureSpec textureSpec : kBaseTextureSpecs) {
for (BufferSpec bufferSpec : BaseBufferSpecs(textureSpec)) {
DoTest(textureSpec, bufferSpec);
}
}
}
TEST_P(CopySplitTest, TextureWidth) {
for (TextureSpec textureSpec : kBaseTextureSpecs) {
for (uint32_t val : kCheckValues) {
if (val % textureSpec.blockWidth != 0) {
continue;
}
textureSpec.width = val;
for (BufferSpec bufferSpec : BaseBufferSpecs(textureSpec)) {
DoTest(textureSpec, bufferSpec);
}
}
}
}
TEST_P(CopySplitTest, TextureHeight) {
for (TextureSpec textureSpec : kBaseTextureSpecs) {
for (uint32_t val : kCheckValues) {
if (val % textureSpec.blockHeight != 0) {
continue;
}
textureSpec.height = val;
for (BufferSpec bufferSpec : BaseBufferSpecs(textureSpec)) {
DoTest(textureSpec, bufferSpec);
}
}
}
}
TEST_P(CopySplitTest, TextureX) {
for (TextureSpec textureSpec : kBaseTextureSpecs) {
for (uint32_t val : kCheckValues) {
textureSpec.x = val;
for (BufferSpec bufferSpec : BaseBufferSpecs(textureSpec)) {
DoTest(textureSpec, bufferSpec);
}
}
}
}
TEST_P(CopySplitTest, TextureY) {
for (TextureSpec textureSpec : kBaseTextureSpecs) {
for (uint32_t val : kCheckValues) {
textureSpec.y = val;
for (BufferSpec bufferSpec : BaseBufferSpecs(textureSpec)) {
DoTest(textureSpec, bufferSpec);
}
}
}
}
TEST_P(CopySplitTest, TexelSize) {
for (TextureSpec textureSpec : kBaseTextureSpecs) {
for (uint32_t texelSize : {4, 8, 16, 32, 64}) {
textureSpec.texelBlockSizeInBytes = texelSize;
for (BufferSpec bufferSpec : BaseBufferSpecs(textureSpec)) {
DoTest(textureSpec, bufferSpec);
}
}
}
}
TEST_P(CopySplitTest, BufferOffset) {
for (TextureSpec textureSpec : kBaseTextureSpecs) {
for (BufferSpec bufferSpec : BaseBufferSpecs(textureSpec)) {
for (uint32_t val : kCheckValues) {
bufferSpec.offset = textureSpec.texelBlockSizeInBytes * val;
DoTest(textureSpec, bufferSpec);
}
}
}
}
TEST_P(CopySplitTest, RowPitch) {
for (TextureSpec textureSpec : kBaseTextureSpecs) {
for (BufferSpec bufferSpec : BaseBufferSpecs(textureSpec)) {
uint32_t baseRowPitch = bufferSpec.bytesPerRow;
for (uint32_t i = 0; i < 5; ++i) {
bufferSpec.bytesPerRow = baseRowPitch + i * 256;
DoTest(textureSpec, bufferSpec);
}
}
}
}
TEST_P(CopySplitTest, ImageHeight) {
for (TextureSpec textureSpec : kBaseTextureSpecs) {
for (BufferSpec bufferSpec : BaseBufferSpecs(textureSpec)) {
uint32_t baseImageHeight = bufferSpec.rowsPerImage;
for (uint32_t i = 0; i < 5; ++i) {
bufferSpec.rowsPerImage = baseImageHeight + i * 256;
DoTest(textureSpec, bufferSpec);
}
}
}
}
INSTANTIATE_TEST_SUITE_P(,
CopySplitTest,
testing::Values(wgpu::TextureDimension::e2D, wgpu::TextureDimension::e3D));