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().ok_or(RenderError::ErrorInitializingCompiler)?;
50 let mut options =
51 shaderc::CompileOptions::new().ok_or(RenderError::ErrorInitializingCompiler)?;
52
53 if optimize {
54 options.set_optimization_level(shaderc::OptimizationLevel::Performance);
55 info!("Enabled optimization by shaderc.");
56 } else {
57 options.set_optimization_level(shaderc::OptimizationLevel::Zero);
58 info!("Disabled optimization by shaderc.");
59 }
60 options.set_forced_version_profile(430, shaderc::GlslProfile::Core);
61 options.set_include_callback(move |name, _, shader_name, _| {
63 Ok(shaderc::ResolvedInclude {
64 resolved_name: name.to_string(),
65 content: resolve_include(name, shader_name)?,
66 })
67 });
68
69 Ok(Self { compiler, options })
70 }
71}
72
73impl Compiler for ShaderCCompiler {
74 fn create_shader_module(
75 &mut self,
76 device: &wgpu::Device,
77 source: &str,
78 stage: ShaderStage,
79 name: &str,
80 ) -> Result<wgpu::ShaderModule, RenderError> {
81 prof_span!(_guard, "create_shader_modules");
82 use std::borrow::Cow;
83
84 let file_name = format!("{}.glsl", name);
85 let file_name = file_name.as_str();
86
87 let spv = self
88 .compiler
89 .compile_into_spirv(source, stage.into(), file_name, "main", Some(&self.options))
90 .map_err(|e| (file_name, e))?;
91
92 let descriptor = wgpu::ShaderModuleDescriptor {
108 label: Some(file_name),
109 source: wgpu::ShaderSource::SpirV(Cow::Borrowed(spv.as_binary())),
110 };
111 let runtimechecks = wgpu::ShaderRuntimeChecks::unchecked();
112 #[expect(unsafe_code)]
113 Ok(unsafe { device.create_shader_module_trusted(descriptor, runtimechecks) })
114 }
115}
116
117pub(super) struct WgpuCompiler {
118 reg: regex::Regex,
119 resolve_include: Box<dyn Fn(&str, &str) -> Result<String, String> + 'static>,
120}
121
122impl WgpuCompiler {
123 pub(super) fn new(
124 resolve_include: impl Fn(&str, &str) -> Result<String, String> + 'static,
125 ) -> Result<Self, RenderError> {
126 let reg = regex::Regex::new("(?mR)^#include +<(.+)>$").unwrap();
127 Ok(Self {
128 reg,
129 resolve_include: Box::new(resolve_include),
130 })
131 }
132}
133
134impl Compiler for WgpuCompiler {
135 fn create_shader_module(
136 &mut self,
137 device: &wgpu::Device,
138 source: &str,
139 stage: ShaderStage,
140 name: &str,
141 ) -> Result<wgpu::ShaderModule, RenderError> {
142 use std::borrow::Cow;
143
144 prof_span!(_guard, "create_shader_modules");
145
146 let label = name;
147
148 device.push_error_scope(wgpu::ErrorFilter::Validation);
149
150 let mut source = Cow::Borrowed(source);
152 let source = loop {
153 let resolve_includes = self.reg.replace_all(&source, |cap: ®ex::Captures| {
154 (self.resolve_include)(cap.get(1).unwrap().as_str(), name).unwrap() });
156
157 match resolve_includes {
158 Cow::Borrowed(source) => break source,
159 Cow::Owned(s) => source = Cow::Owned(s),
160 }
161 };
162
163 let descriptor = wgpu::ShaderModuleDescriptor {
164 label: Some(label),
165 source: wgpu::ShaderSource::Glsl {
166 shader: Cow::Borrowed(source),
167 stage: stage.into(),
168 defines: &[],
169 },
170 };
171 let runtimechecks = wgpu::ShaderRuntimeChecks::unchecked();
172 #[expect(unsafe_code)]
173 let shader = unsafe { device.create_shader_module_trusted(descriptor, runtimechecks) };
174
175 let rt = tokio::runtime::Runtime::new().unwrap();
176
177 if let Some(error) = rt.block_on(device.pop_error_scope()) {
178 Err(RenderError::ShaderWgpuError(label.to_owned(), error))
179 } else {
180 Ok(shader)
181 }
182 }
183}