1use std::{
4 collections::HashSet,
5 path::{Path, PathBuf},
6};
7
8use crate::{
9 include::IncludeHandler,
10 position::{ShaderFileRange, ShaderRange},
11 shader::ShaderContextParams,
12 shader_error::ShaderDiagnostic,
13 symbols::{
14 shader_module::ShaderSymbols,
15 symbol_list::{ShaderSymbolList, ShaderSymbolListRef},
16 symbols::{
17 ShaderSymbol, ShaderSymbolData, ShaderSymbolMode, ShaderSymbolRuntime,
18 ShaderSymbolRuntimeContext,
19 },
20 },
21};
22
23#[derive(Debug, Default, Clone)]
24pub struct ShaderRegion {
25 pub range: ShaderRange,
26 pub is_active: bool, }
29
30impl ShaderRegion {
31 pub fn new(range: ShaderRange, is_active: bool) -> Self {
32 Self { range, is_active }
33 }
34}
35
36#[derive(Debug, Default, Clone)]
37pub struct ShaderPreprocessorContext {
38 defines: Vec<ShaderSymbol>,
39 include_handler: IncludeHandler,
40 dirty_files: HashSet<PathBuf>, depth: usize,
42}
43
44impl ShaderPreprocessorContext {
45 pub fn main(file_path: &Path, shader_params: ShaderContextParams) -> Self {
46 Self {
47 defines: shader_params
48 .defines
49 .iter()
50 .map(|(key, value)| ShaderSymbol {
51 label: key.clone(),
52 requirement: None,
53 data: ShaderSymbolData::Macro {
54 value: value.clone(),
55 },
56 mode: ShaderSymbolMode::RuntimeContext(ShaderSymbolRuntimeContext::new()),
57 })
58 .collect(),
59 include_handler: IncludeHandler::main(
60 &file_path,
61 shader_params.includes,
62 shader_params.path_remapping,
63 ),
64 dirty_files: HashSet::new(),
65 depth: 0,
66 }
67 }
68 pub fn mark_dirty(&mut self, file_path: PathBuf) {
69 self.dirty_files.insert(file_path);
70 }
71 pub fn search_path_in_includes(&mut self, path: &Path) -> Option<PathBuf> {
72 self.include_handler.search_path_in_includes(path)
73 }
74 pub fn push_directory_stack(&mut self, canonical_path: &Path) {
75 self.include_handler.push_directory_stack(canonical_path);
76 }
77 pub fn append_defines(&mut self, defines: Vec<ShaderPreprocessorDefine>) {
78 self.defines
79 .extend(defines.iter().map(|define| define.symbol.clone()));
80 }
81 pub fn increase_depth(&mut self) -> bool {
82 if self.depth < IncludeHandler::DEPTH_LIMIT {
83 self.depth += 1;
84 true
85 } else {
86 false
87 }
88 }
89 pub fn decrease_depth(&mut self) {
90 assert!(self.depth > 0, "Decreasing depth but zero.");
91 self.depth -= 1;
92 }
93 pub fn get_visited_count(&mut self, path: &Path) -> usize {
94 self.include_handler.get_visited_count(path)
95 }
96 pub fn is_dirty(&self, file_path: &Path, context: &ShaderPreprocessorContext) -> bool {
97 fn are_defines_equal(lhs: &Vec<ShaderSymbol>, rhs: &Vec<ShaderSymbol>) -> bool {
100 if lhs.len() != rhs.len() {
101 return false;
102 }
103 for lhs_symbol in lhs.iter() {
104 if rhs
105 .iter()
106 .find(|rhs_symbol| {
107 lhs_symbol.label == rhs_symbol.label
108 && match (&lhs_symbol.data, &rhs_symbol.data) {
109 (
110 ShaderSymbolData::Macro { value: l_value },
111 ShaderSymbolData::Macro { value: r_value },
112 ) => l_value == r_value,
113 _ => false,
114 }
115 })
116 .is_none()
117 {
118 return false;
119 }
120 }
121 true
122 }
123 fn are_includes_equal(lhs: &HashSet<PathBuf>, rhs: &HashSet<PathBuf>) -> bool {
124 if lhs.len() != rhs.len() {
125 return false;
126 }
127 for lhs_symbol in lhs.iter() {
128 if rhs
129 .iter()
130 .find(|rhs_symbol| lhs_symbol.as_os_str() == rhs_symbol.as_os_str())
131 .is_none()
132 {
133 return false;
134 }
135 }
136 true
137 }
138 !are_defines_equal(&context.defines, &self.defines)
139 || !are_includes_equal(
140 context.include_handler.get_includes(),
141 self.include_handler.get_includes(),
142 )
143 || context.dirty_files.contains(file_path)
144 }
145 pub fn get_define_value(&self, name: &str) -> Option<String> {
146 self.defines
147 .iter()
148 .find(|symbol| *symbol.label == *name)
149 .map(|symbol| match &symbol.data {
150 ShaderSymbolData::Macro { value } => value.clone(),
151 _ => panic!("Expected ShaderSymbolData::Macro"),
152 })
153 }
154 pub fn get_defines(&self) -> &Vec<ShaderSymbol> {
155 &self.defines
156 }
157}
158
159#[derive(Debug, Clone)]
160pub struct ShaderPreprocessorInclude {
161 pub cache: Option<ShaderSymbols>,
163 symbol: ShaderSymbol,
164}
165
166#[derive(Debug, Clone)]
167pub struct ShaderPreprocessorDefine {
168 symbol: ShaderSymbol,
169}
170
171#[derive(Debug, Default, Clone, PartialEq, Eq)]
172pub enum ShaderPreprocessorMode {
173 #[default]
174 Default,
175 Once,
176 OnceVisited,
177}
178
179#[derive(Debug, Default, Clone)]
180pub struct ShaderPreprocessor {
181 pub context: ShaderPreprocessorContext, pub includes: Vec<ShaderPreprocessorInclude>,
184 pub defines: Vec<ShaderPreprocessorDefine>,
185 pub regions: Vec<ShaderRegion>,
186 pub diagnostics: Vec<ShaderDiagnostic>, pub mode: ShaderPreprocessorMode,
188}
189impl ShaderPreprocessorDefine {
190 pub fn new(name: String, range: ShaderFileRange, value: Option<String>) -> Self {
191 Self {
192 symbol: ShaderSymbol {
193 label: name.clone(),
194 requirement: None,
195 data: ShaderSymbolData::Macro {
196 value: match &value {
197 Some(value) => value.clone(),
198 None => "".into(),
199 },
200 },
201 mode: ShaderSymbolMode::Runtime(ShaderSymbolRuntime::global(
202 range.file_path,
203 range.range,
204 )),
205 },
206 }
207 }
208 pub fn get_file_path(&self) -> &Path {
209 &self.symbol.mode.unwrap_runtime().file_path
210 }
211 pub fn get_range(&self) -> &ShaderRange {
212 &self.symbol.mode.unwrap_runtime().range
213 }
214 pub fn get_name(&self) -> &String {
215 &self.symbol.label
216 }
217 pub fn get_value(&self) -> Option<&String> {
218 match &self.symbol.data {
219 ShaderSymbolData::Macro { value } => Some(value),
220 _ => None,
221 }
222 }
223}
224impl ShaderPreprocessorInclude {
225 pub fn new(relative_path: String, absolute_path: PathBuf, range: ShaderFileRange) -> Self {
226 Self {
227 cache: None,
228 symbol: ShaderSymbol {
229 label: relative_path,
230 requirement: None,
231 data: ShaderSymbolData::Include {
232 target: absolute_path,
233 },
234 mode: ShaderSymbolMode::Runtime(ShaderSymbolRuntime::global(
235 range.file_path,
236 range.range,
237 )),
238 },
239 }
240 }
241 pub fn get_range(&self) -> &ShaderRange {
242 &self.symbol.mode.unwrap_runtime().range
243 }
244 pub fn get_file_range(&self) -> ShaderFileRange {
245 let runtime = self.symbol.mode.unwrap_runtime();
246 runtime.range.clone_into_file(runtime.file_path.clone())
247 }
248 pub fn get_relative_path(&self) -> &String {
249 &self.symbol.label
250 }
251 pub fn get_absolute_path(&self) -> &Path {
252 match &self.symbol.data {
253 ShaderSymbolData::Include { target } => &target,
254 _ => panic!("Expected ShaderSymbolData::Link"),
255 }
256 }
257 pub fn get_cache(&self) -> &ShaderSymbols {
258 self.cache.as_ref().unwrap()
259 }
260 pub fn get_cache_mut(&mut self) -> &mut ShaderSymbols {
261 self.cache.as_mut().unwrap()
262 }
263}
264
265impl ShaderPreprocessor {
266 pub fn new(context: ShaderPreprocessorContext) -> Self {
267 Self {
268 context: context,
269 includes: Vec::new(),
270 defines: Vec::new(),
271 regions: Vec::new(),
272 diagnostics: Vec::new(),
273 mode: ShaderPreprocessorMode::default(),
274 }
275 }
276 pub fn preprocess_symbols<'a>(
277 &'a self,
278 shader_symbols: &'a ShaderSymbolList,
279 ) -> ShaderSymbolListRef<'a> {
280 let inactive_regions: Vec<&ShaderRegion> =
282 self.regions.iter().filter(|r| !r.is_active).collect();
283 let mut preprocessed_symbols =
284 shader_symbols.filter(move |_symbol_type, symbol| match &symbol.mode {
285 ShaderSymbolMode::Runtime(runtime) => inactive_regions
286 .iter()
287 .find(|r| r.range.contain_bounds(&runtime.range))
288 .is_none(),
289 ShaderSymbolMode::RuntimeContext(_) => true, ShaderSymbolMode::Intrinsic(_) => true, });
292 let mut define_symbols: Vec<&ShaderSymbol> =
294 self.defines.iter().map(|define| &define.symbol).collect();
295 let mut include_symbols: Vec<&ShaderSymbol> = self
297 .includes
298 .iter()
299 .map(|include| &include.symbol)
300 .collect();
301 preprocessed_symbols.macros.append(&mut define_symbols);
302 preprocessed_symbols.includes.append(&mut include_symbols);
303 preprocessed_symbols
304 }
305}