Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Internal FemtoVG renderer API cleanup #2840

Merged
merged 3 commits into from Jun 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 0 additions & 4 deletions internal/backends/winit/lib.rs
Expand Up @@ -30,7 +30,6 @@ mod renderer {
pub(crate) trait WinitCompatibleRenderer {
fn new(
window_builder: winit::window::WindowBuilder,
#[cfg(target_arch = "wasm32")] canvas_id: &str,
) -> Result<(Self, winit::window::Window), PlatformError>
where
Self: Sized;
Expand All @@ -43,9 +42,6 @@ mod renderer {
fn as_core_renderer(&self) -> &dyn i_slint_core::renderer::Renderer;

fn resize_event(&self, size: PhysicalSize) -> Result<(), PlatformError>;

#[cfg(target_arch = "wasm32")]
fn html_canvas_element(&self) -> web_sys::HtmlCanvasElement;
}

#[cfg(feature = "renderer-winit-femtovg")]
Expand Down
35 changes: 22 additions & 13 deletions internal/backends/winit/renderer/femtovg.rs
Expand Up @@ -6,6 +6,10 @@ use i_slint_core::platform::PlatformError;
use i_slint_core::renderer::Renderer;
use i_slint_renderer_femtovg::FemtoVGRenderer;

#[cfg(target_arch = "wasm32")]
use winit::platform::web::WindowExtWebSys;

#[cfg(not(target_arch = "wasm32"))]
mod glcontext;

pub struct GlutinFemtoVGRenderer {
Expand All @@ -15,18 +19,28 @@ pub struct GlutinFemtoVGRenderer {
impl super::WinitCompatibleRenderer for GlutinFemtoVGRenderer {
fn new(
window_builder: winit::window::WindowBuilder,
#[cfg(target_arch = "wasm32")] canvas_id: &str,
) -> Result<(Self, winit::window::Window), PlatformError> {
#[cfg(not(target_arch = "wasm32"))]
let (winit_window, opengl_context) = crate::event_loop::with_window_target(|event_loop| {
glcontext::OpenGLContext::new_context(
window_builder,
event_loop.event_loop_target(),
#[cfg(target_arch = "wasm32")]
canvas_id,
)
glcontext::OpenGLContext::new_context(window_builder, event_loop.event_loop_target())
})?;

#[cfg(target_arch = "wasm32")]
let winit_window = crate::event_loop::with_window_target(|event_loop| {
window_builder.build(event_loop.event_loop_target()).map_err(|winit_os_err| {
format!(
"FemtoVG Renderer: Could not create winit window wrapper for DOM canvas: {}",
winit_os_err
)
})
})?;

let renderer = FemtoVGRenderer::new(opengl_context)?;
let renderer = FemtoVGRenderer::new(
#[cfg(not(target_arch = "wasm32"))]
opengl_context,
#[cfg(target_arch = "wasm32")]
winit_window.canvas(),
)?;

Ok((Self { renderer }, winit_window))
}
Expand All @@ -50,9 +64,4 @@ impl super::WinitCompatibleRenderer for GlutinFemtoVGRenderer {
fn resize_event(&self, size: PhysicalWindowSize) -> Result<(), PlatformError> {
self.renderer.resize(size)
}

#[cfg(target_arch = "wasm32")]
fn html_canvas_element(&self) -> web_sys::HtmlCanvasElement {
self.renderer.html_canvas_element()
}
}
71 changes: 9 additions & 62 deletions internal/backends/winit/renderer/femtovg/glcontext.rs
@@ -1,33 +1,22 @@
// Copyright © SixtyFPS GmbH <info@slint-ui.com>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial

#[cfg(not(target_arch = "wasm32"))]
use glutin::{
context::{ContextApi, ContextAttributesBuilder},
display::GetGlDisplay,
prelude::*,
surface::{SurfaceAttributesBuilder, WindowSurface},
};
use i_slint_core::{api::PhysicalSize, platform::PlatformError};
#[cfg(not(target_arch = "wasm32"))]
use raw_window_handle::HasRawWindowHandle;

pub struct OpenGLContext {
#[cfg(not(target_arch = "wasm32"))]
context: glutin::context::PossiblyCurrentContext,
#[cfg(not(target_arch = "wasm32"))]
surface: glutin::surface::Surface<glutin::surface::WindowSurface>,
#[cfg(target_arch = "wasm32")]
canvas: web_sys::HtmlCanvasElement,
}

unsafe impl i_slint_renderer_femtovg::OpenGLContextWrapper for OpenGLContext {
#[cfg(target_arch = "wasm32")]
fn html_canvas_element(&self) -> web_sys::HtmlCanvasElement {
self.canvas.clone()
}
fn ensure_current(&self) -> Result<(), PlatformError> {
#[cfg(not(target_arch = "wasm32"))]
if !self.context.is_current() {
self.context.make_current(&self.surface).map_err(|glutin_error| -> PlatformError {
format!("FemtoVG: Error making context current: {glutin_error}").into()
Expand All @@ -36,7 +25,6 @@ unsafe impl i_slint_renderer_femtovg::OpenGLContextWrapper for OpenGLContext {
Ok(())
}
fn swap_buffers(&self) -> Result<(), PlatformError> {
#[cfg(not(target_arch = "wasm32"))]
self.surface.swap_buffers(&self.context).map_err(|glutin_error| -> PlatformError {
format!("FemtoVG: Error swapping buffers: {glutin_error}").into()
})?;
Expand All @@ -45,35 +33,25 @@ unsafe impl i_slint_renderer_femtovg::OpenGLContextWrapper for OpenGLContext {
}

fn resize(&self, _size: PhysicalSize) -> Result<(), PlatformError> {
#[cfg(not(target_arch = "wasm32"))]
{
let width = _size.width.try_into().map_err(|_| {
format!(
"Attempting to create window surface with an invalid width: {}",
_size.width
)
})?;
let height = _size.height.try_into().map_err(|_| {
format!(
"Attempting to create window surface with an invalid height: {}",
_size.height
)
})?;
let width = _size.width.try_into().map_err(|_| {
format!("Attempting to create window surface with an invalid width: {}", _size.width)
})?;
let height = _size.height.try_into().map_err(|_| {
format!("Attempting to create window surface with an invalid height: {}", _size.height)
})?;

self.ensure_current()?;
self.surface.resize(&self.context, width, height);

self.ensure_current()?;
self.surface.resize(&self.context, width, height);
}
Ok(())
}

#[cfg(not(target_arch = "wasm32"))]
fn get_proc_address(&self, name: &std::ffi::CStr) -> *const std::ffi::c_void {
self.context.display().get_proc_address(name)
}
}

impl OpenGLContext {
#[cfg(not(target_arch = "wasm32"))]
pub fn new_context<T>(
window_builder: winit::window::WindowBuilder,
window_target: &winit::event_loop::EventLoopWindowTarget<T>,
Expand Down Expand Up @@ -187,35 +165,4 @@ impl OpenGLContext {

Ok((window, Self { context, surface }))
}

#[cfg(target_arch = "wasm32")]
pub fn new_context<T>(
window_builder: winit::window::WindowBuilder,
window_target: &winit::event_loop::EventLoopWindowTarget<T>,
canvas_id: &str,
) -> Result<(winit::window::Window, Self), PlatformError> {
let window = window_builder.build(window_target).map_err(|winit_os_err| {
format!(
"FemtoVG Renderer: Could not create winit window wrapper for DOM canvas: {}",
winit_os_err
)
})?;

use wasm_bindgen::JsCast;

let canvas = web_sys::window()
.ok_or_else(|| "FemtoVG Renderer: Could not retrieve DOM window".to_string())?
.document()
.ok_or_else(|| "FemtoVG Renderer: Could not retrieve DOM document".to_string())?
.get_element_by_id(canvas_id)
.ok_or_else(|| {
format!("FemtoVG Renderer: Could not retrieve existing HTML Canvas element '{canvas_id}'")
})?
.dyn_into::<web_sys::HtmlCanvasElement>()
.map_err(|_| {
format!("FemtoVG Renderer: Specified DOM element '{canvas_id}' is not a HTML Canvas")
})?;

Ok((window, Self { canvas }))
}
}
17 changes: 7 additions & 10 deletions internal/backends/winit/winitwindowadapter.rs
Expand Up @@ -13,6 +13,9 @@ use std::rc::Rc;
#[cfg(target_arch = "wasm32")]
use std::rc::Weak;

#[cfg(target_arch = "wasm32")]
use winit::platform::web::WindowExtWebSys;

use crate::renderer::WinitCompatibleRenderer;
use const_field_offset::FieldOffsets;

Expand Down Expand Up @@ -126,13 +129,7 @@ impl WinitWindowAdapter {
#[cfg(target_arch = "wasm32")]
canvas_id,
)
.and_then(|builder| {
R::new(
builder,
#[cfg(target_arch = "wasm32")]
canvas_id,
)
})?;
.and_then(|builder| R::new(builder))?;

let self_rc = Rc::new_cyclic(|self_weak| Self {
window: OnceCell::with_value(corelib::api::Window::new(self_weak.clone() as _)),
Expand Down Expand Up @@ -415,7 +412,7 @@ impl WindowAdapterSealed for WinitWindowAdapter {
// Auto-resize to the preferred size if users (SlintPad) requests it
#[cfg(target_arch = "wasm32")]
{
let canvas = self.renderer().html_canvas_element();
let canvas = winit_window.canvas();

if canvas
.dataset()
Expand Down Expand Up @@ -461,7 +458,7 @@ impl WindowAdapterSealed for WinitWindowAdapter {

#[cfg(target_arch = "wasm32")]
{
let html_canvas = self.renderer().html_canvas_element();
let html_canvas = winit_window.canvas();
let existing_canvas_size = winit::dpi::LogicalSize::new(
html_canvas.client_width() as f32,
html_canvas.client_height() as f32,
Expand Down Expand Up @@ -587,7 +584,7 @@ impl WindowAdapterSealed for WinitWindowAdapter {
corelib::window::InputMethodRequest::Enable { .. } => {
let mut vkh = self.virtual_keyboard_helper.borrow_mut();
let h = vkh.get_or_insert_with(|| {
let canvas = self.renderer().html_canvas_element();
let canvas = self.winit_window().canvas();
super::wasm_input_helper::WasmInputHelper::new(self.self_weak.clone(), canvas)
});
h.show();
Expand Down
46 changes: 31 additions & 15 deletions internal/renderers/femtovg/lib.rs
Expand Up @@ -47,8 +47,23 @@ pub unsafe trait OpenGLContextWrapper {
fn resize(&self, size: PhysicalWindowSize) -> Result<(), PlatformError>;
#[cfg(not(target_arch = "wasm32"))]
fn get_proc_address(&self, name: &std::ffi::CStr) -> *const std::ffi::c_void;
#[cfg(target_arch = "wasm32")]
fn html_canvas_element(&self) -> web_sys::HtmlCanvasElement;
}

#[cfg(target_arch = "wasm32")]
struct WebGLNeedsNoCurrentContext;
#[cfg(target_arch = "wasm32")]
unsafe impl OpenGLContextWrapper for WebGLNeedsNoCurrentContext {
fn ensure_current(&self) -> Result<(), PlatformError> {
Ok(())
}

fn swap_buffers(&self) -> Result<(), PlatformError> {
Ok(())
}

fn resize(&self, _size: PhysicalWindowSize) -> Result<(), PlatformError> {
Ok(())
}
}

/// Use the FemtoVG renderer when implementing a custom Slint platform where you deliver events to
Expand All @@ -61,14 +76,22 @@ pub struct FemtoVGRenderer {
rendering_metrics_collector: RefCell<Option<Rc<RenderingMetricsCollector>>>,
// Last field, so that it's dropped last and context exists and is current when destroying the FemtoVG canvas
opengl_context: Box<dyn OpenGLContextWrapper>,
#[cfg(target_arch = "wasm32")]
canvas_id: String,
}

impl FemtoVGRenderer {
/// Creates a new renderer is associated with an implementation
/// of the OpenGLContextWrapper trait. The trait serves the purpose of giving the renderer control
/// over when the make the context current, how to retrieve the address of GL functions, and when
/// to swap back and front buffers.
pub fn new(opengl_context: impl OpenGLContextWrapper + 'static) -> Result<Self, PlatformError> {
pub fn new(
#[cfg(not(target_arch = "wasm32"))] opengl_context: impl OpenGLContextWrapper + 'static,
#[cfg(target_arch = "wasm32")] html_canvas: web_sys::HtmlCanvasElement,
) -> Result<Self, PlatformError> {
#[cfg(target_arch = "wasm32")]
let opengl_context = WebGLNeedsNoCurrentContext {};

let opengl_context = Box::new(opengl_context);
#[cfg(not(target_arch = "wasm32"))]
let gl_renderer = unsafe {
Expand All @@ -79,16 +102,13 @@ impl FemtoVGRenderer {
};

#[cfg(target_arch = "wasm32")]
let canvas = opengl_context.html_canvas_element();

#[cfg(target_arch = "wasm32")]
let gl_renderer = match femtovg::renderer::OpenGl::new_from_html_canvas(&canvas) {
let gl_renderer = match femtovg::renderer::OpenGl::new_from_html_canvas(&html_canvas) {
Ok(gl_renderer) => gl_renderer,
Err(_) => {
use wasm_bindgen::JsCast;

// I don't believe that there's a way of disabling the 2D canvas.
let context_2d = canvas
let context_2d = html_canvas
.get_context("2d")
.unwrap()
.unwrap()
Expand Down Expand Up @@ -118,6 +138,8 @@ impl FemtoVGRenderer {
texture_cache: Default::default(),
rendering_metrics_collector: Default::default(),
opengl_context,
#[cfg(target_arch = "wasm32")]
canvas_id: html_canvas.id(),
})
}

Expand Down Expand Up @@ -278,19 +300,13 @@ impl FemtoVGRenderer {
) -> Result<(), PlatformError> {
use i_slint_core::api::GraphicsAPI;

let canvas_element_id = self.opengl_context.html_canvas_element().id();
let api = GraphicsAPI::WebGL {
canvas_element_id: canvas_element_id.as_str(),
canvas_element_id: self.canvas_id.as_str(),
context_type: "webgl",
};
callback(api);
Ok(())
}

#[cfg(target_arch = "wasm32")]
pub fn html_canvas_element(&self) -> web_sys::HtmlCanvasElement {
self.opengl_context.html_canvas_element()
}
}

impl Renderer for FemtoVGRenderer {
Expand Down