glow_wrap/
lib.rs

1use std::ffi::CStr;
2use std::sync::Arc;
3use std::sync::OnceLock;
4#[cfg(target_arch = "wasm32")]
5use std::{borrow::BorrowMut, cell::RefCell};
6
7use glow::HasContext as _;
8
9mod key;
10use key::ConvertKey;
11
12#[cfg(target_arch = "wasm32")]
13struct Ref<T>(T);
14#[cfg(target_arch = "wasm32")]
15unsafe impl<T> Send for Ref<T> {}
16#[cfg(target_arch = "wasm32")]
17unsafe impl<T> Sync for Ref<T> {}
18
19#[cfg(not(target_arch = "wasm32"))]
20static CONTEXT: OnceLock<Arc<glow::Context>> = OnceLock::new();
21#[cfg(target_arch = "wasm32")]
22static CONTEXT: OnceLock<Ref<Arc<glow::Context>>> = OnceLock::new();
23
24#[cfg(target_arch = "wasm32")]
25slotmap::new_key_type! { pub struct WebGlUniformLocationKey; }
26#[cfg(target_arch = "wasm32")]
27unsafe impl Send for WebGlUniformLocationKey {}
28#[cfg(target_arch = "wasm32")]
29unsafe impl Sync for WebGlUniformLocationKey {}
30
31#[cfg(target_arch = "wasm32")]
32static LOCATION: OnceLock<
33    Ref<RefCell<slotmap::SlotMap<WebGlUniformLocationKey, web_sys::WebGlUniformLocation>>>,
34> = OnceLock::new();
35
36#[cfg(not(target_arch = "wasm32"))]
37pub fn set_context(ctx: Arc<glow::Context>) {
38    let _ = CONTEXT.set(ctx);
39}
40
41#[cfg(not(target_arch = "wasm32"))]
42#[inline]
43fn gl() -> &'static glow::Context {
44    CONTEXT.get().unwrap().as_ref()
45}
46
47#[cfg(target_arch = "wasm32")]
48pub fn set_context(ctx: Arc<glow::Context>) {
49    use slotmap::SlotMap;
50    let _ = CONTEXT.set(Ref(ctx));
51    let _ = LOCATION.set(Ref(RefCell::new(SlotMap::with_key())));
52}
53
54#[cfg(target_arch = "wasm32")]
55#[inline]
56fn gl() -> &'static glow::Context {
57    CONTEXT.get().unwrap().0.as_ref()
58}
59
60pub mod types {
61    use std::usize;
62
63    pub type GLfloat = f32;
64    pub type GLenum = u32;
65    pub type GLuint = u32;
66    pub type GLsizei = i32;
67    pub type GLboolean = u8;
68    pub type GLint = i32;
69    pub type GLchar = std::ffi::c_char;
70    pub type GLsizeiptr = usize;
71    pub type GLbitfield = u32;
72}
73
74pub use glow::{
75    ARRAY_BUFFER, BLEND, CLAMP_TO_BORDER, CLAMP_TO_EDGE, COLOR_BUFFER_BIT, COMPILE_STATUS,
76    CONSTANT_COLOR, CULL_FACE, DEPTH_BUFFER_BIT, DST_ALPHA, DST_COLOR, DYNAMIC_DRAW, EQUAL, FALSE,
77    FLOAT, FRAGMENT_SHADER, FRAMEBUFFER_SRGB, FUNC_ADD, FUNC_SUBTRACT, INCR, KEEP, LINEAR,
78    LINEAR_MIPMAP_LINEAR, LINEAR_MIPMAP_NEAREST, MIRRORED_REPEAT, NEAREST, NEAREST_MIPMAP_LINEAR,
79    NEAREST_MIPMAP_NEAREST, NEVER, NOTEQUAL, ONE, ONE_MINUS_SRC_ALPHA, REPEAT, REPLACE, RGBA,
80    SCISSOR_TEST, SRC_ALPHA, SRC_COLOR, SRGB_ALPHA, STENCIL_BUFFER_BIT, STENCIL_TEST, TEXTURE_2D,
81    TEXTURE_BORDER_COLOR, TEXTURE_MAG_FILTER, TEXTURE_MIN_FILTER, TEXTURE_WRAP_S, TEXTURE_WRAP_T,
82    TRIANGLES, TRUE, UNSIGNED_BYTE, VERTEX_SHADER, ZERO,
83};
84
85#[allow(non_snake_case)]
86pub unsafe fn DeleteTextures(n: types::GLsizei, textures: *const types::GLuint) {
87    let textures = std::slice::from_raw_parts(textures, n as usize);
88    for texture in textures {
89        gl().delete_texture(glow::Texture::from_key(*texture));
90    }
91}
92
93#[allow(non_snake_case)]
94pub unsafe fn GenTextures(_: types::GLsizei, textures: *mut types::GLuint) {
95    if let Ok(id) = gl().create_texture() {
96        *textures = glow::Texture::to_key(&id);
97    }
98}
99
100#[allow(non_snake_case)]
101pub unsafe fn BindTexture(target: u32, texture: types::GLuint) {
102    gl().bind_texture(target, Some(glow::Texture::from_key(texture)));
103}
104
105#[allow(non_snake_case)]
106pub unsafe fn TexParameteri(target: u32, parameter: u32, value: i32) {
107    gl().tex_parameter_i32(target, parameter, value);
108}
109
110#[allow(non_snake_case)]
111pub unsafe fn TexParameterfv(target: u32, parameter: u32, values: *const f32) {
112    let values = std::slice::from_raw_parts(values, 4);
113    gl().tex_parameter_f32_slice(target, parameter, values);
114}
115
116#[allow(non_snake_case)]
117pub unsafe fn GenerateMipmap(target: u32) {
118    gl().generate_mipmap(target);
119}
120
121#[allow(non_snake_case)]
122pub unsafe fn TexImage2D(
123    target: u32,
124    level: i32,
125    internal_format: i32,
126    width: i32,
127    height: i32,
128    border: i32,
129    format: u32,
130    ty: u32,
131    pixels: *const u8,
132) {
133    debug_assert!(format == glow::SRGB_ALPHA || format == glow::RGBA);
134    let pixels = std::slice::from_raw_parts(pixels, (width * height * 4) as usize);
135    gl().tex_image_2d(
136        target,
137        level,
138        internal_format,
139        width,
140        height,
141        border,
142        format,
143        ty,
144        glow::PixelUnpackData::Slice(Some(pixels)),
145    );
146}
147
148#[allow(non_snake_case)]
149pub unsafe fn TexSubImage2D(
150    target: u32,
151    level: i32,
152    x_offset: i32,
153    y_offset: i32,
154    width: i32,
155    height: i32,
156    format: u32,
157    ty: u32,
158    pixels: *const u8,
159) {
160    debug_assert!(format == glow::SRGB_ALPHA || format == glow::RGBA);
161    let pixels = std::slice::from_raw_parts(pixels, (width * height * 4) as usize);
162    gl().tex_sub_image_2d(
163        target,
164        level,
165        x_offset,
166        y_offset,
167        width,
168        height,
169        format,
170        ty,
171        glow::PixelUnpackData::Slice(Some(pixels)),
172    );
173}
174
175#[allow(non_snake_case)]
176pub unsafe fn GetUniformLocation(
177    program: types::GLuint,
178    name: *const types::GLchar,
179) -> types::GLint {
180    let name = char_ptr_to_str(name);
181    let location = gl().get_uniform_location(glow::Program::from_key(program), name);
182
183    if let Some(location) = location {
184        #[cfg(target_arch = "wasm32")]
185        {
186            if let Some(Ref(slot)) = LOCATION.get().borrow_mut().as_mut() {
187                let key = slot.borrow_mut().insert(location);
188                return WebGlUniformLocationKey::to_key(&key) as types::GLint;
189            }
190        }
191        #[cfg(not(target_arch = "wasm32"))]
192        {
193            return glow::UniformLocation::to_key(&location) as types::GLint;
194        }
195    }
196
197    return 0;
198}
199
200#[cfg(target_arch = "wasm32")]
201unsafe fn get_uniform<F>(location: types::GLuint, f: F)
202where
203    F: FnOnce(&web_sys::WebGlUniformLocation),
204{
205    let key = WebGlUniformLocationKey::from_key(location);
206    if let Some(Ref(slot)) = LOCATION.get().borrow_mut().as_mut() {
207        if let Some(location) = slot.borrow_mut().get(key) {
208            f(&location);
209        }
210    }
211}
212
213#[cfg(not(target_arch = "wasm32"))]
214unsafe fn get_uniform<F>(location: types::GLuint, f: F)
215where
216    F: FnOnce(&glow::UniformLocation),
217{
218    let location = glow::UniformLocation::from_key(location);
219    f(&location);
220}
221
222#[allow(non_snake_case)]
223pub unsafe fn CreateProgram() -> types::GLuint {
224    match gl().create_program() {
225        Ok(program) => return glow::Program::to_key(&program),
226        Err(_) => {}
227    };
228    return 0;
229}
230
231#[allow(non_snake_case)]
232pub unsafe fn LinkProgram(program: types::GLuint) {
233    let program = glow::Program::from_key(program);
234    gl().link_program(program);
235}
236
237#[allow(non_snake_case)]
238pub unsafe fn UseProgram(program: types::GLuint) {
239    let program = glow::Program::from_key(program);
240    gl().use_program(Some(program));
241}
242
243#[allow(non_snake_case)]
244pub unsafe fn DeleteProgram(program: types::GLuint) {
245    let program = glow::Program::from_key(program);
246    gl().delete_program(program);
247}
248
249#[allow(non_snake_case)]
250pub unsafe fn ProgramUniform1f(program: types::GLuint, location: types::GLint, value: f32) {
251    let program = glow::Program::from_key(program);
252    gl().use_program(Some(program));
253
254    get_uniform(location as u32, |location| {
255        gl().uniform_1_f32(Some(&location), value);
256    });
257}
258
259#[allow(non_snake_case)]
260pub unsafe fn ProgramUniform1i(program: types::GLuint, location: types::GLint, value: i32) {
261    let program = glow::Program::from_key(program);
262    gl().use_program(Some(program));
263    get_uniform(location as u32, |location| {
264        gl().uniform_1_i32(Some(&location), value);
265    });
266}
267
268#[allow(non_snake_case)]
269pub unsafe fn ProgramUniform2f(program: types::GLuint, location: types::GLint, x: f32, y: f32) {
270    let program = glow::Program::from_key(program);
271    gl().use_program(Some(program));
272    get_uniform(location as u32, |location| {
273        gl().uniform_2_f32(Some(&location), x, y);
274    });
275}
276
277#[allow(non_snake_case)]
278pub unsafe fn ProgramUniform3f(
279    program: types::GLuint,
280    location: types::GLint,
281    x: f32,
282    y: f32,
283    z: f32,
284) {
285    let program = glow::Program::from_key(program);
286    gl().use_program(Some(program));
287
288    get_uniform(location as u32, |location| {
289        gl().uniform_3_f32(Some(&location), x, y, z);
290    });
291}
292
293#[allow(non_snake_case)]
294pub unsafe fn ProgramUniform4f(
295    program: types::GLuint,
296    location: types::GLint,
297    x: f32,
298    y: f32,
299    z: f32,
300    w: f32,
301) {
302    let program = glow::Program::from_key(program);
303    gl().use_program(Some(program));
304    get_uniform(location as u32, |location| {
305        gl().uniform_4_f32(Some(&location), x, y, z, w);
306    });
307}
308
309#[allow(non_snake_case)]
310pub unsafe fn Uniform4f(location: types::GLint, x: f32, y: f32, z: f32, w: f32) {
311    get_uniform(location as u32, |location| {
312        gl().uniform_4_f32(Some(&location), x, y, z, w);
313    });
314}
315
316#[allow(non_snake_case)]
317pub unsafe fn ProgramUniformMatrix2fv(
318    program: types::GLuint,
319    location: types::GLint,
320    count: types::GLint,
321    transpose: types::GLboolean,
322    value: *const f32,
323) {
324    let program = glow::Program::from_key(program);
325    gl().use_program(Some(program));
326    let value = std::slice::from_raw_parts(value, count as usize * 4);
327
328    get_uniform(location as u32, |location| {
329        gl().uniform_matrix_2_f32_slice(Some(&location), transpose == TRUE, value);
330    });
331}
332
333#[allow(non_snake_case)]
334pub unsafe fn ProgramUniformMatrix3fv(
335    program: types::GLuint,
336    location: types::GLint,
337    count: types::GLint,
338    transpose: types::GLboolean,
339    value: *const f32,
340) {
341    let program = glow::Program::from_key(program);
342    gl().use_program(Some(program));
343    let value = std::slice::from_raw_parts(value, count as usize * 9);
344
345    get_uniform(location as u32, |location| {
346        gl().uniform_matrix_3_f32_slice(Some(&location), transpose == TRUE, value);
347    });
348}
349
350#[allow(non_snake_case)]
351pub unsafe fn ProgramUniformMatrix4fv(
352    program: types::GLuint,
353    location: types::GLint,
354    count: types::GLint,
355    transpose: types::GLboolean,
356    value: *const f32,
357) {
358    let program = glow::Program::from_key(program);
359    gl().use_program(Some(program));
360    let value = std::slice::from_raw_parts(value, count as usize * 16);
361
362    get_uniform(location as u32, |location| {
363        gl().uniform_matrix_4_f32_slice(Some(&location), transpose == TRUE, value);
364    });
365}
366
367#[allow(non_snake_case)]
368pub unsafe fn DeleteBuffers(_: types::GLsizei, buffer: &types::GLuint) {
369    let buffer = glow::Buffer::from_key(*buffer);
370    gl().delete_buffer(buffer)
371}
372
373#[allow(non_snake_case)]
374pub unsafe fn BindVertexArray(array: types::GLuint) {
375    if array > 0 {
376        let array = glow::VertexArray::from_key(array);
377        gl().bind_vertex_array(Some(array));
378    }
379}
380
381#[allow(non_snake_case)]
382pub unsafe fn GenVertexArrays(_: types::GLsizei, arrays: *mut types::GLuint) {
383    match gl().create_vertex_array() {
384        Ok(array) => *arrays = glow::VertexArray::to_key(&array),
385        Err(_) => {}
386    };
387}
388
389#[allow(non_snake_case)]
390pub unsafe fn DeleteVertexArrays(_: types::GLsizei, array: *const types::GLuint) {
391    let array = glow::VertexArray::from_key(*array);
392    gl().delete_vertex_array(array);
393}
394
395#[allow(non_snake_case)]
396pub unsafe fn BindBuffer(target: types::GLenum, buffer: types::GLuint) {
397    let buffer = glow::Buffer::from_key(buffer);
398    gl().bind_buffer(target, Some(buffer));
399}
400
401#[allow(non_snake_case)]
402pub unsafe fn VertexAttribPointer(
403    index: types::GLuint,
404    size: types::GLint,
405    data_type: types::GLenum,
406    normalized: types::GLboolean,
407    stride: types::GLint,
408    offset: *const types::GLint,
409) {
410    let offset = if offset == std::ptr::null() {
411        0
412    } else {
413        *offset
414    };
415    gl().vertex_attrib_pointer_f32(
416        index,
417        size as i32,
418        data_type,
419        normalized == TRUE,
420        stride,
421        offset,
422    );
423}
424
425#[allow(non_snake_case)]
426pub unsafe fn GenBuffers(_: types::GLsizei, buffer: &mut types::GLuint) {
427    if let Ok(buf) = gl().create_buffer() {
428        *buffer = glow::Buffer::to_key(&buf);
429    }
430}
431
432#[allow(non_snake_case)]
433pub unsafe fn EnableVertexAttribArray(location: types::GLuint) {
434    gl().enable_vertex_attrib_array(location);
435}
436
437#[allow(non_snake_case)]
438pub unsafe fn BufferData(
439    target: types::GLenum,
440    size: types::GLsizeiptr,
441    data: *const f64,
442    usage: types::GLenum,
443) {
444    let data: *const u8 = std::mem::transmute(data);
445    let data = std::slice::from_raw_parts(data, size);
446    gl().buffer_data_u8_slice(target, data, usage);
447}
448
449#[allow(non_snake_case)]
450pub unsafe fn CreateShader(shader_type: types::GLenum) -> types::GLuint {
451    if let Ok(shader) = gl().create_shader(shader_type) {
452        return glow::Shader::to_key(&shader);
453    }
454    return 0;
455}
456
457#[allow(non_snake_case)]
458pub unsafe fn ShaderSource(
459    shader: types::GLuint,
460    _count: types::GLsizei,
461    source: *const *const types::GLchar,
462    _length: *const types::GLint,
463) {
464    let shader = glow::Shader::from_key(shader);
465    let source = char_ptr_to_str(*source);
466    gl().shader_source(shader, source);
467}
468
469#[allow(non_snake_case)]
470pub unsafe fn CompileShader(shader: types::GLuint) {
471    let shader = glow::Shader::from_key(shader);
472    gl().compile_shader(shader);
473}
474
475#[allow(non_snake_case)]
476pub unsafe fn GetCompleStatus(shader: types::GLuint, params: *mut types::GLint) {
477    let shader = glow::Shader::from_key(shader);
478    let status = gl().get_shader_compile_status(shader);
479    *params = status as types::GLint;
480}
481
482#[allow(non_snake_case)]
483pub unsafe fn GetShaderInfoLog(shader: types::GLuint) -> String {
484    let shader = glow::Shader::from_key(shader);
485    let log = gl().get_shader_info_log(shader);
486    log
487}
488
489#[allow(non_snake_case)]
490pub unsafe fn AttachShader(program: types::GLuint, shader: types::GLuint) {
491    let program = glow::Program::from_key(program);
492    let shader = glow::Shader::from_key(shader);
493    gl().attach_shader(program, shader);
494}
495
496#[allow(non_snake_case)]
497pub unsafe fn DeleteShader(shader: types::GLuint) {
498    let shader = glow::Shader::from_key(shader);
499    gl().delete_shader(shader);
500}
501
502#[allow(non_snake_case)]
503pub unsafe fn GetAttribLocation(
504    program: types::GLuint,
505    name: *const types::GLchar,
506) -> types::GLint {
507    let program = glow::Program::from_key(program);
508    gl().get_attrib_location(program, char_ptr_to_str(name))
509        .map(|l| l as i32)
510        .unwrap_or(-1)
511}
512
513#[allow(non_snake_case)]
514pub unsafe fn Enable(parameter: types::GLenum) {
515    gl().enable(parameter)
516}
517
518#[allow(non_snake_case)]
519pub unsafe fn Disable(parameter: types::GLenum) {
520    gl().disable(parameter)
521}
522
523#[allow(non_snake_case)]
524pub unsafe fn DrawArrays(mode: types::GLenum, first: types::GLint, count: types::GLsizei) {
525    gl().draw_arrays(mode, first, count)
526}
527
528#[allow(non_snake_case)]
529pub unsafe fn BindFragDataLocation(
530    program: types::GLuint,
531    color: types::GLuint,
532    name: *const types::GLchar,
533) {
534    let program = glow::Program::from_key(program);
535    gl().bind_frag_data_location(program, color, char_ptr_to_str(name))
536}
537
538#[allow(non_snake_case)]
539pub unsafe fn Viewport(
540    x: types::GLint,
541    y: types::GLint,
542    width: types::GLsizei,
543    height: types::GLsizei,
544) {
545    gl().viewport(x, y, width, height)
546}
547
548#[allow(non_snake_case)]
549pub unsafe fn ClearColor(
550    red: types::GLfloat,
551    green: types::GLfloat,
552    blue: types::GLfloat,
553    alpha: types::GLfloat,
554) {
555    gl().clear_color(red, green, blue, alpha);
556}
557
558#[allow(non_snake_case)]
559pub unsafe fn Clear(mask: types::GLbitfield) {
560    gl().clear(mask);
561}
562
563#[allow(non_snake_case)]
564pub unsafe fn ClearStencil(stencil: types::GLint) {
565    gl().clear_stencil(stencil);
566}
567
568#[allow(non_snake_case)]
569pub unsafe fn Scissor(
570    x: types::GLint,
571    y: types::GLint,
572    width: types::GLsizei,
573    height: types::GLsizei,
574) {
575    gl().scissor(x, y, width, height);
576}
577
578#[allow(non_snake_case)]
579pub unsafe fn StencilFunc(func: types::GLenum, ref_: types::GLint, mask: types::GLuint) {
580    gl().stencil_func(func, ref_, mask);
581}
582
583#[allow(non_snake_case)]
584pub unsafe fn StencilMask(mask: types::GLuint) {
585    gl().stencil_mask(mask);
586}
587
588#[allow(non_snake_case)]
589pub unsafe fn StencilOp(fail: types::GLenum, zfail: types::GLenum, zpass: types::GLenum) {
590    gl().stencil_op(fail, zfail, zpass);
591}
592
593#[allow(non_snake_case)]
594pub unsafe fn BlendColor(
595    red: types::GLfloat,
596    green: types::GLfloat,
597    blue: types::GLfloat,
598    alpha: types::GLfloat,
599) {
600    gl().blend_color(red, green, blue, alpha);
601}
602
603#[allow(non_snake_case)]
604pub unsafe fn BlendEquationSeparate(modeRGB: types::GLenum, modeAlpha: types::GLenum) {
605    gl().blend_equation_separate(modeRGB, modeAlpha);
606}
607
608#[allow(non_snake_case)]
609pub unsafe fn BlendFuncSeparate(
610    sfactorRGB: types::GLenum,
611    dfactorRGB: types::GLenum,
612    sfactorAlpha: types::GLenum,
613    dfactorAlpha: types::GLenum,
614) {
615    gl().blend_func_separate(sfactorRGB, dfactorRGB, sfactorAlpha, dfactorAlpha);
616}
617
618#[inline]
619unsafe fn char_ptr_to_str<'a>(ptr: *const types::GLchar) -> &'a str {
620    CStr::from_ptr(ptr).to_str().unwrap_or("")
621}
622
623#[allow(non_snake_case)]
624pub mod Enable {
625    pub fn is_loaded() -> bool {
626        true
627    }
628}