gl_utils/shader/
macro_def.rs

1//
2//  def_programs!
3//
4/// Define a list of shaders with source paths and a list of shader programs
5/// using those shaders.
6///
7/// This will create two enums in local scope named `ShaderId` and `ProgramId`
8/// with variants from the given identifiers.
9#[macro_export]
10macro_rules! def_programs {
11  //
12  //  main rule
13  //
14  (
15    SHADERS [
16      $($shader:ident, $sourcepath:expr),*
17    ]
18    PROGRAMS [
19      $(
20      program $program:ident {
21        vertex_shader:   $vert_shader:ident
22        $(tessellation_control_shader: $tess_ctrl_shader:ident
23        tessellation_evaluation_shader: $tess_eval_shader:ident)*
24        $(geometry_shader: $geom_shader:ident)*
25        fragment_shader: $frag_shader:ident
26      }
27      )*
28    ]
29
30  ) => {
31
32    $crate::def_programs!{
33      @def_shaders
34      $($shader, $sourcepath),*
35    }
36
37    $crate::def_programs!{
38      @def_programs
39      SHADERS [ $($shader),* ]
40      PROGRAMS [
41        $(
42        program $program {
43          vertex_shader:   $vert_shader
44          $(tessellation_control_shader: $tess_ctrl_shader
45          tessellation_evaluation_shader: $tess_eval_shader)*
46          $(geometry_shader: $geom_shader)*
47          fragment_shader: $frag_shader
48        }
49        )*
50      ]
51    }
52  };
53
54  //
55  //  @def_programs
56  //
57  ( @def_programs
58    SHADERS [ $($shader:ident),* ]
59    PROGRAMS [
60      $(
61      program $program:ident {
62        vertex_shader:   $vert_shader:ident
63        $(tessellation_control_shader: $tess_ctrl_shader:ident
64        tessellation_evaluation_shader: $tess_eval_shader:ident)*
65        $(geometry_shader: $geom_shader:ident)*
66        fragment_shader: $frag_shader:ident
67      }
68      )*
69    ]
70  ) => {
71    #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd,
72      $crate::num_derive::FromPrimitive, $crate::num_derive::ToPrimitive,
73      $crate::enum_iterator::Sequence)]
74    pub enum ProgramId {
75      $($program),*
76    }
77
78    impl ProgramId {
79      pub fn index (self) -> usize {
80        self as usize
81      }
82      fn create_program (&self,
83        glium_facade : &$crate::glium::backend::Facade,
84        shaders      : &$crate::vec_map::VecMap <String>
85      ) -> $crate::glium::Program {
86        match *self {
87          $(
88          ProgramId::$program => {
89            let vertex_shader
90              = shaders.get (ShaderId::$vert_shader as usize).unwrap().as_str();
91            let tessellation_control_shader
92              = $crate::def_programs!(
93                @option_shader shaders [$($tess_ctrl_shader)*]);
94            let tessellation_evaluation_shader
95              = $crate::def_programs!(
96                @option_shader shaders [$($tess_eval_shader)*]);
97            let geometry_shader
98              = $crate::def_programs!(
99                @option_shader shaders [$($geom_shader)*]);
100            let fragment_shader
101              = shaders.get (ShaderId::$frag_shader as usize).unwrap().as_str();
102            $crate::glium::Program::new (
103              glium_facade,
104              $crate::glium::program::ProgramCreationInput::SourceCode {
105                vertex_shader,
106                tessellation_control_shader,
107                tessellation_evaluation_shader,
108                geometry_shader,
109                fragment_shader,
110                transform_feedback_varyings: None,
111                outputs_srgb:                true,
112                uses_point_size:             false
113              }
114            ).unwrap()
115          }
116          )*
117        }
118      }
119    }
120
121    pub fn build_programs (glium_facade : &$crate::glium::backend::Facade) ->
122      Result <$crate::vec_map::VecMap <$crate::glium::Program>, std::io::Error>
123    {
124      log::debug!("building shader programs...");
125
126      let shaders = load_shaders()?;
127      let mut programs = $crate::vec_map::VecMap::new();
128      for program_id in ProgramId::iter_variants() {
129        let pid = program_id as usize;
130        assert!(
131          programs.insert (pid, program_id.create_program (glium_facade, &shaders)
132        ).is_none());
133      }
134
135      log::debug!("...building shader programs");
136      Ok (programs)
137    }
138  };
139
140  //
141  //  @def_shaders
142  //
143  (
144    @def_shaders $($shader:ident, $sourcepath:expr),*
145  ) => {
146
147    #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd,
148      $crate::num_derive::FromPrimitive, $crate::num_derive::ToPrimitive,
149      $crate::enum_iterator::Sequence)]
150    pub enum ShaderId {
151      $($shader),*
152    }
153
154    impl ShaderId {
155      pub fn load_source (&self) -> Result <String, std::io::Error> {
156        use std::io::Read;
157        let mut source_file   = std::fs::File::open (self.sourcepath())?;
158        let mut source_string = std::string::String::new();
159        let _ = source_file.read_to_string (&mut source_string).unwrap();
160        Ok (source_string)
161      }
162
163      pub fn sourcepath (&self) -> &'static str {
164        match *self {
165          $(ShaderId::$shader => $sourcepath),*
166        }
167      }
168    }
169
170    pub fn load_shaders()
171      -> Result <$crate::vec_map::VecMap <String>, std::io::Error>
172    {
173      let mut shaders = $crate::vec_map::VecMap::new();
174      for shader_id in ShaderId::iter_variants() {
175        let source_string = shader_id.load_source()?;
176        let sid = shader_id as usize;
177        assert!{ shaders.insert (sid, source_string).is_none() }
178      }
179      Ok (shaders)
180    }
181
182  };
183
184  //
185  //  @option_shader: Some
186  //
187  (@option_shader $shaders:ident [$shader_id:ident]) => {
188    Some ($shaders.get (ShaderId::$shader_id as usize).unwrap().as_str())
189  };
190
191  //
192  //  @option_shader: None
193  //
194  (@option_shader $shaders:ident []) => { None };
195
196} // end def_programs!
197
198//
199//  def_programs_include!
200//
201/// Like `def_programs!` except that the shader source will be compiled-in with
202/// `include_str!`. The original source path is still available through the
203/// `source_path` method on shader IDs, but it is not required to be present
204/// when loading shaders.
205///
206/// Note that unlike `def_programs!` where paths are relative to the
207/// *executable* invocation directory, here paths are relative to the *source
208/// file* which is invoking the `def_programs_include!` macro.
209#[macro_export]
210macro_rules! def_programs_include {
211  //
212  //  main rule
213  //
214  ( SHADERS [
215      $($shader:ident, $sourcepath:expr),*
216    ]
217    PROGRAMS [
218      $(
219      program $program:ident {
220        vertex_shader:   $vert_shader:ident
221        $(tessellation_control_shader: $tess_ctrl_shader:ident
222        tessellation_evaluation_shader: $tess_eval_shader:ident)*
223        $(geometry_shader: $geom_shader:ident)*
224        fragment_shader: $frag_shader:ident
225      }
226      )*
227    ]
228  ) => {
229    $crate::def_programs_include!{
230      @def_shaders
231      $($shader, $sourcepath),*
232    }
233    $crate::def_programs_include!{
234      @def_programs
235      SHADERS [ $($shader),* ]
236      PROGRAMS [
237        $(
238        program $program {
239          vertex_shader:   $vert_shader
240          $(tessellation_control_shader: $tess_ctrl_shader
241          tessellation_evaluation_shader: $tess_eval_shader)*
242          $(geometry_shader: $geom_shader)*
243          fragment_shader: $frag_shader
244        }
245        )*
246      ]
247    }
248  };
249
250  //
251  //  @def_programs
252  //
253  ( @def_programs
254    SHADERS [ $($shader:ident),* ]
255    PROGRAMS [
256      $(
257      program $program:ident {
258        vertex_shader:   $vert_shader:ident
259        $(tessellation_control_shader: $tess_ctrl_shader:ident
260        tessellation_evaluation_shader: $tess_eval_shader:ident)*
261        $(geometry_shader: $geom_shader:ident)*
262        fragment_shader: $frag_shader:ident
263      }
264      )*
265    ]
266  ) => {
267    #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd,
268      $crate::strum::EnumCount, $crate::strum::EnumIter,
269      $crate::strum::FromRepr)]
270    #[repr(u16)]
271    pub enum ProgramId {
272      $($program),*
273    }
274
275    impl ProgramId {
276      pub fn index (self) -> usize {
277        self as usize
278      }
279      fn create_program (&self,
280        glium_facade : &dyn $crate::glium::backend::Facade,
281        shaders      : &$crate::vec_map::VecMap <&'static str>
282      ) -> $crate::glium::Program {
283        log::debug!(name:?=self; "create shader program");
284        match *self {
285          $(
286          ProgramId::$program => {
287            let vertex_shader
288              = shaders.get (ShaderId::$vert_shader as usize).unwrap();
289            let tessellation_control_shader    = $crate::def_programs_include!(
290              @option_shader shaders [$($tess_ctrl_shader)*]);
291            let tessellation_evaluation_shader = $crate::def_programs_include!(
292              @option_shader shaders [$($tess_eval_shader)*]);
293            let geometry_shader                = $crate::def_programs_include!(
294              @option_shader shaders [$($geom_shader)*]);
295            let fragment_shader
296              = shaders.get (ShaderId::$frag_shader as usize).unwrap();
297            $crate::glium::Program::new (
298              glium_facade,
299              $crate::glium::program::ProgramCreationInput::SourceCode {
300                vertex_shader,
301                tessellation_control_shader,
302                tessellation_evaluation_shader,
303                geometry_shader,
304                fragment_shader,
305                transform_feedback_varyings: None,
306                outputs_srgb:                true,
307                uses_point_size:             false
308              }
309            ).unwrap()
310          }
311          )*
312        }
313      }
314    }
315
316    pub fn build_programs (
317      glium_facade : &dyn $crate::glium::backend::Facade
318    ) -> Result
319      <$crate::vec_map::VecMap <$crate::glium::Program>, std::io::Error>
320    {
321      use $crate::strum::IntoEnumIterator;
322      log::debug!("building shader programs...");
323      let shaders = load_shaders()?;
324      let mut programs = $crate::vec_map::VecMap::new();
325      for program_id in ProgramId::iter() {
326        let pid = program_id as usize;
327        assert!(
328          programs.insert (pid, program_id.create_program (glium_facade, &shaders)
329        ).is_none());
330      }
331
332      log::debug!("...building shader programs");
333      Ok (programs)
334    }
335  };
336
337  //
338  //  @def_shaders
339  //
340  (
341    @def_shaders $($shader:ident, $sourcepath:expr),*
342  ) => {
343    #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd,
344      $crate::strum::EnumCount, $crate::strum::EnumIter,
345      $crate::strum::FromRepr)]
346    #[repr(u16)]
347    pub enum ShaderId {
348      $($shader),*
349    }
350    impl ShaderId {
351      pub fn source_str (&self) -> &'static str {
352        match *self {
353          $(
354          ShaderId::$shader => include_str!($sourcepath)
355          ),*
356        }
357      }
358      pub fn sourcepath (&self) -> &'static str {
359        match *self {
360          $(ShaderId::$shader => $sourcepath),*
361        }
362      }
363    }
364    pub fn load_shaders()
365      -> Result <$crate::vec_map::VecMap <&'static str>, std::io::Error>
366    {
367      use $crate::strum::IntoEnumIterator;
368      let mut shaders = $crate::vec_map::VecMap::new();
369      for shader_id in ShaderId::iter() {
370        let source_str = shader_id.source_str();
371        let sid = shader_id as usize;
372        assert!(shaders.insert (sid, source_str).is_none());
373      }
374      Ok (shaders)
375    }
376  };
377
378  //
379  //  @option_shader: Some
380  //
381  (@option_shader $shaders:ident [$shader_id:ident]) => {
382    Some (*$shaders.get (ShaderId::$shader_id as usize).unwrap())
383  };
384
385  //
386  //  @option_shader: None
387  //
388  (@option_shader $shaders:ident []) => { None };
389
390} // end def_programs_include!