use ash::vk;
use concinnity_core::gfx::render_types;
use concinnity_core::render::error::RenderResult;
use super::CullPlan;
use super::bindless::BindlessPass;
use super::compute::{ComputeCull, main_cull_set};
use crate::vulkan::context::HDR_FORMAT;
use crate::vulkan::descriptor_layout::PoolSizes;
use crate::vulkan::init::InitGpu;
use crate::vulkan::owned::{OwnedDescriptorPool, OwnedPipeline, OwnedRenderPass};
use crate::vulkan::pipeline::*;
use crate::vulkan::pipeline_desc::compute_pipeline;
use crate::vulkan::render_pass::create_main_render_pass_two_pass;
use crate::vulkan::resources::alloc_descriptor_sets;
use crate::vulkan::set_writes::SetWrites;
pub(super) struct TwoPassCull {
pub(super) pipeline: Option<OwnedPipeline>,
pub(super) sets: Vec<vk::DescriptorSet>,
pub(super) pool: Option<OwnedDescriptorPool>,
pub(super) indirect_buffers: Vec<crate::vulkan::allocator::PooledBuffer>,
pub(super) main_render_pass_phase1: Option<OwnedRenderPass>,
pub(super) main_render_pass_phase2: Option<OwnedRenderPass>,
}
pub(super) fn build_two_pass_cull(
gpu: &InitGpu<'_>,
bindless: &BindlessPass,
compute: &ComputeCull,
plan: &CullPlan,
shader_bucket_count: usize,
msaa_samples: vk::SampleCountFlags,
occlusion_two_pass: bool,
) -> RenderResult<TwoPassCull> {
let InitGpu {
hw,
frames,
hot_reload,
..
} = *gpu;
let (device, alloc) = (&hw.device, &hw.alloc);
let n_frames = frames as u32;
let (object_buffers, draw_args_buffers, cull_status_buffers) = (
&bindless.object_buffers,
&compute.draw_args_buffers,
&compute.status_buffers,
);
type TwoPassCullResources = (
Option<OwnedPipeline>,
Vec<vk::DescriptorSet>,
Option<OwnedDescriptorPool>,
Vec<crate::vulkan::allocator::PooledBuffer>,
Option<OwnedRenderPass>,
Option<OwnedRenderPass>,
);
let (
cull_pipeline_phase2,
cull_sets2,
two_pass_pool,
indirect_buffers2,
main_render_pass_phase1,
main_render_pass_phase2,
): TwoPassCullResources = if let Some(kernels) = compute.kernels.as_ref()
&& occlusion_two_pass
{
let (set_layout, pipeline_layout) = (&kernels.set_layout, &kernels.pipeline_layout);
let n = plan.n_cull as u64;
let object_buffer_size = n * std::mem::size_of::<render_types::GpuObjectData>() as u64;
let draw_args_size = n * std::mem::size_of::<render_types::GpuDrawArgs>() as u64;
let indirect_size = shader_bucket_count as u64
* n
* std::mem::size_of::<vk::DrawIndexedIndirectCommand>() as u64;
let status_size = n * std::mem::size_of::<u32>() as u64;
let cs2 = compile_cull_shader_phase2(hot_reload)?;
let pipeline2 = compute_pipeline(device, pipeline_layout.handle(), &cs2, "cull phase-2")?;
let mut ind2_buffers = Vec::with_capacity(frames);
for _ in 0..frames {
ind2_buffers.push(alloc.create_buffer(
indirect_size,
vk::BufferUsageFlags::STORAGE_BUFFER | vk::BufferUsageFlags::INDIRECT_BUFFER,
vk::MemoryPropertyFlags::DEVICE_LOCAL,
)?);
}
let pool_sizes = PoolSizes::default()
.sets(&main_cull_set(), n_frames)
.build();
let pool = device
.create_descriptor_pool(
&vk::DescriptorPoolCreateInfo::default()
.pool_sizes(&pool_sizes)
.max_sets(n_frames),
)
.map_err(|e| crate::vulkan::error::map_vk_result(e, "two-pass cull descriptor pool"))?;
let set_layouts2: Vec<_> = (0..frames).map(|_| set_layout.handle()).collect();
let sets2 = alloc_descriptor_sets(device, pool.handle(), &set_layouts2)?;
for (i, &set) in sets2.iter().enumerate() {
SetWrites::new(set)
.storage_buffer(0, object_buffers[i].buffer(), object_buffer_size)
.storage_buffer(1, draw_args_buffers[i].buffer(), draw_args_size)
.storage_buffer(2, ind2_buffers[i].buffer(), indirect_size)
.storage_buffer(3, cull_status_buffers[i].buffer(), status_size)
.apply(device);
}
let rp1 = create_main_render_pass_two_pass(device, HDR_FORMAT, msaa_samples, false)?;
let rp2 = create_main_render_pass_two_pass(device, HDR_FORMAT, msaa_samples, true)?;
(
Some(pipeline2),
sets2,
Some(pool),
ind2_buffers,
Some(rp1),
Some(rp2),
)
} else {
(None, Vec::new(), None, Vec::new(), None, None)
};
Ok(TwoPassCull {
pipeline: cull_pipeline_phase2,
sets: cull_sets2,
pool: two_pass_pool,
indirect_buffers: indirect_buffers2,
main_render_pass_phase1,
main_render_pass_phase2,
})
}