use ash::vk;
use concinnity_core::gfx::auto_exposure;
use concinnity_core::render::backend_init::{PlanarBudget, PostSettings, WorldFx};
use concinnity_core::render::decal;
use concinnity_core::render::error::RenderResult;
use concinnity_core::render::lights;
use concinnity_core::render::planar_reflection::PlanarReflectors;
use super::ray_tracing::RtResources;
use super::{Features, InitGpu};
use crate::vulkan::context::{
AutoExposureState, CompositeState, DecalState, FogState, HDR_FORMAT, VkCull, VkDescriptors,
VkGeometry, VkSceneAssets, VkTargets,
};
use crate::vulkan::global_set::GlobalBindings;
use crate::vulkan::planar::PlanarReflectionSet;
use crate::vulkan::post::PostSupport;
use crate::vulkan::post::bloom::composite_bloom_view;
use crate::vulkan::post::gbuffer::GbufferResources;
use crate::vulkan::post::post_device::{PostQueue, VkPostDevice, VkPostProbes};
use crate::vulkan::post::ssao::SsaoResources;
use crate::vulkan::post::ssgi::SsgiResources;
use crate::vulkan::post::ssr::SsrResources;
use crate::vulkan::post::taa::TaaResources;
use crate::vulkan::post::upscale::VkUpscaleBackend;
use crate::vulkan::raymarch::RaymarchResources;
use crate::vulkan::swapchain::{
composite_channels, write_composite_channel_set, write_composite_set,
};
use crate::vulkan::transparent::TransparentResources;
pub(super) fn build_upscale(
gpu: &InitGpu<'_>,
swapchain_extent: vk::Extent2D,
post: &PostSettings,
requested: &mut crate::upscale_sdk::UpscaleRequest,
) -> RenderResult<(Option<Box<dyn VkUpscaleBackend>>, vk::Extent2D)> {
let InitGpu {
hw, command_pool, ..
} = *gpu;
let device = &hw.device;
let upscale = if post.temporal_upscaling {
let (built, resolved) = crate::vulkan::post::build_upscaler(
crate::vulkan::post::upscale::UpscalerGpu {
alloc: &hw.alloc,
instance: &hw.instance,
device,
physical_device: hw.physical_device,
command_pool,
queue: hw.graphics_queue,
},
(swapchain_extent.width, swapchain_extent.height),
post.upscale_scale,
requested,
)?;
if resolved == crate::vulkan::post::ResolvedBackend::Dlss
&& let Some(f) = device.debug_filter()
{
f.store(
super::bootstrap::DLSS_FIRST_FRAME_LAYOUT_SUPPRESS,
std::sync::atomic::Ordering::Relaxed,
);
}
built
} else {
None
};
let render_extent = match &upscale {
Some(u) => {
let (w, h) = u.extent().render;
vk::Extent2D {
width: w,
height: h,
}
}
None => swapchain_extent,
};
Ok((upscale, render_extent))
}
pub(super) fn shared_post_device<'a>(
gpu: &InitGpu<'a>,
post_support: &'a PostSupport,
scene: &'a VkSceneAssets,
descriptors: &'a VkDescriptors,
) -> VkPostDevice<'a> {
let InitGpu {
hw,
command_pool,
hot_reload,
..
} = *gpu;
VkPostDevice {
device: &hw.device,
alloc: &hw.alloc,
queue: PostQueue {
command_pool,
queue: hw.graphics_queue,
},
cache: &post_support.cache,
arena: &post_support.arena,
sampler: post_support.sampler.handle(),
cube_sampler: scene.cube_sampler.handle(),
probes: VkPostProbes {
layout: descriptors.global_set_layout.handle(),
sets: &[],
},
frame: 0,
hot_reload,
}
}
pub(super) struct ScreenSpaceInputs<'a> {
pub(super) post: &'a PostSettings,
pub(super) features: &'a Features,
pub(super) targets: &'a VkTargets,
pub(super) scene: &'a VkSceneAssets,
pub(super) descriptors: &'a VkDescriptors,
}
pub(super) struct ScreenSpace {
pub(super) ssao: Option<SsaoResources>,
pub(super) post: PostSupport,
pub(super) ssr: Option<SsrResources>,
pub(super) gbuffer: Option<GbufferResources>,
pub(super) ssgi: Option<SsgiResources>,
}
pub(super) fn build_screen_space(
gpu: &InitGpu<'_>,
inputs: ScreenSpaceInputs<'_>,
) -> RenderResult<ScreenSpace> {
let InitGpu {
hw,
command_pool,
frames,
hot_reload,
..
} = *gpu;
let (device, alloc) = (&hw.device, &hw.alloc);
let ScreenSpaceInputs {
post,
features,
targets,
scene,
descriptors,
} = inputs;
let render_extent = targets.render_extent;
let post_support = crate::vulkan::post::PostSupport::new(device, frames)?;
let init_post_device = shared_post_device(gpu, &post_support, scene, descriptors);
let ssao = match post.ssao {
Some(settings) => Some(SsaoResources::new(
&init_post_device,
settings,
render_extent,
)?),
None => None,
};
let ssr = if post.ssr.is_some() || post.ssgi.is_some() || features.rt_wanted {
Some(crate::vulkan::post::ssr::SsrResources::new(
&init_post_device,
post.ssr,
render_extent,
)?)
} else {
None
};
let gbuffer = if features.gbuffer_enabled {
Some(crate::vulkan::post::gbuffer::GbufferResources::new(
crate::vulkan::post::gbuffer::GbufferDeviceCtx { alloc, device },
crate::vulkan::post::gbuffer::GbufferQueueCtx {
command_pool,
queue: hw.graphics_queue,
},
crate::vulkan::post::gbuffer::GbufferExtent {
width: render_extent.width,
height: render_extent.height,
frames,
},
&targets.transient_pool.gbuffer_pooled(frames),
hot_reload,
)?)
} else {
None
};
let ssgi = match post.ssgi {
Some(settings) => Some(crate::vulkan::post::ssgi::SsgiResources::new(
&init_post_device,
settings,
render_extent,
)?),
None => None,
};
Ok(ScreenSpace {
ssao,
post: post_support,
ssr,
gbuffer,
ssgi,
})
}
pub(super) struct SceneInputWiring<'a> {
pub(super) features: &'a Features,
pub(super) targets: &'a VkTargets,
pub(super) scene: &'a VkSceneAssets,
pub(super) descriptors: &'a VkDescriptors,
pub(super) composite: &'a CompositeState,
pub(super) screen: &'a ScreenSpace,
pub(super) upscale: Option<&'a dyn VkUpscaleBackend>,
}
pub(super) fn build_taa_and_wire_scene_inputs(
gpu: &InitGpu<'_>,
inputs: SceneInputWiring<'_>,
) -> RenderResult<Option<TaaResources>> {
let InitGpu { hw, frames, .. } = *gpu;
let device = &hw.device;
let SceneInputWiring {
features,
targets,
scene,
descriptors,
composite,
screen,
upscale,
} = inputs;
let init_post_device = shared_post_device(gpu, &screen.post, scene, descriptors);
let taa = if features.taa_enabled {
let taa = TaaResources::new(&init_post_device, frames, targets.render_extent)?;
for (i, &set) in composite.sets.iter().enumerate() {
write_composite_set(
device,
set,
taa.output_view(i),
composite_bloom_view(&targets.transient_pool, scene.ssao_white.view, i),
scene.color_lut.view,
);
}
Some(taa)
} else {
None
};
if let Some(up) = upscale {
let up_output_view = up.output().image().view;
for (i, &set) in composite.sets.iter().enumerate() {
write_composite_set(
device,
set,
up_output_view,
composite_bloom_view(&targets.transient_pool, scene.ssao_white.view, i),
scene.color_lut.view,
);
}
}
for (i, &set) in composite.sets.iter().enumerate() {
let channels =
composite_channels(targets, screen.gbuffer.as_ref(), scene.ssao_white.view, i);
write_composite_channel_set(device, set, &channels);
}
Ok(taa)
}
pub(super) struct WorldEffectInputs<'a> {
pub(super) targets: &'a VkTargets,
pub(super) descriptors: &'a VkDescriptors,
pub(super) fx: &'a WorldFx,
pub(super) planar: PlanarBudget,
pub(super) cull: &'a VkCull,
pub(super) n_cull: usize,
pub(super) rt: &'a RtResources,
pub(super) geometry: &'a VkGeometry,
pub(super) post: &'a PostSettings,
}
pub(super) struct WorldEffects {
pub(super) decal: DecalState,
pub(super) fog: FogState,
pub(super) raymarch: Option<RaymarchResources>,
pub(super) planar_reflection: Option<PlanarReflectionSet>,
pub(super) transparent: Option<TransparentResources>,
pub(super) auto_exposure: AutoExposureState,
}
pub(super) fn build_world_effects(
gpu: &InitGpu<'_>,
inputs: WorldEffectInputs<'_>,
bindings: &GlobalBindings<'_>,
) -> RenderResult<WorldEffects> {
let InitGpu {
hw,
command_pool,
frames,
hot_reload,
} = *gpu;
let (device, alloc, graphics_queue) = (&hw.device, &hw.alloc, hw.graphics_queue);
let WorldEffectInputs {
targets,
descriptors,
fx,
planar,
cull,
n_cull,
rt,
geometry,
post,
} = inputs;
let GlobalBindings {
uniforms,
shadow,
scene,
..
} = *bindings;
let (render_extent, msaa_samples) = (targets.render_extent, targets.msaa_samples);
let depth_views: Vec<vk::ImageView> = targets.depth_images.iter().map(|img| img.view).collect();
let hdr_resolve_views: Vec<vk::ImageView> = targets
.hdr_resolve_images
.iter()
.map(|img| img.view)
.collect();
let decals_state = Some(crate::vulkan::decal::DecalResources::new(
crate::vulkan::decal::DecalDeviceContext {
alloc,
device,
command_pool,
queue: graphics_queue,
},
crate::vulkan::decal::DecalPassTargets {
hdr_format: HDR_FORMAT,
hdr_resolve_views: &hdr_resolve_views,
depth_views: &depth_views,
sampler: scene.linear_sampler.handle(),
extent: render_extent,
},
frames,
msaa_samples != vk::SampleCountFlags::TYPE_1,
hot_reload,
)?);
let fog_resources = if fx.fog.is_some() {
Some(crate::vulkan::fog::FogResources::new(
crate::vulkan::fog::FogDeviceContext {
alloc,
device,
command_pool,
queue: graphics_queue,
},
crate::vulkan::fog::FogFrameTargets {
frames,
msaa: msaa_samples != vk::SampleCountFlags::TYPE_1,
hdr_format: HDR_FORMAT,
hdr_resolve_views: &hdr_resolve_views,
depth_views: &depth_views,
extent: render_extent,
},
crate::vulkan::fog::FogShadowResources {
ubos: &shadow.ubos,
map_view: shadow.map.view,
sampler: shadow.sampler.handle(),
},
hot_reload,
)?)
} else {
None
};
let raymarch = crate::vulkan::raymarch::RaymarchResources::try_new(
crate::vulkan::raymarch::RaymarchDeviceContext {
alloc,
device,
command_pool,
queue: graphics_queue,
},
crate::vulkan::raymarch::RaymarchTargetConfig {
frames,
msaa_samples,
width: render_extent.width,
height: render_extent.height,
},
crate::vulkan::raymarch::RaymarchSharedBindings {
shadow_map_view: shadow.map.view,
shadow_sampler: shadow.sampler.handle(),
irradiance_view: scene.env_map.irradiance.view,
prefilter_view: scene.env_map.prefilter.view,
cube_sampler: scene.cube_sampler.handle(),
linear_sampler: scene.linear_sampler.handle(),
light_ubos: &uniforms.light_ubo_buffers,
shadow_ubos: &shadow.ubos,
shadow_render_pass: shadow.render_pass.handle(),
},
&fx.sdf_volumes,
hot_reload,
)?;
let planar = PlanarReflectors::plan(&fx.water_surfaces, &fx.glass_panels, planar);
let planar_slots = planar.assignment.slots.clone();
if planar.overflow() > 0 {
tracing::warn!(
"planar reflection: {} reflector plane(s) exceed the budget and fall back \
to the box-projected probe cube",
planar.overflow()
);
}
let planar_reflection = if planar.planes().is_empty() {
None
} else if let Some(kernels) = cull.cull_kernels.as_ref() {
let cull_sources = crate::vulkan::planar::PlanarCullSources {
frame_object_buffers: &cull.object_buffers,
frame_draw_args_buffers: &cull.draw_args_buffers,
cull_set_layout: kernels.set_layout.handle(),
cull_count: n_cull,
hiz: cull
.hiz
.as_ref()
.map(|h| (h.read_set_layout.handle(), h.read_set_view())),
};
Some(crate::vulkan::planar::PlanarReflectionSet::new(
crate::vulkan::planar::PlanarDevice {
alloc,
device,
command_pool,
queue: graphics_queue,
},
crate::vulkan::planar::PlanarConfig {
frames,
sample_count: msaa_samples,
width: render_extent.width,
height: render_extent.height,
},
planar,
&targets.main_render_pass,
crate::vulkan::planar::PlanarGlobalSet {
layout: descriptors.global_set_layout.handle(),
bindings: *bindings,
},
cull_sources,
)?)
} else {
None
};
let planar_target_views: Vec<vk::ImageView> = planar_reflection
.as_ref()
.map(|s| (0..s.plane_count()).map(|i| s.target_view(i)).collect())
.unwrap_or_default();
let transparent = if fx.glass_panels.is_empty()
&& fx.water_surfaces.is_empty()
&& !rt.has_seethrough_meshes
{
None
} else {
let (scene_views, scene_images): (Vec<vk::ImageView>, Vec<vk::Image>) = (0..frames)
.map(|i| {
if let Some(c) = rt.composite.as_ref() {
(c.output().view(), c.output().image().image)
} else {
(
targets.hdr_resolve_images[i].view,
targets.hdr_resolve_images[i].image,
)
}
})
.unzip();
let transparent_depth_views: Vec<vk::ImageView> =
targets.depth_images.iter().map(|img| img.view).collect();
let reactive_mask_views: Vec<vk::ImageView> = targets
.reactive_mask_images
.iter()
.map(|img| img.view)
.collect();
let rt_inputs = rt.state.accel.as_ref().map(|a| {
let (geom_buffer, geom_size) = a.geom_table();
crate::vulkan::transparent::TransparentRtInputs {
tlas: a.tlas(),
geom_buffer,
geom_size,
deformed_verts: a.deformed_verts(),
skinned_indices: a.skinned_indices(),
}
});
let (water_planar_slots, glass_planar_slots) =
planar_slots.split_at(fx.water_surfaces.len());
Some(crate::vulkan::transparent::TransparentResources::new(
crate::vulkan::transparent::TransparentDeviceCtx {
alloc,
instance: &hw.instance,
device,
physical_device: hw.physical_device,
command_pool,
queue: graphics_queue,
},
crate::vulkan::transparent::TransparentBuildConfig {
frames,
msaa_samples,
width: render_extent.width,
height: render_extent.height,
global_set_layout: descriptors.global_set_layout.handle(),
hot_reload,
reflection_divisor: post.rt_reflections.map_or(1, |rt| rt.divisor),
},
crate::vulkan::transparent::TransparentSceneTargets {
scene_views: &scene_views,
scene_images: &scene_images,
reactive_mask_views: &reactive_mask_views,
depth_views: &transparent_depth_views,
sampler: scene.linear_sampler.handle(),
},
crate::vulkan::transparent::TransparentContent {
glass_panels: &fx.glass_panels,
glass_planar_slots,
water_surfaces: &fx.water_surfaces,
water_planar_slots,
planar_target_views: &planar_target_views,
seethrough_mesh_indices: &rt.seethrough_mesh_indices,
},
crate::vulkan::transparent::TransparentRtSetup {
rt_capable: hw.rt_capable,
vertex_buffer: geometry.vertex_buffer.buffer(),
index_buffer: geometry.index_buffer.buffer(),
rt_inputs,
bindless_set_layout: cull.bindless_set_layout.as_ref().map(|l| l.handle()),
bindless_pool_size: cull.bindless_pool_size,
},
)?)
};
let (auto_exposure, adaptation) = if let Some(settings) = post.auto_exposure.as_ref() {
let resources = crate::vulkan::auto_exposure::AutoExposureResources::new(
alloc,
device,
frames,
&hdr_resolve_views,
hot_reload,
)?;
let adaptation =
auto_exposure::ExposureAdaptation::new(*settings, post.auto_exposure_bias_ev);
(Some(resources), Some(adaptation))
} else {
(None, None)
};
Ok(WorldEffects {
decal: DecalState {
resources: decals_state,
set: decal::DecalSet::new(crate::vulkan::decal::MAX_DECALS, frames),
},
fog: FogState {
resources: fog_resources,
settings: fx.fog,
sun_dir: lights::sun_direction(&uniforms.light_uniforms),
sun_color: lights::sun_color(&uniforms.light_uniforms),
},
raymarch,
planar_reflection,
transparent,
auto_exposure: AutoExposureState {
resources: auto_exposure,
adaptation,
last_elapsed: 0.0,
},
})
}