D3D12: Refactor bind group descriptor tracking and descriptor heap allocation

Extract descriptor offset computation and CPU descriptor recording to
BindGroupLayout and BindGroup. Refactor descriptor heap allocation to
copy from a large CPU heap to a GPU heap.
This commit is contained in:
Austin Eng 2017-06-29 14:11:43 -04:00 committed by Austin Eng
parent aecf0b130e
commit 036f76f51f
14 changed files with 538 additions and 251 deletions

View File

@ -237,6 +237,10 @@ if (WIN32)
SetPIC(d3d12_autogen) SetPIC(d3d12_autogen)
list(APPEND BACKEND_SOURCES list(APPEND BACKEND_SOURCES
${D3D12_DIR}/BindGroupD3D12.cpp
${D3D12_DIR}/BindGroupD3D12.h
${D3D12_DIR}/BindGroupLayoutD3D12.cpp
${D3D12_DIR}/BindGroupLayoutD3D12.h
${D3D12_DIR}/BufferD3D12.cpp ${D3D12_DIR}/BufferD3D12.cpp
${D3D12_DIR}/BufferD3D12.h ${D3D12_DIR}/BufferD3D12.h
${D3D12_DIR}/CommandAllocatorManager.cpp ${D3D12_DIR}/CommandAllocatorManager.cpp

View File

@ -0,0 +1,95 @@
// Copyright 2017 The NXT 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 "common/BitSetIterator.h"
#include "BindGroupD3D12.h"
#include "BindGroupLayoutD3D12.h"
#include "BufferD3D12.h"
#include "SamplerD3D12.h"
#include "TextureD3D12.h"
#include "D3D12Backend.h"
namespace backend {
namespace d3d12 {
BindGroup::BindGroup(Device* device, BindGroupBuilder* builder)
: BindGroupBase(builder), device(device) {
}
void BindGroup::RecordDescriptors(const DescriptorHeapHandle &cbvUavSrvHeapStart, uint32_t* cbvUavSrvHeapOffset, const DescriptorHeapHandle &samplerHeapStart, uint32_t* samplerHeapOffset, uint64_t serial) {
heapSerial = serial;
const auto* bgl = ToBackend(GetLayout());
const auto& layout = bgl->GetBindingInfo();
// Save the offset to the start of the descriptor table in the heap
this->cbvUavSrvHeapOffset = *cbvUavSrvHeapOffset;
this->samplerHeapOffset = *samplerHeapOffset;
const auto& bindingOffsets = bgl->GetBindingOffsets();
auto d3d12Device = device->GetD3D12Device();
for (uint32_t binding : IterateBitSet(layout.mask)) {
switch (layout.types[binding]) {
case nxt::BindingType::UniformBuffer:
{
auto* view = ToBackend(GetBindingAsBufferView(binding));
auto& cbv = view->GetCBVDescriptor();
d3d12Device->CreateConstantBufferView(&cbv, cbvUavSrvHeapStart.GetCPUHandle(*cbvUavSrvHeapOffset + bindingOffsets[binding]));
}
break;
case nxt::BindingType::StorageBuffer:
{
auto* view = ToBackend(GetBindingAsBufferView(binding));
auto& uav = view->GetUAVDescriptor();
d3d12Device->CreateUnorderedAccessView(ToBackend(view->GetBuffer())->GetD3D12Resource().Get(), nullptr, &uav, cbvUavSrvHeapStart.GetCPUHandle(*cbvUavSrvHeapOffset + bindingOffsets[binding]));
}
break;
case nxt::BindingType::SampledTexture:
{
auto* view = ToBackend(GetBindingAsTextureView(binding));
auto& srv = view->GetSRVDescriptor();
d3d12Device->CreateShaderResourceView(ToBackend(view->GetTexture())->GetD3D12Resource().Get(), &srv, cbvUavSrvHeapStart.GetCPUHandle(*cbvUavSrvHeapOffset + bindingOffsets[binding]));
}
break;
case nxt::BindingType::Sampler:
{
auto* sampler = ToBackend(GetBindingAsSampler(binding));
auto& samplerDesc = sampler->GetSamplerDescriptor();
d3d12Device->CreateSampler(&samplerDesc, samplerHeapStart.GetCPUHandle(*samplerHeapOffset + bindingOffsets[binding]));
}
break;
}
}
// Offset by the number of descriptors created
*cbvUavSrvHeapOffset += bgl->GetCbvUavSrvDescriptorCount();
*samplerHeapOffset += bgl->GetSamplerDescriptorCount();
}
uint32_t BindGroup::GetCbvUavSrvHeapOffset() const {
return cbvUavSrvHeapOffset;
}
uint32_t BindGroup::GetSamplerHeapOffset() const {
return samplerHeapOffset;
}
uint64_t BindGroup::GetHeapSerial() const {
return heapSerial;
}
}
}

View File

@ -0,0 +1,50 @@
// Copyright 2017 The NXT 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 BACKEND_D3D12_BINDGROUPD3D12_H_
#define BACKEND_D3D12_BINDGROUPD3D12_H_
#include "common/BindGroup.h"
#include "d3d12_platform.h"
#include "DescriptorHeapAllocator.h"
namespace backend {
namespace d3d12 {
class Device;
class BindGroup : public BindGroupBase {
public:
BindGroup(Device* device, BindGroupBuilder* builder);
void RecordDescriptors(const DescriptorHeapHandle &cbvSrvUavHeapStart, uint32_t* cbvUavSrvHeapOffset, const DescriptorHeapHandle &samplerHeapStart, uint32_t* samplerHeapOffset, uint64_t serial);
uint32_t GetCbvUavSrvHeapOffset() const;
uint32_t GetSamplerHeapOffset() const;
uint64_t GetHeapSerial() const;
private:
Device* device;
uint32_t cbvUavSrvHeapOffset;
uint32_t samplerHeapOffset;
uint32_t cbvUavSrvCount = 0;
uint32_t samplerCount = 0;
uint64_t heapSerial = 0;
};
}
}
#endif // BACKEND_D3D12_BINDGROUPD3D12_H_

View File

@ -0,0 +1,132 @@
// Copyright 2017 The NXT 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 "BindGroupLayoutD3D12.h"
#include "common/BitSetIterator.h"
#include "D3D12Backend.h"
namespace backend {
namespace d3d12 {
BindGroupLayout::BindGroupLayout(Device* device, BindGroupLayoutBuilder* builder)
: BindGroupLayoutBase(builder), device(device), descriptorCounts {} {
const auto& groupInfo = GetBindingInfo();
for (uint32_t binding : IterateBitSet(groupInfo.mask)) {
switch (groupInfo.types[binding]) {
case nxt::BindingType::UniformBuffer:
bindingOffsets[binding] = descriptorCounts[CBV]++;
break;
case nxt::BindingType::StorageBuffer:
bindingOffsets[binding] = descriptorCounts[UAV]++;
break;
case nxt::BindingType::SampledTexture:
bindingOffsets[binding] = descriptorCounts[SRV]++;
break;
case nxt::BindingType::Sampler:
bindingOffsets[binding] = descriptorCounts[Sampler]++;
break;
}
}
auto SetDescriptorRange = [&](uint32_t index, uint32_t count, D3D12_DESCRIPTOR_RANGE_TYPE type) -> bool {
if (count == 0) {
return false;
}
auto& range = ranges[index];
range.RangeType = type;
range.NumDescriptors = count;
range.RegisterSpace = 0;
range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
// These ranges will be copied and range.BaseShaderRegister will be set in d3d12::PipelineLayout to account for bind group register offsets
return true;
};
uint32_t rangeIndex = 0;
// Ranges 0-2 contain the CBV, UAV, and SRV ranges, if they exist, tightly packed
// Range 3 contains the Sampler range, if there is one
if (SetDescriptorRange(rangeIndex, descriptorCounts[CBV], D3D12_DESCRIPTOR_RANGE_TYPE_CBV)) {
rangeIndex++;
}
if (SetDescriptorRange(rangeIndex, descriptorCounts[UAV], D3D12_DESCRIPTOR_RANGE_TYPE_UAV)) {
rangeIndex++;
}
if (SetDescriptorRange(rangeIndex, descriptorCounts[SRV], D3D12_DESCRIPTOR_RANGE_TYPE_SRV)) {
rangeIndex++;
}
SetDescriptorRange(Sampler, descriptorCounts[Sampler], D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER);
// descriptors ranges are offset by the offset + size of the previous range
std::array<uint32_t, DescriptorType::Count> descriptorOffsets;
descriptorOffsets[CBV] = 0;
descriptorOffsets[UAV] = descriptorOffsets[CBV] + descriptorCounts[CBV];
descriptorOffsets[SRV] = descriptorOffsets[UAV] + descriptorCounts[UAV];
descriptorOffsets[Sampler] = 0; // samplers are in a different heap
for (uint32_t binding : IterateBitSet(groupInfo.mask)) {
switch (groupInfo.types[binding]) {
case nxt::BindingType::UniformBuffer:
bindingOffsets[binding] += descriptorOffsets[CBV];
break;
case nxt::BindingType::StorageBuffer:
bindingOffsets[binding] += descriptorOffsets[UAV];
break;
case nxt::BindingType::SampledTexture:
bindingOffsets[binding] += descriptorOffsets[SRV];
break;
case nxt::BindingType::Sampler:
bindingOffsets[binding] += descriptorOffsets[Sampler];
break;
}
}
}
const std::array<uint32_t, kMaxBindingsPerGroup>& BindGroupLayout::GetBindingOffsets() const {
return bindingOffsets;
}
uint32_t BindGroupLayout::GetCbvUavSrvDescriptorTableSize() const {
return (
static_cast<uint32_t>(descriptorCounts[CBV] > 0) +
static_cast<uint32_t>(descriptorCounts[UAV] > 0) +
static_cast<uint32_t>(descriptorCounts[SRV] > 0)
);
}
uint32_t BindGroupLayout::GetSamplerDescriptorTableSize() const {
return descriptorCounts[Sampler] > 0;
}
uint32_t BindGroupLayout::GetCbvUavSrvDescriptorCount() const {
return descriptorCounts[CBV] + descriptorCounts[UAV] + descriptorCounts[SRV];
}
uint32_t BindGroupLayout::GetSamplerDescriptorCount() const {
return descriptorCounts[Sampler];
}
const D3D12_DESCRIPTOR_RANGE* BindGroupLayout::GetCbvUavSrvDescriptorRanges() const {
return ranges;
}
const D3D12_DESCRIPTOR_RANGE* BindGroupLayout::GetSamplerDescriptorRanges() const {
return &ranges[Sampler];
}
}
}

View File

@ -0,0 +1,57 @@
// Copyright 2017 The NXT 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 BACKEND_D3D12_BINDGROUPLAYOUTD3D12_H_
#define BACKEND_D3D12_BINDGROUPLAYOUTD3D12_H_
#include "common/BindGroupLayout.h"
#include "d3d12_platform.h"
namespace backend {
namespace d3d12 {
class Device;
class BindGroupLayout : public BindGroupLayoutBase {
public:
BindGroupLayout(Device* device, BindGroupLayoutBuilder* builder);
enum DescriptorType {
CBV,
UAV,
SRV,
Sampler,
Count,
};
const std::array<uint32_t, kMaxBindingsPerGroup>& GetBindingOffsets() const;
uint32_t GetCbvUavSrvDescriptorTableSize() const;
uint32_t GetSamplerDescriptorTableSize() const;
uint32_t GetCbvUavSrvDescriptorCount() const;
uint32_t GetSamplerDescriptorCount() const;
const D3D12_DESCRIPTOR_RANGE* GetCbvUavSrvDescriptorRanges() const;
const D3D12_DESCRIPTOR_RANGE* GetSamplerDescriptorRanges() const;
private:
Device* device;
std::array<uint32_t, kMaxBindingsPerGroup> bindingOffsets;
std::array<uint32_t, DescriptorType::Count> descriptorCounts;
D3D12_DESCRIPTOR_RANGE ranges[DescriptorType::Count];
};
}
}
#endif // BACKEND_D3D12_BINDGROUPLAYOUTD3D12_H_

View File

@ -16,8 +16,10 @@
#include "common/Commands.h" #include "common/Commands.h"
#include "D3D12Backend.h" #include "D3D12Backend.h"
#include "DescriptorHeapAllocator.h" #include "BindGroupD3D12.h"
#include "BindGroupLayoutD3D12.h"
#include "BufferD3D12.h" #include "BufferD3D12.h"
#include "DescriptorHeapAllocator.h"
#include "InputStateD3D12.h" #include "InputStateD3D12.h"
#include "PipelineD3D12.h" #include "PipelineD3D12.h"
#include "PipelineLayoutD3D12.h" #include "PipelineLayoutD3D12.h"
@ -40,142 +42,152 @@ namespace d3d12 {
struct BindGroupStateTracker { struct BindGroupStateTracker {
uint32_t cbvSrvUavDescriptorIndex = 0; uint32_t cbvSrvUavDescriptorIndex = 0;
uint32_t samplerDescriptorIndex = 0; uint32_t samplerDescriptorIndex = 0;
DescriptorHeapHandle cbvSrvUavDescriptorHeap; DescriptorHeapHandle cbvSrvUavCPUDescriptorHeap;
DescriptorHeapHandle samplerDescriptorHeap; DescriptorHeapHandle samplerCPUDescriptorHeap;
DescriptorHeapHandle cbvSrvUavGPUDescriptorHeap;
DescriptorHeapHandle samplerGPUDescriptorHeap;
std::array<BindGroup*, kMaxBindGroups> bindGroups = {}; std::array<BindGroup*, kMaxBindGroups> bindGroups = {};
Device* device; Device* device;
BindGroupStateTracker(Device* device) : device(device) { BindGroupStateTracker(Device* device) : device(device) {
} }
void TrackSetBindGroup(const BindGroupLayoutBase* bindGroupLayout) { void TrackSetBindGroup(BindGroup* group, uint32_t index) {
const auto& layout = bindGroupLayout->GetBindingInfo(); if (bindGroups[index] != group) {
bindGroups[index] = group;
for (size_t binding = 0; binding < layout.mask.size(); ++binding) { // Descriptors don't need to be recorded if they have already been recorded in the heap. Indices are only updated when descriptors are recorded
if (!layout.mask[binding]) { const uint64_t serial = device->GetSerial();
continue; if (group->GetHeapSerial() != serial) {
} group->RecordDescriptors(cbvSrvUavCPUDescriptorHeap, &cbvSrvUavDescriptorIndex, samplerCPUDescriptorHeap, &samplerDescriptorIndex, serial);
switch (layout.types[binding]) {
case nxt::BindingType::UniformBuffer:
case nxt::BindingType::StorageBuffer:
case nxt::BindingType::SampledTexture:
cbvSrvUavDescriptorIndex++;
case nxt::BindingType::Sampler:
samplerDescriptorIndex++;
} }
} }
} }
void SetBindGroup(Pipeline* pipeline, BindGroup* group, uint32_t index, ComPtr<ID3D12GraphicsCommandList> commandList) { void TrackSetBindInheritedGroup(uint32_t index) {
const auto& layout = group->GetLayout()->GetBindingInfo();
// these indices are the beginning of the descriptor table
uint32_t cbvSrvUavDescriptorStart = cbvSrvUavDescriptorIndex;
uint32_t samplerDescriptorStart = samplerDescriptorIndex;
bindGroups[index] = group;
PipelineLayout* pipelineLayout = ToBackend(pipeline->GetLayout());
// these indices are the offsets from the start of the descriptor table
uint32_t cbvIndex = pipelineLayout->GetDescriptorStartingIndex(index, PipelineLayout::Descriptor::Type::CBV);
uint32_t uavIndex = pipelineLayout->GetDescriptorStartingIndex(index, PipelineLayout::Descriptor::Type::UAV);
uint32_t srvIndex = pipelineLayout->GetDescriptorStartingIndex(index, PipelineLayout::Descriptor::Type::SRV);
uint32_t samplerIndex = pipelineLayout->GetDescriptorStartingIndex(index, PipelineLayout::Descriptor::Type::Sampler);
for (size_t binding = 0; binding < layout.mask.size(); ++binding) {
if (!layout.mask[binding]) {
continue;
}
switch (layout.types[binding]) {
case nxt::BindingType::UniformBuffer:
{
auto* view = ToBackend(group->GetBindingAsBufferView(binding));
auto* buffer = ToBackend(view->GetBuffer());
auto& cbvDesc = view->GetCBVDescriptor();
device->GetD3D12Device()->CreateConstantBufferView(&cbvDesc, cbvSrvUavDescriptorHeap.GetCPUHandle(cbvSrvUavDescriptorStart + cbvIndex++));
cbvSrvUavDescriptorIndex++;
}
break;
case nxt::BindingType::StorageBuffer:
{
auto* view = ToBackend(group->GetBindingAsBufferView(binding));
auto* buffer = ToBackend(view->GetBuffer());
auto& uavDesc = view->GetUAVDescriptor();
device->GetD3D12Device()->CreateUnorderedAccessView(buffer->GetD3D12Resource().Get(), nullptr, &uavDesc, cbvSrvUavDescriptorHeap.GetCPUHandle(cbvSrvUavDescriptorStart + uavIndex++));
cbvSrvUavDescriptorIndex++;
}
break;
case nxt::BindingType::SampledTexture:
{
auto* texture = ToBackend(group->GetBindingAsTextureView(binding)->GetTexture());
auto& srvDesc = texture->GetSRVDescriptor();
device->GetD3D12Device()->CreateShaderResourceView(texture->GetD3D12Resource().Get(), &srvDesc, cbvSrvUavDescriptorHeap.GetCPUHandle(cbvSrvUavDescriptorStart + srvIndex++));
cbvSrvUavDescriptorIndex++;
}
break;
case nxt::BindingType::Sampler:
{
auto* sampler = ToBackend(group->GetBindingAsSampler(binding));
auto& samplerDesc = sampler->GetSamplerDescriptor();
device->GetD3D12Device()->CreateSampler(&samplerDesc, samplerDescriptorHeap.GetCPUHandle(samplerDescriptorStart + samplerIndex++));
samplerDescriptorIndex++;
}
break;
}
}
if (cbvSrvUavDescriptorStart != cbvSrvUavDescriptorIndex) {
uint32_t parameterIndex = pipelineLayout->GetCBVSRVUAVRootParameterIndex(index);
if (pipeline->IsCompute()) {
commandList->SetComputeRootDescriptorTable(parameterIndex, cbvSrvUavDescriptorHeap.GetGPUHandle(cbvSrvUavDescriptorStart));
} else {
commandList->SetGraphicsRootDescriptorTable(parameterIndex, cbvSrvUavDescriptorHeap.GetGPUHandle(cbvSrvUavDescriptorStart));
}
}
if (samplerDescriptorStart != samplerDescriptorIndex) {
uint32_t parameterIndex = pipelineLayout->GetSamplerRootParameterIndex(index);
if (pipeline->IsCompute()) {
commandList->SetComputeRootDescriptorTable(parameterIndex, samplerDescriptorHeap.GetGPUHandle(samplerDescriptorStart));
} else {
commandList->SetGraphicsRootDescriptorTable(parameterIndex, samplerDescriptorHeap.GetGPUHandle(samplerDescriptorStart));
}
}
}
void SetInheritedBindGroup(Pipeline* pipeline, uint32_t index, ComPtr<ID3D12GraphicsCommandList> commandList) {
BindGroup* group = bindGroups[index]; BindGroup* group = bindGroups[index];
ASSERT(group != nullptr); if (group != nullptr) {
SetBindGroup(pipeline, group, index, commandList); TrackSetBindGroup(group, index);
}
} }
void AllocateAndSetDescriptorHeaps(Device* device, ComPtr<ID3D12GraphicsCommandList> commandList) { void SetBindGroup(ComPtr<ID3D12GraphicsCommandList> commandList, Pipeline* pipeline, BindGroup* group, uint32_t index, bool force = false) {
cbvSrvUavDescriptorHeap = device->GetDescriptorHeapAllocator()->Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, cbvSrvUavDescriptorIndex); if (bindGroups[index] != group || force) {
samplerDescriptorHeap = device->GetDescriptorHeapAllocator()->Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, samplerDescriptorIndex); bindGroups[index] = group;
ID3D12DescriptorHeap* descriptorHeaps[2] = { cbvSrvUavDescriptorHeap.Get(), samplerDescriptorHeap.Get() }; PipelineLayout* pipelineLayout = ToBackend(pipeline->GetLayout());
if (descriptorHeaps[0] && descriptorHeaps[1]) { uint32_t cbvUavSrvCount = ToBackend(group->GetLayout())->GetCbvUavSrvDescriptorCount();
commandList->SetDescriptorHeaps(2, descriptorHeaps); uint32_t samplerCount = ToBackend(group->GetLayout())->GetSamplerDescriptorCount();
} else if (descriptorHeaps[0]) {
commandList->SetDescriptorHeaps(1, descriptorHeaps); if (cbvUavSrvCount > 0) {
} else if (descriptorHeaps[1]) { uint32_t parameterIndex = pipelineLayout->GetCbvUavSrvRootParameterIndex(index);
commandList->SetDescriptorHeaps(2, &descriptorHeaps[1]);
if (pipeline->IsCompute()) {
commandList->SetComputeRootDescriptorTable(parameterIndex, cbvSrvUavGPUDescriptorHeap.GetGPUHandle(group->GetCbvUavSrvHeapOffset()));
} else {
commandList->SetGraphicsRootDescriptorTable(parameterIndex, cbvSrvUavGPUDescriptorHeap.GetGPUHandle(group->GetCbvUavSrvHeapOffset()));
}
}
if (samplerCount > 0) {
uint32_t parameterIndex = pipelineLayout->GetSamplerRootParameterIndex(index);
if (pipeline->IsCompute()) {
commandList->SetComputeRootDescriptorTable(parameterIndex, samplerGPUDescriptorHeap.GetGPUHandle(group->GetSamplerHeapOffset()));
} else {
commandList->SetGraphicsRootDescriptorTable(parameterIndex, samplerGPUDescriptorHeap.GetGPUHandle(group->GetSamplerHeapOffset()));
}
}
}
}
void SetInheritedBindGroup(ComPtr<ID3D12GraphicsCommandList> commandList, Pipeline* pipeline, uint32_t index) {
BindGroup* group = bindGroups[index];
if (group != nullptr) {
SetBindGroup(commandList, pipeline, group, index, true);
} }
} }
void Reset() { void Reset() {
cbvSrvUavDescriptorIndex = 0;
samplerDescriptorIndex = 0;
for (uint32_t i = 0; i < kMaxBindGroups; ++i) { for (uint32_t i = 0; i < kMaxBindGroups; ++i) {
bindGroups[i] = nullptr; bindGroups[i] = nullptr;
} }
} }
}; };
void AllocateAndSetDescriptorHeaps(Device* device, BindGroupStateTracker* bindingTracker, CommandIterator* commands) {
auto* descriptorHeapAllocator = device->GetDescriptorHeapAllocator();
// TODO(enga@google.com): This currently allocates CPU heaps of arbitrarily chosen sizes
// This will not work if there are too many descriptors
bindingTracker->cbvSrvUavCPUDescriptorHeap = descriptorHeapAllocator->AllocateCPUHeap(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 8192);
bindingTracker->samplerCPUDescriptorHeap = descriptorHeapAllocator->AllocateCPUHeap(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, 2048);
{
Command type;
Pipeline* lastPipeline = nullptr;
PipelineLayout* lastLayout = nullptr;
while (commands->NextCommandId(&type)) {
switch (type) {
case Command::SetPipeline:
{
SetPipelineCmd* cmd = commands->NextCommand<SetPipelineCmd>();
Pipeline* pipeline = ToBackend(cmd->pipeline).Get();
PipelineLayout* layout = ToBackend(pipeline->GetLayout());
if (lastLayout) {
auto mask = layout->GetBindGroupsLayoutMask();
for (uint32_t i = 0; i < kMaxBindGroups; ++i) {
// matching bind groups are inherited until they differ
if (mask[i] && lastLayout->GetBindGroupLayout(i) == layout->GetBindGroupLayout(i)) {
bindingTracker->TrackSetBindInheritedGroup(i);
} else {
break;
}
}
}
lastPipeline = pipeline;
lastLayout = layout;
}
break;
case Command::SetBindGroup:
{
SetBindGroupCmd* cmd = commands->NextCommand<SetBindGroupCmd>();
BindGroup* group = ToBackend(cmd->group.Get());
bindingTracker->TrackSetBindGroup(group, cmd->index);
}
break;
default:
SkipCommand(commands, type);
}
}
commands->Reset();
}
if (bindingTracker->cbvSrvUavDescriptorIndex > 0) {
// Allocate a GPU-visible heap and copy from the CPU-only heap to the GPU-visible heap
bindingTracker->cbvSrvUavGPUDescriptorHeap = descriptorHeapAllocator->AllocateGPUHeap(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, bindingTracker->cbvSrvUavDescriptorIndex);
device->GetD3D12Device()->CopyDescriptorsSimple(
bindingTracker->cbvSrvUavDescriptorIndex,
bindingTracker->cbvSrvUavGPUDescriptorHeap.GetCPUHandle(0),
bindingTracker->cbvSrvUavCPUDescriptorHeap.GetCPUHandle(0),
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
}
if (bindingTracker->samplerDescriptorIndex > 0) {
bindingTracker->samplerGPUDescriptorHeap = descriptorHeapAllocator->AllocateGPUHeap(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, bindingTracker->samplerDescriptorIndex);
device->GetD3D12Device()->CopyDescriptorsSimple(
bindingTracker->samplerDescriptorIndex,
bindingTracker->samplerGPUDescriptorHeap.GetCPUHandle(0),
bindingTracker->samplerCPUDescriptorHeap.GetCPUHandle(0),
D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
}
}
} }
CommandBuffer::CommandBuffer(Device* device, CommandBufferBuilder* builder) CommandBuffer::CommandBuffer(Device* device, CommandBufferBuilder* builder)
@ -188,55 +200,18 @@ namespace d3d12 {
void CommandBuffer::FillCommands(ComPtr<ID3D12GraphicsCommandList> commandList) { void CommandBuffer::FillCommands(ComPtr<ID3D12GraphicsCommandList> commandList) {
BindGroupStateTracker bindingTracker(device); BindGroupStateTracker bindingTracker(device);
AllocateAndSetDescriptorHeaps(device, &bindingTracker, &commands);
{
Command type;
Pipeline* lastPipeline = nullptr;
PipelineLayout* lastLayout = nullptr;
while(commands.NextCommandId(&type)) {
switch (type) {
case Command::SetPipeline:
{
SetPipelineCmd* cmd = commands.NextCommand<SetPipelineCmd>();
Pipeline* pipeline = ToBackend(cmd->pipeline).Get();
PipelineLayout* layout = ToBackend(pipeline->GetLayout());
if (lastLayout) {
auto mask = layout->GetBindGroupsLayoutMask();
for (uint32_t i = 0; i < kMaxBindGroups; ++i) {
// matching bind groups are inherited until they differ
if (mask[i] && lastLayout->GetBindGroupLayout(i) == layout->GetBindGroupLayout(i)) {
bindingTracker.TrackSetBindGroup(layout->GetBindGroupLayout(i));
} else {
break;
}
}
}
lastPipeline = pipeline;
lastLayout = layout;
}
break;
case Command::SetBindGroup:
{
SetBindGroupCmd* cmd = commands.NextCommand<SetBindGroupCmd>();
BindGroup* group = ToBackend(cmd->group.Get());
bindingTracker.TrackSetBindGroup(group->GetLayout());
}
break;
default:
SkipCommand(&commands, type);
}
}
commands.Reset();
}
bindingTracker.AllocateAndSetDescriptorHeaps(device, commandList);
bindingTracker.Reset(); bindingTracker.Reset();
ID3D12DescriptorHeap* descriptorHeaps[2] = { bindingTracker.cbvSrvUavGPUDescriptorHeap.Get(), bindingTracker.samplerGPUDescriptorHeap.Get() };
if (descriptorHeaps[0] && descriptorHeaps[1]) {
commandList->SetDescriptorHeaps(2, descriptorHeaps);
} else if (descriptorHeaps[0]) {
commandList->SetDescriptorHeaps(1, descriptorHeaps);
} else if (descriptorHeaps[1]) {
commandList->SetDescriptorHeaps(2, &descriptorHeaps[1]);
}
Command type; Command type;
Pipeline* lastPipeline = nullptr; Pipeline* lastPipeline = nullptr;
PipelineLayout* lastLayout = nullptr; PipelineLayout* lastLayout = nullptr;
@ -350,7 +325,7 @@ namespace d3d12 {
for (uint32_t i = 0; i < kMaxBindGroups; ++i) { for (uint32_t i = 0; i < kMaxBindGroups; ++i) {
// matching bind groups are inherited until they differ // matching bind groups are inherited until they differ
if (mask[i] && lastLayout->GetBindGroupLayout(i) == layout->GetBindGroupLayout(i)) { if (mask[i] && lastLayout->GetBindGroupLayout(i) == layout->GetBindGroupLayout(i)) {
bindingTracker.SetInheritedBindGroup(pipeline, i, commandList); bindingTracker.SetInheritedBindGroup(commandList, pipeline, i);
} else { } else {
break; break;
} }
@ -379,7 +354,7 @@ namespace d3d12 {
{ {
SetBindGroupCmd* cmd = commands.NextCommand<SetBindGroupCmd>(); SetBindGroupCmd* cmd = commands.NextCommand<SetBindGroupCmd>();
BindGroup* group = ToBackend(cmd->group.Get()); BindGroup* group = ToBackend(cmd->group.Get());
bindingTracker.SetBindGroup(lastPipeline, group, cmd->index, commandList); bindingTracker.SetBindGroup(commandList, lastPipeline, group, cmd->index);
} }
break; break;

View File

@ -14,6 +14,8 @@
#include "D3D12Backend.h" #include "D3D12Backend.h"
#include "BindGroupD3D12.h"
#include "BindGroupLayoutD3D12.h"
#include "BufferD3D12.h" #include "BufferD3D12.h"
#include "CommandBufferD3D12.h" #include "CommandBufferD3D12.h"
#include "InputStateD3D12.h" #include "InputStateD3D12.h"
@ -248,18 +250,6 @@ namespace d3d12 {
void Device::Release() { void Device::Release() {
} }
// Bind Group
BindGroup::BindGroup(Device* device, BindGroupBuilder* builder)
: BindGroupBase(builder), device(device) {
}
// Bind Group Layout
BindGroupLayout::BindGroupLayout(Device* device, BindGroupLayoutBuilder* builder)
: BindGroupLayoutBase(builder), device(device) {
}
// DepthStencilState // DepthStencilState
DepthStencilState::DepthStencilState(Device* device, DepthStencilStateBuilder* builder) DepthStencilState::DepthStencilState(Device* device, DepthStencilStateBuilder* builder)

View File

@ -155,23 +155,6 @@ namespace d3d12 {
D3D12_CPU_DESCRIPTOR_HANDLE renderTargetDescriptor; D3D12_CPU_DESCRIPTOR_HANDLE renderTargetDescriptor;
}; };
class BindGroup : public BindGroupBase {
public:
BindGroup(Device* device, BindGroupBuilder* builder);
private:
Device* device;
};
class BindGroupLayout : public BindGroupLayoutBase {
public:
BindGroupLayout(Device* device, BindGroupLayoutBuilder* builder);
private:
Device* device;
};
class Framebuffer : public FramebufferBase { class Framebuffer : public FramebufferBase {
public: public:
Framebuffer(Device* device, FramebufferBuilder* builder); Framebuffer(Device* device, FramebufferBuilder* builder);

View File

@ -55,40 +55,52 @@ namespace d3d12 {
} { } {
} }
DescriptorHeapHandle DescriptorHeapAllocator::Allocate(D3D12_DESCRIPTOR_HEAP_TYPE type, uint32_t count) { DescriptorHeapHandle DescriptorHeapAllocator::Allocate(D3D12_DESCRIPTOR_HEAP_TYPE type, uint32_t count, uint32_t allocationSize, DescriptorHeapInfo* heapInfo, D3D12_DESCRIPTOR_HEAP_FLAGS flags) {
// TODO(enga@google.com): This is just a linear allocator so the heap will quickly run out of space causing a new one to be allocated
// We should reuse heap subranges that have been released
if (count == 0) { if (count == 0) {
return DescriptorHeapHandle(); return DescriptorHeapHandle();
} }
auto& pools = descriptorHeapPools[type]; {
for (auto it : pools) { // If the current pool for this type has space, linearly allocate count bytes in the pool
auto& allocationInfo = it.second; auto& allocationInfo = heapInfo->second;
if (allocationInfo.remaining >= count) { if (allocationInfo.remaining >= count) {
DescriptorHeapHandle handle(it.first, sizeIncrements[type], allocationInfo.size - allocationInfo.remaining); DescriptorHeapHandle handle(heapInfo->first, sizeIncrements[type], allocationInfo.size - allocationInfo.remaining);
allocationInfo.remaining -= count; allocationInfo.remaining -= count;
Release(handle); Release(handle);
return handle; return handle;
} }
} }
ASSERT(count <= 2048); // TODO(enga@google.com): Have a very large CPU heap that's copied to GPU-visible heaps // If the pool has no more space, replace the pool with a new one of the specified size
uint32_t descriptorHeapSize = 2048; // TODO(enga@google.com): Allocate much more and use this as a pool
D3D12_DESCRIPTOR_HEAP_DESC heapDescriptor; D3D12_DESCRIPTOR_HEAP_DESC heapDescriptor;
heapDescriptor.Type = type; heapDescriptor.Type = type;
heapDescriptor.NumDescriptors = descriptorHeapSize; heapDescriptor.NumDescriptors = allocationSize;
heapDescriptor.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; heapDescriptor.Flags = flags;
heapDescriptor.NodeMask = 0; heapDescriptor.NodeMask = 0;
ComPtr<ID3D12DescriptorHeap> heap; ComPtr<ID3D12DescriptorHeap> heap;
ASSERT_SUCCESS(device->GetD3D12Device()->CreateDescriptorHeap(&heapDescriptor, IID_PPV_ARGS(&heap))); ASSERT_SUCCESS(device->GetD3D12Device()->CreateDescriptorHeap(&heapDescriptor, IID_PPV_ARGS(&heap)));
AllocationInfo allocationInfo = { descriptorHeapSize, descriptorHeapSize - count };
pools.emplace_back(std::make_pair(heap, allocationInfo)); AllocationInfo allocationInfo = { allocationSize, allocationSize - count };
*heapInfo = std::make_pair(heap, allocationInfo);
DescriptorHeapHandle handle(heap, sizeIncrements[type], 0); DescriptorHeapHandle handle(heap, sizeIncrements[type], 0);
Release(handle); Release(handle);
return handle; return handle;
} }
DescriptorHeapHandle DescriptorHeapAllocator::AllocateCPUHeap(D3D12_DESCRIPTOR_HEAP_TYPE type, uint32_t count) {
return Allocate(type, count, count, &cpuDescriptorHeapInfos[type], D3D12_DESCRIPTOR_HEAP_FLAG_NONE);
}
DescriptorHeapHandle DescriptorHeapAllocator::AllocateGPUHeap(D3D12_DESCRIPTOR_HEAP_TYPE type, uint32_t count) {
ASSERT(type == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV || type == D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
unsigned int heapSize = (type == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV ? kMaxCbvUavSrvHeapSize : kMaxSamplerHeapSize);
return Allocate(type, count, heapSize, &gpuDescriptorHeapInfos[type], D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE);
}
void DescriptorHeapAllocator::FreeDescriptorHeaps(uint64_t lastCompletedSerial) { void DescriptorHeapAllocator::FreeDescriptorHeaps(uint64_t lastCompletedSerial) {
releasedHandles.ClearUpTo(lastCompletedSerial); releasedHandles.ClearUpTo(lastCompletedSerial);
} }

View File

@ -27,6 +27,7 @@ namespace d3d12 {
class Device; class Device;
class DescriptorHeapHandle { class DescriptorHeapHandle {
public: public:
DescriptorHeapHandle(); DescriptorHeapHandle();
DescriptorHeapHandle(ComPtr<ID3D12DescriptorHeap> descriptorHeap, uint32_t sizeIncrement, uint32_t offset); DescriptorHeapHandle(ComPtr<ID3D12DescriptorHeap> descriptorHeap, uint32_t sizeIncrement, uint32_t offset);
@ -46,27 +47,30 @@ namespace d3d12 {
public: public:
DescriptorHeapAllocator(Device* device); DescriptorHeapAllocator(Device* device);
DescriptorHeapHandle Allocate(D3D12_DESCRIPTOR_HEAP_TYPE type, uint32_t count); DescriptorHeapHandle AllocateGPUHeap(D3D12_DESCRIPTOR_HEAP_TYPE type, uint32_t count);
DescriptorHeapHandle AllocateCPUHeap(D3D12_DESCRIPTOR_HEAP_TYPE type, uint32_t count);
void FreeDescriptorHeaps(uint64_t lastCompletedSerial); void FreeDescriptorHeaps(uint64_t lastCompletedSerial);
private: private:
static constexpr unsigned int kMaxCbvUavSrvHeapSize = 1000000;
static constexpr unsigned int kMaxSamplerHeapSize = 2048;
static constexpr unsigned int kDescriptorHeapTypes = D3D12_DESCRIPTOR_HEAP_TYPE::D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES;
struct AllocationInfo {
uint32_t size = 0;
uint32_t remaining = 0;
};
using DescriptorHeapInfo = std::pair<ComPtr<ID3D12DescriptorHeap>, AllocationInfo>;
DescriptorHeapHandle Allocate(D3D12_DESCRIPTOR_HEAP_TYPE type, uint32_t count, uint32_t allocationSize, DescriptorHeapInfo* heapInfo, D3D12_DESCRIPTOR_HEAP_FLAGS flags);
void Release(DescriptorHeapHandle handle); void Release(DescriptorHeapHandle handle);
Device* device; Device* device;
static constexpr unsigned int kDescriptorHeapTypes = D3D12_DESCRIPTOR_HEAP_TYPE::D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES;
struct AllocationInfo {
uint32_t size;
uint32_t remaining;
};
using DescriptorHeapPool = std::pair<ComPtr<ID3D12DescriptorHeap>, AllocationInfo>;
using DescriptorHeapPoolList = std::vector<DescriptorHeapPool>;
std::array<uint32_t, kDescriptorHeapTypes> sizeIncrements; std::array<uint32_t, kDescriptorHeapTypes> sizeIncrements;
std::array<DescriptorHeapPoolList, kDescriptorHeapTypes> descriptorHeapPools; std::array<DescriptorHeapInfo, kDescriptorHeapTypes> cpuDescriptorHeapInfos;
std::array<DescriptorHeapInfo, kDescriptorHeapTypes> gpuDescriptorHeapInfos;
SerialQueue<DescriptorHeapHandle> releasedHandles; SerialQueue<DescriptorHeapHandle> releasedHandles;
}; };

View File

@ -13,6 +13,8 @@
// limitations under the License. // limitations under the License.
#include "D3D12Backend.h" #include "D3D12Backend.h"
#include "BindGroupD3D12.h"
#include "BindGroupLayoutD3D12.h"
#include "BufferD3D12.h" #include "BufferD3D12.h"
#include "CommandBufferD3D12.h" #include "CommandBufferD3D12.h"
#include "InputStateD3D12.h" #include "InputStateD3D12.h"

View File

@ -48,22 +48,24 @@ namespace d3d12 {
// Set the root descriptor table parameter and copy ranges. Ranges are offset by the bind group index // Set the root descriptor table parameter and copy ranges. Ranges are offset by the bind group index
// Returns whether or not the parameter was set. A root parameter is not set if the number of ranges is 0 // Returns whether or not the parameter was set. A root parameter is not set if the number of ranges is 0
auto SetRootDescriptorTable = [&](uint32_t rangeCount, const D3D12_DESCRIPTOR_RANGE* descriptorRanges) -> bool { auto SetRootDescriptorTable = [&](uint32_t rangeCount, const D3D12_DESCRIPTOR_RANGE* descriptorRanges) -> bool {
if (rangeCount > 0) { if (rangeCount == 0) {
auto& rootParameter = rootParameters[parameterIndex]; return false;
rootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
rootParameter.DescriptorTable = rootParameterValues[parameterIndex].DescriptorTable;
rootParameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
rootParameter.DescriptorTable.NumDescriptorRanges = rangeCount;
rootParameter.DescriptorTable.pDescriptorRanges = &ranges[rangeIndex];
} }
auto& rootParameter = rootParameters[parameterIndex];
rootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
rootParameter.DescriptorTable = rootParameterValues[parameterIndex].DescriptorTable;
rootParameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
rootParameter.DescriptorTable.NumDescriptorRanges = rangeCount;
rootParameter.DescriptorTable.pDescriptorRanges = &ranges[rangeIndex];
for (uint32_t i = 0; i < rangeCount; ++i) { for (uint32_t i = 0; i < rangeCount; ++i) {
ranges[rangeIndex] = descriptorRanges[i]; ranges[rangeIndex] = descriptorRanges[i];
ranges[rangeIndex].BaseShaderRegister = group * kMaxBindingsPerGroup; ranges[rangeIndex].BaseShaderRegister = group * kMaxBindingsPerGroup;
rangeIndex++; rangeIndex++;
} }
return (rangeCount > 0); return true;
}; };
if (SetRootDescriptorTable(bindGroupLayout->GetCbvUavSrvDescriptorTableSize(), bindGroupLayout->GetCbvUavSrvDescriptorRanges())) { if (SetRootDescriptorTable(bindGroupLayout->GetCbvUavSrvDescriptorTableSize(), bindGroupLayout->GetCbvUavSrvDescriptorRanges())) {

View File

@ -28,34 +28,16 @@ namespace d3d12 {
public: public:
PipelineLayout(Device* device, PipelineLayoutBuilder* builder); PipelineLayout(Device* device, PipelineLayoutBuilder* builder);
class Descriptor {
public:
enum class Type {
CBV,
UAV,
SRV,
Sampler,
Count
};
static constexpr unsigned int TypeCount = static_cast<typename std::underlying_type<Type>::type>(Type::Count);
};
uint32_t GetCbvUavSrvRootParameterIndex(uint32_t group) const; uint32_t GetCbvUavSrvRootParameterIndex(uint32_t group) const;
uint32_t GetSamplerRootParameterIndex(uint32_t group) const; uint32_t GetSamplerRootParameterIndex(uint32_t group) const;
ComPtr<ID3D12RootSignature> GetRootSignature(); ComPtr<ID3D12RootSignature> GetRootSignature();
private: private:
static constexpr unsigned int ToIndex(Descriptor::Type type) {
return static_cast<typename std::underlying_type<Descriptor::Type>::type>(type);
}
Device* device; Device* device;
std::array<uint32_t, kMaxBindGroups> cbvUavSrvRootParameterInfo; std::array<uint32_t, kMaxBindGroups> cbvUavSrvRootParameterInfo;
std::array<uint32_t, kMaxBindGroups> samplerRootParameterInfo; std::array<uint32_t, kMaxBindGroups> samplerRootParameterInfo;
std::array<std::array<uint32_t, Descriptor::TypeCount>, kMaxBindGroups> descriptorCountInfo;
ComPtr<ID3D12RootSignature> rootSignature; ComPtr<ID3D12RootSignature> rootSignature;
}; };

View File

@ -40,24 +40,23 @@ namespace d3d12 {
Count, Count,
}; };
std::array<uint32_t, RegisterType::Count * kMaxBindGroups> baseRegisters = {};
const auto& resources = compiler.get_shader_resources();
// rename bindings so that each register type b/u/t/s starts at 0 and then offset by kMaxBindingsPerGroup * bindGroupIndex // rename bindings so that each register type b/u/t/s starts at 0 and then offset by kMaxBindingsPerGroup * bindGroupIndex
auto RenumberBindings = [&](std::vector<spirv_cross::Resource> resources, uint32_t offset) { auto RenumberBindings = [&](std::vector<spirv_cross::Resource> resources) {
std::array<uint32_t, kMaxBindGroups> baseRegisters = {};
for (const auto& resource : resources) { for (const auto& resource : resources) {
auto bindGroupIndex = compiler.get_decoration(resource.id, spv::DecorationDescriptorSet); auto bindGroupIndex = compiler.get_decoration(resource.id, spv::DecorationDescriptorSet);
auto& baseRegister = baseRegisters[RegisterType::Count * bindGroupIndex + offset]; auto& baseRegister = baseRegisters[bindGroupIndex];
auto bindGroupOffset = bindGroupIndex * kMaxBindingsPerGroup; auto bindGroupOffset = bindGroupIndex * kMaxBindingsPerGroup;
compiler.set_decoration(resource.id, spv::DecorationBinding, bindGroupOffset + baseRegister++); compiler.set_decoration(resource.id, spv::DecorationBinding, bindGroupOffset + baseRegister++);
} }
}; };
RenumberBindings(resources.uniform_buffers, RegisterType::Buffer); const auto& resources = compiler.get_shader_resources();
RenumberBindings(resources.storage_buffers, RegisterType::UnorderedAccess); RenumberBindings(resources.uniform_buffers); // c
RenumberBindings(resources.separate_images, RegisterType::Texture); RenumberBindings(resources.storage_buffers); // u
RenumberBindings(resources.separate_samplers, RegisterType::Sampler); RenumberBindings(resources.separate_images); // t
RenumberBindings(resources.separate_samplers); // s
hlslSource = compiler.compile(); hlslSource = compiler.compile();