MiniEngine学习笔记 : RootSignature

学习RootSignature类

前言

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

1.RootParameter

1.1 源码展示

  • 类头文件如下:
cpp 复制代码
#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 源码分析

类成员变量如下:

cpp 复制代码
// 仅包含D3D12根签名对象,在其上封装一系列管理方法
D3D12_ROOT_PARAMETER m_RootParam;

类方法如下:

cpp 复制代码
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头文件:

cpp 复制代码
// 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源文件:

cpp 复制代码
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 源码分析

类成员变量如下:

cpp 复制代码
// 此对象是否完成构建
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 复制代码
// 定义在.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库,避免链接错误。
cpp 复制代码
#pragma comment(lib, "d3d12.lib")
#pragma comment(lib, "d3dcompiler.lib")
相关推荐
自在极意功。3 小时前
Java List全面解析:从入门到精通
java·windows·list接口·list的实现类
Sunny_yiyi4 小时前
Java接入飞书发送通知消息
java·windows·飞书
MediaTea4 小时前
Python 文件操作:JSON 格式
开发语言·windows·python·json
Mark_Hide4 小时前
学习笔记5
笔记·学习
Larry_Yanan5 小时前
QML学习笔记(五十一)QML与C++交互:数据转换——基本数据类型
c++·笔记·学习
化作星辰5 小时前
深度学习_原理和进阶_PyTorch入门(2)后续语法2
pytorch·深度学习·学习
一念杂记6 小时前
没网太崩溃!手机电脑网络共享,简单几步搞定网络共享,再也不用为没网担忧~
android·windows
小年糕是糕手6 小时前
【数据结构】常见的排序算法 -- 插入排序
c语言·开发语言·数据结构·学习·算法·leetcode·排序算法
点心快奔跑6 小时前
超详细Windows系统MySQL 安装教程
数据库·windows·mysql