加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

Direct3D 12 尝鲜(三): Pipeline State Object

发布时间:2020-12-13 22:29:41 所属栏目:百科 来源:网络整理
导读:(转载请注明出处) 首先得修正一个错误,前一节说到了Fence中用到帧编号,这个帧编号应该(?)要大于0,所以初始化为1就好了,否则(这一节中)第二次渲染会出一点小错误.(╯‵□′)╯︵┴─┴ 在第一节有提到资源绑定,一般的descriptor,还有就是一个特殊的Root Sig
(转载请注明出处)

首先得修正一个错误,前一节说到了Fence中用到帧编号,这个帧编号应该(?)要大于0,所以初始化为1就好了,否则(这一节中)第二次渲染会出一点小错误.(╯‵□′)╯︵┴─┴

在第一节有提到资源绑定,一般的descriptor,还有就是一个特殊的Root Signatures,微软提到

The root signature defines what resources are bound to the graphics pipeline. A root signature is configured by the app and links command lists to the resources the shaders require. Currently,there is one graphics and one compute root signature per app.

就是说这个Root Signature是被绑定到图像渲染管线上的资源,我们可以利用D3D12SerializeRootSignature先序列化一个Root Signature,将创建的ID3DBlob(其实就是ID3D10Blob的马甲) 传给ID3D12Device::CreateRootSignature用来创建RootSignature.
不像Descriptor那样可以创建连续的多个,只能单个创建. 说完Root Signature就进入这次的主题,Pipeline State Object(PSO),这也是与D3D11的重大差别之一,在官方的博客中提到,在D3D11中,渲染管线每个阶段都可以独立控制,很方便,但是也造成了性能的浪费,驱动需要提供实时Get/Set:

在D3D12中,通过将这些糅合成一个不可再变的PSO,不过有提到:

Which PSO is in use can still be changed dynamically,but to do so the hardware only needs to copy the minimal amount of pre-computed state directly to the hardware registers,rather than computing the hardware state on the fly.

不过就认为不可变也不为过,有点类似于以前的固定管线,不过这个可以多创建几个PSO,常见的几种组合够用了:

可以使用ID3D12Device::CreateGraphicsPipelineState来创建一个PSO,最后两个参数REFIID,void**什么的就不说了,说一下第一个参数D3D12_GRAPHICS_PIPELINE_STATE_DESC,(╯‵□′)╯︵┴─┴,这个东西好大,在x86下,都有500+字节:

typedef struct D3D12_GRAPHICS_PIPELINE_STATE_DESC {
  ID3D12RootSignature           *pRootSignature;
  D3D12_SHADER_BYTECODE         VS;
  D3D12_SHADER_BYTECODE         PS;
  D3D12_SHADER_BYTECODE         DS;
  D3D12_SHADER_BYTECODE         HS;
  D3D12_SHADER_BYTECODE         GS;
  D3D12_STREAM_OUTPUT_DESC      StreamOutput;
  D3D12_BLEND_DESC              BlendState;
  UINT                          SampleMask;
  D3D12_RASTERIZER_DESC         RasterizerState;
  D3D12_DEPTH_STENCIL_DESC      DepthStencilState;
  D3D12_INPUT_LAYOUT_DESC       InputLayout;
  D3D12_INDEX_BUFFER_PROPERTIES IndexBufferProperties;
  D3D12_PRIMITIVE_TOPOLOGY_TYPE PrimitiveTopologyType;
  UINT                          NumRenderTargets;
  DXGI_FORMAT                   RTVFormats[8];
  DXGI_FORMAT                   DSVFormat;
  DXGI_SAMPLE_DESC              SampleDesc;
  UINT                          NodeMask;
  D3D12_CACHED_PIPELINE_STATE   CachedPSO;
} D3D12_GRAPHICS_PIPELINE_STATE_DESC;

我们这里创建一个简单的PSO即可,就是VS-PS组合,可以说是最简单的了. 在屏幕上画一个渐变的矩形(全窗口)

pRootSignature

  • 一个指向ID3D12RootSignature的指针,就是前面创建的

VS

  • 顶点着色器字节码的地址和长度

PS

  • 像素着色器字节码的地址和长度

DS HS GS

  • 域着色器、壳着色器和几何着色器的字节码,这里不需要,0即可

StreamOutput

  • 流输出,0即可

BlendState

  • 混合状态,可以使用D3D12提供的helper用默认的即可: CD3D12_BLEND_DESC(D3D12_DEFAULT)

SampleMask

  • 采样掩码,对于我们这样的垃圾显卡,1就足够了,不过设为0xFFFFFFFF以适应所有采样,有特殊要求可以部分位设为0

RasterizerState

  • 光栅化状态,使用Helper用默认的即可: CD3D12_RASTERIZER_DESC(D3D12_DEFAULT)

DepthStencilState

  • 深度/模板状态,0即可

InputLayout

  • 输入布局,0即可

IndexBufferProperties

  • 索引缓存属性,D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_DISABLED 即可

PrimitiveTopologyType

  • 原始拓扑类型,这里使用三角形即可

NumRenderTargets

  • 目标呈现器数量,这里就一个

RTVFormats

  • RTV输出像素格式,我们只有一个RTV,第一个设为RGBA,其他设为0

DSVFormat

  • DSV格式,这里不需要

SampleDesc

  • 多重采样质量,我们的软件渲染还是设为(1,0)吧

NodeMask

  • 节点掩码,现在不需要,设为0

CachedPSO

  • 已缓存的PSO,应该就是类似于C++的replacement new,这里是第一次创建,0 即可.

说到着色器,那就要编译了,我在D2D特效中一节说到了编译方式,这个算是”离线编译”了,还有就是利用函数即时编译,这里使用的是D3DCompileFromFile函数,还有为了方便,将PS和VS写在了一起

struct VSOut {
    float4 pos              : SV_POSITION;
    float2 texcoord         : TEXCOORD;
};

VSOut VSMain(uint vertexID : SV_VERTEXID) {
    VSOut output;
    output.texcoord = float2((vertexID << 1) & 2,vertexID & 2);
    output.pos = float4(output.texcoord * float2(2,-2) + float2(-1,1),0,1);
    return output;
}

float4 PSMain(VSOut vsOut) : SV_TARGET {
    return float4(0,vsOut.texcoord.y,vsOut.texcoord.x,vsOut.texcoord.y * 0.5);
}

这就是我的创建代码:

ID3DBlob *sig = nullptr,*info = nullptr;
ID3DBlob *ps = nullptr,*vs = nullptr;
D3D12_ROOT_SIGNATURE rootSigDesc = D3D12_ROOT_SIGNATURE();
// 先序列化RootSignature
if (SUCCEEDED(hr = ::D3D12SerializeRootSignature(&rootSigDesc,D3D_ROOT_SIGNATURE_V1,&sig,&info))) {
    // 再创建RootSignature
    hr = m_pd3dDevice->CreateRootSignature(
        0,sig->GetBufferPointer(),sig->GetBufferSize(),IID_ID3D12RootSignature,reinterpret_cast<void**>(&m_prsPipeline)
        );
}
UINT flag = 0;
#if _DEBUG
flag |= D3DCOMPILE_DEBUG;
#endif
// 编译VS
if (SUCCEEDED(hr)) {
    ::SafeRelease(info);
    hr = ::D3DCompileFromFile(
        L"shader2in1.hlsl",nullptr,"VSMain","vs_5_0",flag,&vs,&info
        );
}
// 编译PS
if (SUCCEEDED(hr)) {
    ::SafeRelease(info);
    hr = ::D3DCompileFromFile(
        L"shader2in1.hlsl","PSMain","ps_5_0",&ps,&info
        );
}
// 创建PSO
if (SUCCEEDED(hr)) {
    // 配置PSO
    D3D12_GRAPHICS_PIPELINE_STATE_DESC desc = {
        // Root Signature
        m_prsPipeline,// VS
        { vs->GetBufferPointer(),vs->GetBufferSize() },// PS
        { ps->GetBufferPointer(),ps->GetBufferSize() },// DS
        { nullptr,0 },// HS
        { nullptr,// GS
        { nullptr,// SO
        { nullptr,// 合成
        CD3D12_BLEND_DESC(D3D12_DEFAULT),// 采样掩码
        UINT32_MAX,// 光栅化
        CD3D12_RASTERIZER_DESC(D3D12_DEFAULT),// 深度/模板
        { 0 },// 输入布局
        { nullptr,// 索引缓存
        D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_DISABLED,// 拓扑类型设置为三角
        D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE,// 1个RTV
        1,// RGBA
        { DXGI_FORMAT_R8G8B8A8_UNORM,DXGI_FORMAT_UNKNOWN },// DSV
        DXGI_FORMAT_UNKNOWN,// 采样
        { 1,// 节点掩码
        0,// 缓存管线
        { nullptr,0 }
    };
    // 创建PSO
    hr = m_pd3dDevice->CreateGraphicsPipelineState(
        &desc,IID_ID3D12PipelineState,reinterpret_cast<void**>(&m_pPipelineState)
        );

}
::SafeRelease(sig);
::SafeRelease(info);
::SafeRelease(ps);
::SafeRelease(vs);

好了,为了演示命令列表,这次特地创建了2个,一个负责清除,一个负责刻画,前面的命令就不说了,说一下后面的:

因为我们在输入布局(inputlayout)中没有设置,所以直接使用ID3D12GraphicsCommandList::DrawInstanced渲染,不过还得绑定裁剪矩形,还得设定RTV,现在又能最多输出到8个RT,设置PSO,所以代码长这个样子:

// 创建刻画命令
if(SUCCEEDED(hr)) {
    this->SetResourceBarrier(m_pCmdDraw,m_pTargetBuffer,D3D12_RESOURCE_USAGE_PRESENT,D3D12_RESOURCE_USAGE_RENDER_TARGET);
    auto rtv = m_pRTVDescriptor->GetCPUDescriptorHandleForHeapStart();
    m_pCmdDraw->RSSetViewports(1,&view);
    m_pCmdDraw->SetRenderTargets(&rtv,true,1,nullptr);
    m_pCmdDraw->SetGraphicsRootSignature(m_prsPipeline);
    m_pCmdDraw->SetPipelineState(m_pPipelineState);
    m_pCmdDraw->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
    D3D12_RECT scissor = { 0,(LONG)m_uBufferWidth,(LONG)m_uBufferHeight };
    m_pCmdDraw->RSSetScissorRects(1,&scissor);
    m_pCmdDraw->DrawInstanced(4,0);
    this->SetResourceBarrier(m_pCmdDraw,D3D12_RESOURCE_USAGE_RENDER_TARGET,D3D12_RESOURCE_USAGE_PRESENT);
    hr = m_pCmdDraw->Close();
}

好了,开启DirectComposition的话,超过哦哦就像这个样子:

不过目前还有问题,就是”闪狗眼”,不知道是不是本机原因,还是D3D12未完善,还是其他什么原因:
不能通过ID3D12Device::QueryInterface,获取IDXGIDevice1,尝试使用D3D11on12CreateDevice(官方文档目前未收录),创建一个D3D11的设备,从中获取IDXGIDevice1,这倒是成功了,不过再调用IDXGIDevice1::SetMaximumFrameLatency居然破天荒地返回E_NOTIMP,看到到现在为止,D3D12还没有和其他组件对接好,可惜了….


代码下载地址:

点击这里

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读