Skip to main content

libretro_core/
glsym.rs

1//! Ergonomic OpenGL symbol access for libretro cores.
2//!
3//! Methodology:
4//! - Keep the public API aligned with OpenGL naming where practical.
5//! - Prefer Rust-native inputs and outputs (`&str`, slices, return values) over
6//!   raw pointers, mutable out-params, or `CString`/`CStr` requirements.
7//! - Keep ABI- and pointer-oriented helpers private to this module so core code
8//!   stays focused on rendering intent instead of FFI plumbing.
9//! - Add higher-level helpers only when they remove repetitive multi-call setup
10//!   that every libretro OpenGL core would otherwise need to duplicate.
11//!
12#![allow(unsafe_op_in_unsafe_fn)]
13
14use std::ffi::{CStr, CString, c_char, c_void};
15use std::mem;
16use std::sync::{Mutex, OnceLock};
17
18use enumflags2::{BitFlags, bitflags};
19
20use crate::{HwContextType, Runtime};
21
22const GL_FALSE: u8 = 0;
23const GL_NONE: u32 = 0;
24const GL_COLOR_BUFFER_BIT: u32 = 0x0000_4000;
25const GL_DEPTH_BUFFER_BIT: u32 = 0x0000_0100;
26const GL_STENCIL_BUFFER_BIT: u32 = 0x0000_0400;
27const GL_SCISSOR_TEST: u32 = 0x0C11;
28const GL_BLEND: u32 = 0x0BE2;
29const GL_DEPTH_TEST: u32 = 0x0B71;
30const GL_STENCIL_TEST: u32 = 0x0B90;
31const GL_CULL_FACE: u32 = 0x0B44;
32const GL_MULTISAMPLE: u32 = 0x809D;
33const GL_DITHER: u32 = 0x0BD0;
34const GL_POLYGON_OFFSET_FILL: u32 = 0x8037;
35const GL_PRIMITIVE_RESTART_FIXED_INDEX: u32 = 0x8D69;
36const GL_COMPILE_STATUS: u32 = 0x8B81;
37const GL_LINK_STATUS: u32 = 0x8B82;
38const GL_INFO_LOG_LENGTH: u32 = 0x8B84;
39const GL_VENDOR: u32 = 0x1F00;
40const GL_RENDERER: u32 = 0x1F01;
41const GL_VERSION: u32 = 0x1F02;
42const GL_EXTENSIONS: u32 = 0x1F03;
43const GL_NUM_EXTENSIONS: u32 = 0x821D;
44const GL_MAX_TEXTURE_SIZE: u32 = 0x0D33;
45const GL_MAX_TEXTURE_IMAGE_UNITS: u32 = 0x8872;
46const GL_MAX_VARYING_VECTORS: u32 = 0x8DFC;
47const GL_FLOAT: u32 = 0x1406;
48const GL_TRIANGLES: u32 = 0x0004;
49const GL_ARRAY_BUFFER: u32 = 0x8892;
50const GL_ELEMENT_ARRAY_BUFFER: u32 = 0x8893;
51const GL_COPY_READ_BUFFER: u32 = 0x8F36;
52const GL_COPY_WRITE_BUFFER: u32 = 0x8F37;
53const GL_UNIFORM_BUFFER: u32 = 0x8A11;
54const GL_TRANSFORM_FEEDBACK_BUFFER: u32 = 0x8C8E;
55const GL_STATIC_DRAW: u32 = 0x88E4;
56const GL_STREAM_DRAW: u32 = 0x88E0;
57const GL_DYNAMIC_DRAW: u32 = 0x88E8;
58const GL_FRAMEBUFFER: u32 = 0x8D40;
59const GL_RENDERBUFFER: u32 = 0x8D41;
60const GL_COLOR_ATTACHMENT0: u32 = 0x8CE0;
61const GL_DEPTH_ATTACHMENT: u32 = 0x8D00;
62const GL_STENCIL_ATTACHMENT: u32 = 0x8D20;
63const GL_DEPTH_STENCIL_ATTACHMENT: u32 = 0x821A;
64const GL_DEPTH_COMPONENT16: u32 = 0x81A5;
65const GL_STENCIL_INDEX8: u32 = 0x8D48;
66const GL_RGBA4: u32 = 0x8056;
67const GL_RGB565: u32 = 0x8D62;
68const GL_TEXTURE0: u32 = 0x84C0;
69const GL_TEXTURE_2D: u32 = 0x0DE1;
70const GL_TEXTURE_2D_ARRAY: u32 = 0x8C1A;
71const GL_TEXTURE_MIN_FILTER: u32 = 0x2801;
72const GL_TEXTURE_MAG_FILTER: u32 = 0x2800;
73const GL_TEXTURE_WRAP_S: u32 = 0x2802;
74const GL_TEXTURE_WRAP_T: u32 = 0x2803;
75const GL_TEXTURE_WRAP_R: u32 = 0x8072;
76const GL_CLAMP_TO_EDGE: u32 = 0x812F;
77const GL_REPEAT: u32 = 0x2901;
78const GL_NEAREST: u32 = 0x2600;
79const GL_LINEAR: u32 = 0x2601;
80const GL_NEAREST_MIPMAP_NEAREST: u32 = 0x2700;
81const GL_LINEAR_MIPMAP_NEAREST: u32 = 0x2701;
82const GL_NEAREST_MIPMAP_LINEAR: u32 = 0x2702;
83const GL_LINEAR_MIPMAP_LINEAR: u32 = 0x2703;
84const GL_RGB: u32 = 0x1907;
85const GL_RED: u32 = 0x1903;
86const GL_LUMINANCE: u32 = 0x1909;
87const GL_RGBA: u32 = 0x1908;
88const GL_UNSIGNED_BYTE: u32 = 0x1401;
89const GL_UNSIGNED_SHORT: u32 = 0x1403;
90const GL_UNSIGNED_SHORT_4_4_4_4: u32 = 0x8033;
91const GL_UNSIGNED_SHORT_5_6_5: u32 = 0x8363;
92const GL_PACK_ALIGNMENT: u32 = 0x0D05;
93const GL_UNPACK_ALIGNMENT: u32 = 0x0CF5;
94const GL_ONE: u32 = 1;
95const GL_NEVER: u32 = 0x0200;
96const GL_LESS: u32 = 0x0201;
97const GL_EQUAL: u32 = 0x0202;
98const GL_LEQUAL: u32 = 0x0203;
99const GL_GREATER: u32 = 0x0204;
100const GL_NOTEQUAL: u32 = 0x0205;
101const GL_GEQUAL: u32 = 0x0206;
102const GL_ALWAYS: u32 = 0x0207;
103const GL_FRONT: u32 = 0x0404;
104const GL_BACK: u32 = 0x0405;
105const GL_FRONT_AND_BACK: u32 = 0x0408;
106const GL_CW: u32 = 0x0900;
107const GL_CCW: u32 = 0x0901;
108const GL_KEEP: u32 = 0x1E00;
109const GL_REPLACE: u32 = 0x1E01;
110const GL_INCR: u32 = 0x1E02;
111const GL_DECR: u32 = 0x1E03;
112const GL_INVERT: u32 = 0x150A;
113const GL_INCR_WRAP: u32 = 0x8507;
114const GL_DECR_WRAP: u32 = 0x8508;
115const GL_SRC_ALPHA: u32 = 0x0302;
116const GL_ONE_MINUS_SRC_ALPHA: u32 = 0x0303;
117const GL_FUNC_ADD: u32 = 0x8006;
118const GL_FUNC_REVERSE_SUBTRACT: u32 = 0x800B;
119const GL_R8: u32 = 0x8229;
120const GL_RGBA8: u32 = 0x8058;
121const GL_VERTEX_SHADER: u32 = 0x8B31;
122const GL_FRAGMENT_SHADER: u32 = 0x8B30;
123const GL_HIGH_FLOAT: u32 = 0x8DF2;
124const GL_NO_ERROR: u32 = 0;
125const GL_INVALID_ENUM: u32 = 0x0500;
126const GL_INVALID_VALUE: u32 = 0x0501;
127const GL_INVALID_OPERATION: u32 = 0x0502;
128const GL_OUT_OF_MEMORY: u32 = 0x0505;
129const GL_INVALID_FRAMEBUFFER_OPERATION: u32 = 0x0506;
130const GL_FRAMEBUFFER_COMPLETE: u32 = 0x8CD5;
131const GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: u32 = 0x8CD6;
132const GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: u32 = 0x8CD7;
133const GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: u32 = 0x8CD9;
134const GL_FRAMEBUFFER_UNSUPPORTED: u32 = 0x8CDD;
135const GL_SAMPLES_PASSED: u32 = 0x8914;
136const GL_ANY_SAMPLES_PASSED: u32 = 0x8C2F;
137const GL_ANY_SAMPLES_PASSED_CONSERVATIVE: u32 = 0x8D6A;
138const GL_PRIMITIVES_GENERATED: u32 = 0x8C87;
139const GL_QUERY_RESULT: u32 = 0x8866;
140const GL_QUERY_RESULT_AVAILABLE: u32 = 0x8867;
141const GL_SYNC_GPU_COMMANDS_COMPLETE: u32 = 0x9117;
142const GL_SYNC_FLUSH_COMMANDS_BIT: u32 = 0x0000_0001;
143const GL_ALREADY_SIGNALED: u32 = 0x911A;
144const GL_TIMEOUT_EXPIRED: u32 = 0x911B;
145const GL_CONDITION_SATISFIED: u32 = 0x911C;
146const GL_WAIT_FAILED: u32 = 0x911D;
147const GL_TIMEOUT_IGNORED: u64 = u64::MAX;
148
149type GlClearColor = unsafe extern "C" fn(f32, f32, f32, f32);
150type GlClear = unsafe extern "C" fn(u32);
151type GlEnable = unsafe extern "C" fn(u32);
152type GlDisable = unsafe extern "C" fn(u32);
153type GlDepthFunc = unsafe extern "C" fn(u32);
154type GlDepthMask = unsafe extern "C" fn(u8);
155type GlDepthRangef = unsafe extern "C" fn(f32, f32);
156type GlCullFace = unsafe extern "C" fn(u32);
157type GlFrontFace = unsafe extern "C" fn(u32);
158type GlStencilFunc = unsafe extern "C" fn(u32, i32, u32);
159type GlStencilMaskFn = unsafe extern "C" fn(u32);
160type GlStencilOp = unsafe extern "C" fn(u32, u32, u32);
161type GlStencilFuncSeparate = unsafe extern "C" fn(u32, u32, i32, u32);
162type GlStencilMaskSeparate = unsafe extern "C" fn(u32, u32);
163type GlStencilOpSeparate = unsafe extern "C" fn(u32, u32, u32, u32);
164type GlColorMaskFn = unsafe extern "C" fn(u8, u8, u8, u8);
165type GlPolygonOffsetFn = unsafe extern "C" fn(f32, f32);
166type GlGenQueries = unsafe extern "C" fn(i32, *mut u32);
167type GlDeleteQueries = unsafe extern "C" fn(i32, *const u32);
168type GlBeginQuery = unsafe extern "C" fn(u32, u32);
169type GlEndQuery = unsafe extern "C" fn(u32);
170type GlGetQueryObjectuiv = unsafe extern "C" fn(u32, u32, *mut u32);
171type GlFenceSync = unsafe extern "C" fn(u32, u32) -> *const c_void;
172type GlClientWaitSync = unsafe extern "C" fn(*const c_void, u32, u64) -> u32;
173type GlWaitSync = unsafe extern "C" fn(*const c_void, u32, u64);
174type GlDeleteSync = unsafe extern "C" fn(*const c_void);
175type GlReadPixels = unsafe extern "C" fn(i32, i32, i32, i32, u32, u32, *mut c_void);
176type GlReadBuffer = unsafe extern "C" fn(u32);
177type GlDrawBuffers = unsafe extern "C" fn(i32, *const u32);
178type GlViewport = unsafe extern "C" fn(i32, i32, i32, i32);
179type GlScissor = unsafe extern "C" fn(i32, i32, i32, i32);
180type GlCreateShader = unsafe extern "C" fn(u32) -> u32;
181type GlShaderSource = unsafe extern "C" fn(u32, i32, *const *const c_char, *const i32);
182type GlCompileShader = unsafe extern "C" fn(u32);
183type GlGetShaderIv = unsafe extern "C" fn(u32, u32, *mut i32);
184type GlGetShaderInfoLog = unsafe extern "C" fn(u32, i32, *mut i32, *mut c_char);
185type GlDeleteShader = unsafe extern "C" fn(u32);
186type GlCreateProgram = unsafe extern "C" fn() -> u32;
187type GlAttachShader = unsafe extern "C" fn(u32, u32);
188type GlLinkProgram = unsafe extern "C" fn(u32);
189type GlGetProgramIv = unsafe extern "C" fn(u32, u32, *mut i32);
190type GlGetProgramInfoLog = unsafe extern "C" fn(u32, i32, *mut i32, *mut c_char);
191type GlDeleteProgram = unsafe extern "C" fn(u32);
192type GlUseProgram = unsafe extern "C" fn(u32);
193type GlGenBuffers = unsafe extern "C" fn(i32, *mut u32);
194type GlBindBuffer = unsafe extern "C" fn(u32, u32);
195type GlBindBufferBase = unsafe extern "C" fn(u32, u32, u32);
196type GlBindBufferRange = unsafe extern "C" fn(u32, u32, u32, isize, isize);
197type GlBufferData = unsafe extern "C" fn(u32, isize, *const c_void, u32);
198type GlBufferSubData = unsafe extern "C" fn(u32, isize, isize, *const c_void);
199type GlCopyBufferSubData = unsafe extern "C" fn(u32, u32, isize, isize, isize);
200type GlDeleteBuffers = unsafe extern "C" fn(i32, *const u32);
201type GlGenTextures = unsafe extern "C" fn(i32, *mut u32);
202type GlBindTexture = unsafe extern "C" fn(u32, u32);
203type GlActiveTexture = unsafe extern "C" fn(u32);
204type GlTexParameteri = unsafe extern "C" fn(u32, u32, i32);
205type GlPixelStorei = unsafe extern "C" fn(u32, i32);
206type GlTexImage2D = unsafe extern "C" fn(u32, i32, i32, i32, i32, i32, u32, u32, *const c_void);
207type GlTexSubImage2D = unsafe extern "C" fn(u32, i32, i32, i32, i32, i32, u32, u32, *const c_void);
208type GlTexImage3D =
209    unsafe extern "C" fn(u32, i32, i32, i32, i32, i32, i32, u32, u32, *const c_void);
210type GlTexSubImage3D =
211    unsafe extern "C" fn(u32, i32, i32, i32, i32, i32, i32, i32, u32, u32, *const c_void);
212type GlGenerateMipmap = unsafe extern "C" fn(u32);
213type GlDeleteTextures = unsafe extern "C" fn(i32, *const u32);
214type GlGenVertexArrays = unsafe extern "C" fn(i32, *mut u32);
215type GlBindVertexArray = unsafe extern "C" fn(u32);
216type GlDeleteVertexArrays = unsafe extern "C" fn(i32, *const u32);
217type GlEnableVertexAttribArray = unsafe extern "C" fn(u32);
218type GlDisableVertexAttribArray = unsafe extern "C" fn(u32);
219type GlVertexAttribPointer = unsafe extern "C" fn(u32, i32, u32, u8, i32, *const c_void);
220type GlVertexAttribDivisorFn = unsafe extern "C" fn(u32, u32);
221type GlGetUniformLocation = unsafe extern "C" fn(u32, *const c_char) -> i32;
222type GlGetAttribLocation = unsafe extern "C" fn(u32, *const c_char) -> i32;
223type GlBindAttribLocation = unsafe extern "C" fn(u32, u32, *const c_char);
224type GlUniform1i = unsafe extern "C" fn(i32, i32);
225type GlUniform1f = unsafe extern "C" fn(i32, f32);
226type GlUniform2f = unsafe extern "C" fn(i32, f32, f32);
227type GlUniform3f = unsafe extern "C" fn(i32, f32, f32, f32);
228type GlUniform4f = unsafe extern "C" fn(i32, f32, f32, f32, f32);
229type GlUniform4fv = unsafe extern "C" fn(i32, i32, *const f32);
230type GlUniformMatrix3fv = unsafe extern "C" fn(i32, i32, u8, *const f32);
231type GlUniformMatrix4fv = unsafe extern "C" fn(i32, i32, u8, *const f32);
232type GlDrawArrays = unsafe extern "C" fn(u32, i32, i32);
233type GlDrawArraysInstanced = unsafe extern "C" fn(u32, i32, i32, i32);
234type GlDrawElements = unsafe extern "C" fn(u32, i32, u32, *const c_void);
235type GlDrawRangeElements = unsafe extern "C" fn(u32, u32, u32, i32, u32, *const c_void);
236type GlDrawElementsInstanced = unsafe extern "C" fn(u32, i32, u32, *const c_void, i32);
237type GlBlendColor = unsafe extern "C" fn(f32, f32, f32, f32);
238type GlBlendFunc = unsafe extern "C" fn(u32, u32);
239type GlBlendFuncSeparate = unsafe extern "C" fn(u32, u32, u32, u32);
240type GlBlendEquationFn = unsafe extern "C" fn(u32);
241type GlBlendEquationSeparate = unsafe extern "C" fn(u32, u32);
242type GlGenFramebuffers = unsafe extern "C" fn(i32, *mut u32);
243type GlBindFramebuffer = unsafe extern "C" fn(u32, u32);
244type GlDeleteFramebuffers = unsafe extern "C" fn(i32, *const u32);
245type GlFramebufferTexture2D = unsafe extern "C" fn(u32, u32, u32, u32, i32);
246type GlGenRenderbuffers = unsafe extern "C" fn(i32, *mut u32);
247type GlBindRenderbuffer = unsafe extern "C" fn(u32, u32);
248type GlRenderbufferStorage = unsafe extern "C" fn(u32, u32, i32, i32);
249type GlDeleteRenderbuffers = unsafe extern "C" fn(i32, *const u32);
250type GlFramebufferRenderbuffer = unsafe extern "C" fn(u32, u32, u32, u32);
251type GlBlitFramebuffer = unsafe extern "C" fn(i32, i32, i32, i32, i32, i32, i32, i32, u32, u32);
252type GlGetString = unsafe extern "C" fn(u32) -> *const u8;
253type GlGetStringi = unsafe extern "C" fn(u32, u32) -> *const u8;
254type GlGetIntegerv = unsafe extern "C" fn(u32, *mut i32);
255type GlGetShaderPrecisionFormat = unsafe extern "C" fn(u32, u32, *mut i32, *mut i32);
256type GlGetError = unsafe extern "C" fn() -> u32;
257type GlCheckFramebufferStatus = unsafe extern "C" fn(u32) -> u32;
258type GlInvalidateFramebuffer = unsafe extern "C" fn(u32, i32, *const u32);
259
260#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
261pub struct GlVersionInfo {
262    pub is_gles: bool,
263    pub major: Option<u32>,
264    pub minor: Option<u32>,
265}
266
267impl GlVersionInfo {
268    pub fn version_at_least(self, major: u32, minor: u32) -> bool {
269        match (self.major, self.minor) {
270            (Some(actual_major), Some(actual_minor)) => {
271                actual_major > major || (actual_major == major && actual_minor >= minor)
272            }
273            _ => false,
274        }
275    }
276}
277
278#[derive(Clone, Copy, Debug)]
279pub enum GlBufferTarget {
280    ArrayBuffer,
281    ElementArrayBuffer,
282    CopyReadBuffer,
283    CopyWriteBuffer,
284}
285
286impl GlBufferTarget {
287    fn as_raw(self) -> u32 {
288        match self {
289            Self::ArrayBuffer => GL_ARRAY_BUFFER,
290            Self::ElementArrayBuffer => GL_ELEMENT_ARRAY_BUFFER,
291            Self::CopyReadBuffer => GL_COPY_READ_BUFFER,
292            Self::CopyWriteBuffer => GL_COPY_WRITE_BUFFER,
293        }
294    }
295}
296
297#[derive(Clone, Copy, Debug)]
298pub enum GlBufferUsage {
299    StaticDraw,
300    StreamDraw,
301    DynamicDraw,
302}
303
304impl GlBufferUsage {
305    fn as_raw(self) -> u32 {
306        match self {
307            Self::StaticDraw => GL_STATIC_DRAW,
308            Self::StreamDraw => GL_STREAM_DRAW,
309            Self::DynamicDraw => GL_DYNAMIC_DRAW,
310        }
311    }
312}
313
314#[derive(Clone, Copy, Debug, PartialEq, Eq)]
315pub enum GlIndexedBufferTarget {
316    UniformBuffer,
317    TransformFeedbackBuffer,
318}
319
320impl GlIndexedBufferTarget {
321    fn as_raw(self) -> u32 {
322        match self {
323            Self::UniformBuffer => GL_UNIFORM_BUFFER,
324            Self::TransformFeedbackBuffer => GL_TRANSFORM_FEEDBACK_BUFFER,
325        }
326    }
327}
328
329#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
330pub struct GlBufferBindingIndex(u32);
331
332impl GlBufferBindingIndex {
333    pub const ZERO: Self = Self(0);
334
335    pub const fn from_index(index: u32) -> Self {
336        Self(index)
337    }
338
339    pub const fn as_raw(self) -> u32 {
340        self.0
341    }
342}
343
344#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
345pub struct GlBufferByteOffset(usize);
346
347impl GlBufferByteOffset {
348    pub const fn from_bytes(bytes: usize) -> Self {
349        Self(bytes)
350    }
351
352    pub const fn as_bytes(self) -> usize {
353        self.0
354    }
355
356    fn as_isize(self) -> Result<isize, String> {
357        isize::try_from(self.0)
358            .map_err(|_| format!("GL buffer byte offset {} exceeds isize::MAX", self.0))
359    }
360}
361
362#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
363pub struct GlBufferByteSize(usize);
364
365impl GlBufferByteSize {
366    pub const fn from_bytes(bytes: usize) -> Self {
367        Self(bytes)
368    }
369
370    pub const fn as_bytes(self) -> usize {
371        self.0
372    }
373
374    fn as_isize(self, operation: &str) -> Result<isize, String> {
375        isize::try_from(self.0)
376            .map_err(|_| format!("{operation} byte length {} exceeds isize::MAX", self.0))
377    }
378}
379
380#[derive(Clone, Copy, Debug, PartialEq, Eq)]
381pub struct GlBufferRange {
382    pub offset: GlBufferByteOffset,
383    pub size: GlBufferByteSize,
384}
385
386impl GlBufferRange {
387    pub const fn from_start(size: GlBufferByteSize) -> Self {
388        Self {
389            offset: GlBufferByteOffset::from_bytes(0),
390            size,
391        }
392    }
393
394    pub const fn new(offset: GlBufferByteOffset, size: GlBufferByteSize) -> Self {
395        Self { offset, size }
396    }
397
398    fn as_gl_args(self, operation: &str) -> Result<(isize, isize), String> {
399        Ok((self.offset.as_isize()?, self.size.as_isize(operation)?))
400    }
401}
402
403#[derive(Clone, Copy, Debug, PartialEq, Eq)]
404pub struct GlBuffer(u32);
405
406impl GlBuffer {
407    pub fn as_raw(self) -> u32 {
408        self.0
409    }
410
411    fn from_nonzero(id: u32) -> Result<Self, String> {
412        if id == 0 {
413            Err("glGenBuffers returned 0".to_string())
414        } else {
415            Ok(Self(id))
416        }
417    }
418}
419
420#[derive(Clone, Copy, Debug, PartialEq, Eq)]
421pub struct GlUniformLocation(i32);
422
423impl GlUniformLocation {
424    pub fn as_raw(self) -> i32 {
425        self.0
426    }
427
428    fn from_raw(raw: i32) -> Option<Self> {
429        (raw >= 0).then_some(Self(raw))
430    }
431}
432
433#[derive(Clone, Copy, Debug, PartialEq, Eq)]
434pub enum GlShaderStage {
435    Vertex,
436    Fragment,
437}
438
439impl GlShaderStage {
440    pub fn as_raw(self) -> u32 {
441        match self {
442            Self::Vertex => GL_VERTEX_SHADER,
443            Self::Fragment => GL_FRAGMENT_SHADER,
444        }
445    }
446}
447
448#[derive(Clone, Copy, Debug, PartialEq, Eq)]
449pub struct GlShader(u32);
450
451impl GlShader {
452    pub fn as_raw(self) -> u32 {
453        self.0
454    }
455
456    fn from_nonzero(id: u32, stage: GlShaderStage) -> Result<Self, String> {
457        if id == 0 {
458            Err(format!("glCreateShader({stage:?}) returned 0"))
459        } else {
460            Ok(Self(id))
461        }
462    }
463}
464
465#[derive(Clone, Copy, Debug, PartialEq, Eq)]
466pub struct GlProgram(u32);
467
468impl GlProgram {
469    pub fn as_raw(self) -> u32 {
470        self.0
471    }
472
473    fn from_nonzero(id: u32) -> Result<Self, String> {
474        if id == 0 {
475            Err("glCreateProgram returned 0".to_string())
476        } else {
477            Ok(Self(id))
478        }
479    }
480}
481
482#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
483pub struct GlVertexAttribLocation(u32);
484
485impl GlVertexAttribLocation {
486    pub const ZERO: Self = Self(0);
487
488    pub const fn from_index(index: u32) -> Self {
489        Self(index)
490    }
491
492    pub fn as_raw(self) -> u32 {
493        self.0
494    }
495
496    fn from_raw(raw: i32) -> Option<Self> {
497        (raw >= 0).then_some(Self(raw as u32))
498    }
499}
500
501#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
502pub struct GlVertexAttribDivisor(u32);
503
504impl GlVertexAttribDivisor {
505    pub const ZERO: Self = Self(0);
506    pub const ONE: Self = Self(1);
507
508    pub const fn new(divisor: u32) -> Self {
509        Self(divisor)
510    }
511
512    fn as_raw(self) -> u32 {
513        self.0
514    }
515}
516
517#[derive(Clone, Copy, Debug, PartialEq, Eq)]
518pub enum GlVertexAttribF32Components {
519    One,
520    Two,
521    Three,
522    Four,
523}
524
525impl GlVertexAttribF32Components {
526    pub const fn as_count(self) -> usize {
527        match self {
528            Self::One => 1,
529            Self::Two => 2,
530            Self::Three => 3,
531            Self::Four => 4,
532        }
533    }
534
535    const fn as_gl_size(self) -> i32 {
536        self.as_count() as i32
537    }
538}
539
540#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
541pub struct GlVertexAttribByteOffset(usize);
542
543impl GlVertexAttribByteOffset {
544    pub const ZERO: Self = Self(0);
545
546    pub const fn from_bytes(bytes: usize) -> Self {
547        Self(bytes)
548    }
549
550    pub fn from_f32_count(count: usize) -> Result<Self, String> {
551        count
552            .checked_mul(mem::size_of::<f32>())
553            .map(Self)
554            .ok_or_else(|| format!("vertex attribute f32 offset {count} overflows usize"))
555    }
556
557    const fn as_bytes(self) -> usize {
558        self.0
559    }
560}
561
562#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
563pub struct GlVertexAttribStride(i32);
564
565impl GlVertexAttribStride {
566    pub const TIGHTLY_PACKED: Self = Self(0);
567
568    pub fn from_bytes(bytes: usize) -> Result<Self, String> {
569        i32::try_from(bytes)
570            .map(Self)
571            .map_err(|_| format!("vertex attribute stride {bytes} exceeds i32::MAX"))
572    }
573
574    pub fn from_f32_count(count: usize) -> Result<Self, String> {
575        let bytes = count
576            .checked_mul(mem::size_of::<f32>())
577            .ok_or_else(|| format!("vertex attribute f32 stride {count} overflows usize"))?;
578        Self::from_bytes(bytes)
579    }
580
581    const fn as_i32(self) -> i32 {
582        self.0
583    }
584}
585
586#[derive(Clone, Copy, Debug, PartialEq, Eq)]
587pub struct GlVertexAttribF32Layout {
588    components: GlVertexAttribF32Components,
589    normalized: bool,
590    stride: GlVertexAttribStride,
591    offset: GlVertexAttribByteOffset,
592}
593
594impl GlVertexAttribF32Layout {
595    pub const fn tightly_packed(components: GlVertexAttribF32Components) -> Self {
596        Self {
597            components,
598            normalized: false,
599            stride: GlVertexAttribStride::TIGHTLY_PACKED,
600            offset: GlVertexAttribByteOffset::ZERO,
601        }
602    }
603
604    pub fn interleaved(
605        components: GlVertexAttribF32Components,
606        stride_f32_count: usize,
607    ) -> Result<Self, String> {
608        Ok(Self {
609            components,
610            normalized: false,
611            stride: GlVertexAttribStride::from_f32_count(stride_f32_count)?,
612            offset: GlVertexAttribByteOffset::ZERO,
613        })
614    }
615
616    pub const fn normalized(mut self, normalized: bool) -> Self {
617        self.normalized = normalized;
618        self
619    }
620
621    pub fn with_offset_f32_count(mut self, offset_f32_count: usize) -> Result<Self, String> {
622        self.offset = GlVertexAttribByteOffset::from_f32_count(offset_f32_count)?;
623        Ok(self)
624    }
625
626    pub const fn with_offset_components(mut self, components: GlVertexAttribF32Components) -> Self {
627        self.offset = GlVertexAttribByteOffset(components.as_count() * mem::size_of::<f32>());
628        self
629    }
630
631    pub const fn with_offset(mut self, offset: GlVertexAttribByteOffset) -> Self {
632        self.offset = offset;
633        self
634    }
635}
636
637#[derive(Clone, Copy, Debug)]
638pub enum GlTextureTarget {
639    Texture2D,
640    Texture2DArray,
641}
642
643impl GlTextureTarget {
644    pub fn as_raw(self) -> u32 {
645        match self {
646            Self::Texture2D => GL_TEXTURE_2D,
647            Self::Texture2DArray => GL_TEXTURE_2D_ARRAY,
648        }
649    }
650}
651
652#[derive(Clone, Copy, Debug, PartialEq, Eq)]
653pub struct GlTexture(u32);
654
655impl GlTexture {
656    pub fn as_raw(self) -> u32 {
657        self.0
658    }
659
660    fn from_nonzero(id: u32) -> Result<Self, String> {
661        if id == 0 {
662            Err("glGenTextures returned 0".to_string())
663        } else {
664            Ok(Self(id))
665        }
666    }
667}
668
669#[derive(Clone, Copy, Debug)]
670pub enum GlTextureFilter {
671    Nearest,
672    Linear,
673    NearestMipmapNearest,
674    LinearMipmapNearest,
675}
676
677impl GlTextureFilter {
678    pub fn as_raw(self) -> u32 {
679        match self {
680            Self::Nearest => GL_NEAREST,
681            Self::Linear => GL_LINEAR,
682            Self::NearestMipmapNearest => GL_NEAREST_MIPMAP_NEAREST,
683            Self::LinearMipmapNearest => GL_LINEAR_MIPMAP_NEAREST,
684        }
685    }
686}
687
688#[derive(Clone, Copy, Debug, PartialEq, Eq)]
689pub enum GlTextureMinFilter {
690    Nearest,
691    Linear,
692    NearestMipmapNearest,
693    LinearMipmapNearest,
694    NearestMipmapLinear,
695    LinearMipmapLinear,
696}
697
698impl GlTextureMinFilter {
699    pub fn as_raw(self) -> u32 {
700        match self {
701            Self::Nearest => GL_NEAREST,
702            Self::Linear => GL_LINEAR,
703            Self::NearestMipmapNearest => GL_NEAREST_MIPMAP_NEAREST,
704            Self::LinearMipmapNearest => GL_LINEAR_MIPMAP_NEAREST,
705            Self::NearestMipmapLinear => GL_NEAREST_MIPMAP_LINEAR,
706            Self::LinearMipmapLinear => GL_LINEAR_MIPMAP_LINEAR,
707        }
708    }
709}
710
711#[derive(Clone, Copy, Debug, PartialEq, Eq)]
712pub enum GlTextureMagFilter {
713    Nearest,
714    Linear,
715}
716
717impl GlTextureMagFilter {
718    pub fn as_raw(self) -> u32 {
719        match self {
720            Self::Nearest => GL_NEAREST,
721            Self::Linear => GL_LINEAR,
722        }
723    }
724}
725
726#[derive(Clone, Copy, Debug)]
727pub enum GlTextureWrap {
728    ClampToEdge,
729    Repeat,
730}
731
732impl GlTextureWrap {
733    pub fn as_raw(self) -> u32 {
734        match self {
735            Self::ClampToEdge => GL_CLAMP_TO_EDGE,
736            Self::Repeat => GL_REPEAT,
737        }
738    }
739}
740
741#[derive(Clone, Copy, Debug, PartialEq, Eq)]
742pub enum GlTextureInternalFormat {
743    R8,
744    Luminance,
745    Rgb,
746    Rgba,
747    Rgba8,
748}
749
750impl GlTextureInternalFormat {
751    pub fn as_raw(self) -> u32 {
752        match self {
753            Self::R8 => GL_R8,
754            Self::Luminance => GL_LUMINANCE,
755            Self::Rgb => GL_RGB,
756            Self::Rgba => GL_RGBA,
757            Self::Rgba8 => GL_RGBA8,
758        }
759    }
760}
761
762#[derive(Clone, Copy, Debug, PartialEq, Eq)]
763pub enum GlTextureFormat {
764    Red,
765    Luminance,
766    Rgb,
767    Rgba,
768}
769
770impl GlTextureFormat {
771    pub fn as_raw(self) -> u32 {
772        match self {
773            Self::Red => GL_RED,
774            Self::Luminance => GL_LUMINANCE,
775            Self::Rgb => GL_RGB,
776            Self::Rgba => GL_RGBA,
777        }
778    }
779}
780
781#[derive(Clone, Copy, Debug, PartialEq, Eq)]
782pub enum GlTextureDataType {
783    UnsignedByte,
784    UnsignedShort4444,
785    UnsignedShort565,
786}
787
788impl GlTextureDataType {
789    pub fn as_raw(self) -> u32 {
790        match self {
791            Self::UnsignedByte => GL_UNSIGNED_BYTE,
792            Self::UnsignedShort4444 => GL_UNSIGNED_SHORT_4_4_4_4,
793            Self::UnsignedShort565 => GL_UNSIGNED_SHORT_5_6_5,
794        }
795    }
796}
797
798#[derive(Clone, Copy, Debug, PartialEq, Eq)]
799pub enum GlPixelStoreAlignment {
800    One,
801    Two,
802    Four,
803    Eight,
804}
805
806impl GlPixelStoreAlignment {
807    pub fn as_raw(self) -> i32 {
808        match self {
809            Self::One => 1,
810            Self::Two => 2,
811            Self::Four => 4,
812            Self::Eight => 8,
813        }
814    }
815}
816
817#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
818pub struct GlTextureUnit(u32);
819
820impl GlTextureUnit {
821    pub const ZERO: Self = Self(0);
822
823    pub const fn from_index(index: u32) -> Self {
824        Self(index)
825    }
826
827    pub const fn index(self) -> u32 {
828        self.0
829    }
830
831    fn as_raw(self) -> Result<u32, String> {
832        GL_TEXTURE0
833            .checked_add(self.0)
834            .ok_or_else(|| format!("texture unit index {} overflows GLenum", self.0))
835    }
836}
837
838#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
839pub struct GlTextureLevel(u32);
840
841impl GlTextureLevel {
842    pub const ZERO: Self = Self(0);
843
844    pub const fn from_index(index: u32) -> Self {
845        Self(index)
846    }
847
848    fn as_i32(self, operation: &str) -> Result<i32, String> {
849        i32::try_from(self.0)
850            .map_err(|_| format!("{operation} mip level {} exceeds i32::MAX", self.0))
851    }
852}
853
854#[derive(Clone, Copy, Debug, PartialEq, Eq)]
855pub struct GlTextureOffset2D {
856    pub x: i32,
857    pub y: i32,
858}
859
860impl GlTextureOffset2D {
861    pub const fn new(x: i32, y: i32) -> Self {
862        Self { x, y }
863    }
864}
865
866#[derive(Clone, Copy, Debug, PartialEq, Eq)]
867pub struct GlTextureOffset3D {
868    pub x: i32,
869    pub y: i32,
870    pub z: i32,
871}
872
873impl GlTextureOffset3D {
874    pub const ZERO: Self = Self::new(0, 0, 0);
875
876    pub const fn new(x: i32, y: i32, z: i32) -> Self {
877        Self { x, y, z }
878    }
879}
880
881#[derive(Clone, Copy, Debug, PartialEq, Eq)]
882pub struct GlTextureSize2D {
883    pub width: u32,
884    pub height: u32,
885}
886
887impl GlTextureSize2D {
888    pub const fn new(width: u32, height: u32) -> Self {
889        Self { width, height }
890    }
891
892    fn as_gl_args(self, operation: &str) -> Result<(i32, i32), String> {
893        let width = i32::try_from(self.width)
894            .map_err(|_| format!("{operation} width {} exceeds i32::MAX", self.width))?;
895        let height = i32::try_from(self.height)
896            .map_err(|_| format!("{operation} height {} exceeds i32::MAX", self.height))?;
897        Ok((width, height))
898    }
899}
900
901#[derive(Clone, Copy, Debug, PartialEq, Eq)]
902pub struct GlTextureSize3D {
903    pub width: u32,
904    pub height: u32,
905    pub depth: u32,
906}
907
908impl GlTextureSize3D {
909    pub const fn new(width: u32, height: u32, depth: u32) -> Self {
910        Self {
911            width,
912            height,
913            depth,
914        }
915    }
916
917    fn as_gl_args(self, operation: &str) -> Result<(i32, i32, i32), String> {
918        let width = glsizei_from_u32(self.width, &format!("{operation} width"))?;
919        let height = glsizei_from_u32(self.height, &format!("{operation} height"))?;
920        let depth = glsizei_from_u32(self.depth, &format!("{operation} depth"))?;
921        Ok((width, height, depth))
922    }
923}
924
925#[derive(Clone, Copy, Debug, PartialEq, Eq)]
926pub struct GlRenderbufferSize {
927    pub width: u32,
928    pub height: u32,
929}
930
931impl GlRenderbufferSize {
932    pub const fn new(width: u32, height: u32) -> Self {
933        Self { width, height }
934    }
935
936    fn as_gl_args(self) -> Result<(i32, i32), String> {
937        Ok((
938            glsizei_from_u32(self.width, "renderbuffer width")?,
939            glsizei_from_u32(self.height, "renderbuffer height")?,
940        ))
941    }
942}
943
944#[derive(Clone, Copy, Debug, PartialEq, Eq)]
945pub struct GlRect {
946    pub x: i32,
947    pub y: i32,
948    pub width: u32,
949    pub height: u32,
950}
951
952impl GlRect {
953    pub const fn new(x: i32, y: i32, width: u32, height: u32) -> Self {
954        Self {
955            x,
956            y,
957            width,
958            height,
959        }
960    }
961
962    fn as_gl_args(self, operation: &str) -> Result<(i32, i32, i32, i32), String> {
963        let width = i32::try_from(self.width)
964            .map_err(|_| format!("{operation} width {} exceeds i32::MAX", self.width))?;
965        let height = i32::try_from(self.height)
966            .map_err(|_| format!("{operation} height {} exceeds i32::MAX", self.height))?;
967        Ok((self.x, self.y, width, height))
968    }
969}
970
971#[derive(Clone, Copy, Debug, PartialEq, Eq)]
972pub enum GlFramebufferBuffer {
973    None,
974    Front,
975    Back,
976    ColorAttachment(u32),
977}
978
979impl GlFramebufferBuffer {
980    fn as_raw(self) -> Result<u32, String> {
981        match self {
982            Self::None => Ok(GL_NONE),
983            Self::Front => Ok(GL_FRONT),
984            Self::Back => Ok(GL_BACK),
985            Self::ColorAttachment(index) => GL_COLOR_ATTACHMENT0
986                .checked_add(index)
987                .ok_or_else(|| format!("color attachment index {index} overflows GLenum")),
988        }
989    }
990}
991
992#[bitflags]
993#[repr(u32)]
994#[derive(Clone, Copy, Debug, PartialEq, Eq)]
995pub enum GlFramebufferBlitBuffer {
996    Color = GL_COLOR_BUFFER_BIT,
997    Depth = GL_DEPTH_BUFFER_BIT,
998    Stencil = GL_STENCIL_BUFFER_BIT,
999}
1000
1001#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1002pub enum GlFramebufferBlitFilter {
1003    Nearest,
1004    Linear,
1005}
1006
1007impl GlFramebufferBlitFilter {
1008    fn as_raw(self) -> u32 {
1009        match self {
1010            Self::Nearest => GL_NEAREST,
1011            Self::Linear => GL_LINEAR,
1012        }
1013    }
1014}
1015
1016#[derive(Clone, Copy, Debug)]
1017pub enum GlCapability {
1018    Blend,
1019    CullFace,
1020    DepthTest,
1021    Dither,
1022    Multisample,
1023    PolygonOffsetFill,
1024    PrimitiveRestartFixedIndex,
1025    ScissorTest,
1026    StencilTest,
1027}
1028
1029impl GlCapability {
1030    fn as_raw(self) -> u32 {
1031        match self {
1032            Self::Blend => GL_BLEND,
1033            Self::CullFace => GL_CULL_FACE,
1034            Self::DepthTest => GL_DEPTH_TEST,
1035            Self::Dither => GL_DITHER,
1036            Self::Multisample => GL_MULTISAMPLE,
1037            Self::PolygonOffsetFill => GL_POLYGON_OFFSET_FILL,
1038            Self::PrimitiveRestartFixedIndex => GL_PRIMITIVE_RESTART_FIXED_INDEX,
1039            Self::ScissorTest => GL_SCISSOR_TEST,
1040            Self::StencilTest => GL_STENCIL_TEST,
1041        }
1042    }
1043}
1044
1045#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1046pub enum GlDepthFunction {
1047    Never,
1048    Less,
1049    Equal,
1050    LessOrEqual,
1051    Greater,
1052    NotEqual,
1053    GreaterOrEqual,
1054    Always,
1055}
1056
1057impl GlDepthFunction {
1058    fn as_raw(self) -> u32 {
1059        match self {
1060            Self::Never => GL_NEVER,
1061            Self::Less => GL_LESS,
1062            Self::Equal => GL_EQUAL,
1063            Self::LessOrEqual => GL_LEQUAL,
1064            Self::Greater => GL_GREATER,
1065            Self::NotEqual => GL_NOTEQUAL,
1066            Self::GreaterOrEqual => GL_GEQUAL,
1067            Self::Always => GL_ALWAYS,
1068        }
1069    }
1070}
1071
1072#[derive(Clone, Copy, Debug, PartialEq)]
1073pub struct GlDepthRange {
1074    near: f64,
1075    far: f64,
1076}
1077
1078impl GlDepthRange {
1079    pub const DEFAULT: Self = Self::new(0.0, 1.0);
1080
1081    pub const fn new(near: f64, far: f64) -> Self {
1082        Self { near, far }
1083    }
1084}
1085
1086#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1087pub enum GlCullFaceMode {
1088    Front,
1089    Back,
1090    FrontAndBack,
1091}
1092
1093impl GlCullFaceMode {
1094    fn as_raw(self) -> u32 {
1095        match self {
1096            Self::Front => GL_FRONT,
1097            Self::Back => GL_BACK,
1098            Self::FrontAndBack => GL_FRONT_AND_BACK,
1099        }
1100    }
1101}
1102
1103#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1104pub enum GlFrontFaceWinding {
1105    Clockwise,
1106    CounterClockwise,
1107}
1108
1109impl GlFrontFaceWinding {
1110    fn as_raw(self) -> u32 {
1111        match self {
1112            Self::Clockwise => GL_CW,
1113            Self::CounterClockwise => GL_CCW,
1114        }
1115    }
1116}
1117
1118#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1119pub enum GlStencilFunction {
1120    Never,
1121    Less,
1122    Equal,
1123    LessOrEqual,
1124    Greater,
1125    NotEqual,
1126    GreaterOrEqual,
1127    Always,
1128}
1129
1130impl GlStencilFunction {
1131    fn as_raw(self) -> u32 {
1132        match self {
1133            Self::Never => GL_NEVER,
1134            Self::Less => GL_LESS,
1135            Self::Equal => GL_EQUAL,
1136            Self::LessOrEqual => GL_LEQUAL,
1137            Self::Greater => GL_GREATER,
1138            Self::NotEqual => GL_NOTEQUAL,
1139            Self::GreaterOrEqual => GL_GEQUAL,
1140            Self::Always => GL_ALWAYS,
1141        }
1142    }
1143}
1144
1145#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1146pub struct GlStencilReference(i32);
1147
1148impl GlStencilReference {
1149    pub const fn new(value: i32) -> Self {
1150        Self(value)
1151    }
1152
1153    fn as_raw(self) -> i32 {
1154        self.0
1155    }
1156}
1157
1158#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1159pub struct GlStencilMask(u32);
1160
1161impl GlStencilMask {
1162    pub const NONE: Self = Self(0);
1163    pub const ALL: Self = Self(u32::MAX);
1164
1165    pub const fn new(bits: u32) -> Self {
1166        Self(bits)
1167    }
1168
1169    fn as_raw(self) -> u32 {
1170        self.0
1171    }
1172}
1173
1174#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1175pub enum GlStencilFace {
1176    Front,
1177    Back,
1178    FrontAndBack,
1179}
1180
1181impl GlStencilFace {
1182    fn as_raw(self) -> u32 {
1183        match self {
1184            Self::Front => GL_FRONT,
1185            Self::Back => GL_BACK,
1186            Self::FrontAndBack => GL_FRONT_AND_BACK,
1187        }
1188    }
1189}
1190
1191#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1192pub enum GlStencilOperation {
1193    Keep,
1194    Zero,
1195    Replace,
1196    IncrementClamp,
1197    DecrementClamp,
1198    Invert,
1199    IncrementWrap,
1200    DecrementWrap,
1201}
1202
1203impl GlStencilOperation {
1204    fn as_raw(self) -> u32 {
1205        match self {
1206            Self::Keep => GL_KEEP,
1207            Self::Zero => 0,
1208            Self::Replace => GL_REPLACE,
1209            Self::IncrementClamp => GL_INCR,
1210            Self::DecrementClamp => GL_DECR,
1211            Self::Invert => GL_INVERT,
1212            Self::IncrementWrap => GL_INCR_WRAP,
1213            Self::DecrementWrap => GL_DECR_WRAP,
1214        }
1215    }
1216}
1217
1218#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1219pub struct GlColorWriteMask {
1220    red: bool,
1221    green: bool,
1222    blue: bool,
1223    alpha: bool,
1224}
1225
1226impl GlColorWriteMask {
1227    pub const ALL: Self = Self::new(true, true, true, true);
1228    pub const NONE: Self = Self::new(false, false, false, false);
1229    pub const RGB: Self = Self::new(true, true, true, false);
1230    pub const ALPHA: Self = Self::new(false, false, false, true);
1231
1232    pub const fn new(red: bool, green: bool, blue: bool, alpha: bool) -> Self {
1233        Self {
1234            red,
1235            green,
1236            blue,
1237            alpha,
1238        }
1239    }
1240
1241    fn as_raw(self) -> [u8; 4] {
1242        [
1243            if self.red { 1 } else { GL_FALSE },
1244            if self.green { 1 } else { GL_FALSE },
1245            if self.blue { 1 } else { GL_FALSE },
1246            if self.alpha { 1 } else { GL_FALSE },
1247        ]
1248    }
1249}
1250
1251#[derive(Clone, Copy, Debug, PartialEq)]
1252pub struct GlPolygonOffset {
1253    factor: f32,
1254    units: f32,
1255}
1256
1257impl GlPolygonOffset {
1258    pub const fn new(factor: f32, units: f32) -> Self {
1259        Self { factor, units }
1260    }
1261}
1262
1263#[derive(Clone, Copy, Debug)]
1264pub enum GlBlendFactor {
1265    One,
1266    SourceAlpha,
1267    OneMinusSourceAlpha,
1268}
1269
1270impl GlBlendFactor {
1271    fn as_raw(self) -> u32 {
1272        match self {
1273            Self::One => GL_ONE,
1274            Self::SourceAlpha => GL_SRC_ALPHA,
1275            Self::OneMinusSourceAlpha => GL_ONE_MINUS_SRC_ALPHA,
1276        }
1277    }
1278}
1279
1280#[derive(Clone, Copy, Debug)]
1281pub enum GlBlendEquation {
1282    Add,
1283    ReverseSubtract,
1284}
1285
1286impl GlBlendEquation {
1287    fn as_raw(self) -> u32 {
1288        match self {
1289            Self::Add => GL_FUNC_ADD,
1290            Self::ReverseSubtract => GL_FUNC_REVERSE_SUBTRACT,
1291        }
1292    }
1293}
1294
1295#[derive(Clone, Copy, Debug)]
1296pub enum GlIndexType {
1297    UnsignedShort,
1298}
1299
1300impl GlIndexType {
1301    fn as_raw(self) -> u32 {
1302        match self {
1303            Self::UnsignedShort => GL_UNSIGNED_SHORT,
1304        }
1305    }
1306
1307    const fn byte_len(self) -> usize {
1308        match self {
1309            Self::UnsignedShort => mem::size_of::<u16>(),
1310        }
1311    }
1312}
1313
1314#[derive(Clone, Copy, Debug)]
1315pub enum GlDrawMode {
1316    Triangles,
1317}
1318
1319impl GlDrawMode {
1320    fn as_raw(self) -> u32 {
1321        match self {
1322            Self::Triangles => GL_TRIANGLES,
1323        }
1324    }
1325}
1326
1327#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
1328pub struct GlDrawRange {
1329    pub first: u32,
1330    pub count: u32,
1331}
1332
1333impl GlDrawRange {
1334    pub const fn new(first: u32, count: u32) -> Self {
1335        Self { first, count }
1336    }
1337
1338    pub const fn from_start(count: u32) -> Self {
1339        Self { first: 0, count }
1340    }
1341
1342    fn as_gl_args(self, operation: &str) -> Result<(i32, i32), String> {
1343        let first = i32::try_from(self.first)
1344            .map_err(|_| format!("{operation} first vertex {} exceeds i32::MAX", self.first))?;
1345        let count = i32::try_from(self.count)
1346            .map_err(|_| format!("{operation} vertex count {} exceeds i32::MAX", self.count))?;
1347        Ok((first, count))
1348    }
1349}
1350
1351#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1352pub struct GlElementVertexRange {
1353    pub start: u32,
1354    pub end: u32,
1355}
1356
1357impl GlElementVertexRange {
1358    pub const fn new_unchecked(start: u32, end: u32) -> Self {
1359        Self { start, end }
1360    }
1361
1362    pub fn new(start: u32, end: u32) -> Result<Self, String> {
1363        if start > end {
1364            Err(format!(
1365                "element vertex range start {start} exceeds end {end}"
1366            ))
1367        } else {
1368            Ok(Self { start, end })
1369        }
1370    }
1371
1372    fn as_gl_args(self) -> (u32, u32) {
1373        (self.start, self.end)
1374    }
1375}
1376
1377#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
1378pub struct GlElementByteOffset(usize);
1379
1380impl GlElementByteOffset {
1381    pub const ZERO: Self = Self(0);
1382
1383    pub const fn from_bytes(bytes: usize) -> Self {
1384        Self(bytes)
1385    }
1386
1387    pub fn from_indices(index_type: GlIndexType, index_count: usize) -> Result<Self, String> {
1388        index_count
1389            .checked_mul(index_type.byte_len())
1390            .map(Self)
1391            .ok_or_else(|| format!("element index offset {index_count} overflows usize"))
1392    }
1393
1394    const fn as_bytes(self) -> usize {
1395        self.0
1396    }
1397}
1398
1399#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1400pub struct GlElementRange {
1401    pub count: u32,
1402    pub offset: GlElementByteOffset,
1403}
1404
1405impl GlElementRange {
1406    pub const fn from_start(count: u32) -> Self {
1407        Self {
1408            count,
1409            offset: GlElementByteOffset::ZERO,
1410        }
1411    }
1412
1413    pub const fn new(count: u32, offset: GlElementByteOffset) -> Self {
1414        Self { count, offset }
1415    }
1416
1417    fn as_gl_args(self, operation: &str) -> Result<(i32, usize), String> {
1418        let count = i32::try_from(self.count)
1419            .map_err(|_| format!("{operation} index count {} exceeds i32::MAX", self.count))?;
1420        Ok((count, self.offset.as_bytes()))
1421    }
1422}
1423
1424#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
1425pub struct GlInstanceCount(u32);
1426
1427impl GlInstanceCount {
1428    pub const fn new(count: u32) -> Self {
1429        Self(count)
1430    }
1431
1432    fn as_i32(self, operation: &str) -> Result<i32, String> {
1433        i32::try_from(self.0)
1434            .map_err(|_| format!("{operation} instance count {} exceeds i32::MAX", self.0))
1435    }
1436}
1437
1438#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1439pub struct GlVertexArray(u32);
1440
1441impl GlVertexArray {
1442    pub fn as_raw(self) -> u32 {
1443        self.0
1444    }
1445
1446    fn from_nonzero(id: u32) -> Result<Self, String> {
1447        if id == 0 {
1448            Err("glGenVertexArrays returned 0".to_string())
1449        } else {
1450            Ok(Self(id))
1451        }
1452    }
1453}
1454
1455#[derive(Clone, Copy, Debug)]
1456pub enum GlFramebufferTarget {
1457    Framebuffer,
1458}
1459
1460impl GlFramebufferTarget {
1461    fn as_raw(self) -> u32 {
1462        match self {
1463            Self::Framebuffer => GL_FRAMEBUFFER,
1464        }
1465    }
1466}
1467
1468#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1469pub struct GlFramebuffer(u32);
1470
1471impl GlFramebuffer {
1472    pub const fn from_raw(raw: u32) -> Option<Self> {
1473        if raw == 0 { None } else { Some(Self(raw)) }
1474    }
1475
1476    pub fn as_raw(self) -> u32 {
1477        self.0
1478    }
1479
1480    fn from_nonzero(id: u32) -> Result<Self, String> {
1481        if id == 0 {
1482            Err("glGenFramebuffers returned 0".to_string())
1483        } else {
1484            Ok(Self(id))
1485        }
1486    }
1487}
1488
1489#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1490pub struct GlRenderbuffer(u32);
1491
1492impl GlRenderbuffer {
1493    pub fn as_raw(self) -> u32 {
1494        self.0
1495    }
1496
1497    fn from_nonzero(id: u32) -> Result<Self, String> {
1498        if id == 0 {
1499            Err("glGenRenderbuffers returned 0".to_string())
1500        } else {
1501            Ok(Self(id))
1502        }
1503    }
1504}
1505
1506#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1507pub enum GlFramebufferAttachment {
1508    Color(u32),
1509    Depth,
1510    Stencil,
1511    DepthStencil,
1512}
1513
1514impl GlFramebufferAttachment {
1515    fn as_raw(self) -> Result<u32, String> {
1516        match self {
1517            Self::Color(index) => GL_COLOR_ATTACHMENT0
1518                .checked_add(index)
1519                .ok_or_else(|| format!("framebuffer color attachment index {index} overflowed")),
1520            Self::Depth => Ok(GL_DEPTH_ATTACHMENT),
1521            Self::Stencil => Ok(GL_STENCIL_ATTACHMENT),
1522            Self::DepthStencil => Ok(GL_DEPTH_STENCIL_ATTACHMENT),
1523        }
1524    }
1525}
1526
1527#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1528pub enum GlFramebufferTexture2DTarget {
1529    Texture2D,
1530}
1531
1532impl GlFramebufferTexture2DTarget {
1533    fn as_raw(self) -> u32 {
1534        match self {
1535            Self::Texture2D => GL_TEXTURE_2D,
1536        }
1537    }
1538}
1539
1540#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1541pub enum GlRenderbufferTarget {
1542    Renderbuffer,
1543}
1544
1545impl GlRenderbufferTarget {
1546    fn as_raw(self) -> u32 {
1547        match self {
1548            Self::Renderbuffer => GL_RENDERBUFFER,
1549        }
1550    }
1551}
1552
1553#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1554pub enum GlRenderbufferInternalFormat {
1555    Rgba4,
1556    Rgb565,
1557    DepthComponent16,
1558    StencilIndex8,
1559}
1560
1561impl GlRenderbufferInternalFormat {
1562    fn as_raw(self) -> u32 {
1563        match self {
1564            Self::Rgba4 => GL_RGBA4,
1565            Self::Rgb565 => GL_RGB565,
1566            Self::DepthComponent16 => GL_DEPTH_COMPONENT16,
1567            Self::StencilIndex8 => GL_STENCIL_INDEX8,
1568        }
1569    }
1570}
1571
1572#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
1573pub struct GlQuery(u32);
1574
1575impl GlQuery {
1576    pub const fn from_raw(raw: u32) -> Option<Self> {
1577        if raw == 0 { None } else { Some(Self(raw)) }
1578    }
1579
1580    pub const fn as_raw(self) -> u32 {
1581        self.0
1582    }
1583}
1584
1585#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1586pub enum GlQueryTarget {
1587    SamplesPassed,
1588    AnySamplesPassed,
1589    AnySamplesPassedConservative,
1590    PrimitivesGenerated,
1591}
1592
1593impl GlQueryTarget {
1594    fn as_raw(self) -> u32 {
1595        match self {
1596            Self::SamplesPassed => GL_SAMPLES_PASSED,
1597            Self::AnySamplesPassed => GL_ANY_SAMPLES_PASSED,
1598            Self::AnySamplesPassedConservative => GL_ANY_SAMPLES_PASSED_CONSERVATIVE,
1599            Self::PrimitivesGenerated => GL_PRIMITIVES_GENERATED,
1600        }
1601    }
1602}
1603
1604#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1605pub struct GlSync(*const c_void);
1606
1607impl GlSync {
1608    fn from_raw(raw: *const c_void) -> Option<Self> {
1609        (!raw.is_null()).then_some(Self(raw))
1610    }
1611
1612    fn as_raw(self) -> *const c_void {
1613        self.0
1614    }
1615}
1616
1617#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1618pub struct GlSyncTimeout(u64);
1619
1620impl GlSyncTimeout {
1621    pub const ZERO: Self = Self(0);
1622    pub const IGNORED: Self = Self(GL_TIMEOUT_IGNORED);
1623
1624    pub const fn from_nanos(nanos: u64) -> Self {
1625        Self(nanos)
1626    }
1627
1628    fn as_raw(self) -> u64 {
1629        self.0
1630    }
1631}
1632
1633#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1634pub enum GlSyncWaitResult {
1635    AlreadySignaled,
1636    TimeoutExpired,
1637    ConditionSatisfied,
1638}
1639
1640impl GlSyncWaitResult {
1641    fn from_raw(raw: u32) -> Result<Self, String> {
1642        match raw {
1643            GL_ALREADY_SIGNALED => Ok(Self::AlreadySignaled),
1644            GL_TIMEOUT_EXPIRED => Ok(Self::TimeoutExpired),
1645            GL_CONDITION_SATISFIED => Ok(Self::ConditionSatisfied),
1646            GL_WAIT_FAILED => Err("glClientWaitSync reported GL_WAIT_FAILED".to_string()),
1647            _ => Err(format!(
1648                "glClientWaitSync returned unknown status {raw:#06x}"
1649            )),
1650        }
1651    }
1652}
1653
1654/// Typed OpenGL symbol table resolved from libretro's hardware-render callbacks.
1655///
1656/// `init()` expects the frontend-owned GL context to be active, which in libretro
1657/// means after `hw_context_reset` and before `hw_context_destroy`.
1658#[allow(non_camel_case_types)]
1659#[derive(Clone)]
1660pub struct glsym {
1661    context_type: HwContextType,
1662    version_info: GlVersionInfo,
1663    vendor_string: String,
1664    renderer_string: String,
1665    version_string: String,
1666    extensions_string: String,
1667    max_texture_size: Option<u32>,
1668    max_texture_image_units: Option<u32>,
1669    max_varying_vectors: Option<u32>,
1670    fragment_highp_float: Option<bool>,
1671    clear_color: GlClearColor,
1672    clear: GlClear,
1673    enable: GlEnable,
1674    disable: GlDisable,
1675    depth_func: Option<GlDepthFunc>,
1676    depth_mask: Option<GlDepthMask>,
1677    depth_range_f: Option<GlDepthRangef>,
1678    cull_face: Option<GlCullFace>,
1679    front_face: Option<GlFrontFace>,
1680    stencil_func: Option<GlStencilFunc>,
1681    stencil_mask: Option<GlStencilMaskFn>,
1682    stencil_op: Option<GlStencilOp>,
1683    stencil_func_separate: Option<GlStencilFuncSeparate>,
1684    stencil_mask_separate: Option<GlStencilMaskSeparate>,
1685    stencil_op_separate: Option<GlStencilOpSeparate>,
1686    color_mask: Option<GlColorMaskFn>,
1687    polygon_offset: Option<GlPolygonOffsetFn>,
1688    gen_queries: Option<GlGenQueries>,
1689    delete_queries: Option<GlDeleteQueries>,
1690    begin_query: Option<GlBeginQuery>,
1691    end_query: Option<GlEndQuery>,
1692    get_query_object_uiv: Option<GlGetQueryObjectuiv>,
1693    fence_sync: Option<GlFenceSync>,
1694    client_wait_sync: Option<GlClientWaitSync>,
1695    wait_sync: Option<GlWaitSync>,
1696    delete_sync: Option<GlDeleteSync>,
1697    read_pixels: Option<GlReadPixels>,
1698    read_buffer: Option<GlReadBuffer>,
1699    draw_buffers: Option<GlDrawBuffers>,
1700    viewport: GlViewport,
1701    scissor: GlScissor,
1702    create_shader: GlCreateShader,
1703    shader_source: GlShaderSource,
1704    compile_shader: GlCompileShader,
1705    get_shader_iv: GlGetShaderIv,
1706    get_shader_info_log: GlGetShaderInfoLog,
1707    delete_shader: GlDeleteShader,
1708    create_program: GlCreateProgram,
1709    attach_shader: GlAttachShader,
1710    link_program: GlLinkProgram,
1711    get_program_iv: GlGetProgramIv,
1712    get_program_info_log: GlGetProgramInfoLog,
1713    delete_program: GlDeleteProgram,
1714    use_program: GlUseProgram,
1715    gen_buffers: GlGenBuffers,
1716    bind_buffer: GlBindBuffer,
1717    bind_buffer_base: Option<GlBindBufferBase>,
1718    bind_buffer_range: Option<GlBindBufferRange>,
1719    buffer_data: GlBufferData,
1720    buffer_sub_data: GlBufferSubData,
1721    copy_buffer_sub_data: Option<GlCopyBufferSubData>,
1722    delete_buffers: GlDeleteBuffers,
1723    gen_textures: GlGenTextures,
1724    bind_texture: GlBindTexture,
1725    active_texture: GlActiveTexture,
1726    tex_parameter_i: GlTexParameteri,
1727    pixel_store_i: GlPixelStorei,
1728    tex_image_2d: GlTexImage2D,
1729    tex_sub_image_2d: GlTexSubImage2D,
1730    tex_image_3d: Option<GlTexImage3D>,
1731    tex_sub_image_3d: Option<GlTexSubImage3D>,
1732    generate_mipmap: Option<GlGenerateMipmap>,
1733    delete_textures: GlDeleteTextures,
1734    gen_vertex_arrays: Option<GlGenVertexArrays>,
1735    bind_vertex_array: Option<GlBindVertexArray>,
1736    delete_vertex_arrays: Option<GlDeleteVertexArrays>,
1737    enable_vertex_attrib_array: GlEnableVertexAttribArray,
1738    disable_vertex_attrib_array: GlDisableVertexAttribArray,
1739    vertex_attrib_pointer: GlVertexAttribPointer,
1740    vertex_attrib_divisor: Option<GlVertexAttribDivisorFn>,
1741    get_uniform_location: GlGetUniformLocation,
1742    get_attrib_location: GlGetAttribLocation,
1743    bind_attrib_location: Option<GlBindAttribLocation>,
1744    uniform_1i: GlUniform1i,
1745    uniform_1f: GlUniform1f,
1746    uniform_2f: GlUniform2f,
1747    uniform_3f: GlUniform3f,
1748    uniform_4f: GlUniform4f,
1749    uniform_4fv: GlUniform4fv,
1750    uniform_matrix_3fv: GlUniformMatrix3fv,
1751    uniform_matrix_4fv: GlUniformMatrix4fv,
1752    draw_arrays: GlDrawArrays,
1753    draw_arrays_instanced: Option<GlDrawArraysInstanced>,
1754    draw_elements: GlDrawElements,
1755    draw_range_elements: Option<GlDrawRangeElements>,
1756    draw_elements_instanced: Option<GlDrawElementsInstanced>,
1757    blend_color: Option<GlBlendColor>,
1758    blend_func: GlBlendFunc,
1759    blend_func_separate: Option<GlBlendFuncSeparate>,
1760    blend_equation: GlBlendEquationFn,
1761    blend_equation_separate: Option<GlBlendEquationSeparate>,
1762    gen_framebuffers: Option<GlGenFramebuffers>,
1763    bind_framebuffer: GlBindFramebuffer,
1764    delete_framebuffers: Option<GlDeleteFramebuffers>,
1765    framebuffer_texture_2d: Option<GlFramebufferTexture2D>,
1766    gen_renderbuffers: Option<GlGenRenderbuffers>,
1767    bind_renderbuffer: Option<GlBindRenderbuffer>,
1768    renderbuffer_storage: Option<GlRenderbufferStorage>,
1769    delete_renderbuffers: Option<GlDeleteRenderbuffers>,
1770    framebuffer_renderbuffer: Option<GlFramebufferRenderbuffer>,
1771    blit_framebuffer: Option<GlBlitFramebuffer>,
1772    get_error: Option<GlGetError>,
1773    check_framebuffer_status: Option<GlCheckFramebufferStatus>,
1774    invalidate_framebuffer: Option<GlInvalidateFramebuffer>,
1775}
1776
1777/// Minimal GL entry points needed for legal hardware framebuffer setup.
1778///
1779/// This intentionally loads only framebuffer binding, viewport, and clear calls
1780/// so cores can present a hardware frame before probing shader, texture, or
1781/// product-renderer symbol coverage.
1782#[derive(Clone, Copy)]
1783pub struct CompatGlClear {
1784    context_type: HwContextType,
1785    version_info: GlVersionInfo,
1786    clear_color: GlClearColor,
1787    clear: GlClear,
1788    viewport: GlViewport,
1789    bind_framebuffer: GlBindFramebuffer,
1790    get_error: Option<GlGetError>,
1791    check_framebuffer_status: Option<GlCheckFramebufferStatus>,
1792}
1793
1794/// GLES2-era drawing symbols for simple compatibility rendering.
1795///
1796/// This is narrower than `glsym::init()`: it deliberately avoids product
1797/// renderer symbols such as scissor, draw-elements, matrices, texture arrays,
1798/// VAOs, instancing, and blend equations.
1799#[derive(Clone, Copy)]
1800pub struct CompatGl {
1801    clear: CompatGlClear,
1802    create_shader: GlCreateShader,
1803    shader_source: GlShaderSource,
1804    compile_shader: GlCompileShader,
1805    get_shader_iv: GlGetShaderIv,
1806    get_shader_info_log: GlGetShaderInfoLog,
1807    delete_shader: GlDeleteShader,
1808    create_program: GlCreateProgram,
1809    attach_shader: GlAttachShader,
1810    link_program: GlLinkProgram,
1811    get_program_iv: GlGetProgramIv,
1812    get_program_info_log: GlGetProgramInfoLog,
1813    delete_program: GlDeleteProgram,
1814    use_program: GlUseProgram,
1815    gen_buffers: GlGenBuffers,
1816    bind_buffer: GlBindBuffer,
1817    buffer_data: GlBufferData,
1818    delete_buffers: GlDeleteBuffers,
1819    enable_vertex_attrib_array: GlEnableVertexAttribArray,
1820    disable_vertex_attrib_array: GlDisableVertexAttribArray,
1821    vertex_attrib_pointer: GlVertexAttribPointer,
1822    get_uniform_location: GlGetUniformLocation,
1823    uniform_4fv: GlUniform4fv,
1824    get_attrib_location: GlGetAttribLocation,
1825    draw_arrays: GlDrawArrays,
1826}
1827
1828/// Texture/uniform/blend symbols layered on top of `CompatGl`.
1829///
1830/// Loading this separately keeps the known-good clear/triangle path alive when
1831/// older libretro GL proc lookup fails for texture or blending entry points.
1832/// `glActiveTexture` is required so callers can restore texture unit 0
1833/// explicitly after uploads and draws.
1834#[derive(Clone, Copy)]
1835pub struct CompatTextureGl {
1836    max_texture_size: Option<u32>,
1837    get_error: Option<GlGetError>,
1838    enable: GlEnable,
1839    disable: GlDisable,
1840    gen_textures: GlGenTextures,
1841    bind_texture: GlBindTexture,
1842    active_texture: GlActiveTexture,
1843    tex_parameter_i: GlTexParameteri,
1844    pixel_store_i: GlPixelStorei,
1845    tex_image_2d: GlTexImage2D,
1846    delete_textures: GlDeleteTextures,
1847    get_uniform_location: GlGetUniformLocation,
1848    uniform_1i: GlUniform1i,
1849    uniform_4fv: GlUniform4fv,
1850    blend_func: GlBlendFunc,
1851}
1852
1853/// Ergonomic OpenGL access for libretro hardware-rendered cores.
1854///
1855/// `Gl::init` requires only the minimal symbols needed to submit a legal
1856/// hardware-rendered frame. Shader, buffer, texture, blending, and richer GL
1857/// entry points are loaded opportunistically so cores can keep a clear-only
1858/// diagnostic path alive on frontends with incomplete symbol lookup.
1859#[derive(Clone)]
1860pub struct Gl {
1861    clear: CompatGlClear,
1862    compat: Option<CompatGl>,
1863    texture: Option<CompatTextureGl>,
1864    full: Option<glsym>,
1865}
1866
1867fn parse_gl_version_info(version_string: &str) -> Option<GlVersionInfo> {
1868    let (is_gles, version_token) = if let Some(rest) = version_string.strip_prefix("OpenGL ES-CM ")
1869    {
1870        (true, rest.split_whitespace().next()?)
1871    } else if let Some(rest) = version_string.strip_prefix("OpenGL ES-CL ") {
1872        (true, rest.split_whitespace().next()?)
1873    } else if let Some(rest) = version_string.strip_prefix("OpenGL ES ") {
1874        (true, rest.split_whitespace().next()?)
1875    } else {
1876        (false, version_string.split_whitespace().next()?)
1877    };
1878
1879    let mut parts = version_token.split('.');
1880    let major = parts.next()?.parse::<u32>().ok()?;
1881    let minor = parts.next()?.parse::<u32>().ok()?;
1882    Some(GlVersionInfo {
1883        is_gles,
1884        major: Some(major),
1885        minor: Some(minor),
1886    })
1887}
1888
1889fn fallback_gl_version_info(context_type: HwContextType) -> GlVersionInfo {
1890    match context_type {
1891        HwContextType::OpenGlEs2 => GlVersionInfo {
1892            is_gles: true,
1893            major: Some(2),
1894            minor: Some(0),
1895        },
1896        HwContextType::OpenGlEs3 => GlVersionInfo {
1897            is_gles: true,
1898            major: Some(3),
1899            minor: Some(0),
1900        },
1901        HwContextType::OpenGlEsVersion => GlVersionInfo {
1902            is_gles: true,
1903            major: None,
1904            minor: None,
1905        },
1906        _ => GlVersionInfo::default(),
1907    }
1908}
1909
1910fn query_gl_version_info(get_string: GlGetString, context_type: HwContextType) -> GlVersionInfo {
1911    let version = query_gl_string(get_string, GL_VERSION);
1912    if let Some(parsed) = parse_gl_version_info(&version) {
1913        return parsed;
1914    }
1915
1916    fallback_gl_version_info(context_type)
1917}
1918
1919fn query_gl_string(get_string: GlGetString, name: u32) -> String {
1920    let raw = unsafe { get_string(name) };
1921    if raw.is_null() {
1922        return String::new();
1923    }
1924    unsafe { CStr::from_ptr(raw.cast::<c_char>()) }
1925        .to_string_lossy()
1926        .into_owned()
1927}
1928
1929fn query_gl_string_i(get_string_i: GlGetStringi, name: u32, index: u32) -> Option<String> {
1930    let raw = unsafe { get_string_i(name, index) };
1931    if raw.is_null() {
1932        return None;
1933    }
1934    Some(
1935        unsafe { CStr::from_ptr(raw.cast::<c_char>()) }
1936            .to_string_lossy()
1937            .into_owned(),
1938    )
1939}
1940
1941fn query_gl_indexed_extensions(
1942    get_integer_v: GlGetIntegerv,
1943    get_string_i: Option<GlGetStringi>,
1944) -> String {
1945    let Some(get_string_i) = get_string_i else {
1946        return String::new();
1947    };
1948    let mut count = 0;
1949    unsafe { get_integer_v(GL_NUM_EXTENSIONS, &mut count) };
1950    let Ok(count) = u32::try_from(count) else {
1951        return String::new();
1952    };
1953    (0..count)
1954        .filter_map(|index| query_gl_string_i(get_string_i, GL_EXTENSIONS, index))
1955        .collect::<Vec<_>>()
1956        .join(" ")
1957}
1958
1959fn query_fragment_highp_float(
1960    get_shader_precision_format: Option<GlGetShaderPrecisionFormat>,
1961    version_info: GlVersionInfo,
1962) -> Option<bool> {
1963    if !version_info.is_gles {
1964        return Some(true);
1965    }
1966    let get_shader_precision_format = get_shader_precision_format?;
1967    let mut range = [0i32; 2];
1968    let mut precision = 0i32;
1969    unsafe {
1970        get_shader_precision_format(
1971            GL_FRAGMENT_SHADER,
1972            GL_HIGH_FLOAT,
1973            range.as_mut_ptr(),
1974            &mut precision,
1975        )
1976    };
1977    Some(range != [0, 0] || precision > 0)
1978}
1979
1980fn normalize_positive_gl_limit(value: i32) -> Option<u32> {
1981    u32::try_from(value).ok().filter(|value| *value > 0)
1982}
1983
1984fn glsizei_from_u32(value: u32, label: &str) -> Result<i32, String> {
1985    i32::try_from(value).map_err(|_| format!("{label} {value} exceeds GLsizei::MAX"))
1986}
1987
1988fn query_gl_max_texture_size(get_integer_v: GlGetIntegerv) -> Option<u32> {
1989    let mut value = 0;
1990    unsafe { get_integer_v(GL_MAX_TEXTURE_SIZE, &mut value) };
1991    normalize_positive_gl_limit(value)
1992}
1993
1994fn query_gl_max_texture_image_units(get_integer_v: GlGetIntegerv) -> Option<u32> {
1995    let mut value = 0;
1996    unsafe { get_integer_v(GL_MAX_TEXTURE_IMAGE_UNITS, &mut value) };
1997    normalize_positive_gl_limit(value)
1998}
1999
2000fn query_gl_max_varying_vectors(get_integer_v: GlGetIntegerv) -> Option<u32> {
2001    let mut value = 0;
2002    unsafe { get_integer_v(GL_MAX_VARYING_VECTORS, &mut value) };
2003    normalize_positive_gl_limit(value)
2004}
2005
2006fn is_gles_context(context_type: HwContextType, version_info: GlVersionInfo) -> bool {
2007    version_info.is_gles
2008        || matches!(
2009            context_type,
2010            HwContextType::OpenGlEs2 | HwContextType::OpenGlEs3 | HwContextType::OpenGlEsVersion
2011        )
2012}
2013
2014fn supports_core_texture_arrays(_context_type: HwContextType, version_info: GlVersionInfo) -> bool {
2015    // Function-pointer presence is not enough after process-global fallback:
2016    // desktop GL loaders often expose dispatch stubs for unsupported contexts.
2017    version_info.version_at_least(3, 0)
2018}
2019
2020fn supports_core_instancing(context_type: HwContextType, version_info: GlVersionInfo) -> bool {
2021    let is_gles = is_gles_context(context_type, version_info);
2022    if is_gles {
2023        version_info.version_at_least(3, 0)
2024    } else {
2025        // The product modern desktop path uses core glVertexAttribDivisor and
2026        // OpenGL 3.3-era shader expectations. Do not promote from process-global
2027        // dispatch stubs alone on older compatibility contexts.
2028        version_info.version_at_least(3, 3)
2029    }
2030}
2031
2032fn describe_gl_error(error: u32) -> &'static str {
2033    match error {
2034        GL_NO_ERROR => "GL_NO_ERROR",
2035        GL_INVALID_ENUM => "GL_INVALID_ENUM",
2036        GL_INVALID_VALUE => "GL_INVALID_VALUE",
2037        GL_INVALID_OPERATION => "GL_INVALID_OPERATION",
2038        GL_OUT_OF_MEMORY => "GL_OUT_OF_MEMORY",
2039        GL_INVALID_FRAMEBUFFER_OPERATION => "GL_INVALID_FRAMEBUFFER_OPERATION",
2040        _ => "unknown GL error",
2041    }
2042}
2043
2044fn describe_framebuffer_status(status: u32) -> &'static str {
2045    match status {
2046        GL_FRAMEBUFFER_COMPLETE => "GL_FRAMEBUFFER_COMPLETE",
2047        GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT => "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT",
2048        GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT => {
2049            "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"
2050        }
2051        GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS => "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS",
2052        GL_FRAMEBUFFER_UNSUPPORTED => "GL_FRAMEBUFFER_UNSUPPORTED",
2053        _ => "unknown framebuffer status",
2054    }
2055}
2056
2057fn collect_gl_errors(get_error: Option<GlGetError>, operation: &str) -> Result<(), String> {
2058    let Some(get_error) = get_error else {
2059        return Ok(());
2060    };
2061
2062    let mut errors = Vec::new();
2063    for _ in 0..16 {
2064        let error = unsafe { get_error() };
2065        if error == GL_NO_ERROR {
2066            break;
2067        }
2068        errors.push(error);
2069    }
2070
2071    if errors.is_empty() {
2072        return Ok(());
2073    }
2074
2075    let joined = errors
2076        .into_iter()
2077        .map(|error| format!("{} ({error:#06x})", describe_gl_error(error)))
2078        .collect::<Vec<_>>()
2079        .join(", ");
2080    Err(format!("{operation} observed GL error(s): {joined}"))
2081}
2082
2083fn check_bound_framebuffer_complete(
2084    check_framebuffer_status: Option<GlCheckFramebufferStatus>,
2085    target: GlFramebufferTarget,
2086    operation: &str,
2087) -> Result<(), String> {
2088    let Some(check_framebuffer_status) = check_framebuffer_status else {
2089        return Ok(());
2090    };
2091
2092    let status = unsafe { check_framebuffer_status(target.as_raw()) };
2093    if status == GL_FRAMEBUFFER_COMPLETE {
2094        return Ok(());
2095    }
2096
2097    Err(format!(
2098        "{operation} found incomplete framebuffer: {} ({status:#06x})",
2099        describe_framebuffer_status(status)
2100    ))
2101}
2102
2103impl CompatGlClear {
2104    pub fn init(runtime: &Runtime<'_>) -> Result<Self, String> {
2105        let context_type = runtime
2106            .hw_context_type()
2107            .ok_or_else(|| "hardware render context type is not available".to_string())?;
2108        if !context_type.is_opengl_family() {
2109            return Err(format!(
2110                "CompatGlClear requires an OpenGL-family context, got {context_type:?}"
2111            ));
2112        }
2113        let get_string: Option<GlGetString> = load_optional_gl_symbol(runtime, "glGetString")?;
2114
2115        Ok(Self {
2116            context_type,
2117            version_info: get_string
2118                .map(|get_string| query_gl_version_info(get_string, context_type))
2119                .unwrap_or_else(|| fallback_gl_version_info(context_type)),
2120            clear_color: load_gl_symbol(runtime, "glClearColor")?,
2121            clear: load_gl_symbol(runtime, "glClear")?,
2122            viewport: load_gl_symbol(runtime, "glViewport")?,
2123            bind_framebuffer: load_gl_symbol(runtime, "glBindFramebuffer")?,
2124            get_error: load_optional_gl_symbol(runtime, "glGetError")?,
2125            check_framebuffer_status: load_optional_gl_symbol(runtime, "glCheckFramebufferStatus")?,
2126        })
2127    }
2128
2129    pub fn context_type(&self) -> HwContextType {
2130        self.context_type
2131    }
2132
2133    pub fn version_info(&self) -> GlVersionInfo {
2134        self.version_info
2135    }
2136
2137    pub fn clear_color(&self, r: f32, g: f32, b: f32, a: f32) {
2138        unsafe { (self.clear_color)(r, g, b, a) };
2139    }
2140
2141    pub fn clear_color_depth_buffer(&self) {
2142        unsafe { (self.clear)(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) };
2143    }
2144
2145    fn viewport_raw(&self, x: i32, y: i32, width: i32, height: i32) {
2146        unsafe { (self.viewport)(x, y, width, height) };
2147    }
2148
2149    pub fn viewport(&self, rect: GlRect) -> Result<(), String> {
2150        let (x, y, width, height) = rect.as_gl_args("glViewport")?;
2151        self.viewport_raw(x, y, width, height);
2152        Ok(())
2153    }
2154
2155    fn bind_framebuffer_raw(&self, target: GlFramebufferTarget, framebuffer: u32) {
2156        unsafe { (self.bind_framebuffer)(target.as_raw(), framebuffer) };
2157    }
2158
2159    pub fn unbind_framebuffer(&self, target: GlFramebufferTarget) {
2160        self.bind_framebuffer_raw(target, 0);
2161    }
2162
2163    fn bind_framebuffer_checked_raw(
2164        &self,
2165        target: GlFramebufferTarget,
2166        framebuffer: u32,
2167    ) -> Result<(), String> {
2168        self.bind_framebuffer_raw(target, framebuffer);
2169        self.check_no_error("glBindFramebuffer")?;
2170        if framebuffer != 0 {
2171            self.check_bound_framebuffer_complete(target)?;
2172        }
2173        Ok(())
2174    }
2175
2176    pub fn bind_framebuffer(
2177        &self,
2178        target: GlFramebufferTarget,
2179        framebuffer: Option<GlFramebuffer>,
2180    ) -> Result<(), String> {
2181        self.bind_framebuffer_checked_raw(target, framebuffer.map_or(0, GlFramebuffer::as_raw))
2182    }
2183
2184    pub fn check_no_error(&self, operation: &str) -> Result<(), String> {
2185        collect_gl_errors(self.get_error, operation)
2186    }
2187
2188    pub fn check_bound_framebuffer_complete(
2189        &self,
2190        target: GlFramebufferTarget,
2191    ) -> Result<(), String> {
2192        check_bound_framebuffer_complete(
2193            self.check_framebuffer_status,
2194            target,
2195            "glCheckFramebufferStatus",
2196        )
2197    }
2198
2199    #[doc(hidden)]
2200    pub fn fake_for_testing(config: FakeGlConfig) -> Self {
2201        let gl = glsym::fake_for_testing(config);
2202        Self::from_glsym(gl)
2203    }
2204
2205    pub fn from_glsym(gl: glsym) -> Self {
2206        Self {
2207            context_type: gl.context_type,
2208            version_info: gl.version_info,
2209            clear_color: gl.clear_color,
2210            clear: gl.clear,
2211            viewport: gl.viewport,
2212            bind_framebuffer: gl.bind_framebuffer,
2213            get_error: gl.get_error,
2214            check_framebuffer_status: gl.check_framebuffer_status,
2215        }
2216    }
2217}
2218
2219impl CompatGl {
2220    pub fn init(runtime: &Runtime<'_>) -> Result<Self, String> {
2221        let clear = CompatGlClear::init(runtime)?;
2222        Self::init_from_clear(runtime, clear)
2223    }
2224
2225    pub fn init_from_clear(runtime: &Runtime<'_>, clear: CompatGlClear) -> Result<Self, String> {
2226        Ok(Self {
2227            clear,
2228            create_shader: load_gl_symbol(runtime, "glCreateShader")?,
2229            shader_source: load_gl_symbol(runtime, "glShaderSource")?,
2230            compile_shader: load_gl_symbol(runtime, "glCompileShader")?,
2231            get_shader_iv: load_gl_symbol(runtime, "glGetShaderiv")?,
2232            get_shader_info_log: load_gl_symbol(runtime, "glGetShaderInfoLog")?,
2233            delete_shader: load_gl_symbol(runtime, "glDeleteShader")?,
2234            create_program: load_gl_symbol(runtime, "glCreateProgram")?,
2235            attach_shader: load_gl_symbol(runtime, "glAttachShader")?,
2236            link_program: load_gl_symbol(runtime, "glLinkProgram")?,
2237            get_program_iv: load_gl_symbol(runtime, "glGetProgramiv")?,
2238            get_program_info_log: load_gl_symbol(runtime, "glGetProgramInfoLog")?,
2239            delete_program: load_gl_symbol(runtime, "glDeleteProgram")?,
2240            use_program: load_gl_symbol(runtime, "glUseProgram")?,
2241            gen_buffers: load_gl_symbol(runtime, "glGenBuffers")?,
2242            bind_buffer: load_gl_symbol(runtime, "glBindBuffer")?,
2243            buffer_data: load_gl_symbol(runtime, "glBufferData")?,
2244            delete_buffers: load_gl_symbol(runtime, "glDeleteBuffers")?,
2245            enable_vertex_attrib_array: load_gl_symbol(runtime, "glEnableVertexAttribArray")?,
2246            disable_vertex_attrib_array: load_gl_symbol(runtime, "glDisableVertexAttribArray")?,
2247            vertex_attrib_pointer: load_gl_symbol(runtime, "glVertexAttribPointer")?,
2248            get_uniform_location: load_gl_symbol(runtime, "glGetUniformLocation")?,
2249            uniform_4fv: load_gl_symbol(runtime, "glUniform4fv")?,
2250            get_attrib_location: load_gl_symbol(runtime, "glGetAttribLocation")?,
2251            draw_arrays: load_gl_symbol(runtime, "glDrawArrays")?,
2252        })
2253    }
2254
2255    pub fn clear_symbols(&self) -> CompatGlClear {
2256        self.clear
2257    }
2258
2259    pub fn context_type(&self) -> HwContextType {
2260        self.clear.context_type()
2261    }
2262
2263    pub fn version_info(&self) -> GlVersionInfo {
2264        self.clear.version_info()
2265    }
2266
2267    pub fn clear_color(&self, r: f32, g: f32, b: f32, a: f32) {
2268        self.clear.clear_color(r, g, b, a);
2269    }
2270
2271    pub fn clear_color_depth_buffer(&self) {
2272        self.clear.clear_color_depth_buffer();
2273    }
2274
2275    pub fn viewport(&self, rect: GlRect) -> Result<(), String> {
2276        self.clear.viewport(rect)
2277    }
2278
2279    pub fn unbind_framebuffer(&self, target: GlFramebufferTarget) {
2280        self.clear.unbind_framebuffer(target);
2281    }
2282
2283    pub fn bind_framebuffer(
2284        &self,
2285        target: GlFramebufferTarget,
2286        framebuffer: Option<GlFramebuffer>,
2287    ) -> Result<(), String> {
2288        self.clear.bind_framebuffer(target, framebuffer)
2289    }
2290
2291    pub fn check_no_error(&self, operation: &str) -> Result<(), String> {
2292        self.clear.check_no_error(operation)
2293    }
2294
2295    pub fn check_bound_framebuffer_complete(
2296        &self,
2297        target: GlFramebufferTarget,
2298    ) -> Result<(), String> {
2299        self.clear.check_bound_framebuffer_complete(target)
2300    }
2301
2302    pub fn build_program(
2303        &self,
2304        vertex_shader: &str,
2305        fragment_shader: &str,
2306    ) -> Result<GlProgram, String> {
2307        let vertex = self.compile_shader_source(GlShaderStage::Vertex, vertex_shader)?;
2308        let fragment = match self.compile_shader_source(GlShaderStage::Fragment, fragment_shader) {
2309            Ok(shader) => shader,
2310            Err(error) => {
2311                self.delete_shader(vertex);
2312                return Err(error);
2313            }
2314        };
2315
2316        let program = match self.create_program() {
2317            Ok(program) => program,
2318            Err(error) => {
2319                self.delete_shader(vertex);
2320                self.delete_shader(fragment);
2321                return Err(error);
2322            }
2323        };
2324
2325        self.attach_shader(program, vertex);
2326        self.attach_shader(program, fragment);
2327        let link_result = self.link_program(program);
2328        self.delete_shader(vertex);
2329        self.delete_shader(fragment);
2330
2331        if let Err(error) = link_result {
2332            self.delete_program(program);
2333            return Err(error);
2334        }
2335        Ok(program)
2336    }
2337
2338    pub fn compile_shader_source(
2339        &self,
2340        stage: GlShaderStage,
2341        source: &str,
2342    ) -> Result<GlShader, String> {
2343        let shader = GlShader::from_nonzero(self.create_shader_raw(stage.as_raw()), stage)?;
2344        if let Err(error) = self.shader_source_raw_handle(shader.as_raw(), source) {
2345            self.delete_shader(shader);
2346            return Err(error);
2347        }
2348        self.compile_shader_raw(shader.as_raw());
2349
2350        let status = self.get_shader_iv(shader.as_raw(), GL_COMPILE_STATUS);
2351        if status == 0 {
2352            let log = self.get_shader_info_log(shader.as_raw());
2353            self.delete_shader(shader);
2354            return Err(format!("shader compile failed: {log}"));
2355        }
2356
2357        if let Err(error) = self.check_no_error("CompatGl::compile_shader_source") {
2358            self.delete_shader(shader);
2359            return Err(error);
2360        }
2361        Ok(shader)
2362    }
2363
2364    fn create_shader_raw(&self, shader_type: u32) -> u32 {
2365        unsafe { (self.create_shader)(shader_type) }
2366    }
2367
2368    fn shader_source_raw_handle(&self, shader: u32, source: &str) -> Result<(), String> {
2369        let source = gl_string(source)?;
2370        let ptr = source.as_ptr();
2371        unsafe { (self.shader_source)(shader, 1, &ptr, std::ptr::null()) };
2372        Ok(())
2373    }
2374
2375    fn compile_shader_raw(&self, shader: u32) {
2376        unsafe { (self.compile_shader)(shader) };
2377    }
2378
2379    fn get_shader_iv(&self, shader: u32, parameter: u32) -> i32 {
2380        let mut value = 0;
2381        unsafe { (self.get_shader_iv)(shader, parameter, &mut value) };
2382        value
2383    }
2384
2385    fn get_shader_info_log(&self, shader: u32) -> String {
2386        let length = self.get_shader_iv(shader, GL_INFO_LOG_LENGTH);
2387        if length <= 1 {
2388            return "no log".to_string();
2389        }
2390
2391        let mut buffer = vec![0u8; length as usize];
2392        let mut written = 0;
2393        unsafe {
2394            (self.get_shader_info_log)(
2395                shader,
2396                length,
2397                &mut written,
2398                buffer.as_mut_ptr().cast::<c_char>(),
2399            )
2400        };
2401        String::from_utf8_lossy(&buffer[..written as usize]).into_owned()
2402    }
2403
2404    fn delete_shader_raw(&self, shader: u32) {
2405        unsafe { (self.delete_shader)(shader) };
2406    }
2407
2408    pub fn delete_shader(&self, shader: GlShader) {
2409        self.delete_shader_raw(shader.as_raw());
2410    }
2411
2412    fn create_program_raw(&self) -> u32 {
2413        unsafe { (self.create_program)() }
2414    }
2415
2416    pub fn create_program(&self) -> Result<GlProgram, String> {
2417        GlProgram::from_nonzero(self.create_program_raw())
2418    }
2419
2420    fn attach_shader_raw(&self, program: u32, shader: u32) {
2421        unsafe { (self.attach_shader)(program, shader) };
2422    }
2423
2424    fn attach_shader(&self, program: GlProgram, shader: GlShader) {
2425        self.attach_shader_raw(program.as_raw(), shader.as_raw());
2426    }
2427
2428    fn link_program_raw(&self, program: u32) {
2429        unsafe { (self.link_program)(program) };
2430    }
2431
2432    pub fn link_program(&self, program: GlProgram) -> Result<(), String> {
2433        self.link_program_raw(program.as_raw());
2434        let status = self.get_program_iv(program.as_raw(), GL_LINK_STATUS);
2435        if status == 0 {
2436            let log = self.get_program_info_log(program.as_raw());
2437            return Err(format!("program link failed: {log}"));
2438        }
2439        self.check_no_error("CompatGl::link_program")
2440    }
2441
2442    fn get_program_iv(&self, program: u32, parameter: u32) -> i32 {
2443        let mut value = 0;
2444        unsafe { (self.get_program_iv)(program, parameter, &mut value) };
2445        value
2446    }
2447
2448    fn get_program_info_log(&self, program: u32) -> String {
2449        let length = self.get_program_iv(program, GL_INFO_LOG_LENGTH);
2450        if length <= 1 {
2451            return "no log".to_string();
2452        }
2453
2454        let mut buffer = vec![0u8; length as usize];
2455        let mut written = 0;
2456        unsafe {
2457            (self.get_program_info_log)(
2458                program,
2459                length,
2460                &mut written,
2461                buffer.as_mut_ptr().cast::<c_char>(),
2462            )
2463        };
2464        String::from_utf8_lossy(&buffer[..written as usize]).into_owned()
2465    }
2466
2467    fn delete_program_raw(&self, program: u32) {
2468        unsafe { (self.delete_program)(program) };
2469    }
2470
2471    pub fn delete_program(&self, program: GlProgram) {
2472        self.delete_program_raw(program.as_raw());
2473    }
2474
2475    fn use_program_raw(&self, program: u32) {
2476        unsafe { (self.use_program)(program) };
2477    }
2478
2479    pub fn use_no_program(&self) {
2480        self.use_program_raw(0);
2481    }
2482
2483    pub fn use_program(&self, program: Option<GlProgram>) {
2484        self.use_program_raw(program.map_or(0, GlProgram::as_raw));
2485    }
2486
2487    fn gen_buffer_raw(&self) -> u32 {
2488        let mut id = 0;
2489        unsafe { (self.gen_buffers)(1, &mut id) };
2490        id
2491    }
2492
2493    pub fn gen_buffer(&self) -> Result<GlBuffer, String> {
2494        GlBuffer::from_nonzero(self.gen_buffer_raw())
2495    }
2496
2497    fn bind_buffer_raw(&self, target: GlBufferTarget, buffer: u32) {
2498        unsafe { (self.bind_buffer)(target.as_raw(), buffer) };
2499    }
2500
2501    pub fn unbind_buffer(&self, target: GlBufferTarget) {
2502        self.bind_buffer_raw(target, 0);
2503    }
2504
2505    pub fn bind_buffer(&self, target: GlBufferTarget, buffer: Option<GlBuffer>) {
2506        self.bind_buffer_raw(target, buffer.map_or(0, GlBuffer::as_raw));
2507    }
2508
2509    pub fn buffer_data<T>(
2510        &self,
2511        target: GlBufferTarget,
2512        data: &[T],
2513        usage: GlBufferUsage,
2514    ) -> Result<(), String> {
2515        let byte_len = std::mem::size_of_val(data);
2516        let byte_len = GlBufferByteSize::from_bytes(byte_len).as_isize("GL buffer upload")?;
2517        unsafe {
2518            (self.buffer_data)(
2519                target.as_raw(),
2520                byte_len,
2521                data.as_ptr().cast::<c_void>(),
2522                usage.as_raw(),
2523            );
2524        }
2525        Ok(())
2526    }
2527
2528    fn delete_buffer_raw(&self, id: u32) {
2529        unsafe { (self.delete_buffers)(1, &id) };
2530    }
2531
2532    pub fn delete_buffer(&self, buffer: GlBuffer) {
2533        self.delete_buffer_raw(buffer.as_raw());
2534    }
2535
2536    fn enable_vertex_attrib_array_raw(&self, index: u32) {
2537        unsafe { (self.enable_vertex_attrib_array)(index) };
2538    }
2539
2540    pub fn enable_vertex_attrib(&self, location: GlVertexAttribLocation) {
2541        self.enable_vertex_attrib_array_raw(location.as_raw());
2542    }
2543
2544    fn disable_vertex_attrib_array_raw(&self, index: u32) {
2545        unsafe { (self.disable_vertex_attrib_array)(index) };
2546    }
2547
2548    pub fn disable_vertex_attrib(&self, location: GlVertexAttribLocation) {
2549        self.disable_vertex_attrib_array_raw(location.as_raw());
2550    }
2551
2552    fn vertex_attrib_pointer_f32_raw(
2553        &self,
2554        index: u32,
2555        size: i32,
2556        normalized: bool,
2557        stride: i32,
2558        offset: usize,
2559    ) {
2560        unsafe {
2561            (self.vertex_attrib_pointer)(
2562                index,
2563                size,
2564                GL_FLOAT,
2565                if normalized { 1 } else { GL_FALSE },
2566                stride,
2567                offset as *const c_void,
2568            );
2569        }
2570    }
2571
2572    fn vertex_attrib_pointer_f32_at(
2573        &self,
2574        location: GlVertexAttribLocation,
2575        size: i32,
2576        normalized: bool,
2577        stride: i32,
2578        offset: usize,
2579    ) {
2580        self.vertex_attrib_pointer_f32_raw(location.as_raw(), size, normalized, stride, offset);
2581    }
2582
2583    pub fn vertex_attrib_pointer_f32(
2584        &self,
2585        location: GlVertexAttribLocation,
2586        layout: GlVertexAttribF32Layout,
2587    ) {
2588        self.vertex_attrib_pointer_f32_at(
2589            location,
2590            layout.components.as_gl_size(),
2591            layout.normalized,
2592            layout.stride.as_i32(),
2593            layout.offset.as_bytes(),
2594        );
2595    }
2596
2597    fn attrib_location(
2598        &self,
2599        program: u32,
2600        name: &str,
2601    ) -> Result<Option<GlVertexAttribLocation>, String> {
2602        let name = gl_string(name)?;
2603        let location = unsafe { (self.get_attrib_location)(program, name.as_ptr()) };
2604        Ok(GlVertexAttribLocation::from_raw(location))
2605    }
2606
2607    fn required_attrib(&self, program: u32, name: &str) -> Result<GlVertexAttribLocation, String> {
2608        self.attrib_location(program, name)?
2609            .ok_or_else(|| format!("shader linked without required active attribute {name}"))
2610    }
2611
2612    pub fn required_attrib_location(
2613        &self,
2614        program: GlProgram,
2615        name: &str,
2616    ) -> Result<GlVertexAttribLocation, String> {
2617        self.required_attrib(program.as_raw(), name)
2618    }
2619
2620    fn uniform_location(
2621        &self,
2622        program: u32,
2623        name: &str,
2624    ) -> Result<Option<GlUniformLocation>, String> {
2625        let name = gl_string(name)?;
2626        let location = unsafe { (self.get_uniform_location)(program, name.as_ptr()) };
2627        Ok(GlUniformLocation::from_raw(location))
2628    }
2629
2630    fn required_uniform(&self, program: u32, name: &str) -> Result<GlUniformLocation, String> {
2631        self.uniform_location(program, name)?
2632            .ok_or_else(|| format!("shader linked without required active uniform {name}"))
2633    }
2634
2635    pub fn required_uniform_location(
2636        &self,
2637        program: GlProgram,
2638        name: &str,
2639    ) -> Result<GlUniformLocation, String> {
2640        self.required_uniform(program.as_raw(), name)
2641    }
2642
2643    fn uniform_4fv_raw(&self, location: i32, values: &[f32; 4]) {
2644        unsafe { (self.uniform_4fv)(location, 1, values.as_ptr()) };
2645    }
2646
2647    pub fn uniform_4fv(&self, location: GlUniformLocation, values: &[f32; 4]) {
2648        self.uniform_4fv_raw(location.as_raw(), values);
2649    }
2650
2651    fn draw_arrays_raw(&self, mode: GlDrawMode, first: i32, count: i32) {
2652        unsafe { (self.draw_arrays)(mode.as_raw(), first, count) };
2653    }
2654
2655    pub fn draw_arrays(&self, mode: GlDrawMode, range: GlDrawRange) -> Result<(), String> {
2656        let (first, count) = range.as_gl_args("glDrawArrays")?;
2657        self.draw_arrays_raw(mode, first, count);
2658        Ok(())
2659    }
2660
2661    #[doc(hidden)]
2662    pub fn fake_for_testing(config: FakeGlConfig) -> Self {
2663        let gl = glsym::fake_for_testing(config);
2664        Self::from_glsym(gl)
2665    }
2666
2667    pub fn from_glsym(gl: glsym) -> Self {
2668        Self {
2669            clear: CompatGlClear::from_glsym(gl.clone()),
2670            create_shader: gl.create_shader,
2671            shader_source: gl.shader_source,
2672            compile_shader: gl.compile_shader,
2673            get_shader_iv: gl.get_shader_iv,
2674            get_shader_info_log: gl.get_shader_info_log,
2675            delete_shader: gl.delete_shader,
2676            create_program: gl.create_program,
2677            attach_shader: gl.attach_shader,
2678            link_program: gl.link_program,
2679            get_program_iv: gl.get_program_iv,
2680            get_program_info_log: gl.get_program_info_log,
2681            delete_program: gl.delete_program,
2682            use_program: gl.use_program,
2683            gen_buffers: gl.gen_buffers,
2684            bind_buffer: gl.bind_buffer,
2685            buffer_data: gl.buffer_data,
2686            delete_buffers: gl.delete_buffers,
2687            enable_vertex_attrib_array: gl.enable_vertex_attrib_array,
2688            disable_vertex_attrib_array: gl.disable_vertex_attrib_array,
2689            vertex_attrib_pointer: gl.vertex_attrib_pointer,
2690            get_uniform_location: gl.get_uniform_location,
2691            uniform_4fv: gl.uniform_4fv,
2692            get_attrib_location: gl.get_attrib_location,
2693            draw_arrays: gl.draw_arrays,
2694        }
2695    }
2696}
2697
2698impl CompatTextureGl {
2699    pub fn init(runtime: &Runtime<'_>) -> Result<Self, String> {
2700        let get_integer_v: Option<GlGetIntegerv> =
2701            load_optional_gl_symbol(runtime, "glGetIntegerv")?;
2702        Ok(Self {
2703            max_texture_size: get_integer_v.and_then(query_gl_max_texture_size),
2704            get_error: load_optional_gl_symbol(runtime, "glGetError")?,
2705            enable: load_gl_symbol(runtime, "glEnable")?,
2706            disable: load_gl_symbol(runtime, "glDisable")?,
2707            gen_textures: load_gl_symbol(runtime, "glGenTextures")?,
2708            bind_texture: load_gl_symbol(runtime, "glBindTexture")?,
2709            active_texture: load_gl_symbol(runtime, "glActiveTexture")?,
2710            tex_parameter_i: load_gl_symbol(runtime, "glTexParameteri")?,
2711            pixel_store_i: load_gl_symbol(runtime, "glPixelStorei")?,
2712            tex_image_2d: load_gl_symbol(runtime, "glTexImage2D")?,
2713            delete_textures: load_gl_symbol(runtime, "glDeleteTextures")?,
2714            get_uniform_location: load_gl_symbol(runtime, "glGetUniformLocation")?,
2715            uniform_1i: load_gl_symbol(runtime, "glUniform1i")?,
2716            uniform_4fv: load_gl_symbol(runtime, "glUniform4fv")?,
2717            blend_func: load_gl_symbol(runtime, "glBlendFunc")?,
2718        })
2719    }
2720
2721    pub fn max_texture_size(&self) -> Option<u32> {
2722        self.max_texture_size
2723    }
2724
2725    pub fn from_glsym(gl: glsym) -> Self {
2726        Self {
2727            max_texture_size: gl.max_texture_size,
2728            get_error: gl.get_error,
2729            enable: gl.enable,
2730            disable: gl.disable,
2731            gen_textures: gl.gen_textures,
2732            bind_texture: gl.bind_texture,
2733            active_texture: gl.active_texture,
2734            tex_parameter_i: gl.tex_parameter_i,
2735            pixel_store_i: gl.pixel_store_i,
2736            tex_image_2d: gl.tex_image_2d,
2737            delete_textures: gl.delete_textures,
2738            get_uniform_location: gl.get_uniform_location,
2739            uniform_1i: gl.uniform_1i,
2740            uniform_4fv: gl.uniform_4fv,
2741            blend_func: gl.blend_func,
2742        }
2743    }
2744
2745    pub fn enable(&self, capability: GlCapability) {
2746        unsafe { (self.enable)(capability.as_raw()) };
2747    }
2748
2749    pub fn disable(&self, capability: GlCapability) {
2750        unsafe { (self.disable)(capability.as_raw()) };
2751    }
2752
2753    fn gen_texture_raw(&self) -> u32 {
2754        let mut id = 0;
2755        unsafe { (self.gen_textures)(1, &mut id) };
2756        id
2757    }
2758
2759    pub fn gen_texture(&self) -> Result<GlTexture, String> {
2760        GlTexture::from_nonzero(self.gen_texture_raw())
2761    }
2762
2763    fn bind_texture_raw(&self, target: GlTextureTarget, texture: u32) {
2764        unsafe { (self.bind_texture)(target.as_raw(), texture) };
2765    }
2766
2767    pub fn unbind_texture(&self, target: GlTextureTarget) {
2768        self.bind_texture_raw(target, 0);
2769    }
2770
2771    pub fn bind_texture(&self, target: GlTextureTarget, texture: Option<GlTexture>) {
2772        self.bind_texture_raw(target, texture.map_or(0, GlTexture::as_raw));
2773    }
2774
2775    pub fn active_texture(&self, unit: GlTextureUnit) -> Result<(), String> {
2776        unsafe { (self.active_texture)(unit.as_raw()?) };
2777        Ok(())
2778    }
2779
2780    pub fn tex_min_filter(&self, target: GlTextureTarget, filter: GlTextureMinFilter) {
2781        unsafe {
2782            (self.tex_parameter_i)(
2783                target.as_raw(),
2784                GL_TEXTURE_MIN_FILTER,
2785                filter.as_raw() as i32,
2786            )
2787        };
2788    }
2789
2790    pub fn tex_mag_filter(&self, target: GlTextureTarget, filter: GlTextureMagFilter) {
2791        unsafe {
2792            (self.tex_parameter_i)(
2793                target.as_raw(),
2794                GL_TEXTURE_MAG_FILTER,
2795                filter.as_raw() as i32,
2796            )
2797        };
2798    }
2799
2800    pub fn tex_wrap_s(&self, target: GlTextureTarget, wrap: GlTextureWrap) {
2801        unsafe { (self.tex_parameter_i)(target.as_raw(), GL_TEXTURE_WRAP_S, wrap.as_raw() as i32) };
2802    }
2803
2804    pub fn tex_wrap_t(&self, target: GlTextureTarget, wrap: GlTextureWrap) {
2805        unsafe { (self.tex_parameter_i)(target.as_raw(), GL_TEXTURE_WRAP_T, wrap.as_raw() as i32) };
2806    }
2807
2808    pub fn tex_wrap_r(&self, target: GlTextureTarget, wrap: GlTextureWrap) {
2809        unsafe { (self.tex_parameter_i)(target.as_raw(), GL_TEXTURE_WRAP_R, wrap.as_raw() as i32) };
2810    }
2811
2812    pub fn pixel_store_unpack_alignment(&self, alignment: GlPixelStoreAlignment) {
2813        unsafe { (self.pixel_store_i)(GL_UNPACK_ALIGNMENT, alignment.as_raw()) };
2814    }
2815
2816    fn tex_image_2d_raw(
2817        &self,
2818        target: GlTextureTarget,
2819        internal_format: GlTextureInternalFormat,
2820        level: GlTextureLevel,
2821        size: GlTextureSize2D,
2822        format: GlTextureFormat,
2823        data_type: GlTextureDataType,
2824        bytes: Option<&[u8]>,
2825    ) -> Result<(), String> {
2826        let level = level.as_i32("glTexImage2D")?;
2827        let (width, height) = size.as_gl_args("glTexImage2D")?;
2828        let pixels = bytes
2829            .map(|bytes| bytes.as_ptr().cast::<c_void>())
2830            .unwrap_or(std::ptr::null());
2831        unsafe {
2832            (self.tex_image_2d)(
2833                target.as_raw(),
2834                level,
2835                internal_format.as_raw() as i32,
2836                width,
2837                height,
2838                0,
2839                format.as_raw(),
2840                data_type.as_raw(),
2841                pixels,
2842            );
2843        }
2844        Ok(())
2845    }
2846
2847    pub fn tex_image_2d(
2848        &self,
2849        target: GlTextureTarget,
2850        internal_format: GlTextureInternalFormat,
2851        level: GlTextureLevel,
2852        size: GlTextureSize2D,
2853        format: GlTextureFormat,
2854        data_type: GlTextureDataType,
2855        bytes: Option<&[u8]>,
2856    ) -> Result<(), String> {
2857        self.tex_image_2d_raw(
2858            target,
2859            internal_format,
2860            level,
2861            size,
2862            format,
2863            data_type,
2864            bytes,
2865        )
2866    }
2867
2868    fn delete_texture_raw(&self, id: u32) {
2869        unsafe { (self.delete_textures)(1, &id) };
2870    }
2871
2872    pub fn delete_texture(&self, texture: GlTexture) {
2873        self.delete_texture_raw(texture.as_raw());
2874    }
2875
2876    fn uniform_location(
2877        &self,
2878        program: u32,
2879        name: &str,
2880    ) -> Result<Option<GlUniformLocation>, String> {
2881        let name = gl_string(name)?;
2882        let location = unsafe { (self.get_uniform_location)(program, name.as_ptr()) };
2883        Ok(GlUniformLocation::from_raw(location))
2884    }
2885
2886    fn required_uniform(&self, program: u32, name: &str) -> Result<GlUniformLocation, String> {
2887        self.uniform_location(program, name)?
2888            .ok_or_else(|| format!("shader linked without required active uniform {name}"))
2889    }
2890
2891    pub fn required_uniform_location(
2892        &self,
2893        program: GlProgram,
2894        name: &str,
2895    ) -> Result<GlUniformLocation, String> {
2896        self.required_uniform(program.as_raw(), name)
2897    }
2898
2899    fn uniform_1i_raw(&self, location: i32, value: i32) {
2900        unsafe { (self.uniform_1i)(location, value) };
2901    }
2902
2903    pub fn uniform_1i(&self, location: GlUniformLocation, value: i32) {
2904        self.uniform_1i_raw(location.as_raw(), value);
2905    }
2906
2907    fn uniform_4fv_raw(&self, location: i32, values: &[f32; 4]) {
2908        unsafe { (self.uniform_4fv)(location, 1, values.as_ptr()) };
2909    }
2910
2911    pub fn uniform_4fv(&self, location: GlUniformLocation, values: &[f32; 4]) {
2912        self.uniform_4fv_raw(location.as_raw(), values);
2913    }
2914
2915    pub fn blend_func(&self, source: GlBlendFactor, destination: GlBlendFactor) {
2916        unsafe { (self.blend_func)(source.as_raw(), destination.as_raw()) };
2917    }
2918
2919    pub fn check_no_error(&self, operation: &str) -> Result<(), String> {
2920        collect_gl_errors(self.get_error, operation)
2921    }
2922
2923    #[doc(hidden)]
2924    pub fn fake_for_testing(config: FakeGlConfig) -> Self {
2925        let gl = glsym::fake_for_testing(config);
2926        Self::from_glsym(gl)
2927    }
2928}
2929
2930impl Gl {
2931    pub fn init(runtime: &Runtime<'_>) -> Result<Self, String> {
2932        let clear = CompatGlClear::init(runtime)?;
2933        Ok(Self::init_from_clear(runtime, clear))
2934    }
2935
2936    pub fn init_from_clear(runtime: &Runtime<'_>, clear: CompatGlClear) -> Self {
2937        let compat = CompatGl::init_from_clear(runtime, clear).ok();
2938        let texture = CompatTextureGl::init(runtime).ok();
2939        let full = glsym::init(runtime).ok();
2940        Self {
2941            clear,
2942            compat,
2943            texture,
2944            full,
2945        }
2946    }
2947
2948    pub fn context_type(&self) -> HwContextType {
2949        self.clear.context_type()
2950    }
2951
2952    pub fn version_info(&self) -> GlVersionInfo {
2953        self.clear.version_info()
2954    }
2955
2956    pub fn max_texture_size(&self) -> Option<u32> {
2957        self.full
2958            .as_ref()
2959            .and_then(|gl| gl.max_texture_size())
2960            .or_else(|| {
2961                self.texture
2962                    .as_ref()
2963                    .and_then(CompatTextureGl::max_texture_size)
2964            })
2965    }
2966
2967    pub fn supports_shader_pipeline(&self) -> bool {
2968        self.compat.is_some()
2969    }
2970
2971    pub fn supports_textures(&self) -> bool {
2972        self.texture.is_some()
2973    }
2974
2975    pub fn supports_blending(&self) -> bool {
2976        self.texture.is_some() || self.full.is_some()
2977    }
2978
2979    pub fn supports_vertex_arrays(&self) -> bool {
2980        self.full
2981            .as_ref()
2982            .is_some_and(|gl| gl.supports_vertex_arrays())
2983    }
2984
2985    pub fn clear_color(&self, r: f32, g: f32, b: f32, a: f32) {
2986        self.clear.clear_color(r, g, b, a);
2987    }
2988
2989    pub fn clear_color_buffer(&self) {
2990        if let Some(full) = self.full.as_ref() {
2991            full.clear_color_buffer();
2992        } else {
2993            unsafe { (self.clear.clear)(GL_COLOR_BUFFER_BIT) };
2994        }
2995    }
2996
2997    pub fn clear_color_depth_buffer(&self) {
2998        self.clear.clear_color_depth_buffer();
2999    }
3000
3001    pub fn viewport(&self, rect: GlRect) -> Result<(), String> {
3002        self.clear.viewport(rect)
3003    }
3004
3005    pub fn unbind_framebuffer(&self, target: GlFramebufferTarget) {
3006        self.clear.unbind_framebuffer(target);
3007    }
3008
3009    pub fn bind_framebuffer(
3010        &self,
3011        target: GlFramebufferTarget,
3012        framebuffer: Option<GlFramebuffer>,
3013    ) -> Result<(), String> {
3014        self.clear.bind_framebuffer(target, framebuffer)
3015    }
3016
3017    pub fn check_no_error(&self, operation: &str) -> Result<(), String> {
3018        self.clear.check_no_error(operation)
3019    }
3020
3021    pub fn check_bound_framebuffer_complete(
3022        &self,
3023        target: GlFramebufferTarget,
3024    ) -> Result<(), String> {
3025        self.clear.check_bound_framebuffer_complete(target)
3026    }
3027
3028    pub fn enable(&self, capability: GlCapability) {
3029        if let Some(full) = self.full.as_ref() {
3030            full.enable(capability);
3031        } else if let Some(texture) = self.texture.as_ref() {
3032            texture.enable(capability);
3033        }
3034    }
3035
3036    pub fn disable(&self, capability: GlCapability) {
3037        if let Some(full) = self.full.as_ref() {
3038            full.disable(capability);
3039        } else if let Some(texture) = self.texture.as_ref() {
3040            texture.disable(capability);
3041        }
3042    }
3043
3044    pub fn build_program(
3045        &self,
3046        vertex_shader: &str,
3047        fragment_shader: &str,
3048    ) -> Result<GlProgram, String> {
3049        self.compat()?.build_program(vertex_shader, fragment_shader)
3050    }
3051
3052    pub fn compile_shader_source(
3053        &self,
3054        stage: GlShaderStage,
3055        source: &str,
3056    ) -> Result<GlShader, String> {
3057        self.compat()?.compile_shader_source(stage, source)
3058    }
3059
3060    pub fn delete_shader(&self, shader: GlShader) {
3061        if let Some(compat) = self.compat.as_ref() {
3062            compat.delete_shader(shader);
3063        }
3064    }
3065
3066    pub fn create_program(&self) -> Result<GlProgram, String> {
3067        self.compat()?.create_program()
3068    }
3069
3070    pub fn link_program(&self, program: GlProgram) -> Result<(), String> {
3071        self.compat()?.link_program(program)
3072    }
3073
3074    pub fn delete_program(&self, program: GlProgram) {
3075        if let Some(compat) = self.compat.as_ref() {
3076            compat.delete_program(program);
3077        }
3078    }
3079
3080    pub fn use_no_program(&self) {
3081        self.use_program(None);
3082    }
3083
3084    pub fn use_program(&self, program: Option<GlProgram>) {
3085        if let Some(compat) = self.compat.as_ref() {
3086            compat.use_program(program);
3087        }
3088    }
3089
3090    pub fn gen_buffer(&self) -> Result<GlBuffer, String> {
3091        self.compat()?.gen_buffer()
3092    }
3093
3094    pub fn unbind_buffer(&self, target: GlBufferTarget) {
3095        if let Some(compat) = self.compat.as_ref() {
3096            compat.unbind_buffer(target);
3097        }
3098    }
3099
3100    pub fn bind_buffer(&self, target: GlBufferTarget, buffer: Option<GlBuffer>) {
3101        if let Some(compat) = self.compat.as_ref() {
3102            compat.bind_buffer(target, buffer);
3103        }
3104    }
3105
3106    pub fn buffer_data<T>(
3107        &self,
3108        target: GlBufferTarget,
3109        data: &[T],
3110        usage: GlBufferUsage,
3111    ) -> Result<(), String> {
3112        self.compat()?.buffer_data(target, data, usage)
3113    }
3114
3115    pub fn delete_buffer(&self, buffer: GlBuffer) {
3116        if let Some(compat) = self.compat.as_ref() {
3117            compat.delete_buffer(buffer);
3118        }
3119    }
3120
3121    pub fn enable_vertex_attrib(&self, location: GlVertexAttribLocation) {
3122        if let Some(compat) = self.compat.as_ref() {
3123            compat.enable_vertex_attrib(location);
3124        }
3125    }
3126
3127    pub fn disable_vertex_attrib(&self, location: GlVertexAttribLocation) {
3128        if let Some(compat) = self.compat.as_ref() {
3129            compat.disable_vertex_attrib(location);
3130        }
3131    }
3132
3133    pub fn vertex_attrib_pointer_f32(
3134        &self,
3135        location: GlVertexAttribLocation,
3136        layout: GlVertexAttribF32Layout,
3137    ) {
3138        if let Some(compat) = self.compat.as_ref() {
3139            compat.vertex_attrib_pointer_f32(location, layout);
3140        }
3141    }
3142
3143    pub fn required_attrib_location(
3144        &self,
3145        program: GlProgram,
3146        name: &str,
3147    ) -> Result<GlVertexAttribLocation, String> {
3148        self.compat()?.required_attrib_location(program, name)
3149    }
3150
3151    pub fn required_uniform_location(
3152        &self,
3153        program: GlProgram,
3154        name: &str,
3155    ) -> Result<GlUniformLocation, String> {
3156        if let Some(compat) = self.compat.as_ref() {
3157            compat.required_uniform_location(program, name)
3158        } else if let Some(texture) = self.texture.as_ref() {
3159            texture.required_uniform_location(program, name)
3160        } else {
3161            Err(missing_gl_feature("shader uniform reflection"))
3162        }
3163    }
3164
3165    pub fn uniform_1i(&self, location: GlUniformLocation, value: i32) {
3166        if let Some(texture) = self.texture.as_ref() {
3167            texture.uniform_1i(location, value);
3168        } else if let Some(full) = self.full.as_ref() {
3169            full.uniform_1i(location, value);
3170        }
3171    }
3172
3173    pub fn uniform_4fv(&self, location: GlUniformLocation, values: &[f32; 4]) {
3174        if let Some(compat) = self.compat.as_ref() {
3175            compat.uniform_4fv(location, values);
3176        } else if let Some(texture) = self.texture.as_ref() {
3177            texture.uniform_4fv(location, values);
3178        }
3179    }
3180
3181    pub fn draw_arrays(&self, mode: GlDrawMode, range: GlDrawRange) -> Result<(), String> {
3182        self.compat()?.draw_arrays(mode, range)
3183    }
3184
3185    pub fn gen_texture(&self) -> Result<GlTexture, String> {
3186        self.texture()?.gen_texture()
3187    }
3188
3189    pub fn unbind_texture(&self, target: GlTextureTarget) {
3190        if let Some(texture) = self.texture.as_ref() {
3191            texture.unbind_texture(target);
3192        }
3193    }
3194
3195    pub fn bind_texture(&self, target: GlTextureTarget, texture_id: Option<GlTexture>) {
3196        if let Some(texture) = self.texture.as_ref() {
3197            texture.bind_texture(target, texture_id);
3198        }
3199    }
3200
3201    pub fn active_texture(&self, unit: GlTextureUnit) -> Result<(), String> {
3202        if let Some(texture) = self.texture.as_ref() {
3203            texture.active_texture(unit)
3204        } else {
3205            Ok(())
3206        }
3207    }
3208
3209    pub fn tex_min_filter(&self, target: GlTextureTarget, filter: GlTextureMinFilter) {
3210        if let Some(texture) = self.texture.as_ref() {
3211            texture.tex_min_filter(target, filter);
3212        }
3213    }
3214
3215    pub fn tex_mag_filter(&self, target: GlTextureTarget, filter: GlTextureMagFilter) {
3216        if let Some(texture) = self.texture.as_ref() {
3217            texture.tex_mag_filter(target, filter);
3218        }
3219    }
3220
3221    pub fn tex_wrap_s(&self, target: GlTextureTarget, wrap: GlTextureWrap) {
3222        if let Some(texture) = self.texture.as_ref() {
3223            texture.tex_wrap_s(target, wrap);
3224        }
3225    }
3226
3227    pub fn tex_wrap_t(&self, target: GlTextureTarget, wrap: GlTextureWrap) {
3228        if let Some(texture) = self.texture.as_ref() {
3229            texture.tex_wrap_t(target, wrap);
3230        }
3231    }
3232
3233    pub fn pixel_store_unpack_alignment(&self, alignment: GlPixelStoreAlignment) {
3234        if let Some(texture) = self.texture.as_ref() {
3235            texture.pixel_store_unpack_alignment(alignment);
3236        }
3237    }
3238
3239    pub fn tex_image_2d(
3240        &self,
3241        target: GlTextureTarget,
3242        internal_format: GlTextureInternalFormat,
3243        level: GlTextureLevel,
3244        size: GlTextureSize2D,
3245        format: GlTextureFormat,
3246        data_type: GlTextureDataType,
3247        bytes: Option<&[u8]>,
3248    ) -> Result<(), String> {
3249        self.texture()?.tex_image_2d(
3250            target,
3251            internal_format,
3252            level,
3253            size,
3254            format,
3255            data_type,
3256            bytes,
3257        )
3258    }
3259
3260    pub fn delete_texture(&self, texture_id: GlTexture) {
3261        if let Some(texture) = self.texture.as_ref() {
3262            texture.delete_texture(texture_id);
3263        }
3264    }
3265
3266    pub fn blend_func(&self, source: GlBlendFactor, destination: GlBlendFactor) {
3267        if let Some(texture) = self.texture.as_ref() {
3268            texture.blend_func(source, destination);
3269        } else if let Some(full) = self.full.as_ref() {
3270            full.blend_func(source, destination);
3271        }
3272    }
3273
3274    pub fn gen_vertex_array(&self) -> Result<GlVertexArray, String> {
3275        if let Some(full) = self.full.as_ref() {
3276            full.gen_vertex_array()
3277        } else {
3278            Err(missing_gl_feature("vertex arrays"))
3279        }
3280    }
3281
3282    pub fn bind_vertex_array(&self, array: Option<GlVertexArray>) -> Result<(), String> {
3283        if let Some(full) = self.full.as_ref() {
3284            full.bind_vertex_array(array)
3285        } else {
3286            Ok(())
3287        }
3288    }
3289
3290    pub fn unbind_vertex_array(&self) -> Result<(), String> {
3291        self.bind_vertex_array(None)
3292    }
3293
3294    pub fn delete_vertex_array(&self, array: GlVertexArray) -> Result<(), String> {
3295        if let Some(full) = self.full.as_ref() {
3296            full.delete_vertex_array(array)
3297        } else {
3298            Ok(())
3299        }
3300    }
3301
3302    #[doc(hidden)]
3303    pub fn fake_for_testing(config: FakeGlConfig) -> Self {
3304        let gl = glsym::fake_for_testing(config);
3305        Self::from_glsym(gl)
3306    }
3307
3308    pub fn from_glsym(gl: glsym) -> Self {
3309        Self {
3310            clear: CompatGlClear::from_glsym(gl.clone()),
3311            compat: Some(CompatGl::from_glsym(gl.clone())),
3312            texture: Some(CompatTextureGl::from_glsym(gl.clone())),
3313            full: Some(gl),
3314        }
3315    }
3316
3317    fn compat(&self) -> Result<&CompatGl, String> {
3318        self.compat
3319            .as_ref()
3320            .ok_or_else(|| missing_gl_feature("shader and buffer drawing"))
3321    }
3322
3323    fn texture(&self) -> Result<&CompatTextureGl, String> {
3324        self.texture
3325            .as_ref()
3326            .ok_or_else(|| missing_gl_feature("texture rendering"))
3327    }
3328}
3329
3330fn missing_gl_feature(feature: &str) -> String {
3331    format!("{feature} is not available for this GL context")
3332}
3333
3334impl glsym {
3335    pub fn init(runtime: &Runtime<'_>) -> Result<Self, String> {
3336        let context_type = runtime
3337            .hw_context_type()
3338            .ok_or_else(|| "hardware render context type is not available".to_string())?;
3339        if !context_type.is_opengl_family() {
3340            return Err(format!(
3341                "glsym requires an OpenGL-family context, got {context_type:?}"
3342            ));
3343        }
3344        let get_string: GlGetString = load_gl_symbol(runtime, "glGetString")?;
3345        let get_integer_v: GlGetIntegerv = load_gl_symbol(runtime, "glGetIntegerv")?;
3346        let get_string_i: Option<GlGetStringi> = load_optional_gl_symbol(runtime, "glGetStringi")?;
3347        let version_info = query_gl_version_info(get_string, context_type);
3348        let extensions_string = if version_info.is_gles || !version_info.version_at_least(3, 0) {
3349            query_gl_string(get_string, GL_EXTENSIONS)
3350        } else {
3351            // Desktop OpenGL 3+ core profiles require glGetStringi for
3352            // extensions; glGetString(GL_EXTENSIONS) is invalid and can leave a
3353            // sticky GL error that later shader/bootstrap validation reports.
3354            query_gl_indexed_extensions(get_integer_v, get_string_i)
3355        };
3356        let get_shader_precision_format: Option<GlGetShaderPrecisionFormat> =
3357            load_optional_gl_symbol(runtime, "glGetShaderPrecisionFormat")?;
3358        Ok(Self {
3359            context_type,
3360            version_info,
3361            vendor_string: query_gl_string(get_string, GL_VENDOR),
3362            renderer_string: query_gl_string(get_string, GL_RENDERER),
3363            version_string: query_gl_string(get_string, GL_VERSION),
3364            extensions_string,
3365            max_texture_size: query_gl_max_texture_size(get_integer_v),
3366            max_texture_image_units: query_gl_max_texture_image_units(get_integer_v),
3367            max_varying_vectors: query_gl_max_varying_vectors(get_integer_v),
3368            fragment_highp_float: query_fragment_highp_float(
3369                get_shader_precision_format,
3370                version_info,
3371            ),
3372            clear_color: load_gl_symbol(runtime, "glClearColor")?,
3373            clear: load_gl_symbol(runtime, "glClear")?,
3374            enable: load_gl_symbol(runtime, "glEnable")?,
3375            disable: load_gl_symbol(runtime, "glDisable")?,
3376            depth_func: load_optional_gl_symbol(runtime, "glDepthFunc")?,
3377            depth_mask: load_optional_gl_symbol(runtime, "glDepthMask")?,
3378            depth_range_f: load_optional_gl_symbol(runtime, "glDepthRangef")?,
3379            cull_face: load_optional_gl_symbol(runtime, "glCullFace")?,
3380            front_face: load_optional_gl_symbol(runtime, "glFrontFace")?,
3381            stencil_func: load_optional_gl_symbol(runtime, "glStencilFunc")?,
3382            stencil_mask: load_optional_gl_symbol(runtime, "glStencilMask")?,
3383            stencil_op: load_optional_gl_symbol(runtime, "glStencilOp")?,
3384            stencil_func_separate: load_optional_gl_symbol(runtime, "glStencilFuncSeparate")?,
3385            stencil_mask_separate: load_optional_gl_symbol(runtime, "glStencilMaskSeparate")?,
3386            stencil_op_separate: load_optional_gl_symbol(runtime, "glStencilOpSeparate")?,
3387            color_mask: load_optional_gl_symbol(runtime, "glColorMask")?,
3388            polygon_offset: load_optional_gl_symbol(runtime, "glPolygonOffset")?,
3389            gen_queries: load_optional_gl_symbol(runtime, "glGenQueries")?,
3390            delete_queries: load_optional_gl_symbol(runtime, "glDeleteQueries")?,
3391            begin_query: load_optional_gl_symbol(runtime, "glBeginQuery")?,
3392            end_query: load_optional_gl_symbol(runtime, "glEndQuery")?,
3393            get_query_object_uiv: load_optional_gl_symbol(runtime, "glGetQueryObjectuiv")?,
3394            fence_sync: load_optional_gl_symbol(runtime, "glFenceSync")?,
3395            client_wait_sync: load_optional_gl_symbol(runtime, "glClientWaitSync")?,
3396            wait_sync: load_optional_gl_symbol(runtime, "glWaitSync")?,
3397            delete_sync: load_optional_gl_symbol(runtime, "glDeleteSync")?,
3398            read_pixels: load_optional_gl_symbol(runtime, "glReadPixels")?,
3399            read_buffer: load_optional_gl_symbol(runtime, "glReadBuffer")?,
3400            draw_buffers: load_optional_gl_symbol(runtime, "glDrawBuffers")?,
3401            viewport: load_gl_symbol(runtime, "glViewport")?,
3402            scissor: load_gl_symbol(runtime, "glScissor")?,
3403            create_shader: load_gl_symbol(runtime, "glCreateShader")?,
3404            shader_source: load_gl_symbol(runtime, "glShaderSource")?,
3405            compile_shader: load_gl_symbol(runtime, "glCompileShader")?,
3406            get_shader_iv: load_gl_symbol(runtime, "glGetShaderiv")?,
3407            get_shader_info_log: load_gl_symbol(runtime, "glGetShaderInfoLog")?,
3408            delete_shader: load_gl_symbol(runtime, "glDeleteShader")?,
3409            create_program: load_gl_symbol(runtime, "glCreateProgram")?,
3410            attach_shader: load_gl_symbol(runtime, "glAttachShader")?,
3411            link_program: load_gl_symbol(runtime, "glLinkProgram")?,
3412            get_program_iv: load_gl_symbol(runtime, "glGetProgramiv")?,
3413            get_program_info_log: load_gl_symbol(runtime, "glGetProgramInfoLog")?,
3414            delete_program: load_gl_symbol(runtime, "glDeleteProgram")?,
3415            use_program: load_gl_symbol(runtime, "glUseProgram")?,
3416            gen_buffers: load_gl_symbol(runtime, "glGenBuffers")?,
3417            bind_buffer: load_gl_symbol(runtime, "glBindBuffer")?,
3418            bind_buffer_base: load_optional_gl_symbol_aliases(runtime, &["glBindBufferBase"])?,
3419            bind_buffer_range: load_optional_gl_symbol_aliases(runtime, &["glBindBufferRange"])?,
3420            buffer_data: load_gl_symbol(runtime, "glBufferData")?,
3421            buffer_sub_data: load_gl_symbol(runtime, "glBufferSubData")?,
3422            copy_buffer_sub_data: load_optional_gl_symbol(runtime, "glCopyBufferSubData")?,
3423            delete_buffers: load_gl_symbol(runtime, "glDeleteBuffers")?,
3424            gen_textures: load_gl_symbol(runtime, "glGenTextures")?,
3425            bind_texture: load_gl_symbol(runtime, "glBindTexture")?,
3426            active_texture: load_gl_symbol(runtime, "glActiveTexture")?,
3427            tex_parameter_i: load_gl_symbol(runtime, "glTexParameteri")?,
3428            pixel_store_i: load_gl_symbol(runtime, "glPixelStorei")?,
3429            tex_image_2d: load_gl_symbol(runtime, "glTexImage2D")?,
3430            tex_sub_image_2d: load_gl_symbol(runtime, "glTexSubImage2D")?,
3431            tex_image_3d: load_optional_gl_symbol(runtime, "glTexImage3D")?,
3432            tex_sub_image_3d: load_optional_gl_symbol(runtime, "glTexSubImage3D")?,
3433            generate_mipmap: load_optional_gl_symbol(runtime, "glGenerateMipmap")?,
3434            delete_textures: load_gl_symbol(runtime, "glDeleteTextures")?,
3435            gen_vertex_arrays: load_optional_gl_symbol(runtime, "glGenVertexArrays")?,
3436            bind_vertex_array: load_optional_gl_symbol(runtime, "glBindVertexArray")?,
3437            delete_vertex_arrays: load_optional_gl_symbol(runtime, "glDeleteVertexArrays")?,
3438            enable_vertex_attrib_array: load_gl_symbol(runtime, "glEnableVertexAttribArray")?,
3439            disable_vertex_attrib_array: load_gl_symbol(runtime, "glDisableVertexAttribArray")?,
3440            vertex_attrib_pointer: load_gl_symbol(runtime, "glVertexAttribPointer")?,
3441            vertex_attrib_divisor: load_optional_gl_symbol_aliases(
3442                runtime,
3443                &["glVertexAttribDivisor"],
3444            )?,
3445            get_uniform_location: load_gl_symbol(runtime, "glGetUniformLocation")?,
3446            get_attrib_location: load_gl_symbol(runtime, "glGetAttribLocation")?,
3447            bind_attrib_location: load_optional_gl_symbol(runtime, "glBindAttribLocation")?,
3448            uniform_1i: load_gl_symbol(runtime, "glUniform1i")?,
3449            uniform_1f: load_gl_symbol(runtime, "glUniform1f")?,
3450            uniform_2f: load_gl_symbol(runtime, "glUniform2f")?,
3451            uniform_3f: load_gl_symbol(runtime, "glUniform3f")?,
3452            uniform_4f: load_gl_symbol(runtime, "glUniform4f")?,
3453            uniform_4fv: load_gl_symbol(runtime, "glUniform4fv")?,
3454            uniform_matrix_3fv: load_gl_symbol(runtime, "glUniformMatrix3fv")?,
3455            uniform_matrix_4fv: load_gl_symbol(runtime, "glUniformMatrix4fv")?,
3456            draw_arrays: load_gl_symbol(runtime, "glDrawArrays")?,
3457            draw_arrays_instanced: load_optional_gl_symbol_aliases(
3458                runtime,
3459                &["glDrawArraysInstanced"],
3460            )?,
3461            draw_elements: load_gl_symbol(runtime, "glDrawElements")?,
3462            draw_range_elements: load_optional_gl_symbol(runtime, "glDrawRangeElements")?,
3463            draw_elements_instanced: load_optional_gl_symbol_aliases(
3464                runtime,
3465                &["glDrawElementsInstanced"],
3466            )?,
3467            blend_color: load_optional_gl_symbol(runtime, "glBlendColor")?,
3468            blend_func: load_gl_symbol(runtime, "glBlendFunc")?,
3469            blend_func_separate: load_optional_gl_symbol(runtime, "glBlendFuncSeparate")?,
3470            blend_equation: load_gl_symbol(runtime, "glBlendEquation")?,
3471            blend_equation_separate: load_optional_gl_symbol(runtime, "glBlendEquationSeparate")?,
3472            gen_framebuffers: load_optional_gl_symbol(runtime, "glGenFramebuffers")?,
3473            bind_framebuffer: load_gl_symbol(runtime, "glBindFramebuffer")?,
3474            delete_framebuffers: load_optional_gl_symbol(runtime, "glDeleteFramebuffers")?,
3475            framebuffer_texture_2d: load_optional_gl_symbol(runtime, "glFramebufferTexture2D")?,
3476            gen_renderbuffers: load_optional_gl_symbol(runtime, "glGenRenderbuffers")?,
3477            bind_renderbuffer: load_optional_gl_symbol(runtime, "glBindRenderbuffer")?,
3478            renderbuffer_storage: load_optional_gl_symbol(runtime, "glRenderbufferStorage")?,
3479            delete_renderbuffers: load_optional_gl_symbol(runtime, "glDeleteRenderbuffers")?,
3480            framebuffer_renderbuffer: load_optional_gl_symbol(
3481                runtime,
3482                "glFramebufferRenderbuffer",
3483            )?,
3484            blit_framebuffer: load_optional_gl_symbol_aliases(runtime, &["glBlitFramebuffer"])?,
3485            // Product rendering treats GL error/FBO checks as part of the
3486            // libretro shared-context contract. Narrow compatibility symbol
3487            // groups keep these optional, but the full renderer must not
3488            // silently disable them.
3489            get_error: Some(load_gl_symbol(runtime, "glGetError")?),
3490            check_framebuffer_status: Some(load_gl_symbol(runtime, "glCheckFramebufferStatus")?),
3491            invalidate_framebuffer: load_optional_gl_symbol(runtime, "glInvalidateFramebuffer")?,
3492        })
3493    }
3494
3495    pub fn context_type(&self) -> HwContextType {
3496        self.context_type
3497    }
3498
3499    pub fn version_info(&self) -> GlVersionInfo {
3500        self.version_info
3501    }
3502
3503    pub fn vendor_string(&self) -> &str {
3504        &self.vendor_string
3505    }
3506
3507    pub fn renderer_string(&self) -> &str {
3508        &self.renderer_string
3509    }
3510
3511    pub fn version_string(&self) -> &str {
3512        &self.version_string
3513    }
3514
3515    pub fn extensions_string(&self) -> &str {
3516        &self.extensions_string
3517    }
3518
3519    pub fn max_texture_size(&self) -> Option<u32> {
3520        self.max_texture_size
3521    }
3522
3523    pub fn max_texture_image_units(&self) -> Option<u32> {
3524        self.max_texture_image_units
3525    }
3526
3527    pub fn max_varying_vectors(&self) -> Option<u32> {
3528        self.max_varying_vectors
3529    }
3530
3531    pub fn fragment_highp_float(&self) -> Option<bool> {
3532        self.fragment_highp_float
3533    }
3534
3535    pub fn supports_npot_repeat(&self) -> bool {
3536        !self.version_info.is_gles || self.version_info.version_at_least(3, 0)
3537    }
3538
3539    pub fn supports_invalidate_framebuffer(&self) -> bool {
3540        self.invalidate_framebuffer.is_some()
3541    }
3542
3543    pub fn has_extension(&self, name: &str) -> bool {
3544        self.extensions_string
3545            .split_whitespace()
3546            .any(|extension| extension == name)
3547    }
3548
3549    pub fn clear_color(&self, r: f32, g: f32, b: f32, a: f32) {
3550        unsafe { (self.clear_color)(r, g, b, a) };
3551    }
3552
3553    pub fn clear_color_buffer(&self) {
3554        unsafe { (self.clear)(GL_COLOR_BUFFER_BIT) };
3555    }
3556
3557    pub fn clear_color_depth_buffer(&self) {
3558        unsafe { (self.clear)(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) };
3559    }
3560
3561    pub fn enable(&self, capability: GlCapability) {
3562        unsafe { (self.enable)(capability.as_raw()) };
3563    }
3564
3565    pub fn disable(&self, capability: GlCapability) {
3566        unsafe { (self.disable)(capability.as_raw()) };
3567    }
3568
3569    pub fn depth_func(&self, function: GlDepthFunction) -> Result<(), String> {
3570        let Some(depth_func) = self.depth_func else {
3571            return Err(
3572                "depth comparison function state is not available for this GL context".to_string(),
3573            );
3574        };
3575        unsafe { depth_func(function.as_raw()) };
3576        Ok(())
3577    }
3578
3579    pub fn depth_mask(&self, enabled: bool) -> Result<(), String> {
3580        let Some(depth_mask) = self.depth_mask else {
3581            return Err("depth write mask state is not available for this GL context".to_string());
3582        };
3583        unsafe { depth_mask(if enabled { 1 } else { GL_FALSE }) };
3584        Ok(())
3585    }
3586
3587    pub fn depth_range(&self, range: GlDepthRange) -> Result<(), String> {
3588        if let Some(depth_range_f) = self.depth_range_f {
3589            unsafe { depth_range_f(range.near as f32, range.far as f32) };
3590            Ok(())
3591        } else {
3592            Err("depth range state is not available for this GL context".to_string())
3593        }
3594    }
3595
3596    pub fn cull_face(&self, mode: GlCullFaceMode) -> Result<(), String> {
3597        let Some(cull_face) = self.cull_face else {
3598            return Err("face culling mode state is not available for this GL context".to_string());
3599        };
3600        unsafe { cull_face(mode.as_raw()) };
3601        Ok(())
3602    }
3603
3604    pub fn front_face(&self, winding: GlFrontFaceWinding) -> Result<(), String> {
3605        let Some(front_face) = self.front_face else {
3606            return Err(
3607                "front-face winding state is not available for this GL context".to_string(),
3608            );
3609        };
3610        unsafe { front_face(winding.as_raw()) };
3611        Ok(())
3612    }
3613
3614    pub fn stencil_func(
3615        &self,
3616        function: GlStencilFunction,
3617        reference: GlStencilReference,
3618        mask: GlStencilMask,
3619    ) -> Result<(), String> {
3620        let Some(stencil_func) = self.stencil_func else {
3621            return Err(
3622                "stencil comparison state is not available for this GL context".to_string(),
3623            );
3624        };
3625        unsafe { stencil_func(function.as_raw(), reference.as_raw(), mask.as_raw()) };
3626        Ok(())
3627    }
3628
3629    pub fn stencil_mask(&self, mask: GlStencilMask) -> Result<(), String> {
3630        let Some(stencil_mask) = self.stencil_mask else {
3631            return Err(
3632                "stencil write mask state is not available for this GL context".to_string(),
3633            );
3634        };
3635        unsafe { stencil_mask(mask.as_raw()) };
3636        Ok(())
3637    }
3638
3639    pub fn stencil_op(
3640        &self,
3641        stencil_fail: GlStencilOperation,
3642        depth_fail: GlStencilOperation,
3643        depth_pass: GlStencilOperation,
3644    ) -> Result<(), String> {
3645        let Some(stencil_op) = self.stencil_op else {
3646            return Err("stencil operation state is not available for this GL context".to_string());
3647        };
3648        unsafe {
3649            stencil_op(
3650                stencil_fail.as_raw(),
3651                depth_fail.as_raw(),
3652                depth_pass.as_raw(),
3653            )
3654        };
3655        Ok(())
3656    }
3657
3658    pub fn stencil_func_separate(
3659        &self,
3660        face: GlStencilFace,
3661        function: GlStencilFunction,
3662        reference: GlStencilReference,
3663        mask: GlStencilMask,
3664    ) -> Result<(), String> {
3665        let Some(stencil_func_separate) = self.stencil_func_separate else {
3666            return Err(
3667                "per-face stencil comparison state is not available for this GL context"
3668                    .to_string(),
3669            );
3670        };
3671        unsafe {
3672            stencil_func_separate(
3673                face.as_raw(),
3674                function.as_raw(),
3675                reference.as_raw(),
3676                mask.as_raw(),
3677            )
3678        };
3679        Ok(())
3680    }
3681
3682    pub fn stencil_mask_separate(
3683        &self,
3684        face: GlStencilFace,
3685        mask: GlStencilMask,
3686    ) -> Result<(), String> {
3687        let Some(stencil_mask_separate) = self.stencil_mask_separate else {
3688            return Err(
3689                "per-face stencil write mask state is not available for this GL context"
3690                    .to_string(),
3691            );
3692        };
3693        unsafe { stencil_mask_separate(face.as_raw(), mask.as_raw()) };
3694        Ok(())
3695    }
3696
3697    pub fn stencil_op_separate(
3698        &self,
3699        face: GlStencilFace,
3700        stencil_fail: GlStencilOperation,
3701        depth_fail: GlStencilOperation,
3702        depth_pass: GlStencilOperation,
3703    ) -> Result<(), String> {
3704        let Some(stencil_op_separate) = self.stencil_op_separate else {
3705            return Err(
3706                "per-face stencil operation state is not available for this GL context".to_string(),
3707            );
3708        };
3709        unsafe {
3710            stencil_op_separate(
3711                face.as_raw(),
3712                stencil_fail.as_raw(),
3713                depth_fail.as_raw(),
3714                depth_pass.as_raw(),
3715            )
3716        };
3717        Ok(())
3718    }
3719
3720    pub fn color_mask(&self, mask: GlColorWriteMask) -> Result<(), String> {
3721        let Some(color_mask) = self.color_mask else {
3722            return Err("color write mask state is not available for this GL context".to_string());
3723        };
3724        let [red, green, blue, alpha] = mask.as_raw();
3725        unsafe { color_mask(red, green, blue, alpha) };
3726        Ok(())
3727    }
3728
3729    pub fn polygon_offset(&self, offset: GlPolygonOffset) -> Result<(), String> {
3730        let Some(polygon_offset) = self.polygon_offset else {
3731            return Err("polygon offset state is not available for this GL context".to_string());
3732        };
3733        unsafe { polygon_offset(offset.factor, offset.units) };
3734        Ok(())
3735    }
3736
3737    pub fn supports_queries(&self) -> bool {
3738        self.gen_queries.is_some()
3739            && self.delete_queries.is_some()
3740            && self.begin_query.is_some()
3741            && self.end_query.is_some()
3742            && self.get_query_object_uiv.is_some()
3743    }
3744
3745    pub fn gen_query(&self) -> Result<GlQuery, String> {
3746        let Some(gen_queries) = self.gen_queries else {
3747            return Err("query objects are not available for this GL context".to_string());
3748        };
3749        let mut id = 0;
3750        unsafe { gen_queries(1, &mut id) };
3751        GlQuery::from_raw(id).ok_or_else(|| "glGenQueries returned 0".to_string())
3752    }
3753
3754    pub fn delete_query(&self, query: GlQuery) -> Result<(), String> {
3755        let Some(delete_queries) = self.delete_queries else {
3756            return Err("query object deletion is not available for this GL context".to_string());
3757        };
3758        let id = query.as_raw();
3759        unsafe { delete_queries(1, &id) };
3760        Ok(())
3761    }
3762
3763    pub fn begin_query(&self, target: GlQueryTarget, query: GlQuery) -> Result<(), String> {
3764        let Some(begin_query) = self.begin_query else {
3765            return Err("query begin is not available for this GL context".to_string());
3766        };
3767        unsafe { begin_query(target.as_raw(), query.as_raw()) };
3768        Ok(())
3769    }
3770
3771    pub fn end_query(&self, target: GlQueryTarget) -> Result<(), String> {
3772        let Some(end_query) = self.end_query else {
3773            return Err("query end is not available for this GL context".to_string());
3774        };
3775        unsafe { end_query(target.as_raw()) };
3776        Ok(())
3777    }
3778
3779    pub fn query_result_available(&self, query: GlQuery) -> Result<bool, String> {
3780        let value = self.query_object_u32(query, GL_QUERY_RESULT_AVAILABLE)?;
3781        Ok(value != 0)
3782    }
3783
3784    pub fn query_result_u32(&self, query: GlQuery) -> Result<u32, String> {
3785        self.query_object_u32(query, GL_QUERY_RESULT)
3786    }
3787
3788    fn query_object_u32(&self, query: GlQuery, property: u32) -> Result<u32, String> {
3789        let Some(get_query_object_uiv) = self.get_query_object_uiv else {
3790            return Err("query object results are not available for this GL context".to_string());
3791        };
3792        let mut value = 0;
3793        unsafe { get_query_object_uiv(query.as_raw(), property, &mut value) };
3794        Ok(value)
3795    }
3796
3797    pub fn supports_sync_objects(&self) -> bool {
3798        self.fence_sync.is_some()
3799            && self.client_wait_sync.is_some()
3800            && self.wait_sync.is_some()
3801            && self.delete_sync.is_some()
3802    }
3803
3804    pub fn fence_sync(&self) -> Result<GlSync, String> {
3805        let Some(fence_sync) = self.fence_sync else {
3806            return Err("sync fences are not available for this GL context".to_string());
3807        };
3808        let sync = unsafe { fence_sync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0) };
3809        GlSync::from_raw(sync).ok_or_else(|| "glFenceSync returned null".to_string())
3810    }
3811
3812    pub fn client_wait_sync(
3813        &self,
3814        sync: GlSync,
3815        flush_commands: bool,
3816        timeout: GlSyncTimeout,
3817    ) -> Result<GlSyncWaitResult, String> {
3818        let Some(client_wait_sync) = self.client_wait_sync else {
3819            return Err("client sync waits are not available for this GL context".to_string());
3820        };
3821        let flags = if flush_commands {
3822            GL_SYNC_FLUSH_COMMANDS_BIT
3823        } else {
3824            0
3825        };
3826        let result = unsafe { client_wait_sync(sync.as_raw(), flags, timeout.as_raw()) };
3827        GlSyncWaitResult::from_raw(result)
3828    }
3829
3830    pub fn wait_sync(&self, sync: GlSync) -> Result<(), String> {
3831        let Some(wait_sync) = self.wait_sync else {
3832            return Err("server sync waits are not available for this GL context".to_string());
3833        };
3834        unsafe { wait_sync(sync.as_raw(), 0, GL_TIMEOUT_IGNORED) };
3835        Ok(())
3836    }
3837
3838    pub fn delete_sync(&self, sync: GlSync) -> Result<(), String> {
3839        let Some(delete_sync) = self.delete_sync else {
3840            return Err("sync deletion is not available for this GL context".to_string());
3841        };
3842        unsafe { delete_sync(sync.as_raw()) };
3843        Ok(())
3844    }
3845
3846    pub fn read_pixels(
3847        &self,
3848        rect: GlRect,
3849        format: GlTextureFormat,
3850        pixels: &mut [u8],
3851    ) -> Result<(), String> {
3852        let Some(read_pixels) = self.read_pixels else {
3853            return Err("pixel readback is not available for this GL context".to_string());
3854        };
3855        let (x, y, width, height) = rect.as_gl_args("glReadPixels")?;
3856        let byte_len = read_pixels_len(rect, format)?;
3857        if pixels.len() < byte_len {
3858            return Err(format!(
3859                "glReadPixels requires {byte_len} destination byte(s), got {}",
3860                pixels.len()
3861            ));
3862        }
3863        unsafe {
3864            read_pixels(
3865                x,
3866                y,
3867                width,
3868                height,
3869                format.as_raw(),
3870                GL_UNSIGNED_BYTE,
3871                pixels.as_mut_ptr().cast::<c_void>(),
3872            )
3873        };
3874        Ok(())
3875    }
3876
3877    pub fn read_buffer(&self, buffer: GlFramebufferBuffer) -> Result<(), String> {
3878        let Some(read_buffer) = self.read_buffer else {
3879            return Err("read buffer selection is not available for this GL context".to_string());
3880        };
3881        unsafe { read_buffer(buffer.as_raw()?) };
3882        Ok(())
3883    }
3884
3885    pub fn draw_buffers(&self, buffers: &[GlFramebufferBuffer]) -> Result<(), String> {
3886        let Some(draw_buffers) = self.draw_buffers else {
3887            return Err(
3888                "multiple draw buffer selection is not available for this GL context".to_string(),
3889            );
3890        };
3891        let (count, raw_buffers) = framebuffer_buffer_values(buffers, "draw buffer count")?;
3892        unsafe { draw_buffers(count, raw_buffers.as_ptr()) };
3893        Ok(())
3894    }
3895
3896    pub fn blit_framebuffer(
3897        &self,
3898        source: GlRect,
3899        destination: GlRect,
3900        buffers: BitFlags<GlFramebufferBlitBuffer>,
3901        filter: GlFramebufferBlitFilter,
3902    ) -> Result<(), String> {
3903        let Some(blit_framebuffer) = self.blit_framebuffer else {
3904            return Err("framebuffer blit is not available for this GL context".to_string());
3905        };
3906        let ([src_x0, src_y0, src_x1, src_y1], [dst_x0, dst_y0, dst_x1, dst_y1], mask, filter) =
3907            framebuffer_blit_args(source, destination, buffers, filter)?;
3908        unsafe {
3909            blit_framebuffer(
3910                src_x0, src_y0, src_x1, src_y1, dst_x0, dst_y0, dst_x1, dst_y1, mask, filter,
3911            )
3912        };
3913        Ok(())
3914    }
3915
3916    fn viewport_raw(&self, x: i32, y: i32, width: i32, height: i32) {
3917        unsafe { (self.viewport)(x, y, width, height) };
3918    }
3919
3920    pub fn viewport(&self, rect: GlRect) -> Result<(), String> {
3921        let (x, y, width, height) = rect.as_gl_args("glViewport")?;
3922        self.viewport_raw(x, y, width, height);
3923        Ok(())
3924    }
3925
3926    fn scissor_raw(&self, x: i32, y: i32, width: i32, height: i32) {
3927        unsafe { (self.scissor)(x, y, width, height) };
3928    }
3929
3930    pub fn scissor(&self, rect: GlRect) -> Result<(), String> {
3931        let (x, y, width, height) = rect.as_gl_args("glScissor")?;
3932        self.scissor_raw(x, y, width, height);
3933        Ok(())
3934    }
3935
3936    fn create_shader_raw(&self, shader_type: u32) -> u32 {
3937        unsafe { (self.create_shader)(shader_type) }
3938    }
3939
3940    pub fn create_shader(&self, stage: GlShaderStage) -> Result<GlShader, String> {
3941        GlShader::from_nonzero(self.create_shader_raw(stage.as_raw()), stage)
3942    }
3943
3944    pub fn compile_shader_source(
3945        &self,
3946        stage: GlShaderStage,
3947        source: &str,
3948    ) -> Result<GlShader, String> {
3949        let shader = self.create_shader(stage)?;
3950        if let Err(error) = self.shader_source(shader, source) {
3951            self.delete_shader(shader);
3952            return Err(error);
3953        }
3954        self.compile_shader(shader);
3955
3956        let status = self.get_shader_iv(shader.as_raw(), GL_COMPILE_STATUS);
3957        if status == 0 {
3958            let log = self.get_shader_info_log(shader.as_raw());
3959            self.delete_shader(shader);
3960            return Err(format!("shader compile failed: {log}"));
3961        }
3962
3963        if let Err(error) = self.check_no_error("glsym::compile_shader_source") {
3964            self.delete_shader(shader);
3965            return Err(error);
3966        }
3967        Ok(shader)
3968    }
3969
3970    fn shader_source_raw_handle(&self, shader: u32, source: &str) -> Result<(), String> {
3971        let source = gl_string(source)?;
3972        self.shader_source_raw(shader, source.as_c_str());
3973        Ok(())
3974    }
3975
3976    pub fn shader_source(&self, shader: GlShader, source: &str) -> Result<(), String> {
3977        self.shader_source_raw_handle(shader.as_raw(), source)
3978    }
3979
3980    fn shader_source_raw(&self, shader: u32, source: &CStr) {
3981        let source_ptr = source.as_ptr();
3982        unsafe { (self.shader_source)(shader, 1, &source_ptr, std::ptr::null()) };
3983    }
3984
3985    fn compile_shader_raw(&self, shader: u32) {
3986        unsafe { (self.compile_shader)(shader) };
3987    }
3988
3989    pub fn compile_shader(&self, shader: GlShader) {
3990        self.compile_shader_raw(shader.as_raw());
3991    }
3992
3993    fn get_shader_iv(&self, shader: u32, pname: u32) -> i32 {
3994        let mut value = 0;
3995        self.get_shader_iv_raw(shader, pname, &mut value);
3996        value
3997    }
3998
3999    fn get_shader_iv_raw(&self, shader: u32, pname: u32, params: &mut i32) {
4000        unsafe { (self.get_shader_iv)(shader, pname, params) };
4001    }
4002
4003    fn get_shader_info_log(&self, shader: u32) -> String {
4004        let length = self.get_shader_iv(shader, GL_INFO_LOG_LENGTH);
4005        if length <= 1 {
4006            return "no log".to_string();
4007        }
4008
4009        let mut buffer = vec![0u8; length as usize];
4010        let mut written = 0;
4011        self.get_shader_info_log_raw(
4012            shader,
4013            length,
4014            &mut written,
4015            buffer.as_mut_ptr().cast::<c_char>(),
4016        );
4017        String::from_utf8_lossy(&buffer[..written as usize]).into_owned()
4018    }
4019
4020    fn get_shader_info_log_raw(
4021        &self,
4022        shader: u32,
4023        length: i32,
4024        written: &mut i32,
4025        buffer: *mut c_char,
4026    ) {
4027        unsafe { (self.get_shader_info_log)(shader, length, written, buffer) };
4028    }
4029
4030    fn delete_shader_raw(&self, shader: u32) {
4031        unsafe { (self.delete_shader)(shader) };
4032    }
4033
4034    pub fn delete_shader(&self, shader: GlShader) {
4035        self.delete_shader_raw(shader.as_raw());
4036    }
4037
4038    fn create_program_raw(&self) -> u32 {
4039        unsafe { (self.create_program)() }
4040    }
4041
4042    pub fn create_program(&self) -> Result<GlProgram, String> {
4043        GlProgram::from_nonzero(self.create_program_raw())
4044    }
4045
4046    pub fn build_program(
4047        &self,
4048        vertex_source: &str,
4049        fragment_source: &str,
4050    ) -> Result<GlProgram, String> {
4051        let vertex_shader = self.compile_shader_source(GlShaderStage::Vertex, vertex_source)?;
4052        let fragment_shader =
4053            match self.compile_shader_source(GlShaderStage::Fragment, fragment_source) {
4054                Ok(shader) => shader,
4055                Err(error) => {
4056                    self.delete_shader(vertex_shader);
4057                    return Err(error);
4058                }
4059            };
4060
4061        let program = match self.create_program() {
4062            Ok(program) => program,
4063            Err(error) => {
4064                self.delete_shader(vertex_shader);
4065                self.delete_shader(fragment_shader);
4066                return Err(error);
4067            }
4068        };
4069        self.attach_shader(program, vertex_shader);
4070        self.attach_shader(program, fragment_shader);
4071        let link_result = self.link_program(program);
4072
4073        self.delete_shader(vertex_shader);
4074        self.delete_shader(fragment_shader);
4075
4076        if let Err(error) = link_result {
4077            self.delete_program(program);
4078            return Err(error);
4079        }
4080        Ok(program)
4081    }
4082
4083    fn attach_shader_raw(&self, program: u32, shader: u32) {
4084        unsafe { (self.attach_shader)(program, shader) };
4085    }
4086
4087    pub fn attach_shader(&self, program: GlProgram, shader: GlShader) {
4088        self.attach_shader_raw(program.as_raw(), shader.as_raw());
4089    }
4090
4091    fn link_program_raw(&self, program: u32) {
4092        unsafe { (self.link_program)(program) };
4093    }
4094
4095    pub fn link_program(&self, program: GlProgram) -> Result<(), String> {
4096        self.link_program_raw(program.as_raw());
4097        let status = self.get_program_iv(program.as_raw(), GL_LINK_STATUS);
4098        if status == 0 {
4099            let log = self.get_program_info_log(program.as_raw());
4100            return Err(format!("program link failed: {log}"));
4101        }
4102        self.check_no_error("glsym::link_program")
4103    }
4104
4105    fn get_program_iv(&self, program: u32, pname: u32) -> i32 {
4106        let mut value = 0;
4107        self.get_program_iv_raw(program, pname, &mut value);
4108        value
4109    }
4110
4111    fn get_program_iv_raw(&self, program: u32, pname: u32, params: &mut i32) {
4112        unsafe { (self.get_program_iv)(program, pname, params) };
4113    }
4114
4115    fn get_program_info_log(&self, program: u32) -> String {
4116        let length = self.get_program_iv(program, GL_INFO_LOG_LENGTH);
4117        if length <= 1 {
4118            return "no log".to_string();
4119        }
4120
4121        let mut buffer = vec![0u8; length as usize];
4122        let mut written = 0;
4123        self.get_program_info_log_raw(
4124            program,
4125            length,
4126            &mut written,
4127            buffer.as_mut_ptr().cast::<c_char>(),
4128        );
4129        String::from_utf8_lossy(&buffer[..written as usize]).into_owned()
4130    }
4131
4132    fn get_program_info_log_raw(
4133        &self,
4134        program: u32,
4135        length: i32,
4136        written: &mut i32,
4137        buffer: *mut c_char,
4138    ) {
4139        unsafe { (self.get_program_info_log)(program, length, written, buffer) };
4140    }
4141
4142    fn delete_program_raw(&self, program: u32) {
4143        unsafe { (self.delete_program)(program) };
4144    }
4145
4146    pub fn delete_program(&self, program: GlProgram) {
4147        self.delete_program_raw(program.as_raw());
4148    }
4149
4150    fn use_program_raw(&self, program: u32) {
4151        unsafe { (self.use_program)(program) };
4152    }
4153
4154    pub fn use_no_program(&self) {
4155        self.use_program_raw(0);
4156    }
4157
4158    pub fn use_program(&self, program: Option<GlProgram>) {
4159        self.use_program_raw(program.map_or(0, GlProgram::as_raw));
4160    }
4161
4162    fn gen_buffer_raw(&self) -> u32 {
4163        let mut id = 0;
4164        unsafe { (self.gen_buffers)(1, &mut id) };
4165        id
4166    }
4167
4168    pub fn gen_buffer(&self) -> Result<GlBuffer, String> {
4169        GlBuffer::from_nonzero(self.gen_buffer_raw())
4170    }
4171
4172    fn bind_buffer_raw(&self, target: GlBufferTarget, buffer: u32) {
4173        unsafe { (self.bind_buffer)(target.as_raw(), buffer) };
4174    }
4175
4176    pub fn unbind_buffer(&self, target: GlBufferTarget) {
4177        self.bind_buffer_raw(target, 0);
4178    }
4179
4180    pub fn bind_buffer(&self, target: GlBufferTarget, buffer: Option<GlBuffer>) {
4181        self.bind_buffer_raw(target, buffer.map_or(0, GlBuffer::as_raw));
4182    }
4183
4184    pub fn supports_indexed_buffer_bindings(&self) -> bool {
4185        self.bind_buffer_base.is_some() && self.bind_buffer_range.is_some()
4186    }
4187
4188    pub fn bind_buffer_base(
4189        &self,
4190        target: GlIndexedBufferTarget,
4191        index: GlBufferBindingIndex,
4192        buffer: Option<GlBuffer>,
4193    ) -> Result<(), String> {
4194        let Some(bind_buffer_base) = self.bind_buffer_base else {
4195            return Err(
4196                "indexed buffer base binding is not available for this GL context".to_string(),
4197            );
4198        };
4199        unsafe {
4200            bind_buffer_base(
4201                target.as_raw(),
4202                index.as_raw(),
4203                buffer.map_or(0, GlBuffer::as_raw),
4204            );
4205        }
4206        Ok(())
4207    }
4208
4209    pub fn bind_buffer_range(
4210        &self,
4211        target: GlIndexedBufferTarget,
4212        index: GlBufferBindingIndex,
4213        buffer: Option<GlBuffer>,
4214        range: GlBufferRange,
4215    ) -> Result<(), String> {
4216        let Some(bind_buffer_range) = self.bind_buffer_range else {
4217            return Err(
4218                "indexed buffer range binding is not available for this GL context".to_string(),
4219            );
4220        };
4221        let (offset, size) = range.as_gl_args("glBindBufferRange")?;
4222        unsafe {
4223            bind_buffer_range(
4224                target.as_raw(),
4225                index.as_raw(),
4226                buffer.map_or(0, GlBuffer::as_raw),
4227                offset,
4228                size,
4229            );
4230        }
4231        Ok(())
4232    }
4233
4234    pub fn buffer_data<T>(
4235        &self,
4236        target: GlBufferTarget,
4237        data: &[T],
4238        usage: GlBufferUsage,
4239    ) -> Result<(), String> {
4240        let byte_len = GlBufferByteSize::from_bytes(std::mem::size_of_val(data))
4241            .as_isize("GL buffer upload")?;
4242        unsafe {
4243            self.buffer_data_raw(
4244                target.as_raw(),
4245                byte_len,
4246                data.as_ptr().cast::<c_void>(),
4247                usage.as_raw(),
4248            );
4249        }
4250        Ok(())
4251    }
4252
4253    pub fn buffer_data_empty(
4254        &self,
4255        target: GlBufferTarget,
4256        byte_len: GlBufferByteSize,
4257        usage: GlBufferUsage,
4258    ) -> Result<(), String> {
4259        let byte_len = byte_len.as_isize("GL buffer allocation")?;
4260        unsafe {
4261            self.buffer_data_raw(target.as_raw(), byte_len, std::ptr::null(), usage.as_raw());
4262        }
4263        Ok(())
4264    }
4265
4266    pub fn buffer_sub_data<T>(
4267        &self,
4268        target: GlBufferTarget,
4269        offset: GlBufferByteOffset,
4270        data: &[T],
4271    ) -> Result<(), String> {
4272        let byte_len = std::mem::size_of_val(data);
4273        self.buffer_sub_data_bytes_raw(target, offset, unsafe {
4274            std::slice::from_raw_parts(data.as_ptr().cast::<u8>(), byte_len)
4275        })
4276    }
4277
4278    fn buffer_sub_data_bytes_raw(
4279        &self,
4280        target: GlBufferTarget,
4281        offset: GlBufferByteOffset,
4282        data: &[u8],
4283    ) -> Result<(), String> {
4284        let offset = offset.as_isize()?;
4285        let byte_len = isize::try_from(data.len()).map_err(|_| {
4286            format!(
4287                "GL buffer update byte length {} exceeds isize::MAX",
4288                data.len()
4289            )
4290        })?;
4291        unsafe {
4292            (self.buffer_sub_data)(
4293                target.as_raw(),
4294                offset,
4295                byte_len,
4296                data.as_ptr().cast::<c_void>(),
4297            );
4298        }
4299        Ok(())
4300    }
4301
4302    pub fn copy_buffer_sub_data(
4303        &self,
4304        read_target: GlBufferTarget,
4305        write_target: GlBufferTarget,
4306        read_offset: GlBufferByteOffset,
4307        write_offset: GlBufferByteOffset,
4308        size: GlBufferByteSize,
4309    ) -> Result<(), String> {
4310        let Some(copy_buffer_sub_data) = self.copy_buffer_sub_data else {
4311            return Err("buffer copy is not available for this GL context".to_string());
4312        };
4313        let read_offset = read_offset.as_isize()?;
4314        let write_offset = write_offset.as_isize()?;
4315        let size = size.as_isize("glCopyBufferSubData")?;
4316        unsafe {
4317            copy_buffer_sub_data(
4318                read_target.as_raw(),
4319                write_target.as_raw(),
4320                read_offset,
4321                write_offset,
4322                size,
4323            );
4324        }
4325        Ok(())
4326    }
4327
4328    unsafe fn buffer_data_raw(&self, target: u32, size: isize, data: *const c_void, usage: u32) {
4329        (self.buffer_data)(target, size, data, usage);
4330    }
4331
4332    fn delete_buffer_raw(&self, id: u32) {
4333        unsafe { (self.delete_buffers)(1, &id) };
4334    }
4335
4336    pub fn delete_buffer(&self, buffer: GlBuffer) {
4337        self.delete_buffer_raw(buffer.as_raw());
4338    }
4339
4340    fn gen_texture_raw(&self) -> u32 {
4341        let mut id = 0;
4342        unsafe { (self.gen_textures)(1, &mut id) };
4343        id
4344    }
4345
4346    pub fn gen_texture(&self) -> Result<GlTexture, String> {
4347        GlTexture::from_nonzero(self.gen_texture_raw())
4348    }
4349
4350    fn bind_texture_raw(&self, target: GlTextureTarget, texture: u32) {
4351        unsafe { (self.bind_texture)(target.as_raw(), texture) };
4352    }
4353
4354    pub fn unbind_texture(&self, target: GlTextureTarget) {
4355        self.bind_texture_raw(target, 0);
4356    }
4357
4358    pub fn bind_texture(&self, target: GlTextureTarget, texture: Option<GlTexture>) {
4359        self.bind_texture_raw(target, texture.map_or(0, GlTexture::as_raw));
4360    }
4361
4362    pub fn active_texture(&self, unit: GlTextureUnit) -> Result<(), String> {
4363        unsafe { (self.active_texture)(unit.as_raw()?) };
4364        Ok(())
4365    }
4366
4367    pub fn tex_min_filter(&self, target: GlTextureTarget, filter: GlTextureMinFilter) {
4368        unsafe {
4369            (self.tex_parameter_i)(
4370                target.as_raw(),
4371                GL_TEXTURE_MIN_FILTER,
4372                filter.as_raw() as i32,
4373            )
4374        };
4375    }
4376
4377    pub fn tex_mag_filter(&self, target: GlTextureTarget, filter: GlTextureMagFilter) {
4378        unsafe {
4379            (self.tex_parameter_i)(
4380                target.as_raw(),
4381                GL_TEXTURE_MAG_FILTER,
4382                filter.as_raw() as i32,
4383            )
4384        };
4385    }
4386
4387    pub fn tex_wrap_s(&self, target: GlTextureTarget, wrap: GlTextureWrap) {
4388        unsafe { (self.tex_parameter_i)(target.as_raw(), GL_TEXTURE_WRAP_S, wrap.as_raw() as i32) };
4389    }
4390
4391    pub fn tex_wrap_t(&self, target: GlTextureTarget, wrap: GlTextureWrap) {
4392        unsafe { (self.tex_parameter_i)(target.as_raw(), GL_TEXTURE_WRAP_T, wrap.as_raw() as i32) };
4393    }
4394
4395    pub fn tex_wrap_r(&self, target: GlTextureTarget, wrap: GlTextureWrap) {
4396        unsafe { (self.tex_parameter_i)(target.as_raw(), GL_TEXTURE_WRAP_R, wrap.as_raw() as i32) };
4397    }
4398
4399    pub fn pixel_store_unpack_alignment(&self, alignment: GlPixelStoreAlignment) {
4400        unsafe { (self.pixel_store_i)(GL_UNPACK_ALIGNMENT, alignment.as_raw()) };
4401    }
4402
4403    pub fn pixel_store_pack_alignment(&self, alignment: GlPixelStoreAlignment) {
4404        unsafe { (self.pixel_store_i)(GL_PACK_ALIGNMENT, alignment.as_raw()) };
4405    }
4406
4407    fn tex_image_2d_raw(
4408        &self,
4409        target: GlTextureTarget,
4410        internal_format: GlTextureInternalFormat,
4411        level: GlTextureLevel,
4412        size: GlTextureSize2D,
4413        format: GlTextureFormat,
4414        data_type: GlTextureDataType,
4415        bytes: Option<&[u8]>,
4416    ) -> Result<(), String> {
4417        let level = level.as_i32("glTexImage2D")?;
4418        let (width, height) = size.as_gl_args("glTexImage2D")?;
4419        let pixels = bytes
4420            .map(|bytes| bytes.as_ptr().cast::<c_void>())
4421            .unwrap_or(std::ptr::null());
4422        unsafe {
4423            (self.tex_image_2d)(
4424                target.as_raw(),
4425                level,
4426                internal_format.as_raw() as i32,
4427                width,
4428                height,
4429                0,
4430                format.as_raw(),
4431                data_type.as_raw(),
4432                pixels,
4433            );
4434        }
4435        Ok(())
4436    }
4437
4438    pub fn tex_image_2d(
4439        &self,
4440        target: GlTextureTarget,
4441        internal_format: GlTextureInternalFormat,
4442        level: GlTextureLevel,
4443        size: GlTextureSize2D,
4444        format: GlTextureFormat,
4445        data_type: GlTextureDataType,
4446        bytes: Option<&[u8]>,
4447    ) -> Result<(), String> {
4448        self.tex_image_2d_raw(
4449            target,
4450            internal_format,
4451            level,
4452            size,
4453            format,
4454            data_type,
4455            bytes,
4456        )
4457    }
4458
4459    pub fn tex_sub_image_2d(
4460        &self,
4461        target: GlTextureTarget,
4462        level: GlTextureLevel,
4463        offset: GlTextureOffset2D,
4464        size: GlTextureSize2D,
4465        format: GlTextureFormat,
4466        data_type: GlTextureDataType,
4467        bytes: &[u8],
4468    ) -> Result<(), String> {
4469        let level = level.as_i32("glTexSubImage2D")?;
4470        let (width, height) = size.as_gl_args("glTexSubImage2D")?;
4471        unsafe {
4472            (self.tex_sub_image_2d)(
4473                target.as_raw(),
4474                level,
4475                offset.x,
4476                offset.y,
4477                width,
4478                height,
4479                format.as_raw(),
4480                data_type.as_raw(),
4481                bytes.as_ptr().cast::<c_void>(),
4482            );
4483        }
4484        Ok(())
4485    }
4486
4487    pub fn supports_texture_arrays(&self) -> bool {
4488        self.tex_image_3d.is_some()
4489            && self.tex_sub_image_3d.is_some()
4490            && supports_core_texture_arrays(self.context_type, self.version_info)
4491    }
4492
4493    pub fn tex_image_3d(
4494        &self,
4495        target: GlTextureTarget,
4496        internal_format: GlTextureInternalFormat,
4497        level: GlTextureLevel,
4498        size: GlTextureSize3D,
4499        format: GlTextureFormat,
4500        data_type: GlTextureDataType,
4501        bytes: Option<&[u8]>,
4502    ) -> Result<(), String> {
4503        let Some(tex_image_3d) = self.tex_image_3d else {
4504            return Err("texture arrays are not available for this GL context".to_string());
4505        };
4506        let level = level.as_i32("glTexImage3D")?;
4507        let (width, height, depth) = size.as_gl_args("glTexImage3D")?;
4508        let pixels = bytes
4509            .map(|bytes| bytes.as_ptr().cast::<c_void>())
4510            .unwrap_or(std::ptr::null());
4511        unsafe {
4512            tex_image_3d(
4513                target.as_raw(),
4514                level,
4515                internal_format.as_raw() as i32,
4516                width,
4517                height,
4518                depth,
4519                0,
4520                format.as_raw(),
4521                data_type.as_raw(),
4522                pixels,
4523            );
4524        }
4525        Ok(())
4526    }
4527
4528    pub fn tex_sub_image_3d(
4529        &self,
4530        target: GlTextureTarget,
4531        level: GlTextureLevel,
4532        offset: GlTextureOffset3D,
4533        size: GlTextureSize3D,
4534        format: GlTextureFormat,
4535        data_type: GlTextureDataType,
4536        bytes: &[u8],
4537    ) -> Result<(), String> {
4538        let Some(tex_sub_image_3d) = self.tex_sub_image_3d else {
4539            return Err("texture arrays are not available for this GL context".to_string());
4540        };
4541        let level = level.as_i32("glTexSubImage3D")?;
4542        let (width, height, depth) = size.as_gl_args("glTexSubImage3D")?;
4543        unsafe {
4544            tex_sub_image_3d(
4545                target.as_raw(),
4546                level,
4547                offset.x,
4548                offset.y,
4549                offset.z,
4550                width,
4551                height,
4552                depth,
4553                format.as_raw(),
4554                data_type.as_raw(),
4555                bytes.as_ptr().cast::<c_void>(),
4556            );
4557        }
4558        Ok(())
4559    }
4560
4561    pub fn supports_generate_mipmap(&self) -> bool {
4562        self.generate_mipmap.is_some()
4563    }
4564
4565    pub fn generate_mipmap(&self, target: GlTextureTarget) -> Result<(), String> {
4566        let Some(generate_mipmap) = self.generate_mipmap else {
4567            return Err("glGenerateMipmap is not available for this GL context".to_string());
4568        };
4569        unsafe { generate_mipmap(target.as_raw()) };
4570        Ok(())
4571    }
4572
4573    fn delete_texture_raw(&self, id: u32) {
4574        unsafe { (self.delete_textures)(1, &id) };
4575    }
4576
4577    pub fn delete_texture(&self, texture: GlTexture) {
4578        self.delete_texture_raw(texture.as_raw());
4579    }
4580
4581    pub fn supports_vertex_arrays(&self) -> bool {
4582        self.gen_vertex_arrays.is_some()
4583            && self.bind_vertex_array.is_some()
4584            && self.delete_vertex_arrays.is_some()
4585    }
4586
4587    pub fn supports_instancing(&self) -> bool {
4588        self.vertex_attrib_divisor.is_some()
4589            && self.draw_arrays_instanced.is_some()
4590            && self.draw_elements_instanced.is_some()
4591            && supports_core_instancing(self.context_type, self.version_info)
4592    }
4593
4594    fn gen_vertex_array_raw(&self) -> Result<u32, String> {
4595        let Some(gen_vertex_arrays) = self.gen_vertex_arrays else {
4596            return Err("vertex arrays are not available for this GL context".to_string());
4597        };
4598
4599        let mut id = 0;
4600        unsafe { gen_vertex_arrays(1, &mut id) };
4601        if id == 0 {
4602            return Err("glGenVertexArrays returned 0".to_string());
4603        }
4604        Ok(id)
4605    }
4606
4607    pub fn gen_vertex_array(&self) -> Result<GlVertexArray, String> {
4608        GlVertexArray::from_nonzero(self.gen_vertex_array_raw()?)
4609    }
4610
4611    fn bind_vertex_array_raw(&self, array: u32) -> Result<(), String> {
4612        let Some(bind_vertex_array) = self.bind_vertex_array else {
4613            return Err("vertex arrays are not available for this GL context".to_string());
4614        };
4615
4616        unsafe { bind_vertex_array(array) };
4617        Ok(())
4618    }
4619
4620    pub fn bind_vertex_array(&self, array: Option<GlVertexArray>) -> Result<(), String> {
4621        self.bind_vertex_array_raw(array.map_or(0, GlVertexArray::as_raw))
4622    }
4623
4624    pub fn unbind_vertex_array(&self) -> Result<(), String> {
4625        self.bind_vertex_array(None)
4626    }
4627
4628    fn delete_vertex_array_raw(&self, array: u32) -> Result<(), String> {
4629        let Some(delete_vertex_arrays) = self.delete_vertex_arrays else {
4630            return Err("vertex arrays are not available for this GL context".to_string());
4631        };
4632
4633        unsafe { delete_vertex_arrays(1, &array) };
4634        Ok(())
4635    }
4636
4637    pub fn delete_vertex_array(&self, array: GlVertexArray) -> Result<(), String> {
4638        self.delete_vertex_array_raw(array.as_raw())
4639    }
4640
4641    fn enable_vertex_attrib_array_raw(&self, index: u32) {
4642        unsafe { (self.enable_vertex_attrib_array)(index) };
4643    }
4644
4645    pub fn enable_vertex_attrib(&self, location: GlVertexAttribLocation) {
4646        self.enable_vertex_attrib_array_raw(location.as_raw());
4647    }
4648
4649    fn disable_vertex_attrib_array_raw(&self, index: u32) {
4650        unsafe { (self.disable_vertex_attrib_array)(index) };
4651    }
4652
4653    pub fn disable_vertex_attrib(&self, location: GlVertexAttribLocation) {
4654        self.disable_vertex_attrib_array_raw(location.as_raw());
4655    }
4656
4657    fn vertex_attrib_pointer_f32_raw(
4658        &self,
4659        index: u32,
4660        size: i32,
4661        normalized: bool,
4662        stride: i32,
4663        offset: usize,
4664    ) {
4665        unsafe {
4666            (self.vertex_attrib_pointer)(
4667                index,
4668                size,
4669                GL_FLOAT,
4670                if normalized { 1 } else { GL_FALSE },
4671                stride,
4672                offset as *const c_void,
4673            )
4674        };
4675    }
4676
4677    fn vertex_attrib_pointer_f32_at(
4678        &self,
4679        location: GlVertexAttribLocation,
4680        size: i32,
4681        normalized: bool,
4682        stride: i32,
4683        offset: usize,
4684    ) {
4685        self.vertex_attrib_pointer_f32_raw(location.as_raw(), size, normalized, stride, offset);
4686    }
4687
4688    pub fn vertex_attrib_pointer_f32(
4689        &self,
4690        location: GlVertexAttribLocation,
4691        layout: GlVertexAttribF32Layout,
4692    ) {
4693        self.vertex_attrib_pointer_f32_at(
4694            location,
4695            layout.components.as_gl_size(),
4696            layout.normalized,
4697            layout.stride.as_i32(),
4698            layout.offset.as_bytes(),
4699        );
4700    }
4701
4702    fn vertex_attrib_divisor_raw(&self, index: u32, divisor: u32) -> Result<(), String> {
4703        let Some(vertex_attrib_divisor) = self.vertex_attrib_divisor else {
4704            return Err("instanced attributes are not available for this GL context".to_string());
4705        };
4706
4707        unsafe { vertex_attrib_divisor(index, divisor) };
4708        Ok(())
4709    }
4710
4711    pub fn vertex_attrib_divisor(
4712        &self,
4713        location: GlVertexAttribLocation,
4714        divisor: GlVertexAttribDivisor,
4715    ) -> Result<(), String> {
4716        self.vertex_attrib_divisor_raw(location.as_raw(), divisor.as_raw())
4717    }
4718
4719    fn get_uniform_location(&self, program: u32, name: &str) -> Result<i32, String> {
4720        let name = gl_string(name)?;
4721        Ok(self.get_uniform_location_raw(program, name.as_c_str()))
4722    }
4723
4724    fn uniform_location(
4725        &self,
4726        program: u32,
4727        name: &str,
4728    ) -> Result<Option<GlUniformLocation>, String> {
4729        self.get_uniform_location(program, name)
4730            .map(GlUniformLocation::from_raw)
4731    }
4732
4733    fn required_uniform(&self, program: u32, name: &str) -> Result<GlUniformLocation, String> {
4734        self.uniform_location(program, name)?
4735            .ok_or_else(|| format!("shader linked without required active uniform {name}"))
4736    }
4737
4738    pub fn required_uniform_location(
4739        &self,
4740        program: GlProgram,
4741        name: &str,
4742    ) -> Result<GlUniformLocation, String> {
4743        self.required_uniform(program.as_raw(), name)
4744    }
4745
4746    fn get_uniform_location_raw(&self, program: u32, name: &CStr) -> i32 {
4747        unsafe { (self.get_uniform_location)(program, name.as_ptr()) }
4748    }
4749
4750    fn get_attrib_location(&self, program: u32, name: &str) -> Result<i32, String> {
4751        let name = gl_string(name)?;
4752        Ok(self.get_attrib_location_raw(program, name.as_c_str()))
4753    }
4754
4755    fn attrib_location(
4756        &self,
4757        program: u32,
4758        name: &str,
4759    ) -> Result<Option<GlVertexAttribLocation>, String> {
4760        self.get_attrib_location(program, name)
4761            .map(GlVertexAttribLocation::from_raw)
4762    }
4763
4764    fn required_attrib(&self, program: u32, name: &str) -> Result<GlVertexAttribLocation, String> {
4765        self.attrib_location(program, name)?
4766            .ok_or_else(|| format!("shader linked without required active attribute {name}"))
4767    }
4768
4769    pub fn required_attrib_location(
4770        &self,
4771        program: GlProgram,
4772        name: &str,
4773    ) -> Result<GlVertexAttribLocation, String> {
4774        self.required_attrib(program.as_raw(), name)
4775    }
4776
4777    pub fn bind_attrib_location(
4778        &self,
4779        program: GlProgram,
4780        location: GlVertexAttribLocation,
4781        name: &str,
4782    ) -> Result<(), String> {
4783        let Some(bind_attrib_location) = self.bind_attrib_location else {
4784            return Err(
4785                "attribute location binding is not available for this GL context".to_string(),
4786            );
4787        };
4788        let name = gl_string(name)?;
4789        unsafe { bind_attrib_location(program.as_raw(), location.as_raw(), name.as_ptr()) };
4790        Ok(())
4791    }
4792
4793    fn get_attrib_location_raw(&self, program: u32, name: &CStr) -> i32 {
4794        unsafe { (self.get_attrib_location)(program, name.as_ptr()) }
4795    }
4796
4797    fn uniform_1f_raw(&self, location: i32, value: f32) {
4798        unsafe { (self.uniform_1f)(location, value) };
4799    }
4800
4801    pub fn uniform_1f(&self, location: GlUniformLocation, value: f32) {
4802        self.uniform_1f_raw(location.as_raw(), value);
4803    }
4804
4805    fn uniform_1i_raw(&self, location: i32, value: i32) {
4806        unsafe { (self.uniform_1i)(location, value) };
4807    }
4808
4809    pub fn uniform_1i(&self, location: GlUniformLocation, value: i32) {
4810        self.uniform_1i_raw(location.as_raw(), value);
4811    }
4812
4813    fn uniform_2f_raw(&self, location: i32, x: f32, y: f32) {
4814        unsafe { (self.uniform_2f)(location, x, y) };
4815    }
4816
4817    pub fn uniform_2f(&self, location: GlUniformLocation, values: [f32; 2]) {
4818        self.uniform_2f_raw(location.as_raw(), values[0], values[1]);
4819    }
4820
4821    fn uniform_3f_raw(&self, location: i32, values: [f32; 3]) {
4822        unsafe { (self.uniform_3f)(location, values[0], values[1], values[2]) };
4823    }
4824
4825    pub fn uniform_3f(&self, location: GlUniformLocation, values: [f32; 3]) {
4826        self.uniform_3f_raw(location.as_raw(), values);
4827    }
4828
4829    fn uniform_4f_raw(&self, location: i32, values: [f32; 4]) {
4830        unsafe { (self.uniform_4f)(location, values[0], values[1], values[2], values[3]) };
4831    }
4832
4833    pub fn uniform_4f(&self, location: GlUniformLocation, values: [f32; 4]) {
4834        self.uniform_4f_raw(location.as_raw(), values);
4835    }
4836
4837    fn uniform_4fv_raw(&self, location: i32, values: &[f32; 4]) {
4838        unsafe { (self.uniform_4fv)(location, 1, values.as_ptr()) };
4839    }
4840
4841    pub fn uniform_4fv(&self, location: GlUniformLocation, values: &[f32; 4]) {
4842        self.uniform_4fv_raw(location.as_raw(), values);
4843    }
4844
4845    fn uniform_matrix_3fv_raw(&self, location: i32, transpose: bool, values: &[f32; 9]) {
4846        unsafe {
4847            (self.uniform_matrix_3fv)(
4848                location,
4849                1,
4850                if transpose { 1 } else { GL_FALSE },
4851                values.as_ptr(),
4852            )
4853        };
4854    }
4855
4856    pub fn uniform_matrix_3fv(
4857        &self,
4858        location: GlUniformLocation,
4859        transpose: bool,
4860        values: &[f32; 9],
4861    ) {
4862        self.uniform_matrix_3fv_raw(location.as_raw(), transpose, values);
4863    }
4864
4865    fn uniform_matrix_4fv_raw(&self, location: i32, transpose: bool, values: &[f32; 16]) {
4866        unsafe {
4867            (self.uniform_matrix_4fv)(
4868                location,
4869                1,
4870                if transpose { 1 } else { GL_FALSE },
4871                values.as_ptr(),
4872            )
4873        };
4874    }
4875
4876    pub fn uniform_matrix_4fv(
4877        &self,
4878        location: GlUniformLocation,
4879        transpose: bool,
4880        values: &[f32; 16],
4881    ) {
4882        self.uniform_matrix_4fv_raw(location.as_raw(), transpose, values);
4883    }
4884
4885    fn draw_arrays_raw(&self, mode: GlDrawMode, first: i32, count: i32) {
4886        unsafe { (self.draw_arrays)(mode.as_raw(), first, count) };
4887    }
4888
4889    pub fn draw_arrays(&self, mode: GlDrawMode, range: GlDrawRange) -> Result<(), String> {
4890        let (first, count) = range.as_gl_args("glDrawArrays")?;
4891        self.draw_arrays_raw(mode, first, count);
4892        Ok(())
4893    }
4894
4895    fn draw_arrays_instanced_raw(
4896        &self,
4897        mode: GlDrawMode,
4898        first: i32,
4899        count: i32,
4900        instance_count: i32,
4901    ) -> Result<(), String> {
4902        let Some(draw_arrays_instanced) = self.draw_arrays_instanced else {
4903            return Err("instanced array draws are not available for this GL context".to_string());
4904        };
4905        unsafe {
4906            draw_arrays_instanced(mode.as_raw(), first, count, instance_count);
4907        }
4908        Ok(())
4909    }
4910
4911    pub fn draw_arrays_instanced(
4912        &self,
4913        mode: GlDrawMode,
4914        range: GlDrawRange,
4915        instance_count: GlInstanceCount,
4916    ) -> Result<(), String> {
4917        let (first, count) = range.as_gl_args("glDrawArraysInstanced")?;
4918        let instance_count = instance_count.as_i32("glDrawArraysInstanced")?;
4919        self.draw_arrays_instanced_raw(mode, first, count, instance_count)
4920    }
4921
4922    fn draw_elements_raw(
4923        &self,
4924        mode: GlDrawMode,
4925        count: i32,
4926        index_type: GlIndexType,
4927        offset: usize,
4928    ) {
4929        unsafe {
4930            (self.draw_elements)(
4931                mode.as_raw(),
4932                count,
4933                index_type.as_raw(),
4934                offset as *const c_void,
4935            )
4936        };
4937    }
4938
4939    pub fn draw_elements(
4940        &self,
4941        mode: GlDrawMode,
4942        index_type: GlIndexType,
4943        range: GlElementRange,
4944    ) -> Result<(), String> {
4945        let (count, offset) = range.as_gl_args("glDrawElements")?;
4946        self.draw_elements_raw(mode, count, index_type, offset);
4947        Ok(())
4948    }
4949
4950    pub fn supports_draw_range_elements(&self) -> bool {
4951        self.draw_range_elements.is_some()
4952    }
4953
4954    fn draw_range_elements_raw(
4955        &self,
4956        mode: GlDrawMode,
4957        vertex_range: GlElementVertexRange,
4958        count: i32,
4959        index_type: GlIndexType,
4960        offset: usize,
4961    ) -> Result<(), String> {
4962        let Some(draw_range_elements) = self.draw_range_elements else {
4963            return Err("bounded indexed draws are not available for this GL context".to_string());
4964        };
4965        let (start, end) = vertex_range.as_gl_args();
4966        unsafe {
4967            draw_range_elements(
4968                mode.as_raw(),
4969                start,
4970                end,
4971                count,
4972                index_type.as_raw(),
4973                offset as *const c_void,
4974            );
4975        }
4976        Ok(())
4977    }
4978
4979    pub fn draw_range_elements(
4980        &self,
4981        mode: GlDrawMode,
4982        vertex_range: GlElementVertexRange,
4983        index_type: GlIndexType,
4984        range: GlElementRange,
4985    ) -> Result<(), String> {
4986        let (count, offset) = range.as_gl_args("glDrawRangeElements")?;
4987        self.draw_range_elements_raw(mode, vertex_range, count, index_type, offset)
4988    }
4989
4990    fn draw_elements_instanced_raw(
4991        &self,
4992        mode: GlDrawMode,
4993        count: i32,
4994        index_type: GlIndexType,
4995        offset: usize,
4996        instance_count: i32,
4997    ) -> Result<(), String> {
4998        let Some(draw_elements_instanced) = self.draw_elements_instanced else {
4999            return Err("instanced draws are not available for this GL context".to_string());
5000        };
5001        unsafe {
5002            draw_elements_instanced(
5003                mode.as_raw(),
5004                count,
5005                index_type.as_raw(),
5006                offset as *const c_void,
5007                instance_count,
5008            );
5009        }
5010        Ok(())
5011    }
5012
5013    pub fn draw_elements_instanced(
5014        &self,
5015        mode: GlDrawMode,
5016        index_type: GlIndexType,
5017        range: GlElementRange,
5018        instance_count: GlInstanceCount,
5019    ) -> Result<(), String> {
5020        let (count, offset) = range.as_gl_args("glDrawElementsInstanced")?;
5021        let instance_count = instance_count.as_i32("glDrawElementsInstanced")?;
5022        self.draw_elements_instanced_raw(mode, count, index_type, offset, instance_count)
5023    }
5024
5025    pub fn blend_func(&self, source: GlBlendFactor, destination: GlBlendFactor) {
5026        unsafe { (self.blend_func)(source.as_raw(), destination.as_raw()) };
5027    }
5028
5029    pub fn blend_color(&self, r: f32, g: f32, b: f32, a: f32) -> Result<(), String> {
5030        let Some(blend_color) = self.blend_color else {
5031            return Err("constant blend color is not available for this GL context".to_string());
5032        };
5033        unsafe { blend_color(r, g, b, a) };
5034        Ok(())
5035    }
5036
5037    pub fn blend_func_separate(
5038        &self,
5039        source_rgb: GlBlendFactor,
5040        destination_rgb: GlBlendFactor,
5041        source_alpha: GlBlendFactor,
5042        destination_alpha: GlBlendFactor,
5043    ) -> Result<(), String> {
5044        let Some(blend_func_separate) = self.blend_func_separate else {
5045            return Err("separate blend factors are not available for this GL context".to_string());
5046        };
5047        unsafe {
5048            blend_func_separate(
5049                source_rgb.as_raw(),
5050                destination_rgb.as_raw(),
5051                source_alpha.as_raw(),
5052                destination_alpha.as_raw(),
5053            )
5054        };
5055        Ok(())
5056    }
5057
5058    pub fn blend_equation(&self, equation: GlBlendEquation) {
5059        unsafe { (self.blend_equation)(equation.as_raw()) };
5060    }
5061
5062    pub fn blend_equation_separate(
5063        &self,
5064        rgb: GlBlendEquation,
5065        alpha: GlBlendEquation,
5066    ) -> Result<(), String> {
5067        let Some(blend_equation_separate) = self.blend_equation_separate else {
5068            return Err(
5069                "separate blend equations are not available for this GL context".to_string(),
5070            );
5071        };
5072        unsafe { blend_equation_separate(rgb.as_raw(), alpha.as_raw()) };
5073        Ok(())
5074    }
5075
5076    pub fn supports_framebuffer_objects(&self) -> bool {
5077        self.gen_framebuffers.is_some()
5078            && self.delete_framebuffers.is_some()
5079            && self.framebuffer_texture_2d.is_some()
5080    }
5081
5082    pub fn supports_renderbuffers(&self) -> bool {
5083        self.gen_renderbuffers.is_some()
5084            && self.bind_renderbuffer.is_some()
5085            && self.renderbuffer_storage.is_some()
5086            && self.delete_renderbuffers.is_some()
5087            && self.framebuffer_renderbuffer.is_some()
5088    }
5089
5090    pub fn gen_framebuffer(&self) -> Result<GlFramebuffer, String> {
5091        let Some(gen_framebuffers) = self.gen_framebuffers else {
5092            return Err(
5093                "framebuffer object creation is not available for this GL context".to_string(),
5094            );
5095        };
5096        let mut id = 0;
5097        unsafe { gen_framebuffers(1, &mut id) };
5098        GlFramebuffer::from_nonzero(id)
5099    }
5100
5101    pub fn delete_framebuffer(&self, framebuffer: GlFramebuffer) -> Result<(), String> {
5102        let Some(delete_framebuffers) = self.delete_framebuffers else {
5103            return Err(
5104                "framebuffer object deletion is not available for this GL context".to_string(),
5105            );
5106        };
5107        let id = framebuffer.as_raw();
5108        unsafe { delete_framebuffers(1, &id) };
5109        Ok(())
5110    }
5111
5112    fn bind_framebuffer_raw(&self, target: GlFramebufferTarget, framebuffer: u32) {
5113        unsafe { (self.bind_framebuffer)(target.as_raw(), framebuffer) };
5114    }
5115
5116    pub fn unbind_framebuffer(&self, target: GlFramebufferTarget) {
5117        self.bind_framebuffer_raw(target, 0);
5118    }
5119
5120    fn bind_framebuffer_checked_raw(
5121        &self,
5122        target: GlFramebufferTarget,
5123        framebuffer: u32,
5124    ) -> Result<(), String> {
5125        self.bind_framebuffer_raw(target, framebuffer);
5126        self.check_no_error("glBindFramebuffer")?;
5127        if framebuffer != 0 {
5128            self.check_bound_framebuffer_complete(target)?;
5129        }
5130        Ok(())
5131    }
5132
5133    pub fn bind_framebuffer(
5134        &self,
5135        target: GlFramebufferTarget,
5136        framebuffer: Option<GlFramebuffer>,
5137    ) -> Result<(), String> {
5138        self.bind_framebuffer_checked_raw(target, framebuffer.map_or(0, GlFramebuffer::as_raw))
5139    }
5140
5141    pub fn framebuffer_texture_2d(
5142        &self,
5143        target: GlFramebufferTarget,
5144        attachment: GlFramebufferAttachment,
5145        texture_target: GlFramebufferTexture2DTarget,
5146        texture: Option<GlTexture>,
5147        level: GlTextureLevel,
5148    ) -> Result<(), String> {
5149        let Some(framebuffer_texture_2d) = self.framebuffer_texture_2d else {
5150            return Err(
5151                "2D framebuffer texture attachments are not available for this GL context"
5152                    .to_string(),
5153            );
5154        };
5155        unsafe {
5156            framebuffer_texture_2d(
5157                target.as_raw(),
5158                attachment.as_raw()?,
5159                texture_target.as_raw(),
5160                texture.map_or(0, GlTexture::as_raw),
5161                level.as_i32("glFramebufferTexture2D")?,
5162            );
5163        }
5164        Ok(())
5165    }
5166
5167    pub fn gen_renderbuffer(&self) -> Result<GlRenderbuffer, String> {
5168        let Some(gen_renderbuffers) = self.gen_renderbuffers else {
5169            return Err("renderbuffer creation is not available for this GL context".to_string());
5170        };
5171        let mut id = 0;
5172        unsafe { gen_renderbuffers(1, &mut id) };
5173        GlRenderbuffer::from_nonzero(id)
5174    }
5175
5176    pub fn bind_renderbuffer(
5177        &self,
5178        target: GlRenderbufferTarget,
5179        renderbuffer: Option<GlRenderbuffer>,
5180    ) -> Result<(), String> {
5181        let Some(bind_renderbuffer) = self.bind_renderbuffer else {
5182            return Err("renderbuffer binding is not available for this GL context".to_string());
5183        };
5184        unsafe {
5185            bind_renderbuffer(
5186                target.as_raw(),
5187                renderbuffer.map_or(0, GlRenderbuffer::as_raw),
5188            )
5189        };
5190        Ok(())
5191    }
5192
5193    pub fn delete_renderbuffer(&self, renderbuffer: GlRenderbuffer) -> Result<(), String> {
5194        let Some(delete_renderbuffers) = self.delete_renderbuffers else {
5195            return Err("renderbuffer deletion is not available for this GL context".to_string());
5196        };
5197        let id = renderbuffer.as_raw();
5198        unsafe { delete_renderbuffers(1, &id) };
5199        Ok(())
5200    }
5201
5202    pub fn renderbuffer_storage(
5203        &self,
5204        target: GlRenderbufferTarget,
5205        internal_format: GlRenderbufferInternalFormat,
5206        size: GlRenderbufferSize,
5207    ) -> Result<(), String> {
5208        let Some(renderbuffer_storage) = self.renderbuffer_storage else {
5209            return Err("renderbuffer storage is not available for this GL context".to_string());
5210        };
5211        let (width, height) = size.as_gl_args()?;
5212        unsafe { renderbuffer_storage(target.as_raw(), internal_format.as_raw(), width, height) };
5213        Ok(())
5214    }
5215
5216    pub fn framebuffer_renderbuffer(
5217        &self,
5218        target: GlFramebufferTarget,
5219        attachment: GlFramebufferAttachment,
5220        renderbuffer_target: GlRenderbufferTarget,
5221        renderbuffer: Option<GlRenderbuffer>,
5222    ) -> Result<(), String> {
5223        let Some(framebuffer_renderbuffer) = self.framebuffer_renderbuffer else {
5224            return Err(
5225                "framebuffer renderbuffer attachments are not available for this GL context"
5226                    .to_string(),
5227            );
5228        };
5229        unsafe {
5230            framebuffer_renderbuffer(
5231                target.as_raw(),
5232                attachment.as_raw()?,
5233                renderbuffer_target.as_raw(),
5234                renderbuffer.map_or(0, GlRenderbuffer::as_raw),
5235            );
5236        }
5237        Ok(())
5238    }
5239
5240    pub fn check_no_error(&self, operation: &str) -> Result<(), String> {
5241        collect_gl_errors(self.get_error, operation)
5242    }
5243
5244    pub fn check_bound_framebuffer_complete(
5245        &self,
5246        target: GlFramebufferTarget,
5247    ) -> Result<(), String> {
5248        check_bound_framebuffer_complete(
5249            self.check_framebuffer_status,
5250            target,
5251            "glCheckFramebufferStatus",
5252        )
5253    }
5254
5255    pub fn discard_depth_framebuffer_attachment(&self) -> bool {
5256        let attachments = [GL_DEPTH_ATTACHMENT];
5257        if let Some(invalidate_framebuffer) = self.invalidate_framebuffer {
5258            unsafe {
5259                invalidate_framebuffer(
5260                    GL_FRAMEBUFFER,
5261                    attachments.len() as i32,
5262                    attachments.as_ptr(),
5263                );
5264            }
5265            true
5266        } else {
5267            false
5268        }
5269    }
5270
5271    #[doc(hidden)]
5272    pub fn fake_for_testing(config: FakeGlConfig) -> Self {
5273        let mut state = fake_gl_state()
5274            .lock()
5275            .expect("fake GL state mutex poisoned");
5276        state.configure(config);
5277        Self {
5278            context_type: state.config.context_type,
5279            version_info: state.config.version_info,
5280            vendor_string: state.config.vendor_string.clone(),
5281            renderer_string: state.config.renderer_string.clone(),
5282            version_string: state.config.version_string.clone(),
5283            extensions_string: state.config.extensions_string.clone(),
5284            max_texture_size: state.config.max_texture_size,
5285            max_texture_image_units: state.config.max_texture_image_units,
5286            max_varying_vectors: state.config.max_varying_vectors,
5287            fragment_highp_float: state.config.fragment_highp_float,
5288            clear_color: fake_gl_clear_color,
5289            clear: fake_gl_clear,
5290            enable: fake_gl_enable,
5291            disable: fake_gl_disable,
5292            depth_func: Some(fake_gl_depth_func),
5293            depth_mask: Some(fake_gl_depth_mask),
5294            depth_range_f: Some(fake_gl_depth_range_f),
5295            cull_face: Some(fake_gl_cull_face),
5296            front_face: Some(fake_gl_front_face),
5297            stencil_func: Some(fake_gl_stencil_func),
5298            stencil_mask: Some(fake_gl_stencil_mask),
5299            stencil_op: Some(fake_gl_stencil_op),
5300            stencil_func_separate: Some(fake_gl_stencil_func_separate),
5301            stencil_mask_separate: Some(fake_gl_stencil_mask_separate),
5302            stencil_op_separate: Some(fake_gl_stencil_op_separate),
5303            color_mask: Some(fake_gl_color_mask),
5304            polygon_offset: Some(fake_gl_polygon_offset),
5305            gen_queries: Some(fake_gl_gen_queries),
5306            delete_queries: Some(fake_gl_delete_queries),
5307            begin_query: Some(fake_gl_begin_query),
5308            end_query: Some(fake_gl_end_query),
5309            get_query_object_uiv: Some(fake_gl_get_query_object_uiv),
5310            fence_sync: Some(fake_gl_fence_sync),
5311            client_wait_sync: Some(fake_gl_client_wait_sync),
5312            wait_sync: Some(fake_gl_wait_sync),
5313            delete_sync: Some(fake_gl_delete_sync),
5314            read_pixels: Some(fake_gl_read_pixels),
5315            read_buffer: Some(fake_gl_read_buffer),
5316            draw_buffers: Some(fake_gl_draw_buffers),
5317            viewport: fake_gl_viewport,
5318            scissor: fake_gl_scissor,
5319            create_shader: fake_gl_create_shader,
5320            shader_source: fake_gl_shader_source,
5321            compile_shader: fake_gl_compile_shader,
5322            get_shader_iv: fake_gl_get_shader_iv,
5323            get_shader_info_log: fake_gl_get_shader_info_log,
5324            delete_shader: fake_gl_delete_shader,
5325            create_program: fake_gl_create_program,
5326            attach_shader: fake_gl_attach_shader,
5327            link_program: fake_gl_link_program,
5328            get_program_iv: fake_gl_get_program_iv,
5329            get_program_info_log: fake_gl_get_program_info_log,
5330            delete_program: fake_gl_delete_program,
5331            use_program: fake_gl_use_program,
5332            gen_buffers: fake_gl_gen_buffers,
5333            bind_buffer: fake_gl_bind_buffer,
5334            bind_buffer_base: Some(fake_gl_bind_buffer_base),
5335            bind_buffer_range: Some(fake_gl_bind_buffer_range),
5336            buffer_data: fake_gl_buffer_data,
5337            buffer_sub_data: fake_gl_buffer_sub_data,
5338            copy_buffer_sub_data: Some(fake_gl_copy_buffer_sub_data),
5339            delete_buffers: fake_gl_delete_buffers,
5340            gen_textures: fake_gl_gen_textures,
5341            bind_texture: fake_gl_bind_texture,
5342            active_texture: fake_gl_active_texture,
5343            tex_parameter_i: fake_gl_tex_parameter_i,
5344            pixel_store_i: fake_gl_pixel_store_i,
5345            tex_image_2d: fake_gl_tex_image_2d,
5346            tex_sub_image_2d: fake_gl_tex_sub_image_2d,
5347            tex_image_3d: state
5348                .config
5349                .supports_texture_arrays
5350                .then_some(fake_gl_tex_image_3d),
5351            tex_sub_image_3d: state
5352                .config
5353                .supports_texture_arrays
5354                .then_some(fake_gl_tex_sub_image_3d),
5355            generate_mipmap: state
5356                .config
5357                .supports_generate_mipmap
5358                .then_some(fake_gl_generate_mipmap),
5359            delete_textures: fake_gl_delete_textures,
5360            gen_vertex_arrays: state
5361                .config
5362                .supports_vertex_arrays
5363                .then_some(fake_gl_gen_vertex_arrays),
5364            bind_vertex_array: state
5365                .config
5366                .supports_vertex_arrays
5367                .then_some(fake_gl_bind_vertex_array),
5368            delete_vertex_arrays: state
5369                .config
5370                .supports_vertex_arrays
5371                .then_some(fake_gl_delete_vertex_arrays),
5372            enable_vertex_attrib_array: fake_gl_enable_vertex_attrib_array,
5373            disable_vertex_attrib_array: fake_gl_disable_vertex_attrib_array,
5374            vertex_attrib_pointer: fake_gl_vertex_attrib_pointer,
5375            vertex_attrib_divisor: state
5376                .config
5377                .supports_instancing
5378                .then_some(fake_gl_vertex_attrib_divisor),
5379            get_uniform_location: fake_gl_get_uniform_location,
5380            get_attrib_location: fake_gl_get_attrib_location,
5381            bind_attrib_location: Some(fake_gl_bind_attrib_location),
5382            uniform_1i: fake_gl_uniform_1i,
5383            uniform_1f: fake_gl_uniform_1f,
5384            uniform_2f: fake_gl_uniform_2f,
5385            uniform_3f: fake_gl_uniform_3f,
5386            uniform_4f: fake_gl_uniform_4f,
5387            uniform_4fv: fake_gl_uniform_4fv,
5388            uniform_matrix_3fv: fake_gl_uniform_matrix_3fv,
5389            uniform_matrix_4fv: fake_gl_uniform_matrix_4fv,
5390            draw_arrays: fake_gl_draw_arrays,
5391            draw_arrays_instanced: state
5392                .config
5393                .supports_instancing
5394                .then_some(fake_gl_draw_arrays_instanced),
5395            draw_elements: fake_gl_draw_elements,
5396            draw_range_elements: Some(fake_gl_draw_range_elements),
5397            draw_elements_instanced: state
5398                .config
5399                .supports_instancing
5400                .then_some(fake_gl_draw_elements_instanced),
5401            blend_color: Some(fake_gl_blend_color),
5402            blend_func: fake_gl_blend_func,
5403            blend_func_separate: Some(fake_gl_blend_func_separate),
5404            blend_equation: fake_gl_blend_equation,
5405            blend_equation_separate: Some(fake_gl_blend_equation_separate),
5406            gen_framebuffers: Some(fake_gl_gen_framebuffers),
5407            bind_framebuffer: fake_gl_bind_framebuffer,
5408            delete_framebuffers: Some(fake_gl_delete_framebuffers),
5409            framebuffer_texture_2d: Some(fake_gl_framebuffer_texture_2d),
5410            gen_renderbuffers: Some(fake_gl_gen_renderbuffers),
5411            bind_renderbuffer: Some(fake_gl_bind_renderbuffer),
5412            renderbuffer_storage: Some(fake_gl_renderbuffer_storage),
5413            delete_renderbuffers: Some(fake_gl_delete_renderbuffers),
5414            framebuffer_renderbuffer: Some(fake_gl_framebuffer_renderbuffer),
5415            blit_framebuffer: Some(fake_gl_blit_framebuffer),
5416            get_error: Some(fake_gl_get_error),
5417            check_framebuffer_status: Some(fake_gl_check_framebuffer_status),
5418            invalidate_framebuffer: state
5419                .config
5420                .version_info
5421                .version_at_least(3, 0)
5422                .then_some(fake_gl_invalidate_framebuffer),
5423        }
5424    }
5425
5426    #[doc(hidden)]
5427    pub fn reset_fake_state_for_testing() {
5428        reset_fake_gl_for_testing();
5429    }
5430
5431    #[doc(hidden)]
5432    pub fn snapshot_fake_state_for_testing() -> FakeGlSnapshot {
5433        snapshot_fake_gl_for_testing()
5434    }
5435}
5436
5437fn load_gl_symbol<T>(runtime: &Runtime<'_>, name: &str) -> Result<T, String>
5438where
5439    T: Copy,
5440{
5441    let raw = runtime.hw_proc_address(name)?;
5442    Ok(unsafe { mem::transmute_copy(&raw) })
5443}
5444
5445fn load_optional_gl_symbol<T>(runtime: &Runtime<'_>, name: &str) -> Result<Option<T>, String>
5446where
5447    T: Copy,
5448{
5449    Ok(runtime
5450        .hw_proc_address(name)
5451        .ok()
5452        .map(|raw| unsafe { mem::transmute_copy(&raw) }))
5453}
5454
5455fn load_optional_gl_symbol_aliases<T>(
5456    runtime: &Runtime<'_>,
5457    names: &[&str],
5458) -> Result<Option<T>, String>
5459where
5460    T: Copy,
5461{
5462    for name in names {
5463        if let Some(symbol) = load_optional_gl_symbol(runtime, name)? {
5464            return Ok(Some(symbol));
5465        }
5466    }
5467    Ok(None)
5468}
5469
5470fn gl_string(value: &str) -> Result<CString, String> {
5471    CString::new(value).map_err(|_| format!("GL strings cannot contain interior NULs: {value:?}"))
5472}
5473
5474fn read_pixels_len(rect: GlRect, format: GlTextureFormat) -> Result<usize, String> {
5475    let channels = match format {
5476        GlTextureFormat::Red | GlTextureFormat::Luminance => 1usize,
5477        GlTextureFormat::Rgb => 3,
5478        GlTextureFormat::Rgba => 4,
5479    };
5480    let pixels = usize::try_from(rect.width)
5481        .ok()
5482        .and_then(|width| {
5483            usize::try_from(rect.height)
5484                .ok()
5485                .and_then(|height| width.checked_mul(height))
5486        })
5487        .ok_or_else(|| "glReadPixels dimensions exceed usize::MAX".to_string())?;
5488    pixels
5489        .checked_mul(channels)
5490        .ok_or_else(|| "glReadPixels byte length exceeds usize::MAX".to_string())
5491}
5492
5493fn framebuffer_buffer_values(
5494    buffers: &[GlFramebufferBuffer],
5495    count_label: &str,
5496) -> Result<(i32, Vec<u32>), String> {
5497    let count = i32::try_from(buffers.len())
5498        .map_err(|_| format!("{count_label} {} exceeds GLsizei::MAX", buffers.len()))?;
5499    let raw_buffers = buffers
5500        .iter()
5501        .copied()
5502        .map(GlFramebufferBuffer::as_raw)
5503        .collect::<Result<Vec<_>, _>>()?;
5504    Ok((count, raw_buffers))
5505}
5506
5507fn framebuffer_blit_rect_endpoints(rect: GlRect, label: &str) -> Result<[i32; 4], String> {
5508    let width = glsizei_from_u32(rect.width, &format!("{label} width"))?;
5509    let height = glsizei_from_u32(rect.height, &format!("{label} height"))?;
5510    let x1 = rect
5511        .x
5512        .checked_add(width)
5513        .ok_or_else(|| format!("{label} x endpoint overflows GLint"))?;
5514    let y1 = rect
5515        .y
5516        .checked_add(height)
5517        .ok_or_else(|| format!("{label} y endpoint overflows GLint"))?;
5518    Ok([rect.x, rect.y, x1, y1])
5519}
5520
5521fn framebuffer_blit_args(
5522    source: GlRect,
5523    destination: GlRect,
5524    buffers: BitFlags<GlFramebufferBlitBuffer>,
5525    filter: GlFramebufferBlitFilter,
5526) -> Result<([i32; 4], [i32; 4], u32, u32), String> {
5527    if buffers.bits() == 0 {
5528        return Err("framebuffer blit requires at least one buffer".to_string());
5529    }
5530    let depth_or_stencil = buffers.bits() & (GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT) != 0;
5531    if filter == GlFramebufferBlitFilter::Linear && depth_or_stencil {
5532        return Err("framebuffer depth or stencil blits require nearest filtering".to_string());
5533    }
5534    let source = framebuffer_blit_rect_endpoints(source, "source framebuffer blit rectangle")?;
5535    let destination =
5536        framebuffer_blit_rect_endpoints(destination, "destination framebuffer blit rectangle")?;
5537    Ok((source, destination, buffers.bits(), filter.as_raw()))
5538}
5539
5540#[derive(Clone, Debug, PartialEq, Eq)]
5541pub struct FakeGlConfig {
5542    pub context_type: HwContextType,
5543    pub version_info: GlVersionInfo,
5544    pub vendor_string: String,
5545    pub renderer_string: String,
5546    pub version_string: String,
5547    pub extensions_string: String,
5548    pub max_texture_size: Option<u32>,
5549    pub max_texture_image_units: Option<u32>,
5550    pub max_varying_vectors: Option<u32>,
5551    pub fragment_highp_float: Option<bool>,
5552    pub supports_vertex_arrays: bool,
5553    pub supports_texture_arrays: bool,
5554    pub supports_instancing: bool,
5555    pub supports_generate_mipmap: bool,
5556    pub next_error: Option<u32>,
5557    pub framebuffer_status: u32,
5558}
5559
5560impl Default for FakeGlConfig {
5561    fn default() -> Self {
5562        Self {
5563            context_type: HwContextType::OpenGlEs2,
5564            version_info: GlVersionInfo {
5565                is_gles: true,
5566                major: Some(2),
5567                minor: Some(0),
5568            },
5569            vendor_string: "FakeVendor".to_string(),
5570            renderer_string: "FakeRenderer".to_string(),
5571            version_string: "OpenGL ES 2.0 Fake".to_string(),
5572            extensions_string: String::new(),
5573            max_texture_size: Some(1024),
5574            max_texture_image_units: Some(8),
5575            max_varying_vectors: Some(8),
5576            fragment_highp_float: Some(false),
5577            supports_vertex_arrays: false,
5578            supports_texture_arrays: false,
5579            supports_instancing: false,
5580            supports_generate_mipmap: true,
5581            next_error: None,
5582            framebuffer_status: GL_FRAMEBUFFER_COMPLETE,
5583        }
5584    }
5585}
5586
5587#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5588pub struct FakeTextureParameterCall {
5589    pub target: u32,
5590    pub parameter: u32,
5591    pub value: i32,
5592}
5593
5594#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5595pub struct FakeTextureUpload2D {
5596    pub target: u32,
5597    pub internal_format: u32,
5598    pub width: i32,
5599    pub height: i32,
5600    pub format: u32,
5601    pub type_: u32,
5602}
5603
5604#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5605pub struct FakeTextureSubImage2D {
5606    pub target: u32,
5607    pub level: i32,
5608    pub x: i32,
5609    pub y: i32,
5610    pub width: i32,
5611    pub height: i32,
5612    pub format: u32,
5613    pub type_: u32,
5614    pub has_pixels: bool,
5615}
5616
5617#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5618pub struct FakeBufferDataCall {
5619    pub target: u32,
5620    pub byte_len: usize,
5621    pub usage: u32,
5622    pub has_data: bool,
5623}
5624
5625#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5626pub struct FakeBufferSubDataCall {
5627    pub target: u32,
5628    pub offset: usize,
5629    pub byte_len: usize,
5630}
5631
5632#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5633pub struct FakeCopyBufferSubDataCall {
5634    pub read_target: u32,
5635    pub write_target: u32,
5636    pub read_offset: usize,
5637    pub write_offset: usize,
5638    pub byte_len: usize,
5639}
5640
5641#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5642pub struct FakeBindBufferBaseCall {
5643    pub target: u32,
5644    pub index: u32,
5645    pub buffer: u32,
5646}
5647
5648#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5649pub struct FakeBindBufferRangeCall {
5650    pub target: u32,
5651    pub index: u32,
5652    pub buffer: u32,
5653    pub offset: usize,
5654    pub size: usize,
5655}
5656
5657#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5658pub struct FakeFramebufferTexture2DCall {
5659    pub target: u32,
5660    pub attachment: u32,
5661    pub texture_target: u32,
5662    pub texture: u32,
5663    pub level: i32,
5664}
5665
5666#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5667pub struct FakeRenderbufferStorageCall {
5668    pub target: u32,
5669    pub internal_format: u32,
5670    pub width: i32,
5671    pub height: i32,
5672}
5673
5674#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5675pub struct FakeFramebufferRenderbufferCall {
5676    pub target: u32,
5677    pub attachment: u32,
5678    pub renderbuffer_target: u32,
5679    pub renderbuffer: u32,
5680}
5681
5682#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5683pub struct FakeBlitFramebufferCall {
5684    pub source: [i32; 4],
5685    pub destination: [i32; 4],
5686    pub mask: u32,
5687    pub filter: u32,
5688}
5689
5690#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5691pub struct FakeUniformFloatCall {
5692    pub location: i32,
5693    pub values: [u32; 4],
5694}
5695
5696#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5697pub struct FakeUniformIntCall {
5698    pub location: i32,
5699    pub value: i32,
5700}
5701
5702#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5703pub struct FakeUniformMatrix3Call {
5704    pub location: i32,
5705    pub transpose: bool,
5706    pub values: [u32; 9],
5707}
5708
5709#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5710pub struct FakeStencilFuncCall {
5711    pub function: u32,
5712    pub reference: i32,
5713    pub mask: u32,
5714}
5715
5716#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5717pub struct FakeStencilOpCall {
5718    pub stencil_fail: u32,
5719    pub depth_fail: u32,
5720    pub depth_pass: u32,
5721}
5722
5723#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5724pub struct FakeStencilFuncSeparateCall {
5725    pub face: u32,
5726    pub function: u32,
5727    pub reference: i32,
5728    pub mask: u32,
5729}
5730
5731#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5732pub struct FakeStencilMaskSeparateCall {
5733    pub face: u32,
5734    pub mask: u32,
5735}
5736
5737#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5738pub struct FakeStencilOpSeparateCall {
5739    pub face: u32,
5740    pub stencil_fail: u32,
5741    pub depth_fail: u32,
5742    pub depth_pass: u32,
5743}
5744
5745#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5746pub struct FakeQueryObjectCall {
5747    pub query: u32,
5748    pub property: u32,
5749}
5750
5751#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5752pub struct FakeClientWaitSyncCall {
5753    pub sync: usize,
5754    pub flags: u32,
5755    pub timeout_nanos: u64,
5756}
5757
5758#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5759pub struct FakeWaitSyncCall {
5760    pub sync: usize,
5761    pub flags: u32,
5762    pub timeout_nanos: u64,
5763}
5764
5765#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5766pub struct FakeCreateShaderCall {
5767    pub stage: u32,
5768    pub shader: u32,
5769}
5770
5771#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5772pub struct FakeAttachShaderCall {
5773    pub program: u32,
5774    pub shader: u32,
5775}
5776
5777#[derive(Clone, Debug, PartialEq, Eq)]
5778pub struct FakeBindAttribLocationCall {
5779    pub program: u32,
5780    pub location: u32,
5781    pub name: String,
5782}
5783
5784#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5785pub struct FakeReadPixelsCall {
5786    pub x: i32,
5787    pub y: i32,
5788    pub width: i32,
5789    pub height: i32,
5790    pub format: u32,
5791    pub type_: u32,
5792}
5793
5794#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5795pub struct FakeDrawArraysCall {
5796    pub mode: u32,
5797    pub first: i32,
5798    pub count: i32,
5799    pub instance_count: Option<i32>,
5800}
5801
5802#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5803pub struct FakeDrawElementsCall {
5804    pub mode: u32,
5805    pub vertex_range: Option<(u32, u32)>,
5806    pub count: i32,
5807    pub type_: u32,
5808    pub offset: usize,
5809    pub instance_count: Option<i32>,
5810}
5811
5812#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5813pub struct FakeVertexAttribPointerCall {
5814    pub index: u32,
5815    pub size: i32,
5816    pub type_: u32,
5817    pub normalized: bool,
5818    pub stride: i32,
5819    pub offset: usize,
5820}
5821
5822#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5823pub struct FakeBlendFuncSeparateCall {
5824    pub source_rgb: u32,
5825    pub destination_rgb: u32,
5826    pub source_alpha: u32,
5827    pub destination_alpha: u32,
5828}
5829
5830#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5831pub struct FakeBlendEquationSeparateCall {
5832    pub rgb: u32,
5833    pub alpha: u32,
5834}
5835
5836#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5837pub struct FakeBlendColorCall {
5838    pub color: [u32; 4],
5839}
5840
5841#[derive(Clone, Debug, Default, PartialEq, Eq)]
5842pub struct FakeGlSnapshot {
5843    pub clear_calls: usize,
5844    pub current_clear_color: [u32; 4],
5845    pub clear_colors: Vec<[u32; 4]>,
5846    pub clear_scissor_enabled: Vec<bool>,
5847    pub draw_arrays_calls: usize,
5848    pub draw_arrays_call_args: Vec<FakeDrawArraysCall>,
5849    pub draw_elements_calls: usize,
5850    pub draw_elements_instanced_calls: usize,
5851    pub draw_elements_call_args: Vec<FakeDrawElementsCall>,
5852    pub blend_color_calls: Vec<FakeBlendColorCall>,
5853    pub blend_func_separate_calls: Vec<FakeBlendFuncSeparateCall>,
5854    pub blend_equation_separate_calls: Vec<FakeBlendEquationSeparateCall>,
5855    pub buffer_data_calls: usize,
5856    pub buffer_data_bytes: usize,
5857    pub buffer_data_uploads: Vec<FakeBufferDataCall>,
5858    pub buffer_sub_data_calls: Vec<FakeBufferSubDataCall>,
5859    pub copy_buffer_sub_data_calls: Vec<FakeCopyBufferSubDataCall>,
5860    pub bind_buffer_base_calls: Vec<FakeBindBufferBaseCall>,
5861    pub bind_buffer_range_calls: Vec<FakeBindBufferRangeCall>,
5862    pub created_shaders: Vec<FakeCreateShaderCall>,
5863    pub attached_shaders: Vec<FakeAttachShaderCall>,
5864    pub bind_attrib_location_calls: Vec<FakeBindAttribLocationCall>,
5865    pub linked_programs: Vec<u32>,
5866    pub deleted_buffers: Vec<u32>,
5867    pub deleted_shaders: Vec<u32>,
5868    pub deleted_programs: Vec<u32>,
5869    pub texture_parameter_calls: Vec<FakeTextureParameterCall>,
5870    pub texture_uploads_2d: Vec<FakeTextureUpload2D>,
5871    pub texture_sub_images_2d: Vec<FakeTextureSubImage2D>,
5872    pub texture_uploads_3d_calls: usize,
5873    pub generate_mipmap_calls: Vec<u32>,
5874    pub deleted_textures: Vec<u32>,
5875    pub current_program: u32,
5876    pub use_program_calls: Vec<u32>,
5877    pub uniform_1i_calls: Vec<FakeUniformIntCall>,
5878    pub uniform_3f_calls: Vec<FakeUniformFloatCall>,
5879    pub uniform_4f_calls: Vec<FakeUniformFloatCall>,
5880    pub uniform_4fv_calls: Vec<FakeUniformFloatCall>,
5881    pub uniform_matrix_3fv_calls: Vec<FakeUniformMatrix3Call>,
5882    pub bound_array_buffer: u32,
5883    pub bound_element_array_buffer: u32,
5884    pub bound_vertex_array: u32,
5885    pub vertex_array_bindings: Vec<u32>,
5886    pub deleted_vertex_arrays: Vec<u32>,
5887    pub bound_framebuffer: u32,
5888    pub generated_framebuffers: Vec<u32>,
5889    pub deleted_framebuffers: Vec<u32>,
5890    pub framebuffer_bindings: Vec<u32>,
5891    pub framebuffer_texture_2d_calls: Vec<FakeFramebufferTexture2DCall>,
5892    pub framebuffer_renderbuffer_calls: Vec<FakeFramebufferRenderbufferCall>,
5893    pub blit_framebuffer_calls: Vec<FakeBlitFramebufferCall>,
5894    pub framebuffer_invalidations: Vec<Vec<u32>>,
5895    pub bound_renderbuffer: u32,
5896    pub generated_renderbuffers: Vec<u32>,
5897    pub deleted_renderbuffers: Vec<u32>,
5898    pub renderbuffer_bindings: Vec<u32>,
5899    pub renderbuffer_storage_calls: Vec<FakeRenderbufferStorageCall>,
5900    pub viewport_calls: Vec<(i32, i32, i32, i32)>,
5901    pub pack_alignment: i32,
5902    pub pack_alignment_calls: Vec<i32>,
5903    pub unpack_alignment: i32,
5904    pub unpack_alignment_calls: Vec<i32>,
5905    pub active_texture: u32,
5906    pub bound_texture_2d: u32,
5907    pub bound_texture_2d_units: Vec<(u32, u32)>,
5908    pub bound_texture_2d_array: u32,
5909    pub bound_texture_2d_array_units: Vec<(u32, u32)>,
5910    pub blend_enabled: bool,
5911    pub scissor_enabled: bool,
5912    pub enabled_capabilities: Vec<u32>,
5913    pub depth_function: Option<u32>,
5914    pub depth_mask: bool,
5915    pub depth_range_f_calls: Vec<[u32; 2]>,
5916    pub cull_face_mode: Option<u32>,
5917    pub front_face_winding: Option<u32>,
5918    pub stencil_func: Option<FakeStencilFuncCall>,
5919    pub stencil_mask: u32,
5920    pub stencil_op: Option<FakeStencilOpCall>,
5921    pub stencil_func_separate_calls: Vec<FakeStencilFuncSeparateCall>,
5922    pub stencil_mask_separate_calls: Vec<FakeStencilMaskSeparateCall>,
5923    pub stencil_op_separate_calls: Vec<FakeStencilOpSeparateCall>,
5924    pub color_write_mask: [bool; 4],
5925    pub polygon_offset: Option<[u32; 2]>,
5926    pub generated_queries: Vec<u32>,
5927    pub deleted_queries: Vec<u32>,
5928    pub begin_query_calls: Vec<(u32, u32)>,
5929    pub end_query_calls: Vec<u32>,
5930    pub query_object_uiv_calls: Vec<FakeQueryObjectCall>,
5931    pub generated_syncs: Vec<usize>,
5932    pub client_wait_sync_calls: Vec<FakeClientWaitSyncCall>,
5933    pub wait_sync_calls: Vec<FakeWaitSyncCall>,
5934    pub deleted_syncs: Vec<usize>,
5935    pub read_pixels_calls: Vec<FakeReadPixelsCall>,
5936    pub read_buffer_calls: Vec<u32>,
5937    pub draw_buffers_calls: Vec<Vec<u32>>,
5938    pub scissor_calls: Vec<(i32, i32, i32, i32)>,
5939    pub enabled_vertex_attribs: Vec<u32>,
5940    pub vertex_attrib_pointer_calls: Vec<FakeVertexAttribPointerCall>,
5941}
5942
5943struct FakeGlState {
5944    config: FakeGlConfig,
5945    vendor_string: CString,
5946    renderer_string: CString,
5947    version_string: CString,
5948    extensions_string: CString,
5949    indexed_extensions: Vec<CString>,
5950    next_id: u32,
5951    snapshot: FakeGlSnapshot,
5952}
5953
5954impl Default for FakeGlState {
5955    fn default() -> Self {
5956        let config = FakeGlConfig::default();
5957        let vendor_string =
5958            CString::new(config.vendor_string.clone()).expect("fake GL vendor string");
5959        let renderer_string =
5960            CString::new(config.renderer_string.clone()).expect("fake GL renderer string");
5961        let version_string =
5962            CString::new(config.version_string.clone()).expect("fake GL version string");
5963        let extensions_string =
5964            CString::new(config.extensions_string.clone()).expect("fake GL extensions string");
5965        let indexed_extensions = fake_indexed_extensions(&config.extensions_string);
5966        Self {
5967            config,
5968            vendor_string,
5969            renderer_string,
5970            version_string,
5971            extensions_string,
5972            indexed_extensions,
5973            next_id: 1,
5974            snapshot: FakeGlState::default_snapshot(),
5975        }
5976    }
5977}
5978
5979impl FakeGlState {
5980    fn default_snapshot() -> FakeGlSnapshot {
5981        FakeGlSnapshot {
5982            pack_alignment: 4,
5983            unpack_alignment: 4,
5984            depth_mask: true,
5985            stencil_mask: u32::MAX,
5986            color_write_mask: [true; 4],
5987            ..FakeGlSnapshot::default()
5988        }
5989    }
5990
5991    fn reset(&mut self) {
5992        *self = Self::default();
5993    }
5994
5995    fn configure(&mut self, config: FakeGlConfig) {
5996        self.vendor_string =
5997            CString::new(config.vendor_string.clone()).expect("fake GL vendor string");
5998        self.renderer_string =
5999            CString::new(config.renderer_string.clone()).expect("fake GL renderer string");
6000        self.version_string =
6001            CString::new(config.version_string.clone()).expect("fake GL version string");
6002        self.extensions_string =
6003            CString::new(config.extensions_string.clone()).expect("fake GL extensions string");
6004        self.indexed_extensions = fake_indexed_extensions(&config.extensions_string);
6005        self.config = config;
6006        self.next_id = 1;
6007        self.snapshot = Self::default_snapshot();
6008    }
6009
6010    fn next_id(&mut self) -> u32 {
6011        let id = self.next_id;
6012        self.next_id = self.next_id.saturating_add(1);
6013        id
6014    }
6015
6016    fn set_texture_binding(bindings: &mut Vec<(u32, u32)>, unit: u32, texture: u32) {
6017        if let Some((_, binding)) = bindings
6018            .iter_mut()
6019            .find(|(bound_unit, _)| *bound_unit == unit)
6020        {
6021            *binding = texture;
6022        } else {
6023            bindings.push((unit, texture));
6024        }
6025        bindings.sort_unstable_by_key(|(bound_unit, _)| *bound_unit);
6026    }
6027
6028    fn set_texture_2d_binding(&mut self, unit: u32, texture: u32) {
6029        if unit == 0 {
6030            self.snapshot.bound_texture_2d = texture;
6031        }
6032        Self::set_texture_binding(&mut self.snapshot.bound_texture_2d_units, unit, texture);
6033    }
6034
6035    fn set_texture_2d_array_binding(&mut self, unit: u32, texture: u32) {
6036        if unit == 0 {
6037            self.snapshot.bound_texture_2d_array = texture;
6038        }
6039        Self::set_texture_binding(
6040            &mut self.snapshot.bound_texture_2d_array_units,
6041            unit,
6042            texture,
6043        );
6044    }
6045}
6046
6047fn fake_indexed_extensions(extensions: &str) -> Vec<CString> {
6048    extensions
6049        .split_whitespace()
6050        .map(|extension| CString::new(extension).expect("fake GL extension string"))
6051        .collect()
6052}
6053
6054fn fake_gl_state() -> &'static Mutex<FakeGlState> {
6055    static FAKE_GL_STATE: OnceLock<Mutex<FakeGlState>> = OnceLock::new();
6056    FAKE_GL_STATE.get_or_init(|| Mutex::new(FakeGlState::default()))
6057}
6058
6059pub fn configure_fake_gl_for_testing(config: FakeGlConfig) {
6060    fake_gl_state()
6061        .lock()
6062        .expect("fake GL state mutex poisoned")
6063        .configure(config);
6064}
6065
6066pub fn reset_fake_gl_for_testing() {
6067    fake_gl_state()
6068        .lock()
6069        .expect("fake GL state mutex poisoned")
6070        .reset();
6071}
6072
6073pub fn snapshot_fake_gl_for_testing() -> FakeGlSnapshot {
6074    fake_gl_state()
6075        .lock()
6076        .expect("fake GL state mutex poisoned")
6077        .snapshot
6078        .clone()
6079}
6080
6081/// Returns fake GL procedure addresses for tests.
6082///
6083/// # Safety
6084///
6085/// `symbol` must be either null or point to a valid NUL-terminated C string for
6086/// the duration of the call.
6087pub unsafe extern "C" fn fake_get_proc_address_for_testing(
6088    symbol: *const c_char,
6089) -> Option<unsafe extern "C" fn()> {
6090    if symbol.is_null() {
6091        return None;
6092    }
6093
6094    let symbol = unsafe { CStr::from_ptr(symbol) }.to_str().ok()?;
6095    match symbol {
6096        "glGetString" => Some(mem::transmute::<GlGetString, unsafe extern "C" fn()>(
6097            fake_gl_get_string,
6098        )),
6099        "glGetStringi" => Some(mem::transmute::<GlGetStringi, unsafe extern "C" fn()>(
6100            fake_gl_get_string_i,
6101        )),
6102        "glGetIntegerv" => Some(mem::transmute::<GlGetIntegerv, unsafe extern "C" fn()>(
6103            fake_gl_get_integer_v,
6104        )),
6105        "glGetShaderPrecisionFormat" => Some(mem::transmute::<
6106            GlGetShaderPrecisionFormat,
6107            unsafe extern "C" fn(),
6108        >(fake_gl_get_shader_precision_format)),
6109        "glGetError" => Some(mem::transmute::<GlGetError, unsafe extern "C" fn()>(
6110            fake_gl_get_error,
6111        )),
6112        "glCheckFramebufferStatus" => Some(mem::transmute::<
6113            GlCheckFramebufferStatus,
6114            unsafe extern "C" fn(),
6115        >(fake_gl_check_framebuffer_status)),
6116        "glInvalidateFramebuffer" => {
6117            let state = fake_gl_state()
6118                .lock()
6119                .expect("fake GL state mutex poisoned");
6120            if state.config.version_info.version_at_least(3, 0) {
6121                Some(mem::transmute::<
6122                    GlInvalidateFramebuffer,
6123                    unsafe extern "C" fn(),
6124                >(fake_gl_invalidate_framebuffer))
6125            } else {
6126                None
6127            }
6128        }
6129        "glClearColor" => Some(mem::transmute::<GlClearColor, unsafe extern "C" fn()>(
6130            fake_gl_clear_color,
6131        )),
6132        "glClear" => Some(mem::transmute::<GlClear, unsafe extern "C" fn()>(
6133            fake_gl_clear,
6134        )),
6135        "glEnable" => Some(mem::transmute::<GlEnable, unsafe extern "C" fn()>(
6136            fake_gl_enable,
6137        )),
6138        "glDisable" => Some(mem::transmute::<GlDisable, unsafe extern "C" fn()>(
6139            fake_gl_disable,
6140        )),
6141        "glDepthFunc" => Some(mem::transmute::<GlDepthFunc, unsafe extern "C" fn()>(
6142            fake_gl_depth_func,
6143        )),
6144        "glDepthMask" => Some(mem::transmute::<GlDepthMask, unsafe extern "C" fn()>(
6145            fake_gl_depth_mask,
6146        )),
6147        "glDepthRangef" => Some(mem::transmute::<GlDepthRangef, unsafe extern "C" fn()>(
6148            fake_gl_depth_range_f,
6149        )),
6150        "glCullFace" => Some(mem::transmute::<GlCullFace, unsafe extern "C" fn()>(
6151            fake_gl_cull_face,
6152        )),
6153        "glFrontFace" => Some(mem::transmute::<GlFrontFace, unsafe extern "C" fn()>(
6154            fake_gl_front_face,
6155        )),
6156        "glStencilFunc" => Some(mem::transmute::<GlStencilFunc, unsafe extern "C" fn()>(
6157            fake_gl_stencil_func,
6158        )),
6159        "glStencilMask" => Some(mem::transmute::<GlStencilMaskFn, unsafe extern "C" fn()>(
6160            fake_gl_stencil_mask,
6161        )),
6162        "glStencilOp" => Some(mem::transmute::<GlStencilOp, unsafe extern "C" fn()>(
6163            fake_gl_stencil_op,
6164        )),
6165        "glStencilFuncSeparate" => Some(mem::transmute::<
6166            GlStencilFuncSeparate,
6167            unsafe extern "C" fn(),
6168        >(fake_gl_stencil_func_separate)),
6169        "glStencilMaskSeparate" => Some(mem::transmute::<
6170            GlStencilMaskSeparate,
6171            unsafe extern "C" fn(),
6172        >(fake_gl_stencil_mask_separate)),
6173        "glStencilOpSeparate" => Some(
6174            mem::transmute::<GlStencilOpSeparate, unsafe extern "C" fn()>(
6175                fake_gl_stencil_op_separate,
6176            ),
6177        ),
6178        "glColorMask" => Some(mem::transmute::<GlColorMaskFn, unsafe extern "C" fn()>(
6179            fake_gl_color_mask,
6180        )),
6181        "glPolygonOffset" => Some(mem::transmute::<GlPolygonOffsetFn, unsafe extern "C" fn()>(
6182            fake_gl_polygon_offset,
6183        )),
6184        "glGenQueries" => Some(mem::transmute::<GlGenQueries, unsafe extern "C" fn()>(
6185            fake_gl_gen_queries,
6186        )),
6187        "glDeleteQueries" => Some(mem::transmute::<GlDeleteQueries, unsafe extern "C" fn()>(
6188            fake_gl_delete_queries,
6189        )),
6190        "glBeginQuery" => Some(mem::transmute::<GlBeginQuery, unsafe extern "C" fn()>(
6191            fake_gl_begin_query,
6192        )),
6193        "glEndQuery" => Some(mem::transmute::<GlEndQuery, unsafe extern "C" fn()>(
6194            fake_gl_end_query,
6195        )),
6196        "glGetQueryObjectuiv" => Some(
6197            mem::transmute::<GlGetQueryObjectuiv, unsafe extern "C" fn()>(
6198                fake_gl_get_query_object_uiv,
6199            ),
6200        ),
6201        "glFenceSync" => Some(mem::transmute::<GlFenceSync, unsafe extern "C" fn()>(
6202            fake_gl_fence_sync,
6203        )),
6204        "glClientWaitSync" => Some(mem::transmute::<GlClientWaitSync, unsafe extern "C" fn()>(
6205            fake_gl_client_wait_sync,
6206        )),
6207        "glWaitSync" => Some(mem::transmute::<GlWaitSync, unsafe extern "C" fn()>(
6208            fake_gl_wait_sync,
6209        )),
6210        "glDeleteSync" => Some(mem::transmute::<GlDeleteSync, unsafe extern "C" fn()>(
6211            fake_gl_delete_sync,
6212        )),
6213        "glReadPixels" => Some(mem::transmute::<GlReadPixels, unsafe extern "C" fn()>(
6214            fake_gl_read_pixels,
6215        )),
6216        "glReadBuffer" => Some(mem::transmute::<GlReadBuffer, unsafe extern "C" fn()>(
6217            fake_gl_read_buffer,
6218        )),
6219        "glDrawBuffers" => Some(mem::transmute::<GlDrawBuffers, unsafe extern "C" fn()>(
6220            fake_gl_draw_buffers,
6221        )),
6222        "glViewport" => Some(mem::transmute::<GlViewport, unsafe extern "C" fn()>(
6223            fake_gl_viewport,
6224        )),
6225        "glScissor" => Some(mem::transmute::<GlScissor, unsafe extern "C" fn()>(
6226            fake_gl_scissor,
6227        )),
6228        "glCreateShader" => Some(mem::transmute::<GlCreateShader, unsafe extern "C" fn()>(
6229            fake_gl_create_shader,
6230        )),
6231        "glShaderSource" => Some(mem::transmute::<GlShaderSource, unsafe extern "C" fn()>(
6232            fake_gl_shader_source,
6233        )),
6234        "glCompileShader" => Some(mem::transmute::<GlCompileShader, unsafe extern "C" fn()>(
6235            fake_gl_compile_shader,
6236        )),
6237        "glGetShaderiv" => Some(mem::transmute::<GlGetShaderIv, unsafe extern "C" fn()>(
6238            fake_gl_get_shader_iv,
6239        )),
6240        "glGetShaderInfoLog" => Some(
6241            mem::transmute::<GlGetShaderInfoLog, unsafe extern "C" fn()>(
6242                fake_gl_get_shader_info_log,
6243            ),
6244        ),
6245        "glDeleteShader" => Some(mem::transmute::<GlDeleteShader, unsafe extern "C" fn()>(
6246            fake_gl_delete_shader,
6247        )),
6248        "glCreateProgram" => Some(mem::transmute::<GlCreateProgram, unsafe extern "C" fn()>(
6249            fake_gl_create_program,
6250        )),
6251        "glAttachShader" => Some(mem::transmute::<GlAttachShader, unsafe extern "C" fn()>(
6252            fake_gl_attach_shader,
6253        )),
6254        "glLinkProgram" => Some(mem::transmute::<GlLinkProgram, unsafe extern "C" fn()>(
6255            fake_gl_link_program,
6256        )),
6257        "glGetProgramiv" => Some(mem::transmute::<GlGetProgramIv, unsafe extern "C" fn()>(
6258            fake_gl_get_program_iv,
6259        )),
6260        "glGetProgramInfoLog" => Some(
6261            mem::transmute::<GlGetProgramInfoLog, unsafe extern "C" fn()>(
6262                fake_gl_get_program_info_log,
6263            ),
6264        ),
6265        "glDeleteProgram" => Some(mem::transmute::<GlDeleteProgram, unsafe extern "C" fn()>(
6266            fake_gl_delete_program,
6267        )),
6268        "glUseProgram" => Some(mem::transmute::<GlUseProgram, unsafe extern "C" fn()>(
6269            fake_gl_use_program,
6270        )),
6271        "glGenBuffers" => Some(mem::transmute::<GlGenBuffers, unsafe extern "C" fn()>(
6272            fake_gl_gen_buffers,
6273        )),
6274        "glBindBuffer" => Some(mem::transmute::<GlBindBuffer, unsafe extern "C" fn()>(
6275            fake_gl_bind_buffer,
6276        )),
6277        "glBindBufferBase" => Some(mem::transmute::<GlBindBufferBase, unsafe extern "C" fn()>(
6278            fake_gl_bind_buffer_base,
6279        )),
6280        "glBindBufferRange" => Some(mem::transmute::<GlBindBufferRange, unsafe extern "C" fn()>(
6281            fake_gl_bind_buffer_range,
6282        )),
6283        "glBufferData" => Some(mem::transmute::<GlBufferData, unsafe extern "C" fn()>(
6284            fake_gl_buffer_data,
6285        )),
6286        "glBufferSubData" => Some(mem::transmute::<GlBufferSubData, unsafe extern "C" fn()>(
6287            fake_gl_buffer_sub_data,
6288        )),
6289        "glCopyBufferSubData" => Some(
6290            mem::transmute::<GlCopyBufferSubData, unsafe extern "C" fn()>(
6291                fake_gl_copy_buffer_sub_data,
6292            ),
6293        ),
6294        "glDeleteBuffers" => Some(mem::transmute::<GlDeleteBuffers, unsafe extern "C" fn()>(
6295            fake_gl_delete_buffers,
6296        )),
6297        "glGenTextures" => Some(mem::transmute::<GlGenTextures, unsafe extern "C" fn()>(
6298            fake_gl_gen_textures,
6299        )),
6300        "glBindTexture" => Some(mem::transmute::<GlBindTexture, unsafe extern "C" fn()>(
6301            fake_gl_bind_texture,
6302        )),
6303        "glActiveTexture" => Some(mem::transmute::<GlActiveTexture, unsafe extern "C" fn()>(
6304            fake_gl_active_texture,
6305        )),
6306        "glTexParameteri" => Some(mem::transmute::<GlTexParameteri, unsafe extern "C" fn()>(
6307            fake_gl_tex_parameter_i,
6308        )),
6309        "glPixelStorei" => Some(mem::transmute::<GlPixelStorei, unsafe extern "C" fn()>(
6310            fake_gl_pixel_store_i,
6311        )),
6312        "glTexImage2D" => Some(mem::transmute::<GlTexImage2D, unsafe extern "C" fn()>(
6313            fake_gl_tex_image_2d,
6314        )),
6315        "glTexSubImage2D" => Some(mem::transmute::<GlTexSubImage2D, unsafe extern "C" fn()>(
6316            fake_gl_tex_sub_image_2d,
6317        )),
6318        "glTexImage3D" => fake_gl_state()
6319            .lock()
6320            .expect("fake GL state mutex poisoned")
6321            .config
6322            .supports_texture_arrays
6323            .then_some(mem::transmute::<GlTexImage3D, unsafe extern "C" fn()>(
6324                fake_gl_tex_image_3d,
6325            )),
6326        "glTexSubImage3D" => fake_gl_state()
6327            .lock()
6328            .expect("fake GL state mutex poisoned")
6329            .config
6330            .supports_texture_arrays
6331            .then_some(mem::transmute::<GlTexSubImage3D, unsafe extern "C" fn()>(
6332                fake_gl_tex_sub_image_3d,
6333            )),
6334        "glGenerateMipmap" => fake_gl_state()
6335            .lock()
6336            .expect("fake GL state mutex poisoned")
6337            .config
6338            .supports_generate_mipmap
6339            .then_some(mem::transmute::<GlGenerateMipmap, unsafe extern "C" fn()>(
6340                fake_gl_generate_mipmap,
6341            )),
6342        "glDeleteTextures" => Some(mem::transmute::<GlDeleteTextures, unsafe extern "C" fn()>(
6343            fake_gl_delete_textures,
6344        )),
6345        "glGenVertexArrays" => fake_gl_state()
6346            .lock()
6347            .expect("fake GL state mutex poisoned")
6348            .config
6349            .supports_vertex_arrays
6350            .then_some(mem::transmute::<GlGenVertexArrays, unsafe extern "C" fn()>(
6351                fake_gl_gen_vertex_arrays,
6352            )),
6353        "glBindVertexArray" => fake_gl_state()
6354            .lock()
6355            .expect("fake GL state mutex poisoned")
6356            .config
6357            .supports_vertex_arrays
6358            .then_some(mem::transmute::<GlBindVertexArray, unsafe extern "C" fn()>(
6359                fake_gl_bind_vertex_array,
6360            )),
6361        "glDeleteVertexArrays" => fake_gl_state()
6362            .lock()
6363            .expect("fake GL state mutex poisoned")
6364            .config
6365            .supports_vertex_arrays
6366            .then_some(
6367                mem::transmute::<GlDeleteVertexArrays, unsafe extern "C" fn()>(
6368                    fake_gl_delete_vertex_arrays,
6369                ),
6370            ),
6371        "glEnableVertexAttribArray" => Some(mem::transmute::<
6372            GlEnableVertexAttribArray,
6373            unsafe extern "C" fn(),
6374        >(fake_gl_enable_vertex_attrib_array)),
6375        "glDisableVertexAttribArray" => Some(mem::transmute::<
6376            GlDisableVertexAttribArray,
6377            unsafe extern "C" fn(),
6378        >(fake_gl_disable_vertex_attrib_array)),
6379        "glVertexAttribPointer" => Some(mem::transmute::<
6380            GlVertexAttribPointer,
6381            unsafe extern "C" fn(),
6382        >(fake_gl_vertex_attrib_pointer)),
6383        "glVertexAttribDivisor" => fake_gl_state()
6384            .lock()
6385            .expect("fake GL state mutex poisoned")
6386            .config
6387            .supports_instancing
6388            .then_some(mem::transmute::<
6389                GlVertexAttribDivisorFn,
6390                unsafe extern "C" fn(),
6391            >(fake_gl_vertex_attrib_divisor)),
6392        "glGetUniformLocation" => Some(mem::transmute::<
6393            GlGetUniformLocation,
6394            unsafe extern "C" fn(),
6395        >(fake_gl_get_uniform_location)),
6396        "glGetAttribLocation" => Some(
6397            mem::transmute::<GlGetAttribLocation, unsafe extern "C" fn()>(
6398                fake_gl_get_attrib_location,
6399            ),
6400        ),
6401        "glBindAttribLocation" => Some(mem::transmute::<
6402            GlBindAttribLocation,
6403            unsafe extern "C" fn(),
6404        >(fake_gl_bind_attrib_location)),
6405        "glUniform1i" => Some(mem::transmute::<GlUniform1i, unsafe extern "C" fn()>(
6406            fake_gl_uniform_1i,
6407        )),
6408        "glUniform1f" => Some(mem::transmute::<GlUniform1f, unsafe extern "C" fn()>(
6409            fake_gl_uniform_1f,
6410        )),
6411        "glUniform2f" => Some(mem::transmute::<GlUniform2f, unsafe extern "C" fn()>(
6412            fake_gl_uniform_2f,
6413        )),
6414        "glUniform3f" => Some(mem::transmute::<GlUniform3f, unsafe extern "C" fn()>(
6415            fake_gl_uniform_3f,
6416        )),
6417        "glUniform4f" => Some(mem::transmute::<GlUniform4f, unsafe extern "C" fn()>(
6418            fake_gl_uniform_4f,
6419        )),
6420        "glUniform4fv" => Some(mem::transmute::<GlUniform4fv, unsafe extern "C" fn()>(
6421            fake_gl_uniform_4fv,
6422        )),
6423        "glUniformMatrix3fv" => Some(
6424            mem::transmute::<GlUniformMatrix3fv, unsafe extern "C" fn()>(
6425                fake_gl_uniform_matrix_3fv,
6426            ),
6427        ),
6428        "glUniformMatrix4fv" => Some(
6429            mem::transmute::<GlUniformMatrix4fv, unsafe extern "C" fn()>(
6430                fake_gl_uniform_matrix_4fv,
6431            ),
6432        ),
6433        "glDrawArrays" => Some(mem::transmute::<GlDrawArrays, unsafe extern "C" fn()>(
6434            fake_gl_draw_arrays,
6435        )),
6436        "glDrawArraysInstanced" => fake_gl_state()
6437            .lock()
6438            .expect("fake GL state mutex poisoned")
6439            .config
6440            .supports_instancing
6441            .then_some(mem::transmute::<
6442                GlDrawArraysInstanced,
6443                unsafe extern "C" fn(),
6444            >(fake_gl_draw_arrays_instanced)),
6445        "glDrawElements" => Some(mem::transmute::<GlDrawElements, unsafe extern "C" fn()>(
6446            fake_gl_draw_elements,
6447        )),
6448        "glDrawRangeElements" => Some(
6449            mem::transmute::<GlDrawRangeElements, unsafe extern "C" fn()>(
6450                fake_gl_draw_range_elements,
6451            ),
6452        ),
6453        "glDrawElementsInstanced" => fake_gl_state()
6454            .lock()
6455            .expect("fake GL state mutex poisoned")
6456            .config
6457            .supports_instancing
6458            .then_some(mem::transmute::<
6459                GlDrawElementsInstanced,
6460                unsafe extern "C" fn(),
6461            >(fake_gl_draw_elements_instanced)),
6462        "glBlendColor" => Some(mem::transmute::<GlBlendColor, unsafe extern "C" fn()>(
6463            fake_gl_blend_color,
6464        )),
6465        "glBlendFunc" => Some(mem::transmute::<GlBlendFunc, unsafe extern "C" fn()>(
6466            fake_gl_blend_func,
6467        )),
6468        "glBlendFuncSeparate" => Some(
6469            mem::transmute::<GlBlendFuncSeparate, unsafe extern "C" fn()>(
6470                fake_gl_blend_func_separate,
6471            ),
6472        ),
6473        "glBlendEquation" => Some(mem::transmute::<GlBlendEquationFn, unsafe extern "C" fn()>(
6474            fake_gl_blend_equation,
6475        )),
6476        "glBlendEquationSeparate" => Some(mem::transmute::<
6477            GlBlendEquationSeparate,
6478            unsafe extern "C" fn(),
6479        >(fake_gl_blend_equation_separate)),
6480        "glGenFramebuffers" => Some(mem::transmute::<GlGenFramebuffers, unsafe extern "C" fn()>(
6481            fake_gl_gen_framebuffers,
6482        )),
6483        "glBindFramebuffer" => Some(mem::transmute::<GlBindFramebuffer, unsafe extern "C" fn()>(
6484            fake_gl_bind_framebuffer,
6485        )),
6486        "glDeleteFramebuffers" => Some(mem::transmute::<
6487            GlDeleteFramebuffers,
6488            unsafe extern "C" fn(),
6489        >(fake_gl_delete_framebuffers)),
6490        "glFramebufferTexture2D" => Some(mem::transmute::<
6491            GlFramebufferTexture2D,
6492            unsafe extern "C" fn(),
6493        >(fake_gl_framebuffer_texture_2d)),
6494        "glGenRenderbuffers" => Some(
6495            mem::transmute::<GlGenRenderbuffers, unsafe extern "C" fn()>(fake_gl_gen_renderbuffers),
6496        ),
6497        "glBindRenderbuffer" => Some(
6498            mem::transmute::<GlBindRenderbuffer, unsafe extern "C" fn()>(fake_gl_bind_renderbuffer),
6499        ),
6500        "glRenderbufferStorage" => Some(mem::transmute::<
6501            GlRenderbufferStorage,
6502            unsafe extern "C" fn(),
6503        >(fake_gl_renderbuffer_storage)),
6504        "glDeleteRenderbuffers" => Some(mem::transmute::<
6505            GlDeleteRenderbuffers,
6506            unsafe extern "C" fn(),
6507        >(fake_gl_delete_renderbuffers)),
6508        "glFramebufferRenderbuffer" => Some(mem::transmute::<
6509            GlFramebufferRenderbuffer,
6510            unsafe extern "C" fn(),
6511        >(fake_gl_framebuffer_renderbuffer)),
6512        "glBlitFramebuffer" => Some(mem::transmute::<GlBlitFramebuffer, unsafe extern "C" fn()>(
6513            fake_gl_blit_framebuffer,
6514        )),
6515        _ => None,
6516    }
6517}
6518
6519unsafe extern "C" fn fake_gl_get_string(name: u32) -> *const u8 {
6520    let state = fake_gl_state()
6521        .lock()
6522        .expect("fake GL state mutex poisoned");
6523    match name {
6524        GL_VENDOR => state.vendor_string.as_ptr().cast::<u8>(),
6525        GL_RENDERER => state.renderer_string.as_ptr().cast::<u8>(),
6526        GL_VERSION => state.version_string.as_ptr().cast::<u8>(),
6527        GL_EXTENSIONS => state.extensions_string.as_ptr().cast::<u8>(),
6528        _ => std::ptr::null(),
6529    }
6530}
6531
6532unsafe extern "C" fn fake_gl_get_string_i(name: u32, index: u32) -> *const u8 {
6533    let state = fake_gl_state()
6534        .lock()
6535        .expect("fake GL state mutex poisoned");
6536    if name != GL_EXTENSIONS {
6537        return std::ptr::null();
6538    }
6539    state
6540        .indexed_extensions
6541        .get(index as usize)
6542        .map_or(std::ptr::null(), |extension| {
6543            extension.as_ptr().cast::<u8>()
6544        })
6545}
6546
6547unsafe extern "C" fn fake_gl_get_integer_v(name: u32, value: *mut i32) {
6548    let state = fake_gl_state()
6549        .lock()
6550        .expect("fake GL state mutex poisoned");
6551    let result = match name {
6552        GL_MAX_TEXTURE_SIZE => state.config.max_texture_size,
6553        GL_MAX_TEXTURE_IMAGE_UNITS => state.config.max_texture_image_units,
6554        GL_MAX_VARYING_VECTORS => state.config.max_varying_vectors,
6555        GL_NUM_EXTENSIONS => u32::try_from(state.indexed_extensions.len()).ok(),
6556        _ => None,
6557    }
6558    .and_then(|value| i32::try_from(value).ok())
6559    .unwrap_or(0);
6560
6561    if !value.is_null() {
6562        unsafe { *value = result };
6563    }
6564}
6565
6566unsafe extern "C" fn fake_gl_get_shader_precision_format(
6567    _shader_type: u32,
6568    _precision_type: u32,
6569    range: *mut i32,
6570    precision: *mut i32,
6571) {
6572    let supported = fake_gl_state()
6573        .lock()
6574        .expect("fake GL state mutex poisoned")
6575        .config
6576        .fragment_highp_float
6577        .unwrap_or(false);
6578    if !range.is_null() {
6579        unsafe {
6580            *range = if supported { 127 } else { 0 };
6581            *range.add(1) = if supported { 127 } else { 0 };
6582        }
6583    }
6584    if !precision.is_null() {
6585        unsafe {
6586            *precision = if supported { 23 } else { 0 };
6587        }
6588    }
6589}
6590
6591unsafe extern "C" fn fake_gl_get_error() -> u32 {
6592    let mut state = fake_gl_state()
6593        .lock()
6594        .expect("fake GL state mutex poisoned");
6595    state.config.next_error.take().unwrap_or(GL_NO_ERROR)
6596}
6597
6598unsafe extern "C" fn fake_gl_check_framebuffer_status(_target: u32) -> u32 {
6599    fake_gl_state()
6600        .lock()
6601        .expect("fake GL state mutex poisoned")
6602        .config
6603        .framebuffer_status
6604}
6605
6606unsafe extern "C" fn fake_gl_clear_color(r: f32, g: f32, b: f32, a: f32) {
6607    fake_gl_state()
6608        .lock()
6609        .expect("fake GL state mutex poisoned")
6610        .snapshot
6611        .current_clear_color = [r.to_bits(), g.to_bits(), b.to_bits(), a.to_bits()];
6612}
6613
6614unsafe extern "C" fn fake_gl_clear(_mask: u32) {
6615    let mut state = fake_gl_state()
6616        .lock()
6617        .expect("fake GL state mutex poisoned");
6618    let scissor_enabled = state.snapshot.scissor_enabled;
6619    let clear_color = state.snapshot.current_clear_color;
6620    state.snapshot.clear_calls += 1;
6621    state.snapshot.clear_colors.push(clear_color);
6622    state.snapshot.clear_scissor_enabled.push(scissor_enabled);
6623}
6624
6625unsafe extern "C" fn fake_gl_enable(cap: u32) {
6626    let mut state = fake_gl_state()
6627        .lock()
6628        .expect("fake GL state mutex poisoned");
6629    if !state.snapshot.enabled_capabilities.contains(&cap) {
6630        state.snapshot.enabled_capabilities.push(cap);
6631        state.snapshot.enabled_capabilities.sort_unstable();
6632    }
6633    match cap {
6634        GL_BLEND => state.snapshot.blend_enabled = true,
6635        GL_SCISSOR_TEST => state.snapshot.scissor_enabled = true,
6636        _ => {}
6637    }
6638}
6639
6640unsafe extern "C" fn fake_gl_disable(cap: u32) {
6641    let mut state = fake_gl_state()
6642        .lock()
6643        .expect("fake GL state mutex poisoned");
6644    state
6645        .snapshot
6646        .enabled_capabilities
6647        .retain(|enabled| *enabled != cap);
6648    match cap {
6649        GL_BLEND => state.snapshot.blend_enabled = false,
6650        GL_SCISSOR_TEST => state.snapshot.scissor_enabled = false,
6651        _ => {}
6652    }
6653}
6654
6655unsafe extern "C" fn fake_gl_depth_func(function: u32) {
6656    fake_gl_state()
6657        .lock()
6658        .expect("fake GL state mutex poisoned")
6659        .snapshot
6660        .depth_function = Some(function);
6661}
6662
6663unsafe extern "C" fn fake_gl_depth_mask(enabled: u8) {
6664    fake_gl_state()
6665        .lock()
6666        .expect("fake GL state mutex poisoned")
6667        .snapshot
6668        .depth_mask = enabled != GL_FALSE;
6669}
6670
6671unsafe extern "C" fn fake_gl_depth_range_f(near: f32, far: f32) {
6672    fake_gl_state()
6673        .lock()
6674        .expect("fake GL state mutex poisoned")
6675        .snapshot
6676        .depth_range_f_calls
6677        .push([near.to_bits(), far.to_bits()]);
6678}
6679
6680unsafe extern "C" fn fake_gl_cull_face(mode: u32) {
6681    fake_gl_state()
6682        .lock()
6683        .expect("fake GL state mutex poisoned")
6684        .snapshot
6685        .cull_face_mode = Some(mode);
6686}
6687
6688unsafe extern "C" fn fake_gl_front_face(winding: u32) {
6689    fake_gl_state()
6690        .lock()
6691        .expect("fake GL state mutex poisoned")
6692        .snapshot
6693        .front_face_winding = Some(winding);
6694}
6695
6696unsafe extern "C" fn fake_gl_stencil_func(function: u32, reference: i32, mask: u32) {
6697    fake_gl_state()
6698        .lock()
6699        .expect("fake GL state mutex poisoned")
6700        .snapshot
6701        .stencil_func = Some(FakeStencilFuncCall {
6702        function,
6703        reference,
6704        mask,
6705    });
6706}
6707
6708unsafe extern "C" fn fake_gl_stencil_mask(mask: u32) {
6709    fake_gl_state()
6710        .lock()
6711        .expect("fake GL state mutex poisoned")
6712        .snapshot
6713        .stencil_mask = mask;
6714}
6715
6716unsafe extern "C" fn fake_gl_stencil_op(stencil_fail: u32, depth_fail: u32, depth_pass: u32) {
6717    fake_gl_state()
6718        .lock()
6719        .expect("fake GL state mutex poisoned")
6720        .snapshot
6721        .stencil_op = Some(FakeStencilOpCall {
6722        stencil_fail,
6723        depth_fail,
6724        depth_pass,
6725    });
6726}
6727
6728unsafe extern "C" fn fake_gl_stencil_func_separate(
6729    face: u32,
6730    function: u32,
6731    reference: i32,
6732    mask: u32,
6733) {
6734    fake_gl_state()
6735        .lock()
6736        .expect("fake GL state mutex poisoned")
6737        .snapshot
6738        .stencil_func_separate_calls
6739        .push(FakeStencilFuncSeparateCall {
6740            face,
6741            function,
6742            reference,
6743            mask,
6744        });
6745}
6746
6747unsafe extern "C" fn fake_gl_stencil_mask_separate(face: u32, mask: u32) {
6748    fake_gl_state()
6749        .lock()
6750        .expect("fake GL state mutex poisoned")
6751        .snapshot
6752        .stencil_mask_separate_calls
6753        .push(FakeStencilMaskSeparateCall { face, mask });
6754}
6755
6756unsafe extern "C" fn fake_gl_stencil_op_separate(
6757    face: u32,
6758    stencil_fail: u32,
6759    depth_fail: u32,
6760    depth_pass: u32,
6761) {
6762    fake_gl_state()
6763        .lock()
6764        .expect("fake GL state mutex poisoned")
6765        .snapshot
6766        .stencil_op_separate_calls
6767        .push(FakeStencilOpSeparateCall {
6768            face,
6769            stencil_fail,
6770            depth_fail,
6771            depth_pass,
6772        });
6773}
6774
6775unsafe extern "C" fn fake_gl_color_mask(red: u8, green: u8, blue: u8, alpha: u8) {
6776    fake_gl_state()
6777        .lock()
6778        .expect("fake GL state mutex poisoned")
6779        .snapshot
6780        .color_write_mask = [
6781        red != GL_FALSE,
6782        green != GL_FALSE,
6783        blue != GL_FALSE,
6784        alpha != GL_FALSE,
6785    ];
6786}
6787
6788unsafe extern "C" fn fake_gl_polygon_offset(factor: f32, units: f32) {
6789    fake_gl_state()
6790        .lock()
6791        .expect("fake GL state mutex poisoned")
6792        .snapshot
6793        .polygon_offset = Some([factor.to_bits(), units.to_bits()]);
6794}
6795
6796unsafe extern "C" fn fake_gl_gen_queries(n: i32, queries: *mut u32) {
6797    if queries.is_null() {
6798        return;
6799    }
6800    let mut state = fake_gl_state()
6801        .lock()
6802        .expect("fake GL state mutex poisoned");
6803    for index in 0..usize::try_from(n.max(0)).unwrap_or(0) {
6804        let id = state.next_id();
6805        unsafe { *queries.add(index) = id };
6806        state.snapshot.generated_queries.push(id);
6807    }
6808}
6809
6810unsafe extern "C" fn fake_gl_delete_queries(n: i32, queries: *const u32) {
6811    if queries.is_null() {
6812        return;
6813    }
6814    let mut state = fake_gl_state()
6815        .lock()
6816        .expect("fake GL state mutex poisoned");
6817    for index in 0..usize::try_from(n.max(0)).unwrap_or(0) {
6818        state
6819            .snapshot
6820            .deleted_queries
6821            .push(unsafe { *queries.add(index) });
6822    }
6823}
6824
6825unsafe extern "C" fn fake_gl_begin_query(target: u32, query: u32) {
6826    fake_gl_state()
6827        .lock()
6828        .expect("fake GL state mutex poisoned")
6829        .snapshot
6830        .begin_query_calls
6831        .push((target, query));
6832}
6833
6834unsafe extern "C" fn fake_gl_end_query(target: u32) {
6835    fake_gl_state()
6836        .lock()
6837        .expect("fake GL state mutex poisoned")
6838        .snapshot
6839        .end_query_calls
6840        .push(target);
6841}
6842
6843unsafe extern "C" fn fake_gl_get_query_object_uiv(query: u32, property: u32, value: *mut u32) {
6844    fake_gl_state()
6845        .lock()
6846        .expect("fake GL state mutex poisoned")
6847        .snapshot
6848        .query_object_uiv_calls
6849        .push(FakeQueryObjectCall { query, property });
6850    if value.is_null() {
6851        return;
6852    }
6853    unsafe {
6854        *value = if property == GL_QUERY_RESULT_AVAILABLE {
6855            1
6856        } else {
6857            77
6858        };
6859    }
6860}
6861
6862unsafe extern "C" fn fake_gl_fence_sync(_condition: u32, _flags: u32) -> *const c_void {
6863    let mut state = fake_gl_state()
6864        .lock()
6865        .expect("fake GL state mutex poisoned");
6866    let id = state.next_id() as usize;
6867    state.snapshot.generated_syncs.push(id);
6868    id as *const c_void
6869}
6870
6871unsafe extern "C" fn fake_gl_client_wait_sync(
6872    sync: *const c_void,
6873    flags: u32,
6874    timeout_nanos: u64,
6875) -> u32 {
6876    fake_gl_state()
6877        .lock()
6878        .expect("fake GL state mutex poisoned")
6879        .snapshot
6880        .client_wait_sync_calls
6881        .push(FakeClientWaitSyncCall {
6882            sync: sync as usize,
6883            flags,
6884            timeout_nanos,
6885        });
6886    GL_ALREADY_SIGNALED
6887}
6888
6889unsafe extern "C" fn fake_gl_wait_sync(sync: *const c_void, flags: u32, timeout_nanos: u64) {
6890    fake_gl_state()
6891        .lock()
6892        .expect("fake GL state mutex poisoned")
6893        .snapshot
6894        .wait_sync_calls
6895        .push(FakeWaitSyncCall {
6896            sync: sync as usize,
6897            flags,
6898            timeout_nanos,
6899        });
6900}
6901
6902unsafe extern "C" fn fake_gl_delete_sync(sync: *const c_void) {
6903    fake_gl_state()
6904        .lock()
6905        .expect("fake GL state mutex poisoned")
6906        .snapshot
6907        .deleted_syncs
6908        .push(sync as usize);
6909}
6910
6911unsafe extern "C" fn fake_gl_read_pixels(
6912    x: i32,
6913    y: i32,
6914    width: i32,
6915    height: i32,
6916    format: u32,
6917    type_: u32,
6918    pixels: *mut c_void,
6919) {
6920    fake_gl_state()
6921        .lock()
6922        .expect("fake GL state mutex poisoned")
6923        .snapshot
6924        .read_pixels_calls
6925        .push(FakeReadPixelsCall {
6926            x,
6927            y,
6928            width,
6929            height,
6930            format,
6931            type_,
6932        });
6933    if pixels.is_null() {
6934        return;
6935    }
6936    let channels = match format {
6937        GL_RED | GL_LUMINANCE => 1usize,
6938        GL_RGB => 3,
6939        GL_RGBA => 4,
6940        _ => 0,
6941    };
6942    let byte_len = usize::try_from(width.max(0))
6943        .unwrap_or(0)
6944        .saturating_mul(usize::try_from(height.max(0)).unwrap_or(0))
6945        .saturating_mul(channels);
6946    unsafe { std::ptr::write_bytes(pixels.cast::<u8>(), 0xA5, byte_len) };
6947}
6948
6949unsafe extern "C" fn fake_gl_read_buffer(buffer: u32) {
6950    fake_gl_state()
6951        .lock()
6952        .expect("fake GL state mutex poisoned")
6953        .snapshot
6954        .read_buffer_calls
6955        .push(buffer);
6956}
6957
6958unsafe extern "C" fn fake_gl_draw_buffers(count: i32, buffers: *const u32) {
6959    let buffers = if count <= 0 || buffers.is_null() {
6960        Vec::new()
6961    } else {
6962        unsafe { std::slice::from_raw_parts(buffers, count as usize).to_vec() }
6963    };
6964    fake_gl_state()
6965        .lock()
6966        .expect("fake GL state mutex poisoned")
6967        .snapshot
6968        .draw_buffers_calls
6969        .push(buffers);
6970}
6971
6972unsafe extern "C" fn fake_gl_viewport(x: i32, y: i32, w: i32, h: i32) {
6973    fake_gl_state()
6974        .lock()
6975        .expect("fake GL state mutex poisoned")
6976        .snapshot
6977        .viewport_calls
6978        .push((x, y, w, h));
6979}
6980unsafe extern "C" fn fake_gl_scissor(x: i32, y: i32, w: i32, h: i32) {
6981    fake_gl_state()
6982        .lock()
6983        .expect("fake GL state mutex poisoned")
6984        .snapshot
6985        .scissor_calls
6986        .push((x, y, w, h));
6987}
6988
6989unsafe extern "C" fn fake_gl_create_shader(stage: u32) -> u32 {
6990    let mut state = fake_gl_state()
6991        .lock()
6992        .expect("fake GL state mutex poisoned");
6993    let shader = state.next_id();
6994    state
6995        .snapshot
6996        .created_shaders
6997        .push(FakeCreateShaderCall { stage, shader });
6998    shader
6999}
7000
7001unsafe extern "C" fn fake_gl_shader_source(
7002    _shader: u32,
7003    _count: i32,
7004    _source: *const *const c_char,
7005    _length: *const i32,
7006) {
7007}
7008
7009unsafe extern "C" fn fake_gl_compile_shader(_shader: u32) {}
7010
7011unsafe extern "C" fn fake_gl_get_shader_iv(_shader: u32, pname: u32, params: *mut i32) {
7012    let value = match pname {
7013        GL_COMPILE_STATUS => 1,
7014        GL_INFO_LOG_LENGTH => 0,
7015        _ => 0,
7016    };
7017    if !params.is_null() {
7018        unsafe { *params = value };
7019    }
7020}
7021
7022unsafe extern "C" fn fake_gl_get_shader_info_log(
7023    _shader: u32,
7024    _buf_size: i32,
7025    length: *mut i32,
7026    info_log: *mut c_char,
7027) {
7028    if !length.is_null() {
7029        unsafe { *length = 0 };
7030    }
7031    if !info_log.is_null() {
7032        unsafe { *info_log = 0 };
7033    }
7034}
7035
7036unsafe extern "C" fn fake_gl_delete_shader(shader: u32) {
7037    fake_gl_state()
7038        .lock()
7039        .expect("fake GL state mutex poisoned")
7040        .snapshot
7041        .deleted_shaders
7042        .push(shader);
7043}
7044
7045unsafe extern "C" fn fake_gl_create_program() -> u32 {
7046    fake_gl_state()
7047        .lock()
7048        .expect("fake GL state mutex poisoned")
7049        .next_id()
7050}
7051
7052unsafe extern "C" fn fake_gl_attach_shader(program: u32, shader: u32) {
7053    fake_gl_state()
7054        .lock()
7055        .expect("fake GL state mutex poisoned")
7056        .snapshot
7057        .attached_shaders
7058        .push(FakeAttachShaderCall { program, shader });
7059}
7060
7061unsafe extern "C" fn fake_gl_link_program(program: u32) {
7062    fake_gl_state()
7063        .lock()
7064        .expect("fake GL state mutex poisoned")
7065        .snapshot
7066        .linked_programs
7067        .push(program);
7068}
7069
7070unsafe extern "C" fn fake_gl_get_program_iv(_program: u32, pname: u32, params: *mut i32) {
7071    let value = match pname {
7072        GL_LINK_STATUS => 1,
7073        GL_INFO_LOG_LENGTH => 0,
7074        _ => 0,
7075    };
7076    if !params.is_null() {
7077        unsafe { *params = value };
7078    }
7079}
7080
7081unsafe extern "C" fn fake_gl_get_program_info_log(
7082    _program: u32,
7083    _buf_size: i32,
7084    length: *mut i32,
7085    info_log: *mut c_char,
7086) {
7087    if !length.is_null() {
7088        unsafe { *length = 0 };
7089    }
7090    if !info_log.is_null() {
7091        unsafe { *info_log = 0 };
7092    }
7093}
7094
7095unsafe extern "C" fn fake_gl_delete_program(program: u32) {
7096    fake_gl_state()
7097        .lock()
7098        .expect("fake GL state mutex poisoned")
7099        .snapshot
7100        .deleted_programs
7101        .push(program);
7102}
7103unsafe extern "C" fn fake_gl_use_program(program: u32) {
7104    let mut state = fake_gl_state()
7105        .lock()
7106        .expect("fake GL state mutex poisoned");
7107    state.snapshot.current_program = program;
7108    state.snapshot.use_program_calls.push(program);
7109}
7110
7111unsafe extern "C" fn fake_gl_gen_buffers(n: i32, buffers: *mut u32) {
7112    let mut state = fake_gl_state()
7113        .lock()
7114        .expect("fake GL state mutex poisoned");
7115    for index in 0..usize::try_from(n.max(0)).unwrap_or(0) {
7116        unsafe { *buffers.add(index) = state.next_id() };
7117    }
7118}
7119
7120unsafe extern "C" fn fake_gl_bind_buffer(target: u32, buffer: u32) {
7121    let mut state = fake_gl_state()
7122        .lock()
7123        .expect("fake GL state mutex poisoned");
7124    match target {
7125        GL_ARRAY_BUFFER => state.snapshot.bound_array_buffer = buffer,
7126        GL_ELEMENT_ARRAY_BUFFER => state.snapshot.bound_element_array_buffer = buffer,
7127        _ => {}
7128    }
7129}
7130
7131unsafe extern "C" fn fake_gl_bind_buffer_base(target: u32, index: u32, buffer: u32) {
7132    fake_gl_state()
7133        .lock()
7134        .expect("fake GL state mutex poisoned")
7135        .snapshot
7136        .bind_buffer_base_calls
7137        .push(FakeBindBufferBaseCall {
7138            target,
7139            index,
7140            buffer,
7141        });
7142}
7143
7144unsafe extern "C" fn fake_gl_bind_buffer_range(
7145    target: u32,
7146    index: u32,
7147    buffer: u32,
7148    offset: isize,
7149    size: isize,
7150) {
7151    fake_gl_state()
7152        .lock()
7153        .expect("fake GL state mutex poisoned")
7154        .snapshot
7155        .bind_buffer_range_calls
7156        .push(FakeBindBufferRangeCall {
7157            target,
7158            index,
7159            buffer,
7160            offset: usize::try_from(offset.max(0)).unwrap_or(0),
7161            size: usize::try_from(size.max(0)).unwrap_or(0),
7162        });
7163}
7164
7165unsafe extern "C" fn fake_gl_buffer_data(
7166    _target: u32,
7167    size: isize,
7168    _data: *const c_void,
7169    _usage: u32,
7170) {
7171    let mut state = fake_gl_state()
7172        .lock()
7173        .expect("fake GL state mutex poisoned");
7174    state.snapshot.buffer_data_calls = state.snapshot.buffer_data_calls.saturating_add(1);
7175    if let Ok(size) = usize::try_from(size.max(0)) {
7176        state.snapshot.buffer_data_bytes = state.snapshot.buffer_data_bytes.saturating_add(size);
7177        state.snapshot.buffer_data_uploads.push(FakeBufferDataCall {
7178            target: _target,
7179            byte_len: size,
7180            usage: _usage,
7181            has_data: !_data.is_null(),
7182        });
7183    }
7184}
7185
7186unsafe extern "C" fn fake_gl_buffer_sub_data(
7187    target: u32,
7188    offset: isize,
7189    size: isize,
7190    _data: *const c_void,
7191) {
7192    let mut state = fake_gl_state()
7193        .lock()
7194        .expect("fake GL state mutex poisoned");
7195    state
7196        .snapshot
7197        .buffer_sub_data_calls
7198        .push(FakeBufferSubDataCall {
7199            target,
7200            offset: usize::try_from(offset.max(0)).unwrap_or(0),
7201            byte_len: usize::try_from(size.max(0)).unwrap_or(0),
7202        });
7203}
7204
7205unsafe extern "C" fn fake_gl_copy_buffer_sub_data(
7206    read_target: u32,
7207    write_target: u32,
7208    read_offset: isize,
7209    write_offset: isize,
7210    size: isize,
7211) {
7212    fake_gl_state()
7213        .lock()
7214        .expect("fake GL state mutex poisoned")
7215        .snapshot
7216        .copy_buffer_sub_data_calls
7217        .push(FakeCopyBufferSubDataCall {
7218            read_target,
7219            write_target,
7220            read_offset: usize::try_from(read_offset.max(0)).unwrap_or(0),
7221            write_offset: usize::try_from(write_offset.max(0)).unwrap_or(0),
7222            byte_len: usize::try_from(size.max(0)).unwrap_or(0),
7223        });
7224}
7225
7226unsafe extern "C" fn fake_gl_delete_buffers(n: i32, buffers: *const u32) {
7227    if buffers.is_null() {
7228        return;
7229    }
7230
7231    let mut state = fake_gl_state()
7232        .lock()
7233        .expect("fake GL state mutex poisoned");
7234    for index in 0..usize::try_from(n.max(0)).unwrap_or(0) {
7235        let buffer = unsafe { *buffers.add(index) };
7236        state.snapshot.deleted_buffers.push(buffer);
7237    }
7238}
7239
7240unsafe extern "C" fn fake_gl_gen_textures(n: i32, textures: *mut u32) {
7241    let mut state = fake_gl_state()
7242        .lock()
7243        .expect("fake GL state mutex poisoned");
7244    for index in 0..usize::try_from(n.max(0)).unwrap_or(0) {
7245        unsafe { *textures.add(index) = state.next_id() };
7246    }
7247}
7248
7249unsafe extern "C" fn fake_gl_bind_texture(target: u32, texture: u32) {
7250    let mut state = fake_gl_state()
7251        .lock()
7252        .expect("fake GL state mutex poisoned");
7253    let unit = state.snapshot.active_texture;
7254    match target {
7255        GL_TEXTURE_2D => state.set_texture_2d_binding(unit, texture),
7256        GL_TEXTURE_2D_ARRAY => state.set_texture_2d_array_binding(unit, texture),
7257        _ => {}
7258    }
7259}
7260unsafe extern "C" fn fake_gl_active_texture(texture: u32) {
7261    fake_gl_state()
7262        .lock()
7263        .expect("fake GL state mutex poisoned")
7264        .snapshot
7265        .active_texture = texture.saturating_sub(GL_TEXTURE0);
7266}
7267unsafe extern "C" fn fake_gl_tex_parameter_i(target: u32, parameter: u32, value: i32) {
7268    fake_gl_state()
7269        .lock()
7270        .expect("fake GL state mutex poisoned")
7271        .snapshot
7272        .texture_parameter_calls
7273        .push(FakeTextureParameterCall {
7274            target,
7275            parameter,
7276            value,
7277        });
7278}
7279unsafe extern "C" fn fake_gl_pixel_store_i(parameter: u32, value: i32) {
7280    let mut state = fake_gl_state()
7281        .lock()
7282        .expect("fake GL state mutex poisoned");
7283    match parameter {
7284        GL_PACK_ALIGNMENT => {
7285            state.snapshot.pack_alignment = value;
7286            state.snapshot.pack_alignment_calls.push(value);
7287        }
7288        GL_UNPACK_ALIGNMENT => {
7289            state.snapshot.unpack_alignment = value;
7290            state.snapshot.unpack_alignment_calls.push(value);
7291        }
7292        _ => {}
7293    }
7294}
7295
7296unsafe extern "C" fn fake_gl_tex_image_2d(
7297    target: u32,
7298    _level: i32,
7299    internal_format: i32,
7300    width: i32,
7301    height: i32,
7302    _border: i32,
7303    format: u32,
7304    type_: u32,
7305    _pixels: *const c_void,
7306) {
7307    fake_gl_state()
7308        .lock()
7309        .expect("fake GL state mutex poisoned")
7310        .snapshot
7311        .texture_uploads_2d
7312        .push(FakeTextureUpload2D {
7313            target,
7314            internal_format: internal_format.max(0) as u32,
7315            width,
7316            height,
7317            format,
7318            type_,
7319        });
7320}
7321
7322unsafe extern "C" fn fake_gl_tex_sub_image_2d(
7323    target: u32,
7324    level: i32,
7325    xoffset: i32,
7326    yoffset: i32,
7327    width: i32,
7328    height: i32,
7329    format: u32,
7330    type_: u32,
7331    pixels: *const c_void,
7332) {
7333    fake_gl_state()
7334        .lock()
7335        .expect("fake GL state mutex poisoned")
7336        .snapshot
7337        .texture_sub_images_2d
7338        .push(FakeTextureSubImage2D {
7339            target,
7340            level,
7341            x: xoffset,
7342            y: yoffset,
7343            width,
7344            height,
7345            format,
7346            type_,
7347            has_pixels: !pixels.is_null(),
7348        });
7349}
7350
7351unsafe extern "C" fn fake_gl_tex_image_3d(
7352    _target: u32,
7353    _level: i32,
7354    _internal_format: i32,
7355    _width: i32,
7356    _height: i32,
7357    _depth: i32,
7358    _border: i32,
7359    _format: u32,
7360    _type_: u32,
7361    _pixels: *const c_void,
7362) {
7363    fake_gl_state()
7364        .lock()
7365        .expect("fake GL state mutex poisoned")
7366        .snapshot
7367        .texture_uploads_3d_calls += 1;
7368}
7369
7370unsafe extern "C" fn fake_gl_tex_sub_image_3d(
7371    _target: u32,
7372    _level: i32,
7373    _xoffset: i32,
7374    _yoffset: i32,
7375    _zoffset: i32,
7376    _width: i32,
7377    _height: i32,
7378    _depth: i32,
7379    _format: u32,
7380    _type_: u32,
7381    _pixels: *const c_void,
7382) {
7383}
7384
7385unsafe extern "C" fn fake_gl_generate_mipmap(target: u32) {
7386    fake_gl_state()
7387        .lock()
7388        .expect("fake GL state mutex poisoned")
7389        .snapshot
7390        .generate_mipmap_calls
7391        .push(target);
7392}
7393
7394unsafe extern "C" fn fake_gl_delete_textures(n: i32, textures: *const u32) {
7395    if textures.is_null() {
7396        return;
7397    }
7398
7399    let mut state = fake_gl_state()
7400        .lock()
7401        .expect("fake GL state mutex poisoned");
7402    for index in 0..usize::try_from(n.max(0)).unwrap_or(0) {
7403        let texture = unsafe { *textures.add(index) };
7404        state.snapshot.deleted_textures.push(texture);
7405    }
7406}
7407
7408unsafe extern "C" fn fake_gl_gen_vertex_arrays(n: i32, arrays: *mut u32) {
7409    let mut state = fake_gl_state()
7410        .lock()
7411        .expect("fake GL state mutex poisoned");
7412    for index in 0..usize::try_from(n.max(0)).unwrap_or(0) {
7413        unsafe { *arrays.add(index) = state.next_id() };
7414    }
7415}
7416
7417unsafe extern "C" fn fake_gl_bind_vertex_array(array: u32) {
7418    let mut state = fake_gl_state()
7419        .lock()
7420        .expect("fake GL state mutex poisoned");
7421    state.snapshot.bound_vertex_array = array;
7422    state.snapshot.vertex_array_bindings.push(array);
7423}
7424unsafe extern "C" fn fake_gl_delete_vertex_arrays(n: i32, arrays: *const u32) {
7425    if arrays.is_null() {
7426        return;
7427    }
7428
7429    let mut state = fake_gl_state()
7430        .lock()
7431        .expect("fake GL state mutex poisoned");
7432    for index in 0..usize::try_from(n.max(0)).unwrap_or(0) {
7433        let array = unsafe { *arrays.add(index) };
7434        state.snapshot.deleted_vertex_arrays.push(array);
7435    }
7436}
7437unsafe extern "C" fn fake_gl_enable_vertex_attrib_array(index: u32) {
7438    let mut state = fake_gl_state()
7439        .lock()
7440        .expect("fake GL state mutex poisoned");
7441    if state.snapshot.bound_vertex_array != 0 {
7442        return;
7443    }
7444    if !state.snapshot.enabled_vertex_attribs.contains(&index) {
7445        state.snapshot.enabled_vertex_attribs.push(index);
7446        state.snapshot.enabled_vertex_attribs.sort_unstable();
7447    }
7448}
7449
7450unsafe extern "C" fn fake_gl_disable_vertex_attrib_array(index: u32) {
7451    let mut state = fake_gl_state()
7452        .lock()
7453        .expect("fake GL state mutex poisoned");
7454    if state.snapshot.bound_vertex_array != 0 {
7455        return;
7456    }
7457    state
7458        .snapshot
7459        .enabled_vertex_attribs
7460        .retain(|enabled| *enabled != index);
7461}
7462
7463unsafe extern "C" fn fake_gl_vertex_attrib_pointer(
7464    index: u32,
7465    size: i32,
7466    type_: u32,
7467    normalized: u8,
7468    stride: i32,
7469    pointer: *const c_void,
7470) {
7471    let mut state = fake_gl_state()
7472        .lock()
7473        .expect("fake GL state mutex poisoned");
7474    state
7475        .snapshot
7476        .vertex_attrib_pointer_calls
7477        .push(FakeVertexAttribPointerCall {
7478            index,
7479            size,
7480            type_,
7481            normalized: normalized != GL_FALSE,
7482            stride,
7483            offset: pointer as usize,
7484        });
7485}
7486
7487unsafe extern "C" fn fake_gl_vertex_attrib_divisor(_index: u32, _divisor: u32) {}
7488
7489fn fake_uniform_location(name: &str) -> i32 {
7490    match name {
7491        "projection" => 0,
7492        "canvas_transform" => 1,
7493        "u_viewport" => 2,
7494        "u_font" => 3,
7495        "u_color" => 4,
7496        "definitely_missing" => -1,
7497        _ => 5,
7498    }
7499}
7500
7501fn fake_attribute_location(name: &str) -> i32 {
7502    match name {
7503        "a_corner" | "position" | "a_pos" => 0,
7504        "a_rect" | "uv" | "a_uv" => 1,
7505        "a_uv_rect" | "color" => 2,
7506        "a_color" | "sprite_data" => 3,
7507        "a_clip_rect" => 4,
7508        "a_sprite_data" => 5,
7509        _ => -1,
7510    }
7511}
7512
7513unsafe extern "C" fn fake_gl_get_uniform_location(_program: u32, name: *const c_char) -> i32 {
7514    if name.is_null() {
7515        return -1;
7516    }
7517    let name = unsafe { CStr::from_ptr(name) }.to_str().unwrap_or_default();
7518    fake_uniform_location(name)
7519}
7520
7521unsafe extern "C" fn fake_gl_get_attrib_location(_program: u32, name: *const c_char) -> i32 {
7522    if name.is_null() {
7523        return -1;
7524    }
7525    let name = unsafe { CStr::from_ptr(name) }.to_str().unwrap_or_default();
7526    fake_attribute_location(name)
7527}
7528
7529unsafe extern "C" fn fake_gl_bind_attrib_location(
7530    program: u32,
7531    location: u32,
7532    name: *const c_char,
7533) {
7534    let name = if name.is_null() {
7535        String::new()
7536    } else {
7537        unsafe { CStr::from_ptr(name) }
7538            .to_string_lossy()
7539            .into_owned()
7540    };
7541    fake_gl_state()
7542        .lock()
7543        .expect("fake GL state mutex poisoned")
7544        .snapshot
7545        .bind_attrib_location_calls
7546        .push(FakeBindAttribLocationCall {
7547            program,
7548            location,
7549            name,
7550        });
7551}
7552
7553unsafe extern "C" fn fake_gl_uniform_1i(location: i32, value: i32) {
7554    fake_gl_state()
7555        .lock()
7556        .expect("fake GL state mutex poisoned")
7557        .snapshot
7558        .uniform_1i_calls
7559        .push(FakeUniformIntCall { location, value });
7560}
7561unsafe extern "C" fn fake_gl_uniform_1f(_location: i32, _value: f32) {}
7562unsafe extern "C" fn fake_gl_uniform_2f(_location: i32, _x: f32, _y: f32) {}
7563unsafe extern "C" fn fake_gl_uniform_3f(location: i32, x: f32, y: f32, z: f32) {
7564    fake_gl_state()
7565        .lock()
7566        .expect("fake GL state mutex poisoned")
7567        .snapshot
7568        .uniform_3f_calls
7569        .push(FakeUniformFloatCall {
7570            location,
7571            values: [x.to_bits(), y.to_bits(), z.to_bits(), 0],
7572        });
7573}
7574
7575unsafe extern "C" fn fake_gl_uniform_4f(location: i32, x: f32, y: f32, z: f32, w: f32) {
7576    fake_gl_state()
7577        .lock()
7578        .expect("fake GL state mutex poisoned")
7579        .snapshot
7580        .uniform_4f_calls
7581        .push(FakeUniformFloatCall {
7582            location,
7583            values: [x.to_bits(), y.to_bits(), z.to_bits(), w.to_bits()],
7584        });
7585}
7586
7587unsafe extern "C" fn fake_gl_uniform_4fv(location: i32, count: i32, value: *const f32) {
7588    if value.is_null() || count <= 0 {
7589        return;
7590    }
7591    let values = unsafe { std::slice::from_raw_parts(value, 4) };
7592    fake_gl_state()
7593        .lock()
7594        .expect("fake GL state mutex poisoned")
7595        .snapshot
7596        .uniform_4fv_calls
7597        .push(FakeUniformFloatCall {
7598            location,
7599            values: [
7600                values[0].to_bits(),
7601                values[1].to_bits(),
7602                values[2].to_bits(),
7603                values[3].to_bits(),
7604            ],
7605        });
7606}
7607
7608unsafe extern "C" fn fake_gl_uniform_matrix_3fv(
7609    location: i32,
7610    _count: i32,
7611    transpose: u8,
7612    value: *const f32,
7613) {
7614    if value.is_null() {
7615        return;
7616    }
7617    let values = unsafe { std::slice::from_raw_parts(value, 9) };
7618    fake_gl_state()
7619        .lock()
7620        .expect("fake GL state mutex poisoned")
7621        .snapshot
7622        .uniform_matrix_3fv_calls
7623        .push(FakeUniformMatrix3Call {
7624            location,
7625            transpose: transpose != GL_FALSE,
7626            values: [
7627                values[0].to_bits(),
7628                values[1].to_bits(),
7629                values[2].to_bits(),
7630                values[3].to_bits(),
7631                values[4].to_bits(),
7632                values[5].to_bits(),
7633                values[6].to_bits(),
7634                values[7].to_bits(),
7635                values[8].to_bits(),
7636            ],
7637        });
7638}
7639
7640unsafe extern "C" fn fake_gl_uniform_matrix_4fv(
7641    _location: i32,
7642    _count: i32,
7643    _transpose: u8,
7644    _value: *const f32,
7645) {
7646}
7647
7648unsafe extern "C" fn fake_gl_draw_arrays(mode: u32, first: i32, count: i32) {
7649    let mut state = fake_gl_state()
7650        .lock()
7651        .expect("fake GL state mutex poisoned");
7652    state
7653        .snapshot
7654        .draw_arrays_call_args
7655        .push(FakeDrawArraysCall {
7656            mode,
7657            first,
7658            count,
7659            instance_count: None,
7660        });
7661    state.snapshot.draw_arrays_calls += 1;
7662}
7663
7664unsafe extern "C" fn fake_gl_draw_arrays_instanced(
7665    mode: u32,
7666    first: i32,
7667    count: i32,
7668    instance_count: i32,
7669) {
7670    let mut state = fake_gl_state()
7671        .lock()
7672        .expect("fake GL state mutex poisoned");
7673    state
7674        .snapshot
7675        .draw_arrays_call_args
7676        .push(FakeDrawArraysCall {
7677            mode,
7678            first,
7679            count,
7680            instance_count: Some(instance_count),
7681        });
7682    state.snapshot.draw_arrays_calls += 1;
7683}
7684
7685unsafe extern "C" fn fake_gl_draw_elements(
7686    mode: u32,
7687    count: i32,
7688    type_: u32,
7689    indices: *const c_void,
7690) {
7691    let mut state = fake_gl_state()
7692        .lock()
7693        .expect("fake GL state mutex poisoned");
7694    state.snapshot.draw_elements_calls += 1;
7695    state
7696        .snapshot
7697        .draw_elements_call_args
7698        .push(FakeDrawElementsCall {
7699            mode,
7700            vertex_range: None,
7701            count,
7702            type_,
7703            offset: indices as usize,
7704            instance_count: None,
7705        });
7706}
7707
7708unsafe extern "C" fn fake_gl_draw_range_elements(
7709    mode: u32,
7710    start: u32,
7711    end: u32,
7712    count: i32,
7713    type_: u32,
7714    indices: *const c_void,
7715) {
7716    let mut state = fake_gl_state()
7717        .lock()
7718        .expect("fake GL state mutex poisoned");
7719    state.snapshot.draw_elements_calls += 1;
7720    state
7721        .snapshot
7722        .draw_elements_call_args
7723        .push(FakeDrawElementsCall {
7724            mode,
7725            vertex_range: Some((start, end)),
7726            count,
7727            type_,
7728            offset: indices as usize,
7729            instance_count: None,
7730        });
7731}
7732
7733unsafe extern "C" fn fake_gl_draw_elements_instanced(
7734    mode: u32,
7735    count: i32,
7736    type_: u32,
7737    indices: *const c_void,
7738    instance_count: i32,
7739) {
7740    let mut state = fake_gl_state()
7741        .lock()
7742        .expect("fake GL state mutex poisoned");
7743    state.snapshot.draw_elements_instanced_calls += 1;
7744    state
7745        .snapshot
7746        .draw_elements_call_args
7747        .push(FakeDrawElementsCall {
7748            mode,
7749            vertex_range: None,
7750            count,
7751            type_,
7752            offset: indices as usize,
7753            instance_count: Some(instance_count),
7754        });
7755}
7756
7757unsafe extern "C" fn fake_gl_blend_color(r: f32, g: f32, b: f32, a: f32) {
7758    fake_gl_state()
7759        .lock()
7760        .expect("fake GL state mutex poisoned")
7761        .snapshot
7762        .blend_color_calls
7763        .push(FakeBlendColorCall {
7764            color: [r.to_bits(), g.to_bits(), b.to_bits(), a.to_bits()],
7765        });
7766}
7767
7768unsafe extern "C" fn fake_gl_blend_func(_src: u32, _dst: u32) {}
7769
7770unsafe extern "C" fn fake_gl_blend_func_separate(
7771    source_rgb: u32,
7772    destination_rgb: u32,
7773    source_alpha: u32,
7774    destination_alpha: u32,
7775) {
7776    fake_gl_state()
7777        .lock()
7778        .expect("fake GL state mutex poisoned")
7779        .snapshot
7780        .blend_func_separate_calls
7781        .push(FakeBlendFuncSeparateCall {
7782            source_rgb,
7783            destination_rgb,
7784            source_alpha,
7785            destination_alpha,
7786        });
7787}
7788
7789unsafe extern "C" fn fake_gl_blend_equation(_mode: u32) {}
7790
7791unsafe extern "C" fn fake_gl_blend_equation_separate(rgb: u32, alpha: u32) {
7792    fake_gl_state()
7793        .lock()
7794        .expect("fake GL state mutex poisoned")
7795        .snapshot
7796        .blend_equation_separate_calls
7797        .push(FakeBlendEquationSeparateCall { rgb, alpha });
7798}
7799
7800unsafe extern "C" fn fake_gl_gen_framebuffers(n: i32, framebuffers: *mut u32) {
7801    if framebuffers.is_null() {
7802        return;
7803    }
7804    let mut state = fake_gl_state()
7805        .lock()
7806        .expect("fake GL state mutex poisoned");
7807    for index in 0..usize::try_from(n.max(0)).unwrap_or(0) {
7808        let id = state.next_id();
7809        unsafe { *framebuffers.add(index) = id };
7810        state.snapshot.generated_framebuffers.push(id);
7811    }
7812}
7813
7814unsafe extern "C" fn fake_gl_bind_framebuffer(_target: u32, framebuffer: u32) {
7815    let mut state = fake_gl_state()
7816        .lock()
7817        .expect("fake GL state mutex poisoned");
7818    state.snapshot.bound_framebuffer = framebuffer;
7819    state.snapshot.framebuffer_bindings.push(framebuffer);
7820}
7821
7822unsafe extern "C" fn fake_gl_delete_framebuffers(n: i32, framebuffers: *const u32) {
7823    if framebuffers.is_null() {
7824        return;
7825    }
7826    let mut state = fake_gl_state()
7827        .lock()
7828        .expect("fake GL state mutex poisoned");
7829    for index in 0..usize::try_from(n.max(0)).unwrap_or(0) {
7830        state
7831            .snapshot
7832            .deleted_framebuffers
7833            .push(unsafe { *framebuffers.add(index) });
7834    }
7835}
7836
7837unsafe extern "C" fn fake_gl_framebuffer_texture_2d(
7838    target: u32,
7839    attachment: u32,
7840    texture_target: u32,
7841    texture: u32,
7842    level: i32,
7843) {
7844    fake_gl_state()
7845        .lock()
7846        .expect("fake GL state mutex poisoned")
7847        .snapshot
7848        .framebuffer_texture_2d_calls
7849        .push(FakeFramebufferTexture2DCall {
7850            target,
7851            attachment,
7852            texture_target,
7853            texture,
7854            level,
7855        });
7856}
7857
7858unsafe extern "C" fn fake_gl_gen_renderbuffers(n: i32, renderbuffers: *mut u32) {
7859    if renderbuffers.is_null() {
7860        return;
7861    }
7862    let mut state = fake_gl_state()
7863        .lock()
7864        .expect("fake GL state mutex poisoned");
7865    for index in 0..usize::try_from(n.max(0)).unwrap_or(0) {
7866        let id = state.next_id();
7867        unsafe { *renderbuffers.add(index) = id };
7868        state.snapshot.generated_renderbuffers.push(id);
7869    }
7870}
7871
7872unsafe extern "C" fn fake_gl_bind_renderbuffer(_target: u32, renderbuffer: u32) {
7873    let mut state = fake_gl_state()
7874        .lock()
7875        .expect("fake GL state mutex poisoned");
7876    state.snapshot.bound_renderbuffer = renderbuffer;
7877    state.snapshot.renderbuffer_bindings.push(renderbuffer);
7878}
7879
7880unsafe extern "C" fn fake_gl_renderbuffer_storage(
7881    target: u32,
7882    internal_format: u32,
7883    width: i32,
7884    height: i32,
7885) {
7886    fake_gl_state()
7887        .lock()
7888        .expect("fake GL state mutex poisoned")
7889        .snapshot
7890        .renderbuffer_storage_calls
7891        .push(FakeRenderbufferStorageCall {
7892            target,
7893            internal_format,
7894            width,
7895            height,
7896        });
7897}
7898
7899unsafe extern "C" fn fake_gl_delete_renderbuffers(n: i32, renderbuffers: *const u32) {
7900    if renderbuffers.is_null() {
7901        return;
7902    }
7903    let mut state = fake_gl_state()
7904        .lock()
7905        .expect("fake GL state mutex poisoned");
7906    for index in 0..usize::try_from(n.max(0)).unwrap_or(0) {
7907        state
7908            .snapshot
7909            .deleted_renderbuffers
7910            .push(unsafe { *renderbuffers.add(index) });
7911    }
7912}
7913
7914unsafe extern "C" fn fake_gl_framebuffer_renderbuffer(
7915    target: u32,
7916    attachment: u32,
7917    renderbuffer_target: u32,
7918    renderbuffer: u32,
7919) {
7920    fake_gl_state()
7921        .lock()
7922        .expect("fake GL state mutex poisoned")
7923        .snapshot
7924        .framebuffer_renderbuffer_calls
7925        .push(FakeFramebufferRenderbufferCall {
7926            target,
7927            attachment,
7928            renderbuffer_target,
7929            renderbuffer,
7930        });
7931}
7932
7933unsafe extern "C" fn fake_gl_blit_framebuffer(
7934    source_x0: i32,
7935    source_y0: i32,
7936    source_x1: i32,
7937    source_y1: i32,
7938    destination_x0: i32,
7939    destination_y0: i32,
7940    destination_x1: i32,
7941    destination_y1: i32,
7942    mask: u32,
7943    filter: u32,
7944) {
7945    fake_gl_state()
7946        .lock()
7947        .expect("fake GL state mutex poisoned")
7948        .snapshot
7949        .blit_framebuffer_calls
7950        .push(FakeBlitFramebufferCall {
7951            source: [source_x0, source_y0, source_x1, source_y1],
7952            destination: [
7953                destination_x0,
7954                destination_y0,
7955                destination_x1,
7956                destination_y1,
7957            ],
7958            mask,
7959            filter,
7960        });
7961}
7962
7963unsafe extern "C" fn fake_gl_invalidate_framebuffer(
7964    _target: u32,
7965    num_attachments: i32,
7966    attachments: *const u32,
7967) {
7968    fake_gl_record_framebuffer_invalidation(num_attachments, attachments);
7969}
7970
7971fn fake_gl_record_framebuffer_invalidation(num_attachments: i32, attachments: *const u32) {
7972    let attachments = if num_attachments <= 0 || attachments.is_null() {
7973        Vec::new()
7974    } else {
7975        // Safety: fake GL callers pass a stack slice that is valid for the
7976        // duration of the call, matching the GL API contract.
7977        unsafe { std::slice::from_raw_parts(attachments, num_attachments as usize).to_vec() }
7978    };
7979    fake_gl_state()
7980        .lock()
7981        .expect("fake GL state mutex poisoned")
7982        .snapshot
7983        .framebuffer_invalidations
7984        .push(attachments);
7985}
7986
7987#[cfg(test)]
7988pub(crate) fn fake_gl_test_guard() -> std::sync::MutexGuard<'static, ()> {
7989    static FAKE_GL_TEST_GUARD: OnceLock<Mutex<()>> = OnceLock::new();
7990    FAKE_GL_TEST_GUARD
7991        .get_or_init(|| Mutex::new(()))
7992        .lock()
7993        .expect("fake GL test guard mutex poisoned")
7994}
7995
7996#[cfg(test)]
7997mod tests {
7998    use std::{ffi::c_void, mem, path::Path, process::Command};
7999
8000    use enumflags2::BitFlags;
8001
8002    use super::{
8003        CompatGl, CompatGlClear, FakeAttachShaderCall, FakeBindAttribLocationCall,
8004        FakeBindBufferBaseCall, FakeBindBufferRangeCall, FakeBlendColorCall,
8005        FakeBlendEquationSeparateCall, FakeBlendFuncSeparateCall, FakeBlitFramebufferCall,
8006        FakeBufferDataCall, FakeBufferSubDataCall, FakeCopyBufferSubDataCall, FakeCreateShaderCall,
8007        FakeDrawArraysCall, FakeDrawElementsCall, FakeGlConfig, FakeQueryObjectCall,
8008        FakeUniformIntCall, FakeVertexAttribPointerCall, GL_FLOAT, Gl, GlBlendEquation,
8009        GlBlendFactor, GlBuffer, GlBufferBindingIndex, GlBufferByteOffset, GlBufferByteSize,
8010        GlBufferRange, GlBufferTarget, GlBufferUsage, GlCapability, GlColorWriteMask,
8011        GlCullFaceMode, GlDepthFunction, GlDepthRange, GlDrawMode, GlDrawRange,
8012        GlElementByteOffset, GlElementRange, GlElementVertexRange, GlFramebuffer,
8013        GlFramebufferAttachment, GlFramebufferBlitBuffer, GlFramebufferBlitFilter,
8014        GlFramebufferBuffer, GlFramebufferTarget, GlFramebufferTexture2DTarget, GlFrontFaceWinding,
8015        GlIndexType, GlIndexedBufferTarget, GlInstanceCount, GlPixelStoreAlignment,
8016        GlPolygonOffset, GlProgram, GlQueryTarget, GlRect, GlRenderbufferInternalFormat,
8017        GlRenderbufferSize, GlRenderbufferTarget, GlShaderStage, GlStencilFace, GlStencilFunction,
8018        GlStencilMask, GlStencilOperation, GlStencilReference, GlSyncTimeout, GlSyncWaitResult,
8019        GlTexture, GlTextureDataType, GlTextureFormat, GlTextureInternalFormat, GlTextureLevel,
8020        GlTextureMagFilter, GlTextureMinFilter, GlTextureOffset2D, GlTextureSize2D,
8021        GlTextureTarget, GlTextureUnit, GlTextureWrap, GlUniformLocation, GlVersionInfo,
8022        GlVertexAttribF32Components, GlVertexAttribF32Layout, GlVertexAttribLocation,
8023        GlVertexAttribStride, HwContextType, fake_gl_test_guard, fallback_gl_version_info, glsym,
8024        normalize_positive_gl_limit, parse_gl_version_info, query_gl_indexed_extensions,
8025    };
8026
8027    unsafe extern "C" fn fake_gl_get_string_i(_: u32, _: u32) -> *const u8 {
8028        std::ptr::null()
8029    }
8030
8031    #[test]
8032    fn generated_raw_registry_covers_vendored_gl_xml() {
8033        assert_eq!(crate::glsym_raw::GL_XML_COMMAND_COUNT, 246);
8034        assert_eq!(crate::glsym_raw::GL_XML_TYPE_COUNT, 14);
8035        assert_eq!(crate::glsym_raw::GL_XML_ENUM_COUNT, 622);
8036        assert_eq!(
8037            crate::glsym_raw::GL_XML_COMMAND_NAMES.len(),
8038            crate::glsym_raw::GL_XML_COMMAND_COUNT
8039        );
8040        assert_eq!(
8041            crate::glsym_raw::GL_XML_TYPE_NAMES.len(),
8042            crate::glsym_raw::GL_XML_TYPE_COUNT
8043        );
8044        assert!(crate::glsym_raw::GL_XML_COMMAND_NAMES.contains(&"glGetStringi"));
8045        assert!(
8046            !crate::glsym_raw::GL_XML_COMMAND_NAMES.contains(&"glDebugMessageCallback"),
8047            "desktop/KHR debug commands are outside the default GLES <= 3.0 raw scope"
8048        );
8049        assert!(crate::glsym_raw::GL_XML_TYPE_NAMES.contains(&"GLenum"));
8050        assert!(
8051            !crate::glsym_raw::GL_XML_TYPE_NAMES.contains(&"GLDEBUGPROC"),
8052            "debug callback types are outside the default GLES <= 3.0 raw scope"
8053        );
8054
8055        let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
8056        let verifier = manifest_dir.join("../../scripts/verify_glsym_raw.py");
8057        let output = Command::new("python3")
8058            .arg(verifier)
8059            .output()
8060            .expect("run glsym raw verifier");
8061        assert!(
8062            output.status.success(),
8063            "glsym raw verifier failed: stdout={} stderr={}",
8064            String::from_utf8_lossy(&output.stdout),
8065            String::from_utf8_lossy(&output.stderr)
8066        );
8067    }
8068
8069    #[test]
8070    fn generated_raw_symbol_loader_exposes_registry_commands_optionally() {
8071        let symbols = crate::glsym_raw::GlRawSymbols::load_with(|name| {
8072            (name == "glGetStringi").then_some(fake_gl_get_string_i as *const () as *const c_void)
8073        });
8074
8075        assert_eq!(symbols.available_count(), 1);
8076        assert!(symbols.is_available("glGetStringi"));
8077        assert!(!symbols.is_available("glDebugMessageCallback"));
8078        assert!(symbols.get_stringi.is_some());
8079        assert!(symbols.active_texture.is_none());
8080    }
8081
8082    #[test]
8083    fn indexed_extension_query_uses_get_string_i_without_raw_public_api() {
8084        let _guard = fake_gl_test_guard();
8085        let _gl = glsym::fake_for_testing(FakeGlConfig {
8086            extensions_string: "GL_EXT_texture_filter_anisotropic GL_KHR_debug".to_string(),
8087            ..FakeGlConfig::default()
8088        });
8089
8090        let extensions = query_gl_indexed_extensions(
8091            super::fake_gl_get_integer_v,
8092            Some(super::fake_gl_get_string_i),
8093        );
8094
8095        assert_eq!(extensions, "GL_EXT_texture_filter_anisotropic GL_KHR_debug");
8096    }
8097
8098    #[test]
8099    fn parses_embedded_and_desktop_gl_version_strings() {
8100        assert_eq!(
8101            parse_gl_version_info("OpenGL ES 2.0 Mesa 23.1.0"),
8102            Some(GlVersionInfo {
8103                is_gles: true,
8104                major: Some(2),
8105                minor: Some(0),
8106            })
8107        );
8108        assert_eq!(
8109            parse_gl_version_info("4.6 (Core Profile) Mesa 24.0.1"),
8110            Some(GlVersionInfo {
8111                is_gles: false,
8112                major: Some(4),
8113                minor: Some(6),
8114            })
8115        );
8116    }
8117
8118    #[test]
8119    fn falls_back_to_explicit_gles_enums_when_version_query_is_ambiguous() {
8120        assert_eq!(
8121            fallback_gl_version_info(HwContextType::OpenGlEs2),
8122            GlVersionInfo {
8123                is_gles: true,
8124                major: Some(2),
8125                minor: Some(0),
8126            }
8127        );
8128        assert_eq!(
8129            fallback_gl_version_info(HwContextType::OpenGl),
8130            GlVersionInfo::default()
8131        );
8132    }
8133
8134    #[test]
8135    fn ignores_non_positive_gl_limits() {
8136        assert_eq!(normalize_positive_gl_limit(-1), None);
8137        assert_eq!(normalize_positive_gl_limit(0), None);
8138        assert_eq!(normalize_positive_gl_limit(4096), Some(4096));
8139    }
8140
8141    #[test]
8142    fn blend_separate_helpers_use_typed_factors_and_equations() {
8143        let _guard = fake_gl_test_guard();
8144        let gl = glsym::fake_for_testing(FakeGlConfig::default());
8145
8146        gl.blend_color(0.1, 0.2, 0.3, 0.4).expect("blend color");
8147        gl.blend_func_separate(
8148            GlBlendFactor::SourceAlpha,
8149            GlBlendFactor::OneMinusSourceAlpha,
8150            GlBlendFactor::One,
8151            GlBlendFactor::OneMinusSourceAlpha,
8152        )
8153        .expect("blend func separate");
8154        gl.blend_equation_separate(GlBlendEquation::Add, GlBlendEquation::ReverseSubtract)
8155            .expect("blend equation separate");
8156
8157        let snapshot = glsym::snapshot_fake_state_for_testing();
8158        assert_eq!(
8159            snapshot.blend_color_calls,
8160            vec![FakeBlendColorCall {
8161                color: [
8162                    0.1f32.to_bits(),
8163                    0.2f32.to_bits(),
8164                    0.3f32.to_bits(),
8165                    0.4f32.to_bits(),
8166                ],
8167            }]
8168        );
8169        assert_eq!(
8170            snapshot.blend_func_separate_calls,
8171            vec![FakeBlendFuncSeparateCall {
8172                source_rgb: GlBlendFactor::SourceAlpha.as_raw(),
8173                destination_rgb: GlBlendFactor::OneMinusSourceAlpha.as_raw(),
8174                source_alpha: GlBlendFactor::One.as_raw(),
8175                destination_alpha: GlBlendFactor::OneMinusSourceAlpha.as_raw(),
8176            }]
8177        );
8178        assert_eq!(
8179            snapshot.blend_equation_separate_calls,
8180            vec![FakeBlendEquationSeparateCall {
8181                rgb: GlBlendEquation::Add.as_raw(),
8182                alpha: GlBlendEquation::ReverseSubtract.as_raw(),
8183            }]
8184        );
8185    }
8186
8187    #[test]
8188    fn fake_gl_reports_configured_limits_and_capabilities() {
8189        let _guard = fake_gl_test_guard();
8190        let gl = glsym::fake_for_testing(FakeGlConfig {
8191            context_type: HwContextType::OpenGl,
8192            version_info: GlVersionInfo {
8193                is_gles: false,
8194                major: Some(2),
8195                minor: Some(1),
8196            },
8197            version_string: "2.1 Fake".to_string(),
8198            max_texture_size: Some(2048),
8199            max_texture_image_units: Some(4),
8200            max_varying_vectors: Some(6),
8201            supports_vertex_arrays: true,
8202            supports_texture_arrays: false,
8203            supports_instancing: false,
8204            next_error: None,
8205            framebuffer_status: super::GL_FRAMEBUFFER_COMPLETE,
8206            ..FakeGlConfig::default()
8207        });
8208
8209        assert_eq!(gl.context_type(), HwContextType::OpenGl);
8210        assert_eq!(gl.max_texture_size(), Some(2048));
8211        assert_eq!(gl.max_texture_image_units(), Some(4));
8212        assert_eq!(gl.max_varying_vectors(), Some(6));
8213        assert!(gl.supports_vertex_arrays());
8214        let vertex_array = gl.gen_vertex_array().expect("fake VAO allocation");
8215        gl.bind_vertex_array(Some(vertex_array))
8216            .expect("fake VAO bind");
8217        gl.unbind_vertex_array().expect("fake VAO unbind");
8218        gl.delete_vertex_array(vertex_array)
8219            .expect("fake VAO delete");
8220        gl.clear_color(0.25, 0.5, 0.75, 1.0);
8221        gl.clear_color_buffer();
8222        let snapshot = glsym::snapshot_fake_state_for_testing();
8223        assert_eq!(snapshot.bound_vertex_array, 0);
8224        assert_eq!(
8225            snapshot.vertex_array_bindings,
8226            vec![vertex_array.as_raw(), 0]
8227        );
8228        assert_eq!(snapshot.deleted_vertex_arrays, vec![vertex_array.as_raw()]);
8229        assert_eq!(
8230            snapshot.clear_colors,
8231            vec![[
8232                0.25f32.to_bits(),
8233                0.5f32.to_bits(),
8234                0.75f32.to_bits(),
8235                1.0f32.to_bits()
8236            ]]
8237        );
8238        assert!(!gl.supports_texture_arrays());
8239        assert!(!gl.supports_instancing());
8240        assert!(gl.supports_npot_repeat());
8241    }
8242
8243    #[test]
8244    fn supports_npot_repeat_matches_es2_and_modern_context_classes() {
8245        let _guard = fake_gl_test_guard();
8246        let es2 = glsym::fake_for_testing(FakeGlConfig::default());
8247        assert!(!es2.supports_npot_repeat());
8248
8249        let es3 = glsym::fake_for_testing(FakeGlConfig {
8250            version_info: GlVersionInfo {
8251                is_gles: true,
8252                major: Some(3),
8253                minor: Some(0),
8254            },
8255            version_string: "OpenGL ES 3.0 Fake".to_string(),
8256            supports_texture_arrays: true,
8257            supports_instancing: true,
8258            ..FakeGlConfig::default()
8259        });
8260        assert!(es3.supports_npot_repeat());
8261    }
8262
8263    #[test]
8264    fn modern_feature_helpers_require_context_version_not_only_symbol_presence() {
8265        let _guard = fake_gl_test_guard();
8266        let es2_with_global_symbols = glsym::fake_for_testing(FakeGlConfig {
8267            supports_texture_arrays: true,
8268            supports_instancing: true,
8269            ..FakeGlConfig::default()
8270        });
8271        assert!(!es2_with_global_symbols.supports_texture_arrays());
8272        assert!(!es2_with_global_symbols.supports_instancing());
8273
8274        let desktop_21_with_global_symbols = glsym::fake_for_testing(FakeGlConfig {
8275            context_type: HwContextType::OpenGl,
8276            version_info: GlVersionInfo {
8277                is_gles: false,
8278                major: Some(2),
8279                minor: Some(1),
8280            },
8281            version_string: "2.1 Fake".to_string(),
8282            supports_vertex_arrays: true,
8283            supports_texture_arrays: true,
8284            supports_instancing: true,
8285            ..FakeGlConfig::default()
8286        });
8287        assert!(!desktop_21_with_global_symbols.supports_texture_arrays());
8288        assert!(!desktop_21_with_global_symbols.supports_instancing());
8289
8290        let desktop_33 = glsym::fake_for_testing(FakeGlConfig {
8291            context_type: HwContextType::OpenGl,
8292            version_info: GlVersionInfo {
8293                is_gles: false,
8294                major: Some(3),
8295                minor: Some(3),
8296            },
8297            version_string: "3.3 Fake".to_string(),
8298            supports_vertex_arrays: true,
8299            supports_texture_arrays: true,
8300            supports_instancing: true,
8301            ..FakeGlConfig::default()
8302        });
8303        assert!(desktop_33.supports_texture_arrays());
8304        assert!(desktop_33.supports_instancing());
8305    }
8306
8307    #[test]
8308    fn required_locations_reject_inactive_shader_inputs() {
8309        let _guard = fake_gl_test_guard();
8310        let gl = glsym::fake_for_testing(FakeGlConfig::default());
8311        let program = GlProgram::from_nonzero(1).unwrap();
8312
8313        assert_eq!(
8314            gl.required_attrib_location(program, "position"),
8315            Ok(GlVertexAttribLocation::ZERO)
8316        );
8317        assert_eq!(
8318            gl.required_uniform_location(program, "projection"),
8319            Ok(GlUniformLocation::from_raw(0).unwrap())
8320        );
8321        assert!(
8322            gl.required_attrib_location(program, "definitely_missing")
8323                .unwrap_err()
8324                .contains("required active attribute")
8325        );
8326        assert!(
8327            gl.required_uniform_location(program, "definitely_missing")
8328                .unwrap_err()
8329                .contains("required active uniform")
8330        );
8331        assert_eq!(gl.uniform_location(1, "definitely_missing").unwrap(), None);
8332        assert_eq!(
8333            gl.required_uniform_location(program, "projection").unwrap(),
8334            GlUniformLocation::from_raw(0).unwrap()
8335        );
8336        gl.uniform_2f(GlUniformLocation::from_raw(0).unwrap(), [320.0, 240.0]);
8337    }
8338
8339    #[test]
8340    fn typed_program_helpers_build_use_reflect_and_delete_programs() {
8341        let _guard = fake_gl_test_guard();
8342        let gl = glsym::fake_for_testing(FakeGlConfig::default());
8343        let compat = CompatGl::from_glsym(gl.clone());
8344
8345        let program = gl
8346            .build_program("attribute vec2 position; void main() {}", "void main() {}")
8347            .expect("program object");
8348        let compat_program = compat
8349            .build_program("attribute vec2 a_pos; void main() {}", "void main() {}")
8350            .expect("compat program object");
8351
8352        assert_eq!(
8353            gl.required_attrib_location(program, "position").unwrap(),
8354            GlVertexAttribLocation::ZERO
8355        );
8356        assert_eq!(
8357            compat
8358                .required_uniform_location(compat_program, "projection")
8359                .unwrap(),
8360            GlUniformLocation::from_raw(0).unwrap()
8361        );
8362
8363        gl.bind_attrib_location(program, GlVertexAttribLocation::from_index(2), "a_color")
8364            .expect("bind attribute location");
8365        assert!(
8366            gl.bind_attrib_location(program, GlVertexAttribLocation::ZERO, "bad\0name")
8367                .unwrap_err()
8368                .contains("interior NUL")
8369        );
8370        gl.use_program(Some(program));
8371        compat.use_program(Some(compat_program));
8372        compat.use_program(None);
8373        gl.delete_program(program);
8374        compat.delete_program(compat_program);
8375
8376        let snapshot = glsym::snapshot_fake_state_for_testing();
8377        assert_eq!(
8378            snapshot.use_program_calls,
8379            vec![program.as_raw(), compat_program.as_raw(), 0]
8380        );
8381        assert_eq!(
8382            snapshot.deleted_programs,
8383            vec![program.as_raw(), compat_program.as_raw()]
8384        );
8385        assert_eq!(
8386            snapshot.bind_attrib_location_calls,
8387            vec![FakeBindAttribLocationCall {
8388                program: program.as_raw(),
8389                location: 2,
8390                name: "a_color".to_string(),
8391            }]
8392        );
8393    }
8394
8395    #[test]
8396    fn unified_gl_keeps_clear_path_when_optional_symbols_are_unavailable() {
8397        let _guard = fake_gl_test_guard();
8398        let raw_gl = glsym::fake_for_testing(FakeGlConfig::default());
8399        let gl = Gl {
8400            clear: CompatGlClear::from_glsym(raw_gl),
8401            compat: None,
8402            texture: None,
8403            full: None,
8404        };
8405
8406        assert!(!gl.supports_shader_pipeline());
8407        assert!(!gl.supports_textures());
8408        gl.clear_color(0.1, 0.2, 0.3, 1.0);
8409        gl.clear_color_buffer();
8410        gl.enable(GlCapability::Blend);
8411        gl.disable(GlCapability::Blend);
8412        gl.use_no_program();
8413        gl.unbind_buffer(GlBufferTarget::ArrayBuffer);
8414        gl.unbind_texture(GlTextureTarget::Texture2D);
8415        gl.delete_texture(GlTexture(99));
8416
8417        assert!(
8418            gl.build_program("void main() {}", "void main() {}")
8419                .unwrap_err()
8420                .contains("shader and buffer drawing")
8421        );
8422        assert!(gl.gen_texture().unwrap_err().contains("texture rendering"));
8423
8424        let snapshot = glsym::snapshot_fake_state_for_testing();
8425        assert_eq!(snapshot.clear_colors.len(), 1);
8426        assert!(snapshot.use_program_calls.is_empty());
8427        assert!(snapshot.deleted_textures.is_empty());
8428    }
8429
8430    #[test]
8431    fn typed_uniform_setters_use_locations_and_fixed_size_values() {
8432        let _guard = fake_gl_test_guard();
8433        let gl = glsym::fake_for_testing(FakeGlConfig::default());
8434        let color = gl.required_uniform(1, "projection").unwrap();
8435
8436        gl.uniform_1i(color, 7);
8437        gl.uniform_4fv(color, &[1.0, 0.5, 0.25, 0.125]);
8438        gl.uniform_3f(color, [1.0, 0.5, 0.25]);
8439        gl.uniform_4f(color, [1.0, 0.5, 0.25, 0.125]);
8440        gl.uniform_matrix_3fv(color, true, &[1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 2.0, 3.0, 1.0]);
8441
8442        let snapshot = glsym::snapshot_fake_state_for_testing();
8443        assert_eq!(
8444            snapshot.uniform_1i_calls,
8445            vec![FakeUniformIntCall {
8446                location: color.as_raw(),
8447                value: 7,
8448            }]
8449        );
8450        assert_eq!(snapshot.uniform_4fv_calls.len(), 1);
8451        assert_eq!(
8452            snapshot.uniform_4fv_calls[0].values,
8453            [
8454                1.0_f32.to_bits(),
8455                0.5_f32.to_bits(),
8456                0.25_f32.to_bits(),
8457                0.125_f32.to_bits(),
8458            ]
8459        );
8460        assert_eq!(snapshot.uniform_3f_calls.len(), 1);
8461        assert_eq!(snapshot.uniform_3f_calls[0].location, color.as_raw());
8462        assert_eq!(
8463            snapshot.uniform_3f_calls[0].values,
8464            [1.0_f32.to_bits(), 0.5_f32.to_bits(), 0.25_f32.to_bits(), 0]
8465        );
8466        assert_eq!(snapshot.uniform_4f_calls.len(), 1);
8467        assert_eq!(
8468            snapshot.uniform_4f_calls[0].values,
8469            [
8470                1.0_f32.to_bits(),
8471                0.5_f32.to_bits(),
8472                0.25_f32.to_bits(),
8473                0.125_f32.to_bits(),
8474            ]
8475        );
8476        assert_eq!(snapshot.uniform_matrix_3fv_calls.len(), 1);
8477        assert!(snapshot.uniform_matrix_3fv_calls[0].transpose);
8478        assert_eq!(
8479            snapshot.uniform_matrix_3fv_calls[0].location,
8480            color.as_raw()
8481        );
8482    }
8483
8484    #[test]
8485    fn compat_uniform_helpers_use_typed_locations() {
8486        let _guard = fake_gl_test_guard();
8487        let raw_gl = glsym::fake_for_testing(FakeGlConfig::default());
8488        let gl = CompatGl::from_glsym(raw_gl.clone());
8489        let text_gl = super::CompatTextureGl::from_glsym(raw_gl);
8490
8491        let color = gl.required_uniform(1, "u_color").unwrap();
8492        let font = text_gl.required_uniform(1, "u_font").unwrap();
8493        gl.uniform_4fv(color, &[0.25, 0.5, 0.75, 1.0]);
8494        text_gl.uniform_1i(font, 0);
8495        text_gl.uniform_4fv(color, &[1.0, 0.75, 0.5, 0.25]);
8496
8497        let snapshot = glsym::snapshot_fake_state_for_testing();
8498        assert_eq!(
8499            snapshot.uniform_1i_calls,
8500            vec![FakeUniformIntCall {
8501                location: font.as_raw(),
8502                value: 0,
8503            }]
8504        );
8505        assert_eq!(snapshot.uniform_4fv_calls.len(), 2);
8506        assert_eq!(snapshot.uniform_4fv_calls[0].location, color.as_raw());
8507        assert_eq!(
8508            snapshot.uniform_4fv_calls[1].values,
8509            [
8510                1.0_f32.to_bits(),
8511                0.75_f32.to_bits(),
8512                0.5_f32.to_bits(),
8513                0.25_f32.to_bits(),
8514            ]
8515        );
8516    }
8517
8518    #[test]
8519    fn typed_vertex_attrib_helpers_use_locations() {
8520        let _guard = fake_gl_test_guard();
8521        let gl = glsym::fake_for_testing(FakeGlConfig::default());
8522        let compat = CompatGl::from_glsym(gl.clone());
8523
8524        let position = gl.required_attrib(1, "a_pos").unwrap();
8525        assert_eq!(position, GlVertexAttribLocation::ZERO);
8526        let color = compat.required_attrib(1, "a_color").unwrap();
8527        assert_eq!(color.as_raw(), 3);
8528
8529        gl.enable_vertex_attrib(position);
8530        gl.vertex_attrib_pointer_f32(
8531            position,
8532            GlVertexAttribF32Layout::interleaved(GlVertexAttribF32Components::Two, 5).unwrap(),
8533        );
8534        compat.enable_vertex_attrib(color);
8535        compat.vertex_attrib_pointer_f32(
8536            color,
8537            GlVertexAttribF32Layout::interleaved(GlVertexAttribF32Components::Three, 5)
8538                .unwrap()
8539                .with_offset_components(GlVertexAttribF32Components::Two),
8540        );
8541        compat.disable_vertex_attrib(color);
8542
8543        let snapshot = glsym::snapshot_fake_state_for_testing();
8544        assert_eq!(snapshot.enabled_vertex_attribs, vec![position.as_raw()]);
8545        assert_eq!(
8546            snapshot.vertex_attrib_pointer_calls,
8547            vec![
8548                FakeVertexAttribPointerCall {
8549                    index: position.as_raw(),
8550                    size: 2,
8551                    type_: GL_FLOAT,
8552                    normalized: false,
8553                    stride: 20,
8554                    offset: 0,
8555                },
8556                FakeVertexAttribPointerCall {
8557                    index: color.as_raw(),
8558                    size: 3,
8559                    type_: GL_FLOAT,
8560                    normalized: false,
8561                    stride: 20,
8562                    offset: 8,
8563                },
8564            ]
8565        );
8566        assert!(
8567            GlVertexAttribStride::from_f32_count((i32::MAX as usize / mem::size_of::<f32>()) + 1)
8568                .is_err()
8569        );
8570    }
8571
8572    #[test]
8573    fn draw_range_checks_glsizei_limits() {
8574        let _guard = fake_gl_test_guard();
8575        let gl = glsym::fake_for_testing(FakeGlConfig {
8576            supports_instancing: true,
8577            ..FakeGlConfig::default()
8578        });
8579        let compat = CompatGl::from_glsym(gl.clone());
8580
8581        gl.draw_arrays(GlDrawMode::Triangles, GlDrawRange::from_start(3))
8582            .expect("draw arrays range");
8583        compat
8584            .draw_arrays(GlDrawMode::Triangles, GlDrawRange::new(1, 2))
8585            .expect("compat draw arrays range");
8586        gl.draw_arrays_instanced(
8587            GlDrawMode::Triangles,
8588            GlDrawRange::new(2, 3),
8589            GlInstanceCount::new(4),
8590        )
8591        .expect("instanced draw arrays range");
8592        gl.draw_elements(
8593            GlDrawMode::Triangles,
8594            GlIndexType::UnsignedShort,
8595            GlElementRange::new(
8596                6,
8597                GlElementByteOffset::from_indices(GlIndexType::UnsignedShort, 3).unwrap(),
8598            ),
8599        )
8600        .expect("draw elements range");
8601        assert!(gl.supports_draw_range_elements());
8602        gl.draw_range_elements(
8603            GlDrawMode::Triangles,
8604            GlElementVertexRange::new(2, 7).unwrap(),
8605            GlIndexType::UnsignedShort,
8606            GlElementRange::new(
8607                5,
8608                GlElementByteOffset::from_indices(GlIndexType::UnsignedShort, 4).unwrap(),
8609            ),
8610        )
8611        .expect("draw range elements range");
8612        gl.draw_elements_instanced(
8613            GlDrawMode::Triangles,
8614            GlIndexType::UnsignedShort,
8615            GlElementRange::from_start(6),
8616            GlInstanceCount::new(2),
8617        )
8618        .expect("instanced draw elements range");
8619
8620        assert!(
8621            gl.draw_arrays(
8622                GlDrawMode::Triangles,
8623                GlDrawRange::new(0, i32::MAX as u32 + 1),
8624            )
8625            .unwrap_err()
8626            .contains("vertex count")
8627        );
8628        assert!(
8629            gl.draw_arrays_instanced(
8630                GlDrawMode::Triangles,
8631                GlDrawRange::from_start(1),
8632                GlInstanceCount::new(i32::MAX as u32 + 1),
8633            )
8634            .unwrap_err()
8635            .contains("instance count")
8636        );
8637        assert!(
8638            gl.draw_elements(
8639                GlDrawMode::Triangles,
8640                GlIndexType::UnsignedShort,
8641                GlElementRange::from_start(i32::MAX as u32 + 1),
8642            )
8643            .unwrap_err()
8644            .contains("index count")
8645        );
8646        assert!(
8647            gl.draw_elements_instanced(
8648                GlDrawMode::Triangles,
8649                GlIndexType::UnsignedShort,
8650                GlElementRange::from_start(1),
8651                GlInstanceCount::new(i32::MAX as u32 + 1),
8652            )
8653            .unwrap_err()
8654            .contains("instance count")
8655        );
8656        assert!(
8657            GlElementVertexRange::new(8, 2)
8658                .unwrap_err()
8659                .contains("exceeds end")
8660        );
8661
8662        let snapshot = glsym::snapshot_fake_state_for_testing();
8663        assert_eq!(snapshot.draw_arrays_calls, 3);
8664        assert_eq!(
8665            snapshot.draw_arrays_call_args,
8666            vec![
8667                FakeDrawArraysCall {
8668                    mode: GlDrawMode::Triangles.as_raw(),
8669                    first: 0,
8670                    count: 3,
8671                    instance_count: None,
8672                },
8673                FakeDrawArraysCall {
8674                    mode: GlDrawMode::Triangles.as_raw(),
8675                    first: 1,
8676                    count: 2,
8677                    instance_count: None,
8678                },
8679                FakeDrawArraysCall {
8680                    mode: GlDrawMode::Triangles.as_raw(),
8681                    first: 2,
8682                    count: 3,
8683                    instance_count: Some(4),
8684                },
8685            ]
8686        );
8687        assert_eq!(snapshot.draw_elements_calls, 2);
8688        assert_eq!(snapshot.draw_elements_instanced_calls, 1);
8689        assert_eq!(
8690            snapshot.draw_elements_call_args,
8691            vec![
8692                FakeDrawElementsCall {
8693                    mode: GlDrawMode::Triangles.as_raw(),
8694                    vertex_range: None,
8695                    count: 6,
8696                    type_: GlIndexType::UnsignedShort.as_raw(),
8697                    offset: 6,
8698                    instance_count: None,
8699                },
8700                FakeDrawElementsCall {
8701                    mode: GlDrawMode::Triangles.as_raw(),
8702                    vertex_range: Some((2, 7)),
8703                    count: 5,
8704                    type_: GlIndexType::UnsignedShort.as_raw(),
8705                    offset: 8,
8706                    instance_count: None,
8707                },
8708                FakeDrawElementsCall {
8709                    mode: GlDrawMode::Triangles.as_raw(),
8710                    vertex_range: None,
8711                    count: 6,
8712                    type_: GlIndexType::UnsignedShort.as_raw(),
8713                    offset: 0,
8714                    instance_count: Some(2),
8715                },
8716            ]
8717        );
8718    }
8719
8720    #[test]
8721    fn checked_gl_calls_report_errors_and_incomplete_framebuffers() {
8722        let _guard = fake_gl_test_guard();
8723        let gl = glsym::fake_for_testing(FakeGlConfig {
8724            next_error: Some(super::GL_INVALID_OPERATION),
8725            ..FakeGlConfig::default()
8726        });
8727        let error = gl.check_no_error("unit test draw").unwrap_err();
8728        assert!(error.contains("unit test draw"));
8729        assert!(error.contains("GL_INVALID_OPERATION"));
8730
8731        let gl = glsym::fake_for_testing(FakeGlConfig {
8732            framebuffer_status: super::GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
8733            ..FakeGlConfig::default()
8734        });
8735        let error = gl
8736            .bind_framebuffer(GlFramebufferTarget::Framebuffer, GlFramebuffer::from_raw(7))
8737            .unwrap_err();
8738        assert!(error.contains("incomplete framebuffer"));
8739        assert!(error.contains("GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"));
8740    }
8741
8742    #[test]
8743    fn shader_source_errors_delete_unowned_shader_objects() {
8744        let _guard = fake_gl_test_guard();
8745        let compat = CompatGl::fake_for_testing(FakeGlConfig::default());
8746
8747        let error = compat
8748            .compile_shader_source(GlShaderStage::Vertex, "void\0main")
8749            .unwrap_err();
8750
8751        assert!(error.contains("interior NUL"));
8752        let snapshot = glsym::snapshot_fake_state_for_testing();
8753        assert_eq!(snapshot.deleted_shaders, vec![1]);
8754
8755        let gl = glsym::fake_for_testing(FakeGlConfig::default());
8756        let error = gl
8757            .compile_shader_source(GlShaderStage::Fragment, "void\0main")
8758            .unwrap_err();
8759
8760        assert!(error.contains("interior NUL"));
8761        let snapshot = glsym::snapshot_fake_state_for_testing();
8762        assert_eq!(snapshot.deleted_shaders, vec![1]);
8763    }
8764
8765    #[test]
8766    fn typed_shader_helpers_use_stage_enums_and_shader_handles() {
8767        let _guard = fake_gl_test_guard();
8768        let raw_gl = glsym::fake_for_testing(FakeGlConfig::default());
8769        let compat = CompatGl::from_glsym(raw_gl.clone());
8770
8771        let vertex = compat
8772            .compile_shader_source(GlShaderStage::Vertex, "void main() {}")
8773            .expect("compat vertex shader");
8774        let fragment = raw_gl
8775            .compile_shader_source(GlShaderStage::Fragment, "void main() {}")
8776            .expect("fragment shader");
8777        let program = raw_gl.create_program().expect("program object");
8778        raw_gl.attach_shader(program, vertex);
8779        raw_gl.link_program(program).expect("link program");
8780
8781        raw_gl.delete_shader(fragment);
8782        compat.delete_shader(vertex);
8783        raw_gl.delete_program(program);
8784
8785        let snapshot = glsym::snapshot_fake_state_for_testing();
8786        assert_eq!(
8787            snapshot.created_shaders,
8788            vec![
8789                FakeCreateShaderCall {
8790                    stage: GlShaderStage::Vertex.as_raw(),
8791                    shader: vertex.as_raw(),
8792                },
8793                FakeCreateShaderCall {
8794                    stage: GlShaderStage::Fragment.as_raw(),
8795                    shader: fragment.as_raw(),
8796                },
8797            ]
8798        );
8799        assert_eq!(
8800            snapshot.deleted_shaders,
8801            vec![fragment.as_raw(), vertex.as_raw()]
8802        );
8803        assert_eq!(
8804            snapshot.attached_shaders,
8805            vec![FakeAttachShaderCall {
8806                program: program.as_raw(),
8807                shader: vertex.as_raw(),
8808            }]
8809        );
8810        assert_eq!(snapshot.linked_programs, vec![program.as_raw()]);
8811        assert_eq!(snapshot.deleted_programs, vec![program.as_raw()]);
8812    }
8813
8814    #[test]
8815    fn fake_gl_snapshot_tracks_texture_upload_formats() {
8816        let _guard = fake_gl_test_guard();
8817        let gl = glsym::fake_for_testing(FakeGlConfig::default());
8818        unsafe {
8819            super::fake_gl_tex_image_2d(
8820                super::GlTextureTarget::Texture2D.as_raw(),
8821                0,
8822                GlTextureInternalFormat::Luminance.as_raw() as i32,
8823                32,
8824                16,
8825                0,
8826                GlTextureFormat::Luminance.as_raw(),
8827                super::GL_UNSIGNED_BYTE,
8828                std::ptr::null(),
8829            );
8830        }
8831        gl.tex_image_2d(
8832            GlTextureTarget::Texture2D,
8833            GlTextureInternalFormat::Rgba,
8834            GlTextureLevel::ZERO,
8835            GlTextureSize2D::new(4, 2),
8836            GlTextureFormat::Rgba,
8837            GlTextureDataType::UnsignedByte,
8838            None,
8839        )
8840        .expect("sized texture upload");
8841        assert!(
8842            gl.tex_image_2d(
8843                GlTextureTarget::Texture2D,
8844                GlTextureInternalFormat::Rgba,
8845                GlTextureLevel::ZERO,
8846                GlTextureSize2D::new(u32::MAX, 2),
8847                GlTextureFormat::Rgba,
8848                GlTextureDataType::UnsignedByte,
8849                None,
8850            )
8851            .unwrap_err()
8852            .contains("glTexImage2D width")
8853        );
8854
8855        let snapshot = glsym::snapshot_fake_state_for_testing();
8856        assert_eq!(snapshot.texture_uploads_2d.len(), 2);
8857        assert_eq!(
8858            snapshot.texture_uploads_2d[0].internal_format,
8859            GlTextureInternalFormat::Luminance.as_raw()
8860        );
8861        assert_eq!(
8862            snapshot.texture_uploads_2d[0].format,
8863            GlTextureFormat::Luminance.as_raw()
8864        );
8865        assert_eq!(snapshot.texture_uploads_2d[0].width, 32);
8866        assert_eq!(snapshot.texture_uploads_2d[0].height, 16);
8867        assert_eq!(snapshot.texture_uploads_2d[1].width, 4);
8868        assert_eq!(snapshot.texture_uploads_2d[1].height, 2);
8869    }
8870
8871    #[test]
8872    fn texture_parameter_helpers_use_typed_filters_and_wraps() {
8873        let _guard = fake_gl_test_guard();
8874        let gl = glsym::fake_for_testing(FakeGlConfig::default());
8875
8876        gl.tex_min_filter(
8877            GlTextureTarget::Texture2D,
8878            GlTextureMinFilter::LinearMipmapLinear,
8879        );
8880        gl.tex_mag_filter(GlTextureTarget::Texture2D, GlTextureMagFilter::Linear);
8881        gl.tex_wrap_s(GlTextureTarget::Texture2D, GlTextureWrap::ClampToEdge);
8882        gl.tex_wrap_t(GlTextureTarget::Texture2D, GlTextureWrap::Repeat);
8883        gl.tex_wrap_r(GlTextureTarget::Texture2DArray, GlTextureWrap::ClampToEdge);
8884
8885        let snapshot = glsym::snapshot_fake_state_for_testing();
8886        assert_eq!(
8887            snapshot.texture_parameter_calls,
8888            vec![
8889                super::FakeTextureParameterCall {
8890                    target: GlTextureTarget::Texture2D.as_raw(),
8891                    parameter: super::GL_TEXTURE_MIN_FILTER,
8892                    value: GlTextureMinFilter::LinearMipmapLinear.as_raw() as i32,
8893                },
8894                super::FakeTextureParameterCall {
8895                    target: GlTextureTarget::Texture2D.as_raw(),
8896                    parameter: super::GL_TEXTURE_MAG_FILTER,
8897                    value: GlTextureMagFilter::Linear.as_raw() as i32,
8898                },
8899                super::FakeTextureParameterCall {
8900                    target: GlTextureTarget::Texture2D.as_raw(),
8901                    parameter: super::GL_TEXTURE_WRAP_S,
8902                    value: GlTextureWrap::ClampToEdge.as_raw() as i32,
8903                },
8904                super::FakeTextureParameterCall {
8905                    target: GlTextureTarget::Texture2D.as_raw(),
8906                    parameter: super::GL_TEXTURE_WRAP_T,
8907                    value: GlTextureWrap::Repeat.as_raw() as i32,
8908                },
8909                super::FakeTextureParameterCall {
8910                    target: GlTextureTarget::Texture2DArray.as_raw(),
8911                    parameter: super::GL_TEXTURE_WRAP_R,
8912                    value: GlTextureWrap::ClampToEdge.as_raw() as i32,
8913                },
8914            ]
8915        );
8916    }
8917
8918    #[test]
8919    fn texture_sub_image_2d_uses_typed_offset_size_and_formats() {
8920        let _guard = fake_gl_test_guard();
8921        let gl = glsym::fake_for_testing(FakeGlConfig::default());
8922        let pixels = [255_u8; 16];
8923
8924        gl.tex_sub_image_2d(
8925            GlTextureTarget::Texture2D,
8926            GlTextureLevel::ZERO,
8927            GlTextureOffset2D::new(4, 8),
8928            GlTextureSize2D::new(2, 2),
8929            GlTextureFormat::Rgba,
8930            GlTextureDataType::UnsignedByte,
8931            &pixels,
8932        )
8933        .expect("texture sub image");
8934
8935        let snapshot = glsym::snapshot_fake_state_for_testing();
8936        assert_eq!(snapshot.texture_sub_images_2d.len(), 1);
8937        let upload = snapshot.texture_sub_images_2d[0];
8938        assert_eq!(upload.target, GlTextureTarget::Texture2D.as_raw());
8939        assert_eq!(upload.level, 0);
8940        assert_eq!(upload.x, 4);
8941        assert_eq!(upload.y, 8);
8942        assert_eq!(upload.width, 2);
8943        assert_eq!(upload.height, 2);
8944        assert_eq!(upload.format, GlTextureFormat::Rgba.as_raw());
8945        assert!(upload.has_pixels);
8946    }
8947
8948    #[test]
8949    fn read_pixels_uses_typed_rectangles_formats_and_mut_slices() {
8950        let _guard = fake_gl_test_guard();
8951        let gl = glsym::fake_for_testing(FakeGlConfig::default());
8952        let mut pixels = [0_u8; 16];
8953
8954        gl.read_buffer(GlFramebufferBuffer::ColorAttachment(1))
8955            .expect("read buffer");
8956        gl.draw_buffers(&[
8957            GlFramebufferBuffer::ColorAttachment(0),
8958            GlFramebufferBuffer::ColorAttachment(1),
8959        ])
8960        .expect("draw buffers");
8961        gl.read_pixels(GlRect::new(2, 4, 2, 2), GlTextureFormat::Rgba, &mut pixels)
8962            .expect("read pixels");
8963        assert!(pixels.iter().all(|byte| *byte == 0xA5));
8964
8965        assert!(
8966            gl.read_pixels(GlRect::new(0, 0, 2, 2), GlTextureFormat::Rgb, &mut [0; 11])
8967                .unwrap_err()
8968                .contains("requires 12 destination byte")
8969        );
8970
8971        let snapshot = glsym::snapshot_fake_state_for_testing();
8972        assert_eq!(
8973            snapshot.read_buffer_calls,
8974            vec![GlFramebufferBuffer::ColorAttachment(1).as_raw().unwrap()]
8975        );
8976        assert_eq!(
8977            snapshot.draw_buffers_calls,
8978            vec![vec![
8979                GlFramebufferBuffer::ColorAttachment(0).as_raw().unwrap(),
8980                GlFramebufferBuffer::ColorAttachment(1).as_raw().unwrap(),
8981            ]]
8982        );
8983        assert_eq!(
8984            snapshot.read_pixels_calls,
8985            vec![super::FakeReadPixelsCall {
8986                x: 2,
8987                y: 4,
8988                width: 2,
8989                height: 2,
8990                format: GlTextureFormat::Rgba.as_raw(),
8991                type_: super::GL_UNSIGNED_BYTE,
8992            }]
8993        );
8994    }
8995
8996    #[test]
8997    fn buffer_sub_data_uses_typed_byte_offsets_and_slice_lengths() {
8998        let _guard = fake_gl_test_guard();
8999        let gl = glsym::fake_for_testing(FakeGlConfig::default());
9000        let compat = CompatGl::from_glsym(gl.clone());
9001        let vertices = [1.0_f32, 2.0, 3.0, 4.0];
9002
9003        let buffer = gl.gen_buffer().expect("buffer object");
9004        gl.bind_buffer(GlBufferTarget::ArrayBuffer, Some(buffer));
9005        gl.buffer_data(
9006            GlBufferTarget::ArrayBuffer,
9007            &vertices,
9008            GlBufferUsage::StaticDraw,
9009        )
9010        .expect("buffer data");
9011        compat.bind_buffer(GlBufferTarget::ArrayBuffer, None);
9012        gl.buffer_data_empty(
9013            GlBufferTarget::ElementArrayBuffer,
9014            GlBufferByteSize::from_bytes(64),
9015            GlBufferUsage::DynamicDraw,
9016        )
9017        .expect("empty buffer allocation");
9018        gl.buffer_sub_data(
9019            GlBufferTarget::ArrayBuffer,
9020            GlBufferByteOffset::from_bytes(8),
9021            &vertices,
9022        )
9023        .expect("buffer sub data");
9024        compat.delete_buffer(buffer);
9025
9026        let snapshot = glsym::snapshot_fake_state_for_testing();
9027        assert_eq!(snapshot.bound_array_buffer, 0);
9028        assert_eq!(snapshot.deleted_buffers, vec![buffer.as_raw()]);
9029        assert_eq!(
9030            snapshot.buffer_data_uploads,
9031            vec![
9032                FakeBufferDataCall {
9033                    target: GlBufferTarget::ArrayBuffer.as_raw(),
9034                    byte_len: 16,
9035                    usage: GlBufferUsage::StaticDraw.as_raw(),
9036                    has_data: true,
9037                },
9038                FakeBufferDataCall {
9039                    target: GlBufferTarget::ElementArrayBuffer.as_raw(),
9040                    byte_len: 64,
9041                    usage: GlBufferUsage::DynamicDraw.as_raw(),
9042                    has_data: false,
9043                },
9044            ]
9045        );
9046        assert_eq!(
9047            snapshot.buffer_sub_data_calls,
9048            vec![FakeBufferSubDataCall {
9049                target: GlBufferTarget::ArrayBuffer.as_raw(),
9050                offset: 8,
9051                byte_len: 16,
9052            }]
9053        );
9054    }
9055
9056    #[test]
9057    fn buffer_copy_uses_typed_targets_offsets_and_sizes() {
9058        let _guard = fake_gl_test_guard();
9059        let gl = glsym::fake_for_testing(FakeGlConfig::default());
9060
9061        gl.copy_buffer_sub_data(
9062            GlBufferTarget::CopyReadBuffer,
9063            GlBufferTarget::CopyWriteBuffer,
9064            GlBufferByteOffset::from_bytes(8),
9065            GlBufferByteOffset::from_bytes(24),
9066            GlBufferByteSize::from_bytes(32),
9067        )
9068        .expect("copy buffer sub data");
9069
9070        assert!(
9071            gl.copy_buffer_sub_data(
9072                GlBufferTarget::CopyReadBuffer,
9073                GlBufferTarget::CopyWriteBuffer,
9074                GlBufferByteOffset::from_bytes(0),
9075                GlBufferByteOffset::from_bytes(0),
9076                GlBufferByteSize::from_bytes(usize::MAX),
9077            )
9078            .unwrap_err()
9079            .contains("glCopyBufferSubData byte length")
9080        );
9081
9082        let snapshot = glsym::snapshot_fake_state_for_testing();
9083        assert_eq!(
9084            snapshot.copy_buffer_sub_data_calls,
9085            vec![FakeCopyBufferSubDataCall {
9086                read_target: GlBufferTarget::CopyReadBuffer.as_raw(),
9087                write_target: GlBufferTarget::CopyWriteBuffer.as_raw(),
9088                read_offset: 8,
9089                write_offset: 24,
9090                byte_len: 32,
9091            }]
9092        );
9093    }
9094
9095    #[test]
9096    fn indexed_buffer_bindings_use_typed_targets_indices_and_ranges() {
9097        let _guard = fake_gl_test_guard();
9098        let gl = glsym::fake_for_testing(FakeGlConfig::default());
9099        assert!(gl.supports_indexed_buffer_bindings());
9100
9101        let buffer = gl.gen_buffer().expect("buffer object");
9102        let index = GlBufferBindingIndex::from_index(2);
9103        gl.bind_buffer_base(GlIndexedBufferTarget::UniformBuffer, index, Some(buffer))
9104            .expect("bind buffer base");
9105        gl.bind_buffer_range(
9106            GlIndexedBufferTarget::TransformFeedbackBuffer,
9107            GlBufferBindingIndex::ZERO,
9108            Some(buffer),
9109            GlBufferRange::new(
9110                GlBufferByteOffset::from_bytes(16),
9111                GlBufferByteSize::from_bytes(32),
9112            ),
9113        )
9114        .expect("bind buffer range");
9115        gl.bind_buffer_base(GlIndexedBufferTarget::UniformBuffer, index, None)
9116            .expect("unbind buffer base");
9117
9118        assert!(
9119            gl.bind_buffer_range(
9120                GlIndexedBufferTarget::TransformFeedbackBuffer,
9121                GlBufferBindingIndex::ZERO,
9122                Some(buffer),
9123                GlBufferRange::from_start(GlBufferByteSize::from_bytes(usize::MAX)),
9124            )
9125            .unwrap_err()
9126            .contains("glBindBufferRange byte length")
9127        );
9128
9129        let snapshot = glsym::snapshot_fake_state_for_testing();
9130        assert_eq!(
9131            snapshot.bind_buffer_base_calls,
9132            vec![
9133                FakeBindBufferBaseCall {
9134                    target: GlIndexedBufferTarget::UniformBuffer.as_raw(),
9135                    index: 2,
9136                    buffer: buffer.as_raw(),
9137                },
9138                FakeBindBufferBaseCall {
9139                    target: GlIndexedBufferTarget::UniformBuffer.as_raw(),
9140                    index: 2,
9141                    buffer: 0,
9142                },
9143            ]
9144        );
9145        assert_eq!(
9146            snapshot.bind_buffer_range_calls,
9147            vec![FakeBindBufferRangeCall {
9148                target: GlIndexedBufferTarget::TransformFeedbackBuffer.as_raw(),
9149                index: 0,
9150                buffer: buffer.as_raw(),
9151                offset: 16,
9152                size: 32,
9153            }]
9154        );
9155    }
9156
9157    #[test]
9158    fn checked_buffer_allocation_rejects_lengths_that_exceed_gl_sizeiptr() {
9159        let _guard = fake_gl_test_guard();
9160        let gl = glsym::fake_for_testing(FakeGlConfig::default());
9161
9162        assert!(
9163            gl.buffer_data_empty(
9164                GlBufferTarget::ArrayBuffer,
9165                GlBufferByteSize::from_bytes(usize::MAX),
9166                GlBufferUsage::StreamDraw,
9167            )
9168            .unwrap_err()
9169            .contains("GL buffer allocation byte length")
9170        );
9171
9172        let snapshot = glsym::snapshot_fake_state_for_testing();
9173        assert!(snapshot.buffer_data_uploads.is_empty());
9174    }
9175
9176    #[test]
9177    fn framebuffer_and_renderbuffer_helpers_use_typed_targets_and_attachments() {
9178        let _guard = fake_gl_test_guard();
9179        let gl = glsym::fake_for_testing(FakeGlConfig::default());
9180
9181        assert!(gl.supports_framebuffer_objects());
9182        assert!(gl.supports_renderbuffers());
9183
9184        let framebuffer = gl.gen_framebuffer().expect("framebuffer");
9185        let renderbuffer = gl.gen_renderbuffer().expect("renderbuffer");
9186
9187        gl.bind_framebuffer(GlFramebufferTarget::Framebuffer, Some(framebuffer))
9188            .expect("bind framebuffer");
9189        gl.bind_renderbuffer(GlRenderbufferTarget::Renderbuffer, Some(renderbuffer))
9190            .expect("bind renderbuffer");
9191        gl.renderbuffer_storage(
9192            GlRenderbufferTarget::Renderbuffer,
9193            GlRenderbufferInternalFormat::DepthComponent16,
9194            GlRenderbufferSize::new(320, 240),
9195        )
9196        .expect("renderbuffer storage");
9197        gl.framebuffer_renderbuffer(
9198            GlFramebufferTarget::Framebuffer,
9199            GlFramebufferAttachment::Depth,
9200            GlRenderbufferTarget::Renderbuffer,
9201            Some(renderbuffer),
9202        )
9203        .expect("framebuffer renderbuffer");
9204        gl.framebuffer_texture_2d(
9205            GlFramebufferTarget::Framebuffer,
9206            GlFramebufferAttachment::Color(0),
9207            GlFramebufferTexture2DTarget::Texture2D,
9208            Some(GlTexture::from_nonzero(99).unwrap()),
9209            GlTextureLevel::ZERO,
9210        )
9211        .expect("framebuffer texture");
9212        gl.delete_framebuffer(framebuffer)
9213            .expect("delete framebuffer");
9214        gl.delete_renderbuffer(renderbuffer)
9215            .expect("delete renderbuffer");
9216
9217        let snapshot = glsym::snapshot_fake_state_for_testing();
9218        assert_eq!(snapshot.generated_framebuffers, vec![framebuffer.as_raw()]);
9219        assert_eq!(snapshot.deleted_framebuffers, vec![framebuffer.as_raw()]);
9220        assert_eq!(snapshot.framebuffer_bindings, vec![framebuffer.as_raw()]);
9221        assert_eq!(
9222            snapshot.generated_renderbuffers,
9223            vec![renderbuffer.as_raw()]
9224        );
9225        assert_eq!(snapshot.deleted_renderbuffers, vec![renderbuffer.as_raw()]);
9226        assert_eq!(snapshot.renderbuffer_bindings, vec![renderbuffer.as_raw()]);
9227        assert_eq!(
9228            snapshot.renderbuffer_storage_calls,
9229            vec![super::FakeRenderbufferStorageCall {
9230                target: GlRenderbufferTarget::Renderbuffer.as_raw(),
9231                internal_format: GlRenderbufferInternalFormat::DepthComponent16.as_raw(),
9232                width: 320,
9233                height: 240,
9234            }]
9235        );
9236        assert_eq!(
9237            snapshot.framebuffer_renderbuffer_calls,
9238            vec![super::FakeFramebufferRenderbufferCall {
9239                target: GlFramebufferTarget::Framebuffer.as_raw(),
9240                attachment: GlFramebufferAttachment::Depth.as_raw().unwrap(),
9241                renderbuffer_target: GlRenderbufferTarget::Renderbuffer.as_raw(),
9242                renderbuffer: renderbuffer.as_raw(),
9243            }]
9244        );
9245        assert_eq!(
9246            snapshot.framebuffer_texture_2d_calls,
9247            vec![super::FakeFramebufferTexture2DCall {
9248                target: GlFramebufferTarget::Framebuffer.as_raw(),
9249                attachment: GlFramebufferAttachment::Color(0).as_raw().unwrap(),
9250                texture_target: GlFramebufferTexture2DTarget::Texture2D.as_raw(),
9251                texture: 99,
9252                level: 0,
9253            }]
9254        );
9255    }
9256
9257    #[test]
9258    fn framebuffer_blit_uses_typed_rectangles_buffers_and_filter() {
9259        let _guard = fake_gl_test_guard();
9260        let gl = glsym::fake_for_testing(FakeGlConfig::default());
9261
9262        gl.blit_framebuffer(
9263            GlRect::new(0, 1, 320, 240),
9264            GlRect::new(10, 20, 160, 120),
9265            GlFramebufferBlitBuffer::Color.into(),
9266            GlFramebufferBlitFilter::Linear,
9267        )
9268        .expect("color blit");
9269        gl.blit_framebuffer(
9270            GlRect::new(-5, -6, 32, 16),
9271            GlRect::new(0, 0, 32, 16),
9272            GlFramebufferBlitBuffer::Color
9273                | GlFramebufferBlitBuffer::Depth
9274                | GlFramebufferBlitBuffer::Stencil,
9275            GlFramebufferBlitFilter::Nearest,
9276        )
9277        .expect("depth/stencil blit");
9278        assert!(
9279            gl.blit_framebuffer(
9280                GlRect::new(0, 0, 1, 1),
9281                GlRect::new(0, 0, 1, 1),
9282                BitFlags::empty(),
9283                GlFramebufferBlitFilter::Nearest,
9284            )
9285            .unwrap_err()
9286            .contains("at least one buffer")
9287        );
9288        assert!(
9289            gl.blit_framebuffer(
9290                GlRect::new(0, 0, 1, 1),
9291                GlRect::new(0, 0, 1, 1),
9292                GlFramebufferBlitBuffer::Depth.into(),
9293                GlFramebufferBlitFilter::Linear,
9294            )
9295            .unwrap_err()
9296            .contains("nearest filtering")
9297        );
9298        assert!(
9299            gl.blit_framebuffer(
9300                GlRect::new(i32::MAX, 0, 1, 1),
9301                GlRect::new(0, 0, 1, 1),
9302                GlFramebufferBlitBuffer::Color.into(),
9303                GlFramebufferBlitFilter::Nearest,
9304            )
9305            .unwrap_err()
9306            .contains("x endpoint")
9307        );
9308
9309        let snapshot = glsym::snapshot_fake_state_for_testing();
9310        assert_eq!(
9311            snapshot.blit_framebuffer_calls,
9312            vec![
9313                FakeBlitFramebufferCall {
9314                    source: [0, 1, 320, 241],
9315                    destination: [10, 20, 170, 140],
9316                    mask: GlFramebufferBlitBuffer::Color as u32,
9317                    filter: GlFramebufferBlitFilter::Linear.as_raw(),
9318                },
9319                FakeBlitFramebufferCall {
9320                    source: [-5, -6, 27, 10],
9321                    destination: [0, 0, 32, 16],
9322                    mask: (GlFramebufferBlitBuffer::Color
9323                        | GlFramebufferBlitBuffer::Depth
9324                        | GlFramebufferBlitBuffer::Stencil)
9325                        .bits(),
9326                    filter: GlFramebufferBlitFilter::Nearest.as_raw(),
9327                },
9328            ]
9329        );
9330    }
9331
9332    #[test]
9333    fn query_helpers_use_typed_targets_handles_and_return_values() {
9334        let _guard = fake_gl_test_guard();
9335        let gl = glsym::fake_for_testing(FakeGlConfig::default());
9336
9337        assert!(gl.supports_queries());
9338        let query = gl.gen_query().expect("query object");
9339        gl.begin_query(GlQueryTarget::AnySamplesPassed, query)
9340            .expect("begin query");
9341        gl.end_query(GlQueryTarget::AnySamplesPassed)
9342            .expect("end query");
9343
9344        assert!(gl.query_result_available(query).expect("availability"));
9345        assert_eq!(gl.query_result_u32(query).expect("query result"), 77);
9346        gl.delete_query(query).expect("delete query");
9347
9348        let snapshot = glsym::snapshot_fake_state_for_testing();
9349        assert_eq!(snapshot.generated_queries, vec![query.as_raw()]);
9350        assert_eq!(snapshot.deleted_queries, vec![query.as_raw()]);
9351        assert_eq!(
9352            snapshot.begin_query_calls,
9353            vec![(GlQueryTarget::AnySamplesPassed.as_raw(), query.as_raw())]
9354        );
9355        assert_eq!(
9356            snapshot.end_query_calls,
9357            vec![GlQueryTarget::AnySamplesPassed.as_raw()]
9358        );
9359        assert_eq!(
9360            snapshot.query_object_uiv_calls,
9361            vec![
9362                FakeQueryObjectCall {
9363                    query: query.as_raw(),
9364                    property: super::GL_QUERY_RESULT_AVAILABLE,
9365                },
9366                FakeQueryObjectCall {
9367                    query: query.as_raw(),
9368                    property: super::GL_QUERY_RESULT,
9369                },
9370            ]
9371        );
9372    }
9373
9374    #[test]
9375    fn sync_helpers_hide_raw_fence_pointers() {
9376        let _guard = fake_gl_test_guard();
9377        let gl = glsym::fake_for_testing(FakeGlConfig::default());
9378
9379        assert!(gl.supports_sync_objects());
9380        let sync = gl.fence_sync().expect("sync fence");
9381        assert_eq!(
9382            gl.client_wait_sync(sync, true, GlSyncTimeout::from_nanos(5))
9383                .expect("client wait"),
9384            GlSyncWaitResult::AlreadySignaled
9385        );
9386        gl.wait_sync(sync).expect("server wait");
9387        gl.delete_sync(sync).expect("delete sync");
9388
9389        let sync_id = sync.as_raw() as usize;
9390        let snapshot = glsym::snapshot_fake_state_for_testing();
9391        assert_eq!(snapshot.generated_syncs, vec![sync_id]);
9392        assert_eq!(
9393            snapshot.client_wait_sync_calls,
9394            vec![super::FakeClientWaitSyncCall {
9395                sync: sync_id,
9396                flags: super::GL_SYNC_FLUSH_COMMANDS_BIT,
9397                timeout_nanos: 5,
9398            }]
9399        );
9400        assert_eq!(
9401            snapshot.wait_sync_calls,
9402            vec![super::FakeWaitSyncCall {
9403                sync: sync_id,
9404                flags: 0,
9405                timeout_nanos: super::GL_TIMEOUT_IGNORED,
9406            }]
9407        );
9408        assert_eq!(snapshot.deleted_syncs, vec![sync_id]);
9409    }
9410
9411    #[test]
9412    fn fake_gl_snapshot_tracks_shared_context_state() {
9413        let _guard = fake_gl_test_guard();
9414        let gl = glsym::fake_for_testing(FakeGlConfig::default());
9415
9416        gl.enable(GlCapability::Blend);
9417        gl.enable(GlCapability::DepthTest);
9418        gl.enable(GlCapability::ScissorTest);
9419        gl.enable(GlCapability::CullFace);
9420        gl.depth_func(GlDepthFunction::LessOrEqual).unwrap();
9421        gl.depth_mask(false).unwrap();
9422        gl.cull_face(GlCullFaceMode::Back).unwrap();
9423        gl.front_face(GlFrontFaceWinding::CounterClockwise).unwrap();
9424        gl.stencil_func(
9425            GlStencilFunction::Always,
9426            GlStencilReference::new(3),
9427            GlStencilMask::new(0x0f),
9428        )
9429        .unwrap();
9430        gl.stencil_mask(GlStencilMask::new(0xf0)).unwrap();
9431        gl.stencil_op(
9432            GlStencilOperation::Keep,
9433            GlStencilOperation::Replace,
9434            GlStencilOperation::IncrementWrap,
9435        )
9436        .unwrap();
9437        gl.stencil_func_separate(
9438            GlStencilFace::Back,
9439            GlStencilFunction::LessOrEqual,
9440            GlStencilReference::new(2),
9441            GlStencilMask::ALL,
9442        )
9443        .unwrap();
9444        gl.stencil_mask_separate(GlStencilFace::Front, GlStencilMask::NONE)
9445            .unwrap();
9446        gl.stencil_op_separate(
9447            GlStencilFace::FrontAndBack,
9448            GlStencilOperation::DecrementClamp,
9449            GlStencilOperation::Invert,
9450            GlStencilOperation::Keep,
9451        )
9452        .unwrap();
9453        gl.color_mask(GlColorWriteMask::RGB).unwrap();
9454        gl.polygon_offset(GlPolygonOffset::new(1.25, -2.5)).unwrap();
9455        gl.bind_buffer(
9456            GlBufferTarget::ArrayBuffer,
9457            Some(GlBuffer::from_nonzero(11).unwrap()),
9458        );
9459        gl.bind_buffer(
9460            GlBufferTarget::ElementArrayBuffer,
9461            Some(GlBuffer::from_nonzero(12).unwrap()),
9462        );
9463        gl.bind_framebuffer(
9464            GlFramebufferTarget::Framebuffer,
9465            GlFramebuffer::from_raw(13),
9466        )
9467        .unwrap();
9468        gl.viewport(GlRect::new(1, 2, 320, 240)).unwrap();
9469        gl.scissor(GlRect::new(3, 4, 160, 120)).unwrap();
9470        gl.pixel_store_unpack_alignment(GlPixelStoreAlignment::One);
9471        gl.pixel_store_pack_alignment(GlPixelStoreAlignment::Eight);
9472        gl.active_texture(GlTextureUnit::from_index(1)).unwrap();
9473        let texture_2d_unit_1 = gl.gen_texture().expect("texture unit 1");
9474        let texture_array_unit_1 = gl.gen_texture().expect("texture array unit 1");
9475        let texture_2d_unit_0 = gl.gen_texture().expect("texture unit 0");
9476        let texture_array_unit_0 = gl.gen_texture().expect("texture array unit 0");
9477        gl.bind_texture(GlTextureTarget::Texture2D, Some(texture_2d_unit_1));
9478        gl.bind_texture(GlTextureTarget::Texture2DArray, Some(texture_array_unit_1));
9479        gl.active_texture(GlTextureUnit::ZERO).unwrap();
9480        gl.bind_texture(GlTextureTarget::Texture2D, Some(texture_2d_unit_0));
9481        gl.bind_texture(GlTextureTarget::Texture2DArray, Some(texture_array_unit_0));
9482        assert!(
9483            gl.active_texture(GlTextureUnit::from_index(u32::MAX))
9484                .unwrap_err()
9485                .contains("texture unit index")
9486        );
9487        gl.clear_color_buffer();
9488
9489        let snapshot = glsym::snapshot_fake_state_for_testing();
9490        assert_eq!(snapshot.clear_scissor_enabled, vec![true]);
9491        assert!(snapshot.blend_enabled);
9492        assert!(snapshot.scissor_enabled);
9493        assert_eq!(
9494            snapshot.enabled_capabilities,
9495            vec![
9496                GlCapability::CullFace.as_raw(),
9497                GlCapability::DepthTest.as_raw(),
9498                GlCapability::Blend.as_raw(),
9499                GlCapability::ScissorTest.as_raw(),
9500            ]
9501        );
9502        assert_eq!(
9503            snapshot.depth_function,
9504            Some(GlDepthFunction::LessOrEqual.as_raw())
9505        );
9506        assert!(!snapshot.depth_mask);
9507        gl.depth_range(GlDepthRange::new(0.25, 0.75)).unwrap();
9508        let snapshot = glsym::snapshot_fake_state_for_testing();
9509        assert_eq!(
9510            snapshot.depth_range_f_calls,
9511            vec![[0.25f32.to_bits(), 0.75f32.to_bits()]]
9512        );
9513        assert_eq!(snapshot.cull_face_mode, Some(GlCullFaceMode::Back.as_raw()));
9514        assert_eq!(
9515            snapshot.front_face_winding,
9516            Some(GlFrontFaceWinding::CounterClockwise.as_raw())
9517        );
9518        assert_eq!(
9519            snapshot.stencil_func,
9520            Some(super::FakeStencilFuncCall {
9521                function: GlStencilFunction::Always.as_raw(),
9522                reference: 3,
9523                mask: 0x0f,
9524            })
9525        );
9526        assert_eq!(snapshot.stencil_mask, 0xf0);
9527        assert_eq!(
9528            snapshot.stencil_op,
9529            Some(super::FakeStencilOpCall {
9530                stencil_fail: GlStencilOperation::Keep.as_raw(),
9531                depth_fail: GlStencilOperation::Replace.as_raw(),
9532                depth_pass: GlStencilOperation::IncrementWrap.as_raw(),
9533            })
9534        );
9535        assert_eq!(
9536            snapshot.stencil_func_separate_calls,
9537            vec![super::FakeStencilFuncSeparateCall {
9538                face: GlStencilFace::Back.as_raw(),
9539                function: GlStencilFunction::LessOrEqual.as_raw(),
9540                reference: 2,
9541                mask: u32::MAX,
9542            }]
9543        );
9544        assert_eq!(
9545            snapshot.stencil_mask_separate_calls,
9546            vec![super::FakeStencilMaskSeparateCall {
9547                face: GlStencilFace::Front.as_raw(),
9548                mask: 0,
9549            }]
9550        );
9551        assert_eq!(
9552            snapshot.stencil_op_separate_calls,
9553            vec![super::FakeStencilOpSeparateCall {
9554                face: GlStencilFace::FrontAndBack.as_raw(),
9555                stencil_fail: GlStencilOperation::DecrementClamp.as_raw(),
9556                depth_fail: GlStencilOperation::Invert.as_raw(),
9557                depth_pass: GlStencilOperation::Keep.as_raw(),
9558            }]
9559        );
9560        assert_eq!(snapshot.color_write_mask, [true, true, true, false]);
9561        assert_eq!(
9562            snapshot.polygon_offset,
9563            Some([1.25f32.to_bits(), (-2.5f32).to_bits()])
9564        );
9565        assert_eq!(snapshot.bound_array_buffer, 11);
9566        assert_eq!(snapshot.bound_element_array_buffer, 12);
9567        assert_eq!(snapshot.bound_framebuffer, 13);
9568        assert_eq!(snapshot.framebuffer_bindings, vec![13]);
9569        assert_eq!(snapshot.viewport_calls, vec![(1, 2, 320, 240)]);
9570        assert_eq!(snapshot.scissor_calls, vec![(3, 4, 160, 120)]);
9571        assert_eq!(snapshot.pack_alignment, 8);
9572        assert_eq!(snapshot.pack_alignment_calls, vec![8]);
9573        assert_eq!(snapshot.unpack_alignment, 1);
9574        assert_eq!(snapshot.unpack_alignment_calls, vec![1]);
9575        assert_eq!(snapshot.active_texture, 0);
9576        assert_eq!(snapshot.bound_texture_2d, texture_2d_unit_0.as_raw());
9577        assert_eq!(
9578            snapshot.bound_texture_2d_units,
9579            vec![
9580                (0, texture_2d_unit_0.as_raw()),
9581                (1, texture_2d_unit_1.as_raw())
9582            ]
9583        );
9584        assert_eq!(
9585            snapshot.bound_texture_2d_array,
9586            texture_array_unit_0.as_raw()
9587        );
9588        assert_eq!(
9589            snapshot.bound_texture_2d_array_units,
9590            vec![
9591                (0, texture_array_unit_0.as_raw()),
9592                (1, texture_array_unit_1.as_raw())
9593            ]
9594        );
9595
9596        gl.disable(GlCapability::Blend);
9597        gl.disable(GlCapability::DepthTest);
9598        gl.disable(GlCapability::ScissorTest);
9599        gl.disable(GlCapability::CullFace);
9600        gl.unbind_buffer(GlBufferTarget::ArrayBuffer);
9601        gl.unbind_buffer(GlBufferTarget::ElementArrayBuffer);
9602        gl.unbind_framebuffer(GlFramebufferTarget::Framebuffer);
9603        gl.pixel_store_unpack_alignment(GlPixelStoreAlignment::Four);
9604        gl.pixel_store_pack_alignment(GlPixelStoreAlignment::Four);
9605        gl.active_texture(GlTextureUnit::from_index(1)).unwrap();
9606        gl.bind_texture(GlTextureTarget::Texture2D, None);
9607        gl.bind_texture(GlTextureTarget::Texture2DArray, None);
9608        gl.active_texture(GlTextureUnit::ZERO).unwrap();
9609        gl.bind_texture(GlTextureTarget::Texture2D, None);
9610        gl.bind_texture(GlTextureTarget::Texture2DArray, None);
9611        gl.delete_texture(texture_2d_unit_1);
9612        gl.delete_texture(texture_2d_unit_0);
9613        gl.delete_texture(texture_array_unit_1);
9614        gl.delete_texture(texture_array_unit_0);
9615        gl.clear_color_buffer();
9616
9617        let snapshot = glsym::snapshot_fake_state_for_testing();
9618        assert_eq!(snapshot.clear_scissor_enabled, vec![true, false]);
9619        assert!(!snapshot.blend_enabled);
9620        assert!(!snapshot.scissor_enabled);
9621        assert!(snapshot.enabled_capabilities.is_empty());
9622        assert_eq!(snapshot.bound_array_buffer, 0);
9623        assert_eq!(snapshot.bound_element_array_buffer, 0);
9624        assert_eq!(snapshot.bound_framebuffer, 0);
9625        assert_eq!(snapshot.framebuffer_bindings, vec![13, 0]);
9626        assert_eq!(snapshot.pack_alignment, 4);
9627        assert_eq!(snapshot.pack_alignment_calls, vec![8, 4]);
9628        assert_eq!(snapshot.unpack_alignment, 4);
9629        assert_eq!(snapshot.unpack_alignment_calls, vec![1, 4]);
9630        assert_eq!(snapshot.active_texture, 0);
9631        assert_eq!(snapshot.bound_texture_2d, 0);
9632        assert_eq!(snapshot.bound_texture_2d_units, vec![(0, 0), (1, 0)]);
9633        assert_eq!(
9634            snapshot.deleted_textures,
9635            vec![
9636                texture_2d_unit_1.as_raw(),
9637                texture_2d_unit_0.as_raw(),
9638                texture_array_unit_1.as_raw(),
9639                texture_array_unit_0.as_raw(),
9640            ]
9641        );
9642        assert_eq!(snapshot.bound_texture_2d_array, 0);
9643        assert_eq!(snapshot.bound_texture_2d_array_units, vec![(0, 0), (1, 0)]);
9644    }
9645
9646    #[test]
9647    fn typed_rectangles_reject_sizes_that_exceed_gl_sizei() {
9648        let _guard = fake_gl_test_guard();
9649        let gl = glsym::fake_for_testing(FakeGlConfig::default());
9650
9651        assert!(
9652            gl.viewport(GlRect::new(0, 0, u32::MAX, 1))
9653                .unwrap_err()
9654                .contains("glViewport width")
9655        );
9656        assert!(
9657            gl.scissor(GlRect::new(0, 0, 1, u32::MAX))
9658                .unwrap_err()
9659                .contains("glScissor height")
9660        );
9661
9662        let snapshot = glsym::snapshot_fake_state_for_testing();
9663        assert!(snapshot.viewport_calls.is_empty());
9664        assert!(snapshot.scissor_calls.is_empty());
9665    }
9666}