use std::borrow::Cow;
use super::pipeline::shader_source;
const OBJECT_COMMON_HLSL: &str = include_str!("shaders/object_common.hlsl");
pub(crate) struct HlslProgram {
pub file: &'static str,
pub embedded: &'static str,
pub entry: &'static str,
pub target: &'static str,
pub object_data: bool,
}
impl HlslProgram {
fn body(&self, hot_reload: bool) -> Cow<'static, str> {
shader_source(hot_reload, self.file, self.embedded)
}
pub(crate) fn source(&self, hot_reload: bool) -> String {
let mut src = self.body(hot_reload).into_owned();
if self.object_data {
let object_common = shader_source(hot_reload, "object_common.hlsl", OBJECT_COMMON_HLSL);
src = src.replace("{OBJECT_DATA}", &object_common);
}
src
}
pub(crate) fn compile(&self, hot_reload: bool) -> Result<Vec<u8>, String> {
let source = self.source(hot_reload);
super::pipeline::compile_hlsl(&source, self.entry, self.target)
}
}
pub(crate) fn precompile(out_dir: &std::path::Path, report: &mut crate::precompile::Report) {
for program in ALL {
let source = program.source(false);
let key = super::pipeline::fxc_cache_key(&source, program.entry, program.target);
let compile = || super::pipeline::compile_hlsl(&source, program.entry, program.target);
report.record(
&format!("{} {}", program.entry, program.target),
crate::shader_cache::ensure_in(out_dir, &key, compile),
);
}
}
const MAIN_VERT_HLSL: &str = include_str!("shaders/main_vert.hlsl");
const MAIN_FRAG_HLSL: &str = include_str!("shaders/main_frag.hlsl");
const CULL_HLSL: &str = include_str!("shaders/cull.hlsl");
const fn fxc_main(file: &'static str, embedded: &'static str, target: &'static str) -> HlslProgram {
HlslProgram {
file,
embedded,
entry: "main",
target,
object_data: false,
}
}
pub(super) static MAIN_VERT: HlslProgram = HlslProgram {
file: "main_vert.hlsl",
embedded: MAIN_VERT_HLSL,
entry: "vertex_main",
target: "vs_5_1",
object_data: false,
};
pub(super) static MAIN_VERT_INSTANCED: HlslProgram = HlslProgram {
file: "main_vert.hlsl",
embedded: MAIN_VERT_HLSL,
entry: "vertex_main_instanced",
target: "vs_5_1",
object_data: false,
};
pub(super) static MAIN_FRAG: HlslProgram = fxc_main("main_frag.hlsl", MAIN_FRAG_HLSL, "ps_5_1");
pub(super) static SKINNED_VERT: HlslProgram = fxc_main(
"skinned_vert.hlsl",
include_str!("shaders/skinned_vert.hlsl"),
"vs_5_1",
);
const fn fxc_cull(entry: &'static str) -> HlslProgram {
HlslProgram {
entry,
object_data: true,
..fxc_main("cull.hlsl", CULL_HLSL, "cs_5_1")
}
}
pub(super) static CULL: HlslProgram = fxc_cull("main");
pub(super) static CULL_PHASE2: HlslProgram = fxc_cull("main_phase2");
pub(super) static CULL_SHADOW: HlslProgram = fxc_cull("main_shadow");
pub(crate) static ALL: &[&HlslProgram] = &[
&MAIN_VERT,
&MAIN_FRAG,
&MAIN_VERT_INSTANCED,
&SKINNED_VERT,
&CULL,
&CULL_PHASE2,
&CULL_SHADOW,
];
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn table_has_no_duplicate_programs() {
let mut seen = std::collections::HashSet::new();
for p in ALL {
assert!(
seen.insert((p.source(false), p.entry, p.target)),
"duplicate program: {} {}",
p.entry,
p.target
);
}
}
#[test]
fn every_program_source_contains_its_embedded_body() {
for p in ALL {
let src = p.source(false);
for part in p.embedded.split("{OBJECT_DATA}") {
assert!(src.contains(part), "{} {} lost its body", p.entry, p.target);
}
}
}
#[test]
fn object_data_programs_splice_the_shared_record() {
let mut spliced = 0usize;
for p in ALL {
let src = p.source(false);
let declares = src.contains("struct GpuObjectData");
assert_eq!(
declares, p.object_data,
"{} {}: declares GpuObjectData = {declares}, object_data = {}",
p.entry, p.target, p.object_data
);
assert!(
!src.contains("{OBJECT_DATA}"),
"{} {} left {{OBJECT_DATA}}",
p.entry,
p.target
);
spliced += usize::from(declares);
}
assert_eq!(spliced, 3, "object-data program count changed");
}
}