1#![allow(unused_variables)]
5use alloc::{
6 boxed::Box,
7 rc::Rc,
8 string::{String, ToString},
9 vec::Vec,
10};
11use core::{
12 ffi, fmt,
13 hash::{Hash, Hasher},
14 mem::ManuallyDrop,
15 sync::atomic::{AtomicUsize, Ordering as AtomicOrdering},
16};
17
18use azul_css::{
19 props::{
20 basic::{ColorF, ColorU},
21 style::StyleTransformVec,
22 },
23 AzString, OptionI32, OptionU32, OptionUsize, StringVec, U8Vec,
24};
25pub use gl_context_loader::{
26 ctypes::*, gl, GLeglImageOES, GLsync, GLvoid, GenericGlContext, GlType as GlContextGlType,
27};
28
29pub use crate::glconst::*;
30use crate::{
31 geom::PhysicalSizeU32,
32 hit_test::DocumentId,
33 resources::{
34 Brush, Epoch, ExternalImageId, ImageDescriptor, ImageDescriptorFlags, RawImage,
35 RawImageData, RawImageFormat,
36 },
37 svg::{TessellatedGPUSvgNode, TessellatedSvgNode},
38 window::RendererType,
39 OrderedMap,
40};
41
42pub type GLuint = u32;
43pub type GLint = i32;
44pub type GLint64 = i64;
45pub type GLuint64 = u64;
46pub type GLenum = u32;
47pub type GLintptr = isize;
48pub type GLboolean = u8;
49pub type GLsizeiptr = isize;
50pub type GLbitfield = u32;
51pub type GLsizei = i32;
52pub type GLclampf = f32;
53pub type GLfloat = f32;
54
55pub const GL_RESTART_INDEX: u32 = core::u32::MAX;
56
57#[repr(C)]
60#[derive(Debug)]
61pub struct GlVoidPtrConst {
62 pub ptr: *const GLvoid,
63 pub run_destructor: bool,
64}
65
66impl Clone for GlVoidPtrConst {
67 fn clone(&self) -> Self {
68 Self {
69 ptr: self.ptr,
70 run_destructor: true,
71 }
72 }
73}
74
75impl Drop for GlVoidPtrConst {
76 fn drop(&mut self) {
77 self.run_destructor = false;
78 }
79}
80
81#[repr(C)]
86#[derive(Debug)]
87pub struct GlVoidPtrMut {
88 pub ptr: *mut GLvoid,
89}
90
91impl Clone for GlVoidPtrMut {
92 fn clone(&self) -> Self {
93 Self { ptr: self.ptr }
94 }
95}
96
97#[repr(C)]
99pub struct Refstr {
100 pub ptr: *const u8,
101 pub len: usize,
102}
103
104impl Clone for Refstr {
105 fn clone(&self) -> Self {
106 Self {
107 ptr: self.ptr,
108 len: self.len,
109 }
110 }
111}
112
113impl fmt::Debug for Refstr {
114 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115 self.as_str().fmt(f)
116 }
117}
118
119impl Refstr {
120 #[must_use] pub const fn as_str(&self) -> &str {
121 if self.ptr.is_null() || self.len == 0 {
125 return "";
126 }
127 unsafe { core::str::from_utf8_unchecked(core::slice::from_raw_parts(self.ptr, self.len)) }
128 }
129}
130
131impl From<&str> for Refstr {
132 fn from(s: &str) -> Self {
133 Self {
134 ptr: s.as_ptr(),
135 len: s.len(),
136 }
137 }
138}
139
140#[repr(C)]
142pub struct RefstrVecRef {
143 pub ptr: *const Refstr,
144 pub len: usize,
145}
146
147impl Clone for RefstrVecRef {
148 fn clone(&self) -> Self {
149 Self {
150 ptr: self.ptr,
151 len: self.len,
152 }
153 }
154}
155
156impl fmt::Debug for RefstrVecRef {
157 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
158 self.as_slice().fmt(f)
159 }
160}
161
162impl RefstrVecRef {
163 #[must_use] pub const fn as_slice(&self) -> &[Refstr] {
164 if self.ptr.is_null() || self.len == 0 {
166 return &[];
167 }
168 unsafe { core::slice::from_raw_parts(self.ptr, self.len) }
169 }
170}
171
172impl From<&[Refstr]> for RefstrVecRef {
173 fn from(s: &[Refstr]) -> Self {
174 Self {
175 ptr: s.as_ptr(),
176 len: s.len(),
177 }
178 }
179}
180
181#[repr(C)]
183pub struct GLint64VecRefMut {
184 pub ptr: *mut i64,
185 pub len: usize,
186}
187
188impl Clone for GLint64VecRefMut {
189 fn clone(&self) -> Self {
190 Self {
191 ptr: self.ptr,
192 len: self.len,
193 }
194 }
195}
196
197impl fmt::Debug for GLint64VecRefMut {
198 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
199 self.as_slice().fmt(f)
200 }
201}
202
203impl From<&mut [GLint64]> for GLint64VecRefMut {
204 fn from(s: &mut [GLint64]) -> Self {
205 Self {
206 ptr: s.as_mut_ptr(),
207 len: s.len(),
208 }
209 }
210}
211
212impl GLint64VecRefMut {
213 #[must_use] pub const fn as_slice(&self) -> &[GLint64] {
214 if self.ptr.is_null() || self.len == 0 {
216 return &[];
217 }
218 unsafe { core::slice::from_raw_parts(self.ptr, self.len) }
219 }
220 const fn as_mut_slice(&mut self) -> &mut [GLint64] {
221 if self.ptr.is_null() || self.len == 0 {
223 return &mut [];
224 }
225 unsafe { core::slice::from_raw_parts_mut(self.ptr, self.len) }
226 }
227}
228
229#[repr(C)]
231pub struct GLfloatVecRefMut {
232 pub ptr: *mut f32,
233 pub len: usize,
234}
235
236impl Clone for GLfloatVecRefMut {
237 fn clone(&self) -> Self {
238 Self {
239 ptr: self.ptr,
240 len: self.len,
241 }
242 }
243}
244
245impl fmt::Debug for GLfloatVecRefMut {
246 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
247 self.as_slice().fmt(f)
248 }
249}
250
251impl From<&mut [GLfloat]> for GLfloatVecRefMut {
252 fn from(s: &mut [GLfloat]) -> Self {
253 Self {
254 ptr: s.as_mut_ptr(),
255 len: s.len(),
256 }
257 }
258}
259
260impl GLfloatVecRefMut {
261 #[must_use] pub const fn as_slice(&self) -> &[GLfloat] {
262 if self.ptr.is_null() || self.len == 0 {
264 return &[];
265 }
266 unsafe { core::slice::from_raw_parts(self.ptr, self.len) }
267 }
268 const fn as_mut_slice(&mut self) -> &mut [GLfloat] {
269 if self.ptr.is_null() || self.len == 0 {
271 return &mut [];
272 }
273 unsafe { core::slice::from_raw_parts_mut(self.ptr, self.len) }
274 }
275}
276
277#[repr(C)]
279pub struct GLintVecRefMut {
280 pub ptr: *mut i32,
281 pub len: usize,
282}
283
284impl Clone for GLintVecRefMut {
285 fn clone(&self) -> Self {
286 Self {
287 ptr: self.ptr,
288 len: self.len,
289 }
290 }
291}
292
293impl fmt::Debug for GLintVecRefMut {
294 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
295 self.as_slice().fmt(f)
296 }
297}
298
299impl From<&mut [GLint]> for GLintVecRefMut {
300 fn from(s: &mut [GLint]) -> Self {
301 Self {
302 ptr: s.as_mut_ptr(),
303 len: s.len(),
304 }
305 }
306}
307
308impl GLintVecRefMut {
309 #[must_use] pub const fn as_slice(&self) -> &[GLint] {
310 if self.ptr.is_null() || self.len == 0 {
312 return &[];
313 }
314 unsafe { core::slice::from_raw_parts(self.ptr, self.len) }
315 }
316 const fn as_mut_slice(&mut self) -> &mut [GLint] {
317 if self.ptr.is_null() || self.len == 0 {
319 return &mut [];
320 }
321 unsafe { core::slice::from_raw_parts_mut(self.ptr, self.len) }
322 }
323}
324
325#[repr(C)]
327pub struct GLuintVecRef {
328 pub ptr: *const u32,
329 pub len: usize,
330}
331
332impl Clone for GLuintVecRef {
333 fn clone(&self) -> Self {
334 Self {
335 ptr: self.ptr,
336 len: self.len,
337 }
338 }
339}
340
341impl fmt::Debug for GLuintVecRef {
342 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
343 self.as_slice().fmt(f)
344 }
345}
346
347impl From<&[GLuint]> for GLuintVecRef {
348 fn from(s: &[GLuint]) -> Self {
349 Self {
350 ptr: s.as_ptr(),
351 len: s.len(),
352 }
353 }
354}
355
356impl GLuintVecRef {
357 #[must_use] pub const fn as_slice(&self) -> &[GLuint] {
358 if self.ptr.is_null() || self.len == 0 {
360 return &[];
361 }
362 unsafe { core::slice::from_raw_parts(self.ptr, self.len) }
363 }
364}
365
366#[repr(C)]
368pub struct GLenumVecRef {
369 pub ptr: *const u32,
370 pub len: usize,
371}
372
373impl Clone for GLenumVecRef {
374 fn clone(&self) -> Self {
375 Self {
376 ptr: self.ptr,
377 len: self.len,
378 }
379 }
380}
381
382impl fmt::Debug for GLenumVecRef {
383 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
384 self.as_slice().fmt(f)
385 }
386}
387
388impl From<&[GLenum]> for GLenumVecRef {
389 fn from(s: &[GLenum]) -> Self {
390 Self {
391 ptr: s.as_ptr(),
392 len: s.len(),
393 }
394 }
395}
396
397impl GLenumVecRef {
398 #[must_use] pub const fn as_slice(&self) -> &[GLenum] {
399 if self.ptr.is_null() || self.len == 0 {
401 return &[];
402 }
403 unsafe { core::slice::from_raw_parts(self.ptr, self.len) }
404 }
405}
406
407#[repr(C)]
409pub struct U8VecRef {
410 pub ptr: *const u8,
411 pub len: usize,
412}
413
414impl Clone for U8VecRef {
415 fn clone(&self) -> Self {
416 Self {
417 ptr: self.ptr,
418 len: self.len,
419 }
420 }
421}
422
423impl From<&[u8]> for U8VecRef {
424 fn from(s: &[u8]) -> Self {
425 Self {
426 ptr: s.as_ptr(),
427 len: s.len(),
428 }
429 }
430}
431
432impl U8VecRef {
433 #[must_use] pub const fn as_slice(&self) -> &[u8] {
434 if self.ptr.is_null() || self.len == 0 {
436 return &[];
437 }
438 unsafe { core::slice::from_raw_parts(self.ptr, self.len) }
439 }
440}
441
442impl fmt::Debug for U8VecRef {
443 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
444 self.as_slice().fmt(f)
445 }
446}
447
448impl PartialOrd for U8VecRef {
449 fn partial_cmp(&self, rhs: &Self) -> Option<core::cmp::Ordering> {
450 self.as_slice().partial_cmp(rhs.as_slice())
451 }
452}
453
454impl Ord for U8VecRef {
455 fn cmp(&self, rhs: &Self) -> core::cmp::Ordering {
456 self.as_slice().cmp(rhs.as_slice())
457 }
458}
459
460impl PartialEq for U8VecRef {
461 fn eq(&self, rhs: &Self) -> bool {
462 self.as_slice().eq(rhs.as_slice())
463 }
464}
465
466impl Eq for U8VecRef {}
467
468impl Hash for U8VecRef {
469 fn hash<H>(&self, state: &mut H)
470 where
471 H: Hasher,
472 {
473 self.as_slice().hash(state);
474 }
475}
476
477#[repr(C)]
479pub struct F32VecRef {
480 pub ptr: *const f32,
481 pub len: usize,
482}
483
484impl Clone for F32VecRef {
485 fn clone(&self) -> Self {
486 Self {
487 ptr: self.ptr,
488 len: self.len,
489 }
490 }
491}
492
493impl fmt::Debug for F32VecRef {
494 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
495 self.as_slice().fmt(f)
496 }
497}
498
499impl From<&[f32]> for F32VecRef {
500 fn from(s: &[f32]) -> Self {
501 Self {
502 ptr: s.as_ptr(),
503 len: s.len(),
504 }
505 }
506}
507
508impl F32VecRef {
509 #[must_use] pub const fn as_slice(&self) -> &[f32] {
510 if self.ptr.is_null() || self.len == 0 {
512 return &[];
513 }
514 unsafe { core::slice::from_raw_parts(self.ptr, self.len) }
515 }
516}
517
518#[repr(C)]
520pub struct I32VecRef {
521 pub ptr: *const i32,
522 pub len: usize,
523}
524
525impl Clone for I32VecRef {
526 fn clone(&self) -> Self {
527 Self {
528 ptr: self.ptr,
529 len: self.len,
530 }
531 }
532}
533
534impl fmt::Debug for I32VecRef {
535 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
536 self.as_slice().fmt(f)
537 }
538}
539
540impl From<&[i32]> for I32VecRef {
541 fn from(s: &[i32]) -> Self {
542 Self {
543 ptr: s.as_ptr(),
544 len: s.len(),
545 }
546 }
547}
548
549impl I32VecRef {
550 #[must_use] pub const fn as_slice(&self) -> &[i32] {
551 if self.ptr.is_null() || self.len == 0 {
553 return &[];
554 }
555 unsafe { core::slice::from_raw_parts(self.ptr, self.len) }
556 }
557}
558
559#[repr(C)]
561pub struct GLbooleanVecRefMut {
562 pub ptr: *mut u8,
563 pub len: usize,
564}
565
566impl Clone for GLbooleanVecRefMut {
567 fn clone(&self) -> Self {
568 Self {
569 ptr: self.ptr,
570 len: self.len,
571 }
572 }
573}
574
575impl fmt::Debug for GLbooleanVecRefMut {
576 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
577 self.as_slice().fmt(f)
578 }
579}
580
581impl From<&mut [GLboolean]> for GLbooleanVecRefMut {
582 fn from(s: &mut [GLboolean]) -> Self {
583 Self {
584 ptr: s.as_mut_ptr(),
585 len: s.len(),
586 }
587 }
588}
589
590impl GLbooleanVecRefMut {
591 #[must_use] pub const fn as_slice(&self) -> &[GLboolean] {
592 if self.ptr.is_null() || self.len == 0 {
594 return &[];
595 }
596 unsafe { core::slice::from_raw_parts(self.ptr, self.len) }
597 }
598 const fn as_mut_slice(&mut self) -> &mut [GLboolean] {
599 if self.ptr.is_null() || self.len == 0 {
601 return &mut [];
602 }
603 unsafe { core::slice::from_raw_parts_mut(self.ptr, self.len) }
604 }
605}
606
607#[repr(C)]
609pub struct U8VecRefMut {
610 pub ptr: *mut u8,
611 pub len: usize,
612}
613
614impl Clone for U8VecRefMut {
615 fn clone(&self) -> Self {
616 Self {
617 ptr: self.ptr,
618 len: self.len,
619 }
620 }
621}
622
623impl fmt::Debug for U8VecRefMut {
624 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
625 self.as_slice().fmt(f)
626 }
627}
628
629impl From<&mut [u8]> for U8VecRefMut {
630 fn from(s: &mut [u8]) -> Self {
631 Self {
632 ptr: s.as_mut_ptr(),
633 len: s.len(),
634 }
635 }
636}
637
638impl U8VecRefMut {
639 #[must_use] pub const fn as_slice(&self) -> &[u8] {
640 if self.ptr.is_null() || self.len == 0 {
642 return &[];
643 }
644 unsafe { core::slice::from_raw_parts(self.ptr, self.len) }
645 }
646 const fn as_mut_slice(&mut self) -> &mut [u8] {
647 if self.ptr.is_null() || self.len == 0 {
649 return &mut [];
650 }
651 unsafe { core::slice::from_raw_parts_mut(self.ptr, self.len) }
652 }
653}
654
655impl_option!(
656 U8VecRef,
657 OptionU8VecRef,
658 copy = false,
659 [Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
660);
661
662#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
663#[repr(C)]
664pub struct DebugMessage {
665 pub message: AzString,
666 pub source: GLenum,
667 pub ty: GLenum,
668 pub id: GLenum,
669 pub severity: GLenum,
670}
671
672impl_option!(
673 DebugMessage,
674 OptionDebugMessage,
675 copy = false,
676 [Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash]
677);
678
679impl_vec!(DebugMessage, DebugMessageVec, DebugMessageVecDestructor, DebugMessageVecDestructorType, DebugMessageVecSlice, OptionDebugMessage);
680impl_vec_debug!(DebugMessage, DebugMessageVec);
681impl_vec_partialord!(DebugMessage, DebugMessageVec);
682impl_vec_ord!(DebugMessage, DebugMessageVec);
683impl_vec_clone!(DebugMessage, DebugMessageVec, DebugMessageVecDestructor);
684impl_vec_partialeq!(DebugMessage, DebugMessageVec);
685impl_vec_eq!(DebugMessage, DebugMessageVec);
686impl_vec_hash!(DebugMessage, DebugMessageVec);
687
688impl_vec!(GLint, GLintVec, GLintVecDestructor, GLintVecDestructorType, GLintVecSlice, OptionI32);
689impl_vec_debug!(GLint, GLintVec);
690impl_vec_partialord!(GLint, GLintVec);
691impl_vec_ord!(GLint, GLintVec);
692impl_vec_clone!(GLint, GLintVec, GLintVecDestructor);
693impl_vec_partialeq!(GLint, GLintVec);
694impl_vec_eq!(GLint, GLintVec);
695impl_vec_hash!(GLint, GLintVec);
696
697impl_vec!(GLuint, GLuintVec, GLuintVecDestructor, GLuintVecDestructorType, GLuintVecSlice, OptionU32);
698impl_vec_debug!(GLuint, GLuintVec);
699impl_vec_partialord!(GLuint, GLuintVec);
700impl_vec_ord!(GLuint, GLuintVec);
701impl_vec_clone!(GLuint, GLuintVec, GLuintVecDestructor);
702impl_vec_partialeq!(GLuint, GLuintVec);
703impl_vec_eq!(GLuint, GLuintVec);
704impl_vec_hash!(GLuint, GLuintVec);
705
706#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
707#[repr(C)]
708pub enum GlType {
709 Gl,
710 Gles,
711}
712
713impl From<GlContextGlType> for GlType {
714 fn from(a: GlContextGlType) -> Self {
715 match a {
716 GlContextGlType::Gl => Self::Gl,
717 GlContextGlType::GlEs => Self::Gles,
718 }
719 }
720}
721
722#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
724#[repr(C)]
725#[allow(clippy::pub_underscore_fields)]
727pub struct GetProgramBinaryReturn {
728 pub _0: U8Vec,
729 pub _1: u32,
730}
731
732#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
734#[repr(C)]
735#[allow(clippy::pub_underscore_fields)]
737pub struct GetActiveAttribReturn {
738 pub _0: i32,
739 pub _1: u32,
740 pub _2: AzString,
741}
742
743#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
745#[repr(C)]
746#[allow(clippy::pub_underscore_fields)]
748pub struct GetActiveUniformReturn {
749 pub _0: i32,
750 pub _1: u32,
751 pub _2: AzString,
752}
753
754#[repr(C)]
755pub struct GLsyncPtr {
756 pub ptr: *const c_void, pub run_destructor: bool,
758}
759
760impl Clone for GLsyncPtr {
761 fn clone(&self) -> Self {
762 Self {
763 ptr: self.ptr,
764 run_destructor: true,
765 }
766 }
767}
768
769impl fmt::Debug for GLsyncPtr {
770 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
771 write!(f, "0x{:0x}", self.ptr as usize)
772 }
773}
774
775impl GLsyncPtr {
776 #[must_use] pub const fn new(p: GLsync) -> Self {
777 Self {
778 ptr: p,
779 run_destructor: true,
780 }
781 }
782 #[must_use] pub fn get(self) -> GLsync {
783 self.ptr as GLsync
784 }
785}
786
787impl Drop for GLsyncPtr {
788 fn drop(&mut self) {
789 self.run_destructor = false;
790 }
791}
792
793pub type GlTextureStorage = OrderedMap<Epoch, OrderedMap<ExternalImageId, Texture>>;
796
797static mut ACTIVE_GL_TEXTURES: Option<OrderedMap<DocumentId, GlTextureStorage>> = None;
814
815#[inline]
826#[allow(clippy::deref_addrof)] fn active_gl_textures() -> &'static mut Option<OrderedMap<DocumentId, GlTextureStorage>> {
828 unsafe { &mut *(&raw mut ACTIVE_GL_TEXTURES) }
831}
832
833#[must_use]
842pub fn insert_into_active_gl_textures(
843 document_id: DocumentId,
844 epoch: Epoch,
845 texture: Texture,
846) -> ExternalImageId {
847 let external_image_id = ExternalImageId::new();
848
849 let active = active_gl_textures();
850 if active.is_none() {
851 *active = Some(OrderedMap::new());
852 }
853 let active_textures = active.as_mut().unwrap();
854 let active_epochs = active_textures.entry(document_id).or_default();
855 let active_textures_for_epoch = active_epochs.entry(epoch).or_default();
856 active_textures_for_epoch.insert(external_image_id, texture);
857
858 external_image_id
859}
860
861pub fn gl_textures_remove_epochs_from_pipeline(document_id: &DocumentId, epoch: Epoch) {
864 let Some(active_textures) = active_gl_textures().as_mut() else {
866 return;
867 };
868
869 let Some(active_epochs) = active_textures.get_mut(document_id) else {
870 return;
871 };
872
873 let mut epochs_to_remove = Vec::new();
876
877 for (gl_texture_epoch, _) in active_epochs.iter() {
878 if *gl_texture_epoch < epoch {
879 epochs_to_remove.push(*gl_texture_epoch);
880 }
881 }
882
883 for epoch in epochs_to_remove {
884 active_epochs.remove(&epoch);
885 }
886}
887
888#[must_use] pub fn remove_single_texture_from_active_gl_textures(
890 document_id: &DocumentId,
891 epoch: &Epoch,
892 external_image_id: &ExternalImageId,
893) -> Option<()> {
894 let active_textures = active_gl_textures().as_mut()?;
895 let epochs = active_textures.get_mut(document_id)?;
896 let images_in_epoch = epochs.get_mut(epoch)?;
897 images_in_epoch.remove(external_image_id);
898 Some(())
899}
900
901pub fn gl_textures_remove_active_pipeline(document_id: &DocumentId) {
903 let Some(active_textures) = active_gl_textures().as_mut() else {
904 return;
905 };
906 active_textures.remove(document_id);
907}
908
909#[allow(clippy::cast_precision_loss)] pub fn gl_textures_clear_opengl_cache() {
912 *active_gl_textures() = None;
913}
914
915#[allow(clippy::cast_precision_loss)] #[must_use] pub fn get_opengl_texture(image_key: &ExternalImageId) -> Option<(GLuint, (f32, f32))> {
927 let active_textures = active_gl_textures().as_ref()?;
928 active_textures
929 .values()
930 .flat_map(|active_document| active_document.values())
931 .find_map(|active_epoch| active_epoch.get(image_key))
932 .map(|tex| {
933 (
934 tex.texture_id,
935 (tex.size.width as f32, tex.size.height as f32),
936 )
937 })
938}
939
940#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd)]
942#[repr(C)]
943#[allow(clippy::pub_underscore_fields)]
945pub struct GlShaderPrecisionFormatReturn {
946 pub _0: GLint,
947 pub _1: GLint,
948 pub _2: GLint,
949}
950
951#[repr(C)]
952pub struct GlContextPtr {
953 pub ptr: ManuallyDrop<Box<Rc<GlContextPtrInner>>>,
962 pub renderer_type: RendererType,
964 pub run_destructor: bool,
965}
966
967impl Clone for GlContextPtr {
968 fn clone(&self) -> Self {
969 Self {
970 ptr: ManuallyDrop::new((*self.ptr).clone()),
971 renderer_type: self.renderer_type,
972 run_destructor: true,
973 }
974 }
975}
976
977impl Drop for GlContextPtr {
978 fn drop(&mut self) {
979 if self.run_destructor {
984 self.run_destructor = false;
985 unsafe { ManuallyDrop::drop(&mut self.ptr); }
986 }
987 }
988}
989
990impl GlContextPtr {
991 #[must_use] pub fn get_svg_shader(&self) -> GLuint {
992 self.ptr.svg_shader
993 }
994 #[must_use] pub fn is_gl_usable(&self) -> bool {
1000 self.ptr.svg_shader != 0
1001 }
1002 #[must_use] pub fn get_usable_glsl_version(&self) -> AzString {
1006 self.ptr.glsl_version.clone()
1007 }
1008 #[must_use] pub fn get_brush_shader(&self) -> GLuint {
1010 self.ptr.brush_shader
1011 }
1012 #[must_use] pub fn get_fxaa_shader(&self) -> GLuint {
1013 self.ptr.fxaa_shader
1014 }
1015}
1016
1017#[repr(C)]
1018pub struct GlContextPtrInner {
1019 pub ptr: Rc<GenericGlContext>,
1020 pub svg_shader: GLuint,
1022 pub svg_multicolor_shader: GLuint,
1024 pub fxaa_shader: GLuint,
1026 pub brush_shader: GLuint,
1028 pub glsl_version: AzString,
1031}
1032
1033impl fmt::Debug for GlContextPtrInner {
1034 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1036 f.debug_struct("GlContextPtrInner")
1037 .field("svg_shader", &self.svg_shader)
1038 .field("svg_multicolor_shader", &self.svg_multicolor_shader)
1039 .field("fxaa_shader", &self.fxaa_shader)
1040 .field("brush_shader", &self.brush_shader)
1041 .field("glsl_version", &self.glsl_version)
1042 .finish_non_exhaustive()
1043 }
1044}
1045
1046impl Drop for GlContextPtrInner {
1047 fn drop(&mut self) {
1048 self.ptr.delete_program(self.svg_shader);
1049 self.ptr.delete_program(self.svg_multicolor_shader);
1050 self.ptr.delete_program(self.fxaa_shader);
1051 if self.brush_shader != 0 {
1052 self.ptr.delete_program(self.brush_shader);
1053 }
1054 }
1055}
1056
1057impl_option!(
1058 GlContextPtr,
1059 OptionGlContextPtr,
1060 copy = false,
1061 [Debug, Clone, PartialEq, Eq, PartialOrd, Ord]
1062);
1063
1064impl fmt::Debug for GlContextPtr {
1065 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1066 write!(f, "0x{:0x}", self.as_usize())
1067 }
1068}
1069
1070static SVG_VERTEX_SHADER: &[u8] = b"#version 150
1071
1072#if __VERSION__ != 100
1073 #define varying out
1074 #define attribute in
1075#endif
1076
1077uniform vec2 vBboxSize;
1078uniform mat4 vTransformMatrix;
1079
1080attribute vec2 vAttrXY;
1081
1082void main() {
1083 vec4 vTransposed = vec4(vAttrXY, 1.0, 1.0) * vTransformMatrix;
1084 vec2 vTransposedInScreen = vTransposed.xy / vBboxSize;
1085 vec2 vCalcFinal = (vTransposedInScreen * vec2(2.0)) - vec2(1.0);
1086 gl_Position = vec4(vCalcFinal, 1.0, 1.0);
1087}";
1088
1089static SVG_FRAGMENT_SHADER: &[u8] = b"#version 150
1090
1091precision highp float;
1092
1093uniform vec4 fDrawColor;
1094
1095#if __VERSION__ == 100
1096 #define oFragColor gl_FragColor
1097#else
1098 out vec4 oFragColor;
1099#endif
1100
1101void main() {
1102 oFragColor = fDrawColor;
1103}";
1104
1105static SVG_MULTICOLOR_VERTEX_SHADER: &[u8] = b"#version 150
1106
1107#if __VERSION__ != 100
1108 #define varying out
1109 #define attribute in
1110#endif
1111
1112uniform vec2 vBboxSize;
1113uniform mat4 vTransformMatrix;
1114
1115attribute vec3 vAttrXY;
1116attribute vec4 vColor;
1117varying vec4 fColor;
1118
1119void main() {
1120 vec4 vTransposed = vec4(vAttrXY.xy, 1.0, 1.0) * vTransformMatrix;
1121 vec2 vTransposedInScreen = vTransposed.xy / vBboxSize;
1122 vec2 vCalcFinal = (vTransposedInScreen * vec2(2.0)) - vec2(1.0);
1123 gl_Position = vec4(vCalcFinal, vAttrXY.z, 1.0);
1124 fColor = vColor;
1125}";
1126
1127static SVG_MULTICOLOR_FRAGMENT_SHADER: &[u8] = b"#version 150
1128
1129precision highp float;
1130
1131#if __VERSION__ != 100
1132 #define varying in
1133#endif
1134
1135#if __VERSION__ == 100
1136 #define oFragColor gl_FragColor
1137#else
1138 out vec4 oFragColor;
1139#endif
1140
1141varying vec4 fColor;
1142
1143void main() {
1144 oFragColor = fColor;
1145}";
1146
1147static BRUSH_VERTEX_SHADER: &[u8] = b"#version 150
1152
1153#if __VERSION__ != 100
1154 #define varying out
1155 #define attribute in
1156#endif
1157
1158attribute vec2 aPos;
1159attribute vec2 aUv;
1160varying vec2 vUv;
1161
1162void main() {
1163 vUv = aUv;
1164 gl_Position = vec4(aPos, 0.0, 1.0);
1165}";
1166
1167static BRUSH_FRAGMENT_SHADER: &[u8] = b"#version 150
1168
1169precision highp float;
1170
1171#if __VERSION__ != 100
1172 #define varying in
1173#endif
1174
1175#if __VERSION__ == 100
1176 #define oFragColor gl_FragColor
1177#else
1178 out vec4 oFragColor;
1179#endif
1180
1181uniform vec4 uColor; // rgb + alpha (alpha already folds in flow * color.a)
1182uniform float uHardness; // 0 = soft .. 1 = hard edge
1183
1184varying vec2 vUv;
1185
1186void main() {
1187 float d = length(vUv);
1188 if (d > 1.0) { discard; }
1189 float edge0 = clamp(uHardness, 0.0, 1.0);
1190 float x = clamp((d - edge0) / max(1.0 - edge0, 1.0e-4), 0.0, 1.0);
1191 float cov = 1.0 - (x * x * (3.0 - 2.0 * x));
1192 oFragColor = vec4(uColor.rgb, uColor.a * cov);
1193}";
1194
1195#[allow(dead_code)]
1199#[allow(clippy::used_underscore_binding)] #[allow(clippy::cast_possible_wrap)] fn check_shader_compile(gl_context: &GenericGlContext, shader: GLuint, _label: &str) {
1202 let mut status = [0_i32];
1203 unsafe { gl_context.get_shader_iv(shader, gl::COMPILE_STATUS, &mut status) };
1204 if status[0] != gl::TRUE as i32 {
1205 #[cfg(feature = "std")]
1206 {
1207 let log = gl_context.get_shader_info_log(shader);
1208 eprintln!("azul: {_label} shader compile error: {log}");
1209 }
1210 }
1211}
1212
1213#[allow(dead_code)]
1215#[allow(clippy::used_underscore_binding)] #[allow(clippy::cast_possible_wrap)] fn check_program_link(gl_context: &GenericGlContext, program: GLuint, _label: &str) {
1218 let mut status = [0_i32];
1219 unsafe { gl_context.get_program_iv(program, gl::LINK_STATUS, &mut status) };
1220 if status[0] != gl::TRUE as i32 {
1221 #[cfg(feature = "std")]
1222 {
1223 let log = gl_context.get_program_info_log(program);
1224 eprintln!("azul: {_label} program link error: {log}");
1225 }
1226 }
1227}
1228
1229#[cfg(feature = "std")]
1233fn shader_with_glsl_version(src: &[u8], version_line: &[u8]) -> Vec<u8> {
1234 let body_start = src.iter().position(|&b| b == b'\n').map_or(0, |i| i + 1);
1235 let mut out = Vec::with_capacity(version_line.len() + src.len() - body_start);
1236 out.extend_from_slice(version_line);
1237 out.extend_from_slice(&src[body_start..]);
1238 out
1239}
1240
1241#[cfg(feature = "std")]
1248#[allow(clippy::cast_possible_wrap)]
1250fn try_compile_program(
1251 gl_context: &GenericGlContext,
1252 vert_src: &[u8],
1253 frag_src: &[u8],
1254 version_line: &[u8],
1255 attribs: &[(u32, &str)],
1256) -> Option<GLuint> {
1257 let vs = gl_context.create_shader(gl::VERTEX_SHADER);
1258 gl_context.shader_source(vs, &[shader_with_glsl_version(vert_src, version_line).as_slice()]);
1259 gl_context.compile_shader(vs);
1260 let fs = gl_context.create_shader(gl::FRAGMENT_SHADER);
1261 gl_context.shader_source(fs, &[shader_with_glsl_version(frag_src, version_line).as_slice()]);
1262 gl_context.compile_shader(fs);
1263
1264 let mut s = [0_i32];
1265 unsafe { gl_context.get_shader_iv(vs, gl::COMPILE_STATUS, &mut s) };
1266 let vs_ok = s[0] == gl::TRUE as i32;
1267 unsafe { gl_context.get_shader_iv(fs, gl::COMPILE_STATUS, &mut s) };
1268 let fs_ok = s[0] == gl::TRUE as i32;
1269 if !vs_ok || !fs_ok {
1270 gl_context.delete_shader(vs);
1271 gl_context.delete_shader(fs);
1272 return None;
1273 }
1274
1275 let prog = gl_context.create_program();
1276 gl_context.attach_shader(prog, vs);
1277 gl_context.attach_shader(prog, fs);
1278 for (loc, name) in attribs {
1279 gl_context.bind_attrib_location(prog, *loc, name);
1280 }
1281 gl_context.link_program(prog);
1282 gl_context.delete_shader(vs);
1283 gl_context.delete_shader(fs);
1284
1285 let mut l = [0_i32];
1286 unsafe { gl_context.get_program_iv(prog, gl::LINK_STATUS, &mut l) };
1287 if l[0] == gl::TRUE as i32 {
1288 Some(prog)
1289 } else {
1290 gl_context.delete_program(prog);
1291 None
1292 }
1293}
1294
1295const fn glsl_version_candidates(gl_type: GlType) -> &'static [&'static [u8]] {
1298 match gl_type {
1299 GlType::Gl => &[b"#version 150\n", b"#version 330\n", b"#version 140\n"],
1300 GlType::Gles => &[b"#version 300 es\n", b"#version 100\n"],
1301 }
1302}
1303
1304impl GlContextPtr {
1305 #[must_use] pub fn new(renderer_type: RendererType, gl_context: Rc<GenericGlContext>) -> Self {
1306 #[cfg(feature = "std")]
1317 let (svg_program_id, svg_multicolor_program_id, fxaa_program_id, brush_program_id, glsl_version) =
1318 if matches!(renderer_type, RendererType::Hardware) {
1319 use crate::gl_fxaa::{FXAA_FRAGMENT_SHADER, FXAA_VERTEX_SHADER};
1320 let gl_type: GlType = gl_context.get_type().into();
1321 let mut svg = 0;
1323 let mut chosen: Option<&'static [u8]> = None;
1324 for ver in glsl_version_candidates(gl_type) {
1325 if let Some(p) = try_compile_program(
1326 &gl_context, SVG_VERTEX_SHADER, SVG_FRAGMENT_SHADER, ver, &[(0, "vAttrXY")],
1327 ) {
1328 svg = p;
1329 chosen = Some(ver);
1330 break;
1331 }
1332 }
1333 chosen.map_or_else(|| {
1334 eprintln!(
1335 "azul: GL context UNUSABLE -- no GLSL version ({gl_type:?}) compiled the SVG \
1336 shaders; the window should fall back to CPU rendering (is_gl_usable()=false)"
1337 );
1338 (0, 0, 0, 0, AzString::from_const_str(""))
1339 }, |ver| {
1340 let ver_str: AzString = core::str::from_utf8(ver)
1342 .unwrap_or("")
1343 .trim()
1344 .trim_start_matches("#version ")
1345 .into();
1346 eprintln!(
1347 "azul: GL usable -- shaders compiled at GLSL {} ({:?})",
1348 ver_str.as_str(),
1349 gl_type
1350 );
1351 let mc = try_compile_program(
1352 &gl_context, SVG_MULTICOLOR_VERTEX_SHADER, SVG_MULTICOLOR_FRAGMENT_SHADER,
1353 ver, &[(0, "vAttrXY"), (1, "vColor")],
1354 ).unwrap_or(0);
1355 let fxaa = try_compile_program(
1356 &gl_context, FXAA_VERTEX_SHADER, FXAA_FRAGMENT_SHADER, ver, &[(0, "vAttrXY")],
1357 ).unwrap_or(0);
1358 let brush = try_compile_program(
1359 &gl_context, BRUSH_VERTEX_SHADER, BRUSH_FRAGMENT_SHADER, ver,
1360 &[(0, "aPos"), (1, "aUv")],
1361 ).unwrap_or(0);
1362 (svg, mc, fxaa, brush, ver_str)
1363 })
1364 } else {
1365 (0, 0, 0, 0, AzString::from_const_str(""))
1366 };
1367 #[cfg(not(feature = "std"))]
1369 let (svg_program_id, svg_multicolor_program_id, fxaa_program_id, brush_program_id, glsl_version) =
1370 (0u32, 0u32, 0u32, 0u32, AzString::from_const_str(""));
1371
1372
1373 Self {
1374 ptr: ManuallyDrop::new(Box::new(Rc::new(GlContextPtrInner {
1375 svg_shader: svg_program_id,
1376 svg_multicolor_shader: svg_multicolor_program_id,
1377 fxaa_shader: fxaa_program_id,
1378 brush_shader: brush_program_id,
1379 glsl_version,
1380 ptr: gl_context,
1381 }))),
1382 renderer_type,
1383 run_destructor: true,
1384 }
1385 }
1386
1387 #[must_use] pub fn get(&self) -> &Rc<GenericGlContext> {
1388 &self.ptr.ptr
1389 }
1390 fn as_usize(&self) -> usize {
1391 (Rc::as_ptr(&self.ptr.ptr) as *const c_void) as usize
1392 }
1393}
1394
1395#[allow(clippy::needless_pass_by_value)]
1400impl GlContextPtr {
1401 #[must_use] pub fn get_type(&self) -> GlType {
1402 self.get().get_type().into()
1403 }
1404 pub fn buffer_data_untyped(
1405 &self,
1406 target: GLenum,
1407 size: GLsizeiptr,
1408 data: GlVoidPtrConst,
1409 usage: GLenum,
1410 ) {
1411 self.get()
1412 .buffer_data_untyped(target, size, data.ptr, usage);
1413 }
1414 pub fn buffer_sub_data_untyped(
1415 &self,
1416 target: GLenum,
1417 offset: isize,
1418 size: GLsizeiptr,
1419 data: GlVoidPtrConst,
1420 ) {
1421 self.get()
1422 .buffer_sub_data_untyped(target, offset, size, data.ptr);
1423 }
1424 #[must_use] pub fn map_buffer(&self, target: GLenum, access: GLbitfield) -> GlVoidPtrMut {
1425 GlVoidPtrMut {
1426 ptr: self.get().map_buffer(target, access),
1427 }
1428 }
1429 #[must_use] pub fn map_buffer_range(
1430 &self,
1431 target: GLenum,
1432 offset: GLintptr,
1433 length: GLsizeiptr,
1434 access: GLbitfield,
1435 ) -> GlVoidPtrMut {
1436 GlVoidPtrMut {
1437 ptr: self.get().map_buffer_range(target, offset, length, access),
1438 }
1439 }
1440 #[must_use] pub fn unmap_buffer(&self, target: GLenum) -> GLboolean {
1441 self.get().unmap_buffer(target)
1442 }
1443 pub fn tex_buffer(&self, target: GLenum, internal_format: GLenum, buffer: GLuint) {
1444 self.get().tex_buffer(target, internal_format, buffer);
1445 }
1446 pub fn shader_source(&self, shader: GLuint, strings: StringVec) {
1447 fn str_to_bytes(input: &str) -> Vec<u8> {
1448 let mut v: Vec<u8> = input.into();
1449 v.push(0);
1450 v
1451 }
1452 let shaders_as_bytes = strings
1453 .iter()
1454 .map(|s| str_to_bytes(s.as_str()))
1455 .collect::<Vec<_>>();
1456 let shaders_as_bytes = shaders_as_bytes
1457 .iter()
1458 .map(AsRef::as_ref)
1459 .collect::<Vec<_>>();
1460 self.get().shader_source(shader, &shaders_as_bytes);
1461 }
1462 pub fn read_buffer(&self, mode: GLenum) {
1463 self.get().read_buffer(mode);
1464 }
1465 pub fn read_pixels_into_buffer(
1466 &self,
1467 x: GLint,
1468 y: GLint,
1469 width: GLsizei,
1470 height: GLsizei,
1471 format: GLenum,
1472 pixel_type: GLenum,
1473 mut dst_buffer: U8VecRefMut,
1474 ) {
1475 self.get().read_pixels_into_buffer(
1476 x,
1477 y,
1478 width,
1479 height,
1480 format,
1481 pixel_type,
1482 dst_buffer.as_mut_slice(),
1483 );
1484 }
1485 #[must_use] pub fn read_pixels(
1486 &self,
1487 x: GLint,
1488 y: GLint,
1489 width: GLsizei,
1490 height: GLsizei,
1491 format: GLenum,
1492 pixel_type: GLenum,
1493 ) -> U8Vec {
1494 self.get()
1495 .read_pixels(x, y, width, height, format, pixel_type)
1496 .into()
1497 }
1498 pub fn read_pixels_into_pbo(
1499 &self,
1500 x: GLint,
1501 y: GLint,
1502 width: GLsizei,
1503 height: GLsizei,
1504 format: GLenum,
1505 pixel_type: GLenum,
1506 ) {
1507 unsafe {
1508 self.get()
1509 .read_pixels_into_pbo(x, y, width, height, format, pixel_type);
1510 }
1511 }
1512 pub fn sample_coverage(&self, value: GLclampf, invert: bool) {
1513 self.get().sample_coverage(value, invert);
1514 }
1515 pub fn polygon_offset(&self, factor: GLfloat, units: GLfloat) {
1516 self.get().polygon_offset(factor, units);
1517 }
1518 pub fn pixel_store_i(&self, name: GLenum, param: GLint) {
1519 self.get().pixel_store_i(name, param);
1520 }
1521 #[must_use] pub fn gen_buffers(&self, n: GLsizei) -> GLuintVec {
1522 self.get().gen_buffers(n).into()
1523 }
1524 #[must_use] pub fn gen_renderbuffers(&self, n: GLsizei) -> GLuintVec {
1525 self.get().gen_renderbuffers(n).into()
1526 }
1527 #[must_use] pub fn gen_framebuffers(&self, n: GLsizei) -> GLuintVec {
1528 self.get().gen_framebuffers(n).into()
1529 }
1530 #[must_use] pub fn gen_textures(&self, n: GLsizei) -> GLuintVec {
1531 self.get().gen_textures(n).into()
1532 }
1533 #[must_use] pub fn gen_vertex_arrays(&self, n: GLsizei) -> GLuintVec {
1534 self.get().gen_vertex_arrays(n).into()
1535 }
1536 #[must_use] pub fn gen_queries(&self, n: GLsizei) -> GLuintVec {
1537 self.get().gen_queries(n).into()
1538 }
1539 pub fn begin_query(&self, target: GLenum, id: GLuint) {
1540 self.get().begin_query(target, id);
1541 }
1542 pub fn end_query(&self, target: GLenum) {
1543 self.get().end_query(target);
1544 }
1545 pub fn query_counter(&self, id: GLuint, target: GLenum) {
1546 self.get().query_counter(id, target);
1547 }
1548 #[must_use] pub fn get_query_object_iv(&self, id: GLuint, pname: GLenum) -> i32 {
1549 self.get().get_query_object_iv(id, pname)
1550 }
1551 #[must_use] pub fn get_query_object_uiv(&self, id: GLuint, pname: GLenum) -> u32 {
1552 self.get().get_query_object_uiv(id, pname)
1553 }
1554 #[must_use] pub fn get_query_object_i64v(&self, id: GLuint, pname: GLenum) -> i64 {
1555 self.get().get_query_object_i64v(id, pname)
1556 }
1557 #[must_use] pub fn get_query_object_ui64v(&self, id: GLuint, pname: GLenum) -> u64 {
1558 self.get().get_query_object_ui64v(id, pname)
1559 }
1560 pub fn delete_queries(&self, queries: GLuintVecRef) {
1561 self.get().delete_queries(queries.as_slice());
1562 }
1563 pub fn delete_vertex_arrays(&self, vertex_arrays: GLuintVecRef) {
1564 self.get().delete_vertex_arrays(vertex_arrays.as_slice());
1565 }
1566 pub fn delete_buffers(&self, buffers: GLuintVecRef) {
1567 self.get().delete_buffers(buffers.as_slice());
1568 }
1569 pub fn delete_renderbuffers(&self, renderbuffers: GLuintVecRef) {
1570 self.get().delete_renderbuffers(renderbuffers.as_slice());
1571 }
1572 pub fn delete_framebuffers(&self, framebuffers: GLuintVecRef) {
1573 self.get().delete_framebuffers(framebuffers.as_slice());
1574 }
1575 pub fn delete_textures(&self, textures: GLuintVecRef) {
1576 self.get().delete_textures(textures.as_slice());
1577 }
1578 pub fn framebuffer_renderbuffer(
1579 &self,
1580 target: GLenum,
1581 attachment: GLenum,
1582 renderbuffertarget: GLenum,
1583 renderbuffer: GLuint,
1584 ) {
1585 self.get()
1586 .framebuffer_renderbuffer(target, attachment, renderbuffertarget, renderbuffer);
1587 }
1588 pub fn renderbuffer_storage(
1589 &self,
1590 target: GLenum,
1591 internalformat: GLenum,
1592 width: GLsizei,
1593 height: GLsizei,
1594 ) {
1595 self.get()
1596 .renderbuffer_storage(target, internalformat, width, height);
1597 }
1598 pub fn depth_func(&self, func: GLenum) {
1599 self.get().depth_func(func);
1600 }
1601 pub fn active_texture(&self, texture: GLenum) {
1602 self.get().active_texture(texture);
1603 }
1604 pub fn attach_shader(&self, program: GLuint, shader: GLuint) {
1605 self.get().attach_shader(program, shader);
1606 }
1607 pub fn bind_attrib_location(&self, program: GLuint, index: GLuint, name: &str) {
1608 self.get()
1609 .bind_attrib_location(program, index, name);
1610 }
1611 pub fn get_uniform_iv(&self, program: GLuint, location: GLint, mut result: GLintVecRefMut) {
1612 unsafe {
1613 self.get()
1614 .get_uniform_iv(program, location, result.as_mut_slice());
1615 }
1616 }
1617 pub fn get_uniform_fv(&self, program: GLuint, location: GLint, mut result: GLfloatVecRefMut) {
1618 unsafe {
1619 self.get()
1620 .get_uniform_fv(program, location, result.as_mut_slice());
1621 }
1622 }
1623 #[must_use] pub fn get_uniform_block_index(&self, program: GLuint, name: &str) -> GLuint {
1624 self.get().get_uniform_block_index(program, name)
1625 }
1626 #[must_use] pub fn get_uniform_indices(&self, program: GLuint, names: RefstrVecRef) -> GLuintVec {
1627 let names_vec = names
1628 .as_slice()
1629 .iter()
1630 .map(Refstr::as_str)
1631 .collect::<Vec<_>>();
1632 self.get().get_uniform_indices(program, &names_vec).into()
1633 }
1634 pub fn bind_buffer_base(&self, target: GLenum, index: GLuint, buffer: GLuint) {
1635 self.get().bind_buffer_base(target, index, buffer);
1636 }
1637 pub fn bind_buffer_range(
1638 &self,
1639 target: GLenum,
1640 index: GLuint,
1641 buffer: GLuint,
1642 offset: GLintptr,
1643 size: GLsizeiptr,
1644 ) {
1645 self.get()
1646 .bind_buffer_range(target, index, buffer, offset, size);
1647 }
1648 pub fn uniform_block_binding(
1649 &self,
1650 program: GLuint,
1651 uniform_block_index: GLuint,
1652 uniform_block_binding: GLuint,
1653 ) {
1654 self.get()
1655 .uniform_block_binding(program, uniform_block_index, uniform_block_binding);
1656 }
1657 pub fn bind_buffer(&self, target: GLenum, buffer: GLuint) {
1658 self.get().bind_buffer(target, buffer);
1659 }
1660 pub fn bind_vertex_array(&self, vao: GLuint) {
1661 self.get().bind_vertex_array(vao);
1662 }
1663 pub fn bind_renderbuffer(&self, target: GLenum, renderbuffer: GLuint) {
1664 self.get().bind_renderbuffer(target, renderbuffer);
1665 }
1666 pub fn bind_framebuffer(&self, target: GLenum, framebuffer: GLuint) {
1667 self.get().bind_framebuffer(target, framebuffer);
1668 }
1669 pub fn bind_texture(&self, target: GLenum, texture: GLuint) {
1670 self.get().bind_texture(target, texture);
1671 }
1672 pub fn draw_buffers(&self, bufs: GLenumVecRef) {
1673 self.get().draw_buffers(bufs.as_slice());
1674 }
1675 pub fn tex_image_2d(
1676 &self,
1677 target: GLenum,
1678 level: GLint,
1679 internal_format: GLint,
1680 width: GLsizei,
1681 height: GLsizei,
1682 border: GLint,
1683 format: GLenum,
1684 ty: GLenum,
1685 opt_data: OptionU8VecRef,
1686 ) {
1687 let opt_data = opt_data.as_option();
1688 let opt_data: Option<&[u8]> = opt_data.map(U8VecRef::as_slice);
1689 self.get().tex_image_2d(
1690 target,
1691 level,
1692 internal_format,
1693 width,
1694 height,
1695 border,
1696 format,
1697 ty,
1698 opt_data,
1699 );
1700 }
1701 pub fn compressed_tex_image_2d(
1702 &self,
1703 target: GLenum,
1704 level: GLint,
1705 internal_format: GLenum,
1706 width: GLsizei,
1707 height: GLsizei,
1708 border: GLint,
1709 data: U8VecRef,
1710 ) {
1711 self.get().compressed_tex_image_2d(
1712 target,
1713 level,
1714 internal_format,
1715 width,
1716 height,
1717 border,
1718 data.as_slice(),
1719 );
1720 }
1721 pub fn compressed_tex_sub_image_2d(
1722 &self,
1723 target: GLenum,
1724 level: GLint,
1725 xoffset: GLint,
1726 yoffset: GLint,
1727 width: GLsizei,
1728 height: GLsizei,
1729 format: GLenum,
1730 data: U8VecRef,
1731 ) {
1732 self.get().compressed_tex_sub_image_2d(
1733 target,
1734 level,
1735 xoffset,
1736 yoffset,
1737 width,
1738 height,
1739 format,
1740 data.as_slice(),
1741 );
1742 }
1743 pub fn tex_image_3d(
1744 &self,
1745 target: GLenum,
1746 level: GLint,
1747 internal_format: GLint,
1748 width: GLsizei,
1749 height: GLsizei,
1750 depth: GLsizei,
1751 border: GLint,
1752 format: GLenum,
1753 ty: GLenum,
1754 opt_data: OptionU8VecRef,
1755 ) {
1756 let opt_data = opt_data.as_option();
1757 let opt_data: Option<&[u8]> = opt_data.map(U8VecRef::as_slice);
1758 self.get().tex_image_3d(
1759 target,
1760 level,
1761 internal_format,
1762 width,
1763 height,
1764 depth,
1765 border,
1766 format,
1767 ty,
1768 opt_data,
1769 );
1770 }
1771 pub fn copy_tex_image_2d(
1772 &self,
1773 target: GLenum,
1774 level: GLint,
1775 internal_format: GLenum,
1776 x: GLint,
1777 y: GLint,
1778 width: GLsizei,
1779 height: GLsizei,
1780 border: GLint,
1781 ) {
1782 self.get()
1783 .copy_tex_image_2d(target, level, internal_format, x, y, width, height, border);
1784 }
1785 pub fn copy_tex_sub_image_2d(
1786 &self,
1787 target: GLenum,
1788 level: GLint,
1789 xoffset: GLint,
1790 yoffset: GLint,
1791 x: GLint,
1792 y: GLint,
1793 width: GLsizei,
1794 height: GLsizei,
1795 ) {
1796 self.get()
1797 .copy_tex_sub_image_2d(target, level, xoffset, yoffset, x, y, width, height);
1798 }
1799 pub fn copy_tex_sub_image_3d(
1800 &self,
1801 target: GLenum,
1802 level: GLint,
1803 xoffset: GLint,
1804 yoffset: GLint,
1805 zoffset: GLint,
1806 x: GLint,
1807 y: GLint,
1808 width: GLsizei,
1809 height: GLsizei,
1810 ) {
1811 self.get().copy_tex_sub_image_3d(
1812 target, level, xoffset, yoffset, zoffset, x, y, width, height,
1813 );
1814 }
1815 pub fn tex_sub_image_2d(
1816 &self,
1817 target: GLenum,
1818 level: GLint,
1819 xoffset: GLint,
1820 yoffset: GLint,
1821 width: GLsizei,
1822 height: GLsizei,
1823 format: GLenum,
1824 ty: GLenum,
1825 data: U8VecRef,
1826 ) {
1827 self.get().tex_sub_image_2d(
1828 target,
1829 level,
1830 xoffset,
1831 yoffset,
1832 width,
1833 height,
1834 format,
1835 ty,
1836 data.as_slice(),
1837 );
1838 }
1839 pub fn tex_sub_image_2d_pbo(
1840 &self,
1841 target: GLenum,
1842 level: GLint,
1843 xoffset: GLint,
1844 yoffset: GLint,
1845 width: GLsizei,
1846 height: GLsizei,
1847 format: GLenum,
1848 ty: GLenum,
1849 offset: usize,
1850 ) {
1851 self.get().tex_sub_image_2d_pbo(
1852 target, level, xoffset, yoffset, width, height, format, ty, offset,
1853 );
1854 }
1855 pub fn tex_sub_image_3d(
1856 &self,
1857 target: GLenum,
1858 level: GLint,
1859 xoffset: GLint,
1860 yoffset: GLint,
1861 zoffset: GLint,
1862 width: GLsizei,
1863 height: GLsizei,
1864 depth: GLsizei,
1865 format: GLenum,
1866 ty: GLenum,
1867 data: U8VecRef,
1868 ) {
1869 self.get().tex_sub_image_3d(
1870 target,
1871 level,
1872 xoffset,
1873 yoffset,
1874 zoffset,
1875 width,
1876 height,
1877 depth,
1878 format,
1879 ty,
1880 data.as_slice(),
1881 );
1882 }
1883 pub fn tex_sub_image_3d_pbo(
1884 &self,
1885 target: GLenum,
1886 level: GLint,
1887 xoffset: GLint,
1888 yoffset: GLint,
1889 zoffset: GLint,
1890 width: GLsizei,
1891 height: GLsizei,
1892 depth: GLsizei,
1893 format: GLenum,
1894 ty: GLenum,
1895 offset: usize,
1896 ) {
1897 self.get().tex_sub_image_3d_pbo(
1898 target, level, xoffset, yoffset, zoffset, width, height, depth, format, ty, offset,
1899 );
1900 }
1901 pub fn tex_storage_2d(
1902 &self,
1903 target: GLenum,
1904 levels: GLint,
1905 internal_format: GLenum,
1906 width: GLsizei,
1907 height: GLsizei,
1908 ) {
1909 self.get()
1910 .tex_storage_2d(target, levels, internal_format, width, height);
1911 }
1912 pub fn tex_storage_3d(
1913 &self,
1914 target: GLenum,
1915 levels: GLint,
1916 internal_format: GLenum,
1917 width: GLsizei,
1918 height: GLsizei,
1919 depth: GLsizei,
1920 ) {
1921 self.get()
1922 .tex_storage_3d(target, levels, internal_format, width, height, depth);
1923 }
1924 pub fn get_tex_image_into_buffer(
1925 &self,
1926 target: GLenum,
1927 level: GLint,
1928 format: GLenum,
1929 ty: GLenum,
1930 mut output: U8VecRefMut,
1931 ) {
1932 self.get()
1933 .get_tex_image_into_buffer(target, level, format, ty, output.as_mut_slice());
1934 }
1935 pub fn copy_image_sub_data(
1936 &self,
1937 src_name: GLuint,
1938 src_target: GLenum,
1939 src_level: GLint,
1940 src_x: GLint,
1941 src_y: GLint,
1942 src_z: GLint,
1943 dst_name: GLuint,
1944 dst_target: GLenum,
1945 dst_level: GLint,
1946 dst_x: GLint,
1947 dst_y: GLint,
1948 dst_z: GLint,
1949 src_width: GLsizei,
1950 src_height: GLsizei,
1951 src_depth: GLsizei,
1952 ) {
1953 unsafe {
1954 self.get().copy_image_sub_data(
1955 src_name, src_target, src_level, src_x, src_y, src_z, dst_name, dst_target,
1956 dst_level, dst_x, dst_y, dst_z, src_width, src_height, src_depth,
1957 );
1958 }
1959 }
1960 pub fn invalidate_framebuffer(&self, target: GLenum, attachments: GLenumVecRef) {
1961 self.get()
1962 .invalidate_framebuffer(target, attachments.as_slice());
1963 }
1964 pub fn invalidate_sub_framebuffer(
1965 &self,
1966 target: GLenum,
1967 attachments: GLenumVecRef,
1968 xoffset: GLint,
1969 yoffset: GLint,
1970 width: GLsizei,
1971 height: GLsizei,
1972 ) {
1973 self.get().invalidate_sub_framebuffer(
1974 target,
1975 attachments.as_slice(),
1976 xoffset,
1977 yoffset,
1978 width,
1979 height,
1980 );
1981 }
1982 pub fn get_integer_v(&self, name: GLenum, mut result: GLintVecRefMut) {
1983 unsafe { self.get().get_integer_v(name, result.as_mut_slice()) }
1984 }
1985 pub fn get_integer_64v(&self, name: GLenum, mut result: GLint64VecRefMut) {
1986 unsafe { self.get().get_integer_64v(name, result.as_mut_slice()) }
1987 }
1988 pub fn get_integer_iv(&self, name: GLenum, index: GLuint, mut result: GLintVecRefMut) {
1989 unsafe {
1990 self.get()
1991 .get_integer_iv(name, index, result.as_mut_slice());
1992 }
1993 }
1994 pub fn get_integer_64iv(&self, name: GLenum, index: GLuint, mut result: GLint64VecRefMut) {
1995 unsafe {
1996 self.get()
1997 .get_integer_64iv(name, index, result.as_mut_slice());
1998 }
1999 }
2000 pub fn get_boolean_v(&self, name: GLenum, mut result: GLbooleanVecRefMut) {
2001 unsafe { self.get().get_boolean_v(name, result.as_mut_slice()) }
2002 }
2003 pub fn get_float_v(&self, name: GLenum, mut result: GLfloatVecRefMut) {
2004 unsafe { self.get().get_float_v(name, result.as_mut_slice()) }
2005 }
2006 #[must_use] pub fn get_framebuffer_attachment_parameter_iv(
2007 &self,
2008 target: GLenum,
2009 attachment: GLenum,
2010 pname: GLenum,
2011 ) -> GLint {
2012 self.get()
2013 .get_framebuffer_attachment_parameter_iv(target, attachment, pname)
2014 }
2015 #[must_use] pub fn get_renderbuffer_parameter_iv(&self, target: GLenum, pname: GLenum) -> GLint {
2016 self.get().get_renderbuffer_parameter_iv(target, pname)
2017 }
2018 #[must_use] pub fn get_tex_parameter_iv(&self, target: GLenum, name: GLenum) -> GLint {
2019 self.get().get_tex_parameter_iv(target, name)
2020 }
2021 #[must_use] pub fn get_tex_parameter_fv(&self, target: GLenum, name: GLenum) -> GLfloat {
2022 self.get().get_tex_parameter_fv(target, name)
2023 }
2024 pub fn tex_parameter_i(&self, target: GLenum, pname: GLenum, param: GLint) {
2025 self.get().tex_parameter_i(target, pname, param);
2026 }
2027 pub fn tex_parameter_f(&self, target: GLenum, pname: GLenum, param: GLfloat) {
2028 self.get().tex_parameter_f(target, pname, param);
2029 }
2030 pub fn framebuffer_texture_2d(
2031 &self,
2032 target: GLenum,
2033 attachment: GLenum,
2034 textarget: GLenum,
2035 texture: GLuint,
2036 level: GLint,
2037 ) {
2038 self.get()
2039 .framebuffer_texture_2d(target, attachment, textarget, texture, level);
2040 }
2041 pub fn framebuffer_texture_layer(
2042 &self,
2043 target: GLenum,
2044 attachment: GLenum,
2045 texture: GLuint,
2046 level: GLint,
2047 layer: GLint,
2048 ) {
2049 self.get()
2050 .framebuffer_texture_layer(target, attachment, texture, level, layer);
2051 }
2052 #[allow(clippy::similar_names)] pub fn blit_framebuffer(
2054 &self,
2055 src_x0: GLint,
2056 src_y0: GLint,
2057 src_x1: GLint,
2058 src_y1: GLint,
2059 dst_x0: GLint,
2060 dst_y0: GLint,
2061 dst_x1: GLint,
2062 dst_y1: GLint,
2063 mask: GLbitfield,
2064 filter: GLenum,
2065 ) {
2066 self.get().blit_framebuffer(
2067 src_x0, src_y0, src_x1, src_y1, dst_x0, dst_y0, dst_x1, dst_y1, mask, filter,
2068 );
2069 }
2070 pub fn vertex_attrib_4f(&self, index: GLuint, x: GLfloat, y: GLfloat, z: GLfloat, w: GLfloat) {
2071 self.get().vertex_attrib_4f(index, x, y, z, w);
2072 }
2073 pub fn vertex_attrib_pointer_f32(
2074 &self,
2075 index: GLuint,
2076 size: GLint,
2077 normalized: bool,
2078 stride: GLsizei,
2079 offset: GLuint,
2080 ) {
2081 self.get()
2082 .vertex_attrib_pointer_f32(index, size, normalized, stride, offset);
2083 }
2084 pub fn vertex_attrib_pointer(
2085 &self,
2086 index: GLuint,
2087 size: GLint,
2088 type_: GLenum,
2089 normalized: bool,
2090 stride: GLsizei,
2091 offset: GLuint,
2092 ) {
2093 self.get()
2094 .vertex_attrib_pointer(index, size, type_, normalized, stride, offset);
2095 }
2096 pub fn vertex_attrib_i_pointer(
2097 &self,
2098 index: GLuint,
2099 size: GLint,
2100 type_: GLenum,
2101 stride: GLsizei,
2102 offset: GLuint,
2103 ) {
2104 self.get()
2105 .vertex_attrib_i_pointer(index, size, type_, stride, offset);
2106 }
2107 pub fn vertex_attrib_divisor(&self, index: GLuint, divisor: GLuint) {
2108 self.get().vertex_attrib_divisor(index, divisor);
2109 }
2110 pub fn viewport(&self, x: GLint, y: GLint, width: GLsizei, height: GLsizei) {
2111 self.get().viewport(x, y, width, height);
2112 }
2113 pub fn scissor(&self, x: GLint, y: GLint, width: GLsizei, height: GLsizei) {
2114 self.get().scissor(x, y, width, height);
2115 }
2116 pub fn line_width(&self, width: GLfloat) {
2117 self.get().line_width(width);
2118 }
2119 pub fn use_program(&self, program: GLuint) {
2120 self.get().use_program(program);
2121 }
2122 pub fn validate_program(&self, program: GLuint) {
2123 self.get().validate_program(program);
2124 }
2125 pub fn draw_arrays(&self, mode: GLenum, first: GLint, count: GLsizei) {
2126 self.get().draw_arrays(mode, first, count);
2127 }
2128 pub fn draw_arrays_instanced(
2129 &self,
2130 mode: GLenum,
2131 first: GLint,
2132 count: GLsizei,
2133 primcount: GLsizei,
2134 ) {
2135 self.get()
2136 .draw_arrays_instanced(mode, first, count, primcount);
2137 }
2138 pub fn draw_elements(
2139 &self,
2140 mode: GLenum,
2141 count: GLsizei,
2142 element_type: GLenum,
2143 indices_offset: GLuint,
2144 ) {
2145 self.get()
2146 .draw_elements(mode, count, element_type, indices_offset);
2147 }
2148 pub fn draw_elements_instanced(
2149 &self,
2150 mode: GLenum,
2151 count: GLsizei,
2152 element_type: GLenum,
2153 indices_offset: GLuint,
2154 primcount: GLsizei,
2155 ) {
2156 self.get()
2157 .draw_elements_instanced(mode, count, element_type, indices_offset, primcount);
2158 }
2159 pub fn blend_color(&self, r: f32, g: f32, b: f32, a: f32) {
2160 self.get().blend_color(r, g, b, a);
2161 }
2162 pub fn blend_func(&self, sfactor: GLenum, dfactor: GLenum) {
2163 self.get().blend_func(sfactor, dfactor);
2164 }
2165 pub fn blend_func_separate(
2166 &self,
2167 src_rgb: GLenum,
2168 dest_rgb: GLenum,
2169 src_alpha: GLenum,
2170 dest_alpha: GLenum,
2171 ) {
2172 self.get()
2173 .blend_func_separate(src_rgb, dest_rgb, src_alpha, dest_alpha);
2174 }
2175 pub fn blend_equation(&self, mode: GLenum) {
2176 self.get().blend_equation(mode);
2177 }
2178 pub fn blend_equation_separate(&self, mode_rgb: GLenum, mode_alpha: GLenum) {
2179 self.get().blend_equation_separate(mode_rgb, mode_alpha);
2180 }
2181 #[allow(clippy::fn_params_excessive_bools)]
2184 pub fn color_mask(&self, r: bool, g: bool, b: bool, a: bool) {
2185 self.get().color_mask(r, g, b, a);
2186 }
2187 pub fn cull_face(&self, mode: GLenum) {
2188 self.get().cull_face(mode);
2189 }
2190 pub fn front_face(&self, mode: GLenum) {
2191 self.get().front_face(mode);
2192 }
2193 pub fn enable(&self, cap: GLenum) {
2194 self.get().enable(cap);
2195 }
2196 pub fn disable(&self, cap: GLenum) {
2197 self.get().disable(cap);
2198 }
2199 pub fn hint(&self, param_name: GLenum, param_val: GLenum) {
2200 self.get().hint(param_name, param_val);
2201 }
2202 #[must_use] pub fn is_enabled(&self, cap: GLenum) -> GLboolean {
2203 self.get().is_enabled(cap)
2204 }
2205 #[must_use] pub fn is_shader(&self, shader: GLuint) -> GLboolean {
2206 self.get().is_shader(shader)
2207 }
2208 #[must_use] pub fn is_texture(&self, texture: GLenum) -> GLboolean {
2209 self.get().is_texture(texture)
2210 }
2211 #[must_use] pub fn is_framebuffer(&self, framebuffer: GLenum) -> GLboolean {
2212 self.get().is_framebuffer(framebuffer)
2213 }
2214 #[must_use] pub fn is_renderbuffer(&self, renderbuffer: GLenum) -> GLboolean {
2215 self.get().is_renderbuffer(renderbuffer)
2216 }
2217 #[must_use] pub fn check_frame_buffer_status(&self, target: GLenum) -> GLenum {
2218 self.get().check_frame_buffer_status(target)
2219 }
2220 pub fn enable_vertex_attrib_array(&self, index: GLuint) {
2221 self.get().enable_vertex_attrib_array(index);
2222 }
2223 pub fn disable_vertex_attrib_array(&self, index: GLuint) {
2224 self.get().disable_vertex_attrib_array(index);
2225 }
2226 pub fn uniform_1f(&self, location: GLint, v0: GLfloat) {
2227 self.get().uniform_1f(location, v0);
2228 }
2229 pub fn uniform_1fv(&self, location: GLint, values: F32VecRef) {
2230 self.get().uniform_1fv(location, values.as_slice());
2231 }
2232 pub fn uniform_1i(&self, location: GLint, v0: GLint) {
2233 self.get().uniform_1i(location, v0);
2234 }
2235 pub fn uniform_1iv(&self, location: GLint, values: I32VecRef) {
2236 self.get().uniform_1iv(location, values.as_slice());
2237 }
2238 pub fn uniform_1ui(&self, location: GLint, v0: GLuint) {
2239 self.get().uniform_1ui(location, v0);
2240 }
2241 pub fn uniform_2f(&self, location: GLint, v0: GLfloat, v1: GLfloat) {
2242 self.get().uniform_2f(location, v0, v1);
2243 }
2244 pub fn uniform_2fv(&self, location: GLint, values: F32VecRef) {
2245 self.get().uniform_2fv(location, values.as_slice());
2246 }
2247 pub fn uniform_2i(&self, location: GLint, v0: GLint, v1: GLint) {
2248 self.get().uniform_2i(location, v0, v1);
2249 }
2250 pub fn uniform_2iv(&self, location: GLint, values: I32VecRef) {
2251 self.get().uniform_2iv(location, values.as_slice());
2252 }
2253 pub fn uniform_2ui(&self, location: GLint, v0: GLuint, v1: GLuint) {
2254 self.get().uniform_2ui(location, v0, v1);
2255 }
2256 pub fn uniform_3f(&self, location: GLint, v0: GLfloat, v1: GLfloat, v2: GLfloat) {
2257 self.get().uniform_3f(location, v0, v1, v2);
2258 }
2259 pub fn uniform_3fv(&self, location: GLint, values: F32VecRef) {
2260 self.get().uniform_3fv(location, values.as_slice());
2261 }
2262 pub fn uniform_3i(&self, location: GLint, v0: GLint, v1: GLint, v2: GLint) {
2263 self.get().uniform_3i(location, v0, v1, v2);
2264 }
2265 pub fn uniform_3iv(&self, location: GLint, values: I32VecRef) {
2266 self.get().uniform_3iv(location, values.as_slice());
2267 }
2268 pub fn uniform_3ui(&self, location: GLint, v0: GLuint, v1: GLuint, v2: GLuint) {
2269 self.get().uniform_3ui(location, v0, v1, v2);
2270 }
2271 pub fn uniform_4f(&self, location: GLint, x: GLfloat, y: GLfloat, z: GLfloat, w: GLfloat) {
2272 self.get().uniform_4f(location, x, y, z, w);
2273 }
2274 pub fn uniform_4i(&self, location: GLint, x: GLint, y: GLint, z: GLint, w: GLint) {
2275 self.get().uniform_4i(location, x, y, z, w);
2276 }
2277 pub fn uniform_4iv(&self, location: GLint, values: I32VecRef) {
2278 self.get().uniform_4iv(location, values.as_slice());
2279 }
2280 pub fn uniform_4ui(&self, location: GLint, x: GLuint, y: GLuint, z: GLuint, w: GLuint) {
2281 self.get().uniform_4ui(location, x, y, z, w);
2282 }
2283 pub fn uniform_4fv(&self, location: GLint, values: F32VecRef) {
2284 self.get().uniform_4fv(location, values.as_slice());
2285 }
2286 pub fn uniform_matrix_2fv(&self, location: GLint, transpose: bool, value: F32VecRef) {
2287 self.get()
2288 .uniform_matrix_2fv(location, transpose, value.as_slice());
2289 }
2290 pub fn uniform_matrix_3fv(&self, location: GLint, transpose: bool, value: F32VecRef) {
2291 self.get()
2292 .uniform_matrix_3fv(location, transpose, value.as_slice());
2293 }
2294 pub fn uniform_matrix_4fv(&self, location: GLint, transpose: bool, value: F32VecRef) {
2295 self.get()
2296 .uniform_matrix_4fv(location, transpose, value.as_slice());
2297 }
2298 pub fn depth_mask(&self, flag: bool) {
2299 self.get().depth_mask(flag);
2300 }
2301 pub fn depth_range(&self, near: f64, far: f64) {
2302 self.get().depth_range(near, far);
2303 }
2304 #[must_use] pub fn get_active_attrib(&self, program: GLuint, index: GLuint) -> GetActiveAttribReturn {
2305 let r = self.get().get_active_attrib(program, index);
2306 GetActiveAttribReturn {
2307 _0: r.0,
2308 _1: r.1,
2309 _2: r.2.into(),
2310 }
2311 }
2312 #[must_use] pub fn get_active_uniform(&self, program: GLuint, index: GLuint) -> GetActiveUniformReturn {
2313 let r = self.get().get_active_uniform(program, index);
2314 GetActiveUniformReturn {
2315 _0: r.0,
2316 _1: r.1,
2317 _2: r.2.into(),
2318 }
2319 }
2320 #[must_use] pub fn get_active_uniforms_iv(
2321 &self,
2322 program: GLuint,
2323 indices: GLuintVec,
2324 pname: GLenum,
2325 ) -> GLintVec {
2326 self.get()
2327 .get_active_uniforms_iv(program, indices.into_library_owned_vec(), pname)
2328 .into()
2329 }
2330 #[must_use] pub fn get_active_uniform_block_i(
2331 &self,
2332 program: GLuint,
2333 index: GLuint,
2334 pname: GLenum,
2335 ) -> GLint {
2336 self.get().get_active_uniform_block_i(program, index, pname)
2337 }
2338 #[must_use] pub fn get_active_uniform_block_iv(
2339 &self,
2340 program: GLuint,
2341 index: GLuint,
2342 pname: GLenum,
2343 ) -> GLintVec {
2344 self.get()
2345 .get_active_uniform_block_iv(program, index, pname)
2346 .into()
2347 }
2348 #[must_use] pub fn get_active_uniform_block_name(&self, program: GLuint, index: GLuint) -> AzString {
2349 self.get()
2350 .get_active_uniform_block_name(program, index)
2351 .into()
2352 }
2353 #[must_use] pub fn get_attrib_location(&self, program: GLuint, name: &str) -> c_int {
2354 self.get().get_attrib_location(program, name)
2355 }
2356 #[must_use] pub fn get_frag_data_location(&self, program: GLuint, name: &str) -> c_int {
2357 self.get().get_frag_data_location(program, name)
2358 }
2359 #[must_use] pub fn get_uniform_location(&self, program: GLuint, name: &str) -> c_int {
2360 self.get().get_uniform_location(program, name)
2361 }
2362 #[must_use] pub fn get_program_info_log(&self, program: GLuint) -> AzString {
2363 self.get().get_program_info_log(program).into()
2364 }
2365 pub fn get_program_iv(&self, program: GLuint, pname: GLenum, mut result: GLintVecRefMut) {
2366 unsafe {
2367 self.get()
2368 .get_program_iv(program, pname, result.as_mut_slice());
2369 }
2370 }
2371 #[must_use] pub fn get_program_binary(&self, program: GLuint) -> GetProgramBinaryReturn {
2372 let r = self.get().get_program_binary(program);
2373 GetProgramBinaryReturn {
2374 _0: r.0.into(),
2375 _1: r.1,
2376 }
2377 }
2378 pub fn program_binary(&self, program: GLuint, format: GLenum, binary: U8VecRef) {
2379 self.get()
2380 .program_binary(program, format, binary.as_slice());
2381 }
2382 pub fn program_parameter_i(&self, program: GLuint, pname: GLenum, value: GLint) {
2383 self.get().program_parameter_i(program, pname, value);
2384 }
2385 pub fn get_vertex_attrib_iv(&self, index: GLuint, pname: GLenum, mut result: GLintVecRefMut) {
2386 unsafe {
2387 self.get()
2388 .get_vertex_attrib_iv(index, pname, result.as_mut_slice());
2389 }
2390 }
2391 pub fn get_vertex_attrib_fv(&self, index: GLuint, pname: GLenum, mut result: GLfloatVecRefMut) {
2392 unsafe {
2393 self.get()
2394 .get_vertex_attrib_fv(index, pname, result.as_mut_slice());
2395 }
2396 }
2397 #[must_use] pub fn get_vertex_attrib_pointer_v(&self, index: GLuint, pname: GLenum) -> GLsizeiptr {
2398 self.get().get_vertex_attrib_pointer_v(index, pname)
2399 }
2400 #[must_use] pub fn get_buffer_parameter_iv(&self, target: GLuint, pname: GLenum) -> GLint {
2401 self.get().get_buffer_parameter_iv(target, pname)
2402 }
2403 #[must_use] pub fn get_shader_info_log(&self, shader: GLuint) -> AzString {
2404 self.get().get_shader_info_log(shader).into()
2405 }
2406 #[must_use] pub fn get_string(&self, which: GLenum) -> AzString {
2407 self.get().get_string(which).into()
2408 }
2409 #[must_use] pub fn get_string_i(&self, which: GLenum, index: GLuint) -> AzString {
2410 self.get().get_string_i(which, index).into()
2411 }
2412 pub fn get_shader_iv(&self, shader: GLuint, pname: GLenum, mut result: GLintVecRefMut) {
2413 unsafe {
2414 self.get()
2415 .get_shader_iv(shader, pname, result.as_mut_slice());
2416 }
2417 }
2418 #[must_use] pub fn get_shader_precision_format(
2419 &self,
2420 shader_type: GLuint,
2421 precision_type: GLuint,
2422 ) -> GlShaderPrecisionFormatReturn {
2423 let r = self
2424 .get()
2425 .get_shader_precision_format(shader_type, precision_type);
2426 GlShaderPrecisionFormatReturn {
2427 _0: r.0,
2428 _1: r.1,
2429 _2: r.2,
2430 }
2431 }
2432 pub fn compile_shader(&self, shader: GLuint) {
2433 self.get().compile_shader(shader);
2434 }
2435 #[must_use] pub fn create_program(&self) -> GLuint {
2436 self.get().create_program()
2437 }
2438 pub fn delete_program(&self, program: GLuint) {
2439 self.get().delete_program(program);
2440 }
2441 #[must_use] pub fn create_shader(&self, shader_type: GLenum) -> GLuint {
2442 self.get().create_shader(shader_type)
2443 }
2444 pub fn delete_shader(&self, shader: GLuint) {
2445 self.get().delete_shader(shader);
2446 }
2447 pub fn detach_shader(&self, program: GLuint, shader: GLuint) {
2448 self.get().detach_shader(program, shader);
2449 }
2450 pub fn link_program(&self, program: GLuint) {
2451 self.get().link_program(program);
2452 }
2453 pub fn clear_color(&self, r: f32, g: f32, b: f32, a: f32) {
2454 self.get().clear_color(r, g, b, a);
2455 }
2456 pub fn clear(&self, buffer_mask: GLbitfield) {
2457 self.get().clear(buffer_mask);
2458 }
2459 pub fn clear_depth(&self, depth: f64) {
2460 self.get().clear_depth(depth);
2461 }
2462 pub fn clear_stencil(&self, s: GLint) {
2463 self.get().clear_stencil(s);
2464 }
2465 pub fn flush(&self) {
2466 self.get().flush();
2467 }
2468 pub fn finish(&self) {
2469 self.get().finish();
2470 }
2471 #[must_use] pub fn get_error(&self) -> GLenum {
2472 self.get().get_error()
2473 }
2474 pub fn stencil_mask(&self, mask: GLuint) {
2475 self.get().stencil_mask(mask);
2476 }
2477 pub fn stencil_mask_separate(&self, face: GLenum, mask: GLuint) {
2478 self.get().stencil_mask_separate(face, mask);
2479 }
2480 pub fn stencil_func(&self, func: GLenum, ref_: GLint, mask: GLuint) {
2481 self.get().stencil_func(func, ref_, mask);
2482 }
2483 pub fn stencil_func_separate(&self, face: GLenum, func: GLenum, ref_: GLint, mask: GLuint) {
2484 self.get().stencil_func_separate(face, func, ref_, mask);
2485 }
2486 pub fn stencil_op(&self, sfail: GLenum, dpfail: GLenum, dppass: GLenum) {
2487 self.get().stencil_op(sfail, dpfail, dppass);
2488 }
2489 pub fn stencil_op_separate(&self, face: GLenum, sfail: GLenum, dpfail: GLenum, dppass: GLenum) {
2490 self.get().stencil_op_separate(face, sfail, dpfail, dppass);
2491 }
2492 pub fn egl_image_target_texture2d_oes(&self, target: GLenum, image: GlVoidPtrConst) {
2493 self.get()
2494 .egl_image_target_texture2d_oes(target, image.ptr as *const c_void);
2495 }
2496 pub fn generate_mipmap(&self, target: GLenum) {
2497 self.get().generate_mipmap(target);
2498 }
2499 pub fn insert_event_marker_ext(&self, message: &str) {
2500 self.get().insert_event_marker_ext(message);
2501 }
2502 pub fn push_group_marker_ext(&self, message: &str) {
2503 self.get().push_group_marker_ext(message);
2504 }
2505 pub fn pop_group_marker_ext(&self) {
2506 self.get().pop_group_marker_ext();
2507 }
2508 pub fn debug_message_insert_khr(
2509 &self,
2510 source: GLenum,
2511 type_: GLenum,
2512 id: GLuint,
2513 severity: GLenum,
2514 message: &str,
2515 ) {
2516 self.get()
2517 .debug_message_insert_khr(source, type_, id, severity, message);
2518 }
2519 pub fn push_debug_group_khr(&self, source: GLenum, id: GLuint, message: &str) {
2520 self.get()
2521 .push_debug_group_khr(source, id, message);
2522 }
2523 pub fn pop_debug_group_khr(&self) {
2524 self.get().pop_debug_group_khr();
2525 }
2526 #[must_use] pub fn fence_sync(&self, condition: GLenum, flags: GLbitfield) -> GLsyncPtr {
2527 GLsyncPtr::new(self.get().fence_sync(condition, flags))
2528 }
2529 #[must_use] pub fn client_wait_sync(&self, sync: GLsyncPtr, flags: GLbitfield, timeout: GLuint64) -> u32 {
2530 self.get().client_wait_sync(sync.get(), flags, timeout)
2531 }
2532 pub fn wait_sync(&self, sync: GLsyncPtr, flags: GLbitfield, timeout: GLuint64) {
2533 self.get().wait_sync(sync.get(), flags, timeout);
2534 }
2535 pub fn delete_sync(&self, sync: GLsyncPtr) {
2536 self.get().delete_sync(sync.get());
2537 }
2538 pub fn texture_range_apple(&self, target: GLenum, data: U8VecRef) {
2539 self.get().texture_range_apple(target, data.as_slice());
2540 }
2541 #[must_use] pub fn gen_fences_apple(&self, n: GLsizei) -> GLuintVec {
2542 self.get().gen_fences_apple(n).into()
2543 }
2544 pub fn delete_fences_apple(&self, fences: GLuintVecRef) {
2545 self.get().delete_fences_apple(fences.as_slice());
2546 }
2547 pub fn set_fence_apple(&self, fence: GLuint) {
2548 self.get().set_fence_apple(fence);
2549 }
2550 pub fn finish_fence_apple(&self, fence: GLuint) {
2551 self.get().finish_fence_apple(fence);
2552 }
2553 pub fn test_fence_apple(&self, fence: GLuint) {
2554 self.get().test_fence_apple(fence);
2555 }
2556 #[must_use] pub fn test_object_apple(&self, object: GLenum, name: GLuint) -> GLboolean {
2557 self.get().test_object_apple(object, name)
2558 }
2559 pub fn finish_object_apple(&self, object: GLenum, name: GLuint) {
2560 self.get().finish_object_apple(object, name);
2561 }
2562 #[must_use] pub fn get_frag_data_index(&self, program: GLuint, name: &str) -> GLint {
2563 self.get().get_frag_data_index(program, name)
2564 }
2565 pub fn blend_barrier_khr(&self) {
2566 self.get().blend_barrier_khr();
2567 }
2568 pub fn bind_frag_data_location_indexed(
2569 &self,
2570 program: GLuint,
2571 color_number: GLuint,
2572 index: GLuint,
2573 name: &str,
2574 ) {
2575 self.get()
2576 .bind_frag_data_location_indexed(program, color_number, index, name);
2577 }
2578 #[must_use] pub fn get_debug_messages(&self) -> DebugMessageVec {
2579 let dmv: Vec<DebugMessage> = self
2580 .get()
2581 .get_debug_messages()
2582 .into_iter()
2583 .map(|d| DebugMessage {
2584 message: d.message.into(),
2585 source: d.source,
2586 ty: d.ty,
2587 id: d.id,
2588 severity: d.severity,
2589 })
2590 .collect();
2591 dmv.into()
2592 }
2593 pub fn provoking_vertex_angle(&self, mode: GLenum) {
2594 self.get().provoking_vertex_angle(mode);
2595 }
2596 #[must_use] pub fn gen_vertex_arrays_apple(&self, n: GLsizei) -> GLuintVec {
2597 self.get().gen_vertex_arrays_apple(n).into()
2598 }
2599 pub fn bind_vertex_array_apple(&self, vao: GLuint) {
2600 self.get().bind_vertex_array_apple(vao);
2601 }
2602 pub fn delete_vertex_arrays_apple(&self, vertex_arrays: GLuintVecRef) {
2603 self.get()
2604 .delete_vertex_arrays_apple(vertex_arrays.as_slice());
2605 }
2606 pub fn copy_texture_chromium(
2607 &self,
2608 source_id: GLuint,
2609 source_level: GLint,
2610 dest_target: GLenum,
2611 dest_id: GLuint,
2612 dest_level: GLint,
2613 internal_format: GLint,
2614 dest_type: GLenum,
2615 unpack_flip_y: GLboolean,
2616 unpack_premultiply_alpha: GLboolean,
2617 unpack_unmultiply_alpha: GLboolean,
2618 ) {
2619 self.get().copy_texture_chromium(
2620 source_id,
2621 source_level,
2622 dest_target,
2623 dest_id,
2624 dest_level,
2625 internal_format,
2626 dest_type,
2627 unpack_flip_y,
2628 unpack_premultiply_alpha,
2629 unpack_unmultiply_alpha,
2630 );
2631 }
2632 pub fn copy_sub_texture_chromium(
2633 &self,
2634 source_id: GLuint,
2635 source_level: GLint,
2636 dest_target: GLenum,
2637 dest_id: GLuint,
2638 dest_level: GLint,
2639 x_offset: GLint,
2640 y_offset: GLint,
2641 x: GLint,
2642 y: GLint,
2643 width: GLsizei,
2644 height: GLsizei,
2645 unpack_flip_y: GLboolean,
2646 unpack_premultiply_alpha: GLboolean,
2647 unpack_unmultiply_alpha: GLboolean,
2648 ) {
2649 self.get().copy_sub_texture_chromium(
2650 source_id,
2651 source_level,
2652 dest_target,
2653 dest_id,
2654 dest_level,
2655 x_offset,
2656 y_offset,
2657 x,
2658 y,
2659 width,
2660 height,
2661 unpack_flip_y,
2662 unpack_premultiply_alpha,
2663 unpack_unmultiply_alpha,
2664 );
2665 }
2666 pub fn egl_image_target_renderbuffer_storage_oes(&self, target: u32, image: GlVoidPtrConst) {
2667 self.get().egl_image_target_renderbuffer_storage_oes(
2668 target,
2669 image.ptr as *const c_void,
2670 );
2671 }
2672 pub fn copy_texture_3d_angle(
2673 &self,
2674 source_id: GLuint,
2675 source_level: GLint,
2676 dest_target: GLenum,
2677 dest_id: GLuint,
2678 dest_level: GLint,
2679 internal_format: GLint,
2680 dest_type: GLenum,
2681 unpack_flip_y: GLboolean,
2682 unpack_premultiply_alpha: GLboolean,
2683 unpack_unmultiply_alpha: GLboolean,
2684 ) {
2685 self.get().copy_texture_3d_angle(
2686 source_id,
2687 source_level,
2688 dest_target,
2689 dest_id,
2690 dest_level,
2691 internal_format,
2692 dest_type,
2693 unpack_flip_y,
2694 unpack_premultiply_alpha,
2695 unpack_unmultiply_alpha,
2696 );
2697 }
2698 pub fn copy_sub_texture_3d_angle(
2699 &self,
2700 source_id: GLuint,
2701 source_level: GLint,
2702 dest_target: GLenum,
2703 dest_id: GLuint,
2704 dest_level: GLint,
2705 x_offset: GLint,
2706 y_offset: GLint,
2707 z_offset: GLint,
2708 x: GLint,
2709 y: GLint,
2710 z: GLint,
2711 width: GLsizei,
2712 height: GLsizei,
2713 depth: GLsizei,
2714 unpack_flip_y: GLboolean,
2715 unpack_premultiply_alpha: GLboolean,
2716 unpack_unmultiply_alpha: GLboolean,
2717 ) {
2718 self.get().copy_sub_texture_3d_angle(
2719 source_id,
2720 source_level,
2721 dest_target,
2722 dest_id,
2723 dest_level,
2724 x_offset,
2725 y_offset,
2726 z_offset,
2727 x,
2728 y,
2729 z,
2730 width,
2731 height,
2732 depth,
2733 unpack_flip_y,
2734 unpack_premultiply_alpha,
2735 unpack_unmultiply_alpha,
2736 );
2737 }
2738 pub fn buffer_storage(
2739 &self,
2740 target: GLenum,
2741 size: GLsizeiptr,
2742 data: GlVoidPtrConst,
2743 flags: GLbitfield,
2744 ) {
2745 self.get().buffer_storage(target, size, data.ptr, flags);
2746 }
2747 pub fn flush_mapped_buffer_range(&self, target: GLenum, offset: GLintptr, length: GLsizeiptr) {
2748 self.get().flush_mapped_buffer_range(target, offset, length);
2749 }
2750}
2751
2752impl PartialEq for GlContextPtr {
2753 fn eq(&self, rhs: &Self) -> bool {
2754 self.as_usize().eq(&rhs.as_usize())
2755 }
2756}
2757
2758impl Eq for GlContextPtr {}
2759
2760impl PartialOrd for GlContextPtr {
2761 fn partial_cmp(&self, rhs: &Self) -> Option<core::cmp::Ordering> {
2762 self.as_usize().partial_cmp(&rhs.as_usize())
2763 }
2764}
2765
2766impl Ord for GlContextPtr {
2767 fn cmp(&self, rhs: &Self) -> core::cmp::Ordering {
2768 self.as_usize().cmp(&rhs.as_usize())
2769 }
2770}
2771
2772#[allow(clippy::struct_field_names)]
2778struct GlStateSave {
2779 current_multisample: [u8; 1],
2780 current_index_buffer: [i32; 1],
2781 current_vertex_buffer: [i32; 1],
2782 current_vertex_array_object: [i32; 1],
2783 current_program: [i32; 1],
2784 current_framebuffers: [i32; 1],
2785 current_renderbuffers: [i32; 1],
2786 current_texture_2d: [i32; 1],
2787}
2788
2789impl GlStateSave {
2790 fn save(gl_context: &GlContextPtr) -> Self {
2791 let mut s = Self {
2792 current_multisample: [0],
2793 current_index_buffer: [0],
2794 current_vertex_buffer: [0],
2795 current_vertex_array_object: [0],
2796 current_program: [0],
2797 current_framebuffers: [0],
2798 current_renderbuffers: [0],
2799 current_texture_2d: [0],
2800 };
2801
2802 gl_context.get_boolean_v(gl::MULTISAMPLE, (&mut s.current_multisample[..]).into());
2803 gl_context.get_integer_v(gl::ARRAY_BUFFER_BINDING, (&mut s.current_vertex_buffer[..]).into());
2804 gl_context.get_integer_v(gl::ELEMENT_ARRAY_BUFFER_BINDING, (&mut s.current_index_buffer[..]).into());
2805 gl_context.get_integer_v(gl::CURRENT_PROGRAM, (&mut s.current_program[..]).into());
2806 gl_context.get_integer_v(gl::VERTEX_ARRAY_BINDING, (&mut s.current_vertex_array_object[..]).into());
2807 gl_context.get_integer_v(gl::RENDERBUFFER, (&mut s.current_renderbuffers[..]).into());
2808 gl_context.get_integer_v(gl::FRAMEBUFFER, (&mut s.current_framebuffers[..]).into());
2809 gl_context.get_integer_v(gl::TEXTURE_2D, (&mut s.current_texture_2d[..]).into());
2810
2811 s
2812 }
2813
2814 #[allow(clippy::cast_sign_loss)]
2816 fn restore(&self, gl_context: &GlContextPtr) {
2817 if u32::from(self.current_multisample[0]) == gl::TRUE {
2818 gl_context.enable(gl::MULTISAMPLE);
2819 }
2820 gl_context.bind_framebuffer(gl::FRAMEBUFFER, self.current_framebuffers[0] as u32);
2821 gl_context.bind_texture(gl::TEXTURE_2D, self.current_texture_2d[0] as u32);
2822 gl_context.bind_buffer(gl::RENDERBUFFER, self.current_renderbuffers[0] as u32);
2823 gl_context.bind_vertex_array(self.current_vertex_array_object[0] as u32);
2824 gl_context.bind_buffer(gl::ELEMENT_ARRAY_BUFFER, self.current_index_buffer[0] as u32);
2825 gl_context.bind_buffer(gl::ARRAY_BUFFER, self.current_vertex_buffer[0] as u32);
2826 gl_context.use_program(self.current_program[0] as u32);
2827 }
2828}
2829
2830struct FboRboGuard<'a> {
2836 gl_context: &'a GlContextPtr,
2837 framebuffer_id: GLuint,
2838 renderbuffer_id: GLuint,
2839}
2840
2841impl Drop for FboRboGuard<'_> {
2842 fn drop(&mut self) {
2843 if self.framebuffer_id != 0 {
2844 self.gl_context
2845 .delete_framebuffers((&[self.framebuffer_id])[..].into());
2846 }
2847 if self.renderbuffer_id != 0 {
2848 self.gl_context
2849 .delete_renderbuffers((&[self.renderbuffer_id])[..].into());
2850 }
2851 }
2852}
2853
2854#[repr(C)]
2856pub struct Texture {
2857 pub gl_context: GlContextPtr,
2860 pub texture_id: GLuint,
2862 pub refcount: *const AtomicUsize,
2864 pub size: PhysicalSizeU32,
2866 pub format: RawImageFormat,
2868 pub background_color: ColorU,
2870 pub flags: TextureFlags,
2872 pub run_destructor: bool,
2873}
2874
2875impl Clone for Texture {
2876 #[allow(clippy::cast_sign_loss)] fn clone(&self) -> Self {
2878 unsafe {
2879 (*self.refcount).fetch_add(1, AtomicOrdering::SeqCst);
2880 }
2881 Self {
2882 gl_context: self.gl_context.clone(),
2883 texture_id: self.texture_id,
2884 refcount: self.refcount,
2885 size: self.size,
2886 format: self.format,
2887 background_color: self.background_color,
2888 flags: self.flags,
2889 run_destructor: true,
2890 }
2891 }
2892}
2893
2894impl_option!(
2895 Texture,
2896 OptionTexture,
2897 copy = false,
2898 [Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
2899);
2900
2901impl Texture {
2902 #[must_use] pub fn create(
2903 texture_id: GLuint,
2904 flags: TextureFlags,
2905 size: PhysicalSizeU32,
2906 background_color: ColorU,
2907 gl_context: GlContextPtr,
2908 format: RawImageFormat,
2909 ) -> Self {
2910 Self {
2911 texture_id,
2912 flags,
2913 size,
2914 background_color,
2915 gl_context,
2916 format,
2917 refcount: Box::into_raw(Box::new(AtomicUsize::new(1))),
2918 run_destructor: true,
2919 }
2920 }
2921
2922 #[allow(clippy::cast_possible_wrap)]
2926 #[allow(clippy::cast_sign_loss)] #[must_use] pub fn allocate_rgba8(
2928 gl_context: GlContextPtr,
2929 size: PhysicalSizeU32,
2930 background: ColorU,
2931 ) -> Self {
2932 let textures = gl_context.gen_textures(1);
2933 let texture_id = textures.as_ref()[0];
2934
2935 let mut current_texture_2d = [0_i32];
2936 gl_context.get_integer_v(gl::TEXTURE_2D, (&mut current_texture_2d[..]).into());
2937
2938 gl_context.bind_texture(gl::TEXTURE_2D, texture_id);
2939 gl_context.tex_image_2d(
2940 gl::TEXTURE_2D,
2941 0,
2942 gl::RGBA as i32,
2943 size.width as i32,
2944 size.height as i32,
2945 0,
2946 gl::RGBA,
2947 gl::UNSIGNED_BYTE,
2948 None.into(),
2949 );
2950 gl_context.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as i32);
2951 gl_context.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::NEAREST as i32);
2952 gl_context.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as i32);
2953 gl_context.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as i32);
2954 gl_context.bind_texture(gl::TEXTURE_2D, current_texture_2d[0] as u32);
2955
2956 Self::create(
2957 texture_id,
2958 TextureFlags {
2959 is_opaque: false,
2960 is_video_texture: false,
2961 },
2962 size,
2963 background,
2964 gl_context,
2965 RawImageFormat::BGRA8,
2967 )
2968 }
2969
2970 #[allow(clippy::cast_possible_wrap)]
2976 pub fn clear(&mut self) {
2977 let saved = GlStateSave::save(&self.gl_context);
2978
2979 let framebuffers = self.gl_context.gen_framebuffers(1);
2980 let framebuffer_id = *framebuffers.get(0).unwrap();
2981 let mut fbo_rbo_guard = FboRboGuard {
2984 gl_context: &self.gl_context,
2985 framebuffer_id,
2986 renderbuffer_id: 0,
2987 };
2988 self.gl_context
2989 .bind_framebuffer(gl::FRAMEBUFFER, framebuffer_id);
2990
2991 let depthbuffers = self.gl_context.gen_renderbuffers(1);
2992 let depthbuffer_id = *depthbuffers.get(0).unwrap();
2993 fbo_rbo_guard.renderbuffer_id = depthbuffer_id;
2994
2995 self.gl_context
2996 .bind_texture(gl::TEXTURE_2D, self.texture_id);
2997 self.gl_context.tex_image_2d(
2998 gl::TEXTURE_2D,
2999 0,
3000 gl::RGBA as i32, self.size.width as i32,
3002 self.size.height as i32,
3003 0,
3004 gl::RGBA, gl::UNSIGNED_BYTE,
3006 None.into(),
3007 );
3008 self.gl_context
3009 .tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as i32);
3010 self.gl_context
3011 .tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::NEAREST as i32);
3012 self.gl_context.tex_parameter_i(
3013 gl::TEXTURE_2D,
3014 gl::TEXTURE_WRAP_S,
3015 gl::CLAMP_TO_EDGE as i32,
3016 );
3017 self.gl_context.tex_parameter_i(
3018 gl::TEXTURE_2D,
3019 gl::TEXTURE_WRAP_T,
3020 gl::CLAMP_TO_EDGE as i32,
3021 );
3022
3023 self.gl_context
3024 .bind_renderbuffer(gl::RENDERBUFFER, depthbuffer_id);
3025 self.gl_context.renderbuffer_storage(
3026 gl::RENDERBUFFER,
3027 gl::DEPTH_COMPONENT,
3028 self.size.width as i32,
3029 self.size.height as i32,
3030 );
3031 self.gl_context.framebuffer_renderbuffer(
3032 gl::FRAMEBUFFER,
3033 gl::DEPTH_ATTACHMENT,
3034 gl::RENDERBUFFER,
3035 depthbuffer_id,
3036 );
3037
3038 self.gl_context.framebuffer_texture_2d(
3039 gl::FRAMEBUFFER,
3040 gl::COLOR_ATTACHMENT0,
3041 gl::TEXTURE_2D,
3042 self.texture_id,
3043 0,
3044 );
3045 self.gl_context
3046 .draw_buffers([gl::COLOR_ATTACHMENT0][..].into());
3047
3048 let clear_color: ColorF = self.background_color.into();
3049 self.gl_context
3050 .clear_color(clear_color.r, clear_color.g, clear_color.b, clear_color.a);
3051 self.gl_context.clear_depth(0.0);
3052 self.gl_context
3053 .clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT);
3054
3055 saved.restore(&self.gl_context);
3059 drop(fbo_rbo_guard);
3060 }
3061
3062 #[must_use] pub fn get_descriptor(&self) -> ImageDescriptor {
3063 ImageDescriptor {
3064 format: self.format,
3065 width: self.size.width as usize,
3066 height: self.size.height as usize,
3067 stride: None.into(),
3068 offset: 0,
3069 flags: ImageDescriptorFlags {
3070 is_opaque: self.flags.is_opaque,
3071 allow_mipmaps: false,
3073 },
3074 }
3075 }
3076
3077 pub fn draw_tesselated_svg_gpu_node(
3079 &mut self,
3080 node: &TessellatedGPUSvgNode,
3081 size: PhysicalSizeU32,
3082 color: ColorU,
3083 transforms: StyleTransformVec,
3084 ) -> bool {
3085 node.draw(self, size, color, transforms)
3086 }
3087
3088 pub fn draw_tesselated_colored_svg_gpu_node(
3090 &mut self,
3091 node: &crate::svg::TessellatedColoredGPUSvgNode,
3092 size: PhysicalSizeU32,
3093 transforms: StyleTransformVec,
3094 ) -> bool {
3095 node.draw(self, size, transforms)
3096 }
3097}
3098
3099#[derive(Debug, Default, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
3100#[repr(C)]
3101pub struct TextureFlags {
3102 pub is_opaque: bool,
3104 pub is_video_texture: bool,
3106}
3107
3108impl ::core::fmt::Display for Texture {
3109 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
3110 write!(
3111 f,
3112 "Texture {{ id: {}, {}x{} }}",
3113 self.texture_id, self.size.width, self.size.height
3114 )
3115 }
3116}
3117
3118macro_rules! impl_traits_for_gl_object {
3119 ($struct_name:ident, $gl_id_field:ident) => {
3120 impl ::core::fmt::Debug for $struct_name {
3121 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
3122 write!(f, "{}", self)
3123 }
3124 }
3125
3126 impl Hash for $struct_name {
3127 fn hash<H: Hasher>(&self, state: &mut H) {
3128 self.$gl_id_field.hash(state);
3129 }
3130 }
3131
3132 impl PartialEq for $struct_name {
3133 fn eq(&self, other: &$struct_name) -> bool {
3134 self.$gl_id_field == other.$gl_id_field
3135 }
3136 }
3137
3138 impl Eq for $struct_name {}
3139
3140 impl PartialOrd for $struct_name {
3141 fn partial_cmp(&self, other: &Self) -> Option<::core::cmp::Ordering> {
3142 Some((self.$gl_id_field).cmp(&(other.$gl_id_field)))
3143 }
3144 }
3145
3146 impl Ord for $struct_name {
3147 fn cmp(&self, other: &Self) -> ::core::cmp::Ordering {
3148 (self.$gl_id_field).cmp(&(other.$gl_id_field))
3149 }
3150 }
3151 };
3152}
3153
3154impl Texture {
3155 pub fn paint_dot(&mut self, cx: f32, cy: f32, brush: Brush) {
3160 self.paint_stroke(cx, cy, cx, cy, brush);
3161 }
3162
3163 #[allow(clippy::suboptimal_flops)] #[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap, clippy::cast_precision_loss, clippy::cast_sign_loss)] #[allow(clippy::many_single_char_names)] pub fn paint_stroke(&mut self, x0: f32, y0: f32, x1: f32, y1: f32, brush: Brush) {
3170 let gl = self.gl_context.clone();
3171 let prog = gl.get_brush_shader();
3172 let (tw, th) = (self.size.width as f32, self.size.height as f32);
3173 #[allow(clippy::neg_cmp_op_on_partial_ord)]
3175 if prog == 0 || self.texture_id == 0 || !(brush.radius > 0.0) || tw <= 0.0 || th <= 0.0 {
3176 return;
3177 }
3178
3179 let fbo = gl.gen_framebuffers(1).get(0).copied().unwrap_or(0);
3180 let vbo = gl.gen_buffers(1).get(0).copied().unwrap_or(0);
3181 if fbo == 0 || vbo == 0 {
3182 if fbo != 0 {
3183 gl.delete_framebuffers((&[fbo][..]).into());
3184 }
3185 if vbo != 0 {
3186 gl.delete_buffers((&[vbo][..]).into());
3187 }
3188 return;
3189 }
3190
3191 gl.bind_framebuffer(gl::FRAMEBUFFER, fbo);
3192 gl.framebuffer_texture_2d(
3193 gl::FRAMEBUFFER,
3194 gl::COLOR_ATTACHMENT0,
3195 gl::TEXTURE_2D,
3196 self.texture_id,
3197 0,
3198 );
3199 gl.viewport(0, 0, self.size.width as i32, self.size.height as i32);
3200 gl.enable(gl::BLEND);
3201 gl.blend_func(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
3202 gl.use_program(prog);
3203
3204 let a = (f32::from(brush.color.a) / 255.0) * brush.flow.clamp(0.0, 1.0);
3205 gl.uniform_4f(
3206 gl.get_uniform_location(prog, "uColor"),
3207 f32::from(brush.color.r) / 255.0,
3208 f32::from(brush.color.g) / 255.0,
3209 f32::from(brush.color.b) / 255.0,
3210 a,
3211 );
3212 gl.uniform_1f(gl.get_uniform_location(prog, "uHardness"), brush.hardness);
3213
3214 gl.bind_buffer(gl::ARRAY_BUFFER, vbo);
3215 gl.enable_vertex_attrib_array(0);
3216 gl.enable_vertex_attrib_array(1);
3217 gl.vertex_attrib_pointer_f32(0, 2, false, 16, 0);
3218 gl.vertex_attrib_pointer_f32(1, 2, false, 16, 8);
3219
3220 let dx = x1 - x0;
3221 let dy = y1 - y0;
3222 let len = dx.hypot(dy);
3223 let step = (brush.radius * brush.spacing.max(0.01)).max(0.5);
3224 let n = ((len / step).floor() as i32).max(0);
3225 let r = brush.radius;
3226 for i in 0..=n {
3227 let t = if n == 0 { 1.0 } else { i as f32 / n as f32 };
3228 let px = x0 + dx * t;
3229 let py = y0 + dy * t;
3230 let nx = |x: f32| (x / tw) * 2.0 - 1.0;
3232 let ny = |y: f32| 1.0 - (y / th) * 2.0;
3233 let (l, rr, tp, bt) = (nx(px - r), nx(px + r), ny(py - r), ny(py + r));
3234 let verts: [f32; 16] = [
3236 l, tp, -1.0, -1.0, l, bt, -1.0, 1.0, rr, tp, 1.0, -1.0, rr, bt, 1.0, 1.0,
3237 ];
3238 gl.buffer_data_untyped(
3239 gl::ARRAY_BUFFER,
3240 (verts.len() * size_of::<f32>()) as isize,
3241 GlVoidPtrConst {
3242 ptr: verts.as_ptr() as *const GLvoid,
3243 run_destructor: false,
3244 },
3245 gl::STREAM_DRAW,
3246 );
3247 gl.draw_arrays(gl::TRIANGLE_STRIP, 0, 4);
3248 }
3249
3250 gl.disable_vertex_attrib_array(0);
3251 gl.disable_vertex_attrib_array(1);
3252 gl.bind_buffer(gl::ARRAY_BUFFER, 0);
3253 gl.disable(gl::BLEND);
3254 gl.bind_framebuffer(gl::FRAMEBUFFER, 0);
3255 gl.delete_buffers((&[vbo][..]).into());
3256 gl.delete_framebuffers((&[fbo][..]).into());
3257 }
3258
3259 #[allow(clippy::cast_possible_wrap, clippy::cast_sign_loss)] #[must_use] pub fn copy_to_raw_image(&self) -> RawImage {
3263 let gl = self.gl_context.clone();
3264 let (w, h) = (self.size.width as i32, self.size.height as i32);
3265 if self.texture_id == 0 || w <= 0 || h <= 0 {
3266 return RawImage::null_image();
3267 }
3268 let fbo = gl.gen_framebuffers(1).get(0).copied().unwrap_or(0);
3269 if fbo == 0 {
3270 return RawImage::null_image();
3271 }
3272 gl.bind_framebuffer(gl::FRAMEBUFFER, fbo);
3273 gl.framebuffer_texture_2d(
3274 gl::FRAMEBUFFER,
3275 gl::COLOR_ATTACHMENT0,
3276 gl::TEXTURE_2D,
3277 self.texture_id,
3278 0,
3279 );
3280 let pixels = gl.read_pixels(0, 0, w, h, gl::RGBA, gl::UNSIGNED_BYTE);
3281 gl.bind_framebuffer(gl::FRAMEBUFFER, 0);
3282 gl.delete_framebuffers((&[fbo][..]).into());
3283
3284 let mut bytes = pixels.into_library_owned_vec();
3286 let row = (w as usize) * 4;
3287 let hh = h as usize;
3288 if row > 0 && bytes.len() >= row * hh {
3289 for y in 0..hh / 2 {
3290 let yi = y * row;
3291 let yo = (hh - 1 - y) * row;
3292 for k in 0..row {
3293 bytes.swap(yi + k, yo + k);
3294 }
3295 }
3296 }
3297 RawImage {
3298 pixels: RawImageData::U8(bytes.into()),
3299 width: w as usize,
3300 height: h as usize,
3301 premultiplied_alpha: true,
3302 data_format: RawImageFormat::RGBA8,
3303 tag: Vec::new().into(),
3304 }
3305 }
3306}
3307
3308impl_traits_for_gl_object!(Texture, texture_id);
3309
3310impl Drop for Texture {
3311 fn drop(&mut self) {
3312 if !self.run_destructor {
3318 return;
3319 }
3320 self.run_destructor = false;
3321 let copies = unsafe { (*self.refcount).fetch_sub(1, AtomicOrdering::SeqCst) };
3322 if copies == 1 {
3323 drop(unsafe { Box::from_raw(self.refcount.cast_mut()) });
3324 self.gl_context
3325 .delete_textures((&[self.texture_id])[..].into());
3326 }
3327 }
3328}
3329
3330#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
3332#[repr(C)]
3333pub struct VertexLayout {
3334 pub fields: VertexAttributeVec,
3335}
3336
3337impl_vec!(VertexAttribute, VertexAttributeVec, VertexAttributeVecDestructor, VertexAttributeVecDestructorType, VertexAttributeVecSlice, OptionVertexAttribute);
3338impl_vec_debug!(VertexAttribute, VertexAttributeVec);
3339impl_vec_partialord!(VertexAttribute, VertexAttributeVec);
3340impl_vec_ord!(VertexAttribute, VertexAttributeVec);
3341impl_vec_clone!(
3342 VertexAttribute,
3343 VertexAttributeVec,
3344 VertexAttributeVecDestructor
3345);
3346impl_vec_partialeq!(VertexAttribute, VertexAttributeVec);
3347impl_vec_eq!(VertexAttribute, VertexAttributeVec);
3348impl_vec_hash!(VertexAttribute, VertexAttributeVec);
3349
3350impl VertexLayout {
3351 #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
3355 #[allow(clippy::cast_possible_wrap)] pub fn bind(&self, gl_context: &Rc<GenericGlContext>, program_id: GLuint) {
3357 const VERTICES_ARE_NORMALIZED: bool = false;
3358
3359 let mut offset = 0;
3360
3361 let stride_between_vertices: usize =
3362 self.fields.iter().map(VertexAttribute::get_stride).sum();
3363
3364 for vertex_attribute in &self.fields {
3365 let attribute_location = vertex_attribute.layout_location.as_option().map_or_else(
3366 || gl_context.get_attrib_location(program_id, vertex_attribute.va_name.as_str()),
3367 |ll| *ll as i32,
3368 );
3369
3370 gl_context.vertex_attrib_pointer(
3371 attribute_location as u32,
3372 vertex_attribute.item_count as i32,
3373 vertex_attribute.attribute_type.get_gl_id(),
3374 VERTICES_ARE_NORMALIZED,
3375 stride_between_vertices as i32,
3376 offset as u32,
3377 );
3378 gl_context.enable_vertex_attrib_array(attribute_location as u32);
3379 offset += vertex_attribute.get_stride();
3380 }
3381 }
3382
3383 #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] #[allow(clippy::cast_possible_wrap)] pub fn unbind(&self, gl_context: &Rc<GenericGlContext>, program_id: GLuint) {
3387 for vertex_attribute in &self.fields {
3388 let attribute_location = vertex_attribute.layout_location.as_option().map_or_else(
3389 || gl_context.get_attrib_location(program_id, vertex_attribute.va_name.as_str()),
3390 |ll| *ll as i32,
3391 );
3392 gl_context.disable_vertex_attrib_array(attribute_location as u32);
3393 }
3394 }
3395}
3396
3397#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
3398#[repr(C)]
3399pub struct VertexAttribute {
3400 pub va_name: AzString,
3402 pub layout_location: OptionUsize,
3405 pub attribute_type: VertexAttributeType,
3408 pub item_count: usize,
3411}
3412
3413impl_option!(
3414 VertexAttribute,
3415 OptionVertexAttribute,
3416 copy = false,
3417 [Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
3418);
3419
3420impl VertexAttribute {
3421 #[must_use] pub const fn get_stride(&self) -> usize {
3422 self.attribute_type.get_mem_size() * self.item_count
3423 }
3424}
3425
3426#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
3427#[repr(C)]
3428pub enum VertexAttributeType {
3429 Float,
3431 Double,
3433 UnsignedByte,
3435 UnsignedShort,
3437 UnsignedInt,
3439}
3440
3441impl VertexAttributeType {
3442 #[must_use] pub const fn get_gl_id(&self) -> GLuint {
3445 use self::VertexAttributeType::{Float, Double, UnsignedByte, UnsignedShort, UnsignedInt};
3446 match self {
3447 Float => gl::FLOAT,
3448 Double => gl::DOUBLE,
3449 UnsignedByte => gl::UNSIGNED_BYTE,
3450 UnsignedShort => gl::UNSIGNED_SHORT,
3451 UnsignedInt => gl::UNSIGNED_INT,
3452 }
3453 }
3454
3455 #[must_use] pub const fn get_mem_size(&self) -> usize {
3456 use core::mem;
3457
3458 use self::VertexAttributeType::{Float, Double, UnsignedByte, UnsignedShort, UnsignedInt};
3459 match self {
3460 Float => size_of::<f32>(),
3461 Double => size_of::<f64>(),
3462 UnsignedByte => size_of::<u8>(),
3463 UnsignedShort => size_of::<u16>(),
3464 UnsignedInt => size_of::<u32>(),
3465 }
3466 }
3467}
3468
3469pub trait VertexLayoutDescription {
3470 fn get_description() -> VertexLayout;
3471}
3472
3473#[derive(Debug, PartialEq, Eq, PartialOrd)]
3474#[repr(C)]
3475pub struct VertexArrayObject {
3476 pub vertex_layout: VertexLayout,
3477 pub vao_id: GLuint,
3478 pub gl_context: GlContextPtr,
3479 pub refcount: *const AtomicUsize,
3480 pub run_destructor: bool,
3481}
3482
3483impl VertexArrayObject {
3484 #[must_use] pub fn new(vertex_layout: VertexLayout, vao_id: GLuint, gl_context: GlContextPtr) -> Self {
3485 Self {
3486 vertex_layout,
3487 vao_id,
3488 gl_context,
3489 refcount: Box::into_raw(Box::new(AtomicUsize::new(1))),
3490 run_destructor: true,
3491 }
3492 }
3493}
3494
3495impl Clone for VertexArrayObject {
3496 fn clone(&self) -> Self {
3497 unsafe { (*self.refcount).fetch_add(1, AtomicOrdering::SeqCst) };
3498 Self {
3499 vertex_layout: self.vertex_layout.clone(),
3500 vao_id: self.vao_id,
3501 gl_context: self.gl_context.clone(),
3502 refcount: self.refcount,
3503 run_destructor: true,
3504 }
3505 }
3506}
3507
3508impl Drop for VertexArrayObject {
3509 fn drop(&mut self) {
3510 if !self.run_destructor {
3513 return;
3514 }
3515 self.run_destructor = false;
3516 let copies = unsafe { (*self.refcount).fetch_sub(1, AtomicOrdering::SeqCst) };
3517 if copies == 1 {
3518 drop(unsafe { Box::from_raw(self.refcount.cast_mut()) });
3519 self.gl_context
3520 .delete_vertex_arrays((&[self.vao_id])[..].into());
3521 }
3522 }
3523}
3524
3525#[repr(C)]
3526pub struct VertexBuffer {
3527 pub vao: VertexArrayObject,
3528 pub vertex_buffer_id: GLuint,
3529 pub vertex_buffer_len: usize,
3530 pub index_buffer_id: GLuint,
3531 pub index_buffer_len: usize,
3532 pub refcount: *const AtomicUsize,
3533 pub index_buffer_format: IndexBufferFormat,
3534 pub run_destructor: bool,
3535}
3536
3537impl fmt::Display for VertexBuffer {
3538 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3539 write!(
3540 f,
3541 "VertexBuffer {{ buffer: {} (length: {}) }})",
3542 self.vertex_buffer_id, self.vertex_buffer_len
3543 )
3544 }
3545}
3546
3547impl_traits_for_gl_object!(VertexBuffer, vertex_buffer_id);
3548
3549impl Clone for VertexBuffer {
3550 fn clone(&self) -> Self {
3551 unsafe { (*self.refcount).fetch_add(1, AtomicOrdering::SeqCst) };
3552 Self {
3553 vao: self.vao.clone(),
3554 vertex_buffer_id: self.vertex_buffer_id,
3555 vertex_buffer_len: self.vertex_buffer_len,
3556 index_buffer_id: self.index_buffer_id,
3557 index_buffer_len: self.index_buffer_len,
3558 refcount: self.refcount,
3559 index_buffer_format: self.index_buffer_format,
3560 run_destructor: true,
3561 }
3562 }
3563}
3564
3565impl Drop for VertexBuffer {
3566 fn drop(&mut self) {
3567 if !self.run_destructor {
3570 return;
3571 }
3572 self.run_destructor = false;
3573 let copies = unsafe { (*self.refcount).fetch_sub(1, AtomicOrdering::SeqCst) };
3574 if copies == 1 {
3575 self.vao.vertex_layout = VertexLayout {
3576 fields: VertexAttributeVec::from_const_slice(&[]),
3577 };
3578 drop(unsafe { Box::from_raw(self.refcount.cast_mut()) });
3579 self.vao
3580 .gl_context
3581 .delete_buffers((&[self.vertex_buffer_id, self.index_buffer_id])[..].into());
3582 }
3583 }
3584}
3585
3586impl VertexBuffer {
3587 #[allow(clippy::cast_possible_wrap, clippy::cast_sign_loss)]
3594 #[allow(clippy::cast_possible_truncation)] pub fn new<T: VertexLayoutDescription>(
3596 gl_context: GlContextPtr,
3597 shader_program_id: GLuint,
3598 vertices: &[T],
3599 indices: &[u32],
3600 index_buffer_format: IndexBufferFormat,
3601 ) -> Self {
3602 use core::mem;
3603
3604 let mut current_vertex_array = [0_i32];
3606
3607 gl_context.get_integer_v(gl::VERTEX_ARRAY, (&mut current_vertex_array[..]).into());
3608
3609 let vertex_array_object = gl_context.gen_vertex_arrays(1);
3610 let vertex_array_object = vertex_array_object.get(0).unwrap();
3611
3612 let vertex_buffer_id = gl_context.gen_buffers(1);
3613 let vertex_buffer_id = vertex_buffer_id.get(0).unwrap();
3614
3615 let index_buffer_id = gl_context.gen_buffers(1);
3616 let index_buffer_id = index_buffer_id.get(0).unwrap();
3617
3618 gl_context.bind_vertex_array(*vertex_array_object);
3619
3620 gl_context.bind_buffer(gl::ARRAY_BUFFER, *vertex_buffer_id);
3622 gl_context.buffer_data_untyped(
3623 gl::ARRAY_BUFFER,
3624 size_of_val(vertices) as isize,
3625 GlVoidPtrConst {
3626 ptr: vertices.as_ptr() as *const core::ffi::c_void,
3627 run_destructor: true,
3628 },
3629 gl::STATIC_DRAW,
3630 );
3631
3632 gl_context.bind_buffer(gl::ELEMENT_ARRAY_BUFFER, *index_buffer_id);
3634 gl_context.buffer_data_untyped(
3635 gl::ELEMENT_ARRAY_BUFFER,
3636 size_of_val(indices) as isize,
3637 GlVoidPtrConst {
3638 ptr: indices.as_ptr() as *const core::ffi::c_void,
3639 run_destructor: true,
3640 },
3641 gl::STATIC_DRAW,
3642 );
3643
3644 let vertex_description = T::get_description();
3645 vertex_description.bind(&gl_context.ptr.ptr, shader_program_id);
3646
3647 gl_context.bind_vertex_array(current_vertex_array[0] as u32);
3649
3650 Self::new_raw(
3651 *vertex_buffer_id,
3652 vertices.len(),
3653 VertexArrayObject::new(vertex_description, *vertex_array_object, gl_context),
3654 *index_buffer_id,
3655 indices.len(),
3656 index_buffer_format,
3657 )
3658 }
3659
3660 #[must_use] pub fn new_raw(
3661 vertex_buffer_id: GLuint,
3662 vertex_buffer_len: usize,
3663 vao: VertexArrayObject,
3664 index_buffer_id: GLuint,
3665 index_buffer_len: usize,
3666 index_buffer_format: IndexBufferFormat,
3667 ) -> Self {
3668 Self {
3669 vertex_buffer_id,
3670 vertex_buffer_len,
3671 vao,
3672 index_buffer_id,
3673 index_buffer_len,
3674 index_buffer_format,
3675 refcount: Box::into_raw(Box::new(AtomicUsize::new(1))),
3676 run_destructor: true,
3677 }
3678 }
3679}
3680
3681#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
3682pub enum GlApiVersion {
3683 Gl { major: usize, minor: usize },
3684 GlEs { major: usize, minor: usize },
3685}
3686
3687impl GlApiVersion {
3688 #[allow(clippy::cast_sign_loss)] #[must_use] pub fn get(gl_context: &GlContextPtr) -> Self {
3691 let mut major = [0];
3692 gl_context.get_integer_v(gl::MAJOR_VERSION, (&mut major[..]).into());
3693 let mut minor = [0];
3694 gl_context.get_integer_v(gl::MINOR_VERSION, (&mut minor[..]).into());
3695
3696 let major = major[0] as usize;
3697 let minor = minor[0] as usize;
3698
3699 match gl_context.get_type() {
3700 GlType::Gl => Self::Gl { major, minor },
3701 GlType::Gles => Self::GlEs { major, minor },
3702 }
3703 }
3704}
3705
3706#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
3707#[repr(C)]
3708pub enum IndexBufferFormat {
3709 Points,
3710 Lines,
3711 LineStrip,
3712 Triangles,
3713 TriangleStrip,
3714 TriangleFan,
3715}
3716
3717impl IndexBufferFormat {
3718 #[must_use] pub const fn get_gl_id(&self) -> GLuint {
3720 use self::IndexBufferFormat::{Points, Lines, LineStrip, Triangles, TriangleStrip, TriangleFan};
3721 match self {
3722 Points => gl::POINTS,
3723 Lines => gl::LINES,
3724 LineStrip => gl::LINE_STRIP,
3725 Triangles => gl::TRIANGLES,
3726 TriangleStrip => gl::TRIANGLE_STRIP,
3727 TriangleFan => gl::TRIANGLE_FAN,
3728 }
3729 }
3730}
3731
3732#[derive(Debug, Clone, PartialEq, PartialOrd)]
3733#[repr(C)]
3734pub struct Uniform {
3735 pub uniform_name: AzString,
3736 pub uniform_type: UniformType,
3737}
3738
3739impl Uniform {
3740 pub fn create<S: Into<AzString>>(name: S, uniform_type: UniformType) -> Self {
3741 Self {
3742 uniform_name: name.into(),
3743 uniform_type,
3744 }
3745 }
3746}
3747
3748#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
3749#[repr(C, u8)]
3750pub enum UniformType {
3751 Float(f32),
3752 FloatVec2([f32; 2]),
3753 FloatVec3([f32; 3]),
3754 FloatVec4([f32; 4]),
3755 Int(i32),
3756 IntVec2([i32; 2]),
3757 IntVec3([i32; 3]),
3758 IntVec4([i32; 4]),
3759 UnsignedInt(u32),
3760 UnsignedIntVec2([u32; 2]),
3761 UnsignedIntVec3([u32; 3]),
3762 UnsignedIntVec4([u32; 4]),
3763 Matrix2 {
3764 transpose: bool,
3765 matrix: [f32; 2 * 2],
3766 },
3767 Matrix3 {
3768 transpose: bool,
3769 matrix: [f32; 3 * 3],
3770 },
3771 Matrix4 {
3772 transpose: bool,
3773 matrix: [f32; 4 * 4],
3774 },
3775}
3776
3777impl UniformType {
3778 pub fn set(self, gl_context: &Rc<GenericGlContext>, location: GLint) {
3780 use self::UniformType::{Float, FloatVec2, FloatVec3, FloatVec4, Int, IntVec2, IntVec3, IntVec4, UnsignedInt, UnsignedIntVec2, UnsignedIntVec3, UnsignedIntVec4, Matrix2, Matrix3, Matrix4};
3781 match self {
3782 Float(r) => gl_context.uniform_1f(location, r),
3783 FloatVec2([r, g]) => gl_context.uniform_2f(location, r, g),
3784 FloatVec3([r, g, b]) => gl_context.uniform_3f(location, r, g, b),
3785 FloatVec4([r, g, b, a]) => gl_context.uniform_4f(location, r, g, b, a),
3786 Int(r) => gl_context.uniform_1i(location, r),
3787 IntVec2([r, g]) => gl_context.uniform_2i(location, r, g),
3788 IntVec3([r, g, b]) => gl_context.uniform_3i(location, r, g, b),
3789 IntVec4([r, g, b, a]) => gl_context.uniform_4i(location, r, g, b, a),
3790 UnsignedInt(r) => gl_context.uniform_1ui(location, r),
3791 UnsignedIntVec2([r, g]) => gl_context.uniform_2ui(location, r, g),
3792 UnsignedIntVec3([r, g, b]) => gl_context.uniform_3ui(location, r, g, b),
3793 UnsignedIntVec4([r, g, b, a]) => gl_context.uniform_4ui(location, r, g, b, a),
3794 Matrix2 { transpose, matrix } => {
3795 gl_context.uniform_matrix_2fv(location, transpose, &matrix[..]);
3796 }
3797 Matrix3 { transpose, matrix } => {
3798 gl_context.uniform_matrix_3fv(location, transpose, &matrix[..]);
3799 }
3800 Matrix4 { transpose, matrix } => {
3801 gl_context.uniform_matrix_4fv(location, transpose, &matrix[..]);
3802 }
3803 }
3804 }
3805}
3806
3807#[repr(C)]
3808pub struct GlShader {
3809 pub program_id: GLuint,
3810 pub gl_context: GlContextPtr,
3811 pub run_destructor: bool,
3815}
3816
3817impl ::core::fmt::Display for GlShader {
3818 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
3819 write!(f, "GlShader {{ program_id: {} }}", self.program_id)
3820 }
3821}
3822
3823impl_traits_for_gl_object!(GlShader, program_id);
3824
3825impl Drop for GlShader {
3826 fn drop(&mut self) {
3827 if !self.run_destructor {
3830 return;
3831 }
3832 self.run_destructor = false;
3833 self.gl_context.delete_program(self.program_id);
3834 }
3835}
3836
3837#[repr(C)]
3838#[derive(Clone)]
3839pub struct VertexShaderCompileError {
3840 pub error_id: i32,
3841 pub info_log: AzString,
3842}
3843
3844impl_traits_for_gl_object!(VertexShaderCompileError, error_id);
3845
3846impl ::core::fmt::Display for VertexShaderCompileError {
3847 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
3848 write!(f, "E{}: {}", self.error_id, self.info_log)
3849 }
3850}
3851
3852#[repr(C)]
3853#[derive(Clone)]
3854pub struct FragmentShaderCompileError {
3855 pub error_id: i32,
3856 pub info_log: AzString,
3857}
3858
3859impl_traits_for_gl_object!(FragmentShaderCompileError, error_id);
3860
3861impl ::core::fmt::Display for FragmentShaderCompileError {
3862 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
3863 write!(f, "E{}: {}", self.error_id, self.info_log)
3864 }
3865}
3866
3867#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
3868pub enum GlShaderCompileError {
3869 Vertex(VertexShaderCompileError),
3870 Fragment(FragmentShaderCompileError),
3871}
3872
3873impl ::core::fmt::Display for GlShaderCompileError {
3874 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
3875 use self::GlShaderCompileError::{Vertex, Fragment};
3876 match self {
3877 Vertex(vert_err) => write!(f, "Failed to compile vertex shader: {vert_err}"),
3878 Fragment(frag_err) => write!(f, "Failed to compile fragment shader: {frag_err}"),
3879 }
3880 }
3881}
3882
3883impl ::core::fmt::Debug for GlShaderCompileError {
3884 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
3885 write!(f, "{self}")
3886 }
3887}
3888
3889#[repr(C)]
3890#[derive(Clone)]
3891pub struct GlShaderLinkError {
3892 pub error_id: i32,
3893 pub info_log: AzString,
3894}
3895
3896impl_traits_for_gl_object!(GlShaderLinkError, error_id);
3897
3898impl ::core::fmt::Display for GlShaderLinkError {
3899 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
3900 write!(f, "E{}: {}", self.error_id, self.info_log)
3901 }
3902}
3903
3904#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
3905pub enum GlShaderCreateError {
3906 Compile(GlShaderCompileError),
3907 Link(GlShaderLinkError),
3908 NoShaderCompiler,
3909}
3910
3911impl ::core::fmt::Display for GlShaderCreateError {
3912 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
3913 use self::GlShaderCreateError::{Compile, Link, NoShaderCompiler};
3914 match self {
3915 Compile(compile_err) => write!(f, "Shader compile error: {compile_err}"),
3916 Link(link_err) => write!(f, "Shader linking error: {link_err}"),
3917 NoShaderCompiler => {
3918 write!(f, "OpenGL implementation doesn't include a shader compiler")
3919 }
3920 }
3921 }
3922}
3923
3924impl ::core::fmt::Debug for GlShaderCreateError {
3925 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
3926 write!(f, "{self}")
3927 }
3928}
3929
3930impl GlShader {
3931 #[allow(clippy::cast_possible_truncation)] pub fn new(
3941 gl_context: &GlContextPtr,
3942 vertex_shader: &str,
3943 fragment_shader: &str,
3944 ) -> Result<Self, GlShaderCreateError> {
3945 let mut shader_compiler_supported = [gl::FALSE as u8];
3947 gl_context.get_boolean_v(
3948 gl::SHADER_COMPILER,
3949 (&mut shader_compiler_supported[..]).into(),
3950 );
3951 if u32::from(shader_compiler_supported[0]) == gl::FALSE {
3952 return Err(GlShaderCreateError::NoShaderCompiler);
3954 }
3955
3956 let vertex_shader_object = gl_context.create_shader(gl::VERTEX_SHADER);
3959 gl_context.shader_source(
3960 vertex_shader_object,
3961 vec![AzString::from(vertex_shader.to_string())].into(),
3962 );
3963 gl_context.compile_shader(vertex_shader_object);
3964
3965 if let Some(error_id) = get_gl_shader_error(gl_context, vertex_shader_object) {
3966 let info_log = gl_context.get_shader_info_log(vertex_shader_object);
3967 gl_context.delete_shader(vertex_shader_object);
3968 return Err(GlShaderCreateError::Compile(GlShaderCompileError::Vertex(
3969 VertexShaderCompileError {
3970 error_id,
3971 info_log,
3972 },
3973 )));
3974 }
3975
3976 let fragment_shader_object = gl_context.create_shader(gl::FRAGMENT_SHADER);
3979 gl_context.shader_source(
3980 fragment_shader_object,
3981 vec![AzString::from(fragment_shader.to_string())].into(),
3982 );
3983 gl_context.compile_shader(fragment_shader_object);
3984
3985 if let Some(error_id) = get_gl_shader_error(gl_context, fragment_shader_object) {
3986 let info_log = gl_context.get_shader_info_log(fragment_shader_object);
3987 gl_context.delete_shader(vertex_shader_object);
3988 gl_context.delete_shader(fragment_shader_object);
3989 return Err(GlShaderCreateError::Compile(
3990 GlShaderCompileError::Fragment(FragmentShaderCompileError {
3991 error_id,
3992 info_log,
3993 }),
3994 ));
3995 }
3996
3997 let program_id = gl_context.create_program();
4000 gl_context.attach_shader(program_id, vertex_shader_object);
4001 gl_context.attach_shader(program_id, fragment_shader_object);
4002 gl_context.link_program(program_id);
4003
4004 if let Some(error_id) = get_gl_program_error(gl_context, program_id) {
4005 let info_log = gl_context.get_program_info_log(program_id);
4006 gl_context.delete_shader(vertex_shader_object);
4007 gl_context.delete_shader(fragment_shader_object);
4008 gl_context.delete_program(program_id);
4009 return Err(GlShaderCreateError::Link(GlShaderLinkError {
4010 error_id,
4011 info_log,
4012 }));
4013 }
4014
4015 gl_context.delete_shader(vertex_shader_object);
4016 gl_context.delete_shader(fragment_shader_object);
4017
4018 Ok(Self {
4019 program_id,
4020 gl_context: gl_context.clone(),
4021 run_destructor: true,
4022 })
4023 }
4024
4025 #[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)] #[allow(clippy::too_many_lines)] pub fn draw(
4033 shader_program_id: GLuint,
4035 texture: &mut Texture,
4037 buffers: &[(&VertexBuffer, &[Uniform])],
4039 ) {
4040 use alloc::collections::btree_map::BTreeMap;
4041
4042 const INDEX_TYPE: GLuint = gl::UNSIGNED_INT;
4043
4044 let texture_size = texture.size;
4045
4046 let gl_context = &texture.gl_context;
4047
4048 let saved = GlStateSave::save(gl_context);
4049
4050 let mut current_blend_enabled = [0_u8];
4052 let mut current_primitive_restart_enabled = [0_u8];
4053 gl_context.get_boolean_v(gl::BLEND, (&mut current_blend_enabled[..]).into());
4054 gl_context.get_boolean_v(
4055 gl::PRIMITIVE_RESTART,
4056 (&mut current_primitive_restart_enabled[..]).into(),
4057 );
4058
4059 let framebuffers = gl_context.gen_framebuffers(1);
4061 let framebuffer_id = *framebuffers.get(0).unwrap();
4062 let mut fbo_rbo_guard = FboRboGuard {
4066 gl_context,
4067 framebuffer_id,
4068 renderbuffer_id: 0,
4069 };
4070 gl_context.bind_framebuffer(gl::FRAMEBUFFER, framebuffer_id);
4071
4072 let depthbuffers = gl_context.gen_renderbuffers(1);
4073 let depthbuffer_id = *depthbuffers.get(0).unwrap();
4074 fbo_rbo_guard.renderbuffer_id = depthbuffer_id;
4075
4076 gl_context.bind_texture(gl::TEXTURE_2D, texture.texture_id);
4077 gl_context.tex_image_2d(
4078 gl::TEXTURE_2D,
4079 0,
4080 gl::RGBA as i32, texture_size.width as i32,
4082 texture_size.height as i32,
4083 0,
4084 gl::RGBA, gl::UNSIGNED_BYTE,
4086 None.into(),
4087 );
4088 gl_context.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as i32);
4089 gl_context.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::NEAREST as i32);
4090 gl_context.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as i32);
4091 gl_context.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as i32);
4092
4093 gl_context.bind_renderbuffer(gl::RENDERBUFFER, depthbuffer_id);
4094 gl_context.renderbuffer_storage(
4095 gl::RENDERBUFFER,
4096 gl::DEPTH_COMPONENT,
4097 texture_size.width as i32,
4098 texture_size.height as i32,
4099 );
4100 gl_context.framebuffer_renderbuffer(
4101 gl::FRAMEBUFFER,
4102 gl::DEPTH_ATTACHMENT,
4103 gl::RENDERBUFFER,
4104 depthbuffer_id,
4105 );
4106
4107 gl_context.framebuffer_texture_2d(
4108 gl::FRAMEBUFFER,
4109 gl::COLOR_ATTACHMENT0,
4110 gl::TEXTURE_2D,
4111 texture.texture_id,
4112 0,
4113 );
4114 gl_context.draw_buffers([gl::COLOR_ATTACHMENT0][..].into());
4115
4116 #[cfg(feature = "std")]
4117 {
4118 let fb_check = gl_context.check_frame_buffer_status(gl::FRAMEBUFFER);
4119 match fb_check {
4120 gl::FRAMEBUFFER_COMPLETE => {}
4121 gl::FRAMEBUFFER_UNDEFINED => {
4122 println!("GL_FRAMEBUFFER_UNDEFINED");
4123 }
4124 gl::FRAMEBUFFER_INCOMPLETE_ATTACHMENT => {
4125 println!("GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT");
4126 }
4127 gl::FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT => {
4128 println!("GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT");
4129 }
4130 gl::FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER => {
4131 println!("GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER");
4132 }
4133 gl::FRAMEBUFFER_INCOMPLETE_READ_BUFFER => {
4134 println!("GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER");
4135 }
4136 gl::FRAMEBUFFER_UNSUPPORTED => {
4137 println!("GL_FRAMEBUFFER_UNSUPPORTED");
4138 }
4139 gl::FRAMEBUFFER_INCOMPLETE_MULTISAMPLE => {
4140 println!("GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE");
4141 }
4142 gl::FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS => {
4143 println!("GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS");
4144 }
4145 o => {
4146 println!("glFramebufferStatus returned unknown return code: {o}");
4147 }
4148 }
4149 }
4150
4151 gl_context.viewport(0, 0, texture_size.width as i32, texture_size.height as i32);
4152 gl_context.enable(gl::BLEND);
4153 gl_context.enable(gl::PRIMITIVE_RESTART);
4156 unsafe {
4157 let gl = gl_context.get();
4158 if !gl.glPrimitiveRestartIndex.is_null() {
4159 let func: extern "system" fn(u32) =
4160 core::mem::transmute(gl.glPrimitiveRestartIndex);
4161 func(GL_RESTART_INDEX); }
4163 }
4164 gl_context.disable(gl::MULTISAMPLE);
4165 gl_context.blend_func(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA); gl_context.use_program(shader_program_id);
4167
4168 let mut uniform_locations: BTreeMap<AzString, i32> = BTreeMap::new();
4170 let mut max_uniform_len = 0;
4171 for (_, uniforms) in buffers {
4172 for uniform in *uniforms {
4173 if !uniform_locations.contains_key(&uniform.uniform_name) {
4174 uniform_locations.insert(
4175 uniform.uniform_name.clone(),
4176 gl_context.get_uniform_location(
4177 shader_program_id,
4178 uniform.uniform_name.as_str(),
4179 ),
4180 );
4181 }
4182 }
4183 max_uniform_len = max_uniform_len.max(uniforms.len());
4184 }
4185 let mut current_uniforms = vec![None; max_uniform_len];
4186
4187 for (vertex_index_buffer, uniforms) in buffers {
4192 gl_context.bind_vertex_array(vertex_index_buffer.vao.vao_id);
4193 gl_context.bind_buffer(gl::ARRAY_BUFFER, vertex_index_buffer.vertex_buffer_id);
4194 gl_context.bind_buffer(
4195 gl::ELEMENT_ARRAY_BUFFER,
4196 vertex_index_buffer.index_buffer_id,
4197 );
4198
4199 for (uniform_index, uniform) in uniforms.iter().enumerate() {
4201 if current_uniforms[uniform_index] != Some(uniform.uniform_type) {
4202 let uniform_location = uniform_locations[&uniform.uniform_name];
4203 uniform.uniform_type.set(gl_context.get(), uniform_location);
4204 current_uniforms[uniform_index] = Some(uniform.uniform_type);
4205 }
4206 }
4207
4208 gl_context.draw_elements(
4209 vertex_index_buffer.index_buffer_format.get_gl_id(),
4210 vertex_index_buffer.index_buffer_len as i32,
4211 INDEX_TYPE,
4212 0,
4213 );
4214 }
4215
4216 if u32::from(current_blend_enabled[0]) == gl::FALSE {
4218 gl_context.disable(gl::BLEND);
4219 }
4220 if u32::from(current_primitive_restart_enabled[0]) == gl::FALSE {
4221 gl_context.disable(gl::PRIMITIVE_RESTART);
4222 }
4223
4224 saved.restore(gl_context);
4230 drop(fbo_rbo_guard);
4231
4232 texture.format = RawImageFormat::RGBA8;
4233 texture.flags = TextureFlags {
4234 is_opaque: false,
4235 is_video_texture: false,
4236 };
4237 }
4238}
4239
4240#[allow(clippy::cast_possible_wrap)] fn get_gl_shader_error(context: &GlContextPtr, shader_object: GLuint) -> Option<i32> {
4242 let mut err = [0];
4243 context.get_shader_iv(shader_object, gl::COMPILE_STATUS, (&mut err[..]).into());
4244 let err_code = err[0];
4245 if err_code == gl::TRUE as i32 {
4246 None
4247 } else {
4248 Some(err_code)
4249 }
4250}
4251
4252#[allow(clippy::cast_possible_wrap)] fn get_gl_program_error(context: &GlContextPtr, shader_object: GLuint) -> Option<i32> {
4254 let mut err = [0];
4255 context.get_program_iv(shader_object, gl::LINK_STATUS, (&mut err[..]).into());
4256 let err_code = err[0];
4257 if err_code == gl::TRUE as i32 {
4258 None
4259 } else {
4260 Some(err_code)
4261 }
4262}
4263
4264#[cfg(test)]
4265mod audit_tests {
4266 use super::*;
4267
4268 #[test]
4272 fn refstr_null_and_empty_is_empty_str() {
4273 let null = Refstr {
4274 ptr: core::ptr::null(),
4275 len: 0,
4276 };
4277 assert_eq!(null.as_str(), "");
4278
4279 let null_lenful = Refstr {
4281 ptr: core::ptr::null(),
4282 len: 5,
4283 };
4284 assert_eq!(null_lenful.as_str(), "");
4285
4286 let s = "hello";
4287 let good: Refstr = s.into();
4288 assert_eq!(good.as_str(), "hello");
4289 }
4290
4291 #[test]
4292 fn refstr_vec_ref_null_is_empty_slice() {
4293 let null = RefstrVecRef {
4294 ptr: core::ptr::null(),
4295 len: 3,
4296 };
4297 assert!(null.as_slice().is_empty());
4298 }
4299
4300 #[test]
4301 fn u8_vec_ref_null_is_empty_slice() {
4302 let null = U8VecRef {
4303 ptr: core::ptr::null(),
4304 len: 8,
4305 };
4306 assert!(null.as_slice().is_empty());
4307
4308 let data = [1u8, 2, 3];
4309 let good: U8VecRef = (&data[..]).into();
4310 assert_eq!(good.as_slice(), &[1, 2, 3]);
4311 }
4312}
4313
4314#[cfg(test)]
4315#[allow(clippy::float_cmp)] mod autotest_generated {
4317 use super::*;
4318
4319 fn null_gl() -> Rc<GenericGlContext> {
4331 Rc::new(unsafe { core::mem::zeroed::<GenericGlContext>() })
4334 }
4335
4336 fn null_ctx() -> GlContextPtr {
4337 GlContextPtr::new(RendererType::Software, null_gl())
4338 }
4339
4340 fn test_texture(texture_id: GLuint, width: u32, height: u32) -> Texture {
4341 Texture::create(
4342 texture_id,
4343 TextureFlags {
4344 is_opaque: false,
4345 is_video_texture: false,
4346 },
4347 PhysicalSizeU32 { width, height },
4348 ColorU {
4349 r: 1,
4350 g: 2,
4351 b: 3,
4352 a: 4,
4353 },
4354 null_ctx(),
4355 RawImageFormat::RGBA8,
4356 )
4357 }
4358
4359 const ALL_ATTRIB_TYPES: [VertexAttributeType; 5] = [
4360 VertexAttributeType::Float,
4361 VertexAttributeType::Double,
4362 VertexAttributeType::UnsignedByte,
4363 VertexAttributeType::UnsignedShort,
4364 VertexAttributeType::UnsignedInt,
4365 ];
4366
4367 const ALL_INDEX_FORMATS: [IndexBufferFormat; 6] = [
4368 IndexBufferFormat::Points,
4369 IndexBufferFormat::Lines,
4370 IndexBufferFormat::LineStrip,
4371 IndexBufferFormat::Triangles,
4372 IndexBufferFormat::TriangleStrip,
4373 IndexBufferFormat::TriangleFan,
4374 ];
4375
4376 #[test]
4382 fn refstr_roundtrips_unicode_multibyte_and_combining_marks() {
4383 for s in [
4386 "\u{1F600}",
4387 "日本語のテキスト",
4388 "e\u{0301}\u{0328}combining",
4389 "\u{0}nul\u{0}embedded\u{0}",
4390 "\u{FFFD}\u{200B}zero-width",
4391 ] {
4392 let r: Refstr = s.into();
4393 assert_eq!(r.as_str(), s);
4394 assert_eq!(r.as_str().len(), s.len());
4395 }
4396 }
4397
4398 #[test]
4399 fn refstr_len_zero_over_nonempty_buffer_is_empty_not_dangling() {
4400 let backing = "not empty";
4402 let r = Refstr {
4403 ptr: backing.as_ptr(),
4404 len: 0,
4405 };
4406 assert_eq!(r.as_str(), "");
4407 }
4408
4409 #[test]
4410 fn refstr_huge_input_does_not_hang() {
4411 let huge = "x".repeat(1_000_000);
4413 let r: Refstr = huge.as_str().into();
4414 assert_eq!(r.as_str().len(), 1_000_000);
4415 assert_eq!(r.as_str(), huge.as_str());
4416 }
4417
4418 #[test]
4419 fn refstr_debug_on_null_does_not_panic() {
4420 let null = Refstr {
4421 ptr: core::ptr::null(),
4422 len: usize::MAX,
4423 };
4424 assert_eq!(alloc::format!("{null:?}"), "\"\"");
4426 }
4427
4428 #[test]
4429 fn refstr_clone_preserves_ptr_and_len() {
4430 let s = "clone me";
4431 let r: Refstr = s.into();
4432 let c = r.clone();
4433 assert_eq!(c.as_str(), s);
4434 assert!(core::ptr::eq(c.ptr, r.ptr));
4435 assert_eq!(c.len, r.len);
4436 }
4437
4438 #[test]
4439 fn every_vec_ref_with_null_ptr_and_nonzero_len_is_empty() {
4440 const GARBAGE_LEN: usize = usize::MAX;
4443
4444 assert!(RefstrVecRef {
4445 ptr: core::ptr::null(),
4446 len: GARBAGE_LEN
4447 }
4448 .as_slice()
4449 .is_empty());
4450 assert!(GLuintVecRef {
4451 ptr: core::ptr::null(),
4452 len: GARBAGE_LEN
4453 }
4454 .as_slice()
4455 .is_empty());
4456 assert!(GLenumVecRef {
4457 ptr: core::ptr::null(),
4458 len: GARBAGE_LEN
4459 }
4460 .as_slice()
4461 .is_empty());
4462 assert!(U8VecRef {
4463 ptr: core::ptr::null(),
4464 len: GARBAGE_LEN
4465 }
4466 .as_slice()
4467 .is_empty());
4468 assert!(F32VecRef {
4469 ptr: core::ptr::null(),
4470 len: GARBAGE_LEN
4471 }
4472 .as_slice()
4473 .is_empty());
4474 assert!(I32VecRef {
4475 ptr: core::ptr::null(),
4476 len: GARBAGE_LEN
4477 }
4478 .as_slice()
4479 .is_empty());
4480
4481 let mut m64 = GLint64VecRefMut {
4483 ptr: core::ptr::null_mut(),
4484 len: GARBAGE_LEN,
4485 };
4486 assert!(m64.as_slice().is_empty());
4487 assert!(m64.as_mut_slice().is_empty());
4488
4489 let mut mf = GLfloatVecRefMut {
4490 ptr: core::ptr::null_mut(),
4491 len: GARBAGE_LEN,
4492 };
4493 assert!(mf.as_slice().is_empty());
4494 assert!(mf.as_mut_slice().is_empty());
4495
4496 let mut mi = GLintVecRefMut {
4497 ptr: core::ptr::null_mut(),
4498 len: GARBAGE_LEN,
4499 };
4500 assert!(mi.as_slice().is_empty());
4501 assert!(mi.as_mut_slice().is_empty());
4502
4503 let mut mb = GLbooleanVecRefMut {
4504 ptr: core::ptr::null_mut(),
4505 len: GARBAGE_LEN,
4506 };
4507 assert!(mb.as_slice().is_empty());
4508 assert!(mb.as_mut_slice().is_empty());
4509
4510 let mut mu8 = U8VecRefMut {
4511 ptr: core::ptr::null_mut(),
4512 len: GARBAGE_LEN,
4513 };
4514 assert!(mu8.as_slice().is_empty());
4515 assert!(mu8.as_mut_slice().is_empty());
4516 }
4517
4518 #[test]
4519 fn vec_refs_roundtrip_from_slice() {
4520 let u: [GLuint; 3] = [0, 1, u32::MAX];
4521 assert_eq!(GLuintVecRef::from(&u[..]).as_slice(), &u[..]);
4522
4523 let e: [GLenum; 2] = [gl::TRIANGLES, u32::MAX];
4524 assert_eq!(GLenumVecRef::from(&e[..]).as_slice(), &e[..]);
4525
4526 let b: [u8; 4] = [0, 127, 128, 255];
4527 assert_eq!(U8VecRef::from(&b[..]).as_slice(), &b[..]);
4528
4529 let i: [i32; 3] = [i32::MIN, 0, i32::MAX];
4530 assert_eq!(I32VecRef::from(&i[..]).as_slice(), &i[..]);
4531
4532 let empty: [u8; 0] = [];
4534 assert!(U8VecRef::from(&empty[..]).as_slice().is_empty());
4535 }
4536
4537 #[test]
4538 fn f32_vec_ref_preserves_nan_inf_and_subnormals_bit_exactly() {
4539 let vals: [f32; 6] = [
4542 f32::NAN,
4543 f32::INFINITY,
4544 f32::NEG_INFINITY,
4545 -0.0,
4546 f32::MIN_POSITIVE / 2.0, f32::MAX,
4548 ];
4549 let r = F32VecRef::from(&vals[..]);
4550 let got = r.as_slice();
4551 assert_eq!(got.len(), vals.len());
4552 for (g, v) in got.iter().zip(vals.iter()) {
4553 assert_eq!(g.to_bits(), v.to_bits());
4554 }
4555 assert!(got[0].is_nan());
4556 assert!(got[3].is_sign_negative());
4557 }
4558
4559 #[test]
4560 fn glint64_vec_ref_mut_handles_boundary_values() {
4561 let mut vals: [GLint64; 4] = [i64::MIN, -1, 0, i64::MAX];
4562 let mut r = GLint64VecRefMut::from(&mut vals[..]);
4563 assert_eq!(r.as_slice(), &[i64::MIN, -1, 0, i64::MAX]);
4564
4565 r.as_mut_slice()[0] = i64::MAX;
4567 r.as_mut_slice()[3] = i64::MIN;
4568 assert_eq!(vals, [i64::MAX, -1, 0, i64::MIN]);
4569 }
4570
4571 #[test]
4572 fn mut_vec_refs_write_through_to_the_caller_buffer() {
4573 let mut floats: [GLfloat; 2] = [0.0, 0.0];
4574 GLfloatVecRefMut::from(&mut floats[..]).as_mut_slice()[1] = f32::NAN;
4575 assert!(floats[1].is_nan());
4576
4577 let mut ints: [GLint; 2] = [0, 0];
4578 GLintVecRefMut::from(&mut ints[..]).as_mut_slice()[0] = i32::MIN;
4579 assert_eq!(ints[0], i32::MIN);
4580
4581 let mut bools: [GLboolean; 2] = [0, 0];
4582 GLbooleanVecRefMut::from(&mut bools[..]).as_mut_slice()[1] = 255;
4583 assert_eq!(bools[1], 255);
4584
4585 let mut bytes: [u8; 3] = [1, 2, 3];
4586 U8VecRefMut::from(&mut bytes[..]).as_mut_slice()[2] = 0;
4587 assert_eq!(bytes, [1, 2, 0]);
4588 }
4589
4590 #[test]
4591 fn u8_vec_ref_ord_eq_hash_agree_with_the_underlying_slice() {
4592 use core::hash::{BuildHasher, Hasher};
4593
4594 use alloc::collections::BTreeSet;
4595
4596 let a = [1u8, 2, 3];
4597 let b = [1u8, 2, 4];
4598 let ra = U8VecRef::from(&a[..]);
4599 let rb = U8VecRef::from(&b[..]);
4600
4601 assert_eq!(ra, U8VecRef::from(&a[..]));
4602 assert!(ra < rb);
4603 assert_eq!(ra.cmp(&rb), a[..].cmp(&b[..]));
4604
4605 let null = U8VecRef {
4607 ptr: core::ptr::null(),
4608 len: 99,
4609 };
4610 let empty: [u8; 0] = [];
4611 assert_eq!(null, U8VecRef::from(&empty[..]));
4612
4613 fn hash_of(v: &U8VecRef) -> u64 {
4615 let mut h = core::hash::BuildHasherDefault::<TestHasher>::default().build_hasher();
4616 v.hash(&mut h);
4617 h.finish()
4618 }
4619 #[derive(Default)]
4620 struct TestHasher(u64);
4621 impl Hasher for TestHasher {
4622 fn finish(&self) -> u64 {
4623 self.0
4624 }
4625 fn write(&mut self, bytes: &[u8]) {
4626 for b in bytes {
4627 self.0 = self.0.wrapping_mul(31).wrapping_add(u64::from(*b));
4628 }
4629 }
4630 }
4631 assert_eq!(hash_of(&ra), hash_of(&U8VecRef::from(&a[..])));
4632 assert_eq!(hash_of(&null), hash_of(&U8VecRef::from(&empty[..])));
4633
4634 let mut set = BTreeSet::new();
4636 set.insert(ra.clone());
4637 set.insert(U8VecRef::from(&a[..]));
4638 set.insert(rb);
4639 assert_eq!(set.len(), 2);
4640 }
4641
4642 #[test]
4643 fn refstr_vec_ref_roundtrips_and_maps_back_to_strs() {
4644 let strs = ["", "a", "\u{1F600}"];
4645 let refstrs: Vec<Refstr> = strs.iter().map(|s| Refstr::from(*s)).collect();
4646 let vec_ref = RefstrVecRef::from(&refstrs[..]);
4647 let got: Vec<&str> = vec_ref.as_slice().iter().map(Refstr::as_str).collect();
4648 assert_eq!(got, strs);
4649 }
4650
4651 #[test]
4656 fn glsync_ptr_roundtrips_null_and_nonnull() {
4657 let null = GLsyncPtr::new(core::ptr::null());
4658 assert!(null.clone().get().is_null());
4659 assert_eq!(alloc::format!("{null:?}"), "0x0");
4660
4661 let sentinel = usize::MAX as *const c_void;
4663 let p = GLsyncPtr::new(sentinel);
4664 assert_eq!(p.clone().get() as usize, usize::MAX);
4665 assert!(p.run_destructor);
4666 }
4667
4668 #[cfg(feature = "std")]
4673 #[test]
4674 fn shader_with_glsl_version_replaces_only_the_first_line() {
4675 let src = b"#version 150\nbody line 1\nbody line 2";
4676 let out = shader_with_glsl_version(src, b"#version 300 es\n");
4677 assert_eq!(out, b"#version 300 es\nbody line 1\nbody line 2".to_vec());
4678 }
4679
4680 #[cfg(feature = "std")]
4681 #[test]
4682 fn shader_with_glsl_version_empty_src_yields_just_the_version_line() {
4683 assert_eq!(
4686 shader_with_glsl_version(b"", b"#version 150\n"),
4687 b"#version 150\n".to_vec()
4688 );
4689 }
4690
4691 #[cfg(feature = "std")]
4692 #[test]
4693 fn shader_with_glsl_version_src_without_newline_is_kept_whole() {
4694 let out = shader_with_glsl_version(b"void main(){}", b"#version 150\n");
4697 assert_eq!(out, b"#version 150\nvoid main(){}".to_vec());
4698 }
4699
4700 #[cfg(feature = "std")]
4701 #[test]
4702 fn shader_with_glsl_version_leading_newline_drops_only_that_newline() {
4703 let out = shader_with_glsl_version(b"\nrest", b"V\n");
4704 assert_eq!(out, b"V\nrest".to_vec());
4705 }
4706
4707 #[cfg(feature = "std")]
4708 #[test]
4709 fn shader_with_glsl_version_empty_version_line_still_strips_first_line() {
4710 let out = shader_with_glsl_version(b"#version 150\nbody", b"");
4711 assert_eq!(out, b"body".to_vec());
4712
4713 assert!(shader_with_glsl_version(b"", b"").is_empty());
4715 }
4716
4717 #[cfg(feature = "std")]
4718 #[test]
4719 fn shader_with_glsl_version_is_byte_exact_on_invalid_utf8_and_nul_bytes() {
4720 let src = b"#version 150\n\xFF\xFE\x00\x80body";
4723 let out = shader_with_glsl_version(src, b"#version 100\n");
4724 assert_eq!(out, b"#version 100\n\xFF\xFE\x00\x80body".to_vec());
4725
4726 let out2 = shader_with_glsl_version(&[0xFF, 0xFE, 0x00], b"V\n");
4728 assert_eq!(out2, vec![b'V', b'\n', 0xFF, 0xFE, 0x00]);
4729 }
4730
4731 #[cfg(feature = "std")]
4732 #[test]
4733 fn shader_with_glsl_version_handles_a_1mb_source_without_hanging() {
4734 let mut src = b"#version 150\n".to_vec();
4735 src.resize(src.len() + 1_000_000, b'x');
4736 let out = shader_with_glsl_version(&src, b"#version 300 es\n");
4737 assert_eq!(out.len(), b"#version 300 es\n".len() + 1_000_000);
4738 assert!(out.starts_with(b"#version 300 es\n"));
4739 assert!(out.ends_with(b"xxxx"));
4740 }
4741
4742 #[test]
4747 fn glsl_version_candidates_are_wellformed_and_distinct() {
4748 for gl_type in [GlType::Gl, GlType::Gles] {
4749 let candidates = glsl_version_candidates(gl_type);
4750 assert!(
4751 !candidates.is_empty(),
4752 "{gl_type:?} must have at least one candidate or the probe can never succeed"
4753 );
4754 for c in candidates {
4755 let s = core::str::from_utf8(c).expect("candidate must be valid UTF-8");
4759 assert!(s.starts_with("#version "), "{s:?}");
4760 assert!(s.ends_with('\n'), "{s:?}");
4761 let parsed = s.trim().trim_start_matches("#version ");
4762 assert!(!parsed.is_empty(), "{s:?} must parse to a nonempty version");
4763 }
4764 for (i, a) in candidates.iter().enumerate() {
4766 for b in candidates.iter().skip(i + 1) {
4767 assert_ne!(a, b, "duplicate candidate in {gl_type:?}");
4768 }
4769 }
4770 }
4771
4772 for g in glsl_version_candidates(GlType::Gl) {
4774 assert!(!glsl_version_candidates(GlType::Gles).contains(g));
4775 }
4776
4777 let first_gl = core::str::from_utf8(glsl_version_candidates(GlType::Gl)[0]).unwrap();
4779 assert_eq!(first_gl.trim().trim_start_matches("#version "), "150");
4780 let first_es = core::str::from_utf8(glsl_version_candidates(GlType::Gles)[0]).unwrap();
4781 assert_eq!(first_es.trim().trim_start_matches("#version "), "300 es");
4782 }
4783
4784 #[test]
4785 fn gl_type_from_context_gl_type_is_total() {
4786 assert_eq!(GlType::from(GlContextGlType::Gl), GlType::Gl);
4787 assert_eq!(GlType::from(GlContextGlType::GlEs), GlType::Gles);
4788 }
4789
4790 #[test]
4795 fn gl_context_ptr_software_is_never_usable_and_has_no_shaders() {
4796 let ctx = GlContextPtr::new(RendererType::Software, null_gl());
4799 assert!(!ctx.is_gl_usable());
4800 assert_eq!(ctx.get_svg_shader(), 0);
4801 assert_eq!(ctx.get_brush_shader(), 0);
4802 assert_eq!(ctx.get_fxaa_shader(), 0);
4803 assert_eq!(ctx.get_usable_glsl_version().as_str(), "");
4804 assert_eq!(ctx.renderer_type, RendererType::Software);
4805 }
4806
4807 #[test]
4808 fn gl_context_ptr_hardware_with_broken_driver_falls_back_instead_of_panicking() {
4809 let ctx = GlContextPtr::new(RendererType::Hardware, null_gl());
4815 assert!(
4816 !ctx.is_gl_usable(),
4817 "a driver that compiles nothing must report is_gl_usable() == false"
4818 );
4819 assert_eq!(ctx.get_svg_shader(), 0);
4820 assert_eq!(ctx.get_brush_shader(), 0);
4821 assert_eq!(ctx.get_fxaa_shader(), 0);
4822 assert_eq!(
4823 ctx.get_usable_glsl_version().as_str(),
4824 "",
4825 "glsl_version must be empty when the context is unusable"
4826 );
4827 }
4828
4829 #[test]
4830 fn gl_context_ptr_get_type_defaults_to_desktop_gl_on_an_empty_version_string() {
4831 assert_eq!(null_ctx().get_type(), GlType::Gl);
4834 }
4835
4836 #[test]
4837 fn gl_context_ptr_clone_is_eq_but_distinct_contexts_are_not() {
4838 let a = null_ctx();
4841 let clone = a.clone();
4842 assert_eq!(a, clone);
4843 assert_eq!(a.cmp(&clone), core::cmp::Ordering::Equal);
4844 assert_eq!(a.partial_cmp(&clone), Some(core::cmp::Ordering::Equal));
4845
4846 let b = null_ctx();
4847 assert_ne!(a, b);
4848 assert_eq!(a.cmp(&b), a.partial_cmp(&b).unwrap());
4850 assert_eq!(a.cmp(&b).reverse(), b.cmp(&a));
4851
4852 assert!(Rc::ptr_eq(a.get(), clone.get()));
4854 assert!(!Rc::ptr_eq(a.get(), b.get()));
4855 }
4856
4857 #[test]
4858 fn gl_context_ptr_gen_family_returns_empty_for_every_n_including_negatives() {
4859 let ctx = null_ctx();
4863 for n in [0, 1, -1, i32::MIN, i32::MAX] {
4864 assert!(ctx.gen_buffers(n).as_slice().is_empty(), "gen_buffers({n})");
4865 assert!(ctx.gen_textures(n).as_slice().is_empty(), "gen_textures({n})");
4866 assert!(
4867 ctx.gen_framebuffers(n).as_slice().is_empty(),
4868 "gen_framebuffers({n})"
4869 );
4870 assert!(
4871 ctx.gen_renderbuffers(n).as_slice().is_empty(),
4872 "gen_renderbuffers({n})"
4873 );
4874 assert!(
4875 ctx.gen_vertex_arrays(n).as_slice().is_empty(),
4876 "gen_vertex_arrays({n})"
4877 );
4878 assert!(ctx.gen_queries(n).as_slice().is_empty(), "gen_queries({n})");
4879 }
4880 }
4881
4882 #[test]
4883 fn gl_context_ptr_integer_extremes_do_not_panic() {
4884 let ctx = null_ctx();
4888 let data = [0u8; 4];
4889 let void_ptr = || GlVoidPtrConst {
4890 ptr: data.as_ptr().cast(),
4891 run_destructor: false,
4892 };
4893
4894 for offset in [0isize, -1, isize::MIN, isize::MAX] {
4895 for size in [0isize, -1, isize::MIN, isize::MAX] {
4896 ctx.buffer_sub_data_untyped(gl::ARRAY_BUFFER, offset, size, void_ptr());
4897 let _ = ctx.map_buffer_range(gl::ARRAY_BUFFER, offset, size, 0);
4898 }
4899 }
4900 ctx.buffer_data_untyped(gl::ARRAY_BUFFER, isize::MIN, void_ptr(), gl::STATIC_DRAW);
4901
4902 for offset in [0usize, 1, usize::MAX] {
4904 ctx.tex_sub_image_2d_pbo(
4905 gl::TEXTURE_2D,
4906 0,
4907 0,
4908 0,
4909 1,
4910 1,
4911 gl::RGBA,
4912 gl::UNSIGNED_BYTE,
4913 offset,
4914 );
4915 }
4916
4917 ctx.pixel_store_i(gl::PACK_ALIGNMENT, i32::MIN);
4918 ctx.pixel_store_i(gl::PACK_ALIGNMENT, i32::MAX);
4919 let _ = ctx.unmap_buffer(gl::ARRAY_BUFFER);
4920 }
4921
4922 #[test]
4923 fn gl_context_ptr_float_extremes_do_not_panic() {
4924 let ctx = null_ctx();
4926 for v in [
4927 0.0,
4928 1.0,
4929 -1.0,
4930 f32::NAN,
4931 f32::INFINITY,
4932 f32::NEG_INFINITY,
4933 f32::MIN,
4934 f32::MAX,
4935 ] {
4936 ctx.sample_coverage(v, false);
4937 ctx.sample_coverage(v, true);
4938 ctx.polygon_offset(v, v);
4939 }
4940 }
4941
4942 #[test]
4943 fn gl_context_ptr_shader_source_handles_empty_unicode_and_embedded_nuls() {
4944 let ctx = null_ctx();
4947 let strings: StringVec = vec![
4948 AzString::from(String::new()),
4949 AzString::from("\u{1F600} emoji".to_string()),
4950 AzString::from("has\u{0}nul".to_string()),
4951 AzString::from("x".repeat(100_000)),
4952 ]
4953 .into();
4954 ctx.shader_source(0, strings);
4955
4956 ctx.shader_source(u32::MAX, Vec::<AzString>::new().into());
4958 }
4959
4960 #[test]
4961 fn gl_context_ptr_read_pixels_sizes_the_buffer_from_the_dimensions() {
4962 let ctx = null_ctx();
4970 let px = ctx.read_pixels(0, 0, 2, 3, gl::RGBA, gl::UNSIGNED_BYTE);
4971 assert_eq!(px.len(), 2 * 3 * 4, "RGBA/UNSIGNED_BYTE = 4 bytes per pixel");
4972
4973 assert_eq!(
4975 ctx.read_pixels(0, 0, 0, 0, gl::RGBA, gl::UNSIGNED_BYTE).len(),
4976 0
4977 );
4978 }
4979
4980 #[test]
4981 fn gl_context_ptr_read_pixels_into_buffer_accepts_null_and_undersized_targets() {
4982 let ctx = null_ctx();
4985 ctx.read_pixels_into_buffer(
4986 0,
4987 0,
4988 4,
4989 4,
4990 gl::RGBA,
4991 gl::UNSIGNED_BYTE,
4992 U8VecRefMut {
4993 ptr: core::ptr::null_mut(),
4994 len: 64,
4995 },
4996 );
4997
4998 let mut small = [0u8; 1];
4999 ctx.read_pixels_into_buffer(
5000 0,
5001 0,
5002 4,
5003 4,
5004 gl::RGBA,
5005 gl::UNSIGNED_BYTE,
5006 (&mut small[..]).into(),
5007 );
5008 assert_eq!(small, [0u8; 1]);
5009 }
5010
5011 #[test]
5012 fn gl_context_ptr_uniform_getters_accept_null_result_buffers() {
5013 let ctx = null_ctx();
5014 ctx.get_uniform_iv(
5015 0,
5016 -1,
5017 GLintVecRefMut {
5018 ptr: core::ptr::null_mut(),
5019 len: 16,
5020 },
5021 );
5022 ctx.get_uniform_fv(
5023 0,
5024 i32::MIN,
5025 GLfloatVecRefMut {
5026 ptr: core::ptr::null_mut(),
5027 len: 16,
5028 },
5029 );
5030
5031 let mut ints = [0i32; 2];
5033 ctx.get_uniform_iv(u32::MAX, i32::MAX, (&mut ints[..]).into());
5034 let mut floats = [0f32; 2];
5035 ctx.get_uniform_fv(u32::MAX, i32::MAX, (&mut floats[..]).into());
5036 }
5037
5038 #[test]
5039 fn gl_context_ptr_get_uniform_indices_handles_empty_and_null_name_lists() {
5040 let ctx = null_ctx();
5041 let empty: &[Refstr] = &[];
5042 assert!(ctx
5043 .get_uniform_indices(0, empty.into())
5044 .as_slice()
5045 .is_empty());
5046 assert!(ctx
5047 .get_uniform_indices(
5048 0,
5049 RefstrVecRef {
5050 ptr: core::ptr::null(),
5051 len: 4,
5052 },
5053 )
5054 .as_slice()
5055 .is_empty());
5056 }
5057
5058 #[test]
5059 fn gl_context_ptr_delete_family_accepts_empty_and_null_id_lists() {
5060 let ctx = null_ctx();
5062 let empty: &[GLuint] = &[];
5063 ctx.delete_buffers(empty.into());
5064 ctx.delete_textures(empty.into());
5065 ctx.delete_framebuffers(empty.into());
5066 ctx.delete_renderbuffers(empty.into());
5067 ctx.delete_vertex_arrays(empty.into());
5068 ctx.delete_queries(empty.into());
5069
5070 let null = || GLuintVecRef {
5071 ptr: core::ptr::null(),
5072 len: 7,
5073 };
5074 ctx.delete_buffers(null());
5075 ctx.delete_textures(null());
5076 ctx.delete_framebuffers(null());
5077 ctx.delete_renderbuffers(null());
5078 ctx.delete_vertex_arrays(null());
5079 ctx.delete_queries(null());
5080
5081 let ids: &[GLuint] = &[0, 1, u32::MAX];
5083 ctx.delete_buffers(ids.into());
5084 ctx.delete_textures(ids.into());
5085 }
5086
5087 #[test]
5088 fn gl_context_ptr_draw_buffers_accepts_an_empty_list() {
5089 let ctx = null_ctx();
5090 let empty: &[GLenum] = &[];
5091 ctx.draw_buffers(empty.into());
5092 ctx.draw_buffers(
5093 GLenumVecRef {
5094 ptr: core::ptr::null(),
5095 len: 3,
5096 },
5097 );
5098 }
5099
5100 #[test]
5105 fn vertex_attribute_type_mem_size_matches_the_rust_type_it_names() {
5106 assert_eq!(
5107 VertexAttributeType::Float.get_mem_size(),
5108 core::mem::size_of::<f32>()
5109 );
5110 assert_eq!(
5111 VertexAttributeType::Double.get_mem_size(),
5112 core::mem::size_of::<f64>()
5113 );
5114 assert_eq!(
5115 VertexAttributeType::UnsignedByte.get_mem_size(),
5116 core::mem::size_of::<u8>()
5117 );
5118 assert_eq!(
5119 VertexAttributeType::UnsignedShort.get_mem_size(),
5120 core::mem::size_of::<u16>()
5121 );
5122 assert_eq!(
5123 VertexAttributeType::UnsignedInt.get_mem_size(),
5124 core::mem::size_of::<u32>()
5125 );
5126
5127 for t in ALL_ATTRIB_TYPES {
5130 assert!(t.get_mem_size() > 0, "{t:?}");
5131 }
5132 }
5133
5134 #[test]
5135 fn vertex_attribute_type_gl_ids_are_distinct_and_nonzero() {
5136 for (i, a) in ALL_ATTRIB_TYPES.iter().enumerate() {
5137 assert_ne!(a.get_gl_id(), 0, "{a:?} maps to the GL 'no type' id 0");
5138 for b in ALL_ATTRIB_TYPES.iter().skip(i + 1) {
5139 assert_ne!(
5140 a.get_gl_id(),
5141 b.get_gl_id(),
5142 "{a:?} and {b:?} share a GL id -- one of them would upload as the wrong type"
5143 );
5144 }
5145 }
5146 assert_eq!(VertexAttributeType::Float.get_gl_id(), gl::FLOAT);
5147 assert_eq!(
5148 VertexAttributeType::UnsignedByte.get_gl_id(),
5149 gl::UNSIGNED_BYTE
5150 );
5151 }
5152
5153 #[test]
5154 fn vertex_attribute_get_stride_at_zero_and_at_the_overflow_boundary() {
5155 let attr = |ty, item_count| VertexAttribute {
5156 va_name: AzString::from_const_str("vAttrXY"),
5157 layout_location: OptionUsize::None,
5158 attribute_type: ty,
5159 item_count,
5160 };
5161
5162 for t in ALL_ATTRIB_TYPES {
5164 assert_eq!(attr(t, 0).get_stride(), 0, "{t:?}");
5165 }
5166
5167 assert_eq!(attr(VertexAttributeType::Float, 2).get_stride(), 8);
5169 assert_eq!(attr(VertexAttributeType::Double, 4).get_stride(), 32);
5170 assert_eq!(attr(VertexAttributeType::UnsignedByte, 3).get_stride(), 3);
5171
5172 for t in ALL_ATTRIB_TYPES {
5176 let max_items = usize::MAX / t.get_mem_size();
5177 assert_eq!(
5178 attr(t, max_items).get_stride(),
5179 max_items * t.get_mem_size(),
5180 "{t:?} at the overflow boundary"
5181 );
5182 }
5183 }
5184
5185 #[test]
5186 fn vertex_layout_stride_is_the_sum_of_its_fields() {
5187 let attr = |name: &str, ty, item_count| VertexAttribute {
5188 va_name: AzString::from(name.to_string()),
5189 layout_location: OptionUsize::None,
5190 attribute_type: ty,
5191 item_count,
5192 };
5193
5194 let empty = VertexLayout {
5196 fields: VertexAttributeVec::from_const_slice(&[]),
5197 };
5198 assert_eq!(
5199 empty.fields.iter().map(VertexAttribute::get_stride).sum::<usize>(),
5200 0
5201 );
5202
5203 let layout = VertexLayout {
5205 fields: vec![
5206 attr("vAttrXY", VertexAttributeType::Float, 2),
5207 attr("vColor", VertexAttributeType::UnsignedByte, 4),
5208 ]
5209 .into(),
5210 };
5211 let total: usize = layout.fields.iter().map(VertexAttribute::get_stride).sum();
5212 assert_eq!(total, 12);
5213
5214 assert_eq!(layout, layout.clone());
5216 }
5217
5218 #[test]
5219 fn index_buffer_format_gl_ids_are_distinct() {
5220 for (i, a) in ALL_INDEX_FORMATS.iter().enumerate() {
5222 for b in ALL_INDEX_FORMATS.iter().skip(i + 1) {
5223 assert_ne!(a.get_gl_id(), b.get_gl_id(), "{a:?} vs {b:?}");
5224 }
5225 }
5226 assert_eq!(IndexBufferFormat::Points.get_gl_id(), gl::POINTS);
5227 assert_eq!(
5228 IndexBufferFormat::TriangleStrip.get_gl_id(),
5229 gl::TRIANGLE_STRIP
5230 );
5231 }
5232
5233 #[test]
5238 fn uniform_type_nan_never_equals_itself_so_draw_always_reuploads_it() {
5239 let nan = UniformType::Float(f32::NAN);
5244 assert_ne!(nan, UniformType::Float(f32::NAN));
5245 assert_ne!(Some(nan), Some(nan));
5246
5247 let nan_vec = UniformType::FloatVec4([f32::NAN, 0.0, 0.0, 0.0]);
5248 assert_ne!(nan_vec, nan_vec);
5249
5250 assert_eq!(UniformType::Float(0.0), UniformType::Float(-0.0));
5254
5255 assert_eq!(UniformType::Int(i32::MIN), UniformType::Int(i32::MIN));
5257 assert_ne!(UniformType::Int(0), UniformType::UnsignedInt(0));
5258 }
5259
5260 #[test]
5261 fn uniform_type_matrix_transpose_flag_is_part_of_its_identity() {
5262 let m = [0.0f32; 4];
5263 assert_ne!(
5264 UniformType::Matrix2 {
5265 transpose: false,
5266 matrix: m
5267 },
5268 UniformType::Matrix2 {
5269 transpose: true,
5270 matrix: m
5271 },
5272 );
5273 assert_ne!(
5275 UniformType::FloatVec2([1.0, 2.0]),
5276 UniformType::IntVec2([1, 2])
5277 );
5278 }
5279
5280 #[test]
5281 fn uniform_create_preserves_empty_unicode_and_huge_names() {
5282 let huge = "n".repeat(100_000);
5283 for name in ["", "u_color", "\u{1F600}", "e\u{0301}", huge.as_str()] {
5284 let u = Uniform::create(name.to_string(), UniformType::Int(0));
5285 assert_eq!(u.uniform_name.as_str(), name);
5286 }
5287 }
5288
5289 #[test]
5294 fn gl_shader_new_reports_no_shader_compiler_instead_of_panicking() {
5295 let ctx = null_ctx();
5299 let huge = "x".repeat(200_000);
5300 for (vert, frag) in [
5301 ("", ""),
5302 (" \t\n ", "\n\n"),
5303 ("not glsl at all ;;;{{{", "\u{1F600}"),
5304 ("void main(){}", "void main(){}"),
5305 (huge.as_str(), huge.as_str()),
5306 ] {
5307 let err = GlShader::new(&ctx, vert, frag).unwrap_err();
5308 assert_eq!(err, GlShaderCreateError::NoShaderCompiler);
5309 }
5310 }
5311
5312 #[test]
5313 fn shader_error_types_format_without_panicking_on_unicode_and_extremes() {
5314 let vert = VertexShaderCompileError {
5315 error_id: i32::MIN,
5316 info_log: AzString::from("\u{1F600} log".to_string()),
5317 };
5318 let frag = FragmentShaderCompileError {
5319 error_id: i32::MAX,
5320 info_log: AzString::from(String::new()),
5321 };
5322 let link = GlShaderLinkError {
5323 error_id: -1,
5324 info_log: AzString::from("multi\nline\0log".to_string()),
5325 };
5326
5327 assert!(alloc::format!("{vert}").contains("-2147483648"));
5328 assert!(alloc::format!("{vert}").contains("\u{1F600}"));
5329 assert!(alloc::format!("{frag}").contains("2147483647"));
5330
5331 let compile = GlShaderCompileError::Vertex(vert);
5332 assert!(alloc::format!("{compile}").contains("Failed to compile vertex shader"));
5333 assert_eq!(alloc::format!("{compile:?}"), alloc::format!("{compile}"));
5335
5336 let frag_err = GlShaderCompileError::Fragment(frag);
5337 assert!(alloc::format!("{frag_err}").contains("Failed to compile fragment shader"));
5338
5339 let create = GlShaderCreateError::Link(link);
5340 assert!(alloc::format!("{create}").contains("Shader linking error"));
5341 assert_eq!(alloc::format!("{create:?}"), alloc::format!("{create}"));
5342 assert!(alloc::format!("{}", GlShaderCreateError::NoShaderCompiler)
5343 .contains("doesn't include a shader compiler"));
5344 }
5345
5346 #[test]
5351 fn texture_descriptor_mirrors_the_texture_including_extreme_sizes() {
5352 let tex = test_texture(42, 640, 480);
5353 let d = tex.get_descriptor();
5354 assert_eq!(d.width, 640);
5355 assert_eq!(d.height, 480);
5356 assert_eq!(d.format, RawImageFormat::RGBA8);
5357 assert_eq!(d.offset, 0);
5358 assert!(!d.flags.is_opaque);
5359 assert!(!d.flags.allow_mipmaps, "textures map 1:1, never mipmapped");
5360
5361 let zero = test_texture(1, 0, 0);
5363 assert_eq!(zero.get_descriptor().width, 0);
5364 assert_eq!(zero.get_descriptor().height, 0);
5365
5366 let huge = test_texture(1, u32::MAX, u32::MAX);
5367 assert_eq!(huge.get_descriptor().width, u32::MAX as usize);
5368 assert_eq!(huge.get_descriptor().height, u32::MAX as usize);
5369
5370 let opaque = Texture::create(
5372 7,
5373 TextureFlags {
5374 is_opaque: true,
5375 is_video_texture: true,
5376 },
5377 PhysicalSizeU32 {
5378 width: 2,
5379 height: 2,
5380 },
5381 ColorU {
5382 r: 0,
5383 g: 0,
5384 b: 0,
5385 a: 0,
5386 },
5387 null_ctx(),
5388 RawImageFormat::BGRA8,
5389 );
5390 assert!(opaque.get_descriptor().flags.is_opaque);
5391 assert_eq!(opaque.get_descriptor().format, RawImageFormat::BGRA8);
5392 }
5393
5394 #[test]
5395 fn texture_clone_and_drop_share_one_refcount_without_double_freeing() {
5396 let tex = test_texture(9, 4, 4);
5399 let clones: Vec<Texture> = (0..16).map(|_| tex.clone()).collect();
5400 for c in &clones {
5401 assert_eq!(c.texture_id, 9);
5402 assert_eq!(c.size, tex.size);
5403 assert_eq!(c.format, tex.format);
5404 assert!(c.run_destructor);
5405 }
5406 assert_eq!(clones[0], tex);
5408 assert_eq!(clones[0], clones[1]);
5409 assert_ne!(tex, test_texture(10, 4, 4));
5410
5411 drop(clones); drop(tex); }
5414
5415 #[test]
5416 fn texture_display_and_debug_render_id_and_size() {
5417 let tex = test_texture(3, 16, 32);
5418 assert_eq!(alloc::format!("{tex}"), "Texture { id: 3, 16x32 }");
5419 assert_eq!(alloc::format!("{tex:?}"), alloc::format!("{tex}"));
5421 }
5422
5423 #[test]
5424 fn texture_paint_stroke_is_a_noop_when_gl_is_unusable() {
5425 let mut tex = test_texture(5, 8, 8);
5431 assert_eq!(tex.gl_context.get_brush_shader(), 0);
5432
5433 for radius in [1.0, 0.0, -1.0, f32::NAN, f32::INFINITY, f32::MAX] {
5434 let mut brush = Brush::new(
5435 ColorU {
5436 r: 255,
5437 g: 0,
5438 b: 0,
5439 a: 255,
5440 },
5441 radius,
5442 );
5443 brush.hardness = f32::NAN;
5444 brush.flow = f32::INFINITY;
5445 brush.spacing = 0.0; tex.paint_stroke(f32::NAN, f32::NEG_INFINITY, f32::MAX, -0.0, brush);
5447 tex.paint_dot(f32::NAN, f32::NAN, brush);
5448 }
5449
5450 let mut zero = test_texture(6, 0, 0);
5452 zero.paint_dot(
5453 0.0,
5454 0.0,
5455 Brush::new(
5456 ColorU {
5457 r: 1,
5458 g: 1,
5459 b: 1,
5460 a: 1,
5461 },
5462 4.0,
5463 ),
5464 );
5465 }
5466
5467 #[test]
5468 fn texture_copy_to_raw_image_returns_a_null_image_on_every_degenerate_input() {
5469 let is_null_image = |img: &RawImage| img.width == 0 && img.height == 0;
5476
5477 assert!(is_null_image(&test_texture(0, 16, 16).copy_to_raw_image()));
5479 assert!(is_null_image(&test_texture(1, 0, 0).copy_to_raw_image()));
5481 assert!(is_null_image(&test_texture(1, 16, 0).copy_to_raw_image()));
5482 assert!(is_null_image(&test_texture(1, 0, 16).copy_to_raw_image()));
5483
5484 assert!(is_null_image(
5486 &test_texture(1, u32::MAX, u32::MAX).copy_to_raw_image()
5487 ));
5488 assert!(is_null_image(
5489 &test_texture(1, 1 << 31, 1 << 31).copy_to_raw_image()
5490 ));
5491
5492 assert!(is_null_image(&test_texture(1, 4, 4).copy_to_raw_image()));
5495 }
5496
5497 #[test]
5498 #[should_panic(expected = "called `Option::unwrap()` on a `None` value")]
5499 fn texture_clear_panics_when_the_driver_allocates_no_framebuffer() {
5500 test_texture(1, 4, 4).clear();
5506 }
5507
5508 #[test]
5513 fn vertex_array_object_clone_and_drop_share_one_refcount() {
5514 let layout = VertexLayout {
5515 fields: VertexAttributeVec::from_const_slice(&[]),
5516 };
5517 let vao = VertexArrayObject::new(layout, 77, null_ctx());
5518 assert_eq!(vao.vao_id, 77);
5519 assert!(vao.run_destructor);
5520
5521 let clones: Vec<VertexArrayObject> = (0..8).map(|_| vao.clone()).collect();
5522 assert!(clones.iter().all(|c| c.vao_id == 77));
5523 assert_eq!(clones[0], vao);
5524 drop(clones);
5525 drop(vao);
5526 }
5527
5528 #[test]
5529 fn vertex_buffer_new_raw_keeps_its_fields_and_refcounts_its_clones() {
5530 let vao = VertexArrayObject::new(
5531 VertexLayout {
5532 fields: VertexAttributeVec::from_const_slice(&[]),
5533 },
5534 1,
5535 null_ctx(),
5536 );
5537 let vb = VertexBuffer::new_raw(11, 300, vao, 22, 40, IndexBufferFormat::TriangleStrip);
5538
5539 assert_eq!(vb.vertex_buffer_id, 11);
5540 assert_eq!(vb.vertex_buffer_len, 300);
5541 assert_eq!(vb.index_buffer_id, 22);
5542 assert_eq!(vb.index_buffer_len, 40);
5543 assert_eq!(vb.index_buffer_format, IndexBufferFormat::TriangleStrip);
5544 assert_eq!(
5545 alloc::format!("{vb}"),
5546 "VertexBuffer { buffer: 11 (length: 300) }})"
5547 );
5548
5549 let clones: Vec<VertexBuffer> = (0..8).map(|_| vb.clone()).collect();
5551 assert_eq!(clones[0], vb);
5552 drop(clones);
5553 drop(vb);
5554
5555 let empty_vao = VertexArrayObject::new(
5557 VertexLayout {
5558 fields: VertexAttributeVec::from_const_slice(&[]),
5559 },
5560 0,
5561 null_ctx(),
5562 );
5563 let empty = VertexBuffer::new_raw(0, 0, empty_vao, 0, 0, IndexBufferFormat::Points);
5564 assert_eq!(empty.vertex_buffer_len, 0);
5565 }
5566
5567 #[allow(dead_code)] struct TestVertex {
5569 _xy: [f32; 2],
5570 }
5571
5572 impl VertexLayoutDescription for TestVertex {
5573 fn get_description() -> VertexLayout {
5574 VertexLayout {
5575 fields: vec![VertexAttribute {
5576 va_name: AzString::from_const_str("vAttrXY"),
5577 layout_location: OptionUsize::None,
5578 attribute_type: VertexAttributeType::Float,
5579 item_count: 2,
5580 }]
5581 .into(),
5582 }
5583 }
5584 }
5585
5586 #[test]
5587 #[should_panic(expected = "called `Option::unwrap()` on a `None` value")]
5588 fn vertex_buffer_new_panics_when_the_driver_allocates_no_vao() {
5589 let verts = [TestVertex { _xy: [0.0, 0.0] }];
5592 let _ = VertexBuffer::new(
5593 null_ctx(),
5594 0,
5595 &verts[..],
5596 &[0u32],
5597 IndexBufferFormat::Triangles,
5598 );
5599 }
5600
5601 #[test]
5611 fn gl_texture_cache_insert_lookup_and_eviction_lifecycle() {
5612 let doc = DocumentId {
5613 namespace_id: crate::resources::IdNamespace(7),
5614 id: 1,
5615 };
5616 let other_doc = DocumentId {
5617 namespace_id: crate::resources::IdNamespace(7),
5618 id: 2,
5619 };
5620
5621 gl_textures_clear_opengl_cache();
5623 let stale = ExternalImageId { inner: u64::MAX };
5624 assert!(get_opengl_texture(&stale).is_none());
5625 assert!(
5626 remove_single_texture_from_active_gl_textures(&doc, &Epoch::from(0), &stale).is_none()
5627 );
5628 gl_textures_remove_epochs_from_pipeline(&doc, Epoch::from(0));
5629 gl_textures_remove_active_pipeline(&doc);
5630 gl_textures_clear_opengl_cache(); let id5 = insert_into_active_gl_textures(doc, Epoch::from(5), test_texture(11, 4, 8));
5638 let id7 = insert_into_active_gl_textures(doc, Epoch::from(7), test_texture(22, 16, 32));
5639 assert_ne!(id5, id7, "each insert must mint a unique ExternalImageId");
5640
5641 assert_eq!(get_opengl_texture(&id5), Some((11, (4.0, 8.0))));
5643 assert_eq!(get_opengl_texture(&id7), Some((22, (16.0, 32.0))));
5644 assert!(get_opengl_texture(&stale).is_none());
5645
5646 let id_huge =
5649 insert_into_active_gl_textures(doc, Epoch::from(5), test_texture(33, u32::MAX, 1));
5650 assert_eq!(get_opengl_texture(&id_huge), Some((33, (4_294_967_296.0, 1.0))));
5651
5652 gl_textures_remove_epochs_from_pipeline(&doc, Epoch::from(7));
5654 assert!(get_opengl_texture(&id5).is_none(), "epoch 5 < 7 must be evicted");
5655 assert!(get_opengl_texture(&id_huge).is_none(), "epoch 5 < 7 must be evicted");
5656 assert_eq!(
5657 get_opengl_texture(&id7),
5658 Some((22, (16.0, 32.0))),
5659 "epoch 7 is NOT < 7, so it must survive"
5660 );
5661
5662 gl_textures_remove_epochs_from_pipeline(&other_doc, Epoch::from(u32::MAX));
5664 assert_eq!(get_opengl_texture(&id7), Some((22, (16.0, 32.0))));
5665
5666 assert_eq!(
5668 remove_single_texture_from_active_gl_textures(&doc, &Epoch::from(7), &id7),
5669 Some(())
5670 );
5671 assert!(get_opengl_texture(&id7).is_none());
5672 assert_eq!(
5675 remove_single_texture_from_active_gl_textures(&doc, &Epoch::from(7), &id7),
5676 Some(())
5677 );
5678 assert!(
5680 remove_single_texture_from_active_gl_textures(&other_doc, &Epoch::from(7), &id7)
5681 .is_none()
5682 );
5683 assert!(remove_single_texture_from_active_gl_textures(
5684 &doc,
5685 &Epoch::from(u32::MAX),
5686 &id7
5687 )
5688 .is_none());
5689
5690 let id_a = insert_into_active_gl_textures(doc, Epoch::from(1), test_texture(44, 2, 2));
5692 let id_b = insert_into_active_gl_textures(other_doc, Epoch::from(1), test_texture(55, 2, 2));
5693 assert!(get_opengl_texture(&id_a).is_some());
5694 gl_textures_remove_active_pipeline(&doc);
5695 assert!(get_opengl_texture(&id_a).is_none(), "doc was removed");
5696 assert!(
5697 get_opengl_texture(&id_b).is_some(),
5698 "other_doc must be untouched"
5699 );
5700
5701 gl_textures_clear_opengl_cache();
5703 assert!(get_opengl_texture(&id_b).is_none());
5704
5705 gl_textures_clear_opengl_cache();
5707 }
5708}