Skip to content
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: 4 additions & 0 deletions crates/freya-performance-plugin/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ impl FreyaPlugin for PerformanceOverlayPlugin {
window,
graphics_driver,
..
}
| PluginEvent::GraphicsDriverChanged {
window,
graphics_driver,
} => {
self.get_metrics(window.id()).graphics_driver = graphics_driver;
}
Expand Down
69 changes: 52 additions & 17 deletions crates/freya-winit/src/drivers/gl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use gl::{
};
use glutin::{
config::{
Config,
ConfigTemplateBuilder,
GlConfig,
},
Expand Down Expand Up @@ -87,23 +88,40 @@ impl OpenGLDriver {

let display_builder = DisplayBuilder::new().with_window_attributes(Some(window_attributes));
let (window, gl_config) = display_builder.build(event_loop, template, |configs| {
configs
.reduce(|accum, config| {
let transparency_check = transparent
&& config.supports_transparency().unwrap_or(false)
&& !accum.supports_transparency().unwrap_or(false);

if transparency_check || config.num_samples() < accum.num_samples() {
config
} else {
accum
}
})
.expect("at least one OpenGL config")
Self::pick_config(configs, transparent)
})?;

let window = window.ok_or("OpenGL display builder returned no window")?;

let driver = Self::build(&gl_config, &window, gpu_resource_cache_limit)?;

Ok((driver, window))
}

/// Build the driver on an existing window instead of creating a new one.
pub fn from_window(
event_loop: &ActiveEventLoop,
window: &Window,
gpu_resource_cache_limit: usize,
transparent: bool,
) -> Result<Self, Box<dyn std::error::Error>> {
let template = ConfigTemplateBuilder::new()
.with_alpha_size(8)
.with_transparency(transparent)
.compatible_with_native_window(window.window_handle()?.as_raw());

let (_, gl_config) = DisplayBuilder::new().build(event_loop, template, |configs| {
Self::pick_config(configs, transparent)
})?;

Self::build(&gl_config, window, gpu_resource_cache_limit)
}

fn build(
gl_config: &Config,
window: &Window,
gpu_resource_cache_limit: usize,
) -> Result<Self, Box<dyn std::error::Error>> {
let window_handle = window.window_handle()?;

let context_attributes = ContextAttributesBuilder::new()
Expand All @@ -118,12 +136,12 @@ impl OpenGLDriver {
let not_current_gl_context = unsafe {
match gl_config
.display()
.create_context(&gl_config, &context_attributes)
.create_context(gl_config, &context_attributes)
{
Ok(ctx) => ctx,
Err(_) => gl_config
.display()
.create_context(&gl_config, &fallback_context_attributes)?,
.create_context(gl_config, &fallback_context_attributes)?,
}
};

Expand All @@ -138,7 +156,7 @@ impl OpenGLDriver {
let gl_surface = unsafe {
gl_config
.display()
.create_window_surface(&gl_config, &attrs)?
.create_window_surface(gl_config, &attrs)?
};

let gl_context = not_current_gl_context.make_current(&gl_surface)?;
Expand Down Expand Up @@ -208,7 +226,7 @@ impl OpenGLDriver {
surface,
};

Ok((driver, window))
Ok(driver)
}

pub fn present(&mut self, window: &Window, render: impl FnOnce(&mut SkiaSurface)) {
Expand Down Expand Up @@ -250,4 +268,21 @@ impl OpenGLDriver {

self.surface = surface;
}

/// Pick the OpenGL config, preferring transparency then fewer samples.
fn pick_config(configs: Box<dyn Iterator<Item = Config> + '_>, transparent: bool) -> Config {
configs
.reduce(|accum, config| {
let transparency_check = transparent
&& config.supports_transparency().unwrap_or(false)
&& !accum.supports_transparency().unwrap_or(false);

if transparency_check || config.num_samples() < accum.num_samples() {
config
} else {
accum
}
})
.expect("at least one OpenGL config")
}
}
70 changes: 62 additions & 8 deletions crates/freya-winit/src/drivers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ use winit::{
},
};

/// Unrecoverable graphics error requiring a driver rebuild.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[allow(dead_code)]
pub enum DriverError {
DeviceLost,
OutOfMemory,
}

#[allow(clippy::large_enum_variant)]
pub enum GraphicsDriver {
#[cfg(any(target_os = "linux", target_os = "windows", target_os = "android"))]
Expand Down Expand Up @@ -122,20 +130,57 @@ impl GraphicsDriver {
}
}

/// Rebuild the driver on the existing window, skipping Vulkan.
pub fn recover_reusing_window(
event_loop: &ActiveEventLoop,
window: &Window,
gpu_resource_cache_limit: usize,
transparent: bool,
) -> Self {
#[cfg(target_os = "macos")]
let _ = (event_loop, gpu_resource_cache_limit, transparent);

#[cfg(any(target_os = "linux", target_os = "windows", target_os = "android"))]
match gl::OpenGLDriver::from_window(
event_loop,
window,
gpu_resource_cache_limit,
transparent,
) {
Ok(driver) => return Self::OpenGl(driver),
Err(error) => {
tracing::warn!("OpenGL recovery failed, falling back to software: {error}");
}
}

let driver = software::SoftwareDriver::from_window(window)
.expect("Failed to initialize software renderer fallback");
Self::Software(driver)
}

pub fn present(
&mut self,
_size: PhysicalSize<u32>,
window: &Window,
render: impl FnOnce(&mut SkiaSurface),
) {
) -> Result<(), DriverError> {
match self {
#[cfg(any(target_os = "linux", target_os = "windows", target_os = "android"))]
Self::OpenGl(gl) => gl.present(window, render),
Self::OpenGl(gl) => {
gl.present(window, render);
Ok(())
}
#[cfg(target_os = "macos")]
Self::Metal(mtl) => mtl.present(_size, window, render),
Self::Metal(mtl) => {
mtl.present(_size, window, render);
Ok(())
}
#[cfg(any(target_os = "linux", target_os = "windows"))]
Self::Vulkan(vk) => vk.present(_size, window, render),
Self::Software(sw) => sw.present(_size, window, render),
Self::Software(sw) => {
sw.present(_size, window, render);
Ok(())
}
}
}

Expand All @@ -152,15 +197,24 @@ impl GraphicsDriver {
}
}

pub fn resize(&mut self, size: PhysicalSize<u32>) {
pub fn resize(&mut self, size: PhysicalSize<u32>) -> Result<(), DriverError> {
match self {
#[cfg(any(target_os = "linux", target_os = "windows", target_os = "android"))]
Self::OpenGl(gl) => gl.resize(size),
Self::OpenGl(gl) => {
gl.resize(size);
Ok(())
}
#[cfg(target_os = "macos")]
Self::Metal(mtl) => mtl.resize(size),
Self::Metal(mtl) => {
mtl.resize(size);
Ok(())
}
#[cfg(any(target_os = "linux", target_os = "windows"))]
Self::Vulkan(vk) => vk.resize(size),
Self::Software(sw) => sw.resize(size),
Self::Software(sw) => {
sw.resize(size);
Ok(())
}
}
}
}
16 changes: 9 additions & 7 deletions crates/freya-winit/src/drivers/software.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,12 @@ impl SoftwareDriver {
window_attributes: WindowAttributes,
) -> Result<(Self, Window), Box<dyn std::error::Error>> {
let window = event_loop.create_window(window_attributes)?;
let driver = Self::from_window(&window)?;
Ok((driver, window))
}

/// Build the driver on an existing window instead of creating a new one.
pub fn from_window(window: &Window) -> Result<Self, Box<dyn std::error::Error>> {
let display_handle = window.display_handle()?.as_raw();
let window_handle = window.window_handle()?.as_raw();

Expand All @@ -73,13 +78,10 @@ impl SoftwareDriver {
.map_err(|err| format!("Could not size softbuffer surface: {err}"))?;
}

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

pub fn present(
Expand Down
Loading
Loading