mirror of
https://github.com/gfx-rs/wgpu.git
synced 2026-04-22 03:02:01 -04:00
Remove alignment from struct IR
This commit is contained in:
committed by
Dzmitry Malyshau
parent
6efe347e90
commit
b191c982ec
@@ -663,7 +663,7 @@ impl<'a, W: Write> Writer<'a, W> {
|
||||
// glsl has no pointer types so just write types as normal and loads are skipped
|
||||
TypeInner::Pointer { base, .. } => self.write_type(base),
|
||||
TypeInner::Struct {
|
||||
level: crate::StructLevel::Root,
|
||||
top_level: true,
|
||||
ref members,
|
||||
span: _,
|
||||
} => self.write_struct(true, ty, members),
|
||||
@@ -743,8 +743,7 @@ impl<'a, W: Write> Writer<'a, W> {
|
||||
if let Some(storage_class) = glsl_storage_class(global.class) {
|
||||
write!(self.out, "{} ", storage_class)?;
|
||||
} else if let TypeInner::Struct {
|
||||
level: crate::StructLevel::Root,
|
||||
..
|
||||
top_level: true, ..
|
||||
} = self.module.types[global.ty].inner
|
||||
{
|
||||
write!(self.out, "struct ")?;
|
||||
|
||||
@@ -916,11 +916,11 @@ impl Writer {
|
||||
}
|
||||
}
|
||||
crate::TypeInner::Struct {
|
||||
ref level,
|
||||
top_level,
|
||||
ref members,
|
||||
span: _,
|
||||
} => {
|
||||
if let crate::StructLevel::Root = *level {
|
||||
if top_level {
|
||||
self.decorate(id, Decoration::Block, &[]);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ use crate::{
|
||||
valid::{FunctionInfo, ModuleInfo},
|
||||
Arena, ArraySize, Binding, Constant, Expression, FastHashMap, Function, GlobalVariable, Handle,
|
||||
ImageClass, ImageDimension, Interpolation, Module, SampleLevel, Sampling, ScalarKind,
|
||||
ScalarValue, ShaderStage, Statement, StorageFormat, StructLevel, StructMember, Type, TypeInner,
|
||||
ScalarValue, ShaderStage, Statement, StorageFormat, StructMember, Type, TypeInner,
|
||||
};
|
||||
use bit_set::BitSet;
|
||||
use std::fmt::Write;
|
||||
@@ -94,11 +94,12 @@ impl<W: Write> Writer<W> {
|
||||
// Write all structs
|
||||
for (handle, ty) in module.types.iter() {
|
||||
if let TypeInner::Struct {
|
||||
level, ref members, ..
|
||||
top_level,
|
||||
ref members,
|
||||
..
|
||||
} = ty.inner
|
||||
{
|
||||
let block = level == StructLevel::Root;
|
||||
self.write_struct(module, handle, block, members)?;
|
||||
self.write_struct(module, handle, top_level, members)?;
|
||||
writeln!(self.out)?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -521,46 +521,34 @@ pomelo! {
|
||||
if i.1 == "gl_PerVertex" {
|
||||
None
|
||||
} else {
|
||||
let level = if t.is_empty() {
|
||||
//TODO
|
||||
crate::StructLevel::Normal { alignment: crate::Alignment::new(1).unwrap() }
|
||||
} else {
|
||||
crate::StructLevel::Root
|
||||
};
|
||||
Some(VarDeclaration {
|
||||
type_qualifiers: t,
|
||||
ids_initializers: vec![(None, None)],
|
||||
ty: extra.module.types.fetch_or_append(Type{
|
||||
name: Some(i.1),
|
||||
inner: TypeInner::Struct {
|
||||
level,
|
||||
top_level: !t.is_empty(),
|
||||
members: sdl,
|
||||
span: 0, //TODO
|
||||
},
|
||||
}),
|
||||
type_qualifiers: t,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
declaration ::= type_qualifier(t) Identifier(i1) LeftBrace
|
||||
struct_declaration_list(sdl) RightBrace Identifier(i2) Semicolon {
|
||||
let level = if t.is_empty() {
|
||||
//TODO
|
||||
crate::StructLevel::Normal { alignment: crate::Alignment::new(1).unwrap() }
|
||||
} else {
|
||||
crate::StructLevel::Root
|
||||
};
|
||||
Some(VarDeclaration {
|
||||
type_qualifiers: t,
|
||||
ids_initializers: vec![(Some(i2.1), None)],
|
||||
ty: extra.module.types.fetch_or_append(Type{
|
||||
name: Some(i1.1),
|
||||
inner: TypeInner::Struct {
|
||||
level,
|
||||
top_level: !t.is_empty(),
|
||||
members: sdl,
|
||||
span: 0, //TODO
|
||||
},
|
||||
}),
|
||||
type_qualifiers: t,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -761,7 +749,7 @@ pomelo! {
|
||||
Type{
|
||||
name: Some(i.1),
|
||||
inner: TypeInner::Struct {
|
||||
level: crate::StructLevel::Normal { alignment: crate::Alignment::new(1).unwrap() },
|
||||
top_level: false,
|
||||
members: vec![],
|
||||
span: 0,
|
||||
}
|
||||
|
||||
@@ -353,9 +353,7 @@ impl<I: Iterator<Item = u32>> super::Parser<I> {
|
||||
let ty = module.types.append(crate::Type {
|
||||
name: None,
|
||||
inner: crate::TypeInner::Struct {
|
||||
level: crate::StructLevel::Normal {
|
||||
alignment: crate::Alignment::new(1).unwrap(),
|
||||
},
|
||||
top_level: false,
|
||||
members,
|
||||
span: 0xFFFF, // shouldn't matter
|
||||
},
|
||||
|
||||
@@ -40,6 +40,7 @@ use function::*;
|
||||
|
||||
use crate::{
|
||||
arena::{Arena, Handle},
|
||||
proc::{Alignment, Layouter},
|
||||
FastHashMap,
|
||||
};
|
||||
|
||||
@@ -192,28 +193,6 @@ impl crate::ImageDimension {
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: should this be shared with `crate::proc::wgsl::layout` logic?
|
||||
fn get_alignment(ty: Handle<crate::Type>, arena: &Arena<crate::Type>) -> crate::Alignment {
|
||||
use crate::TypeInner as Ti;
|
||||
match arena[ty].inner {
|
||||
Ti::Scalar { width, .. } => crate::Alignment::new(width as u32).unwrap(),
|
||||
Ti::Vector { size, width, .. }
|
||||
| Ti::Matrix {
|
||||
rows: size, width, ..
|
||||
} => {
|
||||
let count = if size >= crate::VectorSize::Tri { 4 } else { 2 };
|
||||
crate::Alignment::new((count * width) as u32).unwrap()
|
||||
}
|
||||
Ti::Pointer { .. } | Ti::ValuePointer { .. } => crate::Alignment::new(1).unwrap(),
|
||||
Ti::Array { stride, .. } => crate::Alignment::new(stride).unwrap(),
|
||||
Ti::Struct { ref level, .. } => match *level {
|
||||
crate::StructLevel::Root => crate::Alignment::new(1).unwrap(),
|
||||
crate::StructLevel::Normal { alignment } => alignment,
|
||||
},
|
||||
Ti::Image { .. } | Ti::Sampler { .. } => crate::Alignment::new(1).unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
type MemberIndex = u32;
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
@@ -395,6 +374,7 @@ impl Default for Options {
|
||||
pub struct Parser<I> {
|
||||
data: I,
|
||||
state: ModuleState,
|
||||
layouter: Layouter,
|
||||
temp_bytes: Vec<u8>,
|
||||
ext_glsl_id: Option<spirv::Word>,
|
||||
future_decor: FastHashMap<spirv::Word, Decoration>,
|
||||
@@ -432,6 +412,7 @@ impl<I: Iterator<Item = u32>> Parser<I> {
|
||||
Parser {
|
||||
data,
|
||||
state: ModuleState::Empty,
|
||||
layouter: Layouter::default(),
|
||||
temp_bytes: Vec::new(),
|
||||
ext_glsl_id: None,
|
||||
future_decor: FastHashMap::default(),
|
||||
@@ -2319,6 +2300,7 @@ impl<I: Iterator<Item = u32>> Parser<I> {
|
||||
self.index_constants.push(handle);
|
||||
}
|
||||
|
||||
self.layouter.clear();
|
||||
self.dummy_functions = Arena::new();
|
||||
self.lookup_function.clear();
|
||||
self.function_call_graph.clear();
|
||||
@@ -2946,7 +2928,11 @@ impl<I: Iterator<Item = u32>> Parser<I> {
|
||||
let parent_decor = self.future_decor.remove(&id);
|
||||
let block_decor = parent_decor.as_ref().and_then(|decor| decor.block.clone());
|
||||
|
||||
let mut struct_alignment = crate::Alignment::new(1).unwrap();
|
||||
self.layouter
|
||||
.update(&module.types, &module.constants)
|
||||
.unwrap();
|
||||
|
||||
let mut struct_alignment = Alignment::new(1).unwrap();
|
||||
let mut members = Vec::<crate::StructMember>::with_capacity(inst.wc as usize - 2);
|
||||
let mut member_lookups = Vec::with_capacity(members.capacity());
|
||||
let mut storage_access = crate::StorageAccess::empty();
|
||||
@@ -2989,8 +2975,7 @@ impl<I: Iterator<Item = u32>> Parser<I> {
|
||||
}
|
||||
}
|
||||
|
||||
let alignment = get_alignment(ty, &module.types);
|
||||
struct_alignment = struct_alignment.max(alignment);
|
||||
struct_alignment = struct_alignment.max(self.layouter[ty].alignment);
|
||||
|
||||
members.push(crate::StructMember {
|
||||
name: decor.name,
|
||||
@@ -3001,12 +2986,7 @@ impl<I: Iterator<Item = u32>> Parser<I> {
|
||||
}
|
||||
|
||||
let inner = crate::TypeInner::Struct {
|
||||
level: match block_decor {
|
||||
Some(_) => crate::StructLevel::Root,
|
||||
None => crate::StructLevel::Normal {
|
||||
alignment: struct_alignment,
|
||||
},
|
||||
},
|
||||
top_level: block_decor.is_some(),
|
||||
span: match members.last() {
|
||||
Some(member) => {
|
||||
let end = member.offset + module.types[member.ty].inner.span(&module.constants);
|
||||
|
||||
@@ -3,14 +3,15 @@
|
||||
//! [wgsl]: https://gpuweb.github.io/gpuweb/wgsl.html
|
||||
|
||||
mod conv;
|
||||
mod layout;
|
||||
mod lexer;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use crate::{
|
||||
arena::{Arena, Handle},
|
||||
proc::{ensure_block_returns, ResolveContext, ResolveError, TypeResolution},
|
||||
proc::{
|
||||
ensure_block_returns, Alignment, Layouter, ResolveContext, ResolveError, TypeResolution,
|
||||
},
|
||||
FastHashMap,
|
||||
};
|
||||
|
||||
@@ -607,7 +608,7 @@ impl std::error::Error for ParseError {
|
||||
pub struct Parser {
|
||||
scopes: Vec<Scope>,
|
||||
lookup_type: FastHashMap<String, Handle<crate::Type>>,
|
||||
layouter: layout::Layouter,
|
||||
layouter: Layouter,
|
||||
}
|
||||
|
||||
impl Parser {
|
||||
@@ -1557,9 +1558,9 @@ impl Parser {
|
||||
lexer: &mut Lexer<'a>,
|
||||
type_arena: &mut Arena<crate::Type>,
|
||||
const_arena: &mut Arena<crate::Constant>,
|
||||
) -> Result<(Vec<crate::StructMember>, u32, crate::Alignment), Error<'a>> {
|
||||
) -> Result<(Vec<crate::StructMember>, u32), Error<'a>> {
|
||||
let mut offset = 0;
|
||||
let mut alignment = crate::Alignment::new(1).unwrap();
|
||||
let mut alignment = Alignment::new(1).unwrap();
|
||||
let mut members = Vec::new();
|
||||
|
||||
lexer.expect(Token::Paren('{'))?;
|
||||
@@ -1606,8 +1607,8 @@ impl Parser {
|
||||
let name = match lexer.next() {
|
||||
(Token::Word(word), _) => word,
|
||||
(Token::Paren('}'), _) => {
|
||||
let span = layout::Layouter::round_up(alignment, offset);
|
||||
return Ok((members, span, alignment));
|
||||
let span = Layouter::round_up(alignment, offset);
|
||||
return Ok((members, span));
|
||||
}
|
||||
other => return Err(Error::Unexpected(other, "field name")),
|
||||
};
|
||||
@@ -1615,7 +1616,8 @@ impl Parser {
|
||||
let (ty, _access) = self.parse_type_decl(lexer, None, type_arena, const_arena)?;
|
||||
lexer.expect(Token::Separator(';'))?;
|
||||
|
||||
self.layouter.update(type_arena, const_arena);
|
||||
self.layouter.update(type_arena, const_arena).unwrap();
|
||||
|
||||
let (range, align) = self.layouter.member_placement(offset, ty, align, size);
|
||||
alignment = alignment.max(align);
|
||||
offset = range.end;
|
||||
@@ -2603,16 +2605,12 @@ impl Parser {
|
||||
(Token::Separator(';'), _) => {}
|
||||
(Token::Word("struct"), _) => {
|
||||
let name = lexer.next_ident()?;
|
||||
let (members, span, alignment) =
|
||||
let (members, span) =
|
||||
self.parse_struct_body(lexer, &mut module.types, &mut module.constants)?;
|
||||
let ty = module.types.fetch_or_append(crate::Type {
|
||||
name: Some(name.to_string()),
|
||||
inner: crate::TypeInner::Struct {
|
||||
level: if is_block {
|
||||
crate::StructLevel::Root
|
||||
} else {
|
||||
crate::StructLevel::Normal { alignment }
|
||||
},
|
||||
top_level: is_block,
|
||||
members,
|
||||
span,
|
||||
},
|
||||
|
||||
17
src/lib.rs
17
src/lib.rs
@@ -50,7 +50,6 @@ pub use crate::arena::{Arena, Handle, Range};
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
hash::BuildHasherDefault,
|
||||
num::NonZeroU32,
|
||||
};
|
||||
|
||||
#[cfg(feature = "deserialize")]
|
||||
@@ -169,7 +168,6 @@ pub enum BuiltIn {
|
||||
|
||||
/// Number of bytes per scalar.
|
||||
pub type Bytes = u8;
|
||||
pub type Alignment = NonZeroU32;
|
||||
|
||||
/// Number of components in a vector.
|
||||
#[repr(u8)]
|
||||
@@ -358,18 +356,6 @@ pub enum ImageClass {
|
||||
Storage(StorageFormat),
|
||||
}
|
||||
|
||||
/// Qualifier of the type level, at which a struct can be used.
|
||||
#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
|
||||
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
||||
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
|
||||
pub enum StructLevel {
|
||||
/// This is a root level struct, it can't be nested inside
|
||||
/// other composite types.
|
||||
Root,
|
||||
/// This is a normal struct, and it has to be aligned for nesting.
|
||||
Normal { alignment: Alignment },
|
||||
}
|
||||
|
||||
/// A data type declared in the module.
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[cfg_attr(feature = "serialize", derive(Serialize))]
|
||||
@@ -420,8 +406,9 @@ pub enum TypeInner {
|
||||
},
|
||||
/// User-defined structure.
|
||||
Struct {
|
||||
level: StructLevel,
|
||||
top_level: bool,
|
||||
members: Vec<StructMember>,
|
||||
//TODO: should this be unaligned?
|
||||
span: u32,
|
||||
},
|
||||
/// Possibly multidimensional array of texels.
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
use crate::arena::{Arena, Handle};
|
||||
use std::{num::NonZeroU32, ops};
|
||||
|
||||
pub type Alignment = NonZeroU32;
|
||||
|
||||
/// Alignment information for a type.
|
||||
#[derive(Clone, Copy, Debug, Hash, PartialEq)]
|
||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
|
||||
#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
|
||||
pub struct TypeLayout {
|
||||
pub size: u32,
|
||||
pub alignment: crate::Alignment,
|
||||
pub alignment: Alignment,
|
||||
}
|
||||
|
||||
/// Helper processor that derives the sizes of all types.
|
||||
@@ -20,12 +22,23 @@ pub struct Layouter {
|
||||
layouts: Vec<TypeLayout>,
|
||||
}
|
||||
|
||||
impl ops::Index<Handle<crate::Type>> for Layouter {
|
||||
type Output = TypeLayout;
|
||||
fn index(&self, handle: Handle<crate::Type>) -> &TypeLayout {
|
||||
&self.layouts[handle.index()]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, thiserror::Error)]
|
||||
#[error("Base type {0:?} is out of bounds")]
|
||||
pub struct InvalidBaseType(pub Handle<crate::Type>);
|
||||
|
||||
impl Layouter {
|
||||
pub fn clear(&mut self) {
|
||||
self.layouts.clear();
|
||||
}
|
||||
|
||||
pub fn round_up(alignment: crate::Alignment, offset: u32) -> u32 {
|
||||
pub fn round_up(alignment: Alignment, offset: u32) -> u32 {
|
||||
match offset & (alignment.get() - 1) {
|
||||
0 => offset,
|
||||
other => offset + alignment.get() - other,
|
||||
@@ -36,9 +49,9 @@ impl Layouter {
|
||||
&self,
|
||||
offset: u32,
|
||||
ty: Handle<crate::Type>,
|
||||
align: Option<crate::Alignment>,
|
||||
align: Option<Alignment>,
|
||||
size: Option<NonZeroU32>,
|
||||
) -> (ops::Range<u32>, crate::Alignment) {
|
||||
) -> (ops::Range<u32>, Alignment) {
|
||||
let layout = self.layouts[ty.index()];
|
||||
let alignment = align.unwrap_or(layout.alignment);
|
||||
let start = Self::round_up(alignment, offset);
|
||||
@@ -49,14 +62,19 @@ impl Layouter {
|
||||
(start..start + span, alignment)
|
||||
}
|
||||
|
||||
pub fn update(&mut self, types: &Arena<crate::Type>, constants: &Arena<crate::Constant>) {
|
||||
pub fn update(
|
||||
&mut self,
|
||||
types: &Arena<crate::Type>,
|
||||
constants: &Arena<crate::Constant>,
|
||||
) -> Result<(), InvalidBaseType> {
|
||||
use crate::TypeInner as Ti;
|
||||
for (_, ty) in types.iter().skip(self.layouts.len()) {
|
||||
|
||||
for (ty_handle, ty) in types.iter().skip(self.layouts.len()) {
|
||||
let size = ty.inner.span(constants);
|
||||
let layout = match ty.inner {
|
||||
Ti::Scalar { width, .. } => TypeLayout {
|
||||
size,
|
||||
alignment: crate::Alignment::new(width as u32).unwrap(),
|
||||
alignment: Alignment::new(width as u32).unwrap(),
|
||||
},
|
||||
Ti::Vector {
|
||||
size: vec_size,
|
||||
@@ -70,7 +88,7 @@ impl Layouter {
|
||||
} else {
|
||||
2
|
||||
};
|
||||
crate::Alignment::new((count * width) as u32).unwrap()
|
||||
Alignment::new((count * width) as u32).unwrap()
|
||||
},
|
||||
},
|
||||
Ti::Matrix {
|
||||
@@ -81,35 +99,52 @@ impl Layouter {
|
||||
size,
|
||||
alignment: {
|
||||
let count = if rows >= crate::VectorSize::Tri { 4 } else { 2 };
|
||||
crate::Alignment::new((count * width) as u32).unwrap()
|
||||
Alignment::new((count * width) as u32).unwrap()
|
||||
},
|
||||
},
|
||||
Ti::Pointer { .. } | Ti::ValuePointer { .. } => TypeLayout {
|
||||
size,
|
||||
alignment: crate::Alignment::new(1).unwrap(),
|
||||
alignment: Alignment::new(1).unwrap(),
|
||||
},
|
||||
Ti::Array { stride, .. } => TypeLayout {
|
||||
size,
|
||||
alignment: crate::Alignment::new(stride).unwrap(),
|
||||
},
|
||||
Ti::Struct {
|
||||
ref level,
|
||||
members: _,
|
||||
span,
|
||||
Ti::Array {
|
||||
base,
|
||||
stride: _,
|
||||
size: _,
|
||||
} => TypeLayout {
|
||||
size: span,
|
||||
alignment: match *level {
|
||||
crate::StructLevel::Root => crate::Alignment::new(1).unwrap(),
|
||||
crate::StructLevel::Normal { alignment } => alignment,
|
||||
size,
|
||||
alignment: if base < ty_handle {
|
||||
self[base].alignment
|
||||
} else {
|
||||
return Err(InvalidBaseType(base));
|
||||
},
|
||||
},
|
||||
Ti::Struct {
|
||||
top_level: _,
|
||||
span,
|
||||
ref members,
|
||||
} => {
|
||||
let mut alignment = Alignment::new(1).unwrap();
|
||||
for member in members {
|
||||
alignment = if member.ty < ty_handle {
|
||||
alignment.max(self[member.ty].alignment)
|
||||
} else {
|
||||
return Err(InvalidBaseType(member.ty));
|
||||
};
|
||||
}
|
||||
TypeLayout {
|
||||
size: span,
|
||||
alignment,
|
||||
}
|
||||
}
|
||||
Ti::Image { .. } | Ti::Sampler { .. } => TypeLayout {
|
||||
size,
|
||||
alignment: crate::Alignment::new(1).unwrap(),
|
||||
alignment: Alignment::new(1).unwrap(),
|
||||
},
|
||||
};
|
||||
debug_assert!(ty.inner.span(constants) <= layout.size);
|
||||
self.layouts.push(layout);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,12 @@
|
||||
//! Module processing functionality.
|
||||
|
||||
mod interpolator;
|
||||
mod layouter;
|
||||
mod namer;
|
||||
mod terminator;
|
||||
mod typifier;
|
||||
|
||||
pub use layouter::{Alignment, InvalidBaseType, Layouter, TypeLayout};
|
||||
pub use namer::{EntryPointIndex, NameKey, Namer};
|
||||
pub use terminator::ensure_block_returns;
|
||||
pub use typifier::{ResolveContext, ResolveError, TypeResolution};
|
||||
|
||||
@@ -262,7 +262,7 @@ impl VaryingContext<'_> {
|
||||
match self.types[self.ty].inner {
|
||||
//TODO: check the member types
|
||||
crate::TypeInner::Struct {
|
||||
level: crate::StructLevel::Normal { .. },
|
||||
top_level: false,
|
||||
ref members,
|
||||
..
|
||||
} => {
|
||||
@@ -303,7 +303,7 @@ impl super::Validator {
|
||||
}
|
||||
(
|
||||
crate::StorageAccess::all(),
|
||||
TypeFlags::DATA | TypeFlags::HOST_SHARED | TypeFlags::BLOCK,
|
||||
TypeFlags::DATA | TypeFlags::HOST_SHARED | TypeFlags::TOP_LEVEL,
|
||||
true,
|
||||
)
|
||||
}
|
||||
@@ -315,7 +315,10 @@ impl super::Validator {
|
||||
}
|
||||
(
|
||||
crate::StorageAccess::empty(),
|
||||
TypeFlags::DATA | TypeFlags::SIZED | TypeFlags::HOST_SHARED | TypeFlags::BLOCK,
|
||||
TypeFlags::DATA
|
||||
| TypeFlags::SIZED
|
||||
| TypeFlags::HOST_SHARED
|
||||
| TypeFlags::TOP_LEVEL,
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ mod r#type;
|
||||
|
||||
use crate::{
|
||||
arena::{Arena, Handle},
|
||||
proc::{InvalidBaseType, Layouter},
|
||||
FastHashSet,
|
||||
};
|
||||
use bit_set::BitSet;
|
||||
@@ -91,6 +92,7 @@ pub struct Validator {
|
||||
flags: ValidationFlags,
|
||||
capabilities: Capabilities,
|
||||
types: Vec<r#type::TypeInfo>,
|
||||
layouter: Layouter,
|
||||
location_mask: BitSet,
|
||||
bind_group_masks: Vec<BitSet>,
|
||||
select_cases: FastHashSet<i32>,
|
||||
@@ -112,6 +114,8 @@ pub enum ConstantError {
|
||||
|
||||
#[derive(Clone, Debug, thiserror::Error)]
|
||||
pub enum ValidationError {
|
||||
#[error(transparent)]
|
||||
Layouter(#[from] InvalidBaseType),
|
||||
#[error("Type {handle:?} '{name}' is invalid")]
|
||||
Type {
|
||||
handle: Handle<crate::Type>,
|
||||
@@ -196,6 +200,7 @@ impl Validator {
|
||||
flags,
|
||||
capabilities,
|
||||
types: Vec::new(),
|
||||
layouter: Layouter::default(),
|
||||
location_mask: BitSet::new(),
|
||||
bind_group_masks: Vec::new(),
|
||||
select_cases: FastHashSet::default(),
|
||||
@@ -246,6 +251,7 @@ impl Validator {
|
||||
/// Check the given module to be valid.
|
||||
pub fn validate(&mut self, module: &crate::Module) -> Result<ModuleInfo, ValidationError> {
|
||||
self.reset_types(module.types.len());
|
||||
self.layouter.update(&module.types, &module.constants)?;
|
||||
|
||||
if self.flags.contains(ValidationFlags::CONSTANTS) {
|
||||
for (handle, constant) in module.constants.iter() {
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
use super::Capabilities;
|
||||
use crate::arena::{Arena, Handle};
|
||||
use crate::{
|
||||
arena::{Arena, Handle},
|
||||
proc::Alignment,
|
||||
};
|
||||
|
||||
bitflags::bitflags! {
|
||||
#[repr(transparent)]
|
||||
@@ -13,7 +16,7 @@ bitflags::bitflags! {
|
||||
/// Can be used for host-shareable structures.
|
||||
const HOST_SHARED = 0x8;
|
||||
/// This is a top-level host-shareable type.
|
||||
const BLOCK = 0x10;
|
||||
const TOP_LEVEL = 0x10;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,12 +26,6 @@ pub enum Disalignment {
|
||||
ArrayStride { stride: u32, alignment: u32 },
|
||||
#[error("The struct span {span}, is not a multiple of the required alignment {alignment}")]
|
||||
StructSpan { span: u32, alignment: u32 },
|
||||
#[error("The struct span {alignment}, is not a multiple of the member[{member_index}] alignment {member_alignment}")]
|
||||
StructAlignment {
|
||||
alignment: u32,
|
||||
member_index: u32,
|
||||
member_alignment: u32,
|
||||
},
|
||||
#[error("The struct member[{index}] offset {offset} is not a multiple of the required alignment {alignment}")]
|
||||
MemberOffset {
|
||||
index: u32,
|
||||
@@ -63,27 +60,26 @@ pub enum TypeError {
|
||||
"Structure member[{index}] at {offset} and size {size} crosses the structure boundary"
|
||||
)]
|
||||
MemberOutOfBounds { index: u32, offset: u32, size: u32 },
|
||||
#[error("The composite type contains a block structure")]
|
||||
NestedBlock,
|
||||
#[error("The composite type contains a top-level structure")]
|
||||
NestedTopLevel,
|
||||
}
|
||||
|
||||
// Only makes sense if `flags.contains(HOST_SHARED)`
|
||||
type LayoutCompatibility = Result<Option<crate::Alignment>, (Handle<crate::Type>, Disalignment)>;
|
||||
type LayoutCompatibility = Result<Option<Alignment>, (Handle<crate::Type>, Disalignment)>;
|
||||
|
||||
fn check_member_layout(
|
||||
accum: &mut LayoutCompatibility,
|
||||
member: &crate::StructMember,
|
||||
member_index: u32,
|
||||
member_layout: LayoutCompatibility,
|
||||
struct_level: crate::StructLevel,
|
||||
ty_handle: Handle<crate::Type>,
|
||||
parent_handle: Handle<crate::Type>,
|
||||
) {
|
||||
*accum = match (*accum, member_layout) {
|
||||
(Ok(cur_alignment), Ok(align)) => {
|
||||
let align = align.unwrap().get();
|
||||
if member.offset % align != 0 {
|
||||
Err((
|
||||
ty_handle,
|
||||
parent_handle,
|
||||
Disalignment::MemberOffset {
|
||||
index: member_index,
|
||||
offset: member.offset,
|
||||
@@ -91,32 +87,14 @@ fn check_member_layout(
|
||||
},
|
||||
))
|
||||
} else {
|
||||
match struct_level {
|
||||
crate::StructLevel::Normal { alignment } if alignment.get() % align != 0 => {
|
||||
Err((
|
||||
ty_handle,
|
||||
Disalignment::StructAlignment {
|
||||
alignment: alignment.get(),
|
||||
member_index,
|
||||
member_alignment: align,
|
||||
},
|
||||
))
|
||||
}
|
||||
_ => {
|
||||
let combined_alignment =
|
||||
((cur_alignment.unwrap().get() - 1) | (align - 1)) + 1;
|
||||
Ok(crate::Alignment::new(combined_alignment))
|
||||
}
|
||||
}
|
||||
let combined_alignment = ((cur_alignment.unwrap().get() - 1) | (align - 1)) + 1;
|
||||
Ok(Alignment::new(combined_alignment))
|
||||
}
|
||||
}
|
||||
(Err(e), _) | (_, Err(e)) => Err(e),
|
||||
};
|
||||
}
|
||||
|
||||
// For the uniform buffer alignment, array strides and struct sizes must be multiples of 16.
|
||||
const UNIFORM_LAYOUT_ALIGNMENT_MASK: u32 = 0xF;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(super) struct TypeInfo {
|
||||
pub flags: TypeFlags,
|
||||
@@ -134,7 +112,7 @@ impl TypeInfo {
|
||||
}
|
||||
|
||||
fn new(flags: TypeFlags, align: u32) -> Self {
|
||||
let alignment = crate::Alignment::new(align);
|
||||
let alignment = Alignment::new(align);
|
||||
TypeInfo {
|
||||
flags,
|
||||
uniform_layout: Ok(alignment),
|
||||
@@ -157,6 +135,7 @@ impl super::Validator {
|
||||
pub(super) fn reset_types(&mut self, size: usize) {
|
||||
self.types.clear();
|
||||
self.types.resize(size, TypeInfo::dummy());
|
||||
self.layouter.clear();
|
||||
}
|
||||
|
||||
pub(super) fn validate_type(
|
||||
@@ -234,8 +213,8 @@ impl super::Validator {
|
||||
if !base_info.flags.contains(TypeFlags::DATA | TypeFlags::SIZED) {
|
||||
return Err(TypeError::InvalidArrayBaseType(base));
|
||||
}
|
||||
if base_info.flags.contains(TypeFlags::BLOCK) {
|
||||
return Err(TypeError::NestedBlock);
|
||||
if base_info.flags.contains(TypeFlags::TOP_LEVEL) {
|
||||
return Err(TypeError::NestedTopLevel);
|
||||
}
|
||||
|
||||
let base_size = types[base].inner.span(constants);
|
||||
@@ -243,11 +222,12 @@ impl super::Validator {
|
||||
return Err(TypeError::InsufficientArrayStride { stride, base_size });
|
||||
}
|
||||
|
||||
let general_alignment = self.layouter[base].alignment;
|
||||
let uniform_layout = match base_info.uniform_layout {
|
||||
Ok(base_alignment) => {
|
||||
// combine the alignment requirements
|
||||
let align = ((base_alignment.unwrap().get() - 1)
|
||||
| UNIFORM_LAYOUT_ALIGNMENT_MASK)
|
||||
| (general_alignment.get() - 1))
|
||||
+ 1;
|
||||
if stride % align != 0 {
|
||||
Err((
|
||||
@@ -258,14 +238,16 @@ impl super::Validator {
|
||||
},
|
||||
))
|
||||
} else {
|
||||
Ok(crate::Alignment::new(align))
|
||||
Ok(Alignment::new(align))
|
||||
}
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
};
|
||||
let storage_layout = match base_info.storage_layout {
|
||||
Ok(base_alignment) => {
|
||||
let align = base_alignment.unwrap().get();
|
||||
let align = ((base_alignment.unwrap().get() - 1)
|
||||
| (general_alignment.get() - 1))
|
||||
+ 1;
|
||||
if stride % align != 0 {
|
||||
Err((
|
||||
handle,
|
||||
@@ -275,7 +257,7 @@ impl super::Validator {
|
||||
},
|
||||
))
|
||||
} else {
|
||||
Ok(base_alignment)
|
||||
Ok(Alignment::new(align))
|
||||
}
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
@@ -324,7 +306,7 @@ impl super::Validator {
|
||||
}
|
||||
}
|
||||
Ti::Struct {
|
||||
level,
|
||||
top_level,
|
||||
ref members,
|
||||
span,
|
||||
} => {
|
||||
@@ -336,6 +318,7 @@ impl super::Validator {
|
||||
1,
|
||||
);
|
||||
let mut min_offset = 0;
|
||||
|
||||
for (i, member) in members.iter().enumerate() {
|
||||
if member.ty >= handle {
|
||||
return Err(TypeError::UnresolvedBase(member.ty));
|
||||
@@ -344,13 +327,11 @@ impl super::Validator {
|
||||
if !base_info.flags.contains(TypeFlags::DATA) {
|
||||
return Err(TypeError::InvalidData(member.ty));
|
||||
}
|
||||
if level == crate::StructLevel::Root
|
||||
&& !base_info.flags.contains(TypeFlags::INTERFACE)
|
||||
{
|
||||
if top_level && !base_info.flags.contains(TypeFlags::INTERFACE) {
|
||||
return Err(TypeError::InvalidBlockType(member.ty));
|
||||
}
|
||||
if base_info.flags.contains(TypeFlags::BLOCK) {
|
||||
return Err(TypeError::NestedBlock);
|
||||
if base_info.flags.contains(TypeFlags::TOP_LEVEL) {
|
||||
return Err(TypeError::NestedTopLevel);
|
||||
}
|
||||
ti.flags &= base_info.flags;
|
||||
|
||||
@@ -382,7 +363,6 @@ impl super::Validator {
|
||||
member,
|
||||
i as u32,
|
||||
base_info.uniform_layout,
|
||||
level,
|
||||
handle,
|
||||
);
|
||||
check_member_layout(
|
||||
@@ -390,7 +370,6 @@ impl super::Validator {
|
||||
member,
|
||||
i as u32,
|
||||
base_info.storage_layout,
|
||||
level,
|
||||
handle,
|
||||
);
|
||||
|
||||
@@ -406,24 +385,16 @@ impl super::Validator {
|
||||
}
|
||||
}
|
||||
}
|
||||
if let crate::StructLevel::Root = level {
|
||||
ti.flags |= TypeFlags::BLOCK;
|
||||
if top_level {
|
||||
ti.flags |= TypeFlags::TOP_LEVEL;
|
||||
}
|
||||
|
||||
// disabled temporarily, see https://github.com/gpuweb/gpuweb/issues/1558
|
||||
const CHECK_STRUCT_SIZE: bool = false;
|
||||
if CHECK_STRUCT_SIZE
|
||||
&& ti.uniform_layout.is_ok()
|
||||
&& span & UNIFORM_LAYOUT_ALIGNMENT_MASK != 0
|
||||
{
|
||||
ti.uniform_layout = Err((
|
||||
handle,
|
||||
Disalignment::StructSpan {
|
||||
span,
|
||||
alignment: UNIFORM_LAYOUT_ALIGNMENT_MASK + 1,
|
||||
},
|
||||
));
|
||||
let alignment = self.layouter[handle].alignment.get();
|
||||
if span % alignment != 0 {
|
||||
ti.uniform_layout = Err((handle, Disalignment::StructSpan { span, alignment }));
|
||||
ti.storage_layout = Err((handle, Disalignment::StructSpan { span, alignment }));
|
||||
}
|
||||
|
||||
ti
|
||||
}
|
||||
Ti::Image { .. } | Ti::Sampler { .. } => TypeInfo::new(TypeFlags::empty(), 0),
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
(
|
||||
name: Some("PrimeIndices"),
|
||||
inner: Struct(
|
||||
level: Root,
|
||||
top_level: true,
|
||||
members: [
|
||||
(
|
||||
name: Some("data"),
|
||||
|
||||
@@ -102,7 +102,7 @@
|
||||
(
|
||||
name: Some("Globals"),
|
||||
inner: Struct(
|
||||
level: Root,
|
||||
top_level: true,
|
||||
members: [
|
||||
(
|
||||
name: Some("num_lights"),
|
||||
@@ -146,9 +146,7 @@
|
||||
(
|
||||
name: Some("Light"),
|
||||
inner: Struct(
|
||||
level: Normal(
|
||||
alignment: 16,
|
||||
),
|
||||
top_level: false,
|
||||
members: [
|
||||
(
|
||||
name: Some("proj"),
|
||||
@@ -183,7 +181,7 @@
|
||||
(
|
||||
name: Some("Lights"),
|
||||
inner: Struct(
|
||||
level: Root,
|
||||
top_level: true,
|
||||
members: [
|
||||
(
|
||||
name: Some("data"),
|
||||
|
||||
@@ -120,7 +120,7 @@ fn invalid_arrays() {
|
||||
type Bad = array<Block, 4>;
|
||||
"#:
|
||||
Err(naga::valid::ValidationError::Type {
|
||||
error: naga::valid::TypeError::NestedBlock,
|
||||
error: naga::valid::TypeError::NestedTopLevel,
|
||||
..
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user