mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-06-06 14:43:31 +00:00
In previous T2T copy, Dawn requires textures have the same formats. But Vulkan/Metal/D3D12 have ability to copy between "compatible" formats textures. Metal has the most restrict rules without setting interpreter flags when creating textures. It defines "compatible" texture formats to the formats that only have difference on srgb-ness. This CL follow Metal's rule and release the validations for T2T copies. It supports T2T copy between "compatible" texture format textures. Bug: dawn:1204 Change-Id: I50bf04ea15e8026530b3a5bdb5725f56aa192d85 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/74301 Reviewed-by: Corentin Wallez <cwallez@chromium.org> Commit-Queue: Shaobo Yan <shaobo.yan@intel.com>
2627 lines
122 KiB
C++
2627 lines
122 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 "common/Assert.h"
|
|
#include "common/Constants.h"
|
|
#include "common/Math.h"
|
|
#include "tests/unittests/validation/ValidationTest.h"
|
|
#include "utils/TestUtils.h"
|
|
#include "utils/TextureUtils.h"
|
|
#include "utils/WGPUHelpers.h"
|
|
|
|
class CopyCommandTest : public ValidationTest {
|
|
protected:
|
|
wgpu::Buffer CreateBuffer(uint64_t size, wgpu::BufferUsage usage) {
|
|
wgpu::BufferDescriptor descriptor;
|
|
descriptor.size = size;
|
|
descriptor.usage = usage;
|
|
|
|
return device.CreateBuffer(&descriptor);
|
|
}
|
|
|
|
wgpu::Texture Create2DTexture(uint32_t width,
|
|
uint32_t height,
|
|
uint32_t mipLevelCount,
|
|
uint32_t arrayLayerCount,
|
|
wgpu::TextureFormat format,
|
|
wgpu::TextureUsage usage,
|
|
uint32_t sampleCount = 1) {
|
|
wgpu::TextureDescriptor descriptor;
|
|
descriptor.dimension = wgpu::TextureDimension::e2D;
|
|
descriptor.size.width = width;
|
|
descriptor.size.height = height;
|
|
descriptor.size.depthOrArrayLayers = arrayLayerCount;
|
|
descriptor.sampleCount = sampleCount;
|
|
descriptor.format = format;
|
|
descriptor.mipLevelCount = mipLevelCount;
|
|
descriptor.usage = usage;
|
|
wgpu::Texture tex = device.CreateTexture(&descriptor);
|
|
return tex;
|
|
}
|
|
|
|
wgpu::Texture Create3DTexture(uint32_t width,
|
|
uint32_t height,
|
|
uint32_t depth,
|
|
uint32_t mipLevelCount,
|
|
wgpu::TextureFormat format,
|
|
wgpu::TextureUsage usage) {
|
|
wgpu::TextureDescriptor descriptor;
|
|
descriptor.dimension = wgpu::TextureDimension::e3D;
|
|
descriptor.size.width = width;
|
|
descriptor.size.height = height;
|
|
descriptor.size.depthOrArrayLayers = depth;
|
|
descriptor.format = format;
|
|
descriptor.mipLevelCount = mipLevelCount;
|
|
descriptor.usage = usage;
|
|
wgpu::Texture tex = device.CreateTexture(&descriptor);
|
|
return tex;
|
|
}
|
|
|
|
uint32_t BufferSizeForTextureCopy(
|
|
uint32_t width,
|
|
uint32_t height,
|
|
uint32_t depth,
|
|
wgpu::TextureFormat format = wgpu::TextureFormat::RGBA8Unorm) {
|
|
uint32_t bytesPerPixel = utils::GetTexelBlockSizeInBytes(format);
|
|
uint32_t bytesPerRow = Align(width * bytesPerPixel, kTextureBytesPerRowAlignment);
|
|
return (bytesPerRow * (height - 1) + width * bytesPerPixel) * depth;
|
|
}
|
|
|
|
void ValidateExpectation(wgpu::CommandEncoder encoder, utils::Expectation expectation) {
|
|
if (expectation == utils::Expectation::Success) {
|
|
encoder.Finish();
|
|
} else {
|
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
|
}
|
|
}
|
|
|
|
void TestB2TCopy(utils::Expectation expectation,
|
|
wgpu::Buffer srcBuffer,
|
|
uint64_t srcOffset,
|
|
uint32_t srcBytesPerRow,
|
|
uint32_t srcRowsPerImage,
|
|
wgpu::Texture destTexture,
|
|
uint32_t destLevel,
|
|
wgpu::Origin3D destOrigin,
|
|
wgpu::Extent3D extent3D,
|
|
wgpu::TextureAspect aspect = wgpu::TextureAspect::All) {
|
|
wgpu::ImageCopyBuffer imageCopyBuffer =
|
|
utils::CreateImageCopyBuffer(srcBuffer, srcOffset, srcBytesPerRow, srcRowsPerImage);
|
|
wgpu::ImageCopyTexture imageCopyTexture =
|
|
utils::CreateImageCopyTexture(destTexture, destLevel, destOrigin, aspect);
|
|
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
encoder.CopyBufferToTexture(&imageCopyBuffer, &imageCopyTexture, &extent3D);
|
|
|
|
ValidateExpectation(encoder, expectation);
|
|
}
|
|
|
|
void TestT2BCopy(utils::Expectation expectation,
|
|
wgpu::Texture srcTexture,
|
|
uint32_t srcLevel,
|
|
wgpu::Origin3D srcOrigin,
|
|
wgpu::Buffer destBuffer,
|
|
uint64_t destOffset,
|
|
uint32_t destBytesPerRow,
|
|
uint32_t destRowsPerImage,
|
|
wgpu::Extent3D extent3D,
|
|
wgpu::TextureAspect aspect = wgpu::TextureAspect::All) {
|
|
wgpu::ImageCopyBuffer imageCopyBuffer =
|
|
utils::CreateImageCopyBuffer(destBuffer, destOffset, destBytesPerRow, destRowsPerImage);
|
|
wgpu::ImageCopyTexture imageCopyTexture =
|
|
utils::CreateImageCopyTexture(srcTexture, srcLevel, srcOrigin, aspect);
|
|
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
encoder.CopyTextureToBuffer(&imageCopyTexture, &imageCopyBuffer, &extent3D);
|
|
|
|
ValidateExpectation(encoder, expectation);
|
|
}
|
|
|
|
void TestT2TCopy(utils::Expectation expectation,
|
|
wgpu::Texture srcTexture,
|
|
uint32_t srcLevel,
|
|
wgpu::Origin3D srcOrigin,
|
|
wgpu::Texture dstTexture,
|
|
uint32_t dstLevel,
|
|
wgpu::Origin3D dstOrigin,
|
|
wgpu::Extent3D extent3D,
|
|
wgpu::TextureAspect aspect = wgpu::TextureAspect::All) {
|
|
wgpu::ImageCopyTexture srcImageCopyTexture =
|
|
utils::CreateImageCopyTexture(srcTexture, srcLevel, srcOrigin, aspect);
|
|
wgpu::ImageCopyTexture dstImageCopyTexture =
|
|
utils::CreateImageCopyTexture(dstTexture, dstLevel, dstOrigin, aspect);
|
|
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
encoder.CopyTextureToTexture(&srcImageCopyTexture, &dstImageCopyTexture, &extent3D);
|
|
|
|
ValidateExpectation(encoder, expectation);
|
|
}
|
|
|
|
void TestBothTBCopies(utils::Expectation expectation,
|
|
wgpu::Buffer buffer,
|
|
uint64_t bufferOffset,
|
|
uint32_t bufferBytesPerRow,
|
|
uint32_t rowsPerImage,
|
|
wgpu::Texture texture,
|
|
uint32_t level,
|
|
wgpu::Origin3D origin,
|
|
wgpu::Extent3D extent3D) {
|
|
TestB2TCopy(expectation, buffer, bufferOffset, bufferBytesPerRow, rowsPerImage, texture,
|
|
level, origin, extent3D);
|
|
TestT2BCopy(expectation, texture, level, origin, buffer, bufferOffset, bufferBytesPerRow,
|
|
rowsPerImage, extent3D);
|
|
}
|
|
|
|
void TestBothT2TCopies(utils::Expectation expectation,
|
|
wgpu::Texture texture1,
|
|
uint32_t level1,
|
|
wgpu::Origin3D origin1,
|
|
wgpu::Texture texture2,
|
|
uint32_t level2,
|
|
wgpu::Origin3D origin2,
|
|
wgpu::Extent3D extent3D) {
|
|
TestT2TCopy(expectation, texture1, level1, origin1, texture2, level2, origin2, extent3D);
|
|
TestT2TCopy(expectation, texture2, level2, origin2, texture1, level1, origin1, extent3D);
|
|
}
|
|
|
|
void TestBothTBCopiesExactBufferSize(uint32_t bufferBytesPerRow,
|
|
uint32_t rowsPerImage,
|
|
wgpu::Texture texture,
|
|
wgpu::TextureFormat textureFormat,
|
|
wgpu::Origin3D origin,
|
|
wgpu::Extent3D extent3D) {
|
|
// Check the minimal valid bufferSize.
|
|
uint64_t bufferSize =
|
|
utils::RequiredBytesInCopy(bufferBytesPerRow, rowsPerImage, extent3D, textureFormat);
|
|
wgpu::Buffer source =
|
|
CreateBuffer(bufferSize, wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst);
|
|
TestBothTBCopies(utils::Expectation::Success, source, 0, bufferBytesPerRow, rowsPerImage,
|
|
texture, 0, origin, extent3D);
|
|
|
|
// Check bufferSize was indeed minimal.
|
|
uint64_t invalidSize = bufferSize - 1;
|
|
wgpu::Buffer invalidSource =
|
|
CreateBuffer(invalidSize, wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst);
|
|
TestBothTBCopies(utils::Expectation::Failure, invalidSource, 0, bufferBytesPerRow,
|
|
rowsPerImage, texture, 0, origin, extent3D);
|
|
}
|
|
};
|
|
|
|
// Test copies between buffer and multiple array layers of an uncompressed texture
|
|
TEST_F(CopyCommandTest, CopyToMultipleArrayLayers) {
|
|
wgpu::Texture destination =
|
|
CopyCommandTest::Create2DTexture(4, 2, 1, 5, wgpu::TextureFormat::RGBA8Unorm,
|
|
wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc);
|
|
|
|
// Copy to all array layers
|
|
TestBothTBCopiesExactBufferSize(256, 2, destination, wgpu::TextureFormat::RGBA8Unorm, {0, 0, 0},
|
|
{4, 2, 5});
|
|
|
|
// Copy to the highest array layer
|
|
TestBothTBCopiesExactBufferSize(256, 2, destination, wgpu::TextureFormat::RGBA8Unorm, {0, 0, 4},
|
|
{4, 2, 1});
|
|
|
|
// Copy to array layers in the middle
|
|
TestBothTBCopiesExactBufferSize(256, 2, destination, wgpu::TextureFormat::RGBA8Unorm, {0, 0, 1},
|
|
{4, 2, 3});
|
|
|
|
// Copy with a non-packed rowsPerImage
|
|
TestBothTBCopiesExactBufferSize(256, 3, destination, wgpu::TextureFormat::RGBA8Unorm, {0, 0, 0},
|
|
{4, 2, 5});
|
|
|
|
// Copy with bytesPerRow = 512
|
|
TestBothTBCopiesExactBufferSize(512, 2, destination, wgpu::TextureFormat::RGBA8Unorm, {0, 0, 1},
|
|
{4, 2, 3});
|
|
}
|
|
|
|
class CopyCommandTest_B2B : public CopyCommandTest {};
|
|
|
|
// TODO(cwallez@chromium.org): Test that copies are forbidden inside renderpasses
|
|
|
|
// Test a successfull B2B copy
|
|
TEST_F(CopyCommandTest_B2B, Success) {
|
|
wgpu::Buffer source = CreateBuffer(16, wgpu::BufferUsage::CopySrc);
|
|
wgpu::Buffer destination = CreateBuffer(16, wgpu::BufferUsage::CopyDst);
|
|
|
|
// Copy different copies, including some that touch the OOB condition
|
|
{
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
encoder.CopyBufferToBuffer(source, 0, destination, 0, 16);
|
|
encoder.CopyBufferToBuffer(source, 8, destination, 0, 8);
|
|
encoder.CopyBufferToBuffer(source, 0, destination, 8, 8);
|
|
encoder.Finish();
|
|
}
|
|
|
|
// Empty copies are valid
|
|
{
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
encoder.CopyBufferToBuffer(source, 0, destination, 0, 0);
|
|
encoder.CopyBufferToBuffer(source, 0, destination, 16, 0);
|
|
encoder.CopyBufferToBuffer(source, 16, destination, 0, 0);
|
|
encoder.Finish();
|
|
}
|
|
}
|
|
|
|
// Test a successful B2B copy where the last external reference is dropped.
|
|
// This is a regression test for crbug.com/1217741 where submitting a command
|
|
// buffer with dropped resources when the copy size is 0 was a use-after-free.
|
|
TEST_F(CopyCommandTest_B2B, DroppedBuffer) {
|
|
wgpu::Buffer source = CreateBuffer(16, wgpu::BufferUsage::CopySrc);
|
|
wgpu::Buffer destination = CreateBuffer(16, wgpu::BufferUsage::CopyDst);
|
|
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
encoder.CopyBufferToBuffer(source, 0, destination, 0, 0);
|
|
wgpu::CommandBuffer commandBuffer = encoder.Finish();
|
|
|
|
source = nullptr;
|
|
destination = nullptr;
|
|
device.GetQueue().Submit(1, &commandBuffer);
|
|
}
|
|
|
|
// Test B2B copies with OOB
|
|
TEST_F(CopyCommandTest_B2B, OutOfBounds) {
|
|
wgpu::Buffer source = CreateBuffer(16, wgpu::BufferUsage::CopySrc);
|
|
wgpu::Buffer destination = CreateBuffer(16, wgpu::BufferUsage::CopyDst);
|
|
|
|
// OOB on the source
|
|
{
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
encoder.CopyBufferToBuffer(source, 8, destination, 0, 12);
|
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
|
}
|
|
|
|
// OOB on the destination
|
|
{
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
encoder.CopyBufferToBuffer(source, 0, destination, 8, 12);
|
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
|
}
|
|
}
|
|
|
|
// Test B2B copies with incorrect buffer usage
|
|
TEST_F(CopyCommandTest_B2B, BadUsage) {
|
|
wgpu::Buffer source = CreateBuffer(16, wgpu::BufferUsage::CopySrc);
|
|
wgpu::Buffer destination = CreateBuffer(16, wgpu::BufferUsage::CopyDst);
|
|
wgpu::Buffer vertex = CreateBuffer(16, wgpu::BufferUsage::Vertex);
|
|
|
|
// Source with incorrect usage
|
|
{
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
encoder.CopyBufferToBuffer(vertex, 0, destination, 0, 16);
|
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
|
}
|
|
|
|
// Destination with incorrect usage
|
|
{
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
encoder.CopyBufferToBuffer(source, 0, vertex, 0, 16);
|
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
|
}
|
|
}
|
|
|
|
// Test B2B copies with unaligned data size
|
|
TEST_F(CopyCommandTest_B2B, UnalignedSize) {
|
|
wgpu::Buffer source = CreateBuffer(16, wgpu::BufferUsage::CopySrc);
|
|
wgpu::Buffer destination = CreateBuffer(16, wgpu::BufferUsage::CopyDst);
|
|
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
encoder.CopyBufferToBuffer(source, 8, destination, 0, sizeof(uint8_t));
|
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
|
}
|
|
|
|
// Test B2B copies with unaligned offset
|
|
TEST_F(CopyCommandTest_B2B, UnalignedOffset) {
|
|
wgpu::Buffer source = CreateBuffer(16, wgpu::BufferUsage::CopySrc);
|
|
wgpu::Buffer destination = CreateBuffer(16, wgpu::BufferUsage::CopyDst);
|
|
|
|
// Unaligned source offset
|
|
{
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
encoder.CopyBufferToBuffer(source, 9, destination, 0, 4);
|
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
|
}
|
|
|
|
// Unaligned destination offset
|
|
{
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
encoder.CopyBufferToBuffer(source, 8, destination, 1, 4);
|
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
|
}
|
|
}
|
|
|
|
// Test B2B copies with buffers in error state cause errors.
|
|
TEST_F(CopyCommandTest_B2B, BuffersInErrorState) {
|
|
wgpu::BufferDescriptor errorBufferDescriptor;
|
|
errorBufferDescriptor.size = 4;
|
|
errorBufferDescriptor.usage =
|
|
wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst;
|
|
ASSERT_DEVICE_ERROR(wgpu::Buffer errorBuffer = device.CreateBuffer(&errorBufferDescriptor));
|
|
|
|
constexpr uint64_t bufferSize = 4;
|
|
wgpu::Buffer validBuffer =
|
|
CreateBuffer(bufferSize, wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst);
|
|
|
|
{
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
encoder.CopyBufferToBuffer(errorBuffer, 0, validBuffer, 0, 4);
|
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
|
}
|
|
|
|
{
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
encoder.CopyBufferToBuffer(validBuffer, 0, errorBuffer, 0, 4);
|
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
|
}
|
|
}
|
|
|
|
// Test it is not allowed to do B2B copies within same buffer.
|
|
TEST_F(CopyCommandTest_B2B, CopyWithinSameBuffer) {
|
|
constexpr uint32_t kBufferSize = 16u;
|
|
wgpu::Buffer buffer =
|
|
CreateBuffer(kBufferSize, wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst);
|
|
|
|
// srcOffset < dstOffset, and srcOffset + copySize > dstOffset (overlapping)
|
|
{
|
|
constexpr uint32_t kSrcOffset = 0u;
|
|
constexpr uint32_t kDstOffset = 4u;
|
|
constexpr uint32_t kCopySize = 8u;
|
|
ASSERT(kDstOffset > kSrcOffset && kDstOffset < kSrcOffset + kCopySize);
|
|
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
encoder.CopyBufferToBuffer(buffer, kSrcOffset, buffer, kDstOffset, kCopySize);
|
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
|
}
|
|
|
|
// srcOffset < dstOffset, and srcOffset + copySize == dstOffset (not overlapping)
|
|
{
|
|
constexpr uint32_t kSrcOffset = 0u;
|
|
constexpr uint32_t kDstOffset = 8u;
|
|
constexpr uint32_t kCopySize = kDstOffset - kSrcOffset;
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
encoder.CopyBufferToBuffer(buffer, kSrcOffset, buffer, kDstOffset, kCopySize);
|
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
|
}
|
|
|
|
// srcOffset > dstOffset, and srcOffset < dstOffset + copySize (overlapping)
|
|
{
|
|
constexpr uint32_t kSrcOffset = 4u;
|
|
constexpr uint32_t kDstOffset = 0u;
|
|
constexpr uint32_t kCopySize = 8u;
|
|
ASSERT(kSrcOffset > kDstOffset && kSrcOffset < kDstOffset + kCopySize);
|
|
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
encoder.CopyBufferToBuffer(buffer, kSrcOffset, buffer, kDstOffset, kCopySize);
|
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
|
}
|
|
|
|
// srcOffset > dstOffset, and srcOffset + copySize == dstOffset (not overlapping)
|
|
{
|
|
constexpr uint32_t kSrcOffset = 8u;
|
|
constexpr uint32_t kDstOffset = 0u;
|
|
constexpr uint32_t kCopySize = kSrcOffset - kDstOffset;
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
encoder.CopyBufferToBuffer(buffer, kSrcOffset, buffer, kDstOffset, kCopySize);
|
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
|
}
|
|
}
|
|
|
|
class CopyCommandTest_B2T : public CopyCommandTest {
|
|
protected:
|
|
WGPUDevice CreateTestDevice() override {
|
|
wgpu::DeviceDescriptor descriptor;
|
|
wgpu::FeatureName requiredFeatures[2] = {wgpu::FeatureName::Depth24UnormStencil8,
|
|
wgpu::FeatureName::Depth32FloatStencil8};
|
|
descriptor.requiredFeatures = requiredFeatures;
|
|
descriptor.requiredFeaturesCount = 2;
|
|
return adapter.CreateDevice(&descriptor);
|
|
}
|
|
};
|
|
|
|
// Test a successfull B2T copy
|
|
TEST_F(CopyCommandTest_B2T, Success) {
|
|
uint64_t bufferSize = BufferSizeForTextureCopy(4, 4, 1);
|
|
wgpu::Buffer source = CreateBuffer(bufferSize, wgpu::BufferUsage::CopySrc);
|
|
wgpu::Texture destination =
|
|
Create2DTexture(16, 16, 5, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst);
|
|
|
|
// Different copies, including some that touch the OOB condition
|
|
{
|
|
// Copy 4x4 block in corner of first mip.
|
|
TestB2TCopy(utils::Expectation::Success, source, 0, 256, 4, destination, 0, {0, 0, 0},
|
|
{4, 4, 1});
|
|
// Copy 4x4 block in opposite corner of first mip.
|
|
TestB2TCopy(utils::Expectation::Success, source, 0, 256, 4, destination, 0, {12, 12, 0},
|
|
{4, 4, 1});
|
|
// Copy 4x4 block in the 4x4 mip.
|
|
TestB2TCopy(utils::Expectation::Success, source, 0, 256, 4, destination, 2, {0, 0, 0},
|
|
{4, 4, 1});
|
|
// Copy with a buffer offset
|
|
TestB2TCopy(utils::Expectation::Success, source, bufferSize - 4, 256, 1, destination, 0,
|
|
{0, 0, 0}, {1, 1, 1});
|
|
TestB2TCopy(utils::Expectation::Success, source, bufferSize - 4, 256,
|
|
wgpu::kCopyStrideUndefined, destination, 0, {0, 0, 0}, {1, 1, 1});
|
|
}
|
|
|
|
// Copies with a 256-byte aligned bytes per row but unaligned texture region
|
|
{
|
|
// Unaligned region
|
|
TestB2TCopy(utils::Expectation::Success, source, 0, 256, 4, destination, 0, {0, 0, 0},
|
|
{3, 4, 1});
|
|
// Unaligned region with texture offset
|
|
TestB2TCopy(utils::Expectation::Success, source, 0, 256, 3, destination, 0, {5, 7, 0},
|
|
{2, 3, 1});
|
|
// Unaligned region, with buffer offset
|
|
TestB2TCopy(utils::Expectation::Success, source, 31 * 4, 256, 3, destination, 0, {0, 0, 0},
|
|
{3, 3, 1});
|
|
}
|
|
|
|
// bytesPerRow is undefined
|
|
{
|
|
TestB2TCopy(utils::Expectation::Success, source, 0, wgpu::kCopyStrideUndefined, 2,
|
|
destination, 0, {0, 0, 0}, {1, 1, 1});
|
|
TestB2TCopy(utils::Expectation::Success, source, 0, wgpu::kCopyStrideUndefined, 2,
|
|
destination, 0, {0, 0, 0}, {3, 1, 1});
|
|
// Fail because height or depth is greater than 1:
|
|
TestB2TCopy(utils::Expectation::Failure, source, 0, wgpu::kCopyStrideUndefined, 2,
|
|
destination, 0, {0, 0, 0}, {1, 2, 1});
|
|
TestB2TCopy(utils::Expectation::Failure, source, 0, wgpu::kCopyStrideUndefined, 2,
|
|
destination, 0, {0, 0, 0}, {1, 1, 2});
|
|
}
|
|
|
|
// Empty copies are valid
|
|
{
|
|
// An empty copy
|
|
TestB2TCopy(utils::Expectation::Success, source, 0, 0, 0, destination, 0, {0, 0, 0},
|
|
{0, 0, 1});
|
|
TestB2TCopy(utils::Expectation::Success, source, 0, wgpu::kCopyStrideUndefined, 0,
|
|
destination, 0, {0, 0, 0}, {0, 0, 1});
|
|
// An empty copy with depth = 0
|
|
TestB2TCopy(utils::Expectation::Success, source, 0, 0, 0, destination, 0, {0, 0, 0},
|
|
{0, 0, 0});
|
|
TestB2TCopy(utils::Expectation::Success, source, 0, wgpu::kCopyStrideUndefined, 0,
|
|
destination, 0, {0, 0, 0}, {0, 0, 0});
|
|
// An empty copy touching the end of the buffer
|
|
TestB2TCopy(utils::Expectation::Success, source, bufferSize, 0, 0, destination, 0,
|
|
{0, 0, 0}, {0, 0, 1});
|
|
TestB2TCopy(utils::Expectation::Success, source, bufferSize, wgpu::kCopyStrideUndefined, 0,
|
|
destination, 0, {0, 0, 0}, {0, 0, 1});
|
|
// An empty copy touching the side of the texture
|
|
TestB2TCopy(utils::Expectation::Success, source, 0, 0, 0, destination, 0, {16, 16, 0},
|
|
{0, 0, 1});
|
|
TestB2TCopy(utils::Expectation::Success, source, 0, wgpu::kCopyStrideUndefined, 0,
|
|
destination, 0, {16, 16, 0}, {0, 0, 1});
|
|
|
|
// An empty copy with depth = 1 and bytesPerRow > 0
|
|
TestB2TCopy(utils::Expectation::Success, source, 0, kTextureBytesPerRowAlignment, 0,
|
|
destination, 0, {0, 0, 0}, {0, 0, 1});
|
|
// An empty copy with height > 0, depth = 0, bytesPerRow > 0 and rowsPerImage > 0
|
|
TestB2TCopy(utils::Expectation::Success, source, 0, kTextureBytesPerRowAlignment, 3,
|
|
destination, 0, {0, 0, 0}, {0, 1, 0});
|
|
}
|
|
}
|
|
|
|
// Test OOB conditions on the buffer
|
|
TEST_F(CopyCommandTest_B2T, OutOfBoundsOnBuffer) {
|
|
uint64_t bufferSize = BufferSizeForTextureCopy(4, 4, 1);
|
|
wgpu::Buffer source = CreateBuffer(bufferSize, wgpu::BufferUsage::CopySrc);
|
|
wgpu::Texture destination =
|
|
Create2DTexture(16, 16, 5, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst);
|
|
|
|
// OOB on the buffer because we copy too many pixels
|
|
TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 5, destination, 0, {0, 0, 0},
|
|
{4, 5, 1});
|
|
|
|
// OOB on the buffer because of the offset
|
|
TestB2TCopy(utils::Expectation::Failure, source, 4, 256, 4, destination, 0, {0, 0, 0},
|
|
{4, 4, 1});
|
|
|
|
// OOB on the buffer because (bytes per row * (height - 1) + width * bytesPerPixel) * depth
|
|
// overflows
|
|
TestB2TCopy(utils::Expectation::Failure, source, 0, 512, 3, destination, 0, {0, 0, 0},
|
|
{4, 3, 1});
|
|
|
|
// Not OOB on the buffer although bytes per row * height overflows
|
|
// but (bytes per row * (height - 1) + width * bytesPerPixel) * depth does not overflow
|
|
{
|
|
uint32_t sourceBufferSize = BufferSizeForTextureCopy(7, 3, 1);
|
|
ASSERT_TRUE(256 * 3 > sourceBufferSize) << "bytes per row * height should overflow buffer";
|
|
wgpu::Buffer sourceBuffer = CreateBuffer(sourceBufferSize, wgpu::BufferUsage::CopySrc);
|
|
|
|
TestB2TCopy(utils::Expectation::Success, source, 0, 256, 3, destination, 0, {0, 0, 0},
|
|
{7, 3, 1});
|
|
}
|
|
}
|
|
|
|
// Test OOB conditions on the texture
|
|
TEST_F(CopyCommandTest_B2T, OutOfBoundsOnTexture) {
|
|
uint64_t bufferSize = BufferSizeForTextureCopy(4, 4, 1);
|
|
wgpu::Buffer source = CreateBuffer(bufferSize, wgpu::BufferUsage::CopySrc);
|
|
wgpu::Texture destination =
|
|
Create2DTexture(16, 16, 5, 2, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst);
|
|
|
|
// OOB on the texture because x + width overflows
|
|
TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 4, destination, 0, {13, 12, 0},
|
|
{4, 4, 1});
|
|
|
|
// OOB on the texture because y + width overflows
|
|
TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 4, destination, 0, {12, 13, 0},
|
|
{4, 4, 1});
|
|
|
|
// OOB on the texture because we overflow a non-zero mip
|
|
TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 4, destination, 2, {1, 0, 0},
|
|
{4, 4, 1});
|
|
|
|
// OOB on the texture even on an empty copy when we copy to a non-existent mip.
|
|
TestB2TCopy(utils::Expectation::Failure, source, 0, 0, 0, destination, 5, {0, 0, 0}, {0, 0, 1});
|
|
|
|
// OOB on the texture because slice overflows
|
|
TestB2TCopy(utils::Expectation::Failure, source, 0, 0, 0, destination, 0, {0, 0, 2}, {0, 0, 1});
|
|
}
|
|
|
|
// Test that we force Depth=1 on copies to 2D textures
|
|
TEST_F(CopyCommandTest_B2T, DepthConstraintFor2DTextures) {
|
|
wgpu::Buffer source = CreateBuffer(16 * 4, wgpu::BufferUsage::CopySrc);
|
|
wgpu::Texture destination =
|
|
Create2DTexture(16, 16, 5, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst);
|
|
|
|
// Depth > 1 on an empty copy still errors
|
|
TestB2TCopy(utils::Expectation::Failure, source, 0, 0, 0, destination, 0, {0, 0, 0}, {0, 0, 2});
|
|
}
|
|
|
|
// Test B2T copies with incorrect buffer usage
|
|
TEST_F(CopyCommandTest_B2T, IncorrectUsage) {
|
|
wgpu::Buffer source = CreateBuffer(16 * 4, wgpu::BufferUsage::CopySrc);
|
|
wgpu::Buffer vertex = CreateBuffer(16 * 4, wgpu::BufferUsage::Vertex);
|
|
wgpu::Texture destination =
|
|
Create2DTexture(16, 16, 5, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst);
|
|
wgpu::Texture sampled = Create2DTexture(16, 16, 5, 1, wgpu::TextureFormat::RGBA8Unorm,
|
|
wgpu::TextureUsage::TextureBinding);
|
|
|
|
// Incorrect source usage
|
|
TestB2TCopy(utils::Expectation::Failure, vertex, 0, 256, 4, destination, 0, {0, 0, 0},
|
|
{4, 4, 1});
|
|
|
|
// Incorrect destination usage
|
|
TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 4, sampled, 0, {0, 0, 0}, {4, 4, 1});
|
|
}
|
|
|
|
TEST_F(CopyCommandTest_B2T, BytesPerRowConstraints) {
|
|
uint64_t bufferSize = BufferSizeForTextureCopy(128, 16, 1);
|
|
wgpu::Buffer source = CreateBuffer(bufferSize, wgpu::BufferUsage::CopySrc);
|
|
wgpu::Texture destination = Create2DTexture(128, 16, 5, 5, wgpu::TextureFormat::RGBA8Unorm,
|
|
wgpu::TextureUsage::CopyDst);
|
|
|
|
// bytes per row is 0
|
|
{
|
|
// copyHeight > 1
|
|
TestB2TCopy(utils::Expectation::Failure, source, 0, 0, 4, destination, 0, {0, 0, 0},
|
|
{64, 4, 1});
|
|
TestB2TCopy(utils::Expectation::Success, source, 0, 0, 4, destination, 0, {0, 0, 0},
|
|
{0, 4, 1});
|
|
|
|
// copyDepth > 1
|
|
TestB2TCopy(utils::Expectation::Failure, source, 0, 0, 1, destination, 0, {0, 0, 0},
|
|
{64, 1, 4});
|
|
TestB2TCopy(utils::Expectation::Success, source, 0, 0, 1, destination, 0, {0, 0, 0},
|
|
{0, 1, 4});
|
|
|
|
// copyHeight = 1 and copyDepth = 1
|
|
TestB2TCopy(utils::Expectation::Failure, source, 0, 0, 1, destination, 0, {0, 0, 0},
|
|
{64, 1, 1});
|
|
}
|
|
|
|
// bytes per row is not 256-byte aligned
|
|
{
|
|
// copyHeight > 1
|
|
TestB2TCopy(utils::Expectation::Failure, source, 0, 128, 4, destination, 0, {0, 0, 0},
|
|
{4, 4, 1});
|
|
|
|
// copyHeight = 1 and copyDepth = 1
|
|
TestB2TCopy(utils::Expectation::Failure, source, 0, 128, 1, destination, 0, {0, 0, 0},
|
|
{4, 1, 1});
|
|
}
|
|
|
|
// bytes per row is less than width * bytesPerPixel
|
|
{
|
|
// copyHeight > 1
|
|
TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 2, destination, 0, {0, 0, 0},
|
|
{65, 2, 1});
|
|
// copyHeight == 0
|
|
TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 0, destination, 0, {0, 0, 0},
|
|
{65, 0, 1});
|
|
|
|
// copyDepth > 1
|
|
TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 1, destination, 0, {0, 0, 0},
|
|
{65, 1, 2});
|
|
// copyDepth == 0
|
|
TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 1, destination, 0, {0, 0, 0},
|
|
{65, 1, 0});
|
|
|
|
// copyHeight = 1 and copyDepth = 1
|
|
TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 1, destination, 0, {0, 0, 0},
|
|
{65, 1, 1});
|
|
}
|
|
}
|
|
|
|
TEST_F(CopyCommandTest_B2T, RowsPerImageConstraints) {
|
|
uint64_t bufferSize = BufferSizeForTextureCopy(5, 5, 6);
|
|
wgpu::Buffer source = CreateBuffer(bufferSize, wgpu::BufferUsage::CopySrc);
|
|
wgpu::Texture destination =
|
|
Create2DTexture(16, 16, 1, 5, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst);
|
|
|
|
// rowsPerImage is zero
|
|
TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 0, destination, 0, {0, 0, 0},
|
|
{1, 1, 1});
|
|
TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 0, destination, 0, {0, 0, 0},
|
|
{4, 4, 1});
|
|
|
|
// rowsPerImage is undefined
|
|
TestB2TCopy(utils::Expectation::Success, source, 0, 256, wgpu::kCopyStrideUndefined,
|
|
destination, 0, {0, 0, 0}, {4, 4, 1});
|
|
// Fail because depth > 1:
|
|
TestB2TCopy(utils::Expectation::Failure, source, 0, 256, wgpu::kCopyStrideUndefined,
|
|
destination, 0, {0, 0, 0}, {4, 4, 2});
|
|
|
|
// rowsPerImage is equal to copy height (Valid)
|
|
TestB2TCopy(utils::Expectation::Success, source, 0, 256, 4, destination, 0, {0, 0, 0},
|
|
{4, 4, 1});
|
|
TestB2TCopy(utils::Expectation::Success, source, 0, 256, 4, destination, 0, {0, 0, 0},
|
|
{4, 4, 2});
|
|
|
|
// rowsPerImage is larger than copy height (Valid)
|
|
TestB2TCopy(utils::Expectation::Success, source, 0, 256, 5, destination, 0, {0, 0, 0},
|
|
{4, 4, 1});
|
|
TestB2TCopy(utils::Expectation::Success, source, 0, 256, 5, destination, 0, {0, 0, 0},
|
|
{4, 4, 2});
|
|
|
|
// rowsPerImage is less than copy height (Invalid)
|
|
TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 3, destination, 0, {0, 0, 0},
|
|
{4, 4, 1});
|
|
}
|
|
|
|
// Test B2T copies with incorrect buffer offset usage for color texture
|
|
TEST_F(CopyCommandTest_B2T, IncorrectBufferOffsetForColorTexture) {
|
|
uint64_t bufferSize = BufferSizeForTextureCopy(4, 4, 1);
|
|
wgpu::Buffer source = CreateBuffer(bufferSize, wgpu::BufferUsage::CopySrc);
|
|
wgpu::Texture destination =
|
|
Create2DTexture(16, 16, 5, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst);
|
|
|
|
// Correct usage
|
|
TestB2TCopy(utils::Expectation::Success, source, bufferSize - 4, 256, 1, destination, 0,
|
|
{0, 0, 0}, {1, 1, 1});
|
|
|
|
// Incorrect usages
|
|
{
|
|
TestB2TCopy(utils::Expectation::Failure, source, bufferSize - 5, 256, 1, destination, 0,
|
|
{0, 0, 0}, {1, 1, 1});
|
|
TestB2TCopy(utils::Expectation::Failure, source, bufferSize - 6, 256, 1, destination, 0,
|
|
{0, 0, 0}, {1, 1, 1});
|
|
TestB2TCopy(utils::Expectation::Failure, source, bufferSize - 7, 256, 1, destination, 0,
|
|
{0, 0, 0}, {1, 1, 1});
|
|
}
|
|
}
|
|
|
|
// Test B2T copies with incorrect buffer offset usage for depth-stencil texture
|
|
TEST_F(CopyCommandTest_B2T, IncorrectBufferOffsetForDepthStencilTexture) {
|
|
// TODO(dawn:570, dawn:666): List other valid parameters after missing texture formats
|
|
// are implemented, e.g. Stencil8 and depth16unorm.
|
|
std::array<std::tuple<wgpu::TextureFormat, wgpu::TextureAspect>, 3> params = {
|
|
std::make_tuple(wgpu::TextureFormat::Depth24PlusStencil8, wgpu::TextureAspect::StencilOnly),
|
|
std::make_tuple(wgpu::TextureFormat::Depth24UnormStencil8,
|
|
wgpu::TextureAspect::StencilOnly),
|
|
std::make_tuple(wgpu::TextureFormat::Depth32FloatStencil8,
|
|
wgpu::TextureAspect::StencilOnly),
|
|
};
|
|
|
|
uint64_t bufferSize = BufferSizeForTextureCopy(32, 32, 1);
|
|
wgpu::Buffer source = CreateBuffer(bufferSize, wgpu::BufferUsage::CopySrc);
|
|
|
|
for (auto param : params) {
|
|
wgpu::TextureFormat textureFormat = std::get<0>(param);
|
|
wgpu::TextureAspect textureAspect = std::get<1>(param);
|
|
|
|
wgpu::Texture destination =
|
|
Create2DTexture(16, 16, 5, 1, textureFormat, wgpu::TextureUsage::CopyDst);
|
|
|
|
for (uint64_t srcOffset = 0; srcOffset < 8; srcOffset++) {
|
|
utils::Expectation expectation =
|
|
(srcOffset % 4 == 0) ? utils::Expectation::Success : utils::Expectation::Failure;
|
|
TestB2TCopy(expectation, source, srcOffset, 256, 16, destination, 0, {0, 0, 0},
|
|
{16, 16, 1}, textureAspect);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Test multisampled textures cannot be used in B2T copies.
|
|
TEST_F(CopyCommandTest_B2T, CopyToMultisampledTexture) {
|
|
uint64_t bufferSize = BufferSizeForTextureCopy(16, 16, 1);
|
|
wgpu::Buffer source = CreateBuffer(bufferSize, wgpu::BufferUsage::CopySrc);
|
|
wgpu::Texture destination = Create2DTexture(2, 2, 1, 1, wgpu::TextureFormat::RGBA8Unorm,
|
|
wgpu::TextureUsage::CopyDst, 4);
|
|
|
|
TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 2, destination, 0, {0, 0, 0},
|
|
{2, 2, 1});
|
|
}
|
|
|
|
// Test B2T copies with buffer or texture in error state causes errors.
|
|
TEST_F(CopyCommandTest_B2T, BufferOrTextureInErrorState) {
|
|
wgpu::BufferDescriptor errorBufferDescriptor;
|
|
errorBufferDescriptor.size = 4;
|
|
errorBufferDescriptor.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopySrc;
|
|
ASSERT_DEVICE_ERROR(wgpu::Buffer errorBuffer = device.CreateBuffer(&errorBufferDescriptor));
|
|
|
|
wgpu::TextureDescriptor errorTextureDescriptor;
|
|
errorTextureDescriptor.size.depthOrArrayLayers = 0;
|
|
ASSERT_DEVICE_ERROR(wgpu::Texture errorTexture = device.CreateTexture(&errorTextureDescriptor));
|
|
|
|
wgpu::ImageCopyBuffer errorImageCopyBuffer = utils::CreateImageCopyBuffer(errorBuffer, 0, 0, 0);
|
|
wgpu::ImageCopyTexture errorImageCopyTexture =
|
|
utils::CreateImageCopyTexture(errorTexture, 0, {0, 0, 0});
|
|
|
|
wgpu::Extent3D extent3D = {0, 0, 0};
|
|
|
|
{
|
|
wgpu::Texture destination = Create2DTexture(16, 16, 1, 1, wgpu::TextureFormat::RGBA8Unorm,
|
|
wgpu::TextureUsage::CopyDst);
|
|
wgpu::ImageCopyTexture imageCopyTexture =
|
|
utils::CreateImageCopyTexture(destination, 0, {0, 0, 0});
|
|
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
encoder.CopyBufferToTexture(&errorImageCopyBuffer, &imageCopyTexture, &extent3D);
|
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
|
}
|
|
|
|
{
|
|
uint64_t bufferSize = BufferSizeForTextureCopy(4, 4, 1);
|
|
wgpu::Buffer source = CreateBuffer(bufferSize, wgpu::BufferUsage::CopySrc);
|
|
|
|
wgpu::ImageCopyBuffer imageCopyBuffer = utils::CreateImageCopyBuffer(source, 0, 0, 0);
|
|
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
encoder.CopyBufferToTexture(&imageCopyBuffer, &errorImageCopyTexture, &extent3D);
|
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
|
}
|
|
}
|
|
|
|
// Regression tests for a bug in the computation of texture copy buffer size in Dawn.
|
|
TEST_F(CopyCommandTest_B2T, TextureCopyBufferSizeLastRowComputation) {
|
|
constexpr uint32_t kBytesPerRow = 256;
|
|
constexpr uint32_t kWidth = 4;
|
|
constexpr uint32_t kHeight = 4;
|
|
|
|
constexpr std::array<wgpu::TextureFormat, 2> kFormats = {wgpu::TextureFormat::RGBA8Unorm,
|
|
wgpu::TextureFormat::RG8Unorm};
|
|
|
|
{
|
|
// kBytesPerRow * (kHeight - 1) + kWidth is not large enough to be the valid buffer size in
|
|
// this test because the buffer sizes in B2T copies are not in texels but in bytes.
|
|
constexpr uint32_t kInvalidBufferSize = kBytesPerRow * (kHeight - 1) + kWidth;
|
|
|
|
for (wgpu::TextureFormat format : kFormats) {
|
|
wgpu::Buffer source = CreateBuffer(kInvalidBufferSize, wgpu::BufferUsage::CopySrc);
|
|
wgpu::Texture destination =
|
|
Create2DTexture(kWidth, kHeight, 1, 1, format, wgpu::TextureUsage::CopyDst);
|
|
TestB2TCopy(utils::Expectation::Failure, source, 0, kBytesPerRow, kHeight, destination,
|
|
0, {0, 0, 0}, {kWidth, kHeight, 1});
|
|
}
|
|
}
|
|
|
|
{
|
|
for (wgpu::TextureFormat format : kFormats) {
|
|
uint32_t validBufferSize = BufferSizeForTextureCopy(kWidth, kHeight, 1, format);
|
|
wgpu::Texture destination =
|
|
Create2DTexture(kWidth, kHeight, 1, 1, format, wgpu::TextureUsage::CopyDst);
|
|
|
|
// Verify the return value of BufferSizeForTextureCopy() is exactly the minimum valid
|
|
// buffer size in this test.
|
|
{
|
|
uint32_t invalidBuffferSize = validBufferSize - 1;
|
|
wgpu::Buffer source = CreateBuffer(invalidBuffferSize, wgpu::BufferUsage::CopySrc);
|
|
TestB2TCopy(utils::Expectation::Failure, source, 0, kBytesPerRow, kHeight,
|
|
destination, 0, {0, 0, 0}, {kWidth, kHeight, 1});
|
|
}
|
|
|
|
{
|
|
wgpu::Buffer source = CreateBuffer(validBufferSize, wgpu::BufferUsage::CopySrc);
|
|
TestB2TCopy(utils::Expectation::Success, source, 0, kBytesPerRow, kHeight,
|
|
destination, 0, {0, 0, 0}, {kWidth, kHeight, 1});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Test copy from buffer to mip map of non square texture
|
|
TEST_F(CopyCommandTest_B2T, CopyToMipmapOfNonSquareTexture) {
|
|
uint64_t bufferSize = BufferSizeForTextureCopy(4, 2, 1);
|
|
wgpu::Buffer source = CreateBuffer(bufferSize, wgpu::BufferUsage::CopySrc);
|
|
uint32_t maxMipmapLevel = 3;
|
|
wgpu::Texture destination = Create2DTexture(
|
|
4, 2, maxMipmapLevel, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst);
|
|
|
|
// Copy to top level mip map
|
|
TestB2TCopy(utils::Expectation::Success, source, 0, 256, 1, destination, maxMipmapLevel - 1,
|
|
{0, 0, 0}, {1, 1, 1});
|
|
// Copy to high level mip map
|
|
TestB2TCopy(utils::Expectation::Success, source, 0, 256, 1, destination, maxMipmapLevel - 2,
|
|
{0, 0, 0}, {2, 1, 1});
|
|
// Mip level out of range
|
|
TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 1, destination, maxMipmapLevel,
|
|
{0, 0, 0}, {1, 1, 1});
|
|
// Copy origin out of range
|
|
TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 1, destination, maxMipmapLevel - 2,
|
|
{1, 0, 0}, {2, 1, 1});
|
|
// Copy size out of range
|
|
TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 2, destination, maxMipmapLevel - 2,
|
|
{0, 0, 0}, {2, 2, 1});
|
|
}
|
|
|
|
// Test it is invalid to copy to a depth texture
|
|
TEST_F(CopyCommandTest_B2T, CopyToDepthAspect) {
|
|
uint64_t bufferSize = BufferSizeForTextureCopy(16, 16, 1, wgpu::TextureFormat::Depth32Float);
|
|
wgpu::Buffer source = CreateBuffer(bufferSize, wgpu::BufferUsage::CopySrc);
|
|
|
|
for (wgpu::TextureFormat format : utils::kDepthFormats) {
|
|
wgpu::Texture destination =
|
|
Create2DTexture(16, 16, 1, 1, format, wgpu::TextureUsage::CopyDst);
|
|
|
|
// Test it is invalid to copy from a buffer into a depth texture
|
|
TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 16, destination, 0, {0, 0, 0},
|
|
{16, 16, 1}, wgpu::TextureAspect::DepthOnly);
|
|
|
|
if (utils::IsDepthOnlyFormat(format)) {
|
|
// Test "all" of a depth texture which is only the depth aspect.
|
|
TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 16, destination, 0, {0, 0, 0},
|
|
{16, 16, 1}, wgpu::TextureAspect::All);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Test copy to only the stencil aspect of a texture
|
|
TEST_F(CopyCommandTest_B2T, CopyToStencilAspect) {
|
|
uint64_t bufferSize = BufferSizeForTextureCopy(16, 16, 1, wgpu::TextureFormat::R8Uint);
|
|
wgpu::Buffer source = CreateBuffer(bufferSize, wgpu::BufferUsage::CopySrc);
|
|
|
|
for (wgpu::TextureFormat format : utils::kStencilFormats) {
|
|
// Test it is valid to copy from a buffer into the stencil aspect of a depth/stencil texture
|
|
{
|
|
wgpu::Texture destination =
|
|
Create2DTexture(16, 16, 1, 1, format, wgpu::TextureUsage::CopyDst);
|
|
|
|
// TODO(dawn:666): Test "all" of Stencil8 format when it's implemented.
|
|
|
|
TestB2TCopy(utils::Expectation::Success, source, 0, 256, 16, destination, 0, {0, 0, 0},
|
|
{16, 16, 1}, wgpu::TextureAspect::StencilOnly);
|
|
|
|
// And that it fails if the buffer is one byte too small
|
|
wgpu::Buffer sourceSmall = CreateBuffer(bufferSize - 1, wgpu::BufferUsage::CopySrc);
|
|
TestB2TCopy(utils::Expectation::Failure, sourceSmall, 0, 256, 16, destination, 0,
|
|
{0, 0, 0}, {16, 16, 1}, wgpu::TextureAspect::StencilOnly);
|
|
}
|
|
|
|
// A copy fails when using a depth/stencil texture, and the entire subresource isn't copied
|
|
{
|
|
wgpu::Texture destination =
|
|
Create2DTexture(16, 16, 1, 1, format,
|
|
wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment);
|
|
|
|
TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 15, destination, 0, {0, 0, 0},
|
|
{15, 15, 1}, wgpu::TextureAspect::StencilOnly);
|
|
|
|
TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 1, destination, 0, {0, 0, 0},
|
|
{1, 1, 1}, wgpu::TextureAspect::StencilOnly);
|
|
}
|
|
|
|
// Non-zero mip: A copy fails when using a depth/stencil texture, and the entire subresource
|
|
// isn't copied
|
|
{
|
|
uint64_t bufferSize = BufferSizeForTextureCopy(8, 8, 1, wgpu::TextureFormat::R8Uint);
|
|
wgpu::Buffer source = CreateBuffer(bufferSize, wgpu::BufferUsage::CopySrc);
|
|
|
|
wgpu::Texture destination =
|
|
Create2DTexture(16, 16, 2, 1, format,
|
|
wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment);
|
|
|
|
// Whole mip is success
|
|
TestB2TCopy(utils::Expectation::Success, source, 0, 256, 8, destination, 1, {0, 0, 0},
|
|
{8, 8, 1}, wgpu::TextureAspect::StencilOnly);
|
|
|
|
// Partial mip fails
|
|
TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 7, destination, 1, {0, 0, 0},
|
|
{7, 7, 1}, wgpu::TextureAspect::StencilOnly);
|
|
|
|
TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 1, destination, 1, {0, 0, 0},
|
|
{1, 1, 1}, wgpu::TextureAspect::StencilOnly);
|
|
}
|
|
|
|
// Non-zero mip, non-pow-2: A copy fails when using a depth/stencil texture, and the entire
|
|
// subresource isn't copied
|
|
{
|
|
uint64_t bufferSize = BufferSizeForTextureCopy(8, 8, 1, wgpu::TextureFormat::R8Uint);
|
|
wgpu::Buffer source = CreateBuffer(bufferSize, wgpu::BufferUsage::CopySrc);
|
|
|
|
wgpu::Texture destination =
|
|
Create2DTexture(17, 17, 2, 1, format,
|
|
wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment);
|
|
|
|
// Whole mip is success
|
|
TestB2TCopy(utils::Expectation::Success, source, 0, 256, 8, destination, 1, {0, 0, 0},
|
|
{8, 8, 1}, wgpu::TextureAspect::StencilOnly);
|
|
|
|
// Partial mip fails
|
|
TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 7, destination, 1, {0, 0, 0},
|
|
{7, 7, 1}, wgpu::TextureAspect::StencilOnly);
|
|
|
|
TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 1, destination, 1, {0, 0, 0},
|
|
{1, 1, 1}, wgpu::TextureAspect::StencilOnly);
|
|
}
|
|
}
|
|
|
|
// Test it is invalid to copy from a buffer into the stencil aspect of Depth24Plus (no stencil)
|
|
{
|
|
wgpu::Texture destination = Create2DTexture(16, 16, 1, 1, wgpu::TextureFormat::Depth24Plus,
|
|
wgpu::TextureUsage::CopyDst);
|
|
|
|
TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 16, destination, 0, {0, 0, 0},
|
|
{16, 16, 1}, wgpu::TextureAspect::StencilOnly);
|
|
}
|
|
|
|
// Test it is invalid to copy from a buffer into the stencil aspect of a color texture
|
|
{
|
|
wgpu::Texture destination = Create2DTexture(16, 16, 1, 1, wgpu::TextureFormat::RGBA8Uint,
|
|
wgpu::TextureUsage::CopyDst);
|
|
|
|
TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 16, destination, 0, {0, 0, 0},
|
|
{16, 16, 1}, wgpu::TextureAspect::StencilOnly);
|
|
}
|
|
}
|
|
|
|
// Test that CopyB2T throws an error when requiredBytesInCopy overflows uint64_t
|
|
TEST_F(CopyCommandTest_B2T, RequiredBytesInCopyOverflow) {
|
|
wgpu::Buffer source = CreateBuffer(10000, wgpu::BufferUsage::CopySrc);
|
|
wgpu::Texture destination =
|
|
Create2DTexture(1, 1, 1, 16, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst);
|
|
|
|
// Success
|
|
TestB2TCopy(utils::Expectation::Success, source, 0, (1 << 31), (1 << 31), destination, 0,
|
|
{0, 0, 0}, {1, 1, 1});
|
|
// Failure because bytesPerImage * (depth - 1) overflows
|
|
TestB2TCopy(utils::Expectation::Failure, source, 0, (1 << 31), (1 << 31), destination, 0,
|
|
{0, 0, 0}, {1, 1, 16});
|
|
}
|
|
|
|
class CopyCommandTest_T2B : public CopyCommandTest {
|
|
protected:
|
|
WGPUDevice CreateTestDevice() override {
|
|
wgpu::DeviceDescriptor descriptor;
|
|
wgpu::FeatureName requiredFeatures[2] = {wgpu::FeatureName::Depth24UnormStencil8,
|
|
wgpu::FeatureName::Depth32FloatStencil8};
|
|
descriptor.requiredFeatures = requiredFeatures;
|
|
descriptor.requiredFeaturesCount = 2;
|
|
return adapter.CreateDevice(&descriptor);
|
|
}
|
|
};
|
|
|
|
// Test a successfull T2B copy
|
|
TEST_F(CopyCommandTest_T2B, Success) {
|
|
uint64_t bufferSize = BufferSizeForTextureCopy(4, 4, 1);
|
|
wgpu::Texture source =
|
|
Create2DTexture(16, 16, 5, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopySrc);
|
|
wgpu::Buffer destination = CreateBuffer(bufferSize, wgpu::BufferUsage::CopyDst);
|
|
|
|
// Different copies, including some that touch the OOB condition
|
|
{
|
|
// Copy from 4x4 block in corner of first mip.
|
|
TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, 256, 4,
|
|
{4, 4, 1});
|
|
// Copy from 4x4 block in opposite corner of first mip.
|
|
TestT2BCopy(utils::Expectation::Success, source, 0, {12, 12, 0}, destination, 0, 256, 4,
|
|
{4, 4, 1});
|
|
// Copy from 4x4 block in the 4x4 mip.
|
|
TestT2BCopy(utils::Expectation::Success, source, 2, {0, 0, 0}, destination, 0, 256, 4,
|
|
{4, 4, 1});
|
|
// Copy with a buffer offset
|
|
TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, bufferSize - 4,
|
|
256, 1, {1, 1, 1});
|
|
TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, bufferSize - 4,
|
|
256, wgpu::kCopyStrideUndefined, {1, 1, 1});
|
|
}
|
|
|
|
// Copies with a 256-byte aligned bytes per row but unaligned texture region
|
|
{
|
|
// Unaligned region
|
|
TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, 256, 4,
|
|
{3, 4, 1});
|
|
// Unaligned region with texture offset
|
|
TestT2BCopy(utils::Expectation::Success, source, 0, {5, 7, 0}, destination, 0, 256, 3,
|
|
{2, 3, 1});
|
|
// Unaligned region, with buffer offset
|
|
TestT2BCopy(utils::Expectation::Success, source, 2, {0, 0, 0}, destination, 31 * 4, 256, 3,
|
|
{3, 3, 1});
|
|
}
|
|
|
|
// bytesPerRow is undefined
|
|
{
|
|
TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0,
|
|
wgpu::kCopyStrideUndefined, 2, {1, 1, 1});
|
|
TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0,
|
|
wgpu::kCopyStrideUndefined, 2, {3, 1, 1});
|
|
// Fail because height or depth is greater than 1:
|
|
TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0,
|
|
wgpu::kCopyStrideUndefined, 2, {1, 2, 1});
|
|
TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0,
|
|
wgpu::kCopyStrideUndefined, 2, {1, 1, 2});
|
|
}
|
|
|
|
// Empty copies are valid
|
|
{
|
|
// An empty copy
|
|
TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, 0, 0,
|
|
{0, 0, 1});
|
|
TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0,
|
|
wgpu::kCopyStrideUndefined, 0, {0, 0, 1});
|
|
// An empty copy with depth = 0
|
|
TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, 0, 0,
|
|
{0, 0, 0});
|
|
TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0,
|
|
wgpu::kCopyStrideUndefined, 0, {0, 0, 0});
|
|
// An empty copy touching the end of the buffer
|
|
TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, bufferSize, 0,
|
|
0, {0, 0, 1});
|
|
TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, bufferSize,
|
|
wgpu::kCopyStrideUndefined, 0, {0, 0, 1});
|
|
// An empty copy touching the side of the texture
|
|
TestT2BCopy(utils::Expectation::Success, source, 0, {16, 16, 0}, destination, 0, 0, 0,
|
|
{0, 0, 1});
|
|
TestT2BCopy(utils::Expectation::Success, source, 0, {16, 16, 0}, destination, 0,
|
|
wgpu::kCopyStrideUndefined, 0, {0, 0, 1});
|
|
|
|
// An empty copy with depth = 1 and bytesPerRow > 0
|
|
TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0,
|
|
kTextureBytesPerRowAlignment, 0, {0, 0, 1});
|
|
// An empty copy with height > 0, depth = 0, bytesPerRow > 0 and rowsPerImage > 0
|
|
TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0,
|
|
kTextureBytesPerRowAlignment, 3, {0, 1, 0});
|
|
}
|
|
}
|
|
|
|
// Edge cases around requiredBytesInCopy computation for empty copies
|
|
TEST_F(CopyCommandTest_T2B, Empty) {
|
|
wgpu::Texture source =
|
|
Create2DTexture(16, 16, 1, 2, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopySrc);
|
|
|
|
TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0},
|
|
CreateBuffer(0, wgpu::BufferUsage::CopyDst), 0, 256, 4, {0, 0, 0});
|
|
TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0},
|
|
CreateBuffer(0, wgpu::BufferUsage::CopyDst), 0, 256, 4, {4, 0, 0});
|
|
TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0},
|
|
CreateBuffer(0, wgpu::BufferUsage::CopyDst), 0, 256, 4, {4, 4, 0});
|
|
|
|
TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0},
|
|
CreateBuffer(1024, wgpu::BufferUsage::CopyDst), 0, 256, 4, {4, 0, 2});
|
|
TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0},
|
|
CreateBuffer(1023, wgpu::BufferUsage::CopyDst), 0, 256, 4, {4, 0, 2});
|
|
|
|
TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0},
|
|
CreateBuffer(1792, wgpu::BufferUsage::CopyDst), 0, 256, 4, {0, 4, 2});
|
|
TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0},
|
|
CreateBuffer(1791, wgpu::BufferUsage::CopyDst), 0, 256, 4, {0, 4, 2});
|
|
|
|
TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0},
|
|
CreateBuffer(1024, wgpu::BufferUsage::CopyDst), 0, 256, 4, {0, 0, 2});
|
|
TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0},
|
|
CreateBuffer(1023, wgpu::BufferUsage::CopyDst), 0, 256, 4, {0, 0, 2});
|
|
}
|
|
|
|
// Test OOB conditions on the texture
|
|
TEST_F(CopyCommandTest_T2B, OutOfBoundsOnTexture) {
|
|
uint64_t bufferSize = BufferSizeForTextureCopy(4, 4, 1);
|
|
wgpu::Texture source =
|
|
Create2DTexture(16, 16, 5, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopySrc);
|
|
wgpu::Buffer destination = CreateBuffer(bufferSize, wgpu::BufferUsage::CopyDst);
|
|
|
|
// OOB on the texture because x + width overflows
|
|
TestT2BCopy(utils::Expectation::Failure, source, 0, {13, 12, 0}, destination, 0, 256, 4,
|
|
{4, 4, 1});
|
|
|
|
// OOB on the texture because y + width overflows
|
|
TestT2BCopy(utils::Expectation::Failure, source, 0, {12, 13, 0}, destination, 0, 256, 4,
|
|
{4, 4, 1});
|
|
|
|
// OOB on the texture because we overflow a non-zero mip
|
|
TestT2BCopy(utils::Expectation::Failure, source, 2, {1, 0, 0}, destination, 0, 256, 4,
|
|
{4, 4, 1});
|
|
|
|
// OOB on the texture even on an empty copy when we copy from a non-existent mip.
|
|
TestT2BCopy(utils::Expectation::Failure, source, 5, {0, 0, 0}, destination, 0, 0, 4, {0, 0, 1});
|
|
}
|
|
|
|
// Test OOB conditions on the buffer
|
|
TEST_F(CopyCommandTest_T2B, OutOfBoundsOnBuffer) {
|
|
uint64_t bufferSize = BufferSizeForTextureCopy(4, 4, 1);
|
|
wgpu::Texture source =
|
|
Create2DTexture(16, 16, 5, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopySrc);
|
|
wgpu::Buffer destination = CreateBuffer(bufferSize, wgpu::BufferUsage::CopyDst);
|
|
|
|
// OOB on the buffer because we copy too many pixels
|
|
TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, 256, 5,
|
|
{4, 5, 1});
|
|
|
|
// OOB on the buffer because of the offset
|
|
TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 4, 256, 4,
|
|
{4, 4, 1});
|
|
|
|
// OOB on the buffer because (bytes per row * (height - 1) + width * bytesPerPixel) * depth
|
|
// overflows
|
|
TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, 512, 3,
|
|
{4, 3, 1});
|
|
|
|
// Not OOB on the buffer although bytes per row * height overflows
|
|
// but (bytes per row * (height - 1) + width * bytesPerPixel) * depth does not overflow
|
|
{
|
|
uint32_t destinationBufferSize = BufferSizeForTextureCopy(7, 3, 1);
|
|
ASSERT_TRUE(256 * 3 > destinationBufferSize)
|
|
<< "bytes per row * height should overflow buffer";
|
|
wgpu::Buffer destinationBuffer =
|
|
CreateBuffer(destinationBufferSize, wgpu::BufferUsage::CopyDst);
|
|
TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destinationBuffer, 0, 256, 3,
|
|
{7, 3, 1});
|
|
}
|
|
}
|
|
|
|
// Test that we force Depth=1 on copies from to 2D textures
|
|
TEST_F(CopyCommandTest_T2B, DepthConstraintFor2DTextures) {
|
|
uint64_t bufferSize = BufferSizeForTextureCopy(4, 4, 1);
|
|
wgpu::Texture source =
|
|
Create2DTexture(16, 16, 5, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopySrc);
|
|
wgpu::Buffer destination = CreateBuffer(bufferSize, wgpu::BufferUsage::CopyDst);
|
|
|
|
// Depth > 1 on an empty copy still errors
|
|
TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, 0, 0, {0, 0, 2});
|
|
}
|
|
|
|
// Test T2B copies with incorrect buffer usage
|
|
TEST_F(CopyCommandTest_T2B, IncorrectUsage) {
|
|
uint64_t bufferSize = BufferSizeForTextureCopy(4, 4, 1);
|
|
wgpu::Texture source =
|
|
Create2DTexture(16, 16, 5, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopySrc);
|
|
wgpu::Texture sampled = Create2DTexture(16, 16, 5, 1, wgpu::TextureFormat::RGBA8Unorm,
|
|
wgpu::TextureUsage::TextureBinding);
|
|
wgpu::Buffer destination = CreateBuffer(bufferSize, wgpu::BufferUsage::CopyDst);
|
|
wgpu::Buffer vertex = CreateBuffer(bufferSize, wgpu::BufferUsage::Vertex);
|
|
|
|
// Incorrect source usage
|
|
TestT2BCopy(utils::Expectation::Failure, sampled, 0, {0, 0, 0}, destination, 0, 256, 4,
|
|
{4, 4, 1});
|
|
|
|
// Incorrect destination usage
|
|
TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, vertex, 0, 256, 4, {4, 4, 1});
|
|
}
|
|
|
|
TEST_F(CopyCommandTest_T2B, BytesPerRowConstraints) {
|
|
uint64_t bufferSize = BufferSizeForTextureCopy(128, 16, 1);
|
|
wgpu::Texture source = Create2DTexture(128, 16, 5, 5, wgpu::TextureFormat::RGBA8Unorm,
|
|
wgpu::TextureUsage::CopySrc);
|
|
wgpu::Buffer destination = CreateBuffer(bufferSize, wgpu::BufferUsage::CopyDst);
|
|
|
|
// bytes per row is 0
|
|
{
|
|
// copyHeight > 1
|
|
TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, 0, 4,
|
|
{64, 4, 1});
|
|
TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, 0, 4,
|
|
{0, 4, 1});
|
|
|
|
// copyDepth > 1
|
|
TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, 0, 1,
|
|
{64, 1, 4});
|
|
TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, 0, 1,
|
|
{0, 1, 4});
|
|
|
|
// copyHeight = 1 and copyDepth = 1
|
|
TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, 0, 1,
|
|
{64, 1, 1});
|
|
}
|
|
|
|
// bytes per row is not 256-byte aligned
|
|
{
|
|
// copyHeight > 1
|
|
TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, 128, 4,
|
|
{4, 4, 1});
|
|
|
|
// copyHeight = 1 and copyDepth = 1
|
|
TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, 128, 1,
|
|
{4, 1, 1});
|
|
}
|
|
|
|
// bytes per row is less than width * bytesPerPixel
|
|
{
|
|
// copyHeight > 1
|
|
TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, 256, 2,
|
|
{65, 2, 1});
|
|
// copyHeight == 0
|
|
TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, 256, 0,
|
|
{65, 0, 1});
|
|
|
|
// copyDepth > 1
|
|
TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, 256, 1,
|
|
{65, 1, 2});
|
|
// copyDepth == 0
|
|
TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, 256, 1,
|
|
{65, 1, 0});
|
|
|
|
// copyHeight = 1 and copyDepth = 1
|
|
TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, 256, 1,
|
|
{65, 1, 1});
|
|
}
|
|
}
|
|
|
|
TEST_F(CopyCommandTest_T2B, RowsPerImageConstraints) {
|
|
uint64_t bufferSize = BufferSizeForTextureCopy(5, 5, 6);
|
|
wgpu::Texture source =
|
|
Create2DTexture(16, 16, 1, 5, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopySrc);
|
|
wgpu::Buffer destination = CreateBuffer(bufferSize, wgpu::BufferUsage::CopyDst);
|
|
|
|
// rowsPerImage is zero (Valid)
|
|
TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, 256, 0,
|
|
{1, 1, 1});
|
|
TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, 256, 0,
|
|
{4, 4, 1});
|
|
|
|
// rowsPerImage is undefined
|
|
TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, 256,
|
|
wgpu::kCopyStrideUndefined, {4, 4, 1});
|
|
// Fail because depth > 1:
|
|
TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, 256,
|
|
wgpu::kCopyStrideUndefined, {4, 4, 2});
|
|
|
|
// rowsPerImage is equal to copy height (Valid)
|
|
TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, 256, 4,
|
|
{4, 4, 1});
|
|
TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, 256, 4,
|
|
{4, 4, 2});
|
|
|
|
// rowsPerImage exceeds copy height (Valid)
|
|
TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, 256, 5,
|
|
{4, 4, 1});
|
|
TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, 256, 5,
|
|
{4, 4, 2});
|
|
|
|
// rowsPerImage is less than copy height (Invalid)
|
|
TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, 256, 3,
|
|
{4, 4, 1});
|
|
}
|
|
|
|
// Test T2B copies with incorrect buffer offset usage for color texture
|
|
TEST_F(CopyCommandTest_T2B, IncorrectBufferOffsetForColorTexture) {
|
|
uint64_t bufferSize = BufferSizeForTextureCopy(128, 16, 1);
|
|
wgpu::Texture source = Create2DTexture(128, 16, 5, 1, wgpu::TextureFormat::RGBA8Unorm,
|
|
wgpu::TextureUsage::CopySrc);
|
|
wgpu::Buffer destination = CreateBuffer(bufferSize, wgpu::BufferUsage::CopyDst);
|
|
|
|
// Correct usage
|
|
TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, bufferSize - 4, 256,
|
|
1, {1, 1, 1});
|
|
|
|
// Incorrect usages
|
|
TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, bufferSize - 5, 256,
|
|
1, {1, 1, 1});
|
|
TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, bufferSize - 6, 256,
|
|
1, {1, 1, 1});
|
|
TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, bufferSize - 7, 256,
|
|
1, {1, 1, 1});
|
|
}
|
|
|
|
// Test T2B copies with incorrect buffer offset usage for depth-stencil texture
|
|
TEST_F(CopyCommandTest_T2B, IncorrectBufferOffsetForDepthStencilTexture) {
|
|
// TODO(dawn:570, dawn:666): List other valid parameters after missing texture formats
|
|
// are implemented, e.g. Stencil8 and depth16unorm.
|
|
std::array<std::tuple<wgpu::TextureFormat, wgpu::TextureAspect>, 6> params = {
|
|
std::make_tuple(wgpu::TextureFormat::Depth24PlusStencil8, wgpu::TextureAspect::StencilOnly),
|
|
std::make_tuple(wgpu::TextureFormat::Depth32Float, wgpu::TextureAspect::DepthOnly),
|
|
std::make_tuple(wgpu::TextureFormat::Depth32Float, wgpu::TextureAspect::All),
|
|
std::make_tuple(wgpu::TextureFormat::Depth24UnormStencil8,
|
|
wgpu::TextureAspect::StencilOnly),
|
|
std::make_tuple(wgpu::TextureFormat::Depth32FloatStencil8, wgpu::TextureAspect::DepthOnly),
|
|
std::make_tuple(wgpu::TextureFormat::Depth32FloatStencil8,
|
|
wgpu::TextureAspect::StencilOnly),
|
|
};
|
|
|
|
uint64_t bufferSize = BufferSizeForTextureCopy(32, 32, 1);
|
|
wgpu::Buffer destination = CreateBuffer(bufferSize, wgpu::BufferUsage::CopyDst);
|
|
|
|
for (auto param : params) {
|
|
wgpu::TextureFormat textureFormat = std::get<0>(param);
|
|
wgpu::TextureAspect textureAspect = std::get<1>(param);
|
|
|
|
wgpu::Texture source =
|
|
Create2DTexture(16, 16, 5, 1, textureFormat, wgpu::TextureUsage::CopySrc);
|
|
|
|
for (uint64_t dstOffset = 0; dstOffset < 8; dstOffset++) {
|
|
utils::Expectation expectation =
|
|
(dstOffset % 4 == 0) ? utils::Expectation::Success : utils::Expectation::Failure;
|
|
TestT2BCopy(expectation, source, 0, {0, 0, 0}, destination, dstOffset, 256, 16,
|
|
{16, 16, 1}, textureAspect);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Test multisampled textures cannot be used in T2B copies.
|
|
TEST_F(CopyCommandTest_T2B, CopyFromMultisampledTexture) {
|
|
wgpu::Texture source = Create2DTexture(2, 2, 1, 1, wgpu::TextureFormat::RGBA8Unorm,
|
|
wgpu::TextureUsage::CopySrc, 4);
|
|
uint64_t bufferSize = BufferSizeForTextureCopy(16, 16, 1);
|
|
wgpu::Buffer destination = CreateBuffer(bufferSize, wgpu::BufferUsage::CopyDst);
|
|
|
|
TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, 256, 2,
|
|
{2, 2, 1});
|
|
}
|
|
|
|
// Test T2B copies with buffer or texture in error state cause errors.
|
|
TEST_F(CopyCommandTest_T2B, BufferOrTextureInErrorState) {
|
|
wgpu::BufferDescriptor errorBufferDescriptor;
|
|
errorBufferDescriptor.size = 4;
|
|
errorBufferDescriptor.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopySrc;
|
|
ASSERT_DEVICE_ERROR(wgpu::Buffer errorBuffer = device.CreateBuffer(&errorBufferDescriptor));
|
|
|
|
wgpu::TextureDescriptor errorTextureDescriptor;
|
|
errorTextureDescriptor.size.depthOrArrayLayers = 0;
|
|
ASSERT_DEVICE_ERROR(wgpu::Texture errorTexture = device.CreateTexture(&errorTextureDescriptor));
|
|
|
|
wgpu::ImageCopyBuffer errorImageCopyBuffer = utils::CreateImageCopyBuffer(errorBuffer, 0, 0, 0);
|
|
wgpu::ImageCopyTexture errorImageCopyTexture =
|
|
utils::CreateImageCopyTexture(errorTexture, 0, {0, 0, 0});
|
|
|
|
wgpu::Extent3D extent3D = {0, 0, 0};
|
|
|
|
{
|
|
uint64_t bufferSize = BufferSizeForTextureCopy(4, 4, 1);
|
|
wgpu::Buffer source = CreateBuffer(bufferSize, wgpu::BufferUsage::CopySrc);
|
|
|
|
wgpu::ImageCopyBuffer imageCopyBuffer = utils::CreateImageCopyBuffer(source, 0, 0, 0);
|
|
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
encoder.CopyTextureToBuffer(&errorImageCopyTexture, &imageCopyBuffer, &extent3D);
|
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
|
}
|
|
|
|
{
|
|
wgpu::Texture destination = Create2DTexture(16, 16, 1, 1, wgpu::TextureFormat::RGBA8Unorm,
|
|
wgpu::TextureUsage::CopyDst);
|
|
wgpu::ImageCopyTexture imageCopyTexture =
|
|
utils::CreateImageCopyTexture(destination, 0, {0, 0, 0});
|
|
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
encoder.CopyTextureToBuffer(&imageCopyTexture, &errorImageCopyBuffer, &extent3D);
|
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
|
}
|
|
}
|
|
|
|
// Regression tests for a bug in the computation of texture copy buffer size in Dawn.
|
|
TEST_F(CopyCommandTest_T2B, TextureCopyBufferSizeLastRowComputation) {
|
|
constexpr uint32_t kBytesPerRow = 256;
|
|
constexpr uint32_t kWidth = 4;
|
|
constexpr uint32_t kHeight = 4;
|
|
|
|
constexpr std::array<wgpu::TextureFormat, 2> kFormats = {wgpu::TextureFormat::RGBA8Unorm,
|
|
wgpu::TextureFormat::RG8Unorm};
|
|
|
|
{
|
|
// kBytesPerRow * (kHeight - 1) + kWidth is not large enough to be the valid buffer size in
|
|
// this test because the buffer sizes in T2B copies are not in texels but in bytes.
|
|
constexpr uint32_t kInvalidBufferSize = kBytesPerRow * (kHeight - 1) + kWidth;
|
|
|
|
for (wgpu::TextureFormat format : kFormats) {
|
|
wgpu::Texture source =
|
|
Create2DTexture(kWidth, kHeight, 1, 1, format, wgpu::TextureUsage::CopySrc);
|
|
|
|
wgpu::Buffer destination = CreateBuffer(kInvalidBufferSize, wgpu::BufferUsage::CopyDst);
|
|
TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0,
|
|
kBytesPerRow, kHeight, {kWidth, kHeight, 1});
|
|
}
|
|
}
|
|
|
|
{
|
|
for (wgpu::TextureFormat format : kFormats) {
|
|
uint32_t validBufferSize = BufferSizeForTextureCopy(kWidth, kHeight, 1, format);
|
|
wgpu::Texture source =
|
|
Create2DTexture(kWidth, kHeight, 1, 1, format, wgpu::TextureUsage::CopySrc);
|
|
|
|
// Verify the return value of BufferSizeForTextureCopy() is exactly the minimum valid
|
|
// buffer size in this test.
|
|
{
|
|
uint32_t invalidBufferSize = validBufferSize - 1;
|
|
wgpu::Buffer destination =
|
|
CreateBuffer(invalidBufferSize, wgpu::BufferUsage::CopyDst);
|
|
TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0,
|
|
kBytesPerRow, kHeight, {kWidth, kHeight, 1});
|
|
}
|
|
|
|
{
|
|
wgpu::Buffer destination =
|
|
CreateBuffer(validBufferSize, wgpu::BufferUsage::CopyDst);
|
|
TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0,
|
|
kBytesPerRow, kHeight, {kWidth, kHeight, 1});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Test copy from mip map of non square texture to buffer
|
|
TEST_F(CopyCommandTest_T2B, CopyFromMipmapOfNonSquareTexture) {
|
|
uint32_t maxMipmapLevel = 3;
|
|
wgpu::Texture source = Create2DTexture(4, 2, maxMipmapLevel, 1, wgpu::TextureFormat::RGBA8Unorm,
|
|
wgpu::TextureUsage::CopySrc);
|
|
uint64_t bufferSize = BufferSizeForTextureCopy(4, 2, 1);
|
|
wgpu::Buffer destination = CreateBuffer(bufferSize, wgpu::BufferUsage::CopyDst);
|
|
|
|
// Copy from top level mip map
|
|
TestT2BCopy(utils::Expectation::Success, source, maxMipmapLevel - 1, {0, 0, 0}, destination, 0,
|
|
256, 1, {1, 1, 1});
|
|
// Copy from high level mip map
|
|
TestT2BCopy(utils::Expectation::Success, source, maxMipmapLevel - 2, {0, 0, 0}, destination, 0,
|
|
256, 1, {2, 1, 1});
|
|
// Mip level out of range
|
|
TestT2BCopy(utils::Expectation::Failure, source, maxMipmapLevel, {0, 0, 0}, destination, 0, 256,
|
|
1, {2, 1, 1});
|
|
// Copy origin out of range
|
|
TestT2BCopy(utils::Expectation::Failure, source, maxMipmapLevel - 2, {2, 0, 0}, destination, 0,
|
|
256, 1, {2, 1, 1});
|
|
// Copy size out of range
|
|
TestT2BCopy(utils::Expectation::Failure, source, maxMipmapLevel - 2, {1, 0, 0}, destination, 0,
|
|
256, 1, {2, 1, 1});
|
|
}
|
|
|
|
// Test copy from only the depth aspect of a texture
|
|
TEST_F(CopyCommandTest_T2B, CopyFromDepthAspect) {
|
|
uint64_t bufferSize = BufferSizeForTextureCopy(16, 16, 1, wgpu::TextureFormat::Depth32Float);
|
|
wgpu::Buffer destination = CreateBuffer(bufferSize, wgpu::BufferUsage::CopyDst);
|
|
|
|
constexpr std::array<wgpu::TextureFormat, 3> kAllowDepthCopyFormats = {
|
|
wgpu::TextureFormat::Depth16Unorm, wgpu::TextureFormat::Depth32Float,
|
|
wgpu::TextureFormat::Depth32FloatStencil8};
|
|
for (wgpu::TextureFormat format : kAllowDepthCopyFormats) {
|
|
{
|
|
wgpu::Texture source =
|
|
Create2DTexture(16, 16, 1, 1, format, wgpu::TextureUsage::CopySrc);
|
|
|
|
// Test it is valid to copy the depth aspect of these depth/stencil texture
|
|
TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, 256, 16,
|
|
{16, 16, 1}, wgpu::TextureAspect::DepthOnly);
|
|
|
|
if (utils::IsDepthOnlyFormat(format)) {
|
|
// Test "all" of a depth texture which is only the depth aspect.
|
|
TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, 256,
|
|
16, {16, 16, 1}, wgpu::TextureAspect::All);
|
|
}
|
|
}
|
|
}
|
|
|
|
constexpr std::array<wgpu::TextureFormat, 3> kDisallowDepthCopyFormats = {
|
|
wgpu::TextureFormat::Depth24Plus, wgpu::TextureFormat::Depth24PlusStencil8,
|
|
wgpu::TextureFormat::Depth24UnormStencil8};
|
|
for (wgpu::TextureFormat format : kDisallowDepthCopyFormats) {
|
|
{
|
|
wgpu::Texture source =
|
|
Create2DTexture(16, 16, 1, 1, format, wgpu::TextureUsage::CopySrc);
|
|
|
|
// Test it is invalid to copy from the depth aspect of these depth/stencil texture
|
|
TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, 256, 16,
|
|
{16, 16, 1}, wgpu::TextureAspect::DepthOnly);
|
|
}
|
|
}
|
|
|
|
{
|
|
wgpu::Texture source = Create2DTexture(16, 16, 1, 1, wgpu::TextureFormat::R32Float,
|
|
wgpu::TextureUsage::CopySrc);
|
|
|
|
// Test it is invalid to copy from the depth aspect of a color texture
|
|
TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, 256, 16,
|
|
{16, 16, 1}, wgpu::TextureAspect::DepthOnly);
|
|
}
|
|
}
|
|
|
|
// Test copy from only the stencil aspect of a texture
|
|
TEST_F(CopyCommandTest_T2B, CopyFromStencilAspect) {
|
|
uint64_t bufferSize = BufferSizeForTextureCopy(16, 16, 1, wgpu::TextureFormat::R8Uint);
|
|
wgpu::Buffer destination = CreateBuffer(bufferSize, wgpu::BufferUsage::CopyDst);
|
|
|
|
for (wgpu::TextureFormat format : utils::kStencilFormats) {
|
|
{
|
|
wgpu::Texture source =
|
|
Create2DTexture(16, 16, 1, 1, format, wgpu::TextureUsage::CopySrc);
|
|
|
|
// TODO(dawn:666): Test "all" of Stencil8 format when it's implemented
|
|
|
|
// Test it is valid to copy from the stencil aspect of a depth/stencil texture
|
|
TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, 256, 16,
|
|
{16, 16, 1}, wgpu::TextureAspect::StencilOnly);
|
|
|
|
// Test it is invalid if the buffer is too small
|
|
wgpu::Buffer destinationSmall =
|
|
CreateBuffer(bufferSize - 1, wgpu::BufferUsage::CopyDst);
|
|
TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destinationSmall, 0, 256,
|
|
16, {16, 16, 1}, wgpu::TextureAspect::StencilOnly);
|
|
}
|
|
|
|
// A copy fails when using a depth/stencil texture, and the entire subresource isn't
|
|
// copied
|
|
{
|
|
wgpu::Texture source =
|
|
Create2DTexture(16, 16, 1, 1, format, wgpu::TextureUsage::CopySrc);
|
|
|
|
TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, 256, 15,
|
|
{15, 15, 1}, wgpu::TextureAspect::StencilOnly);
|
|
|
|
TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, 256, 1,
|
|
{1, 1, 1}, wgpu::TextureAspect::StencilOnly);
|
|
}
|
|
|
|
// Non-zero mip: A copy fails when using a depth/stencil texture, and the entire
|
|
// subresource isn't copied
|
|
{
|
|
wgpu::Texture source =
|
|
Create2DTexture(16, 16, 2, 1, format, wgpu::TextureUsage::CopySrc);
|
|
|
|
// Whole mip is success
|
|
TestT2BCopy(utils::Expectation::Success, source, 1, {0, 0, 0}, destination, 0, 256, 8,
|
|
{8, 8, 1}, wgpu::TextureAspect::StencilOnly);
|
|
|
|
// Partial mip fails
|
|
TestT2BCopy(utils::Expectation::Failure, source, 1, {0, 0, 0}, destination, 0, 256, 7,
|
|
{7, 7, 1}, wgpu::TextureAspect::StencilOnly);
|
|
|
|
TestT2BCopy(utils::Expectation::Failure, source, 1, {0, 0, 0}, destination, 0, 256, 1,
|
|
{1, 1, 1}, wgpu::TextureAspect::StencilOnly);
|
|
}
|
|
|
|
// Non-zero mip, non-pow-2: A copy fails when using a depth/stencil texture, and the
|
|
// entire subresource isn't copied
|
|
{
|
|
wgpu::Texture source =
|
|
Create2DTexture(17, 17, 2, 1, format, wgpu::TextureUsage::CopySrc);
|
|
|
|
// Whole mip is success
|
|
TestT2BCopy(utils::Expectation::Success, source, 1, {0, 0, 0}, destination, 0, 256, 8,
|
|
{8, 8, 1}, wgpu::TextureAspect::StencilOnly);
|
|
|
|
// Partial mip fails
|
|
TestT2BCopy(utils::Expectation::Failure, source, 1, {0, 0, 0}, destination, 0, 256, 7,
|
|
{7, 7, 1}, wgpu::TextureAspect::StencilOnly);
|
|
|
|
TestT2BCopy(utils::Expectation::Failure, source, 1, {0, 0, 0}, destination, 0, 256, 1,
|
|
{1, 1, 1}, wgpu::TextureAspect::StencilOnly);
|
|
}
|
|
}
|
|
|
|
{
|
|
wgpu::Texture source =
|
|
Create2DTexture(16, 16, 1, 1, wgpu::TextureFormat::R8Uint, wgpu::TextureUsage::CopySrc);
|
|
|
|
// Test it is invalid to copy from the stencil aspect of a color texture
|
|
TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, 256, 16,
|
|
{16, 16, 1}, wgpu::TextureAspect::StencilOnly);
|
|
}
|
|
{
|
|
wgpu::Texture source = Create2DTexture(16, 16, 1, 1, wgpu::TextureFormat::Depth24Plus,
|
|
wgpu::TextureUsage::CopySrc);
|
|
|
|
// Test it is invalid to copy from the stencil aspect of a depth-only texture
|
|
TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, 256, 16,
|
|
{16, 16, 1}, wgpu::TextureAspect::StencilOnly);
|
|
}
|
|
}
|
|
|
|
// Test that CopyT2B throws an error when requiredBytesInCopy overflows uint64_t
|
|
TEST_F(CopyCommandTest_T2B, RequiredBytesInCopyOverflow) {
|
|
wgpu::Buffer destination = CreateBuffer(10000, wgpu::BufferUsage::CopyDst);
|
|
wgpu::Texture source =
|
|
Create2DTexture(1, 1, 1, 16, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopySrc);
|
|
|
|
// Success
|
|
TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, (1 << 31),
|
|
(1 << 31), {1, 1, 1});
|
|
// Failure because bytesPerImage * (depth - 1) overflows
|
|
TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, (1 << 31),
|
|
(1 << 31), {1, 1, 16});
|
|
}
|
|
|
|
class CopyCommandTest_T2T : public CopyCommandTest {
|
|
protected:
|
|
WGPUDevice CreateTestDevice() override {
|
|
wgpu::DeviceDescriptor descriptor;
|
|
wgpu::FeatureName requiredFeatures[2] = {wgpu::FeatureName::Depth24UnormStencil8,
|
|
wgpu::FeatureName::Depth32FloatStencil8};
|
|
descriptor.requiredFeatures = requiredFeatures;
|
|
descriptor.requiredFeaturesCount = 2;
|
|
return adapter.CreateDevice(&descriptor);
|
|
}
|
|
|
|
wgpu::TextureFormat GetCopyCompatibleFormat(wgpu::TextureFormat format) {
|
|
switch (format) {
|
|
case wgpu::TextureFormat::BGRA8Unorm:
|
|
return wgpu::TextureFormat::BGRA8UnormSrgb;
|
|
case wgpu::TextureFormat::BGRA8UnormSrgb:
|
|
return wgpu::TextureFormat::BGRA8Unorm;
|
|
case wgpu::TextureFormat::RGBA8Unorm:
|
|
return wgpu::TextureFormat::RGBA8UnormSrgb;
|
|
case wgpu::TextureFormat::RGBA8UnormSrgb:
|
|
return wgpu::TextureFormat::RGBA8Unorm;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
};
|
|
|
|
TEST_F(CopyCommandTest_T2T, Success) {
|
|
wgpu::Texture source =
|
|
Create2DTexture(16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopySrc);
|
|
wgpu::Texture destination =
|
|
Create2DTexture(16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst);
|
|
|
|
// Different copies, including some that touch the OOB condition
|
|
{
|
|
// Copy a region along top left boundary
|
|
TestT2TCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, {0, 0, 0},
|
|
{4, 4, 1});
|
|
|
|
// Copy entire texture
|
|
TestT2TCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, {0, 0, 0},
|
|
{16, 16, 1});
|
|
|
|
// Copy a region along bottom right boundary
|
|
TestT2TCopy(utils::Expectation::Success, source, 0, {8, 8, 0}, destination, 0, {8, 8, 0},
|
|
{8, 8, 1});
|
|
|
|
// Copy region into mip
|
|
TestT2TCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 2, {0, 0, 0},
|
|
{4, 4, 1});
|
|
|
|
// Copy mip into region
|
|
TestT2TCopy(utils::Expectation::Success, source, 2, {0, 0, 0}, destination, 0, {0, 0, 0},
|
|
{4, 4, 1});
|
|
|
|
// Copy between slices
|
|
TestT2TCopy(utils::Expectation::Success, source, 0, {0, 0, 1}, destination, 0, {0, 0, 1},
|
|
{16, 16, 1});
|
|
|
|
// Copy multiple slices (srcImageCopyTexture.arrayLayer + copySize.depthOrArrayLayers ==
|
|
// srcImageCopyTexture.texture.arrayLayerCount)
|
|
TestT2TCopy(utils::Expectation::Success, source, 0, {0, 0, 2}, destination, 0, {0, 0, 0},
|
|
{16, 16, 2});
|
|
|
|
// Copy multiple slices (dstImageCopyTexture.arrayLayer + copySize.depthOrArrayLayers ==
|
|
// dstImageCopyTexture.texture.arrayLayerCount)
|
|
TestT2TCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, {0, 0, 2},
|
|
{16, 16, 2});
|
|
}
|
|
|
|
// Empty copies are valid
|
|
{
|
|
// An empty copy
|
|
TestT2TCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, {0, 0, 0},
|
|
{0, 0, 1});
|
|
|
|
// An empty copy with depth = 0
|
|
TestT2TCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, {0, 0, 0},
|
|
{0, 0, 0});
|
|
|
|
// An empty copy touching the side of the source texture
|
|
TestT2TCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, {16, 16, 0},
|
|
{0, 0, 1});
|
|
|
|
// An empty copy touching the side of the destination texture
|
|
TestT2TCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, {16, 16, 0},
|
|
{0, 0, 1});
|
|
}
|
|
}
|
|
|
|
TEST_F(CopyCommandTest_T2T, IncorrectUsage) {
|
|
wgpu::Texture source =
|
|
Create2DTexture(16, 16, 5, 2, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopySrc);
|
|
wgpu::Texture destination =
|
|
Create2DTexture(16, 16, 5, 2, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst);
|
|
|
|
// Incorrect source usage causes failure
|
|
TestT2TCopy(utils::Expectation::Failure, destination, 0, {0, 0, 0}, destination, 0, {0, 0, 0},
|
|
{16, 16, 1});
|
|
|
|
// Incorrect destination usage causes failure
|
|
TestT2TCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, source, 0, {0, 0, 0},
|
|
{16, 16, 1});
|
|
}
|
|
|
|
TEST_F(CopyCommandTest_T2T, OutOfBounds) {
|
|
wgpu::Texture source =
|
|
Create2DTexture(16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopySrc);
|
|
wgpu::Texture destination =
|
|
Create2DTexture(16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst);
|
|
|
|
// OOB on source
|
|
{
|
|
// x + width overflows
|
|
TestT2TCopy(utils::Expectation::Failure, source, 0, {1, 0, 0}, destination, 0, {0, 0, 0},
|
|
{16, 16, 1});
|
|
|
|
// y + height overflows
|
|
TestT2TCopy(utils::Expectation::Failure, source, 0, {0, 1, 0}, destination, 0, {0, 0, 0},
|
|
{16, 16, 1});
|
|
|
|
// non-zero mip overflows
|
|
TestT2TCopy(utils::Expectation::Failure, source, 1, {0, 0, 0}, destination, 0, {0, 0, 0},
|
|
{9, 9, 1});
|
|
|
|
// arrayLayer + depth OOB
|
|
TestT2TCopy(utils::Expectation::Failure, source, 0, {0, 0, 3}, destination, 0, {0, 0, 0},
|
|
{16, 16, 2});
|
|
|
|
// empty copy on non-existent mip fails
|
|
TestT2TCopy(utils::Expectation::Failure, source, 6, {0, 0, 0}, destination, 0, {0, 0, 0},
|
|
{0, 0, 1});
|
|
|
|
// empty copy from non-existent slice fails
|
|
TestT2TCopy(utils::Expectation::Failure, source, 0, {0, 0, 4}, destination, 0, {0, 0, 0},
|
|
{0, 0, 1});
|
|
}
|
|
|
|
// OOB on destination
|
|
{
|
|
// x + width overflows
|
|
TestT2TCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, {1, 0, 0},
|
|
{16, 16, 1});
|
|
|
|
// y + height overflows
|
|
TestT2TCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, {0, 1, 0},
|
|
{16, 16, 1});
|
|
|
|
// non-zero mip overflows
|
|
TestT2TCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 1, {0, 0, 0},
|
|
{9, 9, 1});
|
|
|
|
// arrayLayer + depth OOB
|
|
TestT2TCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, {0, 0, 3},
|
|
{16, 16, 2});
|
|
|
|
// empty copy on non-existent mip fails
|
|
TestT2TCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 6, {0, 0, 0},
|
|
{0, 0, 1});
|
|
|
|
// empty copy on non-existent slice fails
|
|
TestT2TCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, {0, 0, 4},
|
|
{0, 0, 1});
|
|
}
|
|
}
|
|
|
|
TEST_F(CopyCommandTest_T2T, 2DTextureDepthStencil) {
|
|
for (wgpu::TextureFormat format : utils::kDepthAndStencilFormats) {
|
|
wgpu::Texture source = Create2DTexture(16, 16, 1, 1, format, wgpu::TextureUsage::CopySrc);
|
|
|
|
wgpu::Texture destination =
|
|
Create2DTexture(16, 16, 1, 1, format, wgpu::TextureUsage::CopyDst);
|
|
|
|
// Success when entire depth stencil subresource is copied
|
|
TestT2TCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, {0, 0, 0},
|
|
{16, 16, 1});
|
|
|
|
// Failure when depth stencil subresource is partially copied
|
|
TestT2TCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, {0, 0, 0},
|
|
{15, 15, 1});
|
|
|
|
// Failure when selecting the depth aspect (not all)
|
|
TestT2TCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, {0, 0, 0},
|
|
{16, 16, 1}, wgpu::TextureAspect::DepthOnly);
|
|
|
|
// Failure when selecting the stencil aspect (not all)
|
|
TestT2TCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, {0, 0, 0},
|
|
{16, 16, 1}, wgpu::TextureAspect::StencilOnly);
|
|
}
|
|
}
|
|
|
|
TEST_F(CopyCommandTest_T2T, 2DTextureDepthOnly) {
|
|
constexpr std::array<wgpu::TextureFormat, 2> kDepthOnlyFormats = {
|
|
wgpu::TextureFormat::Depth24Plus, wgpu::TextureFormat::Depth32Float};
|
|
|
|
for (wgpu::TextureFormat format : kDepthOnlyFormats) {
|
|
wgpu::Texture source = Create2DTexture(16, 16, 1, 1, format, wgpu::TextureUsage::CopySrc);
|
|
|
|
wgpu::Texture destination =
|
|
Create2DTexture(16, 16, 1, 1, format, wgpu::TextureUsage::CopyDst);
|
|
|
|
// Success when entire subresource is copied
|
|
TestT2TCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, {0, 0, 0},
|
|
{16, 16, 1});
|
|
|
|
// Failure when depth subresource is partially copied
|
|
TestT2TCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, {0, 0, 0},
|
|
{15, 15, 1});
|
|
|
|
// Success when selecting the depth aspect (not all)
|
|
TestT2TCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, {0, 0, 0},
|
|
{16, 16, 1}, wgpu::TextureAspect::DepthOnly);
|
|
|
|
// Failure when selecting the stencil aspect (not all)
|
|
TestT2TCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, {0, 0, 0},
|
|
{16, 16, 1}, wgpu::TextureAspect::StencilOnly);
|
|
}
|
|
}
|
|
|
|
TEST_F(CopyCommandTest_T2T, 2DTextureArrayDepthStencil) {
|
|
for (wgpu::TextureFormat format : utils::kDepthAndStencilFormats) {
|
|
{
|
|
wgpu::Texture source =
|
|
Create2DTexture(16, 16, 1, 3, format, wgpu::TextureUsage::CopySrc);
|
|
wgpu::Texture destination =
|
|
Create2DTexture(16, 16, 1, 1, format, wgpu::TextureUsage::CopyDst);
|
|
|
|
// Success when entire depth stencil subresource (layer) is the copy source
|
|
TestT2TCopy(utils::Expectation::Success, source, 0, {0, 0, 1}, destination, 0,
|
|
{0, 0, 0}, {16, 16, 1});
|
|
}
|
|
|
|
{
|
|
wgpu::Texture source =
|
|
Create2DTexture(16, 16, 1, 1, format, wgpu::TextureUsage::CopySrc);
|
|
wgpu::Texture destination =
|
|
Create2DTexture(16, 16, 1, 3, format, wgpu::TextureUsage::CopyDst);
|
|
|
|
// Success when entire depth stencil subresource (layer) is the copy destination
|
|
TestT2TCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0,
|
|
{0, 0, 1}, {16, 16, 1});
|
|
}
|
|
|
|
{
|
|
wgpu::Texture source =
|
|
Create2DTexture(16, 16, 1, 3, format, wgpu::TextureUsage::CopySrc);
|
|
wgpu::Texture destination =
|
|
Create2DTexture(16, 16, 1, 3, format, wgpu::TextureUsage::CopyDst);
|
|
|
|
// Success when src and dst are an entire depth stencil subresource (layer)
|
|
TestT2TCopy(utils::Expectation::Success, source, 0, {0, 0, 2}, destination, 0,
|
|
{0, 0, 1}, {16, 16, 1});
|
|
|
|
// Success when src and dst are an array of entire depth stencil subresources
|
|
TestT2TCopy(utils::Expectation::Success, source, 0, {0, 0, 1}, destination, 0,
|
|
{0, 0, 0}, {16, 16, 2});
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_F(CopyCommandTest_T2T, FormatsMismatch) {
|
|
wgpu::Texture source =
|
|
Create2DTexture(16, 16, 5, 2, wgpu::TextureFormat::RGBA8Uint, wgpu::TextureUsage::CopySrc);
|
|
wgpu::Texture destination =
|
|
Create2DTexture(16, 16, 5, 2, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst);
|
|
|
|
// Failure when formats don't match
|
|
TestT2TCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, {0, 0, 0},
|
|
{0, 0, 1});
|
|
}
|
|
|
|
// Test copying between textures that have srgb compatible texture formats;
|
|
TEST_F(CopyCommandTest_T2T, SrgbFormatsCompatibility) {
|
|
for (wgpu::TextureFormat srcTextureFormat :
|
|
{wgpu::TextureFormat::BGRA8Unorm, wgpu::TextureFormat::BGRA8UnormSrgb,
|
|
wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureFormat::RGBA8UnormSrgb}) {
|
|
wgpu::TextureFormat dstTextureFormat = GetCopyCompatibleFormat(srcTextureFormat);
|
|
wgpu::Texture source =
|
|
Create2DTexture(16, 16, 5, 2, srcTextureFormat, wgpu::TextureUsage::CopySrc);
|
|
wgpu::Texture destination =
|
|
Create2DTexture(16, 16, 5, 2, dstTextureFormat, wgpu::TextureUsage::CopyDst);
|
|
|
|
// Failure when formats don't match
|
|
TestT2TCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, {0, 0, 0},
|
|
{0, 0, 1});
|
|
}
|
|
}
|
|
|
|
TEST_F(CopyCommandTest_T2T, MultisampledCopies) {
|
|
wgpu::Texture sourceMultiSampled1x = Create2DTexture(
|
|
16, 16, 1, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopySrc, 1);
|
|
wgpu::Texture sourceMultiSampled4x = Create2DTexture(
|
|
16, 16, 1, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopySrc, 4);
|
|
wgpu::Texture destinationMultiSampled4x = Create2DTexture(
|
|
16, 16, 1, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst, 4);
|
|
|
|
// Success when entire multisampled subresource is copied
|
|
{
|
|
TestT2TCopy(utils::Expectation::Success, sourceMultiSampled4x, 0, {0, 0, 0},
|
|
destinationMultiSampled4x, 0, {0, 0, 0}, {16, 16, 1});
|
|
}
|
|
|
|
// Failures
|
|
{
|
|
// An empty copy with mismatched samples fails
|
|
TestT2TCopy(utils::Expectation::Failure, sourceMultiSampled1x, 0, {0, 0, 0},
|
|
destinationMultiSampled4x, 0, {0, 0, 0}, {0, 0, 1});
|
|
|
|
// A copy fails when samples are greater than 1, and entire subresource isn't copied
|
|
TestT2TCopy(utils::Expectation::Failure, sourceMultiSampled4x, 0, {0, 0, 0},
|
|
destinationMultiSampled4x, 0, {0, 0, 0}, {15, 15, 1});
|
|
}
|
|
}
|
|
|
|
// Test copy to mip map of non square textures
|
|
TEST_F(CopyCommandTest_T2T, CopyToMipmapOfNonSquareTexture) {
|
|
uint32_t maxMipmapLevel = 3;
|
|
wgpu::Texture source = Create2DTexture(4, 2, maxMipmapLevel, 1, wgpu::TextureFormat::RGBA8Unorm,
|
|
wgpu::TextureUsage::CopySrc);
|
|
wgpu::Texture destination = Create2DTexture(
|
|
4, 2, maxMipmapLevel, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst);
|
|
// Copy to top level mip map
|
|
TestT2TCopy(utils::Expectation::Success, source, maxMipmapLevel - 1, {0, 0, 0}, destination,
|
|
maxMipmapLevel - 1, {0, 0, 0}, {1, 1, 1});
|
|
// Copy to high level mip map
|
|
TestT2TCopy(utils::Expectation::Success, source, maxMipmapLevel - 2, {0, 0, 0}, destination,
|
|
maxMipmapLevel - 2, {0, 0, 0}, {2, 1, 1});
|
|
// Mip level out of range
|
|
TestT2TCopy(utils::Expectation::Failure, source, maxMipmapLevel, {0, 0, 0}, destination,
|
|
maxMipmapLevel, {0, 0, 0}, {2, 1, 1});
|
|
// Copy origin out of range
|
|
TestT2TCopy(utils::Expectation::Failure, source, maxMipmapLevel - 2, {2, 0, 0}, destination,
|
|
maxMipmapLevel - 2, {2, 0, 0}, {2, 1, 1});
|
|
// Copy size out of range
|
|
TestT2TCopy(utils::Expectation::Failure, source, maxMipmapLevel - 2, {1, 0, 0}, destination,
|
|
maxMipmapLevel - 2, {0, 0, 0}, {2, 1, 1});
|
|
}
|
|
|
|
// Test copy within the same texture
|
|
TEST_F(CopyCommandTest_T2T, CopyWithinSameTexture) {
|
|
wgpu::Texture texture =
|
|
Create2DTexture(32, 32, 2, 4, wgpu::TextureFormat::RGBA8Unorm,
|
|
wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst);
|
|
|
|
// The base array layer of the copy source being equal to that of the copy destination is not
|
|
// allowed.
|
|
{
|
|
constexpr uint32_t kBaseArrayLayer = 0;
|
|
|
|
// copyExtent.z == 1
|
|
{
|
|
constexpr uint32_t kCopyArrayLayerCount = 1;
|
|
TestT2TCopy(utils::Expectation::Failure, texture, 0, {0, 0, kBaseArrayLayer}, texture,
|
|
0, {2, 2, kBaseArrayLayer}, {1, 1, kCopyArrayLayerCount});
|
|
}
|
|
|
|
// copyExtent.z > 1
|
|
{
|
|
constexpr uint32_t kCopyArrayLayerCount = 2;
|
|
TestT2TCopy(utils::Expectation::Failure, texture, 0, {0, 0, kBaseArrayLayer}, texture,
|
|
0, {2, 2, kBaseArrayLayer}, {1, 1, kCopyArrayLayerCount});
|
|
}
|
|
}
|
|
|
|
// The array slices of the source involved in the copy have no overlap with those of the
|
|
// destination is allowed.
|
|
{
|
|
constexpr uint32_t kCopyArrayLayerCount = 2;
|
|
|
|
// srcBaseArrayLayer < dstBaseArrayLayer
|
|
{
|
|
constexpr uint32_t kSrcBaseArrayLayer = 0;
|
|
constexpr uint32_t kDstBaseArrayLayer = kSrcBaseArrayLayer + kCopyArrayLayerCount;
|
|
|
|
TestT2TCopy(utils::Expectation::Success, texture, 0, {0, 0, kSrcBaseArrayLayer},
|
|
texture, 0, {0, 0, kDstBaseArrayLayer}, {1, 1, kCopyArrayLayerCount});
|
|
}
|
|
|
|
// srcBaseArrayLayer > dstBaseArrayLayer
|
|
{
|
|
constexpr uint32_t kSrcBaseArrayLayer = 2;
|
|
constexpr uint32_t kDstBaseArrayLayer = kSrcBaseArrayLayer - kCopyArrayLayerCount;
|
|
TestT2TCopy(utils::Expectation::Success, texture, 0, {0, 0, kSrcBaseArrayLayer},
|
|
texture, 0, {0, 0, kDstBaseArrayLayer}, {1, 1, kCopyArrayLayerCount});
|
|
}
|
|
}
|
|
|
|
// Copy between different mipmap levels is allowed.
|
|
{
|
|
constexpr uint32_t kSrcMipLevel = 0;
|
|
constexpr uint32_t kDstMipLevel = 1;
|
|
|
|
// Copy one slice
|
|
{
|
|
constexpr uint32_t kCopyArrayLayerCount = 1;
|
|
TestT2TCopy(utils::Expectation::Success, texture, kSrcMipLevel, {0, 0, 0}, texture,
|
|
kDstMipLevel, {1, 1, 0}, {1, 1, kCopyArrayLayerCount});
|
|
}
|
|
|
|
// The base array layer of the copy source is equal to that of the copy destination.
|
|
{
|
|
constexpr uint32_t kCopyArrayLayerCount = 2;
|
|
constexpr uint32_t kBaseArrayLayer = 0;
|
|
|
|
TestT2TCopy(utils::Expectation::Success, texture, kSrcMipLevel, {0, 0, kBaseArrayLayer},
|
|
texture, kDstMipLevel, {1, 1, kBaseArrayLayer},
|
|
{1, 1, kCopyArrayLayerCount});
|
|
}
|
|
|
|
// The array slices of the source involved in the copy have overlaps with those of the
|
|
// destination, and the copy areas have overlaps.
|
|
{
|
|
constexpr uint32_t kCopyArrayLayerCount = 2;
|
|
|
|
constexpr uint32_t kSrcBaseArrayLayer = 0;
|
|
constexpr uint32_t kDstBaseArrayLayer = 1;
|
|
ASSERT(kSrcBaseArrayLayer + kCopyArrayLayerCount > kDstBaseArrayLayer);
|
|
|
|
constexpr wgpu::Extent3D kCopyExtent = {1, 1, kCopyArrayLayerCount};
|
|
|
|
TestT2TCopy(utils::Expectation::Success, texture, kSrcMipLevel,
|
|
{0, 0, kSrcBaseArrayLayer}, texture, kDstMipLevel,
|
|
{0, 0, kDstBaseArrayLayer}, kCopyExtent);
|
|
}
|
|
}
|
|
|
|
// The array slices of the source involved in the copy have overlaps with those of the
|
|
// destination is not allowed.
|
|
{
|
|
constexpr uint32_t kMipmapLevel = 0;
|
|
constexpr uint32_t kMinBaseArrayLayer = 0;
|
|
constexpr uint32_t kMaxBaseArrayLayer = 1;
|
|
constexpr uint32_t kCopyArrayLayerCount = 3;
|
|
ASSERT(kMinBaseArrayLayer + kCopyArrayLayerCount > kMaxBaseArrayLayer);
|
|
|
|
constexpr wgpu::Extent3D kCopyExtent = {4, 4, kCopyArrayLayerCount};
|
|
|
|
const wgpu::Origin3D srcOrigin = {0, 0, kMinBaseArrayLayer};
|
|
const wgpu::Origin3D dstOrigin = {4, 4, kMaxBaseArrayLayer};
|
|
TestT2TCopy(utils::Expectation::Failure, texture, kMipmapLevel, srcOrigin, texture,
|
|
kMipmapLevel, dstOrigin, kCopyExtent);
|
|
}
|
|
|
|
// Copy between different mipmap levels and array slices is allowed.
|
|
TestT2TCopy(utils::Expectation::Success, texture, 0, {0, 0, 1}, texture, 1, {1, 1, 0},
|
|
{1, 1, 1});
|
|
|
|
// Copy between 3D texture of both overlapping depth ranges is not allowed.
|
|
{
|
|
wgpu::Texture texture3D =
|
|
Create3DTexture(32, 32, 4, 2, wgpu::TextureFormat::RGBA8Unorm,
|
|
wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst);
|
|
|
|
constexpr uint32_t kMipmapLevel = 0;
|
|
constexpr wgpu::Origin3D kSrcOrigin = {0, 0, 0};
|
|
constexpr wgpu::Origin3D kDstOrigin = {0, 0, 1};
|
|
constexpr wgpu::Extent3D kCopyExtent = {4, 4, 2};
|
|
|
|
TestT2TCopy(utils::Expectation::Failure, texture3D, kMipmapLevel, kSrcOrigin, texture3D,
|
|
kMipmapLevel, kDstOrigin, kCopyExtent);
|
|
}
|
|
|
|
// Copy between 3D texture of both non-overlapping depth ranges is not allowed.
|
|
{
|
|
wgpu::Texture texture3D =
|
|
Create3DTexture(32, 32, 4, 2, wgpu::TextureFormat::RGBA8Unorm,
|
|
wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst);
|
|
|
|
constexpr uint32_t kMipmapLevel = 0;
|
|
constexpr wgpu::Origin3D kSrcOrigin = {0, 0, 0};
|
|
constexpr wgpu::Origin3D kDstOrigin = {0, 0, 2};
|
|
constexpr wgpu::Extent3D kCopyExtent = {4, 4, 1};
|
|
|
|
TestT2TCopy(utils::Expectation::Failure, texture3D, kMipmapLevel, kSrcOrigin, texture3D,
|
|
kMipmapLevel, kDstOrigin, kCopyExtent);
|
|
}
|
|
}
|
|
|
|
class CopyCommandTest_CompressedTextureFormats : public CopyCommandTest {
|
|
protected:
|
|
WGPUDevice CreateTestDevice() override {
|
|
wgpu::DeviceDescriptor descriptor;
|
|
wgpu::FeatureName requiredFeatures[3] = {wgpu::FeatureName::TextureCompressionBC,
|
|
wgpu::FeatureName::TextureCompressionETC2,
|
|
wgpu::FeatureName::TextureCompressionASTC};
|
|
descriptor.requiredFeatures = requiredFeatures;
|
|
descriptor.requiredFeaturesCount = 3;
|
|
return adapter.CreateDevice(&descriptor);
|
|
}
|
|
|
|
wgpu::Texture Create2DTexture(wgpu::TextureFormat format,
|
|
uint32_t mipmapLevels,
|
|
uint32_t width,
|
|
uint32_t height) {
|
|
constexpr wgpu::TextureUsage kUsage = wgpu::TextureUsage::CopyDst |
|
|
wgpu::TextureUsage::CopySrc |
|
|
wgpu::TextureUsage::TextureBinding;
|
|
constexpr uint32_t kArrayLayers = 1;
|
|
return CopyCommandTest::Create2DTexture(width, height, mipmapLevels, kArrayLayers, format,
|
|
kUsage, 1);
|
|
}
|
|
|
|
// By default, we use a 4x4 tiling of the format block size.
|
|
wgpu::Texture Create2DTexture(wgpu::TextureFormat format) {
|
|
uint32_t width = utils::GetTextureFormatBlockWidth(format) * 4;
|
|
uint32_t height = utils::GetTextureFormatBlockHeight(format) * 4;
|
|
return Create2DTexture(format, 1, width, height);
|
|
}
|
|
|
|
wgpu::TextureFormat GetCopyCompatibleFormat(wgpu::TextureFormat format) {
|
|
switch (format) {
|
|
case wgpu::TextureFormat::BC1RGBAUnorm:
|
|
return wgpu::TextureFormat::BC1RGBAUnormSrgb;
|
|
case wgpu::TextureFormat::BC1RGBAUnormSrgb:
|
|
return wgpu::TextureFormat::BC1RGBAUnorm;
|
|
case wgpu::TextureFormat::BC2RGBAUnorm:
|
|
return wgpu::TextureFormat::BC2RGBAUnormSrgb;
|
|
case wgpu::TextureFormat::BC2RGBAUnormSrgb:
|
|
return wgpu::TextureFormat::BC2RGBAUnorm;
|
|
case wgpu::TextureFormat::BC3RGBAUnorm:
|
|
return wgpu::TextureFormat::BC3RGBAUnormSrgb;
|
|
case wgpu::TextureFormat::BC3RGBAUnormSrgb:
|
|
return wgpu::TextureFormat::BC3RGBAUnorm;
|
|
case wgpu::TextureFormat::BC7RGBAUnorm:
|
|
return wgpu::TextureFormat::BC7RGBAUnormSrgb;
|
|
case wgpu::TextureFormat::BC7RGBAUnormSrgb:
|
|
return wgpu::TextureFormat::BC7RGBAUnorm;
|
|
case wgpu::TextureFormat::ETC2RGB8Unorm:
|
|
return wgpu::TextureFormat::ETC2RGB8UnormSrgb;
|
|
case wgpu::TextureFormat::ETC2RGB8UnormSrgb:
|
|
return wgpu::TextureFormat::ETC2RGB8Unorm;
|
|
case wgpu::TextureFormat::ETC2RGB8A1Unorm:
|
|
return wgpu::TextureFormat::ETC2RGB8A1UnormSrgb;
|
|
case wgpu::TextureFormat::ETC2RGB8A1UnormSrgb:
|
|
return wgpu::TextureFormat::ETC2RGB8A1Unorm;
|
|
case wgpu::TextureFormat::ETC2RGBA8Unorm:
|
|
return wgpu::TextureFormat::ETC2RGBA8UnormSrgb;
|
|
case wgpu::TextureFormat::ETC2RGBA8UnormSrgb:
|
|
return wgpu::TextureFormat::ETC2RGBA8Unorm;
|
|
case wgpu::TextureFormat::ASTC4x4Unorm:
|
|
return wgpu::TextureFormat::ASTC4x4UnormSrgb;
|
|
case wgpu::TextureFormat::ASTC4x4UnormSrgb:
|
|
return wgpu::TextureFormat::ASTC4x4Unorm;
|
|
case wgpu::TextureFormat::ASTC5x4Unorm:
|
|
return wgpu::TextureFormat::ASTC5x4UnormSrgb;
|
|
case wgpu::TextureFormat::ASTC5x4UnormSrgb:
|
|
return wgpu::TextureFormat::ASTC5x4Unorm;
|
|
case wgpu::TextureFormat::ASTC5x5Unorm:
|
|
return wgpu::TextureFormat::ASTC5x5UnormSrgb;
|
|
case wgpu::TextureFormat::ASTC5x5UnormSrgb:
|
|
return wgpu::TextureFormat::ASTC5x5Unorm;
|
|
case wgpu::TextureFormat::ASTC6x5Unorm:
|
|
return wgpu::TextureFormat::ASTC6x5UnormSrgb;
|
|
case wgpu::TextureFormat::ASTC6x5UnormSrgb:
|
|
return wgpu::TextureFormat::ASTC6x5Unorm;
|
|
case wgpu::TextureFormat::ASTC6x6Unorm:
|
|
return wgpu::TextureFormat::ASTC6x6UnormSrgb;
|
|
case wgpu::TextureFormat::ASTC6x6UnormSrgb:
|
|
return wgpu::TextureFormat::ASTC6x6Unorm;
|
|
case wgpu::TextureFormat::ASTC8x5Unorm:
|
|
return wgpu::TextureFormat::ASTC8x5UnormSrgb;
|
|
case wgpu::TextureFormat::ASTC8x5UnormSrgb:
|
|
return wgpu::TextureFormat::ASTC8x5Unorm;
|
|
case wgpu::TextureFormat::ASTC8x6Unorm:
|
|
return wgpu::TextureFormat::ASTC8x6UnormSrgb;
|
|
case wgpu::TextureFormat::ASTC8x6UnormSrgb:
|
|
return wgpu::TextureFormat::ASTC8x6Unorm;
|
|
case wgpu::TextureFormat::ASTC8x8Unorm:
|
|
return wgpu::TextureFormat::ASTC8x8UnormSrgb;
|
|
case wgpu::TextureFormat::ASTC8x8UnormSrgb:
|
|
return wgpu::TextureFormat::ASTC8x8Unorm;
|
|
case wgpu::TextureFormat::ASTC10x5Unorm:
|
|
return wgpu::TextureFormat::ASTC10x5UnormSrgb;
|
|
case wgpu::TextureFormat::ASTC10x5UnormSrgb:
|
|
return wgpu::TextureFormat::ASTC10x5Unorm;
|
|
case wgpu::TextureFormat::ASTC10x6Unorm:
|
|
return wgpu::TextureFormat::ASTC10x6UnormSrgb;
|
|
case wgpu::TextureFormat::ASTC10x6UnormSrgb:
|
|
return wgpu::TextureFormat::ASTC10x6Unorm;
|
|
case wgpu::TextureFormat::ASTC10x8Unorm:
|
|
return wgpu::TextureFormat::ASTC10x8UnormSrgb;
|
|
case wgpu::TextureFormat::ASTC10x8UnormSrgb:
|
|
return wgpu::TextureFormat::ASTC10x8Unorm;
|
|
case wgpu::TextureFormat::ASTC10x10Unorm:
|
|
return wgpu::TextureFormat::ASTC10x10UnormSrgb;
|
|
case wgpu::TextureFormat::ASTC10x10UnormSrgb:
|
|
return wgpu::TextureFormat::ASTC10x10Unorm;
|
|
case wgpu::TextureFormat::ASTC12x10Unorm:
|
|
return wgpu::TextureFormat::ASTC12x10UnormSrgb;
|
|
case wgpu::TextureFormat::ASTC12x10UnormSrgb:
|
|
return wgpu::TextureFormat::ASTC12x10Unorm;
|
|
case wgpu::TextureFormat::ASTC12x12Unorm:
|
|
return wgpu::TextureFormat::ASTC12x12UnormSrgb;
|
|
case wgpu::TextureFormat::ASTC12x12UnormSrgb:
|
|
return wgpu::TextureFormat::ASTC12x12Unorm;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
};
|
|
|
|
// Tests to verify that bufferOffset must be a multiple of the compressed texture blocks in bytes
|
|
// in buffer-to-texture or texture-to-buffer copies with compressed texture formats.
|
|
TEST_F(CopyCommandTest_CompressedTextureFormats, BufferOffset) {
|
|
wgpu::Buffer buffer =
|
|
CreateBuffer(512, wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst);
|
|
|
|
for (wgpu::TextureFormat format : utils::kCompressedFormats) {
|
|
wgpu::Texture texture = Create2DTexture(format);
|
|
uint32_t blockWidth = utils::GetTextureFormatBlockWidth(format);
|
|
uint32_t blockHeight = utils::GetTextureFormatBlockHeight(format);
|
|
|
|
// Valid usages of BufferOffset in B2T and T2B copies with compressed texture formats.
|
|
{
|
|
uint32_t validBufferOffset = utils::GetTexelBlockSizeInBytes(format);
|
|
TestBothTBCopies(utils::Expectation::Success, buffer, validBufferOffset, 256, 4,
|
|
texture, 0, {0, 0, 0}, {blockWidth, blockHeight, 1});
|
|
}
|
|
|
|
// Failures on invalid bufferOffset.
|
|
{
|
|
uint32_t kInvalidBufferOffset = utils::GetTexelBlockSizeInBytes(format) / 2;
|
|
TestBothTBCopies(utils::Expectation::Failure, buffer, kInvalidBufferOffset, 256, 4,
|
|
texture, 0, {0, 0, 0}, {blockWidth, blockHeight, 1});
|
|
}
|
|
}
|
|
}
|
|
|
|
// Tests to verify that bytesPerRow must not be less than (width / blockWidth) * blockSizeInBytes.
|
|
// Note that in Dawn we require bytesPerRow be a multiple of 256, which ensures bytesPerRow will
|
|
// always be the multiple of compressed texture block width in bytes.
|
|
TEST_F(CopyCommandTest_CompressedTextureFormats, BytesPerRow) {
|
|
wgpu::Buffer buffer =
|
|
CreateBuffer(1024, wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst);
|
|
|
|
// Used to compute test width and height. We choose 320 because it isn't divisible by 256 and
|
|
// hence will need to be aligned.
|
|
constexpr uint32_t kInvalidBytesPerRow = 320;
|
|
|
|
for (wgpu::TextureFormat format : utils::kCompressedFormats) {
|
|
// Compute the test width and height such that the smallest BytesPerRow is always equal to
|
|
// 320. We choose 320 because it isn't divisible by 256 and hence needs to be aligned.
|
|
uint32_t blockWidth = utils::GetTextureFormatBlockWidth(format);
|
|
uint32_t blockHeight = utils::GetTextureFormatBlockHeight(format);
|
|
uint32_t blockByteSize = utils::GetTexelBlockSizeInBytes(format);
|
|
uint32_t testWidth = kInvalidBytesPerRow * blockWidth / blockByteSize;
|
|
uint32_t testHeight = kInvalidBytesPerRow * blockHeight / blockByteSize;
|
|
wgpu::Texture texture = Create2DTexture(format, 1, testWidth, testHeight);
|
|
|
|
// Failures on the BytesPerRow that is not large enough.
|
|
{
|
|
constexpr uint32_t kSmallBytesPerRow = 256;
|
|
TestBothTBCopies(utils::Expectation::Failure, buffer, 0, kSmallBytesPerRow, 4, texture,
|
|
0, {0, 0, 0}, {testWidth, blockHeight, 1});
|
|
}
|
|
|
|
// Test it is not valid to use a BytesPerRow that is not a multiple of 256.
|
|
{
|
|
TestBothTBCopies(utils::Expectation::Failure, buffer, 0, kInvalidBytesPerRow, 4,
|
|
texture, 0, {0, 0, 0}, {testWidth, blockHeight, 1});
|
|
}
|
|
|
|
// Test the smallest valid BytesPerRow should work.
|
|
{
|
|
uint32_t smallestValidBytesPerRow = Align(kInvalidBytesPerRow, 256);
|
|
TestBothTBCopies(utils::Expectation::Success, buffer, 0, smallestValidBytesPerRow, 4,
|
|
texture, 0, {0, 0, 0}, {testWidth, blockHeight, 1});
|
|
}
|
|
}
|
|
}
|
|
|
|
// rowsPerImage must be >= heightInBlocks.
|
|
TEST_F(CopyCommandTest_CompressedTextureFormats, RowsPerImage) {
|
|
wgpu::Buffer buffer =
|
|
CreateBuffer(1024, wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst);
|
|
|
|
for (wgpu::TextureFormat format : utils::kCompressedFormats) {
|
|
wgpu::Texture texture = Create2DTexture(format);
|
|
uint32_t blockWidth = utils::GetTextureFormatBlockWidth(format);
|
|
uint32_t blockHeight = utils::GetTextureFormatBlockHeight(format);
|
|
|
|
// Valid usages of rowsPerImage in B2T and T2B copies with compressed texture formats.
|
|
{
|
|
constexpr uint32_t kValidRowsPerImage = 5;
|
|
TestBothTBCopies(utils::Expectation::Success, buffer, 0, 256, kValidRowsPerImage,
|
|
texture, 0, {0, 0, 0}, {blockWidth, blockHeight * 4, 1});
|
|
}
|
|
{
|
|
constexpr uint32_t kValidRowsPerImage = 4;
|
|
TestBothTBCopies(utils::Expectation::Success, buffer, 0, 256, kValidRowsPerImage,
|
|
texture, 0, {0, 0, 0}, {blockWidth, blockHeight * 4, 1});
|
|
}
|
|
|
|
// rowsPerImage is smaller than height.
|
|
{
|
|
constexpr uint32_t kInvalidRowsPerImage = 3;
|
|
TestBothTBCopies(utils::Expectation::Failure, buffer, 0, 256, kInvalidRowsPerImage,
|
|
texture, 0, {0, 0, 0}, {blockWidth, blockHeight * 5, 1});
|
|
}
|
|
}
|
|
}
|
|
|
|
// Tests to verify that ImageOffset.x must be a multiple of the compressed texture block width and
|
|
// ImageOffset.y must be a multiple of the compressed texture block height in buffer-to-texture,
|
|
// texture-to-buffer or texture-to-texture copies with compressed texture formats.
|
|
TEST_F(CopyCommandTest_CompressedTextureFormats, ImageOffset) {
|
|
wgpu::Buffer buffer =
|
|
CreateBuffer(512, wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst);
|
|
|
|
for (wgpu::TextureFormat format : utils::kCompressedFormats) {
|
|
wgpu::Texture texture = Create2DTexture(format);
|
|
wgpu::Texture texture2 = Create2DTexture(format);
|
|
uint32_t blockWidth = utils::GetTextureFormatBlockWidth(format);
|
|
uint32_t blockHeight = utils::GetTextureFormatBlockHeight(format);
|
|
|
|
wgpu::Origin3D smallestValidOrigin3D = {blockWidth, blockHeight, 0};
|
|
|
|
// Valid usages of ImageOffset in B2T, T2B and T2T copies with compressed texture formats.
|
|
{
|
|
TestBothTBCopies(utils::Expectation::Success, buffer, 0, 256, 4, texture, 0,
|
|
smallestValidOrigin3D, {blockWidth, blockHeight, 1});
|
|
TestBothT2TCopies(utils::Expectation::Success, texture, 0, {0, 0, 0}, texture2, 0,
|
|
smallestValidOrigin3D, {blockWidth, blockHeight, 1});
|
|
}
|
|
|
|
// Failures on invalid ImageOffset.x.
|
|
{
|
|
wgpu::Origin3D invalidOrigin3D = {smallestValidOrigin3D.x - 1, smallestValidOrigin3D.y,
|
|
0};
|
|
TestBothTBCopies(utils::Expectation::Failure, buffer, 0, 256, 4, texture, 0,
|
|
invalidOrigin3D, {blockWidth, blockHeight, 1});
|
|
TestBothT2TCopies(utils::Expectation::Failure, texture, 0, invalidOrigin3D, texture2, 0,
|
|
{0, 0, 0}, {blockWidth, blockHeight, 1});
|
|
}
|
|
|
|
// Failures on invalid ImageOffset.y.
|
|
{
|
|
wgpu::Origin3D invalidOrigin3D = {smallestValidOrigin3D.x, smallestValidOrigin3D.y - 1,
|
|
0};
|
|
TestBothTBCopies(utils::Expectation::Failure, buffer, 0, 256, 4, texture, 0,
|
|
invalidOrigin3D, {blockWidth, blockHeight, 1});
|
|
TestBothT2TCopies(utils::Expectation::Failure, texture, 0, invalidOrigin3D, texture2, 0,
|
|
{0, 0, 0}, {blockWidth, blockHeight, 1});
|
|
}
|
|
}
|
|
}
|
|
|
|
// Tests to verify that ImageExtent.x must be a multiple of the compressed texture block width and
|
|
// ImageExtent.y must be a multiple of the compressed texture block height in buffer-to-texture,
|
|
// texture-to-buffer or texture-to-texture copies with compressed texture formats.
|
|
TEST_F(CopyCommandTest_CompressedTextureFormats, ImageExtent) {
|
|
wgpu::Buffer buffer =
|
|
CreateBuffer(1024, wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst);
|
|
|
|
constexpr uint32_t kMipmapLevels = 3;
|
|
// We choose a prime that is greater than the current max texel dimension size as a multiplier
|
|
// to compute the test texture size so that we can be certain that its level 2 mipmap (x4)
|
|
// cannot be a multiple of the dimension. This is useful for testing padding at the edges of
|
|
// the mipmaps.
|
|
constexpr uint32_t kBlockPerDim = 13;
|
|
|
|
for (wgpu::TextureFormat format : utils::kCompressedFormats) {
|
|
uint32_t blockWidth = utils::GetTextureFormatBlockWidth(format);
|
|
uint32_t blockHeight = utils::GetTextureFormatBlockHeight(format);
|
|
uint32_t testWidth = blockWidth * kBlockPerDim;
|
|
uint32_t testHeight = blockHeight * kBlockPerDim;
|
|
wgpu::Texture texture = Create2DTexture(format, kMipmapLevels, testWidth, testHeight);
|
|
wgpu::Texture texture2 = Create2DTexture(format, kMipmapLevels, testWidth, testHeight);
|
|
|
|
wgpu::Extent3D smallestValidExtent3D = {blockWidth, blockHeight, 1};
|
|
|
|
// Valid usages of ImageExtent in B2T, T2B and T2T copies with compressed texture formats.
|
|
{
|
|
TestBothTBCopies(utils::Expectation::Success, buffer, 0, 256, 4, texture, 0, {0, 0, 0},
|
|
smallestValidExtent3D);
|
|
TestBothT2TCopies(utils::Expectation::Success, texture, 0, {0, 0, 0}, texture2, 0,
|
|
{0, 0, 0}, smallestValidExtent3D);
|
|
}
|
|
|
|
// Valid usages of ImageExtent in B2T, T2B and T2T copies with compressed texture formats
|
|
// and non-zero mipmap levels.
|
|
{
|
|
constexpr uint32_t kTestMipmapLevel = 2;
|
|
wgpu::Origin3D testOrigin = {
|
|
((testWidth >> kTestMipmapLevel) / blockWidth) * blockWidth,
|
|
((testHeight >> kTestMipmapLevel) / blockHeight) * blockHeight, 0};
|
|
TestBothTBCopies(utils::Expectation::Success, buffer, 0, 256, 4, texture,
|
|
kTestMipmapLevel, testOrigin, smallestValidExtent3D);
|
|
TestBothT2TCopies(utils::Expectation::Success, texture, kTestMipmapLevel, testOrigin,
|
|
texture2, 0, {0, 0, 0}, smallestValidExtent3D);
|
|
}
|
|
|
|
// Failures on invalid ImageExtent.x.
|
|
{
|
|
wgpu::Extent3D inValidExtent3D = {smallestValidExtent3D.width - 1,
|
|
smallestValidExtent3D.height, 1};
|
|
TestBothTBCopies(utils::Expectation::Failure, buffer, 0, 256, 4, texture, 0, {0, 0, 0},
|
|
inValidExtent3D);
|
|
TestBothT2TCopies(utils::Expectation::Failure, texture, 0, {0, 0, 0}, texture2, 0,
|
|
{0, 0, 0}, inValidExtent3D);
|
|
}
|
|
|
|
// Failures on invalid ImageExtent.y.
|
|
{
|
|
wgpu::Extent3D inValidExtent3D = {smallestValidExtent3D.width,
|
|
smallestValidExtent3D.height - 1, 1};
|
|
TestBothTBCopies(utils::Expectation::Failure, buffer, 0, 256, 4, texture, 0, {0, 0, 0},
|
|
inValidExtent3D);
|
|
TestBothT2TCopies(utils::Expectation::Failure, texture, 0, {0, 0, 0}, texture2, 0,
|
|
{0, 0, 0}, inValidExtent3D);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Test copies between buffer and multiple array layers of a compressed texture
|
|
TEST_F(CopyCommandTest_CompressedTextureFormats, CopyToMultipleArrayLayers) {
|
|
constexpr uint32_t kWidthMultiplier = 3;
|
|
constexpr uint32_t kHeightMultiplier = 4;
|
|
for (wgpu::TextureFormat format : utils::kCompressedFormats) {
|
|
uint32_t blockWidth = utils::GetTextureFormatBlockWidth(format);
|
|
uint32_t blockHeight = utils::GetTextureFormatBlockHeight(format);
|
|
uint32_t testWidth = kWidthMultiplier * blockWidth;
|
|
uint32_t testHeight = kHeightMultiplier * blockHeight;
|
|
wgpu::Texture texture = CopyCommandTest::Create2DTexture(
|
|
testWidth, testHeight, 1, 20, format,
|
|
wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc);
|
|
|
|
// Copy to all array layers
|
|
TestBothTBCopiesExactBufferSize(256, 4, texture, format, {0, 0, 0},
|
|
{testWidth, testHeight, 20});
|
|
|
|
// Copy to the highest array layer
|
|
TestBothTBCopiesExactBufferSize(256, 4, texture, format, {0, 0, 19},
|
|
{testWidth, testHeight, 1});
|
|
|
|
// Copy to array layers in the middle
|
|
TestBothTBCopiesExactBufferSize(256, 4, texture, format, {0, 0, 1},
|
|
{testWidth, testHeight, 18});
|
|
|
|
// Copy touching the texture corners with a non-packed rowsPerImage
|
|
TestBothTBCopiesExactBufferSize(256, 6, texture, format, {blockWidth, blockHeight, 4},
|
|
{testWidth - blockWidth, testHeight - blockHeight, 16});
|
|
}
|
|
}
|
|
|
|
// Test copying between textures that have srgb compatible texture formats;
|
|
TEST_F(CopyCommandTest_CompressedTextureFormats, SrgbFormatCompatibility) {
|
|
constexpr std::array<wgpu::TextureFormat, 42> srcFormats = {
|
|
wgpu::TextureFormat::BC1RGBAUnorm, wgpu::TextureFormat::BC1RGBAUnormSrgb,
|
|
wgpu::TextureFormat::BC2RGBAUnorm, wgpu::TextureFormat::BC2RGBAUnormSrgb,
|
|
wgpu::TextureFormat::BC3RGBAUnorm, wgpu::TextureFormat::BC3RGBAUnormSrgb,
|
|
wgpu::TextureFormat::BC7RGBAUnorm, wgpu::TextureFormat::BC7RGBAUnormSrgb,
|
|
wgpu::TextureFormat::ETC2RGB8Unorm, wgpu::TextureFormat::ETC2RGB8UnormSrgb,
|
|
wgpu::TextureFormat::ETC2RGB8A1Unorm, wgpu::TextureFormat::ETC2RGB8A1UnormSrgb,
|
|
wgpu::TextureFormat::ETC2RGBA8Unorm, wgpu::TextureFormat::ETC2RGBA8UnormSrgb,
|
|
wgpu::TextureFormat::ASTC4x4Unorm, wgpu::TextureFormat::ASTC4x4UnormSrgb,
|
|
wgpu::TextureFormat::ASTC5x4Unorm, wgpu::TextureFormat::ASTC5x4UnormSrgb,
|
|
wgpu::TextureFormat::ASTC5x5Unorm, wgpu::TextureFormat::ASTC5x5UnormSrgb,
|
|
wgpu::TextureFormat::ASTC6x5Unorm, wgpu::TextureFormat::ASTC6x5UnormSrgb,
|
|
wgpu::TextureFormat::ASTC6x6Unorm, wgpu::TextureFormat::ASTC6x6UnormSrgb,
|
|
wgpu::TextureFormat::ASTC8x5Unorm, wgpu::TextureFormat::ASTC8x5UnormSrgb,
|
|
wgpu::TextureFormat::ASTC8x6Unorm, wgpu::TextureFormat::ASTC8x6UnormSrgb,
|
|
wgpu::TextureFormat::ASTC8x8Unorm, wgpu::TextureFormat::ASTC8x8UnormSrgb,
|
|
wgpu::TextureFormat::ASTC10x5Unorm, wgpu::TextureFormat::ASTC10x5UnormSrgb,
|
|
wgpu::TextureFormat::ASTC10x6Unorm, wgpu::TextureFormat::ASTC10x6UnormSrgb,
|
|
wgpu::TextureFormat::ASTC10x8Unorm, wgpu::TextureFormat::ASTC10x8UnormSrgb,
|
|
wgpu::TextureFormat::ASTC10x10Unorm, wgpu::TextureFormat::ASTC10x10UnormSrgb,
|
|
wgpu::TextureFormat::ASTC12x10Unorm, wgpu::TextureFormat::ASTC12x10UnormSrgb,
|
|
wgpu::TextureFormat::ASTC12x12Unorm, wgpu::TextureFormat::ASTC12x12UnormSrgb};
|
|
|
|
constexpr uint32_t kBlockPerDim = 2;
|
|
constexpr uint32_t kMipmapLevels = 1;
|
|
for (wgpu::TextureFormat srcFormat : srcFormats) {
|
|
uint32_t blockWidth = utils::GetTextureFormatBlockWidth(srcFormat);
|
|
uint32_t blockHeight = utils::GetTextureFormatBlockHeight(srcFormat);
|
|
uint32_t testWidth = blockWidth * kBlockPerDim;
|
|
uint32_t testHeight = blockHeight * kBlockPerDim;
|
|
wgpu::Texture texture = Create2DTexture(srcFormat, kMipmapLevels, testWidth, testHeight);
|
|
wgpu::Texture texture2 = Create2DTexture(GetCopyCompatibleFormat(srcFormat), kMipmapLevels,
|
|
testWidth, testHeight);
|
|
wgpu::Extent3D extent3D = {testWidth, testHeight, 1};
|
|
|
|
TestBothT2TCopies(utils::Expectation::Success, texture, 0, {0, 0, 0}, texture2, 0,
|
|
{0, 0, 0}, extent3D);
|
|
}
|
|
}
|
|
|
|
class CopyCommandTest_ClearBuffer : public CopyCommandTest {};
|
|
|
|
TEST_F(CopyCommandTest_ClearBuffer, Success) {
|
|
wgpu::Buffer destination = CreateBuffer(16, wgpu::BufferUsage::CopyDst);
|
|
|
|
// Clear different ranges, including some that touch the OOB condition
|
|
{
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
encoder.ClearBuffer(destination, 0, 16);
|
|
encoder.ClearBuffer(destination, 0, 8);
|
|
encoder.ClearBuffer(destination, 8, 8);
|
|
encoder.Finish();
|
|
}
|
|
|
|
// Size is allowed to be omitted
|
|
{
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
encoder.ClearBuffer(destination, 0);
|
|
encoder.ClearBuffer(destination, 8);
|
|
encoder.Finish();
|
|
}
|
|
|
|
// Size and Offset are allowed to be omitted
|
|
{
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
encoder.ClearBuffer(destination);
|
|
encoder.Finish();
|
|
}
|
|
}
|
|
|
|
// Test a successful ClearBuffer where the last external reference is dropped.
|
|
TEST_F(CopyCommandTest_ClearBuffer, DroppedBuffer) {
|
|
wgpu::Buffer destination = CreateBuffer(16, wgpu::BufferUsage::CopyDst);
|
|
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
encoder.ClearBuffer(destination, 0, 8);
|
|
wgpu::CommandBuffer commandBuffer = encoder.Finish();
|
|
|
|
destination = nullptr;
|
|
device.GetQueue().Submit(1, &commandBuffer);
|
|
}
|
|
|
|
// Test ClearBuffer copies with OOB
|
|
TEST_F(CopyCommandTest_ClearBuffer, OutOfBounds) {
|
|
wgpu::Buffer destination = CreateBuffer(16, wgpu::BufferUsage::CopyDst);
|
|
|
|
{
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
encoder.ClearBuffer(destination, 8, 12);
|
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
|
}
|
|
|
|
{
|
|
// Despite being zero length, should still raise an error due to being out of bounds.
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
encoder.ClearBuffer(destination, 20, 0);
|
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
|
}
|
|
}
|
|
|
|
// Test ClearBuffer with incorrect buffer usage
|
|
TEST_F(CopyCommandTest_ClearBuffer, BadUsage) {
|
|
wgpu::Buffer vertex = CreateBuffer(16, wgpu::BufferUsage::Vertex);
|
|
|
|
// Destination with incorrect usage
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
encoder.ClearBuffer(vertex, 0, 16);
|
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
|
}
|
|
|
|
// Test ClearBuffer with unaligned data size
|
|
TEST_F(CopyCommandTest_ClearBuffer, UnalignedSize) {
|
|
wgpu::Buffer destination = CreateBuffer(16, wgpu::BufferUsage::CopyDst);
|
|
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
encoder.ClearBuffer(destination, 0, 2);
|
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
|
}
|
|
|
|
// Test ClearBuffer with unaligned offset
|
|
TEST_F(CopyCommandTest_ClearBuffer, UnalignedOffset) {
|
|
wgpu::Buffer destination = CreateBuffer(16, wgpu::BufferUsage::CopyDst);
|
|
|
|
// Unaligned destination offset
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
encoder.ClearBuffer(destination, 2, 4);
|
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
|
}
|
|
|
|
// Test ClearBuffer with buffers in error state cause errors.
|
|
TEST_F(CopyCommandTest_ClearBuffer, BuffersInErrorState) {
|
|
wgpu::BufferDescriptor errorBufferDescriptor;
|
|
errorBufferDescriptor.size = 4;
|
|
errorBufferDescriptor.usage =
|
|
wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst;
|
|
ASSERT_DEVICE_ERROR(wgpu::Buffer errorBuffer = device.CreateBuffer(&errorBufferDescriptor));
|
|
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
encoder.ClearBuffer(errorBuffer, 0, 4);
|
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
|
}
|