1use std::{
2 ffi::CString,
3 num::NonZeroU32,
4};
5
6use freya_engine::prelude::{
7 ColorType,
8 DirectContext,
9 Format,
10 FramebufferInfo,
11 Interface,
12 Surface as SkiaSurface,
13 SurfaceOrigin,
14 backend_render_targets,
15 direct_contexts,
16 wrap_backend_render_target,
17};
18use gl::{
19 types::*,
20 *,
21};
22use glutin::{
23 config::{
24 ConfigTemplateBuilder,
25 GlConfig,
26 },
27 context::{
28 ContextApi,
29 ContextAttributesBuilder,
30 GlProfile,
31 NotCurrentGlContext,
32 PossiblyCurrentContext,
33 },
34 display::{
35 GetGlDisplay,
36 GlDisplay,
37 },
38 prelude::{
39 GlSurface,
40 PossiblyCurrentGlContext,
41 },
42 surface::{
43 Surface as GlutinSurface,
44 SurfaceAttributesBuilder,
45 SwapInterval,
46 WindowSurface,
47 },
48};
49use glutin_winit::DisplayBuilder;
50use raw_window_handle::HasWindowHandle;
51use winit::{
52 dpi::PhysicalSize,
53 event_loop::ActiveEventLoop,
54 window::{
55 Window,
56 WindowAttributes,
57 },
58};
59
60use crate::config::WindowConfig;
61
62pub struct OpenGLDriver {
64 pub(crate) gr_context: DirectContext,
65 pub(crate) gl_surface: GlutinSurface<WindowSurface>,
66 pub(crate) gl_context: PossiblyCurrentContext,
67 pub(crate) fb_info: FramebufferInfo,
68 pub(crate) num_samples: usize,
69 pub(crate) stencil_size: usize,
70 pub(crate) surface: SkiaSurface,
71}
72
73impl Drop for OpenGLDriver {
74 fn drop(&mut self) {
75 if !self.gl_context.is_current() && self.gl_context.make_current(&self.gl_surface).is_err()
76 {
77 self.gr_context.abandon();
78 }
79 }
80}
81
82impl OpenGLDriver {
83 pub fn new(
84 event_loop: &ActiveEventLoop,
85 window_attributes: WindowAttributes,
86 window_config: &WindowConfig,
87 ) -> (Self, Window) {
88 let template = ConfigTemplateBuilder::new()
89 .with_alpha_size(8)
90 .with_transparency(window_config.transparent);
91
92 let display_builder = DisplayBuilder::new().with_window_attributes(Some(window_attributes));
93 let (window, gl_config) = display_builder
94 .build(event_loop, template, |configs| {
95 configs
96 .reduce(|accum, config| {
97 let transparency_check = config.supports_transparency().unwrap_or(false)
98 & !accum.supports_transparency().unwrap_or(false);
99
100 if transparency_check || config.num_samples() < accum.num_samples() {
101 config
102 } else {
103 accum
104 }
105 })
106 .unwrap()
107 })
108 .unwrap();
109
110 let window = window.expect("Could not create window with OpenGL context");
111
112 let window_handle = window.window_handle().unwrap();
113
114 let context_attributes = ContextAttributesBuilder::new()
115 .with_profile(GlProfile::Core)
116 .build(Some(window_handle.as_raw()));
117
118 let fallback_context_attributes = ContextAttributesBuilder::new()
119 .with_profile(GlProfile::Core)
120 .with_context_api(ContextApi::Gles(None))
121 .build(Some(window_handle.as_raw()));
122
123 let not_current_gl_context = unsafe {
124 gl_config
125 .display()
126 .create_context(&gl_config, &context_attributes)
127 .unwrap_or_else(|_| {
128 gl_config
129 .display()
130 .create_context(&gl_config, &fallback_context_attributes)
131 .expect("failed to create context")
132 })
133 };
134
135 let size = window.inner_size();
136
137 let attrs = SurfaceAttributesBuilder::<WindowSurface>::new().build(
138 window_handle.as_raw(),
139 NonZeroU32::new(size.width).unwrap(),
140 NonZeroU32::new(size.height).unwrap(),
141 );
142
143 let gl_surface = unsafe {
144 gl_config
145 .display()
146 .create_window_surface(&gl_config, &attrs)
147 .expect("Could not create gl window surface")
148 };
149
150 let gl_context = not_current_gl_context
151 .make_current(&gl_surface)
152 .expect("Could not make GL context current when setting up skia renderer");
153
154 gl_surface
156 .set_swap_interval(&gl_context, SwapInterval::Wait(NonZeroU32::new(1).unwrap()))
157 .ok();
158
159 load_with(|s| {
160 gl_config
161 .display()
162 .get_proc_address(CString::new(s).unwrap().as_c_str())
163 });
164 let interface = Interface::new_load_with(|name| {
165 if name == "eglGetCurrentDisplay" {
166 return std::ptr::null();
167 }
168 gl_config
169 .display()
170 .get_proc_address(CString::new(name).unwrap().as_c_str())
171 })
172 .expect("Could not create interface");
173
174 let fb_info = {
175 let mut fboid: GLint = 0;
176 unsafe { GetIntegerv(FRAMEBUFFER_BINDING, &mut fboid) };
177
178 FramebufferInfo {
179 fboid: fboid.try_into().unwrap(),
180 format: Format::RGBA8.into(),
181 ..Default::default()
182 }
183 };
184
185 let num_samples = gl_config.num_samples() as usize;
186 let stencil_size = gl_config.stencil_size() as usize;
187
188 let mut gr_context =
189 direct_contexts::make_gl(interface, None).expect("Could not create direct context");
190
191 let render_target = backend_render_targets::make_gl(
192 (size.width as i32, size.height as i32),
193 num_samples,
194 stencil_size,
195 fb_info,
196 );
197 let surface = wrap_backend_render_target(
198 &mut gr_context,
199 &render_target,
200 SurfaceOrigin::BottomLeft,
201 ColorType::RGBA8888,
202 None,
203 None,
204 )
205 .expect("Could not create skia surface");
206
207 let driver = OpenGLDriver {
208 gl_context,
209 gl_surface,
210 gr_context,
211 num_samples,
212 stencil_size,
213 fb_info,
214 surface,
215 };
216
217 (driver, window)
218 }
219
220 pub fn present(&mut self, window: &Window, render: impl FnOnce(&mut SkiaSurface)) {
221 if !self.gl_context.is_current() {
222 self.gl_context.make_current(&self.gl_surface).unwrap();
223 }
224
225 render(&mut self.surface);
226
227 window.pre_present_notify();
228 self.gr_context.flush_submit_and_sync_cpu();
229 self.gl_surface.swap_buffers(&self.gl_context).unwrap();
230 }
231
232 pub fn resize(&mut self, size: PhysicalSize<u32>) {
233 let render_target = backend_render_targets::make_gl(
234 (size.width as i32, size.height as i32),
235 self.num_samples,
236 self.stencil_size,
237 self.fb_info,
238 );
239 let surface = wrap_backend_render_target(
240 &mut self.gr_context,
241 &render_target,
242 SurfaceOrigin::BottomLeft,
243 ColorType::RGBA8888,
244 None,
245 None,
246 )
247 .expect("Could not create skia surface");
248
249 self.gl_surface.resize(
250 &self.gl_context,
251 NonZeroU32::new(size.width).unwrap(),
252 NonZeroU32::new(size.height).unwrap(),
253 );
254
255 self.surface = surface;
256 }
257}