aboutsummaryrefslogtreecommitdiff
path: root/adapters/fparkan-render-vulkan/src/ffi
diff options
context:
space:
mode:
Diffstat (limited to 'adapters/fparkan-render-vulkan/src/ffi')
-rw-r--r--adapters/fparkan-render-vulkan/src/ffi/smoke.rs26
-rw-r--r--adapters/fparkan-render-vulkan/src/ffi/smoke_types.rs36
2 files changed, 59 insertions, 3 deletions
diff --git a/adapters/fparkan-render-vulkan/src/ffi/smoke.rs b/adapters/fparkan-render-vulkan/src/ffi/smoke.rs
index 5db7e3b..63066b4 100644
--- a/adapters/fparkan-render-vulkan/src/ffi/smoke.rs
+++ b/adapters/fparkan-render-vulkan/src/ffi/smoke.rs
@@ -314,6 +314,32 @@ impl VulkanSmokeRenderer {
self.swapchain_recreate_count
}
+ /// Returns the camera that will be uploaded for the next recorded frame.
+ #[must_use]
+ pub const fn camera(&self) -> super::VulkanStaticCamera {
+ self.camera
+ }
+
+ /// Replaces the camera for subsequent frames without recreating GPU resources.
+ ///
+ /// # Errors
+ ///
+ /// Returns [`VulkanSmokeRendererError::InvalidStaticCamera`] when a matrix
+ /// element is non-finite. The caller must update only between
+ /// [`Self::draw_frame`] calls on the renderer-owning thread.
+ pub fn set_camera(
+ &mut self,
+ camera: super::VulkanStaticCamera,
+ ) -> Result<(), VulkanSmokeRendererError> {
+ if !camera.is_finite() {
+ return Err(VulkanSmokeRendererError::InvalidStaticCamera {
+ context: "non-finite clip_from_world matrix",
+ });
+ }
+ self.camera = camera;
+ Ok(())
+ }
+
/// Explicitly idles and tears down the renderer while the native window is still alive.
///
/// # Errors
diff --git a/adapters/fparkan-render-vulkan/src/ffi/smoke_types.rs b/adapters/fparkan-render-vulkan/src/ffi/smoke_types.rs
index 14301f2..47cab7d 100644
--- a/adapters/fparkan-render-vulkan/src/ffi/smoke_types.rs
+++ b/adapters/fparkan-render-vulkan/src/ffi/smoke_types.rs
@@ -72,9 +72,23 @@ impl VulkanStaticCamera {
) -> Option<Self> {
let view = transform.try_direct3d7_view_row_major()?;
let projection = projection.try_direct3d7_projection_row_major()?;
- Some(Self {
- clip_from_world: multiply_row_major(view, projection),
- })
+ Self::from_row_major_view_projection(view, projection)
+ }
+
+ /// Builds a camera from finite row-major view and projection matrices.
+ ///
+ /// This is the runtime-facing form of the contract. It is intentionally
+ /// independent of the D3D7 recovery path so a future native camera
+ /// controller can submit its per-frame matrices without recreating Vulkan
+ /// resources.
+ #[must_use]
+ pub fn from_row_major_view_projection(view: [f32; 16], projection: [f32; 16]) -> Option<Self> {
+ view.iter()
+ .chain(projection.iter())
+ .all(|value| value.is_finite())
+ .then_some(Self {
+ clip_from_world: multiply_row_major(view, projection),
+ })
}
/// Returns whether every matrix element is finite.
@@ -344,6 +358,22 @@ mod static_mesh_tests {
}
#[test]
+ fn static_camera_accepts_finite_runtime_matrices_and_rejects_nan() {
+ let identity = VulkanStaticCamera::default().clip_from_world;
+
+ assert_eq!(
+ VulkanStaticCamera::from_row_major_view_projection(identity, identity),
+ Some(VulkanStaticCamera::default())
+ );
+ let mut invalid = identity;
+ invalid[5] = f32::NAN;
+ assert_eq!(
+ VulkanStaticCamera::from_row_major_view_projection(invalid, identity),
+ None
+ );
+ }
+
+ #[test]
fn static_mesh_rejects_bad_triangle_topology_and_indices() {
let no_vertices = VulkanStaticMesh {
vertices: Vec::new(),