# Shader-Vertrag Dieses Dokument beschreibt den verbindlichen Shader-Vertrag für Plugin-Shader. Grundlage sind PLAN.md §14.4 (Standard-Shaderinputs) und die eingebauten Beispiel-Shader des Vignette-Plugins (`plugins/builtin/filters/com.hms.fx.vignette/shaders/`). ## Standard-Uniform-Satz (§14.4) Jeder Shader erhält nach Bedarf folgende semantische Inputs: | Semantische ID | Typ | Beschreibung | | --- | --- | --- | | `u_input_texture` | Textur | Eingangs-Textur (bei Filtern) | | `u_resolution` | vec2/float4 | Auflösung in Pixeln (`xy`) | | `u_time_seconds` | float | Zeit in Sekunden | | `u_delta_seconds` | float | Zeit seit dem letzten Frame | | `u_frame_index` | float | Frame-Index | | `u_layer_opacity` | float | Opazität des Layers | | `u_audio_rms` | float | Audio-RMS | | `u_audio_peak` | float | Audio-Peak | | `u_audio_bass` | float | Audio-Bass | | `u_audio_mid` | float | Audio-Mid | | `u_audio_treble` | float | Audio-Treble | | `u_audio_beat` | float | Audio-Beat | | deklarierte Pluginparameter | float | Parameter aus `plugin.json` | Semantische Input-IDs, Typen, Wertebereiche, Farbräume und Texturkonventionen sind Teil der versionierten Plugin-API. Backendadapter binden sie an HLSL-Konstanten beziehungsweise GLSL-Uniforms; Projekte referenzieren niemals konkrete Variablennamen eines Backends (PLAN.md §14.4). ## HLSL (D3D11) – cbuffer-Layout Im HLSL-Backend liegen die Standard-Uniforms und Pluginparameter in einem Konstantenpuffer (`cbuffer`). Das Vignette-Beispiel (`shaders/d3d11/main.hlsl`) zeigt das Layout: ```hlsl Texture2D u_input_texture : register(t0); SamplerState u_sampler : register(s0); cbuffer hms_params : register(b0) { float4 u_resolution; // xy = Auflösung in Pixeln float u_time_seconds; float u_delta_seconds; float u_frame_index; float u_layer_opacity; float u_audio_rms; float u_audio_peak; float u_audio_bass; float u_audio_mid; float u_audio_treble; float u_audio_beat; float param_amount; float param_radius; float param_softness; float param_roundness; float param_center_x; float param_center_y; float param_color_r; float param_color_g; float param_color_b; float param_invert; // 0 = abdunkeln, 1 = aufhellen float param_mix; float _pad0; float _pad1; float _pad2; float _pad3; }; ``` ### 16-Byte-Alignment HLSL-Konstantenpuffer werden in 16-Byte-Registern (`float4`) organisiert. Ein `float4` belegt ein Register; einzelne `float`-Werte werden in 16-Byte-Blöcken zusammengefasst. Das Beispiel füllt den Puffer mit `_pad0` bis `_pad3` auf ein Vielfaches von 16 Byte auf, damit das Layout deterministisch bleibt. ### Textur- und Sampler-Bindung - `u_input_texture` ist an `register(t0)` gebunden. - Der Sampler `u_sampler` ist an `register(s0)` gebunden. - Der Konstantenpuffer `hms_params` ist an `register(b0)` gebunden. ## GLSL (OpenGL, Linux x64) Das GL-Backend (`shaders/gl/main.frag`) deklariert die Uniforms einzeln: ```glsl #version 330 core uniform sampler2D u_input_texture; uniform vec2 u_resolution; uniform float u_time_seconds; uniform float u_delta_seconds; uniform float u_frame_index; uniform float u_layer_opacity; uniform float u_audio_rms; uniform float u_audio_peak; uniform float u_audio_bass; uniform float u_audio_mid; uniform float u_audio_treble; uniform float u_audio_beat; uniform float param_amount; uniform float param_radius; uniform float param_softness; uniform float param_roundness; uniform float param_center_x; uniform float param_center_y; uniform float param_color_r; uniform float param_color_g; uniform float param_color_b; uniform float param_invert; uniform float param_mix; in vec2 v_uv; out vec4 fragColor; void main() { vec4 src = texture(u_input_texture, v_uv); // ... fragColor = out_c; } ``` ## GLES (OpenGL ES 2.0, Raspberry Pi) Das GLES-Backend (`shaders/gles/main.frag`) folgt ES-2.0-Regeln: ```glsl #version 100 precision mediump float; uniform sampler2D u_input_texture; uniform vec2 u_resolution; uniform float u_time_seconds; uniform float u_delta_seconds; uniform float u_frame_index; uniform float u_layer_opacity; uniform float u_audio_rms; uniform float u_audio_peak; uniform float u_audio_bass; uniform float u_audio_mid; uniform float u_audio_treble; uniform float u_audio_beat; uniform float param_amount; uniform float param_radius; uniform float param_softness; uniform float param_roundness; uniform float param_center_x; uniform float param_center_y; uniform float param_color_r; uniform float param_color_g; uniform float param_color_b; uniform float param_invert; uniform float param_mix; varying vec2 v_uv; void main() { vec4 src = texture2D(u_input_texture, v_uv); // ... gl_FragColor = out_c; } ``` ## Backend-Syntaxunterschiede Die drei Backends müssen semantisch identische Ergebnisse liefern (PLAN.md §12.6, §15.4). Die wichtigsten Syntaxunterschiede: | Aspekt | HLSL (D3D11) | GLSL (OpenGL) | GLES (ES 2.0) | | --- | --- | --- | --- | | Version | `#version` nicht nötig | `#version 330 core` | `#version 100` | | Texturzugriff | `u_input_texture.Sample(u_sampler, uv)` | `texture(u_input_texture, v_uv)` | `texture2D(u_input_texture, v_uv)` | | Ausgabe | `return float4(...)` an `SV_Target` | `fragColor = ...` | `gl_FragColor = ...` | | Eingabe | `float2 uv : TEXCOORD0` | `in vec2 v_uv` | `varying vec2 v_uv` | | Clamp | `saturate(x)` | `clamp(x, 0.0, 1.0)` | `clamp(x, 0.0, 1.0)` | | Mischen | `lerp(a, b, t)` | `mix(a, b, t)` | `mix(a, b, t)` | | Präzision | – | – | `precision mediump float;` erforderlich | ### GLES-spezifische Regeln - **Konstante Loop-Grenzen:** GLES 2.0 erlaubt nur Schleifen mit konstanten Grenzen. Dynamische Schleifengrenzen sind nicht zulässig. - **`texture2D` statt `texture`:** In ES 2.0 wird `texture2D` verwendet. - **Kein `atan2`:** Die Funktion `atan2` ist in ES 2.0 nicht verfügbar; verwende `atan(y, x)` mit zwei Argumenten oder baue die Logik selbst. - **`precision`-Deklaration:** `precision mediump float;` muss vor den Uniforms stehen. - **`varying` statt `in`/`out`:** ES 2.0 verwendet `varying` für Vertex-zu-Fragment-Daten. ## Multipass-Regeln Multipass-Effekte (z. B. Gaussian Blur, PLAN.md §14.3) deklarieren mehrere Pässe im Manifest: ```json "entrypoints": { "d3d11": { "type": "hlsl_multipass", "passes": [ {"pixel_shader": "shaders/d3d11/horizontal.hlsl"}, {"pixel_shader": "shaders/d3d11/vertical.hlsl"} ] } } ``` - Jeder Pass ist ein eigener Shader mit eigenem Dateipfad. - Die Pässe werden in der deklarierten Reihenfolge ausgeführt; der Ausgang eines Passes ist der Eingang des nächsten. - Jeder Pass erhält denselben Standard-Uniform-Satz. - Die Semantik muss über alle Backends identisch sein. ## Adaptive-Quality-Varianten-Bindung Adaptive-Quality-Varianten (PLAN.md §14.3, §15.4) können die interne Auflösung (`internal_scale`) und die Sample-Anzahl (`samples`) ändern. Die Bindung erfolgt über die deklarierten Varianten im Manifest: ```json "adaptive_quality": { "default": "auto", "variants": [ {"id": "low", "internal_scale": 0.25, "samples": 5}, {"id": "medium", "internal_scale": 0.5, "samples": 9}, {"id": "high", "internal_scale": 1.0, "samples": 17} ] } ``` Die Engine bindet die Variantenwerte an die Shader-Uniforms. Die Parameterwerte bleiben beim Wechsel der Auto-Qualitätsstufe semantisch identisch (PLAN.md §15.3). Details siehe [adaptive-quality.md](adaptive-quality.md). ## Mix-0-Bypass (§15.3) Jeder Effekt besitzt einen `mix`-Parameter. `Mix 0` muss den Effekt kostengünstig bypassen. Das Vignette-Beispiel zeigt das Muster: ```hlsl float m = saturate(param_mix); if (m < 0.01) { return src; // Mix 0 = kostenloser Bypass (§15.3) } ``` ```glsl float m = clamp(param_mix, 0.0, 1.0); if (m < 0.01) { fragColor = src; return; } ```