veloren_voxygen/render/renderer/
compiler.rs1use common_base::prof_span;
2use tracing::info;
3
4use crate::render::RenderError;
5
6pub(super) enum ShaderStage {
7 Vertex,
8 Fragment,
9}
10
11impl From<ShaderStage> for wgpu::naga::ShaderStage {
12 fn from(value: ShaderStage) -> Self {
13 match value {
14 ShaderStage::Vertex => wgpu::naga::ShaderStage::Vertex,
15 ShaderStage::Fragment => wgpu::naga::ShaderStage::Fragment,
16 }
17 }
18}
19
20impl From<ShaderStage> for shaderc::ShaderKind {
21 fn from(value: ShaderStage) -> Self {
22 match value {
23 ShaderStage::Vertex => shaderc::ShaderKind::Vertex,
24 ShaderStage::Fragment => shaderc::ShaderKind::Fragment,
25 }
26 }
27}
28
29pub(super) trait Compiler {
30 fn create_shader_module(
31 &mut self,
32 device: &wgpu::Device,
33 source: &str,
34 stage: ShaderStage,
35 name: &str,
36 ) -> Result<wgpu::ShaderModule, RenderError>;
37}
38
39pub(super) struct ShaderCCompiler {
40 compiler: shaderc::Compiler,
41 options: shaderc::CompileOptions<'static>,
42}
43
44impl ShaderCCompiler {
45 pub(super) fn new(
46 optimize: bool,
47 resolve_include: impl Fn(&str, &str) -> Result<String, String> + 'static,
48 ) -> Result<Self, RenderError> {
49 let compiler = shaderc::Compiler::new()?;
50 let mut options = shaderc::CompileOptions::new()?;
51
52 if optimize {
53 options.set_optimization_level(shaderc::OptimizationLevel::Performance);
54 info!("Enabled optimization by shaderc.");
55 } else {
56 options.set_optimization_level(shaderc::OptimizationLevel::Zero);
57 info!("Disabled optimization by shaderc.");
58 }
59 options.set_forced_version_profile(430, shaderc::GlslProfile::Core);
60 options.set_include_callback(move |name, _, shader_name, _| {
62 Ok(shaderc::ResolvedInclude {
63 resolved_name: name.to_string(),
64 content: resolve_include(name, shader_name)?,
65 })
66 });
67
68 Ok(Self { compiler, options })
69 }
70}
71
72impl Compiler for ShaderCCompiler {
73 fn create_shader_module(
74 &mut self,
75 device: &wgpu::Device,
76 source: &str,
77 stage: ShaderStage,
78 name: &str,
79 ) -> Result<wgpu::ShaderModule, RenderError> {
80 prof_span!(_guard, "create_shader_modules");
81 use std::borrow::Cow;
82
83 let file_name = format!("{}.glsl", name);
84 let file_name = file_name.as_str();
85
86 let spv = self
87 .compiler
88 .compile_into_spirv(source, stage.into(), file_name, "main", Some(&self.options))
89 .map_err(|e| (file_name, e))?;
90
91 let descriptor = wgpu::ShaderModuleDescriptor {
107 label: Some(file_name),
108 source: wgpu::ShaderSource::SpirV(Cow::Borrowed(spv.as_binary())),
109 };
110 let runtimechecks = wgpu::ShaderRuntimeChecks::unchecked();
111 #[expect(unsafe_code)]
112 Ok(unsafe { device.create_shader_module_trusted(descriptor, runtimechecks) })
113 }
114}
115
116pub(super) struct WgpuCompiler {
117 reg: regex::Regex,
118 resolve_include: Box<dyn Fn(&str, &str) -> Result<String, String> + 'static>,
119}
120
121impl WgpuCompiler {
122 pub(super) fn new(
123 resolve_include: impl Fn(&str, &str) -> Result<String, String> + 'static,
124 ) -> Result<Self, RenderError> {
125 let reg = regex::Regex::new("(?mR)^#include +<(.+)>$").unwrap();
126 Ok(Self {
127 reg,
128 resolve_include: Box::new(resolve_include),
129 })
130 }
131}
132
133impl Compiler for WgpuCompiler {
134 fn create_shader_module(
135 &mut self,
136 device: &wgpu::Device,
137 source: &str,
138 stage: ShaderStage,
139 name: &str,
140 ) -> Result<wgpu::ShaderModule, RenderError> {
141 use std::borrow::Cow;
142
143 prof_span!(_guard, "create_shader_modules");
144
145 let label = name;
146
147 device.push_error_scope(wgpu::ErrorFilter::Validation);
148
149 let mut source = Cow::Borrowed(source);
151 let source = loop {
152 let resolve_includes = self.reg.replace_all(&source, |cap: ®ex::Captures| {
153 (self.resolve_include)(cap.get(1).unwrap().as_str(), name).unwrap() });
155
156 match resolve_includes {
157 Cow::Borrowed(source) => break source,
158 Cow::Owned(s) => source = Cow::Owned(s),
159 }
160 };
161
162 let descriptor = wgpu::ShaderModuleDescriptor {
163 label: Some(label),
164 source: wgpu::ShaderSource::Glsl {
165 shader: Cow::Borrowed(source),
166 stage: stage.into(),
167 defines: &[],
168 },
169 };
170 let runtimechecks = wgpu::ShaderRuntimeChecks::unchecked();
171 #[expect(unsafe_code)]
172 let shader = unsafe { device.create_shader_module_trusted(descriptor, runtimechecks) };
173
174 let rt = tokio::runtime::Runtime::new().unwrap();
175
176 if let Some(error) = rt.block_on(device.pop_error_scope()) {
177 Err(RenderError::ShaderWgpuError(label.to_owned(), error))
178 } else {
179 Ok(shader)
180 }
181 }
182}