feat(render): support per-frame camera updates
This commit is contained in:
@@ -314,6 +314,32 @@ impl VulkanSmokeRenderer {
|
|||||||
self.swapchain_recreate_count
|
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.
|
/// Explicitly idles and tears down the renderer while the native window is still alive.
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
|
|||||||
@@ -72,9 +72,23 @@ impl VulkanStaticCamera {
|
|||||||
) -> Option<Self> {
|
) -> Option<Self> {
|
||||||
let view = transform.try_direct3d7_view_row_major()?;
|
let view = transform.try_direct3d7_view_row_major()?;
|
||||||
let projection = projection.try_direct3d7_projection_row_major()?;
|
let projection = projection.try_direct3d7_projection_row_major()?;
|
||||||
Some(Self {
|
Self::from_row_major_view_projection(view, projection)
|
||||||
clip_from_world: multiply_row_major(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.
|
/// Returns whether every matrix element is finite.
|
||||||
@@ -343,6 +357,22 @@ mod static_mesh_tests {
|
|||||||
assert_eq!(camera.clip_from_world[7], 0.65_f32.sin());
|
assert_eq!(camera.clip_from_world[7], 0.65_f32.sin());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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]
|
#[test]
|
||||||
fn static_mesh_rejects_bad_triangle_topology_and_indices() {
|
fn static_mesh_rejects_bad_triangle_topology_and_indices() {
|
||||||
let no_vertices = VulkanStaticMesh {
|
let no_vertices = VulkanStaticMesh {
|
||||||
|
|||||||
@@ -1242,6 +1242,11 @@ impl ApplicationHandler for SmokeApp {
|
|||||||
event_loop.exit();
|
event_loop.exit();
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
if let Err(error) = renderer.set_camera(VulkanStaticCamera::default()) {
|
||||||
|
self.error = Some(error.to_string());
|
||||||
|
event_loop.exit();
|
||||||
|
return;
|
||||||
|
}
|
||||||
match renderer.draw_frame() {
|
match renderer.draw_frame() {
|
||||||
Ok(VulkanSmokeFrameOutcome::Presented) => {
|
Ok(VulkanSmokeFrameOutcome::Presented) => {
|
||||||
self.frames_presented = self.frames_presented.saturating_add(1);
|
self.frames_presented = self.frames_presented.saturating_add(1);
|
||||||
|
|||||||
@@ -1437,6 +1437,15 @@ on its XY diagnostic projection until a runtime supplies a real legacy camera;
|
|||||||
selecting source-world coordinates with identity camera would be a misleading
|
selecting source-world coordinates with identity camera would be a misleading
|
||||||
empty/off-screen viewer rather than a compatibility result.
|
empty/off-screen viewer rather than a compatibility result.
|
||||||
|
|
||||||
|
`VulkanSmokeRenderer::set_camera` now accepts a new finite camera between frame
|
||||||
|
submissions and uses it for the next command buffer without rebuilding the
|
||||||
|
swapchain, buffers, descriptors or pipelines. `VulkanStaticCamera` also accepts
|
||||||
|
generic finite row-major view/projection pairs, so the final game camera
|
||||||
|
controller need not be coupled to the D3D7 recovery type. The native Windows
|
||||||
|
smoke executes that update before each of 300 validation-clean frames. This is
|
||||||
|
the renderer-side cadence contract only; it does not yet supply a gameplay
|
||||||
|
camera controller or claim camera movement parity.
|
||||||
|
|
||||||
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
|
||||||
|
|||||||
Reference in New Issue
Block a user