Refactor copy validation to add helper functions useful for T->B too.

Also fixes validation not taking the mip-level into account when
checking if the copy would fit in the texture.
This commit is contained in:
Corentin Wallez 2017-06-26 11:43:51 -04:00 committed by Corentin Wallez
parent 4b4922cdce
commit b6d52b4ad1
4 changed files with 122 additions and 96 deletions

View File

@ -29,6 +29,56 @@
namespace backend { namespace backend {
namespace {
bool ValidateCopyLocationFitsInTexture(CommandBufferBuilder* builder, const TextureCopyLocation& location) {
const TextureBase* texture = location.texture.Get();
if (location.level >= texture->GetNumMipLevels()) {
builder->HandleError("Copy mip-level out of range");
return false;
}
// All texture dimensions are in uint32_t so by doing checks in uint64_t we avoid overflows.
uint64_t level = location.level;
if (uint64_t(location.x) + uint64_t(location.width) > (static_cast<uint64_t>(texture->GetWidth()) >> level) ||
uint64_t(location.y) + uint64_t(location.height) > (static_cast<uint64_t>(texture->GetHeight()) >> level)) {
builder->HandleError("Copy would touch outside of the texture");
return false;
}
// TODO(cwallez@chromium.org): Check the depth bound differently for 2D arrays and 3D textures
if (location.z != 0 || location.depth != 1) {
builder->HandleError("No support for z != 0 and depth != 1 for now");
return false;
}
return true;
}
bool FitsInBuffer(const BufferBase* buffer, uint32_t offset, uint32_t size) {
uint32_t bufferSize = buffer->GetSize();
return offset <= bufferSize && (size <= (bufferSize - offset));
}
bool ValidateCopySizeFitsInBuffer(CommandBufferBuilder* builder, const BufferCopyLocation& location, uint32_t dataSize) {
if (!FitsInBuffer(location.buffer.Get(), location.offset, dataSize)) {
builder->HandleError("Copy would overflow the buffer");
return false;
}
return true;
}
bool ComputeTextureCopyBufferSize(CommandBufferBuilder* builder, const TextureCopyLocation& location, uint32_t* bufferSize) {
// TODO(cwallez@chromium.org): check for overflows
uint32_t pixelSize = TextureFormatPixelSize(location.texture->GetFormat());
*bufferSize = location.width * location.height * location.depth * pixelSize;
return true;
}
}
CommandBufferBase::CommandBufferBase(CommandBufferBuilder* builder) CommandBufferBase::CommandBufferBase(CommandBufferBuilder* builder)
: device(builder->device), : device(builder->device),
buffersTransitioned(std::move(builder->state->buffersTransitioned)), buffersTransitioned(std::move(builder->state->buffersTransitioned)),
@ -210,27 +260,11 @@ namespace backend {
case Command::CopyBufferToBuffer: case Command::CopyBufferToBuffer:
{ {
CopyBufferToBufferCmd* copy = iterator.NextCommand<CopyBufferToBufferCmd>(); CopyBufferToBufferCmd* copy = iterator.NextCommand<CopyBufferToBufferCmd>();
BufferBase* source = copy->source.Get(); if (!ValidateCopySizeFitsInBuffer(this, copy->source, copy->size) ||
BufferBase* destination = copy->destination.Get(); !ValidateCopySizeFitsInBuffer(this, copy->destination, copy->size) ||
uint32_t sourceOffset = copy->sourceOffset; !state->ValidateCanCopy() ||
uint32_t destinationOffset = copy->destinationOffset; !state->ValidateCanUseBufferAs(copy->source.buffer.Get(), nxt::BufferUsageBit::TransferSrc) ||
uint32_t size = copy->size; !state->ValidateCanUseBufferAs(copy->destination.buffer.Get(), nxt::BufferUsageBit::TransferDst)) {
uint64_t sourceEnd = uint64_t(sourceOffset) + uint64_t(size);
if (sourceEnd > uint64_t(source->GetSize())) {
HandleError("Copy would read after end of the source buffer");
return false;
}
uint64_t destinationEnd = uint64_t(destinationOffset) + uint64_t(size);
if (destinationEnd > uint64_t(destination->GetSize())) {
HandleError("Copy would read after end of the destination buffer");
return false;
}
if (!state->ValidateCanCopy() ||
!state->ValidateCanUseBufferAs(source, nxt::BufferUsageBit::TransferSrc) ||
!state->ValidateCanUseBufferAs(destination, nxt::BufferUsageBit::TransferDst)) {
return false; return false;
} }
} }
@ -239,36 +273,14 @@ namespace backend {
case Command::CopyBufferToTexture: case Command::CopyBufferToTexture:
{ {
CopyBufferToTextureCmd* copy = iterator.NextCommand<CopyBufferToTextureCmd>(); CopyBufferToTextureCmd* copy = iterator.NextCommand<CopyBufferToTextureCmd>();
BufferBase* buffer = copy->buffer.Get();
uint32_t bufferOffset = copy->bufferOffset;
TextureBase* texture = copy->texture.Get();
uint64_t width = copy->width;
uint64_t height = copy->height;
uint64_t depth = copy->depth;
uint64_t x = copy->x;
uint64_t y = copy->y;
uint64_t z = copy->z;
uint32_t level = copy->level;
// TODO(cwallez@chromium.org): check for overflows uint32_t bufferCopySize = 0;
uint64_t pixelSize = TextureFormatPixelSize(texture->GetFormat()); if (!ComputeTextureCopyBufferSize(this, copy->destination, &bufferCopySize) ||
uint64_t dataSize = width * height * depth * pixelSize; !ValidateCopyLocationFitsInTexture(this, copy->destination) ||
if (dataSize + static_cast<uint64_t>(bufferOffset) > static_cast<uint64_t>(buffer->GetSize())) { !ValidateCopySizeFitsInBuffer(this, copy->source, bufferCopySize) ||
HandleError("Copy would read after end of the buffer"); !state->ValidateCanCopy() ||
return false; !state->ValidateCanUseBufferAs(copy->source.buffer.Get(), nxt::BufferUsageBit::TransferSrc) ||
} !state->ValidateCanUseTextureAs(copy->destination.texture.Get(), nxt::TextureUsageBit::TransferDst)) {
if (x + width > static_cast<uint64_t>(texture->GetWidth()) ||
y + height > static_cast<uint64_t>(texture->GetHeight()) ||
z + depth > static_cast<uint64_t>(texture->GetDepth()) ||
level > texture->GetNumMipLevels()) {
HandleError("Copy would write outside of the texture");
return false;
}
if (!state->ValidateCanCopy() ||
!state->ValidateCanUseBufferAs(buffer, nxt::BufferUsageBit::TransferSrc) ||
!state->ValidateCanUseTextureAs(texture, nxt::TextureUsageBit::TransferDst)) {
return false; return false;
} }
} }
@ -424,10 +436,10 @@ namespace backend {
void CommandBufferBuilder::CopyBufferToBuffer(BufferBase* source, uint32_t sourceOffset, BufferBase* destination, uint32_t destinationOffset, uint32_t size) { void CommandBufferBuilder::CopyBufferToBuffer(BufferBase* source, uint32_t sourceOffset, BufferBase* destination, uint32_t destinationOffset, uint32_t size) {
CopyBufferToBufferCmd* copy = allocator.Allocate<CopyBufferToBufferCmd>(Command::CopyBufferToBuffer); CopyBufferToBufferCmd* copy = allocator.Allocate<CopyBufferToBufferCmd>(Command::CopyBufferToBuffer);
new(copy) CopyBufferToBufferCmd; new(copy) CopyBufferToBufferCmd;
copy->source = source; copy->source.buffer = source;
copy->sourceOffset = sourceOffset; copy->source.offset = sourceOffset;
copy->destination = destination; copy->destination.buffer = destination;
copy->destinationOffset = destinationOffset; copy->destination.offset = destinationOffset;
copy->size = size; copy->size = size;
} }
@ -436,16 +448,16 @@ namespace backend {
uint32_t width, uint32_t height, uint32_t depth, uint32_t level) { uint32_t width, uint32_t height, uint32_t depth, uint32_t level) {
CopyBufferToTextureCmd* copy = allocator.Allocate<CopyBufferToTextureCmd>(Command::CopyBufferToTexture); CopyBufferToTextureCmd* copy = allocator.Allocate<CopyBufferToTextureCmd>(Command::CopyBufferToTexture);
new(copy) CopyBufferToTextureCmd; new(copy) CopyBufferToTextureCmd;
copy->buffer = buffer; copy->source.buffer = buffer;
copy->bufferOffset = bufferOffset; copy->source.offset = bufferOffset;
copy->texture = texture; copy->destination.texture = texture;
copy->x = x; copy->destination.x = x;
copy->y = y; copy->destination.y = y;
copy->z = z; copy->destination.z = z;
copy->width = width; copy->destination.width = width;
copy->height = height; copy->destination.height = height;
copy->depth = depth; copy->destination.depth = depth;
copy->level = level; copy->destination.level = level;
} }
void CommandBufferBuilder::Dispatch(uint32_t x, uint32_t y, uint32_t z) { void CommandBufferBuilder::Dispatch(uint32_t x, uint32_t y, uint32_t z) {

View File

@ -54,23 +54,29 @@ namespace backend {
Ref<FramebufferBase> framebuffer; Ref<FramebufferBase> framebuffer;
}; };
struct CopyBufferToBufferCmd { struct BufferCopyLocation {
Ref<BufferBase> source; Ref<BufferBase> buffer;
Ref<BufferBase> destination; uint32_t offset;
uint32_t sourceOffset;
uint32_t destinationOffset;
uint32_t size;
}; };
struct CopyBufferToTextureCmd { struct TextureCopyLocation {
Ref<BufferBase> buffer;
uint32_t bufferOffset;
Ref<TextureBase> texture; Ref<TextureBase> texture;
uint32_t x, y, z; uint32_t x, y, z;
uint32_t width, height, depth; uint32_t width, height, depth;
uint32_t level; uint32_t level;
}; };
struct CopyBufferToBufferCmd {
BufferCopyLocation source;
BufferCopyLocation destination;
uint32_t size;
};
struct CopyBufferToTextureCmd {
BufferCopyLocation source;
TextureCopyLocation destination;
};
struct DispatchCmd { struct DispatchCmd {
uint32_t x; uint32_t x;
uint32_t y; uint32_t y;

View File

@ -163,13 +163,15 @@ namespace metal {
case Command::CopyBufferToBuffer: case Command::CopyBufferToBuffer:
{ {
CopyBufferToBufferCmd* copy = commands.NextCommand<CopyBufferToBufferCmd>(); CopyBufferToBufferCmd* copy = commands.NextCommand<CopyBufferToBufferCmd>();
auto& src = copy->source;
auto& dst = copy->destination;
encoders.EnsureBlit(commandBuffer); encoders.EnsureBlit(commandBuffer);
[encoders.blit [encoders.blit
copyFromBuffer:ToBackend(copy->source)->GetMTLBuffer() copyFromBuffer:ToBackend(src.buffer)->GetMTLBuffer()
sourceOffset:copy->sourceOffset sourceOffset:src.offset
toBuffer:ToBackend(copy->destination)->GetMTLBuffer() toBuffer:ToBackend(dst.buffer)->GetMTLBuffer()
destinationOffset:copy->destinationOffset destinationOffset:dst.offset
size:copy->size]; size:copy->size];
} }
break; break;
@ -177,30 +179,32 @@ namespace metal {
case Command::CopyBufferToTexture: case Command::CopyBufferToTexture:
{ {
CopyBufferToTextureCmd* copy = commands.NextCommand<CopyBufferToTextureCmd>(); CopyBufferToTextureCmd* copy = commands.NextCommand<CopyBufferToTextureCmd>();
Buffer* buffer = ToBackend(copy->buffer.Get()); auto& src = copy->source;
Texture* texture = ToBackend(copy->texture.Get()); auto& dst = copy->destination;
Buffer* buffer = ToBackend(src.buffer.Get());
Texture* texture = ToBackend(dst.texture.Get());
unsigned rowSize = copy->width * TextureFormatPixelSize(texture->GetFormat()); unsigned rowSize = dst.width * TextureFormatPixelSize(texture->GetFormat());
MTLOrigin origin; MTLOrigin origin;
origin.x = copy->x; origin.x = dst.x;
origin.y = copy->y; origin.y = dst.y;
origin.z = copy->z; origin.z = dst.z;
MTLSize size; MTLSize size;
size.width = copy->width; size.width = dst.width;
size.height = copy->height; size.height = dst.height;
size.depth = copy->depth; size.depth = dst.depth;
encoders.EnsureBlit(commandBuffer); encoders.EnsureBlit(commandBuffer);
[encoders.blit [encoders.blit
copyFromBuffer:buffer->GetMTLBuffer() copyFromBuffer:buffer->GetMTLBuffer()
sourceOffset:copy->bufferOffset sourceOffset:src.offset
sourceBytesPerRow:rowSize sourceBytesPerRow:rowSize
sourceBytesPerImage:(rowSize * copy->height) sourceBytesPerImage:(rowSize * dst.height)
sourceSize:size sourceSize:size
toTexture:texture->GetMTLTexture() toTexture:texture->GetMTLTexture()
destinationSlice:0 destinationSlice:0
destinationLevel:copy->level destinationLevel:dst.level
destinationOrigin:origin]; destinationOrigin:origin];
} }
break; break;

View File

@ -81,10 +81,12 @@ namespace opengl {
case Command::CopyBufferToBuffer: case Command::CopyBufferToBuffer:
{ {
CopyBufferToBufferCmd* copy = commands.NextCommand<CopyBufferToBufferCmd>(); CopyBufferToBufferCmd* copy = commands.NextCommand<CopyBufferToBufferCmd>();
auto& src = copy->source;
auto& dst = copy->destination;
glBindBuffer(GL_PIXEL_PACK_BUFFER, ToBackend(copy->source)->GetHandle()); glBindBuffer(GL_PIXEL_PACK_BUFFER, ToBackend(src.buffer)->GetHandle());
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, ToBackend(copy->destination)->GetHandle()); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, ToBackend(dst.buffer)->GetHandle());
glCopyBufferSubData(GL_PIXEL_PACK_BUFFER, GL_PIXEL_UNPACK_BUFFER, copy->sourceOffset, copy->destinationOffset, copy->size); glCopyBufferSubData(GL_PIXEL_PACK_BUFFER, GL_PIXEL_UNPACK_BUFFER, src.offset, dst.offset, copy->size);
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
@ -94,8 +96,10 @@ namespace opengl {
case Command::CopyBufferToTexture: case Command::CopyBufferToTexture:
{ {
CopyBufferToTextureCmd* copy = commands.NextCommand<CopyBufferToTextureCmd>(); CopyBufferToTextureCmd* copy = commands.NextCommand<CopyBufferToTextureCmd>();
Buffer* buffer = ToBackend(copy->buffer.Get()); auto& src = copy->source;
Texture* texture = ToBackend(copy->texture.Get()); auto& dst = copy->destination;
Buffer* buffer = ToBackend(src.buffer.Get());
Texture* texture = ToBackend(dst.texture.Get());
GLenum target = texture->GetGLTarget(); GLenum target = texture->GetGLTarget();
auto format = texture->GetGLFormat(); auto format = texture->GetGLFormat();
@ -103,9 +107,9 @@ namespace opengl {
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
glBindTexture(target, texture->GetHandle()); glBindTexture(target, texture->GetHandle());
glTexSubImage2D(target, copy->level, copy->x, copy->y, copy->width, copy->height, glTexSubImage2D(target, dst.level, dst.x, dst.y, dst.width, dst.height,
format.format, format.type, format.format, format.type,
reinterpret_cast<void*>(static_cast<uintptr_t>(copy->bufferOffset))); reinterpret_cast<void*>(static_cast<uintptr_t>(src.offset)));
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
} }
break; break;