// ── Render Target Management ──────────────────────────────────────────────────
#[derive(Clone, Debug)]
pub struct RenderTargetSet {
pub id: u32,
pub name: String,
pub width: u32,
pub height: u32,
pub color_targets: Vec<u32>,
pub depth_target: Option<u32>,
pub msaa_samples: u32,
pub active: bool,
}
impl RenderTargetSet {
pub fn new(id: u32, name: impl Into<String>, w: u32, h: u32) -> Self {
Self { id, name: name.into(), width: w, height: h, color_targets: Vec::new(), depth_target: None, msaa_samples: 1, active: false }
}
pub fn add_color(&mut self, tex_id: u32) { self.color_targets.push(tex_id); }
pub fn set_depth(&mut self, tex_id: u32) { self.depth_target = Some(tex_id); }
pub fn enable(&mut self) { self.active = true; }
pub fn disable(&mut self) { self.active = false; }
pub fn color_count(&self) -> usize { self.color_targets.len() }
pub fn has_depth(&self) -> bool { self.depth_target.is_some() }
pub fn total_attachments(&self) -> usize { self.color_targets.len() + if self.has_depth() { 1 } else { 0 } }
pub fn aspect_ratio(&self) -> f32 { if self.height == 0 { 1.0 } else { self.width as f32 / self.height as f32 } }
pub fn is_multisampled(&self) -> bool { self.msaa_samples > 1 }
pub fn total_bytes(&self, bpp: u32) -> u64 { self.width as u64 * self.height as u64 * bpp as u64 * (self.color_targets.len() + 1) as u64 }
}
#[derive(Clone, Debug)]
pub struct RenderTargetManager {
pub sets: HashMap<u32, RenderTargetSet>,
pub active_set: Option<u32>,
pub next_id: u32,
pub backbuffer_id: u32,
}
impl RenderTargetManager {
pub fn new() -> Self { Self { sets: HashMap::new(), active_set: None, next_id: 1, backbuffer_id: 0 } }
pub fn create(&mut self, name: impl Into<String>, w: u32, h: u32) -> u32 {
let id = self.next_id; self.next_id += 1;
self.sets.insert(id, RenderTargetSet::new(id, name, w, h));
id
}
pub fn get(&self, id: u32) -> Option<&RenderTargetSet> { self.sets.get(&id) }
pub fn bind(&mut self, id: u32) { if let Some(s) = self.sets.get_mut(&id) { s.enable(); } self.active_set = Some(id); }
pub fn unbind(&mut self) { if let Some(id) = self.active_set { if let Some(s) = self.sets.get_mut(&id) { s.disable(); } } self.active_set = None; }
pub fn resize_all(&mut self, w: u32, h: u32) { for s in self.sets.values_mut() { s.width = w; s.height = h; } }
pub fn count(&self) -> usize { self.sets.len() }
pub fn active(&self) -> Option<&RenderTargetSet> { self.active_set.and_then(|id| self.sets.get(&id)) }
}
impl Default for RenderTargetManager { fn default() -> Self { Self::new() } }
// ── Frame Graph Executor ──────────────────────────────────────────────────────
#[derive(Clone, Debug)]
pub struct FrameGraphExecutor {
pub render_graph: RenderGraph,
pub rt_manager: RenderTargetManager,
pub frame: u64,
pub paused: bool,
}
impl FrameGraphExecutor {
pub fn new() -> Self { Self { render_graph: RenderGraph::new("frame"), rt_manager: RenderTargetManager::new(), frame: 0, paused: false } }
pub fn begin_frame(&mut self) { if !self.paused { self.frame += 1; } }
pub fn pause(&mut self) { self.paused = true; }
pub fn resume(&mut self) { self.paused = false; }
pub fn node_count(&self) -> usize { self.render_graph.node_count() }
pub fn is_running(&self) -> bool { !self.paused }
pub fn frame_number(&self) -> u64 { self.frame }
}
impl Default for FrameGraphExecutor { fn default() -> Self { Self::new() } }
// ── Shader Compiler Statistics ────────────────────────────────────────────────
#[derive(Clone, Debug, Default)]
pub struct ShaderCompilerStats {
pub total_programs: usize,
pub total_materials: usize,
pub total_textures: usize,
pub total_buffers: usize,
pub total_passes: usize,
pub total_post_effects: usize,
pub cache_entries: usize,
pub cache_hit_rate: f32,
pub total_compile_ms: f32,
pub failed_compiles: u32,
pub hot_reloads: u32,
}
impl ShaderCompilerStats {
pub fn from_workspace(ws: &ShaderWorkspace) -> Self {
Self {
total_programs: ws.program_count(),
total_materials: ws.material_count(),
total_textures: ws.texture_count(),
total_buffers: ws.buffer_count(),
total_passes: ws.pipeline.pass_count(),
total_post_effects: ws.post_process.effect_count(),
cache_entries: ws.cache.size(),
cache_hit_rate: ws.cache.hit_rate(),
..Default::default()
}
}
pub fn summary(&self) -> String {
format!("Programs:{} Materials:{} Textures:{} Buffers:{} Cache:{:.0}%", self.total_programs, self.total_materials, self.total_textures, self.total_buffers, self.cache_hit_rate * 100.0)
}
}
// ── Final module constants ────────────────────────────────────────────────────
pub const RENDER_TARGET_MAX: usize = 64;
pub const FRAME_GRAPH_MAX_NODES: usize = 128;
pub const RT_MAX_COLOR_ATTACHMENTS: usize = 8;
pub const RT_DEFAULT_MSAA: u32 = 1;
pub const SHADER_COMPILER_SESSION_MAX: usize = 4;
pub fn all_post_process_effect_names() -> &'static [&'static str] {
&["Bloom", "Tone Mapping", "SSAO", "SSR", "TAA", "Depth of Field", "Volumetric Fog", "Lens Flare", "Motion Blur", "Vignette", "Chromatic Aberration", "Film Grain", "FXAA", "Color Grading", "SMAA"]
}
pub fn shader_compiler_comprehensive_info() -> String {
format!(concat!(
"ShaderCompilerEditor v{} Summary:\n",
" Modules: {}\n",
" Render API targets: {}\n",
" Quality presets: {}\n",
" Post-process effects: {}\n",
" Builtin GLSL snippets: {}\n",
" Builtin GLSL functions: {}\n"
),
shader_system_version(),
shader_module_count(),
supported_render_apis().len(),
RENDER_QUALITY_PRESET_COUNT,
all_post_process_effect_names().len(),
SHADER_BUILTIN_SNIPPETS,
SHADER_GLSL_BUILTIN_FUNCTIONS)
}