Adding Queue::WriteTexture
Added Queue::WriteTexture with validation but no actual implementation. Tests were mostly taken from validation tests for copying buffer to texture. Validation tests for CopyB2T and WriteTexture do not cover 2d-array textures yet. Bug: dawn:483 Change-Id: I9027eb615c02fe2265cde912f6ba17a235b94728 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/24440 Reviewed-by: Corentin Wallez <cwallez@chromium.org> Reviewed-by: Austin Eng <enga@chromium.org> Commit-Queue: Tomek Ponitka <tommek@google.com>
This commit is contained in:
parent
83fe1bc34d
commit
a9c7d64aad
10
dawn.json
10
dawn.json
|
@ -985,6 +985,16 @@
|
|||
{"name": "data", "type": "void", "annotation": "const*", "length": "size"},
|
||||
{"name": "size", "type": "size_t"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "write texture",
|
||||
"args": [
|
||||
{"name": "destinaton", "type": "texture copy view", "annotation": "const*"},
|
||||
{"name": "data", "type": "void", "annotation": "const*"},
|
||||
{"name": "data size", "type": "size_t"},
|
||||
{"name": "data layout", "type": "texture data layout", "annotation": "const*"},
|
||||
{"name": "writeSize", "type": "extent 3D", "annotation": "const*"}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -54,6 +54,14 @@
|
|||
{"name": "buffer offset", "type": "uint64_t"},
|
||||
{"name": "data", "type": "uint8_t", "annotation": "const*", "length": "size"},
|
||||
{"name": "size", "type": "size_t"}
|
||||
],
|
||||
"queue write texture internal": [
|
||||
{"name": "queue id", "type": "ObjectId" },
|
||||
{"name": "destination", "type": "texture copy view", "annotation": "const*"},
|
||||
{"name": "data", "type": "uint8_t", "annotation": "const*"},
|
||||
{"name": "data size", "type": "size_t"},
|
||||
{"name": "data layout", "type": "texture data layout", "annotation": "const*"},
|
||||
{"name": "writeSize", "type": "extent 3D", "annotation": "const*"}
|
||||
]
|
||||
},
|
||||
"return commands": {
|
||||
|
@ -104,7 +112,8 @@
|
|||
"DeviceSetUncapturedErrorCallback",
|
||||
"FenceGetCompletedValue",
|
||||
"FenceOnCompletion",
|
||||
"QueueWriteBuffer"
|
||||
"QueueWriteBuffer",
|
||||
"QueueWriteTexture"
|
||||
],
|
||||
"client_handwritten_commands": [
|
||||
"BufferDestroy",
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include "dawn_native/Buffer.h"
|
||||
#include "dawn_native/CommandBuffer.h"
|
||||
#include "dawn_native/CommandValidation.h"
|
||||
#include "dawn_native/Device.h"
|
||||
#include "dawn_native/DynamicUploader.h"
|
||||
#include "dawn_native/ErrorScope.h"
|
||||
|
@ -131,6 +132,33 @@ namespace dawn_native {
|
|||
buffer, bufferOffset, size);
|
||||
}
|
||||
|
||||
void QueueBase::WriteTexture(const TextureCopyView* destination,
|
||||
const void* data,
|
||||
size_t dataSize,
|
||||
const TextureDataLayout* dataLayout,
|
||||
const Extent3D* writeSize) {
|
||||
GetDevice()->ConsumedError(
|
||||
WriteTextureInternal(destination, data, dataSize, dataLayout, writeSize));
|
||||
}
|
||||
|
||||
MaybeError QueueBase::WriteTextureInternal(const TextureCopyView* destination,
|
||||
const void* data,
|
||||
size_t dataSize,
|
||||
const TextureDataLayout* dataLayout,
|
||||
const Extent3D* writeSize) {
|
||||
DAWN_TRY(ValidateWriteTexture(destination, dataSize, dataLayout, writeSize));
|
||||
return WriteTextureImpl(destination, data, dataSize, dataLayout, writeSize);
|
||||
}
|
||||
|
||||
MaybeError QueueBase::WriteTextureImpl(const TextureCopyView* destination,
|
||||
const void* data,
|
||||
size_t dataSize,
|
||||
const TextureDataLayout* dataLayout,
|
||||
const Extent3D* writeSize) {
|
||||
// TODO(tommek@google.com): This should be implemented.
|
||||
return {};
|
||||
}
|
||||
|
||||
MaybeError QueueBase::ValidateSubmit(uint32_t commandCount,
|
||||
CommandBufferBase* const* commands) const {
|
||||
TRACE_EVENT0(GetDevice()->GetPlatform(), Validation, "Queue::ValidateSubmit");
|
||||
|
@ -215,4 +243,33 @@ namespace dawn_native {
|
|||
return buffer->ValidateCanUseOnQueueNow();
|
||||
}
|
||||
|
||||
MaybeError QueueBase::ValidateWriteTexture(const TextureCopyView* destination,
|
||||
size_t dataSize,
|
||||
const TextureDataLayout* dataLayout,
|
||||
const Extent3D* writeSize) const {
|
||||
DAWN_TRY(GetDevice()->ValidateIsAlive());
|
||||
DAWN_TRY(GetDevice()->ValidateObject(this));
|
||||
DAWN_TRY(GetDevice()->ValidateObject(destination->texture));
|
||||
|
||||
DAWN_TRY(ValidateTextureCopyView(GetDevice(), *destination));
|
||||
|
||||
if (dataLayout->offset > dataSize) {
|
||||
return DAWN_VALIDATION_ERROR("Queue::WriteTexture out of range");
|
||||
}
|
||||
|
||||
if (!(destination->texture->GetUsage() & wgpu::TextureUsage::CopyDst)) {
|
||||
return DAWN_VALIDATION_ERROR("Texture needs the CopyDst usage bit");
|
||||
}
|
||||
|
||||
if (destination->texture->GetSampleCount() > 1) {
|
||||
return DAWN_VALIDATION_ERROR("The sample count of textures must be 1");
|
||||
}
|
||||
|
||||
DAWN_TRY(ValidateLinearTextureData(*dataLayout, dataSize, destination->texture->GetFormat(),
|
||||
*writeSize));
|
||||
DAWN_TRY(ValidateTextureCopyRange(*destination, *writeSize));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace dawn_native
|
||||
|
|
|
@ -34,6 +34,11 @@ namespace dawn_native {
|
|||
void Signal(Fence* fence, uint64_t signalValue);
|
||||
Fence* CreateFence(const FenceDescriptor* descriptor);
|
||||
void WriteBuffer(BufferBase* buffer, uint64_t bufferOffset, const void* data, size_t size);
|
||||
void WriteTexture(const TextureCopyView* destination,
|
||||
const void* data,
|
||||
size_t dataSize,
|
||||
const TextureDataLayout* dataLayout,
|
||||
const Extent3D* writeSize);
|
||||
|
||||
private:
|
||||
QueueBase(DeviceBase* device, ObjectBase::ErrorTag tag);
|
||||
|
@ -42,12 +47,22 @@ namespace dawn_native {
|
|||
uint64_t bufferOffset,
|
||||
const void* data,
|
||||
size_t size);
|
||||
MaybeError WriteTextureInternal(const TextureCopyView* destination,
|
||||
const void* data,
|
||||
size_t dataSize,
|
||||
const TextureDataLayout* dataLayout,
|
||||
const Extent3D* writeSize);
|
||||
|
||||
virtual MaybeError SubmitImpl(uint32_t commandCount, CommandBufferBase* const* commands);
|
||||
virtual MaybeError WriteBufferImpl(BufferBase* buffer,
|
||||
uint64_t bufferOffset,
|
||||
const void* data,
|
||||
size_t size);
|
||||
virtual MaybeError WriteTextureImpl(const TextureCopyView* destination,
|
||||
const void* data,
|
||||
size_t dataSize,
|
||||
const TextureDataLayout* dataLayout,
|
||||
const Extent3D* writeSize);
|
||||
|
||||
MaybeError ValidateSubmit(uint32_t commandCount, CommandBufferBase* const* commands) const;
|
||||
MaybeError ValidateSignal(const Fence* fence, uint64_t signalValue) const;
|
||||
|
@ -55,6 +70,10 @@ namespace dawn_native {
|
|||
MaybeError ValidateWriteBuffer(const BufferBase* buffer,
|
||||
uint64_t bufferOffset,
|
||||
size_t size) const;
|
||||
MaybeError ValidateWriteTexture(const TextureCopyView* destination,
|
||||
size_t dataSize,
|
||||
const TextureDataLayout* dataLayout,
|
||||
const Extent3D* writeSize) const;
|
||||
};
|
||||
|
||||
} // namespace dawn_native
|
||||
|
|
|
@ -72,4 +72,19 @@ namespace dawn_wire { namespace client {
|
|||
device->GetClient()->SerializeCommand(cmd);
|
||||
}
|
||||
|
||||
void Queue::WriteTexture(const WGPUTextureCopyView* destination,
|
||||
const void* data,
|
||||
size_t dataSize,
|
||||
const WGPUTextureDataLayout* dataLayout,
|
||||
const WGPUExtent3D* writeSize) {
|
||||
QueueWriteTextureInternalCmd cmd;
|
||||
cmd.queueId = id;
|
||||
cmd.destination = destination;
|
||||
cmd.dataSize = dataSize;
|
||||
cmd.dataLayout = dataLayout;
|
||||
cmd.writeSize = writeSize;
|
||||
|
||||
device->GetClient()->SerializeCommand(cmd);
|
||||
}
|
||||
|
||||
}} // namespace dawn_wire::client
|
||||
|
|
|
@ -31,6 +31,11 @@ namespace dawn_wire { namespace client {
|
|||
WGPUFence CreateFence(const WGPUFenceDescriptor* descriptor);
|
||||
void Signal(WGPUFence fence, uint64_t signalValue);
|
||||
void WriteBuffer(WGPUBuffer cBuffer, uint64_t bufferOffset, const void* data, size_t size);
|
||||
void WriteTexture(const WGPUTextureCopyView* destination,
|
||||
const void* data,
|
||||
size_t dataSize,
|
||||
const WGPUTextureDataLayout* dataLayout,
|
||||
const WGPUExtent3D* writeSize);
|
||||
};
|
||||
|
||||
}} // namespace dawn_wire::client
|
||||
|
|
|
@ -55,4 +55,21 @@ namespace dawn_wire { namespace server {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Server::DoQueueWriteTextureInternal(ObjectId queueId,
|
||||
const WGPUTextureCopyView* destination,
|
||||
const uint8_t* data,
|
||||
size_t dataSize,
|
||||
const WGPUTextureDataLayout* dataLayout,
|
||||
const WGPUExtent3D* writeSize) {
|
||||
// The null object isn't valid as `self` so we can combine the check with the
|
||||
// check that the ID is valid.
|
||||
auto* queue = QueueObjects().Get(queueId);
|
||||
if (queue == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mProcs.queueWriteTexture(queue->handle, destination, data, dataSize, dataLayout, writeSize);
|
||||
return true;
|
||||
}
|
||||
|
||||
}} // namespace dawn_wire::server
|
||||
|
|
|
@ -192,6 +192,7 @@ test("dawn_unittests") {
|
|||
"unittests/validation/MinimumBufferSizeValidationTests.cpp",
|
||||
"unittests/validation/QuerySetValidationTests.cpp",
|
||||
"unittests/validation/QueueSubmitValidationTests.cpp",
|
||||
"unittests/validation/QueueWriteTextureValidationTests.cpp",
|
||||
"unittests/validation/RenderBundleValidationTests.cpp",
|
||||
"unittests/validation/RenderPassDescriptorValidationTests.cpp",
|
||||
"unittests/validation/RenderPipelineValidationTests.cpp",
|
||||
|
|
|
@ -48,25 +48,12 @@ class CopyCommandTest : public ValidationTest {
|
|||
return tex;
|
||||
}
|
||||
|
||||
// TODO(jiawei.shao@intel.com): support more pixel formats
|
||||
uint32_t TextureFormatPixelSize(wgpu::TextureFormat format) {
|
||||
switch (format) {
|
||||
case wgpu::TextureFormat::RG8Unorm:
|
||||
return 2;
|
||||
case wgpu::TextureFormat::RGBA8Unorm:
|
||||
return 4;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t BufferSizeForTextureCopy(
|
||||
uint32_t width,
|
||||
uint32_t height,
|
||||
uint32_t depth,
|
||||
wgpu::TextureFormat format = wgpu::TextureFormat::RGBA8Unorm) {
|
||||
uint32_t bytesPerPixel = TextureFormatPixelSize(format);
|
||||
uint32_t bytesPerPixel = utils::TextureFormatPixelSize(format);
|
||||
uint32_t bytesPerRow = Align(width * bytesPerPixel, kTextureBytesPerRowAlignment);
|
||||
return (bytesPerRow * (height - 1) + width * bytesPerPixel) * depth;
|
||||
}
|
||||
|
@ -492,7 +479,7 @@ TEST_F(CopyCommandTest_B2T, ImageHeightConstraint) {
|
|||
{4, 4, 1});
|
||||
|
||||
// Image height is larger than copy height (Valid)
|
||||
TestB2TCopy(utils::Expectation::Success, source, 0, 256, 4, destination, 0, {0, 0, 0},
|
||||
TestB2TCopy(utils::Expectation::Success, source, 0, 256, 5, destination, 0, {0, 0, 0},
|
||||
{4, 4, 1});
|
||||
|
||||
// Image height is less than copy height (Invalid)
|
||||
|
@ -1357,30 +1344,6 @@ class CopyCommandTest_CompressedTextureFormats : public CopyCommandTest {
|
|||
kUsage, 1);
|
||||
}
|
||||
|
||||
static uint32_t CompressedFormatBlockSizeInBytes(wgpu::TextureFormat format) {
|
||||
switch (format) {
|
||||
case wgpu::TextureFormat::BC1RGBAUnorm:
|
||||
case wgpu::TextureFormat::BC1RGBAUnormSrgb:
|
||||
case wgpu::TextureFormat::BC4RSnorm:
|
||||
case wgpu::TextureFormat::BC4RUnorm:
|
||||
return 8;
|
||||
case wgpu::TextureFormat::BC2RGBAUnorm:
|
||||
case wgpu::TextureFormat::BC2RGBAUnormSrgb:
|
||||
case wgpu::TextureFormat::BC3RGBAUnorm:
|
||||
case wgpu::TextureFormat::BC3RGBAUnormSrgb:
|
||||
case wgpu::TextureFormat::BC5RGSnorm:
|
||||
case wgpu::TextureFormat::BC5RGUnorm:
|
||||
case wgpu::TextureFormat::BC6HRGBSfloat:
|
||||
case wgpu::TextureFormat::BC6HRGBUfloat:
|
||||
case wgpu::TextureFormat::BC7RGBAUnorm:
|
||||
case wgpu::TextureFormat::BC7RGBAUnormSrgb:
|
||||
return 16;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void TestBothTBCopies(utils::Expectation expectation,
|
||||
wgpu::Buffer buffer,
|
||||
uint64_t bufferOffset,
|
||||
|
@ -1410,15 +1373,6 @@ class CopyCommandTest_CompressedTextureFormats : public CopyCommandTest {
|
|||
|
||||
static constexpr uint32_t kWidth = 16;
|
||||
static constexpr uint32_t kHeight = 16;
|
||||
|
||||
const std::array<wgpu::TextureFormat, 14> kBCFormats = {
|
||||
wgpu::TextureFormat::BC1RGBAUnorm, wgpu::TextureFormat::BC1RGBAUnormSrgb,
|
||||
wgpu::TextureFormat::BC2RGBAUnorm, wgpu::TextureFormat::BC2RGBAUnormSrgb,
|
||||
wgpu::TextureFormat::BC3RGBAUnorm, wgpu::TextureFormat::BC3RGBAUnormSrgb,
|
||||
wgpu::TextureFormat::BC4RUnorm, wgpu::TextureFormat::BC4RSnorm,
|
||||
wgpu::TextureFormat::BC5RGUnorm, wgpu::TextureFormat::BC5RGSnorm,
|
||||
wgpu::TextureFormat::BC6HRGBUfloat, wgpu::TextureFormat::BC6HRGBSfloat,
|
||||
wgpu::TextureFormat::BC7RGBAUnorm, wgpu::TextureFormat::BC7RGBAUnormSrgb};
|
||||
};
|
||||
|
||||
// Tests to verify that bufferOffset must be a multiple of the compressed texture blocks in bytes
|
||||
|
@ -1427,19 +1381,19 @@ TEST_F(CopyCommandTest_CompressedTextureFormats, BufferOffset) {
|
|||
wgpu::Buffer buffer =
|
||||
CreateBuffer(512, wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst);
|
||||
|
||||
for (wgpu::TextureFormat bcFormat : kBCFormats) {
|
||||
for (wgpu::TextureFormat bcFormat : utils::kBCFormats) {
|
||||
wgpu::Texture texture = Create2DTexture(bcFormat);
|
||||
|
||||
// Valid usages of BufferOffset in B2T and T2B copies with compressed texture formats.
|
||||
{
|
||||
uint32_t validBufferOffset = CompressedFormatBlockSizeInBytes(bcFormat);
|
||||
uint32_t validBufferOffset = utils::CompressedFormatBlockSizeInBytes(bcFormat);
|
||||
TestBothTBCopies(utils::Expectation::Success, buffer, validBufferOffset, 256, 4,
|
||||
texture, 0, {0, 0, 0}, {4, 4, 1});
|
||||
}
|
||||
|
||||
// Failures on invalid bufferOffset.
|
||||
{
|
||||
uint32_t kInvalidBufferOffset = CompressedFormatBlockSizeInBytes(bcFormat) / 2;
|
||||
uint32_t kInvalidBufferOffset = utils::CompressedFormatBlockSizeInBytes(bcFormat) / 2;
|
||||
TestBothTBCopies(utils::Expectation::Failure, buffer, kInvalidBufferOffset, 256, 4,
|
||||
texture, 0, {0, 0, 0}, {4, 4, 1});
|
||||
}
|
||||
|
@ -1460,7 +1414,7 @@ TEST_F(CopyCommandTest_CompressedTextureFormats, BytesPerRow) {
|
|||
// Failures on the BytesPerRow that is not large enough.
|
||||
{
|
||||
constexpr uint32_t kSmallBytesPerRow = 256;
|
||||
for (wgpu::TextureFormat bcFormat : kBCFormats) {
|
||||
for (wgpu::TextureFormat bcFormat : utils::kBCFormats) {
|
||||
wgpu::Texture texture = Create2DTexture(bcFormat, 1, kTestWidth, kTestHeight);
|
||||
TestBothTBCopies(utils::Expectation::Failure, buffer, 0, kSmallBytesPerRow, 4,
|
||||
texture, 0, {0, 0, 0}, {kTestWidth, 4, 1});
|
||||
|
@ -1469,10 +1423,10 @@ TEST_F(CopyCommandTest_CompressedTextureFormats, BytesPerRow) {
|
|||
|
||||
// Test it is not valid to use a BytesPerRow that is not a multiple of 256.
|
||||
{
|
||||
for (wgpu::TextureFormat bcFormat : kBCFormats) {
|
||||
for (wgpu::TextureFormat bcFormat : utils::kBCFormats) {
|
||||
wgpu::Texture texture = Create2DTexture(bcFormat, 1, kTestWidth, kTestHeight);
|
||||
uint32_t inValidBytesPerRow =
|
||||
kTestWidth / 4 * CompressedFormatBlockSizeInBytes(bcFormat);
|
||||
kTestWidth / 4 * utils::CompressedFormatBlockSizeInBytes(bcFormat);
|
||||
ASSERT_NE(0u, inValidBytesPerRow % 256);
|
||||
TestBothTBCopies(utils::Expectation::Failure, buffer, 0, inValidBytesPerRow, 4,
|
||||
texture, 0, {0, 0, 0}, {kTestWidth, 4, 1});
|
||||
|
@ -1481,10 +1435,10 @@ TEST_F(CopyCommandTest_CompressedTextureFormats, BytesPerRow) {
|
|||
|
||||
// Test the smallest valid BytesPerRow should work.
|
||||
{
|
||||
for (wgpu::TextureFormat bcFormat : kBCFormats) {
|
||||
for (wgpu::TextureFormat bcFormat : utils::kBCFormats) {
|
||||
wgpu::Texture texture = Create2DTexture(bcFormat, 1, kTestWidth, kTestHeight);
|
||||
uint32_t smallestValidBytesPerRow =
|
||||
Align(kTestWidth / 4 * CompressedFormatBlockSizeInBytes(bcFormat), 256);
|
||||
Align(kTestWidth / 4 * utils::CompressedFormatBlockSizeInBytes(bcFormat), 256);
|
||||
TestBothTBCopies(utils::Expectation::Success, buffer, 0, smallestValidBytesPerRow,
|
||||
4, texture, 0, {0, 0, 0}, {kTestWidth, 4, 1});
|
||||
}
|
||||
|
@ -1498,7 +1452,7 @@ TEST_F(CopyCommandTest_CompressedTextureFormats, ImageHeight) {
|
|||
wgpu::Buffer buffer =
|
||||
CreateBuffer(512, wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst);
|
||||
|
||||
for (wgpu::TextureFormat bcFormat : kBCFormats) {
|
||||
for (wgpu::TextureFormat bcFormat : utils::kBCFormats) {
|
||||
wgpu::Texture texture = Create2DTexture(bcFormat);
|
||||
|
||||
// Valid usages of rowsPerImage in B2T and T2B copies with compressed texture formats.
|
||||
|
@ -1524,7 +1478,7 @@ TEST_F(CopyCommandTest_CompressedTextureFormats, ImageOffset) {
|
|||
wgpu::Buffer buffer =
|
||||
CreateBuffer(512, wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst);
|
||||
|
||||
for (wgpu::TextureFormat bcFormat : kBCFormats) {
|
||||
for (wgpu::TextureFormat bcFormat : utils::kBCFormats) {
|
||||
wgpu::Texture texture = Create2DTexture(bcFormat);
|
||||
wgpu::Texture texture2 = Create2DTexture(bcFormat);
|
||||
|
||||
|
@ -1571,7 +1525,7 @@ TEST_F(CopyCommandTest_CompressedTextureFormats, ImageExtent) {
|
|||
constexpr uint32_t kTestWidth = 60;
|
||||
constexpr uint32_t kTestHeight = 60;
|
||||
|
||||
for (wgpu::TextureFormat bcFormat : kBCFormats) {
|
||||
for (wgpu::TextureFormat bcFormat : utils::kBCFormats) {
|
||||
wgpu::Texture texture = Create2DTexture(bcFormat, kMipmapLevels, kTestWidth, kTestHeight);
|
||||
wgpu::Texture texture2 = Create2DTexture(bcFormat, kMipmapLevels, kTestWidth, kTestHeight);
|
||||
|
||||
|
|
|
@ -0,0 +1,571 @@
|
|||
// Copyright 2020 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 "tests/unittests/validation/ValidationTest.h"
|
||||
|
||||
#include "common/Math.h"
|
||||
#include "utils/WGPUHelpers.h"
|
||||
|
||||
namespace {
|
||||
|
||||
class QueueWriteTextureValidationTest : public ValidationTest {
|
||||
private:
|
||||
void SetUp() override {
|
||||
ValidationTest::SetUp();
|
||||
queue = device.GetDefaultQueue();
|
||||
}
|
||||
|
||||
protected:
|
||||
wgpu::Texture Create2DTexture(wgpu::Extent3D size,
|
||||
uint32_t mipLevelCount,
|
||||
wgpu::TextureFormat format,
|
||||
wgpu::TextureUsage usage,
|
||||
uint32_t sampleCount = 1) {
|
||||
wgpu::TextureDescriptor descriptor;
|
||||
descriptor.dimension = wgpu::TextureDimension::e2D;
|
||||
descriptor.size.width = size.width;
|
||||
descriptor.size.height = size.height;
|
||||
descriptor.size.depth = size.depth;
|
||||
descriptor.sampleCount = sampleCount;
|
||||
descriptor.format = format;
|
||||
descriptor.mipLevelCount = mipLevelCount;
|
||||
descriptor.usage = usage;
|
||||
wgpu::Texture tex = device.CreateTexture(&descriptor);
|
||||
return tex;
|
||||
}
|
||||
|
||||
uint64_t RequiredBytesInCopy(uint32_t bytesPerRow,
|
||||
uint32_t rowsPerImage,
|
||||
wgpu::Extent3D copyExtent,
|
||||
wgpu::TextureFormat format = wgpu::TextureFormat::RGBA8Unorm) {
|
||||
if (copyExtent.width == 0 || copyExtent.height == 0 || copyExtent.depth == 0) {
|
||||
return 0;
|
||||
} else {
|
||||
uint64_t bytesPerImage = bytesPerRow * rowsPerImage;
|
||||
uint64_t bytesInLastSlice =
|
||||
bytesPerRow * (copyExtent.height - 1) +
|
||||
(copyExtent.width * utils::TextureFormatPixelSize(format));
|
||||
return bytesPerImage * (copyExtent.depth - 1) + bytesInLastSlice;
|
||||
}
|
||||
}
|
||||
|
||||
void TestWriteTexture(size_t dataSize,
|
||||
uint32_t dataOffset,
|
||||
uint32_t dataBytesPerRow,
|
||||
uint32_t dataRowsPerImage,
|
||||
wgpu::Texture texture,
|
||||
uint32_t texLevel,
|
||||
wgpu::Origin3D texOrigin,
|
||||
wgpu::Extent3D size) {
|
||||
std::vector<uint8_t> data(dataSize);
|
||||
|
||||
wgpu::TextureDataLayout textureDataLayout;
|
||||
textureDataLayout.offset = dataOffset;
|
||||
textureDataLayout.bytesPerRow = dataBytesPerRow;
|
||||
textureDataLayout.rowsPerImage = dataRowsPerImage;
|
||||
|
||||
wgpu::TextureCopyView textureCopyView =
|
||||
utils::CreateTextureCopyView(texture, texLevel, texOrigin);
|
||||
|
||||
queue.WriteTexture(&textureCopyView, data.data(), dataSize, &textureDataLayout, &size);
|
||||
}
|
||||
|
||||
wgpu::Queue queue;
|
||||
};
|
||||
|
||||
// Test the success case for WriteTexture
|
||||
TEST_F(QueueWriteTextureValidationTest, Success) {
|
||||
const uint64_t dataSize = RequiredBytesInCopy(256, 0, {4, 4, 1});
|
||||
wgpu::Texture destination = Create2DTexture({16, 16, 4}, 5, wgpu::TextureFormat::RGBA8Unorm,
|
||||
wgpu::TextureUsage::CopyDst);
|
||||
|
||||
// Different copies, including some that touch the OOB condition
|
||||
{
|
||||
// Copy 4x4 block in corner of first mip.
|
||||
TestWriteTexture(dataSize, 0, 256, 0, destination, 0, {0, 0, 0}, {4, 4, 1});
|
||||
// Copy 4x4 block in opposite corner of first mip.
|
||||
TestWriteTexture(dataSize, 0, 256, 0, destination, 0, {12, 12, 0}, {4, 4, 1});
|
||||
// Copy 4x4 block in the 4x4 mip.
|
||||
TestWriteTexture(dataSize, 0, 256, 0, destination, 2, {0, 0, 0}, {4, 4, 1});
|
||||
// Copy with a data offset
|
||||
TestWriteTexture(dataSize, dataSize - 4, 256, 0, destination, 0, {0, 0, 0}, {1, 1, 1});
|
||||
}
|
||||
|
||||
// Copies with a 256-byte aligned bytes per row but unaligned texture region
|
||||
{
|
||||
// Unaligned region
|
||||
TestWriteTexture(dataSize, 0, 256, 0, destination, 0, {0, 0, 0}, {3, 4, 1});
|
||||
// Unaligned region with texture offset
|
||||
TestWriteTexture(dataSize, 0, 256, 0, destination, 0, {5, 7, 0}, {2, 3, 1});
|
||||
// Unaligned region, with data offset
|
||||
TestWriteTexture(dataSize, 31 * 4, 256, 0, destination, 0, {0, 0, 0}, {3, 3, 1});
|
||||
}
|
||||
|
||||
// Empty copies are valid
|
||||
{
|
||||
// An empty copy
|
||||
TestWriteTexture(dataSize, 0, 0, 0, destination, 0, {0, 0, 0}, {0, 0, 1});
|
||||
// An empty copy with depth = 0
|
||||
TestWriteTexture(dataSize, 0, 0, 0, destination, 0, {0, 0, 0}, {0, 0, 0});
|
||||
// An empty copy touching the end of the data
|
||||
TestWriteTexture(dataSize, dataSize, 0, 0, destination, 0, {0, 0, 0}, {0, 0, 1});
|
||||
// An empty copy touching the side of the texture
|
||||
TestWriteTexture(dataSize, 0, 0, 0, destination, 0, {16, 16, 0}, {0, 0, 1});
|
||||
// An empty copy with depth = 1 and bytesPerRow > 0
|
||||
TestWriteTexture(dataSize, 0, 256, 0, destination, 0, {0, 0, 0}, {0, 0, 1});
|
||||
// An empty copy with height > 0, depth = 0, bytesPerRow > 0 and rowsPerImage > 0
|
||||
TestWriteTexture(dataSize, 0, 256, 16, destination, 0, {0, 0, 0}, {0, 1, 0});
|
||||
}
|
||||
}
|
||||
|
||||
// Test OOB conditions on the data
|
||||
TEST_F(QueueWriteTextureValidationTest, OutOfBoundsOnData) {
|
||||
const uint64_t dataSize = RequiredBytesInCopy(256, 0, {4, 4, 1});
|
||||
wgpu::Texture destination = Create2DTexture({16, 16, 1}, 5, wgpu::TextureFormat::RGBA8Unorm,
|
||||
wgpu::TextureUsage::CopyDst);
|
||||
|
||||
// OOB on the data because we copy too many pixels
|
||||
ASSERT_DEVICE_ERROR(
|
||||
TestWriteTexture(dataSize, 0, 256, 0, destination, 0, {0, 0, 0}, {4, 5, 1}));
|
||||
|
||||
// OOB on the data because of the offset
|
||||
ASSERT_DEVICE_ERROR(
|
||||
TestWriteTexture(dataSize, 4, 256, 0, destination, 0, {0, 0, 0}, {4, 4, 1}));
|
||||
|
||||
// OOB on the data because RequiredBytesInCopy overflows
|
||||
ASSERT_DEVICE_ERROR(
|
||||
TestWriteTexture(dataSize, 0, 512, 0, destination, 0, {0, 0, 0}, {4, 3, 1}));
|
||||
|
||||
// Not OOB on the data although bytes per row * height overflows
|
||||
// but RequiredBytesInCopy * depth does not overflow
|
||||
{
|
||||
uint32_t sourceDataSize = RequiredBytesInCopy(256, 0, {7, 3, 1});
|
||||
ASSERT_TRUE(256 * 3 > sourceDataSize) << "bytes per row * height should overflow data";
|
||||
|
||||
TestWriteTexture(sourceDataSize, 0, 256, 0, destination, 0, {0, 0, 0}, {7, 3, 1});
|
||||
}
|
||||
}
|
||||
|
||||
// Test OOB conditions on the texture
|
||||
TEST_F(QueueWriteTextureValidationTest, OutOfBoundsOnTexture) {
|
||||
const uint64_t dataSize = RequiredBytesInCopy(256, 0, {4, 4, 1});
|
||||
wgpu::Texture destination = Create2DTexture({16, 16, 2}, 5, wgpu::TextureFormat::RGBA8Unorm,
|
||||
wgpu::TextureUsage::CopyDst);
|
||||
|
||||
// OOB on the texture because x + width overflows
|
||||
ASSERT_DEVICE_ERROR(
|
||||
TestWriteTexture(dataSize, 0, 256, 0, destination, 0, {13, 12, 0}, {4, 4, 1}));
|
||||
|
||||
// OOB on the texture because y + width overflows
|
||||
ASSERT_DEVICE_ERROR(
|
||||
TestWriteTexture(dataSize, 0, 256, 0, destination, 0, {12, 13, 0}, {4, 4, 1}));
|
||||
|
||||
// OOB on the texture because we overflow a non-zero mip
|
||||
ASSERT_DEVICE_ERROR(
|
||||
TestWriteTexture(dataSize, 0, 256, 0, destination, 2, {1, 0, 0}, {4, 4, 1}));
|
||||
|
||||
// OOB on the texture even on an empty copy when we copy to a non-existent mip.
|
||||
ASSERT_DEVICE_ERROR(
|
||||
TestWriteTexture(dataSize, 0, 0, 0, destination, 5, {0, 0, 0}, {0, 0, 1}));
|
||||
|
||||
// OOB on the texture because slice overflows
|
||||
ASSERT_DEVICE_ERROR(
|
||||
TestWriteTexture(dataSize, 0, 0, 0, destination, 0, {0, 0, 2}, {0, 0, 1}));
|
||||
}
|
||||
|
||||
// Test that we force Depth=1 on writes to 2D textures
|
||||
TEST_F(QueueWriteTextureValidationTest, DepthConstraintFor2DTextures) {
|
||||
const uint64_t dataSize = RequiredBytesInCopy(0, 0, {0, 0, 2});
|
||||
wgpu::Texture destination = Create2DTexture({16, 16, 1}, 5, wgpu::TextureFormat::RGBA8Unorm,
|
||||
wgpu::TextureUsage::CopyDst);
|
||||
|
||||
// Depth > 1 on an empty copy still errors
|
||||
ASSERT_DEVICE_ERROR(
|
||||
TestWriteTexture(dataSize, 0, 0, 0, destination, 0, {0, 0, 0}, {0, 0, 2}));
|
||||
}
|
||||
|
||||
// Test WriteTexture with incorrect texture usage
|
||||
TEST_F(QueueWriteTextureValidationTest, IncorrectUsage) {
|
||||
const uint64_t dataSize = RequiredBytesInCopy(256, 0, {4, 4, 1});
|
||||
wgpu::Texture sampled = Create2DTexture({16, 16, 1}, 5, wgpu::TextureFormat::RGBA8Unorm,
|
||||
wgpu::TextureUsage::Sampled);
|
||||
|
||||
// Incorrect destination usage
|
||||
ASSERT_DEVICE_ERROR(
|
||||
TestWriteTexture(dataSize, 0, 256, 0, sampled, 0, {0, 0, 0}, {4, 4, 1}));
|
||||
}
|
||||
|
||||
// Test incorrect values of bytesPerRow and that values not divisible by 256 are allowed.
|
||||
TEST_F(QueueWriteTextureValidationTest, BytesPerRowLimitations) {
|
||||
wgpu::Texture destination = Create2DTexture({3, 7, 1}, 1, wgpu::TextureFormat::RGBA8Unorm,
|
||||
wgpu::TextureUsage::CopyDst);
|
||||
|
||||
// bytesPerRow = 0 is invalid
|
||||
ASSERT_DEVICE_ERROR(TestWriteTexture(128, 0, 0, 0, destination, 0, {0, 0, 0}, {3, 7, 1}));
|
||||
|
||||
// bytesPerRow = 11 is invalid since a row takes 12 bytes.
|
||||
ASSERT_DEVICE_ERROR(TestWriteTexture(128, 0, 11, 0, destination, 0, {0, 0, 0}, {3, 7, 1}));
|
||||
|
||||
// bytesPerRow = 12 is valid since a row takes 12 bytes.
|
||||
TestWriteTexture(128, 0, 12, 0, destination, 0, {0, 0, 0}, {3, 7, 1});
|
||||
|
||||
// bytesPerRow = 13 is valid since a row takes 12 bytes.
|
||||
TestWriteTexture(128, 0, 13, 0, destination, 0, {0, 0, 0}, {3, 7, 1});
|
||||
}
|
||||
|
||||
// Test that if rowsPerImage is greater than 0, it must be at least copy height.
|
||||
TEST_F(QueueWriteTextureValidationTest, ImageHeightConstraint) {
|
||||
uint64_t dataSize = RequiredBytesInCopy(256, 0, {4, 4, 1});
|
||||
wgpu::Texture destination = Create2DTexture({16, 16, 1}, 1, wgpu::TextureFormat::RGBA8Unorm,
|
||||
wgpu::TextureUsage::CopyDst);
|
||||
|
||||
// Image height is zero (Valid)
|
||||
TestWriteTexture(dataSize, 0, 256, 0, destination, 0, {0, 0, 0}, {4, 4, 1});
|
||||
|
||||
// Image height is equal to copy height (Valid)
|
||||
TestWriteTexture(dataSize, 0, 256, 4, destination, 0, {0, 0, 0}, {4, 4, 1});
|
||||
|
||||
// Image height is larger than copy height (Valid)
|
||||
TestWriteTexture(dataSize, 0, 256, 5, destination, 0, {0, 0, 0}, {4, 4, 1});
|
||||
|
||||
// Image height is less than copy height (Invalid)
|
||||
ASSERT_DEVICE_ERROR(
|
||||
TestWriteTexture(dataSize, 0, 256, 3, destination, 0, {0, 0, 0}, {4, 4, 1}));
|
||||
}
|
||||
|
||||
// Test WriteTexture with incorrect data offset usage
|
||||
TEST_F(QueueWriteTextureValidationTest, IncorrectDataOffset) {
|
||||
uint64_t dataSize = RequiredBytesInCopy(256, 0, {4, 4, 1});
|
||||
wgpu::Texture destination = Create2DTexture({16, 16, 1}, 5, wgpu::TextureFormat::RGBA8Unorm,
|
||||
wgpu::TextureUsage::CopyDst);
|
||||
|
||||
// Correct usage
|
||||
TestWriteTexture(dataSize, dataSize - 4, 256, 0, destination, 0, {0, 0, 0}, {1, 1, 1});
|
||||
|
||||
ASSERT_DEVICE_ERROR(
|
||||
TestWriteTexture(dataSize, dataSize - 6, 256, 0, destination, 0, {0, 0, 0}, {1, 1, 1}));
|
||||
}
|
||||
|
||||
// Test multisampled textures can be used in WriteTexture.
|
||||
TEST_F(QueueWriteTextureValidationTest, WriteToMultisampledTexture) {
|
||||
uint64_t dataSize = RequiredBytesInCopy(256, 0, {2, 2, 1});
|
||||
wgpu::Texture destination = Create2DTexture({2, 2, 1}, 1, wgpu::TextureFormat::RGBA8Unorm,
|
||||
wgpu::TextureUsage::CopyDst, 4);
|
||||
|
||||
ASSERT_DEVICE_ERROR(
|
||||
TestWriteTexture(dataSize, 0, 256, 0, destination, 0, {0, 0, 0}, {2, 2, 1}));
|
||||
}
|
||||
|
||||
// Test WriteTexture with texture in error state causes errors.
|
||||
TEST_F(QueueWriteTextureValidationTest, TextureInErrorState) {
|
||||
wgpu::TextureDescriptor errorTextureDescriptor;
|
||||
errorTextureDescriptor.size.depth = 0;
|
||||
ASSERT_DEVICE_ERROR(wgpu::Texture errorTexture =
|
||||
device.CreateTexture(&errorTextureDescriptor));
|
||||
wgpu::TextureCopyView errorTextureCopyView =
|
||||
utils::CreateTextureCopyView(errorTexture, 0, {0, 0, 0});
|
||||
|
||||
wgpu::Extent3D extent3D = {1, 1, 1};
|
||||
|
||||
{
|
||||
std::vector<uint8_t> data(4);
|
||||
wgpu::TextureDataLayout textureDataLayout;
|
||||
textureDataLayout.offset = 0;
|
||||
textureDataLayout.bytesPerRow = 0;
|
||||
textureDataLayout.rowsPerImage = 0;
|
||||
|
||||
ASSERT_DEVICE_ERROR(queue.WriteTexture(&errorTextureCopyView, data.data(), 4,
|
||||
&textureDataLayout, &extent3D));
|
||||
}
|
||||
}
|
||||
|
||||
// Regression tests for a bug in the computation of texture data size in Dawn.
|
||||
TEST_F(QueueWriteTextureValidationTest, TextureWriteDataSizeLastRowComputation) {
|
||||
constexpr uint32_t kBytesPerRow = 256;
|
||||
constexpr uint32_t kWidth = 4;
|
||||
constexpr uint32_t kHeight = 4;
|
||||
|
||||
constexpr std::array<wgpu::TextureFormat, 2> kFormats = {wgpu::TextureFormat::RGBA8Unorm,
|
||||
wgpu::TextureFormat::RG8Unorm};
|
||||
|
||||
{
|
||||
// kBytesPerRow * (kHeight - 1) + kWidth is not large enough to be the valid data size
|
||||
// in this test because the data sizes in WriteTexture are not in texels but in bytes.
|
||||
constexpr uint32_t kInvalidDataSize = kBytesPerRow * (kHeight - 1) + kWidth;
|
||||
|
||||
for (wgpu::TextureFormat format : kFormats) {
|
||||
wgpu::Texture destination =
|
||||
Create2DTexture({kWidth, kHeight, 1}, 1, format, wgpu::TextureUsage::CopyDst);
|
||||
ASSERT_DEVICE_ERROR(TestWriteTexture(kInvalidDataSize, 0, kBytesPerRow, 0,
|
||||
destination, 0, {0, 0, 0},
|
||||
{kWidth, kHeight, 1}));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
for (wgpu::TextureFormat format : kFormats) {
|
||||
uint32_t validDataSize =
|
||||
RequiredBytesInCopy(kBytesPerRow, 0, {kWidth, kHeight, 1}, format);
|
||||
wgpu::Texture destination =
|
||||
Create2DTexture({kWidth, kHeight, 1}, 1, format, wgpu::TextureUsage::CopyDst);
|
||||
|
||||
// Verify the return value of RequiredBytesInCopu() is exactly the minimum valid
|
||||
// data size in this test.
|
||||
{
|
||||
uint32_t invalidDataSize = validDataSize - 1;
|
||||
ASSERT_DEVICE_ERROR(TestWriteTexture(invalidDataSize, 0, kBytesPerRow, 0,
|
||||
destination, 0, {0, 0, 0},
|
||||
{kWidth, kHeight, 1}));
|
||||
}
|
||||
|
||||
{
|
||||
TestWriteTexture(validDataSize, 0, kBytesPerRow, 0, destination, 0, {0, 0, 0},
|
||||
{kWidth, kHeight, 1});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test write from data to mip map of non square texture
|
||||
TEST_F(QueueWriteTextureValidationTest, WriteToMipmapOfNonSquareTexture) {
|
||||
uint64_t dataSize = RequiredBytesInCopy(256, 0, {4, 2, 1});
|
||||
uint32_t maxMipmapLevel = 3;
|
||||
wgpu::Texture destination =
|
||||
Create2DTexture({4, 2, 1}, maxMipmapLevel, wgpu::TextureFormat::RGBA8Unorm,
|
||||
wgpu::TextureUsage::CopyDst);
|
||||
|
||||
// Copy to top level mip map
|
||||
TestWriteTexture(dataSize, 0, 256, 0, destination, maxMipmapLevel - 1, {0, 0, 0},
|
||||
{1, 1, 1});
|
||||
// Copy to high level mip map
|
||||
TestWriteTexture(dataSize, 0, 256, 0, destination, maxMipmapLevel - 2, {0, 0, 0},
|
||||
{2, 1, 1});
|
||||
// Mip level out of range
|
||||
ASSERT_DEVICE_ERROR(TestWriteTexture(dataSize, 0, 256, 0, destination, maxMipmapLevel,
|
||||
{0, 0, 0}, {1, 1, 1}));
|
||||
// Copy origin out of range
|
||||
ASSERT_DEVICE_ERROR(TestWriteTexture(dataSize, 0, 256, 0, destination, maxMipmapLevel - 2,
|
||||
{1, 0, 0}, {2, 1, 1}));
|
||||
// Copy size out of range
|
||||
ASSERT_DEVICE_ERROR(TestWriteTexture(dataSize, 0, 256, 0, destination, maxMipmapLevel - 2,
|
||||
{0, 0, 0}, {2, 2, 1}));
|
||||
}
|
||||
|
||||
class WriteTextureTest_CompressedTextureFormats : public QueueWriteTextureValidationTest {
|
||||
public:
|
||||
WriteTextureTest_CompressedTextureFormats() : QueueWriteTextureValidationTest() {
|
||||
device = CreateDeviceFromAdapter(adapter, {"texture_compression_bc"});
|
||||
}
|
||||
|
||||
protected:
|
||||
wgpu::Texture Create2DTexture(wgpu::TextureFormat format,
|
||||
uint32_t mipmapLevels = 1,
|
||||
uint32_t width = kWidth,
|
||||
uint32_t height = kHeight) {
|
||||
constexpr wgpu::TextureUsage kUsage = wgpu::TextureUsage::CopyDst;
|
||||
constexpr uint32_t kArrayLayers = 1;
|
||||
return QueueWriteTextureValidationTest::Create2DTexture(
|
||||
{width, height, kArrayLayers}, mipmapLevels, format, kUsage, 1);
|
||||
}
|
||||
|
||||
void TestWriteTexture(size_t dataSize,
|
||||
uint32_t dataOffset,
|
||||
uint32_t dataBytesPerRow,
|
||||
uint32_t dataRowsPerImage,
|
||||
wgpu::Texture texture,
|
||||
uint32_t textLevel,
|
||||
wgpu::Origin3D textOrigin,
|
||||
wgpu::Extent3D size) {
|
||||
QueueWriteTextureValidationTest::TestWriteTexture(dataSize, dataOffset, dataBytesPerRow,
|
||||
dataRowsPerImage, texture, textLevel,
|
||||
textOrigin, size);
|
||||
}
|
||||
|
||||
static constexpr uint32_t kWidth = 16;
|
||||
static constexpr uint32_t kHeight = 16;
|
||||
};
|
||||
|
||||
// Tests to verify that data offset must be a multiple of the compressed texture blocks in bytes
|
||||
TEST_F(WriteTextureTest_CompressedTextureFormats, DataOffset) {
|
||||
for (wgpu::TextureFormat bcFormat : utils::kBCFormats) {
|
||||
wgpu::Texture texture = Create2DTexture(bcFormat);
|
||||
|
||||
// Valid usages of data offset.
|
||||
{
|
||||
uint32_t validDataOffset = utils::CompressedFormatBlockSizeInBytes(bcFormat);
|
||||
QueueWriteTextureValidationTest::TestWriteTexture(512, validDataOffset, 256, 4,
|
||||
texture, 0, {0, 0, 0}, {4, 4, 1});
|
||||
}
|
||||
|
||||
// Failures on invalid data offset.
|
||||
{
|
||||
uint32_t kInvalidDataOffset = utils::CompressedFormatBlockSizeInBytes(bcFormat) / 2;
|
||||
ASSERT_DEVICE_ERROR(TestWriteTexture(512, kInvalidDataOffset, 256, 4, texture, 0,
|
||||
{0, 0, 0}, {4, 4, 1}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tests to verify that bytesPerRow must not be less than (width / blockWidth) *
|
||||
// blockSizeInBytes and that it doesn't have to be a multiple of the compressed
|
||||
// texture block width.
|
||||
TEST_F(WriteTextureTest_CompressedTextureFormats, BytesPerRow) {
|
||||
constexpr uint32_t kTestWidth = 160;
|
||||
constexpr uint32_t kTestHeight = 160;
|
||||
|
||||
// Failures on the BytesPerRow that is not large enough.
|
||||
{
|
||||
constexpr uint32_t kSmallBytesPerRow = 256;
|
||||
for (wgpu::TextureFormat bcFormat : utils::kBCFormats) {
|
||||
wgpu::Texture texture = Create2DTexture(bcFormat, 1, kTestWidth, kTestHeight);
|
||||
ASSERT_DEVICE_ERROR(TestWriteTexture(1024, 0, kSmallBytesPerRow, 4, texture, 0,
|
||||
{0, 0, 0}, {kTestWidth, 4, 1}));
|
||||
}
|
||||
}
|
||||
|
||||
// Test it is valid to use a BytesPerRow that is not a multiple of 256.
|
||||
{
|
||||
for (wgpu::TextureFormat bcFormat : utils::kBCFormats) {
|
||||
wgpu::Texture texture = Create2DTexture(bcFormat, 1, kTestWidth, kTestHeight);
|
||||
uint32_t ValidBytesPerRow =
|
||||
kTestWidth / 4 * utils::CompressedFormatBlockSizeInBytes(bcFormat);
|
||||
ASSERT_NE(0u, ValidBytesPerRow % 256);
|
||||
TestWriteTexture(1024, 0, ValidBytesPerRow, 4, texture, 0, {0, 0, 0},
|
||||
{kTestWidth, 4, 1});
|
||||
}
|
||||
}
|
||||
|
||||
for (wgpu::TextureFormat bcFormat : utils::kBCFormats) {
|
||||
wgpu::Texture texture = Create2DTexture(bcFormat);
|
||||
|
||||
// Valid usage of bytesPerRow in WriteTexture with compressed texture formats.
|
||||
{
|
||||
constexpr uint32_t kValidBytesPerRow = 20;
|
||||
TestWriteTexture(512, 0, kValidBytesPerRow, 0, texture, 0, {0, 0, 0}, {4, 4, 1});
|
||||
}
|
||||
|
||||
// Valid bytesPerRow.
|
||||
// Note that image width is not a multiple of blockWidth.
|
||||
{
|
||||
constexpr uint32_t kValidBytesPerRow = 17;
|
||||
TestWriteTexture(512, 0, kValidBytesPerRow, 0, texture, 0, {0, 0, 0}, {4, 4, 1});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tests to verify that rowsPerImage must be a multiple of the compressed texture block height
|
||||
TEST_F(WriteTextureTest_CompressedTextureFormats, RowsPerImage) {
|
||||
for (wgpu::TextureFormat bcFormat : utils::kBCFormats) {
|
||||
wgpu::Texture texture = Create2DTexture(bcFormat);
|
||||
|
||||
// Valid usages of rowsPerImage in WriteTexture with compressed texture formats.
|
||||
{
|
||||
constexpr uint32_t kValidRowsPerImage = 8;
|
||||
TestWriteTexture(512, 0, 256, kValidRowsPerImage, texture, 0, {0, 0, 0}, {4, 4, 1});
|
||||
}
|
||||
|
||||
// 4 is the exact limit for rowsPerImage here.
|
||||
{
|
||||
constexpr uint32_t kValidRowsPerImage = 4;
|
||||
TestWriteTexture(512, 0, 256, kValidRowsPerImage, texture, 0, {0, 0, 0}, {4, 4, 1});
|
||||
}
|
||||
|
||||
// Failure on invalid rowsPerImage.
|
||||
{
|
||||
constexpr uint32_t kInvalidRowsPerImage = 2;
|
||||
ASSERT_DEVICE_ERROR(TestWriteTexture(512, 0, 256, kInvalidRowsPerImage, texture, 0,
|
||||
{0, 0, 0}, {4, 4, 1}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tests to verify that ImageOffset.x must be a multiple of the compressed texture block width
|
||||
// and ImageOffset.y must be a multiple of the compressed texture block height
|
||||
TEST_F(WriteTextureTest_CompressedTextureFormats, ImageOffset) {
|
||||
for (wgpu::TextureFormat bcFormat : utils::kBCFormats) {
|
||||
wgpu::Texture texture = Create2DTexture(bcFormat);
|
||||
wgpu::Texture texture2 = Create2DTexture(bcFormat);
|
||||
|
||||
constexpr wgpu::Origin3D kSmallestValidOrigin3D = {4, 4, 0};
|
||||
|
||||
// Valid usages of ImageOffset in WriteTexture with compressed texture formats.
|
||||
{ TestWriteTexture(512, 0, 256, 4, texture, 0, kSmallestValidOrigin3D, {4, 4, 1}); }
|
||||
|
||||
// Failures on invalid ImageOffset.x.
|
||||
{
|
||||
constexpr wgpu::Origin3D kInvalidOrigin3D = {kSmallestValidOrigin3D.x - 1,
|
||||
kSmallestValidOrigin3D.y, 0};
|
||||
ASSERT_DEVICE_ERROR(
|
||||
TestWriteTexture(512, 0, 256, 4, texture, 0, kInvalidOrigin3D, {4, 4, 1}));
|
||||
}
|
||||
|
||||
// Failures on invalid ImageOffset.y.
|
||||
{
|
||||
constexpr wgpu::Origin3D kInvalidOrigin3D = {kSmallestValidOrigin3D.x,
|
||||
kSmallestValidOrigin3D.y - 1, 0};
|
||||
ASSERT_DEVICE_ERROR(
|
||||
TestWriteTexture(512, 0, 256, 4, texture, 0, kInvalidOrigin3D, {4, 4, 1}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tests to verify that ImageExtent.x must be a multiple of the compressed texture block width
|
||||
// and ImageExtent.y must be a multiple of the compressed texture block height
|
||||
TEST_F(WriteTextureTest_CompressedTextureFormats, ImageExtent) {
|
||||
constexpr uint32_t kMipmapLevels = 3;
|
||||
constexpr uint32_t kTestWidth = 60;
|
||||
constexpr uint32_t kTestHeight = 60;
|
||||
|
||||
for (wgpu::TextureFormat bcFormat : utils::kBCFormats) {
|
||||
wgpu::Texture texture =
|
||||
Create2DTexture(bcFormat, kMipmapLevels, kTestWidth, kTestHeight);
|
||||
wgpu::Texture texture2 =
|
||||
Create2DTexture(bcFormat, kMipmapLevels, kTestWidth, kTestHeight);
|
||||
|
||||
constexpr wgpu::Extent3D kSmallestValidExtent3D = {4, 4, 1};
|
||||
|
||||
// Valid usages of ImageExtent in WriteTexture with compressed texture formats.
|
||||
{ TestWriteTexture(512, 0, 256, 8, texture, 0, {0, 0, 0}, kSmallestValidExtent3D); }
|
||||
|
||||
// Valid usages of ImageExtent in WriteTexture with compressed texture formats
|
||||
// and non-zero mipmap levels.
|
||||
{
|
||||
constexpr uint32_t kTestMipmapLevel = 2;
|
||||
constexpr wgpu::Origin3D kTestOrigin = {
|
||||
(kTestWidth >> kTestMipmapLevel) - kSmallestValidExtent3D.width + 1,
|
||||
(kTestHeight >> kTestMipmapLevel) - kSmallestValidExtent3D.height + 1, 0};
|
||||
|
||||
TestWriteTexture(512, 0, 256, 4, texture, kTestMipmapLevel, kTestOrigin,
|
||||
kSmallestValidExtent3D);
|
||||
}
|
||||
|
||||
// Failures on invalid ImageExtent.x.
|
||||
{
|
||||
constexpr wgpu::Extent3D kInValidExtent3D = {kSmallestValidExtent3D.width - 1,
|
||||
kSmallestValidExtent3D.height, 1};
|
||||
ASSERT_DEVICE_ERROR(
|
||||
TestWriteTexture(512, 0, 256, 4, texture, 0, {0, 0, 0}, kInValidExtent3D));
|
||||
}
|
||||
|
||||
// Failures on invalid ImageExtent.y.
|
||||
{
|
||||
constexpr wgpu::Extent3D kInValidExtent3D = {kSmallestValidExtent3D.width,
|
||||
kSmallestValidExtent3D.height - 1, 1};
|
||||
ASSERT_DEVICE_ERROR(
|
||||
TestWriteTexture(512, 0, 256, 4, texture, 0, {0, 0, 0}, kInValidExtent3D));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
|
@ -417,4 +417,51 @@ namespace utils {
|
|||
|
||||
return layout;
|
||||
}
|
||||
|
||||
// TODO(jiawei.shao@intel.com): support more pixel formats
|
||||
uint32_t TextureFormatPixelSize(wgpu::TextureFormat format) {
|
||||
switch (format) {
|
||||
case wgpu::TextureFormat::RG8Unorm:
|
||||
return 2;
|
||||
case wgpu::TextureFormat::RGBA8Unorm:
|
||||
return 4;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
const std::array<wgpu::TextureFormat, 14> kBCFormats = {
|
||||
wgpu::TextureFormat::BC1RGBAUnorm, wgpu::TextureFormat::BC1RGBAUnormSrgb,
|
||||
wgpu::TextureFormat::BC2RGBAUnorm, wgpu::TextureFormat::BC2RGBAUnormSrgb,
|
||||
wgpu::TextureFormat::BC3RGBAUnorm, wgpu::TextureFormat::BC3RGBAUnormSrgb,
|
||||
wgpu::TextureFormat::BC4RUnorm, wgpu::TextureFormat::BC4RSnorm,
|
||||
wgpu::TextureFormat::BC5RGUnorm, wgpu::TextureFormat::BC5RGSnorm,
|
||||
wgpu::TextureFormat::BC6HRGBUfloat, wgpu::TextureFormat::BC6HRGBSfloat,
|
||||
wgpu::TextureFormat::BC7RGBAUnorm, wgpu::TextureFormat::BC7RGBAUnormSrgb};
|
||||
|
||||
uint32_t CompressedFormatBlockSizeInBytes(wgpu::TextureFormat format) {
|
||||
switch (format) {
|
||||
case wgpu::TextureFormat::BC1RGBAUnorm:
|
||||
case wgpu::TextureFormat::BC1RGBAUnormSrgb:
|
||||
case wgpu::TextureFormat::BC4RSnorm:
|
||||
case wgpu::TextureFormat::BC4RUnorm:
|
||||
return 8;
|
||||
case wgpu::TextureFormat::BC2RGBAUnorm:
|
||||
case wgpu::TextureFormat::BC2RGBAUnormSrgb:
|
||||
case wgpu::TextureFormat::BC3RGBAUnorm:
|
||||
case wgpu::TextureFormat::BC3RGBAUnormSrgb:
|
||||
case wgpu::TextureFormat::BC5RGSnorm:
|
||||
case wgpu::TextureFormat::BC5RGUnorm:
|
||||
case wgpu::TextureFormat::BC6HRGBSfloat:
|
||||
case wgpu::TextureFormat::BC6HRGBUfloat:
|
||||
case wgpu::TextureFormat::BC7RGBAUnorm:
|
||||
case wgpu::TextureFormat::BC7RGBAUnormSrgb:
|
||||
return 16;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
|
|
|
@ -151,6 +151,10 @@ namespace utils {
|
|||
uint32_t mipmapLevel,
|
||||
uint32_t rowsPerImage);
|
||||
|
||||
uint32_t TextureFormatPixelSize(wgpu::TextureFormat format);
|
||||
extern const std::array<wgpu::TextureFormat, 14> kBCFormats;
|
||||
uint32_t CompressedFormatBlockSizeInBytes(wgpu::TextureFormat format);
|
||||
|
||||
} // namespace utils
|
||||
|
||||
#endif // UTILS_DAWNHELPERS_H_
|
||||
|
|
Loading…
Reference in New Issue