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:
Jiawei Shao 2020-07-06 08:24:30 +00:00 committed by Commit Bot service account
parent 59ccb1f6de
commit 80f927d763
20 changed files with 392 additions and 142 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -43,6 +43,7 @@ namespace dawn_native {
UseD3D12SmallShaderVisibleHeapForTesting, UseD3D12SmallShaderVisibleHeapForTesting,
UseDXC, UseDXC,
DisableRobustness, DisableRobustness,
LazyClearBufferOnFirstUse,
EnumCount, EnumCount,
InvalidEnum = EnumCount, InvalidEnum = EnumCount,

View File

@ -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 {};

View File

@ -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;

View File

@ -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);
} }

View File

@ -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,

View File

@ -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;
}; };

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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.

View File

@ -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;

View File

@ -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 {};

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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",

View File

@ -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"}));