Condividi tramite


Creazione di un grafo di collegamento di funzioni e collegamento al codice compilato

In questo articolo viene illustrato come costruire grafici di collegamento di funzioni (FLG) per gli shader e come collegare tali shader a una libreria shader per produrre BLOB shader che il runtime Direct3D può usare.

Obiettivo: Per costruire un grafo di collegamento di funzioni e collegarlo al codice compilato.

Prerequisiti

Partiamo dal presupposto che tu abbia familiarità con C++. Devi inoltre avere un'esperienza di base dei concetti di programmazione di grafica.

Si presuppone anche che sia stata eseguita la creazione di pacchetti di una libreria shader.

Tempo di completamento: 30 minuti.

Istruzioni

1. Costruire un grafo di collegamento di funzione per il vertex shader.

Chiamare la funzione D3DCreateFunctionLinkingGraph per creare un function-linking-graph (ID3D11FunctionLinkingGraph) per rappresentare il vertex shader.

Usare una matrice di strutture D3D11_PARAMETER_DESC per definire i parametri di input per il vertex shader. La fase input-assembler invia i parametri di input al vertex shader. Il layout dei parametri di input del vertex shader corrisponde al layout del vertex shader nel codice compilato. Dopo aver definito i parametri di input, chiamare il metodo ID3D11FunctionLinkingGraph::SetInputSignature per definire il nodo di input (ID3D11LinkingNode) per il vertex shader.

            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());

Chiamare il metodo ID3D11FunctionLinkingGraph::CallFunction per creare un nodo per la funzione vertex shader principale ed effettuare chiamate a ID3D11FunctionLinkingGraph::P assValue per passare valori dal nodo di input al nodo per la funzione vertex shader principale.

            // 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());

Usare una matrice di strutture D3D11_PARAMETER_DESC per definire i parametri di output per il vertex shader. Il vertex shader invia i parametri di output al pixel shader. Il layout dei parametri di output del vertex shader corrisponde al layout del pixel shader nel codice compilato. Dopo aver definito i parametri di output, chiamare il metodo ID3D11FunctionLinkingGraph::SetOutputSignature per definire il nodo di output (ID3D11LinkingNode) per il vertex shader.

            // 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());

Effettuare chiamate a ID3D11FunctionLinkingGraph::P assValue per passare i valori dal nodo per la funzione vertex shader principale al nodo di output.

            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());

Chiamare il metodo ID3D11FunctionLinkingGraph::CreateModuleInstance per finalizzare il grafico del vertex shader.

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

Chiamare la funzione D3DCreateLinker per creare un linker (ID3D11Linker) che è possibile usare per collegare l'istanza della libreria shader creata in Creazione di pacchetti di una libreria shader con l'istanza del grafico vertex shader creato nel passaggio precedente. Chiamare il metodo ID3D11Linker::UseLibrary per specificare la libreria shader da usare per il collegamento. Chiamare il metodo ID3D11Linker::Link per collegare la libreria shader al grafico del vertex shader e per produrre un puntatore all'interfaccia ID3DBlob che è possibile usare per accedere al codice del vertex shader compilato. È quindi possibile passare questo codice vertex shader compilato al metodo ID3D11Device::CreateVertexShader per creare l'oggetto vertex shader e al metodo ID3D11Device::CreateInputLayout per creare l'oggetto layout di input.

            // 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. Costruire un grafo di collegamento di funzioni per il pixel shader.

Chiamare la funzione D3DCreateFunctionLinkingGraph per creare un function-linking-graph (ID3D11FunctionLinkingGraph) per rappresentare il pixel shader.

Usare una matrice di strutture D3D11_PARAMETER_DESC per definire i parametri di input per il pixel shader. La fase vertex shader invia i parametri di input al pixel shader. Il layout dei parametri di input del pixel shader corrisponde al layout del pixel shader nel codice compilato. Dopo aver definito i parametri di input, chiamare il metodo ID3D11FunctionLinkingGraph::SetInputSignature per definire il nodo di input (ID3D11LinkingNode) per il pixel shader.

            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());

Chiamare il metodo ID3D11FunctionLinkingGraph::CallFunction per creare un nodo per la funzione pixel shader principale ed effettuare chiamate a ID3D11FunctionLinkingGraph::P assValue per passare i valori dal nodo di input al nodo per la funzione pixel shader principale.

            // 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());

Usare una matrice di strutture D3D11_PARAMETER_DESC per definire i parametri di output per il pixel shader. Il pixel shader inserisce i parametri di output nella fase di unione dell'output. Dopo aver definito i parametri di output, chiamare il metodo ID3D11FunctionLinkingGraph::SetOutputSignature per definire il nodo di output (ID3D11LinkingNode) per il pixel shader ed effettuare chiamate a ID3D11FunctionLinkingGraph::P assValue per passare i valori da un nodo della funzione pixel shader al nodo di output.

            // 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());

Chiamare il metodo ID3D11FunctionLinkingGraph::CreateModuleInstance per finalizzare il grafico pixel shader.

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

Chiamare la funzione D3DCreateLinker per creare un linker (ID3D11Linker) che è possibile usare per collegare l'istanza della libreria shader creata in Creazione di pacchetti di una libreria shader con l'istanza del grafico pixel shader creato nel passaggio precedente. Chiamare il metodo ID3D11Linker::UseLibrary per specificare la libreria shader da usare per il collegamento. Chiamare il metodo ID3D11Linker::Link per collegare la libreria shader al grafico pixel shader e per produrre un puntatore all'interfaccia ID3DBlob che è possibile usare per accedere al codice pixel shader compilato. È quindi possibile passare questo codice pixel shader compilato al metodo ID3D11Device::CreatePixelShader per creare l'oggetto pixel shader.

            // 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);

Riepilogo

Sono stati usati i metodi ID3D11FunctionLinkingGraph per costruire i grafici dei vertici e dei pixel shader e per specificare la struttura dello shader a livello di codice.

Queste costruzioni di grafi sono costituite da sequenze di chiamate di funzioni precompilate che passano i valori l'uno all'altro. I nodi FLG (ID3D11LinkingNode) rappresentano nodi di input e output shader e chiamate di funzioni della libreria precompilata. L'ordine in cui si registrano i nodi di chiamata di funzione definisce la sequenza di chiamate. È necessario specificare prima il nodo di input (ID3D11FunctionLinkingGraph::SetInputSignature) e l'ultimo nodo di output (ID3D11FunctionLinkingGraph::SetOutputSignature). I bordi FLG definiscono il modo in cui i valori vengono passati da un nodo a un altro. I tipi di dati dei valori passati devono essere gli stessi; non esiste alcuna conversione implicita del tipo. Le regole di forma e scorrimento seguono il comportamento HLSL. I valori possono essere passati in avanti solo in questa sequenza.

Sono stati usati anche i metodi ID3D11Linker per collegare la libreria shader ai grafici dello shader e per produrre BLOB shader da usare per il runtime Direct3D.

Congratulazioni! A questo momento è possibile usare il collegamento di shader nelle proprie app.

Uso del collegamento di shader