Files
dtourolle dc1add9dbb 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.
2026-09-25 04:18:39 -04:00

199 lines
6.9 KiB
Rust

// 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
// cSpell: ignore rasterizers
use std::pin::Pin;
use std::rc::Rc;
use i_slint_core::platform::PlatformError;
use i_slint_core::renderer::RendererSealed;
use i_slint_core::window::WindowAdapter;
use wgpu_29 as wgpu;
use crate::wgpu_29_surface::{Backend, WGPUSurface};
use crate::{SkiaRenderer, SkiaSharedContext};
/// Use the Skia renderer with WGPU when implementing a custom Slint platform where you want the
/// scene to be rendered into a WGPU texture. The rendering is done using the
/// [Skia](https://skia.org/) library with platform-native GPU acceleration.
///
/// This is the Skia equivalent of `FemtoVGWGPURenderer`, offering superior font rendering
/// quality through platform-native text rasterizers.
///
/// Rendering notifier callbacks registered via
/// [`Window::set_rendering_notifier()`](i_slint_core::api::Window::set_rendering_notifier)
/// will receive [`GraphicsAPI::WGPU29`](i_slint_core::api::GraphicsAPI::WGPU29) with the
/// renderer's instance, device, and queue.
pub struct SkiaWGPURenderer {
renderer: SkiaRenderer,
surface: WGPUSurface,
}
impl SkiaWGPURenderer {
/// Creates a new SkiaWGPURenderer.
///
/// The `instance`, `adapter`, `device` and `queue` are the WGPU resources used for rendering.
/// The `adapter` is needed to determine the GPU backend and create the Skia graphics context.
///
/// The wgpu resources are also provided to rendering notifier callbacks via
/// [`GraphicsAPI::WGPU29`](i_slint_core::api::GraphicsAPI::WGPU29).
pub fn new(
instance: wgpu::Instance,
adapter: wgpu::Adapter,
device: wgpu::Device,
queue: wgpu::Queue,
) -> Result<Self, PlatformError> {
let backend: Backend = adapter.get_info().backend.try_into()?;
let gr_context = backend.make_context(&adapter, &device, &queue).ok_or_else(|| {
PlatformError::from("Failed to create Skia graphics context from WGPU")
})?;
let surface = WGPUSurface::new_offscreen(instance, device, queue, backend, gr_context);
let shared_context = SkiaSharedContext::default();
// Use SkiaRenderer::default() to stay resilient to field additions, then disable
// partial rendering — there is no buffer age tracking for external texture targets.
let mut renderer = SkiaRenderer::default(&shared_context);
renderer.partial_rendering_state = None;
Ok(Self { renderer, surface })
}
/// Render the scene to the given texture.
///
/// The texture must have been created with `RENDER_ATTACHMENT` usage and have a supported
/// format. Supported formats depend on the GPU backend: `Rgba8Unorm` and `Rgba8UnormSrgb`
/// are supported on all backends; `Bgra8Unorm` is additionally supported on Metal and Vulkan.
pub fn render_to_texture(&self, texture: &wgpu::Texture) -> Result<(), PlatformError> {
self.renderer.invoke_rendering_notifier_setup(&self.surface)?;
let gr_context = &mut self.surface.gr_context.borrow_mut();
let mut skia_surface =
self.surface.backend.make_surface(gr_context, texture).ok_or_else(|| {
PlatformError::from("Failed to wrap WGPU texture as Skia render target")
})?;
let window_adapter = self.renderer.window_adapter()?;
let window = window_adapter.window();
self.renderer.render_to_canvas(
skia_surface.canvas(),
0.,
(0., 0.),
Some(gr_context),
0,
Some(&self.surface),
window,
None,
);
self.surface.flush_and_submit(gr_context);
Ok(())
}
}
#[doc(hidden)]
impl RendererSealed for SkiaWGPURenderer {
fn text_size(
&self,
text_item: Pin<&dyn i_slint_core::item_rendering::RenderString>,
item_rc: &i_slint_core::items::ItemRc,
max_width: Option<i_slint_core::lengths::LogicalLength>,
text_wrap: i_slint_core::items::TextWrap,
) -> i_slint_core::lengths::LogicalSize {
self.renderer.text_size(text_item, item_rc, max_width, text_wrap)
}
fn char_size(
&self,
text_item: Pin<&dyn i_slint_core::item_rendering::HasFont>,
item_rc: &i_slint_core::items::ItemRc,
ch: char,
) -> i_slint_core::lengths::LogicalSize {
self.renderer.char_size(text_item, item_rc, ch)
}
fn font_metrics(
&self,
font_request: i_slint_core::graphics::FontRequest,
) -> i_slint_core::items::FontMetrics {
self.renderer.font_metrics(font_request)
}
fn text_input_byte_offset_for_position(
&self,
text_input: Pin<&i_slint_core::items::TextInput>,
item_rc: &i_slint_core::items::ItemRc,
pos: i_slint_core::lengths::LogicalPoint,
) -> usize {
self.renderer.text_input_byte_offset_for_position(text_input, item_rc, pos)
}
fn text_input_cursor_rect_for_byte_offset(
&self,
text_input: Pin<&i_slint_core::items::TextInput>,
item_rc: &i_slint_core::items::ItemRc,
byte_offset: usize,
) -> i_slint_core::lengths::LogicalRect {
self.renderer.text_input_cursor_rect_for_byte_offset(text_input, item_rc, byte_offset)
}
fn register_font_from_memory(
&self,
data: &'static [u8],
) -> Result<(), Box<dyn std::error::Error>> {
self.renderer.register_font_from_memory(data)
}
fn register_font_from_path(
&self,
path: &std::path::Path,
) -> Result<(), Box<dyn std::error::Error>> {
self.renderer.register_font_from_path(path)
}
fn set_rendering_notifier(
&self,
callback: Box<dyn i_slint_core::api::RenderingNotifier>,
) -> Result<(), i_slint_core::api::SetRenderingNotifierError> {
self.renderer.set_rendering_notifier(callback)
}
fn free_graphics_resources(
&self,
component: i_slint_core::item_tree::ItemTreeRef,
items: &mut dyn Iterator<Item = Pin<i_slint_core::items::ItemRef<'_>>>,
) -> Result<(), PlatformError> {
self.renderer.free_graphics_resources(component, items)
}
fn set_window_adapter(&self, window_adapter: &Rc<dyn WindowAdapter>) {
self.renderer.set_window_adapter(window_adapter)
}
fn window_adapter(&self) -> Option<Rc<dyn WindowAdapter>> {
RendererSealed::window_adapter(&self.renderer)
}
fn resize(&self, size: i_slint_core::api::PhysicalSize) -> Result<(), PlatformError> {
self.renderer.resize(size)
}
fn take_snapshot(
&self,
) -> Result<
i_slint_core::graphics::SharedPixelBuffer<i_slint_core::graphics::Rgba8Pixel>,
PlatformError,
> {
self.renderer.take_snapshot()
}
fn supports_transformations(&self) -> bool {
self.renderer.supports_transformations()
}
}