Variablensyntax

Verwenden Sie die folgenden Syntaxregeln, um HLSL-Variablen zu deklarieren.

[Storage_Class] [Type_Modifier] Typname[Index] [: Semantic] [: Packoffset] [: Register]; [Anmerkungen] [= Initial_Value]

Parameter

Storage_Class

Optionale Modifizierer für Speicherklassen, die dem Compiler Hinweise zum Bereich und zur Lebensdauer von Variablen geben; die Modifizierer können in beliebiger Reihenfolge angegeben werden.

Wert Beschreibung
extern Kennzeichnen einer globalen Variablen als externe Eingabe für den Shader; dies ist die Standardmarkierung für alle globalen Variablen. Kann nicht mit static kombiniert werden.
nointerpolation Interpolieren Sie die Ausgaben eines Vertex-Shaders nicht, bevor Sie sie an einen Pixelshader übergeben.
precise Das genaue Schlüsselwort schränkt bei Anwendung auf eine Variable alle Berechnungen auf folgende Weise ein, die verwendet werden, um den Wert zu erzeugen, der dieser Variablen auf folgende Weise zugewiesen wird:
  • Separate Vorgänge werden getrennt gehalten. Wenn z. B. eine Mul- und Add-Operation zu einer Mad-Operation zusammengeführt wurden, erzwingt precise, dass die Operationen getrennt bleiben. Stattdessen müssen Sie explizit die systeminterne Mad-Funktion verwenden.
  • Die Reihenfolge der Operationen wird beibehalten. Wenn die Reihenfolge der Anweisungen möglicherweise neu angeordnet wurde, um die Leistung zu verbessern, stellt precise sicher, dass der Compiler die Reihenfolge wie geschrieben beibehält.
  • IEEE-unsichere Vorgänge werden eingeschränkt. Wenn der Compiler möglicherweise schnelle mathematische Operationen verwendet hat, die keine NaN-Werte (Not a number, keine Zahl) und INF-Werte (unendlich) berücksichtigen, erzwingt precise die IEEE-Anforderungen bezüglich NaN- und INF-Werten. Ohne precise sind diese Optimierungen und mathematischen Operationen nicht IEEE-sicher.
  • Das Qualifizieren einer Variable als precise sorgt nicht dafür, dass Operationen, die die Variable verwenden, precise sind. Da precise nur für Operationen gilt, die zu den Werten beitragen, die der als precise qualifizierten Variablen zugeordnet sind, kann die korrekte Qualifizierung der gewünschten Berechnungen als precise schwierig sein; wir empfehlen daher, die Shader-Ausgaben direkt dort, wo Sie sie deklarieren, als precise zu markieren, ob dies auf einem Strukturfeld, einem Ausgabeparameter oder dem Rückgabetyp der Eingabefunktion geschieht. Die Möglichkeit, Optimierungen auf diese Weise zu steuern, führt zu Invarianz bei der modifizierten Ausgabevariable, indem Optimierungen deaktiviert werden, die sich aufgrund von Unterschieden bei den akkumulierten Genauigkeitsdifferenzen auf die Endergebnisse auswirken können. Es ist nützlich, wenn Shader für ein Mosaikschema absolut korrekte Patch-Ränder haben sowie Tiefenwerte über mehrere Durchläufe abgleichen sollen. Beispielcode:
    HLSLmatrix g_mWorldViewProjection;
    void main(in float3 InPos : Position, out precise float4 OutPos : SV_Position)
    {
    // Vorgang ist precise, da er zum precise-Parameter OutPos beiträgt
    OutPos = mul( float4( InPos, 1.0 ), g_mWorldViewProjection );
    }
shared Markieren einer Variable für die gemeinsame Nutzung zwischen Effekten; dies ist ein Hinweis für den Compiler.
groupshared Markieren einer Variable für den von Threadgruppen gemeinsam genutzten Speicher für Compute-Shader. In D3D10 beträgt die maximale Gesamtgröße aller Variablen mit der Speicherklasse „groupshared“ 16 KB, in D3D11 beträgt die maximale Größe 32 KB. Weitere Informationen finden Sie unter Beispiele.
static Markieren einer lokalen Variable, so dass sie einmal initialisiert und zwischen Funktionsaufrufen beibehalten wird. Wenn die Deklaration keinen Initialisierer enthält, wird der Wert auf Null festgelegt. Eine als static markierte globale Variable ist für eine Anwendung nicht sichtbar.
uniform Markieren einer Variablen, deren Daten während der gesamten Ausführung eines Shaders konstant sind (z. B. eine Materialfarbe in einem Vertex-Shader); globale Variablen werden standardmäßig als uniform betrachtet.
volatile Markieren einer Variable, die sich häufig ändert; dies ist ein Hinweis für den Compiler. Dieser Speicherklassen-Modifizierer gilt nur für eine lokale Variable.
Hinweis: Der HLSL-Compiler ignoriert diesen Speicherklassen-Modifizierer derzeit.

Type_Modifier

Optionaler Modifizierer für Variablentypen.

Wert Beschreibung
const Markieren einer Variable, die von einem Shader nicht geändert werden kann und daher in der Variablendeklaration initialisiert werden muss. Globale Variablen gelten standardmäßig als const (Sie können dieses Verhalten unterdrücken, indem Sie das /Gec-Flag an den Compiler übergeben).
row_major Markieren einer Variable, die vier Komponenten in einer einzelnen Zeile speichert, damit diese in einem einzigen konstanten Register gespeichert werden können.
column_major Markieren einer Variable, die vier Komponenten in einer einzelnen Spalte speichert, um die Matrixmathematik zu optimieren.

Hinweis

Wenn Sie keinen Typmodifiziererwert angeben, verwendet der Compiler column_major als Standardwert.

Typ

Jeder HLSL-Typ, der in Data Types (DirectX HLSL) aufgeführt ist.

Name[Index]

ASCII-Zeichenfolge, die eine Shader-Variable eindeutig identifiziert. Um ein optionales Array zu definieren, verwenden Sie index für die Arraygröße, bei der es sich um eine positive ganze Zahl = 1 handelt.

Semantik

Informationen zur Verwendung optionaler Parameter, die vom Compiler verwendet werden, um Shadereingaben und -ausgaben zu verknüpfen. Es gibt mehrere vordefinierte Semantiken für Vertex- und Pixelshader. Der Compiler ignoriert die Semantik, es sei denn, sie wird für eine globale Variable oder einen Parameter deklariert, der an einen Shader übergeben wird.

Packoffset

Optionales Schlüsselwort für das manuelle Packen von Shader-Konstanten. Vgl. packoffset (DirectX HLSL).

Registrieren

Optionales Schlüsselwort zum manuellen Zuweisen einer Shader-Variablen zu einem bestimmten Register. Vgl. register (DirectX HLSL).

Anmerkung(en)

Optionale Metadaten in Form einer Zeichenfolge, die an eine globale Variable angefügt ist. Eine Anmerkung wird vom Effekt-Framework verwendet und von HLSL ignoriert; für die ausführlichere Syntax vgl. Anmerkungssyntax.

Initial_Value

Optionale(r) Anfangswert(e); die Anzahl der Werte sollte mit der Anzahl der Komponenten im Typ übereinstimmen. Jede als extern markierte globale Variable muss mit einem Literal-Wert initialisiert werden; jede als static markierte Variable muss mit einer Konstanten initialisiert werden.

Globale Variablen, die nicht als static oder extern markiert sind, werden nicht in den Shader kompiliert. Der Compiler legt nicht automatisch Standardwerte für globale Variablen fest und kann sie nicht in Optimierungen verwenden. Um globale Variablen dieses Typs zu initialisieren, verwenden Sie die Reflektion, um den Wert abzurufen, und kopieren Sie ihn dann in einen Konstantenpuffer. Sie können beispielsweise die Methode ID3D11ShaderReflection::GetVariableByName verwenden, um die Variable abzurufen, mit der Methode ID3D11ShaderReflectionVariable::GetDesc die Beschreibung der Shader-Variablen erhalten und dann den Anfangswert vom DefaultValue-Mitglied der D3D11_SHADER_VARIABLE_DESC-Struktur erhalten. Um den Wert in den Konstantenpuffer zu kopieren, müssen Sie sicherstellen, dass der Puffer mit CPU-Schreibzugriff (D3D11_CPU_ACCESS_WRITE) erstellt wurde. Weitere Informationen zum Erstellen eines Konstantenpuffers finden Sie unter Vorgehensweise beim Erstellen eines Konstantenpuffers.

Sie können auch das Effekt-Framework verwenden, um das Reflektieren und Festlegen des Anfangswerts automatisch zu verarbeiten. Sie können beispielsweise die Methode ID3DX11EffectPass::Apply verwenden.

Wichtig

Die Unterstützung für dieses Feature wurde in Direct3D 12 entfernt, einschließlich der Möglichkeit, Standard-Initialisierer zu reflektieren.

Beispiele

Hier sind mehrere Beispiele für Deklarierungen von Shader-Variablen.

float fVar;
float4 color;

int iVar[3];

uniform float4 position : SV_POSITION; 

//Default initializers; supported up to Direct3D 11.

float fVar = 3.1f;
int iVar[3] = {1,2,3};
const float4 lightDirection = {0,0,1};

Group shared

HLSL ermöglicht Threads eines Compute-Shaders den Austausch von Werten über gemeinsam genutzten Speicher. HLSL stellt Barrieregrundtypen wie GroupMemoryBarrierWithGroupSync bereit, um sicherzustellen, dass die korrekte Sortierung von Lese- und Schreibvorgängen für freigegebenen Speicher im Shader gewährleistet ist und „Data Races“ vermieden werden.

Hinweis

Die Hardware führt Threads in Gruppen (Warps oder Wave-Fronts) aus, und die Synchronisierung von Barrieren kann manchmal weggelassen werden, um die Leistung zu erhöhen, wenn nur die Synchronisierung von Threads, die derselben Gruppe angehören, korrekt ist. Aus den folgenden Gründen raten wir jedoch dringend davon ab:

  • Diese Auslassung führt zu nicht portablem Code, der möglicherweise nicht auf mancher Hardware nicht funktioniert und nicht für Software-Rasterizer funktioniert, die in der Regel Threads in kleineren Gruppen ausführen.
  • Die Leistungsverbesserungen, die Sie mit dieser Auslassung erzielen können, sind im Vergleich zur Verwendung von All-Thread-Barrieren geringfügig.

In Direct3D 10 gibt es keine Synchronisierung von Threads beim Schreiben in groupshared. Dies bedeutet, dass jeder Thread auf einen einzelnen Speicherort in einem Array zum Schreiben beschränkt ist. Verwenden Sie den Systemwert SV_GroupIndex, um beim Schreiben in dieses Array zu indizieren und so sicherzustellen, dass keine zwei Threads kollidieren können. Beim Lesen haben alle Threads Zugriff auf das gesamte Array zum Lesen.

struct GSData
{
    float4 Color;
    float Factor;
}

groupshared GSData data[5*5*1];

[numthreads(5,5,1)]
void main( uint index : SV_GroupIndex )
{
    data[index].Color = (float4)0;
    data[index].Factor = 2.0f;
    GroupMemoryBarrierWithGroupSync();
    ...
}

Verpackung

Packen Sie Teilkomponenten von Vektoren und Skalaren zusammen, deren Größe ausreicht, um das Überschreiten von Registergrenzen zu verhindern. Beispielsweise sind diese hier sämtlich gültig:

cbuffer MyBuffer
{
    float4 Element1 : packoffset(c0);
    float1 Element2 : packoffset(c1);
    float1 Element3 : packoffset(c1.y);
}

Packungstypen können nicht gemischt werden.

Wie das Register-Schlüsselwort kann ein Packoffset zielspezifisch sein. Das Packen von Unterkomponenten ist nur mit dem Schlüsselwort „packoffset“ und nicht mit dem Schlüsselwort „register“ verfügbar. Innerhalb einer cbuffer-Deklaration wird das Schlüsselwort „register“ für Direct3D 10-Ziele ignoriert, da es als für plattformübergreifende Kompatibilität geltend betrachtet wird.

Gepackte Elemente können sich überlappen, und der Compiler gibt keinen Fehler und keine Warnung aus. In diesem Beispiel überlappen sich Element2 und Element3 mit Element1.x und Element1.y.

cbuffer MyBuffer
{
    float4 Element1 : packoffset(c0);
    float1 Element2 : packoffset(c0);
    float1 Element3 : packoffset(c0.y);
}

Ein Beispiel, das „packoffset“ verwendet, ist: HLSLWithoutFX10-Beispiel.