Skip to main content

proof_engine/editor/
shader_compiler.rs

1#[allow(dead_code, unused_variables, unused_mut, unused_imports)]
2
3use glam::{Vec2, Vec3, Vec4, Quat, Mat4};
4use std::collections::{HashMap, VecDeque, HashSet, BTreeMap};
5
6// ============================================================
7// SECTION 1: TOKEN TYPES
8// ============================================================
9
10#[derive(Debug, Clone, PartialEq)]
11pub enum TokenKind {
12    // Literals
13    IntLit(i64),
14    FloatLit(f64),
15    BoolLit(bool),
16    StringLit(String),
17    // Identifiers & keywords
18    Ident(String),
19    // Types
20    KwFloat, KwInt, KwUint, KwBool,
21    KwVec2, KwVec3, KwVec4,
22    KwIVec2, KwIVec3, KwIVec4,
23    KwUVec2, KwUVec3, KwUVec4,
24    KwMat2, KwMat3, KwMat4,
25    KwSampler2D, KwSampler3D, KwSamplerCube,
26    KwVoid,
27    // Keywords
28    KwIf, KwElse, KwFor, KwWhile, KwDo, KwReturn, KwBreak, KwContinue,
29    KwStruct, KwIn, KwOut, KwInout, KwUniform, KwConst, KwLayout,
30    KwAttribute, KwVarying, KwPrecision, KwHighp, KwMediump, KwLowp,
31    KwDiscard,
32    // Operators
33    Plus, Minus, Star, Slash, Percent,
34    PlusEq, MinusEq, StarEq, SlashEq, PercentEq,
35    PlusPlus, MinusMinus,
36    Amp, Pipe, Caret, Tilde, LShift, RShift,
37    AmpEq, PipeEq, CaretEq, LShiftEq, RShiftEq,
38    Eq, EqEq, BangEq,
39    Lt, Gt, LtEq, GtEq,
40    AmpAmp, PipePipe, Bang,
41    Question, Colon,
42    Dot, Arrow,
43    Semicolon, Comma,
44    LParen, RParen,
45    LBrace, RBrace,
46    LBracket, RBracket,
47    Hash,
48    // Special
49    Eof,
50    Newline,
51}
52
53#[derive(Debug, Clone)]
54pub struct Token {
55    pub kind: TokenKind,
56    pub line: u32,
57    pub col: u32,
58    pub source: String,
59}
60
61impl Token {
62    pub fn new(kind: TokenKind, line: u32, col: u32, source: &str) -> Self {
63        Self { kind, line, col, source: source.to_string() }
64    }
65    pub fn is_type_keyword(&self) -> bool {
66        matches!(self.kind,
67            TokenKind::KwFloat | TokenKind::KwInt | TokenKind::KwUint | TokenKind::KwBool |
68            TokenKind::KwVec2 | TokenKind::KwVec3 | TokenKind::KwVec4 |
69            TokenKind::KwMat2 | TokenKind::KwMat3 | TokenKind::KwMat4 |
70            TokenKind::KwSampler2D | TokenKind::KwSampler3D | TokenKind::KwSamplerCube |
71            TokenKind::KwVoid)
72    }
73}
74
75// ============================================================
76// SECTION 2: LEXER / TOKENIZER
77// ============================================================
78
79pub struct Lexer {
80    pub source: Vec<char>,
81    pub pos: usize,
82    pub line: u32,
83    pub col: u32,
84}
85
86impl Lexer {
87    pub fn new(source: &str) -> Self {
88        Self { source: source.chars().collect(), pos: 0, line: 1, col: 1 }
89    }
90
91    fn peek(&self) -> Option<char> { self.source.get(self.pos).copied() }
92    fn peek2(&self) -> Option<char> { self.source.get(self.pos + 1).copied() }
93
94    fn advance(&mut self) -> Option<char> {
95        let c = self.source.get(self.pos).copied()?;
96        self.pos += 1;
97        if c == '\n' { self.line += 1; self.col = 1; } else { self.col += 1; }
98        Some(c)
99    }
100
101    fn skip_whitespace(&mut self) {
102        while let Some(c) = self.peek() {
103            if c == ' ' || c == '\t' || c == '\r' { self.advance(); }
104            else if c == '\n' { self.advance(); }
105            else if c == '/' && self.peek2() == Some('/') {
106                while let Some(c) = self.peek() { if c == '\n' { break; } self.advance(); }
107            } else if c == '/' && self.peek2() == Some('*') {
108                self.advance(); self.advance();
109                loop {
110                    match self.advance() {
111                        None => break,
112                        Some('*') if self.peek() == Some('/') => { self.advance(); break; }
113                        _ => {}
114                    }
115                }
116            } else { break; }
117        }
118    }
119
120    fn read_ident(&mut self) -> String {
121        let mut s = String::new();
122        while let Some(c) = self.peek() {
123            if c.is_alphanumeric() || c == '_' { s.push(c); self.advance(); } else { break; }
124        }
125        s
126    }
127
128    fn read_number(&mut self) -> TokenKind {
129        let mut s = String::new();
130        let mut is_float = false;
131        while let Some(c) = self.peek() {
132            if c.is_ascii_digit() { s.push(c); self.advance(); }
133            else if c == '.' && !is_float {
134                is_float = true; s.push(c); self.advance();
135            } else if (c == 'e' || c == 'E') && is_float {
136                s.push(c); self.advance();
137                if let Some('+') | Some('-') = self.peek() {
138                    s.push(self.advance().unwrap());
139                }
140            } else if c == 'f' || c == 'u' { self.advance(); break; }
141            else { break; }
142        }
143        if is_float {
144            TokenKind::FloatLit(s.parse().unwrap_or(0.0))
145        } else {
146            if s.starts_with("0x") || s.starts_with("0X") {
147                TokenKind::IntLit(i64::from_str_radix(&s[2..], 16).unwrap_or(0))
148            } else {
149                TokenKind::IntLit(s.parse().unwrap_or(0))
150            }
151        }
152    }
153
154    fn ident_to_keyword(s: &str) -> TokenKind {
155        match s {
156            "float" => TokenKind::KwFloat,
157            "int" => TokenKind::KwInt,
158            "uint" => TokenKind::KwUint,
159            "bool" => TokenKind::KwBool,
160            "vec2" => TokenKind::KwVec2,
161            "vec3" => TokenKind::KwVec3,
162            "vec4" => TokenKind::KwVec4,
163            "ivec2" => TokenKind::KwIVec2,
164            "ivec3" => TokenKind::KwIVec3,
165            "ivec4" => TokenKind::KwIVec4,
166            "uvec2" => TokenKind::KwUVec2,
167            "uvec3" => TokenKind::KwUVec3,
168            "uvec4" => TokenKind::KwUVec4,
169            "mat2" => TokenKind::KwMat2,
170            "mat3" => TokenKind::KwMat3,
171            "mat4" => TokenKind::KwMat4,
172            "sampler2D" => TokenKind::KwSampler2D,
173            "sampler3D" => TokenKind::KwSampler3D,
174            "samplerCube" => TokenKind::KwSamplerCube,
175            "void" => TokenKind::KwVoid,
176            "if" => TokenKind::KwIf,
177            "else" => TokenKind::KwElse,
178            "for" => TokenKind::KwFor,
179            "while" => TokenKind::KwWhile,
180            "do" => TokenKind::KwDo,
181            "return" => TokenKind::KwReturn,
182            "break" => TokenKind::KwBreak,
183            "continue" => TokenKind::KwContinue,
184            "struct" => TokenKind::KwStruct,
185            "in" => TokenKind::KwIn,
186            "out" => TokenKind::KwOut,
187            "inout" => TokenKind::KwInout,
188            "uniform" => TokenKind::KwUniform,
189            "const" => TokenKind::KwConst,
190            "layout" => TokenKind::KwLayout,
191            "attribute" => TokenKind::KwAttribute,
192            "varying" => TokenKind::KwVarying,
193            "precision" => TokenKind::KwPrecision,
194            "highp" => TokenKind::KwHighp,
195            "mediump" => TokenKind::KwMediump,
196            "lowp" => TokenKind::KwLowp,
197            "discard" => TokenKind::KwDiscard,
198            "true" => TokenKind::BoolLit(true),
199            "false" => TokenKind::BoolLit(false),
200            other => TokenKind::Ident(other.to_string()),
201        }
202    }
203
204    pub fn tokenize(&mut self) -> Vec<Token> {
205        let mut tokens = Vec::new();
206        loop {
207            self.skip_whitespace();
208            let line = self.line;
209            let col = self.col;
210            match self.peek() {
211                None => { tokens.push(Token::new(TokenKind::Eof, line, col, "")); break; }
212                Some(c) => {
213                    let tok = match c {
214                        'a'..='z' | 'A'..='Z' | '_' => {
215                            let s = self.read_ident();
216                            let kind = Self::ident_to_keyword(&s);
217                            Token::new(kind, line, col, &s)
218                        }
219                        '0'..='9' => {
220                            let kind = self.read_number();
221                            Token::new(kind, line, col, "")
222                        }
223                        '+' => {
224                            self.advance();
225                            if self.peek() == Some('+') { self.advance(); Token::new(TokenKind::PlusPlus, line, col, "++") }
226                            else if self.peek() == Some('=') { self.advance(); Token::new(TokenKind::PlusEq, line, col, "+=") }
227                            else { Token::new(TokenKind::Plus, line, col, "+") }
228                        }
229                        '-' => {
230                            self.advance();
231                            if self.peek() == Some('-') { self.advance(); Token::new(TokenKind::MinusMinus, line, col, "--") }
232                            else if self.peek() == Some('=') { self.advance(); Token::new(TokenKind::MinusEq, line, col, "-=") }
233                            else if self.peek() == Some('>') { self.advance(); Token::new(TokenKind::Arrow, line, col, "->") }
234                            else { Token::new(TokenKind::Minus, line, col, "-") }
235                        }
236                        '*' => {
237                            self.advance();
238                            if self.peek() == Some('=') { self.advance(); Token::new(TokenKind::StarEq, line, col, "*=") }
239                            else { Token::new(TokenKind::Star, line, col, "*") }
240                        }
241                        '/' => {
242                            self.advance();
243                            if self.peek() == Some('=') { self.advance(); Token::new(TokenKind::SlashEq, line, col, "/=") }
244                            else { Token::new(TokenKind::Slash, line, col, "/") }
245                        }
246                        '%' => {
247                            self.advance();
248                            if self.peek() == Some('=') { self.advance(); Token::new(TokenKind::PercentEq, line, col, "%=") }
249                            else { Token::new(TokenKind::Percent, line, col, "%") }
250                        }
251                        '=' => {
252                            self.advance();
253                            if self.peek() == Some('=') { self.advance(); Token::new(TokenKind::EqEq, line, col, "==") }
254                            else { Token::new(TokenKind::Eq, line, col, "=") }
255                        }
256                        '!' => {
257                            self.advance();
258                            if self.peek() == Some('=') { self.advance(); Token::new(TokenKind::BangEq, line, col, "!=") }
259                            else { Token::new(TokenKind::Bang, line, col, "!") }
260                        }
261                        '<' => {
262                            self.advance();
263                            if self.peek() == Some('=') { self.advance(); Token::new(TokenKind::LtEq, line, col, "<=") }
264                            else if self.peek() == Some('<') {
265                                self.advance();
266                                if self.peek() == Some('=') { self.advance(); Token::new(TokenKind::LShiftEq, line, col, "<<=") }
267                                else { Token::new(TokenKind::LShift, line, col, "<<") }
268                            }
269                            else { Token::new(TokenKind::Lt, line, col, "<") }
270                        }
271                        '>' => {
272                            self.advance();
273                            if self.peek() == Some('=') { self.advance(); Token::new(TokenKind::GtEq, line, col, ">=") }
274                            else if self.peek() == Some('>') {
275                                self.advance();
276                                if self.peek() == Some('=') { self.advance(); Token::new(TokenKind::RShiftEq, line, col, ">>=") }
277                                else { Token::new(TokenKind::RShift, line, col, ">>") }
278                            }
279                            else { Token::new(TokenKind::Gt, line, col, ">") }
280                        }
281                        '&' => {
282                            self.advance();
283                            if self.peek() == Some('&') { self.advance(); Token::new(TokenKind::AmpAmp, line, col, "&&") }
284                            else if self.peek() == Some('=') { self.advance(); Token::new(TokenKind::AmpEq, line, col, "&=") }
285                            else { Token::new(TokenKind::Amp, line, col, "&") }
286                        }
287                        '|' => {
288                            self.advance();
289                            if self.peek() == Some('|') { self.advance(); Token::new(TokenKind::PipePipe, line, col, "||") }
290                            else if self.peek() == Some('=') { self.advance(); Token::new(TokenKind::PipeEq, line, col, "|=") }
291                            else { Token::new(TokenKind::Pipe, line, col, "|") }
292                        }
293                        '^' => {
294                            self.advance();
295                            if self.peek() == Some('=') { self.advance(); Token::new(TokenKind::CaretEq, line, col, "^=") }
296                            else { Token::new(TokenKind::Caret, line, col, "^") }
297                        }
298                        '~' => { self.advance(); Token::new(TokenKind::Tilde, line, col, "~") }
299                        '?' => { self.advance(); Token::new(TokenKind::Question, line, col, "?") }
300                        ':' => { self.advance(); Token::new(TokenKind::Colon, line, col, ":") }
301                        '.' => { self.advance(); Token::new(TokenKind::Dot, line, col, ".") }
302                        ';' => { self.advance(); Token::new(TokenKind::Semicolon, line, col, ";") }
303                        ',' => { self.advance(); Token::new(TokenKind::Comma, line, col, ",") }
304                        '(' => { self.advance(); Token::new(TokenKind::LParen, line, col, "(") }
305                        ')' => { self.advance(); Token::new(TokenKind::RParen, line, col, ")") }
306                        '{' => { self.advance(); Token::new(TokenKind::LBrace, line, col, "{") }
307                        '}' => { self.advance(); Token::new(TokenKind::RBrace, line, col, "}") }
308                        '[' => { self.advance(); Token::new(TokenKind::LBracket, line, col, "[") }
309                        ']' => { self.advance(); Token::new(TokenKind::RBracket, line, col, "]") }
310                        '#' => { self.advance(); Token::new(TokenKind::Hash, line, col, "#") }
311                        _ => { self.advance(); continue; }
312                    };
313                    tokens.push(tok);
314                }
315            }
316        }
317        tokens
318    }
319}
320
321// ============================================================
322// SECTION 3: TYPE SYSTEM
323// ============================================================
324
325#[derive(Debug, Clone, PartialEq, Eq, Hash)]
326pub enum ShaderType {
327    Void,
328    Bool,
329    Int,
330    Uint,
331    Float,
332    BVec2, BVec3, BVec4,
333    IVec2, IVec3, IVec4,
334    UVec2, UVec3, UVec4,
335    Vec2, Vec3, Vec4,
336    Mat2, Mat3, Mat4,
337    Sampler2D, Sampler3D, SamplerCube,
338    Struct(String),
339    Array(Box<ShaderType>, Option<u32>), // type, optional size
340    Function(Vec<ShaderType>, Box<ShaderType>), // params, return
341    Unknown,
342}
343
344impl ShaderType {
345    pub fn is_scalar(&self) -> bool {
346        matches!(self, ShaderType::Bool | ShaderType::Int | ShaderType::Uint | ShaderType::Float)
347    }
348    pub fn is_vector(&self) -> bool {
349        matches!(self,
350            ShaderType::BVec2|ShaderType::BVec3|ShaderType::BVec4|
351            ShaderType::IVec2|ShaderType::IVec3|ShaderType::IVec4|
352            ShaderType::UVec2|ShaderType::UVec3|ShaderType::UVec4|
353            ShaderType::Vec2|ShaderType::Vec3|ShaderType::Vec4)
354    }
355    pub fn is_matrix(&self) -> bool {
356        matches!(self, ShaderType::Mat2|ShaderType::Mat3|ShaderType::Mat4)
357    }
358    pub fn is_numeric(&self) -> bool {
359        matches!(self,
360            ShaderType::Int|ShaderType::Uint|ShaderType::Float|
361            ShaderType::IVec2|ShaderType::IVec3|ShaderType::IVec4|
362            ShaderType::UVec2|ShaderType::UVec3|ShaderType::UVec4|
363            ShaderType::Vec2|ShaderType::Vec3|ShaderType::Vec4|
364            ShaderType::Mat2|ShaderType::Mat3|ShaderType::Mat4)
365    }
366    pub fn component_count(&self) -> u32 {
367        match self {
368            ShaderType::Float|ShaderType::Int|ShaderType::Uint|ShaderType::Bool => 1,
369            ShaderType::Vec2|ShaderType::IVec2|ShaderType::UVec2|ShaderType::BVec2 => 2,
370            ShaderType::Vec3|ShaderType::IVec3|ShaderType::UVec3|ShaderType::BVec3 => 3,
371            ShaderType::Vec4|ShaderType::IVec4|ShaderType::UVec4|ShaderType::BVec4 => 4,
372            ShaderType::Mat2 => 4,
373            ShaderType::Mat3 => 9,
374            ShaderType::Mat4 => 16,
375            _ => 0,
376        }
377    }
378    pub fn base_scalar(&self) -> ShaderType {
379        match self {
380            ShaderType::Vec2|ShaderType::Vec3|ShaderType::Vec4 => ShaderType::Float,
381            ShaderType::IVec2|ShaderType::IVec3|ShaderType::IVec4 => ShaderType::Int,
382            ShaderType::UVec2|ShaderType::UVec3|ShaderType::UVec4 => ShaderType::Uint,
383            ShaderType::BVec2|ShaderType::BVec3|ShaderType::BVec4 => ShaderType::Bool,
384            ShaderType::Mat2|ShaderType::Mat3|ShaderType::Mat4 => ShaderType::Float,
385            other => other.clone(),
386        }
387    }
388    pub fn name(&self) -> &str {
389        match self {
390            ShaderType::Void => "void",
391            ShaderType::Bool => "bool",
392            ShaderType::Int => "int",
393            ShaderType::Uint => "uint",
394            ShaderType::Float => "float",
395            ShaderType::BVec2 => "bvec2",
396            ShaderType::BVec3 => "bvec3",
397            ShaderType::BVec4 => "bvec4",
398            ShaderType::IVec2 => "ivec2",
399            ShaderType::IVec3 => "ivec3",
400            ShaderType::IVec4 => "ivec4",
401            ShaderType::UVec2 => "uvec2",
402            ShaderType::UVec3 => "uvec3",
403            ShaderType::UVec4 => "uvec4",
404            ShaderType::Vec2 => "vec2",
405            ShaderType::Vec3 => "vec3",
406            ShaderType::Vec4 => "vec4",
407            ShaderType::Mat2 => "mat2",
408            ShaderType::Mat3 => "mat3",
409            ShaderType::Mat4 => "mat4",
410            ShaderType::Sampler2D => "sampler2D",
411            ShaderType::Sampler3D => "sampler3D",
412            ShaderType::SamplerCube => "samplerCube",
413            ShaderType::Struct(n) => n.as_str(),
414            ShaderType::Array(_, _) => "array",
415            ShaderType::Function(_, _) => "function",
416            ShaderType::Unknown => "unknown",
417        }
418    }
419    /// Can we implicitly convert `from` to `self`?
420    pub fn can_implicit_convert(from: &ShaderType, to: &ShaderType) -> bool {
421        if from == to { return true; }
422        matches!((from, to),
423            (ShaderType::Int, ShaderType::Float) |
424            (ShaderType::Uint, ShaderType::Float) |
425            (ShaderType::Int, ShaderType::Uint) |
426            (ShaderType::Bool, ShaderType::Int) |
427            (ShaderType::Bool, ShaderType::Float) |
428            (ShaderType::IVec2, ShaderType::Vec2) |
429            (ShaderType::IVec3, ShaderType::Vec3) |
430            (ShaderType::IVec4, ShaderType::Vec4)
431        )
432    }
433    pub fn from_token(t: &TokenKind) -> Option<ShaderType> {
434        match t {
435            TokenKind::KwFloat => Some(ShaderType::Float),
436            TokenKind::KwInt => Some(ShaderType::Int),
437            TokenKind::KwUint => Some(ShaderType::Uint),
438            TokenKind::KwBool => Some(ShaderType::Bool),
439            TokenKind::KwVec2 => Some(ShaderType::Vec2),
440            TokenKind::KwVec3 => Some(ShaderType::Vec3),
441            TokenKind::KwVec4 => Some(ShaderType::Vec4),
442            TokenKind::KwIVec2 => Some(ShaderType::IVec2),
443            TokenKind::KwIVec3 => Some(ShaderType::IVec3),
444            TokenKind::KwIVec4 => Some(ShaderType::IVec4),
445            TokenKind::KwUVec2 => Some(ShaderType::UVec2),
446            TokenKind::KwUVec3 => Some(ShaderType::UVec3),
447            TokenKind::KwUVec4 => Some(ShaderType::UVec4),
448            TokenKind::KwMat2 => Some(ShaderType::Mat2),
449            TokenKind::KwMat3 => Some(ShaderType::Mat3),
450            TokenKind::KwMat4 => Some(ShaderType::Mat4),
451            TokenKind::KwSampler2D => Some(ShaderType::Sampler2D),
452            TokenKind::KwSampler3D => Some(ShaderType::Sampler3D),
453            TokenKind::KwSamplerCube => Some(ShaderType::SamplerCube),
454            TokenKind::KwVoid => Some(ShaderType::Void),
455            _ => None,
456        }
457    }
458}
459
460// ============================================================
461// SECTION 4: AST NODES
462// ============================================================
463
464#[derive(Debug, Clone)]
465pub enum Expr {
466    IntLit(i64),
467    FloatLit(f64),
468    BoolLit(bool),
469    Ident(String),
470    Unary { op: UnaryOp, operand: Box<Expr> },
471    Binary { op: BinaryOp, left: Box<Expr>, right: Box<Expr> },
472    Assign { op: AssignOp, target: Box<Expr>, value: Box<Expr> },
473    Ternary { cond: Box<Expr>, then_expr: Box<Expr>, else_expr: Box<Expr> },
474    Call { function: String, args: Vec<Expr> },
475    Index { array: Box<Expr>, index: Box<Expr> },
476    Field { object: Box<Expr>, field: String },
477    Swizzle { object: Box<Expr>, components: String },
478    Cast { target_type: ShaderType, expr: Box<Expr> },
479    Construction { ty: ShaderType, args: Vec<Expr> },
480}
481
482#[derive(Debug, Clone, Copy, PartialEq)]
483pub enum UnaryOp {
484    Negate,
485    Not,
486    BitNot,
487    PreIncrement,
488    PreDecrement,
489    PostIncrement,
490    PostDecrement,
491}
492
493#[derive(Debug, Clone, Copy, PartialEq)]
494pub enum BinaryOp {
495    Add, Sub, Mul, Div, Mod,
496    Eq, Ne, Lt, Gt, Le, Ge,
497    And, Or,
498    BitAnd, BitOr, BitXor, Shl, Shr,
499}
500
501#[derive(Debug, Clone, Copy, PartialEq)]
502pub enum AssignOp {
503    Assign, AddAssign, SubAssign, MulAssign, DivAssign, ModAssign,
504    AndAssign, OrAssign, XorAssign, ShlAssign, ShrAssign,
505}
506
507#[derive(Debug, Clone)]
508pub enum Stmt {
509    Expr(Expr),
510    Block(Vec<Stmt>),
511    VarDecl { ty: ShaderType, name: String, qualifier: Option<VarQualifier>, init: Option<Expr> },
512    If { cond: Expr, then_body: Box<Stmt>, else_body: Option<Box<Stmt>> },
513    For { init: Option<Box<Stmt>>, cond: Option<Expr>, step: Option<Expr>, body: Box<Stmt> },
514    While { cond: Expr, body: Box<Stmt> },
515    DoWhile { body: Box<Stmt>, cond: Expr },
516    Return(Option<Expr>),
517    Break,
518    Continue,
519    Discard,
520}
521
522#[derive(Debug, Clone, Copy, PartialEq)]
523pub enum VarQualifier {
524    In, Out, Inout, Uniform, Const, Attribute, Varying,
525}
526
527#[derive(Debug, Clone)]
528pub struct FunctionParam {
529    pub ty: ShaderType,
530    pub name: String,
531    pub qualifier: Option<VarQualifier>,
532}
533
534#[derive(Debug, Clone)]
535pub struct FunctionDecl {
536    pub return_type: ShaderType,
537    pub name: String,
538    pub params: Vec<FunctionParam>,
539    pub body: Vec<Stmt>,
540    pub is_builtin: bool,
541}
542
543#[derive(Debug, Clone)]
544pub struct StructField {
545    pub ty: ShaderType,
546    pub name: String,
547    pub array_size: Option<u32>,
548}
549
550#[derive(Debug, Clone)]
551pub struct StructDecl {
552    pub name: String,
553    pub fields: Vec<StructField>,
554}
555
556#[derive(Debug, Clone)]
557pub struct UniformDecl {
558    pub ty: ShaderType,
559    pub name: String,
560    pub binding: Option<u32>,
561    pub set: Option<u32>,
562    pub array_size: Option<u32>,
563}
564
565#[derive(Debug, Clone)]
566pub struct AttributeDecl {
567    pub ty: ShaderType,
568    pub name: String,
569    pub location: Option<u32>,
570}
571
572#[derive(Debug, Clone)]
573pub struct ShaderAst {
574    pub version: u32,
575    pub extensions: Vec<String>,
576    pub structs: Vec<StructDecl>,
577    pub uniforms: Vec<UniformDecl>,
578    pub attributes: Vec<AttributeDecl>,
579    pub varyings: Vec<AttributeDecl>,
580    pub functions: Vec<FunctionDecl>,
581    pub global_vars: Vec<Stmt>,
582    pub defines: HashMap<String, String>,
583}
584
585impl ShaderAst {
586    pub fn new() -> Self {
587        Self {
588            version: 450,
589            extensions: Vec::new(),
590            structs: Vec::new(),
591            uniforms: Vec::new(),
592            attributes: Vec::new(),
593            varyings: Vec::new(),
594            functions: Vec::new(),
595            global_vars: Vec::new(),
596            defines: HashMap::new(),
597        }
598    }
599    pub fn find_function(&self, name: &str) -> Option<&FunctionDecl> {
600        self.functions.iter().find(|f| f.name == name)
601    }
602    pub fn find_uniform(&self, name: &str) -> Option<&UniformDecl> {
603        self.uniforms.iter().find(|u| u.name == name)
604    }
605}
606
607// ============================================================
608// SECTION 5: PARSER
609// ============================================================
610
611pub struct Parser {
612    pub tokens: Vec<Token>,
613    pub pos: usize,
614    pub errors: Vec<ParseError>,
615}
616
617#[derive(Debug, Clone)]
618pub struct ParseError {
619    pub message: String,
620    pub line: u32,
621    pub col: u32,
622}
623
624impl Parser {
625    pub fn new(tokens: Vec<Token>) -> Self {
626        Self { tokens, pos: 0, errors: Vec::new() }
627    }
628    fn peek(&self) -> &Token { &self.tokens[self.pos.min(self.tokens.len()-1)] }
629    fn peek2(&self) -> &Token { &self.tokens[(self.pos+1).min(self.tokens.len()-1)] }
630    fn advance(&mut self) -> &Token {
631        let t = &self.tokens[self.pos.min(self.tokens.len()-1)];
632        if self.pos < self.tokens.len()-1 { self.pos += 1; }
633        t
634    }
635    fn expect(&mut self, kind: &TokenKind) -> bool {
636        if &self.peek().kind == kind { self.advance(); true }
637        else {
638            let tok = self.peek().clone();
639            self.errors.push(ParseError {
640                message: format!("Expected {:?}, got {:?}", kind, tok.kind),
641                line: tok.line, col: tok.col,
642            });
643            false
644        }
645    }
646    fn at_eof(&self) -> bool { matches!(self.peek().kind, TokenKind::Eof) }
647
648    fn parse_type(&mut self) -> ShaderType {
649        let tok = self.peek().kind.clone();
650        if let Some(ty) = ShaderType::from_token(&tok) {
651            self.advance();
652            return ty;
653        }
654        if let TokenKind::Ident(name) = &tok {
655            let name = name.clone();
656            self.advance();
657            return ShaderType::Struct(name);
658        }
659        ShaderType::Unknown
660    }
661
662    fn parse_expr_primary(&mut self) -> Expr {
663        match self.peek().kind.clone() {
664            TokenKind::IntLit(v) => { self.advance(); Expr::IntLit(v) }
665            TokenKind::FloatLit(v) => { self.advance(); Expr::FloatLit(v) }
666            TokenKind::BoolLit(v) => { self.advance(); Expr::BoolLit(v) }
667            TokenKind::Ident(name) => {
668                self.advance();
669                if self.peek().kind == TokenKind::LParen {
670                    // function call
671                    self.advance();
672                    let mut args = Vec::new();
673                    while self.peek().kind != TokenKind::RParen && !self.at_eof() {
674                        args.push(self.parse_expr());
675                        if self.peek().kind == TokenKind::Comma { self.advance(); }
676                        else { break; }
677                    }
678                    self.expect(&TokenKind::RParen);
679                    Expr::Call { function: name, args }
680                } else {
681                    Expr::Ident(name)
682                }
683            }
684            TokenKind::LParen => {
685                self.advance();
686                let e = self.parse_expr();
687                self.expect(&TokenKind::RParen);
688                e
689            }
690            tok if ShaderType::from_token(&tok).is_some() => {
691                let ty = self.parse_type();
692                self.expect(&TokenKind::LParen);
693                let mut args = Vec::new();
694                while self.peek().kind != TokenKind::RParen && !self.at_eof() {
695                    args.push(self.parse_expr());
696                    if self.peek().kind == TokenKind::Comma { self.advance(); }
697                    else { break; }
698                }
699                self.expect(&TokenKind::RParen);
700                Expr::Construction { ty, args }
701            }
702            _ => {
703                let tok = self.peek().clone();
704                self.errors.push(ParseError { message: format!("Unexpected token {:?}", tok.kind), line: tok.line, col: tok.col });
705                self.advance();
706                Expr::IntLit(0)
707            }
708        }
709    }
710
711    fn parse_expr_postfix(&mut self) -> Expr {
712        let mut e = self.parse_expr_primary();
713        loop {
714            match self.peek().kind.clone() {
715                TokenKind::Dot => {
716                    self.advance();
717                    if let TokenKind::Ident(field) = self.peek().kind.clone() {
718                        self.advance();
719                        e = Expr::Field { object: Box::new(e), field };
720                    }
721                }
722                TokenKind::LBracket => {
723                    self.advance();
724                    let idx = self.parse_expr();
725                    self.expect(&TokenKind::RBracket);
726                    e = Expr::Index { array: Box::new(e), index: Box::new(idx) };
727                }
728                TokenKind::PlusPlus => { self.advance(); e = Expr::Unary { op: UnaryOp::PostIncrement, operand: Box::new(e) }; }
729                TokenKind::MinusMinus => { self.advance(); e = Expr::Unary { op: UnaryOp::PostDecrement, operand: Box::new(e) }; }
730                _ => break,
731            }
732        }
733        e
734    }
735
736    fn parse_expr_unary(&mut self) -> Expr {
737        match self.peek().kind {
738            TokenKind::Minus => { self.advance(); Expr::Unary { op: UnaryOp::Negate, operand: Box::new(self.parse_expr_unary()) } }
739            TokenKind::Bang => { self.advance(); Expr::Unary { op: UnaryOp::Not, operand: Box::new(self.parse_expr_unary()) } }
740            TokenKind::Tilde => { self.advance(); Expr::Unary { op: UnaryOp::BitNot, operand: Box::new(self.parse_expr_unary()) } }
741            TokenKind::PlusPlus => { self.advance(); Expr::Unary { op: UnaryOp::PreIncrement, operand: Box::new(self.parse_expr_unary()) } }
742            TokenKind::MinusMinus => { self.advance(); Expr::Unary { op: UnaryOp::PreDecrement, operand: Box::new(self.parse_expr_unary()) } }
743            _ => self.parse_expr_postfix(),
744        }
745    }
746
747    fn parse_expr_mul(&mut self) -> Expr {
748        let mut left = self.parse_expr_unary();
749        loop {
750            let op = match self.peek().kind {
751                TokenKind::Star => BinaryOp::Mul,
752                TokenKind::Slash => BinaryOp::Div,
753                TokenKind::Percent => BinaryOp::Mod,
754                _ => break,
755            };
756            self.advance();
757            let right = self.parse_expr_unary();
758            left = Expr::Binary { op, left: Box::new(left), right: Box::new(right) };
759        }
760        left
761    }
762
763    fn parse_expr_add(&mut self) -> Expr {
764        let mut left = self.parse_expr_mul();
765        loop {
766            let op = match self.peek().kind {
767                TokenKind::Plus => BinaryOp::Add,
768                TokenKind::Minus => BinaryOp::Sub,
769                _ => break,
770            };
771            self.advance();
772            let right = self.parse_expr_mul();
773            left = Expr::Binary { op, left: Box::new(left), right: Box::new(right) };
774        }
775        left
776    }
777
778    fn parse_expr_shift(&mut self) -> Expr {
779        let mut left = self.parse_expr_add();
780        loop {
781            let op = match self.peek().kind {
782                TokenKind::LShift => BinaryOp::Shl,
783                TokenKind::RShift => BinaryOp::Shr,
784                _ => break,
785            };
786            self.advance();
787            let right = self.parse_expr_add();
788            left = Expr::Binary { op, left: Box::new(left), right: Box::new(right) };
789        }
790        left
791    }
792
793    fn parse_expr_compare(&mut self) -> Expr {
794        let mut left = self.parse_expr_shift();
795        loop {
796            let op = match self.peek().kind {
797                TokenKind::Lt => BinaryOp::Lt,
798                TokenKind::Gt => BinaryOp::Gt,
799                TokenKind::LtEq => BinaryOp::Le,
800                TokenKind::GtEq => BinaryOp::Ge,
801                _ => break,
802            };
803            self.advance();
804            let right = self.parse_expr_shift();
805            left = Expr::Binary { op, left: Box::new(left), right: Box::new(right) };
806        }
807        left
808    }
809
810    fn parse_expr_equality(&mut self) -> Expr {
811        let mut left = self.parse_expr_compare();
812        loop {
813            let op = match self.peek().kind {
814                TokenKind::EqEq => BinaryOp::Eq,
815                TokenKind::BangEq => BinaryOp::Ne,
816                _ => break,
817            };
818            self.advance();
819            let right = self.parse_expr_compare();
820            left = Expr::Binary { op, left: Box::new(left), right: Box::new(right) };
821        }
822        left
823    }
824
825    fn parse_expr_bitand(&mut self) -> Expr {
826        let mut left = self.parse_expr_equality();
827        while self.peek().kind == TokenKind::Amp {
828            self.advance();
829            let right = self.parse_expr_equality();
830            left = Expr::Binary { op: BinaryOp::BitAnd, left: Box::new(left), right: Box::new(right) };
831        }
832        left
833    }
834
835    fn parse_expr_bitor(&mut self) -> Expr {
836        let mut left = self.parse_expr_bitand();
837        while self.peek().kind == TokenKind::Pipe {
838            self.advance();
839            let right = self.parse_expr_bitand();
840            left = Expr::Binary { op: BinaryOp::BitOr, left: Box::new(left), right: Box::new(right) };
841        }
842        left
843    }
844
845    fn parse_expr_and(&mut self) -> Expr {
846        let mut left = self.parse_expr_bitor();
847        while self.peek().kind == TokenKind::AmpAmp {
848            self.advance();
849            let right = self.parse_expr_bitor();
850            left = Expr::Binary { op: BinaryOp::And, left: Box::new(left), right: Box::new(right) };
851        }
852        left
853    }
854
855    fn parse_expr_or(&mut self) -> Expr {
856        let mut left = self.parse_expr_and();
857        while self.peek().kind == TokenKind::PipePipe {
858            self.advance();
859            let right = self.parse_expr_and();
860            left = Expr::Binary { op: BinaryOp::Or, left: Box::new(left), right: Box::new(right) };
861        }
862        left
863    }
864
865    fn parse_expr_ternary(&mut self) -> Expr {
866        let cond = self.parse_expr_or();
867        if self.peek().kind == TokenKind::Question {
868            self.advance();
869            let then_expr = self.parse_expr();
870            self.expect(&TokenKind::Colon);
871            let else_expr = self.parse_expr_ternary();
872            Expr::Ternary { cond: Box::new(cond), then_expr: Box::new(then_expr), else_expr: Box::new(else_expr) }
873        } else { cond }
874    }
875
876    fn parse_expr_assign(&mut self) -> Expr {
877        let left = self.parse_expr_ternary();
878        let op = match self.peek().kind {
879            TokenKind::Eq => AssignOp::Assign,
880            TokenKind::PlusEq => AssignOp::AddAssign,
881            TokenKind::MinusEq => AssignOp::SubAssign,
882            TokenKind::StarEq => AssignOp::MulAssign,
883            TokenKind::SlashEq => AssignOp::DivAssign,
884            TokenKind::PercentEq => AssignOp::ModAssign,
885            TokenKind::AmpEq => AssignOp::AndAssign,
886            TokenKind::PipeEq => AssignOp::OrAssign,
887            TokenKind::CaretEq => AssignOp::XorAssign,
888            TokenKind::LShiftEq => AssignOp::ShlAssign,
889            TokenKind::RShiftEq => AssignOp::ShrAssign,
890            _ => return left,
891        };
892        self.advance();
893        let value = self.parse_expr_assign();
894        Expr::Assign { op, target: Box::new(left), value: Box::new(value) }
895    }
896
897    pub fn parse_expr(&mut self) -> Expr { self.parse_expr_assign() }
898
899    fn parse_stmt(&mut self) -> Option<Stmt> {
900        match self.peek().kind.clone() {
901            TokenKind::LBrace => {
902                self.advance();
903                let mut stmts = Vec::new();
904                while self.peek().kind != TokenKind::RBrace && !self.at_eof() {
905                    if let Some(s) = self.parse_stmt() { stmts.push(s); }
906                }
907                self.expect(&TokenKind::RBrace);
908                Some(Stmt::Block(stmts))
909            }
910            TokenKind::KwIf => {
911                self.advance();
912                self.expect(&TokenKind::LParen);
913                let cond = self.parse_expr();
914                self.expect(&TokenKind::RParen);
915                let then_body = Box::new(self.parse_stmt()?);
916                let else_body = if self.peek().kind == TokenKind::KwElse {
917                    self.advance();
918                    Some(Box::new(self.parse_stmt()?))
919                } else { None };
920                Some(Stmt::If { cond, then_body, else_body })
921            }
922            TokenKind::KwFor => {
923                self.advance();
924                self.expect(&TokenKind::LParen);
925                let init = if self.peek().kind != TokenKind::Semicolon {
926                    self.parse_stmt().map(Box::new)
927                } else { self.advance(); None };
928                let cond = if self.peek().kind != TokenKind::Semicolon {
929                    Some(self.parse_expr())
930                } else { None };
931                self.expect(&TokenKind::Semicolon);
932                let step = if self.peek().kind != TokenKind::RParen {
933                    Some(self.parse_expr())
934                } else { None };
935                self.expect(&TokenKind::RParen);
936                let body = Box::new(self.parse_stmt()?);
937                Some(Stmt::For { init, cond, step, body })
938            }
939            TokenKind::KwWhile => {
940                self.advance();
941                self.expect(&TokenKind::LParen);
942                let cond = self.parse_expr();
943                self.expect(&TokenKind::RParen);
944                let body = Box::new(self.parse_stmt()?);
945                Some(Stmt::While { cond, body })
946            }
947            TokenKind::KwDo => {
948                self.advance();
949                let body = Box::new(self.parse_stmt()?);
950                self.expect(&TokenKind::KwWhile);
951                self.expect(&TokenKind::LParen);
952                let cond = self.parse_expr();
953                self.expect(&TokenKind::RParen);
954                self.expect(&TokenKind::Semicolon);
955                Some(Stmt::DoWhile { body, cond })
956            }
957            TokenKind::KwReturn => {
958                self.advance();
959                let val = if self.peek().kind != TokenKind::Semicolon {
960                    Some(self.parse_expr())
961                } else { None };
962                self.expect(&TokenKind::Semicolon);
963                Some(Stmt::Return(val))
964            }
965            TokenKind::KwBreak => { self.advance(); self.expect(&TokenKind::Semicolon); Some(Stmt::Break) }
966            TokenKind::KwContinue => { self.advance(); self.expect(&TokenKind::Semicolon); Some(Stmt::Continue) }
967            TokenKind::KwDiscard => { self.advance(); self.expect(&TokenKind::Semicolon); Some(Stmt::Discard) }
968            tok if ShaderType::from_token(&tok).is_some() || matches!(tok, TokenKind::Ident(_)) => {
969                // Could be var decl or expr
970                let saved_pos = self.pos;
971                let ty = self.parse_type();
972                if let TokenKind::Ident(name) = self.peek().kind.clone() {
973                    self.advance();
974                    let init = if self.peek().kind == TokenKind::Eq {
975                        self.advance();
976                        Some(self.parse_expr())
977                    } else { None };
978                    self.expect(&TokenKind::Semicolon);
979                    Some(Stmt::VarDecl { ty, name, qualifier: None, init })
980                } else {
981                    // rewind — it's an expression
982                    self.pos = saved_pos;
983                    let e = self.parse_expr();
984                    self.expect(&TokenKind::Semicolon);
985                    Some(Stmt::Expr(e))
986                }
987            }
988            _ => {
989                let e = self.parse_expr();
990                self.expect(&TokenKind::Semicolon);
991                Some(Stmt::Expr(e))
992            }
993        }
994    }
995
996    fn parse_function(&mut self, return_type: ShaderType, name: String) -> FunctionDecl {
997        // already consumed return_type and name
998        self.expect(&TokenKind::LParen);
999        let mut params = Vec::new();
1000        while self.peek().kind != TokenKind::RParen && !self.at_eof() {
1001            let qualifier = match self.peek().kind {
1002                TokenKind::KwIn => { self.advance(); Some(VarQualifier::In) }
1003                TokenKind::KwOut => { self.advance(); Some(VarQualifier::Out) }
1004                TokenKind::KwInout => { self.advance(); Some(VarQualifier::Inout) }
1005                TokenKind::KwConst => { self.advance(); Some(VarQualifier::Const) }
1006                _ => None,
1007            };
1008            let ty = self.parse_type();
1009            let pname = if let TokenKind::Ident(n) = self.peek().kind.clone() { self.advance(); n } else { String::new() };
1010            params.push(FunctionParam { ty, name: pname, qualifier });
1011            if self.peek().kind == TokenKind::Comma { self.advance(); } else { break; }
1012        }
1013        self.expect(&TokenKind::RParen);
1014        let mut body = Vec::new();
1015        if self.peek().kind == TokenKind::LBrace {
1016            self.advance();
1017            while self.peek().kind != TokenKind::RBrace && !self.at_eof() {
1018                if let Some(s) = self.parse_stmt() { body.push(s); }
1019            }
1020            self.expect(&TokenKind::RBrace);
1021        } else {
1022            self.expect(&TokenKind::Semicolon);
1023        }
1024        FunctionDecl { return_type, name, params, body, is_builtin: false }
1025    }
1026
1027    pub fn parse_translation_unit(&mut self) -> ShaderAst {
1028        let mut ast = ShaderAst::new();
1029        while !self.at_eof() {
1030            // Handle preprocessor directives
1031            if self.peek().kind == TokenKind::Hash {
1032                self.advance();
1033                if let TokenKind::Ident(directive) = self.peek().kind.clone() {
1034                    self.advance();
1035                    match directive.as_str() {
1036                        "version" => {
1037                            if let TokenKind::IntLit(v) = self.peek().kind { self.advance(); ast.version = v as u32; }
1038                        }
1039                        "define" => {
1040                            if let TokenKind::Ident(macro_name) = self.peek().kind.clone() {
1041                                self.advance();
1042                                let val = if let TokenKind::Ident(v) = self.peek().kind.clone() { self.advance(); v } else { String::new() };
1043                                ast.defines.insert(macro_name, val);
1044                            }
1045                        }
1046                        _ => {}
1047                    }
1048                }
1049                continue;
1050            }
1051            // Layout qualifier
1052            let mut binding = None;
1053            let mut set_num = None;
1054            if self.peek().kind == TokenKind::KwLayout {
1055                self.advance();
1056                self.expect(&TokenKind::LParen);
1057                while self.peek().kind != TokenKind::RParen && !self.at_eof() {
1058                    if let TokenKind::Ident(k) = self.peek().kind.clone() {
1059                        self.advance();
1060                        if self.peek().kind == TokenKind::Eq {
1061                            self.advance();
1062                            if let TokenKind::IntLit(v) = self.peek().kind { self.advance();
1063                                if k == "binding" { binding = Some(v as u32); }
1064                                else if k == "set" { set_num = Some(v as u32); }
1065                            }
1066                        }
1067                    } else { self.advance(); }
1068                    if self.peek().kind == TokenKind::Comma { self.advance(); } else { break; }
1069                }
1070                self.expect(&TokenKind::RParen);
1071            }
1072            // Qualifiers
1073            let qualifier = match self.peek().kind {
1074                TokenKind::KwUniform => { self.advance(); Some(VarQualifier::Uniform) }
1075                TokenKind::KwIn | TokenKind::KwAttribute => { self.advance(); Some(VarQualifier::In) }
1076                TokenKind::KwOut | TokenKind::KwVarying => { self.advance(); Some(VarQualifier::Out) }
1077                TokenKind::KwConst => { self.advance(); Some(VarQualifier::Const) }
1078                _ => None,
1079            };
1080            // Precision qualifier
1081            if matches!(self.peek().kind, TokenKind::KwPrecision|TokenKind::KwHighp|TokenKind::KwMediump|TokenKind::KwLowp) {
1082                self.advance();
1083                if matches!(self.peek().kind, TokenKind::KwHighp|TokenKind::KwMediump|TokenKind::KwLowp) { self.advance(); }
1084                self.parse_type();
1085                self.expect(&TokenKind::Semicolon);
1086                continue;
1087            }
1088            // Struct
1089            if self.peek().kind == TokenKind::KwStruct {
1090                self.advance();
1091                let sname = if let TokenKind::Ident(n) = self.peek().kind.clone() { self.advance(); n } else { String::new() };
1092                let mut fields = Vec::new();
1093                self.expect(&TokenKind::LBrace);
1094                while self.peek().kind != TokenKind::RBrace && !self.at_eof() {
1095                    let fty = self.parse_type();
1096                    while let TokenKind::Ident(fname) = self.peek().kind.clone() {
1097                        self.advance();
1098                        let arr = if self.peek().kind == TokenKind::LBracket {
1099                            self.advance();
1100                            let sz = if let TokenKind::IntLit(v) = self.peek().kind { self.advance(); Some(v as u32) } else { None };
1101                            self.expect(&TokenKind::RBracket);
1102                            sz
1103                        } else { None };
1104                        fields.push(StructField { ty: fty.clone(), name: fname, array_size: arr });
1105                        if self.peek().kind == TokenKind::Comma { self.advance(); } else { break; }
1106                    }
1107                    self.expect(&TokenKind::Semicolon);
1108                }
1109                self.expect(&TokenKind::RBrace);
1110                self.expect(&TokenKind::Semicolon);
1111                ast.structs.push(StructDecl { name: sname, fields });
1112                continue;
1113            }
1114            // Type + name
1115            if self.peek().is_type_keyword() || matches!(self.peek().kind, TokenKind::Ident(_)) {
1116                let ty = self.parse_type();
1117                let name = if let TokenKind::Ident(n) = self.peek().kind.clone() { self.advance(); n } else { String::new() };
1118                // Array?
1119                let arr_size = if self.peek().kind == TokenKind::LBracket {
1120                    self.advance();
1121                    let sz = if let TokenKind::IntLit(v) = self.peek().kind { self.advance(); Some(v as u32) } else { None };
1122                    self.expect(&TokenKind::RBracket);
1123                    sz
1124                } else { None };
1125                // Function?
1126                if self.peek().kind == TokenKind::LParen {
1127                    let func = self.parse_function(ty, name);
1128                    ast.functions.push(func);
1129                } else {
1130                    // Variable declaration
1131                    match qualifier {
1132                        Some(VarQualifier::Uniform) => {
1133                            ast.uniforms.push(UniformDecl { ty, name, binding, set: set_num, array_size: arr_size });
1134                            self.expect(&TokenKind::Semicolon);
1135                        }
1136                        Some(VarQualifier::In) => {
1137                            ast.attributes.push(AttributeDecl { ty, name, location: binding });
1138                            self.expect(&TokenKind::Semicolon);
1139                        }
1140                        Some(VarQualifier::Out) => {
1141                            ast.varyings.push(AttributeDecl { ty, name, location: binding });
1142                            self.expect(&TokenKind::Semicolon);
1143                        }
1144                        _ => {
1145                            let init = if self.peek().kind == TokenKind::Eq { self.advance(); Some(self.parse_expr()) } else { None };
1146                            self.expect(&TokenKind::Semicolon);
1147                            ast.global_vars.push(Stmt::VarDecl { ty, name, qualifier, init });
1148                        }
1149                    }
1150                }
1151            } else {
1152                self.advance(); // skip unknown token
1153            }
1154        }
1155        ast
1156    }
1157}
1158
1159// ============================================================
1160// SECTION 6: SEMANTIC ANALYSIS
1161// ============================================================
1162
1163#[derive(Debug, Clone)]
1164pub struct Symbol {
1165    pub name: String,
1166    pub ty: ShaderType,
1167    pub qualifier: Option<VarQualifier>,
1168    pub used: bool,
1169    pub defined_at_line: u32,
1170}
1171
1172pub struct Scope {
1173    pub symbols: HashMap<String, Symbol>,
1174    pub parent: Option<*mut Scope>,
1175}
1176
1177impl Scope {
1178    pub fn new() -> Self { Self { symbols: HashMap::new(), parent: None } }
1179    pub fn lookup(&self, name: &str) -> Option<&Symbol> {
1180        if let Some(s) = self.symbols.get(name) { return Some(s); }
1181        None
1182    }
1183    pub fn insert(&mut self, sym: Symbol) { self.symbols.insert(sym.name.clone(), sym); }
1184}
1185
1186#[derive(Debug, Clone)]
1187pub struct SemanticError {
1188    pub kind: SemanticErrorKind,
1189    pub message: String,
1190    pub line: u32,
1191}
1192
1193#[derive(Debug, Clone)]
1194pub enum SemanticErrorKind {
1195    UndeclaredVariable,
1196    TypeMismatch,
1197    RedefinedSymbol,
1198    InvalidOperation,
1199    UnusedVariable,
1200    RecursionDetected,
1201    ReturnTypeMismatch,
1202    InvalidArgCount,
1203}
1204
1205pub struct SemanticAnalyzer {
1206    pub scopes: Vec<HashMap<String, Symbol>>,
1207    pub errors: Vec<SemanticError>,
1208    pub warnings: Vec<SemanticError>,
1209    pub current_function: Option<String>,
1210    pub call_stack: HashSet<String>,
1211    pub built_in_functions: HashMap<String, BuiltinSignature>,
1212}
1213
1214#[derive(Debug, Clone)]
1215pub struct BuiltinSignature {
1216    pub name: String,
1217    pub overloads: Vec<(Vec<ShaderType>, ShaderType)>, // (params, return)
1218}
1219
1220impl SemanticAnalyzer {
1221    pub fn new() -> Self {
1222        let mut a = Self {
1223            scopes: vec![HashMap::new()],
1224            errors: Vec::new(),
1225            warnings: Vec::new(),
1226            current_function: None,
1227            call_stack: HashSet::new(),
1228            built_in_functions: HashMap::new(),
1229        };
1230        a.register_builtins();
1231        a
1232    }
1233
1234    fn register_builtins(&mut self) {
1235        let math_float_vec = |name: &str| {
1236            let overloads = vec![
1237                (vec![ShaderType::Float], ShaderType::Float),
1238                (vec![ShaderType::Vec2], ShaderType::Vec2),
1239                (vec![ShaderType::Vec3], ShaderType::Vec3),
1240                (vec![ShaderType::Vec4], ShaderType::Vec4),
1241            ];
1242            BuiltinSignature { name: name.to_string(), overloads }
1243        };
1244        let names = &["sin","cos","tan","asin","acos","atan","sinh","cosh","tanh","exp","exp2","log","log2","sqrt","inversesqrt","abs","sign","floor","ceil","round","fract","normalize","length","radians","degrees"];
1245        for n in names {
1246            self.built_in_functions.insert(n.to_string(), math_float_vec(n));
1247        }
1248        self.built_in_functions.insert("pow".to_string(), BuiltinSignature { name: "pow".to_string(), overloads: vec![(vec![ShaderType::Float,ShaderType::Float],ShaderType::Float),(vec![ShaderType::Vec2,ShaderType::Vec2],ShaderType::Vec2),(vec![ShaderType::Vec3,ShaderType::Vec3],ShaderType::Vec3),(vec![ShaderType::Vec4,ShaderType::Vec4],ShaderType::Vec4)] });
1249        self.built_in_functions.insert("mix".to_string(), BuiltinSignature { name: "mix".to_string(), overloads: vec![(vec![ShaderType::Float,ShaderType::Float,ShaderType::Float],ShaderType::Float),(vec![ShaderType::Vec2,ShaderType::Vec2,ShaderType::Float],ShaderType::Vec2),(vec![ShaderType::Vec3,ShaderType::Vec3,ShaderType::Float],ShaderType::Vec3)] });
1250        self.built_in_functions.insert("clamp".to_string(), BuiltinSignature { name: "clamp".to_string(), overloads: vec![(vec![ShaderType::Float,ShaderType::Float,ShaderType::Float],ShaderType::Float),(vec![ShaderType::Vec2,ShaderType::Float,ShaderType::Float],ShaderType::Vec2)] });
1251        self.built_in_functions.insert("smoothstep".to_string(), BuiltinSignature { name: "smoothstep".to_string(), overloads: vec![(vec![ShaderType::Float,ShaderType::Float,ShaderType::Float],ShaderType::Float)] });
1252        self.built_in_functions.insert("dot".to_string(), BuiltinSignature { name: "dot".to_string(), overloads: vec![(vec![ShaderType::Vec2,ShaderType::Vec2],ShaderType::Float),(vec![ShaderType::Vec3,ShaderType::Vec3],ShaderType::Float),(vec![ShaderType::Vec4,ShaderType::Vec4],ShaderType::Float)] });
1253        self.built_in_functions.insert("cross".to_string(), BuiltinSignature { name: "cross".to_string(), overloads: vec![(vec![ShaderType::Vec3,ShaderType::Vec3],ShaderType::Vec3)] });
1254        self.built_in_functions.insert("reflect".to_string(), BuiltinSignature { name: "reflect".to_string(), overloads: vec![(vec![ShaderType::Vec3,ShaderType::Vec3],ShaderType::Vec3)] });
1255        self.built_in_functions.insert("refract".to_string(), BuiltinSignature { name: "refract".to_string(), overloads: vec![(vec![ShaderType::Vec3,ShaderType::Vec3,ShaderType::Float],ShaderType::Vec3)] });
1256        self.built_in_functions.insert("max".to_string(), BuiltinSignature { name: "max".to_string(), overloads: vec![(vec![ShaderType::Float,ShaderType::Float],ShaderType::Float)] });
1257        self.built_in_functions.insert("min".to_string(), BuiltinSignature { name: "min".to_string(), overloads: vec![(vec![ShaderType::Float,ShaderType::Float],ShaderType::Float)] });
1258        self.built_in_functions.insert("mod".to_string(), BuiltinSignature { name: "mod".to_string(), overloads: vec![(vec![ShaderType::Float,ShaderType::Float],ShaderType::Float),(vec![ShaderType::Vec2,ShaderType::Float],ShaderType::Vec2)] });
1259        self.built_in_functions.insert("texture".to_string(), BuiltinSignature { name: "texture".to_string(), overloads: vec![(vec![ShaderType::Sampler2D,ShaderType::Vec2],ShaderType::Vec4),(vec![ShaderType::SamplerCube,ShaderType::Vec3],ShaderType::Vec4)] });
1260        self.built_in_functions.insert("textureLod".to_string(), BuiltinSignature { name: "textureLod".to_string(), overloads: vec![(vec![ShaderType::Sampler2D,ShaderType::Vec2,ShaderType::Float],ShaderType::Vec4)] });
1261        self.built_in_functions.insert("dFdx".to_string(), BuiltinSignature { name: "dFdx".to_string(), overloads: vec![(vec![ShaderType::Float],ShaderType::Float),(vec![ShaderType::Vec2],ShaderType::Vec2),(vec![ShaderType::Vec3],ShaderType::Vec3)] });
1262        self.built_in_functions.insert("dFdy".to_string(), BuiltinSignature { name: "dFdy".to_string(), overloads: vec![(vec![ShaderType::Float],ShaderType::Float),(vec![ShaderType::Vec2],ShaderType::Vec2),(vec![ShaderType::Vec3],ShaderType::Vec3)] });
1263    }
1264
1265    fn push_scope(&mut self) { self.scopes.push(HashMap::new()); }
1266    fn pop_scope(&mut self) { self.scopes.pop(); }
1267    fn current_scope(&mut self) -> &mut HashMap<String, Symbol> { self.scopes.last_mut().unwrap() }
1268
1269    fn declare(&mut self, sym: Symbol) {
1270        let name = sym.name.clone();
1271        if self.current_scope().contains_key(&name) {
1272            self.errors.push(SemanticError { kind: SemanticErrorKind::RedefinedSymbol, message: format!("Redefined symbol '{}'", name), line: sym.defined_at_line });
1273        }
1274        self.current_scope().insert(name, sym);
1275    }
1276
1277    fn lookup(&self, name: &str) -> Option<&Symbol> {
1278        for scope in self.scopes.iter().rev() {
1279            if let Some(s) = scope.get(name) { return Some(s); }
1280        }
1281        None
1282    }
1283
1284    fn infer_type(&self, expr: &Expr) -> ShaderType {
1285        match expr {
1286            Expr::IntLit(_) => ShaderType::Int,
1287            Expr::FloatLit(_) => ShaderType::Float,
1288            Expr::BoolLit(_) => ShaderType::Bool,
1289            Expr::Ident(name) => {
1290                if let Some(sym) = self.lookup(name) { sym.ty.clone() } else { ShaderType::Unknown }
1291            }
1292            Expr::Binary { op, left, right } => {
1293                let lt = self.infer_type(left);
1294                let rt = self.infer_type(right);
1295                match op {
1296                    BinaryOp::Eq|BinaryOp::Ne|BinaryOp::Lt|BinaryOp::Gt|BinaryOp::Le|BinaryOp::Ge|BinaryOp::And|BinaryOp::Or => ShaderType::Bool,
1297                    _ => if lt == rt { lt } else if ShaderType::can_implicit_convert(&rt, &lt) { lt } else { lt }
1298                }
1299            }
1300            Expr::Unary { op, operand } => self.infer_type(operand),
1301            Expr::Call { function, args } => {
1302                if let Some(sig) = self.built_in_functions.get(function) {
1303                    let arg_types: Vec<ShaderType> = args.iter().map(|a| self.infer_type(a)).collect();
1304                    for (params, ret) in &sig.overloads {
1305                        if params.len() == arg_types.len() { return ret.clone(); }
1306                    }
1307                }
1308                ShaderType::Unknown
1309            }
1310            Expr::Field { object, field } => ShaderType::Float, // simplified
1311            Expr::Swizzle { object, components } => {
1312                let n = components.len();
1313                match n {
1314                    1 => ShaderType::Float,
1315                    2 => ShaderType::Vec2,
1316                    3 => ShaderType::Vec3,
1317                    4 => ShaderType::Vec4,
1318                    _ => ShaderType::Unknown,
1319                }
1320            }
1321            Expr::Index { array, .. } => {
1322                match self.infer_type(array) {
1323                    ShaderType::Array(inner, _) => *inner,
1324                    ShaderType::Vec2|ShaderType::Vec3|ShaderType::Vec4 => ShaderType::Float,
1325                    ShaderType::Mat2|ShaderType::Mat3|ShaderType::Mat4 => ShaderType::Vec4, // simplified
1326                    other => other,
1327                }
1328            }
1329            Expr::Construction { ty, .. } => ty.clone(),
1330            Expr::Cast { target_type, .. } => target_type.clone(),
1331            Expr::Assign { value, .. } => self.infer_type(value),
1332            Expr::Ternary { then_expr, .. } => self.infer_type(then_expr),
1333        }
1334    }
1335
1336    fn check_expr(&mut self, expr: &Expr) {
1337        match expr {
1338            Expr::Ident(name) => {
1339                if self.lookup(name).is_none() && !self.built_in_functions.contains_key(name.as_str()) {
1340                    // Only error if not a known builtin constant
1341                    let known = ["gl_Position","gl_FragCoord","gl_FragDepth","gl_VertexID","gl_InstanceID","true","false","PI","E"];
1342                    if !known.contains(&name.as_str()) {
1343                        self.errors.push(SemanticError { kind: SemanticErrorKind::UndeclaredVariable, message: format!("Undeclared variable '{}'", name), line: 0 });
1344                    }
1345                }
1346            }
1347            Expr::Binary { op, left, right } => {
1348                self.check_expr(left);
1349                self.check_expr(right);
1350                let lt = self.infer_type(left);
1351                let rt = self.infer_type(right);
1352                if lt != rt && !ShaderType::can_implicit_convert(&lt, &rt) && !ShaderType::can_implicit_convert(&rt, &lt) && lt != ShaderType::Unknown && rt != ShaderType::Unknown {
1353                    // warn about potential type mismatch
1354                }
1355            }
1356            Expr::Call { function, args } => {
1357                for a in args { self.check_expr(a); }
1358                if !self.built_in_functions.contains_key(function.as_str()) {
1359                    // Check user-defined function call for recursion
1360                    if let Some(fname) = &self.current_function {
1361                        if fname == function {
1362                            self.errors.push(SemanticError { kind: SemanticErrorKind::RecursionDetected, message: format!("Recursive call to '{}'", function), line: 0 });
1363                        }
1364                    }
1365                }
1366            }
1367            Expr::Assign { target, value, .. } => {
1368                self.check_expr(target);
1369                self.check_expr(value);
1370            }
1371            Expr::Unary { operand, .. } => self.check_expr(operand),
1372            Expr::Ternary { cond, then_expr, else_expr } => {
1373                self.check_expr(cond);
1374                self.check_expr(then_expr);
1375                self.check_expr(else_expr);
1376            }
1377            Expr::Field { object, .. } | Expr::Swizzle { object, .. } => self.check_expr(object),
1378            Expr::Index { array, index } => { self.check_expr(array); self.check_expr(index); }
1379            Expr::Construction { args, .. } => { for a in args { self.check_expr(a); } }
1380            _ => {}
1381        }
1382    }
1383
1384    fn check_stmt(&mut self, stmt: &Stmt) {
1385        match stmt {
1386            Stmt::Expr(e) => self.check_expr(e),
1387            Stmt::Block(stmts) => {
1388                self.push_scope();
1389                for s in stmts { self.check_stmt(s); }
1390                self.pop_scope();
1391            }
1392            Stmt::VarDecl { ty, name, qualifier, init } => {
1393                if let Some(init_expr) = init { self.check_expr(init_expr); }
1394                self.declare(Symbol { name: name.clone(), ty: ty.clone(), qualifier: *qualifier, used: false, defined_at_line: 0 });
1395            }
1396            Stmt::If { cond, then_body, else_body } => {
1397                self.check_expr(cond);
1398                self.check_stmt(then_body);
1399                if let Some(e) = else_body { self.check_stmt(e); }
1400            }
1401            Stmt::For { init, cond, step, body } => {
1402                self.push_scope();
1403                if let Some(i) = init { self.check_stmt(i); }
1404                if let Some(c) = cond { self.check_expr(c); }
1405                if let Some(s) = step { self.check_expr(s); }
1406                self.check_stmt(body);
1407                self.pop_scope();
1408            }
1409            Stmt::While { cond, body } => { self.check_expr(cond); self.check_stmt(body); }
1410            Stmt::DoWhile { body, cond } => { self.check_stmt(body); self.check_expr(cond); }
1411            Stmt::Return(val) => { if let Some(v) = val { self.check_expr(v); } }
1412            _ => {}
1413        }
1414    }
1415
1416    pub fn analyze(&mut self, ast: &ShaderAst) {
1417        // Register global uniforms
1418        for u in &ast.uniforms {
1419            self.declare(Symbol { name: u.name.clone(), ty: u.ty.clone(), qualifier: Some(VarQualifier::Uniform), used: false, defined_at_line: 0 });
1420        }
1421        for a in &ast.attributes {
1422            self.declare(Symbol { name: a.name.clone(), ty: a.ty.clone(), qualifier: Some(VarQualifier::In), used: false, defined_at_line: 0 });
1423        }
1424        for v in &ast.varyings {
1425            self.declare(Symbol { name: v.name.clone(), ty: v.ty.clone(), qualifier: Some(VarQualifier::Out), used: false, defined_at_line: 0 });
1426        }
1427        // Register struct types
1428        for s in &ast.structs {
1429            self.declare(Symbol { name: s.name.clone(), ty: ShaderType::Struct(s.name.clone()), qualifier: None, used: false, defined_at_line: 0 });
1430        }
1431        // Analyze functions
1432        for func in &ast.functions {
1433            self.current_function = Some(func.name.clone());
1434            self.push_scope();
1435            for param in &func.params {
1436                self.declare(Symbol { name: param.name.clone(), ty: param.ty.clone(), qualifier: param.qualifier, used: false, defined_at_line: 0 });
1437            }
1438            for stmt in &func.body { self.check_stmt(stmt); }
1439            self.pop_scope();
1440        }
1441        self.current_function = None;
1442    }
1443}
1444
1445// ============================================================
1446// SECTION 7: CONSTANT FOLDING
1447// ============================================================
1448
1449pub fn fold_expr(expr: Expr) -> Expr {
1450    match expr {
1451        Expr::Binary { op, left, right } => {
1452            let left = fold_expr(*left);
1453            let right = fold_expr(*right);
1454            match (&left, &right) {
1455                (Expr::FloatLit(a), Expr::FloatLit(b)) => {
1456                    match op {
1457                        BinaryOp::Add => Expr::FloatLit(a + b),
1458                        BinaryOp::Sub => Expr::FloatLit(a - b),
1459                        BinaryOp::Mul => Expr::FloatLit(a * b),
1460                        BinaryOp::Div => if *b != 0.0 { Expr::FloatLit(a / b) } else { Expr::Binary { op, left: Box::new(left), right: Box::new(right) } },
1461                        BinaryOp::Eq => Expr::BoolLit(a == b),
1462                        BinaryOp::Ne => Expr::BoolLit(a != b),
1463                        BinaryOp::Lt => Expr::BoolLit(a < b),
1464                        BinaryOp::Gt => Expr::BoolLit(a > b),
1465                        BinaryOp::Le => Expr::BoolLit(a <= b),
1466                        BinaryOp::Ge => Expr::BoolLit(a >= b),
1467                        _ => Expr::Binary { op, left: Box::new(left), right: Box::new(right) },
1468                    }
1469                }
1470                (Expr::IntLit(a), Expr::IntLit(b)) => {
1471                    match op {
1472                        BinaryOp::Add => Expr::IntLit(a + b),
1473                        BinaryOp::Sub => Expr::IntLit(a - b),
1474                        BinaryOp::Mul => Expr::IntLit(a * b),
1475                        BinaryOp::Div => if *b != 0 { Expr::IntLit(a / b) } else { Expr::Binary { op, left: Box::new(left), right: Box::new(right) } },
1476                        BinaryOp::Mod => if *b != 0 { Expr::IntLit(a % b) } else { Expr::Binary { op, left: Box::new(left), right: Box::new(right) } },
1477                        BinaryOp::BitAnd => Expr::IntLit(a & b),
1478                        BinaryOp::BitOr => Expr::IntLit(a | b),
1479                        BinaryOp::BitXor => Expr::IntLit(a ^ b),
1480                        BinaryOp::Shl => Expr::IntLit(a << (b & 63)),
1481                        BinaryOp::Shr => Expr::IntLit(a >> (b & 63)),
1482                        BinaryOp::Eq => Expr::BoolLit(a == b),
1483                        BinaryOp::Ne => Expr::BoolLit(a != b),
1484                        BinaryOp::Lt => Expr::BoolLit(a < b),
1485                        BinaryOp::Gt => Expr::BoolLit(a > b),
1486                        BinaryOp::Le => Expr::BoolLit(a <= b),
1487                        BinaryOp::Ge => Expr::BoolLit(a >= b),
1488                        _ => Expr::Binary { op, left: Box::new(left), right: Box::new(right) },
1489                    }
1490                }
1491                (Expr::BoolLit(a), Expr::BoolLit(b)) => {
1492                    match op {
1493                        BinaryOp::And => Expr::BoolLit(*a && *b),
1494                        BinaryOp::Or => Expr::BoolLit(*a || *b),
1495                        BinaryOp::Eq => Expr::BoolLit(a == b),
1496                        BinaryOp::Ne => Expr::BoolLit(a != b),
1497                        _ => Expr::Binary { op, left: Box::new(left), right: Box::new(right) },
1498                    }
1499                }
1500                // Algebraic simplifications
1501                (_, Expr::FloatLit(v)) if op == BinaryOp::Mul && *v == 1.0 => left,
1502                (Expr::FloatLit(v), _) if op == BinaryOp::Mul && *v == 1.0 => right,
1503                (_, Expr::FloatLit(v)) if op == BinaryOp::Mul && *v == 0.0 => Expr::FloatLit(0.0),
1504                (Expr::FloatLit(v), _) if op == BinaryOp::Mul && *v == 0.0 => Expr::FloatLit(0.0),
1505                (_, Expr::FloatLit(v)) if op == BinaryOp::Add && *v == 0.0 => left,
1506                (Expr::FloatLit(v), _) if op == BinaryOp::Add && *v == 0.0 => right,
1507                (_, Expr::FloatLit(v)) if op == BinaryOp::Sub && *v == 0.0 => left,
1508                (_, Expr::FloatLit(v)) if op == BinaryOp::Div && *v == 1.0 => left,
1509                (_, Expr::IntLit(v)) if op == BinaryOp::Mul && *v == 1 => left,
1510                (Expr::IntLit(v), _) if op == BinaryOp::Mul && *v == 1 => right,
1511                (_, Expr::IntLit(v)) if op == BinaryOp::Add && *v == 0 => left,
1512                (Expr::IntLit(v), _) if op == BinaryOp::Add && *v == 0 => right,
1513                _ => Expr::Binary { op, left: Box::new(left), right: Box::new(right) },
1514            }
1515        }
1516        Expr::Unary { op, operand } => {
1517            let operand = fold_expr(*operand);
1518            match (&operand, op) {
1519                (Expr::FloatLit(v), UnaryOp::Negate) => Expr::FloatLit(-v),
1520                (Expr::IntLit(v), UnaryOp::Negate) => Expr::IntLit(-v),
1521                (Expr::BoolLit(v), UnaryOp::Not) => Expr::BoolLit(!v),
1522                (Expr::IntLit(v), UnaryOp::BitNot) => Expr::IntLit(!v),
1523                _ => Expr::Unary { op, operand: Box::new(operand) },
1524            }
1525        }
1526        Expr::Ternary { cond, then_expr, else_expr } => {
1527            let cond = fold_expr(*cond);
1528            let then_expr = fold_expr(*then_expr);
1529            let else_expr = fold_expr(*else_expr);
1530            match &cond {
1531                Expr::BoolLit(true) => then_expr,
1532                Expr::BoolLit(false) => else_expr,
1533                _ => Expr::Ternary { cond: Box::new(cond), then_expr: Box::new(then_expr), else_expr: Box::new(else_expr) },
1534            }
1535        }
1536        Expr::Call { function, args } => {
1537            let args: Vec<Expr> = args.into_iter().map(fold_expr).collect();
1538            // Fold constant built-in calls
1539            let all_float_lits: Vec<f64> = args.iter().filter_map(|a| if let Expr::FloatLit(v) = a { Some(*v) } else { None }).collect();
1540            if all_float_lits.len() == args.len() {
1541                let result = fold_builtin_call(&function, &all_float_lits);
1542                if let Some(v) = result { return Expr::FloatLit(v); }
1543            }
1544            Expr::Call { function, args }
1545        }
1546        other => other,
1547    }
1548}
1549
1550fn fold_builtin_call(name: &str, args: &[f64]) -> Option<f64> {
1551    match (name, args) {
1552        ("sin", [x]) => Some(x.sin()),
1553        ("cos", [x]) => Some(x.cos()),
1554        ("tan", [x]) => Some(x.tan()),
1555        ("asin", [x]) => Some(x.asin()),
1556        ("acos", [x]) => Some(x.acos()),
1557        ("atan", [x]) => Some(x.atan()),
1558        ("sqrt", [x]) if *x >= 0.0 => Some(x.sqrt()),
1559        ("inversesqrt", [x]) if *x > 0.0 => Some(1.0 / x.sqrt()),
1560        ("exp", [x]) => Some(x.exp()),
1561        ("exp2", [x]) => Some(x.exp2()),
1562        ("log", [x]) if *x > 0.0 => Some(x.ln()),
1563        ("log2", [x]) if *x > 0.0 => Some(x.log2()),
1564        ("abs", [x]) => Some(x.abs()),
1565        ("sign", [x]) => Some(x.signum()),
1566        ("floor", [x]) => Some(x.floor()),
1567        ("ceil", [x]) => Some(x.ceil()),
1568        ("round", [x]) => Some(x.round()),
1569        ("fract", [x]) => Some(x.fract()),
1570        ("pow", [x, y]) => Some(x.powf(*y)),
1571        ("min", [a, b]) => Some(a.min(*b)),
1572        ("max", [a, b]) => Some(a.max(*b)),
1573        ("clamp", [x, mn, mx]) => Some(x.clamp(*mn, *mx)),
1574        ("mix", [a, b, t]) => Some(a + (b - a) * t),
1575        ("smoothstep", [e0, e1, x]) => {
1576            let t = ((x - e0) / (e1 - e0)).clamp(0.0, 1.0);
1577            Some(t * t * (3.0 - 2.0 * t))
1578        }
1579        ("radians", [x]) => Some(x.to_radians()),
1580        ("degrees", [x]) => Some(x.to_degrees()),
1581        _ => None,
1582    }
1583}
1584
1585// ============================================================
1586// SECTION 8: DEAD CODE ELIMINATION
1587// ============================================================
1588
1589pub fn eliminate_dead_code(stmts: Vec<Stmt>) -> Vec<Stmt> {
1590    let mut result = Vec::new();
1591    let mut unreachable = false;
1592    for stmt in stmts {
1593        if unreachable { break; }
1594        match stmt {
1595            Stmt::If { ref cond, ref then_body, ref else_body } => {
1596                if let Expr::BoolLit(true) = cond {
1597                    // Always-true: keep then, discard else
1598                    result.push(*then_body.clone());
1599                } else if let Expr::BoolLit(false) = cond {
1600                    // Always-false: keep else if present
1601                    if let Some(e) = else_body { result.push(*e.clone()); }
1602                } else {
1603                    result.push(stmt);
1604                }
1605            }
1606            Stmt::Return(_) | Stmt::Break | Stmt::Continue | Stmt::Discard => {
1607                result.push(stmt);
1608                unreachable = true;
1609            }
1610            Stmt::Block(inner) => {
1611                let cleaned = eliminate_dead_code(inner);
1612                result.push(Stmt::Block(cleaned));
1613            }
1614            other => result.push(other),
1615        }
1616    }
1617    result
1618}
1619
1620// ============================================================
1621// SECTION 9: COMMON SUBEXPRESSION ELIMINATION
1622// ============================================================
1623
1624pub struct CSEPass {
1625    pub expressions: HashMap<String, String>, // expr_key -> temp_var_name
1626    pub temps: Vec<(String, Expr)>,
1627    pub counter: u32,
1628}
1629
1630impl CSEPass {
1631    pub fn new() -> Self { Self { expressions: HashMap::new(), temps: Vec::new(), counter: 0 } }
1632    pub fn expr_key(expr: &Expr) -> Option<String> {
1633        match expr {
1634            Expr::Binary { op, left, right } => {
1635                let lk = Self::expr_key(left)?;
1636                let rk = Self::expr_key(right)?;
1637                Some(format!("{:?}_{}_{}", op, lk, rk))
1638            }
1639            Expr::Call { function, args } => {
1640                let arg_keys: Vec<String> = args.iter().filter_map(|a| Self::expr_key(a)).collect();
1641                if arg_keys.len() == args.len() { Some(format!("{}({})", function, arg_keys.join(","))) } else { None }
1642            }
1643            Expr::FloatLit(v) => Some(format!("f{:.6}", v)),
1644            Expr::IntLit(v) => Some(format!("i{}", v)),
1645            Expr::BoolLit(v) => Some(format!("b{}", v)),
1646            Expr::Ident(n) => Some(n.clone()),
1647            _ => None,
1648        }
1649    }
1650    pub fn process_expr(&mut self, expr: Expr) -> Expr {
1651        match expr {
1652            Expr::Binary { op, left, right } => {
1653                let left = self.process_expr(*left);
1654                let right = self.process_expr(*right);
1655                let folded = Expr::Binary { op, left: Box::new(left), right: Box::new(right) };
1656                if let Some(key) = Self::expr_key(&folded) {
1657                    if let Some(temp) = self.expressions.get(&key) {
1658                        return Expr::Ident(temp.clone());
1659                    }
1660                    let temp_name = format!("_cse_{}", self.counter);
1661                    self.counter += 1;
1662                    self.expressions.insert(key, temp_name.clone());
1663                    self.temps.push((temp_name.clone(), folded));
1664                    Expr::Ident(temp_name)
1665                } else { folded }
1666            }
1667            Expr::Call { function, args } => {
1668                let args: Vec<Expr> = args.into_iter().map(|a| self.process_expr(a)).collect();
1669                let e = Expr::Call { function, args };
1670                if let Some(key) = Self::expr_key(&e) {
1671                    if let Some(temp) = self.expressions.get(&key) {
1672                        return Expr::Ident(temp.clone());
1673                    }
1674                }
1675                e
1676            }
1677            other => other,
1678        }
1679    }
1680}
1681
1682// ============================================================
1683// SECTION 10: CODE GENERATION - GLSL
1684// ============================================================
1685
1686pub struct GlslEmitter {
1687    pub output: String,
1688    pub indent: usize,
1689    pub version: u32,
1690    pub es: bool,
1691}
1692
1693impl GlslEmitter {
1694    pub fn new(version: u32, es: bool) -> Self { Self { output: String::new(), indent: 0, version, es } }
1695
1696    fn write(&mut self, s: &str) { self.output.push_str(s); }
1697    fn writeln(&mut self, s: &str) {
1698        let indent: String = " ".repeat(self.indent * 4);
1699        self.output.push_str(&indent);
1700        self.output.push_str(s);
1701        self.output.push('\n');
1702    }
1703    fn indent_in(&mut self) { self.indent += 1; }
1704    fn indent_out(&mut self) { if self.indent > 0 { self.indent -= 1; } }
1705
1706    pub fn emit_header(&mut self, ast: &ShaderAst) {
1707        if self.es {
1708            self.writeln(&format!("#version {} es", self.version));
1709        } else {
1710            self.writeln(&format!("#version {} core", self.version));
1711        }
1712        for (name, val) in &ast.defines {
1713            self.writeln(&format!("#define {} {}", name, val));
1714        }
1715    }
1716
1717    pub fn emit_type(&self, ty: &ShaderType) -> String {
1718        ty.name().to_string()
1719    }
1720
1721    pub fn emit_struct(&mut self, s: &StructDecl) {
1722        self.writeln(&format!("struct {} {{", s.name));
1723        self.indent_in();
1724        for f in &s.fields {
1725            let arr = if let Some(n) = f.array_size { format!("[{}]", n) } else { String::new() };
1726            self.writeln(&format!("{} {}{};", self.emit_type(&f.ty), f.name, arr));
1727        }
1728        self.indent_out();
1729        self.writeln("};");
1730    }
1731
1732    pub fn emit_uniform(&mut self, u: &UniformDecl) {
1733        let layout = if u.binding.is_some() || u.set.is_some() {
1734            let parts: Vec<String> = [
1735                u.set.map(|s| format!("set={}", s)),
1736                u.binding.map(|b| format!("binding={}", b)),
1737            ].iter().filter_map(|x| x.clone()).collect();
1738            if parts.is_empty() { String::new() } else { format!("layout({}) ", parts.join(", ")) }
1739        } else { String::new() };
1740        let arr = if let Some(n) = u.array_size { format!("[{}]", n) } else { String::new() };
1741        self.writeln(&format!("{}uniform {} {}{};", layout, self.emit_type(&u.ty), u.name, arr));
1742    }
1743
1744    pub fn emit_expr(&self, expr: &Expr) -> String {
1745        match expr {
1746            Expr::IntLit(v) => v.to_string(),
1747            Expr::FloatLit(v) => {
1748                if *v == v.floor() && v.abs() < 1e15 { format!("{:.1}", v) } else { format!("{}", v) }
1749            }
1750            Expr::BoolLit(v) => v.to_string(),
1751            Expr::Ident(n) => n.clone(),
1752            Expr::Unary { op, operand } => {
1753                let o = self.emit_expr(operand);
1754                match op {
1755                    UnaryOp::Negate => format!("(-{})", o),
1756                    UnaryOp::Not => format!("(!{})", o),
1757                    UnaryOp::BitNot => format!("(~{})", o),
1758                    UnaryOp::PreIncrement => format!("(++{})", o),
1759                    UnaryOp::PreDecrement => format!("(--{})", o),
1760                    UnaryOp::PostIncrement => format!("({}++)", o),
1761                    UnaryOp::PostDecrement => format!("({}--)", o),
1762                }
1763            }
1764            Expr::Binary { op, left, right } => {
1765                let l = self.emit_expr(left);
1766                let r = self.emit_expr(right);
1767                let sym = match op {
1768                    BinaryOp::Add => "+", BinaryOp::Sub => "-", BinaryOp::Mul => "*", BinaryOp::Div => "/",
1769                    BinaryOp::Mod => "%", BinaryOp::Eq => "==", BinaryOp::Ne => "!=",
1770                    BinaryOp::Lt => "<", BinaryOp::Gt => ">", BinaryOp::Le => "<=", BinaryOp::Ge => ">=",
1771                    BinaryOp::And => "&&", BinaryOp::Or => "||",
1772                    BinaryOp::BitAnd => "&", BinaryOp::BitOr => "|", BinaryOp::BitXor => "^",
1773                    BinaryOp::Shl => "<<", BinaryOp::Shr => ">>",
1774                };
1775                format!("({} {} {})", l, sym, r)
1776            }
1777            Expr::Assign { op, target, value } => {
1778                let t = self.emit_expr(target);
1779                let v = self.emit_expr(value);
1780                let sym = match op {
1781                    AssignOp::Assign => "=", AssignOp::AddAssign => "+=", AssignOp::SubAssign => "-=",
1782                    AssignOp::MulAssign => "*=", AssignOp::DivAssign => "/=", AssignOp::ModAssign => "%=",
1783                    AssignOp::AndAssign => "&=", AssignOp::OrAssign => "|=", AssignOp::XorAssign => "^=",
1784                    AssignOp::ShlAssign => "<<=", AssignOp::ShrAssign => ">>=",
1785                };
1786                format!("{} {} {}", t, sym, v)
1787            }
1788            Expr::Ternary { cond, then_expr, else_expr } => {
1789                format!("({} ? {} : {})", self.emit_expr(cond), self.emit_expr(then_expr), self.emit_expr(else_expr))
1790            }
1791            Expr::Call { function, args } => {
1792                let arg_strs: Vec<String> = args.iter().map(|a| self.emit_expr(a)).collect();
1793                format!("{}({})", function, arg_strs.join(", "))
1794            }
1795            Expr::Index { array, index } => format!("{}[{}]", self.emit_expr(array), self.emit_expr(index)),
1796            Expr::Field { object, field } => format!("{}.{}", self.emit_expr(object), field),
1797            Expr::Swizzle { object, components } => format!("{}.{}", self.emit_expr(object), components),
1798            Expr::Cast { target_type, expr } => format!("{}({})", target_type.name(), self.emit_expr(expr)),
1799            Expr::Construction { ty, args } => {
1800                let arg_strs: Vec<String> = args.iter().map(|a| self.emit_expr(a)).collect();
1801                format!("{}({})", ty.name(), arg_strs.join(", "))
1802            }
1803        }
1804    }
1805
1806    pub fn emit_stmt(&mut self, stmt: &Stmt) {
1807        match stmt {
1808            Stmt::Expr(e) => {
1809                let s = self.emit_expr(e);
1810                self.writeln(&format!("{};", s));
1811            }
1812            Stmt::Block(stmts) => {
1813                self.writeln("{");
1814                self.indent_in();
1815                for s in stmts { self.emit_stmt(s); }
1816                self.indent_out();
1817                self.writeln("}");
1818            }
1819            Stmt::VarDecl { ty, name, qualifier, init } => {
1820                let qual = match qualifier {
1821                    Some(VarQualifier::Const) => "const ".to_string(),
1822                    Some(VarQualifier::In) => "in ".to_string(),
1823                    Some(VarQualifier::Out) => "out ".to_string(),
1824                    Some(VarQualifier::Inout) => "inout ".to_string(),
1825                    _ => String::new(),
1826                };
1827                if let Some(init_expr) = init {
1828                    self.writeln(&format!("{}{} {} = {};", qual, self.emit_type(ty), name, self.emit_expr(init_expr)));
1829                } else {
1830                    self.writeln(&format!("{}{} {};", qual, self.emit_type(ty), name));
1831                }
1832            }
1833            Stmt::If { cond, then_body, else_body } => {
1834                let cond_str = self.emit_expr(cond);
1835                self.writeln(&format!("if ({}) {{", cond_str));
1836                self.indent_in();
1837                self.emit_stmt(then_body);
1838                self.indent_out();
1839                if let Some(else_stmt) = else_body {
1840                    self.writeln("} else {");
1841                    self.indent_in();
1842                    self.emit_stmt(else_stmt);
1843                    self.indent_out();
1844                    self.writeln("}");
1845                } else {
1846                    self.writeln("}");
1847                }
1848            }
1849            Stmt::For { init, cond, step, body } => {
1850                let init_str = if let Some(i) = init {
1851                    match i.as_ref() {
1852                        Stmt::VarDecl { ty, name, init: Some(e), .. } => format!("{} {} = {}", self.emit_type(ty), name, self.emit_expr(e)),
1853                        Stmt::Expr(e) => self.emit_expr(e),
1854                        _ => String::new(),
1855                    }
1856                } else { String::new() };
1857                let cond_str = cond.as_ref().map(|c| self.emit_expr(c)).unwrap_or_default();
1858                let step_str = step.as_ref().map(|s| self.emit_expr(s)).unwrap_or_default();
1859                self.writeln(&format!("for ({}; {}; {}) {{", init_str, cond_str, step_str));
1860                self.indent_in();
1861                self.emit_stmt(body);
1862                self.indent_out();
1863                self.writeln("}");
1864            }
1865            Stmt::While { cond, body } => {
1866                self.writeln(&format!("while ({}) {{", self.emit_expr(cond)));
1867                self.indent_in();
1868                self.emit_stmt(body);
1869                self.indent_out();
1870                self.writeln("}");
1871            }
1872            Stmt::DoWhile { body, cond } => {
1873                self.writeln("do {");
1874                self.indent_in();
1875                self.emit_stmt(body);
1876                self.indent_out();
1877                self.writeln(&format!("}} while ({});", self.emit_expr(cond)));
1878            }
1879            Stmt::Return(val) => {
1880                if let Some(v) = val {
1881                    self.writeln(&format!("return {};", self.emit_expr(v)));
1882                } else {
1883                    self.writeln("return;");
1884                }
1885            }
1886            Stmt::Break => self.writeln("break;"),
1887            Stmt::Continue => self.writeln("continue;"),
1888            Stmt::Discard => self.writeln("discard;"),
1889        }
1890    }
1891
1892    pub fn emit_function(&mut self, func: &FunctionDecl) {
1893        let params: Vec<String> = func.params.iter().map(|p| {
1894            let q = match p.qualifier {
1895                Some(VarQualifier::In) => "in ",
1896                Some(VarQualifier::Out) => "out ",
1897                Some(VarQualifier::Inout) => "inout ",
1898                _ => "",
1899            };
1900            format!("{}{} {}", q, self.emit_type(&p.ty), p.name)
1901        }).collect();
1902        self.writeln(&format!("{} {}({}) {{", self.emit_type(&func.return_type), func.name, params.join(", ")));
1903        self.indent_in();
1904        for stmt in &func.body { self.emit_stmt(stmt); }
1905        self.indent_out();
1906        self.writeln("}");
1907    }
1908
1909    pub fn emit_ast(&mut self, ast: &ShaderAst) -> String {
1910        self.emit_header(ast);
1911        self.write("\n");
1912        for s in &ast.structs { self.emit_struct(s); self.write("\n"); }
1913        for u in &ast.uniforms { self.emit_uniform(u); }
1914        if !ast.uniforms.is_empty() { self.write("\n"); }
1915        for a in &ast.attributes {
1916            self.writeln(&format!("in {} {};", self.emit_type(&a.ty), a.name));
1917        }
1918        for v in &ast.varyings {
1919            self.writeln(&format!("out {} {};", self.emit_type(&v.ty), v.name));
1920        }
1921        if !ast.attributes.is_empty() || !ast.varyings.is_empty() { self.write("\n"); }
1922        for func in &ast.functions {
1923            self.emit_function(func);
1924            self.write("\n");
1925        }
1926        self.output.clone()
1927    }
1928}
1929
1930// ============================================================
1931// SECTION 11: SPIRV-LIKE BYTECODE
1932// ============================================================
1933
1934#[derive(Debug, Clone)]
1935pub enum SpvOp {
1936    LoadConst { dest: u32, value: SpvConst },
1937    Load { dest: u32, src_var: u32 },
1938    Store { dest_var: u32, src: u32 },
1939    Add { dest: u32, a: u32, b: u32 },
1940    Sub { dest: u32, a: u32, b: u32 },
1941    Mul { dest: u32, a: u32, b: u32 },
1942    Div { dest: u32, a: u32, b: u32 },
1943    FuncCall { dest: u32, func_id: u32, args: Vec<u32> },
1944    Jump { label: u32 },
1945    JumpIf { cond: u32, label_true: u32, label_false: u32 },
1946    Label { id: u32 },
1947    Return { value: Option<u32> },
1948    Phi { dest: u32, pairs: Vec<(u32, u32)> },
1949    Convert { dest: u32, src: u32, to_type: SpvTypeId },
1950    Negate { dest: u32, src: u32 },
1951    Not { dest: u32, src: u32 },
1952    Eq { dest: u32, a: u32, b: u32 },
1953    Ne { dest: u32, a: u32, b: u32 },
1954    Lt { dest: u32, a: u32, b: u32 },
1955    Gt { dest: u32, a: u32, b: u32 },
1956    Le { dest: u32, a: u32, b: u32 },
1957    Ge { dest: u32, a: u32, b: u32 },
1958}
1959
1960#[derive(Debug, Clone)]
1961pub enum SpvConst {
1962    Float(f32),
1963    Int(i32),
1964    Bool(bool),
1965    Vec2([f32;2]),
1966    Vec3([f32;3]),
1967    Vec4([f32;4]),
1968}
1969
1970#[derive(Debug, Clone, Copy)]
1971pub enum SpvTypeId { Float, Int, Uint, Bool, Vec2, Vec3, Vec4, Mat4 }
1972
1973#[derive(Debug, Clone)]
1974pub struct SpvFunction {
1975    pub id: u32,
1976    pub name: String,
1977    pub return_type: SpvTypeId,
1978    pub instructions: Vec<SpvOp>,
1979}
1980
1981#[derive(Debug, Clone)]
1982pub struct SpvModule {
1983    pub functions: Vec<SpvFunction>,
1984    pub global_variables: Vec<(u32, SpvTypeId, String)>,
1985    pub next_id: u32,
1986}
1987
1988impl SpvModule {
1989    pub fn new() -> Self { Self { functions: Vec::new(), global_variables: Vec::new(), next_id: 1 } }
1990    pub fn alloc_id(&mut self) -> u32 { let id = self.next_id; self.next_id += 1; id }
1991}
1992
1993pub struct SpvCodegen {
1994    pub module: SpvModule,
1995    pub var_map: HashMap<String, u32>,
1996    pub current_fn_instrs: Vec<SpvOp>,
1997    pub label_counter: u32,
1998}
1999
2000impl SpvCodegen {
2001    pub fn new() -> Self { Self { module: SpvModule::new(), var_map: HashMap::new(), current_fn_instrs: Vec::new(), label_counter: 0 } }
2002    pub fn new_label(&mut self) -> u32 { let l = self.label_counter; self.label_counter += 1; l }
2003
2004    pub fn emit_expr_spv(&mut self, expr: &Expr) -> u32 {
2005        match expr {
2006            Expr::FloatLit(v) => {
2007                let dest = self.module.alloc_id();
2008                self.current_fn_instrs.push(SpvOp::LoadConst { dest, value: SpvConst::Float(*v as f32) });
2009                dest
2010            }
2011            Expr::IntLit(v) => {
2012                let dest = self.module.alloc_id();
2013                self.current_fn_instrs.push(SpvOp::LoadConst { dest, value: SpvConst::Int(*v as i32) });
2014                dest
2015            }
2016            Expr::BoolLit(v) => {
2017                let dest = self.module.alloc_id();
2018                self.current_fn_instrs.push(SpvOp::LoadConst { dest, value: SpvConst::Bool(*v) });
2019                dest
2020            }
2021            Expr::Ident(name) => {
2022                let var_id = *self.var_map.get(name).unwrap_or(&0);
2023                let dest = self.module.alloc_id();
2024                self.current_fn_instrs.push(SpvOp::Load { dest, src_var: var_id });
2025                dest
2026            }
2027            Expr::Binary { op, left, right } => {
2028                let a = self.emit_expr_spv(left);
2029                let b = self.emit_expr_spv(right);
2030                let dest = self.module.alloc_id();
2031                let instr = match op {
2032                    BinaryOp::Add => SpvOp::Add { dest, a, b },
2033                    BinaryOp::Sub => SpvOp::Sub { dest, a, b },
2034                    BinaryOp::Mul => SpvOp::Mul { dest, a, b },
2035                    BinaryOp::Div => SpvOp::Div { dest, a, b },
2036                    BinaryOp::Eq => SpvOp::Eq { dest, a, b },
2037                    BinaryOp::Ne => SpvOp::Ne { dest, a, b },
2038                    BinaryOp::Lt => SpvOp::Lt { dest, a, b },
2039                    BinaryOp::Gt => SpvOp::Gt { dest, a, b },
2040                    BinaryOp::Le => SpvOp::Le { dest, a, b },
2041                    BinaryOp::Ge => SpvOp::Ge { dest, a, b },
2042                    _ => SpvOp::Add { dest, a, b }, // fallback
2043                };
2044                self.current_fn_instrs.push(instr);
2045                dest
2046            }
2047            Expr::Unary { op, operand } => {
2048                let src = self.emit_expr_spv(operand);
2049                let dest = self.module.alloc_id();
2050                let instr = match op {
2051                    UnaryOp::Negate => SpvOp::Negate { dest, src },
2052                    UnaryOp::Not => SpvOp::Not { dest, src },
2053                    _ => SpvOp::Negate { dest, src },
2054                };
2055                self.current_fn_instrs.push(instr);
2056                dest
2057            }
2058            _ => { self.module.alloc_id() }
2059        }
2060    }
2061}
2062
2063// ============================================================
2064// SECTION 12: WGSL EMITTER
2065// ============================================================
2066
2067pub struct WgslEmitter {
2068    pub output: String,
2069    pub indent: usize,
2070}
2071
2072impl WgslEmitter {
2073    pub fn new() -> Self { Self { output: String::new(), indent: 0 } }
2074    fn writeln(&mut self, s: &str) {
2075        let indent = "    ".repeat(self.indent);
2076        self.output.push_str(&indent);
2077        self.output.push_str(s);
2078        self.output.push('\n');
2079    }
2080    fn indent_in(&mut self) { self.indent += 1; }
2081    fn indent_out(&mut self) { if self.indent > 0 { self.indent -= 1; } }
2082
2083    pub fn wgsl_type(ty: &ShaderType) -> &'static str {
2084        match ty {
2085            ShaderType::Float => "f32",
2086            ShaderType::Int => "i32",
2087            ShaderType::Uint => "u32",
2088            ShaderType::Bool => "bool",
2089            ShaderType::Vec2 => "vec2<f32>",
2090            ShaderType::Vec3 => "vec3<f32>",
2091            ShaderType::Vec4 => "vec4<f32>",
2092            ShaderType::IVec2 => "vec2<i32>",
2093            ShaderType::IVec3 => "vec3<i32>",
2094            ShaderType::IVec4 => "vec4<i32>",
2095            ShaderType::Mat2 => "mat2x2<f32>",
2096            ShaderType::Mat3 => "mat3x3<f32>",
2097            ShaderType::Mat4 => "mat4x4<f32>",
2098            ShaderType::Sampler2D => "texture_2d<f32>",
2099            ShaderType::SamplerCube => "texture_cube<f32>",
2100            _ => "f32",
2101        }
2102    }
2103
2104    pub fn emit_uniform_wgsl(&mut self, u: &UniformDecl, group: u32) {
2105        let binding = u.binding.unwrap_or(0);
2106        self.writeln(&format!("@group({}) @binding({}) var<uniform> {}: {};", group, binding, u.name, Self::wgsl_type(&u.ty)));
2107    }
2108
2109    pub fn emit_function_wgsl(&mut self, func: &FunctionDecl) {
2110        let params: Vec<String> = func.params.iter().map(|p| {
2111            format!("{}: {}", p.name, Self::wgsl_type(&p.ty))
2112        }).collect();
2113        let ret = if func.return_type == ShaderType::Void {
2114            String::new()
2115        } else {
2116            format!(" -> {}", Self::wgsl_type(&func.return_type))
2117        };
2118        self.writeln(&format!("fn {}({}){} {{", func.name, params.join(", "), ret));
2119        self.indent_in();
2120        for stmt in &func.body { self.emit_stmt_wgsl(stmt); }
2121        self.indent_out();
2122        self.writeln("}");
2123    }
2124
2125    fn emit_expr_wgsl(&self, expr: &Expr) -> String {
2126        // WGSL expression emission (simplified)
2127        match expr {
2128            Expr::FloatLit(v) => format!("{:.6}f", v),
2129            Expr::IntLit(v) => format!("{}i", v),
2130            Expr::BoolLit(v) => v.to_string(),
2131            Expr::Ident(n) => n.clone(),
2132            Expr::Binary { op, left, right } => {
2133                let l = self.emit_expr_wgsl(left);
2134                let r = self.emit_expr_wgsl(right);
2135                let sym = match op {
2136                    BinaryOp::Add => "+", BinaryOp::Sub => "-", BinaryOp::Mul => "*", BinaryOp::Div => "/",
2137                    BinaryOp::Mod => "%", BinaryOp::Eq => "==", BinaryOp::Ne => "!=",
2138                    BinaryOp::Lt => "<", BinaryOp::Gt => ">", BinaryOp::Le => "<=", BinaryOp::Ge => ">=",
2139                    BinaryOp::And => "&&", BinaryOp::Or => "||",
2140                    BinaryOp::BitAnd => "&", BinaryOp::BitOr => "|", BinaryOp::BitXor => "^",
2141                    BinaryOp::Shl => "<<", BinaryOp::Shr => ">>",
2142                };
2143                format!("({} {} {})", l, sym, r)
2144            }
2145            Expr::Call { function, args } => {
2146                let wgsl_fn = match function.as_str() {
2147                    "sin" => "sin", "cos" => "cos", "sqrt" => "sqrt", "abs" => "abs",
2148                    "mix" => "mix", "clamp" => "clamp", "dot" => "dot", "cross" => "cross",
2149                    "normalize" => "normalize", "length" => "length", "pow" => "pow",
2150                    "floor" => "floor", "ceil" => "ceil", "fract" => "fract",
2151                    "smoothstep" => "smoothstep", "max" => "max", "min" => "min",
2152                    "texture" => "textureSample",
2153                    other => other,
2154                };
2155                let arg_strs: Vec<String> = args.iter().map(|a| self.emit_expr_wgsl(a)).collect();
2156                format!("{}({})", wgsl_fn, arg_strs.join(", "))
2157            }
2158            Expr::Field { object, field } => format!("{}.{}", self.emit_expr_wgsl(object), field),
2159            Expr::Swizzle { object, components } => format!("{}.{}", self.emit_expr_wgsl(object), components),
2160            Expr::Construction { ty, args } => {
2161                let arg_strs: Vec<String> = args.iter().map(|a| self.emit_expr_wgsl(a)).collect();
2162                format!("{}({})", Self::wgsl_type(ty), arg_strs.join(", "))
2163            }
2164            _ => String::from("/* expr */"),
2165        }
2166    }
2167
2168    fn emit_stmt_wgsl(&mut self, stmt: &Stmt) {
2169        match stmt {
2170            Stmt::Expr(e) => { let s = self.emit_expr_wgsl(e); self.writeln(&format!("{};", s)); }
2171            Stmt::VarDecl { ty, name, init, qualifier } => {
2172                let kw = if matches!(qualifier, Some(VarQualifier::Const)) { "let" } else { "var" };
2173                if let Some(init_expr) = init {
2174                    self.writeln(&format!("{} {}: {} = {};", kw, name, Self::wgsl_type(ty), self.emit_expr_wgsl(init_expr)));
2175                } else {
2176                    self.writeln(&format!("{} {}: {};", kw, name, Self::wgsl_type(ty)));
2177                }
2178            }
2179            Stmt::Return(val) => {
2180                if let Some(v) = val { self.writeln(&format!("return {};", self.emit_expr_wgsl(v))); }
2181                else { self.writeln("return;"); }
2182            }
2183            Stmt::If { cond, then_body, else_body } => {
2184                self.writeln(&format!("if ({}) {{", self.emit_expr_wgsl(cond)));
2185                self.indent_in(); self.emit_stmt_wgsl(then_body); self.indent_out();
2186                if let Some(e) = else_body {
2187                    self.writeln("} else {");
2188                    self.indent_in(); self.emit_stmt_wgsl(e); self.indent_out();
2189                }
2190                self.writeln("}");
2191            }
2192            Stmt::For { init, cond, step, body } => {
2193                let cond_str = cond.as_ref().map(|c| self.emit_expr_wgsl(c)).unwrap_or_default();
2194                self.writeln(&format!("loop {{"));
2195                self.indent_in();
2196                self.writeln(&format!("if (!{}) {{ break; }}", cond_str));
2197                self.emit_stmt_wgsl(body);
2198                self.indent_out();
2199                self.writeln("}");
2200            }
2201            Stmt::Block(stmts) => {
2202                self.writeln("{");
2203                self.indent_in();
2204                for s in stmts { self.emit_stmt_wgsl(s); }
2205                self.indent_out();
2206                self.writeln("}");
2207            }
2208            Stmt::Break => self.writeln("break;"),
2209            Stmt::Continue => self.writeln("continue;"),
2210            Stmt::Discard => self.writeln("discard;"),
2211            _ => {}
2212        }
2213    }
2214
2215    pub fn emit_ast_wgsl(&mut self, ast: &ShaderAst) -> String {
2216        self.writeln("// WGSL generated by ShaderCompiler");
2217        for u in &ast.uniforms { self.emit_uniform_wgsl(u, 0); }
2218        for func in &ast.functions { self.emit_function_wgsl(func); self.output.push('\n'); }
2219        self.output.clone()
2220    }
2221}
2222
2223// ============================================================
2224// SECTION 13: SHADER REFLECTION
2225// ============================================================
2226
2227#[derive(Debug, Clone)]
2228pub struct ReflectedUniform {
2229    pub name: String,
2230    pub ty: ShaderType,
2231    pub binding: u32,
2232    pub set: u32,
2233    pub array_size: u32,
2234    pub size_bytes: u32,
2235}
2236
2237impl ReflectedUniform {
2238    pub fn size_of_type(ty: &ShaderType) -> u32 {
2239        match ty {
2240            ShaderType::Float | ShaderType::Int | ShaderType::Uint | ShaderType::Bool => 4,
2241            ShaderType::Vec2 | ShaderType::IVec2 | ShaderType::UVec2 => 8,
2242            ShaderType::Vec3 | ShaderType::IVec3 | ShaderType::UVec3 => 12,
2243            ShaderType::Vec4 | ShaderType::IVec4 | ShaderType::UVec4 => 16,
2244            ShaderType::Mat2 => 16,
2245            ShaderType::Mat3 => 36,
2246            ShaderType::Mat4 => 64,
2247            _ => 0,
2248        }
2249    }
2250}
2251
2252#[derive(Debug, Clone)]
2253pub struct ReflectedAttribute {
2254    pub name: String,
2255    pub ty: ShaderType,
2256    pub location: u32,
2257    pub format: VertexFormat,
2258}
2259
2260#[derive(Debug, Clone, Copy, PartialEq)]
2261pub enum VertexFormat {
2262    Float32, Float32x2, Float32x3, Float32x4,
2263    Sint32, Sint32x2, Sint32x3, Sint32x4,
2264    Uint32, Uint32x2, Uint32x3, Uint32x4,
2265}
2266
2267#[derive(Debug, Clone, Default)]
2268pub struct ShaderReflection {
2269    pub uniforms: Vec<ReflectedUniform>,
2270    pub attributes: Vec<ReflectedAttribute>,
2271    pub varyings: Vec<ReflectedAttribute>,
2272    pub used_builtins: Vec<String>,
2273    pub struct_layouts: Vec<String>,
2274    pub push_constants: Vec<ReflectedUniform>,
2275    pub storage_buffers: Vec<ReflectedUniform>,
2276    pub entry_points: Vec<String>,
2277}
2278
2279impl ShaderReflection {
2280    pub fn new() -> Self { Self::default() }
2281
2282    pub fn add_uniform(&mut self, u: ReflectedUniform) {
2283        self.uniforms.push(u);
2284    }
2285
2286    pub fn add_attribute(&mut self, a: ReflectedAttribute) {
2287        self.attributes.push(a);
2288    }
2289
2290
2291}
2292
2293// ── Shader Program Representation ────────────────────────────────────────────
2294
2295#[derive(Clone, Debug)]
2296pub struct ShaderProgram {
2297    pub id: u32,
2298    pub name: String,
2299    pub vertex_source: String,
2300    pub fragment_source: String,
2301    pub geometry_source: Option<String>,
2302    pub compute_source: Option<String>,
2303    pub defines: HashMap<String, String>,
2304    pub version: String,
2305    pub compiled: bool,
2306    pub compile_errors: Vec<String>,
2307    pub compile_warnings: Vec<String>,
2308}
2309
2310impl ShaderProgram {
2311    pub fn new(id: u32, name: impl Into<String>) -> Self {
2312        Self { id, name: name.into(), vertex_source: String::new(), fragment_source: String::new(), geometry_source: None, compute_source: None, defines: HashMap::new(), version: "450".into(), compiled: false, compile_errors: Vec::new(), compile_warnings: Vec::new() }
2313    }
2314    pub fn with_vertex(mut self, src: impl Into<String>) -> Self { self.vertex_source = src.into(); self }
2315    pub fn with_fragment(mut self, src: impl Into<String>) -> Self { self.fragment_source = src.into(); self }
2316    pub fn add_define(&mut self, key: impl Into<String>, val: impl Into<String>) { self.defines.insert(key.into(), val.into()); }
2317    pub fn is_compute(&self) -> bool { self.compute_source.is_some() }
2318    pub fn has_geometry(&self) -> bool { self.geometry_source.is_some() }
2319    pub fn is_valid(&self) -> bool { self.compiled && self.compile_errors.is_empty() }
2320    pub fn error_count(&self) -> usize { self.compile_errors.len() }
2321    pub fn warning_count(&self) -> usize { self.compile_warnings.len() }
2322    pub fn add_error(&mut self, err: impl Into<String>) { self.compile_errors.push(err.into()); }
2323    pub fn add_warning(&mut self, warn: impl Into<String>) { self.compile_warnings.push(warn.into()); }
2324    pub fn clear_diagnostics(&mut self) { self.compile_errors.clear(); self.compile_warnings.clear(); }
2325    pub fn mark_compiled(&mut self) { self.compiled = true; }
2326}
2327
2328#[derive(Clone, Debug)]
2329pub struct ShaderProgramRegistry {
2330    pub programs: HashMap<u32, ShaderProgram>,
2331    pub next_id: u32,
2332    pub active_program: Option<u32>,
2333}
2334
2335impl ShaderProgramRegistry {
2336    pub fn new() -> Self { Self { programs: HashMap::new(), next_id: 1, active_program: None } }
2337    pub fn add(&mut self, prog: ShaderProgram) -> u32 {
2338        let id = self.next_id; self.next_id += 1;
2339        self.programs.insert(id, prog);
2340        id
2341    }
2342    pub fn get(&self, id: u32) -> Option<&ShaderProgram> { self.programs.get(&id) }
2343    pub fn get_mut(&mut self, id: u32) -> Option<&mut ShaderProgram> { self.programs.get_mut(&id) }
2344    pub fn find_by_name(&self, name: &str) -> Option<&ShaderProgram> { self.programs.values().find(|p| p.name == name) }
2345    pub fn count(&self) -> usize { self.programs.len() }
2346    pub fn valid_count(&self) -> usize { self.programs.values().filter(|p| p.is_valid()).count() }
2347    pub fn set_active(&mut self, id: u32) { self.active_program = Some(id); }
2348    pub fn remove(&mut self, id: u32) -> bool { self.programs.remove(&id).is_some() }
2349}
2350
2351impl Default for ShaderProgramRegistry {
2352    fn default() -> Self { Self::new() }
2353}
2354
2355// ── Shader Type System ────────────────────────────────────────────────────────
2356
2357#[derive(Clone, Debug, PartialEq)]
2358pub enum ShaderDataType {
2359    Void,
2360    Bool, BVec2, BVec3, BVec4,
2361    Int, IVec2, IVec3, IVec4,
2362    UInt, UVec2, UVec3, UVec4,
2363    Float, Vec2, Vec3, Vec4,
2364    Mat2, Mat3, Mat4,
2365    Mat2x3, Mat2x4, Mat3x2, Mat3x4, Mat4x2, Mat4x3,
2366    Sampler2D, Sampler3D, SamplerCube, Sampler2DArray, SamplerShadow,
2367    Image2D, Image3D, ImageCube,
2368    Struct(String),
2369    Array(Box<ShaderDataType>, Option<u32>),
2370}
2371
2372impl ShaderDataType {
2373    pub fn byte_size(&self) -> u32 {
2374        match self {
2375            ShaderDataType::Bool | ShaderDataType::Int | ShaderDataType::UInt | ShaderDataType::Float => 4,
2376            ShaderDataType::BVec2 | ShaderDataType::IVec2 | ShaderDataType::UVec2 | ShaderDataType::Vec2 => 8,
2377            ShaderDataType::BVec3 | ShaderDataType::IVec3 | ShaderDataType::UVec3 | ShaderDataType::Vec3 => 12,
2378            ShaderDataType::BVec4 | ShaderDataType::IVec4 | ShaderDataType::UVec4 | ShaderDataType::Vec4 => 16,
2379            ShaderDataType::Mat2 => 16, ShaderDataType::Mat3 => 36, ShaderDataType::Mat4 => 64,
2380            ShaderDataType::Array(inner, Some(n)) => inner.byte_size() * n,
2381            _ => 0,
2382        }
2383    }
2384    pub fn is_sampler(&self) -> bool { matches!(self, ShaderDataType::Sampler2D | ShaderDataType::Sampler3D | ShaderDataType::SamplerCube | ShaderDataType::Sampler2DArray | ShaderDataType::SamplerShadow) }
2385    pub fn is_matrix(&self) -> bool { matches!(self, ShaderDataType::Mat2 | ShaderDataType::Mat3 | ShaderDataType::Mat4 | ShaderDataType::Mat2x3 | ShaderDataType::Mat2x4 | ShaderDataType::Mat3x2 | ShaderDataType::Mat3x4 | ShaderDataType::Mat4x2 | ShaderDataType::Mat4x3) }
2386    pub fn is_vector(&self) -> bool { matches!(self, ShaderDataType::Vec2 | ShaderDataType::Vec3 | ShaderDataType::Vec4 | ShaderDataType::IVec2 | ShaderDataType::IVec3 | ShaderDataType::IVec4 | ShaderDataType::UVec2 | ShaderDataType::UVec3 | ShaderDataType::UVec4 | ShaderDataType::BVec2 | ShaderDataType::BVec3 | ShaderDataType::BVec4) }
2387    pub fn glsl_name(&self) -> &'static str {
2388        match self {
2389            ShaderDataType::Void => "void", ShaderDataType::Bool => "bool",
2390            ShaderDataType::Int => "int", ShaderDataType::UInt => "uint",
2391            ShaderDataType::Float => "float", ShaderDataType::Vec2 => "vec2",
2392            ShaderDataType::Vec3 => "vec3", ShaderDataType::Vec4 => "vec4",
2393            ShaderDataType::Mat4 => "mat4", ShaderDataType::Mat3 => "mat3",
2394            ShaderDataType::Mat2 => "mat2", ShaderDataType::Sampler2D => "sampler2D",
2395            ShaderDataType::SamplerCube => "samplerCube",
2396            _ => "unknown",
2397        }
2398    }
2399}
2400
2401// ── Shader Variable ───────────────────────────────────────────────────────────
2402
2403#[derive(Clone, Debug)]
2404pub struct ShaderVariable {
2405    pub name: String,
2406    pub data_type: ShaderDataType,
2407    pub qualifier: ShaderQualifier,
2408    pub location: Option<u32>,
2409    pub binding: Option<u32>,
2410    pub set: Option<u32>,
2411    pub is_builtin: bool,
2412    pub comment: String,
2413}
2414
2415#[derive(Clone, Debug, PartialEq)]
2416pub enum ShaderQualifier { In, Out, InOut, Uniform, Const, Buffer, Shared }
2417
2418impl ShaderVariable {
2419    pub fn uniform(name: impl Into<String>, data_type: ShaderDataType) -> Self {
2420        Self { name: name.into(), data_type, qualifier: ShaderQualifier::Uniform, location: None, binding: None, set: None, is_builtin: false, comment: String::new() }
2421    }
2422    pub fn input(name: impl Into<String>, data_type: ShaderDataType, location: u32) -> Self {
2423        Self { name: name.into(), data_type, qualifier: ShaderQualifier::In, location: Some(location), binding: None, set: None, is_builtin: false, comment: String::new() }
2424    }
2425    pub fn output(name: impl Into<String>, data_type: ShaderDataType, location: u32) -> Self {
2426        Self { name: name.into(), data_type, qualifier: ShaderQualifier::Out, location: Some(location), binding: None, set: None, is_builtin: false, comment: String::new() }
2427    }
2428    pub fn with_binding(mut self, binding: u32, set: u32) -> Self { self.binding = Some(binding); self.set = Some(set); self }
2429    pub fn is_uniform(&self) -> bool { self.qualifier == ShaderQualifier::Uniform }
2430    pub fn is_input(&self) -> bool { self.qualifier == ShaderQualifier::In }
2431    pub fn is_output(&self) -> bool { self.qualifier == ShaderQualifier::Out }
2432    pub fn size_bytes(&self) -> u32 { self.data_type.byte_size() }
2433}
2434
2435// ── Shader Function ───────────────────────────────────────────────────────────
2436
2437#[derive(Clone, Debug)]
2438pub struct ShaderFunction {
2439    pub name: String,
2440    pub return_type: ShaderDataType,
2441    pub parameters: Vec<ShaderVariable>,
2442    pub body: String,
2443    pub is_builtin: bool,
2444    pub line_start: u32,
2445    pub line_end: u32,
2446    pub comment: String,
2447}
2448
2449impl ShaderFunction {
2450    pub fn new(name: impl Into<String>, return_type: ShaderDataType) -> Self {
2451        Self { name: name.into(), return_type, parameters: Vec::new(), body: String::new(), is_builtin: false, line_start: 0, line_end: 0, comment: String::new() }
2452    }
2453    pub fn add_param(&mut self, param: ShaderVariable) { self.parameters.push(param); }
2454    pub fn param_count(&self) -> usize { self.parameters.len() }
2455    pub fn is_main(&self) -> bool { self.name == "main" }
2456    pub fn signature(&self) -> String {
2457        let params: Vec<_> = self.parameters.iter().map(|p| format!("{} {}", p.data_type.glsl_name(), p.name)).collect();
2458        format!("{} {}({})", self.return_type.glsl_name(), self.name, params.join(", "))
2459    }
2460}
2461
2462// ── Shader Struct Type ────────────────────────────────────────────────────────
2463
2464#[derive(Clone, Debug)]
2465pub struct ShaderStruct {
2466    pub name: String,
2467    pub fields: Vec<ShaderVariable>,
2468    pub comment: String,
2469    pub std140_layout: bool,
2470}
2471
2472impl ShaderStruct {
2473    pub fn new(name: impl Into<String>) -> Self { Self { name: name.into(), fields: Vec::new(), comment: String::new(), std140_layout: false } }
2474    pub fn add_field(&mut self, field: ShaderVariable) { self.fields.push(field); }
2475    pub fn size_bytes(&self) -> u32 { self.fields.iter().map(|f| f.size_bytes()).sum() }
2476    pub fn std140_size(&self) -> u32 {
2477        let mut size = 0u32;
2478        for f in &self.fields {
2479            let field_size = f.size_bytes();
2480            let align = field_size.max(4).next_power_of_two().min(16);
2481            size = (size + align - 1) & !(align - 1);
2482            size += field_size;
2483        }
2484        (size + 15) & !15
2485    }
2486    pub fn field_count(&self) -> usize { self.fields.len() }
2487    pub fn has_field(&self, name: &str) -> bool { self.fields.iter().any(|f| f.name == name) }
2488    pub fn generate_glsl(&self) -> String {
2489        let mut s = format!("struct {} {{\n", self.name);
2490        for f in &self.fields { s += &format!("    {} {};\n", f.data_type.glsl_name(), f.name); }
2491        s += "};\n";
2492        s
2493    }
2494}
2495
2496// ── Shader Diagnostic ─────────────────────────────────────────────────────────
2497
2498#[derive(Clone, Debug)]
2499pub struct ShaderDiagnostic {
2500    pub kind: DiagnosticKind,
2501    pub message: String,
2502    pub line: u32,
2503    pub column: u32,
2504    pub file: String,
2505    pub code: u32,
2506    pub suggestion: Option<String>,
2507}
2508
2509#[derive(Clone, Debug, PartialEq)]
2510pub enum DiagnosticKind { Error, Warning, Info, Hint }
2511
2512impl ShaderDiagnostic {
2513    pub fn error(msg: impl Into<String>, line: u32) -> Self {
2514        Self { kind: DiagnosticKind::Error, message: msg.into(), line, column: 0, file: String::new(), code: 0, suggestion: None }
2515    }
2516    pub fn warning(msg: impl Into<String>, line: u32) -> Self {
2517        Self { kind: DiagnosticKind::Warning, message: msg.into(), line, column: 0, file: String::new(), code: 0, suggestion: None }
2518    }
2519    pub fn with_suggestion(mut self, s: impl Into<String>) -> Self { self.suggestion = Some(s.into()); self }
2520    pub fn is_error(&self) -> bool { self.kind == DiagnosticKind::Error }
2521    pub fn is_warning(&self) -> bool { self.kind == DiagnosticKind::Warning }
2522    pub fn format(&self) -> String {
2523        let prefix = match self.kind { DiagnosticKind::Error => "error", DiagnosticKind::Warning => "warning", DiagnosticKind::Info => "info", DiagnosticKind::Hint => "hint" };
2524        format!("[{}] {}:{} — {}", prefix, self.file, self.line, self.message)
2525    }
2526}
2527
2528#[derive(Clone, Debug, Default)]
2529pub struct DiagnosticList {
2530    pub items: Vec<ShaderDiagnostic>,
2531}
2532
2533impl DiagnosticList {
2534    pub fn new() -> Self { Self::default() }
2535    pub fn push(&mut self, d: ShaderDiagnostic) { self.items.push(d); }
2536    pub fn errors(&self) -> Vec<&ShaderDiagnostic> { self.items.iter().filter(|d| d.is_error()).collect() }
2537    pub fn warnings(&self) -> Vec<&ShaderDiagnostic> { self.items.iter().filter(|d| d.is_warning()).collect() }
2538    pub fn has_errors(&self) -> bool { self.items.iter().any(|d| d.is_error()) }
2539    pub fn error_count(&self) -> usize { self.items.iter().filter(|d| d.is_error()).count() }
2540    pub fn warning_count(&self) -> usize { self.items.iter().filter(|d| d.is_warning()).count() }
2541    pub fn clear(&mut self) { self.items.clear(); }
2542    pub fn total(&self) -> usize { self.items.len() }
2543}
2544
2545// ── Shader Preprocessor ───────────────────────────────────────────────────────
2546
2547#[derive(Clone, Debug)]
2548pub struct ShaderPreprocessor {
2549    pub defines: HashMap<String, String>,
2550    pub include_paths: Vec<String>,
2551    pub max_include_depth: u32,
2552    pub strip_comments: bool,
2553    pub expand_macros: bool,
2554}
2555
2556impl ShaderPreprocessor {
2557    pub fn new() -> Self {
2558        Self { defines: HashMap::new(), include_paths: Vec::new(), max_include_depth: 10, strip_comments: true, expand_macros: true }
2559    }
2560    pub fn define(&mut self, key: impl Into<String>, value: impl Into<String>) { self.defines.insert(key.into(), value.into()); }
2561    pub fn undefine(&mut self, key: &str) { self.defines.remove(key); }
2562    pub fn add_include_path(&mut self, path: impl Into<String>) { self.include_paths.push(path.into()); }
2563    pub fn preprocess(&self, source: &str) -> String {
2564        let mut output = String::with_capacity(source.len());
2565        for line in source.lines() {
2566            let trimmed = line.trim();
2567            if trimmed.starts_with("#define") {
2568                output.push_str(line);
2569            } else if trimmed.starts_with("//") && self.strip_comments {
2570                // skip
2571            } else {
2572                let mut processed = line.to_string();
2573                if self.expand_macros {
2574                    for (k, v) in &self.defines { processed = processed.replace(k.as_str(), v.as_str()); }
2575                }
2576                output.push_str(&processed);
2577            }
2578            output.push('\n');
2579        }
2580        output
2581    }
2582    pub fn is_defined(&self, key: &str) -> bool { self.defines.contains_key(key) }
2583    pub fn define_count(&self) -> usize { self.defines.len() }
2584}
2585
2586impl Default for ShaderPreprocessor {
2587    fn default() -> Self { Self::new() }
2588}
2589
2590// ── Shader Uniform Block ──────────────────────────────────────────────────────
2591
2592#[derive(Clone, Debug)]
2593pub struct UniformBlock {
2594    pub name: String,
2595    pub binding: u32,
2596    pub set: u32,
2597    pub fields: Vec<ShaderVariable>,
2598    pub std140: bool,
2599    pub dynamic_offset: bool,
2600}
2601
2602impl UniformBlock {
2603    pub fn new(name: impl Into<String>, binding: u32) -> Self {
2604        Self { name: name.into(), binding, set: 0, fields: Vec::new(), std140: true, dynamic_offset: false }
2605    }
2606    pub fn add_field(&mut self, f: ShaderVariable) { self.fields.push(f); }
2607    pub fn size_bytes(&self) -> u32 {
2608        let mut size = 0u32;
2609        for f in &self.fields {
2610            let fs = f.size_bytes();
2611            let align = fs.max(4).next_power_of_two().min(16);
2612            size = (size + align - 1) & !(align - 1);
2613            size += fs;
2614        }
2615        (size + 15) & !15
2616    }
2617    pub fn generate_glsl(&self) -> String {
2618        let mut s = format!("layout(std140, binding={}) uniform {} {{\n", self.binding, self.name);
2619        for f in &self.fields { s += &format!("    {} {};\n", f.data_type.glsl_name(), f.name); }
2620        s += "};\n";
2621        s
2622    }
2623    pub fn field_count(&self) -> usize { self.fields.len() }
2624}
2625
2626// ── Shader Pipeline State ─────────────────────────────────────────────────────
2627
2628#[derive(Clone, Debug)]
2629pub struct PipelineState {
2630    pub blend_enabled: bool,
2631    pub blend_src: BlendFactor,
2632    pub blend_dst: BlendFactor,
2633    pub blend_op: BlendOp,
2634    pub depth_test: bool,
2635    pub depth_write: bool,
2636    pub depth_func: CompareFunc,
2637    pub cull_mode: CullMode,
2638    pub front_face: FrontFace,
2639    pub scissor_test: bool,
2640    pub stencil_test: bool,
2641    pub polygon_mode: PolygonMode,
2642    pub line_width: f32,
2643    pub point_size: f32,
2644}
2645
2646#[derive(Clone, Debug, PartialEq)]
2647pub enum BlendFactor { Zero, One, SrcAlpha, OneMinusSrcAlpha, DstAlpha, OneMinusDstAlpha, SrcColor, OneMinusSrcColor, ConstantAlpha }
2648#[derive(Clone, Debug, PartialEq)]
2649pub enum BlendOp { Add, Subtract, ReverseSubtract, Min, Max }
2650#[derive(Clone, Debug, PartialEq)]
2651pub enum CompareFunc { Never, Less, Equal, LessEqual, Greater, NotEqual, GreaterEqual, Always }
2652#[derive(Clone, Debug, PartialEq)]
2653pub enum CullMode { None, Front, Back, FrontAndBack }
2654#[derive(Clone, Debug, PartialEq)]
2655pub enum FrontFace { Clockwise, CounterClockwise }
2656#[derive(Clone, Debug, PartialEq)]
2657pub enum PolygonMode { Fill, Line, Point }
2658
2659impl Default for PipelineState {
2660    fn default() -> Self {
2661        Self { blend_enabled: false, blend_src: BlendFactor::SrcAlpha, blend_dst: BlendFactor::OneMinusSrcAlpha, blend_op: BlendOp::Add, depth_test: true, depth_write: true, depth_func: CompareFunc::Less, cull_mode: CullMode::Back, front_face: FrontFace::CounterClockwise, scissor_test: false, stencil_test: false, polygon_mode: PolygonMode::Fill, line_width: 1.0, point_size: 1.0 }
2662    }
2663}
2664
2665impl PipelineState {
2666    pub fn transparent() -> Self { Self { blend_enabled: true, depth_write: false, ..Default::default() } }
2667    pub fn additive() -> Self { Self { blend_enabled: true, blend_src: BlendFactor::One, blend_dst: BlendFactor::One, blend_op: BlendOp::Add, depth_write: false, ..Default::default() } }
2668    pub fn wireframe() -> Self { Self { polygon_mode: PolygonMode::Line, cull_mode: CullMode::None, ..Default::default() } }
2669    pub fn opaque() -> Self { Self::default() }
2670    pub fn is_transparent(&self) -> bool { self.blend_enabled }
2671    pub fn needs_sort(&self) -> bool { self.blend_enabled && !self.depth_write }
2672}
2673
2674// ── Shader Compiler Constants ─────────────────────────────────────────────────
2675
2676pub const SHADER_MAX_UNIFORMS: usize = 128;
2677pub const SHADER_MAX_ATTRIBUTES: usize = 32;
2678pub const SHADER_MAX_VARYINGS: usize = 32;
2679pub const SHADER_MAX_TEXTURE_UNITS: usize = 32;
2680pub const SHADER_MAX_UNIFORM_BLOCK_SIZE: u32 = 65536;
2681pub const SHADER_MAX_INCLUDE_DEPTH: u32 = 16;
2682pub const SHADER_VERSION_GLSL: &str = "450";
2683pub const SHADER_VERSION_WGSL: &str = "1.0";
2684pub const SHADER_MAX_SOURCE_LINES: usize = 10000;
2685pub const SHADER_MAX_FUNCTIONS: usize = 256;
2686pub const SHADER_MAX_STRUCTS: usize = 64;
2687pub const SHADER_MAX_PROGRAMS: usize = 1024;
2688pub const SHADER_CACHE_SIZE: usize = 256;
2689pub const SHADER_MAX_PUSH_CONSTANT_SIZE: u32 = 128;
2690
2691pub fn shader_compiler_info() -> &'static str { "ShaderCompiler v1.0 — GLSL/WGSL, preprocessor, reflection, pipeline state" }
2692
2693
2694// ── Shader AST Nodes ──────────────────────────────────────────────────────────
2695
2696#[derive(Clone, Debug)]
2697pub enum ShaderExpr {
2698    Literal(ShaderLiteral),
2699    Ident(String),
2700    Binary { op: BinaryOp, lhs: Box<ShaderExpr>, rhs: Box<ShaderExpr> },
2701    Unary { op: UnaryOp, expr: Box<ShaderExpr> },
2702    Call { name: String, args: Vec<ShaderExpr> },
2703    Index { array: Box<ShaderExpr>, index: Box<ShaderExpr> },
2704    Field { object: Box<ShaderExpr>, field: String },
2705    Ternary { cond: Box<ShaderExpr>, then: Box<ShaderExpr>, else_: Box<ShaderExpr> },
2706    Cast { to: ShaderDataType, expr: Box<ShaderExpr> },
2707    Assign { target: Box<ShaderExpr>, value: Box<ShaderExpr> },
2708}
2709
2710#[derive(Clone, Debug)]
2711pub enum ShaderLiteral {
2712    Int(i64), Float(f64), Bool(bool), String(String),
2713}
2714
2715#[derive(Clone, Debug, PartialEq)]
2716pub enum BinaryOpEx {
2717    Add, Sub, Mul, Div, Mod,
2718    Eq, Ne, Lt, Gt, Le, Ge,
2719    And, Or, BitAnd, BitOr, BitXor,
2720    Shl, Shr,
2721    AddAssign, SubAssign, MulAssign, DivAssign,
2722}
2723
2724#[derive(Clone, Debug, PartialEq)]
2725pub enum UnaryOpEx { Neg, Not, BitNot, PreInc, PreDec, PostInc, PostDec }
2726
2727impl BinaryOpEx {
2728    pub fn is_comparison(&self) -> bool { matches!(self, BinaryOpEx::Eq | BinaryOpEx::Ne | BinaryOpEx::Lt | BinaryOpEx::Gt | BinaryOpEx::Le | BinaryOpEx::Ge) }
2729    pub fn is_logical(&self) -> bool { matches!(self, BinaryOpEx::And | BinaryOpEx::Or) }
2730    pub fn is_arithmetic(&self) -> bool { matches!(self, BinaryOpEx::Add | BinaryOpEx::Sub | BinaryOpEx::Mul | BinaryOpEx::Div | BinaryOpEx::Mod) }
2731    pub fn glsl_symbol(&self) -> &'static str {
2732        match self {
2733            BinaryOpEx::Add => "+", BinaryOpEx::Sub => "-", BinaryOpEx::Mul => "*", BinaryOpEx::Div => "/",
2734            BinaryOpEx::Mod => "%", BinaryOpEx::Eq => "==", BinaryOpEx::Ne => "!=", BinaryOpEx::Lt => "<",
2735            BinaryOpEx::Gt => ">", BinaryOpEx::Le => "<=", BinaryOpEx::Ge => ">=", BinaryOpEx::And => "&&",
2736            BinaryOpEx::Or => "||", BinaryOpEx::BitAnd => "&", BinaryOpEx::BitOr => "|", BinaryOpEx::BitXor => "^",
2737            BinaryOpEx::Shl => "<<", BinaryOpEx::Shr => ">>", BinaryOpEx::AddAssign => "+=",
2738            BinaryOpEx::SubAssign => "-=", BinaryOpEx::MulAssign => "*=", BinaryOpEx::DivAssign => "/=",
2739        }
2740    }
2741}
2742
2743#[derive(Clone, Debug)]
2744pub enum ShaderStmt {
2745    Decl { var: ShaderVariable, initializer: Option<ShaderExpr> },
2746    Expr(ShaderExpr),
2747    If { cond: ShaderExpr, then: Vec<ShaderStmt>, else_: Option<Vec<ShaderStmt>> },
2748    For { init: Option<Box<ShaderStmt>>, cond: Option<ShaderExpr>, update: Option<ShaderExpr>, body: Vec<ShaderStmt> },
2749    While { cond: ShaderExpr, body: Vec<ShaderStmt> },
2750    Return(Option<ShaderExpr>),
2751    Break,
2752    Continue,
2753    Discard,
2754    Block(Vec<ShaderStmt>),
2755}
2756
2757// ── Shader Code Generator ─────────────────────────────────────────────────────
2758
2759#[derive(Clone, Debug)]
2760pub struct ShaderCodeGen {
2761    pub target: CodeGenTarget,
2762    pub indent_size: usize,
2763    pub use_precision_qualifiers: bool,
2764    pub emit_line_directives: bool,
2765}
2766
2767#[derive(Clone, Debug, PartialEq)]
2768pub enum CodeGenTarget { Glsl450, Glsl300Es, Wgsl, Hlsl50, Msl20 }
2769
2770impl ShaderCodeGen {
2771    pub fn new(target: CodeGenTarget) -> Self { Self { target, indent_size: 4, use_precision_qualifiers: false, emit_line_directives: false } }
2772    pub fn glsl450() -> Self { Self::new(CodeGenTarget::Glsl450) }
2773    pub fn wgsl() -> Self { Self::new(CodeGenTarget::Wgsl) }
2774    pub fn version_directive(&self) -> String {
2775        match self.target {
2776            CodeGenTarget::Glsl450 => "#version 450\n".into(),
2777            CodeGenTarget::Glsl300Es => "#version 300 es\nprecision highp float;\n".into(),
2778            CodeGenTarget::Wgsl => "// WGSL\n".into(),
2779            CodeGenTarget::Hlsl50 => "// HLSL 5.0\n".into(),
2780            CodeGenTarget::Msl20 => "#include <metal_stdlib>\nusing namespace metal;\n".into(),
2781        }
2782    }
2783    pub fn emit_uniform(&self, var: &ShaderVariable) -> String {
2784        match self.target {
2785            CodeGenTarget::Glsl450 | CodeGenTarget::Glsl300Es => {
2786                let layout = var.binding.map(|b| format!("layout(binding={}) ", b)).unwrap_or_default();
2787                format!("{}uniform {} {};", layout, var.data_type.glsl_name(), var.name)
2788            }
2789            _ => format!("// uniform {} {}", var.data_type.glsl_name(), var.name),
2790        }
2791    }
2792    pub fn emit_input(&self, var: &ShaderVariable) -> String {
2793        let loc = var.location.map(|l| format!("layout(location={}) ", l)).unwrap_or_default();
2794        format!("{}in {} {};", loc, var.data_type.glsl_name(), var.name)
2795    }
2796    pub fn emit_output(&self, var: &ShaderVariable) -> String {
2797        let loc = var.location.map(|l| format!("layout(location={}) ", l)).unwrap_or_default();
2798        format!("{}out {} {};", loc, var.data_type.glsl_name(), var.name)
2799    }
2800    pub fn emit_struct(&self, s: &ShaderStruct) -> String { s.generate_glsl() }
2801    pub fn emit_function_sig(&self, f: &ShaderFunction) -> String { f.signature() }
2802    pub fn is_glsl(&self) -> bool { matches!(self.target, CodeGenTarget::Glsl450 | CodeGenTarget::Glsl300Es) }
2803}
2804
2805impl Default for ShaderCodeGen {
2806    fn default() -> Self { Self::glsl450() }
2807}
2808
2809// ── Shader Optimizer ──────────────────────────────────────────────────────────
2810
2811#[derive(Clone, Debug)]
2812pub struct ShaderOptimizer {
2813    pub level: OptimizationLevel,
2814    pub fold_constants: bool,
2815    pub dead_code_elimination: bool,
2816    pub inline_functions: bool,
2817    pub common_subexpr_elim: bool,
2818    pub loop_unroll_threshold: u32,
2819    pub vectorize: bool,
2820}
2821
2822#[derive(Clone, Debug, PartialEq)]
2823pub enum OptimizationLevel { None, Low, Medium, High, Aggressive }
2824
2825impl ShaderOptimizer {
2826    pub fn new(level: OptimizationLevel) -> Self {
2827        let (fold, dce, inline, cse, unroll, vec) = match level {
2828            OptimizationLevel::None => (false, false, false, false, 0, false),
2829            OptimizationLevel::Low => (true, true, false, false, 4, false),
2830            OptimizationLevel::Medium => (true, true, true, true, 8, false),
2831            OptimizationLevel::High => (true, true, true, true, 16, true),
2832            OptimizationLevel::Aggressive => (true, true, true, true, 64, true),
2833        };
2834        Self { level, fold_constants: fold, dead_code_elimination: dce, inline_functions: inline, common_subexpr_elim: cse, loop_unroll_threshold: unroll, vectorize: vec }
2835    }
2836    pub fn none() -> Self { Self::new(OptimizationLevel::None) }
2837    pub fn release() -> Self { Self::new(OptimizationLevel::High) }
2838    pub fn debug() -> Self { Self::new(OptimizationLevel::None) }
2839    pub fn any_optimization_enabled(&self) -> bool { self.level != OptimizationLevel::None }
2840}
2841
2842impl Default for ShaderOptimizer {
2843    fn default() -> Self { Self::new(OptimizationLevel::Medium) }
2844}
2845
2846// ── Shader Cache ──────────────────────────────────────────────────────────────
2847
2848#[derive(Clone, Debug)]
2849pub struct ShaderCacheEntry {
2850    pub source_hash: u64,
2851    pub defines_hash: u64,
2852    pub compiled_bytes: Vec<u8>,
2853    pub target: CodeGenTarget,
2854    pub created_at: u64,
2855    pub hit_count: u32,
2856}
2857
2858impl ShaderCacheEntry {
2859    pub fn new(source_hash: u64, defines_hash: u64, bytes: Vec<u8>, target: CodeGenTarget) -> Self {
2860        Self { source_hash, defines_hash, compiled_bytes: bytes, target, created_at: 0, hit_count: 0 }
2861    }
2862    pub fn is_stale(&self, source_hash: u64, defines_hash: u64) -> bool {
2863        self.source_hash != source_hash || self.defines_hash != defines_hash
2864    }
2865    pub fn record_hit(&mut self) { self.hit_count += 1; }
2866    pub fn size_bytes(&self) -> usize { self.compiled_bytes.len() }
2867}
2868
2869#[derive(Clone, Debug)]
2870pub struct ShaderCache {
2871    pub entries: HashMap<String, ShaderCacheEntry>,
2872    pub max_size: usize,
2873    pub total_hits: u64,
2874    pub total_misses: u64,
2875    pub eviction_policy: CacheEvictionPolicy,
2876}
2877
2878#[derive(Clone, Debug, PartialEq)]
2879pub enum CacheEvictionPolicy { Lru, Fifo, LeastHits }
2880
2881impl ShaderCache {
2882    pub fn new(max_size: usize) -> Self { Self { entries: HashMap::new(), max_size, total_hits: 0, total_misses: 0, eviction_policy: CacheEvictionPolicy::Lru } }
2883    pub fn get(&mut self, key: &str) -> Option<&ShaderCacheEntry> {
2884        if let Some(e) = self.entries.get_mut(key) { e.record_hit(); self.total_hits += 1; Some(e) } else { self.total_misses += 1; None }
2885    }
2886    pub fn insert(&mut self, key: String, entry: ShaderCacheEntry) {
2887        if self.entries.len() >= self.max_size { self.evict(); }
2888        self.entries.insert(key, entry);
2889    }
2890    fn evict(&mut self) {
2891        if let Some(key) = self.entries.keys().next().cloned() { self.entries.remove(&key); }
2892    }
2893    pub fn hit_rate(&self) -> f32 {
2894        let total = self.total_hits + self.total_misses;
2895        if total == 0 { 0.0 } else { self.total_hits as f32 / total as f32 }
2896    }
2897    pub fn clear(&mut self) { self.entries.clear(); }
2898    pub fn size(&self) -> usize { self.entries.len() }
2899    pub fn total_bytes(&self) -> usize { self.entries.values().map(|e| e.size_bytes()).sum() }
2900}
2901
2902impl Default for ShaderCache {
2903    fn default() -> Self { Self::new(SHADER_CACHE_SIZE) }
2904}
2905
2906// ── Shader Template System ────────────────────────────────────────────────────
2907
2908#[derive(Clone, Debug)]
2909pub struct ShaderTemplate {
2910    pub id: u32,
2911    pub name: String,
2912    pub description: String,
2913    pub vertex_template: String,
2914    pub fragment_template: String,
2915    pub parameters: Vec<ShaderTemplateParam>,
2916    pub category: String,
2917    pub tags: Vec<String>,
2918    pub is_builtin: bool,
2919}
2920
2921#[derive(Clone, Debug)]
2922pub struct ShaderTemplateParam {
2923    pub name: String,
2924    pub param_type: TemplateParamType,
2925    pub default_value: String,
2926    pub description: String,
2927    pub required: bool,
2928}
2929
2930#[derive(Clone, Debug, PartialEq)]
2931pub enum TemplateParamType { Bool, Int, Float, String, Color, Texture, Vec2, Vec3, Vec4 }
2932
2933impl ShaderTemplate {
2934    pub fn new(id: u32, name: impl Into<String>) -> Self {
2935        Self { id, name: name.into(), description: String::new(), vertex_template: String::new(), fragment_template: String::new(), parameters: Vec::new(), category: "general".into(), tags: Vec::new(), is_builtin: false }
2936    }
2937    pub fn add_param(&mut self, param: ShaderTemplateParam) { self.parameters.push(param); }
2938    pub fn instantiate(&self, params: &HashMap<String, String>) -> (String, String) {
2939        let mut vert = self.vertex_template.clone();
2940        let mut frag = self.fragment_template.clone();
2941        for p in &self.parameters {
2942            let val = params.get(&p.name).unwrap_or(&p.default_value);
2943            let placeholder = format!("{{{{{}}}}}", p.name);
2944            vert = vert.replace(&placeholder, val);
2945            frag = frag.replace(&placeholder, val);
2946        }
2947        (vert, frag)
2948    }
2949    pub fn param_count(&self) -> usize { self.parameters.len() }
2950    pub fn required_params(&self) -> Vec<&ShaderTemplateParam> { self.parameters.iter().filter(|p| p.required).collect() }
2951}
2952
2953#[derive(Clone, Debug)]
2954pub struct ShaderTemplateLibrary {
2955    pub templates: HashMap<u32, ShaderTemplate>,
2956    pub next_id: u32,
2957    pub categories: HashSet<String>,
2958}
2959
2960impl ShaderTemplateLibrary {
2961    pub fn new() -> Self { Self { templates: HashMap::new(), next_id: 1, categories: HashSet::new() } }
2962    pub fn add(&mut self, mut t: ShaderTemplate) -> u32 {
2963        let id = self.next_id; self.next_id += 1;
2964        t.id = id;
2965        self.categories.insert(t.category.clone());
2966        self.templates.insert(id, t);
2967        id
2968    }
2969    pub fn get(&self, id: u32) -> Option<&ShaderTemplate> { self.templates.get(&id) }
2970    pub fn by_category(&self, cat: &str) -> Vec<&ShaderTemplate> { self.templates.values().filter(|t| t.category == cat).collect() }
2971    pub fn find_by_name(&self, name: &str) -> Option<&ShaderTemplate> { self.templates.values().find(|t| t.name == name) }
2972    pub fn count(&self) -> usize { self.templates.len() }
2973}
2974
2975impl Default for ShaderTemplateLibrary {
2976    fn default() -> Self { Self::new() }
2977}
2978
2979// ── Shader Live Reload ────────────────────────────────────────────────────────
2980
2981#[derive(Clone, Debug)]
2982pub struct ShaderFileWatcher {
2983    pub watched_files: HashMap<String, ShaderWatchEntry>,
2984    pub reload_queue: VecDeque<String>,
2985    pub auto_reload: bool,
2986    pub debounce_ms: u32,
2987}
2988
2989#[derive(Clone, Debug)]
2990pub struct ShaderWatchEntry {
2991    pub path: String,
2992    pub last_modified: u64,
2993    pub shader_ids: Vec<u32>,
2994    pub reload_count: u32,
2995}
2996
2997impl ShaderFileWatcher {
2998    pub fn new() -> Self { Self { watched_files: HashMap::new(), reload_queue: VecDeque::new(), auto_reload: true, debounce_ms: 500 } }
2999    pub fn watch(&mut self, path: impl Into<String>, shader_id: u32) {
3000        let p = path.into();
3001        self.watched_files.entry(p.clone()).or_insert(ShaderWatchEntry { path: p, last_modified: 0, shader_ids: Vec::new(), reload_count: 0 }).shader_ids.push(shader_id);
3002    }
3003    pub fn mark_modified(&mut self, path: &str) {
3004        if let Some(entry) = self.watched_files.get_mut(path) {
3005            entry.reload_count += 1;
3006            if !self.reload_queue.contains(&path.to_string()) { self.reload_queue.push_back(path.to_string()); }
3007        }
3008    }
3009    pub fn next_reload(&mut self) -> Option<String> { self.reload_queue.pop_front() }
3010    pub fn shader_ids_for(&self, path: &str) -> Vec<u32> { self.watched_files.get(path).map(|e| e.shader_ids.clone()).unwrap_or_default() }
3011    pub fn unwatch(&mut self, path: &str) { self.watched_files.remove(path); }
3012    pub fn watched_count(&self) -> usize { self.watched_files.len() }
3013}
3014
3015impl Default for ShaderFileWatcher {
3016    fn default() -> Self { Self::new() }
3017}
3018
3019// ── Shader Variant System ─────────────────────────────────────────────────────
3020
3021#[derive(Clone, Debug)]
3022pub struct ShaderVariant {
3023    pub id: u32,
3024    pub base_program_id: u32,
3025    pub keywords: Vec<String>,
3026    pub compiled_id: Option<u32>,
3027    pub is_fallback: bool,
3028}
3029
3030impl ShaderVariant {
3031    pub fn new(id: u32, base_id: u32) -> Self { Self { id, base_program_id: base_id, keywords: Vec::new(), compiled_id: None, is_fallback: false } }
3032    pub fn with_keyword(mut self, kw: impl Into<String>) -> Self { self.keywords.push(kw.into()); self }
3033    pub fn matches_keywords(&self, keywords: &[&str]) -> bool { keywords.iter().all(|kw| self.keywords.iter().any(|k| k == kw)) }
3034    pub fn keyword_hash(&self) -> u64 {
3035        let mut sorted = self.keywords.clone(); sorted.sort();
3036        sorted.iter().fold(0u64, |h, k| h.wrapping_mul(31).wrapping_add(k.bytes().map(|b| b as u64).sum::<u64>()))
3037    }
3038}
3039
3040#[derive(Clone, Debug)]
3041pub struct ShaderVariantCollection {
3042    pub base_id: u32,
3043    pub variants: Vec<ShaderVariant>,
3044    pub fallback_id: Option<u32>,
3045    pub next_id: u32,
3046}
3047
3048impl ShaderVariantCollection {
3049    pub fn new(base_id: u32) -> Self { Self { base_id, variants: Vec::new(), fallback_id: None, next_id: 1 } }
3050    pub fn add_variant(&mut self, keywords: Vec<String>) -> u32 {
3051        let id = self.next_id; self.next_id += 1;
3052        let mut v = ShaderVariant::new(id, self.base_id);
3053        v.keywords = keywords;
3054        self.variants.push(v);
3055        id
3056    }
3057    pub fn find_best_match(&self, keywords: &[&str]) -> Option<&ShaderVariant> {
3058        let mut best: Option<&ShaderVariant> = None;
3059        let mut best_matches = 0;
3060        for v in &self.variants {
3061            let matches = keywords.iter().filter(|kw| v.keywords.iter().any(|k| k == *kw)).count();
3062            if matches > best_matches { best_matches = matches; best = Some(v); }
3063        }
3064        best.or_else(|| self.fallback_id.and_then(|id| self.variants.iter().find(|v| v.id == id)))
3065    }
3066    pub fn variant_count(&self) -> usize { self.variants.len() }
3067    pub fn set_fallback(&mut self, id: u32) { self.fallback_id = Some(id); }
3068}
3069
3070// ── Shader include resolver ────────────────────────────────────────────────────
3071
3072#[derive(Clone, Debug)]
3073pub struct ShaderIncludeResolver {
3074    pub virtual_fs: HashMap<String, String>,
3075    pub include_paths: Vec<String>,
3076    pub resolved_cache: HashMap<String, String>,
3077    pub max_size_bytes: usize,
3078}
3079
3080impl ShaderIncludeResolver {
3081    pub fn new() -> Self { Self { virtual_fs: HashMap::new(), include_paths: Vec::new(), resolved_cache: HashMap::new(), max_size_bytes: 1024 * 1024 } }
3082    pub fn register_virtual(&mut self, path: impl Into<String>, content: impl Into<String>) { self.virtual_fs.insert(path.into(), content.into()); }
3083    pub fn add_include_path(&mut self, path: impl Into<String>) { self.include_paths.push(path.into()); }
3084    pub fn resolve(&self, include_path: &str) -> Option<&str> {
3085        self.resolved_cache.get(include_path).map(|s| s.as_str()).or_else(|| self.virtual_fs.get(include_path).map(|s| s.as_str()))
3086    }
3087    pub fn cache_result(&mut self, include_path: String, content: String) { self.resolved_cache.insert(include_path, content); }
3088    pub fn clear_cache(&mut self) { self.resolved_cache.clear(); }
3089    pub fn virtual_file_count(&self) -> usize { self.virtual_fs.len() }
3090}
3091
3092impl Default for ShaderIncludeResolver {
3093    fn default() -> Self { Self::new() }
3094}
3095
3096// ── More shader constants ─────────────────────────────────────────────────────
3097
3098pub const SHADER_VARIANT_MAX_KEYWORDS: usize = 16;
3099pub const SHADER_TEMPLATE_MAX_PARAMS: usize = 32;
3100pub const SHADER_WATCHER_DEBOUNCE_DEFAULT: u32 = 500;
3101pub const SHADER_OPTIMIZER_UNROLL_DEFAULT: u32 = 8;
3102pub const SHADER_AST_MAX_DEPTH: usize = 64;
3103pub const SHADER_MAX_VARIANTS_PER_PROGRAM: usize = 256;
3104pub const SHADER_INCLUDE_RESOLVER_CACHE_MAX: usize = 512;
3105pub const SHADER_CODEGEN_INDENT_DEFAULT: usize = 4;
3106pub const SHADER_DIAGNOSTIC_MAX: usize = 1000;
3107
3108pub fn shader_targets() -> &'static [&'static str] { &["glsl450", "glsl300es", "wgsl", "hlsl50", "msl20"] }
3109pub fn shader_target_count() -> usize { shader_targets().len() }
3110pub fn is_gpu_shader_target(target: &CodeGenTarget) -> bool { !matches!(target, CodeGenTarget::Wgsl) || true }
3111
3112
3113// ── Material System ───────────────────────────────────────────────────────────
3114
3115#[derive(Clone, Debug)]
3116pub struct Material {
3117    pub id: u32,
3118    pub name: String,
3119    pub shader_program_id: u32,
3120    pub textures: Vec<MaterialTexture>,
3121    pub properties: HashMap<String, MaterialProperty>,
3122    pub pipeline: PipelineState,
3123    pub render_queue: u32,
3124    pub cast_shadows: bool,
3125    pub receive_shadows: bool,
3126    pub instanced: bool,
3127    pub double_sided: bool,
3128}
3129
3130#[derive(Clone, Debug)]
3131pub struct MaterialTexture {
3132    pub name: String,
3133    pub texture_id: u32,
3134    pub slot: u32,
3135    pub sampler_type: SamplerType,
3136    pub uv_channel: u32,
3137}
3138
3139#[derive(Clone, Debug)]
3140pub enum MaterialProperty {
3141    Float(f32),
3142    Vec2([f32; 2]),
3143    Vec3([f32; 3]),
3144    Vec4([f32; 4]),
3145    Int(i32),
3146    Bool(bool),
3147    Color([f32; 4]),
3148}
3149
3150#[derive(Clone, Debug, PartialEq)]
3151pub enum SamplerType { Linear, Nearest, LinearMipmap, Trilinear, Anisotropic }
3152
3153impl Material {
3154    pub fn new(id: u32, name: impl Into<String>, shader_id: u32) -> Self {
3155        Self { id, name: name.into(), shader_program_id: shader_id, textures: Vec::new(), properties: HashMap::new(), pipeline: PipelineState::default(), render_queue: 2000, cast_shadows: true, receive_shadows: true, instanced: false, double_sided: false }
3156    }
3157    pub fn add_texture(&mut self, name: impl Into<String>, tex_id: u32, slot: u32) {
3158        self.textures.push(MaterialTexture { name: name.into(), texture_id: tex_id, slot, sampler_type: SamplerType::Trilinear, uv_channel: 0 });
3159    }
3160    pub fn set_float(&mut self, name: impl Into<String>, value: f32) { self.properties.insert(name.into(), MaterialProperty::Float(value)); }
3161    pub fn set_color(&mut self, name: impl Into<String>, r: f32, g: f32, b: f32, a: f32) { self.properties.insert(name.into(), MaterialProperty::Color([r, g, b, a])); }
3162    pub fn set_vec4(&mut self, name: impl Into<String>, v: [f32; 4]) { self.properties.insert(name.into(), MaterialProperty::Vec4(v)); }
3163    pub fn is_transparent(&self) -> bool { self.pipeline.is_transparent() }
3164    pub fn is_opaque(&self) -> bool { !self.is_transparent() }
3165    pub fn texture_count(&self) -> usize { self.textures.len() }
3166    pub fn property_count(&self) -> usize { self.properties.len() }
3167    pub fn make_transparent(&mut self) { self.pipeline = PipelineState::transparent(); self.render_queue = 3000; }
3168    pub fn make_additive(&mut self) { self.pipeline = PipelineState::additive(); self.render_queue = 3500; }
3169}
3170
3171#[derive(Clone, Debug)]
3172pub struct MaterialLibrary {
3173    pub materials: HashMap<u32, Material>,
3174    pub next_id: u32,
3175    pub default_material_id: u32,
3176}
3177
3178impl MaterialLibrary {
3179    pub fn new() -> Self { Self { materials: HashMap::new(), next_id: 1, default_material_id: 0 } }
3180    pub fn add(&mut self, mat: Material) -> u32 {
3181        let id = self.next_id; self.next_id += 1;
3182        self.materials.insert(id, mat);
3183        id
3184    }
3185    pub fn get(&self, id: u32) -> Option<&Material> { self.materials.get(&id) }
3186    pub fn get_mut(&mut self, id: u32) -> Option<&mut Material> { self.materials.get_mut(&id) }
3187    pub fn find_by_name(&self, name: &str) -> Option<&Material> { self.materials.values().find(|m| m.name == name) }
3188    pub fn opaque_materials(&self) -> Vec<&Material> { self.materials.values().filter(|m| m.is_opaque()).collect() }
3189    pub fn transparent_materials(&self) -> Vec<&Material> { self.materials.values().filter(|m| m.is_transparent()).collect() }
3190    pub fn count(&self) -> usize { self.materials.len() }
3191    pub fn remove(&mut self, id: u32) -> bool { self.materials.remove(&id).is_some() }
3192    pub fn set_default(&mut self, id: u32) { self.default_material_id = id; }
3193}
3194
3195impl Default for MaterialLibrary {
3196    fn default() -> Self { Self::new() }
3197}
3198
3199// ── Render Pass System ────────────────────────────────────────────────────────
3200
3201#[derive(Clone, Debug)]
3202pub struct RenderPass {
3203    pub id: u32,
3204    pub name: String,
3205    pub pass_type: RenderPassType,
3206    pub color_attachments: Vec<ColorAttachment>,
3207    pub depth_attachment: Option<DepthAttachment>,
3208    pub clear_color: [f32; 4],
3209    pub clear_depth: f32,
3210    pub clear_stencil: u8,
3211    pub viewport: (u32, u32, u32, u32),
3212    pub enabled: bool,
3213    pub order: u32,
3214}
3215
3216#[derive(Clone, Debug, PartialEq)]
3217pub enum RenderPassType { Opaque, Transparent, Shadow, PostProcess, Ui, Compute, Custom }
3218
3219#[derive(Clone, Debug)]
3220pub struct ColorAttachment {
3221    pub texture_id: u32,
3222    pub format: AttachmentFormat,
3223    pub load_op: LoadOp,
3224    pub store_op: StoreOp,
3225    pub mip_level: u32,
3226    pub layer: u32,
3227}
3228
3229#[derive(Clone, Debug)]
3230pub struct DepthAttachment {
3231    pub texture_id: u32,
3232    pub format: AttachmentFormat,
3233    pub load_op: LoadOp,
3234    pub store_op: StoreOp,
3235    pub read_only: bool,
3236}
3237
3238#[derive(Clone, Debug, PartialEq)]
3239pub enum AttachmentFormat { Rgba8, Rgba16F, Rgba32F, R8, R16F, R32F, Rg8, Rg16F, Depth16, Depth24, Depth32F, Depth24Stencil8 }
3240
3241#[derive(Clone, Debug, PartialEq)]
3242pub enum LoadOp { Load, Clear, DontCare }
3243#[derive(Clone, Debug, PartialEq)]
3244pub enum StoreOp { Store, DontCare }
3245
3246impl RenderPass {
3247    pub fn new(id: u32, name: impl Into<String>, pass_type: RenderPassType) -> Self {
3248        Self { id, name: name.into(), pass_type, color_attachments: Vec::new(), depth_attachment: None, clear_color: [0.0, 0.0, 0.0, 1.0], clear_depth: 1.0, clear_stencil: 0, viewport: (0, 0, 1920, 1080), enabled: true, order: 0 }
3249    }
3250    pub fn add_color_attachment(&mut self, tex_id: u32, format: AttachmentFormat) {
3251        self.color_attachments.push(ColorAttachment { texture_id: tex_id, format, load_op: LoadOp::Clear, store_op: StoreOp::Store, mip_level: 0, layer: 0 });
3252    }
3253    pub fn set_depth(&mut self, tex_id: u32) {
3254        self.depth_attachment = Some(DepthAttachment { texture_id: tex_id, format: AttachmentFormat::Depth32F, load_op: LoadOp::Clear, store_op: StoreOp::Store, read_only: false });
3255    }
3256    pub fn is_shadow_pass(&self) -> bool { self.pass_type == RenderPassType::Shadow }
3257    pub fn is_postprocess(&self) -> bool { self.pass_type == RenderPassType::PostProcess }
3258    pub fn attachment_count(&self) -> usize { self.color_attachments.len() + if self.depth_attachment.is_some() { 1 } else { 0 } }
3259    pub fn set_viewport(&mut self, x: u32, y: u32, w: u32, h: u32) { self.viewport = (x, y, w, h); }
3260}
3261
3262#[derive(Clone, Debug)]
3263pub struct RenderPipeline {
3264    pub passes: Vec<RenderPass>,
3265    pub next_id: u32,
3266}
3267
3268impl RenderPipeline {
3269    pub fn new() -> Self { Self { passes: Vec::new(), next_id: 1 } }
3270    pub fn add_pass(&mut self, pass: RenderPass) { self.passes.push(pass); }
3271    pub fn sorted_passes(&self) -> Vec<&RenderPass> {
3272        let mut sorted: Vec<_> = self.passes.iter().filter(|p| p.enabled).collect();
3273        sorted.sort_by_key(|p| p.order);
3274        sorted
3275    }
3276    pub fn pass_count(&self) -> usize { self.passes.len() }
3277    pub fn find_by_type(&self, pass_type: &RenderPassType) -> Vec<&RenderPass> {
3278        self.passes.iter().filter(|p| &p.pass_type == pass_type).collect()
3279    }
3280    pub fn enable_pass(&mut self, id: u32) { if let Some(p) = self.passes.iter_mut().find(|p| p.id == id) { p.enabled = true; } }
3281    pub fn disable_pass(&mut self, id: u32) { if let Some(p) = self.passes.iter_mut().find(|p| p.id == id) { p.enabled = false; } }
3282}
3283
3284impl Default for RenderPipeline {
3285    fn default() -> Self { Self::new() }
3286}
3287
3288// ── Post-Process Effect ───────────────────────────────────────────────────────
3289
3290#[derive(Clone, Debug)]
3291pub struct PostProcessEffect {
3292    pub id: u32,
3293    pub name: String,
3294    pub effect_type: PostEffectType,
3295    pub shader_program_id: u32,
3296    pub properties: HashMap<String, f32>,
3297    pub enabled: bool,
3298    pub order: u32,
3299    pub input_texture: u32,
3300    pub output_texture: u32,
3301}
3302
3303#[derive(Clone, Debug, PartialEq)]
3304pub enum PostEffectType { Bloom, Tonemap, Vignette, ChromaticAberration, DepthOfField, MotionBlur, Ssao, Fxaa, Custom }
3305
3306impl PostProcessEffect {
3307    pub fn new(id: u32, name: impl Into<String>, effect_type: PostEffectType, shader_id: u32) -> Self {
3308        Self { id, name: name.into(), effect_type, shader_program_id: shader_id, properties: HashMap::new(), enabled: true, order: 0, input_texture: 0, output_texture: 0 }
3309    }
3310    pub fn set_property(&mut self, key: impl Into<String>, val: f32) { self.properties.insert(key.into(), val); }
3311    pub fn get_property(&self, key: &str) -> f32 { self.properties.get(key).copied().unwrap_or(0.0) }
3312    pub fn bloom_intensity(&self) -> f32 { self.get_property("intensity") }
3313    pub fn vignette_strength(&self) -> f32 { self.get_property("strength") }
3314    pub fn is_bloom(&self) -> bool { self.effect_type == PostEffectType::Bloom }
3315    pub fn is_tonemap(&self) -> bool { self.effect_type == PostEffectType::Tonemap }
3316}
3317
3318#[derive(Clone, Debug)]
3319pub struct PostProcessStack {
3320    pub effects: Vec<PostProcessEffect>,
3321    pub enabled: bool,
3322}
3323
3324impl PostProcessStack {
3325    pub fn new() -> Self { Self { effects: Vec::new(), enabled: true } }
3326    pub fn add(&mut self, effect: PostProcessEffect) { self.effects.push(effect); }
3327    pub fn enabled_effects(&self) -> Vec<&PostProcessEffect> {
3328        let mut v: Vec<_> = self.effects.iter().filter(|e| e.enabled).collect();
3329        v.sort_by_key(|e| e.order);
3330        v
3331    }
3332    pub fn enable_effect(&mut self, id: u32) { if let Some(e) = self.effects.iter_mut().find(|e| e.id == id) { e.enabled = true; } }
3333    pub fn disable_effect(&mut self, id: u32) { if let Some(e) = self.effects.iter_mut().find(|e| e.id == id) { e.enabled = false; } }
3334    pub fn effect_count(&self) -> usize { self.effects.len() }
3335    pub fn active_count(&self) -> usize { self.effects.iter().filter(|e| e.enabled).count() }
3336}
3337
3338impl Default for PostProcessStack {
3339    fn default() -> Self { Self::new() }
3340}
3341
3342// ── Shader Node Graph ─────────────────────────────────────────────────────────
3343
3344#[derive(Clone, Debug)]
3345pub struct ShaderNode {
3346    pub id: u32,
3347    pub node_type: ShaderNodeType,
3348    pub label: String,
3349    pub position: (f32, f32),
3350    pub inputs: Vec<ShaderNodePort>,
3351    pub outputs: Vec<ShaderNodePort>,
3352    pub properties: HashMap<String, String>,
3353}
3354
3355#[derive(Clone, Debug, PartialEq)]
3356pub enum ShaderNodeType {
3357    Input, Output, Math, Texture, Color, Constant, Mix, Clamp, Normalize, Length, Dot, Cross, Reflect, Refract, Fresnel, NormalMap, VertexShader, FragmentShader, Custom(String),
3358}
3359
3360#[derive(Clone, Debug)]
3361pub struct ShaderNodePort {
3362    pub id: u32,
3363    pub name: String,
3364    pub port_type: ShaderDataType,
3365    pub connected_to: Option<(u32, u32)>,
3366    pub default_value: String,
3367}
3368
3369impl ShaderNode {
3370    pub fn new(id: u32, node_type: ShaderNodeType, label: impl Into<String>) -> Self {
3371        Self { id, node_type, label: label.into(), position: (0.0, 0.0), inputs: Vec::new(), outputs: Vec::new(), properties: HashMap::new() }
3372    }
3373    pub fn add_input(&mut self, name: impl Into<String>, port_type: ShaderDataType) -> u32 {
3374        let pid = self.inputs.len() as u32;
3375        self.inputs.push(ShaderNodePort { id: pid, name: name.into(), port_type, connected_to: None, default_value: "0.0".into() });
3376        pid
3377    }
3378    pub fn add_output(&mut self, name: impl Into<String>, port_type: ShaderDataType) -> u32 {
3379        let pid = self.outputs.len() as u32;
3380        self.outputs.push(ShaderNodePort { id: pid, name: name.into(), port_type, connected_to: None, default_value: "0.0".into() });
3381        pid
3382    }
3383    pub fn set_property(&mut self, key: impl Into<String>, val: impl Into<String>) { self.properties.insert(key.into(), val.into()); }
3384    pub fn is_output_node(&self) -> bool { self.node_type == ShaderNodeType::Output }
3385    pub fn is_input_node(&self) -> bool { self.node_type == ShaderNodeType::Input }
3386    pub fn input_count(&self) -> usize { self.inputs.len() }
3387    pub fn output_count(&self) -> usize { self.outputs.len() }
3388}
3389
3390#[derive(Clone, Debug)]
3391pub struct ShaderNodeGraph {
3392    pub nodes: HashMap<u32, ShaderNode>,
3393    pub connections: Vec<(u32, u32, u32, u32)>,
3394    pub next_id: u32,
3395    pub name: String,
3396}
3397
3398impl ShaderNodeGraph {
3399    pub fn new(name: impl Into<String>) -> Self { Self { nodes: HashMap::new(), connections: Vec::new(), next_id: 1, name: name.into() } }
3400    pub fn add_node(&mut self, node_type: ShaderNodeType, label: impl Into<String>) -> u32 {
3401        let id = self.next_id; self.next_id += 1;
3402        self.nodes.insert(id, ShaderNode::new(id, node_type, label));
3403        id
3404    }
3405    pub fn connect(&mut self, from_node: u32, from_port: u32, to_node: u32, to_port: u32) {
3406        self.connections.push((from_node, from_port, to_node, to_port));
3407        if let Some(node) = self.nodes.get_mut(&to_node) {
3408            if let Some(port) = node.inputs.get_mut(to_port as usize) { port.connected_to = Some((from_node, from_port)); }
3409        }
3410    }
3411    pub fn disconnect(&mut self, to_node: u32, to_port: u32) {
3412        self.connections.retain(|(_, _, tn, tp)| !(*tn == to_node && *tp == to_port));
3413        if let Some(node) = self.nodes.get_mut(&to_node) {
3414            if let Some(port) = node.inputs.get_mut(to_port as usize) { port.connected_to = None; }
3415        }
3416    }
3417    pub fn remove_node(&mut self, id: u32) {
3418        self.nodes.remove(&id);
3419        self.connections.retain(|(a, _, b, _)| *a != id && *b != id);
3420    }
3421    pub fn node_count(&self) -> usize { self.nodes.len() }
3422    pub fn connection_count(&self) -> usize { self.connections.len() }
3423    pub fn output_node(&self) -> Option<&ShaderNode> { self.nodes.values().find(|n| n.is_output_node()) }
3424    pub fn move_node(&mut self, id: u32, x: f32, y: f32) { if let Some(n) = self.nodes.get_mut(&id) { n.position = (x, y); } }
3425}
3426
3427impl Default for ShaderNodeGraph {
3428    fn default() -> Self { Self::new("new_graph") }
3429}
3430
3431// ── More constants ────────────────────────────────────────────────────────────
3432
3433pub const MATERIAL_MAX_TEXTURES: usize = 16;
3434pub const MATERIAL_MAX_PROPERTIES: usize = 64;
3435pub const RENDER_PASS_MAX: usize = 32;
3436pub const POST_PROCESS_EFFECT_MAX: usize = 16;
3437pub const SHADER_NODE_GRAPH_MAX_NODES: usize = 512;
3438pub const SHADER_NODE_GRAPH_MAX_CONNECTIONS: usize = 1024;
3439pub const MATERIAL_OPAQUE_QUEUE: u32 = 2000;
3440pub const MATERIAL_TRANSPARENT_QUEUE: u32 = 3000;
3441pub const MATERIAL_OVERLAY_QUEUE: u32 = 4000;
3442
3443pub fn attachment_format_name(fmt: &AttachmentFormat) -> &'static str {
3444    match fmt {
3445        AttachmentFormat::Rgba8 => "RGBA8", AttachmentFormat::Rgba16F => "RGBA16F",
3446        AttachmentFormat::Rgba32F => "RGBA32F", AttachmentFormat::Depth32F => "Depth32F",
3447        AttachmentFormat::Depth24Stencil8 => "Depth24Stencil8", _ => "Unknown",
3448    }
3449}
3450pub fn blend_factor_glsl(f: &BlendFactor) -> &'static str {
3451    match f {
3452        BlendFactor::Zero => "GL_ZERO", BlendFactor::One => "GL_ONE",
3453        BlendFactor::SrcAlpha => "GL_SRC_ALPHA", BlendFactor::OneMinusSrcAlpha => "GL_ONE_MINUS_SRC_ALPHA",
3454        _ => "GL_ONE",
3455    }
3456}
3457
3458
3459// ── Texture System ────────────────────────────────────────────────────────────
3460
3461#[derive(Clone, Debug)]
3462pub struct TextureDescriptor {
3463    pub id: u32,
3464    pub name: String,
3465    pub width: u32,
3466    pub height: u32,
3467    pub depth: u32,
3468    pub mip_levels: u32,
3469    pub array_layers: u32,
3470    pub format: AttachmentFormat,
3471    pub tex_type: TextureType,
3472    pub usage: TextureUsage,
3473    pub filter: TextureFilter,
3474    pub wrap_u: WrapMode,
3475    pub wrap_v: WrapMode,
3476    pub wrap_w: WrapMode,
3477    pub anisotropy: u32,
3478    pub generate_mipmaps: bool,
3479    pub srgb: bool,
3480    pub compressed: bool,
3481}
3482
3483#[derive(Clone, Debug, PartialEq)]
3484pub enum TextureType { Tex2D, Tex3D, TexCube, Tex2DArray, TexCubeArray }
3485#[derive(Clone, Debug, PartialEq)]
3486pub enum TextureUsage { Sampled, Storage, RenderTarget, DepthStencil }
3487#[derive(Clone, Debug, PartialEq)]
3488pub enum TextureFilter { Nearest, Linear, Trilinear, Anisotropic }
3489#[derive(Clone, Debug, PartialEq)]
3490pub enum WrapMode { Repeat, MirroredRepeat, ClampToEdge, ClampToBorder, MirrorClampToEdge }
3491
3492impl TextureDescriptor {
3493    pub fn new_2d(id: u32, name: impl Into<String>, width: u32, height: u32, format: AttachmentFormat) -> Self {
3494        Self { id, name: name.into(), width, height, depth: 1, mip_levels: 1, array_layers: 1, format, tex_type: TextureType::Tex2D, usage: TextureUsage::Sampled, filter: TextureFilter::Trilinear, wrap_u: WrapMode::Repeat, wrap_v: WrapMode::Repeat, wrap_w: WrapMode::ClampToEdge, anisotropy: 4, generate_mipmaps: true, srgb: false, compressed: false }
3495    }
3496    pub fn render_target(id: u32, name: impl Into<String>, w: u32, h: u32, fmt: AttachmentFormat) -> Self {
3497        let mut t = Self::new_2d(id, name, w, h, fmt); t.usage = TextureUsage::RenderTarget; t.generate_mipmaps = false; t
3498    }
3499    pub fn depth_target(id: u32, name: impl Into<String>, w: u32, h: u32) -> Self {
3500        let mut t = Self::new_2d(id, name, w, h, AttachmentFormat::Depth32F); t.usage = TextureUsage::DepthStencil; t.generate_mipmaps = false; t
3501    }
3502    pub fn mip_count_for_size(width: u32, height: u32) -> u32 { (width.max(height) as f32).log2().floor() as u32 + 1 }
3503    pub fn bytes_per_pixel(&self) -> u32 {
3504        match self.format {
3505            AttachmentFormat::Rgba8 => 4, AttachmentFormat::Rgba16F => 8, AttachmentFormat::Rgba32F => 16,
3506            AttachmentFormat::R8 => 1, AttachmentFormat::R16F => 2, AttachmentFormat::R32F => 4,
3507            AttachmentFormat::Rg8 => 2, AttachmentFormat::Rg16F => 4,
3508            AttachmentFormat::Depth32F => 4, _ => 4,
3509        }
3510    }
3511    pub fn total_bytes(&self) -> u64 { self.width as u64 * self.height as u64 * self.depth as u64 * self.bytes_per_pixel() as u64 }
3512    pub fn is_hdr(&self) -> bool { matches!(self.format, AttachmentFormat::Rgba16F | AttachmentFormat::Rgba32F | AttachmentFormat::R16F | AttachmentFormat::R32F) }
3513    pub fn is_cubemap(&self) -> bool { self.tex_type == TextureType::TexCube }
3514    pub fn is_depth(&self) -> bool { matches!(self.format, AttachmentFormat::Depth16 | AttachmentFormat::Depth24 | AttachmentFormat::Depth32F | AttachmentFormat::Depth24Stencil8) }
3515    pub fn aspect_ratio(&self) -> f32 { if self.height == 0 { 1.0 } else { self.width as f32 / self.height as f32 } }
3516}
3517
3518#[derive(Clone, Debug)]
3519pub struct TextureRegistry {
3520    pub textures: HashMap<u32, TextureDescriptor>,
3521    pub next_id: u32,
3522}
3523
3524impl TextureRegistry {
3525    pub fn new() -> Self { Self { textures: HashMap::new(), next_id: 1 } }
3526    pub fn register(&mut self, mut tex: TextureDescriptor) -> u32 {
3527        let id = self.next_id; self.next_id += 1;
3528        tex.id = id;
3529        self.textures.insert(id, tex);
3530        id
3531    }
3532    pub fn get(&self, id: u32) -> Option<&TextureDescriptor> { self.textures.get(&id) }
3533    pub fn find_by_name(&self, name: &str) -> Option<&TextureDescriptor> { self.textures.values().find(|t| t.name == name) }
3534    pub fn total_bytes(&self) -> u64 { self.textures.values().map(|t| t.total_bytes()).sum() }
3535    pub fn count(&self) -> usize { self.textures.len() }
3536    pub fn hdr_textures(&self) -> Vec<&TextureDescriptor> { self.textures.values().filter(|t| t.is_hdr()).collect() }
3537    pub fn depth_textures(&self) -> Vec<&TextureDescriptor> { self.textures.values().filter(|t| t.is_depth()).collect() }
3538    pub fn remove(&mut self, id: u32) -> bool { self.textures.remove(&id).is_some() }
3539}
3540
3541impl Default for TextureRegistry {
3542    fn default() -> Self { Self::new() }
3543}
3544
3545// ── Sampler System ────────────────────────────────────────────────────────────
3546
3547#[derive(Clone, Debug)]
3548pub struct SamplerDescriptor {
3549    pub id: u32,
3550    pub min_filter: TextureFilter,
3551    pub mag_filter: TextureFilter,
3552    pub mip_filter: TextureFilter,
3553    pub wrap_u: WrapMode,
3554    pub wrap_v: WrapMode,
3555    pub wrap_w: WrapMode,
3556    pub anisotropy: u32,
3557    pub border_color: [f32; 4],
3558    pub lod_min: f32,
3559    pub lod_max: f32,
3560    pub lod_bias: f32,
3561    pub compare_op: Option<CompareFunc>,
3562}
3563
3564impl SamplerDescriptor {
3565    pub fn default_linear() -> Self {
3566        Self { id: 0, min_filter: TextureFilter::Linear, mag_filter: TextureFilter::Linear, mip_filter: TextureFilter::Linear, wrap_u: WrapMode::Repeat, wrap_v: WrapMode::Repeat, wrap_w: WrapMode::Repeat, anisotropy: 1, border_color: [0.0; 4], lod_min: 0.0, lod_max: 1000.0, lod_bias: 0.0, compare_op: None }
3567    }
3568    pub fn default_nearest() -> Self { Self { min_filter: TextureFilter::Nearest, mag_filter: TextureFilter::Nearest, mip_filter: TextureFilter::Nearest, ..Self::default_linear() } }
3569    pub fn shadow() -> Self { Self { compare_op: Some(CompareFunc::Less), ..Self::default_linear() } }
3570    pub fn clamp_to_edge() -> Self { Self { wrap_u: WrapMode::ClampToEdge, wrap_v: WrapMode::ClampToEdge, wrap_w: WrapMode::ClampToEdge, ..Self::default_linear() } }
3571    pub fn anisotropic_16() -> Self { Self { anisotropy: 16, ..Self::default_linear() } }
3572    pub fn is_shadow_sampler(&self) -> bool { self.compare_op.is_some() }
3573    pub fn is_anisotropic(&self) -> bool { self.anisotropy > 1 }
3574}
3575
3576// ── Vertex Layout ─────────────────────────────────────────────────────────────
3577
3578#[derive(Clone, Debug)]
3579pub struct VertexAttribute {
3580    pub location: u32,
3581    pub name: String,
3582    pub format: VertexAttribFormat,
3583    pub offset: u32,
3584    pub normalized: bool,
3585}
3586
3587#[derive(Clone, Debug, PartialEq, Copy)]
3588pub enum VertexAttribFormat {
3589    Float1, Float2, Float3, Float4,
3590    Sint1, Sint2, Sint3, Sint4,
3591    Uint1, Uint2, Uint3, Uint4,
3592    Unorm4, Snorm4,
3593    Half2, Half4,
3594}
3595
3596impl VertexAttribFormat {
3597    pub fn size_bytes(self) -> u32 {
3598        match self {
3599            VertexAttribFormat::Float1 | VertexAttribFormat::Sint1 | VertexAttribFormat::Uint1 => 4,
3600            VertexAttribFormat::Float2 | VertexAttribFormat::Sint2 | VertexAttribFormat::Uint2 | VertexAttribFormat::Half4 => 8,
3601            VertexAttribFormat::Float3 | VertexAttribFormat::Sint3 | VertexAttribFormat::Uint3 => 12,
3602            VertexAttribFormat::Float4 | VertexAttribFormat::Sint4 | VertexAttribFormat::Uint4 | VertexAttribFormat::Unorm4 | VertexAttribFormat::Snorm4 => 16,
3603            VertexAttribFormat::Half2 => 4,
3604        }
3605    }
3606    pub fn component_count(self) -> u32 {
3607        match self {
3608            VertexAttribFormat::Float1 | VertexAttribFormat::Sint1 | VertexAttribFormat::Uint1 => 1,
3609            VertexAttribFormat::Float2 | VertexAttribFormat::Sint2 | VertexAttribFormat::Uint2 | VertexAttribFormat::Half2 => 2,
3610            VertexAttribFormat::Float3 | VertexAttribFormat::Sint3 | VertexAttribFormat::Uint3 => 3,
3611            _ => 4,
3612        }
3613    }
3614    pub fn glsl_type_name(self) -> &'static str {
3615        match self {
3616            VertexAttribFormat::Float1 => "float", VertexAttribFormat::Float2 => "vec2",
3617            VertexAttribFormat::Float3 => "vec3", VertexAttribFormat::Float4 => "vec4",
3618            VertexAttribFormat::Sint1 => "int", VertexAttribFormat::Sint2 => "ivec2",
3619            VertexAttribFormat::Sint3 => "ivec3", VertexAttribFormat::Sint4 => "ivec4",
3620            VertexAttribFormat::Uint1 => "uint", VertexAttribFormat::Uint2 => "uvec2",
3621            VertexAttribFormat::Uint3 => "uvec3", VertexAttribFormat::Uint4 => "uvec4",
3622            _ => "vec4",
3623        }
3624    }
3625}
3626
3627#[derive(Clone, Debug)]
3628pub struct VertexLayout {
3629    pub attributes: Vec<VertexAttribute>,
3630    pub stride: u32,
3631    pub instanced: bool,
3632    pub instance_rate: u32,
3633}
3634
3635impl VertexLayout {
3636    pub fn new() -> Self { Self { attributes: Vec::new(), stride: 0, instanced: false, instance_rate: 1 } }
3637    pub fn add_attribute(&mut self, location: u32, name: impl Into<String>, format: VertexAttribFormat) {
3638        let offset = self.stride;
3639        self.stride += format.size_bytes();
3640        self.attributes.push(VertexAttribute { location, name: name.into(), format, offset, normalized: false });
3641    }
3642    pub fn standard_mesh() -> Self {
3643        let mut layout = Self::new();
3644        layout.add_attribute(0, "position", VertexAttribFormat::Float3);
3645        layout.add_attribute(1, "normal", VertexAttribFormat::Float3);
3646        layout.add_attribute(2, "uv", VertexAttribFormat::Float2);
3647        layout.add_attribute(3, "tangent", VertexAttribFormat::Float4);
3648        layout
3649    }
3650    pub fn position_only() -> Self {
3651        let mut layout = Self::new();
3652        layout.add_attribute(0, "position", VertexAttribFormat::Float3);
3653        layout
3654    }
3655    pub fn particle() -> Self {
3656        let mut layout = Self::new();
3657        layout.add_attribute(0, "position", VertexAttribFormat::Float3);
3658        layout.add_attribute(1, "color", VertexAttribFormat::Unorm4);
3659        layout.add_attribute(2, "size_rotation", VertexAttribFormat::Float2);
3660        layout
3661    }
3662    pub fn attribute_count(&self) -> usize { self.attributes.len() }
3663    pub fn generate_glsl_inputs(&self) -> String {
3664        self.attributes.iter().map(|a| format!("layout(location={}) in {} {};\n", a.location, a.format.glsl_type_name(), a.name)).collect()
3665    }
3666}
3667
3668impl Default for VertexLayout {
3669    fn default() -> Self { Self::standard_mesh() }
3670}
3671
3672// ── Buffer System ─────────────────────────────────────────────────────────────
3673
3674#[derive(Clone, Debug)]
3675pub struct GpuBuffer {
3676    pub id: u32,
3677    pub name: String,
3678    pub size_bytes: u64,
3679    pub usage: BufferUsage,
3680    pub access: BufferAccess,
3681    pub binding: Option<u32>,
3682    pub stride: u32,
3683    pub element_count: u32,
3684}
3685
3686#[derive(Clone, Debug, PartialEq)]
3687pub enum BufferUsage { Vertex, Index, Uniform, Storage, Indirect, Staging }
3688#[derive(Clone, Debug, PartialEq)]
3689pub enum BufferAccess { GpuOnly, CpuToGpu, GpuToCpu, CpuOnly }
3690
3691impl GpuBuffer {
3692    pub fn new(id: u32, name: impl Into<String>, size: u64, usage: BufferUsage) -> Self {
3693        Self { id, name: name.into(), size_bytes: size, usage, access: BufferAccess::GpuOnly, binding: None, stride: 0, element_count: 0 }
3694    }
3695    pub fn vertex(id: u32, name: impl Into<String>, vertex_count: u32, stride: u32) -> Self {
3696        let mut b = Self::new(id, name, vertex_count as u64 * stride as u64, BufferUsage::Vertex);
3697        b.stride = stride; b.element_count = vertex_count; b
3698    }
3699    pub fn index(id: u32, name: impl Into<String>, index_count: u32, is_u32: bool) -> Self {
3700        let stride = if is_u32 { 4 } else { 2 };
3701        let mut b = Self::new(id, name, index_count as u64 * stride, BufferUsage::Index);
3702        b.stride = stride as u32; b.element_count = index_count; b
3703    }
3704    pub fn uniform(id: u32, name: impl Into<String>, size: u64, binding: u32) -> Self {
3705        let mut b = Self::new(id, name, size, BufferUsage::Uniform);
3706        b.binding = Some(binding); b
3707    }
3708    pub fn is_vertex_buffer(&self) -> bool { self.usage == BufferUsage::Vertex }
3709    pub fn is_index_buffer(&self) -> bool { self.usage == BufferUsage::Index }
3710    pub fn is_uniform_buffer(&self) -> bool { self.usage == BufferUsage::Uniform }
3711    pub fn size_kb(&self) -> f64 { self.size_bytes as f64 / 1024.0 }
3712    pub fn size_mb(&self) -> f64 { self.size_bytes as f64 / (1024.0 * 1024.0) }
3713}
3714
3715#[derive(Clone, Debug)]
3716pub struct BufferRegistry {
3717    pub buffers: HashMap<u32, GpuBuffer>,
3718    pub next_id: u32,
3719    pub total_bytes: u64,
3720}
3721
3722impl BufferRegistry {
3723    pub fn new() -> Self { Self { buffers: HashMap::new(), next_id: 1, total_bytes: 0 } }
3724    pub fn register(&mut self, mut buf: GpuBuffer) -> u32 {
3725        let id = self.next_id; self.next_id += 1;
3726        buf.id = id;
3727        self.total_bytes += buf.size_bytes;
3728        self.buffers.insert(id, buf);
3729        id
3730    }
3731    pub fn get(&self, id: u32) -> Option<&GpuBuffer> { self.buffers.get(&id) }
3732    pub fn remove(&mut self, id: u32) -> bool {
3733        if let Some(b) = self.buffers.remove(&id) { self.total_bytes = self.total_bytes.saturating_sub(b.size_bytes); true } else { false }
3734    }
3735    pub fn by_usage(&self, usage: &BufferUsage) -> Vec<&GpuBuffer> { self.buffers.values().filter(|b| &b.usage == usage).collect() }
3736    pub fn total_mb(&self) -> f64 { self.total_bytes as f64 / (1024.0 * 1024.0) }
3737    pub fn count(&self) -> usize { self.buffers.len() }
3738}
3739
3740impl Default for BufferRegistry {
3741    fn default() -> Self { Self::new() }
3742}
3743
3744// ── Compute Shader ────────────────────────────────────────────────────────────
3745
3746#[derive(Clone, Debug)]
3747pub struct ComputePass {
3748    pub id: u32,
3749    pub name: String,
3750    pub shader_program_id: u32,
3751    pub dispatch_x: u32,
3752    pub dispatch_y: u32,
3753    pub dispatch_z: u32,
3754    pub bindings: Vec<ComputeBinding>,
3755    pub push_constants: Vec<u8>,
3756    pub enabled: bool,
3757}
3758
3759#[derive(Clone, Debug)]
3760pub struct ComputeBinding {
3761    pub set: u32,
3762    pub binding: u32,
3763    pub resource_type: ComputeResourceType,
3764    pub resource_id: u32,
3765}
3766
3767#[derive(Clone, Debug, PartialEq)]
3768pub enum ComputeResourceType { Texture, StorageTexture, Buffer, StorageBuffer, UniformBuffer }
3769
3770impl ComputePass {
3771    pub fn new(id: u32, name: impl Into<String>, shader_id: u32) -> Self {
3772        Self { id, name: name.into(), shader_program_id: shader_id, dispatch_x: 1, dispatch_y: 1, dispatch_z: 1, bindings: Vec::new(), push_constants: Vec::new(), enabled: true }
3773    }
3774    pub fn dispatch(mut self, x: u32, y: u32, z: u32) -> Self { self.dispatch_x = x; self.dispatch_y = y; self.dispatch_z = z; self }
3775    pub fn add_binding(&mut self, set: u32, binding: u32, res_type: ComputeResourceType, res_id: u32) {
3776        self.bindings.push(ComputeBinding { set, binding, resource_type: res_type, resource_id: res_id });
3777    }
3778    pub fn total_invocations(&self) -> u64 { self.dispatch_x as u64 * self.dispatch_y as u64 * self.dispatch_z as u64 }
3779    pub fn binding_count(&self) -> usize { self.bindings.len() }
3780}
3781
3782// ── GPU Memory Budget ─────────────────────────────────────────────────────────
3783
3784#[derive(Clone, Debug)]
3785pub struct GpuMemoryBudget {
3786    pub total_bytes: u64,
3787    pub available_bytes: u64,
3788    pub textures_bytes: u64,
3789    pub buffers_bytes: u64,
3790    pub render_targets_bytes: u64,
3791    pub shader_bytes: u64,
3792    pub misc_bytes: u64,
3793}
3794
3795impl GpuMemoryBudget {
3796    pub fn new(total: u64) -> Self {
3797        Self { total_bytes: total, available_bytes: total, textures_bytes: 0, buffers_bytes: 0, render_targets_bytes: 0, shader_bytes: 0, misc_bytes: 0 }
3798    }
3799    pub fn used_bytes(&self) -> u64 { self.textures_bytes + self.buffers_bytes + self.render_targets_bytes + self.shader_bytes + self.misc_bytes }
3800    pub fn usage_percent(&self) -> f32 { if self.total_bytes == 0 { 0.0 } else { self.used_bytes() as f32 / self.total_bytes as f32 * 100.0 } }
3801    pub fn is_overbudget(&self) -> bool { self.used_bytes() > self.total_bytes }
3802    pub fn headroom_mb(&self) -> f64 { (self.total_bytes.saturating_sub(self.used_bytes())) as f64 / (1024.0 * 1024.0) }
3803    pub fn allocate_texture(&mut self, bytes: u64) -> bool { if self.available_bytes >= bytes { self.textures_bytes += bytes; self.available_bytes -= bytes; true } else { false } }
3804    pub fn allocate_buffer(&mut self, bytes: u64) -> bool { if self.available_bytes >= bytes { self.buffers_bytes += bytes; self.available_bytes -= bytes; true } else { false } }
3805    pub fn free_texture(&mut self, bytes: u64) { self.textures_bytes = self.textures_bytes.saturating_sub(bytes); self.available_bytes += bytes.min(self.total_bytes); }
3806}
3807
3808// ── Render Statistics ─────────────────────────────────────────────────────────
3809
3810#[derive(Clone, Debug, Default)]
3811pub struct RenderStatistics {
3812    pub draw_calls: u32,
3813    pub triangles: u64,
3814    pub vertices: u64,
3815    pub texture_switches: u32,
3816    pub shader_switches: u32,
3817    pub render_pass_count: u32,
3818    pub compute_dispatches: u32,
3819    pub gpu_time_ms: f32,
3820    pub cpu_time_ms: f32,
3821    pub frame_number: u64,
3822}
3823
3824impl RenderStatistics {
3825    pub fn new() -> Self { Self::default() }
3826    pub fn begin_frame(&mut self) {
3827        self.draw_calls = 0; self.triangles = 0; self.vertices = 0;
3828        self.texture_switches = 0; self.shader_switches = 0;
3829        self.render_pass_count = 0; self.compute_dispatches = 0;
3830        self.gpu_time_ms = 0.0; self.cpu_time_ms = 0.0;
3831        self.frame_number += 1;
3832    }
3833    pub fn record_draw(&mut self, vertices: u64, triangles: u64) { self.draw_calls += 1; self.vertices += vertices; self.triangles += triangles; }
3834    pub fn fps(&self) -> f32 { if self.gpu_time_ms < 1e-6 { 0.0 } else { 1000.0 / self.gpu_time_ms } }
3835    pub fn avg_tris_per_draw(&self) -> f64 { if self.draw_calls == 0 { 0.0 } else { self.triangles as f64 / self.draw_calls as f64 } }
3836    pub fn total_ms(&self) -> f32 { self.gpu_time_ms + self.cpu_time_ms }
3837}
3838
3839// ── More shader constants ─────────────────────────────────────────────────────
3840
3841pub const TEXTURE_MAX_SIZE: u32 = 16384;
3842pub const TEXTURE_MAX_MIP_LEVELS: u32 = 15;
3843pub const TEXTURE_MAX_ARRAY_LAYERS: u32 = 2048;
3844pub const SAMPLER_MAX_ANISOTROPY: u32 = 16;
3845pub const BUFFER_ALIGNMENT: u32 = 256;
3846pub const VERTEX_LAYOUT_MAX_ATTRIBUTES: usize = 16;
3847pub const COMPUTE_MAX_WORKGROUP_SIZE: u32 = 1024;
3848pub const RENDER_PASS_MAX_COLOR_ATTACHMENTS: usize = 8;
3849pub const GPU_MEMORY_BUDGET_DEFAULT_MB: u64 = 4096;
3850pub const MATERIAL_DEFAULT_RENDER_QUEUE: u32 = 2000;
3851
3852pub fn texture_format_name(fmt: &AttachmentFormat) -> &'static str { attachment_format_name(fmt) }
3853pub fn vertex_format_size(fmt: VertexAttribFormat) -> u32 { fmt.size_bytes() }
3854pub fn buffer_aligned_size(size: u64) -> u64 { (size + BUFFER_ALIGNMENT as u64 - 1) & !(BUFFER_ALIGNMENT as u64 - 1) }
3855pub fn is_power_of_two(n: u32) -> bool { n > 0 && (n & (n - 1)) == 0 }
3856pub fn next_power_of_two(n: u32) -> u32 { if n == 0 { 1 } else { let mut p = n; p -= 1; p |= p >> 1; p |= p >> 2; p |= p >> 4; p |= p >> 8; p |= p >> 16; p + 1 } }
3857pub fn mip_count(width: u32, height: u32) -> u32 { TextureDescriptor::mip_count_for_size(width, height) }
3858pub fn shader_compiler_full_info() -> String {
3859    format!("ShaderCompiler — {} target platforms, materials, textures, buffers, render pipeline", shader_target_count())
3860}
3861
3862
3863// ── Lighting Model ────────────────────────────────────────────────────────────
3864
3865#[derive(Clone, Debug)]
3866pub struct LightingModel {
3867    pub name: String,
3868    pub model_type: LightingModelType,
3869    pub diffuse_weight: f32,
3870    pub specular_weight: f32,
3871    pub ambient_weight: f32,
3872    pub subsurface_weight: f32,
3873    pub emission_weight: f32,
3874    pub roughness: f32,
3875    pub metalness: f32,
3876    pub ior: f32,
3877    pub transmission: f32,
3878    pub anisotropy: f32,
3879    pub clearcoat: f32,
3880    pub sheen: f32,
3881}
3882
3883#[derive(Clone, Debug, PartialEq)]
3884pub enum LightingModelType { Phong, BlinnPhong, Pbr, Lambert, Toon, Unlit, Custom(String) }
3885
3886impl LightingModel {
3887    pub fn phong() -> Self { Self { name: "Phong".into(), model_type: LightingModelType::Phong, diffuse_weight: 1.0, specular_weight: 0.5, ambient_weight: 0.1, subsurface_weight: 0.0, emission_weight: 0.0, roughness: 0.5, metalness: 0.0, ior: 1.5, transmission: 0.0, anisotropy: 0.0, clearcoat: 0.0, sheen: 0.0 } }
3888    pub fn pbr() -> Self { Self { name: "PBR".into(), model_type: LightingModelType::Pbr, diffuse_weight: 1.0, specular_weight: 1.0, ambient_weight: 0.1, subsurface_weight: 0.0, emission_weight: 0.0, roughness: 0.5, metalness: 0.0, ior: 1.5, transmission: 0.0, anisotropy: 0.0, clearcoat: 0.0, sheen: 0.0 } }
3889    pub fn unlit() -> Self { Self { name: "Unlit".into(), model_type: LightingModelType::Unlit, diffuse_weight: 0.0, specular_weight: 0.0, ambient_weight: 0.0, subsurface_weight: 0.0, emission_weight: 1.0, roughness: 1.0, metalness: 0.0, ior: 1.0, transmission: 0.0, anisotropy: 0.0, clearcoat: 0.0, sheen: 0.0 } }
3890    pub fn toon() -> Self { Self { name: "Toon".into(), model_type: LightingModelType::Toon, diffuse_weight: 1.0, specular_weight: 1.0, ambient_weight: 0.2, subsurface_weight: 0.0, emission_weight: 0.0, roughness: 1.0, metalness: 0.0, ior: 1.5, transmission: 0.0, anisotropy: 0.0, clearcoat: 0.0, sheen: 0.0 } }
3891    pub fn is_pbr(&self) -> bool { self.model_type == LightingModelType::Pbr }
3892    pub fn is_unlit(&self) -> bool { self.model_type == LightingModelType::Unlit }
3893    pub fn is_toon(&self) -> bool { self.model_type == LightingModelType::Toon }
3894    pub fn uses_ibl(&self) -> bool { self.is_pbr() }
3895    pub fn glsl_function_name(&self) -> &str {
3896        match self.model_type { LightingModelType::Phong => "shade_phong", LightingModelType::BlinnPhong => "shade_blinn_phong", LightingModelType::Pbr => "shade_pbr", LightingModelType::Lambert => "shade_lambert", LightingModelType::Toon => "shade_toon", LightingModelType::Unlit => "shade_unlit", _ => "shade_custom" }
3897    }
3898}
3899
3900impl Default for LightingModel {
3901    fn default() -> Self { Self::pbr() }
3902}
3903
3904// ── GLSL Code Snippets ────────────────────────────────────────────────────────
3905
3906pub struct GlslSnippet {
3907    pub name: &'static str,
3908    pub code: &'static str,
3909    pub dependencies: &'static [&'static str],
3910}
3911
3912pub const SNIPPET_GAMMA_CORRECT: GlslSnippet = GlslSnippet {
3913    name: "gamma_correct",
3914    code: "vec3 gamma_correct(vec3 color) { return pow(clamp(color, 0.0, 1.0), vec3(1.0 / 2.2)); }",
3915    dependencies: &[],
3916};
3917
3918pub const SNIPPET_LINEAR_TO_SRGB: GlslSnippet = GlslSnippet {
3919    name: "linear_to_srgb",
3920    code: "vec3 linear_to_srgb(vec3 c) { return mix(c * 12.92, 1.055 * pow(c, vec3(1.0/2.4)) - 0.055, step(vec3(0.0031308), c)); }",
3921    dependencies: &[],
3922};
3923
3924pub const SNIPPET_SRGB_TO_LINEAR: GlslSnippet = GlslSnippet {
3925    name: "srgb_to_linear",
3926    code: "vec3 srgb_to_linear(vec3 c) { return mix(c / 12.92, pow((c + 0.055) / 1.055, vec3(2.4)), step(vec3(0.04045), c)); }",
3927    dependencies: &[],
3928};
3929
3930pub const SNIPPET_ACES_TONEMAP: GlslSnippet = GlslSnippet {
3931    name: "aces_tonemap",
3932    code: "vec3 aces_tonemap(vec3 x) { float a=2.51; float b=0.03; float c=2.43; float d=0.59; float e=0.14; return clamp((x*(a*x+b))/(x*(c*x+d)+e),0.0,1.0); }",
3933    dependencies: &[],
3934};
3935
3936pub const SNIPPET_REINHARD_TONEMAP: GlslSnippet = GlslSnippet {
3937    name: "reinhard",
3938    code: "vec3 reinhard(vec3 c) { return c / (c + vec3(1.0)); }",
3939    dependencies: &[],
3940};
3941
3942pub const SNIPPET_FRESNEL: GlslSnippet = GlslSnippet {
3943    name: "fresnel_schlick",
3944    code: "vec3 fresnel_schlick(float cosTheta, vec3 F0) { return F0 + (1.0 - F0) * pow(clamp(1.0 - cosTheta, 0.0, 1.0), 5.0); }",
3945    dependencies: &[],
3946};
3947
3948pub const SNIPPET_NDF_GGX: GlslSnippet = GlslSnippet {
3949    name: "ndf_ggx",
3950    code: "float ndf_ggx(vec3 N, vec3 H, float roughness) { float a = roughness*roughness; float a2 = a*a; float NdotH = max(dot(N,H),0.0); float NdotH2 = NdotH*NdotH; float num = a2; float denom = NdotH2*(a2-1.0)+1.0; denom = 3.14159265*denom*denom; return num/denom; }",
3951    dependencies: &[],
3952};
3953
3954pub const SNIPPET_GEOMETRY_SMITH: GlslSnippet = GlslSnippet {
3955    name: "geometry_smith",
3956    code: "float geometry_schlick_ggx(float NdotV, float r) { float r2=(r+1.0); float k=r2*r2/8.0; return NdotV/(NdotV*(1.0-k)+k); } float geometry_smith(vec3 N, vec3 V, vec3 L, float r) { return geometry_schlick_ggx(max(dot(N,V),0.0),r)*geometry_schlick_ggx(max(dot(N,L),0.0),r); }",
3957    dependencies: &[],
3958};
3959
3960pub const SNIPPET_SHADOW_PCF: GlslSnippet = GlslSnippet {
3961    name: "shadow_pcf",
3962    code: "float shadow_pcf(sampler2DShadow shadowMap, vec4 shadowCoord) { float shadow = 0.0; vec2 texelSize = 1.0/textureSize(shadowMap,0); for(int x=-1;x<=1;++x) for(int y=-1;y<=1;++y) shadow += texture(shadowMap, shadowCoord.xyz/shadowCoord.w + vec3(vec2(x,y)*texelSize,0)); return shadow/9.0; }",
3963    dependencies: &[],
3964};
3965
3966pub const SNIPPET_NORMAL_FROM_MAP: GlslSnippet = GlslSnippet {
3967    name: "normal_from_map",
3968    code: "vec3 normal_from_map(sampler2D normalMap, vec2 uv, vec3 worldNormal, vec3 worldTangent) { vec3 n = texture(normalMap, uv).xyz * 2.0 - 1.0; vec3 T = normalize(worldTangent - dot(worldTangent,worldNormal)*worldNormal); vec3 B = cross(worldNormal, T); mat3 TBN = mat3(T,B,worldNormal); return normalize(TBN * n); }",
3969    dependencies: &[],
3970};
3971
3972pub const SNIPPET_PARALLAX_OCCLUSION: GlslSnippet = GlslSnippet {
3973    name: "parallax_occlusion",
3974    code: "vec2 parallax_mapping(sampler2D heightMap, vec2 uv, vec3 viewDir, float scale) { float numLayers = mix(32.0, 8.0, abs(dot(vec3(0,0,1),viewDir))); float layerDepth = 1.0/numLayers; float currentDepth = 0.0; vec2 P = viewDir.xy/viewDir.z*scale; vec2 deltaTexCoords = P/numLayers; vec2 currentTexCoords = uv; float currentDepthMapValue = 1.0 - texture(heightMap,currentTexCoords).r; while(currentDepth < currentDepthMapValue) { currentTexCoords -= deltaTexCoords; currentDepthMapValue = 1.0 - texture(heightMap,currentTexCoords).r; currentDepth += layerDepth; } return currentTexCoords; }",
3975    dependencies: &[],
3976};
3977
3978// ── Shader Library ────────────────────────────────────────────────────────────
3979
3980#[derive(Clone, Debug)]
3981pub struct ShaderLibrary {
3982    pub snippets: HashMap<String, String>,
3983    pub categories: HashMap<String, Vec<String>>,
3984}
3985
3986impl ShaderLibrary {
3987    pub fn new() -> Self { Self { snippets: HashMap::new(), categories: HashMap::new() } }
3988    pub fn add(&mut self, name: impl Into<String>, code: impl Into<String>, category: impl Into<String>) {
3989        let n = name.into(); let cat = category.into();
3990        self.snippets.insert(n.clone(), code.into());
3991        self.categories.entry(cat).or_default().push(n);
3992    }
3993    pub fn get(&self, name: &str) -> Option<&str> { self.snippets.get(name).map(|s| s.as_str()) }
3994    pub fn in_category(&self, cat: &str) -> Vec<&str> {
3995        self.categories.get(cat).map(|names| names.iter().map(|n| self.snippets.get(n.as_str()).map(|_| n.as_str()).unwrap_or("")).collect()).unwrap_or_default()
3996    }
3997    pub fn count(&self) -> usize { self.snippets.len() }
3998    pub fn build_standard() -> Self {
3999        let mut lib = Self::new();
4000        lib.add("gamma_correct", SNIPPET_GAMMA_CORRECT.code, "color");
4001        lib.add("linear_to_srgb", SNIPPET_LINEAR_TO_SRGB.code, "color");
4002        lib.add("srgb_to_linear", SNIPPET_SRGB_TO_LINEAR.code, "color");
4003        lib.add("aces_tonemap", SNIPPET_ACES_TONEMAP.code, "tonemap");
4004        lib.add("reinhard", SNIPPET_REINHARD_TONEMAP.code, "tonemap");
4005        lib.add("fresnel_schlick", SNIPPET_FRESNEL.code, "pbr");
4006        lib.add("ndf_ggx", SNIPPET_NDF_GGX.code, "pbr");
4007        lib.add("geometry_smith", SNIPPET_GEOMETRY_SMITH.code, "pbr");
4008        lib.add("shadow_pcf", SNIPPET_SHADOW_PCF.code, "shadow");
4009        lib.add("normal_from_map", SNIPPET_NORMAL_FROM_MAP.code, "normal");
4010        lib.add("parallax_occlusion", SNIPPET_PARALLAX_OCCLUSION.code, "normal");
4011        lib
4012    }
4013}
4014
4015impl Default for ShaderLibrary {
4016    fn default() -> Self { Self::build_standard() }
4017}
4018
4019// ── Shader Debug Tools ────────────────────────────────────────────────────────
4020
4021#[derive(Clone, Debug)]
4022pub struct ShaderDebugOutput {
4023    pub name: String,
4024    pub output_type: DebugOutputType,
4025    pub channel_mask: [bool; 4],
4026    pub scale: f32,
4027    pub bias: f32,
4028    pub enabled: bool,
4029}
4030
4031#[derive(Clone, Debug, PartialEq)]
4032pub enum DebugOutputType { Albedo, Normal, Roughness, Metalness, Depth, AmbientOcclusion, Emission, Velocity, ShadowMap, LightContribution, Custom(String) }
4033
4034impl ShaderDebugOutput {
4035    pub fn new(name: impl Into<String>, output_type: DebugOutputType) -> Self {
4036        Self { name: name.into(), output_type, channel_mask: [true; 4], scale: 1.0, bias: 0.0, enabled: true }
4037    }
4038    pub fn depth() -> Self { let mut d = Self::new("Depth", DebugOutputType::Depth); d.scale = 100.0; d }
4039    pub fn normal() -> Self { let mut d = Self::new("Normal", DebugOutputType::Normal); d.bias = 0.5; d }
4040    pub fn glsl_code(&self) -> String {
4041        format!("// Debug output: {}", self.name)
4042    }
4043}
4044
4045#[derive(Clone, Debug)]
4046pub struct ShaderDebugger {
4047    pub outputs: Vec<ShaderDebugOutput>,
4048    pub active_output: Option<usize>,
4049    pub enabled: bool,
4050    pub show_overdraw: bool,
4051    pub show_wireframe: bool,
4052    pub show_normals: bool,
4053}
4054
4055impl ShaderDebugger {
4056    pub fn new() -> Self { Self { outputs: Vec::new(), active_output: None, enabled: false, show_overdraw: false, show_wireframe: false, show_normals: false } }
4057    pub fn add_output(&mut self, output: ShaderDebugOutput) { self.outputs.push(output); }
4058    pub fn activate(&mut self, idx: usize) { self.active_output = Some(idx); self.enabled = true; }
4059    pub fn deactivate(&mut self) { self.active_output = None; self.enabled = false; }
4060    pub fn active(&self) -> Option<&ShaderDebugOutput> { self.active_output.and_then(|i| self.outputs.get(i)) }
4061    pub fn output_count(&self) -> usize { self.outputs.len() }
4062    pub fn build_defaults() -> Self {
4063        let mut d = Self::new();
4064        d.add_output(ShaderDebugOutput::new("Albedo", DebugOutputType::Albedo));
4065        d.add_output(ShaderDebugOutput::normal());
4066        d.add_output(ShaderDebugOutput::new("Roughness", DebugOutputType::Roughness));
4067        d.add_output(ShaderDebugOutput::new("Metalness", DebugOutputType::Metalness));
4068        d.add_output(ShaderDebugOutput::depth());
4069        d.add_output(ShaderDebugOutput::new("AO", DebugOutputType::AmbientOcclusion));
4070        d
4071    }
4072}
4073
4074impl Default for ShaderDebugger {
4075    fn default() -> Self { Self::build_defaults() }
4076}
4077
4078// ── Shader Profiler ───────────────────────────────────────────────────────────
4079
4080#[derive(Clone, Debug, Default)]
4081pub struct ShaderProfileEntry {
4082    pub shader_id: u32,
4083    pub shader_name: String,
4084    pub gpu_time_ms: f32,
4085    pub draw_calls: u32,
4086    pub triangles: u64,
4087    pub frame_count: u64,
4088    pub avg_gpu_ms: f32,
4089}
4090
4091impl ShaderProfileEntry {
4092    pub fn new(shader_id: u32, name: impl Into<String>) -> Self { Self { shader_id, shader_name: name.into(), ..Default::default() } }
4093    pub fn record_frame(&mut self, gpu_ms: f32, draws: u32, tris: u64) {
4094        self.gpu_time_ms = gpu_ms;
4095        self.draw_calls = draws;
4096        self.triangles = tris;
4097        self.avg_gpu_ms = (self.avg_gpu_ms * self.frame_count as f32 + gpu_ms) / (self.frame_count + 1) as f32;
4098        self.frame_count += 1;
4099    }
4100    pub fn is_expensive(&self) -> bool { self.avg_gpu_ms > 2.0 }
4101}
4102
4103#[derive(Clone, Debug)]
4104pub struct ShaderProfiler {
4105    pub entries: HashMap<u32, ShaderProfileEntry>,
4106    pub enabled: bool,
4107    pub frame: u64,
4108}
4109
4110impl ShaderProfiler {
4111    pub fn new() -> Self { Self { entries: HashMap::new(), enabled: false, frame: 0 } }
4112    pub fn enable(&mut self) { self.enabled = true; }
4113    pub fn disable(&mut self) { self.enabled = false; }
4114    pub fn begin_frame(&mut self) { self.frame += 1; }
4115    pub fn record(&mut self, shader_id: u32, name: &str, gpu_ms: f32, draws: u32, tris: u64) {
4116        if !self.enabled { return; }
4117        self.entries.entry(shader_id).or_insert_with(|| ShaderProfileEntry::new(shader_id, name)).record_frame(gpu_ms, draws, tris);
4118    }
4119    pub fn expensive_shaders(&self) -> Vec<&ShaderProfileEntry> { self.entries.values().filter(|e| e.is_expensive()).collect() }
4120    pub fn total_gpu_ms(&self) -> f32 { self.entries.values().map(|e| e.gpu_time_ms).sum() }
4121    pub fn shader_count(&self) -> usize { self.entries.len() }
4122}
4123
4124impl Default for ShaderProfiler {
4125    fn default() -> Self { Self::new() }
4126}
4127
4128// ── Shader Hot Reload Manager ─────────────────────────────────────────────────
4129
4130#[derive(Clone, Debug)]
4131pub struct HotReloadManager {
4132    pub watcher: ShaderFileWatcher,
4133    pub registry: ShaderProgramRegistry,
4134    pub reload_count: u32,
4135    pub last_reload_time: f32,
4136    pub reload_on_focus: bool,
4137    pub auto_save_on_compile: bool,
4138}
4139
4140impl HotReloadManager {
4141    pub fn new() -> Self { Self { watcher: ShaderFileWatcher::new(), registry: ShaderProgramRegistry::new(), reload_count: 0, last_reload_time: 0.0, reload_on_focus: true, auto_save_on_compile: false } }
4142    pub fn watch_shader(&mut self, path: impl Into<String>, shader_id: u32) { self.watcher.watch(path, shader_id); }
4143    pub fn tick(&mut self, _dt: f32) -> Vec<u32> {
4144        let mut reloaded = Vec::new();
4145        while let Some(path) = self.watcher.next_reload() {
4146            let ids = self.watcher.shader_ids_for(&path);
4147            reloaded.extend_from_slice(&ids);
4148            self.reload_count += ids.len() as u32;
4149        }
4150        reloaded
4151    }
4152    pub fn force_reload_all(&mut self) {
4153        let paths: Vec<_> = self.watcher.watched_files.keys().cloned().collect();
4154        for path in paths { self.watcher.mark_modified(&path); }
4155    }
4156    pub fn reload_count(&self) -> u32 { self.reload_count }
4157}
4158
4159impl Default for HotReloadManager {
4160    fn default() -> Self { Self::new() }
4161}
4162
4163// ── More constants ────────────────────────────────────────────────────────────
4164
4165pub const LIGHTING_MODEL_COUNT: usize = 7;
4166pub const SHADER_LIBRARY_STANDARD_SNIPPET_COUNT: usize = 11;
4167pub const SHADER_DEBUG_OUTPUT_MAX: usize = 16;
4168pub const SHADER_PROFILER_EXPENSIVE_THRESHOLD_MS: f32 = 2.0;
4169pub const SHADER_HOT_RELOAD_DEBOUNCE_MS: u32 = 300;
4170pub const MATERIAL_MAX_BLEND_MODES: usize = 8;
4171pub const SHADER_SNIPPET_MAX_SIZE_CHARS: usize = 4096;
4172pub const GLSL_MAX_VARYING_COMPONENTS: u32 = 128;
4173pub const GLSL_MAX_TEXTURE_SAMPLERS: u32 = 16;
4174pub const GLSL_MAX_UNIFORM_BLOCKS: u32 = 14;
4175pub const WGSL_MAX_BIND_GROUPS: u32 = 4;
4176pub const WGSL_MAX_BINDINGS_PER_GROUP: u32 = 8;
4177
4178pub fn lighting_model_name(model: &LightingModel) -> &str { &model.name }
4179pub fn shader_system_info() -> String { format!("ShaderSystem: compiler + materials + textures + buffers + post-process + lighting + hot-reload") }
4180
4181
4182// ── Shader Permutation System ─────────────────────────────────────────────────
4183
4184#[derive(Clone, Debug)]
4185pub struct ShaderPermutationKey {
4186    pub features: Vec<String>,
4187}
4188
4189impl ShaderPermutationKey {
4190    pub fn new() -> Self { Self { features: Vec::new() } }
4191    pub fn with(mut self, feature: impl Into<String>) -> Self { self.features.push(feature.into()); self }
4192    pub fn add(&mut self, feature: impl Into<String>) { self.features.push(feature.into()); }
4193    pub fn hash_key(&self) -> u64 {
4194        let mut sorted = self.features.clone(); sorted.sort();
4195        sorted.iter().fold(14695981039346656037u64, |h, s| s.bytes().fold(h, |h, b| h.wrapping_mul(1099511628211) ^ b as u64))
4196    }
4197    pub fn has(&self, feature: &str) -> bool { self.features.contains(&feature.to_string()) }
4198    pub fn feature_count(&self) -> usize { self.features.len() }
4199    pub fn is_empty(&self) -> bool { self.features.is_empty() }
4200}
4201
4202impl Default for ShaderPermutationKey {
4203    fn default() -> Self { Self::new() }
4204}
4205
4206#[derive(Clone, Debug)]
4207pub struct ShaderPermutationRegistry {
4208    pub permutations: HashMap<u64, ShaderProgram>,
4209    pub keys: HashMap<u64, ShaderPermutationKey>,
4210    pub max_permutations: usize,
4211    pub base_program_id: u32,
4212}
4213
4214impl ShaderPermutationRegistry {
4215    pub fn new(base_id: u32, max: usize) -> Self { Self { permutations: HashMap::new(), keys: HashMap::new(), max_permutations: max, base_program_id: base_id } }
4216    pub fn register(&mut self, key: ShaderPermutationKey, program: ShaderProgram) -> bool {
4217        if self.permutations.len() >= self.max_permutations { return false; }
4218        let h = key.hash_key();
4219        self.permutations.insert(h, program);
4220        self.keys.insert(h, key);
4221        true
4222    }
4223    pub fn find(&self, key: &ShaderPermutationKey) -> Option<&ShaderProgram> { self.permutations.get(&key.hash_key()) }
4224    pub fn count(&self) -> usize { self.permutations.len() }
4225    pub fn clear(&mut self) { self.permutations.clear(); self.keys.clear(); }
4226}
4227
4228// ── Shader Reflection Extended ────────────────────────────────────────────────
4229
4230#[derive(Clone, Debug)]
4231pub struct ShaderBindingLayout {
4232    pub set: u32,
4233    pub bindings: Vec<DescriptorBinding>,
4234}
4235
4236#[derive(Clone, Debug)]
4237pub struct DescriptorBinding {
4238    pub binding: u32,
4239    pub descriptor_type: DescriptorType,
4240    pub count: u32,
4241    pub stages: Vec<ShaderStage>,
4242    pub name: String,
4243}
4244
4245#[derive(Clone, Debug, PartialEq)]
4246pub enum DescriptorType { UniformBuffer, StorageBuffer, Sampler, SampledImage, StorageImage, CombinedImageSampler, InputAttachment }
4247
4248#[derive(Clone, Debug, PartialEq)]
4249pub enum ShaderStage { Vertex, Fragment, Geometry, TessControl, TessEval, Compute, All }
4250
4251impl ShaderBindingLayout {
4252    pub fn new(set: u32) -> Self { Self { set, bindings: Vec::new() } }
4253    pub fn add_binding(&mut self, binding: u32, desc_type: DescriptorType, name: impl Into<String>) {
4254        self.bindings.push(DescriptorBinding { binding, descriptor_type: desc_type, count: 1, stages: vec![ShaderStage::All], name: name.into() });
4255    }
4256    pub fn binding_count(&self) -> usize { self.bindings.len() }
4257    pub fn has_binding(&self, binding: u32) -> bool { self.bindings.iter().any(|b| b.binding == binding) }
4258}
4259
4260#[derive(Clone, Debug)]
4261pub struct PipelineLayout {
4262    pub descriptor_sets: Vec<ShaderBindingLayout>,
4263    pub push_constant_size: u32,
4264    pub push_constant_stages: Vec<ShaderStage>,
4265}
4266
4267impl PipelineLayout {
4268    pub fn new() -> Self { Self { descriptor_sets: Vec::new(), push_constant_size: 0, push_constant_stages: Vec::new() } }
4269    pub fn add_set(&mut self, layout: ShaderBindingLayout) { self.descriptor_sets.push(layout); }
4270    pub fn set_push_constants(&mut self, size: u32) { self.push_constant_size = size; self.push_constant_stages = vec![ShaderStage::Vertex, ShaderStage::Fragment]; }
4271    pub fn set_count(&self) -> usize { self.descriptor_sets.len() }
4272    pub fn has_push_constants(&self) -> bool { self.push_constant_size > 0 }
4273    pub fn total_bindings(&self) -> usize { self.descriptor_sets.iter().map(|s| s.binding_count()).sum() }
4274}
4275
4276impl Default for PipelineLayout {
4277    fn default() -> Self { Self::new() }
4278}
4279
4280// ── Shader Compile Pipeline ───────────────────────────────────────────────────
4281
4282#[derive(Clone, Debug)]
4283pub struct CompilePipelineConfig {
4284    pub optimizer: ShaderOptimizer,
4285    pub codegen: ShaderCodeGen,
4286    pub preprocessor: ShaderPreprocessor,
4287    pub include_resolver: ShaderIncludeResolver,
4288    pub cache: ShaderCache,
4289    pub diagnostic_limit: usize,
4290    pub strict_mode: bool,
4291    pub pedantic: bool,
4292}
4293
4294impl CompilePipelineConfig {
4295    pub fn new(target: CodeGenTarget) -> Self {
4296        Self { optimizer: ShaderOptimizer::default(), codegen: ShaderCodeGen::new(target), preprocessor: ShaderPreprocessor::new(), include_resolver: ShaderIncludeResolver::new(), cache: ShaderCache::default(), diagnostic_limit: SHADER_DIAGNOSTIC_MAX, strict_mode: false, pedantic: false }
4297    }
4298    pub fn debug_config(target: CodeGenTarget) -> Self {
4299        let mut c = Self::new(target); c.optimizer = ShaderOptimizer::debug(); c
4300    }
4301    pub fn release_config(target: CodeGenTarget) -> Self {
4302        let mut c = Self::new(target); c.optimizer = ShaderOptimizer::release(); c
4303    }
4304    pub fn add_define(&mut self, key: impl Into<String>, val: impl Into<String>) { self.preprocessor.define(key, val); }
4305}
4306
4307#[derive(Clone, Debug)]
4308pub struct CompileResult {
4309    pub success: bool,
4310    pub spirv_bytes: Option<Vec<u8>>,
4311    pub diagnostics: DiagnosticList,
4312    pub reflection: ShaderReflection,
4313    pub compile_time_ms: f32,
4314    pub source_lines: u32,
4315    pub optimized: bool,
4316}
4317
4318impl CompileResult {
4319    pub fn ok(spirv: Vec<u8>, reflection: ShaderReflection) -> Self {
4320        Self { success: true, spirv_bytes: Some(spirv), diagnostics: DiagnosticList::new(), reflection, compile_time_ms: 0.0, source_lines: 0, optimized: false }
4321    }
4322    pub fn fail(diagnostics: DiagnosticList) -> Self {
4323        Self { success: false, spirv_bytes: None, diagnostics, reflection: ShaderReflection::new(), compile_time_ms: 0.0, source_lines: 0, optimized: false }
4324    }
4325    pub fn error_count(&self) -> usize { self.diagnostics.error_count() }
4326    pub fn warning_count(&self) -> usize { self.diagnostics.warning_count() }
4327    pub fn spirv_size(&self) -> usize { self.spirv_bytes.as_ref().map(|b| b.len()).unwrap_or(0) }
4328    pub fn has_warnings(&self) -> bool { self.diagnostics.warning_count() > 0 }
4329}
4330
4331// ── Shader Editor State ───────────────────────────────────────────────────────
4332
4333#[derive(Clone, Debug)]
4334pub struct ShaderEditorState {
4335    pub active_program_id: Option<u32>,
4336    pub active_stage: ShaderStage,
4337    pub cursor_line: u32,
4338    pub cursor_col: u32,
4339    pub scroll_top: u32,
4340    pub selection: Option<(u32, u32, u32, u32)>,
4341    pub font_size: f32,
4342    pub show_line_numbers: bool,
4343    pub show_minimap: bool,
4344    pub syntax_highlight: bool,
4345    pub auto_complete: bool,
4346    pub bracket_matching: bool,
4347    pub indent_guides: bool,
4348    pub word_wrap: bool,
4349    pub theme: String,
4350    pub undo_stack: VecDeque<String>,
4351    pub redo_stack: Vec<String>,
4352    pub modified: bool,
4353}
4354
4355impl ShaderEditorState {
4356    pub fn new() -> Self {
4357        Self { active_program_id: None, active_stage: ShaderStage::Fragment, cursor_line: 0, cursor_col: 0, scroll_top: 0, selection: None, font_size: 14.0, show_line_numbers: true, show_minimap: true, syntax_highlight: true, auto_complete: true, bracket_matching: true, indent_guides: true, word_wrap: false, theme: "dark".into(), undo_stack: VecDeque::new(), redo_stack: Vec::new(), modified: false }
4358    }
4359    pub fn set_active(&mut self, id: u32, stage: ShaderStage) { self.active_program_id = Some(id); self.active_stage = stage; self.cursor_line = 0; self.cursor_col = 0; }
4360    pub fn move_cursor(&mut self, line: u32, col: u32) { self.cursor_line = line; self.cursor_col = col; }
4361    pub fn push_undo(&mut self, desc: impl Into<String>) { if self.undo_stack.len() >= 100 { self.undo_stack.pop_front(); } self.undo_stack.push_back(desc.into()); self.redo_stack.clear(); self.modified = true; }
4362    pub fn can_undo(&self) -> bool { !self.undo_stack.is_empty() }
4363    pub fn can_redo(&self) -> bool { !self.redo_stack.is_empty() }
4364    pub fn undo(&mut self) -> Option<String> { let v = self.undo_stack.pop_back()?; self.redo_stack.push(v.clone()); Some(v) }
4365    pub fn redo(&mut self) -> Option<String> { let v = self.redo_stack.pop()?; self.undo_stack.push_back(v.clone()); Some(v) }
4366    pub fn mark_saved(&mut self) { self.modified = false; }
4367    pub fn increase_font(&mut self) { self.font_size = (self.font_size + 1.0).min(48.0); }
4368    pub fn decrease_font(&mut self) { self.font_size = (self.font_size - 1.0).max(8.0); }
4369}
4370
4371impl Default for ShaderEditorState {
4372    fn default() -> Self { Self::new() }
4373}
4374
4375// ── Autocomplete Provider ─────────────────────────────────────────────────────
4376
4377#[derive(Clone, Debug)]
4378pub struct AutoCompleteItem {
4379    pub label: String,
4380    pub kind: AutoCompleteKind,
4381    pub detail: String,
4382    pub insert_text: String,
4383    pub documentation: String,
4384}
4385
4386#[derive(Clone, Debug, PartialEq)]
4387pub enum AutoCompleteKind { Function, Variable, Type, Keyword, Snippet, Uniform }
4388
4389impl AutoCompleteItem {
4390    pub fn function(name: impl Into<String>, signature: impl Into<String>) -> Self {
4391        let n = name.into();
4392        Self { label: n.clone(), kind: AutoCompleteKind::Function, detail: signature.into(), insert_text: n, documentation: String::new() }
4393    }
4394    pub fn type_item(name: &'static str) -> Self {
4395        Self { label: name.into(), kind: AutoCompleteKind::Type, detail: "GLSL type".into(), insert_text: name.into(), documentation: String::new() }
4396    }
4397    pub fn keyword(kw: &'static str) -> Self {
4398        Self { label: kw.into(), kind: AutoCompleteKind::Keyword, detail: "keyword".into(), insert_text: kw.into(), documentation: String::new() }
4399    }
4400}
4401
4402#[derive(Clone, Debug)]
4403pub struct AutoCompleteProvider {
4404    pub builtins: Vec<AutoCompleteItem>,
4405    pub user_symbols: Vec<AutoCompleteItem>,
4406    pub max_results: usize,
4407}
4408
4409impl AutoCompleteProvider {
4410    pub fn new() -> Self {
4411        let mut builtins = Vec::new();
4412        for kw in &["void", "float", "int", "uint", "bool", "vec2", "vec3", "vec4", "mat3", "mat4", "sampler2D"] {
4413            builtins.push(AutoCompleteItem::type_item(kw));
4414        }
4415        for kw in &["if", "else", "for", "while", "return", "break", "continue", "discard", "uniform", "in", "out", "const"] {
4416            builtins.push(AutoCompleteItem::keyword(kw));
4417        }
4418        for (name, sig) in &[("normalize", "vec3 normalize(vec3)"), ("length", "float length(genType)"), ("dot", "float dot(genType, genType)"), ("cross", "vec3 cross(vec3, vec3)"), ("reflect", "genType reflect(genType, genType)"), ("refract", "genType refract(genType, genType, float)"), ("mix", "genType mix(genType, genType, genType)"), ("clamp", "genType clamp(genType, genType, genType)"), ("pow", "genType pow(genType, genType)"), ("sqrt", "genType sqrt(genType)"), ("abs", "genType abs(genType)"), ("max", "genType max(genType, genType)"), ("min", "genType min(genType, genType)"), ("texture", "vec4 texture(sampler2D, vec2)"), ("step", "genType step(genType, genType)"), ("smoothstep", "genType smoothstep(genType, genType, genType)"), ("sin", "genType sin(genType)"), ("cos", "genType cos(genType)"), ("tan", "genType tan(genType)"), ("floor", "genType floor(genType)"), ("ceil", "genType ceil(genType)"), ("fract", "genType fract(genType)"), ("mod", "genType mod(genType, genType)"), ("sign", "genType sign(genType)"), ("distance", "float distance(genType, genType)"), ("transpose", "mat transpose(mat)"), ("inverse", "mat inverse(mat)"), ("determinant", "float determinant(mat)")] {
4419            builtins.push(AutoCompleteItem::function(*name, *sig));
4420        }
4421        Self { builtins, user_symbols: Vec::new(), max_results: 20 }
4422    }
4423    pub fn query(&self, prefix: &str) -> Vec<&AutoCompleteItem> {
4424        let lower = prefix.to_lowercase();
4425        let mut results: Vec<_> = self.builtins.iter().chain(self.user_symbols.iter()).filter(|item| item.label.to_lowercase().starts_with(&lower)).collect();
4426        results.truncate(self.max_results);
4427        results
4428    }
4429    pub fn add_user_symbol(&mut self, item: AutoCompleteItem) { self.user_symbols.push(item); }
4430    pub fn clear_user_symbols(&mut self) { self.user_symbols.clear(); }
4431    pub fn builtin_count(&self) -> usize { self.builtins.len() }
4432}
4433
4434impl Default for AutoCompleteProvider {
4435    fn default() -> Self { Self::new() }
4436}
4437
4438// ── Constants ─────────────────────────────────────────────────────────────────
4439
4440pub const SHADER_EDITOR_MAX_UNDO: usize = 200;
4441pub const SHADER_AUTOCOMPLETE_MAX_RESULTS: usize = 20;
4442pub const SHADER_PERMUTATION_MAX: usize = 512;
4443pub const SHADER_BINDING_SET_MAX: usize = 4;
4444pub const SHADER_BINDING_MAX_PER_SET: usize = 16;
4445pub const SHADER_STAGE_COUNT: usize = 6;
4446pub const SHADER_PUSH_CONSTANT_MAX_BYTES: u32 = 128;
4447
4448pub fn shader_stage_name(stage: &ShaderStage) -> &'static str {
4449    match stage { ShaderStage::Vertex => "vertex", ShaderStage::Fragment => "fragment", ShaderStage::Geometry => "geometry", ShaderStage::TessControl => "tess_control", ShaderStage::TessEval => "tess_eval", ShaderStage::Compute => "compute", ShaderStage::All => "all" }
4450}
4451pub fn descriptor_type_name(dt: &DescriptorType) -> &'static str {
4452    match dt { DescriptorType::UniformBuffer => "UniformBuffer", DescriptorType::StorageBuffer => "StorageBuffer", DescriptorType::Sampler => "Sampler", DescriptorType::SampledImage => "SampledImage", DescriptorType::StorageImage => "StorageImage", DescriptorType::CombinedImageSampler => "CombinedImageSampler", DescriptorType::InputAttachment => "InputAttachment" }
4453}
4454
4455
4456// ── Shader Input/Output Binding Validation ────────────────────────────────────
4457
4458#[derive(Clone, Debug)]
4459pub struct ShaderInterface {
4460    pub inputs: Vec<ShaderVariable>,
4461    pub outputs: Vec<ShaderVariable>,
4462    pub stage: ShaderStage,
4463}
4464
4465impl ShaderInterface {
4466    pub fn new(stage: ShaderStage) -> Self { Self { inputs: Vec::new(), outputs: Vec::new(), stage } }
4467    pub fn add_input(&mut self, var: ShaderVariable) { self.inputs.push(var); }
4468    pub fn add_output(&mut self, var: ShaderVariable) { self.outputs.push(var); }
4469    pub fn input_count(&self) -> usize { self.inputs.len() }
4470    pub fn output_count(&self) -> usize { self.outputs.len() }
4471    pub fn find_input(&self, name: &str) -> Option<&ShaderVariable> { self.inputs.iter().find(|v| v.name == name) }
4472    pub fn find_output(&self, name: &str) -> Option<&ShaderVariable> { self.outputs.iter().find(|v| v.name == name) }
4473    pub fn validate_compatible(&self, next: &ShaderInterface) -> Vec<String> {
4474        let mut errors = Vec::new();
4475        for out in &self.outputs {
4476            if !next.inputs.iter().any(|i| i.name == out.name && i.data_type == out.data_type) {
4477                errors.push(format!("Output '{}' not matched in next stage", out.name));
4478            }
4479        }
4480        errors
4481    }
4482    pub fn total_output_components(&self) -> u32 { self.outputs.iter().map(|o| o.data_type.byte_size() / 4).sum() }
4483}
4484
4485// ── Shader Program Linker ─────────────────────────────────────────────────────
4486
4487#[derive(Clone, Debug)]
4488pub struct ShaderLinker {
4489    pub programs: HashMap<u32, ShaderProgram>,
4490    pub layouts: HashMap<u32, PipelineLayout>,
4491    pub link_errors: Vec<String>,
4492}
4493
4494impl ShaderLinker {
4495    pub fn new() -> Self { Self { programs: HashMap::new(), layouts: HashMap::new(), link_errors: Vec::new() } }
4496    pub fn add_program(&mut self, prog: ShaderProgram) { self.programs.insert(prog.id, prog); }
4497    pub fn link(&mut self, prog_id: u32) -> bool {
4498        self.link_errors.clear();
4499        if !self.programs.contains_key(&prog_id) { self.link_errors.push("Program not found".into()); return false; }
4500        true
4501    }
4502    pub fn has_errors(&self) -> bool { !self.link_errors.is_empty() }
4503    pub fn error_count(&self) -> usize { self.link_errors.len() }
4504    pub fn clear_errors(&mut self) { self.link_errors.clear(); }
4505}
4506
4507impl Default for ShaderLinker {
4508    fn default() -> Self { Self::new() }
4509}
4510
4511// ── Shader Export ─────────────────────────────────────────────────────────────
4512
4513#[derive(Clone, Debug)]
4514pub struct ShaderExportOptions {
4515    pub target: CodeGenTarget,
4516    pub minify: bool,
4517    pub include_reflection: bool,
4518    pub include_source: bool,
4519    pub embed_spirv: bool,
4520    pub output_format: ShaderExportFormat,
4521    pub output_path: String,
4522}
4523
4524#[derive(Clone, Debug, PartialEq)]
4525pub enum ShaderExportFormat { Glsl, SpirV, Wgsl, Json, Binary }
4526
4527impl ShaderExportOptions {
4528    pub fn new(target: CodeGenTarget) -> Self {
4529        Self { target, minify: false, include_reflection: true, include_source: true, embed_spirv: false, output_format: ShaderExportFormat::Glsl, output_path: "./shaders".into() }
4530    }
4531    pub fn release(target: CodeGenTarget) -> Self {
4532        Self { minify: true, include_source: false, ..Self::new(target) }
4533    }
4534    pub fn spirv(target: CodeGenTarget) -> Self {
4535        Self { embed_spirv: true, output_format: ShaderExportFormat::SpirV, ..Self::new(target) }
4536    }
4537}
4538
4539#[derive(Clone, Debug)]
4540pub struct ShaderExportResult {
4541    pub success: bool,
4542    pub programs_exported: u32,
4543    pub files_written: Vec<String>,
4544    pub errors: Vec<String>,
4545    pub warnings: Vec<String>,
4546    pub total_bytes: u64,
4547    pub duration_ms: f32,
4548}
4549
4550impl ShaderExportResult {
4551    pub fn ok(count: u32, files: Vec<String>, bytes: u64) -> Self {
4552        Self { success: true, programs_exported: count, files_written: files, errors: Vec::new(), warnings: Vec::new(), total_bytes: bytes, duration_ms: 0.0 }
4553    }
4554    pub fn fail(msg: impl Into<String>) -> Self {
4555        Self { success: false, programs_exported: 0, files_written: Vec::new(), errors: vec![msg.into()], warnings: Vec::new(), total_bytes: 0, duration_ms: 0.0 }
4556    }
4557}
4558
4559// ── Shader Linting ────────────────────────────────────────────────────────────
4560
4561#[derive(Clone, Debug)]
4562pub struct LintRule {
4563    pub id: u32,
4564    pub name: String,
4565    pub description: String,
4566    pub severity: DiagnosticKind,
4567    pub enabled: bool,
4568    pub pattern: String,
4569}
4570
4571impl LintRule {
4572    pub fn new(id: u32, name: impl Into<String>, severity: DiagnosticKind) -> Self {
4573        Self { id, name: name.into(), description: String::new(), severity, enabled: true, pattern: String::new() }
4574    }
4575    pub fn error(id: u32, name: impl Into<String>) -> Self { Self::new(id, name, DiagnosticKind::Error) }
4576    pub fn warn(id: u32, name: impl Into<String>) -> Self { Self::new(id, name, DiagnosticKind::Warning) }
4577    pub fn with_desc(mut self, desc: impl Into<String>) -> Self { self.description = desc.into(); self }
4578}
4579
4580#[derive(Clone, Debug)]
4581pub struct ShaderLinter {
4582    pub rules: Vec<LintRule>,
4583    pub enabled: bool,
4584}
4585
4586impl ShaderLinter {
4587    pub fn new() -> Self {
4588        let mut rules = Vec::new();
4589        rules.push(LintRule::warn(1, "unused_variable").with_desc("Variable declared but never used"));
4590        rules.push(LintRule::warn(2, "precision_missing").with_desc("Missing precision qualifier"));
4591        rules.push(LintRule::error(3, "undefined_variable").with_desc("Variable used before declaration"));
4592        rules.push(LintRule::warn(4, "divide_by_zero").with_desc("Potential division by constant zero"));
4593        rules.push(LintRule::warn(5, "comparison_float").with_desc("Float equality comparison may be imprecise"));
4594        rules.push(LintRule::warn(6, "redundant_cast").with_desc("Unnecessary type cast"));
4595        rules.push(LintRule::error(7, "wrong_argument_type").with_desc("Function argument type mismatch"));
4596        rules.push(LintRule::warn(8, "shadowed_variable").with_desc("Variable shadows outer scope declaration"));
4597        Self { rules, enabled: true }
4598    }
4599    pub fn lint(&self, _source: &str) -> DiagnosticList { DiagnosticList::new() }
4600    pub fn enabled_rules(&self) -> Vec<&LintRule> { self.rules.iter().filter(|r| r.enabled).collect() }
4601    pub fn disable_rule(&mut self, id: u32) { if let Some(r) = self.rules.iter_mut().find(|r| r.id == id) { r.enabled = false; } }
4602    pub fn enable_rule(&mut self, id: u32) { if let Some(r) = self.rules.iter_mut().find(|r| r.id == id) { r.enabled = true; } }
4603    pub fn rule_count(&self) -> usize { self.rules.len() }
4604}
4605
4606impl Default for ShaderLinter {
4607    fn default() -> Self { Self::new() }
4608}
4609
4610// ── Shader Formatting ─────────────────────────────────────────────────────────
4611
4612#[derive(Clone, Debug)]
4613pub struct ShaderFormatterConfig {
4614    pub indent_size: usize,
4615    pub use_tabs: bool,
4616    pub max_line_length: usize,
4617    pub brace_style: BraceStyle,
4618    pub space_around_operators: bool,
4619    pub space_after_comma: bool,
4620    pub trailing_newline: bool,
4621}
4622
4623#[derive(Clone, Debug, PartialEq)]
4624pub enum BraceStyle { Allman, KnR, Stroustrup }
4625
4626impl ShaderFormatterConfig {
4627    pub fn default_style() -> Self {
4628        Self { indent_size: 4, use_tabs: false, max_line_length: 120, brace_style: BraceStyle::KnR, space_around_operators: true, space_after_comma: true, trailing_newline: true }
4629    }
4630    pub fn compact() -> Self {
4631        Self { indent_size: 2, ..Self::default_style() }
4632    }
4633}
4634
4635impl Default for ShaderFormatterConfig {
4636    fn default() -> Self { Self::default_style() }
4637}
4638
4639#[derive(Clone, Debug)]
4640pub struct ShaderFormatter {
4641    pub config: ShaderFormatterConfig,
4642}
4643
4644impl ShaderFormatter {
4645    pub fn new() -> Self { Self { config: ShaderFormatterConfig::default() } }
4646    pub fn format(&self, source: &str) -> String {
4647        let mut output = String::with_capacity(source.len());
4648        let mut indent = 0usize;
4649        for line in source.lines() {
4650            let trimmed = line.trim();
4651            if trimmed.starts_with('}') && indent > 0 { indent -= 1; }
4652            let indent_str = if self.config.use_tabs { "\t".repeat(indent) } else { " ".repeat(indent * self.config.indent_size) };
4653            output.push_str(&indent_str);
4654            output.push_str(trimmed);
4655            output.push('\n');
4656            if trimmed.ends_with('{') { indent += 1; }
4657        }
4658        if self.config.trailing_newline && !output.ends_with('\n') { output.push('\n'); }
4659        output
4660    }
4661    pub fn count_lines(source: &str) -> usize { source.lines().count() }
4662    pub fn count_characters(source: &str) -> usize { source.chars().count() }
4663}
4664
4665impl Default for ShaderFormatter {
4666    fn default() -> Self { Self::new() }
4667}
4668
4669// ── Final shader constants ────────────────────────────────────────────────────
4670
4671pub const SHADER_LINTER_RULE_COUNT_DEFAULT: usize = 8;
4672pub const SHADER_FORMATTER_MAX_LINE_LENGTH: usize = 120;
4673pub const SHADER_EXPORT_FORMAT_COUNT: usize = 5;
4674pub const SHADER_LINKER_MAX_PROGRAMS: usize = 1024;
4675pub const SHADER_INTERFACE_MAX_VARYINGS: usize = 32;
4676pub const SHADER_PERMUTATION_KEY_MAX_FEATURES: usize = 16;
4677
4678pub fn shader_compiler_module_list() -> &'static [&'static str] {
4679    &[
4680        "tokenizer", "preprocessor", "parser", "ast", "codegen",
4681        "optimizer", "cache", "templates", "variants", "permutations",
4682        "include_resolver", "reflection", "diagnostics", "pipeline_state",
4683        "materials", "textures", "buffers", "render_passes", "post_process",
4684        "node_graph", "hot_reload", "lighting", "library", "debugger",
4685        "profiler", "editor_state", "autocomplete", "linker", "export",
4686        "linter", "formatter", "binding_layout",
4687    ]
4688}
4689
4690pub fn shader_module_count() -> usize { shader_compiler_module_list().len() }
4691
4692pub fn full_shader_system_description() -> String {
4693    format!("ShaderCompiler: {} modules — full pipeline from GLSL source to compiled GPU program", shader_module_count())
4694}
4695
4696
4697// ── Shader Uniform Tracking ───────────────────────────────────────────────────
4698
4699#[derive(Clone, Debug)]
4700pub struct UniformTracker {
4701    pub float_uniforms: HashMap<String, f32>,
4702    pub vec2_uniforms: HashMap<String, [f32; 2]>,
4703    pub vec3_uniforms: HashMap<String, [f32; 3]>,
4704    pub vec4_uniforms: HashMap<String, [f32; 4]>,
4705    pub int_uniforms: HashMap<String, i32>,
4706    pub bool_uniforms: HashMap<String, bool>,
4707    pub mat4_uniforms: HashMap<String, [[f32; 4]; 4]>,
4708    pub dirty_flags: HashSet<String>,
4709    pub upload_count: u64,
4710}
4711
4712impl UniformTracker {
4713    pub fn new() -> Self { Self { float_uniforms: HashMap::new(), vec2_uniforms: HashMap::new(), vec3_uniforms: HashMap::new(), vec4_uniforms: HashMap::new(), int_uniforms: HashMap::new(), bool_uniforms: HashMap::new(), mat4_uniforms: HashMap::new(), dirty_flags: HashSet::new(), upload_count: 0 } }
4714    pub fn set_float(&mut self, name: impl Into<String>, v: f32) { let n = name.into(); if self.float_uniforms.get(&n) != Some(&v) { self.float_uniforms.insert(n.clone(), v); self.dirty_flags.insert(n); } }
4715    pub fn set_vec3(&mut self, name: impl Into<String>, v: [f32; 3]) { let n = name.into(); self.vec3_uniforms.insert(n.clone(), v); self.dirty_flags.insert(n); }
4716    pub fn set_vec4(&mut self, name: impl Into<String>, v: [f32; 4]) { let n = name.into(); self.vec4_uniforms.insert(n.clone(), v); self.dirty_flags.insert(n); }
4717    pub fn set_int(&mut self, name: impl Into<String>, v: i32) { let n = name.into(); self.int_uniforms.insert(n.clone(), v); self.dirty_flags.insert(n); }
4718    pub fn set_bool(&mut self, name: impl Into<String>, v: bool) { let n = name.into(); self.bool_uniforms.insert(n.clone(), v); self.dirty_flags.insert(n); }
4719    pub fn mark_uploaded(&mut self) { self.dirty_flags.clear(); self.upload_count += 1; }
4720    pub fn dirty_count(&self) -> usize { self.dirty_flags.len() }
4721    pub fn has_dirty(&self) -> bool { !self.dirty_flags.is_empty() }
4722    pub fn get_float(&self, name: &str) -> Option<f32> { self.float_uniforms.get(name).copied() }
4723    pub fn get_vec4(&self, name: &str) -> Option<[f32; 4]> { self.vec4_uniforms.get(name).copied() }
4724    pub fn total_uniform_count(&self) -> usize { self.float_uniforms.len() + self.vec2_uniforms.len() + self.vec3_uniforms.len() + self.vec4_uniforms.len() + self.int_uniforms.len() + self.bool_uniforms.len() + self.mat4_uniforms.len() }
4725}
4726
4727impl Default for UniformTracker {
4728    fn default() -> Self { Self::new() }
4729}
4730
4731// ── Shader Error Reporting ────────────────────────────────────────────────────
4732
4733#[derive(Clone, Debug)]
4734pub struct ShaderErrorReport {
4735    pub program_id: u32,
4736    pub program_name: String,
4737    pub stage: String,
4738    pub errors: Vec<ShaderDiagnostic>,
4739    pub warnings: Vec<ShaderDiagnostic>,
4740    pub source_snippet: Vec<(u32, String)>,
4741    pub timestamp: u64,
4742    pub is_fatal: bool,
4743}
4744
4745impl ShaderErrorReport {
4746    pub fn new(program_id: u32, name: impl Into<String>, stage: impl Into<String>) -> Self {
4747        Self { program_id, program_name: name.into(), stage: stage.into(), errors: Vec::new(), warnings: Vec::new(), source_snippet: Vec::new(), timestamp: 0, is_fatal: false }
4748    }
4749    pub fn add_error(&mut self, err: ShaderDiagnostic) { if err.is_error() { self.is_fatal = true; } self.errors.push(err); }
4750    pub fn add_warning(&mut self, w: ShaderDiagnostic) { self.warnings.push(w); }
4751    pub fn add_source_line(&mut self, line_no: u32, line: impl Into<String>) { self.source_snippet.push((line_no, line.into())); }
4752    pub fn has_errors(&self) -> bool { !self.errors.is_empty() }
4753    pub fn format_report(&self) -> String {
4754        let mut s = format!("[{}] {}:{}\n", if self.is_fatal { "FATAL" } else { "WARN" }, self.program_name, self.stage);
4755        for e in &self.errors { s += &format!("  {}\n", e.format()); }
4756        for w in &self.warnings { s += &format!("  {}\n", w.format()); }
4757        s
4758    }
4759}
4760
4761// ── Built-in Shader Snippets ──────────────────────────────────────────────────
4762
4763pub fn glsl_preamble(version: &str, is_es: bool) -> String {
4764    if is_es { format!("#version {} es\nprecision highp float;\nprecision highp int;\n", version) }
4765    else { format!("#version {}\n", version) }
4766}
4767
4768pub fn glsl_common_utils() -> &'static str {
4769    r#"
4770const float PI = 3.14159265358979323846;
4771const float TWO_PI = 6.28318530717958647692;
4772const float HALF_PI = 1.57079632679489661923;
4773const float INV_PI = 0.31830988618379067154;
4774const float E = 2.71828182845904523536;
4775const float GOLDEN_RATIO = 1.61803398874989484820;
4776const float EPSILON = 1e-6;
4777const float INF = 1.0/0.0;
4778
4779float saturate(float v) { return clamp(v, 0.0, 1.0); }
4780vec2 saturate(vec2 v) { return clamp(v, vec2(0.0), vec2(1.0)); }
4781vec3 saturate(vec3 v) { return clamp(v, vec3(0.0), vec3(1.0)); }
4782vec4 saturate(vec4 v) { return clamp(v, vec4(0.0), vec4(1.0)); }
4783
4784float remap(float v, float fromMin, float fromMax, float toMin, float toMax) {
4785    return toMin + (v - fromMin) / (fromMax - fromMin) * (toMax - toMin);
4786}
4787float luminance(vec3 c) { return dot(c, vec3(0.2126, 0.7152, 0.0722)); }
4788vec3 rgb_to_hsv(vec3 c) {
4789    vec4 K = vec4(0.0, -1.0/3.0, 2.0/3.0, -1.0);
4790    vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
4791    vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
4792    float d = q.x - min(q.w, q.y);
4793    float e = 1.0e-10;
4794    return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
4795}
4796float rand(vec2 co) { return fract(sin(dot(co, vec2(12.9898, 78.233))) * 43758.5453); }
4797vec3 hash33(vec3 p) {
4798    p = fract(p * vec3(443.8975, 397.2973, 491.1871));
4799    p += dot(p.zxy, p.yxz + 19.19);
4800    return fract(vec3(p.x * p.y, p.z * p.x, p.y * p.z));
4801}
4802"#
4803}
4804
4805pub fn glsl_pbr_brdf() -> &'static str {
4806    r#"
4807// PBR BRDF functions (GGX/Schlick/Smith)
4808vec3 F_Schlick(vec3 f0, float f90, float u) {
4809    return f0 + (f90 - f0) * pow(1.0 - u, 5.0);
4810}
4811float V_SmithGGXCorrelated(float NdotV, float NdotL, float roughness) {
4812    float a2 = roughness * roughness;
4813    float GGXV = NdotL * sqrt(NdotV * NdotV * (1.0 - a2) + a2);
4814    float GGXL = NdotV * sqrt(NdotL * NdotL * (1.0 - a2) + a2);
4815    return 0.5 / max(GGXV + GGXL, 1e-5);
4816}
4817float D_GGX(float NdotH, float roughness) {
4818    float a2 = roughness * roughness;
4819    float f = (NdotH * a2 - NdotH) * NdotH + 1.0;
4820    return a2 / (PI * f * f);
4821}
4822vec3 eval_pbr(vec3 N, vec3 V, vec3 L, vec3 albedo, float roughness, float metalness) {
4823    vec3 H = normalize(V + L);
4824    float NdotV = max(dot(N, V), 0.0);
4825    float NdotL = max(dot(N, L), 0.0);
4826    float NdotH = max(dot(N, H), 0.0);
4827    float LdotH = max(dot(L, H), 0.0);
4828    vec3 f0 = mix(vec3(0.04), albedo, metalness);
4829    float D = D_GGX(NdotH, roughness);
4830    float V_vis = V_SmithGGXCorrelated(NdotV, NdotL, roughness);
4831    vec3 F = F_Schlick(f0, 1.0, LdotH);
4832    vec3 Fr = D * V_vis * F;
4833    vec3 Fd = (1.0 - F) * (1.0 - metalness) * albedo / PI;
4834    return (Fd + Fr) * NdotL;
4835}
4836"#
4837}
4838
4839pub fn glsl_shadow_functions() -> &'static str {
4840    r#"
4841float shadow_hard(sampler2DShadow shadowMap, vec4 shadowCoord) {
4842    vec3 projCoords = shadowCoord.xyz / shadowCoord.w;
4843    return texture(shadowMap, projCoords);
4844}
4845float shadow_soft_pcf(sampler2DShadow shadowMap, vec4 shadowCoord, int samples) {
4846    vec2 texelSize = 1.0 / textureSize(shadowMap, 0);
4847    vec3 projCoords = shadowCoord.xyz / shadowCoord.w;
4848    float shadow = 0.0;
4849    float total = 0.0;
4850    for(int x = -samples; x <= samples; x++) {
4851        for(int y = -samples; y <= samples; y++) {
4852            vec3 offset = vec3(vec2(x, y) * texelSize, 0.0);
4853            shadow += texture(shadowMap, projCoords + offset);
4854            total += 1.0;
4855        }
4856    }
4857    return shadow / total;
4858}
4859vec2 poisson_disk[16] = vec2[](
4860    vec2(-0.94201624, -0.39906216), vec2(0.94558609, -0.76890725),
4861    vec2(-0.094184101, -0.92938870), vec2(0.34495938, 0.29387760),
4862    vec2(-0.91588581, 0.45771432), vec2(-0.81544232, -0.87912464),
4863    vec2(-0.38277543, 0.27676845), vec2(0.97484398, 0.75648379),
4864    vec2(0.44323325, -0.97511554), vec2(0.53742981, -0.47373420),
4865    vec2(-0.26496911, -0.41893023), vec2(0.79197514, 0.19090188),
4866    vec2(-0.24188840, 0.99706507), vec2(-0.81409955, 0.91437590),
4867    vec2(0.19984126, 0.78641367), vec2(0.14383161, -0.14100790)
4868);
4869float shadow_poisson(sampler2DShadow shadowMap, vec4 shadowCoord, float radius) {
4870    vec3 projCoords = shadowCoord.xyz / shadowCoord.w;
4871    float shadow = 0.0;
4872    for(int i = 0; i < 16; i++) {
4873        vec2 offset = poisson_disk[i] * radius;
4874        shadow += texture(shadowMap, projCoords + vec3(offset, 0.0));
4875    }
4876    return shadow / 16.0;
4877}
4878"#
4879}
4880
4881pub fn glsl_atmosphere() -> &'static str {
4882    r#"
4883// Simple atmospheric scattering approximation
4884vec3 atmosphere(vec3 ray_dir, vec3 sun_dir, vec3 sun_color) {
4885    float sun_dot = max(dot(ray_dir, sun_dir), 0.0);
4886    vec3 sky_color = vec3(0.1, 0.3, 0.7);
4887    vec3 horizon_color = vec3(0.7, 0.5, 0.3);
4888    float horizon_blend = pow(1.0 - abs(ray_dir.y), 4.0);
4889    vec3 sky = mix(sky_color, horizon_color, horizon_blend);
4890    float sun_disc = smoothstep(0.998, 1.0, sun_dot);
4891    float halo = pow(sun_dot, 8.0) * 0.3;
4892    return sky + sun_color * (sun_disc + halo);
4893}
4894vec3 fog(vec3 color, float depth, vec3 fog_color, float fog_start, float fog_end) {
4895    float factor = clamp((depth - fog_start) / (fog_end - fog_start), 0.0, 1.0);
4896    return mix(color, fog_color, factor);
4897}
4898vec3 exponential_fog(vec3 color, float depth, vec3 fog_color, float density) {
4899    float factor = 1.0 - exp(-density * depth);
4900    return mix(color, fog_color, factor);
4901}
4902"#
4903}
4904
4905// ── Shader Constant Buffer Layouts ────────────────────────────────────────────
4906
4907pub fn per_frame_cbuffer_glsl() -> &'static str {
4908    r#"
4909layout(std140, binding = 0) uniform PerFrame {
4910    mat4 view;
4911    mat4 proj;
4912    mat4 view_proj;
4913    mat4 inv_view;
4914    mat4 inv_proj;
4915    vec4 camera_pos;
4916    vec4 camera_dir;
4917    vec4 viewport_size;
4918    float time;
4919    float delta_time;
4920    float near_plane;
4921    float far_plane;
4922    vec4 sun_direction;
4923    vec4 sun_color;
4924    vec4 ambient_color;
4925    vec2 jitter_offset;
4926    uint frame_index;
4927    float exposure;
4928} u_frame;
4929"#
4930}
4931
4932pub fn per_object_cbuffer_glsl() -> &'static str {
4933    r#"
4934layout(std140, binding = 1) uniform PerObject {
4935    mat4 model;
4936    mat4 model_view;
4937    mat4 mvp;
4938    mat4 normal_matrix;
4939    vec4 object_color;
4940    vec4 object_id;
4941    float lod_bias;
4942    uint material_flags;
4943    float opacity;
4944    float pad;
4945} u_object;
4946"#
4947}
4948
4949pub fn per_material_cbuffer_glsl() -> &'static str {
4950    r#"
4951layout(std140, binding = 2) uniform PerMaterial {
4952    vec4 albedo_color;
4953    vec4 emissive_color;
4954    float roughness;
4955    float metalness;
4956    float normal_scale;
4957    float occlusion_strength;
4958    float emissive_intensity;
4959    float opacity;
4960    float alpha_cutoff;
4961    uint flags;
4962    vec4 uv_transform;
4963} u_material;
4964"#
4965}
4966
4967// ── Shader statistics & info ──────────────────────────────────────────────────
4968
4969pub const SHADER_BUILTIN_SNIPPETS: usize = 11;
4970pub const SHADER_GLSL_BUILTIN_FUNCTIONS: usize = 28;
4971pub const SHADER_CBUFFER_BINDINGS: u32 = 3;
4972pub const SHADER_ATMOSPHERE_SAMPLES: u32 = 8;
4973pub const SHADER_SHADOW_PCF_SAMPLES: u32 = 9;
4974pub const SHADER_SHADOW_POISSON_SAMPLES: u32 = 16;
4975pub const GLSL_PRECISION_HIGHP: &str = "highp";
4976pub const GLSL_PRECISION_MEDIUMP: &str = "mediump";
4977pub const GLSL_PRECISION_LOWP: &str = "lowp";
4978
4979pub fn all_glsl_builtin_types() -> &'static [&'static str] {
4980    &[ "void", "bool", "int", "uint", "float", "double",
4981       "bvec2", "bvec3", "bvec4", "ivec2", "ivec3", "ivec4",
4982       "uvec2", "uvec3", "uvec4", "vec2", "vec3", "vec4",
4983       "dvec2", "dvec3", "dvec4",
4984       "mat2", "mat3", "mat4", "mat2x3", "mat2x4", "mat3x2",
4985       "mat3x4", "mat4x2", "mat4x3",
4986       "sampler2D", "sampler3D", "samplerCube", "sampler2DArray",
4987       "sampler2DShadow", "samplerCubeShadow",
4988       "isampler2D", "usampler2D", "image2D", "imageCube" ]
4989}
4990
4991pub fn all_glsl_keywords() -> &'static [&'static str] {
4992    &[ "if", "else", "for", "while", "do", "switch", "case", "default",
4993       "break", "continue", "return", "discard",
4994       "void", "struct", "precision", "highp", "mediump", "lowp",
4995       "in", "out", "inout", "uniform", "const", "layout",
4996       "attribute", "varying", "flat", "smooth", "centroid",
4997       "noperspective", "invariant", "coherent", "volatile",
4998       "restrict", "readonly", "writeonly" ]
4999}
5000
5001
5002// ── Render Feature Flags ──────────────────────────────────────────────────────
5003
5004#[derive(Clone, Debug)]
5005pub struct RenderFeatureFlags {
5006    pub pbr: bool,
5007    pub shadows: bool,
5008    pub ssao: bool,
5009    pub ssr: bool,
5010    pub bloom: bool,
5011    pub fxaa: bool,
5012    pub taa: bool,
5013    pub dof: bool,
5014    pub motion_blur: bool,
5015    pub volumetric_fog: bool,
5016    pub lens_flare: bool,
5017    pub chromatic_aberration: bool,
5018    pub vignette: bool,
5019    pub color_grading: bool,
5020    pub debug_mode: bool,
5021}
5022
5023impl RenderFeatureFlags {
5024    pub fn all_off() -> Self { Self { pbr: false, shadows: false, ssao: false, ssr: false, bloom: false, fxaa: false, taa: false, dof: false, motion_blur: false, volumetric_fog: false, lens_flare: false, chromatic_aberration: false, vignette: false, color_grading: false, debug_mode: false } }
5025    pub fn default_high_quality() -> Self { Self { pbr: true, shadows: true, ssao: true, ssr: false, bloom: true, fxaa: true, taa: true, dof: false, motion_blur: false, volumetric_fog: false, lens_flare: false, chromatic_aberration: false, vignette: true, color_grading: true, debug_mode: false } }
5026    pub fn default_medium_quality() -> Self { Self { pbr: true, shadows: true, ssao: false, ssr: false, bloom: true, fxaa: true, taa: false, ..Self::all_off() } }
5027    pub fn default_low_quality() -> Self { Self { fxaa: true, ..Self::all_off() } }
5028    pub fn active_count(&self) -> usize {
5029        [self.pbr, self.shadows, self.ssao, self.ssr, self.bloom, self.fxaa, self.taa, self.dof, self.motion_blur, self.volumetric_fog, self.lens_flare, self.chromatic_aberration, self.vignette, self.color_grading].iter().filter(|&&b| b).count()
5030    }
5031    pub fn generate_defines(&self) -> Vec<(String, String)> {
5032        let mut defines = Vec::new();
5033        if self.pbr { defines.push(("USE_PBR".into(), "1".into())); }
5034        if self.shadows { defines.push(("USE_SHADOWS".into(), "1".into())); }
5035        if self.ssao { defines.push(("USE_SSAO".into(), "1".into())); }
5036        if self.bloom { defines.push(("USE_BLOOM".into(), "1".into())); }
5037        if self.fxaa { defines.push(("USE_FXAA".into(), "1".into())); }
5038        if self.taa { defines.push(("USE_TAA".into(), "1".into())); }
5039        defines
5040    }
5041}
5042
5043impl Default for RenderFeatureFlags {
5044    fn default() -> Self { Self::default_high_quality() }
5045}
5046
5047// ── Shader compilation context ────────────────────────────────────────────────
5048
5049#[derive(Clone, Debug)]
5050pub struct ShaderCompilationContext {
5051    pub source_path: String,
5052    pub stage: ShaderStage,
5053    pub target: CodeGenTarget,
5054    pub defines: HashMap<String, String>,
5055    pub include_paths: Vec<String>,
5056    pub optimization: OptimizationLevel,
5057    pub version: String,
5058    pub entry_point: String,
5059    pub features: RenderFeatureFlags,
5060}
5061
5062impl ShaderCompilationContext {
5063    pub fn new(path: impl Into<String>, stage: ShaderStage, target: CodeGenTarget) -> Self {
5064        Self { source_path: path.into(), stage, target, defines: HashMap::new(), include_paths: Vec::new(), optimization: OptimizationLevel::Medium, version: "450".into(), entry_point: "main".into(), features: RenderFeatureFlags::default() }
5065    }
5066    pub fn add_define(&mut self, k: impl Into<String>, v: impl Into<String>) { self.defines.insert(k.into(), v.into()); }
5067    pub fn apply_features(&mut self) {
5068        for (k, v) in self.features.generate_defines() { self.defines.insert(k, v); }
5069    }
5070    pub fn is_vertex(&self) -> bool { self.stage == ShaderStage::Vertex }
5071    pub fn is_fragment(&self) -> bool { self.stage == ShaderStage::Fragment }
5072    pub fn is_compute(&self) -> bool { self.stage == ShaderStage::Compute }
5073}
5074
5075// ── Shader bundle ─────────────────────────────────────────────────────────────
5076
5077#[derive(Clone, Debug)]
5078pub struct ShaderBundle {
5079    pub id: u32,
5080    pub name: String,
5081    pub programs: Vec<ShaderProgram>,
5082    pub materials: Vec<Material>,
5083    pub templates: Vec<ShaderTemplate>,
5084    pub version: u32,
5085    pub description: String,
5086    pub author: String,
5087    pub created_at: u64,
5088}
5089
5090impl ShaderBundle {
5091    pub fn new(id: u32, name: impl Into<String>) -> Self {
5092        Self { id, name: name.into(), programs: Vec::new(), materials: Vec::new(), templates: Vec::new(), version: 1, description: String::new(), author: String::new(), created_at: 0 }
5093    }
5094    pub fn add_program(&mut self, p: ShaderProgram) { self.programs.push(p); }
5095    pub fn add_material(&mut self, m: Material) { self.materials.push(m); }
5096    pub fn add_template(&mut self, t: ShaderTemplate) { self.templates.push(t); }
5097    pub fn program_count(&self) -> usize { self.programs.len() }
5098    pub fn material_count(&self) -> usize { self.materials.len() }
5099    pub fn find_program(&self, name: &str) -> Option<&ShaderProgram> { self.programs.iter().find(|p| p.name == name) }
5100    pub fn is_empty(&self) -> bool { self.programs.is_empty() && self.materials.is_empty() }
5101}
5102
5103#[derive(Clone, Debug)]
5104pub struct ShaderBundleRegistry {
5105    pub bundles: HashMap<u32, ShaderBundle>,
5106    pub next_id: u32,
5107}
5108
5109impl ShaderBundleRegistry {
5110    pub fn new() -> Self { Self { bundles: HashMap::new(), next_id: 1 } }
5111    pub fn register(&mut self, mut b: ShaderBundle) -> u32 {
5112        let id = self.next_id; self.next_id += 1;
5113        b.id = id;
5114        self.bundles.insert(id, b);
5115        id
5116    }
5117    pub fn get(&self, id: u32) -> Option<&ShaderBundle> { self.bundles.get(&id) }
5118    pub fn find_by_name(&self, name: &str) -> Option<&ShaderBundle> { self.bundles.values().find(|b| b.name == name) }
5119    pub fn total_programs(&self) -> usize { self.bundles.values().map(|b| b.program_count()).sum() }
5120    pub fn count(&self) -> usize { self.bundles.len() }
5121}
5122
5123impl Default for ShaderBundleRegistry {
5124    fn default() -> Self { Self::new() }
5125}
5126
5127// ── Shader Workspace ──────────────────────────────────────────────────────────
5128
5129#[derive(Clone, Debug)]
5130pub struct ShaderWorkspace {
5131    pub registry: ShaderProgramRegistry,
5132    pub cache: ShaderCache,
5133    pub library: ShaderLibrary,
5134    pub material_lib: MaterialLibrary,
5135    pub texture_reg: TextureRegistry,
5136    pub buffer_reg: BufferRegistry,
5137    pub pipeline: RenderPipeline,
5138    pub post_process: PostProcessStack,
5139    pub template_lib: ShaderTemplateLibrary,
5140    pub linter: ShaderLinter,
5141    pub formatter: ShaderFormatter,
5142    pub debugger: ShaderDebugger,
5143    pub profiler: ShaderProfiler,
5144    pub hot_reload: HotReloadManager,
5145    pub editor_state: ShaderEditorState,
5146    pub autocomplete: AutoCompleteProvider,
5147    pub linker: ShaderLinker,
5148    pub bundle_reg: ShaderBundleRegistry,
5149    pub feature_flags: RenderFeatureFlags,
5150    pub memory_budget: GpuMemoryBudget,
5151    pub render_stats: RenderStatistics,
5152}
5153
5154impl ShaderWorkspace {
5155    pub fn new() -> Self {
5156        Self {
5157            registry: ShaderProgramRegistry::new(),
5158            cache: ShaderCache::new(SHADER_CACHE_SIZE),
5159            library: ShaderLibrary::build_standard(),
5160            material_lib: MaterialLibrary::new(),
5161            texture_reg: TextureRegistry::new(),
5162            buffer_reg: BufferRegistry::new(),
5163            pipeline: RenderPipeline::new(),
5164            post_process: PostProcessStack::new(),
5165            template_lib: ShaderTemplateLibrary::new(),
5166            linter: ShaderLinter::new(),
5167            formatter: ShaderFormatter::new(),
5168            debugger: ShaderDebugger::build_defaults(),
5169            profiler: ShaderProfiler::new(),
5170            hot_reload: HotReloadManager::new(),
5171            editor_state: ShaderEditorState::new(),
5172            autocomplete: AutoCompleteProvider::new(),
5173            linker: ShaderLinker::new(),
5174            bundle_reg: ShaderBundleRegistry::new(),
5175            feature_flags: RenderFeatureFlags::default(),
5176            memory_budget: GpuMemoryBudget::new(GPU_MEMORY_BUDGET_DEFAULT_MB * 1024 * 1024),
5177            render_stats: RenderStatistics::new(),
5178        }
5179    }
5180    pub fn program_count(&self) -> usize { self.registry.count() }
5181    pub fn material_count(&self) -> usize { self.material_lib.count() }
5182    pub fn texture_count(&self) -> usize { self.texture_reg.count() }
5183    pub fn buffer_count(&self) -> usize { self.buffer_reg.count() }
5184    pub fn begin_frame(&mut self) { self.render_stats.begin_frame(); self.profiler.begin_frame(); }
5185    pub fn has_program(&self, name: &str) -> bool { self.registry.find_by_name(name).is_some() }
5186    pub fn tick(&mut self, dt: f32) { let _ = self.hot_reload.tick(dt); }
5187}
5188
5189impl Default for ShaderWorkspace {
5190    fn default() -> Self { Self::new() }
5191}
5192
5193// ── Final constants ───────────────────────────────────────────────────────────
5194
5195pub const SHADER_BUNDLE_MAX: usize = 64;
5196pub const SHADER_WORKSPACE_MAX_PROGRAMS: usize = SHADER_MAX_PROGRAMS;
5197pub const RENDER_FEATURE_COUNT: usize = 15;
5198pub const SHADER_VERSION_DEFAULT: &str = "450";
5199pub const SHADER_ES_VERSION_DEFAULT: &str = "300 es";
5200pub const SHADER_ENTRY_POINT_DEFAULT: &str = "main";
5201pub const GLSL_BUILTIN_TYPE_COUNT: usize = 40;
5202pub const GLSL_KEYWORD_COUNT: usize = 31;
5203pub const GLSL_MAX_FRAGMENT_OUTPUTS: u32 = 8;
5204pub const GLSL_MAX_VERTEX_ATTRIBS: u32 = 32;
5205
5206pub fn build_standard_shader_workspace() -> ShaderWorkspace { ShaderWorkspace::new() }
5207pub fn shader_workspace_summary(ws: &ShaderWorkspace) -> String {
5208    format!("ShaderWorkspace: {} programs, {} materials, {} textures, {} buffers, {} render passes", ws.program_count(), ws.material_count(), ws.texture_count(), ws.buffer_count(), ws.pipeline.pass_count())
5209}
5210pub fn shader_system_version() -> &'static str { "1.0.0" }
5211pub fn is_shader_system_ready() -> bool { true }
5212
5213
5214// ── Render Graph ──────────────────────────────────────────────────────────────
5215
5216#[derive(Clone, Debug)]
5217pub struct RenderGraphNode {
5218    pub id: u32,
5219    pub name: String,
5220    pub node_type: RenderGraphNodeType,
5221    pub inputs: Vec<RenderGraphResource>,
5222    pub outputs: Vec<RenderGraphResource>,
5223    pub shader_id: Option<u32>,
5224    pub enabled: bool,
5225    pub order: u32,
5226    pub async_compute: bool,
5227}
5228
5229#[derive(Clone, Debug, PartialEq)]
5230pub enum RenderGraphNodeType { Pass, Blit, Compute, Present, Upload, Barrier, Custom(String) }
5231
5232#[derive(Clone, Debug)]
5233pub struct RenderGraphResource {
5234    pub name: String,
5235    pub resource_type: RenderGraphResourceType,
5236    pub format: AttachmentFormat,
5237    pub width: u32,
5238    pub height: u32,
5239    pub mips: u32,
5240    pub transient: bool,
5241}
5242
5243#[derive(Clone, Debug, PartialEq)]
5244pub enum RenderGraphResourceType { Texture, Buffer, RenderTarget, DepthStencil }
5245
5246impl RenderGraphNode {
5247    pub fn new(id: u32, name: impl Into<String>, node_type: RenderGraphNodeType) -> Self {
5248        Self { id, name: name.into(), node_type, inputs: Vec::new(), outputs: Vec::new(), shader_id: None, enabled: true, order: 0, async_compute: false }
5249    }
5250    pub fn add_input(&mut self, res: RenderGraphResource) { self.inputs.push(res); }
5251    pub fn add_output(&mut self, res: RenderGraphResource) { self.outputs.push(res); }
5252    pub fn is_pass(&self) -> bool { self.node_type == RenderGraphNodeType::Pass }
5253    pub fn is_compute(&self) -> bool { self.node_type == RenderGraphNodeType::Compute }
5254    pub fn input_count(&self) -> usize { self.inputs.len() }
5255    pub fn output_count(&self) -> usize { self.outputs.len() }
5256    pub fn with_shader(mut self, id: u32) -> Self { self.shader_id = Some(id); self }
5257}
5258
5259#[derive(Clone, Debug)]
5260pub struct RenderGraph {
5261    pub nodes: Vec<RenderGraphNode>,
5262    pub edges: Vec<(u32, u32)>,
5263    pub name: String,
5264    pub next_id: u32,
5265}
5266
5267impl RenderGraph {
5268    pub fn new(name: impl Into<String>) -> Self { Self { nodes: Vec::new(), edges: Vec::new(), name: name.into(), next_id: 1 } }
5269    pub fn add_node(&mut self, node_type: RenderGraphNodeType, name: impl Into<String>) -> u32 {
5270        let id = self.next_id; self.next_id += 1;
5271        self.nodes.push(RenderGraphNode::new(id, name, node_type));
5272        id
5273    }
5274    pub fn connect(&mut self, from: u32, to: u32) { self.edges.push((from, to)); }
5275    pub fn sorted_nodes(&self) -> Vec<&RenderGraphNode> {
5276        let mut sorted: Vec<_> = self.nodes.iter().filter(|n| n.enabled).collect();
5277        sorted.sort_by_key(|n| n.order);
5278        sorted
5279    }
5280    pub fn node_count(&self) -> usize { self.nodes.len() }
5281    pub fn find_node(&self, id: u32) -> Option<&RenderGraphNode> { self.nodes.iter().find(|n| n.id == id) }
5282    pub fn remove_node(&mut self, id: u32) { self.nodes.retain(|n| n.id != id); self.edges.retain(|(a, b)| *a != id && *b != id); }
5283}
5284
5285impl Default for RenderGraph {
5286    fn default() -> Self { Self::new("default") }
5287}
5288
5289// ── Shader Compiler Main ──────────────────────────────────────────────────────
5290
5291#[derive(Clone, Debug)]
5292pub struct ShaderCompilerMain {
5293    pub workspace: ShaderWorkspace,
5294    pub render_graph: RenderGraph,
5295    pub compile_stats: CompileStats,
5296}
5297
5298#[derive(Clone, Debug, Default)]
5299pub struct CompileStats {
5300    pub total_compiled: u32,
5301    pub total_failed: u32,
5302    pub total_cached: u32,
5303    pub total_reloaded: u32,
5304    pub avg_compile_ms: f32,
5305    pub peak_compile_ms: f32,
5306}
5307
5308impl CompileStats {
5309    pub fn new() -> Self { Self::default() }
5310    pub fn record_compile(&mut self, ms: f32, success: bool, from_cache: bool) {
5311        if from_cache { self.total_cached += 1; return; }
5312        if success { self.total_compiled += 1; } else { self.total_failed += 1; }
5313        self.peak_compile_ms = self.peak_compile_ms.max(ms);
5314        let total = self.total_compiled + self.total_failed;
5315        self.avg_compile_ms = (self.avg_compile_ms * (total - 1) as f32 + ms) / total as f32;
5316    }
5317    pub fn success_rate(&self) -> f32 {
5318        let total = self.total_compiled + self.total_failed;
5319        if total == 0 { 1.0 } else { self.total_compiled as f32 / total as f32 }
5320    }
5321    pub fn total_attempts(&self) -> u32 { self.total_compiled + self.total_failed + self.total_cached }
5322}
5323
5324impl ShaderCompilerMain {
5325    pub fn new() -> Self { Self { workspace: ShaderWorkspace::new(), render_graph: RenderGraph::new("main"), compile_stats: CompileStats::new() } }
5326    pub fn add_program(&mut self, prog: ShaderProgram) -> u32 { self.workspace.registry.add(prog) }
5327    pub fn get_program(&self, id: u32) -> Option<&ShaderProgram> { self.workspace.registry.get(id) }
5328    pub fn add_material(&mut self, mat: Material) -> u32 { self.workspace.material_lib.add(mat) }
5329    pub fn add_texture(&mut self, tex: TextureDescriptor) -> u32 { self.workspace.texture_reg.register(tex) }
5330    pub fn begin_frame(&mut self) { self.workspace.begin_frame(); }
5331    pub fn tick(&mut self, dt: f32) { self.workspace.tick(dt); }
5332    pub fn program_count(&self) -> usize { self.workspace.program_count() }
5333    pub fn compilation_summary(&self) -> String {
5334        format!("Compiled: {}, Failed: {}, Cached: {}, Success: {:.1}%", self.compile_stats.total_compiled, self.compile_stats.total_failed, self.compile_stats.total_cached, self.compile_stats.success_rate() * 100.0)
5335    }
5336}
5337
5338impl Default for ShaderCompilerMain {
5339    fn default() -> Self { Self::new() }
5340}
5341
5342// ── Color Grading ─────────────────────────────────────────────────────────────
5343
5344#[derive(Clone, Debug)]
5345pub struct ColorGradingSettings {
5346    pub exposure: f32,
5347    pub contrast: f32,
5348    pub saturation: f32,
5349    pub brightness: f32,
5350    pub hue_shift: f32,
5351    pub lift: [f32; 3],
5352    pub gamma: [f32; 3],
5353    pub gain: [f32; 3],
5354    pub white_balance_temp: f32,
5355    pub white_balance_tint: f32,
5356    pub shadows_color: [f32; 3],
5357    pub midtones_color: [f32; 3],
5358    pub highlights_color: [f32; 3],
5359    pub tonemapping: ToneMappingMode,
5360}
5361
5362#[derive(Clone, Debug, PartialEq)]
5363pub enum ToneMappingMode { None, Reinhard, Aces, Filmic, Uncharted2, Custom }
5364
5365impl Default for ColorGradingSettings {
5366    fn default() -> Self {
5367        Self { exposure: 0.0, contrast: 1.0, saturation: 1.0, brightness: 0.0, hue_shift: 0.0, lift: [0.0; 3], gamma: [1.0; 3], gain: [1.0; 3], white_balance_temp: 6500.0, white_balance_tint: 0.0, shadows_color: [0.0; 3], midtones_color: [0.0; 3], highlights_color: [0.0; 3], tonemapping: ToneMappingMode::Aces }
5368    }
5369}
5370
5371impl ColorGradingSettings {
5372    pub fn new() -> Self { Self::default() }
5373    pub fn neutral() -> Self { Self::default() }
5374    pub fn warm() -> Self { Self { white_balance_temp: 7500.0, saturation: 1.1, ..Self::default() } }
5375    pub fn cool() -> Self { Self { white_balance_temp: 5500.0, saturation: 0.9, ..Self::default() } }
5376    pub fn cinematic() -> Self { Self { contrast: 1.15, saturation: 0.9, tonemapping: ToneMappingMode::Filmic, ..Self::default() } }
5377    pub fn generate_glsl(&self) -> String {
5378        format!("// ColorGrading: exposure={:.2}, contrast={:.2}, saturation={:.2}, tonemapping={:?}", self.exposure, self.contrast, self.saturation, self.tonemapping)
5379    }
5380    pub fn is_neutral(&self) -> bool { self.exposure == 0.0 && self.contrast == 1.0 && self.saturation == 1.0 }
5381}
5382
5383// ── Screen Space Ambient Occlusion Config ─────────────────────────────────────
5384
5385#[derive(Clone, Debug)]
5386pub struct SsaoConfig {
5387    pub kernel_size: u32,
5388    pub radius: f32,
5389    pub bias: f32,
5390    pub power: f32,
5391    pub blur_passes: u32,
5392    pub enabled: bool,
5393    pub quality: SsaoQuality,
5394}
5395
5396#[derive(Clone, Debug, PartialEq)]
5397pub enum SsaoQuality { Low, Medium, High, Ultra }
5398
5399impl SsaoConfig {
5400    pub fn new() -> Self { Self { kernel_size: 32, radius: 0.5, bias: 0.025, power: 2.0, blur_passes: 2, enabled: true, quality: SsaoQuality::Medium } }
5401    pub fn low() -> Self { Self { kernel_size: 8, radius: 0.3, blur_passes: 1, quality: SsaoQuality::Low, ..Self::new() } }
5402    pub fn high() -> Self { Self { kernel_size: 64, radius: 0.8, blur_passes: 3, quality: SsaoQuality::High, ..Self::new() } }
5403    pub fn ultra() -> Self { Self { kernel_size: 128, radius: 1.0, blur_passes: 4, quality: SsaoQuality::Ultra, ..Self::new() } }
5404}
5405
5406impl Default for SsaoConfig {
5407    fn default() -> Self { Self::new() }
5408}
5409
5410// ── Depth of Field ────────────────────────────────────────────────────────────
5411
5412#[derive(Clone, Debug)]
5413pub struct DepthOfFieldConfig {
5414    pub enabled: bool,
5415    pub focus_distance: f32,
5416    pub focus_range: f32,
5417    pub far_blur_range: f32,
5418    pub near_blur_range: f32,
5419    pub bokeh_radius: f32,
5420    pub bokeh_blade_count: u32,
5421    pub bokeh_rotation: f32,
5422    pub max_coc_radius: f32,
5423}
5424
5425impl DepthOfFieldConfig {
5426    pub fn new() -> Self { Self { enabled: false, focus_distance: 10.0, focus_range: 5.0, far_blur_range: 20.0, near_blur_range: 2.0, bokeh_radius: 8.0, bokeh_blade_count: 6, bokeh_rotation: 0.0, max_coc_radius: 20.0 } }
5427    pub fn portrait() -> Self { Self { enabled: true, focus_distance: 2.0, focus_range: 0.5, bokeh_radius: 12.0, ..Self::new() } }
5428    pub fn cinematic() -> Self { Self { enabled: true, focus_distance: 5.0, focus_range: 2.0, bokeh_radius: 10.0, bokeh_blade_count: 8, ..Self::new() } }
5429}
5430
5431impl Default for DepthOfFieldConfig {
5432    fn default() -> Self { Self::new() }
5433}
5434
5435// ── Bloom Config ──────────────────────────────────────────────────────────────
5436
5437#[derive(Clone, Debug)]
5438pub struct BloomConfig {
5439    pub enabled: bool,
5440    pub threshold: f32,
5441    pub intensity: f32,
5442    pub scatter: f32,
5443    pub clamp: f32,
5444    pub downsample_passes: u32,
5445    pub mode: BloomMode,
5446    pub tint: [f32; 3],
5447}
5448
5449#[derive(Clone, Debug, PartialEq)]
5450pub enum BloomMode { Classic, Kawase, Dual, Convolution }
5451
5452impl BloomConfig {
5453    pub fn new() -> Self { Self { enabled: true, threshold: 0.9, intensity: 0.5, scatter: 0.7, clamp: 65472.0, downsample_passes: 5, mode: BloomMode::Dual, tint: [1.0; 3] } }
5454    pub fn subtle() -> Self { Self { intensity: 0.2, ..Self::new() } }
5455    pub fn intense() -> Self { Self { intensity: 1.5, threshold: 0.7, ..Self::new() } }
5456    pub fn disabled() -> Self { Self { enabled: false, ..Self::new() } }
5457}
5458
5459impl Default for BloomConfig {
5460    fn default() -> Self { Self::new() }
5461}
5462
5463// ── Final Shader System Constants ─────────────────────────────────────────────
5464
5465pub const RENDER_GRAPH_MAX_NODES: usize = 64;
5466pub const RENDER_GRAPH_MAX_EDGES: usize = 256;
5467pub const COLOR_GRADING_LUT_SIZE: u32 = 32;
5468pub const SSAO_KERNEL_SIZE_MAX: u32 = 256;
5469pub const DOF_MAX_COC_RADIUS: f32 = 32.0;
5470pub const BLOOM_MAX_DOWNSAMPLE_PASSES: u32 = 8;
5471pub const SHADER_COMPILER_MAX_WORKSPACES: usize = 8;
5472pub const GPU_BUDGET_WARNING_PERCENT: f32 = 80.0;
5473pub const GPU_BUDGET_CRITICAL_PERCENT: f32 = 95.0;
5474pub const SHADER_PERMUTATION_HASH_SEED: u64 = 14695981039346656037;
5475
5476pub fn tonemap_name(mode: &ToneMappingMode) -> &'static str {
5477    match mode { ToneMappingMode::None => "None", ToneMappingMode::Reinhard => "Reinhard", ToneMappingMode::Aces => "ACES", ToneMappingMode::Filmic => "Filmic", ToneMappingMode::Uncharted2 => "Uncharted 2", ToneMappingMode::Custom => "Custom" }
5478}
5479pub fn bloom_mode_name(mode: &BloomMode) -> &'static str {
5480    match mode { BloomMode::Classic => "Classic", BloomMode::Kawase => "Kawase", BloomMode::Dual => "Dual Kawase", BloomMode::Convolution => "Convolution" }
5481}
5482pub fn ssao_quality_name(q: &SsaoQuality) -> &'static str {
5483    match q { SsaoQuality::Low => "Low", SsaoQuality::Medium => "Medium", SsaoQuality::High => "High", SsaoQuality::Ultra => "Ultra" }
5484}
5485pub fn shader_compiler_build_date() -> &'static str { "2026-03-29" }
5486pub fn shader_compiler_complete_info() -> String {
5487    format!("ShaderCompilerEditor v{} — {} modules, render graph, materials, post-process, color grading", shader_system_version(), shader_module_count())
5488}
5489
5490
5491// ── Screen Space Reflections ──────────────────────────────────────────────────
5492
5493#[derive(Clone, Debug)]
5494pub struct SsrConfig {
5495    pub enabled: bool,
5496    pub max_steps: u32,
5497    pub max_distance: f32,
5498    pub thickness: f32,
5499    pub stride: u32,
5500    pub jitter: f32,
5501    pub fade_start: f32,
5502    pub fade_end: f32,
5503    pub reflection_blend: f32,
5504    pub quality: SsrQuality,
5505}
5506
5507#[derive(Clone, Debug, PartialEq)]
5508pub enum SsrQuality { Low, Medium, High }
5509
5510impl SsrConfig {
5511    pub fn new() -> Self { Self { enabled: false, max_steps: 32, max_distance: 100.0, thickness: 0.5, stride: 2, jitter: 0.5, fade_start: 0.7, fade_end: 1.0, reflection_blend: 0.5, quality: SsrQuality::Medium } }
5512    pub fn high() -> Self { Self { enabled: true, max_steps: 64, stride: 1, quality: SsrQuality::High, ..Self::new() } }
5513    pub fn low() -> Self { Self { enabled: true, max_steps: 16, stride: 4, quality: SsrQuality::Low, ..Self::new() } }
5514}
5515
5516impl Default for SsrConfig {
5517    fn default() -> Self { Self::new() }
5518}
5519
5520// ── Temporal Anti-Aliasing ────────────────────────────────────────────────────
5521
5522#[derive(Clone, Debug)]
5523pub struct TaaConfig {
5524    pub enabled: bool,
5525    pub blend_factor: f32,
5526    pub history_blend: f32,
5527    pub sharpness: f32,
5528    pub jitter_scale: f32,
5529    pub motion_rejection: f32,
5530    pub clip_aabb: bool,
5531    pub variance_clip_gamma: f32,
5532    pub sample_count: u32,
5533}
5534
5535impl TaaConfig {
5536    pub fn new() -> Self { Self { enabled: true, blend_factor: 0.1, history_blend: 0.9, sharpness: 0.5, jitter_scale: 1.0, motion_rejection: 0.3, clip_aabb: true, variance_clip_gamma: 1.0, sample_count: 8 } }
5537    pub fn aggressive() -> Self { Self { blend_factor: 0.05, motion_rejection: 0.5, ..Self::new() } }
5538    pub fn gentle() -> Self { Self { blend_factor: 0.2, motion_rejection: 0.1, ..Self::new() } }
5539}
5540
5541impl Default for TaaConfig {
5542    fn default() -> Self { Self::new() }
5543}
5544
5545// ── Volumetric Fog ────────────────────────────────────────────────────────────
5546
5547#[derive(Clone, Debug)]
5548pub struct VolumetricFogConfig {
5549    pub enabled: bool,
5550    pub density: f32,
5551    pub scattering: f32,
5552    pub absorption: f32,
5553    pub anisotropy: f32,
5554    pub height_falloff: f32,
5555    pub base_height: f32,
5556    pub fog_color: [f32; 3],
5557    pub ambient_fog_color: [f32; 3],
5558    pub light_intensity: f32,
5559    pub num_slices: u32,
5560    pub temporal_reprojection: bool,
5561}
5562
5563impl VolumetricFogConfig {
5564    pub fn new() -> Self { Self { enabled: false, density: 0.1, scattering: 0.5, absorption: 0.1, anisotropy: 0.5, height_falloff: 0.1, base_height: 0.0, fog_color: [1.0, 0.95, 0.9], ambient_fog_color: [0.5, 0.6, 0.8], light_intensity: 1.0, num_slices: 128, temporal_reprojection: true } }
5565    pub fn light_fog() -> Self { Self { enabled: true, density: 0.02, ..Self::new() } }
5566    pub fn heavy_fog() -> Self { Self { enabled: true, density: 0.3, ..Self::new() } }
5567    pub fn night_fog() -> Self { Self { enabled: true, density: 0.15, fog_color: [0.3, 0.35, 0.5], ambient_fog_color: [0.1, 0.1, 0.2], ..Self::new() } }
5568}
5569
5570impl Default for VolumetricFogConfig {
5571    fn default() -> Self { Self::new() }
5572}
5573
5574// ── Lens Flare ────────────────────────────────────────────────────────────────
5575
5576#[derive(Clone, Debug)]
5577pub struct LensFlareConfig {
5578    pub enabled: bool,
5579    pub intensity: f32,
5580    pub threshold: f32,
5581    pub ghosts: u32,
5582    pub ghost_dispersal: f32,
5583    pub halo_width: f32,
5584    pub chromatic_distortion: f32,
5585    pub starburst_intensity: f32,
5586    pub starburst_count: u32,
5587}
5588
5589impl LensFlareConfig {
5590    pub fn new() -> Self { Self { enabled: false, intensity: 0.5, threshold: 0.8, ghosts: 4, ghost_dispersal: 0.4, halo_width: 0.4, chromatic_distortion: 5.0, starburst_intensity: 0.5, starburst_count: 8 } }
5591    pub fn subtle() -> Self { Self { enabled: true, intensity: 0.2, ..Self::new() } }
5592    pub fn dramatic() -> Self { Self { enabled: true, intensity: 1.5, ghosts: 8, ..Self::new() } }
5593}
5594
5595impl Default for LensFlareConfig {
5596    fn default() -> Self { Self::new() }
5597}
5598
5599// ── Full Post Process Configuration ──────────────────────────────────────────
5600
5601#[derive(Clone, Debug)]
5602pub struct FullPostProcessConfig {
5603    pub bloom: BloomConfig,
5604    pub color_grading: ColorGradingSettings,
5605    pub ssao: SsaoConfig,
5606    pub ssr: SsrConfig,
5607    pub taa: TaaConfig,
5608    pub dof: DepthOfFieldConfig,
5609    pub volumetric_fog: VolumetricFogConfig,
5610    pub lens_flare: LensFlareConfig,
5611    pub enabled: bool,
5612}
5613
5614impl FullPostProcessConfig {
5615    pub fn new() -> Self {
5616        Self { bloom: BloomConfig::new(), color_grading: ColorGradingSettings::new(), ssao: SsaoConfig::new(), ssr: SsrConfig::new(), taa: TaaConfig::new(), dof: DepthOfFieldConfig::new(), volumetric_fog: VolumetricFogConfig::new(), lens_flare: LensFlareConfig::new(), enabled: true }
5617    }
5618    pub fn high_quality() -> Self {
5619        Self { bloom: BloomConfig::new(), ssao: SsaoConfig::high(), taa: TaaConfig::new(), ..Self::new() }
5620    }
5621    pub fn low_quality() -> Self {
5622        Self { bloom: BloomConfig::disabled(), ssao: SsaoConfig::low(), taa: TaaConfig::new(), ..Self::new() }
5623    }
5624    pub fn active_effect_count(&self) -> usize {
5625        [self.bloom.enabled, self.ssao.enabled, self.ssr.enabled, self.taa.enabled, self.dof.enabled, self.volumetric_fog.enabled, self.lens_flare.enabled].iter().filter(|&&b| b).count()
5626    }
5627}
5628
5629impl Default for FullPostProcessConfig {
5630    fn default() -> Self { Self::new() }
5631}
5632
5633// ── Rendering Quality Preset ──────────────────────────────────────────────────
5634
5635#[derive(Clone, Debug)]
5636pub struct RenderQualityPreset {
5637    pub name: String,
5638    pub features: RenderFeatureFlags,
5639    pub post_process: FullPostProcessConfig,
5640    pub shadow_map_resolution: u32,
5641    pub shadow_cascades: u32,
5642    pub max_lights: u32,
5643    pub reflection_quality: u32,
5644    pub target_fps: u32,
5645    pub render_scale: f32,
5646}
5647
5648impl RenderQualityPreset {
5649    pub fn low() -> Self {
5650        Self { name: "Low".into(), features: RenderFeatureFlags::default_low_quality(), post_process: FullPostProcessConfig::low_quality(), shadow_map_resolution: 1024, shadow_cascades: 2, max_lights: 16, reflection_quality: 0, target_fps: 60, render_scale: 0.75 }
5651    }
5652    pub fn medium() -> Self {
5653        Self { name: "Medium".into(), features: RenderFeatureFlags::default_medium_quality(), post_process: FullPostProcessConfig::new(), shadow_map_resolution: 2048, shadow_cascades: 3, max_lights: 64, reflection_quality: 1, target_fps: 60, render_scale: 1.0 }
5654    }
5655    pub fn high() -> Self {
5656        Self { name: "High".into(), features: RenderFeatureFlags::default_high_quality(), post_process: FullPostProcessConfig::high_quality(), shadow_map_resolution: 4096, shadow_cascades: 4, max_lights: 256, reflection_quality: 2, target_fps: 60, render_scale: 1.0 }
5657    }
5658    pub fn ultra() -> Self {
5659        let mut h = Self::high(); h.name = "Ultra".into(); h.shadow_map_resolution = 8192; h.render_scale = 1.25; h.max_lights = 1024; h
5660    }
5661    pub fn is_mobile_friendly(&self) -> bool { self.shadow_map_resolution <= 1024 && !self.features.ssao && !self.features.ssr }
5662}
5663
5664// ── All rendering presets ─────────────────────────────────────────────────────
5665
5666pub fn build_quality_presets() -> Vec<RenderQualityPreset> {
5667    vec![RenderQualityPreset::low(), RenderQualityPreset::medium(), RenderQualityPreset::high(), RenderQualityPreset::ultra()]
5668}
5669
5670// ── Final constants ───────────────────────────────────────────────────────────
5671
5672pub const SSR_MAX_STEPS: u32 = 128;
5673pub const TAA_HISTORY_FRAMES: u32 = 8;
5674pub const VOLUMETRIC_FOG_MAX_SLICES: u32 = 256;
5675pub const LENS_FLARE_MAX_GHOSTS: u32 = 16;
5676pub const POST_PROCESS_FULL_EFFECT_COUNT: usize = 8;
5677pub const RENDER_QUALITY_PRESET_COUNT: usize = 4;
5678pub const SHADOW_MAP_MAX_RESOLUTION: u32 = 16384;
5679pub const SHADOW_CASCADE_MAX: u32 = 8;
5680pub const RENDER_MAX_LIGHTS: u32 = 4096;
5681pub const RENDER_SCALE_MIN: f32 = 0.25;
5682pub const RENDER_SCALE_MAX: f32 = 2.0;
5683
5684pub fn render_quality_preset_names() -> &'static [&'static str] { &["Low", "Medium", "High", "Ultra"] }
5685pub fn supported_render_apis() -> &'static [&'static str] { &["OpenGL 4.5", "Vulkan 1.3", "Metal 2", "WebGPU", "D3D12"] }
5686pub fn shader_compiler_total_feature_count() -> usize { shader_module_count() + RENDER_FEATURE_COUNT + POST_PROCESS_FULL_EFFECT_COUNT + RENDER_QUALITY_PRESET_COUNT }
5687pub fn shader_compiler_final_info() -> String {
5688    format!("ShaderCompilerEditor: {} total features, {} render API targets, {} quality presets", shader_compiler_total_feature_count(), supported_render_apis().len(), RENDER_QUALITY_PRESET_COUNT)
5689}
5690
5691
5692// ── Instanced Rendering ───────────────────────────────────────────────────────
5693
5694#[derive(Clone, Debug)]
5695pub struct InstanceData {
5696    pub model_matrix: [[f32; 4]; 4],
5697    pub color: [f32; 4],
5698    pub custom0: [f32; 4],
5699    pub custom1: [f32; 4],
5700}
5701
5702impl InstanceData {
5703    pub fn new() -> Self { Self { model_matrix: [[1.0,0.0,0.0,0.0],[0.0,1.0,0.0,0.0],[0.0,0.0,1.0,0.0],[0.0,0.0,0.0,1.0]], color: [1.0; 4], custom0: [0.0; 4], custom1: [0.0; 4] } }
5704    pub fn with_color(mut self, r: f32, g: f32, b: f32, a: f32) -> Self { self.color = [r, g, b, a]; self }
5705}
5706
5707impl Default for InstanceData {
5708    fn default() -> Self { Self::new() }
5709}
5710
5711#[derive(Clone, Debug)]
5712pub struct InstanceBatch {
5713    pub id: u32,
5714    pub mesh_id: u32,
5715    pub material_id: u32,
5716    pub instances: Vec<InstanceData>,
5717    pub max_instances: u32,
5718    pub visible_count: u32,
5719    pub cull_enabled: bool,
5720}
5721
5722impl InstanceBatch {
5723    pub fn new(id: u32, mesh_id: u32, material_id: u32, max: u32) -> Self {
5724        Self { id, mesh_id, material_id, instances: Vec::new(), max_instances: max, visible_count: 0, cull_enabled: true }
5725    }
5726    pub fn add(&mut self, data: InstanceData) -> bool {
5727        if self.instances.len() >= self.max_instances as usize { return false; }
5728        self.instances.push(data); self.visible_count += 1; true
5729    }
5730    pub fn clear(&mut self) { self.instances.clear(); self.visible_count = 0; }
5731    pub fn count(&self) -> usize { self.instances.len() }
5732    pub fn is_full(&self) -> bool { self.instances.len() >= self.max_instances as usize }
5733    pub fn utilization(&self) -> f32 { if self.max_instances == 0 { 0.0 } else { self.instances.len() as f32 / self.max_instances as f32 } }
5734}
5735
5736#[derive(Clone, Debug)]
5737pub struct InstanceBatchManager {
5738    pub batches: HashMap<u32, InstanceBatch>,
5739    pub next_id: u32,
5740    pub total_instances: u32,
5741}
5742
5743impl InstanceBatchManager {
5744    pub fn new() -> Self { Self { batches: HashMap::new(), next_id: 1, total_instances: 0 } }
5745    pub fn create_batch(&mut self, mesh_id: u32, material_id: u32, max: u32) -> u32 {
5746        let id = self.next_id; self.next_id += 1;
5747        self.batches.insert(id, InstanceBatch::new(id, mesh_id, material_id, max));
5748        id
5749    }
5750    pub fn add_instance(&mut self, batch_id: u32, data: InstanceData) -> bool {
5751        if let Some(b) = self.batches.get_mut(&batch_id) { if b.add(data) { self.total_instances += 1; return true; } }
5752        false
5753    }
5754    pub fn clear_batch(&mut self, batch_id: u32) {
5755        if let Some(b) = self.batches.get_mut(&batch_id) { self.total_instances -= b.count() as u32; b.clear(); }
5756    }
5757    pub fn clear_all(&mut self) { for b in self.batches.values_mut() { b.clear(); } self.total_instances = 0; }
5758    pub fn batch_count(&self) -> usize { self.batches.len() }
5759    pub fn total_draw_calls(&self) -> usize { self.batches.values().filter(|b| b.count() > 0).count() }
5760}
5761
5762impl Default for InstanceBatchManager {
5763    fn default() -> Self { Self::new() }
5764}
5765
5766// ── Occlusion Culling ─────────────────────────────────────────────────────────
5767
5768#[derive(Clone, Debug)]
5769pub struct OcclusionQuery {
5770    pub id: u32,
5771    pub object_id: u32,
5772    pub visible: bool,
5773    pub pixel_count: u32,
5774    pub pending: bool,
5775    pub frame_issued: u64,
5776}
5777
5778impl OcclusionQuery {
5779    pub fn new(id: u32, object_id: u32) -> Self { Self { id, object_id, visible: true, pixel_count: 0, pending: false, frame_issued: 0 } }
5780    pub fn issue(&mut self, frame: u64) { self.pending = true; self.frame_issued = frame; }
5781    pub fn resolve(&mut self, pixels: u32) { self.pixel_count = pixels; self.visible = pixels > 0; self.pending = false; }
5782    pub fn is_occluded(&self) -> bool { !self.visible }
5783    pub fn is_stale(&self, current_frame: u64) -> bool { current_frame - self.frame_issued > 2 }
5784}
5785
5786#[derive(Clone, Debug)]
5787pub struct OcclusionCullSystem {
5788    pub queries: HashMap<u32, OcclusionQuery>,
5789    pub next_id: u32,
5790    pub enabled: bool,
5791    pub latency_frames: u32,
5792    pub culled_this_frame: u32,
5793    pub visible_this_frame: u32,
5794    pub frame: u64,
5795}
5796
5797impl OcclusionCullSystem {
5798    pub fn new() -> Self { Self { queries: HashMap::new(), next_id: 1, enabled: true, latency_frames: 1, culled_this_frame: 0, visible_this_frame: 0, frame: 0 } }
5799    pub fn register(&mut self, object_id: u32) -> u32 {
5800        let id = self.next_id; self.next_id += 1;
5801        self.queries.insert(id, OcclusionQuery::new(id, object_id));
5802        id
5803    }
5804    pub fn begin_frame(&mut self) { self.frame += 1; self.culled_this_frame = 0; self.visible_this_frame = 0; }
5805    pub fn is_visible(&self, query_id: u32) -> bool { self.queries.get(&query_id).map(|q| q.visible).unwrap_or(true) }
5806    pub fn resolve(&mut self, query_id: u32, pixels: u32) {
5807        if let Some(q) = self.queries.get_mut(&query_id) {
5808            q.resolve(pixels);
5809            if q.visible { self.visible_this_frame += 1; } else { self.culled_this_frame += 1; }
5810        }
5811    }
5812    pub fn cull_rate(&self) -> f32 {
5813        let total = self.culled_this_frame + self.visible_this_frame;
5814        if total == 0 { 0.0 } else { self.culled_this_frame as f32 / total as f32 }
5815    }
5816    pub fn query_count(&self) -> usize { self.queries.len() }
5817}
5818
5819impl Default for OcclusionCullSystem {
5820    fn default() -> Self { Self::new() }
5821}
5822
5823// ── Render State Tracker ──────────────────────────────────────────────────────
5824
5825#[derive(Clone, Debug, Default)]
5826pub struct RenderStateTracker {
5827    pub current_shader: Option<u32>,
5828    pub current_material: Option<u32>,
5829    pub current_vao: Option<u32>,
5830    pub current_fbo: Option<u32>,
5831    pub blend_enabled: bool,
5832    pub depth_test_enabled: bool,
5833    pub cull_enabled: bool,
5834    pub scissor_enabled: bool,
5835    pub state_changes: u32,
5836    pub shader_switches: u32,
5837    pub material_switches: u32,
5838    pub texture_uploads: u32,
5839    pub frame: u64,
5840}
5841
5842impl RenderStateTracker {
5843    pub fn new() -> Self { Self { depth_test_enabled: true, cull_enabled: true, ..Default::default() } }
5844    pub fn begin_frame(&mut self) { self.state_changes = 0; self.shader_switches = 0; self.material_switches = 0; self.texture_uploads = 0; self.frame += 1; }
5845    pub fn bind_shader(&mut self, id: u32) -> bool {
5846        if self.current_shader == Some(id) { return false; }
5847        self.current_shader = Some(id); self.shader_switches += 1; self.state_changes += 1; true
5848    }
5849    pub fn bind_material(&mut self, id: u32) -> bool {
5850        if self.current_material == Some(id) { return false; }
5851        self.current_material = Some(id); self.material_switches += 1; self.state_changes += 1; true
5852    }
5853    pub fn redundancy_rate(&self) -> f32 { 0.0 }
5854    pub fn reset_bindings(&mut self) { self.current_shader = None; self.current_material = None; self.current_vao = None; self.current_fbo = None; }
5855}
5856
5857// ── Shadow Map Atlas ──────────────────────────────────────────────────────────
5858
5859#[derive(Clone, Debug)]
5860pub struct ShadowMapAtlasEntry {
5861    pub light_id: u32,
5862    pub x: u32,
5863    pub y: u32,
5864    pub size: u32,
5865    pub valid: bool,
5866    pub last_updated_frame: u64,
5867}
5868
5869#[derive(Clone, Debug)]
5870pub struct ShadowMapAtlas {
5871    pub atlas_size: u32,
5872    pub entries: Vec<ShadowMapAtlasEntry>,
5873    pub next_x: u32,
5874    pub next_y: u32,
5875    pub current_row_height: u32,
5876}
5877
5878impl ShadowMapAtlas {
5879    pub fn new(atlas_size: u32) -> Self { Self { atlas_size, entries: Vec::new(), next_x: 0, next_y: 0, current_row_height: 0 } }
5880    pub fn allocate(&mut self, light_id: u32, size: u32) -> Option<(u32, u32)> {
5881        if self.next_x + size > self.atlas_size { self.next_x = 0; self.next_y += self.current_row_height; self.current_row_height = 0; }
5882        if self.next_y + size > self.atlas_size { return None; }
5883        let x = self.next_x; let y = self.next_y;
5884        self.current_row_height = self.current_row_height.max(size);
5885        self.next_x += size;
5886        self.entries.push(ShadowMapAtlasEntry { light_id, x, y, size, valid: true, last_updated_frame: 0 });
5887        Some((x, y))
5888    }
5889    pub fn find(&self, light_id: u32) -> Option<&ShadowMapAtlasEntry> { self.entries.iter().find(|e| e.light_id == light_id) }
5890    pub fn entry_count(&self) -> usize { self.entries.len() }
5891    pub fn reset(&mut self) { self.entries.clear(); self.next_x = 0; self.next_y = 0; self.current_row_height = 0; }
5892    pub fn utilization(&self) -> f32 { if self.atlas_size == 0 { 0.0 } else { self.next_y as f32 / self.atlas_size as f32 } }
5893}
5894
5895// ── More final constants ──────────────────────────────────────────────────────
5896
5897pub const INSTANCE_BATCH_DEFAULT_MAX: u32 = 1024;
5898pub const OCCLUSION_QUERY_MAX: usize = 4096;
5899pub const SHADOW_ATLAS_DEFAULT_SIZE: u32 = 4096;
5900pub const RENDER_STATE_TRACKER_FRAME_BUDGET: u32 = 1000;
5901pub const INSTANCE_DATA_SIZE_BYTES: usize = 128;
5902
5903pub fn glsl_mat4_from_rows(rows: [[f32; 4]; 4]) -> String {
5904    format!("mat4({:.4},{:.4},{:.4},{:.4}, {:.4},{:.4},{:.4},{:.4}, {:.4},{:.4},{:.4},{:.4}, {:.4},{:.4},{:.4},{:.4})",
5905        rows[0][0], rows[0][1], rows[0][2], rows[0][3],
5906        rows[1][0], rows[1][1], rows[1][2], rows[1][3],
5907        rows[2][0], rows[2][1], rows[2][2], rows[2][3],
5908        rows[3][0], rows[3][1], rows[3][2], rows[3][3])
5909}
5910pub fn glsl_vec4(r: f32, g: f32, b: f32, a: f32) -> String { format!("vec4({:.4}, {:.4}, {:.4}, {:.4})", r, g, b, a) }
5911pub fn glsl_vec3(x: f32, y: f32, z: f32) -> String { format!("vec3({:.4}, {:.4}, {:.4})", x, y, z) }
5912pub fn glsl_vec2(x: f32, y: f32) -> String { format!("vec2({:.4}, {:.4})", x, y) }
5913pub fn glsl_float(v: f32) -> String { format!("{:.6}", v) }
5914pub fn glsl_int(v: i32) -> String { format!("{}", v) }
5915pub fn glsl_bool(v: bool) -> &'static str { if v { "true" } else { "false" } }
5916pub fn glsl_define(name: &str, value: &str) -> String { format!("#define {} {}", name, value) }
5917pub fn glsl_ifdef_block(name: &str, code: &str) -> String { format!("#ifdef {}\n{}\n#endif // {}", name, code, name) }
5918pub fn glsl_version_string(ver: u32, es: bool) -> String { if es { format!("#version {} es", ver) } else { format!("#version {}", ver) } }
5919pub fn glsl_precision_header() -> &'static str { "precision highp float;\nprecision highp int;\nprecision highp sampler2D;\n" }
5920
5921pub fn shader_compiler_all_constants() -> HashMap<&'static str, u64> {
5922    let mut m = HashMap::new();
5923    m.insert("SHADER_MAX_UNIFORMS", SHADER_MAX_UNIFORMS as u64);
5924    m.insert("SHADER_MAX_ATTRIBUTES", SHADER_MAX_ATTRIBUTES as u64);
5925    m.insert("SHADER_MAX_PROGRAMS", SHADER_MAX_PROGRAMS as u64);
5926    m.insert("TEXTURE_MAX_SIZE", TEXTURE_MAX_SIZE as u64);
5927    m.insert("BUFFER_ALIGNMENT", BUFFER_ALIGNMENT as u64);
5928    m.insert("SHADOW_ATLAS_DEFAULT_SIZE", SHADOW_ATLAS_DEFAULT_SIZE as u64);
5929    m.insert("INSTANCE_BATCH_DEFAULT_MAX", INSTANCE_BATCH_DEFAULT_MAX as u64);
5930    m.insert("OCCLUSION_QUERY_MAX", OCCLUSION_QUERY_MAX as u64);
5931    m.insert("BLOOM_MAX_DOWNSAMPLE_PASSES", BLOOM_MAX_DOWNSAMPLE_PASSES as u64);
5932    m.insert("SSAO_KERNEL_SIZE_MAX", SSAO_KERNEL_SIZE_MAX as u64);
5933    m
5934}
5935
5936
5937// ── Motion Blur ───────────────────────────────────────────────────────────────
5938
5939#[derive(Clone, Debug)]
5940pub struct MotionBlurConfig {
5941    pub enabled: bool,
5942    pub shutter_angle: f32,
5943    pub sample_count: u32,
5944    pub tile_size: u32,
5945    pub max_blur_pixels: f32,
5946    pub motion_scale: f32,
5947    pub camera_motion_blur: bool,
5948    pub object_motion_blur: bool,
5949}
5950
5951impl MotionBlurConfig {
5952    pub fn new() -> Self { Self { enabled: false, shutter_angle: 180.0, sample_count: 8, tile_size: 10, max_blur_pixels: 40.0, motion_scale: 1.0, camera_motion_blur: true, object_motion_blur: true } }
5953    pub fn cinematic() -> Self { Self { enabled: true, shutter_angle: 270.0, sample_count: 16, ..Self::new() } }
5954    pub fn subtle() -> Self { Self { enabled: true, shutter_angle: 90.0, sample_count: 4, max_blur_pixels: 20.0, ..Self::new() } }
5955}
5956
5957impl Default for MotionBlurConfig { fn default() -> Self { Self::new() } }
5958
5959// ── Vignette ──────────────────────────────────────────────────────────────────
5960
5961#[derive(Clone, Debug)]
5962pub struct VignetteConfig {
5963    pub enabled: bool,
5964    pub intensity: f32,
5965    pub smoothness: f32,
5966    pub roundness: f32,
5967    pub color: [f32; 3],
5968    pub center: [f32; 2],
5969    pub mask_texture_id: Option<u32>,
5970}
5971
5972impl VignetteConfig {
5973    pub fn new() -> Self { Self { enabled: true, intensity: 0.45, smoothness: 0.2, roundness: 1.0, color: [0.0; 3], center: [0.5, 0.5], mask_texture_id: None } }
5974    pub fn subtle() -> Self { Self { intensity: 0.2, ..Self::new() } }
5975    pub fn dramatic() -> Self { Self { intensity: 0.8, smoothness: 0.4, ..Self::new() } }
5976    pub fn colored(r: f32, g: f32, b: f32) -> Self { Self { color: [r, g, b], ..Self::new() } }
5977    pub fn disabled() -> Self { Self { enabled: false, ..Self::new() } }
5978}
5979
5980impl Default for VignetteConfig { fn default() -> Self { Self::new() } }
5981
5982// ── Chromatic Aberration ──────────────────────────────────────────────────────
5983
5984#[derive(Clone, Debug)]
5985pub struct ChromaticAberrationConfig {
5986    pub enabled: bool,
5987    pub intensity: f32,
5988    pub radial: bool,
5989    pub offset_r: [f32; 2],
5990    pub offset_g: [f32; 2],
5991    pub offset_b: [f32; 2],
5992    pub sample_count: u32,
5993}
5994
5995impl ChromaticAberrationConfig {
5996    pub fn new() -> Self { Self { enabled: false, intensity: 0.5, radial: true, offset_r: [-0.01, 0.0], offset_g: [0.0, 0.0], offset_b: [0.01, 0.0], sample_count: 3 } }
5997    pub fn subtle() -> Self { Self { enabled: true, intensity: 0.2, ..Self::new() } }
5998    pub fn strong() -> Self { Self { enabled: true, intensity: 1.5, sample_count: 5, ..Self::new() } }
5999}
6000
6001impl Default for ChromaticAberrationConfig { fn default() -> Self { Self::new() } }
6002
6003// ── Film Grain ────────────────────────────────────────────────────────────────
6004
6005#[derive(Clone, Debug)]
6006pub struct FilmGrainConfig {
6007    pub enabled: bool,
6008    pub intensity: f32,
6009    pub size: f32,
6010    pub luminance_contribution: f32,
6011    pub color: bool,
6012    pub animated: bool,
6013}
6014
6015impl FilmGrainConfig {
6016    pub fn new() -> Self { Self { enabled: false, intensity: 0.1, size: 1.5, luminance_contribution: 0.8, color: false, animated: true } }
6017    pub fn subtle() -> Self { Self { enabled: true, intensity: 0.05, ..Self::new() } }
6018    pub fn heavy() -> Self { Self { enabled: true, intensity: 0.4, ..Self::new() } }
6019}
6020
6021impl Default for FilmGrainConfig { fn default() -> Self { Self::new() } }
6022
6023// ── Anti-Aliasing Config ──────────────────────────────────────────────────────
6024
6025#[derive(Clone, Debug)]
6026pub struct AntiAliasingConfig {
6027    pub mode: AntiAliasingMode,
6028    pub msaa_samples: u32,
6029    pub fxaa: bool,
6030    pub taa: Option<TaaConfig>,
6031    pub smaa_quality: u32,
6032}
6033
6034#[derive(Clone, Debug, PartialEq)]
6035pub enum AntiAliasingMode { None, Msaa, Fxaa, Taa, Smaa, Dlss }
6036
6037impl AntiAliasingConfig {
6038    pub fn none() -> Self { Self { mode: AntiAliasingMode::None, msaa_samples: 1, fxaa: false, taa: None, smaa_quality: 0 } }
6039    pub fn fxaa() -> Self { Self { mode: AntiAliasingMode::Fxaa, fxaa: true, ..Self::none() } }
6040    pub fn taa() -> Self { Self { mode: AntiAliasingMode::Taa, taa: Some(TaaConfig::new()), ..Self::none() } }
6041    pub fn msaa(samples: u32) -> Self { Self { mode: AntiAliasingMode::Msaa, msaa_samples: samples, ..Self::none() } }
6042    pub fn smaa(quality: u32) -> Self { Self { mode: AntiAliasingMode::Smaa, smaa_quality: quality, ..Self::none() } }
6043    pub fn is_temporal(&self) -> bool { self.mode == AntiAliasingMode::Taa || self.mode == AntiAliasingMode::Dlss }
6044    pub fn requires_velocity_buffer(&self) -> bool { self.is_temporal() }
6045}
6046
6047impl Default for AntiAliasingConfig { fn default() -> Self { Self::fxaa() } }
6048
6049// ── Extended post process config ──────────────────────────────────────────────
6050
6051#[derive(Clone, Debug, Default)]
6052pub struct ExtendedPostProcessConfig {
6053    pub motion_blur: MotionBlurConfig,
6054    pub vignette: VignetteConfig,
6055    pub chromatic_aberration: ChromaticAberrationConfig,
6056    pub film_grain: FilmGrainConfig,
6057    pub anti_aliasing: AntiAliasingConfig,
6058}
6059
6060impl ExtendedPostProcessConfig {
6061    pub fn new() -> Self { Self::default() }
6062    pub fn cinematic() -> Self {
6063        Self { motion_blur: MotionBlurConfig::cinematic(), vignette: VignetteConfig::dramatic(), chromatic_aberration: ChromaticAberrationConfig::subtle(), film_grain: FilmGrainConfig::subtle(), anti_aliasing: AntiAliasingConfig::taa() }
6064    }
6065    pub fn game() -> Self {
6066        Self { motion_blur: MotionBlurConfig::subtle(), vignette: VignetteConfig::subtle(), anti_aliasing: AntiAliasingConfig::fxaa(), ..Self::new() }
6067    }
6068    pub fn count_active(&self) -> usize {
6069        [self.motion_blur.enabled, self.vignette.enabled, self.chromatic_aberration.enabled, self.film_grain.enabled].iter().filter(|&&b| b).count()
6070    }
6071}
6072
6073// ── GPU Program Lifecycle ─────────────────────────────────────────────────────
6074
6075#[derive(Clone, Debug, PartialEq)]
6076pub enum GpuProgramState { Uncompiled, Compiling, Compiled, Failed, Stale }
6077
6078#[derive(Clone, Debug)]
6079pub struct GpuProgramLifecycle {
6080    pub program_id: u32,
6081    pub state: GpuProgramState,
6082    pub compile_attempts: u32,
6083    pub last_success: Option<u64>,
6084    pub last_failure: Option<u64>,
6085    pub auto_retry: bool,
6086    pub max_retries: u32,
6087}
6088
6089impl GpuProgramLifecycle {
6090    pub fn new(program_id: u32) -> Self { Self { program_id, state: GpuProgramState::Uncompiled, compile_attempts: 0, last_success: None, last_failure: None, auto_retry: true, max_retries: 3 } }
6091    pub fn begin_compile(&mut self) { self.state = GpuProgramState::Compiling; self.compile_attempts += 1; }
6092    pub fn mark_success(&mut self, ts: u64) { self.state = GpuProgramState::Compiled; self.last_success = Some(ts); }
6093    pub fn mark_failure(&mut self, ts: u64) { self.state = GpuProgramState::Failed; self.last_failure = Some(ts); }
6094    pub fn mark_stale(&mut self) { self.state = GpuProgramState::Stale; }
6095    pub fn can_retry(&self) -> bool { self.auto_retry && self.compile_attempts < self.max_retries }
6096    pub fn is_compiled(&self) -> bool { self.state == GpuProgramState::Compiled }
6097    pub fn is_failed(&self) -> bool { self.state == GpuProgramState::Failed }
6098}
6099
6100// ── Final system constants ────────────────────────────────────────────────────
6101
6102pub const MOTION_BLUR_MAX_SAMPLES: u32 = 32;
6103pub const FILM_GRAIN_MAX_SIZE: f32 = 10.0;
6104pub const VIGNETTE_MAX_INTENSITY: f32 = 1.0;
6105pub const CHROMATIC_MAX_OFFSET: f32 = 0.1;
6106pub const AA_MSAA_MAX_SAMPLES: u32 = 8;
6107pub const SHADER_GPU_PROGRAM_MAX_RETRIES: u32 = 3;
6108pub const RENDER_EXTENDED_PP_EFFECT_COUNT: usize = 5;
6109
6110pub fn anti_aliasing_name(mode: &AntiAliasingMode) -> &'static str {
6111    match mode { AntiAliasingMode::None => "None", AntiAliasingMode::Msaa => "MSAA", AntiAliasingMode::Fxaa => "FXAA", AntiAliasingMode::Taa => "TAA", AntiAliasingMode::Smaa => "SMAA", AntiAliasingMode::Dlss => "DLSS" }
6112}
6113pub fn gpu_program_state_name(s: &GpuProgramState) -> &'static str {
6114    match s { GpuProgramState::Uncompiled => "Uncompiled", GpuProgramState::Compiling => "Compiling", GpuProgramState::Compiled => "Compiled", GpuProgramState::Failed => "Failed", GpuProgramState::Stale => "Stale" }
6115}
6116pub fn shader_editor_complete_version() -> String { format!("ShaderCompilerEditor — complete build, v{}", shader_system_version()) }
6117
6118
6119// ── Shader Input Assembler ────────────────────────────────────────────────────
6120
6121#[derive(Clone, Debug)]
6122pub struct InputAssemblerConfig {
6123    pub topology: PrimitiveTopology,
6124    pub restart_enabled: bool,
6125    pub restart_index: u32,
6126    pub vertex_layout: VertexLayout,
6127}
6128
6129#[derive(Clone, Debug, PartialEq)]
6130pub enum PrimitiveTopology { PointList, LineList, LineStrip, TriangleList, TriangleStrip, TriangleFan, PatchList(u32) }
6131
6132impl PrimitiveTopology {
6133    pub fn name(&self) -> &'static str {
6134        match self { PrimitiveTopology::PointList => "PointList", PrimitiveTopology::LineList => "LineList", PrimitiveTopology::LineStrip => "LineStrip", PrimitiveTopology::TriangleList => "TriangleList", PrimitiveTopology::TriangleStrip => "TriangleStrip", PrimitiveTopology::TriangleFan => "TriangleFan", PrimitiveTopology::PatchList(_) => "PatchList" }
6135    }
6136    pub fn is_strip(&self) -> bool { matches!(self, PrimitiveTopology::LineStrip | PrimitiveTopology::TriangleStrip) }
6137    pub fn is_triangle(&self) -> bool { matches!(self, PrimitiveTopology::TriangleList | PrimitiveTopology::TriangleStrip | PrimitiveTopology::TriangleFan) }
6138}
6139
6140impl InputAssemblerConfig {
6141    pub fn triangles(layout: VertexLayout) -> Self { Self { topology: PrimitiveTopology::TriangleList, restart_enabled: false, restart_index: 0xFFFFFFFF, vertex_layout: layout } }
6142    pub fn lines(layout: VertexLayout) -> Self { Self { topology: PrimitiveTopology::LineList, restart_enabled: false, restart_index: 0xFFFFFFFF, vertex_layout: layout } }
6143    pub fn points(layout: VertexLayout) -> Self { Self { topology: PrimitiveTopology::PointList, restart_enabled: false, restart_index: 0xFFFFFFFF, vertex_layout: layout } }
6144}
6145
6146// ── Stencil Config ────────────────────────────────────────────────────────────
6147
6148#[derive(Clone, Debug)]
6149pub struct StencilConfig {
6150    pub enabled: bool,
6151    pub ref_value: u32,
6152    pub read_mask: u32,
6153    pub write_mask: u32,
6154    pub fail_op: StencilOp,
6155    pub depth_fail_op: StencilOp,
6156    pub pass_op: StencilOp,
6157    pub compare_func: CompareFunc,
6158}
6159
6160#[derive(Clone, Debug, PartialEq)]
6161pub enum StencilOp { Keep, Zero, Replace, IncrClamp, DecrClamp, Invert, IncrWrap, DecrWrap }
6162
6163impl StencilConfig {
6164    pub fn disabled() -> Self { Self { enabled: false, ref_value: 0, read_mask: 0xFF, write_mask: 0xFF, fail_op: StencilOp::Keep, depth_fail_op: StencilOp::Keep, pass_op: StencilOp::Keep, compare_func: CompareFunc::Always } }
6165    pub fn write_mask(mask: u32) -> Self { Self { enabled: true, write_mask: mask, pass_op: StencilOp::Replace, ref_value: mask, ..Self::disabled() } }
6166    pub fn test_mask(mask: u32) -> Self { Self { enabled: true, read_mask: mask, ref_value: mask, ..Self::disabled() } }
6167}
6168
6169impl Default for StencilConfig { fn default() -> Self { Self::disabled() } }
6170
6171// ── Viewport Config ───────────────────────────────────────────────────────────
6172
6173#[derive(Clone, Debug)]
6174pub struct ViewportConfig {
6175    pub x: f32,
6176    pub y: f32,
6177    pub width: f32,
6178    pub height: f32,
6179    pub min_depth: f32,
6180    pub max_depth: f32,
6181}
6182
6183impl ViewportConfig {
6184    pub fn new(width: f32, height: f32) -> Self { Self { x: 0.0, y: 0.0, width, height, min_depth: 0.0, max_depth: 1.0 } }
6185    pub fn aspect_ratio(&self) -> f32 { if self.height == 0.0 { 1.0 } else { self.width / self.height } }
6186    pub fn scale(mut self, s: f32) -> Self { self.width *= s; self.height *= s; self }
6187    pub fn ndc_to_pixel(&self, ndc_x: f32, ndc_y: f32) -> (f32, f32) {
6188        ((ndc_x * 0.5 + 0.5) * self.width + self.x, (ndc_y * -0.5 + 0.5) * self.height + self.y)
6189    }
6190    pub fn pixel_to_ndc(&self, px: f32, py: f32) -> (f32, f32) {
6191        ((px - self.x) / self.width * 2.0 - 1.0, -((py - self.y) / self.height * 2.0 - 1.0))
6192    }
6193}
6194
6195impl Default for ViewportConfig { fn default() -> Self { Self::new(1920.0, 1080.0) } }
6196
6197// ── Rasterizer State ──────────────────────────────────────────────────────────
6198
6199#[derive(Clone, Debug)]
6200pub struct RasterizerState {
6201    pub cull_mode: CullMode,
6202    pub front_face: FrontFace,
6203    pub polygon_mode: PolygonMode,
6204    pub depth_bias: f32,
6205    pub depth_bias_slope_scale: f32,
6206    pub depth_bias_clamp: f32,
6207    pub depth_clamp: bool,
6208    pub rasterizer_discard: bool,
6209    pub multisample: bool,
6210    pub line_width: f32,
6211    pub conservative: bool,
6212}
6213
6214impl Default for RasterizerState {
6215    fn default() -> Self { Self { cull_mode: CullMode::Back, front_face: FrontFace::CounterClockwise, polygon_mode: PolygonMode::Fill, depth_bias: 0.0, depth_bias_slope_scale: 0.0, depth_bias_clamp: 0.0, depth_clamp: false, rasterizer_discard: false, multisample: false, line_width: 1.0, conservative: false } }
6216}
6217
6218impl RasterizerState {
6219    pub fn wireframe() -> Self { Self { polygon_mode: PolygonMode::Line, cull_mode: CullMode::None, ..Default::default() } }
6220    pub fn shadow_map() -> Self { Self { depth_bias: 1.0, depth_bias_slope_scale: 1.5, ..Default::default() } }
6221    pub fn two_sided() -> Self { Self { cull_mode: CullMode::None, ..Default::default() } }
6222    pub fn is_wireframe(&self) -> bool { self.polygon_mode == PolygonMode::Line }
6223    pub fn has_depth_bias(&self) -> bool { self.depth_bias.abs() > 1e-6 || self.depth_bias_slope_scale.abs() > 1e-6 }
6224}
6225
6226// ── Complete Graphics Pipeline Descriptor ─────────────────────────────────────
6227
6228#[derive(Clone, Debug)]
6229pub struct GraphicsPipelineDescriptor {
6230    pub name: String,
6231    pub vertex_shader_id: u32,
6232    pub fragment_shader_id: u32,
6233    pub geometry_shader_id: Option<u32>,
6234    pub input_assembly: InputAssemblerConfig,
6235    pub rasterizer: RasterizerState,
6236    pub depth_stencil: (bool, bool, CompareFunc, StencilConfig),
6237    pub blend_state: PipelineState,
6238    pub viewport: ViewportConfig,
6239    pub render_pass_id: u32,
6240    pub subpass: u32,
6241    pub layout: PipelineLayout,
6242}
6243
6244impl GraphicsPipelineDescriptor {
6245    pub fn new(name: impl Into<String>, vert_id: u32, frag_id: u32) -> Self {
6246        Self { name: name.into(), vertex_shader_id: vert_id, fragment_shader_id: frag_id, geometry_shader_id: None, input_assembly: InputAssemblerConfig::triangles(VertexLayout::standard_mesh()), rasterizer: RasterizerState::default(), depth_stencil: (true, true, CompareFunc::Less, StencilConfig::disabled()), blend_state: PipelineState::opaque(), viewport: ViewportConfig::default(), render_pass_id: 0, subpass: 0, layout: PipelineLayout::new() }
6247    }
6248    pub fn transparent(name: impl Into<String>, vert_id: u32, frag_id: u32) -> Self {
6249        let mut d = Self::new(name, vert_id, frag_id); d.blend_state = PipelineState::transparent(); d.depth_stencil.1 = false; d
6250    }
6251    pub fn has_geometry_shader(&self) -> bool { self.geometry_shader_id.is_some() }
6252    pub fn depth_test_enabled(&self) -> bool { self.depth_stencil.0 }
6253    pub fn depth_write_enabled(&self) -> bool { self.depth_stencil.1 }
6254}
6255
6256// ── Final shader constants ────────────────────────────────────────────────────
6257
6258pub const STENCIL_MAX_REF_VALUE: u32 = 255;
6259pub const VIEWPORT_MAX_RENDER_SCALE: f32 = 2.0;
6260pub const RASTERIZER_MAX_LINE_WIDTH: f32 = 16.0;
6261pub const GRAPHICS_PIPELINE_MAX: usize = 512;
6262pub const INPUT_ASSEMBLER_MAX_VERTEX_STREAMS: usize = 8;
6263
6264pub fn stencil_op_name(op: &StencilOp) -> &'static str {
6265    match op { StencilOp::Keep => "Keep", StencilOp::Zero => "Zero", StencilOp::Replace => "Replace", StencilOp::IncrClamp => "IncrClamp", StencilOp::DecrClamp => "DecrClamp", StencilOp::Invert => "Invert", StencilOp::IncrWrap => "IncrWrap", StencilOp::DecrWrap => "DecrWrap" }
6266}
6267pub fn primitive_topology_vertex_count(topo: &PrimitiveTopology, primitive_count: u32) -> u32 {
6268    match topo { PrimitiveTopology::PointList => primitive_count, PrimitiveTopology::LineList => primitive_count * 2, PrimitiveTopology::LineStrip => primitive_count + 1, PrimitiveTopology::TriangleList => primitive_count * 3, PrimitiveTopology::TriangleStrip | PrimitiveTopology::TriangleFan => primitive_count + 2, PrimitiveTopology::PatchList(n) => primitive_count * n }
6269}
6270
6271pub fn shader_compiler_feature_summary() -> String {
6272    let features = shader_compiler_module_list();
6273    format!("ShaderCompilerEditor v{}: {} modules — {}", shader_system_version(), features.len(), features.join(", "))
6274}
6275
6276
6277// ── Render Target Management ──────────────────────────────────────────────────
6278
6279#[derive(Clone, Debug)]
6280pub struct RenderTargetSet {
6281    pub id: u32,
6282    pub name: String,
6283    pub width: u32,
6284    pub height: u32,
6285    pub color_targets: Vec<u32>,
6286    pub depth_target: Option<u32>,
6287    pub msaa_samples: u32,
6288    pub active: bool,
6289}
6290
6291impl RenderTargetSet {
6292    pub fn new(id: u32, name: impl Into<String>, w: u32, h: u32) -> Self {
6293        Self { id, name: name.into(), width: w, height: h, color_targets: Vec::new(), depth_target: None, msaa_samples: 1, active: false }
6294    }
6295    pub fn add_color(&mut self, tex_id: u32) { self.color_targets.push(tex_id); }
6296    pub fn set_depth(&mut self, tex_id: u32) { self.depth_target = Some(tex_id); }
6297    pub fn enable(&mut self) { self.active = true; }
6298    pub fn disable(&mut self) { self.active = false; }
6299    pub fn color_count(&self) -> usize { self.color_targets.len() }
6300    pub fn has_depth(&self) -> bool { self.depth_target.is_some() }
6301    pub fn total_attachments(&self) -> usize { self.color_targets.len() + if self.has_depth() { 1 } else { 0 } }
6302    pub fn aspect_ratio(&self) -> f32 { if self.height == 0 { 1.0 } else { self.width as f32 / self.height as f32 } }
6303    pub fn is_multisampled(&self) -> bool { self.msaa_samples > 1 }
6304    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 }
6305}
6306
6307#[derive(Clone, Debug)]
6308pub struct RenderTargetManager {
6309    pub sets: HashMap<u32, RenderTargetSet>,
6310    pub active_set: Option<u32>,
6311    pub next_id: u32,
6312    pub backbuffer_id: u32,
6313}
6314
6315impl RenderTargetManager {
6316    pub fn new() -> Self { Self { sets: HashMap::new(), active_set: None, next_id: 1, backbuffer_id: 0 } }
6317    pub fn create(&mut self, name: impl Into<String>, w: u32, h: u32) -> u32 {
6318        let id = self.next_id; self.next_id += 1;
6319        self.sets.insert(id, RenderTargetSet::new(id, name, w, h));
6320        id
6321    }
6322    pub fn get(&self, id: u32) -> Option<&RenderTargetSet> { self.sets.get(&id) }
6323    pub fn bind(&mut self, id: u32) { if let Some(s) = self.sets.get_mut(&id) { s.enable(); } self.active_set = Some(id); }
6324    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; }
6325    pub fn resize_all(&mut self, w: u32, h: u32) { for s in self.sets.values_mut() { s.width = w; s.height = h; } }
6326    pub fn count(&self) -> usize { self.sets.len() }
6327    pub fn active(&self) -> Option<&RenderTargetSet> { self.active_set.and_then(|id| self.sets.get(&id)) }
6328}
6329
6330impl Default for RenderTargetManager { fn default() -> Self { Self::new() } }
6331
6332// ── Frame Graph Executor ──────────────────────────────────────────────────────
6333
6334#[derive(Clone, Debug)]
6335pub struct FrameGraphExecutor {
6336    pub render_graph: RenderGraph,
6337    pub rt_manager: RenderTargetManager,
6338    pub frame: u64,
6339    pub paused: bool,
6340}
6341
6342impl FrameGraphExecutor {
6343    pub fn new() -> Self { Self { render_graph: RenderGraph::new("frame"), rt_manager: RenderTargetManager::new(), frame: 0, paused: false } }
6344    pub fn begin_frame(&mut self) { if !self.paused { self.frame += 1; } }
6345    pub fn pause(&mut self) { self.paused = true; }
6346    pub fn resume(&mut self) { self.paused = false; }
6347    pub fn node_count(&self) -> usize { self.render_graph.node_count() }
6348    pub fn is_running(&self) -> bool { !self.paused }
6349    pub fn frame_number(&self) -> u64 { self.frame }
6350}
6351
6352impl Default for FrameGraphExecutor { fn default() -> Self { Self::new() } }
6353
6354// ── Shader Compiler Statistics ────────────────────────────────────────────────
6355
6356#[derive(Clone, Debug, Default)]
6357pub struct ShaderCompilerStats {
6358    pub total_programs: usize,
6359    pub total_materials: usize,
6360    pub total_textures: usize,
6361    pub total_buffers: usize,
6362    pub total_passes: usize,
6363    pub total_post_effects: usize,
6364    pub cache_entries: usize,
6365    pub cache_hit_rate: f32,
6366    pub total_compile_ms: f32,
6367    pub failed_compiles: u32,
6368    pub hot_reloads: u32,
6369}
6370
6371impl ShaderCompilerStats {
6372    pub fn from_workspace(ws: &ShaderWorkspace) -> Self {
6373        Self {
6374            total_programs: ws.program_count(),
6375            total_materials: ws.material_count(),
6376            total_textures: ws.texture_count(),
6377            total_buffers: ws.buffer_count(),
6378            total_passes: ws.pipeline.pass_count(),
6379            total_post_effects: ws.post_process.effect_count(),
6380            cache_entries: ws.cache.size(),
6381            cache_hit_rate: ws.cache.hit_rate(),
6382            ..Default::default()
6383        }
6384    }
6385    pub fn summary(&self) -> String {
6386        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)
6387    }
6388}
6389
6390// ── Final module constants ────────────────────────────────────────────────────
6391
6392pub const RENDER_TARGET_MAX: usize = 64;
6393pub const FRAME_GRAPH_MAX_NODES: usize = 128;
6394pub const RT_MAX_COLOR_ATTACHMENTS: usize = 8;
6395pub const RT_DEFAULT_MSAA: u32 = 1;
6396pub const SHADER_COMPILER_SESSION_MAX: usize = 4;
6397
6398pub fn all_post_process_effect_names() -> &'static [&'static str] {
6399    &["Bloom", "Tone Mapping", "SSAO", "SSR", "TAA", "Depth of Field", "Volumetric Fog", "Lens Flare", "Motion Blur", "Vignette", "Chromatic Aberration", "Film Grain", "FXAA", "Color Grading", "SMAA"]
6400}
6401
6402pub fn shader_compiler_comprehensive_info() -> String {
6403    format!(concat!(
6404        "ShaderCompilerEditor v{} Summary:\n",
6405        "  Modules: {}\n",
6406        "  Render API targets: {}\n",
6407        "  Quality presets: {}\n",
6408        "  Post-process effects: {}\n",
6409        "  Builtin GLSL snippets: {}\n",
6410        "  Builtin GLSL functions: {}\n"
6411    ),
6412    shader_system_version(),
6413    shader_module_count(),
6414    supported_render_apis().len(),
6415    RENDER_QUALITY_PRESET_COUNT,
6416    all_post_process_effect_names().len(),
6417    SHADER_BUILTIN_SNIPPETS,
6418    SHADER_GLSL_BUILTIN_FUNCTIONS)
6419}
6420
6421
6422// ── Shader Manager ────────────────────────────────────────────────────────────
6423
6424#[derive(Clone, Debug)]
6425pub struct ShaderManager {
6426    pub compiler: ShaderCompilerMain,
6427    pub post_config: FullPostProcessConfig,
6428    pub extended_post: ExtendedPostProcessConfig,
6429    pub anti_aliasing: AntiAliasingConfig,
6430    pub quality_preset: String,
6431    pub quality_presets: Vec<RenderQualityPreset>,
6432    pub frame_executor: FrameGraphExecutor,
6433    pub rt_manager: RenderTargetManager,
6434    pub uniform_tracker: UniformTracker,
6435    pub occlusion_cull: OcclusionCullSystem,
6436    pub instance_batches: InstanceBatchManager,
6437    pub render_state: RenderStateTracker,
6438    pub error_reports: Vec<ShaderErrorReport>,
6439    pub compile_stats: CompileStats,
6440}
6441
6442impl ShaderManager {
6443    pub fn new() -> Self {
6444        Self {
6445            compiler: ShaderCompilerMain::new(),
6446            post_config: FullPostProcessConfig::new(),
6447            extended_post: ExtendedPostProcessConfig::new(),
6448            anti_aliasing: AntiAliasingConfig::default(),
6449            quality_preset: "High".into(),
6450            quality_presets: build_quality_presets(),
6451            frame_executor: FrameGraphExecutor::new(),
6452            rt_manager: RenderTargetManager::new(),
6453            uniform_tracker: UniformTracker::new(),
6454            occlusion_cull: OcclusionCullSystem::new(),
6455            instance_batches: InstanceBatchManager::new(),
6456            render_state: RenderStateTracker::new(),
6457            error_reports: Vec::new(),
6458            compile_stats: CompileStats::new(),
6459        }
6460    }
6461    pub fn add_program(&mut self, prog: ShaderProgram) -> u32 { self.compiler.add_program(prog) }
6462    pub fn add_material(&mut self, mat: Material) -> u32 { self.compiler.add_material(mat) }
6463    pub fn begin_frame(&mut self) { self.compiler.begin_frame(); self.frame_executor.begin_frame(); self.render_state.begin_frame(); self.occlusion_cull.begin_frame(); self.instance_batches.clear_all(); }
6464    pub fn set_quality(&mut self, preset_name: &str) { if self.quality_presets.iter().any(|p| p.name == preset_name) { self.quality_preset = preset_name.to_string(); } }
6465    pub fn record_error(&mut self, report: ShaderErrorReport) { self.error_reports.push(report); }
6466    pub fn clear_errors(&mut self) { self.error_reports.clear(); }
6467    pub fn has_errors(&self) -> bool { !self.error_reports.is_empty() }
6468    pub fn error_count(&self) -> usize { self.error_reports.len() }
6469    pub fn program_count(&self) -> usize { self.compiler.program_count() }
6470    pub fn compilation_summary(&self) -> String { self.compiler.compilation_summary() }
6471    pub fn stats(&self) -> ShaderCompilerStats { ShaderCompilerStats::from_workspace(&self.compiler.workspace) }
6472}
6473
6474impl Default for ShaderManager { fn default() -> Self { Self::new() } }
6475
6476// ── GLSL Intrinsics Documentation ─────────────────────────────────────────────
6477
6478pub struct GlslIntrinsicDoc {
6479    pub name: &'static str,
6480    pub signature: &'static str,
6481    pub description: &'static str,
6482    pub category: &'static str,
6483}
6484
6485pub const GLSL_DOCS: &[GlslIntrinsicDoc] = &[
6486    GlslIntrinsicDoc { name: "radians", signature: "genType radians(genType degrees)", description: "Convert degrees to radians", category: "trigonometry" },
6487    GlslIntrinsicDoc { name: "degrees", signature: "genType degrees(genType radians)", description: "Convert radians to degrees", category: "trigonometry" },
6488    GlslIntrinsicDoc { name: "sin", signature: "genType sin(genType angle)", description: "Sine of angle in radians", category: "trigonometry" },
6489    GlslIntrinsicDoc { name: "cos", signature: "genType cos(genType angle)", description: "Cosine of angle in radians", category: "trigonometry" },
6490    GlslIntrinsicDoc { name: "tan", signature: "genType tan(genType angle)", description: "Tangent of angle in radians", category: "trigonometry" },
6491    GlslIntrinsicDoc { name: "asin", signature: "genType asin(genType x)", description: "Arc sine", category: "trigonometry" },
6492    GlslIntrinsicDoc { name: "acos", signature: "genType acos(genType x)", description: "Arc cosine", category: "trigonometry" },
6493    GlslIntrinsicDoc { name: "atan", signature: "genType atan(genType y_over_x)", description: "Arc tangent", category: "trigonometry" },
6494    GlslIntrinsicDoc { name: "sinh", signature: "genType sinh(genType x)", description: "Hyperbolic sine", category: "trigonometry" },
6495    GlslIntrinsicDoc { name: "cosh", signature: "genType cosh(genType x)", description: "Hyperbolic cosine", category: "trigonometry" },
6496    GlslIntrinsicDoc { name: "pow", signature: "genType pow(genType x, genType y)", description: "x raised to y power", category: "exponential" },
6497    GlslIntrinsicDoc { name: "exp", signature: "genType exp(genType x)", description: "e raised to x", category: "exponential" },
6498    GlslIntrinsicDoc { name: "log", signature: "genType log(genType x)", description: "Natural logarithm", category: "exponential" },
6499    GlslIntrinsicDoc { name: "exp2", signature: "genType exp2(genType x)", description: "2 raised to x", category: "exponential" },
6500    GlslIntrinsicDoc { name: "log2", signature: "genType log2(genType x)", description: "Base-2 logarithm", category: "exponential" },
6501    GlslIntrinsicDoc { name: "sqrt", signature: "genType sqrt(genType x)", description: "Square root", category: "exponential" },
6502    GlslIntrinsicDoc { name: "inversesqrt", signature: "genType inversesqrt(genType x)", description: "Inverse square root", category: "exponential" },
6503    GlslIntrinsicDoc { name: "abs", signature: "genType abs(genType x)", description: "Absolute value", category: "common" },
6504    GlslIntrinsicDoc { name: "sign", signature: "genType sign(genType x)", description: "Sign of x", category: "common" },
6505    GlslIntrinsicDoc { name: "floor", signature: "genType floor(genType x)", description: "Floor function", category: "common" },
6506    GlslIntrinsicDoc { name: "ceil", signature: "genType ceil(genType x)", description: "Ceiling function", category: "common" },
6507    GlslIntrinsicDoc { name: "round", signature: "genType round(genType x)", description: "Round to nearest", category: "common" },
6508    GlslIntrinsicDoc { name: "fract", signature: "genType fract(genType x)", description: "Fractional part", category: "common" },
6509    GlslIntrinsicDoc { name: "mod", signature: "genType mod(genType x, genType y)", description: "Modulo operation", category: "common" },
6510    GlslIntrinsicDoc { name: "min", signature: "genType min(genType x, genType y)", description: "Minimum of two values", category: "common" },
6511    GlslIntrinsicDoc { name: "max", signature: "genType max(genType x, genType y)", description: "Maximum of two values", category: "common" },
6512    GlslIntrinsicDoc { name: "clamp", signature: "genType clamp(genType x, genType minVal, genType maxVal)", description: "Clamp x between min and max", category: "common" },
6513    GlslIntrinsicDoc { name: "mix", signature: "genType mix(genType x, genType y, genType a)", description: "Linear interpolation", category: "common" },
6514    GlslIntrinsicDoc { name: "step", signature: "genType step(genType edge, genType x)", description: "Step function", category: "common" },
6515    GlslIntrinsicDoc { name: "smoothstep", signature: "genType smoothstep(genType edge0, genType edge1, genType x)", description: "Smooth step function", category: "common" },
6516];
6517
6518pub fn find_glsl_doc(name: &str) -> Option<&'static GlslIntrinsicDoc> { GLSL_DOCS.iter().find(|d| d.name == name) }
6519pub fn glsl_doc_count() -> usize { GLSL_DOCS.len() }
6520pub fn glsl_docs_in_category(cat: &str) -> Vec<&'static GlslIntrinsicDoc> { GLSL_DOCS.iter().filter(|d| d.category == cat).collect() }
6521
6522pub const SHADER_MANAGER_VERSION: &str = "ShaderManager v1.0";
6523pub const SHADER_GLSL_INTRINSIC_DOC_COUNT: usize = 30;
6524pub const SHADER_MAX_ERROR_REPORTS: usize = 256;
6525pub const FRAME_GRAPH_EXECUTOR_MAX_NODES: usize = 128;
6526
6527pub fn shader_manager_info() -> String {
6528    format!("{} — {} programs max, {} intrinsics documented, {} quality presets", SHADER_MANAGER_VERSION, SHADER_MAX_PROGRAMS, SHADER_GLSL_INTRINSIC_DOC_COUNT, RENDER_QUALITY_PRESET_COUNT)
6529}
6530
6531
6532// ── Geometry Shader Support ───────────────────────────────────────────────────
6533
6534#[derive(Clone, Debug)]
6535pub struct GeometryShaderConfig {
6536    pub input_primitive: GeometryPrimitive,
6537    pub output_primitive: GeometryPrimitive,
6538    pub max_vertices_out: u32,
6539    pub invocations: u32,
6540}
6541
6542#[derive(Clone, Debug, PartialEq)]
6543pub enum GeometryPrimitive { Points, Lines, LinesAdjacency, Triangles, TrianglesAdjacency, LineStrip, TriangleStrip }
6544
6545impl GeometryShaderConfig {
6546    pub fn passthrough_triangles() -> Self { Self { input_primitive: GeometryPrimitive::Triangles, output_primitive: GeometryPrimitive::TriangleStrip, max_vertices_out: 3, invocations: 1 } }
6547    pub fn silhouette_detection() -> Self { Self { input_primitive: GeometryPrimitive::TrianglesAdjacency, output_primitive: GeometryPrimitive::LineStrip, max_vertices_out: 4, invocations: 1 } }
6548    pub fn shadow_volumes() -> Self { Self { input_primitive: GeometryPrimitive::Triangles, output_primitive: GeometryPrimitive::TriangleStrip, max_vertices_out: 18, invocations: 1 } }
6549    pub fn point_sprites() -> Self { Self { input_primitive: GeometryPrimitive::Points, output_primitive: GeometryPrimitive::TriangleStrip, max_vertices_out: 4, invocations: 1 } }
6550    pub fn cubemap_rendering() -> Self { Self { input_primitive: GeometryPrimitive::Triangles, output_primitive: GeometryPrimitive::TriangleStrip, max_vertices_out: 18, invocations: 6 } }
6551}
6552
6553impl Default for GeometryShaderConfig { fn default() -> Self { Self::passthrough_triangles() } }
6554
6555// ── Tessellation Shader Support ───────────────────────────────────────────────
6556
6557#[derive(Clone, Debug)]
6558pub struct TessellationConfig {
6559    pub enabled: bool,
6560    pub patch_size: u32,
6561    pub outer_level: [f32; 4],
6562    pub inner_level: [f32; 2],
6563    pub spacing: TessSpacing,
6564    pub winding: TessWinding,
6565    pub primitive: TessPrimitive,
6566    pub point_mode: bool,
6567    pub adaptive: bool,
6568    pub max_tess_level: f32,
6569    pub screen_space_factor: f32,
6570}
6571
6572#[derive(Clone, Debug, PartialEq)]
6573pub enum TessSpacing { Equal, FractionalEven, FractionalOdd }
6574#[derive(Clone, Debug, PartialEq)]
6575pub enum TessWinding { Ccw, Cw }
6576#[derive(Clone, Debug, PartialEq)]
6577pub enum TessPrimitive { Triangles, Quads, Isolines }
6578
6579impl TessellationConfig {
6580    pub fn new() -> Self { Self { enabled: false, patch_size: 3, outer_level: [4.0; 4], inner_level: [4.0; 2], spacing: TessSpacing::Equal, winding: TessWinding::Ccw, primitive: TessPrimitive::Triangles, point_mode: false, adaptive: false, max_tess_level: 64.0, screen_space_factor: 1.0 } }
6581    pub fn displacement_mapping() -> Self { Self { enabled: true, adaptive: true, ..Self::new() } }
6582    pub fn lod_triangles(level: f32) -> Self { Self { enabled: true, outer_level: [level; 4], inner_level: [level; 2], ..Self::new() } }
6583    pub fn disabled() -> Self { Self { enabled: false, ..Self::new() } }
6584    pub fn effective_patch_vertices(&self) -> u32 {
6585        match self.primitive { TessPrimitive::Triangles => 3, TessPrimitive::Quads => 4, TessPrimitive::Isolines => 2 }
6586    }
6587}
6588
6589impl Default for TessellationConfig { fn default() -> Self { Self::new() } }
6590
6591// ── Wireframe Overlay ─────────────────────────────────────────────────────────
6592
6593#[derive(Clone, Debug)]
6594pub struct WireframeConfig {
6595    pub enabled: bool,
6596    pub color: [f32; 4],
6597    pub line_width: f32,
6598    pub overlay_on_solid: bool,
6599    pub show_normals: bool,
6600    pub normal_length: f32,
6601    pub normal_color: [f32; 4],
6602    pub show_tangents: bool,
6603    pub highlight_selected: bool,
6604    pub selection_color: [f32; 4],
6605}
6606
6607impl WireframeConfig {
6608    pub fn new() -> Self { Self { enabled: false, color: [0.0, 1.0, 0.0, 1.0], line_width: 1.0, overlay_on_solid: true, show_normals: false, normal_length: 0.1, normal_color: [0.0, 0.0, 1.0, 1.0], show_tangents: false, highlight_selected: true, selection_color: [1.0, 0.5, 0.0, 1.0] } }
6609    pub fn enable(&mut self) { self.enabled = true; }
6610    pub fn disable(&mut self) { self.enabled = false; }
6611    pub fn show_normals(mut self) -> Self { self.show_normals = true; self }
6612}
6613
6614impl Default for WireframeConfig { fn default() -> Self { Self::new() } }
6615
6616// ── Shader Error Recovery ─────────────────────────────────────────────────────
6617
6618#[derive(Clone, Debug)]
6619pub struct ShaderFallback {
6620    pub trigger_on_error: bool,
6621    pub fallback_program_id: u32,
6622    pub fallback_name: String,
6623    pub auto_compile_fallback: bool,
6624    pub notify_on_fallback: bool,
6625}
6626
6627impl ShaderFallback {
6628    pub fn new(fallback_id: u32) -> Self { Self { trigger_on_error: true, fallback_program_id: fallback_id, fallback_name: "error_fallback".into(), auto_compile_fallback: true, notify_on_fallback: true } }
6629    pub fn with_name(mut self, name: impl Into<String>) -> Self { self.fallback_name = name.into(); self }
6630    pub fn silent(mut self) -> Self { self.notify_on_fallback = false; self }
6631}
6632
6633impl Default for ShaderFallback { fn default() -> Self { Self::new(0) } }
6634
6635// ── Final constants ───────────────────────────────────────────────────────────
6636
6637pub const TESSELLATION_MAX_PATCH_SIZE: u32 = 32;
6638pub const TESSELLATION_MAX_LEVEL: f32 = 64.0;
6639pub const GEOMETRY_SHADER_MAX_VERTICES_OUT: u32 = 256;
6640pub const GEOMETRY_SHADER_MAX_INVOCATIONS: u32 = 32;
6641pub const WIREFRAME_LINE_WIDTH_MAX: f32 = 8.0;
6642pub const SHADER_FALLBACK_MAX_DEPTH: u32 = 3;
6643
6644pub fn geometry_primitive_name(p: &GeometryPrimitive) -> &'static str {
6645    match p { GeometryPrimitive::Points => "points", GeometryPrimitive::Lines => "lines", GeometryPrimitive::LinesAdjacency => "lines_adjacency", GeometryPrimitive::Triangles => "triangles", GeometryPrimitive::TrianglesAdjacency => "triangles_adjacency", GeometryPrimitive::LineStrip => "line_strip", GeometryPrimitive::TriangleStrip => "triangle_strip" }
6646}
6647
6648pub fn tess_spacing_name(s: &TessSpacing) -> &'static str {
6649    match s { TessSpacing::Equal => "equal_spacing", TessSpacing::FractionalEven => "fractional_even_spacing", TessSpacing::FractionalOdd => "fractional_odd_spacing" }
6650}
6651
6652pub fn glsl_layout_tess_ctrl(patch_size: u32) -> String { format!("layout(vertices = {}) out;", patch_size) }
6653pub fn glsl_layout_tess_eval(primitive: &TessPrimitive, spacing: &TessSpacing, winding: &TessWinding) -> String {
6654    format!("layout({}, {}, {}) in;", match primitive { TessPrimitive::Triangles => "triangles", TessPrimitive::Quads => "quads", TessPrimitive::Isolines => "isolines" }, tess_spacing_name(spacing), match winding { TessWinding::Ccw => "ccw", TessWinding::Cw => "cw" })
6655}
6656pub fn glsl_layout_geometry(input: &GeometryPrimitive, output: &GeometryPrimitive, max_verts: u32) -> String {
6657    format!("layout({}) in;\nlayout({}, max_vertices = {}) out;", geometry_primitive_name(input), geometry_primitive_name(output), max_verts)
6658}
6659
6660pub fn shader_complete_feature_list() -> Vec<String> {
6661    let mut features = shader_compiler_module_list().iter().map(|s| s.to_string()).collect::<Vec<_>>();
6662    features.extend(all_post_process_effect_names().iter().map(|s| s.to_string()));
6663    features.extend(all_glsl_keywords().iter().map(|s| s.to_string()));
6664    features
6665}
6666pub fn shader_feature_count() -> usize { shader_complete_feature_list().len() }
6667
6668
6669// ── Shader Compilation Summary ────────────────────────────────────────────────
6670
6671#[derive(Clone, Debug, Default)]
6672pub struct ShaderSystemState {
6673    pub initialized: bool,
6674    pub programs_loaded: u32,
6675    pub materials_loaded: u32,
6676    pub textures_loaded: u32,
6677    pub compile_errors: u32,
6678    pub compile_warnings: u32,
6679    pub last_error: Option<String>,
6680    pub uptime_frames: u64,
6681    pub total_draw_calls: u64,
6682    pub total_triangles: u64,
6683}
6684
6685impl ShaderSystemState {
6686    pub fn new() -> Self { Self { initialized: false, ..Default::default() } }
6687    pub fn initialize(&mut self) { self.initialized = true; }
6688    pub fn record_draw(&mut self, triangles: u64) { self.total_draw_calls += 1; self.total_triangles += triangles; }
6689    pub fn record_compile_error(&mut self, msg: impl Into<String>) { self.compile_errors += 1; self.last_error = Some(msg.into()); }
6690    pub fn record_compile_warning(&mut self) { self.compile_warnings += 1; }
6691    pub fn tick_frame(&mut self) { self.uptime_frames += 1; }
6692    pub fn is_healthy(&self) -> bool { self.initialized && self.compile_errors == 0 }
6693    pub fn avg_tris_per_call(&self) -> f64 { if self.total_draw_calls == 0 { 0.0 } else { self.total_triangles as f64 / self.total_draw_calls as f64 } }
6694    pub fn summary(&self) -> String {
6695        format!("ShaderSystem: init={}, programs={}, materials={}, textures={}, errors={}, warnings={}", self.initialized, self.programs_loaded, self.materials_loaded, self.textures_loaded, self.compile_errors, self.compile_warnings)
6696    }
6697}
6698
6699// ── Built-in PBR Vertex Shader ────────────────────────────────────────────────
6700
6701pub fn builtin_pbr_vertex_shader() -> &'static str {
6702    r#"#version 450
6703layout(location=0) in vec3 a_position;
6704layout(location=1) in vec3 a_normal;
6705layout(location=2) in vec2 a_uv;
6706layout(location=3) in vec4 a_tangent;
6707layout(std140, binding=0) uniform PerFrame { mat4 view; mat4 proj; mat4 view_proj; vec4 camera_pos; float time; float delta_time; float near_plane; float far_plane; };
6708layout(std140, binding=1) uniform PerObject { mat4 model; mat4 mvp; mat4 normal_matrix; };
6709layout(location=0) out vec3 v_world_pos;
6710layout(location=1) out vec3 v_normal;
6711layout(location=2) out vec2 v_uv;
6712layout(location=3) out vec3 v_tangent;
6713layout(location=4) out vec3 v_bitangent;
6714void main() {
6715    vec4 world_pos = model * vec4(a_position, 1.0);
6716    v_world_pos = world_pos.xyz;
6717    v_normal = normalize((normal_matrix * vec4(a_normal, 0.0)).xyz);
6718    v_tangent = normalize((normal_matrix * vec4(a_tangent.xyz, 0.0)).xyz);
6719    v_bitangent = cross(v_normal, v_tangent) * a_tangent.w;
6720    v_uv = a_uv;
6721    gl_Position = view_proj * world_pos;
6722}"#
6723}
6724
6725pub fn builtin_pbr_fragment_shader() -> &'static str {
6726    r#"#version 450
6727layout(location=0) in vec3 v_world_pos;
6728layout(location=1) in vec3 v_normal;
6729layout(location=2) in vec2 v_uv;
6730layout(location=3) in vec3 v_tangent;
6731layout(location=4) in vec3 v_bitangent;
6732layout(std140, binding=2) uniform PerMaterial { vec4 albedo_color; vec4 emissive_color; float roughness; float metalness; float normal_scale; float occlusion_strength; float emissive_intensity; float opacity; };
6733layout(binding=0) uniform sampler2D u_albedo;
6734layout(binding=1) uniform sampler2D u_normal_map;
6735layout(binding=2) uniform sampler2D u_orm;
6736layout(location=0) out vec4 out_color;
6737const float PI = 3.14159265;
6738void main() {
6739    vec3 albedo = texture(u_albedo, v_uv).rgb * albedo_color.rgb;
6740    vec3 orm = texture(u_orm, v_uv).rgb;
6741    float ao = orm.r; float roughness_v = orm.g * roughness; float metalness_v = orm.b * metalness;
6742    vec3 N = normalize(v_normal);
6743    vec3 emit = emissive_color.rgb * emissive_intensity;
6744    out_color = vec4(albedo + emit, opacity);
6745}"#
6746}
6747
6748pub fn builtin_unlit_vertex_shader() -> &'static str {
6749    r#"#version 450
6750layout(location=0) in vec3 a_position;
6751layout(location=2) in vec2 a_uv;
6752layout(std140, binding=1) uniform PerObject { mat4 mvp; mat4 model; mat4 normal_matrix; };
6753layout(location=0) out vec2 v_uv;
6754void main() { v_uv = a_uv; gl_Position = mvp * vec4(a_position, 1.0); }"#
6755}
6756
6757pub fn builtin_unlit_fragment_shader() -> &'static str {
6758    r#"#version 450
6759layout(location=0) in vec2 v_uv;
6760layout(binding=0) uniform sampler2D u_texture;
6761layout(std140, binding=2) uniform PerMaterial { vec4 albedo_color; vec4 emissive_color; float roughness; float metalness; float normal_scale; float occlusion_strength; float emissive_intensity; float opacity; };
6762layout(location=0) out vec4 out_color;
6763void main() { out_color = texture(u_texture, v_uv) * albedo_color; }"#
6764}
6765
6766pub const SHADER_SYSTEM_STATE_VERSION: u32 = 1;
6767pub const BUILTIN_PBR_SHADER_STAGES: usize = 2;
6768pub const BUILTIN_UNLIT_SHADER_STAGES: usize = 2;
6769pub const BUILTIN_SHADER_COUNT: usize = 4;
6770
6771pub fn builtin_shader_names() -> &'static [&'static str] { &["pbr_vertex", "pbr_fragment", "unlit_vertex", "unlit_fragment"] }
6772pub fn get_builtin_shader(name: &str) -> Option<&'static str> {
6773    match name {
6774        "pbr_vertex" => Some(builtin_pbr_vertex_shader()),
6775        "pbr_fragment" => Some(builtin_pbr_fragment_shader()),
6776        "unlit_vertex" => Some(builtin_unlit_vertex_shader()),
6777        "unlit_fragment" => Some(builtin_unlit_fragment_shader()),
6778        _ => None,
6779    }
6780}
6781pub fn shader_editor_complete_build_info() -> String {
6782    format!("ShaderCompilerEditor Complete Build — {} builtin shaders, {} intrinsic docs, {} module total", BUILTIN_SHADER_COUNT, SHADER_GLSL_INTRINSIC_DOC_COUNT, shader_module_count())
6783}
6784
6785
6786// ── Shader Utility Functions ──────────────────────────────────────────────────
6787
6788pub fn blend_factor_to_str(f: &BlendFactor) -> &'static str {
6789    match f {
6790        BlendFactor::Zero => "Zero",
6791        BlendFactor::One => "One",
6792        BlendFactor::SrcAlpha => "SrcAlpha",
6793        BlendFactor::OneMinusSrcAlpha => "OneMinusSrcAlpha",
6794        BlendFactor::DstAlpha => "DstAlpha",
6795        BlendFactor::OneMinusDstAlpha => "OneMinusDstAlpha",
6796        BlendFactor::SrcColor => "SrcColor",
6797        BlendFactor::OneMinusSrcColor => "OneMinusSrcColor",
6798        BlendFactor::ConstantAlpha => "ConstantAlpha",
6799    }
6800}
6801
6802pub fn compare_func_to_str(f: &CompareFunc) -> &'static str {
6803    match f {
6804        CompareFunc::Never => "Never",
6805        CompareFunc::Less => "Less",
6806        CompareFunc::Equal => "Equal",
6807        CompareFunc::LessEqual => "LessEqual",
6808        CompareFunc::Greater => "Greater",
6809        CompareFunc::NotEqual => "NotEqual",
6810        CompareFunc::GreaterEqual => "GreaterEqual",
6811        CompareFunc::Always => "Always",
6812    }
6813}
6814
6815pub fn cull_mode_to_str(m: &CullMode) -> &'static str {
6816    match m {
6817        CullMode::None => "None",
6818        CullMode::Front => "Front",
6819        CullMode::Back => "Back",
6820        CullMode::FrontAndBack => "FrontAndBack",
6821    }
6822}
6823
6824pub fn polygon_mode_to_str(m: &PolygonMode) -> &'static str {
6825    match m { PolygonMode::Fill => "Fill", PolygonMode::Line => "Line", PolygonMode::Point => "Point" }
6826}
6827
6828pub fn attachment_format_is_depth(f: &AttachmentFormat) -> bool {
6829    matches!(f, AttachmentFormat::Depth16 | AttachmentFormat::Depth24 | AttachmentFormat::Depth32F | AttachmentFormat::Depth24Stencil8)
6830}
6831
6832pub fn attachment_format_channel_count(f: &AttachmentFormat) -> u32 {
6833    match f {
6834        AttachmentFormat::Rgba8 | AttachmentFormat::Rgba16F | AttachmentFormat::Rgba32F => 4,
6835        AttachmentFormat::Rg8 | AttachmentFormat::Rg16F => 2,
6836        AttachmentFormat::R8 | AttachmentFormat::R16F | AttachmentFormat::R32F => 1,
6837        AttachmentFormat::Depth16 | AttachmentFormat::Depth24 | AttachmentFormat::Depth32F => 1,
6838        AttachmentFormat::Depth24Stencil8 => 2,
6839    }
6840}
6841
6842pub fn buffer_usage_to_str(u: &BufferUsage) -> &'static str {
6843    match u {
6844        BufferUsage::Vertex => "Vertex",
6845        BufferUsage::Index => "Index",
6846        BufferUsage::Uniform => "Uniform",
6847        BufferUsage::Storage => "Storage",
6848        BufferUsage::Indirect => "Indirect",
6849        BufferUsage::Staging => "Staging",
6850    }
6851}
6852
6853pub fn sampler_type_to_str(t: &SamplerType) -> &'static str {
6854    match t {
6855        SamplerType::Linear => "Linear",
6856        SamplerType::Nearest => "Nearest",
6857        SamplerType::LinearMipmap => "LinearMipmap",
6858        SamplerType::Trilinear => "Trilinear",
6859        SamplerType::Anisotropic => "Anisotropic",
6860    }
6861}
6862
6863pub fn render_pass_type_to_str(t: &RenderPassType) -> &'static str {
6864    match t {
6865        RenderPassType::Opaque => "Opaque",
6866        RenderPassType::Transparent => "Transparent",
6867        RenderPassType::Shadow => "Shadow",
6868        RenderPassType::PostProcess => "PostProcess",
6869        RenderPassType::Ui => "UI",
6870        RenderPassType::Compute => "Compute",
6871        RenderPassType::Custom => "Custom",
6872    }
6873}
6874
6875pub fn post_effect_type_to_str(t: &PostEffectType) -> &'static str {
6876    match t {
6877        PostEffectType::Bloom => "Bloom",
6878        PostEffectType::Tonemap => "Tonemap",
6879        PostEffectType::Vignette => "Vignette",
6880        PostEffectType::ChromaticAberration => "ChromaticAberration",
6881        PostEffectType::DepthOfField => "DepthOfField",
6882        PostEffectType::MotionBlur => "MotionBlur",
6883        PostEffectType::Ssao => "SSAO",
6884        PostEffectType::Fxaa => "FXAA",
6885        PostEffectType::Custom => "Custom",
6886    }
6887}
6888
6889pub fn tone_mapping_mode_to_str(m: &ToneMappingMode) -> &'static str {
6890    match m {
6891        ToneMappingMode::None => "None",
6892        ToneMappingMode::Reinhard => "Reinhard",
6893        ToneMappingMode::Aces => "ACES",
6894        ToneMappingMode::Filmic => "Filmic",
6895        ToneMappingMode::Uncharted2 => "Uncharted2",
6896        ToneMappingMode::Custom => "Custom",
6897    }
6898}
6899
6900pub fn lighting_model_type_to_str(t: &LightingModelType) -> &'static str {
6901    match t {
6902        LightingModelType::Pbr => "PBR",
6903        LightingModelType::Phong => "Phong",
6904        LightingModelType::BlinnPhong => "BlinnPhong",
6905        LightingModelType::Lambert => "Lambert",
6906        LightingModelType::Toon => "Toon",
6907        LightingModelType::Unlit => "Unlit",
6908        LightingModelType::Custom(_) => "Custom",
6909    }
6910}
6911
6912pub fn descriptor_type_to_str(t: &DescriptorType) -> &'static str {
6913    match t {
6914        DescriptorType::UniformBuffer => "UniformBuffer",
6915        DescriptorType::StorageBuffer => "StorageBuffer",
6916        DescriptorType::Sampler => "Sampler",
6917        DescriptorType::SampledImage => "SampledImage",
6918        DescriptorType::StorageImage => "StorageImage",
6919        DescriptorType::CombinedImageSampler => "CombinedImageSampler",
6920        DescriptorType::InputAttachment => "InputAttachment",
6921    }
6922}
6923
6924pub fn render_graph_node_type_to_str(t: &RenderGraphNodeType) -> &'static str {
6925    match t {
6926        RenderGraphNodeType::Pass => "Pass",
6927        RenderGraphNodeType::Blit => "Blit",
6928        RenderGraphNodeType::Compute => "Compute",
6929        RenderGraphNodeType::Present => "Present",
6930        RenderGraphNodeType::Upload => "Upload",
6931        RenderGraphNodeType::Barrier => "Barrier",
6932        RenderGraphNodeType::Custom(_) => "Custom",
6933    }
6934}
6935
6936pub fn bloom_mode_to_str(m: &BloomMode) -> &'static str {
6937    match m {
6938        BloomMode::Classic => "Classic",
6939        BloomMode::Kawase => "Kawase",
6940        BloomMode::Dual => "Dual",
6941        BloomMode::Convolution => "Convolution",
6942    }
6943}
6944
6945pub fn ssao_quality_to_str(q: &SsaoQuality) -> &'static str {
6946    match q {
6947        SsaoQuality::Low => "Low",
6948        SsaoQuality::Medium => "Medium",
6949        SsaoQuality::High => "High",
6950        SsaoQuality::Ultra => "Ultra",
6951    }
6952}
6953
6954pub fn anti_aliasing_mode_to_str(m: &AntiAliasingMode) -> &'static str {
6955    match m {
6956        AntiAliasingMode::None => "None",
6957        AntiAliasingMode::Msaa => "MSAA",
6958        AntiAliasingMode::Fxaa => "FXAA",
6959        AntiAliasingMode::Taa => "TAA",
6960        AntiAliasingMode::Smaa => "SMAA",
6961        AntiAliasingMode::Dlss => "DLSS",
6962    }
6963}
6964
6965pub fn optimization_level_to_str(o: &OptimizationLevel) -> &'static str {
6966    match o {
6967        OptimizationLevel::None => "None",
6968        OptimizationLevel::Low => "Low",
6969        OptimizationLevel::Medium => "Medium",
6970        OptimizationLevel::High => "High",
6971        OptimizationLevel::Aggressive => "Aggressive",
6972    }
6973}
6974
6975pub fn code_gen_target_to_str(t: &CodeGenTarget) -> &'static str {
6976    match t {
6977        CodeGenTarget::Glsl450 => "GLSL 4.50",
6978        CodeGenTarget::Glsl300Es => "GLSL 3.00 ES",
6979        CodeGenTarget::Wgsl => "WGSL",
6980        CodeGenTarget::Hlsl50 => "HLSL 5.0",
6981        CodeGenTarget::Msl20 => "Metal SL 2.0",
6982    }
6983}
6984
6985pub fn shader_export_format_to_str(f: &ShaderExportFormat) -> &'static str {
6986    match f {
6987        ShaderExportFormat::Glsl => "GLSL",
6988        ShaderExportFormat::SpirV => "SPIR-V",
6989        ShaderExportFormat::Wgsl => "WGSL",
6990        ShaderExportFormat::Json => "JSON",
6991        ShaderExportFormat::Binary => "Binary",
6992    }
6993}
6994
6995pub fn stencil_op_to_str(op: &StencilOp) -> &'static str {
6996    match op {
6997        StencilOp::Keep => "Keep", StencilOp::Zero => "Zero", StencilOp::Replace => "Replace",
6998        StencilOp::IncrClamp => "IncrClamp", StencilOp::DecrClamp => "DecrClamp",
6999        StencilOp::Invert => "Invert", StencilOp::IncrWrap => "IncrWrap", StencilOp::DecrWrap => "DecrWrap",
7000    }
7001}
7002
7003pub fn tess_primitive_to_str(p: &TessPrimitive) -> &'static str {
7004    match p { TessPrimitive::Triangles => "triangles", TessPrimitive::Quads => "quads", TessPrimitive::Isolines => "isolines" }
7005}
7006
7007pub fn tess_winding_to_str(w: &TessWinding) -> &'static str {
7008    match w { TessWinding::Ccw => "ccw", TessWinding::Cw => "cw" }
7009}
7010
7011// ── Shader System Credits & Versioning ───────────────────────────────────────
7012
7013pub const SHADER_COMPILER_FULL_VERSION: &str = "ShaderCompilerEditor v2.0.0-proof-engine";
7014pub const SHADER_COMPILER_BUILD_DATE: &str = "2026-03-29";
7015pub const SHADER_COMPILER_AUTHOR: &str = "proof-engine rendering team";
7016pub const SHADER_MAX_MATERIALS_PER_PASS: usize = 256;
7017pub const SHADER_MAX_LIGHTS: usize = 512;
7018pub const SHADER_MAX_SHADOW_CASCADES: usize = 4;
7019pub const SHADER_MAX_REFLECTION_PROBES: usize = 64;
7020pub const SHADER_COMPUTE_MAX_WORKGROUP_SIZE: u32 = 1024;
7021pub const SHADER_MAX_PUSH_CONSTANT_BYTES: u32 = 128;
7022pub const SHADER_MAX_DESCRIPTOR_SETS: u32 = 4;
7023pub const SHADER_MAX_BINDINGS_PER_SET: u32 = 16;
7024
7025pub fn shader_compiler_credits() -> &'static str {
7026    "ShaderCompilerEditor — Built for proof-engine. Supports GLSL, HLSL, WGSL, SPIR-V, and Metal SL."
7027}
7028
7029pub fn shader_pipeline_summary(manager: &ShaderManager) -> String {
7030    format!(
7031        "Programs: {} | Errors: {} | Quality: {} | Frames: {}",
7032        manager.program_count(),
7033        manager.error_count(),
7034        manager.quality_preset,
7035        manager.frame_executor.frame_number(),
7036    )
7037}
7038