diff options
Diffstat (limited to 'adapters')
| -rw-r--r-- | adapters/fparkan-render-vulkan/shaders/triangle.frag | 15 | ||||
| -rw-r--r-- | adapters/fparkan-render-vulkan/shaders/triangle.frag.spv | bin | 14512 -> 14716 bytes | |||
| -rw-r--r-- | adapters/fparkan-render-vulkan/src/ffi.rs | 2 | ||||
| -rw-r--r-- | adapters/fparkan-render-vulkan/src/ffi/packet_distance.rs | 52 | ||||
| -rw-r--r-- | adapters/fparkan-render-vulkan/src/ffi/reflection.rs | 5 | ||||
| -rw-r--r-- | adapters/fparkan-render-vulkan/src/ffi/smoke.rs | 57 | ||||
| -rw-r--r-- | adapters/fparkan-render-vulkan/src/ffi/tests.rs | 26 | ||||
| -rw-r--r-- | adapters/fparkan-render-vulkan/src/policy.rs | 2 |
8 files changed, 135 insertions, 24 deletions
diff --git a/adapters/fparkan-render-vulkan/shaders/triangle.frag b/adapters/fparkan-render-vulkan/shaders/triangle.frag index 5d10152..2c71278 100644 --- a/adapters/fparkan-render-vulkan/shaders/triangle.frag +++ b/adapters/fparkan-render-vulkan/shaders/triangle.frag @@ -82,12 +82,15 @@ void main() { vec2 bump_uv = plane_uv * frame.embm_wave.y + frame.embm_wave.x; vec2 duv = max(vec2(-1.0), (texture(embm_bump, bump_uv).rg * 255.0 - 128.0) / 127.0); vec2 displacement = duv * frame.embm_wave.zw; - // shortcut: convert native map-UV displacement to the current mirror projection; - // replace this projection when accepting the original shifted reflection camera. - vec3 displaced_world = in_world_position - vec3(displacement.yx * span, 0.0); - vec4 displaced_clip = frame.clip_from_world * vec4(displaced_world, 1.0); - if (displaced_clip.w > 0.000001) { - reflection_uv = displaced_clip.xy / displaced_clip.w * 0.5 + 0.5; + if ((mode & 128) != 0) { + reflection_uv = plane_uv + displacement; + } else { + // shortcut: non-perspective API cameras retain the projective mirror fallback. + vec3 displaced_world = in_world_position - vec3(displacement.yx * span, 0.0); + vec4 displaced_clip = frame.clip_from_world * vec4(displaced_world, 1.0); + if (displaced_clip.w > 0.000001) { + reflection_uv = displaced_clip.xy / displaced_clip.w * 0.5 + 0.5; + } } vec3 rgb = texture(reflection_color, reflection_uv).rgb * in_base_diffuse + in_base_specular; out_color = vec4(mix(rgb * in_color, frame.fog_color.rgb, in_fog_factor), material.diffuse_alpha); diff --git a/adapters/fparkan-render-vulkan/shaders/triangle.frag.spv b/adapters/fparkan-render-vulkan/shaders/triangle.frag.spv Binary files differindex de3ace6..6deef9d 100644 --- a/adapters/fparkan-render-vulkan/shaders/triangle.frag.spv +++ b/adapters/fparkan-render-vulkan/shaders/triangle.frag.spv diff --git a/adapters/fparkan-render-vulkan/src/ffi.rs b/adapters/fparkan-render-vulkan/src/ffi.rs index 030b8ac..04be2d3 100644 --- a/adapters/fparkan-render-vulkan/src/ffi.rs +++ b/adapters/fparkan-render-vulkan/src/ffi.rs @@ -11,6 +11,7 @@ mod asset_mesh; mod capabilities; mod instance; +mod packet_distance; mod reflection; mod resources; mod runtime; @@ -50,6 +51,7 @@ pub use self::instance::{ }; #[cfg(test)] use self::instance::{cstring_vec, ensure_instance_extensions_available}; +pub use self::packet_distance::legacy_packet_distance_squared; use self::resources::{ color_subresource_range, create_command_pool, create_depth_attachment, create_frame_sync, create_frame_uniform_buffer, create_index_buffer, create_material_specular_buffer, diff --git a/adapters/fparkan-render-vulkan/src/ffi/packet_distance.rs b/adapters/fparkan-render-vulkan/src/ffi/packet_distance.rs new file mode 100644 index 0000000..b6b8938 --- /dev/null +++ b/adapters/fparkan-render-vulkan/src/ffi/packet_distance.rs @@ -0,0 +1,52 @@ +#![allow(unsafe_code)] +//! The native CPU instruction used for render-packet distance keys. + +/// Reproduces Ngi32's squared length multiplied by `RSQRTSS` on x86-64. +/// Other architectures retain the portable distance and report that boundary. +#[must_use] +pub fn legacy_packet_distance_squared(squared: f32) -> Option<f32> { + if !squared.is_finite() || squared < 0.0 { + return None; + } + if squared == 0.0 { + static NOTICE: std::sync::Once = std::sync::Once::new(); + NOTICE.call_once(|| { + eprintln!("render_coverage packet_zero_distance_fallback=1 (stable zero instead of native 0 * infinity)"); + }); + return Some(0.0); + } + #[cfg(target_arch = "x86_64")] + let distance = { + use core::arch::x86_64::{_mm_cvtss_f32, _mm_rsqrt_ss, _mm_set_ss}; + // SAFETY: SSE is part of the x86-64 baseline; these intrinsics only + // operate on register values and do not access memory. + let inverse = unsafe { _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ss(squared))) }; + squared * inverse + }; + #[cfg(not(target_arch = "x86_64"))] + let distance = { + static NOTICE: std::sync::Once = std::sync::Once::new(); + NOTICE.call_once(|| { + eprintln!("render_coverage packet_distance_native_rsqrt_unsupported=1 (using portable square root)"); + }); + squared.sqrt() + }; + distance.is_finite().then_some(distance) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn packet_distance_preserves_the_native_approximation_error_bound() { + for squared in [0.000_001_f32, 1.0, 9.0, 11_393.854, 1_000_000.0] { + let distance = legacy_packet_distance_squared(squared).unwrap(); + assert!((distance / squared.sqrt() - 1.0).abs() < 0.000_3); + } + assert_eq!(legacy_packet_distance_squared(0.0), Some(0.0)); + for invalid in [-1.0, f32::NAN, f32::INFINITY] { + assert_eq!(legacy_packet_distance_squared(invalid), None); + } + } +} diff --git a/adapters/fparkan-render-vulkan/src/ffi/reflection.rs b/adapters/fparkan-render-vulkan/src/ffi/reflection.rs index 62a0d1e..1a9c9ae 100644 --- a/adapters/fparkan-render-vulkan/src/ffi/reflection.rs +++ b/adapters/fparkan-render-vulkan/src/ffi/reflection.rs @@ -221,6 +221,9 @@ impl ReflectionResources { depth_request: fparkan_platform::DepthStencilSupport, render_pass: vk::RenderPass, ) -> Result<(), VulkanSmokeRendererError> { + let side = fparkan_render::water::legacy_embm_reflection_size([extent.0, extent.1]); + let extent = (side, side); + eprintln!("render_reflection target_extent={side}x{side}"); let info = vk::ImageCreateInfo::default() .image_type(vk::ImageType::TYPE_2D) .format(vk::Format::from_raw(format)) @@ -236,7 +239,7 @@ impl ReflectionResources { .usage(vk::ImageUsageFlags::COLOR_ATTACHMENT | vk::ImageUsageFlags::SAMPLED) .sharing_mode(vk::SharingMode::EXCLUSIVE) .initial_layout(vk::ImageLayout::UNDEFINED); - // SAFETY: This target uses the validated non-zero swapchain extent and format. + // SAFETY: The validated swapchain format is retained; the square side is in 2..=1024. let image = unsafe { self.device.create_image(&info, None) }.map_err(|result| { VulkanSmokeRendererError::VulkanOperation { context: "vkCreateImage(reflection)", diff --git a/adapters/fparkan-render-vulkan/src/ffi/smoke.rs b/adapters/fparkan-render-vulkan/src/ffi/smoke.rs index bd745ad..a016645 100644 --- a/adapters/fparkan-render-vulkan/src/ffi/smoke.rs +++ b/adapters/fparkan-render-vulkan/src/ffi/smoke.rs @@ -373,7 +373,7 @@ impl VulkanSmokeRenderer { plane_heights.sort_unstable(); plane_heights.dedup(); if !plane_heights.is_empty() { - eprintln!("render_coverage planar_reflection_shifted_camera_unsupported={} (native bump map and clock; using a projective mirror until shifted-camera mapping is implemented)", plane_heights.len()); + eprintln!("render_coverage planar_reflection_render_context_approx={} (native shifted window, target size and bump UV; main-view depth range and sky/fog/FX remain approximations; non-perspective cameras use mirror fallback)", plane_heights.len()); eprintln!("render_coverage planar_reflection_other_planes_unsupported={} (nearest active plane is reflected)", plane_heights.len().saturating_sub(1)); } if unsupported_nonplanar != 0 { @@ -1564,11 +1564,18 @@ impl VulkanSmokeRenderer { .active_reflection_plane() .map(|height| { let mut frame = self.frame_uniforms; - frame.clip_from_world = + frame.clip_from_world = fparkan_render::water::legacy_embm_reflection_clip( + frame.clip_from_world, + frame.camera_position, + height, + frame.embm_bounds, + ) + .or_else(|| { fparkan_render::reflect_clip_from_world_about_z(frame.clip_from_world, height) - .ok_or(VulkanSmokeRendererError::InvalidStaticCamera { - context: "non-finite reflected clip transform", - })?; + }) + .ok_or(VulkanSmokeRendererError::InvalidStaticCamera { + context: "non-finite reflected clip transform", + })?; frame.camera_position[2] = 2.0 * height - frame.camera_position[2]; if !frame.is_finite() { return Err(VulkanSmokeRendererError::InvalidStaticCamera { @@ -1819,6 +1826,15 @@ impl VulkanSmokeRenderer { self.draw_order_dirty = false; } let reflection_plane = self.active_reflection_plane(); + let shifted_reflection = reflection_plane.is_some_and(|height| { + fparkan_render::water::legacy_embm_reflection_clip( + self.frame_uniforms.clip_from_world, + self.frame_uniforms.camera_position, + height, + self.frame_uniforms.embm_bounds, + ) + .is_some() + }); let device = self.device_ref()?; let swapchain = self.swapchain_ref()?; let resources = self.resources_ref()?; @@ -1858,17 +1874,27 @@ impl VulkanSmokeRenderer { }, }, ]; - let render_area = vk::Rect2D { - offset: vk::Offset2D { x: 0, y: 0 }, - extent: vk::Extent2D { - width: swapchain.report.plan.extent.0, - height: swapchain.report.plan.extent.1, - }, - }; for reflected in [true, false] { if reflected && reflection_plane.is_none() { continue; } + let extent = if reflected { + let target = resources.reflection.color.as_ref().ok_or( + VulkanSmokeRendererError::InvariantViolation { + context: "active reflection without a color target", + }, + )?; + (target.width, target.height) + } else { + swapchain.report.plan.extent + }; + let render_area = vk::Rect2D { + offset: vk::Offset2D { x: 0, y: 0 }, + extent: vk::Extent2D { + width: extent.0, + height: extent.1, + }, + }; let framebuffer = if reflected { resources.reflection.framebuffer } else { @@ -2053,14 +2079,11 @@ impl VulkanSmokeRenderer { && reflection_plane.is_some() && self.reflection_planes[range_index] == reflection_plane { - 64 + 64 | if shifted_reflection { 128 } else { 0 } } else { 0 }) as f32; - let viewport = super::swapchain_resources::draw_viewport( - swapchain.report.plan.extent, - material_mode, - ); + let viewport = super::swapchain_resources::draw_viewport(extent, material_mode); let next_viewport_depth = [viewport.min_depth, viewport.max_depth]; if viewport_depth != Some(next_viewport_depth) { device diff --git a/adapters/fparkan-render-vulkan/src/ffi/tests.rs b/adapters/fparkan-render-vulkan/src/ffi/tests.rs index b06bb26..4b2f7d2 100644 --- a/adapters/fparkan-render-vulkan/src/ffi/tests.rs +++ b/adapters/fparkan-render-vulkan/src/ffi/tests.rs @@ -419,6 +419,32 @@ fn swapchain_plan_enables_transfer_source_when_surface_supports_it() { } #[test] +fn swapchain_plan_falls_back_to_immediate_without_overriding_supported_preference() { + let mut request = swapchain_request(); + request.present_modes = vec![ + vk::PresentModeKHR::IMMEDIATE.as_raw(), + vk::PresentModeKHR::FIFO.as_raw(), + ]; + assert_eq!( + plan_vulkan_swapchain(&request).unwrap().present_mode, + vk::PresentModeKHR::IMMEDIATE.as_raw() + ); + request.preferred_present_mode = vk::PresentModeKHR::FIFO.as_raw(); + assert_eq!( + plan_vulkan_swapchain(&request).unwrap().present_mode, + vk::PresentModeKHR::FIFO.as_raw() + ); + request + .present_modes + .push(vk::PresentModeKHR::MAILBOX.as_raw()); + request.preferred_present_mode = vk::PresentModeKHR::MAILBOX.as_raw(); + assert_eq!( + plan_vulkan_swapchain(&request).unwrap().present_mode, + vk::PresentModeKHR::MAILBOX.as_raw() + ); +} + +#[test] fn swapchain_plan_uses_fifo_and_current_extent_fallbacks() { let mut request = swapchain_request(); request.preferred_present_mode = vk::PresentModeKHR::IMMEDIATE.as_raw(); diff --git a/adapters/fparkan-render-vulkan/src/policy.rs b/adapters/fparkan-render-vulkan/src/policy.rs index e1f01c8..8eceb22 100644 --- a/adapters/fparkan-render-vulkan/src/policy.rs +++ b/adapters/fparkan-render-vulkan/src/policy.rs @@ -495,6 +495,8 @@ fn undefined_surface_format_override( fn select_present_mode(present_modes: &[i32], preferred: i32) -> Result<i32, VulkanSwapchainError> { if present_modes.contains(&preferred) { Ok(preferred) + } else if present_modes.contains(&vk::PresentModeKHR::IMMEDIATE.as_raw()) { + Ok(vk::PresentModeKHR::IMMEDIATE.as_raw()) } else if present_modes.contains(&vk::PresentModeKHR::FIFO.as_raw()) { Ok(vk::PresentModeKHR::FIFO.as_raw()) } else { |
