Implement GPUCommandEncoder::ClearBuffer
Implements ClearBuffer for all backends. Bug: dawn:1170 Change-Id: Ifc687d55727821c4fc134bf95020794c9d325025 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/68642 Reviewed-by: Kai Ninomiya <kainino@chromium.org> Reviewed-by: Corentin Wallez <cwallez@chromium.org> Commit-Queue: Brandon Jones <bajones@chromium.org>
This commit is contained in:
parent
2bf99905a7
commit
d3105bfa47
10
dawn.json
10
dawn.json
|
@ -582,13 +582,11 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "fill buffer",
|
"name": "clear buffer",
|
||||||
"tags": ["upstream"],
|
|
||||||
"args": [
|
"args": [
|
||||||
{"name": "destination", "type": "buffer"},
|
{"name": "buffer", "type": "buffer"},
|
||||||
{"name": "destination offset", "type": "uint64_t"},
|
{"name": "offset", "type": "uint64_t", "default": 0},
|
||||||
{"name": "size", "type": "uint64_t"},
|
{"name": "size", "type": "uint64_t", "default": "WGPU_WHOLE_SIZE"}
|
||||||
{"name": "value", "type": "uint8_t", "default": 0}
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -540,6 +540,11 @@ namespace dawn_native {
|
||||||
CallMapCallback(mapID, status);
|
CallMapCallback(mapID, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool BufferBase::NeedsInitialization() const {
|
||||||
|
return !mIsDataInitialized &&
|
||||||
|
GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse);
|
||||||
|
}
|
||||||
|
|
||||||
bool BufferBase::IsDataInitialized() const {
|
bool BufferBase::IsDataInitialized() const {
|
||||||
return mIsDataInitialized;
|
return mIsDataInitialized;
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,6 +64,7 @@ namespace dawn_native {
|
||||||
MaybeError ValidateCanUseOnQueueNow() const;
|
MaybeError ValidateCanUseOnQueueNow() const;
|
||||||
|
|
||||||
bool IsFullBufferRange(uint64_t offset, uint64_t size) const;
|
bool IsFullBufferRange(uint64_t offset, uint64_t size) const;
|
||||||
|
bool NeedsInitialization() const;
|
||||||
bool IsDataInitialized() const;
|
bool IsDataInitialized() const;
|
||||||
void SetIsDataInitialized();
|
void SetIsDataInitialized();
|
||||||
|
|
||||||
|
|
|
@ -879,6 +879,57 @@ namespace dawn_native {
|
||||||
destination->texture, copySize);
|
destination->texture, copySize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CommandEncoder::APIClearBuffer(BufferBase* buffer, uint64_t offset, uint64_t size) {
|
||||||
|
mEncodingContext.TryEncode(
|
||||||
|
this,
|
||||||
|
[&](CommandAllocator* allocator) -> MaybeError {
|
||||||
|
if (GetDevice()->IsValidationEnabled()) {
|
||||||
|
DAWN_TRY(GetDevice()->ValidateObject(buffer));
|
||||||
|
|
||||||
|
uint64_t bufferSize = buffer->GetSize();
|
||||||
|
DAWN_INVALID_IF(offset > bufferSize,
|
||||||
|
"Buffer offset (%u) is larger than the size (%u) of %s.",
|
||||||
|
offset, bufferSize, buffer);
|
||||||
|
|
||||||
|
uint64_t remainingSize = bufferSize - offset;
|
||||||
|
if (size == wgpu::kWholeSize) {
|
||||||
|
size = remainingSize;
|
||||||
|
} else {
|
||||||
|
DAWN_INVALID_IF(size > remainingSize,
|
||||||
|
"Buffer range (offset: %u, size: %u) doesn't fit in "
|
||||||
|
"the size (%u) of %s.",
|
||||||
|
offset, size, bufferSize, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
DAWN_TRY_CONTEXT(ValidateCanUseAs(buffer, wgpu::BufferUsage::CopyDst),
|
||||||
|
"validating buffer %s usage.", buffer);
|
||||||
|
|
||||||
|
// Size must be a multiple of 4 bytes on macOS.
|
||||||
|
DAWN_INVALID_IF(size % 4 != 0, "Fill size (%u) is not a multiple of 4 bytes.",
|
||||||
|
size);
|
||||||
|
|
||||||
|
// Offset must be multiples of 4 bytes on macOS.
|
||||||
|
DAWN_INVALID_IF(offset % 4 != 0, "Offset (%u) is not a multiple of 4 bytes,",
|
||||||
|
offset);
|
||||||
|
|
||||||
|
mTopLevelBuffers.insert(buffer);
|
||||||
|
} else {
|
||||||
|
if (size == wgpu::kWholeSize) {
|
||||||
|
DAWN_ASSERT(buffer->GetSize() >= offset);
|
||||||
|
size = buffer->GetSize() - offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ClearBufferCmd* cmd = allocator->Allocate<ClearBufferCmd>(Command::ClearBuffer);
|
||||||
|
cmd->buffer = buffer;
|
||||||
|
cmd->offset = offset;
|
||||||
|
cmd->size = size;
|
||||||
|
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
"encoding %s.ClearBuffer(%s, %u, %u).", this, buffer, offset, size);
|
||||||
|
}
|
||||||
|
|
||||||
void CommandEncoder::APIInjectValidationError(const char* message) {
|
void CommandEncoder::APIInjectValidationError(const char* message) {
|
||||||
if (mEncodingContext.CheckCurrentEncoder(this)) {
|
if (mEncodingContext.CheckCurrentEncoder(this)) {
|
||||||
mEncodingContext.HandleError(DAWN_VALIDATION_ERROR(message));
|
mEncodingContext.HandleError(DAWN_VALIDATION_ERROR(message));
|
||||||
|
|
|
@ -59,6 +59,7 @@ namespace dawn_native {
|
||||||
void APICopyTextureToTextureInternal(const ImageCopyTexture* source,
|
void APICopyTextureToTextureInternal(const ImageCopyTexture* source,
|
||||||
const ImageCopyTexture* destination,
|
const ImageCopyTexture* destination,
|
||||||
const Extent3D* copySize);
|
const Extent3D* copySize);
|
||||||
|
void APIClearBuffer(BufferBase* destination, uint64_t destinationOffset, uint64_t size);
|
||||||
|
|
||||||
void APIInjectValidationError(const char* message);
|
void APIInjectValidationError(const char* message);
|
||||||
void APIInsertDebugMarker(const char* groupLabel);
|
void APIInsertDebugMarker(const char* groupLabel);
|
||||||
|
|
|
@ -121,6 +121,11 @@ namespace dawn_native {
|
||||||
cmd->~ExecuteBundlesCmd();
|
cmd->~ExecuteBundlesCmd();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case Command::ClearBuffer: {
|
||||||
|
ClearBufferCmd* cmd = commands->NextCommand<ClearBufferCmd>();
|
||||||
|
cmd->~ClearBufferCmd();
|
||||||
|
break;
|
||||||
|
}
|
||||||
case Command::InsertDebugMarker: {
|
case Command::InsertDebugMarker: {
|
||||||
InsertDebugMarkerCmd* cmd = commands->NextCommand<InsertDebugMarkerCmd>();
|
InsertDebugMarkerCmd* cmd = commands->NextCommand<InsertDebugMarkerCmd>();
|
||||||
commands->NextData<char>(cmd->length + 1);
|
commands->NextData<char>(cmd->length + 1);
|
||||||
|
@ -280,6 +285,10 @@ namespace dawn_native {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case Command::ClearBuffer:
|
||||||
|
commands->NextCommand<ClearBufferCmd>();
|
||||||
|
break;
|
||||||
|
|
||||||
case Command::InsertDebugMarker: {
|
case Command::InsertDebugMarker: {
|
||||||
InsertDebugMarkerCmd* cmd = commands->NextCommand<InsertDebugMarkerCmd>();
|
InsertDebugMarkerCmd* cmd = commands->NextCommand<InsertDebugMarkerCmd>();
|
||||||
commands->NextData<char>(cmd->length + 1);
|
commands->NextData<char>(cmd->length + 1);
|
||||||
|
|
|
@ -36,6 +36,7 @@ namespace dawn_native {
|
||||||
BeginComputePass,
|
BeginComputePass,
|
||||||
BeginOcclusionQuery,
|
BeginOcclusionQuery,
|
||||||
BeginRenderPass,
|
BeginRenderPass,
|
||||||
|
ClearBuffer,
|
||||||
CopyBufferToBuffer,
|
CopyBufferToBuffer,
|
||||||
CopyBufferToTexture,
|
CopyBufferToTexture,
|
||||||
CopyTextureToBuffer,
|
CopyTextureToBuffer,
|
||||||
|
@ -196,6 +197,12 @@ namespace dawn_native {
|
||||||
uint32_t count;
|
uint32_t count;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ClearBufferCmd {
|
||||||
|
Ref<BufferBase> buffer;
|
||||||
|
uint64_t offset;
|
||||||
|
uint64_t size;
|
||||||
|
};
|
||||||
|
|
||||||
struct InsertDebugMarkerCmd {
|
struct InsertDebugMarkerCmd {
|
||||||
uint32_t length;
|
uint32_t length;
|
||||||
};
|
};
|
||||||
|
|
|
@ -400,37 +400,34 @@ namespace dawn_native { namespace d3d12 {
|
||||||
}
|
}
|
||||||
|
|
||||||
MaybeError Buffer::EnsureDataInitialized(CommandRecordingContext* commandContext) {
|
MaybeError Buffer::EnsureDataInitialized(CommandRecordingContext* commandContext) {
|
||||||
if (IsDataInitialized() ||
|
if (!NeedsInitialization()) {
|
||||||
!GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) {
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
DAWN_TRY(InitializeToZero(commandContext));
|
DAWN_TRY(InitializeToZero(commandContext));
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
MaybeError Buffer::EnsureDataInitializedAsDestination(CommandRecordingContext* commandContext,
|
ResultOrError<bool> Buffer::EnsureDataInitializedAsDestination(
|
||||||
uint64_t offset,
|
CommandRecordingContext* commandContext,
|
||||||
uint64_t size) {
|
uint64_t offset,
|
||||||
if (IsDataInitialized() ||
|
uint64_t size) {
|
||||||
!GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) {
|
if (!NeedsInitialization()) {
|
||||||
return {};
|
return {false};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsFullBufferRange(offset, size)) {
|
if (IsFullBufferRange(offset, size)) {
|
||||||
SetIsDataInitialized();
|
SetIsDataInitialized();
|
||||||
} else {
|
return {false};
|
||||||
DAWN_TRY(InitializeToZero(commandContext));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
DAWN_TRY(InitializeToZero(commandContext));
|
||||||
|
return {true};
|
||||||
}
|
}
|
||||||
|
|
||||||
MaybeError Buffer::EnsureDataInitializedAsDestination(CommandRecordingContext* commandContext,
|
MaybeError Buffer::EnsureDataInitializedAsDestination(CommandRecordingContext* commandContext,
|
||||||
const CopyTextureToBufferCmd* copy) {
|
const CopyTextureToBufferCmd* copy) {
|
||||||
if (IsDataInitialized() ||
|
if (!NeedsInitialization()) {
|
||||||
!GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) {
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -449,8 +446,7 @@ namespace dawn_native { namespace d3d12 {
|
||||||
}
|
}
|
||||||
|
|
||||||
MaybeError Buffer::InitializeToZero(CommandRecordingContext* commandContext) {
|
MaybeError Buffer::InitializeToZero(CommandRecordingContext* commandContext) {
|
||||||
ASSERT(GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse));
|
ASSERT(NeedsInitialization());
|
||||||
ASSERT(!IsDataInitialized());
|
|
||||||
|
|
||||||
// TODO(crbug.com/dawn/484): skip initializing the buffer when it is created on a heap
|
// TODO(crbug.com/dawn/484): skip initializing the buffer when it is created on a heap
|
||||||
// that has already been zero initialized.
|
// that has already been zero initialized.
|
||||||
|
|
|
@ -43,11 +43,13 @@ namespace dawn_native { namespace d3d12 {
|
||||||
bool CheckIsResidentForTesting() const;
|
bool CheckIsResidentForTesting() const;
|
||||||
|
|
||||||
MaybeError EnsureDataInitialized(CommandRecordingContext* commandContext);
|
MaybeError EnsureDataInitialized(CommandRecordingContext* commandContext);
|
||||||
MaybeError EnsureDataInitializedAsDestination(CommandRecordingContext* commandContext,
|
ResultOrError<bool> EnsureDataInitializedAsDestination(
|
||||||
uint64_t offset,
|
CommandRecordingContext* commandContext,
|
||||||
uint64_t size);
|
uint64_t offset,
|
||||||
|
uint64_t size);
|
||||||
MaybeError EnsureDataInitializedAsDestination(CommandRecordingContext* commandContext,
|
MaybeError EnsureDataInitializedAsDestination(CommandRecordingContext* commandContext,
|
||||||
const CopyTextureToBufferCmd* copy);
|
const CopyTextureToBufferCmd* copy);
|
||||||
|
|
||||||
// Dawn API
|
// Dawn API
|
||||||
void SetLabelImpl() override;
|
void SetLabelImpl() override;
|
||||||
|
|
||||||
|
|
|
@ -137,22 +137,6 @@ namespace dawn_native { namespace d3d12 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MaybeError ClearBufferToZero(Device* device,
|
|
||||||
Buffer* destination,
|
|
||||||
uint64_t destinationOffset,
|
|
||||||
uint64_t size) {
|
|
||||||
DynamicUploader* uploader = device->GetDynamicUploader();
|
|
||||||
UploadHandle uploadHandle;
|
|
||||||
DAWN_TRY_ASSIGN(uploadHandle,
|
|
||||||
uploader->Allocate(size, device->GetPendingCommandSerial(),
|
|
||||||
kCopyBufferToBufferOffsetAlignment));
|
|
||||||
memset(uploadHandle.mappedBuffer, 0u, size);
|
|
||||||
|
|
||||||
return device->CopyFromStagingToBuffer(uploadHandle.stagingBuffer,
|
|
||||||
uploadHandle.startOffset, destination,
|
|
||||||
destinationOffset, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RecordFirstIndexOffset(ID3D12GraphicsCommandList* commandList,
|
void RecordFirstIndexOffset(ID3D12GraphicsCommandList* commandList,
|
||||||
RenderPipeline* pipeline,
|
RenderPipeline* pipeline,
|
||||||
uint32_t firstVertex,
|
uint32_t firstVertex,
|
||||||
|
@ -731,8 +715,11 @@ namespace dawn_native { namespace d3d12 {
|
||||||
Buffer* dstBuffer = ToBackend(copy->destination.Get());
|
Buffer* dstBuffer = ToBackend(copy->destination.Get());
|
||||||
|
|
||||||
DAWN_TRY(srcBuffer->EnsureDataInitialized(commandContext));
|
DAWN_TRY(srcBuffer->EnsureDataInitialized(commandContext));
|
||||||
DAWN_TRY(dstBuffer->EnsureDataInitializedAsDestination(
|
bool cleared;
|
||||||
commandContext, copy->destinationOffset, copy->size));
|
DAWN_TRY_ASSIGN(cleared,
|
||||||
|
dstBuffer->EnsureDataInitializedAsDestination(
|
||||||
|
commandContext, copy->destinationOffset, copy->size));
|
||||||
|
DAWN_UNUSED(cleared);
|
||||||
|
|
||||||
srcBuffer->TrackUsageAndTransitionNow(commandContext,
|
srcBuffer->TrackUsageAndTransitionNow(commandContext,
|
||||||
wgpu::BufferUsage::CopySrc);
|
wgpu::BufferUsage::CopySrc);
|
||||||
|
@ -933,6 +920,26 @@ namespace dawn_native { namespace d3d12 {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case Command::ClearBuffer: {
|
||||||
|
ClearBufferCmd* cmd = mCommands.NextCommand<ClearBufferCmd>();
|
||||||
|
if (cmd->size == 0) {
|
||||||
|
// Skip no-op fills.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Buffer* dstBuffer = ToBackend(cmd->buffer.Get());
|
||||||
|
|
||||||
|
bool clearedToZero;
|
||||||
|
DAWN_TRY_ASSIGN(clearedToZero, dstBuffer->EnsureDataInitializedAsDestination(
|
||||||
|
commandContext, cmd->offset, cmd->size));
|
||||||
|
|
||||||
|
if (!clearedToZero) {
|
||||||
|
DAWN_TRY(device->ClearBufferToZero(commandContext, cmd->buffer.Get(),
|
||||||
|
cmd->offset, cmd->size));
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case Command::ResolveQuerySet: {
|
case Command::ResolveQuerySet: {
|
||||||
ResolveQuerySetCmd* cmd = mCommands.NextCommand<ResolveQuerySetCmd>();
|
ResolveQuerySetCmd* cmd = mCommands.NextCommand<ResolveQuerySetCmd>();
|
||||||
QuerySet* querySet = ToBackend(cmd->querySet.Get());
|
QuerySet* querySet = ToBackend(cmd->querySet.Get());
|
||||||
|
@ -941,8 +948,11 @@ namespace dawn_native { namespace d3d12 {
|
||||||
Buffer* destination = ToBackend(cmd->destination.Get());
|
Buffer* destination = ToBackend(cmd->destination.Get());
|
||||||
uint64_t destinationOffset = cmd->destinationOffset;
|
uint64_t destinationOffset = cmd->destinationOffset;
|
||||||
|
|
||||||
DAWN_TRY(destination->EnsureDataInitializedAsDestination(
|
bool cleared;
|
||||||
commandContext, destinationOffset, queryCount * sizeof(uint64_t)));
|
DAWN_TRY_ASSIGN(cleared, destination->EnsureDataInitializedAsDestination(
|
||||||
|
commandContext, destinationOffset,
|
||||||
|
queryCount * sizeof(uint64_t)));
|
||||||
|
DAWN_UNUSED(cleared);
|
||||||
|
|
||||||
// Resolving unavailable queries is undefined behaviour on D3D12, we only can
|
// Resolving unavailable queries is undefined behaviour on D3D12, we only can
|
||||||
// resolve the available part of sparse queries. In order to resolve the
|
// resolve the available part of sparse queries. In order to resolve the
|
||||||
|
@ -952,8 +962,9 @@ namespace dawn_native { namespace d3d12 {
|
||||||
auto endIt = querySet->GetQueryAvailability().begin() + firstQuery + queryCount;
|
auto endIt = querySet->GetQueryAvailability().begin() + firstQuery + queryCount;
|
||||||
bool hasUnavailableQueries = std::find(startIt, endIt, false) != endIt;
|
bool hasUnavailableQueries = std::find(startIt, endIt, false) != endIt;
|
||||||
if (hasUnavailableQueries) {
|
if (hasUnavailableQueries) {
|
||||||
DAWN_TRY(ClearBufferToZero(device, destination, destinationOffset,
|
DAWN_TRY(device->ClearBufferToZero(commandContext, destination,
|
||||||
queryCount * sizeof(uint64_t)));
|
destinationOffset,
|
||||||
|
queryCount * sizeof(uint64_t)));
|
||||||
}
|
}
|
||||||
|
|
||||||
destination->TrackUsageAndTransitionNow(commandContext,
|
destination->TrackUsageAndTransitionNow(commandContext,
|
||||||
|
@ -1030,8 +1041,10 @@ namespace dawn_native { namespace d3d12 {
|
||||||
ASSERT(uploadHandle.mappedBuffer != nullptr);
|
ASSERT(uploadHandle.mappedBuffer != nullptr);
|
||||||
memcpy(uploadHandle.mappedBuffer, data, size);
|
memcpy(uploadHandle.mappedBuffer, data, size);
|
||||||
|
|
||||||
DAWN_TRY(dstBuffer->EnsureDataInitializedAsDestination(commandContext, offset,
|
bool cleared;
|
||||||
size));
|
DAWN_TRY_ASSIGN(cleared, dstBuffer->EnsureDataInitializedAsDestination(
|
||||||
|
commandContext, offset, size));
|
||||||
|
DAWN_UNUSED(cleared);
|
||||||
dstBuffer->TrackUsageAndTransitionNow(commandContext,
|
dstBuffer->TrackUsageAndTransitionNow(commandContext,
|
||||||
wgpu::BufferUsage::CopyDst);
|
wgpu::BufferUsage::CopyDst);
|
||||||
commandList->CopyBufferRegion(
|
commandList->CopyBufferRegion(
|
||||||
|
|
|
@ -465,8 +465,10 @@ namespace dawn_native { namespace d3d12 {
|
||||||
|
|
||||||
Buffer* dstBuffer = ToBackend(destination);
|
Buffer* dstBuffer = ToBackend(destination);
|
||||||
|
|
||||||
DAWN_TRY(dstBuffer->EnsureDataInitializedAsDestination(commandRecordingContext,
|
bool cleared;
|
||||||
destinationOffset, size));
|
DAWN_TRY_ASSIGN(cleared, dstBuffer->EnsureDataInitializedAsDestination(
|
||||||
|
commandRecordingContext, destinationOffset, size));
|
||||||
|
DAWN_UNUSED(cleared);
|
||||||
|
|
||||||
CopyFromStagingToBufferImpl(commandRecordingContext, source, sourceOffset, destination,
|
CopyFromStagingToBufferImpl(commandRecordingContext, source, sourceOffset, destination,
|
||||||
destinationOffset, size);
|
destinationOffset, size);
|
||||||
|
|
|
@ -32,11 +32,11 @@ namespace dawn_native { namespace metal {
|
||||||
const BufferDescriptor* descriptor);
|
const BufferDescriptor* descriptor);
|
||||||
id<MTLBuffer> GetMTLBuffer() const;
|
id<MTLBuffer> GetMTLBuffer() const;
|
||||||
|
|
||||||
void EnsureDataInitialized(CommandRecordingContext* commandContext);
|
bool EnsureDataInitialized(CommandRecordingContext* commandContext);
|
||||||
void EnsureDataInitializedAsDestination(CommandRecordingContext* commandContext,
|
bool EnsureDataInitializedAsDestination(CommandRecordingContext* commandContext,
|
||||||
uint64_t offset,
|
uint64_t offset,
|
||||||
uint64_t size);
|
uint64_t size);
|
||||||
void EnsureDataInitializedAsDestination(CommandRecordingContext* commandContext,
|
bool EnsureDataInitializedAsDestination(CommandRecordingContext* commandContext,
|
||||||
const CopyTextureToBufferCmd* copy);
|
const CopyTextureToBufferCmd* copy);
|
||||||
|
|
||||||
static uint64_t QueryMaxBufferLength(id<MTLDevice> mtlDevice);
|
static uint64_t QueryMaxBufferLength(id<MTLDevice> mtlDevice);
|
||||||
|
|
|
@ -176,47 +176,48 @@ namespace dawn_native { namespace metal {
|
||||||
mMtlBuffer = nullptr;
|
mMtlBuffer = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Buffer::EnsureDataInitialized(CommandRecordingContext* commandContext) {
|
bool Buffer::EnsureDataInitialized(CommandRecordingContext* commandContext) {
|
||||||
if (IsDataInitialized() ||
|
if (!NeedsInitialization()) {
|
||||||
!GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) {
|
return false;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
InitializeToZero(commandContext);
|
InitializeToZero(commandContext);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Buffer::EnsureDataInitializedAsDestination(CommandRecordingContext* commandContext,
|
bool Buffer::EnsureDataInitializedAsDestination(CommandRecordingContext* commandContext,
|
||||||
uint64_t offset,
|
uint64_t offset,
|
||||||
uint64_t size) {
|
uint64_t size) {
|
||||||
if (IsDataInitialized() ||
|
if (!NeedsInitialization()) {
|
||||||
!GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) {
|
return false;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsFullBufferRange(offset, size)) {
|
if (IsFullBufferRange(offset, size)) {
|
||||||
SetIsDataInitialized();
|
SetIsDataInitialized();
|
||||||
} else {
|
return false;
|
||||||
InitializeToZero(commandContext);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InitializeToZero(commandContext);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Buffer::EnsureDataInitializedAsDestination(CommandRecordingContext* commandContext,
|
bool Buffer::EnsureDataInitializedAsDestination(CommandRecordingContext* commandContext,
|
||||||
const CopyTextureToBufferCmd* copy) {
|
const CopyTextureToBufferCmd* copy) {
|
||||||
if (IsDataInitialized() ||
|
if (!NeedsInitialization()) {
|
||||||
!GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) {
|
return false;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsFullBufferOverwrittenInTextureToBufferCopy(copy)) {
|
if (IsFullBufferOverwrittenInTextureToBufferCopy(copy)) {
|
||||||
SetIsDataInitialized();
|
SetIsDataInitialized();
|
||||||
} else {
|
return false;
|
||||||
InitializeToZero(commandContext);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InitializeToZero(commandContext);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Buffer::InitializeToZero(CommandRecordingContext* commandContext) {
|
void Buffer::InitializeToZero(CommandRecordingContext* commandContext) {
|
||||||
ASSERT(GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse));
|
ASSERT(NeedsInitialization());
|
||||||
ASSERT(!IsDataInitialized());
|
|
||||||
|
|
||||||
ClearBuffer(commandContext, uint8_t(0u));
|
ClearBuffer(commandContext, uint8_t(0u));
|
||||||
|
|
||||||
|
|
|
@ -914,6 +914,26 @@ namespace dawn_native { namespace metal {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case Command::ClearBuffer: {
|
||||||
|
ClearBufferCmd* cmd = mCommands.NextCommand<ClearBufferCmd>();
|
||||||
|
if (cmd->size == 0) {
|
||||||
|
// Skip no-op copies.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Buffer* dstBuffer = ToBackend(cmd->buffer.Get());
|
||||||
|
|
||||||
|
bool clearedToZero = dstBuffer->EnsureDataInitializedAsDestination(
|
||||||
|
commandContext, cmd->offset, cmd->size);
|
||||||
|
|
||||||
|
if (!clearedToZero) {
|
||||||
|
[commandContext->EnsureBlit() fillBuffer:dstBuffer->GetMTLBuffer()
|
||||||
|
range:NSMakeRange(cmd->offset, cmd->size)
|
||||||
|
value:0u];
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case Command::ResolveQuerySet: {
|
case Command::ResolveQuerySet: {
|
||||||
ResolveQuerySetCmd* cmd = mCommands.NextCommand<ResolveQuerySetCmd>();
|
ResolveQuerySetCmd* cmd = mCommands.NextCommand<ResolveQuerySetCmd>();
|
||||||
QuerySet* querySet = ToBackend(cmd->querySet.Get());
|
QuerySet* querySet = ToBackend(cmd->querySet.Get());
|
||||||
|
|
|
@ -67,44 +67,45 @@ namespace dawn_native { namespace opengl {
|
||||||
return mBuffer;
|
return mBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Buffer::EnsureDataInitialized() {
|
bool Buffer::EnsureDataInitialized() {
|
||||||
if (IsDataInitialized() ||
|
if (!NeedsInitialization()) {
|
||||||
!GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) {
|
return false;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
InitializeToZero();
|
InitializeToZero();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Buffer::EnsureDataInitializedAsDestination(uint64_t offset, uint64_t size) {
|
bool Buffer::EnsureDataInitializedAsDestination(uint64_t offset, uint64_t size) {
|
||||||
if (IsDataInitialized() ||
|
if (!NeedsInitialization()) {
|
||||||
!GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) {
|
return false;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsFullBufferRange(offset, size)) {
|
if (IsFullBufferRange(offset, size)) {
|
||||||
SetIsDataInitialized();
|
SetIsDataInitialized();
|
||||||
} else {
|
return false;
|
||||||
InitializeToZero();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InitializeToZero();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Buffer::EnsureDataInitializedAsDestination(const CopyTextureToBufferCmd* copy) {
|
bool Buffer::EnsureDataInitializedAsDestination(const CopyTextureToBufferCmd* copy) {
|
||||||
if (IsDataInitialized() ||
|
if (!NeedsInitialization()) {
|
||||||
!GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) {
|
return false;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsFullBufferOverwrittenInTextureToBufferCopy(copy)) {
|
if (IsFullBufferOverwrittenInTextureToBufferCopy(copy)) {
|
||||||
SetIsDataInitialized();
|
SetIsDataInitialized();
|
||||||
} else {
|
return false;
|
||||||
InitializeToZero();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InitializeToZero();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Buffer::InitializeToZero() {
|
void Buffer::InitializeToZero() {
|
||||||
ASSERT(GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse));
|
ASSERT(NeedsInitialization());
|
||||||
ASSERT(!IsDataInitialized());
|
|
||||||
|
|
||||||
const uint64_t size = GetAllocatedSize();
|
const uint64_t size = GetAllocatedSize();
|
||||||
Device* device = ToBackend(GetDevice());
|
Device* device = ToBackend(GetDevice());
|
||||||
|
|
|
@ -33,9 +33,9 @@ namespace dawn_native { namespace opengl {
|
||||||
|
|
||||||
GLuint GetHandle() const;
|
GLuint GetHandle() const;
|
||||||
|
|
||||||
void EnsureDataInitialized();
|
bool EnsureDataInitialized();
|
||||||
void EnsureDataInitializedAsDestination(uint64_t offset, uint64_t size);
|
bool EnsureDataInitializedAsDestination(uint64_t offset, uint64_t size);
|
||||||
void EnsureDataInitializedAsDestination(const CopyTextureToBufferCmd* copy);
|
bool EnsureDataInitializedAsDestination(const CopyTextureToBufferCmd* copy);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Buffer(Device* device, const BufferDescriptor* descriptor, bool shouldLazyClear);
|
Buffer(Device* device, const BufferDescriptor* descriptor, bool shouldLazyClear);
|
||||||
|
|
|
@ -825,6 +825,27 @@ namespace dawn_native { namespace opengl {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case Command::ClearBuffer: {
|
||||||
|
ClearBufferCmd* cmd = mCommands.NextCommand<ClearBufferCmd>();
|
||||||
|
if (cmd->size == 0) {
|
||||||
|
// Skip no-op fills.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Buffer* dstBuffer = ToBackend(cmd->buffer.Get());
|
||||||
|
|
||||||
|
bool clearedToZero =
|
||||||
|
dstBuffer->EnsureDataInitializedAsDestination(cmd->offset, cmd->size);
|
||||||
|
|
||||||
|
if (!clearedToZero) {
|
||||||
|
const std::vector<uint8_t> clearValues(cmd->size, 0u);
|
||||||
|
gl.BindBuffer(GL_ARRAY_BUFFER, dstBuffer->GetHandle());
|
||||||
|
gl.BufferSubData(GL_ARRAY_BUFFER, cmd->offset, cmd->size,
|
||||||
|
clearValues.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case Command::ResolveQuerySet: {
|
case Command::ResolveQuerySet: {
|
||||||
// TODO(crbug.com/dawn/434): Resolve non-precise occlusion query.
|
// TODO(crbug.com/dawn/434): Resolve non-precise occlusion query.
|
||||||
SkipCommand(&mCommands, type);
|
SkipCommand(&mCommands, type);
|
||||||
|
|
|
@ -340,42 +340,44 @@ namespace dawn_native { namespace vulkan {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Buffer::EnsureDataInitialized(CommandRecordingContext* recordingContext) {
|
bool Buffer::EnsureDataInitialized(CommandRecordingContext* recordingContext) {
|
||||||
if (IsDataInitialized() ||
|
if (!NeedsInitialization()) {
|
||||||
!GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) {
|
return false;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
InitializeToZero(recordingContext);
|
InitializeToZero(recordingContext);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Buffer::EnsureDataInitializedAsDestination(CommandRecordingContext* recordingContext,
|
bool Buffer::EnsureDataInitializedAsDestination(CommandRecordingContext* recordingContext,
|
||||||
uint64_t offset,
|
uint64_t offset,
|
||||||
uint64_t size) {
|
uint64_t size) {
|
||||||
if (IsDataInitialized() ||
|
if (!NeedsInitialization()) {
|
||||||
!GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) {
|
return false;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsFullBufferRange(offset, size)) {
|
if (IsFullBufferRange(offset, size)) {
|
||||||
SetIsDataInitialized();
|
SetIsDataInitialized();
|
||||||
} else {
|
return false;
|
||||||
InitializeToZero(recordingContext);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InitializeToZero(recordingContext);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Buffer::EnsureDataInitializedAsDestination(CommandRecordingContext* recordingContext,
|
bool Buffer::EnsureDataInitializedAsDestination(CommandRecordingContext* recordingContext,
|
||||||
const CopyTextureToBufferCmd* copy) {
|
const CopyTextureToBufferCmd* copy) {
|
||||||
if (IsDataInitialized() ||
|
if (!NeedsInitialization()) {
|
||||||
!GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) {
|
return false;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsFullBufferOverwrittenInTextureToBufferCopy(copy)) {
|
if (IsFullBufferOverwrittenInTextureToBufferCopy(copy)) {
|
||||||
SetIsDataInitialized();
|
SetIsDataInitialized();
|
||||||
} else {
|
return false;
|
||||||
InitializeToZero(recordingContext);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InitializeToZero(recordingContext);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Buffer::SetLabelImpl() {
|
void Buffer::SetLabelImpl() {
|
||||||
|
@ -384,8 +386,7 @@ namespace dawn_native { namespace vulkan {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Buffer::InitializeToZero(CommandRecordingContext* recordingContext) {
|
void Buffer::InitializeToZero(CommandRecordingContext* recordingContext) {
|
||||||
ASSERT(GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse));
|
ASSERT(NeedsInitialization());
|
||||||
ASSERT(!IsDataInitialized());
|
|
||||||
|
|
||||||
ClearBuffer(recordingContext, 0u);
|
ClearBuffer(recordingContext, 0u);
|
||||||
GetDevice()->IncrementLazyClearCountForTesting();
|
GetDevice()->IncrementLazyClearCountForTesting();
|
||||||
|
|
|
@ -42,11 +42,12 @@ namespace dawn_native { namespace vulkan {
|
||||||
VkPipelineStageFlags* srcStages,
|
VkPipelineStageFlags* srcStages,
|
||||||
VkPipelineStageFlags* dstStages);
|
VkPipelineStageFlags* dstStages);
|
||||||
|
|
||||||
void EnsureDataInitialized(CommandRecordingContext* recordingContext);
|
// All the Ensure methods return true if the buffer was initialized to zero.
|
||||||
void EnsureDataInitializedAsDestination(CommandRecordingContext* recordingContext,
|
bool EnsureDataInitialized(CommandRecordingContext* recordingContext);
|
||||||
|
bool EnsureDataInitializedAsDestination(CommandRecordingContext* recordingContext,
|
||||||
uint64_t offset,
|
uint64_t offset,
|
||||||
uint64_t size);
|
uint64_t size);
|
||||||
void EnsureDataInitializedAsDestination(CommandRecordingContext* recordingContext,
|
bool EnsureDataInitializedAsDestination(CommandRecordingContext* recordingContext,
|
||||||
const CopyTextureToBufferCmd* copy);
|
const CopyTextureToBufferCmd* copy);
|
||||||
|
|
||||||
// Dawn API
|
// Dawn API
|
||||||
|
|
|
@ -707,6 +707,27 @@ namespace dawn_native { namespace vulkan {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case Command::ClearBuffer: {
|
||||||
|
ClearBufferCmd* cmd = mCommands.NextCommand<ClearBufferCmd>();
|
||||||
|
if (cmd->size == 0) {
|
||||||
|
// Skip no-op fills.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Buffer* dstBuffer = ToBackend(cmd->buffer.Get());
|
||||||
|
bool clearedToZero = dstBuffer->EnsureDataInitializedAsDestination(
|
||||||
|
recordingContext, cmd->offset, cmd->size);
|
||||||
|
|
||||||
|
if (!clearedToZero) {
|
||||||
|
dstBuffer->TransitionUsageNow(recordingContext, wgpu::BufferUsage::CopyDst);
|
||||||
|
device->fn.CmdFillBuffer(recordingContext->commandBuffer,
|
||||||
|
dstBuffer->GetHandle(), cmd->offset, cmd->size,
|
||||||
|
0u);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case Command::BeginRenderPass: {
|
case Command::BeginRenderPass: {
|
||||||
BeginRenderPassCmd* cmd = mCommands.NextCommand<BeginRenderPassCmd>();
|
BeginRenderPassCmd* cmd = mCommands.NextCommand<BeginRenderPassCmd>();
|
||||||
|
|
||||||
|
|
|
@ -547,6 +547,40 @@ class CopyTests_B2B : public DawnTest {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ClearBufferTests : public DawnTest {
|
||||||
|
protected:
|
||||||
|
// This is the same signature as ClearBuffer except that the buffers are replaced by
|
||||||
|
// only their size.
|
||||||
|
void DoTest(uint64_t bufferSize, uint64_t clearOffset, uint64_t clearSize) {
|
||||||
|
ASSERT(bufferSize % 4 == 0);
|
||||||
|
ASSERT(clearSize % 4 == 0);
|
||||||
|
|
||||||
|
// Create our test buffer, filled with non-zeroes
|
||||||
|
std::vector<uint32_t> bufferData(static_cast<size_t>(bufferSize / sizeof(uint32_t)));
|
||||||
|
for (size_t i = 0; i < bufferData.size(); i++) {
|
||||||
|
bufferData[i] = i + 1;
|
||||||
|
}
|
||||||
|
wgpu::Buffer buffer = utils::CreateBufferFromData(
|
||||||
|
device, bufferData.data(), bufferData.size() * sizeof(uint32_t),
|
||||||
|
wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::CopySrc);
|
||||||
|
|
||||||
|
std::vector<uint8_t> fillData(static_cast<size_t>(clearSize), 0u);
|
||||||
|
|
||||||
|
// Submit the fill
|
||||||
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
|
encoder.ClearBuffer(buffer, clearOffset, clearSize);
|
||||||
|
wgpu::CommandBuffer commands = encoder.Finish();
|
||||||
|
queue.Submit(1, &commands);
|
||||||
|
|
||||||
|
// Check destination is exactly the expected content.
|
||||||
|
EXPECT_BUFFER_U32_RANGE_EQ(bufferData.data(), buffer, 0, clearOffset / sizeof(uint32_t));
|
||||||
|
EXPECT_BUFFER_U8_RANGE_EQ(fillData.data(), buffer, clearOffset, clearSize);
|
||||||
|
uint64_t clearEnd = clearOffset + clearSize;
|
||||||
|
EXPECT_BUFFER_U32_RANGE_EQ(bufferData.data() + clearEnd / sizeof(uint32_t), buffer,
|
||||||
|
clearEnd, (bufferSize - clearEnd) / sizeof(uint32_t));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Test that copying an entire texture with 256-byte aligned dimensions works
|
// Test that copying an entire texture with 256-byte aligned dimensions works
|
||||||
TEST_P(CopyTests_T2B, FullTextureAligned) {
|
TEST_P(CopyTests_T2B, FullTextureAligned) {
|
||||||
constexpr uint32_t kWidth = 256;
|
constexpr uint32_t kWidth = 256;
|
||||||
|
@ -2407,3 +2441,31 @@ DAWN_INSTANTIATE_TEST(CopyTests_B2B,
|
||||||
OpenGLBackend(),
|
OpenGLBackend(),
|
||||||
OpenGLESBackend(),
|
OpenGLESBackend(),
|
||||||
VulkanBackend());
|
VulkanBackend());
|
||||||
|
|
||||||
|
// Test clearing full buffers
|
||||||
|
TEST_P(ClearBufferTests, FullClear) {
|
||||||
|
DoTest(kSmallBufferSize, 0, kSmallBufferSize);
|
||||||
|
DoTest(kLargeBufferSize, 0, kLargeBufferSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test clearing small pieces of a buffer at different corner case offsets
|
||||||
|
TEST_P(ClearBufferTests, SmallClearInBigBuffer) {
|
||||||
|
constexpr uint64_t kEndOffset = kLargeBufferSize - kSmallBufferSize;
|
||||||
|
DoTest(kLargeBufferSize, 0, kSmallBufferSize);
|
||||||
|
DoTest(kLargeBufferSize, kSmallBufferSize, kSmallBufferSize);
|
||||||
|
DoTest(kLargeBufferSize, kEndOffset, kSmallBufferSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test zero-size clears
|
||||||
|
TEST_P(ClearBufferTests, ZeroSizedClear) {
|
||||||
|
DoTest(kLargeBufferSize, 0, 0);
|
||||||
|
DoTest(kLargeBufferSize, kSmallBufferSize, 0);
|
||||||
|
DoTest(kLargeBufferSize, kLargeBufferSize, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
DAWN_INSTANTIATE_TEST(ClearBufferTests,
|
||||||
|
D3D12Backend(),
|
||||||
|
MetalBackend(),
|
||||||
|
OpenGLBackend(),
|
||||||
|
OpenGLESBackend(),
|
||||||
|
VulkanBackend());
|
|
@ -2345,3 +2345,105 @@ TEST_F(CopyCommandTest_CompressedTextureFormats, CopyToMultipleArrayLayers) {
|
||||||
{testWidth - blockWidth, testHeight - blockHeight, 16});
|
{testWidth - blockWidth, testHeight - blockHeight, 16});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CopyCommandTest_ClearBuffer : public CopyCommandTest {};
|
||||||
|
|
||||||
|
TEST_F(CopyCommandTest_ClearBuffer, Success) {
|
||||||
|
wgpu::Buffer destination = CreateBuffer(16, wgpu::BufferUsage::CopyDst);
|
||||||
|
|
||||||
|
// Clear different ranges, including some that touch the OOB condition
|
||||||
|
{
|
||||||
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
|
encoder.ClearBuffer(destination, 0, 16);
|
||||||
|
encoder.ClearBuffer(destination, 0, 8);
|
||||||
|
encoder.ClearBuffer(destination, 8, 8);
|
||||||
|
encoder.Finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size is allowed to be omitted
|
||||||
|
{
|
||||||
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
|
encoder.ClearBuffer(destination, 0);
|
||||||
|
encoder.ClearBuffer(destination, 8);
|
||||||
|
encoder.Finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size and Offset are allowed to be omitted
|
||||||
|
{
|
||||||
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
|
encoder.ClearBuffer(destination);
|
||||||
|
encoder.Finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test a successful ClearBuffer where the last external reference is dropped.
|
||||||
|
TEST_F(CopyCommandTest_ClearBuffer, DroppedBuffer) {
|
||||||
|
wgpu::Buffer destination = CreateBuffer(16, wgpu::BufferUsage::CopyDst);
|
||||||
|
|
||||||
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
|
encoder.ClearBuffer(destination, 0, 8);
|
||||||
|
wgpu::CommandBuffer commandBuffer = encoder.Finish();
|
||||||
|
|
||||||
|
destination = nullptr;
|
||||||
|
device.GetQueue().Submit(1, &commandBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test ClearBuffer copies with OOB
|
||||||
|
TEST_F(CopyCommandTest_ClearBuffer, OutOfBounds) {
|
||||||
|
wgpu::Buffer destination = CreateBuffer(16, wgpu::BufferUsage::CopyDst);
|
||||||
|
|
||||||
|
{
|
||||||
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
|
encoder.ClearBuffer(destination, 8, 12);
|
||||||
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Despite being zero length, should still raise an error due to being out of bounds.
|
||||||
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
|
encoder.ClearBuffer(destination, 20, 0);
|
||||||
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test ClearBuffer with incorrect buffer usage
|
||||||
|
TEST_F(CopyCommandTest_ClearBuffer, BadUsage) {
|
||||||
|
wgpu::Buffer vertex = CreateBuffer(16, wgpu::BufferUsage::Vertex);
|
||||||
|
|
||||||
|
// Destination with incorrect usage
|
||||||
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
|
encoder.ClearBuffer(vertex, 0, 16);
|
||||||
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test ClearBuffer with unaligned data size
|
||||||
|
TEST_F(CopyCommandTest_ClearBuffer, UnalignedSize) {
|
||||||
|
wgpu::Buffer destination = CreateBuffer(16, wgpu::BufferUsage::CopyDst);
|
||||||
|
|
||||||
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
|
encoder.ClearBuffer(destination, 0, 2);
|
||||||
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test ClearBuffer with unaligned offset
|
||||||
|
TEST_F(CopyCommandTest_ClearBuffer, UnalignedOffset) {
|
||||||
|
wgpu::Buffer destination = CreateBuffer(16, wgpu::BufferUsage::CopyDst);
|
||||||
|
|
||||||
|
// Unaligned destination offset
|
||||||
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
|
encoder.ClearBuffer(destination, 2, 4);
|
||||||
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test ClearBuffer with buffers in error state cause errors.
|
||||||
|
TEST_F(CopyCommandTest_ClearBuffer, BuffersInErrorState) {
|
||||||
|
wgpu::BufferDescriptor errorBufferDescriptor;
|
||||||
|
errorBufferDescriptor.size = 4;
|
||||||
|
errorBufferDescriptor.usage =
|
||||||
|
wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst;
|
||||||
|
ASSERT_DEVICE_ERROR(wgpu::Buffer errorBuffer = device.CreateBuffer(&errorBufferDescriptor));
|
||||||
|
|
||||||
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
|
encoder.ClearBuffer(errorBuffer, 0, 4);
|
||||||
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue