Vendor wgpu-hal 29.0.4 and i-slint-renderer-skia 1.17.1, unmodified
The Android develop view reads its frame back through memory (TD-1) because wgpu's Vulkan swapchain never pre-rotates, and a portrait window on this tablet's landscape panel then tears. The fix is a small patch to each of these two crates, and this commit is only the ground it lands on: both are byte-for-byte the crates.io sources the lockfile already resolved, so the commits that follow are the patch and nothing else. third_party/ is excluded from the workspace, or every path dependency under the root would become a member and `--workspace` would test and lint upstream code as ours. The README says how to carry the patches across a Slint or wgpu bump, which matters because a stale version here does not fail the build — cargo just warns and uses the unpatched crate.
This commit is contained in:
@@ -0,0 +1,256 @@
|
||||
// Copyright © SixtyFPS GmbH <info@slint.dev>
|
||||
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
|
||||
|
||||
use i_slint_core::api::{PhysicalSize as PhysicalWindowSize, Window};
|
||||
use i_slint_core::graphics::RequestedGraphicsAPI;
|
||||
use i_slint_core::partial_renderer::DirtyRegion;
|
||||
use i_slint_core::renderer::DrawOutcome;
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::num::NonZeroU32;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::SkiaSharedContext;
|
||||
|
||||
pub trait RenderBuffer {
|
||||
fn with_buffer(
|
||||
&self,
|
||||
window: &Window,
|
||||
size: PhysicalWindowSize,
|
||||
render_callback: &mut dyn FnMut(
|
||||
NonZeroU32,
|
||||
NonZeroU32,
|
||||
skia_safe::ColorType,
|
||||
u8,
|
||||
&mut [u8],
|
||||
) -> Result<
|
||||
Option<DirtyRegion>,
|
||||
i_slint_core::platform::PlatformError,
|
||||
>,
|
||||
) -> Result<(), i_slint_core::platform::PlatformError>;
|
||||
}
|
||||
|
||||
#[cfg(feature = "softbuffer")]
|
||||
struct SoftbufferRenderBuffer {
|
||||
_context: softbuffer::Context<Arc<dyn raw_window_handle::HasDisplayHandle + Send + Sync>>,
|
||||
surface: RefCell<
|
||||
softbuffer::Surface<
|
||||
Arc<dyn raw_window_handle::HasDisplayHandle + Send + Sync>,
|
||||
Arc<dyn raw_window_handle::HasWindowHandle + Send + Sync>,
|
||||
>,
|
||||
>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "softbuffer")]
|
||||
impl RenderBuffer for SoftbufferRenderBuffer {
|
||||
fn with_buffer(
|
||||
&self,
|
||||
window: &Window,
|
||||
size: PhysicalWindowSize,
|
||||
render_callback: &mut dyn FnMut(
|
||||
NonZeroU32,
|
||||
NonZeroU32,
|
||||
skia_safe::ColorType,
|
||||
u8,
|
||||
&mut [u8],
|
||||
) -> Result<
|
||||
Option<DirtyRegion>,
|
||||
i_slint_core::platform::PlatformError,
|
||||
>,
|
||||
) -> Result<(), i_slint_core::platform::PlatformError> {
|
||||
let Some((width, height)) = size.width.try_into().ok().zip(size.height.try_into().ok())
|
||||
else {
|
||||
// Nothing to render
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let mut surface = self.surface.borrow_mut();
|
||||
|
||||
surface
|
||||
.resize(width, height)
|
||||
.map_err(|e| format!("Error resizing softbuffer surface: {e}"))?;
|
||||
|
||||
let mut target_buffer = surface
|
||||
.buffer_mut()
|
||||
.map_err(|e| format!("Error retrieving softbuffer rendering buffer: {e}"))?;
|
||||
|
||||
let dirty_region = render_callback(
|
||||
width,
|
||||
height,
|
||||
skia_safe::ColorType::BGRA8888,
|
||||
target_buffer.age(),
|
||||
bytemuck::cast_slice_mut(target_buffer.as_mut()),
|
||||
)?;
|
||||
|
||||
if let Some(dirty_region) = dirty_region {
|
||||
let scale_factor = i_slint_core::lengths::ScaleFactor::new(window.scale_factor());
|
||||
|
||||
let damage_rects = dirty_region
|
||||
.iter()
|
||||
.map(|logical| {
|
||||
let physical_rect = (logical.to_rect() * scale_factor).round_out();
|
||||
softbuffer::Rect {
|
||||
x: physical_rect.min_x().ceil() as _,
|
||||
y: physical_rect.min_y().ceil() as _,
|
||||
width: ((physical_rect.width() as i32).max(1) as u32).try_into().unwrap(),
|
||||
height: ((physical_rect.height() as i32).max(1) as u32).try_into().unwrap(),
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
target_buffer.present_with_damage(&damage_rects)
|
||||
} else {
|
||||
target_buffer.present()
|
||||
}
|
||||
.map_err(|e| format!("Error presenting softbuffer buffer after skia rendering: {e}"))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// This surface renders into the given window using Skia's software rasterize.
|
||||
pub struct SoftwareSurface {
|
||||
render_buffer: Box<dyn RenderBuffer>,
|
||||
}
|
||||
|
||||
impl super::Surface for SoftwareSurface {
|
||||
#[cfg(feature = "softbuffer")]
|
||||
fn new(
|
||||
_shared_context: &SkiaSharedContext,
|
||||
window_handle: Arc<dyn raw_window_handle::HasWindowHandle + Send + Sync>,
|
||||
display_handle: Arc<dyn raw_window_handle::HasDisplayHandle + Send + Sync>,
|
||||
_size: PhysicalWindowSize,
|
||||
_requested_graphics_api: Option<RequestedGraphicsAPI>,
|
||||
) -> Result<Self, i_slint_core::platform::PlatformError> {
|
||||
let _context = softbuffer::Context::new(display_handle)
|
||||
.map_err(|e| format!("Error creating softbuffer context: {e}"))?;
|
||||
|
||||
let surface =
|
||||
softbuffer::Surface::new(&_context, window_handle).map_err(|softbuffer_error| {
|
||||
format!("Error creating softbuffer surface: {softbuffer_error}")
|
||||
})?;
|
||||
|
||||
let surface_access =
|
||||
Box::new(SoftbufferRenderBuffer { _context, surface: RefCell::new(surface) });
|
||||
|
||||
Ok(Self { render_buffer: surface_access })
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "softbuffer"))]
|
||||
fn new(
|
||||
_shared_context: &SkiaSharedContext,
|
||||
_window_handle: Arc<dyn raw_window_handle::HasWindowHandle + Send + Sync>,
|
||||
_display_handle: Arc<dyn raw_window_handle::HasDisplayHandle + Send + Sync>,
|
||||
_size: PhysicalWindowSize,
|
||||
_requested_graphics_api: Option<RequestedGraphicsAPI>,
|
||||
) -> Result<Self, i_slint_core::platform::PlatformError> {
|
||||
struct DummyBuffer;
|
||||
impl RenderBuffer for DummyBuffer {
|
||||
fn with_buffer(
|
||||
&self,
|
||||
_window: &Window,
|
||||
_size: PhysicalWindowSize,
|
||||
_render_callback: &mut dyn FnMut(
|
||||
std::num::NonZeroU32,
|
||||
std::num::NonZeroU32,
|
||||
skia_safe::ColorType,
|
||||
u8,
|
||||
&mut [u8],
|
||||
) -> Result<
|
||||
Option<DirtyRegion>,
|
||||
i_slint_core::platform::PlatformError,
|
||||
>,
|
||||
) -> Result<(), i_slint_core::platform::PlatformError> {
|
||||
Err("Slint's Skia renderer compiled without the 'softbuffer' feature cannot render into a window".into())
|
||||
}
|
||||
}
|
||||
Ok(DummyBuffer.into())
|
||||
}
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
"software"
|
||||
}
|
||||
|
||||
fn resize_event(
|
||||
&self,
|
||||
_size: PhysicalWindowSize,
|
||||
) -> Result<(), i_slint_core::platform::PlatformError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn render(
|
||||
&self,
|
||||
window: &Window,
|
||||
size: PhysicalWindowSize,
|
||||
callback: &dyn Fn(
|
||||
&skia_safe::Canvas,
|
||||
Option<&mut skia_safe::gpu::DirectContext>,
|
||||
u8,
|
||||
) -> Option<DirtyRegion>,
|
||||
pre_present_callback: &RefCell<Option<Box<dyn FnMut()>>>,
|
||||
) -> Result<DrawOutcome, i_slint_core::platform::PlatformError> {
|
||||
self.render_buffer.with_buffer(
|
||||
window,
|
||||
size,
|
||||
&mut |width, height, pixel_format, age, pixels| {
|
||||
let mut surface_borrow = skia_safe::surfaces::wrap_pixels(
|
||||
&skia_safe::ImageInfo::new(
|
||||
(width.get() as i32, height.get() as i32),
|
||||
pixel_format,
|
||||
skia_safe::AlphaType::Opaque,
|
||||
None,
|
||||
),
|
||||
pixels,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.ok_or_else(|| {
|
||||
"Error wrapping target buffer for rendering into with Skia".to_string()
|
||||
})?;
|
||||
|
||||
let dirty_region = callback(surface_borrow.canvas(), None, age);
|
||||
|
||||
if let Some(pre_present_callback) = pre_present_callback.borrow_mut().as_mut() {
|
||||
pre_present_callback();
|
||||
}
|
||||
|
||||
Ok(dirty_region)
|
||||
},
|
||||
)?;
|
||||
Ok(DrawOutcome::Success)
|
||||
}
|
||||
|
||||
fn bits_per_pixel(&self) -> Result<u8, i_slint_core::platform::PlatformError> {
|
||||
Ok(24)
|
||||
}
|
||||
|
||||
fn use_partial_rendering(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: RenderBuffer + 'static> From<T> for SoftwareSurface {
|
||||
fn from(render_buffer: T) -> Self {
|
||||
Self { render_buffer: Box::new(render_buffer) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: RenderBuffer + 'static> RenderBuffer for Rc<T> {
|
||||
fn with_buffer(
|
||||
&self,
|
||||
window: &Window,
|
||||
size: PhysicalWindowSize,
|
||||
render_callback: &mut dyn FnMut(
|
||||
NonZeroU32,
|
||||
NonZeroU32,
|
||||
skia_safe::ColorType,
|
||||
u8,
|
||||
&mut [u8],
|
||||
) -> Result<
|
||||
Option<DirtyRegion>,
|
||||
i_slint_core::platform::PlatformError,
|
||||
>,
|
||||
) -> Result<(), i_slint_core::platform::PlatformError> {
|
||||
self.as_ref().with_buffer(window, size, render_callback)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user