use windows::Win32::Graphics::Direct3D12::*;
use super::allocator::DeviceAllocator;
use crate::components::GlassPanel;
use crate::directx::context::dump_on_err;
use crate::directx::slang_builtins;
use crate::directx::slang_builtins::SlangCompile;
use crate::directx::transparent::{
GlassMeshProducer, RecordUpload, TransparentProducer, TransparentRecord, create_transparent_pso,
};
use crate::geometry::glass_quad::build_glass_quad;
use crate::gfx::mesh_payload::Vertex;
pub(in crate::directx) use concinnity_render::uniforms::GlassParams;
fn glass_params_from(panel: &GlassPanel, planar: f32) -> GlassParams {
let n = panel.normal; GlassParams {
centre: [panel.centre[0], panel.centre[1], panel.centre[2], 0.0],
normal: [n[0], n[1], n[2], 0.0],
tint: [panel.tint[0], panel.tint[1], panel.tint[2], 0.0],
opacity: panel.opacity,
refraction_strength: panel.refraction_strength,
fresnel_power: panel.fresnel_power,
planar,
}
}
pub(in crate::directx) fn compile_glass_shaders(
msaa_samples: u32,
hot_reload: bool,
) -> Result<(Vec<u8>, Vec<u8>), String> {
let frag = if msaa_samples > 1 {
&slang_builtins::GLASS_FRAG_MSAA
} else {
&slang_builtins::GLASS_FRAG
};
let vs = slang_builtins::GLASS_VERT.compile(hot_reload)?;
let ps = frag.compile(hot_reload)?;
Ok((vs, ps))
}
pub(in crate::directx) fn rebuild_glass_pso(
device: &ID3D12Device,
root_sig: &ID3D12RootSignature,
msaa_samples: u32,
hot_reload: bool,
info_queue: Option<&ID3D12InfoQueue>,
) -> Result<ID3D12PipelineState, String> {
let (vs, ps) = compile_glass_shaders(msaa_samples, hot_reload)?;
dump_on_err(
info_queue,
create_transparent_pso(device, root_sig, &vs, &ps),
)
}
struct GlassRtShaders {
vs: Vec<u8>,
flat_ps: Vec<u8>,
textured_ps: Vec<u8>,
}
fn compile_glass_rt_shaders(msaa_samples: u32, hot_reload: bool) -> Result<GlassRtShaders, String> {
let msaa = msaa_samples > 1;
let flat = if msaa {
&slang_builtins::GLASS_RT_FRAG_MSAA
} else {
&slang_builtins::GLASS_RT_FRAG
};
let textured = if msaa {
&slang_builtins::GLASS_RT_FRAG_TEXTURED_MSAA
} else {
&slang_builtins::GLASS_RT_FRAG_TEXTURED
};
Ok(GlassRtShaders {
vs: slang_builtins::GLASS_VERT.compile(hot_reload)?,
flat_ps: flat.compile(hot_reload)?,
textured_ps: textured.compile(hot_reload)?,
})
}
#[derive(Clone, Copy)]
pub(in crate::directx) struct GlassBuild<'a> {
pub alloc: &'a DeviceAllocator,
pub root_sig: &'a ID3D12RootSignature,
pub rt_root_sig: Option<&'a ID3D12RootSignature>,
pub msaa_samples: u32,
pub hot_reload: bool,
pub info_queue: Option<&'a ID3D12InfoQueue>,
}
pub(in crate::directx) fn build_glass_producer(
build: GlassBuild,
panels: &[GlassPanel],
planar_slots: &[Option<usize>],
) -> Result<TransparentProducer, String> {
let GlassBuild {
alloc,
root_sig,
rt_root_sig,
msaa_samples,
hot_reload,
info_queue,
} = build;
let device = alloc.device();
let (vs, ps) = compile_glass_shaders(msaa_samples, hot_reload)?;
let pso = dump_on_err(
info_queue,
create_transparent_pso(device, root_sig, &vs, &ps),
)?;
let (flat_rt_pso, textured_rt_pso) = match rt_root_sig {
Some(sig) => {
match build_glass_rt_pipelines(device, sig, msaa_samples, hot_reload, info_queue) {
Ok(pair) => (Some(pair.0), Some(pair.1)),
Err(e) => {
tracing::warn!(
"glass RT reflection pipeline build failed ({e}); \
using the probe/planar glass path"
);
(None, None)
}
}
}
None => (None, None),
};
let mut records = Vec::with_capacity(panels.len());
for (i, panel) in panels.iter().enumerate() {
let planar_slot = planar_slots.get(i).copied().flatten();
let (verts, idxs) = build_glass_quad(panel.centre, panel.normal, panel.half_size);
let packed: Vec<Vertex> = verts
.into_iter()
.map(|(pos, normal, color, uv)| Vertex {
pos,
normal,
tangent: [1.0, 0.0, 0.0],
color,
uv,
})
.collect();
let params = glass_params_from(panel, if planar_slot.is_some() { 1.0 } else { 0.0 });
records.push(TransparentRecord::upload(
alloc,
RecordUpload {
vertices: &packed,
indices: &idxs,
params: bytemuck::bytes_of(¶ms),
visible: panel.visible,
centre: panel.centre,
planar_slot,
},
)?);
}
Ok(TransparentProducer {
pso,
flat_rt_pso,
textured_rt_pso,
records,
})
}
fn build_glass_rt_pipelines(
device: &ID3D12Device,
rt_root_sig: &ID3D12RootSignature,
msaa_samples: u32,
hot_reload: bool,
info_queue: Option<&ID3D12InfoQueue>,
) -> Result<(ID3D12PipelineState, ID3D12PipelineState), String> {
let shaders = compile_glass_rt_shaders(msaa_samples, hot_reload)?;
let flat = dump_on_err(
info_queue,
create_transparent_pso(device, rt_root_sig, &shaders.vs, &shaders.flat_ps),
)?;
let textured = dump_on_err(
info_queue,
create_transparent_pso(device, rt_root_sig, &shaders.vs, &shaders.textured_ps),
)?;
Ok((flat, textured))
}
#[derive(Clone, Copy)]
pub(in crate::directx) struct GlassMeshBuild<'a> {
pub alloc: &'a DeviceAllocator,
pub rt_root_sig: &'a ID3D12RootSignature,
pub msaa_samples: u32,
pub hot_reload: bool,
pub info_queue: Option<&'a ID3D12InfoQueue>,
}
fn compile_glass_mesh_shaders(
msaa_samples: u32,
hot_reload: bool,
) -> Result<GlassRtShaders, String> {
let msaa = msaa_samples > 1;
let flat = if msaa {
&slang_builtins::GLASS_MESH_RT_FRAG_MSAA
} else {
&slang_builtins::GLASS_MESH_RT_FRAG
};
let textured = if msaa {
&slang_builtins::GLASS_MESH_RT_FRAG_TEXTURED_MSAA
} else {
&slang_builtins::GLASS_MESH_RT_FRAG_TEXTURED
};
Ok(GlassRtShaders {
vs: slang_builtins::GLASS_MESH_VERT.compile(hot_reload)?,
flat_ps: flat.compile(hot_reload)?,
textured_ps: textured.compile(hot_reload)?,
})
}
pub(in crate::directx) fn build_glass_mesh_producer(
build: GlassMeshBuild,
object_indices: &[usize],
) -> Result<GlassMeshProducer, String> {
let GlassMeshBuild {
alloc,
rt_root_sig,
msaa_samples,
hot_reload,
info_queue,
} = build;
let device = alloc.device();
let shaders = compile_glass_mesh_shaders(msaa_samples, hot_reload)?;
let flat_rt_pso = dump_on_err(
info_queue,
create_transparent_pso(device, rt_root_sig, &shaders.vs, &shaders.flat_ps),
)?;
let textured_rt_pso = dump_on_err(
info_queue,
create_transparent_pso(device, rt_root_sig, &shaders.vs, &shaders.textured_ps),
)?;
GlassMeshProducer::new(
alloc,
flat_rt_pso,
Some(textured_rt_pso),
object_indices.to_vec(),
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn glass_shaders_compile() {
if !crate::slangc_gate::slangc_available() {
return;
}
for msaa in [1u32, 4] {
super::compile_glass_shaders(msaa, false)
.unwrap_or_else(|e| panic!("glass shaders (msaa={msaa}) must compile: {e}"));
}
}
#[test]
fn glass_rt_shaders_compile() {
if !crate::slangc_gate::slangc_available() {
return;
}
for msaa in [1u32, 4] {
super::compile_glass_rt_shaders(msaa, false)
.unwrap_or_else(|e| panic!("glass_rt shaders (msaa={msaa}) must compile: {e}"));
}
}
#[test]
fn glass_mesh_shaders_compile() {
if !crate::slangc_gate::slangc_available() {
return;
}
for msaa in [1u32, 4] {
super::compile_glass_mesh_shaders(msaa, false)
.unwrap_or_else(|e| panic!("glass_mesh shaders (msaa={msaa}) must compile: {e}"));
}
}
#[test]
fn glass_params_from_maps_fields() {
let panel = GlassPanel {
centre: [1.0, 2.0, 3.0],
normal: [0.0, 0.0, 1.0],
tint: [0.6, 0.85, 0.9],
opacity: 0.45,
refraction_strength: 0.04,
fresnel_power: 4.0,
..Default::default()
};
let p = glass_params_from(&panel, 1.0);
assert_eq!(p.centre, [1.0, 2.0, 3.0, 0.0]);
assert_eq!(p.normal, [0.0, 0.0, 1.0, 0.0]);
assert_eq!(p.tint, [0.6, 0.85, 0.9, 0.0]);
assert_eq!(p.opacity, 0.45);
assert_eq!(p.refraction_strength, 0.04);
assert_eq!(p.fresnel_power, 4.0);
assert_eq!(p.planar, 1.0);
assert_eq!(glass_params_from(&panel, 0.0).planar, 0.0);
}
}