use concinnity_core::gfx::render_types;
use concinnity_core::render::error::RenderResult;
use windows::Win32::Graphics::Direct3D12::*;
use super::CullPlan;
use super::bindless::BindlessPass;
use crate::directx::allocator::PooledBuffer;
use crate::directx::context::{DxDescriptors, DxTargets, FRAMES, align256, dump_on_err};
use crate::directx::cull::{
CullKernels, INDIRECT_COMMAND_STRIDE, compile_cull_shader, create_cull_command_signature,
create_cull_pso, create_cull_root_signature,
};
use crate::directx::descriptor_slot::SrvSlot;
use crate::directx::error::map_hresult;
use crate::directx::hiz::{HiZDeviceCtx, HiZResources, HiZTarget};
use crate::directx::init::{HIZ_MAX_MIPS, InitGpu};
use crate::directx::texture::create_uav_buffer;
pub(super) struct ComputeCull {
pub(super) kernels: Option<CullKernels>,
pub(super) command_signature: Option<ID3D12CommandSignature>,
pub(super) draw_args_buffers: Vec<PooledBuffer>,
pub(super) draw_args_ptrs: Vec<*mut u8>,
pub(super) indirect_buffers: Vec<ID3D12Resource>,
pub(super) status_buffers: Vec<ID3D12Resource>,
pub(super) hiz: Option<HiZResources>,
}
pub(super) struct ComputeInputs<'a> {
pub(super) bindless: &'a BindlessPass,
pub(super) plan: &'a CullPlan,
pub(super) descriptors: &'a DxDescriptors,
pub(super) targets: &'a DxTargets,
}
pub(super) fn indirect_buffer_size(bucket_count: usize, n_cull: usize) -> u64 {
align256((bucket_count * n_cull) as u64 * INDIRECT_COMMAND_STRIDE as u64)
}
pub(super) fn status_buffer_size(n_cull: usize) -> u64 {
align256((n_cull as u64) * std::mem::size_of::<u32>() as u64)
}
pub(super) fn build_compute_cull(
gpu: &InitGpu<'_>,
inputs: ComputeInputs<'_>,
) -> RenderResult<ComputeCull> {
let ComputeInputs {
bindless,
plan,
descriptors,
targets,
} = inputs;
let device = gpu.hw.alloc.device();
let info_queue = gpu.hw.info_queue.as_ref();
let n_cull = plan.n_cull;
if n_cull == 0 {
return Ok(ComputeCull {
kernels: None,
command_signature: None,
draw_args_buffers: Vec::new(),
draw_args_ptrs: Vec::new(),
indirect_buffers: Vec::new(),
status_buffers: Vec::new(),
hiz: None,
});
}
let cs = compile_cull_shader(gpu.hot_reload)?;
let crs = dump_on_err(info_queue, create_cull_root_signature(device))?;
let cps = dump_on_err(info_queue, create_cull_pso(device, &crs, &cs))?;
let csig = dump_on_err(
info_queue,
create_cull_command_signature(device, &bindless.root_sigs.main),
)?;
let draw_args_size =
align256((n_cull * std::mem::size_of::<render_types::GpuDrawArgs>()) as u64);
let indirect_size = indirect_buffer_size(1 + bindless.world_pipelines.len(), n_cull);
let status_size = status_buffer_size(n_cull);
let mut draw_args_buffers: Vec<PooledBuffer> = Vec::with_capacity(FRAMES + 1);
let mut draw_args_ptrs: Vec<*mut u8> = Vec::with_capacity(FRAMES + 1);
let mut indirect_buffers: Vec<ID3D12Resource> = Vec::with_capacity(FRAMES + 1);
let mut status_buffers: Vec<ID3D12Resource> = Vec::with_capacity(FRAMES + 1);
for _ in 0..FRAMES + 1 {
let da = gpu.hw.alloc.alloc_buffer(
draw_args_size,
D3D12_HEAP_TYPE_UPLOAD,
D3D12_RESOURCE_STATE_GENERIC_READ,
)?;
let mut ptr = std::ptr::null_mut::<std::ffi::c_void>();
unsafe { da.Map(0, None, Some(&mut ptr)) }
.map_err(|e| map_hresult(e.code(), "map draw args buffer"))?;
draw_args_ptrs.push(ptr as *mut u8);
draw_args_buffers.push(da);
indirect_buffers.push(create_uav_buffer(
device,
indirect_size,
D3D12_RESOURCE_STATE_COMMON,
)?);
status_buffers.push(create_uav_buffer(
device,
status_size,
D3D12_RESOURCE_STATE_COMMON,
)?);
}
let hiz = build_hiz(gpu, descriptors, targets)?;
Ok(ComputeCull {
kernels: Some(CullKernels {
root_sig: crs,
pso: cps,
pso_phase2: None,
}),
command_signature: Some(csig),
draw_args_buffers,
draw_args_ptrs,
indirect_buffers,
status_buffers,
hiz: Some(hiz),
})
}
fn build_hiz(
gpu: &InitGpu<'_>,
descriptors: &DxDescriptors,
targets: &DxTargets,
) -> RenderResult<HiZResources> {
let layout = &descriptors.layout;
let mut mip_uav_cpus: Vec<D3D12_CPU_DESCRIPTOR_HANDLE> = Vec::with_capacity(HIZ_MAX_MIPS);
let mut mip_uav_gpus: Vec<SrvSlot> = Vec::with_capacity(HIZ_MAX_MIPS);
for i in 0..HIZ_MAX_MIPS {
mip_uav_cpus.push(descriptors.slot_cpu(layout.hiz_uav_base_slot + i));
mip_uav_gpus.push(descriptors.slot_gpu(layout.hiz_uav_base_slot + i));
}
let hiz = HiZResources::new(
HiZDeviceCtx {
device: &gpu.hw.device,
info_queue: gpu.hw.info_queue.as_ref(),
hot_reload: gpu.hot_reload,
},
HiZTarget {
width: targets.extent.render_width,
height: targets.extent.render_height,
srv_cpu: descriptors.slot_cpu(layout.hiz_srv_slot),
srv_gpu: descriptors.slot_gpu(layout.hiz_srv_slot),
depth_srv_gpu: targets.main_depth_srv_gpu,
mip_uav_cpus,
mip_uav_gpus,
},
)?;
Ok(hiz)
}