logo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
//! Shader backend interface.
//!
//! This interface defines the low-level API shaders must implement to be usable.
//!
//! Shader support is quite complex and requires several concepts to be implemented by the backend. The first one is the
//! concept of « shader stage ». A shader stage represents a single logic of shader processing. Oftentimes, backends
//! support at least two of them:
//!
//! - Vertex shader, which is run for all vertices.
//! - Fragment shader, which is run for all fragments rasterized by the backend.
//!
//! Other backends support optional backend stages, such as:
//!
//! - Geometry shader, which is run for every primitive (point, lines, triangles, etc.).
//! - Tessellation shaders, run to tessellate the vertex stream.
//! - Compute shaders, special kind of shaders used to compute non-image related data on the backend using the shader
//!   pipeline.
//!
//! Then, the concept of a « shader program », which agregates shader stages into a single entity after a process of «
//! linking » the various shader stages. A shader program is a pipeline resource, so it will be used inside a graphics
//! pipeline to shade a scene. At the higher level, shader programs are typed with different type variables that don’t
//! leak in the backend, but some have meaning, which is a good transition to the next concept: uniforms. In this
//! backend, uniforms are user-defined structures that can only be built by backend-specific ways. This is why another
//! trait must be implement do perform all the lookups and uniforms construction.
//!
//! Finally, some traits exist to provide more features, such as [`ShaderData`] to support shader data operations.

use crate::{
  shader::{
    ProgramError, ShaderDataError, StageError, StageType, TessellationStages, Uniform, UniformType,
    UniformWarning, VertexAttribWarning,
  },
  vertex::Semantics,
};

/// Backend support for uniforms.
///
/// When a backend implements [`Uniformable`], it adds support for the type parameter as being a recognized _uniform
/// type_ and then can be mapped in a uniform interface via [`Uniform`].
///
/// Implementing such a trait is relatively trivial:
///
/// - You must implement [`Uniformable::ty`], which reifies the type of the uniform using [`UniformType`]. If your
///   uniform type is not supported in [`UniformType`], it means the API doesn’t know about it and then that type cannot
///   be supported.
/// - You must implement [`Uniformable::update`], which updates the value of the [`Uniform`] in a given shader program.
///   For indirect values such as bound resources (textures, shader data, etc.), uploading will most of the time be a
///   binding update on the backend side.
/// - You must provide an associated type that will be the actual type users will pass. This is needed because some
///   uniform types cannot be expressed directly, such as existentials (think of types with lifetimes, for instance).
pub unsafe trait Uniformable<'a, T>: Shader {
  type Target: 'a;

  /// Return the size of the uniform.
  ///
  /// For regular uniform variables, this should be `1`. For arrays, it should be the length of the array.
  /// For anything that is not sized, such as texture bindings, shader data, etc., set it to `0`.
  const SIZE: usize;

  /// Reify the type of the uniform as a [`UniformType`].
  unsafe fn ty() -> UniformType;

  /// Update the associated value of the [`Uniform`] in the given shader program.
  unsafe fn update(program: &mut Self::ProgramRepr, uniform: &'a Uniform<T>, value: Self::Target);
}

/// Shader support.
///
/// This trait provides several concepts as once, as they all depend on each other:
///
/// - Shader stages.
/// - Shader programs.
/// - Uniform builders.
///
/// The associated type [`Shader::StageRepr`] is the backend representation of a shader stage. They are created with
/// [`Shader::new_stage`] with a [`StageType`] representing the shader stage type that must be created. Because the
/// backend might not support this type of shader stage, it might fail with a [`StageError`].
pub unsafe trait Shader {
  /// Backend representation of a shader stage.
  type StageRepr;

  /// Backend representation of a shader program.
  type ProgramRepr;

  /// Backend representation of a uniform builder.
  type UniformBuilderRepr;

  /// Create a new shader stage of type [`StageType`].
  unsafe fn new_stage(&mut self, ty: StageType, src: &str) -> Result<Self::StageRepr, StageError>;

  /// Create a new shader program by combining several shader stages.
  ///
  /// The vertex and fragment stages are mandatory. The other ones are optional and then must be inspected to check
  /// whether they were provided by the user.
  unsafe fn new_program(
    &mut self,
    vertex: &Self::StageRepr,
    tess: Option<TessellationStages<Self::StageRepr>>,
    geometry: Option<&Self::StageRepr>,
    fragment: &Self::StageRepr,
  ) -> Result<Self::ProgramRepr, ProgramError>;

  /// Apply semantics.
  ///
  /// This is a very specific operations that happen right after the shader program got successfully created by the
  /// backend. This function is responsible in setting whatever might be needed by the backend to allocate, prepare or
  /// validate the semantics — i.e. `Sem` which implements [`Semantics`].
  unsafe fn apply_semantics<Sem>(
    program: &mut Self::ProgramRepr,
  ) -> Result<Vec<VertexAttribWarning>, ProgramError>
  where
    Sem: Semantics;

  /// Construct a new uniform builder.
  ///
  /// This method must create a uniform builder, which will be used when passed to the user.
  unsafe fn new_uniform_builder(
    program: &mut Self::ProgramRepr,
  ) -> Result<Self::UniformBuilderRepr, ProgramError>;

  /// Lookup a [`Uniform`].
  ///
  /// This method must lookup a [`Uniform`] and map it, or return the appropriate error.
  unsafe fn ask_uniform<T>(
    uniform_builder: &mut Self::UniformBuilderRepr,
    name: &str,
  ) -> Result<Uniform<T>, UniformWarning>
  where
    Self: for<'u> Uniformable<'u, T>;

  /// Backend representation of an _unbound_ [`Uniform`] (i.e. that is inactive in the shader program).
  ///
  /// This is a method taking a uniform builder so that the builder can accumulate a state.
  unsafe fn unbound<T>(uniform_builder: &mut Self::UniformBuilderRepr) -> Uniform<T>
  where
    Self: for<'u> Uniformable<'u, T>;
}

/// Shader data backend.
pub unsafe trait ShaderData<T> {
  /// Representation of the data by the backend.
  type ShaderDataRepr;

  /// Build a new shader data from some values represented via an iterator.
  unsafe fn new_shader_data(
    &mut self,
    values: impl Iterator<Item = T>,
  ) -> Result<Self::ShaderDataRepr, ShaderDataError>;

  /// Access an item at index `i`.
  unsafe fn get_shader_data_at(
    shader_data: &Self::ShaderDataRepr,
    i: usize,
  ) -> Result<T, ShaderDataError>;

  /// Set an item at index `i`.
  ///
  /// Return the previous value.
  unsafe fn set_shader_data_at(
    shader_data: &mut Self::ShaderDataRepr,
    i: usize,
    x: T,
  ) -> Result<T, ShaderDataError>;

  /// Set values by providing an iterator.
  unsafe fn set_shader_data_values(
    shader_data: &mut Self::ShaderDataRepr,
    values: impl Iterator<Item = T>,
  ) -> Result<(), ShaderDataError>;
}