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
@@ -0,0 +1,3 @@
add_granite_internal_lib(granite-path path_utils.hpp path_utils.cpp)
target_include_directories(granite-path PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(granite-path PRIVATE granite-util)
@@ -0,0 +1,282 @@
/* Copyright (c) 2017-2026 Hans-Kristian Arntzen
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#define NOMINMAX
#include "path_utils.hpp"
#include "string_helpers.hpp"
#include <algorithm>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#else
#include <unistd.h>
#ifdef __linux__
#include <linux/limits.h>
#endif
#endif
namespace Granite
{
namespace Path
{
std::string enforce_protocol(const std::string &path)
{
if (path.empty())
return "";
auto index = path.find("://");
if (index == std::string::npos)
return std::string("file://") + path;
else
return path;
}
std::string canonicalize_path(const std::string &path)
{
std::string transformed;
transformed.resize(path.size());
transform(begin(path), end(path), begin(transformed), [](char c) -> char { return c == '\\' ? '/' : c; });
auto data = Util::split_no_empty(transformed, "/");
std::vector<std::string> result;
for (auto &i : data)
{
if (i == "..")
{
if (!result.empty())
result.pop_back();
}
else if (i != ".")
result.push_back(std::move(i));
}
std::string res;
for (auto &i : result)
{
if (&i != result.data())
res += "/";
res += i;
}
return res;
}
static size_t find_last_slash(const std::string &str)
{
#ifdef _WIN32
auto index = str.find_last_of("/\\");
#else
auto index = str.find_last_of('/');
#endif
return index;
}
bool is_abspath(const std::string &path)
{
if (path.empty())
return false;
if (path.front() == '/')
return true;
#ifdef _WIN32
{
auto index = std::min(path.find(":/"), path.find(":\\"));
if (index != std::string::npos)
return true;
}
#endif
return path.find("://") != std::string::npos;
}
bool is_root_path(const std::string &path)
{
if (path.empty())
return false;
if (path.front() == '/' && path.size() == 1)
return true;
#ifdef _WIN32
{
auto index = std::min(path.find(":/"), path.find(":\\"));
if (index != std::string::npos && (index + 2) == path.size())
return true;
}
#endif
auto index = path.find("://");
return index != std::string::npos && (index + 3) == path.size();
}
std::string join(const std::string &base, const std::string &path)
{
if (base.empty())
return path;
if (path.empty())
return base;
if (is_abspath(path))
return path;
auto index = find_last_slash(base);
bool need_slash = index != base.size() - 1;
return Util::join(base, need_slash ? "/" : "", path);
}
std::string basedir(const std::string &path)
{
if (path.empty())
return "";
if (is_root_path(path))
return path;
auto index = find_last_slash(path);
if (index == std::string::npos)
return ".";
// Preserve the first slash.
if (index == 0 && is_abspath(path))
index++;
auto ret = path.substr(0, index + 1);
if (!is_root_path(ret))
ret.pop_back();
return ret;
}
std::string basename(const std::string &path)
{
if (path.empty())
return "";
auto index = find_last_slash(path);
if (index == std::string::npos)
return path;
auto base = path.substr(index + 1, std::string::npos);
return base;
}
std::string relpath(const std::string &base, const std::string &path)
{
return Path::join(basedir(base), path);
}
std::string ext(const std::string &path)
{
auto index = path.find_last_of('.');
if (index == std::string::npos)
return "";
else
return path.substr(index + 1, std::string::npos);
}
std::pair<std::string, std::string> split(const std::string &path)
{
if (path.empty())
return make_pair(std::string("."), std::string("."));
auto index = find_last_slash(path);
if (index == std::string::npos)
return make_pair(std::string("."), path);
auto base = path.substr(index + 1, std::string::npos);
return make_pair(path.substr(0, index), base);
}
std::pair<std::string, std::string> protocol_split(const std::string &path)
{
if (path.empty())
return make_pair(std::string(""), std::string(""));
auto index = path.find("://");
if (index == std::string::npos)
return make_pair(std::string(""), path);
return make_pair(path.substr(0, index), path.substr(index + 3, std::string::npos));
}
std::string get_executable_path()
{
#ifdef _WIN32
wchar_t target[4096];
DWORD ret = GetModuleFileNameW(GetModuleHandle(nullptr), target, sizeof(target) / sizeof(wchar_t));
return canonicalize_path(Path::to_utf8(target, ret));
#else
pid_t pid = getpid();
static const char *exts[] = { "exe", "file", "a.out" };
char link_path[PATH_MAX];
char target[PATH_MAX];
for (auto *ext : exts)
{
snprintf(link_path, sizeof(link_path), "/proc/%u/%s",
unsigned(pid), ext);
ssize_t ret = readlink(link_path, target, sizeof(target) - 1);
if (ret >= 0)
{
target[ret] = '\0';
return std::string(target);
}
}
return "";
#endif
}
#ifdef _WIN32
std::string to_utf8(const wchar_t *wstr, size_t len)
{
std::vector<char> char_buffer;
auto ret = WideCharToMultiByte(CP_UTF8, 0, wstr, len, nullptr, 0, nullptr, nullptr);
if (ret < 0)
return "";
char_buffer.resize(ret);
WideCharToMultiByte(CP_UTF8, 0, wstr, len, char_buffer.data(), char_buffer.size(), nullptr, nullptr);
return std::string(char_buffer.data(), char_buffer.size());
}
std::wstring to_utf16(const char *str, size_t len)
{
std::vector<wchar_t> wchar_buffer;
auto ret = MultiByteToWideChar(CP_UTF8, 0, str, len, nullptr, 0);
if (ret < 0)
return L"";
wchar_buffer.resize(ret);
MultiByteToWideChar(CP_UTF8, 0, str, len, wchar_buffer.data(), wchar_buffer.size());
return std::wstring(wchar_buffer.data(), wchar_buffer.size());
}
std::string to_utf8(const std::wstring &wstr)
{
return to_utf8(wstr.data(), wstr.size());
}
std::wstring to_utf16(const std::string &str)
{
return to_utf16(str.data(), str.size());
}
#endif
}
}
@@ -0,0 +1,51 @@
/* Copyright (c) 2017-2026 Hans-Kristian Arntzen
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#pragma once
#include <string>
#include <utility>
namespace Granite
{
namespace Path
{
std::string join(const std::string &base, const std::string &path);
std::string basedir(const std::string &path);
std::string basename(const std::string &path);
std::pair<std::string, std::string> split(const std::string &path);
std::string relpath(const std::string &base, const std::string &path);
std::string ext(const std::string &path);
std::pair<std::string, std::string> protocol_split(const std::string &path);
bool is_abspath(const std::string &path);
bool is_root_path(const std::string &path);
std::string canonicalize_path(const std::string &path);
std::string enforce_protocol(const std::string &path);
std::string get_executable_path();
#ifdef _WIN32
std::string to_utf8(const wchar_t *wstr, size_t len);
std::wstring to_utf16(const char *str, size_t len);
std::string to_utf8(const std::wstring &wstr);
std::wstring to_utf16(const std::string &str);
#endif
}
}