Implement buffer lazy initialization before writeBuffer
This patch implements buffer lazy initialization before writeBuffer(): if the buffer is not initialized and writeBuffer() doesn't cover the whole buffer, the buffer will be cleared to 0, otherwise the buffer shouldn't be cleared. This patch also introduces a toggle LazyClearBufferOnFirstUse for the development of buffer lazy initialization: before buffer lazy initialization being completely supported, all the related code will only be enabled behind this toggle to prevent the buffers with valid content being unexpectedly cleared. BUG=dawn:414 TEST=dawn_end2end_tests Change-Id: I99a2aa98ca4b9b21d69c6b32080afb525e2c4ad3 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/24041 Commit-Queue: Jiawei Shao <jiawei.shao@intel.com> Reviewed-by: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
parent
59ccb1f6de
commit
80f927d763
|
@ -488,6 +488,10 @@ namespace dawn_native {
|
||||||
mState = BufferState::Destroyed;
|
mState = BufferState::Destroyed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool BufferBase::IsMapped() const {
|
||||||
|
return mState == BufferState::Mapped;
|
||||||
|
}
|
||||||
|
|
||||||
void BufferBase::OnMapCommandSerialFinished(uint32_t mapSerial, bool isWrite) {
|
void BufferBase::OnMapCommandSerialFinished(uint32_t mapSerial, bool isWrite) {
|
||||||
void* data = GetMappedPointerImpl();
|
void* data = GetMappedPointerImpl();
|
||||||
if (isWrite) {
|
if (isWrite) {
|
||||||
|
@ -497,4 +501,16 @@ namespace dawn_native {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool BufferBase::IsDataInitialized() const {
|
||||||
|
return mIsDataInitialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BufferBase::SetIsDataInitialized() {
|
||||||
|
mIsDataInitialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BufferBase::IsFullBufferRange(uint64_t offset, uint64_t size) const {
|
||||||
|
return offset == 0 && size == GetSize();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace dawn_native
|
} // namespace dawn_native
|
||||||
|
|
|
@ -40,8 +40,6 @@ namespace dawn_native {
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum class ClearValue { Zero, NonZero };
|
|
||||||
|
|
||||||
BufferBase(DeviceBase* device, const BufferDescriptor* descriptor);
|
BufferBase(DeviceBase* device, const BufferDescriptor* descriptor);
|
||||||
|
|
||||||
static BufferBase* MakeError(DeviceBase* device);
|
static BufferBase* MakeError(DeviceBase* device);
|
||||||
|
@ -57,6 +55,10 @@ namespace dawn_native {
|
||||||
|
|
||||||
MaybeError ValidateCanUseOnQueueNow() const;
|
MaybeError ValidateCanUseOnQueueNow() const;
|
||||||
|
|
||||||
|
bool IsFullBufferRange(uint64_t offset, uint64_t size) const;
|
||||||
|
bool IsDataInitialized() const;
|
||||||
|
void SetIsDataInitialized();
|
||||||
|
|
||||||
// Dawn API
|
// Dawn API
|
||||||
void SetSubData(uint64_t start, uint64_t count, const void* data);
|
void SetSubData(uint64_t start, uint64_t count, const void* data);
|
||||||
void MapReadAsync(WGPUBufferMapReadCallback callback, void* userdata);
|
void MapReadAsync(WGPUBufferMapReadCallback callback, void* userdata);
|
||||||
|
@ -81,6 +83,8 @@ namespace dawn_native {
|
||||||
|
|
||||||
void DestroyInternal();
|
void DestroyInternal();
|
||||||
|
|
||||||
|
bool IsMapped() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual MaybeError MapAtCreationImpl(uint8_t** mappedPointer) = 0;
|
virtual MaybeError MapAtCreationImpl(uint8_t** mappedPointer) = 0;
|
||||||
virtual MaybeError MapReadAsyncImpl(uint32_t serial) = 0;
|
virtual MaybeError MapReadAsyncImpl(uint32_t serial) = 0;
|
||||||
|
@ -109,6 +113,8 @@ namespace dawn_native {
|
||||||
std::unique_ptr<StagingBufferBase> mStagingBuffer;
|
std::unique_ptr<StagingBufferBase> mStagingBuffer;
|
||||||
|
|
||||||
BufferState mState;
|
BufferState mState;
|
||||||
|
|
||||||
|
bool mIsDataInitialized = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace dawn_native
|
} // namespace dawn_native
|
||||||
|
|
|
@ -29,8 +29,8 @@ namespace dawn_native {
|
||||||
using ToggleEnumAndInfoList =
|
using ToggleEnumAndInfoList =
|
||||||
std::array<ToggleEnumAndInfo, static_cast<size_t>(Toggle::EnumCount)>;
|
std::array<ToggleEnumAndInfo, static_cast<size_t>(Toggle::EnumCount)>;
|
||||||
|
|
||||||
static constexpr ToggleEnumAndInfoList kToggleNameAndInfoList = {{
|
static constexpr ToggleEnumAndInfoList kToggleNameAndInfoList = {
|
||||||
{Toggle::EmulateStoreAndMSAAResolve,
|
{{Toggle::EmulateStoreAndMSAAResolve,
|
||||||
{"emulate_store_and_msaa_resolve",
|
{"emulate_store_and_msaa_resolve",
|
||||||
"Emulate storing into multisampled color attachments and doing MSAA resolve "
|
"Emulate storing into multisampled color attachments and doing MSAA resolve "
|
||||||
"simultaneously. This workaround is enabled by default on the Metal drivers that do "
|
"simultaneously. This workaround is enabled by default on the Metal drivers that do "
|
||||||
|
@ -133,7 +133,12 @@ namespace dawn_native {
|
||||||
"https://crbug.com/dawn/402"}},
|
"https://crbug.com/dawn/402"}},
|
||||||
{Toggle::DisableRobustness,
|
{Toggle::DisableRobustness,
|
||||||
{"disable_robustness", "Disable robust buffer access", "https://crbug.com/dawn/480"}},
|
{"disable_robustness", "Disable robust buffer access", "https://crbug.com/dawn/480"}},
|
||||||
}};
|
{Toggle::LazyClearBufferOnFirstUse,
|
||||||
|
{"lazy_clear_buffer_on_first_use",
|
||||||
|
"Clear buffers on their first use. This is a temporary toggle only for the "
|
||||||
|
"development of buffer lazy initialization and will be removed after buffer lazy "
|
||||||
|
"initialization is completely implemented.",
|
||||||
|
"https://crbug.com/dawn/414"}}}};
|
||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,7 @@ namespace dawn_native {
|
||||||
UseD3D12SmallShaderVisibleHeapForTesting,
|
UseD3D12SmallShaderVisibleHeapForTesting,
|
||||||
UseDXC,
|
UseDXC,
|
||||||
DisableRobustness,
|
DisableRobustness,
|
||||||
|
LazyClearBufferOnFirstUse,
|
||||||
|
|
||||||
EnumCount,
|
EnumCount,
|
||||||
InvalidEnum = EnumCount,
|
InvalidEnum = EnumCount,
|
||||||
|
|
|
@ -123,7 +123,11 @@ namespace dawn_native { namespace d3d12 {
|
||||||
ToBackend(GetDevice())->AllocateMemory(heapType, resourceDescriptor, bufferUsage));
|
ToBackend(GetDevice())->AllocateMemory(heapType, resourceDescriptor, bufferUsage));
|
||||||
|
|
||||||
if (GetDevice()->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting)) {
|
if (GetDevice()->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting)) {
|
||||||
DAWN_TRY(ClearBuffer(ClearValue::NonZero));
|
CommandRecordingContext* commandRecordingContext;
|
||||||
|
DAWN_TRY_ASSIGN(commandRecordingContext,
|
||||||
|
ToBackend(GetDevice())->GetPendingCommandContext());
|
||||||
|
|
||||||
|
DAWN_TRY(ClearBuffer(commandRecordingContext, uint8_t(1u)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
@ -310,18 +314,25 @@ namespace dawn_native { namespace d3d12 {
|
||||||
return mResourceAllocation.GetInfo().mMethod == allocationMethod;
|
return mResourceAllocation.GetInfo().mMethod == allocationMethod;
|
||||||
}
|
}
|
||||||
|
|
||||||
MaybeError Buffer::ClearBuffer(ClearValue clearValue) {
|
MaybeError Buffer::ClearBufferContentsToZero(CommandRecordingContext* commandContext) {
|
||||||
// TODO(jiawei.shao@intel.com): support buffer lazy-initialization to 0.
|
ASSERT(GetDevice()->IsToggleEnabled(Toggle::LazyClearBufferOnFirstUse));
|
||||||
ASSERT(clearValue == BufferBase::ClearValue::NonZero);
|
ASSERT(!IsDataInitialized());
|
||||||
constexpr uint8_t kClearBufferValue = 1u;
|
|
||||||
|
|
||||||
|
DAWN_TRY(ClearBuffer(commandContext, uint8_t(0u)));
|
||||||
|
SetIsDataInitialized();
|
||||||
|
GetDevice()->IncrementLazyClearCountForTesting();
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
MaybeError Buffer::ClearBuffer(CommandRecordingContext* commandContext, uint8_t clearValue) {
|
||||||
Device* device = ToBackend(GetDevice());
|
Device* device = ToBackend(GetDevice());
|
||||||
|
|
||||||
// The state of the buffers on UPLOAD heap must always be GENERIC_READ and cannot be
|
// The state of the buffers on UPLOAD heap must always be GENERIC_READ and cannot be
|
||||||
// changed away, so we can only clear such buffer with buffer mapping.
|
// changed away, so we can only clear such buffer with buffer mapping.
|
||||||
if (D3D12HeapType(GetUsage()) == D3D12_HEAP_TYPE_UPLOAD) {
|
if (D3D12HeapType(GetUsage()) == D3D12_HEAP_TYPE_UPLOAD) {
|
||||||
DAWN_TRY(MapInternal(true, "D3D12 map at clear buffer"));
|
DAWN_TRY(MapInternal(true, "D3D12 map at clear buffer"));
|
||||||
memset(mMappedData, kClearBufferValue, GetSize());
|
memset(mMappedData, clearValue, GetSize());
|
||||||
UnmapImpl();
|
UnmapImpl();
|
||||||
} else {
|
} else {
|
||||||
// TODO(jiawei.shao@intel.com): use ClearUnorderedAccessView*() when the buffer usage
|
// TODO(jiawei.shao@intel.com): use ClearUnorderedAccessView*() when the buffer usage
|
||||||
|
@ -331,10 +342,10 @@ namespace dawn_native { namespace d3d12 {
|
||||||
DAWN_TRY_ASSIGN(uploadHandle,
|
DAWN_TRY_ASSIGN(uploadHandle,
|
||||||
uploader->Allocate(GetSize(), device->GetPendingCommandSerial()));
|
uploader->Allocate(GetSize(), device->GetPendingCommandSerial()));
|
||||||
|
|
||||||
memset(uploadHandle.mappedBuffer, kClearBufferValue, GetSize());
|
memset(uploadHandle.mappedBuffer, clearValue, GetSize());
|
||||||
|
|
||||||
DAWN_TRY(device->CopyFromStagingToBuffer(uploadHandle.stagingBuffer,
|
device->CopyFromStagingToBufferImpl(commandContext, uploadHandle.stagingBuffer,
|
||||||
uploadHandle.startOffset, this, 0, GetSize()));
|
uploadHandle.startOffset, this, 0, GetSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
|
|
@ -44,6 +44,8 @@ namespace dawn_native { namespace d3d12 {
|
||||||
bool CheckAllocationMethodForTesting(AllocationMethod allocationMethod) const;
|
bool CheckAllocationMethodForTesting(AllocationMethod allocationMethod) const;
|
||||||
bool CheckIsResidentForTesting() const;
|
bool CheckIsResidentForTesting() const;
|
||||||
|
|
||||||
|
MaybeError ClearBufferContentsToZero(CommandRecordingContext* commandContext);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
~Buffer() override;
|
~Buffer() override;
|
||||||
// Dawn API
|
// Dawn API
|
||||||
|
@ -61,7 +63,7 @@ namespace dawn_native { namespace d3d12 {
|
||||||
D3D12_RESOURCE_BARRIER* barrier,
|
D3D12_RESOURCE_BARRIER* barrier,
|
||||||
wgpu::BufferUsage newUsage);
|
wgpu::BufferUsage newUsage);
|
||||||
|
|
||||||
MaybeError ClearBuffer(ClearValue clearValue);
|
MaybeError ClearBuffer(CommandRecordingContext* commandContext, uint8_t clearValue);
|
||||||
|
|
||||||
ResourceHeapAllocation mResourceAllocation;
|
ResourceHeapAllocation mResourceAllocation;
|
||||||
bool mFixedResourceState = false;
|
bool mFixedResourceState = false;
|
||||||
|
|
|
@ -336,16 +336,39 @@ namespace dawn_native { namespace d3d12 {
|
||||||
DAWN_TRY_ASSIGN(commandRecordingContext, GetPendingCommandContext());
|
DAWN_TRY_ASSIGN(commandRecordingContext, GetPendingCommandContext());
|
||||||
|
|
||||||
Buffer* dstBuffer = ToBackend(destination);
|
Buffer* dstBuffer = ToBackend(destination);
|
||||||
StagingBuffer* srcBuffer = ToBackend(source);
|
|
||||||
dstBuffer->TrackUsageAndTransitionNow(commandRecordingContext, wgpu::BufferUsage::CopyDst);
|
|
||||||
|
|
||||||
commandRecordingContext->GetCommandList()->CopyBufferRegion(
|
// TODO(jiawei.shao@intel.com): use Toggle::LazyClearResourceOnFirstUse when the support of
|
||||||
dstBuffer->GetD3D12Resource(), destinationOffset, srcBuffer->GetResource(),
|
// buffer lazy initialization is completed.
|
||||||
sourceOffset, size);
|
if (IsToggleEnabled(Toggle::LazyClearBufferOnFirstUse) && !dstBuffer->IsDataInitialized()) {
|
||||||
|
if (dstBuffer->IsFullBufferRange(destinationOffset, size)) {
|
||||||
|
dstBuffer->SetIsDataInitialized();
|
||||||
|
} else {
|
||||||
|
DAWN_TRY(dstBuffer->ClearBufferContentsToZero(commandRecordingContext));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CopyFromStagingToBufferImpl(commandRecordingContext, source, sourceOffset, destination,
|
||||||
|
destinationOffset, size);
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Device::CopyFromStagingToBufferImpl(CommandRecordingContext* commandContext,
|
||||||
|
StagingBufferBase* source,
|
||||||
|
uint64_t sourceOffset,
|
||||||
|
BufferBase* destination,
|
||||||
|
uint64_t destinationOffset,
|
||||||
|
uint64_t size) {
|
||||||
|
ASSERT(commandContext != nullptr);
|
||||||
|
Buffer* dstBuffer = ToBackend(destination);
|
||||||
|
StagingBuffer* srcBuffer = ToBackend(source);
|
||||||
|
dstBuffer->TrackUsageAndTransitionNow(commandContext, wgpu::BufferUsage::CopyDst);
|
||||||
|
|
||||||
|
commandContext->GetCommandList()->CopyBufferRegion(
|
||||||
|
dstBuffer->GetD3D12Resource(), destinationOffset, srcBuffer->GetResource(),
|
||||||
|
sourceOffset, size);
|
||||||
|
}
|
||||||
|
|
||||||
void Device::DeallocateMemory(ResourceHeapAllocation& allocation) {
|
void Device::DeallocateMemory(ResourceHeapAllocation& allocation) {
|
||||||
mResourceAllocatorManager->DeallocateMemory(allocation);
|
mResourceAllocatorManager->DeallocateMemory(allocation);
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,6 +91,13 @@ namespace dawn_native { namespace d3d12 {
|
||||||
uint64_t destinationOffset,
|
uint64_t destinationOffset,
|
||||||
uint64_t size) override;
|
uint64_t size) override;
|
||||||
|
|
||||||
|
void CopyFromStagingToBufferImpl(CommandRecordingContext* commandContext,
|
||||||
|
StagingBufferBase* source,
|
||||||
|
uint64_t sourceOffset,
|
||||||
|
BufferBase* destination,
|
||||||
|
uint64_t destinationOffset,
|
||||||
|
uint64_t size);
|
||||||
|
|
||||||
ResultOrError<ResourceHeapAllocation> AllocateMemory(
|
ResultOrError<ResourceHeapAllocation> AllocateMemory(
|
||||||
D3D12_HEAP_TYPE heapType,
|
D3D12_HEAP_TYPE heapType,
|
||||||
const D3D12_RESOURCE_DESC& resourceDescriptor,
|
const D3D12_RESOURCE_DESC& resourceDescriptor,
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
|
|
||||||
namespace dawn_native { namespace metal {
|
namespace dawn_native { namespace metal {
|
||||||
|
|
||||||
|
class CommandRecordingContext;
|
||||||
class Device;
|
class Device;
|
||||||
|
|
||||||
class Buffer : public BufferBase {
|
class Buffer : public BufferBase {
|
||||||
|
@ -29,6 +30,8 @@ namespace dawn_native { namespace metal {
|
||||||
static ResultOrError<Buffer*> Create(Device* device, const BufferDescriptor* descriptor);
|
static ResultOrError<Buffer*> Create(Device* device, const BufferDescriptor* descriptor);
|
||||||
id<MTLBuffer> GetMTLBuffer() const;
|
id<MTLBuffer> GetMTLBuffer() const;
|
||||||
|
|
||||||
|
void ClearBufferContentsToZero(CommandRecordingContext* commandContext);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using BufferBase::BufferBase;
|
using BufferBase::BufferBase;
|
||||||
MaybeError Initialize();
|
MaybeError Initialize();
|
||||||
|
@ -43,7 +46,7 @@ namespace dawn_native { namespace metal {
|
||||||
bool IsMapWritable() const override;
|
bool IsMapWritable() const override;
|
||||||
MaybeError MapAtCreationImpl(uint8_t** mappedPointer) override;
|
MaybeError MapAtCreationImpl(uint8_t** mappedPointer) override;
|
||||||
|
|
||||||
void ClearBuffer(BufferBase::ClearValue clearValue);
|
void ClearBuffer(CommandRecordingContext* commandContext, uint8_t clearValue);
|
||||||
|
|
||||||
id<MTLBuffer> mMtlBuffer = nil;
|
id<MTLBuffer> mMtlBuffer = nil;
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include "dawn_native/metal/BufferMTL.h"
|
#include "dawn_native/metal/BufferMTL.h"
|
||||||
|
|
||||||
#include "common/Math.h"
|
#include "common/Math.h"
|
||||||
|
#include "dawn_native/metal/CommandRecordingContext.h"
|
||||||
#include "dawn_native/metal/DeviceMTL.h"
|
#include "dawn_native/metal/DeviceMTL.h"
|
||||||
|
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
@ -87,7 +88,9 @@ namespace dawn_native { namespace metal {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GetDevice()->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting)) {
|
if (GetDevice()->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting)) {
|
||||||
ClearBuffer(BufferBase::ClearValue::NonZero);
|
CommandRecordingContext* commandContext =
|
||||||
|
ToBackend(GetDevice())->GetPendingCommandContext();
|
||||||
|
ClearBuffer(commandContext, uint8_t(1u));
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
@ -132,16 +135,21 @@ namespace dawn_native { namespace metal {
|
||||||
mMtlBuffer = nil;
|
mMtlBuffer = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Buffer::ClearBuffer(BufferBase::ClearValue clearValue) {
|
void Buffer::ClearBufferContentsToZero(CommandRecordingContext* commandContext) {
|
||||||
// TODO(jiawei.shao@intel.com): support buffer lazy-initialization to 0.
|
ASSERT(GetDevice()->IsToggleEnabled(Toggle::LazyClearBufferOnFirstUse));
|
||||||
ASSERT(clearValue == BufferBase::ClearValue::NonZero);
|
ASSERT(!IsDataInitialized());
|
||||||
const uint8_t clearBufferValue = 1;
|
|
||||||
|
|
||||||
Device* device = ToBackend(GetDevice());
|
ClearBuffer(commandContext, uint8_t(0u));
|
||||||
CommandRecordingContext* commandContext = device->GetPendingCommandContext();
|
|
||||||
|
SetIsDataInitialized();
|
||||||
|
GetDevice()->IncrementLazyClearCountForTesting();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Buffer::ClearBuffer(CommandRecordingContext* commandContext, uint8_t clearValue) {
|
||||||
|
ASSERT(commandContext != nullptr);
|
||||||
[commandContext->EnsureBlit() fillBuffer:mMtlBuffer
|
[commandContext->EnsureBlit() fillBuffer:mMtlBuffer
|
||||||
range:NSMakeRange(0, GetSize())
|
range:NSMakeRange(0, GetSize())
|
||||||
value:clearBufferValue];
|
value:clearValue];
|
||||||
}
|
}
|
||||||
|
|
||||||
}} // namespace dawn_native::metal
|
}} // namespace dawn_native::metal
|
||||||
|
|
|
@ -254,6 +254,17 @@ namespace dawn_native { namespace metal {
|
||||||
// this function.
|
// this function.
|
||||||
ASSERT(size != 0);
|
ASSERT(size != 0);
|
||||||
|
|
||||||
|
// TODO(jiawei.shao@intel.com): use Toggle::LazyClearResourceOnFirstUse when the support of
|
||||||
|
// buffer lazy initialization is completed.
|
||||||
|
if (IsToggleEnabled(Toggle::LazyClearBufferOnFirstUse) &&
|
||||||
|
!destination->IsDataInitialized()) {
|
||||||
|
if (destination->IsFullBufferRange(destinationOffset, size)) {
|
||||||
|
destination->SetIsDataInitialized();
|
||||||
|
} else {
|
||||||
|
ToBackend(destination)->ClearBufferContentsToZero(GetPendingCommandContext());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
id<MTLBuffer> uploadBuffer = ToBackend(source)->GetBufferHandle();
|
id<MTLBuffer> uploadBuffer = ToBackend(source)->GetBufferHandle();
|
||||||
id<MTLBuffer> buffer = ToBackend(destination)->GetMTLBuffer();
|
id<MTLBuffer> buffer = ToBackend(destination)->GetMTLBuffer();
|
||||||
[GetPendingCommandContext()->EnsureBlit() copyFromBuffer:uploadBuffer
|
[GetPendingCommandContext()->EnsureBlit() copyFromBuffer:uploadBuffer
|
||||||
|
|
|
@ -197,6 +197,10 @@ namespace dawn_native { namespace null {
|
||||||
BufferBase* destination,
|
BufferBase* destination,
|
||||||
uint64_t destinationOffset,
|
uint64_t destinationOffset,
|
||||||
uint64_t size) {
|
uint64_t size) {
|
||||||
|
if (IsToggleEnabled(Toggle::LazyClearBufferOnFirstUse)) {
|
||||||
|
destination->SetIsDataInitialized();
|
||||||
|
}
|
||||||
|
|
||||||
auto operation = std::make_unique<CopyFromStagingToBufferOperation>();
|
auto operation = std::make_unique<CopyFromStagingToBufferOperation>();
|
||||||
operation->staging = source;
|
operation->staging = source;
|
||||||
operation->destination = ToBackend(destination);
|
operation->destination = ToBackend(destination);
|
||||||
|
|
|
@ -24,7 +24,7 @@ namespace dawn_native { namespace opengl {
|
||||||
: BufferBase(device, descriptor) {
|
: BufferBase(device, descriptor) {
|
||||||
// TODO(cwallez@chromium.org): Have a global "zero" buffer instead of creating a new 4-byte
|
// TODO(cwallez@chromium.org): Have a global "zero" buffer instead of creating a new 4-byte
|
||||||
// buffer?
|
// buffer?
|
||||||
uint64_t size = std::max(GetSize(), uint64_t(4u));
|
uint64_t size = GetAppliedSize();
|
||||||
|
|
||||||
device->gl.GenBuffers(1, &mBuffer);
|
device->gl.GenBuffers(1, &mBuffer);
|
||||||
device->gl.BindBuffer(GL_ARRAY_BUFFER, mBuffer);
|
device->gl.BindBuffer(GL_ARRAY_BUFFER, mBuffer);
|
||||||
|
@ -45,6 +45,27 @@ namespace dawn_native { namespace opengl {
|
||||||
return mBuffer;
|
return mBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint64_t Buffer::GetAppliedSize() const {
|
||||||
|
// TODO(cwallez@chromium.org): Have a global "zero" buffer instead of creating a new 4-byte
|
||||||
|
// buffer?
|
||||||
|
return std::max(GetSize(), uint64_t(4u));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Buffer::ClearBufferContentsToZero() {
|
||||||
|
ASSERT(GetDevice()->IsToggleEnabled(Toggle::LazyClearBufferOnFirstUse));
|
||||||
|
ASSERT(!IsDataInitialized());
|
||||||
|
|
||||||
|
const uint64_t size = GetAppliedSize();
|
||||||
|
Device* device = ToBackend(GetDevice());
|
||||||
|
|
||||||
|
const std::vector<uint8_t> clearValues(size, 0u);
|
||||||
|
device->gl.BindBuffer(GL_ARRAY_BUFFER, mBuffer);
|
||||||
|
device->gl.BufferSubData(GL_ARRAY_BUFFER, 0, size, clearValues.data());
|
||||||
|
|
||||||
|
SetIsDataInitialized();
|
||||||
|
device->IncrementLazyClearCountForTesting();
|
||||||
|
}
|
||||||
|
|
||||||
bool Buffer::IsMapWritable() const {
|
bool Buffer::IsMapWritable() const {
|
||||||
// TODO(enga): All buffers in GL can be mapped. Investigate if mapping them will cause the
|
// TODO(enga): All buffers in GL can be mapped. Investigate if mapping them will cause the
|
||||||
// driver to migrate it to shared memory.
|
// driver to migrate it to shared memory.
|
||||||
|
|
|
@ -29,6 +29,8 @@ namespace dawn_native { namespace opengl {
|
||||||
|
|
||||||
GLuint GetHandle() const;
|
GLuint GetHandle() const;
|
||||||
|
|
||||||
|
void ClearBufferContentsToZero();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
~Buffer() override;
|
~Buffer() override;
|
||||||
// Dawn API
|
// Dawn API
|
||||||
|
@ -40,6 +42,7 @@ namespace dawn_native { namespace opengl {
|
||||||
bool IsMapWritable() const override;
|
bool IsMapWritable() const override;
|
||||||
MaybeError MapAtCreationImpl(uint8_t** mappedPointer) override;
|
MaybeError MapAtCreationImpl(uint8_t** mappedPointer) override;
|
||||||
void* GetMappedPointerImpl() override;
|
void* GetMappedPointerImpl() override;
|
||||||
|
uint64_t GetAppliedSize() const;
|
||||||
|
|
||||||
GLuint mBuffer = 0;
|
GLuint mBuffer = 0;
|
||||||
void* mMappedData = nullptr;
|
void* mMappedData = nullptr;
|
||||||
|
|
|
@ -44,6 +44,17 @@ namespace dawn_native { namespace opengl {
|
||||||
size_t size) {
|
size_t size) {
|
||||||
const OpenGLFunctions& gl = ToBackend(GetDevice())->gl;
|
const OpenGLFunctions& gl = ToBackend(GetDevice())->gl;
|
||||||
|
|
||||||
|
// TODO(jiawei.shao@intel.com): use Toggle::LazyClearResourceOnFirstUse when the support of
|
||||||
|
// buffer lazy initialization is completed.
|
||||||
|
if (GetDevice()->IsToggleEnabled(Toggle::LazyClearBufferOnFirstUse) &&
|
||||||
|
!buffer->IsDataInitialized()) {
|
||||||
|
if (buffer->IsFullBufferRange(bufferOffset, size)) {
|
||||||
|
buffer->SetIsDataInitialized();
|
||||||
|
} else {
|
||||||
|
ToBackend(buffer)->ClearBufferContentsToZero();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
gl.BindBuffer(GL_ARRAY_BUFFER, ToBackend(buffer)->GetHandle());
|
gl.BindBuffer(GL_ARRAY_BUFFER, ToBackend(buffer)->GetHandle());
|
||||||
gl.BufferSubData(GL_ARRAY_BUFFER, bufferOffset, size, data);
|
gl.BufferSubData(GL_ARRAY_BUFFER, bufferOffset, size, data);
|
||||||
return {};
|
return {};
|
||||||
|
|
|
@ -166,7 +166,7 @@ namespace dawn_native { namespace vulkan {
|
||||||
"vkBindBufferMemory"));
|
"vkBindBufferMemory"));
|
||||||
|
|
||||||
if (device->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting)) {
|
if (device->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting)) {
|
||||||
ClearBuffer(device->GetPendingRecordingContext(), ClearValue::NonZero);
|
ClearBuffer(device->GetPendingRecordingContext(), 0x01010101);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
@ -287,20 +287,25 @@ namespace dawn_native { namespace vulkan {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Buffer::ClearBuffer(CommandRecordingContext* recordingContext, ClearValue clearValue) {
|
void Buffer::ClearBufferContentsToZero(CommandRecordingContext* recordingContext) {
|
||||||
|
ASSERT(GetDevice()->IsToggleEnabled(Toggle::LazyClearBufferOnFirstUse));
|
||||||
|
ASSERT(!IsDataInitialized());
|
||||||
|
|
||||||
|
ClearBuffer(recordingContext, 0u);
|
||||||
|
|
||||||
|
SetIsDataInitialized();
|
||||||
|
GetDevice()->IncrementLazyClearCountForTesting();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Buffer::ClearBuffer(CommandRecordingContext* recordingContext, uint32_t clearValue) {
|
||||||
ASSERT(recordingContext != nullptr);
|
ASSERT(recordingContext != nullptr);
|
||||||
|
|
||||||
// TODO(jiawei.shao@intel.com): support buffer lazy-initialization to 0.
|
|
||||||
ASSERT(clearValue == BufferBase::ClearValue::NonZero);
|
|
||||||
|
|
||||||
constexpr uint32_t kClearBufferValue = 0x01010101;
|
|
||||||
|
|
||||||
TransitionUsageNow(recordingContext, wgpu::BufferUsage::CopyDst);
|
TransitionUsageNow(recordingContext, wgpu::BufferUsage::CopyDst);
|
||||||
|
|
||||||
Device* device = ToBackend(GetDevice());
|
Device* device = ToBackend(GetDevice());
|
||||||
// TODO(jiawei.shao@intel.com): find out why VK_WHOLE_SIZE doesn't work on old Windows Intel
|
// TODO(jiawei.shao@intel.com): find out why VK_WHOLE_SIZE doesn't work on old Windows Intel
|
||||||
// Vulkan drivers.
|
// Vulkan drivers.
|
||||||
device->fn.CmdFillBuffer(recordingContext->commandBuffer, mHandle, 0, GetSize(),
|
device->fn.CmdFillBuffer(recordingContext->commandBuffer, mHandle, 0, GetSize(),
|
||||||
kClearBufferValue);
|
clearValue);
|
||||||
}
|
}
|
||||||
}} // namespace dawn_native::vulkan
|
}} // namespace dawn_native::vulkan
|
||||||
|
|
|
@ -45,11 +45,13 @@ namespace dawn_native { namespace vulkan {
|
||||||
VkPipelineStageFlags* srcStages,
|
VkPipelineStageFlags* srcStages,
|
||||||
VkPipelineStageFlags* dstStages);
|
VkPipelineStageFlags* dstStages);
|
||||||
|
|
||||||
|
void ClearBufferContentsToZero(CommandRecordingContext* recordingContext);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
~Buffer() override;
|
~Buffer() override;
|
||||||
using BufferBase::BufferBase;
|
using BufferBase::BufferBase;
|
||||||
MaybeError Initialize();
|
MaybeError Initialize();
|
||||||
void ClearBuffer(CommandRecordingContext* recordingContext, ClearValue clearValue);
|
void ClearBuffer(CommandRecordingContext* recordingContext, uint32_t clearValue);
|
||||||
|
|
||||||
// Dawn API
|
// Dawn API
|
||||||
MaybeError MapReadAsyncImpl(uint32_t serial) override;
|
MaybeError MapReadAsyncImpl(uint32_t serial) override;
|
||||||
|
|
|
@ -590,6 +590,19 @@ namespace dawn_native { namespace vulkan {
|
||||||
// calling this function.
|
// calling this function.
|
||||||
ASSERT(size != 0);
|
ASSERT(size != 0);
|
||||||
|
|
||||||
|
CommandRecordingContext* recordingContext = GetPendingRecordingContext();
|
||||||
|
|
||||||
|
// TODO(jiawei.shao@intel.com): use Toggle::LazyClearResourceOnFirstUse when the support of
|
||||||
|
// buffer lazy initialization is completed.
|
||||||
|
if (IsToggleEnabled(Toggle::LazyClearBufferOnFirstUse) &&
|
||||||
|
!destination->IsDataInitialized()) {
|
||||||
|
if (destination->IsFullBufferRange(destinationOffset, size)) {
|
||||||
|
destination->SetIsDataInitialized();
|
||||||
|
} else {
|
||||||
|
ToBackend(destination)->ClearBufferContentsToZero(recordingContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Insert memory barrier to ensure host write operations are made visible before
|
// Insert memory barrier to ensure host write operations are made visible before
|
||||||
// copying from the staging buffer. However, this barrier can be removed (see note below).
|
// copying from the staging buffer. However, this barrier can be removed (see note below).
|
||||||
//
|
//
|
||||||
|
@ -599,7 +612,6 @@ namespace dawn_native { namespace vulkan {
|
||||||
|
|
||||||
// Insert pipeline barrier to ensure correct ordering with previous memory operations on the
|
// Insert pipeline barrier to ensure correct ordering with previous memory operations on the
|
||||||
// buffer.
|
// buffer.
|
||||||
CommandRecordingContext* recordingContext = GetPendingRecordingContext();
|
|
||||||
ToBackend(destination)->TransitionUsageNow(recordingContext, wgpu::BufferUsage::CopyDst);
|
ToBackend(destination)->TransitionUsageNow(recordingContext, wgpu::BufferUsage::CopyDst);
|
||||||
|
|
||||||
VkBufferCopy copy;
|
VkBufferCopy copy;
|
||||||
|
|
|
@ -259,6 +259,7 @@ source_set("dawn_end2end_tests_sources") {
|
||||||
"end2end/BasicTests.cpp",
|
"end2end/BasicTests.cpp",
|
||||||
"end2end/BindGroupTests.cpp",
|
"end2end/BindGroupTests.cpp",
|
||||||
"end2end/BufferTests.cpp",
|
"end2end/BufferTests.cpp",
|
||||||
|
"end2end/BufferZeroInitTests.cpp",
|
||||||
"end2end/ClipSpaceTests.cpp",
|
"end2end/ClipSpaceTests.cpp",
|
||||||
"end2end/ColorStateTests.cpp",
|
"end2end/ColorStateTests.cpp",
|
||||||
"end2end/CompressedTextureFormatTests.cpp",
|
"end2end/CompressedTextureFormatTests.cpp",
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
// Copyright 2020 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"
|
||||||
|
|
||||||
|
#define EXPECT_LAZY_CLEAR(N, statement) \
|
||||||
|
do { \
|
||||||
|
if (UsesWire()) { \
|
||||||
|
statement; \
|
||||||
|
} else { \
|
||||||
|
size_t lazyClearsBefore = dawn_native::GetLazyClearCountForTesting(device.Get()); \
|
||||||
|
statement; \
|
||||||
|
size_t lazyClearsAfter = dawn_native::GetLazyClearCountForTesting(device.Get()); \
|
||||||
|
EXPECT_EQ(N, lazyClearsAfter - lazyClearsBefore); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
class BufferZeroInitTest : public DawnTest {
|
||||||
|
public:
|
||||||
|
wgpu::Buffer CreateBuffer(uint64_t size, wgpu::BufferUsage usage) {
|
||||||
|
wgpu::BufferDescriptor descriptor;
|
||||||
|
descriptor.size = size;
|
||||||
|
descriptor.usage = usage;
|
||||||
|
return device.CreateBuffer(&descriptor);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Test that calling writeBuffer to overwrite the entire buffer doesn't need to lazily initialize
|
||||||
|
// the destination buffer.
|
||||||
|
TEST_P(BufferZeroInitTest, WriteBufferToEntireBuffer) {
|
||||||
|
constexpr uint32_t kBufferSize = 8u;
|
||||||
|
constexpr wgpu::BufferUsage kBufferUsage =
|
||||||
|
wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst;
|
||||||
|
wgpu::Buffer buffer = CreateBuffer(kBufferSize, kBufferUsage);
|
||||||
|
|
||||||
|
constexpr std::array<uint32_t, kBufferSize / sizeof(uint32_t)> kExpectedData = {
|
||||||
|
{0x02020202u, 0x02020202u}};
|
||||||
|
EXPECT_LAZY_CLEAR(0u, queue.WriteBuffer(buffer, 0, kExpectedData.data(), kBufferSize));
|
||||||
|
|
||||||
|
EXPECT_BUFFER_U32_RANGE_EQ(kExpectedData.data(), buffer, 0, kBufferSize / sizeof(uint32_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that calling writeBuffer to overwrite a part of buffer needs to lazily initialize the
|
||||||
|
// destination buffer.
|
||||||
|
TEST_P(BufferZeroInitTest, WriteBufferToSubBuffer) {
|
||||||
|
constexpr uint32_t kBufferSize = 8u;
|
||||||
|
constexpr wgpu::BufferUsage kBufferUsage =
|
||||||
|
wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst;
|
||||||
|
|
||||||
|
constexpr uint32_t kCopyValue = 0x02020202u;
|
||||||
|
|
||||||
|
// offset == 0
|
||||||
|
{
|
||||||
|
wgpu::Buffer buffer = CreateBuffer(kBufferSize, kBufferUsage);
|
||||||
|
|
||||||
|
constexpr uint32_t kCopyOffset = 0u;
|
||||||
|
EXPECT_LAZY_CLEAR(1u,
|
||||||
|
queue.WriteBuffer(buffer, kCopyOffset, &kCopyValue, sizeof(kCopyValue)));
|
||||||
|
|
||||||
|
EXPECT_BUFFER_U32_EQ(kCopyValue, buffer, kCopyOffset);
|
||||||
|
EXPECT_BUFFER_U32_EQ(0, buffer, kBufferSize - sizeof(kCopyValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
// offset > 0
|
||||||
|
{
|
||||||
|
wgpu::Buffer buffer = CreateBuffer(kBufferSize, kBufferUsage);
|
||||||
|
|
||||||
|
constexpr uint32_t kCopyOffset = 4u;
|
||||||
|
EXPECT_LAZY_CLEAR(1u,
|
||||||
|
queue.WriteBuffer(buffer, kCopyOffset, &kCopyValue, sizeof(kCopyValue)));
|
||||||
|
|
||||||
|
EXPECT_BUFFER_U32_EQ(0, buffer, 0);
|
||||||
|
EXPECT_BUFFER_U32_EQ(kCopyValue, buffer, kCopyOffset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DAWN_INSTANTIATE_TEST(BufferZeroInitTest,
|
||||||
|
D3D12Backend({"nonzero_clear_resources_on_creation_for_testing",
|
||||||
|
"lazy_clear_buffer_on_first_use"}),
|
||||||
|
MetalBackend({"nonzero_clear_resources_on_creation_for_testing",
|
||||||
|
"lazy_clear_buffer_on_first_use"}),
|
||||||
|
OpenGLBackend({"nonzero_clear_resources_on_creation_for_testing",
|
||||||
|
"lazy_clear_buffer_on_first_use"}),
|
||||||
|
VulkanBackend({"nonzero_clear_resources_on_creation_for_testing",
|
||||||
|
"lazy_clear_buffer_on_first_use"}));
|
Loading…
Reference in New Issue