mirror of
https://github.com/gfx-rs/wgpu.git
synced 2024-11-21 22:33:49 +00:00
[naga-cli] Add input-kind
and shader-stage
args (#5411)
This commit is contained in:
parent
4e77762a32
commit
e0ac24aeab
@ -49,6 +49,8 @@ Bottom level categories:
|
||||
|
||||
### New features
|
||||
|
||||
- Added `--shader-stage` and `--input-kind` options to naga-cli for specifying vertex/fragment/compute shaders, and frontend. by @ratmice in [#5411](https://github.com/gfx-rs/wgpu/pull/5411)
|
||||
|
||||
#### General
|
||||
|
||||
- Implemented the `Unorm10_10_10_2` VertexFormat.
|
||||
|
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -2161,6 +2161,7 @@ dependencies = [
|
||||
name = "naga-cli"
|
||||
version = "0.19.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argh",
|
||||
"bincode",
|
||||
"codespan-reporting",
|
||||
|
@ -68,7 +68,7 @@ path = "./naga"
|
||||
version = "0.19.2"
|
||||
|
||||
[workspace.dependencies]
|
||||
anyhow = "1.0"
|
||||
anyhow = "1.0.23"
|
||||
arrayvec = "0.7"
|
||||
bit-vec = "0.6"
|
||||
bitflags = "2"
|
||||
|
@ -23,6 +23,7 @@ log = "0.4"
|
||||
codespan-reporting = "0.11"
|
||||
env_logger = "0.11"
|
||||
argh = "0.1.5"
|
||||
anyhow.workspace = true
|
||||
|
||||
[dependencies.naga]
|
||||
version = "0.19"
|
||||
|
@ -1,4 +1,5 @@
|
||||
#![allow(clippy::manual_strip)]
|
||||
use anyhow::{anyhow, Context as _};
|
||||
#[allow(unused_imports)]
|
||||
use std::fs;
|
||||
use std::{error::Error, fmt, io::Read, path::Path, str::FromStr};
|
||||
@ -62,6 +63,16 @@ struct Args {
|
||||
#[argh(option)]
|
||||
shader_model: Option<ShaderModelArg>,
|
||||
|
||||
/// the shader stage, for example 'frag', 'vert', or 'compute'.
|
||||
/// if the shader stage is unspecified it will be derived from
|
||||
/// the file extension.
|
||||
#[argh(option)]
|
||||
shader_stage: Option<ShaderStage>,
|
||||
|
||||
/// the kind of input, e.g. 'glsl', 'wgsl', 'spv', or 'bin'.
|
||||
#[argh(option)]
|
||||
input_kind: Option<InputKind>,
|
||||
|
||||
/// the metal version to use, for example, 1.0, 1.1, 1.2, etc.
|
||||
#[argh(option)]
|
||||
metal_version: Option<MslVersionArg>,
|
||||
@ -170,6 +181,46 @@ impl FromStr for ShaderModelArg {
|
||||
}
|
||||
}
|
||||
|
||||
/// Newtype so we can implement [`FromStr`] for `ShaderSource`.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct ShaderStage(naga::ShaderStage);
|
||||
|
||||
impl FromStr for ShaderStage {
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
use naga::ShaderStage;
|
||||
Ok(Self(match s.to_lowercase().as_str() {
|
||||
"frag" | "fragment" => ShaderStage::Fragment,
|
||||
"comp" | "compute" => ShaderStage::Compute,
|
||||
"vert" | "vertex" => ShaderStage::Vertex,
|
||||
_ => return Err(anyhow!("Invalid shader stage: {s}")),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
/// Input kind/file extension mapping
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum InputKind {
|
||||
Bincode,
|
||||
Glsl,
|
||||
SpirV,
|
||||
Wgsl,
|
||||
}
|
||||
impl FromStr for InputKind {
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(match s.to_lowercase().as_str() {
|
||||
"bin" => InputKind::Bincode,
|
||||
"glsl" => InputKind::Glsl,
|
||||
"spv" => InputKind::SpirV,
|
||||
"wgsl" => InputKind::Wgsl,
|
||||
_ => return Err(anyhow!("Invalid value for --input-kind: {s}")),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Newtype so we can implement [`FromStr`] for [`naga::back::glsl::Version`].
|
||||
#[derive(Clone, Debug)]
|
||||
struct GlslProfileArg(naga::back::glsl::Version);
|
||||
@ -247,6 +298,8 @@ struct Parameters<'a> {
|
||||
msl: naga::back::msl::Options,
|
||||
glsl: naga::back::glsl::Options,
|
||||
hlsl: naga::back::hlsl::Options,
|
||||
input_kind: Option<InputKind>,
|
||||
shader_stage: Option<ShaderStage>,
|
||||
}
|
||||
|
||||
trait PrettyResult {
|
||||
@ -300,7 +353,7 @@ impl fmt::Display for CliError {
|
||||
}
|
||||
impl std::error::Error for CliError {}
|
||||
|
||||
fn run() -> Result<(), Box<dyn std::error::Error>> {
|
||||
fn run() -> anyhow::Result<()> {
|
||||
env_logger::init();
|
||||
|
||||
// Parse commandline arguments
|
||||
@ -381,6 +434,9 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
|
||||
return Err(CliError("Input file path is not specified").into());
|
||||
};
|
||||
|
||||
params.input_kind = args.input_kind;
|
||||
params.shader_stage = args.shader_stage;
|
||||
|
||||
let Parsed {
|
||||
mut module,
|
||||
input_text,
|
||||
@ -500,67 +556,70 @@ struct Parsed {
|
||||
input_text: Option<String>,
|
||||
}
|
||||
|
||||
fn parse_input(
|
||||
input_path: &Path,
|
||||
input: Vec<u8>,
|
||||
params: &Parameters,
|
||||
) -> Result<Parsed, Box<dyn std::error::Error>> {
|
||||
let (module, input_text) = match Path::new(&input_path)
|
||||
.extension()
|
||||
.ok_or(CliError("Input filename has no extension"))?
|
||||
.to_str()
|
||||
.ok_or(CliError("Input filename not valid unicode"))?
|
||||
{
|
||||
"bin" => (bincode::deserialize(&input)?, None),
|
||||
"spv" => naga::front::spv::parse_u8_slice(&input, ¶ms.spv_in).map(|m| (m, None))?,
|
||||
"wgsl" => {
|
||||
fn parse_input(input_path: &Path, input: Vec<u8>, params: &Parameters) -> anyhow::Result<Parsed> {
|
||||
let input_kind = match params.input_kind {
|
||||
Some(kind) => kind,
|
||||
None => input_path
|
||||
.extension()
|
||||
.context("Input filename has no extension")?
|
||||
.to_str()
|
||||
.context("Input filename not valid unicode")?
|
||||
.parse()
|
||||
.context("Unable to determine --input-kind from filename")?,
|
||||
};
|
||||
|
||||
let (module, input_text) = match input_kind {
|
||||
InputKind::Bincode => (bincode::deserialize(&input)?, None),
|
||||
InputKind::SpirV => {
|
||||
naga::front::spv::parse_u8_slice(&input, ¶ms.spv_in).map(|m| (m, None))?
|
||||
}
|
||||
InputKind::Wgsl => {
|
||||
let input = String::from_utf8(input)?;
|
||||
let result = naga::front::wgsl::parse_str(&input);
|
||||
match result {
|
||||
Ok(v) => (v, Some(input)),
|
||||
Err(ref e) => {
|
||||
let message = format!(
|
||||
let message = anyhow!(
|
||||
"Could not parse WGSL:\n{}",
|
||||
e.emit_to_string_with_path(&input, input_path)
|
||||
);
|
||||
return Err(message.into());
|
||||
return Err(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
ext @ ("vert" | "frag" | "comp" | "glsl") => {
|
||||
InputKind::Glsl => {
|
||||
let shader_stage = match params.shader_stage {
|
||||
Some(shader_stage) => shader_stage,
|
||||
None => {
|
||||
// filename.shader_stage.glsl -> filename.shader_stage
|
||||
let file_stem = input_path
|
||||
.file_stem()
|
||||
.context("Unable to determine file stem from input filename.")?;
|
||||
// filename.shader_stage -> shader_stage
|
||||
let inner_ext = Path::new(file_stem)
|
||||
.extension()
|
||||
.context("Unable to determine inner extension from input filename.")?
|
||||
.to_str()
|
||||
.context("Input filename not valid unicode")?;
|
||||
inner_ext.parse().context("from input filename")?
|
||||
}
|
||||
};
|
||||
let input = String::from_utf8(input)?;
|
||||
let mut parser = naga::front::glsl::Frontend::default();
|
||||
|
||||
(
|
||||
parser
|
||||
.parse(
|
||||
&naga::front::glsl::Options {
|
||||
stage: match ext {
|
||||
"vert" => naga::ShaderStage::Vertex,
|
||||
"frag" => naga::ShaderStage::Fragment,
|
||||
"comp" => naga::ShaderStage::Compute,
|
||||
"glsl" => {
|
||||
let internal_name = input_path.to_string_lossy();
|
||||
match Path::new(&internal_name[..internal_name.len()-5])
|
||||
.extension()
|
||||
.ok_or(CliError("Input filename ending with .glsl has no internal extension"))?
|
||||
.to_str()
|
||||
.ok_or(CliError("Input filename not valid unicode"))?
|
||||
{
|
||||
"vert" => naga::ShaderStage::Vertex,
|
||||
"frag" => naga::ShaderStage::Fragment,
|
||||
"comp" => naga::ShaderStage::Compute,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
},
|
||||
_ => unreachable!(),
|
||||
},
|
||||
stage: shader_stage.0,
|
||||
defines: Default::default(),
|
||||
},
|
||||
&input,
|
||||
)
|
||||
.unwrap_or_else(|error| {
|
||||
let filename = input_path.file_name().and_then(std::ffi::OsStr::to_str).unwrap_or("glsl");
|
||||
let filename = input_path
|
||||
.file_name()
|
||||
.and_then(std::ffi::OsStr::to_str)
|
||||
.unwrap_or("glsl");
|
||||
let mut writer = StandardStream::stderr(ColorChoice::Auto);
|
||||
error.emit_to_writer_with_path(&mut writer, &input, filename);
|
||||
std::process::exit(1);
|
||||
@ -568,7 +627,6 @@ fn parse_input(
|
||||
Some(input),
|
||||
)
|
||||
}
|
||||
_ => return Err(CliError("Unknown input file extension").into()),
|
||||
};
|
||||
|
||||
Ok(Parsed { module, input_text })
|
||||
@ -579,7 +637,7 @@ fn write_output(
|
||||
info: &Option<naga::valid::ModuleInfo>,
|
||||
params: &Parameters,
|
||||
output_path: &str,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
) -> anyhow::Result<()> {
|
||||
match Path::new(&output_path)
|
||||
.extension()
|
||||
.ok_or(CliError("Output filename has no extension"))?
|
||||
@ -744,7 +802,7 @@ fn write_output(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn bulk_validate(args: Args, params: &Parameters) -> Result<(), Box<dyn std::error::Error>> {
|
||||
fn bulk_validate(args: Args, params: &Parameters) -> anyhow::Result<()> {
|
||||
let mut invalid = vec![];
|
||||
for input_path in args.files {
|
||||
let path = Path::new(&input_path);
|
||||
@ -787,7 +845,7 @@ fn bulk_validate(args: Args, params: &Parameters) -> Result<(), Box<dyn std::err
|
||||
for path in invalid {
|
||||
writeln!(&mut formatted, " {path}").unwrap();
|
||||
}
|
||||
return Err(formatted.into());
|
||||
return Err(anyhow!(formatted));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
Loading…
Reference in New Issue
Block a user