Создание графа связывания функций и связывание его с скомпилированным кодом

Здесь мы покажем, как создавать графы компоновки функций (FLG) для шейдеров и как связать эти шейдеры с библиотекой шейдеров для создания больших двоичных объектов шейдеров, которые может использовать среда выполнения Direct3D.

Цель: Создание графа связывания функций и связывание его с скомпилированным кодом.

Предварительные требования

Предполагается, что вы знакомы с C++. Также вы должны быть знакомы с основными принципами программирования графики.

Мы также предполагаем, что вы прошли упаковку библиотеки шейдеров.

Время выполнения: 30 минут.

Инструкции

1. Создайте граф связывания функций для вершинного шейдера.

Вызовите функцию D3DCreateFunctionLinkingGraph , чтобы создать граф связывания функций (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, чтобы создать узел для функции вершинного шейдера main и выполнить вызовы ID3D11FunctionLinkingGraph::P assValue для передачи значений из входного узла в узел для функции main вершинного шейдера.

            // 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 для передачи значений из узла для функции вершинного шейдера main в выходной узел.

            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 для создания объекта input-layout.

            // 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, чтобы создать узел для функции main пиксельного шейдера и выполнить вызовы ID3D11FunctionLinkingGraph::P assValue для передачи значений из входного узла в узел для функции шейдера main пикселей.

            // 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.

Поздравляем! Теперь вы готовы использовать компоновку шейдеров в собственных приложениях.

Использование связывания шейдеров