use concinnity_core::gfx::render_types::{FALLBACK_TEXTURE_COUNT, PostProcessParams};
use concinnity_core::render::backend_init::{self, BackendInit, PostSettings, WorldShader};
use concinnity_core::render::error::{RenderError, RenderResult};
use concinnity_core::render::scene_state::{DrawList, SceneState};
use self::heap_layout::{RtvHeapLayout, SrvHeapParams};
use super::context::*;
use super::geometry_upload::GeometryUploads;
use super::hot_reload::HotReloadState;
use super::resources::skinning::SkinnedState;
mod adapter;
mod bloom;
mod bootstrap;
mod commands;
mod composite;
mod cull;
mod descriptors;
mod effects;
pub(in crate::directx) mod heap_layout;
mod heaps;
pub(in crate::directx) mod pipelines;
mod ray_tracing;
mod scene_assets;
mod scene_data;
mod shadow;
mod targets;
mod text;
pub(in crate::directx) const HIZ_MAX_MIPS: usize = 15;
struct InitGpu<'a> {
hw: &'a DxHardware,
hot_reload: bool,
}
struct Features {
taa_enabled: bool,
msaa_samples: u32,
post_process: PostProcessParams,
ssao_enabled: bool,
gbuffer_enabled: bool,
}
impl Features {
fn resolve(hw: &DxHardware, post: &PostSettings) -> Self {
let taa_enabled = post.taa_enabled || post.temporal_upscaling;
let msaa_samples = bootstrap::resolve_sample_count(
bootstrap::query_msaa_samples(&hw.device),
post.hdr_samples,
);
let post_process = hw.hdr_mode.post_process_params(post.post_process);
let rt_enabled = post.rt_reflections.is_some() && hw.rt_capable;
if post.rt_reflections.is_some() && !hw.rt_capable {
tracing::warn!(
"ray_traced_reflections requested but the GPU does not report DXR \
tier 1.1; falling back to screen-space reflections"
);
}
let ssao_enabled = post.ssao.is_some();
Self {
taa_enabled,
msaa_samples,
post_process,
ssao_enabled,
gbuffer_enabled: taa_enabled
|| ssao_enabled
|| post.ssr.is_some()
|| post.ssgi.is_some()
|| rt_enabled,
}
}
}
impl DxContext {
pub(crate) fn new(init: BackendInit<'_>) -> RenderResult<Self> {
Self::build(init, None)
}
fn build(
init: BackendInit<'_>,
reuse: Option<(DxHardware, bootstrap::DxgiSwapchain)>,
) -> 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()))?;
let output = (window.width, window.height);
super::context::record_main_thread();
let swapchain_config = backend_init::SwapchainConfig {
frames_in_flight: frames_in_flight.max(1),
hdr_display: post.hdr_display,
hdr_pq: post.hdr_pq,
};
let (hw, swapchain) = match reuse {
Some(reuse) => reuse,
None => bootstrap::setup(
bootstrap::WindowConfig {
title: window.title.as_str(),
width: output.0,
height: output.1,
title_bar: window.title_bar,
},
validation,
vsync,
swapchain_config,
)?,
};
let features = Features::resolve(&hw, &post);
super::pso_library::install(&hw.device, hw.adapter.as_ref());
let gpu = InitGpu {
hw: &hw,
hot_reload,
};
let planar = effects::plan_planar(&fx, planar);
let planar_slots = planar.assignment.slots.clone();
let rtv = RtvHeapLayout::compute(features.msaa_samples);
let swapchain = heaps::build_swapchain(&gpu, swapchain, &rtv, vsync)?;
let descriptors = descriptors::build_descriptors(
&gpu,
&SrvHeapParams {
n_atlases: media.text_atlases.len(),
gbuffer_srv_extra: heap_layout::GBUFFER_TARGETS,
rt_output_srv_extra: heap_layout::RT_OUTPUT_TARGETS,
planar_resolve_srv_extra: planar.planes().len(),
albedo_count: media.textures.len().max(1),
normal_count: FALLBACK_TEXTURE_COUNT,
},
anisotropy,
)?;
let upscale = effects::build_upscale(&gpu, &descriptors, output, &post)?;
let scene =
scene_assets::build_scene_assets(&gpu, &descriptors, &media, &area_lights, &world)?;
let probe_gpu =
crate::directx::probe_set::ProbeSetGpu::new(&gpu.hw.device, &gpu.hw.alloc, FRAMES)?;
probe_gpu.stand_in.write_srv(
&gpu.hw.device,
descriptors.slot_cpu(descriptors.layout.probe_cubes_srv_slot),
);
let targets = targets::build_targets(
&gpu,
targets::TargetInputs {
descriptors: &descriptors,
swapchain: &swapchain,
rtv: &rtv,
upscale: &upscale,
features: &features,
output,
clear_color,
},
)?;
let uniforms = scene_data::build_uniforms(&gpu, light_uniforms, &local_lights)?;
let light_cull = scene_data::build_light_cull(&gpu)?;
let shadow = shadow::build_shadow(
&gpu,
&descriptors,
&targets,
&shadows,
&uniforms.light_uniforms,
)?;
let spot_shadow = shadow::build_spot_shadow(
&gpu,
&descriptors,
&targets,
&spot_shadows,
shadows.map_size,
)?;
let plan = cull::plan_cull(&world);
let cull = cull::build_cull(
&gpu,
cull::CullInputs {
world: &world,
world_shaders: &world_shaders,
plan: &plan,
descriptors: &descriptors,
targets: &targets,
albedo_count: scene.textures.len(),
shadow_enabled: shadow.map_size > 0,
spot_slices: spot_shadow.count() as usize,
gbuffer_enabled: features.gbuffer_enabled,
occlusion_two_pass: post.occlusion_two_pass,
},
)?;
let probe_prefilter = cull::build_probe_prefilter(&gpu, &cull)?;
let text = text::build_text(&gpu, &descriptors, &media, swapchain.format)?;
let composite = composite::build_composite(&gpu, swapchain.format)?;
let post_descriptors = descriptors::build_post_descriptors(&descriptors, &swapchain, &rtv);
let quality_slots =
effects::build_quality_slots(&gpu, &descriptors, &swapchain, &targets, &rtv);
let post_device = effects::post_device(&gpu, &descriptors, &post_descriptors);
let reflection_composite =
ray_tracing::build_reflection_composite(&post_device, &targets, &post)?;
let bloom = bloom::build_bloom(&post_device, &targets, output)?;
let taa = effects::build_taa(&post_device, &features, &targets)?;
let ssao = effects::build_ssao(&gpu, &post_device, &descriptors, &targets, post.ssao)?;
let ssr = effects::build_ssr(&post_device, &targets, &post)?;
let ssgi = effects::build_ssgi(&post_device, &targets, &post)?;
let rt_reflections =
ray_tracing::build_rt_reflections(&gpu, &quality_slots, &targets, &post);
let gbuffer =
effects::build_gbuffer(&gpu, &targets, &quality_slots, features.gbuffer_enabled)?;
let decal = effects::build_decals(&gpu, &descriptors, &targets, &scene, fx.decals)?;
let fog = effects::build_fog(
&gpu,
&descriptors,
&targets,
&shadow,
fx.fog,
&uniforms.light_uniforms,
)?;
let particle =
effects::build_particles(&gpu, &descriptors, &targets, &scene, fx.particles)?;
let commands = commands::build_commands(&gpu)?;
let frame_sync = commands::build_frame_sync(&gpu)?;
let timestamps = commands::build_timestamps(&gpu);
let auto_exposure = effects::build_auto_exposure(&gpu, &post)?;
let raymarch = effects::build_raymarch(
&gpu,
&descriptors,
&targets,
&shadow,
&scene,
&fx.sdf_volumes,
)?;
let sky = super::sky::DxSky::build(
&gpu.hw.device,
features.msaa_samples,
media.env_map_background,
hot_reload,
)?;
let planar_reflection = effects::build_planar_reflection(
&gpu,
&descriptors,
&targets,
planar,
plan.n_cull,
clear_color,
)?;
let transparent = effects::build_transparent(
&gpu,
effects::TransparentInputs {
descriptors: &descriptors,
targets: &targets,
reflection_slots: quality_slots.glass_reflection,
reflection_divisor: post.rt_reflections.map_or(1, |rt| rt.divisor),
planar_slots: &planar_slots,
glass_panels: &fx.glass_panels,
water_surfaces: &fx.water_surfaces,
draw_objects: &world.draw_objects,
},
)?;
let rt = ray_tracing::build_ray_tracing(
&gpu,
ray_tracing::RtInputs {
world: &world,
scene: &scene,
reflections: rt_reflections.as_ref(),
transparent: transparent.as_ref(),
post: &post,
},
);
crate::shader::cache::report_init();
super::pso_library::serialize();
crate::shader::pipeline_cache::report_init(super::pso_library::disk_state());
crate::shader::runtime_cache::checkpoint();
let pooled = hw.alloc.stats();
tracing::info!(
"device allocator: {} heap(s), {} KiB reserved for {} KiB of resources",
pooled.block_count,
pooled.reserved_bytes / 1024,
pooled.in_use_bytes / 1024,
);
Ok(Self {
post: post_descriptors,
swapchain,
targets,
upscale,
shadow,
spot_shadow,
scene,
descriptors,
geometry_uploads: std::cell::RefCell::new(GeometryUploads::new(FRAMES)),
skinned: SkinnedState::new(),
uniforms,
light_cull,
cull,
text,
composite,
bloom: Some(bloom),
post_process: features.post_process,
gbuffer,
taa,
ssao,
ssr,
ssgi,
reflection_composite,
rt_reflections,
rt,
decal,
lines: super::line::LineState::empty(),
raymarch,
sky,
transparent,
planar_reflection,
fog,
particle,
commands,
frame_sync,
current_frame: 0,
stream: StreamState::new(),
state: SceneState::new(
DrawList::with_runtime_reserve(
world.draw_objects,
plan.n_instances,
world.n_chunk_max,
),
clear_color,
),
graph_cache: std::cell::RefCell::new(None),
instanced: DxInstanced::new(world.instanced_clusters),
wireframe: Default::default(),
diagnostics: Default::default(),
timestamps,
auto_exposure,
hot_reload: HotReloadState::new(hot_reload),
world_shader: world_programs.cloned(),
quality_slots,
probe: ProbeState::new(probe_prefilter, probe_gpu),
hw,
})
}
}
impl DxContext {
pub(in crate::directx) fn apply_world_reload(
&mut self,
init: BackendInit<'_>,
) -> RenderResult<()> {
self.wait_idle();
let swapchain = bootstrap::DxgiSwapchain {
handle: self.swapchain.handle.clone(),
format: self.swapchain.format,
allow_tearing: self.swapchain.allow_tearing,
};
let reuse = (self.hw.hand_over()?, swapchain);
*self = DxContext::build(init, Some(reuse))?;
Ok(())
}
}