use super::Shader;
use crate::SpirvShader;
pub use shaderc::{self, ShaderKind, SourceLanguage};
macro_rules! vk_make_version {
($major: expr, $minor: expr, $patch: expr) => {{
let (major, minor, patch): (u32, u32, u32) = ($major, $minor, $patch);
(major << 22) | (minor << 12) | patch
}};
}
#[derive(Debug)]
pub enum ShaderCError {
Init,
NonUtf8Path(std::path::PathBuf),
Io(std::io::Error),
ShaderC(::shaderc::Error),
}
impl std::error::Error for ShaderCError {}
impl std::fmt::Display for ShaderCError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ShaderCError::Init => write!(f, "failed to init Shaderc"),
ShaderCError::NonUtf8Path(path) => {
write!(f, "path {:?} is not valid UTF-8 string", path)
}
ShaderCError::Io(e) => write!(f, "{}", e),
ShaderCError::ShaderC(e) => write!(f, "{}", e),
}
}
}
impl From<std::io::Error> for ShaderCError {
fn from(e: std::io::Error) -> Self {
ShaderCError::Io(e)
}
}
impl From<::shaderc::Error> for ShaderCError {
fn from(e: ::shaderc::Error) -> Self {
ShaderCError::ShaderC(e)
}
}
#[derive(Clone, Copy, Debug)]
pub struct FileShaderInfo<P, E> {
path: P,
kind: ShaderKind,
lang: SourceLanguage,
entry: E,
}
impl<P, E> FileShaderInfo<P, E> {
pub fn new(path: P, kind: ShaderKind, lang: SourceLanguage, entry: E) -> Self {
FileShaderInfo {
path,
kind,
lang,
entry,
}
}
}
impl<P, E> FileShaderInfo<P, E>
where
E: AsRef<str>,
{
pub fn precompile(&self) -> Result<SpirvShader, <Self as Shader>::Error>
where
Self: Shader,
{
Ok(SpirvShader::new(
self.spirv()?.into_owned(),
stage_from_kind(&self.kind),
self.entry.as_ref(),
))
}
}
impl<P, E> Shader for FileShaderInfo<P, E>
where
P: AsRef<std::path::Path> + std::fmt::Debug,
E: AsRef<str>,
{
type Error = ShaderCError;
fn spirv(&self) -> Result<std::borrow::Cow<'static, [u32]>, ShaderCError> {
let code = std::fs::read_to_string(&self.path)?;
let artifact = shaderc::Compiler::new()
.ok_or(ShaderCError::Init)?
.compile_into_spirv(
&code,
self.kind,
self.path
.as_ref()
.to_str()
.ok_or_else(|| ShaderCError::NonUtf8Path(self.path.as_ref().to_owned()))?,
self.entry.as_ref(),
Some({
let mut ops = shaderc::CompileOptions::new().ok_or(ShaderCError::Init)?;
ops.set_target_env(shaderc::TargetEnv::Vulkan, vk_make_version!(1, 0, 0));
ops.set_source_language(self.lang);
ops.set_generate_debug_info();
ops.set_optimization_level(shaderc::OptimizationLevel::Performance);
ops
})
.as_ref(),
)?;
Ok(std::borrow::Cow::Owned(artifact.as_binary().into()))
}
fn entry(&self) -> &str {
self.entry.as_ref()
}
fn stage(&self) -> rendy_core::hal::pso::ShaderStageFlags {
stage_from_kind(&self.kind)
}
}
#[derive(Clone, Copy, Debug)]
pub struct SourceCodeShaderInfo<P, E, S> {
source: S,
path: P,
kind: ShaderKind,
lang: SourceLanguage,
entry: E,
}
impl<P, E, S> SourceCodeShaderInfo<P, E, S> {
pub fn new(source: S, path: P, kind: ShaderKind, lang: SourceLanguage, entry: E) -> Self {
SourceCodeShaderInfo {
source,
path,
kind,
lang,
entry,
}
}
}
impl<P, E, S> SourceCodeShaderInfo<P, E, S>
where
E: AsRef<str>,
{
pub fn precompile(&self) -> Result<SpirvShader, <Self as Shader>::Error>
where
Self: Shader,
{
Ok(SpirvShader::new(
self.spirv()?.into_owned(),
stage_from_kind(&self.kind),
self.entry.as_ref(),
))
}
}
impl<P, E, S> Shader for SourceCodeShaderInfo<P, E, S>
where
P: AsRef<std::path::Path> + std::fmt::Debug,
E: AsRef<str>,
S: AsRef<str> + std::fmt::Debug,
{
type Error = ShaderCError;
fn spirv(&self) -> Result<std::borrow::Cow<'static, [u32]>, ShaderCError> {
let artifact = shaderc::Compiler::new()
.ok_or(ShaderCError::Init)?
.compile_into_spirv(
self.source.as_ref(),
self.kind,
self.path
.as_ref()
.to_str()
.ok_or_else(|| ShaderCError::NonUtf8Path(self.path.as_ref().to_owned()))?,
self.entry.as_ref(),
Some({
let mut ops = shaderc::CompileOptions::new().ok_or(ShaderCError::Init)?;
ops.set_target_env(shaderc::TargetEnv::Vulkan, vk_make_version!(1, 0, 0));
ops.set_source_language(self.lang);
ops.set_generate_debug_info();
ops.set_optimization_level(shaderc::OptimizationLevel::Performance);
ops
})
.as_ref(),
)?;
Ok(std::borrow::Cow::Owned(artifact.as_binary().into()))
}
fn entry(&self) -> &str {
self.entry.as_ref()
}
fn stage(&self) -> rendy_core::hal::pso::ShaderStageFlags {
stage_from_kind(&self.kind)
}
}
pub type SourceShaderInfo = SourceCodeShaderInfo<&'static str, &'static str, &'static str>;
#[deprecated(
since = "0.2.1",
note = "StaticShaderInfo will be removed in favor of PathBufShaderInfo soon. Please move to that implementation."
)]
pub type StaticShaderInfo = FileShaderInfo<&'static str, &'static str>;
pub type PathBufShaderInfo = FileShaderInfo<std::path::PathBuf, &'static str>;
fn stage_from_kind(kind: &ShaderKind) -> rendy_core::hal::pso::ShaderStageFlags {
use rendy_core::hal::pso::ShaderStageFlags;
match kind {
ShaderKind::Vertex => ShaderStageFlags::VERTEX,
ShaderKind::Fragment => ShaderStageFlags::FRAGMENT,
ShaderKind::Geometry => ShaderStageFlags::GEOMETRY,
ShaderKind::TessEvaluation => ShaderStageFlags::HULL,
ShaderKind::TessControl => ShaderStageFlags::DOMAIN,
ShaderKind::Compute => ShaderStageFlags::COMPUTE,
_ => panic!("Invalid shader type specified"),
}
}