freya_winit/drivers/
gl.rs

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
62/// Graphics driver using OpenGL.
63pub 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        // Try setting vsync.
155        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}