1
//! OpenGL context wrappers, texture cache management, shader compilation,
2
//! vertex buffer abstractions, and FFI-safe GL type aliases for the C/Python API.
3

            
4
#![allow(unused_variables)]
5
use alloc::{
6
    boxed::Box,
7
    rc::Rc,
8
    string::{String, ToString},
9
    vec::Vec,
10
};
11
use core::{
12
    ffi, fmt,
13
    hash::{Hash, Hasher},
14
    mem::ManuallyDrop,
15
    sync::atomic::{AtomicUsize, Ordering as AtomicOrdering},
16
};
17

            
18
use azul_css::{
19
    props::{
20
        basic::{ColorF, ColorU},
21
        style::StyleTransformVec,
22
    },
23
    AzString, OptionI32, OptionU32, OptionUsize, StringVec, U8Vec,
24
};
25
pub use gl_context_loader::{
26
    ctypes::*, gl, GLeglImageOES, GLsync, GLvoid, GenericGlContext, GlType as GlContextGlType,
27
};
28

            
29
pub use crate::glconst::*;
30
use 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

            
42
pub type GLuint = u32;
43
pub type GLint = i32;
44
pub type GLint64 = i64;
45
pub type GLuint64 = u64;
46
pub type GLenum = u32;
47
pub type GLintptr = isize;
48
pub type GLboolean = u8;
49
pub type GLsizeiptr = isize;
50
pub type GLbitfield = u32;
51
pub type GLsizei = i32;
52
pub type GLclampf = f32;
53
pub type GLfloat = f32;
54

            
55
pub const GL_RESTART_INDEX: u32 = core::u32::MAX;
56

            
57
/// Passing *const c_void is not easily possible when generating APIs,
58
/// so this wrapper struct is for easier API generation
59
#[repr(C)]
60
#[derive(Debug)]
61
pub struct GlVoidPtrConst {
62
    pub ptr: *const GLvoid,
63
    pub run_destructor: bool,
64
}
65

            
66
impl Clone for GlVoidPtrConst {
67
    fn clone(&self) -> Self {
68
        Self {
69
            ptr: self.ptr,
70
            run_destructor: true,
71
        }
72
    }
73
}
74

            
75
impl Drop for GlVoidPtrConst {
76
    fn drop(&mut self) {
77
        self.run_destructor = false;
78
    }
79
}
80

            
81
/// Struct returned from the C API
82
///
83
/// Because of Python, every object has to be clone-able,
84
/// so yes there may exist more than one mutable reference
85
#[repr(C)]
86
#[derive(Debug)]
87
pub struct GlVoidPtrMut {
88
    pub ptr: *mut GLvoid,
89
}
90

            
91
impl Clone for GlVoidPtrMut {
92
    fn clone(&self) -> Self {
93
        Self { ptr: self.ptr }
94
    }
95
}
96

            
97
/// FFI-safe wrapper for `&str`.
98
#[repr(C)]
99
pub struct Refstr {
100
    pub ptr: *const u8,
101
    pub len: usize,
102
}
103

            
104
impl Clone for Refstr {
105
    fn clone(&self) -> Self {
106
        Self {
107
            ptr: self.ptr,
108
            len: self.len,
109
        }
110
    }
111
}
112

            
113
impl core::fmt::Debug for Refstr {
114
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
115
        self.as_str().fmt(f)
116
    }
117
}
118

            
119
impl Refstr {
120
    pub fn as_str(&self) -> &str {
121
        unsafe { core::str::from_utf8_unchecked(core::slice::from_raw_parts(self.ptr, self.len)) }
122
    }
123
}
124

            
125
impl From<&str> for Refstr {
126
    fn from(s: &str) -> Self {
127
        Self {
128
            ptr: s.as_ptr(),
129
            len: s.len(),
130
        }
131
    }
132
}
133

            
134
/// FFI-safe wrapper for `&[&str]`.
135
#[repr(C)]
136
pub struct RefstrVecRef {
137
    pub ptr: *const Refstr,
138
    pub len: usize,
139
}
140

            
141
impl Clone for RefstrVecRef {
142
    fn clone(&self) -> Self {
143
        Self {
144
            ptr: self.ptr,
145
            len: self.len,
146
        }
147
    }
148
}
149

            
150
impl core::fmt::Debug for RefstrVecRef {
151
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
152
        self.as_slice().fmt(f)
153
    }
154
}
155

            
156
impl RefstrVecRef {
157
    pub fn as_slice(&self) -> &[Refstr] {
158
        unsafe { core::slice::from_raw_parts(self.ptr, self.len) }
159
    }
160
}
161

            
162
impl From<&[Refstr]> for RefstrVecRef {
163
    fn from(s: &[Refstr]) -> Self {
164
        Self {
165
            ptr: s.as_ptr(),
166
            len: s.len(),
167
        }
168
    }
169
}
170

            
171
/// FFI-safe wrapper for `&mut [GLint64]`.
172
#[repr(C)]
173
pub struct GLint64VecRefMut {
174
    pub ptr: *mut i64,
175
    pub len: usize,
176
}
177

            
178
impl Clone for GLint64VecRefMut {
179
    fn clone(&self) -> Self {
180
        Self {
181
            ptr: self.ptr,
182
            len: self.len,
183
        }
184
    }
185
}
186

            
187
impl core::fmt::Debug for GLint64VecRefMut {
188
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
189
        self.as_slice().fmt(f)
190
    }
191
}
192

            
193
impl From<&mut [GLint64]> for GLint64VecRefMut {
194
    fn from(s: &mut [GLint64]) -> Self {
195
        Self {
196
            ptr: s.as_mut_ptr(),
197
            len: s.len(),
198
        }
199
    }
200
}
201

            
202
impl GLint64VecRefMut {
203
    pub fn as_slice(&self) -> &[GLint64] {
204
        unsafe { core::slice::from_raw_parts(self.ptr, self.len) }
205
    }
206
    fn as_mut_slice(&mut self) -> &mut [GLint64] {
207
        unsafe { core::slice::from_raw_parts_mut(self.ptr, self.len) }
208
    }
209
}
210

            
211
/// FFI-safe wrapper for `&mut [GLfloat]`.
212
#[repr(C)]
213
pub struct GLfloatVecRefMut {
214
    pub ptr: *mut f32,
215
    pub len: usize,
216
}
217

            
218
impl Clone for GLfloatVecRefMut {
219
    fn clone(&self) -> Self {
220
        Self {
221
            ptr: self.ptr,
222
            len: self.len,
223
        }
224
    }
225
}
226

            
227
impl core::fmt::Debug for GLfloatVecRefMut {
228
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
229
        self.as_slice().fmt(f)
230
    }
231
}
232

            
233
impl From<&mut [GLfloat]> for GLfloatVecRefMut {
234
    fn from(s: &mut [GLfloat]) -> Self {
235
        Self {
236
            ptr: s.as_mut_ptr(),
237
            len: s.len(),
238
        }
239
    }
240
}
241

            
242
impl GLfloatVecRefMut {
243
    pub fn as_slice(&self) -> &[GLfloat] {
244
        unsafe { core::slice::from_raw_parts(self.ptr, self.len) }
245
    }
246
    fn as_mut_slice(&mut self) -> &mut [GLfloat] {
247
        unsafe { core::slice::from_raw_parts_mut(self.ptr, self.len) }
248
    }
249
}
250

            
251
/// FFI-safe wrapper for `&mut [GLint]`.
252
#[repr(C)]
253
pub struct GLintVecRefMut {
254
    pub ptr: *mut i32,
255
    pub len: usize,
256
}
257

            
258
impl Clone for GLintVecRefMut {
259
    fn clone(&self) -> Self {
260
        Self {
261
            ptr: self.ptr,
262
            len: self.len,
263
        }
264
    }
265
}
266

            
267
impl core::fmt::Debug for GLintVecRefMut {
268
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
269
        self.as_slice().fmt(f)
270
    }
271
}
272

            
273
impl From<&mut [GLint]> for GLintVecRefMut {
274
    fn from(s: &mut [GLint]) -> Self {
275
        Self {
276
            ptr: s.as_mut_ptr(),
277
            len: s.len(),
278
        }
279
    }
280
}
281

            
282
impl GLintVecRefMut {
283
    pub fn as_slice(&self) -> &[GLint] {
284
        unsafe { core::slice::from_raw_parts(self.ptr, self.len) }
285
    }
286
    fn as_mut_slice(&mut self) -> &mut [GLint] {
287
        unsafe { core::slice::from_raw_parts_mut(self.ptr, self.len) }
288
    }
289
}
290

            
291
/// FFI-safe wrapper for `&[GLuint]`.
292
#[repr(C)]
293
pub struct GLuintVecRef {
294
    pub ptr: *const u32,
295
    pub len: usize,
296
}
297

            
298
impl Clone for GLuintVecRef {
299
    fn clone(&self) -> Self {
300
        Self {
301
            ptr: self.ptr,
302
            len: self.len,
303
        }
304
    }
305
}
306

            
307
impl core::fmt::Debug for GLuintVecRef {
308
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
309
        self.as_slice().fmt(f)
310
    }
311
}
312

            
313
impl From<&[GLuint]> for GLuintVecRef {
314
    fn from(s: &[GLuint]) -> Self {
315
        Self {
316
            ptr: s.as_ptr(),
317
            len: s.len(),
318
        }
319
    }
320
}
321

            
322
impl GLuintVecRef {
323
    pub fn as_slice(&self) -> &[GLuint] {
324
        unsafe { core::slice::from_raw_parts(self.ptr, self.len) }
325
    }
326
}
327

            
328
/// FFI-safe wrapper for `&[GLenum]`.
329
#[repr(C)]
330
pub struct GLenumVecRef {
331
    pub ptr: *const u32,
332
    pub len: usize,
333
}
334

            
335
impl Clone for GLenumVecRef {
336
    fn clone(&self) -> Self {
337
        Self {
338
            ptr: self.ptr,
339
            len: self.len,
340
        }
341
    }
342
}
343

            
344
impl core::fmt::Debug for GLenumVecRef {
345
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
346
        self.as_slice().fmt(f)
347
    }
348
}
349

            
350
impl From<&[GLenum]> for GLenumVecRef {
351
    fn from(s: &[GLenum]) -> Self {
352
        Self {
353
            ptr: s.as_ptr(),
354
            len: s.len(),
355
        }
356
    }
357
}
358

            
359
impl GLenumVecRef {
360
    pub fn as_slice(&self) -> &[GLenum] {
361
        unsafe { core::slice::from_raw_parts(self.ptr, self.len) }
362
    }
363
}
364

            
365
/// FFI-safe wrapper for `&[u8]`.
366
#[repr(C)]
367
pub struct U8VecRef {
368
    pub ptr: *const u8,
369
    pub len: usize,
370
}
371

            
372
impl Clone for U8VecRef {
373
    fn clone(&self) -> Self {
374
        Self {
375
            ptr: self.ptr,
376
            len: self.len,
377
        }
378
    }
379
}
380

            
381
impl From<&[u8]> for U8VecRef {
382
    fn from(s: &[u8]) -> Self {
383
        Self {
384
            ptr: s.as_ptr(),
385
            len: s.len(),
386
        }
387
    }
388
}
389

            
390
impl U8VecRef {
391
    pub fn as_slice(&self) -> &[u8] {
392
        unsafe { core::slice::from_raw_parts(self.ptr, self.len) }
393
    }
394
}
395

            
396
impl fmt::Debug for U8VecRef {
397
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
398
        self.as_slice().fmt(f)
399
    }
400
}
401

            
402
impl PartialOrd for U8VecRef {
403
    fn partial_cmp(&self, rhs: &Self) -> Option<core::cmp::Ordering> {
404
        self.as_slice().partial_cmp(rhs.as_slice())
405
    }
406
}
407

            
408
impl Ord for U8VecRef {
409
    fn cmp(&self, rhs: &Self) -> core::cmp::Ordering {
410
        self.as_slice().cmp(rhs.as_slice())
411
    }
412
}
413

            
414
impl PartialEq for U8VecRef {
415
    fn eq(&self, rhs: &Self) -> bool {
416
        self.as_slice().eq(rhs.as_slice())
417
    }
418
}
419

            
420
impl Eq for U8VecRef {}
421

            
422
impl core::hash::Hash for U8VecRef {
423
    fn hash<H>(&self, state: &mut H)
424
    where
425
        H: core::hash::Hasher,
426
    {
427
        self.as_slice().hash(state)
428
    }
429
}
430

            
431
/// FFI-safe wrapper for `&[f32]`.
432
#[repr(C)]
433
pub struct F32VecRef {
434
    pub ptr: *const f32,
435
    pub len: usize,
436
}
437

            
438
impl Clone for F32VecRef {
439
    fn clone(&self) -> Self {
440
        Self {
441
            ptr: self.ptr,
442
            len: self.len,
443
        }
444
    }
445
}
446

            
447
impl core::fmt::Debug for F32VecRef {
448
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
449
        self.as_slice().fmt(f)
450
    }
451
}
452

            
453
impl From<&[f32]> for F32VecRef {
454
    fn from(s: &[f32]) -> Self {
455
        Self {
456
            ptr: s.as_ptr(),
457
            len: s.len(),
458
        }
459
    }
460
}
461

            
462
impl F32VecRef {
463
    pub fn as_slice(&self) -> &[f32] {
464
        unsafe { core::slice::from_raw_parts(self.ptr, self.len) }
465
    }
466
}
467

            
468
/// FFI-safe wrapper for `&[i32]`.
469
#[repr(C)]
470
pub struct I32VecRef {
471
    pub ptr: *const i32,
472
    pub len: usize,
473
}
474

            
475
impl Clone for I32VecRef {
476
    fn clone(&self) -> Self {
477
        Self {
478
            ptr: self.ptr,
479
            len: self.len,
480
        }
481
    }
482
}
483

            
484
impl core::fmt::Debug for I32VecRef {
485
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
486
        self.as_slice().fmt(f)
487
    }
488
}
489

            
490
impl From<&[i32]> for I32VecRef {
491
    fn from(s: &[i32]) -> Self {
492
        Self {
493
            ptr: s.as_ptr(),
494
            len: s.len(),
495
        }
496
    }
497
}
498

            
499
impl I32VecRef {
500
    pub fn as_slice(&self) -> &[i32] {
501
        unsafe { core::slice::from_raw_parts(self.ptr, self.len) }
502
    }
503
}
504

            
505
/// FFI-safe wrapper for `&mut [GLboolean]` (i.e. `&mut [u8]`).
506
#[repr(C)]
507
pub struct GLbooleanVecRefMut {
508
    pub ptr: *mut u8,
509
    pub len: usize,
510
}
511

            
512
impl Clone for GLbooleanVecRefMut {
513
    fn clone(&self) -> Self {
514
        Self {
515
            ptr: self.ptr,
516
            len: self.len,
517
        }
518
    }
519
}
520

            
521
impl core::fmt::Debug for GLbooleanVecRefMut {
522
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
523
        self.as_slice().fmt(f)
524
    }
525
}
526

            
527
impl From<&mut [GLboolean]> for GLbooleanVecRefMut {
528
    fn from(s: &mut [GLboolean]) -> Self {
529
        Self {
530
            ptr: s.as_mut_ptr(),
531
            len: s.len(),
532
        }
533
    }
534
}
535

            
536
impl GLbooleanVecRefMut {
537
    pub fn as_slice(&self) -> &[GLboolean] {
538
        unsafe { core::slice::from_raw_parts(self.ptr, self.len) }
539
    }
540
    fn as_mut_slice(&mut self) -> &mut [GLboolean] {
541
        unsafe { core::slice::from_raw_parts_mut(self.ptr, self.len) }
542
    }
543
}
544

            
545
/// FFI-safe wrapper for `&mut [u8]`.
546
#[repr(C)]
547
pub struct U8VecRefMut {
548
    pub ptr: *mut u8,
549
    pub len: usize,
550
}
551

            
552
impl Clone for U8VecRefMut {
553
    fn clone(&self) -> Self {
554
        Self {
555
            ptr: self.ptr,
556
            len: self.len,
557
        }
558
    }
559
}
560

            
561
impl core::fmt::Debug for U8VecRefMut {
562
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
563
        self.as_slice().fmt(f)
564
    }
565
}
566

            
567
impl From<&mut [u8]> for U8VecRefMut {
568
    fn from(s: &mut [u8]) -> Self {
569
        Self {
570
            ptr: s.as_mut_ptr(),
571
            len: s.len(),
572
        }
573
    }
574
}
575

            
576
impl U8VecRefMut {
577
    pub fn as_slice(&self) -> &[u8] {
578
        unsafe { core::slice::from_raw_parts(self.ptr, self.len) }
579
    }
580
    fn as_mut_slice(&mut self) -> &mut [u8] {
581
        unsafe { core::slice::from_raw_parts_mut(self.ptr, self.len) }
582
    }
583
}
584

            
585
impl_option!(
586
    U8VecRef,
587
    OptionU8VecRef,
588
    copy = false,
589
    [Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
590
);
591

            
592
#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
593
#[repr(C)]
594
pub struct DebugMessage {
595
    pub message: AzString,
596
    pub source: GLenum,
597
    pub ty: GLenum,
598
    pub id: GLenum,
599
    pub severity: GLenum,
600
}
601

            
602
impl_option!(
603
    DebugMessage,
604
    OptionDebugMessage,
605
    copy = false,
606
    [Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash]
607
);
608

            
609
impl_vec!(DebugMessage, DebugMessageVec, DebugMessageVecDestructor, DebugMessageVecDestructorType, DebugMessageVecSlice, OptionDebugMessage);
610
impl_vec_debug!(DebugMessage, DebugMessageVec);
611
impl_vec_partialord!(DebugMessage, DebugMessageVec);
612
impl_vec_ord!(DebugMessage, DebugMessageVec);
613
impl_vec_clone!(DebugMessage, DebugMessageVec, DebugMessageVecDestructor);
614
impl_vec_partialeq!(DebugMessage, DebugMessageVec);
615
impl_vec_eq!(DebugMessage, DebugMessageVec);
616
impl_vec_hash!(DebugMessage, DebugMessageVec);
617

            
618
impl_vec!(GLint, GLintVec, GLintVecDestructor, GLintVecDestructorType, GLintVecSlice, OptionI32);
619
impl_vec_debug!(GLint, GLintVec);
620
impl_vec_partialord!(GLint, GLintVec);
621
impl_vec_ord!(GLint, GLintVec);
622
impl_vec_clone!(GLint, GLintVec, GLintVecDestructor);
623
impl_vec_partialeq!(GLint, GLintVec);
624
impl_vec_eq!(GLint, GLintVec);
625
impl_vec_hash!(GLint, GLintVec);
626

            
627
impl_vec!(GLuint, GLuintVec, GLuintVecDestructor, GLuintVecDestructorType, GLuintVecSlice, OptionU32);
628
impl_vec_debug!(GLuint, GLuintVec);
629
impl_vec_partialord!(GLuint, GLuintVec);
630
impl_vec_ord!(GLuint, GLuintVec);
631
impl_vec_clone!(GLuint, GLuintVec, GLuintVecDestructor);
632
impl_vec_partialeq!(GLuint, GLuintVec);
633
impl_vec_eq!(GLuint, GLuintVec);
634
impl_vec_hash!(GLuint, GLuintVec);
635

            
636
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
637
#[repr(C)]
638
pub enum GlType {
639
    Gl,
640
    Gles,
641
}
642

            
643
impl From<GlContextGlType> for GlType {
644
    fn from(a: GlContextGlType) -> GlType {
645
        match a {
646
            GlContextGlType::Gl => GlType::Gl,
647
            GlContextGlType::GlEs => GlType::Gles,
648
        }
649
    }
650
}
651

            
652
// (U8Vec, u32)
653
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
654
#[repr(C)]
655
pub struct GetProgramBinaryReturn {
656
    pub _0: U8Vec,
657
    pub _1: u32,
658
}
659

            
660
// (i32, u32, AzString)
661
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
662
#[repr(C)]
663
pub struct GetActiveAttribReturn {
664
    pub _0: i32,
665
    pub _1: u32,
666
    pub _2: AzString,
667
}
668

            
669
// (i32, u32, AzString)
670
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
671
#[repr(C)]
672
pub struct GetActiveUniformReturn {
673
    pub _0: i32,
674
    pub _1: u32,
675
    pub _2: AzString,
676
}
677

            
678
#[repr(C)]
679
pub struct GLsyncPtr {
680
    pub ptr: *const c_void, /* *const __GLsync */
681
    pub run_destructor: bool,
682
}
683

            
684
impl Clone for GLsyncPtr {
685
    fn clone(&self) -> Self {
686
        Self {
687
            ptr: self.ptr,
688
            run_destructor: true,
689
        }
690
    }
691
}
692

            
693
impl core::fmt::Debug for GLsyncPtr {
694
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
695
        write!(f, "0x{:0x}", self.ptr as usize)
696
    }
697
}
698

            
699
impl GLsyncPtr {
700
    pub fn new(p: GLsync) -> Self {
701
        Self {
702
            ptr: p as *const c_void,
703
            run_destructor: true,
704
        }
705
    }
706
    pub fn get(self) -> GLsync {
707
        self.ptr as GLsync
708
    }
709
}
710

            
711
impl Drop for GLsyncPtr {
712
    fn drop(&mut self) {
713
        self.run_destructor = false;
714
    }
715
}
716

            
717
/// Each pipeline (window) has its own OpenGL textures. GL Textures can technically
718
/// be shared across pipelines, however this turns out to be very difficult in practice.
719
pub type GlTextureStorage = OrderedMap<Epoch, OrderedMap<ExternalImageId, Texture>>;
720

            
721
/// Non-cleaned up textures. When a GlTexture is registered, it has to stay active as long
722
/// as WebRender needs it for drawing. To transparently do this, we store the epoch that the
723
/// texture was originally created with, and check, **after we have drawn the frame**,
724
/// if there are any textures that need cleanup.
725
///
726
/// Because the Texture2d is wrapped in an Rc, the destructor (which cleans up the OpenGL
727
/// texture) does not run until we remove the textures
728
///
729
/// Note: Because textures could be used after the current draw call (ex. for scrolling),
730
/// the ACTIVE_GL_TEXTURES are indexed by their epoch. Use `renderer.flush_pipeline_info()`
731
/// to see which textures are still active and which ones can be safely removed.
732
///
733
/// See: https://github.com/servo/webrender/issues/2940
734
///
735
/// WARNING: Not thread-safe (however, the Texture itself is thread-unsafe, so it's unlikely to ever
736
/// be misused)
737
static mut ACTIVE_GL_TEXTURES: Option<OrderedMap<DocumentId, GlTextureStorage>> = None;
738

            
739
/// Inserts a new texture into the OpenGL texture cache, returns a new image ID
740
/// for the inserted texture
741
///
742
/// This function exists so azul doesn't have to use `lazy_static` as a dependency
743
pub fn insert_into_active_gl_textures(
744
    document_id: DocumentId,
745
    epoch: Epoch,
746
    texture: Texture,
747
) -> ExternalImageId {
748
    let external_image_id = ExternalImageId::new();
749

            
750
    unsafe {
751
        if ACTIVE_GL_TEXTURES.is_none() {
752
            ACTIVE_GL_TEXTURES = Some(OrderedMap::new());
753
        }
754
        let active_textures = ACTIVE_GL_TEXTURES.as_mut().unwrap();
755
        let active_epochs = active_textures
756
            .entry(document_id)
757
            .or_insert_with(|| OrderedMap::new());
758
        let active_textures_for_epoch = active_epochs
759
            .entry(epoch)
760
            .or_insert_with(|| OrderedMap::new());
761
        active_textures_for_epoch.insert(external_image_id, texture);
762
    }
763

            
764
    external_image_id
765
}
766

            
767
/// Destroys all textures from the given `document_id`
768
/// where the texture is **older** than the given `epoch`.
769
pub fn gl_textures_remove_epochs_from_pipeline(document_id: &DocumentId, epoch: Epoch) {
770
    // TODO: Handle overflow of Epochs correctly (low priority)
771
    unsafe {
772
        let active_textures = match ACTIVE_GL_TEXTURES.as_mut() {
773
            Some(s) => s,
774
            None => return,
775
        };
776

            
777
        let active_epochs = match active_textures.get_mut(document_id) {
778
            Some(s) => s,
779
            None => return,
780
        };
781

            
782
        // NOTE: original code used retain() but that
783
        // doesn't work on no_std
784
        let mut epochs_to_remove = Vec::new();
785

            
786
        for (gl_texture_epoch, _) in active_epochs.iter() {
787
            if *gl_texture_epoch < epoch {
788
                epochs_to_remove.push(*gl_texture_epoch);
789
            }
790
        }
791

            
792
        for epoch in epochs_to_remove {
793
            active_epochs.remove(&epoch);
794
        }
795
    }
796
}
797

            
798
// document_id, epoch, external_image_id
799
pub fn remove_single_texture_from_active_gl_textures(
800
    document_id: &DocumentId,
801
    epoch: &Epoch,
802
    external_image_id: &ExternalImageId,
803
) -> Option<()> {
804
    let mut active_textures = unsafe { ACTIVE_GL_TEXTURES.as_mut()? };
805
    let mut epochs = active_textures.get_mut(document_id)?;
806
    let mut images_in_epoch = epochs.get_mut(epoch)?;
807
    images_in_epoch.remove(external_image_id);
808
    Some(())
809
}
810

            
811
/// Removes a DocumentId from the active epochs
812
pub fn gl_textures_remove_active_pipeline(document_id: &DocumentId) {
813
    unsafe {
814
        let active_textures = match ACTIVE_GL_TEXTURES.as_mut() {
815
            Some(s) => s,
816
            None => return,
817
        };
818
        active_textures.remove(document_id);
819
    }
820
}
821

            
822
/// Destroys all textures, usually done before destroying the OpenGL context
823
pub fn gl_textures_clear_opengl_cache() {
824
    unsafe {
825
        ACTIVE_GL_TEXTURES = None;
826
    }
827
}
828

            
829
// Search all epoch hash maps for the given key
830
// There does not seem to be a way to get the epoch for the key,
831
// so we simply have to search all active epochs
832
//
833
// NOTE: Invalid textures can be generated on minimize / maximize
834
// Luckily, webrender simply ignores an invalid texture, so we don't
835
// need to check whether a window is maximized or minimized - if
836
// we encounter an invalid ID, webrender simply won't draw anything,
837
// but at least it won't crash. Usually invalid textures are also 0x0
838
// pixels large - so it's not like we had anything to draw anyway.
839
pub fn get_opengl_texture(image_key: &ExternalImageId) -> Option<(GLuint, (f32, f32))> {
840
    let active_textures = unsafe { ACTIVE_GL_TEXTURES.as_ref()? };
841
    active_textures
842
        .values()
843
        .flat_map(|active_document| active_document.values())
844
        .find_map(|active_epoch| active_epoch.get(image_key))
845
        .map(|tex| {
846
            (
847
                tex.texture_id,
848
                (tex.size.width as f32, tex.size.height as f32),
849
            )
850
        })
851
}
852

            
853
/// For .get_gl_precision_format(), but ABI-safe - returning an array or a tuple is not ABI-safe
854
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
855
#[repr(C)]
856
pub struct GlShaderPrecisionFormatReturn {
857
    pub _0: GLint,
858
    pub _1: GLint,
859
    pub _2: GLint,
860
}
861

            
862
#[repr(C)]
863
pub struct GlContextPtr {
864
    /// `ManuallyDrop` so the owned `Box` is freed ONLY when `run_destructor` is
865
    /// still set (see `Drop`). The codegen FFI wrappers (`AzTexture` etc.) embed
866
    /// this by value AND have their own `Drop` that `drop_in_place`s the real
867
    /// type first; Rust's drop glue would then drop this field a SECOND time on
868
    /// the same bytes. Gating the `Box` free on `run_destructor` (which the first
869
    /// drop clears in the shared memory) makes that second drop a safe no-op.
870
    /// Layout is unchanged: `ManuallyDrop<Box<T>>` is a single pointer, identical
871
    /// to the old `Box<T>` and to the FFI `*mut c_void`.
872
    pub ptr: ManuallyDrop<Box<Rc<GlContextPtrInner>>>,
873
    /// Whether to force a hardware or software renderer
874
    pub renderer_type: RendererType,
875
    pub run_destructor: bool,
876
}
877

            
878
impl Clone for GlContextPtr {
879
    fn clone(&self) -> Self {
880
        Self {
881
            ptr: ManuallyDrop::new((*self.ptr).clone()),
882
            renderer_type: self.renderer_type.clone(),
883
            run_destructor: true,
884
        }
885
    }
886
}
887

            
888
impl Drop for GlContextPtr {
889
    fn drop(&mut self) {
890
        // Only free the owned Box if this instance still owns it. The FFI wrapper
891
        // double-drop (see the struct doc) hits these same bytes a second time
892
        // with `run_destructor` already cleared by the first drop -> no-op, no
893
        // double-free.
894
        if self.run_destructor {
895
            self.run_destructor = false;
896
            unsafe { ManuallyDrop::drop(&mut self.ptr); }
897
        }
898
    }
899
}
900

            
901
impl GlContextPtr {
902
    pub fn get_svg_shader(&self) -> GLuint {
903
        self.ptr.svg_shader
904
    }
905
    /// Whether this hardware GL context proved usable at construction (the SVG
906
    /// shaders compiled+linked at some GLSL version). `false` means context
907
    /// creation succeeded but the driver can't run our shaders -- the caller
908
    /// should fall back to CPU rendering. Always `false` for a Software context
909
    /// (which never compiles these shaders); only meaningful on the GPU path.
910
    pub fn is_gl_usable(&self) -> bool {
911
        self.ptr.svg_shader != 0
912
    }
913
    /// The GLSL `#version` the driver accepted at construction (e.g. "150" or
914
    /// "300 es"), discovered by the probe. Empty string if the context is
915
    /// unusable / software. Exposed in the API so apps can report/branch on it.
916
    pub fn get_usable_glsl_version(&self) -> AzString {
917
        self.ptr.glsl_version.clone()
918
    }
919
    /// Soft-brush shader program for the GPU painting API (0 if unusable).
920
    pub fn get_brush_shader(&self) -> GLuint {
921
        self.ptr.brush_shader
922
    }
923
    pub fn get_fxaa_shader(&self) -> GLuint {
924
        self.ptr.fxaa_shader
925
    }
926
}
927

            
928
#[repr(C)]
929
pub struct GlContextPtrInner {
930
    pub ptr: Rc<GenericGlContext>,
931
    /// SVG shader program (library-internal use)
932
    pub svg_shader: GLuint,
933
    /// SVG multicolor shader program (library-internal use)
934
    pub svg_multicolor_shader: GLuint,
935
    /// FXAA shader program (library-internal use)
936
    pub fxaa_shader: GLuint,
937
    /// Soft-brush shader program for the GPU painting API (0 if unusable).
938
    pub brush_shader: GLuint,
939
    /// The GLSL `#version` directive that compiled (e.g. "150" or "300 es"),
940
    /// discovered by the probe in `new()`. Empty if the context is unusable.
941
    pub glsl_version: AzString,
942
}
943

            
944
impl Drop for GlContextPtrInner {
945
    fn drop(&mut self) {
946
        self.ptr.delete_program(self.svg_shader);
947
        self.ptr.delete_program(self.svg_multicolor_shader);
948
        self.ptr.delete_program(self.fxaa_shader);
949
        if self.brush_shader != 0 {
950
            self.ptr.delete_program(self.brush_shader);
951
        }
952
    }
953
}
954

            
955
impl_option!(
956
    GlContextPtr,
957
    OptionGlContextPtr,
958
    copy = false,
959
    [Debug, Clone, PartialEq, Eq, PartialOrd, Ord]
960
);
961

            
962
impl core::fmt::Debug for GlContextPtr {
963
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
964
        write!(f, "0x{:0x}", self.as_usize())
965
    }
966
}
967

            
968
static SVG_VERTEX_SHADER: &[u8] = b"#version 150
969

            
970
#if __VERSION__ != 100
971
    #define varying out
972
    #define attribute in
973
#endif
974

            
975
uniform vec2 vBboxSize;
976
uniform mat4 vTransformMatrix;
977

            
978
attribute vec2 vAttrXY;
979

            
980
void main() {
981
    vec4 vTransposed = vec4(vAttrXY, 1.0, 1.0) * vTransformMatrix;
982
    vec2 vTransposedInScreen = vTransposed.xy / vBboxSize;
983
    vec2 vCalcFinal = (vTransposedInScreen * vec2(2.0)) - vec2(1.0);
984
    gl_Position = vec4(vCalcFinal, 1.0, 1.0);
985
}";
986

            
987
static SVG_FRAGMENT_SHADER: &[u8] = b"#version 150
988

            
989
precision highp float;
990

            
991
uniform vec4 fDrawColor;
992

            
993
#if __VERSION__ == 100
994
    #define oFragColor gl_FragColor
995
#else
996
    out vec4 oFragColor;
997
#endif
998

            
999
void main() {
    oFragColor = fDrawColor;
}";
static SVG_MULTICOLOR_VERTEX_SHADER: &[u8] = b"#version 150
#if __VERSION__ != 100
    #define varying out
    #define attribute in
#endif
uniform vec2 vBboxSize;
uniform mat4 vTransformMatrix;
attribute vec3 vAttrXY;
attribute vec4 vColor;
varying vec4 fColor;
void main() {
    vec4 vTransposed = vec4(vAttrXY.xy, 1.0, 1.0) * vTransformMatrix;
    vec2 vTransposedInScreen = vTransposed.xy / vBboxSize;
    vec2 vCalcFinal = (vTransposedInScreen * vec2(2.0)) - vec2(1.0);
    gl_Position = vec4(vCalcFinal, vAttrXY.z, 1.0);
    fColor = vColor;
}";
static SVG_MULTICOLOR_FRAGMENT_SHADER: &[u8] = b"#version 150
precision highp float;
#if __VERSION__ != 100
    #define varying in
#endif
#if __VERSION__ == 100
    #define oFragColor gl_FragColor
#else
    out vec4 oFragColor;
#endif
varying vec4 fColor;
void main() {
    oFragColor = fColor;
}";
// Soft-brush shaders for the GPU painting API. A unit quad [-1,1]^2 (aUv) is
// positioned in NDC (aPos); the fragment computes the same radial falloff as
// the CPU `brush_dab_coverage` (1 - smoothstep(hardness, 1, dist)) so GPU and
// CPU strokes match. Version-agnostic via `__VERSION__` like the SVG shaders.
static BRUSH_VERTEX_SHADER: &[u8] = b"#version 150
#if __VERSION__ != 100
    #define varying out
    #define attribute in
#endif
attribute vec2 aPos;
attribute vec2 aUv;
varying vec2 vUv;
void main() {
    vUv = aUv;
    gl_Position = vec4(aPos, 0.0, 1.0);
}";
static BRUSH_FRAGMENT_SHADER: &[u8] = b"#version 150
precision highp float;
#if __VERSION__ != 100
    #define varying in
#endif
#if __VERSION__ == 100
    #define oFragColor gl_FragColor
#else
    out vec4 oFragColor;
#endif
uniform vec4 uColor;     // rgb + alpha (alpha already folds in flow * color.a)
uniform float uHardness; // 0 = soft .. 1 = hard edge
varying vec2 vUv;
void main() {
    float d = length(vUv);
    if (d > 1.0) { discard; }
    float edge0 = clamp(uHardness, 0.0, 1.0);
    float x = clamp((d - edge0) / max(1.0 - edge0, 1.0e-4), 0.0, 1.0);
    float cov = 1.0 - (x * x * (3.0 - 2.0 * x));
    oFragColor = vec4(uColor.rgb, uColor.a * cov);
}";
/// Checks if a shader compiled successfully. Logs an error under `std`.
/// (Retained for diagnostics; the version probe in `GlContextPtr::new` now does
/// its own status checks.)
#[allow(dead_code)]
fn check_shader_compile(gl_context: &GenericGlContext, shader: GLuint, _label: &str) {
    let mut status = [0_i32];
    unsafe { gl_context.get_shader_iv(shader, gl::COMPILE_STATUS, &mut status) };
    if status[0] != gl::TRUE as i32 {
        #[cfg(feature = "std")]
        {
            let log = gl_context.get_shader_info_log(shader);
            eprintln!("azul: {} shader compile error: {}", _label, log);
        }
    }
}
/// Checks if a program linked successfully. Logs an error under `std`.
#[allow(dead_code)]
fn check_program_link(gl_context: &GenericGlContext, program: GLuint, _label: &str) {
    let mut status = [0_i32];
    unsafe { gl_context.get_program_iv(program, gl::LINK_STATUS, &mut status) };
    if status[0] != gl::TRUE as i32 {
        #[cfg(feature = "std")]
        {
            let log = gl_context.get_program_info_log(program);
            eprintln!("azul: {} program link error: {}", _label, log);
        }
    }
}
/// Swap the leading `#version ...` line of a bundled shader for `version_line`
/// (which must include the trailing newline). The shader bodies branch on
/// `__VERSION__`, so only the directive needs to change between GL and GLES.
#[cfg(feature = "std")]
fn shader_with_glsl_version(src: &[u8], version_line: &[u8]) -> Vec<u8> {
    let body_start = src.iter().position(|&b| b == b'\n').map(|i| i + 1).unwrap_or(0);
    let mut out = Vec::with_capacity(version_line.len() + src.len() - body_start);
    out.extend_from_slice(version_line);
    out.extend_from_slice(&src[body_start..]);
    out
}
/// Try to compile+link a vertex+fragment program at a specific GLSL `#version`.
/// Returns the linked program id, or `None` (after cleanup) on ANY compile or
/// link failure. This is how we PROVE a GL context is actually usable and which
/// `#version` its driver accepts -- creating a context can succeed yet leave it
/// unable to compile our shaders (broken driver, or a GLES context that rejects
/// the desktop `#version 150`).
#[cfg(feature = "std")]
fn try_compile_program(
    gl_context: &GenericGlContext,
    vert_src: &[u8],
    frag_src: &[u8],
    version_line: &[u8],
    attribs: &[(u32, &str)],
) -> Option<GLuint> {
    let vs = gl_context.create_shader(gl::VERTEX_SHADER);
    gl_context.shader_source(vs, &[shader_with_glsl_version(vert_src, version_line).as_slice()]);
    gl_context.compile_shader(vs);
    let fs = gl_context.create_shader(gl::FRAGMENT_SHADER);
    gl_context.shader_source(fs, &[shader_with_glsl_version(frag_src, version_line).as_slice()]);
    gl_context.compile_shader(fs);
    let mut s = [0_i32];
    unsafe { gl_context.get_shader_iv(vs, gl::COMPILE_STATUS, &mut s) };
    let vs_ok = s[0] == gl::TRUE as i32;
    unsafe { gl_context.get_shader_iv(fs, gl::COMPILE_STATUS, &mut s) };
    let fs_ok = s[0] == gl::TRUE as i32;
    if !vs_ok || !fs_ok {
        gl_context.delete_shader(vs);
        gl_context.delete_shader(fs);
        return None;
    }
    let prog = gl_context.create_program();
    gl_context.attach_shader(prog, vs);
    gl_context.attach_shader(prog, fs);
    for (loc, name) in attribs {
        gl_context.bind_attrib_location(prog, *loc, (*name).into());
    }
    gl_context.link_program(prog);
    gl_context.delete_shader(vs);
    gl_context.delete_shader(fs);
    let mut l = [0_i32];
    unsafe { gl_context.get_program_iv(prog, gl::LINK_STATUS, &mut l) };
    if l[0] == gl::TRUE as i32 {
        Some(prog)
    } else {
        gl_context.delete_program(prog);
        None
    }
}
/// GLSL `#version` directives to try, in preference order, per context type.
/// The first that compiles+links the SVG shaders is used for every program.
fn glsl_version_candidates(gl_type: GlType) -> &'static [&'static [u8]] {
    match gl_type {
        GlType::Gl => &[b"#version 150\n", b"#version 330\n", b"#version 140\n"],
        GlType::Gles => &[b"#version 300 es\n", b"#version 100\n"],
    }
}
impl GlContextPtr {
    pub fn new(renderer_type: RendererType, gl_context: Rc<GenericGlContext>) -> Self {
        // Only attempt the SVG/FXAA GL shaders for a real GPU. In Software/CPU
        // mode nothing composites through them.
        //
        // PROVE the context is usable rather than trusting context creation: try
        // compiling the SVG program at each candidate `#version` for this context
        // type (desktop GL 1.50/3.30/1.40, or GLES 3.00/1.00) and use the first
        // that compiles+links for ALL programs. A GLES GPU (mobile) rejects the
        // desktop `#version 150`, and a broken driver rejects everything -- in the
        // latter case all program IDs stay 0 and `is_gl_usable()` returns false so
        // the window can fall back to CPU rendering.
        #[cfg(feature = "std")]
        let (svg_program_id, svg_multicolor_program_id, fxaa_program_id, brush_program_id, glsl_version) =
            if matches!(renderer_type, RendererType::Hardware) {
                use crate::gl_fxaa::{FXAA_FRAGMENT_SHADER, FXAA_VERTEX_SHADER};
                let gl_type: GlType = gl_context.get_type().into();
                // Probe via the SVG program; the first version that links wins.
                let mut svg = 0;
                let mut chosen: Option<&'static [u8]> = None;
                for ver in glsl_version_candidates(gl_type) {
                    if let Some(p) = try_compile_program(
                        &gl_context, SVG_VERTEX_SHADER, SVG_FRAGMENT_SHADER, ver, &[(0, "vAttrXY")],
                    ) {
                        svg = p;
                        chosen = Some(ver);
                        break;
                    }
                }
                match chosen {
                    Some(ver) => {
                        // "150" / "300 es": the directive minus "#version " and newline.
                        let ver_str: AzString = core::str::from_utf8(ver)
                            .unwrap_or("")
                            .trim()
                            .trim_start_matches("#version ")
                            .into();
                        eprintln!(
                            "azul: GL usable -- shaders compiled at GLSL {} ({:?})",
                            ver_str.as_str(),
                            gl_type
                        );
                        let mc = try_compile_program(
                            &gl_context, SVG_MULTICOLOR_VERTEX_SHADER, SVG_MULTICOLOR_FRAGMENT_SHADER,
                            ver, &[(0, "vAttrXY"), (1, "vColor")],
                        ).unwrap_or(0);
                        let fxaa = try_compile_program(
                            &gl_context, FXAA_VERTEX_SHADER, FXAA_FRAGMENT_SHADER, ver, &[(0, "vAttrXY")],
                        ).unwrap_or(0);
                        let brush = try_compile_program(
                            &gl_context, BRUSH_VERTEX_SHADER, BRUSH_FRAGMENT_SHADER, ver,
                            &[(0, "aPos"), (1, "aUv")],
                        ).unwrap_or(0);
                        (svg, mc, fxaa, brush, ver_str)
                    }
                    None => {
                        eprintln!(
                            "azul: GL context UNUSABLE -- no GLSL version ({:?}) compiled the SVG \
                             shaders; the window should fall back to CPU rendering (is_gl_usable()=false)",
                            gl_type
                        );
                        (0, 0, 0, 0, AzString::from_const_str(""))
                    }
                }
            } else {
                (0, 0, 0, 0, AzString::from_const_str(""))
            };
        // no_std build keeps the original behavior (no probe / no shaders).
        #[cfg(not(feature = "std"))]
        let (svg_program_id, svg_multicolor_program_id, fxaa_program_id, brush_program_id, glsl_version) =
            (0u32, 0u32, 0u32, 0u32, AzString::from_const_str(""));
        let me = Self {
            ptr: ManuallyDrop::new(Box::new(Rc::new(GlContextPtrInner {
                svg_shader: svg_program_id,
                svg_multicolor_shader: svg_multicolor_program_id,
                fxaa_shader: fxaa_program_id,
                brush_shader: brush_program_id,
                glsl_version,
                ptr: gl_context,
            }))),
            renderer_type,
            run_destructor: true,
        };
        me
    }
    pub fn get<'a>(&'a self) -> &'a Rc<GenericGlContext> {
        &self.ptr.ptr
    }
    fn as_usize(&self) -> usize {
        (Rc::as_ptr(&self.ptr.ptr) as *const c_void) as usize
    }
}
impl GlContextPtr {
    pub fn get_type(&self) -> GlType {
        self.get().get_type().into()
    }
    pub fn buffer_data_untyped(
        &self,
        target: GLenum,
        size: GLsizeiptr,
        data: GlVoidPtrConst,
        usage: GLenum,
    ) {
        self.get()
            .buffer_data_untyped(target, size, data.ptr, usage)
    }
    pub fn buffer_sub_data_untyped(
        &self,
        target: GLenum,
        offset: isize,
        size: GLsizeiptr,
        data: GlVoidPtrConst,
    ) {
        self.get()
            .buffer_sub_data_untyped(target, offset, size, data.ptr)
    }
    pub fn map_buffer(&self, target: GLenum, access: GLbitfield) -> GlVoidPtrMut {
        GlVoidPtrMut {
            ptr: self.get().map_buffer(target, access),
        }
    }
    pub fn map_buffer_range(
        &self,
        target: GLenum,
        offset: GLintptr,
        length: GLsizeiptr,
        access: GLbitfield,
    ) -> GlVoidPtrMut {
        GlVoidPtrMut {
            ptr: self.get().map_buffer_range(target, offset, length, access),
        }
    }
    pub fn unmap_buffer(&self, target: GLenum) -> GLboolean {
        self.get().unmap_buffer(target)
    }
    pub fn tex_buffer(&self, target: GLenum, internal_format: GLenum, buffer: GLuint) {
        self.get().tex_buffer(target, internal_format, buffer)
    }
    pub fn shader_source(&self, shader: GLuint, strings: StringVec) {
        fn str_to_bytes(input: &str) -> Vec<u8> {
            let mut v: Vec<u8> = input.into();
            v.push(0);
            v
        }
        let shaders_as_bytes = strings
            .iter()
            .map(|s| str_to_bytes(s.as_str()))
            .collect::<Vec<_>>();
        let shaders_as_bytes = shaders_as_bytes
            .iter()
            .map(|s| s.as_ref())
            .collect::<Vec<_>>();
        self.get().shader_source(shader, &shaders_as_bytes)
    }
    pub fn read_buffer(&self, mode: GLenum) {
        self.get().read_buffer(mode)
    }
    pub fn read_pixels_into_buffer(
        &self,
        x: GLint,
        y: GLint,
        width: GLsizei,
        height: GLsizei,
        format: GLenum,
        pixel_type: GLenum,
        mut dst_buffer: U8VecRefMut,
    ) {
        self.get().read_pixels_into_buffer(
            x,
            y,
            width,
            height,
            format,
            pixel_type,
            dst_buffer.as_mut_slice(),
        )
    }
    pub fn read_pixels(
        &self,
        x: GLint,
        y: GLint,
        width: GLsizei,
        height: GLsizei,
        format: GLenum,
        pixel_type: GLenum,
    ) -> U8Vec {
        self.get()
            .read_pixels(x, y, width, height, format, pixel_type)
            .into()
    }
    pub fn read_pixels_into_pbo(
        &self,
        x: GLint,
        y: GLint,
        width: GLsizei,
        height: GLsizei,
        format: GLenum,
        pixel_type: GLenum,
    ) {
        unsafe {
            self.get()
                .read_pixels_into_pbo(x, y, width, height, format, pixel_type)
        }
    }
    pub fn sample_coverage(&self, value: GLclampf, invert: bool) {
        self.get().sample_coverage(value, invert)
    }
    pub fn polygon_offset(&self, factor: GLfloat, units: GLfloat) {
        self.get().polygon_offset(factor, units)
    }
    pub fn pixel_store_i(&self, name: GLenum, param: GLint) {
        self.get().pixel_store_i(name, param)
    }
    pub fn gen_buffers(&self, n: GLsizei) -> GLuintVec {
        self.get().gen_buffers(n).into()
    }
    pub fn gen_renderbuffers(&self, n: GLsizei) -> GLuintVec {
        self.get().gen_renderbuffers(n).into()
    }
    pub fn gen_framebuffers(&self, n: GLsizei) -> GLuintVec {
        self.get().gen_framebuffers(n).into()
    }
    pub fn gen_textures(&self, n: GLsizei) -> GLuintVec {
        self.get().gen_textures(n).into()
    }
    pub fn gen_vertex_arrays(&self, n: GLsizei) -> GLuintVec {
        self.get().gen_vertex_arrays(n).into()
    }
    pub fn gen_queries(&self, n: GLsizei) -> GLuintVec {
        self.get().gen_queries(n).into()
    }
    pub fn begin_query(&self, target: GLenum, id: GLuint) {
        self.get().begin_query(target, id)
    }
    pub fn end_query(&self, target: GLenum) {
        self.get().end_query(target)
    }
    pub fn query_counter(&self, id: GLuint, target: GLenum) {
        self.get().query_counter(id, target)
    }
    pub fn get_query_object_iv(&self, id: GLuint, pname: GLenum) -> i32 {
        self.get().get_query_object_iv(id, pname)
    }
    pub fn get_query_object_uiv(&self, id: GLuint, pname: GLenum) -> u32 {
        self.get().get_query_object_uiv(id, pname)
    }
    pub fn get_query_object_i64v(&self, id: GLuint, pname: GLenum) -> i64 {
        self.get().get_query_object_i64v(id, pname)
    }
    pub fn get_query_object_ui64v(&self, id: GLuint, pname: GLenum) -> u64 {
        self.get().get_query_object_ui64v(id, pname)
    }
    pub fn delete_queries(&self, queries: GLuintVecRef) {
        self.get().delete_queries(queries.as_slice())
    }
    pub fn delete_vertex_arrays(&self, vertex_arrays: GLuintVecRef) {
        self.get().delete_vertex_arrays(vertex_arrays.as_slice())
    }
    pub fn delete_buffers(&self, buffers: GLuintVecRef) {
        self.get().delete_buffers(buffers.as_slice())
    }
    pub fn delete_renderbuffers(&self, renderbuffers: GLuintVecRef) {
        self.get().delete_renderbuffers(renderbuffers.as_slice())
    }
    pub fn delete_framebuffers(&self, framebuffers: GLuintVecRef) {
        self.get().delete_framebuffers(framebuffers.as_slice())
    }
    pub fn delete_textures(&self, textures: GLuintVecRef) {
        self.get().delete_textures(textures.as_slice())
    }
    pub fn framebuffer_renderbuffer(
        &self,
        target: GLenum,
        attachment: GLenum,
        renderbuffertarget: GLenum,
        renderbuffer: GLuint,
    ) {
        self.get()
            .framebuffer_renderbuffer(target, attachment, renderbuffertarget, renderbuffer)
    }
    pub fn renderbuffer_storage(
        &self,
        target: GLenum,
        internalformat: GLenum,
        width: GLsizei,
        height: GLsizei,
    ) {
        self.get()
            .renderbuffer_storage(target, internalformat, width, height)
    }
    pub fn depth_func(&self, func: GLenum) {
        self.get().depth_func(func)
    }
    pub fn active_texture(&self, texture: GLenum) {
        self.get().active_texture(texture)
    }
    pub fn attach_shader(&self, program: GLuint, shader: GLuint) {
        self.get().attach_shader(program, shader)
    }
    pub fn bind_attrib_location(&self, program: GLuint, index: GLuint, name: &str) {
        self.get()
            .bind_attrib_location(program, index, name)
    }
    pub fn get_uniform_iv(&self, program: GLuint, location: GLint, mut result: GLintVecRefMut) {
        unsafe {
            self.get()
                .get_uniform_iv(program, location, result.as_mut_slice())
        }
    }
    pub fn get_uniform_fv(&self, program: GLuint, location: GLint, mut result: GLfloatVecRefMut) {
        unsafe {
            self.get()
                .get_uniform_fv(program, location, result.as_mut_slice())
        }
    }
    pub fn get_uniform_block_index(&self, program: GLuint, name: &str) -> GLuint {
        self.get().get_uniform_block_index(program, name)
    }
    pub fn get_uniform_indices(&self, program: GLuint, names: RefstrVecRef) -> GLuintVec {
        let names_vec = names
            .as_slice()
            .iter()
            .map(|n| n.as_str())
            .collect::<Vec<_>>();
        self.get().get_uniform_indices(program, &names_vec).into()
    }
    pub fn bind_buffer_base(&self, target: GLenum, index: GLuint, buffer: GLuint) {
        self.get().bind_buffer_base(target, index, buffer)
    }
    pub fn bind_buffer_range(
        &self,
        target: GLenum,
        index: GLuint,
        buffer: GLuint,
        offset: GLintptr,
        size: GLsizeiptr,
    ) {
        self.get()
            .bind_buffer_range(target, index, buffer, offset, size)
    }
    pub fn uniform_block_binding(
        &self,
        program: GLuint,
        uniform_block_index: GLuint,
        uniform_block_binding: GLuint,
    ) {
        self.get()
            .uniform_block_binding(program, uniform_block_index, uniform_block_binding)
    }
    pub fn bind_buffer(&self, target: GLenum, buffer: GLuint) {
        self.get().bind_buffer(target, buffer)
    }
    pub fn bind_vertex_array(&self, vao: GLuint) {
        self.get().bind_vertex_array(vao)
    }
    pub fn bind_renderbuffer(&self, target: GLenum, renderbuffer: GLuint) {
        self.get().bind_renderbuffer(target, renderbuffer)
    }
    pub fn bind_framebuffer(&self, target: GLenum, framebuffer: GLuint) {
        self.get().bind_framebuffer(target, framebuffer)
    }
    pub fn bind_texture(&self, target: GLenum, texture: GLuint) {
        self.get().bind_texture(target, texture)
    }
    pub fn draw_buffers(&self, bufs: GLenumVecRef) {
        self.get().draw_buffers(bufs.as_slice())
    }
    pub fn tex_image_2d(
        &self,
        target: GLenum,
        level: GLint,
        internal_format: GLint,
        width: GLsizei,
        height: GLsizei,
        border: GLint,
        format: GLenum,
        ty: GLenum,
        opt_data: OptionU8VecRef,
    ) {
        let opt_data = opt_data.as_option();
        let opt_data: Option<&[u8]> = opt_data.map(|o| o.as_slice());
        self.get().tex_image_2d(
            target,
            level,
            internal_format,
            width,
            height,
            border,
            format,
            ty,
            opt_data,
        )
    }
    pub fn compressed_tex_image_2d(
        &self,
        target: GLenum,
        level: GLint,
        internal_format: GLenum,
        width: GLsizei,
        height: GLsizei,
        border: GLint,
        data: U8VecRef,
    ) {
        self.get().compressed_tex_image_2d(
            target,
            level,
            internal_format,
            width,
            height,
            border,
            data.as_slice(),
        )
    }
    pub fn compressed_tex_sub_image_2d(
        &self,
        target: GLenum,
        level: GLint,
        xoffset: GLint,
        yoffset: GLint,
        width: GLsizei,
        height: GLsizei,
        format: GLenum,
        data: U8VecRef,
    ) {
        self.get().compressed_tex_sub_image_2d(
            target,
            level,
            xoffset,
            yoffset,
            width,
            height,
            format,
            data.as_slice(),
        )
    }
    pub fn tex_image_3d(
        &self,
        target: GLenum,
        level: GLint,
        internal_format: GLint,
        width: GLsizei,
        height: GLsizei,
        depth: GLsizei,
        border: GLint,
        format: GLenum,
        ty: GLenum,
        opt_data: OptionU8VecRef,
    ) {
        let opt_data = opt_data.as_option();
        let opt_data: Option<&[u8]> = opt_data.map(|o| o.as_slice());
        self.get().tex_image_3d(
            target,
            level,
            internal_format,
            width,
            height,
            depth,
            border,
            format,
            ty,
            opt_data,
        )
    }
    pub fn copy_tex_image_2d(
        &self,
        target: GLenum,
        level: GLint,
        internal_format: GLenum,
        x: GLint,
        y: GLint,
        width: GLsizei,
        height: GLsizei,
        border: GLint,
    ) {
        self.get()
            .copy_tex_image_2d(target, level, internal_format, x, y, width, height, border)
    }
    pub fn copy_tex_sub_image_2d(
        &self,
        target: GLenum,
        level: GLint,
        xoffset: GLint,
        yoffset: GLint,
        x: GLint,
        y: GLint,
        width: GLsizei,
        height: GLsizei,
    ) {
        self.get()
            .copy_tex_sub_image_2d(target, level, xoffset, yoffset, x, y, width, height)
    }
    pub fn copy_tex_sub_image_3d(
        &self,
        target: GLenum,
        level: GLint,
        xoffset: GLint,
        yoffset: GLint,
        zoffset: GLint,
        x: GLint,
        y: GLint,
        width: GLsizei,
        height: GLsizei,
    ) {
        self.get().copy_tex_sub_image_3d(
            target, level, xoffset, yoffset, zoffset, x, y, width, height,
        )
    }
    pub fn tex_sub_image_2d(
        &self,
        target: GLenum,
        level: GLint,
        xoffset: GLint,
        yoffset: GLint,
        width: GLsizei,
        height: GLsizei,
        format: GLenum,
        ty: GLenum,
        data: U8VecRef,
    ) {
        self.get().tex_sub_image_2d(
            target,
            level,
            xoffset,
            yoffset,
            width,
            height,
            format,
            ty,
            data.as_slice(),
        )
    }
    pub fn tex_sub_image_2d_pbo(
        &self,
        target: GLenum,
        level: GLint,
        xoffset: GLint,
        yoffset: GLint,
        width: GLsizei,
        height: GLsizei,
        format: GLenum,
        ty: GLenum,
        offset: usize,
    ) {
        self.get().tex_sub_image_2d_pbo(
            target, level, xoffset, yoffset, width, height, format, ty, offset,
        )
    }
    pub fn tex_sub_image_3d(
        &self,
        target: GLenum,
        level: GLint,
        xoffset: GLint,
        yoffset: GLint,
        zoffset: GLint,
        width: GLsizei,
        height: GLsizei,
        depth: GLsizei,
        format: GLenum,
        ty: GLenum,
        data: U8VecRef,
    ) {
        self.get().tex_sub_image_3d(
            target,
            level,
            xoffset,
            yoffset,
            zoffset,
            width,
            height,
            depth,
            format,
            ty,
            data.as_slice(),
        )
    }
    pub fn tex_sub_image_3d_pbo(
        &self,
        target: GLenum,
        level: GLint,
        xoffset: GLint,
        yoffset: GLint,
        zoffset: GLint,
        width: GLsizei,
        height: GLsizei,
        depth: GLsizei,
        format: GLenum,
        ty: GLenum,
        offset: usize,
    ) {
        self.get().tex_sub_image_3d_pbo(
            target, level, xoffset, yoffset, zoffset, width, height, depth, format, ty, offset,
        )
    }
    pub fn tex_storage_2d(
        &self,
        target: GLenum,
        levels: GLint,
        internal_format: GLenum,
        width: GLsizei,
        height: GLsizei,
    ) {
        self.get()
            .tex_storage_2d(target, levels, internal_format, width, height)
    }
    pub fn tex_storage_3d(
        &self,
        target: GLenum,
        levels: GLint,
        internal_format: GLenum,
        width: GLsizei,
        height: GLsizei,
        depth: GLsizei,
    ) {
        self.get()
            .tex_storage_3d(target, levels, internal_format, width, height, depth)
    }
    pub fn get_tex_image_into_buffer(
        &self,
        target: GLenum,
        level: GLint,
        format: GLenum,
        ty: GLenum,
        mut output: U8VecRefMut,
    ) {
        self.get()
            .get_tex_image_into_buffer(target, level, format, ty, output.as_mut_slice())
    }
    pub fn copy_image_sub_data(
        &self,
        src_name: GLuint,
        src_target: GLenum,
        src_level: GLint,
        src_x: GLint,
        src_y: GLint,
        src_z: GLint,
        dst_name: GLuint,
        dst_target: GLenum,
        dst_level: GLint,
        dst_x: GLint,
        dst_y: GLint,
        dst_z: GLint,
        src_width: GLsizei,
        src_height: GLsizei,
        src_depth: GLsizei,
    ) {
        unsafe {
            self.get().copy_image_sub_data(
                src_name, src_target, src_level, src_x, src_y, src_z, dst_name, dst_target,
                dst_level, dst_x, dst_y, dst_z, src_width, src_height, src_depth,
            )
        }
    }
    pub fn invalidate_framebuffer(&self, target: GLenum, attachments: GLenumVecRef) {
        self.get()
            .invalidate_framebuffer(target, attachments.as_slice())
    }
    pub fn invalidate_sub_framebuffer(
        &self,
        target: GLenum,
        attachments: GLenumVecRef,
        xoffset: GLint,
        yoffset: GLint,
        width: GLsizei,
        height: GLsizei,
    ) {
        self.get().invalidate_sub_framebuffer(
            target,
            attachments.as_slice(),
            xoffset,
            yoffset,
            width,
            height,
        )
    }
    pub fn get_integer_v(&self, name: GLenum, mut result: GLintVecRefMut) {
        unsafe { self.get().get_integer_v(name, result.as_mut_slice()) }
    }
    pub fn get_integer_64v(&self, name: GLenum, mut result: GLint64VecRefMut) {
        unsafe { self.get().get_integer_64v(name, result.as_mut_slice()) }
    }
    pub fn get_integer_iv(&self, name: GLenum, index: GLuint, mut result: GLintVecRefMut) {
        unsafe {
            self.get()
                .get_integer_iv(name, index, result.as_mut_slice())
        }
    }
    pub fn get_integer_64iv(&self, name: GLenum, index: GLuint, mut result: GLint64VecRefMut) {
        unsafe {
            self.get()
                .get_integer_64iv(name, index, result.as_mut_slice())
        }
    }
    pub fn get_boolean_v(&self, name: GLenum, mut result: GLbooleanVecRefMut) {
        unsafe { self.get().get_boolean_v(name, result.as_mut_slice()) }
    }
    pub fn get_float_v(&self, name: GLenum, mut result: GLfloatVecRefMut) {
        unsafe { self.get().get_float_v(name, result.as_mut_slice()) }
    }
    pub fn get_framebuffer_attachment_parameter_iv(
        &self,
        target: GLenum,
        attachment: GLenum,
        pname: GLenum,
    ) -> GLint {
        self.get()
            .get_framebuffer_attachment_parameter_iv(target, attachment, pname)
    }
    pub fn get_renderbuffer_parameter_iv(&self, target: GLenum, pname: GLenum) -> GLint {
        self.get().get_renderbuffer_parameter_iv(target, pname)
    }
    pub fn get_tex_parameter_iv(&self, target: GLenum, name: GLenum) -> GLint {
        self.get().get_tex_parameter_iv(target, name)
    }
    pub fn get_tex_parameter_fv(&self, target: GLenum, name: GLenum) -> GLfloat {
        self.get().get_tex_parameter_fv(target, name)
    }
    pub fn tex_parameter_i(&self, target: GLenum, pname: GLenum, param: GLint) {
        self.get().tex_parameter_i(target, pname, param)
    }
    pub fn tex_parameter_f(&self, target: GLenum, pname: GLenum, param: GLfloat) {
        self.get().tex_parameter_f(target, pname, param)
    }
    pub fn framebuffer_texture_2d(
        &self,
        target: GLenum,
        attachment: GLenum,
        textarget: GLenum,
        texture: GLuint,
        level: GLint,
    ) {
        self.get()
            .framebuffer_texture_2d(target, attachment, textarget, texture, level)
    }
    pub fn framebuffer_texture_layer(
        &self,
        target: GLenum,
        attachment: GLenum,
        texture: GLuint,
        level: GLint,
        layer: GLint,
    ) {
        self.get()
            .framebuffer_texture_layer(target, attachment, texture, level, layer)
    }
    pub fn blit_framebuffer(
        &self,
        src_x0: GLint,
        src_y0: GLint,
        src_x1: GLint,
        src_y1: GLint,
        dst_x0: GLint,
        dst_y0: GLint,
        dst_x1: GLint,
        dst_y1: GLint,
        mask: GLbitfield,
        filter: GLenum,
    ) {
        self.get().blit_framebuffer(
            src_x0, src_y0, src_x1, src_y1, dst_x0, dst_y0, dst_x1, dst_y1, mask, filter,
        )
    }
    pub fn vertex_attrib_4f(&self, index: GLuint, x: GLfloat, y: GLfloat, z: GLfloat, w: GLfloat) {
        self.get().vertex_attrib_4f(index, x, y, z, w)
    }
    pub fn vertex_attrib_pointer_f32(
        &self,
        index: GLuint,
        size: GLint,
        normalized: bool,
        stride: GLsizei,
        offset: GLuint,
    ) {
        self.get()
            .vertex_attrib_pointer_f32(index, size, normalized, stride, offset)
    }
    pub fn vertex_attrib_pointer(
        &self,
        index: GLuint,
        size: GLint,
        type_: GLenum,
        normalized: bool,
        stride: GLsizei,
        offset: GLuint,
    ) {
        self.get()
            .vertex_attrib_pointer(index, size, type_, normalized, stride, offset)
    }
    pub fn vertex_attrib_i_pointer(
        &self,
        index: GLuint,
        size: GLint,
        type_: GLenum,
        stride: GLsizei,
        offset: GLuint,
    ) {
        self.get()
            .vertex_attrib_i_pointer(index, size, type_, stride, offset)
    }
    pub fn vertex_attrib_divisor(&self, index: GLuint, divisor: GLuint) {
        self.get().vertex_attrib_divisor(index, divisor)
    }
    pub fn viewport(&self, x: GLint, y: GLint, width: GLsizei, height: GLsizei) {
        self.get().viewport(x, y, width, height)
    }
    pub fn scissor(&self, x: GLint, y: GLint, width: GLsizei, height: GLsizei) {
        self.get().scissor(x, y, width, height)
    }
    pub fn line_width(&self, width: GLfloat) {
        self.get().line_width(width)
    }
    pub fn use_program(&self, program: GLuint) {
        self.get().use_program(program)
    }
    pub fn validate_program(&self, program: GLuint) {
        self.get().validate_program(program)
    }
    pub fn draw_arrays(&self, mode: GLenum, first: GLint, count: GLsizei) {
        self.get().draw_arrays(mode, first, count)
    }
    pub fn draw_arrays_instanced(
        &self,
        mode: GLenum,
        first: GLint,
        count: GLsizei,
        primcount: GLsizei,
    ) {
        self.get()
            .draw_arrays_instanced(mode, first, count, primcount)
    }
    pub fn draw_elements(
        &self,
        mode: GLenum,
        count: GLsizei,
        element_type: GLenum,
        indices_offset: GLuint,
    ) {
        self.get()
            .draw_elements(mode, count, element_type, indices_offset)
    }
    pub fn draw_elements_instanced(
        &self,
        mode: GLenum,
        count: GLsizei,
        element_type: GLenum,
        indices_offset: GLuint,
        primcount: GLsizei,
    ) {
        self.get()
            .draw_elements_instanced(mode, count, element_type, indices_offset, primcount)
    }
    pub fn blend_color(&self, r: f32, g: f32, b: f32, a: f32) {
        self.get().blend_color(r, g, b, a)
    }
    pub fn blend_func(&self, sfactor: GLenum, dfactor: GLenum) {
        self.get().blend_func(sfactor, dfactor)
    }
    pub fn blend_func_separate(
        &self,
        src_rgb: GLenum,
        dest_rgb: GLenum,
        src_alpha: GLenum,
        dest_alpha: GLenum,
    ) {
        self.get()
            .blend_func_separate(src_rgb, dest_rgb, src_alpha, dest_alpha)
    }
    pub fn blend_equation(&self, mode: GLenum) {
        self.get().blend_equation(mode)
    }
    pub fn blend_equation_separate(&self, mode_rgb: GLenum, mode_alpha: GLenum) {
        self.get().blend_equation_separate(mode_rgb, mode_alpha)
    }
    pub fn color_mask(&self, r: bool, g: bool, b: bool, a: bool) {
        self.get().color_mask(r, g, b, a)
    }
    pub fn cull_face(&self, mode: GLenum) {
        self.get().cull_face(mode)
    }
    pub fn front_face(&self, mode: GLenum) {
        self.get().front_face(mode)
    }
    pub fn enable(&self, cap: GLenum) {
        self.get().enable(cap)
    }
    pub fn disable(&self, cap: GLenum) {
        self.get().disable(cap)
    }
    pub fn hint(&self, param_name: GLenum, param_val: GLenum) {
        self.get().hint(param_name, param_val)
    }
    pub fn is_enabled(&self, cap: GLenum) -> GLboolean {
        self.get().is_enabled(cap)
    }
    pub fn is_shader(&self, shader: GLuint) -> GLboolean {
        self.get().is_shader(shader)
    }
    pub fn is_texture(&self, texture: GLenum) -> GLboolean {
        self.get().is_texture(texture)
    }
    pub fn is_framebuffer(&self, framebuffer: GLenum) -> GLboolean {
        self.get().is_framebuffer(framebuffer)
    }
    pub fn is_renderbuffer(&self, renderbuffer: GLenum) -> GLboolean {
        self.get().is_renderbuffer(renderbuffer)
    }
    pub fn check_frame_buffer_status(&self, target: GLenum) -> GLenum {
        self.get().check_frame_buffer_status(target)
    }
    pub fn enable_vertex_attrib_array(&self, index: GLuint) {
        self.get().enable_vertex_attrib_array(index)
    }
    pub fn disable_vertex_attrib_array(&self, index: GLuint) {
        self.get().disable_vertex_attrib_array(index)
    }
    pub fn uniform_1f(&self, location: GLint, v0: GLfloat) {
        self.get().uniform_1f(location, v0)
    }
    pub fn uniform_1fv(&self, location: GLint, values: F32VecRef) {
        self.get().uniform_1fv(location, values.as_slice())
    }
    pub fn uniform_1i(&self, location: GLint, v0: GLint) {
        self.get().uniform_1i(location, v0)
    }
    pub fn uniform_1iv(&self, location: GLint, values: I32VecRef) {
        self.get().uniform_1iv(location, values.as_slice())
    }
    pub fn uniform_1ui(&self, location: GLint, v0: GLuint) {
        self.get().uniform_1ui(location, v0)
    }
    pub fn uniform_2f(&self, location: GLint, v0: GLfloat, v1: GLfloat) {
        self.get().uniform_2f(location, v0, v1)
    }
    pub fn uniform_2fv(&self, location: GLint, values: F32VecRef) {
        self.get().uniform_2fv(location, values.as_slice())
    }
    pub fn uniform_2i(&self, location: GLint, v0: GLint, v1: GLint) {
        self.get().uniform_2i(location, v0, v1)
    }
    pub fn uniform_2iv(&self, location: GLint, values: I32VecRef) {
        self.get().uniform_2iv(location, values.as_slice())
    }
    pub fn uniform_2ui(&self, location: GLint, v0: GLuint, v1: GLuint) {
        self.get().uniform_2ui(location, v0, v1)
    }
    pub fn uniform_3f(&self, location: GLint, v0: GLfloat, v1: GLfloat, v2: GLfloat) {
        self.get().uniform_3f(location, v0, v1, v2)
    }
    pub fn uniform_3fv(&self, location: GLint, values: F32VecRef) {
        self.get().uniform_3fv(location, values.as_slice())
    }
    pub fn uniform_3i(&self, location: GLint, v0: GLint, v1: GLint, v2: GLint) {
        self.get().uniform_3i(location, v0, v1, v2)
    }
    pub fn uniform_3iv(&self, location: GLint, values: I32VecRef) {
        self.get().uniform_3iv(location, values.as_slice())
    }
    pub fn uniform_3ui(&self, location: GLint, v0: GLuint, v1: GLuint, v2: GLuint) {
        self.get().uniform_3ui(location, v0, v1, v2)
    }
    pub fn uniform_4f(&self, location: GLint, x: GLfloat, y: GLfloat, z: GLfloat, w: GLfloat) {
        self.get().uniform_4f(location, x, y, z, w)
    }
    pub fn uniform_4i(&self, location: GLint, x: GLint, y: GLint, z: GLint, w: GLint) {
        self.get().uniform_4i(location, x, y, z, w)
    }
    pub fn uniform_4iv(&self, location: GLint, values: I32VecRef) {
        self.get().uniform_4iv(location, values.as_slice())
    }
    pub fn uniform_4ui(&self, location: GLint, x: GLuint, y: GLuint, z: GLuint, w: GLuint) {
        self.get().uniform_4ui(location, x, y, z, w)
    }
    pub fn uniform_4fv(&self, location: GLint, values: F32VecRef) {
        self.get().uniform_4fv(location, values.as_slice())
    }
    pub fn uniform_matrix_2fv(&self, location: GLint, transpose: bool, value: F32VecRef) {
        self.get()
            .uniform_matrix_2fv(location, transpose, value.as_slice())
    }
    pub fn uniform_matrix_3fv(&self, location: GLint, transpose: bool, value: F32VecRef) {
        self.get()
            .uniform_matrix_3fv(location, transpose, value.as_slice())
    }
    pub fn uniform_matrix_4fv(&self, location: GLint, transpose: bool, value: F32VecRef) {
        self.get()
            .uniform_matrix_4fv(location, transpose, value.as_slice())
    }
    pub fn depth_mask(&self, flag: bool) {
        self.get().depth_mask(flag)
    }
    pub fn depth_range(&self, near: f64, far: f64) {
        self.get().depth_range(near, far)
    }
    pub fn get_active_attrib(&self, program: GLuint, index: GLuint) -> GetActiveAttribReturn {
        let r = self.get().get_active_attrib(program, index);
        GetActiveAttribReturn {
            _0: r.0,
            _1: r.1,
            _2: r.2.into(),
        }
    }
    pub fn get_active_uniform(&self, program: GLuint, index: GLuint) -> GetActiveUniformReturn {
        let r = self.get().get_active_uniform(program, index);
        GetActiveUniformReturn {
            _0: r.0,
            _1: r.1,
            _2: r.2.into(),
        }
    }
    pub fn get_active_uniforms_iv(
        &self,
        program: GLuint,
        indices: GLuintVec,
        pname: GLenum,
    ) -> GLintVec {
        self.get()
            .get_active_uniforms_iv(program, indices.into_library_owned_vec(), pname)
            .into()
    }
    pub fn get_active_uniform_block_i(
        &self,
        program: GLuint,
        index: GLuint,
        pname: GLenum,
    ) -> GLint {
        self.get().get_active_uniform_block_i(program, index, pname)
    }
    pub fn get_active_uniform_block_iv(
        &self,
        program: GLuint,
        index: GLuint,
        pname: GLenum,
    ) -> GLintVec {
        self.get()
            .get_active_uniform_block_iv(program, index, pname)
            .into()
    }
    pub fn get_active_uniform_block_name(&self, program: GLuint, index: GLuint) -> AzString {
        self.get()
            .get_active_uniform_block_name(program, index)
            .into()
    }
    pub fn get_attrib_location(&self, program: GLuint, name: &str) -> c_int {
        self.get().get_attrib_location(program, name)
    }
    pub fn get_frag_data_location(&self, program: GLuint, name: &str) -> c_int {
        self.get().get_frag_data_location(program, name)
    }
    pub fn get_uniform_location(&self, program: GLuint, name: &str) -> c_int {
        self.get().get_uniform_location(program, name)
    }
    pub fn get_program_info_log(&self, program: GLuint) -> AzString {
        self.get().get_program_info_log(program).into()
    }
    pub fn get_program_iv(&self, program: GLuint, pname: GLenum, mut result: GLintVecRefMut) {
        unsafe {
            self.get()
                .get_program_iv(program, pname, result.as_mut_slice())
        }
    }
    pub fn get_program_binary(&self, program: GLuint) -> GetProgramBinaryReturn {
        let r = self.get().get_program_binary(program);
        GetProgramBinaryReturn {
            _0: r.0.into(),
            _1: r.1,
        }
    }
    pub fn program_binary(&self, program: GLuint, format: GLenum, binary: U8VecRef) {
        self.get()
            .program_binary(program, format, binary.as_slice())
    }
    pub fn program_parameter_i(&self, program: GLuint, pname: GLenum, value: GLint) {
        self.get().program_parameter_i(program, pname, value)
    }
    pub fn get_vertex_attrib_iv(&self, index: GLuint, pname: GLenum, mut result: GLintVecRefMut) {
        unsafe {
            self.get()
                .get_vertex_attrib_iv(index, pname, result.as_mut_slice())
        }
    }
    pub fn get_vertex_attrib_fv(&self, index: GLuint, pname: GLenum, mut result: GLfloatVecRefMut) {
        unsafe {
            self.get()
                .get_vertex_attrib_fv(index, pname, result.as_mut_slice())
        }
    }
    pub fn get_vertex_attrib_pointer_v(&self, index: GLuint, pname: GLenum) -> GLsizeiptr {
        self.get().get_vertex_attrib_pointer_v(index, pname)
    }
    pub fn get_buffer_parameter_iv(&self, target: GLuint, pname: GLenum) -> GLint {
        self.get().get_buffer_parameter_iv(target, pname)
    }
    pub fn get_shader_info_log(&self, shader: GLuint) -> AzString {
        self.get().get_shader_info_log(shader).into()
    }
    pub fn get_string(&self, which: GLenum) -> AzString {
        self.get().get_string(which).into()
    }
    pub fn get_string_i(&self, which: GLenum, index: GLuint) -> AzString {
        self.get().get_string_i(which, index).into()
    }
    pub fn get_shader_iv(&self, shader: GLuint, pname: GLenum, mut result: GLintVecRefMut) {
        unsafe {
            self.get()
                .get_shader_iv(shader, pname, result.as_mut_slice())
        }
    }
    pub fn get_shader_precision_format(
        &self,
        shader_type: GLuint,
        precision_type: GLuint,
    ) -> GlShaderPrecisionFormatReturn {
        let r = self
            .get()
            .get_shader_precision_format(shader_type, precision_type);
        GlShaderPrecisionFormatReturn {
            _0: r.0,
            _1: r.1,
            _2: r.2,
        }
    }
    pub fn compile_shader(&self, shader: GLuint) {
        self.get().compile_shader(shader)
    }
    pub fn create_program(&self) -> GLuint {
        self.get().create_program()
    }
    pub fn delete_program(&self, program: GLuint) {
        self.get().delete_program(program)
    }
    pub fn create_shader(&self, shader_type: GLenum) -> GLuint {
        self.get().create_shader(shader_type)
    }
    pub fn delete_shader(&self, shader: GLuint) {
        self.get().delete_shader(shader)
    }
    pub fn detach_shader(&self, program: GLuint, shader: GLuint) {
        self.get().detach_shader(program, shader)
    }
    pub fn link_program(&self, program: GLuint) {
        self.get().link_program(program)
    }
    pub fn clear_color(&self, r: f32, g: f32, b: f32, a: f32) {
        self.get().clear_color(r, g, b, a)
    }
    pub fn clear(&self, buffer_mask: GLbitfield) {
        self.get().clear(buffer_mask)
    }
    pub fn clear_depth(&self, depth: f64) {
        self.get().clear_depth(depth)
    }
    pub fn clear_stencil(&self, s: GLint) {
        self.get().clear_stencil(s)
    }
    pub fn flush(&self) {
        self.get().flush()
    }
    pub fn finish(&self) {
        self.get().finish()
    }
    pub fn get_error(&self) -> GLenum {
        self.get().get_error()
    }
    pub fn stencil_mask(&self, mask: GLuint) {
        self.get().stencil_mask(mask)
    }
    pub fn stencil_mask_separate(&self, face: GLenum, mask: GLuint) {
        self.get().stencil_mask_separate(face, mask)
    }
    pub fn stencil_func(&self, func: GLenum, ref_: GLint, mask: GLuint) {
        self.get().stencil_func(func, ref_, mask)
    }
    pub fn stencil_func_separate(&self, face: GLenum, func: GLenum, ref_: GLint, mask: GLuint) {
        self.get().stencil_func_separate(face, func, ref_, mask)
    }
    pub fn stencil_op(&self, sfail: GLenum, dpfail: GLenum, dppass: GLenum) {
        self.get().stencil_op(sfail, dpfail, dppass)
    }
    pub fn stencil_op_separate(&self, face: GLenum, sfail: GLenum, dpfail: GLenum, dppass: GLenum) {
        self.get().stencil_op_separate(face, sfail, dpfail, dppass)
    }
    pub fn egl_image_target_texture2d_oes(&self, target: GLenum, image: GlVoidPtrConst) {
        self.get()
            .egl_image_target_texture2d_oes(target, image.ptr as *const gl_context_loader::c_void)
    }
    pub fn generate_mipmap(&self, target: GLenum) {
        self.get().generate_mipmap(target)
    }
    pub fn insert_event_marker_ext(&self, message: &str) {
        self.get().insert_event_marker_ext(message)
    }
    pub fn push_group_marker_ext(&self, message: &str) {
        self.get().push_group_marker_ext(message)
    }
    pub fn pop_group_marker_ext(&self) {
        self.get().pop_group_marker_ext()
    }
    pub fn debug_message_insert_khr(
        &self,
        source: GLenum,
        type_: GLenum,
        id: GLuint,
        severity: GLenum,
        message: &str,
    ) {
        self.get()
            .debug_message_insert_khr(source, type_, id, severity, message)
    }
    pub fn push_debug_group_khr(&self, source: GLenum, id: GLuint, message: &str) {
        self.get()
            .push_debug_group_khr(source, id, message)
    }
    pub fn pop_debug_group_khr(&self) {
        self.get().pop_debug_group_khr()
    }
    pub fn fence_sync(&self, condition: GLenum, flags: GLbitfield) -> GLsyncPtr {
        GLsyncPtr::new(self.get().fence_sync(condition, flags))
    }
    pub fn client_wait_sync(&self, sync: GLsyncPtr, flags: GLbitfield, timeout: GLuint64) -> u32 {
        self.get().client_wait_sync(sync.get(), flags, timeout)
    }
    pub fn wait_sync(&self, sync: GLsyncPtr, flags: GLbitfield, timeout: GLuint64) {
        self.get().wait_sync(sync.get(), flags, timeout)
    }
    pub fn delete_sync(&self, sync: GLsyncPtr) {
        self.get().delete_sync(sync.get())
    }
    pub fn texture_range_apple(&self, target: GLenum, data: U8VecRef) {
        self.get().texture_range_apple(target, data.as_slice())
    }
    pub fn gen_fences_apple(&self, n: GLsizei) -> GLuintVec {
        self.get().gen_fences_apple(n).into()
    }
    pub fn delete_fences_apple(&self, fences: GLuintVecRef) {
        self.get().delete_fences_apple(fences.as_slice())
    }
    pub fn set_fence_apple(&self, fence: GLuint) {
        self.get().set_fence_apple(fence)
    }
    pub fn finish_fence_apple(&self, fence: GLuint) {
        self.get().finish_fence_apple(fence)
    }
    pub fn test_fence_apple(&self, fence: GLuint) {
        self.get().test_fence_apple(fence)
    }
    pub fn test_object_apple(&self, object: GLenum, name: GLuint) -> GLboolean {
        self.get().test_object_apple(object, name)
    }
    pub fn finish_object_apple(&self, object: GLenum, name: GLuint) {
        self.get().finish_object_apple(object, name)
    }
    pub fn get_frag_data_index(&self, program: GLuint, name: &str) -> GLint {
        self.get().get_frag_data_index(program, name)
    }
    pub fn blend_barrier_khr(&self) {
        self.get().blend_barrier_khr()
    }
    pub fn bind_frag_data_location_indexed(
        &self,
        program: GLuint,
        color_number: GLuint,
        index: GLuint,
        name: &str,
    ) {
        self.get()
            .bind_frag_data_location_indexed(program, color_number, index, name)
    }
    pub fn get_debug_messages(&self) -> DebugMessageVec {
        let dmv: Vec<DebugMessage> = self
            .get()
            .get_debug_messages()
            .into_iter()
            .map(|d| DebugMessage {
                message: d.message.into(),
                source: d.source,
                ty: d.ty,
                id: d.id,
                severity: d.severity,
            })
            .collect();
        dmv.into()
    }
    pub fn provoking_vertex_angle(&self, mode: GLenum) {
        self.get().provoking_vertex_angle(mode)
    }
    pub fn gen_vertex_arrays_apple(&self, n: GLsizei) -> GLuintVec {
        self.get().gen_vertex_arrays_apple(n).into()
    }
    pub fn bind_vertex_array_apple(&self, vao: GLuint) {
        self.get().bind_vertex_array_apple(vao)
    }
    pub fn delete_vertex_arrays_apple(&self, vertex_arrays: GLuintVecRef) {
        self.get()
            .delete_vertex_arrays_apple(vertex_arrays.as_slice())
    }
    pub fn copy_texture_chromium(
        &self,
        source_id: GLuint,
        source_level: GLint,
        dest_target: GLenum,
        dest_id: GLuint,
        dest_level: GLint,
        internal_format: GLint,
        dest_type: GLenum,
        unpack_flip_y: GLboolean,
        unpack_premultiply_alpha: GLboolean,
        unpack_unmultiply_alpha: GLboolean,
    ) {
        self.get().copy_texture_chromium(
            source_id,
            source_level,
            dest_target,
            dest_id,
            dest_level,
            internal_format,
            dest_type,
            unpack_flip_y,
            unpack_premultiply_alpha,
            unpack_unmultiply_alpha,
        )
    }
    pub fn copy_sub_texture_chromium(
        &self,
        source_id: GLuint,
        source_level: GLint,
        dest_target: GLenum,
        dest_id: GLuint,
        dest_level: GLint,
        x_offset: GLint,
        y_offset: GLint,
        x: GLint,
        y: GLint,
        width: GLsizei,
        height: GLsizei,
        unpack_flip_y: GLboolean,
        unpack_premultiply_alpha: GLboolean,
        unpack_unmultiply_alpha: GLboolean,
    ) {
        self.get().copy_sub_texture_chromium(
            source_id,
            source_level,
            dest_target,
            dest_id,
            dest_level,
            x_offset,
            y_offset,
            x,
            y,
            width,
            height,
            unpack_flip_y,
            unpack_premultiply_alpha,
            unpack_unmultiply_alpha,
        )
    }
    pub fn egl_image_target_renderbuffer_storage_oes(&self, target: u32, image: GlVoidPtrConst) {
        self.get().egl_image_target_renderbuffer_storage_oes(
            target,
            image.ptr as *const gl_context_loader::c_void,
        )
    }
    pub fn copy_texture_3d_angle(
        &self,
        source_id: GLuint,
        source_level: GLint,
        dest_target: GLenum,
        dest_id: GLuint,
        dest_level: GLint,
        internal_format: GLint,
        dest_type: GLenum,
        unpack_flip_y: GLboolean,
        unpack_premultiply_alpha: GLboolean,
        unpack_unmultiply_alpha: GLboolean,
    ) {
        self.get().copy_texture_3d_angle(
            source_id,
            source_level,
            dest_target,
            dest_id,
            dest_level,
            internal_format,
            dest_type,
            unpack_flip_y,
            unpack_premultiply_alpha,
            unpack_unmultiply_alpha,
        )
    }
    pub fn copy_sub_texture_3d_angle(
        &self,
        source_id: GLuint,
        source_level: GLint,
        dest_target: GLenum,
        dest_id: GLuint,
        dest_level: GLint,
        x_offset: GLint,
        y_offset: GLint,
        z_offset: GLint,
        x: GLint,
        y: GLint,
        z: GLint,
        width: GLsizei,
        height: GLsizei,
        depth: GLsizei,
        unpack_flip_y: GLboolean,
        unpack_premultiply_alpha: GLboolean,
        unpack_unmultiply_alpha: GLboolean,
    ) {
        self.get().copy_sub_texture_3d_angle(
            source_id,
            source_level,
            dest_target,
            dest_id,
            dest_level,
            x_offset,
            y_offset,
            z_offset,
            x,
            y,
            z,
            width,
            height,
            depth,
            unpack_flip_y,
            unpack_premultiply_alpha,
            unpack_unmultiply_alpha,
        )
    }
    pub fn buffer_storage(
        &self,
        target: GLenum,
        size: GLsizeiptr,
        data: GlVoidPtrConst,
        flags: GLbitfield,
    ) {
        self.get().buffer_storage(target, size, data.ptr, flags)
    }
    pub fn flush_mapped_buffer_range(&self, target: GLenum, offset: GLintptr, length: GLsizeiptr) {
        self.get().flush_mapped_buffer_range(target, offset, length)
    }
}
impl PartialEq for GlContextPtr {
    fn eq(&self, rhs: &Self) -> bool {
        self.as_usize().eq(&rhs.as_usize())
    }
}
impl Eq for GlContextPtr {}
impl PartialOrd for GlContextPtr {
    fn partial_cmp(&self, rhs: &Self) -> Option<core::cmp::Ordering> {
        self.as_usize().partial_cmp(&rhs.as_usize())
    }
}
impl Ord for GlContextPtr {
    fn cmp(&self, rhs: &Self) -> core::cmp::Ordering {
        self.as_usize().cmp(&rhs.as_usize())
    }
}
/// Saved OpenGL state for save/restore around framebuffer operations.
/// Used by `Texture::clear()` and `GlShader::draw()` to avoid corrupting
/// the caller's GL state.
struct GlStateSave {
    current_multisample: [u8; 1],
    current_index_buffer: [i32; 1],
    current_vertex_buffer: [i32; 1],
    current_vertex_array_object: [i32; 1],
    current_program: [i32; 1],
    current_framebuffers: [i32; 1],
    current_renderbuffers: [i32; 1],
    current_texture_2d: [i32; 1],
}
impl GlStateSave {
    fn save(gl_context: &GlContextPtr) -> Self {
        let mut s = GlStateSave {
            current_multisample: [0],
            current_index_buffer: [0],
            current_vertex_buffer: [0],
            current_vertex_array_object: [0],
            current_program: [0],
            current_framebuffers: [0],
            current_renderbuffers: [0],
            current_texture_2d: [0],
        };
        gl_context.get_boolean_v(gl::MULTISAMPLE, (&mut s.current_multisample[..]).into());
        gl_context.get_integer_v(gl::ARRAY_BUFFER_BINDING, (&mut s.current_vertex_buffer[..]).into());
        gl_context.get_integer_v(gl::ELEMENT_ARRAY_BUFFER_BINDING, (&mut s.current_index_buffer[..]).into());
        gl_context.get_integer_v(gl::CURRENT_PROGRAM, (&mut s.current_program[..]).into());
        gl_context.get_integer_v(gl::VERTEX_ARRAY_BINDING, (&mut s.current_vertex_array_object[..]).into());
        gl_context.get_integer_v(gl::RENDERBUFFER, (&mut s.current_renderbuffers[..]).into());
        gl_context.get_integer_v(gl::FRAMEBUFFER, (&mut s.current_framebuffers[..]).into());
        gl_context.get_integer_v(gl::TEXTURE_2D, (&mut s.current_texture_2d[..]).into());
        s
    }
    fn restore(&self, gl_context: &GlContextPtr) {
        if u32::from(self.current_multisample[0]) == gl::TRUE {
            gl_context.enable(gl::MULTISAMPLE);
        }
        gl_context.bind_framebuffer(gl::FRAMEBUFFER, self.current_framebuffers[0] as u32);
        gl_context.bind_texture(gl::TEXTURE_2D, self.current_texture_2d[0] as u32);
        gl_context.bind_buffer(gl::RENDERBUFFER, self.current_renderbuffers[0] as u32);
        gl_context.bind_vertex_array(self.current_vertex_array_object[0] as u32);
        gl_context.bind_buffer(gl::ELEMENT_ARRAY_BUFFER, self.current_index_buffer[0] as u32);
        gl_context.bind_buffer(gl::ARRAY_BUFFER, self.current_vertex_buffer[0] as u32);
        gl_context.use_program(self.current_program[0] as u32);
    }
}
/// OpenGL texture, use `ReadOnlyWindow::create_texture` to create a texture
#[repr(C)]
pub struct Texture {
    /// A reference-counted pointer to the OpenGL context (so that the texture can be deleted in
    /// the destructor)
    pub gl_context: GlContextPtr,
    /// Raw OpenGL texture ID
    pub texture_id: GLuint,
    /// Reference count, shared across
    pub refcount: *const AtomicUsize,
    /// Size of this texture (in pixels)
    pub size: PhysicalSizeU32,
    /// Format of the texture (rgba8, brga8, etc.)
    pub format: RawImageFormat,
    /// Background color of this texture
    pub background_color: ColorU,
    /// Hints and flags for optimization purposes
    pub flags: TextureFlags,
    pub run_destructor: bool,
}
impl Clone for Texture {
    fn clone(&self) -> Self {
        unsafe {
            (*self.refcount).fetch_add(1, AtomicOrdering::SeqCst);
        }
        Self {
            gl_context: self.gl_context.clone(),
            texture_id: self.texture_id.clone(),
            refcount: self.refcount,
            size: self.size.clone(),
            format: self.format.clone(),
            background_color: self.background_color.clone(),
            flags: self.flags.clone(),
            run_destructor: true,
        }
    }
}
impl_option!(
    Texture,
    OptionTexture,
    copy = false,
    [Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
);
impl Texture {
    pub fn create(
        texture_id: GLuint,
        flags: TextureFlags,
        size: PhysicalSizeU32,
        background_color: ColorU,
        gl_context: GlContextPtr,
        format: RawImageFormat,
    ) -> Self {
        Self {
            texture_id,
            flags,
            size,
            background_color,
            gl_context,
            format,
            refcount: Box::into_raw(Box::new(AtomicUsize::new(1))),
            run_destructor: true,
        }
    }
    pub fn allocate_rgba8(
        gl_context: GlContextPtr,
        size: PhysicalSizeU32,
        background: ColorU,
    ) -> Self {
        let textures = gl_context.gen_textures(1);
        let texture_id = textures.as_ref()[0];
        let mut current_texture_2d = [0_i32];
        gl_context.get_integer_v(gl::TEXTURE_2D, (&mut current_texture_2d[..]).into());
        gl_context.bind_texture(gl::TEXTURE_2D, texture_id);
        gl_context.tex_image_2d(
            gl::TEXTURE_2D,
            0,
            gl::RGBA as i32,
            size.width as i32,
            size.height as i32,
            0,
            gl::RGBA,
            gl::UNSIGNED_BYTE,
            None.into(),
        );
        gl_context.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as i32);
        gl_context.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::NEAREST as i32);
        gl_context.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as i32);
        gl_context.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as i32);
        gl_context.bind_texture(gl::TEXTURE_2D, current_texture_2d[0] as u32);
        Self::create(
            texture_id,
            TextureFlags {
                is_opaque: false,
                is_video_texture: false,
            },
            size,
            background,
            gl_context,
            // Format is BGRA8 for WebRender integration, despite the GL upload using RGBA
            RawImageFormat::BGRA8,
        )
    }
    pub fn clear(&mut self) {
        let saved = GlStateSave::save(&self.gl_context);
        let framebuffers = self.gl_context.gen_framebuffers(1);
        let framebuffer_id = framebuffers.get(0).unwrap();
        self.gl_context
            .bind_framebuffer(gl::FRAMEBUFFER, *framebuffer_id);
        let depthbuffers = self.gl_context.gen_renderbuffers(1);
        let depthbuffer_id = depthbuffers.get(0).unwrap();
        self.gl_context
            .bind_texture(gl::TEXTURE_2D, self.texture_id);
        self.gl_context.tex_image_2d(
            gl::TEXTURE_2D,
            0,
            gl::RGBA as i32, // NOT RGBA8 - will generate INVALID_ENUM!
            self.size.width as i32,
            self.size.height as i32,
            0,
            gl::RGBA, // gl::BGRA?
            gl::UNSIGNED_BYTE,
            None.into(),
        );
        self.gl_context
            .tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as i32);
        self.gl_context
            .tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::NEAREST as i32);
        self.gl_context.tex_parameter_i(
            gl::TEXTURE_2D,
            gl::TEXTURE_WRAP_S,
            gl::CLAMP_TO_EDGE as i32,
        );
        self.gl_context.tex_parameter_i(
            gl::TEXTURE_2D,
            gl::TEXTURE_WRAP_T,
            gl::CLAMP_TO_EDGE as i32,
        );
        self.gl_context
            .bind_renderbuffer(gl::RENDERBUFFER, *depthbuffer_id);
        self.gl_context.renderbuffer_storage(
            gl::RENDERBUFFER,
            gl::DEPTH_COMPONENT,
            self.size.width as i32,
            self.size.height as i32,
        );
        self.gl_context.framebuffer_renderbuffer(
            gl::FRAMEBUFFER,
            gl::DEPTH_ATTACHMENT,
            gl::RENDERBUFFER,
            *depthbuffer_id,
        );
        self.gl_context.framebuffer_texture_2d(
            gl::FRAMEBUFFER,
            gl::COLOR_ATTACHMENT0,
            gl::TEXTURE_2D,
            self.texture_id,
            0,
        );
        self.gl_context
            .draw_buffers([gl::COLOR_ATTACHMENT0][..].into());
        let clear_color: ColorF = self.background_color.into();
        self.gl_context
            .clear_color(clear_color.r, clear_color.g, clear_color.b, clear_color.a);
        self.gl_context.clear_depth(0.0);
        self.gl_context
            .clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT);
        self.gl_context
            .delete_framebuffers((&[*framebuffer_id])[..].into());
        self.gl_context
            .delete_renderbuffers((&[*depthbuffer_id])[..].into());
        saved.restore(&self.gl_context);
    }
    pub fn get_descriptor(&self) -> ImageDescriptor {
        ImageDescriptor {
            format: self.format,
            width: self.size.width as usize,
            height: self.size.height as usize,
            stride: None.into(),
            offset: 0,
            flags: ImageDescriptorFlags {
                is_opaque: self.flags.is_opaque,
                // The texture gets mapped 1:1 onto the display, so there is no need for mipmaps
                allow_mipmaps: false,
            },
        }
    }
    /// Draws a `TessellatedGPUSvgNode` with the given color to the texture
    pub fn draw_tesselated_svg_gpu_node(
        &mut self,
        node: &TessellatedGPUSvgNode,
        size: PhysicalSizeU32,
        color: ColorU,
        transforms: StyleTransformVec,
    ) -> bool {
        node.draw(self, size, color, transforms)
    }
    /// Draws a `TessellatedColoredGPUSvgNode` to the texture
    pub fn draw_tesselated_colored_svg_gpu_node(
        &mut self,
        node: &crate::svg::TessellatedColoredGPUSvgNode,
        size: PhysicalSizeU32,
        transforms: StyleTransformVec,
    ) -> bool {
        node.draw(self, size, transforms)
    }
}
#[derive(Debug, Default, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[repr(C)]
pub struct TextureFlags {
    /// Whether this texture contains an alpha component
    pub is_opaque: bool,
    /// Optimization: use the compositor instead of OpenGL for energy optimization
    pub is_video_texture: bool,
}
impl ::core::fmt::Display for Texture {
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        write!(
            f,
            "Texture {{ id: {}, {}x{} }}",
            self.texture_id, self.size.width, self.size.height
        )
    }
}
macro_rules! impl_traits_for_gl_object {
    ($struct_name:ident, $gl_id_field:ident) => {
        impl ::core::fmt::Debug for $struct_name {
            fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
                write!(f, "{}", self)
            }
        }
        impl Hash for $struct_name {
            fn hash<H: Hasher>(&self, state: &mut H) {
                self.$gl_id_field.hash(state);
            }
        }
        impl PartialEq for $struct_name {
            fn eq(&self, other: &$struct_name) -> bool {
                self.$gl_id_field == other.$gl_id_field
            }
        }
        impl Eq for $struct_name {}
        impl PartialOrd for $struct_name {
            fn partial_cmp(&self, other: &Self) -> Option<::core::cmp::Ordering> {
                Some((self.$gl_id_field).cmp(&(other.$gl_id_field)))
            }
        }
        impl Ord for $struct_name {
            fn cmp(&self, other: &Self) -> ::core::cmp::Ordering {
                (self.$gl_id_field).cmp(&(other.$gl_id_field))
            }
        }
    };
    ($struct_name:ident < $lt:lifetime > , $gl_id_field:ident) => {
        impl<$lt> ::core::fmt::Debug for $struct_name<$lt> {
            fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
                write!(f, "{}", self)
            }
        }
        impl<$lt> Hash for $struct_name<$lt> {
            fn hash<H: Hasher>(&self, state: &mut H) {
                self.$gl_id_field.hash(state);
            }
        }
        impl<$lt> PartialEq for $struct_name<$lt> {
            fn eq(&self, other: &$struct_name) -> bool {
                self.$gl_id_field == other.$gl_id_field
            }
        }
        impl<$lt> Eq for $struct_name<$lt> {}
        impl<$lt> PartialOrd for $struct_name<$lt> {
            fn partial_cmp(&self, other: &Self) -> Option<::core::cmp::Ordering> {
                Some((self.$gl_id_field).cmp(&(other.$gl_id_field)))
            }
        }
        impl<$lt> Ord for $struct_name<$lt> {
            fn cmp(&self, other: &Self) -> ::core::cmp::Ordering {
                (self.$gl_id_field).cmp(&(other.$gl_id_field))
            }
        }
    };
    ($struct_name:ident < $t:ident : $constraint:ident > , $gl_id_field:ident) => {
        impl<$t: $constraint> ::core::fmt::Debug for $struct_name<$t> {
            fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
                write!(f, "{}", self)
            }
        }
        impl<$t: $constraint> Hash for $struct_name<$t> {
            fn hash<H: Hasher>(&self, state: &mut H) {
                self.$gl_id_field.hash(state);
            }
        }
        impl<$t: $constraint> PartialEq for $struct_name<$t> {
            fn eq(&self, other: &$struct_name<$t>) -> bool {
                self.$gl_id_field == other.$gl_id_field
            }
        }
        impl<$t: $constraint> Eq for $struct_name<$t> {}
        impl<$t: $constraint> PartialOrd for $struct_name<$t> {
            fn partial_cmp(&self, other: &Self) -> Option<::core::cmp::Ordering> {
                Some((self.$gl_id_field).cmp(&(other.$gl_id_field)))
            }
        }
        impl<$t: $constraint> Ord for $struct_name<$t> {
            fn cmp(&self, other: &Self) -> ::core::cmp::Ordering {
                (self.$gl_id_field).cmp(&(other.$gl_id_field))
            }
        }
    };
}
impl Texture {
    /// GPU painting: stamp one soft-brush dab centered at (`cx`, `cy`) in texture
    /// pixel coordinates (origin top-left, matching [`RawImage::paint_dot`]).
    /// No-op if the GL context is unusable -- the caller should then use the CPU
    /// `RawImage` path (`GlContextPtr::is_gl_usable`).
    pub fn paint_dot(&mut self, cx: f32, cy: f32, brush: Brush) {
        self.paint_stroke(cx, cy, cx, cy, brush);
    }
    /// GPU painting: stamp dabs along (`x0`,`y0`)->(`x1`,`y1`) into this texture
    /// via an FBO + the soft-brush shader, alpha-over blended. Same spacing +
    /// falloff as the CPU `RawImage::paint_stroke`. No-op if GL is unusable.
    pub fn paint_stroke(&mut self, x0: f32, y0: f32, x1: f32, y1: f32, brush: Brush) {
        let gl = self.gl_context.clone();
        let prog = gl.get_brush_shader();
        let (tw, th) = (self.size.width as f32, self.size.height as f32);
        if prog == 0 || self.texture_id == 0 || !(brush.radius > 0.0) || tw <= 0.0 || th <= 0.0 {
            return;
        }
        let fbo = gl.gen_framebuffers(1).get(0).copied().unwrap_or(0);
        let vbo = gl.gen_buffers(1).get(0).copied().unwrap_or(0);
        if fbo == 0 || vbo == 0 {
            if fbo != 0 {
                gl.delete_framebuffers((&[fbo][..]).into());
            }
            if vbo != 0 {
                gl.delete_buffers((&[vbo][..]).into());
            }
            return;
        }
        gl.bind_framebuffer(gl::FRAMEBUFFER, fbo);
        gl.framebuffer_texture_2d(
            gl::FRAMEBUFFER,
            gl::COLOR_ATTACHMENT0,
            gl::TEXTURE_2D,
            self.texture_id,
            0,
        );
        gl.viewport(0, 0, self.size.width as i32, self.size.height as i32);
        gl.enable(gl::BLEND);
        gl.blend_func(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
        gl.use_program(prog);
        let a = (brush.color.a as f32 / 255.0) * brush.flow.max(0.0).min(1.0);
        gl.uniform_4f(
            gl.get_uniform_location(prog, "uColor"),
            brush.color.r as f32 / 255.0,
            brush.color.g as f32 / 255.0,
            brush.color.b as f32 / 255.0,
            a,
        );
        gl.uniform_1f(gl.get_uniform_location(prog, "uHardness"), brush.hardness);
        gl.bind_buffer(gl::ARRAY_BUFFER, vbo);
        gl.enable_vertex_attrib_array(0);
        gl.enable_vertex_attrib_array(1);
        gl.vertex_attrib_pointer_f32(0, 2, false, 16, 0);
        gl.vertex_attrib_pointer_f32(1, 2, false, 16, 8);
        let dx = x1 - x0;
        let dy = y1 - y0;
        let len = (dx * dx + dy * dy).sqrt();
        let step = (brush.radius * brush.spacing.max(0.01)).max(0.5);
        let n = ((len / step).floor() as i32).max(0);
        let r = brush.radius;
        for i in 0..=n {
            let t = if n == 0 { 1.0 } else { i as f32 / n as f32 };
            let px = x0 + dx * t;
            let py = y0 + dy * t;
            // dab bbox -> NDC; y is flipped so (0,0) is top-left like the CPU path.
            let nx = |x: f32| (x / tw) * 2.0 - 1.0;
            let ny = |y: f32| 1.0 - (y / th) * 2.0;
            let (l, rr, tp, bt) = (nx(px - r), nx(px + r), ny(py - r), ny(py + r));
            // TRIANGLE_STRIP: TL, BL, TR, BR -- interleaved (pos.x, pos.y, uv.x, uv.y).
            let verts: [f32; 16] = [
                l, tp, -1.0, -1.0, l, bt, -1.0, 1.0, rr, tp, 1.0, -1.0, rr, bt, 1.0, 1.0,
            ];
            gl.buffer_data_untyped(
                gl::ARRAY_BUFFER,
                (verts.len() * core::mem::size_of::<f32>()) as isize,
                GlVoidPtrConst {
                    ptr: verts.as_ptr() as *const GLvoid,
                    run_destructor: false,
                },
                gl::STREAM_DRAW,
            );
            gl.draw_arrays(gl::TRIANGLE_STRIP, 0, 4);
        }
        gl.disable_vertex_attrib_array(0);
        gl.disable_vertex_attrib_array(1);
        gl.bind_buffer(gl::ARRAY_BUFFER, 0);
        gl.disable(gl::BLEND);
        gl.bind_framebuffer(gl::FRAMEBUFFER, 0);
        gl.delete_buffers((&[vbo][..]).into());
        gl.delete_framebuffers((&[fbo][..]).into());
    }
    /// Read this texture's pixels back into an RGBA8 `RawImage` (top-left origin)
    /// -- for exporting the painted canvas to disk. Binds an FBO + glReadPixels.
    pub fn copy_to_raw_image(&self) -> RawImage {
        let gl = self.gl_context.clone();
        let (w, h) = (self.size.width as i32, self.size.height as i32);
        if self.texture_id == 0 || w <= 0 || h <= 0 {
            return RawImage::null_image();
        }
        let fbo = gl.gen_framebuffers(1).get(0).copied().unwrap_or(0);
        if fbo == 0 {
            return RawImage::null_image();
        }
        gl.bind_framebuffer(gl::FRAMEBUFFER, fbo);
        gl.framebuffer_texture_2d(
            gl::FRAMEBUFFER,
            gl::COLOR_ATTACHMENT0,
            gl::TEXTURE_2D,
            self.texture_id,
            0,
        );
        let pixels = gl.read_pixels(0, 0, w, h, gl::RGBA, gl::UNSIGNED_BYTE);
        gl.bind_framebuffer(gl::FRAMEBUFFER, 0);
        gl.delete_framebuffers((&[fbo][..]).into());
        // glReadPixels uses a bottom-left origin; flip rows to top-left for saving.
        let mut bytes = pixels.into_library_owned_vec();
        let row = (w as usize) * 4;
        let hh = h as usize;
        if row > 0 && bytes.len() >= row * hh {
            for y in 0..hh / 2 {
                let yi = y * row;
                let yo = (hh - 1 - y) * row;
                for k in 0..row {
                    bytes.swap(yi + k, yo + k);
                }
            }
        }
        RawImage {
            pixels: RawImageData::U8(bytes.into()),
            width: w as usize,
            height: h as usize,
            premultiplied_alpha: true,
            data_format: RawImageFormat::RGBA8,
            tag: Vec::new().into(),
        }
    }
}
impl_traits_for_gl_object!(Texture, texture_id);
impl Drop for Texture {
    fn drop(&mut self) {
        self.run_destructor = false;
        let copies = unsafe { (*self.refcount).fetch_sub(1, AtomicOrdering::SeqCst) };
        if copies == 1 {
            let _ = unsafe { Box::from_raw(self.refcount as *mut AtomicUsize) };
            self.gl_context
                .delete_textures((&[self.texture_id])[..].into());
        }
    }
}
/// Describes the vertex layout and offsets
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)]
pub struct VertexLayout {
    pub fields: VertexAttributeVec,
}
impl_vec!(VertexAttribute, VertexAttributeVec, VertexAttributeVecDestructor, VertexAttributeVecDestructorType, VertexAttributeVecSlice, OptionVertexAttribute);
impl_vec_debug!(VertexAttribute, VertexAttributeVec);
impl_vec_partialord!(VertexAttribute, VertexAttributeVec);
impl_vec_ord!(VertexAttribute, VertexAttributeVec);
impl_vec_clone!(
    VertexAttribute,
    VertexAttributeVec,
    VertexAttributeVecDestructor
);
impl_vec_partialeq!(VertexAttribute, VertexAttributeVec);
impl_vec_eq!(VertexAttribute, VertexAttributeVec);
impl_vec_hash!(VertexAttribute, VertexAttributeVec);
impl VertexLayout {
    /// Submits the vertex buffer description to OpenGL
    pub fn bind(&self, gl_context: &Rc<GenericGlContext>, program_id: GLuint) {
        const VERTICES_ARE_NORMALIZED: bool = false;
        let mut offset = 0;
        let stride_between_vertices: usize =
            self.fields.iter().map(VertexAttribute::get_stride).sum();
        for vertex_attribute in self.fields.iter() {
            let attribute_location = vertex_attribute
                .layout_location
                .as_option()
                .map(|ll| *ll as i32)
                .unwrap_or_else(|| {
                    gl_context
                        .get_attrib_location(program_id, vertex_attribute.va_name.as_str().into())
                });
            gl_context.vertex_attrib_pointer(
                attribute_location as u32,
                vertex_attribute.item_count as i32,
                vertex_attribute.attribute_type.get_gl_id(),
                VERTICES_ARE_NORMALIZED,
                stride_between_vertices as i32,
                offset as u32,
            );
            gl_context.enable_vertex_attrib_array(attribute_location as u32);
            offset += vertex_attribute.get_stride();
        }
    }
    /// Unsets the vertex buffer description
    pub fn unbind(&self, gl_context: &Rc<GenericGlContext>, program_id: GLuint) {
        for vertex_attribute in self.fields.iter() {
            let attribute_location = vertex_attribute
                .layout_location
                .as_option()
                .map(|ll| *ll as i32)
                .unwrap_or_else(|| {
                    gl_context
                        .get_attrib_location(program_id, vertex_attribute.va_name.as_str().into())
                });
            gl_context.disable_vertex_attrib_array(attribute_location as u32);
        }
    }
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)]
pub struct VertexAttribute {
    /// Attribute name of the vertex attribute in the vertex shader, i.e. `"vAttrXY"`
    pub va_name: AzString,
    /// If the vertex shader has a specific location, (like `layout(location = 2) vAttrXY`),
    /// use this instead of the name to look up the uniform location.
    pub layout_location: OptionUsize,
    /// Type of items of this attribute (i.e. for a `FloatVec2`, would be
    /// `VertexAttributeType::Float`)
    pub attribute_type: VertexAttributeType,
    /// Number of items of this attribute (i.e. for a `FloatVec2`, would be `2` (= 2 consecutive
    /// f32 values))
    pub item_count: usize,
}
impl_option!(
    VertexAttribute,
    OptionVertexAttribute,
    copy = false,
    [Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
);
impl VertexAttribute {
    pub fn get_stride(&self) -> usize {
        self.attribute_type.get_mem_size() * self.item_count
    }
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)]
pub enum VertexAttributeType {
    /// Vertex attribute has type `f32`
    Float,
    /// Vertex attribute has type `f64`
    Double,
    /// Vertex attribute has type `u8`
    UnsignedByte,
    /// Vertex attribute has type `u16`
    UnsignedShort,
    /// Vertex attribute has type `u32`
    UnsignedInt,
}
impl VertexAttributeType {
    /// Returns the OpenGL id for the vertex attribute type, ex. `gl::UNSIGNED_BYTE` for
    /// `VertexAttributeType::UnsignedByte`.
    pub fn get_gl_id(&self) -> GLuint {
        use self::VertexAttributeType::*;
        match self {
            Float => gl::FLOAT,
            Double => gl::DOUBLE,
            UnsignedByte => gl::UNSIGNED_BYTE,
            UnsignedShort => gl::UNSIGNED_SHORT,
            UnsignedInt => gl::UNSIGNED_INT,
        }
    }
    pub fn get_mem_size(&self) -> usize {
        use core::mem;
        use self::VertexAttributeType::*;
        match self {
            Float => mem::size_of::<f32>(),
            Double => mem::size_of::<f64>(),
            UnsignedByte => mem::size_of::<u8>(),
            UnsignedShort => mem::size_of::<u16>(),
            UnsignedInt => mem::size_of::<u32>(),
        }
    }
}
pub trait VertexLayoutDescription {
    fn get_description() -> VertexLayout;
}
#[derive(Debug, PartialEq, PartialOrd)]
#[repr(C)]
pub struct VertexArrayObject {
    pub vertex_layout: VertexLayout,
    pub vao_id: GLuint,
    pub gl_context: GlContextPtr,
    pub refcount: *const AtomicUsize,
    pub run_destructor: bool,
}
impl VertexArrayObject {
    pub fn new(vertex_layout: VertexLayout, vao_id: GLuint, gl_context: GlContextPtr) -> Self {
        Self {
            vertex_layout,
            vao_id,
            gl_context,
            refcount: Box::into_raw(Box::new(AtomicUsize::new(1))),
            run_destructor: true,
        }
    }
}
impl Clone for VertexArrayObject {
    fn clone(&self) -> Self {
        unsafe { (*self.refcount).fetch_add(1, AtomicOrdering::SeqCst) };
        Self {
            vertex_layout: self.vertex_layout.clone(),
            vao_id: self.vao_id,
            gl_context: self.gl_context.clone(),
            refcount: self.refcount,
            run_destructor: true,
        }
    }
}
impl Drop for VertexArrayObject {
    fn drop(&mut self) {
        self.run_destructor = false;
        let copies = unsafe { (*self.refcount).fetch_sub(1, AtomicOrdering::SeqCst) };
        if copies == 1 {
            let _ = unsafe { Box::from_raw(self.refcount as *mut AtomicUsize) };
            self.gl_context
                .delete_vertex_arrays((&[self.vao_id])[..].into());
        }
    }
}
#[repr(C)]
pub struct VertexBuffer {
    pub vao: VertexArrayObject,
    pub vertex_buffer_id: GLuint,
    pub vertex_buffer_len: usize,
    pub index_buffer_id: GLuint,
    pub index_buffer_len: usize,
    pub refcount: *const AtomicUsize,
    pub index_buffer_format: IndexBufferFormat,
    pub run_destructor: bool,
}
impl core::fmt::Display for VertexBuffer {
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
        write!(
            f,
            "VertexBuffer {{ buffer: {} (length: {}) }})",
            self.vertex_buffer_id, self.vertex_buffer_len
        )
    }
}
impl_traits_for_gl_object!(VertexBuffer, vertex_buffer_id);
impl Clone for VertexBuffer {
    fn clone(&self) -> Self {
        unsafe { (*self.refcount).fetch_add(1, AtomicOrdering::SeqCst) };
        Self {
            vao: self.vao.clone(),
            vertex_buffer_id: self.vertex_buffer_id.clone(),
            vertex_buffer_len: self.vertex_buffer_len.clone(),
            index_buffer_id: self.index_buffer_id.clone(),
            index_buffer_len: self.index_buffer_len.clone(),
            refcount: self.refcount,
            index_buffer_format: self.index_buffer_format.clone(),
            run_destructor: true,
        }
    }
}
impl Drop for VertexBuffer {
    fn drop(&mut self) {
        self.run_destructor = false;
        let copies = unsafe { (*self.refcount).fetch_sub(1, AtomicOrdering::SeqCst) };
        if copies == 1 {
            self.vao.vertex_layout = VertexLayout {
                fields: VertexAttributeVec::from_const_slice(&[]),
            };
            let _ = unsafe { Box::from_raw(self.refcount as *mut AtomicUsize) };
            self.vao
                .gl_context
                .delete_buffers((&[self.vertex_buffer_id, self.index_buffer_id])[..].into());
        }
    }
}
impl VertexBuffer {
    pub fn new<T: VertexLayoutDescription>(
        gl_context: GlContextPtr,
        shader_program_id: GLuint,
        vertices: &[T],
        indices: &[u32],
        index_buffer_format: IndexBufferFormat,
    ) -> Self {
        use core::mem;
        // Save the OpenGL state
        let mut current_vertex_array = [0_i32];
        gl_context.get_integer_v(gl::VERTEX_ARRAY, (&mut current_vertex_array[..]).into());
        let vertex_array_object = gl_context.gen_vertex_arrays(1);
        let vertex_array_object = vertex_array_object.get(0).unwrap();
        let vertex_buffer_id = gl_context.gen_buffers(1);
        let vertex_buffer_id = vertex_buffer_id.get(0).unwrap();
        let index_buffer_id = gl_context.gen_buffers(1);
        let index_buffer_id = index_buffer_id.get(0).unwrap();
        gl_context.bind_vertex_array(*vertex_array_object);
        // Upload vertex data to GPU
        gl_context.bind_buffer(gl::ARRAY_BUFFER, *vertex_buffer_id);
        gl_context.buffer_data_untyped(
            gl::ARRAY_BUFFER,
            (mem::size_of::<T>() * vertices.len()) as isize,
            GlVoidPtrConst {
                ptr: vertices.as_ptr() as *const core::ffi::c_void,
                run_destructor: true,
            },
            gl::STATIC_DRAW,
        );
        // Generate the index buffer + upload data
        gl_context.bind_buffer(gl::ELEMENT_ARRAY_BUFFER, *index_buffer_id);
        gl_context.buffer_data_untyped(
            gl::ELEMENT_ARRAY_BUFFER,
            (mem::size_of::<u32>() * indices.len()) as isize,
            GlVoidPtrConst {
                ptr: indices.as_ptr() as *const core::ffi::c_void,
                run_destructor: true,
            },
            gl::STATIC_DRAW,
        );
        let vertex_description = T::get_description();
        vertex_description.bind(&gl_context.ptr.ptr, shader_program_id);
        // Reset the OpenGL state
        gl_context.bind_vertex_array(current_vertex_array[0] as u32);
        Self::new_raw(
            *vertex_buffer_id,
            vertices.len(),
            VertexArrayObject::new(vertex_description, *vertex_array_object, gl_context),
            *index_buffer_id,
            indices.len(),
            index_buffer_format,
        )
    }
    pub fn new_raw(
        vertex_buffer_id: GLuint,
        vertex_buffer_len: usize,
        vao: VertexArrayObject,
        index_buffer_id: GLuint,
        index_buffer_len: usize,
        index_buffer_format: IndexBufferFormat,
    ) -> Self {
        Self {
            vertex_buffer_id,
            vertex_buffer_len,
            vao,
            index_buffer_id,
            index_buffer_len,
            index_buffer_format,
            refcount: Box::into_raw(Box::new(AtomicUsize::new(1))),
            run_destructor: true,
        }
    }
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum GlApiVersion {
    Gl { major: usize, minor: usize },
    GlEs { major: usize, minor: usize },
}
impl GlApiVersion {
    /// Returns the OpenGL version of the context
    pub fn get(gl_context: &GlContextPtr) -> Self {
        let mut major = [0];
        gl_context.get_integer_v(gl::MAJOR_VERSION, (&mut major[..]).into());
        let mut minor = [0];
        gl_context.get_integer_v(gl::MINOR_VERSION, (&mut minor[..]).into());
        let major = major[0] as usize;
        let minor = minor[0] as usize;
        match gl_context.get_type() {
            GlType::Gl => GlApiVersion::Gl { major, minor },
            GlType::Gles => GlApiVersion::GlEs { major, minor },
        }
    }
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)]
pub enum IndexBufferFormat {
    Points,
    Lines,
    LineStrip,
    Triangles,
    TriangleStrip,
    TriangleFan,
}
impl IndexBufferFormat {
    /// Returns the `gl::TRIANGLE_STRIP` / `gl::POINTS`, etc.
    pub fn get_gl_id(&self) -> GLuint {
        use self::IndexBufferFormat::*;
        match self {
            Points => gl::POINTS,
            Lines => gl::LINES,
            LineStrip => gl::LINE_STRIP,
            Triangles => gl::TRIANGLES,
            TriangleStrip => gl::TRIANGLE_STRIP,
            TriangleFan => gl::TRIANGLE_FAN,
        }
    }
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
#[repr(C)]
pub struct Uniform {
    pub uniform_name: AzString,
    pub uniform_type: UniformType,
}
impl Uniform {
    pub fn create<S: Into<AzString>>(name: S, uniform_type: UniformType) -> Self {
        Self {
            uniform_name: name.into(),
            uniform_type,
        }
    }
}
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
#[repr(C, u8)]
pub enum UniformType {
    Float(f32),
    FloatVec2([f32; 2]),
    FloatVec3([f32; 3]),
    FloatVec4([f32; 4]),
    Int(i32),
    IntVec2([i32; 2]),
    IntVec3([i32; 3]),
    IntVec4([i32; 4]),
    UnsignedInt(u32),
    UnsignedIntVec2([u32; 2]),
    UnsignedIntVec3([u32; 3]),
    UnsignedIntVec4([u32; 4]),
    Matrix2 {
        transpose: bool,
        matrix: [f32; 2 * 2],
    },
    Matrix3 {
        transpose: bool,
        matrix: [f32; 3 * 3],
    },
    Matrix4 {
        transpose: bool,
        matrix: [f32; 4 * 4],
    },
}
impl UniformType {
    /// Set a specific uniform
    pub fn set(self, gl_context: &Rc<GenericGlContext>, location: GLint) {
        use self::UniformType::*;
        match self {
            Float(r) => gl_context.uniform_1f(location, r),
            FloatVec2([r, g]) => gl_context.uniform_2f(location, r, g),
            FloatVec3([r, g, b]) => gl_context.uniform_3f(location, r, g, b),
            FloatVec4([r, g, b, a]) => gl_context.uniform_4f(location, r, g, b, a),
            Int(r) => gl_context.uniform_1i(location, r),
            IntVec2([r, g]) => gl_context.uniform_2i(location, r, g),
            IntVec3([r, g, b]) => gl_context.uniform_3i(location, r, g, b),
            IntVec4([r, g, b, a]) => gl_context.uniform_4i(location, r, g, b, a),
            UnsignedInt(r) => gl_context.uniform_1ui(location, r),
            UnsignedIntVec2([r, g]) => gl_context.uniform_2ui(location, r, g),
            UnsignedIntVec3([r, g, b]) => gl_context.uniform_3ui(location, r, g, b),
            UnsignedIntVec4([r, g, b, a]) => gl_context.uniform_4ui(location, r, g, b, a),
            Matrix2 { transpose, matrix } => {
                gl_context.uniform_matrix_2fv(location, transpose, &matrix[..])
            }
            Matrix3 { transpose, matrix } => {
                gl_context.uniform_matrix_3fv(location, transpose, &matrix[..])
            }
            Matrix4 { transpose, matrix } => {
                gl_context.uniform_matrix_4fv(location, transpose, &matrix[..])
            }
        }
    }
}
#[repr(C)]
pub struct GlShader {
    pub program_id: GLuint,
    pub gl_context: GlContextPtr,
}
impl ::core::fmt::Display for GlShader {
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        write!(f, "GlShader {{ program_id: {} }}", self.program_id)
    }
}
impl_traits_for_gl_object!(GlShader, program_id);
impl Drop for GlShader {
    fn drop(&mut self) {
        self.gl_context.delete_program(self.program_id);
    }
}
#[repr(C)]
#[derive(Clone)]
pub struct VertexShaderCompileError {
    pub error_id: i32,
    pub info_log: AzString,
}
impl_traits_for_gl_object!(VertexShaderCompileError, error_id);
impl ::core::fmt::Display for VertexShaderCompileError {
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        write!(f, "E{}: {}", self.error_id, self.info_log)
    }
}
#[repr(C)]
#[derive(Clone)]
pub struct FragmentShaderCompileError {
    pub error_id: i32,
    pub info_log: AzString,
}
impl_traits_for_gl_object!(FragmentShaderCompileError, error_id);
impl ::core::fmt::Display for FragmentShaderCompileError {
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        write!(f, "E{}: {}", self.error_id, self.info_log)
    }
}
#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum GlShaderCompileError {
    Vertex(VertexShaderCompileError),
    Fragment(FragmentShaderCompileError),
}
impl ::core::fmt::Display for GlShaderCompileError {
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        use self::GlShaderCompileError::*;
        match self {
            Vertex(vert_err) => write!(f, "Failed to compile vertex shader: {}", vert_err),
            Fragment(frag_err) => write!(f, "Failed to compile fragment shader: {}", frag_err),
        }
    }
}
impl ::core::fmt::Debug for GlShaderCompileError {
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        write!(f, "{}", self)
    }
}
#[repr(C)]
#[derive(Clone)]
pub struct GlShaderLinkError {
    pub error_id: i32,
    pub info_log: AzString,
}
impl_traits_for_gl_object!(GlShaderLinkError, error_id);
impl ::core::fmt::Display for GlShaderLinkError {
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        write!(f, "E{}: {}", self.error_id, self.info_log)
    }
}
#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum GlShaderCreateError {
    Compile(GlShaderCompileError),
    Link(GlShaderLinkError),
    NoShaderCompiler,
}
impl ::core::fmt::Display for GlShaderCreateError {
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        use self::GlShaderCreateError::*;
        match self {
            Compile(compile_err) => write!(f, "Shader compile error: {}", compile_err),
            Link(link_err) => write!(f, "Shader linking error: {}", link_err),
            NoShaderCompiler => {
                write!(f, "OpenGL implementation doesn't include a shader compiler")
            }
        }
    }
}
impl ::core::fmt::Debug for GlShaderCreateError {
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        write!(f, "{}", self)
    }
}
impl GlShader {
    /// Compiles and creates a new OpenGL shader, created from a vertex and a fragment shader
    /// string.
    ///
    /// If the shader fails to compile, the shader object gets automatically deleted, no cleanup
    /// necessary.
    pub fn new(
        gl_context: &GlContextPtr,
        vertex_shader: &str,
        fragment_shader: &str,
    ) -> Result<Self, GlShaderCreateError> {
        // Check whether the OpenGL implementation supports a shader compiler...
        let mut shader_compiler_supported = [gl::FALSE as u8];
        gl_context.get_boolean_v(
            gl::SHADER_COMPILER,
            (&mut shader_compiler_supported[..]).into(),
        );
        if u32::from(shader_compiler_supported[0]) == gl::FALSE {
            // Implementation only supports binary shaders
            return Err(GlShaderCreateError::NoShaderCompiler);
        }
        // Compile vertex shader
        let vertex_shader_object = gl_context.create_shader(gl::VERTEX_SHADER);
        gl_context.shader_source(
            vertex_shader_object,
            vec![AzString::from(vertex_shader.to_string())].into(),
        );
        gl_context.compile_shader(vertex_shader_object);
        if let Some(error_id) = get_gl_shader_error(&gl_context, vertex_shader_object) {
            let info_log = gl_context.get_shader_info_log(vertex_shader_object);
            gl_context.delete_shader(vertex_shader_object);
            return Err(GlShaderCreateError::Compile(GlShaderCompileError::Vertex(
                VertexShaderCompileError {
                    error_id,
                    info_log: info_log.into(),
                },
            )));
        }
        // Compile fragment shader
        let fragment_shader_object = gl_context.create_shader(gl::FRAGMENT_SHADER);
        gl_context.shader_source(
            fragment_shader_object,
            vec![AzString::from(fragment_shader.to_string())].into(),
        );
        gl_context.compile_shader(fragment_shader_object);
        if let Some(error_id) = get_gl_shader_error(&gl_context, fragment_shader_object) {
            let info_log = gl_context.get_shader_info_log(fragment_shader_object);
            gl_context.delete_shader(vertex_shader_object);
            gl_context.delete_shader(fragment_shader_object);
            return Err(GlShaderCreateError::Compile(
                GlShaderCompileError::Fragment(FragmentShaderCompileError {
                    error_id,
                    info_log: info_log.into(),
                }),
            ));
        }
        // Link program
        let program_id = gl_context.create_program();
        gl_context.attach_shader(program_id, vertex_shader_object);
        gl_context.attach_shader(program_id, fragment_shader_object);
        gl_context.link_program(program_id);
        if let Some(error_id) = get_gl_program_error(&gl_context, program_id) {
            let info_log = gl_context.get_program_info_log(program_id);
            gl_context.delete_shader(vertex_shader_object);
            gl_context.delete_shader(fragment_shader_object);
            gl_context.delete_program(program_id);
            return Err(GlShaderCreateError::Link(GlShaderLinkError {
                error_id,
                info_log: info_log.into(),
            }));
        }
        gl_context.delete_shader(vertex_shader_object);
        gl_context.delete_shader(fragment_shader_object);
        Ok(GlShader {
            program_id,
            gl_context: gl_context.clone(),
        })
    }
    /// Draws vertex buffers, index buffers + uniforms to the texture
    pub fn draw(
        // shader to use for drawing
        shader_program_id: GLuint,
        // note: texture is &mut so the texture is reusable -
        texture: &mut Texture,
        // buffers + uniforms to draw
        buffers: &[(&VertexBuffer, &[Uniform])],
    ) {
        use alloc::collections::btree_map::BTreeMap;
        const INDEX_TYPE: GLuint = gl::UNSIGNED_INT;
        let texture_size = texture.size;
        let gl_context = &texture.gl_context;
        let saved = GlStateSave::save(gl_context);
        // save draw()-specific state not covered by GlStateSave
        let mut current_blend_enabled = [0_u8];
        let mut current_primitive_restart_enabled = [0_u8];
        gl_context.get_boolean_v(gl::BLEND, (&mut current_blend_enabled[..]).into());
        gl_context.get_boolean_v(
            gl::PRIMITIVE_RESTART,
            (&mut current_primitive_restart_enabled[..]).into(),
        );
        // 1. Create the framebuffer
        let framebuffers = gl_context.gen_framebuffers(1);
        let framebuffer_id = framebuffers.get(0).unwrap();
        gl_context.bind_framebuffer(gl::FRAMEBUFFER, *framebuffer_id);
        let depthbuffers = gl_context.gen_renderbuffers(1);
        let depthbuffer_id = depthbuffers.get(0).unwrap();
        gl_context.bind_texture(gl::TEXTURE_2D, texture.texture_id);
        gl_context.tex_image_2d(
            gl::TEXTURE_2D,
            0,
            gl::RGBA as i32, // NOT RGBA8 - will generate INVALID_ENUM!
            texture_size.width as i32,
            texture_size.height as i32,
            0,
            gl::RGBA, // gl::BGRA?
            gl::UNSIGNED_BYTE,
            None.into(),
        );
        gl_context.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as i32);
        gl_context.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::NEAREST as i32);
        gl_context.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as i32);
        gl_context.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as i32);
        gl_context.bind_renderbuffer(gl::RENDERBUFFER, *depthbuffer_id);
        gl_context.renderbuffer_storage(
            gl::RENDERBUFFER,
            gl::DEPTH_COMPONENT,
            texture_size.width as i32,
            texture_size.height as i32,
        );
        gl_context.framebuffer_renderbuffer(
            gl::FRAMEBUFFER,
            gl::DEPTH_ATTACHMENT,
            gl::RENDERBUFFER,
            *depthbuffer_id,
        );
        gl_context.framebuffer_texture_2d(
            gl::FRAMEBUFFER,
            gl::COLOR_ATTACHMENT0,
            gl::TEXTURE_2D,
            texture.texture_id,
            0,
        );
        gl_context.draw_buffers([gl::COLOR_ATTACHMENT0][..].into());
        #[cfg(feature = "std")]
        {
            let fb_check = gl_context.check_frame_buffer_status(gl::FRAMEBUFFER);
            match fb_check {
                gl::FRAMEBUFFER_COMPLETE => {}
                gl::FRAMEBUFFER_UNDEFINED => {
                    println!("GL_FRAMEBUFFER_UNDEFINED");
                }
                gl::FRAMEBUFFER_INCOMPLETE_ATTACHMENT => {
                    println!("GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT");
                }
                gl::FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT => {
                    println!("GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT");
                }
                gl::FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER => {
                    println!("GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER");
                }
                gl::FRAMEBUFFER_INCOMPLETE_READ_BUFFER => {
                    println!("GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER");
                }
                gl::FRAMEBUFFER_UNSUPPORTED => {
                    println!("GL_FRAMEBUFFER_UNSUPPORTED");
                }
                gl::FRAMEBUFFER_INCOMPLETE_MULTISAMPLE => {
                    println!("GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE");
                }
                gl::FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS => {
                    println!("GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS");
                }
                o => {
                    println!("glFramebufferStatus returned unknown return code: {}", o);
                }
            }
        }
        gl_context.viewport(0, 0, texture_size.width as i32, texture_size.height as i32);
        gl_context.enable(gl::BLEND);
        // Use GL_PRIMITIVE_RESTART (OpenGL 3.1+) instead of
        // GL_PRIMITIVE_RESTART_FIXED_INDEX (4.3+) for macOS compatibility.
        gl_context.enable(gl::PRIMITIVE_RESTART);
        unsafe {
            let gl = gl_context.get();
            if gl.glPrimitiveRestartIndex != core::ptr::null_mut() {
                let func: extern "system" fn(u32) =
                    core::mem::transmute(gl.glPrimitiveRestartIndex);
                func(GL_RESTART_INDEX); // u32::MAX
            }
        }
        gl_context.disable(gl::MULTISAMPLE);
        gl_context.blend_func(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA); // TODO: enable / disable
        gl_context.use_program(shader_program_id);
        // Avoid multiple calls to get_uniform_location by caching the uniform locations
        let mut uniform_locations: BTreeMap<AzString, i32> = BTreeMap::new();
        let mut max_uniform_len = 0;
        for (_, uniforms) in buffers {
            for uniform in uniforms.iter() {
                if !uniform_locations.contains_key(&uniform.uniform_name) {
                    uniform_locations.insert(
                        uniform.uniform_name.clone(),
                        gl_context.get_uniform_location(
                            shader_program_id,
                            uniform.uniform_name.as_str().into(),
                        ),
                    );
                }
            }
            max_uniform_len = max_uniform_len.max(uniforms.len());
        }
        let mut current_uniforms = vec![None; max_uniform_len];
        // Since the description of the vertex buffers is always the same,
        // only the first layer needs to bind its VAO
        // Draw the actual layers
        for (vertex_index_buffer, uniforms) in buffers {
            gl_context.bind_vertex_array(vertex_index_buffer.vao.vao_id);
            gl_context.bind_buffer(gl::ARRAY_BUFFER, vertex_index_buffer.vertex_buffer_id);
            gl_context.bind_buffer(
                gl::ELEMENT_ARRAY_BUFFER,
                vertex_index_buffer.index_buffer_id,
            );
            // Only set the uniform if the value has changed
            for (uniform_index, uniform) in uniforms.iter().enumerate() {
                if current_uniforms[uniform_index] != Some(uniform.uniform_type) {
                    let uniform_location = uniform_locations[&uniform.uniform_name];
                    uniform.uniform_type.set(gl_context.get(), uniform_location);
                    current_uniforms[uniform_index] = Some(uniform.uniform_type);
                }
            }
            gl_context.draw_elements(
                vertex_index_buffer.index_buffer_format.get_gl_id(),
                vertex_index_buffer.index_buffer_len as i32,
                INDEX_TYPE,
                0,
            );
        }
        // Reset draw()-specific state
        if u32::from(current_blend_enabled[0]) == gl::FALSE {
            gl_context.disable(gl::BLEND);
        }
        if u32::from(current_primitive_restart_enabled[0]) == gl::FALSE {
            gl_context.disable(gl::PRIMITIVE_RESTART);
        }
        gl_context.delete_framebuffers((&[*framebuffer_id])[..].into());
        gl_context.delete_renderbuffers((&[*depthbuffer_id])[..].into());
        // Reset common GL state
        saved.restore(gl_context);
        texture.format = RawImageFormat::RGBA8;
        texture.flags = TextureFlags {
            is_opaque: false,
            is_video_texture: false,
        };
    }
}
fn get_gl_shader_error(context: &GlContextPtr, shader_object: GLuint) -> Option<i32> {
    let mut err = [0];
    context.get_shader_iv(shader_object, gl::COMPILE_STATUS, (&mut err[..]).into());
    let err_code = err[0];
    if err_code == gl::TRUE as i32 {
        None
    } else {
        Some(err_code)
    }
}
fn get_gl_program_error(context: &GlContextPtr, shader_object: GLuint) -> Option<i32> {
    let mut err = [0];
    context.get_program_iv(shader_object, gl::LINK_STATUS, (&mut err[..]).into());
    let err_code = err[0];
    if err_code == gl::TRUE as i32 {
        None
    } else {
        Some(err_code)
    }
}