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 show_over_game(&self, rgba: &[u8], w: u32, h: u32) -> bool {
245 self.backend.as_ref().map(|b| b.angle.show_image_over_game(rgba, w, h)).is_some()
246 }
247 pub fn read_frame(&self) -> Option<(u32, u32, Vec<u8>)> {
249 self.backend.as_ref().map(|b| b.angle.read_frame())
250 }
251 pub fn size(&self) -> Option<(u32, u32)> {
253 self.backend.as_ref().map(|b| b.angle.size())
254 }
255 pub fn renderer(&self) -> Option<&str> {
256 self.backend.as_ref().map(|b| b.angle.renderer.as_str())
257 }
258}
259
260pub(super) fn forward(
263 be: &mut Backend,
264 model: &mut Gl,
265 name: &str,
266 args: &[Val],
267 results: &mut [Val],
268 mem: &mut [u8],
269) -> wasmtime::Result<()> {
270 let a = |i: usize| -> i32 {
271 match args[i] {
272 Val::I32(v) => v,
273 Val::I64(v) => v as i32,
274 _ => 0,
275 }
276 };
277 let u = |i: usize| a(i) as u32;
278 let f = |i: usize| match args[i] {
279 Val::F32(b) => f32::from_bits(b),
280 _ => 0.0,
281 };
282 let model_result = match results.first() {
283 Some(Val::I32(v)) => *v as i64,
284 _ => 0,
285 };
286 let mut ret = |v: i64| {
287 if let Some(r) = results.first_mut() {
288 *r = Val::I32(v as i32);
289 }
290 };
291 let base = mem.as_mut_ptr();
292 let at = |p: u32| -> *const c_void { if p == 0 { std::ptr::null() } else { unsafe { base.add(p as usize) as *const c_void } } };
293 let at_mut = |p: u32| -> *mut c_void { unsafe { base.add(p as usize) as *mut c_void } };
294 let off = |o: u32| o as usize as *const c_void;
295 let g = &be.angle.gl as *const crate::gles::Gles;
296 let g = unsafe { &*g };
297 use Kind::*;
298 unsafe {
299 match name {
300 "width" => ret(be.angle.size().0 as i64),
302 "height" => ret(be.angle.size().1 as i64),
303 "present" => {
304 be.angle.swap();
305 be.presented = true;
306 }
307 "get_error" => {
308 if model_result == 0 {
309 ret((g.glGetError)() as i64)
310 }
311 }
312 "get_string" => {
313 let s = if u(0) == GL_EXTENSIONS { be.webgl_extensions().join(" ") } else { cstr((g.glGetString)(u(0))) };
314 ret(text(mem, u(1), u(2), &s)? as i64)
315 }
316 "enable_extension" => {
317 let n = guest_str(mem, u(0), u(1))?;
318 ret(be.enable_extension(&n) as i64)
319 }
320 "get_integerv" | "get_floatv" | "get_integer64v" => {
321 let pname = u(0);
322 if model.model_param(pname).is_some() {
323 return Ok(());
324 }
325 be.drain(model);
326 let n = param_count(g, pname).max(1);
327 let (dst, count) = (u(1), u(2));
328 match name {
329 "get_floatv" => {
330 let mut v = vec![0f32; n.max(16)];
331 (g.glGetFloatv)(pname, v.as_mut_ptr() as *mut c_void);
332 if (g.glGetError)() == INVALID_ENUM {
333 model.error(INVALID_ENUM);
334 {
335 ret(-1);
336 return Ok(());
337 }
338 }
339 v.truncate(n);
340 ret(put_f(mem, dst, count, &v)? as i64)
341 }
342 _ => {
343 let mut v = vec![0i64; n.max(16)];
344 (g.glGetInteger64v)(pname, v.as_mut_ptr() as *mut c_void);
345 if (g.glGetError)() == INVALID_ENUM {
346 model.error(INVALID_ENUM);
347 {
348 ret(-1);
349 return Ok(());
350 }
351 }
352 v.truncate(n);
353 match pname {
355 0x8919 => v[0] = be.model_name(Sampler, v[0] as u32) as i64,
356 0x8E25 => v[0] = be.model_name(TransformFeedback, v[0] as u32) as i64,
357 _ => {}
358 }
359 ret(put_i(mem, dst, count, &v, name == "get_integer64v")? as i64)
360 }
361 }
362 }
363 "get_integeri_v" => {
364 be.drain(model);
365 let mut v = 0i64;
366 (g.glGetInteger64i_v)(u(0), u(1), &mut v as *mut i64 as *mut c_void);
367 if (g.glGetError)() != 0 {
368 model.error(INVALID_ENUM);
369 {
370 ret(-1);
371 return Ok(());
372 }
373 }
374 if u(0) == 0x8A28 || u(0) == 0x8C8F {
375 v = be.model_name(Buffer, v as u32) as i64;
376 }
377 ret(put_i(mem, u(2), u(3), &[v], false)? as i64)
378 }
379 "get_internalformativ" => {
380 let (t, fmt, pname) = (u(0), u(1), u(2));
381 let n = if pname == 0x80A9 { be.int(|p| (g.glGetInternalformativ)(t, fmt, 0x9380, 1, p)).max(0) as usize } else { 1 };
382 let mut v = vec![0i32; n.max(1)];
383 (g.glGetInternalformativ)(t, fmt, pname, n as i32, v.as_mut_ptr() as *mut c_void);
384 v.truncate(n);
385 let v: Vec<i64> = v.into_iter().map(i64::from).collect();
386 ret(put_i(mem, u(3), u(4), &v, false)? as i64)
387 }
388 "get_shader_precision_format" => {
389 let (mut range, mut precision) = ([0i32; 2], 0i32);
390 (g.glGetShaderPrecisionFormat)(u(0), u(1), range.as_mut_ptr() as *mut c_void, &mut precision as *mut i32 as *mut c_void);
391 put_i(mem, u(2), 3, &[range[0] as i64, range[1] as i64, precision as i64], false)?;
392 }
393
394 "active_texture" => (g.glActiveTexture)(u(0)),
396 "blend_color" => (g.glBlendColor)(f(0), f(1), f(2), f(3)),
397 "blend_equation" => (g.glBlendEquation)(u(0)),
398 "blend_equation_separate" => (g.glBlendEquationSeparate)(u(0), u(1)),
399 "blend_func" => (g.glBlendFunc)(u(0), u(1)),
400 "blend_func_separate" => (g.glBlendFuncSeparate)(u(0), u(1), u(2), u(3)),
401 "clear" => (g.glClear)(u(0)),
402 "clear_color" => (g.glClearColor)(f(0), f(1), f(2), f(3)),
403 "clear_depthf" => (g.glClearDepthf)(f(0)),
404 "clear_stencil" => (g.glClearStencil)(a(0)),
405 "color_mask" => (g.glColorMask)((u(0) != 0) as u8, (u(1) != 0) as u8, (u(2) != 0) as u8, (u(3) != 0) as u8),
406 "cull_face" => (g.glCullFace)(u(0)),
407 "depth_func" => (g.glDepthFunc)(u(0)),
408 "depth_mask" => (g.glDepthMask)((u(0) != 0) as u8),
409 "depth_rangef" => (g.glDepthRangef)(f(0), f(1)),
410 "disable" => (g.glDisable)(u(0)),
411 "enable" => (g.glEnable)(u(0)),
412 "is_enabled" => ret((g.glIsEnabled)(u(0)) as i64),
413 "front_face" => (g.glFrontFace)(u(0)),
414 "hint" => (g.glHint)(u(0), u(1)),
415 "line_width" => (g.glLineWidth)(f(0)),
416 "pixel_storei" => {
417 if !(0x9240..=0x9244).contains(&u(0)) {
419 (g.glPixelStorei)(u(0), a(1))
420 }
421 }
422 "polygon_offset" => (g.glPolygonOffset)(f(0), f(1)),
423 "sample_coverage" => (g.glSampleCoverage)(f(0), (u(1) != 0) as u8),
424 "scissor" => (g.glScissor)(a(0), a(1), a(2), a(3)),
425 "viewport" => (g.glViewport)(a(0), a(1), a(2), a(3)),
426 "stencil_func" => (g.glStencilFunc)(u(0), a(1), u(2)),
427 "stencil_func_separate" => (g.glStencilFuncSeparate)(u(0), u(1), a(2), u(3)),
428 "stencil_mask" => (g.glStencilMask)(u(0)),
429 "stencil_mask_separate" => (g.glStencilMaskSeparate)(u(0), u(1)),
430 "stencil_op" => (g.glStencilOp)(u(0), u(1), u(2)),
431 "stencil_op_separate" => (g.glStencilOpSeparate)(u(0), u(1), u(2), u(3)),
432 "finish" => (g.glFinish)(),
433 "flush" => (g.glFlush)(),
434
435 "create_buffer" | "create_texture" | "create_vertex_array" | "create_sampler" | "create_framebuffer"
437 | "create_renderbuffer" | "create_query" | "create_transform_feedback" => {
438 let (kind, make): (Kind, unsafe extern "system" fn(i32, *mut c_void)) = match name {
439 "create_buffer" => (Buffer, g.glGenBuffers),
440 "create_texture" => (Texture, g.glGenTextures),
441 "create_vertex_array" => (VertexArray, g.glGenVertexArrays),
442 "create_sampler" => (Sampler, g.glGenSamplers),
443 "create_framebuffer" => (Framebuffer, g.glGenFramebuffers),
444 "create_renderbuffer" => (Renderbuffer, g.glGenRenderbuffers),
445 "create_query" => (Query, g.glGenQueries),
446 _ => (TransformFeedback, g.glGenTransformFeedbacks),
447 };
448 let mut x = 0u32;
449 make(1, &mut x as *mut u32 as *mut c_void);
450 be.put_name(kind, model_result as u32, x);
451 }
452 "delete_buffer" | "delete_texture" | "delete_vertex_array" | "delete_sampler" | "delete_framebuffer"
453 | "delete_renderbuffer" | "delete_query" | "delete_transform_feedback" => {
454 let (kind, del): (Kind, unsafe extern "system" fn(i32, *const c_void)) = match name {
455 "delete_buffer" => (Buffer, g.glDeleteBuffers),
456 "delete_texture" => (Texture, g.glDeleteTextures),
457 "delete_vertex_array" => (VertexArray, g.glDeleteVertexArrays),
458 "delete_sampler" => (Sampler, g.glDeleteSamplers),
459 "delete_framebuffer" => (Framebuffer, g.glDeleteFramebuffers),
460 "delete_renderbuffer" => (Renderbuffer, g.glDeleteRenderbuffers),
461 "delete_query" => (Query, g.glDeleteQueries),
462 _ => (TransformFeedback, g.glDeleteTransformFeedbacks),
463 };
464 if let Some(x) = be.take(kind, u(0)) {
465 del(1, &x as *const u32 as *const c_void);
466 }
467 }
468 "create_shader" => {
469 let x = (g.glCreateShader)(u(0));
470 be.put_name(Shader, model_result as u32, x);
471 }
472 "create_program" => {
473 let x = (g.glCreateProgram)();
474 be.put_name(Program, model_result as u32, x);
475 }
476 "delete_shader" => {
477 if let Some(x) = be.take(Shader, u(0)) {
478 (g.glDeleteShader)(x)
479 }
480 }
481 "delete_program" => {
482 be.uniforms.retain(|(p, _), _| *p != u(0));
483 if let Some(x) = be.take(Program, u(0)) {
484 (g.glDeleteProgram)(x)
485 }
486 }
487 "fence_sync" => {
488 let s = (g.glFenceSync)(u(0), u(1));
489 be.syncs.insert(model_result as u32, s as usize);
490 }
491 "delete_sync" => {
492 if let Some(s) = be.syncs.remove(&u(0)) {
493 (g.glDeleteSync)(s as *const c_void)
494 }
495 }
496 "is_buffer" | "is_texture" | "is_vertex_array" | "is_sampler" | "is_framebuffer" | "is_renderbuffer"
498 | "is_shader" | "is_program" | "is_query" | "is_sync" | "is_transform_feedback" | "frame_shown"
499 | "get_shader_source" | "get_queryiv" | "client_wait_sync" | "wait_sync" | "get_synciv" => {}
500
501 "bind_buffer" => (g.glBindBuffer)(u(0), be.obj(Buffer, u(1))),
503 "bind_buffer_base" => (g.glBindBufferBase)(u(0), u(1), be.obj(Buffer, u(2))),
504 "bind_buffer_range" => (g.glBindBufferRange)(u(0), u(1), be.obj(Buffer, u(2)), u(3) as isize, u(4) as isize),
505 "buffer_data" => (g.glBufferData)(u(0), u(2) as isize, at(u(1)), u(3)),
506 "buffer_sub_data" => (g.glBufferSubData)(u(0), u(1) as isize, u(3) as isize, at(u(2))),
507 "copy_buffer_sub_data" => (g.glCopyBufferSubData)(u(0), u(1), u(2) as isize, u(3) as isize, u(4) as isize),
508 "get_buffer_sub_data" => {
509 let (t, o, dst, len) = (u(0), u(1), u(2), u(3));
511 if len > 0 {
512 let p = (g.glMapBufferRange)(t, o as isize, len as isize, 0x0001);
513 if !p.is_null() {
514 let out = guest_slice_mut(mem, dst, len as u64)?;
515 std::ptr::copy_nonoverlapping(p as *const u8, out.as_mut_ptr(), len as usize);
516 (g.glUnmapBuffer)(t);
517 }
518 }
519 }
520 "get_buffer_parameteriv" => ret(be.int(|p| (g.glGetBufferParameteriv)(u(0), u(1), p)) as i64),
521
522 "bind_vertex_array" => (g.glBindVertexArray)(be.obj(VertexArray, u(0))),
524 "enable_vertex_attrib_array" => (g.glEnableVertexAttribArray)(u(0)),
525 "disable_vertex_attrib_array" => (g.glDisableVertexAttribArray)(u(0)),
526 "vertex_attrib_pointer" => (g.glVertexAttribPointer)(u(0), a(1), u(2), (u(3) != 0) as u8, a(4), off(u(5))),
527 "vertex_attrib_ipointer" => (g.glVertexAttribIPointer)(u(0), a(1), u(2), a(3), off(u(4))),
528 "vertex_attrib_divisor" => (g.glVertexAttribDivisor)(u(0), u(1)),
529 "vertex_attrib4f" => (g.glVertexAttrib4f)(u(0), f(1), f(2), f(3), f(4)),
530 "vertex_attribi4i" => (g.glVertexAttribI4i)(u(0), a(1), a(2), a(3), a(4)),
531 "vertex_attribi4ui" => (g.glVertexAttribI4ui)(u(0), u(1), u(2), u(3), u(4)),
532 "get_vertex_attribiv" => {
533 let v = be.int(|p| (g.glGetVertexAttribiv)(u(0), u(1), p));
534 ret(if u(1) == 0x889F { be.model_name(Buffer, v as u32) as i64 } else { v as i64 })
535 }
536 "get_vertex_attribfv" => {
537 let mut v = [0f32; 4];
538 (g.glGetVertexAttribfv)(u(0), u(1), v.as_mut_ptr() as *mut c_void);
539 let n = if u(1) == 0x8626 { 4 } else { 1 };
540 ret(put_f(mem, u(2), u(3), &v[..n])? as i64)
541 }
542 "get_vertex_attrib_offset" => {
543 let mut p: *mut c_void = std::ptr::null_mut();
544 (g.glGetVertexAttribPointerv)(u(0), u(1), &mut p as *mut *mut c_void as *mut c_void);
545 ret(p as usize as i64)
546 }
547
548 "draw_arrays" => (g.glDrawArrays)(u(0), a(1), a(2)),
550 "draw_elements" => (g.glDrawElements)(u(0), a(1), u(2), off(u(3))),
551 "draw_arrays_instanced" => (g.glDrawArraysInstanced)(u(0), a(1), a(2), a(3)),
552 "draw_elements_instanced" => (g.glDrawElementsInstanced)(u(0), a(1), u(2), off(u(3)), a(4)),
553 "draw_range_elements" => (g.glDrawRangeElements)(u(0), u(1), u(2), a(3), u(4), off(u(5))),
554 "draw_buffers" => (g.glDrawBuffers)(a(1), at(u(0))),
555 "clear_bufferiv" => (g.glClearBufferiv)(u(0), a(1), at(u(2))),
556 "clear_bufferuiv" => (g.glClearBufferuiv)(u(0), a(1), at(u(2))),
557 "clear_bufferfv" => (g.glClearBufferfv)(u(0), a(1), at(u(2))),
558 "clear_bufferfi" => (g.glClearBufferfi)(u(0), a(1), f(2), a(3)),
559
560 "bind_texture" => (g.glBindTexture)(u(0), be.obj(Texture, u(1))),
562 "tex_parameteri" => (g.glTexParameteri)(u(0), u(1), a(2)),
563 "tex_parameterf" => (g.glTexParameterf)(u(0), u(1), f(2)),
564 "get_tex_parameteriv" => ret(be.int(|p| (g.glGetTexParameteriv)(u(0), u(1), p)) as i64),
565 "get_tex_parameterfv" | "get_sampler_parameterfv" => {
566 let mut v = 0f32;
567 if name == "get_tex_parameterfv" {
568 (g.glGetTexParameterfv)(u(0), u(1), &mut v as *mut f32 as *mut c_void);
569 } else {
570 (g.glGetSamplerParameterfv)(be.obj(Sampler, u(0)), u(1), &mut v as *mut f32 as *mut c_void);
571 }
572 if let Some(r) = results.first_mut() {
573 *r = Val::F32(v.to_bits());
574 }
575 }
576 "tex_image_2d" | "tex_image_3d" | "tex_sub_image_2d" | "tex_sub_image_3d" => {
577 let n = args.len();
579 let (ptr, len) = (u(n - 2), u(n - 1));
580 let px = if model.bound_buffer(PIXEL_UNPACK_BUFFER) != 0 { off(len) } else { at(ptr) };
581 match name {
582 "tex_image_2d" => (g.glTexImage2D)(u(0), a(1), a(2), a(3), a(4), a(5), u(6), u(7), px),
583 "tex_image_3d" => (g.glTexImage3D)(u(0), a(1), a(2), a(3), a(4), a(5), a(6), u(7), u(8), px),
584 "tex_sub_image_2d" => (g.glTexSubImage2D)(u(0), a(1), a(2), a(3), a(4), a(5), u(6), u(7), px),
585 _ => (g.glTexSubImage3D)(u(0), a(1), a(2), a(3), a(4), a(5), a(6), a(7), u(8), u(9), px),
586 }
587 }
588 "tex_storage_2d" => (g.glTexStorage2D)(u(0), a(1), u(2), a(3), a(4)),
589 "tex_storage_3d" => (g.glTexStorage3D)(u(0), a(1), u(2), a(3), a(4), a(5)),
590 "compressed_tex_image_2d" => (g.glCompressedTexImage2D)(u(0), a(1), u(2), a(3), a(4), a(5), a(7), at(u(6))),
591 "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))),
592 "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))),
593 "compressed_tex_sub_image_3d" => {
594 (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)))
595 }
596 "copy_tex_image_2d" => (g.glCopyTexImage2D)(u(0), a(1), u(2), a(3), a(4), a(5), a(6), a(7)),
597 "copy_tex_sub_image_2d" => (g.glCopyTexSubImage2D)(u(0), a(1), a(2), a(3), a(4), a(5), a(6), a(7)),
598 "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)),
599 "generate_mipmap" => (g.glGenerateMipmap)(u(0)),
600
601 "bind_sampler" => (g.glBindSampler)(u(0), be.obj(Sampler, u(1))),
603 "sampler_parameteri" => (g.glSamplerParameteri)(be.obj(Sampler, u(0)), u(1), a(2)),
604 "sampler_parameterf" => (g.glSamplerParameterf)(be.obj(Sampler, u(0)), u(1), f(2)),
605 "get_sampler_parameteriv" => ret(be.int(|p| (g.glGetSamplerParameteriv)(be.obj(Sampler, u(0)), u(1), p)) as i64),
606
607 "bind_framebuffer" => (g.glBindFramebuffer)(u(0), be.obj(Framebuffer, u(1))),
609 "check_framebuffer_status" => ret((g.glCheckFramebufferStatus)(u(0)) as i64),
610 "framebuffer_texture_2d" => (g.glFramebufferTexture2D)(u(0), u(1), u(2), be.obj(Texture, u(3)), a(4)),
611 "framebuffer_texture_layer" => (g.glFramebufferTextureLayer)(u(0), u(1), be.obj(Texture, u(2)), a(3), a(4)),
612 "framebuffer_renderbuffer" => (g.glFramebufferRenderbuffer)(u(0), u(1), u(2), be.obj(Renderbuffer, u(3))),
613 "get_framebuffer_attachment_parameteriv" => {
614 let (t, att, p) = (u(0), u(1), u(2));
615 let v = be.int(|q| (g.glGetFramebufferAttachmentParameteriv)(t, att, p, q));
616 if p == 0x8CD1 {
617 let ty = be.int(|q| (g.glGetFramebufferAttachmentParameteriv)(t, att, 0x8CD0, q)) as u32;
619 let kind = if ty == 0x1702 { Texture } else { Renderbuffer };
620 ret(be.model_name(kind, v as u32) as i64)
621 } else {
622 ret(v as i64)
623 }
624 }
625 "blit_framebuffer" => (g.glBlitFramebuffer)(a(0), a(1), a(2), a(3), a(4), a(5), a(6), a(7), u(8), u(9)),
626 "invalidate_framebuffer" => (g.glInvalidateFramebuffer)(u(0), a(2), at(u(1))),
627 "invalidate_sub_framebuffer" => (g.glInvalidateSubFramebuffer)(u(0), a(2), at(u(1)), a(3), a(4), a(5), a(6)),
628 "read_buffer" => (g.glReadBuffer)(u(0)),
629 "read_pixels" => {
630 let dst = if model.bound_buffer(PIXEL_PACK_BUFFER) != 0 { off(u(6)) as *mut c_void } else { at_mut(u(6)) };
631 (g.glReadPixels)(a(0), a(1), a(2), a(3), u(4), u(5), dst)
632 }
633 "bind_renderbuffer" => (g.glBindRenderbuffer)(u(0), be.obj(Renderbuffer, u(1))),
634 "renderbuffer_storage" => (g.glRenderbufferStorage)(u(0), u(1), a(2), a(3)),
635 "renderbuffer_storage_multisample" => (g.glRenderbufferStorageMultisample)(u(0), a(1), u(2), a(3), a(4)),
636 "get_renderbuffer_parameteriv" => ret(be.int(|p| (g.glGetRenderbufferParameteriv)(u(0), u(1), p)) as i64),
637
638 "shader_source" => {
640 let p = at(u(1));
641 let len = a(2);
642 (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)
643 }
644 "compile_shader" => (g.glCompileShader)(be.obj(Shader, u(0))),
645 "get_shaderiv" => {
646 if u(1) != 0x8B4F && u(1) != 0x8B88 {
647 ret(be.int(|p| (g.glGetShaderiv)(be.obj(Shader, u(0)), u(1), p)) as i64)
648 }
649 }
650 "get_shader_info_log" => {
651 let log = be.info_log(true, be.obj(Shader, u(0)));
652 ret(text(mem, u(1), u(2), &log)? as i64)
653 }
654 "attach_shader" => (g.glAttachShader)(be.obj(Program, u(0)), be.obj(Shader, u(1))),
655 "detach_shader" => (g.glDetachShader)(be.obj(Program, u(0)), be.obj(Shader, u(1))),
656 "link_program" => {
657 be.uniforms.retain(|(p, _), _| *p != u(0));
658 (g.glLinkProgram)(be.obj(Program, u(0)))
659 }
660 "use_program" => (g.glUseProgram)(be.obj(Program, u(0))),
661 "validate_program" => (g.glValidateProgram)(be.obj(Program, u(0))),
662 "get_programiv" => ret(be.int(|p| (g.glGetProgramiv)(be.obj(Program, u(0)), u(1), p)) as i64),
663 "get_program_info_log" => {
664 let log = be.info_log(false, be.obj(Program, u(0)));
665 ret(text(mem, u(1), u(2), &log)? as i64)
666 }
667 "get_attached_shaders" => {
668 let mut v = [0u32; 16];
669 let mut n = 0i32;
670 (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);
671 let names: Vec<i64> = v[..n.max(0) as usize].iter().map(|&s| be.model_name(Shader, s) as i64).collect();
672 ret(put_i(mem, u(1), u(2), &names, false)? as i64)
673 }
674 "bind_attrib_location" => {
675 let n = c_string(&guest_str(mem, u(2), u(3))?);
676 (g.glBindAttribLocation)(be.obj(Program, u(0)), u(1), n.as_ptr() as *const c_void)
677 }
678 "get_attrib_location" | "get_frag_data_location" => {
679 let n = c_string(&guest_str(mem, u(1), u(2))?);
680 let p = be.obj(Program, u(0));
681 ret(if name == "get_attrib_location" {
682 (g.glGetAttribLocation)(p, n.as_ptr() as *const c_void)
683 } else {
684 (g.glGetFragDataLocation)(p, n.as_ptr() as *const c_void)
685 } as i64)
686 }
687 "get_active_attrib" | "get_active_uniform" | "get_transform_feedback_varying" => {
688 let p = be.obj(Program, u(0));
689 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)) =
690 match name {
691 "get_active_attrib" => (0x8B89, 0x8B8A, g.glGetActiveAttrib),
692 "get_active_uniform" => (0x8B86, 0x8B87, g.glGetActiveUniform),
693 _ => (0x8C83, 0x8C76, g.glGetTransformFeedbackVarying),
694 };
695 let count = be.int(|q| (g.glGetProgramiv)(p, count_p, q));
696 if u(1) as i64 >= count as i64 {
697 model.error(INVALID_VALUE);
698 {
699 ret(-1);
700 return Ok(());
701 }
702 }
703 let max = be.int(|q| (g.glGetProgramiv)(p, max_p, q)).max(1);
704 let mut buf = vec![0u8; max as usize];
705 let (mut len, mut size, mut ty) = (0i32, 0i32, 0u32);
706 get(
707 p, u(1), max, &mut len as *mut i32 as *mut c_void, &mut size as *mut i32 as *mut c_void,
708 &mut ty as *mut u32 as *mut c_void, buf.as_mut_ptr() as *mut c_void,
709 );
710 let s = String::from_utf8_lossy(&buf[..len.max(0) as usize]).into_owned();
711 put_i(mem, u(4), 2, &[size as i64, ty as i64], false)?;
712 ret(text(mem, u(2), u(3), &s)? as i64)
713 }
714 "get_uniform_location" => {
715 let uname = guest_str(mem, u(1), u(2))?;
716 let p = be.obj(Program, u(0));
717 let c = c_string(&uname);
718 let loc = (g.glGetUniformLocation)(p, c.as_ptr() as *const c_void);
719 let model_loc = model_result as i32;
720 if loc < 0 {
721 if let Some(t) = model.uniforms.get_mut(&u(0)) {
723 if t.by_name.get(&uname) == Some(&model_loc) && model_loc == t.count - 1 {
724 t.by_name.remove(&uname);
725 t.count -= 1;
726 }
727 }
728 {
729 ret(-1);
730 return Ok(());
731 }
732 }
733 if !be.uniforms.contains_key(&(u(0), model_loc)) {
734 let comps = be.uniform_components(p, &uname);
735 be.uniforms.insert((u(0), model_loc), (loc, comps));
736 }
737 }
738 "get_uniform_index" => {
739 let c = c_string(&guest_str(mem, u(1), u(2))?);
740 let ptr = c.as_ptr();
741 let mut idx = 0u32;
742 (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);
743 ret(idx as i32 as i64)
744 }
745 "get_active_uniformsiv" => {
746 (g.glGetActiveUniformsiv)(be.obj(Program, u(0)), a(2), at(u(1)), u(3), at_mut(u(4)))
747 }
748 "get_uniform_block_index" => {
749 let c = c_string(&guest_str(mem, u(1), u(2))?);
750 ret((g.glGetUniformBlockIndex)(be.obj(Program, u(0)), c.as_ptr() as *const c_void) as i32 as i64)
751 }
752 "get_active_uniform_block_name" => {
753 let p = be.obj(Program, u(0));
754 let n = be.int(|q| (g.glGetActiveUniformBlockiv)(p, u(1), 0x8A41, q)).max(1);
755 let mut buf = vec![0u8; n as usize];
756 let mut len = 0i32;
757 (g.glGetActiveUniformBlockName)(p, u(1), n, &mut len as *mut i32 as *mut c_void, buf.as_mut_ptr() as *mut c_void);
758 let s = String::from_utf8_lossy(&buf[..len.max(0) as usize]).into_owned();
759 ret(text(mem, u(2), u(3), &s)? as i64)
760 }
761 "get_active_uniform_blockiv" => {
762 let p = be.obj(Program, u(0));
763 let n = if u(2) == 0x8A43 { be.int(|q| (g.glGetActiveUniformBlockiv)(p, u(1), 0x8A42, q)).max(0) as usize } else { 1 };
764 let mut v = vec![0i32; n.max(1)];
765 (g.glGetActiveUniformBlockiv)(p, u(1), u(2), v.as_mut_ptr() as *mut c_void);
766 v.truncate(n);
767 let v: Vec<i64> = v.into_iter().map(i64::from).collect();
768 ret(put_i(mem, u(3), u(4), &v, false)? as i64)
769 }
770 "uniform_block_binding" => (g.glUniformBlockBinding)(be.obj(Program, u(0)), u(1), u(2)),
771 "get_uniformfv" | "get_uniformiv" | "get_uniformuiv" => {
772 let Some(&(loc, comps)) = be.uniforms.get(&(u(0), a(1))) else { return Ok(()) };
773 let p = be.obj(Program, u(0));
774 let n = comps.min(16) as usize;
775 if name == "get_uniformfv" {
776 let mut v = [0f32; 16];
777 (g.glGetUniformfv)(p, loc, v.as_mut_ptr() as *mut c_void);
778 ret(put_f(mem, u(2), u(3), &v[..n])? as i64)
779 } else {
780 let mut v = [0i32; 16];
781 if name == "get_uniformiv" {
782 (g.glGetUniformiv)(p, loc, v.as_mut_ptr() as *mut c_void);
783 } else {
784 (g.glGetUniformuiv)(p, loc, v.as_mut_ptr() as *mut c_void);
785 }
786 let v: Vec<i64> = v[..n].iter().map(|&x| x as i64).collect();
787 ret(put_i(mem, u(2), u(3), &v, false)? as i64)
788 }
789 }
790 "transform_feedback_varyings" => {
791 let bytes = guest_slice(mem, u(1), u(2) as u64)?;
792 let names: Vec<std::ffi::CString> = bytes
793 .split(|&b| b == 0)
794 .take(u(3) as usize)
795 .map(|s| c_string(&String::from_utf8_lossy(s)))
796 .collect();
797 let ptrs: Vec<*const c_char> = names.iter().map(|c| c.as_ptr()).collect();
798 (g.glTransformFeedbackVaryings)(be.obj(Program, u(0)), ptrs.len() as i32, ptrs.as_ptr() as *const c_void, u(4))
799 }
800
801 n if n.starts_with("uniform") => {
803 let Some(&(loc, _)) = be.uniforms.get(&(model.program, a(0))) else { return Ok(()) };
804 match n {
805 "uniform1f" => (g.glUniform1f)(loc, f(1)),
806 "uniform2f" => (g.glUniform2f)(loc, f(1), f(2)),
807 "uniform3f" => (g.glUniform3f)(loc, f(1), f(2), f(3)),
808 "uniform4f" => (g.glUniform4f)(loc, f(1), f(2), f(3), f(4)),
809 "uniform1i" => (g.glUniform1i)(loc, a(1)),
810 "uniform2i" => (g.glUniform2i)(loc, a(1), a(2)),
811 "uniform3i" => (g.glUniform3i)(loc, a(1), a(2), a(3)),
812 "uniform4i" => (g.glUniform4i)(loc, a(1), a(2), a(3), a(4)),
813 "uniform1ui" => (g.glUniform1ui)(loc, u(1)),
814 "uniform2ui" => (g.glUniform2ui)(loc, u(1), u(2)),
815 "uniform3ui" => (g.glUniform3ui)(loc, u(1), u(2), u(3)),
816 "uniform4ui" => (g.glUniform4ui)(loc, u(1), u(2), u(3), u(4)),
817 "uniform1fv" => (g.glUniform1fv)(loc, a(1), at(u(2))),
818 "uniform2fv" => (g.glUniform2fv)(loc, a(1), at(u(2))),
819 "uniform3fv" => (g.glUniform3fv)(loc, a(1), at(u(2))),
820 "uniform4fv" => (g.glUniform4fv)(loc, a(1), at(u(2))),
821 "uniform1iv" => (g.glUniform1iv)(loc, a(1), at(u(2))),
822 "uniform2iv" => (g.glUniform2iv)(loc, a(1), at(u(2))),
823 "uniform3iv" => (g.glUniform3iv)(loc, a(1), at(u(2))),
824 "uniform4iv" => (g.glUniform4iv)(loc, a(1), at(u(2))),
825 "uniform1uiv" => (g.glUniform1uiv)(loc, a(1), at(u(2))),
826 "uniform2uiv" => (g.glUniform2uiv)(loc, a(1), at(u(2))),
827 "uniform3uiv" => (g.glUniform3uiv)(loc, a(1), at(u(2))),
828 "uniform4uiv" => (g.glUniform4uiv)(loc, a(1), at(u(2))),
829 _ => {
830 let m = match n {
831 "uniform_matrix2fv" => g.glUniformMatrix2fv,
832 "uniform_matrix3fv" => g.glUniformMatrix3fv,
833 "uniform_matrix4fv" => g.glUniformMatrix4fv,
834 "uniform_matrix2x3fv" => g.glUniformMatrix2x3fv,
835 "uniform_matrix3x2fv" => g.glUniformMatrix3x2fv,
836 "uniform_matrix2x4fv" => g.glUniformMatrix2x4fv,
837 "uniform_matrix4x2fv" => g.glUniformMatrix4x2fv,
838 "uniform_matrix3x4fv" => g.glUniformMatrix3x4fv,
839 _ => g.glUniformMatrix4x3fv,
840 };
841 m(loc, a(1), (u(2) != 0) as u8, at(u(3)))
842 }
843 }
844 }
845
846 "begin_query" => (g.glBeginQuery)(u(0), be.obj(Query, u(1))),
848 "end_query" => (g.glEndQuery)(u(0)),
849 "get_query_objectuiv" => {
850 if u(1) == 0x8866 && model_result != 0 {
852 let mut v = 0u32;
853 (g.glGetQueryObjectuiv)(be.obj(Query, u(0)), 0x8866, &mut v as *mut u32 as *mut c_void);
854 ret(v as i64)
855 }
856 }
857 "bind_transform_feedback" => (g.glBindTransformFeedback)(u(0), be.obj(TransformFeedback, u(1))),
858 "begin_transform_feedback" => (g.glBeginTransformFeedback)(u(0)),
859 "end_transform_feedback" => (g.glEndTransformFeedback)(),
860 "pause_transform_feedback" => (g.glPauseTransformFeedback)(),
861 "resume_transform_feedback" => (g.glResumeTransformFeedback)(),
862 _ => wasmtime::bail!("gasm:gl.{name}: no GL backend for it"),
863 }
864 }
865 Ok(())
866}