diff --git a/apps/pdfcraft/src/main.rs b/apps/pdfcraft/src/main.rs index 721b7da1b..118b7ff44 100644 --- a/apps/pdfcraft/src/main.rs +++ b/apps/pdfcraft/src/main.rs @@ -557,6 +557,11 @@ fn write_control_file(path: &str, port: u16, token: &str) -> std::io::Result<()> /// `WGPU_BACKEND` asks for them. Creating a Vulkan instance loads every installed Vulkan driver /// into the process, and a faulty one (an Intel driver in issue #37) crashed PdfCraft before /// its window appeared. D3D12 is the native, best-supported backend there. +/// - On Windows, Direct3D 12 presents through a DirectComposition visual rather than a swap chain +/// on the window itself, unless `WGPU_DX12_PRESENTATION_SYSTEM` says otherwise. While the window +/// is being resized, Windows stretched the window's swap chain, still at the old size, to the +/// new one, so the text shimmered and the left panel's width bounced for as long as the drag +/// lasted (issue #2738). A visual's last frame is shown at its own size instead. fn configure_gpu(native: &mut eframe::NativeOptions) { let eframe::egui_wgpu::WgpuSetup::CreateNew(setup) = &mut native.wgpu_options.wgpu_setup else { return }; // Nothing draws or dispatches indirectly, so wgpu's check of indirect arguments only costs a @@ -604,6 +609,15 @@ fn configure_gpu(native: &mut eframe::NativeOptions) { if cfg!(target_os = "windows") && std::env::var_os("WGPU_BACKEND").is_none() { setup.instance_descriptor.backends = eframe::wgpu::Backends::DX12 | eframe::wgpu::Backends::GL; } + if cfg!(target_os = "windows") { + setup.instance_descriptor.backend_options.dx12.presentation_system = dx12_presentation(eframe::wgpu::Dx12SwapchainKind::from_env()); + } +} + +/// How Direct3D 12 presents on Windows: through a DirectComposition visual (issue #2738), unless +/// `WGPU_DX12_PRESENTATION_SYSTEM` asked for something else (`asked`). +fn dx12_presentation(asked: Option) -> eframe::wgpu::Dx12SwapchainKind { + asked.unwrap_or(eframe::wgpu::Dx12SwapchainKind::DxgiFromVisual) } /// Surface textures must fit the device's enabled limits, not just the physical GPU's limits. @@ -878,7 +892,7 @@ mod tests { } #[test] - fn gpu_backends_avoid_vulkan_on_windows_and_prefer_low_power() { + fn gpu_setup_avoids_vulkan_and_stretched_resizes_on_windows_and_prefers_low_power() { let mut native = eframe::NativeOptions::default(); super::configure_gpu(&mut native); let eframe::egui_wgpu::WgpuSetup::CreateNew(setup) = &native.wgpu_options.wgpu_setup else { @@ -896,6 +910,18 @@ mod tests { assert!(backends.contains(eframe::wgpu::Backends::DX12), "{backends:?}"); assert!(!backends.contains(eframe::wgpu::Backends::VULKAN), "issue #37: {backends:?}"); } + use eframe::wgpu::Dx12SwapchainKind::{DxgiFromHwnd, DxgiFromVisual}; + assert_eq!(super::dx12_presentation(None), DxgiFromVisual); + assert_eq!(super::dx12_presentation(Some(DxgiFromHwnd)), DxgiFromHwnd, "WGPU_DX12_PRESENTATION_SYSTEM still decides"); + assert_eq!(super::dx12_presentation(Some(DxgiFromVisual)), DxgiFromVisual); + let presentation = setup.instance_descriptor.backend_options.dx12.presentation_system; + if cfg!(target_os = "windows") { + // A composition visual unless WGPU_DX12_PRESENTATION_SYSTEM asks otherwise. + let asked = eframe::wgpu::Dx12SwapchainKind::from_env(); + assert_eq!(presentation, asked.unwrap_or(DxgiFromVisual), "issue #2738: resizing stretches a window swap chain"); + } else { + assert_eq!(presentation, eframe::wgpu::Dx12SwapchainKind::default().with_env(), "only Windows changes it"); + } } #[test] diff --git a/docs/development.md b/docs/development.md index e599effc7..1c6cce6a9 100644 --- a/docs/development.md +++ b/docs/development.md @@ -127,6 +127,7 @@ copies retain their current preferences locations. | `XDG_DATA_HOME` | Linux/FreeBSD: base of the log folder and crash recovery; previous preferences are migrated from here | | `WGPU_POWER_PREF` | GPU choice; by default PdfCraft draws on the GPU the Wayland compositor draws with (it says so through linux-dmabuf feedback), else the GPU that drives the primary display (Windows) or the built-in panel (Linux), else the low-power (integrated) GPU. The log says which one was chosen | | `WGPU_BACKEND` | Graphics backend; by default Windows uses Direct3D 12, falling back to OpenGL | +| `WGPU_DX12_PRESENTATION_SYSTEM` | Windows, Direct3D 12: `hwnd` presents through a swap chain on the window itself, which Windows stretches while the window is resized (#2738); unset or `visual`, PdfCraft presents through a DirectComposition visual | | `WGPU_VALIDATION_INDIRECT_CALL` | `1` turns wgpu's check of indirect draw arguments back on. PdfCraft draws nothing indirectly and turns it off, since the compute shader the check needs fails to compile on some drivers (Metal on an Intel Iris Pro), which lost the device at start-up | | `PDFCRAFT_RENDERER` | `gl` starts with OpenGL (glow) and never loads wgpu; `wgpu` reports a wgpu failure instead of retrying with OpenGL; unset, wgpu is retried with OpenGL when it can't start (see [Renderer fallback](#renderer-fallback)) | | `PDFCRAFT_TITLEBAR` | Linux: `app` draws PdfCraft's own title bar (window buttons in the tab strip, in the order of GNOME's `button-layout`, and resize edges); `system` keeps the frame winit or the desktop draws; unset, PdfCraft draws its own when the Wayland compositor draws no frames (GNOME), and leaves it to the desktop otherwise (KDE, Sway, Hyprland, X11). Where PdfCraft draws it, Preferences ▸ Use system title bar switches back to winit's frame without a restart; `system` also hides that option |