Skip to main content

egui_baseview/renderer/opengl/
renderer.rs

1use super::OpenGlError;
2use baseview::WindowContext;
3use baseview::dpi::PhysicalSize;
4use baseview::gl::GlConfig;
5use egui::FullOutput;
6use egui_glow::Painter;
7use std::sync::Arc;
8
9#[derive(Debug, Clone, PartialEq)]
10pub struct GraphicsConfig {
11    pub gl_config: GlConfig,
12
13    /// Controls whether to apply dithering to minimize banding artifacts.
14    ///
15    /// Dithering assumes an sRGB output and thus will apply noise to any input value that lies between
16    /// two 8bit values after applying the sRGB OETF function, i.e. if it's not a whole 8bit value in "gamma space".
17    /// This means that only inputs from texture interpolation and vertex colors should be affected in practice.
18    ///
19    /// Defaults to true.
20    pub dithering: bool,
21
22    /// Needed for cross compiling for VirtualBox VMSVGA driver with OpenGL ES 2.0 and OpenGL 2.1 which doesn't support SRGB texture.
23    /// See <https://github.com/emilk/egui/pull/1993>.
24    ///
25    /// For OpenGL ES 2.0: set this to [`egui_glow::ShaderVersion::Es100`] to solve blank texture problem (by using the "fallback shader").
26    pub shader_version: Option<egui_glow::ShaderVersion>,
27}
28
29impl Default for GraphicsConfig {
30    fn default() -> Self {
31        Self {
32            gl_config: GlConfig::default(),
33            shader_version: None,
34            dithering: true,
35        }
36    }
37}
38
39pub struct Renderer {
40    glow_context: Arc<egui_glow::glow::Context>,
41    painter: Painter,
42    pub window: WindowContext,
43}
44
45impl Renderer {
46    pub fn new(window: WindowContext, config: GraphicsConfig) -> Result<Self, OpenGlError> {
47        let context = window.gl_context().ok_or(OpenGlError::NoContext)?;
48        unsafe {
49            context.make_current();
50        }
51
52        let glow_context = Arc::new(unsafe {
53            egui_glow::glow::Context::from_loader_function(|s| context.get_proc_address(s))
54        });
55
56        let painter = egui_glow::Painter::new(
57            Arc::clone(&glow_context),
58            "",
59            config.shader_version,
60            config.dithering,
61        )
62        .map_err(OpenGlError::CreatePainter)?;
63
64        unsafe {
65            context.make_not_current();
66        }
67
68        Ok(Self {
69            window,
70            glow_context,
71            painter,
72        })
73    }
74
75    pub fn max_texture_side(&self) -> usize {
76        self.painter.max_texture_side()
77    }
78
79    pub fn render(
80        &mut self,
81        _window: &WindowContext,
82        clear_color: egui::Rgba,
83        physical_size: PhysicalSize<u32>,
84        pixels_per_point: f32,
85        egui_ctx: &mut egui::Context,
86        full_output: &mut FullOutput,
87    ) {
88        let PhysicalSize {
89            width: canvas_width,
90            height: canvas_height,
91        } = physical_size;
92
93        let shapes = std::mem::take(&mut full_output.shapes);
94        let textures_delta = &mut full_output.textures_delta;
95
96        let context = self
97            .window
98            .gl_context()
99            .expect("failed to get baseview gl context");
100        unsafe {
101            context.make_current();
102        }
103
104        unsafe {
105            use egui_glow::glow::HasContext as _;
106            self.glow_context.clear_color(
107                clear_color.r(),
108                clear_color.g(),
109                clear_color.b(),
110                clear_color.a(),
111            );
112            self.glow_context.clear(egui_glow::glow::COLOR_BUFFER_BIT);
113        }
114
115        for (id, image_delta) in &textures_delta.set {
116            self.painter.set_texture(*id, image_delta);
117        }
118
119        let clipped_primitives = egui_ctx.tessellate(shapes, pixels_per_point);
120        let dimensions: [u32; 2] = [canvas_width, canvas_height];
121
122        self.painter
123            .paint_primitives(dimensions, pixels_per_point, &clipped_primitives);
124
125        for id in textures_delta.free.drain(..) {
126            self.painter.free_texture(id);
127        }
128
129        unsafe {
130            context.swap_buffers();
131            context.make_not_current();
132        }
133    }
134}
135
136impl Drop for Renderer {
137    fn drop(&mut self) {
138        self.painter.destroy()
139    }
140}