Skip to main content

gasm/
gles.rs

1//! The OpenGL ES 3.0 C API on gasm:gl, for Rust guests: the same functions as the
2//! C SDK's `gasm_gl.c` (there is no libc to link it), with C signatures, so GL
3//! loaders can use them through [`get_proc_address`]. glow's native backend runs
4//! on them (sdk/glow, design/gasm-gl.md):
5//!
6//! ```ignore
7//! let gl = unsafe { glow::Context::from_loader_function_cstr(gasm::gles::get_proc_address) };
8//! ```
9//!
10//! Most functions forward one to one (generated: gles_gen.rs); the ones here adapt
11//! GL's C conventions to the ABI: name arrays, string arrays, the pixel store (upload
12//! lengths are exact), cached glGetString pointers, glMapBufferRange in guest memory.
13//! Unused functions cost nothing: only what a game (or its loader) references is linked.
14
15#![allow(non_snake_case, clippy::missing_safety_doc, clippy::too_many_arguments)]
16
17use std::ffi::{c_void, CStr};
18use std::sync::Mutex;
19
20use crate::sys;
21
22include!("gles_gen.rs");
23
24/// A GLES 3.0 entry point by name (null for anything else): the loader for glow.
25pub fn get_proc_address(name: &CStr) -> *const c_void {
26    lookup(name.to_bytes())
27}
28
29const GL_INVALID_ENUM: u32 = 0x0500;
30const GL_INVALID_VALUE: u32 = 0x0501;
31const GL_INVALID_OPERATION: u32 = 0x0502;
32const GL_COLOR: u32 = 0x1800;
33const GL_PIXEL_PACK_BUFFER: u32 = 0x88EB;
34const GL_PIXEL_UNPACK_BUFFER: u32 = 0x88EC;
35const GL_EXTENSIONS: u32 = 0x1F03;
36const GL_NUM_EXTENSIONS: u32 = 0x821D;
37const GL_MAP_READ_BIT: u32 = 0x0001;
38const GL_MAP_WRITE_BIT: u32 = 0x0002;
39const GL_MAP_INVALIDATE_RANGE_BIT: u32 = 0x0004;
40const GL_MAP_INVALIDATE_BUFFER_BIT: u32 = 0x0008;
41const GL_MAP_FLUSH_EXPLICIT_BIT: u32 = 0x0010;
42
43#[derive(Clone, Copy)]
44struct Store {
45    alignment: i32,
46    row_length: i32,
47    image_height: i32,
48    skip_pixels: i32,
49    skip_rows: i32,
50    skip_images: i32,
51}
52const STORE: Store = Store { alignment: 4, row_length: 0, image_height: 0, skip_pixels: 0, skip_rows: 0, skip_images: 0 };
53
54struct Mapping {
55    target: u32,
56    data: Vec<u8>,
57    offset: isize,
58    access: u32,
59}
60
61struct State {
62    /// errors found here (no binary formats, bad indices)
63    error: u32,
64    unpack: Store,
65    pack: Store,
66    unpack_buffer: u32,
67    pack_buffer: u32,
68    /// glGetString results: they stay valid
69    strings: Vec<(u32, Box<[u8]>)>,
70    extensions: Option<Vec<Box<[u8]>>>,
71    maps: Vec<Mapping>,
72}
73
74static STATE: Mutex<State> = Mutex::new(State {
75    error: 0,
76    unpack: STORE,
77    pack: STORE,
78    unpack_buffer: 0,
79    pack_buffer: 0,
80    strings: Vec::new(),
81    extensions: None,
82    maps: Vec::new(),
83});
84
85fn state<R>(f: impl FnOnce(&mut State) -> R) -> R {
86    f(&mut STATE.lock().unwrap_or_else(|e| e.into_inner()))
87}
88
89fn set_error(e: u32) {
90    state(|s| s.error = e);
91}
92
93fn pixel_bytes(format: u32, ty: u32) -> u32 {
94    match ty {
95        0x8363 | 0x8033 | 0x8034 => return 2,
96        0x8368 | 0x8C3B | 0x8C3E | 0x84FA => return 4,
97        0x8DAD => return 8,
98        _ => {}
99    }
100    let size = match ty {
101        0x1400 | 0x1401 => 1,
102        0x1402 | 0x1403 | 0x140B => 2,
103        0x1404..=0x1406 => 4,
104        _ => 0,
105    };
106    let comps = match format {
107        0x1903 | 0x8D94 | 0x1906 | 0x1909 | 0x1902 => 1,
108        0x8227 | 0x8228 | 0x190A | 0x84F9 => 2,
109        0x1907 | 0x8D98 => 3,
110        0x1908 | 0x8D99 => 4,
111        _ => 0,
112    };
113    size * comps
114}
115
116/// Bytes of a w×h×d image under a pixel store (GLES 3.0 3.7.1; the same formula in the runners).
117fn image_bytes(s: &Store, w: i32, h: i32, d: i32, format: u32, ty: u32) -> u32 {
118    let bpp = pixel_bytes(format, ty) as u64;
119    if w <= 0 || h <= 0 || d <= 0 || bpp == 0 || s.alignment <= 0 {
120        return 0;
121    }
122    let (w, h, d) = (w as u64, h as u64, d as u64);
123    let row_len = if s.row_length > 0 { s.row_length as u64 } else { w };
124    let a = s.alignment as u64;
125    let row = (row_len * bpp).div_ceil(a) * a;
126    let img_h = if s.image_height > 0 { s.image_height as u64 } else { h };
127    let img = row * img_h;
128    (s.skip_images as u64 * img + (d - 1) * img + s.skip_rows as u64 * row + (h - 1) * row + s.skip_pixels as u64 * bpp + w * bpp) as u32
129}
130
131/// An upload's pixels: (pointer, len); with an unpack buffer bound, the pointer is an offset.
132fn unpack(pixels: *const c_void, w: i32, h: i32, d: i32, format: u32, ty: u32) -> (*const u8, u32) {
133    state(|s| {
134        if s.unpack_buffer != 0 {
135            (std::ptr::null(), pixels as usize as u32)
136        } else if pixels.is_null() {
137            (std::ptr::null(), 0)
138        } else {
139            (pixels as *const u8, image_bytes(&s.unpack, w, h, d, format, ty))
140        }
141    })
142}
143
144unsafe fn cstr_len(p: *const c_void) -> u32 {
145    unsafe { CStr::from_ptr(p as *const std::ffi::c_char) }.to_bytes().len() as u32
146}
147
148/// The "returns the full length, copies if it fits" imports, as GL's truncating copies.
149unsafe fn truncated(get: impl Fn(*mut u8, u32) -> i32, size: i32, length: *mut c_void, out: *mut c_void) {
150    let n = get(std::ptr::null_mut(), 0);
151    let length = length as *mut i32;
152    if n < 0 || size <= 0 {
153        if !length.is_null() {
154            unsafe { *length = 0 };
155        }
156        return;
157    }
158    let mut tmp = vec![0u8; n as usize];
159    get(tmp.as_mut_ptr(), n as u32);
160    let k = n.min(size - 1) as usize;
161    unsafe {
162        std::ptr::copy_nonoverlapping(tmp.as_ptr(), out as *mut u8, k);
163        *(out as *mut u8).add(k) = 0;
164        if !length.is_null() {
165            *length = k as i32;
166        }
167    }
168}
169
170unsafe fn active_info(f: unsafe fn(u32, u32, *mut u8, u32, *mut u8) -> i32, program: u32, index: u32, size: i32, length: *mut c_void, out_size: *mut c_void, out_type: *mut c_void, name: *mut c_void) {
171    let mut info = [0i32; 2];
172    let n = unsafe { f(program, index, std::ptr::null_mut(), 0, info.as_mut_ptr() as *mut u8) };
173    let length = length as *mut i32;
174    if n < 0 {
175        if !length.is_null() {
176            unsafe { *length = 0 };
177        }
178        return;
179    }
180    let mut tmp = vec![0u8; n as usize];
181    unsafe { f(program, index, tmp.as_mut_ptr(), n as u32, info.as_mut_ptr() as *mut u8) };
182    let k = if size > 0 { n.min(size - 1) as usize } else { 0 };
183    unsafe {
184        if size > 0 {
185            std::ptr::copy_nonoverlapping(tmp.as_ptr(), name as *mut u8, k);
186            *(name as *mut u8).add(k) = 0;
187        }
188        if !length.is_null() {
189            *length = k as i32;
190        }
191        if !out_size.is_null() {
192            *(out_size as *mut i32) = info[0];
193        }
194        if !out_type.is_null() {
195            *(out_type as *mut u32) = info[1] as u32;
196        }
197    }
198}
199
200// ---- errors and strings ----------------------------------------------------------------------
201
202pub unsafe extern "C" fn glGetError() -> u32 {
203    let e = state(|s| std::mem::take(&mut s.error));
204    if e != 0 { e } else { unsafe { sys::gl_get_error() } }
205}
206
207fn string(name: u32) -> Option<Box<[u8]>> {
208    let n = unsafe { sys::gl_get_string(name, std::ptr::null_mut(), 0) };
209    if n < 0 {
210        return None;
211    }
212    let mut v = vec![0u8; n as usize + 1];
213    unsafe { sys::gl_get_string(name, v.as_mut_ptr(), n as u32) };
214    Some(v.into_boxed_slice())
215}
216
217pub unsafe extern "C" fn glGetString(name: u32) -> *const c_void {
218    if ![0x1F00, 0x1F01, 0x1F02, 0x8B8C, GL_EXTENSIONS].contains(&name) {
219        set_error(GL_INVALID_ENUM);
220        return std::ptr::null();
221    }
222    if let Some(p) = state(|s| s.strings.iter().find(|(n, _)| *n == name).map(|(_, b)| b.as_ptr())) {
223        return p as *const c_void;
224    }
225    let Some(b) = string(name) else { return std::ptr::null() };
226    state(|s| {
227        s.strings.push((name, b));
228        s.strings.last().map_or(std::ptr::null(), |(_, b)| b.as_ptr() as *const c_void)
229    })
230}
231
232fn extensions() -> usize {
233    if let Some(n) = state(|s| s.extensions.as_ref().map(Vec::len)) {
234        return n;
235    }
236    let all = string(GL_EXTENSIONS).unwrap_or_default();
237    let all = &all[..all.len().saturating_sub(1)];
238    let list: Vec<Box<[u8]>> = all
239        .split(|&b| b == b' ')
240        .filter(|e| !e.is_empty())
241        .map(|e| [e, &[0][..]].concat().into_boxed_slice())
242        .collect();
243    let n = list.len();
244    state(|s| s.extensions = Some(list));
245    n
246}
247
248pub unsafe extern "C" fn glGetStringi(name: u32, index: u32) -> *const c_void {
249    if name != GL_EXTENSIONS {
250        set_error(GL_INVALID_ENUM);
251        return std::ptr::null();
252    }
253    if index as usize >= extensions() {
254        set_error(GL_INVALID_VALUE);
255        return std::ptr::null();
256    }
257    state(|s| s.extensions.as_ref().map_or(std::ptr::null(), |l| l[index as usize].as_ptr() as *const c_void))
258}
259
260// ---- queries -------------------------------------------------------------------------------------
261
262pub unsafe extern "C" fn glGetIntegerv(pname: u32, data: *mut c_void) {
263    if pname == GL_NUM_EXTENSIONS {
264        unsafe { *(data as *mut i32) = extensions() as i32 };
265        return;
266    }
267    unsafe { sys::gl_get_integerv(pname, data as *mut u8, 64) };
268}
269pub unsafe extern "C" fn glGetBooleanv(pname: u32, data: *mut c_void) {
270    let mut v = [0i32; 64];
271    let n = unsafe { sys::gl_get_integerv(pname, v.as_mut_ptr() as *mut u8, 64) };
272    for (i, x) in v.iter().take(n.clamp(0, 64) as usize).enumerate() {
273        unsafe { *(data as *mut u8).add(i) = (*x != 0) as u8 };
274    }
275}
276pub unsafe extern "C" fn glGetFloatv(pname: u32, data: *mut c_void) {
277    unsafe { sys::gl_get_floatv(pname, data as *mut u8, 64) };
278}
279pub unsafe extern "C" fn glGetInteger64v(pname: u32, data: *mut c_void) {
280    unsafe { sys::gl_get_integer64v(pname, data as *mut u8, 64) };
281}
282pub unsafe extern "C" fn glGetIntegeri_v(target: u32, index: u32, data: *mut c_void) {
283    unsafe { sys::gl_get_integeri_v(target, index, data as *mut u8, 16) };
284}
285pub unsafe extern "C" fn glGetInteger64i_v(target: u32, index: u32, data: *mut c_void) {
286    let mut v = [0i32; 16];
287    let n = unsafe { sys::gl_get_integeri_v(target, index, v.as_mut_ptr() as *mut u8, 16) };
288    for (i, x) in v.iter().take(n.clamp(0, 16) as usize).enumerate() {
289        unsafe { *(data as *mut i64).add(i) = *x as i64 };
290    }
291}
292pub unsafe extern "C" fn glGetInternalformativ(target: u32, internalformat: u32, pname: u32, count: i32, params: *mut c_void) {
293    unsafe { sys::gl_get_internalformativ(target, internalformat, pname, params as *mut u8, count.max(0) as u32) };
294}
295pub unsafe extern "C" fn glGetShaderPrecisionFormat(shadertype: u32, precisiontype: u32, range: *mut c_void, precision: *mut c_void) {
296    let mut v = [0i32; 3];
297    unsafe {
298        sys::gl_get_shader_precision_format(shadertype, precisiontype, v.as_mut_ptr() as *mut u8);
299        *(range as *mut i32) = v[0];
300        *(range as *mut i32).add(1) = v[1];
301        *(precision as *mut i32) = v[2];
302    }
303}
304
305macro_rules! single {
306    ($($name:ident($($p:ident: $t:ty),*) => $call:ident;)*) => {$(
307        pub unsafe extern "C" fn $name($($p: $t,)* params: *mut c_void) {
308            unsafe { *(params as *mut i32) = sys::$call($($p),*) as i32 };
309        }
310    )*};
311}
312single! {
313    glGetShaderiv(shader: u32, pname: u32) => gl_get_shaderiv;
314    glGetProgramiv(program: u32, pname: u32) => gl_get_programiv;
315    glGetBufferParameteriv(target: u32, pname: u32) => gl_get_buffer_parameteriv;
316    glGetRenderbufferParameteriv(target: u32, pname: u32) => gl_get_renderbuffer_parameteriv;
317    glGetFramebufferAttachmentParameteriv(target: u32, attachment: u32, pname: u32) => gl_get_framebuffer_attachment_parameteriv;
318    glGetTexParameteriv(target: u32, pname: u32) => gl_get_tex_parameteriv;
319    glGetSamplerParameteriv(sampler: u32, pname: u32) => gl_get_sampler_parameteriv;
320    glGetQueryiv(target: u32, pname: u32) => gl_get_queryiv;
321    glGetQueryObjectuiv(id: u32, pname: u32) => gl_get_query_objectuiv;
322    glGetVertexAttribiv(index: u32, pname: u32) => gl_get_vertex_attribiv;
323    glGetVertexAttribIiv(index: u32, pname: u32) => gl_get_vertex_attribiv;
324    glGetVertexAttribIuiv(index: u32, pname: u32) => gl_get_vertex_attribiv;
325}
326pub unsafe extern "C" fn glGetTexParameterfv(target: u32, pname: u32, params: *mut c_void) {
327    unsafe { *(params as *mut f32) = sys::gl_get_tex_parameterfv(target, pname) };
328}
329pub unsafe extern "C" fn glGetSamplerParameterfv(sampler: u32, pname: u32, params: *mut c_void) {
330    unsafe { *(params as *mut f32) = sys::gl_get_sampler_parameterfv(sampler, pname) };
331}
332pub unsafe extern "C" fn glGetBufferParameteri64v(target: u32, pname: u32, params: *mut c_void) {
333    unsafe { *(params as *mut i64) = sys::gl_get_buffer_parameteriv(target, pname) as i64 };
334}
335pub unsafe extern "C" fn glGetVertexAttribfv(index: u32, pname: u32, params: *mut c_void) {
336    unsafe { sys::gl_get_vertex_attribfv(index, pname, params as *mut u8, 4) };
337}
338pub unsafe extern "C" fn glGetVertexAttribPointerv(index: u32, pname: u32, pointer: *mut c_void) {
339    unsafe { *(pointer as *mut usize) = sys::gl_get_vertex_attrib_offset(index, pname) as usize };
340}
341pub unsafe extern "C" fn glGetUniformfv(program: u32, location: i32, params: *mut c_void) {
342    unsafe { sys::gl_get_uniformfv(program, location, params as *mut u8, 16) };
343}
344pub unsafe extern "C" fn glGetUniformiv(program: u32, location: i32, params: *mut c_void) {
345    unsafe { sys::gl_get_uniformiv(program, location, params as *mut u8, 16) };
346}
347pub unsafe extern "C" fn glGetUniformuiv(program: u32, location: i32, params: *mut c_void) {
348    unsafe { sys::gl_get_uniformuiv(program, location, params as *mut u8, 16) };
349}
350
351// ---- names ----------------------------------------------------------------------------------------
352
353macro_rules! names {
354    ($($gen:ident, $del:ident => $create:ident, $delete:ident;)*) => {$(
355        pub unsafe extern "C" fn $gen(n: i32, out: *mut c_void) {
356            for i in 0..n.max(0) as usize {
357                unsafe { *(out as *mut u32).add(i) = sys::$create() };
358            }
359        }
360        pub unsafe extern "C" fn $del(n: i32, names: *const c_void) {
361            for i in 0..n.max(0) as usize {
362                unsafe { sys::$delete(*(names as *const u32).add(i)) };
363            }
364        }
365    )*};
366}
367names! {
368    glGenBuffers, glDeleteBuffers => gl_create_buffer, gl_delete_buffer;
369    glGenTextures, glDeleteTextures => gl_create_texture, gl_delete_texture;
370    glGenFramebuffers, glDeleteFramebuffers => gl_create_framebuffer, gl_delete_framebuffer;
371    glGenRenderbuffers, glDeleteRenderbuffers => gl_create_renderbuffer, gl_delete_renderbuffer;
372    glGenQueries, glDeleteQueries => gl_create_query, gl_delete_query;
373    glGenVertexArrays, glDeleteVertexArrays => gl_create_vertex_array, gl_delete_vertex_array;
374    glGenSamplers, glDeleteSamplers => gl_create_sampler, gl_delete_sampler;
375    glGenTransformFeedbacks, glDeleteTransformFeedbacks => gl_create_transform_feedback, gl_delete_transform_feedback;
376}
377
378// ---- buffers ----------------------------------------------------------------------------------------
379
380pub unsafe extern "C" fn glBindBuffer(target: u32, buffer: u32) {
381    state(|s| match target {
382        GL_PIXEL_UNPACK_BUFFER => s.unpack_buffer = buffer,
383        GL_PIXEL_PACK_BUFFER => s.pack_buffer = buffer,
384        _ => {}
385    });
386    unsafe { sys::gl_bind_buffer(target, buffer) };
387}
388pub unsafe extern "C" fn glBufferData(target: u32, size: isize, data: *const c_void, usage: u32) {
389    unsafe { sys::gl_buffer_data(target, data as *const u8, size as u32, usage) };
390}
391pub unsafe extern "C" fn glBufferSubData(target: u32, offset: isize, size: isize, data: *const c_void) {
392    unsafe { sys::gl_buffer_sub_data(target, offset as u32, data as *const u8, size as u32) };
393}
394pub unsafe extern "C" fn glMapBufferRange(target: u32, offset: isize, length: isize, access: u32) -> *mut c_void {
395    if length <= 0 || offset < 0 || state(|s| s.maps.iter().any(|m| m.target == target)) {
396        set_error(GL_INVALID_OPERATION);
397        return std::ptr::null_mut();
398    }
399    let mut data = vec![0u8; length as usize];
400    if access & GL_MAP_READ_BIT != 0 || access & (GL_MAP_INVALIDATE_RANGE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT) == 0 {
401        unsafe { sys::gl_get_buffer_sub_data(target, offset as u32, data.as_mut_ptr(), length as u32) }; // the current contents
402    }
403    let p = data.as_mut_ptr() as *mut c_void;
404    state(|s| s.maps.push(Mapping { target, data, offset, access }));
405    p
406}
407pub unsafe extern "C" fn glFlushMappedBufferRange(target: u32, offset: isize, length: isize) {
408    let ok = state(|s| match s.maps.iter().find(|m| m.target == target) {
409        Some(m) if offset >= 0 && length >= 0 && (offset + length) as usize <= m.data.len() => {
410            unsafe { sys::gl_buffer_sub_data(target, (m.offset + offset) as u32, m.data.as_ptr().add(offset as usize), length as u32) };
411            true
412        }
413        _ => false,
414    });
415    if !ok {
416        set_error(GL_INVALID_VALUE);
417    }
418}
419pub unsafe extern "C" fn glUnmapBuffer(target: u32) -> u8 {
420    let Some(m) = state(|s| s.maps.iter().position(|m| m.target == target).map(|i| s.maps.remove(i))) else {
421        set_error(GL_INVALID_OPERATION);
422        return 0;
423    };
424    if m.access & GL_MAP_WRITE_BIT != 0 && m.access & GL_MAP_FLUSH_EXPLICIT_BIT == 0 {
425        unsafe { sys::gl_buffer_sub_data(target, m.offset as u32, m.data.as_ptr(), m.data.len() as u32) };
426    }
427    1
428}
429pub unsafe extern "C" fn glGetBufferPointerv(target: u32, _pname: u32, params: *mut c_void) {
430    let p = state(|s| s.maps.iter().find(|m| m.target == target).map_or(std::ptr::null(), |m| m.data.as_ptr()));
431    unsafe { *(params as *mut *const u8) = p };
432}
433
434// ---- vertex attributes, drawing ------------------------------------------------------------------------
435
436pub unsafe extern "C" fn glVertexAttribPointer(index: u32, size: i32, ty: u32, normalized: u8, stride: i32, pointer: *const c_void) {
437    unsafe { sys::gl_vertex_attrib_pointer(index, size, ty, normalized as u32, stride, pointer as usize as u32) };
438}
439pub unsafe extern "C" fn glVertexAttribIPointer(index: u32, size: i32, ty: u32, stride: i32, pointer: *const c_void) {
440    unsafe { sys::gl_vertex_attrib_ipointer(index, size, ty, stride, pointer as usize as u32) };
441}
442pub unsafe extern "C" fn glVertexAttrib1f(index: u32, x: f32) {
443    unsafe { sys::gl_vertex_attrib4f(index, x, 0.0, 0.0, 1.0) };
444}
445pub unsafe extern "C" fn glVertexAttrib2f(index: u32, x: f32, y: f32) {
446    unsafe { sys::gl_vertex_attrib4f(index, x, y, 0.0, 1.0) };
447}
448pub unsafe extern "C" fn glVertexAttrib3f(index: u32, x: f32, y: f32, z: f32) {
449    unsafe { sys::gl_vertex_attrib4f(index, x, y, z, 1.0) };
450}
451pub unsafe extern "C" fn glVertexAttrib1fv(index: u32, v: *const c_void) {
452    let v = v as *const f32;
453    unsafe { sys::gl_vertex_attrib4f(index, *v, 0.0, 0.0, 1.0) };
454}
455pub unsafe extern "C" fn glVertexAttrib2fv(index: u32, v: *const c_void) {
456    let v = v as *const f32;
457    unsafe { sys::gl_vertex_attrib4f(index, *v, *v.add(1), 0.0, 1.0) };
458}
459pub unsafe extern "C" fn glVertexAttrib3fv(index: u32, v: *const c_void) {
460    let v = v as *const f32;
461    unsafe { sys::gl_vertex_attrib4f(index, *v, *v.add(1), *v.add(2), 1.0) };
462}
463pub unsafe extern "C" fn glVertexAttrib4fv(index: u32, v: *const c_void) {
464    let v = v as *const f32;
465    unsafe { sys::gl_vertex_attrib4f(index, *v, *v.add(1), *v.add(2), *v.add(3)) };
466}
467pub unsafe extern "C" fn glVertexAttribI4iv(index: u32, v: *const c_void) {
468    let v = v as *const i32;
469    unsafe { sys::gl_vertex_attribi4i(index, *v, *v.add(1), *v.add(2), *v.add(3)) };
470}
471pub unsafe extern "C" fn glVertexAttribI4uiv(index: u32, v: *const c_void) {
472    let v = v as *const u32;
473    unsafe { sys::gl_vertex_attribi4ui(index, *v, *v.add(1), *v.add(2), *v.add(3)) };
474}
475pub unsafe extern "C" fn glDrawElements(mode: u32, count: i32, ty: u32, indices: *const c_void) {
476    unsafe { sys::gl_draw_elements(mode, count, ty, indices as usize as u32) };
477}
478pub unsafe extern "C" fn glDrawRangeElements(mode: u32, start: u32, end: u32, count: i32, ty: u32, indices: *const c_void) {
479    unsafe { sys::gl_draw_range_elements(mode, start, end, count, ty, indices as usize as u32) };
480}
481pub unsafe extern "C" fn glDrawElementsInstanced(mode: u32, count: i32, ty: u32, indices: *const c_void, instances: i32) {
482    unsafe { sys::gl_draw_elements_instanced(mode, count, ty, indices as usize as u32, instances) };
483}
484pub unsafe extern "C" fn glDrawBuffers(n: i32, bufs: *const c_void) {
485    unsafe { sys::gl_draw_buffers(bufs as *const u8, n.max(0) as u32) };
486}
487pub unsafe extern "C" fn glClearBufferiv(buffer: u32, drawbuffer: i32, value: *const c_void) {
488    unsafe { sys::gl_clear_bufferiv(buffer, drawbuffer, value as *const u8, if buffer == GL_COLOR { 4 } else { 1 }) };
489}
490pub unsafe extern "C" fn glClearBufferuiv(buffer: u32, drawbuffer: i32, value: *const c_void) {
491    unsafe { sys::gl_clear_bufferuiv(buffer, drawbuffer, value as *const u8, if buffer == GL_COLOR { 4 } else { 1 }) };
492}
493pub unsafe extern "C" fn glClearBufferfv(buffer: u32, drawbuffer: i32, value: *const c_void) {
494    unsafe { sys::gl_clear_bufferfv(buffer, drawbuffer, value as *const u8, if buffer == GL_COLOR { 4 } else { 1 }) };
495}
496
497// ---- textures, pixels -------------------------------------------------------------------------------------
498
499pub unsafe extern "C" fn glPixelStorei(pname: u32, param: i32) {
500    state(|s| match pname {
501        0x0CF5 => s.unpack.alignment = param,
502        0x0CF2 => s.unpack.row_length = param,
503        0x806E => s.unpack.image_height = param,
504        0x0CF4 => s.unpack.skip_pixels = param,
505        0x0CF3 => s.unpack.skip_rows = param,
506        0x806D => s.unpack.skip_images = param,
507        0x0D05 => s.pack.alignment = param,
508        0x0D02 => s.pack.row_length = param,
509        0x0D04 => s.pack.skip_pixels = param,
510        0x0D03 => s.pack.skip_rows = param,
511        _ => {}
512    });
513    unsafe { sys::gl_pixel_storei(pname, param) };
514}
515pub unsafe extern "C" fn glTexImage2D(target: u32, level: i32, ifmt: i32, w: i32, h: i32, border: i32, format: u32, ty: u32, pixels: *const c_void) {
516    let (p, len) = unpack(pixels, w, h, 1, format, ty);
517    unsafe { sys::gl_tex_image_2d(target, level, ifmt, w, h, border, format, ty, p, len) };
518}
519pub unsafe extern "C" fn glTexImage3D(target: u32, level: i32, ifmt: i32, w: i32, h: i32, d: i32, border: i32, format: u32, ty: u32, pixels: *const c_void) {
520    let (p, len) = unpack(pixels, w, h, d, format, ty);
521    unsafe { sys::gl_tex_image_3d(target, level, ifmt, w, h, d, border, format, ty, p, len) };
522}
523pub unsafe extern "C" fn glTexSubImage2D(target: u32, level: i32, x: i32, y: i32, w: i32, h: i32, format: u32, ty: u32, pixels: *const c_void) {
524    let (p, len) = unpack(pixels, w, h, 1, format, ty);
525    unsafe { sys::gl_tex_sub_image_2d(target, level, x, y, w, h, format, ty, p, len) };
526}
527pub unsafe extern "C" fn glTexSubImage3D(target: u32, level: i32, x: i32, y: i32, z: i32, w: i32, h: i32, d: i32, format: u32, ty: u32, pixels: *const c_void) {
528    let (p, len) = unpack(pixels, w, h, d, format, ty);
529    unsafe { sys::gl_tex_sub_image_3d(target, level, x, y, z, w, h, d, format, ty, p, len) };
530}
531pub unsafe extern "C" fn glCompressedTexImage2D(target: u32, level: i32, ifmt: u32, w: i32, h: i32, border: i32, size: i32, data: *const c_void) {
532    unsafe { sys::gl_compressed_tex_image_2d(target, level, ifmt, w, h, border, data as *const u8, size as u32) };
533}
534pub unsafe extern "C" fn glCompressedTexImage3D(target: u32, level: i32, ifmt: u32, w: i32, h: i32, d: i32, border: i32, size: i32, data: *const c_void) {
535    unsafe { sys::gl_compressed_tex_image_3d(target, level, ifmt, w, h, d, border, data as *const u8, size as u32) };
536}
537pub unsafe extern "C" fn glCompressedTexSubImage2D(target: u32, level: i32, x: i32, y: i32, w: i32, h: i32, format: u32, size: i32, data: *const c_void) {
538    unsafe { sys::gl_compressed_tex_sub_image_2d(target, level, x, y, w, h, format, data as *const u8, size as u32) };
539}
540pub unsafe extern "C" fn glCompressedTexSubImage3D(
541    target: u32, level: i32, x: i32, y: i32, z: i32, w: i32, h: i32, d: i32, format: u32, size: i32, data: *const c_void,
542) {
543    unsafe { sys::gl_compressed_tex_sub_image_3d(target, level, x, y, z, w, h, d, format, data as *const u8, size as u32) };
544}
545pub unsafe extern "C" fn glTexParameteriv(target: u32, pname: u32, params: *const c_void) {
546    unsafe { sys::gl_tex_parameteri(target, pname, *(params as *const i32)) };
547}
548pub unsafe extern "C" fn glTexParameterfv(target: u32, pname: u32, params: *const c_void) {
549    unsafe { sys::gl_tex_parameterf(target, pname, *(params as *const f32)) };
550}
551pub unsafe extern "C" fn glSamplerParameteriv(sampler: u32, pname: u32, params: *const c_void) {
552    unsafe { sys::gl_sampler_parameteri(sampler, pname, *(params as *const i32)) };
553}
554pub unsafe extern "C" fn glSamplerParameterfv(sampler: u32, pname: u32, params: *const c_void) {
555    unsafe { sys::gl_sampler_parameterf(sampler, pname, *(params as *const f32)) };
556}
557pub unsafe extern "C" fn glReadPixels(x: i32, y: i32, w: i32, h: i32, format: u32, ty: u32, pixels: *mut c_void) {
558    let (pack_buffer, len) = state(|s| (s.pack_buffer, image_bytes(&s.pack, w, h, 1, format, ty)));
559    unsafe { sys::gl_read_pixels(x, y, w, h, format, ty, pixels as *mut u8, if pack_buffer != 0 { 0 } else { len }) };
560}
561pub unsafe extern "C" fn glInvalidateFramebuffer(target: u32, n: i32, attachments: *const c_void) {
562    unsafe { sys::gl_invalidate_framebuffer(target, attachments as *const u8, n.max(0) as u32) };
563}
564pub unsafe extern "C" fn glInvalidateSubFramebuffer(target: u32, n: i32, attachments: *const c_void, x: i32, y: i32, w: i32, h: i32) {
565    unsafe { sys::gl_invalidate_sub_framebuffer(target, attachments as *const u8, n.max(0) as u32, x, y, w, h) };
566}
567
568// ---- shaders and programs ---------------------------------------------------------------------------------
569
570pub unsafe extern "C" fn glShaderSource(shader: u32, count: i32, strings: *const c_void, lengths: *const c_void) {
571    let strings = strings as *const *const c_void;
572    let lengths = lengths as *const i32;
573    let mut all = Vec::new();
574    for i in 0..count.max(0) as usize {
575        unsafe {
576            let p = *strings.add(i);
577            let n = if !lengths.is_null() && *lengths.add(i) >= 0 { *lengths.add(i) as u32 } else { cstr_len(p) };
578            all.extend_from_slice(std::slice::from_raw_parts(p as *const u8, n as usize));
579        }
580    }
581    unsafe { sys::gl_shader_source(shader, all.as_ptr(), all.len() as u32) };
582}
583pub unsafe extern "C" fn glGetShaderInfoLog(shader: u32, size: i32, length: *mut c_void, log: *mut c_void) {
584    unsafe { truncated(|p, n| sys::gl_get_shader_info_log(shader, p, n), size, length, log) };
585}
586pub unsafe extern "C" fn glGetProgramInfoLog(program: u32, size: i32, length: *mut c_void, log: *mut c_void) {
587    unsafe { truncated(|p, n| sys::gl_get_program_info_log(program, p, n), size, length, log) };
588}
589pub unsafe extern "C" fn glGetShaderSource(shader: u32, size: i32, length: *mut c_void, source: *mut c_void) {
590    unsafe { truncated(|p, n| sys::gl_get_shader_source(shader, p, n), size, length, source) };
591}
592pub unsafe extern "C" fn glGetActiveUniformBlockName(program: u32, index: u32, size: i32, length: *mut c_void, name: *mut c_void) {
593    unsafe { truncated(|p, n| sys::gl_get_active_uniform_block_name(program, index, p, n), size, length, name) };
594}
595pub unsafe extern "C" fn glGetActiveAttrib(program: u32, index: u32, size: i32, length: *mut c_void, out_size: *mut c_void, ty: *mut c_void, name: *mut c_void) {
596    unsafe { active_info(|p, i, d, c, info| sys::gl_get_active_attrib(p, i, d, c, info), program, index, size, length, out_size, ty, name) };
597}
598pub unsafe extern "C" fn glGetActiveUniform(program: u32, index: u32, size: i32, length: *mut c_void, out_size: *mut c_void, ty: *mut c_void, name: *mut c_void) {
599    unsafe { active_info(|p, i, d, c, info| sys::gl_get_active_uniform(p, i, d, c, info), program, index, size, length, out_size, ty, name) };
600}
601pub unsafe extern "C" fn glGetTransformFeedbackVarying(
602    program: u32, index: u32, size: i32, length: *mut c_void, out_size: *mut c_void, ty: *mut c_void, name: *mut c_void,
603) {
604    unsafe { active_info(|p, i, d, c, info| sys::gl_get_transform_feedback_varying(p, i, d, c, info), program, index, size, length, out_size, ty, name) };
605}
606pub unsafe extern "C" fn glGetAttachedShaders(program: u32, max: i32, count: *mut c_void, shaders: *mut c_void) {
607    let n = unsafe { sys::gl_get_attached_shaders(program, shaders as *mut u8, max.max(0) as u32) };
608    if !count.is_null() {
609        unsafe { *(count as *mut i32) = n.min(max) };
610    }
611}
612pub unsafe extern "C" fn glBindAttribLocation(program: u32, index: u32, name: *const c_void) {
613    unsafe { sys::gl_bind_attrib_location(program, index, name as *const u8, cstr_len(name)) };
614}
615pub unsafe extern "C" fn glGetAttribLocation(program: u32, name: *const c_void) -> i32 {
616    unsafe { sys::gl_get_attrib_location(program, name as *const u8, cstr_len(name)) }
617}
618pub unsafe extern "C" fn glGetFragDataLocation(program: u32, name: *const c_void) -> i32 {
619    unsafe { sys::gl_get_frag_data_location(program, name as *const u8, cstr_len(name)) }
620}
621pub unsafe extern "C" fn glGetUniformLocation(program: u32, name: *const c_void) -> i32 {
622    unsafe { sys::gl_get_uniform_location(program, name as *const u8, cstr_len(name)) }
623}
624pub unsafe extern "C" fn glGetUniformBlockIndex(program: u32, name: *const c_void) -> u32 {
625    unsafe { sys::gl_get_uniform_block_index(program, name as *const u8, cstr_len(name)) }
626}
627pub unsafe extern "C" fn glGetUniformIndices(program: u32, count: i32, names: *const c_void, indices: *mut c_void) {
628    let names = names as *const *const c_void;
629    for i in 0..count.max(0) as usize {
630        unsafe {
631            let n = *names.add(i);
632            *(indices as *mut u32).add(i) = sys::gl_get_uniform_index(program, n as *const u8, cstr_len(n));
633        }
634    }
635}
636pub unsafe extern "C" fn glGetActiveUniformsiv(program: u32, count: i32, indices: *const c_void, pname: u32, params: *mut c_void) {
637    unsafe { sys::gl_get_active_uniformsiv(program, indices as *const u8, count.max(0) as u32, pname, params as *mut u8) };
638}
639pub unsafe extern "C" fn glGetActiveUniformBlockiv(program: u32, index: u32, pname: u32, params: *mut c_void) {
640    unsafe { sys::gl_get_active_uniform_blockiv(program, index, pname, params as *mut u8, 64) };
641}
642pub unsafe extern "C" fn glTransformFeedbackVaryings(program: u32, count: i32, varyings: *const c_void, mode: u32) {
643    let varyings = varyings as *const *const c_void;
644    let mut all = Vec::new();
645    for i in 0..count.max(0) as usize {
646        unsafe {
647            let p = *varyings.add(i);
648            all.extend_from_slice(std::slice::from_raw_parts(p as *const u8, cstr_len(p) as usize + 1));
649        }
650    }
651    unsafe { sys::gl_transform_feedback_varyings(program, all.as_ptr(), all.len() as u32, count.max(0) as u32, mode) };
652}
653pub unsafe extern "C" fn glReleaseShaderCompiler() {}
654pub unsafe extern "C" fn glShaderBinary(_count: i32, _shaders: *const c_void, _format: u32, _binary: *const c_void, _length: i32) {
655    set_error(GL_INVALID_ENUM); // WebGL has no shader formats
656}
657pub unsafe extern "C" fn glGetProgramBinary(_program: u32, _size: i32, length: *mut c_void, _format: *mut c_void, _binary: *mut c_void) {
658    if !length.is_null() {
659        unsafe { *(length as *mut i32) = 0 };
660    }
661    set_error(GL_INVALID_OPERATION);
662}
663pub unsafe extern "C" fn glProgramBinary(_program: u32, _format: u32, _binary: *const c_void, _length: i32) {
664    set_error(GL_INVALID_ENUM);
665}
666pub unsafe extern "C" fn glProgramParameteri(_program: u32, _pname: u32, _value: i32) {}
667
668// ---- sync (a GLsync is a gasm:gl sync name) ------------------------------------------------------------------
669
670pub unsafe extern "C" fn glFenceSync(condition: u32, flags: u32) -> *const c_void {
671    unsafe { sys::gl_fence_sync(condition, flags) as usize as *const c_void }
672}
673pub unsafe extern "C" fn glIsSync(sync: *const c_void) -> u8 {
674    unsafe { sys::gl_is_sync(sync as usize as u32) as u8 }
675}
676pub unsafe extern "C" fn glDeleteSync(sync: *const c_void) {
677    unsafe { sys::gl_delete_sync(sync as usize as u32) };
678}
679pub unsafe extern "C" fn glClientWaitSync(sync: *const c_void, flags: u32, timeout: u64) -> u32 {
680    unsafe { sys::gl_client_wait_sync(sync as usize as u32, flags, timeout) }
681}
682pub unsafe extern "C" fn glWaitSync(sync: *const c_void, flags: u32, timeout: u64) {
683    unsafe { sys::gl_wait_sync(sync as usize as u32, flags, timeout) };
684}
685pub unsafe extern "C" fn glGetSynciv(sync: *const c_void, pname: u32, count: i32, length: *mut c_void, values: *mut c_void) {
686    if count > 0 {
687        unsafe { *(values as *mut i32) = sys::gl_get_synciv(sync as usize as u32, pname) };
688    }
689    if !length.is_null() {
690        unsafe { *(length as *mut i32) = (count > 0) as i32 };
691    }
692}
693