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:
parent
4b4922cdce
commit
b6d52b4ad1
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue