use super::ShaderLayout; use bevy_asset::Handle; use glsl_to_spirv::compile; use std::{io::Read, marker::Copy}; /// The stage of a shader #[derive(Hash, Eq, PartialEq, Copy, Clone, Debug)] pub enum ShaderStage { Vertex, Fragment, Compute, } impl Into for ShaderStage { fn into(self) -> glsl_to_spirv::ShaderType { match self { ShaderStage::Vertex => glsl_to_spirv::ShaderType::Vertex, ShaderStage::Fragment => glsl_to_spirv::ShaderType::Fragment, ShaderStage::Compute => glsl_to_spirv::ShaderType::Compute, } } } fn glsl_to_spirv( glsl_source: &str, stage: ShaderStage, shader_defs: Option<&[String]>, ) -> Vec { let mut output = compile(glsl_source, stage.into(), shader_defs).unwrap(); let mut spv_bytes = Vec::new(); output.read_to_end(&mut spv_bytes).unwrap(); bytes_to_words(&spv_bytes) } fn bytes_to_words(bytes: &[u8]) -> Vec { let mut words = Vec::new(); for bytes4 in bytes.chunks(4) { words.push(u32::from_le_bytes([ bytes4[0], bytes4[1], bytes4[2], bytes4[3], ])); } words } /// The full "source" of a shader #[derive(Clone, Debug, Hash, Eq, PartialEq)] pub enum ShaderSource { Spirv(Vec), Glsl(String), } impl ShaderSource { pub fn spirv_from_bytes(bytes: &[u8]) -> ShaderSource { ShaderSource::Spirv(bytes_to_words(bytes)) } } /// A shader, as defined by its [ShaderSource] and [ShaderStage] #[derive(Clone, Debug)] pub struct Shader { pub source: ShaderSource, pub stage: ShaderStage, } impl Shader { pub fn new(stage: ShaderStage, source: ShaderSource) -> Shader { Shader { stage, source } } pub fn from_glsl(stage: ShaderStage, glsl: &str) -> Shader { Shader { source: ShaderSource::Glsl(glsl.to_string()), stage, } } pub fn get_spirv(&self, macros: Option<&[String]>) -> Vec { match self.source { ShaderSource::Spirv(ref bytes) => bytes.clone(), ShaderSource::Glsl(ref source) => glsl_to_spirv(&source, self.stage, macros), } } pub fn get_spirv_shader(&self, macros: Option<&[String]>) -> Shader { Shader { source: ShaderSource::Spirv(self.get_spirv(macros)), stage: self.stage, } } pub fn reflect_layout(&self, enforce_bevy_conventions: bool) -> Option { if let ShaderSource::Spirv(ref spirv) = self.source { Some(ShaderLayout::from_spirv( spirv.as_slice(), enforce_bevy_conventions, )) } else { panic!("Cannot reflect layout of non-SpirV shader. Try compiling this shader to SpirV first using self.get_spirv_shader()"); } } } /// All stages in a shader program #[derive(Clone, Debug)] pub struct ShaderStages { pub vertex: Handle, pub fragment: Option>, } impl ShaderStages { pub fn new(vertex_shader: Handle) -> Self { ShaderStages { vertex: vertex_shader, fragment: None, } } }