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:
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import plotly.express as px
|
||||
import plotly.graph_objects as go
|
||||
import pandas as pd
|
||||
import sys
|
||||
import csv
|
||||
import os
|
||||
|
||||
def main():
|
||||
codecs = ['h264_nvenc', 'hevc_nvenc', 'av1_nvenc', 'pyrowave']
|
||||
cols = ['xpsnr', 'ssim', 'ssimulacra2', 'vmaf', 'vmafneg']
|
||||
|
||||
codec_name = {
|
||||
'h264_nvenc' : 'H.264 NVENC',
|
||||
'hevc_nvenc' : 'H.265 NVENC',
|
||||
'av1_nvenc' : 'AV1 NVENC',
|
||||
'pyrowave' : 'PyroWave' }
|
||||
|
||||
metric_name = {
|
||||
'xpsnr' : 'W-XPSNR',
|
||||
'ssim' : 'SSIM',
|
||||
'ssimulacra2' : 'SSIMULACRA2',
|
||||
'vmaf' : 'VMAF',
|
||||
'vmafneg' : 'VMAF NEG' }
|
||||
|
||||
data_sets = {}
|
||||
|
||||
for codec in codecs:
|
||||
with open(os.path.join(sys.argv[1], codec + '.csv')) as f:
|
||||
reader = csv.DictReader(f)
|
||||
bpps = []
|
||||
results = dict()
|
||||
for col in cols:
|
||||
results[col] = []
|
||||
for row in reader:
|
||||
bpp = row['bpp']
|
||||
bpps.append(bpp)
|
||||
for col in cols:
|
||||
results[col].append(float(row[col]))
|
||||
data_sets[codec] = ([float(x) for x in bpps], results)
|
||||
|
||||
print(data_sets)
|
||||
|
||||
for col in cols:
|
||||
fig = go.Figure()
|
||||
for codec in codecs:
|
||||
codec_results = data_sets[codec]
|
||||
fig.add_trace(go.Scatter(x = codec_results[0], y = codec_results[1][col], name = codec_name[codec], showlegend = True))
|
||||
fig.update_traces(mode = 'markers+lines')
|
||||
fig.update_xaxes(title_text = 'bits per pixel')
|
||||
fig.update_yaxes(title_text = metric_name[col])
|
||||
fig.update_layout(legend = dict(title = dict(text = 'codec')))
|
||||
fig.update_layout(title = os.path.basename(sys.argv[1]))
|
||||
fig.write_image(os.path.join(sys.argv[1], col + '.png'))
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,168 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
input_file="$1"
|
||||
output_dir="$2"
|
||||
|
||||
if [ -z $input_file ]; then
|
||||
echo "Need to specify input."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z $output_dir ]; then
|
||||
echo "Need to specify output."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$output_dir"
|
||||
rm -f "$output_dir"/*.csv
|
||||
|
||||
csv_header="bpp,xpsnr,ssim,ssimulacra2,vmaf,vmafneg"
|
||||
echo $csv_header > "$output_dir"/h264_nvenc.csv
|
||||
echo $csv_header > "$output_dir"/hevc_nvenc.csv
|
||||
echo $csv_header > "$output_dir"/av1_nvenc.csv
|
||||
#echo $csv_header > "$output_dir"/h264_vaapi.csv
|
||||
#echo $csv_header > "$output_dir"/hevc_vaapi.csv
|
||||
#echo $csv_header > "$output_dir"/av1_vaapi.csv
|
||||
echo $csv_header > "$output_dir"/pyrowave.csv
|
||||
|
||||
append_stats_to_csv()
|
||||
{
|
||||
stats=$1
|
||||
csv=$2
|
||||
|
||||
bpp=$(grep "BPP =" $stats | grep -oE '[^ ]+$')
|
||||
xpsnr=$(grep "W-XPSNR:" $stats | grep -oE '[^ ]+$')
|
||||
ssim=$(grep "SSIM Score:" $stats | grep -oE '[^ ]+$')
|
||||
ssimulacra2=$(grep "Average:" $stats | grep -oE '[^ ]+$')
|
||||
vmaf=$(grep "VMAF:" $stats | grep -oE '[^ ]+$')
|
||||
vmafneg=$(grep "VMAF NEG:" $stats | grep -oE '[^ ]+$')
|
||||
|
||||
echo "$bpp,$xpsnr,$ssim,$ssimulacra2,$vmaf,$vmafneg" >> $csv
|
||||
}
|
||||
|
||||
encode_nvenc()
|
||||
{
|
||||
bit_rate=$1k
|
||||
# Round up to not give codec an impossible situation
|
||||
buf_size=$(($1 / 60 + 1))k
|
||||
codec=$2
|
||||
encoded_name="$output_dir"/${codec}_nvenc_${bit_rate}.mkv
|
||||
stats=""$output_dir"/${codec}_nvenc_stats_${bit_rate}.txt"
|
||||
|
||||
echo "============="
|
||||
echo "Encoding with NVENC, bitrate ${bit_rate}."
|
||||
echo ffmpeg -y -i $input_file -b:v $bit_rate -c:v ${codec}_nvenc \
|
||||
-preset p1 -tune ull -g 1 -rc cbr -bufsize $buf_size $encoded_name
|
||||
ffmpeg -y -i $input_file -b:v $bit_rate -c:v ${codec}_nvenc \
|
||||
-preset p1 -tune ull -g 1 -rc cbr -bufsize $buf_size $encoded_name >/dev/null 2>/dev/null
|
||||
ffmpeg -y -i $encoded_name $encoded_name.y4m >/dev/null 2>/dev/null
|
||||
psy-ex-scores.py $input_file $encoded_name.y4m -t 16 -e 4 | ansi2txt > $stats
|
||||
rm $encoded_name.y4m
|
||||
echo "Stats in ${stats}, reference output in ${encoded_name}."
|
||||
du -b $encoded_name
|
||||
echo "============="
|
||||
|
||||
actual_size=$(stat -c %s $encoded_name)
|
||||
input_size=$(stat -c %s $input_file)
|
||||
bpp=$(echo "scale=3; 12 * $actual_size / $input_size" | bc)
|
||||
echo "BPP = $bpp" >> $stats
|
||||
|
||||
append_stats_to_csv $stats "$output_dir"/${codec}_nvenc.csv
|
||||
|
||||
rm $encoded_name
|
||||
}
|
||||
|
||||
encode_vaapi()
|
||||
{
|
||||
bit_rate=${1}k
|
||||
# Round up to not give codec an impossible situation
|
||||
buf_size=$(($1 / 60 + 1))k
|
||||
codec=$2
|
||||
encoded_name="$output_dir"/${codec}_vaapi_${bit_rate}.mkv
|
||||
stats=""$output_dir"/${codec}_vaapi_stats_${bit_rate}.txt"
|
||||
echo "============="
|
||||
echo "Encoding with VAAPI, bitrate ${bit_rate}."
|
||||
ffmpeg -y -vaapi_device /dev/dri/renderD128 -i $input_file -c:v ${codec}_vaapi \
|
||||
-idr_interval 1 -g 1 -rc_mode CBR -b:v $bit_rate -bufsize $buf_size \
|
||||
-vf format=nv12,hwupload $encoded_name >/dev/null 2>/dev/null
|
||||
ffmpeg -y -i $encoded_name $encoded_name.y4m >/dev/null 2>/dev/null
|
||||
psy-ex-scores.py $input_file $encoded_name.y4m -t 16 -e 4 | ansi2txt > $stats
|
||||
rm $encoded_name.y4m
|
||||
echo "Stats in ${stats}, reference output in ${encoded_name}."
|
||||
du -b $encoded_name
|
||||
echo "============="
|
||||
|
||||
actual_size=$(stat -c %s $encoded_name)
|
||||
input_size=$(stat -c %s $input_file)
|
||||
bpp=$(echo "scale=3; 12 * $actual_size / $input_size" | bc)
|
||||
echo "BPP = $bpp" >> $stats
|
||||
|
||||
append_stats_to_csv $stats "$output_dir"/${codec}_vaapi.csv
|
||||
|
||||
rm $encoded_name
|
||||
}
|
||||
|
||||
encode_pyrowave()
|
||||
{
|
||||
bit_rate=${1}k
|
||||
bytes_per_image=$((($1 * 1000) / (60 * 8)))
|
||||
encoded_name="$output_dir"/pyrowave_${bit_rate}.y4m
|
||||
stats=""$output_dir"/pyrowave_stats_${bit_rate}.txt"
|
||||
echo "============="
|
||||
echo "Encoding with PyroWave, bitrate ${bit_rate}, $bytes_per_image bytes per image."
|
||||
pyrowave-sandbox $input_file $encoded_name $bytes_per_image 2>/dev/null
|
||||
psy-ex-scores.py $input_file $encoded_name -t 16 -e 4 | ansi2txt > $stats
|
||||
echo "Stats in ${stats}, reference output in ${encoded_name}."
|
||||
echo "============="
|
||||
|
||||
bpp=$(echo "scale=3; 8 * $bytes_per_image / (1920 * 1080)" | bc)
|
||||
echo "BPP = $bpp" >> $stats
|
||||
append_stats_to_csv $stats "$output_dir"/pyrowave.csv
|
||||
|
||||
rm $encoded_name
|
||||
}
|
||||
|
||||
encode_nvenc_group()
|
||||
{
|
||||
encode_nvenc $1 h264
|
||||
encode_nvenc $1 hevc
|
||||
encode_nvenc $1 av1
|
||||
}
|
||||
|
||||
encode_vaapi_group()
|
||||
{
|
||||
encode_vaapi $1 h264
|
||||
encode_vaapi $1 hevc
|
||||
encode_vaapi $1 av1
|
||||
}
|
||||
|
||||
encode_accel_group()
|
||||
{
|
||||
encode_nvenc_group $1
|
||||
# VAAPI seems to not understand what CBR rate control means :') Ignore it.
|
||||
#encode_vaapi_group $1
|
||||
}
|
||||
|
||||
encode_pyrowave 50000
|
||||
encode_pyrowave 75000
|
||||
encode_pyrowave 100000
|
||||
encode_pyrowave 150000
|
||||
encode_pyrowave 200000
|
||||
encode_pyrowave 250000
|
||||
encode_pyrowave 300000
|
||||
encode_pyrowave 400000
|
||||
encode_pyrowave 500000
|
||||
encode_accel_group 50000
|
||||
encode_accel_group 75000
|
||||
encode_accel_group 100000
|
||||
encode_accel_group 150000
|
||||
encode_accel_group 200000
|
||||
encode_accel_group 250000
|
||||
encode_accel_group 300000
|
||||
encode_accel_group 400000
|
||||
encode_accel_group 500000
|
||||
|
||||
python plot.py $output_dir
|
||||
|
||||
Reference in New Issue
Block a user