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 entry: &'static str,
pub profile: &'static str,
pub common: Defines,
pub metal: Defines,
pub vulkan: Defines,
pub directx: Defines,
pub splices: &'static [(&'static str, &'static str)],
}
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, &defines, self.splices)
}
}
pub(super) fn reflection(program: &Program, target: Target) -> Result<String, 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 = crate::compiler_work::dir()?;
slang::reflect(&job, work.path())
.map_err(|e| format!("{} ({}): {e}", program.entry, target.label()))
}
pub(super) fn layouts(
program: &Program,
target: Target,
) -> Result<BTreeMap<String, ShaderStruct>, String> {
reflect::structs(&reflection(program, target)?)
}
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",
entry: "vertex_main_bindless",
profile: "vs_6_0",
common: &[PROBES],
metal: &[("METAL_ABI", "1"), POOL],
vulkan: &[POOL],
directx: &[("DXIL_ABI", "1")],
splices: &[],
};
pub(super) static CULL_KERNEL: Program = Program {
file: "cull.slang",
entry: "cull_kernel",
profile: "cs_6_0",
common: &[],
metal: &[("METAL_BINDINGS", "1")],
vulkan: &[],
directx: &[("DXIL_ABI", "1")],
splices: &[],
};
pub(super) static LIGHT_CULL_KERNEL: Program = Program {
file: "light_cull.slang",
entry: "light_cull_kernel",
profile: "cs_6_0",
common: &[],
metal: &[],
vulkan: &[],
directx: &[],
splices: &[],
};
pub(super) static RT_SKIN_KERNEL: Program = Program {
file: "rt_skin.slang",
entry: "rt_skin",
profile: "cs_6_5",
common: &[],
metal: &[("METAL_BINDINGS", "1")],
vulkan: &[],
directx: &[],
splices: &[],
};
pub(super) static GBUFFER_PREPASS_VERT: Program = Program {
file: "gbuffer_prepass.slang",
entry: "gbuffer_prepass_vertex_bindless",
profile: "vs_6_0",
common: &[("GB_BINDLESS", "1")],
metal: &[("METAL_BINDINGS", "1")],
vulkan: &[],
directx: &[("DXIL_ABI", "1")],
splices: &[],
};
pub(super) static SHADOW_VERT: Program = Program {
file: "shadow.slang",
entry: "shadow_vertex_main",
profile: "vs_6_0",
common: &[("SHADOW_STATIC", "1")],
metal: &[("METAL_BINDINGS", "1")],
vulkan: &[],
directx: &[("DXIL_ABI", "1")],
splices: &[],
};
pub(super) static GLASS_VERT: Program = Program {
file: "glass.slang",
entry: "glass_vertex",
profile: "vs_6_0",
common: &[PROBES],
metal: &[("METAL_ABI", "1")],
vulkan: &[("USE_MSAA", "1")],
directx: &[("DXIL_ABI", "1")],
splices: &[],
};
pub(super) static GLASS_MESH_VERT: Program = Program {
file: "glass_mesh.slang",
entry: "glass_mesh_vertex",
profile: "vs_6_0",
common: &[PROBES],
metal: &[("METAL_ABI", "1")],
vulkan: &[("USE_MSAA", "1")],
directx: &[("DXIL_ABI", "1")],
splices: &[],
};
pub(super) static WATER_VERT: Program = Program {
file: "water.slang",
entry: "water_vertex",
profile: "vs_6_0",
common: &[PROBES],
metal: &[("METAL_ABI", "1")],
vulkan: &[("USE_MSAA", "1")],
directx: &[("DXIL_ABI", "1")],
splices: &[],
};
pub(super) static RT_REFLECTIONS_FRAG: Program = Program {
file: "rt_reflections.slang",
entry: "rt_reflections_fragment",
profile: "ps_6_5",
common: &[PROBES],
metal: &[("METAL_ABI", "1")],
vulkan: &[],
directx: &[("DXIL_ABI", "1")],
splices: &[],
};
pub(super) static DECAL_VERT: Program = Program {
file: "decal.slang",
entry: "decal_vertex",
profile: "vs_6_0",
common: &[],
metal: &[],
vulkan: &[],
directx: &[],
splices: &[],
};
pub(super) static LINE_VERT: Program = Program {
file: "line.slang",
entry: "line_vertex",
profile: "vs_6_0",
common: &[],
metal: &[],
vulkan: &[],
directx: &[],
splices: &[],
};
pub(super) static PARTICLE_VERT: Program = Program {
file: "particle.slang",
entry: "particle_vertex",
profile: "vs_6_0",
common: &[],
metal: &[("METAL_BINDINGS", "1")],
vulkan: &[],
directx: &[("DXIL_ABI", "1")],
splices: &[],
};
pub(super) static TEXT_VERT: Program = Program {
file: "text.slang",
entry: "text_vertex_main",
profile: "vs_6_0",
common: &[],
metal: &[("METAL_BINDINGS", "1")],
vulkan: &[],
directx: &[],
splices: &[],
};
pub(super) static TAA_FRAG: Program = Program {
file: "taa.slang",
entry: "taa_fragment_main",
profile: "ps_6_0",
common: &[],
metal: &[],
vulkan: &[],
directx: &[],
splices: &[],
};
pub(super) static BLOOM_PREFILTER: Program = Program {
file: "bloom.slang",
entry: "bloom_prefilter_fragment",
profile: "ps_6_0",
common: &[("BLOOM_PREFILTER", "1")],
metal: &[],
vulkan: &[],
directx: &[],
splices: &[],
};
pub(super) static COMPOSITE_FRAG: Program = Program {
file: "composite.slang",
entry: "composite_fragment",
profile: "ps_6_0",
common: &[],
metal: &[],
vulkan: &[],
directx: &[],
splices: &[],
};
pub(super) static SSAO_KERNEL: Program = Program {
file: "ssao.slang",
entry: "ssao_kernel_fragment",
profile: "ps_6_0",
common: &[("SSAO_KERNEL", "1")],
metal: &[],
vulkan: &[],
directx: &[],
splices: &[],
};
pub(super) static SSR_RESOLVE: Program = Program {
file: "ssr.slang",
entry: "ssr_resolve_fragment",
profile: "ps_6_0",
common: &[PROBES],
metal: &[],
vulkan: &[],
directx: &[("SPLIT_PROBE_SAMPLER", "1")],
splices: &[],
};
pub(super) static SSGI_GATHER: Program = Program {
file: "ssgi.slang",
entry: "ssgi_gather_fragment",
profile: "ps_6_0",
common: &[("SSGI_GATHER", "1")],
metal: &[],
vulkan: &[],
directx: &[],
splices: &[],
};
pub(super) static FOG_FROXEL: Program = Program {
file: "fog.slang",
entry: "fog_froxel_kernel",
profile: "cs_6_0",
common: &[("FOG_FROXEL", "1")],
metal: &[],
vulkan: &[],
directx: &[("DXIL_SPLIT", "1")],
splices: &[],
};
pub(super) static AUTO_EXPOSURE_BUILD: Program = Program {
file: "auto_exposure.slang",
entry: "histogram_build",
profile: "cs_6_0",
common: &[("AE_BUILD", "1")],
metal: &[("METAL_BINDINGS", "1")],
vulkan: &[],
directx: &[],
splices: &[],
};
pub(super) static HIZ_INIT_SINGLE: Program = Program {
file: "hiz_build.slang",
entry: "hiz_init_single",
profile: "cs_6_0",
common: &[("HIZ_INIT_SINGLE", "1")],
metal: &[],
vulkan: &[],
directx: &[],
splices: &[],
};
const SDF_STANDIN: (&str, &str) = (
"{SDF_BODY}",
"float map(float3 p, SdfParams q, float t) { return sdSphere(p, 0.5); }\n\
SdfSurface shade(float3 p, float3 n, SdfParams q, float t, float2 uv) {\n\
SdfSurface s; s.albedo = float3(1.0, 1.0, 1.0); s.roughness = 0.5;\n\
s.metallic = 0.0; s.emissive = float3(0.0, 0.0, 0.0);\n\
s.transmitted = float3(0.0, 0.0, 0.0); return s; }\n",
);
pub(super) static RAYMARCH_FRAG: Program = Program {
file: "raymarch.slang",
entry: "raymarch_fragment",
profile: "ps_6_0",
common: &[("RAYMARCH_SURFACE", "1")],
metal: &[("RAYMARCH_METAL", "1")],
vulkan: &[],
directx: &[("RAYMARCH_DXIL", "1")],
splices: &[SDF_STANDIN],
};
pub(super) static RAYMARCH_SHADOW_VERT: Program = Program {
file: "raymarch.slang",
entry: "raymarch_shadow_vertex",
profile: "vs_6_0",
common: &[("RAYMARCH_SHADOW", "1")],
metal: &[("RAYMARCH_METAL", "1")],
vulkan: &[],
directx: &[("RAYMARCH_DXIL", "1")],
splices: &[SDF_STANDIN],
};