duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

d3d12_descriptor_heap_manager.h (7622B)


      1 // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
      2 // SPDX-License-Identifier: (GPL-3.0 OR PolyForm-Strict-1.0.0)
      3 
      4 #pragma once
      5 
      6 #include "common/hash_combine.h"
      7 #include "common/types.h"
      8 #include "common/windows_headers.h"
      9 
     10 #include <bitset>
     11 #include <cstring>
     12 #include <d3d12.h>
     13 #include <unordered_map>
     14 #include <vector>
     15 #include <wrl/client.h>
     16 
     17 class Error;
     18 
     19 // This class provides an abstraction for D3D12 descriptor heaps.
     20 struct D3D12DescriptorHandle final
     21 {
     22   enum : u32
     23   {
     24     INVALID_INDEX = 0xFFFFFFFF
     25   };
     26 
     27   D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle{};
     28   D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle{};
     29   u32 index = INVALID_INDEX;
     30 
     31   ALWAYS_INLINE operator bool() const { return index != INVALID_INDEX; }
     32 
     33   ALWAYS_INLINE operator D3D12_CPU_DESCRIPTOR_HANDLE() const { return cpu_handle; }
     34   ALWAYS_INLINE operator D3D12_GPU_DESCRIPTOR_HANDLE() const { return gpu_handle; }
     35 
     36   ALWAYS_INLINE bool operator==(const D3D12DescriptorHandle& rhs) const { return (index == rhs.index); }
     37   ALWAYS_INLINE bool operator!=(const D3D12DescriptorHandle& rhs) const { return (index != rhs.index); }
     38   ALWAYS_INLINE bool operator<(const D3D12DescriptorHandle& rhs) const { return (index < rhs.index); }
     39   ALWAYS_INLINE bool operator<=(const D3D12DescriptorHandle& rhs) const { return (index <= rhs.index); }
     40   ALWAYS_INLINE bool operator>(const D3D12DescriptorHandle& rhs) const { return (index > rhs.index); }
     41   ALWAYS_INLINE bool operator>=(const D3D12DescriptorHandle& rhs) const { return (index >= rhs.index); }
     42 
     43   ALWAYS_INLINE void Clear()
     44   {
     45     cpu_handle = {};
     46     gpu_handle = {};
     47     index = INVALID_INDEX;
     48   }
     49 };
     50 
     51 class D3D12DescriptorHeapManager final
     52 {
     53 public:
     54   D3D12DescriptorHeapManager();
     55   ~D3D12DescriptorHeapManager();
     56 
     57   ID3D12DescriptorHeap* GetDescriptorHeap() const { return m_descriptor_heap.Get(); }
     58   u32 GetDescriptorIncrementSize() const { return m_descriptor_increment_size; }
     59 
     60   bool Create(ID3D12Device* device, D3D12_DESCRIPTOR_HEAP_TYPE type, u32 num_descriptors, bool shader_visible,
     61               Error* error);
     62   void Destroy();
     63 
     64   bool Allocate(D3D12DescriptorHandle* handle);
     65   void Free(D3D12DescriptorHandle* handle);
     66   void Free(u32 index);
     67 
     68 private:
     69   Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> m_descriptor_heap;
     70   u32 m_num_descriptors = 0;
     71   u32 m_descriptor_increment_size = 0;
     72   bool m_shader_visible = false;
     73 
     74   D3D12_CPU_DESCRIPTOR_HANDLE m_heap_base_cpu = {};
     75   D3D12_GPU_DESCRIPTOR_HANDLE m_heap_base_gpu = {};
     76 
     77   static constexpr u32 BITSET_SIZE = 1024;
     78   using BitSetType = std::bitset<BITSET_SIZE>;
     79   std::vector<BitSetType> m_free_slots = {};
     80 };
     81 
     82 class D3D12DescriptorAllocator
     83 {
     84 public:
     85   D3D12DescriptorAllocator();
     86   ~D3D12DescriptorAllocator();
     87 
     88   ALWAYS_INLINE ID3D12DescriptorHeap* GetDescriptorHeap() const { return m_descriptor_heap.Get(); }
     89   ALWAYS_INLINE u32 GetDescriptorIncrementSize() const { return m_descriptor_increment_size; }
     90 
     91   bool Create(ID3D12Device* device, D3D12_DESCRIPTOR_HEAP_TYPE type, u32 num_descriptors, Error* error);
     92   void Destroy();
     93 
     94   bool Allocate(u32 num_handles, D3D12DescriptorHandle* out_base_handle);
     95   void Reset();
     96 
     97 private:
     98   Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> m_descriptor_heap;
     99   u32 m_descriptor_increment_size = 0;
    100   u32 m_num_descriptors = 0;
    101   u32 m_current_offset = 0;
    102 
    103   D3D12_CPU_DESCRIPTOR_HANDLE m_heap_base_cpu = {};
    104   D3D12_GPU_DESCRIPTOR_HANDLE m_heap_base_gpu = {};
    105 };
    106 
    107 template<u32 NumSamplers>
    108 class D3D12GroupedSamplerAllocator : private D3D12DescriptorAllocator
    109 {
    110   struct Key
    111   {
    112     u32 idx[NumSamplers];
    113 
    114     ALWAYS_INLINE bool operator==(const Key& rhs) const { return (std::memcmp(idx, rhs.idx, sizeof(idx)) == 0); }
    115     ALWAYS_INLINE bool operator!=(const Key& rhs) const { return (std::memcmp(idx, rhs.idx, sizeof(idx)) != 0); }
    116   };
    117 
    118   struct KeyHash
    119   {
    120     ALWAYS_INLINE std::size_t operator()(const Key& key) const
    121     {
    122       size_t seed = 0;
    123       for (u32 i : key.idx)
    124         hash_combine(seed, i);
    125       return seed;
    126     }
    127   };
    128 
    129 public:
    130   D3D12GroupedSamplerAllocator();
    131   ~D3D12GroupedSamplerAllocator();
    132 
    133   using D3D12DescriptorAllocator::GetDescriptorHeap;
    134   using D3D12DescriptorAllocator::GetDescriptorIncrementSize;
    135 
    136   bool Create(ID3D12Device* device, u32 num_descriptors, Error* error);
    137   void Destroy();
    138 
    139   bool LookupSingle(ID3D12Device* device, D3D12DescriptorHandle* gpu_handle, const D3D12DescriptorHandle& cpu_handle);
    140   bool LookupGroup(ID3D12Device* device, D3D12DescriptorHandle* gpu_handle, const D3D12DescriptorHandle* cpu_handles);
    141 
    142   // Clears cache but doesn't reset allocator.
    143   void InvalidateCache();
    144 
    145   void Reset();
    146   bool ShouldReset() const;
    147 
    148 private:
    149   std::unordered_map<Key, D3D12DescriptorHandle, KeyHash> m_groups;
    150 };
    151 
    152 template<u32 NumSamplers>
    153 D3D12GroupedSamplerAllocator<NumSamplers>::D3D12GroupedSamplerAllocator() = default;
    154 
    155 template<u32 NumSamplers>
    156 D3D12GroupedSamplerAllocator<NumSamplers>::~D3D12GroupedSamplerAllocator() = default;
    157 
    158 template<u32 NumSamplers>
    159 bool D3D12GroupedSamplerAllocator<NumSamplers>::Create(ID3D12Device* device, u32 num_descriptors, Error* error)
    160 {
    161   return D3D12DescriptorAllocator::Create(device, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, num_descriptors, error);
    162 }
    163 
    164 template<u32 NumSamplers>
    165 void D3D12GroupedSamplerAllocator<NumSamplers>::Destroy()
    166 {
    167   D3D12DescriptorAllocator::Destroy();
    168 }
    169 
    170 template<u32 NumSamplers>
    171 void D3D12GroupedSamplerAllocator<NumSamplers>::Reset()
    172 {
    173   m_groups.clear();
    174   D3D12DescriptorAllocator::Reset();
    175 }
    176 
    177 template<u32 NumSamplers>
    178 void D3D12GroupedSamplerAllocator<NumSamplers>::InvalidateCache()
    179 {
    180   m_groups.clear();
    181 }
    182 
    183 template<u32 NumSamplers>
    184 bool D3D12GroupedSamplerAllocator<NumSamplers>::LookupSingle(ID3D12Device* device, D3D12DescriptorHandle* gpu_handle,
    185                                                              const D3D12DescriptorHandle& cpu_handle)
    186 {
    187   Key key;
    188   key.idx[0] = cpu_handle.index;
    189   for (u32 i = 1; i < NumSamplers; i++)
    190     key.idx[i] = 0;
    191 
    192   auto it = m_groups.find(key);
    193   if (it != m_groups.end())
    194   {
    195     *gpu_handle = it->second;
    196     return true;
    197   }
    198 
    199   if (!Allocate(1, gpu_handle))
    200     return false;
    201 
    202   device->CopyDescriptorsSimple(1, *gpu_handle, cpu_handle, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
    203   m_groups.emplace(key, *gpu_handle);
    204   return true;
    205 }
    206 
    207 template<u32 NumSamplers>
    208 bool D3D12GroupedSamplerAllocator<NumSamplers>::LookupGroup(ID3D12Device* device, D3D12DescriptorHandle* gpu_handle,
    209                                                             const D3D12DescriptorHandle* cpu_handles)
    210 {
    211   Key key;
    212   for (u32 i = 0; i < NumSamplers; i++)
    213     key.idx[i] = cpu_handles[i].index;
    214 
    215   auto it = m_groups.find(key);
    216   if (it != m_groups.end())
    217   {
    218     *gpu_handle = it->second;
    219     return true;
    220   }
    221 
    222   if (!Allocate(NumSamplers, gpu_handle))
    223     return false;
    224 
    225   D3D12_CPU_DESCRIPTOR_HANDLE dst_handle = *gpu_handle;
    226   UINT dst_size = NumSamplers;
    227   D3D12_CPU_DESCRIPTOR_HANDLE src_handles[NumSamplers];
    228   UINT src_sizes[NumSamplers];
    229   for (u32 i = 0; i < NumSamplers; i++)
    230   {
    231     src_handles[i] = cpu_handles[i];
    232     src_sizes[i] = 1;
    233   }
    234   device->CopyDescriptors(1, &dst_handle, &dst_size, NumSamplers, src_handles, src_sizes,
    235                           D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
    236 
    237   m_groups.emplace(key, *gpu_handle);
    238   return true;
    239 }
    240 
    241 template<u32 NumSamplers>
    242 bool D3D12GroupedSamplerAllocator<NumSamplers>::ShouldReset() const
    243 {
    244   // We only reset the sampler heap if more than half of the descriptors are used.
    245   // This saves descriptor copying when there isn't a large number of sampler configs per frame.
    246   return m_groups.size() >= (D3D12_MAX_SHADER_VISIBLE_SAMPLER_HEAP_SIZE / 2);
    247 }