use ash::vk;
use concinnity_core::gfx::render_types::PostProcessParams;
use concinnity_core::render::backend_init::{BackendInit, PostSettings, WorldShader};
use concinnity_core::render::error::{RenderError, RenderResult};
use concinnity_core::render::scene_state::{DrawList, SceneState};
use super::context::*;
use super::geometry_upload::GeometryUploads;
use super::texture::GpuUploadContext;
use crate::vulkan::global_set::GlobalBindings;
mod bloom;
pub(in crate::vulkan) mod bootstrap;
mod commands;
mod composite;
mod cull;
mod descriptors;
mod effects;
mod ray_tracing;
mod scene_assets;
mod scene_data;
mod shadow;
mod targets;
mod text;
struct InitGpu<'a> {
hw: &'a VkHardware,
command_pool: vk::CommandPool,
frames: usize,
hot_reload: bool,
}
impl InitGpu<'_> {
fn upload(&self) -> GpuUploadContext<'_> {
GpuUploadContext {
alloc: &self.hw.alloc,
device: &self.hw.device,
command_pool: self.command_pool,
queue: self.hw.graphics_queue,
}
}
}
struct Features {
taa_enabled: bool,
msaa_samples: vk::SampleCountFlags,
post_process: PostProcessParams,
rt_wanted: bool,
gbuffer_enabled: bool,
}
impl Features {
fn resolve(hw: &VkHardware, post: &PostSettings) -> Self {
let taa_enabled = post.taa_enabled || post.temporal_upscaling;
let msaa_samples = super::device::resolve_sample_count(
super::device::get_max_usable_sample_count(&hw.instance, hw.physical_device),
post.hdr_samples,
);
tracing::info!("vulkan HDR target: {}x MSAA", msaa_samples.as_raw().max(1));
let post_process = hw.hdr_mode.post_process_params(post.post_process);
let rt_wanted = post.rt_reflections.is_some() && hw.rt_capable;
Self {
taa_enabled,
msaa_samples,
post_process,
rt_wanted,
gbuffer_enabled: taa_enabled
|| post.ssao.is_some()
|| post.ssr.is_some()
|| post.ssgi.is_some()
|| rt_wanted,
}
}
}
impl VkContext {
pub(crate) fn new(init: BackendInit<'_>) -> RenderResult<Self> {
Self::build(init, None)
}
fn build(
init: BackendInit<'_>,
reuse: Option<(VkHardware, SwapchainState)>,
) -> RenderResult<Self> {
let BackendInit {
window,
validation,
frames_in_flight,
vsync,
clear_color,
hot_reload,
capture: _,
embedded_surface: _,
scene: world,
shaders: world_shaders,
media,
light_uniforms,
local_lights,
spot_shadows,
area_lights,
shadows,
anisotropy,
planar,
post,
fx,
requirements: _,
} = init;
let &WorldShader {
programs: world_programs,
deferred: _,
} = world_shaders
.first()
.ok_or_else(|| RenderError::Other("BackendInit carried no shaders".to_string()))?;
super::context::record_main_thread();
let frames = frames_in_flight.max(1);
let request = bootstrap::HardwareRequest {
window,
validation,
frames,
vsync,
post: &post,
};
let (hw, swapchain) = match reuse {
Some(reuse) => bootstrap::inherit_hardware(reuse, &request)?,
None => bootstrap::acquire_hardware(&request)?,
};
let features = Features::resolve(&hw, &post);
let command_pool = commands::create_command_pool(&hw)?;
let gpu = InitGpu {
hw: &hw,
command_pool,
frames,
hot_reload,
};
let mut upscale_requested = crate::upscale_sdk::UpscaleRequest::from_post(&post);
let (upscale, render_extent) =
effects::build_upscale(&gpu, swapchain.extent, &post, &mut upscale_requested)?;
commands::reset_timestamp_queries(&gpu)?;
let area_light = scene_data::build_area_lights(&gpu, &area_lights)?;
let scene = scene_assets::build_scene_assets(&gpu, &media, anisotropy)?;
let targets = targets::build_render_targets(
&gpu,
targets::TargetInputs {
swapchain: &swapchain,
features: &features,
render_extent,
ssao_enabled: post.ssao.is_some(),
},
)?;
let (geometry, uniforms, light_cull) = scene_data::build_scene_resources(
&gpu,
scene_data::SceneInputs {
world: &world,
local_lights: &local_lights,
light_uniforms,
},
)?;
let shadow = shadow::build_shadow(&gpu, &shadows, &uniforms.light_uniforms)?;
let spot_shadow = shadow::build_spot_shadow(&gpu, &shadow, &spot_shadows)?;
let probe_gpu = crate::vulkan::probe_set::ProbeSetGpu::new(&gpu.upload(), frames)?;
for (frame, records) in probe_gpu.records.iter().enumerate() {
light_cull.write_probe_records(&hw.device, frame, records.descriptor());
}
let globals = GlobalBindings {
uniforms: &uniforms,
light_cull: &light_cull,
shadow: &shadow,
spot_shadow: &spot_shadow,
area_light: &area_light,
scene: &scene,
targets: &targets,
probes: &probe_gpu,
};
let plan = cull::plan_cull(&gpu, &world, media.textures);
let descriptors = descriptors::build_descriptors(
&gpu,
descriptors::SetPoolInputs {
instanced_clusters: &world.instanced_clusters,
text_atlas_count: media.text_atlases.len(),
plan: &plan,
},
&globals,
)?;
let screen = effects::build_screen_space(
&gpu,
effects::ScreenSpaceInputs {
post: &post,
features: &features,
targets: &targets,
scene: &scene,
descriptors: &descriptors,
},
)?;
let (cull, probe_prefilter) = cull::build_cull(
&gpu,
cull::CullInputs {
world: &world,
world_shaders: &world_shaders,
plan: &plan,
occlusion_two_pass: post.occlusion_two_pass,
descriptors: &descriptors,
targets: &targets,
scene: &scene,
shadow: &shadow,
spot_slices: spot_shadow.count() as usize,
gbuffer: screen.gbuffer.as_ref(),
swapchain_format: swapchain.format,
},
)?;
let rt = ray_tracing::build_rt_reflections(
&gpu,
ray_tracing::RtInputs {
world: &world,
geometry: &geometry,
scene: &scene,
targets: &targets,
gbuffer: screen.gbuffer.as_ref(),
descriptors: &descriptors,
cull: &cull,
post: &post,
post_support: &screen.post,
rt_wanted: features.rt_wanted,
},
)?;
let bloom = bloom::build_bloom(
&gpu,
bloom::BloomInputs {
extent: swapchain.extent,
post: &screen.post,
scene: &scene,
descriptors: &descriptors,
},
)?;
let composite = composite::build_composite(
&gpu,
composite::CompositeInputs {
swapchain: &swapchain,
descriptors: &descriptors,
scene: &scene,
targets: &targets,
sampler: &screen.post.sampler,
reflection_composite: rt.composite.as_ref(),
},
)?;
let text = text::build_text(&gpu, &media, &composite, &descriptors)?;
let taa = effects::build_taa_and_wire_scene_inputs(
&gpu,
effects::SceneInputWiring {
features: &features,
targets: &targets,
scene: &scene,
descriptors: &descriptors,
composite: &composite,
screen: &screen,
upscale: upscale.as_deref(),
},
)?;
let world_fx = effects::build_world_effects(
&gpu,
effects::WorldEffectInputs {
targets: &targets,
descriptors: &descriptors,
fx: &fx,
planar,
cull: &cull,
n_cull: plan.n_cull,
rt: &rt,
geometry: &geometry,
post: &post,
},
&globals,
)?;
let (commands, frame_sync) = commands::build_frame_commands(&gpu, &swapchain)?;
let sky = crate::vulkan::sky::VkSky::build(
&hw.device,
descriptors.global_set_layout.handle(),
targets.main_render_pass.handle(),
targets.msaa_samples,
media.env_map_background,
hot_reload,
)?;
let mut me = Self {
swapchain,
targets,
composite,
shadow,
spot_shadow,
area_light,
scene,
cull,
light_cull,
text,
bloom: Some(bloom),
post_process: features.post_process,
taa,
post: screen.post,
upscale,
upscale_requested,
ssao: screen.ssao,
ssr: screen.ssr,
reflection_composite: rt.composite,
ssgi: screen.ssgi,
gbuffer: screen.gbuffer,
rt_reflections: rt.reflections,
rt: rt.state,
decal: world_fx.decal,
lines: crate::vulkan::line::LineState::empty(),
fog: world_fx.fog,
raymarch: world_fx.raymarch,
sky,
transparent: world_fx.transparent,
planar_reflection: world_fx.planar_reflection,
particle: Default::default(),
auto_exposure: world_fx.auto_exposure,
hot_reload: HotReloadState::new(hot_reload),
pipeline_gate: Default::default(),
world_shader: world_programs.cloned(),
frame_stats: Default::default(),
draw_calls_accum: Default::default(),
descriptors,
instanced: VkInstanced::new(world.instanced_clusters),
geometry,
geometry_uploads: core::cell::RefCell::new(GeometryUploads::new(frames)),
skinned: VkSkinned::new(),
uniforms,
frame_sync,
current_frame: 0,
frames_in_flight: frames,
commands,
state: SceneState::new(
DrawList::with_runtime_reserve(
world.draw_objects,
plan.n_instances,
world.n_chunk_max,
),
clear_color,
),
graph_cache: None,
barrier_scratch: None,
wireframe: Default::default(),
probe: ProbeState::new(probe_prefilter, probe_gpu),
stream: StreamState::new(frames),
reused_by_successor: false,
world_content_destroyed: false,
hw,
};
me.upload_initial_decals(fx.decals)?;
me.upload_initial_particles(fx.particles)?;
me.hw.alloc.reclaim_idle();
tracing::info!(
"device allocator: {} ({} allocations allowed)",
me.hw.alloc.stats(),
me.hw.alloc.max_allocations(),
);
crate::shader::cache::report_init();
super::pipeline_cache::serialize(&me.hw.device);
crate::shader::pipeline_cache::report_init(super::pipeline_cache::disk_state());
crate::shader::runtime_cache::checkpoint();
Ok(me)
}
pub(in crate::vulkan) fn apply_world_reload(
&mut self,
init: BackendInit<'_>,
) -> RenderResult<()> {
self.wait_idle();
let reuse = (self.hw.hand_over()?, self.swapchain.share());
self.reused_by_successor = true;
self.destroy_world_content();
self.hw.alloc.reclaim_idle();
self.hw.device.reclaim_idle();
tracing::debug!("reload: old world freed: {}", self.hw.alloc.stats());
match VkContext::build(init, Some(reuse)) {
Ok(rebuilt) => {
*self = rebuilt;
Ok(())
}
Err(e) => {
self.reused_by_successor = false;
self.destroy_swapchain_resources();
Err(e)
}
}
}
}