Rephrasing CopyBufferToTexture validation

Moved some of the validation helper functions from CommandEncoder.cpp
to CommandValidation.cpp. This will make them accessible for
Queue::WriteTexture. Also introduced ValidateLinearTextureData
and ValidateTextureCopyRange which combine already implemented
checks in a way that's defined in WebGPU spec.

Bug: dawn:483
Change-Id: I04304c5e4906f3745c6adf75758fae179c6ffcfe
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/24283
Commit-Queue: Tomek Ponitka <tommek@google.com>
Reviewed-by: Austin Eng <enga@chromium.org>
This commit is contained in:
Tomek Ponitka 2020-07-07 10:18:51 +00:00 committed by Commit Bot service account
parent 92554d75f3
commit 0f5d496f12
4 changed files with 228 additions and 214 deletions

View File

@ -1536,6 +1536,15 @@
{"name": "origin", "type": "origin 3D"}
]
},
"texture data layout": {
"category": "structure",
"extensible": true,
"members": [
{"name": "offset", "type": "uint64_t", "default": 0},
{"name": "bytes per row", "type": "uint32_t"},
{"name": "rows per image", "type": "uint32_t", "default": 0}
]
},
"texture descriptor": {
"category": "structure",
"extensible": true,

View File

@ -38,55 +38,6 @@ namespace dawn_native {
namespace {
// TODO(jiawei.shao@intel.com): add validations on the texture-to-texture copies within the
// same texture.
MaybeError ValidateCopySizeFitsInTexture(const TextureCopyView& textureCopy,
const Extent3D& copySize) {
const TextureBase* texture = textureCopy.texture;
if (textureCopy.mipLevel >= texture->GetNumMipLevels()) {
return DAWN_VALIDATION_ERROR("Copy mipLevel out of range");
}
Extent3D mipSize = texture->GetMipLevelPhysicalSize(textureCopy.mipLevel);
// For 2D textures, include the array layer as depth so it can be checked with other
// dimensions.
ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D);
mipSize.depth = texture->GetArrayLayers();
// All texture dimensions are in uint32_t so by doing checks in uint64_t we avoid
// overflows.
if (static_cast<uint64_t>(textureCopy.origin.x) +
static_cast<uint64_t>(copySize.width) >
static_cast<uint64_t>(mipSize.width) ||
static_cast<uint64_t>(textureCopy.origin.y) +
static_cast<uint64_t>(copySize.height) >
static_cast<uint64_t>(mipSize.height) ||
static_cast<uint64_t>(textureCopy.origin.z) +
static_cast<uint64_t>(copySize.depth) >
static_cast<uint64_t>(mipSize.depth)) {
return DAWN_VALIDATION_ERROR("Copy would touch outside of the texture");
}
return {};
}
MaybeError ValidateCopySizeFitsInBuffer(const Ref<BufferBase>& buffer,
uint64_t offset,
uint64_t size) {
uint64_t bufferSize = buffer->GetSize();
bool fitsInBuffer = offset <= bufferSize && (size <= (bufferSize - offset));
if (!fitsInBuffer) {
return DAWN_VALIDATION_ERROR("Copy would overflow the buffer");
}
return {};
}
MaybeError ValidateCopySizeFitsInBuffer(const BufferCopyView& bufferCopy,
uint64_t dataSize) {
return ValidateCopySizeFitsInBuffer(bufferCopy.buffer, bufferCopy.offset, dataSize);
}
MaybeError ValidateB2BCopyAlignment(uint64_t dataSize,
uint64_t srcOffset,
uint64_t dstOffset) {
@ -104,31 +55,6 @@ namespace dawn_native {
return {};
}
MaybeError ValidateTexelBufferOffset(const BufferCopyView& bufferCopy,
const Format& format) {
if (bufferCopy.offset % format.blockByteSize != 0) {
return DAWN_VALIDATION_ERROR(
"Buffer offset must be a multiple of the texel or block size");
}
return {};
}
MaybeError ValidateRowsPerImage(const Format& format,
uint32_t rowsPerImage,
uint32_t copyHeight) {
if (rowsPerImage < copyHeight) {
return DAWN_VALIDATION_ERROR("rowsPerImage must not be less than the copy height.");
}
if (rowsPerImage % format.blockHeight != 0) {
return DAWN_VALIDATION_ERROR(
"rowsPerImage must be a multiple of compressed texture format block height");
}
return {};
}
MaybeError ValidateTextureSampleCountInCopyCommands(const TextureBase* texture) {
if (texture->GetSampleCount() > 1) {
return DAWN_VALIDATION_ERROR("The sample count of textures must be 1");
@ -193,77 +119,6 @@ namespace dawn_native {
return {};
}
MaybeError ComputeTextureCopyBufferSize(const Format& textureFormat,
const Extent3D& copySize,
uint32_t bytesPerRow,
uint32_t rowsPerImage,
uint32_t* bufferSize) {
ASSERT(rowsPerImage >= copySize.height);
if (copySize.width == 0 || copySize.height == 0 || copySize.depth == 0) {
*bufferSize = 0;
return {};
}
uint32_t blockByteSize = textureFormat.blockByteSize;
uint32_t blockWidth = textureFormat.blockWidth;
uint32_t blockHeight = textureFormat.blockHeight;
// TODO(cwallez@chromium.org): check for overflows
uint32_t slicePitch = bytesPerRow * rowsPerImage / blockWidth;
ASSERT(copySize.height >= 1);
uint32_t sliceSize = bytesPerRow * (copySize.height / blockHeight - 1) +
(copySize.width / blockWidth) * blockByteSize;
ASSERT(copySize.depth >= 1);
*bufferSize = (slicePitch * (copySize.depth - 1)) + sliceSize;
return {};
}
MaybeError ValidateBytesPerRow(const Format& format,
const Extent3D& copySize,
uint32_t bytesPerRow) {
if (bytesPerRow % kTextureBytesPerRowAlignment != 0) {
return DAWN_VALIDATION_ERROR("bytesPerRow must be a multiple of 256");
}
if (bytesPerRow < copySize.width / format.blockWidth * format.blockByteSize) {
return DAWN_VALIDATION_ERROR(
"bytesPerRow must not be less than the number of bytes per row");
}
return {};
}
MaybeError ValidateImageOrigin(const Format& format, const Origin3D& offset) {
if (offset.x % format.blockWidth != 0) {
return DAWN_VALIDATION_ERROR(
"Offset.x must be a multiple of compressed texture format block width");
}
if (offset.y % format.blockHeight != 0) {
return DAWN_VALIDATION_ERROR(
"Offset.y must be a multiple of compressed texture format block height");
}
return {};
}
MaybeError ValidateImageCopySize(const Format& format, const Extent3D& extent) {
if (extent.width % format.blockWidth != 0) {
return DAWN_VALIDATION_ERROR(
"Extent.width must be a multiple of compressed texture format block width");
}
if (extent.height % format.blockHeight != 0) {
return DAWN_VALIDATION_ERROR(
"Extent.height must be a multiple of compressed texture format block height");
}
return {};
}
MaybeError ValidateCanUseAs(const BufferBase* buffer, wgpu::BufferUsage usage) {
ASSERT(wgpu::HasZeroOrOneBits(usage));
if (!(buffer->GetUsage() & usage)) {
@ -692,10 +547,26 @@ namespace dawn_native {
DAWN_TRY_ASSIGN(fixedDest, FixTextureCopyView(GetDevice(), destination));
destination = &fixedDest;
// Validate objects before doing the defaulting.
if (GetDevice()->IsValidationEnabled()) {
DAWN_TRY(GetDevice()->ValidateObject(source->buffer));
DAWN_TRY(GetDevice()->ValidateObject(destination->texture));
DAWN_TRY(ValidateBufferCopyView(GetDevice(), *source));
DAWN_TRY(ValidateCanUseAs(source->buffer, wgpu::BufferUsage::CopySrc));
DAWN_TRY(ValidateTextureCopyView(GetDevice(), *destination));
DAWN_TRY(ValidateCanUseAs(destination->texture, wgpu::TextureUsage::CopyDst));
DAWN_TRY(ValidateTextureSampleCountInCopyCommands(destination->texture));
TextureDataLayout sourceAsTextureDataLayout;
sourceAsTextureDataLayout.offset = source->offset;
sourceAsTextureDataLayout.bytesPerRow = source->bytesPerRow;
sourceAsTextureDataLayout.rowsPerImage = source->rowsPerImage;
DAWN_TRY(ValidateLinearTextureData(sourceAsTextureDataLayout,
source->buffer->GetSize(),
destination->texture->GetFormat(), *copySize));
DAWN_TRY(ValidateTextureCopyRange(*destination, *copySize));
mTopLevelBuffers.insert(source->buffer);
mTopLevelTextures.insert(destination->texture);
}
// Compute default value for rowsPerImage
@ -704,35 +575,6 @@ namespace dawn_native {
defaultedRowsPerImage = copySize->height;
}
// Perform the rest of the validation using the default values.
if (GetDevice()->IsValidationEnabled()) {
DAWN_TRY(ValidateTextureSampleCountInCopyCommands(destination->texture));
DAWN_TRY(ValidateRowsPerImage(destination->texture->GetFormat(),
defaultedRowsPerImage, copySize->height));
DAWN_TRY(
ValidateImageOrigin(destination->texture->GetFormat(), destination->origin));
DAWN_TRY(ValidateImageCopySize(destination->texture->GetFormat(), *copySize));
uint32_t bufferCopySize = 0;
DAWN_TRY(ValidateBytesPerRow(destination->texture->GetFormat(), *copySize,
source->bytesPerRow));
DAWN_TRY(ComputeTextureCopyBufferSize(destination->texture->GetFormat(), *copySize,
source->bytesPerRow, defaultedRowsPerImage,
&bufferCopySize));
DAWN_TRY(ValidateCopySizeFitsInTexture(*destination, *copySize));
DAWN_TRY(ValidateCopySizeFitsInBuffer(*source, bufferCopySize));
DAWN_TRY(ValidateTexelBufferOffset(*source, destination->texture->GetFormat()));
DAWN_TRY(ValidateCanUseAs(source->buffer, wgpu::BufferUsage::CopySrc));
DAWN_TRY(ValidateCanUseAs(destination->texture, wgpu::TextureUsage::CopyDst));
mTopLevelBuffers.insert(source->buffer);
mTopLevelTextures.insert(destination->texture);
}
// Record the copy command.
CopyBufferToTextureCmd* copy =
allocator->Allocate<CopyBufferToTextureCmd>(Command::CopyBufferToTexture);
@ -759,10 +601,26 @@ namespace dawn_native {
DAWN_TRY_ASSIGN(fixedSrc, FixTextureCopyView(GetDevice(), source));
source = &fixedSrc;
// Validate objects before doing the defaulting.
if (GetDevice()->IsValidationEnabled()) {
DAWN_TRY(GetDevice()->ValidateObject(source->texture));
DAWN_TRY(GetDevice()->ValidateObject(destination->buffer));
DAWN_TRY(ValidateTextureCopyView(GetDevice(), *source));
DAWN_TRY(ValidateCanUseAs(source->texture, wgpu::TextureUsage::CopySrc));
DAWN_TRY(ValidateTextureSampleCountInCopyCommands(source->texture));
DAWN_TRY(ValidateBufferCopyView(GetDevice(), *destination));
DAWN_TRY(ValidateCanUseAs(destination->buffer, wgpu::BufferUsage::CopyDst));
TextureDataLayout dstAsTextureDataLayout;
dstAsTextureDataLayout.offset = destination->offset;
dstAsTextureDataLayout.bytesPerRow = destination->bytesPerRow;
dstAsTextureDataLayout.rowsPerImage = destination->rowsPerImage;
DAWN_TRY(ValidateLinearTextureData(dstAsTextureDataLayout,
destination->buffer->GetSize(),
source->texture->GetFormat(), *copySize));
DAWN_TRY(ValidateTextureCopyRange(*source, *copySize));
mTopLevelTextures.insert(source->texture);
mTopLevelBuffers.insert(destination->buffer);
}
// Compute default value for rowsPerImage
@ -771,33 +629,6 @@ namespace dawn_native {
defaultedRowsPerImage = copySize->height;
}
// Perform the rest of the validation using the default values.
if (GetDevice()->IsValidationEnabled()) {
DAWN_TRY(ValidateTextureSampleCountInCopyCommands(source->texture));
DAWN_TRY(ValidateRowsPerImage(source->texture->GetFormat(), defaultedRowsPerImage,
copySize->height));
DAWN_TRY(ValidateImageOrigin(source->texture->GetFormat(), source->origin));
DAWN_TRY(ValidateImageCopySize(source->texture->GetFormat(), *copySize));
uint32_t bufferCopySize = 0;
DAWN_TRY(ValidateBytesPerRow(source->texture->GetFormat(), *copySize,
destination->bytesPerRow));
DAWN_TRY(ComputeTextureCopyBufferSize(source->texture->GetFormat(), *copySize,
destination->bytesPerRow,
defaultedRowsPerImage, &bufferCopySize));
DAWN_TRY(ValidateCopySizeFitsInTexture(*source, *copySize));
DAWN_TRY(ValidateCopySizeFitsInBuffer(*destination, bufferCopySize));
DAWN_TRY(ValidateTexelBufferOffset(*destination, source->texture->GetFormat()));
DAWN_TRY(ValidateCanUseAs(source->texture, wgpu::TextureUsage::CopySrc));
DAWN_TRY(ValidateCanUseAs(destination->buffer, wgpu::BufferUsage::CopyDst));
mTopLevelTextures.insert(source->texture);
mTopLevelBuffers.insert(destination->buffer);
}
// Record the copy command.
CopyTextureToBufferCmd* copy =
allocator->Allocate<CopyTextureToBufferCmd>(Command::CopyTextureToBuffer);
@ -834,14 +665,11 @@ namespace dawn_native {
DAWN_TRY(
ValidateTextureToTextureCopyRestrictions(*source, *destination, *copySize));
DAWN_TRY(ValidateImageOrigin(source->texture->GetFormat(), source->origin));
DAWN_TRY(ValidateImageCopySize(source->texture->GetFormat(), *copySize));
DAWN_TRY(
ValidateImageOrigin(destination->texture->GetFormat(), destination->origin));
DAWN_TRY(ValidateImageCopySize(destination->texture->GetFormat(), *copySize));
DAWN_TRY(ValidateTextureCopyRange(*source, *copySize));
DAWN_TRY(ValidateTextureCopyRange(*destination, *copySize));
DAWN_TRY(ValidateCopySizeFitsInTexture(*source, *copySize));
DAWN_TRY(ValidateCopySizeFitsInTexture(*destination, *copySize));
DAWN_TRY(ValidateTextureCopyView(GetDevice(), *source));
DAWN_TRY(ValidateTextureCopyView(GetDevice(), *destination));
DAWN_TRY(ValidateCanUseAs(source->texture, wgpu::TextureUsage::CopySrc));
DAWN_TRY(ValidateCanUseAs(destination->texture, wgpu::TextureUsage::CopyDst));

View File

@ -19,6 +19,7 @@
#include "dawn_native/Buffer.h"
#include "dawn_native/CommandBufferStateTracker.h"
#include "dawn_native/Commands.h"
#include "dawn_native/Device.h"
#include "dawn_native/PassResourceUsage.h"
#include "dawn_native/QuerySet.h"
#include "dawn_native/RenderBundle.h"
@ -119,6 +120,36 @@ namespace dawn_native {
return {};
}
void ComputeRequiredBytesInCopy(const Format& textureFormat,
const Extent3D& copySize,
uint32_t bytesPerRow,
uint32_t rowsPerImage,
uint32_t* result) {
// Default value for rowsPerImage
if (rowsPerImage == 0) {
rowsPerImage = copySize.height;
}
ASSERT(rowsPerImage >= copySize.height);
if (copySize.width == 0 || copySize.height == 0 || copySize.depth == 0) {
*result = 0;
return;
}
uint32_t blockByteSize = textureFormat.blockByteSize;
uint32_t blockWidth = textureFormat.blockWidth;
uint32_t blockHeight = textureFormat.blockHeight;
// TODO(cwallez@chromium.org): check for overflows
uint32_t slicePitch = bytesPerRow * rowsPerImage / blockWidth;
ASSERT(copySize.height >= 1);
uint32_t sliceSize = bytesPerRow * (copySize.height / blockHeight - 1) +
(copySize.width / blockWidth) * blockByteSize;
ASSERT(copySize.depth >= 1);
*result = (slicePitch * (copySize.depth - 1)) + sliceSize;
}
} // namespace
MaybeError ValidateCanPopDebugGroup(uint64_t debugGroupStackSize) {
@ -369,4 +400,127 @@ namespace dawn_native {
static_cast<uint64_t>(maxStart);
}
MaybeError ValidateCopySizeFitsInBuffer(const Ref<BufferBase>& buffer,
uint64_t offset,
uint64_t size) {
uint64_t bufferSize = buffer->GetSize();
bool fitsInBuffer = offset <= bufferSize && (size <= (bufferSize - offset));
if (!fitsInBuffer) {
return DAWN_VALIDATION_ERROR("Copy would overflow the buffer");
}
return {};
}
MaybeError ValidateLinearTextureData(const TextureDataLayout& layout,
uint64_t byteSize,
const Format& format,
const Extent3D& copyExtent) {
// Validation for the copy being in-bounds:
if (layout.rowsPerImage != 0 && layout.rowsPerImage < copyExtent.height) {
return DAWN_VALIDATION_ERROR("rowsPerImage must not be less than the copy height.");
}
// TODO(tommek@google.com): to match the spec this should only be checked when
// copyExtent.depth > 1.
uint32_t requiredBytesInCopy = 0;
ComputeRequiredBytesInCopy(format, copyExtent, layout.bytesPerRow, layout.rowsPerImage,
&requiredBytesInCopy);
bool fitsInData =
layout.offset <= byteSize && (requiredBytesInCopy <= (byteSize - layout.offset));
if (!fitsInData) {
return DAWN_VALIDATION_ERROR(
"Required size for texture data layout exceeds the given size");
}
// Validation for the texel block alignments:
if (layout.rowsPerImage % format.blockHeight != 0) {
return DAWN_VALIDATION_ERROR(
"rowsPerImage must be a multiple of compressed texture format block height");
}
if (layout.offset % format.blockByteSize != 0) {
return DAWN_VALIDATION_ERROR("Offset must be a multiple of the texel or block size");
}
// Validation for other members in layout:
if (layout.bytesPerRow < copyExtent.width / format.blockWidth * format.blockByteSize) {
return DAWN_VALIDATION_ERROR(
"bytesPerRow must not be less than the number of bytes per row");
}
// TODO(tommek@google.com): to match the spec there should be another condition here
// on rowsPerImage >= copyExtent.height if copyExtent.depth > 1.
return {};
}
MaybeError ValidateBufferCopyView(DeviceBase const* device,
const BufferCopyView& bufferCopyView) {
DAWN_TRY(device->ValidateObject(bufferCopyView.buffer));
if (bufferCopyView.bytesPerRow % kTextureBytesPerRowAlignment != 0) {
return DAWN_VALIDATION_ERROR("bytesPerRow must be a multiple of 256");
}
return {};
}
MaybeError ValidateTextureCopyView(DeviceBase const* device,
const TextureCopyView& textureCopy) {
DAWN_TRY(device->ValidateObject(textureCopy.texture));
if (textureCopy.mipLevel >= textureCopy.texture->GetNumMipLevels()) {
return DAWN_VALIDATION_ERROR("mipLevel out of range");
}
if (textureCopy.origin.x % textureCopy.texture->GetFormat().blockWidth != 0) {
return DAWN_VALIDATION_ERROR(
"Offset.x must be a multiple of compressed texture format block width");
}
if (textureCopy.origin.y % textureCopy.texture->GetFormat().blockHeight != 0) {
return DAWN_VALIDATION_ERROR(
"Offset.y must be a multiple of compressed texture format block height");
}
return {};
}
MaybeError ValidateTextureCopyRange(const TextureCopyView& textureCopy,
const Extent3D& copySize) {
// TODO(jiawei.shao@intel.com): add validations on the texture-to-texture copies within the
// same texture.
const TextureBase* texture = textureCopy.texture;
// Validation for the copy being in-bounds:
Extent3D mipSize = texture->GetMipLevelPhysicalSize(textureCopy.mipLevel);
// For 2D textures, include the array layer as depth so it can be checked with other
// dimensions.
ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D);
mipSize.depth = texture->GetArrayLayers();
// All texture dimensions are in uint32_t so by doing checks in uint64_t we avoid
// overflows.
if (static_cast<uint64_t>(textureCopy.origin.x) + static_cast<uint64_t>(copySize.width) >
static_cast<uint64_t>(mipSize.width) ||
static_cast<uint64_t>(textureCopy.origin.y) + static_cast<uint64_t>(copySize.height) >
static_cast<uint64_t>(mipSize.height) ||
static_cast<uint64_t>(textureCopy.origin.z) + static_cast<uint64_t>(copySize.depth) >
static_cast<uint64_t>(mipSize.depth)) {
return DAWN_VALIDATION_ERROR("Touching outside of the texture");
}
// Validation for the texel block alignments:
if (copySize.width % textureCopy.texture->GetFormat().blockWidth != 0) {
return DAWN_VALIDATION_ERROR(
"copySize.width must be a multiple of compressed texture format block width");
}
if (copySize.height % textureCopy.texture->GetFormat().blockHeight != 0) {
return DAWN_VALIDATION_ERROR(
"copySize.height must be a multiple of compressed texture format block height");
}
return {};
}
} // namespace dawn_native

View File

@ -17,6 +17,7 @@
#include "dawn_native/CommandAllocator.h"
#include "dawn_native/Error.h"
#include "dawn_native/Texture.h"
#include <vector>
@ -39,6 +40,28 @@ namespace dawn_native {
MaybeError ValidateTimestampQuery(QuerySetBase* querySet, uint32_t queryIndex);
MaybeError ValidateLinearTextureData(const TextureDataLayout& layout,
uint64_t byteSize,
const Format& format,
const Extent3D& copyExtent);
MaybeError ValidateTextureCopyRange(const TextureCopyView& textureCopyView,
const Extent3D& copySize);
MaybeError ValidateBufferCopyView(DeviceBase const* device,
const BufferCopyView& bufferCopyView);
MaybeError ValidateTextureCopyView(DeviceBase const* device,
const TextureCopyView& textureCopyView);
MaybeError ValidateRowsPerImage(const Format& format,
uint32_t rowsPerImage,
uint32_t copyHeight);
MaybeError ValidateBytesPerRow(const Format& format,
const Extent3D& copySize,
uint32_t bytesPerRow);
MaybeError ValidateCopySizeFitsInBuffer(const Ref<BufferBase>& buffer,
uint64_t offset,
uint64_t size);
bool IsRangeOverlapped(uint32_t startA, uint32_t startB, uint32_t length);
} // namespace dawn_native