Skip to main content

speedy2d/
glwrapper.rs

1/*
2 *  Copyright 2021 QuantumBadger
3 *
4 *  Licensed under the Apache License, Version 2.0 (the "License");
5 *  you may not use this file except in compliance with the License.
6 *  You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 *  Unless required by applicable law or agreed to in writing, software
11 *  distributed under the License is distributed on an "AS IS" BASIS,
12 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 *  See the License for the specific language governing permissions and
14 *  limitations under the License.
15 */
16
17use std::cell::RefCell;
18use std::collections::HashMap;
19use std::convert::TryInto;
20use std::fmt::{Debug, Formatter};
21use std::hash::{Hash, Hasher};
22use std::num::TryFromIntError;
23use std::ptr;
24use std::rc::{Rc, Weak};
25
26use crate::color::Color;
27use crate::dimen::UVec2;
28use crate::error::{BacktraceError, Context, ErrorMessage};
29use crate::glbackend::constants::*;
30use crate::glbackend::types::{
31    GLTypeBuffer,
32    GLTypeProgram,
33    GLTypeShader,
34    GLTypeTexture,
35    GLTypeUniformLocation,
36    GLenum,
37    GLint,
38    GLuint
39};
40use crate::glbackend::GLBackend;
41use crate::{ImageDataType, RawBitmapData};
42
43#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
44#[allow(dead_code)]
45pub enum GLVersion
46{
47    OpenGL2_0,
48    WebGL2_0
49}
50
51impl From<TryFromIntError> for BacktraceError<ErrorMessage>
52{
53    fn from(_: TryFromIntError) -> Self
54    {
55        ErrorMessage::msg("Integer conversion failed/out of bounds")
56    }
57}
58
59fn gl_check_error_always(
60    context: &GLContextManager
61) -> Result<(), BacktraceError<ErrorMessage>>
62{
63    context.with_gl_backend(|backend| backend.gl_check_error_always())
64}
65
66fn gl_clear_and_log_old_error(context: &GLContextManager)
67{
68    context.with_gl_backend(|backend| backend.gl_clear_and_log_old_error())
69}
70
71trait GLHandleOwner<HandleType: GLHandleId>
72{
73    fn get_handle(&self) -> HandleType::HandleRawType;
74}
75
76#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
77enum GLHandleType
78{
79    Program,
80    Shader,
81    Buffer,
82    Texture
83}
84
85trait GLHandleId: Debug + Hash + PartialEq + Eq
86{
87    type HandleRawType;
88    fn delete(&self, context: &GLContextManager);
89}
90
91#[derive(Debug, Hash, PartialEq, Eq)]
92struct GLHandleTypeProgram
93{
94    handle: GLTypeProgram
95}
96
97#[derive(Debug, Hash, PartialEq, Eq)]
98struct GLHandleTypeShader
99{
100    handle: GLTypeShader
101}
102
103#[derive(Debug, Hash, PartialEq, Eq)]
104struct GLHandleTypeBuffer
105{
106    handle: GLTypeBuffer
107}
108
109#[derive(Debug, Hash, PartialEq, Eq)]
110struct GLHandleTypeTexture
111{
112    handle: GLTypeTexture
113}
114
115struct GLHandle<HandleType: GLHandleId>
116{
117    context: Weak<RefCell<GLContextManagerState>>,
118    handle: HandleType,
119    handle_type: GLHandleType
120}
121
122impl<HandleType: GLHandleId> Debug for GLHandle<HandleType>
123{
124    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result
125    {
126        f.debug_struct("GLHandle")
127            .field("handle", &self.handle)
128            .field("handle_type", &self.handle_type)
129            .finish()
130    }
131}
132
133impl<HandleType: GLHandleId> Hash for GLHandle<HandleType>
134{
135    fn hash<H: Hasher>(&self, state: &mut H)
136    {
137        self.handle.hash(state);
138        self.handle_type.hash(state);
139    }
140}
141
142impl<HandleType: GLHandleId> PartialEq for GLHandle<HandleType>
143{
144    fn eq(&self, other: &Self) -> bool
145    {
146        match self.context.upgrade() {
147            None => return false,
148            Some(self_context) => match other.context.upgrade() {
149                None => return false,
150                Some(other_context) => {
151                    if *RefCell::borrow(&self_context) != *RefCell::borrow(&other_context)
152                    {
153                        return false;
154                    }
155                }
156            }
157        }
158
159        self.handle == other.handle && self.handle_type == other.handle_type
160    }
161}
162
163impl<HandleType: GLHandleId> Eq for GLHandle<HandleType> {}
164
165impl<HandleType: GLHandleId> GLHandle<HandleType>
166{
167    fn wrap<F>(
168        context: &GLContextManager,
169        handle_type: GLHandleType,
170        handle_creator: F
171    ) -> Result<Self, BacktraceError<ErrorMessage>>
172    where
173        F: FnOnce() -> Result<HandleType, BacktraceError<ErrorMessage>>
174    {
175        match handle_type {
176            GLHandleType::Program => gl_clear_and_log_old_error(context),
177            GLHandleType::Shader => gl_clear_and_log_old_error(context),
178            GLHandleType::Buffer => {}
179            GLHandleType::Texture => {}
180        }
181
182        let handle = handle_creator().context("Handle creation failed")?;
183
184        match handle_type {
185            GLHandleType::Program => gl_check_error_always(context)?,
186            GLHandleType::Shader => gl_check_error_always(context)?,
187            GLHandleType::Buffer => {}
188            GLHandleType::Texture => {}
189        }
190
191        Ok(GLHandle {
192            context: Rc::downgrade(&context.state),
193            handle,
194            handle_type
195        })
196    }
197
198    #[inline]
199    #[must_use]
200    fn obtain_context_if_valid(&self) -> Option<GLContextManager>
201    {
202        obtain_context_from_weak_if_valid(&self.context)
203    }
204}
205
206impl<HandleType: GLHandleId> Drop for GLHandle<HandleType>
207{
208    fn drop(&mut self)
209    {
210        if let Some(context) = self.obtain_context_if_valid() {
211            self.handle.delete(&context);
212        }
213    }
214}
215
216impl GLHandleId for GLHandleTypeProgram
217{
218    type HandleRawType = GLTypeProgram;
219
220    fn delete(&self, context: &GLContextManager)
221    {
222        context
223            .with_gl_backend(|backend| unsafe { backend.gl_delete_program(self.handle) });
224    }
225}
226
227impl GLHandleId for GLHandleTypeShader
228{
229    type HandleRawType = GLTypeShader;
230
231    fn delete(&self, context: &GLContextManager)
232    {
233        context
234            .with_gl_backend(|backend| unsafe { backend.gl_delete_shader(self.handle) });
235    }
236}
237
238impl GLHandleId for GLHandleTypeBuffer
239{
240    type HandleRawType = GLTypeBuffer;
241
242    fn delete(&self, context: &GLContextManager)
243    {
244        context
245            .with_gl_backend(|backend| unsafe { backend.gl_delete_buffer(self.handle) });
246    }
247}
248
249impl GLHandleId for GLHandleTypeTexture
250{
251    type HandleRawType = GLTypeTexture;
252
253    fn delete(&self, context: &GLContextManager)
254    {
255        context
256            .with_gl_backend(|backend| unsafe { backend.gl_delete_texture(self.handle) });
257    }
258}
259
260#[derive(Debug)]
261pub struct GLProgram
262{
263    handle: GLHandle<GLHandleTypeProgram>,
264    attribute_handles: HashMap<&'static str, GLAttributeHandle>
265}
266
267impl Hash for GLProgram
268{
269    fn hash<H: Hasher>(&self, state: &mut H)
270    {
271        self.handle.hash(state);
272    }
273}
274
275impl PartialEq for GLProgram
276{
277    fn eq(&self, other: &Self) -> bool
278    {
279        ptr::eq(self, other)
280    }
281}
282
283impl Eq for GLProgram {}
284
285impl GLHandleOwner<GLHandleTypeProgram> for GLProgram
286{
287    fn get_handle(&self) -> <GLHandleTypeProgram as GLHandleId>::HandleRawType
288    {
289        self.handle.handle.handle
290    }
291}
292
293impl GLProgram
294{
295    fn new(context: &GLContextManager) -> Result<Self, BacktraceError<ErrorMessage>>
296    {
297        context.with_gl_backend(|backend| {
298            Ok(GLProgram {
299                handle: GLHandle::wrap(context, GLHandleType::Program, || unsafe {
300                    Ok(GLHandleTypeProgram {
301                        handle: backend.gl_create_program()?
302                    })
303                })?,
304                attribute_handles: HashMap::new()
305            })
306        })
307    }
308
309    fn attach_shader(
310        &mut self,
311        context: &GLContextManager,
312        shader: &GLShader
313    ) -> Result<(), BacktraceError<ErrorMessage>>
314    {
315        context.with_gl_backend(|backend| unsafe {
316            backend.gl_attach_shader(self.get_handle(), shader.get_handle());
317        });
318
319        gl_check_error_always(context)?;
320
321        Ok(())
322    }
323
324    fn link(
325        context: &GLContextManager,
326        vertex_shader: &GLShader,
327        fragment_shader: &GLShader,
328        attribute_names: impl IntoIterator<Item = &'static &'static str>
329    ) -> Result<Self, BacktraceError<ErrorMessage>>
330    {
331        gl_clear_and_log_old_error(context);
332
333        let mut program = GLProgram::new(context)?;
334
335        program.attach_shader(context, vertex_shader)?;
336        program.attach_shader(context, fragment_shader)?;
337
338        context.with_gl_backend(|backend| unsafe {
339            backend.gl_link_program(program.get_handle());
340        });
341
342        gl_check_error_always(context)?;
343
344        context.with_gl_backend(|backend| unsafe {
345            if backend.gl_get_program_link_status(program.get_handle()) {
346                Ok(())
347            } else {
348                let msg = backend.gl_get_program_info_log(program.get_handle())?;
349                Err(ErrorMessage::msg(format!(
350                    "Program linking failed: '{msg}'"
351                )))
352            }
353        })?;
354
355        gl_check_error_always(context)?;
356
357        for attribute_name in attribute_names.into_iter() {
358            program.attribute_handles.insert(
359                attribute_name.as_ref(),
360                program.get_attribute_handle(attribute_name.as_ref())?
361            );
362        }
363
364        Ok(program)
365    }
366
367    fn enable(&self, context: &GLContextManager)
368    {
369        context.with_gl_backend(|backend| {
370            unsafe {
371                backend.gl_use_program(self.get_handle());
372            }
373
374            for attribute in self.attribute_handles.values() {
375                unsafe {
376                    backend.gl_enable_vertex_attrib_array(attribute.handle);
377                }
378            }
379        });
380    }
381
382    fn disable(&self, context: &GLContextManager)
383    {
384        context.with_gl_backend(|backend| {
385            for attribute in self.attribute_handles.values() {
386                unsafe {
387                    backend.gl_disable_vertex_attrib_array(attribute.handle);
388                }
389            }
390        });
391    }
392
393    pub fn get_attribute_handle(
394        &self,
395        name: &str
396    ) -> Result<GLAttributeHandle, BacktraceError<ErrorMessage>>
397    {
398        let context = self
399            .handle
400            .obtain_context_if_valid()
401            .ok_or_else(|| ErrorMessage::msg("GL context no longer valid"))?;
402
403        let handle = context.with_gl_backend(|backend| unsafe {
404            backend.gl_get_attrib_location(self.get_handle(), name)
405        });
406
407        gl_check_error_always(&context)?;
408
409        match handle {
410            None => Err(ErrorMessage::msg(format!(
411                "Attribute handle {name} is invalid"
412            ))),
413            Some(handle) => Ok(GLAttributeHandle { handle })
414        }
415    }
416
417    pub fn get_uniform_handle(
418        &self,
419        context: &GLContextManager,
420        name: &str
421    ) -> Result<GLUniformHandle, BacktraceError<ErrorMessage>>
422    {
423        if !context.is_valid() {
424            return Err(ErrorMessage::msg("GL context no longer valid"));
425        }
426
427        let handle = context.with_gl_backend(|backend| unsafe {
428            backend.gl_get_uniform_location(self.get_handle(), name)
429        });
430
431        gl_check_error_always(context)?;
432
433        match handle {
434            None => Err(ErrorMessage::msg(format!(
435                "Uniform handle {name} is invalid"
436            ))),
437            Some(handle) => Ok(GLUniformHandle { handle })
438        }
439    }
440}
441
442#[derive(Debug, Hash, PartialEq, Eq, Clone)]
443pub enum GLShaderType
444{
445    Vertex,
446    Fragment
447}
448
449impl GLShaderType
450{
451    fn gl_constant(&self) -> GLenum
452    {
453        match self {
454            GLShaderType::Vertex => GL_VERTEX_SHADER,
455            GLShaderType::Fragment => GL_FRAGMENT_SHADER
456        }
457    }
458}
459
460pub struct GLShader
461{
462    handle: GLHandle<GLHandleTypeShader>
463}
464
465impl GLHandleOwner<GLHandleTypeShader> for GLShader
466{
467    fn get_handle(&self) -> <GLHandleTypeShader as GLHandleId>::HandleRawType
468    {
469        self.handle.handle.handle
470    }
471}
472
473impl GLShader
474{
475    fn new(
476        context: &GLContextManager,
477        shader_type: GLShaderType
478    ) -> Result<Self, BacktraceError<ErrorMessage>>
479    {
480        Ok(GLShader {
481            handle: GLHandle::wrap(context, GLHandleType::Shader, || {
482                context.with_gl_backend(|backend| unsafe {
483                    Ok(GLHandleTypeShader {
484                        handle: backend.gl_create_shader(shader_type.gl_constant())?
485                    })
486                })
487            })?
488        })
489    }
490
491    fn compile(
492        context: &GLContextManager,
493        shader_type: GLShaderType,
494        source: &str
495    ) -> Result<Self, BacktraceError<ErrorMessage>>
496    {
497        gl_clear_and_log_old_error(context);
498
499        let shader = GLShader::new(context, shader_type)?;
500
501        context.with_gl_backend(|backend| unsafe {
502            backend.gl_shader_source(shader.get_handle(), source);
503            backend.gl_check_error_always()?;
504
505            backend.gl_compile_shader(shader.get_handle());
506            backend.gl_check_error_always()?;
507
508            if backend.gl_get_shader_compile_status(shader.get_handle()) {
509                Ok(shader)
510            } else {
511                Err(ErrorMessage::msg(context.with_gl_backend(|backend| {
512                    backend.gl_get_shader_info_log(shader.get_handle())
513                })?))
514            }
515        })
516    }
517}
518
519#[derive(Debug)]
520pub struct GLAttributeHandle
521{
522    handle: GLuint
523}
524
525#[derive(Debug)]
526pub struct GLUniformHandle
527{
528    handle: GLTypeUniformLocation
529}
530
531impl GLUniformHandle
532{
533    pub fn set_value_float(&self, context: &GLContextManager, value: f32)
534    {
535        context.with_gl_backend(|backend| unsafe {
536            backend.gl_uniform_1f(&self.handle, value)
537        })
538    }
539
540    pub fn set_value_int(&self, context: &GLContextManager, value: i32)
541    {
542        context.with_gl_backend(|backend| unsafe {
543            backend.gl_uniform_1i(&self.handle, value)
544        })
545    }
546}
547
548pub enum GLBufferTarget
549{
550    Array,
551    #[allow(dead_code)]
552    ElementArray
553}
554
555impl GLBufferTarget
556{
557    fn gl_constant(&self) -> GLenum
558    {
559        match self {
560            GLBufferTarget::Array => GL_ARRAY_BUFFER,
561            GLBufferTarget::ElementArray => GL_ELEMENT_ARRAY_BUFFER
562        }
563    }
564}
565
566pub struct GLBuffer
567{
568    handle: GLHandle<GLHandleTypeBuffer>,
569    target: GLBufferTarget,
570    components_per_vertex: GLint,
571    attrib_index: GLAttributeHandle
572}
573
574impl GLHandleOwner<GLHandleTypeBuffer> for GLBuffer
575{
576    fn get_handle(&self) -> <GLHandleTypeBuffer as GLHandleId>::HandleRawType
577    {
578        self.handle.handle.handle
579    }
580}
581
582impl GLBuffer
583{
584    fn new(
585        context: &GLContextManager,
586        target: GLBufferTarget,
587        components_per_vertex: GLint,
588        attrib_index: GLAttributeHandle
589    ) -> Result<Self, BacktraceError<ErrorMessage>>
590    {
591        gl_clear_and_log_old_error(context);
592
593        let handle = GLHandle::wrap(context, GLHandleType::Buffer, || {
594            context.with_gl_backend(|backend| unsafe {
595                Ok(GLHandleTypeBuffer {
596                    handle: backend.gl_gen_buffer()?
597                })
598            })
599        })?;
600
601        Ok(GLBuffer {
602            handle,
603            target,
604            components_per_vertex,
605            attrib_index
606        })
607    }
608
609    pub fn set_data(&mut self, context: &GLContextManager, data: &[f32])
610    {
611        if !context.is_valid() {
612            log::warn!("Ignoring buffer set_data: invalid GL context");
613            return;
614        }
615
616        context.with_gl_backend(|backend| unsafe {
617            backend.gl_bind_buffer(self.target.gl_constant(), self.get_handle());
618
619            backend.gl_buffer_data_f32(self.target.gl_constant(), data, GL_DYNAMIC_DRAW);
620
621            backend.gl_vertex_attrib_pointer_f32(
622                self.attrib_index.handle,
623                self.components_per_vertex,
624                GL_FLOAT,
625                false,
626                0,
627                0
628            )
629        });
630    }
631}
632
633#[derive(Debug, Hash, PartialEq, Eq, Clone)]
634pub enum GLTextureSmoothing
635{
636    NearestNeighbour,
637    Linear
638}
639
640#[allow(clippy::upper_case_acronyms)]
641#[derive(Debug, Hash, PartialEq, Eq, Clone)]
642pub enum GLTextureImageFormatU8
643{
644    #[allow(dead_code)]
645    Red,
646    RGB,
647    RGBA
648}
649
650impl From<ImageDataType> for GLTextureImageFormatU8
651{
652    fn from(value: ImageDataType) -> Self
653    {
654        match value {
655            ImageDataType::RGB => Self::RGB,
656            ImageDataType::RGBA => Self::RGBA
657        }
658    }
659}
660
661impl GLTextureImageFormatU8
662{
663    fn get_internal_format(&self) -> GLenum
664    {
665        match self {
666            GLTextureImageFormatU8::Red => GL_R8,
667            GLTextureImageFormatU8::RGB => GL_RGB8,
668            GLTextureImageFormatU8::RGBA => GL_RGBA8
669        }
670    }
671
672    fn get_format(&self) -> GLenum
673    {
674        match self {
675            GLTextureImageFormatU8::Red => GL_RED,
676            GLTextureImageFormatU8::RGB => GL_RGB,
677            GLTextureImageFormatU8::RGBA => GL_RGBA
678        }
679    }
680
681    fn get_bytes_per_pixel(&self) -> usize
682    {
683        match self {
684            GLTextureImageFormatU8::Red => 1,
685            GLTextureImageFormatU8::RGB => 3,
686            GLTextureImageFormatU8::RGBA => 4
687        }
688    }
689}
690
691#[derive(Clone, Debug, Hash, PartialEq, Eq)]
692pub struct GLTexture
693{
694    handle: Rc<GLHandle<GLHandleTypeTexture>>
695}
696
697impl GLHandleOwner<GLHandleTypeTexture> for GLTexture
698{
699    fn get_handle(&self) -> <GLHandleTypeTexture as GLHandleId>::HandleRawType
700    {
701        self.handle.handle.handle
702    }
703}
704
705impl GLTexture
706{
707    fn new(context: &GLContextManager) -> Result<Self, BacktraceError<ErrorMessage>>
708    {
709        let handle = GLHandle::wrap(context, GLHandleType::Texture, || {
710            context.with_gl_backend(|backend| unsafe {
711                Ok(GLHandleTypeTexture {
712                    handle: backend.gl_gen_texture()?
713                })
714            })
715        })?;
716
717        Ok(GLTexture {
718            handle: Rc::new(handle)
719        })
720    }
721
722    pub fn set_image_data(
723        &self,
724        context: &GLContextManager,
725        format: GLTextureImageFormatU8,
726        smoothing: GLTextureSmoothing,
727        size: &UVec2,
728        data: &[u8]
729    ) -> Result<(), BacktraceError<ErrorMessage>>
730    {
731        if !context.is_valid() {
732            log::warn!("Ignoring texture set_image_data: invalid GL context");
733            return Ok(());
734        }
735
736        let smoothing_constant = match smoothing {
737            GLTextureSmoothing::NearestNeighbour => GL_NEAREST,
738            GLTextureSmoothing::Linear => GL_LINEAR
739        } as GLint;
740
741        context.bind_texture(self);
742
743        let width_stride_bytes = size.x as usize * format.get_bytes_per_pixel();
744
745        let unpack_alignment = if width_stride_bytes.is_multiple_of(8) {
746            8
747        } else if width_stride_bytes.is_multiple_of(4) {
748            4
749        } else if width_stride_bytes.is_multiple_of(2) {
750            2
751        } else {
752            1
753        };
754
755        context.with_gl_backend::<Result<(), BacktraceError<ErrorMessage>>, _>(
756            |backend| unsafe {
757                backend.gl_pixel_store_i(GL_UNPACK_ALIGNMENT, unpack_alignment);
758                backend.gl_tex_parameter_i(
759                    GL_TEXTURE_2D,
760                    GL_TEXTURE_WRAP_S,
761                    GL_CLAMP_TO_EDGE as GLint
762                );
763                backend.gl_tex_parameter_i(
764                    GL_TEXTURE_2D,
765                    GL_TEXTURE_WRAP_T,
766                    GL_CLAMP_TO_EDGE as GLint
767                );
768                backend.gl_tex_parameter_i(
769                    GL_TEXTURE_2D,
770                    GL_TEXTURE_MIN_FILTER,
771                    smoothing_constant
772                );
773                backend.gl_tex_parameter_i(
774                    GL_TEXTURE_2D,
775                    GL_TEXTURE_MAG_FILTER,
776                    smoothing_constant
777                );
778
779                backend.gl_tex_image_2d(
780                    GL_TEXTURE_2D,
781                    0,
782                    format
783                        .get_internal_format()
784                        .try_into()
785                        .context("Failed to cast internal format")?,
786                    size.x.try_into()?,
787                    size.y.try_into()?,
788                    0,
789                    format.get_format(),
790                    GL_UNSIGNED_BYTE,
791                    Some(data)
792                );
793
794                Ok(())
795            }
796        )
797    }
798}
799
800#[must_use]
801fn obtain_context_if_valid(
802    state: &RefCell<GLContextManagerState>
803) -> Option<GLContextManager>
804{
805    let state = state.borrow_mut();
806
807    if state.is_valid {
808        Some(GLContextManager {
809            state: state.weak_ref_to_self.upgrade().unwrap()
810        })
811    } else {
812        None
813    }
814}
815
816#[inline]
817#[must_use]
818fn obtain_context_from_weak_if_valid(
819    state: &Weak<RefCell<GLContextManagerState>>
820) -> Option<GLContextManager>
821{
822    match state.upgrade() {
823        None => None,
824        Some(state) => obtain_context_if_valid(&state)
825    }
826}
827
828struct GLContextManagerState
829{
830    is_valid: bool,
831    active_texture: Option<GLTexture>,
832    active_program: Option<Rc<GLProgram>>,
833    active_blend_mode: Option<GLBlendEnabled>,
834    viewport_size: Option<UVec2>,
835    scissor_enabled: bool,
836    gl_backend: Rc<dyn GLBackend + 'static>,
837    gl_version: GLVersion,
838    weak_ref_to_self: Weak<RefCell<GLContextManagerState>>
839}
840
841impl PartialEq for GLContextManagerState
842{
843    fn eq(&self, other: &Self) -> bool
844    {
845        ptr::eq(self, other)
846    }
847}
848
849#[derive(Clone)]
850pub struct GLContextManager
851{
852    state: Rc<RefCell<GLContextManagerState>>
853}
854
855impl GLContextManager
856{
857    pub fn create(
858        gl_backend: Rc<dyn GLBackend>,
859        gl_version: GLVersion
860    ) -> Result<Self, BacktraceError<ErrorMessage>>
861    {
862        let manager = GLContextManager {
863            state: Rc::new(RefCell::new(GLContextManagerState {
864                is_valid: true,
865                active_texture: None,
866                active_program: None,
867                active_blend_mode: None,
868                viewport_size: None,
869                scissor_enabled: false,
870                gl_backend,
871                gl_version,
872                weak_ref_to_self: Weak::new()
873            }))
874        };
875
876        RefCell::borrow_mut(&manager.state).weak_ref_to_self =
877            Rc::downgrade(&manager.state);
878
879        log::info!("GL context manager created");
880
881        Ok(manager)
882    }
883
884    pub fn mark_invalid(&self)
885    {
886        log::info!("GL context manager is now inactive");
887        RefCell::borrow_mut(&self.state).is_valid = false;
888    }
889
890    pub fn new_buffer(
891        &self,
892        target: GLBufferTarget,
893        components_per_vertex: GLint,
894        attrib_index: GLAttributeHandle
895    ) -> Result<GLBuffer, BacktraceError<ErrorMessage>>
896    {
897        self.ensure_valid()?;
898        GLBuffer::new(self, target, components_per_vertex, attrib_index)
899    }
900
901    pub fn new_shader(
902        &self,
903        shader_type: GLShaderType,
904        source: &str
905    ) -> Result<GLShader, BacktraceError<ErrorMessage>>
906    {
907        self.ensure_valid()?;
908        GLShader::compile(self, shader_type, source)
909    }
910
911    pub fn new_program(
912        &self,
913        vertex_shader: &GLShader,
914        fragment_shader: &GLShader,
915        attribute_names: impl IntoIterator<Item = &'static &'static str>
916    ) -> Result<Rc<GLProgram>, BacktraceError<ErrorMessage>>
917    {
918        self.ensure_valid()?;
919
920        Ok(Rc::new(GLProgram::link(
921            self,
922            vertex_shader,
923            fragment_shader,
924            attribute_names
925        )?))
926    }
927
928    pub fn new_texture(&self) -> Result<GLTexture, BacktraceError<ErrorMessage>>
929    {
930        self.ensure_valid()?;
931        GLTexture::new(self)
932    }
933
934    pub fn set_viewport_size(&self, size: UVec2)
935    {
936        if !self.is_valid() {
937            log::warn!("Ignoring set_viewport_size: invalid GL context");
938            return;
939        }
940
941        log::info!("Setting viewport size to {}x{}", size.x, size.y);
942
943        self.state.borrow_mut().viewport_size = Some(size);
944
945        self.with_gl_backend(|backend| unsafe {
946            backend.gl_viewport(0, 0, size.x as i32, size.y as i32);
947        });
948    }
949
950    pub fn bind_texture(&self, texture: &GLTexture)
951    {
952        if !self.is_valid() {
953            log::warn!("Ignoring bind_texture: invalid GL context");
954            return;
955        }
956
957        if RefCell::borrow(&self.state).active_texture.as_ref() == Some(texture) {
958            // Already bound
959            return;
960        }
961
962        // Drop separately to avoid a duplicate borrow of `state`.
963        let old_active_texture = RefCell::borrow_mut(&self.state).active_texture.take();
964        drop(old_active_texture);
965
966        RefCell::borrow_mut(&self.state).active_texture = Some(texture.clone());
967
968        self.with_gl_backend(|backend| unsafe {
969            backend.gl_active_texture(GL_TEXTURE0);
970            backend.gl_bind_texture(GL_TEXTURE_2D, Some(texture.get_handle()));
971        });
972    }
973
974    pub fn unbind_texture(&self)
975    {
976        #[cfg(not(target_arch = "wasm32"))]
977        {
978            if !self.is_valid() {
979                log::warn!("Ignoring unbind_texture: invalid GL context");
980                return;
981            }
982
983            if RefCell::borrow(&self.state)
984                .active_texture
985                .as_ref()
986                .is_none()
987            {
988                // Already unbound
989                return;
990            }
991
992            self.with_gl_backend(|backend| unsafe {
993                backend.gl_active_texture(GL_TEXTURE0);
994                backend.gl_bind_texture(GL_TEXTURE_2D, None);
995            });
996
997            // Drop separately to avoid a duplicate borrow of `state`.
998            let old_texture = RefCell::borrow_mut(&self.state).active_texture.take();
999            drop(old_texture);
1000        }
1001    }
1002
1003    pub fn use_program(&self, program: &Rc<GLProgram>)
1004    {
1005        if !self.is_valid() {
1006            log::warn!("Ignoring use_program: invalid GL context");
1007            return;
1008        }
1009
1010        if RefCell::borrow(&self.state).active_program.as_ref() == Some(program) {
1011            // Already bound
1012            return;
1013        }
1014
1015        if let Some(existing_program) = &RefCell::borrow_mut(&self.state).active_program {
1016            existing_program.disable(self);
1017        }
1018
1019        RefCell::borrow_mut(&self.state).active_program = Some(program.clone());
1020        program.enable(self);
1021    }
1022
1023    fn set_blend_mode(&self, blend_mode: GLBlendEnabled)
1024    {
1025        if RefCell::borrow(&self.state).active_blend_mode == Some(blend_mode.clone()) {
1026            return;
1027        }
1028
1029        RefCell::borrow_mut(&self.state).active_blend_mode = Some(blend_mode.clone());
1030
1031        match blend_mode {
1032            GLBlendEnabled::Enabled(mode) => match mode {
1033                GLBlendMode::OneMinusSrcAlpha => self.with_gl_backend(|backend| unsafe {
1034                    backend.gl_enable(GL_BLEND);
1035                    backend.gl_blend_func_separate(
1036                        GL_SRC_ALPHA,
1037                        GL_ONE_MINUS_SRC_ALPHA,
1038                        GL_ONE,
1039                        GL_ONE_MINUS_SRC_ALPHA
1040                    );
1041                })
1042            },
1043
1044            GLBlendEnabled::Disabled => self.with_gl_backend(|backend| unsafe {
1045                backend.gl_disable(GL_BLEND);
1046            })
1047        }
1048    }
1049
1050    pub fn set_enable_scissor(&self, enabled: bool)
1051    {
1052        if enabled != self.state.borrow().scissor_enabled {
1053            self.with_gl_backend(|backend| unsafe {
1054                match enabled {
1055                    true => backend.gl_enable(GL_SCISSOR_TEST),
1056                    false => backend.gl_disable(GL_SCISSOR_TEST)
1057                }
1058            });
1059            self.state.borrow_mut().scissor_enabled = enabled;
1060        }
1061    }
1062
1063    pub fn set_clip(&self, x: i32, y: i32, width: i32, height: i32)
1064    {
1065        let vp_height = match self.state.borrow().viewport_size {
1066            None => panic!("Call to set_clip before viewport size set"),
1067            Some(viewport_size) => viewport_size.y as i32
1068        };
1069        self.with_gl_backend(|backend| unsafe {
1070            backend.gl_scissor(x, vp_height - y - height, width, height);
1071        });
1072    }
1073
1074    pub fn draw_triangles(&self, blend_mode: GLBlendEnabled, vertex_count: usize)
1075    {
1076        if !self.is_valid() {
1077            log::warn!("Ignoring draw_triangles: invalid GL context");
1078            return;
1079        }
1080
1081        self.set_blend_mode(blend_mode);
1082
1083        self.with_gl_backend(|backend| unsafe {
1084            backend.gl_draw_arrays(GL_TRIANGLES, 0, vertex_count.try_into().unwrap());
1085        });
1086    }
1087
1088    pub fn clear_screen(&self, color: Color)
1089    {
1090        if !self.is_valid() {
1091            log::warn!("Ignoring clear_screen: invalid GL context");
1092            return;
1093        }
1094
1095        self.with_gl_backend(|backend| unsafe {
1096            backend.gl_clear_color(color.r(), color.g(), color.b(), color.a());
1097            backend.gl_clear(GL_COLOR_BUFFER_BIT);
1098        });
1099    }
1100
1101    fn with_gl_backend<Return, F>(&self, callback: F) -> Return
1102    where
1103        F: FnOnce(&Rc<dyn GLBackend>) -> Return
1104    {
1105        let backend = RefCell::borrow(&self.state).gl_backend.clone();
1106        callback(&backend)
1107    }
1108
1109    fn is_valid(&self) -> bool
1110    {
1111        RefCell::borrow(&self.state).is_valid
1112    }
1113
1114    fn ensure_valid(&self) -> Result<(), BacktraceError<ErrorMessage>>
1115    {
1116        if !self.is_valid() {
1117            Err(ErrorMessage::msg("GL context no longer valid"))
1118        } else {
1119            Ok(())
1120        }
1121    }
1122
1123    pub fn version(&self) -> GLVersion
1124    {
1125        self.state.borrow().gl_version
1126    }
1127
1128    pub fn capture(&mut self, format: ImageDataType) -> RawBitmapData
1129    {
1130        let viewport_size = match self.state.borrow().viewport_size {
1131            None => return RawBitmapData::new(vec![], (0, 0), format),
1132            Some(value) => value
1133        };
1134
1135        let width: usize = viewport_size.x.try_into().unwrap();
1136        let height: usize = viewport_size.y.try_into().unwrap();
1137
1138        let gl_format = GLTextureImageFormatU8::from(format);
1139
1140        let bpp = gl_format.get_bytes_per_pixel();
1141        let gl_format = gl_format.get_format();
1142
1143        let bytes = width * height * bpp;
1144
1145        let mut buf: Vec<u8> = Vec::with_capacity(bytes);
1146
1147        self.with_gl_backend(|backend| unsafe {
1148            backend.gl_read_pixels(
1149                0,
1150                0,
1151                width.try_into().unwrap(),
1152                height.try_into().unwrap(),
1153                gl_format,
1154                GL_UNSIGNED_BYTE,
1155                buf.spare_capacity_mut()
1156            );
1157        });
1158
1159        unsafe {
1160            buf.set_len(bytes);
1161        }
1162
1163        let row_bytes = width * bpp;
1164
1165        let buf_ptr = buf.as_mut_ptr();
1166
1167        for row in 0..(height / 2) {
1168            let bottom_row = height - row - 1;
1169
1170            let top_start = row * row_bytes;
1171            let bottom_start = bottom_row * row_bytes;
1172
1173            unsafe {
1174                ptr::swap_nonoverlapping(
1175                    buf_ptr.add(top_start),
1176                    buf_ptr.add(bottom_start),
1177                    row_bytes
1178                );
1179            }
1180        }
1181
1182        RawBitmapData::new(buf, viewport_size, format)
1183    }
1184}
1185
1186#[derive(Debug, Hash, PartialEq, Eq, Clone)]
1187pub enum GLBlendMode
1188{
1189    OneMinusSrcAlpha
1190}
1191
1192#[derive(Debug, Hash, PartialEq, Eq, Clone)]
1193pub enum GLBlendEnabled
1194{
1195    Enabled(GLBlendMode),
1196    #[allow(dead_code)]
1197    Disabled
1198}