feat(host): vendor PyroWave + minimal Granite subset as crates/pyrowave-sys

Phase 0 of design/pyrowave-codec-plan.md — the opt-in wired-LAN ultra-low-
latency codec. Vendored at upstream 509e4f88 (API 0.4.0, Granite 44362775,
volk + vulkan-headers pins in PUNKTFUNK-VENDOR.txt), pruned to the 6.6 MB
the standalone no-renderer build needs; scripts/vendor-pyrowave.sh
reproduces the tree (a pin bump is protocol-affecting, plan §4.2).

build.rs drives the wrapper CMakeLists (static archives incl. a static
C-API lib upstream only ships shared) + bindgen over pyrowave.h; Linux and
Windows only, empty stub elsewhere (Apple gets a native Metal port, §4.7).
Offline-safe by construction: no network, no system lib, vendored Vulkan
headers — same model as the opus dep (flatpak builder has no network).

Phase-0 validation on .21 (RTX 5070 Ti, driver 610.43.03):
- upstream pyrowave-c-test + interop test (incl. dmabuf/DRM-modifier
  Vulkan<->Vulkan) pass, from the pristine AND the pruned tree
- GPU kernel times at ~1.6 bpp noise: encode/decode 0.090/0.042 ms @800p,
  0.146/0.067 @1080p, 0.226/0.103 @1440p, 0.477/0.201 @4K — order of
  magnitude under NVENC's 1-2 ms retrieve, CBR lands within ~100 B of
  target
- cargo test -p pyrowave-sys green (static link + API-version pin check)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-15 00:35:10 +02:00
parent 1b73361372
commit 4c3b11445c
396 changed files with 140058 additions and 0 deletions
+252
View File
@@ -0,0 +1,252 @@
#version 450
// Copyright (c) 2025 Hans-Kristian Arntzen
// SPDX-License-Identifier: MIT
#extension GL_EXT_control_flow_attributes : require
layout(location = 0) in vec2 vUV;
layout(location = 0, component = 2) in float vIntCoord;
#if CHROMA_CONFIG == 0
#define OUTPUT_PLANES 1
#define INPUT_PLANES 1
#elif CHROMA_CONFIG == 1
#define OUTPUT_PLANES 2
#define INPUT_PLANES 3
#elif CHROMA_CONFIG == 2
#define OUTPUT_PLANES 3
#define INPUT_PLANES 2
#else
#error "Invalid chroma config"
#endif
layout(location = 0) out mediump float oY;
#if OUTPUT_PLANES == 2
layout(location = 1) out mediump vec2 oCbCr;
#elif OUTPUT_PLANES == 3
layout(location = 1) out mediump float oCb;
layout(location = 2) out mediump float oCr;
#endif
layout(set = 0, binding = 0) uniform mediump texture2D uYEven;
layout(set = 0, binding = 1) uniform mediump texture2D uYOdd;
layout(set = 0, binding = 2) uniform mediump sampler uSampler;
#if INPUT_PLANES == 3
layout(set = 0, binding = 3) uniform mediump texture2D uCbEven;
layout(set = 0, binding = 4) uniform mediump texture2D uCbOdd;
layout(set = 0, binding = 5) uniform mediump texture2D uCrEven;
layout(set = 0, binding = 6) uniform mediump texture2D uCrOdd;
#elif INPUT_PLANES == 2
layout(set = 0, binding = 3) uniform mediump texture2D uCbCrEven;
layout(set = 0, binding = 4) uniform mediump texture2D uCbCrOdd;
#endif
// Direct and naive implementing of the CDF 9/7 synthesis filters.
// Optimized for the mobile GPUs which don't have any
// competent compute/shared memory performance whatsoever,
// i.e. anything not AMD/NV/Intel.
layout(constant_id = 0) const bool VERTICAL = false;
layout(constant_id = 1) const bool FINAL_Y = false;
layout(constant_id = 2) const bool FINAL_CBCR = false;
layout(constant_id = 3) const int EDGE_CONDITION = 0;
const ivec2 OFFSET_M2 = VERTICAL ? ivec2(0, 0) : ivec2(0, 0);
const ivec2 OFFSET_M1 = VERTICAL ? ivec2(0, 1) : ivec2(1, 0);
const ivec2 OFFSET_C = VERTICAL ? ivec2(0, 2) : ivec2(2, 0);
const ivec2 OFFSET_P1 = VERTICAL ? ivec2(0, 3) : ivec2(3, 0);
const ivec2 OFFSET_P2 = VERTICAL ? ivec2(0, 4) : ivec2(4, 0);
const float SYNTHESIS_LP_0 = 1.11508705;
const float SYNTHESIS_LP_1 = 0.591271763114;
const float SYNTHESIS_LP_2 = -0.057543526229;
const float SYNTHESIS_LP_3 = -0.091271763114;
const float SYNTHESIS_HP_0 = 0.602949018236;
const float SYNTHESIS_HP_1 = -0.266864118443;
const float SYNTHESIS_HP_2 = -0.078223266529;
const float SYNTHESIS_HP_3 = 0.016864118443;
const float SYNTHESIS_HP_4 = 0.026748757411;
layout(push_constant) uniform Registers
{
vec2 uv_offset;
vec2 half_texel_offset;
float res_scale;
int aligned_transform_size;
};
float[10] sample_component_gather(mediump texture2D tex_even, mediump texture2D tex_odd)
{
float components[10];
vec2 gather_uv = vUV + half_texel_offset;
vec2 even0, even1, odd0, odd1;
if (VERTICAL)
{
even0 = textureGatherOffset(sampler2D(tex_even, uSampler), gather_uv, OFFSET_M1).wx;
even1 = textureGatherOffset(sampler2D(tex_even, uSampler), gather_uv, OFFSET_P1).wx;
odd0 = textureGatherOffset(sampler2D(tex_odd, uSampler), gather_uv, OFFSET_M2).wx;
odd1 = textureGatherOffset(sampler2D(tex_odd, uSampler), gather_uv, OFFSET_C).wx;
}
else
{
even0 = textureGatherOffset(sampler2D(tex_even, uSampler), gather_uv, OFFSET_M1).wz;
even1 = textureGatherOffset(sampler2D(tex_even, uSampler), gather_uv, OFFSET_P1).wz;
odd0 = textureGatherOffset(sampler2D(tex_odd, uSampler), gather_uv, OFFSET_M2).wz;
odd1 = textureGatherOffset(sampler2D(tex_odd, uSampler), gather_uv, OFFSET_C).wz;
}
components[0] = 0.0;
components[1] = odd0.x;
components[2] = even0.x;
components[3] = odd0.y;
components[4] = even0.y;
components[5] = odd1.x;
components[6] = even1.x;
components[7] = odd1.y;
components[8] = even1.y;
components[9] = textureLodOffset(sampler2D(tex_odd, uSampler), vUV, 0.0, OFFSET_P2).x;
return components;
}
vec2[10] sample_component_gather2(mediump texture2D tex_even, mediump texture2D tex_odd)
{
vec2 components[10];
// Little point in using gather here, at least for now.
components[0] = vec2(0.0);
components[1] = textureLodOffset(sampler2D(tex_odd, uSampler), vUV, 0.0, OFFSET_M2).xy;
components[2] = textureLodOffset(sampler2D(tex_even, uSampler), vUV, 0.0, OFFSET_M1).xy;
components[3] = textureLodOffset(sampler2D(tex_odd, uSampler), vUV, 0.0, OFFSET_M1).xy;
components[4] = textureLodOffset(sampler2D(tex_even, uSampler), vUV, 0.0, OFFSET_C).xy;
components[5] = textureLodOffset(sampler2D(tex_odd, uSampler), vUV, 0.0, OFFSET_C).xy;
components[6] = textureLodOffset(sampler2D(tex_even, uSampler), vUV, 0.0, OFFSET_P1).xy;
components[7] = textureLodOffset(sampler2D(tex_odd, uSampler), vUV, 0.0, OFFSET_P1).xy;
components[8] = textureLodOffset(sampler2D(tex_even, uSampler), vUV, 0.0, OFFSET_P2).xy;
components[9] = textureLodOffset(sampler2D(tex_odd, uSampler), vUV, 0.0, OFFSET_P2).xy;
return components;
}
void main()
{
bool is_odd = (int(vIntCoord) & 1) != 0;
float Y[10] = sample_component_gather(uYEven, uYOdd);
#if INPUT_PLANES == 2
vec2 CbCr[10] = sample_component_gather2(uCbCrEven, uCbCrOdd);
#elif INPUT_PLANES == 3
float Cb[10] = sample_component_gather(uCbEven, uCbOdd);
float Cr[10] = sample_component_gather(uCrEven, uCrOdd);
vec2 CbCr[10];
[[unroll]]
for (int i = 0; i < 10; i++)
CbCr[i] = vec2(Cb[i], Cr[i]);
#endif
if (EDGE_CONDITION < 0)
{
// The mirroring rules are particular.
// For odd inputs we can rely on the mirrored sampling to get intended behavior.
if (vIntCoord < 1.0)
{
// Y4 is the pivot.
Y[2] = Y[6];
#if INPUT_SAMPLES > 1
CbCr[2] = CbCr[6];
#endif
}
}
else if (EDGE_CONDITION > 0)
{
if (vIntCoord + 2.0 > aligned_transform_size)
{
// We're on the last two pixels.
// Y5 is the pivot. LP inputs behave as expected when using mirroring.
Y[7] = Y[3];
Y[9] = Y[1];
#if INPUT_SAMPLES > 1
CbCr[7] = CbCr[3];
CbCr[9] = CbCr[1];
#endif
}
else if (vIntCoord + 4.0 >= aligned_transform_size)
{
// Y7 is the pivot.
Y[9] = Y[5];
#if INPUT_SAMPLES > 1
CbCr[9] = CbCr[5];
#endif
}
}
#if INPUT_PLANES > 1
#define AccumT vec3
#define GenInput(comp) vec3(Y[comp], CbCr[comp])
#else
#define AccumT float
#define GenInput(comp) Y[comp]
#endif
AccumT C0, C1, C2, C3, C4;
float W0, W1, W2, W3, W4;
// Not ideal, but gotta do what we gotta do.
// GPU will have to take both paths here,
// but at least we avoid dynamic load-store which is RIP perf on these chips ...
if (is_odd)
{
C0 = GenInput(5);
C1 = GenInput(4) + GenInput(6);
C2 = GenInput(3) + GenInput(7);
C3 = GenInput(2) + GenInput(8);
C4 = GenInput(1) + GenInput(9);
W0 = SYNTHESIS_HP_0;
W1 = SYNTHESIS_LP_1;
W2 = SYNTHESIS_HP_2;
W3 = SYNTHESIS_LP_3;
W4 = SYNTHESIS_HP_4;
}
else
{
C0 = GenInput(4);
C1 = GenInput(3) + GenInput(5);
C2 = GenInput(2) + GenInput(6);
C3 = GenInput(1) + GenInput(7);
C4 = AccumT(0.0);
W0 = SYNTHESIS_LP_0;
W1 = SYNTHESIS_HP_1;
W2 = SYNTHESIS_LP_2;
W3 = SYNTHESIS_HP_3;
W4 = 0.0;
}
AccumT result = C0 * W0 + C1 * W1 + C2 * W2 + C3 * W3 + C4 * W4;
#if OUTPUT_PLANES == 3
oY = result.x;
oCb = result.y;
oCr = result.z;
#elif OUTPUT_PLANES == 2
oY = result.x;
oCbCr = result.yz;
#else
oY = result;
#endif
if (FINAL_Y)
oY += 0.5;
if (FINAL_CBCR)
{
#if OUTPUT_PLANES == 3
oCb += 0.5;
oCr += 0.5;
#elif OUTPUT_PLANES == 2
oCbCr += 0.5;
#endif
}
}