Skip to content

Commit 6de0a9f

Browse files
Merge pull request #12 from NortheBridge/feat/hdr10
HDR10: FP16 adapter contract, per-mode wire depth, format-aware ring
2 parents 2f7daf5 + 184a94b commit 6de0a9f

9 files changed

Lines changed: 184 additions & 29 deletions

File tree

CLAUDE.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,3 +206,36 @@ Integration lessons (all host-side, none required driver changes):
206206
Next: tranche 3b — ring-consuming capture backend in LuminalShine
207207
(`display_vgd` platf::display_t), driver HDR10 caps, cursor/gamma DDIs,
208208
WGC-RELIABILITY §7 alttab_stress port.
209+
210+
### Tranche 3b + HDR10 — COMPLETE (2026-07-20, Windows box)
211+
212+
**Milestones verified the same day:** (1) LuminalShine consumes the
213+
frame ring directly (`display_vgd_vram_t`: claim → keyed-mutex key 1 →
214+
GPU copy → release; no WGC helper, latency parity ~5 ms) and (2) **HDR10
215+
end to end** — driver build 2 (caps 0x185) creates HDR monitors
216+
(bit_depth=110 wire value), Windows engages advanced color off our
217+
CTA-861.3 EDID block, the ring carries FP16 scRGB, and LuminalShine
218+
encodes HEVC Main10 4:4:4 with HDR metadata. AV1 HDR 10-bit also works;
219+
AV1 4:4:4 is an NVENC hardware gap on RTX 5080 (not a software item).
220+
221+
HDR bring-up lessons (one wasted signing round each — check first):
222+
- **IDDCX_ADAPTER_FLAGS_CAN_PROCESS_FP16 is a contract, not a flag.**
223+
AdapterInitAsync fails STATUS_INVALID_PARAMETER unless the driver also
224+
registers EvtIddCxMonitorSetGammaRamp (HDR 3x4 matrix; GammaSupport
225+
must not be NONE) and acquires via IddCxSwapChainReleaseAndAcquire-
226+
Buffer2 (METADATA2). See "Updates for IddCx 1.10" doc for the full
227+
obligation list. ETW breadcrumbs localize this in one traced
228+
pnputil /restart-device — no re-sign needed to diagnose.
229+
- **Proto bit_depth wire values are 8/10/110/112** (HDR carries a
230+
leading "1"); hdr=1 with bit_depth=10 is BAD_BIT_DEPTH (-4), and the
231+
host log only shows "result=-4" — check dispatch err codes first.
232+
- Ring textures follow the acquired frame's DXGI format; an
233+
advanced-color toggle (BGRA8 ⇄ FP16) is a generation bump like a size
234+
change. Host reader re-latches format automatically.
235+
- Host-side stall detection must key off `latest_sequence` vs the last
236+
delivered sequence, never cumulative publish counters — an idle
237+
desktop is indistinguishable from a stall by counters alone.
238+
239+
Next: cursor + gamma DDIs (hardware cursor ABI is in proto v0.3),
240+
WGC-RELIABILITY §7 alttab_stress port, phase 7 packaging (installer,
241+
strict control-device SDDL — release blocker, uninstaller).

crates/luminal-vgd-driver/src/shell/bindings.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,23 @@ pub unsafe fn swapchain_release_and_acquire_buffer(
9797
)
9898
}
9999

100+
/// v1.10 variant — mandatory for CAN_PROCESS_FP16 adapters (per-frame
101+
/// IDDCX_METADATA2: HDR metadata, surface color space, SDR white level).
102+
/// Safe to call unconditionally: IddMinimumVersionRequired = 10 means the
103+
/// OS only loads us with a ≥1.10 function table.
104+
pub unsafe fn swapchain_release_and_acquire_buffer2(
105+
swapchain: IDDCX_SWAPCHAIN,
106+
in_args: *mut IDARG_IN_RELEASEANDACQUIREBUFFER2,
107+
out_args: *mut IDARG_OUT_RELEASEANDACQUIREBUFFER2,
108+
) -> NTSTATUS {
109+
iddcx_call!(
110+
_IDDFUNCENUM_IddCxSwapChainReleaseAndAcquireBuffer2TableIndex as PFN_IDDCXSWAPCHAINRELEASEANDACQUIREBUFFER2,
111+
swapchain,
112+
in_args,
113+
out_args
114+
)
115+
}
116+
100117
pub unsafe fn swapchain_finished_processing_frame(swapchain: IDDCX_SWAPCHAIN) -> NTSTATUS {
101118
iddcx_call!(
102119
_IDDFUNCENUM_IddCxSwapChainFinishedProcessingFrameTableIndex as PFN_IDDCXSWAPCHAINFINISHEDPROCESSINGFRAME,

crates/luminal-vgd-driver/src/shell/entry.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ unsafe extern "C" fn evt_device_add(
8888
idd.EvtIddCxAdapterCommitModes2 = Some(monitors::evt_commit_modes2);
8989
idd.EvtIddCxMonitorSetDefaultHdrMetaData = Some(monitors::evt_set_default_hdr_metadata);
9090
idd.EvtIddCxMonitorQueryTargetModes2 = Some(monitors::evt_query_target_modes2);
91+
// CAN_PROCESS_FP16 obligates the gamma-ramp DDI (HDR 3x4 matrix);
92+
// an FP16 adapter without it fails AdapterInitAsync validation.
93+
idd.EvtIddCxMonitorSetGammaRamp = Some(monitors::evt_monitor_set_gamma_ramp);
9194

9295
let status = bindings::device_init_config(device_init, &idd);
9396
if status != STATUS_SUCCESS {
@@ -183,6 +186,11 @@ unsafe extern "C" fn evt_d0_entry(
183186
let mut caps: ffi::IDDCX_ADAPTER_CAPS = zeroed();
184187
caps.Size = size_of::<ffi::IDDCX_ADAPTER_CAPS>() as u32;
185188
caps.MaxMonitorsSupported = luminal_driver_proto::DEFAULT_MAX_MONITORS;
189+
// HDR10: the OS only offers advanced color on an indirect display when
190+
// the adapter declares it can process FP16 desktop content; the frame
191+
// pipeline is format-agnostic (ring textures follow the acquired
192+
// frame's desc), so this is safe to declare unconditionally.
193+
caps.Flags = ffi::IDDCX_ADAPTER_FLAGS_IDDCX_ADAPTER_FLAGS_CAN_PROCESS_FP16;
186194
// MaxDisplayPipelineRate stays 0 (IddSampleDriver convention);
187195
// u64::MAX fails IddCxAdapterInitAsync parameter validation.
188196
caps.EndPointDiagnostics.Size = size_of::<ffi::IDDCX_ENDPOINT_DIAGNOSTIC_INFO>() as u32;
@@ -193,7 +201,10 @@ unsafe extern "C" fn evt_d0_entry(
193201
caps.EndPointDiagnostics.pEndPointManufacturerName = MANUFACTURER.as_ptr();
194202
caps.EndPointDiagnostics.pHardwareVersion = addr_of_mut!(version);
195203
caps.EndPointDiagnostics.pFirmwareVersion = addr_of_mut!(version);
196-
caps.EndPointDiagnostics.GammaSupport = ffi::IDDCX_FEATURE_IMPLEMENTATION_IDDCX_FEATURE_IMPLEMENTATION_NONE;
204+
// SOFTWARE, matching the registered EvtIddCxMonitorSetGammaRamp: the
205+
// HDR 3x4 matrix is acknowledged by the driver (conversion happens in
206+
// the host encoder). NONE alongside CAN_PROCESS_FP16 is contradictory.
207+
caps.EndPointDiagnostics.GammaSupport = ffi::IDDCX_FEATURE_IMPLEMENTATION_IDDCX_FEATURE_IMPLEMENTATION_SOFTWARE;
197208

198209
let mut in_args: ffi::IDARG_IN_ADAPTER_INIT = zeroed();
199210
in_args.WdfDevice = device;

crates/luminal-vgd-driver/src/shell/mod.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,18 @@ use std::time::Instant;
2929
use crate::dispatch::{DeviceState, HandleCtx};
3030
use luminal_vgd_core::modes::Mode;
3131

32-
/// Capabilities the phase-2 shell actually delivers. SDR only for now:
33-
/// HDR, hardware cursor, and gamma caps are added when their DDIs land
34-
/// (phases 2+/4 per docs/FEATURE-MATRIX.md).
35-
pub(crate) const SHELL_CAPS: u32 =
36-
luminal_driver_proto::caps::MULTI_MODE | luminal_driver_proto::caps::PERMANENT_POOL;
32+
/// Capabilities the shell actually delivers. HDR10/SDR10 landed with the
33+
/// HDR tranche (FP16-capable adapter, per-mode wire bit depth, format-
34+
/// aware ring textures); hardware cursor and gamma caps are added when
35+
/// their DDIs land (docs/FEATURE-MATRIX.md).
36+
pub(crate) const SHELL_CAPS: u32 = luminal_driver_proto::caps::MULTI_MODE
37+
| luminal_driver_proto::caps::PERMANENT_POOL
38+
| luminal_driver_proto::caps::HDR10
39+
| luminal_driver_proto::caps::SDR10_BIT;
3740

3841
/// Monotonic build stamp reported in HANDSHAKE/GET_STATUS (CI will stamp
3942
/// this properly in phase 7; hand-bumped during bring-up).
40-
pub(crate) const DRIVER_BUILD: u32 = 1;
43+
pub(crate) const DRIVER_BUILD: u32 = 2;
4144

4245
/// NUL-terminated UTF-16 literal; size the array one past the text so the
4346
/// terminator survives.

crates/luminal-vgd-driver/src/shell/monitors.rs

Lines changed: 60 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -269,10 +269,20 @@ pub unsafe extern "C" fn evt_commit_modes(
269269
// HDR paths get real implementations alongside caps::HDR10 later.
270270
// ---------------------------------------------------------------------
271271

272-
/// SDR-only wire format: 8-bit RGB, nothing else.
273-
fn wire_bpc_sdr8() -> ffi::IDDCX_WIRE_BITS_PER_COMPONENT {
272+
/// Wire format for one mode: RGB at the session's bit depth (8 for SDR-8,
273+
/// 10 for SDR-10/HDR10, 12 for HDR12). The OS picks the highest depth the
274+
/// whole path supports; HDR additionally needs the EDID's CTA-861.3 block
275+
/// (core::edid) and the adapter's CAN_PROCESS_FP16 flag (shell::entry).
276+
fn wire_bpc_for(mode: &Mode) -> ffi::IDDCX_WIRE_BITS_PER_COMPONENT {
277+
use luminal_driver_proto::BitDepth;
274278
let mut bpc: ffi::IDDCX_WIRE_BITS_PER_COMPONENT = unsafe { zeroed() };
275-
bpc.Rgb = ffi::IDDCX_BITS_PER_COMPONENT_IDDCX_BITS_PER_COMPONENT_8;
279+
bpc.Rgb = match mode.bit_depth {
280+
BitDepth::Sdr8 => ffi::IDDCX_BITS_PER_COMPONENT_IDDCX_BITS_PER_COMPONENT_8,
281+
BitDepth::Sdr10 | BitDepth::Hdr10 => {
282+
ffi::IDDCX_BITS_PER_COMPONENT_IDDCX_BITS_PER_COMPONENT_10
283+
}
284+
BitDepth::Hdr12 => ffi::IDDCX_BITS_PER_COMPONENT_IDDCX_BITS_PER_COMPONENT_12,
285+
};
276286
bpc
277287
}
278288

@@ -282,7 +292,11 @@ pub unsafe extern "C" fn evt_adapter_query_target_info(
282292
out_args: *mut ffi::IDARG_OUT_QUERYTARGET_INFO,
283293
) -> NTSTATUS {
284294
let out = &mut *out_args;
285-
out.TargetCaps = ffi::IDDCX_TARGET_CAPS_IDDCX_TARGET_CAPS_NONE;
295+
// HDR10/advanced color needs the target to advertise the wide/high
296+
// color-space pipeline; harmless for SDR-only monitors (the OS still
297+
// gates on the per-monitor EDID + wire bit depth).
298+
out.TargetCaps = ffi::IDDCX_TARGET_CAPS_IDDCX_TARGET_CAPS_HIGH_COLOR_SPACE
299+
| ffi::IDDCX_TARGET_CAPS_IDDCX_TARGET_CAPS_WIDE_COLOR_SPACE;
286300
out.DitheringSupport = zeroed();
287301
STATUS_SUCCESS
288302
}
@@ -313,7 +327,7 @@ pub unsafe extern "C" fn evt_parse_monitor_description2(
313327
slot.Size = size_of::<ffi::IDDCX_MONITOR_MODE2>() as u32;
314328
slot.Origin = ffi::IDDCX_MONITOR_MODE_ORIGIN_IDDCX_MONITOR_MODE_ORIGIN_MONITORDESCRIPTOR;
315329
slot.MonitorVideoSignalInfo = signal_info(mode, 0);
316-
slot.BitsPerComponent = wire_bpc_sdr8();
330+
slot.BitsPerComponent = wire_bpc_for(mode);
317331
}
318332
out.MonitorModeBufferOutputCount = fill as u32;
319333
STATUS_SUCCESS
@@ -343,7 +357,7 @@ pub unsafe extern "C" fn evt_query_target_modes2(
343357
// unused. A nonzero requirement against a zero adapter budget makes
344358
// every mode unactivatable (Extend reverts, Scale/Res grayed).
345359
slot.RequiredBandwidth = 0;
346-
slot.BitsPerComponent = wire_bpc_sdr8();
360+
slot.BitsPerComponent = wire_bpc_for(mode);
347361
}
348362
out.TargetModeBufferOutputCount = fill as u32;
349363
STATUS_SUCCESS
@@ -356,11 +370,47 @@ pub unsafe extern "C" fn evt_commit_modes2(
356370
STATUS_SUCCESS
357371
}
358372

373+
/// CAN_PROCESS_FP16 contract (IddCx 1.10): the OS provides the HDR 3x4
374+
/// color matrix here for adapters that advertise FP16 processing. Our
375+
/// pipeline is pass-through — ring consumers receive the composed FP16
376+
/// scRGB desktop verbatim and the host encoder performs the colorspace
377+
/// conversion — so the matrix is acknowledged and traced, not applied to
378+
/// pixels. GammaSupport is declared SOFTWARE to match.
379+
pub unsafe extern "C" fn evt_monitor_set_gamma_ramp(
380+
monitor: ffi::IDDCX_MONITOR,
381+
in_args: *const ffi::IDARG_IN_SET_GAMMARAMP,
382+
) -> NTSTATUS {
383+
let inp = &*in_args;
384+
let ramp_type = inp.Type as u32;
385+
tracelogging::write_event!(
386+
PROVIDER,
387+
"SetGammaRamp",
388+
level(Informational),
389+
u64("monitor", &(monitor as u64)),
390+
u32("type", &ramp_type),
391+
u32("size", &inp.GammaRampSizeInBytes)
392+
);
393+
STATUS_SUCCESS
394+
}
395+
359396
pub unsafe extern "C" fn evt_set_default_hdr_metadata(
360-
_monitor: ffi::IDDCX_MONITOR,
361-
_in_args: *const ffi::IDARG_IN_MONITOR_SET_DEFAULT_HDR_METADATA,
397+
monitor: ffi::IDDCX_MONITOR,
398+
in_args: *const ffi::IDARG_IN_MONITOR_SET_DEFAULT_HDR_METADATA,
362399
) -> NTSTATUS {
363-
// SDR-only shell: accept and ignore. Stored + used when caps::HDR10
364-
// and the phase-4 frame pipeline land.
400+
// The OS pushes the desktop's effective HDR10 static metadata here when
401+
// advanced color engages. The wire pixels already carry the composed
402+
// desktop (FP16 scRGB), so nothing needs reprocessing — trace it for
403+
// diagnostics; surfacing it to the host via GET_STATUS can come later
404+
// if the encoder wants monitor-derived mastering data.
405+
let inp = &*in_args;
406+
let meta_type = inp.Type as u32;
407+
tracelogging::write_event!(
408+
PROVIDER,
409+
"SetDefaultHdrMetaData",
410+
level(Informational),
411+
u64("monitor", &(monitor as u64)),
412+
u32("type", &meta_type),
413+
u32("size", &inp.Size)
414+
);
365415
STATUS_SUCCESS
366416
}

crates/luminal-vgd-driver/src/shell/ring.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use windows::Win32::Graphics::Direct3D11::{
2525
D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX, D3D11_RESOURCE_MISC_SHARED_NTHANDLE,
2626
D3D11_TEXTURE2D_DESC, D3D11_USAGE_DEFAULT,
2727
};
28-
use windows::Win32::Graphics::Dxgi::Common::{DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_SAMPLE_DESC};
28+
use windows::Win32::Graphics::Dxgi::Common::{DXGI_FORMAT, DXGI_SAMPLE_DESC};
2929
use windows::Win32::Graphics::Dxgi::{
3030
IDXGIKeyedMutex, IDXGIResource1, DXGI_SHARED_RESOURCE_READ, DXGI_SHARED_RESOURCE_WRITE,
3131
};
@@ -298,22 +298,25 @@ pub fn acquire_mutex(mutex: &IDXGIKeyedMutex, key: u64, timeout_ms: u32) -> Acqu
298298
}
299299

300300
/// Create the `count` named shared textures for (`session_id`,
301-
/// `generation`) at the given dimensions. BGRA8 (the IddCx frame surface
302-
/// format); keyed mutex starts at key 0 (driver-writable).
301+
/// `generation`) at the given dimensions and format. The format follows
302+
/// the acquired swapchain frame's desc — BGRA8 for SDR desktops, FP16
303+
/// (scRGB) or R10G10B10A2 once the OS composes the display in advanced
304+
/// color; keyed mutex starts at key 0 (driver-writable).
303305
pub fn create_shared_textures(
304306
device: &ID3D11Device,
305307
session_id: u64,
306308
generation: u32,
307309
count: u32,
308310
width: u32,
309311
height: u32,
312+
format: DXGI_FORMAT,
310313
) -> windows::core::Result<Vec<SharedTexture>> {
311314
let desc = D3D11_TEXTURE2D_DESC {
312315
Width: width,
313316
Height: height,
314317
MipLevels: 1,
315318
ArraySize: 1,
316-
Format: DXGI_FORMAT_B8G8R8A8_UNORM,
319+
Format: format,
317320
SampleDesc: DXGI_SAMPLE_DESC { Count: 1, Quality: 0 },
318321
Usage: D3D11_USAGE_DEFAULT,
319322
BindFlags: (D3D11_BIND_RENDER_TARGET.0 | D3D11_BIND_SHADER_RESOURCE.0) as u32,

crates/luminal-vgd-driver/src/shell/swapchain.rs

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ pub(crate) struct FrameRing {
5555
textures: Vec<SharedTexture>,
5656
tex_width: u32,
5757
tex_height: u32,
58+
/// DXGI_FORMAT raw value of the current textures; the OS switches the
59+
/// swapchain surface format when advanced color toggles (BGRA8 ⇄ FP16),
60+
/// which retires textures exactly like a size change.
61+
tex_format: u32,
5862
/// Per-slot: has this slot ever been published this generation? A
5963
/// never-published slot's keyed mutex is still at its creation key
6064
/// (0, driver); after the first publish it lives at key 1 (host).
@@ -88,6 +92,7 @@ impl FrameRing {
8892
textures: Vec::new(),
8993
tex_width: 0,
9094
tex_height: 0,
95+
tex_format: 0,
9196
ever_published: vec![false; slots],
9297
assigned_before: false,
9398
}
@@ -99,6 +104,7 @@ impl FrameRing {
99104
self.textures.clear();
100105
self.tex_width = 0;
101106
self.tex_height = 0;
107+
self.tex_format = 0;
102108
self.ever_published.iter_mut().for_each(|b| *b = false);
103109
let generation = self.policy.rebuild();
104110
if let Some(s) = &self.section {
@@ -299,9 +305,16 @@ fn frame_loop(
299305
last_heartbeat = Instant::now();
300306
}
301307

302-
let mut out: ffi::IDARG_OUT_RELEASEANDACQUIREBUFFER = unsafe { zeroed() };
303-
let status =
304-
unsafe { bindings::swapchain_release_and_acquire_buffer(swapchain.0.cast(), &mut out) };
308+
// Buffer2 is mandatory for CAN_PROCESS_FP16 adapters (IddCx 1.10):
309+
// METADATA2 carries per-frame HDR metadata / surface color space /
310+
// SDR white level alongside the same surface + QPC fields.
311+
let mut in_args: ffi::IDARG_IN_RELEASEANDACQUIREBUFFER2 = unsafe { zeroed() };
312+
in_args.Size = size_of::<ffi::IDARG_IN_RELEASEANDACQUIREBUFFER2>() as u32;
313+
let mut out: ffi::IDARG_OUT_RELEASEANDACQUIREBUFFER2 = unsafe { zeroed() };
314+
out.MetaData.Size = size_of::<ffi::IDDCX_METADATA2>() as u32;
315+
let status = unsafe {
316+
bindings::swapchain_release_and_acquire_buffer2(swapchain.0.cast(), &mut in_args, &mut out)
317+
};
305318
if status == STATUS_PENDING || status == E_PENDING {
306319
unsafe {
307320
let _ = WaitForSingleObject(HANDLE(frame_event.0), 100);
@@ -370,7 +383,7 @@ fn publish_frame(
370383
ring: &mut FrameRing,
371384
device: &ID3D11Device,
372385
context: &ID3D11DeviceContext,
373-
meta: &ffi::IDDCX_METADATA,
386+
meta: &ffi::IDDCX_METADATA2,
374387
) -> windows::core::Result<()> {
375388
if ring.section.is_none() {
376389
return Ok(()); // transport disabled; drain-only
@@ -387,9 +400,14 @@ fn publish_frame(
387400
unsafe { frame_tex.GetDesc(&mut desc) };
388401

389402
// Lazy texture (re)creation: first frame, or the committed mode
390-
// changed size. A size change is a full generation bump so the host
391-
// re-opens textures by name.
392-
if ring.textures.is_empty() || desc.Width != ring.tex_width || desc.Height != ring.tex_height {
403+
// changed size, or the surface format changed (advanced-color toggle
404+
// switches BGRA8 ⇄ FP16). Any change is a full generation bump so the
405+
// host re-opens textures by name and re-latches the format.
406+
if ring.textures.is_empty()
407+
|| desc.Width != ring.tex_width
408+
|| desc.Height != ring.tex_height
409+
|| desc.Format.0 as u32 != ring.tex_format
410+
{
393411
if !ring.textures.is_empty() {
394412
ring.retire_textures();
395413
}
@@ -401,17 +419,20 @@ fn publish_frame(
401419
slots,
402420
desc.Width,
403421
desc.Height,
422+
desc.Format,
404423
)?;
405424
ring.tex_width = desc.Width;
406425
ring.tex_height = desc.Height;
426+
ring.tex_format = desc.Format.0 as u32;
407427
tracelogging::write_event!(
408428
PROVIDER,
409429
"RingTexturesCreated",
410430
level(Informational),
411431
u64("session", &session_id),
412432
u32("generation", &ring.policy.generation),
413433
u32("width", &desc.Width),
414-
u32("height", &desc.Height)
434+
u32("height", &desc.Height),
435+
u32("format", &(desc.Format.0 as u32))
415436
);
416437
}
417438

crates/luminal-vgd-ffi/include/luminal_vgd.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,15 @@
2020
*/
2121
#define VGD_ERR_IO -1000
2222

23+
/**
24+
* Capability bits of `VgdCaps.caps` the backend gates on (literal
25+
* mirrors of proto `caps::*`; the asserts below keep them honest —
26+
* cbindgen cannot evaluate cross-crate constants).
27+
*/
28+
#define VGD_CAP_HDR10 1
29+
30+
#define VGD_CAP_SDR10_BIT 4
31+
2332
typedef struct VgdDeviceHandle VgdDeviceHandle;
2433

2534
typedef struct VgdRingHandle VgdRingHandle;

crates/luminal-vgd-ffi/src/lib.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@ use luminal_vgd_host::device::{RingView, VgdDevice};
3131
/// negative proto `err::*` code from the driver.
3232
pub const VGD_ERR_IO: i32 = -1000;
3333

34+
/// Capability bits of `VgdCaps.caps` the backend gates on (literal
35+
/// mirrors of proto `caps::*`; the asserts below keep them honest —
36+
/// cbindgen cannot evaluate cross-crate constants).
37+
pub const VGD_CAP_HDR10: u32 = 1;
38+
pub const VGD_CAP_SDR10_BIT: u32 = 4;
39+
const _: () = assert!(VGD_CAP_HDR10 == luminal_driver_proto::caps::HDR10);
40+
const _: () = assert!(VGD_CAP_SDR10_BIT == luminal_driver_proto::caps::SDR10_BIT);
41+
3442
pub struct VgdDeviceHandle(VgdDevice);
3543
pub struct VgdRingHandle(RingView);
3644

0 commit comments

Comments
 (0)