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