Skip to main content

qualia_core_db/render/
control.rs

1//! Interface Control Plane (ICP) — fixed-size `PortalControlCommand` envelopes.
2//!
3//! Hot path: packed `u64` SPSC ring; producers (local HID, remote relay) push;
4//! `QualiaPortal::tick` drains and applies. Separate from Sonic Token `0x53` magic.
5
6use std::cell::UnsafeCell;
7use std::sync::atomic::{AtomicU32, Ordering};
8
9pub const ICP_MAGIC_BIT: u64 = 1u64 << 63;
10
11pub const OP_SET_CAMERA_DELTA: u8 = 0x60;
12pub const OP_NAVIGATE_INDEX: u8 = 0x61;
13pub const OP_COLLAPSE_Q: u8 = 0x62;
14pub const OP_SET_STANDPOINT_SCALAR: u8 = 0x63;
15pub const OP_MENU_ACTION: u8 = 0x64;
16pub const OP_SONIC_TOKEN_FORWARD: u8 = 0x65;
17pub const OP_SWIPE_GESTURE: u8 = 0x66;
18pub const OP_BUTTON_ACTION: u8 = 0x67;
19pub const OP_TILT_FRAME: u8 = 0x68;
20
21pub const MENU_ACTION_HOME: u16 = 1;
22pub const MENU_ACTION_SONIFY_TOGGLE: u16 = 2;
23
24pub const STANDPOINT_SCALAR_T_SLICE: u8 = 0;
25pub const STANDPOINT_SCALAR_T_WINDOW: u8 = 1;
26pub const STANDPOINT_SCALAR_EPISTEMIC_Q: u8 = 2;
27
28pub const CONTROL_RING_CAP: usize = 256;
29
30/// Packed ICP command (8 bytes).
31#[repr(transparent)]
32#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33pub struct PortalControlCommand {
34    pub raw: u64,
35}
36
37impl PortalControlCommand {
38    #[inline]
39    pub const fn empty() -> Self {
40        Self { raw: 0 }
41    }
42
43    #[inline]
44    pub fn opcode(self) -> u8 {
45        (self.raw as u8) & 0x7f
46    }
47
48    #[inline]
49    pub fn has_icp_magic(self) -> bool {
50        (self.raw & ICP_MAGIC_BIT) != 0
51    }
52
53    #[inline]
54    pub fn tensor_or_menu_index(self) -> u16 {
55        ((self.raw >> 16) & 0xffff) as u16
56    }
57
58    #[inline]
59    pub fn param_a_i16(self) -> i16 {
60        ((self.raw >> 32) & 0xffff) as i16
61    }
62
63    #[inline]
64    pub fn param_b_i8(self) -> i8 {
65        ((self.raw >> 48) & 0xff) as i8
66    }
67
68    #[inline]
69    pub fn channel(self) -> u8 {
70        ((self.raw >> 8) & 0xff) as u8
71    }
72
73    #[inline]
74    pub fn with_magic(mut self) -> Self {
75        self.raw |= ICP_MAGIC_BIT;
76        self
77    }
78
79    #[inline]
80    pub fn pack(opcode: u8, channel: u8, index: u16, param_a: i16, param_b: i8) -> Self {
81        let raw = (opcode as u64)
82            | ((channel as u64) << 8)
83            | ((index as u64) << 16)
84            | ((param_a as u64 & 0xffff) << 32)
85            | (((param_b as i64 as u64) & 0xff) << 48)
86            | ICP_MAGIC_BIT;
87        Self { raw }
88    }
89
90    /// Pack camera deltas: `param_a` = dyaw×1000, index = dpitch×1000 (i16), `param_b` = dzoom×1000.
91    #[inline]
92    pub fn set_camera_delta_scaled(dyaw: f32, dpitch: f32, dzoom: f32) -> Self {
93        let ya = (dyaw * 1000.0).clamp(-32767.0, 32767.0) as i16;
94        let pi = (dpitch * 1000.0).clamp(-32767.0, 32767.0) as i16;
95        let zo = (dzoom * 1000.0).clamp(-127.0, 127.0) as i8;
96        Self::pack(OP_SET_CAMERA_DELTA, 0, pi as u16, ya, zo).with_magic()
97    }
98
99    #[inline]
100    pub fn decode_camera_delta(self) -> (f32, f32, f32) {
101        let dyaw = self.param_a_i16() as f32 / 1000.0;
102        let dpitch = self.tensor_or_menu_index() as i16 as f32 / 1000.0;
103        let dzoom = self.param_b_i8() as f32 / 1000.0;
104        (dyaw, dpitch, dzoom)
105    }
106
107    #[inline]
108    pub fn navigate_index(index: u16) -> Self {
109        Self::pack(OP_NAVIGATE_INDEX, 0, index, 0, 0).with_magic()
110    }
111
112    #[inline]
113    pub fn collapse_q(index: u16) -> Self {
114        Self::pack(OP_COLLAPSE_Q, 0, index, 0, 0).with_magic()
115    }
116
117    #[inline]
118    pub fn menu_action(menu_id: u16) -> Self {
119        Self::pack(OP_MENU_ACTION, 0, menu_id, 0, 0).with_magic()
120    }
121
122    #[inline]
123    pub fn standpoint_scalar(kind: u8, delta: f32) -> Self {
124        let scaled = (delta * 1000.0).clamp(-32767.0, 32767.0) as i16;
125        Self::pack(OP_SET_STANDPOINT_SCALAR, kind, 0, scaled, 0).with_magic()
126    }
127
128    /// Embed a Sonic Token payload (bits 0..62); opcode + ICP magic in low byte / bit 63.
129    #[inline]
130    pub fn sonic_token_forward(token_raw: u64) -> Self {
131        let body = token_raw & 0x7fff_ffff_ffff_ff00;
132        Self {
133            raw: body | (OP_SONIC_TOKEN_FORWARD as u64) | ICP_MAGIC_BIT,
134        }
135    }
136
137    #[inline]
138    pub fn embedded_sonic_raw(self) -> u64 {
139        self.raw & 0x7fff_ffff_ffff_ff00
140    }
141}
142
143/// Fixed-capacity SPSC ring of packed control commands.
144pub struct ControlCommandRing {
145    slots: UnsafeCell<[u64; CONTROL_RING_CAP]>,
146    write_seq: AtomicU32,
147    read_seq: AtomicU32,
148}
149
150unsafe impl Sync for ControlCommandRing {}
151
152impl ControlCommandRing {
153    pub const fn new() -> Self {
154        Self {
155            slots: UnsafeCell::new([0u64; CONTROL_RING_CAP]),
156            write_seq: AtomicU32::new(0),
157            read_seq: AtomicU32::new(0),
158        }
159    }
160
161    #[inline]
162    pub fn len(&self) -> usize {
163        let w = self.write_seq.load(Ordering::Acquire);
164        let r = self.read_seq.load(Ordering::Acquire);
165        w.wrapping_sub(r) as usize
166    }
167
168    pub fn try_push(&self, cmd: PortalControlCommand) -> bool {
169        let w = self.write_seq.load(Ordering::Relaxed);
170        let r = self.read_seq.load(Ordering::Acquire);
171        if w.wrapping_sub(r) >= CONTROL_RING_CAP as u32 {
172            return false;
173        }
174        let slot = (w % CONTROL_RING_CAP as u32) as usize;
175        unsafe {
176            (*self.slots.get())[slot] = cmd.raw;
177        }
178        self.write_seq.store(w.wrapping_add(1), Ordering::Release);
179        true
180    }
181
182    pub fn try_pop(&self) -> Option<PortalControlCommand> {
183        let r = self.read_seq.load(Ordering::Relaxed);
184        let w = self.write_seq.load(Ordering::Acquire);
185        if r == w {
186            return None;
187        }
188        let slot = (r % CONTROL_RING_CAP as u32) as usize;
189        let raw = unsafe { (*self.slots.get())[slot] };
190        self.read_seq.store(r.wrapping_add(1), Ordering::Release);
191        Some(PortalControlCommand { raw })
192    }
193}
194
195static CONTROL_RING: ControlCommandRing = ControlCommandRing::new();
196
197#[inline]
198pub fn control_ring() -> &'static ControlCommandRing {
199    &CONTROL_RING
200}
201
202#[inline]
203pub fn push_control_command(cmd: PortalControlCommand) -> bool {
204    control_ring().try_push(cmd)
205}
206
207#[inline]
208pub fn push_control_raw(raw: u64) -> bool {
209    push_control_command(PortalControlCommand { raw })
210}
211
212#[inline]
213pub fn pop_control_command() -> Option<PortalControlCommand> {
214    control_ring().try_pop()
215}
216
217#[inline]
218pub fn control_pending() -> u32 {
219    control_ring().len() as u32
220}
221
222#[cfg(test)]
223mod tests {
224    use super::*;
225
226    fn fresh_ring() -> ControlCommandRing {
227        ControlCommandRing::new()
228    }
229
230    #[test]
231    fn icp_pack_magic_and_opcode() {
232        let cmd = PortalControlCommand::navigate_index(42);
233        assert!(cmd.has_icp_magic());
234        assert_eq!(cmd.opcode(), OP_NAVIGATE_INDEX);
235        assert_eq!(cmd.tensor_or_menu_index(), 42);
236    }
237
238    #[test]
239    fn camera_delta_roundtrip() {
240        let cmd = PortalControlCommand::set_camera_delta_scaled(0.05, -0.02, 0.1);
241        let (y, p, z) = cmd.decode_camera_delta();
242        assert!((y - 0.05).abs() < 0.002);
243        assert!((p - (-0.02)).abs() < 0.002);
244        assert!((z - 0.1).abs() < 0.002);
245    }
246
247    #[test]
248    fn control_ring_push_pop() {
249        let ring = fresh_ring();
250        assert!(ring.try_push(PortalControlCommand::menu_action(MENU_ACTION_HOME)));
251        let popped = ring.try_pop().expect("cmd");
252        assert_eq!(popped.opcode(), OP_MENU_ACTION);
253    }
254
255    #[test]
256    fn opcode_constants_distinct_from_sonic() {
257        assert_ne!(OP_SET_CAMERA_DELTA, 0x53);
258        assert_ne!(OP_NAVIGATE_INDEX, OP_COLLAPSE_Q);
259    }
260}