1use 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 return;
960 }
961
962 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 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 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 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}