Effektshader-Verknüpfung

Direct2D verwendet eine Optimierung namens Effektshaderverknüpfung, die mehrere Effektgraph-Renderingdurchgänge in einen einzelnen Durchlauf kombiniert.

Übersicht über die Verknüpfung von Effekt-Shadern

Die Optimierungen zum Verknüpfen von Effekt-Shadern bauen auf der HLSL-Shaderverknüpfung auf, einem Direct3D 11.2-Feature, mit dem Pixel- und Vertex-Shader zur Laufzeit generiert werden können, indem vorkompilierte Shaderfunktionen verknüpft werden. Die folgenden Abbildungen veranschaulichen das Konzept der Effektshaderverknüpfung in einem Effektdiagramm. Die erste Abbildung zeigt ein typisches Direct2D-Effektdiagramm mit vier Renderingtransformationen. Ohne Shaderverknüpfung benötigt jede Transformation einen Renderingdurchlauf und erfordert eine Zwischenoberfläche. Insgesamt erfordert dieses Diagramm 4 Durchläufe und 3 Zwischenläufe.

Transformationsdiagramm ohne Shaderverknüpfung: 4 Durchläufe und 3 Zwischenläufe.

Die zweite Abbildung zeigt das gleiche Effektdiagramm, bei dem jede Renderingtransformation durch eine verlinkbare Funktionsversion ersetzt wurde. Direct2D kann das gesamte Diagramm verknüpfen und in einem Durchgang ausführen, ohne Zwischenschritte zu benötigen. Dies kann zu einer erheblichen Verringerung der GPU-Ausführungszeit und einer Verringerung der Spitzenauslastung des GPU-Arbeitsspeichers führen.

Transformationsdiagramm mit Shaderverknüpfung: 1 Durchlauf, 0 Zwischenschritte.

 

Effektshaderverknüpfung funktioniert auf einzelnen Transformationen innerhalb eines Effekts. Dies bedeutet, dass auch ein Graph mit einem einzelnen Effekt von der Shaderverknüpfung profitieren kann, wenn dieser Effekt mehrere gültige Transformationen aufweist.

Verwenden der Effektshaderverknüpfung

Wenn Sie eine Direct2D-Anwendung erstellen, die Effekte verwendet, müssen Sie nichts tun, um die Wirkungsshaderverknüpfung zu nutzen. Direct2D analysiert automatisch das Effektdiagramm, um die optimale Methode zum Verknüpfen der einzelnen Transformationen zu ermitteln.

Effektautoren sind dafür verantwortlich, ihre Wirkung so zu implementieren, dass die Verknüpfung von Effekt-Shadern unterstützt wird. Weitere Informationen finden Sie weiter unten im Abschnitt Erstellen eines shaderkompatiblen benutzerdefinierten Effekts . Alle integrierten Effekte unterstützen die Shaderverknüpfung.

Direct2D verknüpft benachbarte Renderingtransformationen nur in Situationen, in denen dies vorteilhaft ist. Bei der Entscheidung, ob zwei Transformationen verknüpft werden sollen, werden mehrere Faktoren berücksichtigt. Beispielsweise wird die Shaderverknüpfung nicht ausgeführt, wenn eine der Transformationen Vertex- oder Compute-Shader verwendet, da nur Pixel-Shader verknüpft werden können. Wenn ein Effekt nicht so erstellt wurde, dass er mit Shaderverknüpfungen kompatibel ist, werden umgebende Transformationen nicht damit verknüpft.

Für den Fall, dass eine solche Verknüpfungsgefahr besteht, verknüpft Direct2D keine Transformationen neben der Gefahr, sondern versucht trotzdem, den Rest des Diagramms zu verknüpfen.

Transformationsdiagramm mit Verknüpfungsgefahr: 2 Durchläufe, 1 Zwischenschritt.

Erstellen eines benutzerdefinierten Effekts mit Shaderverknüpfung

Wenn Sie einen eigenen benutzerdefinierten Direct2D-Effekt erstellen, müssen Sie sicherstellen, dass seine Transformationen die Verknüpfung von Effektshader unterstützen. Dies erfordert einige geringfügige Änderungen an der Implementierung früherer benutzerdefinierter Effekte. Wenn eine Transformation innerhalb Ihres benutzerdefinierten Effekts keine Shaderverknüpfung unterstützt, verknüpft Direct2D sie nicht mit Transformationen, die im Effektdiagramm an ihn angrenzen.

Als benutzerdefinierter Effektautor sollten Sie sich über mehrere wichtige Konzepte und Anforderungen bewusst sein:

  • Keine Änderungen an Schnittstellenimplementierungen

    Sie müssen keinen Code ändern, der die verschiedenen Effektschnittstellen wie ID2D1DrawTransform implementiert.

  • Bereitstellen einer vollständigen und einer Exportfunktionsversion von Shadern

    Sie müssen eine Exportfunktionsversion der Shader Ihres Effekts bereitstellen, die mit Direct2D verknüpft werden können. Darüber hinaus müssen Sie weiterhin den ursprünglichen vollständigen Shader bereitstellen. Dies liegt daran, dass Direct2D zur Laufzeit die richtige Shaderversion auswählt, je nachdem, ob die Shaderverknüpfung auf einen bestimmten Link im Diagramm angewendet werden soll.

    Wenn eine Transformation nur das vollständige Pixel-Shaderblob bereitstellt (über ID2D1EffectContext::LoadPixelShader), wird sie nicht mit benachbarten Transformationen verknüpft.

  • Hilfsfunktionen

    Direct2D bietet HLSL-Hilfsfunktionen und -makros, die automatisch sowohl die vollständige als auch die Exportfunktionsversion eines Shaders generieren. Diese Helfer finden Sie unter d2d1effecthelpers.hlsli. Darüber hinaus können Sie mit dem HLSL-Compiler (FXC) den Exportfunktions-Shader in ein privates Feld im vollständigen Shader einfügen. Auf diese Weise müssen Sie nur einmal einen Shader erstellen und beide Versionen gleichzeitig an Direct2D übergeben. Sowohl d2d1effecthelpers.hlsli als auch der FXC-Compiler sind als Teil des Windows SDK enthalten.

    Die Hilfsfunktionen:

    Sie können auch zwei Versionen jedes Shaders manuell erstellen und zweimal kompilieren, solange die unten unter Exportfunktionsspezifikationen beschriebenen Spezifikationen erfüllt sind.

  • Nur Pixel-Shader

    Direct2D unterstützt keine Verknüpfung von Compute- oder Vertex-Shadern. Wenn Ihr Effekt jedoch sowohl einen Vertex- als auch einen Pixel-Shader verwendet, kann die Ausgabe des Pixel-Shaders weiterhin verknüpft werden.

  • Einfache und komplexe Stichprobenentnahme

    Das Verknüpfen der Shaderfunktion funktioniert, indem die Ausgabe eines Pixel-Shaderdurchlaufs mit der Eingabe eines nachfolgenden Pixel-Shaderdurchlaufs verbunden wird. Dies ist nur möglich, wenn der nutzende Pixel-Shader nur einen einzelnen Eingabewert benötigt, um seine Berechnung durchzuführen. Dieser Wert stammt normalerweise aus dem Sampling einer Eingabetextur an der Texturkoordinate, die vom Vertex-Shader ausgegeben wird. Ein solcher Pixel-Shader soll eine einfache Stichprobenentnahme durchführen.

    Die Graustufenkonvertierung ist ein Beispiel für einfache Stichprobenerstellung. Der Wert eines bestimmten Ausgabepixels hängt nur vom Wert des entsprechenden Eingabepixels ab.

    Einige Pixel-Shader, z. B. eine gaußsche Unschärfe, berechnen ihre Ausgabe aus mehreren Eingabebeispielen und nicht nur aus einem einzelnen Beispiel. Ein solcher Pixel-Shader soll eine komplexe Stichprobenentnahme durchführen.

    Gausssche Unschärfe ist ein Beispiel für komplexe Stichprobenentnahmen. Der Wert des mittleren Ausgabepixels hängt von mehreren Eingabepixeln ab.

    Nur Shaderfunktionen mit einfachen Eingaben können ihre Eingaben von einer anderen Shaderfunktion bereitstellen. Shaderfunktionen mit komplexen Eingaben müssen zum Beispiel mit einer Eingabetextur versehen werden. Dies bedeutet, dass Direct2D einen Shader nicht mit komplexen Eingaben mit seinem Vorgänger verknüpft.

    Wenn Sie die Direct2D HLSL-Hilfsprogramme verwenden, müssen Sie in der HLSL angeben, ob ein Shader komplexe oder einfache Eingaben verwendet.

Beispiel für einen verknüpfungskompatiblen Effektshader

Mithilfe der D2D-Hilfsprogramme stellt der folgende Codeausschnitt einen einfachen verknüpfungskompatiblen Effektshader dar:

#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;
}          

Beachten Sie in diesem kurzen Beispiel, dass keine Funktionsparameter deklariert werden, dass die Anzahl der Eingaben und der Typ jeder Eingabe vor der Eingabefunktion deklariert werden, dass die Eingabe durch Aufrufen von D2DGetInput abgerufen wird und dass Präprozessordirektiven definiert werden müssen, bevor die Hilfsdatei enthalten ist.

Ein verknüpfungskompatibler Shader muss sowohl einen regulären Single-Pass-Pixel-Shader als auch eine Export-Shaderfunktion bereitstellen. Das D2D_PS_ENTRY Makro ermöglicht es, dass jede dieser Elemente aus demselben Code generiert wird, wenn es in Verbindung mit dem Kompilierungsskript für Shader verwendet wird.

Beim Kompilieren eines vollständigen Shaders werden die Makros in den folgenden Code erweitert, der über eine D2D-Effekte-kompatible Eingabesignatur verfügt.

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;
    }    

Beim Kompilieren einer Exportfunktionsversion desselben Codes wird der folgende Code generiert:

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

Beachten Sie, dass die Textureingabe, die normalerweise durch Sampling einer Textur2D abgerufen wird, durch eine Funktionseingabe (input0) ersetzt wurde.

Eine vollständige, schrittweise Beschreibung der Schritte, die Sie zum Schreiben eines verknüpfungskompatiblen Effekts ausführen müssen, finden Sie im Tutorial Benutzerdefinierte Effekte und im Beispiel für benutzerdefinierte Direct2D-Bildeffekte.

Kompilieren eines verknüpfungskompatiblen Shaders

Um verlinkbar zu sein, muss das an D2D übergebene Pixel-Shaderblob sowohl die vollständige als auch die Exportfunktionsversion des Shaders enthalten. Dies wird erreicht, indem die kompilierte Exportfunktion in den D3D_BLOB_PRIVATE_DATA Bereich eingebettet wird.

Wenn die Shader mit den D2D-Hilfsfunktionen erstellt werden, muss zur Kompilierungszeit ein D2D-Kompilierungsziel definiert werden. Die Kompilierungszieltypen sind D2D_FULL_SHADER und D2D_FUNCTION.

Das Kompilieren eines verknüpfungskompatiblen Effektshaders ist ein zweistufiger Prozess:

Hinweis

Wenn Sie einen Effekt mithilfe von Visual Studio kompilieren, sollten Sie eine Batchdatei erstellen, die beide FXC-Befehle ausführt, und diese Batchdatei als benutzerdefinierten Buildschritt ausführen, der vor dem Kompilierungsschritt ausgeführt wird.

 

Schritt 1: Kompilieren der Exportfunktion

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

Um die Exportfunktionsversion Ihres Shaders zu kompilieren, müssen Sie die folgenden Flags an FXC übergeben.

Flag Beschreibung
/T <ShaderModel> Legen Sie <ShaderModel> auf das entsprechende Pixel-Shaderprofil fest, wie in FXC Syntax definiert. Dies muss eines der Profile sein, die unter "HLSL-Shaderverknüpfung" aufgeführt sind.
<MyShaderFile.hlsl> Legen Sie MyShaderFile> auf den Namen der HLSL-Datei fest<.
/D D2D_FUNCTION Diese Definition weist FXC an, die Exportfunktionsversion des Shaders zu kompilieren.
/D D2D_ENTRY=<entry> Legen Sie <den Eintrag> auf den Namen des HLSL-Einstiegspunkts fest, den Sie innerhalb des D2D_PS_ENTRY Makros definiert haben.
/Fl <MyShaderFile.fxlib> Legen Sie <MyShaderfile> auf den Speicherort fest, an dem Sie die Exportfunktionsversion des Shaders speichern möchten. Beachten Sie, dass die Erweiterung .fxlib nur zur einfacheren Identifizierung dient.

Schritt 2: Kompilieren des vollständigen Shaders und Einbetten der Exportfunktion

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           

Um die Vollversion Ihres Shaders mit eingebetteter Exportversion zu kompilieren, müssen Sie die folgenden Flags an FXC übergeben.

Flag Beschreibung
/T <ShaderModel> Legen Sie <ShaderModel> auf das entsprechende Pixel-Shaderprofil fest, wie in FXC Syntax definiert. Dies muss das Pixel-Shaderprofil sein, das dem in Schritt 1 angegebenen Verknüpfungsprofil entspricht.
<MyShaderFile.hlsl> Legen Sie MyShaderFile> auf den Namen der HLSL-Datei fest<.
/D D2D_FULL_SHADER Diese Definition weist FXC an, die Vollversion des Shaders zu kompilieren.
/D D2D_ENTRY=<entry> Legen Sie den Eintrag> auf den Namen des HLSL-Einstiegspunkts fest<, den Sie im Makro D2D_PS_ENTRY() definiert haben.
Eintrag "/E <"> Legen Sie den Eintrag> auf den Namen des HLSL-Einstiegspunkts fest<, den Sie im Makro D2D_PS_ENTRY() definiert haben.
/setprivate <MyShaderFile.fxlib> Dieses Argument weist FXC an, den in Schritt 1 generierten Exportfunktionsshader in den D3D_BLOB_PRIVATE_DATA Bereich einzubetten.
/Fo <MyShader.cso> Legen Sie <MyShader> auf den Speicherort für den endgültigen, kombinierten kompilierten Shader fest.
/Fh <MyShader.h> Legen Sie <MyShader> auf fest, wo Sie den endgültigen kombinierten Header speichern möchten.

Exportieren von Funktionsspezifikationen

Es ist möglich – aber nicht empfohlen –, einen kompatiblen Effekt-Shader zu erstellen, ohne die von D2D bereitgestellten Hilfsprogramme zu verwenden. Es muss darauf geachtet werden, dass sowohl die vollständigen Shader- als auch die Exportfunktionseingabesignaturen den D2D-Spezifikationen entsprechen.

Die Spezifikationen für vollständige Shader sind identisch mit früheren Windows-Versionen. Kurz gesagt, die Pixel-Shader-Eingabeparameter müssen SV_POSITION, SCENE_POSITION und ein TEXCOORD pro Effekteingabe sein.

Für die Exportfunktion muss die Funktion einen float4 zurückgeben, und ihre Eingaben müssen einer der folgenden Typen sein:

  • Einfache Eingabe

    float4 d2d_inputN : INPUTN         
    

    Bei einfachen Eingaben fügt D2D entweder eine Sample-Funktion zwischen der Eingabetextur und der Shaderfunktion ein, oder die Eingabe wird von der Ausgabe einer anderen Shaderfunktion bereitgestellt.

  • Komplexe Eingabe

    float4 d2d_uvN  : TEXCOORDN                
    

    Bei komplexen Eingaben übergibt D2D nur eine Texturkoordinate, wie in Windows 8 Dokumentation beschrieben.

  • Ausgabeverzeichnis

    float4 d2d_posScene : SCENE_POSITION                
    

    Es kann nur eine SCENE_POSITION Eingabe definiert werden. Dieser Parameter sollte nur bei Bedarf eingeschlossen werden, da nur eine Funktion pro verknüpftem Shader diesen Parameter verwenden kann.

Die Semantik muss wie oben definiert werden, da D2D die Semantik untersucht, um zu entscheiden, wie Funktionen miteinander verknüpft werden sollen. Wenn eine Funktionseingabe nicht mit einem der oben genannten Typen übereinstimmt, wird die Funktion für die Shaderverknüpfung abgelehnt.

HLSL-Hilfsprogramme

ID3D11Linker-Schnittstelle

ID3D11FunctionLinkingGraph-Schnittstelle