diff --git a/third_party/README.md b/third_party/README.md index 2f9d111..ba4339b 100644 --- a/third_party/README.md +++ b/third_party/README.md @@ -28,7 +28,24 @@ upstream change would make it unnecessary. ## wgpu-hal 29.0.4 — Vulkan pre-rotation on Android -*(patch lands in the next commit)* +Upstream creates every Vulkan swapchain with `preTransform = IDENTITY` +(`src/vulkan/swapchain/native.rs`, gfx-rs/wgpu#3345). On Android, a window +whose orientation differs from the panel's is then rotated by the +compositor on the GPU (`composition=CLIENT`), and on this tablet in +portrait those frames tear. + +The patch adds two methods to `wgpu_hal::vulkan::Surface`: + +- `current_transform(&Adapter)` — the surface's `currentTransform`. +- `set_pre_transform(transform)` — the `preTransform` for the next + swapchain. Opt-in: nothing calls it but the Skia patch below, and the + default is still `IDENTITY`, so desktop and every other caller behave + exactly as upstream. + +Setting it is a promise that the caller draws rotated into a swapchain +sized in the panel's orientation; wgpu itself rotates nothing. + +Unnecessary once wgpu exposes pre-rotation itself (#3345). ## i-slint-renderer-skia 1.17.1 — rotate the canvas to match diff --git a/third_party/wgpu-hal-29.0.4/src/vulkan/mod.rs b/third_party/wgpu-hal-29.0.4/src/vulkan/mod.rs index 2fbb629..bb47726 100644 --- a/third_party/wgpu-hal-29.0.4/src/vulkan/mod.rs +++ b/third_party/wgpu-hal-29.0.4/src/vulkan/mod.rs @@ -208,6 +208,41 @@ impl Surface { ) } + /// DarkRoom patch: the surface's `currentTransform` — how the presentation + /// engine will rotate what is presented to it — or `None` if the surface + /// is not a native Vulkan surface or the query failed. + /// + /// On Android this is how a caller learns the display's rotation relative + /// to the panel, so it can draw pre-rotated (see [`Self::set_pre_transform`]). + pub fn current_transform(&self, adapter: &Adapter) -> Option { + self.inner + .as_any() + .downcast_ref::()? + .current_transform(adapter) + } + + /// DarkRoom patch: create the next swapchain with this `preTransform` + /// instead of `IDENTITY`. Takes effect on the next `configure`. + /// + /// This is a promise that every frame presented afterwards is already + /// rotated by `transform`, *and* that the configured extent is the + /// unrotated one — width and height swapped from the window's for a + /// 90° or 270° rotation. Keeping the promise is entirely the caller's + /// job: wgpu does not rotate anything. The payoff on Android is that the + /// compositor scans the buffer out directly instead of rotating it on the + /// GPU, and `vkQueuePresentKHR` stops returning `VK_SUBOPTIMAL_KHR`. + /// + /// Does nothing on a surface that is not a native Vulkan surface. + pub fn set_pre_transform(&self, transform: vk::SurfaceTransformFlagsKHR) { + if let Some(native) = self + .inner + .as_any() + .downcast_ref::() + { + native.set_pre_transform(transform); + } + } + /// Get the raw Vulkan swapchain associated with this surface. /// /// Returns [`None`] if the surface is not configured or if the swapchain diff --git a/third_party/wgpu-hal-29.0.4/src/vulkan/swapchain/native.rs b/third_party/wgpu-hal-29.0.4/src/vulkan/swapchain/native.rs index 66f08db..cf383a4 100644 --- a/third_party/wgpu-hal-29.0.4/src/vulkan/swapchain/native.rs +++ b/third_party/wgpu-hal-29.0.4/src/vulkan/swapchain/native.rs @@ -17,6 +17,10 @@ pub(crate) struct NativeSurface { raw: vk::SurfaceKHR, functor: khr::surface::Instance, instance: Arc, + // DarkRoom patch: the `preTransform` the next swapchain is created with. + // `IDENTITY` unless a caller that rotates its own drawing asks otherwise + // through `vulkan::Surface::set_pre_transform`. + pre_transform: Mutex, } impl NativeSurface { @@ -26,12 +30,33 @@ impl NativeSurface { raw, functor, instance: Arc::clone(&instance.shared), + pre_transform: Mutex::new(vk::SurfaceTransformFlagsKHR::IDENTITY), } } pub fn as_raw(&self) -> vk::SurfaceKHR { self.raw } + + /// DarkRoom patch: the transform the presentation engine will apply to + /// this surface right now, as `vkGetPhysicalDeviceSurfaceCapabilitiesKHR` + /// reports it. + pub fn current_transform( + &self, + adapter: &crate::vulkan::Adapter, + ) -> Option { + let caps = unsafe { + self.functor + .get_physical_device_surface_capabilities(adapter.raw, self.raw) + } + .inspect_err(|e| log::error!("get_physical_device_surface_capabilities: {e}")) + .ok()?; + Some(caps.current_transform) + } + + pub fn set_pre_transform(&self, transform: vk::SurfaceTransformFlagsKHR) { + *self.pre_transform.lock() = transform; + } } impl Surface for NativeSurface { @@ -199,7 +224,11 @@ impl Surface for NativeSurface { .image_array_layers(config.extent.depth_or_array_layers) .image_usage(conv::map_texture_usage(config.usage)) .image_sharing_mode(vk::SharingMode::EXCLUSIVE) - .pre_transform(vk::SurfaceTransformFlagsKHR::IDENTITY) + // DarkRoom patch: upstream hardcodes `IDENTITY` here + // (gfx-rs/wgpu#3345). The value is a promise that the image is + // already drawn rotated, so it is only ever something other than + // `IDENTITY` when the caller has said it draws that way. + .pre_transform(*self.pre_transform.lock()) .composite_alpha(conv::map_composite_alpha_mode(config.composite_alpha_mode)) .present_mode(conv::map_present_mode(config.present_mode)) .clipped(true)