Files
hms-mediaengine/docs/plugin-sdk/shader-contract.md
T
HMS MediaEngine Agent 985f156525 Phase 3: Plugin-SDK-Dokumentation (docs/plugin-sdk/, 7 Dateien)
- README: Pluginarten, Verzeichnisstruktur, Quick Start (§14.1/§14.2)
- manifest-reference: komplette plugin.json-Feldreferenz mit
  Validator-Regeln und ZIP-Sicherheit (§27.2)
- shader-contract: Standard-Uniformsatz je Backend, cbuffer-Layout,
  16-Byte-Alignment, Multipass, AQ-Bindung, GLES-Einschraenkungen (§14.4)
- parameters-and-dmx: dmx_slots max 8 (§14.7), Kurven, 16-Bit-Werte,
  gemeinsamer Effektvertrag mix/blend/quality (§14.8)
- lifecycle: Zustandsmaschine, Quarantaene, Show-Lock (§14.5/§26.3)
- adaptive-quality: Varianten, Semantik-Erhalt, Kompilierung vor
  Aktivierung, Framegrenzen-Wechsel (§5.2)
- example-walkthrough: komplettes Filter-Plugin anhand vignette
- 1199 Zeilen, Deutsch, alle Beispiele aus echten Repo-Dateien
2026-09-11 01:57:30 +02:00

7.8 KiB
Raw Blame History

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:

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:

#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:

#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:

"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:

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

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:

float m = saturate(param_mix);
if (m < 0.01)
{
    return src; // Mix 0 = kostenloser Bypass (§15.3)
}
float m = clamp(param_mix, 0.0, 1.0);
if (m < 0.01)
{
    fragColor = src;
    return;
}