Поделиться через


Связывание шейдеров эффектов

Direct2D использует оптимизацию, называемую связыванием шейдера эффектов, которая объединяет несколько проходов рендеринга графа эффектов в один проход.

Общие сведения о связывании шейдеров эффектов

Оптимизация связывания эффектных шейдеров основывается на связывании шейдеров HLSL, функции в составе Direct3D 11.2, которая позволяет создавать шейдеры пикселей и вершин в процессе выполнения путем связывания предварительно скомпилированных функций шейдера. На следующих рисунках показана концепция связывания шейдеров эффектов в графе эффектов. На первом рисунке показан типичный граф эффектов Direct2D с четырьмя преобразованиями отрисовки. Без связывания шейдеров каждое преобразование использует отрисовочный проход и требует промежуточной поверхности; всего для этого графа требуется 4 прохода и 3 промежуточные поверхности.

график трансформации без подключения шейдера: 4 прохода и 3 промежуточных.

На втором рисунке показан тот же граф эффектов, в котором каждое преобразование отрисовки было заменено на связанную версию функции. Direct2D может связать весь граф и выполнить его в одном проходе без каких-либо промежуточных. Это может обеспечить значительное снижение времени выполнения GPU и снижение пикового потребления памяти GPU.

преобразование графа с связыванием шейдера: 1 проход, 0 промежуточных.

 

Связывание шейдеров эффектов осуществляется на уровне отдельных преобразований внутри эффекта, что означает, что даже граф с одним эффектом может получить выгоду от связывания шейдеров, если этот эффект имеет несколько допустимых преобразований.

Использование линковки шейдеров эффектов

Если вы создаете приложение Direct2D, использующее эффекты, вам не нужно ничего делать, чтобы воспользоваться преимуществами связывания шейдеров эффектов. Direct2D автоматически анализирует граф эффектов, чтобы определить наиболее оптимальный способ связывания каждого преобразования.

Авторы эффектов отвечают за реализацию своих эффектов таким образом, чтобы поддерживать связывание шейдеров эффектов; дополнительную информацию см. в разделе Создание пользовательского эффекта, совместимого со связыванием шейдеров ниже. Все встроенные эффекты поддерживают связывание шейдеров.

Direct2D будет связывать соседние преобразования отрисовки только в тех случаях, когда это целесообразно. Он учитывает несколько факторов при определении того, следует ли связывать два преобразования. Например, связывание шейдеров не выполняется, если одно из преобразований использует вершины или вычислительные шейдеры, так как можно связать только шейдеры пикселей. Кроме того, если эффект не был создан для совместимости с связыванием шейдера, окружающие преобразования не будут связаны с ним.

В случае, если существует такая опасность связывания, Direct2D не будет связывать какие-либо преобразования, смежные с опасностью, но по-прежнему попытаются связать оставшуюся часть графа.

граф преобразования с опасностью связывания: 2 проходит, 1 промежуточный.

Создание настраиваемого эффекта, совместимого с подключением шейдера

Если вы создаете собственный пользовательский эффект Direct2D, необходимо убедиться, что его преобразования поддерживают связывание шейдеров эффектов. Для этого требуются некоторые незначительные изменения от того, как были реализованы предыдущие пользовательские эффекты. Если преобразование в пользовательском эффекте не поддерживает связывание шейдеров, Direct2D не будет связывать его с любыми преобразованиями, смежными с ним в графе эффектов.

В качестве автора пользовательского эффекта следует учитывать несколько ключевых понятий и требований:

  • Нет изменений в реализации интерфейса эффектов

    Вам не нужно изменять код, реализуя различные интерфейсы эффектов, такие как ID2D1DrawTransform.

  • Укажите полную и экспортную версию шейдеров

    Необходимо предоставить версию функции экспорта шейдеров эффекта, которые можно связать с Direct2D. Кроме того, необходимо также продолжить предоставление исходного полного шейдера; Это связано с тем, что Direct2D выбирает в среде выполнения правильную версию шейдера в зависимости от того, следует ли применять связывание шейдеров к определенной ссылке в графе.

    Если преобразование предоставляет только полный двоичный блок шейдера пикселей (через ID2D1EffectContext::LoadPixelShader), оно не будет связано с соседними преобразованиями.

  • вспомогательные функции

    Direct2D предоставляет вспомогательные функции HLSL и макросы, которые автоматически создают как полные версии шейдеров, так и версии функций их экспорта. Эти хелперы можно найти в d2d1effecthelpers.hlsli. Кроме того, компилятор HLSL (FXC) позволяет вставлять шейдер функции экспорта в частное поле в полном шейдере. Таким образом, необходимо создать шейдер только один раз и передать обе версии в Direct2D одновременно. Как d2d1effecthelpers.hlsli, так и компилятор FXC включены в состав пакета SDK для Windows.

    Вспомогательные функции:

    Вы также можете вручную создать две версии каждого шейдера и скомпилировать их дважды, если выполнены спецификации, описанные ниже в Спецификации функций экспорта.

  • Только шейдеры пикселей

    Direct2D не поддерживает связывание вычислительных или вершинных шейдеров. Однако если в эффекте используются шейдеры вершин и пикселей, выходные данные шейдера пикселей по-прежнему могут быть связаны.

  • Простая и сложная выборка

    Связывание шейдерных функций работает путем подключения выходных данных одного пиксельного шейдера к входным данным последующего пиксельного шейдера. Это возможно только в том случае, если пиксельный шейдер-потребитель требует лишь одно входное значение для выполнения вычислений; Обычно это значение получается из выборки входной текстуры по координате текстуры, эмитированной шейдером вершин. Такой шейдер пикселей, как говорят, выполняет простую выборку.

    преобразование в градации серого является примером простой выборки. Значение выходного пикселя зависит только от значения соответствующего входного пикселя.

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

    гаусианский размытие является примером сложной выборки. Значение пикселя вывода в центре зависит от нескольких входных пикселей.

    Только функции шейдера с простыми входными данными могут иметь входные данные, предоставляемые другой функцией шейдера. Функции шейдера с сложными входными данными должны быть предоставлены с помощью входной текстуры для примера. Это означает, что Direct2D не будет связывать шейдер со сложными входными данными с его предшественником.

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

Пример шейдера эффектов, совместимых с связыванием

Используя вспомогательные средства D2D, следующий фрагмент кода представляет простой шейдер эффектов, совместимый с связыванием:

#define D2D_INPUT_COUNT 1
#define D2D_INPUT0_SIMPLE
#include “d2d1effecthelpers.hlsli”

D2D_PS_ENTRY(LinkingCompatiblePixelShader)
{
    float4 input = D2DGetInput(0);
    input.rgb *= input.a;
    return input;
}          

В этом кратком примере обратите внимание, что параметры функции не объявляются, что число входных данных и типов каждого входного значения объявляется перед функцией входа, входные данные извлекаются путем вызова D2DGetInput, и перед включением вспомогательного файла необходимо определить директивы препроцессора.

Шейдер, совместимый с связыванием, должен предоставлять как обычный однопроходный шейдер пикселей, так и функцию шейдера экспорта. Макрос D2D_PS_ENTRY позволяет создавать каждое из них из одного кода при использовании в сочетании со скриптом компиляции шейдера.

При компиляции полного шейдера макросы развертываются в следующем коде, который имеет входную сигнатуру, совместимую со стандартом D2D.

Texture2D<float4> InputTexture0;
SamplerState InputSampler0;

float4 LinkingCompatiblePixelShader(
    float4 pos   : SV_POSITION,
    float4 posScene : SCENE_POSITION,
    float4 uv0  : TEXCOORD0
    ) : SV_Target
    {
        float4 input = InputTexture0.Sample(InputSampler0, uv0.xy);
        input.rgb *= input.a;
        return input;
    }    

При компиляции версии функции экспорта одного кода создается следующий код:

// Shader function version
export float4 LinkingCompatiblePixelShader_Function(
    float4 input0 : INPUT0)
    {
        input.rgb *= input.a;
        return input;
    }      

Обратите внимание, что входные данные текстуры, обычно полученные выборкой Texture2D, заменены входными данными функции (input0).

Чтобы просмотреть полное пошаговое описание того, что необходимо сделать для создания эффекта, совместимого с привязкой, см. руководство по пользовательским эффектам и примеру пользовательских эффектов изображений Direct2D.

Компиляция совместимого шейдера для связывания

Чтобы можно было связать, двоичный объект шейдера пикселей, переданный D2D, должен содержать как полную, так и экспортируемую версии шейдера. Это достигается путем внедрения скомпилированной функции экспорта в область D3D_BLOB_PRIVATE_DATA.

Когда шейдеры создаются с помощью вспомогательных функций D2D, целевой объект компиляции D2D должен быть определен во время компиляции. Целевые типы компиляции — это D2D_FULL_SHADER и D2D_FUNCTION.

Компиляция шейдера эффектов, совместимого с связыванием, представляет собой двухэтапный процесс:

Заметка

При компиляции эффекта с помощью Visual Studio необходимо создать пакетный файл, который выполняет обе команды FXC и запустить этот пакетный файл как пользовательский шаг сборки, который выполняется перед этапом компиляции.

 

Шаг 1. Компиляция функции экспорта

fxc /T <shadermodel> <MyShaderFile>.hlsl /D D2D_FUNCTION /D D2D_ENTRY=<entry> /Fl <MyShaderFile>.fxlib           

Чтобы скомпилировать версию функции экспорта шейдера, необходимо передать следующие флаги в FXC.

Флаг Описание
/T <ШейдернаяМодель> Задайте <shaderModel> соответствующим профилем шейдера пикселей, как определено в синтаксисе FXC. Это должен быть один из профилей, перечисленных в разделе "Связывание шейдера HLSL".
<MyShaderFile>.hlsl Задайте <MyShaderFile> имя файла HLSL.
/D D2D_FUNCTION Это определение указывает FXC компилировать версию функции экспорта шейдера.
Запись /D D2D_ENTRY=<> Задайте для <записи> имя точки входа HLSL, определенной внутри макроса D2D_PS_ENTRY.
/Fl <MyShaderFile>.fxlib Задайте <MyShaderfile>, где требуется сохранить версию функции экспорта шейдера. Обратите внимание, что расширение .fxlib предназначено только для удобства идентификации.

Шаг 2. Компиляция полного шейдера и внедрение функции экспорта

fxc /T ps_<shadermodel> <MyShaderFile>.hlsl /D D2D_FULL_SHADER /D D2D_ENTRY=<entry> /E <entry> /setprivate <MyShaderFile>.fxlib /Fo <MyShader>.cso /Fh <MyShader>.h           

Чтобы скомпилировать полную версию шейдера со встроенной версией экспорта, необходимо передать следующие флаги в FXC.

Флаг Описание
/T <ШейдерМодель> Установите <ShaderModel> на соответствующий профиль пиксельного шейдера, как указано в синтаксисе FXC. Это должен быть профиль шейдера пикселей, соответствующий профилю связывания, указанному на шаге 1.
<MyShaderFile>.hlsl Задайте <MyShaderFile> имя файла HLSL.
/D D2D_FULL_SHADER Это определение предписывает FXC компилировать полную версию шейдера.
Запись /D D2D_ENTRY=<> Задайте <записи> имя точки входа HLSL, определенной внутри макроса D2D_PS_ENTRY().
Запись /E <> Установите для записи <и> имя точки входа HLSL, которое вы определили внутри макроса D2D_PS_ENTRY().
/setprivate <MyShaderFile>.fxlib Этот аргумент инструктирует FXC внедрить шейдер экспортной функции, созданный на шаге 1, в область D3D_BLOB_PRIVATE_DATA.
/Fo <MyShader>.cso Задайте <MyShader> на место, где вы хотите сохранить объединенный скомпилированный шейдер.
/Fh <MyShader>.h Задайте для <MyShader> место хранения окончательного объединенного заголовка.

Экспорт спецификаций функций

Возможно, хотя и не рекомендуется, создавать совместимый шейдер эффектов без использования вспомогательных средств D2D. Следует позаботиться о том, чтобы сигнатуры входных данных полного шейдера и функции экспорта соответствовали спецификациям D2D.

Спецификации полных шейдеров совпадают с более ранними версиями Windows. Кратко говоря, входные параметры шейдера пикселей должны быть SV_POSITION, SCENE_POSITION и один TEXCOORD для входных данных эффекта.

Для функции экспорта функция должна возвращать float4, а входные данные должны быть одним из следующих типов:

  • Простые входные данные

    float4 d2d_inputN : INPUTN         
    

    Для простых входных данных D2D либо вставляет функцию выборки между входной текстурой и функцией шейдера, или входные данные будут предоставлены выходными данными другой функции шейдера.

  • Сложные входные данные

    float4 d2d_uvN  : TEXCOORDN                
    

    Для сложных входных данных D2D передает только координату текстуры, как описано в документации по Windows 8.

  • Расположение выходных данных

    float4 d2d_posScene : SCENE_POSITION                
    

    Можно определить только один вход SCENE_POSITION. Этот параметр следует включать только при необходимости, так как только одна функция для связанного шейдера может использовать этот параметр.

Семантика должна быть определена как описано выше, так как D2D проверяет семантику, чтобы решить, как связать функции вместе. Если входные данные функции не соответствуют одному из указанных выше типов, функция будет отклонена для связывания шейдера.

Вспомогательные функции HLSL

интерфейс ID3D11Linker

интерфейс ID3D11FunctionLinkingGraph