1use std::collections::HashMap;
8use std::ffi::{c_char, c_void};
9
10use wasmtime::Val;
11
12use super::{guest_slice, guest_slice_mut, guest_str, text, Gl, Kind, PIXEL_PACK_BUFFER, PIXEL_UNPACK_BUFFER};
13use crate::angle::{c_string, Angle};
14
15const EXTENSIONS: &[(&str, &[&str])] = &[
17 ("EXT_color_buffer_float", &["GL_EXT_color_buffer_float"]),
18 ("EXT_color_buffer_half_float", &["GL_EXT_color_buffer_half_float"]),
19 ("EXT_float_blend", &["GL_EXT_float_blend"]),
20 ("EXT_texture_filter_anisotropic", &["GL_EXT_texture_filter_anisotropic"]),
21 ("OES_texture_float_linear", &["GL_OES_texture_float_linear"]),
22 (
23 "WEBGL_compressed_texture_s3tc",
24 &["GL_EXT_texture_compression_dxt1", "GL_ANGLE_texture_compression_dxt3", "GL_ANGLE_texture_compression_dxt5"],
25 ),
26 ("WEBGL_compressed_texture_s3tc_srgb", &["GL_EXT_texture_compression_s3tc_srgb"]),
27 ("EXT_texture_compression_bptc", &["GL_EXT_texture_compression_bptc"]),
28 ("EXT_texture_compression_rgtc", &["GL_EXT_texture_compression_rgtc"]),
29 ("WEBGL_compressed_texture_etc", &["GL_ANGLE_compressed_texture_etc"]),
30 ("WEBGL_compressed_texture_astc", &["GL_KHR_texture_compression_astc_ldr"]),
31];
32
33const GL_EXTENSIONS: u32 = 0x1F03;
34const GL_NUM_EXTENSIONS: u32 = 0x821D;
35const GL_REQUESTABLE_EXTENSIONS_ANGLE: u32 = 0x93A8;
36const INVALID_ENUM: u32 = 0x0500;
37const INVALID_VALUE: u32 = 0x0501;
38
39pub struct Backend {
41 pub angle: Angle,
42 names: HashMap<(Kind, u32), u32>,
44 rev: HashMap<(Kind, u32), u32>,
45 syncs: HashMap<u32, usize>,
46 uniforms: HashMap<(u32, i32), (i32, u32)>,
48 presented: bool,
50}
51
52impl Backend {
53 pub fn new(angle: Angle) -> Backend {
54 Backend { angle, names: HashMap::new(), rev: HashMap::new(), syncs: HashMap::new(), uniforms: HashMap::new(), presented: false }
55 }
56 fn obj(&self, k: Kind, n: u32) -> u32 {
57 if n == 0 { 0 } else { self.names.get(&(k, n)).copied().unwrap_or(0) }
58 }
59 fn model_name(&self, k: Kind, gl: u32) -> u32 {
60 if gl == 0 { 0 } else { self.rev.get(&(k, gl)).copied().unwrap_or(0) }
61 }
62 fn put_name(&mut self, k: Kind, n: u32, gl: u32) {
63 self.names.insert((k, n), gl);
64 self.rev.insert((k, gl), n);
65 }
66 fn take(&mut self, k: Kind, n: u32) -> Option<u32> {
67 let gl = self.names.remove(&(k, n))?;
68 self.rev.remove(&(k, gl));
69 Some(gl)
70 }
71 fn gl_extensions(&self) -> Vec<String> {
73 let g = &self.angle.gl;
74 let mut out = Vec::new();
75 unsafe {
76 let mut n = 0;
77 (g.glGetIntegerv)(GL_NUM_EXTENSIONS, &mut n as *mut i32 as *mut c_void);
78 for i in 0..n.max(0) as u32 {
79 out.push(cstr((g.glGetStringi)(GL_EXTENSIONS, i)));
80 }
81 out.extend(cstr((g.glGetString)(GL_REQUESTABLE_EXTENSIONS_ANGLE)).split_whitespace().map(str::to_owned));
82 }
83 out
84 }
85 fn webgl_extensions(&self) -> Vec<&'static str> {
87 let have = self.gl_extensions();
88 EXTENSIONS.iter().filter(|(_, gl)| gl.iter().all(|e| have.iter().any(|h| h == e))).map(|(w, _)| *w).collect()
89 }
90 fn enable_extension(&self, name: &str) -> bool {
91 let Some((_, gl)) = EXTENSIONS.iter().find(|(w, _)| *w == name) else { return false };
92 let have = self.gl_extensions();
93 if !gl.iter().all(|e| have.iter().any(|h| h == e)) {
94 return false;
95 }
96 for e in *gl {
97 let c = c_string(e);
98 unsafe { (self.angle.gl.glRequestExtensionANGLE)(c.as_ptr() as *const c_void) };
99 }
100 true
101 }
102 fn drain(&self, model: &mut Gl) -> u32 {
105 let mut last = 0;
106 loop {
107 let e = unsafe { (self.angle.gl.glGetError)() };
108 if e == 0 {
109 return last;
110 }
111 model.error(e);
112 last = e;
113 }
114 }
115 fn int(&self, f: impl FnOnce(*mut c_void)) -> i32 {
116 let mut v = 0i32;
117 f(&mut v as *mut i32 as *mut c_void);
118 v
119 }
120 fn info_log(&self, shader: bool, obj: u32) -> String {
121 let g = &self.angle.gl;
122 unsafe {
123 let len = self.int(|p| if shader { (g.glGetShaderiv)(obj, 0x8B84, p) } else { (g.glGetProgramiv)(obj, 0x8B84, p) });
124 if len <= 0 {
125 return String::new();
126 }
127 let mut buf = vec![0u8; len as usize];
128 let mut got = 0;
129 if shader {
130 (g.glGetShaderInfoLog)(obj, len, &mut got as *mut i32 as *mut c_void, buf.as_mut_ptr() as *mut c_void);
131 } else {
132 (g.glGetProgramInfoLog)(obj, len, &mut got as *mut i32 as *mut c_void, buf.as_mut_ptr() as *mut c_void);
133 }
134 buf.truncate(got.max(0) as usize);
135 String::from_utf8_lossy(&buf).into_owned()
136 }
137 }
138 fn uniform_components(&self, program: u32, name: &str) -> u32 {
140 let g = &self.angle.gl;
141 let base = name.split('[').next().unwrap_or(name);
142 unsafe {
143 let n = self.int(|p| (g.glGetProgramiv)(program, 0x8B86, p));
144 let max = self.int(|p| (g.glGetProgramiv)(program, 0x8B87, p)).max(1);
145 for i in 0..n.max(0) as u32 {
146 let mut buf = vec![0u8; max as usize];
147 let (mut len, mut size, mut ty) = (0i32, 0i32, 0u32);
148 (g.glGetActiveUniform)(
149 program, i, max, &mut len as *mut i32 as *mut c_void, &mut size as *mut i32 as *mut c_void,
150 &mut ty as *mut u32 as *mut c_void, buf.as_mut_ptr() as *mut c_void,
151 );
152 let u = String::from_utf8_lossy(&buf[..len.max(0) as usize]).into_owned();
153 if u == name || u.split('[').next() == Some(base) {
154 return type_components(ty);
155 }
156 }
157 }
158 16
159 }
160}
161
162fn type_components(ty: u32) -> u32 {
164 match ty {
165 0x8B50 | 0x8B53 | 0x8DC6 | 0x8B57 => 2, 0x8B51 | 0x8B54 | 0x8DC7 | 0x8B58 => 3,
167 0x8B52 | 0x8B55 | 0x8DC8 | 0x8B59 | 0x8B5A => 4, 0x8B5B => 9,
169 0x8B5C => 16,
170 0x8B65 | 0x8B66 => 6, 0x8B67 | 0x8B68 => 8,
172 0x8B69 | 0x8B6A => 12,
173 _ => 1,
174 }
175}
176
177fn cstr(p: *const c_void) -> String {
178 if p.is_null() {
179 return String::new();
180 }
181 unsafe { std::ffi::CStr::from_ptr(p as *const c_char) }.to_string_lossy().into_owned()
182}
183
184fn param_count(g: &crate::gles::Gles, pname: u32) -> usize {
186 match pname {
187 0x0D3A | 0x846D | 0x846E | 0x0B70 => 2,
188 0x0BA2 | 0x0C10 | 0x0C22 | 0x0C23 | 0x8005 => 4,
189 0x86A3 => {
190 let mut n = 0i32;
191 unsafe { (g.glGetIntegerv)(0x86A2, &mut n as *mut i32 as *mut c_void) };
192 n.max(0) as usize
193 }
194 _ => 1,
195 }
196}
197
198fn put_f(mem: &mut [u8], dst: u32, count: u32, v: &[f32]) -> wasmtime::Result<i32> {
199 let n = v.len().min(count as usize);
200 if n > 0 {
201 let out = guest_slice_mut(mem, dst, n as u64 * 4)?;
202 for (i, x) in v[..n].iter().enumerate() {
203 out[i * 4..i * 4 + 4].copy_from_slice(&x.to_le_bytes());
204 }
205 }
206 Ok(v.len() as i32)
207}
208
209fn put_i(mem: &mut [u8], dst: u32, count: u32, v: &[i64], wide: bool) -> wasmtime::Result<i32> {
210 let n = v.len().min(count as usize);
211 let size = if wide { 8 } else { 4 };
212 if n > 0 {
213 let out = guest_slice_mut(mem, dst, (n * size) as u64)?;
214 for (i, x) in v[..n].iter().enumerate() {
215 if wide {
216 out[i * 8..i * 8 + 8].copy_from_slice(&x.to_le_bytes());
217 } else {
218 out[i * 4..i * 4 + 4].copy_from_slice(&(*x as i32).to_le_bytes());
219 }
220 }
221 }
222 Ok(v.len() as i32)
223}
224
225impl Gl {
226 pub fn attach(&mut self, angle: Angle) {
228 self.backend = Some(Box::new(Backend::new(angle)));
229 }
230 pub fn has_backend(&self) -> bool {
231 self.backend.is_some()
232 }
233 pub fn end_frame(&mut self, show: bool) {
235 if let Some(b) = &mut self.backend {
236 if show && !b.presented {
237 b.angle.swap();
238 }
239 b.presented = false;
240 }
241 }
242 pub fn read_frame(&self) -> Option<(u32, u32, Vec<u8>)> {
244 self.backend.as_ref().map(|b| b.angle.read_frame())
245 }
246 pub fn size(&self) -> Option<(u32, u32)> {
248 self.backend.as_ref().map(|b| b.angle.size())
249 }
250 pub fn renderer(&self) -> Option<&str> {
251 self.backend.as_ref().map(|b| b.angle.renderer.as_str())
252 }
253}
254
255pub(super) fn forward(
258 be: &mut Backend,
259 model: &mut Gl,
260 name: &str,
261 args: &[Val],
262 results: &mut [Val],
263 mem: &mut [u8],
264) -> wasmtime::Result<()> {
265 let a = |i: usize| -> i32 {
266 match args[i] {
267 Val::I32(v) => v,
268 Val::I64(v) => v as i32,
269 _ => 0,
270 }
271 };
272 let u = |i: usize| a(i) as u32;
273 let f = |i: usize| match args[i] {
274 Val::F32(b) => f32::from_bits(b),
275 _ => 0.0,
276 };
277 let model_result = match results.first() {
278 Some(Val::I32(v)) => *v as i64,
279 _ => 0,
280 };
281 let mut ret = |v: i64| {
282 if let Some(r) = results.first_mut() {
283 *r = Val::I32(v as i32);
284 }
285 };
286 let base = mem.as_mut_ptr();
287 let at = |p: u32| -> *const c_void { if p == 0 { std::ptr::null() } else { unsafe { base.add(p as usize) as *const c_void } } };
288 let at_mut = |p: u32| -> *mut c_void { unsafe { base.add(p as usize) as *mut c_void } };
289 let off = |o: u32| o as usize as *const c_void;
290 let g = &be.angle.gl as *const crate::gles::Gles;
291 let g = unsafe { &*g };
292 use Kind::*;
293 unsafe {
294 match name {
295 "width" => ret(be.angle.size().0 as i64),
297 "height" => ret(be.angle.size().1 as i64),
298 "present" => {
299 be.angle.swap();
300 be.presented = true;
301 }
302 "get_error" => {
303 if model_result == 0 {
304 ret((g.glGetError)() as i64)
305 }
306 }
307 "get_string" => {
308 let s = if u(0) == GL_EXTENSIONS { be.webgl_extensions().join(" ") } else { cstr((g.glGetString)(u(0))) };
309 ret(text(mem, u(1), u(2), &s)? as i64)
310 }
311 "enable_extension" => {
312 let n = guest_str(mem, u(0), u(1))?;
313 ret(be.enable_extension(&n) as i64)
314 }
315 "get_integerv" | "get_floatv" | "get_integer64v" => {
316 let pname = u(0);
317 if model.model_param(pname).is_some() {
318 return Ok(());
319 }
320 be.drain(model);
321 let n = param_count(g, pname).max(1);
322 let (dst, count) = (u(1), u(2));
323 match name {
324 "get_floatv" => {
325 let mut v = vec![0f32; n.max(16)];
326 (g.glGetFloatv)(pname, v.as_mut_ptr() as *mut c_void);
327 if (g.glGetError)() == INVALID_ENUM {
328 model.error(INVALID_ENUM);
329 {
330 ret(-1);
331 return Ok(());
332 }
333 }
334 v.truncate(n);
335 ret(put_f(mem, dst, count, &v)? as i64)
336 }
337 _ => {
338 let mut v = vec![0i64; n.max(16)];
339 (g.glGetInteger64v)(pname, v.as_mut_ptr() as *mut c_void);
340 if (g.glGetError)() == INVALID_ENUM {
341 model.error(INVALID_ENUM);
342 {
343 ret(-1);
344 return Ok(());
345 }
346 }
347 v.truncate(n);
348 match pname {
350 0x8919 => v[0] = be.model_name(Sampler, v[0] as u32) as i64,
351 0x8E25 => v[0] = be.model_name(TransformFeedback, v[0] as u32) as i64,
352 _ => {}
353 }
354 ret(put_i(mem, dst, count, &v, name == "get_integer64v")? as i64)
355 }
356 }
357 }
358 "get_integeri_v" => {
359 be.drain(model);
360 let mut v = 0i64;
361 (g.glGetInteger64i_v)(u(0), u(1), &mut v as *mut i64 as *mut c_void);
362 if (g.glGetError)() != 0 {
363 model.error(INVALID_ENUM);
364 {
365 ret(-1);
366 return Ok(());
367 }
368 }
369 if u(0) == 0x8A28 || u(0) == 0x8C8F {
370 v = be.model_name(Buffer, v as u32) as i64;
371 }
372 ret(put_i(mem, u(2), u(3), &[v], false)? as i64)
373 }
374 "get_internalformativ" => {
375 let (t, fmt, pname) = (u(0), u(1), u(2));
376 let n = if pname == 0x80A9 { be.int(|p| (g.glGetInternalformativ)(t, fmt, 0x9380, 1, p)).max(0) as usize } else { 1 };
377 let mut v = vec![0i32; n.max(1)];
378 (g.glGetInternalformativ)(t, fmt, pname, n as i32, v.as_mut_ptr() as *mut c_void);
379 v.truncate(n);
380 let v: Vec<i64> = v.into_iter().map(i64::from).collect();
381 ret(put_i(mem, u(3), u(4), &v, false)? as i64)
382 }
383 "get_shader_precision_format" => {
384 let (mut range, mut precision) = ([0i32; 2], 0i32);
385 (g.glGetShaderPrecisionFormat)(u(0), u(1), range.as_mut_ptr() as *mut c_void, &mut precision as *mut i32 as *mut c_void);
386 put_i(mem, u(2), 3, &[range[0] as i64, range[1] as i64, precision as i64], false)?;
387 }
388
389 "active_texture" => (g.glActiveTexture)(u(0)),
391 "blend_color" => (g.glBlendColor)(f(0), f(1), f(2), f(3)),
392 "blend_equation" => (g.glBlendEquation)(u(0)),
393 "blend_equation_separate" => (g.glBlendEquationSeparate)(u(0), u(1)),
394 "blend_func" => (g.glBlendFunc)(u(0), u(1)),
395 "blend_func_separate" => (g.glBlendFuncSeparate)(u(0), u(1), u(2), u(3)),
396 "clear" => (g.glClear)(u(0)),
397 "clear_color" => (g.glClearColor)(f(0), f(1), f(2), f(3)),
398 "clear_depthf" => (g.glClearDepthf)(f(0)),
399 "clear_stencil" => (g.glClearStencil)(a(0)),
400 "color_mask" => (g.glColorMask)((u(0) != 0) as u8, (u(1) != 0) as u8, (u(2) != 0) as u8, (u(3) != 0) as u8),
401 "cull_face" => (g.glCullFace)(u(0)),
402 "depth_func" => (g.glDepthFunc)(u(0)),
403 "depth_mask" => (g.glDepthMask)((u(0) != 0) as u8),
404 "depth_rangef" => (g.glDepthRangef)(f(0), f(1)),
405 "disable" => (g.glDisable)(u(0)),
406 "enable" => (g.glEnable)(u(0)),
407 "is_enabled" => ret((g.glIsEnabled)(u(0)) as i64),
408 "front_face" => (g.glFrontFace)(u(0)),
409 "hint" => (g.glHint)(u(0), u(1)),
410 "line_width" => (g.glLineWidth)(f(0)),
411 "pixel_storei" => {
412 if !(0x9240..=0x9244).contains(&u(0)) {
414 (g.glPixelStorei)(u(0), a(1))
415 }
416 }
417 "polygon_offset" => (g.glPolygonOffset)(f(0), f(1)),
418 "sample_coverage" => (g.glSampleCoverage)(f(0), (u(1) != 0) as u8),
419 "scissor" => (g.glScissor)(a(0), a(1), a(2), a(3)),
420 "viewport" => (g.glViewport)(a(0), a(1), a(2), a(3)),
421 "stencil_func" => (g.glStencilFunc)(u(0), a(1), u(2)),
422 "stencil_func_separate" => (g.glStencilFuncSeparate)(u(0), u(1), a(2), u(3)),
423 "stencil_mask" => (g.glStencilMask)(u(0)),
424 "stencil_mask_separate" => (g.glStencilMaskSeparate)(u(0), u(1)),
425 "stencil_op" => (g.glStencilOp)(u(0), u(1), u(2)),
426 "stencil_op_separate" => (g.glStencilOpSeparate)(u(0), u(1), u(2), u(3)),
427 "finish" => (g.glFinish)(),
428 "flush" => (g.glFlush)(),
429
430 "create_buffer" | "create_texture" | "create_vertex_array" | "create_sampler" | "create_framebuffer"
432 | "create_renderbuffer" | "create_query" | "create_transform_feedback" => {
433 let (kind, make): (Kind, unsafe extern "system" fn(i32, *mut c_void)) = match name {
434 "create_buffer" => (Buffer, g.glGenBuffers),
435 "create_texture" => (Texture, g.glGenTextures),
436 "create_vertex_array" => (VertexArray, g.glGenVertexArrays),
437 "create_sampler" => (Sampler, g.glGenSamplers),
438 "create_framebuffer" => (Framebuffer, g.glGenFramebuffers),
439 "create_renderbuffer" => (Renderbuffer, g.glGenRenderbuffers),
440 "create_query" => (Query, g.glGenQueries),
441 _ => (TransformFeedback, g.glGenTransformFeedbacks),
442 };
443 let mut x = 0u32;
444 make(1, &mut x as *mut u32 as *mut c_void);
445 be.put_name(kind, model_result as u32, x);
446 }
447 "delete_buffer" | "delete_texture" | "delete_vertex_array" | "delete_sampler" | "delete_framebuffer"
448 | "delete_renderbuffer" | "delete_query" | "delete_transform_feedback" => {
449 let (kind, del): (Kind, unsafe extern "system" fn(i32, *const c_void)) = match name {
450 "delete_buffer" => (Buffer, g.glDeleteBuffers),
451 "delete_texture" => (Texture, g.glDeleteTextures),
452 "delete_vertex_array" => (VertexArray, g.glDeleteVertexArrays),
453 "delete_sampler" => (Sampler, g.glDeleteSamplers),
454 "delete_framebuffer" => (Framebuffer, g.glDeleteFramebuffers),
455 "delete_renderbuffer" => (Renderbuffer, g.glDeleteRenderbuffers),
456 "delete_query" => (Query, g.glDeleteQueries),
457 _ => (TransformFeedback, g.glDeleteTransformFeedbacks),
458 };
459 if let Some(x) = be.take(kind, u(0)) {
460 del(1, &x as *const u32 as *const c_void);
461 }
462 }
463 "create_shader" => {
464 let x = (g.glCreateShader)(u(0));
465 be.put_name(Shader, model_result as u32, x);
466 }
467 "create_program" => {
468 let x = (g.glCreateProgram)();
469 be.put_name(Program, model_result as u32, x);
470 }
471 "delete_shader" => {
472 if let Some(x) = be.take(Shader, u(0)) {
473 (g.glDeleteShader)(x)
474 }
475 }
476 "delete_program" => {
477 be.uniforms.retain(|(p, _), _| *p != u(0));
478 if let Some(x) = be.take(Program, u(0)) {
479 (g.glDeleteProgram)(x)
480 }
481 }
482 "fence_sync" => {
483 let s = (g.glFenceSync)(u(0), u(1));
484 be.syncs.insert(model_result as u32, s as usize);
485 }
486 "delete_sync" => {
487 if let Some(s) = be.syncs.remove(&u(0)) {
488 (g.glDeleteSync)(s as *const c_void)
489 }
490 }
491 "is_buffer" | "is_texture" | "is_vertex_array" | "is_sampler" | "is_framebuffer" | "is_renderbuffer"
493 | "is_shader" | "is_program" | "is_query" | "is_sync" | "is_transform_feedback" | "frame_shown"
494 | "get_shader_source" | "get_queryiv" | "client_wait_sync" | "wait_sync" | "get_synciv" => {}
495
496 "bind_buffer" => (g.glBindBuffer)(u(0), be.obj(Buffer, u(1))),
498 "bind_buffer_base" => (g.glBindBufferBase)(u(0), u(1), be.obj(Buffer, u(2))),
499 "bind_buffer_range" => (g.glBindBufferRange)(u(0), u(1), be.obj(Buffer, u(2)), u(3) as isize, u(4) as isize),
500 "buffer_data" => (g.glBufferData)(u(0), u(2) as isize, at(u(1)), u(3)),
501 "buffer_sub_data" => (g.glBufferSubData)(u(0), u(1) as isize, u(3) as isize, at(u(2))),
502 "copy_buffer_sub_data" => (g.glCopyBufferSubData)(u(0), u(1), u(2) as isize, u(3) as isize, u(4) as isize),
503 "get_buffer_sub_data" => {
504 let (t, o, dst, len) = (u(0), u(1), u(2), u(3));
506 if len > 0 {
507 let p = (g.glMapBufferRange)(t, o as isize, len as isize, 0x0001);
508 if !p.is_null() {
509 let out = guest_slice_mut(mem, dst, len as u64)?;
510 std::ptr::copy_nonoverlapping(p as *const u8, out.as_mut_ptr(), len as usize);
511 (g.glUnmapBuffer)(t);
512 }
513 }
514 }
515 "get_buffer_parameteriv" => ret(be.int(|p| (g.glGetBufferParameteriv)(u(0), u(1), p)) as i64),
516
517 "bind_vertex_array" => (g.glBindVertexArray)(be.obj(VertexArray, u(0))),
519 "enable_vertex_attrib_array" => (g.glEnableVertexAttribArray)(u(0)),
520 "disable_vertex_attrib_array" => (g.glDisableVertexAttribArray)(u(0)),
521 "vertex_attrib_pointer" => (g.glVertexAttribPointer)(u(0), a(1), u(2), (u(3) != 0) as u8, a(4), off(u(5))),
522 "vertex_attrib_ipointer" => (g.glVertexAttribIPointer)(u(0), a(1), u(2), a(3), off(u(4))),
523 "vertex_attrib_divisor" => (g.glVertexAttribDivisor)(u(0), u(1)),
524 "vertex_attrib4f" => (g.glVertexAttrib4f)(u(0), f(1), f(2), f(3), f(4)),
525 "vertex_attribi4i" => (g.glVertexAttribI4i)(u(0), a(1), a(2), a(3), a(4)),
526 "vertex_attribi4ui" => (g.glVertexAttribI4ui)(u(0), u(1), u(2), u(3), u(4)),
527 "get_vertex_attribiv" => {
528 let v = be.int(|p| (g.glGetVertexAttribiv)(u(0), u(1), p));
529 ret(if u(1) == 0x889F { be.model_name(Buffer, v as u32) as i64 } else { v as i64 })
530 }
531 "get_vertex_attribfv" => {
532 let mut v = [0f32; 4];
533 (g.glGetVertexAttribfv)(u(0), u(1), v.as_mut_ptr() as *mut c_void);
534 let n = if u(1) == 0x8626 { 4 } else { 1 };
535 ret(put_f(mem, u(2), u(3), &v[..n])? as i64)
536 }
537 "get_vertex_attrib_offset" => {
538 let mut p: *mut c_void = std::ptr::null_mut();
539 (g.glGetVertexAttribPointerv)(u(0), u(1), &mut p as *mut *mut c_void as *mut c_void);
540 ret(p as usize as i64)
541 }
542
543 "draw_arrays" => (g.glDrawArrays)(u(0), a(1), a(2)),
545 "draw_elements" => (g.glDrawElements)(u(0), a(1), u(2), off(u(3))),
546 "draw_arrays_instanced" => (g.glDrawArraysInstanced)(u(0), a(1), a(2), a(3)),
547 "draw_elements_instanced" => (g.glDrawElementsInstanced)(u(0), a(1), u(2), off(u(3)), a(4)),
548 "draw_range_elements" => (g.glDrawRangeElements)(u(0), u(1), u(2), a(3), u(4), off(u(5))),
549 "draw_buffers" => (g.glDrawBuffers)(a(1), at(u(0))),
550 "clear_bufferiv" => (g.glClearBufferiv)(u(0), a(1), at(u(2))),
551 "clear_bufferuiv" => (g.glClearBufferuiv)(u(0), a(1), at(u(2))),
552 "clear_bufferfv" => (g.glClearBufferfv)(u(0), a(1), at(u(2))),
553 "clear_bufferfi" => (g.glClearBufferfi)(u(0), a(1), f(2), a(3)),
554
555 "bind_texture" => (g.glBindTexture)(u(0), be.obj(Texture, u(1))),
557 "tex_parameteri" => (g.glTexParameteri)(u(0), u(1), a(2)),
558 "tex_parameterf" => (g.glTexParameterf)(u(0), u(1), f(2)),
559 "get_tex_parameteriv" => ret(be.int(|p| (g.glGetTexParameteriv)(u(0), u(1), p)) as i64),
560 "get_tex_parameterfv" | "get_sampler_parameterfv" => {
561 let mut v = 0f32;
562 if name == "get_tex_parameterfv" {
563 (g.glGetTexParameterfv)(u(0), u(1), &mut v as *mut f32 as *mut c_void);
564 } else {
565 (g.glGetSamplerParameterfv)(be.obj(Sampler, u(0)), u(1), &mut v as *mut f32 as *mut c_void);
566 }
567 if let Some(r) = results.first_mut() {
568 *r = Val::F32(v.to_bits());
569 }
570 }
571 "tex_image_2d" | "tex_image_3d" | "tex_sub_image_2d" | "tex_sub_image_3d" => {
572 let n = args.len();
574 let (ptr, len) = (u(n - 2), u(n - 1));
575 let px = if model.bound_buffer(PIXEL_UNPACK_BUFFER) != 0 { off(len) } else { at(ptr) };
576 match name {
577 "tex_image_2d" => (g.glTexImage2D)(u(0), a(1), a(2), a(3), a(4), a(5), u(6), u(7), px),
578 "tex_image_3d" => (g.glTexImage3D)(u(0), a(1), a(2), a(3), a(4), a(5), a(6), u(7), u(8), px),
579 "tex_sub_image_2d" => (g.glTexSubImage2D)(u(0), a(1), a(2), a(3), a(4), a(5), u(6), u(7), px),
580 _ => (g.glTexSubImage3D)(u(0), a(1), a(2), a(3), a(4), a(5), a(6), a(7), u(8), u(9), px),
581 }
582 }
583 "tex_storage_2d" => (g.glTexStorage2D)(u(0), a(1), u(2), a(3), a(4)),
584 "tex_storage_3d" => (g.glTexStorage3D)(u(0), a(1), u(2), a(3), a(4), a(5)),
585 "compressed_tex_image_2d" => (g.glCompressedTexImage2D)(u(0), a(1), u(2), a(3), a(4), a(5), a(7), at(u(6))),
586 "compressed_tex_image_3d" => (g.glCompressedTexImage3D)(u(0), a(1), u(2), a(3), a(4), a(5), a(6), a(8), at(u(7))),
587 "compressed_tex_sub_image_2d" => (g.glCompressedTexSubImage2D)(u(0), a(1), a(2), a(3), a(4), a(5), u(6), a(8), at(u(7))),
588 "compressed_tex_sub_image_3d" => {
589 (g.glCompressedTexSubImage3D)(u(0), a(1), a(2), a(3), a(4), a(5), a(6), a(7), u(8), a(10), at(u(9)))
590 }
591 "copy_tex_image_2d" => (g.glCopyTexImage2D)(u(0), a(1), u(2), a(3), a(4), a(5), a(6), a(7)),
592 "copy_tex_sub_image_2d" => (g.glCopyTexSubImage2D)(u(0), a(1), a(2), a(3), a(4), a(5), a(6), a(7)),
593 "copy_tex_sub_image_3d" => (g.glCopyTexSubImage3D)(u(0), a(1), a(2), a(3), a(4), a(5), a(6), a(7), a(8)),
594 "generate_mipmap" => (g.glGenerateMipmap)(u(0)),
595
596 "bind_sampler" => (g.glBindSampler)(u(0), be.obj(Sampler, u(1))),
598 "sampler_parameteri" => (g.glSamplerParameteri)(be.obj(Sampler, u(0)), u(1), a(2)),
599 "sampler_parameterf" => (g.glSamplerParameterf)(be.obj(Sampler, u(0)), u(1), f(2)),
600 "get_sampler_parameteriv" => ret(be.int(|p| (g.glGetSamplerParameteriv)(be.obj(Sampler, u(0)), u(1), p)) as i64),
601
602 "bind_framebuffer" => (g.glBindFramebuffer)(u(0), be.obj(Framebuffer, u(1))),
604 "check_framebuffer_status" => ret((g.glCheckFramebufferStatus)(u(0)) as i64),
605 "framebuffer_texture_2d" => (g.glFramebufferTexture2D)(u(0), u(1), u(2), be.obj(Texture, u(3)), a(4)),
606 "framebuffer_texture_layer" => (g.glFramebufferTextureLayer)(u(0), u(1), be.obj(Texture, u(2)), a(3), a(4)),
607 "framebuffer_renderbuffer" => (g.glFramebufferRenderbuffer)(u(0), u(1), u(2), be.obj(Renderbuffer, u(3))),
608 "get_framebuffer_attachment_parameteriv" => {
609 let (t, att, p) = (u(0), u(1), u(2));
610 let v = be.int(|q| (g.glGetFramebufferAttachmentParameteriv)(t, att, p, q));
611 if p == 0x8CD1 {
612 let ty = be.int(|q| (g.glGetFramebufferAttachmentParameteriv)(t, att, 0x8CD0, q)) as u32;
614 let kind = if ty == 0x1702 { Texture } else { Renderbuffer };
615 ret(be.model_name(kind, v as u32) as i64)
616 } else {
617 ret(v as i64)
618 }
619 }
620 "blit_framebuffer" => (g.glBlitFramebuffer)(a(0), a(1), a(2), a(3), a(4), a(5), a(6), a(7), u(8), u(9)),
621 "invalidate_framebuffer" => (g.glInvalidateFramebuffer)(u(0), a(2), at(u(1))),
622 "invalidate_sub_framebuffer" => (g.glInvalidateSubFramebuffer)(u(0), a(2), at(u(1)), a(3), a(4), a(5), a(6)),
623 "read_buffer" => (g.glReadBuffer)(u(0)),
624 "read_pixels" => {
625 let dst = if model.bound_buffer(PIXEL_PACK_BUFFER) != 0 { off(u(6)) as *mut c_void } else { at_mut(u(6)) };
626 (g.glReadPixels)(a(0), a(1), a(2), a(3), u(4), u(5), dst)
627 }
628 "bind_renderbuffer" => (g.glBindRenderbuffer)(u(0), be.obj(Renderbuffer, u(1))),
629 "renderbuffer_storage" => (g.glRenderbufferStorage)(u(0), u(1), a(2), a(3)),
630 "renderbuffer_storage_multisample" => (g.glRenderbufferStorageMultisample)(u(0), a(1), u(2), a(3), a(4)),
631 "get_renderbuffer_parameteriv" => ret(be.int(|p| (g.glGetRenderbufferParameteriv)(u(0), u(1), p)) as i64),
632
633 "shader_source" => {
635 let p = at(u(1));
636 let len = a(2);
637 (g.glShaderSource)(be.obj(Shader, u(0)), 1, &p as *const *const c_void as *const c_void, &len as *const i32 as *const c_void)
638 }
639 "compile_shader" => (g.glCompileShader)(be.obj(Shader, u(0))),
640 "get_shaderiv" => {
641 if u(1) != 0x8B4F && u(1) != 0x8B88 {
642 ret(be.int(|p| (g.glGetShaderiv)(be.obj(Shader, u(0)), u(1), p)) as i64)
643 }
644 }
645 "get_shader_info_log" => {
646 let log = be.info_log(true, be.obj(Shader, u(0)));
647 ret(text(mem, u(1), u(2), &log)? as i64)
648 }
649 "attach_shader" => (g.glAttachShader)(be.obj(Program, u(0)), be.obj(Shader, u(1))),
650 "detach_shader" => (g.glDetachShader)(be.obj(Program, u(0)), be.obj(Shader, u(1))),
651 "link_program" => {
652 be.uniforms.retain(|(p, _), _| *p != u(0));
653 (g.glLinkProgram)(be.obj(Program, u(0)))
654 }
655 "use_program" => (g.glUseProgram)(be.obj(Program, u(0))),
656 "validate_program" => (g.glValidateProgram)(be.obj(Program, u(0))),
657 "get_programiv" => ret(be.int(|p| (g.glGetProgramiv)(be.obj(Program, u(0)), u(1), p)) as i64),
658 "get_program_info_log" => {
659 let log = be.info_log(false, be.obj(Program, u(0)));
660 ret(text(mem, u(1), u(2), &log)? as i64)
661 }
662 "get_attached_shaders" => {
663 let mut v = [0u32; 16];
664 let mut n = 0i32;
665 (g.glGetAttachedShaders)(be.obj(Program, u(0)), 16, &mut n as *mut i32 as *mut c_void, v.as_mut_ptr() as *mut c_void);
666 let names: Vec<i64> = v[..n.max(0) as usize].iter().map(|&s| be.model_name(Shader, s) as i64).collect();
667 ret(put_i(mem, u(1), u(2), &names, false)? as i64)
668 }
669 "bind_attrib_location" => {
670 let n = c_string(&guest_str(mem, u(2), u(3))?);
671 (g.glBindAttribLocation)(be.obj(Program, u(0)), u(1), n.as_ptr() as *const c_void)
672 }
673 "get_attrib_location" | "get_frag_data_location" => {
674 let n = c_string(&guest_str(mem, u(1), u(2))?);
675 let p = be.obj(Program, u(0));
676 ret(if name == "get_attrib_location" {
677 (g.glGetAttribLocation)(p, n.as_ptr() as *const c_void)
678 } else {
679 (g.glGetFragDataLocation)(p, n.as_ptr() as *const c_void)
680 } as i64)
681 }
682 "get_active_attrib" | "get_active_uniform" | "get_transform_feedback_varying" => {
683 let p = be.obj(Program, u(0));
684 let (count_p, max_p, get): (u32, u32, unsafe extern "system" fn(u32, u32, i32, *mut c_void, *mut c_void, *mut c_void, *mut c_void)) =
685 match name {
686 "get_active_attrib" => (0x8B89, 0x8B8A, g.glGetActiveAttrib),
687 "get_active_uniform" => (0x8B86, 0x8B87, g.glGetActiveUniform),
688 _ => (0x8C83, 0x8C76, g.glGetTransformFeedbackVarying),
689 };
690 let count = be.int(|q| (g.glGetProgramiv)(p, count_p, q));
691 if u(1) as i64 >= count as i64 {
692 model.error(INVALID_VALUE);
693 {
694 ret(-1);
695 return Ok(());
696 }
697 }
698 let max = be.int(|q| (g.glGetProgramiv)(p, max_p, q)).max(1);
699 let mut buf = vec![0u8; max as usize];
700 let (mut len, mut size, mut ty) = (0i32, 0i32, 0u32);
701 get(
702 p, u(1), max, &mut len as *mut i32 as *mut c_void, &mut size as *mut i32 as *mut c_void,
703 &mut ty as *mut u32 as *mut c_void, buf.as_mut_ptr() as *mut c_void,
704 );
705 let s = String::from_utf8_lossy(&buf[..len.max(0) as usize]).into_owned();
706 put_i(mem, u(4), 2, &[size as i64, ty as i64], false)?;
707 ret(text(mem, u(2), u(3), &s)? as i64)
708 }
709 "get_uniform_location" => {
710 let uname = guest_str(mem, u(1), u(2))?;
711 let p = be.obj(Program, u(0));
712 let c = c_string(&uname);
713 let loc = (g.glGetUniformLocation)(p, c.as_ptr() as *const c_void);
714 let model_loc = model_result as i32;
715 if loc < 0 {
716 if let Some(t) = model.uniforms.get_mut(&u(0)) {
718 if t.by_name.get(&uname) == Some(&model_loc) && model_loc == t.count - 1 {
719 t.by_name.remove(&uname);
720 t.count -= 1;
721 }
722 }
723 {
724 ret(-1);
725 return Ok(());
726 }
727 }
728 if !be.uniforms.contains_key(&(u(0), model_loc)) {
729 let comps = be.uniform_components(p, &uname);
730 be.uniforms.insert((u(0), model_loc), (loc, comps));
731 }
732 }
733 "get_uniform_index" => {
734 let c = c_string(&guest_str(mem, u(1), u(2))?);
735 let ptr = c.as_ptr();
736 let mut idx = 0u32;
737 (g.glGetUniformIndices)(be.obj(Program, u(0)), 1, &ptr as *const *const c_char as *const c_void, &mut idx as *mut u32 as *mut c_void);
738 ret(idx as i32 as i64)
739 }
740 "get_active_uniformsiv" => {
741 (g.glGetActiveUniformsiv)(be.obj(Program, u(0)), a(2), at(u(1)), u(3), at_mut(u(4)))
742 }
743 "get_uniform_block_index" => {
744 let c = c_string(&guest_str(mem, u(1), u(2))?);
745 ret((g.glGetUniformBlockIndex)(be.obj(Program, u(0)), c.as_ptr() as *const c_void) as i32 as i64)
746 }
747 "get_active_uniform_block_name" => {
748 let p = be.obj(Program, u(0));
749 let n = be.int(|q| (g.glGetActiveUniformBlockiv)(p, u(1), 0x8A41, q)).max(1);
750 let mut buf = vec![0u8; n as usize];
751 let mut len = 0i32;
752 (g.glGetActiveUniformBlockName)(p, u(1), n, &mut len as *mut i32 as *mut c_void, buf.as_mut_ptr() as *mut c_void);
753 let s = String::from_utf8_lossy(&buf[..len.max(0) as usize]).into_owned();
754 ret(text(mem, u(2), u(3), &s)? as i64)
755 }
756 "get_active_uniform_blockiv" => {
757 let p = be.obj(Program, u(0));
758 let n = if u(2) == 0x8A43 { be.int(|q| (g.glGetActiveUniformBlockiv)(p, u(1), 0x8A42, q)).max(0) as usize } else { 1 };
759 let mut v = vec![0i32; n.max(1)];
760 (g.glGetActiveUniformBlockiv)(p, u(1), u(2), v.as_mut_ptr() as *mut c_void);
761 v.truncate(n);
762 let v: Vec<i64> = v.into_iter().map(i64::from).collect();
763 ret(put_i(mem, u(3), u(4), &v, false)? as i64)
764 }
765 "uniform_block_binding" => (g.glUniformBlockBinding)(be.obj(Program, u(0)), u(1), u(2)),
766 "get_uniformfv" | "get_uniformiv" | "get_uniformuiv" => {
767 let Some(&(loc, comps)) = be.uniforms.get(&(u(0), a(1))) else { return Ok(()) };
768 let p = be.obj(Program, u(0));
769 let n = comps.min(16) as usize;
770 if name == "get_uniformfv" {
771 let mut v = [0f32; 16];
772 (g.glGetUniformfv)(p, loc, v.as_mut_ptr() as *mut c_void);
773 ret(put_f(mem, u(2), u(3), &v[..n])? as i64)
774 } else {
775 let mut v = [0i32; 16];
776 if name == "get_uniformiv" {
777 (g.glGetUniformiv)(p, loc, v.as_mut_ptr() as *mut c_void);
778 } else {
779 (g.glGetUniformuiv)(p, loc, v.as_mut_ptr() as *mut c_void);
780 }
781 let v: Vec<i64> = v[..n].iter().map(|&x| x as i64).collect();
782 ret(put_i(mem, u(2), u(3), &v, false)? as i64)
783 }
784 }
785 "transform_feedback_varyings" => {
786 let bytes = guest_slice(mem, u(1), u(2) as u64)?;
787 let names: Vec<std::ffi::CString> = bytes
788 .split(|&b| b == 0)
789 .take(u(3) as usize)
790 .map(|s| c_string(&String::from_utf8_lossy(s)))
791 .collect();
792 let ptrs: Vec<*const c_char> = names.iter().map(|c| c.as_ptr()).collect();
793 (g.glTransformFeedbackVaryings)(be.obj(Program, u(0)), ptrs.len() as i32, ptrs.as_ptr() as *const c_void, u(4))
794 }
795
796 n if n.starts_with("uniform") => {
798 let Some(&(loc, _)) = be.uniforms.get(&(model.program, a(0))) else { return Ok(()) };
799 match n {
800 "uniform1f" => (g.glUniform1f)(loc, f(1)),
801 "uniform2f" => (g.glUniform2f)(loc, f(1), f(2)),
802 "uniform3f" => (g.glUniform3f)(loc, f(1), f(2), f(3)),
803 "uniform4f" => (g.glUniform4f)(loc, f(1), f(2), f(3), f(4)),
804 "uniform1i" => (g.glUniform1i)(loc, a(1)),
805 "uniform2i" => (g.glUniform2i)(loc, a(1), a(2)),
806 "uniform3i" => (g.glUniform3i)(loc, a(1), a(2), a(3)),
807 "uniform4i" => (g.glUniform4i)(loc, a(1), a(2), a(3), a(4)),
808 "uniform1ui" => (g.glUniform1ui)(loc, u(1)),
809 "uniform2ui" => (g.glUniform2ui)(loc, u(1), u(2)),
810 "uniform3ui" => (g.glUniform3ui)(loc, u(1), u(2), u(3)),
811 "uniform4ui" => (g.glUniform4ui)(loc, u(1), u(2), u(3), u(4)),
812 "uniform1fv" => (g.glUniform1fv)(loc, a(1), at(u(2))),
813 "uniform2fv" => (g.glUniform2fv)(loc, a(1), at(u(2))),
814 "uniform3fv" => (g.glUniform3fv)(loc, a(1), at(u(2))),
815 "uniform4fv" => (g.glUniform4fv)(loc, a(1), at(u(2))),
816 "uniform1iv" => (g.glUniform1iv)(loc, a(1), at(u(2))),
817 "uniform2iv" => (g.glUniform2iv)(loc, a(1), at(u(2))),
818 "uniform3iv" => (g.glUniform3iv)(loc, a(1), at(u(2))),
819 "uniform4iv" => (g.glUniform4iv)(loc, a(1), at(u(2))),
820 "uniform1uiv" => (g.glUniform1uiv)(loc, a(1), at(u(2))),
821 "uniform2uiv" => (g.glUniform2uiv)(loc, a(1), at(u(2))),
822 "uniform3uiv" => (g.glUniform3uiv)(loc, a(1), at(u(2))),
823 "uniform4uiv" => (g.glUniform4uiv)(loc, a(1), at(u(2))),
824 _ => {
825 let m = match n {
826 "uniform_matrix2fv" => g.glUniformMatrix2fv,
827 "uniform_matrix3fv" => g.glUniformMatrix3fv,
828 "uniform_matrix4fv" => g.glUniformMatrix4fv,
829 "uniform_matrix2x3fv" => g.glUniformMatrix2x3fv,
830 "uniform_matrix3x2fv" => g.glUniformMatrix3x2fv,
831 "uniform_matrix2x4fv" => g.glUniformMatrix2x4fv,
832 "uniform_matrix4x2fv" => g.glUniformMatrix4x2fv,
833 "uniform_matrix3x4fv" => g.glUniformMatrix3x4fv,
834 _ => g.glUniformMatrix4x3fv,
835 };
836 m(loc, a(1), (u(2) != 0) as u8, at(u(3)))
837 }
838 }
839 }
840
841 "begin_query" => (g.glBeginQuery)(u(0), be.obj(Query, u(1))),
843 "end_query" => (g.glEndQuery)(u(0)),
844 "get_query_objectuiv" => {
845 if u(1) == 0x8866 && model_result != 0 {
847 let mut v = 0u32;
848 (g.glGetQueryObjectuiv)(be.obj(Query, u(0)), 0x8866, &mut v as *mut u32 as *mut c_void);
849 ret(v as i64)
850 }
851 }
852 "bind_transform_feedback" => (g.glBindTransformFeedback)(u(0), be.obj(TransformFeedback, u(1))),
853 "begin_transform_feedback" => (g.glBeginTransformFeedback)(u(0)),
854 "end_transform_feedback" => (g.glEndTransformFeedback)(),
855 "pause_transform_feedback" => (g.glPauseTransformFeedback)(),
856 "resume_transform_feedback" => (g.glResumeTransformFeedback)(),
857 _ => wasmtime::bail!("gasm:gl.{name}: no GL backend for it"),
858 }
859 }
860 Ok(())
861}