use ash::vk;
use concinnity_core::gfx::render_types::{
self, LightUniforms, NUM_SHADOW_CASCADES, ShadowUniforms, SpotShadowData,
};
use concinnity_core::render::backend_init::ShadowParams;
use concinnity_core::render::error::RenderResult;
use concinnity_core::render::lights;
use super::InitGpu;
use crate::vulkan::context::{VkShadow, VkSpotShadow};
use crate::vulkan::draw::upload_shadow_uniforms;
use crate::vulkan::pipeline::{create_shadow_pipeline, resolve_shadow_shader};
use crate::vulkan::render_pass::create_shadow_render_pass;
use crate::vulkan::resources::create_descriptor_set_layout;
use crate::vulkan::swapchain::create_shadow_framebuffers;
use crate::vulkan::texture::{create_sampler_shadow, create_shadow_map_array};
pub(super) fn build_shadow(
gpu: &InitGpu<'_>,
shadows: &ShadowParams,
light_uniforms: &LightUniforms,
) -> RenderResult<VkShadow> {
let InitGpu {
hw,
frames,
hot_reload,
..
} = *gpu;
let device = &hw.device;
let map = create_shadow_map_array(&gpu.upload(), shadows.map_size, NUM_SHADOW_CASCADES as u32)?;
let render_pass = create_shadow_render_pass(device)?;
let sampler = create_sampler_shadow(device)?;
let global_set_layout = create_descriptor_set_layout(
device,
&crate::vulkan::descriptor_layout::shadow_global_set(),
)?;
let (pipeline, pipeline_layout, framebuffers) = if shadows.map_size > 0
&& let Ok(Some(shadow_spv)) = resolve_shadow_shader(hot_reload)
{
let shadow_pc_range = vk::PushConstantRange::default()
.stage_flags(vk::ShaderStageFlags::VERTEX)
.offset(0)
.size(80);
let shadow_set_layouts = [global_set_layout.handle()];
let layout = device
.create_pipeline_layout(
&vk::PipelineLayoutCreateInfo::default()
.set_layouts(&shadow_set_layouts)
.push_constant_ranges(std::slice::from_ref(&shadow_pc_range)),
)
.map_err(|e| crate::vulkan::error::map_vk_result(e, "shadow pipeline layout"))?;
let pl =
create_shadow_pipeline(device, render_pass.handle(), layout.handle(), &shadow_spv)?;
let fbs = create_shadow_framebuffers(device, render_pass.handle(), &map, shadows.map_size)?;
(Some(pl), Some(layout), fbs)
} else {
(None, None, Vec::new())
};
let shadow_ubo_size = std::mem::size_of::<ShadowUniforms>() as u64;
let mut ubos = Vec::with_capacity(frames);
for _ in 0..frames {
ubos.push(hw.alloc.create_buffer(
shadow_ubo_size,
vk::BufferUsageFlags::UNIFORM_BUFFER,
vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT,
)?);
}
let uniforms = concinnity_core::render::csm::empty_shadow_uniforms();
for ubo in &ubos {
upload_shadow_uniforms(ubo, &uniforms);
}
Ok(VkShadow {
render_pass,
map,
map_size: shadows.map_size,
framebuffers,
pipeline,
pipeline_layout,
global_set_layout,
sampler,
skinned_pipeline: None,
skinned_pipeline_layout: None,
ubos,
uniforms,
light_dir: lights::sun_direction(light_uniforms),
update: shadows.update,
distance: shadows.distance,
cascades: shadows.cascades,
scheduler: Default::default(),
render_mask: 0,
})
}
pub(super) fn build_spot_shadow(
gpu: &InitGpu<'_>,
shadow: &VkShadow,
spot_shadows: &[SpotShadowData],
) -> RenderResult<VkSpotShadow> {
let hw = gpu.hw;
let slice_size = render_types::spot_shadow_slice_size(shadow.map_size);
let map = create_shadow_map_array(
&gpu.upload(),
if spot_shadows.is_empty() {
0
} else {
slice_size
},
spot_shadows.len().max(1) as u32,
)?;
crate::vulkan::draw::spot_shadow::build_spot_shadow(
crate::vulkan::draw::spot_shadow::SpotShadowBuild {
alloc: &hw.alloc,
instance: &hw.instance,
device: &hw.device,
physical_device: hw.physical_device,
map,
render_pass: shadow.render_pass.handle(),
set_layout: shadow.global_set_layout.handle(),
slice_size,
spot_shadows,
},
)
}