[TSM.ID].[11031972] PXE : 10 Template/Kosong -> REAL Implementation (3Z Complete)

This commit is contained in:
TSM.ID
2026-05-25 05:10:47 +07:00
parent 9e5f7c78a9
commit c3b4acd7c1
10 changed files with 1180 additions and 457 deletions
+102 -81
View File
@@ -1,104 +1,125 @@
//! [TSM.ID].[11031972] — Platform X Ecosystem
//! xcu-render-pipeline — GPU render pipeline with DAG pass system
#![deny(warnings)]
//! [TSM.ID].[11031972] -- Platform X Ecosystem
//! xcu-render-pipeline -- GPU-agnostic Render Graph (DAG-based)
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
#[derive(Debug)]
pub enum XcuError {
InitFailed(String),
InvalidConfig(String),
OperationFailed(String),
ResourceExhausted,
NotFound(String),
Timeout,
}
impl std::fmt::Display for XcuError {
pub enum RenderError { CyclicGraph(String), PassFailed(String), ResourceMissing(String) }
impl std::fmt::Display for RenderError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::InitFailed(e) => write!(f, "Init failed: {e}"),
Self::InvalidConfig(e) => write!(f, "Invalid config: {e}"),
Self::OperationFailed(e) => write!(f, "Operation failed: {e}"),
Self::ResourceExhausted => write!(f, "Resource exhausted"),
Self::NotFound(e) => write!(f, "Not found: {e}"),
Self::Timeout => write!(f, "Operation timed out"),
match self { Self::CyclicGraph(e) => write!(f, "Cycle: {e}"), Self::PassFailed(e) => write!(f, "Pass: {e}"), Self::ResourceMissing(e) => write!(f, "Missing: {e}") }
}
}
impl std::error::Error for RenderError {}
#[derive(Debug, Clone)]
pub enum GpuBackend { Vulkan, Metal, WebGpu, OpenGl, Software }
#[derive(Debug, Clone)]
pub struct RenderPass { pub name: String, pub inputs: Vec<String>, pub outputs: Vec<String>, pub shader: String }
#[derive(Debug, Clone)]
pub struct RenderResource { pub name: String, pub width: u32, pub height: u32, pub format: String, pub size_bytes: u64 }
pub struct RenderGraph {
passes: Vec<RenderPass>,
resources: HashMap<String, RenderResource>,
execution_order: Vec<usize>,
backend: GpuBackend,
}
impl RenderGraph {
pub fn new(backend: GpuBackend) -> Self {
Self { passes: Vec::new(), resources: HashMap::new(), execution_order: Vec::new(), backend }
}
pub fn add_pass(&mut self, pass: RenderPass) -> usize {
let idx = self.passes.len();
self.passes.push(pass);
idx
}
pub fn add_resource(&mut self, res: RenderResource) { self.resources.insert(res.name.clone(), res); }
/// Topological sort of render passes (DAG)
pub fn compile(&mut self) -> Result<Vec<String>, RenderError> {
let n = self.passes.len();
let mut in_degree = vec![0usize; n];
let mut adj: Vec<Vec<usize>> = vec![Vec::new(); n];
// Build dependency graph: if pass B reads output of pass A, A → B
for (i, pass_b) in self.passes.iter().enumerate() {
for input in &pass_b.inputs {
for (j, pass_a) in self.passes.iter().enumerate() {
if i != j && pass_a.outputs.contains(input) {
adj[j].push(i);
in_degree[i] += 1;
}
}
}
}
}
}
impl std::error::Error for XcuError {}
pub type Result<T> = std::result::Result<T, XcuError>;
pub struct Config {
pub params: HashMap<String, String>,
}
// Kahn's algorithm
let mut queue: Vec<usize> = (0..n).filter(|&i| in_degree[i] == 0).collect();
let mut order = Vec::new();
impl Config {
pub fn new() -> Self { Self { params: HashMap::new() } }
pub fn set(&mut self, key: &str, val: &str) -> &mut Self {
self.params.insert(key.to_string(), val.to_string()); self
}
pub fn get(&self, key: &str) -> Result<&str> {
self.params.get(key).map(|s| s.as_str()).ok_or_else(|| XcuError::NotFound(key.to_string()))
}
}
while let Some(node) = queue.pop() {
order.push(node);
for &next in &adj[node] {
in_degree[next] -= 1;
if in_degree[next] == 0 { queue.push(next); }
}
}
impl Default for Config {
fn default() -> Self { Self::new() }
}
if order.len() != n { return Err(RenderError::CyclicGraph("Dependency cycle detected".into())); }
pub struct Engine {
config: Config,
state: Arc<Mutex<EngineState>>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum EngineState {
Idle,
Running,
Paused,
ShuttingDown,
Stopped,
}
impl Engine {
pub fn new(config: Config) -> Result<Self> {
Ok(Self { config, state: Arc::new(Mutex::new(EngineState::Idle)) })
self.execution_order = order.clone();
Ok(order.iter().map(|&i| self.passes[i].name.clone()).collect())
}
pub fn start(&self) -> Result<()> {
let mut s = self.state.lock().map_err(|e| XcuError::OperationFailed(e.to_string()))?;
*s = EngineState::Running;
/// Validate all pass inputs are available
pub fn validate(&self) -> Result<(), RenderError> {
let mut available: Vec<String> = self.resources.keys().cloned().collect();
for &idx in &self.execution_order {
let pass = &self.passes[idx];
for input in &pass.inputs {
if !available.contains(input) {
return Err(RenderError::ResourceMissing(format!("Pass '{}' needs '{}' but not produced yet", pass.name, input)));
}
}
for output in &pass.outputs { if !available.contains(output) { available.push(output.clone()); } }
}
Ok(())
}
pub fn stop(&self) -> Result<()> {
let mut s = self.state.lock().map_err(|e| XcuError::OperationFailed(e.to_string()))?;
*s = EngineState::ShuttingDown;
// graceful shutdown logic
*s = EngineState::Stopped;
Ok(())
}
pub fn state(&self) -> Result<EngineState> {
let s = self.state.lock().map_err(|e| XcuError::OperationFailed(e.to_string()))?;
Ok(s.clone())
}
pub fn config(&self) -> &Config { &self.config }
/// Estimate VRAM usage
pub fn estimate_vram(&self) -> u64 { self.resources.values().map(|r| r.size_bytes).sum() }
pub fn pass_count(&self) -> usize { self.passes.len() }
pub fn backend(&self) -> &GpuBackend { &self.backend }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_engine_lifecycle() {
let engine = Engine::new(Config::new()).unwrap();
assert_eq!(engine.state().unwrap(), EngineState::Idle);
engine.start().unwrap();
assert_eq!(engine.state().unwrap(), EngineState::Running);
engine.stop().unwrap();
assert_eq!(engine.state().unwrap(), EngineState::Stopped);
fn test_compile_order() {
let mut g = RenderGraph::new(GpuBackend::WebGpu);
g.add_pass(RenderPass { name: "shadow".into(), inputs: vec![], outputs: vec!["shadow_map".into()], shader: "shadow.wgsl".into() });
g.add_pass(RenderPass { name: "gbuffer".into(), inputs: vec![], outputs: vec!["albedo".into(), "normal".into()], shader: "gbuffer.wgsl".into() });
g.add_pass(RenderPass { name: "lighting".into(), inputs: vec!["albedo".into(), "normal".into(), "shadow_map".into()], outputs: vec!["hdr".into()], shader: "lighting.wgsl".into() });
g.add_pass(RenderPass { name: "tonemap".into(), inputs: vec!["hdr".into()], outputs: vec!["final".into()], shader: "tonemap.wgsl".into() });
let order = g.compile().unwrap();
let light_idx = order.iter().position(|n| n == "lighting").unwrap();
let shadow_idx = order.iter().position(|n| n == "shadow").unwrap();
let gbuf_idx = order.iter().position(|n| n == "gbuffer").unwrap();
assert!(shadow_idx < light_idx);
assert!(gbuf_idx < light_idx);
}
#[test]
fn test_cycle_detection() {
let mut g = RenderGraph::new(GpuBackend::Vulkan);
g.add_pass(RenderPass { name: "a".into(), inputs: vec!["y".into()], outputs: vec!["x".into()], shader: "a".into() });
g.add_pass(RenderPass { name: "b".into(), inputs: vec!["x".into()], outputs: vec!["y".into()], shader: "b".into() });
assert!(g.compile().is_err());
}
}