Skip to main content

oxihuman_export/
mesh_shader_export.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! Mesh shader stub export (DirectX 12 / Vulkan mesh shading pipeline).
6
7/// A mesh shading pipeline stage.
8#[derive(Clone, Copy, PartialEq)]
9pub enum MeshShaderStage {
10    Task,
11    Mesh,
12    Pixel,
13}
14
15impl MeshShaderStage {
16    pub fn name(&self) -> &'static str {
17        match self {
18            MeshShaderStage::Task => "task",
19            MeshShaderStage::Mesh => "mesh",
20            MeshShaderStage::Pixel => "pixel",
21        }
22    }
23}
24
25/// A mesh shader program.
26pub struct MeshShaderProgram {
27    pub stage: MeshShaderStage,
28    pub entry_point: String,
29    pub source: String,
30    pub max_vertices: u32,
31    pub max_primitives: u32,
32}
33
34/// A mesh shader pipeline export.
35pub struct MeshShaderExport {
36    pub programs: Vec<MeshShaderProgram>,
37    pub amplification_factor: u32,
38}
39
40/// Create a new mesh shader export.
41pub fn new_mesh_shader_export() -> MeshShaderExport {
42    MeshShaderExport {
43        programs: Vec::new(),
44        amplification_factor: 1,
45    }
46}
47
48/// Add a mesh shader program.
49#[allow(clippy::too_many_arguments)]
50pub fn add_mesh_shader_program(
51    exp: &mut MeshShaderExport,
52    stage: MeshShaderStage,
53    entry: &str,
54    source: &str,
55    max_verts: u32,
56    max_prims: u32,
57) {
58    exp.programs.push(MeshShaderProgram {
59        stage,
60        entry_point: entry.to_string(),
61        source: source.to_string(),
62        max_vertices: max_verts,
63        max_primitives: max_prims,
64    });
65}
66
67/// Program count.
68pub fn mesh_shader_program_count(exp: &MeshShaderExport) -> usize {
69    exp.programs.len()
70}
71
72/// Find a program by stage.
73pub fn find_mesh_shader_program(
74    exp: &MeshShaderExport,
75    stage: MeshShaderStage,
76) -> Option<&MeshShaderProgram> {
77    exp.programs.iter().find(|p| p.stage == stage)
78}
79
80/// Validate (must have a mesh stage).
81pub fn validate_mesh_shader_export(exp: &MeshShaderExport) -> bool {
82    find_mesh_shader_program(exp, MeshShaderStage::Mesh).is_some()
83}
84
85/// Render a pipeline summary.
86pub fn render_mesh_shader_summary(exp: &MeshShaderExport) -> String {
87    let stages: Vec<&str> = exp.programs.iter().map(|p| p.stage.name()).collect();
88    format!(
89        "MeshShader stages:[{}] amplification:{}",
90        stages.join(","),
91        exp.amplification_factor
92    )
93}
94
95#[cfg(test)]
96mod tests {
97    use super::*;
98
99    #[test]
100    fn new_export_empty() {
101        let exp = new_mesh_shader_export();
102        assert_eq!(mesh_shader_program_count(&exp), 0 /* empty */);
103    }
104
105    #[test]
106    fn add_program_increments() {
107        let mut exp = new_mesh_shader_export();
108        add_mesh_shader_program(&mut exp, MeshShaderStage::Mesh, "MeshMain", "", 256, 256);
109        assert_eq!(mesh_shader_program_count(&exp), 1 /* one program */);
110    }
111
112    #[test]
113    fn stage_name_correct() {
114        assert_eq!(MeshShaderStage::Task.name(), "task" /* task stage */);
115    }
116
117    #[test]
118    fn find_program_by_stage() {
119        let mut exp = new_mesh_shader_export();
120        add_mesh_shader_program(&mut exp, MeshShaderStage::Pixel, "PSMain", "", 0, 0);
121        assert!(find_mesh_shader_program(&exp, MeshShaderStage::Pixel).is_some() /* found */);
122    }
123
124    #[test]
125    fn find_missing_none() {
126        let exp = new_mesh_shader_export();
127        assert!(find_mesh_shader_program(&exp, MeshShaderStage::Task).is_none() /* not found */);
128    }
129
130    #[test]
131    fn validate_needs_mesh_stage() {
132        let mut exp = new_mesh_shader_export();
133        assert!(!validate_mesh_shader_export(&exp) /* no mesh stage */);
134        add_mesh_shader_program(&mut exp, MeshShaderStage::Mesh, "MeshMain", "", 64, 42);
135        assert!(validate_mesh_shader_export(&exp) /* now valid */);
136    }
137
138    #[test]
139    fn render_summary_contains_stage_names() {
140        let mut exp = new_mesh_shader_export();
141        add_mesh_shader_program(&mut exp, MeshShaderStage::Mesh, "m", "", 64, 64);
142        add_mesh_shader_program(&mut exp, MeshShaderStage::Pixel, "p", "", 0, 0);
143        let s = render_mesh_shader_summary(&exp);
144        assert!(s.contains("mesh") /* mesh stage */);
145        assert!(s.contains("pixel") /* pixel stage */);
146    }
147
148    #[test]
149    fn max_vertices_stored() {
150        let mut exp = new_mesh_shader_export();
151        add_mesh_shader_program(&mut exp, MeshShaderStage::Mesh, "m", "", 128, 84);
152        let p = find_mesh_shader_program(&exp, MeshShaderStage::Mesh).expect("should succeed");
153        assert_eq!(p.max_vertices, 128 /* correct */);
154    }
155
156    #[test]
157    fn amplification_factor_default_one() {
158        let exp = new_mesh_shader_export();
159        assert_eq!(exp.amplification_factor, 1 /* default */);
160    }
161}