use concinnity_core::components::GlassPanel;
use concinnity_core::geometry::glass_quad::build_glass_quad;
use concinnity_core::gfx::mesh_payload::Vertex;
use concinnity_core::render::error::RenderResult;
use concinnity_core::render::uniforms::GlassParams;
use windows::Win32::Graphics::Direct3D12::*;
use super::allocator::DeviceAllocator;
use crate::directx::builtin_shaders;
use crate::directx::builtin_shaders::CompileProgram;
use crate::directx::context::dump_on_err;
use crate::directx::transparent::{
GlassMeshProducer, RecordUpload, TracedGlassPsos, TransparentProducer, TransparentRecord,
create_glass_reflection_pso, create_transparent_pso,
};
pub(in crate::directx) fn compile_glass_shaders(
msaa_samples: u32,
hot_reload: bool,
) -> RenderResult<(Vec<u8>, Vec<u8>)> {
let vs = builtin_shaders::GLASS_VERT.compile(hot_reload)?;
let ps = builtin_shaders::GLASS_FRAG
.at(msaa_samples > 1)
.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>,
) -> RenderResult<ID3D12PipelineState> {
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>,
reflection_flat_ps: Vec<u8>,
reflection_textured_ps: Vec<u8>,
}
fn compile_glass_rt_shaders(msaa_samples: u32, hot_reload: bool) -> RenderResult<GlassRtShaders> {
use builtin_shaders as sb;
let msaa = msaa_samples > 1;
Ok(GlassRtShaders {
vs: sb::GLASS_VERT.compile(hot_reload)?,
flat_ps: sb::GLASS_FRAG_RT.at(msaa).compile(hot_reload)?,
textured_ps: sb::GLASS_FRAG_RT_TEXTURED.at(msaa).compile(hot_reload)?,
reflection_flat_ps: sb::GLASS_REFLECTION_FRAG.at(msaa).compile(hot_reload)?,
reflection_textured_ps: sb::GLASS_REFLECTION_FRAG_TEXTURED
.at(msaa)
.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>],
) -> RenderResult<TransparentProducer> {
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 traced = match rt_root_sig {
Some(sig) => {
match build_glass_rt_pipelines(device, sig, msaa_samples, hot_reload, info_queue) {
Ok(psos) => Some(psos),
Err(e) => {
tracing::warn!(
"glass RT reflection pipeline build failed ({e}); \
using the probe/planar glass path"
);
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.center, 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 = GlassParams::from_panel(panel, planar_slot.is_some());
records.push(TransparentRecord::upload(
alloc,
RecordUpload {
vertices: &packed,
indices: &idxs,
params: bytemuck::bytes_of(¶ms),
visible: panel.visible,
center: panel.center,
planar_slot,
},
)?);
}
let (flat_rt_pso, textured_rt_pso, reflection_flat_pso, reflection_textured_pso) = match traced
{
Some(p) => (
Some(p.shade_flat),
Some(p.shade_textured),
Some(p.reflection_flat),
Some(p.reflection_textured),
),
None => (None, None, None, None),
};
Ok(TransparentProducer {
pso,
flat_rt_pso,
textured_rt_pso,
reflection_flat_pso,
reflection_textured_pso,
records,
})
}
fn build_glass_rt_pipelines(
device: &ID3D12Device,
rt_root_sig: &ID3D12RootSignature,
msaa_samples: u32,
hot_reload: bool,
info_queue: Option<&ID3D12InfoQueue>,
) -> RenderResult<TracedGlassPsos> {
let shaders = compile_glass_rt_shaders(msaa_samples, hot_reload)?;
build_traced_psos(device, rt_root_sig, &shaders, info_queue)
}
fn build_traced_psos(
device: &ID3D12Device,
rt_root_sig: &ID3D12RootSignature,
shaders: &GlassRtShaders,
info_queue: Option<&ID3D12InfoQueue>,
) -> RenderResult<TracedGlassPsos> {
let shade = |ps: &[u8]| {
dump_on_err(
info_queue,
create_transparent_pso(device, rt_root_sig, &shaders.vs, ps),
)
};
let reflection = |ps: &[u8]| {
dump_on_err(
info_queue,
create_glass_reflection_pso(device, rt_root_sig, &shaders.vs, ps),
)
};
Ok(TracedGlassPsos {
shade_flat: shade(&shaders.flat_ps)?,
shade_textured: shade(&shaders.textured_ps)?,
reflection_flat: reflection(&shaders.reflection_flat_ps)?,
reflection_textured: reflection(&shaders.reflection_textured_ps)?,
})
}
#[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) -> RenderResult<GlassRtShaders> {
use builtin_shaders as sb;
let msaa = msaa_samples > 1;
Ok(GlassRtShaders {
vs: sb::GLASS_MESH_VERT.compile(hot_reload)?,
flat_ps: sb::GLASS_MESH_FRAG_RT.at(msaa).compile(hot_reload)?,
textured_ps: sb::GLASS_MESH_FRAG_RT_TEXTURED
.at(msaa)
.compile(hot_reload)?,
reflection_flat_ps: sb::GLASS_MESH_REFLECTION_FRAG
.at(msaa)
.compile(hot_reload)?,
reflection_textured_ps: sb::GLASS_MESH_REFLECTION_FRAG_TEXTURED
.at(msaa)
.compile(hot_reload)?,
})
}
pub(in crate::directx) fn build_glass_mesh_producer(
build: GlassMeshBuild,
object_indices: &[usize],
) -> RenderResult<GlassMeshProducer> {
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 psos = build_traced_psos(device, rt_root_sig, &shaders, info_queue)?;
GlassMeshProducer::new(alloc, psos, object_indices.to_vec())
}
#[cfg(test)]
mod tests {
#[test]
fn glass_shaders_compile() {
concinnity_shader::require_dxc!();
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() {
concinnity_shader::require_dxc!();
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() {
concinnity_shader::require_dxc!();
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}"));
}
}
}