use concinnity_core::gfx::render_types::{NUM_SHADOW_CASCADES, ShadowUniforms};
use concinnity_core::render::backend_init::ShadowCadence;
use concinnity_core::render::depth::DEPTH_CLEAR;
use concinnity_core::render::shadow_schedule;
use windows::Win32::Foundation::RECT;
use windows::Win32::Graphics::Direct3D12::*;
use crate::directx::com;
use crate::directx::context::DxContext;
use crate::directx::descriptor_slot::SrvSlot;
use crate::directx::root_constants::RootConstants;
use crate::directx::texture::GpuResource;
pub(in crate::directx) struct ShadowState {
pub resource: Option<GpuResource<ID3D12Resource>>,
pub dsvs: Vec<D3D12_CPU_DESCRIPTOR_HANDLE>,
pub map_size: u32,
pub layers: u32,
pub srv_gpu: SrvSlot,
pub light_dir: [f32; 3],
pub cadence: ShadowCadence,
pub scheduler: shadow_schedule::ShadowCascadeScheduler,
pub render_mask: u32,
pub uniforms: ShadowUniforms,
}
#[derive(Clone, Copy)]
pub(in crate::directx) struct ShadowView<'a> {
pub dsv: D3D12_CPU_DESCRIPTOR_HANDLE,
pub ubo_gva: u64,
pub vp_index: u32,
pub indirect: &'a ID3D12Resource,
pub first_command: usize,
}
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>,
) {
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
};
self.set_shadow_raster_state(cmd, sm);
for cascade_idx in 0..NUM_SHADOW_CASCADES {
if render_mask & (1u32 << cascade_idx) == 0 {
continue;
}
self.clear_shadow_slice(cmd, self.shadow.dsvs[cascade_idx]);
}
if let Some(indirect) = self.cull.shadow_indirect_buffers.get(frame_idx)
&& self.shadow_views_drawable()
{
self.encode_shadow_culls(cmd, frame_idx, render_mask, cam_pos);
self.bind_shadow_views(cmd, frame_idx);
let n_cull = self.cull_count();
for cascade_idx in 0..NUM_SHADOW_CASCADES {
if render_mask & (1u32 << cascade_idx) == 0 {
continue;
}
self.draw_shadow_view(
cmd,
frame_idx,
ShadowView {
dsv: self.shadow.dsvs[cascade_idx],
ubo_gva: shadow_ubo_gva,
vp_index: cascade_idx as u32,
indirect,
first_command: cascade_idx * n_cull,
},
);
}
}
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);
}
}
pub(in crate::directx) fn set_shadow_raster_state(
&self,
cmd: &ID3D12GraphicsCommandList,
size: u32,
) {
unsafe {
cmd.RSSetViewports(&[D3D12_VIEWPORT {
TopLeftX: 0.0,
TopLeftY: 0.0,
Width: size as f32,
Height: size as f32,
MinDepth: 0.0,
MaxDepth: 1.0,
}]);
cmd.RSSetScissorRects(&[RECT {
left: 0,
top: 0,
right: size as i32,
bottom: size as i32,
}]);
cmd.IASetPrimitiveTopology(
windows::Win32::Graphics::Direct3D::D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST,
);
}
}
pub(in crate::directx) fn clear_shadow_slice(
&self,
cmd: &ID3D12GraphicsCommandList,
dsv: D3D12_CPU_DESCRIPTOR_HANDLE,
) {
unsafe {
cmd.OMSetRenderTargets(0, None, false, Some(&dsv));
cmd.ClearDepthStencilView(dsv, D3D12_CLEAR_FLAG_DEPTH, DEPTH_CLEAR, 0, None);
}
}
pub(in crate::directx) fn shadow_views_drawable(&self) -> bool {
self.cull.shadow_bindless_pso.is_some()
&& self.cull.shadow_bindless_cmd_sig.is_some()
&& self.cull_count() > 0
}
pub(in crate::directx) fn bind_shadow_views(
&self,
cmd: &ID3D12GraphicsCommandList,
frame_idx: usize,
) {
let (Some(pso), Some(root_sig)) = (
self.cull.shadow_bindless_pso.as_ref(),
self.cull.shadow_bindless_root_sig.as_ref(),
) else {
return;
};
let object_gva = com::gpu_va(&self.cull.object_buffer_resources[frame_idx]);
unsafe {
cmd.SetPipelineState(pso);
cmd.SetGraphicsRootSignature(root_sig);
cmd.SetGraphicsRootShaderResourceView(3, object_gva);
}
}
pub(in crate::directx) fn draw_shadow_view(
&self,
cmd: &ID3D12GraphicsCommandList,
frame_idx: usize,
view: ShadowView<'_>,
) {
let Some(cmd_sig) = self.cull.shadow_bindless_cmd_sig.as_ref() else {
return;
};
let prefix = self.skinned_record_base();
let stride = crate::directx::cull::INDIRECT_COMMAND_STRIDE as u64;
let byte_off = |first: usize| first as u64 * stride;
unsafe {
cmd.OMSetRenderTargets(0, None, false, Some(&view.dsv));
cmd.SetGraphicsRootConstantBufferView(1, view.ubo_gva);
cmd.set_graphics_root_constants(2, &view.vp_index);
cmd.IASetVertexBuffers(0, Some(&[self.scene.geometry.vertex_buffer_view]));
cmd.IASetIndexBuffer(Some(&self.scene.geometry.index_buffer_view));
cmd.ExecuteIndirect(
cmd_sig,
prefix as u32,
view.indirect,
byte_off(view.first_command),
None::<&ID3D12Resource>,
0,
);
}
self.inc_draw_calls(1);
if self.state.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));
cmd.ExecuteIndirect(
cmd_sig,
self.state.draw.n_skinned as u32,
view.indirect,
byte_off(view.first_command + prefix),
None::<&ID3D12Resource>,
0,
);
}
self.inc_draw_calls(1);
}
}
}