1#![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#[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#[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#[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#[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#[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 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 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 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 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
6081pub 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 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}