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

Updated BackendSelector API #7090

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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::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::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
168 changes: 168 additions & 0 deletions internal/backends/selector/api.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
// 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 alloc::boxed::Box;
use alloc::{format, string::String};

use i_slint_core::api::PlatformError;
use i_slint_core::graphics::{RequestedGraphicsAPI, RequestedOpenGLVersion};

/// 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(RequestedOpenGLVersion::OpenGLES(Some((major, minor))).into());
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(RequestedOpenGLVersion::OpenGLES(None).into());
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(RequestedOpenGLVersion::OpenGL(None).into());
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(RequestedOpenGLVersion::OpenGL(Some((major, minor))).into());
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 to the selector that the backend must render with Direct 3D.
#[must_use]
pub fn require_d3d(mut self) -> Self {
self.requested_graphics_api = Some(RequestedGraphicsAPI::Direct3D);
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(all(feature = "i-slint-backend-linuxkms", target_os = "linux"))]
"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.as_deref(),
)?)
}
#[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))
}
}
74 changes: 7 additions & 67 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 @@ -39,78 +37,18 @@ fn create_linuxkms_backend() -> Result<Box<dyn Platform + 'static>, PlatformErro

cfg_if::cfg_if! {
if #[cfg(target_os = "android")] {
const DEFAULT_BACKEND_NAME: &'static str = "";
} 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)
}
}
const DEFAULT_BACKEND_NAME: &'static str = "";
}
}

Expand Down Expand Up @@ -223,3 +161,5 @@ pub fn with_global_context<R>(f: impl FnOnce(&SlintContext) -> R) -> Result<R, P

result
}

pub mod api;
Loading
Loading