fyrox_graphics/gl/
server.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21use crate::geometry_buffer::GpuGeometryBuffer;
22use crate::gpu_program::GpuProgram;
23use crate::query::GpuQuery;
24use crate::read_buffer::GpuAsyncReadBuffer;
25use crate::{
26    buffer::GpuBuffer,
27    buffer::{BufferKind, BufferUsage},
28    core::{color::Color, log::Log, math::Rect},
29    error::FrameworkError,
30    framebuffer::Attachment,
31    framebuffer::GpuFrameBuffer,
32    geometry_buffer::GeometryBufferDescriptor,
33    gl::{
34        self, framebuffer::GlFrameBuffer, geometry_buffer::GlGeometryBuffer, program::GlProgram,
35        query::GlQuery, read_buffer::GlAsyncReadBuffer, texture::GlTexture, ToGlConstant,
36    },
37    gpu_program::ShaderResourceDefinition,
38    gpu_texture::{GpuTexture, GpuTextureDescriptor},
39    server::{GraphicsServer, ServerCapabilities, SharedGraphicsServer},
40    stats::PipelineStatistics,
41    BlendEquation, BlendFactor, BlendFunc, BlendMode, ColorMask, CompareFunc, CullFace,
42    DrawParameters, PolygonFace, PolygonFillMode, ScissorBox, StencilAction, StencilFunc,
43    StencilOp,
44};
45use glow::HasContext;
46#[cfg(not(target_arch = "wasm32"))]
47use glutin::{
48    config::ConfigTemplateBuilder,
49    context::{
50        ContextApi, ContextAttributesBuilder, GlProfile, NotCurrentGlContext,
51        PossiblyCurrentContext, Version,
52    },
53    display::{GetGlDisplay, GlDisplay},
54    surface::{GlSurface, Surface, SwapInterval, WindowSurface},
55};
56#[cfg(not(target_arch = "wasm32"))]
57use glutin_winit::{DisplayBuilder, GlWindow};
58#[cfg(not(target_arch = "wasm32"))]
59use raw_window_handle::HasRawWindowHandle;
60use std::cell::RefCell;
61use std::ops::DerefMut;
62use std::rc::{Rc, Weak};
63#[cfg(not(target_arch = "wasm32"))]
64use std::{ffi::CString, num::NonZeroU32};
65use winit::{
66    event_loop::EventLoopWindowTarget,
67    window::{Window, WindowBuilder},
68};
69
70impl ToGlConstant for PolygonFace {
71    fn into_gl(self) -> u32 {
72        match self {
73            Self::Front => glow::FRONT,
74            Self::Back => glow::BACK,
75            Self::FrontAndBack => glow::FRONT_AND_BACK,
76        }
77    }
78}
79
80impl ToGlConstant for PolygonFillMode {
81    fn into_gl(self) -> u32 {
82        match self {
83            Self::Point => glow::POINT,
84            Self::Line => glow::LINE,
85            Self::Fill => glow::FILL,
86        }
87    }
88}
89
90impl ToGlConstant for StencilAction {
91    fn into_gl(self) -> u32 {
92        match self {
93            StencilAction::Keep => glow::KEEP,
94            StencilAction::Zero => glow::ZERO,
95            StencilAction::Replace => glow::REPLACE,
96            StencilAction::Incr => glow::INCR,
97            StencilAction::IncrWrap => glow::INCR_WRAP,
98            StencilAction::Decr => glow::DECR,
99            StencilAction::DecrWrap => glow::DECR_WRAP,
100            StencilAction::Invert => glow::INVERT,
101        }
102    }
103}
104
105impl ToGlConstant for BlendMode {
106    fn into_gl(self) -> u32 {
107        match self {
108            Self::Add => glow::FUNC_ADD,
109            Self::Subtract => glow::FUNC_SUBTRACT,
110            Self::ReverseSubtract => glow::FUNC_REVERSE_SUBTRACT,
111            Self::Min => glow::MIN,
112            Self::Max => glow::MAX,
113        }
114    }
115}
116
117impl ToGlConstant for BlendFactor {
118    fn into_gl(self) -> u32 {
119        match self {
120            Self::Zero => glow::ZERO,
121            Self::One => glow::ONE,
122            Self::SrcColor => glow::SRC_COLOR,
123            Self::OneMinusSrcColor => glow::ONE_MINUS_SRC_COLOR,
124            Self::DstColor => glow::DST_COLOR,
125            Self::OneMinusDstColor => glow::ONE_MINUS_DST_COLOR,
126            Self::SrcAlpha => glow::SRC_ALPHA,
127            Self::OneMinusSrcAlpha => glow::ONE_MINUS_SRC_ALPHA,
128            Self::DstAlpha => glow::DST_ALPHA,
129            Self::OneMinusDstAlpha => glow::ONE_MINUS_DST_ALPHA,
130            Self::ConstantColor => glow::CONSTANT_COLOR,
131            Self::OneMinusConstantColor => glow::ONE_MINUS_CONSTANT_COLOR,
132            Self::ConstantAlpha => glow::CONSTANT_ALPHA,
133            Self::OneMinusConstantAlpha => glow::ONE_MINUS_CONSTANT_ALPHA,
134            Self::SrcAlphaSaturate => glow::SRC_ALPHA_SATURATE,
135            Self::Src1Color => glow::SRC1_COLOR,
136            Self::OneMinusSrc1Color => glow::ONE_MINUS_SRC1_COLOR,
137            Self::Src1Alpha => glow::SRC1_ALPHA,
138            Self::OneMinusSrc1Alpha => glow::ONE_MINUS_SRC1_ALPHA,
139        }
140    }
141}
142
143impl ToGlConstant for CompareFunc {
144    fn into_gl(self) -> u32 {
145        match self {
146            Self::Never => glow::NEVER,
147            Self::Less => glow::LESS,
148            Self::Equal => glow::EQUAL,
149            Self::LessOrEqual => glow::LEQUAL,
150            Self::Greater => glow::GREATER,
151            Self::NotEqual => glow::NOTEQUAL,
152            Self::GreaterOrEqual => glow::GEQUAL,
153            Self::Always => glow::ALWAYS,
154        }
155    }
156}
157
158impl ToGlConstant for CullFace {
159    fn into_gl(self) -> u32 {
160        match self {
161            Self::Back => glow::BACK,
162            Self::Front => glow::FRONT,
163        }
164    }
165}
166
167#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
168pub enum GlKind {
169    OpenGL,
170    OpenGLES,
171}
172
173pub(crate) struct InnerState {
174    blend: bool,
175
176    depth_test: bool,
177    depth_write: bool,
178    depth_func: CompareFunc,
179
180    color_write: ColorMask,
181    stencil_test: bool,
182    cull_face: CullFace,
183    culling: bool,
184    stencil_mask: u32,
185    clear_color: Color,
186    clear_stencil: i32,
187    clear_depth: f32,
188    scissor_test: bool,
189
190    polygon_face: PolygonFace,
191    polygon_fill_mode: PolygonFillMode,
192
193    framebuffer: Option<glow::Framebuffer>,
194    viewport: Rect<i32>,
195
196    blend_func: BlendFunc,
197    blend_equation: BlendEquation,
198
199    program: Option<glow::Program>,
200    texture_units_storage: TextureUnitsStorage,
201
202    stencil_func: StencilFunc,
203    stencil_op: StencilOp,
204
205    vao: Option<glow::VertexArray>,
206
207    frame_statistics: PipelineStatistics,
208    gl_kind: GlKind,
209
210    pub(crate) queries: Vec<glow::Query>,
211
212    #[cfg(not(target_arch = "wasm32"))]
213    gl_context: PossiblyCurrentContext,
214    #[cfg(not(target_arch = "wasm32"))]
215    gl_surface: Surface<WindowSurface>,
216}
217
218impl InnerState {
219    fn new(
220        gl_kind: GlKind,
221        #[cfg(not(target_arch = "wasm32"))] gl_context: PossiblyCurrentContext,
222        #[cfg(not(target_arch = "wasm32"))] gl_surface: Surface<WindowSurface>,
223    ) -> Self {
224        Self {
225            blend: false,
226            depth_test: false,
227            depth_write: true,
228            depth_func: Default::default(),
229            color_write: Default::default(),
230            stencil_test: false,
231            cull_face: CullFace::Back,
232            culling: false,
233            stencil_mask: 0xFFFF_FFFF,
234            clear_color: Color::from_rgba(0, 0, 0, 0),
235            clear_stencil: 0,
236            clear_depth: 1.0,
237            scissor_test: false,
238            polygon_face: Default::default(),
239            polygon_fill_mode: Default::default(),
240            framebuffer: None,
241            blend_func: Default::default(),
242            viewport: Rect::new(0, 0, 1, 1),
243            program: Default::default(),
244            texture_units_storage: TextureUnitsStorage {
245                active_unit: 0,
246                units: Default::default(),
247            },
248            stencil_func: Default::default(),
249            stencil_op: Default::default(),
250            vao: Default::default(),
251            frame_statistics: Default::default(),
252            blend_equation: Default::default(),
253            gl_kind,
254            queries: Default::default(),
255            #[cfg(not(target_arch = "wasm32"))]
256            gl_context,
257            #[cfg(not(target_arch = "wasm32"))]
258            gl_surface,
259        }
260    }
261}
262
263pub struct GlGraphicsServer {
264    pub gl: glow::Context,
265    pub(crate) state: RefCell<InnerState>,
266    this: RefCell<Option<Weak<GlGraphicsServer>>>,
267}
268
269#[derive(Copy, Clone)]
270struct TextureBinding {
271    target: u32,
272    texture: Option<glow::Texture>,
273}
274
275#[derive(Copy, Clone)]
276struct TextureUnit {
277    bindings: [TextureBinding; 4],
278}
279
280impl Default for TextureUnit {
281    fn default() -> Self {
282        Self {
283            bindings: [
284                TextureBinding {
285                    target: glow::TEXTURE_2D,
286                    texture: None,
287                },
288                TextureBinding {
289                    target: glow::TEXTURE_3D,
290                    texture: None,
291                },
292                TextureBinding {
293                    target: glow::TEXTURE_1D,
294                    texture: None,
295                },
296                TextureBinding {
297                    target: glow::TEXTURE_CUBE_MAP,
298                    texture: None,
299                },
300            ],
301        }
302    }
303}
304
305#[derive(Default)]
306struct TextureUnitsStorage {
307    active_unit: u32,
308    units: [TextureUnit; 32],
309}
310
311impl GlGraphicsServer {
312    #[allow(clippy::new_ret_no_self)]
313    #[allow(unused_mut)]
314    pub fn new(
315        #[allow(unused_variables)] vsync: bool,
316        #[allow(unused_variables)] msaa_sample_count: Option<u8>,
317        window_target: &EventLoopWindowTarget<()>,
318        window_builder: WindowBuilder,
319    ) -> Result<(Window, SharedGraphicsServer), FrameworkError> {
320        #[cfg(not(target_arch = "wasm32"))]
321        let (window, gl_context, gl_surface, mut context, gl_kind) = {
322            let mut template = ConfigTemplateBuilder::new()
323                .prefer_hardware_accelerated(Some(true))
324                .with_stencil_size(8)
325                .with_depth_size(24);
326
327            if let Some(sample_count) = msaa_sample_count {
328                template = template.with_multisampling(sample_count);
329            }
330
331            let (opt_window, gl_config) = DisplayBuilder::new()
332                .with_window_builder(Some(window_builder))
333                .build(window_target, template, |mut configs| {
334                    configs.next().unwrap()
335                })?;
336
337            let window = opt_window.unwrap();
338
339            let raw_window_handle = window.raw_window_handle();
340
341            let gl_display = gl_config.display();
342
343            #[cfg(debug_assertions)]
344            let debug = true;
345
346            #[cfg(not(debug_assertions))]
347            let debug = true;
348
349            let gl3_3_core_context_attributes = ContextAttributesBuilder::new()
350                .with_debug(debug)
351                .with_profile(GlProfile::Core)
352                .with_context_api(ContextApi::OpenGl(Some(Version::new(3, 3))))
353                .build(Some(raw_window_handle));
354
355            let gles3_context_attributes = ContextAttributesBuilder::new()
356                .with_debug(debug)
357                .with_profile(GlProfile::Core)
358                .with_context_api(ContextApi::Gles(Some(Version::new(3, 0))))
359                .build(Some(raw_window_handle));
360
361            unsafe {
362                let attrs = window.build_surface_attributes(Default::default());
363
364                let gl_surface = gl_config
365                    .display()
366                    .create_window_surface(&gl_config, &attrs)?;
367
368                let (non_current_gl_context, gl_kind) = if let Ok(gl3_3_core_context) =
369                    gl_display.create_context(&gl_config, &gl3_3_core_context_attributes)
370                {
371                    (gl3_3_core_context, GlKind::OpenGL)
372                } else {
373                    (
374                        gl_display.create_context(&gl_config, &gles3_context_attributes)?,
375                        GlKind::OpenGLES,
376                    )
377                };
378
379                let gl_context = non_current_gl_context.make_current(&gl_surface)?;
380
381                if vsync {
382                    Log::verify(gl_surface.set_swap_interval(
383                        &gl_context,
384                        SwapInterval::Wait(NonZeroU32::new(1).unwrap()),
385                    ));
386                }
387
388                (
389                    window,
390                    gl_context,
391                    gl_surface,
392                    glow::Context::from_loader_function(|s| {
393                        gl_display.get_proc_address(&CString::new(s).unwrap())
394                    }),
395                    gl_kind,
396                )
397            }
398        };
399
400        #[cfg(target_arch = "wasm32")]
401        let (window, mut context, gl_kind) = {
402            use crate::core::wasm_bindgen::JsCast;
403            use serde::{Deserialize, Serialize};
404            use winit::{
405                dpi::{LogicalSize, PhysicalSize},
406                platform::web::WindowExtWebSys,
407            };
408
409            let inner_size = window_builder.window_attributes().inner_size;
410            let window = window_builder.build(window_target).unwrap();
411
412            let web_window = crate::core::web_sys::window().unwrap();
413            let scale_factor = web_window.device_pixel_ratio();
414
415            let canvas = window.canvas().unwrap();
416
417            // For some reason winit completely ignores the requested inner size. This is a quick-n-dirty fix
418            // that also handles HiDPI monitors. It has one issue - if user changes DPI, it won't be handled
419            // correctly.
420            if let Some(inner_size) = inner_size {
421                let physical_inner_size: PhysicalSize<u32> = inner_size.to_physical(scale_factor);
422
423                canvas.set_width(physical_inner_size.width);
424                canvas.set_height(physical_inner_size.height);
425
426                let logical_inner_size: LogicalSize<f64> = inner_size.to_logical(scale_factor);
427                Log::verify(
428                    canvas
429                        .style()
430                        .set_property("width", &format!("{}px", logical_inner_size.width)),
431                );
432                Log::verify(
433                    canvas
434                        .style()
435                        .set_property("height", &format!("{}px", logical_inner_size.height)),
436                );
437            }
438
439            let document = web_window.document().unwrap();
440            let body = document.body().unwrap();
441
442            body.append_child(&canvas)
443                .expect("Append canvas to HTML body");
444
445            #[derive(Serialize, Deserialize)]
446            #[allow(non_snake_case)]
447            struct ContextAttributes {
448                alpha: bool,
449                premultipliedAlpha: bool,
450                powerPreference: String,
451            }
452
453            let context_attributes = ContextAttributes {
454                // Prevent blending with the background of the canvas. Otherwise the background
455                // will "leak" and interfere with the pixels produced by the engine.
456                alpha: false,
457                premultipliedAlpha: false,
458                // Try to use high performance GPU.
459                powerPreference: "high-performance".to_string(),
460            };
461
462            let webgl2_context = canvas
463                .get_context_with_context_options(
464                    "webgl2",
465                    &serde_wasm_bindgen::to_value(&context_attributes).unwrap(),
466                )
467                .unwrap()
468                .unwrap()
469                .dyn_into::<crate::core::web_sys::WebGl2RenderingContext>()
470                .unwrap();
471            (
472                window,
473                glow::Context::from_webgl2_context(webgl2_context),
474                GlKind::OpenGLES,
475            )
476        };
477
478        #[cfg(not(target_arch = "wasm32"))]
479        gl_surface.resize(
480            &gl_context,
481            NonZeroU32::new(window.inner_size().width)
482                .unwrap_or_else(|| NonZeroU32::new(1).unwrap()),
483            NonZeroU32::new(window.inner_size().height)
484                .unwrap_or_else(|| NonZeroU32::new(1).unwrap()),
485        );
486
487        // Dump available GL extensions to the log, this will help debugging graphical issues.
488        Log::info(format!(
489            "Supported GL Extensions: {:?}",
490            context.supported_extensions()
491        ));
492
493        unsafe {
494            context.depth_func(CompareFunc::default().into_gl());
495
496            #[cfg(debug_assertions)]
497            {
498                use crate::core::log::{Log, MessageKind};
499
500                if context.supported_extensions().contains("GL_KHR_debug") {
501                    context.debug_message_callback(|source, msg_type, id, severity, message| {
502                        let message_kind = if severity == glow::DEBUG_SEVERITY_HIGH {
503                            MessageKind::Error
504                        } else if severity == glow::DEBUG_SEVERITY_MEDIUM
505                            || severity == glow::DEBUG_SEVERITY_LOW
506                        {
507                            MessageKind::Warning
508                        } else {
509                            // Ignore any info because it tend to produce spam.
510                            return;
511                        };
512
513                        let source = if source == glow::DEBUG_SOURCE_API {
514                            "Calls to the OpenGL API"
515                        } else if source == glow::DEBUG_SOURCE_WINDOW_SYSTEM {
516                            "Calls to a window-system API"
517                        } else if source == glow::DEBUG_SOURCE_SHADER_COMPILER {
518                            "A compiler for a shading language"
519                        } else if source == glow::DEBUG_SOURCE_THIRD_PARTY {
520                            "An application associated with OpenGL"
521                        } else if source == glow::DEBUG_SOURCE_APPLICATION {
522                            "Generated by the user of this application"
523                        } else {
524                            "Other"
525                        };
526
527                        let msg_type = if msg_type == glow::DEBUG_TYPE_ERROR {
528                            "An error, typically from the API"
529                        } else if msg_type == glow::DEBUG_TYPE_DEPRECATED_BEHAVIOR {
530                            "Some behavior marked deprecated has been used"
531                        } else if msg_type == glow::DEBUG_TYPE_UNDEFINED_BEHAVIOR {
532                            "Something has invoked undefined behavior"
533                        } else if msg_type == glow::DEBUG_TYPE_PORTABILITY {
534                            "Some functionality the user relies upon is not portable"
535                        } else if msg_type == glow::DEBUG_TYPE_PERFORMANCE {
536                            "Code has triggered possible performance issues"
537                        } else if msg_type == glow::DEBUG_TYPE_MARKER {
538                            "Command stream annotation"
539                        } else if msg_type == glow::DEBUG_TYPE_PUSH_GROUP
540                            || msg_type == glow::DEBUG_TYPE_POP_GROUP
541                        {
542                            "Group pushing"
543                        } else {
544                            "Other"
545                        };
546
547                        Log::writeln(
548                            message_kind,
549                            format!(
550                                "OpenGL Message\n\
551                            \tSource: {source}\n\
552                            \tType: {msg_type}\n\
553                            \tId: {id}\n\
554                            \tMessage: {message}"
555                            ),
556                        );
557                    })
558                }
559            }
560        }
561
562        let state = Self {
563            gl: context,
564            state: RefCell::new(InnerState::new(
565                gl_kind,
566                #[cfg(not(target_arch = "wasm32"))]
567                gl_context,
568                #[cfg(not(target_arch = "wasm32"))]
569                gl_surface,
570            )),
571            this: Default::default(),
572        };
573
574        let shared = Rc::new(state);
575
576        *shared.this.borrow_mut() = Some(Rc::downgrade(&shared));
577
578        Ok((window, shared))
579    }
580
581    pub fn weak(&self) -> Weak<Self> {
582        self.this.borrow().as_ref().unwrap().clone()
583    }
584
585    pub fn gl_kind(&self) -> GlKind {
586        self.state.borrow().gl_kind
587    }
588
589    pub fn free_texture_unit(&self) -> Option<u32> {
590        let state = self.state.borrow();
591        for (index, unit) in state.texture_units_storage.units.iter().enumerate() {
592            if unit
593                .bindings
594                .iter()
595                .all(|binding| binding.texture.is_none())
596            {
597                return Some(index as u32);
598            }
599        }
600        None
601    }
602
603    pub(crate) fn set_framebuffer(&self, framebuffer: Option<glow::Framebuffer>) {
604        let mut state = self.state.borrow_mut();
605        if state.framebuffer != framebuffer {
606            state.framebuffer = framebuffer;
607
608            state.frame_statistics.framebuffer_binding_changes += 1;
609
610            unsafe {
611                self.gl
612                    .bind_framebuffer(glow::FRAMEBUFFER, state.framebuffer)
613            }
614        }
615    }
616
617    pub(crate) fn set_viewport(&self, viewport: Rect<i32>) {
618        let mut state = self.state.borrow_mut();
619        if state.viewport != viewport {
620            state.viewport = viewport;
621
622            unsafe {
623                self.gl.viewport(
624                    state.viewport.x(),
625                    state.viewport.y(),
626                    state.viewport.w(),
627                    state.viewport.h(),
628                );
629            }
630        }
631    }
632
633    pub(crate) fn set_blend(&self, blend: bool) {
634        let mut state = self.state.borrow_mut();
635        if state.blend != blend {
636            state.blend = blend;
637
638            state.frame_statistics.blend_state_changes += 1;
639
640            unsafe {
641                if state.blend {
642                    self.gl.enable(glow::BLEND);
643                } else {
644                    self.gl.disable(glow::BLEND);
645                }
646            }
647        }
648    }
649
650    pub(crate) fn set_depth_test(&self, depth_test: bool) {
651        let mut state = self.state.borrow_mut();
652        if state.depth_test != depth_test {
653            state.depth_test = depth_test;
654
655            unsafe {
656                if state.depth_test {
657                    self.gl.enable(glow::DEPTH_TEST);
658                } else {
659                    self.gl.disable(glow::DEPTH_TEST);
660                }
661            }
662        }
663    }
664
665    pub(crate) fn set_depth_write(&self, depth_write: bool) {
666        let mut state = self.state.borrow_mut();
667        if state.depth_write != depth_write {
668            state.depth_write = depth_write;
669
670            unsafe {
671                self.gl.depth_mask(state.depth_write);
672            }
673        }
674    }
675
676    pub(crate) fn set_color_write(&self, color_write: ColorMask) {
677        let mut state = self.state.borrow_mut();
678        if state.color_write != color_write {
679            state.color_write = color_write;
680
681            unsafe {
682                self.gl.color_mask(
683                    state.color_write.red,
684                    state.color_write.green,
685                    state.color_write.blue,
686                    state.color_write.alpha,
687                );
688            }
689        }
690    }
691
692    pub(crate) fn set_stencil_test(&self, stencil_test: bool) {
693        let mut state = self.state.borrow_mut();
694        if state.stencil_test != stencil_test {
695            state.stencil_test = stencil_test;
696
697            unsafe {
698                if state.stencil_test {
699                    self.gl.enable(glow::STENCIL_TEST);
700                } else {
701                    self.gl.disable(glow::STENCIL_TEST);
702                }
703            }
704        }
705    }
706
707    pub(crate) fn set_cull_face(&self, cull_face: CullFace) {
708        let mut state = self.state.borrow_mut();
709        if state.cull_face != cull_face {
710            state.cull_face = cull_face;
711
712            unsafe { self.gl.cull_face(state.cull_face.into_gl()) }
713        }
714    }
715
716    pub(crate) fn set_culling(&self, culling: bool) {
717        let mut state = self.state.borrow_mut();
718        if state.culling != culling {
719            state.culling = culling;
720
721            unsafe {
722                if state.culling {
723                    self.gl.enable(glow::CULL_FACE);
724                } else {
725                    self.gl.disable(glow::CULL_FACE);
726                }
727            }
728        }
729    }
730
731    pub(crate) fn set_stencil_mask(&self, stencil_mask: u32) {
732        let mut state = self.state.borrow_mut();
733        if state.stencil_mask != stencil_mask {
734            state.stencil_mask = stencil_mask;
735
736            unsafe {
737                self.gl.stencil_mask(stencil_mask);
738            }
739        }
740    }
741
742    pub(crate) fn set_clear_color(&self, color: Color) {
743        let mut state = self.state.borrow_mut();
744        if state.clear_color != color {
745            state.clear_color = color;
746
747            let rgba = color.as_frgba();
748            unsafe {
749                self.gl.clear_color(rgba.x, rgba.y, rgba.z, rgba.w);
750            }
751        }
752    }
753
754    pub(crate) fn set_clear_depth(&self, depth: f32) {
755        let mut state = self.state.borrow_mut();
756        if (state.clear_depth - depth).abs() > f32::EPSILON {
757            state.clear_depth = depth;
758
759            unsafe {
760                self.gl.clear_depth_f32(depth);
761            }
762        }
763    }
764
765    pub(crate) fn set_clear_stencil(&self, stencil: i32) {
766        let mut state = self.state.borrow_mut();
767        if state.clear_stencil != stencil {
768            state.clear_stencil = stencil;
769
770            unsafe {
771                self.gl.clear_stencil(stencil);
772            }
773        }
774    }
775
776    pub(crate) fn set_blend_func(&self, func: BlendFunc) {
777        let mut state = self.state.borrow_mut();
778        if state.blend_func != func {
779            state.blend_func = func;
780
781            unsafe {
782                self.gl.blend_func_separate(
783                    state.blend_func.sfactor.into_gl(),
784                    state.blend_func.dfactor.into_gl(),
785                    state.blend_func.alpha_sfactor.into_gl(),
786                    state.blend_func.alpha_dfactor.into_gl(),
787                );
788            }
789        }
790    }
791
792    pub(crate) fn set_blend_equation(&self, equation: BlendEquation) {
793        let mut state = self.state.borrow_mut();
794        if state.blend_equation != equation {
795            state.blend_equation = equation;
796
797            unsafe {
798                self.gl.blend_equation_separate(
799                    state.blend_equation.rgb.into_gl(),
800                    state.blend_equation.alpha.into_gl(),
801                );
802            }
803        }
804    }
805
806    pub(crate) fn set_depth_func(&self, depth_func: CompareFunc) {
807        let mut state = self.state.borrow_mut();
808        if state.depth_func != depth_func {
809            state.depth_func = depth_func;
810
811            unsafe {
812                self.gl.depth_func(depth_func.into_gl());
813            }
814        }
815    }
816
817    pub(crate) fn set_program(&self, program: Option<glow::Program>) {
818        let mut state = self.state.borrow_mut();
819        if state.program != program {
820            state.program = program;
821
822            state.frame_statistics.program_binding_changes += 1;
823
824            unsafe {
825                self.gl.use_program(state.program);
826            }
827        }
828    }
829
830    pub(crate) fn set_texture(&self, unit_index: u32, target: u32, texture: Option<glow::Texture>) {
831        unsafe fn bind_texture(
832            gl: &glow::Context,
833            target: u32,
834            texture: Option<glow::Texture>,
835            unit_index: u32,
836            active_unit: &mut u32,
837        ) {
838            if *active_unit != unit_index {
839                *active_unit = unit_index;
840                gl.active_texture(glow::TEXTURE0 + unit_index);
841            }
842            gl.bind_texture(target, texture);
843        }
844
845        unsafe {
846            let mut state_guard = self.state.borrow_mut();
847            let state = state_guard.deref_mut();
848
849            let unit = &mut state.texture_units_storage.units[unit_index as usize];
850            let active_unit = &mut state.texture_units_storage.active_unit;
851            for binding in unit.bindings.iter_mut() {
852                if binding.target == target {
853                    if binding.texture != texture {
854                        binding.texture = texture;
855                        bind_texture(&self.gl, binding.target, texture, unit_index, active_unit);
856                        state.frame_statistics.texture_binding_changes += 1;
857                    }
858                } else if binding.texture.is_some() {
859                    binding.texture = None;
860                    bind_texture(&self.gl, binding.target, None, unit_index, active_unit);
861                    state.frame_statistics.texture_binding_changes += 1;
862                }
863            }
864        }
865    }
866
867    pub(crate) fn set_stencil_func(&self, func: StencilFunc) {
868        let mut state = self.state.borrow_mut();
869        if state.stencil_func != func {
870            state.stencil_func = func;
871
872            unsafe {
873                self.gl.stencil_func(
874                    state.stencil_func.func.into_gl(),
875                    state.stencil_func.ref_value as i32,
876                    state.stencil_func.mask,
877                );
878            }
879        }
880    }
881
882    pub(crate) fn set_stencil_op(&self, op: StencilOp) {
883        let mut state = self.state.borrow_mut();
884        if state.stencil_op != op {
885            state.stencil_op = op;
886
887            unsafe {
888                self.gl.stencil_op(
889                    state.stencil_op.fail.into_gl(),
890                    state.stencil_op.zfail.into_gl(),
891                    state.stencil_op.zpass.into_gl(),
892                );
893
894                self.gl.stencil_mask(state.stencil_op.write_mask);
895            }
896        }
897    }
898
899    pub(crate) fn set_vertex_array_object(&self, vao: Option<glow::VertexArray>) {
900        let mut state = self.state.borrow_mut();
901        if state.vao != vao {
902            state.vao = vao;
903
904            state.frame_statistics.vao_binding_changes += 1;
905
906            unsafe {
907                self.gl.bind_vertex_array(state.vao);
908            }
909        }
910    }
911
912    pub(crate) fn set_scissor_test(&self, scissor_test: bool) {
913        let mut state = self.state.borrow_mut();
914        if state.scissor_test != scissor_test {
915            state.scissor_test = scissor_test;
916
917            unsafe {
918                if scissor_test {
919                    self.gl.enable(glow::SCISSOR_TEST);
920                } else {
921                    self.gl.disable(glow::SCISSOR_TEST);
922                }
923            }
924        }
925    }
926
927    pub(crate) fn set_scissor_box(&self, scissor_box: &ScissorBox) {
928        unsafe {
929            self.gl.scissor(
930                scissor_box.x,
931                scissor_box.y,
932                scissor_box.width,
933                scissor_box.height,
934            );
935        }
936    }
937
938    pub(crate) fn apply_draw_parameters(&self, draw_params: &DrawParameters) {
939        let DrawParameters {
940            cull_face,
941            color_write,
942            depth_write,
943            stencil_test,
944            depth_test,
945            blend,
946            stencil_op,
947            scissor_box,
948        } = draw_params;
949
950        if let Some(ref blend_params) = blend {
951            self.set_blend_func(blend_params.func);
952            self.set_blend_equation(blend_params.equation);
953            self.set_blend(true);
954        } else {
955            self.set_blend(false);
956        }
957
958        if let Some(depth_func) = depth_test {
959            self.set_depth_func(*depth_func);
960            self.set_depth_test(true);
961        } else {
962            self.set_depth_test(false);
963        }
964        self.set_depth_write(*depth_write);
965
966        self.set_color_write(*color_write);
967
968        if let Some(stencil_func) = stencil_test {
969            self.set_stencil_test(true);
970            self.set_stencil_func(*stencil_func);
971        } else {
972            self.set_stencil_test(false);
973        }
974
975        self.set_stencil_op(*stencil_op);
976
977        if let Some(cull_face) = cull_face {
978            self.set_cull_face(*cull_face);
979            self.set_culling(true);
980        } else {
981            self.set_culling(false);
982        }
983
984        if let Some(scissor_box) = scissor_box {
985            self.set_scissor_test(true);
986            self.set_scissor_box(scissor_box);
987        } else {
988            self.set_scissor_test(false);
989        }
990    }
991}
992
993impl GraphicsServer for GlGraphicsServer {
994    fn create_buffer(
995        &self,
996        size: usize,
997        buffer_kind: BufferKind,
998        buffer_usage: BufferUsage,
999    ) -> Result<GpuBuffer, FrameworkError> {
1000        Ok(GpuBuffer(Rc::new(gl::buffer::GlBuffer::new(
1001            self,
1002            size,
1003            buffer_kind,
1004            buffer_usage,
1005        )?)))
1006    }
1007
1008    fn create_texture(&self, desc: GpuTextureDescriptor) -> Result<GpuTexture, FrameworkError> {
1009        Ok(GpuTexture(Rc::new(GlTexture::new(self, desc)?)))
1010    }
1011
1012    fn create_frame_buffer(
1013        &self,
1014        depth_attachment: Option<Attachment>,
1015        color_attachments: Vec<Attachment>,
1016    ) -> Result<GpuFrameBuffer, FrameworkError> {
1017        Ok(GpuFrameBuffer(Rc::new(GlFrameBuffer::new(
1018            self,
1019            depth_attachment,
1020            color_attachments,
1021        )?)))
1022    }
1023
1024    fn back_buffer(&self) -> GpuFrameBuffer {
1025        GpuFrameBuffer(Rc::new(GlFrameBuffer::backbuffer(self)))
1026    }
1027
1028    fn create_query(&self) -> Result<GpuQuery, FrameworkError> {
1029        Ok(GpuQuery(Rc::new(GlQuery::new(self)?)))
1030    }
1031
1032    fn create_program(
1033        &self,
1034        name: &str,
1035        vertex_source: &str,
1036        fragment_source: &str,
1037        resources: &[ShaderResourceDefinition],
1038    ) -> Result<GpuProgram, FrameworkError> {
1039        Ok(GpuProgram(Rc::new(GlProgram::from_source_and_resources(
1040            self,
1041            name,
1042            vertex_source,
1043            fragment_source,
1044            resources,
1045        )?)))
1046    }
1047
1048    fn create_async_read_buffer(
1049        &self,
1050        pixel_size: usize,
1051        pixel_count: usize,
1052    ) -> Result<GpuAsyncReadBuffer, FrameworkError> {
1053        Ok(GpuAsyncReadBuffer(Rc::new(GlAsyncReadBuffer::new(
1054            self,
1055            pixel_size,
1056            pixel_count,
1057        )?)))
1058    }
1059
1060    fn create_geometry_buffer(
1061        &self,
1062        desc: GeometryBufferDescriptor,
1063    ) -> Result<GpuGeometryBuffer, FrameworkError> {
1064        Ok(GpuGeometryBuffer(Rc::new(GlGeometryBuffer::new(
1065            self, desc,
1066        )?)))
1067    }
1068
1069    fn weak(self: Rc<Self>) -> Weak<dyn GraphicsServer> {
1070        self.this.borrow().as_ref().unwrap().clone()
1071    }
1072
1073    fn flush(&self) {
1074        unsafe {
1075            self.gl.flush();
1076        }
1077    }
1078
1079    fn finish(&self) {
1080        unsafe {
1081            self.gl.finish();
1082        }
1083    }
1084
1085    fn invalidate_resource_bindings_cache(&self) {
1086        let mut state = self.state.borrow_mut();
1087
1088        unsafe {
1089            for (unit_index, unit) in state.texture_units_storage.units.iter().enumerate() {
1090                self.gl.active_texture(glow::TEXTURE0 + unit_index as u32);
1091                for binding in unit.bindings.iter() {
1092                    self.gl.bind_texture(binding.target, None)
1093                }
1094            }
1095            self.gl.active_texture(glow::TEXTURE0);
1096        }
1097        state.texture_units_storage = Default::default();
1098
1099        state.program = Default::default();
1100        state.frame_statistics = Default::default();
1101    }
1102
1103    fn pipeline_statistics(&self) -> PipelineStatistics {
1104        self.state.borrow().frame_statistics
1105    }
1106
1107    fn swap_buffers(&self) -> Result<(), FrameworkError> {
1108        #[cfg(not(target_arch = "wasm32"))]
1109        {
1110            let state = self.state.borrow();
1111            Ok(state.gl_surface.swap_buffers(&state.gl_context)?)
1112        }
1113
1114        #[cfg(target_arch = "wasm32")]
1115        {
1116            Ok(())
1117        }
1118    }
1119
1120    fn set_frame_size(&self, #[allow(unused_variables)] new_size: (u32, u32)) {
1121        #[cfg(not(target_arch = "wasm32"))]
1122        {
1123            use std::num::NonZeroU32;
1124            let state = self.state.borrow();
1125            state.gl_surface.resize(
1126                &state.gl_context,
1127                NonZeroU32::new(new_size.0).unwrap_or_else(|| NonZeroU32::new(1).unwrap()),
1128                NonZeroU32::new(new_size.1).unwrap_or_else(|| NonZeroU32::new(1).unwrap()),
1129            );
1130        }
1131    }
1132
1133    fn capabilities(&self) -> ServerCapabilities {
1134        let gl = &self.gl;
1135        unsafe {
1136            ServerCapabilities {
1137                max_uniform_block_size: gl.get_parameter_i32(glow::MAX_UNIFORM_BLOCK_SIZE) as usize,
1138                uniform_buffer_offset_alignment: gl
1139                    .get_parameter_i32(glow::UNIFORM_BUFFER_OFFSET_ALIGNMENT)
1140                    as usize,
1141                max_lod_bias: gl.get_parameter_f32(glow::MAX_TEXTURE_LOD_BIAS),
1142            }
1143        }
1144    }
1145
1146    fn set_polygon_fill_mode(&self, polygon_face: PolygonFace, polygon_fill_mode: PolygonFillMode) {
1147        let mut state = self.state.borrow_mut();
1148        if state.polygon_fill_mode != polygon_fill_mode || state.polygon_face != polygon_face {
1149            state.polygon_fill_mode = polygon_fill_mode;
1150            state.polygon_face = polygon_face;
1151
1152            unsafe {
1153                self.gl.polygon_mode(
1154                    state.polygon_face.into_gl(),
1155                    state.polygon_fill_mode.into_gl(),
1156                )
1157            }
1158        }
1159    }
1160}