diff --git a/reference/shaders-no-opt/comp/specialization-constant-evaluation.comp b/reference/shaders-no-opt/comp/specialization-constant-evaluation.comp new file mode 100644 index 00000000..69583596 --- /dev/null +++ b/reference/shaders-no-opt/comp/specialization-constant-evaluation.comp @@ -0,0 +1,321 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +#ifndef SPIRV_CROSS_CONSTANT_ID_2 +#define SPIRV_CROSS_CONSTANT_ID_2 1 +#endif +const int SONE = SPIRV_CROSS_CONSTANT_ID_2; +#ifndef SPIRV_CROSS_CONSTANT_ID_3 +#define SPIRV_CROSS_CONSTANT_ID_3 2 +#endif +const int STWO = SPIRV_CROSS_CONSTANT_ID_3; +const int _10 = (SONE + STWO); +const uint _13 = (uint(_10) + 0u); +#ifndef SPIRV_CROSS_CONSTANT_ID_5 +#define SPIRV_CROSS_CONSTANT_ID_5 1u +#endif +const uint UONE = SPIRV_CROSS_CONSTANT_ID_5; +const uint _15 = (_13 + UONE); +#ifndef SPIRV_CROSS_CONSTANT_ID_6 +#define SPIRV_CROSS_CONSTANT_ID_6 2u +#endif +const uint UTWO = SPIRV_CROSS_CONSTANT_ID_6; +const uint IADD = (_15 + UTWO); +const uint _19 = (IADD - 5u); +const uint _28 = (uint(SONE) + 0u); +const uint ISUB = (UTWO - _28); +const uint IMUL = (UTWO * UTWO); +const uint _37 = (IMUL - 3u); +const uint UDIV = (UTWO / UTWO); +#ifndef SPIRV_CROSS_CONSTANT_ID_4 +#define SPIRV_CROSS_CONSTANT_ID_4 -2 +#endif +const int SNEG_TWO = SPIRV_CROSS_CONSTANT_ID_4; +const int SDIV = (STWO / SNEG_TWO); +const int _52 = (SDIV + 2); +#ifndef SPIRV_CROSS_CONSTANT_ID_7 +#define SPIRV_CROSS_CONSTANT_ID_7 -3 +#endif +const int SNEG_THREE = SPIRV_CROSS_CONSTANT_ID_7; +const int SMOD = (STWO % SNEG_THREE); +const int _66 = (SMOD + 2); +const uint UMOD = (IADD % IMUL); +const uint _73 = (UMOD - 1u); +const uint LSHL = (IADD << ISUB); +const uint _81 = (LSHL - 11u); +const uint RSHL = (IADD >> ISUB); +const uint _89 = (RSHL - 2u); +const int _95 = int(IADD + 0u); +const int _96 = (-_95); +const int _97 = (-SDIV); +const int RSHA = (_96 >> _97); +const int _100 = (RSHA + 4); +const bool IEQ = (IADD == ISUB); +const int _109 = IEQ ? 2 : 1; +const bool INEQ = (IADD != ISUB); +const int _116 = INEQ ? 1 : 2; +const bool ULT = (IADD < ISUB); +const int _123 = ULT ? 2 : 1; +const bool ULE = (IADD <= ISUB); +const int _130 = ULE ? 2 : 1; +const bool UGT = (IADD > ISUB); +const int _137 = UGT ? 1 : 2; +const bool UGE = (IADD >= ISUB); +const int _144 = UGE ? 1 : 2; +const bool SLT = (SMOD < 1); +const int _151 = SLT ? 1 : 2; +const bool SLE = (SMOD <= 1); +const int _158 = SLE ? 1 : 2; +const bool SGT = (SMOD > 1); +const int _165 = SGT ? 2 : 1; +const bool SGE = (SMOD >= 1); +const int _172 = SGE ? 2 : 1; +const bool LOR = (IEQ || SLT); +const int _179 = LOR ? 1 : 2; +const bool LAND = (IEQ && SLT); +const int _186 = LAND ? 2 : 1; +const bool LNOT = (!LOR); +const int _193 = LNOT ? 2 : 1; +const uint AND = (IADD & IADD); +const uint _200 = (AND - 5u); +const uint OR = (IADD | ISUB); +const uint _208 = (OR - 6u); +const uint XOR = (IADD ^ IADD); +const uint _215 = (XOR + 1u); +const uint NOT = (~XOR); +const uint _223 = (NOT - 4294967294u); +const bool LEQ = (LAND == LNOT); +const int _230 = LEQ ? 1 : 2; +const bool LNEQ = (LAND != LNOT); +const int _237 = LNEQ ? 2 : 1; +const uint SEL = IEQ ? IADD : ISUB; +#ifndef SPIRV_CROSS_CONSTANT_ID_0 +#define SPIRV_CROSS_CONSTANT_ID_0 true +#endif +const bool TRUE = SPIRV_CROSS_CONSTANT_ID_0; +#ifndef SPIRV_CROSS_CONSTANT_ID_1 +#define SPIRV_CROSS_CONSTANT_ID_1 false +#endif +const bool FALSE = SPIRV_CROSS_CONSTANT_ID_1; + +layout(binding = 0, std430) buffer SSBO_IAdd +{ + float val[_19]; + float dummy; +} IAdd; + +layout(binding = 1, std430) buffer SSBO_ISub +{ + float val[ISUB]; + float dummy; +} ISub; + +layout(binding = 2, std430) buffer SSBO_IMul +{ + float val[_37]; + float dummy; +} IMul; + +layout(binding = 3, std430) buffer SSBO_UDiv +{ + float val[UDIV]; + float dummy; +} UDiv; + +layout(binding = 4, std430) buffer SSBO_SDiv +{ + float val[_52]; + float dummy; +} SDiv; + +layout(binding = 5, std430) buffer SSBO_SRem +{ + float val[1]; + float dummy; +} SRem; + +layout(binding = 6, std430) buffer SSBO_SMod +{ + float val[_66]; + float dummy; +} SMod; + +layout(binding = 7, std430) buffer SSBO_UMod +{ + float val[_73]; + float dummy; +} UMod; + +layout(binding = 8, std430) buffer SSBO_LShl +{ + float val[_81]; + float dummy; +} LShl; + +layout(binding = 9, std430) buffer SSBO_RShl +{ + float val[_89]; + float dummy; +} RShl; + +layout(binding = 10, std430) buffer SSBO_RSha +{ + float val[_100]; + float dummy; +} RSha; + +layout(binding = 11, std430) buffer SSBO_IEq +{ + float val[_109]; + float dummy; +} IEq; + +layout(binding = 12, std430) buffer SSBO_INeq +{ + float val[_116]; + float dummy; +} INeq; + +layout(binding = 13, std430) buffer SSBO_Ult +{ + float val[_123]; + float dummy; +} Ult; + +layout(binding = 14, std430) buffer SSBO_Ule +{ + float val[_130]; + float dummy; +} Ule; + +layout(binding = 15, std430) buffer SSBO_Ugt +{ + float val[_137]; + float dummy; +} Ugt; + +layout(binding = 16, std430) buffer SSBO_Uge +{ + float val[_144]; + float dummy; +} Uge; + +layout(binding = 17, std430) buffer SSBO_Slt +{ + float val[_151]; + float dummy; +} Slt; + +layout(binding = 18, std430) buffer SSBO_Sle +{ + float val[_158]; + float dummy; +} Sle; + +layout(binding = 19, std430) buffer SSBO_Sgt +{ + float val[_165]; + float dummy; +} Sgt; + +layout(binding = 20, std430) buffer SSBO_Sge +{ + float val[_172]; + float dummy; +} Sge; + +layout(binding = 21, std430) buffer SSBO_Lor +{ + float val[_179]; + float dummy; +} Lor; + +layout(binding = 22, std430) buffer SSBO_Land +{ + float val[_186]; + float dummy; +} Land; + +layout(binding = 23, std430) buffer SSBO_Lnot +{ + float val[_193]; + float dummy; +} Lnot; + +layout(binding = 24, std430) buffer SSBO_And +{ + float val[_200]; + float dummy; +} And; + +layout(binding = 24, std430) buffer SSBO_Or +{ + float val[_208]; + float dummy; +} Or; + +layout(binding = 24, std430) buffer SSBO_Xor +{ + float val[_215]; + float dummy; +} Xor; + +layout(binding = 25, std430) buffer SSBO_Not +{ + float val[_223]; + float dummy; +} Not; + +layout(binding = 26, std430) buffer SSBO_Leq +{ + float val[_230]; + float dummy; +} Leq; + +layout(binding = 27, std430) buffer SSBO_Lneq +{ + float val[_237]; + float dummy; +} Lneq; + +layout(binding = 28, std430) buffer SSBO_Sel +{ + float val[SEL]; + float dummy; +} Sel; + +void main() +{ + IAdd.val[0] = 0.0; + ISub.val[0] = 0.0; + IMul.val[0] = 0.0; + UDiv.val[0] = 0.0; + SDiv.val[0] = 0.0; + SRem.val[0] = 0.0; + SMod.val[0] = 0.0; + UMod.val[0] = 0.0; + LShl.val[0] = 0.0; + RShl.val[0] = 0.0; + RSha.val[0] = 0.0; + IEq.val[0] = 0.0; + INeq.val[0] = 0.0; + Ult.val[0] = 0.0; + Ule.val[0] = 0.0; + Ugt.val[0] = 0.0; + Uge.val[0] = 0.0; + Slt.val[0] = 0.0; + Sle.val[0] = 0.0; + Sgt.val[0] = 0.0; + Sge.val[0] = 0.0; + Lor.val[0] = 0.0; + Land.val[0] = 0.0; + Lnot.val[0] = 0.0; + And.val[0] = 0.0; + Or.val[0] = 0.0; + Xor.val[0] = 0.0; + Not.val[0] = 0.0; + Leq.val[0] = 0.0; + Lneq.val[0] = 0.0; + Sel.val[0] = 0.0; +} + diff --git a/shaders-no-opt/comp/specialization-constant-evaluation.comp b/shaders-no-opt/comp/specialization-constant-evaluation.comp new file mode 100644 index 00000000..d45d021a --- /dev/null +++ b/shaders-no-opt/comp/specialization-constant-evaluation.comp @@ -0,0 +1,123 @@ +#version 450 + +layout(local_size_x = 1) in; + +layout(constant_id = 0) const bool TRUE = true; +layout(constant_id = 1) const bool FALSE = false; +layout(constant_id = 2) const int SONE = 1; +layout(constant_id = 3) const int STWO = 2; +layout(constant_id = 4) const int SNEG_TWO = -2; +layout(constant_id = 5) const uint UONE = 1; +layout(constant_id = 6) const uint UTWO = 2; +layout(constant_id = 7) const int SNEG_THREE = -3; + +const uint IADD = SONE + STWO + UONE + UTWO; // 6 +const uint ISUB = UTWO - SONE; // 1 +const uint IMUL = UTWO * UTWO; // 4 +const uint UDIV = UTWO / UTWO; // 1 +const int SDIV = STWO / SNEG_TWO; // -1 +//const int SREM = STWO % SNEG_THREE; // 1 +const int SREM = 1; +const int SMOD = STWO % SNEG_THREE; // -1 +const uint UMOD = IADD % IMUL; // 2 + +const uint LSHL = IADD << ISUB; // 12 +const uint RSHL = IADD >> ISUB; // 3 +const int RSHA = (-int(IADD)) >> (-SDIV); // -3 + +const bool IEQ = IADD == ISUB; // false +const bool INEQ = IADD != ISUB; // true +const bool ULT = IADD < ISUB; // false +const bool ULE = IADD <= ISUB; // false +const bool UGT = IADD > ISUB; // true +const bool UGE = IADD >= ISUB; // true + +const bool SLT = SMOD < SREM; // true +const bool SLE = SMOD <= SREM; // true +const bool SGT = SMOD > SREM; // false +const bool SGE = SMOD >= SREM; // false + +const bool LOR = IEQ || SLT; // true +const bool LAND = IEQ && SLT; // false +const bool LNOT = !LOR; // false + +const uint AND = IADD & IADD; // 6 +const uint OR = IADD | ISUB; // 7 +const uint XOR = IADD ^ IADD; // 0 +const uint NOT = ~XOR; // UINT_MAX + +const bool LEQ = LAND == LNOT; // true +const bool LNEQ = LAND != LNOT; // false + +const uint SEL = IEQ ? IADD : ISUB; // 1 + +#define DUMMY_SSBO(name, bind, size) layout(std430, set = 0, binding = bind) buffer SSBO_##name { float val[size]; float dummy; } name + +// Normalize all sizes to 1 element so that the default offsets in glslang matches up with what we should be computing. +// If we do it right, we should get no layout(offset = N) expressions. +DUMMY_SSBO(IAdd, 0, IADD - 5); +DUMMY_SSBO(ISub, 1, ISUB); +DUMMY_SSBO(IMul, 2, IMUL - 3); +DUMMY_SSBO(UDiv, 3, UDIV); +DUMMY_SSBO(SDiv, 4, SDIV + 2); +DUMMY_SSBO(SRem, 5, SREM); +DUMMY_SSBO(SMod, 6, SMOD + 2); +DUMMY_SSBO(UMod, 7, UMOD - 1); +DUMMY_SSBO(LShl, 8, LSHL - 11); +DUMMY_SSBO(RShl, 9, RSHL - 2); +DUMMY_SSBO(RSha, 10, RSHA + 4); +DUMMY_SSBO(IEq, 11, IEQ ? 2 : 1); +DUMMY_SSBO(INeq, 12, INEQ ? 1 : 2); +DUMMY_SSBO(Ult, 13, ULT ? 2 : 1); +DUMMY_SSBO(Ule, 14, ULE ? 2 : 1); +DUMMY_SSBO(Ugt, 15, UGT ? 1 : 2); +DUMMY_SSBO(Uge, 16, UGE ? 1 : 2); +DUMMY_SSBO(Slt, 17, SLT ? 1 : 2); +DUMMY_SSBO(Sle, 18, SLE ? 1 : 2); +DUMMY_SSBO(Sgt, 19, SGT ? 2 : 1); +DUMMY_SSBO(Sge, 20, SGE ? 2 : 1); +DUMMY_SSBO(Lor, 21, LOR ? 1 : 2); +DUMMY_SSBO(Land, 22, LAND ? 2 : 1); +DUMMY_SSBO(Lnot, 23, LNOT ? 2 : 1); +DUMMY_SSBO(And, 24, AND - 5); +DUMMY_SSBO(Or, 24, OR - 6); +DUMMY_SSBO(Xor, 24, XOR + 1); +DUMMY_SSBO(Not, 25, NOT - 0xfffffffeu); +DUMMY_SSBO(Leq, 26, LEQ ? 1 : 2); +DUMMY_SSBO(Lneq, 27, LNEQ ? 2 : 1); +DUMMY_SSBO(Sel, 28, SEL); + +void main() +{ + IAdd.val[0] = 0.0; + ISub.val[0] = 0.0; + IMul.val[0] = 0.0; + UDiv.val[0] = 0.0; + SDiv.val[0] = 0.0; + SRem.val[0] = 0.0; + SMod.val[0] = 0.0; + UMod.val[0] = 0.0; + LShl.val[0] = 0.0; + RShl.val[0] = 0.0; + RSha.val[0] = 0.0; + IEq.val[0] = 0.0; + INeq.val[0] = 0.0; + Ult.val[0] = 0.0; + Ule.val[0] = 0.0; + Ugt.val[0] = 0.0; + Uge.val[0] = 0.0; + Slt.val[0] = 0.0; + Sle.val[0] = 0.0; + Sgt.val[0] = 0.0; + Sge.val[0] = 0.0; + Lor.val[0] = 0.0; + Land.val[0] = 0.0; + Lnot.val[0] = 0.0; + And.val[0] = 0.0; + Or.val[0] = 0.0; + Xor.val[0] = 0.0; + Not.val[0] = 0.0; + Leq.val[0] = 0.0; + Lneq.val[0] = 0.0; + Sel.val[0] = 0.0; +} diff --git a/spirv_cross.cpp b/spirv_cross.cpp index fe91c0d8..aa276572 100644 --- a/spirv_cross.cpp +++ b/spirv_cross.cpp @@ -1652,6 +1652,148 @@ size_t Compiler::get_declared_struct_size_runtime_array(const SPIRType &type, si return size; } +uint32_t Compiler::evaluate_spec_constant_u32(const SPIRConstantOp &spec) const +{ + auto &result_type = get(spec.basetype); + if (result_type.basetype != SPIRType::UInt && result_type.basetype != SPIRType::Int && result_type.basetype != SPIRType::Boolean) + SPIRV_CROSS_THROW("Only 32-bit integers and booleans are currently supported when evaluating specialization constants.\n"); + if (!is_scalar(result_type)) + SPIRV_CROSS_THROW("Spec constant evaluation must be a scalar.\n"); + + uint32_t value = 0; + + const auto eval_u32 = [&](uint32_t id) -> uint32_t { + auto &type = expression_type(id); + if (type.basetype != SPIRType::UInt && type.basetype != SPIRType::Int && type.basetype != SPIRType::Boolean) + SPIRV_CROSS_THROW("Only 32-bit integers and booleans are currently supported when evaluating specialization constants.\n"); + if (!is_scalar(type)) + SPIRV_CROSS_THROW("Spec constant evaluation must be a scalar.\n"); + if (const auto *c = this->maybe_get(id)) + return c->scalar(); + else + return evaluate_spec_constant_u32(this->get(id)); + }; + +#define binary_spec_op(op, binary_op) \ + case Op##op: value = eval_u32(spec.arguments[0]) binary_op eval_u32(spec.arguments[1]); break +#define binary_spec_op_cast(op, binary_op, type) \ + case Op##op: value = uint32_t(type(eval_u32(spec.arguments[0])) binary_op type(eval_u32(spec.arguments[1]))); break + + // Support the basic opcodes which are typically used when computing array sizes. + switch (spec.opcode) + { + binary_spec_op(IAdd, +); + binary_spec_op(ISub, -); + binary_spec_op(IMul, *); + binary_spec_op(BitwiseAnd, &); + binary_spec_op(BitwiseOr, |); + binary_spec_op(BitwiseXor, ^); + binary_spec_op(LogicalAnd, &); + binary_spec_op(LogicalOr, |); + binary_spec_op(ShiftLeftLogical, <<); + binary_spec_op(ShiftRightLogical, >>); + binary_spec_op_cast(ShiftRightArithmetic, >>, int32_t); + binary_spec_op(LogicalEqual, ==); + binary_spec_op(LogicalNotEqual, !=); + binary_spec_op(IEqual, ==); + binary_spec_op(INotEqual, !=); + binary_spec_op(ULessThan, <); + binary_spec_op(ULessThanEqual, <=); + binary_spec_op(UGreaterThan, >); + binary_spec_op(UGreaterThanEqual, >=); + binary_spec_op_cast(SLessThan, <, int32_t); + binary_spec_op_cast(SLessThanEqual, <=, int32_t); + binary_spec_op_cast(SGreaterThan, >, int32_t); + binary_spec_op_cast(SGreaterThanEqual, >=, int32_t); +#undef binary_spec_op +#undef binary_spec_op_cast + + case OpLogicalNot: + value = uint32_t(!eval_u32(spec.arguments[0])); + break; + + case OpNot: + value = ~eval_u32(spec.arguments[0]); + break; + + case OpSNegate: + value = -eval_u32(spec.arguments[0]); + break; + + case OpSelect: + value = eval_u32(spec.arguments[0]) ? eval_u32(spec.arguments[1]) : eval_u32(spec.arguments[2]); + break; + + case OpUMod: + { + uint32_t a = eval_u32(spec.arguments[0]); + uint32_t b = eval_u32(spec.arguments[1]); + if (b == 0) + SPIRV_CROSS_THROW("Undefined behavior in UMod, b == 0.\n"); + value = a % b; + break; + } + + case OpSRem: + { + auto a = int32_t(eval_u32(spec.arguments[0])); + auto b = int32_t(eval_u32(spec.arguments[1])); + if (b == 0) + SPIRV_CROSS_THROW("Undefined behavior in SRem, b == 0.\n"); + value = a % b; + break; + } + + case OpSMod: + { + auto a = int32_t(eval_u32(spec.arguments[0])); + auto b = int32_t(eval_u32(spec.arguments[1])); + if (b == 0) + SPIRV_CROSS_THROW("Undefined behavior in SMod, b == 0.\n"); + auto v = a % b; + + // Makes sure we match the sign of b, not a. + if ((b < 0 && v > 0) || (b > 0 && v < 0)) + v += b; + value = v; + break; + } + + case OpUDiv: + { + uint32_t a = eval_u32(spec.arguments[0]); + uint32_t b = eval_u32(spec.arguments[1]); + if (b == 0) + SPIRV_CROSS_THROW("Undefined behavior in UDiv, b == 0.\n"); + value = a / b; + break; + } + + case OpSDiv: + { + auto a = int32_t(eval_u32(spec.arguments[0])); + auto b = int32_t(eval_u32(spec.arguments[1])); + if (b == 0) + SPIRV_CROSS_THROW("Undefined behavior in SDiv, b == 0.\n"); + value = a / b; + break; + } + + default: + SPIRV_CROSS_THROW("Unsupported spec constant opcode for evaluation.\n"); + } + + return value; +} + +uint32_t Compiler::evaluate_constant_u32(uint32_t id) const +{ + if (const auto *c = maybe_get(id)) + return c->scalar(); + else + return evaluate_spec_constant_u32(get(id)); +} + size_t Compiler::get_declared_struct_member_size(const SPIRType &struct_type, uint32_t index) const { if (struct_type.member_types.empty()) @@ -1686,7 +1828,7 @@ size_t Compiler::get_declared_struct_member_size(const SPIRType &struct_type, ui { // For arrays, we can use ArrayStride to get an easy check. bool array_size_literal = type.array_size_literal.back(); - uint32_t array_size = array_size_literal ? type.array.back() : get(type.array.back()).scalar(); + uint32_t array_size = array_size_literal ? type.array.back() : evaluate_constant_u32(type.array.back()); return type_struct_member_array_stride(struct_type, index) * array_size; } else if (type.basetype == SPIRType::Struct) diff --git a/spirv_cross.hpp b/spirv_cross.hpp index 17c08188..a502da11 100644 --- a/spirv_cross.hpp +++ b/spirv_cross.hpp @@ -1060,6 +1060,9 @@ protected: bool flush_phi_required(BlockID from, BlockID to) const; + uint32_t evaluate_spec_constant_u32(const SPIRConstantOp &spec) const; + uint32_t evaluate_constant_u32(uint32_t id) const; + private: // Used only to implement the old deprecated get_entry_point() interface. const SPIREntryPoint &get_first_entry_point(const std::string &name) const; diff --git a/spirv_glsl.cpp b/spirv_glsl.cpp index 3cba193f..23f4f30f 100644 --- a/spirv_glsl.cpp +++ b/spirv_glsl.cpp @@ -6755,7 +6755,7 @@ void CompilerGLSL::emit_subgroup_op(const Instruction &i) uint32_t result_type = ops[0]; uint32_t id = ops[1]; - auto scope = static_cast(get(ops[2]).scalar()); + auto scope = static_cast(evaluate_constant_u32(ops[2])); if (scope != ScopeSubgroup) SPIRV_CROSS_THROW("Only subgroup scope is supported."); @@ -6889,7 +6889,7 @@ case OpGroupNonUniform##op: \ case OpGroupNonUniformQuadSwap: { - uint32_t direction = get(ops[4]).scalar(); + uint32_t direction = evaluate_constant_u32(ops[4]); if (direction == 0) emit_unary_func_op(result_type, id, ops[3], "subgroupQuadSwapHorizontal"); else if (direction == 1) @@ -7635,7 +7635,7 @@ string CompilerGLSL::access_chain_internal(uint32_t base, const uint32_t *indice else if (type->basetype == SPIRType::Struct) { if (!is_literal) - index = get(index).scalar(); + index = evaluate_constant_u32(index); if (index >= type->member_types.size()) SPIRV_CROSS_THROW("Member index is out of bounds!"); @@ -8156,7 +8156,7 @@ std::pair CompilerGLSL::flattened_access_chain_offset( // We also check if this member is a builtin, since we then replace the entire expression with the builtin one. else if (type->basetype == SPIRType::Struct) { - index = get(index).scalar(); + index = evaluate_constant_u32(index); if (index >= type->member_types.size()) SPIRV_CROSS_THROW("Member index is out of bounds!"); @@ -8184,7 +8184,7 @@ std::pair CompilerGLSL::flattened_access_chain_offset( auto *constant = maybe_get(index); if (constant) { - index = get(index).scalar(); + index = evaluate_constant_u32(index); offset += index * (row_major_matrix_needs_conversion ? (type->width / 8) : matrix_stride); } else @@ -8213,7 +8213,7 @@ std::pair CompilerGLSL::flattened_access_chain_offset( auto *constant = maybe_get(index); if (constant) { - index = get(index).scalar(); + index = evaluate_constant_u32(index); offset += index * (row_major_matrix_needs_conversion ? matrix_stride : (type->width / 8)); } else @@ -10805,14 +10805,14 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) if (opcode == OpMemoryBarrier) { - memory = get(ops[0]).scalar(); - semantics = get(ops[1]).scalar(); + memory = evaluate_constant_u32(ops[0]); + semantics = evaluate_constant_u32(ops[1]); } else { - execution_scope = get(ops[0]).scalar(); - memory = get(ops[1]).scalar(); - semantics = get(ops[2]).scalar(); + execution_scope = evaluate_constant_u32(ops[0]); + memory = evaluate_constant_u32(ops[1]); + semantics = evaluate_constant_u32(ops[2]); } if (execution_scope == ScopeSubgroup || memory == ScopeSubgroup) @@ -10841,8 +10841,8 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction) if (next && next->op == OpControlBarrier) { auto *next_ops = stream(*next); - uint32_t next_memory = get(next_ops[1]).scalar(); - uint32_t next_semantics = get(next_ops[2]).scalar(); + uint32_t next_memory = evaluate_constant_u32(next_ops[1]); + uint32_t next_semantics = evaluate_constant_u32(next_ops[2]); next_semantics = mask_relevant_memory_semantics(next_semantics); bool memory_scope_covered = false; @@ -11795,15 +11795,7 @@ uint32_t CompilerGLSL::to_array_size_literal(const SPIRType &type, uint32_t inde { // Use the default spec constant value. // This is the best we can do. - uint32_t array_size_id = type.array[index]; - - // Explicitly check for this case. The error message you would get (bad cast) makes no sense otherwise. - if (ir.ids[array_size_id].get_type() == TypeConstantOp) - SPIRV_CROSS_THROW("An array size was found to be an OpSpecConstantOp. This is not supported since " - "SPIRV-Cross cannot deduce the actual size here."); - - uint32_t array_size = get(array_size_id).scalar(); - return array_size; + return evaluate_constant_u32(type.array[index]); } } diff --git a/spirv_hlsl.cpp b/spirv_hlsl.cpp index 50eb5aea..070a83e5 100644 --- a/spirv_hlsl.cpp +++ b/spirv_hlsl.cpp @@ -790,7 +790,7 @@ uint32_t CompilerHLSL::type_to_consumed_locations(const SPIRType &type) const if (type.array_size_literal[i]) array_multiplier *= type.array[i]; else - array_multiplier *= get(type.array[i]).scalar(); + array_multiplier *= evaluate_constant_u32(type.array[i]); } elements += array_multiplier * type.columns; } @@ -2860,7 +2860,7 @@ void CompilerHLSL::emit_texture_op(const Instruction &i, bool sparse) } else if (gather) { - uint32_t comp_num = get(comp).scalar(); + uint32_t comp_num = evaluate_constant_u32(comp); if (hlsl_options.shader_model >= 50) { switch (comp_num) @@ -4454,7 +4454,7 @@ void CompilerHLSL::emit_subgroup_op(const Instruction &i) uint32_t result_type = ops[0]; uint32_t id = ops[1]; - auto scope = static_cast(get(ops[2]).scalar()); + auto scope = static_cast(evaluate_constant_u32(ops[2])); if (scope != ScopeSubgroup) SPIRV_CROSS_THROW("Only subgroup scope is supported."); @@ -4611,7 +4611,7 @@ case OpGroupNonUniform##op: \ case OpGroupNonUniformQuadSwap: { - uint32_t direction = get(ops[4]).scalar(); + uint32_t direction = evaluate_constant_u32(ops[4]); if (direction == 0) emit_unary_func_op(result_type, id, ops[3], "QuadReadAcrossX"); else if (direction == 1) @@ -5269,13 +5269,13 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction) if (opcode == OpMemoryBarrier) { - memory = get(ops[0]).scalar(); - semantics = get(ops[1]).scalar(); + memory = evaluate_constant_u32(ops[0]); + semantics = evaluate_constant_u32(ops[1]); } else { - memory = get(ops[1]).scalar(); - semantics = get(ops[2]).scalar(); + memory = evaluate_constant_u32(ops[1]); + semantics = evaluate_constant_u32(ops[2]); } if (memory == ScopeSubgroup) @@ -5295,8 +5295,8 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction) if (next && next->op == OpControlBarrier) { auto *next_ops = stream(*next); - uint32_t next_memory = get(next_ops[1]).scalar(); - uint32_t next_semantics = get(next_ops[2]).scalar(); + uint32_t next_memory = evaluate_constant_u32(next_ops[1]); + uint32_t next_semantics = evaluate_constant_u32(next_ops[2]); next_semantics = mask_relevant_memory_semantics(next_semantics); // There is no "just execution barrier" in HLSL. diff --git a/spirv_msl.cpp b/spirv_msl.cpp index 87891f4a..0aff6e7f 100644 --- a/spirv_msl.cpp +++ b/spirv_msl.cpp @@ -7175,8 +7175,8 @@ void CompilerMSL::emit_barrier(uint32_t id_exe_scope, uint32_t id_mem_scope, uin if (get_execution_model() != ExecutionModelGLCompute && get_execution_model() != ExecutionModelTessellationControl) return; - uint32_t exe_scope = id_exe_scope ? get(id_exe_scope).scalar() : uint32_t(ScopeInvocation); - uint32_t mem_scope = id_mem_scope ? get(id_mem_scope).scalar() : uint32_t(ScopeInvocation); + uint32_t exe_scope = id_exe_scope ? evaluate_constant_u32(id_exe_scope) : uint32_t(ScopeInvocation); + uint32_t mem_scope = id_mem_scope ? evaluate_constant_u32(id_mem_scope) : uint32_t(ScopeInvocation); // Use the wider of the two scopes (smaller value) exe_scope = min(exe_scope, mem_scope); @@ -7187,7 +7187,7 @@ void CompilerMSL::emit_barrier(uint32_t id_exe_scope, uint32_t id_mem_scope, uin bar_stmt = "threadgroup_barrier"; bar_stmt += "("; - uint32_t mem_sem = id_mem_sem ? get(id_mem_sem).scalar() : uint32_t(MemorySemanticsMaskNone); + uint32_t mem_sem = id_mem_sem ? evaluate_constant_u32(id_mem_sem) : uint32_t(MemorySemanticsMaskNone); // Use the | operator to combine flags if we can. if (msl_options.supports_msl_version(1, 2)) @@ -8534,13 +8534,7 @@ string CompilerMSL::round_fp_tex_coords(string tex_coords, bool coord_is_fp) // The ID must be a scalar constant. string CompilerMSL::to_component_argument(uint32_t id) { - if (ir.ids[id].get_type() != TypeConstant) - { - SPIRV_CROSS_THROW("ID " + to_string(id) + " is not an OpConstant."); - return "component::x"; - } - - uint32_t component_index = get(id).scalar(); + uint32_t component_index = evaluate_constant_u32(id); switch (component_index) { case 0: @@ -11820,7 +11814,7 @@ void CompilerMSL::emit_subgroup_op(const Instruction &i) uint32_t result_type = ops[0]; uint32_t id = ops[1]; - auto scope = static_cast(get(ops[2]).scalar()); + auto scope = static_cast(evaluate_constant_u32(ops[2])); if (scope != ScopeSubgroup) SPIRV_CROSS_THROW("Only subgroup scope is supported."); @@ -11920,7 +11914,7 @@ case OpGroupNonUniform##op: \ else if (operation == GroupOperationClusteredReduce) \ { \ /* Only cluster sizes of 4 are supported. */ \ - uint32_t cluster_size = get(ops[5]).scalar(); \ + uint32_t cluster_size = evaluate_constant_u32(ops[5]); \ if (cluster_size != 4) \ SPIRV_CROSS_THROW("Metal only supports quad ClusteredReduce."); \ emit_unary_func_op(result_type, id, ops[4], "quad_" #msl_op); \ @@ -11949,7 +11943,7 @@ case OpGroupNonUniform##op: \ else if (operation == GroupOperationClusteredReduce) \ { \ /* Only cluster sizes of 4 are supported. */ \ - uint32_t cluster_size = get(ops[5]).scalar(); \ + uint32_t cluster_size = evaluate_constant_u32(ops[5]); \ if (cluster_size != 4) \ SPIRV_CROSS_THROW("Metal only supports quad ClusteredReduce."); \ emit_unary_func_op(result_type, id, ops[4], "quad_" #msl_op); \ @@ -11972,7 +11966,7 @@ case OpGroupNonUniform##op: \ else if (operation == GroupOperationClusteredReduce) \ { \ /* Only cluster sizes of 4 are supported. */ \ - uint32_t cluster_size = get(ops[5]).scalar(); \ + uint32_t cluster_size = evaluate_constant_u32(ops[5]); \ if (cluster_size != 4) \ SPIRV_CROSS_THROW("Metal only supports quad ClusteredReduce."); \ emit_unary_func_op_cast(result_type, id, ops[4], "quad_" #msl_op, type, type); \ @@ -12010,7 +12004,7 @@ case OpGroupNonUniform##op: \ // n 2 | 3 0 1 // e 3 | 2 1 0 // Notice that target = source ^ (direction + 1). - uint32_t mask = get(ops[4]).scalar() + 1; + uint32_t mask = evaluate_constant_u32(ops[4]) + 1; uint32_t mask_id = ir.increase_bound_by(1); set(mask_id, expression_type_id(ops[4]), mask, false); emit_binary_func_op(result_type, id, ops[3], mask_id, "quad_shuffle_xor");