use std::collections::BTreeMap;
use concinnity_slang as slang;
use crate::shader_layout::reflect::{self, ShaderStruct};
use crate::slang_source;
type Defines = &'static [(&'static str, &'static str)];
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub(super) enum Target {
Metal,
Vulkan,
DirectX,
}
impl Target {
pub(super) const ALL: [Target; 3] = [Target::Metal, Target::Vulkan, Target::DirectX];
pub(super) fn label(self) -> &'static str {
match self {
Target::Metal => "metal",
Target::Vulkan => "vulkan",
Target::DirectX => "directx",
}
}
fn slang_target(self, profile: &'static str) -> slang::SlangTarget {
match self {
Target::Metal => slang::SlangTarget::Metal,
Target::Vulkan => slang::SlangTarget::Spirv,
Target::DirectX => slang::SlangTarget::Hlsl(profile),
}
}
}
pub(super) struct Program {
pub file: &'static str,
pub embedded: &'static str,
pub entry: &'static str,
pub profile: &'static str,
pub common: Defines,
pub metal: Defines,
pub vulkan: Defines,
pub directx: Defines,
}
impl Program {
fn source(&self, target: Target) -> String {
let backend = match target {
Target::Metal => self.metal,
Target::Vulkan => self.vulkan,
Target::DirectX => self.directx,
};
let defines: Vec<(&str, &str)> = self.common.iter().chain(backend).copied().collect();
slang_source::assemble(false, self.file, self.embedded, &defines)
}
}
pub(super) fn layouts(
program: &Program,
target: Target,
) -> Result<BTreeMap<String, ShaderStruct>, String> {
let source = program.source(target);
let job = slang::SlangJob {
source: &source,
file_name: program.file,
entries: &[program.entry],
target: target.slang_target(program.profile),
};
let work_dir = std::env::temp_dir().join("cn_shader_layout");
let json = slang::reflect(&job, &work_dir)
.map_err(|e| format!("{} ({}): {e}", program.entry, target.label()))?;
reflect::structs(&json)
}
const MAIN_BINDLESS: &str = include_str!("../shaders/main_bindless.slang");
const LIGHT_CULL: &str = include_str!("../shaders/light_cull.slang");
pub(super) const RT_SKIN: &str = include_str!("../shaders/rt_skin.slang");
const GBUFFER_PREPASS: &str = include_str!("../shaders/gbuffer_prepass.slang");
const SHADOW: &str = include_str!("../shaders/shadow.slang");
const GLASS: &str = include_str!("../shaders/glass.slang");
const WATER: &str = include_str!("../shaders/water.slang");
const GLASS_MESH: &str = include_str!("../shaders/glass_mesh.slang");
const RT_REFLECTIONS: &str = include_str!("../shaders/rt_reflections.slang");
const DECAL: &str = include_str!("../shaders/decal.slang");
const LINE: &str = include_str!("../shaders/line.slang");
const PARTICLE: &str = include_str!("../shaders/particle.slang");
const TEXT: &str = include_str!("../shaders/text.slang");
const TAA: &str = include_str!("../shaders/taa.slang");
const BLOOM: &str = include_str!("../shaders/bloom.slang");
const COMPOSITE: &str = include_str!("../shaders/composite.slang");
const SSAO: &str = include_str!("../shaders/ssao.slang");
const SSR: &str = include_str!("../shaders/ssr.slang");
const SSGI: &str = include_str!("../shaders/ssgi.slang");
const FOG: &str = include_str!("../shaders/fog.slang");
const AUTO_EXPOSURE: &str = include_str!("../shaders/auto_exposure.slang");
const HIZ_BUILD: &str = include_str!("../shaders/hiz_build.slang");
const PROBES: (&str, &str) = ("MAX_PROBES", "8");
const POOL: (&str, &str) = ("POOL_SIZE", "1024");
pub(super) static MAIN_BINDLESS_VERT: Program = Program {
file: "main_bindless.slang",
embedded: MAIN_BINDLESS,
entry: "vertex_main_bindless",
profile: "vs_6_0",
common: &[PROBES],
metal: &[("METAL_ABI", "1"), POOL],
vulkan: &[POOL],
directx: &[("DXIL_ABI", "1")],
};
pub(super) static LIGHT_CULL_KERNEL: Program = Program {
file: "light_cull.slang",
embedded: LIGHT_CULL,
entry: "light_cull_kernel",
profile: "cs_6_0",
common: &[],
metal: &[],
vulkan: &[],
directx: &[],
};
pub(super) static RT_SKIN_KERNEL: Program = Program {
file: "rt_skin.slang",
embedded: RT_SKIN,
entry: "rt_skin",
profile: "cs_6_5",
common: &[],
metal: &[("METAL_BINDINGS", "1")],
vulkan: &[],
directx: &[],
};
pub(super) static GBUFFER_PREPASS_VERT: Program = Program {
file: "gbuffer_prepass.slang",
embedded: GBUFFER_PREPASS,
entry: "gbuffer_prepass_vertex",
profile: "vs_6_0",
common: &[("GB_STATIC", "1")],
metal: &[("METAL_BINDINGS", "1")],
vulkan: &[],
directx: &[("DXIL_ABI", "1")],
};
pub(super) static GBUFFER_PREPASS_FRAG: Program = Program {
file: "gbuffer_prepass.slang",
embedded: GBUFFER_PREPASS,
entry: "gbuffer_prepass_fragment",
profile: "ps_6_0",
common: &[("GB_FRAGMENT", "1")],
metal: &[("METAL_BINDINGS", "1")],
vulkan: &[],
directx: &[("DXIL_ABI", "1")],
};
pub(super) static SHADOW_VERT: Program = Program {
file: "shadow.slang",
embedded: SHADOW,
entry: "shadow_vertex_main",
profile: "vs_6_0",
common: &[("SHADOW_STATIC", "1")],
metal: &[("METAL_BINDINGS", "1")],
vulkan: &[],
directx: &[("DXIL_ABI", "1")],
};
pub(super) static GLASS_VERT: Program = Program {
file: "glass.slang",
embedded: GLASS,
entry: "glass_vertex",
profile: "vs_6_0",
common: &[PROBES],
metal: &[("METAL_ABI", "1")],
vulkan: &[("USE_MSAA", "1")],
directx: &[("DXIL_ABI", "1")],
};
pub(super) static GLASS_MESH_VERT: Program = Program {
file: "glass_mesh.slang",
embedded: GLASS_MESH,
entry: "glass_mesh_vertex",
profile: "vs_6_0",
common: &[PROBES],
metal: &[("METAL_ABI", "1")],
vulkan: &[("USE_MSAA", "1")],
directx: &[("DXIL_ABI", "1")],
};
pub(super) static WATER_VERT: Program = Program {
file: "water.slang",
embedded: WATER,
entry: "water_vertex",
profile: "vs_6_0",
common: &[PROBES],
metal: &[("METAL_ABI", "1")],
vulkan: &[("USE_MSAA", "1")],
directx: &[("DXIL_ABI", "1")],
};
pub(super) static RT_REFLECTIONS_FRAG: Program = Program {
file: "rt_reflections.slang",
embedded: RT_REFLECTIONS,
entry: "rt_reflections_fragment",
profile: "ps_6_5",
common: &[PROBES],
metal: &[("METAL_ABI", "1")],
vulkan: &[],
directx: &[("DXIL_ABI", "1")],
};
pub(super) static DECAL_VERT: Program = Program {
file: "decal.slang",
embedded: DECAL,
entry: "decal_vertex",
profile: "vs_6_0",
common: &[],
metal: &[],
vulkan: &[],
directx: &[],
};
pub(super) static LINE_VERT: Program = Program {
file: "line.slang",
embedded: LINE,
entry: "line_vertex",
profile: "vs_6_0",
common: &[],
metal: &[],
vulkan: &[],
directx: &[],
};
pub(super) static PARTICLE_VERT: Program = Program {
file: "particle.slang",
embedded: PARTICLE,
entry: "particle_vertex",
profile: "vs_6_0",
common: &[],
metal: &[("METAL_BINDINGS", "1")],
vulkan: &[],
directx: &[("DXIL_ABI", "1")],
};
pub(super) static TEXT_VERT: Program = Program {
file: "text.slang",
embedded: TEXT,
entry: "text_vertex_main",
profile: "vs_6_0",
common: &[],
metal: &[("METAL_BINDINGS", "1")],
vulkan: &[],
directx: &[],
};
pub(super) static TAA_FRAG: Program = Program {
file: "taa.slang",
embedded: TAA,
entry: "taa_fragment_main",
profile: "ps_6_0",
common: &[],
metal: &[],
vulkan: &[],
directx: &[],
};
pub(super) static BLOOM_PREFILTER: Program = Program {
file: "bloom.slang",
embedded: BLOOM,
entry: "bloom_prefilter_fragment",
profile: "ps_6_0",
common: &[("BLOOM_PREFILTER", "1")],
metal: &[],
vulkan: &[],
directx: &[],
};
pub(super) static COMPOSITE_FRAG: Program = Program {
file: "composite.slang",
embedded: COMPOSITE,
entry: "composite_fragment",
profile: "ps_6_0",
common: &[],
metal: &[],
vulkan: &[],
directx: &[],
};
pub(super) static SSAO_KERNEL: Program = Program {
file: "ssao.slang",
embedded: SSAO,
entry: "ssao_kernel_fragment",
profile: "ps_6_0",
common: &[("SSAO_KERNEL", "1")],
metal: &[],
vulkan: &[],
directx: &[],
};
pub(super) static SSR_RESOLVE: Program = Program {
file: "ssr.slang",
embedded: SSR,
entry: "ssr_resolve_fragment",
profile: "ps_6_0",
common: &[PROBES],
metal: &[],
vulkan: &[],
directx: &[("SPLIT_PROBE_SAMPLER", "1")],
};
pub(super) static SSGI_GATHER: Program = Program {
file: "ssgi.slang",
embedded: SSGI,
entry: "ssgi_gather_fragment",
profile: "ps_6_0",
common: &[("SSGI_GATHER", "1")],
metal: &[],
vulkan: &[],
directx: &[],
};
pub(super) static FOG_FROXEL: Program = Program {
file: "fog.slang",
embedded: FOG,
entry: "fog_froxel_kernel",
profile: "cs_6_0",
common: &[("FOG_FROXEL", "1")],
metal: &[],
vulkan: &[],
directx: &[("DXIL_SPLIT", "1")],
};
pub(super) static AUTO_EXPOSURE_BUILD: Program = Program {
file: "auto_exposure.slang",
embedded: AUTO_EXPOSURE,
entry: "histogram_build",
profile: "cs_6_0",
common: &[("AE_BUILD", "1")],
metal: &[("METAL_BINDINGS", "1")],
vulkan: &[],
directx: &[],
};
pub(super) static HIZ_INIT_SINGLE: Program = Program {
file: "hiz_build.slang",
embedded: HIZ_BUILD,
entry: "hiz_init_single",
profile: "cs_6_0",
common: &[("HIZ_INIT_SINGLE", "1")],
metal: &[],
vulkan: &[],
directx: &[],
};