1 #![allow(clippy::manual_strip)]
2 #[allow(unused_imports)]
3 use std::fs;
4 use std::{env, error::Error, path::Path};
5
6 #[derive(Default)]
7 struct Parameters {
8 validation_flags: naga::valid::ValidationFlags,
9 #[cfg(feature = "spv-in")]
10 spv_adjust_coordinate_space: bool,
11 #[cfg(feature = "spv-in")]
12 spv_flow_dump_prefix: Option<String>,
13 #[cfg(feature = "spv-out")]
14 spv: naga::back::spv::Options,
15 #[cfg(feature = "msl-out")]
16 msl: naga::back::msl::Options,
17 #[cfg(feature = "glsl-out")]
18 glsl: naga::back::glsl::Options,
19 }
20
21 trait PrettyResult {
22 type Target;
unwrap_pretty(self) -> Self::Target23 fn unwrap_pretty(self) -> Self::Target;
24 }
25
print_err(error: impl Error)26 fn print_err(error: impl Error) {
27 eprintln!("{}:", error);
28 let mut e = error.source();
29 while let Some(source) = e {
30 eprintln!("\t{}", source);
31 e = source.source();
32 }
33 }
34
35 impl<T, E: Error> PrettyResult for Result<T, E> {
36 type Target = T;
unwrap_pretty(self) -> T37 fn unwrap_pretty(self) -> T {
38 match self {
39 Result::Ok(value) => value,
40 Result::Err(error) => {
41 print_err(error);
42 std::process::exit(1);
43 }
44 }
45 }
46 }
47
main()48 fn main() {
49 //env_logger::init(); // uncomment during development
50
51 let mut input_path = None;
52 let mut output_paths = Vec::new();
53 //TODO: read the parameters from RON?
54 #[allow(unused_mut)]
55 let mut params = Parameters::default();
56
57 let mut args = env::args();
58 let _ = args.next().unwrap();
59 #[allow(clippy::while_let_on_iterator)]
60 while let Some(arg) = args.next() {
61 //TODO: use `strip_prefix` when MSRV reaches 1.45.0
62 if arg.starts_with("--") {
63 match &arg[2..] {
64 "validate" => {
65 let value = args.next().unwrap().parse().unwrap();
66 params.validation_flags =
67 naga::valid::ValidationFlags::from_bits(value).unwrap();
68 }
69 #[cfg(feature = "spv-in")]
70 "flow-dir" => params.spv_flow_dump_prefix = args.next(),
71 #[cfg(feature = "glsl-out")]
72 "entry-point" => params.glsl.entry_point = args.next().unwrap(),
73 #[cfg(feature = "glsl-out")]
74 "profile" => {
75 use naga::back::glsl::Version;
76 let string = args.next().unwrap();
77 //TODO: use `strip_prefix` in 1.45.0
78 params.glsl.version = if string.starts_with("core") {
79 Version::Desktop(string[4..].parse().unwrap_or(330))
80 } else if string.starts_with("es") {
81 Version::Embedded(string[2..].parse().unwrap_or(310))
82 } else {
83 panic!("Unknown profile: {}", string)
84 };
85 }
86 other => log::warn!("Unknown parameter: {}", other),
87 }
88 } else if input_path.is_none() {
89 input_path = Some(arg);
90 } else {
91 output_paths.push(arg);
92 }
93 }
94
95 let input_path = match input_path {
96 Some(ref string) => string,
97 None => {
98 println!("Call with <input> <output> [<options>]");
99 return;
100 }
101 };
102 let module = match Path::new(input_path)
103 .extension()
104 .expect("Input has no extension?")
105 .to_str()
106 .unwrap()
107 {
108 #[cfg(feature = "spv-in")]
109 "spv" => {
110 let options = naga::front::spv::Options {
111 adjust_coordinate_space: params.spv_adjust_coordinate_space,
112 strict_capabilities: false,
113 flow_graph_dump_prefix: params.spv_flow_dump_prefix.map(std::path::PathBuf::from),
114 };
115 let input = fs::read(input_path).unwrap();
116 naga::front::spv::parse_u8_slice(&input, &options).unwrap()
117 }
118 #[cfg(feature = "wgsl-in")]
119 "wgsl" => {
120 let input = fs::read_to_string(input_path).unwrap();
121 let result = naga::front::wgsl::parse_str(&input);
122 match result {
123 Ok(v) => v,
124 Err(ref e) => {
125 e.emit_to_stderr(&input);
126 panic!("unable to parse WGSL");
127 }
128 }
129 }
130 #[cfg(feature = "glsl-in")]
131 "vert" => {
132 let input = fs::read_to_string(input_path).unwrap();
133 let mut entry_points = naga::FastHashMap::default();
134 entry_points.insert("main".to_string(), naga::ShaderStage::Vertex);
135 naga::front::glsl::parse_str(
136 &input,
137 &naga::front::glsl::Options {
138 entry_points,
139 defines: Default::default(),
140 },
141 )
142 .unwrap_pretty()
143 }
144 #[cfg(feature = "glsl-in")]
145 "frag" => {
146 let input = fs::read_to_string(input_path).unwrap();
147 let mut entry_points = naga::FastHashMap::default();
148 entry_points.insert("main".to_string(), naga::ShaderStage::Fragment);
149 naga::front::glsl::parse_str(
150 &input,
151 &naga::front::glsl::Options {
152 entry_points,
153 defines: Default::default(),
154 },
155 )
156 .unwrap_pretty()
157 }
158 #[cfg(feature = "glsl-in")]
159 "comp" => {
160 let input = fs::read_to_string(input_path).unwrap();
161 let mut entry_points = naga::FastHashMap::default();
162 entry_points.insert("main".to_string(), naga::ShaderStage::Compute);
163 naga::front::glsl::parse_str(
164 &input,
165 &naga::front::glsl::Options {
166 entry_points,
167 defines: Default::default(),
168 },
169 )
170 .unwrap_pretty()
171 }
172 other => {
173 if true {
174 // prevent "unreachable_code" warnings
175 panic!("Unknown input extension: {}", other);
176 }
177 naga::Module::default()
178 }
179 };
180
181 // validate the IR
182 let info = match naga::valid::Validator::new(
183 params.validation_flags,
184 naga::valid::Capabilities::all(),
185 )
186 .validate(&module)
187 {
188 Ok(info) => Some(info),
189 Err(error) => {
190 print_err(error);
191 None
192 }
193 };
194
195 if output_paths.is_empty() {
196 if info.is_some() {
197 println!("Validation successful");
198 return;
199 } else {
200 std::process::exit(!0);
201 }
202 }
203
204 for output_path in output_paths {
205 match Path::new(&output_path)
206 .extension()
207 .expect("Output has no extension?")
208 .to_str()
209 .unwrap()
210 {
211 "txt" => {
212 use std::io::Write;
213
214 let mut file = fs::File::create(output_path).unwrap();
215 writeln!(file, "{:#?}", module).unwrap();
216 if let Some(ref info) = info {
217 writeln!(file).unwrap();
218 writeln!(file, "{:#?}", info).unwrap();
219 }
220 }
221 #[cfg(feature = "msl-out")]
222 "metal" => {
223 use naga::back::msl;
224
225 let pipeline_options = msl::PipelineOptions::default();
226 let (msl, _) = msl::write_string(
227 &module,
228 info.as_ref().unwrap(),
229 ¶ms.msl,
230 &pipeline_options,
231 )
232 .unwrap_pretty();
233 fs::write(output_path, msl).unwrap();
234 }
235 #[cfg(feature = "spv-out")]
236 "spv" => {
237 use naga::back::spv;
238
239 let spv =
240 spv::write_vec(&module, info.as_ref().unwrap(), ¶ms.spv).unwrap_pretty();
241 let bytes = spv
242 .iter()
243 .fold(Vec::with_capacity(spv.len() * 4), |mut v, w| {
244 v.extend_from_slice(&w.to_le_bytes());
245 v
246 });
247
248 fs::write(output_path, bytes.as_slice()).unwrap();
249 }
250 #[cfg(feature = "glsl-out")]
251 stage @ "vert" | stage @ "frag" | stage @ "comp" => {
252 use naga::back::glsl;
253
254 params.glsl.shader_stage = match stage {
255 "vert" => naga::ShaderStage::Vertex,
256 "frag" => naga::ShaderStage::Fragment,
257 "comp" => naga::ShaderStage::Compute,
258 _ => unreachable!(),
259 };
260
261 let mut buffer = String::new();
262 let mut writer =
263 glsl::Writer::new(&mut buffer, &module, info.as_ref().unwrap(), ¶ms.glsl)
264 .unwrap_pretty();
265 writer.write().unwrap();
266 fs::write(output_path, buffer).unwrap();
267 }
268 #[cfg(feature = "dot-out")]
269 "dot" => {
270 use naga::back::dot;
271
272 let output = dot::write(&module, info.as_ref()).unwrap();
273 fs::write(output_path, output).unwrap();
274 }
275 #[cfg(feature = "hlsl-out")]
276 "hlsl" => {
277 use naga::back::hlsl;
278
279 let hlsl = hlsl::write_string(&module).unwrap_pretty();
280 fs::write(output_path, hlsl).unwrap();
281 }
282 #[cfg(feature = "wgsl-out")]
283 "wgsl" => {
284 use naga::back::wgsl;
285
286 let wgsl = wgsl::write_string(&module, info.as_ref().unwrap()).unwrap_pretty();
287 fs::write(output_path, wgsl).unwrap();
288 }
289 other => {
290 let _ = params;
291 println!(
292 "Unknown output extension: {}, forgot to enable a feature?",
293 other
294 );
295 }
296 }
297 }
298 }
299