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