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 {}