1#[cfg(any(target_os = "linux", target_os = "windows", target_os = "android"))]
2mod gl;
3#[cfg(target_os = "macos")]
4mod metal;
5mod software;
6#[cfg(any(target_os = "linux", target_os = "windows"))]
7mod vulkan;
8
9use freya_engine::prelude::Surface as SkiaSurface;
10use winit::{
11 dpi::PhysicalSize,
12 event_loop::ActiveEventLoop,
13 window::{
14 Window,
15 WindowAttributes,
16 },
17};
18
19#[allow(clippy::large_enum_variant)]
20pub enum GraphicsDriver {
21 #[cfg(any(target_os = "linux", target_os = "windows", target_os = "android"))]
22 OpenGl(gl::OpenGLDriver),
23 #[cfg(target_os = "macos")]
24 Metal(metal::MetalDriver),
25 #[cfg(any(target_os = "linux", target_os = "windows"))]
26 Vulkan(vulkan::VulkanDriver),
27 Software(software::SoftwareDriver),
28}
29
30impl GraphicsDriver {
31 #[allow(clippy::needless_return)]
32 pub fn new(
33 event_loop: &ActiveEventLoop,
34 window_attributes: WindowAttributes,
35 gpu_resource_cache_limit: usize,
36 ) -> (Self, Window) {
37 let renderer = std::env::var("FREYA_RENDERER")
38 .ok()
39 .map(|v| v.to_ascii_lowercase());
40 let renderer = renderer.as_deref();
41
42 if renderer == Some("software") {
44 match software::SoftwareDriver::new(event_loop, window_attributes.clone()) {
45 Ok((driver, window)) => return (Self::Software(driver), window),
46 Err(err) => {
47 tracing::warn!(
48 "Software renderer initialization failed, falling back to default: {err}"
49 );
50 }
51 }
52 }
53
54 #[cfg(target_os = "macos")]
56 {
57 let (driver, window) =
58 metal::MetalDriver::new(event_loop, window_attributes, gpu_resource_cache_limit);
59
60 return (Self::Metal(driver), window);
61 }
62
63 #[cfg(target_os = "android")]
65 {
66 match gl::OpenGLDriver::new(
67 event_loop,
68 window_attributes.clone(),
69 gpu_resource_cache_limit,
70 ) {
71 Ok((driver, window)) => return (Self::OpenGl(driver), window),
72 Err(err) => {
73 tracing::warn!("OpenGL initialization failed, falling back to software: {err}");
74 }
75 }
76
77 let (driver, window) = software::SoftwareDriver::new(event_loop, window_attributes)
78 .expect("Failed to initialize software renderer fallback");
79 return (Self::Software(driver), window);
80 }
81
82 #[cfg(all(not(target_os = "macos"), not(target_os = "android")))]
86 {
87 let use_vulkan = if cfg!(target_os = "windows") {
88 renderer == Some("vulkan")
89 } else {
90 renderer != Some("opengl")
91 };
92
93 if use_vulkan {
94 match vulkan::VulkanDriver::new(
95 event_loop,
96 window_attributes.clone(),
97 gpu_resource_cache_limit,
98 ) {
99 Ok((driver, window)) => return (Self::Vulkan(driver), window),
100 Err(err) => {
101 tracing::warn!(
102 "Vulkan initialization failed, falling back to OpenGL: {err}"
103 );
104 }
105 }
106 }
107
108 match gl::OpenGLDriver::new(
109 event_loop,
110 window_attributes.clone(),
111 gpu_resource_cache_limit,
112 ) {
113 Ok((driver, window)) => return (Self::OpenGl(driver), window),
114 Err(err) => {
115 tracing::warn!("OpenGL initialization failed, falling back to software: {err}");
116 }
117 }
118
119 let (driver, window) = software::SoftwareDriver::new(event_loop, window_attributes)
120 .expect("Failed to initialize software renderer fallback");
121 return (Self::Software(driver), window);
122 }
123 }
124
125 pub fn present(
126 &mut self,
127 _size: PhysicalSize<u32>,
128 window: &Window,
129 render: impl FnOnce(&mut SkiaSurface),
130 ) {
131 match self {
132 #[cfg(any(target_os = "linux", target_os = "windows", target_os = "android"))]
133 Self::OpenGl(gl) => gl.present(window, render),
134 #[cfg(target_os = "macos")]
135 Self::Metal(mtl) => mtl.present(_size, window, render),
136 #[cfg(any(target_os = "linux", target_os = "windows"))]
137 Self::Vulkan(vk) => vk.present(_size, window, render),
138 Self::Software(sw) => sw.present(_size, window, render),
139 }
140 }
141
142 pub fn name(&self) -> &'static str {
144 match self {
145 #[cfg(any(target_os = "linux", target_os = "windows", target_os = "android"))]
146 Self::OpenGl(_) => "OpenGL",
147 #[cfg(target_os = "macos")]
148 Self::Metal(_) => "Metal",
149 #[cfg(any(target_os = "linux", target_os = "windows"))]
150 Self::Vulkan(_) => "Vulkan",
151 Self::Software(_) => "Software",
152 }
153 }
154
155 pub fn resize(&mut self, size: PhysicalSize<u32>) {
156 match self {
157 #[cfg(any(target_os = "linux", target_os = "windows", target_os = "android"))]
158 Self::OpenGl(gl) => gl.resize(size),
159 #[cfg(target_os = "macos")]
160 Self::Metal(mtl) => mtl.resize(size),
161 #[cfg(any(target_os = "linux", target_os = "windows"))]
162 Self::Vulkan(vk) => vk.resize(size),
163 Self::Software(sw) => sw.resize(size),
164 }
165 }
166}