mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-06-06 14:43:31 +00:00
Add T->B copies.
This implements T->B copies on the Metal backend only and while it adds validation tests, end2end tests will be done in a follow-up commit.
This commit is contained in:
parent
492cbe4a43
commit
e9d347e89e
22
next.json
22
next.json
@ -295,6 +295,28 @@
|
|||||||
{"name": "image height", "type": "uint32_t"}
|
{"name": "image height", "type": "uint32_t"}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "copy texture to buffer",
|
||||||
|
"args": [
|
||||||
|
{"name": "texture", "type": "texture"},
|
||||||
|
{"name": "x", "type": "uint32_t"},
|
||||||
|
{"name": "y", "type": "uint32_t"},
|
||||||
|
{"name": "z", "type": "uint32_t"},
|
||||||
|
{"name": "width", "type": "uint32_t"},
|
||||||
|
{"name": "height", "type": "uint32_t"},
|
||||||
|
{"name": "depth", "type": "uint32_t"},
|
||||||
|
{"name": "level", "type": "uint32_t"},
|
||||||
|
{"name": "buffer", "type": "buffer"},
|
||||||
|
{"name": "buffer offset", "type": "uint32_t"}
|
||||||
|
],
|
||||||
|
"TODO": [
|
||||||
|
"Make pretty with Offset and Extents structures",
|
||||||
|
"Allow choosing the aspect (depth vs. stencil)?",
|
||||||
|
"Add these arguments too",
|
||||||
|
{"name": "row length", "type": "uint32_t"},
|
||||||
|
{"name": "image height", "type": "uint32_t"}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "dispatch",
|
"name": "dispatch",
|
||||||
"args": [
|
"args": [
|
||||||
|
@ -129,6 +129,12 @@ namespace backend {
|
|||||||
copy->~CopyBufferToTextureCmd();
|
copy->~CopyBufferToTextureCmd();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case Command::CopyTextureToBuffer:
|
||||||
|
{
|
||||||
|
CopyTextureToBufferCmd* copy = commands->NextCommand<CopyTextureToBufferCmd>();
|
||||||
|
copy->~CopyTextureToBufferCmd();
|
||||||
|
}
|
||||||
|
break;
|
||||||
case Command::Dispatch:
|
case Command::Dispatch:
|
||||||
{
|
{
|
||||||
DispatchCmd* dispatch = commands->NextCommand<DispatchCmd>();
|
DispatchCmd* dispatch = commands->NextCommand<DispatchCmd>();
|
||||||
@ -286,6 +292,22 @@ namespace backend {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case Command::CopyTextureToBuffer:
|
||||||
|
{
|
||||||
|
CopyTextureToBufferCmd* copy = iterator.NextCommand<CopyTextureToBufferCmd>();
|
||||||
|
|
||||||
|
uint32_t bufferCopySize = 0;
|
||||||
|
if (!ComputeTextureCopyBufferSize(this, copy->source, &bufferCopySize) ||
|
||||||
|
!ValidateCopyLocationFitsInTexture(this, copy->source) ||
|
||||||
|
!ValidateCopySizeFitsInBuffer(this, copy->destination, bufferCopySize) ||
|
||||||
|
!state->ValidateCanCopy() ||
|
||||||
|
!state->ValidateCanUseTextureAs(copy->source.texture.Get(), nxt::TextureUsageBit::TransferSrc) ||
|
||||||
|
!state->ValidateCanUseBufferAs(copy->destination.buffer.Get(), nxt::BufferUsageBit::TransferDst)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case Command::Dispatch:
|
case Command::Dispatch:
|
||||||
{
|
{
|
||||||
iterator.NextCommand<DispatchCmd>();
|
iterator.NextCommand<DispatchCmd>();
|
||||||
@ -460,6 +482,23 @@ namespace backend {
|
|||||||
copy->destination.level = level;
|
copy->destination.level = level;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CommandBufferBuilder::CopyTextureToBuffer(TextureBase* texture, uint32_t x, uint32_t y, uint32_t z,
|
||||||
|
uint32_t width, uint32_t height, uint32_t depth, uint32_t level,
|
||||||
|
BufferBase* buffer, uint32_t bufferOffset) {
|
||||||
|
CopyTextureToBufferCmd* copy = allocator.Allocate<CopyTextureToBufferCmd>(Command::CopyTextureToBuffer);
|
||||||
|
new(copy) CopyTextureToBufferCmd;
|
||||||
|
copy->source.texture = texture;
|
||||||
|
copy->source.x = x;
|
||||||
|
copy->source.y = y;
|
||||||
|
copy->source.z = z;
|
||||||
|
copy->source.width = width;
|
||||||
|
copy->source.height = height;
|
||||||
|
copy->source.depth = depth;
|
||||||
|
copy->source.level = level;
|
||||||
|
copy->destination.buffer = buffer;
|
||||||
|
copy->destination.offset = bufferOffset;
|
||||||
|
}
|
||||||
|
|
||||||
void CommandBufferBuilder::Dispatch(uint32_t x, uint32_t y, uint32_t z) {
|
void CommandBufferBuilder::Dispatch(uint32_t x, uint32_t y, uint32_t z) {
|
||||||
DispatchCmd* dispatch = allocator.Allocate<DispatchCmd>(Command::Dispatch);
|
DispatchCmd* dispatch = allocator.Allocate<DispatchCmd>(Command::Dispatch);
|
||||||
new(dispatch) DispatchCmd;
|
new(dispatch) DispatchCmd;
|
||||||
|
@ -65,6 +65,9 @@ namespace backend {
|
|||||||
void CopyBufferToTexture(BufferBase* buffer, uint32_t bufferOffset,
|
void CopyBufferToTexture(BufferBase* buffer, uint32_t bufferOffset,
|
||||||
TextureBase* texture, uint32_t x, uint32_t y, uint32_t z,
|
TextureBase* texture, uint32_t x, uint32_t y, uint32_t z,
|
||||||
uint32_t width, uint32_t height, uint32_t depth, uint32_t level);
|
uint32_t width, uint32_t height, uint32_t depth, uint32_t level);
|
||||||
|
void CopyTextureToBuffer(TextureBase* texture, uint32_t x, uint32_t y, uint32_t z,
|
||||||
|
uint32_t width, uint32_t height, uint32_t depth, uint32_t level,
|
||||||
|
BufferBase* buffer, uint32_t bufferOffset);
|
||||||
void Dispatch(uint32_t x, uint32_t y, uint32_t z);
|
void Dispatch(uint32_t x, uint32_t y, uint32_t z);
|
||||||
void DrawArrays(uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance);
|
void DrawArrays(uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance);
|
||||||
void DrawElements(uint32_t vertexCount, uint32_t instanceCount, uint32_t firstIndex, uint32_t firstInstance);
|
void DrawElements(uint32_t vertexCount, uint32_t instanceCount, uint32_t firstIndex, uint32_t firstInstance);
|
||||||
|
@ -32,6 +32,7 @@ namespace backend {
|
|||||||
BeginRenderPass,
|
BeginRenderPass,
|
||||||
CopyBufferToBuffer,
|
CopyBufferToBuffer,
|
||||||
CopyBufferToTexture,
|
CopyBufferToTexture,
|
||||||
|
CopyTextureToBuffer,
|
||||||
Dispatch,
|
Dispatch,
|
||||||
DrawArrays,
|
DrawArrays,
|
||||||
DrawElements,
|
DrawElements,
|
||||||
@ -77,6 +78,11 @@ namespace backend {
|
|||||||
TextureCopyLocation destination;
|
TextureCopyLocation destination;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct CopyTextureToBufferCmd {
|
||||||
|
TextureCopyLocation source;
|
||||||
|
BufferCopyLocation destination;
|
||||||
|
};
|
||||||
|
|
||||||
struct DispatchCmd {
|
struct DispatchCmd {
|
||||||
uint32_t x;
|
uint32_t x;
|
||||||
uint32_t y;
|
uint32_t y;
|
||||||
|
@ -73,6 +73,7 @@ namespace d3d12 {
|
|||||||
commandList->OMSetRenderTargets(1, &device->GetCurrentRenderTargetDescriptor(), FALSE, nullptr);
|
commandList->OMSetRenderTargets(1, &device->GetCurrentRenderTargetDescriptor(), FALSE, nullptr);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Command::CopyBufferToBuffer:
|
case Command::CopyBufferToBuffer:
|
||||||
{
|
{
|
||||||
CopyBufferToBufferCmd* copy = commands.NextCommand<CopyBufferToBufferCmd>();
|
CopyBufferToBufferCmd* copy = commands.NextCommand<CopyBufferToBufferCmd>();
|
||||||
@ -85,6 +86,12 @@ namespace d3d12 {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case Command::CopyTextureToBuffer:
|
||||||
|
{
|
||||||
|
CopyTextureToBufferCmd* copy = commands.NextCommand<CopyTextureToBufferCmd>();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case Command::Dispatch:
|
case Command::Dispatch:
|
||||||
{
|
{
|
||||||
DispatchCmd* dispatch = commands.NextCommand<DispatchCmd>();
|
DispatchCmd* dispatch = commands.NextCommand<DispatchCmd>();
|
||||||
|
@ -209,6 +209,39 @@ namespace metal {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case Command::CopyTextureToBuffer:
|
||||||
|
{
|
||||||
|
CopyTextureToBufferCmd* copy = commands.NextCommand<CopyTextureToBufferCmd>();
|
||||||
|
auto& src = copy->source;
|
||||||
|
auto& dst = copy->destination;
|
||||||
|
Texture* texture = ToBackend(src.texture.Get());
|
||||||
|
Buffer* buffer = ToBackend(dst.buffer.Get());
|
||||||
|
|
||||||
|
unsigned rowSize = src.width * TextureFormatPixelSize(texture->GetFormat());
|
||||||
|
MTLOrigin origin;
|
||||||
|
origin.x = src.x;
|
||||||
|
origin.y = src.y;
|
||||||
|
origin.z = src.z;
|
||||||
|
|
||||||
|
MTLSize size;
|
||||||
|
size.width = src.width;
|
||||||
|
size.height = src.height;
|
||||||
|
size.depth = src.depth;
|
||||||
|
|
||||||
|
encoders.EnsureBlit(commandBuffer);
|
||||||
|
[encoders.blit
|
||||||
|
copyFromTexture:texture->GetMTLTexture()
|
||||||
|
sourceSlice:0
|
||||||
|
sourceLevel:src.level
|
||||||
|
sourceOrigin:origin
|
||||||
|
sourceSize:size
|
||||||
|
toBuffer:buffer->GetMTLBuffer()
|
||||||
|
destinationOffset:dst.offset
|
||||||
|
destinationBytesPerRow:rowSize
|
||||||
|
destinationBytesPerImage:rowSize * src.height];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case Command::Dispatch:
|
case Command::Dispatch:
|
||||||
{
|
{
|
||||||
DispatchCmd* dispatch = commands.NextCommand<DispatchCmd>();
|
DispatchCmd* dispatch = commands.NextCommand<DispatchCmd>();
|
||||||
|
@ -114,6 +114,13 @@ namespace opengl {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case Command::CopyTextureToBuffer:
|
||||||
|
{
|
||||||
|
CopyTextureToBufferCmd* copy = commands.NextCommand<CopyTextureToBufferCmd>();
|
||||||
|
// TODO(cwallez@chromium.org): implement using a temporary FBO and ReadPixels
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case Command::Dispatch:
|
case Command::Dispatch:
|
||||||
{
|
{
|
||||||
DispatchCmd* dispatch = commands.NextCommand<DispatchCmd>();
|
DispatchCmd* dispatch = commands.NextCommand<DispatchCmd>();
|
||||||
|
@ -245,3 +245,140 @@ TEST_F(CopyCommandTest_B2T, IncorrectUsage) {
|
|||||||
.GetResult();
|
.GetResult();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CopyCommandTest_T2B : public CopyCommandTest {
|
||||||
|
};
|
||||||
|
|
||||||
|
// Test a successfull T2B copy
|
||||||
|
TEST_F(CopyCommandTest_T2B, Success) {
|
||||||
|
nxt::Texture source = CreateFrozen2DTexture(16, 16, 5, nxt::TextureFormat::R8G8B8A8Unorm,
|
||||||
|
nxt::TextureUsageBit::TransferSrc);
|
||||||
|
nxt::Buffer destination = CreateFrozenBuffer(16 * 4, nxt::BufferUsageBit::TransferDst);
|
||||||
|
|
||||||
|
// Different copies, including some that touch the OOB condition
|
||||||
|
{
|
||||||
|
nxt::CommandBuffer commands = AssertWillBeSuccess(device.CreateCommandBufferBuilder())
|
||||||
|
// Copy from 4x4 block in corner of first mip.
|
||||||
|
.CopyTextureToBuffer(source, 0, 0, 0, 4, 4, 1, 0, destination, 0)
|
||||||
|
// Copy from 4x4 block in opposite corner of first mip.
|
||||||
|
.CopyTextureToBuffer(source, 12, 12, 0, 4, 4, 1, 0, destination, 0)
|
||||||
|
// Copy from 4x4 block in the 4x4 mip.
|
||||||
|
.CopyTextureToBuffer(source, 0, 0, 0, 4, 4, 1, 2, destination, 0)
|
||||||
|
// Copy with a buffer offset
|
||||||
|
.CopyTextureToBuffer(source, 0, 0, 0, 1, 1, 1, 4, destination, 15 * 4)
|
||||||
|
.GetResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Empty copies are valid
|
||||||
|
{
|
||||||
|
nxt::CommandBuffer commands = AssertWillBeSuccess(device.CreateCommandBufferBuilder())
|
||||||
|
// An empty copy
|
||||||
|
.CopyTextureToBuffer(source, 0, 0, 0, 0, 0, 1, 0, destination, 0)
|
||||||
|
// An empty copy touching the end of the buffer
|
||||||
|
.CopyTextureToBuffer(source, 0, 0, 0, 0, 0, 1, 0, destination, 16 * 4)
|
||||||
|
// An empty copy touching the side of the texture
|
||||||
|
.CopyTextureToBuffer(source, 16, 16, 0, 0, 0, 1, 0, destination, 0)
|
||||||
|
.GetResult();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test OOB conditions on the texture
|
||||||
|
TEST_F(CopyCommandTest_T2B, OutOfBoundsOnTexture) {
|
||||||
|
nxt::Texture source = CreateFrozen2DTexture(16, 16, 5, nxt::TextureFormat::R8G8B8A8Unorm,
|
||||||
|
nxt::TextureUsageBit::TransferSrc);
|
||||||
|
nxt::Buffer destination = CreateFrozenBuffer(16 * 4, nxt::BufferUsageBit::TransferDst);
|
||||||
|
|
||||||
|
// OOB on the texture because x + width overflows
|
||||||
|
{
|
||||||
|
nxt::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
|
||||||
|
.CopyTextureToBuffer(source, 13, 12, 0, 4, 4, 1, 0, destination, 0)
|
||||||
|
.GetResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
// OOB on the texture because y + width overflows
|
||||||
|
{
|
||||||
|
nxt::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
|
||||||
|
.CopyTextureToBuffer(source, 12, 13, 0, 4, 4, 1, 0, destination, 0)
|
||||||
|
.GetResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
// OOB on the texture because we overflow a non-zero mip
|
||||||
|
{
|
||||||
|
nxt::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
|
||||||
|
.CopyTextureToBuffer(source, 1, 0, 0, 4, 4, 1, 2, destination, 0)
|
||||||
|
.GetResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
// OOB on the texture even on an empty copy when we copy from a non-existent mip.
|
||||||
|
{
|
||||||
|
nxt::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
|
||||||
|
.CopyTextureToBuffer(source, 0, 0, 0, 0, 0, 1, 5, destination, 0)
|
||||||
|
.GetResult();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test OOB conditions on the buffer
|
||||||
|
TEST_F(CopyCommandTest_T2B, OutOfBoundsOnBuffer) {
|
||||||
|
nxt::Texture source = CreateFrozen2DTexture(16, 16, 5, nxt::TextureFormat::R8G8B8A8Unorm,
|
||||||
|
nxt::TextureUsageBit::TransferSrc);
|
||||||
|
nxt::Buffer destination = CreateFrozenBuffer(16 * 4, nxt::BufferUsageBit::TransferDst);
|
||||||
|
|
||||||
|
// OOB on the buffer because we copy too many pixels
|
||||||
|
{
|
||||||
|
nxt::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
|
||||||
|
.CopyTextureToBuffer(source, 0, 0, 0, 4, 5, 1, 0, destination, 0)
|
||||||
|
.GetResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
// OOB on the buffer because of the offset
|
||||||
|
{
|
||||||
|
nxt::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
|
||||||
|
.CopyTextureToBuffer(source, 0, 0, 0, 4, 4, 1, 0, destination, 1)
|
||||||
|
.GetResult();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that we force Z=0 and Depth=1 on copies from to 2D textures
|
||||||
|
TEST_F(CopyCommandTest_T2B, ZDepthConstraintFor2DTextures) {
|
||||||
|
nxt::Texture source = CreateFrozen2DTexture(16, 16, 5, nxt::TextureFormat::R8G8B8A8Unorm,
|
||||||
|
nxt::TextureUsageBit::TransferSrc);
|
||||||
|
nxt::Buffer destination = CreateFrozenBuffer(16 * 4, nxt::BufferUsageBit::TransferDst);
|
||||||
|
|
||||||
|
// Z=1 on an empty copy still errors
|
||||||
|
{
|
||||||
|
nxt::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
|
||||||
|
.CopyTextureToBuffer(source, 0, 0, 1, 0, 0, 1, 0, destination, 0)
|
||||||
|
.GetResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Depth=0 on an empty copy still errors
|
||||||
|
{
|
||||||
|
nxt::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
|
||||||
|
.CopyTextureToBuffer(source, 0, 0, 0, 0, 0, 0, 0, destination, 0)
|
||||||
|
.GetResult();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test B2B copies with incorrect buffer usage
|
||||||
|
TEST_F(CopyCommandTest_T2B, IncorrectUsage) {
|
||||||
|
nxt::Texture source = CreateFrozen2DTexture(16, 16, 5, nxt::TextureFormat::R8G8B8A8Unorm,
|
||||||
|
nxt::TextureUsageBit::TransferSrc);
|
||||||
|
nxt::Texture sampled = CreateFrozen2DTexture(16, 16, 5, nxt::TextureFormat::R8G8B8A8Unorm,
|
||||||
|
nxt::TextureUsageBit::Sampled);
|
||||||
|
nxt::Buffer destination = CreateFrozenBuffer(16 * 4, nxt::BufferUsageBit::TransferDst);
|
||||||
|
nxt::Buffer vertex = CreateFrozenBuffer(16 * 4, nxt::BufferUsageBit::Vertex);
|
||||||
|
|
||||||
|
// Incorrect source usage
|
||||||
|
{
|
||||||
|
nxt::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
|
||||||
|
.CopyTextureToBuffer(sampled, 0, 0, 0, 4, 4, 1, 0, destination, 0)
|
||||||
|
.GetResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Incorrect destination usage
|
||||||
|
{
|
||||||
|
nxt::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
|
||||||
|
.CopyTextureToBuffer(source, 0, 0, 0, 4, 4, 1, 0, vertex, 0)
|
||||||
|
.GetResult();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user