Skip to main content

freya_winit/drivers/
mod.rs

1#[cfg(any(target_os = "linux", target_os = "windows", target_os = "android"))]
2mod gl;
3#[cfg(target_os = "macos")]
4mod metal;
5#[cfg(any(target_os = "linux", target_os = "windows"))]
6mod vulkan;
7
8use freya_engine::prelude::Surface as SkiaSurface;
9use winit::{
10    dpi::PhysicalSize,
11    event_loop::ActiveEventLoop,
12    window::{
13        Window,
14        WindowAttributes,
15    },
16};
17
18#[allow(clippy::large_enum_variant)]
19pub enum GraphicsDriver {
20    #[cfg(any(target_os = "linux", target_os = "windows", target_os = "android"))]
21    OpenGl(gl::OpenGLDriver),
22    #[cfg(target_os = "macos")]
23    Metal(metal::MetalDriver),
24    #[cfg(any(target_os = "linux", target_os = "windows"))]
25    Vulkan(vulkan::VulkanDriver),
26}
27
28impl GraphicsDriver {
29    #[allow(clippy::needless_return)]
30    pub fn new(
31        event_loop: &ActiveEventLoop,
32        window_attributes: WindowAttributes,
33    ) -> (Self, Window) {
34        // Metal (macOS)
35        #[cfg(target_os = "macos")]
36        {
37            let (driver, window) = metal::MetalDriver::new(event_loop, window_attributes);
38
39            return (Self::Metal(driver), window);
40        }
41
42        // OpenGL only on Android.
43        #[cfg(target_os = "android")]
44        {
45            let (driver, window) = gl::OpenGLDriver::new(event_loop, window_attributes);
46
47            return (Self::OpenGl(driver), window);
48        }
49
50        // Vulkan by default with OpenGL as fallback.
51        // Set FREYA_RENDERER=opengl to force OpenGL.
52        #[cfg(all(not(target_os = "macos"), not(target_os = "android")))]
53        {
54            let force_opengl =
55                std::env::var("FREYA_RENDERER").is_ok_and(|v| v.eq_ignore_ascii_case("opengl"));
56
57            if !force_opengl {
58                let vk_attrs = window_attributes.clone();
59                match vulkan::VulkanDriver::new(event_loop, vk_attrs) {
60                    Ok((driver, window)) => return (Self::Vulkan(driver), window),
61                    Err(err) => {
62                        tracing::warn!(
63                            "Vulkan initialization failed, falling back to OpenGL: {err}"
64                        );
65                    }
66                }
67            }
68
69            let (driver, window) = gl::OpenGLDriver::new(event_loop, window_attributes);
70
71            return (Self::OpenGl(driver), window);
72        }
73    }
74
75    pub fn present(
76        &mut self,
77        _size: PhysicalSize<u32>,
78        window: &Window,
79        render: impl FnOnce(&mut SkiaSurface),
80    ) {
81        match self {
82            #[cfg(any(target_os = "linux", target_os = "windows", target_os = "android"))]
83            Self::OpenGl(gl) => gl.present(window, render),
84            #[cfg(target_os = "macos")]
85            Self::Metal(mtl) => mtl.present(_size, window, render),
86            #[cfg(any(target_os = "linux", target_os = "windows"))]
87            Self::Vulkan(vk) => vk.present(_size, window, render),
88        }
89    }
90
91    /// The name of the active graphics driver.
92    pub fn name(&self) -> &'static str {
93        match self {
94            #[cfg(any(target_os = "linux", target_os = "windows", target_os = "android"))]
95            Self::OpenGl(_) => "OpenGL",
96            #[cfg(target_os = "macos")]
97            Self::Metal(_) => "Metal",
98            #[cfg(any(target_os = "linux", target_os = "windows"))]
99            Self::Vulkan(_) => "Vulkan",
100        }
101    }
102
103    pub fn resize(&mut self, size: PhysicalSize<u32>) {
104        match self {
105            #[cfg(any(target_os = "linux", target_os = "windows", target_os = "android"))]
106            Self::OpenGl(gl) => gl.resize(size),
107            #[cfg(target_os = "macos")]
108            Self::Metal(mtl) => mtl.resize(size),
109            #[cfg(any(target_os = "linux", target_os = "windows"))]
110            Self::Vulkan(vk) => vk.resize(size),
111        }
112    }
113}