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 self.gr_context.abandon();
76 }
77}
78
79impl OpenGLDriver {
80 pub fn new(
81 event_loop: &ActiveEventLoop,
82 window_attributes: WindowAttributes,
83 window_config: &WindowConfig,
84 ) -> (Self, Window) {
85 let template = ConfigTemplateBuilder::new()
86 .with_alpha_size(8)
87 .with_transparency(window_config.transparent);
88
89 let display_builder = DisplayBuilder::new().with_window_attributes(Some(window_attributes));
90 let (window, gl_config) = display_builder
91 .build(event_loop, template, |configs| {
92 configs
93 .reduce(|accum, config| {
94 let transparency_check = config.supports_transparency().unwrap_or(false)
95 & !accum.supports_transparency().unwrap_or(false);
96
97 if transparency_check || config.num_samples() < accum.num_samples() {
98 config
99 } else {
100 accum
101 }
102 })
103 .unwrap()
104 })
105 .unwrap();
106
107 let window = window.expect("Could not create window with OpenGL context");
108
109 let window_handle = window.window_handle().unwrap();
110
111 let context_attributes = ContextAttributesBuilder::new()
112 .with_profile(GlProfile::Core)
113 .build(Some(window_handle.as_raw()));
114
115 let fallback_context_attributes = ContextAttributesBuilder::new()
116 .with_profile(GlProfile::Core)
117 .with_context_api(ContextApi::Gles(None))
118 .build(Some(window_handle.as_raw()));
119
120 let not_current_gl_context = unsafe {
121 gl_config
122 .display()
123 .create_context(&gl_config, &context_attributes)
124 .unwrap_or_else(|_| {
125 gl_config
126 .display()
127 .create_context(&gl_config, &fallback_context_attributes)
128 .expect("failed to create context")
129 })
130 };
131
132 let size = window.inner_size();
133
134 let attrs = SurfaceAttributesBuilder::<WindowSurface>::new().build(
135 window_handle.as_raw(),
136 NonZeroU32::new(size.width).unwrap(),
137 NonZeroU32::new(size.height).unwrap(),
138 );
139
140 let gl_surface = unsafe {
141 gl_config
142 .display()
143 .create_window_surface(&gl_config, &attrs)
144 .expect("Could not create gl window surface")
145 };
146
147 let gl_context = not_current_gl_context
148 .make_current(&gl_surface)
149 .expect("Could not make GL context current when setting up skia renderer");
150
151 gl_surface
153 .set_swap_interval(&gl_context, SwapInterval::Wait(NonZeroU32::new(1).unwrap()))
154 .ok();
155
156 load_with(|s| {
157 gl_config
158 .display()
159 .get_proc_address(CString::new(s).unwrap().as_c_str())
160 });
161 let interface = Interface::new_load_with(|name| {
162 if name == "eglGetCurrentDisplay" {
163 return std::ptr::null();
164 }
165 gl_config
166 .display()
167 .get_proc_address(CString::new(name).unwrap().as_c_str())
168 })
169 .expect("Could not create interface");
170
171 let fb_info = {
172 let mut fboid: GLint = 0;
173 unsafe { GetIntegerv(FRAMEBUFFER_BINDING, &mut fboid) };
174
175 FramebufferInfo {
176 fboid: fboid.try_into().unwrap(),
177 format: Format::RGBA8.into(),
178 ..Default::default()
179 }
180 };
181
182 let num_samples = gl_config.num_samples() as usize;
183 let stencil_size = gl_config.stencil_size() as usize;
184
185 let mut gr_context =
186 direct_contexts::make_gl(interface, None).expect("Could not create direct context");
187
188 let render_target = backend_render_targets::make_gl(
189 (size.width as i32, size.height as i32),
190 num_samples,
191 stencil_size,
192 fb_info,
193 );
194 let surface = wrap_backend_render_target(
195 &mut gr_context,
196 &render_target,
197 SurfaceOrigin::BottomLeft,
198 ColorType::RGBA8888,
199 None,
200 None,
201 )
202 .expect("Could not create skia surface");
203
204 let driver = OpenGLDriver {
205 gl_context,
206 gl_surface,
207 gr_context,
208 num_samples,
209 stencil_size,
210 fb_info,
211 surface,
212 };
213
214 (driver, window)
215 }
216
217 pub fn present(&mut self, window: &Window, render: impl FnOnce(&mut SkiaSurface)) {
218 if !self.gl_context.is_current() {
219 self.gl_context.make_current(&self.gl_surface).unwrap();
220 }
221
222 render(&mut self.surface);
223
224 window.pre_present_notify();
225 self.gr_context.flush_submit_and_sync_cpu();
226 self.gl_surface.swap_buffers(&self.gl_context).unwrap();
227 }
228
229 pub fn resize(&mut self, size: PhysicalSize<u32>) {
230 let render_target = backend_render_targets::make_gl(
231 (size.width as i32, size.height as i32),
232 self.num_samples,
233 self.stencil_size,
234 self.fb_info,
235 );
236 let surface = wrap_backend_render_target(
237 &mut self.gr_context,
238 &render_target,
239 SurfaceOrigin::BottomLeft,
240 ColorType::RGBA8888,
241 None,
242 None,
243 )
244 .expect("Could not create skia surface");
245
246 self.gl_surface.resize(
247 &self.gl_context,
248 NonZeroU32::new(size.width.max(1)).unwrap(),
249 NonZeroU32::new(size.height.max(1)).unwrap(),
250 );
251
252 self.surface = surface;
253 }
254}