luminance_webgl/webgl2/
shader.rs

1//! Shader support for WebGL2.
2
3use super::buffer::{Buffer, BufferError};
4use crate::webgl2::{state::WebGL2State, WebGL2};
5use luminance::{
6  backend::shader::{Shader, ShaderData, Uniformable},
7  pipeline::{ShaderDataBinding, TextureBinding},
8  pixel::{SamplerType, Type as PixelType},
9  shader::{
10    types::{Arr, Mat22, Mat33, Mat44, Vec2, Vec3, Vec4},
11    ProgramError, ShaderDataError, StageError, StageType, TessellationStages, Uniform, UniformType,
12    UniformWarning, VertexAttribWarning,
13  },
14  texture::{Dim, Dimensionable},
15  vertex::Semantics,
16};
17use luminance_std140::{ArrElem, Std140};
18use std::{cell::RefCell, collections::HashMap, mem, rc::Rc};
19use web_sys::{WebGl2RenderingContext, WebGlProgram, WebGlShader, WebGlUniformLocation};
20
21#[derive(Debug)]
22pub struct Stage {
23  handle: WebGlShader,
24  ty: StageType,
25  state: Rc<RefCell<WebGL2State>>,
26}
27
28impl Drop for Stage {
29  fn drop(&mut self) {
30    self.state.borrow().ctx.delete_shader(Some(&self.handle));
31  }
32}
33
34impl Stage {
35  fn new(webgl2: &mut WebGL2, ty: StageType, src: &str) -> Result<Self, StageError> {
36    let state = webgl2.state.borrow();
37
38    let shader_ty = webgl_shader_type(ty)
39      .ok_or_else(|| StageError::CompilationFailed(ty, "unsupported shader type".to_owned()))?;
40
41    let handle = state.ctx.create_shader(shader_ty).ok_or_else(|| {
42      StageError::CompilationFailed(ty, "unable to create shader stage".to_owned())
43    })?;
44
45    state.ctx.shader_source(&handle, &patch_shader_src(src));
46    state.ctx.compile_shader(&handle);
47
48    let compiled = state
49      .ctx
50      .get_shader_parameter(&handle, WebGl2RenderingContext::COMPILE_STATUS)
51      .as_bool()
52      .ok_or_else(|| {
53        StageError::CompilationFailed(ty, "cannot determine compilation status".to_owned())
54      })?;
55
56    if compiled {
57      Ok(Stage {
58        handle,
59        ty,
60        state: webgl2.state.clone(),
61      })
62    } else {
63      let log = state
64        .ctx
65        .get_shader_info_log(&handle)
66        .ok_or_else(|| StageError::CompilationFailed(ty, "no compilation error".to_owned()))?;
67
68      state.ctx.delete_shader(Some(&handle));
69
70      Err(StageError::compilation_failed(ty, log))
71    }
72  }
73
74  fn handle(&self) -> &WebGlShader {
75    &self.handle
76  }
77}
78
79/// A type used to map [`i32`] (uniform locations) to [`WebGlUniformLocation`].
80///
81/// It is typically shared with internal mutation (Rc + RefCell) so that it can add the location
82/// mappings in the associated [`Program`].
83type LocationMap = HashMap<i32, WebGlUniformLocation>;
84
85#[derive(Debug)]
86pub struct Program {
87  pub(crate) handle: WebGlProgram,
88  location_map: Rc<RefCell<LocationMap>>,
89  state: Rc<RefCell<WebGL2State>>,
90}
91
92impl Drop for Program {
93  fn drop(&mut self) {
94    self.state.borrow().ctx.delete_program(Some(&self.handle));
95  }
96}
97
98impl Program {
99  fn new(
100    webgl2: &mut WebGL2,
101    vertex: &Stage,
102    tess: Option<TessellationStages<Stage>>,
103    geometry: Option<&Stage>,
104    fragment: &Stage,
105  ) -> Result<Self, ProgramError> {
106    let state = webgl2.state.borrow();
107
108    let handle = state.ctx.create_program().ok_or_else(|| {
109      ProgramError::CreationFailed("unable to allocate GPU shader program".to_owned())
110    })?;
111
112    if let Some(TessellationStages {
113      control,
114      evaluation,
115    }) = tess
116    {
117      state.ctx.attach_shader(&handle, control.handle());
118      state.ctx.attach_shader(&handle, evaluation.handle());
119    }
120
121    state.ctx.attach_shader(&handle, vertex.handle());
122
123    if let Some(geometry) = geometry {
124      state.ctx.attach_shader(&handle, geometry.handle());
125    }
126
127    state.ctx.attach_shader(&handle, fragment.handle());
128
129    let location_map = Rc::new(RefCell::new(HashMap::new()));
130    let state = webgl2.state.clone();
131    let program = Program {
132      handle,
133      location_map,
134      state,
135    };
136
137    program.link().map(move |_| program)
138  }
139
140  fn link(&self) -> Result<(), ProgramError> {
141    let handle = &self.handle;
142    let state = self.state.borrow();
143
144    state.ctx.link_program(handle);
145
146    let linked = state
147      .ctx
148      .get_program_parameter(handle, WebGl2RenderingContext::LINK_STATUS)
149      .as_bool()
150      .ok_or_else(|| ProgramError::LinkFailed("unknown link status".to_owned()))?;
151
152    if linked {
153      Ok(())
154    } else {
155      let log = state
156        .ctx
157        .get_program_info_log(handle)
158        .unwrap_or("unknown link error".to_owned());
159      Err(ProgramError::link_failed(log))
160    }
161  }
162
163  fn handle(&self) -> &WebGlProgram {
164    &self.handle
165  }
166}
167
168pub struct UniformBuilder {
169  handle: WebGlProgram,
170  location_map: Rc<RefCell<LocationMap>>,
171  state: Rc<RefCell<WebGL2State>>,
172}
173
174impl UniformBuilder {
175  fn new(program: &Program) -> Self {
176    UniformBuilder {
177      handle: program.handle.clone(),
178      location_map: program.location_map.clone(),
179      state: program.state.clone(),
180    }
181  }
182
183  fn ask_uniform<T>(
184    &mut self,
185    name: &str,
186    ty: UniformType,
187    size: usize,
188  ) -> Result<Uniform<T>, UniformWarning>
189  where
190    WebGL2: for<'a> Uniformable<'a, T>,
191  {
192    let location = self
193      .state
194      .borrow()
195      .ctx
196      .get_uniform_location(&self.handle, name);
197
198    match location {
199      Some(location) => {
200        // if we correctly map the uniform, we generate a new ID by using the length of the current
201        // location map — we never remove items, so it will always grow — and insert the indirection
202        let mut location_map = self.location_map.borrow_mut();
203        let idx = location_map.len() as i32;
204        location_map.insert(idx, location);
205
206        // check the type
207        uniform_type_match(
208          &self.state.borrow(),
209          &self.handle,
210          name,
211          idx as u32,
212          ty,
213          size,
214        )?;
215
216        Ok(unsafe { Uniform::new(idx) })
217      }
218
219      None => Err(UniformWarning::inactive(name)),
220    }
221  }
222
223  fn ask_uniform_block<T>(&self, name: &str) -> Result<Uniform<T>, UniformWarning>
224  where
225    WebGL2: for<'a> Uniformable<'a, T>,
226  {
227    let location = self
228      .state
229      .borrow()
230      .ctx
231      .get_uniform_block_index(&self.handle, name);
232
233    if location == WebGl2RenderingContext::INVALID_INDEX {
234      Err(UniformWarning::inactive(name))
235    } else {
236      Ok(unsafe { Uniform::new(location as _) })
237    }
238  }
239}
240
241unsafe impl Shader for WebGL2 {
242  type StageRepr = Stage;
243
244  type ProgramRepr = Program;
245
246  type UniformBuilderRepr = UniformBuilder;
247
248  unsafe fn new_stage(&mut self, ty: StageType, src: &str) -> Result<Self::StageRepr, StageError> {
249    Stage::new(self, ty, src)
250  }
251
252  unsafe fn new_program(
253    &mut self,
254    vertex: &Self::StageRepr,
255    tess: Option<TessellationStages<Self::StageRepr>>,
256    geometry: Option<&Self::StageRepr>,
257    fragment: &Self::StageRepr,
258  ) -> Result<Self::ProgramRepr, ProgramError> {
259    Program::new(self, vertex, tess, geometry, fragment)
260  }
261
262  unsafe fn apply_semantics<Sem>(
263    program: &mut Self::ProgramRepr,
264  ) -> Result<Vec<VertexAttribWarning>, ProgramError>
265  where
266    Sem: Semantics,
267  {
268    let warnings = {
269      let state = program.state.borrow();
270      bind_vertex_attribs_locations::<Sem>(&state, program)
271    };
272
273    // we need to link again to make the location mappings a thing
274    program.link()?;
275    Ok(warnings)
276  }
277
278  unsafe fn new_uniform_builder(
279    program: &mut Self::ProgramRepr,
280  ) -> Result<Self::UniformBuilderRepr, ProgramError> {
281    Ok(UniformBuilder::new(&program))
282  }
283
284  unsafe fn ask_uniform<T>(
285    uniform_builder: &mut Self::UniformBuilderRepr,
286    name: &str,
287  ) -> Result<Uniform<T>, UniformWarning>
288  where
289    Self: for<'a> Uniformable<'a, T>,
290  {
291    let ty = Self::ty();
292    let uniform = match ty {
293      UniformType::ShaderDataBinding => uniform_builder.ask_uniform_block(name)?,
294      _ => uniform_builder.ask_uniform(name, ty, Self::SIZE)?,
295    };
296
297    Ok(uniform)
298  }
299
300  unsafe fn unbound<T>(_: &mut Self::UniformBuilderRepr) -> Uniform<T>
301  where
302    Self: for<'a> Uniformable<'a, T>,
303  {
304    Uniform::new(-1)
305  }
306}
307
308fn webgl_shader_type(ty: StageType) -> Option<u32> {
309  match ty {
310    StageType::VertexShader => Some(WebGl2RenderingContext::VERTEX_SHADER),
311    StageType::FragmentShader => Some(WebGl2RenderingContext::FRAGMENT_SHADER),
312    _ => None,
313  }
314}
315
316const GLSL_PRAGMA: &str = "#version 300 es\n\
317                           precision highp float;\n\
318                           precision highp int;
319                           layout(std140) uniform;\n";
320
321fn patch_shader_src(src: &str) -> String {
322  let mut pragma = String::from(GLSL_PRAGMA);
323  pragma.push_str(src);
324  pragma
325}
326
327fn uniform_type_match(
328  state: &WebGL2State,
329  program: &WebGlProgram,
330  name: &str,
331  location: u32,
332  ty: UniformType,
333  size: usize,
334) -> Result<(), UniformWarning> {
335  // uniform blocks are not handled the same way as regular uniforms, so we can already re-use the previously queried
336  // location, which is an index for them
337  let index = if ty == UniformType::ShaderDataBinding {
338    location
339  } else {
340    // create a 1-item array to hold the name of the uniform we’d like to get information from
341    let name_array = js_sys::Array::new();
342    name_array.push(&name.into()); // push the name as a JsValue
343
344    // get the index of the uniform; it’s represented as an array of a single element, since our
345    // input has only one element
346    state
347      .ctx
348      .get_uniform_indices(program, name_array.as_ref())
349      .ok_or_else(|| UniformWarning::TypeMismatch("cannot retrieve uniform index".to_owned(), ty))?
350      .get(0)
351      .as_f64()
352      .map(|x| x as u32)
353      .ok_or_else(|| {
354        UniformWarning::TypeMismatch("wrong type when retrieving uniform".to_owned(), ty)
355      })?
356  };
357
358  // get its size and type
359  let info = state
360    .ctx
361    .get_active_uniform(program, index)
362    .ok_or_else(|| UniformWarning::TypeMismatch("cannot retrieve active uniform".to_owned(), ty))?;
363
364  let found_size = info.size() as usize;
365  if size > 0 && found_size != size {
366    return Err(UniformWarning::size_mismatch(name, size, found_size));
367  }
368
369  check_types_match(name, ty, info.type_())
370}
371
372#[allow(clippy::cognitive_complexity)]
373fn check_types_match(name: &str, ty: UniformType, glty: u32) -> Result<(), UniformWarning> {
374  // helper macro to check type mismatch for each variant
375  macro_rules! milkcheck {
376    ($ty:expr, $( ( $v:tt, $t:tt ) ),* $(,)?) => {
377      match $ty {
378        $(
379          UniformType::$v => {
380            if glty == WebGl2RenderingContext::$t {
381              Ok(())
382            } else {
383              Err(UniformWarning::type_mismatch(name, ty))
384            }
385          }
386        )*
387
388        _ => Err(UniformWarning::unsupported_type(name, ty))
389      }
390    }
391  }
392
393  milkcheck!(
394    ty,
395    // scalars
396    (Int, INT),
397    (UInt, UNSIGNED_INT),
398    (Float, FLOAT),
399    (Bool, BOOL),
400    // vectors
401    (IVec2, INT_VEC2),
402    (IVec3, INT_VEC3),
403    (IVec4, INT_VEC4),
404    (UIVec2, UNSIGNED_INT_VEC2),
405    (UIVec3, UNSIGNED_INT_VEC3),
406    (UIVec4, UNSIGNED_INT_VEC4),
407    (Vec2, FLOAT_VEC2),
408    (Vec3, FLOAT_VEC3),
409    (Vec4, FLOAT_VEC4),
410    (BVec2, BOOL_VEC2),
411    (BVec3, BOOL_VEC3),
412    (BVec4, BOOL_VEC4),
413    // matrices
414    (M22, FLOAT_MAT2),
415    (M33, FLOAT_MAT3),
416    (M44, FLOAT_MAT4),
417    // textures
418    (ISampler2D, INT_SAMPLER_2D),
419    (ISampler3D, INT_SAMPLER_3D),
420    (ISampler2DArray, INT_SAMPLER_2D_ARRAY),
421    (UISampler2D, UNSIGNED_INT_SAMPLER_2D),
422    (UISampler3D, UNSIGNED_INT_SAMPLER_3D),
423    (UISampler2DArray, UNSIGNED_INT_SAMPLER_2D_ARRAY),
424    (Sampler2D, SAMPLER_2D),
425    (Sampler3D, SAMPLER_3D),
426    (Sampler2DArray, SAMPLER_2D_ARRAY),
427    (ICubemap, INT_SAMPLER_CUBE),
428    (UICubemap, UNSIGNED_INT_SAMPLER_CUBE),
429    (Cubemap, SAMPLER_CUBE),
430  )
431}
432
433fn bind_vertex_attribs_locations<Sem>(
434  state: &WebGL2State,
435  program: &Program,
436) -> Vec<VertexAttribWarning>
437where
438  Sem: Semantics,
439{
440  let mut warnings = Vec::new();
441
442  for desc in Sem::semantics_set() {
443    match get_vertex_attrib_location(state, program, &desc.name) {
444      Ok(_) => {
445        let index = desc.index as u32;
446        state
447          .ctx
448          .bind_attrib_location(program.handle(), index, &desc.name);
449      }
450
451      Err(warning) => warnings.push(warning),
452    }
453  }
454
455  warnings
456}
457
458fn get_vertex_attrib_location(
459  state: &WebGL2State,
460  program: &Program,
461  name: &str,
462) -> Result<i32, VertexAttribWarning> {
463  let location = state.ctx.get_attrib_location(program.handle(), name);
464
465  if location < 0 {
466    Err(VertexAttribWarning::inactive(name))
467  } else {
468    Ok(location)
469  }
470}
471
472// A helper macro used to implement Uniformable for very similar function types. Several forms
473// exist: mostly, slice form (like &[_]) will have to compute a length and pass the length down the
474// WebGL function along with a flatten version of a slice, while scalar versions will simply
475// forward the arguments.
476macro_rules! impl_Uniformable {
477  (vec arr $q:ident $t:ty, $size:expr, $uty:tt, $f:tt) => {
478    unsafe impl<'a, const N: usize> Uniformable<'a, Arr<$q<$t>, N>> for WebGL2 {
479      type Target = &'a [$q<$t>; N];
480
481      const SIZE: usize = N;
482
483      unsafe fn ty() -> UniformType {
484        UniformType::$uty
485      }
486
487      unsafe fn update(
488        program: &mut Program,
489        uniform: &'a Uniform<Arr<$q<$t>, N>>,
490        value: Self::Target,
491      ) {
492        let len = value.len();
493        let data = flatten_slice!(value: $t, len = $size * N);
494
495        program.state.borrow().ctx.$f(
496          program.location_map.borrow().get(&uniform.index()),
497          data,
498          0, // offset
499          len as _,
500        );
501      }
502    }
503  };
504
505  (arr $t:ty , $uty:tt, $f:tt) => {
506    unsafe impl<'a, const N: usize> Uniformable<'a, Arr<$t, N>> for WebGL2 {
507      type Target = &'a [$t; N];
508
509      const SIZE: usize = N;
510
511      unsafe fn ty() -> UniformType {
512        UniformType::$uty
513      }
514
515      unsafe fn update(
516        program: &mut Program,
517        uniform: &'a Uniform<Arr<$t, N>>,
518        value: Self::Target,
519      ) {
520        program
521          .state
522          .borrow()
523          .ctx
524          .$f(program.location_map.borrow().get(&uniform.index()), value);
525      }
526    }
527  };
528
529  (vec $t:ty, $uty:tt, $f:tt) => {
530    unsafe impl<'a> Uniformable<'a, $t> for WebGL2 {
531      type Target = $t;
532
533      const SIZE: usize = 1;
534
535      unsafe fn ty() -> UniformType {
536        UniformType::$uty
537      }
538
539      unsafe fn update(program: &mut Program, uniform: &'a Uniform<$t>, value: Self::Target) {
540        program.state.borrow().ctx.$f(
541          program.location_map.borrow().get(&uniform.index()),
542          value.as_ref(),
543        );
544      }
545    }
546  };
547
548  ($t:ty, $uty:tt, $f:tt) => {
549    unsafe impl<'a> Uniformable<'a, $t> for WebGL2 {
550      type Target = $t;
551
552      const SIZE: usize = 1;
553
554      unsafe fn ty() -> UniformType {
555        UniformType::$uty
556      }
557
558      unsafe fn update(program: &mut Program, uniform: &'a Uniform<$t>, value: Self::Target) {
559        program
560          .state
561          .borrow()
562          .ctx
563          .$f(program.location_map.borrow().get(&uniform.index()), value);
564      }
565    }
566  };
567
568  // matrix notation
569  (mat arr $q:ident $t:ty, $size:expr, $uty:tt, $f:tt) => {
570    unsafe impl<'a, const N: usize> Uniformable<'a, Arr<$q<$t>, N>> for WebGL2 {
571      type Target = &'a [$q<$t>; N];
572
573      const SIZE: usize = N;
574
575      unsafe fn ty() -> UniformType {
576        UniformType::$uty
577      }
578
579      unsafe fn update(
580        program: &mut Program,
581        uniform: &'a Uniform<Arr<$q<$t>, N>>,
582        value: Self::Target,
583      ) {
584        let data = flatten_slice!(value: $t, len = $size * N);
585
586        program.state.borrow().ctx.$f(
587          program.location_map.borrow().get(&uniform.index()),
588          false,
589          data,
590          0,
591          value.len() as u32,
592        );
593      }
594    }
595  };
596
597  (mat $q:ident $t:ty, $size:expr, $uty:tt, $f:tt) => {
598    unsafe impl<'a> Uniformable<'a, $q<$t>> for WebGL2 {
599      type Target = $q<$t>;
600
601      const SIZE: usize = 1;
602
603      unsafe fn ty() -> UniformType {
604        UniformType::$uty
605      }
606
607      unsafe fn update(program: &mut Program, uniform: &'a Uniform<$q<$t>>, value: Self::Target) {
608        let data = flatten_slice!(value: $t, len = $size);
609
610        program.state.borrow().ctx.$f(
611          program.location_map.borrow().get(&uniform.index()),
612          false,
613          data,
614        );
615      }
616    }
617  };
618}
619
620// here we go in deep mud
621impl_Uniformable!(i32, Int, uniform1i);
622impl_Uniformable!(vec Vec2<i32>, IVec2, uniform2iv_with_i32_array);
623impl_Uniformable!(vec Vec3<i32>, IVec3, uniform3iv_with_i32_array);
624impl_Uniformable!(vec Vec4<i32>, IVec4, uniform4iv_with_i32_array);
625impl_Uniformable!(arr i32, Int, uniform1iv_with_i32_array);
626impl_Uniformable!(
627  vec arr Vec2 i32,
628  2,
629  IVec2,
630  uniform2iv_with_i32_array_and_src_offset_and_src_length
631);
632impl_Uniformable!(
633  vec arr Vec3 i32,
634  3,
635  IVec3,
636  uniform3iv_with_i32_array_and_src_offset_and_src_length
637);
638impl_Uniformable!(
639  vec arr Vec4 i32,
640  4,
641  IVec4,
642  uniform4iv_with_i32_array_and_src_offset_and_src_length
643);
644
645impl_Uniformable!(u32, UInt, uniform1ui);
646impl_Uniformable!(vec Vec2<u32>, UIVec2, uniform2uiv_with_u32_array);
647impl_Uniformable!(vec Vec3<u32>, UIVec3, uniform3uiv_with_u32_array);
648impl_Uniformable!(vec Vec4<u32>, UIVec4, uniform4uiv_with_u32_array);
649impl_Uniformable!(arr u32, UInt, uniform1uiv_with_u32_array);
650impl_Uniformable!(
651  vec arr Vec2 u32,
652  2,
653  UIVec2,
654  uniform2uiv_with_u32_array_and_src_offset_and_src_length
655);
656impl_Uniformable!(
657  vec arr Vec3 u32,
658  3,
659  UIVec3,
660  uniform3uiv_with_u32_array_and_src_offset_and_src_length
661);
662impl_Uniformable!(
663  vec arr Vec4 u32,
664  4,
665  UIVec4,
666  uniform4uiv_with_u32_array_and_src_offset_and_src_length
667);
668
669impl_Uniformable!(f32, Float, uniform1f);
670impl_Uniformable!(vec Vec2<f32>, Vec2, uniform2fv_with_f32_array);
671impl_Uniformable!(vec Vec3<f32>, Vec3, uniform3fv_with_f32_array);
672impl_Uniformable!(vec Vec4<f32>, Vec4, uniform4fv_with_f32_array);
673impl_Uniformable!(arr f32, Float, uniform1fv_with_f32_array);
674impl_Uniformable!(
675  vec arr Vec2 f32,
676  2,
677  Vec2,
678  uniform2fv_with_f32_array_and_src_offset_and_src_length
679);
680impl_Uniformable!(
681  vec arr Vec3 f32,
682  3,
683  Vec3,
684  uniform3fv_with_f32_array_and_src_offset_and_src_length
685);
686impl_Uniformable!(
687  vec arr Vec4 f32,
688  4,
689  Vec4,
690  uniform4fv_with_f32_array_and_src_offset_and_src_length
691);
692
693// please don’t judge me
694impl_Uniformable!(mat Mat22 f32, 4, M22, uniform_matrix2fv_with_f32_array);
695impl_Uniformable!(mat arr Mat22 f32, 4, M22, uniform_matrix2fv_with_f32_array_and_src_offset_and_src_length);
696
697impl_Uniformable!(mat Mat33 f32, 9, M33, uniform_matrix3fv_with_f32_array);
698impl_Uniformable!(mat arr Mat33 f32, 9, M33, uniform_matrix3fv_with_f32_array_and_src_offset_and_src_length);
699
700impl_Uniformable!(mat Mat44 f32, 16, M44, uniform_matrix4fv_with_f32_array);
701impl_Uniformable!(mat arr Mat44 f32, 16, M44, uniform_matrix4fv_with_f32_array_and_src_offset_and_src_length);
702
703// Special exception for booleans: because we cannot simply send the bool Rust type down to the
704// GPU, we have to convert them to 32-bit integer (unsigned), which is a total fuck up and waste of
705// memory bandwidth, but well, WebGL / OpenGL, whatcha wanna do. Also, for slice versions… we have
706// to allocate short-living, temporary vectors to hold the converted data.
707//
708// All this makes me so sad that I want a corgi by my side. Please. And chimken nuggets.
709unsafe impl<'a> Uniformable<'a, bool> for WebGL2 {
710  type Target = bool;
711
712  const SIZE: usize = 1;
713
714  unsafe fn ty() -> UniformType {
715    UniformType::Bool
716  }
717
718  unsafe fn update(program: &mut Program, uniform: &'a Uniform<bool>, value: Self::Target) {
719    program.state.borrow().ctx.uniform1ui(
720      program.location_map.borrow().get(&uniform.index()),
721      value as u32,
722    );
723  }
724}
725
726unsafe impl<'a> Uniformable<'a, Vec2<bool>> for WebGL2 {
727  type Target = Vec2<bool>;
728
729  const SIZE: usize = 1;
730
731  unsafe fn ty() -> UniformType {
732    UniformType::BVec2
733  }
734
735  unsafe fn update(program: &mut Program, uniform: &'a Uniform<Vec2<bool>>, value: Self::Target) {
736    let v = [value[0] as u32, value[1] as u32];
737
738    program
739      .state
740      .borrow()
741      .ctx
742      .uniform2uiv_with_u32_array(program.location_map.borrow().get(&uniform.index()), &v);
743  }
744}
745
746unsafe impl<'a> Uniformable<'a, Vec3<bool>> for WebGL2 {
747  type Target = Vec3<bool>;
748
749  const SIZE: usize = 1;
750
751  unsafe fn ty() -> UniformType {
752    UniformType::BVec3
753  }
754
755  unsafe fn update(program: &mut Program, uniform: &'a Uniform<Vec3<bool>>, value: Self::Target) {
756    let v = [value[0] as u32, value[1] as u32, value[2] as u32];
757
758    program
759      .state
760      .borrow()
761      .ctx
762      .uniform3uiv_with_u32_array(program.location_map.borrow().get(&uniform.index()), &v);
763  }
764}
765
766unsafe impl<'a> Uniformable<'a, Vec4<bool>> for WebGL2 {
767  type Target = Vec4<bool>;
768
769  const SIZE: usize = 1;
770
771  unsafe fn ty() -> UniformType {
772    UniformType::BVec4
773  }
774
775  unsafe fn update(program: &mut Program, uniform: &'a Uniform<Vec4<bool>>, value: Self::Target) {
776    let v = [
777      value[0] as u32,
778      value[1] as u32,
779      value[2] as u32,
780      value[3] as u32,
781    ];
782
783    program
784      .state
785      .borrow()
786      .ctx
787      .uniform4uiv_with_u32_array(program.location_map.borrow().get(&uniform.index()), &v);
788  }
789}
790
791// bool cache for optimizing [bool; N] -> [u32; N]
792static mut BOOL_CACHE: Vec<u32> = Vec::new();
793
794unsafe impl<'a, const N: usize> Uniformable<'a, Arr<bool, N>> for WebGL2 {
795  type Target = &'a [bool; N];
796
797  const SIZE: usize = N;
798
799  unsafe fn ty() -> UniformType {
800    UniformType::Bool
801  }
802
803  unsafe fn update(program: &mut Program, uniform: &'a Uniform<Arr<bool, N>>, value: Self::Target) {
804    BOOL_CACHE.clear();
805    BOOL_CACHE.extend(value.iter().map(|x| *x as u32));
806
807    program.state.borrow().ctx.uniform1uiv_with_u32_array(
808      program.location_map.borrow().get(&uniform.index()),
809      &BOOL_CACHE,
810    );
811  }
812}
813
814unsafe impl<'a, const N: usize> Uniformable<'a, Arr<Vec2<bool>, N>> for WebGL2 {
815  type Target = &'a [Vec2<bool>; N];
816
817  const SIZE: usize = N;
818
819  unsafe fn ty() -> UniformType {
820    UniformType::BVec2
821  }
822
823  unsafe fn update(
824    program: &mut Program,
825    uniform: &'a Uniform<Arr<Vec2<bool>, N>>,
826    value: Self::Target,
827  ) {
828    BOOL_CACHE.clear();
829    BOOL_CACHE.extend(value.iter().flat_map(|x| [x[0] as u32, x[1] as u32]));
830
831    program.state.borrow().ctx.uniform2uiv_with_u32_array(
832      program.location_map.borrow().get(&uniform.index()),
833      &BOOL_CACHE,
834    );
835  }
836}
837
838unsafe impl<'a, const N: usize> Uniformable<'a, Arr<Vec3<bool>, N>> for WebGL2 {
839  type Target = &'a [Vec3<bool>; N];
840
841  const SIZE: usize = N;
842
843  unsafe fn ty() -> UniformType {
844    UniformType::BVec3
845  }
846
847  unsafe fn update(
848    program: &mut Program,
849    uniform: &'a Uniform<Arr<Vec3<bool>, N>>,
850    value: Self::Target,
851  ) {
852    BOOL_CACHE.clear();
853    BOOL_CACHE.extend(
854      value
855        .iter()
856        .flat_map(|x| [x[0] as u32, x[1] as u32, x[2] as u32]),
857    );
858
859    program.state.borrow().ctx.uniform3uiv_with_u32_array(
860      program.location_map.borrow().get(&uniform.index()),
861      &BOOL_CACHE,
862    );
863  }
864}
865
866unsafe impl<'a, const N: usize> Uniformable<'a, Arr<Vec4<bool>, N>> for WebGL2 {
867  type Target = &'a [Vec4<bool>; N];
868
869  const SIZE: usize = N;
870
871  unsafe fn ty() -> UniformType {
872    UniformType::BVec4
873  }
874
875  unsafe fn update(
876    program: &mut Program,
877    uniform: &'a Uniform<Arr<Vec4<bool>, N>>,
878    value: Self::Target,
879  ) {
880    BOOL_CACHE.clear();
881    BOOL_CACHE.extend(
882      value
883        .iter()
884        .flat_map(|x| [x[0] as u32, x[1] as u32, x[2] as u32, x[3] as u32]),
885    );
886
887    program.state.borrow().ctx.uniform4uiv_with_u32_array(
888      program.location_map.borrow().get(&uniform.index()),
889      &BOOL_CACHE,
890    );
891  }
892}
893
894unsafe impl<'a, T> Uniformable<'a, ShaderDataBinding<T>> for WebGL2
895where
896  T: 'a,
897{
898  type Target = ShaderDataBinding<T>;
899
900  const SIZE: usize = 0;
901
902  unsafe fn ty() -> UniformType {
903    UniformType::ShaderDataBinding
904  }
905
906  unsafe fn update(
907    program: &mut Program,
908    uniform: &'a Uniform<ShaderDataBinding<T>>,
909    value: Self::Target,
910  ) {
911    program.state.borrow().ctx.uniform_block_binding(
912      &program.handle,
913      uniform.index() as u32,
914      value.binding(),
915    );
916  }
917}
918
919unsafe impl<'a, D, S> Uniformable<'a, TextureBinding<D, S>> for WebGL2
920where
921  D: 'a + Dimensionable,
922  S: 'a + SamplerType,
923{
924  type Target = TextureBinding<D, S>;
925
926  const SIZE: usize = 0;
927
928  unsafe fn ty() -> UniformType {
929    match (S::sample_type(), D::dim()) {
930      (PixelType::NormIntegral, Dim::Dim1) => UniformType::Sampler1D,
931      (PixelType::NormUnsigned, Dim::Dim1) => UniformType::Sampler1D,
932      (PixelType::Integral, Dim::Dim1) => UniformType::ISampler1D,
933      (PixelType::Unsigned, Dim::Dim1) => UniformType::UISampler1D,
934      (PixelType::Floating, Dim::Dim1) => UniformType::Sampler1D,
935
936      (PixelType::NormIntegral, Dim::Dim2) => UniformType::Sampler2D,
937      (PixelType::NormUnsigned, Dim::Dim2) => UniformType::Sampler2D,
938      (PixelType::Integral, Dim::Dim2) => UniformType::ISampler2D,
939      (PixelType::Unsigned, Dim::Dim2) => UniformType::UISampler2D,
940      (PixelType::Floating, Dim::Dim2) => UniformType::Sampler2D,
941
942      (PixelType::NormIntegral, Dim::Dim3) => UniformType::Sampler3D,
943      (PixelType::NormUnsigned, Dim::Dim3) => UniformType::Sampler3D,
944      (PixelType::Integral, Dim::Dim3) => UniformType::ISampler3D,
945      (PixelType::Unsigned, Dim::Dim3) => UniformType::UISampler3D,
946      (PixelType::Floating, Dim::Dim3) => UniformType::Sampler3D,
947
948      (PixelType::NormIntegral, Dim::Cubemap) => UniformType::Cubemap,
949      (PixelType::NormUnsigned, Dim::Cubemap) => UniformType::Cubemap,
950      (PixelType::Integral, Dim::Cubemap) => UniformType::ICubemap,
951      (PixelType::Unsigned, Dim::Cubemap) => UniformType::UICubemap,
952      (PixelType::Floating, Dim::Cubemap) => UniformType::Cubemap,
953
954      (PixelType::NormIntegral, Dim::Dim1Array) => UniformType::Sampler1DArray,
955      (PixelType::NormUnsigned, Dim::Dim1Array) => UniformType::Sampler1DArray,
956      (PixelType::Integral, Dim::Dim1Array) => UniformType::ISampler1DArray,
957      (PixelType::Unsigned, Dim::Dim1Array) => UniformType::UISampler1DArray,
958      (PixelType::Floating, Dim::Dim1Array) => UniformType::Sampler1DArray,
959
960      (PixelType::NormIntegral, Dim::Dim2Array) => UniformType::Sampler2DArray,
961      (PixelType::NormUnsigned, Dim::Dim2Array) => UniformType::Sampler2DArray,
962      (PixelType::Integral, Dim::Dim2Array) => UniformType::ISampler2DArray,
963      (PixelType::Unsigned, Dim::Dim2Array) => UniformType::UISampler2DArray,
964      (PixelType::Floating, Dim::Dim2Array) => UniformType::Sampler2DArray,
965    }
966  }
967
968  unsafe fn update(
969    program: &mut Program,
970    uniform: &'a Uniform<TextureBinding<D, S>>,
971    value: Self::Target,
972  ) {
973    program.state.borrow().ctx.uniform1i(
974      program.location_map.borrow().get(&uniform.index()),
975      value.binding() as i32,
976    );
977  }
978}
979
980unsafe impl<T> ShaderData<T> for WebGL2
981where
982  T: Std140,
983{
984  type ShaderDataRepr =
985    Buffer<<ArrElem<T> as Std140>::Encoded, { WebGl2RenderingContext::UNIFORM_BUFFER }>;
986
987  unsafe fn new_shader_data(
988    &mut self,
989    values: impl Iterator<Item = T>,
990  ) -> Result<Self::ShaderDataRepr, ShaderDataError> {
991    Buffer::from_vec(
992      self,
993      values
994        .into_iter()
995        .map(|x| ArrElem(x).std140_encode())
996        .collect(),
997    )
998    .map_err(|BufferError::CannotCreate| ShaderDataError::CannotCreate)
999  }
1000
1001  unsafe fn get_shader_data_at(
1002    shader_data: &Self::ShaderDataRepr,
1003    i: usize,
1004  ) -> Result<T, ShaderDataError> {
1005    shader_data
1006      .buf
1007      .get(i)
1008      .map(|&x| <ArrElem<T> as Std140>::std140_decode(x).0)
1009      .ok_or_else(|| ShaderDataError::OutOfBounds { index: i })
1010  }
1011
1012  unsafe fn set_shader_data_at(
1013    shader_data: &mut Self::ShaderDataRepr,
1014    i: usize,
1015    x: T,
1016  ) -> Result<T, ShaderDataError> {
1017    let prev = mem::replace(
1018      &mut shader_data.slice_buffer_mut()[i],
1019      ArrElem(x).std140_encode(),
1020    );
1021
1022    Ok(<ArrElem<T> as Std140>::std140_decode(prev).0)
1023  }
1024
1025  unsafe fn set_shader_data_values(
1026    shader_data: &mut Self::ShaderDataRepr,
1027    values: impl Iterator<Item = T>,
1028  ) -> Result<(), ShaderDataError> {
1029    let mut slice = shader_data.slice_buffer_mut();
1030
1031    for (item, value) in slice.iter_mut().zip(values) {
1032      *item = ArrElem(value).std140_encode();
1033    }
1034
1035    Ok(())
1036  }
1037}