Introduce WriteBuffer command
This command copies data from host memory into a GPU buffer. It's analogous to Queue::WriteBuffer, but executed in the context of a command buffer, sequenced with other encoded commands. This is useful for supporting a notion of a shared scratch buffer, with a single allocation whose contents may need to be overwritten with new data before each pass that uses it. Bug: dawn:809 Change-Id: If58d49c52a41127e2980dd626fd687eb1c91fe28 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/64001 Reviewed-by: Austin Eng <enga@chromium.org> Reviewed-by: Corentin Wallez <cwallez@chromium.org> Commit-Queue: Ken Rockot <rockot@google.com>
This commit is contained in:
parent
1a965ab7f8
commit
6237c8a121
|
@ -522,6 +522,15 @@
|
||||||
{"name": "destination offset", "type": "uint64_t"}
|
{"name": "destination offset", "type": "uint64_t"}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "write buffer",
|
||||||
|
"args": [
|
||||||
|
{"name": "buffer", "type": "buffer"},
|
||||||
|
{"name": "buffer offset", "type": "uint64_t"},
|
||||||
|
{"name": "data", "type": "uint8_t", "annotation": "const*", "length": "size"},
|
||||||
|
{"name": "size", "type": "uint64_t"}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "write timestamp",
|
"name": "write timestamp",
|
||||||
"args": [
|
"args": [
|
||||||
|
|
|
@ -873,6 +873,29 @@ namespace dawn_native {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CommandEncoder::APIWriteBuffer(BufferBase* buffer,
|
||||||
|
uint64_t bufferOffset,
|
||||||
|
const uint8_t* data,
|
||||||
|
uint64_t size) {
|
||||||
|
mEncodingContext.TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError {
|
||||||
|
if (GetDevice()->IsValidationEnabled()) {
|
||||||
|
DAWN_TRY(ValidateWriteBuffer(GetDevice(), buffer, bufferOffset, size));
|
||||||
|
}
|
||||||
|
|
||||||
|
WriteBufferCmd* cmd = allocator->Allocate<WriteBufferCmd>(Command::WriteBuffer);
|
||||||
|
cmd->buffer = buffer;
|
||||||
|
cmd->offset = bufferOffset;
|
||||||
|
cmd->size = size;
|
||||||
|
|
||||||
|
uint8_t* inlinedData = allocator->AllocateData<uint8_t>(size);
|
||||||
|
memcpy(inlinedData, data, size);
|
||||||
|
|
||||||
|
mTopLevelBuffers.insert(buffer);
|
||||||
|
|
||||||
|
return {};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void CommandEncoder::APIWriteTimestamp(QuerySetBase* querySet, uint32_t queryIndex) {
|
void CommandEncoder::APIWriteTimestamp(QuerySetBase* querySet, uint32_t queryIndex) {
|
||||||
mEncodingContext.TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError {
|
mEncodingContext.TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError {
|
||||||
if (GetDevice()->IsValidationEnabled()) {
|
if (GetDevice()->IsValidationEnabled()) {
|
||||||
|
|
|
@ -68,6 +68,10 @@ namespace dawn_native {
|
||||||
uint32_t queryCount,
|
uint32_t queryCount,
|
||||||
BufferBase* destination,
|
BufferBase* destination,
|
||||||
uint64_t destinationOffset);
|
uint64_t destinationOffset);
|
||||||
|
void APIWriteBuffer(BufferBase* buffer,
|
||||||
|
uint64_t bufferOffset,
|
||||||
|
const uint8_t* data,
|
||||||
|
uint64_t size);
|
||||||
void APIWriteTimestamp(QuerySetBase* querySet, uint32_t queryIndex);
|
void APIWriteTimestamp(QuerySetBase* querySet, uint32_t queryIndex);
|
||||||
|
|
||||||
CommandBufferBase* APIFinish(const CommandBufferDescriptor* descriptor = nullptr);
|
CommandBufferBase* APIFinish(const CommandBufferDescriptor* descriptor = nullptr);
|
||||||
|
|
|
@ -72,6 +72,31 @@ namespace dawn_native {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MaybeError ValidateWriteBuffer(const DeviceBase* device,
|
||||||
|
const BufferBase* buffer,
|
||||||
|
uint64_t bufferOffset,
|
||||||
|
uint64_t size) {
|
||||||
|
DAWN_TRY(device->ValidateObject(buffer));
|
||||||
|
|
||||||
|
if (bufferOffset % 4 != 0) {
|
||||||
|
return DAWN_VALIDATION_ERROR("WriteBuffer bufferOffset must be a multiple of 4");
|
||||||
|
}
|
||||||
|
if (size % 4 != 0) {
|
||||||
|
return DAWN_VALIDATION_ERROR("WriteBuffer size must be a multiple of 4");
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t bufferSize = buffer->GetSize();
|
||||||
|
if (bufferOffset > bufferSize || size > (bufferSize - bufferOffset)) {
|
||||||
|
return DAWN_VALIDATION_ERROR("WriteBuffer out of range");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(buffer->GetUsage() & wgpu::BufferUsage::CopyDst)) {
|
||||||
|
return DAWN_VALIDATION_ERROR("Buffer needs the CopyDst usage bit");
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
bool IsRangeOverlapped(uint32_t startA, uint32_t startB, uint32_t length) {
|
bool IsRangeOverlapped(uint32_t startA, uint32_t startB, uint32_t length) {
|
||||||
uint32_t maxStart = std::max(startA, startB);
|
uint32_t maxStart = std::max(startA, startB);
|
||||||
uint32_t minStart = std::min(startA, startB);
|
uint32_t minStart = std::min(startA, startB);
|
||||||
|
|
|
@ -31,6 +31,11 @@ namespace dawn_native {
|
||||||
|
|
||||||
MaybeError ValidateTimestampQuery(QuerySetBase* querySet, uint32_t queryIndex);
|
MaybeError ValidateTimestampQuery(QuerySetBase* querySet, uint32_t queryIndex);
|
||||||
|
|
||||||
|
MaybeError ValidateWriteBuffer(const DeviceBase* device,
|
||||||
|
const BufferBase* buffer,
|
||||||
|
uint64_t bufferOffset,
|
||||||
|
uint64_t size);
|
||||||
|
|
||||||
ResultOrError<uint64_t> ComputeRequiredBytesInCopy(const TexelBlockInfo& blockInfo,
|
ResultOrError<uint64_t> ComputeRequiredBytesInCopy(const TexelBlockInfo& blockInfo,
|
||||||
const Extent3D& copySize,
|
const Extent3D& copySize,
|
||||||
uint32_t bytesPerRow,
|
uint32_t bytesPerRow,
|
||||||
|
|
|
@ -191,6 +191,12 @@ namespace dawn_native {
|
||||||
cmd->~SetVertexBufferCmd();
|
cmd->~SetVertexBufferCmd();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case Command::WriteBuffer: {
|
||||||
|
WriteBufferCmd* write = commands->NextCommand<WriteBufferCmd>();
|
||||||
|
commands->NextData<uint8_t>(write->size);
|
||||||
|
write->~WriteBufferCmd();
|
||||||
|
break;
|
||||||
|
}
|
||||||
case Command::WriteTimestamp: {
|
case Command::WriteTimestamp: {
|
||||||
WriteTimestampCmd* cmd = commands->NextCommand<WriteTimestampCmd>();
|
WriteTimestampCmd* cmd = commands->NextCommand<WriteTimestampCmd>();
|
||||||
cmd->~WriteTimestampCmd();
|
cmd->~WriteTimestampCmd();
|
||||||
|
@ -336,6 +342,10 @@ namespace dawn_native {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case Command::WriteBuffer:
|
||||||
|
commands->NextCommand<WriteBufferCmd>();
|
||||||
|
break;
|
||||||
|
|
||||||
case Command::WriteTimestamp: {
|
case Command::WriteTimestamp: {
|
||||||
commands->NextCommand<WriteTimestampCmd>();
|
commands->NextCommand<WriteTimestampCmd>();
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -63,6 +63,7 @@ namespace dawn_native {
|
||||||
SetBindGroup,
|
SetBindGroup,
|
||||||
SetIndexBuffer,
|
SetIndexBuffer,
|
||||||
SetVertexBuffer,
|
SetVertexBuffer,
|
||||||
|
WriteBuffer,
|
||||||
WriteTimestamp,
|
WriteTimestamp,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -255,6 +256,12 @@ namespace dawn_native {
|
||||||
uint64_t size;
|
uint64_t size;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct WriteBufferCmd {
|
||||||
|
Ref<BufferBase> buffer;
|
||||||
|
uint64_t offset;
|
||||||
|
uint64_t size;
|
||||||
|
};
|
||||||
|
|
||||||
struct WriteTimestampCmd {
|
struct WriteTimestampCmd {
|
||||||
Ref<QuerySetBase> querySet;
|
Ref<QuerySetBase> querySet;
|
||||||
uint32_t queryIndex;
|
uint32_t queryIndex;
|
||||||
|
|
|
@ -244,7 +244,10 @@ namespace dawn_native {
|
||||||
uint64_t bufferOffset,
|
uint64_t bufferOffset,
|
||||||
const void* data,
|
const void* data,
|
||||||
size_t size) {
|
size_t size) {
|
||||||
DAWN_TRY(ValidateWriteBuffer(buffer, bufferOffset, size));
|
DAWN_TRY(GetDevice()->ValidateIsAlive());
|
||||||
|
DAWN_TRY(GetDevice()->ValidateObject(this));
|
||||||
|
DAWN_TRY(ValidateWriteBuffer(GetDevice(), buffer, bufferOffset, size));
|
||||||
|
DAWN_TRY(buffer->ValidateCanUseOnQueueNow());
|
||||||
return WriteBufferImpl(buffer, bufferOffset, data, size);
|
return WriteBufferImpl(buffer, bufferOffset, data, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -430,34 +433,6 @@ namespace dawn_native {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
MaybeError QueueBase::ValidateWriteBuffer(const BufferBase* buffer,
|
|
||||||
uint64_t bufferOffset,
|
|
||||||
size_t size) const {
|
|
||||||
DAWN_TRY(GetDevice()->ValidateIsAlive());
|
|
||||||
DAWN_TRY(GetDevice()->ValidateObject(this));
|
|
||||||
DAWN_TRY(GetDevice()->ValidateObject(buffer));
|
|
||||||
|
|
||||||
if (bufferOffset % 4 != 0) {
|
|
||||||
return DAWN_VALIDATION_ERROR("Queue::WriteBuffer bufferOffset must be a multiple of 4");
|
|
||||||
}
|
|
||||||
if (size % 4 != 0) {
|
|
||||||
return DAWN_VALIDATION_ERROR("Queue::WriteBuffer size must be a multiple of 4");
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t bufferSize = buffer->GetSize();
|
|
||||||
if (bufferOffset > bufferSize || size > (bufferSize - bufferOffset)) {
|
|
||||||
return DAWN_VALIDATION_ERROR("Queue::WriteBuffer out of range");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(buffer->GetUsage() & wgpu::BufferUsage::CopyDst)) {
|
|
||||||
return DAWN_VALIDATION_ERROR("Buffer needs the CopyDst usage bit");
|
|
||||||
}
|
|
||||||
|
|
||||||
DAWN_TRY(buffer->ValidateCanUseOnQueueNow());
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
MaybeError QueueBase::ValidateWriteTexture(const ImageCopyTexture* destination,
|
MaybeError QueueBase::ValidateWriteTexture(const ImageCopyTexture* destination,
|
||||||
size_t dataSize,
|
size_t dataSize,
|
||||||
const TextureDataLayout& dataLayout,
|
const TextureDataLayout& dataLayout,
|
||||||
|
|
|
@ -92,9 +92,6 @@ namespace dawn_native {
|
||||||
MaybeError ValidateSubmit(uint32_t commandCount, CommandBufferBase* const* commands) const;
|
MaybeError ValidateSubmit(uint32_t commandCount, CommandBufferBase* const* commands) const;
|
||||||
MaybeError ValidateOnSubmittedWorkDone(uint64_t signalValue,
|
MaybeError ValidateOnSubmittedWorkDone(uint64_t signalValue,
|
||||||
WGPUQueueWorkDoneStatus* status) const;
|
WGPUQueueWorkDoneStatus* status) const;
|
||||||
MaybeError ValidateWriteBuffer(const BufferBase* buffer,
|
|
||||||
uint64_t bufferOffset,
|
|
||||||
size_t size) const;
|
|
||||||
MaybeError ValidateWriteTexture(const ImageCopyTexture* destination,
|
MaybeError ValidateWriteTexture(const ImageCopyTexture* destination,
|
||||||
size_t dataSize,
|
size_t dataSize,
|
||||||
const TextureDataLayout& dataLayout,
|
const TextureDataLayout& dataLayout,
|
||||||
|
|
|
@ -981,6 +981,36 @@ namespace dawn_native { namespace d3d12 {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case Command::WriteBuffer: {
|
||||||
|
WriteBufferCmd* write = mCommands.NextCommand<WriteBufferCmd>();
|
||||||
|
const uint64_t offset = write->offset;
|
||||||
|
const uint64_t size = write->size;
|
||||||
|
if (size == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Buffer* dstBuffer = ToBackend(write->buffer.Get());
|
||||||
|
uint8_t* data = mCommands.NextData<uint8_t>(size);
|
||||||
|
Device* device = ToBackend(GetDevice());
|
||||||
|
|
||||||
|
UploadHandle uploadHandle;
|
||||||
|
DAWN_TRY_ASSIGN(uploadHandle, device->GetDynamicUploader()->Allocate(
|
||||||
|
size, device->GetPendingCommandSerial(),
|
||||||
|
kCopyBufferToBufferOffsetAlignment));
|
||||||
|
ASSERT(uploadHandle.mappedBuffer != nullptr);
|
||||||
|
memcpy(uploadHandle.mappedBuffer, data, size);
|
||||||
|
|
||||||
|
DAWN_TRY(dstBuffer->EnsureDataInitializedAsDestination(commandContext, offset,
|
||||||
|
size));
|
||||||
|
dstBuffer->TrackUsageAndTransitionNow(commandContext,
|
||||||
|
wgpu::BufferUsage::CopyDst);
|
||||||
|
commandList->CopyBufferRegion(
|
||||||
|
dstBuffer->GetD3D12Resource(), offset,
|
||||||
|
ToBackend(uploadHandle.stagingBuffer)->GetResource(),
|
||||||
|
uploadHandle.startOffset, size);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include "dawn_native/BindGroupTracker.h"
|
#include "dawn_native/BindGroupTracker.h"
|
||||||
#include "dawn_native/CommandEncoder.h"
|
#include "dawn_native/CommandEncoder.h"
|
||||||
#include "dawn_native/Commands.h"
|
#include "dawn_native/Commands.h"
|
||||||
|
#include "dawn_native/DynamicUploader.h"
|
||||||
#include "dawn_native/ExternalTexture.h"
|
#include "dawn_native/ExternalTexture.h"
|
||||||
#include "dawn_native/RenderBundle.h"
|
#include "dawn_native/RenderBundle.h"
|
||||||
#include "dawn_native/metal/BindGroupMTL.h"
|
#include "dawn_native/metal/BindGroupMTL.h"
|
||||||
|
@ -27,6 +28,7 @@
|
||||||
#include "dawn_native/metal/QuerySetMTL.h"
|
#include "dawn_native/metal/QuerySetMTL.h"
|
||||||
#include "dawn_native/metal/RenderPipelineMTL.h"
|
#include "dawn_native/metal/RenderPipelineMTL.h"
|
||||||
#include "dawn_native/metal/SamplerMTL.h"
|
#include "dawn_native/metal/SamplerMTL.h"
|
||||||
|
#include "dawn_native/metal/StagingBufferMTL.h"
|
||||||
#include "dawn_native/metal/TextureMTL.h"
|
#include "dawn_native/metal/TextureMTL.h"
|
||||||
#include "dawn_native/metal/UtilsMetal.h"
|
#include "dawn_native/metal/UtilsMetal.h"
|
||||||
|
|
||||||
|
@ -985,6 +987,36 @@ namespace dawn_native { namespace metal {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case Command::WriteBuffer: {
|
||||||
|
WriteBufferCmd* write = mCommands.NextCommand<WriteBufferCmd>();
|
||||||
|
const uint64_t offset = write->offset;
|
||||||
|
const uint64_t size = write->size;
|
||||||
|
if (size == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Buffer* dstBuffer = ToBackend(write->buffer.Get());
|
||||||
|
uint8_t* data = mCommands.NextData<uint8_t>(size);
|
||||||
|
Device* device = ToBackend(GetDevice());
|
||||||
|
|
||||||
|
UploadHandle uploadHandle;
|
||||||
|
DAWN_TRY_ASSIGN(uploadHandle, device->GetDynamicUploader()->Allocate(
|
||||||
|
size, device->GetPendingCommandSerial(),
|
||||||
|
kCopyBufferToBufferOffsetAlignment));
|
||||||
|
ASSERT(uploadHandle.mappedBuffer != nullptr);
|
||||||
|
memcpy(uploadHandle.mappedBuffer, data, size);
|
||||||
|
|
||||||
|
dstBuffer->EnsureDataInitializedAsDestination(commandContext, offset, size);
|
||||||
|
|
||||||
|
[commandContext->EnsureBlit()
|
||||||
|
copyFromBuffer:ToBackend(uploadHandle.stagingBuffer)->GetBufferHandle()
|
||||||
|
sourceOffset:uploadHandle.startOffset
|
||||||
|
toBuffer:dstBuffer->GetMTLBuffer()
|
||||||
|
destinationOffset:offset
|
||||||
|
size:size];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
|
@ -843,6 +843,23 @@ namespace dawn_native { namespace opengl {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case Command::WriteBuffer: {
|
||||||
|
WriteBufferCmd* write = mCommands.NextCommand<WriteBufferCmd>();
|
||||||
|
uint64_t offset = write->offset;
|
||||||
|
uint64_t size = write->size;
|
||||||
|
if (size == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Buffer* dstBuffer = ToBackend(write->buffer.Get());
|
||||||
|
uint8_t* data = mCommands.NextData<uint8_t>(size);
|
||||||
|
dstBuffer->EnsureDataInitializedAsDestination(offset, size);
|
||||||
|
|
||||||
|
gl.BindBuffer(GL_ARRAY_BUFFER, dstBuffer->GetHandle());
|
||||||
|
gl.BufferSubData(GL_ARRAY_BUFFER, offset, size, data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include "dawn_native/CommandEncoder.h"
|
#include "dawn_native/CommandEncoder.h"
|
||||||
#include "dawn_native/CommandValidation.h"
|
#include "dawn_native/CommandValidation.h"
|
||||||
#include "dawn_native/Commands.h"
|
#include "dawn_native/Commands.h"
|
||||||
|
#include "dawn_native/DynamicUploader.h"
|
||||||
#include "dawn_native/EnumMaskIterator.h"
|
#include "dawn_native/EnumMaskIterator.h"
|
||||||
#include "dawn_native/RenderBundle.h"
|
#include "dawn_native/RenderBundle.h"
|
||||||
#include "dawn_native/vulkan/BindGroupVk.h"
|
#include "dawn_native/vulkan/BindGroupVk.h"
|
||||||
|
@ -30,6 +31,7 @@
|
||||||
#include "dawn_native/vulkan/QuerySetVk.h"
|
#include "dawn_native/vulkan/QuerySetVk.h"
|
||||||
#include "dawn_native/vulkan/RenderPassCache.h"
|
#include "dawn_native/vulkan/RenderPassCache.h"
|
||||||
#include "dawn_native/vulkan/RenderPipelineVk.h"
|
#include "dawn_native/vulkan/RenderPipelineVk.h"
|
||||||
|
#include "dawn_native/vulkan/StagingBufferVk.h"
|
||||||
#include "dawn_native/vulkan/TextureVk.h"
|
#include "dawn_native/vulkan/TextureVk.h"
|
||||||
#include "dawn_native/vulkan/UtilsVulkan.h"
|
#include "dawn_native/vulkan/UtilsVulkan.h"
|
||||||
#include "dawn_native/vulkan/VulkanError.h"
|
#include "dawn_native/vulkan/VulkanError.h"
|
||||||
|
@ -822,6 +824,40 @@ namespace dawn_native { namespace vulkan {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case Command::WriteBuffer: {
|
||||||
|
WriteBufferCmd* write = mCommands.NextCommand<WriteBufferCmd>();
|
||||||
|
const uint64_t offset = write->offset;
|
||||||
|
const uint64_t size = write->size;
|
||||||
|
if (size == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Buffer* dstBuffer = ToBackend(write->buffer.Get());
|
||||||
|
uint8_t* data = mCommands.NextData<uint8_t>(size);
|
||||||
|
Device* device = ToBackend(GetDevice());
|
||||||
|
|
||||||
|
UploadHandle uploadHandle;
|
||||||
|
DAWN_TRY_ASSIGN(uploadHandle, device->GetDynamicUploader()->Allocate(
|
||||||
|
size, device->GetPendingCommandSerial(),
|
||||||
|
kCopyBufferToBufferOffsetAlignment));
|
||||||
|
ASSERT(uploadHandle.mappedBuffer != nullptr);
|
||||||
|
memcpy(uploadHandle.mappedBuffer, data, size);
|
||||||
|
|
||||||
|
dstBuffer->EnsureDataInitializedAsDestination(recordingContext, offset, size);
|
||||||
|
|
||||||
|
dstBuffer->TransitionUsageNow(recordingContext, wgpu::BufferUsage::CopyDst);
|
||||||
|
|
||||||
|
VkBufferCopy copy;
|
||||||
|
copy.srcOffset = uploadHandle.startOffset;
|
||||||
|
copy.dstOffset = offset;
|
||||||
|
copy.size = size;
|
||||||
|
|
||||||
|
device->fn.CmdCopyBuffer(
|
||||||
|
commands, ToBackend(uploadHandle.stagingBuffer)->GetBufferHandle(),
|
||||||
|
dstBuffer->GetHandle(), 1, ©);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -229,6 +229,7 @@ test("dawn_unittests") {
|
||||||
"unittests/validation/VertexBufferValidationTests.cpp",
|
"unittests/validation/VertexBufferValidationTests.cpp",
|
||||||
"unittests/validation/VertexStateValidationTests.cpp",
|
"unittests/validation/VertexStateValidationTests.cpp",
|
||||||
"unittests/validation/VideoViewsValidationTests.cpp",
|
"unittests/validation/VideoViewsValidationTests.cpp",
|
||||||
|
"unittests/validation/WriteBufferTests.cpp",
|
||||||
"unittests/wire/WireArgumentTests.cpp",
|
"unittests/wire/WireArgumentTests.cpp",
|
||||||
"unittests/wire/WireBasicTests.cpp",
|
"unittests/wire/WireBasicTests.cpp",
|
||||||
"unittests/wire/WireBufferMappingTests.cpp",
|
"unittests/wire/WireBufferMappingTests.cpp",
|
||||||
|
@ -298,6 +299,7 @@ source_set("dawn_end2end_tests_sources") {
|
||||||
"end2end/BufferZeroInitTests.cpp",
|
"end2end/BufferZeroInitTests.cpp",
|
||||||
"end2end/ClipSpaceTests.cpp",
|
"end2end/ClipSpaceTests.cpp",
|
||||||
"end2end/ColorStateTests.cpp",
|
"end2end/ColorStateTests.cpp",
|
||||||
|
"end2end/CommandEncoderTests.cpp",
|
||||||
"end2end/CompressedTextureFormatTests.cpp",
|
"end2end/CompressedTextureFormatTests.cpp",
|
||||||
"end2end/ComputeCopyStorageBufferTests.cpp",
|
"end2end/ComputeCopyStorageBufferTests.cpp",
|
||||||
"end2end/ComputeDispatchTests.cpp",
|
"end2end/ComputeDispatchTests.cpp",
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
// Copyright 2021 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/DawnTest.h"
|
||||||
|
#include "utils/WGPUHelpers.h"
|
||||||
|
|
||||||
|
class CommandEncoderTests : public DawnTest {};
|
||||||
|
|
||||||
|
// Tests WriteBuffer commands on CommandEncoder.
|
||||||
|
TEST_P(CommandEncoderTests, WriteBuffer) {
|
||||||
|
wgpu::Buffer bufferA = utils::CreateBufferFromData(
|
||||||
|
device, wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::CopySrc, {0, 0, 0, 0});
|
||||||
|
wgpu::Buffer bufferB = utils::CreateBufferFromData(
|
||||||
|
device, wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::CopySrc, {0, 0, 0, 0});
|
||||||
|
wgpu::Buffer bufferC = utils::CreateBufferFromData(
|
||||||
|
device, wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::CopySrc, {0, 0, 0, 0});
|
||||||
|
|
||||||
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
|
|
||||||
|
const uint32_t kData1 = 1;
|
||||||
|
encoder.WriteBuffer(bufferA, 0, reinterpret_cast<const uint8_t*>(&kData1), sizeof(kData1));
|
||||||
|
encoder.CopyBufferToBuffer(bufferA, 0, bufferB, sizeof(uint32_t), 3 * sizeof(uint32_t));
|
||||||
|
|
||||||
|
const uint32_t kData2 = 2;
|
||||||
|
encoder.WriteBuffer(bufferB, 0, reinterpret_cast<const uint8_t*>(&kData2), sizeof(kData2));
|
||||||
|
encoder.CopyBufferToBuffer(bufferB, 0, bufferC, sizeof(uint32_t), 3 * sizeof(uint32_t));
|
||||||
|
|
||||||
|
wgpu::CommandBuffer commands = encoder.Finish();
|
||||||
|
queue.Submit(1, &commands);
|
||||||
|
|
||||||
|
EXPECT_BUFFER_U32_EQ(0, bufferC, 0);
|
||||||
|
EXPECT_BUFFER_U32_EQ(2, bufferC, sizeof(uint32_t));
|
||||||
|
EXPECT_BUFFER_U32_EQ(1, bufferC, 2 * sizeof(uint32_t));
|
||||||
|
EXPECT_BUFFER_U32_EQ(0, bufferC, 3 * sizeof(uint32_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
DAWN_INSTANTIATE_TEST(CommandEncoderTests,
|
||||||
|
D3D12Backend(),
|
||||||
|
MetalBackend(),
|
||||||
|
OpenGLBackend(),
|
||||||
|
OpenGLESBackend(),
|
||||||
|
VulkanBackend());
|
|
@ -0,0 +1,104 @@
|
||||||
|
// Copyright 2021 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 "utils/ComboRenderPipelineDescriptor.h"
|
||||||
|
#include "utils/WGPUHelpers.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class WriteBufferTest : public ValidationTest {
|
||||||
|
public:
|
||||||
|
wgpu::Buffer CreateWritableBuffer(uint64_t size) {
|
||||||
|
wgpu::BufferDescriptor desc;
|
||||||
|
desc.usage = wgpu::BufferUsage::CopyDst;
|
||||||
|
desc.size = size;
|
||||||
|
return device.CreateBuffer(&desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
wgpu::CommandBuffer EncodeWriteBuffer(wgpu::Buffer buffer,
|
||||||
|
uint64_t bufferOffset,
|
||||||
|
uint64_t size) {
|
||||||
|
std::vector<uint8_t> data(size);
|
||||||
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
|
encoder.WriteBuffer(buffer, bufferOffset, data.data(), size);
|
||||||
|
return encoder.Finish();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Tests that the buffer offset is validated to be a multiple of 4 bytes.
|
||||||
|
TEST_F(WriteBufferTest, OffsetAlignment) {
|
||||||
|
wgpu::Buffer buffer = CreateWritableBuffer(64);
|
||||||
|
EncodeWriteBuffer(buffer, 0, 4);
|
||||||
|
EncodeWriteBuffer(buffer, 4, 4);
|
||||||
|
EncodeWriteBuffer(buffer, 60, 4);
|
||||||
|
ASSERT_DEVICE_ERROR(EncodeWriteBuffer(buffer, 1, 4));
|
||||||
|
ASSERT_DEVICE_ERROR(EncodeWriteBuffer(buffer, 2, 4));
|
||||||
|
ASSERT_DEVICE_ERROR(EncodeWriteBuffer(buffer, 3, 4));
|
||||||
|
ASSERT_DEVICE_ERROR(EncodeWriteBuffer(buffer, 5, 4));
|
||||||
|
ASSERT_DEVICE_ERROR(EncodeWriteBuffer(buffer, 11, 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that the buffer size is validated to be a multiple of 4 bytes.
|
||||||
|
TEST_F(WriteBufferTest, SizeAlignment) {
|
||||||
|
wgpu::Buffer buffer = CreateWritableBuffer(64);
|
||||||
|
EncodeWriteBuffer(buffer, 0, 64);
|
||||||
|
EncodeWriteBuffer(buffer, 4, 60);
|
||||||
|
EncodeWriteBuffer(buffer, 40, 24);
|
||||||
|
ASSERT_DEVICE_ERROR(EncodeWriteBuffer(buffer, 0, 63));
|
||||||
|
ASSERT_DEVICE_ERROR(EncodeWriteBuffer(buffer, 4, 1));
|
||||||
|
ASSERT_DEVICE_ERROR(EncodeWriteBuffer(buffer, 4, 2));
|
||||||
|
ASSERT_DEVICE_ERROR(EncodeWriteBuffer(buffer, 40, 23));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that the buffer size and offset are validated to fit within the bounds of the buffer.
|
||||||
|
TEST_F(WriteBufferTest, BufferBounds) {
|
||||||
|
wgpu::Buffer buffer = CreateWritableBuffer(64);
|
||||||
|
EncodeWriteBuffer(buffer, 0, 64);
|
||||||
|
EncodeWriteBuffer(buffer, 4, 60);
|
||||||
|
EncodeWriteBuffer(buffer, 40, 24);
|
||||||
|
ASSERT_DEVICE_ERROR(EncodeWriteBuffer(buffer, 0, 68));
|
||||||
|
ASSERT_DEVICE_ERROR(EncodeWriteBuffer(buffer, 4, 64));
|
||||||
|
ASSERT_DEVICE_ERROR(EncodeWriteBuffer(buffer, 60, 8));
|
||||||
|
ASSERT_DEVICE_ERROR(EncodeWriteBuffer(buffer, 64, 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that the destination buffer's usage is validated to contain CopyDst.
|
||||||
|
TEST_F(WriteBufferTest, RequireCopyDstUsage) {
|
||||||
|
wgpu::BufferDescriptor desc;
|
||||||
|
desc.usage = wgpu::BufferUsage::CopySrc;
|
||||||
|
desc.size = 64;
|
||||||
|
wgpu::Buffer buffer = device.CreateBuffer(&desc);
|
||||||
|
|
||||||
|
ASSERT_DEVICE_ERROR(EncodeWriteBuffer(buffer, 0, 64));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that the destination buffer's state is validated at submission.
|
||||||
|
TEST_F(WriteBufferTest, ValidBufferState) {
|
||||||
|
wgpu::BufferDescriptor desc;
|
||||||
|
desc.usage = wgpu::BufferUsage::CopyDst;
|
||||||
|
desc.size = 64;
|
||||||
|
desc.mappedAtCreation = true;
|
||||||
|
wgpu::Buffer buffer = device.CreateBuffer(&desc);
|
||||||
|
|
||||||
|
wgpu::CommandBuffer commands = EncodeWriteBuffer(buffer, 0, 64);
|
||||||
|
ASSERT_DEVICE_ERROR(device.GetQueue().Submit(1, &commands));
|
||||||
|
|
||||||
|
commands = EncodeWriteBuffer(buffer, 0, 64);
|
||||||
|
buffer.Unmap();
|
||||||
|
device.GetQueue().Submit(1, &commands);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
Loading…
Reference in New Issue