Move global use flags into FunctionInfo

This commit is contained in:
Dzmitry Malyshau 2021-02-17 14:55:46 -05:00 committed by Dzmitry Malyshau
parent 9f0cdfe9e5
commit 8a0382a89d
17 changed files with 235 additions and 360 deletions

View File

@ -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()

View File

@ -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 {

View File

@ -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();

View File

@ -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)
}

View File

@ -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);

View File

@ -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,
],
)
}

View File

@ -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
}

View File

@ -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![],

View File

@ -1,7 +1,5 @@
//! Parsers which load shaders into memory.
mod global_usage;
#[cfg(feature = "glsl-in")]
pub mod glsl;
#[cfg(feature = "spv-in")]

View File

@ -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) => {

View File

@ -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))

View File

@ -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.

View File

@ -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() {

View File

@ -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)
}
}

View File

@ -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),

View File

@ -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"),

View File

@ -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, &params);
check_output_spv(&module, &analysis, name, &params);
}
}
#[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, &params);
check_output_spv(&module, &analysis, name, &params);
}
#[cfg(feature = "serialize")]
{