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",
|
||||
"tags": ["upstream"],
|
||||
"name": "clear buffer",
|
||||
"args": [
|
||||
{"name": "destination", "type": "buffer"},
|
||||
{"name": "destination offset", "type": "uint64_t"},
|
||||
{"name": "size", "type": "uint64_t"},
|
||||
{"name": "value", "type": "uint8_t", "default": 0}
|
||||
{"name": "buffer", "type": "buffer"},
|
||||
{"name": "offset", "type": "uint64_t", "default": 0},
|
||||
{"name": "size", "type": "uint64_t", "default": "WGPU_WHOLE_SIZE"}
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
@ -540,6 +540,11 @@ namespace dawn_native {
|
|||
CallMapCallback(mapID, status);
|
||||
}
|
||||
|
||||
bool BufferBase::NeedsInitialization() const {
|
||||
return !mIsDataInitialized &&
|
||||
GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse);
|
||||
}
|
||||
|
||||
bool BufferBase::IsDataInitialized() const {
|
||||
return mIsDataInitialized;
|
||||
}
|
||||
|
|
|
@ -64,6 +64,7 @@ namespace dawn_native {
|
|||
MaybeError ValidateCanUseOnQueueNow() const;
|
||||
|
||||
bool IsFullBufferRange(uint64_t offset, uint64_t size) const;
|
||||
bool NeedsInitialization() const;
|
||||
bool IsDataInitialized() const;
|
||||
void SetIsDataInitialized();
|
||||
|
||||
|
|
|
@ -879,6 +879,57 @@ namespace dawn_native {
|
|||
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) {
|
||||
if (mEncodingContext.CheckCurrentEncoder(this)) {
|
||||
mEncodingContext.HandleError(DAWN_VALIDATION_ERROR(message));
|
||||
|
|
|
@ -59,6 +59,7 @@ namespace dawn_native {
|
|||
void APICopyTextureToTextureInternal(const ImageCopyTexture* source,
|
||||
const ImageCopyTexture* destination,
|
||||
const Extent3D* copySize);
|
||||
void APIClearBuffer(BufferBase* destination, uint64_t destinationOffset, uint64_t size);
|
||||
|
||||
void APIInjectValidationError(const char* message);
|
||||
void APIInsertDebugMarker(const char* groupLabel);
|
||||
|
|
|
@ -121,6 +121,11 @@ namespace dawn_native {
|
|||
cmd->~ExecuteBundlesCmd();
|
||||
break;
|
||||
}
|
||||
case Command::ClearBuffer: {
|
||||
ClearBufferCmd* cmd = commands->NextCommand<ClearBufferCmd>();
|
||||
cmd->~ClearBufferCmd();
|
||||
break;
|
||||
}
|
||||
case Command::InsertDebugMarker: {
|
||||
InsertDebugMarkerCmd* cmd = commands->NextCommand<InsertDebugMarkerCmd>();
|
||||
commands->NextData<char>(cmd->length + 1);
|
||||
|
@ -280,6 +285,10 @@ namespace dawn_native {
|
|||
break;
|
||||
}
|
||||
|
||||
case Command::ClearBuffer:
|
||||
commands->NextCommand<ClearBufferCmd>();
|
||||
break;
|
||||
|
||||
case Command::InsertDebugMarker: {
|
||||
InsertDebugMarkerCmd* cmd = commands->NextCommand<InsertDebugMarkerCmd>();
|
||||
commands->NextData<char>(cmd->length + 1);
|
||||
|
|
|
@ -36,6 +36,7 @@ namespace dawn_native {
|
|||
BeginComputePass,
|
||||
BeginOcclusionQuery,
|
||||
BeginRenderPass,
|
||||
ClearBuffer,
|
||||
CopyBufferToBuffer,
|
||||
CopyBufferToTexture,
|
||||
CopyTextureToBuffer,
|
||||
|
@ -196,6 +197,12 @@ namespace dawn_native {
|
|||
uint32_t count;
|
||||
};
|
||||
|
||||
struct ClearBufferCmd {
|
||||
Ref<BufferBase> buffer;
|
||||
uint64_t offset;
|
||||
uint64_t size;
|
||||
};
|
||||
|
||||
struct InsertDebugMarkerCmd {
|
||||
uint32_t length;
|
||||
};
|
||||
|
|
|
@ -400,37 +400,34 @@ namespace dawn_native { namespace d3d12 {
|
|||
}
|
||||
|
||||
MaybeError Buffer::EnsureDataInitialized(CommandRecordingContext* commandContext) {
|
||||
if (IsDataInitialized() ||
|
||||
!GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) {
|
||||
if (!NeedsInitialization()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
DAWN_TRY(InitializeToZero(commandContext));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
MaybeError Buffer::EnsureDataInitializedAsDestination(CommandRecordingContext* commandContext,
|
||||
ResultOrError<bool> Buffer::EnsureDataInitializedAsDestination(
|
||||
CommandRecordingContext* commandContext,
|
||||
uint64_t offset,
|
||||
uint64_t size) {
|
||||
if (IsDataInitialized() ||
|
||||
!GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) {
|
||||
return {};
|
||||
if (!NeedsInitialization()) {
|
||||
return {false};
|
||||
}
|
||||
|
||||
if (IsFullBufferRange(offset, size)) {
|
||||
SetIsDataInitialized();
|
||||
} else {
|
||||
DAWN_TRY(InitializeToZero(commandContext));
|
||||
return {false};
|
||||
}
|
||||
|
||||
return {};
|
||||
DAWN_TRY(InitializeToZero(commandContext));
|
||||
return {true};
|
||||
}
|
||||
|
||||
MaybeError Buffer::EnsureDataInitializedAsDestination(CommandRecordingContext* commandContext,
|
||||
const CopyTextureToBufferCmd* copy) {
|
||||
if (IsDataInitialized() ||
|
||||
!GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) {
|
||||
if (!NeedsInitialization()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -449,8 +446,7 @@ namespace dawn_native { namespace d3d12 {
|
|||
}
|
||||
|
||||
MaybeError Buffer::InitializeToZero(CommandRecordingContext* commandContext) {
|
||||
ASSERT(GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse));
|
||||
ASSERT(!IsDataInitialized());
|
||||
ASSERT(NeedsInitialization());
|
||||
|
||||
// TODO(crbug.com/dawn/484): skip initializing the buffer when it is created on a heap
|
||||
// that has already been zero initialized.
|
||||
|
|
|
@ -43,11 +43,13 @@ namespace dawn_native { namespace d3d12 {
|
|||
bool CheckIsResidentForTesting() const;
|
||||
|
||||
MaybeError EnsureDataInitialized(CommandRecordingContext* commandContext);
|
||||
MaybeError EnsureDataInitializedAsDestination(CommandRecordingContext* commandContext,
|
||||
ResultOrError<bool> EnsureDataInitializedAsDestination(
|
||||
CommandRecordingContext* commandContext,
|
||||
uint64_t offset,
|
||||
uint64_t size);
|
||||
MaybeError EnsureDataInitializedAsDestination(CommandRecordingContext* commandContext,
|
||||
const CopyTextureToBufferCmd* copy);
|
||||
|
||||
// Dawn API
|
||||
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,
|
||||
RenderPipeline* pipeline,
|
||||
uint32_t firstVertex,
|
||||
|
@ -731,8 +715,11 @@ namespace dawn_native { namespace d3d12 {
|
|||
Buffer* dstBuffer = ToBackend(copy->destination.Get());
|
||||
|
||||
DAWN_TRY(srcBuffer->EnsureDataInitialized(commandContext));
|
||||
DAWN_TRY(dstBuffer->EnsureDataInitializedAsDestination(
|
||||
bool cleared;
|
||||
DAWN_TRY_ASSIGN(cleared,
|
||||
dstBuffer->EnsureDataInitializedAsDestination(
|
||||
commandContext, copy->destinationOffset, copy->size));
|
||||
DAWN_UNUSED(cleared);
|
||||
|
||||
srcBuffer->TrackUsageAndTransitionNow(commandContext,
|
||||
wgpu::BufferUsage::CopySrc);
|
||||
|
@ -933,6 +920,26 @@ namespace dawn_native { namespace d3d12 {
|
|||
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: {
|
||||
ResolveQuerySetCmd* cmd = mCommands.NextCommand<ResolveQuerySetCmd>();
|
||||
QuerySet* querySet = ToBackend(cmd->querySet.Get());
|
||||
|
@ -941,8 +948,11 @@ namespace dawn_native { namespace d3d12 {
|
|||
Buffer* destination = ToBackend(cmd->destination.Get());
|
||||
uint64_t destinationOffset = cmd->destinationOffset;
|
||||
|
||||
DAWN_TRY(destination->EnsureDataInitializedAsDestination(
|
||||
commandContext, destinationOffset, queryCount * sizeof(uint64_t)));
|
||||
bool cleared;
|
||||
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
|
||||
// resolve the available part of sparse queries. In order to resolve the
|
||||
|
@ -952,7 +962,8 @@ namespace dawn_native { namespace d3d12 {
|
|||
auto endIt = querySet->GetQueryAvailability().begin() + firstQuery + queryCount;
|
||||
bool hasUnavailableQueries = std::find(startIt, endIt, false) != endIt;
|
||||
if (hasUnavailableQueries) {
|
||||
DAWN_TRY(ClearBufferToZero(device, destination, destinationOffset,
|
||||
DAWN_TRY(device->ClearBufferToZero(commandContext, destination,
|
||||
destinationOffset,
|
||||
queryCount * sizeof(uint64_t)));
|
||||
}
|
||||
|
||||
|
@ -1030,8 +1041,10 @@ namespace dawn_native { namespace d3d12 {
|
|||
ASSERT(uploadHandle.mappedBuffer != nullptr);
|
||||
memcpy(uploadHandle.mappedBuffer, data, size);
|
||||
|
||||
DAWN_TRY(dstBuffer->EnsureDataInitializedAsDestination(commandContext, offset,
|
||||
size));
|
||||
bool cleared;
|
||||
DAWN_TRY_ASSIGN(cleared, dstBuffer->EnsureDataInitializedAsDestination(
|
||||
commandContext, offset, size));
|
||||
DAWN_UNUSED(cleared);
|
||||
dstBuffer->TrackUsageAndTransitionNow(commandContext,
|
||||
wgpu::BufferUsage::CopyDst);
|
||||
commandList->CopyBufferRegion(
|
||||
|
|
|
@ -465,8 +465,10 @@ namespace dawn_native { namespace d3d12 {
|
|||
|
||||
Buffer* dstBuffer = ToBackend(destination);
|
||||
|
||||
DAWN_TRY(dstBuffer->EnsureDataInitializedAsDestination(commandRecordingContext,
|
||||
destinationOffset, size));
|
||||
bool cleared;
|
||||
DAWN_TRY_ASSIGN(cleared, dstBuffer->EnsureDataInitializedAsDestination(
|
||||
commandRecordingContext, destinationOffset, size));
|
||||
DAWN_UNUSED(cleared);
|
||||
|
||||
CopyFromStagingToBufferImpl(commandRecordingContext, source, sourceOffset, destination,
|
||||
destinationOffset, size);
|
||||
|
|
|
@ -32,11 +32,11 @@ namespace dawn_native { namespace metal {
|
|||
const BufferDescriptor* descriptor);
|
||||
id<MTLBuffer> GetMTLBuffer() const;
|
||||
|
||||
void EnsureDataInitialized(CommandRecordingContext* commandContext);
|
||||
void EnsureDataInitializedAsDestination(CommandRecordingContext* commandContext,
|
||||
bool EnsureDataInitialized(CommandRecordingContext* commandContext);
|
||||
bool EnsureDataInitializedAsDestination(CommandRecordingContext* commandContext,
|
||||
uint64_t offset,
|
||||
uint64_t size);
|
||||
void EnsureDataInitializedAsDestination(CommandRecordingContext* commandContext,
|
||||
bool EnsureDataInitializedAsDestination(CommandRecordingContext* commandContext,
|
||||
const CopyTextureToBufferCmd* copy);
|
||||
|
||||
static uint64_t QueryMaxBufferLength(id<MTLDevice> mtlDevice);
|
||||
|
|
|
@ -176,47 +176,48 @@ namespace dawn_native { namespace metal {
|
|||
mMtlBuffer = nullptr;
|
||||
}
|
||||
|
||||
void Buffer::EnsureDataInitialized(CommandRecordingContext* commandContext) {
|
||||
if (IsDataInitialized() ||
|
||||
!GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) {
|
||||
return;
|
||||
bool Buffer::EnsureDataInitialized(CommandRecordingContext* commandContext) {
|
||||
if (!NeedsInitialization()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
InitializeToZero(commandContext);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Buffer::EnsureDataInitializedAsDestination(CommandRecordingContext* commandContext,
|
||||
bool Buffer::EnsureDataInitializedAsDestination(CommandRecordingContext* commandContext,
|
||||
uint64_t offset,
|
||||
uint64_t size) {
|
||||
if (IsDataInitialized() ||
|
||||
!GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) {
|
||||
return;
|
||||
if (!NeedsInitialization()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsFullBufferRange(offset, size)) {
|
||||
SetIsDataInitialized();
|
||||
} else {
|
||||
InitializeToZero(commandContext);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Buffer::EnsureDataInitializedAsDestination(CommandRecordingContext* commandContext,
|
||||
InitializeToZero(commandContext);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Buffer::EnsureDataInitializedAsDestination(CommandRecordingContext* commandContext,
|
||||
const CopyTextureToBufferCmd* copy) {
|
||||
if (IsDataInitialized() ||
|
||||
!GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) {
|
||||
return;
|
||||
if (!NeedsInitialization()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsFullBufferOverwrittenInTextureToBufferCopy(copy)) {
|
||||
SetIsDataInitialized();
|
||||
} else {
|
||||
InitializeToZero(commandContext);
|
||||
return false;
|
||||
}
|
||||
|
||||
InitializeToZero(commandContext);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Buffer::InitializeToZero(CommandRecordingContext* commandContext) {
|
||||
ASSERT(GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse));
|
||||
ASSERT(!IsDataInitialized());
|
||||
ASSERT(NeedsInitialization());
|
||||
|
||||
ClearBuffer(commandContext, uint8_t(0u));
|
||||
|
||||
|
|
|
@ -914,6 +914,26 @@ namespace dawn_native { namespace metal {
|
|||
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: {
|
||||
ResolveQuerySetCmd* cmd = mCommands.NextCommand<ResolveQuerySetCmd>();
|
||||
QuerySet* querySet = ToBackend(cmd->querySet.Get());
|
||||
|
|
|
@ -67,44 +67,45 @@ namespace dawn_native { namespace opengl {
|
|||
return mBuffer;
|
||||
}
|
||||
|
||||
void Buffer::EnsureDataInitialized() {
|
||||
if (IsDataInitialized() ||
|
||||
!GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) {
|
||||
return;
|
||||
bool Buffer::EnsureDataInitialized() {
|
||||
if (!NeedsInitialization()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
InitializeToZero();
|
||||
return true;
|
||||
}
|
||||
|
||||
void Buffer::EnsureDataInitializedAsDestination(uint64_t offset, uint64_t size) {
|
||||
if (IsDataInitialized() ||
|
||||
!GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) {
|
||||
return;
|
||||
bool Buffer::EnsureDataInitializedAsDestination(uint64_t offset, uint64_t size) {
|
||||
if (!NeedsInitialization()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsFullBufferRange(offset, size)) {
|
||||
SetIsDataInitialized();
|
||||
} else {
|
||||
InitializeToZero();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Buffer::EnsureDataInitializedAsDestination(const CopyTextureToBufferCmd* copy) {
|
||||
if (IsDataInitialized() ||
|
||||
!GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) {
|
||||
return;
|
||||
InitializeToZero();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Buffer::EnsureDataInitializedAsDestination(const CopyTextureToBufferCmd* copy) {
|
||||
if (!NeedsInitialization()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsFullBufferOverwrittenInTextureToBufferCopy(copy)) {
|
||||
SetIsDataInitialized();
|
||||
} else {
|
||||
InitializeToZero();
|
||||
return false;
|
||||
}
|
||||
|
||||
InitializeToZero();
|
||||
return true;
|
||||
}
|
||||
|
||||
void Buffer::InitializeToZero() {
|
||||
ASSERT(GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse));
|
||||
ASSERT(!IsDataInitialized());
|
||||
ASSERT(NeedsInitialization());
|
||||
|
||||
const uint64_t size = GetAllocatedSize();
|
||||
Device* device = ToBackend(GetDevice());
|
||||
|
|
|
@ -33,9 +33,9 @@ namespace dawn_native { namespace opengl {
|
|||
|
||||
GLuint GetHandle() const;
|
||||
|
||||
void EnsureDataInitialized();
|
||||
void EnsureDataInitializedAsDestination(uint64_t offset, uint64_t size);
|
||||
void EnsureDataInitializedAsDestination(const CopyTextureToBufferCmd* copy);
|
||||
bool EnsureDataInitialized();
|
||||
bool EnsureDataInitializedAsDestination(uint64_t offset, uint64_t size);
|
||||
bool EnsureDataInitializedAsDestination(const CopyTextureToBufferCmd* copy);
|
||||
|
||||
private:
|
||||
Buffer(Device* device, const BufferDescriptor* descriptor, bool shouldLazyClear);
|
||||
|
|
|
@ -825,6 +825,27 @@ namespace dawn_native { namespace opengl {
|
|||
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: {
|
||||
// TODO(crbug.com/dawn/434): Resolve non-precise occlusion query.
|
||||
SkipCommand(&mCommands, type);
|
||||
|
|
|
@ -340,42 +340,44 @@ namespace dawn_native { namespace vulkan {
|
|||
}
|
||||
}
|
||||
|
||||
void Buffer::EnsureDataInitialized(CommandRecordingContext* recordingContext) {
|
||||
if (IsDataInitialized() ||
|
||||
!GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) {
|
||||
return;
|
||||
bool Buffer::EnsureDataInitialized(CommandRecordingContext* recordingContext) {
|
||||
if (!NeedsInitialization()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
InitializeToZero(recordingContext);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Buffer::EnsureDataInitializedAsDestination(CommandRecordingContext* recordingContext,
|
||||
bool Buffer::EnsureDataInitializedAsDestination(CommandRecordingContext* recordingContext,
|
||||
uint64_t offset,
|
||||
uint64_t size) {
|
||||
if (IsDataInitialized() ||
|
||||
!GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) {
|
||||
return;
|
||||
if (!NeedsInitialization()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsFullBufferRange(offset, size)) {
|
||||
SetIsDataInitialized();
|
||||
} else {
|
||||
InitializeToZero(recordingContext);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Buffer::EnsureDataInitializedAsDestination(CommandRecordingContext* recordingContext,
|
||||
InitializeToZero(recordingContext);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Buffer::EnsureDataInitializedAsDestination(CommandRecordingContext* recordingContext,
|
||||
const CopyTextureToBufferCmd* copy) {
|
||||
if (IsDataInitialized() ||
|
||||
!GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) {
|
||||
return;
|
||||
if (!NeedsInitialization()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsFullBufferOverwrittenInTextureToBufferCopy(copy)) {
|
||||
SetIsDataInitialized();
|
||||
} else {
|
||||
InitializeToZero(recordingContext);
|
||||
return false;
|
||||
}
|
||||
|
||||
InitializeToZero(recordingContext);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Buffer::SetLabelImpl() {
|
||||
|
@ -384,8 +386,7 @@ namespace dawn_native { namespace vulkan {
|
|||
}
|
||||
|
||||
void Buffer::InitializeToZero(CommandRecordingContext* recordingContext) {
|
||||
ASSERT(GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse));
|
||||
ASSERT(!IsDataInitialized());
|
||||
ASSERT(NeedsInitialization());
|
||||
|
||||
ClearBuffer(recordingContext, 0u);
|
||||
GetDevice()->IncrementLazyClearCountForTesting();
|
||||
|
|
|
@ -42,11 +42,12 @@ namespace dawn_native { namespace vulkan {
|
|||
VkPipelineStageFlags* srcStages,
|
||||
VkPipelineStageFlags* dstStages);
|
||||
|
||||
void EnsureDataInitialized(CommandRecordingContext* recordingContext);
|
||||
void EnsureDataInitializedAsDestination(CommandRecordingContext* recordingContext,
|
||||
// All the Ensure methods return true if the buffer was initialized to zero.
|
||||
bool EnsureDataInitialized(CommandRecordingContext* recordingContext);
|
||||
bool EnsureDataInitializedAsDestination(CommandRecordingContext* recordingContext,
|
||||
uint64_t offset,
|
||||
uint64_t size);
|
||||
void EnsureDataInitializedAsDestination(CommandRecordingContext* recordingContext,
|
||||
bool EnsureDataInitializedAsDestination(CommandRecordingContext* recordingContext,
|
||||
const CopyTextureToBufferCmd* copy);
|
||||
|
||||
// Dawn API
|
||||
|
|
|
@ -707,6 +707,27 @@ namespace dawn_native { namespace vulkan {
|
|||
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: {
|
||||
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_P(CopyTests_T2B, FullTextureAligned) {
|
||||
constexpr uint32_t kWidth = 256;
|
||||
|
@ -2407,3 +2441,31 @@ DAWN_INSTANTIATE_TEST(CopyTests_B2B,
|
|||
OpenGLBackend(),
|
||||
OpenGLESBackend(),
|
||||
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});
|
||||
}
|
||||
}
|
||||
|
||||
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