use concinnity_core::gfx::render_types::clone_reserve;
use concinnity_core::render::backend_init::{SceneData, WorldShader};
use concinnity_core::render::error::RenderResult;
use concinnity_core::transform::IDENTITY;
use super::InitGpu;
use crate::directx::context::{DxDescriptors, DxTargets, FRAMES};
use crate::directx::cull::{CullKernels, CullState};
use crate::directx::probe_prefilter::ProbePrefilterPipelines;
use bindless::BindlessPass;
use compute::ComputeCull;
mod bindless;
mod compute;
mod gbuffer;
mod shadow;
mod two_pass;
pub(super) struct CullPlan {
pub(super) n_instances: usize,
pub(super) n_cull: usize,
}
pub(super) fn plan_cull(world: &SceneData<'_>) -> CullPlan {
let n_instances: usize = world
.instanced_clusters
.iter()
.map(|c| c.instances.len())
.sum();
let n_objects = world.draw_objects.len();
let n_cull =
n_objects + n_instances + world.n_chunk_max + clone_reserve(n_objects) + world.n_skinned;
CullPlan {
n_instances,
n_cull,
}
}
pub(super) struct CullInputs<'a> {
pub(super) world: &'a SceneData<'a>,
pub(super) world_shaders: &'a [WorldShader<'a>],
pub(super) plan: &'a CullPlan,
pub(super) descriptors: &'a DxDescriptors,
pub(super) targets: &'a DxTargets,
pub(super) albedo_count: usize,
pub(super) shadow_enabled: bool,
pub(super) spot_slices: usize,
pub(super) gbuffer_enabled: bool,
pub(super) occlusion_two_pass: bool,
}
pub(super) fn build_cull(gpu: &InitGpu<'_>, inputs: CullInputs<'_>) -> RenderResult<CullState> {
let CullInputs {
world,
world_shaders,
plan,
descriptors,
targets,
albedo_count,
shadow_enabled,
spot_slices,
gbuffer_enabled,
occlusion_two_pass,
} = inputs;
let bindless = bindless::build_bindless_pass(
gpu,
world,
world_shaders,
plan,
targets.hdr.msaa_samples,
gbuffer_enabled,
)?;
let compute = compute::build_compute_cull(
gpu,
compute::ComputeInputs {
bindless: &bindless,
plan,
descriptors,
targets,
},
)?;
let two_pass =
two_pass::build_two_pass_cull(gpu, &bindless, &compute, plan, occlusion_two_pass)?;
let shadow_cull = shadow::build_shadow_cull(gpu, &compute, plan, shadow_enabled, spot_slices)?;
let gbuffer_pass = gbuffer::build_gbuffer_pass(gpu, &compute, plan, gbuffer_enabled)?;
write_instance_records(world, plan, albedo_count, &bindless, &compute);
let bindless_pool_gpu = (0..FRAMES)
.map(|f| {
descriptors
.slot_gpu(descriptors.layout.flat_pool_base_slot + f * descriptors.flat_pool_len)
})
.collect();
Ok(CullState {
main_bindless_root_sig: Some(bindless.root_sigs.main),
main_bindless_pso: Some(bindless.psos.main),
prepass_root_sig: Some(bindless.prepass_root_sig),
prepass_cmd_sig: Some(bindless.prepass_cmd_sig),
main_prepass_pso: bindless.psos.prepass,
world_pipelines: concinnity_core::render::world_pipelines::WorldPipelines::new(
bindless.world_pipelines,
),
bucket_stride: plan.n_cull,
bindless_main_shaders: bindless.shaders,
object_buffer_resources: bindless.object_buffers,
object_buffer_ptrs: bindless.object_ptrs,
material_params: bindless.material_params,
bindless_pool_gpu,
cull_kernels: compute.kernels.map(|kernels| CullKernels {
pso_phase2: two_pass.pso,
..kernels
}),
cull_command_signature: compute.command_signature,
draw_args_buffer_resources: compute.draw_args_buffers,
draw_args_buffer_ptrs: compute.draw_args_ptrs,
indirect_cmd_buffers: compute.indirect_buffers,
cull_status_buffers: compute.status_buffers,
indirect_cmd_buffers_2: two_pass.indirect_buffers,
shadow_bindless_root_sig: shadow_cull.bindless_root_sig,
shadow_bindless_pso: shadow_cull.bindless_pso,
shadow_bindless_cmd_sig: shadow_cull.cmd_sig,
cull_pso_shadow: shadow_cull.cull_pso,
shadow_indirect_buffers: shadow_cull.indirect_buffers,
shadow_cull_status_buffers: shadow_cull.status_buffers,
spot_indirect_buffers: shadow_cull.spot_indirect_buffers,
prev_model_buffers: gbuffer_pass.prev_model_buffers,
model_history_root_sig: gbuffer_pass.model_history_root_sig,
model_history_pso: gbuffer_pass.model_history_pso,
occlusion_two_pass,
hiz: compute.hiz,
prev_view_proj: std::cell::Cell::new(IDENTITY),
hiz_valid: std::cell::Cell::new(false),
})
}
pub(super) fn build_probe_prefilter(
gpu: &InitGpu<'_>,
cull: &CullState,
) -> RenderResult<Option<ProbePrefilterPipelines>> {
let typed_uav_load = crate::directx::probe_prefilter::typed_uav_load_supported(&gpu.hw.device);
if cull.cull_kernels.is_some() && !typed_uav_load {
tracing::warn!(
"reflection probes: device lacks TypedUAVLoadAdditionalFormats, skipping probe baking"
);
}
let probe_prefilter = match cull.cull_kernels.is_some() && typed_uav_load {
true => Some(ProbePrefilterPipelines::new(
&gpu.hw.device,
gpu.hot_reload,
)?),
false => None,
};
Ok(probe_prefilter)
}
fn write_instance_records(
world: &SceneData<'_>,
plan: &CullPlan,
albedo_count: usize,
bindless: &BindlessPass,
compute: &ComputeCull,
) {
use concinnity_core::gfx::render_types::{
GpuDrawArgs, GpuObjectData, draw_args_flags, instance_object_records,
};
if plan.n_instances == 0 || bindless.object_ptrs.is_empty() {
return;
}
let n_objects = world.draw_objects.len();
let records = instance_object_records(&world.instanced_clusters, albedo_count as u32);
let mut draw_args: Vec<GpuDrawArgs> = Vec::with_capacity(records.len());
for cluster in &world.instanced_clusters {
for _ in &cluster.instances {
draw_args.push(GpuDrawArgs {
index_count: cluster.index_count as u32,
index_offset: cluster.index_offset as u32,
base_vertex: 0,
flags: draw_args_flags(true, true, true),
});
}
}
let obj_stride = std::mem::size_of::<GpuObjectData>();
let da_stride = std::mem::size_of::<GpuDrawArgs>();
for (obj_ptr, da_ptr) in bindless
.object_ptrs
.iter()
.zip(compute.draw_args_ptrs.iter())
{
unsafe {
std::ptr::copy_nonoverlapping(
records.as_ptr() as *const u8,
obj_ptr.add(n_objects * obj_stride),
records.len() * obj_stride,
);
std::ptr::copy_nonoverlapping(
draw_args.as_ptr() as *const u8,
da_ptr.add(n_objects * da_stride),
draw_args.len() * da_stride,
);
}
}
}