Skip to content

Commit

Permalink
Work in progress for BackendSelector API
Browse files Browse the repository at this point in the history
  • Loading branch information
tronical committed Dec 13, 2024
1 parent bfdf0e2 commit ab55408
Show file tree
Hide file tree
Showing 18 changed files with 320 additions and 221 deletions.
4 changes: 2 additions & 2 deletions api/rs/slint/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -409,8 +409,6 @@ macro_rules! init_translations {
pub mod platform {
pub use i_slint_core::platform::*;

pub use i_slint_backend_selector::PlatformBuilder;

/// This module contains the [`femtovg_renderer::FemtoVGRenderer`] and related types.
///
/// It is only enabled when the `renderer-femtovg` Slint feature is enabled.
Expand All @@ -430,6 +428,8 @@ pub mod platform {
))]
pub mod android;

pub use i_slint_backend_selector::api::*;

/// Helper type that helps checking that the generated code is generated for the right version
#[doc(hidden)]
#[allow(non_camel_case_types)]
Expand Down
7 changes: 3 additions & 4 deletions examples/opengl_texture/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -398,11 +398,10 @@ impl DemoRenderer {
}

fn main() {
let platform = slint::platform::PlatformBuilder::new()
.with_opengl_api(slint::OpenGLAPI::GLES(None))
.build()
slint::platform::BackendSelector::new()
.require_opengl_es()
.select()
.expect("Unable to create Slint backend with OpenGL ES renderer");
slint::platform::set_platform(platform).unwrap();

let app = App::new().unwrap();

Expand Down
7 changes: 3 additions & 4 deletions examples/opengl_underlay/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,11 +176,10 @@ pub fn main() {
#[cfg(all(debug_assertions, target_arch = "wasm32"))]
console_error_panic_hook::set_once();

let platform = slint::platform::PlatformBuilder::new()
.with_opengl_api(slint::OpenGLAPI::GLES(None))
.build()
slint::platform::BackendSelector::new()
.require_opengl_es()
.select()
.expect("Unable to create Slint backend with OpenGL ES renderer");
slint::platform::set_platform(platform).unwrap();

let app = App::new().unwrap();

Expand Down
156 changes: 156 additions & 0 deletions internal/backends/selector/api.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
// Copyright © SixtyFPS GmbH <[email protected]>
// 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::PlatformError;
use i_slint_core::graphics::RequestedGraphicsAPI;

/// Use the BackendSelector to configure one of Slint's built-in backends with a renderer
/// to accomodate specific needs of your application. This is a programmatic substitute for
/// the `SLINT_BACKEND` environment variable.
///
/// For example, to configure Slint to use a renderer that supports OpenGL ES 3.0, configure
/// the `BackendSelector` as follows:
/// ```rust,no_run
/// # use i_slint_backend_selector::api::BackendSelector;
/// let selector = BackendSelector::new().require_opengl_es_with_version(3, 0);
/// if let Err(err) = selector.select() {
/// eprintln!("Error selecting backend with OpenGL ES support: {err}");
/// }
/// ```
pub struct BackendSelector {
requested_graphics_api: Option<RequestedGraphicsAPI>,
backend: Option<String>,
renderer: Option<String>,
}

impl BackendSelector {
/// Creates a new BackendSelector.
#[must_use]
pub fn new() -> BackendSelector {
BackendSelector { requested_graphics_api: None, backend: None, renderer: None }
}

/// Adds the requirement to the selector that the backend must render with OpenGL ES
/// and the specified major and minor version.
#[must_use]
pub fn require_opengl_es_with_version(mut self, major: u8, minor: u8) -> Self {
self.requested_graphics_api = Some(RequestedGraphicsAPI::OpenGLES(Some((major, minor))));
self
}

/// Adds the requirement to the selector that the backend must render with OpenGL ES.
#[must_use]
pub fn require_opengl_es(mut self) -> Self {
self.requested_graphics_api = Some(RequestedGraphicsAPI::OpenGLES(None));
self
}

/// Adds the requirement to the selector that the backend must render with OpenGL.
#[must_use]
pub fn require_opengl(mut self) -> Self {
self.requested_graphics_api = Some(RequestedGraphicsAPI::OpenGL(None));
self
}

/// Adds the requirement to the selector that the backend must render with OpenGL
/// and the specified major and minor version.
#[must_use]
pub fn require_opengl_with_version(mut self, major: u8, minor: u8) -> Self {
self.requested_graphics_api = Some(RequestedGraphicsAPI::OpenGL(Some((major, minor))));
self
}

/// Adds the requirement to the selector that the backend must render with Apple's Metal framework.
#[must_use]
pub fn require_metal(mut self) -> Self {
self.requested_graphics_api = Some(RequestedGraphicsAPI::Metal);
self
}

/// Adds the requirement to the selector that the backend must render with Vulkan.
#[must_use]
pub fn require_vulkan(mut self) -> Self {
self.requested_graphics_api = Some(RequestedGraphicsAPI::Vulkan);
self
}

/// Adds the requirement that the selected renderer must match the given name. This is
/// equivalent to setting the `SLINT_BACKEND=name` environment variable and requires
/// that the corresponding renderer feature is enabled. For example, to select the Skia renderer,
/// enable the `renderer-skia` feature and call this function with `skia` as argument.
#[must_use]
pub fn renderer_name(mut self, name: String) -> Self {
self.renderer = Some(name);
self
}

/// Adds the requirement that the selected backend must match the given name. This is
/// equivalent to setting the `SLINT_BACKEND=name` environment variable and requires
/// that the corresponding backend feature is enabled. For example, to select the winit backend,
/// enable the `backend-winit` feature and call this function with `winit` as argument.
#[must_use]
pub fn backend_name(mut self, name: String) -> Self {
self.backend = Some(name);
self
}

/// Completes the backend selection process and tries to combine with specified requirements
/// with the different backends and renderers enabled at compile time. On success, the selected
/// backend is automatically set to be active. Returns an error if the requirements could not be met.
pub fn select(self) -> Result<(), PlatformError> {
let backend_name = self.backend.as_deref().unwrap_or(super::DEFAULT_BACKEND_NAME);

let backend: Box<dyn i_slint_core::platform::Platform> = match backend_name {
#[cfg(feature = "i-slint-backend-linuxkms")]
"linuxkms" => {
if self.requested_graphics_api.is_some() {
return Err("The linuxkms backend does not implement renderer selection by graphics API".into());
}

Box::new(i_slint_backend_linuxkms::Backend::new_with_renderer_by_name(
self.renderer,
));
}
#[cfg(feature = "i-slint-backend-winit")]
"winit" => {
let builder = i_slint_backend_winit::Backend::builder();

let builder = match self.requested_graphics_api {
Some(api) => builder.request_graphics_api(api),
None => builder,
};

let builder = match self.renderer {
Some(name) => builder.with_renderer_name(name),
None => builder,
};

Box::new(builder.build()?)
}
#[cfg(feature = "i-slint-backend-qt")]
"qt" => {
if self.requested_graphics_api.is_some() {
return Err(
"The qt backend does not implement renderer selection by graphics API"
.into(),
);
}
if self.renderer.is_some() {
return Err(
"The qt backend does not implement renderer selection by name".into()
);
}
Box::new(i_slint_backend_qt::Backend::new())
}
requested_backend @ _ => {
return Err(format!(
"{requested_backend} backend requested but it is not available"
)
.into());
}
};

i_slint_core::platform::set_platform(backend)
.map_err(|set_platform_error| PlatformError::SetPlatformError(set_platform_error))
}
}
71 changes: 5 additions & 66 deletions internal/backends/selector/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@ extern crate alloc;
use alloc::boxed::Box;
use i_slint_core::platform::Platform;
use i_slint_core::platform::PlatformError;
use i_slint_core::OpenGLAPI;
use i_slint_core::SlintContext;
use i_slint_core::SlintRenderer;

#[cfg(all(feature = "i-slint-backend-qt", not(no_qt), not(target_os = "android")))]
fn create_qt_backend() -> Result<Box<dyn Platform + 'static>, PlatformError> {
Expand All @@ -41,79 +39,18 @@ cfg_if::cfg_if! {
if #[cfg(target_os = "android")] {
} else if #[cfg(all(feature = "i-slint-backend-qt", not(no_qt)))] {
use i_slint_backend_qt as default_backend;
const DEFAULT_BACKEND_NAME: &'static str = "qt";
} else if #[cfg(feature = "i-slint-backend-winit")] {
use i_slint_backend_winit as default_backend;
const DEFAULT_BACKEND_NAME: &'static str = "winit";
} else if #[cfg(all(feature = "i-slint-backend-linuxkms", target_os = "linux"))] {
use i_slint_backend_linuxkms as default_backend;
const DEFAULT_BACKEND_NAME: &'static str = "linuxkms";
} else {

}
}

pub struct PlatformBuilder {
opengl_api: Option<OpenGLAPI>,
renderer: Option<SlintRenderer>,
}

impl PlatformBuilder {
/// Creates a new PlatformBuilder for configuring aspects of the Platform.
pub fn new() -> PlatformBuilder {
PlatformBuilder { opengl_api: None, renderer: None }
}

/// Configures this builder to use the specified OpenGL API when building the platform later.
pub fn with_opengl_api(mut self, opengl_api: OpenGLAPI) -> Self {
self.opengl_api = Some(opengl_api);
self
}

/// Configures this builder to use the specified renderer when building the platform later.
pub fn with_renderer(mut self, renderer: SlintRenderer) -> Self {
self.renderer = Some(renderer);
self
}

/// Builds the platform with the parameters configured previously. Set the resulting platform
/// with `slint::platform::set_platform()`:
///
/// # Example
///
/// ```rust,no_run
/// use i_slint_core::OpenGLAPI;
/// use i_slint_core::platform;
/// use i_slint_backend_selector::PlatformBuilder;
///
/// let platform = PlatformBuilder::new()
/// .with_opengl_api(OpenGLAPI::GL(None))
/// .build()
/// .unwrap();
/// platform::set_platform(platform).unwrap();
/// ```
pub fn build(self) -> Result<Box<dyn Platform + 'static>, PlatformError> {
cfg_if::cfg_if! {
if #[cfg(feature = "i-slint-backend-winit")] {
let builder = i_slint_backend_winit::Backend::builder().with_allow_fallback(false);

let builder = match self.opengl_api {
Some(api) => builder.with_opengl_api(api),
None => builder,
};

let builder = match self.renderer {
Some(SlintRenderer::FemtoVG) => builder.with_renderer_name("femtovg"),
Some(SlintRenderer::Skia) => builder.with_renderer_name("skia"),
Some(SlintRenderer::Software) => builder.with_renderer_name("software"),
None => builder,
};

Ok(Box::new(builder.build()?))
} else {
Err(PlatformError::NoPlatform)
}
}
}
}

cfg_if::cfg_if! {
if #[cfg(all(not(target_os = "android"), any(
all(feature = "i-slint-backend-qt", not(no_qt)),
Expand Down Expand Up @@ -223,3 +160,5 @@ pub fn with_global_context<R>(f: impl FnOnce(&SlintContext) -> R) -> Result<R, P

result
}

pub mod api;
Loading

0 comments on commit ab55408

Please sign in to comment.