mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-08-11 06:29:11 +00:00
Vulkan backend had been disabled on Intel Windows Vulkan driver < 30.0.101.2111, these suppressed end2end tests are fixed on that version and later, we can remove the suppression. Bug: dawn:1392, dawn:819, dawn:1172, dawn:1489 Change-Id: I91a8dfac833b068e8e3bb70d9f9af977f7b6026c Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/113610 Kokoro: Kokoro <noreply+kokoro@google.com> Reviewed-by: Corentin Wallez <cwallez@chromium.org> Commit-Queue: Austin Eng <enga@chromium.org> Reviewed-by: Austin Eng <enga@chromium.org>
2928 lines
119 KiB
C++
2928 lines
119 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 <algorithm>
|
|
#include <array>
|
|
#include <vector>
|
|
|
|
#include "dawn/common/Constants.h"
|
|
#include "dawn/common/Math.h"
|
|
#include "dawn/tests/DawnTest.h"
|
|
#include "dawn/utils/TestUtils.h"
|
|
#include "dawn/utils/TextureUtils.h"
|
|
#include "dawn/utils/WGPUHelpers.h"
|
|
|
|
// For MinimumBufferSpec bytesPerRow and rowsPerImage, compute a default from the copy extent.
|
|
constexpr uint32_t kStrideComputeDefault = 0xFFFF'FFFEul;
|
|
|
|
constexpr wgpu::TextureFormat kDefaultFormat = wgpu::TextureFormat::RGBA8Unorm;
|
|
|
|
class CopyTests {
|
|
protected:
|
|
struct TextureSpec {
|
|
wgpu::TextureFormat format = kDefaultFormat;
|
|
wgpu::Origin3D copyOrigin = {0, 0, 0};
|
|
wgpu::Extent3D textureSize;
|
|
uint32_t copyLevel = 0;
|
|
uint32_t levelCount = 1;
|
|
};
|
|
|
|
struct BufferSpec {
|
|
uint64_t size;
|
|
uint64_t offset;
|
|
uint32_t bytesPerRow;
|
|
uint32_t rowsPerImage;
|
|
};
|
|
|
|
static std::vector<uint8_t> GetExpectedTextureData(const utils::TextureDataCopyLayout& layout) {
|
|
uint32_t bytesPerTexelBlock = layout.bytesPerRow / layout.texelBlocksPerRow;
|
|
std::vector<uint8_t> textureData(layout.byteLength);
|
|
for (uint32_t layer = 0; layer < layout.mipSize.depthOrArrayLayers; ++layer) {
|
|
const uint32_t byteOffsetPerSlice = layout.bytesPerImage * layer;
|
|
for (uint32_t y = 0; y < layout.mipSize.height; ++y) {
|
|
for (uint32_t x = 0; x < layout.mipSize.width * bytesPerTexelBlock; ++x) {
|
|
uint32_t i = x + y * layout.bytesPerRow;
|
|
textureData[byteOffsetPerSlice + i] =
|
|
static_cast<uint8_t>((x + 1 + (layer + 1) * y) % 256);
|
|
}
|
|
}
|
|
}
|
|
return textureData;
|
|
}
|
|
|
|
// TODO(crbug.com/dawn/818): remove this function when all the tests in this file support
|
|
// testing arbitrary formats.
|
|
static std::vector<utils::RGBA8> GetExpectedTextureDataRGBA8(
|
|
const utils::TextureDataCopyLayout& layout) {
|
|
std::vector<utils::RGBA8> textureData(layout.texelBlockCount);
|
|
for (uint32_t layer = 0; layer < layout.mipSize.depthOrArrayLayers; ++layer) {
|
|
const uint32_t texelIndexOffsetPerSlice = layout.texelBlocksPerImage * layer;
|
|
for (uint32_t y = 0; y < layout.mipSize.height; ++y) {
|
|
for (uint32_t x = 0; x < layout.mipSize.width; ++x) {
|
|
uint32_t i = x + y * layout.texelBlocksPerRow;
|
|
textureData[texelIndexOffsetPerSlice + i] =
|
|
utils::RGBA8(static_cast<uint8_t>((x + layer * x) % 256),
|
|
static_cast<uint8_t>((y + layer * y) % 256),
|
|
static_cast<uint8_t>(x / 256), static_cast<uint8_t>(y / 256));
|
|
}
|
|
}
|
|
}
|
|
|
|
return textureData;
|
|
}
|
|
|
|
static BufferSpec MinimumBufferSpec(uint32_t width,
|
|
uint32_t height,
|
|
uint32_t depth = 1,
|
|
wgpu::TextureFormat format = kDefaultFormat) {
|
|
return MinimumBufferSpec({width, height, depth}, kStrideComputeDefault,
|
|
depth == 1 ? wgpu::kCopyStrideUndefined : kStrideComputeDefault,
|
|
format);
|
|
}
|
|
|
|
static BufferSpec MinimumBufferSpec(wgpu::Extent3D copyExtent,
|
|
uint32_t overrideBytesPerRow = kStrideComputeDefault,
|
|
uint32_t overrideRowsPerImage = kStrideComputeDefault,
|
|
wgpu::TextureFormat format = kDefaultFormat) {
|
|
uint32_t bytesPerRow = utils::GetMinimumBytesPerRow(format, copyExtent.width);
|
|
if (overrideBytesPerRow != kStrideComputeDefault) {
|
|
bytesPerRow = overrideBytesPerRow;
|
|
}
|
|
uint32_t rowsPerImage = copyExtent.height;
|
|
if (overrideRowsPerImage != kStrideComputeDefault) {
|
|
rowsPerImage = overrideRowsPerImage;
|
|
}
|
|
|
|
uint32_t totalDataSize =
|
|
utils::RequiredBytesInCopy(bytesPerRow, rowsPerImage, copyExtent, format);
|
|
return {totalDataSize, 0, bytesPerRow, rowsPerImage};
|
|
}
|
|
static void CopyTextureData(uint32_t bytesPerTexelBlock,
|
|
const void* srcData,
|
|
uint32_t widthInBlocks,
|
|
uint32_t heightInBlocks,
|
|
uint32_t depthInBlocks,
|
|
uint32_t srcBytesPerRow,
|
|
uint32_t srcRowsPerImage,
|
|
void* dstData,
|
|
uint32_t dstBytesPerRow,
|
|
uint32_t dstRowsPerImage) {
|
|
for (unsigned int z = 0; z < depthInBlocks; ++z) {
|
|
uint32_t srcDepthOffset = z * srcBytesPerRow * srcRowsPerImage;
|
|
uint32_t dstDepthOffset = z * dstBytesPerRow * dstRowsPerImage;
|
|
for (unsigned int y = 0; y < heightInBlocks; ++y) {
|
|
memcpy(static_cast<uint8_t*>(dstData) + dstDepthOffset + y * dstBytesPerRow,
|
|
static_cast<const uint8_t*>(srcData) + srcDepthOffset + y * srcBytesPerRow,
|
|
widthInBlocks * bytesPerTexelBlock);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
class CopyTests_T2B : public CopyTests, public DawnTest {
|
|
protected:
|
|
void DoTest(const TextureSpec& textureSpec,
|
|
const BufferSpec& bufferSpec,
|
|
const wgpu::Extent3D& copySize,
|
|
wgpu::TextureDimension dimension = wgpu::TextureDimension::e2D) {
|
|
// TODO(crbug.com/dawn/818): support testing arbitrary formats
|
|
ASSERT_EQ(kDefaultFormat, textureSpec.format);
|
|
|
|
const uint32_t bytesPerTexel = utils::GetTexelBlockSizeInBytes(textureSpec.format);
|
|
// Create a texture that is `width` x `height` with (`level` + 1) mip levels.
|
|
wgpu::TextureDescriptor descriptor;
|
|
descriptor.dimension = dimension;
|
|
descriptor.size = textureSpec.textureSize;
|
|
descriptor.sampleCount = 1;
|
|
descriptor.format = textureSpec.format;
|
|
descriptor.mipLevelCount = textureSpec.levelCount;
|
|
descriptor.usage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc;
|
|
wgpu::Texture texture = device.CreateTexture(&descriptor);
|
|
|
|
// Layout for initial data upload to texture.
|
|
// Some parts of this result are also reused later.
|
|
const utils::TextureDataCopyLayout copyLayout =
|
|
utils::GetTextureDataCopyLayoutForTextureAtLevel(
|
|
textureSpec.format, textureSpec.textureSize, textureSpec.copyLevel, dimension);
|
|
|
|
// Initialize the source texture
|
|
std::vector<utils::RGBA8> textureArrayData = GetExpectedTextureDataRGBA8(copyLayout);
|
|
{
|
|
wgpu::ImageCopyTexture imageCopyTexture =
|
|
utils::CreateImageCopyTexture(texture, textureSpec.copyLevel, {0, 0, 0});
|
|
wgpu::TextureDataLayout textureDataLayout =
|
|
utils::CreateTextureDataLayout(0, copyLayout.bytesPerRow, copyLayout.rowsPerImage);
|
|
queue.WriteTexture(&imageCopyTexture, textureArrayData.data(), copyLayout.byteLength,
|
|
&textureDataLayout, ©Layout.mipSize);
|
|
}
|
|
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
|
|
// Create a buffer of `size` and populate it with empty data (0,0,0,0) Note:
|
|
// Prepopulating the buffer with empty data ensures that there is not random data in the
|
|
// expectation and helps ensure that the padding due to the bytes per row is not modified
|
|
// by the copy.
|
|
wgpu::BufferDescriptor bufferDesc;
|
|
bufferDesc.size = bufferSpec.size;
|
|
bufferDesc.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst;
|
|
wgpu::Buffer buffer = device.CreateBuffer(&bufferDesc);
|
|
|
|
{
|
|
wgpu::ImageCopyTexture imageCopyTexture = utils::CreateImageCopyTexture(
|
|
texture, textureSpec.copyLevel, textureSpec.copyOrigin);
|
|
wgpu::ImageCopyBuffer imageCopyBuffer = utils::CreateImageCopyBuffer(
|
|
buffer, bufferSpec.offset, bufferSpec.bytesPerRow, bufferSpec.rowsPerImage);
|
|
encoder.CopyTextureToBuffer(&imageCopyTexture, &imageCopyBuffer, ©Size);
|
|
}
|
|
|
|
wgpu::CommandBuffer commands = encoder.Finish();
|
|
queue.Submit(1, &commands);
|
|
|
|
uint64_t bufferOffset = bufferSpec.offset;
|
|
|
|
uint32_t copyLayer = copySize.depthOrArrayLayers;
|
|
uint32_t copyDepth = 1;
|
|
if (dimension == wgpu::TextureDimension::e3D) {
|
|
copyLayer = 1;
|
|
copyDepth = copySize.depthOrArrayLayers;
|
|
}
|
|
|
|
const wgpu::Extent3D copySizePerLayer = {copySize.width, copySize.height, copyDepth};
|
|
// Texels in single layer.
|
|
const uint32_t texelCountInCopyRegion = utils::GetTexelCountInCopyRegion(
|
|
bufferSpec.bytesPerRow, bufferSpec.rowsPerImage, copySizePerLayer, textureSpec.format);
|
|
const uint32_t maxArrayLayer = textureSpec.copyOrigin.z + copyLayer;
|
|
std::vector<utils::RGBA8> expected(texelCountInCopyRegion);
|
|
for (uint32_t layer = textureSpec.copyOrigin.z; layer < maxArrayLayer; ++layer) {
|
|
// Copy the data used to create the upload buffer in the specified copy region to have
|
|
// the same format as the expected buffer data.
|
|
std::fill(expected.begin(), expected.end(), utils::RGBA8());
|
|
const uint32_t texelIndexOffset = copyLayout.texelBlocksPerImage * layer;
|
|
const uint32_t expectedTexelArrayDataStartIndex =
|
|
texelIndexOffset + (textureSpec.copyOrigin.x +
|
|
textureSpec.copyOrigin.y * copyLayout.texelBlocksPerRow);
|
|
|
|
CopyTextureData(bytesPerTexel,
|
|
textureArrayData.data() + expectedTexelArrayDataStartIndex,
|
|
copySize.width, copySize.height, copyDepth, copyLayout.bytesPerRow,
|
|
copyLayout.rowsPerImage, expected.data(), bufferSpec.bytesPerRow,
|
|
bufferSpec.rowsPerImage);
|
|
|
|
EXPECT_BUFFER_U32_RANGE_EQ(reinterpret_cast<const uint32_t*>(expected.data()), buffer,
|
|
bufferOffset, static_cast<uint32_t>(expected.size()))
|
|
<< "Texture to Buffer copy failed copying region [(" << textureSpec.copyOrigin.x
|
|
<< ", " << textureSpec.copyOrigin.y << ", " << textureSpec.copyOrigin.z << "), ("
|
|
<< textureSpec.copyOrigin.x + copySize.width << ", "
|
|
<< textureSpec.copyOrigin.y + copySize.height << ", "
|
|
<< textureSpec.copyOrigin.z + copySize.depthOrArrayLayers << ")) from "
|
|
<< textureSpec.textureSize.width << " x " << textureSpec.textureSize.height
|
|
<< " texture at mip level " << textureSpec.copyLevel << " layer " << layer << " to "
|
|
<< bufferSpec.size << "-byte buffer with offset " << bufferOffset
|
|
<< " and bytes per row " << bufferSpec.bytesPerRow << std::endl;
|
|
|
|
bufferOffset += bufferSpec.bytesPerRow * bufferSpec.rowsPerImage;
|
|
}
|
|
}
|
|
};
|
|
|
|
class CopyTests_B2T : public CopyTests, public DawnTest {
|
|
protected:
|
|
static void FillBufferData(utils::RGBA8* data, size_t count) {
|
|
for (size_t i = 0; i < count; ++i) {
|
|
data[i] =
|
|
utils::RGBA8(static_cast<uint8_t>(i % 256), static_cast<uint8_t>((i / 256) % 256),
|
|
static_cast<uint8_t>((i / 256 / 256) % 256), 255);
|
|
}
|
|
}
|
|
|
|
void DoTest(const TextureSpec& textureSpec,
|
|
const BufferSpec& bufferSpec,
|
|
const wgpu::Extent3D& copySize,
|
|
wgpu::TextureDimension dimension = wgpu::TextureDimension::e2D) {
|
|
// TODO(crbug.com/dawn/818): support testing arbitrary formats
|
|
ASSERT_EQ(kDefaultFormat, textureSpec.format);
|
|
// Create a buffer of size `size` and populate it with data
|
|
const uint32_t bytesPerTexel = utils::GetTexelBlockSizeInBytes(textureSpec.format);
|
|
std::vector<utils::RGBA8> bufferData(bufferSpec.size / bytesPerTexel);
|
|
FillBufferData(bufferData.data(), bufferData.size());
|
|
wgpu::Buffer buffer =
|
|
utils::CreateBufferFromData(device, bufferData.data(), bufferSpec.size,
|
|
wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst);
|
|
|
|
// Create a texture that is `width` x `height` with (`level` + 1) mip levels.
|
|
wgpu::TextureDescriptor descriptor;
|
|
descriptor.dimension = dimension;
|
|
descriptor.size = textureSpec.textureSize;
|
|
descriptor.sampleCount = 1;
|
|
descriptor.format = textureSpec.format;
|
|
descriptor.mipLevelCount = textureSpec.levelCount;
|
|
descriptor.usage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc;
|
|
wgpu::Texture texture = device.CreateTexture(&descriptor);
|
|
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
|
|
wgpu::ImageCopyBuffer imageCopyBuffer = utils::CreateImageCopyBuffer(
|
|
buffer, bufferSpec.offset, bufferSpec.bytesPerRow, bufferSpec.rowsPerImage);
|
|
wgpu::ImageCopyTexture imageCopyTexture =
|
|
utils::CreateImageCopyTexture(texture, textureSpec.copyLevel, textureSpec.copyOrigin);
|
|
encoder.CopyBufferToTexture(&imageCopyBuffer, &imageCopyTexture, ©Size);
|
|
|
|
wgpu::CommandBuffer commands = encoder.Finish();
|
|
queue.Submit(1, &commands);
|
|
|
|
const utils::TextureDataCopyLayout copyLayout =
|
|
utils::GetTextureDataCopyLayoutForTextureAtLevel(
|
|
textureSpec.format, textureSpec.textureSize, textureSpec.copyLevel, dimension,
|
|
bufferSpec.rowsPerImage);
|
|
|
|
uint32_t copyLayer = copySize.depthOrArrayLayers;
|
|
uint32_t copyDepth = 1;
|
|
if (dimension == wgpu::TextureDimension::e3D) {
|
|
copyLayer = 1;
|
|
copyDepth = copySize.depthOrArrayLayers;
|
|
}
|
|
|
|
uint64_t bufferOffset = bufferSpec.offset;
|
|
const uint32_t blockWidth = utils::GetTextureFormatBlockWidth(textureSpec.format);
|
|
const uint32_t blockHeight = utils::GetTextureFormatBlockHeight(textureSpec.format);
|
|
const uint32_t texelCountPerLayer = copyDepth * (copyLayout.mipSize.width / blockWidth) *
|
|
(copyLayout.mipSize.height / blockHeight) *
|
|
bytesPerTexel;
|
|
for (uint32_t layer = 0; layer < copyLayer; ++layer) {
|
|
// Copy and pack the data used to create the buffer in the specified copy region to have
|
|
// the same format as the expected texture data.
|
|
std::vector<utils::RGBA8> expected(texelCountPerLayer);
|
|
CopyTextureData(bytesPerTexel, bufferData.data() + bufferOffset / bytesPerTexel,
|
|
copySize.width, copySize.height, copyDepth, bufferSpec.bytesPerRow,
|
|
bufferSpec.rowsPerImage, expected.data(),
|
|
copySize.width * bytesPerTexel, copySize.height);
|
|
|
|
EXPECT_TEXTURE_EQ(expected.data(), texture,
|
|
{textureSpec.copyOrigin.x, textureSpec.copyOrigin.y,
|
|
textureSpec.copyOrigin.z + layer},
|
|
{copySize.width, copySize.height, copyDepth}, textureSpec.copyLevel)
|
|
<< "Buffer to Texture copy failed copying " << bufferSpec.size
|
|
<< "-byte buffer with offset " << bufferSpec.offset << " and bytes per row "
|
|
<< bufferSpec.bytesPerRow << " to [(" << textureSpec.copyOrigin.x << ", "
|
|
<< textureSpec.copyOrigin.y << "), (" << textureSpec.copyOrigin.x + copySize.width
|
|
<< ", " << textureSpec.copyOrigin.y + copySize.height << ")) region of "
|
|
<< textureSpec.textureSize.width << " x " << textureSpec.textureSize.height
|
|
<< " texture at mip level " << textureSpec.copyLevel << " layer " << layer
|
|
<< std::endl;
|
|
bufferOffset += copyLayout.bytesPerImage;
|
|
}
|
|
}
|
|
};
|
|
|
|
namespace {
|
|
// The CopyTests Texture to Texture in this class will validate both CopyTextureToTexture and
|
|
// CopyTextureToTextureInternal.
|
|
using UsageCopySrc = bool;
|
|
DAWN_TEST_PARAM_STRUCT(CopyTestsParams, UsageCopySrc);
|
|
|
|
using SrcColorFormat = wgpu::TextureFormat;
|
|
DAWN_TEST_PARAM_STRUCT(SrcColorFormatParams, SrcColorFormat);
|
|
} // namespace
|
|
|
|
template <typename Parent>
|
|
class CopyTests_T2TBase : public CopyTests, public Parent {
|
|
protected:
|
|
std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
|
|
return {wgpu::FeatureName::DawnInternalUsages};
|
|
}
|
|
|
|
void DoTest(const TextureSpec& srcSpec,
|
|
const TextureSpec& dstSpec,
|
|
const wgpu::Extent3D& copySize,
|
|
wgpu::TextureDimension srcDimension,
|
|
wgpu::TextureDimension dstDimension,
|
|
bool copyWithinSameTexture = false,
|
|
bool usageCopySrc = false) {
|
|
const wgpu::TextureFormat format = srcSpec.format;
|
|
|
|
wgpu::TextureDescriptor srcDescriptor;
|
|
srcDescriptor.dimension = srcDimension;
|
|
srcDescriptor.size = srcSpec.textureSize;
|
|
srcDescriptor.sampleCount = 1;
|
|
srcDescriptor.format = format;
|
|
srcDescriptor.mipLevelCount = srcSpec.levelCount;
|
|
srcDescriptor.usage = wgpu::TextureUsage::CopyDst;
|
|
// This test will have two versions, one where we check the normal CopyToCopy, and for that
|
|
// we will add the CopySrc usage, and the one where we test the CopyToCopyInternal, and then
|
|
// we have to add the CopySrc to the Internal usage.
|
|
wgpu::DawnTextureInternalUsageDescriptor internalDesc = {};
|
|
srcDescriptor.nextInChain = &internalDesc;
|
|
if (usageCopySrc) {
|
|
srcDescriptor.usage |= wgpu::TextureUsage::CopySrc;
|
|
} else {
|
|
internalDesc.internalUsage = wgpu::TextureUsage::CopySrc;
|
|
}
|
|
wgpu::Texture srcTexture = this->device.CreateTexture(&srcDescriptor);
|
|
|
|
wgpu::Texture dstTexture;
|
|
if (copyWithinSameTexture) {
|
|
dstTexture = srcTexture;
|
|
} else {
|
|
wgpu::TextureDescriptor dstDescriptor;
|
|
dstDescriptor.dimension = dstDimension;
|
|
dstDescriptor.size = dstSpec.textureSize;
|
|
dstDescriptor.sampleCount = 1;
|
|
dstDescriptor.format = dstSpec.format;
|
|
dstDescriptor.mipLevelCount = dstSpec.levelCount;
|
|
dstDescriptor.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst;
|
|
dstTexture = this->device.CreateTexture(&dstDescriptor);
|
|
}
|
|
|
|
// Create an upload buffer and use it to completely populate the subresources of the src
|
|
// texture that will be copied from at the given mip level.
|
|
const utils::TextureDataCopyLayout srcDataCopyLayout =
|
|
utils::GetTextureDataCopyLayoutForTextureAtLevel(
|
|
format,
|
|
{srcSpec.textureSize.width, srcSpec.textureSize.height,
|
|
srcDimension == wgpu::TextureDimension::e3D
|
|
? srcSpec.textureSize.depthOrArrayLayers
|
|
: copySize.depthOrArrayLayers},
|
|
srcSpec.copyLevel, srcDimension);
|
|
|
|
// Initialize the source texture
|
|
const std::vector<uint8_t> srcTextureCopyData = GetExpectedTextureData(srcDataCopyLayout);
|
|
{
|
|
wgpu::ImageCopyTexture imageCopyTexture = utils::CreateImageCopyTexture(
|
|
srcTexture, srcSpec.copyLevel, {0, 0, srcSpec.copyOrigin.z});
|
|
wgpu::TextureDataLayout textureDataLayout = utils::CreateTextureDataLayout(
|
|
0, srcDataCopyLayout.bytesPerRow, srcDataCopyLayout.rowsPerImage);
|
|
this->queue.WriteTexture(&imageCopyTexture, srcTextureCopyData.data(),
|
|
srcDataCopyLayout.byteLength, &textureDataLayout,
|
|
&srcDataCopyLayout.mipSize);
|
|
}
|
|
|
|
wgpu::CommandEncoder encoder = this->device.CreateCommandEncoder();
|
|
|
|
// Perform the texture to texture copy
|
|
wgpu::ImageCopyTexture srcImageCopyTexture =
|
|
utils::CreateImageCopyTexture(srcTexture, srcSpec.copyLevel, srcSpec.copyOrigin);
|
|
wgpu::ImageCopyTexture dstImageCopyTexture =
|
|
utils::CreateImageCopyTexture(dstTexture, dstSpec.copyLevel, dstSpec.copyOrigin);
|
|
|
|
if (usageCopySrc) {
|
|
encoder.CopyTextureToTexture(&srcImageCopyTexture, &dstImageCopyTexture, ©Size);
|
|
} else {
|
|
encoder.CopyTextureToTextureInternal(&srcImageCopyTexture, &dstImageCopyTexture,
|
|
©Size);
|
|
}
|
|
|
|
// Create an output buffer and use it to completely populate the subresources of the dst
|
|
// texture that will be copied to at the given mip level.
|
|
const utils::TextureDataCopyLayout dstDataCopyLayout =
|
|
utils::GetTextureDataCopyLayoutForTextureAtLevel(
|
|
format,
|
|
{dstSpec.textureSize.width, dstSpec.textureSize.height,
|
|
dstDimension == wgpu::TextureDimension::e3D
|
|
? dstSpec.textureSize.depthOrArrayLayers
|
|
: copySize.depthOrArrayLayers},
|
|
dstSpec.copyLevel, dstDimension);
|
|
wgpu::BufferDescriptor outputBufferDescriptor;
|
|
outputBufferDescriptor.size = dstDataCopyLayout.byteLength;
|
|
outputBufferDescriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst;
|
|
wgpu::Buffer outputBuffer = this->device.CreateBuffer(&outputBufferDescriptor);
|
|
const uint32_t bytesPerTexel = utils::GetTexelBlockSizeInBytes(format);
|
|
const uint32_t expectedDstDataOffset = dstSpec.copyOrigin.x * bytesPerTexel +
|
|
dstSpec.copyOrigin.y * dstDataCopyLayout.bytesPerRow;
|
|
wgpu::ImageCopyBuffer outputImageCopyBuffer = utils::CreateImageCopyBuffer(
|
|
outputBuffer, expectedDstDataOffset, dstDataCopyLayout.bytesPerRow,
|
|
dstDataCopyLayout.rowsPerImage);
|
|
encoder.CopyTextureToBuffer(&dstImageCopyTexture, &outputImageCopyBuffer, ©Size);
|
|
|
|
wgpu::CommandBuffer commands = encoder.Finish();
|
|
this->queue.Submit(1, &commands);
|
|
|
|
// Validate if the data in outputBuffer is what we expected, including the untouched data
|
|
// outside of the copy.
|
|
{
|
|
// Validate the output buffer slice-by-slice regardless of whether the destination
|
|
// texture is 3D or 2D. The dimension here doesn't matter - we're only populating the
|
|
// CPU data to verify against.
|
|
uint32_t copyLayer = copySize.depthOrArrayLayers;
|
|
uint32_t copyDepth = 1;
|
|
|
|
const uint64_t validDataSizePerDstTextureLayer = utils::RequiredBytesInCopy(
|
|
dstDataCopyLayout.bytesPerRow, dstDataCopyLayout.mipSize.height,
|
|
dstDataCopyLayout.mipSize.width, dstDataCopyLayout.mipSize.height, copyDepth,
|
|
bytesPerTexel);
|
|
|
|
// expectedDstDataPerSlice stores one layer of the destination texture.
|
|
std::vector<uint8_t> expectedDstDataPerSlice(validDataSizePerDstTextureLayer);
|
|
for (uint32_t slice = 0; slice < copyLayer; ++slice) {
|
|
// For each source texture array slice involved in the copy, emulate the T2T copy
|
|
// on the CPU side by "copying" the copy data from the "source texture"
|
|
// (srcTextureCopyData) to the "destination texture" (expectedDstDataPerSlice).
|
|
std::fill(expectedDstDataPerSlice.begin(), expectedDstDataPerSlice.end(), 0);
|
|
|
|
const uint32_t srcBytesOffset = srcDataCopyLayout.bytesPerImage * slice;
|
|
|
|
// Get the offset of the srcTextureCopyData that contains the copy data on the
|
|
// slice-th texture array layer of the source texture.
|
|
const uint32_t srcTexelDataOffset =
|
|
srcBytesOffset + (srcSpec.copyOrigin.x * bytesPerTexel +
|
|
srcSpec.copyOrigin.y * srcDataCopyLayout.bytesPerRow);
|
|
// Do the T2T "copy" on the CPU side to get the expected texel value at the
|
|
CopyTextureData(bytesPerTexel, &srcTextureCopyData[srcTexelDataOffset],
|
|
copySize.width, copySize.height, copyDepth,
|
|
srcDataCopyLayout.bytesPerRow, srcDataCopyLayout.rowsPerImage,
|
|
&expectedDstDataPerSlice[expectedDstDataOffset],
|
|
dstDataCopyLayout.bytesPerRow, dstDataCopyLayout.rowsPerImage);
|
|
|
|
// Compare the content of the destination texture at the (dstSpec.copyOrigin.z +
|
|
// slice)-th layer to its expected data after the copy (the outputBuffer contains
|
|
// the data of the destination texture since the dstSpec.copyOrigin.z-th layer).
|
|
uint64_t outputBufferExpectationBytesOffset =
|
|
dstDataCopyLayout.bytesPerImage * slice;
|
|
EXPECT_BUFFER_U32_RANGE_EQ(
|
|
reinterpret_cast<const uint32_t*>(expectedDstDataPerSlice.data()), outputBuffer,
|
|
outputBufferExpectationBytesOffset,
|
|
validDataSizePerDstTextureLayer / sizeof(uint32_t));
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
class CopyTests_T2T : public CopyTests_T2TBase<DawnTestWithParams<CopyTestsParams>> {
|
|
protected:
|
|
void DoTest(const TextureSpec& srcSpec,
|
|
const TextureSpec& dstSpec,
|
|
const wgpu::Extent3D& copySize,
|
|
bool copyWithinSameTexture = false,
|
|
wgpu::TextureDimension dimension = wgpu::TextureDimension::e2D) {
|
|
DoTest(srcSpec, dstSpec, copySize, dimension, dimension, copyWithinSameTexture);
|
|
}
|
|
|
|
void DoTest(const TextureSpec& srcSpec,
|
|
const TextureSpec& dstSpec,
|
|
const wgpu::Extent3D& copySize,
|
|
wgpu::TextureDimension srcDimension,
|
|
wgpu::TextureDimension dstDimension,
|
|
bool copyWithinSameTexture = false) {
|
|
const bool usageCopySrc = GetParam().mUsageCopySrc;
|
|
// If we do this test with a CopyWithinSameTexture, it will need to have usageCopySrc in the
|
|
// public usage of the texture as it will later use a CopyTextureToBuffer, that needs the
|
|
// public usage of it.
|
|
DAWN_TEST_UNSUPPORTED_IF(!usageCopySrc && copyWithinSameTexture);
|
|
|
|
ASSERT_EQ(srcSpec.format, dstSpec.format);
|
|
|
|
CopyTests_T2TBase<DawnTestWithParams<CopyTestsParams>>::DoTest(
|
|
srcSpec, dstSpec, copySize, srcDimension, dstDimension, copyWithinSameTexture,
|
|
usageCopySrc);
|
|
}
|
|
};
|
|
|
|
class CopyTests_Formats : public CopyTests_T2TBase<DawnTestWithParams<SrcColorFormatParams>> {
|
|
protected:
|
|
// Texture format is compatible and could be copied to each other if the only diff is srgb-ness.
|
|
wgpu::TextureFormat GetCopyCompatibleFormat(wgpu::TextureFormat format) {
|
|
switch (format) {
|
|
case wgpu::TextureFormat::RGBA8Unorm:
|
|
return wgpu::TextureFormat::RGBA8UnormSrgb;
|
|
case wgpu::TextureFormat::RGBA8UnormSrgb:
|
|
return wgpu::TextureFormat::RGBA8Unorm;
|
|
case wgpu::TextureFormat::BGRA8Unorm:
|
|
return wgpu::TextureFormat::BGRA8UnormSrgb;
|
|
case wgpu::TextureFormat::BGRA8UnormSrgb:
|
|
return wgpu::TextureFormat::BGRA8Unorm;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
void DoTest(TextureSpec srcSpec,
|
|
TextureSpec dstSpec,
|
|
const wgpu::Extent3D& copySize,
|
|
wgpu::TextureDimension srcDimension = wgpu::TextureDimension::e2D,
|
|
wgpu::TextureDimension dstDimension = wgpu::TextureDimension::e2D) {
|
|
srcSpec.format = GetParam().mSrcColorFormat;
|
|
dstSpec.format = GetCopyCompatibleFormat(srcSpec.format);
|
|
|
|
CopyTests_T2TBase<DawnTestWithParams<SrcColorFormatParams>>::DoTest(
|
|
srcSpec, dstSpec, copySize, srcDimension, dstDimension);
|
|
}
|
|
};
|
|
|
|
class CopyTests_B2B : public DawnTest {
|
|
protected:
|
|
// This is the same signature as CopyBufferToBuffer except that the buffers are replaced by
|
|
// only their size.
|
|
void DoTest(uint64_t sourceSize,
|
|
uint64_t sourceOffset,
|
|
uint64_t destinationSize,
|
|
uint64_t destinationOffset,
|
|
uint64_t copySize) {
|
|
ASSERT(sourceSize % 4 == 0);
|
|
ASSERT(destinationSize % 4 == 0);
|
|
|
|
// Create our two test buffers, destination filled with zeros, source filled with non-zeroes
|
|
std::vector<uint32_t> zeroes(static_cast<size_t>(destinationSize / sizeof(uint32_t)));
|
|
wgpu::Buffer destination =
|
|
utils::CreateBufferFromData(device, zeroes.data(), zeroes.size() * sizeof(uint32_t),
|
|
wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::CopySrc);
|
|
|
|
std::vector<uint32_t> sourceData(static_cast<size_t>(sourceSize / sizeof(uint32_t)));
|
|
for (size_t i = 0; i < sourceData.size(); i++) {
|
|
sourceData[i] = i + 1;
|
|
}
|
|
wgpu::Buffer source = utils::CreateBufferFromData(device, sourceData.data(),
|
|
sourceData.size() * sizeof(uint32_t),
|
|
wgpu::BufferUsage::CopySrc);
|
|
|
|
// Submit the copy
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
encoder.CopyBufferToBuffer(source, sourceOffset, destination, destinationOffset, copySize);
|
|
wgpu::CommandBuffer commands = encoder.Finish();
|
|
queue.Submit(1, &commands);
|
|
|
|
// Check destination is exactly the expected content.
|
|
EXPECT_BUFFER_U32_RANGE_EQ(zeroes.data(), destination, 0,
|
|
destinationOffset / sizeof(uint32_t));
|
|
EXPECT_BUFFER_U32_RANGE_EQ(sourceData.data() + sourceOffset / sizeof(uint32_t), destination,
|
|
destinationOffset, copySize / sizeof(uint32_t));
|
|
uint64_t copyEnd = destinationOffset + copySize;
|
|
EXPECT_BUFFER_U32_RANGE_EQ(zeroes.data(), destination, copyEnd,
|
|
(destinationSize - copyEnd) / sizeof(uint32_t));
|
|
}
|
|
};
|
|
|
|
class ClearBufferTests : public DawnTest {
|
|
protected:
|
|
// This is the same signature as ClearBuffer except that the buffers are replaced by
|
|
// only their size.
|
|
void DoTest(uint64_t bufferSize, uint64_t clearOffset, uint64_t clearSize) {
|
|
ASSERT(bufferSize % 4 == 0);
|
|
ASSERT(clearSize % 4 == 0);
|
|
|
|
// Create our test buffer, filled with non-zeroes
|
|
std::vector<uint32_t> bufferData(static_cast<size_t>(bufferSize / sizeof(uint32_t)));
|
|
for (size_t i = 0; i < bufferData.size(); i++) {
|
|
bufferData[i] = i + 1;
|
|
}
|
|
wgpu::Buffer buffer = utils::CreateBufferFromData(
|
|
device, bufferData.data(), bufferData.size() * sizeof(uint32_t),
|
|
wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::CopySrc);
|
|
|
|
std::vector<uint8_t> fillData(static_cast<size_t>(clearSize), 0u);
|
|
|
|
// Submit the fill
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
encoder.ClearBuffer(buffer, clearOffset, clearSize);
|
|
wgpu::CommandBuffer commands = encoder.Finish();
|
|
queue.Submit(1, &commands);
|
|
|
|
// Check destination is exactly the expected content.
|
|
EXPECT_BUFFER_U32_RANGE_EQ(bufferData.data(), buffer, 0, clearOffset / sizeof(uint32_t));
|
|
EXPECT_BUFFER_U8_RANGE_EQ(fillData.data(), buffer, clearOffset, clearSize);
|
|
uint64_t clearEnd = clearOffset + clearSize;
|
|
EXPECT_BUFFER_U32_RANGE_EQ(bufferData.data() + clearEnd / sizeof(uint32_t), buffer,
|
|
clearEnd, (bufferSize - clearEnd) / sizeof(uint32_t));
|
|
}
|
|
};
|
|
|
|
// Test that copying an entire texture with 256-byte aligned dimensions works
|
|
TEST_P(CopyTests_T2B, FullTextureAligned) {
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {kWidth, kHeight, 1};
|
|
|
|
DoTest(textureSpec, MinimumBufferSpec(kWidth, kHeight), {kWidth, kHeight, 1});
|
|
}
|
|
|
|
// Test noop copies
|
|
TEST_P(CopyTests_T2B, ZeroSizedCopy) {
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {kWidth, kHeight, 1};
|
|
|
|
DoTest(textureSpec, MinimumBufferSpec(kWidth, kHeight), {0, kHeight, 1});
|
|
DoTest(textureSpec, MinimumBufferSpec(kWidth, kHeight), {kWidth, 0, 1});
|
|
DoTest(textureSpec, MinimumBufferSpec(kWidth, kHeight), {kWidth, kHeight, 0});
|
|
}
|
|
|
|
// Test that copying an entire texture without 256-byte aligned dimensions works
|
|
TEST_P(CopyTests_T2B, FullTextureUnaligned) {
|
|
constexpr uint32_t kWidth = 259;
|
|
constexpr uint32_t kHeight = 127;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {kWidth, kHeight, 1};
|
|
|
|
DoTest(textureSpec, MinimumBufferSpec(kWidth, kHeight), {kWidth, kHeight, 1});
|
|
}
|
|
|
|
// Test that reading pixels from a 256-byte aligned texture works
|
|
TEST_P(CopyTests_T2B, PixelReadAligned) {
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
BufferSpec pixelBuffer = MinimumBufferSpec(1, 1);
|
|
|
|
constexpr wgpu::Extent3D kCopySize = {1, 1, 1};
|
|
constexpr wgpu::Extent3D kTextureSize = {kWidth, kHeight, 1};
|
|
TextureSpec defaultTextureSpec;
|
|
defaultTextureSpec.textureSize = kTextureSize;
|
|
|
|
{
|
|
TextureSpec textureSpec = defaultTextureSpec;
|
|
DoTest(textureSpec, pixelBuffer, kCopySize);
|
|
}
|
|
|
|
{
|
|
TextureSpec textureSpec = defaultTextureSpec;
|
|
textureSpec.copyOrigin = {kWidth - 1, 0, 0};
|
|
DoTest(textureSpec, pixelBuffer, kCopySize);
|
|
}
|
|
|
|
{
|
|
TextureSpec textureSpec = defaultTextureSpec;
|
|
textureSpec.copyOrigin = {0, kHeight - 1, 0};
|
|
DoTest(textureSpec, pixelBuffer, kCopySize);
|
|
}
|
|
|
|
{
|
|
TextureSpec textureSpec = defaultTextureSpec;
|
|
textureSpec.copyOrigin = {kWidth - 1, kHeight - 1, 0};
|
|
DoTest(textureSpec, pixelBuffer, kCopySize);
|
|
}
|
|
|
|
{
|
|
TextureSpec textureSpec = defaultTextureSpec;
|
|
textureSpec.copyOrigin = {kWidth / 3, kHeight / 7, 0};
|
|
DoTest(textureSpec, pixelBuffer, kCopySize);
|
|
}
|
|
|
|
{
|
|
TextureSpec textureSpec = defaultTextureSpec;
|
|
textureSpec.copyOrigin = {kWidth / 7, kHeight / 3, 0};
|
|
DoTest(textureSpec, pixelBuffer, kCopySize);
|
|
}
|
|
}
|
|
|
|
// Test that copying pixels from a texture that is not 256-byte aligned works
|
|
TEST_P(CopyTests_T2B, PixelReadUnaligned) {
|
|
constexpr uint32_t kWidth = 259;
|
|
constexpr uint32_t kHeight = 127;
|
|
BufferSpec pixelBuffer = MinimumBufferSpec(1, 1);
|
|
|
|
constexpr wgpu::Extent3D kCopySize = {1, 1, 1};
|
|
constexpr wgpu::Extent3D kTextureSize = {kWidth, kHeight, 1};
|
|
TextureSpec defaultTextureSpec;
|
|
defaultTextureSpec.textureSize = kTextureSize;
|
|
|
|
{
|
|
TextureSpec textureSpec = defaultTextureSpec;
|
|
DoTest(textureSpec, pixelBuffer, kCopySize);
|
|
}
|
|
|
|
{
|
|
TextureSpec textureSpec = defaultTextureSpec;
|
|
textureSpec.copyOrigin = {kWidth - 1, 0, 0};
|
|
DoTest(textureSpec, pixelBuffer, kCopySize);
|
|
}
|
|
|
|
{
|
|
TextureSpec textureSpec = defaultTextureSpec;
|
|
textureSpec.copyOrigin = {0, kHeight - 1, 0};
|
|
DoTest(textureSpec, pixelBuffer, kCopySize);
|
|
}
|
|
|
|
{
|
|
TextureSpec textureSpec = defaultTextureSpec;
|
|
textureSpec.copyOrigin = {kWidth - 1, kHeight - 1, 0};
|
|
DoTest(textureSpec, pixelBuffer, kCopySize);
|
|
}
|
|
|
|
{
|
|
TextureSpec textureSpec = defaultTextureSpec;
|
|
textureSpec.copyOrigin = {kWidth / 3, kHeight / 7, 0};
|
|
DoTest(textureSpec, pixelBuffer, kCopySize);
|
|
}
|
|
|
|
{
|
|
TextureSpec textureSpec = defaultTextureSpec;
|
|
textureSpec.copyOrigin = {kWidth / 7, kHeight / 3, 0};
|
|
DoTest(textureSpec, pixelBuffer, kCopySize);
|
|
}
|
|
}
|
|
|
|
// Test that copying regions with 256-byte aligned sizes works
|
|
TEST_P(CopyTests_T2B, TextureRegionAligned) {
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
for (unsigned int w : {64, 128, 256}) {
|
|
for (unsigned int h : {16, 32, 48}) {
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {kWidth, kHeight, 1};
|
|
DoTest(textureSpec, MinimumBufferSpec(w, h), {w, h, 1});
|
|
}
|
|
}
|
|
}
|
|
|
|
// Test that copying regions without 256-byte aligned sizes works
|
|
TEST_P(CopyTests_T2B, TextureRegionUnaligned) {
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
|
|
TextureSpec defaultTextureSpec;
|
|
defaultTextureSpec.textureSize = {kWidth, kHeight, 1};
|
|
|
|
for (unsigned int w : {13, 63, 65}) {
|
|
for (unsigned int h : {17, 19, 63}) {
|
|
TextureSpec textureSpec = defaultTextureSpec;
|
|
DoTest(textureSpec, MinimumBufferSpec(w, h), {w, h, 1});
|
|
}
|
|
}
|
|
}
|
|
|
|
// Test that copying mips with 256-byte aligned sizes works
|
|
TEST_P(CopyTests_T2B, TextureMipAligned) {
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
|
|
TextureSpec defaultTextureSpec;
|
|
defaultTextureSpec.textureSize = {kWidth, kHeight, 1};
|
|
|
|
for (unsigned int i = 1; i < 4; ++i) {
|
|
TextureSpec textureSpec = defaultTextureSpec;
|
|
textureSpec.copyLevel = i;
|
|
textureSpec.levelCount = i + 1;
|
|
DoTest(textureSpec, MinimumBufferSpec(kWidth >> i, kHeight >> i),
|
|
{kWidth >> i, kHeight >> i, 1});
|
|
}
|
|
}
|
|
|
|
// Test that copying mips when one dimension is 256-byte aligned and another dimension reach one
|
|
// works
|
|
TEST_P(CopyTests_T2B, TextureMipDimensionReachOne) {
|
|
constexpr uint32_t mipLevelCount = 4;
|
|
constexpr uint32_t kWidth = 256 << mipLevelCount;
|
|
constexpr uint32_t kHeight = 2;
|
|
|
|
TextureSpec defaultTextureSpec;
|
|
defaultTextureSpec.textureSize = {kWidth, kHeight, 1};
|
|
|
|
TextureSpec textureSpec = defaultTextureSpec;
|
|
textureSpec.levelCount = mipLevelCount;
|
|
|
|
for (unsigned int i = 0; i < 4; ++i) {
|
|
textureSpec.copyLevel = i;
|
|
DoTest(textureSpec,
|
|
MinimumBufferSpec(std::max(kWidth >> i, 1u), std::max(kHeight >> i, 1u)),
|
|
{std::max(kWidth >> i, 1u), std::max(kHeight >> i, 1u), 1});
|
|
}
|
|
}
|
|
|
|
// Test that copying mips without 256-byte aligned sizes works
|
|
TEST_P(CopyTests_T2B, TextureMipUnaligned) {
|
|
constexpr uint32_t kWidth = 259;
|
|
constexpr uint32_t kHeight = 127;
|
|
|
|
TextureSpec defaultTextureSpec;
|
|
defaultTextureSpec.textureSize = {kWidth, kHeight, 1};
|
|
|
|
for (unsigned int i = 1; i < 4; ++i) {
|
|
TextureSpec textureSpec = defaultTextureSpec;
|
|
textureSpec.copyLevel = i;
|
|
textureSpec.levelCount = i + 1;
|
|
DoTest(textureSpec, MinimumBufferSpec(kWidth >> i, kHeight >> i),
|
|
{kWidth >> i, kHeight >> i, 1});
|
|
}
|
|
}
|
|
|
|
// Test that copying with a 512-byte aligned buffer offset works
|
|
TEST_P(CopyTests_T2B, OffsetBufferAligned) {
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {kWidth, kHeight, 1};
|
|
|
|
for (unsigned int i = 0; i < 3; ++i) {
|
|
BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kHeight);
|
|
uint64_t offset = 512 * i;
|
|
bufferSpec.size += offset;
|
|
bufferSpec.offset += offset;
|
|
DoTest(textureSpec, bufferSpec, {kWidth, kHeight, 1});
|
|
}
|
|
}
|
|
|
|
// Test that copying without a 512-byte aligned buffer offset works
|
|
TEST_P(CopyTests_T2B, OffsetBufferUnaligned) {
|
|
constexpr uint32_t kWidth = 128;
|
|
constexpr uint32_t kHeight = 128;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {kWidth, kHeight, 1};
|
|
|
|
const uint32_t bytesPerTexel = utils::GetTexelBlockSizeInBytes(textureSpec.format);
|
|
for (uint32_t i = bytesPerTexel; i < 512; i += bytesPerTexel * 9) {
|
|
BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kHeight);
|
|
bufferSpec.size += i;
|
|
bufferSpec.offset += i;
|
|
DoTest(textureSpec, bufferSpec, {kWidth, kHeight, 1});
|
|
}
|
|
}
|
|
|
|
// Test that copying without a 512-byte aligned buffer offset that is greater than the bytes per row
|
|
// works
|
|
TEST_P(CopyTests_T2B, OffsetBufferUnalignedSmallBytesPerRow) {
|
|
constexpr uint32_t kWidth = 32;
|
|
constexpr uint32_t kHeight = 128;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {kWidth, kHeight, 1};
|
|
|
|
const uint32_t bytesPerTexel = utils::GetTexelBlockSizeInBytes(textureSpec.format);
|
|
for (uint32_t i = 256 + bytesPerTexel; i < 512; i += bytesPerTexel * 9) {
|
|
BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kHeight);
|
|
bufferSpec.size += i;
|
|
bufferSpec.offset += i;
|
|
DoTest(textureSpec, bufferSpec, {kWidth, kHeight, 1});
|
|
}
|
|
}
|
|
|
|
// Test that copying with a greater bytes per row than needed on a 256-byte aligned texture works
|
|
TEST_P(CopyTests_T2B, BytesPerRowAligned) {
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {kWidth, kHeight, 1};
|
|
|
|
BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kHeight);
|
|
for (unsigned int i = 1; i < 4; ++i) {
|
|
bufferSpec.bytesPerRow += 256;
|
|
bufferSpec.size += 256 * kHeight;
|
|
DoTest(textureSpec, bufferSpec, {kWidth, kHeight, 1});
|
|
}
|
|
}
|
|
|
|
// Test that copying with a greater bytes per row than needed on a texture that is not 256-byte
|
|
// aligned works
|
|
TEST_P(CopyTests_T2B, BytesPerRowUnaligned) {
|
|
constexpr uint32_t kWidth = 259;
|
|
constexpr uint32_t kHeight = 127;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {kWidth, kHeight, 1};
|
|
|
|
BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kHeight);
|
|
for (unsigned int i = 1; i < 4; ++i) {
|
|
bufferSpec.bytesPerRow += 256;
|
|
bufferSpec.size += 256 * kHeight;
|
|
DoTest(textureSpec, bufferSpec, {kWidth, kHeight, 1});
|
|
}
|
|
}
|
|
|
|
// Test that copying with bytesPerRow = 0 and bytesPerRow < bytesInACompleteRow works
|
|
// when we're copying one row only
|
|
TEST_P(CopyTests_T2B, BytesPerRowWithOneRowCopy) {
|
|
constexpr uint32_t kWidth = 259;
|
|
constexpr uint32_t kHeight = 127;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {kWidth, kHeight, 1};
|
|
|
|
{
|
|
BufferSpec bufferSpec = MinimumBufferSpec(5, 1);
|
|
|
|
// bytesPerRow undefined
|
|
bufferSpec.bytesPerRow = wgpu::kCopyStrideUndefined;
|
|
DoTest(textureSpec, bufferSpec, {5, 1, 1});
|
|
}
|
|
}
|
|
|
|
TEST_P(CopyTests_T2B, StrideSpecialCases) {
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {4, 4, 4};
|
|
|
|
// bytesPerRow 0
|
|
for (const wgpu::Extent3D copyExtent :
|
|
{wgpu::Extent3D{0, 2, 2}, {0, 0, 2}, {0, 2, 0}, {0, 0, 0}}) {
|
|
DoTest(textureSpec, MinimumBufferSpec(copyExtent, 0, 2), copyExtent);
|
|
}
|
|
|
|
// bytesPerRow undefined
|
|
for (const wgpu::Extent3D copyExtent :
|
|
{wgpu::Extent3D{2, 1, 1}, {2, 0, 1}, {2, 1, 0}, {2, 0, 0}}) {
|
|
DoTest(textureSpec, MinimumBufferSpec(copyExtent, wgpu::kCopyStrideUndefined, 2),
|
|
copyExtent);
|
|
}
|
|
|
|
// rowsPerImage 0
|
|
for (const wgpu::Extent3D copyExtent :
|
|
{wgpu::Extent3D{2, 0, 2}, {2, 0, 0}, {0, 0, 2}, {0, 0, 0}}) {
|
|
DoTest(textureSpec, MinimumBufferSpec(copyExtent, 256, 0), copyExtent);
|
|
}
|
|
|
|
// rowsPerImage undefined
|
|
for (const wgpu::Extent3D copyExtent : {wgpu::Extent3D{2, 2, 1}, {2, 2, 0}}) {
|
|
DoTest(textureSpec, MinimumBufferSpec(copyExtent, 256, wgpu::kCopyStrideUndefined),
|
|
copyExtent);
|
|
}
|
|
}
|
|
|
|
// Test copying a single slice with rowsPerImage larger than copy height and rowsPerImage will not
|
|
// take effect. If rowsPerImage takes effect, it looks like the copy may go past the end of the
|
|
// buffer.
|
|
TEST_P(CopyTests_T2B, RowsPerImageShouldNotCauseBufferOOBIfDepthOrArrayLayersIsOne) {
|
|
// Check various offsets to cover each code path in the 2D split code in TextureCopySplitter.
|
|
for (uint32_t offset : {0, 4, 64}) {
|
|
constexpr uint32_t kWidth = 250;
|
|
constexpr uint32_t kHeight = 3;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {kWidth, kHeight, 1};
|
|
|
|
BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kHeight);
|
|
bufferSpec.rowsPerImage = 2 * kHeight;
|
|
bufferSpec.offset = offset;
|
|
bufferSpec.size += offset;
|
|
DoTest(textureSpec, bufferSpec, {kWidth, kHeight, 1});
|
|
DoTest(textureSpec, bufferSpec, {kWidth, kHeight, 1}, wgpu::TextureDimension::e3D);
|
|
}
|
|
}
|
|
|
|
// Test copying a single row with bytesPerRow larger than copy width and bytesPerRow will not
|
|
// take effect. If bytesPerRow takes effect, it looks like the copy may go past the end of the
|
|
// buffer.
|
|
TEST_P(CopyTests_T2B, BytesPerRowShouldNotCauseBufferOOBIfCopyHeightIsOne) {
|
|
// Check various offsets to cover each code path in the 2D split code in TextureCopySplitter.
|
|
for (uint32_t offset : {0, 4, 100}) {
|
|
constexpr uint32_t kWidth = 250;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {kWidth, 1, 1};
|
|
|
|
BufferSpec bufferSpec = MinimumBufferSpec(kWidth, 1);
|
|
bufferSpec.bytesPerRow = 1280; // the default bytesPerRow is 1024.
|
|
bufferSpec.offset = offset;
|
|
bufferSpec.size += offset;
|
|
DoTest(textureSpec, bufferSpec, {kWidth, 1, 1});
|
|
DoTest(textureSpec, bufferSpec, {kWidth, 1, 1}, wgpu::TextureDimension::e3D);
|
|
}
|
|
}
|
|
|
|
// A regression test for a bug on D3D12 backend that causes crash when doing texture-to-texture
|
|
// copy one row with the texture format Depth32Float.
|
|
TEST_P(CopyTests_T2B, CopyOneRowWithDepth32Float) {
|
|
// TODO(crbug.com/dawn/667): Work around the fact that some platforms do not support reading
|
|
// depth.
|
|
DAWN_TEST_UNSUPPORTED_IF(HasToggleEnabled("disable_depth_read"));
|
|
|
|
constexpr wgpu::TextureFormat kFormat = wgpu::TextureFormat::Depth32Float;
|
|
constexpr uint32_t kPixelsPerRow = 4u;
|
|
|
|
wgpu::TextureDescriptor textureDescriptor;
|
|
textureDescriptor.format = kFormat;
|
|
textureDescriptor.size = {kPixelsPerRow, 1, 1};
|
|
textureDescriptor.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::RenderAttachment;
|
|
wgpu::Texture texture = device.CreateTexture(&textureDescriptor);
|
|
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
|
|
// Initialize the depth texture with 0.5f.
|
|
constexpr float kClearDepthValue = 0.5f;
|
|
utils::ComboRenderPassDescriptor renderPass({}, texture.CreateView());
|
|
renderPass.UnsetDepthStencilLoadStoreOpsForFormat(kFormat);
|
|
renderPass.cDepthStencilAttachmentInfo.depthClearValue = kClearDepthValue;
|
|
renderPass.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Clear;
|
|
renderPass.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Store;
|
|
wgpu::RenderPassEncoder renderPassEncoder = encoder.BeginRenderPass(&renderPass);
|
|
renderPassEncoder.End();
|
|
|
|
constexpr uint32_t kBufferCopyOffset = kTextureBytesPerRowAlignment;
|
|
const uint32_t kBufferSize =
|
|
kBufferCopyOffset + utils::GetTexelBlockSizeInBytes(kFormat) * kPixelsPerRow;
|
|
wgpu::BufferDescriptor bufferDescriptor;
|
|
bufferDescriptor.size = kBufferSize;
|
|
bufferDescriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst;
|
|
wgpu::Buffer buffer = device.CreateBuffer(&bufferDescriptor);
|
|
|
|
wgpu::ImageCopyBuffer imageCopyBuffer =
|
|
utils::CreateImageCopyBuffer(buffer, kBufferCopyOffset, kTextureBytesPerRowAlignment);
|
|
wgpu::ImageCopyTexture imageCopyTexture = utils::CreateImageCopyTexture(texture, 0, {0, 0, 0});
|
|
|
|
wgpu::Extent3D copySize = textureDescriptor.size;
|
|
encoder.CopyTextureToBuffer(&imageCopyTexture, &imageCopyBuffer, ©Size);
|
|
wgpu::CommandBuffer commandBuffer = encoder.Finish();
|
|
queue.Submit(1, &commandBuffer);
|
|
|
|
std::array<float, kPixelsPerRow> expectedValues;
|
|
std::fill(expectedValues.begin(), expectedValues.end(), kClearDepthValue);
|
|
EXPECT_BUFFER_FLOAT_RANGE_EQ(expectedValues.data(), buffer, kBufferCopyOffset, kPixelsPerRow);
|
|
}
|
|
|
|
// Test that copying whole texture 2D array layers in one texture-to-buffer-copy works.
|
|
TEST_P(CopyTests_T2B, Texture2DArrayFull) {
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
constexpr uint32_t kLayers = 6u;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {kWidth, kHeight, kLayers};
|
|
|
|
DoTest(textureSpec, MinimumBufferSpec(kWidth, kHeight, kLayers), {kWidth, kHeight, kLayers});
|
|
}
|
|
|
|
// Test that copying a range of texture 2D array layers in one texture-to-buffer-copy works.
|
|
TEST_P(CopyTests_T2B, Texture2DArraySubRegion) {
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
constexpr uint32_t kLayers = 6u;
|
|
constexpr uint32_t kBaseLayer = 2u;
|
|
constexpr uint32_t kCopyLayers = 3u;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.copyOrigin = {0, 0, kBaseLayer};
|
|
textureSpec.textureSize = {kWidth, kHeight, kLayers};
|
|
|
|
DoTest(textureSpec, MinimumBufferSpec(kWidth, kHeight, kCopyLayers),
|
|
{kWidth, kHeight, kCopyLayers});
|
|
}
|
|
|
|
// Test that copying texture 2D array mips with 256-byte aligned sizes works
|
|
TEST_P(CopyTests_T2B, Texture2DArrayMip) {
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
constexpr uint32_t kLayers = 6u;
|
|
|
|
TextureSpec defaultTextureSpec;
|
|
defaultTextureSpec.textureSize = {kWidth, kHeight, kLayers};
|
|
|
|
for (unsigned int i = 1; i < 4; ++i) {
|
|
TextureSpec textureSpec = defaultTextureSpec;
|
|
textureSpec.copyLevel = i;
|
|
textureSpec.levelCount = i + 1;
|
|
|
|
DoTest(textureSpec, MinimumBufferSpec(kWidth >> i, kHeight >> i, kLayers),
|
|
{kWidth >> i, kHeight >> i, kLayers});
|
|
}
|
|
}
|
|
|
|
// Test that copying from a range of texture 2D array layers in one texture-to-buffer-copy when
|
|
// RowsPerImage is not equal to the height of the texture works.
|
|
TEST_P(CopyTests_T2B, Texture2DArrayRegionNonzeroRowsPerImage) {
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
constexpr uint32_t kLayers = 6u;
|
|
constexpr uint32_t kBaseLayer = 2u;
|
|
constexpr uint32_t kCopyLayers = 3u;
|
|
|
|
constexpr uint32_t kRowsPerImage = kHeight * 2;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.copyOrigin = {0, 0, kBaseLayer};
|
|
textureSpec.textureSize = {kWidth, kHeight, kLayers};
|
|
|
|
BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kRowsPerImage, kCopyLayers);
|
|
bufferSpec.rowsPerImage = kRowsPerImage;
|
|
DoTest(textureSpec, bufferSpec, {kWidth, kHeight, kCopyLayers});
|
|
}
|
|
|
|
// Test a special code path in the D3D12 backends when (BytesPerRow * RowsPerImage) is not a
|
|
// multiple of 512.
|
|
TEST_P(CopyTests_T2B, Texture2DArrayRegionWithOffsetOddRowsPerImage) {
|
|
constexpr uint32_t kWidth = 64;
|
|
constexpr uint32_t kHeight = 128;
|
|
constexpr uint32_t kLayers = 8u;
|
|
constexpr uint32_t kBaseLayer = 2u;
|
|
constexpr uint32_t kCopyLayers = 5u;
|
|
|
|
constexpr uint32_t kRowsPerImage = kHeight + 1;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.copyOrigin = {0, 0, kBaseLayer};
|
|
textureSpec.textureSize = {kWidth, kHeight, kLayers};
|
|
|
|
BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kRowsPerImage, kCopyLayers);
|
|
bufferSpec.offset += 128u;
|
|
bufferSpec.size += 128u;
|
|
bufferSpec.rowsPerImage = kRowsPerImage;
|
|
DoTest(textureSpec, bufferSpec, {kWidth, kHeight, kCopyLayers});
|
|
}
|
|
|
|
// Test a special code path in the D3D12 backends when (BytesPerRow * RowsPerImage) is a multiple
|
|
// of 512.
|
|
TEST_P(CopyTests_T2B, Texture2DArrayRegionWithOffsetEvenRowsPerImage) {
|
|
constexpr uint32_t kWidth = 64;
|
|
constexpr uint32_t kHeight = 128;
|
|
constexpr uint32_t kLayers = 8u;
|
|
constexpr uint32_t kBaseLayer = 2u;
|
|
constexpr uint32_t kCopyLayers = 4u;
|
|
|
|
constexpr uint32_t kRowsPerImage = kHeight + 2;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.copyOrigin = {0, 0, kBaseLayer};
|
|
textureSpec.textureSize = {kWidth, kHeight, kLayers};
|
|
|
|
BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kRowsPerImage, kCopyLayers);
|
|
bufferSpec.offset += 128u;
|
|
bufferSpec.size += 128u;
|
|
bufferSpec.rowsPerImage = kRowsPerImage;
|
|
DoTest(textureSpec, bufferSpec, {kWidth, kHeight, kCopyLayers});
|
|
}
|
|
|
|
// Test that copying whole 3D texture in one texture-to-buffer-copy works.
|
|
TEST_P(CopyTests_T2B, Texture3DFull) {
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
constexpr uint32_t kDepth = 6;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {kWidth, kHeight, kDepth};
|
|
|
|
DoTest(textureSpec, MinimumBufferSpec(kWidth, kHeight, kDepth), {kWidth, kHeight, kDepth},
|
|
wgpu::TextureDimension::e3D);
|
|
}
|
|
|
|
// Test that copying a range of texture 3D depths in one texture-to-buffer-copy works.
|
|
TEST_P(CopyTests_T2B, Texture3DSubRegion) {
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
constexpr uint32_t kDepth = 6;
|
|
constexpr uint32_t kBaseDepth = 2u;
|
|
constexpr uint32_t kCopyDepth = 3u;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.copyOrigin = {0, 0, kBaseDepth};
|
|
textureSpec.textureSize = {kWidth, kHeight, kDepth};
|
|
|
|
DoTest(textureSpec, MinimumBufferSpec(kWidth, kHeight, kCopyDepth),
|
|
{kWidth / 2, kHeight / 2, kCopyDepth}, wgpu::TextureDimension::e3D);
|
|
}
|
|
|
|
TEST_P(CopyTests_T2B, Texture3DNoSplitRowDataWithEmptyFirstRow) {
|
|
constexpr uint32_t kWidth = 2;
|
|
constexpr uint32_t kHeight = 4;
|
|
constexpr uint32_t kDepth = 3;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {kWidth, kHeight, kDepth};
|
|
BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kHeight, kDepth);
|
|
|
|
// The tests below are designed to test TextureCopySplitter for 3D textures on D3D12.
|
|
// Base: no split for a row + no empty first row
|
|
bufferSpec.offset = 60;
|
|
bufferSpec.size += bufferSpec.offset;
|
|
DoTest(textureSpec, bufferSpec, {kWidth, kHeight, kDepth}, wgpu::TextureDimension::e3D);
|
|
|
|
// This test will cover: no split for a row + empty first row
|
|
bufferSpec.offset = 260;
|
|
bufferSpec.size += bufferSpec.offset;
|
|
DoTest(textureSpec, bufferSpec, {kWidth, kHeight, kDepth}, wgpu::TextureDimension::e3D);
|
|
}
|
|
|
|
TEST_P(CopyTests_T2B, Texture3DSplitRowDataWithoutEmptyFirstRow) {
|
|
constexpr uint32_t kWidth = 259;
|
|
constexpr uint32_t kHeight = 127;
|
|
constexpr uint32_t kDepth = 3;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {kWidth, kHeight, kDepth};
|
|
BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kHeight, kDepth);
|
|
|
|
// The test below is designed to test TextureCopySplitter for 3D textures on D3D12.
|
|
// This test will cover: split for a row + no empty first row for both split regions
|
|
bufferSpec.offset = 260;
|
|
bufferSpec.size += bufferSpec.offset;
|
|
DoTest(textureSpec, bufferSpec, {kWidth, kHeight, kDepth}, wgpu::TextureDimension::e3D);
|
|
}
|
|
|
|
TEST_P(CopyTests_T2B, Texture3DSplitRowDataWithEmptyFirstRow) {
|
|
constexpr uint32_t kWidth = 39;
|
|
constexpr uint32_t kHeight = 4;
|
|
constexpr uint32_t kDepth = 3;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {kWidth, kHeight, kDepth};
|
|
BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kHeight, kDepth);
|
|
|
|
// The tests below are designed to test TextureCopySplitter for 3D textures on D3D12.
|
|
// This test will cover: split for a row + empty first row for the head block
|
|
bufferSpec.offset = 400;
|
|
bufferSpec.size += bufferSpec.offset;
|
|
DoTest(textureSpec, bufferSpec, {kWidth, kHeight, kDepth}, wgpu::TextureDimension::e3D);
|
|
|
|
// This test will cover: split for a row + empty first row for the tail block
|
|
bufferSpec.offset = 160;
|
|
bufferSpec.size += bufferSpec.offset;
|
|
DoTest(textureSpec, bufferSpec, {kWidth, kHeight, kDepth}, wgpu::TextureDimension::e3D);
|
|
}
|
|
|
|
TEST_P(CopyTests_T2B, Texture3DCopyHeightIsOneCopyWidthIsTiny) {
|
|
constexpr uint32_t kWidth = 2;
|
|
constexpr uint32_t kHeight = 1;
|
|
constexpr uint32_t kDepth = 3;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {kWidth, kHeight, kDepth};
|
|
BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kHeight, kDepth);
|
|
|
|
// The tests below are designed to test TextureCopySplitter for 3D textures on D3D12.
|
|
// Base: no split for a row, no empty row, and copy height is 1
|
|
bufferSpec.offset = 60;
|
|
bufferSpec.size += bufferSpec.offset;
|
|
DoTest(textureSpec, bufferSpec, {kWidth, kHeight, kDepth}, wgpu::TextureDimension::e3D);
|
|
|
|
// This test will cover: no split for a row + empty first row, and copy height is 1
|
|
bufferSpec.offset = 260;
|
|
bufferSpec.size += bufferSpec.offset;
|
|
DoTest(textureSpec, bufferSpec, {kWidth, kHeight, kDepth}, wgpu::TextureDimension::e3D);
|
|
}
|
|
|
|
TEST_P(CopyTests_T2B, Texture3DCopyHeightIsOneCopyWidthIsSmall) {
|
|
constexpr uint32_t kWidth = 39;
|
|
constexpr uint32_t kHeight = 1;
|
|
constexpr uint32_t kDepth = 3;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {kWidth, kHeight, kDepth};
|
|
BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kHeight, kDepth);
|
|
|
|
// The tests below are designed to test TextureCopySplitter for 3D textures on D3D12.
|
|
// This test will cover: split for a row + empty first row for the head block, and copy height
|
|
// is 1
|
|
bufferSpec.offset = 400;
|
|
bufferSpec.size += bufferSpec.offset;
|
|
DoTest(textureSpec, bufferSpec, {kWidth, kHeight, kDepth}, wgpu::TextureDimension::e3D);
|
|
|
|
// This test will cover: split for a row + empty first row for the tail block, and copy height
|
|
// is 1
|
|
bufferSpec.offset = 160;
|
|
bufferSpec.size += bufferSpec.offset;
|
|
DoTest(textureSpec, bufferSpec, {kWidth, kHeight, kDepth}, wgpu::TextureDimension::e3D);
|
|
}
|
|
|
|
// Test that copying texture 3D array mips with 256-byte aligned sizes works
|
|
TEST_P(CopyTests_T2B, Texture3DMipAligned) {
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
constexpr uint32_t kDepth = 64u;
|
|
|
|
TextureSpec defaultTextureSpec;
|
|
defaultTextureSpec.textureSize = {kWidth, kHeight, kDepth};
|
|
|
|
for (unsigned int i = 1; i < 6; ++i) {
|
|
TextureSpec textureSpec = defaultTextureSpec;
|
|
textureSpec.copyLevel = i;
|
|
textureSpec.levelCount = i + 1;
|
|
|
|
DoTest(textureSpec, MinimumBufferSpec(kWidth >> i, kHeight >> i, kDepth >> i),
|
|
{kWidth >> i, kHeight >> i, kDepth >> i}, wgpu::TextureDimension::e3D);
|
|
}
|
|
}
|
|
|
|
// Test that copying texture 3D array mips with 256-byte unaligned sizes works
|
|
TEST_P(CopyTests_T2B, Texture3DMipUnaligned) {
|
|
constexpr uint32_t kWidth = 261;
|
|
constexpr uint32_t kHeight = 123;
|
|
constexpr uint32_t kDepth = 69u;
|
|
|
|
TextureSpec defaultTextureSpec;
|
|
defaultTextureSpec.textureSize = {kWidth, kHeight, kDepth};
|
|
|
|
for (unsigned int i = 1; i < 6; ++i) {
|
|
TextureSpec textureSpec = defaultTextureSpec;
|
|
textureSpec.copyLevel = i;
|
|
textureSpec.levelCount = i + 1;
|
|
|
|
DoTest(textureSpec, MinimumBufferSpec(kWidth >> i, kHeight >> i, kDepth >> i),
|
|
{kWidth >> i, kHeight >> i, kDepth >> i}, wgpu::TextureDimension::e3D);
|
|
}
|
|
}
|
|
|
|
DAWN_INSTANTIATE_TEST(CopyTests_T2B,
|
|
D3D12Backend(),
|
|
MetalBackend(),
|
|
OpenGLBackend(),
|
|
OpenGLESBackend(),
|
|
VulkanBackend());
|
|
|
|
// Test that copying an entire texture with 256-byte aligned dimensions works
|
|
TEST_P(CopyTests_B2T, FullTextureAligned) {
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {kWidth, kHeight, 1};
|
|
|
|
DoTest(textureSpec, MinimumBufferSpec(kWidth, kHeight), {kWidth, kHeight, 1});
|
|
}
|
|
|
|
// Test noop copies.
|
|
TEST_P(CopyTests_B2T, ZeroSizedCopy) {
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {kWidth, kHeight, 1};
|
|
|
|
DoTest(textureSpec, MinimumBufferSpec(kWidth, kHeight), {0, kHeight, 1});
|
|
DoTest(textureSpec, MinimumBufferSpec(kWidth, kHeight), {kWidth, 0, 1});
|
|
DoTest(textureSpec, MinimumBufferSpec(kWidth, kHeight), {kWidth, kHeight, 0});
|
|
}
|
|
|
|
// Test that copying an entire texture without 256-byte aligned dimensions works
|
|
TEST_P(CopyTests_B2T, FullTextureUnaligned) {
|
|
constexpr uint32_t kWidth = 259;
|
|
constexpr uint32_t kHeight = 127;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {kWidth, kHeight, 1};
|
|
|
|
DoTest(textureSpec, MinimumBufferSpec(kWidth, kHeight), {kWidth, kHeight, 1});
|
|
}
|
|
|
|
// Test that reading pixels from a 256-byte aligned texture works
|
|
TEST_P(CopyTests_B2T, PixelReadAligned) {
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
BufferSpec pixelBuffer = MinimumBufferSpec(1, 1);
|
|
|
|
constexpr wgpu::Extent3D kCopySize = {1, 1, 1};
|
|
constexpr wgpu::Extent3D kTextureSize = {kWidth, kHeight, 1};
|
|
TextureSpec defaultTextureSpec;
|
|
defaultTextureSpec.textureSize = kTextureSize;
|
|
|
|
{
|
|
TextureSpec textureSpec = defaultTextureSpec;
|
|
DoTest(textureSpec, pixelBuffer, kCopySize);
|
|
}
|
|
|
|
{
|
|
TextureSpec textureSpec = defaultTextureSpec;
|
|
textureSpec.copyOrigin = {kWidth - 1, 0, 0};
|
|
DoTest(textureSpec, pixelBuffer, kCopySize);
|
|
}
|
|
|
|
{
|
|
TextureSpec textureSpec = defaultTextureSpec;
|
|
textureSpec.copyOrigin = {0, kHeight - 1, 0};
|
|
DoTest(textureSpec, pixelBuffer, kCopySize);
|
|
}
|
|
|
|
{
|
|
TextureSpec textureSpec = defaultTextureSpec;
|
|
textureSpec.copyOrigin = {kWidth - 1, kHeight - 1, 0};
|
|
DoTest(textureSpec, pixelBuffer, kCopySize);
|
|
}
|
|
|
|
{
|
|
TextureSpec textureSpec = defaultTextureSpec;
|
|
textureSpec.copyOrigin = {kWidth / 3, kHeight / 7, 0};
|
|
DoTest(textureSpec, pixelBuffer, kCopySize);
|
|
}
|
|
|
|
{
|
|
TextureSpec textureSpec = defaultTextureSpec;
|
|
textureSpec.copyOrigin = {kWidth / 7, kHeight / 3, 0};
|
|
DoTest(textureSpec, pixelBuffer, kCopySize);
|
|
}
|
|
}
|
|
|
|
// Test that copying pixels from a texture that is not 256-byte aligned works
|
|
TEST_P(CopyTests_B2T, PixelReadUnaligned) {
|
|
constexpr uint32_t kWidth = 259;
|
|
constexpr uint32_t kHeight = 127;
|
|
BufferSpec pixelBuffer = MinimumBufferSpec(1, 1);
|
|
|
|
constexpr wgpu::Extent3D kCopySize = {1, 1, 1};
|
|
constexpr wgpu::Extent3D kTextureSize = {kWidth, kHeight, 1};
|
|
TextureSpec defaultTextureSpec;
|
|
defaultTextureSpec.textureSize = kTextureSize;
|
|
|
|
{
|
|
TextureSpec textureSpec = defaultTextureSpec;
|
|
DoTest(textureSpec, pixelBuffer, kCopySize);
|
|
}
|
|
|
|
{
|
|
TextureSpec textureSpec = defaultTextureSpec;
|
|
textureSpec.copyOrigin = {kWidth - 1, 0, 0};
|
|
DoTest(textureSpec, pixelBuffer, kCopySize);
|
|
}
|
|
|
|
{
|
|
TextureSpec textureSpec = defaultTextureSpec;
|
|
textureSpec.copyOrigin = {0, kHeight - 1, 0};
|
|
DoTest(textureSpec, pixelBuffer, kCopySize);
|
|
}
|
|
|
|
{
|
|
TextureSpec textureSpec = defaultTextureSpec;
|
|
textureSpec.copyOrigin = {kWidth - 1, kHeight - 1, 0};
|
|
DoTest(textureSpec, pixelBuffer, kCopySize);
|
|
}
|
|
|
|
{
|
|
TextureSpec textureSpec = defaultTextureSpec;
|
|
textureSpec.copyOrigin = {kWidth / 3, kHeight / 7, 0};
|
|
DoTest(textureSpec, pixelBuffer, kCopySize);
|
|
}
|
|
|
|
{
|
|
TextureSpec textureSpec = defaultTextureSpec;
|
|
textureSpec.copyOrigin = {kWidth / 7, kHeight / 3, 0};
|
|
DoTest(textureSpec, pixelBuffer, kCopySize);
|
|
}
|
|
}
|
|
|
|
// Test that copying regions with 256-byte aligned sizes works
|
|
TEST_P(CopyTests_B2T, TextureRegionAligned) {
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
for (unsigned int w : {64, 128, 256}) {
|
|
for (unsigned int h : {16, 32, 48}) {
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {kWidth, kHeight, 1};
|
|
DoTest(textureSpec, MinimumBufferSpec(w, h), {w, h, 1});
|
|
}
|
|
}
|
|
}
|
|
|
|
// Test that copying regions without 256-byte aligned sizes works
|
|
TEST_P(CopyTests_B2T, TextureRegionUnaligned) {
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
|
|
TextureSpec defaultTextureSpec;
|
|
defaultTextureSpec.textureSize = {kWidth, kHeight, 1};
|
|
|
|
for (unsigned int w : {13, 63, 65}) {
|
|
for (unsigned int h : {17, 19, 63}) {
|
|
TextureSpec textureSpec = defaultTextureSpec;
|
|
DoTest(textureSpec, MinimumBufferSpec(w, h), {w, h, 1});
|
|
}
|
|
}
|
|
}
|
|
|
|
// Test that copying mips with 256-byte aligned sizes works
|
|
TEST_P(CopyTests_B2T, TextureMipAligned) {
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
|
|
TextureSpec defaultTextureSpec;
|
|
defaultTextureSpec.textureSize = {kWidth, kHeight, 1};
|
|
|
|
for (unsigned int i = 1; i < 4; ++i) {
|
|
TextureSpec textureSpec = defaultTextureSpec;
|
|
textureSpec.copyLevel = i;
|
|
textureSpec.levelCount = i + 1;
|
|
DoTest(textureSpec, MinimumBufferSpec(kWidth >> i, kHeight >> i),
|
|
{kWidth >> i, kHeight >> i, 1});
|
|
}
|
|
}
|
|
|
|
// Test that copying mips without 256-byte aligned sizes works
|
|
TEST_P(CopyTests_B2T, TextureMipUnaligned) {
|
|
constexpr uint32_t kWidth = 259;
|
|
constexpr uint32_t kHeight = 127;
|
|
|
|
TextureSpec defaultTextureSpec;
|
|
defaultTextureSpec.textureSize = {kWidth, kHeight, 1};
|
|
|
|
for (unsigned int i = 1; i < 4; ++i) {
|
|
TextureSpec textureSpec = defaultTextureSpec;
|
|
textureSpec.copyLevel = i;
|
|
textureSpec.levelCount = i + 1;
|
|
DoTest(textureSpec, MinimumBufferSpec(kWidth >> i, kHeight >> i),
|
|
{kWidth >> i, kHeight >> i, 1});
|
|
}
|
|
}
|
|
|
|
// Test that copying with a 512-byte aligned buffer offset works
|
|
TEST_P(CopyTests_B2T, OffsetBufferAligned) {
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {kWidth, kHeight, 1};
|
|
|
|
for (unsigned int i = 0; i < 3; ++i) {
|
|
BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kHeight);
|
|
uint64_t offset = 512 * i;
|
|
bufferSpec.size += offset;
|
|
bufferSpec.offset += offset;
|
|
DoTest(textureSpec, bufferSpec, {kWidth, kHeight, 1});
|
|
}
|
|
}
|
|
|
|
// Test that copying without a 512-byte aligned buffer offset works
|
|
TEST_P(CopyTests_B2T, OffsetBufferUnaligned) {
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {kWidth, kHeight, 1};
|
|
|
|
const uint32_t bytesPerTexel = utils::GetTexelBlockSizeInBytes(textureSpec.format);
|
|
for (uint32_t i = bytesPerTexel; i < 512; i += bytesPerTexel * 9) {
|
|
BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kHeight);
|
|
bufferSpec.size += i;
|
|
bufferSpec.offset += i;
|
|
DoTest(textureSpec, bufferSpec, {kWidth, kHeight, 1});
|
|
}
|
|
}
|
|
|
|
// Test that copying without a 512-byte aligned buffer offset that is greater than the bytes per row
|
|
// works
|
|
TEST_P(CopyTests_B2T, OffsetBufferUnalignedSmallBytesPerRow) {
|
|
constexpr uint32_t kWidth = 32;
|
|
constexpr uint32_t kHeight = 128;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {kWidth, kHeight, 1};
|
|
|
|
const uint32_t bytesPerTexel = utils::GetTexelBlockSizeInBytes(textureSpec.format);
|
|
for (uint32_t i = 256 + bytesPerTexel; i < 512; i += bytesPerTexel * 9) {
|
|
BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kHeight);
|
|
bufferSpec.size += i;
|
|
bufferSpec.offset += i;
|
|
DoTest(textureSpec, bufferSpec, {kWidth, kHeight, 1});
|
|
}
|
|
}
|
|
|
|
// Test that copying with a greater bytes per row than needed on a 256-byte aligned texture works
|
|
TEST_P(CopyTests_B2T, BytesPerRowAligned) {
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {kWidth, kHeight, 1};
|
|
|
|
BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kHeight);
|
|
for (unsigned int i = 1; i < 4; ++i) {
|
|
bufferSpec.bytesPerRow += 256;
|
|
bufferSpec.size += 256 * kHeight;
|
|
DoTest(textureSpec, bufferSpec, {kWidth, kHeight, 1});
|
|
}
|
|
}
|
|
|
|
// Test that copying with a greater bytes per row than needed on a texture that is not 256-byte
|
|
// aligned works
|
|
TEST_P(CopyTests_B2T, BytesPerRowUnaligned) {
|
|
constexpr uint32_t kWidth = 259;
|
|
constexpr uint32_t kHeight = 127;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {kWidth, kHeight, 1};
|
|
|
|
BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kHeight);
|
|
for (unsigned int i = 1; i < 4; ++i) {
|
|
bufferSpec.bytesPerRow += 256;
|
|
bufferSpec.size += 256 * kHeight;
|
|
DoTest(textureSpec, bufferSpec, {kWidth, kHeight, 1});
|
|
}
|
|
}
|
|
|
|
// Test that copying with bytesPerRow = 0 and bytesPerRow < bytesInACompleteRow works
|
|
// when we're copying one row only
|
|
TEST_P(CopyTests_B2T, BytesPerRowWithOneRowCopy) {
|
|
constexpr uint32_t kWidth = 259;
|
|
constexpr uint32_t kHeight = 127;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {kWidth, kHeight, 1};
|
|
|
|
{
|
|
BufferSpec bufferSpec = MinimumBufferSpec(5, 1);
|
|
|
|
// bytesPerRow undefined
|
|
bufferSpec.bytesPerRow = wgpu::kCopyStrideUndefined;
|
|
DoTest(textureSpec, bufferSpec, {5, 1, 1});
|
|
}
|
|
}
|
|
|
|
TEST_P(CopyTests_B2T, StrideSpecialCases) {
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {4, 4, 4};
|
|
|
|
// bytesPerRow 0
|
|
for (const wgpu::Extent3D copyExtent :
|
|
{wgpu::Extent3D{0, 2, 2}, {0, 0, 2}, {0, 2, 0}, {0, 0, 0}}) {
|
|
DoTest(textureSpec, MinimumBufferSpec(copyExtent, 0, 2), copyExtent);
|
|
}
|
|
|
|
// bytesPerRow undefined
|
|
for (const wgpu::Extent3D copyExtent :
|
|
{wgpu::Extent3D{2, 1, 1}, {2, 0, 1}, {2, 1, 0}, {2, 0, 0}}) {
|
|
DoTest(textureSpec, MinimumBufferSpec(copyExtent, wgpu::kCopyStrideUndefined, 2),
|
|
copyExtent);
|
|
}
|
|
|
|
// rowsPerImage 0
|
|
for (const wgpu::Extent3D copyExtent :
|
|
{wgpu::Extent3D{2, 0, 2}, {2, 0, 0}, {0, 0, 2}, {0, 0, 0}}) {
|
|
DoTest(textureSpec, MinimumBufferSpec(copyExtent, 256, 0), copyExtent);
|
|
}
|
|
|
|
// rowsPerImage undefined
|
|
for (const wgpu::Extent3D copyExtent : {wgpu::Extent3D{2, 2, 1}, {2, 2, 0}}) {
|
|
DoTest(textureSpec, MinimumBufferSpec(copyExtent, 256, wgpu::kCopyStrideUndefined),
|
|
copyExtent);
|
|
}
|
|
}
|
|
|
|
// Test that copying whole texture 2D array layers in one texture-to-buffer-copy works.
|
|
TEST_P(CopyTests_B2T, Texture2DArrayFull) {
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
constexpr uint32_t kLayers = 6u;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {kWidth, kHeight, kLayers};
|
|
|
|
DoTest(textureSpec, MinimumBufferSpec(kWidth, kHeight, kLayers), {kWidth, kHeight, kLayers});
|
|
}
|
|
|
|
// Test that copying a range of texture 2D array layers in one texture-to-buffer-copy works.
|
|
TEST_P(CopyTests_B2T, Texture2DArraySubRegion) {
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
constexpr uint32_t kLayers = 6u;
|
|
constexpr uint32_t kBaseLayer = 2u;
|
|
constexpr uint32_t kCopyLayers = 3u;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.copyOrigin = {0, 0, kBaseLayer};
|
|
textureSpec.textureSize = {kWidth, kHeight, kLayers};
|
|
|
|
DoTest(textureSpec, MinimumBufferSpec(kWidth, kHeight, kCopyLayers),
|
|
{kWidth, kHeight, kCopyLayers});
|
|
}
|
|
|
|
// Test that copying into a range of texture 2D array layers in one texture-to-buffer-copy when
|
|
// RowsPerImage is not equal to the height of the texture works.
|
|
TEST_P(CopyTests_B2T, Texture2DArrayRegionNonzeroRowsPerImage) {
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
constexpr uint32_t kLayers = 6u;
|
|
constexpr uint32_t kBaseLayer = 2u;
|
|
constexpr uint32_t kCopyLayers = 3u;
|
|
|
|
constexpr uint32_t kRowsPerImage = kHeight * 2;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.copyOrigin = {0, 0, kBaseLayer};
|
|
textureSpec.textureSize = {kWidth, kHeight, kLayers};
|
|
|
|
BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kRowsPerImage, kCopyLayers);
|
|
bufferSpec.rowsPerImage = kRowsPerImage;
|
|
DoTest(textureSpec, bufferSpec, {kWidth, kHeight, kCopyLayers});
|
|
}
|
|
|
|
// Test a special code path in the D3D12 backends when (BytesPerRow * RowsPerImage) is not a
|
|
// multiple of 512.
|
|
TEST_P(CopyTests_B2T, Texture2DArrayRegionWithOffsetOddRowsPerImage) {
|
|
constexpr uint32_t kWidth = 64;
|
|
constexpr uint32_t kHeight = 128;
|
|
constexpr uint32_t kLayers = 8u;
|
|
constexpr uint32_t kBaseLayer = 2u;
|
|
constexpr uint32_t kCopyLayers = 5u;
|
|
|
|
constexpr uint32_t kRowsPerImage = kHeight + 1;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.copyOrigin = {0, 0, kBaseLayer};
|
|
textureSpec.textureSize = {kWidth, kHeight, kLayers};
|
|
|
|
BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kRowsPerImage, kCopyLayers);
|
|
bufferSpec.offset += 128u;
|
|
bufferSpec.size += 128u;
|
|
bufferSpec.rowsPerImage = kRowsPerImage;
|
|
DoTest(textureSpec, bufferSpec, {kWidth, kHeight, kCopyLayers});
|
|
}
|
|
|
|
// Test a special code path in the D3D12 backends when (BytesPerRow * RowsPerImage) is a multiple
|
|
// of 512.
|
|
TEST_P(CopyTests_B2T, Texture2DArrayRegionWithOffsetEvenRowsPerImage) {
|
|
constexpr uint32_t kWidth = 64;
|
|
constexpr uint32_t kHeight = 128;
|
|
constexpr uint32_t kLayers = 8u;
|
|
constexpr uint32_t kBaseLayer = 2u;
|
|
constexpr uint32_t kCopyLayers = 5u;
|
|
|
|
constexpr uint32_t kRowsPerImage = kHeight + 2;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.copyOrigin = {0, 0, kBaseLayer};
|
|
textureSpec.textureSize = {kWidth, kHeight, kLayers};
|
|
|
|
BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kRowsPerImage, kCopyLayers);
|
|
bufferSpec.offset += 128u;
|
|
bufferSpec.size += 128u;
|
|
bufferSpec.rowsPerImage = kRowsPerImage;
|
|
DoTest(textureSpec, bufferSpec, {kWidth, kHeight, kCopyLayers});
|
|
}
|
|
|
|
// Test that copying whole texture 3D in one buffer-to-texture-copy works.
|
|
TEST_P(CopyTests_B2T, Texture3DFull) {
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
constexpr uint32_t kDepth = 6;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {kWidth, kHeight, kDepth};
|
|
|
|
DoTest(textureSpec, MinimumBufferSpec(kWidth, kHeight, kDepth), {kWidth, kHeight, kDepth},
|
|
wgpu::TextureDimension::e3D);
|
|
}
|
|
|
|
// Test that copying a range of texture 3D Depths in one texture-to-buffer-copy works.
|
|
TEST_P(CopyTests_B2T, Texture3DSubRegion) {
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
constexpr uint32_t kDepth = 6;
|
|
constexpr uint32_t kBaseDepth = 2u;
|
|
constexpr uint32_t kCopyDepth = 3u;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.copyOrigin = {0, 0, kBaseDepth};
|
|
textureSpec.textureSize = {kWidth, kHeight, kDepth};
|
|
|
|
DoTest(textureSpec, MinimumBufferSpec(kWidth, kHeight, kCopyDepth),
|
|
{kWidth / 2, kHeight / 2, kCopyDepth}, wgpu::TextureDimension::e3D);
|
|
}
|
|
|
|
TEST_P(CopyTests_B2T, Texture3DNoSplitRowDataWithEmptyFirstRow) {
|
|
constexpr uint32_t kWidth = 2;
|
|
constexpr uint32_t kHeight = 4;
|
|
constexpr uint32_t kDepth = 3;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {kWidth, kHeight, kDepth};
|
|
BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kHeight, kDepth);
|
|
|
|
// The tests below are designed to test TextureCopySplitter for 3D textures on D3D12.
|
|
// Base: no split for a row + no empty first row
|
|
bufferSpec.offset = 60;
|
|
bufferSpec.size += bufferSpec.offset;
|
|
DoTest(textureSpec, bufferSpec, {kWidth, kHeight, kDepth}, wgpu::TextureDimension::e3D);
|
|
|
|
// This test will cover: no split for a row + empty first row
|
|
bufferSpec.offset = 260;
|
|
bufferSpec.size += bufferSpec.offset;
|
|
DoTest(textureSpec, bufferSpec, {kWidth, kHeight, kDepth}, wgpu::TextureDimension::e3D);
|
|
}
|
|
|
|
TEST_P(CopyTests_B2T, Texture3DSplitRowDataWithoutEmptyFirstRow) {
|
|
constexpr uint32_t kWidth = 259;
|
|
constexpr uint32_t kHeight = 127;
|
|
constexpr uint32_t kDepth = 3;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {kWidth, kHeight, kDepth};
|
|
BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kHeight, kDepth);
|
|
|
|
// The test below is designed to test TextureCopySplitter for 3D textures on D3D12.
|
|
// This test will cover: split for a row + no empty first row for both split regions
|
|
bufferSpec.offset = 260;
|
|
bufferSpec.size += bufferSpec.offset;
|
|
DoTest(textureSpec, bufferSpec, {kWidth, kHeight, kDepth}, wgpu::TextureDimension::e3D);
|
|
}
|
|
|
|
TEST_P(CopyTests_B2T, Texture3DSplitRowDataWithEmptyFirstRow) {
|
|
constexpr uint32_t kWidth = 39;
|
|
constexpr uint32_t kHeight = 4;
|
|
constexpr uint32_t kDepth = 3;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {kWidth, kHeight, kDepth};
|
|
BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kHeight, kDepth);
|
|
|
|
// The tests below are designed to test TextureCopySplitter for 3D textures on D3D12.
|
|
// This test will cover: split for a row + empty first row for the head block
|
|
bufferSpec.offset = 400;
|
|
bufferSpec.size += bufferSpec.offset;
|
|
DoTest(textureSpec, bufferSpec, {kWidth, kHeight, kDepth}, wgpu::TextureDimension::e3D);
|
|
|
|
// This test will cover: split for a row + empty first row for the tail block
|
|
bufferSpec.offset = 160;
|
|
bufferSpec.size += bufferSpec.offset;
|
|
DoTest(textureSpec, bufferSpec, {kWidth, kHeight, kDepth}, wgpu::TextureDimension::e3D);
|
|
}
|
|
|
|
TEST_P(CopyTests_B2T, Texture3DCopyHeightIsOneCopyWidthIsTiny) {
|
|
constexpr uint32_t kWidth = 2;
|
|
constexpr uint32_t kHeight = 1;
|
|
constexpr uint32_t kDepth = 3;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {kWidth, kHeight, kDepth};
|
|
BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kHeight, kDepth);
|
|
|
|
// The tests below are designed to test TextureCopySplitter for 3D textures on D3D12.
|
|
// Base: no split for a row, no empty row, and copy height is 1
|
|
bufferSpec.offset = 60;
|
|
bufferSpec.size += bufferSpec.offset;
|
|
DoTest(textureSpec, bufferSpec, {kWidth, kHeight, kDepth}, wgpu::TextureDimension::e3D);
|
|
|
|
// This test will cover: no split for a row + empty first row, and copy height is 1
|
|
bufferSpec.offset = 260;
|
|
bufferSpec.size += bufferSpec.offset;
|
|
DoTest(textureSpec, bufferSpec, {kWidth, kHeight, kDepth}, wgpu::TextureDimension::e3D);
|
|
}
|
|
|
|
TEST_P(CopyTests_B2T, Texture3DCopyHeightIsOneCopyWidthIsSmall) {
|
|
constexpr uint32_t kWidth = 39;
|
|
constexpr uint32_t kHeight = 1;
|
|
constexpr uint32_t kDepth = 3;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {kWidth, kHeight, kDepth};
|
|
BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kHeight, kDepth);
|
|
|
|
// The tests below are designed to test TextureCopySplitter for 3D textures on D3D12.
|
|
// This test will cover: split for a row + empty first row for the head block, and copy height
|
|
// is 1
|
|
bufferSpec.offset = 400;
|
|
bufferSpec.size += bufferSpec.offset;
|
|
DoTest(textureSpec, bufferSpec, {kWidth, kHeight, kDepth}, wgpu::TextureDimension::e3D);
|
|
|
|
// This test will cover: split for a row + empty first row for the tail block, and copy height
|
|
// is 1
|
|
bufferSpec.offset = 160;
|
|
bufferSpec.size += bufferSpec.offset;
|
|
DoTest(textureSpec, bufferSpec, {kWidth, kHeight, kDepth}, wgpu::TextureDimension::e3D);
|
|
}
|
|
|
|
// Test that copying texture 3D array mips with 256-byte aligned sizes works
|
|
TEST_P(CopyTests_B2T, Texture3DMipAligned) {
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
constexpr uint32_t kDepth = 64u;
|
|
|
|
TextureSpec defaultTextureSpec;
|
|
defaultTextureSpec.textureSize = {kWidth, kHeight, kDepth};
|
|
|
|
for (unsigned int i = 1; i < 6; ++i) {
|
|
TextureSpec textureSpec = defaultTextureSpec;
|
|
textureSpec.copyLevel = i;
|
|
textureSpec.levelCount = i + 1;
|
|
|
|
DoTest(textureSpec, MinimumBufferSpec(kWidth >> i, kHeight >> i, kDepth >> i),
|
|
{kWidth >> i, kHeight >> i, kDepth >> i}, wgpu::TextureDimension::e3D);
|
|
}
|
|
}
|
|
|
|
// Test that copying texture 3D array mips with 256-byte unaligned sizes works
|
|
TEST_P(CopyTests_B2T, Texture3DMipUnaligned) {
|
|
constexpr uint32_t kWidth = 261;
|
|
constexpr uint32_t kHeight = 123;
|
|
constexpr uint32_t kDepth = 69u;
|
|
|
|
TextureSpec defaultTextureSpec;
|
|
defaultTextureSpec.textureSize = {kWidth, kHeight, kDepth};
|
|
|
|
for (unsigned int i = 1; i < 6; ++i) {
|
|
TextureSpec textureSpec = defaultTextureSpec;
|
|
textureSpec.copyLevel = i;
|
|
textureSpec.levelCount = i + 1;
|
|
|
|
DoTest(textureSpec, MinimumBufferSpec(kWidth >> i, kHeight >> i, kDepth >> i),
|
|
{kWidth >> i, kHeight >> i, kDepth >> i}, wgpu::TextureDimension::e3D);
|
|
}
|
|
}
|
|
|
|
DAWN_INSTANTIATE_TEST(CopyTests_B2T,
|
|
D3D12Backend(),
|
|
MetalBackend(),
|
|
OpenGLBackend(),
|
|
OpenGLESBackend(),
|
|
VulkanBackend());
|
|
|
|
TEST_P(CopyTests_T2T, Texture) {
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {kWidth, kHeight, 1};
|
|
DoTest(textureSpec, textureSpec, {kWidth, kHeight, 1});
|
|
}
|
|
|
|
// Test noop copies.
|
|
TEST_P(CopyTests_T2T, ZeroSizedCopy) {
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {kWidth, kHeight, 1};
|
|
DoTest(textureSpec, textureSpec, {0, kHeight, 1});
|
|
DoTest(textureSpec, textureSpec, {kWidth, 0, 1});
|
|
DoTest(textureSpec, textureSpec, {kWidth, kHeight, 0});
|
|
}
|
|
|
|
TEST_P(CopyTests_T2T, TextureRegion) {
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
|
|
TextureSpec defaultTextureSpec;
|
|
defaultTextureSpec.textureSize = {kWidth, kHeight, 1};
|
|
|
|
for (unsigned int w : {64, 128, 256}) {
|
|
for (unsigned int h : {16, 32, 48}) {
|
|
TextureSpec textureSpec = defaultTextureSpec;
|
|
DoTest(textureSpec, textureSpec, {w, h, 1});
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_P(CopyTests_T2T, TextureMip) {
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
|
|
TextureSpec defaultTextureSpec;
|
|
defaultTextureSpec.textureSize = {kWidth, kHeight, 1};
|
|
|
|
for (unsigned int i = 1; i < 4; ++i) {
|
|
TextureSpec textureSpec = defaultTextureSpec;
|
|
textureSpec.copyLevel = i;
|
|
textureSpec.levelCount = i + 1;
|
|
|
|
DoTest(textureSpec, textureSpec, {kWidth >> i, kHeight >> i, 1});
|
|
}
|
|
}
|
|
|
|
TEST_P(CopyTests_T2T, SingleMipSrcMultipleMipDst) {
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
|
|
TextureSpec defaultTextureSpec;
|
|
|
|
for (unsigned int i = 1; i < 4; ++i) {
|
|
TextureSpec srcTextureSpec = defaultTextureSpec;
|
|
srcTextureSpec.textureSize = {kWidth >> i, kHeight >> i, 1};
|
|
|
|
TextureSpec dstTextureSpec = defaultTextureSpec;
|
|
dstTextureSpec.textureSize = {kWidth, kHeight, 1};
|
|
dstTextureSpec.copyLevel = i;
|
|
dstTextureSpec.levelCount = i + 1;
|
|
|
|
DoTest(srcTextureSpec, dstTextureSpec, {kWidth >> i, kHeight >> i, 1});
|
|
}
|
|
}
|
|
|
|
TEST_P(CopyTests_T2T, MultipleMipSrcSingleMipDst) {
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
|
|
TextureSpec defaultTextureSpec;
|
|
|
|
for (unsigned int i = 1; i < 4; ++i) {
|
|
TextureSpec srcTextureSpec = defaultTextureSpec;
|
|
srcTextureSpec.textureSize = {kWidth, kHeight, 1};
|
|
srcTextureSpec.copyLevel = i;
|
|
srcTextureSpec.levelCount = i + 1;
|
|
|
|
TextureSpec dstTextureSpec = defaultTextureSpec;
|
|
dstTextureSpec.textureSize = {kWidth >> i, kHeight >> i, 1};
|
|
|
|
DoTest(srcTextureSpec, dstTextureSpec, {kWidth >> i, kHeight >> i, 1});
|
|
}
|
|
}
|
|
|
|
// Test that copying from one mip level to another mip level within the same 2D texture works.
|
|
TEST_P(CopyTests_T2T, Texture2DSameTextureDifferentMipLevels) {
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
|
|
TextureSpec defaultTextureSpec;
|
|
defaultTextureSpec.textureSize = {kWidth, kHeight, 1};
|
|
defaultTextureSpec.levelCount = 6;
|
|
|
|
for (unsigned int i = 1; i < 6; ++i) {
|
|
TextureSpec srcSpec = defaultTextureSpec;
|
|
srcSpec.copyLevel = i - 1;
|
|
TextureSpec dstSpec = defaultTextureSpec;
|
|
dstSpec.copyLevel = i;
|
|
|
|
DoTest(srcSpec, dstSpec, {kWidth >> i, kHeight >> i, 1}, true);
|
|
}
|
|
}
|
|
|
|
// Test copying the whole 2D array texture.
|
|
TEST_P(CopyTests_T2T, Texture2DArrayFull) {
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
constexpr uint32_t kLayers = 6u;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {kWidth, kHeight, kLayers};
|
|
|
|
DoTest(textureSpec, textureSpec, {kWidth, kHeight, kLayers});
|
|
}
|
|
|
|
// Test copying a subresource region of the 2D array texture.
|
|
TEST_P(CopyTests_T2T, Texture2DArrayRegion) {
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
constexpr uint32_t kLayers = 6u;
|
|
|
|
TextureSpec defaultTextureSpec;
|
|
defaultTextureSpec.textureSize = {kWidth, kHeight, kLayers};
|
|
|
|
for (unsigned int w : {64, 128, 256}) {
|
|
for (unsigned int h : {16, 32, 48}) {
|
|
TextureSpec textureSpec = defaultTextureSpec;
|
|
DoTest(textureSpec, textureSpec, {w, h, kLayers});
|
|
}
|
|
}
|
|
}
|
|
|
|
// Test copying one slice of a 2D array texture.
|
|
TEST_P(CopyTests_T2T, Texture2DArrayCopyOneSlice) {
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
constexpr uint32_t kLayers = 6u;
|
|
constexpr uint32_t kSrcBaseLayer = 1u;
|
|
constexpr uint32_t kDstBaseLayer = 3u;
|
|
constexpr uint32_t kCopyArrayLayerCount = 1u;
|
|
|
|
TextureSpec defaultTextureSpec;
|
|
defaultTextureSpec.textureSize = {kWidth, kHeight, kLayers};
|
|
|
|
TextureSpec srcTextureSpec = defaultTextureSpec;
|
|
srcTextureSpec.copyOrigin = {0, 0, kSrcBaseLayer};
|
|
|
|
TextureSpec dstTextureSpec = defaultTextureSpec;
|
|
dstTextureSpec.copyOrigin = {0, 0, kDstBaseLayer};
|
|
|
|
DoTest(srcTextureSpec, dstTextureSpec, {kWidth, kHeight, kCopyArrayLayerCount});
|
|
}
|
|
|
|
// Test copying multiple contiguous slices of a 2D array texture.
|
|
TEST_P(CopyTests_T2T, Texture2DArrayCopyMultipleSlices) {
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
constexpr uint32_t kLayers = 6u;
|
|
constexpr uint32_t kSrcBaseLayer = 0u;
|
|
constexpr uint32_t kDstBaseLayer = 3u;
|
|
constexpr uint32_t kCopyArrayLayerCount = 3u;
|
|
|
|
TextureSpec defaultTextureSpec;
|
|
defaultTextureSpec.textureSize = {kWidth, kHeight, kLayers};
|
|
|
|
TextureSpec srcTextureSpec = defaultTextureSpec;
|
|
srcTextureSpec.copyOrigin = {0, 0, kSrcBaseLayer};
|
|
|
|
TextureSpec dstTextureSpec = defaultTextureSpec;
|
|
dstTextureSpec.copyOrigin = {0, 0, kDstBaseLayer};
|
|
|
|
DoTest(srcTextureSpec, dstTextureSpec, {kWidth, kHeight, kCopyArrayLayerCount});
|
|
}
|
|
|
|
// Test copying one texture slice within the same texture.
|
|
TEST_P(CopyTests_T2T, CopyWithinSameTextureOneSlice) {
|
|
constexpr uint32_t kWidth = 256u;
|
|
constexpr uint32_t kHeight = 128u;
|
|
constexpr uint32_t kLayers = 6u;
|
|
constexpr uint32_t kSrcBaseLayer = 0u;
|
|
constexpr uint32_t kDstBaseLayer = 3u;
|
|
constexpr uint32_t kCopyArrayLayerCount = 1u;
|
|
|
|
TextureSpec defaultTextureSpec;
|
|
defaultTextureSpec.textureSize = {kWidth, kHeight, kLayers};
|
|
|
|
TextureSpec srcTextureSpec = defaultTextureSpec;
|
|
srcTextureSpec.copyOrigin = {0, 0, kSrcBaseLayer};
|
|
|
|
TextureSpec dstTextureSpec = defaultTextureSpec;
|
|
dstTextureSpec.copyOrigin = {0, 0, kDstBaseLayer};
|
|
|
|
DoTest(srcTextureSpec, dstTextureSpec, {kWidth, kHeight, kCopyArrayLayerCount}, true);
|
|
}
|
|
|
|
// Test copying multiple contiguous texture slices within the same texture with non-overlapped
|
|
// slices.
|
|
TEST_P(CopyTests_T2T, CopyWithinSameTextureNonOverlappedSlices) {
|
|
constexpr uint32_t kWidth = 256u;
|
|
constexpr uint32_t kHeight = 128u;
|
|
constexpr uint32_t kLayers = 6u;
|
|
constexpr uint32_t kSrcBaseLayer = 0u;
|
|
constexpr uint32_t kDstBaseLayer = 3u;
|
|
constexpr uint32_t kCopyArrayLayerCount = 3u;
|
|
|
|
TextureSpec defaultTextureSpec;
|
|
defaultTextureSpec.textureSize = {kWidth, kHeight, kLayers};
|
|
|
|
TextureSpec srcTextureSpec = defaultTextureSpec;
|
|
srcTextureSpec.copyOrigin = {0, 0, kSrcBaseLayer};
|
|
|
|
TextureSpec dstTextureSpec = defaultTextureSpec;
|
|
dstTextureSpec.copyOrigin = {0, 0, kDstBaseLayer};
|
|
|
|
DoTest(srcTextureSpec, dstTextureSpec, {kWidth, kHeight, kCopyArrayLayerCount}, true);
|
|
}
|
|
|
|
// A regression test (from WebGPU CTS) for an Intel D3D12 driver bug about T2T copy with specific
|
|
// texture formats. See http://crbug.com/1161355 for more details.
|
|
TEST_P(CopyTests_T2T, CopyFromNonZeroMipLevelWithTexelBlockSizeLessThan4Bytes) {
|
|
constexpr std::array<wgpu::TextureFormat, 11> kFormats = {
|
|
{wgpu::TextureFormat::RG8Sint, wgpu::TextureFormat::RG8Uint, wgpu::TextureFormat::RG8Snorm,
|
|
wgpu::TextureFormat::RG8Unorm, wgpu::TextureFormat::R16Float, wgpu::TextureFormat::R16Sint,
|
|
wgpu::TextureFormat::R16Uint, wgpu::TextureFormat::R8Snorm, wgpu::TextureFormat::R8Unorm,
|
|
wgpu::TextureFormat::R8Sint, wgpu::TextureFormat::R8Uint}};
|
|
|
|
constexpr uint32_t kSrcLevelCount = 4;
|
|
constexpr uint32_t kDstLevelCount = 5;
|
|
constexpr uint32_t kSrcSize = 2 << kSrcLevelCount;
|
|
constexpr uint32_t kDstSize = 2 << kDstLevelCount;
|
|
ASSERT_LE(kSrcSize, kTextureBytesPerRowAlignment);
|
|
ASSERT_LE(kDstSize, kTextureBytesPerRowAlignment);
|
|
|
|
// The copyLayer to test:
|
|
// 1u (non-array texture), 3u (copyLayer < copyWidth), 5u (copyLayer > copyWidth)
|
|
constexpr std::array<uint32_t, 3> kTestTextureLayer = {1u, 3u, 5u};
|
|
|
|
for (wgpu::TextureFormat format : kFormats) {
|
|
if (HasToggleEnabled("disable_snorm_read") &&
|
|
(format == wgpu::TextureFormat::RG8Snorm || format == wgpu::TextureFormat::R8Snorm)) {
|
|
continue;
|
|
}
|
|
|
|
if (HasToggleEnabled("disable_r8_rg8_mipmaps") &&
|
|
(format == wgpu::TextureFormat::R8Unorm || format == wgpu::TextureFormat::RG8Unorm)) {
|
|
continue;
|
|
}
|
|
|
|
for (uint32_t textureLayer : kTestTextureLayer) {
|
|
const wgpu::Extent3D kUploadSize = {4u, 4u, textureLayer};
|
|
|
|
for (uint32_t srcLevel = 0; srcLevel < kSrcLevelCount; ++srcLevel) {
|
|
for (uint32_t dstLevel = 0; dstLevel < kDstLevelCount; ++dstLevel) {
|
|
TextureSpec srcSpec;
|
|
srcSpec.levelCount = kSrcLevelCount;
|
|
srcSpec.format = format;
|
|
srcSpec.copyLevel = srcLevel;
|
|
srcSpec.textureSize = {kSrcSize, kSrcSize, textureLayer};
|
|
|
|
TextureSpec dstSpec = srcSpec;
|
|
dstSpec.levelCount = kDstLevelCount;
|
|
dstSpec.copyLevel = dstLevel;
|
|
dstSpec.textureSize = {kDstSize, kDstSize, textureLayer};
|
|
|
|
DoTest(srcSpec, dstSpec, kUploadSize);
|
|
}
|
|
}
|
|
|
|
// Resolve all the deferred expectations now to avoid allocating too much memory
|
|
// in mDeferredExpectations.
|
|
ResolveDeferredExpectationsNow();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Test that copying from one mip level to another mip level within the same 2D array texture works.
|
|
TEST_P(CopyTests_T2T, Texture2DArraySameTextureDifferentMipLevels) {
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
constexpr uint32_t kLayers = 8u;
|
|
|
|
TextureSpec defaultTextureSpec;
|
|
defaultTextureSpec.textureSize = {kWidth, kHeight, kLayers};
|
|
defaultTextureSpec.levelCount = 6;
|
|
|
|
for (unsigned int i = 1; i < 6; ++i) {
|
|
TextureSpec srcSpec = defaultTextureSpec;
|
|
srcSpec.copyLevel = i - 1;
|
|
TextureSpec dstSpec = defaultTextureSpec;
|
|
dstSpec.copyLevel = i;
|
|
|
|
DoTest(srcSpec, dstSpec, {kWidth >> i, kHeight >> i, kLayers}, true);
|
|
}
|
|
}
|
|
|
|
// Test that copying whole 3D texture in one texture-to-texture-copy works.
|
|
TEST_P(CopyTests_T2T, Texture3DFull) {
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
constexpr uint32_t kDepth = 6u;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {kWidth, kHeight, kDepth};
|
|
|
|
DoTest(textureSpec, textureSpec, {kWidth, kHeight, kDepth}, false, wgpu::TextureDimension::e3D);
|
|
}
|
|
|
|
// Test that copying from one mip level to another mip level within the same 3D texture works.
|
|
TEST_P(CopyTests_T2T, Texture3DSameTextureDifferentMipLevels) {
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
constexpr uint32_t kDepth = 6u;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {kWidth, kHeight, kDepth};
|
|
textureSpec.levelCount = 2;
|
|
|
|
TextureSpec dstSpec = textureSpec;
|
|
dstSpec.copyLevel = 1;
|
|
|
|
DoTest(textureSpec, dstSpec, {kWidth >> 1, kHeight >> 1, kDepth >> 1}, true,
|
|
wgpu::TextureDimension::e3D);
|
|
}
|
|
|
|
// Test that copying whole 3D texture to a 2D array in one texture-to-texture-copy works.
|
|
TEST_P(CopyTests_T2T, Texture3DTo2DArrayFull) {
|
|
// TODO(crbug.com/dawn/1425): Remove this suppression.
|
|
DAWN_SUPPRESS_TEST_IF(IsANGLE() && IsWindows() && IsIntel());
|
|
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
constexpr uint32_t kDepth = 6u;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {kWidth, kHeight, kDepth};
|
|
|
|
DoTest(textureSpec, textureSpec, {kWidth, kHeight, kDepth}, wgpu::TextureDimension::e3D,
|
|
wgpu::TextureDimension::e2D);
|
|
}
|
|
|
|
// Test that copying between 3D texture and 2D array textures works. It includes partial copy
|
|
// for src and/or dst texture, non-zero offset (copy origin), non-zero mip level.
|
|
TEST_P(CopyTests_T2T, Texture3DAnd2DArraySubRegion) {
|
|
// TODO(crbug.com/dawn/1216): Remove this suppression.
|
|
DAWN_SUPPRESS_TEST_IF(IsD3D12() && IsNvidia());
|
|
|
|
// TODO(crbug.com/dawn/1426): Remove this suppression.
|
|
DAWN_SUPPRESS_TEST_IF(IsANGLE() && IsWindows() && IsIntel());
|
|
|
|
constexpr uint32_t kWidth = 8;
|
|
constexpr uint32_t kHeight = 4;
|
|
constexpr uint32_t kDepth = 2u;
|
|
|
|
TextureSpec baseSpec;
|
|
baseSpec.textureSize = {kWidth, kHeight, kDepth};
|
|
|
|
TextureSpec srcSpec = baseSpec;
|
|
TextureSpec dstSpec = baseSpec;
|
|
|
|
// dst texture is a partial copy
|
|
dstSpec.textureSize = {kWidth * 2, kHeight * 2, kDepth * 2};
|
|
DoTest(srcSpec, dstSpec, {kWidth, kHeight, kDepth}, wgpu::TextureDimension::e3D,
|
|
wgpu::TextureDimension::e2D);
|
|
DoTest(srcSpec, dstSpec, {kWidth, kHeight, kDepth}, wgpu::TextureDimension::e2D,
|
|
wgpu::TextureDimension::e3D);
|
|
|
|
// src texture is a partial copy
|
|
srcSpec.textureSize = {kWidth * 2, kHeight * 2, kDepth * 2};
|
|
dstSpec = baseSpec;
|
|
DoTest(srcSpec, dstSpec, {kWidth, kHeight, kDepth}, wgpu::TextureDimension::e3D,
|
|
wgpu::TextureDimension::e2D);
|
|
DoTest(srcSpec, dstSpec, {kWidth, kHeight, kDepth}, wgpu::TextureDimension::e2D,
|
|
wgpu::TextureDimension::e3D);
|
|
|
|
// Both src and dst texture is a partial copy
|
|
srcSpec.textureSize = {kWidth * 2, kHeight * 2, kDepth * 2};
|
|
dstSpec.textureSize = {kWidth * 2, kHeight * 2, kDepth * 2};
|
|
DoTest(srcSpec, dstSpec, {kWidth, kHeight, kDepth}, wgpu::TextureDimension::e3D,
|
|
wgpu::TextureDimension::e2D);
|
|
DoTest(srcSpec, dstSpec, {kWidth, kHeight, kDepth}, wgpu::TextureDimension::e2D,
|
|
wgpu::TextureDimension::e3D);
|
|
|
|
// Non-zero offset (copy origin)
|
|
srcSpec = baseSpec;
|
|
dstSpec.textureSize = {kWidth * 2, kHeight * 2, kDepth * 2};
|
|
dstSpec.copyOrigin = {kWidth, kHeight, kDepth};
|
|
DoTest(srcSpec, dstSpec, {kWidth, kHeight, kDepth}, wgpu::TextureDimension::e3D,
|
|
wgpu::TextureDimension::e2D);
|
|
DoTest(srcSpec, dstSpec, {kWidth, kHeight, kDepth}, wgpu::TextureDimension::e2D,
|
|
wgpu::TextureDimension::e3D);
|
|
|
|
// Non-zero mip level
|
|
srcSpec = baseSpec;
|
|
dstSpec.textureSize = {kWidth * 2, kHeight * 2, kDepth * 2};
|
|
dstSpec.copyOrigin = {0, 0, 0};
|
|
dstSpec.copyLevel = 1;
|
|
dstSpec.levelCount = 2;
|
|
DoTest(srcSpec, dstSpec, {kWidth, kHeight, kDepth}, wgpu::TextureDimension::e3D,
|
|
wgpu::TextureDimension::e2D);
|
|
DoTest(srcSpec, dstSpec, {kWidth, kHeight, kDepth}, wgpu::TextureDimension::e2D,
|
|
wgpu::TextureDimension::e3D);
|
|
}
|
|
|
|
// Test that copying whole 2D array to a 3D texture in one texture-to-texture-copy works.
|
|
TEST_P(CopyTests_T2T, Texture2DArrayTo3DFull) {
|
|
// TODO(crbug.com/dawn/1216): Remove this suppression.
|
|
DAWN_SUPPRESS_TEST_IF(IsD3D12() && IsNvidia());
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
constexpr uint32_t kDepth = 6u;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {kWidth, kHeight, kDepth};
|
|
|
|
DoTest(textureSpec, textureSpec, {kWidth, kHeight, kDepth}, wgpu::TextureDimension::e2D,
|
|
wgpu::TextureDimension::e3D);
|
|
}
|
|
|
|
// Test that copying subregion of a 3D texture in one texture-to-texture-copy works.
|
|
TEST_P(CopyTests_T2T, Texture3DSubRegion) {
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
constexpr uint32_t kDepth = 6u;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {kWidth, kHeight, kDepth};
|
|
|
|
DoTest(textureSpec, textureSpec, {kWidth / 2, kHeight / 2, kDepth / 2}, false,
|
|
wgpu::TextureDimension::e3D);
|
|
}
|
|
|
|
// Test that copying subregion of a 3D texture to a 2D array in one texture-to-texture-copy works.
|
|
TEST_P(CopyTests_T2T, Texture3DTo2DArraySubRegion) {
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
constexpr uint32_t kDepth = 6u;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {kWidth, kHeight, kDepth};
|
|
|
|
DoTest(textureSpec, textureSpec, {kWidth / 2, kHeight / 2, kDepth / 2},
|
|
wgpu::TextureDimension::e3D, wgpu::TextureDimension::e2D);
|
|
}
|
|
|
|
// Test that copying subregion of a 2D array to a 3D texture to in one texture-to-texture-copy
|
|
// works.
|
|
TEST_P(CopyTests_T2T, Texture2DArrayTo3DSubRegion) {
|
|
// TODO(crbug.com/dawn/1216): Remove this suppression.
|
|
DAWN_SUPPRESS_TEST_IF(IsD3D12() && IsNvidia());
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
constexpr uint32_t kDepth = 6u;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {kWidth, kHeight, kDepth};
|
|
|
|
DoTest(textureSpec, textureSpec, {kWidth / 2, kHeight / 2, kDepth / 2},
|
|
wgpu::TextureDimension::e2D, wgpu::TextureDimension::e3D);
|
|
}
|
|
|
|
// Test that copying texture 3D array mips in one texture-to-texture-copy works
|
|
TEST_P(CopyTests_T2T, Texture3DMipAligned) {
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
constexpr uint32_t kDepth = 64u;
|
|
|
|
TextureSpec defaultTextureSpec;
|
|
defaultTextureSpec.textureSize = {kWidth, kHeight, kDepth};
|
|
|
|
for (unsigned int i = 1; i < 6; ++i) {
|
|
TextureSpec textureSpec = defaultTextureSpec;
|
|
textureSpec.copyLevel = i;
|
|
textureSpec.levelCount = i + 1;
|
|
|
|
DoTest(textureSpec, textureSpec, {kWidth >> i, kHeight >> i, kDepth >> i},
|
|
wgpu::TextureDimension::e3D, wgpu::TextureDimension::e3D);
|
|
}
|
|
}
|
|
|
|
// Test that copying texture 3D array mips in one texture-to-texture-copy works
|
|
TEST_P(CopyTests_T2T, Texture3DMipUnaligned) {
|
|
constexpr uint32_t kWidth = 261;
|
|
constexpr uint32_t kHeight = 123;
|
|
constexpr uint32_t kDepth = 69u;
|
|
|
|
TextureSpec defaultTextureSpec;
|
|
defaultTextureSpec.textureSize = {kWidth, kHeight, kDepth};
|
|
|
|
for (unsigned int i = 1; i < 6; ++i) {
|
|
TextureSpec textureSpec = defaultTextureSpec;
|
|
textureSpec.copyLevel = i;
|
|
textureSpec.levelCount = i + 1;
|
|
|
|
DoTest(textureSpec, textureSpec, {kWidth >> i, kHeight >> i, kDepth >> i},
|
|
wgpu::TextureDimension::e3D, wgpu::TextureDimension::e3D);
|
|
}
|
|
}
|
|
|
|
DAWN_INSTANTIATE_TEST_P(CopyTests_T2T,
|
|
{D3D12Backend(),
|
|
D3D12Backend({"use_temp_buffer_in_small_format_texture_to_texture_copy_"
|
|
"from_greater_to_less_mip_level"}),
|
|
MetalBackend(), OpenGLBackend(), OpenGLESBackend(), VulkanBackend()},
|
|
{true, false});
|
|
|
|
// Test copying between textures that have srgb compatible texture formats;
|
|
TEST_P(CopyTests_Formats, SrgbCompatibility) {
|
|
// Skip backends because which fails to support *-srgb formats
|
|
// and bgra* formats.
|
|
DAWN_SUPPRESS_TEST_IF(IsOpenGLES());
|
|
DAWN_SUPPRESS_TEST_IF(IsOpenGL() && IsLinux());
|
|
|
|
constexpr uint32_t kWidth = 256;
|
|
constexpr uint32_t kHeight = 128;
|
|
|
|
TextureSpec textureSpec;
|
|
textureSpec.textureSize = {kWidth, kHeight, 1};
|
|
DoTest(textureSpec, textureSpec, {kWidth, kHeight, 1});
|
|
}
|
|
|
|
DAWN_INSTANTIATE_TEST_P(CopyTests_Formats,
|
|
{D3D12Backend(), MetalBackend(), OpenGLBackend(), OpenGLESBackend(),
|
|
VulkanBackend()},
|
|
{wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureFormat::RGBA8UnormSrgb,
|
|
wgpu::TextureFormat::BGRA8Unorm, wgpu::TextureFormat::BGRA8UnormSrgb});
|
|
|
|
static constexpr uint64_t kSmallBufferSize = 4;
|
|
static constexpr uint64_t kLargeBufferSize = 1 << 16;
|
|
|
|
// Test copying full buffers
|
|
TEST_P(CopyTests_B2B, FullCopy) {
|
|
DoTest(kSmallBufferSize, 0, kSmallBufferSize, 0, kSmallBufferSize);
|
|
DoTest(kLargeBufferSize, 0, kLargeBufferSize, 0, kLargeBufferSize);
|
|
}
|
|
|
|
// Test copying small pieces of a buffer at different corner case offsets
|
|
TEST_P(CopyTests_B2B, SmallCopyInBigBuffer) {
|
|
constexpr uint64_t kEndOffset = kLargeBufferSize - kSmallBufferSize;
|
|
DoTest(kLargeBufferSize, 0, kLargeBufferSize, 0, kSmallBufferSize);
|
|
DoTest(kLargeBufferSize, kEndOffset, kLargeBufferSize, 0, kSmallBufferSize);
|
|
DoTest(kLargeBufferSize, 0, kLargeBufferSize, kEndOffset, kSmallBufferSize);
|
|
DoTest(kLargeBufferSize, kEndOffset, kLargeBufferSize, kEndOffset, kSmallBufferSize);
|
|
}
|
|
|
|
// Test zero-size copies
|
|
TEST_P(CopyTests_B2B, ZeroSizedCopy) {
|
|
DoTest(kLargeBufferSize, 0, kLargeBufferSize, 0, 0);
|
|
DoTest(kLargeBufferSize, 0, kLargeBufferSize, kLargeBufferSize, 0);
|
|
DoTest(kLargeBufferSize, kLargeBufferSize, kLargeBufferSize, 0, 0);
|
|
DoTest(kLargeBufferSize, kLargeBufferSize, kLargeBufferSize, kLargeBufferSize, 0);
|
|
}
|
|
|
|
DAWN_INSTANTIATE_TEST(CopyTests_B2B,
|
|
D3D12Backend(),
|
|
MetalBackend(),
|
|
OpenGLBackend(),
|
|
OpenGLESBackend(),
|
|
VulkanBackend());
|
|
|
|
// Test clearing full buffers
|
|
TEST_P(ClearBufferTests, FullClear) {
|
|
DoTest(kSmallBufferSize, 0, kSmallBufferSize);
|
|
DoTest(kLargeBufferSize, 0, kLargeBufferSize);
|
|
}
|
|
|
|
// Test clearing small pieces of a buffer at different corner case offsets
|
|
TEST_P(ClearBufferTests, SmallClearInBigBuffer) {
|
|
constexpr uint64_t kEndOffset = kLargeBufferSize - kSmallBufferSize;
|
|
DoTest(kLargeBufferSize, 0, kSmallBufferSize);
|
|
DoTest(kLargeBufferSize, kSmallBufferSize, kSmallBufferSize);
|
|
DoTest(kLargeBufferSize, kEndOffset, kSmallBufferSize);
|
|
}
|
|
|
|
// Test zero-size clears
|
|
TEST_P(ClearBufferTests, ZeroSizedClear) {
|
|
DoTest(kLargeBufferSize, 0, 0);
|
|
DoTest(kLargeBufferSize, kSmallBufferSize, 0);
|
|
DoTest(kLargeBufferSize, kLargeBufferSize, 0);
|
|
}
|
|
|
|
DAWN_INSTANTIATE_TEST(ClearBufferTests,
|
|
D3D12Backend(),
|
|
MetalBackend(),
|
|
OpenGLBackend(),
|
|
OpenGLESBackend(),
|
|
VulkanBackend());
|
|
|
|
// Regression tests to reproduce a flaky failure when running whole WebGPU CTS on Intel GPUs.
|
|
// See crbug.com/dawn/1487 for more details.
|
|
namespace {
|
|
using TextureFormat = wgpu::TextureFormat;
|
|
|
|
enum class InitializationMethod {
|
|
CopyBufferToTexture,
|
|
WriteTexture,
|
|
CopyTextureToTexture,
|
|
};
|
|
|
|
std::ostream& operator<<(std::ostream& o, InitializationMethod method) {
|
|
switch (method) {
|
|
case InitializationMethod::CopyBufferToTexture:
|
|
o << "CopyBufferToTexture";
|
|
break;
|
|
case InitializationMethod::WriteTexture:
|
|
o << "WriteTexture";
|
|
break;
|
|
case InitializationMethod::CopyTextureToTexture:
|
|
o << "CopyTextureToTexture";
|
|
break;
|
|
default:
|
|
UNREACHABLE();
|
|
break;
|
|
}
|
|
|
|
return o;
|
|
}
|
|
|
|
using AddRenderAttachmentUsage = bool;
|
|
|
|
DAWN_TEST_PARAM_STRUCT(CopyToDepthStencilTextureAfterDestroyingBigBufferTestsParams,
|
|
TextureFormat,
|
|
InitializationMethod,
|
|
AddRenderAttachmentUsage);
|
|
|
|
} // anonymous namespace
|
|
|
|
class CopyToDepthStencilTextureAfterDestroyingBigBufferTests
|
|
: public DawnTestWithParams<CopyToDepthStencilTextureAfterDestroyingBigBufferTestsParams> {};
|
|
|
|
TEST_P(CopyToDepthStencilTextureAfterDestroyingBigBufferTests, DoTest) {
|
|
// TODO(crbug.com/dawn/1492): Support CopyBufferToTexture() with Depth16Unorm on OpenGL /
|
|
// OpenGL ES backends.
|
|
DAWN_SUPPRESS_TEST_IF(GetParam().mTextureFormat == wgpu::TextureFormat::Depth16Unorm &&
|
|
(IsOpenGL() || IsOpenGLES()));
|
|
|
|
// Copies to stencil textures are unsupported on the OpenGL backend.
|
|
DAWN_TEST_UNSUPPORTED_IF(GetParam().mTextureFormat == wgpu::TextureFormat::Stencil8 &&
|
|
(IsOpenGL() || IsOpenGLES()));
|
|
|
|
wgpu::TextureFormat format = GetParam().mTextureFormat;
|
|
|
|
const uint32_t texelBlockSize = utils::GetTexelBlockSizeInBytes(format);
|
|
const uint32_t expectedDataSize = Align(texelBlockSize, 4u);
|
|
|
|
// First, create a big buffer and fill some garbage data on DEFAULT heap.
|
|
constexpr size_t kBigBufferSize = 159740u;
|
|
constexpr uint8_t kGarbageData = 255u;
|
|
std::array<uint8_t, kBigBufferSize> garbageData;
|
|
garbageData.fill(kGarbageData);
|
|
|
|
wgpu::Buffer bigBuffer =
|
|
utils::CreateBufferFromData(device, garbageData.data(), garbageData.size(),
|
|
wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst);
|
|
// Next, destroy the buffer. Its heap is still alive and contains the garbage data.
|
|
bigBuffer.Destroy();
|
|
|
|
// Ensure the underlying ID3D12Resource of bigBuffer is deleted.
|
|
bool submittedWorkDone = false;
|
|
queue.OnSubmittedWorkDone(
|
|
0,
|
|
[](WGPUQueueWorkDoneStatus status, void* userdata) {
|
|
EXPECT_EQ(status, WGPUQueueWorkDoneStatus_Success);
|
|
*static_cast<bool*>(userdata) = true;
|
|
},
|
|
&submittedWorkDone);
|
|
while (!submittedWorkDone) {
|
|
WaitABit();
|
|
}
|
|
|
|
// Then, create a small texture, which should be allocated on the heap that contains the
|
|
// garbage data.
|
|
wgpu::TextureDescriptor textureDescriptor = {};
|
|
textureDescriptor.format = format;
|
|
textureDescriptor.size = {1, 1, 1};
|
|
textureDescriptor.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst;
|
|
if (GetParam().mAddRenderAttachmentUsage) {
|
|
textureDescriptor.usage |= wgpu::TextureUsage::RenderAttachment;
|
|
}
|
|
wgpu::Texture texture = device.CreateTexture(&textureDescriptor);
|
|
|
|
// Finally, upload valid data into the texture and validate its contents.
|
|
std::vector<uint8_t> expectedData(expectedDataSize);
|
|
constexpr uint8_t kBaseValue = 204u;
|
|
for (uint32_t i = 0; i < texelBlockSize; ++i) {
|
|
expectedData[i] = static_cast<uint8_t>(i + kBaseValue);
|
|
}
|
|
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
wgpu::Extent3D copySize = {1, 1, 1};
|
|
|
|
auto EncodeUploadDataToTexture = [this, copySize](wgpu::CommandEncoder encoder,
|
|
wgpu::Texture destinationTexture,
|
|
const std::vector<uint8_t>& expectedData) {
|
|
wgpu::BufferDescriptor bufferDescriptor = {};
|
|
bufferDescriptor.size = expectedData.size();
|
|
bufferDescriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::MapWrite;
|
|
wgpu::Buffer uploadBuffer = device.CreateBuffer(&bufferDescriptor);
|
|
bool done = false;
|
|
uploadBuffer.MapAsync(
|
|
wgpu::MapMode::Write, 0, static_cast<uint32_t>(expectedData.size()),
|
|
[](WGPUBufferMapAsyncStatus status, void* userdata) {
|
|
ASSERT_EQ(WGPUBufferMapAsyncStatus_Success, status);
|
|
*static_cast<bool*>(userdata) = true;
|
|
},
|
|
&done);
|
|
while (!done) {
|
|
WaitABit();
|
|
}
|
|
|
|
uint8_t* uploadData = static_cast<uint8_t*>(uploadBuffer.GetMappedRange());
|
|
memcpy(uploadData, expectedData.data(), expectedData.size());
|
|
uploadBuffer.Unmap();
|
|
|
|
wgpu::ImageCopyBuffer imageCopyBuffer = utils::CreateImageCopyBuffer(uploadBuffer);
|
|
wgpu::ImageCopyTexture imageCopyTexture = utils::CreateImageCopyTexture(destinationTexture);
|
|
encoder.CopyBufferToTexture(&imageCopyBuffer, &imageCopyTexture, ©Size);
|
|
};
|
|
|
|
switch (GetParam().mInitializationMethod) {
|
|
case InitializationMethod::CopyBufferToTexture: {
|
|
EncodeUploadDataToTexture(encoder, texture, expectedData);
|
|
break;
|
|
}
|
|
case InitializationMethod::WriteTexture: {
|
|
wgpu::ImageCopyTexture imageCopyTexture = utils::CreateImageCopyTexture(texture);
|
|
wgpu::TextureDataLayout layout;
|
|
queue.WriteTexture(&imageCopyTexture, expectedData.data(), texelBlockSize, &layout,
|
|
©Size);
|
|
break;
|
|
}
|
|
case InitializationMethod::CopyTextureToTexture: {
|
|
wgpu::Texture stagingTexture = device.CreateTexture(&textureDescriptor);
|
|
EncodeUploadDataToTexture(encoder, stagingTexture, expectedData);
|
|
|
|
wgpu::ImageCopyTexture imageCopyStagingTexture =
|
|
utils::CreateImageCopyTexture(stagingTexture);
|
|
wgpu::ImageCopyTexture imageCopyTexture = utils::CreateImageCopyTexture(texture);
|
|
encoder.CopyTextureToTexture(&imageCopyStagingTexture, &imageCopyTexture, ©Size);
|
|
break;
|
|
}
|
|
default:
|
|
UNREACHABLE();
|
|
break;
|
|
}
|
|
|
|
wgpu::BufferDescriptor destinationBufferDescriptor = {};
|
|
destinationBufferDescriptor.size = expectedDataSize;
|
|
destinationBufferDescriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst;
|
|
wgpu::Buffer destinationBuffer = device.CreateBuffer(&destinationBufferDescriptor);
|
|
|
|
wgpu::ImageCopyTexture imageCopyTexture = utils::CreateImageCopyTexture(texture);
|
|
wgpu::ImageCopyBuffer imageCopyDestinationBuffer =
|
|
utils::CreateImageCopyBuffer(destinationBuffer);
|
|
encoder.CopyTextureToBuffer(&imageCopyTexture, &imageCopyDestinationBuffer, ©Size);
|
|
|
|
wgpu::CommandBuffer commandBuffer = encoder.Finish();
|
|
queue.Submit(1, &commandBuffer);
|
|
|
|
EXPECT_BUFFER_U8_RANGE_EQ(expectedData.data(), destinationBuffer, 0, expectedDataSize);
|
|
}
|
|
|
|
DAWN_INSTANTIATE_TEST_P(
|
|
CopyToDepthStencilTextureAfterDestroyingBigBufferTests,
|
|
{D3D12Backend(), D3D12Backend({"d3d12_force_clear_copyable_depth_stencil_texture_on_creation"}),
|
|
MetalBackend(), OpenGLBackend(), OpenGLESBackend(), VulkanBackend()},
|
|
{wgpu::TextureFormat::Depth16Unorm, wgpu::TextureFormat::Stencil8},
|
|
{InitializationMethod::CopyBufferToTexture, InitializationMethod::WriteTexture,
|
|
InitializationMethod::CopyTextureToTexture},
|
|
{true, false});
|
|
|
|
// A series of regression tests for an Intel D3D12 driver issue about creating textures with
|
|
// CreatePlacedResource(). See crbug.com/1237175 for more details.
|
|
class T2TCopyFromDirtyHeapTests : public DawnTest {
|
|
public:
|
|
void DoTest(uint32_t layerCount, uint32_t levelCount) {
|
|
std::vector<uint32_t> expectedData;
|
|
wgpu::Buffer uploadBuffer = GetUploadBufferAndExpectedData(&expectedData);
|
|
|
|
// First, create colorTexture1 and colorTexture2 and fill data into them.
|
|
wgpu::Texture colorTexture1 = Create2DTexture(kTextureSize, layerCount, levelCount);
|
|
Initialize2DTexture(colorTexture1, layerCount, levelCount, uploadBuffer);
|
|
wgpu::Texture colorTexture2 = Create2DTexture(kTextureSize, layerCount, levelCount);
|
|
Initialize2DTexture(colorTexture2, layerCount, levelCount, uploadBuffer);
|
|
|
|
// Next, destroy colorTexture1.
|
|
colorTexture1.Destroy();
|
|
|
|
// Ensure colorTexture1 has been destroyed on the backend.
|
|
EnsureSubmittedWorkDone();
|
|
// Call an empty queue.Submit to workaround crbug.com/dawn/833 where resources are not
|
|
// recycled until the next serial.
|
|
queue.Submit(0, nullptr);
|
|
EnsureSubmittedWorkDone();
|
|
|
|
// Then, try to create destinationTextures which should be allocated on the memory used by
|
|
// colorTexture1 previously, copy data from colorTexture2 into each destinationTexture and
|
|
// verify the data in destinationTexture.
|
|
std::vector<wgpu::Texture> destinationTextures;
|
|
|
|
for (uint32_t layer = 0; layer < layerCount; ++layer) {
|
|
for (uint32_t level = 0; level < levelCount; ++level) {
|
|
uint32_t textureSizeAtLevel = kTextureSize >> level;
|
|
wgpu::Texture destinationTexture = Create2DTexture(textureSizeAtLevel, 1, 1);
|
|
// Save all destinationTextures so that they won't be deleted during the test.
|
|
destinationTextures.push_back(destinationTexture);
|
|
|
|
CopyIntoStagingTextureAndVerifyTexelData(colorTexture2, level, layer,
|
|
destinationTexture, expectedData);
|
|
}
|
|
}
|
|
}
|
|
|
|
wgpu::Buffer GetUploadBufferAndExpectedData(std::vector<uint32_t>* expectedData) {
|
|
const uint32_t kBytesPerRow =
|
|
Align(kBytesPerBlock * kTextureSize, kTextureBytesPerRowAlignment);
|
|
const size_t kBufferSize =
|
|
kBytesPerRow * (kTextureSize - 1) + kTextureSize * kBytesPerBlock;
|
|
|
|
expectedData->resize(kBufferSize / sizeof(uint32_t));
|
|
for (uint32_t y = 0; y < kTextureSize; ++y) {
|
|
for (uint32_t x = 0; x < kTextureSize * (kBytesPerBlock / sizeof(uint32_t)); ++x) {
|
|
uint32_t index = (kBytesPerRow / sizeof(uint32_t)) * y + x;
|
|
(*expectedData)[index] = x + y * 1000;
|
|
}
|
|
}
|
|
|
|
wgpu::BufferDescriptor uploadBufferDesc = {};
|
|
uploadBufferDesc.size = kBufferSize;
|
|
uploadBufferDesc.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::MapWrite;
|
|
uploadBufferDesc.mappedAtCreation = true;
|
|
wgpu::Buffer uploadBuffer = device.CreateBuffer(&uploadBufferDesc);
|
|
|
|
memcpy(uploadBuffer.GetMappedRange(), expectedData->data(), kBufferSize);
|
|
uploadBuffer.Unmap();
|
|
|
|
return uploadBuffer;
|
|
}
|
|
|
|
wgpu::Texture Create2DTexture(uint32_t textureSize, uint32_t layerCount, uint32_t levelCount) {
|
|
wgpu::TextureDescriptor colorTextureDesc = {};
|
|
colorTextureDesc.format = kFormat;
|
|
colorTextureDesc.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst |
|
|
wgpu::TextureUsage::RenderAttachment;
|
|
colorTextureDesc.mipLevelCount = levelCount;
|
|
colorTextureDesc.size = {textureSize, textureSize, layerCount};
|
|
return device.CreateTexture(&colorTextureDesc);
|
|
}
|
|
|
|
void Initialize2DTexture(wgpu::Texture texture,
|
|
uint32_t layerCount,
|
|
uint32_t levelCount,
|
|
wgpu::Buffer uploadBuffer) {
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
for (uint32_t layer = 0; layer < layerCount; ++layer) {
|
|
for (uint32_t level = 0; level < levelCount; ++level) {
|
|
wgpu::ImageCopyBuffer uploadCopyBuffer =
|
|
utils::CreateImageCopyBuffer(uploadBuffer, 0, kBytesPerRow, kTextureSize);
|
|
wgpu::ImageCopyTexture colorCopyTexture =
|
|
utils::CreateImageCopyTexture(texture, level, {0, 0, layer});
|
|
|
|
wgpu::Extent3D copySize = {kTextureSize >> level, kTextureSize >> level, 1};
|
|
encoder.CopyBufferToTexture(&uploadCopyBuffer, &colorCopyTexture, ©Size);
|
|
}
|
|
}
|
|
wgpu::CommandBuffer commandBuffer = encoder.Finish();
|
|
queue.Submit(1, &commandBuffer);
|
|
}
|
|
|
|
void CopyIntoStagingTextureAndVerifyTexelData(wgpu::Texture sourceTexture,
|
|
uint32_t copyLevel,
|
|
uint32_t copyLayer,
|
|
wgpu::Texture stagingTexture,
|
|
const std::vector<uint32_t>& expectedData) {
|
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
|
|
// Copy from miplevel = copyLevel and arrayLayer = copyLayer of sourceTexture into
|
|
// stagingTexture
|
|
wgpu::ImageCopyTexture colorCopyTexture =
|
|
utils::CreateImageCopyTexture(sourceTexture, copyLevel, {0, 0, copyLayer});
|
|
uint32_t stagingTextureSize = kTextureSize >> copyLevel;
|
|
wgpu::Extent3D copySize = {stagingTextureSize, stagingTextureSize, 1};
|
|
wgpu::ImageCopyTexture stagingCopyTexture = utils::CreateImageCopyTexture(stagingTexture);
|
|
encoder.CopyTextureToTexture(&colorCopyTexture, &stagingCopyTexture, ©Size);
|
|
|
|
// Copy from stagingTexture into readback buffer. Note that we don't use EXPECT_BUFFER_xxx()
|
|
// because the buffers with CopySrc | CopyDst may also be allocated on the same memory of
|
|
// colorTexture1, then stagingTexture will not be able to be allocated at that piece of
|
|
// memory, which is not what we intended to do.
|
|
const size_t kBufferSize =
|
|
kBytesPerRow * (stagingTextureSize - 1) + stagingTextureSize * kBytesPerBlock;
|
|
wgpu::BufferDescriptor readbackBufferDesc = {};
|
|
readbackBufferDesc.size = kBufferSize;
|
|
readbackBufferDesc.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopyDst;
|
|
wgpu::Buffer readbackBuffer = device.CreateBuffer(&readbackBufferDesc);
|
|
wgpu::ImageCopyBuffer readbackCopyBuffer =
|
|
utils::CreateImageCopyBuffer(readbackBuffer, 0, kBytesPerRow, kTextureSize);
|
|
encoder.CopyTextureToBuffer(&stagingCopyTexture, &readbackCopyBuffer, ©Size);
|
|
|
|
wgpu::CommandBuffer commandBuffer = encoder.Finish();
|
|
queue.Submit(1, &commandBuffer);
|
|
|
|
// Check the data in readback buffer
|
|
bool done = false;
|
|
readbackBuffer.MapAsync(
|
|
wgpu::MapMode::Read, 0, kBufferSize,
|
|
[](WGPUBufferMapAsyncStatus status, void* userdata) {
|
|
ASSERT_EQ(WGPUBufferMapAsyncStatus_Success, status);
|
|
*static_cast<bool*>(userdata) = true;
|
|
},
|
|
&done);
|
|
while (!done) {
|
|
WaitABit();
|
|
}
|
|
|
|
const uint32_t* readbackData =
|
|
static_cast<const uint32_t*>(readbackBuffer.GetConstMappedRange());
|
|
for (uint32_t y = 0; y < stagingTextureSize; ++y) {
|
|
for (uint32_t x = 0; x < stagingTextureSize * (kBytesPerBlock / sizeof(uint32_t));
|
|
++x) {
|
|
uint32_t index = y * (kBytesPerRow / sizeof(uint32_t)) + x;
|
|
ASSERT_EQ(expectedData[index], readbackData[index]);
|
|
}
|
|
}
|
|
|
|
readbackBuffer.Destroy();
|
|
}
|
|
|
|
void EnsureSubmittedWorkDone() {
|
|
bool submittedWorkDone = false;
|
|
queue.OnSubmittedWorkDone(
|
|
0,
|
|
[](WGPUQueueWorkDoneStatus status, void* userdata) {
|
|
EXPECT_EQ(status, WGPUQueueWorkDoneStatus_Success);
|
|
*static_cast<bool*>(userdata) = true;
|
|
},
|
|
&submittedWorkDone);
|
|
while (!submittedWorkDone) {
|
|
WaitABit();
|
|
}
|
|
}
|
|
|
|
private:
|
|
const uint32_t kTextureSize = 63;
|
|
const wgpu::TextureFormat kFormat = wgpu::TextureFormat::RGBA32Uint;
|
|
const uint32_t kBytesPerBlock = 16;
|
|
const uint32_t kBytesPerRow =
|
|
Align(kBytesPerBlock * kTextureSize, kTextureBytesPerRowAlignment);
|
|
};
|
|
|
|
TEST_P(T2TCopyFromDirtyHeapTests, From2DArrayTexture) {
|
|
constexpr uint32_t kLayerCount = 7;
|
|
constexpr uint32_t kLevelCount = 1;
|
|
DoTest(kLayerCount, kLevelCount);
|
|
}
|
|
|
|
TEST_P(T2TCopyFromDirtyHeapTests, From2DMultiMipmapLevelTexture) {
|
|
constexpr uint32_t kLayerCount = 1;
|
|
constexpr uint32_t kLevelCount = 5;
|
|
DoTest(kLayerCount, kLevelCount);
|
|
}
|
|
|
|
DAWN_INSTANTIATE_TEST(T2TCopyFromDirtyHeapTests,
|
|
D3D12Backend(),
|
|
MetalBackend(),
|
|
OpenGLBackend(),
|
|
OpenGLESBackend(),
|
|
VulkanBackend());
|