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
use crate::error::CompileError;
use shaderc::{IncludeType, ResolvedInclude};
use shaderc::{ShaderKind, CompileOptions};
use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};

pub fn compile<T>(path: T, shader_kind: ShaderKind) -> Result<Vec<u32>, CompileError>
where
    T: AsRef<Path>,
{
    let mut options = CompileOptions::new().ok_or(CompileError::CreateCompiler)?;
    compile_with_options(path, shader_kind, options)
}

pub fn compile_with_options<T>(path: T, shader_kind: ShaderKind, mut options: CompileOptions) -> Result<Vec<u32>, CompileError>
    where
        T: AsRef<Path>,
{
    // TODO Probably shouldn't create this every time.
    let mut compiler = shaderc::Compiler::new().ok_or(CompileError::CreateCompiler)?;
    let mut f = File::open(&path).map_err(CompileError::Open)?;
    let mut src = String::new();
    f.read_to_string(&mut src).map_err(CompileError::Open)?;
    options.set_include_callback(|path, include_type, folder_path, depth| {
        get_include(path, include_type, folder_path, depth)
    });
    let result = compiler
        .compile_into_spirv(
            src.as_str(),
            shader_kind,
            path.as_ref().to_str().ok_or(CompileError::InvalidPath)?,
            "main",
            Some(&options),
        )
        .map_err(CompileError::Compile)?;
    let data = result.as_binary();
    Ok(data.to_owned())
}

fn get_include(
    path: &str,
    include_type: IncludeType,
    folder_path: &str,
    _depth: usize,
) -> Result<ResolvedInclude, String> {
    match include_type {
        IncludeType::Relative => {
            let p = Path::new(path);
            let mut folder = PathBuf::from(folder_path);
            folder.pop();
            folder.push(p);
            let p = folder;
            if !p.is_file() {
                return Err("Include doesn't point to file".to_string());
            }

            let resolved_name = p
                .to_str()
                .ok_or("Path has invalid characters".to_string())?
                .to_owned();
            let p = p.canonicalize().map_err(|_|"Failed to parse include path".to_string())?;
            let mut content = String::new();
            File::open(p)
                .map_err(|_|"Couldn't open include directory".to_string())?
                .read_to_string(&mut content)
                .map_err(|_|"Failed to read included shader".to_string())?;
            Ok(ResolvedInclude {
                resolved_name,
                content,
            })
        }
        IncludeType::Standard => Err("Standard includes are unimplemented".to_string()),
    }
}