Skip to main content

oxilean_codegen/glsl_backend/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use std::collections::{HashMap, HashSet, VecDeque};
6
7/// std140 / std430 layout for UBOs.
8#[allow(dead_code)]
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum GlslBlockLayout {
11    Std140,
12    Std430,
13    Shared,
14}
15impl GlslBlockLayout {
16    /// Layout qualifier string.
17    pub fn qualifier_str(self) -> &'static str {
18        match self {
19            GlslBlockLayout::Std140 => "std140",
20            GlslBlockLayout::Std430 => "std430",
21            GlslBlockLayout::Shared => "shared",
22        }
23    }
24}
25/// Statistics for GLSLExt passes.
26#[allow(dead_code)]
27#[derive(Debug, Clone, Default)]
28pub struct GLSLExtPassStats {
29    pub iterations: usize,
30    pub changed: bool,
31    pub nodes_visited: usize,
32    pub nodes_modified: usize,
33    pub time_ms: u64,
34    pub memory_bytes: usize,
35    pub errors: usize,
36}
37impl GLSLExtPassStats {
38    #[allow(dead_code)]
39    pub fn new() -> Self {
40        Self::default()
41    }
42    #[allow(dead_code)]
43    pub fn visit(&mut self) {
44        self.nodes_visited += 1;
45    }
46    #[allow(dead_code)]
47    pub fn modify(&mut self) {
48        self.nodes_modified += 1;
49        self.changed = true;
50    }
51    #[allow(dead_code)]
52    pub fn iterate(&mut self) {
53        self.iterations += 1;
54    }
55    #[allow(dead_code)]
56    pub fn error(&mut self) {
57        self.errors += 1;
58    }
59    #[allow(dead_code)]
60    pub fn efficiency(&self) -> f64 {
61        if self.nodes_visited == 0 {
62            0.0
63        } else {
64            self.nodes_modified as f64 / self.nodes_visited as f64
65        }
66    }
67    #[allow(dead_code)]
68    pub fn merge(&mut self, o: &GLSLExtPassStats) {
69        self.iterations += o.iterations;
70        self.changed |= o.changed;
71        self.nodes_visited += o.nodes_visited;
72        self.nodes_modified += o.nodes_modified;
73        self.time_ms += o.time_ms;
74        self.memory_bytes = self.memory_bytes.max(o.memory_bytes);
75        self.errors += o.errors;
76    }
77}
78#[allow(dead_code)]
79pub struct GLSLConstantFoldingHelper;
80impl GLSLConstantFoldingHelper {
81    #[allow(dead_code)]
82    pub fn fold_add_i64(a: i64, b: i64) -> Option<i64> {
83        a.checked_add(b)
84    }
85    #[allow(dead_code)]
86    pub fn fold_sub_i64(a: i64, b: i64) -> Option<i64> {
87        a.checked_sub(b)
88    }
89    #[allow(dead_code)]
90    pub fn fold_mul_i64(a: i64, b: i64) -> Option<i64> {
91        a.checked_mul(b)
92    }
93    #[allow(dead_code)]
94    pub fn fold_div_i64(a: i64, b: i64) -> Option<i64> {
95        if b == 0 {
96            None
97        } else {
98            a.checked_div(b)
99        }
100    }
101    #[allow(dead_code)]
102    pub fn fold_add_f64(a: f64, b: f64) -> f64 {
103        a + b
104    }
105    #[allow(dead_code)]
106    pub fn fold_mul_f64(a: f64, b: f64) -> f64 {
107        a * b
108    }
109    #[allow(dead_code)]
110    pub fn fold_neg_i64(a: i64) -> Option<i64> {
111        a.checked_neg()
112    }
113    #[allow(dead_code)]
114    pub fn fold_not_bool(a: bool) -> bool {
115        !a
116    }
117    #[allow(dead_code)]
118    pub fn fold_and_bool(a: bool, b: bool) -> bool {
119        a && b
120    }
121    #[allow(dead_code)]
122    pub fn fold_or_bool(a: bool, b: bool) -> bool {
123        a || b
124    }
125    #[allow(dead_code)]
126    pub fn fold_shl_i64(a: i64, b: u32) -> Option<i64> {
127        a.checked_shl(b)
128    }
129    #[allow(dead_code)]
130    pub fn fold_shr_i64(a: i64, b: u32) -> Option<i64> {
131        a.checked_shr(b)
132    }
133    #[allow(dead_code)]
134    pub fn fold_rem_i64(a: i64, b: i64) -> Option<i64> {
135        if b == 0 {
136            None
137        } else {
138            Some(a % b)
139        }
140    }
141    #[allow(dead_code)]
142    pub fn fold_bitand_i64(a: i64, b: i64) -> i64 {
143        a & b
144    }
145    #[allow(dead_code)]
146    pub fn fold_bitor_i64(a: i64, b: i64) -> i64 {
147        a | b
148    }
149    #[allow(dead_code)]
150    pub fn fold_bitxor_i64(a: i64, b: i64) -> i64 {
151        a ^ b
152    }
153    #[allow(dead_code)]
154    pub fn fold_bitnot_i64(a: i64) -> i64 {
155        !a
156    }
157}
158/// A member of a GLSL uniform block.
159#[allow(dead_code)]
160#[derive(Debug, Clone)]
161pub struct GlslBlockMember {
162    pub name: String,
163    pub ty: GLSLType,
164    pub array_size: Option<usize>,
165}
166#[allow(dead_code)]
167impl GlslBlockMember {
168    pub fn new(name: impl Into<String>, ty: GLSLType) -> Self {
169        GlslBlockMember {
170            name: name.into(),
171            ty,
172            array_size: None,
173        }
174    }
175    pub fn array(name: impl Into<String>, ty: GLSLType, size: usize) -> Self {
176        GlslBlockMember {
177            name: name.into(),
178            ty,
179            array_size: Some(size),
180        }
181    }
182}
183#[allow(dead_code)]
184pub struct GLSLPassRegistry {
185    pub(super) configs: Vec<GLSLPassConfig>,
186    pub(super) stats: std::collections::HashMap<String, GLSLPassStats>,
187}
188impl GLSLPassRegistry {
189    #[allow(dead_code)]
190    pub fn new() -> Self {
191        GLSLPassRegistry {
192            configs: Vec::new(),
193            stats: std::collections::HashMap::new(),
194        }
195    }
196    #[allow(dead_code)]
197    pub fn register(&mut self, config: GLSLPassConfig) {
198        self.stats
199            .insert(config.pass_name.clone(), GLSLPassStats::new());
200        self.configs.push(config);
201    }
202    #[allow(dead_code)]
203    pub fn enabled_passes(&self) -> Vec<&GLSLPassConfig> {
204        self.configs.iter().filter(|c| c.enabled).collect()
205    }
206    #[allow(dead_code)]
207    pub fn get_stats(&self, name: &str) -> Option<&GLSLPassStats> {
208        self.stats.get(name)
209    }
210    #[allow(dead_code)]
211    pub fn total_passes(&self) -> usize {
212        self.configs.len()
213    }
214    #[allow(dead_code)]
215    pub fn enabled_count(&self) -> usize {
216        self.enabled_passes().len()
217    }
218    #[allow(dead_code)]
219    pub fn update_stats(&mut self, name: &str, changes: u64, time_ms: u64, iter: u32) {
220        if let Some(stats) = self.stats.get_mut(name) {
221            stats.record_run(changes, time_ms, iter);
222        }
223    }
224}
225#[allow(dead_code)]
226#[derive(Debug, Clone)]
227pub struct GLSLDominatorTree {
228    pub idom: Vec<Option<u32>>,
229    pub dom_children: Vec<Vec<u32>>,
230    pub dom_depth: Vec<u32>,
231}
232impl GLSLDominatorTree {
233    #[allow(dead_code)]
234    pub fn new(size: usize) -> Self {
235        GLSLDominatorTree {
236            idom: vec![None; size],
237            dom_children: vec![Vec::new(); size],
238            dom_depth: vec![0; size],
239        }
240    }
241    #[allow(dead_code)]
242    pub fn set_idom(&mut self, node: usize, idom: u32) {
243        self.idom[node] = Some(idom);
244    }
245    #[allow(dead_code)]
246    pub fn dominates(&self, a: usize, b: usize) -> bool {
247        if a == b {
248            return true;
249        }
250        let mut cur = b;
251        loop {
252            match self.idom[cur] {
253                Some(parent) if parent as usize == a => return true,
254                Some(parent) if parent as usize == cur => return false,
255                Some(parent) => cur = parent as usize,
256                None => return false,
257            }
258        }
259    }
260    #[allow(dead_code)]
261    pub fn depth(&self, node: usize) -> u32 {
262        self.dom_depth.get(node).copied().unwrap_or(0)
263    }
264}
265/// A single field inside a GLSL `struct` definition.
266#[derive(Debug, Clone)]
267pub struct GLSLStructField {
268    /// Field name.
269    pub name: String,
270    /// Field type.
271    pub ty: GLSLType,
272}
273impl GLSLStructField {
274    /// Create a new struct field.
275    pub fn new(name: impl Into<String>, ty: GLSLType) -> Self {
276        GLSLStructField {
277            name: name.into(),
278            ty,
279        }
280    }
281}
282/// GLSL type system.
283#[derive(Debug, Clone, PartialEq, Eq, Hash)]
284pub enum GLSLType {
285    /// `void`
286    Void,
287    /// `bool`
288    Bool,
289    /// `int`
290    Int,
291    /// `uint`
292    Uint,
293    /// `float`
294    Float,
295    /// `double`
296    Double,
297    /// `vec2`
298    Vec2,
299    /// `vec3`
300    Vec3,
301    /// `vec4`
302    Vec4,
303    /// `ivec2`
304    IVec2,
305    /// `ivec3`
306    IVec3,
307    /// `ivec4`
308    IVec4,
309    /// `uvec2`
310    UVec2,
311    /// `uvec3`
312    UVec3,
313    /// `uvec4`
314    UVec4,
315    /// `bvec2`
316    BVec2,
317    /// `bvec3`
318    BVec3,
319    /// `bvec4`
320    BVec4,
321    /// `mat2`
322    Mat2,
323    /// `mat3`
324    Mat3,
325    /// `mat4`
326    Mat4,
327    /// `mat2x3`
328    Mat2x3,
329    /// `mat2x4`
330    Mat2x4,
331    /// `mat3x2`
332    Mat3x2,
333    /// `mat3x4`
334    Mat3x4,
335    /// `mat4x2`
336    Mat4x2,
337    /// `mat4x3`
338    Mat4x3,
339    /// `dmat4`
340    DMat4,
341    /// `sampler2D`
342    Sampler2D,
343    /// `sampler3D`
344    Sampler3D,
345    /// `samplerCube`
346    SamplerCube,
347    /// `sampler2DArray`
348    Sampler2DArray,
349    /// `sampler2DShadow`
350    Sampler2DShadow,
351    /// `isampler2D`
352    ISampler2D,
353    /// `usampler2D`
354    USampler2D,
355    /// `image2D`
356    Image2D,
357    /// `uimage2D`
358    UImage2D,
359    /// A named struct type.
360    Struct(String),
361    /// A fixed-length array of another type.
362    Array(Box<GLSLType>, u32),
363}
364impl GLSLType {
365    /// Return the GLSL keyword for this type.
366    pub fn keyword(&self) -> String {
367        match self {
368            GLSLType::Void => "void".into(),
369            GLSLType::Bool => "bool".into(),
370            GLSLType::Int => "int".into(),
371            GLSLType::Uint => "uint".into(),
372            GLSLType::Float => "float".into(),
373            GLSLType::Double => "double".into(),
374            GLSLType::Vec2 => "vec2".into(),
375            GLSLType::Vec3 => "vec3".into(),
376            GLSLType::Vec4 => "vec4".into(),
377            GLSLType::IVec2 => "ivec2".into(),
378            GLSLType::IVec3 => "ivec3".into(),
379            GLSLType::IVec4 => "ivec4".into(),
380            GLSLType::UVec2 => "uvec2".into(),
381            GLSLType::UVec3 => "uvec3".into(),
382            GLSLType::UVec4 => "uvec4".into(),
383            GLSLType::BVec2 => "bvec2".into(),
384            GLSLType::BVec3 => "bvec3".into(),
385            GLSLType::BVec4 => "bvec4".into(),
386            GLSLType::Mat2 => "mat2".into(),
387            GLSLType::Mat3 => "mat3".into(),
388            GLSLType::Mat4 => "mat4".into(),
389            GLSLType::Mat2x3 => "mat2x3".into(),
390            GLSLType::Mat2x4 => "mat2x4".into(),
391            GLSLType::Mat3x2 => "mat3x2".into(),
392            GLSLType::Mat3x4 => "mat3x4".into(),
393            GLSLType::Mat4x2 => "mat4x2".into(),
394            GLSLType::Mat4x3 => "mat4x3".into(),
395            GLSLType::DMat4 => "dmat4".into(),
396            GLSLType::Sampler2D => "sampler2D".into(),
397            GLSLType::Sampler3D => "sampler3D".into(),
398            GLSLType::SamplerCube => "samplerCube".into(),
399            GLSLType::Sampler2DArray => "sampler2DArray".into(),
400            GLSLType::Sampler2DShadow => "sampler2DShadow".into(),
401            GLSLType::ISampler2D => "isampler2D".into(),
402            GLSLType::USampler2D => "usampler2D".into(),
403            GLSLType::Image2D => "image2D".into(),
404            GLSLType::UImage2D => "uimage2D".into(),
405            GLSLType::Struct(name) => name.clone(),
406            GLSLType::Array(elem, _) => elem.keyword(),
407        }
408    }
409    /// Return the component (column) count for vector/matrix types, or 1.
410    pub fn component_count(&self) -> u32 {
411        match self {
412            GLSLType::Vec2 | GLSLType::IVec2 | GLSLType::UVec2 | GLSLType::BVec2 => 2,
413            GLSLType::Vec3 | GLSLType::IVec3 | GLSLType::UVec3 | GLSLType::BVec3 => 3,
414            GLSLType::Vec4 | GLSLType::IVec4 | GLSLType::UVec4 | GLSLType::BVec4 => 4,
415            GLSLType::Mat2 | GLSLType::Mat2x3 | GLSLType::Mat2x4 => 2,
416            GLSLType::Mat3 | GLSLType::Mat3x2 | GLSLType::Mat3x4 => 3,
417            GLSLType::Mat4 | GLSLType::Mat4x2 | GLSLType::Mat4x3 | GLSLType::DMat4 => 4,
418            _ => 1,
419        }
420    }
421    /// Return true if the type is a sampler or image type.
422    pub fn is_opaque(&self) -> bool {
423        matches!(
424            self,
425            GLSLType::Sampler2D
426                | GLSLType::Sampler3D
427                | GLSLType::SamplerCube
428                | GLSLType::Sampler2DArray
429                | GLSLType::Sampler2DShadow
430                | GLSLType::ISampler2D
431                | GLSLType::USampler2D
432                | GLSLType::Image2D
433                | GLSLType::UImage2D
434        )
435    }
436    /// Return true if the type is a floating-point scalar or vector.
437    pub fn is_float_like(&self) -> bool {
438        matches!(
439            self,
440            GLSLType::Float | GLSLType::Double | GLSLType::Vec2 | GLSLType::Vec3 | GLSLType::Vec4
441        )
442    }
443}
444/// A simple type-inference context for GLSL expression analysis.
445#[allow(dead_code)]
446pub struct GlslTypeInference {
447    pub(super) bindings: std::collections::HashMap<String, GLSLType>,
448    pub(super) version: GLSLVersion,
449}
450#[allow(dead_code)]
451impl GlslTypeInference {
452    /// Create a new inference context for the given GLSL version.
453    pub fn new(version: GLSLVersion) -> Self {
454        GlslTypeInference {
455            bindings: std::collections::HashMap::new(),
456            version,
457        }
458    }
459    /// Bind a name to a GLSL type.
460    pub fn bind(&mut self, name: impl Into<String>, ty: GLSLType) {
461        self.bindings.insert(name.into(), ty);
462    }
463    /// Look up the type of a name.
464    pub fn lookup(&self, name: &str) -> Option<&GLSLType> {
465        self.bindings.get(name)
466    }
467    /// Component count of a vector/matrix type.
468    pub fn num_components(ty: &GLSLType) -> usize {
469        match ty {
470            GLSLType::Vec2 | GLSLType::IVec2 | GLSLType::BVec2 => 2,
471            GLSLType::Vec3 | GLSLType::IVec3 | GLSLType::BVec3 => 3,
472            GLSLType::Vec4 | GLSLType::IVec4 | GLSLType::BVec4 => 4,
473            GLSLType::Mat2 => 4,
474            GLSLType::Mat3 => 9,
475            GLSLType::Mat4 => 16,
476            _ => 1,
477        }
478    }
479    /// Whether a type is available in this version.
480    pub fn type_available(&self, ty: &GLSLType) -> bool {
481        match ty {
482            GLSLType::Uint => self.version.supports_uint(),
483            _ => true,
484        }
485    }
486    /// Return the version.
487    pub fn version(&self) -> GLSLVersion {
488        self.version
489    }
490    /// Number of active bindings.
491    pub fn num_bindings(&self) -> usize {
492        self.bindings.len()
493    }
494}
495#[allow(dead_code)]
496#[derive(Debug, Clone, PartialEq)]
497pub enum GLSLPassPhase {
498    Analysis,
499    Transformation,
500    Verification,
501    Cleanup,
502}
503impl GLSLPassPhase {
504    #[allow(dead_code)]
505    pub fn name(&self) -> &str {
506        match self {
507            GLSLPassPhase::Analysis => "analysis",
508            GLSLPassPhase::Transformation => "transformation",
509            GLSLPassPhase::Verification => "verification",
510            GLSLPassPhase::Cleanup => "cleanup",
511        }
512    }
513    #[allow(dead_code)]
514    pub fn is_modifying(&self) -> bool {
515        matches!(self, GLSLPassPhase::Transformation | GLSLPassPhase::Cleanup)
516    }
517}
518/// Worklist for GLSLExt.
519#[allow(dead_code)]
520#[derive(Debug, Clone)]
521pub struct GLSLExtWorklist {
522    pub(super) items: std::collections::VecDeque<usize>,
523    pub(super) present: Vec<bool>,
524}
525impl GLSLExtWorklist {
526    #[allow(dead_code)]
527    pub fn new(capacity: usize) -> Self {
528        Self {
529            items: std::collections::VecDeque::new(),
530            present: vec![false; capacity],
531        }
532    }
533    #[allow(dead_code)]
534    pub fn push(&mut self, id: usize) {
535        if id < self.present.len() && !self.present[id] {
536            self.present[id] = true;
537            self.items.push_back(id);
538        }
539    }
540    #[allow(dead_code)]
541    pub fn push_front(&mut self, id: usize) {
542        if id < self.present.len() && !self.present[id] {
543            self.present[id] = true;
544            self.items.push_front(id);
545        }
546    }
547    #[allow(dead_code)]
548    pub fn pop(&mut self) -> Option<usize> {
549        let id = self.items.pop_front()?;
550        if id < self.present.len() {
551            self.present[id] = false;
552        }
553        Some(id)
554    }
555    #[allow(dead_code)]
556    pub fn is_empty(&self) -> bool {
557        self.items.is_empty()
558    }
559    #[allow(dead_code)]
560    pub fn len(&self) -> usize {
561        self.items.len()
562    }
563    #[allow(dead_code)]
564    pub fn contains(&self, id: usize) -> bool {
565        id < self.present.len() && self.present[id]
566    }
567    #[allow(dead_code)]
568    pub fn drain_all(&mut self) -> Vec<usize> {
569        let v: Vec<usize> = self.items.drain(..).collect();
570        for &id in &v {
571            if id < self.present.len() {
572                self.present[id] = false;
573            }
574        }
575        v
576    }
577}
578/// Constant folding helper for GLSLExt.
579#[allow(dead_code)]
580#[derive(Debug, Clone, Default)]
581pub struct GLSLExtConstFolder {
582    pub(super) folds: usize,
583    pub(super) failures: usize,
584    pub(super) enabled: bool,
585}
586impl GLSLExtConstFolder {
587    #[allow(dead_code)]
588    pub fn new() -> Self {
589        Self {
590            folds: 0,
591            failures: 0,
592            enabled: true,
593        }
594    }
595    #[allow(dead_code)]
596    pub fn add_i64(&mut self, a: i64, b: i64) -> Option<i64> {
597        self.folds += 1;
598        a.checked_add(b)
599    }
600    #[allow(dead_code)]
601    pub fn sub_i64(&mut self, a: i64, b: i64) -> Option<i64> {
602        self.folds += 1;
603        a.checked_sub(b)
604    }
605    #[allow(dead_code)]
606    pub fn mul_i64(&mut self, a: i64, b: i64) -> Option<i64> {
607        self.folds += 1;
608        a.checked_mul(b)
609    }
610    #[allow(dead_code)]
611    pub fn div_i64(&mut self, a: i64, b: i64) -> Option<i64> {
612        if b == 0 {
613            self.failures += 1;
614            None
615        } else {
616            self.folds += 1;
617            a.checked_div(b)
618        }
619    }
620    #[allow(dead_code)]
621    pub fn rem_i64(&mut self, a: i64, b: i64) -> Option<i64> {
622        if b == 0 {
623            self.failures += 1;
624            None
625        } else {
626            self.folds += 1;
627            a.checked_rem(b)
628        }
629    }
630    #[allow(dead_code)]
631    pub fn neg_i64(&mut self, a: i64) -> Option<i64> {
632        self.folds += 1;
633        a.checked_neg()
634    }
635    #[allow(dead_code)]
636    pub fn shl_i64(&mut self, a: i64, s: u32) -> Option<i64> {
637        if s >= 64 {
638            self.failures += 1;
639            None
640        } else {
641            self.folds += 1;
642            a.checked_shl(s)
643        }
644    }
645    #[allow(dead_code)]
646    pub fn shr_i64(&mut self, a: i64, s: u32) -> Option<i64> {
647        if s >= 64 {
648            self.failures += 1;
649            None
650        } else {
651            self.folds += 1;
652            a.checked_shr(s)
653        }
654    }
655    #[allow(dead_code)]
656    pub fn and_i64(&mut self, a: i64, b: i64) -> i64 {
657        self.folds += 1;
658        a & b
659    }
660    #[allow(dead_code)]
661    pub fn or_i64(&mut self, a: i64, b: i64) -> i64 {
662        self.folds += 1;
663        a | b
664    }
665    #[allow(dead_code)]
666    pub fn xor_i64(&mut self, a: i64, b: i64) -> i64 {
667        self.folds += 1;
668        a ^ b
669    }
670    #[allow(dead_code)]
671    pub fn not_i64(&mut self, a: i64) -> i64 {
672        self.folds += 1;
673        !a
674    }
675    #[allow(dead_code)]
676    pub fn fold_count(&self) -> usize {
677        self.folds
678    }
679    #[allow(dead_code)]
680    pub fn failure_count(&self) -> usize {
681        self.failures
682    }
683    #[allow(dead_code)]
684    pub fn enable(&mut self) {
685        self.enabled = true;
686    }
687    #[allow(dead_code)]
688    pub fn disable(&mut self) {
689        self.enabled = false;
690    }
691    #[allow(dead_code)]
692    pub fn is_enabled(&self) -> bool {
693        self.enabled
694    }
695}
696/// A simple GLSL macro expander.
697#[allow(dead_code)]
698#[derive(Debug, Clone, Default)]
699pub struct GlslMacroExpander {
700    pub(super) defines: std::collections::HashMap<String, String>,
701}
702#[allow(dead_code)]
703impl GlslMacroExpander {
704    pub fn new() -> Self {
705        GlslMacroExpander {
706            defines: std::collections::HashMap::new(),
707        }
708    }
709    pub fn define(&mut self, name: impl Into<String>, value: impl Into<String>) {
710        self.defines.insert(name.into(), value.into());
711    }
712    pub fn undef(&mut self, name: &str) {
713        self.defines.remove(name);
714    }
715    pub fn is_defined(&self, name: &str) -> bool {
716        self.defines.contains_key(name)
717    }
718    pub fn value(&self, name: &str) -> Option<&str> {
719        self.defines.get(name).map(|s| s.as_str())
720    }
721    pub fn emit_defines(&self) -> String {
722        let mut names: Vec<&String> = self.defines.keys().collect();
723        names.sort();
724        names
725            .iter()
726            .map(|name| {
727                let val = &self.defines[*name];
728                if val.is_empty() {
729                    format!("#define {}\n", name)
730                } else {
731                    format!("#define {} {}\n", name, val)
732                }
733            })
734            .collect()
735    }
736    pub fn num_defines(&self) -> usize {
737        self.defines.len()
738    }
739}
740/// A GLSL `struct` type definition.
741#[derive(Debug, Clone)]
742pub struct GLSLStruct {
743    /// Struct name.
744    pub name: String,
745    /// Fields in declaration order.
746    pub fields: Vec<GLSLStructField>,
747}
748impl GLSLStruct {
749    /// Create a new empty struct.
750    pub fn new(name: impl Into<String>) -> Self {
751        GLSLStruct {
752            name: name.into(),
753            fields: Vec::new(),
754        }
755    }
756    /// Add a field.
757    pub fn add_field(&mut self, name: impl Into<String>, ty: GLSLType) {
758        self.fields.push(GLSLStructField::new(name, ty));
759    }
760    /// Emit the struct definition.
761    pub fn emit(&self) -> String {
762        let mut out = format!("struct {} {{\n", self.name);
763        for f in &self.fields {
764            out.push_str(&format!("    {} {};\n", f.ty, f.name));
765        }
766        out.push_str("};");
767        out
768    }
769}
770/// A GLSL function definition.
771#[derive(Debug, Clone)]
772pub struct GLSLFunction {
773    /// Function name.
774    pub name: String,
775    /// Return type.
776    pub return_type: GLSLType,
777    /// Ordered list of parameters.
778    pub params: Vec<GLSLVariable>,
779    /// Body statements (each is emitted on its own line with indentation).
780    pub body: Vec<String>,
781}
782impl GLSLFunction {
783    /// Create a new function with an empty body.
784    pub fn new(name: impl Into<String>, return_type: GLSLType) -> Self {
785        GLSLFunction {
786            name: name.into(),
787            return_type,
788            params: Vec::new(),
789            body: Vec::new(),
790        }
791    }
792    /// Add a parameter.
793    pub fn add_param(&mut self, var: GLSLVariable) {
794        self.params.push(var);
795    }
796    /// Append a statement to the body.
797    pub fn add_statement(&mut self, stmt: impl Into<String>) {
798        self.body.push(stmt.into());
799    }
800    /// Emit the full function definition.
801    pub fn emit(&self) -> String {
802        let params: Vec<String> = self.params.iter().map(|p| p.emit_param()).collect();
803        let mut out = format!(
804            "{} {}({}) {{\n",
805            self.return_type,
806            self.name,
807            params.join(", ")
808        );
809        for stmt in &self.body {
810            out.push_str(&format!("    {};\n", stmt));
811        }
812        out.push('}');
813        out
814    }
815    /// Emit the forward declaration (prototype).
816    pub fn emit_prototype(&self) -> String {
817        let params: Vec<String> = self.params.iter().map(|p| p.emit_param()).collect();
818        format!("{} {}({});", self.return_type, self.name, params.join(", "))
819    }
820}
821/// A folded GLSL constant value.
822#[allow(dead_code)]
823#[derive(Debug, Clone, PartialEq)]
824pub enum GlslConstant {
825    Float(f64),
826    Int(i64),
827    Bool(bool),
828    Vec(Vec<f64>),
829}
830#[allow(dead_code)]
831impl GlslConstant {
832    /// Emit as a GLSL literal string.
833    pub fn to_glsl_literal(&self) -> String {
834        match self {
835            GlslConstant::Float(v) => {
836                if v.fract() == 0.0 {
837                    format!("{:.1}", v)
838                } else {
839                    format!("{}", v)
840                }
841            }
842            GlslConstant::Int(v) => format!("{}", v),
843            GlslConstant::Bool(b) => {
844                if *b {
845                    "true".to_string()
846                } else {
847                    "false".to_string()
848                }
849            }
850            GlslConstant::Vec(c) => {
851                let inner: Vec<String> = c
852                    .iter()
853                    .map(|v| {
854                        if v.fract() == 0.0 {
855                            format!("{:.1}", v)
856                        } else {
857                            format!("{}", v)
858                        }
859                    })
860                    .collect();
861                format!("vec{}({})", c.len(), inner.join(", "))
862            }
863        }
864    }
865    /// Is this the additive identity?
866    pub fn is_zero(&self) -> bool {
867        match self {
868            GlslConstant::Float(v) => *v == 0.0,
869            GlslConstant::Int(v) => *v == 0,
870            GlslConstant::Bool(b) => !b,
871            GlslConstant::Vec(c) => c.iter().all(|x| *x == 0.0),
872        }
873    }
874    /// Is this the multiplicative identity?
875    pub fn is_one(&self) -> bool {
876        match self {
877            GlslConstant::Float(v) => *v == 1.0,
878            GlslConstant::Int(v) => *v == 1,
879            _ => false,
880        }
881    }
882    /// Add two compatible constants.
883    pub fn add(&self, other: &GlslConstant) -> Option<GlslConstant> {
884        match (self, other) {
885            (GlslConstant::Float(a), GlslConstant::Float(b)) => Some(GlslConstant::Float(a + b)),
886            (GlslConstant::Int(a), GlslConstant::Int(b)) => Some(GlslConstant::Int(a + b)),
887            _ => None,
888        }
889    }
890}
891/// Tracks output variables declared by a GLSL shader stage.
892#[allow(dead_code)]
893#[derive(Debug, Clone, Default)]
894pub struct GlslOutputVariableSet {
895    pub(super) vars: Vec<(String, GLSLType, Option<u32>)>,
896}
897#[allow(dead_code)]
898impl GlslOutputVariableSet {
899    pub fn new() -> Self {
900        GlslOutputVariableSet { vars: Vec::new() }
901    }
902    pub fn add(&mut self, name: impl Into<String>, ty: GLSLType, location: Option<u32>) {
903        self.vars.push((name.into(), ty, location));
904    }
905    pub fn len(&self) -> usize {
906        self.vars.len()
907    }
908    pub fn is_empty(&self) -> bool {
909        self.vars.is_empty()
910    }
911    pub fn emit(&self, version: GLSLVersion) -> String {
912        let mut out = String::new();
913        for (name, ty, loc) in &self.vars {
914            if let Some(l) = loc {
915                if version.supports_layout_location() {
916                    out.push_str(&format!("layout(location = {}) ", l));
917                }
918            }
919            out.push_str(&format!("out {} {};\n", ty.keyword(), name));
920        }
921        out
922    }
923}
924/// Pass execution phase for GLSLExt.
925#[allow(dead_code)]
926#[derive(Debug, Clone, PartialEq, Eq, Hash)]
927pub enum GLSLExtPassPhase {
928    Early,
929    Middle,
930    Late,
931    Finalize,
932}
933impl GLSLExtPassPhase {
934    #[allow(dead_code)]
935    pub fn is_early(&self) -> bool {
936        matches!(self, Self::Early)
937    }
938    #[allow(dead_code)]
939    pub fn is_middle(&self) -> bool {
940        matches!(self, Self::Middle)
941    }
942    #[allow(dead_code)]
943    pub fn is_late(&self) -> bool {
944        matches!(self, Self::Late)
945    }
946    #[allow(dead_code)]
947    pub fn is_finalize(&self) -> bool {
948        matches!(self, Self::Finalize)
949    }
950    #[allow(dead_code)]
951    pub fn order(&self) -> u32 {
952        match self {
953            Self::Early => 0,
954            Self::Middle => 1,
955            Self::Late => 2,
956            Self::Finalize => 3,
957        }
958    }
959    #[allow(dead_code)]
960    pub fn from_order(n: u32) -> Option<Self> {
961        match n {
962            0 => Some(Self::Early),
963            1 => Some(Self::Middle),
964            2 => Some(Self::Late),
965            3 => Some(Self::Finalize),
966            _ => None,
967        }
968    }
969}
970#[allow(dead_code)]
971#[derive(Debug, Clone)]
972pub struct GLSLCacheEntry {
973    pub key: String,
974    pub data: Vec<u8>,
975    pub timestamp: u64,
976    pub valid: bool,
977}
978/// Liveness analysis for GLSLExt.
979#[allow(dead_code)]
980#[derive(Debug, Clone, Default)]
981pub struct GLSLExtLiveness {
982    pub live_in: Vec<Vec<usize>>,
983    pub live_out: Vec<Vec<usize>>,
984    pub defs: Vec<Vec<usize>>,
985    pub uses: Vec<Vec<usize>>,
986}
987impl GLSLExtLiveness {
988    #[allow(dead_code)]
989    pub fn new(n: usize) -> Self {
990        Self {
991            live_in: vec![Vec::new(); n],
992            live_out: vec![Vec::new(); n],
993            defs: vec![Vec::new(); n],
994            uses: vec![Vec::new(); n],
995        }
996    }
997    #[allow(dead_code)]
998    pub fn live_in(&self, b: usize, v: usize) -> bool {
999        self.live_in.get(b).map(|s| s.contains(&v)).unwrap_or(false)
1000    }
1001    #[allow(dead_code)]
1002    pub fn live_out(&self, b: usize, v: usize) -> bool {
1003        self.live_out
1004            .get(b)
1005            .map(|s| s.contains(&v))
1006            .unwrap_or(false)
1007    }
1008    #[allow(dead_code)]
1009    pub fn add_def(&mut self, b: usize, v: usize) {
1010        if let Some(s) = self.defs.get_mut(b) {
1011            if !s.contains(&v) {
1012                s.push(v);
1013            }
1014        }
1015    }
1016    #[allow(dead_code)]
1017    pub fn add_use(&mut self, b: usize, v: usize) {
1018        if let Some(s) = self.uses.get_mut(b) {
1019            if !s.contains(&v) {
1020                s.push(v);
1021            }
1022        }
1023    }
1024    #[allow(dead_code)]
1025    pub fn var_is_used_in_block(&self, b: usize, v: usize) -> bool {
1026        self.uses.get(b).map(|s| s.contains(&v)).unwrap_or(false)
1027    }
1028    #[allow(dead_code)]
1029    pub fn var_is_def_in_block(&self, b: usize, v: usize) -> bool {
1030        self.defs.get(b).map(|s| s.contains(&v)).unwrap_or(false)
1031    }
1032}
1033#[allow(dead_code)]
1034#[derive(Debug, Clone, Default)]
1035pub struct GLSLPassStats {
1036    pub total_runs: u32,
1037    pub successful_runs: u32,
1038    pub total_changes: u64,
1039    pub time_ms: u64,
1040    pub iterations_used: u32,
1041}
1042impl GLSLPassStats {
1043    #[allow(dead_code)]
1044    pub fn new() -> Self {
1045        Self::default()
1046    }
1047    #[allow(dead_code)]
1048    pub fn record_run(&mut self, changes: u64, time_ms: u64, iterations: u32) {
1049        self.total_runs += 1;
1050        self.successful_runs += 1;
1051        self.total_changes += changes;
1052        self.time_ms += time_ms;
1053        self.iterations_used = iterations;
1054    }
1055    #[allow(dead_code)]
1056    pub fn average_changes_per_run(&self) -> f64 {
1057        if self.total_runs == 0 {
1058            return 0.0;
1059        }
1060        self.total_changes as f64 / self.total_runs as f64
1061    }
1062    #[allow(dead_code)]
1063    pub fn success_rate(&self) -> f64 {
1064        if self.total_runs == 0 {
1065            return 0.0;
1066        }
1067        self.successful_runs as f64 / self.total_runs as f64
1068    }
1069    #[allow(dead_code)]
1070    pub fn format_summary(&self) -> String {
1071        format!(
1072            "Runs: {}/{}, Changes: {}, Time: {}ms",
1073            self.successful_runs, self.total_runs, self.total_changes, self.time_ms
1074        )
1075    }
1076}
1077#[allow(dead_code)]
1078#[derive(Debug, Clone)]
1079pub struct GLSLWorklist {
1080    pub(super) items: std::collections::VecDeque<u32>,
1081    pub(super) in_worklist: std::collections::HashSet<u32>,
1082}
1083impl GLSLWorklist {
1084    #[allow(dead_code)]
1085    pub fn new() -> Self {
1086        GLSLWorklist {
1087            items: std::collections::VecDeque::new(),
1088            in_worklist: std::collections::HashSet::new(),
1089        }
1090    }
1091    #[allow(dead_code)]
1092    pub fn push(&mut self, item: u32) -> bool {
1093        if self.in_worklist.insert(item) {
1094            self.items.push_back(item);
1095            true
1096        } else {
1097            false
1098        }
1099    }
1100    #[allow(dead_code)]
1101    pub fn pop(&mut self) -> Option<u32> {
1102        let item = self.items.pop_front()?;
1103        self.in_worklist.remove(&item);
1104        Some(item)
1105    }
1106    #[allow(dead_code)]
1107    pub fn is_empty(&self) -> bool {
1108        self.items.is_empty()
1109    }
1110    #[allow(dead_code)]
1111    pub fn len(&self) -> usize {
1112        self.items.len()
1113    }
1114    #[allow(dead_code)]
1115    pub fn contains(&self, item: u32) -> bool {
1116        self.in_worklist.contains(&item)
1117    }
1118}
1119#[allow(dead_code)]
1120#[derive(Debug, Clone)]
1121pub struct GLSLDepGraph {
1122    pub(super) nodes: Vec<u32>,
1123    pub(super) edges: Vec<(u32, u32)>,
1124}
1125impl GLSLDepGraph {
1126    #[allow(dead_code)]
1127    pub fn new() -> Self {
1128        GLSLDepGraph {
1129            nodes: Vec::new(),
1130            edges: Vec::new(),
1131        }
1132    }
1133    #[allow(dead_code)]
1134    pub fn add_node(&mut self, id: u32) {
1135        if !self.nodes.contains(&id) {
1136            self.nodes.push(id);
1137        }
1138    }
1139    #[allow(dead_code)]
1140    pub fn add_dep(&mut self, dep: u32, dependent: u32) {
1141        self.add_node(dep);
1142        self.add_node(dependent);
1143        self.edges.push((dep, dependent));
1144    }
1145    #[allow(dead_code)]
1146    pub fn dependents_of(&self, node: u32) -> Vec<u32> {
1147        self.edges
1148            .iter()
1149            .filter(|(d, _)| *d == node)
1150            .map(|(_, dep)| *dep)
1151            .collect()
1152    }
1153    #[allow(dead_code)]
1154    pub fn dependencies_of(&self, node: u32) -> Vec<u32> {
1155        self.edges
1156            .iter()
1157            .filter(|(_, dep)| *dep == node)
1158            .map(|(d, _)| *d)
1159            .collect()
1160    }
1161    #[allow(dead_code)]
1162    pub fn topological_sort(&self) -> Vec<u32> {
1163        let mut in_degree: std::collections::HashMap<u32, u32> = std::collections::HashMap::new();
1164        for &n in &self.nodes {
1165            in_degree.insert(n, 0);
1166        }
1167        for (_, dep) in &self.edges {
1168            *in_degree.entry(*dep).or_insert(0) += 1;
1169        }
1170        let mut queue: std::collections::VecDeque<u32> = self
1171            .nodes
1172            .iter()
1173            .filter(|&&n| in_degree[&n] == 0)
1174            .copied()
1175            .collect();
1176        let mut result = Vec::new();
1177        while let Some(node) = queue.pop_front() {
1178            result.push(node);
1179            for dep in self.dependents_of(node) {
1180                let cnt = in_degree.entry(dep).or_insert(0);
1181                *cnt = cnt.saturating_sub(1);
1182                if *cnt == 0 {
1183                    queue.push_back(dep);
1184                }
1185            }
1186        }
1187        result
1188    }
1189    #[allow(dead_code)]
1190    pub fn has_cycle(&self) -> bool {
1191        self.topological_sort().len() < self.nodes.len()
1192    }
1193}
1194/// Metadata about a GLSL built-in function.
1195#[allow(dead_code)]
1196#[derive(Debug, Clone)]
1197pub struct GlslBuiltinFn {
1198    pub name: &'static str,
1199    pub category: GlslBuiltinCategory,
1200    pub min_version: u32,
1201}
1202impl GlslBuiltinFn {
1203    pub(super) const fn new(name: &'static str, cat: GlslBuiltinCategory, ver: u32) -> Self {
1204        GlslBuiltinFn {
1205            name,
1206            category: cat,
1207            min_version: ver,
1208        }
1209    }
1210}
1211/// A complete GLSL shader for a single pipeline stage.
1212#[derive(Debug, Clone)]
1213pub struct GLSLShader {
1214    /// Target GLSL version.
1215    pub version: GLSLVersion,
1216    /// Pipeline stage.
1217    pub stage: GLSLShaderStage,
1218    /// Extension enable directives (e.g. `"GL_ARB_shader_storage_buffer_object"`).
1219    pub extensions: Vec<String>,
1220    /// Global `#define` macros (name, optional value).
1221    pub defines: Vec<(String, Option<String>)>,
1222    /// Struct type definitions (emitted before globals).
1223    pub structs: Vec<GLSLStruct>,
1224    /// Global `in` variables (stage inputs).
1225    pub inputs: Vec<GLSLVariable>,
1226    /// Global `out` variables (stage outputs).
1227    pub outputs: Vec<GLSLVariable>,
1228    /// Global `uniform` variables.
1229    pub uniforms: Vec<GLSLVariable>,
1230    /// Additional global variables (e.g. shared memory in compute).
1231    pub globals: Vec<GLSLVariable>,
1232    /// Helper functions (emitted before `main`).
1233    pub functions: Vec<GLSLFunction>,
1234    /// Body statements of the implicit `void main()` function.
1235    pub main_body: Vec<String>,
1236}
1237impl GLSLShader {
1238    /// Create a new, empty shader for the given version and stage.
1239    pub fn new(version: GLSLVersion, stage: GLSLShaderStage) -> Self {
1240        GLSLShader {
1241            version,
1242            stage,
1243            extensions: Vec::new(),
1244            defines: Vec::new(),
1245            structs: Vec::new(),
1246            inputs: Vec::new(),
1247            outputs: Vec::new(),
1248            uniforms: Vec::new(),
1249            globals: Vec::new(),
1250            functions: Vec::new(),
1251            main_body: Vec::new(),
1252        }
1253    }
1254    /// Add an extension directive.
1255    pub fn add_extension(&mut self, ext: impl Into<String>) {
1256        self.extensions.push(ext.into());
1257    }
1258    /// Add a `#define` macro without a value.
1259    pub fn add_define(&mut self, name: impl Into<String>) {
1260        self.defines.push((name.into(), None));
1261    }
1262    /// Add a `#define` macro with a value.
1263    pub fn add_define_value(&mut self, name: impl Into<String>, value: impl Into<String>) {
1264        self.defines.push((name.into(), Some(value.into())));
1265    }
1266    /// Add a struct definition.
1267    pub fn add_struct(&mut self, s: GLSLStruct) {
1268        self.structs.push(s);
1269    }
1270    /// Add a stage input.
1271    pub fn add_input(&mut self, v: GLSLVariable) {
1272        self.inputs.push(v);
1273    }
1274    /// Add a stage output.
1275    pub fn add_output(&mut self, v: GLSLVariable) {
1276        self.outputs.push(v);
1277    }
1278    /// Add a uniform variable.
1279    pub fn add_uniform(&mut self, v: GLSLVariable) {
1280        self.uniforms.push(v);
1281    }
1282    /// Add a global variable.
1283    pub fn add_global(&mut self, v: GLSLVariable) {
1284        self.globals.push(v);
1285    }
1286    /// Add a helper function.
1287    pub fn add_function(&mut self, f: GLSLFunction) {
1288        self.functions.push(f);
1289    }
1290    /// Append a statement to `main`.
1291    pub fn add_main_statement(&mut self, stmt: impl Into<String>) {
1292        self.main_body.push(stmt.into());
1293    }
1294}
1295/// Analysis cache for GLSLExt.
1296#[allow(dead_code)]
1297#[derive(Debug)]
1298pub struct GLSLExtCache {
1299    pub(super) entries: Vec<(u64, Vec<u8>, bool, u32)>,
1300    pub(super) cap: usize,
1301    pub(super) total_hits: u64,
1302    pub(super) total_misses: u64,
1303}
1304impl GLSLExtCache {
1305    #[allow(dead_code)]
1306    pub fn new(cap: usize) -> Self {
1307        Self {
1308            entries: Vec::new(),
1309            cap,
1310            total_hits: 0,
1311            total_misses: 0,
1312        }
1313    }
1314    #[allow(dead_code)]
1315    pub fn get(&mut self, key: u64) -> Option<&[u8]> {
1316        for e in self.entries.iter_mut() {
1317            if e.0 == key && e.2 {
1318                e.3 += 1;
1319                self.total_hits += 1;
1320                return Some(&e.1);
1321            }
1322        }
1323        self.total_misses += 1;
1324        None
1325    }
1326    #[allow(dead_code)]
1327    pub fn put(&mut self, key: u64, data: Vec<u8>) {
1328        if self.entries.len() >= self.cap {
1329            self.entries.retain(|e| e.2);
1330            if self.entries.len() >= self.cap {
1331                self.entries.remove(0);
1332            }
1333        }
1334        self.entries.push((key, data, true, 0));
1335    }
1336    #[allow(dead_code)]
1337    pub fn invalidate(&mut self) {
1338        for e in self.entries.iter_mut() {
1339            e.2 = false;
1340        }
1341    }
1342    #[allow(dead_code)]
1343    pub fn hit_rate(&self) -> f64 {
1344        let t = self.total_hits + self.total_misses;
1345        if t == 0 {
1346            0.0
1347        } else {
1348            self.total_hits as f64 / t as f64
1349        }
1350    }
1351    #[allow(dead_code)]
1352    pub fn live_count(&self) -> usize {
1353        self.entries.iter().filter(|e| e.2).count()
1354    }
1355}
1356#[allow(dead_code)]
1357#[derive(Debug, Clone)]
1358pub struct GLSLPassConfig {
1359    pub phase: GLSLPassPhase,
1360    pub enabled: bool,
1361    pub max_iterations: u32,
1362    pub debug_output: bool,
1363    pub pass_name: String,
1364}
1365impl GLSLPassConfig {
1366    #[allow(dead_code)]
1367    pub fn new(name: impl Into<String>, phase: GLSLPassPhase) -> Self {
1368        GLSLPassConfig {
1369            phase,
1370            enabled: true,
1371            max_iterations: 10,
1372            debug_output: false,
1373            pass_name: name.into(),
1374        }
1375    }
1376    #[allow(dead_code)]
1377    pub fn disabled(mut self) -> Self {
1378        self.enabled = false;
1379        self
1380    }
1381    #[allow(dead_code)]
1382    pub fn with_debug(mut self) -> Self {
1383        self.debug_output = true;
1384        self
1385    }
1386    #[allow(dead_code)]
1387    pub fn max_iter(mut self, n: u32) -> Self {
1388        self.max_iterations = n;
1389        self
1390    }
1391}
1392#[allow(dead_code)]
1393#[derive(Debug, Clone)]
1394pub struct GLSLLivenessInfo {
1395    pub live_in: Vec<std::collections::HashSet<u32>>,
1396    pub live_out: Vec<std::collections::HashSet<u32>>,
1397    pub defs: Vec<std::collections::HashSet<u32>>,
1398    pub uses: Vec<std::collections::HashSet<u32>>,
1399}
1400impl GLSLLivenessInfo {
1401    #[allow(dead_code)]
1402    pub fn new(block_count: usize) -> Self {
1403        GLSLLivenessInfo {
1404            live_in: vec![std::collections::HashSet::new(); block_count],
1405            live_out: vec![std::collections::HashSet::new(); block_count],
1406            defs: vec![std::collections::HashSet::new(); block_count],
1407            uses: vec![std::collections::HashSet::new(); block_count],
1408        }
1409    }
1410    #[allow(dead_code)]
1411    pub fn add_def(&mut self, block: usize, var: u32) {
1412        if block < self.defs.len() {
1413            self.defs[block].insert(var);
1414        }
1415    }
1416    #[allow(dead_code)]
1417    pub fn add_use(&mut self, block: usize, var: u32) {
1418        if block < self.uses.len() {
1419            self.uses[block].insert(var);
1420        }
1421    }
1422    #[allow(dead_code)]
1423    pub fn is_live_in(&self, block: usize, var: u32) -> bool {
1424        self.live_in
1425            .get(block)
1426            .map(|s| s.contains(&var))
1427            .unwrap_or(false)
1428    }
1429    #[allow(dead_code)]
1430    pub fn is_live_out(&self, block: usize, var: u32) -> bool {
1431        self.live_out
1432            .get(block)
1433            .map(|s| s.contains(&var))
1434            .unwrap_or(false)
1435    }
1436}
1437/// Code generation backend that emits GLSL source text.
1438pub struct GLSLBackend {
1439    /// Target GLSL version.
1440    pub version: GLSLVersion,
1441}
1442impl GLSLBackend {
1443    /// Create a new backend targeting the specified GLSL version.
1444    pub fn new(version: GLSLVersion) -> Self {
1445        GLSLBackend { version }
1446    }
1447    /// Return the GLSL keyword string for a type.
1448    pub fn emit_type(&self, ty: &GLSLType) -> String {
1449        ty.keyword()
1450    }
1451    /// Emit a complete GLSL shader as a source string.
1452    pub fn emit_shader(&self, shader: &GLSLShader) -> String {
1453        let mut out = String::new();
1454        out.push_str(&shader.version.version_line());
1455        out.push('\n');
1456        for ext in &shader.extensions {
1457            out.push_str(&format!("#extension {} : enable\n", ext));
1458        }
1459        if !shader.extensions.is_empty() {
1460            out.push('\n');
1461        }
1462        for (name, val) in &shader.defines {
1463            match val {
1464                Some(v) => out.push_str(&format!("#define {} {}\n", name, v)),
1465                None => out.push_str(&format!("#define {}\n", name)),
1466            }
1467        }
1468        if !shader.defines.is_empty() {
1469            out.push('\n');
1470        }
1471        for s in &shader.structs {
1472            out.push_str(&s.emit());
1473            out.push_str("\n\n");
1474        }
1475        for v in &shader.inputs {
1476            out.push_str(&v.emit_global());
1477            out.push('\n');
1478        }
1479        if !shader.inputs.is_empty() {
1480            out.push('\n');
1481        }
1482        for v in &shader.outputs {
1483            out.push_str(&v.emit_global());
1484            out.push('\n');
1485        }
1486        if !shader.outputs.is_empty() {
1487            out.push('\n');
1488        }
1489        for v in &shader.uniforms {
1490            out.push_str(&v.emit_global());
1491            out.push('\n');
1492        }
1493        if !shader.uniforms.is_empty() {
1494            out.push('\n');
1495        }
1496        for v in &shader.globals {
1497            out.push_str(&v.emit_global());
1498            out.push('\n');
1499        }
1500        if !shader.globals.is_empty() {
1501            out.push('\n');
1502        }
1503        for f in &shader.functions {
1504            out.push_str(&f.emit());
1505            out.push_str("\n\n");
1506        }
1507        out.push_str("void main() {\n");
1508        for stmt in &shader.main_body {
1509            out.push_str(&format!("    {};\n", stmt));
1510        }
1511        out.push_str("}\n");
1512        out
1513    }
1514    /// Build a minimal vertex shader template that passes a position through.
1515    pub fn vertex_shader_template(&self) -> String {
1516        let mut shader = GLSLShader::new(self.version, GLSLShaderStage::Vertex);
1517        if self.version.supports_layout_location() {
1518            shader.add_input(GLSLVariable::layout_input("aPosition", GLSLType::Vec3, 0));
1519            shader.add_input(GLSLVariable::layout_input("aTexCoord", GLSLType::Vec2, 1));
1520            shader.add_output(GLSLVariable::layout_output("vTexCoord", GLSLType::Vec2, 0));
1521        } else {
1522            shader.add_input(GLSLVariable::input("aPosition", GLSLType::Vec3));
1523            shader.add_input(GLSLVariable::input("aTexCoord", GLSLType::Vec2));
1524            shader.add_output(GLSLVariable::output("vTexCoord", GLSLType::Vec2));
1525        }
1526        shader.add_uniform(GLSLVariable::uniform("uMVP", GLSLType::Mat4));
1527        shader.add_main_statement("gl_Position = uMVP * vec4(aPosition, 1.0)");
1528        shader.add_main_statement("vTexCoord = aTexCoord");
1529        self.emit_shader(&shader)
1530    }
1531    /// Build a minimal fragment (pixel) shader template that samples a texture.
1532    pub fn fragment_shader_template(&self) -> String {
1533        let mut shader = GLSLShader::new(self.version, GLSLShaderStage::Fragment);
1534        if self.version.supports_layout_location() {
1535            shader.add_input(GLSLVariable::layout_input("vTexCoord", GLSLType::Vec2, 0));
1536            shader.add_output(GLSLVariable::layout_output("fragColor", GLSLType::Vec4, 0));
1537        } else {
1538            shader.add_input(GLSLVariable::input("vTexCoord", GLSLType::Vec2));
1539            shader.add_output(GLSLVariable::output("fragColor", GLSLType::Vec4));
1540        }
1541        shader.add_uniform(GLSLVariable::uniform("uTexture", GLSLType::Sampler2D));
1542        shader.add_main_statement("fragColor = texture(uTexture, vTexCoord)");
1543        self.emit_shader(&shader)
1544    }
1545    /// Build a Phong lighting vertex shader template.
1546    pub fn phong_vertex_template(&self) -> String {
1547        let mut shader = GLSLShader::new(self.version, GLSLShaderStage::Vertex);
1548        shader.add_input(GLSLVariable::layout_input("aPosition", GLSLType::Vec3, 0));
1549        shader.add_input(GLSLVariable::layout_input("aNormal", GLSLType::Vec3, 1));
1550        shader.add_output(GLSLVariable::layout_output("vNormal", GLSLType::Vec3, 0));
1551        shader.add_output(GLSLVariable::layout_output("vFragPos", GLSLType::Vec3, 1));
1552        shader.add_uniform(GLSLVariable::uniform("uModel", GLSLType::Mat4));
1553        shader.add_uniform(GLSLVariable::uniform("uView", GLSLType::Mat4));
1554        shader.add_uniform(GLSLVariable::uniform("uProjection", GLSLType::Mat4));
1555        shader.add_main_statement("vFragPos = vec3(uModel * vec4(aPosition, 1.0))");
1556        shader.add_main_statement("vNormal = mat3(transpose(inverse(uModel))) * aNormal");
1557        shader.add_main_statement("gl_Position = uProjection * uView * vec4(vFragPos, 1.0)");
1558        self.emit_shader(&shader)
1559    }
1560    /// Build a Phong lighting fragment shader template.
1561    pub fn phong_fragment_template(&self) -> String {
1562        let mut shader = GLSLShader::new(self.version, GLSLShaderStage::Fragment);
1563        shader.add_input(GLSLVariable::layout_input("vNormal", GLSLType::Vec3, 0));
1564        shader.add_input(GLSLVariable::layout_input("vFragPos", GLSLType::Vec3, 1));
1565        shader.add_output(GLSLVariable::layout_output("fragColor", GLSLType::Vec4, 0));
1566        shader.add_uniform(GLSLVariable::uniform("uLightPos", GLSLType::Vec3));
1567        shader.add_uniform(GLSLVariable::uniform("uViewPos", GLSLType::Vec3));
1568        shader.add_uniform(GLSLVariable::uniform("uLightColor", GLSLType::Vec3));
1569        shader.add_uniform(GLSLVariable::uniform("uObjectColor", GLSLType::Vec3));
1570        shader.add_main_statement("float ambientStrength = 0.1");
1571        shader.add_main_statement("vec3 ambient = ambientStrength * uLightColor");
1572        shader.add_main_statement("vec3 norm = normalize(vNormal)");
1573        shader.add_main_statement("vec3 lightDir = normalize(uLightPos - vFragPos)");
1574        shader.add_main_statement("float diff = max(dot(norm, lightDir), 0.0)");
1575        shader.add_main_statement("vec3 diffuse = diff * uLightColor");
1576        shader.add_main_statement("float specularStrength = 0.5");
1577        shader.add_main_statement("vec3 viewDir = normalize(uViewPos - vFragPos)");
1578        shader.add_main_statement("vec3 reflectDir = reflect(-lightDir, norm)");
1579        shader.add_main_statement("float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32.0)");
1580        shader.add_main_statement("vec3 specular = specularStrength * spec * uLightColor");
1581        shader.add_main_statement("vec3 result = (ambient + diffuse + specular) * uObjectColor");
1582        shader.add_main_statement("fragColor = vec4(result, 1.0)");
1583        self.emit_shader(&shader)
1584    }
1585    /// Build a compute shader template (GLSL 4.3+).
1586    pub fn compute_shader_template(&self, local_x: u32, local_y: u32, local_z: u32) -> String {
1587        let mut shader = GLSLShader::new(self.version, GLSLShaderStage::Compute);
1588        shader.add_extension("GL_ARB_compute_shader");
1589        shader.add_main_statement("uint idx = gl_GlobalInvocationID.x");
1590        shader.add_main_statement("// TODO: compute work here");
1591        let mut out = self.emit_shader(&shader);
1592        let layout_line = format!(
1593            "layout(local_size_x = {}, local_size_y = {}, local_size_z = {}) in;\n",
1594            local_x, local_y, local_z
1595        );
1596        if let Some(pos) = out.find('\n') {
1597            out.insert_str(pos + 1, &layout_line);
1598        }
1599        out
1600    }
1601}
1602/// Configuration for a GLSL compute shader work-group.
1603#[allow(dead_code)]
1604#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1605pub struct GlslComputeWorkgroup {
1606    pub local_size_x: u32,
1607    pub local_size_y: u32,
1608    pub local_size_z: u32,
1609}
1610#[allow(dead_code)]
1611impl GlslComputeWorkgroup {
1612    pub fn linear(x: u32) -> Self {
1613        GlslComputeWorkgroup {
1614            local_size_x: x,
1615            local_size_y: 1,
1616            local_size_z: 1,
1617        }
1618    }
1619    pub fn planar(x: u32, y: u32) -> Self {
1620        GlslComputeWorkgroup {
1621            local_size_x: x,
1622            local_size_y: y,
1623            local_size_z: 1,
1624        }
1625    }
1626    pub fn volumetric(x: u32, y: u32, z: u32) -> Self {
1627        GlslComputeWorkgroup {
1628            local_size_x: x,
1629            local_size_y: y,
1630            local_size_z: z,
1631        }
1632    }
1633    pub fn total_threads(&self) -> u32 {
1634        self.local_size_x * self.local_size_y * self.local_size_z
1635    }
1636    pub fn emit_layout(&self) -> String {
1637        format!(
1638            "layout(local_size_x = {}, local_size_y = {}, local_size_z = {}) in;\n",
1639            self.local_size_x, self.local_size_y, self.local_size_z
1640        )
1641    }
1642}
1643/// Dominator tree for GLSLExt.
1644#[allow(dead_code)]
1645#[derive(Debug, Clone)]
1646pub struct GLSLExtDomTree {
1647    pub(super) idom: Vec<Option<usize>>,
1648    pub(super) children: Vec<Vec<usize>>,
1649    pub(super) depth: Vec<usize>,
1650}
1651impl GLSLExtDomTree {
1652    #[allow(dead_code)]
1653    pub fn new(n: usize) -> Self {
1654        Self {
1655            idom: vec![None; n],
1656            children: vec![Vec::new(); n],
1657            depth: vec![0; n],
1658        }
1659    }
1660    #[allow(dead_code)]
1661    pub fn set_idom(&mut self, node: usize, dom: usize) {
1662        if node < self.idom.len() {
1663            self.idom[node] = Some(dom);
1664            if dom < self.children.len() {
1665                self.children[dom].push(node);
1666            }
1667            self.depth[node] = if dom < self.depth.len() {
1668                self.depth[dom] + 1
1669            } else {
1670                1
1671            };
1672        }
1673    }
1674    #[allow(dead_code)]
1675    pub fn dominates(&self, a: usize, mut b: usize) -> bool {
1676        if a == b {
1677            return true;
1678        }
1679        let n = self.idom.len();
1680        for _ in 0..n {
1681            match self.idom.get(b).copied().flatten() {
1682                None => return false,
1683                Some(p) if p == a => return true,
1684                Some(p) if p == b => return false,
1685                Some(p) => b = p,
1686            }
1687        }
1688        false
1689    }
1690    #[allow(dead_code)]
1691    pub fn children_of(&self, n: usize) -> &[usize] {
1692        self.children.get(n).map(|v| v.as_slice()).unwrap_or(&[])
1693    }
1694    #[allow(dead_code)]
1695    pub fn depth_of(&self, n: usize) -> usize {
1696        self.depth.get(n).copied().unwrap_or(0)
1697    }
1698    #[allow(dead_code)]
1699    pub fn lca(&self, mut a: usize, mut b: usize) -> usize {
1700        let n = self.idom.len();
1701        for _ in 0..(2 * n) {
1702            if a == b {
1703                return a;
1704            }
1705            if self.depth_of(a) > self.depth_of(b) {
1706                a = self.idom.get(a).and_then(|x| *x).unwrap_or(a);
1707            } else {
1708                b = self.idom.get(b).and_then(|x| *x).unwrap_or(b);
1709            }
1710        }
1711        0
1712    }
1713}
1714/// Generates include guards for GLSL headers.
1715#[allow(dead_code)]
1716#[derive(Debug, Clone)]
1717pub struct GlslIncludeGuard {
1718    pub macro_name: String,
1719}
1720#[allow(dead_code)]
1721impl GlslIncludeGuard {
1722    pub fn from_filename(filename: &str) -> Self {
1723        let macro_name = filename
1724            .replace('.', "_")
1725            .replace('/', "_")
1726            .replace('-', "_")
1727            .to_uppercase();
1728        GlslIncludeGuard {
1729            macro_name: format!("{}_GUARD", macro_name),
1730        }
1731    }
1732    pub fn open(&self) -> String {
1733        format!("#ifndef {0}\n#define {0}\n", self.macro_name)
1734    }
1735    pub fn close(&self) -> String {
1736        format!("#endif // {}\n", self.macro_name)
1737    }
1738}
1739/// Dependency graph for GLSLExt.
1740#[allow(dead_code)]
1741#[derive(Debug, Clone)]
1742pub struct GLSLExtDepGraph {
1743    pub(super) n: usize,
1744    pub(super) adj: Vec<Vec<usize>>,
1745    pub(super) rev: Vec<Vec<usize>>,
1746    pub(super) edge_count: usize,
1747}
1748impl GLSLExtDepGraph {
1749    #[allow(dead_code)]
1750    pub fn new(n: usize) -> Self {
1751        Self {
1752            n,
1753            adj: vec![Vec::new(); n],
1754            rev: vec![Vec::new(); n],
1755            edge_count: 0,
1756        }
1757    }
1758    #[allow(dead_code)]
1759    pub fn add_edge(&mut self, from: usize, to: usize) {
1760        if from < self.n && to < self.n {
1761            if !self.adj[from].contains(&to) {
1762                self.adj[from].push(to);
1763                self.rev[to].push(from);
1764                self.edge_count += 1;
1765            }
1766        }
1767    }
1768    #[allow(dead_code)]
1769    pub fn succs(&self, n: usize) -> &[usize] {
1770        self.adj.get(n).map(|v| v.as_slice()).unwrap_or(&[])
1771    }
1772    #[allow(dead_code)]
1773    pub fn preds(&self, n: usize) -> &[usize] {
1774        self.rev.get(n).map(|v| v.as_slice()).unwrap_or(&[])
1775    }
1776    #[allow(dead_code)]
1777    pub fn topo_sort(&self) -> Option<Vec<usize>> {
1778        let mut deg: Vec<usize> = (0..self.n).map(|i| self.rev[i].len()).collect();
1779        let mut q: std::collections::VecDeque<usize> =
1780            (0..self.n).filter(|&i| deg[i] == 0).collect();
1781        let mut out = Vec::with_capacity(self.n);
1782        while let Some(u) = q.pop_front() {
1783            out.push(u);
1784            for &v in &self.adj[u] {
1785                deg[v] -= 1;
1786                if deg[v] == 0 {
1787                    q.push_back(v);
1788                }
1789            }
1790        }
1791        if out.len() == self.n {
1792            Some(out)
1793        } else {
1794            None
1795        }
1796    }
1797    #[allow(dead_code)]
1798    pub fn has_cycle(&self) -> bool {
1799        self.topo_sort().is_none()
1800    }
1801    #[allow(dead_code)]
1802    pub fn reachable(&self, start: usize) -> Vec<usize> {
1803        let mut vis = vec![false; self.n];
1804        let mut stk = vec![start];
1805        let mut out = Vec::new();
1806        while let Some(u) = stk.pop() {
1807            if u < self.n && !vis[u] {
1808                vis[u] = true;
1809                out.push(u);
1810                for &v in &self.adj[u] {
1811                    if !vis[v] {
1812                        stk.push(v);
1813                    }
1814                }
1815            }
1816        }
1817        out
1818    }
1819    #[allow(dead_code)]
1820    pub fn scc(&self) -> Vec<Vec<usize>> {
1821        let mut visited = vec![false; self.n];
1822        let mut order = Vec::new();
1823        for i in 0..self.n {
1824            if !visited[i] {
1825                let mut stk = vec![(i, 0usize)];
1826                while let Some((u, idx)) = stk.last_mut() {
1827                    if !visited[*u] {
1828                        visited[*u] = true;
1829                    }
1830                    if *idx < self.adj[*u].len() {
1831                        let v = self.adj[*u][*idx];
1832                        *idx += 1;
1833                        if !visited[v] {
1834                            stk.push((v, 0));
1835                        }
1836                    } else {
1837                        order.push(*u);
1838                        stk.pop();
1839                    }
1840                }
1841            }
1842        }
1843        let mut comp = vec![usize::MAX; self.n];
1844        let mut components: Vec<Vec<usize>> = Vec::new();
1845        for &start in order.iter().rev() {
1846            if comp[start] == usize::MAX {
1847                let cid = components.len();
1848                let mut component = Vec::new();
1849                let mut stk = vec![start];
1850                while let Some(u) = stk.pop() {
1851                    if comp[u] == usize::MAX {
1852                        comp[u] = cid;
1853                        component.push(u);
1854                        for &v in &self.rev[u] {
1855                            if comp[v] == usize::MAX {
1856                                stk.push(v);
1857                            }
1858                        }
1859                    }
1860                }
1861                components.push(component);
1862            }
1863        }
1864        components
1865    }
1866    #[allow(dead_code)]
1867    pub fn node_count(&self) -> usize {
1868        self.n
1869    }
1870    #[allow(dead_code)]
1871    pub fn edge_count(&self) -> usize {
1872        self.edge_count
1873    }
1874}
1875/// The pipeline stage that a GLSL shader implements.
1876#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1877pub enum GLSLShaderStage {
1878    /// Vertex shader — runs once per vertex.
1879    Vertex,
1880    /// Fragment (pixel) shader — runs once per fragment.
1881    Fragment,
1882    /// Geometry shader — runs once per primitive.
1883    Geometry,
1884    /// Tessellation control shader.
1885    TessControl,
1886    /// Tessellation evaluation shader.
1887    TessEval,
1888    /// Compute shader (GLSL 4.3+).
1889    Compute,
1890}
1891/// Storage / parameter qualifier for a GLSL variable declaration.
1892#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1893pub enum GLSLQualifier {
1894    /// No qualifier (local variable inside a function).
1895    None,
1896    /// `in` — stage input.
1897    In,
1898    /// `out` — stage output.
1899    Out,
1900    /// `inout` — read/write function parameter.
1901    InOut,
1902    /// `uniform` — uniform variable (set from the host).
1903    Uniform,
1904    /// `const` — compile-time constant.
1905    Const,
1906    /// `flat` — flat interpolation.
1907    Flat,
1908    /// `centroid in` — centroid-interpolated input.
1909    CentroidIn,
1910    /// `centroid out` — centroid-interpolated output.
1911    CentroidOut,
1912    /// `layout(…) in` with a custom layout string.
1913    LayoutIn(String),
1914    /// `layout(…) out` with a custom layout string.
1915    LayoutOut(String),
1916    /// `layout(…) uniform` with a custom layout string.
1917    LayoutUniform(String),
1918}
1919impl GLSLQualifier {
1920    /// Emit the qualifier tokens that precede the type name.
1921    pub fn prefix(&self) -> String {
1922        match self {
1923            GLSLQualifier::None => String::new(),
1924            GLSLQualifier::In => "in ".into(),
1925            GLSLQualifier::Out => "out ".into(),
1926            GLSLQualifier::InOut => "inout ".into(),
1927            GLSLQualifier::Uniform => "uniform ".into(),
1928            GLSLQualifier::Const => "const ".into(),
1929            GLSLQualifier::Flat => "flat in ".into(),
1930            GLSLQualifier::CentroidIn => "centroid in ".into(),
1931            GLSLQualifier::CentroidOut => "centroid out ".into(),
1932            GLSLQualifier::LayoutIn(l) => format!("layout({}) in ", l),
1933            GLSLQualifier::LayoutOut(l) => format!("layout({}) out ", l),
1934            GLSLQualifier::LayoutUniform(l) => format!("layout({}) uniform ", l),
1935        }
1936    }
1937}
1938/// A GLSL uniform buffer object.
1939#[allow(dead_code)]
1940#[derive(Debug, Clone)]
1941pub struct GlslUniformBlock {
1942    pub block_name: String,
1943    pub instance_name: Option<String>,
1944    pub layout: GlslBlockLayout,
1945    pub binding: Option<u32>,
1946    pub members: Vec<GlslBlockMember>,
1947    pub is_ssbo: bool,
1948}
1949#[allow(dead_code)]
1950impl GlslUniformBlock {
1951    pub fn ubo(block_name: impl Into<String>) -> Self {
1952        GlslUniformBlock {
1953            block_name: block_name.into(),
1954            instance_name: None,
1955            layout: GlslBlockLayout::Std140,
1956            binding: None,
1957            members: Vec::new(),
1958            is_ssbo: false,
1959        }
1960    }
1961    pub fn with_binding(mut self, b: u32) -> Self {
1962        self.binding = Some(b);
1963        self
1964    }
1965    pub fn add_member(&mut self, m: GlslBlockMember) {
1966        self.members.push(m);
1967    }
1968    pub fn num_members(&self) -> usize {
1969        self.members.len()
1970    }
1971    pub fn emit(&self) -> String {
1972        let layout_str = self.layout.qualifier_str();
1973        let mut out = if let Some(b) = self.binding {
1974            format!("layout({}, binding = {}) ", layout_str, b)
1975        } else {
1976            format!("layout({}) ", layout_str)
1977        };
1978        let kw = if self.is_ssbo { "buffer" } else { "uniform" };
1979        out.push_str(&format!("{} {} {{\n", kw, self.block_name));
1980        for m in &self.members {
1981            let ty_str = m.ty.keyword();
1982            if let Some(sz) = m.array_size {
1983                out.push_str(&format!("    {} {}[{}];\n", ty_str, m.name, sz));
1984            } else {
1985                out.push_str(&format!("    {} {};\n", ty_str, m.name));
1986            }
1987        }
1988        if let Some(ref inst) = self.instance_name {
1989            out.push_str(&format!("}} {};\n", inst));
1990        } else {
1991            out.push_str("};\n");
1992        }
1993        out
1994    }
1995}
1996/// Supported GLSL version targets.
1997#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1998pub enum GLSLVersion {
1999    /// GLSL 1.20 — OpenGL 2.1 (legacy)
2000    V120,
2001    /// GLSL 3.30 — OpenGL 3.3 (core profile, widespread)
2002    V330,
2003    /// GLSL 4.50 — OpenGL 4.5 (DSA, direct state access era)
2004    V450,
2005    /// GLSL 4.60 — OpenGL 4.6 / SPIR-V interop
2006    V460,
2007}
2008impl GLSLVersion {
2009    /// Return the integer version number used in `#version` directives.
2010    pub fn number(self) -> u32 {
2011        match self {
2012            GLSLVersion::V120 => 120,
2013            GLSLVersion::V330 => 330,
2014            GLSLVersion::V450 => 450,
2015            GLSLVersion::V460 => 460,
2016        }
2017    }
2018    /// Return the profile string appended after the version number.
2019    ///
2020    /// GLSL 1.20 predates the core/compatibility split; later versions
2021    /// default to `core`.
2022    pub fn profile(self) -> &'static str {
2023        match self {
2024            GLSLVersion::V120 => "",
2025            _ => " core",
2026        }
2027    }
2028    /// Emit the full `#version` line.
2029    pub fn version_line(self) -> String {
2030        format!("#version {}{}", self.number(), self.profile())
2031    }
2032    /// Whether this version supports explicit `layout(location = …)` qualifiers.
2033    pub fn supports_layout_location(self) -> bool {
2034        self.number() >= 330
2035    }
2036    /// Whether this version supports compute shaders.
2037    pub fn supports_compute(self) -> bool {
2038        self.number() >= 430
2039    }
2040    /// Whether this version supports `uint` / `uvec*` types.
2041    pub fn supports_uint(self) -> bool {
2042        self.number() >= 130
2043    }
2044}
2045/// GLSL precision qualifier (relevant for mediump / highp / lowp).
2046#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2047pub enum GLSLPrecision {
2048    Low,
2049    Medium,
2050    High,
2051}
2052/// Pass registry for GLSLExt.
2053#[allow(dead_code)]
2054#[derive(Debug, Default)]
2055pub struct GLSLExtPassRegistry {
2056    pub(super) configs: Vec<GLSLExtPassConfig>,
2057    pub(super) stats: Vec<GLSLExtPassStats>,
2058}
2059impl GLSLExtPassRegistry {
2060    #[allow(dead_code)]
2061    pub fn new() -> Self {
2062        Self::default()
2063    }
2064    #[allow(dead_code)]
2065    pub fn register(&mut self, c: GLSLExtPassConfig) {
2066        self.stats.push(GLSLExtPassStats::new());
2067        self.configs.push(c);
2068    }
2069    #[allow(dead_code)]
2070    pub fn len(&self) -> usize {
2071        self.configs.len()
2072    }
2073    #[allow(dead_code)]
2074    pub fn is_empty(&self) -> bool {
2075        self.configs.is_empty()
2076    }
2077    #[allow(dead_code)]
2078    pub fn get(&self, i: usize) -> Option<&GLSLExtPassConfig> {
2079        self.configs.get(i)
2080    }
2081    #[allow(dead_code)]
2082    pub fn get_stats(&self, i: usize) -> Option<&GLSLExtPassStats> {
2083        self.stats.get(i)
2084    }
2085    #[allow(dead_code)]
2086    pub fn enabled_passes(&self) -> Vec<&GLSLExtPassConfig> {
2087        self.configs.iter().filter(|c| c.enabled).collect()
2088    }
2089    #[allow(dead_code)]
2090    pub fn passes_in_phase(&self, ph: &GLSLExtPassPhase) -> Vec<&GLSLExtPassConfig> {
2091        self.configs
2092            .iter()
2093            .filter(|c| c.enabled && &c.phase == ph)
2094            .collect()
2095    }
2096    #[allow(dead_code)]
2097    pub fn total_nodes_visited(&self) -> usize {
2098        self.stats.iter().map(|s| s.nodes_visited).sum()
2099    }
2100    #[allow(dead_code)]
2101    pub fn any_changed(&self) -> bool {
2102        self.stats.iter().any(|s| s.changed)
2103    }
2104}
2105/// Category of a GLSL built-in function.
2106#[allow(dead_code)]
2107#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2108pub enum GlslBuiltinCategory {
2109    Trigonometric,
2110    Exponential,
2111    Common,
2112    GeometricVector,
2113    MatrixOp,
2114    TextureSampling,
2115    Atomic,
2116}
2117/// A simple GLSL expression, sufficient for code-generation purposes.
2118#[derive(Debug, Clone)]
2119pub enum GLSLExpr {
2120    /// A literal number or boolean token.
2121    Literal(String),
2122    /// A variable reference.
2123    Var(String),
2124    /// A binary infix operation.
2125    BinOp {
2126        op: String,
2127        lhs: Box<GLSLExpr>,
2128        rhs: Box<GLSLExpr>,
2129    },
2130    /// A unary prefix operation.
2131    UnaryOp { op: String, operand: Box<GLSLExpr> },
2132    /// A function/constructor call.
2133    Call { func: String, args: Vec<GLSLExpr> },
2134    /// A field access (e.g. `v.xy`).
2135    Field { base: Box<GLSLExpr>, field: String },
2136    /// An array index access.
2137    Index {
2138        base: Box<GLSLExpr>,
2139        index: Box<GLSLExpr>,
2140    },
2141    /// A ternary `cond ? then : else` expression.
2142    Ternary {
2143        cond: Box<GLSLExpr>,
2144        then_expr: Box<GLSLExpr>,
2145        else_expr: Box<GLSLExpr>,
2146    },
2147}
2148impl GLSLExpr {
2149    /// Emit this expression as a GLSL source string.
2150    pub fn emit(&self) -> String {
2151        match self {
2152            GLSLExpr::Literal(s) => s.clone(),
2153            GLSLExpr::Var(name) => name.clone(),
2154            GLSLExpr::BinOp { op, lhs, rhs } => {
2155                format!("({} {} {})", lhs.emit(), op, rhs.emit())
2156            }
2157            GLSLExpr::UnaryOp { op, operand } => format!("({}{})", op, operand.emit()),
2158            GLSLExpr::Call { func, args } => {
2159                let arg_strs: Vec<String> = args.iter().map(|a| a.emit()).collect();
2160                format!("{}({})", func, arg_strs.join(", "))
2161            }
2162            GLSLExpr::Field { base, field } => format!("{}.{}", base.emit(), field),
2163            GLSLExpr::Index { base, index } => {
2164                format!("{}[{}]", base.emit(), index.emit())
2165            }
2166            GLSLExpr::Ternary {
2167                cond,
2168                then_expr,
2169                else_expr,
2170            } => {
2171                format!(
2172                    "({} ? {} : {})",
2173                    cond.emit(),
2174                    then_expr.emit(),
2175                    else_expr.emit()
2176                )
2177            }
2178        }
2179    }
2180    /// Convenience: build a binary operation.
2181    pub fn binop(op: impl Into<String>, lhs: GLSLExpr, rhs: GLSLExpr) -> Self {
2182        GLSLExpr::BinOp {
2183            op: op.into(),
2184            lhs: Box::new(lhs),
2185            rhs: Box::new(rhs),
2186        }
2187    }
2188    /// Convenience: build a function/constructor call.
2189    pub fn call(func: impl Into<String>, args: Vec<GLSLExpr>) -> Self {
2190        GLSLExpr::Call {
2191            func: func.into(),
2192            args,
2193        }
2194    }
2195    /// Convenience: build a variable expression.
2196    pub fn var(name: impl Into<String>) -> Self {
2197        GLSLExpr::Var(name.into())
2198    }
2199    /// Convenience: build a float literal.
2200    pub fn float(v: f32) -> Self {
2201        GLSLExpr::Literal(format!("{:.6}", v))
2202    }
2203}
2204#[allow(dead_code)]
2205#[derive(Debug, Clone)]
2206pub struct GLSLAnalysisCache {
2207    pub(super) entries: std::collections::HashMap<String, GLSLCacheEntry>,
2208    pub(super) max_size: usize,
2209    pub(super) hits: u64,
2210    pub(super) misses: u64,
2211}
2212impl GLSLAnalysisCache {
2213    #[allow(dead_code)]
2214    pub fn new(max_size: usize) -> Self {
2215        GLSLAnalysisCache {
2216            entries: std::collections::HashMap::new(),
2217            max_size,
2218            hits: 0,
2219            misses: 0,
2220        }
2221    }
2222    #[allow(dead_code)]
2223    pub fn get(&mut self, key: &str) -> Option<&GLSLCacheEntry> {
2224        if self.entries.contains_key(key) {
2225            self.hits += 1;
2226            self.entries.get(key)
2227        } else {
2228            self.misses += 1;
2229            None
2230        }
2231    }
2232    #[allow(dead_code)]
2233    pub fn insert(&mut self, key: String, data: Vec<u8>) {
2234        if self.entries.len() >= self.max_size {
2235            if let Some(oldest) = self.entries.keys().next().cloned() {
2236                self.entries.remove(&oldest);
2237            }
2238        }
2239        self.entries.insert(
2240            key.clone(),
2241            GLSLCacheEntry {
2242                key,
2243                data,
2244                timestamp: 0,
2245                valid: true,
2246            },
2247        );
2248    }
2249    #[allow(dead_code)]
2250    pub fn invalidate(&mut self, key: &str) {
2251        if let Some(entry) = self.entries.get_mut(key) {
2252            entry.valid = false;
2253        }
2254    }
2255    #[allow(dead_code)]
2256    pub fn clear(&mut self) {
2257        self.entries.clear();
2258    }
2259    #[allow(dead_code)]
2260    pub fn hit_rate(&self) -> f64 {
2261        let total = self.hits + self.misses;
2262        if total == 0 {
2263            return 0.0;
2264        }
2265        self.hits as f64 / total as f64
2266    }
2267    #[allow(dead_code)]
2268    pub fn size(&self) -> usize {
2269        self.entries.len()
2270    }
2271}
2272/// A single GLSL variable (global or function parameter).
2273#[derive(Debug, Clone)]
2274pub struct GLSLVariable {
2275    /// Variable name.
2276    pub name: String,
2277    /// GLSL type.
2278    pub ty: GLSLType,
2279    /// Storage / parameter qualifier.
2280    pub qualifier: GLSLQualifier,
2281    /// Optional initialiser expression (e.g. for `const` variables).
2282    pub initializer: Option<String>,
2283}
2284impl GLSLVariable {
2285    /// Create a new variable with the given name, type, and qualifier.
2286    pub fn new(name: impl Into<String>, ty: GLSLType, qualifier: GLSLQualifier) -> Self {
2287        GLSLVariable {
2288            name: name.into(),
2289            ty,
2290            qualifier,
2291            initializer: None,
2292        }
2293    }
2294    /// Create a uniform variable.
2295    pub fn uniform(name: impl Into<String>, ty: GLSLType) -> Self {
2296        Self::new(name, ty, GLSLQualifier::Uniform)
2297    }
2298    /// Create a stage input variable.
2299    pub fn input(name: impl Into<String>, ty: GLSLType) -> Self {
2300        Self::new(name, ty, GLSLQualifier::In)
2301    }
2302    /// Create a stage output variable.
2303    pub fn output(name: impl Into<String>, ty: GLSLType) -> Self {
2304        Self::new(name, ty, GLSLQualifier::Out)
2305    }
2306    /// Create a layout-qualified input variable with the given location index.
2307    pub fn layout_input(name: impl Into<String>, ty: GLSLType, location: u32) -> Self {
2308        Self::new(
2309            name,
2310            ty,
2311            GLSLQualifier::LayoutIn(format!("location = {}", location)),
2312        )
2313    }
2314    /// Create a layout-qualified output variable with the given location index.
2315    pub fn layout_output(name: impl Into<String>, ty: GLSLType, location: u32) -> Self {
2316        Self::new(
2317            name,
2318            ty,
2319            GLSLQualifier::LayoutOut(format!("location = {}", location)),
2320        )
2321    }
2322    /// Emit the declaration as a top-level global statement.
2323    pub fn emit_global(&self) -> String {
2324        match &self.initializer {
2325            Some(init) => {
2326                format!(
2327                    "{}{} {} = {};",
2328                    self.qualifier.prefix(),
2329                    self.ty,
2330                    self.name,
2331                    init
2332                )
2333            }
2334            None => format!("{}{} {};", self.qualifier.prefix(), self.ty, self.name),
2335        }
2336    }
2337    /// Emit the declaration as a function parameter (no semicolon, no initializer).
2338    pub fn emit_param(&self) -> String {
2339        format!("{}{} {}", self.qualifier.prefix(), self.ty, self.name)
2340    }
2341}
2342/// Validates GLSL swizzle expressions.
2343#[allow(dead_code)]
2344pub struct GlslSwizzleValidator;
2345impl GlslSwizzleValidator {
2346    /// Validate a swizzle mask for a vector of `components` elements.
2347    pub fn validate(mask: &str, components: usize) -> Result<usize, String> {
2348        if mask.is_empty() || mask.len() > 4 {
2349            return Err(format!(
2350                "swizzle mask length {} is not in [1,4]",
2351                mask.len()
2352            ));
2353        }
2354        let xyzw = ['x', 'y', 'z', 'w'];
2355        let rgba = ['r', 'g', 'b', 'a'];
2356        let stpq = ['s', 't', 'p', 'q'];
2357        let first = mask
2358            .chars()
2359            .next()
2360            .expect("mask is non-empty; checked at function entry");
2361        let set: &[char] = if xyzw.contains(&first) {
2362            &xyzw[..components]
2363        } else if rgba.contains(&first) {
2364            &rgba[..components]
2365        } else if stpq.contains(&first) {
2366            &stpq[..components]
2367        } else {
2368            return Err(format!("unknown swizzle character '{}'", first));
2369        };
2370        for ch in mask.chars() {
2371            if !set.contains(&ch) {
2372                return Err(format!(
2373                    "swizzle char '{}' out of range for {}-component vector",
2374                    ch, components
2375                ));
2376            }
2377        }
2378        Ok(mask.len())
2379    }
2380}
2381/// Configuration for GLSLExt passes.
2382#[allow(dead_code)]
2383#[derive(Debug, Clone)]
2384pub struct GLSLExtPassConfig {
2385    pub name: String,
2386    pub phase: GLSLExtPassPhase,
2387    pub enabled: bool,
2388    pub max_iterations: usize,
2389    pub debug: u32,
2390    pub timeout_ms: Option<u64>,
2391}
2392impl GLSLExtPassConfig {
2393    #[allow(dead_code)]
2394    pub fn new(name: impl Into<String>) -> Self {
2395        Self {
2396            name: name.into(),
2397            phase: GLSLExtPassPhase::Middle,
2398            enabled: true,
2399            max_iterations: 100,
2400            debug: 0,
2401            timeout_ms: None,
2402        }
2403    }
2404    #[allow(dead_code)]
2405    pub fn with_phase(mut self, phase: GLSLExtPassPhase) -> Self {
2406        self.phase = phase;
2407        self
2408    }
2409    #[allow(dead_code)]
2410    pub fn with_max_iter(mut self, n: usize) -> Self {
2411        self.max_iterations = n;
2412        self
2413    }
2414    #[allow(dead_code)]
2415    pub fn with_debug(mut self, d: u32) -> Self {
2416        self.debug = d;
2417        self
2418    }
2419    #[allow(dead_code)]
2420    pub fn disabled(mut self) -> Self {
2421        self.enabled = false;
2422        self
2423    }
2424    #[allow(dead_code)]
2425    pub fn with_timeout(mut self, ms: u64) -> Self {
2426        self.timeout_ms = Some(ms);
2427        self
2428    }
2429    #[allow(dead_code)]
2430    pub fn is_debug_enabled(&self) -> bool {
2431        self.debug > 0
2432    }
2433}