學習RootSignature類

  • 前言
  • 1.RootParameter
  • 1.1 源碼展示
  • 1.2 源碼分析
  • 2.RootSignature
  • 2.1 源碼展示
  • 2.2 源碼分析
  • 3.總結


前言

  • 書接上回DescriptorHeap,本篇文章分析RootSignature.h文件,包含RootParameter和RootSignature類。

1.RootParameter

1.1 源碼展示

  • 類頭文件如下:
#pragma once

#include "pch.h"

class DescriptorCache;

class RootParameter
{
    friend class RootSignature;
public:

    RootParameter() 
    {
        m_RootParam.ParameterType = (D3D12_ROOT_PARAMETER_TYPE)0xFFFFFFFF;
    }

    ~RootParameter()
    {
        Clear();
    }

    void Clear()
    {
        if (m_RootParam.ParameterType == D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE)
            delete [] m_RootParam.DescriptorTable.pDescriptorRanges;

        m_RootParam.ParameterType = (D3D12_ROOT_PARAMETER_TYPE)0xFFFFFFFF;
    }

    void InitAsConstants( UINT Register, UINT NumDwords, D3D12_SHADER_VISIBILITY Visibility = D3D12_SHADER_VISIBILITY_ALL, UINT Space = 0 )
    {
        m_RootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
        m_RootParam.ShaderVisibility = Visibility;
        m_RootParam.Constants.Num32BitValues = NumDwords;
        m_RootParam.Constants.ShaderRegister = Register;
        m_RootParam.Constants.RegisterSpace = Space;
    }

    void InitAsConstantBuffer( UINT Register, D3D12_SHADER_VISIBILITY Visibility = D3D12_SHADER_VISIBILITY_ALL, UINT Space = 0 )
    {
        m_RootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
        m_RootParam.ShaderVisibility = Visibility;
        m_RootParam.Descriptor.ShaderRegister = Register;
        m_RootParam.Descriptor.RegisterSpace = Space;
    }

    void InitAsBufferSRV( UINT Register, D3D12_SHADER_VISIBILITY Visibility = D3D12_SHADER_VISIBILITY_ALL, UINT Space = 0 )
    {
        m_RootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_SRV;
        m_RootParam.ShaderVisibility = Visibility;
        m_RootParam.Descriptor.ShaderRegister = Register;
        m_RootParam.Descriptor.RegisterSpace = Space;
    }

    void InitAsBufferUAV( UINT Register, D3D12_SHADER_VISIBILITY Visibility = D3D12_SHADER_VISIBILITY_ALL, UINT Space = 0 )
    {
        m_RootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV;
        m_RootParam.ShaderVisibility = Visibility;
        m_RootParam.Descriptor.ShaderRegister = Register;
        m_RootParam.Descriptor.RegisterSpace = Space;
    }

    void InitAsDescriptorRange( D3D12_DESCRIPTOR_RANGE_TYPE Type, UINT Register, UINT Count, D3D12_SHADER_VISIBILITY Visibility = D3D12_SHADER_VISIBILITY_ALL, UINT Space = 0 )
    {
        InitAsDescriptorTable(1, Visibility);
        SetTableRange(0, Type, Register, Count, Space);
    }

    void InitAsDescriptorTable( UINT RangeCount, D3D12_SHADER_VISIBILITY Visibility = D3D12_SHADER_VISIBILITY_ALL )
    {
        m_RootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
        m_RootParam.ShaderVisibility = Visibility;
        m_RootParam.DescriptorTable.NumDescriptorRanges = RangeCount;
        m_RootParam.DescriptorTable.pDescriptorRanges = new D3D12_DESCRIPTOR_RANGE[RangeCount];
    }

    void SetTableRange( UINT RangeIndex, D3D12_DESCRIPTOR_RANGE_TYPE Type, UINT Register, UINT Count, UINT Space = 0 )
    {
        D3D12_DESCRIPTOR_RANGE* range = const_cast<D3D12_DESCRIPTOR_RANGE*>(m_RootParam.DescriptorTable.pDescriptorRanges + RangeIndex);
        range->RangeType = Type;
        range->NumDescriptors = Count;
        range->BaseShaderRegister = Register;
        range->RegisterSpace = Space;
        range->OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
    }

    const D3D12_ROOT_PARAMETER& operator() ( void ) const { return m_RootParam; }
        
protected:

    D3D12_ROOT_PARAMETER m_RootParam;
};

1.2 源碼分析

類成員變量如下:

// 僅包含D3D12根簽名對象,在其上封裝一系列管理方法
D3D12_ROOT_PARAMETER m_RootParam;

類方法如下:

class RootParameter
{
    friend class RootSignature;
public:

    // 構造函數,初始化根參數類型為無效值
    RootParameter() 
    {
        m_RootParam.ParameterType = (D3D12_ROOT_PARAMETER_TYPE)0xFFFFFFFF;
    }

    // 析構時Clear
    ~RootParameter()
    {
        Clear();
    }

    // 清理根簽名
    void Clear()
    {
        // 若是描述符表,則釋放動態分配的描述符範圍數組
        if (m_RootParam.ParameterType == D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE)
            delete [] m_RootParam.DescriptorTable.pDescriptorRanges;

        // 重置類型為無效值
        m_RootParam.ParameterType = (D3D12_ROOT_PARAMETER_TYPE)0xFFFFFFFF;
    }

    // 初始化根參數,為根常量
    void InitAsConstants( UINT Register, UINT NumDwords, D3D12_SHADER_VISIBILITY Visibility = D3D12_SHADER_VISIBILITY_ALL, UINT Space = 0 )
    {
        // 類型為根常量
        m_RootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;

        // 哪個着色器可見
        m_RootParam.ShaderVisibility = Visibility;

        // 可以包含多少個float變量
        m_RootParam.Constants.Num32BitValues = NumDwords;

        // 綁定到Shader哪個寄存器
        m_RootParam.Constants.ShaderRegister = Register;
        
        // 寄存器空間
        m_RootParam.Constants.RegisterSpace = Space;
    }

    // 初始化根參數,為一個常量緩衝區描述符
    void InitAsConstantBuffer( UINT Register, D3D12_SHADER_VISIBILITY Visibility = D3D12_SHADER_VISIBILITY_ALL, UINT Space = 0 )
    {
        m_RootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
        m_RootParam.ShaderVisibility = Visibility;
        m_RootParam.Descriptor.ShaderRegister = Register;
        m_RootParam.Descriptor.RegisterSpace = Space;
    }

    // 初始化根參數,為一個着色器資源視圖
    void InitAsBufferSRV( UINT Register, D3D12_SHADER_VISIBILITY Visibility = D3D12_SHADER_VISIBILITY_ALL, UINT Space = 0 )
    {
        m_RootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_SRV;
        m_RootParam.ShaderVisibility = Visibility;
        m_RootParam.Descriptor.ShaderRegister = Register;
        m_RootParam.Descriptor.RegisterSpace = Space;
    }

    // 初始化根參數,為一個無序訪問視圖
    void InitAsBufferUAV( UINT Register, D3D12_SHADER_VISIBILITY Visibility = D3D12_SHADER_VISIBILITY_ALL, UINT Space = 0 )
    {
        m_RootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV;
        m_RootParam.ShaderVisibility = Visibility;
        m_RootParam.Descriptor.ShaderRegister = Register;
        m_RootParam.Descriptor.RegisterSpace = Space;
    }

    // 初始化根參數,為一個根描述符範圍
    void InitAsDescriptorRange( D3D12_DESCRIPTOR_RANGE_TYPE Type, UINT Register, UINT Count, D3D12_SHADER_VISIBILITY Visibility = D3D12_SHADER_VISIBILITY_ALL, UINT Space = 0 )
    {
        InitAsDescriptorTable(1, Visibility);
        SetTableRange(0, Type, Register, Count, Space);
    }

    // 初始為描述符表
    void InitAsDescriptorTable( UINT RangeCount, D3D12_SHADER_VISIBILITY Visibility = D3D12_SHADER_VISIBILITY_ALL )
    {
        // 描述符表類型和着色器可見性
        m_RootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
        m_RootParam.ShaderVisibility = Visibility;

        // 描述符範圍數量和內存分配
        m_RootParam.DescriptorTable.NumDescriptorRanges = RangeCount;
        m_RootParam.DescriptorTable.pDescriptorRanges = new D3D12_DESCRIPTOR_RANGE[RangeCount];
    }

    // 設置描述符範圍
    void SetTableRange( UINT RangeIndex, D3D12_DESCRIPTOR_RANGE_TYPE Type, UINT Register, UINT Count, UINT Space = 0 )
    {
        // 獲取描述符範圍
        D3D12_DESCRIPTOR_RANGE* range = const_cast<D3D12_DESCRIPTOR_RANGE*>(m_RootParam.DescriptorTable.pDescriptorRanges + RangeIndex);
        
        // 設置描述符範圍
        range->RangeType = Type;
        range->NumDescriptors = Count;
        range->BaseShaderRegister = Register;
        range->RegisterSpace = Space;

        // 一個描述符表只能綁定到一個描述符堆,D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND
        // 指定該描述符範圍在描述符表中的起始位置相對於描述符表起始位置的偏移量。
        range->OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
    }

    // 在需要時返回D3D12_ROOT_PARAMETER
    const D3D12_ROOT_PARAMETER& operator() ( void ) const { return m_RootParam; }
        
protected:

    // 僅包含D3D12根簽名對象,在其上封裝一系列管理方法
    D3D12_ROOT_PARAMETER m_RootParam;
};

RootSignature封裝了D3D12_ROOT_PARAMETER並提供了一系列操作函數。

2.RootSignature

2.1 源碼展示

RootSignature頭文件:

// Maximum 64 DWORDS divied up amongst all root parameters.
// Root constants = 1 DWORD * NumConstants
// Root descriptor (CBV, SRV, or UAV) = 2 DWORDs each
// Descriptor table pointer = 1 DWORD
// Static samplers = 0 DWORDS (compiled into shader)
class RootSignature
{
    friend class DynamicDescriptorHeap;

public:

    RootSignature( UINT NumRootParams = 0, UINT NumStaticSamplers = 0 ) : m_Finalized(FALSE), m_NumParameters(NumRootParams)
    {
        Reset(NumRootParams, NumStaticSamplers);
    }

    ~RootSignature()
    {
    }

    static void DestroyAll(void);

    void Reset( UINT NumRootParams, UINT NumStaticSamplers = 0 )
    {
        if (NumRootParams > 0)
            m_ParamArray.reset(new RootParameter[NumRootParams]);
        else
            m_ParamArray = nullptr;
        m_NumParameters = NumRootParams;

        if (NumStaticSamplers > 0)
            m_SamplerArray.reset(new D3D12_STATIC_SAMPLER_DESC[NumStaticSamplers]);
        else
            m_SamplerArray = nullptr;
        m_NumSamplers = NumStaticSamplers;
        m_NumInitializedStaticSamplers = 0;
    }

    RootParameter& operator[] ( size_t EntryIndex )
    {
        ASSERT(EntryIndex < m_NumParameters);
        return m_ParamArray.get()[EntryIndex];
    }

    const RootParameter& operator[] ( size_t EntryIndex ) const
    {
        ASSERT(EntryIndex < m_NumParameters);
        return m_ParamArray.get()[EntryIndex];
    }

    void InitStaticSampler( UINT Register, const D3D12_SAMPLER_DESC& NonStaticSamplerDesc,
        D3D12_SHADER_VISIBILITY Visibility = D3D12_SHADER_VISIBILITY_ALL );

    void Finalize(const std::wstring& name, D3D12_ROOT_SIGNATURE_FLAGS Flags = D3D12_ROOT_SIGNATURE_FLAG_NONE);

    ID3D12RootSignature* GetSignature() const { return m_Signature; }

protected:

    BOOL m_Finalized;
    UINT m_NumParameters;
    UINT m_NumSamplers;
    UINT m_NumInitializedStaticSamplers;
    uint32_t m_DescriptorTableBitMap;		// One bit is set for root parameters that are non-sampler descriptor tables
    uint32_t m_SamplerTableBitMap;			// One bit is set for root parameters that are sampler descriptor tables
    uint32_t m_DescriptorTableSize[16];		// Non-sampler descriptor tables need to know their descriptor count
    std::unique_ptr<RootParameter[]> m_ParamArray;
    std::unique_ptr<D3D12_STATIC_SAMPLER_DESC[]> m_SamplerArray;
    ID3D12RootSignature* m_Signature;
};

RootSignature源文件:

static std::map< size_t, ComPtr<ID3D12RootSignature> > s_RootSignatureHashMap;

void RootSignature::DestroyAll(void)
{
    s_RootSignatureHashMap.clear();
}

void RootSignature::InitStaticSampler(
    UINT Register,
    const D3D12_SAMPLER_DESC& NonStaticSamplerDesc,
    D3D12_SHADER_VISIBILITY Visibility )
{
    ASSERT(m_NumInitializedStaticSamplers < m_NumSamplers);
    D3D12_STATIC_SAMPLER_DESC& StaticSamplerDesc = m_SamplerArray[m_NumInitializedStaticSamplers++];

    StaticSamplerDesc.Filter = NonStaticSamplerDesc.Filter;
    StaticSamplerDesc.AddressU = NonStaticSamplerDesc.AddressU;
    StaticSamplerDesc.AddressV = NonStaticSamplerDesc.AddressV;
    StaticSamplerDesc.AddressW = NonStaticSamplerDesc.AddressW;
    StaticSamplerDesc.MipLODBias = NonStaticSamplerDesc.MipLODBias;
    StaticSamplerDesc.MaxAnisotropy = NonStaticSamplerDesc.MaxAnisotropy;
    StaticSamplerDesc.ComparisonFunc = NonStaticSamplerDesc.ComparisonFunc;
    StaticSamplerDesc.BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE;
    StaticSamplerDesc.MinLOD = NonStaticSamplerDesc.MinLOD;
    StaticSamplerDesc.MaxLOD = NonStaticSamplerDesc.MaxLOD;
    StaticSamplerDesc.ShaderRegister = Register;
    StaticSamplerDesc.RegisterSpace = 0;
    StaticSamplerDesc.ShaderVisibility = Visibility;

    if (StaticSamplerDesc.AddressU == D3D12_TEXTURE_ADDRESS_MODE_BORDER ||
        StaticSamplerDesc.AddressV == D3D12_TEXTURE_ADDRESS_MODE_BORDER ||
        StaticSamplerDesc.AddressW == D3D12_TEXTURE_ADDRESS_MODE_BORDER)
    {
        WARN_ONCE_IF_NOT(
            // Transparent Black
            NonStaticSamplerDesc.BorderColor[0] == 0.0f &&
            NonStaticSamplerDesc.BorderColor[1] == 0.0f &&
            NonStaticSamplerDesc.BorderColor[2] == 0.0f &&
            NonStaticSamplerDesc.BorderColor[3] == 0.0f ||
            // Opaque Black
            NonStaticSamplerDesc.BorderColor[0] == 0.0f &&
            NonStaticSamplerDesc.BorderColor[1] == 0.0f &&
            NonStaticSamplerDesc.BorderColor[2] == 0.0f &&
            NonStaticSamplerDesc.BorderColor[3] == 1.0f ||
            // Opaque White
            NonStaticSamplerDesc.BorderColor[0] == 1.0f &&
            NonStaticSamplerDesc.BorderColor[1] == 1.0f &&
            NonStaticSamplerDesc.BorderColor[2] == 1.0f &&
            NonStaticSamplerDesc.BorderColor[3] == 1.0f,
            "Sampler border color does not match static sampler limitations");

        if (NonStaticSamplerDesc.BorderColor[3] == 1.0f)
        {
            if (NonStaticSamplerDesc.BorderColor[0] == 1.0f)
                StaticSamplerDesc.BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE;
            else
                StaticSamplerDesc.BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK;
        }
        else
            StaticSamplerDesc.BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK;
    }
}

void RootSignature::Finalize(const std::wstring& name, D3D12_ROOT_SIGNATURE_FLAGS Flags)
{
    if (m_Finalized)
        return;

    ASSERT(m_NumInitializedStaticSamplers == m_NumSamplers);

    D3D12_ROOT_SIGNATURE_DESC RootDesc;
    RootDesc.NumParameters = m_NumParameters;
    RootDesc.pParameters = (const D3D12_ROOT_PARAMETER*)m_ParamArray.get();
    RootDesc.NumStaticSamplers = m_NumSamplers;
    RootDesc.pStaticSamplers = (const D3D12_STATIC_SAMPLER_DESC*)m_SamplerArray.get();
    RootDesc.Flags = Flags;

    m_DescriptorTableBitMap = 0;
    m_SamplerTableBitMap = 0;

    size_t HashCode = Utility::HashState(&RootDesc.Flags);
    HashCode = Utility::HashState( RootDesc.pStaticSamplers, m_NumSamplers, HashCode );

    for (UINT Param = 0; Param < m_NumParameters; ++Param)
    {
        const D3D12_ROOT_PARAMETER& RootParam = RootDesc.pParameters[Param];
        m_DescriptorTableSize[Param] = 0;

        if (RootParam.ParameterType == D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE)
        {
            ASSERT(RootParam.DescriptorTable.pDescriptorRanges != nullptr);

            HashCode = Utility::HashState( RootParam.DescriptorTable.pDescriptorRanges,
                RootParam.DescriptorTable.NumDescriptorRanges, HashCode );

            // We keep track of sampler descriptor tables separately from CBV_SRV_UAV descriptor tables
            if (RootParam.DescriptorTable.pDescriptorRanges->RangeType == D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER)
                m_SamplerTableBitMap |= (1 << Param);
            else
                m_DescriptorTableBitMap |= (1 << Param);

            for (UINT TableRange = 0; TableRange < RootParam.DescriptorTable.NumDescriptorRanges; ++TableRange)
                m_DescriptorTableSize[Param] += RootParam.DescriptorTable.pDescriptorRanges[TableRange].NumDescriptors;
        }
        else
            HashCode = Utility::HashState( &RootParam, 1, HashCode );
    }

    ID3D12RootSignature** RSRef = nullptr;
    bool firstCompile = false;
    {
        static mutex s_HashMapMutex;
        lock_guard<mutex> CS(s_HashMapMutex);
        auto iter = s_RootSignatureHashMap.find(HashCode);

        // Reserve space so the next inquiry will find that someone got here first.
        if (iter == s_RootSignatureHashMap.end())
        {
            RSRef = s_RootSignatureHashMap[HashCode].GetAddressOf();
            firstCompile = true;
        }
        else
            RSRef = iter->second.GetAddressOf();
    }

    if (firstCompile)
    {
        ComPtr<ID3DBlob> pOutBlob, pErrorBlob;

        ASSERT_SUCCEEDED( D3D12SerializeRootSignature(&RootDesc, D3D_ROOT_SIGNATURE_VERSION_1,
            pOutBlob.GetAddressOf(), pErrorBlob.GetAddressOf()));

        ASSERT_SUCCEEDED( g_Device->CreateRootSignature(1, pOutBlob->GetBufferPointer(), pOutBlob->GetBufferSize(),
            MY_IID_PPV_ARGS(&m_Signature)) );

        m_Signature->SetName(name.c_str());

        s_RootSignatureHashMap[HashCode].Attach(m_Signature);
        ASSERT(*RSRef == m_Signature);
    }
    else
    {
        while (*RSRef == nullptr)
            this_thread::yield();
        m_Signature = *RSRef;
    }

    m_Finalized = TRUE;
}

2.2 源碼分析

類成員變量如下:

// 此對象是否完成構建
BOOL m_Finalized;

// 包含的根參數個數
UINT m_NumParameters;

// 包含的採樣數個數
UINT m_NumSamplers;

// 已經初始化的採樣器個數
UINT m_NumInitializedStaticSamplers;

// 每個位表示對應索引的根參數 是否為非採樣器描述符表
uint32_t m_DescriptorTableBitMap;		

// 每個位表示對應索引的根參數 是否為採樣器描述符表
uint32_t m_SamplerTableBitMap;		

// 根簽名最多包含16個根參數,此數組表示每個根參數包含的描述符數量
uint32_t m_DescriptorTableSize[16];		

// 根參數數組
std::unique_ptr<RootParameter[]> m_ParamArray;

// 採樣器數組
std::unique_ptr<D3D12_STATIC_SAMPLER_DESC[]> m_SamplerArray;

// D3D12根簽名對象
ID3D12RootSignature* m_Signature;

類方法如下:

// 定義在.cpp裏的全局靜態根簽名表,鍵為根簽名的hash值
static std::map<size_t, ComPtr<ID3D12RootSignature>> s_RootSignatureHashMap;

// 構造函數,初始化m_Finalized=false,m_NumParamete=NumRootParams
RootSignature::RootSignature(UINT NumRootParams = 0, UINT NumStaticSamplers = 0) : m_Finalized(FALSE), m_NumParameters(NumRootParams)
{
    // 調用Reset
    Reset(NumRootParams, NumStaticSamplers);
}

RootSignature::~RootSignature()
{
}

// 重置根簽名
void RootSignature::Reset(UINT NumRootParams, UINT NumStaticSamplers = 0)
{
    // 根據根參數數量,初始化m_ParamArray
    if (NumRootParams > 0)
        m_ParamArray.reset(new RootParameter[NumRootParams]);
    else
        m_ParamArray = nullptr;
    m_NumParameters = NumRootParams;

    // 根據靜態採樣器數量,初始化m_SamplerArray
    if (NumStaticSamplers > 0)
        m_SamplerArray.reset(new D3D12_STATIC_SAMPLER_DESC[NumStaticSamplers]);
    else
        m_SamplerArray = nullptr;

    // 記錄採樣器數量,重置已初始化採樣器數為0
    m_NumSamplers = NumStaticSamplers;
    m_NumInitializedStaticSamplers = 0;
}

// 獲取根參數
RootParameter& RootSignature::operator[] (size_t EntryIndex)
{
    ASSERT(EntryIndex < m_NumParameters);
    return m_ParamArray.get()[EntryIndex];
}

const RootParameter& RootSignature::operator[] (size_t EntryIndex) const
{
    ASSERT(EntryIndex < m_NumParameters);
    return m_ParamArray.get()[EntryIndex];
}

// 獲取D3D12根簽名對象
ID3D12RootSignature* RootSignature::GetSignature() const { return m_Signature; }

// 靜態方法-銷燬所有根簽名
void RootSignature::DestroyAll(void)
{
    // 清空全局根簽名表,COM指針保證調用ID3D12RootSignature的析構函數
    s_RootSignatureHashMap.clear();
}

// 初始化一個靜態採樣器
void RootSignature::InitStaticSampler(
    UINT Register,
    const D3D12_SAMPLER_DESC& NonStaticSamplerDesc,
    D3D12_SHADER_VISIBILITY Visibility )
{
    // 檢查沒有超過預計採樣器數
    ASSERT(m_NumInitializedStaticSamplers < m_NumSamplers);

    // 獲取要修改的採樣器
    D3D12_STATIC_SAMPLER_DESC& StaticSamplerDesc = m_SamplerArray[m_NumInitializedStaticSamplers++];

    // 賦值
    StaticSamplerDesc.Filter = NonStaticSamplerDesc.Filter;
    StaticSamplerDesc.AddressU = NonStaticSamplerDesc.AddressU;
    StaticSamplerDesc.AddressV = NonStaticSamplerDesc.AddressV;
    StaticSamplerDesc.AddressW = NonStaticSamplerDesc.AddressW;
    StaticSamplerDesc.MipLODBias = NonStaticSamplerDesc.MipLODBias;
    StaticSamplerDesc.MaxAnisotropy = NonStaticSamplerDesc.MaxAnisotropy;
    StaticSamplerDesc.ComparisonFunc = NonStaticSamplerDesc.ComparisonFunc;
    StaticSamplerDesc.BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE;
    StaticSamplerDesc.MinLOD = NonStaticSamplerDesc.MinLOD;
    StaticSamplerDesc.MaxLOD = NonStaticSamplerDesc.MaxLOD;
    StaticSamplerDesc.ShaderRegister = Register;     // 使用Register覆蓋
    StaticSamplerDesc.RegisterSpace = 0;             // 使用Space-0覆蓋
    StaticSamplerDesc.ShaderVisibility = Visibility; // 使用Visibility覆蓋

    // 如果尋址模式為D3D12_TEXTURE_ADDRESS_MODE_BORDER
    if (StaticSamplerDesc.AddressU == D3D12_TEXTURE_ADDRESS_MODE_BORDER ||
        StaticSamplerDesc.AddressV == D3D12_TEXTURE_ADDRESS_MODE_BORDER ||
        StaticSamplerDesc.AddressW == D3D12_TEXTURE_ADDRESS_MODE_BORDER)
    {
        // 檢查採樣器邊框顏色必須靜態採樣器限制匹配 
        // 靜態採樣器在編譯時確定,性能更好,但只能使用有限的預定義邊框顏色
        WARN_ONCE_IF_NOT(
            // Transparent Black
            NonStaticSamplerDesc.BorderColor[0] == 0.0f &&
            NonStaticSamplerDesc.BorderColor[1] == 0.0f &&
            NonStaticSamplerDesc.BorderColor[2] == 0.0f &&
            NonStaticSamplerDesc.BorderColor[3] == 0.0f ||
            // Opaque Black
            NonStaticSamplerDesc.BorderColor[0] == 0.0f &&
            NonStaticSamplerDesc.BorderColor[1] == 0.0f &&
            NonStaticSamplerDesc.BorderColor[2] == 0.0f &&
            NonStaticSamplerDesc.BorderColor[3] == 1.0f ||
            // Opaque White
            NonStaticSamplerDesc.BorderColor[0] == 1.0f &&
            NonStaticSamplerDesc.BorderColor[1] == 1.0f &&
            NonStaticSamplerDesc.BorderColor[2] == 1.0f &&
            NonStaticSamplerDesc.BorderColor[3] == 1.0f,
            "Sampler border color does not match static sampler limitations");

        /*
            根據輸入的邊框顏色值,轉換為對應的DX12靜態邊框顏色枚舉值: 
                如果Alpha=1且RGB全為1 → 不透明白
                如果Alpha=1但RGB不全為1 → 不透明黑
                如果Alpha≠1 → 透明黑
        */
        if (NonStaticSamplerDesc.BorderColor[3] == 1.0f)
        {
            if (NonStaticSamplerDesc.BorderColor[0] == 1.0f)
                StaticSamplerDesc.BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE;
            else
                StaticSamplerDesc.BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK;
        }
        else
            StaticSamplerDesc.BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK;
    }
}

// 完成根簽名創建
void RootSignature::Finalize(const std::wstring& name, D3D12_ROOT_SIGNATURE_FLAGS Flags)
{
    // 若已經創建則退出
    if (m_Finalized)
        return;

    // 檢測已初始化靜態採樣器數目等於設置的採樣器數
    ASSERT(m_NumInitializedStaticSamplers == m_NumSamplers);

    // 根簽名描述
    D3D12_ROOT_SIGNATURE_DESC RootDesc;

    // 設置根參數
    RootDesc.NumParameters = m_NumParameters;
    RootDesc.pParameters = (const D3D12_ROOT_PARAMETER*)m_ParamArray.get();
    
    // 設置靜態採樣器
    RootDesc.NumStaticSamplers = m_NumSamplers;
    RootDesc.pStaticSamplers = (const D3D12_STATIC_SAMPLER_DESC*)m_SamplerArray.get();
    
    // 設置根簽名標誌
    RootDesc.Flags = Flags;

    // 重置位掩碼都為0
    m_DescriptorTableBitMap = 0;
    m_SamplerTableBitMap = 0;

    // 對根簽名標誌計算Hash
    size_t HashCode = Utility::HashState(&RootDesc.Flags);

    // 在HashCode基礎上,再對靜態採樣器指向的數據計算新的Hash
    HashCode = Utility::HashState( RootDesc.pStaticSamplers, m_NumSamplers, HashCode );

    // 遍歷每個根參數
    for (UINT Param = 0; Param < m_NumParameters; ++Param)
    {
        // 獲取根參數
        const D3D12_ROOT_PARAMETER& RootParam = RootDesc.pParameters[Param];
        
        // 重置根參數包含的描述符數目為0
        m_DescriptorTableSize[Param] = 0;

        // 如果根參數是描述符表
        if (RootParam.ParameterType == D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE)
        {
            // 描述符範圍必須有數據
            ASSERT(RootParam.DescriptorTable.pDescriptorRanges != nullptr);

            // 對描述符表中的所有描述符範圍數據計算Hash
            HashCode = Utility::HashState( RootParam.DescriptorTable.pDescriptorRanges,
                RootParam.DescriptorTable.NumDescriptorRanges, HashCode );

            // 我們將採樣器描述符表與 CBV_SRV_UAV 描述符表分開跟蹤。
            // 如果此描述符表是D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER,則將m_SamplerTableBitMap對應位置為1
            if (RootParam.DescriptorTable.pDescriptorRanges->RangeType == D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER)
                m_SamplerTableBitMap |= (1 << Param);
            else
                m_DescriptorTableBitMap |= (1 << Param);

            // 遍歷描述符中每個描述符範圍,累計該描述符表包含的描述符總數
            for (UINT TableRange = 0; TableRange < RootParam.DescriptorTable.NumDescriptorRanges; ++TableRange)
                m_DescriptorTableSize[Param] += RootParam.DescriptorTable.pDescriptorRanges[TableRange].NumDescriptors;
        }
        // 如果不是描述符表,則直接計算根參數的Hash
        else
            HashCode = Utility::HashState( &RootParam, 1, HashCode );
    }

    ID3D12RootSignature** RSRef = nullptr;
    bool firstCompile = false;
    {
        // 加鎖,保護全局根簽名表s_RootSignatureHashMap
        static mutex s_HashMapMutex;
        lock_guard<mutex> CS(s_HashMapMutex);

        // 根據根參數和靜態採樣器配置信息,得到最終的HashCode
        // 使用最終HashCode查詢全局根簽名表
        auto iter = s_RootSignatureHashMap.find(HashCode);

        // 如果此中根簽名沒有登記在表裏
        if (iter == s_RootSignatureHashMap.end())
        {
            // 則在表裏創建此Hash(先佔位鍵,值還未初始化)
            RSRef = s_RootSignatureHashMap[HashCode].GetAddressOf();

            // 標記需要創建
            firstCompile = true;
        }
        else
            // 如果表內存在,則直接使用表中RootSignature
            RSRef = iter->second.GetAddressOf();
    }

    // 構建
    if (firstCompile)
    {
        ComPtr<ID3DBlob> pOutBlob, pErrorBlob;

        ASSERT_SUCCEEDED( D3D12SerializeRootSignature(&RootDesc, D3D_ROOT_SIGNATURE_VERSION_1,
            pOutBlob.GetAddressOf(), pErrorBlob.GetAddressOf()));

        ASSERT_SUCCEEDED( g_Device->CreateRootSignature(1, pOutBlob->GetBufferPointer(), pOutBlob->GetBufferSize(),
            MY_IID_PPV_ARGS(&m_Signature)) );

        m_Signature->SetName(name.c_str());

        // 最終構建完畢再替換到表中
        s_RootSignatureHashMap[HashCode].Attach(m_Signature);
        ASSERT(*RSRef == m_Signature);
    }
    else
    {
        // 如果在表中找到,可能另一個線程還在創建
        // 檢查表內對應值,若為空則循環等待
        while (*RSRef == nullptr)
            // 讓出CPU,減少資源消耗
            this_thread::yield();

        // 獲取對應值
        m_Signature = *RSRef;
    }

    // 標記已經初始化
    m_Finalized = TRUE;
}

3.總結

  • RootParameter封裝了D3D12_ROOT_PARAMETER,提供了一系列輔助方法。
  • RootSignature封裝了ID3D12RootSignature,並使用全局根簽名表,以複用同樣的根簽名。
  • 要將二者作為獨立組件,只需為RootSignature::Finalize添加ID3D12Device參數,替代實現中的g_Device。
  • 額外需要添加Common.h和Hash.h,支持Hash計算功能。還在pch.h最後添加了如下代碼,鏈接d3d12庫,避免鏈接錯誤。
#pragma comment(lib, "d3d12.lib")
#pragma comment(lib, "d3dcompiler.lib")