diff --git a/crates/reco-autocam/src/lib.rs b/crates/reco-autocam/src/lib.rs index b2d178a7..cc9231da 100644 --- a/crates/reco-autocam/src/lib.rs +++ b/crates/reco-autocam/src/lib.rs @@ -201,6 +201,9 @@ pub fn setup_autocam( let detection_interval = config.detection_interval; let tracking_mode = config.tracking_mode; let field_roi = config.field_roi.as_ref(); + // Only read by the tensorrt-native path and the Linux ORT-GPU path + // below - an ort-only Windows build has neither compiled in. + #[cfg(any(feature = "tensorrt-native", all(feature = "ort", target_os = "linux")))] let is_10bit = config.is_10bit; let mut detection_active = false; diff --git a/crates/reco-core/src/interop/d3d11.rs b/crates/reco-core/src/interop/d3d11.rs index 4b6f7313..3fdb509d 100644 --- a/crates/reco-core/src/interop/d3d11.rs +++ b/crates/reco-core/src/interop/d3d11.rs @@ -378,7 +378,13 @@ impl D3d11StagingPool { /// /// Performs `CopySubresourceRegion` (GPU-to-GPU, ~0.2ms) then waits /// for completion via an event query before returning. - pub fn stage_frame( + /// + /// # Safety + /// + /// `src_texture`, if non-null, must be a valid `ID3D11Texture2D*` COM + /// pointer (as produced by FFmpeg's D3D11VA decode path via + /// `AVFrame::data[0]`) that stays alive for the duration of this call. + pub unsafe fn stage_frame( &mut self, src_texture: *mut c_void, array_slice: usize, diff --git a/crates/reco-core/src/session/detection_dispatch.rs b/crates/reco-core/src/session/detection_dispatch.rs index cfbc19d5..d70b94d4 100644 --- a/crates/reco-core/src/session/detection_dispatch.rs +++ b/crates/reco-core/src/session/detection_dispatch.rs @@ -75,7 +75,10 @@ fn cpu_frames<'a>( } /// Per-camera CUDA NV12 frames from the shared-texture slot pointers. -#[cfg(any(target_os = "linux", target_os = "windows"))] +/// +/// Linux only - the only caller (the `GpuResident` zero-copy match arm) +/// is `#[cfg(target_os = "linux")]`. +#[cfg(target_os = "linux")] pub(super) fn cuda_nv12_frames( left_buf: &crate::interop::zero_copy::GpuBufInfo, right_buf: &crate::interop::zero_copy::GpuBufInfo, @@ -506,7 +509,10 @@ impl StitchSession { } /// Run GPU-resident detection from CUDA NV12 shared textures. - #[cfg(any(target_os = "linux", target_os = "windows"))] + /// + /// Linux only - the only caller (the `GpuResident` zero-copy match + /// arm) is `#[cfg(target_os = "linux")]`. + #[cfg(target_os = "linux")] pub(crate) fn detect_and_update_director_gpu( &mut self, left_buf: &crate::interop::zero_copy::GpuBufInfo, diff --git a/crates/reco-core/src/session/frame_processing.rs b/crates/reco-core/src/session/frame_processing.rs index 8e416881..0453fcce 100644 --- a/crates/reco-core/src/session/frame_processing.rs +++ b/crates/reco-core/src/session/frame_processing.rs @@ -585,8 +585,12 @@ impl StitchSession { let right_pool_slot = left_pool_slot + 2; let pool = self.d3d11_staging_pool.as_mut().unwrap(); - pool.stage_frame(left_texture, left_slice, left_pool_slot)?; - pool.stage_frame(right_texture, right_slice, right_pool_slot)?; + // SAFETY: left_texture/right_texture are FFmpeg D3D11VA decode + // textures (AVFrame::data[0]) valid for this call's duration. + unsafe { + pool.stage_frame(left_texture, left_slice, left_pool_slot)?; + pool.stage_frame(right_texture, right_slice, right_pool_slot)?; + } Ok(first_frame) } @@ -904,8 +908,12 @@ impl StitchSession { let pool = self.d3d11_staging_pool.as_mut().unwrap(); let left_slot = (produce_index as usize * 2) % pool.n_slots(); let right_slot = (produce_index as usize * 2 + 1) % pool.n_slots(); - pool.stage_frame(*left_texture, *left_slice, left_slot)?; - pool.stage_frame(*right_texture, *right_slice, right_slot)?; + // SAFETY: left_texture/right_texture are FFmpeg D3D11VA decode + // textures (AVFrame::data[0]) valid for this call's duration. + unsafe { + pool.stage_frame(*left_texture, *left_slice, left_slot)?; + pool.stage_frame(*right_texture, *right_slice, right_slot)?; + } return Ok(Some(left_slot)); } Ok(None) diff --git a/crates/reco-core/src/session/mod.rs b/crates/reco-core/src/session/mod.rs index 84059ad3..81805f35 100644 --- a/crates/reco-core/src/session/mod.rs +++ b/crates/reco-core/src/session/mod.rs @@ -113,8 +113,9 @@ pub struct StitchSession { std::sync::mpsc::SyncSender, std::sync::mpsc::SyncSender, )>, - /// CUDA buffer info for GPU detection (GPU zero-copy). - #[cfg(any(target_os = "linux", target_os = "windows"))] + /// CUDA buffer info for GPU detection (Linux GPU zero-copy path only - + /// the only reader, `FrameLoopContext::gpu_buf_info`, is Linux-only). + #[cfg(target_os = "linux")] pub(crate) gpu_buf_info: Option<( crate::interop::zero_copy::GpuBufInfo, crate::interop::zero_copy::GpuBufInfo, @@ -263,7 +264,7 @@ impl StitchSession { gpu_bind_groups: None, #[cfg(target_os = "linux")] gpu_slot_free_tx: None, - #[cfg(any(target_os = "linux", target_os = "windows"))] + #[cfg(target_os = "linux")] gpu_buf_info: None, #[cfg(target_os = "linux")] gpu_shared_views: None, diff --git a/crates/reco-io/src/smart_source.rs b/crates/reco-io/src/smart_source.rs index e90a00ec..1acc7f5a 100644 --- a/crates/reco-io/src/smart_source.rs +++ b/crates/reco-io/src/smart_source.rs @@ -65,13 +65,15 @@ enum WindowsDecodeState { right: crate::stitch_job::InputPath, sync_offset: i64, }, + // No join_handles/shutdown flag here, unlike the Linux CUDA path + // (LinuxZeroCopyState): the D3D11VA decode threads exit via channel + // drop, with no ordering hazard on shutdown that a flag needs to + // coordinate. Running { pair_rx: std::sync::mpsc::Receiver<( crate::ffmpeg::decoder::D3d11Frame, crate::ffmpeg::decoder::D3d11Frame, )>, - join_handles: Vec>, - shutdown: std::sync::Arc, }, } @@ -107,11 +109,7 @@ impl WindowsZeroCopyState { let right = right.clone(); let sync_offset = *sync_offset; let pair_rx = crate::zero_copy::spawn_d3d11_decode_pair(&left, &right, sync_offset); - self.decode = WindowsDecodeState::Running { - pair_rx, - join_handles: Vec::new(), - shutdown: std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)), - }; + self.decode = WindowsDecodeState::Running { pair_rx }; } } } @@ -775,14 +773,14 @@ impl Drop for SmartFileSource { // channel, causing decode threads to exit when send() fails. // Without this, orphaned decode threads keep the process alive. #[cfg(target_os = "windows")] - if let SourceMode::D3d11ZeroCopy(state) = &mut self.mode { - if let WindowsDecodeState::Running { pair_rx, .. } = &mut state.decode { - drop(std::mem::replace( - pair_rx, - std::sync::mpsc::sync_channel(0).1, - )); - log::debug!("D3D11VA source dropped, decode threads signalled to exit"); - } + if let SourceMode::D3d11ZeroCopy(state) = &mut self.mode + && let WindowsDecodeState::Running { pair_rx } = &mut state.decode + { + drop(std::mem::replace( + pair_rx, + std::sync::mpsc::sync_channel(0).1, + )); + log::debug!("D3D11VA source dropped, decode threads signalled to exit"); } // MetalZeroCopy: the Receiver is owned directly by the enum // variant and drops naturally when the mode is replaced.