Port the Skia renderer to rwh06 and the new softbuffer

... by accepting an Rc<dyn Has*Handle> in the interface. This is required for softbuffer use.
This commit is contained in:
Simon Hausmann 2024-06-17 17:46:09 +02:00
parent 40f2833bac
commit 4c888bf1ae
10 changed files with 156 additions and 123 deletions

View file

@ -511,18 +511,34 @@ mod software_renderer {
pub mod skia {
use super::*;
use raw_window_handle::{RawDisplayHandle, RawWindowHandle};
use std::rc::Rc;
struct CppRawHandle(RawWindowHandle, RawDisplayHandle);
struct RawHandlePair((RawWindowHandle, RawDisplayHandle));
// the raw handle type are #[non_exhaustive], so they can't be initialize with the convenient syntax. Work that around.
macro_rules! init_raw {
($ty:ty { $($var:ident),* }) => {
{
let mut h = <$ty>::empty();
$(h.$var = $var;)*
h
}
};
impl raw_window_handle::HasDisplayHandle for RawHandlePair {
fn display_handle(
&self,
) -> Result<raw_window_handle::DisplayHandle<'_>, raw_window_handle::HandleError> {
// Safety: It is assumed that the C++ side keeps the window/display handles alive.
Ok(unsafe { raw_window_handle::DisplayHandle::borrow_raw(self.0 .1) })
}
}
impl raw_window_handle::HasWindowHandle for RawHandlePair {
fn window_handle(
&self,
) -> Result<raw_window_handle::WindowHandle<'_>, raw_window_handle::HandleError> {
// Safety: It is assumed that the C++ side keeps the window/display handles alive.
Ok(unsafe { raw_window_handle::WindowHandle::borrow_raw(self.0 .0) })
}
}
struct CppRawHandle(Rc<RawHandlePair>);
impl From<(RawWindowHandle, RawDisplayHandle)> for CppRawHandle {
fn from(pair: (RawWindowHandle, RawDisplayHandle)) -> Self {
Self(Rc::new(RawHandlePair(pair)))
}
}
type CppRawHandleOpaque = *const c_void;
@ -530,15 +546,14 @@ pub mod skia {
#[no_mangle]
pub unsafe extern "C" fn slint_new_raw_window_handle_win32(
hwnd: *mut c_void,
hinstance: *mut c_void,
_hinstance: *mut c_void,
) -> CppRawHandleOpaque {
let handle = CppRawHandle(
RawWindowHandle::Win32(init_raw!(raw_window_handle::Win32WindowHandle {
hwnd,
hinstance
})),
RawDisplayHandle::Windows(raw_window_handle::WindowsDisplayHandle::empty()),
);
let handle = CppRawHandle::from((
RawWindowHandle::Win32(raw_window_handle::Win32WindowHandle::new(
(hwnd as isize).try_into().expect("C++: NativeWindowHandle created with null hwnd"),
)),
RawDisplayHandle::Windows(raw_window_handle::WindowsDisplayHandle::new()),
));
Box::into_raw(Box::new(handle)) as CppRawHandleOpaque
}
@ -550,10 +565,21 @@ pub mod skia {
screen: core::ffi::c_int,
) -> CppRawHandleOpaque {
use raw_window_handle::{XcbDisplayHandle, XcbWindowHandle};
let handle = CppRawHandle(
RawWindowHandle::Xcb(init_raw!(XcbWindowHandle { window, visual_id })),
RawDisplayHandle::Xcb(init_raw!(XcbDisplayHandle { connection, screen })),
);
let handle = CppRawHandle::from((
RawWindowHandle::Xcb({
let mut hnd = XcbWindowHandle::new(
window
.try_into()
.expect("C++: NativeWindowHandle created with null xcb window handle"),
);
hnd.visual_id = visual_id.try_into().ok();
hnd
}),
RawDisplayHandle::Xcb(XcbDisplayHandle::new(
Some(core::ptr::NonNull::new_unchecked(connection)),
screen,
)),
));
Box::into_raw(Box::new(handle)) as CppRawHandleOpaque
}
@ -565,10 +591,17 @@ pub mod skia {
screen: core::ffi::c_int,
) -> CppRawHandleOpaque {
use raw_window_handle::{XlibDisplayHandle, XlibWindowHandle};
let handle = CppRawHandle(
RawWindowHandle::Xlib(init_raw!(XlibWindowHandle { window, visual_id })),
RawDisplayHandle::Xlib(init_raw!(XlibDisplayHandle { display, screen })),
);
let handle = CppRawHandle::from((
RawWindowHandle::Xlib({
let mut hnd = XlibWindowHandle::new(window);
hnd.visual_id = visual_id;
hnd
}),
RawDisplayHandle::Xlib(XlibDisplayHandle::new(
Some(core::ptr::NonNull::new_unchecked(display)),
screen,
)),
));
Box::into_raw(Box::new(handle)) as CppRawHandleOpaque
}
@ -578,23 +611,29 @@ pub mod skia {
display: *mut c_void,
) -> CppRawHandleOpaque {
use raw_window_handle::{WaylandDisplayHandle, WaylandWindowHandle};
let handle = CppRawHandle(
RawWindowHandle::Wayland(init_raw!(WaylandWindowHandle { surface })),
RawDisplayHandle::Wayland(init_raw!(WaylandDisplayHandle { display })),
);
let handle = CppRawHandle::from((
RawWindowHandle::Wayland(WaylandWindowHandle::new(core::ptr::NonNull::new_unchecked(
surface,
))),
RawDisplayHandle::Wayland(WaylandDisplayHandle::new(
core::ptr::NonNull::new_unchecked(display),
)),
));
Box::into_raw(Box::new(handle)) as CppRawHandleOpaque
}
#[no_mangle]
pub unsafe extern "C" fn slint_new_raw_window_handle_appkit(
ns_view: *mut c_void,
ns_window: *mut c_void,
_ns_window: *mut c_void,
) -> CppRawHandleOpaque {
use raw_window_handle::{AppKitDisplayHandle, AppKitWindowHandle};
let handle = CppRawHandle(
RawWindowHandle::AppKit(init_raw!(AppKitWindowHandle { ns_view, ns_window })),
RawDisplayHandle::AppKit(AppKitDisplayHandle::empty()),
);
let handle = CppRawHandle::from((
RawWindowHandle::AppKit(AppKitWindowHandle::new(core::ptr::NonNull::new_unchecked(
ns_view,
))),
RawDisplayHandle::AppKit(AppKitDisplayHandle::new()),
));
Box::into_raw(Box::new(handle)) as CppRawHandleOpaque
}
@ -613,21 +652,10 @@ pub mod skia {
) -> SkiaRendererOpaque {
let handle = &*(handle_opaque as *const CppRawHandle);
// Safety: This is safe because the handle remains valid; the next rwh release provides `new()` without unsafe.
let active_handle = unsafe { raw_window_handle::ActiveHandle::new_unchecked() };
// Safety: the C++ code should ensure that the handle is valid
let (window_handle, display_handle) = unsafe {
(
raw_window_handle::WindowHandle::borrow_raw(handle.0, active_handle),
raw_window_handle::DisplayHandle::borrow_raw(handle.1),
)
};
let boxed_renderer: Box<SkiaRenderer> = Box::new(
SkiaRenderer::new(
window_handle,
display_handle,
handle.0.clone(),
handle.0.clone(),
PhysicalSize { width: size.width, height: size.height },
)
.unwrap(),

View file

@ -485,7 +485,7 @@ impl EventLoopState {
Event::Resumed => ALL_WINDOWS.with(|ws| {
for (_, window_weak) in ws.borrow().iter() {
if let Some(w) = window_weak.upgrade() {
if let Err(e) = w.renderer.resumed(&w.winit_window()) {
if let Err(e) = w.renderer.resumed(w.winit_window()) {
self.loop_error = Some(e);
}
}

View file

@ -32,6 +32,8 @@ pub enum SlintUserEvent {
}
mod renderer {
use std::rc::Rc;
use i_slint_core::platform::PlatformError;
pub trait WinitCompatibleRenderer {
@ -43,7 +45,7 @@ mod renderer {
fn occluded(&self, _: bool) {}
// Got winit::Event::Resumed
fn resumed(&self, _winit_window: &winit::window::Window) -> Result<(), PlatformError> {
fn resumed(&self, _winit_window: Rc<winit::window::Window>) -> Result<(), PlatformError> {
Ok(())
}
}

View file

@ -5,7 +5,6 @@ use std::rc::Rc;
use crate::winitwindowadapter::physical_size_to_slint;
use i_slint_core::platform::PlatformError;
use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle};
pub struct WinitSkiaRenderer {
renderer: i_slint_renderer_skia::SkiaRenderer,
@ -13,11 +12,11 @@ pub struct WinitSkiaRenderer {
impl WinitSkiaRenderer {
pub fn new(
window_builder: winit::window::WindowBuilder,
window_attributes: winit::window::WindowAttributes,
) -> Result<(Box<dyn super::WinitCompatibleRenderer>, Rc<winit::window::Window>), PlatformError>
{
let winit_window = Rc::new(crate::event_loop::with_window_target(|event_loop| {
window_builder.build(event_loop.event_loop_target()).map_err(|winit_os_error| {
event_loop.create_window(window_attributes).map_err(|winit_os_error| {
format!("Error creating native window for Skia rendering: {}", winit_os_error)
.into()
})
@ -37,11 +36,11 @@ impl WinitSkiaRenderer {
#[cfg(not(target_os = "android"))]
pub fn new_software(
window_builder: winit::window::WindowBuilder,
window_attributes: winit::window::WindowAttributes,
) -> Result<(Box<dyn super::WinitCompatibleRenderer>, Rc<winit::window::Window>), PlatformError>
{
let winit_window = Rc::new(crate::event_loop::with_window_target(|event_loop| {
window_builder.build(event_loop.event_loop_target()).map_err(|winit_os_error| {
event_loop.create_window(window_attributes).map_err(|winit_os_error| {
format!("Error creating native window for Skia rendering: {}", winit_os_error)
.into()
})
@ -60,11 +59,11 @@ impl WinitSkiaRenderer {
}
pub fn new_opengl(
window_builder: winit::window::WindowBuilder,
window_attributes: winit::window::WindowAttributes,
) -> Result<(Box<dyn super::WinitCompatibleRenderer>, Rc<winit::window::Window>), PlatformError>
{
let winit_window = Rc::new(crate::event_loop::with_window_target(|event_loop| {
window_builder.build(event_loop.event_loop_target()).map_err(|winit_os_error| {
event_loop.create_window(window_attributes).map_err(|winit_os_error| {
format!("Error creating native window for Skia rendering: {}", winit_os_error)
.into()
})
@ -92,29 +91,12 @@ impl super::WinitCompatibleRenderer for WinitSkiaRenderer {
&self.renderer
}
fn resumed(&self, winit_window: &winit::window::Window) -> Result<(), PlatformError> {
fn resumed(&self, winit_window: Rc<winit::window::Window>) -> Result<(), PlatformError> {
let size = winit_window.inner_size();
// Safety: This is safe because the handle remains valid; the next rwh release provides `new()` without unsafe.
let active_handle = unsafe { raw_window_handle::ActiveHandle::new_unchecked() };
// Safety: API wise we can't guarantee that the window/display handles remain valid, so we
// use unsafe here. However the winit window adapter keeps the winit window alive as long as
// the renderer.
// TODO: remove once winit implements HasWindowHandle/HasDisplayHandle
let (window_handle, display_handle) = unsafe {
(
raw_window_handle::WindowHandle::borrow_raw(
winit_window.raw_window_handle(),
active_handle,
),
raw_window_handle::DisplayHandle::borrow_raw(winit_window.raw_display_handle()),
)
};
self.renderer.set_window_handle(
window_handle,
display_handle,
winit_window.clone(),
winit_window.clone(),
physical_size_to_slint(&size),
winit_window.scale_factor() as f32,
)

View file

@ -174,7 +174,7 @@ impl WinitWindowAdapter {
});
let winit_window = self_rc.winit_window();
if let Err(e) = self_rc.renderer.resumed(&winit_window) {
if let Err(e) = self_rc.renderer.resumed(winit_window.clone()) {
i_slint_core::debug_log!("Error initialing renderer in winit backend with window: {e}");
}
let id = winit_window.id();

View file

@ -4,6 +4,7 @@
use i_slint_core::api::PhysicalSize as PhysicalWindowSize;
use i_slint_core::platform::PlatformError;
use std::cell::RefCell;
use std::rc::Rc;
use windows::core::Interface;
use windows::Win32::Graphics::Direct3D::D3D_FEATURE_LEVEL_11_0;
use windows::Win32::Graphics::Dxgi::Common::DXGI_STANDARD_MULTISAMPLE_QUALITY_PATTERN;
@ -245,8 +246,8 @@ pub struct D3DSurface {
impl super::Surface for D3DSurface {
fn new(
window_handle: raw_window_handle::WindowHandle<'_>,
_display_handle: raw_window_handle::DisplayHandle<'_>,
window_handle: Rc<dyn raw_window_handle::HasWindowHandle>,
_display_handle: Rc<dyn raw_window_handle::HasDisplayHandle>,
size: PhysicalWindowSize,
) -> Result<Self, i_slint_core::platform::PlatformError> {
let factory_flags = 0;
@ -357,6 +358,10 @@ impl super::Surface for D3DSurface {
let gr_context = unsafe { skia_safe::gpu::DirectContext::new_d3d(&backend_context, None) }
.ok_or_else(|| format!("unable to create Skia D3D DirectContext"))?;
let window_handle = window_handle
.window_handle()
.map_err(|e| format!("error obtaining window handle for skia d3d renderer: {e}"))?;
let swap_chain = RefCell::new(SwapChain::new(
queue,
&device,

View file

@ -63,8 +63,8 @@ cfg_if::cfg_if! {
}
fn create_default_surface(
window_handle: raw_window_handle::WindowHandle<'_>,
display_handle: raw_window_handle::DisplayHandle<'_>,
window_handle: Rc<dyn raw_window_handle::HasWindowHandle>,
display_handle: Rc<dyn raw_window_handle::HasDisplayHandle>,
size: PhysicalWindowSize,
) -> Result<Box<dyn Surface>, PlatformError> {
match DefaultSurface::new(window_handle.clone(), display_handle.clone(), size) {
@ -94,8 +94,8 @@ pub struct SkiaRenderer {
rendering_first_time: Cell<bool>,
surface: RefCell<Option<Box<dyn Surface>>>,
surface_factory: fn(
window_handle: raw_window_handle::WindowHandle<'_>,
display_handle: raw_window_handle::DisplayHandle<'_>,
window_handle: Rc<dyn raw_window_handle::HasWindowHandle>,
display_handle: Rc<dyn raw_window_handle::HasDisplayHandle>,
size: PhysicalWindowSize,
) -> Result<Box<dyn Surface>, PlatformError>,
pre_present_callback: RefCell<Option<Box<dyn FnMut()>>>,
@ -157,8 +157,8 @@ impl SkiaRenderer {
/// Creates a new renderer is associated with the provided window adapter.
pub fn new(
window_handle: raw_window_handle::WindowHandle<'_>,
display_handle: raw_window_handle::DisplayHandle<'_>,
window_handle: Rc<dyn raw_window_handle::HasWindowHandle>,
display_handle: Rc<dyn raw_window_handle::HasDisplayHandle>,
size: PhysicalWindowSize,
) -> Result<Self, PlatformError> {
Ok(Self::new_with_surface(create_default_surface(window_handle, display_handle, size)?))
@ -192,8 +192,8 @@ impl SkiaRenderer {
/// Reset the surface to the window given the window handle
pub fn set_window_handle(
&self,
window_handle: raw_window_handle::WindowHandle<'_>,
display_handle: raw_window_handle::DisplayHandle<'_>,
window_handle: Rc<dyn raw_window_handle::HasWindowHandle>,
display_handle: Rc<dyn raw_window_handle::HasDisplayHandle>,
size: PhysicalWindowSize,
scale_factor: f32,
) -> Result<(), PlatformError> {
@ -547,8 +547,8 @@ impl Drop for SkiaRenderer {
pub trait Surface {
/// Creates a new surface with the given window, display, and size.
fn new(
window_handle: raw_window_handle::WindowHandle<'_>,
display_handle: raw_window_handle::DisplayHandle<'_>,
window_handle: Rc<dyn raw_window_handle::HasWindowHandle>,
display_handle: Rc<dyn raw_window_handle::HasDisplayHandle>,
size: PhysicalWindowSize,
) -> Result<Self, PlatformError>
where

View file

@ -8,10 +8,10 @@ use i_slint_core::api::PhysicalSize as PhysicalWindowSize;
use metal::MTLPixelFormat;
use objc::{rc::autoreleasepool, runtime::YES};
use raw_window_handle::HasRawWindowHandle;
use skia_safe::gpu::mtl;
use std::cell::RefCell;
use std::rc::Rc;
/// This surface renders into the given window using Metal. The provided display argument
/// is ignored, as it has no meaning on macOS.
@ -23,8 +23,8 @@ pub struct MetalSurface {
impl super::Surface for MetalSurface {
fn new(
window_handle: raw_window_handle::WindowHandle<'_>,
_display_handle: raw_window_handle::DisplayHandle<'_>,
window_handle: Rc<dyn raw_window_handle::HasWindowHandle>,
_display_handle: Rc<dyn raw_window_handle::HasDisplayHandle>,
size: PhysicalWindowSize,
) -> Result<Self, i_slint_core::platform::PlatformError> {
let device = metal::Device::system_default()
@ -39,10 +39,14 @@ impl super::Surface for MetalSurface {
layer.set_drawable_size(CGSize::new(size.width as f64, size.height as f64));
unsafe {
let view = match window_handle.raw_window_handle() {
let view = match window_handle
.window_handle()
.map_err(|e| format!("Error obtaining window handle for skia metal renderer: {e}"))?
.as_raw()
{
raw_window_handle::RawWindowHandle::AppKit(
raw_window_handle::AppKitWindowHandle { ns_view, .. },
) => ns_view,
) => ns_view.as_ptr(),
_ => {
return Err("Skia Renderer: Metal surface is only supported with AppKit".into())
}

View file

@ -1,7 +1,9 @@
// 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 std::{cell::RefCell, num::NonZeroU32};
use std::cell::RefCell;
use std::num::NonZeroU32;
use std::rc::Rc;
use glutin::{
config::GetGlConfig,
@ -24,8 +26,8 @@ pub struct OpenGLSurface {
impl super::Surface for OpenGLSurface {
fn new(
window_handle: raw_window_handle::WindowHandle<'_>,
display_handle: raw_window_handle::DisplayHandle<'_>,
window_handle: Rc<dyn raw_window_handle::HasWindowHandle>,
display_handle: Rc<dyn raw_window_handle::HasDisplayHandle>,
size: PhysicalWindowSize,
) -> Result<Self, PlatformError> {
let width: std::num::NonZeroU32 = size.width.try_into().map_err(|_| {
@ -35,6 +37,13 @@ impl super::Surface for OpenGLSurface {
format!("Attempting to create window surface with an invalid height: {}", size.height)
})?;
let window_handle = window_handle
.window_handle()
.map_err(|e| format!("error obtaining window handle for skia opengl renderer: {e}"))?;
let display_handle = display_handle
.display_handle()
.map_err(|e| format!("error obtaining display handle for skia opengl renderer: {e}"))?;
let (current_glutin_context, glutin_surface) =
Self::init_glutin(window_handle, display_handle, width, height)?;
@ -220,17 +229,14 @@ impl OpenGLSurface {
}
let gl_display = unsafe {
glutin::display::Display::new(
_display_handle.as_raw(),
display_api_preference,
)
.map_err(|glutin_error| {
format!(
"Error creating glutin display for native display {:#?}: {}",
_display_handle.as_raw(),
glutin_error
)
})?
glutin::display::Display::new(_display_handle.as_raw(), display_api_preference)
.map_err(|glutin_error| {
format!(
"Error creating glutin display for native display {:#?}: {}",
_display_handle.as_raw(),
glutin_error
)
})?
};
let config_template_builder = glutin::config::ConfigTemplateBuilder::new();
@ -315,10 +321,10 @@ impl OpenGLSurface {
if let raw_window_handle::RawWindowHandle::AppKit(raw_window_handle::AppKitWindowHandle {
ns_view,
..
}) = _window_handle.raw_window_handle()
}) = _window_handle.as_raw()
{
use cocoa::appkit::NSView;
let view_id: cocoa::base::id = ns_view as *const _ as *mut _;
let view_id: cocoa::base::id = ns_view.as_ptr() as *const _ as *mut _;
unsafe {
NSView::setLayerContentsPlacement(view_id, cocoa::appkit::NSViewLayerContentsPlacement::NSViewLayerContentsPlacementTopLeft)
}

View file

@ -3,7 +3,9 @@
use i_slint_core::api::PhysicalSize as PhysicalWindowSize;
use std::{cell::RefCell, num::NonZeroU32};
use std::cell::RefCell;
use std::num::NonZeroU32;
use std::rc::Rc;
pub trait RenderBuffer {
fn with_buffer(
@ -20,8 +22,13 @@ pub trait RenderBuffer {
}
struct SoftbufferRenderBuffer {
_context: softbuffer::Context,
surface: RefCell<softbuffer::Surface>,
_context: softbuffer::Context<Rc<dyn raw_window_handle::HasDisplayHandle>>,
surface: RefCell<
softbuffer::Surface<
Rc<dyn raw_window_handle::HasDisplayHandle>,
Rc<dyn raw_window_handle::HasWindowHandle>,
>,
>,
}
impl RenderBuffer for SoftbufferRenderBuffer {
@ -74,18 +81,17 @@ pub struct SoftwareSurface {
impl super::Surface for SoftwareSurface {
fn new(
window_handle: raw_window_handle::WindowHandle<'_>,
display_handle: raw_window_handle::DisplayHandle<'_>,
window_handle: Rc<dyn raw_window_handle::HasWindowHandle>,
display_handle: Rc<dyn raw_window_handle::HasDisplayHandle>,
_size: PhysicalWindowSize,
) -> Result<Self, i_slint_core::platform::PlatformError> {
let _context = unsafe {
softbuffer::Context::new(&display_handle)
.map_err(|e| format!("Error creating softbuffer context: {e}"))?
};
let _context = softbuffer::Context::new(display_handle)
.map_err(|e| format!("Error creating softbuffer context: {e}"))?;
let surface = unsafe { softbuffer::Surface::new(&_context, &window_handle) }.map_err(
|softbuffer_error| format!("Error creating softbuffer surface: {}", softbuffer_error),
)?;
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) });