Der Nutzer hat recht: Der Ordner war voller Entwicklungs-Muell. Jetzt ist sauber getrennt: ROOT (was der Nutzer sieht und braucht): - run.py = das Programm - hms_app/ = der Anwendungscode - HMS MediaEngine.app = macOS Doppelklick-Starter - HMS-Start.vbs = Windows Doppelklick-Starter - HMS-Install.vbs = Windows Erst-Installation - HMS-Mac-Install.command = macOS Homebrew-Installation - HMS-Portable-Install.command = macOS Portable-Installation (16GB-Fix) - installer_gui.py = grafischer Installer - launcher.pyw + launcher_core.py = interne Start-Logik - LIESMICH.txt = 10-Zeilen-Kurzanleitung - .gitignore _entwicklung/ (alles andere, NICHT benoetigt): - packages/ apps/ native/ plugins/ tools/ schemas/ tests/ docs/ build/ fixture_profiles/ - PLAN.md STATUS.md ERRORS.md TEST_REPORT.md CHANGELOG.md README.md - pyproject.toml uv.lock setup_*.sh/ps1 make_mac_app.py Diese Trennung gilt ab sofort fuer alle Commits. Der Nutzer kann _entwicklung/ loeschen wenn er Platz braucht - die App laeuft ohne. Verifiziert: App startet nach Aufraeumen unveraendert (Health 200).
7.8 KiB
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_textureist anregister(t0)gebunden.- Der Sampler
u_samplerist anregister(s0)gebunden. - Der Konstantenpuffer
hms_paramsist anregister(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.
texture2Dstatttexture: In ES 2.0 wirdtexture2Dverwendet.- Kein
atan2: Die Funktionatan2ist in ES 2.0 nicht verfügbar; verwendeatan(y, x)mit zwei Argumenten oder baue die Logik selbst. precision-Deklaration:precision mediump float;muss vor den Uniforms stehen.varyingstattin/out: ES 2.0 verwendetvaryingfü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;
}