mirror of
https://github.com/gfx-rs/wgpu.git
synced 2024-11-27 09:14:01 +00:00
Move global use flags into FunctionInfo
This commit is contained in:
parent
9f0cdfe9e5
commit
8a0382a89d
@ -221,7 +221,7 @@ fn main() {
|
||||
capabilities: params.spv_capabilities,
|
||||
};
|
||||
|
||||
let spv = spv::write_vec(&module, &options).unwrap_pretty();
|
||||
let spv = spv::write_vec(&module, &analysis, &options).unwrap_pretty();
|
||||
|
||||
let bytes = spv
|
||||
.iter()
|
||||
|
@ -405,18 +405,20 @@ impl<'a, W: Write> Writer<'a, W> {
|
||||
}
|
||||
}
|
||||
|
||||
let ep_info = self
|
||||
.analysis
|
||||
.get_entry_point(self.options.entry_point.0, &self.options.entry_point.1);
|
||||
|
||||
// Write the globals
|
||||
//
|
||||
// We filter all globals that aren't used by the selected entry point as they might be
|
||||
// interfere with each other (i.e. two globals with the same location but different with
|
||||
// different classes)
|
||||
for (handle, global) in self
|
||||
.module
|
||||
.global_variables
|
||||
.iter()
|
||||
.zip(&self.entry_point.function.global_usage)
|
||||
.filter_map(|(global, usage)| Some(global).filter(|_| !usage.is_empty()))
|
||||
{
|
||||
for (handle, global) in self.module.global_variables.iter() {
|
||||
if ep_info[handle].is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip builtins
|
||||
// TODO: Write them if they have modifiers
|
||||
if let Some(crate::Binding::BuiltIn(_)) = global.binding {
|
||||
@ -1775,13 +1777,8 @@ impl<'a, W: Write> Writer<'a, W> {
|
||||
}
|
||||
}
|
||||
|
||||
for ((handle, var), &usage) in self
|
||||
.module
|
||||
.global_variables
|
||||
.iter()
|
||||
.zip(&self.entry_point.function.global_usage)
|
||||
{
|
||||
if usage.is_empty() {
|
||||
for (handle, var) in self.module.global_variables.iter() {
|
||||
if info[handle].is_empty() {
|
||||
continue;
|
||||
}
|
||||
match self.module.types[var.ty].inner {
|
||||
|
@ -2,7 +2,7 @@ use super::{keywords::RESERVED, Error, LocationMode, Options, TranslationInfo};
|
||||
use crate::{
|
||||
arena::Handle,
|
||||
proc::{
|
||||
analyzer::{Analysis, FunctionInfo},
|
||||
analyzer::{Analysis, FunctionInfo, GlobalUse},
|
||||
EntryPointIndex, Interface, NameKey, Namer, ResolveContext, Typifier, Visitor,
|
||||
},
|
||||
FastHashMap,
|
||||
@ -34,7 +34,7 @@ struct TypedGlobalVariable<'a> {
|
||||
module: &'a crate::Module,
|
||||
names: &'a FastHashMap<NameKey, String>,
|
||||
handle: Handle<crate::GlobalVariable>,
|
||||
usage: crate::GlobalUse,
|
||||
usage: GlobalUse,
|
||||
}
|
||||
|
||||
impl<'a> TypedGlobalVariable<'a> {
|
||||
@ -47,7 +47,7 @@ impl<'a> TypedGlobalVariable<'a> {
|
||||
let (space_qualifier, reference) = match ty.inner {
|
||||
crate::TypeInner::Struct { .. } => match var.class {
|
||||
crate::StorageClass::Uniform | crate::StorageClass::Storage => {
|
||||
let space = if self.usage.contains(crate::GlobalUse::WRITE) {
|
||||
let space = if self.usage.contains(GlobalUse::WRITE) {
|
||||
"device "
|
||||
} else {
|
||||
"constant "
|
||||
@ -163,6 +163,7 @@ struct ExpressionContext<'a> {
|
||||
function: &'a crate::Function,
|
||||
origin: FunctionOrigin,
|
||||
module: &'a crate::Module,
|
||||
analysis: &'a Analysis,
|
||||
}
|
||||
|
||||
struct StatementContext<'a> {
|
||||
@ -212,7 +213,6 @@ impl<W: Write> Writer<W> {
|
||||
parameters: &[Handle<crate::Expression>],
|
||||
context: &ExpressionContext,
|
||||
) -> Result<(), Error> {
|
||||
let fun = &context.module.functions[fun_handle];
|
||||
let fun_name = &self.names[&NameKey::Function(fun_handle)];
|
||||
write!(self.out, "{}(", fun_name)?;
|
||||
// first, write down the actual arguments
|
||||
@ -224,13 +224,9 @@ impl<W: Write> Writer<W> {
|
||||
}
|
||||
// follow-up with any global resources used
|
||||
let mut separate = !parameters.is_empty();
|
||||
for ((handle, var), &usage) in context
|
||||
.module
|
||||
.global_variables
|
||||
.iter()
|
||||
.zip(&fun.global_usage)
|
||||
{
|
||||
if !usage.is_empty() && var.class.needs_pass_through() {
|
||||
let fun_info = &context.analysis[fun_handle];
|
||||
for (handle, var) in context.module.global_variables.iter() {
|
||||
if !fun_info[handle].is_empty() && var.class.needs_pass_through() {
|
||||
let name = &self.names[&NameKey::GlobalVariable(handle)];
|
||||
if separate {
|
||||
write!(self.out, ", ")?;
|
||||
@ -1122,9 +1118,10 @@ impl<W: Write> Writer<W> {
|
||||
},
|
||||
)?;
|
||||
|
||||
let fun_info = &analysis[fun_handle];
|
||||
pass_through_globals.clear();
|
||||
for ((handle, var), &usage) in module.global_variables.iter().zip(&fun.global_usage) {
|
||||
if !usage.is_empty() && var.class.needs_pass_through() {
|
||||
for (handle, var) in module.global_variables.iter() {
|
||||
if !fun_info[handle].is_empty() && var.class.needs_pass_through() {
|
||||
pass_through_globals.push(handle);
|
||||
}
|
||||
}
|
||||
@ -1152,7 +1149,7 @@ impl<W: Write> Writer<W> {
|
||||
module,
|
||||
names: &self.names,
|
||||
handle,
|
||||
usage: fun.global_usage[handle.index()],
|
||||
usage: fun_info[handle],
|
||||
};
|
||||
let separator = separate(index + 1 == pass_through_globals.len());
|
||||
write!(self.out, "{}", INDENT)?;
|
||||
@ -1177,8 +1174,9 @@ impl<W: Write> Writer<W> {
|
||||
function: fun,
|
||||
origin: FunctionOrigin::Handle(fun_handle),
|
||||
module,
|
||||
analysis,
|
||||
},
|
||||
fun_info: &analysis[fun_handle],
|
||||
fun_info,
|
||||
return_value: None,
|
||||
};
|
||||
self.named_expressions.clear();
|
||||
@ -1192,6 +1190,7 @@ impl<W: Write> Writer<W> {
|
||||
};
|
||||
for (ep_index, (&(stage, ref ep_name), ep)) in module.entry_points.iter().enumerate() {
|
||||
let fun = &ep.function;
|
||||
let fun_info = analysis.get_entry_point(stage, ep_name);
|
||||
self.typifier.resolve_all(
|
||||
&fun.expressions,
|
||||
&module.types,
|
||||
@ -1206,7 +1205,7 @@ impl<W: Write> Writer<W> {
|
||||
|
||||
// find the entry point(s) and inputs/outputs
|
||||
let mut last_used_global = None;
|
||||
for ((handle, var), &usage) in module.global_variables.iter().zip(&fun.global_usage) {
|
||||
for (handle, var) in module.global_variables.iter() {
|
||||
match var.class {
|
||||
crate::StorageClass::Input => {
|
||||
if let Some(crate::Binding::Location(_)) = var.binding {
|
||||
@ -1216,7 +1215,7 @@ impl<W: Write> Writer<W> {
|
||||
crate::StorageClass::Output => continue,
|
||||
_ => {}
|
||||
}
|
||||
if !usage.is_empty() {
|
||||
if !fun_info[handle].is_empty() {
|
||||
last_used_global = Some(handle);
|
||||
}
|
||||
}
|
||||
@ -1246,11 +1245,9 @@ impl<W: Write> Writer<W> {
|
||||
crate::ShaderStage::Vertex | crate::ShaderStage::Fragment => {
|
||||
// make dedicated input/output structs
|
||||
writeln!(self.out, "struct {} {{", location_input_name)?;
|
||||
for ((handle, var), &usage) in
|
||||
module.global_variables.iter().zip(&fun.global_usage)
|
||||
{
|
||||
for (handle, var) in module.global_variables.iter() {
|
||||
if var.class != crate::StorageClass::Input
|
||||
|| !usage.contains(crate::GlobalUse::READ)
|
||||
|| !fun_info[handle].contains(GlobalUse::READ)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@ -1262,7 +1259,7 @@ impl<W: Write> Writer<W> {
|
||||
module,
|
||||
names: &self.names,
|
||||
handle,
|
||||
usage: crate::GlobalUse::empty(),
|
||||
usage: GlobalUse::empty(),
|
||||
};
|
||||
write!(self.out, "{}", INDENT)?;
|
||||
tyvar.try_fmt(&mut self.out)?;
|
||||
@ -1274,11 +1271,9 @@ impl<W: Write> Writer<W> {
|
||||
writeln!(self.out)?;
|
||||
|
||||
writeln!(self.out, "struct {} {{", output_name)?;
|
||||
for ((handle, var), &usage) in
|
||||
module.global_variables.iter().zip(&fun.global_usage)
|
||||
{
|
||||
for (handle, var) in module.global_variables.iter() {
|
||||
if var.class != crate::StorageClass::Output
|
||||
|| !usage.contains(crate::GlobalUse::WRITE)
|
||||
|| !fun_info[handle].contains(GlobalUse::WRITE)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@ -1287,7 +1282,7 @@ impl<W: Write> Writer<W> {
|
||||
module,
|
||||
names: &self.names,
|
||||
handle,
|
||||
usage: crate::GlobalUse::empty(),
|
||||
usage: GlobalUse::empty(),
|
||||
};
|
||||
write!(self.out, "{}", INDENT)?;
|
||||
tyvar.try_fmt(&mut self.out)?;
|
||||
@ -1314,7 +1309,8 @@ impl<W: Write> Writer<W> {
|
||||
}
|
||||
};
|
||||
|
||||
for ((handle, var), &usage) in module.global_variables.iter().zip(&fun.global_usage) {
|
||||
for (handle, var) in module.global_variables.iter() {
|
||||
let usage = fun_info[handle];
|
||||
if usage.is_empty() || var.class == crate::StorageClass::Output {
|
||||
continue;
|
||||
}
|
||||
@ -1384,8 +1380,9 @@ impl<W: Write> Writer<W> {
|
||||
function: fun,
|
||||
origin: FunctionOrigin::EntryPoint(ep_index as _),
|
||||
module,
|
||||
analysis,
|
||||
},
|
||||
fun_info: analysis.get_entry_point(stage, ep_name),
|
||||
fun_info,
|
||||
return_value,
|
||||
};
|
||||
self.named_expressions.clear();
|
||||
|
@ -65,9 +65,13 @@ impl Default for Options {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_vec(module: &crate::Module, options: &Options) -> Result<Vec<u32>, Error> {
|
||||
pub fn write_vec(
|
||||
module: &crate::Module,
|
||||
analysis: &crate::proc::analyzer::Analysis,
|
||||
options: &Options,
|
||||
) -> Result<Vec<u32>, Error> {
|
||||
let mut words = Vec::new();
|
||||
let mut w = Writer::new(options)?;
|
||||
w.write(module, &mut words)?;
|
||||
w.write(module, analysis, &mut words)?;
|
||||
Ok(words)
|
||||
}
|
||||
|
@ -2,7 +2,10 @@
|
||||
use super::{Instruction, LogicalLayout, Options, PhysicalLayout, WriterFlags};
|
||||
use crate::{
|
||||
arena::{Arena, Handle},
|
||||
proc::{Layouter, ResolveContext, ResolveError, Typifier},
|
||||
proc::{
|
||||
analyzer::{Analysis, FunctionInfo},
|
||||
Layouter, ResolveContext, ResolveError, Typifier,
|
||||
},
|
||||
};
|
||||
use spirv::Word;
|
||||
use std::collections::hash_map::Entry;
|
||||
@ -456,18 +459,17 @@ impl Writer {
|
||||
entry_point: &crate::EntryPoint,
|
||||
stage: crate::ShaderStage,
|
||||
name: &str,
|
||||
info: &FunctionInfo,
|
||||
ir_module: &crate::Module,
|
||||
) -> Result<Instruction, Error> {
|
||||
let function_id = self.write_function(&entry_point.function, ir_module)?;
|
||||
|
||||
let mut interface_ids = vec![];
|
||||
for ((handle, var), &usage) in ir_module
|
||||
.global_variables
|
||||
.iter()
|
||||
.zip(&entry_point.function.global_usage)
|
||||
{
|
||||
for (handle, var) in ir_module.global_variables.iter() {
|
||||
let is_io = match var.class {
|
||||
crate::StorageClass::Input | crate::StorageClass::Output => !usage.is_empty(),
|
||||
crate::StorageClass::Input | crate::StorageClass::Output => {
|
||||
!info[handle].is_empty()
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
if is_io {
|
||||
@ -2115,7 +2117,11 @@ impl Writer {
|
||||
self.physical_layout.bound = self.id_count + 1;
|
||||
}
|
||||
|
||||
fn write_logical_layout(&mut self, ir_module: &crate::Module) -> Result<(), Error> {
|
||||
fn write_logical_layout(
|
||||
&mut self,
|
||||
ir_module: &crate::Module,
|
||||
analysis: &Analysis,
|
||||
) -> Result<(), Error> {
|
||||
Instruction::type_void(self.void_type).to_words(&mut self.logical_layout.declarations);
|
||||
Instruction::ext_inst_import(self.gl450_ext_inst_id, "GLSL.std.450")
|
||||
.to_words(&mut self.logical_layout.ext_inst_imports);
|
||||
@ -2148,7 +2154,8 @@ impl Writer {
|
||||
}
|
||||
|
||||
for (&(stage, ref name), ir_ep) in ir_module.entry_points.iter() {
|
||||
let ep_instruction = self.write_entry_point(ir_ep, stage, name, ir_module)?;
|
||||
let info = analysis.get_entry_point(stage, name);
|
||||
let ep_instruction = self.write_entry_point(ir_ep, stage, name, info, ir_module)?;
|
||||
ep_instruction.to_words(&mut self.logical_layout.entry_points);
|
||||
}
|
||||
|
||||
@ -2177,11 +2184,16 @@ impl Writer {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn write(&mut self, ir_module: &crate::Module, words: &mut Vec<Word>) -> Result<(), Error> {
|
||||
pub fn write(
|
||||
&mut self,
|
||||
ir_module: &crate::Module,
|
||||
analysis: &Analysis,
|
||||
words: &mut Vec<Word>,
|
||||
) -> Result<(), Error> {
|
||||
self.layouter
|
||||
.initialize(&ir_module.types, &ir_module.constants);
|
||||
|
||||
self.write_logical_layout(ir_module)?;
|
||||
self.write_logical_layout(ir_module, analysis)?;
|
||||
self.write_physical_layout();
|
||||
|
||||
self.physical_layout.in_words(words);
|
||||
|
@ -1,114 +0,0 @@
|
||||
use crate::{
|
||||
arena::{Arena, Handle},
|
||||
proc::{Interface, Visitor},
|
||||
};
|
||||
use bit_set::BitSet;
|
||||
|
||||
struct GlobalUseVisitor<'a> {
|
||||
usage: &'a mut [crate::GlobalUse],
|
||||
functions: &'a Arena<crate::Function>,
|
||||
}
|
||||
|
||||
impl Visitor for GlobalUseVisitor<'_> {
|
||||
fn visit_expr(&mut self, _: Handle<crate::Expression>, expr: &crate::Expression) {
|
||||
if let crate::Expression::GlobalVariable(handle) = expr {
|
||||
self.usage[handle.index()] |= crate::GlobalUse::READ;
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_lhs_expr(&mut self, _: Handle<crate::Expression>, expr: &crate::Expression) {
|
||||
if let crate::Expression::GlobalVariable(handle) = expr {
|
||||
self.usage[handle.index()] |= crate::GlobalUse::WRITE;
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_fun(&mut self, fun: Handle<crate::Function>) {
|
||||
for (mine, other) in self.usage.iter_mut().zip(&self.functions[fun].global_usage) {
|
||||
*mine |= *other;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::Function {
|
||||
pub fn fill_global_use(&mut self, globals_num: usize, functions: &Arena<crate::Function>) {
|
||||
let mut mask = BitSet::with_capacity(self.expressions.len());
|
||||
self.global_usage.clear();
|
||||
self.global_usage
|
||||
.resize(globals_num, crate::GlobalUse::empty());
|
||||
|
||||
let mut io = Interface {
|
||||
expressions: &self.expressions,
|
||||
local_variables: &self.local_variables,
|
||||
visitor: GlobalUseVisitor {
|
||||
usage: &mut self.global_usage,
|
||||
functions,
|
||||
},
|
||||
mask: &mut mask,
|
||||
};
|
||||
io.traverse(&self.body);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn global_usage_scan() {
|
||||
let test_global = crate::GlobalVariable {
|
||||
name: None,
|
||||
class: crate::StorageClass::Uniform,
|
||||
binding: None,
|
||||
ty: Handle::new(std::num::NonZeroU32::new(1).unwrap()),
|
||||
init: None,
|
||||
interpolation: None,
|
||||
storage_access: crate::StorageAccess::empty(),
|
||||
};
|
||||
let mut test_globals = Arena::new();
|
||||
|
||||
let global_1 = test_globals.append(test_global.clone());
|
||||
let global_2 = test_globals.append(test_global.clone());
|
||||
let global_3 = test_globals.append(test_global.clone());
|
||||
let global_4 = test_globals.append(test_global);
|
||||
|
||||
let mut expressions = Arena::new();
|
||||
let global_1_expr = expressions.append(crate::Expression::GlobalVariable(global_1));
|
||||
let global_2_expr = expressions.append(crate::Expression::GlobalVariable(global_2));
|
||||
let global_3_expr = expressions.append(crate::Expression::GlobalVariable(global_3));
|
||||
let global_4_expr = expressions.append(crate::Expression::GlobalVariable(global_4));
|
||||
|
||||
let test_body = vec![
|
||||
crate::Statement::Return {
|
||||
value: Some(global_1_expr),
|
||||
},
|
||||
crate::Statement::Store {
|
||||
pointer: global_2_expr,
|
||||
value: global_1_expr,
|
||||
},
|
||||
crate::Statement::Store {
|
||||
pointer: expressions.append(crate::Expression::Access {
|
||||
base: global_3_expr,
|
||||
index: global_4_expr,
|
||||
}),
|
||||
value: global_1_expr,
|
||||
},
|
||||
];
|
||||
|
||||
let mut function = crate::Function {
|
||||
name: None,
|
||||
arguments: Vec::new(),
|
||||
return_type: None,
|
||||
local_variables: Arena::new(),
|
||||
expressions,
|
||||
global_usage: Vec::new(),
|
||||
body: test_body,
|
||||
};
|
||||
let other_functions = Arena::new();
|
||||
function.fill_global_use(test_globals.len(), &other_functions);
|
||||
|
||||
assert_eq!(
|
||||
&function.global_usage,
|
||||
&[
|
||||
crate::GlobalUse::READ,
|
||||
crate::GlobalUse::WRITE,
|
||||
crate::GlobalUse::WRITE,
|
||||
crate::GlobalUse::READ,
|
||||
],
|
||||
)
|
||||
}
|
@ -278,7 +278,6 @@ impl Program<'_> {
|
||||
self.context.typifier = Typifier::new();
|
||||
ensure_block_returns(&mut block);
|
||||
f.body = block;
|
||||
f.fill_global_use(self.module.global_variables.len(), &self.module.functions);
|
||||
f
|
||||
}
|
||||
|
||||
|
@ -1004,7 +1004,6 @@ pomelo! {
|
||||
name: Some(n.1),
|
||||
arguments: vec![],
|
||||
return_type: t.1,
|
||||
global_usage: vec![],
|
||||
local_variables: Arena::<LocalVariable>::new(),
|
||||
expressions: Arena::<Expression>::new(),
|
||||
body: vec![],
|
||||
|
@ -1,7 +1,5 @@
|
||||
//! Parsers which load shaders into memory.
|
||||
|
||||
mod global_usage;
|
||||
|
||||
#[cfg(feature = "glsl-in")]
|
||||
pub mod glsl;
|
||||
#[cfg(feature = "spv-in")]
|
||||
|
@ -68,7 +68,6 @@ impl<I: Iterator<Item = u32>> super::Parser<I> {
|
||||
} else {
|
||||
Some(self.lookup_type.lookup(result_type_id)?.handle)
|
||||
},
|
||||
global_usage: Vec::new(),
|
||||
local_variables: Arena::new(),
|
||||
expressions: self.make_expression_storage(),
|
||||
body: Vec::new(),
|
||||
@ -147,7 +146,6 @@ impl<I: Iterator<Item = u32>> super::Parser<I> {
|
||||
|
||||
// done
|
||||
self.patch_function_calls(&mut fun)?;
|
||||
fun.fill_global_use(module.global_variables.len(), &module.functions);
|
||||
|
||||
let dump_suffix = match self.lookup_entry_point.remove(&fun_id) {
|
||||
Some(ep) => {
|
||||
|
@ -1988,7 +1988,6 @@ impl Parser {
|
||||
name: Some(fun_name.to_string()),
|
||||
arguments,
|
||||
return_type,
|
||||
global_usage: Vec::new(),
|
||||
local_variables: Arena::new(),
|
||||
expressions,
|
||||
body: Vec::new(),
|
||||
@ -2014,7 +2013,6 @@ impl Parser {
|
||||
// fixup the IR
|
||||
ensure_block_returns(&mut fun.body);
|
||||
// done
|
||||
fun.fill_global_use(module.global_variables.len(), &module.functions);
|
||||
self.scopes.pop();
|
||||
|
||||
Ok((fun, fun_name))
|
||||
|
16
src/lib.rs
16
src/lib.rs
@ -430,18 +430,6 @@ pub enum Binding {
|
||||
Resource { group: u32, binding: u32 },
|
||||
}
|
||||
|
||||
bitflags::bitflags! {
|
||||
/// Indicates how a global variable is used.
|
||||
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
||||
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
|
||||
pub struct GlobalUse: u8 {
|
||||
/// Data will be read from the variable.
|
||||
const READ = 0x1;
|
||||
/// Data will be written to the variable.
|
||||
const WRITE = 0x2;
|
||||
}
|
||||
}
|
||||
|
||||
/// Variable defined at module level.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
||||
@ -833,10 +821,6 @@ pub struct Function {
|
||||
pub arguments: Vec<FunctionArgument>,
|
||||
/// The return type of this function, if any.
|
||||
pub return_type: Option<Handle<Type>>,
|
||||
/// Vector of global variable usages.
|
||||
///
|
||||
/// Each item corresponds to a global variable in the module.
|
||||
pub global_usage: Vec<GlobalUse>,
|
||||
/// Local variables defined and used in the function.
|
||||
pub local_variables: Arena<LocalVariable>,
|
||||
/// Expressions used inside this function.
|
||||
|
@ -42,6 +42,20 @@ bitflags::bitflags! {
|
||||
}
|
||||
}
|
||||
|
||||
bitflags::bitflags! {
|
||||
/// Indicates how a global variable is used.
|
||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
|
||||
#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
|
||||
pub struct GlobalUse: u8 {
|
||||
/// Data will be read from the variable.
|
||||
const READ = 0x1;
|
||||
/// Data will be written to the variable.
|
||||
const WRITE = 0x2;
|
||||
/// The information about the data is queried.
|
||||
const QUERY = 0x4;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||
pub struct SamplingKey {
|
||||
pub image: Handle<crate::GlobalVariable>,
|
||||
@ -52,14 +66,40 @@ pub struct SamplingKey {
|
||||
pub struct ExpressionInfo {
|
||||
pub control_flags: ControlFlags,
|
||||
pub ref_count: usize,
|
||||
assignable_global: Option<Handle<crate::GlobalVariable>>,
|
||||
}
|
||||
|
||||
pub struct FunctionInfo {
|
||||
/// Accumulated control flags of this function.
|
||||
pub control_flags: ControlFlags,
|
||||
/// Set of image-sampler pais used with sampling.
|
||||
pub sampling_set: crate::FastHashSet<SamplingKey>,
|
||||
/// Vector of global variable usages.
|
||||
///
|
||||
/// Each item corresponds to a global variable in the module.
|
||||
global_uses: Box<[GlobalUse]>,
|
||||
/// Vector of expression infos.
|
||||
///
|
||||
/// Each item corresponds to an expression in the function.
|
||||
expressions: Box<[ExpressionInfo]>,
|
||||
}
|
||||
|
||||
impl FunctionInfo {
|
||||
pub fn global_variable_count(&self) -> usize {
|
||||
self.global_uses.len()
|
||||
}
|
||||
pub fn expression_count(&self) -> usize {
|
||||
self.expressions.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Index<Handle<crate::GlobalVariable>> for FunctionInfo {
|
||||
type Output = GlobalUse;
|
||||
fn index(&self, handle: Handle<crate::GlobalVariable>) -> &GlobalUse {
|
||||
&self.global_uses[handle.index()]
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Index<Handle<crate::Expression>> for FunctionInfo {
|
||||
type Output = ExpressionInfo;
|
||||
fn index(&self, handle: Handle<crate::Expression>) -> &ExpressionInfo {
|
||||
@ -78,13 +118,68 @@ pub enum AnalysisError {
|
||||
}
|
||||
|
||||
impl FunctionInfo {
|
||||
/// Adds a value-type reference to an expression.
|
||||
#[must_use]
|
||||
fn add_ref(&mut self, handle: Handle<crate::Expression>) -> ControlFlags {
|
||||
fn add_ref_impl(
|
||||
&mut self,
|
||||
handle: Handle<crate::Expression>,
|
||||
global_use: GlobalUse,
|
||||
) -> ControlFlags {
|
||||
let info = &mut self.expressions[handle.index()];
|
||||
info.ref_count += 1;
|
||||
// mark the used global as read
|
||||
if let Some(global) = info.assignable_global {
|
||||
self.global_uses[global.index()] |= global_use;
|
||||
}
|
||||
info.control_flags
|
||||
}
|
||||
|
||||
/// Adds a value-type reference to an expression.
|
||||
#[must_use]
|
||||
fn add_ref(&mut self, handle: Handle<crate::Expression>) -> ControlFlags {
|
||||
self.add_ref_impl(handle, GlobalUse::READ)
|
||||
}
|
||||
|
||||
/// Adds a potentially assignable reference to an expression.
|
||||
/// These are destinations for `Store` and `ImageStore` statements,
|
||||
/// which can transit through `Access` and `AccessIndex`.
|
||||
#[must_use]
|
||||
fn add_assignable_ref(
|
||||
&mut self,
|
||||
handle: Handle<crate::Expression>,
|
||||
assignable_global: &mut Option<Handle<crate::GlobalVariable>>,
|
||||
) -> ControlFlags {
|
||||
let info = &mut self.expressions[handle.index()];
|
||||
info.ref_count += 1;
|
||||
// propagate the assignable global up the chain, till it either hits
|
||||
// a value-type expression, or the assignment statement.
|
||||
if let Some(global) = info.assignable_global {
|
||||
if let Some(_old) = assignable_global.replace(global) {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
info.control_flags
|
||||
}
|
||||
|
||||
/// Inherit information from a called function.
|
||||
fn process_call(
|
||||
&mut self,
|
||||
info: &Self,
|
||||
arguments: &[Handle<crate::Expression>],
|
||||
) -> ControlFlags {
|
||||
for key in info.sampling_set.iter() {
|
||||
self.sampling_set.insert(key.clone());
|
||||
}
|
||||
for (mine, other) in self.global_uses.iter_mut().zip(info.global_uses.iter()) {
|
||||
*mine |= *other;
|
||||
}
|
||||
let mut flags = info.control_flags;
|
||||
for &argument in arguments {
|
||||
flags |= self.add_ref(argument);
|
||||
}
|
||||
flags
|
||||
}
|
||||
|
||||
/// Computes the control flags of a given expression, and store them
|
||||
/// in `self.expressions`. Also, bumps the reference counts on
|
||||
/// dependent expressions.
|
||||
@ -97,9 +192,12 @@ impl FunctionInfo {
|
||||
) -> Result<(), AnalysisError> {
|
||||
use crate::{Expression as E, SampleLevel as Sl};
|
||||
|
||||
let mut assignable_global = None;
|
||||
let control_flags = match expression_arena[handle] {
|
||||
E::Access { base, index } => self.add_ref(base) | self.add_ref(index),
|
||||
E::AccessIndex { base, .. } => self.add_ref(base),
|
||||
E::Access { base, index } => {
|
||||
self.add_assignable_ref(base, &mut assignable_global) | self.add_ref(index)
|
||||
}
|
||||
E::AccessIndex { base, .. } => self.add_assignable_ref(base, &mut assignable_global),
|
||||
E::Constant(_) => ControlFlags::empty(),
|
||||
E::Compose { ref components, .. } => {
|
||||
let mut accum = ControlFlags::empty();
|
||||
@ -110,6 +208,7 @@ impl FunctionInfo {
|
||||
}
|
||||
E::FunctionArgument(_) => ControlFlags::NON_UNIFORM_RESULT, //TODO?
|
||||
E::GlobalVariable(handle) => {
|
||||
assignable_global = Some(handle);
|
||||
let var = &global_var_arena[handle];
|
||||
let uniform = if let Some(crate::Binding::BuiltIn(built_in)) = var.binding {
|
||||
match built_in {
|
||||
@ -210,7 +309,7 @@ impl FunctionInfo {
|
||||
crate::ImageQuery::Size { level: Some(h) } => self.add_ref(h),
|
||||
_ => ControlFlags::empty(),
|
||||
};
|
||||
self.add_ref(image) | query_flags
|
||||
self.add_ref_impl(image, GlobalUse::QUERY) | query_flags
|
||||
}
|
||||
E::Unary { expr, .. } => self.add_ref(expr),
|
||||
E::Binary { left, right, .. } => self.add_ref(left) | self.add_ref(right),
|
||||
@ -239,23 +338,14 @@ impl FunctionInfo {
|
||||
E::Call {
|
||||
function,
|
||||
ref arguments,
|
||||
} => {
|
||||
let other_info = &other_functions[function.index()];
|
||||
for key in other_info.sampling_set.iter() {
|
||||
self.sampling_set.insert(key.clone());
|
||||
}
|
||||
let mut accum = other_info.control_flags;
|
||||
for &argument in arguments {
|
||||
accum |= self.add_ref(argument);
|
||||
}
|
||||
accum
|
||||
}
|
||||
E::ArrayLength(expr) => self.add_ref(expr),
|
||||
} => self.process_call(&other_functions[function.index()], arguments),
|
||||
E::ArrayLength(expr) => self.add_ref_impl(expr, GlobalUse::QUERY),
|
||||
};
|
||||
|
||||
self.expressions[handle.index()] = ExpressionInfo {
|
||||
control_flags,
|
||||
ref_count: 0,
|
||||
assignable_global,
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
@ -334,7 +424,9 @@ impl FunctionInfo {
|
||||
};
|
||||
ControlFlags::MAY_EXIT | flags
|
||||
}
|
||||
S::Store { pointer, value } => self.add_ref(pointer) | self.add_ref(value),
|
||||
S::Store { pointer, value } => {
|
||||
self.add_ref_impl(pointer, GlobalUse::WRITE) | self.add_ref(value)
|
||||
}
|
||||
S::ImageStore {
|
||||
image,
|
||||
coordinate,
|
||||
@ -346,20 +438,14 @@ impl FunctionInfo {
|
||||
None => ControlFlags::empty(),
|
||||
};
|
||||
array_flags
|
||||
| self.add_ref(image)
|
||||
| self.add_ref_impl(image, GlobalUse::WRITE)
|
||||
| self.add_ref(coordinate)
|
||||
| self.add_ref(value)
|
||||
}
|
||||
S::Call {
|
||||
function,
|
||||
ref arguments,
|
||||
} => {
|
||||
let mut flags = other_functions[function.index()].control_flags;
|
||||
for &argument in arguments {
|
||||
flags |= self.add_ref(argument);
|
||||
}
|
||||
flags
|
||||
}
|
||||
} => self.process_call(&other_functions[function.index()], arguments),
|
||||
};
|
||||
|
||||
if flags.contains(ControlFlags::REQUIRE_UNIFORM) && !is_uniform {
|
||||
@ -389,6 +475,7 @@ impl Analysis {
|
||||
let mut info = FunctionInfo {
|
||||
control_flags: ControlFlags::empty(),
|
||||
sampling_set: crate::FastHashSet::default(),
|
||||
global_uses: vec![GlobalUse::empty(); global_var_arena.len()].into_boxed_slice(),
|
||||
expressions: vec![ExpressionInfo::default(); fun.expressions.len()].into_boxed_slice(),
|
||||
};
|
||||
|
||||
@ -490,6 +577,7 @@ fn uniform_control_flow() {
|
||||
let mut info = FunctionInfo {
|
||||
control_flags: ControlFlags::empty(),
|
||||
sampling_set: crate::FastHashSet::default(),
|
||||
global_uses: vec![GlobalUse::empty(); global_var_arena.len()].into_boxed_slice(),
|
||||
expressions: vec![ExpressionInfo::default(); expressions.len()].into_boxed_slice(),
|
||||
};
|
||||
for (handle, _) in expressions.iter() {
|
||||
|
@ -1,5 +1,5 @@
|
||||
use super::{
|
||||
analyzer::{Analysis, AnalysisError},
|
||||
analyzer::{Analysis, AnalysisError, FunctionInfo, GlobalUse},
|
||||
typifier::{ResolveContext, ResolveError, Typifier},
|
||||
};
|
||||
use crate::arena::{Arena, Handle};
|
||||
@ -81,8 +81,6 @@ pub enum FunctionError {
|
||||
Resolve(#[from] ResolveError),
|
||||
#[error("There are instructions after `return`/`break`/`continue`")]
|
||||
InvalidControlFlowExitTail,
|
||||
#[error("The global use array has a different length from the global variables")]
|
||||
InvalidGlobalUsageLength,
|
||||
#[error("Local variable {handle:?} '{name}' is invalid: {error:?}")]
|
||||
LocalVariable {
|
||||
handle: Handle<crate::LocalVariable>,
|
||||
@ -106,7 +104,7 @@ pub enum EntryPointError {
|
||||
#[error("Can't have a return value")]
|
||||
UnexpectedReturnValue,
|
||||
#[error("Global variable {0:?} is used incorrectly as {1:?}")]
|
||||
InvalidGlobalUsage(Handle<crate::GlobalVariable>, crate::GlobalUse),
|
||||
InvalidGlobalUsage(Handle<crate::GlobalVariable>, GlobalUse),
|
||||
#[error("Bindings for {0:?} conflict with other global variables")]
|
||||
BindingCollision(Handle<crate::GlobalVariable>),
|
||||
#[error("Built-in {0:?} is not applicable to this entry point")]
|
||||
@ -256,38 +254,38 @@ impl crate::GlobalVariable {
|
||||
}
|
||||
}
|
||||
|
||||
fn storage_usage(access: crate::StorageAccess) -> crate::GlobalUse {
|
||||
let mut storage_usage = crate::GlobalUse::empty();
|
||||
fn storage_usage(access: crate::StorageAccess) -> GlobalUse {
|
||||
let mut storage_usage = GlobalUse::QUERY;
|
||||
if access.contains(crate::StorageAccess::LOAD) {
|
||||
storage_usage |= crate::GlobalUse::READ;
|
||||
storage_usage |= GlobalUse::READ;
|
||||
}
|
||||
if access.contains(crate::StorageAccess::STORE) {
|
||||
storage_usage |= crate::GlobalUse::WRITE;
|
||||
storage_usage |= GlobalUse::WRITE;
|
||||
}
|
||||
storage_usage
|
||||
}
|
||||
|
||||
fn built_in_usage(built_in: crate::BuiltIn) -> (crate::ShaderStage, crate::GlobalUse) {
|
||||
use crate::{BuiltIn as Bi, GlobalUse as Gu, ShaderStage as Ss};
|
||||
fn built_in_usage(built_in: crate::BuiltIn) -> (crate::ShaderStage, GlobalUse) {
|
||||
use crate::{BuiltIn as Bi, ShaderStage as Ss};
|
||||
match built_in {
|
||||
Bi::BaseInstance => (Ss::Vertex, Gu::READ),
|
||||
Bi::BaseVertex => (Ss::Vertex, Gu::READ),
|
||||
Bi::ClipDistance => (Ss::Vertex, Gu::WRITE),
|
||||
Bi::InstanceIndex => (Ss::Vertex, Gu::READ),
|
||||
Bi::PointSize => (Ss::Vertex, Gu::WRITE),
|
||||
Bi::Position => (Ss::Vertex, Gu::WRITE),
|
||||
Bi::VertexIndex => (Ss::Vertex, Gu::READ),
|
||||
Bi::FragCoord => (Ss::Fragment, Gu::READ),
|
||||
Bi::FragDepth => (Ss::Fragment, Gu::WRITE),
|
||||
Bi::FrontFacing => (Ss::Fragment, Gu::READ),
|
||||
Bi::SampleIndex => (Ss::Fragment, Gu::READ),
|
||||
Bi::SampleMaskIn => (Ss::Fragment, Gu::READ),
|
||||
Bi::SampleMaskOut => (Ss::Fragment, Gu::WRITE),
|
||||
Bi::GlobalInvocationId => (Ss::Compute, Gu::READ),
|
||||
Bi::LocalInvocationId => (Ss::Compute, Gu::READ),
|
||||
Bi::LocalInvocationIndex => (Ss::Compute, Gu::READ),
|
||||
Bi::WorkGroupId => (Ss::Compute, Gu::READ),
|
||||
Bi::WorkGroupSize => (Ss::Compute, Gu::READ),
|
||||
Bi::BaseInstance => (Ss::Vertex, GlobalUse::READ),
|
||||
Bi::BaseVertex => (Ss::Vertex, GlobalUse::READ),
|
||||
Bi::ClipDistance => (Ss::Vertex, GlobalUse::WRITE),
|
||||
Bi::InstanceIndex => (Ss::Vertex, GlobalUse::READ),
|
||||
Bi::PointSize => (Ss::Vertex, GlobalUse::WRITE),
|
||||
Bi::Position => (Ss::Vertex, GlobalUse::WRITE),
|
||||
Bi::VertexIndex => (Ss::Vertex, GlobalUse::READ),
|
||||
Bi::FragCoord => (Ss::Fragment, GlobalUse::READ),
|
||||
Bi::FragDepth => (Ss::Fragment, GlobalUse::WRITE),
|
||||
Bi::FrontFacing => (Ss::Fragment, GlobalUse::READ),
|
||||
Bi::SampleIndex => (Ss::Fragment, GlobalUse::READ),
|
||||
Bi::SampleMaskIn => (Ss::Fragment, GlobalUse::READ),
|
||||
Bi::SampleMaskOut => (Ss::Fragment, GlobalUse::WRITE),
|
||||
Bi::GlobalInvocationId => (Ss::Compute, GlobalUse::READ),
|
||||
Bi::LocalInvocationId => (Ss::Compute, GlobalUse::READ),
|
||||
Bi::LocalInvocationIndex => (Ss::Compute, GlobalUse::READ),
|
||||
Bi::WorkGroupId => (Ss::Compute, GlobalUse::READ),
|
||||
Bi::WorkGroupSize => (Ss::Compute, GlobalUse::READ),
|
||||
}
|
||||
}
|
||||
|
||||
@ -531,6 +529,7 @@ impl Validator {
|
||||
fn validate_function(
|
||||
&mut self,
|
||||
fun: &crate::Function,
|
||||
_info: &FunctionInfo,
|
||||
module: &crate::Module,
|
||||
) -> Result<(), FunctionError> {
|
||||
let resolve_ctx = ResolveContext {
|
||||
@ -543,10 +542,6 @@ impl Validator {
|
||||
self.typifier
|
||||
.resolve_all(&fun.expressions, &module.types, &resolve_ctx)?;
|
||||
|
||||
if fun.global_usage.len() > module.global_variables.len() {
|
||||
return Err(FunctionError::InvalidGlobalUsageLength);
|
||||
}
|
||||
|
||||
for (var_handle, var) in fun.local_variables.iter() {
|
||||
self.validate_local_var(var, &module.types, &module.constants)
|
||||
.map_err(|error| FunctionError::LocalVariable {
|
||||
@ -575,6 +570,7 @@ impl Validator {
|
||||
&mut self,
|
||||
ep: &crate::EntryPoint,
|
||||
stage: crate::ShaderStage,
|
||||
info: &FunctionInfo,
|
||||
module: &crate::Module,
|
||||
) -> Result<(), EntryPointError> {
|
||||
if ep.early_depth_test.is_some() && stage != crate::ShaderStage::Fragment {
|
||||
@ -598,11 +594,8 @@ impl Validator {
|
||||
bg.clear();
|
||||
}
|
||||
|
||||
for ((var_handle, var), &usage) in module
|
||||
.global_variables
|
||||
.iter()
|
||||
.zip(&ep.function.global_usage)
|
||||
{
|
||||
for (var_handle, var) in module.global_variables.iter() {
|
||||
let usage = info[var_handle];
|
||||
if usage.is_empty() {
|
||||
continue;
|
||||
}
|
||||
@ -629,9 +622,7 @@ impl Validator {
|
||||
match var.binding {
|
||||
Some(crate::Binding::BuiltIn(built_in)) => {
|
||||
let (allowed_stage, allowed_usage) = built_in_usage(built_in);
|
||||
if allowed_stage != stage
|
||||
|| !allowed_usage.contains(crate::GlobalUse::READ)
|
||||
{
|
||||
if allowed_stage != stage || !allowed_usage.contains(GlobalUse::READ) {
|
||||
return Err(EntryPointError::InvalidBuiltIn(built_in));
|
||||
}
|
||||
}
|
||||
@ -643,15 +634,13 @@ impl Validator {
|
||||
Some(crate::Binding::Resource { .. }) => unreachable!(),
|
||||
None => (),
|
||||
}
|
||||
crate::GlobalUse::READ
|
||||
GlobalUse::READ
|
||||
}
|
||||
crate::StorageClass::Output => {
|
||||
match var.binding {
|
||||
Some(crate::Binding::BuiltIn(built_in)) => {
|
||||
let (allowed_stage, allowed_usage) = built_in_usage(built_in);
|
||||
if allowed_stage != stage
|
||||
|| !allowed_usage.contains(crate::GlobalUse::WRITE)
|
||||
{
|
||||
if allowed_stage != stage || !allowed_usage.contains(GlobalUse::WRITE) {
|
||||
return Err(EntryPointError::InvalidBuiltIn(built_in));
|
||||
}
|
||||
}
|
||||
@ -663,21 +652,19 @@ impl Validator {
|
||||
Some(crate::Binding::Resource { .. }) => unreachable!(),
|
||||
None => (),
|
||||
}
|
||||
crate::GlobalUse::READ | crate::GlobalUse::WRITE
|
||||
GlobalUse::READ | GlobalUse::WRITE
|
||||
}
|
||||
crate::StorageClass::Uniform => crate::GlobalUse::READ,
|
||||
crate::StorageClass::Uniform => GlobalUse::READ | GlobalUse::QUERY,
|
||||
crate::StorageClass::Storage => storage_usage(var.storage_access),
|
||||
crate::StorageClass::Handle => match module.types[var.ty].inner {
|
||||
crate::TypeInner::Image {
|
||||
class: crate::ImageClass::Storage(_),
|
||||
..
|
||||
} => storage_usage(var.storage_access),
|
||||
_ => crate::GlobalUse::READ,
|
||||
_ => GlobalUse::READ | GlobalUse::QUERY,
|
||||
},
|
||||
crate::StorageClass::Private | crate::StorageClass::WorkGroup => {
|
||||
crate::GlobalUse::all()
|
||||
}
|
||||
crate::StorageClass::PushConstant => crate::GlobalUse::READ,
|
||||
crate::StorageClass::Private | crate::StorageClass::WorkGroup => GlobalUse::all(),
|
||||
crate::StorageClass::PushConstant => GlobalUse::READ,
|
||||
};
|
||||
if !allowed_usage.contains(usage) {
|
||||
log::warn!("\tUsage error for: {:?}", var);
|
||||
@ -706,7 +693,7 @@ impl Validator {
|
||||
return Err(EntryPointError::UnexpectedReturnValue);
|
||||
}
|
||||
|
||||
self.validate_function(&ep.function, module)?;
|
||||
self.validate_function(&ep.function, info, module)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -717,6 +704,8 @@ impl Validator {
|
||||
self.type_flags
|
||||
.resize(module.types.len(), TypeFlags::empty());
|
||||
|
||||
let analysis = Analysis::new(module)?;
|
||||
|
||||
for (handle, constant) in module.constants.iter() {
|
||||
self.validate_constant(handle, &module.constants, &module.types)
|
||||
.map_err(|error| ValidationError::Constant {
|
||||
@ -750,12 +739,13 @@ impl Validator {
|
||||
}
|
||||
|
||||
for (fun_handle, fun) in module.functions.iter() {
|
||||
self.validate_function(fun, module)
|
||||
self.validate_function(fun, &analysis[fun_handle], module)
|
||||
.map_err(|e| ValidationError::Function(fun_handle, e))?;
|
||||
}
|
||||
|
||||
for (&(stage, ref name), entry_point) in module.entry_points.iter() {
|
||||
self.validate_entry_point(entry_point, stage, module)
|
||||
let info = analysis.get_entry_point(stage, name);
|
||||
self.validate_entry_point(entry_point, stage, info, module)
|
||||
.map_err(|error| ValidationError::EntryPoint {
|
||||
stage,
|
||||
name: name.to_string(),
|
||||
@ -763,7 +753,6 @@ impl Validator {
|
||||
})?;
|
||||
}
|
||||
|
||||
let analysis = Analysis::new(module)?;
|
||||
Ok(analysis)
|
||||
}
|
||||
}
|
||||
|
@ -139,26 +139,6 @@ expression: output
|
||||
name: Some("frag_main"),
|
||||
arguments: [],
|
||||
return_type: None,
|
||||
global_usage: [
|
||||
(
|
||||
bits: 0,
|
||||
),
|
||||
(
|
||||
bits: 0,
|
||||
),
|
||||
(
|
||||
bits: 0,
|
||||
),
|
||||
(
|
||||
bits: 0,
|
||||
),
|
||||
(
|
||||
bits: 0,
|
||||
),
|
||||
(
|
||||
bits: 2,
|
||||
),
|
||||
],
|
||||
local_variables: [],
|
||||
expressions: [
|
||||
GlobalVariable(1),
|
||||
@ -200,20 +180,6 @@ expression: output
|
||||
name: Some("vert_main"),
|
||||
arguments: [],
|
||||
return_type: None,
|
||||
global_usage: [
|
||||
(
|
||||
bits: 1,
|
||||
),
|
||||
(
|
||||
bits: 1,
|
||||
),
|
||||
(
|
||||
bits: 2,
|
||||
),
|
||||
(
|
||||
bits: 2,
|
||||
),
|
||||
],
|
||||
local_variables: [],
|
||||
expressions: [
|
||||
Constant(1),
|
||||
|
@ -592,29 +592,6 @@ expression: output
|
||||
),
|
||||
],
|
||||
return_type: Some(1),
|
||||
global_usage: [
|
||||
(
|
||||
bits: 1,
|
||||
),
|
||||
(
|
||||
bits: 1,
|
||||
),
|
||||
(
|
||||
bits: 0,
|
||||
),
|
||||
(
|
||||
bits: 0,
|
||||
),
|
||||
(
|
||||
bits: 0,
|
||||
),
|
||||
(
|
||||
bits: 0,
|
||||
),
|
||||
(
|
||||
bits: 0,
|
||||
),
|
||||
],
|
||||
local_variables: [],
|
||||
expressions: [
|
||||
GlobalVariable(3),
|
||||
@ -825,29 +802,6 @@ expression: output
|
||||
name: Some("fs_main"),
|
||||
arguments: [],
|
||||
return_type: None,
|
||||
global_usage: [
|
||||
(
|
||||
bits: 1,
|
||||
),
|
||||
(
|
||||
bits: 1,
|
||||
),
|
||||
(
|
||||
bits: 1,
|
||||
),
|
||||
(
|
||||
bits: 1,
|
||||
),
|
||||
(
|
||||
bits: 1,
|
||||
),
|
||||
(
|
||||
bits: 1,
|
||||
),
|
||||
(
|
||||
bits: 2,
|
||||
),
|
||||
],
|
||||
local_variables: [
|
||||
(
|
||||
name: Some("color"),
|
||||
|
@ -49,6 +49,7 @@ struct Parameters {
|
||||
mtl_bindings: naga::FastHashMap<BindSource, BindTarget>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn with_snapshot_settings<F: FnOnce() -> ()>(snapshot_assertion: F) {
|
||||
let mut settings = insta::Settings::new();
|
||||
settings.set_snapshot_path("out");
|
||||
@ -57,7 +58,12 @@ fn with_snapshot_settings<F: FnOnce() -> ()>(snapshot_assertion: F) {
|
||||
}
|
||||
|
||||
#[cfg(feature = "spv-out")]
|
||||
fn check_output_spv(module: &naga::Module, name: &str, params: &Parameters) {
|
||||
fn check_output_spv(
|
||||
module: &naga::Module,
|
||||
analysis: &naga::proc::analyzer::Analysis,
|
||||
name: &str,
|
||||
params: &Parameters,
|
||||
) {
|
||||
use naga::back::spv;
|
||||
use rspirv::binary::Disassemble;
|
||||
|
||||
@ -67,7 +73,7 @@ fn check_output_spv(module: &naga::Module, name: &str, params: &Parameters) {
|
||||
capabilities: params.spv_capabilities.clone(),
|
||||
};
|
||||
|
||||
let spv = spv::write_vec(&module, &options).unwrap();
|
||||
let spv = spv::write_vec(module, analysis, &options).unwrap();
|
||||
|
||||
let dis = rspirv::dr::load_words(spv)
|
||||
.expect("Produced invalid SPIR-V")
|
||||
@ -162,7 +168,7 @@ fn convert_wgsl(name: &str, language: Language) {
|
||||
#[cfg(feature = "spv-out")]
|
||||
{
|
||||
if language.contains(Language::SPIRV) {
|
||||
check_output_spv(&module, name, ¶ms);
|
||||
check_output_spv(&module, &analysis, name, ¶ms);
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "msl-out")]
|
||||
@ -264,11 +270,11 @@ fn convert_glsl(name: &str, entry_points: naga::FastHashMap<String, naga::Shader
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
naga::proc::Validator::new().validate(&module).unwrap();
|
||||
let analysis = naga::proc::Validator::new().validate(&module).unwrap();
|
||||
|
||||
#[cfg(feature = "spv-out")]
|
||||
{
|
||||
check_output_spv(&module, name, ¶ms);
|
||||
check_output_spv(&module, &analysis, name, ¶ms);
|
||||
}
|
||||
#[cfg(feature = "serialize")]
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user