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}