1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
//! Shader templates for the geometry pass.
use askama::Template;
use crate::{
render_passes::geometry::shader::cache_key::ShaderCacheKeyGeometry,
shaders::{AwsmShaderError, Result},
};
/// Geometry pass shader template components.
#[derive(Debug)]
pub struct ShaderTemplateGeometry {
pub bind_groups: ShaderTemplateGeometryBindGroups,
pub vertex: ShaderTemplateGeometryVertex,
pub fragment: ShaderTemplateGeometryFragment,
}
/// Bind group template for the geometry pass.
#[derive(Template, Debug)]
#[template(path = "geometry_wgsl/bind_groups.wgsl", whitespace = "minimize")]
pub struct ShaderTemplateGeometryBindGroups {
/// `true` when `@group(2) @binding(0)` is a `storage, read` array
/// of `GeometryMeshMeta` indexed by `@builtin(instance_index)`
/// (requires the `indirect-first-instance` WebGPU feature on the
/// device). `false` when it's a uniform with dynamic offset (the
/// portable shape; also the only shape used by the instanced
/// path).
meta_storage_array: bool,
}
impl ShaderTemplateGeometryBindGroups {
/// Creates a bind group template from the cache key.
pub fn new(cache_key: &ShaderCacheKeyGeometry) -> Self {
Self {
meta_storage_array: cache_key.meta_storage_array,
}
}
}
/// Vertex shader template for the geometry pass.
#[derive(Template, Debug)]
#[template(path = "geometry_wgsl/vertex.wgsl", whitespace = "minimize")]
pub struct ShaderTemplateGeometryVertex {
max_morph_unroll: u32,
max_skin_unroll: u32,
instancing_transforms: bool,
/// Same flag as on [`ShaderTemplateGeometryBindGroups`] — when
/// true, the vertex `main` loads `geometry_mesh_meta` from the
/// storage array via `@builtin(instance_index)`. When false, the
/// uniform binding is already populated (set via dynamic offset
/// by the CPU) and no shader-side load is needed.
meta_storage_array: bool,
/// `true` for a per-material custom-vertex pipeline — renders the gated
/// `custom_displace_vertex` hook (the struct/loader/body fields below feed
/// it). Driven by the cache key's `dynamic_vertex_shader`; `false` (the
/// default for every shared-pipeline mesh) keeps the output byte-identical.
has_custom_vertex: bool,
/// The author's WGSL displacement body, wrapped into
/// `custom_displace_vertex` at render time. Empty unless `has_custom_vertex`.
dynamic_wgsl_vertex: String,
/// Auto-generated `struct MaterialData { ... }` decl for the hook.
/// Empty unless `has_custom_vertex`.
dynamic_vertex_struct_decl: String,
/// Auto-generated `material_data_load` accessor for the hook.
/// Empty unless `has_custom_vertex`.
dynamic_vertex_loader_decl: String,
}
impl ShaderTemplateGeometryVertex {
/// Creates a vertex shader template from the cache key.
pub fn new(cache_key: &ShaderCacheKeyGeometry) -> Self {
let dv = cache_key.dynamic_vertex_shader.as_ref();
Self {
max_morph_unroll: 2,
max_skin_unroll: 2,
instancing_transforms: cache_key.instancing_transforms,
meta_storage_array: cache_key.meta_storage_array,
has_custom_vertex: dv.is_some(),
dynamic_wgsl_vertex: dv.map(|d| d.wgsl_vertex.clone()).unwrap_or_default(),
dynamic_vertex_struct_decl: dv.map(|d| d.struct_decl.clone()).unwrap_or_default(),
dynamic_vertex_loader_decl: dv.map(|d| d.loader_decl.clone()).unwrap_or_default(),
}
}
}
/// Fragment shader template for the geometry pass.
#[derive(Template, Debug)]
#[template(path = "geometry_wgsl/fragment.wgsl", whitespace = "minimize")]
pub struct ShaderTemplateGeometryFragment {}
impl ShaderTemplateGeometryFragment {
/// Creates a fragment shader template from the cache key.
pub fn new(_cache_key: &ShaderCacheKeyGeometry) -> Self {
Self {}
}
}
impl TryFrom<&ShaderCacheKeyGeometry> for ShaderTemplateGeometry {
type Error = AwsmShaderError;
fn try_from(value: &ShaderCacheKeyGeometry) -> Result<Self> {
Ok(Self {
bind_groups: ShaderTemplateGeometryBindGroups::new(value),
vertex: ShaderTemplateGeometryVertex::new(value),
fragment: ShaderTemplateGeometryFragment::new(value),
})
}
}
impl ShaderTemplateGeometry {
/// Renders the geometry shader template into WGSL.
pub fn into_source(self) -> Result<String> {
let bind_groups_source = self.bind_groups.render()?;
let vertex_source = self.vertex.render()?;
let fragment_source = self.fragment.render()?;
let source = format!(
"{}\n{}\n{}",
bind_groups_source, vertex_source, fragment_source
);
// print_shader_source(&vertex_source, false);
//print_shader_source(&source, false);
Ok(source)
}
/// Returns an optional debug label for shader compilation.
pub fn debug_label(&self) -> Option<&str> {
Some("Geometry")
}
}