From c4756d2dfb3225ad92612b5438609849fcf920d2 Mon Sep 17 00:00:00 2001 From: wicast Date: Wed, 3 Apr 2024 10:17:30 +0800 Subject: [PATCH] fix(spv-out): OpSourceContinued for large source (#5390) --- naga/src/back/spv/helpers.rs | 229 +-- naga/src/back/spv/instructions.rs | 2236 +++++++++++++++-------------- naga/src/back/spv/writer.rs | 2 +- 3 files changed, 1257 insertions(+), 1210 deletions(-) diff --git a/naga/src/back/spv/helpers.rs b/naga/src/back/spv/helpers.rs index 5b6226db85..c653a44c7e 100644 --- a/naga/src/back/spv/helpers.rs +++ b/naga/src/back/spv/helpers.rs @@ -1,109 +1,120 @@ -use crate::{Handle, UniqueArena}; -use spirv::Word; - -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(super) fn string_to_words(input: &str) -> Vec { - let bytes = input.as_bytes(); - let mut words = bytes_to_words(bytes); - - if bytes.len() % 4 == 0 { - // nul-termination - words.push(0x0u32); - } - - words -} - -pub(super) const fn map_storage_class(space: crate::AddressSpace) -> spirv::StorageClass { - match space { - crate::AddressSpace::Handle => spirv::StorageClass::UniformConstant, - crate::AddressSpace::Function => spirv::StorageClass::Function, - crate::AddressSpace::Private => spirv::StorageClass::Private, - crate::AddressSpace::Storage { .. } => spirv::StorageClass::StorageBuffer, - crate::AddressSpace::Uniform => spirv::StorageClass::Uniform, - crate::AddressSpace::WorkGroup => spirv::StorageClass::Workgroup, - crate::AddressSpace::PushConstant => spirv::StorageClass::PushConstant, - } -} - -pub(super) fn contains_builtin( - binding: Option<&crate::Binding>, - ty: Handle, - arena: &UniqueArena, - built_in: crate::BuiltIn, -) -> bool { - if let Some(&crate::Binding::BuiltIn(bi)) = binding { - bi == built_in - } else if let crate::TypeInner::Struct { ref members, .. } = arena[ty].inner { - members - .iter() - .any(|member| contains_builtin(member.binding.as_ref(), member.ty, arena, built_in)) - } else { - false // unreachable - } -} - -impl crate::AddressSpace { - pub(super) const fn to_spirv_semantics_and_scope( - self, - ) -> (spirv::MemorySemantics, spirv::Scope) { - match self { - Self::Storage { .. } => (spirv::MemorySemantics::UNIFORM_MEMORY, spirv::Scope::Device), - Self::WorkGroup => ( - spirv::MemorySemantics::WORKGROUP_MEMORY, - spirv::Scope::Workgroup, - ), - _ => (spirv::MemorySemantics::empty(), spirv::Scope::Invocation), - } - } -} - -/// Return true if the global requires a type decorated with `Block`. -/// -/// Vulkan spec v1.3 §15.6.2, "Descriptor Set Interface", says: -/// -/// > Variables identified with the `Uniform` storage class are used to -/// > access transparent buffer backed resources. Such variables must -/// > be: -/// > -/// > - typed as `OpTypeStruct`, or an array of this type, -/// > -/// > - identified with a `Block` or `BufferBlock` decoration, and -/// > -/// > - laid out explicitly using the `Offset`, `ArrayStride`, and -/// > `MatrixStride` decorations as specified in §15.6.4, "Offset -/// > and Stride Assignment." -// See `back::spv::GlobalVariable::access_id` for details. -pub fn global_needs_wrapper(ir_module: &crate::Module, var: &crate::GlobalVariable) -> bool { - match var.space { - crate::AddressSpace::Uniform - | crate::AddressSpace::Storage { .. } - | crate::AddressSpace::PushConstant => {} - _ => return false, - }; - match ir_module.types[var.ty].inner { - crate::TypeInner::Struct { - ref members, - span: _, - } => match members.last() { - Some(member) => match ir_module.types[member.ty].inner { - // Structs with dynamically sized arrays can't be copied and can't be wrapped. - crate::TypeInner::Array { - size: crate::ArraySize::Dynamic, - .. - } => false, - _ => true, - }, - None => false, - }, - crate::TypeInner::BindingArray { .. } => false, - // if it's not a structure or a binding array, let's wrap it to be able to put "Block" - _ => true, - } -} +use crate::{Handle, UniqueArena}; +use spirv::Word; + +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(super) fn string_to_words(input: &str) -> Vec { + let bytes = input.as_bytes(); + + str_bytes_to_words(bytes) +} + +pub(super) fn str_bytes_to_words(bytes: &[u8]) -> Vec { + let mut words = bytes_to_words(bytes); + if bytes.len() % 4 == 0 { + // nul-termination + words.push(0x0u32); + } + + words +} + +pub(super) fn string_to_byte_chunks(input: &str, limit: usize) -> Vec<&[u8]> { + let bytes = input.as_bytes(); + let words = bytes.chunks(limit).collect(); + + words +} + +pub(super) const fn map_storage_class(space: crate::AddressSpace) -> spirv::StorageClass { + match space { + crate::AddressSpace::Handle => spirv::StorageClass::UniformConstant, + crate::AddressSpace::Function => spirv::StorageClass::Function, + crate::AddressSpace::Private => spirv::StorageClass::Private, + crate::AddressSpace::Storage { .. } => spirv::StorageClass::StorageBuffer, + crate::AddressSpace::Uniform => spirv::StorageClass::Uniform, + crate::AddressSpace::WorkGroup => spirv::StorageClass::Workgroup, + crate::AddressSpace::PushConstant => spirv::StorageClass::PushConstant, + } +} + +pub(super) fn contains_builtin( + binding: Option<&crate::Binding>, + ty: Handle, + arena: &UniqueArena, + built_in: crate::BuiltIn, +) -> bool { + if let Some(&crate::Binding::BuiltIn(bi)) = binding { + bi == built_in + } else if let crate::TypeInner::Struct { ref members, .. } = arena[ty].inner { + members + .iter() + .any(|member| contains_builtin(member.binding.as_ref(), member.ty, arena, built_in)) + } else { + false // unreachable + } +} + +impl crate::AddressSpace { + pub(super) const fn to_spirv_semantics_and_scope( + self, + ) -> (spirv::MemorySemantics, spirv::Scope) { + match self { + Self::Storage { .. } => (spirv::MemorySemantics::UNIFORM_MEMORY, spirv::Scope::Device), + Self::WorkGroup => ( + spirv::MemorySemantics::WORKGROUP_MEMORY, + spirv::Scope::Workgroup, + ), + _ => (spirv::MemorySemantics::empty(), spirv::Scope::Invocation), + } + } +} + +/// Return true if the global requires a type decorated with `Block`. +/// +/// Vulkan spec v1.3 §15.6.2, "Descriptor Set Interface", says: +/// +/// > Variables identified with the `Uniform` storage class are used to +/// > access transparent buffer backed resources. Such variables must +/// > be: +/// > +/// > - typed as `OpTypeStruct`, or an array of this type, +/// > +/// > - identified with a `Block` or `BufferBlock` decoration, and +/// > +/// > - laid out explicitly using the `Offset`, `ArrayStride`, and +/// > `MatrixStride` decorations as specified in §15.6.4, "Offset +/// > and Stride Assignment." +// See `back::spv::GlobalVariable::access_id` for details. +pub fn global_needs_wrapper(ir_module: &crate::Module, var: &crate::GlobalVariable) -> bool { + match var.space { + crate::AddressSpace::Uniform + | crate::AddressSpace::Storage { .. } + | crate::AddressSpace::PushConstant => {} + _ => return false, + }; + match ir_module.types[var.ty].inner { + crate::TypeInner::Struct { + ref members, + span: _, + } => match members.last() { + Some(member) => match ir_module.types[member.ty].inner { + // Structs with dynamically sized arrays can't be copied and can't be wrapped. + crate::TypeInner::Array { + size: crate::ArraySize::Dynamic, + .. + } => false, + _ => true, + }, + None => false, + }, + crate::TypeInner::BindingArray { .. } => false, + // if it's not a structure or a binding array, let's wrap it to be able to put "Block" + _ => true, + } +} diff --git a/naga/src/back/spv/instructions.rs b/naga/src/back/spv/instructions.rs index b963793ad3..c92f64961b 100644 --- a/naga/src/back/spv/instructions.rs +++ b/naga/src/back/spv/instructions.rs @@ -1,1100 +1,1136 @@ -use super::{block::DebugInfoInner, helpers}; -use spirv::{Op, Word}; - -pub(super) enum Signedness { - Unsigned = 0, - Signed = 1, -} - -pub(super) enum SampleLod { - Explicit, - Implicit, -} - -pub(super) struct Case { - pub value: Word, - pub label_id: Word, -} - -impl super::Instruction { - // - // Debug Instructions - // - - pub(super) fn string(name: &str, id: Word) -> Self { - let mut instruction = Self::new(Op::String); - instruction.set_result(id); - instruction.add_operands(helpers::string_to_words(name)); - instruction - } - - pub(super) fn source( - source_language: spirv::SourceLanguage, - version: u32, - source: &Option, - ) -> Self { - let mut instruction = Self::new(Op::Source); - instruction.add_operand(source_language as u32); - instruction.add_operands(helpers::bytes_to_words(&version.to_le_bytes())); - if let Some(source) = source.as_ref() { - instruction.add_operand(source.source_file_id); - instruction.add_operands(helpers::string_to_words(source.source_code)); - } - instruction - } - - pub(super) fn name(target_id: Word, name: &str) -> Self { - let mut instruction = Self::new(Op::Name); - instruction.add_operand(target_id); - instruction.add_operands(helpers::string_to_words(name)); - instruction - } - - pub(super) fn member_name(target_id: Word, member: Word, name: &str) -> Self { - let mut instruction = Self::new(Op::MemberName); - instruction.add_operand(target_id); - instruction.add_operand(member); - instruction.add_operands(helpers::string_to_words(name)); - instruction - } - - pub(super) fn line(file: Word, line: Word, column: Word) -> Self { - let mut instruction = Self::new(Op::Line); - instruction.add_operand(file); - instruction.add_operand(line); - instruction.add_operand(column); - instruction - } - - pub(super) const fn no_line() -> Self { - Self::new(Op::NoLine) - } - - // - // Annotation Instructions - // - - pub(super) fn decorate( - target_id: Word, - decoration: spirv::Decoration, - operands: &[Word], - ) -> Self { - let mut instruction = Self::new(Op::Decorate); - instruction.add_operand(target_id); - instruction.add_operand(decoration as u32); - for operand in operands { - instruction.add_operand(*operand) - } - instruction - } - - pub(super) fn member_decorate( - target_id: Word, - member_index: Word, - decoration: spirv::Decoration, - operands: &[Word], - ) -> Self { - let mut instruction = Self::new(Op::MemberDecorate); - instruction.add_operand(target_id); - instruction.add_operand(member_index); - instruction.add_operand(decoration as u32); - for operand in operands { - instruction.add_operand(*operand) - } - 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); - instruction.add_operands(helpers::string_to_words(name)); - instruction - } - - pub(super) fn ext_inst( - set_id: Word, - op: spirv::GLOp, - result_type_id: Word, - id: Word, - operands: &[Word], - ) -> Self { - let mut instruction = Self::new(Op::ExtInst); - instruction.set_type(result_type_id); - instruction.set_result(id); - instruction.add_operand(set_id); - instruction.add_operand(op as u32); - for operand in operands { - instruction.add_operand(*operand) - } - instruction - } - - // - // Mode-Setting Instructions - // - - pub(super) fn memory_model( - addressing_model: spirv::AddressingModel, - memory_model: spirv::MemoryModel, - ) -> Self { - let mut instruction = Self::new(Op::MemoryModel); - instruction.add_operand(addressing_model as u32); - instruction.add_operand(memory_model as u32); - instruction - } - - pub(super) fn entry_point( - execution_model: spirv::ExecutionModel, - entry_point_id: Word, - name: &str, - interface_ids: &[Word], - ) -> Self { - let mut instruction = Self::new(Op::EntryPoint); - instruction.add_operand(execution_model as u32); - instruction.add_operand(entry_point_id); - instruction.add_operands(helpers::string_to_words(name)); - - for interface_id in interface_ids { - instruction.add_operand(*interface_id); - } - - instruction - } - - pub(super) fn execution_mode( - entry_point_id: Word, - execution_mode: spirv::ExecutionMode, - args: &[Word], - ) -> Self { - let mut instruction = Self::new(Op::ExecutionMode); - instruction.add_operand(entry_point_id); - instruction.add_operand(execution_mode as u32); - for arg in args { - instruction.add_operand(*arg); - } - instruction - } - - pub(super) fn capability(capability: spirv::Capability) -> Self { - let mut instruction = Self::new(Op::Capability); - instruction.add_operand(capability as u32); - instruction - } - - // - // Type-Declaration Instructions - // - - pub(super) fn type_void(id: Word) -> Self { - let mut instruction = Self::new(Op::TypeVoid); - instruction.set_result(id); - instruction - } - - pub(super) fn type_bool(id: Word) -> Self { - let mut instruction = Self::new(Op::TypeBool); - instruction.set_result(id); - instruction - } - - pub(super) fn type_int(id: Word, width: Word, signedness: Signedness) -> Self { - let mut instruction = Self::new(Op::TypeInt); - instruction.set_result(id); - instruction.add_operand(width); - instruction.add_operand(signedness as u32); - instruction - } - - pub(super) fn type_float(id: Word, width: Word) -> Self { - let mut instruction = Self::new(Op::TypeFloat); - instruction.set_result(id); - instruction.add_operand(width); - instruction - } - - pub(super) fn type_vector( - id: Word, - component_type_id: Word, - component_count: crate::VectorSize, - ) -> Self { - let mut instruction = Self::new(Op::TypeVector); - instruction.set_result(id); - instruction.add_operand(component_type_id); - instruction.add_operand(component_count as u32); - instruction - } - - pub(super) fn type_matrix( - id: Word, - column_type_id: Word, - column_count: crate::VectorSize, - ) -> Self { - let mut instruction = Self::new(Op::TypeMatrix); - instruction.set_result(id); - instruction.add_operand(column_type_id); - instruction.add_operand(column_count as u32); - instruction - } - - #[allow(clippy::too_many_arguments)] - pub(super) fn type_image( - id: Word, - sampled_type_id: Word, - dim: spirv::Dim, - flags: super::ImageTypeFlags, - image_format: spirv::ImageFormat, - ) -> Self { - let mut instruction = Self::new(Op::TypeImage); - instruction.set_result(id); - instruction.add_operand(sampled_type_id); - instruction.add_operand(dim as u32); - instruction.add_operand(flags.contains(super::ImageTypeFlags::DEPTH) as u32); - instruction.add_operand(flags.contains(super::ImageTypeFlags::ARRAYED) as u32); - instruction.add_operand(flags.contains(super::ImageTypeFlags::MULTISAMPLED) as u32); - instruction.add_operand(if flags.contains(super::ImageTypeFlags::SAMPLED) { - 1 - } else { - 2 - }); - instruction.add_operand(image_format as u32); - instruction - } - - pub(super) fn type_sampler(id: Word) -> Self { - let mut instruction = Self::new(Op::TypeSampler); - instruction.set_result(id); - instruction - } - - pub(super) fn type_acceleration_structure(id: Word) -> Self { - let mut instruction = Self::new(Op::TypeAccelerationStructureKHR); - instruction.set_result(id); - instruction - } - - pub(super) fn type_ray_query(id: Word) -> Self { - let mut instruction = Self::new(Op::TypeRayQueryKHR); - instruction.set_result(id); - instruction - } - - pub(super) fn type_sampled_image(id: Word, image_type_id: Word) -> Self { - let mut instruction = Self::new(Op::TypeSampledImage); - instruction.set_result(id); - instruction.add_operand(image_type_id); - instruction - } - - pub(super) fn type_array(id: Word, element_type_id: Word, length_id: Word) -> Self { - let mut instruction = Self::new(Op::TypeArray); - instruction.set_result(id); - instruction.add_operand(element_type_id); - instruction.add_operand(length_id); - instruction - } - - pub(super) fn type_runtime_array(id: Word, element_type_id: Word) -> Self { - let mut instruction = Self::new(Op::TypeRuntimeArray); - instruction.set_result(id); - instruction.add_operand(element_type_id); - instruction - } - - pub(super) fn type_struct(id: Word, member_ids: &[Word]) -> Self { - let mut instruction = Self::new(Op::TypeStruct); - instruction.set_result(id); - - for member_id in member_ids { - instruction.add_operand(*member_id) - } - - instruction - } - - pub(super) fn type_pointer( - id: Word, - storage_class: spirv::StorageClass, - type_id: Word, - ) -> Self { - let mut instruction = Self::new(Op::TypePointer); - instruction.set_result(id); - instruction.add_operand(storage_class as u32); - instruction.add_operand(type_id); - instruction - } - - pub(super) fn type_function(id: Word, return_type_id: Word, parameter_ids: &[Word]) -> Self { - let mut instruction = Self::new(Op::TypeFunction); - instruction.set_result(id); - instruction.add_operand(return_type_id); - - for parameter_id in parameter_ids { - instruction.add_operand(*parameter_id); - } - - instruction - } - - // - // Constant-Creation Instructions - // - - pub(super) fn constant_null(result_type_id: Word, id: Word) -> Self { - let mut instruction = Self::new(Op::ConstantNull); - instruction.set_type(result_type_id); - instruction.set_result(id); - instruction - } - - pub(super) fn constant_true(result_type_id: Word, id: Word) -> Self { - let mut instruction = Self::new(Op::ConstantTrue); - instruction.set_type(result_type_id); - instruction.set_result(id); - instruction - } - - pub(super) fn constant_false(result_type_id: Word, id: Word) -> Self { - let mut instruction = Self::new(Op::ConstantFalse); - instruction.set_type(result_type_id); - instruction.set_result(id); - instruction - } - - pub(super) fn constant_32bit(result_type_id: Word, id: Word, value: Word) -> Self { - Self::constant(result_type_id, id, &[value]) - } - - pub(super) fn constant_64bit(result_type_id: Word, id: Word, low: Word, high: Word) -> Self { - Self::constant(result_type_id, id, &[low, high]) - } - - pub(super) fn constant(result_type_id: Word, id: Word, values: &[Word]) -> Self { - let mut instruction = Self::new(Op::Constant); - instruction.set_type(result_type_id); - instruction.set_result(id); - - for value in values { - instruction.add_operand(*value); - } - - instruction - } - - pub(super) fn constant_composite( - result_type_id: Word, - id: Word, - constituent_ids: &[Word], - ) -> Self { - let mut instruction = Self::new(Op::ConstantComposite); - instruction.set_type(result_type_id); - instruction.set_result(id); - - for constituent_id in constituent_ids { - instruction.add_operand(*constituent_id); - } - - instruction - } - - // - // Memory Instructions - // - - pub(super) fn variable( - result_type_id: Word, - id: Word, - storage_class: spirv::StorageClass, - initializer_id: Option, - ) -> Self { - let mut instruction = Self::new(Op::Variable); - instruction.set_type(result_type_id); - instruction.set_result(id); - instruction.add_operand(storage_class as u32); - - if let Some(initializer_id) = initializer_id { - instruction.add_operand(initializer_id); - } - - instruction - } - - pub(super) fn load( - result_type_id: Word, - id: Word, - pointer_id: Word, - memory_access: Option, - ) -> Self { - let mut instruction = Self::new(Op::Load); - instruction.set_type(result_type_id); - instruction.set_result(id); - instruction.add_operand(pointer_id); - - if let Some(memory_access) = memory_access { - instruction.add_operand(memory_access.bits()); - } - - instruction - } - - pub(super) fn atomic_load( - result_type_id: Word, - id: Word, - pointer_id: Word, - scope_id: Word, - semantics_id: Word, - ) -> Self { - let mut instruction = Self::new(Op::AtomicLoad); - instruction.set_type(result_type_id); - instruction.set_result(id); - instruction.add_operand(pointer_id); - instruction.add_operand(scope_id); - instruction.add_operand(semantics_id); - instruction - } - - pub(super) fn store( - pointer_id: Word, - value_id: Word, - memory_access: Option, - ) -> Self { - let mut instruction = Self::new(Op::Store); - instruction.add_operand(pointer_id); - instruction.add_operand(value_id); - - if let Some(memory_access) = memory_access { - instruction.add_operand(memory_access.bits()); - } - - instruction - } - - pub(super) fn atomic_store( - pointer_id: Word, - scope_id: Word, - semantics_id: Word, - value_id: Word, - ) -> Self { - let mut instruction = Self::new(Op::AtomicStore); - instruction.add_operand(pointer_id); - instruction.add_operand(scope_id); - instruction.add_operand(semantics_id); - instruction.add_operand(value_id); - instruction - } - - pub(super) fn access_chain( - result_type_id: Word, - id: Word, - base_id: Word, - index_ids: &[Word], - ) -> Self { - let mut instruction = Self::new(Op::AccessChain); - instruction.set_type(result_type_id); - instruction.set_result(id); - instruction.add_operand(base_id); - - for index_id in index_ids { - instruction.add_operand(*index_id); - } - - instruction - } - - pub(super) fn array_length( - result_type_id: Word, - id: Word, - structure_id: Word, - array_member: Word, - ) -> Self { - let mut instruction = Self::new(Op::ArrayLength); - instruction.set_type(result_type_id); - instruction.set_result(id); - instruction.add_operand(structure_id); - instruction.add_operand(array_member); - instruction - } - - // - // Function Instructions - // - - pub(super) fn function( - return_type_id: Word, - id: Word, - function_control: spirv::FunctionControl, - function_type_id: Word, - ) -> Self { - let mut instruction = Self::new(Op::Function); - instruction.set_type(return_type_id); - instruction.set_result(id); - instruction.add_operand(function_control.bits()); - instruction.add_operand(function_type_id); - instruction - } - - pub(super) fn function_parameter(result_type_id: Word, id: Word) -> Self { - let mut instruction = Self::new(Op::FunctionParameter); - instruction.set_type(result_type_id); - instruction.set_result(id); - instruction - } - - pub(super) const fn function_end() -> Self { - Self::new(Op::FunctionEnd) - } - - pub(super) fn function_call( - result_type_id: Word, - id: Word, - function_id: Word, - argument_ids: &[Word], - ) -> Self { - let mut instruction = Self::new(Op::FunctionCall); - instruction.set_type(result_type_id); - instruction.set_result(id); - instruction.add_operand(function_id); - - for argument_id in argument_ids { - instruction.add_operand(*argument_id); - } - - instruction - } - - // - // Image Instructions - // - - pub(super) fn sampled_image( - result_type_id: Word, - id: Word, - image: Word, - sampler: Word, - ) -> Self { - let mut instruction = Self::new(Op::SampledImage); - instruction.set_type(result_type_id); - instruction.set_result(id); - instruction.add_operand(image); - instruction.add_operand(sampler); - instruction - } - - pub(super) fn image_sample( - result_type_id: Word, - id: Word, - lod: SampleLod, - sampled_image: Word, - coordinates: Word, - depth_ref: Option, - ) -> Self { - let op = match (lod, depth_ref) { - (SampleLod::Explicit, None) => Op::ImageSampleExplicitLod, - (SampleLod::Implicit, None) => Op::ImageSampleImplicitLod, - (SampleLod::Explicit, Some(_)) => Op::ImageSampleDrefExplicitLod, - (SampleLod::Implicit, Some(_)) => Op::ImageSampleDrefImplicitLod, - }; - - let mut instruction = Self::new(op); - instruction.set_type(result_type_id); - instruction.set_result(id); - instruction.add_operand(sampled_image); - instruction.add_operand(coordinates); - if let Some(dref) = depth_ref { - instruction.add_operand(dref); - } - - instruction - } - - pub(super) fn image_gather( - result_type_id: Word, - id: Word, - sampled_image: Word, - coordinates: Word, - component_id: Word, - depth_ref: Option, - ) -> Self { - let op = match depth_ref { - None => Op::ImageGather, - Some(_) => Op::ImageDrefGather, - }; - - let mut instruction = Self::new(op); - instruction.set_type(result_type_id); - instruction.set_result(id); - instruction.add_operand(sampled_image); - instruction.add_operand(coordinates); - if let Some(dref) = depth_ref { - instruction.add_operand(dref); - } else { - instruction.add_operand(component_id); - } - - instruction - } - - pub(super) fn image_fetch_or_read( - op: Op, - result_type_id: Word, - id: Word, - image: Word, - coordinates: Word, - ) -> Self { - let mut instruction = Self::new(op); - instruction.set_type(result_type_id); - instruction.set_result(id); - instruction.add_operand(image); - instruction.add_operand(coordinates); - instruction - } - - pub(super) fn image_write(image: Word, coordinates: Word, value: Word) -> Self { - let mut instruction = Self::new(Op::ImageWrite); - instruction.add_operand(image); - instruction.add_operand(coordinates); - instruction.add_operand(value); - instruction - } - - pub(super) fn image_query(op: Op, result_type_id: Word, id: Word, image: Word) -> Self { - let mut instruction = Self::new(op); - instruction.set_type(result_type_id); - instruction.set_result(id); - instruction.add_operand(image); - instruction - } - - // - // Ray Query Instructions - // - #[allow(clippy::too_many_arguments)] - pub(super) fn ray_query_initialize( - query: Word, - acceleration_structure: Word, - ray_flags: Word, - cull_mask: Word, - ray_origin: Word, - ray_tmin: Word, - ray_dir: Word, - ray_tmax: Word, - ) -> Self { - let mut instruction = Self::new(Op::RayQueryInitializeKHR); - instruction.add_operand(query); - instruction.add_operand(acceleration_structure); - instruction.add_operand(ray_flags); - instruction.add_operand(cull_mask); - instruction.add_operand(ray_origin); - instruction.add_operand(ray_tmin); - instruction.add_operand(ray_dir); - instruction.add_operand(ray_tmax); - instruction - } - - pub(super) fn ray_query_proceed(result_type_id: Word, id: Word, query: Word) -> Self { - let mut instruction = Self::new(Op::RayQueryProceedKHR); - instruction.set_type(result_type_id); - instruction.set_result(id); - instruction.add_operand(query); - instruction - } - - pub(super) fn ray_query_get_intersection( - op: Op, - result_type_id: Word, - id: Word, - query: Word, - intersection: Word, - ) -> Self { - let mut instruction = Self::new(op); - instruction.set_type(result_type_id); - instruction.set_result(id); - instruction.add_operand(query); - instruction.add_operand(intersection); - instruction - } - - // - // Conversion Instructions - // - pub(super) fn unary(op: Op, result_type_id: Word, id: Word, value: Word) -> Self { - let mut instruction = Self::new(op); - instruction.set_type(result_type_id); - instruction.set_result(id); - instruction.add_operand(value); - instruction - } - - // - // Composite Instructions - // - - pub(super) fn composite_construct( - result_type_id: Word, - id: Word, - constituent_ids: &[Word], - ) -> Self { - let mut instruction = Self::new(Op::CompositeConstruct); - instruction.set_type(result_type_id); - instruction.set_result(id); - - for constituent_id in constituent_ids { - instruction.add_operand(*constituent_id); - } - - instruction - } - - pub(super) fn composite_extract( - result_type_id: Word, - id: Word, - composite_id: Word, - indices: &[Word], - ) -> Self { - let mut instruction = Self::new(Op::CompositeExtract); - instruction.set_type(result_type_id); - instruction.set_result(id); - - instruction.add_operand(composite_id); - for index in indices { - instruction.add_operand(*index); - } - - instruction - } - - pub(super) fn vector_extract_dynamic( - result_type_id: Word, - id: Word, - vector_id: Word, - index_id: Word, - ) -> Self { - let mut instruction = Self::new(Op::VectorExtractDynamic); - instruction.set_type(result_type_id); - instruction.set_result(id); - - instruction.add_operand(vector_id); - instruction.add_operand(index_id); - - instruction - } - - pub(super) fn vector_shuffle( - result_type_id: Word, - id: Word, - v1_id: Word, - v2_id: Word, - components: &[Word], - ) -> Self { - let mut instruction = Self::new(Op::VectorShuffle); - instruction.set_type(result_type_id); - instruction.set_result(id); - instruction.add_operand(v1_id); - instruction.add_operand(v2_id); - - for &component in components { - instruction.add_operand(component); - } - - instruction - } - - // - // Arithmetic Instructions - // - pub(super) fn binary( - op: Op, - result_type_id: Word, - id: Word, - operand_1: Word, - operand_2: Word, - ) -> Self { - let mut instruction = Self::new(op); - instruction.set_type(result_type_id); - instruction.set_result(id); - instruction.add_operand(operand_1); - instruction.add_operand(operand_2); - instruction - } - - pub(super) fn ternary( - op: Op, - result_type_id: Word, - id: Word, - operand_1: Word, - operand_2: Word, - operand_3: Word, - ) -> Self { - let mut instruction = Self::new(op); - instruction.set_type(result_type_id); - instruction.set_result(id); - instruction.add_operand(operand_1); - instruction.add_operand(operand_2); - instruction.add_operand(operand_3); - instruction - } - - pub(super) fn quaternary( - op: Op, - result_type_id: Word, - id: Word, - operand_1: Word, - operand_2: Word, - operand_3: Word, - operand_4: Word, - ) -> Self { - let mut instruction = Self::new(op); - instruction.set_type(result_type_id); - instruction.set_result(id); - instruction.add_operand(operand_1); - instruction.add_operand(operand_2); - instruction.add_operand(operand_3); - instruction.add_operand(operand_4); - instruction - } - - pub(super) fn relational(op: Op, result_type_id: Word, id: Word, expr_id: Word) -> Self { - let mut instruction = Self::new(op); - instruction.set_type(result_type_id); - instruction.set_result(id); - instruction.add_operand(expr_id); - instruction - } - - pub(super) fn atomic_binary( - op: Op, - result_type_id: Word, - id: Word, - pointer: Word, - scope_id: Word, - semantics_id: Word, - value: Word, - ) -> Self { - let mut instruction = Self::new(op); - instruction.set_type(result_type_id); - instruction.set_result(id); - instruction.add_operand(pointer); - instruction.add_operand(scope_id); - instruction.add_operand(semantics_id); - instruction.add_operand(value); - instruction - } - - // - // Bit Instructions - // - - // - // Relational and Logical Instructions - // - - // - // Derivative Instructions - // - - pub(super) fn derivative(op: Op, result_type_id: Word, id: Word, expr_id: Word) -> Self { - let mut instruction = Self::new(op); - instruction.set_type(result_type_id); - instruction.set_result(id); - instruction.add_operand(expr_id); - instruction - } - - // - // Control-Flow Instructions - // - - pub(super) fn phi( - result_type_id: Word, - result_id: Word, - var_parent_pairs: &[(Word, Word)], - ) -> Self { - let mut instruction = Self::new(Op::Phi); - instruction.add_operand(result_type_id); - instruction.add_operand(result_id); - for &(variable, parent) in var_parent_pairs { - instruction.add_operand(variable); - instruction.add_operand(parent); - } - instruction - } - - pub(super) fn selection_merge( - merge_id: Word, - selection_control: spirv::SelectionControl, - ) -> Self { - let mut instruction = Self::new(Op::SelectionMerge); - instruction.add_operand(merge_id); - instruction.add_operand(selection_control.bits()); - instruction - } - - pub(super) fn loop_merge( - merge_id: Word, - continuing_id: Word, - selection_control: spirv::SelectionControl, - ) -> Self { - let mut instruction = Self::new(Op::LoopMerge); - instruction.add_operand(merge_id); - instruction.add_operand(continuing_id); - instruction.add_operand(selection_control.bits()); - instruction - } - - pub(super) fn label(id: Word) -> Self { - let mut instruction = Self::new(Op::Label); - instruction.set_result(id); - instruction - } - - pub(super) fn branch(id: Word) -> Self { - let mut instruction = Self::new(Op::Branch); - instruction.add_operand(id); - instruction - } - - // TODO Branch Weights not implemented. - pub(super) fn branch_conditional( - condition_id: Word, - true_label: Word, - false_label: Word, - ) -> Self { - let mut instruction = Self::new(Op::BranchConditional); - instruction.add_operand(condition_id); - instruction.add_operand(true_label); - instruction.add_operand(false_label); - instruction - } - - pub(super) fn switch(selector_id: Word, default_id: Word, cases: &[Case]) -> Self { - let mut instruction = Self::new(Op::Switch); - instruction.add_operand(selector_id); - instruction.add_operand(default_id); - for case in cases { - instruction.add_operand(case.value); - instruction.add_operand(case.label_id); - } - instruction - } - - pub(super) fn select( - result_type_id: Word, - id: Word, - condition_id: Word, - accept_id: Word, - reject_id: Word, - ) -> Self { - let mut instruction = Self::new(Op::Select); - instruction.add_operand(result_type_id); - instruction.add_operand(id); - instruction.add_operand(condition_id); - instruction.add_operand(accept_id); - instruction.add_operand(reject_id); - instruction - } - - pub(super) const fn kill() -> Self { - Self::new(Op::Kill) - } - - pub(super) const fn return_void() -> Self { - Self::new(Op::Return) - } - - pub(super) fn return_value(value_id: Word) -> Self { - let mut instruction = Self::new(Op::ReturnValue); - instruction.add_operand(value_id); - instruction - } - - // - // Atomic Instructions - // - - // - // Primitive Instructions - // - - // Barriers - - pub(super) fn control_barrier( - exec_scope_id: Word, - mem_scope_id: Word, - semantics_id: Word, - ) -> Self { - let mut instruction = Self::new(Op::ControlBarrier); - instruction.add_operand(exec_scope_id); - instruction.add_operand(mem_scope_id); - instruction.add_operand(semantics_id); - instruction - } -} - -impl From for spirv::ImageFormat { - fn from(format: crate::StorageFormat) -> Self { - use crate::StorageFormat as Sf; - match format { - Sf::R8Unorm => Self::R8, - Sf::R8Snorm => Self::R8Snorm, - Sf::R8Uint => Self::R8ui, - Sf::R8Sint => Self::R8i, - Sf::R16Uint => Self::R16ui, - Sf::R16Sint => Self::R16i, - Sf::R16Float => Self::R16f, - Sf::Rg8Unorm => Self::Rg8, - Sf::Rg8Snorm => Self::Rg8Snorm, - Sf::Rg8Uint => Self::Rg8ui, - Sf::Rg8Sint => Self::Rg8i, - Sf::R32Uint => Self::R32ui, - Sf::R32Sint => Self::R32i, - Sf::R32Float => Self::R32f, - Sf::Rg16Uint => Self::Rg16ui, - Sf::Rg16Sint => Self::Rg16i, - Sf::Rg16Float => Self::Rg16f, - Sf::Rgba8Unorm => Self::Rgba8, - Sf::Rgba8Snorm => Self::Rgba8Snorm, - Sf::Rgba8Uint => Self::Rgba8ui, - Sf::Rgba8Sint => Self::Rgba8i, - Sf::Bgra8Unorm => Self::Unknown, - Sf::Rgb10a2Uint => Self::Rgb10a2ui, - Sf::Rgb10a2Unorm => Self::Rgb10A2, - Sf::Rg11b10Float => Self::R11fG11fB10f, - Sf::Rg32Uint => Self::Rg32ui, - Sf::Rg32Sint => Self::Rg32i, - Sf::Rg32Float => Self::Rg32f, - Sf::Rgba16Uint => Self::Rgba16ui, - Sf::Rgba16Sint => Self::Rgba16i, - Sf::Rgba16Float => Self::Rgba16f, - Sf::Rgba32Uint => Self::Rgba32ui, - Sf::Rgba32Sint => Self::Rgba32i, - Sf::Rgba32Float => Self::Rgba32f, - Sf::R16Unorm => Self::R16, - Sf::R16Snorm => Self::R16Snorm, - Sf::Rg16Unorm => Self::Rg16, - Sf::Rg16Snorm => Self::Rg16Snorm, - Sf::Rgba16Unorm => Self::Rgba16, - Sf::Rgba16Snorm => Self::Rgba16Snorm, - } - } -} - -impl From for spirv::Dim { - fn from(dim: crate::ImageDimension) -> Self { - use crate::ImageDimension as Id; - match dim { - Id::D1 => Self::Dim1D, - Id::D2 => Self::Dim2D, - Id::D3 => Self::Dim3D, - Id::Cube => Self::DimCube, - } - } -} +use super::{block::DebugInfoInner, helpers}; +use spirv::{Op, Word}; + +pub(super) enum Signedness { + Unsigned = 0, + Signed = 1, +} + +pub(super) enum SampleLod { + Explicit, + Implicit, +} + +pub(super) struct Case { + pub value: Word, + pub label_id: Word, +} + +impl super::Instruction { + // + // Debug Instructions + // + + pub(super) fn string(name: &str, id: Word) -> Self { + let mut instruction = Self::new(Op::String); + instruction.set_result(id); + instruction.add_operands(helpers::string_to_words(name)); + instruction + } + + pub(super) fn source( + source_language: spirv::SourceLanguage, + version: u32, + source: &Option, + ) -> Self { + let mut instruction = Self::new(Op::Source); + instruction.add_operand(source_language as u32); + instruction.add_operands(helpers::bytes_to_words(&version.to_le_bytes())); + if let Some(source) = source.as_ref() { + instruction.add_operand(source.source_file_id); + instruction.add_operands(helpers::string_to_words(source.source_code)); + } + instruction + } + + pub(super) fn source_continued(source: &[u8]) -> Self { + let mut instruction = Self::new(Op::SourceContinued); + instruction.add_operands(helpers::str_bytes_to_words(source)); + instruction + } + + pub(super) fn source_auto_continued( + source_language: spirv::SourceLanguage, + version: u32, + source: &Option, + ) -> Vec { + let mut instructions = vec![]; + + let with_continue = source.as_ref().and_then(|debug_info| { + (debug_info.source_code.len() > u16::MAX as usize).then_some(debug_info) + }); + if let Some(debug_info) = with_continue { + let mut instruction = Self::new(Op::Source); + instruction.add_operand(source_language as u32); + instruction.add_operands(helpers::bytes_to_words(&version.to_le_bytes())); + + let words = helpers::string_to_byte_chunks(debug_info.source_code, u16::MAX as usize); + instruction.add_operand(debug_info.source_file_id); + instruction.add_operands(helpers::str_bytes_to_words(words[0])); + instructions.push(instruction); + for word_bytes in words[1..].iter() { + let instruction_continue = Self::source_continued(word_bytes); + instructions.push(instruction_continue); + } + } else { + let instruction = Self::source(source_language, version, source); + instructions.push(instruction); + } + instructions + } + + pub(super) fn name(target_id: Word, name: &str) -> Self { + let mut instruction = Self::new(Op::Name); + instruction.add_operand(target_id); + instruction.add_operands(helpers::string_to_words(name)); + instruction + } + + pub(super) fn member_name(target_id: Word, member: Word, name: &str) -> Self { + let mut instruction = Self::new(Op::MemberName); + instruction.add_operand(target_id); + instruction.add_operand(member); + instruction.add_operands(helpers::string_to_words(name)); + instruction + } + + pub(super) fn line(file: Word, line: Word, column: Word) -> Self { + let mut instruction = Self::new(Op::Line); + instruction.add_operand(file); + instruction.add_operand(line); + instruction.add_operand(column); + instruction + } + + pub(super) const fn no_line() -> Self { + Self::new(Op::NoLine) + } + + // + // Annotation Instructions + // + + pub(super) fn decorate( + target_id: Word, + decoration: spirv::Decoration, + operands: &[Word], + ) -> Self { + let mut instruction = Self::new(Op::Decorate); + instruction.add_operand(target_id); + instruction.add_operand(decoration as u32); + for operand in operands { + instruction.add_operand(*operand) + } + instruction + } + + pub(super) fn member_decorate( + target_id: Word, + member_index: Word, + decoration: spirv::Decoration, + operands: &[Word], + ) -> Self { + let mut instruction = Self::new(Op::MemberDecorate); + instruction.add_operand(target_id); + instruction.add_operand(member_index); + instruction.add_operand(decoration as u32); + for operand in operands { + instruction.add_operand(*operand) + } + 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); + instruction.add_operands(helpers::string_to_words(name)); + instruction + } + + pub(super) fn ext_inst( + set_id: Word, + op: spirv::GLOp, + result_type_id: Word, + id: Word, + operands: &[Word], + ) -> Self { + let mut instruction = Self::new(Op::ExtInst); + instruction.set_type(result_type_id); + instruction.set_result(id); + instruction.add_operand(set_id); + instruction.add_operand(op as u32); + for operand in operands { + instruction.add_operand(*operand) + } + instruction + } + + // + // Mode-Setting Instructions + // + + pub(super) fn memory_model( + addressing_model: spirv::AddressingModel, + memory_model: spirv::MemoryModel, + ) -> Self { + let mut instruction = Self::new(Op::MemoryModel); + instruction.add_operand(addressing_model as u32); + instruction.add_operand(memory_model as u32); + instruction + } + + pub(super) fn entry_point( + execution_model: spirv::ExecutionModel, + entry_point_id: Word, + name: &str, + interface_ids: &[Word], + ) -> Self { + let mut instruction = Self::new(Op::EntryPoint); + instruction.add_operand(execution_model as u32); + instruction.add_operand(entry_point_id); + instruction.add_operands(helpers::string_to_words(name)); + + for interface_id in interface_ids { + instruction.add_operand(*interface_id); + } + + instruction + } + + pub(super) fn execution_mode( + entry_point_id: Word, + execution_mode: spirv::ExecutionMode, + args: &[Word], + ) -> Self { + let mut instruction = Self::new(Op::ExecutionMode); + instruction.add_operand(entry_point_id); + instruction.add_operand(execution_mode as u32); + for arg in args { + instruction.add_operand(*arg); + } + instruction + } + + pub(super) fn capability(capability: spirv::Capability) -> Self { + let mut instruction = Self::new(Op::Capability); + instruction.add_operand(capability as u32); + instruction + } + + // + // Type-Declaration Instructions + // + + pub(super) fn type_void(id: Word) -> Self { + let mut instruction = Self::new(Op::TypeVoid); + instruction.set_result(id); + instruction + } + + pub(super) fn type_bool(id: Word) -> Self { + let mut instruction = Self::new(Op::TypeBool); + instruction.set_result(id); + instruction + } + + pub(super) fn type_int(id: Word, width: Word, signedness: Signedness) -> Self { + let mut instruction = Self::new(Op::TypeInt); + instruction.set_result(id); + instruction.add_operand(width); + instruction.add_operand(signedness as u32); + instruction + } + + pub(super) fn type_float(id: Word, width: Word) -> Self { + let mut instruction = Self::new(Op::TypeFloat); + instruction.set_result(id); + instruction.add_operand(width); + instruction + } + + pub(super) fn type_vector( + id: Word, + component_type_id: Word, + component_count: crate::VectorSize, + ) -> Self { + let mut instruction = Self::new(Op::TypeVector); + instruction.set_result(id); + instruction.add_operand(component_type_id); + instruction.add_operand(component_count as u32); + instruction + } + + pub(super) fn type_matrix( + id: Word, + column_type_id: Word, + column_count: crate::VectorSize, + ) -> Self { + let mut instruction = Self::new(Op::TypeMatrix); + instruction.set_result(id); + instruction.add_operand(column_type_id); + instruction.add_operand(column_count as u32); + instruction + } + + #[allow(clippy::too_many_arguments)] + pub(super) fn type_image( + id: Word, + sampled_type_id: Word, + dim: spirv::Dim, + flags: super::ImageTypeFlags, + image_format: spirv::ImageFormat, + ) -> Self { + let mut instruction = Self::new(Op::TypeImage); + instruction.set_result(id); + instruction.add_operand(sampled_type_id); + instruction.add_operand(dim as u32); + instruction.add_operand(flags.contains(super::ImageTypeFlags::DEPTH) as u32); + instruction.add_operand(flags.contains(super::ImageTypeFlags::ARRAYED) as u32); + instruction.add_operand(flags.contains(super::ImageTypeFlags::MULTISAMPLED) as u32); + instruction.add_operand(if flags.contains(super::ImageTypeFlags::SAMPLED) { + 1 + } else { + 2 + }); + instruction.add_operand(image_format as u32); + instruction + } + + pub(super) fn type_sampler(id: Word) -> Self { + let mut instruction = Self::new(Op::TypeSampler); + instruction.set_result(id); + instruction + } + + pub(super) fn type_acceleration_structure(id: Word) -> Self { + let mut instruction = Self::new(Op::TypeAccelerationStructureKHR); + instruction.set_result(id); + instruction + } + + pub(super) fn type_ray_query(id: Word) -> Self { + let mut instruction = Self::new(Op::TypeRayQueryKHR); + instruction.set_result(id); + instruction + } + + pub(super) fn type_sampled_image(id: Word, image_type_id: Word) -> Self { + let mut instruction = Self::new(Op::TypeSampledImage); + instruction.set_result(id); + instruction.add_operand(image_type_id); + instruction + } + + pub(super) fn type_array(id: Word, element_type_id: Word, length_id: Word) -> Self { + let mut instruction = Self::new(Op::TypeArray); + instruction.set_result(id); + instruction.add_operand(element_type_id); + instruction.add_operand(length_id); + instruction + } + + pub(super) fn type_runtime_array(id: Word, element_type_id: Word) -> Self { + let mut instruction = Self::new(Op::TypeRuntimeArray); + instruction.set_result(id); + instruction.add_operand(element_type_id); + instruction + } + + pub(super) fn type_struct(id: Word, member_ids: &[Word]) -> Self { + let mut instruction = Self::new(Op::TypeStruct); + instruction.set_result(id); + + for member_id in member_ids { + instruction.add_operand(*member_id) + } + + instruction + } + + pub(super) fn type_pointer( + id: Word, + storage_class: spirv::StorageClass, + type_id: Word, + ) -> Self { + let mut instruction = Self::new(Op::TypePointer); + instruction.set_result(id); + instruction.add_operand(storage_class as u32); + instruction.add_operand(type_id); + instruction + } + + pub(super) fn type_function(id: Word, return_type_id: Word, parameter_ids: &[Word]) -> Self { + let mut instruction = Self::new(Op::TypeFunction); + instruction.set_result(id); + instruction.add_operand(return_type_id); + + for parameter_id in parameter_ids { + instruction.add_operand(*parameter_id); + } + + instruction + } + + // + // Constant-Creation Instructions + // + + pub(super) fn constant_null(result_type_id: Word, id: Word) -> Self { + let mut instruction = Self::new(Op::ConstantNull); + instruction.set_type(result_type_id); + instruction.set_result(id); + instruction + } + + pub(super) fn constant_true(result_type_id: Word, id: Word) -> Self { + let mut instruction = Self::new(Op::ConstantTrue); + instruction.set_type(result_type_id); + instruction.set_result(id); + instruction + } + + pub(super) fn constant_false(result_type_id: Word, id: Word) -> Self { + let mut instruction = Self::new(Op::ConstantFalse); + instruction.set_type(result_type_id); + instruction.set_result(id); + instruction + } + + pub(super) fn constant_32bit(result_type_id: Word, id: Word, value: Word) -> Self { + Self::constant(result_type_id, id, &[value]) + } + + pub(super) fn constant_64bit(result_type_id: Word, id: Word, low: Word, high: Word) -> Self { + Self::constant(result_type_id, id, &[low, high]) + } + + pub(super) fn constant(result_type_id: Word, id: Word, values: &[Word]) -> Self { + let mut instruction = Self::new(Op::Constant); + instruction.set_type(result_type_id); + instruction.set_result(id); + + for value in values { + instruction.add_operand(*value); + } + + instruction + } + + pub(super) fn constant_composite( + result_type_id: Word, + id: Word, + constituent_ids: &[Word], + ) -> Self { + let mut instruction = Self::new(Op::ConstantComposite); + instruction.set_type(result_type_id); + instruction.set_result(id); + + for constituent_id in constituent_ids { + instruction.add_operand(*constituent_id); + } + + instruction + } + + // + // Memory Instructions + // + + pub(super) fn variable( + result_type_id: Word, + id: Word, + storage_class: spirv::StorageClass, + initializer_id: Option, + ) -> Self { + let mut instruction = Self::new(Op::Variable); + instruction.set_type(result_type_id); + instruction.set_result(id); + instruction.add_operand(storage_class as u32); + + if let Some(initializer_id) = initializer_id { + instruction.add_operand(initializer_id); + } + + instruction + } + + pub(super) fn load( + result_type_id: Word, + id: Word, + pointer_id: Word, + memory_access: Option, + ) -> Self { + let mut instruction = Self::new(Op::Load); + instruction.set_type(result_type_id); + instruction.set_result(id); + instruction.add_operand(pointer_id); + + if let Some(memory_access) = memory_access { + instruction.add_operand(memory_access.bits()); + } + + instruction + } + + pub(super) fn atomic_load( + result_type_id: Word, + id: Word, + pointer_id: Word, + scope_id: Word, + semantics_id: Word, + ) -> Self { + let mut instruction = Self::new(Op::AtomicLoad); + instruction.set_type(result_type_id); + instruction.set_result(id); + instruction.add_operand(pointer_id); + instruction.add_operand(scope_id); + instruction.add_operand(semantics_id); + instruction + } + + pub(super) fn store( + pointer_id: Word, + value_id: Word, + memory_access: Option, + ) -> Self { + let mut instruction = Self::new(Op::Store); + instruction.add_operand(pointer_id); + instruction.add_operand(value_id); + + if let Some(memory_access) = memory_access { + instruction.add_operand(memory_access.bits()); + } + + instruction + } + + pub(super) fn atomic_store( + pointer_id: Word, + scope_id: Word, + semantics_id: Word, + value_id: Word, + ) -> Self { + let mut instruction = Self::new(Op::AtomicStore); + instruction.add_operand(pointer_id); + instruction.add_operand(scope_id); + instruction.add_operand(semantics_id); + instruction.add_operand(value_id); + instruction + } + + pub(super) fn access_chain( + result_type_id: Word, + id: Word, + base_id: Word, + index_ids: &[Word], + ) -> Self { + let mut instruction = Self::new(Op::AccessChain); + instruction.set_type(result_type_id); + instruction.set_result(id); + instruction.add_operand(base_id); + + for index_id in index_ids { + instruction.add_operand(*index_id); + } + + instruction + } + + pub(super) fn array_length( + result_type_id: Word, + id: Word, + structure_id: Word, + array_member: Word, + ) -> Self { + let mut instruction = Self::new(Op::ArrayLength); + instruction.set_type(result_type_id); + instruction.set_result(id); + instruction.add_operand(structure_id); + instruction.add_operand(array_member); + instruction + } + + // + // Function Instructions + // + + pub(super) fn function( + return_type_id: Word, + id: Word, + function_control: spirv::FunctionControl, + function_type_id: Word, + ) -> Self { + let mut instruction = Self::new(Op::Function); + instruction.set_type(return_type_id); + instruction.set_result(id); + instruction.add_operand(function_control.bits()); + instruction.add_operand(function_type_id); + instruction + } + + pub(super) fn function_parameter(result_type_id: Word, id: Word) -> Self { + let mut instruction = Self::new(Op::FunctionParameter); + instruction.set_type(result_type_id); + instruction.set_result(id); + instruction + } + + pub(super) const fn function_end() -> Self { + Self::new(Op::FunctionEnd) + } + + pub(super) fn function_call( + result_type_id: Word, + id: Word, + function_id: Word, + argument_ids: &[Word], + ) -> Self { + let mut instruction = Self::new(Op::FunctionCall); + instruction.set_type(result_type_id); + instruction.set_result(id); + instruction.add_operand(function_id); + + for argument_id in argument_ids { + instruction.add_operand(*argument_id); + } + + instruction + } + + // + // Image Instructions + // + + pub(super) fn sampled_image( + result_type_id: Word, + id: Word, + image: Word, + sampler: Word, + ) -> Self { + let mut instruction = Self::new(Op::SampledImage); + instruction.set_type(result_type_id); + instruction.set_result(id); + instruction.add_operand(image); + instruction.add_operand(sampler); + instruction + } + + pub(super) fn image_sample( + result_type_id: Word, + id: Word, + lod: SampleLod, + sampled_image: Word, + coordinates: Word, + depth_ref: Option, + ) -> Self { + let op = match (lod, depth_ref) { + (SampleLod::Explicit, None) => Op::ImageSampleExplicitLod, + (SampleLod::Implicit, None) => Op::ImageSampleImplicitLod, + (SampleLod::Explicit, Some(_)) => Op::ImageSampleDrefExplicitLod, + (SampleLod::Implicit, Some(_)) => Op::ImageSampleDrefImplicitLod, + }; + + let mut instruction = Self::new(op); + instruction.set_type(result_type_id); + instruction.set_result(id); + instruction.add_operand(sampled_image); + instruction.add_operand(coordinates); + if let Some(dref) = depth_ref { + instruction.add_operand(dref); + } + + instruction + } + + pub(super) fn image_gather( + result_type_id: Word, + id: Word, + sampled_image: Word, + coordinates: Word, + component_id: Word, + depth_ref: Option, + ) -> Self { + let op = match depth_ref { + None => Op::ImageGather, + Some(_) => Op::ImageDrefGather, + }; + + let mut instruction = Self::new(op); + instruction.set_type(result_type_id); + instruction.set_result(id); + instruction.add_operand(sampled_image); + instruction.add_operand(coordinates); + if let Some(dref) = depth_ref { + instruction.add_operand(dref); + } else { + instruction.add_operand(component_id); + } + + instruction + } + + pub(super) fn image_fetch_or_read( + op: Op, + result_type_id: Word, + id: Word, + image: Word, + coordinates: Word, + ) -> Self { + let mut instruction = Self::new(op); + instruction.set_type(result_type_id); + instruction.set_result(id); + instruction.add_operand(image); + instruction.add_operand(coordinates); + instruction + } + + pub(super) fn image_write(image: Word, coordinates: Word, value: Word) -> Self { + let mut instruction = Self::new(Op::ImageWrite); + instruction.add_operand(image); + instruction.add_operand(coordinates); + instruction.add_operand(value); + instruction + } + + pub(super) fn image_query(op: Op, result_type_id: Word, id: Word, image: Word) -> Self { + let mut instruction = Self::new(op); + instruction.set_type(result_type_id); + instruction.set_result(id); + instruction.add_operand(image); + instruction + } + + // + // Ray Query Instructions + // + #[allow(clippy::too_many_arguments)] + pub(super) fn ray_query_initialize( + query: Word, + acceleration_structure: Word, + ray_flags: Word, + cull_mask: Word, + ray_origin: Word, + ray_tmin: Word, + ray_dir: Word, + ray_tmax: Word, + ) -> Self { + let mut instruction = Self::new(Op::RayQueryInitializeKHR); + instruction.add_operand(query); + instruction.add_operand(acceleration_structure); + instruction.add_operand(ray_flags); + instruction.add_operand(cull_mask); + instruction.add_operand(ray_origin); + instruction.add_operand(ray_tmin); + instruction.add_operand(ray_dir); + instruction.add_operand(ray_tmax); + instruction + } + + pub(super) fn ray_query_proceed(result_type_id: Word, id: Word, query: Word) -> Self { + let mut instruction = Self::new(Op::RayQueryProceedKHR); + instruction.set_type(result_type_id); + instruction.set_result(id); + instruction.add_operand(query); + instruction + } + + pub(super) fn ray_query_get_intersection( + op: Op, + result_type_id: Word, + id: Word, + query: Word, + intersection: Word, + ) -> Self { + let mut instruction = Self::new(op); + instruction.set_type(result_type_id); + instruction.set_result(id); + instruction.add_operand(query); + instruction.add_operand(intersection); + instruction + } + + // + // Conversion Instructions + // + pub(super) fn unary(op: Op, result_type_id: Word, id: Word, value: Word) -> Self { + let mut instruction = Self::new(op); + instruction.set_type(result_type_id); + instruction.set_result(id); + instruction.add_operand(value); + instruction + } + + // + // Composite Instructions + // + + pub(super) fn composite_construct( + result_type_id: Word, + id: Word, + constituent_ids: &[Word], + ) -> Self { + let mut instruction = Self::new(Op::CompositeConstruct); + instruction.set_type(result_type_id); + instruction.set_result(id); + + for constituent_id in constituent_ids { + instruction.add_operand(*constituent_id); + } + + instruction + } + + pub(super) fn composite_extract( + result_type_id: Word, + id: Word, + composite_id: Word, + indices: &[Word], + ) -> Self { + let mut instruction = Self::new(Op::CompositeExtract); + instruction.set_type(result_type_id); + instruction.set_result(id); + + instruction.add_operand(composite_id); + for index in indices { + instruction.add_operand(*index); + } + + instruction + } + + pub(super) fn vector_extract_dynamic( + result_type_id: Word, + id: Word, + vector_id: Word, + index_id: Word, + ) -> Self { + let mut instruction = Self::new(Op::VectorExtractDynamic); + instruction.set_type(result_type_id); + instruction.set_result(id); + + instruction.add_operand(vector_id); + instruction.add_operand(index_id); + + instruction + } + + pub(super) fn vector_shuffle( + result_type_id: Word, + id: Word, + v1_id: Word, + v2_id: Word, + components: &[Word], + ) -> Self { + let mut instruction = Self::new(Op::VectorShuffle); + instruction.set_type(result_type_id); + instruction.set_result(id); + instruction.add_operand(v1_id); + instruction.add_operand(v2_id); + + for &component in components { + instruction.add_operand(component); + } + + instruction + } + + // + // Arithmetic Instructions + // + pub(super) fn binary( + op: Op, + result_type_id: Word, + id: Word, + operand_1: Word, + operand_2: Word, + ) -> Self { + let mut instruction = Self::new(op); + instruction.set_type(result_type_id); + instruction.set_result(id); + instruction.add_operand(operand_1); + instruction.add_operand(operand_2); + instruction + } + + pub(super) fn ternary( + op: Op, + result_type_id: Word, + id: Word, + operand_1: Word, + operand_2: Word, + operand_3: Word, + ) -> Self { + let mut instruction = Self::new(op); + instruction.set_type(result_type_id); + instruction.set_result(id); + instruction.add_operand(operand_1); + instruction.add_operand(operand_2); + instruction.add_operand(operand_3); + instruction + } + + pub(super) fn quaternary( + op: Op, + result_type_id: Word, + id: Word, + operand_1: Word, + operand_2: Word, + operand_3: Word, + operand_4: Word, + ) -> Self { + let mut instruction = Self::new(op); + instruction.set_type(result_type_id); + instruction.set_result(id); + instruction.add_operand(operand_1); + instruction.add_operand(operand_2); + instruction.add_operand(operand_3); + instruction.add_operand(operand_4); + instruction + } + + pub(super) fn relational(op: Op, result_type_id: Word, id: Word, expr_id: Word) -> Self { + let mut instruction = Self::new(op); + instruction.set_type(result_type_id); + instruction.set_result(id); + instruction.add_operand(expr_id); + instruction + } + + pub(super) fn atomic_binary( + op: Op, + result_type_id: Word, + id: Word, + pointer: Word, + scope_id: Word, + semantics_id: Word, + value: Word, + ) -> Self { + let mut instruction = Self::new(op); + instruction.set_type(result_type_id); + instruction.set_result(id); + instruction.add_operand(pointer); + instruction.add_operand(scope_id); + instruction.add_operand(semantics_id); + instruction.add_operand(value); + instruction + } + + // + // Bit Instructions + // + + // + // Relational and Logical Instructions + // + + // + // Derivative Instructions + // + + pub(super) fn derivative(op: Op, result_type_id: Word, id: Word, expr_id: Word) -> Self { + let mut instruction = Self::new(op); + instruction.set_type(result_type_id); + instruction.set_result(id); + instruction.add_operand(expr_id); + instruction + } + + // + // Control-Flow Instructions + // + + pub(super) fn phi( + result_type_id: Word, + result_id: Word, + var_parent_pairs: &[(Word, Word)], + ) -> Self { + let mut instruction = Self::new(Op::Phi); + instruction.add_operand(result_type_id); + instruction.add_operand(result_id); + for &(variable, parent) in var_parent_pairs { + instruction.add_operand(variable); + instruction.add_operand(parent); + } + instruction + } + + pub(super) fn selection_merge( + merge_id: Word, + selection_control: spirv::SelectionControl, + ) -> Self { + let mut instruction = Self::new(Op::SelectionMerge); + instruction.add_operand(merge_id); + instruction.add_operand(selection_control.bits()); + instruction + } + + pub(super) fn loop_merge( + merge_id: Word, + continuing_id: Word, + selection_control: spirv::SelectionControl, + ) -> Self { + let mut instruction = Self::new(Op::LoopMerge); + instruction.add_operand(merge_id); + instruction.add_operand(continuing_id); + instruction.add_operand(selection_control.bits()); + instruction + } + + pub(super) fn label(id: Word) -> Self { + let mut instruction = Self::new(Op::Label); + instruction.set_result(id); + instruction + } + + pub(super) fn branch(id: Word) -> Self { + let mut instruction = Self::new(Op::Branch); + instruction.add_operand(id); + instruction + } + + // TODO Branch Weights not implemented. + pub(super) fn branch_conditional( + condition_id: Word, + true_label: Word, + false_label: Word, + ) -> Self { + let mut instruction = Self::new(Op::BranchConditional); + instruction.add_operand(condition_id); + instruction.add_operand(true_label); + instruction.add_operand(false_label); + instruction + } + + pub(super) fn switch(selector_id: Word, default_id: Word, cases: &[Case]) -> Self { + let mut instruction = Self::new(Op::Switch); + instruction.add_operand(selector_id); + instruction.add_operand(default_id); + for case in cases { + instruction.add_operand(case.value); + instruction.add_operand(case.label_id); + } + instruction + } + + pub(super) fn select( + result_type_id: Word, + id: Word, + condition_id: Word, + accept_id: Word, + reject_id: Word, + ) -> Self { + let mut instruction = Self::new(Op::Select); + instruction.add_operand(result_type_id); + instruction.add_operand(id); + instruction.add_operand(condition_id); + instruction.add_operand(accept_id); + instruction.add_operand(reject_id); + instruction + } + + pub(super) const fn kill() -> Self { + Self::new(Op::Kill) + } + + pub(super) const fn return_void() -> Self { + Self::new(Op::Return) + } + + pub(super) fn return_value(value_id: Word) -> Self { + let mut instruction = Self::new(Op::ReturnValue); + instruction.add_operand(value_id); + instruction + } + + // + // Atomic Instructions + // + + // + // Primitive Instructions + // + + // Barriers + + pub(super) fn control_barrier( + exec_scope_id: Word, + mem_scope_id: Word, + semantics_id: Word, + ) -> Self { + let mut instruction = Self::new(Op::ControlBarrier); + instruction.add_operand(exec_scope_id); + instruction.add_operand(mem_scope_id); + instruction.add_operand(semantics_id); + instruction + } +} + +impl From for spirv::ImageFormat { + fn from(format: crate::StorageFormat) -> Self { + use crate::StorageFormat as Sf; + match format { + Sf::R8Unorm => Self::R8, + Sf::R8Snorm => Self::R8Snorm, + Sf::R8Uint => Self::R8ui, + Sf::R8Sint => Self::R8i, + Sf::R16Uint => Self::R16ui, + Sf::R16Sint => Self::R16i, + Sf::R16Float => Self::R16f, + Sf::Rg8Unorm => Self::Rg8, + Sf::Rg8Snorm => Self::Rg8Snorm, + Sf::Rg8Uint => Self::Rg8ui, + Sf::Rg8Sint => Self::Rg8i, + Sf::R32Uint => Self::R32ui, + Sf::R32Sint => Self::R32i, + Sf::R32Float => Self::R32f, + Sf::Rg16Uint => Self::Rg16ui, + Sf::Rg16Sint => Self::Rg16i, + Sf::Rg16Float => Self::Rg16f, + Sf::Rgba8Unorm => Self::Rgba8, + Sf::Rgba8Snorm => Self::Rgba8Snorm, + Sf::Rgba8Uint => Self::Rgba8ui, + Sf::Rgba8Sint => Self::Rgba8i, + Sf::Bgra8Unorm => Self::Unknown, + Sf::Rgb10a2Uint => Self::Rgb10a2ui, + Sf::Rgb10a2Unorm => Self::Rgb10A2, + Sf::Rg11b10Float => Self::R11fG11fB10f, + Sf::Rg32Uint => Self::Rg32ui, + Sf::Rg32Sint => Self::Rg32i, + Sf::Rg32Float => Self::Rg32f, + Sf::Rgba16Uint => Self::Rgba16ui, + Sf::Rgba16Sint => Self::Rgba16i, + Sf::Rgba16Float => Self::Rgba16f, + Sf::Rgba32Uint => Self::Rgba32ui, + Sf::Rgba32Sint => Self::Rgba32i, + Sf::Rgba32Float => Self::Rgba32f, + Sf::R16Unorm => Self::R16, + Sf::R16Snorm => Self::R16Snorm, + Sf::Rg16Unorm => Self::Rg16, + Sf::Rg16Snorm => Self::Rg16Snorm, + Sf::Rgba16Unorm => Self::Rgba16, + Sf::Rgba16Snorm => Self::Rgba16Snorm, + } + } +} + +impl From for spirv::Dim { + fn from(dim: crate::ImageDimension) -> Self { + use crate::ImageDimension as Id; + match dim { + Id::D1 => Self::Dim1D, + Id::D2 => Self::Dim2D, + Id::D3 => Self::Dim3D, + Id::Cube => Self::DimCube, + } + } +} diff --git a/naga/src/back/spv/writer.rs b/naga/src/back/spv/writer.rs index de3220bbda..e91a03825c 100644 --- a/naga/src/back/spv/writer.rs +++ b/naga/src/back/spv/writer.rs @@ -1891,7 +1891,7 @@ impl Writer { source_code: debug_info.source_code, source_file_id, }); - self.debugs.push(Instruction::source( + self.debugs.append(&mut Instruction::source_auto_continued( spirv::SourceLanguage::Unknown, 0, &debug_info_inner,