From 7d042337e29ce51248bd25b0339a579e744cde5b Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Thu, 25 Mar 2021 11:30:18 -0400 Subject: [PATCH] Align the block decoration with SPIR-V, require storage buffers --- src/back/spv/helpers.rs | 16 +++++++-- src/back/spv/instructions.rs | 6 ++++ src/back/spv/layout.rs | 20 ----------- src/back/spv/mod.rs | 3 ++ src/back/spv/writer.rs | 64 ++++++++++++----------------------- src/front/spv/mod.rs | 5 +-- src/lib.rs | 1 + src/valid/interface.rs | 38 ++++++++------------- src/valid/type.rs | 20 +++++++++-- tests/in/boids.wgsl | 1 - tests/in/shadow.wgsl | 1 - tests/out/boids.spvasm.snap | 15 ++++---- tests/out/collatz.spvasm.snap | 11 +++--- tests/out/quad.spvasm.snap | 4 +++ tests/out/shadow.ron.snap | 2 +- tests/out/shadow.spvasm.snap | 11 +++--- tests/out/skybox.spvasm.snap | 4 +++ 17 files changed, 107 insertions(+), 115 deletions(-) diff --git a/src/back/spv/helpers.rs b/src/back/spv/helpers.rs index 5facbe8b69..35b1389594 100644 --- a/src/back/spv/helpers.rs +++ b/src/back/spv/helpers.rs @@ -1,13 +1,13 @@ use spirv::Word; -pub(crate) fn bytes_to_words(bytes: &[u8]) -> Vec { +pub(super) fn bytes_to_words(bytes: &[u8]) -> Vec { 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 { +pub(super) fn string_to_words(input: &str) -> Vec { 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 { 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, + } +} diff --git a/src/back/spv/instructions.rs b/src/back/spv/instructions.rs index b1033c8023..18de62be04 100644 --- a/src/back/spv/instructions.rs +++ b/src/back/spv/instructions.rs @@ -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); diff --git a/src/back/spv/layout.rs b/src/back/spv/layout.rs index b2879089f9..5708157ba5 100644 --- a/src/back/spv/layout.rs +++ b/src/back/spv/layout.rs @@ -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 { diff --git a/src/back/spv/mod.rs b/src/back/spv/mod.rs index 4251b5cb41..e4fa140196 100644 --- a/src/back/spv/mod.rs +++ b/src/back/spv/mod.rs @@ -1,3 +1,6 @@ +/*! Standard Portable Intermediate Representation (SPIR-V) backend +!*/ + mod helpers; mod instructions; mod layout; diff --git a/src/back/spv/writer.rs b/src/back/spv/writer.rs index cabceae445..91fcada40a 100644 --- a/src/back/spv/writer.rs +++ b/src/back/spv/writer.rs @@ -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, Word>, global_variables: Vec, 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, crate::StorageAccess>, gl450_ext_inst_id: Word, temp_chain: Vec, } @@ -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)?; diff --git a/src/front/spv/mod.rs b/src/front/spv/mod.rs index dd7a791f1d..46e1453ff9 100644 --- a/src/front/spv/mod.rs +++ b/src/front/spv/mod.rs @@ -2406,7 +2406,6 @@ impl> Parser { 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> Parser { .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> Parser { } 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 { diff --git a/src/lib.rs b/src/lib.rs index 8d2590f3fd..ee1ca08696 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -393,6 +393,7 @@ pub enum TypeInner { }, /// User-defined structure. Struct { + /// This is a top-level host-shareable structure. block: bool, members: Vec, }, diff --git a/src/valid/interface.rs b/src/valid/interface.rs index a66ed65e15..576f4448c3 100644 --- a/src/valid/interface.rs +++ b/src/valid/interface.rs @@ -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, ) } diff --git a/src/valid/type.rs b/src/valid/type.rs index 508666722d..c3622f289e 100644 --- a/src/valid/type.rs +++ b/src/valid/type.rs @@ -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; diff --git a/tests/in/boids.wgsl b/tests/in/boids.wgsl index f41a6ffa0e..935d8ee08c 100644 --- a/tests/in/boids.wgsl +++ b/tests/in/boids.wgsl @@ -1,6 +1,5 @@ const NUM_PARTICLES: u32 = 1500u; -[[block]] struct Particle { pos : vec2; vel : vec2; diff --git a/tests/in/shadow.wgsl b/tests/in/shadow.wgsl index 8ccea67005..f40802cb43 100644 --- a/tests/in/shadow.wgsl +++ b/tests/in/shadow.wgsl @@ -6,7 +6,6 @@ struct Globals { [[group(0), binding(0)]] var u_globals: Globals; -[[block]] struct Light { proj: mat4x4; pos: vec4; diff --git a/tests/out/boids.spvasm.snap b/tests/out/boids.spvasm.snap index 0df16aceca..41e343a603 100644 --- a/tests/out/boids.spvasm.snap +++ b/tests/out/boids.spvasm.snap @@ -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 diff --git a/tests/out/collatz.spvasm.snap b/tests/out/collatz.spvasm.snap index 379b5a3f01..472cebff22 100644 --- a/tests/out/collatz.spvasm.snap +++ b/tests/out/collatz.spvasm.snap @@ -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 diff --git a/tests/out/quad.spvasm.snap b/tests/out/quad.spvasm.snap index 62a1dd3360..425fdedd51 100644 --- a/tests/out/quad.spvasm.snap +++ b/tests/out/quad.spvasm.snap @@ -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 diff --git a/tests/out/shadow.ron.snap b/tests/out/shadow.ron.snap index 8ba659a143..055887ff19 100644 --- a/tests/out/shadow.ron.snap +++ b/tests/out/shadow.ron.snap @@ -150,7 +150,7 @@ expression: output ( name: Some("Light"), inner: Struct( - block: true, + block: false, members: [ ( name: Some("proj"), diff --git a/tests/out/shadow.spvasm.snap b/tests/out/shadow.spvasm.snap index 23bee506c8..cf78faaed6 100644 --- a/tests/out/shadow.spvasm.snap +++ b/tests/out/shadow.spvasm.snap @@ -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 diff --git a/tests/out/skybox.spvasm.snap b/tests/out/skybox.spvasm.snap index 3f544eae7c..112aacac50 100644 --- a/tests/out/skybox.spvasm.snap +++ b/tests/out/skybox.spvasm.snap @@ -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