From d045c4f81719c6eb0f623dccc608816d36f52cbd Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 18 Feb 2026 00:24:14 +0000 Subject: [PATCH] fix(windows): revert editor to RGBA output and fix DX12 adapter selection Two issues discovered during testing on Windows with NVIDIA RTX 2000: 1. The NV12 render pipeline (RgbaToNv12Converter compute shader + buffer readback) hangs on the Vulkan backend, causing BufferAsyncError and preventing any frame from being delivered to the editor. Reverted the editor renderer back to the proven render_immediate() RGBA path which works reliably on all backends. The NV12 path remains available for export where it has been battle-tested. 2. The DX12 | Vulkan backend specification allowed wgpu to pick Vulkan over DX12 (seen in logs: adapter_backend=Vulkan despite DX12 being listed). This prevents D3D11 shared handle zero-copy since that requires a D3D12 device. Fixed by creating a DX12-only instance first to probe for DX12 availability, then falling back to all backends only if DX12 isn't available. This ensures DX12 is used when present. Co-authored-by: Richie McIlroy --- apps/desktop/src-tauri/src/gpu_context.rs | 28 +++++++++++--- crates/editor/src/editor.rs | 46 +++++------------------ crates/rendering/src/lib.rs | 28 +++++++++++--- 3 files changed, 55 insertions(+), 47 deletions(-) diff --git a/apps/desktop/src-tauri/src/gpu_context.rs b/apps/desktop/src-tauri/src/gpu_context.rs index d9f8ac6221..4fb5fe6da6 100644 --- a/apps/desktop/src-tauri/src/gpu_context.rs +++ b/apps/desktop/src-tauri/src/gpu_context.rs @@ -49,14 +49,32 @@ pub struct SharedGpuContext { static GPU: OnceCell> = OnceCell::const_new(); async fn init_gpu_inner() -> Option { - #[cfg(target_os = "windows")] - let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor { - backends: wgpu::Backends::DX12 | wgpu::Backends::VULKAN, - ..Default::default() - }); #[cfg(not(target_os = "windows"))] let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor::default()); + #[cfg(target_os = "windows")] + let instance = { + let dx12_instance = wgpu::Instance::new(&wgpu::InstanceDescriptor { + backends: wgpu::Backends::DX12, + ..Default::default() + }); + let has_dx12 = dx12_instance + .request_adapter(&wgpu::RequestAdapterOptions { + power_preference: wgpu::PowerPreference::HighPerformance, + force_fallback_adapter: false, + compatible_surface: None, + }) + .await + .is_ok(); + if has_dx12 { + tracing::info!("Using DX12 backend for shared GPU context"); + dx12_instance + } else { + tracing::info!("DX12 not available for shared context, falling back to all backends"); + wgpu::Instance::new(&wgpu::InstanceDescriptor::default()) + } + }; + let hardware_adapter = instance .request_adapter(&wgpu::RequestAdapterOptions { power_preference: wgpu::PowerPreference::HighPerformance, diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index d7ce869c9d..f378538a08 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -147,48 +147,20 @@ impl Renderer { break; } } - let nv12_result = frame_renderer - .render_nv12( - current.segment_frames.clone(), - current.uniforms.clone(), + match frame_renderer + .render_immediate( + current.segment_frames, + current.uniforms, ¤t.cursor, &mut layers, ) - .await; - - match nv12_result { - Ok(pipeline_frame) => { - if let Some(prev) = pipeline_frame { - (self.frame_cb)(EditorFrameOutput::Nv12(prev)); - } - match frame_renderer.flush_pipeline_nv12().await { - Some(Ok(current_frame)) => { - (self.frame_cb)(EditorFrameOutput::Nv12(current_frame)); - } - Some(Err(e)) => { - tracing::warn!(error = %e, "Failed to flush NV12 pipeline frame"); - } - None => {} - } + .await + { + Ok(frame) => { + (self.frame_cb)(EditorFrameOutput::Rgba(frame)); } Err(e) => { - tracing::warn!(error = %e, "NV12 render failed, falling back to RGBA"); - match frame_renderer - .render_immediate( - current.segment_frames, - current.uniforms, - ¤t.cursor, - &mut layers, - ) - .await - { - Ok(frame) => { - (self.frame_cb)(EditorFrameOutput::Rgba(frame)); - } - Err(e) => { - tracing::error!(error = %e, "Failed to render frame in editor"); - } - } + tracing::error!(error = %e, "Failed to render frame in editor"); } } diff --git a/crates/rendering/src/lib.rs b/crates/rendering/src/lib.rs index 5690609fec..14ce7b3d5d 100644 --- a/crates/rendering/src/lib.rs +++ b/crates/rendering/src/lib.rs @@ -979,14 +979,32 @@ impl RenderVideoConstants { .map(|c| XY::new(c.width, c.height)), }; - #[cfg(target_os = "windows")] - let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor { - backends: wgpu::Backends::DX12 | wgpu::Backends::VULKAN, - ..Default::default() - }); #[cfg(not(target_os = "windows"))] let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor::default()); + #[cfg(target_os = "windows")] + let instance = { + let dx12_instance = wgpu::Instance::new(&wgpu::InstanceDescriptor { + backends: wgpu::Backends::DX12, + ..Default::default() + }); + let has_dx12 = dx12_instance + .request_adapter(&wgpu::RequestAdapterOptions { + power_preference: wgpu::PowerPreference::HighPerformance, + force_fallback_adapter: false, + compatible_surface: None, + }) + .await + .is_ok(); + if has_dx12 { + tracing::info!("Using DX12 backend for optimal D3D11 interop"); + dx12_instance + } else { + tracing::info!("DX12 not available, falling back to all backends"); + wgpu::Instance::new(&wgpu::InstanceDescriptor::default()) + } + }; + let hardware_adapter = instance .request_adapter(&wgpu::RequestAdapterOptions { power_preference: wgpu::PowerPreference::HighPerformance,