nsys-gl-utils 0.11.9

OpenGL and graphics utilities
Documentation
//
//  def_programs!
//
/// Define a list of shaders with source paths and a list of shader programs
/// using those shaders.
///
/// This will create two enums in local scope named `ShaderId` and `ProgramId`
/// with variants from the given identifiers.
#[macro_export]
macro_rules! def_programs {
  //
  //  main rule
  //
  (
    SHADERS [
      $($shader:ident, $sourcepath:expr),*
    ]
    PROGRAMS [
      $(
      program $program:ident {
        vertex_shader:   $vert_shader:ident
        $(tessellation_control_shader: $tess_ctrl_shader:ident
        tessellation_evaluation_shader: $tess_eval_shader:ident)*
        $(geometry_shader: $geom_shader:ident)*
        fragment_shader: $frag_shader:ident
      }
      )*
    ]

  ) => {

    $crate::def_programs!{
      @def_shaders
      $($shader, $sourcepath),*
    }

    $crate::def_programs!{
      @def_programs
      SHADERS [ $($shader),* ]
      PROGRAMS [
        $(
        program $program {
          vertex_shader:   $vert_shader
          $(tessellation_control_shader: $tess_ctrl_shader
          tessellation_evaluation_shader: $tess_eval_shader)*
          $(geometry_shader: $geom_shader)*
          fragment_shader: $frag_shader
        }
        )*
      ]
    }
  };

  //
  //  @def_programs
  //
  ( @def_programs
    SHADERS [ $($shader:ident),* ]
    PROGRAMS [
      $(
      program $program:ident {
        vertex_shader:   $vert_shader:ident
        $(tessellation_control_shader: $tess_ctrl_shader:ident
        tessellation_evaluation_shader: $tess_eval_shader:ident)*
        $(geometry_shader: $geom_shader:ident)*
        fragment_shader: $frag_shader:ident
      }
      )*
    ]
  ) => {
    #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd,
      $crate::num_derive::FromPrimitive, $crate::num_derive::ToPrimitive,
      $crate::enum_iterator::Sequence)]
    pub enum ProgramId {
      $($program),*
    }

    impl ProgramId {
      pub fn index (self) -> usize {
        self as usize
      }
      fn create_program (&self,
        glium_facade : &$crate::glium::backend::Facade,
        shaders      : &$crate::vec_map::VecMap <String>
      ) -> $crate::glium::Program {
        match *self {
          $(
          ProgramId::$program => {
            let vertex_shader
              = shaders.get (ShaderId::$vert_shader as usize).unwrap().as_str();
            let tessellation_control_shader
              = $crate::def_programs!(
                @option_shader shaders [$($tess_ctrl_shader)*]);
            let tessellation_evaluation_shader
              = $crate::def_programs!(
                @option_shader shaders [$($tess_eval_shader)*]);
            let geometry_shader
              = $crate::def_programs!(
                @option_shader shaders [$($geom_shader)*]);
            let fragment_shader
              = shaders.get (ShaderId::$frag_shader as usize).unwrap().as_str();
            $crate::glium::Program::new (
              glium_facade,
              $crate::glium::program::ProgramCreationInput::SourceCode {
                vertex_shader,
                tessellation_control_shader,
                tessellation_evaluation_shader,
                geometry_shader,
                fragment_shader,
                transform_feedback_varyings: None,
                outputs_srgb:                true,
                uses_point_size:             false
              }
            ).unwrap()
          }
          )*
        }
      }
    }

    pub fn build_programs (glium_facade : &$crate::glium::backend::Facade) ->
      Result <$crate::vec_map::VecMap <$crate::glium::Program>, std::io::Error>
    {
      log::debug!("building shader programs...");

      let shaders = load_shaders()?;
      let mut programs = $crate::vec_map::VecMap::new();
      for program_id in ProgramId::iter_variants() {
        let pid = program_id as usize;
        assert!(
          programs.insert (pid, program_id.create_program (glium_facade, &shaders)
        ).is_none());
      }

      log::debug!("...building shader programs");
      Ok (programs)
    }
  };

  //
  //  @def_shaders
  //
  (
    @def_shaders $($shader:ident, $sourcepath:expr),*
  ) => {

    #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd,
      $crate::num_derive::FromPrimitive, $crate::num_derive::ToPrimitive,
      $crate::enum_iterator::Sequence)]
    pub enum ShaderId {
      $($shader),*
    }

    impl ShaderId {
      pub fn load_source (&self) -> Result <String, std::io::Error> {
        use std::io::Read;
        let mut source_file   = std::fs::File::open (self.sourcepath())?;
        let mut source_string = std::string::String::new();
        let _ = source_file.read_to_string (&mut source_string).unwrap();
        Ok (source_string)
      }

      pub fn sourcepath (&self) -> &'static str {
        match *self {
          $(ShaderId::$shader => $sourcepath),*
        }
      }
    }

    pub fn load_shaders()
      -> Result <$crate::vec_map::VecMap <String>, std::io::Error>
    {
      let mut shaders = $crate::vec_map::VecMap::new();
      for shader_id in ShaderId::iter_variants() {
        let source_string = shader_id.load_source()?;
        let sid = shader_id as usize;
        assert!{ shaders.insert (sid, source_string).is_none() }
      }
      Ok (shaders)
    }

  };

  //
  //  @option_shader: Some
  //
  (@option_shader $shaders:ident [$shader_id:ident]) => {
    Some ($shaders.get (ShaderId::$shader_id as usize).unwrap().as_str())
  };

  //
  //  @option_shader: None
  //
  (@option_shader $shaders:ident []) => { None };

} // end def_programs!

//
//  def_programs_include!
//
/// Like `def_programs!` except that the shader source will be compiled-in with
/// `include_str!`. The original source path is still available through the
/// `source_path` method on shader IDs, but it is not required to be present
/// when loading shaders.
///
/// Note that unlike `def_programs!` where paths are relative to the
/// *executable* invocation directory, here paths are relative to the *source
/// file* which is invoking the `def_programs_include!` macro.
#[macro_export]
macro_rules! def_programs_include {
  //
  //  main rule
  //
  ( SHADERS [
      $($shader:ident, $sourcepath:expr),*
    ]
    PROGRAMS [
      $(
      program $program:ident {
        vertex_shader:   $vert_shader:ident
        $(tessellation_control_shader: $tess_ctrl_shader:ident
        tessellation_evaluation_shader: $tess_eval_shader:ident)*
        $(geometry_shader: $geom_shader:ident)*
        fragment_shader: $frag_shader:ident
      }
      )*
    ]
  ) => {
    $crate::def_programs_include!{
      @def_shaders
      $($shader, $sourcepath),*
    }
    $crate::def_programs_include!{
      @def_programs
      SHADERS [ $($shader),* ]
      PROGRAMS [
        $(
        program $program {
          vertex_shader:   $vert_shader
          $(tessellation_control_shader: $tess_ctrl_shader
          tessellation_evaluation_shader: $tess_eval_shader)*
          $(geometry_shader: $geom_shader)*
          fragment_shader: $frag_shader
        }
        )*
      ]
    }
  };

  //
  //  @def_programs
  //
  ( @def_programs
    SHADERS [ $($shader:ident),* ]
    PROGRAMS [
      $(
      program $program:ident {
        vertex_shader:   $vert_shader:ident
        $(tessellation_control_shader: $tess_ctrl_shader:ident
        tessellation_evaluation_shader: $tess_eval_shader:ident)*
        $(geometry_shader: $geom_shader:ident)*
        fragment_shader: $frag_shader:ident
      }
      )*
    ]
  ) => {
    #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd,
      $crate::strum::EnumCount, $crate::strum::EnumIter,
      $crate::strum::FromRepr)]
    #[repr(u16)]
    pub enum ProgramId {
      $($program),*
    }

    impl ProgramId {
      pub const fn index (self) -> usize {
        self as usize
      }
      fn create_program (&self,
        glium_facade : &dyn $crate::glium::backend::Facade,
        shaders      : &$crate::vec_map::VecMap <&'static str>
      ) -> $crate::glium::Program {
        log::debug!(name:?=self; "create shader program");
        match *self {
          $(
          ProgramId::$program => {
            let vertex_shader
              = shaders.get (ShaderId::$vert_shader as usize).unwrap();
            let tessellation_control_shader    = $crate::def_programs_include!(
              @option_shader shaders [$($tess_ctrl_shader)*]);
            let tessellation_evaluation_shader = $crate::def_programs_include!(
              @option_shader shaders [$($tess_eval_shader)*]);
            let geometry_shader                = $crate::def_programs_include!(
              @option_shader shaders [$($geom_shader)*]);
            let fragment_shader
              = shaders.get (ShaderId::$frag_shader as usize).unwrap();
            $crate::glium::Program::new (
              glium_facade,
              $crate::glium::program::ProgramCreationInput::SourceCode {
                vertex_shader,
                tessellation_control_shader,
                tessellation_evaluation_shader,
                geometry_shader,
                fragment_shader,
                transform_feedback_varyings: None,
                outputs_srgb:                true,
                uses_point_size:             false
              }
            ).unwrap()
          }
          )*
        }
      }
    }

    pub fn build_programs (
      glium_facade : &dyn $crate::glium::backend::Facade
    ) -> Result
      <$crate::vec_map::VecMap <$crate::glium::Program>, std::io::Error>
    {
      use $crate::strum::IntoEnumIterator;
      log::debug!("building shader programs...");
      let shaders = load_shaders()?;
      let mut programs = $crate::vec_map::VecMap::new();
      for program_id in ProgramId::iter() {
        let pid = program_id as usize;
        assert!(
          programs.insert (pid, program_id.create_program (glium_facade, &shaders)
        ).is_none());
      }

      log::debug!("...building shader programs");
      Ok (programs)
    }
  };

  //
  //  @def_shaders
  //
  (
    @def_shaders $($shader:ident, $sourcepath:expr),*
  ) => {
    #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd,
      $crate::strum::EnumCount, $crate::strum::EnumIter,
      $crate::strum::FromRepr)]
    #[repr(u16)]
    pub enum ShaderId {
      $($shader),*
    }
    impl ShaderId {
      pub const fn source_str (&self) -> &'static str {
        match *self {
          $(
          ShaderId::$shader => include_str!($sourcepath)
          ),*
        }
      }
      pub const fn sourcepath (&self) -> &'static str {
        match *self {
          $(ShaderId::$shader => $sourcepath),*
        }
      }
    }
    pub fn load_shaders()
      -> Result <$crate::vec_map::VecMap <&'static str>, std::io::Error>
    {
      use $crate::strum::IntoEnumIterator;
      let mut shaders = $crate::vec_map::VecMap::new();
      for shader_id in ShaderId::iter() {
        let source_str = shader_id.source_str();
        let sid = shader_id as usize;
        assert!(shaders.insert (sid, source_str).is_none());
      }
      Ok (shaders)
    }
  };

  //
  //  @option_shader: Some
  //
  (@option_shader $shaders:ident [$shader_id:ident]) => {
    Some (*$shaders.get (ShaderId::$shader_id as usize).unwrap())
  };

  //
  //  @option_shader: None
  //
  (@option_shader $shaders:ident []) => { None };

} // end def_programs_include!