use windows::Win32::Foundation::RECT;
use windows::Win32::Graphics::Direct3D12::*;
use crate::gfx::render_types::NUM_SHADOW_CASCADES;
use crate::directx::com;
use crate::directx::context::DxContext;
#[derive(Copy, Clone)]
#[repr(C)]
struct ShadowPush {
model: [[f32; 4]; 4],
cascade_idx: u32,
_pad: [u32; 3],
}
#[derive(Clone, Copy)]
struct ShadowPipeline<'a> {
pso: &'a ID3D12PipelineState,
root_sig: &'a ID3D12RootSignature,
}
#[derive(Clone, Copy)]
pub(in crate::directx) struct ShadowPassBinding<'a> {
pub pso: &'a ID3D12PipelineState,
pub root_sig: &'a ID3D12RootSignature,
pub ubo_gva: u64,
pub slice_idx: u32,
}
impl DxContext {
pub(in crate::directx) fn encode_shadow_pass(
&self,
cmd: &ID3D12GraphicsCommandList,
frame_idx: usize,
shadow_ubo_gva: u64,
cam_pos: [f32; 3],
raymarch_view: Option<&crate::directx::raymarch::RaymarchView>,
) {
let (Some(shadow_pso), Some(shadow_root_sig)) =
(self.shadow_pso.as_ref(), self.shadow_root_sig.as_ref())
else {
return;
};
if self.shadow.dsvs.is_empty() {
return;
}
let sm = self.shadow.map_size;
let all_cascades = (1u32 << NUM_SHADOW_CASCADES) - 1;
let render_mask = if self.shadow.render_mask == 0 {
all_cascades
} else {
self.shadow.render_mask
};
unsafe {
let vp = D3D12_VIEWPORT {
TopLeftX: 0.0,
TopLeftY: 0.0,
Width: sm as f32,
Height: sm as f32,
MinDepth: 0.0,
MaxDepth: 1.0,
};
cmd.RSSetViewports(&[vp]);
let scissor = RECT {
left: 0,
top: 0,
right: sm as i32,
bottom: sm as i32,
};
cmd.RSSetScissorRects(&[scissor]);
cmd.IASetPrimitiveTopology(
windows::Win32::Graphics::Direct3D::D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST,
);
}
if self.cull.shadow_bindless_pso.is_some() && self.cull_count() > 0 {
self.encode_shadow_pass_gpu_driven(
cmd,
frame_idx,
shadow_ubo_gva,
cam_pos,
render_mask,
ShadowPipeline {
pso: shadow_pso,
root_sig: shadow_root_sig,
},
);
} else {
self.encode_shadow_pass_legacy(
cmd,
frame_idx,
shadow_ubo_gva,
cam_pos,
render_mask,
ShadowPipeline {
pso: shadow_pso,
root_sig: shadow_root_sig,
},
);
}
if let Some(view) = raymarch_view
&& let Err(e) = self.encode_sdf_shadow_casters(cmd, frame_idx, shadow_ubo_gva, view)
{
tracing::error!("encode_sdf_shadow_casters: {}", e);
}
}
fn encode_shadow_pass_gpu_driven(
&self,
cmd: &ID3D12GraphicsCommandList,
frame_idx: usize,
shadow_ubo_gva: u64,
cam_pos: [f32; 3],
render_mask: u32,
legacy_pipeline: ShadowPipeline<'_>,
) {
let ShadowPipeline {
pso: legacy_shadow_pso,
root_sig: legacy_shadow_root_sig,
} = legacy_pipeline;
let (Some(sb_pso), Some(sb_root), Some(sb_sig), Some(indirect)) = (
self.cull.shadow_bindless_pso.as_ref(),
self.cull.shadow_bindless_root_sig.as_ref(),
self.cull.shadow_bindless_cmd_sig.as_ref(),
self.cull.shadow_indirect_buffers.get(frame_idx),
) else {
return;
};
let n_cull = self.cull_count();
let prefix = self.skinned_record_base();
let stride = crate::directx::cull::INDIRECT_COMMAND_STRIDE as usize;
let object_gva = com::gpu_va(&self.cull.object_buffer_resources[frame_idx]);
self.encode_shadow_culls(cmd, frame_idx, render_mask, cam_pos);
unsafe {
cmd.SetPipelineState(sb_pso);
cmd.SetGraphicsRootSignature(sb_root);
cmd.IASetVertexBuffers(0, Some(&[self.geometry.vertex_buffer_view]));
cmd.IASetIndexBuffer(Some(&self.geometry.index_buffer_view));
cmd.SetGraphicsRootConstantBufferView(1, shadow_ubo_gva);
cmd.SetGraphicsRootShaderResourceView(3, object_gva);
}
for cascade_idx in 0..NUM_SHADOW_CASCADES {
if render_mask & (1u32 << cascade_idx) == 0 {
continue;
}
let dsv = self.shadow.dsvs[cascade_idx];
let c = cascade_idx as u32;
unsafe {
cmd.OMSetRenderTargets(0, None, false, Some(&dsv));
cmd.ClearDepthStencilView(dsv, D3D12_CLEAR_FLAG_DEPTH, 1.0, 0, None);
cmd.SetGraphicsRoot32BitConstants(
2,
1,
&c as *const u32 as *const std::ffi::c_void,
0,
);
let byte_off = ((cascade_idx * n_cull) * stride) as u64;
cmd.ExecuteIndirect(
sb_sig,
prefix as u32,
indirect,
byte_off,
None::<&ID3D12Resource>,
0,
);
}
self.inc_draw_calls(1);
}
if self.draw.n_skinned > 0
&& let Some(deformed_vbv) = self.skinned.deformed_vbvs.get(frame_idx)
{
unsafe {
cmd.IASetVertexBuffers(0, Some(&[*deformed_vbv]));
cmd.IASetIndexBuffer(Some(&self.skinned.index_buffer_view));
}
for cascade_idx in 0..NUM_SHADOW_CASCADES {
if render_mask & (1u32 << cascade_idx) == 0 {
continue;
}
let dsv = self.shadow.dsvs[cascade_idx];
let c = cascade_idx as u32;
unsafe {
cmd.OMSetRenderTargets(0, None, false, Some(&dsv));
cmd.SetGraphicsRoot32BitConstants(
2,
1,
&c as *const u32 as *const std::ffi::c_void,
0,
);
let byte_off = ((cascade_idx * n_cull + prefix) * stride) as u64;
cmd.ExecuteIndirect(
sb_sig,
self.draw.n_skinned as u32,
indirect,
byte_off,
None::<&ID3D12Resource>,
0,
);
}
self.inc_draw_calls(1);
}
}
self.encode_shadow_legacy_extra(
cmd,
render_mask,
cam_pos,
legacy_shadow_pso,
legacy_shadow_root_sig,
shadow_ubo_gva,
);
}
fn encode_shadow_legacy_extra(
&self,
cmd: &ID3D12GraphicsCommandList,
render_mask: u32,
cam_pos: [f32; 3],
shadow_pso: &ID3D12PipelineState,
shadow_root_sig: &ID3D12RootSignature,
shadow_ubo_gva: u64,
) {
if self.clone.slot_by_draw_idx.is_empty() {
return;
}
unsafe {
cmd.SetPipelineState(shadow_pso);
cmd.SetGraphicsRootSignature(shadow_root_sig);
cmd.IASetVertexBuffers(0, Some(&[self.geometry.vertex_buffer_view]));
cmd.IASetIndexBuffer(Some(&self.geometry.index_buffer_view));
cmd.SetGraphicsRootConstantBufferView(1, shadow_ubo_gva);
}
for cascade_idx in 0..NUM_SHADOW_CASCADES {
if render_mask & (1u32 << cascade_idx) == 0 {
continue;
}
let dsv = self.shadow.dsvs[cascade_idx];
unsafe {
cmd.OMSetRenderTargets(0, None, false, Some(&dsv));
}
let skip_seethrough = self.mesh_glass_active();
for (i, obj) in self.draw.objects.iter().enumerate() {
if i < self.draw.n_objects || !obj.visible || !obj.resident {
continue;
}
if !self.clone.slot_by_draw_idx.contains_key(&i) {
continue; }
if skip_seethrough && obj.material.see_through != 0 {
continue; }
let push = ShadowPush {
model: obj.model,
cascade_idx: cascade_idx as u32,
_pad: [0; 3],
};
let d = crate::gfx::lod::camera_distance(obj, cam_pos);
let (index_offset, index_count) = obj.active_lod(d);
unsafe {
cmd.SetGraphicsRoot32BitConstants(
0,
20,
&push as *const ShadowPush as *const std::ffi::c_void,
0,
);
cmd.DrawIndexedInstanced(
index_count as u32,
1,
index_offset as u32,
obj.base_vertex,
0,
);
}
self.inc_draw_calls(1);
}
}
}
pub(in crate::directx) fn encode_shadow_casters_into(
&self,
cmd: &ID3D12GraphicsCommandList,
bind: ShadowPassBinding<'_>,
cam_pos: [f32; 3],
) {
unsafe {
cmd.SetPipelineState(bind.pso);
cmd.SetGraphicsRootSignature(bind.root_sig);
cmd.IASetVertexBuffers(0, Some(&[self.geometry.vertex_buffer_view]));
cmd.IASetIndexBuffer(Some(&self.geometry.index_buffer_view));
cmd.SetGraphicsRootConstantBufferView(1, bind.ubo_gva);
let skip_seethrough = self.mesh_glass_active();
for obj in &self.draw.objects {
if !obj.visible || !obj.resident {
continue;
}
if skip_seethrough && obj.material.see_through != 0 {
continue;
}
let push = ShadowPush {
model: obj.model,
cascade_idx: bind.slice_idx,
_pad: [0; 3],
};
let d = crate::gfx::lod::camera_distance(obj, cam_pos);
let (index_offset, index_count) = obj.active_lod(d);
cmd.SetGraphicsRoot32BitConstants(
0,
20,
&push as *const ShadowPush as *const std::ffi::c_void,
0,
);
cmd.DrawIndexedInstanced(
index_count as u32,
1,
index_offset as u32,
obj.base_vertex,
0,
);
self.inc_draw_calls(1);
}
let layouts = self.instanced.bucket_layouts.read().unwrap();
for buckets in layouts.iter() {
for bucket in buckets.iter() {
for &model in &bucket.instances {
let push = ShadowPush {
model,
cascade_idx: bind.slice_idx,
_pad: [0; 3],
};
cmd.SetGraphicsRoot32BitConstants(
0,
20,
&push as *const ShadowPush as *const std::ffi::c_void,
0,
);
cmd.DrawIndexedInstanced(
bucket.index_count as u32,
1,
bucket.index_offset as u32,
0,
0,
);
self.inc_draw_calls(1);
}
}
}
}
}
pub(in crate::directx) fn encode_shadow_skinned_into(
&self,
cmd: &ID3D12GraphicsCommandList,
ubo_gva: u64,
slice_idx: u32,
frame_idx: usize,
cam_pos: [f32; 3],
) {
let (Some(pso), Some(root_sig)) = (
self.skinned.shadow_pso.as_ref(),
self.skinned.shadow_root_sig.as_ref(),
) else {
return;
};
if self.skinned.draw_objects.is_empty() {
return;
}
unsafe {
cmd.SetPipelineState(pso);
cmd.SetGraphicsRootSignature(root_sig);
cmd.IASetPrimitiveTopology(
windows::Win32::Graphics::Direct3D::D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST,
);
cmd.IASetVertexBuffers(0, Some(&[self.skinned.vertex_buffer_view]));
cmd.IASetIndexBuffer(Some(&self.skinned.index_buffer_view));
cmd.SetGraphicsRootConstantBufferView(1, ubo_gva);
for (i, obj) in self.skinned.draw_objects.iter().enumerate() {
if !obj.visible {
continue;
}
let d = crate::gfx::lod::skinned_camera_distance(obj, cam_pos);
let (index_offset, index_count) = obj.active_lod(d);
let push = ShadowPush {
model: obj.model,
cascade_idx: slice_idx,
_pad: [0; 3],
};
cmd.SetGraphicsRoot32BitConstants(
0,
20,
&push as *const ShadowPush as *const std::ffi::c_void,
0,
);
cmd.SetGraphicsRootShaderResourceView(2, self.skinned_joint_gva(frame_idx, i));
cmd.DrawIndexedInstanced(index_count as u32, 1, index_offset as u32, 0, 0);
self.inc_draw_calls(1);
}
}
}
fn encode_shadow_pass_legacy(
&self,
cmd: &ID3D12GraphicsCommandList,
frame_idx: usize,
shadow_ubo_gva: u64,
cam_pos: [f32; 3],
render_mask: u32,
pipeline: ShadowPipeline<'_>,
) {
for cascade_idx in 0..NUM_SHADOW_CASCADES {
if render_mask & (1u32 << cascade_idx) == 0 {
continue;
}
let dsv = self.shadow.dsvs[cascade_idx];
unsafe {
cmd.OMSetRenderTargets(0, None, false, Some(&dsv));
cmd.ClearDepthStencilView(dsv, D3D12_CLEAR_FLAG_DEPTH, 1.0, 0, None);
}
self.encode_shadow_casters_into(
cmd,
ShadowPassBinding {
pso: pipeline.pso,
root_sig: pipeline.root_sig,
ubo_gva: shadow_ubo_gva,
slice_idx: cascade_idx as u32,
},
cam_pos,
);
}
for cascade_idx in 0..NUM_SHADOW_CASCADES {
if render_mask & (1u32 << cascade_idx) == 0 {
continue;
}
let dsv = self.shadow.dsvs[cascade_idx];
unsafe {
cmd.OMSetRenderTargets(0, None, false, Some(&dsv));
}
self.encode_shadow_skinned_into(
cmd,
shadow_ubo_gva,
cascade_idx as u32,
frame_idx,
cam_pos,
);
}
}
}