use super::AcquiredSurface;
use super::PassMode;
use super::SavedUiState;
use super::font_atlas;
use super::passes;
use super::rendergraph;
impl super::WgpuRenderer {
pub(super) fn cleanup_unused_camera_viewports(&mut self, required_cameras: &[freecs::Entity]) {
self.camera_viewports
.retain(|entity, _| required_cameras.contains(entity));
}
pub(super) fn extract_frame_dirty_state(&mut self, world: &mut crate::ecs::world::World) {
use crate::ecs::prefab::resources::{mesh_cache_take_dirty, mesh_cache_take_dirty_skinned};
let frame_state = world.resources.mesh_render_state.take_frame_state();
let dirty_meshes = mesh_cache_take_dirty(&mut world.resources.mesh_cache);
let dirty_skinned = mesh_cache_take_dirty_skinned(&mut world.resources.mesh_cache);
if let Some(mesh_pass) = self.graph.get_pass_mut("mesh_pass")
&& let Some(pass) =
(mesh_pass as &mut dyn std::any::Any).downcast_mut::<passes::MeshPass>()
{
pass.frame_dirty = Some(frame_state);
pass.dirty_mesh_names = dirty_meshes;
}
if let Some(skinned_pass) = self.graph.get_pass_mut("skinned_mesh_pass")
&& let Some(pass) = (skinned_pass as &mut dyn std::any::Any)
.downcast_mut::<passes::geometry::SkinnedMeshPass>()
{
pass.dirty_skinned_mesh_names = dirty_skinned;
}
}
pub(super) fn resize_render_buffers(&mut self, width: u32, height: u32) {
if self.render_buffer_size == (width, height) {
return;
}
let width = width.max(1);
let height = height.max(1);
let (old_width, old_height) = self.render_buffer_size;
let _ = self
.graph
.resize_all_transient_proportional(old_width, old_height, width, height);
if let Some(mesh_pass) = self.graph.get_pass_mut("mesh_pass")
&& let Some(mesh_pass) =
(mesh_pass as &mut dyn std::any::Any).downcast_mut::<passes::MeshPass>()
{
mesh_pass.resize(&self.device, width, height);
}
if let Some(bloom_pass) = self.graph.get_pass_mut("bloom_pass")
&& let Some(bloom_pass) =
(bloom_pass as &mut dyn std::any::Any).downcast_mut::<passes::BloomPass>()
{
bloom_pass.resize(&self.device, &self.queue, width, height);
}
if let Some(outline_pass) = self.graph.get_pass_mut("outline_pass")
&& let Some(outline_pass) =
(outline_pass as &mut dyn std::any::Any).downcast_mut::<passes::OutlinePass>()
{
outline_pass.resize(width, height);
}
self.render_buffer_size = (width, height);
}
pub(super) fn acquire_secondary_surface_texture(
&self,
id: usize,
) -> Result<Option<AcquiredSurface>, Box<dyn std::error::Error>> {
let Some(secondary) = self.secondary_surfaces.get(&id) else {
return Ok(None);
};
let width = secondary.width;
let height = secondary.height;
let surface_texture = match secondary.surface.get_current_texture() {
wgpu::CurrentSurfaceTexture::Success(frame)
| wgpu::CurrentSurfaceTexture::Suboptimal(frame) => frame,
wgpu::CurrentSurfaceTexture::Outdated => {
secondary.surface.configure(&self.device, &secondary.config);
match secondary.surface.get_current_texture() {
wgpu::CurrentSurfaceTexture::Success(frame)
| wgpu::CurrentSurfaceTexture::Suboptimal(frame) => frame,
other => {
panic!("Failed to get surface texture after reconfiguration: {other:?}")
}
}
}
_ => {
return Ok(None);
}
};
let view = surface_texture
.texture
.create_view(&wgpu::TextureViewDescriptor::default());
Ok(Some((surface_texture, view, width, height)))
}
pub(super) fn swap_secondary_ui_buffers(
&self,
world: &mut crate::ecs::world::World,
id: usize,
width: u32,
height: u32,
) -> SavedUiState {
let cached_viewport_size = world.resources.window.cached_viewport_size;
let render_buffer_size = self.render_buffer_size;
world.resources.window.cached_viewport_size = Some((width, height));
if let Some(buffers) = world.resources.retained_ui.secondary_buffers.get_mut(&id) {
let frame_rects = std::mem::replace(
&mut world.resources.retained_ui.frame_rects,
std::mem::take(&mut buffers.frame_rects),
);
let frame_ui_images = std::mem::replace(
&mut world.resources.retained_ui.frame_ui_images,
std::mem::take(&mut buffers.frame_ui_images),
);
let frame_text_meshes = std::mem::replace(
&mut world.resources.retained_ui.frame_text_meshes,
std::mem::take(&mut buffers.frame_text_meshes),
);
SavedUiState {
did_swap: true,
frame_rects,
frame_ui_images,
frame_text_meshes,
cached_viewport_size,
render_buffer_size,
}
} else {
SavedUiState {
did_swap: false,
frame_rects: Vec::new(),
frame_ui_images: Vec::new(),
frame_text_meshes: Vec::new(),
cached_viewport_size,
render_buffer_size,
}
}
}
pub(super) fn restore_secondary_ui_buffers(
&mut self,
world: &mut crate::ecs::world::World,
saved: SavedUiState,
) {
if saved.did_swap {
world.resources.retained_ui.frame_rects = saved.frame_rects;
world.resources.retained_ui.frame_ui_images = saved.frame_ui_images;
world.resources.retained_ui.frame_text_meshes = saved.frame_text_meshes;
}
world.resources.window.cached_viewport_size = saved.cached_viewport_size;
self.resize_render_buffers(saved.render_buffer_size.0, saved.render_buffer_size.1);
}
pub(super) fn execute_to_external_target(
&mut self,
world: &mut crate::ecs::world::World,
target_texture: Option<&wgpu::Texture>,
target_view: &wgpu::TextureView,
width: u32,
height: u32,
pass_mode: PassMode,
) -> Result<(), rendergraph::RenderGraphError> {
let saved_states = self.graph.save_pass_enabled_states();
let has_culling_override = matches!(pass_mode, PassMode::World { .. })
&& world.resources.graphics.culling_camera_override.is_some();
if has_culling_override
&& let Some(mesh_pass) = self.graph.get_pass_mut("mesh_pass")
&& let Some(mesh_pass) =
(mesh_pass as &mut dyn std::any::Any).downcast_mut::<passes::MeshPass>()
{
mesh_pass.preserve_hiz = true;
}
self.resize_render_buffers(width, height);
if matches!(pass_mode, PassMode::World { .. })
&& let Some(lines_pass) = self.graph.get_pass_mut("lines_pass")
&& let Some(lines_pass) =
(lines_pass as &mut dyn std::any::Any).downcast_mut::<passes::LinesPass>()
{
passes::sync_lines_data(lines_pass, &self.device, &self.queue, world);
}
match pass_mode {
PassMode::World { enable_ui } => {
let _ = self.graph.set_pass_enabled("egui_pass", false);
let _ = self.graph.set_pass_enabled("viewport_blit_pass", false);
if !enable_ui {
let _ = self.graph.set_pass_enabled("ui_pass", false);
let _ = self.graph.set_pass_enabled("ui_image_pass", false);
}
let graphics = &world.resources.graphics;
let _ = self
.graph
.set_pass_enabled("bloom_pass", graphics.bloom_enabled);
let _ = self
.graph
.set_pass_enabled("ssao_pass", graphics.ssao_enabled);
let _ = self
.graph
.set_pass_enabled("ssao_blur_pass", graphics.ssao_enabled);
let _ = self
.graph
.set_pass_enabled("ssr_pass", graphics.ssr_enabled);
let _ = self
.graph
.set_pass_enabled("ssr_blur_pass", graphics.ssr_enabled);
let _ = self
.graph
.set_pass_enabled("ssgi_pass", graphics.ssgi_enabled);
let _ = self
.graph
.set_pass_enabled("ssgi_blur_pass", graphics.ssgi_enabled);
}
PassMode::UiOnly => {
for name in saved_states.keys() {
let _ = self.graph.set_pass_enabled(name, false);
}
let _ = self.graph.set_pass_enabled("ui_pass", true);
let _ = self.graph.set_pass_enabled("ui_image_pass", true);
}
}
self.graph.set_external_texture(
self.swapchain_id,
target_texture.cloned(),
target_view.clone(),
width,
height,
);
if matches!(pass_mode, PassMode::World { .. }) {
self.extract_frame_dirty_state(world);
}
let execute_result = self.graph.execute(&self.device, &self.queue, world);
let execute_error = match execute_result {
Ok(command_buffers) => {
self.queue.submit(command_buffers);
None
}
Err(error) => Some(error),
};
self.graph.restore_pass_enabled_states(&saved_states);
if has_culling_override
&& let Some(mesh_pass) = self.graph.get_pass_mut("mesh_pass")
&& let Some(mesh_pass) =
(mesh_pass as &mut dyn std::any::Any).downcast_mut::<passes::MeshPass>()
{
mesh_pass.preserve_hiz = false;
}
match execute_error {
Some(error) => Err(error),
None => Ok(()),
}
}
pub fn render_world_to_texture(
&mut self,
world: &mut crate::ecs::world::World,
target_texture: Option<&wgpu::Texture>,
target_view: &wgpu::TextureView,
width: u32,
height: u32,
) -> Result<(), Box<dyn std::error::Error>> {
self.execute_to_external_target(
world,
target_texture,
target_view,
width,
height,
PassMode::World { enable_ui: false },
)?;
Ok(())
}
pub fn device(&self) -> &wgpu::Device {
&self.device
}
pub fn queue(&self) -> &wgpu::Queue {
&self.queue
}
pub fn surface_format(&self) -> wgpu::TextureFormat {
self.surface_format
}
pub fn surface_size(&self) -> (u32, u32) {
(self.surface_config.width, self.surface_config.height)
}
pub(super) fn render_to_secondary_surface(
&mut self,
id: usize,
world: &mut crate::ecs::world::World,
pass_mode: PassMode,
) -> Result<(), Box<dyn std::error::Error>> {
let Some((surface_texture, view, width, height)) =
self.acquire_secondary_surface_texture(id)?
else {
return Ok(());
};
let saved = self.swap_secondary_ui_buffers(world, id, width, height);
let result = self.execute_to_external_target(
world,
Some(&surface_texture.texture),
&view,
width,
height,
pass_mode,
);
self.restore_secondary_ui_buffers(world, saved);
result?;
surface_texture.present();
Ok(())
}
pub fn cleanup_world(&mut self, world_id: u64) {
if let Some(mesh_pass) = self.graph.get_pass_mut("mesh_pass")
&& let Some(mesh_pass) =
(mesh_pass as &mut dyn std::any::Any).downcast_mut::<passes::MeshPass>()
{
mesh_pass.cleanup_world_state(world_id);
}
}
pub fn cleanup_stale_worlds(&mut self, max_age_frames: u64) {
if let Some(mesh_pass) = self.graph.get_pass_mut("mesh_pass")
&& let Some(mesh_pass) =
(mesh_pass as &mut dyn std::any::Any).downcast_mut::<passes::MeshPass>()
{
mesh_pass.cleanup_stale_world_states(max_age_frames);
}
}
pub(super) fn regenerate_bitmap_atlases(
&mut self,
new_scale: f32,
new_subpixel: bool,
world: &mut crate::ecs::world::World,
) {
self.bitmap_font_textures.clear();
self.bitmap_font_data.clear();
world.resources.text_cache.font_manager.clear_bitmap_fonts();
for (source_data, base_size) in &self.bitmap_font_sources {
let atlas = match font_atlas::generate_font_atlas_bitmap(
&self.device,
&self.queue,
source_data,
*base_size,
new_scale,
new_subpixel,
) {
Ok(atlas) => atlas,
Err(error) => {
tracing::error!("Failed to regenerate bitmap font atlas: {}", error);
continue;
}
};
self.bitmap_font_textures
.push((atlas.texture, atlas.texture_view));
let texture_index = (self.bitmap_font_textures.len() - 1) as u32;
let atlas_data = crate::ecs::text::resources::FontAtlasData {
texture_index,
glyphs: atlas.glyphs,
kerning: atlas.kerning,
width: atlas.width,
height: atlas.height,
font_size: atlas.font_size,
sdf_range: atlas.sdf_range,
};
self.bitmap_font_data.push(atlas_data.clone());
world
.resources
.text_cache
.font_manager
.add_bitmap_font(atlas_data);
}
if let Some(ui_pass) = self.graph.get_pass_mut("ui_pass")
&& let Some(ui_pass) =
(ui_pass as &mut dyn std::any::Any).downcast_mut::<passes::UiPass>()
{
ui_pass.update_texture_bind_groups(&self.device, &self.bitmap_font_textures);
}
world.resources.retained_ui.text_mesh_cache.clear();
self.bitmap_atlas_scale = new_scale;
self.bitmap_atlas_subpixel = new_subpixel;
}
}