use concinnity_slang as slang;
use concinnity_toolchain::{
Backend, SlangLibSpec, emit_backend_cfg, emit_check_cfgs, hash_sources,
precompile_metal_shaders, setup_graphics_sdks,
};
use std::path::PathBuf;
const SOURCE_ONLY_METAL_SHADERS: &[&str] = &[];
const SLANG_METAL_POOL_SIZE: usize = 1024;
const SLANG_METAL_MAX_PROBES: usize = 8;
const SLANG_MAIN_DEFINES: &[(&str, &str)] = &[
("METAL_ABI", "1"),
("POOL_SIZE", "1024"),
("MAX_PROBES", "8"),
];
const SLANG_DXIL_MAIN_DEFINES: &[(&str, &str)] = &[("DXIL_ABI", "1"), ("MAX_PROBES", "8")];
const SLANG_PROBE_DEFINES: &[(&str, &str)] = &[("METAL_ABI", "1"), ("MAX_PROBES", "8")];
const DXIL_ABI_REGISTERS: &[(&str, &str)] = &[
("objid_cb", "b0"),
("view_cb", "b1"),
("lights_cb", "b2"),
("shadow_cb", "b3"),
("probe_set_cb", "b4"),
("cluster_cb", "b5"),
("shadow_map", "t0"),
("local_lights_sb", "t1"),
("cluster_list_sb", "t2"),
("objects_sb", "t3"),
("ssao_tex", "t4"),
("irradiance_cube", "t5"),
("prefilter_cube", "t6"),
("probe_cubes", "t7"),
("spot_shadows_sb", "t15"),
("spot_shadow_map", "t16"),
("area_lights_sb", "t17"),
("ltc_matrix", "t18"),
("ltc_magnitude", "t19"),
("tex_pool", "t0, space1"),
("shadow_sampler", "s0"),
("linear_sampler", "s1"),
("cube_sampler", "s2"),
];
struct DxilAbi {
file: &'static str,
gates: &'static [&'static str],
entry: &'static str,
profile: &'static str,
registers: &'static [(&'static str, &'static str)],
}
const SLANG_DXIL_ENTRY_ABI: &[DxilAbi] = &[
DxilAbi {
file: "cull.slang",
gates: &[],
entry: "cull_kernel",
profile: "cs_6_0",
registers: &[
("cull", "b0"),
("objects", "t0"),
("draw_args", "t1"),
("hiz_tex", "t2"),
("commands", "u0"),
("cull_status", "u1"),
],
},
DxilAbi {
file: "cull.slang",
gates: &["CULL_PHASE2"],
entry: "cull_kernel",
profile: "cs_6_0",
registers: &[
("cull", "b0"),
("objects", "t0"),
("draw_args", "t1"),
("hiz_tex", "t2"),
("commands", "u0"),
("cull_status", "u1"),
],
},
DxilAbi {
file: "cull.slang",
gates: &["SHADOW_CULL"],
entry: "cull_kernel",
profile: "cs_6_0",
registers: &[
("cull", "b0"),
("objects", "t0"),
("draw_args", "t1"),
("commands", "u0"),
],
},
DxilAbi {
file: "gbuffer_prepass.slang",
gates: &["GB_BINDLESS"],
entry: "gbuffer_prepass_vertex_bindless",
profile: "vs_6_0",
registers: &[
("objid_cb", "b0"),
("gb_view", "b1"),
("objects", "t0"),
("prev_models", "t1"),
],
},
DxilAbi {
file: "gbuffer_prepass.slang",
gates: &["GB_FRAGMENT_BINDLESS"],
entry: "gbuffer_prepass_fragment_bindless",
profile: "ps_6_0",
registers: &[],
},
DxilAbi {
file: "shadow.slang",
gates: &["SHADOW_STATIC"],
entry: "shadow_vertex_main",
profile: "vs_6_0",
registers: &[("push", "b0"), ("shadow_cb", "b1")],
},
DxilAbi {
file: "shadow.slang",
gates: &["SHADOW_SKINNED"],
entry: "shadow_vertex_main_skinned",
profile: "vs_6_0",
registers: &[("push", "b0"), ("shadow_cb", "b1"), ("joints", "t0")],
},
DxilAbi {
file: "shadow.slang",
gates: &["SHADOW_BINDLESS"],
entry: "shadow_vertex_bindless",
profile: "vs_6_0",
registers: &[
("objid_cb", "b0"),
("shadow_cb", "b1"),
("push", "b2"),
("objects", "t0"),
],
},
DxilAbi {
file: "rt_reflections.slang",
gates: &[],
entry: "rt_reflections_fragment",
profile: "ps_6_5",
registers: RT_REFLECTIONS_REGISTERS,
},
DxilAbi {
file: "rt_reflections.slang",
gates: &["RT_TEXTURED"],
entry: "rt_reflections_fragment",
profile: "ps_6_5",
registers: RT_REFLECTIONS_TEXTURED_REGISTERS,
},
DxilAbi {
file: "glass.slang",
gates: &[],
entry: "glass_vertex",
profile: "vs_6_0",
registers: &[("view", "b0")],
},
DxilAbi {
file: "glass.slang",
gates: &[],
entry: "glass_fragment",
profile: "ps_6_0",
registers: GLASS_REGISTERS,
},
DxilAbi {
file: "glass.slang",
gates: &["GLASS_RT"],
entry: "glass_rt_fragment",
profile: "ps_6_5",
registers: GLASS_RT_REGISTERS,
},
DxilAbi {
file: "glass.slang",
gates: &["GLASS_RT", "RT_TEXTURED"],
entry: "glass_rt_fragment",
profile: "ps_6_5",
registers: GLASS_RT_TEXTURED_REGISTERS,
},
DxilAbi {
file: "glass_mesh.slang",
gates: &[],
entry: "glass_mesh_vertex",
profile: "vs_6_0",
registers: &[("view", "b0"), ("params", "b1")],
},
DxilAbi {
file: "glass_mesh.slang",
gates: &[],
entry: "glass_mesh_rt_fragment",
profile: "ps_6_5",
registers: GLASS_MESH_RT_REGISTERS,
},
DxilAbi {
file: "glass_mesh.slang",
gates: &["RT_TEXTURED"],
entry: "glass_mesh_rt_fragment",
profile: "ps_6_5",
registers: GLASS_RT_TEXTURED_REGISTERS,
},
DxilAbi {
file: "water.slang",
gates: &[],
entry: "water_vertex",
profile: "vs_6_0",
registers: &[("view", "b0"), ("params", "b1")],
},
DxilAbi {
file: "water.slang",
gates: &[],
entry: "water_fragment",
profile: "ps_6_0",
registers: GLASS_REGISTERS,
},
DxilAbi {
file: "water.slang",
gates: &["WATER_RT"],
entry: "water_rt_fragment",
profile: "ps_6_5",
registers: WATER_RT_REGISTERS,
},
DxilAbi {
file: "water.slang",
gates: &["WATER_RT", "RT_TEXTURED"],
entry: "water_rt_fragment",
profile: "ps_6_5",
registers: WATER_RT_TEXTURED_REGISTERS,
},
DxilAbi {
file: "rt_skin.slang",
gates: &[],
entry: "rt_skin",
profile: "cs_6_5",
registers: &[
("src", "t0"),
("palette", "t1"),
("dst", "u0"),
("morph_data", "t2"),
("morph_weights", "t3"),
("params", "b0"),
],
},
DxilAbi {
file: "particle.slang",
gates: &[],
entry: "particle_vertex",
profile: "vs_6_0",
registers: &[("view", "b0"), ("params", "b1"), ("pool", "t0")],
},
DxilAbi {
file: "particle.slang",
gates: &[],
entry: "particle_fragment",
profile: "ps_6_0",
registers: &[("albedo_texture", "t1"), ("albedo_sampler", "s0")],
},
DxilAbi {
file: "decal.slang",
gates: &[],
entry: "decal_vertex",
profile: "vs_6_0",
registers: &[("view", "b0"), ("params", "b1")],
},
DxilAbi {
file: "decal.slang",
gates: &[],
entry: "decal_fragment",
profile: "ps_6_0",
registers: &[
("view", "b0"),
("params", "b1"),
("scene_depth", "t0"),
("decal_tex_texture", "t1"),
("decal_tex_sampler", "s0"),
],
},
DxilAbi {
file: "line.slang",
gates: &[],
entry: "line_vertex",
profile: "vs_6_0",
registers: &[("view", "b0")],
},
DxilAbi {
file: "line.slang",
gates: &[],
entry: "line_fragment",
profile: "ps_6_0",
registers: &[("view", "b0"), ("scene_depth", "t0")],
},
DxilAbi {
file: "text.slang",
gates: &[],
entry: "text_vertex_main",
profile: "vs_6_0",
registers: &[("uni", "b0")],
},
DxilAbi {
file: "text.slang",
gates: &[],
entry: "text_fragment_main",
profile: "ps_6_0",
registers: &[("atlas_texture", "t0"), ("atlas_sampler", "s0")],
},
];
const RT_REFLECTIONS_REGISTERS: &[(&str, &str)] = &[
("rt_params", "b0"),
("probe_set", "b4"),
("scene_tlas", "t0"),
("verts", "t1"),
("indices", "t2"),
("geom", "t3"),
("scene_tex", "t4"),
("gbuffer", "t5"),
("rough_tex", "t6"),
("prefilter", "t7"),
("sverts", "t8"),
("sidx", "t9"),
("probe_cubes", "t10"),
("screen_sampler", "s0"),
("cube_sampler", "s1"),
("probe_cube_sampler", "s3"),
];
const RT_REFLECTIONS_TEXTURED_REGISTERS: &[(&str, &str)] =
&[("tex_pool", "t0, space1"), ("pool_sampler", "s2")];
const GLASS_REGISTERS: &[(&str, &str)] = &[
("view", "b0"),
("params", "b1"),
("probe_set", "b4"),
("scene_color", "t0"),
("scene_depth", "t1"),
("prefilter_cube", "t2"),
("planar_reflection", "t3"),
("probe_cubes", "t7"),
("post_samp", "s0"),
("cube_sampler", "s2"),
];
const GLASS_RT_REGISTERS: &[(&str, &str)] = &[
("view", "b0"),
("params", "b1"),
("rt_params", "b5"),
("probe_set", "b4"),
("scene_color", "t0"),
("scene_depth", "t1"),
("prefilter_cube", "t2"),
("scene_tlas", "t4"),
("verts", "t5"),
("indices", "t6"),
("sverts", "t8"),
("sidx", "t9"),
("geom", "t10"),
("probe_cubes", "t20"),
];
const GLASS_RT_TEXTURED_REGISTERS: &[(&str, &str)] =
&[("tex_pool", "t0, space1"), ("pool_sampler", "s1")];
const WATER_RT_REGISTERS: &[(&str, &str)] = &[
("view", "b0"),
("params", "b1"),
("rt_params", "b5"),
("probe_set", "b4"),
("scene_color", "t0"),
("scene_depth", "t1"),
("prefilter_cube", "t2"),
("planar_reflection", "t3"),
("scene_tlas", "t4"),
("verts", "t5"),
("indices", "t6"),
("sverts", "t8"),
("sidx", "t9"),
("geom", "t10"),
("probe_cubes", "t20"),
];
const WATER_RT_TEXTURED_REGISTERS: &[(&str, &str)] = &[
("planar_reflection", "t3"),
("tex_pool", "t0, space1"),
("pool_sampler", "s1"),
];
const GLASS_MESH_RT_REGISTERS: &[(&str, &str)] = &[
("view", "b0"),
("params", "b1"),
("rt_params", "b5"),
("probe_set", "b4"),
("scene_color", "t0"),
("scene_depth", "t1"),
("prefilter_cube", "t2"),
("scene_tlas", "t4"),
("verts", "t5"),
("indices", "t6"),
("sverts", "t8"),
("sidx", "t9"),
("geom", "t10"),
("probe_cubes", "t20"),
("post_samp", "s0"),
("cube_sampler", "s2"),
];
const SLANG_METAL_LIBS: &[SlangLibSpec] = &[
SlangLibSpec {
name: "main_bindless_vert.slang",
file: "main_bindless.slang",
entries: &["vertex_main_bindless"],
defines: SLANG_MAIN_DEFINES,
},
SlangLibSpec {
name: "main_bindless_frag.slang",
file: "main_bindless.slang",
entries: &["fragment_main_bindless"],
defines: SLANG_MAIN_DEFINES,
},
SlangLibSpec {
name: "cull_phase1.slang",
file: "cull.slang",
entries: &["cull_kernel"],
defines: &[("METAL_BINDINGS", "1")],
},
SlangLibSpec {
name: "cull_phase2.slang",
file: "cull.slang",
entries: &["cull_kernel"],
defines: &[("CULL_PHASE2", "1"), ("METAL_BINDINGS", "1")],
},
SlangLibSpec {
name: "cull_shadow.slang",
file: "cull.slang",
entries: &["cull_kernel"],
defines: &[("SHADOW_CULL", "1"), ("METAL_BINDINGS", "1")],
},
SlangLibSpec {
name: "light_cull.slang",
file: "light_cull.slang",
entries: &["light_cull_kernel"],
defines: &[],
},
SlangLibSpec {
name: "hiz_init_msaa.slang",
file: "hiz_build.slang",
entries: &["hiz_init_msaa"],
defines: &[("HIZ_INIT_MSAA", "1")],
},
SlangLibSpec {
name: "hiz_downsample.slang",
file: "hiz_build.slang",
entries: &["hiz_downsample"],
defines: &[("HIZ_DOWNSAMPLE", "1")],
},
SlangLibSpec {
name: "probe_mip0.slang",
file: "probe_prefilter.slang",
entries: &["probe_mip0"],
defines: &[("PROBE_MIP0", "1"), ("METAL_BINDINGS", "1")],
},
SlangLibSpec {
name: "probe_downsample.slang",
file: "probe_prefilter.slang",
entries: &["probe_downsample"],
defines: &[("PROBE_DOWNSAMPLE", "1"), ("METAL_BINDINGS", "1")],
},
SlangLibSpec {
name: "probe_ggx.slang",
file: "probe_prefilter.slang",
entries: &["probe_ggx"],
defines: &[("PROBE_GGX", "1"), ("METAL_BINDINGS", "1")],
},
SlangLibSpec {
name: "gbuffer_prepass_vert_bindless.slang",
file: "gbuffer_prepass.slang",
entries: &["gbuffer_prepass_vertex_bindless"],
defines: &[("GB_BINDLESS", "1"), ("METAL_BINDINGS", "1")],
},
SlangLibSpec {
name: "gbuffer_prepass_frag_bindless.slang",
file: "gbuffer_prepass.slang",
entries: &["gbuffer_prepass_fragment_bindless"],
defines: &[("GB_FRAGMENT_BINDLESS", "1"), ("METAL_BINDINGS", "1")],
},
SlangLibSpec {
name: "shadow_vert.slang",
file: "shadow.slang",
entries: &["shadow_vertex_main"],
defines: &[("SHADOW_STATIC", "1"), ("METAL_BINDINGS", "1")],
},
SlangLibSpec {
name: "shadow_vert_skinned.slang",
file: "shadow.slang",
entries: &["shadow_vertex_main_skinned"],
defines: &[("SHADOW_SKINNED", "1"), ("METAL_BINDINGS", "1")],
},
SlangLibSpec {
name: "shadow_vert_bindless.slang",
file: "shadow.slang",
entries: &["shadow_vertex_bindless"],
defines: &[("SHADOW_BINDLESS", "1"), ("METAL_BINDINGS", "1")],
},
SlangLibSpec {
name: "fullscreen_vert.slang",
file: "fullscreen.slang",
entries: &["fullscreen_vertex"],
defines: &[],
},
SlangLibSpec {
name: "taa_frag.slang",
file: "taa.slang",
entries: &["taa_fragment_main"],
defines: &[],
},
SlangLibSpec {
name: "bloom_prefilter.slang",
file: "bloom.slang",
entries: &["bloom_prefilter_fragment"],
defines: &[("BLOOM_PREFILTER", "1")],
},
SlangLibSpec {
name: "bloom_downsample.slang",
file: "bloom.slang",
entries: &["bloom_downsample_fragment"],
defines: &[("BLOOM_DOWNSAMPLE", "1")],
},
SlangLibSpec {
name: "bloom_upsample.slang",
file: "bloom.slang",
entries: &["bloom_upsample_fragment"],
defines: &[("BLOOM_UPSAMPLE", "1")],
},
SlangLibSpec {
name: "composite_frag.slang",
file: "composite.slang",
entries: &["composite_fragment"],
defines: &[],
},
SlangLibSpec {
name: "ssao_kernel.slang",
file: "ssao.slang",
entries: &["ssao_kernel_fragment"],
defines: &[("SSAO_KERNEL", "1")],
},
SlangLibSpec {
name: "ssao_blur.slang",
file: "ssao.slang",
entries: &["ssao_blur_fragment"],
defines: &[("SSAO_BLUR", "1")],
},
SlangLibSpec {
name: "ssr_resolve.slang",
file: "ssr.slang",
entries: &["ssr_resolve_fragment"],
defines: SLANG_PROBE_DEFINES,
},
SlangLibSpec {
name: "ssgi_gather.slang",
file: "ssgi.slang",
entries: &["ssgi_gather_fragment"],
defines: &[("SSGI_GATHER", "1")],
},
SlangLibSpec {
name: "ssgi_composite.slang",
file: "ssgi.slang",
entries: &["ssgi_composite_fragment"],
defines: &[("SSGI_COMPOSITE", "1")],
},
SlangLibSpec {
name: "reflection_blur.slang",
file: "reflection.slang",
entries: &["reflection_blur_fragment"],
defines: &[("REFLECTION_BLUR", "1")],
},
SlangLibSpec {
name: "reflection_composite.slang",
file: "reflection.slang",
entries: &["reflection_composite_fragment"],
defines: &[("REFLECTION_COMPOSITE", "1")],
},
SlangLibSpec {
name: "fog_froxel.slang",
file: "fog.slang",
entries: &["fog_froxel_kernel"],
defines: &[("FOG_FROXEL", "1")],
},
SlangLibSpec {
name: "fog_frag.slang",
file: "fog.slang",
entries: &["fog_fragment"],
defines: &[("USE_MSAA", "0")],
},
SlangLibSpec {
name: "auto_exposure_build.slang",
file: "auto_exposure.slang",
entries: &["histogram_build"],
defines: &[("AE_BUILD", "1"), ("METAL_BINDINGS", "1")],
},
SlangLibSpec {
name: "auto_exposure_average.slang",
file: "auto_exposure.slang",
entries: &["histogram_average"],
defines: &[("AE_AVERAGE", "1"), ("METAL_BINDINGS", "1")],
},
SlangLibSpec {
name: "rt_skin.slang",
file: "rt_skin.slang",
entries: &["rt_skin"],
defines: &[("METAL_BINDINGS", "1")],
},
SlangLibSpec {
name: "particle_simulate.slang",
file: "particle_simulate.slang",
entries: &["particle_simulate"],
defines: &[("METAL_BINDINGS", "1")],
},
SlangLibSpec {
name: "particle_vert.slang",
file: "particle.slang",
entries: &["particle_vertex"],
defines: &[("METAL_BINDINGS", "1")],
},
SlangLibSpec {
name: "particle_frag.slang",
file: "particle.slang",
entries: &["particle_fragment"],
defines: &[("METAL_BINDINGS", "1")],
},
SlangLibSpec {
name: "decal_vert.slang",
file: "decal.slang",
entries: &["decal_vertex"],
defines: &[],
},
SlangLibSpec {
name: "decal_frag.slang",
file: "decal.slang",
entries: &["decal_fragment"],
defines: &[("USE_MSAA", "0")],
},
SlangLibSpec {
name: "line_vert.slang",
file: "line.slang",
entries: &["line_vertex"],
defines: &[],
},
SlangLibSpec {
name: "line_frag.slang",
file: "line.slang",
entries: &["line_fragment"],
defines: &[("USE_MSAA", "0")],
},
SlangLibSpec {
name: "text_vert.slang",
file: "text.slang",
entries: &["text_vertex_main"],
defines: &[("METAL_BINDINGS", "1")],
},
SlangLibSpec {
name: "text_frag.slang",
file: "text.slang",
entries: &["text_fragment_main"],
defines: &[("METAL_BINDINGS", "1")],
},
SlangLibSpec {
name: "rt_reflections_frag.slang",
file: "rt_reflections.slang",
entries: &["rt_reflections_fragment"],
defines: SLANG_RT_DEFINES,
},
SlangLibSpec {
name: "rt_reflections_frag_textured.slang",
file: "rt_reflections.slang",
entries: &["rt_reflections_fragment"],
defines: SLANG_RT_TEXTURED_DEFINES,
},
SlangLibSpec {
name: "glass_vert.slang",
file: "glass.slang",
entries: &["glass_vertex"],
defines: SLANG_GLASS_DEFINES,
},
SlangLibSpec {
name: "glass_frag.slang",
file: "glass.slang",
entries: &["glass_fragment"],
defines: SLANG_GLASS_DEFINES,
},
SlangLibSpec {
name: "glass_frag_rt.slang",
file: "glass.slang",
entries: &["glass_rt_fragment"],
defines: SLANG_GLASS_RT_DEFINES,
},
SlangLibSpec {
name: "glass_frag_rt_textured.slang",
file: "glass.slang",
entries: &["glass_rt_fragment"],
defines: SLANG_GLASS_RT_TEXTURED_DEFINES,
},
SlangLibSpec {
name: "glass_mesh_vert.slang",
file: "glass_mesh.slang",
entries: &["glass_mesh_vertex"],
defines: SLANG_GLASS_MESH_DEFINES,
},
SlangLibSpec {
name: "glass_mesh_frag_rt.slang",
file: "glass_mesh.slang",
entries: &["glass_mesh_rt_fragment"],
defines: SLANG_GLASS_MESH_DEFINES,
},
SlangLibSpec {
name: "glass_mesh_frag_rt_textured.slang",
file: "glass_mesh.slang",
entries: &["glass_mesh_rt_fragment"],
defines: SLANG_GLASS_MESH_TEXTURED_DEFINES,
},
SlangLibSpec {
name: "water_vert.slang",
file: "water.slang",
entries: &["water_vertex"],
defines: SLANG_WATER_DEFINES,
},
SlangLibSpec {
name: "water_frag.slang",
file: "water.slang",
entries: &["water_fragment"],
defines: SLANG_WATER_DEFINES,
},
SlangLibSpec {
name: "water_frag_rt.slang",
file: "water.slang",
entries: &["water_rt_fragment"],
defines: SLANG_WATER_RT_DEFINES,
},
SlangLibSpec {
name: "water_frag_rt_textured.slang",
file: "water.slang",
entries: &["water_rt_fragment"],
defines: SLANG_WATER_RT_TEXTURED_DEFINES,
},
];
const SLANG_RT_DEFINES: &[(&str, &str)] = &[("METAL_ABI", "1"), ("MAX_PROBES", "8")];
const SLANG_RT_TEXTURED_DEFINES: &[(&str, &str)] = &[
("METAL_ABI", "1"),
("RT_TEXTURED", "1"),
("POOL_SIZE", "1024"),
("MAX_PROBES", "8"),
];
const SLANG_GLASS_DEFINES: &[(&str, &str)] = &[("METAL_ABI", "1"), ("MAX_PROBES", "8")];
const SLANG_GLASS_RT_DEFINES: &[(&str, &str)] =
&[("METAL_ABI", "1"), ("GLASS_RT", "1"), ("MAX_PROBES", "8")];
const SLANG_GLASS_RT_TEXTURED_DEFINES: &[(&str, &str)] = &[
("METAL_ABI", "1"),
("GLASS_RT", "1"),
("RT_TEXTURED", "1"),
("POOL_SIZE", "1024"),
("MAX_PROBES", "8"),
];
const SLANG_GLASS_MESH_DEFINES: &[(&str, &str)] = &[("METAL_ABI", "1"), ("MAX_PROBES", "8")];
const SLANG_GLASS_MESH_TEXTURED_DEFINES: &[(&str, &str)] = &[
("METAL_ABI", "1"),
("RT_TEXTURED", "1"),
("POOL_SIZE", "1024"),
("MAX_PROBES", "8"),
];
const SLANG_WATER_DEFINES: &[(&str, &str)] = &[("METAL_ABI", "1"), ("MAX_PROBES", "8")];
const SLANG_WATER_RT_DEFINES: &[(&str, &str)] =
&[("METAL_ABI", "1"), ("WATER_RT", "1"), ("MAX_PROBES", "8")];
const SLANG_WATER_RT_TEXTURED_DEFINES: &[(&str, &str)] = &[
("METAL_ABI", "1"),
("WATER_RT", "1"),
("RT_TEXTURED", "1"),
("POOL_SIZE", "1024"),
("MAX_PROBES", "8"),
];
const SHADER_COMPILE_SOURCES: &[&str] = &[
"src/shader_cache.rs",
"src/runtime_cache.rs",
"src/slang_source.rs",
"src/directx/pipeline.rs",
"src/directx/slang_builtins.rs",
"src/metal/msl_cache.rs",
"src/metal/slang_shaders.rs",
"src/vulkan/pipeline.rs",
"src/vulkan/slang_builtins.rs",
];
fn precompile_spirv() {
use concinnity_core::render::slang_programs::vk::{self, Sizes};
use concinnity_core::render::uniforms::{BINDLESS_POOL_SIZE, MAX_PROBES};
let pool = BINDLESS_POOL_SIZE.to_string();
let probes = MAX_PROBES.to_string();
let mut artifacts = Vec::new();
for program in vk::ALL {
for msaa in msaa_variants(program.msaa) {
let mut defines: Vec<(&str, &str)> = program.gates.iter().map(|g| (*g, "1")).collect();
if program.msaa {
defines.push(("USE_MSAA", if *msaa { "1" } else { "0" }));
}
if program.sizes == Sizes::PoolAndProbes {
defines.push(("POOL_SIZE", pool.as_str()));
}
if program.sizes != Sizes::None {
defines.push(("MAX_PROBES", probes.as_str()));
}
artifacts.push(concinnity_toolchain::SlangArtifact {
name: spirv_artifact_name(program.label, *msaa),
source: concinnity_core::render::slang_source::assemble(program.file, &defines),
entry: program.entry,
file_name: program.file,
target: slang::SlangTarget::Spirv,
});
}
}
concinnity_toolchain::precompile_slang_artifacts(
&artifacts,
"engine_spirv.rs",
"embedded_spirv",
);
}
fn msaa_variants(takes_msaa: bool) -> &'static [bool] {
if takes_msaa { &[false, true] } else { &[false] }
}
fn spirv_artifact_name(label: &str, msaa: bool) -> String {
if msaa {
format!("{label}.msaa")
} else {
label.to_string()
}
}
fn precompile_dxil() {
use concinnity_core::render::slang_programs::dx;
if !cfg!(windows) {
concinnity_toolchain::precompile_slang_artifacts(&[], "engine_dxil.rs", "embedded_dxil");
return;
}
let artifacts: Vec<_> = dx::ALL
.iter()
.map(|p| concinnity_toolchain::SlangArtifact {
name: p.label.to_string(),
source: concinnity_core::render::slang_source::assemble(p.file, p.defines),
entry: p.entry,
file_name: p.file,
target: slang::SlangTarget::Dxil(p.profile),
})
.collect();
concinnity_toolchain::precompile_slang_artifacts(&artifacts, "engine_dxil.rs", "embedded_dxil");
}
fn main() {
emit_check_cfgs();
let backend = emit_backend_cfg();
setup_graphics_sdks(backend);
if backend == Some(Backend::Metal) {
let manifest = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
let shaders_dir = manifest.join("src/metal/shaders");
precompile_metal_shaders(&shaders_dir, SOURCE_ONLY_METAL_SHADERS, SLANG_METAL_LIBS);
assert_slang_metal_abi();
emit_slang_metal_defines();
}
if backend == Some(Backend::Dx) {
assert_slang_dxil_abi();
precompile_dxil();
}
if backend == Some(Backend::Vk) {
precompile_spirv();
}
emit_shader_compile_source_hash();
}
fn assert_slang_metal_abi() {
if slang::slangc_path().is_none() {
return;
}
let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap());
for abi in SLANG_METAL_ENTRY_ABI {
let source = concinnity_core::render::slang_source::assemble(abi.file, abi.defines);
let job = slang::SlangJob {
source: &source,
file_name: "metal_abi_check.slang",
entries: &[abi.entry],
target: slang::SlangTarget::Metal,
};
let msl = slang::compile(&job, &out_dir).expect("slang ABI check compile");
let msl = String::from_utf8_lossy(&msl);
assert_every_param_is_attributed(abi, &msl_entry_params(&msl, abi.entry));
for (param, attribute) in abi.slots {
assert!(
msl_binds(&msl, param, attribute),
"{}: Metal ABI drifted at `{}`: expected `{param}` on [[{attribute}]] in the \
emitted MSL. \
The Metal binding layout is what the encoder writes and what world shaders \
compile from; fix the .slang declarations or the slot assignment \
before shipping.",
abi.file,
abi.entry
);
}
}
}
fn assert_every_param_is_attributed(abi: &MetalAbi, params: &[String]) {
let unattributed: Vec<&str> = params
.iter()
.filter(|p| !p.contains("[["))
.map(|p| msl_param_name(p))
.collect();
assert!(
unattributed.is_empty(),
"{} ({}): the emitted MSL has entry parameters with no binding attribute: \
{unattributed:?}. slangc gives a global-scope resource array no [[texture(n)]] / \
[[sampler(n)]], so the Metal compiler places it at the next unused slot and no host \
binding can be checked against it. Declare it inside a ParameterBlock instead (see \
main_bindless.slang's METAL_ABI block, or `ProbeCubes` in this file's five \
probe-sampling shaders).",
abi.file,
abi.entry,
);
}
fn msl_binds(msl: &str, param: &str, attribute: &str) -> bool {
let marker = format!(" [[{attribute}]]");
msl.match_indices(&marker).any(|(at, _)| {
msl[..at]
.rsplit(|c: char| c.is_whitespace())
.next()
.and_then(|name| name.rsplit_once('_'))
.is_some_and(|(head, tail)| head == param && tail.chars().all(|c| c.is_ascii_digit()))
})
}
fn msl_entry_params(msl: &str, entry: &str) -> Vec<String> {
let stage = ["[[fragment]]", "[[vertex]]", "[[kernel]]"]
.iter()
.find_map(|tag| msl.find(tag))
.unwrap_or_else(|| panic!("emitted MSL declares no entry point for `{entry}`"));
let open = stage
+ msl[stage..]
.find('(')
.unwrap_or_else(|| panic!("emitted MSL entry point `{entry}` has no parameter list"));
assert!(
msl[stage..open].trim_end().ends_with(entry),
"emitted MSL entry point is not `{entry}`: {}",
&msl[stage..open]
);
let mut params = Vec::new();
let mut depth = 0i32;
let mut start = open + 1;
for (at, ch) in msl[open..].char_indices().map(|(i, c)| (open + i, c)) {
match ch {
'(' | '<' | '[' => depth += 1,
')' | '>' | ']' => {
depth -= 1;
if depth == 0 {
params.push(msl[start..at].trim().to_string());
break;
}
}
',' if depth == 1 => {
params.push(msl[start..at].trim().to_string());
start = at + 1;
}
_ => {}
}
}
params
}
fn msl_param_name(param: &str) -> &str {
let name = param
.rsplit(|c: char| c.is_whitespace())
.next()
.unwrap_or(param);
match name.rsplit_once('_') {
Some((head, tail)) if !tail.is_empty() && tail.chars().all(|c| c.is_ascii_digit()) => head,
_ => name,
}
}
struct MetalAbi {
file: &'static str,
entry: &'static str,
defines: &'static [(&'static str, &'static str)],
slots: &'static [(&'static str, &'static str)],
}
const SLANG_METAL_ENTRY_ABI: &[MetalAbi] = &[
MetalAbi {
file: "cull.slang",
entry: "cull_kernel",
defines: &[("METAL_BINDINGS", "1")],
slots: &[
("objects", "buffer(0)"),
("draw_args", "buffer(1)"),
("cull", "buffer(2)"),
("cull_status", "buffer(5)"),
("hiz_tex", "texture(0)"),
],
},
MetalAbi {
file: "cull.slang",
entry: "cull_kernel",
defines: &[("CULL_PHASE2", "1"), ("METAL_BINDINGS", "1")],
slots: &[
("objects", "buffer(0)"),
("cull", "buffer(2)"),
("cull_status", "buffer(5)"),
("hiz_tex", "texture(0)"),
],
},
MetalAbi {
file: "cull.slang",
entry: "cull_kernel",
defines: &[("SHADOW_CULL", "1"), ("METAL_BINDINGS", "1")],
slots: &[
("objects", "buffer(0)"),
("draw_args", "buffer(1)"),
("cull", "buffer(2)"),
("cull_status", "buffer(5)"),
],
},
MetalAbi {
file: "main_bindless.slang",
entry: "fragment_main_bindless",
defines: SLANG_MAIN_DEFINES,
slots: &[
("view_cb", "buffer(0)"),
("lights_cb", "buffer(4)"),
("shadow_cb", "buffer(5)"),
("probe_set_cb", "buffer(6)"),
("tex", "buffer(7)"),
("local_lights_sb", "buffer(8)"),
("objects_sb", "buffer(9)"),
("samps", "buffer(10)"),
("cluster_cb", "buffer(11)"),
("cluster_list_sb", "buffer(12)"),
("spot_shadows_sb", "buffer(13)"),
("area_lights_sb", "buffer(14)"),
],
},
MetalAbi {
file: "ssr.slang",
entry: "ssr_resolve_fragment",
defines: SLANG_PROBE_DEFINES,
slots: &[
("params", "buffer(0)"),
("probe_set", "buffer(1)"),
("scene_texture", "texture(0)"),
("scene_sampler", "sampler(0)"),
("gbuffer_texture", "texture(1)"),
("gbuffer_sampler", "sampler(1)"),
("rough_tex_texture", "texture(2)"),
("rough_tex_sampler", "sampler(2)"),
("prefilter_texture", "texture(3)"),
("prefilter_sampler", "sampler(3)"),
("probe_cube_set", "buffer(11)"),
("probe_cube_sampler", "sampler(4)"),
],
},
MetalAbi {
file: "rt_reflections.slang",
entry: "rt_reflections_fragment",
defines: SLANG_RT_DEFINES,
slots: &[
("rt_params", "buffer(0)"),
("verts", "buffer(1)"),
("indices", "buffer(2)"),
("geom", "buffer(3)"),
("scene_tlas", "buffer(4)"),
("sverts", "buffer(5)"),
("sidx", "buffer(6)"),
("probe_set", "buffer(8)"),
("scene_tex_texture", "texture(0)"),
("scene_tex_sampler", "sampler(0)"),
("gbuffer_texture", "texture(1)"),
("gbuffer_sampler", "sampler(1)"),
("rough_tex_texture", "texture(2)"),
("rough_tex_sampler", "sampler(2)"),
("prefilter_texture", "texture(3)"),
("prefilter_sampler", "sampler(3)"),
("probe_cube_set", "buffer(11)"),
("probe_cube_sampler", "sampler(4)"),
],
},
MetalAbi {
file: "rt_reflections.slang",
entry: "rt_reflections_fragment",
defines: SLANG_RT_TEXTURED_DEFINES,
slots: &[
("rt_params", "buffer(0)"),
("verts", "buffer(1)"),
("indices", "buffer(2)"),
("geom", "buffer(3)"),
("scene_tlas", "buffer(4)"),
("sverts", "buffer(5)"),
("sidx", "buffer(6)"),
("pool", "buffer(7)"),
("pool_sampler", "sampler(5)"),
("probe_set", "buffer(8)"),
("scene_tex_texture", "texture(0)"),
("scene_tex_sampler", "sampler(0)"),
("gbuffer_texture", "texture(1)"),
("gbuffer_sampler", "sampler(1)"),
("rough_tex_texture", "texture(2)"),
("rough_tex_sampler", "sampler(2)"),
("prefilter_texture", "texture(3)"),
("prefilter_sampler", "sampler(3)"),
("probe_cube_set", "buffer(11)"),
("probe_cube_sampler", "sampler(4)"),
],
},
MetalAbi {
file: "glass.slang",
entry: "glass_fragment",
defines: SLANG_GLASS_DEFINES,
slots: &[
("view", "buffer(5)"),
("params", "buffer(6)"),
("probe_set", "buffer(7)"),
("scene_color_texture", "texture(0)"),
("scene_color_sampler", "sampler(0)"),
("scene_depth", "texture(1)"),
("prefilter_cube_texture", "texture(2)"),
("prefilter_cube_sampler", "sampler(1)"),
("probe_cube_set", "buffer(11)"),
("probe_cube_sampler", "sampler(2)"),
("planar_reflection_texture", "texture(3)"),
("planar_reflection_sampler", "sampler(3)"),
],
},
MetalAbi {
file: "glass.slang",
entry: "glass_rt_fragment",
defines: SLANG_GLASS_RT_DEFINES,
slots: &[
("view", "buffer(5)"),
("params", "buffer(6)"),
("probe_set", "buffer(7)"),
("scene_color_texture", "texture(0)"),
("scene_color_sampler", "sampler(0)"),
("scene_depth", "texture(1)"),
("prefilter_cube_texture", "texture(2)"),
("prefilter_cube_sampler", "sampler(1)"),
("probe_cube_set", "buffer(11)"),
("probe_cube_sampler", "sampler(2)"),
("rt_params", "buffer(0)"),
("verts", "buffer(1)"),
("indices", "buffer(2)"),
("geom", "buffer(3)"),
("scene_tlas", "buffer(4)"),
("sverts", "buffer(8)"),
("sidx", "buffer(9)"),
],
},
MetalAbi {
file: "glass.slang",
entry: "glass_rt_fragment",
defines: SLANG_GLASS_RT_TEXTURED_DEFINES,
slots: &[
("view", "buffer(5)"),
("params", "buffer(6)"),
("probe_set", "buffer(7)"),
("scene_color_texture", "texture(0)"),
("scene_color_sampler", "sampler(0)"),
("scene_depth", "texture(1)"),
("prefilter_cube_texture", "texture(2)"),
("prefilter_cube_sampler", "sampler(1)"),
("probe_cube_set", "buffer(11)"),
("probe_cube_sampler", "sampler(2)"),
("rt_params", "buffer(0)"),
("verts", "buffer(1)"),
("indices", "buffer(2)"),
("geom", "buffer(3)"),
("scene_tlas", "buffer(4)"),
("sverts", "buffer(8)"),
("sidx", "buffer(9)"),
("pool", "buffer(10)"),
("pool_sampler", "sampler(4)"),
],
},
MetalAbi {
file: "glass_mesh.slang",
entry: "glass_mesh_rt_fragment",
defines: SLANG_GLASS_MESH_DEFINES,
slots: &[
("view", "buffer(5)"),
("params", "buffer(6)"),
("probe_set", "buffer(7)"),
("scene_color_texture", "texture(0)"),
("scene_color_sampler", "sampler(0)"),
("scene_depth", "texture(1)"),
("prefilter_cube_texture", "texture(2)"),
("prefilter_cube_sampler", "sampler(1)"),
("probe_cube_set", "buffer(11)"),
("probe_cube_sampler", "sampler(2)"),
("rt_params", "buffer(0)"),
("verts", "buffer(1)"),
("indices", "buffer(2)"),
("geom", "buffer(3)"),
("scene_tlas", "buffer(4)"),
("sverts", "buffer(8)"),
("sidx", "buffer(9)"),
],
},
MetalAbi {
file: "glass_mesh.slang",
entry: "glass_mesh_rt_fragment",
defines: SLANG_GLASS_MESH_TEXTURED_DEFINES,
slots: &[
("view", "buffer(5)"),
("params", "buffer(6)"),
("probe_set", "buffer(7)"),
("scene_color_texture", "texture(0)"),
("scene_color_sampler", "sampler(0)"),
("scene_depth", "texture(1)"),
("prefilter_cube_texture", "texture(2)"),
("prefilter_cube_sampler", "sampler(1)"),
("probe_cube_set", "buffer(11)"),
("probe_cube_sampler", "sampler(2)"),
("rt_params", "buffer(0)"),
("verts", "buffer(1)"),
("indices", "buffer(2)"),
("geom", "buffer(3)"),
("scene_tlas", "buffer(4)"),
("sverts", "buffer(8)"),
("sidx", "buffer(9)"),
("pool", "buffer(10)"),
("pool_sampler", "sampler(4)"),
],
},
MetalAbi {
file: "water.slang",
entry: "water_fragment",
defines: SLANG_WATER_DEFINES,
slots: &[
("view", "buffer(5)"),
("params", "buffer(6)"),
("probe_set", "buffer(7)"),
("scene_color_texture", "texture(0)"),
("scene_color_sampler", "sampler(0)"),
("scene_depth", "texture(1)"),
("prefilter_cube_texture", "texture(2)"),
("prefilter_cube_sampler", "sampler(1)"),
("probe_cube_set", "buffer(11)"),
("probe_cube_sampler", "sampler(2)"),
("planar_reflection_texture", "texture(3)"),
("planar_reflection_sampler", "sampler(3)"),
],
},
MetalAbi {
file: "water.slang",
entry: "water_rt_fragment",
defines: SLANG_WATER_RT_DEFINES,
slots: &[
("view", "buffer(5)"),
("params", "buffer(6)"),
("probe_set", "buffer(7)"),
("scene_color_texture", "texture(0)"),
("scene_color_sampler", "sampler(0)"),
("scene_depth", "texture(1)"),
("prefilter_cube_texture", "texture(2)"),
("prefilter_cube_sampler", "sampler(1)"),
("probe_cube_set", "buffer(11)"),
("probe_cube_sampler", "sampler(2)"),
("planar_reflection_texture", "texture(3)"),
("planar_reflection_sampler", "sampler(3)"),
("rt_params", "buffer(0)"),
("verts", "buffer(1)"),
("indices", "buffer(2)"),
("geom", "buffer(3)"),
("scene_tlas", "buffer(4)"),
("sverts", "buffer(8)"),
("sidx", "buffer(9)"),
],
},
MetalAbi {
file: "water.slang",
entry: "water_rt_fragment",
defines: SLANG_WATER_RT_TEXTURED_DEFINES,
slots: &[
("view", "buffer(5)"),
("params", "buffer(6)"),
("probe_set", "buffer(7)"),
("scene_color_texture", "texture(0)"),
("scene_color_sampler", "sampler(0)"),
("scene_depth", "texture(1)"),
("prefilter_cube_texture", "texture(2)"),
("prefilter_cube_sampler", "sampler(1)"),
("probe_cube_set", "buffer(11)"),
("probe_cube_sampler", "sampler(2)"),
("planar_reflection_texture", "texture(3)"),
("planar_reflection_sampler", "sampler(3)"),
("rt_params", "buffer(0)"),
("verts", "buffer(1)"),
("indices", "buffer(2)"),
("geom", "buffer(3)"),
("scene_tlas", "buffer(4)"),
("sverts", "buffer(8)"),
("sidx", "buffer(9)"),
("pool", "buffer(10)"),
("pool_sampler", "sampler(4)"),
],
},
];
fn assert_slang_dxil_abi() {
if slang::slangc_path().is_none() {
return;
}
let source = concinnity_core::render::slang_source::assemble(
"main_bindless.slang",
SLANG_DXIL_MAIN_DEFINES,
);
let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap());
let mut emitted = String::new();
for (entry, profile) in [
("vertex_main_bindless", "vs_6_0"),
("fragment_main_bindless", "ps_6_0"),
] {
emitted.push_str(&emit_dxil_hlsl(
&out_dir,
"main_bindless_abi_check.slang",
&source,
entry,
profile,
));
}
for (param, register) in DXIL_ABI_REGISTERS {
assert_dxil_register(
&emitted,
param,
register,
"The DirectX bindless binding layout is frozen (world Shader assets build PSOs \
against the same root signature); fix the .slang declarations or the root \
signature in src/directx/init/pipelines.rs before shipping.",
);
}
assert_slang_dxil_entry_abi(&out_dir);
}
fn assert_slang_dxil_entry_abi(out_dir: &std::path::Path) {
for abi in SLANG_DXIL_ENTRY_ABI {
let mut defines: Vec<(&str, &str)> = vec![("DXIL_ABI", "1")];
defines.extend(abi.gates.iter().map(|gate| (*gate, "1")));
let source = concinnity_core::render::slang_source::assemble(abi.file, &defines);
let emitted = emit_dxil_hlsl(out_dir, abi.file, &source, abi.entry, abi.profile);
for (param, register) in abi.registers {
assert_dxil_register(
&emitted,
param,
register,
"Fix the .slang declaration or the matching root signature under \
src/directx before shipping.",
);
}
}
}
fn emit_dxil_hlsl(
out_dir: &std::path::Path,
file_name: &str,
source: &str,
entry: &str,
profile: &'static str,
) -> String {
let job = slang::SlangJob {
source,
file_name,
entries: &[entry],
target: slang::SlangTarget::Hlsl(profile),
};
let hlsl = slang::compile(&job, out_dir)
.unwrap_or_else(|e| panic!("slang DXIL ABI check ({entry}): {e}"));
String::from_utf8_lossy(&hlsl).into_owned()
}
fn assert_dxil_register(emitted: &str, param: &str, register: &str, remedy: &str) {
let expected = format!("{param}_0 : register({register})");
let expected_array = format!("{param}_0[");
assert!(
emitted.contains(&expected)
|| emitted.lines().any(
|l| l.contains(&expected_array) && l.contains(&format!("register({register})"))
),
"slang DXIL ABI drifted: expected `{param}` at register({register}). {remedy}"
);
}
fn emit_slang_metal_defines() {
let value = |key: &str| {
SLANG_MAIN_DEFINES
.iter()
.find(|(k, _)| *k == key)
.map(|(_, v)| *v)
.unwrap_or_default()
};
assert_eq!(value("POOL_SIZE"), SLANG_METAL_POOL_SIZE.to_string());
assert_eq!(value("MAX_PROBES"), SLANG_METAL_MAX_PROBES.to_string());
let out = PathBuf::from(std::env::var("OUT_DIR").unwrap()).join("slang_metal_defines.rs");
std::fs::write(
&out,
format!(
"pub(crate) const SLANG_METAL_POOL_SIZE: usize = {SLANG_METAL_POOL_SIZE};\n\
pub(crate) const SLANG_METAL_MAX_PROBES: usize = {SLANG_METAL_MAX_PROBES};\n"
),
)
.expect("write slang_metal_defines.rs");
}
fn emit_shader_compile_source_hash() {
let manifest = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap());
let roots: Vec<PathBuf> = SHADER_COMPILE_SOURCES
.iter()
.map(|p| manifest.join(p))
.collect();
let hash = hash_sources(&roots);
let out =
PathBuf::from(std::env::var("OUT_DIR").unwrap()).join("shader_compile_source_hash.rs");
std::fs::write(
&out,
format!("const SHADER_COMPILE_SOURCE_HASH: u32 = {hash:#010x};\n"),
)
.expect("write shader_compile_source_hash.rs");
}