LiquidVR™
LiquidVR™ 提供了一个基于 Direct3D 11 的接口,应用程序可以通过该接口访问以下 GPU 功能,而无论系统是否安装了 VR 设备。
本页内容
首先,您需要一个支持 GPU 工作图的 Microsoft DirectX® 着色器编译器和 Agility SDK 运行时版本。这些可以从 Microsoft 直接获取,在此处。您还需要一张支持 GPU 工作图的显卡(AMD Radeon™ RX 7000 系列显卡)以及支持 GPU 工作图的 AMD 驱动程序,可以在此处获取。
注意:本示例旨在向熟悉 DirectX 12 的用户介绍工作图。如果您还不熟悉 DirectX 12,建议在继续之前了解更多信息!
提供了一个最小的 C++ 示例,实现了工作图的基本“Hello World”范例。您可以在此处找到它。该程序还经历了设置 DirectX 并输出结果的过程,因此仅设置工作图本身的基本组件将在以下各节中进行概述。
创建一个包含至少一个工作图节点的 HLSL 源文件。请注意,声明中可能涉及许多新的 HLSL 内建函数和属性。其中许多选项是互斥的,本示例并未尝试展示所有这些选项!
[Shader("node")][NodeLaunch("broadcasting")][NodeDispatchGrid(1, 1, 1)][NumThreads(1, 1, 1)]void BroadcastNode(){ ...}将应用程序配置为使用与工作图兼容的 Agility SDK 版本。
extern "C" { __declspec(dllexport) extern const UINT D3D12SDKVersion = 613; } // or later在继续之前,请确保当前的 DirectX 12 堆栈支持工作图。
D3D12_FEATURE_DATA_D3D12_OPTIONS21 Options = {};pDevice->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS21, &Options, sizeof(Options));
return (Options.WorkGraphsTier != D3D12_WORK_GRAPHS_TIER_NOT_SUPPORTED);像编译传统着色器一样编译您的工作图源。将 nullptr 作为入口点,将 lib_6_8 作为目标配置文件。
{#highlight-nullptr-lib_6_8}
pCompiler->Compile(pSource, nullptr, nullptr, L"lib_6_8", nullptr, 0, nullptr, 0, nullptr, &pOperationResult)创建一个状态对象,其中包含一个 CD3DX12_DXIL_LIBRARY_SUBOBJECT(包含编译后的着色器源)和一个 CD3DX12_WORK_GRAPH_SUBOBJECT(包含目标节点和名称)。
说明
- 即使您只有一个工作图子对象,也仍然希望指定一个名称以便稍后查找相应的程序标识符。
- 在填充
CD3DX12_WORK_GRAPH_SUBOBJECT时,我们调用了IncludeAllAvailableNodes()。这简化了一些设置,我们稍后会再回到这一点。
CD3DX12_STATE_OBJECT_DESC Desc(D3D12_STATE_OBJECT_TYPE_EXECUTABLE);
...
CD3DX12_DXIL_LIBRARY_SUBOBJECT* LibraryDesc = Desc.CreateSubobject<CD3DX12_DXIL_LIBRARY_SUBOBJECT>();CD3DX12_SHADER_BYTECODE gwgLibraryCode(pGwgLibrary);LibraryDesc->SetDXILLibrary(&gwgLibraryCode);
CD3DX12_WORK_GRAPH_SUBOBJECT* WorkGraphDesc = Desc.CreateSubobject<CD3DX12_WORK_GRAPH_SUBOBJECT>();WorkGraphDesc->IncludeAllAvailableNodes();WorkGraphDesc->SetProgramName(kProgramName);
pDevice->CreateStateObject(Desc, IID_PPV_ARGS(&pStateObject));为工作图程序分配要使用的后备内存。
UINT WGIndex = pWorkGraphProperties->GetWorkGraphIndex(kProgramName);D3D12_WORK_GRAPH_MEMORY_REQUIREMENTS MemoryRequirements = {};pWorkGraphProperties->GetWorkGraphMemoryRequirements(WGIndex, &MemoryRequirements);if (MemoryRequirements.MaxSizeInBytes > 0){ pBackingMemoryResource = AllocateBuffer(pDevice, MemoryRequirements.MaxSizeInBytes, D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_HEAP_TYPE_DEFAULT);}创建一个 D3D12_SET_PROGRAM_DESC 来封装状态对象。
注意:每个唯一的程序至少需要设置一次
D3D12_SET_WORK_GRAPH_FLAG_INITIALIZE标志,但使用它会产生性能开销。对于将运行多次的工作图,请不要在每次 Dispatch 时都指定它!
注意:我们建议将该值设置为
MaxSizeInBytes以确保最佳性能。
D3D12_SET_PROGRAM_DESC Desc = {};Desc.Type = D3D12_PROGRAM_TYPE_WORK_GRAPH;Desc.WorkGraph.ProgramIdentifier = pStateObjectProperties->GetProgramIdentifier(kProgramName);Desc.WorkGraph.Flags = D3D12_SET_WORK_GRAPH_FLAG_INITIALIZE;if (pBackingMemoryResource){ Desc.WorkGraph.BackingMemory = { pBackingMemoryResource->GetGPUVirtualAddress(), MemoryRequirements.MaxSizeInBytes };}最后,创建一个 D3D12_DISPATCH_GRAPH_DESC 来封装 Dispatch 的输入。
说明
- 即使图不接受任何输入记录,您也必须将
NumRecords设置为大于 0 的值,否则将不会发生任何 Dispatch。如果您不确定这意味着什么,我们稍后会再讨论!- 在此示例中,我们编译的工作图源仅包含一个节点,并且该节点被包含为图的入口点(因为我们在创建状态对象时调用了
IncludeAllAvailableNodes())。这意味着我们可以安全地假设只有一个可能的入口点,并且该入口点的索引为 0。
D3D12_DISPATCH_GRAPH_DESC DispatchGraphDesc = { };DispatchGraphDesc.Mode = D3D12_DISPATCH_MODE_NODE_CPU_INPUT;DispatchGraphDesc.NodeCPUInput = { };DispatchGraphDesc.NodeCPUInput.EntrypointIndex = 0;DispatchGraphDesc.NodeCPUInput.NumRecords = 1;在活动命令列表中放置命令以设置程序并 Dispatch 工作图。如果插入到现有的命令列表中,您需要查询 ID3D12GraphicsCommandListExperimental 接口以访问这些命令。
pCommandList->SetProgram(&SetProgramDesc);pCommandList->DispatchGraph(&DispatchGraphDesc);