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        // Linux: Vulkan by default, set FREYA_RENDERER=opengl to force OpenGL.
51        // Windows: OpenGL by default, set FREYA_RENDERER=vulkan to force Vulkan.
52        #[cfg(all(not(target_os = "macos"), not(target_os = "android")))]
53        {
54            let renderer = std::env::var("FREYA_RENDERER");
55
56            let use_vulkan = if cfg!(target_os = "windows") {
57                renderer.is_ok_and(|v| v.eq_ignore_ascii_case("vulkan"))
58            } else {
59                !renderer.is_ok_and(|v| v.eq_ignore_ascii_case("opengl"))
60            };
61
62            if use_vulkan {
63                let vk_attrs = window_attributes.clone();
64                match vulkan::VulkanDriver::new(event_loop, vk_attrs) {
65                    Ok((driver, window)) => return (Self::Vulkan(driver), window),
66                    Err(err) => {
67                        tracing::warn!(
68                            "Vulkan initialization failed, falling back to OpenGL: {err}"
69                        );
70                    }
71                }
72            }
73
74            let (driver, window) = gl::OpenGLDriver::new(event_loop, window_attributes);
75
76            return (Self::OpenGl(driver), window);
77        }
78    }
79
80    pub fn present(
81        &mut self,
82        _size: PhysicalSize<u32>,
83        window: &Window,
84        render: impl FnOnce(&mut SkiaSurface),
85    ) {
86        match self {
87            #[cfg(any(target_os = "linux", target_os = "windows", target_os = "android"))]
88            Self::OpenGl(gl) => gl.present(window, render),
89            #[cfg(target_os = "macos")]
90            Self::Metal(mtl) => mtl.present(_size, window, render),
91            #[cfg(any(target_os = "linux", target_os = "windows"))]
92            Self::Vulkan(vk) => vk.present(_size, window, render),
93        }
94    }
95
96    /// The name of the active graphics driver.
97    pub fn name(&self) -> &'static str {
98        match self {
99            #[cfg(any(target_os = "linux", target_os = "windows", target_os = "android"))]
100            Self::OpenGl(_) => "OpenGL",
101            #[cfg(target_os = "macos")]
102            Self::Metal(_) => "Metal",
103            #[cfg(any(target_os = "linux", target_os = "windows"))]
104            Self::Vulkan(_) => "Vulkan",
105        }
106    }
107
108    pub fn resize(&mut self, size: PhysicalSize<u32>) {
109        match self {
110            #[cfg(any(target_os = "linux", target_os = "windows", target_os = "android"))]
111            Self::OpenGl(gl) => gl.resize(size),
112            #[cfg(target_os = "macos")]
113            Self::Metal(mtl) => mtl.resize(size),
114            #[cfg(any(target_os = "linux", target_os = "windows"))]
115            Self::Vulkan(vk) => vk.resize(size),
116        }
117    }
118}