D3D12: Allocate GPU bind groups at draw/dispatch.
Instead of counting descriptors to be allocated for the entire command
buffer in a pre-pass, the bindgroup state tracker is used to allocate
only dirty bindgroups upon recording draw/dispatch. If the heap has no
more room and must be changed, bindgroups will be re-created according
to the BGL.
A future change will address the CPU descriptors and removal of the
pre-pass.
BUG=dawn:256,dawn:307
Change-Id: I6603de17cfda713bd4512c46e1c93618ca01bb7b
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/13400
Commit-Queue: Bryan Bernhart <bryan.bernhart@intel.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/dawn_native/CMakeLists.txt b/src/dawn_native/CMakeLists.txt
index 2a3fe40..e90f790 100644
--- a/src/dawn_native/CMakeLists.txt
+++ b/src/dawn_native/CMakeLists.txt
@@ -174,6 +174,8 @@
"d3d12/D3D12Error.h"
"d3d12/D3D12Info.cpp"
"d3d12/D3D12Info.h"
+ "d3d12/DescriptorHeapAllocationD3D12.cpp",
+ "d3d12/DescriptorHeapAllocationD3D12.h",
"d3d12/DescriptorHeapAllocator.cpp"
"d3d12/DescriptorHeapAllocator.h"
"d3d12/DeviceD3D12.cpp"
@@ -203,6 +205,8 @@
"d3d12/SamplerD3D12.h"
"d3d12/ShaderModuleD3D12.cpp"
"d3d12/ShaderModuleD3D12.h"
+ "d3d12/ShaderVisibleDescriptorAllocatorD3D12.cpp",
+ "d3d12/ShaderVisibleDescriptorAllocatorD3D12.h",
"d3d12/StagingBufferD3D12.cpp"
"d3d12/StagingBufferD3D12.h"
"d3d12/SwapChainD3D12.cpp"
diff --git a/src/dawn_native/d3d12/BindGroupD3D12.cpp b/src/dawn_native/d3d12/BindGroupD3D12.cpp
index 60a67d2..c8138b2 100644
--- a/src/dawn_native/d3d12/BindGroupD3D12.cpp
+++ b/src/dawn_native/d3d12/BindGroupD3D12.cpp
@@ -17,6 +17,7 @@
#include "dawn_native/d3d12/BindGroupLayoutD3D12.h"
#include "dawn_native/d3d12/BufferD3D12.h"
#include "dawn_native/d3d12/SamplerD3D12.h"
+#include "dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.h"
#include "dawn_native/d3d12/TextureD3D12.h"
#include "dawn_native/d3d12/DeviceD3D12.h"
@@ -27,23 +28,60 @@
: BindGroupBase(device, descriptor) {
}
- void BindGroup::AllocateDescriptors(const DescriptorHeapHandle& cbvUavSrvHeapStart,
- uint32_t* cbvUavSrvHeapOffset,
- const DescriptorHeapHandle& samplerHeapStart,
- uint32_t* samplerHeapOffset) {
- const auto* bgl = ToBackend(GetLayout());
- const auto& layout = bgl->GetBindingInfo();
+ ResultOrError<bool> BindGroup::Populate(ShaderVisibleDescriptorAllocator* allocator) {
+ Device* device = ToBackend(GetDevice());
- // Save the offset to the start of the descriptor table in the heap
- mCbvUavSrvHeapOffset = *cbvUavSrvHeapOffset;
- mSamplerHeapOffset = *samplerHeapOffset;
+ if (allocator->IsAllocationStillValid(mLastUsageSerial, mHeapSerial)) {
+ return true;
+ }
+
+ // Attempt to allocate descriptors for the currently bound shader-visible heaps.
+ // If either failed, return early to re-allocate and switch the heaps.
+ const BindGroupLayout* bgl = ToBackend(GetLayout());
+ const Serial pendingSerial = device->GetPendingCommandSerial();
+
+ const uint32_t cbvUavSrvDescriptorCount = bgl->GetCbvUavSrvDescriptorCount();
+ DescriptorHeapAllocation cbvSrvUavDescriptorHeapAllocation;
+ if (cbvUavSrvDescriptorCount > 0) {
+ DAWN_TRY_ASSIGN(
+ cbvSrvUavDescriptorHeapAllocation,
+ allocator->AllocateGPUDescriptors(cbvUavSrvDescriptorCount, pendingSerial,
+ D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV));
+ if (cbvSrvUavDescriptorHeapAllocation.IsInvalid()) {
+ return false;
+ }
+
+ mBaseCbvSrvUavDescriptor = cbvSrvUavDescriptorHeapAllocation.GetGPUHandle(0);
+ }
+
+ const uint32_t samplerDescriptorCount = bgl->GetSamplerDescriptorCount();
+ DescriptorHeapAllocation samplerDescriptorHeapAllocation;
+ if (samplerDescriptorCount > 0) {
+ DAWN_TRY_ASSIGN(samplerDescriptorHeapAllocation,
+ allocator->AllocateGPUDescriptors(samplerDescriptorCount, pendingSerial,
+ D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER));
+ if (samplerDescriptorHeapAllocation.IsInvalid()) {
+ return false;
+ }
+
+ mBaseSamplerDescriptor = samplerDescriptorHeapAllocation.GetGPUHandle(0);
+ }
+
+ // Record both the device and heap serials to determine later if the allocations are still
+ // valid.
+ mLastUsageSerial = pendingSerial;
+ mHeapSerial = allocator->GetShaderVisibleHeapsSerial();
+
+ const auto& layout = bgl->GetBindingInfo();
const auto& bindingOffsets = bgl->GetBindingOffsets();
- auto d3d12Device = ToBackend(GetDevice())->GetD3D12Device();
+ ID3D12Device* d3d12Device = device->GetD3D12Device().Get();
+
for (uint32_t bindingIndex : IterateBitSet(layout.mask)) {
- // It's not necessary to create descriptors in descriptor heap for dynamic resources.
- // So skip allocating descriptors in descriptor heaps for dynamic buffers.
+ // It's not necessary to create descriptors in descriptor heap for dynamic
+ // resources. So skip allocating descriptors in descriptor heaps for dynamic
+ // buffers.
if (layout.hasDynamicOffset[bindingIndex]) {
continue;
}
@@ -53,14 +91,14 @@
BufferBinding binding = GetBindingAsBufferBinding(bindingIndex);
D3D12_CONSTANT_BUFFER_VIEW_DESC desc;
- // TODO(enga@google.com): investigate if this needs to be a constraint at the
- // API level
+ // TODO(enga@google.com): investigate if this needs to be a constraint at
+ // the API level
desc.SizeInBytes = Align(binding.size, 256);
desc.BufferLocation = ToBackend(binding.buffer)->GetVA() + binding.offset;
d3d12Device->CreateConstantBufferView(
- &desc, cbvUavSrvHeapStart.GetCPUHandle(*cbvUavSrvHeapOffset +
- bindingOffsets[bindingIndex]));
+ &desc, cbvSrvUavDescriptorHeapAllocation.GetCPUHandle(
+ bindingOffsets[bindingIndex]));
} break;
case wgpu::BindingType::StorageBuffer: {
BufferBinding binding = GetBindingAsBufferBinding(bindingIndex);
@@ -83,16 +121,16 @@
d3d12Device->CreateUnorderedAccessView(
ToBackend(binding.buffer)->GetD3D12Resource().Get(), nullptr, &desc,
- cbvUavSrvHeapStart.GetCPUHandle(*cbvUavSrvHeapOffset +
- bindingOffsets[bindingIndex]));
+ cbvSrvUavDescriptorHeapAllocation.GetCPUHandle(
+ bindingOffsets[bindingIndex]));
} break;
case wgpu::BindingType::ReadonlyStorageBuffer: {
BufferBinding binding = GetBindingAsBufferBinding(bindingIndex);
// Like StorageBuffer, SPIRV-Cross outputs HLSL shaders for readonly storage
// buffer with ByteAddressBuffer. So we must use D3D12_BUFFER_SRV_FLAG_RAW
- // when making the SRV descriptor. And it has similar requirement for format,
- // element size, etc.
+ // when making the SRV descriptor. And it has similar requirement for
+ // format, element size, etc.
D3D12_SHADER_RESOURCE_VIEW_DESC desc;
desc.Format = DXGI_FORMAT_R32_TYPELESS;
desc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER;
@@ -103,23 +141,23 @@
desc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_RAW;
d3d12Device->CreateShaderResourceView(
ToBackend(binding.buffer)->GetD3D12Resource().Get(), &desc,
- cbvUavSrvHeapStart.GetCPUHandle(*cbvUavSrvHeapOffset +
- bindingOffsets[bindingIndex]));
+ cbvSrvUavDescriptorHeapAllocation.GetCPUHandle(
+ bindingOffsets[bindingIndex]));
} break;
case wgpu::BindingType::SampledTexture: {
auto* view = ToBackend(GetBindingAsTextureView(bindingIndex));
auto& srv = view->GetSRVDescriptor();
d3d12Device->CreateShaderResourceView(
ToBackend(view->GetTexture())->GetD3D12Resource(), &srv,
- cbvUavSrvHeapStart.GetCPUHandle(*cbvUavSrvHeapOffset +
- bindingOffsets[bindingIndex]));
+ cbvSrvUavDescriptorHeapAllocation.GetCPUHandle(
+ bindingOffsets[bindingIndex]));
} break;
case wgpu::BindingType::Sampler: {
auto* sampler = ToBackend(GetBindingAsSampler(bindingIndex));
auto& samplerDesc = sampler->GetSamplerDescriptor();
d3d12Device->CreateSampler(
- &samplerDesc, samplerHeapStart.GetCPUHandle(*samplerHeapOffset +
- bindingOffsets[bindingIndex]));
+ &samplerDesc,
+ samplerDescriptorHeapAllocation.GetCPUHandle(bindingOffsets[bindingIndex]));
} break;
case wgpu::BindingType::StorageTexture:
@@ -130,24 +168,14 @@
}
}
- // Offset by the number of descriptors created
- *cbvUavSrvHeapOffset += bgl->GetCbvUavSrvDescriptorCount();
- *samplerHeapOffset += bgl->GetSamplerDescriptorCount();
+ return true;
}
- uint32_t BindGroup::GetCbvUavSrvHeapOffset() const {
- return mCbvUavSrvHeapOffset;
+ D3D12_GPU_DESCRIPTOR_HANDLE BindGroup::GetBaseCbvUavSrvDescriptor() const {
+ return mBaseCbvSrvUavDescriptor;
}
- uint32_t BindGroup::GetSamplerHeapOffset() const {
- return mSamplerHeapOffset;
+ D3D12_GPU_DESCRIPTOR_HANDLE BindGroup::GetBaseSamplerDescriptor() const {
+ return mBaseSamplerDescriptor;
}
-
- bool BindGroup::TestAndSetCounted(uint64_t heapSerial, uint32_t indexInSubmit) {
- bool isCounted = (mHeapSerial == heapSerial && mIndexInSubmit == indexInSubmit);
- mHeapSerial = heapSerial;
- mIndexInSubmit = indexInSubmit;
- return isCounted;
- }
-
}} // namespace dawn_native::d3d12
diff --git a/src/dawn_native/d3d12/BindGroupD3D12.h b/src/dawn_native/d3d12/BindGroupD3D12.h
index 458e992..e00a662 100644
--- a/src/dawn_native/d3d12/BindGroupD3D12.h
+++ b/src/dawn_native/d3d12/BindGroupD3D12.h
@@ -15,37 +15,32 @@
#ifndef DAWNNATIVE_D3D12_BINDGROUPD3D12_H_
#define DAWNNATIVE_D3D12_BINDGROUPD3D12_H_
+#include "common/Serial.h"
#include "dawn_native/BindGroup.h"
-
#include "dawn_native/d3d12/d3d12_platform.h"
-#include "dawn_native/d3d12/DescriptorHeapAllocator.h"
-
namespace dawn_native { namespace d3d12 {
class Device;
+ class ShaderVisibleDescriptorAllocator;
class BindGroup : public BindGroupBase {
public:
BindGroup(Device* device, const BindGroupDescriptor* descriptor);
- void AllocateDescriptors(const DescriptorHeapHandle& cbvSrvUavHeapStart,
- uint32_t* cbvUavSrvHeapOffset,
- const DescriptorHeapHandle& samplerHeapStart,
- uint32_t* samplerHeapOffset);
- uint32_t GetCbvUavSrvHeapOffset() const;
- uint32_t GetSamplerHeapOffset() const;
+ // Returns true if the BindGroup was successfully populated.
+ ResultOrError<bool> Populate(ShaderVisibleDescriptorAllocator* allocator);
- bool TestAndSetCounted(uint64_t heapSerial, uint32_t indexInSubmit);
+ D3D12_GPU_DESCRIPTOR_HANDLE GetBaseCbvUavSrvDescriptor() const;
+ D3D12_GPU_DESCRIPTOR_HANDLE GetBaseSamplerDescriptor() const;
private:
- uint32_t mCbvUavSrvHeapOffset;
- uint32_t mSamplerHeapOffset;
+ Serial mLastUsageSerial = 0;
+ Serial mHeapSerial = 0;
- uint64_t mHeapSerial = 0;
- uint32_t mIndexInSubmit = 0;
+ D3D12_GPU_DESCRIPTOR_HANDLE mBaseCbvSrvUavDescriptor = {0};
+ D3D12_GPU_DESCRIPTOR_HANDLE mBaseSamplerDescriptor = {0};
};
-
}} // namespace dawn_native::d3d12
#endif // DAWNNATIVE_D3D12_BINDGROUPD3D12_H_
diff --git a/src/dawn_native/d3d12/CommandBufferD3D12.cpp b/src/dawn_native/d3d12/CommandBufferD3D12.cpp
index a6dcbc5..2ab9d8c 100644
--- a/src/dawn_native/d3d12/CommandBufferD3D12.cpp
+++ b/src/dawn_native/d3d12/CommandBufferD3D12.cpp
@@ -31,6 +31,7 @@
#include "dawn_native/d3d12/RenderPassBuilderD3D12.h"
#include "dawn_native/d3d12/RenderPipelineD3D12.h"
#include "dawn_native/d3d12/SamplerD3D12.h"
+#include "dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.h"
#include "dawn_native/d3d12/TextureCopySplitter.h"
#include "dawn_native/d3d12/TextureD3D12.h"
#include "dawn_native/d3d12/UtilsD3D12.h"
@@ -71,74 +72,56 @@
class BindGroupStateTracker : public BindGroupAndStorageBarrierTrackerBase<false, uint64_t> {
public:
BindGroupStateTracker(Device* device)
- : BindGroupAndStorageBarrierTrackerBase(), mDevice(device) {
+ : BindGroupAndStorageBarrierTrackerBase(),
+ mAllocator(device->GetShaderVisibleDescriptorAllocator()) {
}
void SetInComputePass(bool inCompute_) {
mInCompute = inCompute_;
}
- MaybeError AllocateDescriptorHeaps(Device* device) {
- // This function should only be called once.
- ASSERT(mCbvSrvUavGPUDescriptorHeap.Get() == nullptr &&
- mSamplerGPUDescriptorHeap.Get() == nullptr);
-
- DescriptorHeapAllocator* descriptorHeapAllocator = device->GetDescriptorHeapAllocator();
-
- if (mCbvSrvUavDescriptorHeapSize > 0) {
- DAWN_TRY_ASSIGN(
- mCbvSrvUavGPUDescriptorHeap,
- descriptorHeapAllocator->AllocateGPUHeap(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
- mCbvSrvUavDescriptorHeapSize));
- }
-
- if (mSamplerDescriptorHeapSize > 0) {
- DAWN_TRY_ASSIGN(mSamplerGPUDescriptorHeap, descriptorHeapAllocator->AllocateGPUHeap(
- D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER,
- mSamplerDescriptorHeapSize));
- }
-
- uint32_t cbvSrvUavDescriptorIndex = 0;
- uint32_t samplerDescriptorIndex = 0;
- for (BindGroup* group : mBindGroupsToAllocate) {
- ASSERT(group);
- ASSERT(cbvSrvUavDescriptorIndex +
- ToBackend(group->GetLayout())->GetCbvUavSrvDescriptorCount() <=
- mCbvSrvUavDescriptorHeapSize);
- ASSERT(samplerDescriptorIndex +
- ToBackend(group->GetLayout())->GetSamplerDescriptorCount() <=
- mSamplerDescriptorHeapSize);
- group->AllocateDescriptors(mCbvSrvUavGPUDescriptorHeap, &cbvSrvUavDescriptorIndex,
- mSamplerGPUDescriptorHeap, &samplerDescriptorIndex);
- }
-
- ASSERT(cbvSrvUavDescriptorIndex == mCbvSrvUavDescriptorHeapSize);
- ASSERT(samplerDescriptorIndex == mSamplerDescriptorHeapSize);
-
- return {};
- }
-
- // This function must only be called before calling AllocateDescriptorHeaps().
- void TrackSetBindGroup(BindGroup* group, uint32_t index, uint32_t indexInSubmit) {
- if (mBindGroups[index] != group) {
- mBindGroups[index] = group;
- if (!group->TestAndSetCounted(mDevice->GetPendingCommandSerial(), indexInSubmit)) {
- const BindGroupLayout* layout = ToBackend(group->GetLayout());
-
- mCbvSrvUavDescriptorHeapSize += layout->GetCbvUavSrvDescriptorCount();
- mSamplerDescriptorHeapSize += layout->GetSamplerDescriptorCount();
- mBindGroupsToAllocate.push_back(group);
+ MaybeError Apply(CommandRecordingContext* commandContext) {
+ // Bindgroups are allocated in shader-visible descriptor heaps which are managed by a
+ // ringbuffer. There can be a single shader-visible descriptor heap of each type bound
+ // at any given time. This means that when we switch heaps, all other currently bound
+ // bindgroups must be re-populated. Bindgroups can fail allocation gracefully which is
+ // the signal to change the bounded heaps.
+ // Re-populating all bindgroups after the last one fails causes duplicated allocations
+ // to occur on overflow.
+ // TODO(bryan.bernhart@intel.com): Consider further optimization.
+ bool didCreateBindGroups = true;
+ for (uint32_t index : IterateBitSet(mDirtyBindGroups)) {
+ DAWN_TRY_ASSIGN(didCreateBindGroups,
+ ToBackend(mBindGroups[index])->Populate(mAllocator));
+ if (!didCreateBindGroups) {
+ break;
}
}
- }
- void Apply(CommandRecordingContext* commandContext) {
+ // This will re-create bindgroups for both heaps even if only one overflowed.
+ // TODO(bryan.bernhart@intel.com): Consider re-allocating heaps independently
+ // such that overflowing one doesn't re-allocate the another.
ID3D12GraphicsCommandList* commandList = commandContext->GetCommandList();
+ if (!didCreateBindGroups) {
+ DAWN_TRY(mAllocator->AllocateAndSwitchShaderVisibleHeaps());
+
+ mDirtyBindGroupsObjectChangedOrIsDynamic |= mBindGroupLayoutsMask;
+ mDirtyBindGroups |= mBindGroupLayoutsMask;
+
+ // Must be called before applying the bindgroups.
+ SetID3D12DescriptorHeaps(commandList);
+
+ for (uint32_t index : IterateBitSet(mBindGroupLayoutsMask)) {
+ DAWN_TRY_ASSIGN(didCreateBindGroups,
+ ToBackend(mBindGroups[index])->Populate(mAllocator));
+ ASSERT(didCreateBindGroups);
+ }
+ }
for (uint32_t index : IterateBitSet(mDirtyBindGroupsObjectChangedOrIsDynamic)) {
- ApplyBindGroup(commandList, ToBackend(mPipelineLayout), index,
- ToBackend(mBindGroups[index]), mDynamicOffsetCounts[index],
- mDynamicOffsets[index].data());
+ BindGroup* group = ToBackend(mBindGroups[index]);
+ ApplyBindGroup(commandList, ToBackend(mPipelineLayout), index, group,
+ mDynamicOffsetCounts[index], mDynamicOffsets[index].data());
}
if (mInCompute) {
@@ -169,34 +152,26 @@
}
}
DidApply();
+
+ return {};
}
- void Reset() {
- for (uint32_t i = 0; i < kMaxBindGroups; ++i) {
- mBindGroups[i] = nullptr;
- }
- }
-
- void SetID3D12DescriptorHeaps(ComPtr<ID3D12GraphicsCommandList> commandList) {
+ void SetID3D12DescriptorHeaps(ID3D12GraphicsCommandList* commandList) {
ASSERT(commandList != nullptr);
- ID3D12DescriptorHeap* descriptorHeaps[2] = {mCbvSrvUavGPUDescriptorHeap.Get(),
- mSamplerGPUDescriptorHeap.Get()};
- if (descriptorHeaps[0] && descriptorHeaps[1]) {
- commandList->SetDescriptorHeaps(2, descriptorHeaps);
- } else if (descriptorHeaps[0]) {
- commandList->SetDescriptorHeaps(1, descriptorHeaps);
- } else if (descriptorHeaps[1]) {
- commandList->SetDescriptorHeaps(1, &descriptorHeaps[1]);
- }
+ std::array<ID3D12DescriptorHeap*, 2> descriptorHeaps =
+ mAllocator->GetShaderVisibleHeaps();
+ ASSERT(descriptorHeaps[0] != nullptr);
+ ASSERT(descriptorHeaps[1] != nullptr);
+ commandList->SetDescriptorHeaps(2, descriptorHeaps.data());
}
private:
void ApplyBindGroup(ID3D12GraphicsCommandList* commandList,
- PipelineLayout* pipelineLayout,
+ const PipelineLayout* pipelineLayout,
uint32_t index,
BindGroup* group,
uint32_t dynamicOffsetCount,
- uint64_t* dynamicOffsets) {
+ const uint64_t* dynamicOffsets) {
// Usually, the application won't set the same offsets many times,
// so always try to apply dynamic offsets even if the offsets stay the same
if (dynamicOffsetCount) {
@@ -262,47 +237,37 @@
return;
}
- uint32_t cbvUavSrvCount = ToBackend(group->GetLayout())->GetCbvUavSrvDescriptorCount();
- uint32_t samplerCount = ToBackend(group->GetLayout())->GetSamplerDescriptorCount();
+ const uint32_t cbvUavSrvCount =
+ ToBackend(group->GetLayout())->GetCbvUavSrvDescriptorCount();
+ const uint32_t samplerCount =
+ ToBackend(group->GetLayout())->GetSamplerDescriptorCount();
if (cbvUavSrvCount > 0) {
uint32_t parameterIndex = pipelineLayout->GetCbvUavSrvRootParameterIndex(index);
-
+ const D3D12_GPU_DESCRIPTOR_HANDLE baseDescriptor =
+ group->GetBaseCbvUavSrvDescriptor();
if (mInCompute) {
- commandList->SetComputeRootDescriptorTable(
- parameterIndex,
- mCbvSrvUavGPUDescriptorHeap.GetGPUHandle(group->GetCbvUavSrvHeapOffset()));
+ commandList->SetComputeRootDescriptorTable(parameterIndex, baseDescriptor);
} else {
- commandList->SetGraphicsRootDescriptorTable(
- parameterIndex,
- mCbvSrvUavGPUDescriptorHeap.GetGPUHandle(group->GetCbvUavSrvHeapOffset()));
+ commandList->SetGraphicsRootDescriptorTable(parameterIndex, baseDescriptor);
}
}
if (samplerCount > 0) {
uint32_t parameterIndex = pipelineLayout->GetSamplerRootParameterIndex(index);
-
+ const D3D12_GPU_DESCRIPTOR_HANDLE baseDescriptor =
+ group->GetBaseSamplerDescriptor();
if (mInCompute) {
- commandList->SetComputeRootDescriptorTable(
- parameterIndex,
- mSamplerGPUDescriptorHeap.GetGPUHandle(group->GetSamplerHeapOffset()));
+ commandList->SetComputeRootDescriptorTable(parameterIndex, baseDescriptor);
} else {
- commandList->SetGraphicsRootDescriptorTable(
- parameterIndex,
- mSamplerGPUDescriptorHeap.GetGPUHandle(group->GetSamplerHeapOffset()));
+ commandList->SetGraphicsRootDescriptorTable(parameterIndex, baseDescriptor);
}
}
}
- uint32_t mCbvSrvUavDescriptorHeapSize = 0;
- uint32_t mSamplerDescriptorHeapSize = 0;
- std::deque<BindGroup*> mBindGroupsToAllocate = {};
bool mInCompute = false;
- DescriptorHeapHandle mCbvSrvUavGPUDescriptorHeap = {};
- DescriptorHeapHandle mSamplerGPUDescriptorHeap = {};
-
- Device* mDevice;
+ ShaderVisibleDescriptorAllocator* mAllocator;
};
class RenderPassDescriptorHeapTracker {
@@ -486,21 +451,12 @@
MaybeError AllocateAndSetDescriptorHeaps(Device* device,
BindGroupStateTracker* bindingTracker,
RenderPassDescriptorHeapTracker* renderPassTracker,
- CommandIterator* commands,
- uint32_t indexInSubmit) {
+ CommandIterator* commands) {
{
Command type;
auto HandleCommand = [&](CommandIterator* commands, Command type) {
switch (type) {
- case Command::SetBindGroup: {
- SetBindGroupCmd* cmd = commands->NextCommand<SetBindGroupCmd>();
- BindGroup* group = ToBackend(cmd->group.Get());
- if (cmd->dynamicOffsetCount) {
- commands->NextData<uint32_t>(cmd->dynamicOffsetCount);
- }
- bindingTracker->TrackSetBindGroup(group, cmd->index, indexInSubmit);
- } break;
case Command::BeginRenderPass: {
BeginRenderPassCmd* cmd = commands->NextCommand<BeginRenderPassCmd>();
renderPassTracker->TrackRenderPass(cmd);
@@ -534,7 +490,6 @@
}
DAWN_TRY(renderPassTracker->AllocateRTVAndDSVHeaps());
- DAWN_TRY(bindingTracker->AllocateDescriptorHeaps(device));
return {};
}
@@ -582,8 +537,7 @@
FreeCommands(&mCommands);
}
- MaybeError CommandBuffer::RecordCommands(CommandRecordingContext* commandContext,
- uint32_t indexInSubmit) {
+ MaybeError CommandBuffer::RecordCommands(CommandRecordingContext* commandContext) {
Device* device = ToBackend(GetDevice());
BindGroupStateTracker bindingTracker(device);
RenderPassDescriptorHeapTracker renderPassTracker(device);
@@ -596,11 +550,13 @@
// heaps set using a small CommandList inserted just before the main CommandList.
{
DAWN_TRY(AllocateAndSetDescriptorHeaps(device, &bindingTracker, &renderPassTracker,
- &mCommands, indexInSubmit));
- bindingTracker.Reset();
- bindingTracker.SetID3D12DescriptorHeaps(commandList);
+ &mCommands));
}
+ // Make sure we use the correct descriptors for this command list. Could be done once per
+ // actual command list but here is ok because there should be few command buffers.
+ bindingTracker.SetID3D12DescriptorHeaps(commandList);
+
// Records the necessary barriers for the resource usage pre-computed by the frontend
auto TransitionForPass = [](CommandRecordingContext* commandContext,
const PassResourceUsage& usages) -> bool {
@@ -663,7 +619,7 @@
TransitionForPass(commandContext, passResourceUsages[nextPassNumber]);
bindingTracker.SetInComputePass(true);
- RecordComputePass(commandContext, &bindingTracker);
+ DAWN_TRY(RecordComputePass(commandContext, &bindingTracker));
nextPassNumber++;
} break;
@@ -677,8 +633,8 @@
bindingTracker.SetInComputePass(false);
LazyClearRenderPassAttachments(beginRenderPassCmd);
- RecordRenderPass(commandContext, &bindingTracker, &renderPassTracker,
- beginRenderPassCmd, passHasUAV);
+ DAWN_TRY(RecordRenderPass(commandContext, &bindingTracker, &renderPassTracker,
+ beginRenderPassCmd, passHasUAV));
nextPassNumber++;
} break;
@@ -827,8 +783,8 @@
return {};
}
- void CommandBuffer::RecordComputePass(CommandRecordingContext* commandContext,
- BindGroupStateTracker* bindingTracker) {
+ MaybeError CommandBuffer::RecordComputePass(CommandRecordingContext* commandContext,
+ BindGroupStateTracker* bindingTracker) {
PipelineLayout* lastLayout = nullptr;
ID3D12GraphicsCommandList* commandList = commandContext->GetCommandList();
@@ -838,14 +794,14 @@
case Command::Dispatch: {
DispatchCmd* dispatch = mCommands.NextCommand<DispatchCmd>();
- bindingTracker->Apply(commandContext);
+ DAWN_TRY(bindingTracker->Apply(commandContext));
commandList->Dispatch(dispatch->x, dispatch->y, dispatch->z);
} break;
case Command::DispatchIndirect: {
DispatchIndirectCmd* dispatch = mCommands.NextCommand<DispatchIndirectCmd>();
- bindingTracker->Apply(commandContext);
+ DAWN_TRY(bindingTracker->Apply(commandContext));
Buffer* buffer = ToBackend(dispatch->indirectBuffer.Get());
ComPtr<ID3D12CommandSignature> signature =
ToBackend(GetDevice())->GetDispatchIndirectSignature();
@@ -856,7 +812,7 @@
case Command::EndComputePass: {
mCommands.NextCommand<EndComputePassCmd>();
- return;
+ return {};
} break;
case Command::SetComputePipeline: {
@@ -924,6 +880,8 @@
default: { UNREACHABLE(); } break;
}
}
+
+ return {};
}
void CommandBuffer::SetupRenderPass(CommandRecordingContext* commandContext,
@@ -1040,7 +998,7 @@
: nullptr);
}
- void CommandBuffer::RecordRenderPass(
+ MaybeError CommandBuffer::RecordRenderPass(
CommandRecordingContext* commandContext,
BindGroupStateTracker* bindingTracker,
RenderPassDescriptorHeapTracker* renderPassDescriptorHeapTracker,
@@ -1093,12 +1051,12 @@
VertexBufferTracker vertexBufferTracker = {};
IndexBufferTracker indexBufferTracker = {};
- auto EncodeRenderBundleCommand = [&](CommandIterator* iter, Command type) {
+ auto EncodeRenderBundleCommand = [&](CommandIterator* iter, Command type) -> MaybeError {
switch (type) {
case Command::Draw: {
DrawCmd* draw = iter->NextCommand<DrawCmd>();
- bindingTracker->Apply(commandContext);
+ DAWN_TRY(bindingTracker->Apply(commandContext));
vertexBufferTracker.Apply(commandList, lastPipeline);
commandList->DrawInstanced(draw->vertexCount, draw->instanceCount,
draw->firstVertex, draw->firstInstance);
@@ -1107,7 +1065,7 @@
case Command::DrawIndexed: {
DrawIndexedCmd* draw = iter->NextCommand<DrawIndexedCmd>();
- bindingTracker->Apply(commandContext);
+ DAWN_TRY(bindingTracker->Apply(commandContext));
indexBufferTracker.Apply(commandList);
vertexBufferTracker.Apply(commandList, lastPipeline);
commandList->DrawIndexedInstanced(draw->indexCount, draw->instanceCount,
@@ -1118,7 +1076,7 @@
case Command::DrawIndirect: {
DrawIndirectCmd* draw = iter->NextCommand<DrawIndirectCmd>();
- bindingTracker->Apply(commandContext);
+ DAWN_TRY(bindingTracker->Apply(commandContext));
vertexBufferTracker.Apply(commandList, lastPipeline);
Buffer* buffer = ToBackend(draw->indirectBuffer.Get());
ComPtr<ID3D12CommandSignature> signature =
@@ -1131,7 +1089,7 @@
case Command::DrawIndexedIndirect: {
DrawIndexedIndirectCmd* draw = iter->NextCommand<DrawIndexedIndirectCmd>();
- bindingTracker->Apply(commandContext);
+ DAWN_TRY(bindingTracker->Apply(commandContext));
indexBufferTracker.Apply(commandList);
vertexBufferTracker.Apply(commandList, lastPipeline);
Buffer* buffer = ToBackend(draw->indirectBuffer.Get());
@@ -1224,6 +1182,7 @@
UNREACHABLE();
break;
}
+ return {};
};
Command type;
@@ -1236,7 +1195,7 @@
} else if (renderPass->attachmentState->GetSampleCount() > 1) {
ResolveMultisampledRenderPass(commandContext, renderPass);
}
- return;
+ return {};
} break;
case Command::SetStencilReference: {
@@ -1282,14 +1241,14 @@
CommandIterator* iter = bundles[i]->GetCommands();
iter->Reset();
while (iter->NextCommandId(&type)) {
- EncodeRenderBundleCommand(iter, type);
+ DAWN_TRY(EncodeRenderBundleCommand(iter, type));
}
}
} break;
- default: { EncodeRenderBundleCommand(&mCommands, type); } break;
+ default: { DAWN_TRY(EncodeRenderBundleCommand(&mCommands, type)); } break;
}
}
+ return {};
}
-
}} // namespace dawn_native::d3d12
diff --git a/src/dawn_native/d3d12/CommandBufferD3D12.h b/src/dawn_native/d3d12/CommandBufferD3D12.h
index d710d08..eba2505 100644
--- a/src/dawn_native/d3d12/CommandBufferD3D12.h
+++ b/src/dawn_native/d3d12/CommandBufferD3D12.h
@@ -49,16 +49,17 @@
CommandBuffer(CommandEncoder* encoder, const CommandBufferDescriptor* descriptor);
~CommandBuffer();
- MaybeError RecordCommands(CommandRecordingContext* commandContext, uint32_t indexInSubmit);
+ MaybeError RecordCommands(CommandRecordingContext* commandContext);
private:
- void RecordComputePass(CommandRecordingContext* commandContext,
- BindGroupStateTracker* bindingTracker);
- void RecordRenderPass(CommandRecordingContext* commandContext,
- BindGroupStateTracker* bindingTracker,
- RenderPassDescriptorHeapTracker* renderPassDescriptorHeapTracker,
- BeginRenderPassCmd* renderPass,
- bool passHasUAV);
+ MaybeError RecordComputePass(CommandRecordingContext* commandContext,
+ BindGroupStateTracker* bindingTracker);
+ MaybeError RecordRenderPass(
+ CommandRecordingContext* commandContext,
+ BindGroupStateTracker* bindingTracker,
+ RenderPassDescriptorHeapTracker* renderPassDescriptorHeapTracker,
+ BeginRenderPassCmd* renderPass,
+ bool passHasUAV);
void SetupRenderPass(CommandRecordingContext* commandContext,
BeginRenderPassCmd* renderPass,
RenderPassBuilder* renderPassBuilder);
diff --git a/src/dawn_native/d3d12/DescriptorHeapAllocationD3D12.cpp b/src/dawn_native/d3d12/DescriptorHeapAllocationD3D12.cpp
new file mode 100644
index 0000000..fd16f1e
--- /dev/null
+++ b/src/dawn_native/d3d12/DescriptorHeapAllocationD3D12.cpp
@@ -0,0 +1,49 @@
+// 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 "dawn_native/d3d12/DescriptorHeapAllocationD3D12.h"
+#include "dawn_native/Error.h"
+
+namespace dawn_native { namespace d3d12 {
+
+ DescriptorHeapAllocation::DescriptorHeapAllocation() : mSizeIncrement(0) {
+ }
+
+ DescriptorHeapAllocation::DescriptorHeapAllocation(
+ uint32_t sizeIncrement,
+ D3D12_CPU_DESCRIPTOR_HANDLE baseCPUDescriptorHandle,
+ D3D12_GPU_DESCRIPTOR_HANDLE baseGPUDescriptorHandle)
+ : mSizeIncrement(sizeIncrement),
+ mBaseCPUDescriptorHandle(baseCPUDescriptorHandle),
+ mBaseGPUDescriptorHandle(baseGPUDescriptorHandle) {
+ }
+
+ D3D12_CPU_DESCRIPTOR_HANDLE DescriptorHeapAllocation::GetCPUHandle(uint32_t offset) const {
+ ASSERT(!IsInvalid());
+ D3D12_CPU_DESCRIPTOR_HANDLE cpuHandle = mBaseCPUDescriptorHandle;
+ cpuHandle.ptr += mSizeIncrement * offset;
+ return cpuHandle;
+ }
+
+ D3D12_GPU_DESCRIPTOR_HANDLE DescriptorHeapAllocation::GetGPUHandle(uint32_t offset) const {
+ ASSERT(!IsInvalid());
+ D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle = mBaseGPUDescriptorHandle;
+ gpuHandle.ptr += mSizeIncrement * offset;
+ return gpuHandle;
+ }
+
+ bool DescriptorHeapAllocation::IsInvalid() const {
+ return mBaseCPUDescriptorHandle.ptr == 0;
+ }
+}} // namespace dawn_native::d3d12
\ No newline at end of file
diff --git a/src/dawn_native/d3d12/DescriptorHeapAllocationD3D12.h b/src/dawn_native/d3d12/DescriptorHeapAllocationD3D12.h
new file mode 100644
index 0000000..30a034c
--- /dev/null
+++ b/src/dawn_native/d3d12/DescriptorHeapAllocationD3D12.h
@@ -0,0 +1,46 @@
+// 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.
+
+#ifndef DAWNNATIVE_D3D12_DESCRIPTORHEAPALLOCATIOND3D12_H_
+#define DAWNNATIVE_D3D12_DESCRIPTORHEAPALLOCATIOND3D12_H_
+
+#include "dawn_native/d3d12/d3d12_platform.h"
+
+#include <cstdint>
+
+namespace dawn_native { namespace d3d12 {
+
+ // Wrapper for a handle into a descriptor heap.
+ class DescriptorHeapAllocation {
+ public:
+ DescriptorHeapAllocation();
+ DescriptorHeapAllocation(uint32_t sizeIncrement,
+ D3D12_CPU_DESCRIPTOR_HANDLE baseCPUDescriptorHandle,
+ D3D12_GPU_DESCRIPTOR_HANDLE baseGPUDescriptorHandle);
+ ~DescriptorHeapAllocation() = default;
+
+ D3D12_CPU_DESCRIPTOR_HANDLE GetCPUHandle(uint32_t offset) const;
+ D3D12_GPU_DESCRIPTOR_HANDLE GetGPUHandle(uint32_t offset) const;
+
+ bool IsInvalid() const;
+
+ private:
+ uint32_t mSizeIncrement;
+
+ D3D12_CPU_DESCRIPTOR_HANDLE mBaseCPUDescriptorHandle = {0};
+ D3D12_GPU_DESCRIPTOR_HANDLE mBaseGPUDescriptorHandle = {0};
+ };
+}} // namespace dawn_native::d3d12
+
+#endif // DAWNNATIVE_D3D12_DESCRIPTORHEAPALLOCATIOND3D12_H_
\ No newline at end of file
diff --git a/src/dawn_native/d3d12/DeviceD3D12.cpp b/src/dawn_native/d3d12/DeviceD3D12.cpp
index 18734e6..085dffd 100644
--- a/src/dawn_native/d3d12/DeviceD3D12.cpp
+++ b/src/dawn_native/d3d12/DeviceD3D12.cpp
@@ -35,6 +35,7 @@
#include "dawn_native/d3d12/ResourceAllocatorManagerD3D12.h"
#include "dawn_native/d3d12/SamplerD3D12.h"
#include "dawn_native/d3d12/ShaderModuleD3D12.h"
+#include "dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.h"
#include "dawn_native/d3d12/StagingBufferD3D12.h"
#include "dawn_native/d3d12/SwapChainD3D12.h"
#include "dawn_native/d3d12/TextureD3D12.h"
@@ -72,6 +73,11 @@
// Initialize backend services
mCommandAllocatorManager = std::make_unique<CommandAllocatorManager>(this);
mDescriptorHeapAllocator = std::make_unique<DescriptorHeapAllocator>(this);
+
+ mShaderVisibleDescriptorAllocator =
+ std::make_unique<ShaderVisibleDescriptorAllocator>(this);
+ DAWN_TRY(mShaderVisibleDescriptorAllocator->Initialize());
+
mMapRequestTracker = std::make_unique<MapRequestTracker>(this);
mResourceAllocatorManager = std::make_unique<ResourceAllocatorManager>(this);
@@ -179,7 +185,7 @@
mResourceAllocatorManager->Tick(mCompletedSerial);
DAWN_TRY(mCommandAllocatorManager->Tick(mCompletedSerial));
- mDescriptorHeapAllocator->Deallocate(mCompletedSerial);
+ mShaderVisibleDescriptorAllocator->Tick(mCompletedSerial);
mMapRequestTracker->Tick(mCompletedSerial);
mUsedComObjectRefs.ClearUpTo(mCompletedSerial);
DAWN_TRY(ExecutePendingCommandContext());
@@ -433,4 +439,7 @@
ASSERT(!mPendingCommands.IsOpen());
}
+ ShaderVisibleDescriptorAllocator* Device::GetShaderVisibleDescriptorAllocator() const {
+ return mShaderVisibleDescriptorAllocator.get();
+ }
}} // namespace dawn_native::d3d12
diff --git a/src/dawn_native/d3d12/DeviceD3D12.h b/src/dawn_native/d3d12/DeviceD3D12.h
index 65edada..4817102 100644
--- a/src/dawn_native/d3d12/DeviceD3D12.h
+++ b/src/dawn_native/d3d12/DeviceD3D12.h
@@ -30,6 +30,7 @@
class CommandAllocatorManager;
class DescriptorHeapAllocator;
+ class ShaderVisibleDescriptorAllocator;
class MapRequestTracker;
class PlatformFunctions;
class ResourceAllocatorManager;
@@ -95,6 +96,8 @@
void DeallocateMemory(ResourceHeapAllocation& allocation);
+ ShaderVisibleDescriptorAllocator* GetShaderVisibleDescriptorAllocator() const;
+
TextureBase* WrapSharedHandle(const TextureDescriptor* descriptor,
HANDLE sharedHandle,
uint64_t acquireMutexKey);
@@ -158,6 +161,7 @@
std::unique_ptr<DescriptorHeapAllocator> mDescriptorHeapAllocator;
std::unique_ptr<MapRequestTracker> mMapRequestTracker;
std::unique_ptr<ResourceAllocatorManager> mResourceAllocatorManager;
+ std::unique_ptr<ShaderVisibleDescriptorAllocator> mShaderVisibleDescriptorAllocator;
};
}} // namespace dawn_native::d3d12
diff --git a/src/dawn_native/d3d12/QueueD3D12.cpp b/src/dawn_native/d3d12/QueueD3D12.cpp
index c563c50..710e41f 100644
--- a/src/dawn_native/d3d12/QueueD3D12.cpp
+++ b/src/dawn_native/d3d12/QueueD3D12.cpp
@@ -36,7 +36,7 @@
TRACE_EVENT_BEGIN0(GetDevice()->GetPlatform(), Recording,
"CommandBufferD3D12::RecordCommands");
for (uint32_t i = 0; i < commandCount; ++i) {
- DAWN_TRY(ToBackend(commands[i])->RecordCommands(commandContext, i));
+ DAWN_TRY(ToBackend(commands[i])->RecordCommands(commandContext));
}
TRACE_EVENT_END0(GetDevice()->GetPlatform(), Recording,
"CommandBufferD3D12::RecordCommands");
diff --git a/src/dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.cpp b/src/dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.cpp
new file mode 100644
index 0000000..6843d21
--- /dev/null
+++ b/src/dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.cpp
@@ -0,0 +1,168 @@
+// 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 "dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.h"
+#include "dawn_native/d3d12/D3D12Error.h"
+#include "dawn_native/d3d12/DeviceD3D12.h"
+
+namespace dawn_native { namespace d3d12 {
+
+ // Check that d3d heap type enum correctly mirrors the type index used by the static arrays.
+ static_assert(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV == 0, "");
+ static_assert(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER == 1, "");
+
+ uint32_t GetD3D12ShaderVisibleHeapSize(D3D12_DESCRIPTOR_HEAP_TYPE heapType) {
+ switch (heapType) {
+ case D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV:
+ return D3D12_MAX_SHADER_VISIBLE_DESCRIPTOR_HEAP_SIZE_TIER_1;
+ case D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER:
+ return D3D12_MAX_SHADER_VISIBLE_SAMPLER_HEAP_SIZE;
+ default:
+ UNREACHABLE();
+ }
+ }
+
+ D3D12_DESCRIPTOR_HEAP_FLAGS GetD3D12HeapFlags(D3D12_DESCRIPTOR_HEAP_TYPE heapType) {
+ switch (heapType) {
+ case D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV:
+ case D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER:
+ return D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
+ default:
+ UNREACHABLE();
+ }
+ }
+
+ ShaderVisibleDescriptorAllocator::ShaderVisibleDescriptorAllocator(Device* device)
+ : mDevice(device),
+ mSizeIncrements{
+ device->GetD3D12Device()->GetDescriptorHandleIncrementSize(
+ D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV),
+ device->GetD3D12Device()->GetDescriptorHandleIncrementSize(
+ D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER),
+ } {
+ }
+
+ MaybeError ShaderVisibleDescriptorAllocator::Initialize() {
+ ASSERT(mShaderVisibleBuffers[D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV].heap.Get() == nullptr);
+ ASSERT(mShaderVisibleBuffers[D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER].heap.Get() == nullptr);
+ DAWN_TRY(AllocateAndSwitchShaderVisibleHeaps());
+ return {};
+ }
+
+ MaybeError ShaderVisibleDescriptorAllocator::AllocateAndSwitchShaderVisibleHeaps() {
+ // TODO(bryan.bernhart@intel.com): Allocating to max heap size wastes memory
+ // should the developer not allocate any bindings for the heap type.
+ // Consider dynamically re-sizing GPU heaps.
+ DAWN_TRY(
+ AllocateGPUHeap(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
+ GetD3D12ShaderVisibleHeapSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV),
+ GetD3D12HeapFlags(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV)));
+ DAWN_TRY(AllocateGPUHeap(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER,
+ GetD3D12ShaderVisibleHeapSize(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER),
+ GetD3D12HeapFlags(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER)));
+
+ // Invalidate all bindgroup allocations on previously bound heaps by incrementing the heap
+ // serial. When a bindgroup attempts to re-populate, it will compare with its recorded
+ // heap serial.
+ mShaderVisibleHeapsSerial++;
+
+ return {};
+ }
+
+ ResultOrError<DescriptorHeapAllocation>
+ ShaderVisibleDescriptorAllocator::AllocateGPUDescriptors(uint32_t descriptorCount,
+ Serial pendingSerial,
+ D3D12_DESCRIPTOR_HEAP_TYPE heapType) {
+ ASSERT(heapType == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV ||
+ heapType == D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
+ ASSERT(mShaderVisibleBuffers[heapType].heap != nullptr);
+ const uint64_t startOffset =
+ mShaderVisibleBuffers[heapType].allocator.Allocate(descriptorCount, pendingSerial);
+ if (startOffset == RingBufferAllocator::kInvalidOffset) {
+ return DescriptorHeapAllocation{}; // Invalid
+ }
+
+ ID3D12DescriptorHeap* descriptorHeap = mShaderVisibleBuffers[heapType].heap.Get();
+
+ D3D12_CPU_DESCRIPTOR_HANDLE baseCPUDescriptor =
+ descriptorHeap->GetCPUDescriptorHandleForHeapStart();
+ baseCPUDescriptor.ptr += mSizeIncrements[heapType] * startOffset;
+
+ D3D12_GPU_DESCRIPTOR_HANDLE baseGPUDescriptor =
+ descriptorHeap->GetGPUDescriptorHandleForHeapStart();
+ baseGPUDescriptor.ptr += mSizeIncrements[heapType] * startOffset;
+
+ return DescriptorHeapAllocation{mSizeIncrements[heapType], baseCPUDescriptor,
+ baseGPUDescriptor};
+ }
+
+ std::array<ID3D12DescriptorHeap*, 2> ShaderVisibleDescriptorAllocator::GetShaderVisibleHeaps()
+ const {
+ return {mShaderVisibleBuffers[D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV].heap.Get(),
+ mShaderVisibleBuffers[D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER].heap.Get()};
+ }
+
+ void ShaderVisibleDescriptorAllocator::Tick(uint64_t completedSerial) {
+ for (uint32_t i = 0; i < mShaderVisibleBuffers.size(); i++) {
+ ASSERT(mShaderVisibleBuffers[i].heap != nullptr);
+ mShaderVisibleBuffers[i].allocator.Deallocate(completedSerial);
+ }
+ }
+
+ // Creates a GPU descriptor heap that manages descriptors in a FIFO queue.
+ MaybeError ShaderVisibleDescriptorAllocator::AllocateGPUHeap(
+ D3D12_DESCRIPTOR_HEAP_TYPE heapType,
+ uint32_t descriptorCount,
+ D3D12_DESCRIPTOR_HEAP_FLAGS heapFlags) {
+ ASSERT(heapType == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV ||
+ heapType == D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
+ if (mShaderVisibleBuffers[heapType].heap != nullptr) {
+ mDevice->ReferenceUntilUnused(std::move(mShaderVisibleBuffers[heapType].heap));
+ }
+
+ D3D12_DESCRIPTOR_HEAP_DESC heapDescriptor;
+ heapDescriptor.Type = heapType;
+ heapDescriptor.NumDescriptors = descriptorCount;
+ heapDescriptor.Flags = heapFlags;
+ heapDescriptor.NodeMask = 0;
+ ComPtr<ID3D12DescriptorHeap> heap;
+ DAWN_TRY(CheckOutOfMemoryHRESULT(
+ mDevice->GetD3D12Device()->CreateDescriptorHeap(&heapDescriptor, IID_PPV_ARGS(&heap)),
+ "ID3D12Device::CreateDescriptorHeap"));
+
+ // Create a FIFO buffer from the recently created heap.
+ mShaderVisibleBuffers[heapType].heap = std::move(heap);
+ mShaderVisibleBuffers[heapType].allocator = RingBufferAllocator(descriptorCount);
+ return {};
+ }
+
+ Serial ShaderVisibleDescriptorAllocator::GetShaderVisibleHeapsSerial() const {
+ return mShaderVisibleHeapsSerial;
+ }
+
+ uint64_t ShaderVisibleDescriptorAllocator::GetShaderVisibleHeapSizeForTesting(
+ D3D12_DESCRIPTOR_HEAP_TYPE heapType) const {
+ ASSERT(heapType == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV ||
+ heapType == D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
+ return mShaderVisibleBuffers[heapType].allocator.GetSize();
+ }
+
+ bool ShaderVisibleDescriptorAllocator::IsAllocationStillValid(Serial lastUsageSerial,
+ Serial heapSerial) const {
+ // Consider valid if allocated for the pending submit and the shader visible heaps
+ // have not switched over.
+ return (lastUsageSerial > mDevice->GetCompletedCommandSerial() &&
+ heapSerial == mShaderVisibleHeapsSerial);
+ }
+}} // namespace dawn_native::d3d12
\ No newline at end of file
diff --git a/src/dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.h b/src/dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.h
new file mode 100644
index 0000000..5c6241e
--- /dev/null
+++ b/src/dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.h
@@ -0,0 +1,71 @@
+// 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.
+
+#ifndef DAWNNATIVE_D3D12_SHADERVISIBLEDESCRIPTORALLOCATOR_H_
+#define DAWNNATIVE_D3D12_SHADERVISIBLEDESCRIPTORALLOCATOR_H_
+
+#include "dawn_native/Error.h"
+#include "dawn_native/RingBufferAllocator.h"
+#include "dawn_native/d3d12/DescriptorHeapAllocationD3D12.h"
+
+#include <array>
+
+namespace dawn_native { namespace d3d12 {
+
+ class Device;
+
+ // Manages descriptor heap allocators used by the device to create descriptors using allocation
+ // methods based on the heap type.
+ class ShaderVisibleDescriptorAllocator {
+ public:
+ ShaderVisibleDescriptorAllocator(Device* device);
+ MaybeError Initialize();
+
+ ResultOrError<DescriptorHeapAllocation> AllocateGPUDescriptors(
+ uint32_t descriptorCount,
+ Serial pendingSerial,
+ D3D12_DESCRIPTOR_HEAP_TYPE heapType);
+
+ void Tick(uint64_t completedSerial);
+ Serial GetShaderVisibleHeapsSerial() const;
+
+ std::array<ID3D12DescriptorHeap*, 2> GetShaderVisibleHeaps() const;
+ MaybeError AllocateAndSwitchShaderVisibleHeaps();
+
+ uint64_t GetShaderVisibleHeapSizeForTesting(D3D12_DESCRIPTOR_HEAP_TYPE heapType) const;
+
+ bool IsAllocationStillValid(Serial lastUsageSerial, Serial heapSerial) const;
+
+ private:
+ MaybeError AllocateGPUHeap(D3D12_DESCRIPTOR_HEAP_TYPE heapType,
+ uint32_t descriptorCount,
+ D3D12_DESCRIPTOR_HEAP_FLAGS heapFlags);
+
+ struct ShaderVisibleBuffer {
+ ComPtr<ID3D12DescriptorHeap> heap;
+ RingBufferAllocator allocator;
+ };
+
+ Device* mDevice;
+
+ // The serial value of 0 means the shader-visible heaps have not been allocated.
+ // This value is never returned by GetShaderVisibleHeapsSerial() after Initialize().
+ Serial mShaderVisibleHeapsSerial = 0;
+
+ std::array<ShaderVisibleBuffer, 2> mShaderVisibleBuffers;
+ std::array<uint32_t, 2> mSizeIncrements;
+ };
+}} // namespace dawn_native::d3d12
+
+#endif // DAWNNATIVE_D3D12_SHADERVISIBLEDESCRIPTORALLOCATOR_H_
\ No newline at end of file
diff --git a/src/tests/white_box/D3D12DescriptorHeapTests.cpp b/src/tests/white_box/D3D12DescriptorHeapTests.cpp
new file mode 100644
index 0000000..283f4a3
--- /dev/null
+++ b/src/tests/white_box/D3D12DescriptorHeapTests.cpp
@@ -0,0 +1,88 @@
+// 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 "dawn_native/d3d12/DeviceD3D12.h"
+#include "dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.h"
+#include "utils/ComboRenderPipelineDescriptor.h"
+#include "utils/WGPUHelpers.h"
+
+constexpr uint32_t kRTSize = 4;
+
+using namespace dawn_native::d3d12;
+
+class D3D12DescriptorHeapTests : public DawnTest {
+ private:
+ void TestSetUp() override {
+ DAWN_SKIP_TEST_IF(UsesWire());
+ }
+};
+
+// Verify the shader visible heaps switch over within a single submit.
+TEST_P(D3D12DescriptorHeapTests, SwitchOverHeaps) {
+ utils::ComboRenderPipelineDescriptor renderPipelineDescriptor(device);
+
+ // Fill in a sampler heap with "sampler only" bindgroups (1x sampler per group) by creating a
+ // sampler bindgroup each draw. After HEAP_SIZE + 1 draws, the heaps must switch over.
+ renderPipelineDescriptor.vertexStage.module =
+ utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"(
+ #version 450
+ void main() {
+ gl_Position = vec4(0.f, 0.f, 0.f, 1.f);
+ })");
+
+ renderPipelineDescriptor.cFragmentStage.module =
+ utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"(#version 450
+ layout(set = 0, binding = 0) uniform sampler sampler0;
+ layout(location = 0) out vec4 fragColor;
+ void main() {
+ fragColor = vec4(0.0, 0.0, 0.0, 0.0);
+ })");
+
+ wgpu::RenderPipeline renderPipeline = device.CreateRenderPipeline(&renderPipelineDescriptor);
+ utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
+
+ wgpu::SamplerDescriptor samplerDesc = utils::GetDefaultSamplerDescriptor();
+ wgpu::Sampler sampler = device.CreateSampler(&samplerDesc);
+
+ Device* d3dDevice = reinterpret_cast<Device*>(device.Get());
+ ShaderVisibleDescriptorAllocator* allocator = d3dDevice->GetShaderVisibleDescriptorAllocator();
+ const uint64_t samplerHeapSize =
+ allocator->GetShaderVisibleHeapSizeForTesting(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
+
+ const Serial heapSerial = allocator->GetShaderVisibleHeapsSerial();
+
+ wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+ {
+ wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
+
+ pass.SetPipeline(renderPipeline);
+
+ for (uint32_t i = 0; i < samplerHeapSize + 1; ++i) {
+ pass.SetBindGroup(0, utils::MakeBindGroup(device, renderPipeline.GetBindGroupLayout(0),
+ {{0, sampler}}));
+ pass.Draw(3, 1, 0, 0);
+ }
+
+ pass.EndPass();
+ }
+
+ wgpu::CommandBuffer commands = encoder.Finish();
+ queue.Submit(1, &commands);
+
+ EXPECT_EQ(allocator->GetShaderVisibleHeapsSerial(), heapSerial + 1);
+}
+
+DAWN_INSTANTIATE_TEST(D3D12DescriptorHeapTests, D3D12Backend());