构造函数链接关系图并将其链接到已编译的代码

下面介绍如何为着色器构造函数链接图 (FLG) ,以及如何将这些着色器链接到着色器库,以生成 Direct3D 运行时可以使用的着色器 blob。

目的: 构造函数链接关系图并将其链接到已编译的代码。

先决条件

我们假定你熟悉 C++。 你还需要具有图形编程概念方面的基本经验。

我们还假设你通过 打包着色器库

完成时间: 30 分钟。

说明

1.为顶点着色器构造函数链接图。

调用 D3DCreateFunctionLinkingGraph 函数以创建 function-linking-graph (ID3D11FunctionLinkingGraph) 来表示顶点着色器。

使用 D3D11_PARAMETER_DESC 结构的数组来定义顶点着色器的输入参数。 输入汇编器阶段将输入参数馈送到顶点着色器。 顶点着色器的输入参数布局与编译代码中的顶点着色器的布局匹配。 定义输入参数后,调用 ID3D11FunctionLinkingGraph::SetInputSignature 方法来定义顶点着色器的输入节点 (ID3D11LinkingNode) 。

            ComPtr<ID3D11FunctionLinkingGraph> vertexShaderGraph;
            DX::ThrowIfFailed(D3DCreateFunctionLinkingGraph(0, &vertexShaderGraph));

            // Define the main input node which will be fed by the Input Assembler pipeline stage.
            static const D3D11_PARAMETER_DESC vertexShaderInputParameters[] =
            {
                {"inputPos",  "POSITION0", D3D_SVT_FLOAT, D3D_SVC_VECTOR, 1, 3, D3D_INTERPOLATION_LINEAR, D3D_PF_IN, 0, 0, 0, 0},
                {"inputTex",  "TEXCOORD0", D3D_SVT_FLOAT, D3D_SVC_VECTOR, 1, 2, D3D_INTERPOLATION_LINEAR, D3D_PF_IN, 0, 0, 0, 0},
                {"inputNorm", "NORMAL0",   D3D_SVT_FLOAT, D3D_SVC_VECTOR, 1, 3, D3D_INTERPOLATION_LINEAR, D3D_PF_IN, 0, 0, 0, 0}
            };
            ComPtr<ID3D11LinkingNode> vertexShaderInputNode;
            LinkingThrowIfFailed(vertexShaderGraph->SetInputSignature(vertexShaderInputParameters, ARRAYSIZE(vertexShaderInputParameters), 
                &vertexShaderInputNode), vertexShaderGraph.Get());

调用 ID3D11FunctionLinkingGraph::CallFunction 方法为主顶点着色器函数创建节点,并调用 ID3D11FunctionLinkingGraph::P assValue ,将输入节点中的值传递给主顶点着色器函数的节点。

            // Create a node for the main VertexFunction call using the output of the helper functions.
            ComPtr<ID3D11LinkingNode> vertexFunctionCallNode;
            LinkingThrowIfFailed(vertexShaderGraph->CallFunction("", shaderLibrary.Get(), "VertexFunction", &vertexFunctionCallNode), 
                vertexShaderGraph.Get());

            // Define the graph edges from the input node and helper function nodes.
            LinkingThrowIfFailed(vertexShaderGraph->PassValue(homogenizeCallNodeForPos.Get(), D3D_RETURN_PARAMETER_INDEX, 
                vertexFunctionCallNode.Get(), 0), vertexShaderGraph.Get());
            LinkingThrowIfFailed(vertexShaderGraph->PassValue(vertexShaderInputNode.Get(), 1, vertexFunctionCallNode.Get(), 1), 
                vertexShaderGraph.Get());
            LinkingThrowIfFailed(vertexShaderGraph->PassValue(homogenizeCallNodeForNorm.Get(), D3D_RETURN_PARAMETER_INDEX, 
                vertexFunctionCallNode.Get(), 2), vertexShaderGraph.Get());

使用 D3D11_PARAMETER_DESC 结构的数组来定义顶点着色器的输出参数。 顶点着色器将其输出参数馈送给像素着色器。 顶点着色器的输出参数的布局与编译代码中的像素着色器的布局匹配。 定义输出参数后,调用 ID3D11FunctionLinkingGraph::SetOutputSignature 方法,为顶点着色器定义输出节点 (ID3D11LinkingNode) 。

            // Define the main output node which will feed the Pixel Shader pipeline stage.
            static const D3D11_PARAMETER_DESC vertexShaderOutputParameters[] =
            {
                {"outputTex",  "TEXCOORD0",   D3D_SVT_FLOAT, D3D_SVC_VECTOR, 1, 2, D3D_INTERPOLATION_UNDEFINED, D3D_PF_OUT, 0, 0, 0, 0},
                {"outputNorm", "NORMAL0",     D3D_SVT_FLOAT, D3D_SVC_VECTOR, 1, 3, D3D_INTERPOLATION_UNDEFINED, D3D_PF_OUT, 0, 0, 0, 0},
                {"outputPos",  "SV_POSITION", D3D_SVT_FLOAT, D3D_SVC_VECTOR, 1, 4, D3D_INTERPOLATION_UNDEFINED, D3D_PF_OUT, 0, 0, 0, 0}
            };
            ComPtr<ID3D11LinkingNode> vertexShaderOutputNode;
            LinkingThrowIfFailed(vertexShaderGraph->SetOutputSignature(vertexShaderOutputParameters, ARRAYSIZE(vertexShaderOutputParameters), 
                &vertexShaderOutputNode), vertexShaderGraph.Get());

调用 ID3D11FunctionLinkingGraph::P assValue ,将主顶点着色器函数的节点的值传递到输出节点。

            LinkingThrowIfFailed(vertexShaderGraph->PassValue(vertexFunctionCallNode.Get(), 0, vertexShaderOutputNode.Get(), 2), 
                vertexShaderGraph.Get());
            LinkingThrowIfFailed(vertexShaderGraph->PassValue(vertexFunctionCallNode.Get(), 1, vertexShaderOutputNode.Get(), 0), 
                vertexShaderGraph.Get());
            LinkingThrowIfFailed(vertexShaderGraph->PassValue(vertexFunctionCallNode.Get(), 2, vertexShaderOutputNode.Get(), 1), 
                vertexShaderGraph.Get());

调用 ID3D11FunctionLinkingGraph::CreateModuleInstance 方法以完成顶点着色器图形。

            // Finalize the vertex shader graph.
            ComPtr<ID3D11ModuleInstance> vertexShaderGraphInstance;
            LinkingThrowIfFailed(vertexShaderGraph->CreateModuleInstance(&vertexShaderGraphInstance, nullptr), vertexShaderGraph.Get());

调用 D3DCreateLinker 函数以创建一个链接器 (ID3D11Linker) ,该链接器库可用于链接在 打包着色器库中创建的着色器库 的实例以及在上一步中创建的顶点着色器图形的实例。 调用 ID3D11Linker::UseLibrary 方法以指定要用于链接的着色器库。 调用 ID3D11Linker::Link 方法,将着色器库与顶点着色器图链接,并生成指向可用于访问已编译顶点着色器代码的 ID3DBlob 接口的指针。 然后,可以将此编译的顶点着色器代码传递给 ID3D11Device::CreateVertexShader 方法,以创建顶点着色器对象,并将 ID3D11Device::CreateInputLayout 方法传递给创建输入布局对象。

            // Create a linker and hook up the module instance.
            ComPtr<ID3D11Linker> linker;
            DX::ThrowIfFailed(D3DCreateLinker(&linker));
            DX::ThrowIfFailed(linker->UseLibrary(shaderLibraryInstance.Get()));

            // Link the vertex shader.
            ComPtr<ID3DBlob> errorBlob;
            if (FAILED(linker->Link(vertexShaderGraphInstance.Get(), "main", ("vs" + m_shaderModelSuffix).c_str(), 0, &vertexShaderBlob, 
                &errorBlob)))
            {
                throw errorBlob;
            }


    ComPtr<ID3D11VertexShader> vertexShader;
    DX::ThrowIfFailed(
        device->CreateVertexShader(
            vertexShaderBlob->GetBufferPointer(),
            vertexShaderBlob->GetBufferSize(),
            nullptr,
            &vertexShader
            )
        );
    context->VSSetShader(vertexShader.Get(), nullptr, 0);
    D3D11_INPUT_ELEMENT_DESC inputLayoutDesc[] =
    {
        { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0,  D3D11_INPUT_PER_VERTEX_DATA, 0 },
        { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT,    0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
        { "NORMAL",   0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 20, D3D11_INPUT_PER_VERTEX_DATA, 0 }
    };
    ComPtr<ID3D11InputLayout> inputLayout;
    DX::ThrowIfFailed(device->CreateInputLayout(inputLayoutDesc, ARRAYSIZE(inputLayoutDesc), vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), &inputLayout));
    context->IASetInputLayout(inputLayout.Get());

3.为像素着色器构造函数链接图。

调用 D3DCreateFunctionLinkingGraph 函数以创建函数链接图形 (ID3D11FunctionLinkingGraph) 来表示像素着色器。

使用 D3D11_PARAMETER_DESC 结构的数组来定义像素着色器的输入参数。 顶点着色器阶段将输入参数馈送到像素着色器。 像素着色器的输入参数的布局与编译代码中的像素着色器的布局匹配。 定义输入参数后,调用 ID3D11FunctionLinkingGraph::SetInputSignature 方法,为像素着色器定义输入节点 (ID3D11LinkingNode) 。

            ComPtr<ID3D11FunctionLinkingGraph> pixelShaderGraph;
            DX::ThrowIfFailed(D3DCreateFunctionLinkingGraph(0, &pixelShaderGraph));

            // Define the main input node which will be fed by the vertex shader pipeline stage.
            static const D3D11_PARAMETER_DESC pixelShaderInputParameters[] =
            {
                {"inputTex",  "TEXCOORD0",   D3D_SVT_FLOAT, D3D_SVC_VECTOR, 1, 2, D3D_INTERPOLATION_UNDEFINED, D3D_PF_IN, 0, 0, 0, 0},
                {"inputNorm", "NORMAL0",     D3D_SVT_FLOAT, D3D_SVC_VECTOR, 1, 3, D3D_INTERPOLATION_UNDEFINED, D3D_PF_IN, 0, 0, 0, 0},
                {"inputPos",  "SV_POSITION", D3D_SVT_FLOAT, D3D_SVC_VECTOR, 1, 4, D3D_INTERPOLATION_UNDEFINED, D3D_PF_IN, 0, 0, 0, 0}
            };
            ComPtr<ID3D11LinkingNode> pixelShaderInputNode;
            LinkingThrowIfFailed(pixelShaderGraph->SetInputSignature(pixelShaderInputParameters, ARRAYSIZE(pixelShaderInputParameters), 
                &pixelShaderInputNode), pixelShaderGraph.Get());

调用 ID3D11FunctionLinkingGraph::CallFunction 方法,为主像素着色器函数创建节点,并调用 ID3D11FunctionLinkingGraph::P assValue ,将输入节点中的值传递给主像素着色器函数的节点。

            // Create a node for the main ColorFunction call and connect it to the pixel shader inputs.
            ComPtr<ID3D11LinkingNode> colorValueNode;
            LinkingThrowIfFailed(pixelShaderGraph->CallFunction("", shaderLibrary.Get(), "ColorFunction", &colorValueNode), 
                pixelShaderGraph.Get());

            // Define the graph edges from the input node.
            LinkingThrowIfFailed(pixelShaderGraph->PassValue(pixelShaderInputNode.Get(), 0, colorValueNode.Get(), 0), 
                pixelShaderGraph.Get());
            LinkingThrowIfFailed(pixelShaderGraph->PassValue(pixelShaderInputNode.Get(), 1, colorValueNode.Get(), 1), 
                pixelShaderGraph.Get());

使用 D3D11_PARAMETER_DESC 结构的数组来定义像素着色器的输出参数。 像素着色器将其输出参数馈送到 输出合并阶段。 定义输出参数后,调用 ID3D11FunctionLinkingGraph::SetOutputSignature 方法以定义像素着色器的输出节点 (ID3D11LinkingNode) ,并调用 ID3D11FunctionLinkingGraph::P assValue 将像素着色器函数节点中的值传递给输出节点。

            // Define the main output node which will feed the Output Merger pipeline stage.
            D3D11_PARAMETER_DESC pixelShaderOutputParameters[] =
            {
                {"outputColor", "SV_TARGET", D3D_SVT_FLOAT, D3D_SVC_VECTOR, 1, 4, D3D_INTERPOLATION_UNDEFINED, D3D_PF_OUT, 0, 0, 0, 0}
            };
            ComPtr<ID3D11LinkingNode> pixelShaderOutputNode;
            LinkingThrowIfFailed(pixelShaderGraph->SetOutputSignature(pixelShaderOutputParameters, ARRAYSIZE(pixelShaderOutputParameters), 
                &pixelShaderOutputNode), pixelShaderGraph.Get());
            LinkingThrowIfFailed(pixelShaderGraph->PassValue(fillAlphaCallNode.Get(), D3D_RETURN_PARAMETER_INDEX, pixelShaderOutputNode.Get(), 0), 
                pixelShaderGraph.Get());

调用 ID3D11FunctionLinkingGraph::CreateModuleInstance 方法以完成像素着色器图形。

            // Finalize the pixel shader graph.
            ComPtr<ID3D11ModuleInstance> pixelShaderGraphInstance;
            LinkingThrowIfFailed(pixelShaderGraph->CreateModuleInstance(&pixelShaderGraphInstance, nullptr), pixelShaderGraph.Get());

调用 D3DCreateLinker 函数以创建一个链接器 (ID3D11Linker) ,该链接器库可用于链接在 打包着色器库中创建的着色器库 的实例以及在上一步中创建的像素着色器图形的实例。 调用 ID3D11Linker::UseLibrary 方法以指定要用于链接的着色器库。 调用 ID3D11Linker::Link 方法,将着色器库与像素着色器图链接,并生成指向 ID3DBlob 接口的指针,可用于访问编译的像素着色器代码。 然后,可以将此编译的像素着色器代码传递给 ID3D11Device::CreatePixelShader 方法以创建像素着色器对象。

            // Create a linker and hook up the module instance.
            ComPtr<ID3D11Linker> linker;
            DX::ThrowIfFailed(D3DCreateLinker(&linker));
            DX::ThrowIfFailed(linker->UseLibrary(shaderLibraryInstance.Get()));

            // Link the pixel shader.
            ComPtr<ID3DBlob> errorBlob;
            if (FAILED(linker->Link(pixelShaderGraphInstance.Get(), "main", ("ps" + m_shaderModelSuffix).c_str(), 0, &pixelShaderBlob, &errorBlob)))
            {
                throw errorBlob;
            }


    ComPtr<ID3D11PixelShader> pixelShader;
    DX::ThrowIfFailed(
        device->CreatePixelShader(
            pixelShaderBlob->GetBufferPointer(),
            pixelShaderBlob->GetBufferSize(),
            nullptr,
            &pixelShader
            )
        );
    context->PSSetShader(pixelShader.Get(), nullptr, 0);

总结

我们使用 ID3D11FunctionLinkingGraph 方法构造顶点和像素着色器图形,并以编程方式指定着色器结构。

这些图形构造由预编译函数调用序列组成,这些调用相互传递值。 FLG 节点 (ID3D11LinkingNode) 表示预编译库函数的输入和输出着色器节点和调用。 注册函数调用节点的顺序定义调用序列。 必须首先指定输入节点 (ID3D11FunctionLinkingGraph::SetInputSignature) ,最后一个 (ID3D11FunctionLinkingGraph::SetOutputSignature) 。 FLG 边缘定义如何将值从一个节点传递到另一个节点。 传递值的数据类型必须相同;没有隐式类型转换。 形状和重排规则遵循 HLSL 行为。 值只能在此序列中向前传递。

我们还使用 ID3D11Linker 方法将着色器库与着色器图链接,并为要使用的 Direct3D 运行时生成着色器 Blob。

祝贺你! 现在,你已准备好在自己的应用中使用着色器链接。

使用着色器链接