D3D12渲染技术之光栅化与管道

光栅化状态

虽然渲染管道的许多部分都是可编程的,但某些部分只是可配置的, 由D3D12_RASTERIZER_DESC结构表示的光栅化器状态组用于配置渲染管道的光栅化阶段:

typedef struct D3D12_RASTERIZER_DESC {
D3D12_FILL_MODE FillMode;   // Default: D3D12_FILL_SOLID
  D3D12_CULL_MODE CullMode;   // Default: D3D12_CULL_BACK
  BOOL FrontCounterClockwise; // Default: false
  INT DepthBias;              // Default: 0
  FLOAT DepthBiasClamp;       // Default: 0.0f
  FLOAT SlopeScaledDepthBias; // Default: 0.0f
  BOOL DepthClipEnable;       // Default: true
  BOOL ScissorEnable;         // Default: false
  BOOL MultisampleEnable;     // Default: false
  BOOL AntialiasedLineEnable; // Default: false
  UINT ForcedSampleCount;     // Default: 0

  // Default: D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF
  D3D12_CONSERVATIVE_RASTERIZATION_MODE ConservativeRaster;
} D3D12_RASTERIZER_DESC;

这个结构体有很多项,我们要使用它,就必须搞清楚这些项的含义,DX文档都有详细的介绍。本篇博客就不一一说明了,接下来我们看看如何使用。
以下代码显示如何创建启用线框模式并禁用背面剔除的栅格化状态:

CD3DX12_RASTERIZER_DESC rsDesc(D3D12_DEFAULT);
rsDesc.FillMode = D3D12_FILL_WIREFRAME;
rsDesc.CullMode = D3D12_CULL_NONE;

CD3DX12_RASTERIZER_DESC是一个扩展类,它扩展了D3D12_RASTERIZER_DESC并添加了一些辅助构造函数。 特别是,它有一个构造函数,它接受类型为CD3D12_DEFAULT的对象,它只是一个用于重载的伪类型,表示光栅化器状态成员应初始化为默认值。 CD3D12_DEFAULT和D3D12_DEFAULT的定义如下:

struct CD3D12_DEFAULT {};
extern const DECLSPEC_SELECTANY CD3D12_DEFAULT D3D12_DEFAULT;

管道状态

前面,我们已经展示了如何描述输入布局描述,如何创建顶点和像素着色器,以及如何配置光栅化器状态组,我们还没有展示如何将这些对象绑定到图形管道以供实际使用。 控制图形管道状态的大多数对象被指定称为管道状态对象(PSO)的聚合,它由ID3D12PipelineState接口表示。 要创建PSO,我们首先通过填写D3D12_GRAPHICS_PIPELINE_STATE_DESC实例来描述它:

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_PRIMITIVE_TOPOLOGY_TYPE PrimitiveTopologyType;
  UINT NumRenderTargets;
  DXGI_FORMAT RTVFormats[8];
  DXGI_FORMAT DSVFormat;
  DXGI_SAMPLE_DESC SampleDesc;
} D3D12_GRAPHICS_PIPELINE_STATE_DESC;

在我们填写了D3D12_GRAPHICS_PIPELINE_STATE_DESC实例之后,我们使用ID3D12Device :: CreateGraphicsPipelineState方法创建了一个ID3D12PipelineState对象:

ComPtr<ID3D12RootSignature> mRootSignature;
std::vector<D3D12_INPUT_ELEMENT_DESC> mInputLayout;
ComPtr<ID3DBlob> mvsByteCode;
ComPtr<ID3DBlob> mpsByteCode;
…
D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc;
ZeroMemory(&psoDesc, sizeof(D3D12_GRAPHICS_PIPELINE_STATE_DESC));
psoDesc.InputLayout = { mInputLayout.data(), (UINT)mInputLayout.size() };
psoDesc.pRootSignature = mRootSignature.Get();
psoDesc.VS = 
{ 
  reinterpret_cast<BYTE*>(mvsByteCode->GetBufferPointer()), 
  mvsByteCode->GetBufferSize() 
};
psoDesc.PS = 
{ 
  reinterpret_cast<BYTE*>(mpsByteCode->GetBufferPointer()), 
  mpsByteCode->GetBufferSize() 
};
psoDesc.RasterizerState = CD3D12_RASTERIZER_DESC(D3D12_DEFAULT);
psoDesc.BlendState = CD3D12_BLEND_DESC(D3D12_DEFAULT);
psoDesc.DepthStencilState = CD3D12_DEPTH_STENCIL_DESC(D3D12_DEFAULT);
psoDesc.SampleMask = UINT_MAX;
psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
psoDesc.NumRenderTargets = 1;
psoDesc.RTVFormats[0] = mBackBufferFormat;
psoDesc.SampleDesc.Count = m4xMsaaState ? 4 : 1;
psoDesc.SampleDesc.Quality = m4xMsaaState ? (m4xMsaaQuality - 1) : 0;
psoDesc.DSVFormat = mDepthStencilFormat;

ComPtr<ID3D12PipelineState> mPSO;
md3dDevice->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&mPSO)));

这在一个ID3D12PipelineState对象中是相当多的状态,Direct3D可以验证所有状态是否兼容,并且驱动程序可以预先生成所有代码以编程硬件状态。在Direct3D 11状态模型中,这些渲染状态块分别设置,但是,如果一个状态发生变化,则可能还需要驱动程序将硬件重新编程为另一个依赖状态,由于许多状态已更改为配置管道,因此硬件状态可能会被重新编程,为了避免这种冗余,驱动程序通常推迟编程硬件状态,直到在整个流水线状态已发出绘制调用,它需要跟踪哪些状态已更改,然后生成代码以在运行时对硬件状态进行编程。在新的Direct3D 12模型中,驱动程序可以生成在初始化时编程管道状态所需的所有代码。

注意,由于PSO验证和创建可能非常耗时,因此应在初始化时生成PSO。 一个例外可能是在第一次引用时根据需要在运行时创建PSO; 然后将其存储在一个集合(如哈希表)中,以便快速获取以供将来使用。

并非所有渲染状态都封装在PSO中, 视口和裁剪矩形等某些状态是独立于PSO指定的。 这种状态可以有效地独立地设置到另一个流水线状态,因此通过将它们包括在PSO中没有什么优势。
Direct3D基本上是一个状态机, 在我们改变之前,事情一直保持现状, 如果我们正在绘制的某些对象使用一个PSO,而我们正在绘制的其他对象需要不同的PSO,那么我们需要构建代码,如下所示:

// Reset specifies initial PSO.
mCommandList->Reset(mDirectCmdListAlloc.Get(), mPSO1.Get())
/* …draw objects using PSO 1… */

// Change PSO
mCommandList->SetPipelineState(mPSO2.Get());
/* …draw objects using PSO 2… */

// Change PSO
mCommandList->SetPipelineState(mPSO3.Get());
/* …draw objects using PSO 3… */

换句话说,当PSO绑定到命令列表时,它将不会更改,直到覆盖它(或重置命令列表)。

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页