feat(render): retain raw camera projection state

This commit is contained in:
2026-07-18 14:26:25 +04:00
parent 02bfbcff84
commit c2b3f87e2c
2 changed files with 64 additions and 0 deletions
+57
View File
@@ -142,6 +142,37 @@ pub struct RawCameraPose {
pub selector2: RawCameraTransform, pub selector2: RawCameraTransform,
} }
/// Raw projection state observed through the original `CBufferingCamera` ABI.
///
/// The five-float context block is intentionally represented as original words:
/// its indices are observed, but their semantic labels have not yet all been
/// recovered. The field-of-view value is the type-0 input to `tan(fov / 2)`.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct RawCameraProjection {
/// Viewport rectangle as `(left, top, right, bottom)`.
pub viewport: [i32; 4],
/// Original projection selector.
pub projection_type: u32,
/// Original IEEE-754 FOV value in radians.
pub field_of_view_radians_bits: u32,
/// Exact five-float context block returned by the primary renderer.
pub context_words: [u32; 5],
}
impl RawCameraProjection {
/// Returns the type-0 FOV input in radians.
#[must_use]
pub fn field_of_view_radians(self) -> f32 {
f32::from_bits(self.field_of_view_radians_bits)
}
/// Returns the five context values without assigning semantic labels.
#[must_use]
pub fn context_values(self) -> [f32; 5] {
self.context_words.map(f32::from_bits)
}
}
/// Immutable camera data visible to command generation. /// Immutable camera data visible to command generation.
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct CameraSnapshot { pub struct CameraSnapshot {
@@ -155,6 +186,11 @@ pub struct CameraSnapshot {
/// conventions have been recovered; it preserves the ABI boundary for the /// conventions have been recovered; it preserves the ABI boundary for the
/// runtime adapter and deterministic captures. /// runtime adapter and deterministic captures.
pub raw_pose: Option<RawCameraPose>, pub raw_pose: Option<RawCameraPose>,
/// Optional unconverted source-projection state.
///
/// This is retained independently of `projection`, because its legacy
/// context words have not yet been mapped to a Vulkan clip convention.
pub raw_projection: Option<RawCameraProjection>,
} }
impl Default for CameraSnapshot { impl Default for CameraSnapshot {
@@ -163,6 +199,7 @@ impl Default for CameraSnapshot {
view: identity_transform(), view: identity_transform(),
projection: identity_transform(), projection: identity_transform(),
raw_pose: None, raw_pose: None,
raw_projection: None,
} }
} }
} }
@@ -874,6 +911,26 @@ mod tests {
assert_eq!(CameraSnapshot::default().raw_pose, None); assert_eq!(CameraSnapshot::default().raw_pose, None);
} }
#[test]
fn raw_camera_projection_preserves_live_context_words_without_labeling_them() {
let projection = RawCameraProjection {
viewport: [0, 0, 1024, 768],
projection_type: 0,
field_of_view_radians_bits: 1.04_f32.to_bits(),
context_words: [
0.0_f32.to_bits(),
0.5_f32.to_bits(),
700.0_f32.to_bits(),
0.1_f32.to_bits(),
0.99_f32.to_bits(),
],
};
assert_eq!(projection.field_of_view_radians(), 1.04);
assert_eq!(projection.context_values(), [0.0, 0.5, 700.0, 0.1, 0.99]);
assert_eq!(CameraSnapshot::default().raw_projection, None);
}
#[test] #[test]
fn raw_camera_transform_inverts_only_non_singular_affine_blocks() { fn raw_camera_transform_inverts_only_non_singular_affine_blocks() {
let source = [ let source = [
+7
View File
@@ -1364,6 +1364,13 @@ is only a table lookup keyed by two still-unnamed inputs. No virtual call was
made by the probe. Therefore the block's values cannot yet be assigned as made by the probe. Therefore the block's values cannot yet be assigned as
near/far or reused by the Vulkan adapter. near/far or reused by the Vulkan adapter.
The runtime render model now retains this evidence as `RawCameraProjection` on
`CameraSnapshot`: the viewport rectangle, projection selector, exact FOV bits,
and all five context words travel through the backend-neutral boundary without
changing the current Vulkan projection. Its float accessors deliberately leave
the context fields unnamed; preserving a raw ABI value is safer than encoding a
near/far or clip-space convention before it is demonstrated.
A fresh no-input launch of the canonical `iron_3d.exe` did create a responsive A fresh no-input launch of the canonical `iron_3d.exe` did create a responsive
window titled `Parkan. Железная Стратегия`. A read-only probe then requested window titled `Parkan. Железная Стратегия`. A read-only probe then requested
`PROCESS_QUERY_INFORMATION | PROCESS_VM_READ` and attempted to read the known `PROCESS_QUERY_INFORMATION | PROCESS_VM_READ` and attempted to read the known