luminance/
shader.rs

1//! Shader stages, programs and uniforms.
2//!
3//! This module contains everything related to _shaders_. Shader programs — shaders, for short —
4//! are GPU binaries that run to perform a series of transformation. Typically run when a draw
5//! command is issued, they are responsible for:
6//!
7//! - Transforming vertex data. This is done in a _vertex shader_. Vertex data, such as the
8//!   positions, colors, UV coordinates, bi-tangents, etc. of each vertices will go through the
9//!   vertex shader and get transformed based on the code provided inside the stage. For example,
10//!   vertices can be projected on the screen with a perspective and view matrices.
11//! - Tessellating primitive patches. This is done in _tessellation shaders_.
12//! - Filtering, transforming again or even generating new vertices and primitives. This is done
13//!   by the _geometry shader_.
14//! - And finally, outputting a color for each _fragment_ covered by the objects you render. This
15//!   is done by the _fragment shader_.
16//!
17//! # Shader stages
18//!
19//! Right now, five shader stages  — [`Stage`] — are supported, ordered by usage in the graphics
20//! pipeline:
21//!
22//! 1. [`StageType::VertexShader`].
23//! 2. [`StageType::TessellationControlShader`].
24//! 3. [`StageType::TessellationEvaluationShader`].
25//! 4. [`StageType::GeometryShader`].
26//! 5. [`StageType::FragmentShader`].
27//!
28//! Those are not all mandatory: only the _vertex_ stage and _fragment_ stages are mandatory. If
29//! you want tessellation shaders, you have to provide both of them.
30//!
31//! Shader stages — [`Stage`] — are compiled independently at runtime by your GPU driver, and then
32//! _linked_ into a shader program. The creation of a [`Stage`] implies using an input string,
33//! representing the _source code_ of the stage. This is an opaque [`String`] that must represent
34//! a GLSL stage. The backend will transform the string into its own representation if needed.
35//!
36//! > For this version of the crate, the GLSL string must be at least 330-compliant. It is possible
37//! > that this changes in the future to be more flexible, but right now GLSL 150, for instance, is
38//! > not allowed.
39//!
40//! # Shader program
41//!
42//! A shader program — [`Program`] is akin to a binary program, but runs on GPU. It is invoked when
43//! you issue draw commands. It will run each stages you’ve put in it to transform vertices and
44//! rasterize fragments inside a framebuffer. Once this is done, the framebuffer will contain
45//! altered fragments by the final stage (fragment shader). If the shader program outputs several
46//! properties, we call that situation _MRT_ (Multiple Render Target) and the framebuffer must be
47//! configured to be able to receive those outputs — basically, it means that its _color slots_
48//! and/or _depth slots_ must adapt to the output of the shader program.
49//!
50//! Creating shader programs is done by gathering the [`Stage`] you want and _linking_ them. Some
51//! helper methods allow to create a shader [`Program`] directly from the string source for each
52//! stage, removing the need to build each stage individually.
53//!
54//! Shader programs are typed with three important piece of information:
55//!
56//! - The vertex [`Semantics`].
57//! - The render target outputs.
58//! - The [`UniformInterface`].
59//!
60//!
61//! # Vertex semantics
62//!
63//! When a shader program runs, it first executes the mandatory _vertex stage on a set of
64//! vertices. Those vertices have a given format — that is described by the [`Vertex`] trait.
65//! Because running a shader on an incompatible vertex would yield wrong results, both the
66//! vertices and the shader program must be tagged with a type which must implement the
67//! [`Semantics`] trait. More on that on the documentation of [`Semantics`].
68//!
69//! # Render target outputs
70//!
71//! A shader program, in its final mandatory _fragment stage_, will write values into the currently
72//! in-use framebuffer. The number of “channels” to write to represents the render targets.
73//! Typically, simple renders will simply write the color of a pixel — so only one render target.
74//! In that case, the type of the output of the shader program must match the color slot of the
75//! framebuffer it is used with.
76//!
77//! However, it is possible to write more data. For instance,
78//! [deferred shading](https://en.wikipedia.org/wiki/Deferred_shading) is a technique that requires
79//! to write several data to a framebuffer, called G-buffer (for geometry buffer): space
80//! coordinates, normals, tangents, bi-tangents, etc. In that case, your framebuffer must have
81//! a type matching the outputs of the fragment shader, too.
82//!
83//! # Shader customization
84//!
85//! A shader [`Program`] represents some code, in a binary form, that transform data. If you
86//! consider such code, it can adapt to the kind of data it receives, but the behavior is static.
87//! That means that it shouldn’t be possible to ask the program to do something else — shader
88//! programs don’t have a state as they must be spawned in parallel for your vertices, pixels, etc.
89//! However, there is a way to dynamically change what happens inside a shader program. That way
90//!
91//! The concept is similar to environment variables: you can declare, in your shader stages,
92//! _environment variables_ that will receive values from the host (i.e. on the Rust side). It is
93//! not possible to change those values while a draw command is issued: you have to change them
94//! in between draw commands. For this reason, those environment variables are often called
95//! _constant buffers_, _uniform_, _uniform buffers_, etc. by several graphics libraries. In our
96//! case, right now, we call them [`Uniform`].
97//!
98//! ## Uniforms
99//!
100//! A [`Uniform`] is parametric type that accepts the type of the value it will be able to change.
101//! For instance, `Uniform<f32>` represents a `f32` that can be changed in a shader program. That
102//! value can be set by the Rust program when the shader program is not currently in use — no
103//! draw commands.
104//!
105//! A [`Uniform`] is a _single_ variable that allows the most basic form of customization. It’s
106//! very similar to environment variables. You can declare several ones as you would declare
107//! several environment variables. More on that on the documentation of [`Uniform`].
108//!
109//! ## Shader data
110//!
111//! Another way to pass data to shader is to use a [`ShaderData`]. This kind of object allows to
112//! shared data between shaders: set the data once, it will be available to any shader you pass the
113//! [`ShaderData`] to.
114//!
115//! Most implementation also allows much more data via this mechanism, allowing to pass huge amount
116//! of data to implement various techniques, such as _geometry instancing_ for instance.
117//!
118//! ## Uniform interfaces
119//!
120//! As with vertex semantics and render targets, the uniforms that can be used with a shader program
121//! are part of its type, too, and represented by a single type that must implement
122//! [`UniformInterface`]. That type can contain anything, but it is advised to just put [`Uniform`]
123//! fields in it. More on the [`UniformInterface`] documentation.
124//!
125//! [`Vertex`]: crate::vertex::Vertex
126//! [`Pipeline`]: crate::pipeline::Pipeline
127//! [`ShaderData`]: crate::shader::ShaderData
128
129pub mod types;
130
131use crate::{
132  backend::shader::{Shader, ShaderData as ShaderDataBackend, Uniformable},
133  context::GraphicsContext,
134  vertex::Semantics,
135};
136use std::{error, fmt, marker::PhantomData};
137
138/// A shader stage type.
139#[derive(Clone, Copy, Debug, Eq, PartialEq)]
140pub enum StageType {
141  /// Vertex shader.
142  VertexShader,
143  /// Tessellation control shader.
144  TessellationControlShader,
145  /// Tessellation evaluation shader.
146  TessellationEvaluationShader,
147  /// Geometry shader.
148  GeometryShader,
149  /// Fragment shader.
150  FragmentShader,
151}
152
153impl fmt::Display for StageType {
154  fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
155    match *self {
156      StageType::VertexShader => f.write_str("vertex shader"),
157      StageType::TessellationControlShader => f.write_str("tessellation control shader"),
158      StageType::TessellationEvaluationShader => f.write_str("tessellation evaluation shader"),
159      StageType::GeometryShader => f.write_str("geometry shader"),
160      StageType::FragmentShader => f.write_str("fragment shader"),
161    }
162  }
163}
164
165/// Errors that shader stages can emit.
166#[non_exhaustive]
167#[derive(Clone, Debug, Eq, PartialEq)]
168pub enum StageError {
169  /// Occurs when a shader fails to compile.
170  CompilationFailed(StageType, String),
171  /// Occurs when you try to create a shader which type is not supported on the current hardware.
172  UnsupportedType(StageType),
173}
174
175impl StageError {
176  /// Occurs when a shader fails to compile.
177  pub fn compilation_failed(ty: StageType, reason: impl Into<String>) -> Self {
178    StageError::CompilationFailed(ty, reason.into())
179  }
180
181  /// Occurs when you try to create a shader which type is not supported on the current hardware.
182  pub fn unsupported_type(ty: StageType) -> Self {
183    StageError::UnsupportedType(ty)
184  }
185}
186
187impl fmt::Display for StageError {
188  fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
189    match *self {
190      StageError::CompilationFailed(ref ty, ref r) => write!(f, "{} compilation error: {}", ty, r),
191
192      StageError::UnsupportedType(ty) => write!(f, "unsupported {}", ty),
193    }
194  }
195}
196
197impl error::Error for StageError {}
198
199impl From<StageError> for ProgramError {
200  fn from(e: StageError) -> Self {
201    ProgramError::StageError(e)
202  }
203}
204
205/// Tessellation stages.
206///
207/// - The `control` stage represents the _tessellation control stage_, which is invoked first.
208/// - The `evaluation` stage represents the _tessellation evaluation stage_, which is invoked after
209///   the control stage has finished.
210///
211/// # Parametricity
212///
213/// - `S` is the representation of the stage. Depending on the interface you choose to create a
214///   [`Program`], it might be a [`Stage`] or something akin to [`&str`] / [`String`].
215///
216/// [`&str`]: str
217pub struct TessellationStages<'a, S>
218where
219  S: ?Sized,
220{
221  /// Tessellation control representation.
222  pub control: &'a S,
223  /// Tessellation evaluation representation.
224  pub evaluation: &'a S,
225}
226
227/// Errors that a [`Program`] can generate.
228#[non_exhaustive]
229#[derive(Debug, Eq, PartialEq)]
230pub enum ProgramError {
231  /// Creating the program failed.
232  CreationFailed(String),
233  /// A shader stage failed to compile or validate its state.
234  StageError(StageError),
235  /// Program link failed. You can inspect the reason by looking at the contained [`String`].
236  LinkFailed(String),
237  /// A program warning.
238  Warning(ProgramWarning),
239}
240
241impl ProgramError {
242  /// Creating the program failed.
243  pub fn creation_failed(reason: impl Into<String>) -> Self {
244    ProgramError::CreationFailed(reason.into())
245  }
246
247  /// A shader stage failed to compile or validate its state.
248  pub fn stage_error(e: StageError) -> Self {
249    ProgramError::StageError(e)
250  }
251
252  /// Program link failed. You can inspect the reason by looking at the contained [`String`].
253  pub fn link_failed(reason: impl Into<String>) -> Self {
254    ProgramError::LinkFailed(reason.into())
255  }
256
257  /// A program warning.
258  pub fn warning(w: ProgramWarning) -> Self {
259    ProgramError::Warning(w)
260  }
261}
262
263impl fmt::Display for ProgramError {
264  fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
265    match *self {
266      ProgramError::CreationFailed(ref e) => write!(f, "cannot create shader program: {}", e),
267
268      ProgramError::StageError(ref e) => write!(f, "shader program has stage error: {}", e),
269
270      ProgramError::LinkFailed(ref s) => write!(f, "shader program failed to link: {}", s),
271
272      ProgramError::Warning(ref e) => write!(f, "shader program warning: {}", e),
273    }
274  }
275}
276
277impl error::Error for ProgramError {
278  fn source(&self) -> Option<&(dyn error::Error + 'static)> {
279    match self {
280      ProgramError::StageError(e) => Some(e),
281      _ => None,
282    }
283  }
284}
285
286/// Program warnings, not necessarily considered blocking errors.
287#[derive(Debug, Eq, PartialEq)]
288pub enum ProgramWarning {
289  /// Some uniform configuration is ill-formed. It can be a problem of inactive uniform, mismatch
290  /// type, etc. Check the [`UniformWarning`] type for more information.
291  Uniform(UniformWarning),
292  /// Some vertex attribute is ill-formed.
293  VertexAttrib(VertexAttribWarning),
294}
295
296impl fmt::Display for ProgramWarning {
297  fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
298    match *self {
299      ProgramWarning::Uniform(ref e) => write!(f, "uniform warning: {}", e),
300      ProgramWarning::VertexAttrib(ref e) => write!(f, "vertex attribute warning: {}", e),
301    }
302  }
303}
304
305impl error::Error for ProgramWarning {
306  fn source(&self) -> Option<&(dyn error::Error + 'static)> {
307    match self {
308      ProgramWarning::Uniform(e) => Some(e),
309      ProgramWarning::VertexAttrib(e) => Some(e),
310    }
311  }
312}
313
314impl From<ProgramWarning> for ProgramError {
315  fn from(e: ProgramWarning) -> Self {
316    ProgramError::Warning(e)
317  }
318}
319
320/// Warnings related to uniform issues.
321#[non_exhaustive]
322#[derive(Debug, Eq, PartialEq)]
323pub enum UniformWarning {
324  /// Inactive uniform (not in use / no participation to the final output in shaders).
325  Inactive(String),
326
327  /// Type mismatch between the static requested type (i.e. the `T` in [`Uniform<T>`] for instance)
328  /// and the type that got reflected from the backend in the shaders.
329  ///
330  /// The [`String`] is the name of the uniform; the second one gives the type mismatch.
331  ///
332  /// [`Uniform<T>`]: crate::shader::Uniform
333  TypeMismatch(String, UniformType),
334
335  /// The requested type is unsupported by the backend.
336  ///
337  /// The [`String`] is the name of the uniform. The [`UniformType`] is the type that is not
338  /// supported by the backend.
339  UnsupportedType(String, UniformType),
340
341  /// Size mismatch between the static requested type (i.e. the `T` in [`Uniform<T>`] for instance)
342  /// and the size that got reflected from the backend in the shaders.
343  ///
344  /// [`Uniform<T>`]: crate::shader::Uniform
345  SizeMismatch {
346    /// Name of the uniform.
347    name: String,
348
349    /// Size of the uniform (static).
350    size: usize,
351
352    /// Found size of the uniform (in the shader).
353    found_size: usize,
354  },
355}
356
357impl UniformWarning {
358  /// Create an inactive uniform warning.
359  pub fn inactive<N>(name: N) -> Self
360  where
361    N: Into<String>,
362  {
363    UniformWarning::Inactive(name.into())
364  }
365
366  /// Create a type mismatch.
367  pub fn type_mismatch<N>(name: N, ty: UniformType) -> Self
368  where
369    N: Into<String>,
370  {
371    UniformWarning::TypeMismatch(name.into(), ty)
372  }
373
374  /// Create an unsupported type error.
375  pub fn unsupported_type<N>(name: N, ty: UniformType) -> Self
376  where
377    N: Into<String>,
378  {
379    UniformWarning::UnsupportedType(name.into(), ty)
380  }
381
382  /// Create a size mismatch error.
383  pub fn size_mismatch(name: impl Into<String>, size: usize, found_size: usize) -> Self {
384    UniformWarning::SizeMismatch {
385      name: name.into(),
386      size,
387      found_size,
388    }
389  }
390}
391
392impl fmt::Display for UniformWarning {
393  fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
394    match *self {
395      UniformWarning::Inactive(ref s) => write!(f, "inactive {} uniform", s),
396
397      UniformWarning::TypeMismatch(ref n, ref t) => {
398        write!(f, "type mismatch for uniform {}: {}", n, t)
399      }
400
401      UniformWarning::UnsupportedType(ref name, ref ty) => {
402        write!(f, "unsupported type {} for uniform {}", ty, name)
403      }
404
405      UniformWarning::SizeMismatch {
406        ref name,
407        size,
408        found_size,
409      } => {
410        write!(
411          f,
412          "size mismatch for uniform {}: {} (tdetected size={}",
413          name, size, found_size
414        )
415      }
416    }
417  }
418}
419
420impl From<UniformWarning> for ProgramWarning {
421  fn from(e: UniformWarning) -> Self {
422    ProgramWarning::Uniform(e)
423  }
424}
425
426impl error::Error for UniformWarning {}
427
428/// Warnings related to vertex attributes issues.
429#[non_exhaustive]
430#[derive(Debug, Eq, PartialEq)]
431pub enum VertexAttribWarning {
432  /// Inactive vertex attribute (not read).
433  Inactive(String),
434}
435
436impl VertexAttribWarning {
437  /// Inactive vertex attribute (not read).
438  pub fn inactive(attrib: impl Into<String>) -> Self {
439    VertexAttribWarning::Inactive(attrib.into())
440  }
441}
442
443impl fmt::Display for VertexAttribWarning {
444  fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
445    match *self {
446      VertexAttribWarning::Inactive(ref s) => write!(f, "inactive {} vertex attribute", s),
447    }
448  }
449}
450
451impl From<VertexAttribWarning> for ProgramWarning {
452  fn from(e: VertexAttribWarning) -> Self {
453    ProgramWarning::VertexAttrib(e)
454  }
455}
456
457impl error::Error for VertexAttribWarning {}
458
459/// A GPU shader program environment variable.
460///
461/// A uniform is a special variable that can be used to send data to a GPU. Several
462/// forms exist, but the idea is that `T` represents the data you want to send. Some exceptions
463/// exist that allow to pass shared data — such as [`ShaderDataBinding`] to pass a
464/// [`ShaderData`], or [`TextureBinding`] to pass a [`Texture`] in order to fetch from it in a
465/// shader stage.
466///
467/// You will never be able to store them by your own. Instead, you must use a [`UniformInterface`],
468/// which provides a _contravariant_ interface for you. Creation is `unsafe` and should be
469/// avoided. The [`UniformInterface`] is the only safe way to create those.
470///
471/// # Parametricity
472///
473/// - `T` is the type of data you want to be able to set in a shader program.
474///
475/// [`Texture`]: crate::texture::Texture
476/// [`TextureBinding`]: crate::pipeline::TextureBinding
477/// [`ShaderDataBinding`]: crate::pipeline::ShaderDataBinding
478#[derive(Debug)]
479pub struct Uniform<T>
480where
481  T: ?Sized,
482{
483  index: i32,
484  _t: PhantomData<*const T>,
485}
486
487impl<T> Uniform<T>
488where
489  T: ?Sized,
490{
491  /// Create a new [`Uniform`].
492  ///
493  /// # Safety
494  ///
495  /// This method must be used **only** by backends. If you end up using it,
496  /// then you’re doing something wrong. Read on [`UniformInterface`] for further
497  /// information.
498  pub unsafe fn new(index: i32) -> Self {
499    Uniform {
500      index,
501      _t: PhantomData,
502    }
503  }
504
505  /// Retrieve the internal index.
506  ///
507  /// Even though that function is safe, you have no reason to use it. Read on
508  /// [`UniformInterface`] for further details.
509  pub fn index(&self) -> i32 {
510    self.index
511  }
512}
513
514/// Type of a uniform.
515///
516/// This is an exhaustive list of possible types of value you can send to a shader program.
517/// A [`UniformType`] is associated to any type that can be considered sent via the
518/// [`Uniformable`] trait.
519#[derive(Clone, Copy, Debug, Eq, PartialEq)]
520pub enum UniformType {
521  // scalars
522  /// 32-bit signed integer.
523  Int,
524  /// 32-bit unsigned integer.
525  UInt,
526  /// 32-bit floating-point number.
527  Float,
528  /// 64-bit floating-point number.
529  Double,
530  /// Boolean.
531  Bool,
532
533  // vectors
534  /// 2D signed integral vector.
535  IVec2,
536  /// 3D signed integral vector.
537  IVec3,
538  /// 4D signed integral vector.
539  IVec4,
540  /// 2D unsigned integral vector.
541  UIVec2,
542  /// 3D unsigned integral vector.
543  UIVec3,
544  /// 4D unsigned integral vector.
545  UIVec4,
546  /// 2D floating-point vector.
547  Vec2,
548  /// 3D floating-point vector.
549  Vec3,
550  /// 4D floating-point vector.
551  Vec4,
552  /// 2D floating-point (double) vector.
553  DVec2,
554  /// 3D floating-point (double) vector.
555  DVec3,
556  /// 4D floating-point (double) vector.
557  DVec4,
558  /// 2D boolean vector.
559  BVec2,
560  /// 3D boolean vector.
561  BVec3,
562  /// 4D boolean vector.
563  BVec4,
564
565  // matrices
566  /// 2×2 floating-point matrix.
567  M22,
568  /// 3×3 floating-point matrix.
569  M33,
570  /// 4×4 floating-point matrix.
571  M44,
572  /// 2×2 floating-point (double) matrix.
573  DM22,
574  /// 3×3 floating-point (double) matrix.
575  DM33,
576  /// 4×4 floating-point (double) matrix.
577  DM44,
578
579  // textures
580  /// Signed integral 1D texture sampler.
581  ISampler1D,
582  /// Signed integral 2D texture sampler.
583  ISampler2D,
584  /// Signed integral 3D texture sampler.
585  ISampler3D,
586  /// Signed integral 1D array texture sampler.
587  ISampler1DArray,
588  /// Signed integral 2D array texture sampler.
589  ISampler2DArray,
590  /// Unsigned integral 1D texture sampler.
591  UISampler1D,
592  /// Unsigned integral 2D texture sampler.
593  UISampler2D,
594  /// Unsigned integral 3D texture sampler.
595  UISampler3D,
596  /// Unsigned integral 1D array texture sampler.
597  UISampler1DArray,
598  /// Unsigned integral 2D array texture sampler.
599  UISampler2DArray,
600  /// Floating-point 1D texture sampler.
601  Sampler1D,
602  /// Floating-point 2D texture sampler.
603  Sampler2D,
604  /// Floating-point 3D texture sampler.
605  Sampler3D,
606  /// Floating-point 1D array texture sampler.
607  Sampler1DArray,
608  /// Floating-point 2D array texture sampler.
609  Sampler2DArray,
610  /// Signed cubemap sampler.
611  ICubemap,
612  /// Unsigned cubemap sampler.
613  UICubemap,
614  /// Floating-point cubemap sampler.
615  Cubemap,
616
617  /// Shader data binding.
618  ShaderDataBinding,
619}
620
621impl fmt::Display for UniformType {
622  fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
623    match *self {
624      UniformType::Int => f.write_str("int"),
625      UniformType::UInt => f.write_str("uint"),
626      UniformType::Float => f.write_str("float"),
627      UniformType::Double => f.write_str("double"),
628      UniformType::Bool => f.write_str("bool"),
629      UniformType::IVec2 => f.write_str("ivec2"),
630      UniformType::IVec3 => f.write_str("ivec3"),
631      UniformType::IVec4 => f.write_str("ivec4"),
632      UniformType::UIVec2 => f.write_str("uvec2"),
633      UniformType::UIVec3 => f.write_str("uvec3"),
634      UniformType::UIVec4 => f.write_str("uvec4"),
635      UniformType::Vec2 => f.write_str("vec2"),
636      UniformType::Vec3 => f.write_str("vec3"),
637      UniformType::Vec4 => f.write_str("vec4"),
638      UniformType::DVec2 => f.write_str("dvec2"),
639      UniformType::DVec3 => f.write_str("dvec3"),
640      UniformType::DVec4 => f.write_str("dvec4"),
641      UniformType::BVec2 => f.write_str("bvec2"),
642      UniformType::BVec3 => f.write_str("bvec3"),
643      UniformType::BVec4 => f.write_str("bvec4"),
644      UniformType::M22 => f.write_str("mat2"),
645      UniformType::M33 => f.write_str("mat3"),
646      UniformType::M44 => f.write_str("mat4"),
647      UniformType::DM22 => f.write_str("dmat2"),
648      UniformType::DM33 => f.write_str("dmat3"),
649      UniformType::DM44 => f.write_str("dmat4"),
650      UniformType::ISampler1D => f.write_str("isampler1D"),
651      UniformType::ISampler2D => f.write_str("isampler2D"),
652      UniformType::ISampler3D => f.write_str("isampler3D"),
653      UniformType::ISampler1DArray => f.write_str("isampler1DArray"),
654      UniformType::ISampler2DArray => f.write_str("isampler2DArray"),
655      UniformType::UISampler1D => f.write_str("usampler1D"),
656      UniformType::UISampler2D => f.write_str("usampler2D"),
657      UniformType::UISampler3D => f.write_str("usampler3D"),
658      UniformType::UISampler1DArray => f.write_str("usampler1DArray"),
659      UniformType::UISampler2DArray => f.write_str("usampler2DArray"),
660      UniformType::Sampler1D => f.write_str("sampler1D"),
661      UniformType::Sampler2D => f.write_str("sampler2D"),
662      UniformType::Sampler3D => f.write_str("sampler3D"),
663      UniformType::Sampler1DArray => f.write_str("sampler1DArray"),
664      UniformType::Sampler2DArray => f.write_str("sampler2DArray"),
665      UniformType::ICubemap => f.write_str("isamplerCube"),
666      UniformType::UICubemap => f.write_str("usamplerCube"),
667      UniformType::Cubemap => f.write_str("samplerCube"),
668      UniformType::ShaderDataBinding => f.write_str("shader data binding"),
669    }
670  }
671}
672
673/// A shader stage.
674///
675/// # Parametricity
676///
677/// - `B` is the backend type.
678///
679/// [`&str`]: str
680pub struct Stage<B>
681where
682  B: ?Sized + Shader,
683{
684  repr: B::StageRepr,
685}
686
687impl<B> Stage<B>
688where
689  B: ?Sized + Shader,
690{
691  /// Create a new stage of type `ty` by compiling `src`.
692  ///
693  /// # Parametricity
694  ///
695  /// - `C` is the graphics context. `C::Backend` must implement the [`Shader`] trait.
696  /// - `R` is the source code to use in the stage. It must implement [`AsRef<str>`].
697  ///
698  /// # Notes
699  ///
700  /// Feel free to consider using [`GraphicsContext::new_shader_stage`] for a simpler form of
701  /// this method.
702  ///
703  /// [`AsRef<str>`]: AsRef
704  pub fn new<C, R>(ctx: &mut C, ty: StageType, src: R) -> Result<Self, StageError>
705  where
706    C: GraphicsContext<Backend = B>,
707    R: AsRef<str>,
708  {
709    unsafe {
710      ctx
711        .backend()
712        .new_stage(ty, src.as_ref())
713        .map(|repr| Stage { repr })
714    }
715  }
716}
717
718/// A builder of [`Uniform`].
719///
720/// A [`UniformBuilder`] is an important type as it’s the only one that allows to safely create
721/// [`Uniform`] values.
722///
723/// # Parametricity
724///
725/// - `B` is the backend type. It must implement the [`Shader`] trait.
726pub struct UniformBuilder<'a, B>
727where
728  B: ?Sized + Shader,
729{
730  repr: B::UniformBuilderRepr,
731  warnings: Vec<UniformWarning>,
732  _a: PhantomData<&'a mut ()>,
733}
734
735impl<'a, B> UniformBuilder<'a, B>
736where
737  B: ?Sized + Shader,
738{
739  /// Ask the creation of a [`Uniform`], identified by its `name`.
740  pub fn ask<T>(&mut self, name: &str) -> Result<Uniform<T>, UniformWarning>
741  where
742    B: for<'u> Uniformable<'u, T>,
743  {
744    unsafe { B::ask_uniform(&mut self.repr, name) }
745  }
746
747  /// Ask the creation of a [`Uniform`], identified by its `name`.
748  ///
749  /// If the name is not found, an _unbound_ [`Uniform`] is returned (i.e. a [`Uniform`]) that does
750  /// nothing.
751  pub fn ask_or_unbound<T>(&mut self, name: &str) -> Uniform<T>
752  where
753    B: for<'u> Uniformable<'u, T>,
754  {
755    match self.ask(name) {
756      Ok(uniform) => uniform,
757      Err(err) => {
758        self.warnings.push(err);
759        unsafe { B::unbound(&mut self.repr) }
760      }
761    }
762  }
763}
764
765/// [`Uniform`] interface.
766///
767/// When a type implements [`UniformInterface`], it means that it can be used as part of a shader
768/// [`Program`] type. When a [`Program`] is in use in a graphics pipeline, its [`UniformInterface`]
769/// is automatically provided to the user, giving them access to all the fields declared in. Then,
770/// they can pass data to shaders before issuing draw commands.
771///
772/// # Parametricity
773///
774/// - `B` is the backend type. It must implement [`Shader`].
775/// - `E` is the environment type. Set by default to `()`, it allows to pass a mutable
776///   object at construction-site of the [`UniformInterface`]. It can be useful to generate
777///   events or customize the way the [`Uniform`] are built by doing some lookups in hashmaps, etc.
778///
779/// # Notes
780///
781/// Implementing this trait — especially [`UniformInterface::uniform_interface`] can be a bit
782/// overwhelming. It is highly recommended to use [luminance-derive]’s `UniformInterface`
783/// proc-macro, which will do that for you by scanning your type declaration.
784///
785/// [luminance-derive]: https://crates.io/crates/luminance-derive
786pub trait UniformInterface<B, E = ()>: Sized
787where
788  B: Shader,
789{
790  /// Create a [`UniformInterface`] by constructing [`Uniform`]s with a [`UniformBuilder`] and an
791  /// optional environment object.
792  ///
793  /// This method is the only place where `Self` should be created. In theory, you could create it
794  /// the way you want (since the type is provided by you) but not all types make sense. You will
795  /// likely want to have some [`Uniform`] objects in your type, and the [`UniformBuilder`] that is
796  /// provided as argument is the only way to create them.
797  fn uniform_interface<'a>(
798    builder: &mut UniformBuilder<'a, B>,
799    env: &mut E,
800  ) -> Result<Self, UniformWarning>;
801}
802
803impl<B, E> UniformInterface<B, E> for ()
804where
805  B: Shader,
806{
807  fn uniform_interface<'a>(
808    _: &mut UniformBuilder<'a, B>,
809    _: &mut E,
810  ) -> Result<Self, UniformWarning> {
811    Ok(())
812  }
813}
814
815/// A built program with potential warnings.
816///
817/// The sole purpose of this type is to be destructured when a program is built.
818///
819/// # Parametricity
820///
821/// - `B` is the backend type.
822/// - `Sem` is the [`Semantics`] type.
823/// - `Out` is the render target type.
824/// - `Uni` is the [`UniformInterface`] type.
825pub struct BuiltProgram<B, Sem, Out, Uni>
826where
827  B: Shader,
828{
829  /// Built program.
830  pub program: Program<B, Sem, Out, Uni>,
831  /// Potential warnings.
832  pub warnings: Vec<ProgramError>,
833}
834
835impl<B, Sem, Out, Uni> BuiltProgram<B, Sem, Out, Uni>
836where
837  B: Shader,
838{
839  /// Get the program and ignore the warnings.
840  pub fn ignore_warnings(self) -> Program<B, Sem, Out, Uni> {
841    self.program
842  }
843}
844
845/// A [`Program`] uniform adaptation that has failed.
846///
847/// # Parametricity
848///
849/// - `B` is the backend type.
850/// - `Sem` is the [`Semantics`] type.
851/// - `Out` is the render target type.
852/// - `Uni` is the [`UniformInterface`] type.
853pub struct AdaptationFailure<B, Sem, Out, Uni>
854where
855  B: Shader,
856{
857  /// Program used before trying to adapt.
858  pub program: Program<B, Sem, Out, Uni>,
859  /// Program error that prevented to adapt.
860  pub error: ProgramError,
861}
862
863impl<B, Sem, Out, Uni> AdaptationFailure<B, Sem, Out, Uni>
864where
865  B: Shader,
866{
867  pub(crate) fn new(program: Program<B, Sem, Out, Uni>, error: ProgramError) -> Self {
868    AdaptationFailure { program, error }
869  }
870
871  /// Get the program and ignore the error.
872  pub fn ignore_error(self) -> Program<B, Sem, Out, Uni> {
873    self.program
874  }
875}
876
877/// Interact with the [`UniformInterface`] carried by a [`Program`] and/or perform dynamic
878/// uniform lookup.
879///
880/// This type allows to set — [`ProgramInterface::set`] – uniforms for a [`Program`].
881///
882/// In the case where you don’t have a uniform interface or need to dynamically lookup uniforms,
883/// you can use the [`ProgramInterface::query`] method.
884///
885/// # Parametricity
886///
887/// `B` is the backend type.
888pub struct ProgramInterface<'a, B>
889where
890  B: Shader,
891{
892  pub(crate) program: &'a mut B::ProgramRepr,
893}
894
895impl<'a, B> ProgramInterface<'a, B>
896where
897  B: Shader,
898{
899  /// Set a value on a [`Uniform`].
900  ///
901  /// The value that is passed depends on the associated [`Uniformable::Target`] type. Most of the time, it will be the
902  /// same as `T`, but it might sometimes be something different if you are using existential types, such as with types
903  /// with lifetimes.
904  pub fn set<'u, T>(&'u mut self, uniform: &'u Uniform<T>, value: B::Target)
905  where
906    B: Uniformable<'u, T>,
907  {
908    unsafe { B::update(self.program, uniform, value) };
909  }
910
911  /// Get back a [`UniformBuilder`] to dynamically access [`Uniform`] objects.
912  pub fn query(&mut self) -> Result<UniformBuilder<'a, B>, ProgramError> {
913    unsafe {
914      B::new_uniform_builder(&mut self.program).map(|repr| UniformBuilder {
915        repr,
916        warnings: Vec::new(),
917        _a: PhantomData,
918      })
919    }
920  }
921}
922
923/// A [`Program`] builder.
924///
925/// This type allows to create shader programs without having to worry too much about the highly
926/// generic API.
927pub struct ProgramBuilder<'a, C, Sem, Out, Uni> {
928  ctx: &'a mut C,
929  _phantom: PhantomData<(Sem, Out, Uni)>,
930}
931
932impl<'a, C, Sem, Out, Uni> ProgramBuilder<'a, C, Sem, Out, Uni>
933where
934  C: GraphicsContext,
935  C::Backend: Shader,
936  Sem: Semantics,
937{
938  /// Create a new [`ProgramBuilder`] from a [`GraphicsContext`].
939  pub fn new(ctx: &'a mut C) -> Self {
940    ProgramBuilder {
941      ctx,
942      _phantom: PhantomData,
943    }
944  }
945
946  /// Create a [`Program`] by linking [`Stage`]s and accessing a mutable environment variable.
947  ///
948  /// # Parametricity
949  ///
950  /// - `T` is an [`Option`] containing a [`TessellationStages`] with [`Stage`] inside.
951  /// - `G` is an [`Option`] containing a [`Stage`] inside (geometry shader).
952  /// - `E` is the mutable environment variable.
953  ///
954  /// # Notes
955  ///
956  /// Feel free to look at the documentation of [`GraphicsContext::new_shader_program`] for
957  /// a simpler interface.
958  pub fn from_stages_env<'b, T, G, E>(
959    &mut self,
960    vertex: &'b Stage<C::Backend>,
961    tess: T,
962    geometry: G,
963    fragment: &'b Stage<C::Backend>,
964    env: &mut E,
965  ) -> Result<BuiltProgram<C::Backend, Sem, Out, Uni>, ProgramError>
966  where
967    Uni: UniformInterface<C::Backend, E>,
968    T: Into<Option<TessellationStages<'b, Stage<C::Backend>>>>,
969    G: Into<Option<&'b Stage<C::Backend>>>,
970  {
971    let tess = tess.into();
972    let geometry = geometry.into();
973
974    unsafe {
975      let mut repr = self.ctx.backend().new_program(
976        &vertex.repr,
977        tess.map(|stages| TessellationStages {
978          control: &stages.control.repr,
979          evaluation: &stages.evaluation.repr,
980        }),
981        geometry.map(|stage| &stage.repr),
982        &fragment.repr,
983      )?;
984
985      let warnings = C::Backend::apply_semantics::<Sem>(&mut repr)?
986        .into_iter()
987        .map(|w| ProgramError::Warning(w.into()))
988        .collect();
989
990      let mut uniform_builder =
991        C::Backend::new_uniform_builder(&mut repr).map(|repr| UniformBuilder {
992          repr,
993          warnings: Vec::new(),
994          _a: PhantomData,
995        })?;
996
997      let uni =
998        Uni::uniform_interface(&mut uniform_builder, env).map_err(ProgramWarning::Uniform)?;
999
1000      let program = Program {
1001        repr,
1002        uni,
1003        _sem: PhantomData,
1004        _out: PhantomData,
1005      };
1006
1007      Ok(BuiltProgram { program, warnings })
1008    }
1009  }
1010
1011  /// Create a [`Program`] by linking [`Stage`]s.
1012  ///
1013  /// # Parametricity
1014  ///
1015  /// - `T` is an [`Option`] containing a [`TessellationStages`] with [`Stage`] inside.
1016  /// - `G` is an [`Option`] containing a [`Stage`] inside (geometry shader).
1017  ///
1018  /// # Notes
1019  ///
1020  /// Feel free to look at the documentation of [`GraphicsContext::new_shader_program`] for
1021  /// a simpler interface.
1022  pub fn from_stages<'b, T, G>(
1023    &mut self,
1024    vertex: &'b Stage<C::Backend>,
1025    tess: T,
1026    geometry: G,
1027    fragment: &'b Stage<C::Backend>,
1028  ) -> Result<BuiltProgram<C::Backend, Sem, Out, Uni>, ProgramError>
1029  where
1030    Uni: UniformInterface<C::Backend>,
1031    T: Into<Option<TessellationStages<'b, Stage<C::Backend>>>>,
1032    G: Into<Option<&'b Stage<C::Backend>>>,
1033  {
1034    Self::from_stages_env(self, vertex, tess, geometry, fragment, &mut ())
1035  }
1036
1037  /// Create a [`Program`] by linking [`&str`]s and accessing a mutable environment variable.
1038  ///
1039  /// # Parametricity
1040  ///
1041  /// - `C` is the graphics context.
1042  /// - `T` is an [`Option`] containing a [`TessellationStages`] with [`&str`] inside.
1043  /// - `G` is an [`Option`] containing a [`Stage`] inside (geometry shader).
1044  /// - `E` is the mutable environment variable.
1045  ///
1046  /// # Notes
1047  ///
1048  /// Feel free to look at the documentation of [`GraphicsContext::new_shader_program`] for
1049  /// a simpler interface.
1050  ///
1051  /// [`&str`]: str
1052  pub fn from_strings_env<'b, T, G, E>(
1053    &mut self,
1054    vertex: &'b str,
1055    tess: T,
1056    geometry: G,
1057    fragment: &'b str,
1058    env: &mut E,
1059  ) -> Result<BuiltProgram<C::Backend, Sem, Out, Uni>, ProgramError>
1060  where
1061    Uni: UniformInterface<C::Backend, E>,
1062    T: Into<Option<TessellationStages<'b, str>>>,
1063    G: Into<Option<&'b str>>,
1064  {
1065    let vs_stage = Stage::new(self.ctx, StageType::VertexShader, vertex)?;
1066
1067    let tess_stages = match tess.into() {
1068      Some(TessellationStages {
1069        control,
1070        evaluation,
1071      }) => {
1072        let control_stage = Stage::new(self.ctx, StageType::TessellationControlShader, control)?;
1073        let evaluation_stage = Stage::new(
1074          self.ctx,
1075          StageType::TessellationEvaluationShader,
1076          evaluation,
1077        )?;
1078        Some((control_stage, evaluation_stage))
1079      }
1080      None => None,
1081    };
1082    let tess_stages =
1083      tess_stages
1084        .as_ref()
1085        .map(|(ref control, ref evaluation)| TessellationStages {
1086          control,
1087          evaluation,
1088        });
1089
1090    let gs_stage = match geometry.into() {
1091      Some(geometry) => Some(Stage::new(self.ctx, StageType::GeometryShader, geometry)?),
1092      None => None,
1093    };
1094
1095    let fs_stage = Stage::new(self.ctx, StageType::FragmentShader, fragment)?;
1096
1097    Self::from_stages_env(
1098      self,
1099      &vs_stage,
1100      tess_stages,
1101      gs_stage.as_ref(),
1102      &fs_stage,
1103      env,
1104    )
1105  }
1106
1107  /// Create a [`Program`] by linking [`&str`]s.
1108  ///
1109  /// # Parametricity
1110  ///
1111  /// - `C` is the graphics context.
1112  /// - `T` is an [`Option`] containing a [`TessellationStages`] with [`&str`] inside.
1113  /// - `G` is an [`Option`] containing a [`Stage`] inside (geometry shader).
1114  ///
1115  /// # Notes
1116  ///
1117  /// Feel free to look at the documentation of [`GraphicsContext::new_shader_program`] for
1118  /// a simpler interface.
1119  ///
1120  /// [`&str`]: str
1121  pub fn from_strings<'b, T, G>(
1122    &mut self,
1123    vertex: &'b str,
1124    tess: T,
1125    geometry: G,
1126    fragment: &'b str,
1127  ) -> Result<BuiltProgram<C::Backend, Sem, Out, Uni>, ProgramError>
1128  where
1129    Uni: UniformInterface<C::Backend>,
1130    T: Into<Option<TessellationStages<'b, str>>>,
1131    G: Into<Option<&'b str>>,
1132  {
1133    Self::from_strings_env(self, vertex, tess, geometry, fragment, &mut ())
1134  }
1135}
1136
1137/// A shader program.
1138///
1139/// Shader programs are GPU binaries that execute when a draw command is issued.
1140///
1141/// # Parametricity
1142///
1143/// - `B` is the backend type.
1144/// - `Sem` is the [`Semantics`] type.
1145/// - `Out` is the render target type.
1146/// - `Uni` is the [`UniformInterface`] type.
1147pub struct Program<B, Sem, Out, Uni>
1148where
1149  B: Shader,
1150{
1151  pub(crate) repr: B::ProgramRepr,
1152  pub(crate) uni: Uni,
1153  _sem: PhantomData<*const Sem>,
1154  _out: PhantomData<*const Out>,
1155}
1156
1157impl<B, Sem, Out, Uni> Program<B, Sem, Out, Uni>
1158where
1159  B: Shader,
1160  Sem: Semantics,
1161{
1162  /// Create a new [`UniformInterface`] but keep the [`Program`] around without rebuilding it.
1163  ///
1164  /// # Parametricity
1165  ///
1166  /// - `Q` is the new [`UniformInterface`].
1167  pub fn adapt<Q>(self) -> Result<BuiltProgram<B, Sem, Out, Q>, AdaptationFailure<B, Sem, Out, Uni>>
1168  where
1169    Q: UniformInterface<B>,
1170  {
1171    self.adapt_env(&mut ())
1172  }
1173
1174  /// Create a new [`UniformInterface`] but keep the [`Program`] around without rebuilding it, by
1175  /// using a mutable environment variable.
1176  ///
1177  /// # Parametricity
1178  ///
1179  /// - `Q` is the new [`UniformInterface`].
1180  /// - `E` is the mutable environment variable.
1181  pub fn adapt_env<Q, E>(
1182    mut self,
1183    env: &mut E,
1184  ) -> Result<BuiltProgram<B, Sem, Out, Q>, AdaptationFailure<B, Sem, Out, Uni>>
1185  where
1186    Q: UniformInterface<B, E>,
1187  {
1188    // first, try to create the new uniform interface
1189    let mut uniform_builder: UniformBuilder<B> =
1190      match unsafe { B::new_uniform_builder(&mut self.repr) } {
1191        Ok(repr) => UniformBuilder {
1192          repr,
1193          warnings: Vec::new(),
1194          _a: PhantomData,
1195        },
1196
1197        Err(e) => return Err(AdaptationFailure::new(self, e)),
1198      };
1199
1200    let uni = match Q::uniform_interface(&mut uniform_builder, env) {
1201      Ok(uni) => uni,
1202      Err(e) => {
1203        return Err(AdaptationFailure::new(
1204          self,
1205          ProgramWarning::Uniform(e).into(),
1206        ))
1207      }
1208    };
1209
1210    let warnings = uniform_builder
1211      .warnings
1212      .into_iter()
1213      .map(|w| ProgramError::Warning(w.into()))
1214      .collect();
1215
1216    let program = Program {
1217      repr: self.repr,
1218      uni,
1219      _sem: PhantomData,
1220      _out: PhantomData,
1221    };
1222
1223    Ok(BuiltProgram { program, warnings })
1224  }
1225
1226  /// Re-create the [`UniformInterface`] but keep the [`Program`] around without rebuilding it.
1227  ///
1228  /// # Parametricity
1229  ///
1230  /// - `E` is the mutable environment variable.
1231  pub fn readapt_env<E>(
1232    self,
1233    env: &mut E,
1234  ) -> Result<BuiltProgram<B, Sem, Out, Uni>, AdaptationFailure<B, Sem, Out, Uni>>
1235  where
1236    Uni: UniformInterface<B, E>,
1237  {
1238    self.adapt_env(env)
1239  }
1240}
1241
1242/// Shader data.
1243///
1244/// # Parametricity
1245///
1246/// - `B` is the backend type.
1247/// - `T` is the type of the carried items.
1248pub struct ShaderData<B, T>
1249where
1250  B: ?Sized + ShaderDataBackend<T>,
1251{
1252  pub(crate) repr: B::ShaderDataRepr,
1253}
1254
1255impl<B, T> ShaderData<B, T>
1256where
1257  B: ?Sized + ShaderDataBackend<T>,
1258{
1259  /// Create a [`ShaderData`] via an iterator of values.
1260  pub fn new(
1261    ctx: &mut impl GraphicsContext<Backend = B>,
1262    values: impl IntoIterator<Item = T>,
1263  ) -> Result<Self, ShaderDataError> {
1264    let repr = unsafe { ctx.backend().new_shader_data(values.into_iter())? };
1265    Ok(Self { repr })
1266  }
1267
1268  /// Get the value at index `i`.
1269  pub fn at(&self, i: usize) -> Result<T, ShaderDataError> {
1270    unsafe { B::get_shader_data_at(&self.repr, i) }
1271  }
1272
1273  /// Set the item at index `i` with the value `x`.
1274  ///
1275  /// Return the previous value.
1276  pub fn set(&mut self, i: usize, x: T) -> Result<T, ShaderDataError> {
1277    unsafe { B::set_shader_data_at(&mut self.repr, i, x) }
1278  }
1279
1280  /// Replace all the values with the one provided by the iterator.
1281  pub fn replace(&mut self, values: impl IntoIterator<Item = T>) -> Result<(), ShaderDataError> {
1282    unsafe { B::set_shader_data_values(&mut self.repr, values.into_iter()) }
1283  }
1284}
1285
1286/// Possible errors that can occur with shader data.
1287#[non_exhaustive]
1288#[derive(Clone, Debug, Eq, PartialEq)]
1289pub enum ShaderDataError {
1290  /// Cannot create the shader data on the backend side.
1291  CannotCreate,
1292
1293  /// Index out of bounds.,
1294  OutOfBounds {
1295    /// Tried (incorrect) index.
1296    index: usize,
1297  },
1298
1299  /// Cannot set data.
1300  CannotSetData {
1301    /// Tried (incorrect) index.
1302    index: usize,
1303  },
1304
1305  /// Cannot replace data.
1306  CannotReplaceData,
1307}
1308
1309impl fmt::Display for ShaderDataError {
1310  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1311    match self {
1312      ShaderDataError::CannotCreate => f.write_str("cannot create shader data"),
1313
1314      ShaderDataError::OutOfBounds { index } => {
1315        write!(f, "cannot get shader data item; out of bounds {}", index)
1316      }
1317
1318      ShaderDataError::CannotSetData { index } => {
1319        write!(f, "cannot get shader data item; out of bounds {}", index)
1320      }
1321
1322      ShaderDataError::CannotReplaceData => f.write_str("cannot replace shader data"),
1323    }
1324  }
1325}
1326
1327impl std::error::Error for ShaderDataError {}