Align the block decoration with SPIR-V, require storage buffers

This commit is contained in:
Dzmitry Malyshau
2021-03-25 11:30:18 -04:00
committed by Dzmitry Malyshau
parent e87f57d82c
commit 7d042337e2
17 changed files with 107 additions and 115 deletions

View File

@@ -1,13 +1,13 @@
use spirv::Word;
pub(crate) fn bytes_to_words(bytes: &[u8]) -> Vec<Word> {
pub(super) fn bytes_to_words(bytes: &[u8]) -> Vec<Word> {
bytes
.chunks(4)
.map(|chars| chars.iter().rev().fold(0u32, |u, c| (u << 8) | *c as u32))
.collect()
}
pub(crate) fn string_to_words(input: &str) -> Vec<Word> {
pub(super) fn string_to_words(input: &str) -> Vec<Word> {
let bytes = input.as_bytes();
let mut words = bytes_to_words(bytes);
@@ -18,3 +18,15 @@ pub(crate) fn string_to_words(input: &str) -> Vec<Word> {
words
}
pub(super) fn map_storage_class(class: crate::StorageClass) -> spirv::StorageClass {
match class {
crate::StorageClass::Handle => spirv::StorageClass::UniformConstant,
crate::StorageClass::Function => spirv::StorageClass::Function,
crate::StorageClass::Private => spirv::StorageClass::Private,
crate::StorageClass::Storage => spirv::StorageClass::StorageBuffer,
crate::StorageClass::Uniform => spirv::StorageClass::Uniform,
crate::StorageClass::WorkGroup => spirv::StorageClass::Workgroup,
crate::StorageClass::PushConstant => spirv::StorageClass::PushConstant,
}
}

View File

@@ -81,6 +81,12 @@ impl super::Instruction {
// Extension Instructions
//
pub(super) fn extension(name: &str) -> Self {
let mut instruction = Self::new(Op::Extension);
instruction.add_operands(helpers::string_to_words(name));
instruction
}
pub(super) fn ext_inst_import(id: Word, name: &str) -> Self {
let mut instruction = Self::new(Op::ExtInstImport);
instruction.set_result(id);

View File

@@ -23,26 +23,6 @@ impl PhysicalLayout {
sink.extend(iter::once(self.bound));
sink.extend(iter::once(self.instruction_schema));
}
pub(super) fn supports_storage_buffers(&self) -> bool {
self.version >= 0x10300
}
pub(super) fn map_storage_class(&self, class: crate::StorageClass) -> spirv::StorageClass {
match class {
crate::StorageClass::Handle => spirv::StorageClass::UniformConstant,
crate::StorageClass::Function => spirv::StorageClass::Function,
crate::StorageClass::Private => spirv::StorageClass::Private,
crate::StorageClass::Storage if self.supports_storage_buffers() => {
spirv::StorageClass::StorageBuffer
}
crate::StorageClass::Storage | crate::StorageClass::Uniform => {
spirv::StorageClass::Uniform
}
crate::StorageClass::WorkGroup => spirv::StorageClass::Workgroup,
crate::StorageClass::PushConstant => spirv::StorageClass::PushConstant,
}
}
}
impl LogicalLayout {

View File

@@ -1,3 +1,6 @@
/*! Standard Portable Intermediate Representation (SPIR-V) backend
!*/
mod helpers;
mod instructions;
mod layout;

View File

@@ -1,5 +1,6 @@
/*! Standard Portable Intermediate Representation (SPIR-V) backend !*/
use super::{Instruction, LogicalLayout, Options, PhysicalLayout, WriterFlags};
use super::{
helpers::map_storage_class, Instruction, LogicalLayout, Options, PhysicalLayout, WriterFlags,
};
use crate::{
arena::{Arena, Handle},
proc::{Layouter, TypeResolution},
@@ -141,7 +142,7 @@ impl PhysicalLayout {
},
crate::TypeInner::Pointer { base, class } => LocalType::Pointer {
base,
class: self.map_storage_class(class),
class: map_storage_class(class),
},
crate::TypeInner::ValuePointer {
size,
@@ -152,7 +153,7 @@ impl PhysicalLayout {
vector_size: size,
kind,
width,
pointer_class: Some(self.map_storage_class(class)),
pointer_class: Some(map_storage_class(class)),
},
_ => return None,
})
@@ -266,9 +267,6 @@ pub struct Writer {
lookup_constant: crate::FastHashMap<Handle<crate::Constant>, Word>,
global_variables: Vec<GlobalVariable>,
cached: CachedExpressions,
// TODO: this is a type property that depends on the global variable that uses it
// so it may require us to duplicate the type!
struct_type_handles: crate::FastHashMap<Handle<crate::Type>, crate::StorageAccess>,
gl450_ext_inst_id: Word,
temp_chain: Vec<Word>,
}
@@ -283,6 +281,7 @@ impl Writer {
let mut id_gen = IdGenerator::default();
let gl450_ext_inst_id = id_gen.next();
let void_type = id_gen.next();
Ok(Writer {
physical_layout: PhysicalLayout::new(raw_version),
logical_layout: LogicalLayout::default(),
@@ -299,7 +298,6 @@ impl Writer {
lookup_constant: crate::FastHashMap::default(),
global_variables: Vec::new(),
cached: CachedExpressions::default(),
struct_type_handles: crate::FastHashMap::default(),
gl450_ext_inst_id,
temp_chain: Vec::new(),
})
@@ -815,18 +813,10 @@ impl Writer {
crate::ArraySize::Dynamic => Instruction::type_runtime_array(id, type_id),
}
}
crate::TypeInner::Struct {
block: true,
ref members,
} => {
if let Some(&access) = self.struct_type_handles.get(&handle) {
let decoration = if access.is_empty() {
spirv::Decoration::Block
} else {
spirv::Decoration::BufferBlock
};
crate::TypeInner::Struct { block, ref members } => {
if block {
self.annotations
.push(Instruction::decorate(id, decoration, &[]));
.push(Instruction::decorate(id, spirv::Decoration::Block, &[]));
}
let mut current_offset = 0;
@@ -877,20 +867,9 @@ impl Writer {
}
Instruction::type_struct(id, member_ids.as_slice())
}
crate::TypeInner::Struct {
block: false,
ref members,
} => {
let mut member_ids = Vec::with_capacity(members.len());
for member in members {
let member_id = self.get_type_id(arena, LookupType::Handle(member.ty))?;
member_ids.push(member_id);
}
Instruction::type_struct(id, member_ids.as_slice())
}
crate::TypeInner::Pointer { base, class } => {
let type_id = self.get_type_id(arena, LookupType::Handle(base))?;
let raw_class = self.physical_layout.map_storage_class(class);
let raw_class = map_storage_class(class);
Instruction::type_pointer(id, raw_class, type_id)
}
crate::TypeInner::ValuePointer {
@@ -899,7 +878,7 @@ impl Writer {
width,
class,
} => {
let raw_class = self.physical_layout.map_storage_class(class);
let raw_class = map_storage_class(class);
let type_id = self.get_type_id(
arena,
LookupType::Local(LocalType::Value {
@@ -1091,9 +1070,7 @@ impl Writer {
) -> Result<(Instruction, Word, spirv::StorageClass), Error> {
let id = self.id_gen.next();
let class = self
.physical_layout
.map_storage_class(global_variable.class);
let class = map_storage_class(global_variable.class);
self.check(class.required_capabilities())?;
let init_word = global_variable
@@ -2345,6 +2322,15 @@ impl Writer {
ir_module: &crate::Module,
mod_info: &ModuleInfo,
) -> Result<(), Error> {
let has_storage_buffers = ir_module
.global_variables
.iter()
.any(|(_, var)| var.class == crate::StorageClass::Storage);
if self.physical_layout.version < 0x10300 && has_storage_buffers {
// enable the storage buffer class on < SPV-1.3
Instruction::extension("SPV_KHR_storage_buffer_storage_class")
.to_words(&mut self.logical_layout.extensions);
}
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);
@@ -2371,14 +2357,6 @@ impl Writer {
}
}
// then gather the struct type handles, then affect types
self.struct_type_handles.clear();
for (_, var) in ir_module.global_variables.iter() {
if let crate::TypeInner::Struct { .. } = ir_module.types[var.ty].inner {
self.struct_type_handles.insert(var.ty, var.storage_access);
}
}
// then all types, some of them may rely on constants and struct type set
for (handle, _) in ir_module.types.iter() {
self.write_type_declaration_arena(&ir_module.types, &mod_info.layouter, handle)?;

View File

@@ -2406,7 +2406,6 @@ impl<I: Iterator<Item = u32>> Parser<I> {
let mut members = Vec::with_capacity(inst.wc as usize - 2);
let mut member_type_ids = Vec::with_capacity(members.capacity());
let mut host_shared = false;
for i in 0..u32::from(inst.wc) - 2 {
let type_id = self.next()?;
member_type_ids.push(type_id);
@@ -2415,8 +2414,6 @@ impl<I: Iterator<Item = u32>> Parser<I> {
.future_member_decor
.remove(&(id, i))
.unwrap_or_default();
// this is a bit of a hack
host_shared |= decor.offset.is_some();
members.push(crate::StructMember {
name: decor.name,
ty,
@@ -2427,7 +2424,7 @@ impl<I: Iterator<Item = u32>> Parser<I> {
}
let inner = crate::TypeInner::Struct {
block: block_decor.is_some() || host_shared,
block: block_decor.is_some(),
members,
};
let ty_handle = module.types.append(crate::Type {

View File

@@ -393,6 +393,7 @@ pub enum TypeInner {
},
/// User-defined structure.
Struct {
/// This is a top-level host-shareable structure.
block: bool,
members: Vec<StructMember>,
},

View File

@@ -272,42 +272,32 @@ impl super::Validator {
let (allowed_storage_access, required_type_flags, is_resource) = match var.class {
crate::StorageClass::Function => return Err(GlobalVariableError::InvalidUsage),
crate::StorageClass::Storage => {
match types[var.ty].inner {
crate::TypeInner::Struct { block: true, .. } => {
if let Err((ty_handle, ref disalignment)) = type_info.storage_layout {
if self.flags.contains(ValidationFlags::STRUCT_LAYOUTS) {
return Err(GlobalVariableError::Alignment(
ty_handle,
disalignment.clone(),
));
}
}
if let Err((ty_handle, ref disalignment)) = type_info.storage_layout {
if self.flags.contains(ValidationFlags::STRUCT_LAYOUTS) {
return Err(GlobalVariableError::Alignment(
ty_handle,
disalignment.clone(),
));
}
_ => return Err(GlobalVariableError::InvalidType),
}
(
crate::StorageAccess::all(),
TypeFlags::DATA | TypeFlags::HOST_SHARED,
TypeFlags::DATA | TypeFlags::HOST_SHARED | TypeFlags::BLOCK,
true,
)
}
crate::StorageClass::Uniform => {
match types[var.ty].inner {
crate::TypeInner::Struct { block: true, .. } => {
if let Err((ty_handle, ref disalignment)) = type_info.uniform_layout {
if self.flags.contains(ValidationFlags::STRUCT_LAYOUTS) {
return Err(GlobalVariableError::Alignment(
ty_handle,
disalignment.clone(),
));
}
}
if let Err((ty_handle, ref disalignment)) = type_info.uniform_layout {
if self.flags.contains(ValidationFlags::STRUCT_LAYOUTS) {
return Err(GlobalVariableError::Alignment(
ty_handle,
disalignment.clone(),
));
}
_ => return Err(GlobalVariableError::InvalidType),
}
(
crate::StorageAccess::empty(),
TypeFlags::DATA | TypeFlags::SIZED | TypeFlags::HOST_SHARED,
TypeFlags::DATA | TypeFlags::SIZED | TypeFlags::HOST_SHARED | TypeFlags::BLOCK,
true,
)
}

View File

@@ -14,6 +14,8 @@ bitflags::bitflags! {
const INTERFACE = 0x4;
/// Can be used for host-shareable structures.
const HOST_SHARED = 0x8;
/// This is a top-level host-shareable type.
const BLOCK = 0x10;
}
}
@@ -61,6 +63,8 @@ pub enum TypeError {
size: u32,
base_size: u32,
},
#[error("The composite type contains a block structure")]
NestedBlock,
}
// Only makes sense if `flags.contains(HOST_SHARED)`
@@ -163,6 +167,9 @@ 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);
}
let base_layout = &layouter[base];
if let Some(stride) = stride {
@@ -239,7 +246,10 @@ impl super::Validator {
}
}
Ti::Struct { block, ref members } => {
let mut flags = TypeFlags::all();
let mut flags = TypeFlags::DATA
| TypeFlags::SIZED
| TypeFlags::HOST_SHARED
| TypeFlags::INTERFACE;
let mut uniform_layout = Ok(());
let mut storage_layout = Ok(());
let mut offset = 0;
@@ -248,13 +258,16 @@ impl super::Validator {
return Err(TypeError::UnresolvedBase(member.ty));
}
let base_info = &self.types[member.ty.index()];
flags &= base_info.flags;
if !base_info.flags.contains(TypeFlags::DATA) {
return Err(TypeError::InvalidData(member.ty));
}
if block && !base_info.flags.contains(TypeFlags::INTERFACE) {
return Err(TypeError::InvalidBlockType(member.ty));
}
if base_info.flags.contains(TypeFlags::BLOCK) {
return Err(TypeError::NestedBlock);
}
flags &= base_info.flags;
let base_layout = &layouter[member.ty];
let (range, _alignment) = layouter.member_placement(offset, member);
@@ -294,6 +307,9 @@ impl super::Validator {
uniform_layout = uniform_layout.or_else(|_| base_info.uniform_layout.clone());
storage_layout = storage_layout.or_else(|_| base_info.storage_layout.clone());
}
if block {
flags |= TypeFlags::BLOCK;
}
// disabled temporarily, see https://github.com/gpuweb/gpuweb/issues/1558
const CHECK_STRUCT_SIZE: bool = false;

View File

@@ -1,6 +1,5 @@
const NUM_PARTICLES: u32 = 1500u;
[[block]]
struct Particle {
pos : vec2<f32>;
vel : vec2<f32>;

View File

@@ -6,7 +6,6 @@ struct Globals {
[[group(0), binding(0)]]
var<uniform> u_globals: Globals;
[[block]]
struct Light {
proj: mat4x4<f32>;
pos: vec4<f32>;

View File

@@ -7,6 +7,7 @@ expression: dis
; Generator: rspirv
; Bound: 221
OpCapability Shader
OpExtension "SPV_KHR_storage_buffer_storage_class"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %43 "main" %40
@@ -53,7 +54,7 @@ OpMemberDecorate %17 4 Offset 16
OpMemberDecorate %17 5 Offset 20
OpMemberDecorate %17 6 Offset 24
OpDecorate %18 ArrayStride 16
OpDecorate %19 BufferBlock
OpDecorate %19 Block
OpMemberDecorate %19 0 Offset 0
OpDecorate %21 DescriptorSet 0
OpDecorate %21 Binding 0
@@ -84,9 +85,9 @@ OpDecorate %40 BuiltIn GlobalInvocationId
%20 = OpTypeVector %4 3
%22 = OpTypePointer Uniform %17
%21 = OpVariable %22 Uniform
%24 = OpTypePointer Uniform %19
%23 = OpVariable %24 Uniform
%25 = OpVariable %24 Uniform
%24 = OpTypePointer StorageBuffer %19
%23 = OpVariable %24 StorageBuffer
%25 = OpVariable %24 StorageBuffer
%27 = OpTypePointer Function %15
%33 = OpTypePointer Function %8
%38 = OpTypePointer Function %4
@@ -94,9 +95,9 @@ OpDecorate %40 BuiltIn GlobalInvocationId
%40 = OpVariable %41 Input
%44 = OpTypeFunction %2
%47 = OpTypeBool
%51 = OpTypePointer Uniform %18
%52 = OpTypePointer Uniform %16
%53 = OpTypePointer Uniform %15
%51 = OpTypePointer StorageBuffer %18
%52 = OpTypePointer StorageBuffer %16
%53 = OpTypePointer StorageBuffer %15
%54 = OpConstant %8 0
%55 = OpConstant %8 0
%58 = OpConstant %8 1

View File

@@ -7,6 +7,7 @@ expression: dis
; Generator: rspirv
; Bound: 62
OpCapability Shader
OpExtension "SPV_KHR_storage_buffer_storage_class"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %48 "main" %45
@@ -22,7 +23,7 @@ OpName %45 "global_id"
OpName %48 "main"
OpName %48 "main"
OpDecorate %8 ArrayStride 4
OpDecorate %9 BufferBlock
OpDecorate %9 Block
OpMemberDecorate %9 0 Offset 0
OpDecorate %11 DescriptorSet 0
OpDecorate %11 Binding 0
@@ -36,16 +37,16 @@ OpDecorate %45 BuiltIn GlobalInvocationId
%8 = OpTypeRuntimeArray %4
%9 = OpTypeStruct %8
%10 = OpTypeVector %4 3
%12 = OpTypePointer Uniform %9
%11 = OpVariable %12 Uniform
%12 = OpTypePointer StorageBuffer %9
%11 = OpVariable %12 StorageBuffer
%14 = OpTypePointer Function %4
%19 = OpTypeFunction %4 %4
%26 = OpTypeBool
%46 = OpTypePointer Input %10
%45 = OpVariable %46 Input
%49 = OpTypeFunction %2
%51 = OpTypePointer Uniform %8
%53 = OpTypePointer Uniform %4
%51 = OpTypePointer StorageBuffer %8
%53 = OpTypePointer StorageBuffer %4
%55 = OpTypeInt 32 1
%56 = OpConstant %55 0
%60 = OpConstant %55 0

View File

@@ -15,6 +15,8 @@ OpExecutionMode %47 OriginUpperLeft
OpSource GLSL 450
OpName %3 "c_scale"
OpName %9 "VertexOutput"
OpMemberName %9 0 "uv"
OpMemberName %9 1 "position"
OpName %12 "u_texture"
OpName %14 "u_sampler"
OpName %16 "out"
@@ -27,6 +29,8 @@ OpName %28 "main"
OpName %44 "uv"
OpName %47 "main"
OpName %47 "main"
OpMemberDecorate %9 0 Offset 0
OpMemberDecorate %9 1 Offset 8
OpDecorate %12 DescriptorSet 0
OpDecorate %12 Binding 0
OpDecorate %14 DescriptorSet 0

View File

@@ -150,7 +150,7 @@ expression: output
(
name: Some("Light"),
inner: Struct(
block: true,
block: false,
members: [
(
name: Some("proj"),

View File

@@ -7,6 +7,7 @@ expression: dis
; Generator: rspirv
; Bound: 137
OpCapability Shader
OpExtension "SPV_KHR_storage_buffer_storage_class"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %81 "fs_main" %73 %76 %79
@@ -41,7 +42,7 @@ OpMemberDecorate %17 0 MatrixStride 16
OpMemberDecorate %17 1 Offset 64
OpMemberDecorate %17 2 Offset 80
OpDecorate %18 ArrayStride 96
OpDecorate %19 BufferBlock
OpDecorate %19 Block
OpMemberDecorate %19 0 Offset 0
OpDecorate %25 DescriptorSet 0
OpDecorate %25 Binding 0
@@ -80,8 +81,8 @@ OpDecorate %79 Location 0
%24 = OpConstantComposite %23 %8 %8 %8
%26 = OpTypePointer Uniform %14
%25 = OpVariable %26 Uniform
%28 = OpTypePointer Uniform %19
%27 = OpVariable %28 Uniform
%28 = OpTypePointer StorageBuffer %19
%27 = OpVariable %28 StorageBuffer
%30 = OpTypePointer UniformConstant %20
%29 = OpVariable %30 UniformConstant
%32 = OpTypePointer UniformConstant %21
@@ -102,8 +103,8 @@ OpDecorate %79 Location 0
%82 = OpTypeFunction %2
%92 = OpTypePointer Uniform %13
%93 = OpConstant %56 0
%101 = OpTypePointer Uniform %18
%103 = OpTypePointer Uniform %17
%101 = OpTypePointer StorageBuffer %18
%103 = OpTypePointer StorageBuffer %17
%104 = OpConstant %56 0
%36 = OpFunction %4 None %37
%34 = OpFunctionParameter %10

View File

@@ -14,6 +14,8 @@ OpEntryPoint Fragment %108 "fs_main" %101 %104 %107
OpExecutionMode %108 OriginUpperLeft
OpSource GLSL 450
OpName %12 "VertexOutput"
OpMemberName %12 0 "position"
OpMemberName %12 1 "uv"
OpName %14 "Data"
OpMemberName %14 0 "proj_inv"
OpMemberName %14 1 "view"
@@ -32,6 +34,8 @@ OpName %101 "position"
OpName %104 "uv"
OpName %108 "fs_main"
OpName %108 "fs_main"
OpMemberDecorate %12 0 Offset 0
OpMemberDecorate %12 1 Offset 16
OpDecorate %14 Block
OpMemberDecorate %14 0 Offset 0
OpMemberDecorate %14 0 ColMajor