Path: blob/master/src/util/d3d12_descriptor_heap_manager.h
4223 views
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <[email protected]>1// SPDX-License-Identifier: CC-BY-NC-ND-4.023#pragma once45#include "common/hash_combine.h"6#include "common/types.h"7#include "common/windows_headers.h"89#include <bitset>10#include <cstring>11#include <d3d12.h>12#include <unordered_map>13#include <vector>14#include <wrl/client.h>1516class Error;1718// This class provides an abstraction for D3D12 descriptor heaps.19struct D3D12DescriptorHandle final20{21enum : u3222{23INVALID_INDEX = 0xFFFFFFFF24};2526D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle{};27D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle{};28u32 index = INVALID_INDEX;2930ALWAYS_INLINE operator bool() const { return index != INVALID_INDEX; }3132ALWAYS_INLINE operator D3D12_CPU_DESCRIPTOR_HANDLE() const { return cpu_handle; }33ALWAYS_INLINE operator D3D12_GPU_DESCRIPTOR_HANDLE() const { return gpu_handle; }3435ALWAYS_INLINE bool operator==(const D3D12DescriptorHandle& rhs) const { return (index == rhs.index); }36ALWAYS_INLINE bool operator!=(const D3D12DescriptorHandle& rhs) const { return (index != rhs.index); }37ALWAYS_INLINE bool operator<(const D3D12DescriptorHandle& rhs) const { return (index < rhs.index); }38ALWAYS_INLINE bool operator<=(const D3D12DescriptorHandle& rhs) const { return (index <= rhs.index); }39ALWAYS_INLINE bool operator>(const D3D12DescriptorHandle& rhs) const { return (index > rhs.index); }40ALWAYS_INLINE bool operator>=(const D3D12DescriptorHandle& rhs) const { return (index >= rhs.index); }4142ALWAYS_INLINE void Clear()43{44cpu_handle = {};45gpu_handle = {};46index = INVALID_INDEX;47}48};4950class D3D12DescriptorHeapManager final51{52public:53D3D12DescriptorHeapManager();54~D3D12DescriptorHeapManager();5556ID3D12DescriptorHeap* GetDescriptorHeap() const { return m_descriptor_heap.Get(); }57u32 GetDescriptorIncrementSize() const { return m_descriptor_increment_size; }5859bool Create(ID3D12Device* device, D3D12_DESCRIPTOR_HEAP_TYPE type, u32 num_descriptors, bool shader_visible,60Error* error);61void Destroy();6263bool Allocate(D3D12DescriptorHandle* handle);64void Free(D3D12DescriptorHandle* handle);65void Free(u32 index);6667private:68Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> m_descriptor_heap;69u32 m_num_descriptors = 0;70u32 m_descriptor_increment_size = 0;71bool m_shader_visible = false;7273D3D12_CPU_DESCRIPTOR_HANDLE m_heap_base_cpu = {};74D3D12_GPU_DESCRIPTOR_HANDLE m_heap_base_gpu = {};7576static constexpr u32 BITSET_SIZE = 1024;77using BitSetType = std::bitset<BITSET_SIZE>;78std::vector<BitSetType> m_free_slots = {};79};8081class D3D12DescriptorAllocator82{83public:84D3D12DescriptorAllocator();85~D3D12DescriptorAllocator();8687ALWAYS_INLINE ID3D12DescriptorHeap* GetDescriptorHeap() const { return m_descriptor_heap.Get(); }88ALWAYS_INLINE u32 GetDescriptorIncrementSize() const { return m_descriptor_increment_size; }8990bool Create(ID3D12Device* device, D3D12_DESCRIPTOR_HEAP_TYPE type, u32 num_descriptors, Error* error);91void Destroy();9293bool Allocate(u32 num_handles, D3D12DescriptorHandle* out_base_handle);94void Reset();9596private:97Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> m_descriptor_heap;98u32 m_descriptor_increment_size = 0;99u32 m_num_descriptors = 0;100u32 m_current_offset = 0;101102D3D12_CPU_DESCRIPTOR_HANDLE m_heap_base_cpu = {};103D3D12_GPU_DESCRIPTOR_HANDLE m_heap_base_gpu = {};104};105106template<u32 NumSamplers>107class D3D12GroupedSamplerAllocator : private D3D12DescriptorAllocator108{109struct Key110{111u32 idx[NumSamplers];112113ALWAYS_INLINE bool operator==(const Key& rhs) const { return (std::memcmp(idx, rhs.idx, sizeof(idx)) == 0); }114ALWAYS_INLINE bool operator!=(const Key& rhs) const { return (std::memcmp(idx, rhs.idx, sizeof(idx)) != 0); }115};116117struct KeyHash118{119ALWAYS_INLINE std::size_t operator()(const Key& key) const120{121size_t seed = 0;122for (u32 i : key.idx)123hash_combine(seed, i);124return seed;125}126};127128public:129D3D12GroupedSamplerAllocator();130~D3D12GroupedSamplerAllocator();131132using D3D12DescriptorAllocator::GetDescriptorHeap;133using D3D12DescriptorAllocator::GetDescriptorIncrementSize;134135bool Create(ID3D12Device* device, u32 num_descriptors, Error* error);136void Destroy();137138bool LookupSingle(ID3D12Device* device, D3D12DescriptorHandle* gpu_handle, const D3D12DescriptorHandle& cpu_handle);139bool LookupGroup(ID3D12Device* device, D3D12DescriptorHandle* gpu_handle, const D3D12DescriptorHandle* cpu_handles);140141// Clears cache but doesn't reset allocator.142void InvalidateCache();143144void Reset();145bool ShouldReset() const;146147private:148std::unordered_map<Key, D3D12DescriptorHandle, KeyHash> m_groups;149};150151template<u32 NumSamplers>152D3D12GroupedSamplerAllocator<NumSamplers>::D3D12GroupedSamplerAllocator() = default;153154template<u32 NumSamplers>155D3D12GroupedSamplerAllocator<NumSamplers>::~D3D12GroupedSamplerAllocator() = default;156157template<u32 NumSamplers>158bool D3D12GroupedSamplerAllocator<NumSamplers>::Create(ID3D12Device* device, u32 num_descriptors, Error* error)159{160return D3D12DescriptorAllocator::Create(device, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, num_descriptors, error);161}162163template<u32 NumSamplers>164void D3D12GroupedSamplerAllocator<NumSamplers>::Destroy()165{166D3D12DescriptorAllocator::Destroy();167}168169template<u32 NumSamplers>170void D3D12GroupedSamplerAllocator<NumSamplers>::Reset()171{172m_groups.clear();173D3D12DescriptorAllocator::Reset();174}175176template<u32 NumSamplers>177void D3D12GroupedSamplerAllocator<NumSamplers>::InvalidateCache()178{179m_groups.clear();180}181182template<u32 NumSamplers>183bool D3D12GroupedSamplerAllocator<NumSamplers>::LookupSingle(ID3D12Device* device, D3D12DescriptorHandle* gpu_handle,184const D3D12DescriptorHandle& cpu_handle)185{186Key key;187key.idx[0] = cpu_handle.index;188for (u32 i = 1; i < NumSamplers; i++)189key.idx[i] = 0;190191auto it = m_groups.find(key);192if (it != m_groups.end())193{194*gpu_handle = it->second;195return true;196}197198if (!Allocate(1, gpu_handle))199return false;200201device->CopyDescriptorsSimple(1, *gpu_handle, cpu_handle, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);202m_groups.emplace(key, *gpu_handle);203return true;204}205206template<u32 NumSamplers>207bool D3D12GroupedSamplerAllocator<NumSamplers>::LookupGroup(ID3D12Device* device, D3D12DescriptorHandle* gpu_handle,208const D3D12DescriptorHandle* cpu_handles)209{210Key key;211for (u32 i = 0; i < NumSamplers; i++)212key.idx[i] = cpu_handles[i].index;213214auto it = m_groups.find(key);215if (it != m_groups.end())216{217*gpu_handle = it->second;218return true;219}220221if (!Allocate(NumSamplers, gpu_handle))222return false;223224D3D12_CPU_DESCRIPTOR_HANDLE dst_handle = *gpu_handle;225UINT dst_size = NumSamplers;226D3D12_CPU_DESCRIPTOR_HANDLE src_handles[NumSamplers];227UINT src_sizes[NumSamplers];228for (u32 i = 0; i < NumSamplers; i++)229{230src_handles[i] = cpu_handles[i];231src_sizes[i] = 1;232}233device->CopyDescriptors(1, &dst_handle, &dst_size, NumSamplers, src_handles, src_sizes,234D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);235236m_groups.emplace(key, *gpu_handle);237return true;238}239240template<u32 NumSamplers>241bool D3D12GroupedSamplerAllocator<NumSamplers>::ShouldReset() const242{243// We only reset the sampler heap if more than half of the descriptors are used.244// This saves descriptor copying when there isn't a large number of sampler configs per frame.245return m_groups.size() >= (D3D12_MAX_SHADER_VISIBLE_SAMPLER_HEAP_SIZE / 2);246}247248249