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,111 +29,116 @@ 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 "
"not support MTLStoreActionStoreAndMultisampleResolve. To support StoreOp::Store on " "not support MTLStoreActionStoreAndMultisampleResolve. To support StoreOp::Store on "
"those platforms, we should do MSAA resolve in another render pass after ending the " "those platforms, we should do MSAA resolve in another render pass after ending the "
"previous one.", "previous one.",
"https://crbug.com/dawn/56"}}, "https://crbug.com/dawn/56"}},
{Toggle::NonzeroClearResourcesOnCreationForTesting, {Toggle::NonzeroClearResourcesOnCreationForTesting,
{"nonzero_clear_resources_on_creation_for_testing", {"nonzero_clear_resources_on_creation_for_testing",
"Clears texture to full 1 bits as soon as they are created, but doesn't update " "Clears texture to full 1 bits as soon as they are created, but doesn't update "
"the tracking state of the texture. This way we can test the logic of clearing " "the tracking state of the texture. This way we can test the logic of clearing "
"textures that use recycled memory.", "textures that use recycled memory.",
"https://crbug.com/dawn/145"}}, "https://crbug.com/dawn/145"}},
{Toggle::AlwaysResolveIntoZeroLevelAndLayer, {Toggle::AlwaysResolveIntoZeroLevelAndLayer,
{"always_resolve_into_zero_level_and_layer", {"always_resolve_into_zero_level_and_layer",
"When the resolve target is a texture view that is created on the non-zero level or " "When the resolve target is a texture view that is created on the non-zero level or "
"layer of a texture, we first resolve into a temporarily 2D texture with only one " "layer of a texture, we first resolve into a temporarily 2D texture with only one "
"mipmap level and one array layer, and copy the result of MSAA resolve into the " "mipmap level and one array layer, and copy the result of MSAA resolve into the "
"true resolve target. This workaround is enabled by default on the Metal drivers " "true resolve target. This workaround is enabled by default on the Metal drivers "
"that have bugs when setting non-zero resolveLevel or resolveSlice.", "that have bugs when setting non-zero resolveLevel or resolveSlice.",
"https://crbug.com/dawn/56"}}, "https://crbug.com/dawn/56"}},
{Toggle::LazyClearResourceOnFirstUse, {Toggle::LazyClearResourceOnFirstUse,
{"lazy_clear_resource_on_first_use", {"lazy_clear_resource_on_first_use",
"Clears resource to zero on first usage. This initializes the resource " "Clears resource to zero on first usage. This initializes the resource "
"so that no dirty bits from recycled memory is present in the new resource.", "so that no dirty bits from recycled memory is present in the new resource.",
"https://crbug.com/dawn/145"}}, "https://crbug.com/dawn/145"}},
{Toggle::TurnOffVsync, {Toggle::TurnOffVsync,
{"turn_off_vsync", {"turn_off_vsync",
"Turn off vsync when rendering. In order to do performance test or run perf tests, " "Turn off vsync when rendering. In order to do performance test or run perf tests, "
"turn off vsync so that the fps can exeed 60.", "turn off vsync so that the fps can exeed 60.",
"https://crbug.com/dawn/237"}}, "https://crbug.com/dawn/237"}},
{Toggle::UseTemporaryBufferInCompressedTextureToTextureCopy, {Toggle::UseTemporaryBufferInCompressedTextureToTextureCopy,
{"use_temporary_buffer_in_texture_to_texture_copy", {"use_temporary_buffer_in_texture_to_texture_copy",
"Split texture-to-texture copy into two copies: copy from source texture into a " "Split texture-to-texture copy into two copies: copy from source texture into a "
"temporary buffer, and copy from the temporary buffer into the destination texture " "temporary buffer, and copy from the temporary buffer into the destination texture "
"when copying between compressed textures that don't have block-aligned sizes. This " "when copying between compressed textures that don't have block-aligned sizes. This "
"workaround is enabled by default on all Vulkan drivers to solve an issue in the " "workaround is enabled by default on all Vulkan drivers to solve an issue in the "
"Vulkan SPEC about the texture-to-texture copies with compressed formats. See #1005 " "Vulkan SPEC about the texture-to-texture copies with compressed formats. See #1005 "
"(https://github.com/KhronosGroup/Vulkan-Docs/issues/1005) for more details.", "(https://github.com/KhronosGroup/Vulkan-Docs/issues/1005) for more details.",
"https://crbug.com/dawn/42"}}, "https://crbug.com/dawn/42"}},
{Toggle::UseD3D12ResourceHeapTier2, {Toggle::UseD3D12ResourceHeapTier2,
{"use_d3d12_resource_heap_tier2", {"use_d3d12_resource_heap_tier2",
"Enable support for resource heap tier 2. Resource heap tier 2 allows mixing of " "Enable support for resource heap tier 2. Resource heap tier 2 allows mixing of "
"texture and buffers in the same heap. This allows better heap re-use and reduces " "texture and buffers in the same heap. This allows better heap re-use and reduces "
"fragmentation.", "fragmentation.",
"https://crbug.com/dawn/27"}}, "https://crbug.com/dawn/27"}},
{Toggle::UseD3D12RenderPass, {Toggle::UseD3D12RenderPass,
{"use_d3d12_render_pass", {"use_d3d12_render_pass",
"Use the D3D12 render pass API introduced in Windows build 1809 by default. On " "Use the D3D12 render pass API introduced in Windows build 1809 by default. On "
"versions of Windows prior to build 1809, or when this toggle is turned off, Dawn " "versions of Windows prior to build 1809, or when this toggle is turned off, Dawn "
"will emulate a render pass.", "will emulate a render pass.",
"https://crbug.com/dawn/36"}}, "https://crbug.com/dawn/36"}},
{Toggle::UseD3D12ResidencyManagement, {Toggle::UseD3D12ResidencyManagement,
{"use_d3d12_residency_management", {"use_d3d12_residency_management",
"Enable residency management. This allows page-in and page-out of resource heaps in " "Enable residency management. This allows page-in and page-out of resource heaps in "
"GPU memory. This component improves overcommitted performance by keeping the most " "GPU memory. This component improves overcommitted performance by keeping the most "
"recently used resources local to the GPU. Turning this component off can cause " "recently used resources local to the GPU. Turning this component off can cause "
"allocation failures when application memory exceeds physical device memory.", "allocation failures when application memory exceeds physical device memory.",
"https://crbug.com/dawn/193"}}, "https://crbug.com/dawn/193"}},
{Toggle::SkipValidation, {Toggle::SkipValidation,
{"skip_validation", "Skip expensive validation of Dawn commands.", {"skip_validation", "Skip expensive validation of Dawn commands.",
"https://crbug.com/dawn/271"}}, "https://crbug.com/dawn/271"}},
{Toggle::UseSpvc, {Toggle::UseSpvc,
{"use_spvc", {"use_spvc",
"Enable use of spvc for shader compilation, instead of accessing spirv_cross " "Enable use of spvc for shader compilation, instead of accessing spirv_cross "
"directly.", "directly.",
"https://crbug.com/dawn/288"}}, "https://crbug.com/dawn/288"}},
{Toggle::UseSpvcParser, {Toggle::UseSpvcParser,
{"use_spvc_parser", {"use_spvc_parser",
"Enable usage of spvc's internal parsing and IR generation code, instead of " "Enable usage of spvc's internal parsing and IR generation code, instead of "
"spirv_cross's.", "spirv_cross's.",
"https://crbug.com/dawn/288"}}, "https://crbug.com/dawn/288"}},
{Toggle::VulkanUseD32S8, {Toggle::VulkanUseD32S8,
{"vulkan_use_d32s8", {"vulkan_use_d32s8",
"Vulkan mandates support of either D32_FLOAT_S8 or D24_UNORM_S8. When available the " "Vulkan mandates support of either D32_FLOAT_S8 or D24_UNORM_S8. When available the "
"backend will use D32S8 (toggle to on) but setting the toggle to off will make it" "backend will use D32S8 (toggle to on) but setting the toggle to off will make it"
"use the D24S8 format when possible.", "use the D24S8 format when possible.",
"https://crbug.com/dawn/286"}}, "https://crbug.com/dawn/286"}},
{Toggle::MetalDisableSamplerCompare, {Toggle::MetalDisableSamplerCompare,
{"metal_disable_sampler_compare", {"metal_disable_sampler_compare",
"Disables the use of sampler compare on Metal. This is unsupported before A9 " "Disables the use of sampler compare on Metal. This is unsupported before A9 "
"processors.", "processors.",
"https://crbug.com/dawn/342"}}, "https://crbug.com/dawn/342"}},
{Toggle::DisableBaseVertex, {Toggle::DisableBaseVertex,
{"disable_base_vertex", {"disable_base_vertex",
"Disables the use of non-zero base vertex which is unsupported on some platforms.", "Disables the use of non-zero base vertex which is unsupported on some platforms.",
"https://crbug.com/dawn/343"}}, "https://crbug.com/dawn/343"}},
{Toggle::DisableBaseInstance, {Toggle::DisableBaseInstance,
{"disable_base_instance", {"disable_base_instance",
"Disables the use of non-zero base instance which is unsupported on some " "Disables the use of non-zero base instance which is unsupported on some "
"platforms.", "platforms.",
"https://crbug.com/dawn/343"}}, "https://crbug.com/dawn/343"}},
{Toggle::UseD3D12SmallShaderVisibleHeapForTesting, {Toggle::UseD3D12SmallShaderVisibleHeapForTesting,
{"use_d3d12_small_shader_visible_heap", {"use_d3d12_small_shader_visible_heap",
"Enable use of a small D3D12 shader visible heap, instead of using a large one by " "Enable use of a small D3D12 shader visible heap, instead of using a large one by "
"default. This setting is used to test bindgroup encoding.", "default. This setting is used to test bindgroup encoding.",
"https://crbug.com/dawn/155"}}, "https://crbug.com/dawn/155"}},
{Toggle::UseDXC, {Toggle::UseDXC,
{"use_dxc", "Use DXC instead of FXC for compiling HLSL", {"use_dxc", "Use DXC instead of FXC for compiling HLSL",
"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"}));