D3D12: Enable nonzero_clear_resources_on_creation_for_testing on buffer

This patch enables nonzero_clear_resources_on_creation_for_testing
toggle on buffer on D3D12 backends as a preparation of supporting
buffer lazy-initialization in Dawn.

BUG=dawn:414
TEST=dawn_end2end_tests

Change-Id: Id4f45ff5ccf906692c3855451b120aa56f68c7a9
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/23142
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Jiawei Shao <jiawei.shao@intel.com>
This commit is contained in:
Jiawei Shao 2020-06-13 00:59:13 +00:00 committed by Commit Bot service account
parent 1663b1d04d
commit 2ef2be746d
3 changed files with 78 additions and 27 deletions

View File

@ -17,6 +17,7 @@
#include "common/Assert.h" #include "common/Assert.h"
#include "common/Constants.h" #include "common/Constants.h"
#include "common/Math.h" #include "common/Math.h"
#include "dawn_native/DynamicUploader.h"
#include "dawn_native/d3d12/CommandRecordingContext.h" #include "dawn_native/d3d12/CommandRecordingContext.h"
#include "dawn_native/d3d12/D3D12Error.h" #include "dawn_native/d3d12/D3D12Error.h"
#include "dawn_native/d3d12/DeviceD3D12.h" #include "dawn_native/d3d12/DeviceD3D12.h"
@ -120,6 +121,11 @@ namespace dawn_native { namespace d3d12 {
DAWN_TRY_ASSIGN( DAWN_TRY_ASSIGN(
mResourceAllocation, mResourceAllocation,
ToBackend(GetDevice())->AllocateMemory(heapType, resourceDescriptor, bufferUsage)); ToBackend(GetDevice())->AllocateMemory(heapType, resourceDescriptor, bufferUsage));
if (GetDevice()->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting)) {
DAWN_TRY(ClearBuffer(ClearValue::NonZero));
}
return {}; return {};
} }
@ -238,57 +244,60 @@ namespace dawn_native { namespace d3d12 {
return (GetUsage() & (wgpu::BufferUsage::MapRead | wgpu::BufferUsage::MapWrite)) != 0; return (GetUsage() & (wgpu::BufferUsage::MapRead | wgpu::BufferUsage::MapWrite)) != 0;
} }
MaybeError Buffer::MapAtCreationImpl(uint8_t** mappedPointer) { MaybeError Buffer::MapBufferInternal(D3D12_RANGE mappedRange,
void** mappedPointer,
const char* contextInfo) {
// The mapped buffer can be accessed at any time, so it must be locked to ensure it is never // The mapped buffer can be accessed at any time, so it must be locked to ensure it is never
// evicted. This buffer should already have been made resident when it was created. // evicted. This buffer should already have been made resident when it was created.
Heap* heap = ToBackend(mResourceAllocation.GetResourceHeap()); Heap* heap = ToBackend(mResourceAllocation.GetResourceHeap());
DAWN_TRY(ToBackend(GetDevice())->GetResidencyManager()->LockAllocation(heap)); DAWN_TRY(ToBackend(GetDevice())->GetResidencyManager()->LockAllocation(heap));
DAWN_TRY(
CheckHRESULT(GetD3D12Resource()->Map(0, &mappedRange, mappedPointer), contextInfo));
return {};
}
void Buffer::UnmapBufferInternal(D3D12_RANGE mappedRange) {
GetD3D12Resource()->Unmap(0, &mappedRange);
// When buffers are mapped, they are locked to keep them in resident memory. We must unlock
// them when they are unmapped.
Heap* heap = ToBackend(mResourceAllocation.GetResourceHeap());
ToBackend(GetDevice())->GetResidencyManager()->UnlockAllocation(heap);
}
MaybeError Buffer::MapAtCreationImpl(uint8_t** mappedPointer) {
mWrittenMappedRange = {0, static_cast<size_t>(GetSize())}; mWrittenMappedRange = {0, static_cast<size_t>(GetSize())};
DAWN_TRY(CheckHRESULT(GetD3D12Resource()->Map(0, &mWrittenMappedRange, DAWN_TRY(MapBufferInternal(mWrittenMappedRange, reinterpret_cast<void**>(mappedPointer),
reinterpret_cast<void**>(mappedPointer)),
"D3D12 map at creation")); "D3D12 map at creation"));
mMappedData = reinterpret_cast<char*>(mappedPointer); mMappedData = reinterpret_cast<char*>(mappedPointer);
return {}; return {};
} }
MaybeError Buffer::MapReadAsyncImpl(uint32_t serial) { MaybeError Buffer::MapReadAsyncImpl(uint32_t serial) {
// The mapped buffer can be accessed at any time, so we must make the buffer resident and
// lock it to ensure it is never evicted.
Heap* heap = ToBackend(mResourceAllocation.GetResourceHeap());
DAWN_TRY(ToBackend(GetDevice())->GetResidencyManager()->LockAllocation(heap));
mWrittenMappedRange = {}; mWrittenMappedRange = {};
D3D12_RANGE readRange = {0, static_cast<size_t>(GetSize())}; D3D12_RANGE readRange = {0, static_cast<size_t>(GetSize())};
DAWN_TRY(CheckHRESULT( DAWN_TRY(MapBufferInternal(readRange, reinterpret_cast<void**>(&mMappedData),
GetD3D12Resource()->Map(0, &readRange, reinterpret_cast<void**>(&mMappedData)),
"D3D12 map read async")); "D3D12 map read async"));
// There is no need to transition the resource to a new state: D3D12 seems to make the GPU // There is no need to transition the resource to a new state: D3D12 seems to make the GPU
// writes available when the fence is passed. // writes available when the fence is passed.
return {}; return {};
} }
MaybeError Buffer::MapWriteAsyncImpl(uint32_t serial) { MaybeError Buffer::MapWriteAsyncImpl(uint32_t serial) {
// The mapped buffer can be accessed at any time, so we must make the buffer resident and
// lock it to ensure it is never evicted.
Heap* heap = ToBackend(mResourceAllocation.GetResourceHeap());
DAWN_TRY(ToBackend(GetDevice())->GetResidencyManager()->LockAllocation(heap));
mWrittenMappedRange = {0, static_cast<size_t>(GetSize())}; mWrittenMappedRange = {0, static_cast<size_t>(GetSize())};
DAWN_TRY(CheckHRESULT(GetD3D12Resource()->Map(0, &mWrittenMappedRange, DAWN_TRY(MapBufferInternal(mWrittenMappedRange, reinterpret_cast<void**>(&mMappedData),
reinterpret_cast<void**>(&mMappedData)),
"D3D12 map write async")); "D3D12 map write async"));
// There is no need to transition the resource to a new state: D3D12 seems to make the CPU // There is no need to transition the resource to a new state: D3D12 seems to make the CPU
// writes available on queue submission. // writes available on queue submission.
return {}; return {};
} }
void Buffer::UnmapImpl() { void Buffer::UnmapImpl() {
GetD3D12Resource()->Unmap(0, &mWrittenMappedRange); UnmapBufferInternal(mWrittenMappedRange);
// When buffers are mapped, they are locked to keep them in resident memory. We must unlock
// them when they are unmapped.
Heap* heap = ToBackend(mResourceAllocation.GetResourceHeap());
ToBackend(GetDevice())->GetResidencyManager()->UnlockAllocation(heap);
mWrittenMappedRange = {}; mWrittenMappedRange = {};
mMappedData = nullptr; mMappedData = nullptr;
} }
@ -317,4 +326,39 @@ namespace dawn_native { namespace d3d12 {
return mResourceAllocation.GetInfo().mMethod == allocationMethod; return mResourceAllocation.GetInfo().mMethod == allocationMethod;
} }
MaybeError Buffer::ClearBuffer(ClearValue clearValue) {
// TODO(jiawei.shao@intel.com): support buffer lazy-initialization to 0.
ASSERT(clearValue == BufferBase::ClearValue::NonZero);
constexpr uint8_t kClearBufferValue = 1u;
Device* device = ToBackend(GetDevice());
// 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.
if (D3D12HeapType(GetUsage()) == D3D12_HEAP_TYPE_UPLOAD) {
uint8_t* mappedData = nullptr;
D3D12_RANGE writeRange = {0, static_cast<size_t>(GetSize())};
DAWN_TRY(MapBufferInternal(writeRange, reinterpret_cast<void**>(&mappedData),
"D3D12 map at clear buffer"));
memset(mappedData, kClearBufferValue, GetSize());
UnmapBufferInternal(writeRange);
mappedData = nullptr;
} else {
// TODO(jiawei.shao@intel.com): use ClearUnorderedAccessView*() when the buffer usage
// includes STORAGE.
DynamicUploader* uploader = device->GetDynamicUploader();
UploadHandle uploadHandle;
DAWN_TRY_ASSIGN(uploadHandle,
uploader->Allocate(GetSize(), device->GetPendingCommandSerial()));
memset(uploadHandle.mappedBuffer, kClearBufferValue, GetSize());
DAWN_TRY(device->CopyFromStagingToBuffer(uploadHandle.stagingBuffer,
uploadHandle.startOffset, this, 0, GetSize()));
}
return {};
}
}} // namespace dawn_native::d3d12 }} // namespace dawn_native::d3d12

View File

@ -55,11 +55,17 @@ namespace dawn_native { namespace d3d12 {
bool IsMapWritable() const override; bool IsMapWritable() const override;
virtual MaybeError MapAtCreationImpl(uint8_t** mappedPointer) override; virtual MaybeError MapAtCreationImpl(uint8_t** mappedPointer) override;
void* GetMappedPointerImpl() override; void* GetMappedPointerImpl() override;
MaybeError MapBufferInternal(D3D12_RANGE mappedRange,
void** mappedPointer,
const char* contextInfo);
void UnmapBufferInternal(D3D12_RANGE mappedRange);
bool TransitionUsageAndGetResourceBarrier(CommandRecordingContext* commandContext, bool TransitionUsageAndGetResourceBarrier(CommandRecordingContext* commandContext,
D3D12_RESOURCE_BARRIER* barrier, D3D12_RESOURCE_BARRIER* barrier,
wgpu::BufferUsage newUsage); wgpu::BufferUsage newUsage);
MaybeError ClearBuffer(ClearValue clearValue);
ResourceHeapAllocation mResourceAllocation; ResourceHeapAllocation mResourceAllocation;
bool mFixedResourceState = false; bool mFixedResourceState = false;
wgpu::BufferUsage mLastUsage = wgpu::BufferUsage::None; wgpu::BufferUsage mLastUsage = wgpu::BufferUsage::None;

View File

@ -35,8 +35,8 @@ TEST_P(NonzeroBufferCreationTests, BufferCreationWithCopyDstUsage) {
} }
// Verify that each byte of the buffer has all been initialized to 1 with the toggle enabled when it // Verify that each byte of the buffer has all been initialized to 1 with the toggle enabled when it
// is created without CopyDst usage. // is created with MapWrite without CopyDst usage.
TEST_P(NonzeroBufferCreationTests, BufferCreationWithoutCopyDstUsage) { TEST_P(NonzeroBufferCreationTests, BufferCreationWithMapWriteWithoutCopyDstUsage) {
constexpr uint32_t kSize = 32u; constexpr uint32_t kSize = 32u;
wgpu::BufferDescriptor descriptor; wgpu::BufferDescriptor descriptor;
@ -51,5 +51,6 @@ TEST_P(NonzeroBufferCreationTests, BufferCreationWithoutCopyDstUsage) {
} }
DAWN_INSTANTIATE_TEST(NonzeroBufferCreationTests, DAWN_INSTANTIATE_TEST(NonzeroBufferCreationTests,
D3D12Backend({"nonzero_clear_resources_on_creation_for_testing"}),
MetalBackend({"nonzero_clear_resources_on_creation_for_testing"}), MetalBackend({"nonzero_clear_resources_on_creation_for_testing"}),
VulkanBackend({"nonzero_clear_resources_on_creation_for_testing"})); VulkanBackend({"nonzero_clear_resources_on_creation_for_testing"}));