Support BC5 formats on D3D12

This patch adds the support of BC5 formats on D3D12 backend. On D3D12,
the "rowPitch" refers to the number of bytes in a row of blocks (which
covers up to 4 scanlines at once) for the textures in BC formats.

This patch also adds the related end2end tests for four typical cases of
B2T and T2B copies on D3D12:
(1) copyBytesPerRowPitch + byteOffsetPerRowPitch <= rowPitch and
    texelOffset.y == 0
(2) copyBytesPerRowPitch + byteOffsetPerRowPitch <= rowPitch and
    texelOffset.y > 0
(3) copyBytesPerRowPitch + byteOffsetPerRowPitch > rowPitch
(4) texelOffset.z > 0

BUG=dawn:42
TEST=dawn_end2end_tests

Change-Id: If27ab3e56596e25c1c5be787ca021c0748021a46
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/8541
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
Jiawei Shao 2019-07-05 08:06:30 +00:00 committed by Commit Bot service account
parent 3789858479
commit 3392b200c6
6 changed files with 236 additions and 66 deletions

View File

@ -521,9 +521,8 @@ namespace dawn_native { namespace d3d12 {
texture->TransitionUsageNow(commandList, dawn::TextureUsageBit::TransferDst); texture->TransitionUsageNow(commandList, dawn::TextureUsageBit::TransferDst);
auto copySplit = ComputeTextureCopySplit( auto copySplit = ComputeTextureCopySplit(
copy->destination.origin, copy->copySize, copy->destination.origin, copy->copySize, texture->GetFormat(),
texture->GetFormat().blockByteSize, copy->source.offset, copy->source.offset, copy->source.rowPitch, copy->source.imageHeight);
copy->source.rowPitch, copy->source.imageHeight);
D3D12_TEXTURE_COPY_LOCATION textureLocation = D3D12_TEXTURE_COPY_LOCATION textureLocation =
CreateTextureCopyLocationForTexture(*texture, copy->destination.level, CreateTextureCopyLocationForTexture(*texture, copy->destination.level,
@ -568,7 +567,7 @@ namespace dawn_native { namespace d3d12 {
buffer->TransitionUsageNow(commandList, dawn::BufferUsageBit::TransferDst); buffer->TransitionUsageNow(commandList, dawn::BufferUsageBit::TransferDst);
auto copySplit = ComputeTextureCopySplit( auto copySplit = ComputeTextureCopySplit(
copy->source.origin, copy->copySize, texture->GetFormat().blockByteSize, copy->source.origin, copy->copySize, texture->GetFormat(),
copy->destination.offset, copy->destination.rowPitch, copy->destination.offset, copy->destination.rowPitch,
copy->destination.imageHeight); copy->destination.imageHeight);

View File

@ -20,31 +20,29 @@
namespace dawn_native { namespace d3d12 { namespace dawn_native { namespace d3d12 {
namespace { namespace {
void ComputeTexelOffsets(uint32_t offset, Origin3D ComputeTexelOffsets(Format format,
uint32_t rowPitch, uint32_t offset,
uint32_t slicePitch, uint32_t rowPitch,
uint32_t texelSize, uint32_t slicePitch) {
Origin3D* texelOffset) {
uint32_t byteOffsetX = offset % rowPitch; uint32_t byteOffsetX = offset % rowPitch;
offset -= byteOffsetX; offset -= byteOffsetX;
uint32_t byteOffsetY = offset % slicePitch; uint32_t byteOffsetY = offset % slicePitch;
uint32_t byteOffsetZ = offset - byteOffsetY; uint32_t byteOffsetZ = offset - byteOffsetY;
texelOffset->x = byteOffsetX / texelSize; return {byteOffsetX / format.blockByteSize * format.blockWidth,
texelOffset->y = byteOffsetY / rowPitch; byteOffsetY / rowPitch * format.blockHeight, byteOffsetZ / slicePitch};
texelOffset->z = byteOffsetZ / slicePitch;
} }
} // namespace } // namespace
TextureCopySplit ComputeTextureCopySplit(Origin3D origin, TextureCopySplit ComputeTextureCopySplit(Origin3D origin,
Extent3D copySize, Extent3D copySize,
uint32_t texelSize, Format format,
uint64_t offset, uint64_t offset,
uint32_t rowPitch, uint32_t rowPitch,
uint32_t imageHeight) { uint32_t imageHeight) {
TextureCopySplit copy; TextureCopySplit copy;
ASSERT(rowPitch % texelSize == 0); ASSERT(rowPitch % format.blockByteSize == 0);
uint64_t alignedOffset = uint64_t alignedOffset =
offset & ~static_cast<uint64_t>(D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT - 1); offset & ~static_cast<uint64_t>(D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT - 1);
@ -70,13 +68,13 @@ namespace dawn_native { namespace d3d12 {
ASSERT(alignedOffset < offset); ASSERT(alignedOffset < offset);
ASSERT(offset - alignedOffset < D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT); ASSERT(offset - alignedOffset < D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT);
Origin3D texelOffset; uint32_t slicePitch = rowPitch * (imageHeight / format.blockHeight);
ComputeTexelOffsets(static_cast<uint32_t>(offset - alignedOffset), rowPitch, Origin3D texelOffset = ComputeTexelOffsets(
rowPitch * imageHeight, texelSize, &texelOffset); format, static_cast<uint32_t>(offset - alignedOffset), rowPitch, slicePitch);
uint32_t rowPitchInTexels = rowPitch / texelSize; uint32_t copyBytesPerRowPitch = copySize.width / format.blockWidth * format.blockByteSize;
uint32_t byteOffsetInRowPitch = texelOffset.x / format.blockWidth * format.blockByteSize;
if (copySize.width + texelOffset.x <= rowPitchInTexels) { if (copyBytesPerRowPitch + byteOffsetInRowPitch <= rowPitch) {
// The region's rows fit inside the row pitch. In this case, extend the width of the // The region's rows fit inside the row pitch. In this case, extend the width of the
// PlacedFootprint and copy the buffer with an offset location // PlacedFootprint and copy the buffer with an offset location
// |<--------------- row pitch --------------->| // |<--------------- row pitch --------------->|
@ -152,13 +150,14 @@ namespace dawn_native { namespace d3d12 {
copy.copies[0].textureOffset = origin; copy.copies[0].textureOffset = origin;
ASSERT(rowPitchInTexels > texelOffset.x); ASSERT(rowPitch > byteOffsetInRowPitch);
copy.copies[0].copySize.width = rowPitchInTexels - texelOffset.x; uint32_t texelsPerRow = rowPitch / format.blockByteSize * format.blockWidth;
copy.copies[0].copySize.width = texelsPerRow - texelOffset.x;
copy.copies[0].copySize.height = copySize.height; copy.copies[0].copySize.height = copySize.height;
copy.copies[0].copySize.depth = copySize.depth; copy.copies[0].copySize.depth = copySize.depth;
copy.copies[0].bufferOffset = texelOffset; copy.copies[0].bufferOffset = texelOffset;
copy.copies[0].bufferSize.width = rowPitchInTexels; copy.copies[0].bufferSize.width = texelsPerRow;
copy.copies[0].bufferSize.height = imageHeight + texelOffset.y; copy.copies[0].bufferSize.height = imageHeight + texelOffset.y;
copy.copies[0].bufferSize.depth = copySize.depth + texelOffset.z; copy.copies[0].bufferSize.depth = copySize.depth + texelOffset.z;
@ -172,10 +171,10 @@ namespace dawn_native { namespace d3d12 {
copy.copies[1].copySize.depth = copySize.depth; copy.copies[1].copySize.depth = copySize.depth;
copy.copies[1].bufferOffset.x = 0; copy.copies[1].bufferOffset.x = 0;
copy.copies[1].bufferOffset.y = texelOffset.y + 1; copy.copies[1].bufferOffset.y = texelOffset.y + format.blockHeight;
copy.copies[1].bufferOffset.z = texelOffset.z; copy.copies[1].bufferOffset.z = texelOffset.z;
copy.copies[1].bufferSize.width = copy.copies[1].copySize.width; copy.copies[1].bufferSize.width = copy.copies[1].copySize.width;
copy.copies[1].bufferSize.height = imageHeight + texelOffset.y + 1; copy.copies[1].bufferSize.height = imageHeight + texelOffset.y + format.blockHeight;
copy.copies[1].bufferSize.depth = copySize.depth + texelOffset.z; copy.copies[1].bufferSize.depth = copySize.depth + texelOffset.z;
return copy; return copy;

View File

@ -15,6 +15,7 @@
#ifndef DAWNNATIVE_D3D12_TEXTURECOPYSPLITTER_H_ #ifndef DAWNNATIVE_D3D12_TEXTURECOPYSPLITTER_H_
#define DAWNNATIVE_D3D12_TEXTURECOPYSPLITTER_H_ #define DAWNNATIVE_D3D12_TEXTURECOPYSPLITTER_H_
#include "dawn_native/Texture.h"
#include "dawn_native/dawn_platform.h" #include "dawn_native/dawn_platform.h"
#include <array> #include <array>
@ -39,7 +40,7 @@ namespace dawn_native { namespace d3d12 {
TextureCopySplit ComputeTextureCopySplit(Origin3D origin, TextureCopySplit ComputeTextureCopySplit(Origin3D origin,
Extent3D copySize, Extent3D copySize,
uint32_t texelSize, Format format,
uint64_t offset, uint64_t offset,
uint32_t rowPitch, uint32_t rowPitch,
uint32_t imageHeight); uint32_t imageHeight);

View File

@ -64,20 +64,19 @@ namespace dawn_native { namespace d3d12 {
// A multisampled resource must have either D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET or // A multisampled resource must have either D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET or
// D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL set in D3D12_RESOURCE_DESC::Flags. // D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL set in D3D12_RESOURCE_DESC::Flags.
// https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ns-d3d12-d3d12_resource // https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ns-d3d12-d3d12_resource_desc
// _desc // Currently all textures are zero-initialized via the render-target path so always add
if ((usage & dawn::TextureUsageBit::OutputAttachment) || isMultisampledTexture) { // the render target flag, except for compressed textures for which the render-target
// flag is invalid.
// TODO(natlee@microsoft.com, jiawei.shao@intel.com): do not require render target for
// lazy clearing.
if ((usage & dawn::TextureUsageBit::OutputAttachment) || isMultisampledTexture ||
!format.isCompressed) {
if (format.HasDepthOrStencil()) { if (format.HasDepthOrStencil()) {
flags |= D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; flags |= D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
} else { } else {
flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
} }
} else if ((usage & dawn::TextureUsageBit::TransferDst) ||
(usage & dawn::TextureUsageBit::TransferSrc)) {
// if texture is used as copy source or destination, it may need to be
// cleared/initialized, which requires it to be a render target
// TODO(natlee@microsoft.com): optimize texture clearing without render target
flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
} }
ASSERT(!(flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL) || ASSERT(!(flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL) ||
@ -114,6 +113,13 @@ namespace dawn_native { namespace d3d12 {
return DXGI_FORMAT_B8G8R8A8_UNORM; return DXGI_FORMAT_B8G8R8A8_UNORM;
case dawn::TextureFormat::Depth24PlusStencil8: case dawn::TextureFormat::Depth24PlusStencil8:
return DXGI_FORMAT_D32_FLOAT_S8X24_UINT; return DXGI_FORMAT_D32_FLOAT_S8X24_UINT;
// TODO(jiawei.shao@intel.com): support all BC formats
case dawn::TextureFormat::BC5RGSnorm:
return DXGI_FORMAT_BC5_SNORM;
case dawn::TextureFormat::BC5RGUnorm:
return DXGI_FORMAT_BC5_UNORM;
default: default:
UNREACHABLE(); UNREACHABLE();
} }
@ -335,6 +341,13 @@ namespace dawn_native { namespace d3d12 {
uint32_t levelCount, uint32_t levelCount,
uint32_t baseArrayLayer, uint32_t baseArrayLayer,
uint32_t layerCount) { uint32_t layerCount) {
// TODO(jiawei.shao@intel.com): initialize the textures in compressed formats with copies.
if (GetFormat().isCompressed) {
SetIsSubresourceContentInitialized(baseMipLevel, levelCount, baseArrayLayer,
layerCount);
return;
}
Device* device = ToBackend(GetDevice()); Device* device = ToBackend(GetDevice());
DescriptorHeapAllocator* descriptorHeapAllocator = device->GetDescriptorHeapAllocator(); DescriptorHeapAllocator* descriptorHeapAllocator = device->GetDescriptorHeapAllocator();

View File

@ -83,7 +83,8 @@ class CompressedTextureBCFormatTest : public DawnTest {
bufferRowPitchInBytes = bufferRowPitchInBytes =
copyWidthInBlockAtLevel * CompressedFormatBlockSizeInBytes(copyConfig.format); copyWidthInBlockAtLevel * CompressedFormatBlockSizeInBytes(copyConfig.format);
} }
uint32_t uploadBufferSize = bufferRowPitchInBytes * copyHeightInBlockAtLevel; uint32_t uploadBufferSize =
copyConfig.bufferOffset + bufferRowPitchInBytes * copyHeightInBlockAtLevel;
// Fill uploadData with the pre-prepared one-block compressed texture data. // Fill uploadData with the pre-prepared one-block compressed texture data.
std::vector<uint8_t> uploadData(uploadBufferSize, 0); std::vector<uint8_t> uploadData(uploadBufferSize, 0);
@ -546,5 +547,107 @@ TEST_P(CompressedTextureBCFormatTest, CopyPartofTextureSubResourceIntoNonZeroMip
} }
} }
// TODO(jiawei.shao@intel.com): support BC formats on D3D12, Metal and OpenGL backend // Test the special case of the B2T copies on the D3D12 backend that the buffer offset and texture
DAWN_INSTANTIATE_TEST(CompressedTextureBCFormatTest, VulkanBackend); // extent exactly fit the RowPitch.
TEST_P(CompressedTextureBCFormatTest, BufferOffsetAndExtentFitRowPitch) {
CopyConfig config;
config.textureWidthLevel0 = 8;
config.textureHeightLevel0 = 8;
config.rowPitchAlignment = kTextureRowPitchAlignment;
config.copyExtent3D = {config.textureWidthLevel0, config.textureHeightLevel0, 1};
const uint32_t blockCountPerRow = config.textureWidthLevel0 / kBCBlockWidthInTexels;
for (dawn::TextureFormat format : kBCFormats) {
config.format = format;
const uint32_t blockSizeInBytes = CompressedFormatBlockSizeInBytes(format);
const uint32_t blockCountPerRowPitch = config.rowPitchAlignment / blockSizeInBytes;
config.bufferOffset = (blockCountPerRowPitch - blockCountPerRow) * blockSizeInBytes;
TestCopyRegionIntoBCFormatTextures(config);
}
}
// Test the special case of the B2T copies on the D3D12 backend that the buffer offset exceeds the
// slice pitch (slicePitch = rowPitch * (imageHeightInTexels / blockHeightInTexels)). On D3D12
// backend the texelOffset.y will be greater than 0 after calcuting the texelOffset in the function
// ComputeTexelOffsets().
TEST_P(CompressedTextureBCFormatTest, BufferOffsetExceedsSlicePitch) {
CopyConfig config;
config.textureWidthLevel0 = 8;
config.textureHeightLevel0 = 8;
config.rowPitchAlignment = kTextureRowPitchAlignment;
config.copyExtent3D = {config.textureWidthLevel0, config.textureHeightLevel0, 1};
const uint32_t blockCountPerRow = config.textureWidthLevel0 / kBCBlockWidthInTexels;
const uint32_t slicePitchInBytes =
config.rowPitchAlignment * (config.textureHeightLevel0 / kBCBlockHeightInTexels);
for (dawn::TextureFormat format : kBCFormats) {
config.format = format;
const uint32_t blockSizeInBytes = CompressedFormatBlockSizeInBytes(format);
const uint32_t blockCountPerRowPitch = config.rowPitchAlignment / blockSizeInBytes;
config.bufferOffset = (blockCountPerRowPitch - blockCountPerRow) * blockSizeInBytes +
config.rowPitchAlignment + slicePitchInBytes;
TestCopyRegionIntoBCFormatTextures(config);
}
}
// Test the special case of the B2T copies on the D3D12 backend that the buffer offset and texture
// extent exceed the RowPitch. On D3D12 backend two copies are required for this case.
TEST_P(CompressedTextureBCFormatTest, CopyWithBufferOffsetAndExtentExceedRowPitch) {
CopyConfig config;
config.textureWidthLevel0 = 8;
config.textureHeightLevel0 = 8;
config.rowPitchAlignment = kTextureRowPitchAlignment;
config.copyExtent3D = {config.textureWidthLevel0, config.textureHeightLevel0, 1};
const uint32_t blockCountPerRow = config.textureWidthLevel0 / kBCBlockWidthInTexels;
constexpr uint32_t kExceedRowBlockCount = 1;
for (dawn::TextureFormat format : kBCFormats) {
config.format = format;
const uint32_t blockSizeInBytes = CompressedFormatBlockSizeInBytes(format);
const uint32_t blockCountPerRowPitch = config.rowPitchAlignment / blockSizeInBytes;
config.bufferOffset =
(blockCountPerRowPitch - blockCountPerRow + kExceedRowBlockCount) * blockSizeInBytes;
TestCopyRegionIntoBCFormatTextures(config);
}
}
// Test the special case of the B2T copies on the D3D12 backend that the slicePitch is equal to the
// rowPitch. On D3D12 backend the texelOffset.z will be greater than 0 after calcuting the
// texelOffset in the function ComputeTexelOffsets().
TEST_P(CompressedTextureBCFormatTest, RowPitchEqualToSlicePitch) {
CopyConfig config;
config.textureWidthLevel0 = 8;
config.textureHeightLevel0 = kBCBlockHeightInTexels;
config.rowPitchAlignment = kTextureRowPitchAlignment;
config.copyExtent3D = {config.textureWidthLevel0, config.textureHeightLevel0, 1};
const uint32_t blockCountPerRow = config.textureWidthLevel0 / kBCBlockWidthInTexels;
const uint32_t slicePitchInBytes = config.rowPitchAlignment;
for (dawn::TextureFormat format : kBCFormats) {
config.format = format;
const uint32_t blockSizeInBytes = CompressedFormatBlockSizeInBytes(format);
const uint32_t blockCountPerRowPitch = config.rowPitchAlignment / blockSizeInBytes;
config.bufferOffset =
(blockCountPerRowPitch - blockCountPerRow) * blockSizeInBytes + slicePitchInBytes;
TestCopyRegionIntoBCFormatTextures(config);
}
}
// TODO(jiawei.shao@intel.com): support BC formats on Metal and OpenGL backend
DAWN_INSTANTIATE_TEST(CompressedTextureBCFormatTest, D3D12Backend, VulkanBackend);

View File

@ -14,11 +14,12 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include "dawn_native/d3d12/d3d12_platform.h"
#include "dawn_native/d3d12/TextureCopySplitter.h"
#include "common/Assert.h" #include "common/Assert.h"
#include "common/Constants.h" #include "common/Constants.h"
#include "common/Math.h" #include "common/Math.h"
#include "dawn_native/Texture.h"
#include "dawn_native/d3d12/TextureCopySplitter.h"
#include "dawn_native/d3d12/d3d12_platform.h"
using namespace dawn_native::d3d12; using namespace dawn_native::d3d12;
@ -31,7 +32,9 @@ namespace {
uint32_t width; uint32_t width;
uint32_t height; uint32_t height;
uint32_t depth; uint32_t depth;
uint32_t texelSize; uint32_t texelBlockSizeInBytes;
uint32_t blockWidth = 1;
uint32_t blockHeight = 1;
}; };
struct BufferSpec { struct BufferSpec {
@ -116,15 +119,25 @@ namespace {
void ValidateBufferOffset(const TextureSpec& textureSpec, const BufferSpec& bufferSpec, const TextureCopySplit& copySplit) { void ValidateBufferOffset(const TextureSpec& textureSpec, const BufferSpec& bufferSpec, const TextureCopySplit& copySplit) {
ASSERT_TRUE(copySplit.count > 0); ASSERT_TRUE(copySplit.count > 0);
uint32_t texelsPerBlock = textureSpec.blockWidth * textureSpec.blockHeight;
for (uint32_t i = 0; i < copySplit.count; ++i) { for (uint32_t i = 0; i < copySplit.count; ++i) {
const auto& copy = copySplit.copies[i]; const auto& copy = copySplit.copies[i];
uint32_t rowPitchInTexels = bufferSpec.rowPitch / textureSpec.texelSize; uint32_t rowPitchInTexels =
uint32_t slicePitchInTexels = rowPitchInTexels * bufferSpec.imageHeight; bufferSpec.rowPitch / textureSpec.texelBlockSizeInBytes * texelsPerBlock;
uint32_t absoluteTexelOffset = copySplit.offset / textureSpec.texelSize + copy.bufferOffset.x + copy.bufferOffset.y * rowPitchInTexels + copy.bufferOffset.z * slicePitchInTexels; uint32_t slicePitchInTexels =
rowPitchInTexels * (bufferSpec.imageHeight / textureSpec.blockHeight);
uint32_t absoluteTexelOffset =
copySplit.offset / textureSpec.texelBlockSizeInBytes * texelsPerBlock +
copy.bufferOffset.x / textureSpec.blockWidth * texelsPerBlock +
copy.bufferOffset.y / textureSpec.blockHeight * rowPitchInTexels +
copy.bufferOffset.z * slicePitchInTexels;
ASSERT(absoluteTexelOffset >= bufferSpec.offset / textureSpec.texelSize); ASSERT(absoluteTexelOffset >=
uint32_t relativeTexelOffset = absoluteTexelOffset - bufferSpec.offset / textureSpec.texelSize; bufferSpec.offset / textureSpec.texelBlockSizeInBytes * texelsPerBlock);
uint32_t relativeTexelOffset =
absoluteTexelOffset -
bufferSpec.offset / textureSpec.texelBlockSizeInBytes * texelsPerBlock;
uint32_t z = relativeTexelOffset / slicePitchInTexels; uint32_t z = relativeTexelOffset / slicePitchInTexels;
uint32_t y = (relativeTexelOffset % slicePitchInTexels) / rowPitchInTexels; uint32_t y = (relativeTexelOffset % slicePitchInTexels) / rowPitchInTexels;
@ -147,9 +160,9 @@ namespace {
std::ostream& operator<<(std::ostream& os, const TextureSpec& textureSpec) { std::ostream& operator<<(std::ostream& os, const TextureSpec& textureSpec) {
os << "TextureSpec(" os << "TextureSpec("
<< "[(" << textureSpec.x << ", " << textureSpec.y << ", " << textureSpec.z << "), (" << textureSpec.width << ", " << textureSpec.height << ", " << textureSpec.depth << ")], " << "[(" << textureSpec.x << ", " << textureSpec.y << ", " << textureSpec.z << "), ("
<< textureSpec.texelSize << textureSpec.width << ", " << textureSpec.height << ", " << textureSpec.depth << ")], "
<< ")"; << textureSpec.texelBlockSizeInBytes << ")";
return os; return os;
} }
@ -186,32 +199,62 @@ namespace {
{59, 13, 0, 257, 31, 1, 4}, {59, 13, 0, 257, 31, 1, 4},
{17, 73, 0, 17, 93, 1, 4}, {17, 73, 0, 17, 93, 1, 4},
{17, 73, 59, 17, 93, 99, 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. rowPitch is the minimum required // Define base buffer sizes to work with: some offsets aligned, some unaligned. rowPitch is the minimum required
std::array<BufferSpec, 13> BaseBufferSpecs(const TextureSpec& textureSpec) { std::array<BufferSpec, 13> BaseBufferSpecs(const TextureSpec& textureSpec) {
uint32_t rowPitch = Align(textureSpec.texelSize * textureSpec.width, kTextureRowPitchAlignment); uint32_t rowPitch =
Align(textureSpec.texelBlockSizeInBytes * textureSpec.width, kTextureRowPitchAlignment);
auto alignNonPow2 = [](uint32_t value, uint32_t size) -> uint32_t { auto alignNonPow2 = [](uint32_t value, uint32_t size) -> uint32_t {
return value == 0 ? 0 : ((value - 1) / size + 1) * size; return value == 0 ? 0 : ((value - 1) / size + 1) * size;
}; };
return { return {
BufferSpec{alignNonPow2(0, textureSpec.texelSize), rowPitch, textureSpec.height}, BufferSpec{alignNonPow2(0, textureSpec.texelBlockSizeInBytes), rowPitch,
BufferSpec{alignNonPow2(512, textureSpec.texelSize), rowPitch, textureSpec.height}, textureSpec.height},
BufferSpec{alignNonPow2(1024, textureSpec.texelSize), rowPitch, textureSpec.height}, BufferSpec{alignNonPow2(512, textureSpec.texelBlockSizeInBytes), rowPitch,
BufferSpec{alignNonPow2(1024, textureSpec.texelSize), rowPitch, textureSpec.height * 2}, textureSpec.height},
BufferSpec{alignNonPow2(1024, textureSpec.texelBlockSizeInBytes), rowPitch,
textureSpec.height},
BufferSpec{alignNonPow2(1024, textureSpec.texelBlockSizeInBytes), rowPitch,
textureSpec.height * 2},
BufferSpec{alignNonPow2(32, textureSpec.texelSize), rowPitch, textureSpec.height}, BufferSpec{alignNonPow2(32, textureSpec.texelBlockSizeInBytes), rowPitch,
BufferSpec{alignNonPow2(64, textureSpec.texelSize), rowPitch, textureSpec.height}, textureSpec.height},
BufferSpec{alignNonPow2(64, textureSpec.texelSize), rowPitch, textureSpec.height * 2}, BufferSpec{alignNonPow2(64, textureSpec.texelBlockSizeInBytes), rowPitch,
textureSpec.height},
BufferSpec{alignNonPow2(64, textureSpec.texelBlockSizeInBytes), rowPitch,
textureSpec.height * 2},
BufferSpec{alignNonPow2(31, textureSpec.texelSize), rowPitch, textureSpec.height}, BufferSpec{alignNonPow2(31, textureSpec.texelBlockSizeInBytes), rowPitch,
BufferSpec{alignNonPow2(257, textureSpec.texelSize), rowPitch, textureSpec.height}, textureSpec.height},
BufferSpec{alignNonPow2(511, textureSpec.texelSize), rowPitch, textureSpec.height}, BufferSpec{alignNonPow2(257, textureSpec.texelBlockSizeInBytes), rowPitch,
BufferSpec{alignNonPow2(513, textureSpec.texelSize), rowPitch, textureSpec.height}, textureSpec.height},
BufferSpec{alignNonPow2(1023, textureSpec.texelSize), rowPitch, textureSpec.height}, BufferSpec{alignNonPow2(511, textureSpec.texelBlockSizeInBytes), rowPitch,
BufferSpec{alignNonPow2(1023, textureSpec.texelSize), rowPitch, textureSpec.height * 2}, textureSpec.height},
BufferSpec{alignNonPow2(513, textureSpec.texelBlockSizeInBytes), rowPitch,
textureSpec.height},
BufferSpec{alignNonPow2(1023, textureSpec.texelBlockSizeInBytes), rowPitch,
textureSpec.height},
BufferSpec{alignNonPow2(1023, textureSpec.texelBlockSizeInBytes), rowPitch,
textureSpec.height * 2},
}; };
} }
@ -228,9 +271,15 @@ namespace {
class CopySplitTest : public testing::Test { class CopySplitTest : public testing::Test {
protected: protected:
TextureCopySplit DoTest(const TextureSpec& textureSpec, const BufferSpec& bufferSpec) { TextureCopySplit DoTest(const TextureSpec& textureSpec, const BufferSpec& bufferSpec) {
ASSERT(textureSpec.width % textureSpec.blockWidth == 0 &&
textureSpec.height % textureSpec.blockHeight == 0);
dawn_native::Format fakeFormat = {};
fakeFormat.blockWidth = textureSpec.blockWidth;
fakeFormat.blockHeight = textureSpec.blockHeight;
fakeFormat.blockByteSize = textureSpec.texelBlockSizeInBytes;
TextureCopySplit copySplit = ComputeTextureCopySplit( TextureCopySplit copySplit = ComputeTextureCopySplit(
{textureSpec.x, textureSpec.y, textureSpec.z}, {textureSpec.x, textureSpec.y, textureSpec.z},
{textureSpec.width, textureSpec.height, textureSpec.depth}, textureSpec.texelSize, {textureSpec.width, textureSpec.height, textureSpec.depth}, fakeFormat,
bufferSpec.offset, bufferSpec.rowPitch, bufferSpec.imageHeight); bufferSpec.offset, bufferSpec.rowPitch, bufferSpec.imageHeight);
ValidateCopySplit(textureSpec, bufferSpec, copySplit); ValidateCopySplit(textureSpec, bufferSpec, copySplit);
return copySplit; return copySplit;
@ -255,6 +304,9 @@ TEST_F(CopySplitTest, General) {
TEST_F(CopySplitTest, TextureWidth) { TEST_F(CopySplitTest, TextureWidth) {
for (TextureSpec textureSpec : kBaseTextureSpecs) { for (TextureSpec textureSpec : kBaseTextureSpecs) {
for (uint32_t val : kCheckValues) { for (uint32_t val : kCheckValues) {
if (val % textureSpec.blockWidth != 0) {
continue;
}
textureSpec.width = val; textureSpec.width = val;
for (BufferSpec bufferSpec : BaseBufferSpecs(textureSpec)) { for (BufferSpec bufferSpec : BaseBufferSpecs(textureSpec)) {
@ -273,6 +325,9 @@ TEST_F(CopySplitTest, TextureWidth) {
TEST_F(CopySplitTest, TextureHeight) { TEST_F(CopySplitTest, TextureHeight) {
for (TextureSpec textureSpec : kBaseTextureSpecs) { for (TextureSpec textureSpec : kBaseTextureSpecs) {
for (uint32_t val : kCheckValues) { for (uint32_t val : kCheckValues) {
if (val % textureSpec.blockHeight != 0) {
continue;
}
textureSpec.height = val; textureSpec.height = val;
for (BufferSpec bufferSpec : BaseBufferSpecs(textureSpec)) { for (BufferSpec bufferSpec : BaseBufferSpecs(textureSpec)) {
@ -327,7 +382,7 @@ TEST_F(CopySplitTest, TextureY) {
TEST_F(CopySplitTest, TexelSize) { TEST_F(CopySplitTest, TexelSize) {
for (TextureSpec textureSpec : kBaseTextureSpecs) { for (TextureSpec textureSpec : kBaseTextureSpecs) {
for (uint32_t texelSize : {4, 8, 16, 32, 64}) { for (uint32_t texelSize : {4, 8, 16, 32, 64}) {
textureSpec.texelSize = texelSize; textureSpec.texelBlockSizeInBytes = texelSize;
for (BufferSpec bufferSpec : BaseBufferSpecs(textureSpec)) { for (BufferSpec bufferSpec : BaseBufferSpecs(textureSpec)) {
TextureCopySplit copySplit = DoTest(textureSpec, bufferSpec); TextureCopySplit copySplit = DoTest(textureSpec, bufferSpec);
@ -346,7 +401,7 @@ TEST_F(CopySplitTest, BufferOffset) {
for (TextureSpec textureSpec : kBaseTextureSpecs) { for (TextureSpec textureSpec : kBaseTextureSpecs) {
for (BufferSpec bufferSpec : BaseBufferSpecs(textureSpec)) { for (BufferSpec bufferSpec : BaseBufferSpecs(textureSpec)) {
for (uint32_t val : kCheckValues) { for (uint32_t val : kCheckValues) {
bufferSpec.offset = textureSpec.texelSize * val; bufferSpec.offset = textureSpec.texelBlockSizeInBytes * val;
TextureCopySplit copySplit = DoTest(textureSpec, bufferSpec); TextureCopySplit copySplit = DoTest(textureSpec, bufferSpec);
if (HasFatalFailure()) { if (HasFatalFailure()) {