use super::passes;
use crate::render::wgpu::rendergraph::{
render_graph_get_pass_mut, render_graph_resize_all_transient_proportional,
};
impl super::WgpuRenderer {
pub(super) fn cleanup_unused_camera_viewports(
&mut self,
active_camera_entities: &std::collections::HashSet<freecs::Entity>,
) {
self.camera_viewports
.retain(|entity, _| active_camera_entities.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.assets.mesh_cache);
let dirty_skinned = mesh_cache_take_dirty_skinned(&mut world.resources.assets.mesh_cache);
if let Some(mesh_pass) = render_graph_get_pass_mut(&mut self.graph, "mesh_pass")
&& let Some(pass) =
(mesh_pass as &mut dyn std::any::Any).downcast_mut::<passes::MeshPass>()
{
pass.frame_dirty = Some(frame_state);
let dirty_ids: std::collections::HashSet<u32> = dirty_meshes
.iter()
.filter_map(|name| pass.mesh_id_for_name(name))
.collect();
pass.dirty_mesh_ids = dirty_ids;
}
if let Some(skinned_pass) = render_graph_get_pass_mut(&mut self.graph, "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_for_window(&mut self, width: u32, height: u32) {
let already_dispatched_at_this_size = self
.window_render_state
.recent_buffer_sizes
.contains(&(width, height));
if already_dispatched_at_this_size && self.render_buffer_size == (width, height) {
return;
}
if !already_dispatched_at_this_size {
self.window_render_state
.recent_buffer_sizes
.push((width, height));
if self.window_render_state.recent_buffer_sizes.len()
> super::RECENT_BUFFER_SIZE_CAPACITY
{
self.window_render_state.recent_buffer_sizes.remove(0);
}
}
self.resize_render_buffers(width, height);
}
pub(super) fn record_window_dispatch(&mut self, settings_version: u64) {
self.window_render_state.last_settings_version = Some(settings_version);
}
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 _ = render_graph_resize_all_transient_proportional(
&mut self.graph,
old_width,
old_height,
width,
height,
);
if let Some(mesh_pass) = render_graph_get_pass_mut(&mut self.graph, "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) = render_graph_get_pass_mut(&mut self.graph, "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);
}
#[cfg(feature = "debug_render")]
if let Some(outline_pass) = render_graph_get_pass_mut(&mut self.graph, "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 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 fn cleanup_world(&mut self, world_id: u64) {
if let Some(mesh_pass) = render_graph_get_pass_mut(&mut self.graph, "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) = render_graph_get_pass_mut(&mut self.graph, "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);
}
}
}
fn compute_ui_text_signature(text_instance: &crate::ecs::ui::types::UiTextInstance) -> u64 {
use std::hash::{Hash, Hasher};
let mut hasher = std::collections::hash_map::DefaultHasher::new();
text_instance.text.hash(&mut hasher);
text_instance.font_size.to_bits().hash(&mut hasher);
(text_instance.alignment as u8).hash(&mut hasher);
(text_instance.vertical_alignment as u8).hash(&mut hasher);
text_instance.monospace.hash(&mut hasher);
text_instance
.wrap_width
.map(|w| w.to_bits())
.unwrap_or(u32::MAX)
.hash(&mut hasher);
hasher.finish()
}
impl super::WgpuRenderer {
pub(super) fn prepare_text_meshes(&mut self, world: &mut crate::ecs::world::World) {
let pre_revision = self.glyph_atlas.revision;
let mut dirty_entries: Vec<(
freecs::Entity,
usize,
crate::ecs::text::components::TextProperties,
)> = Vec::new();
world
.core
.query()
.with(crate::ecs::world::TEXT)
.iter(|entity, table, idx| {
let text = &table.text[idx];
if text.dirty {
dirty_entries.push((entity, text.text_index, text.properties.clone()));
}
});
for (entity, text_index, properties) in dirty_entries {
let text_content = match world.resources.text.cache.get_text(text_index) {
Some(t) => t.to_string(),
None => continue,
};
let mesh = crate::render::wgpu::text_mesh::generate_text_mesh_world(
&text_content,
&mut crate::render::wgpu::text_mesh::TextMeshContext {
font_engine: &mut world.resources.text.font_engine,
glyph_atlas: &mut self.glyph_atlas,
device: &self.device,
queue: &self.queue,
},
&properties,
);
if let Some(text) = world.core.get_text_mut(entity) {
text.cached_mesh = Some(mesh);
text.dirty = false;
}
}
let mut frame_text = std::mem::take(&mut world.resources.retained_ui.frame.text_meshes);
let mut active_entities: std::collections::HashSet<freecs::Entity> =
std::collections::HashSet::new();
for instance in &mut frame_text {
let signature = compute_ui_text_signature(instance);
let cached = instance
.entity
.and_then(|e| self.text_mesh_signatures.get(&e).copied());
if cached == Some(signature) {
if let Some(entity) = instance.entity {
active_entities.insert(entity);
}
continue;
}
let properties = crate::ecs::text::components::TextProperties {
font_size: instance.font_size,
color: instance.color,
alignment: instance.alignment,
vertical_alignment: instance.vertical_alignment,
line_height: 1.2,
outline_color: instance.outline_color,
outline_width: instance.outline_width,
monospace_width: if instance.monospace { Some(0.0) } else { None },
font_kind: instance.font_kind,
..crate::ecs::text::components::TextProperties::default()
};
instance.mesh = crate::render::wgpu::text_mesh::generate_text_mesh_ui(
&instance.text,
&mut crate::render::wgpu::text_mesh::TextMeshContext {
font_engine: &mut world.resources.text.font_engine,
glyph_atlas: &mut self.glyph_atlas,
device: &self.device,
queue: &self.queue,
},
&properties,
instance.wrap_width,
);
if let Some(entity) = instance.entity {
self.text_mesh_signatures.insert(entity, signature);
active_entities.insert(entity);
}
}
let stale: Vec<freecs::Entity> = self
.text_mesh_signatures
.keys()
.copied()
.filter(|e| !active_entities.contains(e))
.collect();
for entity in stale {
self.text_mesh_signatures.remove(&entity);
}
world.resources.retained_ui.frame.text_meshes = frame_text;
if self.glyph_atlas.revision != pre_revision {
if let Some(text_pass) = render_graph_get_pass_mut(&mut self.graph, "text_pass")
&& let Some(text_pass) =
(text_pass as &mut dyn std::any::Any).downcast_mut::<passes::TextPass>()
{
text_pass.update_glyph_atlas(&self.device, &self.glyph_atlas.texture_view);
}
if let Some(ui_pass) = render_graph_get_pass_mut(&mut self.graph, "ui_pass")
&& let Some(ui_pass) =
(ui_pass as &mut dyn std::any::Any).downcast_mut::<passes::UiPass>()
{
ui_pass.update_glyph_atlas(&self.device, &self.glyph_atlas.texture_view);
}
}
}
}