Improve wgsl-out

This commit is contained in:
Gordon-F 2021-04-20 13:02:13 +03:00 committed by Dzmitry Malyshau
parent 02105db045
commit 3accf4dc15
6 changed files with 695 additions and 60 deletions

View File

@ -61,5 +61,5 @@ validate-dot: $(SNAPSHOTS_OUT)/*.dot
validate-wgsl: $(SNAPSHOTS_OUT)/*.wgsl
@set -e && for file in $^ ; do \
echo "Validating" $${file#"$(SNAPSHOTS_OUT)/"}; \
cargo run --bin convert --features wgsl-in $${file} >/dev/null; \
cargo run --bin convert --features wgsl-in $${file}; \
done

View File

@ -269,7 +269,7 @@ fn main() {
"wgsl" => {
use naga::back::wgsl;
let wgsl = wgsl::write_string(&module).unwrap_pretty();
let wgsl = wgsl::write_string(&module, info.as_ref().unwrap()).unwrap_pretty();
fs::write(output_path, wgsl).unwrap();
}
other => {

View File

@ -1,8 +1,6 @@
mod keywords;
mod writer;
use std::io::Error as IoError;
use std::string::FromUtf8Error;
use thiserror::Error;
pub use writer::Writer;
@ -10,14 +8,17 @@ pub use writer::Writer;
#[derive(Error, Debug)]
pub enum Error {
#[error(transparent)]
IoError(#[from] IoError),
#[error(transparent)]
Utf8(#[from] FromUtf8Error),
FmtError(#[from] std::fmt::Error),
#[error("{0}")]
Custom(String),
}
pub fn write_string(module: &crate::Module) -> Result<String, Error> {
let mut w = Writer::new(Vec::new());
w.write(module)?;
let output = String::from_utf8(w.finish())?;
pub fn write_string(
module: &crate::Module,
info: &crate::valid::ModuleInfo,
) -> Result<String, Error> {
let mut w = Writer::new(String::new());
w.write(module, info)?;
let output = w.finish();
Ok(output)
}

View File

@ -1,24 +1,58 @@
// TODO: temp
#![allow(dead_code)]
use super::Error;
use crate::FastHashMap;
use crate::{
back::wgsl::keywords::RESERVED, Function, Module, ShaderStage, StructLevel, TypeInner,
back::wgsl::keywords::RESERVED,
proc::{EntryPointIndex, TypeResolution},
valid::{FunctionInfo, ModuleInfo},
Arena, BinaryOperator, Binding, Constant, Expression, Function, GlobalVariable, Handle,
ImageClass, ImageDimension, Module, ScalarKind, ShaderStage, Statement, StorageFormat,
StructLevel, Type, TypeInner,
};
use crate::{
proc::{NameKey, Namer},
StructMember,
};
use std::io::Write;
use crate::{FastHashMap, VectorSize};
use std::fmt::Write;
const _INDENT: &str = " ";
const INDENT: &str = " ";
/// Shorthand result used internally by the backend
type BackendResult = Result<(), Error>;
enum Decoration {
VertexStage,
FragmentStage,
ComputeStage { workgroup_size: [u32; 3] },
/// WGSL attribute
/// https://gpuweb.github.io/gpuweb/wgsl/#attributes
enum Attribute {
Binding(u32),
Block,
BuiltIn(crate::BuiltIn),
Group(u32),
Location(u32),
Stage(ShaderStage),
WorkGroupSize([u32; 3]),
}
/// Stores the current function type (either a regular function or an entry point)
///
/// Also stores data needed to identify it (handle for a regular function or index for an entry point)
// TODO: copy-paste from glsl-out
enum FunctionType {
/// A regular function and it's handle
Function(Handle<Function>),
/// A entry point and it's index
EntryPoint(EntryPointIndex),
}
/// Helper structure that stores data needed when writing the function
// TODO: copy-paste from glsl-out
struct FunctionCtx<'a> {
/// The current function type being written
ty: FunctionType,
/// Analysis about the function
info: &'a FunctionInfo,
/// The expression arena of the current function being written
expressions: &'a Arena<Expression>,
}
pub struct Writer<W> {
@ -36,10 +70,24 @@ impl<W: Write> Writer<W> {
}
}
pub fn write(&mut self, module: &Module) -> BackendResult {
pub fn write(&mut self, module: &Module, info: &ModuleInfo) -> BackendResult {
self.names.clear();
self.namer.reset(module, RESERVED, &mut self.names);
// Write all constants
for (_, constant) in module.constants.iter() {
if constant.name.is_some() {
self.write_constant(&constant, true)?;
}
}
// Write all globals
for (_, global) in module.global_variables.iter() {
if global.name.is_some() {
self.write_global(&module, &global)?;
}
}
// Write all structs
for (handle, ty) in module.types.iter() {
if let TypeInner::Struct {
@ -48,24 +96,31 @@ impl<W: Write> Writer<W> {
{
let name = &self.names[&NameKey::Type(handle)].clone();
let block = level == StructLevel::Root;
self.write_struct(name, block, members)?;
self.write_struct(module, name, block, members)?;
writeln!(self.out)?;
}
}
for (_, ep) in module.entry_points.iter().enumerate() {
let decoration = match ep.stage {
ShaderStage::Vertex => Decoration::VertexStage,
ShaderStage::Fragment => Decoration::FragmentStage,
ShaderStage::Compute => Decoration::ComputeStage {
workgroup_size: ep.workgroup_size,
},
for (index, ep) in module.entry_points.iter().enumerate() {
let attributes = match ep.stage {
ShaderStage::Vertex | ShaderStage::Fragment => vec![Attribute::Stage(ep.stage)],
ShaderStage::Compute => vec![
Attribute::Stage(ShaderStage::Compute),
Attribute::WorkGroupSize(ep.workgroup_size),
],
};
self.write_decoration(decoration)?;
// Add a newline after decoration
self.write_attributes(&attributes)?;
// Add a newline after attribute
writeln!(self.out)?;
let func_ctx = FunctionCtx {
ty: FunctionType::EntryPoint(index as u16),
info: &info.get_entry_point(index),
expressions: &ep.function.expressions,
};
self.write_function(&module, &ep.function, &func_ctx)?;
writeln!(self.out)?;
self.write_function(&ep.function)?;
}
// Add a newline at the end of file
@ -79,38 +134,108 @@ impl<W: Write> Writer<W> {
///
/// # Notes
/// Ends in a newline
fn write_function(&mut self, func: &Function) -> BackendResult {
write!(self.out, "fn {}(", func.name.as_ref().unwrap())?; // TODO: unnamed function?
write!(self.out, ")")?;
fn write_function(
&mut self,
module: &Module,
func: &Function,
func_ctx: &FunctionCtx<'_>,
) -> BackendResult {
// TODO: unnamed function?
write!(self.out, "fn {}(", func.name.as_ref().unwrap())?;
// Write function arguments
// TODO: another function type
if let FunctionType::EntryPoint(ep_index) = func_ctx.ty {
for (index, arg) in func.arguments.iter().enumerate() {
// Write argument attribute if a binding is present
if let Some(ref binding) = arg.binding {
self.write_attributes(&[map_binding_to_attribute(binding)])?;
write!(self.out, " ")?;
}
// Write argument name
write!(
self.out,
"{}: ",
&self.names[&NameKey::EntryPointArgument(ep_index, index as u32)]
)?;
// Write argument type
self.write_type(module, arg.ty)?;
if index < func.arguments.len() - 1 {
// Add a separator between args
write!(self.out, ", ")?;
}
}
write!(self.out, ")")?;
}
// Write function return type
if let Some(ref result) = func.result {
if let Some(ref binding) = result.binding {
write!(self.out, " -> ")?;
self.write_attributes(&[map_binding_to_attribute(binding)])?;
write!(self.out, " ")?;
self.write_type(module, result.ty)?;
// Extra space only for readability
write!(self.out, " ")?;
} else {
let struct_name = &self.names[&NameKey::Type(result.ty)].clone();
write!(self.out, " -> {} ", struct_name)?;
}
}
write!(self.out, "{{")?;
write!(self.out, "}}")?;
writeln!(self.out)?;
// Write the function body (statement list)
for sta in func.body.iter() {
// The indentation should always be 1 when writing the function body
self.write_stmt(&module, sta, &func_ctx, 1)?;
}
writeln!(self.out, "}}")?;
Ok(())
}
/// Helper method to write a decoration
/// Helper method to write a attribute
///
/// # Notes
/// Adds no leading or trailing whitespace
fn write_decoration(&mut self, decoration: Decoration) -> BackendResult {
fn write_attributes(&mut self, attributes: &[Attribute]) -> BackendResult {
write!(self.out, "[[")?;
match decoration {
Decoration::VertexStage => write!(self.out, "stage(vertex)")?,
Decoration::FragmentStage => write!(self.out, "stage(fragment)")?,
Decoration::ComputeStage { workgroup_size } => {
write!(
self.out,
"{}",
format!(
"stage(compute), workgroup_size({}, {}, {})",
workgroup_size[0], workgroup_size[1], workgroup_size[2]
)
)?;
for (index, attribute) in attributes.iter().enumerate() {
match *attribute {
Attribute::Block => {
write!(self.out, "block")?;
}
Attribute::Location(id) => write!(self.out, "location({})", id)?,
Attribute::BuiltIn(builtin_attrib) => {
let builtin_str = builtin_str(builtin_attrib);
if let Some(builtin) = builtin_str {
write!(self.out, "builtin({})", builtin)?
} else {
log::warn!("Unsupported builtin attribute: {:?}", builtin_attrib);
}
}
Attribute::Stage(shader_stage) => match shader_stage {
ShaderStage::Vertex => write!(self.out, "stage(vertex)")?,
ShaderStage::Fragment => write!(self.out, "stage(fragment)")?,
ShaderStage::Compute => write!(self.out, "stage(compute)")?,
},
Attribute::WorkGroupSize(size) => {
write!(
self.out,
"{}",
format!("workgroup_size({}, {}, {})", size[0], size[1], size[2])
)?;
}
Attribute::Binding(id) => write!(self.out, "binding({})", id)?,
Attribute::Group(id) => write!(self.out, "group({})", id)?,
};
if index < attributes.len() - 1 {
// Add a separator between args
write!(self.out, ", ")?;
}
Decoration::Block => {
write!(self.out, "block")?;
}
};
}
write!(self.out, "]]")?;
Ok(())
@ -122,23 +247,528 @@ impl<W: Write> Writer<W> {
/// Ends in a newline
fn write_struct(
&mut self,
module: &Module,
name: &str,
block: bool,
_members: &[StructMember],
members: &[StructMember],
) -> BackendResult {
if block {
self.write_decoration(Decoration::Block)?;
self.write_attributes(&[Attribute::Block])?;
writeln!(self.out)?;
}
write!(self.out, "struct {} {{", name)?;
write!(self.out, "}}")?;
writeln!(self.out)?;
for (_, member) in members.iter().enumerate() {
// The indentation is only for readability
write!(self.out, "{}", INDENT)?;
if let Some(ref binding) = member.binding {
self.write_attributes(&[map_binding_to_attribute(binding)])?;
write!(self.out, " ")?;
}
// Write struct member name and type
write!(self.out, "{}: ", member.name.as_ref().unwrap())?;
self.write_type(module, member.ty)?;
write!(self.out, ";")?;
writeln!(self.out)?;
}
write!(self.out, "}};")?;
writeln!(self.out)?;
Ok(())
}
/// Helper method used to write non image/sampler types
///
/// # Notes
/// Adds no trailing or leading whitespace
fn write_type(&mut self, module: &Module, ty: Handle<Type>) -> BackendResult {
let inner = &module.types[ty].inner;
match *inner {
TypeInner::Struct { .. } => {
// Get the struct name
let name = &self.names[&NameKey::Type(ty)];
write!(self.out, "{}", name)?;
return Ok(());
}
ref other => self.write_value_type(module, other)?,
}
Ok(())
}
/// Helper method used to write value types
///
/// # Notes
/// Adds no trailing or leading whitespace
fn write_value_type(&mut self, _module: &Module, inner: &TypeInner) -> BackendResult {
match *inner {
TypeInner::Vector { size, kind, .. } => write!(
self.out,
"{}",
format!("vec{}<{}>", vector_size_str(size), scalar_kind_str(kind),)
)?,
TypeInner::Sampler { comparison: false } => {
write!(self.out, "sampler")?;
}
TypeInner::Sampler { comparison: true } => {
write!(self.out, "sampler_comparison")?;
}
TypeInner::Image {
dim,
arrayed,
class,
} => {
// More about texture types: https://gpuweb.github.io/gpuweb/wgsl/#sampled-texture-type
let dim_str = image_dimension_str(dim);
let arrayed_str = if arrayed { "_array" } else { "" };
let (class_str, multisampled_str, scalar_str) = match class {
ImageClass::Sampled { kind, multi } => (
"",
if multi { "multisampled" } else { "" },
format!("<{}>", scalar_kind_str(kind)),
),
ImageClass::Depth => ("depth", "", String::from("")),
ImageClass::Storage(storage_format) => (
"storage",
"",
format!("<{}>", storage_format_str(storage_format)),
),
};
let ty_str = format!(
"texture_{}{}{}{}{}",
class_str, multisampled_str, dim_str, arrayed_str, scalar_str
);
write!(self.out, "{}", ty_str)?;
}
_ => todo!("write_value_type {:?}", inner),
}
Ok(())
}
/// Helper method used to write statements
///
/// # Notes
/// Always adds a newline
fn write_stmt(
&mut self,
module: &Module,
stmt: &Statement,
func_ctx: &FunctionCtx<'_>,
indent: usize,
) -> BackendResult {
match *stmt {
Statement::Emit(ref range) => {
for handle in range.clone() {
let min_ref_count = func_ctx.expressions[handle].bake_ref_count();
if min_ref_count <= func_ctx.info[handle].ref_count {
match func_ctx.info[handle].ty {
TypeResolution::Handle(ty_handle) => {
write!(self.out, "{}", INDENT.repeat(indent))?;
self.write_type(module, ty_handle)?
}
TypeResolution::Value(ref _inner) => {
//TODO:
//write!(self.out, "{}", INDENT.repeat(indent))?;
//self.write_value_type(module, inner)?
}
}
}
}
}
// TODO: copy-paste from glsl-out
Statement::If {
condition,
ref accept,
ref reject,
} => {
write!(self.out, "{}", INDENT.repeat(indent))?;
write!(self.out, "if (")?;
self.write_expr(module, condition, func_ctx)?;
writeln!(self.out, ") {{")?;
for sta in accept {
// Increase indentation to help with readability
self.write_stmt(module, sta, func_ctx, indent + 1)?;
}
// If there are no statements in the reject block we skip writing it
// This is only for readability
if !reject.is_empty() {
writeln!(self.out, "{}}} else {{", INDENT.repeat(indent))?;
for sta in reject {
// Increase indentation to help with readability
self.write_stmt(module, sta, func_ctx, indent + 1)?;
}
}
writeln!(self.out, "{}}}", INDENT.repeat(indent))?
}
Statement::Return { value } => {
write!(self.out, "{}", INDENT.repeat(indent))?;
if let Some(return_value) = value {
write!(self.out, "return ")?;
self.write_expr(module, return_value, &func_ctx)?;
writeln!(self.out, ";")?;
} else {
writeln!(self.out, "return;")?;
}
}
// TODO: copy-paste from glsl-out
Statement::Kill => {
write!(self.out, "{}", INDENT.repeat(indent))?;
writeln!(self.out, "discard;")?
}
_ => todo!("write_stmt {:?}", stmt),
}
Ok(())
}
/// Helper method to write expressions
///
/// # Notes
/// Doesn't add any newlines or leading/trailing spaces
fn write_expr(
&mut self,
module: &Module,
expr: Handle<Expression>,
func_ctx: &FunctionCtx<'_>,
) -> BackendResult {
let expression = &func_ctx.expressions[expr];
match *expression {
Expression::Constant(constant) => {
self.write_constant(&module.constants[constant], false)?
}
Expression::Compose { ty, ref components } => {
self.write_type(&module, ty)?;
write!(self.out, "(")?;
self.write_slice(components, |this, _, arg| {
this.write_expr(&module, *arg, func_ctx)
})?;
write!(self.out, ")")?
}
Expression::FunctionArgument(pos) => {
let name_key = match func_ctx.ty {
FunctionType::Function(handle) => NameKey::FunctionArgument(handle, pos),
FunctionType::EntryPoint(ep_index) => {
NameKey::EntryPointArgument(ep_index, pos)
}
};
let name = &self.names[&name_key];
write!(self.out, "{}", name)?;
}
Expression::Binary { op, left, right } => {
self.write_expr(module, left, func_ctx)?;
// TODO: glsl-out copy-paste
write!(
self.out,
" {} ",
match op {
BinaryOperator::Add => "+",
BinaryOperator::Subtract => "-",
BinaryOperator::Multiply => "*",
BinaryOperator::Divide => "/",
BinaryOperator::Modulo => "%",
BinaryOperator::Equal => "==",
BinaryOperator::NotEqual => "!=",
BinaryOperator::Less => "<",
BinaryOperator::LessEqual => "<=",
BinaryOperator::Greater => ">",
BinaryOperator::GreaterEqual => ">=",
BinaryOperator::And => "&",
BinaryOperator::ExclusiveOr => "^",
BinaryOperator::InclusiveOr => "|",
BinaryOperator::LogicalAnd => "&&",
BinaryOperator::LogicalOr => "||",
BinaryOperator::ShiftLeft => "<<",
BinaryOperator::ShiftRight => ">>",
}
)?;
self.write_expr(module, right, func_ctx)?;
}
// TODO: copy-paste from glsl-out
Expression::Access { base, index } => {
self.write_expr(module, base, func_ctx)?;
write!(self.out, "[")?;
self.write_expr(module, index, func_ctx)?;
write!(self.out, "]")?
}
// TODO: copy-paste from glsl-out
Expression::AccessIndex { base, index } => {
self.write_expr(module, base, func_ctx)?;
let base_ty_res = &func_ctx.info[base].ty;
let mut resolved = base_ty_res.inner_with(&module.types);
let base_ty_handle = match *resolved {
TypeInner::Pointer { base, class: _ } => {
resolved = &module.types[base].inner;
Some(base)
}
_ => base_ty_res.handle(),
};
match *resolved {
TypeInner::Vector { .. }
| TypeInner::Matrix { .. }
| TypeInner::Array { .. }
| TypeInner::ValuePointer { .. } => write!(self.out, "[{}]", index)?,
TypeInner::Struct { .. } => {
// This will never panic in case the type is a `Struct`, this is not true
// for other types so we can only check while inside this match arm
let ty = base_ty_handle.unwrap();
write!(
self.out,
".{}",
&self.names[&NameKey::StructMember(ty, index)]
)?
}
ref other => return Err(Error::Custom(format!("Cannot index {:?}", other))),
}
}
Expression::ImageSample {
image,
sampler,
coordinate,
array_index: _,
offset: _,
level,
depth_ref: _,
} => {
// TODO: other texture functions
// TODO: comments
let fun_name = match level {
crate::SampleLevel::Auto => "textureSample",
_ => todo!("expression_imagesample_level {:?}", level),
};
write!(self.out, "{}(", fun_name)?;
self.write_expr(module, image, func_ctx)?;
write!(self.out, ", ")?;
self.write_expr(module, sampler, func_ctx)?;
write!(self.out, ", ")?;
self.write_expr(module, coordinate, func_ctx)?;
write!(self.out, ")")?;
}
// TODO: copy-paste from msl-out
Expression::GlobalVariable(handle) => {
let name = &self.names[&NameKey::GlobalVariable(handle)];
write!(self.out, "{}", name)?;
}
_ => todo!("write_expr {:?}", expression),
}
Ok(())
}
/// Helper method that writes a list of comma separated `T` with a writer function `F`
///
/// The writer function `F` receives a mutable reference to `self` that if needed won't cause
/// borrow checker issues (using for example a closure with `self` will cause issues), the
/// second argument is the 0 based index of the element on the list, and the last element is
/// a reference to the element `T` being written
///
/// # Notes
/// - Adds no newlines or leading/trailing whitespace
/// - The last element won't have a trailing `,`
// TODO: copy-paste from glsl-out
fn write_slice<T, F: FnMut(&mut Self, u32, &T) -> BackendResult>(
&mut self,
data: &[T],
mut f: F,
) -> BackendResult {
// Loop trough `data` invoking `f` for each element
for (i, item) in data.iter().enumerate() {
f(self, i as u32, item)?;
// Only write a comma if isn't the last element
if i != data.len().saturating_sub(1) {
// The leading space is for readability only
write!(self.out, ", ")?;
}
}
Ok(())
}
/// Helper method used to write global variables
fn write_global(&mut self, module: &Module, global: &GlobalVariable) -> BackendResult {
if let Some(ref binding) = global.binding {
self.write_attributes(&[
Attribute::Group(binding.group),
Attribute::Binding(binding.binding),
])?;
write!(self.out, " ")?;
}
if let Some(ref name) = global.name {
// First write only global name
write!(self.out, "var {}: ", name)?;
// Write global type
self.write_type(module, global.ty)?;
// End with semicolon and extra newline for readability
writeln!(self.out, ";")?;
writeln!(self.out)?;
}
Ok(())
}
/// Helper method used to write constants
///
/// # Notes
/// Adds newlines for global constants
fn write_constant(&mut self, constant: &Constant, global: bool) -> BackendResult {
match constant.inner {
crate::ConstantInner::Scalar {
width: _,
ref value,
} => {
if let Some(ref name) = constant.name {
if global {
// First write only constant name
write!(self.out, "let {}: ", name)?;
// Next write constant type and value
match *value {
crate::ScalarValue::Sint(value) => {
write!(self.out, "i32 = {}", value)?;
}
crate::ScalarValue::Uint(value) => {
write!(self.out, "u32 = {}", value)?;
}
crate::ScalarValue::Float(value) => {
write!(self.out, "f32 = {}", value)?;
}
crate::ScalarValue::Bool(value) => {
write!(self.out, "bool = {}", value)?;
}
};
// End with semicolon and extra newline for readability
writeln!(self.out, ";")?;
writeln!(self.out)?;
} else {
write!(self.out, "{}", name)?;
}
} else {
match *value {
crate::ScalarValue::Sint(value) => {
write!(self.out, "{}", value)?;
}
crate::ScalarValue::Uint(value) => {
write!(self.out, "{}", value)?;
}
// TODO: fix float
crate::ScalarValue::Float(value) => {
write!(self.out, "{:.1}", value)?;
}
crate::ScalarValue::Bool(value) => {
write!(self.out, "{}", value)?;
}
};
}
}
_ => todo!("write_constant {:?}", constant.inner),
}
Ok(())
}
pub fn finish(self) -> W {
self.out
}
}
fn builtin_str(built_in: crate::BuiltIn) -> Option<&'static str> {
use crate::BuiltIn;
match built_in {
BuiltIn::VertexIndex => Some("vertex_index"),
BuiltIn::InstanceIndex => Some("instance_index"),
BuiltIn::Position => Some("position"),
BuiltIn::FrontFacing => Some("front_facing"),
BuiltIn::FragDepth => Some("frag_depth"),
BuiltIn::LocalInvocationId => Some("local_invocation_id"),
BuiltIn::LocalInvocationIndex => Some("local_invocation_index"),
BuiltIn::GlobalInvocationId => Some("global_invocation_id"),
BuiltIn::WorkGroupId => Some("workgroup_id"),
BuiltIn::WorkGroupSize => Some("workgroup_size"),
BuiltIn::SampleIndex => Some("sample_index"),
BuiltIn::SampleMask => Some("sample_mask"),
_ => None,
}
}
//TODO: copy-paste from msl-out
fn vector_size_str(size: VectorSize) -> &'static str {
match size {
VectorSize::Bi => "2",
VectorSize::Tri => "3",
VectorSize::Quad => "4",
}
}
//TODO: copy-paste from msl-out
fn image_dimension_str(dim: ImageDimension) -> &'static str {
match dim {
ImageDimension::D1 => "1d",
ImageDimension::D2 => "2d",
ImageDimension::D3 => "3d",
ImageDimension::Cube => "cube",
}
}
fn scalar_kind_str(kind: ScalarKind) -> &'static str {
match kind {
crate::ScalarKind::Float => "f32",
crate::ScalarKind::Sint => "i32",
crate::ScalarKind::Uint => "u32",
crate::ScalarKind::Bool => "bool",
}
}
fn storage_format_str(format: StorageFormat) -> &'static str {
match format {
StorageFormat::R8Unorm => "r8unorm",
StorageFormat::R8Snorm => "r8snorm",
StorageFormat::R8Uint => "r8uint",
StorageFormat::R8Sint => "r8sint",
StorageFormat::R16Uint => "r16uint",
StorageFormat::R16Sint => "r16sint",
StorageFormat::R16Float => "r16float",
StorageFormat::Rg8Unorm => "rg8unorm",
StorageFormat::Rg8Snorm => "rg8snorm",
StorageFormat::Rg8Uint => "rg8uint",
StorageFormat::Rg8Sint => "rg8sint",
StorageFormat::R32Uint => "r32uint",
StorageFormat::R32Sint => "r32sint",
StorageFormat::R32Float => "r32float",
StorageFormat::Rg16Uint => "rg16uint",
StorageFormat::Rg16Sint => "rg16sint",
StorageFormat::Rg16Float => "rg16float",
StorageFormat::Rgba8Unorm => "rgba8unorm",
StorageFormat::Rgba8Snorm => "rgba8snorm",
StorageFormat::Rgba8Uint => "rgba8uint",
StorageFormat::Rgba8Sint => "rgba8sint",
StorageFormat::Rgb10a2Unorm => "rgb10a2unorm",
StorageFormat::Rg11b10Float => "rg11b10float",
StorageFormat::Rg32Uint => "rg32uint",
StorageFormat::Rg32Sint => "rg32sint",
StorageFormat::Rg32Float => "rg32float",
StorageFormat::Rgba16Uint => "rgba16uint",
StorageFormat::Rgba16Sint => "rgba16sint",
StorageFormat::Rgba16Float => "rgba16float",
StorageFormat::Rgba32Uint => "rgba32uint",
StorageFormat::Rgba32Sint => "rgba32sint",
StorageFormat::Rgba32Float => "rgba32float",
}
}
fn map_binding_to_attribute(binding: &Binding) -> Attribute {
match *binding {
Binding::BuiltIn(built_in) => Attribute::BuiltIn(built_in),
//TODO: Interpolation
Binding::Location { location, .. } => Attribute::Location(location),
}
}

View File

@ -1,2 +1,6 @@
[[stage(compute), workgroup_size(1, 1, 1)]]
fn main(){}
fn main(){
return;
}

View File

@ -102,7 +102,7 @@ fn check_targets(module: &naga::Module, name: &str, targets: Targets) {
#[cfg(feature = "wgsl-out")]
{
if targets.contains(Targets::WGSL) {
check_output_wgsl(module, &dest);
check_output_wgsl(module, &info, &dest);
}
}
}
@ -207,10 +207,10 @@ fn check_output_hlsl(module: &naga::Module, destination: &PathBuf) {
}
#[cfg(feature = "wgsl-out")]
fn check_output_wgsl(module: &naga::Module, destination: &PathBuf) {
fn check_output_wgsl(module: &naga::Module, info: &naga::valid::ModuleInfo, destination: &PathBuf) {
use naga::back::wgsl;
let string = wgsl::write_string(module).unwrap();
let string = wgsl::write_string(module, info).unwrap();
fs::write(destination.with_extension("wgsl"), string).unwrap();
}