diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fa8bd6303..7355eaf07c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,7 +42,7 @@ Bottom level categories: #### 'wgpu::Instance::enumerate_adapters` is now `async` & available on WebGPU -Making `enumerate_adapters` async allows custom backends to use it along with elimnating some native/non-native distinctions +Making `enumerate_adapters` async allows custom backends to use it along with eliminating some native/non-native distinctions This is a breaking change @@ -54,6 +54,12 @@ This is a breaking change By @R-Cramer4 in [#8230](https://github.com/gfx-rs/wgpu/pull/8230) +### Changes + +#### naga + +- The SPIR-V frontend implements OpSpecConstantOp. By @hasenbanck in [8308](https://github.com/gfx-rs/wgpu/pull/8308). + ## v27.0.2 (2025-10-03) ### Bug Fixes diff --git a/naga/src/front/spv/error.rs b/naga/src/front/spv/error.rs index 3e18e4034a..e0194fbd15 100644 --- a/naga/src/front/spv/error.rs +++ b/naga/src/front/spv/error.rs @@ -1,8 +1,8 @@ +use alloc::borrow::Cow; use alloc::{ format, string::{String, ToString}, }; - use codespan_reporting::diagnostic::Diagnostic; use codespan_reporting::files::SimpleFile; use codespan_reporting::term; @@ -14,6 +14,8 @@ use crate::{ front::atomic_upgrade, }; +use crate::proc::ConstantEvaluatorError; + #[derive(Clone, Debug, thiserror::Error)] pub enum Error { #[error("invalid header")] @@ -26,6 +28,12 @@ pub enum Error { UnknownCapability(spirv::Word), #[error("unsupported instruction {1:?} at {0:?}")] UnsupportedInstruction(ModuleState, spirv::Op), + #[error("unsupported opcode in specialization constant operation {0:?}")] + UnsupportedSpecConstantOp(spirv::Op), + #[error("invalid opcode in specialization constant operation {0:?}")] + InvalidSpecConstantOp(spirv::Op), + #[error("{0}")] + SemanticError(Cow<'static, str>), #[error("unsupported capability {0:?}")] UnsupportedCapability(spirv::Capability), #[error("unsupported extension {0}")] @@ -151,11 +159,16 @@ pub enum Error { NonBindingArrayOfImageOrSamplers, #[error("naga only supports specialization constant IDs up to 65535 but was given {0}")] SpecIdTooHigh(u32), - #[error("atomic upgrade error: {0}")] AtomicUpgradeError(atomic_upgrade::Error), } +impl From for Error { + fn from(err: ConstantEvaluatorError) -> Self { + Error::SemanticError(err.to_string().into()) + } +} + impl Error { pub fn emit_to_writer(&self, writer: &mut impl ErrorWrite, source: &str) { self.emit_to_writer_with_path(writer, source, "glsl"); diff --git a/naga/src/front/spv/mod.rs b/naga/src/front/spv/mod.rs index 5e1b114650..bd2be6f5cf 100644 --- a/naga/src/front/spv/mod.rs +++ b/naga/src/front/spv/mod.rs @@ -310,6 +310,13 @@ impl Constant { Self::Override(o) => crate::Expression::Override(o), } } + + const fn to_expr_kind(&self) -> crate::proc::ExpressionKind { + match *self { + Self::Constant(_) => crate::proc::ExpressionKind::Const, + Self::Override(_) => crate::proc::ExpressionKind::Override, + } + } } #[derive(Debug)] @@ -477,6 +484,8 @@ enum MergeBlockInformation { SwitchMerge, } +type OptimizationResult = Result, Option)>, Error>; + /// Fragments of Naga IR, to be assembled into `Statements` once data flow is /// resolved. /// @@ -4753,6 +4762,8 @@ impl> Frontend { self.lookup_function.clear(); self.function_call_graph.clear(); + let mut global_expression_kind_tracker = crate::proc::ExpressionKindTracker::new(); + loop { use spirv::Op; @@ -4792,18 +4803,74 @@ impl> Frontend { Op::TypeImage => self.parse_type_image(inst, &mut module), Op::TypeSampledImage => self.parse_type_sampled_image(inst), Op::TypeSampler => self.parse_type_sampler(inst, &mut module), - Op::Constant | Op::SpecConstant => self.parse_constant(inst, &mut module), - Op::ConstantComposite | Op::SpecConstantComposite => { - self.parse_composite_constant(inst, &mut module) - } - Op::ConstantNull | Op::Undef => self.parse_null_constant(inst, &mut module), - Op::ConstantTrue | Op::SpecConstantTrue => { - self.parse_bool_constant(inst, true, &mut module) - } - Op::ConstantFalse | Op::SpecConstantFalse => { - self.parse_bool_constant(inst, false, &mut module) - } - Op::Variable => self.parse_global_variable(inst, &mut module), + Op::Constant => self.parse_constant( + inst, + &mut module, + &mut global_expression_kind_tracker, + crate::proc::ExpressionKind::Const, + ), + Op::SpecConstant => self.parse_constant( + inst, + &mut module, + &mut global_expression_kind_tracker, + crate::proc::ExpressionKind::Override, + ), + Op::ConstantComposite => self.parse_composite_constant( + inst, + &mut module, + &mut global_expression_kind_tracker, + crate::proc::ExpressionKind::Const, + ), + Op::SpecConstantComposite => self.parse_composite_constant( + inst, + &mut module, + &mut global_expression_kind_tracker, + crate::proc::ExpressionKind::Override, + ), + Op::ConstantNull | Op::Undef => self.parse_null_constant( + inst, + &mut module, + &mut global_expression_kind_tracker, + crate::proc::ExpressionKind::Const, + ), + Op::ConstantTrue => self.parse_bool_constant( + inst, + true, + &mut module, + &mut global_expression_kind_tracker, + crate::proc::ExpressionKind::Const, + ), + Op::SpecConstantTrue => self.parse_bool_constant( + inst, + true, + &mut module, + &mut global_expression_kind_tracker, + crate::proc::ExpressionKind::Override, + ), + Op::ConstantFalse => self.parse_bool_constant( + inst, + false, + &mut module, + &mut global_expression_kind_tracker, + crate::proc::ExpressionKind::Const, + ), + Op::SpecConstantFalse => self.parse_bool_constant( + inst, + false, + &mut module, + &mut global_expression_kind_tracker, + crate::proc::ExpressionKind::Override, + ), + Op::SpecConstantOp => self.parse_spec_constant_op( + inst, + &mut module, + &mut global_expression_kind_tracker, + ), + Op::Variable => self.parse_global_variable( + inst, + &mut module, + &mut global_expression_kind_tracker, + ), Op::Function => { self.switch(ModuleState::Function, inst.op)?; inst.expect(5)?; @@ -5746,6 +5813,8 @@ impl> Frontend { &mut self, inst: Instruction, module: &mut crate::Module, + global_expression_kind_tracker: &mut crate::proc::ExpressionKindTracker, + expr_kind: crate::proc::ExpressionKind, ) -> Result<(), Error> { let start = self.data_offset; self.switch(ModuleState::Type, inst.op)?; @@ -5811,9 +5880,13 @@ impl> Frontend { let span = self.span_from_with_op(start); - let init = module - .global_expressions - .append(crate::Expression::Literal(literal), span); + let init = append_global_expression( + module, + global_expression_kind_tracker, + crate::Expression::Literal(literal), + expr_kind, + span, + ); self.insert_parsed_constant(module, id, type_id, ty, init, span) } @@ -5822,6 +5895,8 @@ impl> Frontend { &mut self, inst: Instruction, module: &mut crate::Module, + global_expression_kind_tracker: &mut crate::proc::ExpressionKindTracker, + expr_kind: crate::proc::ExpressionKind, ) -> Result<(), Error> { let start = self.data_offset; self.switch(ModuleState::Type, inst.op)?; @@ -5838,17 +5913,25 @@ impl> Frontend { let component_id = self.next()?; let span = self.span_from_with_op(start); let constant = self.lookup_constant.lookup(component_id)?; - let expr = module - .global_expressions - .append(constant.inner.to_expr(), span); + let expr = append_global_expression( + module, + global_expression_kind_tracker, + constant.inner.to_expr(), + constant.inner.to_expr_kind(), + span, + ); components.push(expr); } let span = self.span_from_with_op(start); - let init = module - .global_expressions - .append(crate::Expression::Compose { ty, components }, span); + let init = append_global_expression( + module, + global_expression_kind_tracker, + crate::Expression::Compose { ty, components }, + expr_kind, + span, + ); self.insert_parsed_constant(module, id, type_id, ty, init, span) } @@ -5857,6 +5940,8 @@ impl> Frontend { &mut self, inst: Instruction, module: &mut crate::Module, + global_expression_kind_tracker: &mut crate::proc::ExpressionKindTracker, + expr_kind: crate::proc::ExpressionKind, ) -> Result<(), Error> { let start = self.data_offset; self.switch(ModuleState::Type, inst.op)?; @@ -5868,9 +5953,13 @@ impl> Frontend { let type_lookup = self.lookup_type.lookup(type_id)?; let ty = type_lookup.handle; - let init = module - .global_expressions - .append(crate::Expression::ZeroValue(ty), span); + let init = append_global_expression( + module, + global_expression_kind_tracker, + crate::Expression::ZeroValue(ty), + expr_kind, + span, + ); self.insert_parsed_constant(module, id, type_id, ty, init, span) } @@ -5880,6 +5969,8 @@ impl> Frontend { inst: Instruction, value: bool, module: &mut crate::Module, + global_expression_kind_tracker: &mut crate::proc::ExpressionKindTracker, + expr_kind: crate::proc::ExpressionKind, ) -> Result<(), Error> { let start = self.data_offset; self.switch(ModuleState::Type, inst.op)?; @@ -5891,14 +5982,791 @@ impl> Frontend { let type_lookup = self.lookup_type.lookup(type_id)?; let ty = type_lookup.handle; - let init = module.global_expressions.append( + let init = append_global_expression( + module, + global_expression_kind_tracker, crate::Expression::Literal(crate::Literal::Bool(value)), + expr_kind, span, ); self.insert_parsed_constant(module, id, type_id, ty, init, span) } + fn eval_and_append_expression( + &mut self, + module: &mut crate::Module, + global_expression_kind_tracker: &mut crate::proc::ExpressionKindTracker, + expr: crate::Expression, + span: crate::Span, + ) -> Result, Error> { + let mut evaluator = crate::proc::ConstantEvaluator::for_wgsl_module( + module, + global_expression_kind_tracker, + &mut self.layouter, + true, + ); + evaluator + .try_eval_and_append(expr, span) + .map_err(|error| error.into()) + } + + fn get_expr_scalar( + &self, + module: &crate::Module, + expr_handle: Handle, + ) -> Option { + match module.global_expressions[expr_handle] { + crate::Expression::Literal(ref literal) => Some(literal.scalar()), + crate::Expression::Constant(const_handle) => { + let ty = module.constants[const_handle].ty; + match module.types[ty].inner { + crate::TypeInner::Scalar(scalar) | crate::TypeInner::Vector { scalar, .. } => { + Some(scalar) + } + _ => None, + } + } + crate::Expression::Override(override_handle) => { + let ty = module.overrides[override_handle].ty; + match module.types[ty].inner { + crate::TypeInner::Scalar(scalar) | crate::TypeInner::Vector { scalar, .. } => { + Some(scalar) + } + _ => None, + } + } + crate::Expression::As { kind, convert, .. } => { + convert.map(|width| crate::Scalar { kind, width }) + } + // We don't have enough information to determine the scalar type. + _ => None, + } + } + + fn convert_expr_to_target_scalar( + &mut self, + module: &mut crate::Module, + global_expression_kind_tracker: &mut crate::proc::ExpressionKindTracker, + expr: &mut Handle, + target_scalar: crate::Scalar, + span: crate::Span, + ) -> Result<(), Error> { + *expr = self.eval_and_append_expression( + module, + global_expression_kind_tracker, + crate::Expression::As { + expr: *expr, + kind: target_scalar.kind, + convert: Some(target_scalar.width), + }, + span, + )?; + Ok(()) + } + + /// Apply implicit type conversions for binary operations to ensure SPIR-V -> WGSL compatibility. + #[allow(clippy::too_many_arguments)] + fn implicit_cast_operators( + &mut self, + module: &mut crate::Module, + global_expression_kind_tracker: &mut crate::proc::ExpressionKindTracker, + op: crate::BinaryOperator, + ty: Handle, + left_expr: &mut Handle, + right_expr: &mut Handle, + span: crate::Span, + ) -> Result<(), Error> { + match op { + crate::BinaryOperator::ShiftLeft | crate::BinaryOperator::ShiftRight => { + // Shift operations: convert right operand (shift amount) to u32. + if let Some(current_scalar) = self.get_expr_scalar(module, *right_expr) { + if current_scalar != crate::Scalar::U32 { + self.convert_expr_to_target_scalar( + module, + global_expression_kind_tracker, + right_expr, + crate::Scalar::U32, + span, + )?; + } + } + } + // Comparison operators: convert operands to the same type. + crate::BinaryOperator::Equal + | crate::BinaryOperator::NotEqual + | crate::BinaryOperator::Less + | crate::BinaryOperator::LessEqual + | crate::BinaryOperator::Greater + | crate::BinaryOperator::GreaterEqual => { + let left_scalar = self.get_expr_scalar(module, *left_expr); + let right_scalar = self.get_expr_scalar(module, *right_expr); + + if let (Some(left), Some(right)) = (left_scalar, right_scalar) { + if left != right { + let target_scalar = if left.kind != right.kind { + // Different signedness: use unsigned. + crate::Scalar { + kind: crate::ScalarKind::Uint, + width: left.width.max(right.width), + } + } else { + // Same signedness: use the wider type. + if left.width > right.width { + left + } else { + right + } + }; + + if left != target_scalar { + self.convert_expr_to_target_scalar( + module, + global_expression_kind_tracker, + left_expr, + target_scalar, + span, + )?; + } + + if right != target_scalar { + self.convert_expr_to_target_scalar( + module, + global_expression_kind_tracker, + right_expr, + target_scalar, + span, + )?; + } + } + } + } + // Logical operators: no change needed. + crate::BinaryOperator::LogicalAnd | crate::BinaryOperator::LogicalOr => {} + // All other operators: convert operands to match the result type. + _ => { + if let crate::TypeInner::Scalar(target_scalar) + | crate::TypeInner::Vector { + scalar: target_scalar, + .. + } = module.types[ty].inner + { + if let Some(left_scalar) = self.get_expr_scalar(module, *left_expr) { + if left_scalar != target_scalar { + self.convert_expr_to_target_scalar( + module, + global_expression_kind_tracker, + left_expr, + target_scalar, + span, + )?; + } + } + + if let Some(right_scalar) = self.get_expr_scalar(module, *right_expr) { + if right_scalar != target_scalar { + self.convert_expr_to_target_scalar( + module, + global_expression_kind_tracker, + right_expr, + target_scalar, + span, + )?; + } + } + } + } + } + + Ok(()) + } + + fn is_constant_zero( + &self, + module: &crate::Module, + const_id: spirv::Word, + ) -> Result { + let lookup = self.lookup_constant.lookup(const_id)?; + match lookup.inner { + Constant::Constant(const_handle) => { + let init = module.constants[const_handle].init; + match module.global_expressions[init] { + crate::Expression::Literal(literal) => Ok(match literal { + crate::Literal::I32(v) => v == 0, + crate::Literal::U32(v) => v == 0, + crate::Literal::I64(v) => v == 0, + crate::Literal::U64(v) => v == 0, + _ => false, + }), + _ => Ok(false), + } + } + Constant::Override(_) => Ok(false), + } + } + + fn is_constant_one( + &self, + module: &crate::Module, + const_id: spirv::Word, + ) -> Result { + let lookup = self.lookup_constant.lookup(const_id)?; + match lookup.inner { + Constant::Constant(const_handle) => { + let init = module.constants[const_handle].init; + match module.global_expressions[init] { + crate::Expression::Literal(literal) => Ok(match literal { + crate::Literal::I32(v) => v == 1, + crate::Literal::U32(v) => v == 1, + crate::Literal::I64(v) => v == 1, + crate::Literal::U64(v) => v == 1, + _ => false, + }), + _ => Ok(false), + } + } + Constant::Override(_) => Ok(false), + } + } + + fn get_constant_name(&self, module: &crate::Module, const_id: spirv::Word) -> Option { + let lookup = self.lookup_constant.lookup(const_id).ok()?; + match lookup.inner { + Constant::Override(override_handle) => module.overrides[override_handle].name.clone(), + Constant::Constant(_) => None, + } + } + + fn format_target_type(&self, module: &crate::Module, ty: Handle) -> String { + match module.types[ty].inner { + crate::TypeInner::Scalar(scalar) | crate::TypeInner::Vector { scalar, .. } => { + match scalar.kind { + crate::ScalarKind::Sint => String::from("int"), + crate::ScalarKind::Uint => String::from("uint"), + crate::ScalarKind::Float => String::from("float"), + crate::ScalarKind::Bool => String::from("bool"), + crate::ScalarKind::AbstractInt => String::from("abstract_int"), + crate::ScalarKind::AbstractFloat => String::from("abstract_float"), + } + } + _ => String::from("unknown"), + } + } + + /// Try to optimize common SPIR-V type conversion patterns to simpler `As` expressions. + /// + /// Example: IAdd %a_value %zero + #[allow(clippy::too_many_arguments)] + fn try_optimize_type_conversion_pattern( + &mut self, + module: &mut crate::Module, + global_expression_kind_tracker: &mut crate::proc::ExpressionKindTracker, + opcode: spirv::Op, + ty: Handle, + left_id: spirv::Word, + right_id: spirv::Word, + span: crate::Span, + ) -> OptimizationResult { + if matches!(opcode, spirv::Op::IAdd | spirv::Op::BitwiseOr) { + let left_is_zero = self.is_constant_zero(module, left_id)?; + let right_is_zero = self.is_constant_zero(module, right_id)?; + + let (value_id, source_name) = if left_is_zero && !right_is_zero { + (right_id, self.get_constant_name(module, right_id)) + } else if right_is_zero && !left_is_zero { + (left_id, self.get_constant_name(module, left_id)) + } else { + return Ok(None); + }; + + let scalar = match module.types[ty].inner { + crate::TypeInner::Scalar(scalar) + | crate::TypeInner::Vector { scalar, .. } + | crate::TypeInner::Matrix { scalar, .. } => scalar, + _ => return Ok(None), + }; + + let value_expr_inner = self.lookup_constant.lookup(value_id)?.inner.to_expr(); + let value_expr = self.eval_and_append_expression( + module, + global_expression_kind_tracker, + value_expr_inner, + span, + )?; + + let as_expr = self.eval_and_append_expression( + module, + global_expression_kind_tracker, + crate::Expression::As { + expr: value_expr, + kind: scalar.kind, + convert: Some(scalar.width), + }, + span, + )?; + + let derived_name = source_name.map(|name| { + let target_type = self.format_target_type(module, ty); + format!("{name}_as_{target_type}") + }); + + return Ok(Some((as_expr, derived_name))); + } + + Ok(None) + } + + /// Try to optimize `select` pattern used for bool to int/uint conversion to simpler `As` expressions. + /// + /// Example: Select %bool_value %one %zero + #[allow(clippy::too_many_arguments)] + fn try_optimize_select_conversion( + &mut self, + module: &mut crate::Module, + global_expression_kind_tracker: &mut crate::proc::ExpressionKindTracker, + ty: Handle, + condition_id: spirv::Word, + o1_id: spirv::Word, + o2_id: spirv::Word, + span: crate::Span, + ) -> OptimizationResult { + let o1_is_one = self.is_constant_one(module, o1_id)?; + let o2_is_zero = self.is_constant_zero(module, o2_id)?; + + if !o1_is_one || !o2_is_zero { + return Ok(None); + } + + let scalar = match module.types[ty].inner { + crate::TypeInner::Scalar(scalar) + | crate::TypeInner::Vector { scalar, .. } + | crate::TypeInner::Matrix { scalar, .. } => scalar, + _ => return Ok(None), + }; + + let condition_expr_inner = self.lookup_constant.lookup(condition_id)?.inner.to_expr(); + let condition_expr = self.eval_and_append_expression( + module, + global_expression_kind_tracker, + condition_expr_inner, + span, + )?; + + let as_expr = self.eval_and_append_expression( + module, + global_expression_kind_tracker, + crate::Expression::As { + expr: condition_expr, + kind: scalar.kind, + convert: Some(scalar.width), + }, + span, + )?; + + let source_name = self.get_constant_name(module, condition_id); + let derived_name = source_name.map(|name| { + let target_type = self.format_target_type(module, ty); + format!("{name}_as_{target_type}") + }); + + Ok(Some((as_expr, derived_name))) + } + + fn parse_spec_constant_op( + &mut self, + inst: Instruction, + module: &mut crate::Module, + global_expression_kind_tracker: &mut crate::proc::ExpressionKindTracker, + ) -> Result<(), Error> { + use spirv::Op; + + let start = self.data_offset; + self.switch(ModuleState::Type, inst.op)?; + inst.expect_at_least(4)?; + + let result_type_id = self.next()?; + let result_id = self.next()?; + let opcode_word = self.next()?; + + let type_lookup = self.lookup_type.lookup(result_type_id)?; + let ty = type_lookup.handle; + let span = self.span_from_with_op(start); + + let opcode = Op::from_u32(opcode_word).ok_or(Error::UnsupportedInstruction( + self.state, + Op::SpecConstantOp, + ))?; + + let mut derived_name: Option = None; + + let init = match opcode { + Op::SConvert | Op::UConvert | Op::FConvert => { + let value_id = self.next()?; + + if let Some(source_name) = self.get_constant_name(module, value_id) { + let target_type = self.format_target_type(module, ty); + derived_name = Some(format!("{source_name}_as_{target_type}")); + } + + let scalar = match module.types[ty].inner { + crate::TypeInner::Scalar(scalar) + | crate::TypeInner::Vector { scalar, .. } + | crate::TypeInner::Matrix { scalar, .. } => scalar, + _ => return Err(Error::InvalidAsType(ty)), + }; + + let value_expr_inner = self.lookup_constant.lookup(value_id)?.inner.to_expr(); + let value_expr = self.eval_and_append_expression( + module, + global_expression_kind_tracker, + value_expr_inner, + span, + )?; + + self.eval_and_append_expression( + module, + global_expression_kind_tracker, + crate::Expression::As { + expr: value_expr, + kind: scalar.kind, + convert: Some(scalar.width), + }, + span, + )? + } + + Op::SNegate | Op::Not | Op::LogicalNot => { + let value_id = self.next()?; + + let op = match opcode { + Op::SNegate => crate::UnaryOperator::Negate, + Op::Not => crate::UnaryOperator::BitwiseNot, + Op::LogicalNot => crate::UnaryOperator::LogicalNot, + _ => unreachable!(), + }; + + let value_expr_inner = self.lookup_constant.lookup(value_id)?.inner.to_expr(); + let value_expr = self.eval_and_append_expression( + module, + global_expression_kind_tracker, + value_expr_inner, + span, + )?; + + self.eval_and_append_expression( + module, + global_expression_kind_tracker, + crate::Expression::Unary { + op, + expr: value_expr, + }, + span, + )? + } + + Op::IAdd + | Op::ISub + | Op::IMul + | Op::UDiv + | Op::SDiv + | Op::SRem + | Op::UMod + | Op::BitwiseOr + | Op::BitwiseXor + | Op::BitwiseAnd + | Op::ShiftLeftLogical + | Op::ShiftRightLogical + | Op::ShiftRightArithmetic + | Op::LogicalOr + | Op::LogicalAnd + | Op::LogicalEqual + | Op::LogicalNotEqual + | Op::IEqual + | Op::INotEqual + | Op::ULessThan + | Op::SLessThan + | Op::UGreaterThan + | Op::SGreaterThan + | Op::ULessThanEqual + | Op::SLessThanEqual + | Op::UGreaterThanEqual + | Op::SGreaterThanEqual => { + let left_id = self.next()?; + let right_id = self.next()?; + + match self.try_optimize_type_conversion_pattern( + module, + global_expression_kind_tracker, + opcode, + ty, + left_id, + right_id, + span, + )? { + Some((optimized_expr, name)) => { + derived_name = name; + optimized_expr + } + None => { + let op = match opcode { + Op::IAdd => crate::BinaryOperator::Add, + Op::ISub => crate::BinaryOperator::Subtract, + Op::IMul => crate::BinaryOperator::Multiply, + Op::UDiv | Op::SDiv => crate::BinaryOperator::Divide, + Op::SRem | Op::UMod => crate::BinaryOperator::Modulo, + Op::BitwiseOr => crate::BinaryOperator::InclusiveOr, + Op::BitwiseXor => crate::BinaryOperator::ExclusiveOr, + Op::BitwiseAnd => crate::BinaryOperator::And, + Op::ShiftLeftLogical => crate::BinaryOperator::ShiftLeft, + Op::ShiftRightLogical | Op::ShiftRightArithmetic => { + crate::BinaryOperator::ShiftRight + } + Op::LogicalOr => crate::BinaryOperator::LogicalOr, + Op::LogicalAnd => crate::BinaryOperator::LogicalAnd, + Op::LogicalEqual => crate::BinaryOperator::Equal, + Op::LogicalNotEqual => crate::BinaryOperator::NotEqual, + Op::IEqual => crate::BinaryOperator::Equal, + Op::INotEqual => crate::BinaryOperator::NotEqual, + Op::ULessThan | Op::SLessThan => crate::BinaryOperator::Less, + Op::UGreaterThan | Op::SGreaterThan => crate::BinaryOperator::Greater, + Op::ULessThanEqual | Op::SLessThanEqual => { + crate::BinaryOperator::LessEqual + } + Op::UGreaterThanEqual | Op::SGreaterThanEqual => { + crate::BinaryOperator::GreaterEqual + } + _ => unreachable!(), + }; + + let left_expr_inner = self.lookup_constant.lookup(left_id)?.inner.to_expr(); + let right_expr_inner = + self.lookup_constant.lookup(right_id)?.inner.to_expr(); + let mut left_expr = self.eval_and_append_expression( + module, + global_expression_kind_tracker, + left_expr_inner, + span, + )?; + let mut right_expr = self.eval_and_append_expression( + module, + global_expression_kind_tracker, + right_expr_inner, + span, + )?; + + self.implicit_cast_operators( + module, + global_expression_kind_tracker, + op, + ty, + &mut left_expr, + &mut right_expr, + span, + )?; + + self.eval_and_append_expression( + module, + global_expression_kind_tracker, + crate::Expression::Binary { + op, + left: left_expr, + right: right_expr, + }, + span, + )? + } + } + } + + Op::SMod => { + // x - y * int(floor(float(x) / float(y))) + + let left_id = self.next()?; + let right_id = self.next()?; + + let scalar = match module.types[ty].inner { + crate::TypeInner::Scalar(scalar) | crate::TypeInner::Vector { scalar, .. } => { + scalar + } + _ => return Err(Error::InvalidAsType(ty)), + }; + + let left_inner = self.lookup_constant.lookup(left_id)?.inner.to_expr(); + let right_inner = self.lookup_constant.lookup(right_id)?.inner.to_expr(); + let left = self.eval_and_append_expression( + module, + global_expression_kind_tracker, + left_inner, + span, + )?; + let right = self.eval_and_append_expression( + module, + global_expression_kind_tracker, + right_inner, + span, + )?; + + let left_cast = self.eval_and_append_expression( + module, + global_expression_kind_tracker, + crate::Expression::As { + expr: left, + kind: crate::ScalarKind::Float, + convert: Some(scalar.width), + }, + span, + )?; + let right_cast = self.eval_and_append_expression( + module, + global_expression_kind_tracker, + crate::Expression::As { + expr: right, + kind: crate::ScalarKind::Float, + convert: Some(scalar.width), + }, + span, + )?; + let div = self.eval_and_append_expression( + module, + global_expression_kind_tracker, + crate::Expression::Binary { + op: crate::BinaryOperator::Divide, + left: left_cast, + right: right_cast, + }, + span, + )?; + let floor = self.eval_and_append_expression( + module, + global_expression_kind_tracker, + crate::Expression::Math { + fun: crate::MathFunction::Floor, + arg: div, + arg1: None, + arg2: None, + arg3: None, + }, + span, + )?; + let cast = self.eval_and_append_expression( + module, + global_expression_kind_tracker, + crate::Expression::As { + expr: floor, + kind: scalar.kind, + convert: Some(scalar.width), + }, + span, + )?; + let mult = self.eval_and_append_expression( + module, + global_expression_kind_tracker, + crate::Expression::Binary { + op: crate::BinaryOperator::Multiply, + left: cast, + right, + }, + span, + )?; + self.eval_and_append_expression( + module, + global_expression_kind_tracker, + crate::Expression::Binary { + op: crate::BinaryOperator::Subtract, + left, + right: mult, + }, + span, + )? + } + + Op::Select => { + let condition_id = self.next()?; + let o1_id = self.next()?; + let o2_id = self.next()?; + + match self.try_optimize_select_conversion( + module, + global_expression_kind_tracker, + ty, + condition_id, + o1_id, + o2_id, + span, + )? { + Some((optimized_expr, name)) => { + derived_name = name; + optimized_expr + } + None => { + let cond_expr = self.lookup_constant.lookup(condition_id)?.inner.to_expr(); + let o1_expr = self.lookup_constant.lookup(o1_id)?.inner.to_expr(); + let o2_expr = self.lookup_constant.lookup(o2_id)?.inner.to_expr(); + let cond = self.eval_and_append_expression( + module, + global_expression_kind_tracker, + cond_expr, + span, + )?; + let o1 = self.eval_and_append_expression( + module, + global_expression_kind_tracker, + o1_expr, + span, + )?; + let o2 = self.eval_and_append_expression( + module, + global_expression_kind_tracker, + o2_expr, + span, + )?; + + self.eval_and_append_expression( + module, + global_expression_kind_tracker, + crate::Expression::Select { + condition: cond, + accept: o1, + reject: o2, + }, + span, + )? + } + } + } + + Op::VectorShuffle | Op::CompositeExtract | Op::CompositeInsert | Op::QuantizeToF16 => { + // Nothing stops us from implementing these cases in general. + // I just couldn't get them to work properly. + return Err(Error::UnsupportedSpecConstantOp(opcode)); + } + + _ => return Err(Error::InvalidSpecConstantOp(opcode)), + }; + + let decor = self.future_decor.remove(&result_id).unwrap_or_default(); + let name = decor + .name + .or(derived_name) + .or_else(|| Some(format!("_spec_const_op_{result_id}"))); + let op_override = crate::Override { + name, + id: None, + ty, + init: Some(init), + }; + + self.lookup_constant.insert( + result_id, + LookupConstant { + inner: Constant::Override(module.overrides.append(op_override, span)), + type_id: result_type_id, + }, + ); + + Ok(()) + } + fn insert_parsed_constant( &mut self, module: &mut crate::Module, @@ -5936,6 +6804,7 @@ impl> Frontend { &mut self, inst: Instruction, module: &mut crate::Module, + global_expression_kind_tracker: &mut crate::proc::ExpressionKindTracker, ) -> Result<(), Error> { let start = self.data_offset; self.switch(ModuleState::Type, inst.op)?; @@ -5949,9 +6818,13 @@ impl> Frontend { let init_id = self.next()?; let span = self.span_from_with_op(start); let lconst = self.lookup_constant.lookup(init_id)?; - let expr = module - .global_expressions - .append(lconst.inner.to_expr(), span); + let expr = append_global_expression( + module, + global_expression_kind_tracker, + lconst.inner.to_expr(), + lconst.inner.to_expr_kind(), + span, + ); Some(expr) } else { None @@ -6070,9 +6943,10 @@ impl> Frontend { let init = match binding { Some(crate::Binding::BuiltIn(built_in)) => { match null::generate_default_built_in( + module, + global_expression_kind_tracker, Some(built_in), ty, - &mut module.global_expressions, span, ) { Ok(handle) => Some(handle), @@ -6085,25 +6959,36 @@ impl> Frontend { Some(crate::Binding::Location { .. }) => None, None => match module.types[ty].inner { crate::TypeInner::Struct { ref members, .. } => { - let mut components = Vec::with_capacity(members.len()); - for member in members.iter() { - let built_in = match member.binding { - Some(crate::Binding::BuiltIn(built_in)) => Some(built_in), - _ => None, - }; + // Collect member information first to avoid borrow conflicts + let member_info: Vec<_> = members + .iter() + .map(|member| { + let built_in = match member.binding { + Some(crate::Binding::BuiltIn(built_in)) => Some(built_in), + _ => None, + }; + (built_in, member.ty) + }) + .collect(); + + let mut components = Vec::with_capacity(member_info.len()); + for (built_in, member_ty) in member_info { let handle = null::generate_default_built_in( + module, + global_expression_kind_tracker, built_in, - member.ty, - &mut module.global_expressions, + member_ty, span, )?; components.push(handle); } - Some( - module - .global_expressions - .append(crate::Expression::Compose { ty, components }, span), - ) + Some(append_global_expression( + module, + global_expression_kind_tracker, + crate::Expression::Compose { ty, components }, + crate::proc::ExpressionKind::Const, + span, + )) } _ => None, }, @@ -6195,6 +7080,18 @@ impl> Frontend { } } +fn append_global_expression( + module: &mut crate::Module, + global_expression_kind_tracker: &mut crate::proc::ExpressionKindTracker, + expr: crate::Expression, + expr_kind: crate::proc::ExpressionKind, + span: crate::Span, +) -> Handle { + let handle = module.global_expressions.append(expr, span); + global_expression_kind_tracker.insert(handle, expr_kind); + handle +} + fn make_index_literal( ctx: &mut BlockContext, index: u32, diff --git a/naga/src/front/spv/null.rs b/naga/src/front/spv/null.rs index 8cd33f142f..ff81f66008 100644 --- a/naga/src/front/spv/null.rs +++ b/naga/src/front/spv/null.rs @@ -1,21 +1,32 @@ use alloc::vec; use super::Error; -use crate::arena::{Arena, Handle}; +use crate::arena::Handle; /// Create a default value for an output built-in. pub fn generate_default_built_in( + module: &mut crate::Module, + global_expression_kind_tracker: &mut crate::proc::ExpressionKindTracker, built_in: Option, ty: Handle, - global_expressions: &mut Arena, span: crate::Span, ) -> Result, Error> { let expr = match built_in { Some(crate::BuiltIn::Position { .. }) => { - let zero = global_expressions - .append(crate::Expression::Literal(crate::Literal::F32(0.0)), span); - let one = global_expressions - .append(crate::Expression::Literal(crate::Literal::F32(1.0)), span); + let zero = super::append_global_expression( + module, + global_expression_kind_tracker, + crate::Expression::Literal(crate::Literal::F32(0.0)), + crate::proc::ExpressionKind::Const, + span, + ); + let one = super::append_global_expression( + module, + global_expression_kind_tracker, + crate::Expression::Literal(crate::Literal::F32(1.0)), + crate::proc::ExpressionKind::Const, + span, + ); crate::Expression::Compose { ty, components: vec![zero, zero, zero, one], @@ -29,5 +40,11 @@ pub fn generate_default_built_in( // Note: `crate::BuiltIn::ClipDistance` is intentionally left for the default path _ => crate::Expression::ZeroValue(ty), }; - Ok(global_expressions.append(expr, span)) + Ok(super::append_global_expression( + module, + global_expression_kind_tracker, + expr, + crate::proc::ExpressionKind::Const, + span, + )) } diff --git a/naga/tests/in/spv/spec-constant-op-chained.spvasm b/naga/tests/in/spv/spec-constant-op-chained.spvasm new file mode 100644 index 0000000000..aa05b955b3 --- /dev/null +++ b/naga/tests/in/spv/spec-constant-op-chained.spvasm @@ -0,0 +1,74 @@ +; SPIR-V +; Version: 1.4 +; Generator: Manual +; Bound: 50 +; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %output_buffer + OpExecutionMode %main LocalSize 1 1 1 + +; Decorations for spec constants + OpDecorate %spec_a SpecId 0 + OpDecorate %spec_b SpecId 1 + +; Decorations for storage buffer + OpDecorate %OutputStruct Block + OpMemberDecorate %OutputStruct 0 Offset 0 + OpMemberDecorate %OutputStruct 1 Offset 4 + OpMemberDecorate %OutputStruct 2 Offset 8 + OpDecorate %output_buffer DescriptorSet 0 + OpDecorate %output_buffer Binding 0 + +; Types + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + +; Storage buffer types +%OutputStruct = OpTypeStruct %uint %uint %uint +%_ptr_StorageBuffer_OutputStruct = OpTypePointer StorageBuffer %OutputStruct +%output_buffer = OpVariable %_ptr_StorageBuffer_OutputStruct StorageBuffer +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint + +; Base spec constants +%spec_a = OpSpecConstant %int 0 +%spec_b = OpSpecConstant %int 0 + +; Basic chaining +%add_result = OpSpecConstantOp %int IAdd %spec_a %spec_b ; 10 + 3 = 13 +%mul_result = OpSpecConstantOp %int IMul %add_result %spec_b ; 13 * 3 = 39 +%sub_result = OpSpecConstantOp %int ISub %mul_result %add_result ; 39 - 13 = 26 + +; Deeper chaining +%step1 = OpSpecConstantOp %int IAdd %spec_a %spec_b ; 10 + 3 = 13 +%step2 = OpSpecConstantOp %int IMul %step1 %spec_a ; 13 * 10 = 130 +%const_one = OpConstant %int 1 +%step3 = OpSpecConstantOp %int ISub %step2 %const_one ; 130 - 1 = 129 +%step4 = OpSpecConstantOp %int IMul %step3 %spec_b ; 129 * 3 = 387 + +; Constants for indices + %idx_0 = OpConstant %uint 0 + %idx_1 = OpConstant %uint 1 + %idx_2 = OpConstant %uint 2 + +; Main function + %main = OpFunction %void None %3 + %5 = OpLabel + +; Store results + %mul_uint = OpBitcast %uint %mul_result + %ptr_0 = OpAccessChain %_ptr_StorageBuffer_uint %output_buffer %idx_0 + OpStore %ptr_0 %mul_uint + + %sub_uint = OpBitcast %uint %sub_result + %ptr_1 = OpAccessChain %_ptr_StorageBuffer_uint %output_buffer %idx_1 + OpStore %ptr_1 %sub_uint + + %step4_uint = OpBitcast %uint %step4 + %ptr_2 = OpAccessChain %_ptr_StorageBuffer_uint %output_buffer %idx_2 + OpStore %ptr_2 %step4_uint + + OpReturn + OpFunctionEnd diff --git a/naga/tests/in/spv/spec-constant-op-chained.toml b/naga/tests/in/spv/spec-constant-op-chained.toml new file mode 100644 index 0000000000..c0c904e632 --- /dev/null +++ b/naga/tests/in/spv/spec-constant-op-chained.toml @@ -0,0 +1,8 @@ +pipeline_constants = { 0 = 10, 1 = 3 } +targets = "HLSL" + +[spv-in] +adjust_coordinate_space = false + +[spv] +separate_entry_points = true diff --git a/naga/tests/in/spv/spec-constant-op.comp b/naga/tests/in/spv/spec-constant-op.comp new file mode 100644 index 0000000000..463a0b0724 --- /dev/null +++ b/naga/tests/in/spv/spec-constant-op.comp @@ -0,0 +1,219 @@ +// Compiled with: +// glslang -V -o naga/tests/in/spv/spec-constant-op.spv naga/tests/in/spv/spec-constant-op.comp +// Disassembled with: +// spirv-dis naga/tests/in/spv/spec-constant-op.spv -o naga/tests/in/spv/spec-constant-op.spvasm +// Based on this test of glslang: https://github.com/KhronosGroup/glslang/blob/main/Test/spv.specConstantOperations.vert +#version 450 + +layout(constant_id = 0) const float sp_float = 0.0; +layout(constant_id = 1) const int sp_int = 0; +layout(constant_id = 2) const uint sp_uint = 0; +layout(constant_id = 3) const int sp_sint = 0; + +// +// Scalars +// + +// uint/int <-> bool conversion +const bool bool_from_int = bool(sp_int); // bool(3) = true +const bool bool_from_uint = bool(sp_uint); // bool(20) = true +const int int_from_bool = int(bool_from_int); // int(true) = 1 +const uint uint_from_bool = uint(bool_from_int); // uint(true) = 1 + +// uint <-> int conversion +const uint sp_uint_from_sint = uint(sp_sint); // uint(4) = 4 +const int sp_sint_from_uint = int(sp_uint); // int(20) = 20 + +// Negate and Not +const int negate_int = -sp_int; // -3 = -3 +const int not_int = ~sp_int; // ~3 = -4 + +// Add and Subtract +const int sp_int_add_two = sp_int + 2; // 3 + 2 = 5 +const int sp_int_add_two_sub_three = sp_int + 2 - 3; // 3 + 2 - 3 = 2 +const int sp_int_add_two_sub_four = sp_int_add_two - 4; // 5 - 4 = 1 + +// Mixed-type arithmetic operations +const uint mixed_sint_add_uint = sp_sint + sp_uint; // 4 + 20 = 24 +const uint mixed_uint_sub_sint = sp_uint - sp_sint; // 20 - 4 = 16 +const uint mixed_spec_sint_add_const_uint = sp_sint + 10u; // 4 + 10 = 14 +const uint mixed_const_sint_mul_spec_uint = 3 * sp_uint; // 3 * 20 = 60 + +// Mul, Div +const int sp_sint_mul_two = sp_sint * 2; // 4 * 2 = 8 +const uint sp_uint_mul_two = sp_uint * 2; // 20 * 2 = 40 +const int sp_sint_mul_two_div_five = sp_sint_mul_two / 5; // 8 / 5 = 1 +const uint sp_uint_mul_two_div_five = sp_uint_mul_two / 5; // 40 / 5 = 8 +const int sp_sint_mul_three_div_five = sp_sint * 3 / 5; // 4 * 3 / 5 = 2 + +// Rem +const int sp_sint_rem_four = sp_sint % 4; // 4 % 4 = 0 +const uint sp_uint_rem_four = sp_uint % 4; // 20 % 4 = 0 + +// Mixed-type modulo operations +const uint mixed_sint_mod_uint = sp_sint % sp_uint; // 4 % 20 = 4 +const uint mixed_uint_mod_sint = sp_uint % sp_sint; // 20 % 4 = 0 +const int mixed_sint_smod_const_int = sp_sint % 3; // 4 % 3 = 1 +const uint mixed_uint_mod_const_uint = sp_uint % 7u; // 20 % 7 = 6 + +// Shift +const int sp_sint_shift_right_arithmetic = sp_sint >> 10; // 4 >> 10 = 0 +const uint sp_uint_shift_right_arithmetic = sp_uint >> 20u; // 20 >> 20 = 0 +const int sp_sint_shift_left = sp_sint << 1u; // 4 << 1 = 8 +const uint sp_uint_shift_left = sp_uint << 2; // 20 << 2 = 80 + +// Bitwise And, Or, Xor +const int sp_sint_and_3 = sp_sint & 3; // 4 & 3 = 0 +const int sp_sint_or_256 = sp_sint | 0x100; // 4 | 256 = 260 +const uint sp_uint_xor_512 = sp_uint ^ 0x200; // 20 ^ 512 = 532 + +// Mixed-type bitwise operations +const uint mixed_sint_and_uint = sp_sint & sp_uint; // 4 & 20 = 4 +const uint mixed_uint_or_sint = sp_uint | sp_sint; // 20 | 4 = 20 +const uint mixed_sint_xor_uint = sp_sint ^ sp_uint; // 4 ^ 20 = 16 +const uint mixed_spec_uint_and_const_sint = sp_uint & 15; // 20 & 15 = 4 +const uint mixed_const_uint_or_spec_sint = 0x80u | sp_sint; // 128 | 4 = 132 + +// Scalar comparison +const bool sp_int_lt_sp_sint = sp_int < sp_sint; // 3 < 4 = true +const bool sp_int_le_sp_sint = sp_int <= sp_sint; // 3 <= 4 = true +const bool sp_uint_equal_sp_uint = sp_uint == sp_uint; // 20 == 20 = true +const bool sp_uint_not_equal_sp_uint = sp_uint != sp_uint; // 20 != 20 = false +const bool sp_int_gt_sp_sint = sp_int > sp_sint; // 3 > 4 = false +const bool sp_int_ge_sp_sint = sp_int >= sp_sint; // 3 >= 4 = false + +// Mixed-type comparisons +const bool mixed_sint_lt_uint = sp_sint < sp_uint; // 4 < 20 = true +const bool mixed_uint_ge_sint = sp_uint >= sp_sint; // 20 >= 4 = true +const bool mixed_sint_eq_uint = sp_sint == sp_uint; // 4 == 20 = false + +// Logical operations +const bool logical_and = bool_from_int && bool_from_uint; // true && true = true +const bool logical_or = bool_from_int || bool_from_uint; // true || true = true +const bool logical_equal = bool_from_int == bool_from_uint; // true == true = true +const bool logical_not_equal = bool_from_int != bool_from_uint; // true != true = false + +// ternary +layout(constant_id = 4) const int a = 0; +layout(constant_id = 5) const int b = 0; +layout(constant_id = 6) const bool c = false; +const int t1 = c ? 13 : 17; // true ? 13 : 17 = 13 +const int t2 = c ? a : 17; // true ? 1 : 17 = 1 +const int t3 = true ? a : 17; // true ? 1 : 17 = 1 +const int t4 = a > b ? 13 + a : 17 * b; // 1 > 0 ? 13+1 : 17*0 = 14 + +layout(set = 0, binding = 0) buffer OutputBuffer { + uint bool_from_int_val; + uint bool_from_uint_val; + int int_from_bool_val; + uint uint_from_bool_val; + uint sp_uint_from_sint_val; + int sp_sint_from_uint_val; + int negate_int_val; + int not_int_val; + int sp_int_add_two_val; + int sp_int_add_two_sub_three_val; + int sp_int_add_two_sub_four_val; + uint mixed_sint_add_uint_val; + uint mixed_uint_sub_sint_val; + uint mixed_spec_sint_add_const_uint_val; + uint mixed_const_sint_mul_spec_uint_val; + int sp_sint_mul_two_val; + uint sp_uint_mul_two_val; + int sp_sint_mul_two_div_five_val; + uint sp_uint_mul_two_div_five_val; + int sp_sint_mul_three_div_five_val; + int sp_sint_rem_four_val; + uint sp_uint_rem_four_val; + uint mixed_sint_mod_uint_val; + uint mixed_uint_mod_sint_val; + int mixed_sint_smod_const_int_val; + uint mixed_uint_mod_const_uint_val; + int sp_sint_shift_right_arithmetic_val; + uint sp_uint_shift_right_arithmetic_val; + int sp_sint_shift_left_val; + uint sp_uint_shift_left_val; + int sp_sint_and_3_val; + int sp_sint_or_256_val; + uint sp_uint_xor_512_val; + uint mixed_sint_and_uint_val; + uint mixed_uint_or_sint_val; + uint mixed_sint_xor_uint_val; + uint mixed_spec_uint_and_const_sint_val; + uint mixed_const_uint_or_spec_sint_val; + uint sp_int_lt_sp_sint_val; + uint sp_int_le_sp_sint_val; + uint sp_uint_equal_sp_uint_val; + uint sp_uint_not_equal_sp_uint_val; + uint sp_int_gt_sp_sint_val; + uint sp_int_ge_sp_sint_val; + uint mixed_sint_lt_uint_val; + uint mixed_uint_ge_sint_val; + uint mixed_sint_eq_uint_val; + uint logical_and_val; + uint logical_or_val; + uint logical_equal_val; + uint logical_not_equal_val; + int t1_val; + int t2_val; + int t3_val; + int t4_val; +} output_buffer; + +void main() { + output_buffer.bool_from_int_val = uint(bool_from_int); + output_buffer.bool_from_uint_val = uint(bool_from_uint); + output_buffer.int_from_bool_val = int_from_bool; + output_buffer.uint_from_bool_val = uint_from_bool; + output_buffer.sp_uint_from_sint_val = sp_uint_from_sint; + output_buffer.sp_sint_from_uint_val = sp_sint_from_uint; + output_buffer.negate_int_val = negate_int; + output_buffer.not_int_val = not_int; + output_buffer.sp_int_add_two_val = sp_int_add_two; + output_buffer.sp_int_add_two_sub_three_val = sp_int_add_two_sub_three; + output_buffer.sp_int_add_two_sub_four_val = sp_int_add_two_sub_four; + output_buffer.mixed_sint_add_uint_val = mixed_sint_add_uint; + output_buffer.mixed_uint_sub_sint_val = mixed_uint_sub_sint; + output_buffer.mixed_spec_sint_add_const_uint_val = mixed_spec_sint_add_const_uint; + output_buffer.mixed_const_sint_mul_spec_uint_val = mixed_const_sint_mul_spec_uint; + output_buffer.sp_sint_mul_two_val = sp_sint_mul_two; + output_buffer.sp_uint_mul_two_val = sp_uint_mul_two; + output_buffer.sp_sint_mul_two_div_five_val = sp_sint_mul_two_div_five; + output_buffer.sp_uint_mul_two_div_five_val = sp_uint_mul_two_div_five; + output_buffer.sp_sint_mul_three_div_five_val = sp_sint_mul_three_div_five; + output_buffer.sp_sint_rem_four_val = sp_sint_rem_four; + output_buffer.sp_uint_rem_four_val = sp_uint_rem_four; + output_buffer.mixed_sint_mod_uint_val = mixed_sint_mod_uint; + output_buffer.mixed_uint_mod_sint_val = mixed_uint_mod_sint; + output_buffer.mixed_sint_smod_const_int_val = mixed_sint_smod_const_int; + output_buffer.mixed_uint_mod_const_uint_val = mixed_uint_mod_const_uint; + output_buffer.sp_sint_shift_right_arithmetic_val = sp_sint_shift_right_arithmetic; + output_buffer.sp_uint_shift_right_arithmetic_val = sp_uint_shift_right_arithmetic; + output_buffer.sp_sint_shift_left_val = sp_sint_shift_left; + output_buffer.sp_uint_shift_left_val = sp_uint_shift_left; + output_buffer.sp_sint_and_3_val = sp_sint_and_3; + output_buffer.sp_sint_or_256_val = sp_sint_or_256; + output_buffer.sp_uint_xor_512_val = sp_uint_xor_512; + output_buffer.mixed_sint_and_uint_val = mixed_sint_and_uint; + output_buffer.mixed_uint_or_sint_val = mixed_uint_or_sint; + output_buffer.mixed_sint_xor_uint_val = mixed_sint_xor_uint; + output_buffer.mixed_spec_uint_and_const_sint_val = mixed_spec_uint_and_const_sint; + output_buffer.mixed_const_uint_or_spec_sint_val = mixed_const_uint_or_spec_sint; + output_buffer.sp_int_lt_sp_sint_val = uint(sp_int_lt_sp_sint); + output_buffer.sp_int_le_sp_sint_val = uint(sp_int_le_sp_sint); + output_buffer.sp_uint_equal_sp_uint_val = uint(sp_uint_equal_sp_uint); + output_buffer.sp_uint_not_equal_sp_uint_val = uint(sp_uint_not_equal_sp_uint); + output_buffer.sp_int_gt_sp_sint_val = uint(sp_int_gt_sp_sint); + output_buffer.sp_int_ge_sp_sint_val = uint(sp_int_ge_sp_sint); + output_buffer.mixed_sint_lt_uint_val = uint(mixed_sint_lt_uint); + output_buffer.mixed_uint_ge_sint_val = uint(mixed_uint_ge_sint); + output_buffer.mixed_sint_eq_uint_val = uint(mixed_sint_eq_uint); + output_buffer.logical_and_val = uint(logical_and); + output_buffer.logical_or_val = uint(logical_or); + output_buffer.logical_equal_val = uint(logical_equal); + output_buffer.logical_not_equal_val = uint(logical_not_equal); + output_buffer.t1_val = t1; + output_buffer.t2_val = t2; + output_buffer.t3_val = t3; + output_buffer.t4_val = t4; +} diff --git a/naga/tests/in/spv/spec-constant-op.spvasm b/naga/tests/in/spv/spec-constant-op.spvasm new file mode 100644 index 0000000000..f0b8020b32 --- /dev/null +++ b/naga/tests/in/spv/spec-constant-op.spvasm @@ -0,0 +1,484 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 11 +; Bound: 233 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpName %OutputBuffer "OutputBuffer" + OpMemberName %OutputBuffer 0 "bool_from_int_val" + OpMemberName %OutputBuffer 1 "bool_from_uint_val" + OpMemberName %OutputBuffer 2 "int_from_bool_val" + OpMemberName %OutputBuffer 3 "uint_from_bool_val" + OpMemberName %OutputBuffer 4 "sp_uint_from_sint_val" + OpMemberName %OutputBuffer 5 "sp_sint_from_uint_val" + OpMemberName %OutputBuffer 6 "negate_int_val" + OpMemberName %OutputBuffer 7 "not_int_val" + OpMemberName %OutputBuffer 8 "sp_int_add_two_val" + OpMemberName %OutputBuffer 9 "sp_int_add_two_sub_three_val" + OpMemberName %OutputBuffer 10 "sp_int_add_two_sub_four_val" + OpMemberName %OutputBuffer 11 "mixed_sint_add_uint_val" + OpMemberName %OutputBuffer 12 "mixed_uint_sub_sint_val" + OpMemberName %OutputBuffer 13 "mixed_spec_sint_add_const_uint_val" + OpMemberName %OutputBuffer 14 "mixed_const_sint_mul_spec_uint_val" + OpMemberName %OutputBuffer 15 "sp_sint_mul_two_val" + OpMemberName %OutputBuffer 16 "sp_uint_mul_two_val" + OpMemberName %OutputBuffer 17 "sp_sint_mul_two_div_five_val" + OpMemberName %OutputBuffer 18 "sp_uint_mul_two_div_five_val" + OpMemberName %OutputBuffer 19 "sp_sint_mul_three_div_five_val" + OpMemberName %OutputBuffer 20 "sp_sint_rem_four_val" + OpMemberName %OutputBuffer 21 "sp_uint_rem_four_val" + OpMemberName %OutputBuffer 22 "mixed_sint_mod_uint_val" + OpMemberName %OutputBuffer 23 "mixed_uint_mod_sint_val" + OpMemberName %OutputBuffer 24 "mixed_sint_smod_const_int_val" + OpMemberName %OutputBuffer 25 "mixed_uint_mod_const_uint_val" + OpMemberName %OutputBuffer 26 "sp_sint_shift_right_arithmetic_val" + OpMemberName %OutputBuffer 27 "sp_uint_shift_right_arithmetic_val" + OpMemberName %OutputBuffer 28 "sp_sint_shift_left_val" + OpMemberName %OutputBuffer 29 "sp_uint_shift_left_val" + OpMemberName %OutputBuffer 30 "sp_sint_and_3_val" + OpMemberName %OutputBuffer 31 "sp_sint_or_256_val" + OpMemberName %OutputBuffer 32 "sp_uint_xor_512_val" + OpMemberName %OutputBuffer 33 "mixed_sint_and_uint_val" + OpMemberName %OutputBuffer 34 "mixed_uint_or_sint_val" + OpMemberName %OutputBuffer 35 "mixed_sint_xor_uint_val" + OpMemberName %OutputBuffer 36 "mixed_spec_uint_and_const_sint_val" + OpMemberName %OutputBuffer 37 "mixed_const_uint_or_spec_sint_val" + OpMemberName %OutputBuffer 38 "sp_int_lt_sp_sint_val" + OpMemberName %OutputBuffer 39 "sp_int_le_sp_sint_val" + OpMemberName %OutputBuffer 40 "sp_uint_equal_sp_uint_val" + OpMemberName %OutputBuffer 41 "sp_uint_not_equal_sp_uint_val" + OpMemberName %OutputBuffer 42 "sp_int_gt_sp_sint_val" + OpMemberName %OutputBuffer 43 "sp_int_ge_sp_sint_val" + OpMemberName %OutputBuffer 44 "mixed_sint_lt_uint_val" + OpMemberName %OutputBuffer 45 "mixed_uint_ge_sint_val" + OpMemberName %OutputBuffer 46 "mixed_sint_eq_uint_val" + OpMemberName %OutputBuffer 47 "logical_and_val" + OpMemberName %OutputBuffer 48 "logical_or_val" + OpMemberName %OutputBuffer 49 "logical_equal_val" + OpMemberName %OutputBuffer 50 "logical_not_equal_val" + OpMemberName %OutputBuffer 51 "t1_val" + OpMemberName %OutputBuffer 52 "t2_val" + OpMemberName %OutputBuffer 53 "t3_val" + OpMemberName %OutputBuffer 54 "t4_val" + OpName %output_buffer "output_buffer" + OpName %sp_int "sp_int" + OpName %bool_from_int "bool_from_int" + OpName %sp_uint "sp_uint" + OpName %bool_from_uint "bool_from_uint" + OpName %int_from_bool "int_from_bool" + OpName %uint_from_bool "uint_from_bool" + OpName %sp_sint "sp_sint" + OpName %sp_uint_from_sint "sp_uint_from_sint" + OpName %sp_sint_from_uint "sp_sint_from_uint" + OpName %negate_int "negate_int" + OpName %not_int "not_int" + OpName %sp_int_add_two "sp_int_add_two" + OpName %sp_int_add_two_sub_three "sp_int_add_two_sub_three" + OpName %sp_int_add_two_sub_four "sp_int_add_two_sub_four" + OpName %mixed_sint_add_uint "mixed_sint_add_uint" + OpName %mixed_uint_sub_sint "mixed_uint_sub_sint" + OpName %mixed_spec_sint_add_const_uint "mixed_spec_sint_add_const_uint" + OpName %mixed_const_sint_mul_spec_uint "mixed_const_sint_mul_spec_uint" + OpName %sp_sint_mul_two "sp_sint_mul_two" + OpName %sp_uint_mul_two "sp_uint_mul_two" + OpName %sp_sint_mul_two_div_five "sp_sint_mul_two_div_five" + OpName %sp_uint_mul_two_div_five "sp_uint_mul_two_div_five" + OpName %sp_sint_mul_three_div_five "sp_sint_mul_three_div_five" + OpName %sp_sint_rem_four "sp_sint_rem_four" + OpName %sp_uint_rem_four "sp_uint_rem_four" + OpName %mixed_sint_mod_uint "mixed_sint_mod_uint" + OpName %mixed_uint_mod_sint "mixed_uint_mod_sint" + OpName %mixed_sint_smod_const_int "mixed_sint_smod_const_int" + OpName %mixed_uint_mod_const_uint "mixed_uint_mod_const_uint" + OpName %sp_sint_shift_right_arithmetic "sp_sint_shift_right_arithmetic" + OpName %sp_uint_shift_right_arithmetic "sp_uint_shift_right_arithmetic" + OpName %sp_sint_shift_left "sp_sint_shift_left" + OpName %sp_uint_shift_left "sp_uint_shift_left" + OpName %sp_sint_and_3 "sp_sint_and_3" + OpName %sp_sint_or_256 "sp_sint_or_256" + OpName %sp_uint_xor_512 "sp_uint_xor_512" + OpName %mixed_sint_and_uint "mixed_sint_and_uint" + OpName %mixed_uint_or_sint "mixed_uint_or_sint" + OpName %mixed_sint_xor_uint "mixed_sint_xor_uint" + OpName %mixed_spec_uint_and_const_sint "mixed_spec_uint_and_const_sint" + OpName %mixed_const_uint_or_spec_sint "mixed_const_uint_or_spec_sint" + OpName %sp_int_lt_sp_sint "sp_int_lt_sp_sint" + OpName %sp_int_le_sp_sint "sp_int_le_sp_sint" + OpName %sp_uint_equal_sp_uint "sp_uint_equal_sp_uint" + OpName %sp_uint_not_equal_sp_uint "sp_uint_not_equal_sp_uint" + OpName %sp_int_gt_sp_sint "sp_int_gt_sp_sint" + OpName %sp_int_ge_sp_sint "sp_int_ge_sp_sint" + OpName %mixed_sint_lt_uint "mixed_sint_lt_uint" + OpName %mixed_uint_ge_sint "mixed_uint_ge_sint" + OpName %mixed_sint_eq_uint "mixed_sint_eq_uint" + OpName %logical_and "logical_and" + OpName %logical_or "logical_or" + OpName %logical_equal "logical_equal" + OpName %logical_not_equal "logical_not_equal" + OpName %c "c" + OpName %t1 "t1" + OpName %a "a" + OpName %t2 "t2" + OpName %t3 "t3" + OpName %b "b" + OpName %t4 "t4" + OpName %sp_float "sp_float" + OpDecorate %OutputBuffer BufferBlock + OpMemberDecorate %OutputBuffer 0 Offset 0 + OpMemberDecorate %OutputBuffer 1 Offset 4 + OpMemberDecorate %OutputBuffer 2 Offset 8 + OpMemberDecorate %OutputBuffer 3 Offset 12 + OpMemberDecorate %OutputBuffer 4 Offset 16 + OpMemberDecorate %OutputBuffer 5 Offset 20 + OpMemberDecorate %OutputBuffer 6 Offset 24 + OpMemberDecorate %OutputBuffer 7 Offset 28 + OpMemberDecorate %OutputBuffer 8 Offset 32 + OpMemberDecorate %OutputBuffer 9 Offset 36 + OpMemberDecorate %OutputBuffer 10 Offset 40 + OpMemberDecorate %OutputBuffer 11 Offset 44 + OpMemberDecorate %OutputBuffer 12 Offset 48 + OpMemberDecorate %OutputBuffer 13 Offset 52 + OpMemberDecorate %OutputBuffer 14 Offset 56 + OpMemberDecorate %OutputBuffer 15 Offset 60 + OpMemberDecorate %OutputBuffer 16 Offset 64 + OpMemberDecorate %OutputBuffer 17 Offset 68 + OpMemberDecorate %OutputBuffer 18 Offset 72 + OpMemberDecorate %OutputBuffer 19 Offset 76 + OpMemberDecorate %OutputBuffer 20 Offset 80 + OpMemberDecorate %OutputBuffer 21 Offset 84 + OpMemberDecorate %OutputBuffer 22 Offset 88 + OpMemberDecorate %OutputBuffer 23 Offset 92 + OpMemberDecorate %OutputBuffer 24 Offset 96 + OpMemberDecorate %OutputBuffer 25 Offset 100 + OpMemberDecorate %OutputBuffer 26 Offset 104 + OpMemberDecorate %OutputBuffer 27 Offset 108 + OpMemberDecorate %OutputBuffer 28 Offset 112 + OpMemberDecorate %OutputBuffer 29 Offset 116 + OpMemberDecorate %OutputBuffer 30 Offset 120 + OpMemberDecorate %OutputBuffer 31 Offset 124 + OpMemberDecorate %OutputBuffer 32 Offset 128 + OpMemberDecorate %OutputBuffer 33 Offset 132 + OpMemberDecorate %OutputBuffer 34 Offset 136 + OpMemberDecorate %OutputBuffer 35 Offset 140 + OpMemberDecorate %OutputBuffer 36 Offset 144 + OpMemberDecorate %OutputBuffer 37 Offset 148 + OpMemberDecorate %OutputBuffer 38 Offset 152 + OpMemberDecorate %OutputBuffer 39 Offset 156 + OpMemberDecorate %OutputBuffer 40 Offset 160 + OpMemberDecorate %OutputBuffer 41 Offset 164 + OpMemberDecorate %OutputBuffer 42 Offset 168 + OpMemberDecorate %OutputBuffer 43 Offset 172 + OpMemberDecorate %OutputBuffer 44 Offset 176 + OpMemberDecorate %OutputBuffer 45 Offset 180 + OpMemberDecorate %OutputBuffer 46 Offset 184 + OpMemberDecorate %OutputBuffer 47 Offset 188 + OpMemberDecorate %OutputBuffer 48 Offset 192 + OpMemberDecorate %OutputBuffer 49 Offset 196 + OpMemberDecorate %OutputBuffer 50 Offset 200 + OpMemberDecorate %OutputBuffer 51 Offset 204 + OpMemberDecorate %OutputBuffer 52 Offset 208 + OpMemberDecorate %OutputBuffer 53 Offset 212 + OpMemberDecorate %OutputBuffer 54 Offset 216 + OpDecorate %output_buffer Binding 0 + OpDecorate %output_buffer DescriptorSet 0 + OpDecorate %sp_int SpecId 1 + OpDecorate %sp_uint SpecId 2 + OpDecorate %sp_sint SpecId 3 + OpDecorate %c SpecId 6 + OpDecorate %a SpecId 4 + OpDecorate %b SpecId 5 + OpDecorate %sp_float SpecId 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %int = OpTypeInt 32 1 +%OutputBuffer = OpTypeStruct %uint %uint %int %uint %uint %int %int %int %int %int %int %uint %uint %uint %uint %int %uint %int %uint %int %int %uint %uint %uint %int %uint %int %uint %int %uint %int %int %uint %uint %uint %uint %uint %uint %uint %uint %uint %uint %uint %uint %uint %uint %uint %uint %uint %uint %uint %int %int %int %int +%_ptr_Uniform_OutputBuffer = OpTypePointer Uniform %OutputBuffer +%output_buffer = OpVariable %_ptr_Uniform_OutputBuffer Uniform + %int_0 = OpConstant %int 0 + %sp_int = OpSpecConstant %int 0 + %bool = OpTypeBool + %uint_0 = OpConstant %uint 0 +%bool_from_int = OpSpecConstantOp %bool INotEqual %sp_int %uint_0 + %uint_1 = OpConstant %uint 1 + %17 = OpSpecConstantOp %uint Select %bool_from_int %uint_1 %uint_0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint + %int_1 = OpConstant %int 1 + %sp_uint = OpSpecConstant %uint 0 +%bool_from_uint = OpSpecConstantOp %bool INotEqual %sp_uint %uint_0 + %23 = OpSpecConstantOp %uint Select %bool_from_uint %uint_1 %uint_0 + %int_2 = OpConstant %int 2 +%int_from_bool = OpSpecConstantOp %int Select %bool_from_int %int_1 %int_0 +%_ptr_Uniform_int = OpTypePointer Uniform %int + %int_3 = OpConstant %int 3 +%uint_from_bool = OpSpecConstantOp %uint Select %bool_from_int %uint_1 %uint_0 + %int_4 = OpConstant %int 4 + %sp_sint = OpSpecConstant %int 0 +%sp_uint_from_sint = OpSpecConstantOp %uint IAdd %sp_sint %uint_0 + %int_5 = OpConstant %int 5 +%sp_sint_from_uint = OpSpecConstantOp %int IAdd %sp_uint %uint_0 + %int_6 = OpConstant %int 6 + %negate_int = OpSpecConstantOp %int SNegate %sp_int + %int_7 = OpConstant %int 7 + %not_int = OpSpecConstantOp %int Not %sp_int + %int_8 = OpConstant %int 8 +%sp_int_add_two = OpSpecConstantOp %int IAdd %sp_int %int_2 + %int_9 = OpConstant %int 9 + %49 = OpSpecConstantOp %int IAdd %sp_int %int_2 +%sp_int_add_two_sub_three = OpSpecConstantOp %int ISub %49 %int_3 + %int_10 = OpConstant %int 10 +%sp_int_add_two_sub_four = OpSpecConstantOp %int ISub %sp_int_add_two %int_4 + %int_11 = OpConstant %int 11 + %56 = OpSpecConstantOp %uint IAdd %sp_sint %uint_0 +%mixed_sint_add_uint = OpSpecConstantOp %uint IAdd %56 %sp_uint + %int_12 = OpConstant %int 12 + %60 = OpSpecConstantOp %uint IAdd %sp_sint %uint_0 +%mixed_uint_sub_sint = OpSpecConstantOp %uint ISub %sp_uint %60 + %int_13 = OpConstant %int 13 + %64 = OpSpecConstantOp %uint IAdd %sp_sint %uint_0 + %uint_10 = OpConstant %uint 10 +%mixed_spec_sint_add_const_uint = OpSpecConstantOp %uint IAdd %64 %uint_10 + %int_14 = OpConstant %int 14 + %uint_3 = OpConstant %uint 3 +%mixed_const_sint_mul_spec_uint = OpSpecConstantOp %uint IMul %uint_3 %sp_uint + %int_15 = OpConstant %int 15 +%sp_sint_mul_two = OpSpecConstantOp %int IMul %sp_sint %int_2 + %int_16 = OpConstant %int 16 + %uint_2 = OpConstant %uint 2 +%sp_uint_mul_two = OpSpecConstantOp %uint IMul %sp_uint %uint_2 + %int_17 = OpConstant %int 17 +%sp_sint_mul_two_div_five = OpSpecConstantOp %int SDiv %sp_sint_mul_two %int_5 + %int_18 = OpConstant %int 18 + %uint_5 = OpConstant %uint 5 +%sp_uint_mul_two_div_five = OpSpecConstantOp %uint UDiv %sp_uint_mul_two %uint_5 + %int_19 = OpConstant %int 19 + %87 = OpSpecConstantOp %int IMul %sp_sint %int_3 +%sp_sint_mul_three_div_five = OpSpecConstantOp %int SDiv %87 %int_5 + %int_20 = OpConstant %int 20 +%sp_sint_rem_four = OpSpecConstantOp %int SMod %sp_sint %int_4 + %int_21 = OpConstant %int 21 + %uint_4 = OpConstant %uint 4 +%sp_uint_rem_four = OpSpecConstantOp %uint UMod %sp_uint %uint_4 + %int_22 = OpConstant %int 22 + %98 = OpSpecConstantOp %uint IAdd %sp_sint %uint_0 +%mixed_sint_mod_uint = OpSpecConstantOp %uint UMod %98 %sp_uint + %int_23 = OpConstant %int 23 + %102 = OpSpecConstantOp %uint IAdd %sp_sint %uint_0 +%mixed_uint_mod_sint = OpSpecConstantOp %uint UMod %sp_uint %102 + %int_24 = OpConstant %int 24 +%mixed_sint_smod_const_int = OpSpecConstantOp %int SMod %sp_sint %int_3 + %int_25 = OpConstant %int 25 + %uint_7 = OpConstant %uint 7 +%mixed_uint_mod_const_uint = OpSpecConstantOp %uint UMod %sp_uint %uint_7 + %int_26 = OpConstant %int 26 +%sp_sint_shift_right_arithmetic = OpSpecConstantOp %int ShiftRightArithmetic %sp_sint %int_10 + %int_27 = OpConstant %int 27 + %uint_20 = OpConstant %uint 20 +%sp_uint_shift_right_arithmetic = OpSpecConstantOp %uint ShiftRightLogical %sp_uint %uint_20 + %int_28 = OpConstant %int 28 +%sp_sint_shift_left = OpSpecConstantOp %int ShiftLeftLogical %sp_sint %uint_1 + %int_29 = OpConstant %int 29 +%sp_uint_shift_left = OpSpecConstantOp %uint ShiftLeftLogical %sp_uint %int_2 + %int_30 = OpConstant %int 30 +%sp_sint_and_3 = OpSpecConstantOp %int BitwiseAnd %sp_sint %int_3 + %int_31 = OpConstant %int 31 + %int_256 = OpConstant %int 256 +%sp_sint_or_256 = OpSpecConstantOp %int BitwiseOr %sp_sint %int_256 + %int_32 = OpConstant %int 32 + %uint_512 = OpConstant %uint 512 +%sp_uint_xor_512 = OpSpecConstantOp %uint BitwiseXor %sp_uint %uint_512 + %int_33 = OpConstant %int 33 + %137 = OpSpecConstantOp %uint IAdd %sp_sint %uint_0 +%mixed_sint_and_uint = OpSpecConstantOp %uint BitwiseAnd %137 %sp_uint + %int_34 = OpConstant %int 34 + %141 = OpSpecConstantOp %uint IAdd %sp_sint %uint_0 +%mixed_uint_or_sint = OpSpecConstantOp %uint BitwiseOr %sp_uint %141 + %int_35 = OpConstant %int 35 + %145 = OpSpecConstantOp %uint IAdd %sp_sint %uint_0 +%mixed_sint_xor_uint = OpSpecConstantOp %uint BitwiseXor %145 %sp_uint + %int_36 = OpConstant %int 36 + %uint_15 = OpConstant %uint 15 +%mixed_spec_uint_and_const_sint = OpSpecConstantOp %uint BitwiseAnd %sp_uint %uint_15 + %int_37 = OpConstant %int 37 + %uint_128 = OpConstant %uint 128 + %154 = OpSpecConstantOp %uint IAdd %sp_sint %uint_0 +%mixed_const_uint_or_spec_sint = OpSpecConstantOp %uint BitwiseOr %uint_128 %154 + %int_38 = OpConstant %int 38 +%sp_int_lt_sp_sint = OpSpecConstantOp %bool SLessThan %sp_int %sp_sint + %159 = OpSpecConstantOp %uint Select %sp_int_lt_sp_sint %uint_1 %uint_0 + %int_39 = OpConstant %int 39 +%sp_int_le_sp_sint = OpSpecConstantOp %bool SLessThanEqual %sp_int %sp_sint + %163 = OpSpecConstantOp %uint Select %sp_int_le_sp_sint %uint_1 %uint_0 + %int_40 = OpConstant %int 40 +%sp_uint_equal_sp_uint = OpSpecConstantOp %bool IEqual %sp_uint %sp_uint + %167 = OpSpecConstantOp %uint Select %sp_uint_equal_sp_uint %uint_1 %uint_0 + %int_41 = OpConstant %int 41 +%sp_uint_not_equal_sp_uint = OpSpecConstantOp %bool INotEqual %sp_uint %sp_uint + %171 = OpSpecConstantOp %uint Select %sp_uint_not_equal_sp_uint %uint_1 %uint_0 + %int_42 = OpConstant %int 42 +%sp_int_gt_sp_sint = OpSpecConstantOp %bool SGreaterThan %sp_int %sp_sint + %175 = OpSpecConstantOp %uint Select %sp_int_gt_sp_sint %uint_1 %uint_0 + %int_43 = OpConstant %int 43 +%sp_int_ge_sp_sint = OpSpecConstantOp %bool SGreaterThanEqual %sp_int %sp_sint + %179 = OpSpecConstantOp %uint Select %sp_int_ge_sp_sint %uint_1 %uint_0 + %int_44 = OpConstant %int 44 + %182 = OpSpecConstantOp %uint IAdd %sp_sint %uint_0 +%mixed_sint_lt_uint = OpSpecConstantOp %bool ULessThan %182 %sp_uint + %184 = OpSpecConstantOp %uint Select %mixed_sint_lt_uint %uint_1 %uint_0 + %int_45 = OpConstant %int 45 + %187 = OpSpecConstantOp %uint IAdd %sp_sint %uint_0 +%mixed_uint_ge_sint = OpSpecConstantOp %bool UGreaterThanEqual %sp_uint %187 + %189 = OpSpecConstantOp %uint Select %mixed_uint_ge_sint %uint_1 %uint_0 + %int_46 = OpConstant %int 46 + %192 = OpSpecConstantOp %uint IAdd %sp_sint %uint_0 +%mixed_sint_eq_uint = OpSpecConstantOp %bool IEqual %192 %sp_uint + %194 = OpSpecConstantOp %uint Select %mixed_sint_eq_uint %uint_1 %uint_0 + %int_47 = OpConstant %int 47 +%logical_and = OpSpecConstantOp %bool LogicalAnd %bool_from_int %bool_from_uint + %198 = OpSpecConstantOp %uint Select %logical_and %uint_1 %uint_0 + %int_48 = OpConstant %int 48 + %logical_or = OpSpecConstantOp %bool LogicalOr %bool_from_int %bool_from_uint + %202 = OpSpecConstantOp %uint Select %logical_or %uint_1 %uint_0 + %int_49 = OpConstant %int 49 +%logical_equal = OpSpecConstantOp %bool LogicalEqual %bool_from_int %bool_from_uint + %206 = OpSpecConstantOp %uint Select %logical_equal %uint_1 %uint_0 + %int_50 = OpConstant %int 50 +%logical_not_equal = OpSpecConstantOp %bool LogicalNotEqual %bool_from_int %bool_from_uint + %210 = OpSpecConstantOp %uint Select %logical_not_equal %uint_1 %uint_0 + %int_51 = OpConstant %int 51 + %c = OpSpecConstantFalse %bool + %t1 = OpSpecConstantOp %int Select %c %int_13 %int_17 + %int_52 = OpConstant %int 52 + %a = OpSpecConstant %int 0 + %t2 = OpSpecConstantOp %int Select %c %a %int_17 + %int_53 = OpConstant %int 53 + %true = OpConstantTrue %bool + %t3 = OpSpecConstantOp %int Select %true %a %int_17 + %int_54 = OpConstant %int 54 + %b = OpSpecConstant %int 0 + %226 = OpSpecConstantOp %bool SGreaterThan %a %b + %227 = OpSpecConstantOp %int IAdd %int_13 %a + %228 = OpSpecConstantOp %int IMul %int_17 %b + %t4 = OpSpecConstantOp %int Select %226 %227 %228 + %float = OpTypeFloat 32 + %sp_float = OpSpecConstant %float 0 + %main = OpFunction %void None %3 + %5 = OpLabel + %19 = OpAccessChain %_ptr_Uniform_uint %output_buffer %int_0 + OpStore %19 %17 + %24 = OpAccessChain %_ptr_Uniform_uint %output_buffer %int_1 + OpStore %24 %23 + %28 = OpAccessChain %_ptr_Uniform_int %output_buffer %int_2 + OpStore %28 %int_from_bool + %31 = OpAccessChain %_ptr_Uniform_uint %output_buffer %int_3 + OpStore %31 %uint_from_bool + %35 = OpAccessChain %_ptr_Uniform_uint %output_buffer %int_4 + OpStore %35 %sp_uint_from_sint + %38 = OpAccessChain %_ptr_Uniform_int %output_buffer %int_5 + OpStore %38 %sp_sint_from_uint + %41 = OpAccessChain %_ptr_Uniform_int %output_buffer %int_6 + OpStore %41 %negate_int + %44 = OpAccessChain %_ptr_Uniform_int %output_buffer %int_7 + OpStore %44 %not_int + %47 = OpAccessChain %_ptr_Uniform_int %output_buffer %int_8 + OpStore %47 %sp_int_add_two + %51 = OpAccessChain %_ptr_Uniform_int %output_buffer %int_9 + OpStore %51 %sp_int_add_two_sub_three + %54 = OpAccessChain %_ptr_Uniform_int %output_buffer %int_10 + OpStore %54 %sp_int_add_two_sub_four + %58 = OpAccessChain %_ptr_Uniform_uint %output_buffer %int_11 + OpStore %58 %mixed_sint_add_uint + %62 = OpAccessChain %_ptr_Uniform_uint %output_buffer %int_12 + OpStore %62 %mixed_uint_sub_sint + %67 = OpAccessChain %_ptr_Uniform_uint %output_buffer %int_13 + OpStore %67 %mixed_spec_sint_add_const_uint + %71 = OpAccessChain %_ptr_Uniform_uint %output_buffer %int_14 + OpStore %71 %mixed_const_sint_mul_spec_uint + %74 = OpAccessChain %_ptr_Uniform_int %output_buffer %int_15 + OpStore %74 %sp_sint_mul_two + %78 = OpAccessChain %_ptr_Uniform_uint %output_buffer %int_16 + OpStore %78 %sp_uint_mul_two + %81 = OpAccessChain %_ptr_Uniform_int %output_buffer %int_17 + OpStore %81 %sp_sint_mul_two_div_five + %85 = OpAccessChain %_ptr_Uniform_uint %output_buffer %int_18 + OpStore %85 %sp_uint_mul_two_div_five + %89 = OpAccessChain %_ptr_Uniform_int %output_buffer %int_19 + OpStore %89 %sp_sint_mul_three_div_five + %92 = OpAccessChain %_ptr_Uniform_int %output_buffer %int_20 + OpStore %92 %sp_sint_rem_four + %96 = OpAccessChain %_ptr_Uniform_uint %output_buffer %int_21 + OpStore %96 %sp_uint_rem_four + %100 = OpAccessChain %_ptr_Uniform_uint %output_buffer %int_22 + OpStore %100 %mixed_sint_mod_uint + %104 = OpAccessChain %_ptr_Uniform_uint %output_buffer %int_23 + OpStore %104 %mixed_uint_mod_sint + %107 = OpAccessChain %_ptr_Uniform_int %output_buffer %int_24 + OpStore %107 %mixed_sint_smod_const_int + %111 = OpAccessChain %_ptr_Uniform_uint %output_buffer %int_25 + OpStore %111 %mixed_uint_mod_const_uint + %114 = OpAccessChain %_ptr_Uniform_int %output_buffer %int_26 + OpStore %114 %sp_sint_shift_right_arithmetic + %118 = OpAccessChain %_ptr_Uniform_uint %output_buffer %int_27 + OpStore %118 %sp_uint_shift_right_arithmetic + %121 = OpAccessChain %_ptr_Uniform_int %output_buffer %int_28 + OpStore %121 %sp_sint_shift_left + %124 = OpAccessChain %_ptr_Uniform_uint %output_buffer %int_29 + OpStore %124 %sp_uint_shift_left + %127 = OpAccessChain %_ptr_Uniform_int %output_buffer %int_30 + OpStore %127 %sp_sint_and_3 + %131 = OpAccessChain %_ptr_Uniform_int %output_buffer %int_31 + OpStore %131 %sp_sint_or_256 + %135 = OpAccessChain %_ptr_Uniform_uint %output_buffer %int_32 + OpStore %135 %sp_uint_xor_512 + %139 = OpAccessChain %_ptr_Uniform_uint %output_buffer %int_33 + OpStore %139 %mixed_sint_and_uint + %143 = OpAccessChain %_ptr_Uniform_uint %output_buffer %int_34 + OpStore %143 %mixed_uint_or_sint + %147 = OpAccessChain %_ptr_Uniform_uint %output_buffer %int_35 + OpStore %147 %mixed_sint_xor_uint + %151 = OpAccessChain %_ptr_Uniform_uint %output_buffer %int_36 + OpStore %151 %mixed_spec_uint_and_const_sint + %156 = OpAccessChain %_ptr_Uniform_uint %output_buffer %int_37 + OpStore %156 %mixed_const_uint_or_spec_sint + %160 = OpAccessChain %_ptr_Uniform_uint %output_buffer %int_38 + OpStore %160 %159 + %164 = OpAccessChain %_ptr_Uniform_uint %output_buffer %int_39 + OpStore %164 %163 + %168 = OpAccessChain %_ptr_Uniform_uint %output_buffer %int_40 + OpStore %168 %167 + %172 = OpAccessChain %_ptr_Uniform_uint %output_buffer %int_41 + OpStore %172 %171 + %176 = OpAccessChain %_ptr_Uniform_uint %output_buffer %int_42 + OpStore %176 %175 + %180 = OpAccessChain %_ptr_Uniform_uint %output_buffer %int_43 + OpStore %180 %179 + %185 = OpAccessChain %_ptr_Uniform_uint %output_buffer %int_44 + OpStore %185 %184 + %190 = OpAccessChain %_ptr_Uniform_uint %output_buffer %int_45 + OpStore %190 %189 + %195 = OpAccessChain %_ptr_Uniform_uint %output_buffer %int_46 + OpStore %195 %194 + %199 = OpAccessChain %_ptr_Uniform_uint %output_buffer %int_47 + OpStore %199 %198 + %203 = OpAccessChain %_ptr_Uniform_uint %output_buffer %int_48 + OpStore %203 %202 + %207 = OpAccessChain %_ptr_Uniform_uint %output_buffer %int_49 + OpStore %207 %206 + %211 = OpAccessChain %_ptr_Uniform_uint %output_buffer %int_50 + OpStore %211 %210 + %215 = OpAccessChain %_ptr_Uniform_int %output_buffer %int_51 + OpStore %215 %t1 + %219 = OpAccessChain %_ptr_Uniform_int %output_buffer %int_52 + OpStore %219 %t2 + %223 = OpAccessChain %_ptr_Uniform_int %output_buffer %int_53 + OpStore %223 %t3 + %230 = OpAccessChain %_ptr_Uniform_int %output_buffer %int_54 + OpStore %230 %t4 + OpReturn + OpFunctionEnd diff --git a/naga/tests/in/spv/spec-constant-op.toml b/naga/tests/in/spv/spec-constant-op.toml new file mode 100644 index 0000000000..d543050284 --- /dev/null +++ b/naga/tests/in/spv/spec-constant-op.toml @@ -0,0 +1,8 @@ +pipeline_constants = { 0 = 10, 1 = 3, 2 = 20, 3 = 4, 4 = 1.0, 5 = 0.0, 6 = 10.5 } +targets = "HLSL" + +[spv-in] +adjust_coordinate_space = false + +[spv] +separate_entry_points = true diff --git a/naga/tests/out/hlsl/spv-spec-constant-op-chained.hlsl b/naga/tests/out/hlsl/spv-spec-constant-op-chained.hlsl new file mode 100644 index 0000000000..c5f4dd5bf3 --- /dev/null +++ b/naga/tests/out/hlsl/spv-spec-constant-op-chained.hlsl @@ -0,0 +1,29 @@ +struct type_2 { + uint member; + uint member_1; + uint member_2; +}; + +static const int _spec_const_op_12_ = int(13); +static const int _spec_const_op_13_ = int(39); +static const int _spec_const_op_14_ = int(26); +static const int _spec_const_op_15_ = int(13); +static const int _spec_const_op_16_ = int(130); +static const int _spec_const_op_18_ = int(129); +static const int _spec_const_op_19_ = int(387); + +RWByteAddressBuffer global : register(u0); + +void function() +{ + global.Store(0, asuint(asuint(_spec_const_op_13_))); + global.Store(4, asuint(asuint(_spec_const_op_14_))); + global.Store(8, asuint(asuint(_spec_const_op_19_))); + return; +} + +[numthreads(1, 1, 1)] +void main() +{ + function(); +} diff --git a/naga/tests/out/hlsl/spv-spec-constant-op-chained.ron b/naga/tests/out/hlsl/spv-spec-constant-op-chained.ron new file mode 100644 index 0000000000..a07b03300b --- /dev/null +++ b/naga/tests/out/hlsl/spv-spec-constant-op-chained.ron @@ -0,0 +1,12 @@ +( + vertex:[ + ], + fragment:[ + ], + compute:[ + ( + entry_point:"main", + target_profile:"cs_5_1", + ), + ], +) diff --git a/naga/tests/out/hlsl/spv-spec-constant-op.hlsl b/naga/tests/out/hlsl/spv-spec-constant-op.hlsl new file mode 100644 index 0000000000..abee6dc4d0 --- /dev/null +++ b/naga/tests/out/hlsl/spv-spec-constant-op.hlsl @@ -0,0 +1,219 @@ +struct OutputBuffer { + uint bool_from_int_val; + uint bool_from_uint_val; + int int_from_bool_val; + uint uint_from_bool_val; + uint sp_uint_from_sint_val; + int sp_sint_from_uint_val; + int negate_int_val; + int not_int_val; + int sp_int_add_two_val; + int sp_int_add_two_sub_three_val; + int sp_int_add_two_sub_four_val; + uint mixed_sint_add_uint_val; + uint mixed_uint_sub_sint_val; + uint mixed_spec_sint_add_const_uint_val; + uint mixed_const_sint_mul_spec_uint_val; + int sp_sint_mul_two_val; + uint sp_uint_mul_two_val; + int sp_sint_mul_two_div_five_val; + uint sp_uint_mul_two_div_five_val; + int sp_sint_mul_three_div_five_val; + int sp_sint_rem_four_val; + uint sp_uint_rem_four_val; + uint mixed_sint_mod_uint_val; + uint mixed_uint_mod_sint_val; + int mixed_sint_smod_const_int_val; + uint mixed_uint_mod_const_uint_val; + int sp_sint_shift_right_arithmetic_val; + uint sp_uint_shift_right_arithmetic_val; + int sp_sint_shift_left_val; + uint sp_uint_shift_left_val; + int sp_sint_and_3_val; + int sp_sint_or_256_val; + uint sp_uint_xor_512_val; + uint mixed_sint_and_uint_val; + uint mixed_uint_or_sint_val; + uint mixed_sint_xor_uint_val; + uint mixed_spec_uint_and_const_sint_val; + uint mixed_const_uint_or_spec_sint_val; + uint sp_int_lt_sp_sint_val; + uint sp_int_le_sp_sint_val; + uint sp_uint_equal_sp_uint_val; + uint sp_uint_not_equal_sp_uint_val; + uint sp_int_gt_sp_sint_val; + uint sp_int_ge_sp_sint_val; + uint mixed_sint_lt_uint_val; + uint mixed_uint_ge_sint_val; + uint mixed_sint_eq_uint_val; + uint logical_and_val; + uint logical_or_val; + uint logical_equal_val; + uint logical_not_equal_val; + int t1_val; + int t2_val; + int t3_val; + int t4_val; +}; + +static const int sp_int = int(3); +static const bool bool_from_int = true; +static const uint bool_from_int_as_uint = 1u; +static const uint sp_uint = 20u; +static const bool bool_from_uint = true; +static const uint bool_from_uint_as_uint = 1u; +static const int int_from_bool = int(1); +static const uint uint_from_bool = 1u; +static const int sp_sint = int(4); +static const uint sp_uint_from_sint = 4u; +static const int sp_sint_from_uint = int(20); +static const int negate_int = int(-3); +static const int not_int = int(-4); +static const int sp_int_add_two = int(5); +static const int _spec_const_op_89_ = int(5); +static const int sp_int_add_two_sub_three = int(2); +static const int sp_int_add_two_sub_four = int(1); +static const uint sp_sint_as_uint = 4u; +static const uint mixed_sint_add_uint = 24u; +static const uint sp_sint_as_uint_1 = 4u; +static const uint mixed_uint_sub_sint = 16u; +static const uint sp_sint_as_uint_2 = 4u; +static const uint mixed_spec_sint_add_const_uint = 14u; +static const uint mixed_const_sint_mul_spec_uint = 60u; +static const int sp_sint_mul_two = int(8); +static const uint sp_uint_mul_two = 40u; +static const int sp_sint_mul_two_div_five = int(1); +static const uint sp_uint_mul_two_div_five = 8u; +static const int _spec_const_op_107_ = int(12); +static const int sp_sint_mul_three_div_five = int(2); +static const int sp_sint_rem_four = int(0); +static const uint sp_uint_rem_four = 0u; +static const uint sp_sint_as_uint_3 = 4u; +static const uint mixed_sint_mod_uint = 4u; +static const uint sp_sint_as_uint_4 = 4u; +static const uint mixed_uint_mod_sint = 0u; +static const int mixed_sint_smod_const_int = int(1); +static const uint mixed_uint_mod_const_uint = 6u; +static const int sp_sint_shift_right_arithmetic = int(0); +static const uint sp_uint_shift_right_arithmetic = 0u; +static const int sp_sint_shift_left = int(8); +static const uint sp_uint_shift_left = 80u; +static const int sp_sint_and_3_ = int(0); +static const int sp_sint_or_256_ = int(260); +static const uint sp_uint_xor_512_ = 532u; +static const uint sp_sint_as_uint_5 = 4u; +static const uint mixed_sint_and_uint = 4u; +static const uint sp_sint_as_uint_6 = 4u; +static const uint mixed_uint_or_sint = 20u; +static const uint sp_sint_as_uint_7 = 4u; +static const uint mixed_sint_xor_uint = 16u; +static const uint mixed_spec_uint_and_const_sint = 4u; +static const uint sp_sint_as_uint_8 = 4u; +static const uint mixed_const_uint_or_spec_sint = 132u; +static const bool sp_int_lt_sp_sint = true; +static const uint sp_int_lt_sp_sint_as_uint = 1u; +static const bool sp_int_le_sp_sint = true; +static const uint sp_int_le_sp_sint_as_uint = 1u; +static const bool sp_uint_equal_sp_uint = true; +static const uint sp_uint_equal_sp_uint_as_uint = 1u; +static const bool sp_uint_not_equal_sp_uint = false; +static const uint sp_uint_not_equal_sp_uint_as_uint = 0u; +static const bool sp_int_gt_sp_sint = false; +static const uint sp_int_gt_sp_sint_as_uint = 0u; +static const bool sp_int_ge_sp_sint = false; +static const uint sp_int_ge_sp_sint_as_uint = 0u; +static const uint sp_sint_as_uint_9 = 4u; +static const bool mixed_sint_lt_uint = true; +static const uint mixed_sint_lt_uint_as_uint = 1u; +static const uint sp_sint_as_uint_10 = 4u; +static const bool mixed_uint_ge_sint = true; +static const uint mixed_uint_ge_sint_as_uint = 1u; +static const uint sp_sint_as_uint_11 = 4u; +static const bool mixed_sint_eq_uint = false; +static const uint mixed_sint_eq_uint_as_uint = 0u; +static const bool logical_and = true; +static const uint logical_and_as_uint = 1u; +static const bool logical_or = true; +static const uint logical_or_as_uint = 1u; +static const bool logical_equal = true; +static const uint logical_equal_as_uint = 1u; +static const bool logical_not_equal = false; +static const uint logical_not_equal_as_uint = 0u; +static const bool c = true; +static const int t1_ = int(13); +static const int a = int(1); +static const int t2_ = int(1); +static const int t3_ = int(1); +static const int b = int(0); +static const bool _spec_const_op_173_ = true; +static const int _spec_const_op_174_ = int(14); +static const int _spec_const_op_175_ = int(0); +static const int t4_ = int(14); + +RWByteAddressBuffer output_buffer : register(u0); + +void main_1() +{ + output_buffer.Store(0, asuint(bool_from_int_as_uint)); + output_buffer.Store(4, asuint(bool_from_uint_as_uint)); + output_buffer.Store(8, asuint(int_from_bool)); + output_buffer.Store(12, asuint(uint_from_bool)); + output_buffer.Store(16, asuint(sp_uint_from_sint)); + output_buffer.Store(20, asuint(sp_sint_from_uint)); + output_buffer.Store(24, asuint(negate_int)); + output_buffer.Store(28, asuint(not_int)); + output_buffer.Store(32, asuint(sp_int_add_two)); + output_buffer.Store(36, asuint(sp_int_add_two_sub_three)); + output_buffer.Store(40, asuint(sp_int_add_two_sub_four)); + output_buffer.Store(44, asuint(mixed_sint_add_uint)); + output_buffer.Store(48, asuint(mixed_uint_sub_sint)); + output_buffer.Store(52, asuint(mixed_spec_sint_add_const_uint)); + output_buffer.Store(56, asuint(mixed_const_sint_mul_spec_uint)); + output_buffer.Store(60, asuint(sp_sint_mul_two)); + output_buffer.Store(64, asuint(sp_uint_mul_two)); + output_buffer.Store(68, asuint(sp_sint_mul_two_div_five)); + output_buffer.Store(72, asuint(sp_uint_mul_two_div_five)); + output_buffer.Store(76, asuint(sp_sint_mul_three_div_five)); + output_buffer.Store(80, asuint(sp_sint_rem_four)); + output_buffer.Store(84, asuint(sp_uint_rem_four)); + output_buffer.Store(88, asuint(mixed_sint_mod_uint)); + output_buffer.Store(92, asuint(mixed_uint_mod_sint)); + output_buffer.Store(96, asuint(mixed_sint_smod_const_int)); + output_buffer.Store(100, asuint(mixed_uint_mod_const_uint)); + output_buffer.Store(104, asuint(sp_sint_shift_right_arithmetic)); + output_buffer.Store(108, asuint(sp_uint_shift_right_arithmetic)); + output_buffer.Store(112, asuint(sp_sint_shift_left)); + output_buffer.Store(116, asuint(sp_uint_shift_left)); + output_buffer.Store(120, asuint(sp_sint_and_3_)); + output_buffer.Store(124, asuint(sp_sint_or_256_)); + output_buffer.Store(128, asuint(sp_uint_xor_512_)); + output_buffer.Store(132, asuint(mixed_sint_and_uint)); + output_buffer.Store(136, asuint(mixed_uint_or_sint)); + output_buffer.Store(140, asuint(mixed_sint_xor_uint)); + output_buffer.Store(144, asuint(mixed_spec_uint_and_const_sint)); + output_buffer.Store(148, asuint(mixed_const_uint_or_spec_sint)); + output_buffer.Store(152, asuint(sp_int_lt_sp_sint_as_uint)); + output_buffer.Store(156, asuint(sp_int_le_sp_sint_as_uint)); + output_buffer.Store(160, asuint(sp_uint_equal_sp_uint_as_uint)); + output_buffer.Store(164, asuint(sp_uint_not_equal_sp_uint_as_uint)); + output_buffer.Store(168, asuint(sp_int_gt_sp_sint_as_uint)); + output_buffer.Store(172, asuint(sp_int_ge_sp_sint_as_uint)); + output_buffer.Store(176, asuint(mixed_sint_lt_uint_as_uint)); + output_buffer.Store(180, asuint(mixed_uint_ge_sint_as_uint)); + output_buffer.Store(184, asuint(mixed_sint_eq_uint_as_uint)); + output_buffer.Store(188, asuint(logical_and_as_uint)); + output_buffer.Store(192, asuint(logical_or_as_uint)); + output_buffer.Store(196, asuint(logical_equal_as_uint)); + output_buffer.Store(200, asuint(logical_not_equal_as_uint)); + output_buffer.Store(204, asuint(t1_)); + output_buffer.Store(208, asuint(t2_)); + output_buffer.Store(212, asuint(t3_)); + output_buffer.Store(216, asuint(t4_)); + return; +} + +[numthreads(1, 1, 1)] +void main() +{ + main_1(); +} diff --git a/naga/tests/out/hlsl/spv-spec-constant-op.ron b/naga/tests/out/hlsl/spv-spec-constant-op.ron new file mode 100644 index 0000000000..a07b03300b --- /dev/null +++ b/naga/tests/out/hlsl/spv-spec-constant-op.ron @@ -0,0 +1,12 @@ +( + vertex:[ + ], + fragment:[ + ], + compute:[ + ( + entry_point:"main", + target_profile:"cs_5_1", + ), + ], +)