use crate::components::{
GlassPanel, SdfVolume, ShaderPrograms, ShadowUpdate, UpscalerBackend, WaterSurface, Window,
};
use crate::gfx::auto_exposure::AutoExposureSettings;
use crate::gfx::mesh_payload::Vertex;
use crate::gfx::render_types::{
AreaLightData, DrawObject, GpuLight, InstancedCluster, LightUniforms, PostProcessTunables,
SpotShadowData,
};
use crate::gfx::rt_reflections::RtReflectionSettings;
use crate::gfx::ssao::SsaoSettings;
use crate::gfx::ssgi::SsgiSettings;
use crate::gfx::ssr::SsrSettings;
use crate::render::decal::DecalRecord;
use crate::render::particles::ParticleEmitterRecord;
use crate::render::rt_geom::RtDynamicMode;
use crate::render::volumetric_fog::FogSettings;
use alloc::string::String;
use alloc::vec;
use alloc::vec::Vec;
pub struct SceneData<'a> {
pub vertices: &'a [Vertex],
pub indices: &'a [u32],
pub draw_objects: Vec<DrawObject>,
pub instanced_clusters: Vec<InstancedCluster>,
pub n_skinned: usize,
pub n_chunk_max: usize,
}
#[derive(Clone, Copy)]
pub struct WorldShader<'a> {
pub programs: Option<&'a ShaderPrograms>,
pub deferred: bool,
}
pub struct MediaPayloads<'a> {
pub textures: &'a [crate::bake::texture::TextureImage],
pub text_atlases: Vec<(u32, u32, Vec<u8>)>,
pub env_map_bytes: Option<&'a [u8]>,
pub color_lut_bytes: Option<&'a [u8]>,
}
#[derive(Copy, Clone, Debug)]
pub struct ShadowParams {
pub map_size: u32,
pub update: ShadowUpdate,
pub distance: u32,
pub cascades: u32,
}
pub struct PostSettings {
pub post_process: PostProcessTunables,
pub taa_enabled: bool,
pub ssao: Option<SsaoSettings>,
pub ssr: Option<SsrSettings>,
pub ssgi: Option<SsgiSettings>,
pub rt_reflections: Option<RtReflectionSettings>,
pub rt_dynamic: RtDynamicMode,
pub rt_skinned_geometry: bool,
pub reflection_blur_scale: u32,
pub auto_exposure: Option<AutoExposureSettings>,
pub auto_exposure_bias_ev: f32,
pub hdr_display: bool,
pub hdr_pq: bool,
pub temporal_upscaling: bool,
pub upscale_scale: f32,
pub upscale_backend: UpscalerBackend,
pub occlusion_two_pass: bool,
}
pub struct WorldFx {
pub decals: Vec<DecalRecord>,
pub particles: Vec<ParticleEmitterRecord>,
pub fog: Option<FogSettings>,
pub water_surfaces: Vec<WaterSurface>,
pub glass_panels: Vec<GlassPanel>,
pub sdf_volumes: Vec<(SdfVolume, Vec<u8>, String)>,
}
pub struct BackendInit<'a> {
pub window: &'a Window,
pub validation: bool,
pub frames_in_flight: usize,
pub vsync: bool,
pub clear_color: [f32; 4],
pub hot_reload: bool,
pub capture: bool,
pub scene: SceneData<'a>,
pub shaders: Vec<WorldShader<'a>>,
pub media: MediaPayloads<'a>,
pub light_uniforms: LightUniforms,
pub local_lights: Vec<GpuLight>,
pub spot_shadows: Vec<SpotShadowData>,
pub area_lights: Vec<AreaLightData>,
pub shadows: ShadowParams,
pub anisotropy: u32,
pub planar_planes: usize,
pub post: PostSettings,
pub fx: WorldFx,
pub requirements: RenderRequirements,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
pub struct SwapchainConfig {
pub frames_in_flight: usize,
pub hdr_display: bool,
pub hdr_pq: bool,
}
#[derive(Copy, Clone, Debug)]
pub struct RenderRequirements {
pub scene: bool,
}
impl Default for RenderRequirements {
fn default() -> Self {
RenderRequirements { scene: true }
}
}
impl RenderRequirements {
pub fn derive(scene: &SceneData, fx: &WorldFx) -> Self {
let scene_present = !scene.vertices.is_empty()
|| !scene.draw_objects.is_empty()
|| !scene.instanced_clusters.is_empty()
|| scene.n_skinned > 0
|| scene.n_chunk_max > 0
|| !fx.water_surfaces.is_empty()
|| !fx.glass_panels.is_empty()
|| !fx.sdf_volumes.is_empty()
|| !fx.particles.is_empty()
|| !fx.decals.is_empty();
RenderRequirements {
scene: scene_present,
}
}
}
impl<'a> BackendInit<'a> {
pub fn minimal(window: &'a Window, text_atlases: Vec<(u32, u32, Vec<u8>)>) -> Self {
let mut init = Self {
window,
validation: false,
frames_in_flight: 2,
vsync: true,
clear_color: [0.0, 0.0, 0.0, 1.0],
hot_reload: false,
capture: false,
scene: SceneData {
vertices: &[],
indices: &[],
draw_objects: Vec::new(),
instanced_clusters: Vec::new(),
n_skinned: 0,
n_chunk_max: 0,
},
shaders: vec![WorldShader {
programs: None,
deferred: false,
}],
media: MediaPayloads {
textures: &[],
text_atlases,
env_map_bytes: None,
color_lut_bytes: None,
},
light_uniforms: LightUniforms::DEFAULT,
local_lights: Vec::new(),
spot_shadows: Vec::new(),
area_lights: Vec::new(),
shadows: ShadowParams {
map_size: 0,
update: ShadowUpdate::default(),
distance: 0,
cascades: 1,
},
anisotropy: 1,
planar_planes: 0,
post: PostSettings {
post_process: PostProcessTunables::DEFAULT,
taa_enabled: false,
ssao: None,
ssr: None,
ssgi: None,
rt_reflections: None,
rt_dynamic: RtDynamicMode::Auto,
rt_skinned_geometry: true,
reflection_blur_scale: 1,
auto_exposure: None,
auto_exposure_bias_ev: 0.0,
hdr_display: false,
hdr_pq: false,
temporal_upscaling: false,
upscale_scale: 1.0,
upscale_backend: UpscalerBackend::Auto,
occlusion_two_pass: false,
},
fx: WorldFx {
decals: Vec::new(),
particles: Vec::new(),
fog: None,
water_surfaces: Vec::new(),
glass_panels: Vec::new(),
sdf_volumes: Vec::new(),
},
requirements: Default::default(),
};
init.resolve_requirements();
init
}
pub fn swapchain_config(&self) -> SwapchainConfig {
SwapchainConfig {
frames_in_flight: self.frames_in_flight.max(1),
hdr_display: self.post.hdr_display,
hdr_pq: self.post.hdr_pq,
}
}
pub fn resolve_requirements(&mut self) {
let req = RenderRequirements::derive(&self.scene, &self.fx);
if !req.scene {
trim_scene_features(
&mut self.shadows,
&mut self.post,
&mut self.fx,
&mut self.planar_planes,
);
}
self.requirements = req;
}
}
fn trim_scene_features(
shadows: &mut ShadowParams,
post: &mut PostSettings,
fx: &mut WorldFx,
planar_planes: &mut usize,
) {
shadows.map_size = 0;
post.taa_enabled = false;
post.ssao = None;
post.ssr = None;
post.ssgi = None;
post.rt_reflections = None;
post.auto_exposure = None;
post.temporal_upscaling = false;
post.occlusion_two_pass = false;
fx.fog = None;
*planar_planes = 0;
}
#[cfg(test)]
mod tests {
use super::*;
fn empty_scene() -> SceneData<'static> {
SceneData {
vertices: &[],
indices: &[],
draw_objects: Vec::new(),
instanced_clusters: Vec::new(),
n_skinned: 0,
n_chunk_max: 0,
}
}
fn empty_fx() -> WorldFx {
WorldFx {
decals: Vec::new(),
particles: Vec::new(),
fog: None,
water_surfaces: Vec::new(),
glass_panels: Vec::new(),
sdf_volumes: Vec::new(),
}
}
fn full_post() -> PostSettings {
PostSettings {
post_process: PostProcessTunables::DEFAULT,
taa_enabled: true,
ssao: Some(SsaoSettings::resolve(0.5, 1.0)),
ssr: None,
ssgi: None,
rt_reflections: None,
rt_dynamic: RtDynamicMode::Auto,
rt_skinned_geometry: true,
reflection_blur_scale: 2,
auto_exposure: None,
auto_exposure_bias_ev: 0.0,
hdr_display: false,
hdr_pq: false,
temporal_upscaling: true,
upscale_scale: 0.5,
upscale_backend: UpscalerBackend::Auto,
occlusion_two_pass: true,
}
}
#[test]
fn text_only_world_derives_no_scene() {
let req = RenderRequirements::derive(&empty_scene(), &empty_fx());
assert!(!req.scene);
}
#[test]
fn minimal_carries_only_a_window_and_its_atlases() {
let window = Window::default();
let atlas = vec![(2u32, 2u32, vec![255u8; 2 * 2 * 4])];
let init = BackendInit::minimal(&window, atlas);
assert_eq!(init.media.text_atlases.len(), 1);
assert_eq!(init.shaders.len(), 1);
assert!(init.shaders[0].programs.is_none());
assert!(!init.shaders[0].deferred);
assert!(!init.requirements.scene);
assert_eq!(init.shadows.map_size, 0);
assert!(!init.post.taa_enabled);
assert!(init.post.ssao.is_none());
assert_eq!(init.planar_planes, 0);
}
#[test]
fn any_scene_content_derives_scene() {
let mut scene = empty_scene();
scene.n_skinned = 1;
assert!(RenderRequirements::derive(&scene, &empty_fx()).scene);
let mut scene = empty_scene();
scene.n_chunk_max = 8;
assert!(RenderRequirements::derive(&scene, &empty_fx()).scene);
let scene = empty_scene();
let mut fx = empty_fx();
fx.water_surfaces.push(WaterSurface::default());
assert!(RenderRequirements::derive(&scene, &fx).scene);
}
#[test]
fn sceneless_world_trims_scene_features() {
let mut shadows = ShadowParams {
map_size: 2048,
update: ShadowUpdate::default(),
distance: 120,
cascades: 4,
};
let mut post = full_post();
let mut fx = empty_fx();
let mut planar = 3usize;
trim_scene_features(&mut shadows, &mut post, &mut fx, &mut planar);
assert_eq!(shadows.map_size, 0);
assert!(!post.taa_enabled);
assert!(post.ssao.is_none());
assert!(!post.temporal_upscaling);
assert!(!post.occlusion_two_pass);
assert!(fx.fog.is_none());
assert_eq!(planar, 0);
}
#[test]
fn scene_world_keeps_settings() {
let mut scene = empty_scene();
scene.n_skinned = 2;
let fx = empty_fx();
let req = RenderRequirements::derive(&scene, &fx);
assert!(req.scene);
}
}