e2c9bfd3d9
apple / swift (push) Successful in 1m4s
windows-host / package (push) Successful in 6m28s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 1m14s
windows-msix / package (x64, C:\Users\Public\ffmpeg, x86_64-pc-windows-msvc, C:\t) (push) Successful in 1m10s
release / apple (push) Successful in 7m53s
android / android (push) Successful in 10m33s
ci / web (push) Successful in 44s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 3m4s
ci / docs-site (push) Successful in 53s
ci / rust (push) Successful in 12m22s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 1m11s
apple / screenshots (push) Successful in 5m24s
deb / build-publish (push) Successful in 3m16s
decky / build-publish (push) Successful in 21s
ci / bench (push) Successful in 4m42s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 27s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 2m34s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 2m42s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 2m13s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 47s
flatpak / build-publish (push) Successful in 4m24s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 8m5s
docker / deploy-docs (push) Successful in 25s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 7m44s
HDR (display-driven, matching the WGC path): - CTA-861.3 HDR EDID (BT.2020 primaries + HDR Static Metadata block) so Windows offers "Use HDR" on the virtual display. The host FOLLOWS the display's live advanced-color state, recreating the shared ring at the matching format (FP16 in HDR / BGRA in SDR) on a toggle — no freeze. - Always emit Main10/BT.2020-PQ Rgb10a2 while the display is HDR; the client auto-detects PQ from the HEVC VUI (clients under-report VIDEO_CAP_10BIT). Generic HDR10 mastering SEI on every IDR. - Generation-tagged `latest` (gen<<40|seq<<8|slot) + driver `is_stale` re-attach kill the toggle-time garbage frame and any stale-ring read. Perf: - Pipeline the encode loop (Capturer::pipeline_depth; IDD-push = 2): submit N+1 before polling N so the convert/copy on the 3D engine overlaps the NVENC encode of N on the ASIC. PUNKTFUNK_IDD_DEPTH overrides (1 = synchronous). - Rotating host output ring (OUT_RING) so the in-flight encode and the next convert never touch the same texture. - HDR converts directly from the keyed-mutex slot's SRV into the output ring (drops the redundant slot->fp16 scratch copy); SDR copies the BGRA slot in. The slot mutex is held only across the convert/copy, not the encode. RING_LEN 3->6 for publish headroom. - Capture-health diagnostic: new_fps vs repeat_fps under PUNKTFUNK_PERF (a low new_fps at a high send rate means the source isn't compositing, not an encode stall). Validated live on the RTX box: 5120x1440@240 HDR streams; driver composes ~180 new fps, encode 240 fps @ ~4.3 ms p50. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
345 lines
8.6 KiB
Rust
345 lines
8.6 KiB
Rust
#![allow(non_snake_case)]
|
|
#![allow(clippy::missing_errors_doc)]
|
|
|
|
use std::sync::OnceLock;
|
|
|
|
use wdf_umdf_sys::{
|
|
IDARG_IN_ADAPTERSETRENDERADAPTER, IDARG_IN_ADAPTER_INIT, IDARG_IN_MONITORCREATE,
|
|
IDARG_IN_QUERY_HWCURSOR, IDARG_IN_SETUP_HWCURSOR, IDARG_IN_SWAPCHAINSETDEVICE,
|
|
IDARG_OUT_ADAPTER_INIT, IDARG_OUT_MONITORARRIVAL, IDARG_OUT_MONITORCREATE,
|
|
IDARG_IN_RELEASEANDACQUIREBUFFER2, IDARG_OUT_QUERY_HWCURSOR, IDARG_OUT_RELEASEANDACQUIREBUFFER,
|
|
IDARG_OUT_RELEASEANDACQUIREBUFFER2, IDDCX_ADAPTER, IDDCX_MONITOR,
|
|
IDDCX_SWAPCHAIN, IDD_CX_CLIENT_CONFIG, NTSTATUS, WDFDEVICE, WDFDEVICE_INIT,
|
|
};
|
|
|
|
#[derive(Copy, Clone, Debug, thiserror::Error)]
|
|
pub enum IddCxError {
|
|
#[error("{0}")]
|
|
IddCxFunctionNotAvailable(&'static str),
|
|
#[error("{0}")]
|
|
CallFailed(NTSTATUS),
|
|
#[error("{0}")]
|
|
NtStatus(NTSTATUS),
|
|
}
|
|
|
|
impl From<IddCxError> for NTSTATUS {
|
|
fn from(value: IddCxError) -> Self {
|
|
#[allow(clippy::enum_glob_use)]
|
|
use IddCxError::*;
|
|
match value {
|
|
IddCxFunctionNotAvailable(_) => Self::STATUS_NOT_FOUND,
|
|
CallFailed(status) => status,
|
|
NtStatus(n) => n,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<NTSTATUS> for IddCxError {
|
|
fn from(value: NTSTATUS) -> Self {
|
|
IddCxError::CallFailed(value)
|
|
}
|
|
}
|
|
|
|
impl From<i32> for IddCxError {
|
|
fn from(val: i32) -> Self {
|
|
IddCxError::NtStatus(NTSTATUS(val))
|
|
}
|
|
}
|
|
|
|
// void IddCx functions return () on success; required by the call macro's error arm but never an error.
|
|
impl From<()> for IddCxError {
|
|
fn from(_: ()) -> Self {
|
|
IddCxError::NtStatus(NTSTATUS(0))
|
|
}
|
|
}
|
|
|
|
macro_rules! IddCxCall {
|
|
($name:ident ( $($args:expr),* )) => {
|
|
IddCxCall!(false, $name($($args),*))
|
|
};
|
|
|
|
($other_is_error:expr, $name:ident ( $($args:expr),* )) => {{
|
|
static CACHED_FN: OnceLock<
|
|
Result<
|
|
::paste::paste!(::wdf_umdf_sys::[<PFN_ $name:upper>]),
|
|
IddCxError
|
|
>
|
|
> = OnceLock::new();
|
|
|
|
let f = CACHED_FN.get_or_init(|| {
|
|
::paste::paste! {
|
|
const FN_INDEX: usize = ::wdf_umdf_sys::IDDFUNCENUM::[<$name TableIndex>].0 as usize;
|
|
|
|
// validate that wdf function can be used
|
|
let is_available = ::wdf_umdf_sys::IddCxIsFunctionAvailable!($name);
|
|
|
|
if is_available {
|
|
// SAFETY: Only immutable accesses are done to this
|
|
// The underlying array is Copy, so we call as_ptr() directly on it inside block
|
|
let fn_table = unsafe { ::wdf_umdf_sys::IddFunctions.as_ptr() };
|
|
|
|
// SAFETY: Ensured that this is present by if condition from `WdfIsFunctionAvailable!`
|
|
let f = unsafe {
|
|
fn_table.add(FN_INDEX)
|
|
.cast::<::wdf_umdf_sys::[<PFN_ $name:upper>]>()
|
|
};
|
|
|
|
// SAFETY: Ensured that this is present by if condition from `IddIsFunctionAvailable!`
|
|
let f = unsafe { f.read() };
|
|
|
|
Ok(f)
|
|
} else {
|
|
Err($crate::IddCxError::IddCxFunctionNotAvailable(concat!(stringify!($name), " is not available")))
|
|
}
|
|
}
|
|
}).clone()?;
|
|
|
|
// SAFETY: Above: If it's Ok, then it's guaranteed to be Some(fn)
|
|
let f = unsafe { f.unwrap_unchecked() };
|
|
|
|
// SAFETY: Pointer to globals is always immutable
|
|
let globals = unsafe { ::wdf_umdf_sys::IddDriverGlobals };
|
|
|
|
// SAFETY: None. User is responsible for safety and must use their own unsafe block
|
|
let result = unsafe { f(globals, $($args),*) };
|
|
|
|
if $crate::is_nt_error(&result, $other_is_error) {
|
|
Err(result.into())
|
|
} else {
|
|
Ok(result.into())
|
|
}
|
|
|
|
}};
|
|
}
|
|
|
|
/// # Safety
|
|
///
|
|
/// None. User is responsible for safety.
|
|
pub unsafe fn IddCxDeviceInitConfig(
|
|
// in, out
|
|
DeviceInit: &mut WDFDEVICE_INIT,
|
|
// in
|
|
Config: &IDD_CX_CLIENT_CONFIG,
|
|
) -> Result<NTSTATUS, IddCxError> {
|
|
IddCxCall! {
|
|
IddCxDeviceInitConfig(
|
|
DeviceInit,
|
|
Config
|
|
)
|
|
}
|
|
}
|
|
|
|
/// # Safety
|
|
///
|
|
/// None. User is responsible for safety.
|
|
pub unsafe fn IddCxDeviceInitialize(
|
|
// in
|
|
Device: WDFDEVICE,
|
|
) -> Result<NTSTATUS, IddCxError> {
|
|
IddCxCall! {
|
|
IddCxDeviceInitialize(
|
|
Device
|
|
)
|
|
}
|
|
}
|
|
|
|
/// # Safety
|
|
///
|
|
/// None. User is responsible for safety.
|
|
pub unsafe fn IddCxAdapterInitAsync(
|
|
// in
|
|
pInArgs: &IDARG_IN_ADAPTER_INIT,
|
|
// out
|
|
pOutArgs: &mut IDARG_OUT_ADAPTER_INIT,
|
|
) -> Result<NTSTATUS, IddCxError> {
|
|
IddCxCall! {
|
|
IddCxAdapterInitAsync(
|
|
pInArgs,
|
|
pOutArgs
|
|
)
|
|
}
|
|
}
|
|
|
|
/// # Safety
|
|
///
|
|
/// None. User is responsible for safety.
|
|
#[rustfmt::skip]
|
|
pub unsafe fn IddCxMonitorCreate(
|
|
// in
|
|
AdapterObject: IDDCX_ADAPTER,
|
|
// in
|
|
pInArgs: &IDARG_IN_MONITORCREATE,
|
|
// out
|
|
pOutArgs: &mut IDARG_OUT_MONITORCREATE,
|
|
) -> Result<NTSTATUS, IddCxError> {
|
|
IddCxCall!(
|
|
IddCxMonitorCreate(
|
|
AdapterObject,
|
|
pInArgs,
|
|
pOutArgs
|
|
)
|
|
)
|
|
}
|
|
|
|
/// # Safety
|
|
///
|
|
/// None. User is responsible for safety.
|
|
#[rustfmt::skip]
|
|
pub unsafe fn IddCxMonitorArrival(
|
|
// in
|
|
MonitorObject: IDDCX_MONITOR,
|
|
// out
|
|
pOutArgs: &mut IDARG_OUT_MONITORARRIVAL,
|
|
) -> Result<NTSTATUS, IddCxError> {
|
|
IddCxCall!(
|
|
IddCxMonitorArrival(
|
|
MonitorObject,
|
|
pOutArgs
|
|
)
|
|
)
|
|
}
|
|
|
|
/// # Safety
|
|
///
|
|
/// None. User is responsible for safety.
|
|
#[rustfmt::skip]
|
|
pub unsafe fn IddCxSwapChainSetDevice(
|
|
// in
|
|
SwapChainObject: IDDCX_SWAPCHAIN,
|
|
// in
|
|
pInArgs: &IDARG_IN_SWAPCHAINSETDEVICE
|
|
) -> Result<NTSTATUS, IddCxError> {
|
|
IddCxCall!(
|
|
true,
|
|
IddCxSwapChainSetDevice(
|
|
SwapChainObject,
|
|
pInArgs
|
|
)
|
|
)
|
|
}
|
|
|
|
/// # Safety
|
|
///
|
|
/// None. User is responsible for safety.
|
|
#[rustfmt::skip]
|
|
pub unsafe fn IddCxSwapChainReleaseAndAcquireBuffer(
|
|
// in
|
|
SwapChainObject: IDDCX_SWAPCHAIN,
|
|
// out
|
|
pOutArgs: &mut IDARG_OUT_RELEASEANDACQUIREBUFFER
|
|
) -> Result<NTSTATUS, IddCxError> {
|
|
IddCxCall!(
|
|
true,
|
|
IddCxSwapChainReleaseAndAcquireBuffer(
|
|
SwapChainObject,
|
|
pOutArgs
|
|
)
|
|
)
|
|
}
|
|
|
|
/// IddCx 1.10 HDR variant — required once the adapter sets `CAN_PROCESS_FP16`. Provides per-frame
|
|
/// `IDDCX_METADATA2` (surface colour space, HDR metadata, SDR white level).
|
|
///
|
|
/// # Safety
|
|
/// None. User is responsible for safety.
|
|
#[rustfmt::skip]
|
|
pub unsafe fn IddCxSwapChainReleaseAndAcquireBuffer2(
|
|
// in
|
|
SwapChainObject: IDDCX_SWAPCHAIN,
|
|
// in
|
|
pInArgs: &mut IDARG_IN_RELEASEANDACQUIREBUFFER2,
|
|
// out
|
|
pOutArgs: &mut IDARG_OUT_RELEASEANDACQUIREBUFFER2
|
|
) -> Result<NTSTATUS, IddCxError> {
|
|
IddCxCall!(
|
|
true,
|
|
IddCxSwapChainReleaseAndAcquireBuffer2(
|
|
SwapChainObject,
|
|
pInArgs,
|
|
pOutArgs
|
|
)
|
|
)
|
|
}
|
|
|
|
/// # Safety
|
|
///
|
|
/// None. User is responsible for safety.
|
|
#[rustfmt::skip]
|
|
pub unsafe fn IddCxSwapChainFinishedProcessingFrame(
|
|
// in
|
|
SwapChainObject: IDDCX_SWAPCHAIN
|
|
) -> Result<NTSTATUS, IddCxError> {
|
|
IddCxCall!(
|
|
true,
|
|
IddCxSwapChainFinishedProcessingFrame(
|
|
SwapChainObject
|
|
)
|
|
)
|
|
}
|
|
|
|
/// # Safety
|
|
///
|
|
/// None. User is responsible for safety.
|
|
#[rustfmt::skip]
|
|
pub unsafe fn IddCxMonitorDeparture(
|
|
// in
|
|
MonitorObject: IDDCX_MONITOR
|
|
) -> Result<NTSTATUS, IddCxError> {
|
|
IddCxCall!(
|
|
IddCxMonitorDeparture(
|
|
MonitorObject
|
|
)
|
|
)
|
|
}
|
|
|
|
/// # Safety
|
|
///
|
|
/// None. User is responsible for safety.
|
|
pub unsafe fn IddCxAdapterSetRenderAdapter(
|
|
// in
|
|
AdapterObject: IDDCX_ADAPTER,
|
|
// in
|
|
pInArgs: *const IDARG_IN_ADAPTERSETRENDERADAPTER,
|
|
) -> Result<(), IddCxError> {
|
|
IddCxCall!(IddCxAdapterSetRenderAdapter(AdapterObject, pInArgs))
|
|
}
|
|
|
|
/// # Safety
|
|
///
|
|
/// None. User is responsible for safety.
|
|
#[rustfmt::skip]
|
|
pub unsafe fn IddCxMonitorSetupHardwareCursor(
|
|
// in
|
|
MonitorObject: IDDCX_MONITOR,
|
|
// in
|
|
pInArgs: &IDARG_IN_SETUP_HWCURSOR
|
|
) -> Result<NTSTATUS, IddCxError> {
|
|
IddCxCall!(
|
|
IddCxMonitorSetupHardwareCursor(
|
|
MonitorObject,
|
|
pInArgs
|
|
)
|
|
)
|
|
}
|
|
|
|
/// # Safety
|
|
///
|
|
/// None. User is responsible for safety.
|
|
#[rustfmt::skip]
|
|
pub unsafe fn IddCxMonitorQueryHardwareCursor(
|
|
// in
|
|
MonitorObject: IDDCX_MONITOR,
|
|
// in
|
|
pInArgs: &IDARG_IN_QUERY_HWCURSOR,
|
|
// out
|
|
pOutArgs: &mut IDARG_OUT_QUERY_HWCURSOR
|
|
) -> Result<NTSTATUS, IddCxError> {
|
|
IddCxCall!(
|
|
IddCxMonitorQueryHardwareCursor(
|
|
MonitorObject,
|
|
pInArgs,
|
|
pOutArgs
|
|
)
|
|
)
|
|
}
|