diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterAlu.cpp b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterAlu.cpp index 4bfd16659..63ed452ff 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterAlu.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterAlu.cpp @@ -1,4 +1,4 @@ -#include "graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h" +#include "graphics/shader/recompiler/backend/spirv/spirvEmitterInstructions.h" #include @@ -277,402 +277,364 @@ uint32_t EmitPackHalf(EmitterState& state, uint32_t src) { } // namespace -bool EmitValueAlu(ValueEmitContext& ctx, const IR::Inst& inst) { +uint32_t EmitConvertU16U32(ValueEmitContext& ctx, uint32_t arg0) { + auto& state = ctx.state; + return EmitNative(ctx, arg0, ConstantU32(state, 0xffffu)); +} + +uint32_t EmitConvertU8U32(ValueEmitContext& ctx, uint32_t arg0) { + auto& state = ctx.state; + return EmitNative(ctx, arg0, ConstantU32(state, 0xffu)); +} + +uint32_t EmitConvertF16F32(ValueEmitContext& ctx, uint32_t arg0) { auto& state = ctx.state; - const auto op = inst.GetOpcode(); - const auto unary = [&](uint32_t spirv, IR::Type type) { - ctx.Emit(inst, spirv, type, {ctx.Arg(inst, 0)}); - return true; - }; - const auto binary = [&](uint32_t spirv, IR::Type type) { - ctx.Emit(inst, spirv, type, {ctx.Arg(inst, 0), ctx.Arg(inst, 1)}); - return true; - }; - const auto ext_unary = [&](uint32_t ext) { - ctx.Define(inst, EmitExt(state, TypeF32(state), ext, {ctx.Arg(inst, 0)})); - return true; - }; - switch (op) { - case IR::ValueOpcode::BitCastU16F16: - case IR::ValueOpcode::BitCastF16U16: - case IR::ValueOpcode::ConvertU32U16: - case IR::ValueOpcode::ConvertU32U8: - case IR::ValueOpcode::BitCastU32F32: return unary(spv::OpBitcast, IR::Type::U32); - case IR::ValueOpcode::BitCastF32U32: return unary(spv::OpBitcast, IR::Type::F32); - case IR::ValueOpcode::ConvertU16U32: - ctx.Emit(inst, spv::OpBitwiseAnd, IR::Type::U16, - {ctx.Arg(inst, 0), ConstantU32(state, 0xffffu)}); - return true; - case IR::ValueOpcode::ConvertU8U32: - ctx.Emit(inst, spv::OpBitwiseAnd, IR::Type::U8, - {ctx.Arg(inst, 0), ConstantU32(state, 0xffu)}); - return true; - case IR::ValueOpcode::ConvertF16F32: { - const auto pair = state.builder.AllocateId(); - state.builder.AddFunction({spv::OpCompositeConstruct, TypeF32Vector(state, 2), pair, - ctx.Arg(inst, 0), ConstantF32(state, 0)}); - ctx.Define(inst, EmitPackHalf(state, pair)); - return true; - } - case IR::ValueOpcode::ConvertF32F16: - ctx.Define(inst, EmitF16BitsToF32(state, ctx.Arg(inst, 0))); - return true; - case IR::ValueOpcode::ConvertS32F32: - ctx.Define(inst, EmitF32ToU32(state, ctx.Arg(inst, 0), true)); - return true; - case IR::ValueOpcode::ConvertU32F32: - ctx.Define(inst, EmitF32ToU32(state, ctx.Arg(inst, 0), false)); - return true; - case IR::ValueOpcode::ConvertF32S32: { - const auto signed_value = - Unary(state, spv::OpBitcast, TypeI32(state), ctx.Arg(inst, 0)); - ctx.Emit(inst, spv::OpConvertSToF, IR::Type::F32, {signed_value}); - return true; - } - case IR::ValueOpcode::ConvertF32U32: return unary(spv::OpConvertUToF, IR::Type::F32); - case IR::ValueOpcode::CompositeConstructU64: - ctx.Emit(inst, spv::OpCompositeConstruct, IR::Type::U64, - {ctx.Arg(inst, 0), ctx.Arg(inst, 1)}); - return true; - case IR::ValueOpcode::CompositeConstructU32x2: - ctx.Emit(inst, spv::OpCompositeConstruct, IR::Type::U32x2, - {ctx.Arg(inst, 0), ctx.Arg(inst, 1)}); - return true; - case IR::ValueOpcode::CompositeConstructU32x3: - ctx.Emit(inst, spv::OpCompositeConstruct, IR::Type::U32x3, - {ctx.Arg(inst, 0), ctx.Arg(inst, 1), ctx.Arg(inst, 2)}); - return true; - case IR::ValueOpcode::CompositeConstructF32x2: - ctx.Emit(inst, spv::OpCompositeConstruct, IR::Type::F32x2, - {ctx.Arg(inst, 0), ctx.Arg(inst, 1)}); - return true; - case IR::ValueOpcode::CompositeConstructU32x4: - ctx.Emit(inst, spv::OpCompositeConstruct, IR::Type::U32x4, - {ctx.Arg(inst, 0), ctx.Arg(inst, 1), ctx.Arg(inst, 2), ctx.Arg(inst, 3)}); - return true; - case IR::ValueOpcode::CompositeExtractU64: - case IR::ValueOpcode::CompositeExtractU32x2: - case IR::ValueOpcode::CompositeExtractU32x3: - case IR::ValueOpcode::CompositeExtractU32x4: - ctx.Emit(inst, spv::OpCompositeExtract, IR::Type::U32, - {ctx.Arg(inst, 0), inst.Arg(1).U32()}); - return true; - case IR::ValueOpcode::PackHalf2x16: - ctx.Define(inst, - EmitExt(state, TypeU32(state), GLSLstd450PackHalf2x16, {ctx.Arg(inst, 0)})); - return true; - case IR::ValueOpcode::PackSnorm2x16: - ctx.Define(inst, - EmitExt(state, TypeU32(state), GLSLstd450PackSnorm2x16, {ctx.Arg(inst, 0)})); - return true; - case IR::ValueOpcode::PackUnorm2x16: - ctx.Define(inst, - EmitExt(state, TypeU32(state), GLSLstd450PackUnorm2x16, {ctx.Arg(inst, 0)})); - return true; - case IR::ValueOpcode::PackFloat2x16Rtz: { - const auto low = EmitF32ToF16RtzBits(state, ctx.Arg(inst, 0)); - const auto high = - Binary(state, spv::OpShiftLeftLogical, TypeU32(state), - EmitF32ToF16RtzBits(state, ctx.Arg(inst, 1)), ConstantU32(state, 16)); - ctx.Define(inst, Binary(state, spv::OpBitwiseOr, TypeU32(state), low, high)); - return true; - } - case IR::ValueOpcode::FPAbs32: - ctx.Define(inst, EmitFAbsValue(state, ctx.Arg(inst, 0))); - return true; - case IR::ValueOpcode::FPNeg32: - ctx.Define(inst, EmitFNegateValue(state, ctx.Arg(inst, 0))); - return true; - case IR::ValueOpcode::FPSaturate32: - ctx.Define(inst, EmitExt(state, TypeF32(state), GLSLstd450FClamp, - {ctx.Arg(inst, 0), ConstantF32(state, 0), - ConstantF32(state, 0x3f800000u)})); - return true; - case IR::ValueOpcode::BitFieldInsert: - ctx.Emit(inst, spv::OpBitFieldInsert, IR::Type::U32, - {ctx.Arg(inst, 0), ctx.Arg(inst, 1), ctx.Arg(inst, 2), ctx.Arg(inst, 3)}); - return true; - case IR::ValueOpcode::BitFieldUExtract: - ctx.Emit(inst, spv::OpBitFieldUExtract, IR::Type::U32, - {ctx.Arg(inst, 0), ctx.Arg(inst, 1), ctx.Arg(inst, 2)}); - return true; - case IR::ValueOpcode::BitFieldSExtract: - ctx.Emit(inst, spv::OpBitFieldSExtract, IR::Type::U32, - {ctx.Arg(inst, 0), ctx.Arg(inst, 1), ctx.Arg(inst, 2)}); - return true; - case IR::ValueOpcode::SelectU1: - ctx.Emit(inst, spv::OpSelect, IR::Type::U1, - {ctx.Arg(inst, 0), ctx.Arg(inst, 1), ctx.Arg(inst, 2)}); - return true; - case IR::ValueOpcode::SelectU32: - ctx.Emit(inst, spv::OpSelect, IR::Type::U32, - {ctx.Arg(inst, 0), ctx.Arg(inst, 1), ctx.Arg(inst, 2)}); - return true; - case IR::ValueOpcode::SelectF32: - ctx.Emit(inst, spv::OpSelect, IR::Type::F32, - {ctx.Arg(inst, 0), ctx.Arg(inst, 1), ctx.Arg(inst, 2)}); - return true; - case IR::ValueOpcode::IAdd32: return binary(spv::OpIAdd, IR::Type::U32); - case IR::ValueOpcode::ISub32: return binary(spv::OpISub, IR::Type::U32); - case IR::ValueOpcode::IMul32: return binary(spv::OpIMul, IR::Type::U32); - case IR::ValueOpcode::UDiv32: return binary(spv::OpUDiv, IR::Type::U32); - case IR::ValueOpcode::IAdd64: - ctx.Define(inst, EmitAdd64(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1))); - return true; - case IR::ValueOpcode::ISub64: - ctx.Define(inst, EmitSub64(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1))); - return true; - case IR::ValueOpcode::IMul64: - ctx.Define(inst, EmitMul64(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1))); - return true; - case IR::ValueOpcode::IAddCarry32: - ctx.Emit(inst, spv::OpIAddCarry, IR::Type::U32x2, {ctx.Arg(inst, 0), ctx.Arg(inst, 1)}); - return true; - case IR::ValueOpcode::SMulHi: - ctx.Define(inst, EmitMulHigh(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1), true)); - return true; - case IR::ValueOpcode::UMulHi: - ctx.Define(inst, EmitMulHigh(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1), false)); - return true; - case IR::ValueOpcode::IAbs32: { - const auto value = ctx.Arg(inst, 0); - const auto neg = Unary(state, spv::OpSNegate, TypeU32(state), value); - const auto negative = - Binary(state, spv::OpSLessThan, TypeBool(state), value, ConstantU32(state, 0)); - ctx.Define(inst, Select(state, TypeU32(state), negative, neg, value)); - return true; - } - case IR::ValueOpcode::ShiftLeftLogical32: - return binary(spv::OpShiftLeftLogical, IR::Type::U32); - case IR::ValueOpcode::ShiftRightLogical32: - return binary(spv::OpShiftRightLogical, IR::Type::U32); - case IR::ValueOpcode::ShiftRightArithmetic32: - return binary(spv::OpShiftRightArithmetic, IR::Type::U32); - case IR::ValueOpcode::ShiftLeftLogical64: - ctx.Define(inst, - inst.Arg(1).Resolve().IsImmediate() - ? EmitConstantShift64(state, spv::OpShiftLeftLogical, ctx.Arg(inst, 0), - inst.Arg(1).Resolve().U32()) - : EmitShift64(state, spv::OpShiftLeftLogical, ctx.Arg(inst, 0), - ctx.Arg(inst, 1))); - return true; - case IR::ValueOpcode::ShiftRightLogical64: - ctx.Define(inst, - inst.Arg(1).Resolve().IsImmediate() - ? EmitConstantShift64(state, spv::OpShiftRightLogical, ctx.Arg(inst, 0), - inst.Arg(1).Resolve().U32()) - : EmitShift64(state, spv::OpShiftRightLogical, ctx.Arg(inst, 0), - ctx.Arg(inst, 1))); - return true; - case IR::ValueOpcode::ShiftRightArithmetic64: - ctx.Define(inst, - inst.Arg(1).Resolve().IsImmediate() - ? EmitConstantShift64(state, spv::OpShiftRightArithmetic, - ctx.Arg(inst, 0), inst.Arg(1).Resolve().U32()) - : EmitShift64(state, spv::OpShiftRightArithmetic, ctx.Arg(inst, 0), - ctx.Arg(inst, 1))); - return true; - case IR::ValueOpcode::BitwiseAnd32: return binary(spv::OpBitwiseAnd, IR::Type::U32); - case IR::ValueOpcode::BitwiseOr32: return binary(spv::OpBitwiseOr, IR::Type::U32); - case IR::ValueOpcode::BitwiseXor32: return binary(spv::OpBitwiseXor, IR::Type::U32); - case IR::ValueOpcode::BitwiseNot32: return unary(spv::OpNot, IR::Type::U32); - case IR::ValueOpcode::BitwiseAnd64: - ctx.Define(inst, Binary(state, spv::OpBitwiseAnd, TypeU64(state), ctx.Arg(inst, 0), - ctx.Arg(inst, 1))); - return true; - case IR::ValueOpcode::BitReverse32: return unary(spv::OpBitReverse, IR::Type::U32); - case IR::ValueOpcode::BitCount32: return unary(spv::OpBitCount, IR::Type::U32); - case IR::ValueOpcode::BitCount64: { - const auto pair = - ExtractPair(state, Unary(state, spv::OpBitCount, TypeU64(state), ctx.Arg(inst, 0))); - ctx.Define(inst, Binary(state, spv::OpIAdd, TypeU32(state), pair.low, pair.high)); - return true; - } - case IR::ValueOpcode::FindILsb32: { - const auto value = - EmitExt(state, TypeI32(state), GLSLstd450FindILsb, {ctx.Arg(inst, 0)}); - ctx.Define(inst, Unary(state, spv::OpBitcast, TypeU32(state), value)); - return true; - } - case IR::ValueOpcode::FindUMsb32: { - const auto value = - EmitExt(state, TypeI32(state), GLSLstd450FindUMsb, {ctx.Arg(inst, 0)}); - ctx.Define(inst, Unary(state, spv::OpBitcast, TypeU32(state), value)); - return true; - } - case IR::ValueOpcode::FindUMsb64: - ctx.Define(inst, EmitFindMsb64(state, ctx.Arg(inst, 0))); - return true; - case IR::ValueOpcode::SMin32: - ctx.Define(inst, EmitMinMaxI32Value(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1), false)); - return true; - case IR::ValueOpcode::SMax32: - ctx.Define(inst, EmitMinMaxI32Value(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1), true)); - return true; - case IR::ValueOpcode::UMin32: - ctx.Define(inst, EmitMinMaxU32Value(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1), false)); - return true; - case IR::ValueOpcode::UMax32: - ctx.Define(inst, EmitMinMaxU32Value(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1), true)); - return true; - case IR::ValueOpcode::SMinTri32: - ctx.Define(inst, EmitMinMax3(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1), - ctx.Arg(inst, 2), true, false)); - return true; - case IR::ValueOpcode::SMaxTri32: - ctx.Define(inst, EmitMinMax3(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1), - ctx.Arg(inst, 2), true, true)); - return true; - case IR::ValueOpcode::UMinTri32: - ctx.Define(inst, EmitMinMax3(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1), - ctx.Arg(inst, 2), false, false)); - return true; - case IR::ValueOpcode::UMaxTri32: - ctx.Define(inst, EmitMinMax3(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1), - ctx.Arg(inst, 2), false, true)); - return true; - case IR::ValueOpcode::SMedTri32: - ctx.Define(inst, - EmitMed3(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1), ctx.Arg(inst, 2), true)); - return true; - case IR::ValueOpcode::UMedTri32: - ctx.Define( - inst, EmitMed3(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1), ctx.Arg(inst, 2), false)); - return true; - case IR::ValueOpcode::SLessThan32: return binary(spv::OpSLessThan, IR::Type::U1); - case IR::ValueOpcode::ULessThan32: return binary(spv::OpULessThan, IR::Type::U1); - case IR::ValueOpcode::IEqual32: return binary(spv::OpIEqual, IR::Type::U1); - case IR::ValueOpcode::SLessThanEqual32: return binary(spv::OpSLessThanEqual, IR::Type::U1); - case IR::ValueOpcode::ULessThanEqual32: return binary(spv::OpULessThanEqual, IR::Type::U1); - case IR::ValueOpcode::SGreaterThan32: return binary(spv::OpSGreaterThan, IR::Type::U1); - case IR::ValueOpcode::UGreaterThan32: return binary(spv::OpUGreaterThan, IR::Type::U1); - case IR::ValueOpcode::INotEqual32: return binary(spv::OpINotEqual, IR::Type::U1); - case IR::ValueOpcode::SGreaterThanEqual32: - return binary(spv::OpSGreaterThanEqual, IR::Type::U1); - case IR::ValueOpcode::UGreaterThanEqual32: - return binary(spv::OpUGreaterThanEqual, IR::Type::U1); - case IR::ValueOpcode::IEqual64: - ctx.Define(inst, CompareEqual64(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1), false)); - return true; - case IR::ValueOpcode::INotEqual64: - ctx.Define(inst, CompareEqual64(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1), true)); - return true; - case IR::ValueOpcode::ULessThan64: - ctx.Define(inst, CompareOrdered64(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1), - spv::OpULessThan, spv::OpULessThan)); - return true; - case IR::ValueOpcode::SLessThan64: - ctx.Define(inst, CompareOrdered64(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1), - spv::OpSLessThan, spv::OpULessThan)); - return true; - case IR::ValueOpcode::UGreaterThan64: - ctx.Define(inst, CompareOrdered64(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1), - spv::OpUGreaterThan, spv::OpUGreaterThan)); - return true; - case IR::ValueOpcode::LogicalOr: return binary(spv::OpLogicalOr, IR::Type::U1); - case IR::ValueOpcode::LogicalAnd: return binary(spv::OpLogicalAnd, IR::Type::U1); - case IR::ValueOpcode::LogicalXor: return binary(spv::OpLogicalNotEqual, IR::Type::U1); - case IR::ValueOpcode::LogicalNot: return unary(spv::OpLogicalNot, IR::Type::U1); - case IR::ValueOpcode::FPOrdEqual32: return binary(spv::OpFOrdEqual, IR::Type::U1); - case IR::ValueOpcode::FPUnordEqual32: return binary(spv::OpFUnordEqual, IR::Type::U1); - case IR::ValueOpcode::FPOrdNotEqual32: return binary(spv::OpFOrdNotEqual, IR::Type::U1); - case IR::ValueOpcode::FPUnordNotEqual32: return binary(spv::OpFUnordNotEqual, IR::Type::U1); - case IR::ValueOpcode::FPOrdLessThan32: return binary(spv::OpFOrdLessThan, IR::Type::U1); - case IR::ValueOpcode::FPUnordLessThan32: return binary(spv::OpFUnordLessThan, IR::Type::U1); - case IR::ValueOpcode::FPOrdGreaterThan32: - return binary(spv::OpFOrdGreaterThan, IR::Type::U1); - case IR::ValueOpcode::FPUnordGreaterThan32: - return binary(spv::OpFUnordGreaterThan, IR::Type::U1); - case IR::ValueOpcode::FPOrdLessThanEqual32: - return binary(spv::OpFOrdLessThanEqual, IR::Type::U1); - case IR::ValueOpcode::FPUnordLessThanEqual32: - return binary(spv::OpFUnordLessThanEqual, IR::Type::U1); - case IR::ValueOpcode::FPOrdGreaterThanEqual32: - return binary(spv::OpFOrdGreaterThanEqual, IR::Type::U1); - case IR::ValueOpcode::FPUnordGreaterThanEqual32: - return binary(spv::OpFUnordGreaterThanEqual, IR::Type::U1); - case IR::ValueOpcode::FPIsNan32: - ctx.Emit(inst, spv::OpFUnordNotEqual, IR::Type::U1, - {ctx.Arg(inst, 0), ctx.Arg(inst, 0)}); - return true; - case IR::ValueOpcode::FPCmpClass32: - ctx.Define(inst, EmitClassMaskF32(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1))); - return true; - case IR::ValueOpcode::FPAdd32: return binary(spv::OpFAdd, IR::Type::F32); - case IR::ValueOpcode::FPSub32: return binary(spv::OpFSub, IR::Type::F32); - case IR::ValueOpcode::FPMul32: return binary(spv::OpFMul, IR::Type::F32); - case IR::ValueOpcode::FPFma32: - ctx.Define(inst, EmitExt(state, TypeF32(state), GLSLstd450Fma, - {ctx.Arg(inst, 0), ctx.Arg(inst, 1), ctx.Arg(inst, 2)})); - return true; - case IR::ValueOpcode::FPMin32: - ctx.Define(inst, EmitMinMaxF32Value(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1), false)); - return true; - case IR::ValueOpcode::FPMax32: - ctx.Define(inst, EmitMinMaxF32Value(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1), true)); - return true; - case IR::ValueOpcode::FPMinTri32: - ctx.Define(inst, EmitFMinMax3(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1), - ctx.Arg(inst, 2), false)); - return true; - case IR::ValueOpcode::FPMaxTri32: - ctx.Define(inst, EmitFMinMax3(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1), - ctx.Arg(inst, 2), true)); - return true; - case IR::ValueOpcode::FPMedTri32: - ctx.Define(inst, - EmitFMed3(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1), ctx.Arg(inst, 2))); - return true; - case IR::ValueOpcode::FPRecip32: { - const auto source = EmitFlushF32DenormToSignedZero(state, ctx.Arg(inst, 0)); - ctx.Define(inst, Binary(state, spv::OpFDiv, TypeF32(state), - ConstantF32(state, 0x3f800000u), source)); - return true; - } - case IR::ValueOpcode::FPRecipIFlag32: - // Integer-to-float inputs used by IFLAG cannot be denormal. - ctx.Define(inst, Binary(state, spv::OpFDiv, TypeF32(state), - ConstantF32(state, 0x3f800000u), ctx.Arg(inst, 0))); - return true; - case IR::ValueOpcode::FPRecipSqrt32: - ctx.Define(inst, EmitExt(state, TypeF32(state), GLSLstd450InverseSqrt, - {EmitFlushF32DenormToSignedZero(state, ctx.Arg(inst, 0))})); - return true; - case IR::ValueOpcode::FPSqrt: - ctx.Define(inst, EmitExt(state, TypeF32(state), GLSLstd450Sqrt, - {EmitFlushF32DenormToSignedZero(state, ctx.Arg(inst, 0))})); - return true; - case IR::ValueOpcode::FPSin: - case IR::ValueOpcode::FPCos: { - auto source = EmitTrigCycleF32(state, ctx.Arg(inst, 0), op == IR::ValueOpcode::FPSin); - source = - Binary(state, spv::OpFMul, TypeF32(state), source, ConstantF32(state, 0x40c90fdbu)); - ctx.Define(inst, EmitExt(state, TypeF32(state), - op == IR::ValueOpcode::FPSin ? GLSLstd450Sin : GLSLstd450Cos, - {source})); - return true; - } - case IR::ValueOpcode::FPExp2: - ctx.Define(inst, EmitExt(state, TypeF32(state), GLSLstd450Exp2, - {EmitFlushF32DenormToSignedZero(state, ctx.Arg(inst, 0))})); - return true; - case IR::ValueOpcode::FPLog2: - ctx.Define(inst, EmitExt(state, TypeF32(state), GLSLstd450Log2, - {EmitFlushF32DenormToSignedZero(state, ctx.Arg(inst, 0))})); - return true; - case IR::ValueOpcode::FPLdexp: { - const auto exponent = Unary(state, spv::OpBitcast, TypeI32(state), ctx.Arg(inst, 1)); - ctx.Define(inst, EmitExt(state, TypeF32(state), GLSLstd450Ldexp, - {ctx.Arg(inst, 0), exponent})); - return true; - } - case IR::ValueOpcode::FPRoundEven32: return ext_unary(GLSLstd450RoundEven); - case IR::ValueOpcode::FPFloor32: return ext_unary(GLSLstd450Floor); - case IR::ValueOpcode::FPCeil32: return ext_unary(GLSLstd450Ceil); - case IR::ValueOpcode::FPTrunc32: return ext_unary(GLSLstd450Trunc); - case IR::ValueOpcode::FPFract32: return ext_unary(GLSLstd450Fract); - default: return false; - } + const auto pair = state.builder.AllocateId(); + state.builder.AddFunction( + {spv::OpCompositeConstruct, TypeF32Vector(state, 2), pair, arg0, ConstantF32(state, 0)}); + return EmitPackHalf(state, pair); +} + +uint32_t EmitConvertF32F16(ValueEmitContext& ctx, uint32_t arg0) { + auto& state = ctx.state; + return EmitF16BitsToF32(state, arg0); +} + +uint32_t EmitConvertS32F32(ValueEmitContext& ctx, uint32_t arg0) { + auto& state = ctx.state; + return EmitF32ToU32(state, arg0, true); +} + +uint32_t EmitConvertU32F32(ValueEmitContext& ctx, uint32_t arg0) { + auto& state = ctx.state; + return EmitF32ToU32(state, arg0, false); +} + +uint32_t EmitConvertF32S32(ValueEmitContext& ctx, uint32_t arg0) { + auto& state = ctx.state; + const auto signed_value = Unary(state, spv::OpBitcast, TypeI32(state), arg0); + return EmitNative(ctx, signed_value); +} + +uint32_t EmitCompositeExtractU64(ValueEmitContext& ctx, uint32_t arg0, IR::Value arg1) { + return EmitNative(ctx, arg0, arg1.U32()); +} + +uint32_t EmitPackHalf2x16(ValueEmitContext& ctx, uint32_t arg0) { + auto& state = ctx.state; + return EmitExt(state, TypeU32(state), GLSLstd450PackHalf2x16, {arg0}); +} + +uint32_t EmitPackSnorm2x16(ValueEmitContext& ctx, uint32_t arg0) { + auto& state = ctx.state; + return EmitExt(state, TypeU32(state), GLSLstd450PackSnorm2x16, {arg0}); +} + +uint32_t EmitPackUnorm2x16(ValueEmitContext& ctx, uint32_t arg0) { + auto& state = ctx.state; + return EmitExt(state, TypeU32(state), GLSLstd450PackUnorm2x16, {arg0}); +} + +uint32_t EmitPackFloat2x16Rtz(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1) { + auto& state = ctx.state; + const auto low = EmitF32ToF16RtzBits(state, arg0); + const auto high = Binary(state, spv::OpShiftLeftLogical, TypeU32(state), + EmitF32ToF16RtzBits(state, arg1), ConstantU32(state, 16)); + return Binary(state, spv::OpBitwiseOr, TypeU32(state), low, high); +} + +uint32_t EmitFPAbs32(ValueEmitContext& ctx, uint32_t arg0) { + auto& state = ctx.state; + return EmitFAbsValue(state, arg0); +} + +uint32_t EmitFPNeg32(ValueEmitContext& ctx, uint32_t arg0) { + auto& state = ctx.state; + return EmitFNegateValue(state, arg0); +} + +uint32_t EmitFPSaturate32(ValueEmitContext& ctx, uint32_t arg0) { + auto& state = ctx.state; + return EmitExt(state, TypeF32(state), GLSLstd450FClamp, + {arg0, ConstantF32(state, 0), ConstantF32(state, 0x3f800000u)}); +} + +uint32_t EmitIAdd64(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1) { + auto& state = ctx.state; + return EmitAdd64(state, arg0, arg1); +} + +uint32_t EmitISub64(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1) { + auto& state = ctx.state; + return EmitSub64(state, arg0, arg1); +} + +uint32_t EmitIMul64(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1) { + auto& state = ctx.state; + return EmitMul64(state, arg0, arg1); +} + +uint32_t EmitSMulHi(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1) { + auto& state = ctx.state; + return EmitMulHigh(state, arg0, arg1, true); +} + +uint32_t EmitUMulHi(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1) { + auto& state = ctx.state; + return EmitMulHigh(state, arg0, arg1, false); +} + +uint32_t EmitIAbs32(ValueEmitContext& ctx, uint32_t arg0) { + auto& state = ctx.state; + const auto value = arg0; + const auto neg = Unary(state, spv::OpSNegate, TypeU32(state), value); + const auto negative = + Binary(state, spv::OpSLessThan, TypeBool(state), value, ConstantU32(state, 0)); + return Select(state, TypeU32(state), negative, neg, value); +} + +uint32_t EmitShiftLeftLogical64(ValueEmitContext& ctx, uint32_t arg0, IR::Value arg1) { + auto& state = ctx.state; + return arg1.Resolve().IsImmediate() + ? EmitConstantShift64(state, spv::OpShiftLeftLogical, arg0, arg1.Resolve().U32()) + : EmitShift64(state, spv::OpShiftLeftLogical, arg0, ctx.Def(arg1)); +} + +uint32_t EmitShiftRightLogical64(ValueEmitContext& ctx, uint32_t arg0, IR::Value arg1) { + auto& state = ctx.state; + return arg1.Resolve().IsImmediate() + ? EmitConstantShift64(state, spv::OpShiftRightLogical, arg0, arg1.Resolve().U32()) + : EmitShift64(state, spv::OpShiftRightLogical, arg0, ctx.Def(arg1)); +} + +uint32_t EmitShiftRightArithmetic64(ValueEmitContext& ctx, uint32_t arg0, IR::Value arg1) { + auto& state = ctx.state; + return arg1.Resolve().IsImmediate() + ? EmitConstantShift64(state, spv::OpShiftRightArithmetic, arg0, arg1.Resolve().U32()) + : EmitShift64(state, spv::OpShiftRightArithmetic, arg0, ctx.Def(arg1)); +} + +uint32_t EmitBitwiseAnd64(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1) { + auto& state = ctx.state; + return Binary(state, spv::OpBitwiseAnd, TypeU64(state), arg0, arg1); +} + +uint32_t EmitBitCount64(ValueEmitContext& ctx, uint32_t arg0) { + auto& state = ctx.state; + const auto pair = ExtractPair(state, Unary(state, spv::OpBitCount, TypeU64(state), arg0)); + return Binary(state, spv::OpIAdd, TypeU32(state), pair.low, pair.high); +} + +uint32_t EmitFindILsb32(ValueEmitContext& ctx, uint32_t arg0) { + auto& state = ctx.state; + const auto value = EmitExt(state, TypeI32(state), GLSLstd450FindILsb, {arg0}); + return Unary(state, spv::OpBitcast, TypeU32(state), value); +} + +uint32_t EmitFindUMsb32(ValueEmitContext& ctx, uint32_t arg0) { + auto& state = ctx.state; + const auto value = EmitExt(state, TypeI32(state), GLSLstd450FindUMsb, {arg0}); + return Unary(state, spv::OpBitcast, TypeU32(state), value); +} + +uint32_t EmitFindUMsb64(ValueEmitContext& ctx, uint32_t arg0) { + auto& state = ctx.state; + return EmitFindMsb64(state, arg0); +} + +uint32_t EmitSMin32(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1) { + auto& state = ctx.state; + return EmitMinMaxI32Value(state, arg0, arg1, false); +} + +uint32_t EmitSMax32(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1) { + auto& state = ctx.state; + return EmitMinMaxI32Value(state, arg0, arg1, true); +} + +uint32_t EmitUMin32(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1) { + auto& state = ctx.state; + return EmitMinMaxU32Value(state, arg0, arg1, false); +} + +uint32_t EmitUMax32(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1) { + auto& state = ctx.state; + return EmitMinMaxU32Value(state, arg0, arg1, true); +} + +uint32_t EmitSMinTri32(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1, uint32_t arg2) { + auto& state = ctx.state; + return EmitMinMax3(state, arg0, arg1, arg2, true, false); +} + +uint32_t EmitSMaxTri32(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1, uint32_t arg2) { + auto& state = ctx.state; + return EmitMinMax3(state, arg0, arg1, arg2, true, true); +} + +uint32_t EmitUMinTri32(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1, uint32_t arg2) { + auto& state = ctx.state; + return EmitMinMax3(state, arg0, arg1, arg2, false, false); +} + +uint32_t EmitUMaxTri32(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1, uint32_t arg2) { + auto& state = ctx.state; + return EmitMinMax3(state, arg0, arg1, arg2, false, true); +} + +uint32_t EmitSMedTri32(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1, uint32_t arg2) { + auto& state = ctx.state; + return EmitMed3(state, arg0, arg1, arg2, true); +} + +uint32_t EmitUMedTri32(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1, uint32_t arg2) { + auto& state = ctx.state; + return EmitMed3(state, arg0, arg1, arg2, false); +} + +uint32_t EmitIEqual64(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1) { + auto& state = ctx.state; + return CompareEqual64(state, arg0, arg1, false); +} + +uint32_t EmitINotEqual64(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1) { + auto& state = ctx.state; + return CompareEqual64(state, arg0, arg1, true); +} + +uint32_t EmitULessThan64(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1) { + auto& state = ctx.state; + return CompareOrdered64(state, arg0, arg1, spv::OpULessThan, spv::OpULessThan); +} + +uint32_t EmitSLessThan64(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1) { + auto& state = ctx.state; + return CompareOrdered64(state, arg0, arg1, spv::OpSLessThan, spv::OpULessThan); +} + +uint32_t EmitUGreaterThan64(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1) { + auto& state = ctx.state; + return CompareOrdered64(state, arg0, arg1, spv::OpUGreaterThan, spv::OpUGreaterThan); +} + +uint32_t EmitFPIsNan32(ValueEmitContext& ctx, uint32_t arg0) { + return EmitNative(ctx, arg0, arg0); +} + +uint32_t EmitFPCmpClass32(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1) { + auto& state = ctx.state; + return EmitClassMaskF32(state, arg0, arg1); +} + +uint32_t EmitFPFma32(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1, uint32_t arg2) { + auto& state = ctx.state; + return EmitExt(state, TypeF32(state), GLSLstd450Fma, {arg0, arg1, arg2}); +} + +uint32_t EmitFPMin32(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1) { + auto& state = ctx.state; + return EmitMinMaxF32Value(state, arg0, arg1, false); +} + +uint32_t EmitFPMax32(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1) { + auto& state = ctx.state; + return EmitMinMaxF32Value(state, arg0, arg1, true); +} + +uint32_t EmitFPMinTri32(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1, uint32_t arg2) { + auto& state = ctx.state; + return EmitFMinMax3(state, arg0, arg1, arg2, false); +} + +uint32_t EmitFPMaxTri32(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1, uint32_t arg2) { + auto& state = ctx.state; + return EmitFMinMax3(state, arg0, arg1, arg2, true); +} + +uint32_t EmitFPMedTri32(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1, uint32_t arg2) { + auto& state = ctx.state; + return EmitFMed3(state, arg0, arg1, arg2); +} + +uint32_t EmitFPRecip32(ValueEmitContext& ctx, uint32_t arg0) { + auto& state = ctx.state; + const auto source = EmitFlushF32DenormToSignedZero(state, arg0); + return Binary(state, spv::OpFDiv, TypeF32(state), ConstantF32(state, 0x3f800000u), source); +} + +uint32_t EmitFPRecipIFlag32(ValueEmitContext& ctx, uint32_t arg0) { + auto& state = ctx.state; + // Integer-to-float inputs used by IFLAG cannot be denormal. + return Binary(state, spv::OpFDiv, TypeF32(state), ConstantF32(state, 0x3f800000u), arg0); +} + +uint32_t EmitFPRecipSqrt32(ValueEmitContext& ctx, uint32_t arg0) { + auto& state = ctx.state; + return EmitExt(state, TypeF32(state), GLSLstd450InverseSqrt, + {EmitFlushF32DenormToSignedZero(state, arg0)}); +} + +uint32_t EmitFPSqrt(ValueEmitContext& ctx, uint32_t arg0) { + auto& state = ctx.state; + return EmitExt(state, TypeF32(state), GLSLstd450Sqrt, + {EmitFlushF32DenormToSignedZero(state, arg0)}); +} + +uint32_t EmitFPExp2(ValueEmitContext& ctx, uint32_t arg0) { + auto& state = ctx.state; + return EmitExt(state, TypeF32(state), GLSLstd450Exp2, + {EmitFlushF32DenormToSignedZero(state, arg0)}); +} + +uint32_t EmitFPLog2(ValueEmitContext& ctx, uint32_t arg0) { + auto& state = ctx.state; + return EmitExt(state, TypeF32(state), GLSLstd450Log2, + {EmitFlushF32DenormToSignedZero(state, arg0)}); +} + +uint32_t EmitFPLdexp(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1) { + auto& state = ctx.state; + const auto exponent = Unary(state, spv::OpBitcast, TypeI32(state), arg1); + return EmitExt(state, TypeF32(state), GLSLstd450Ldexp, {arg0, exponent}); +} + +uint32_t EmitFPRoundEven32(ValueEmitContext& ctx, uint32_t arg0) { + auto& state = ctx.state; + return EmitExt(state, TypeF32(state), GLSLstd450RoundEven, {arg0}); +} + +uint32_t EmitFPFloor32(ValueEmitContext& ctx, uint32_t arg0) { + auto& state = ctx.state; + return EmitExt(state, TypeF32(state), GLSLstd450Floor, {arg0}); +} + +uint32_t EmitFPCeil32(ValueEmitContext& ctx, uint32_t arg0) { + auto& state = ctx.state; + return EmitExt(state, TypeF32(state), GLSLstd450Ceil, {arg0}); +} + +uint32_t EmitFPTrunc32(ValueEmitContext& ctx, uint32_t arg0) { + auto& state = ctx.state; + return EmitExt(state, TypeF32(state), GLSLstd450Trunc, {arg0}); +} + +uint32_t EmitFPFract32(ValueEmitContext& ctx, uint32_t arg0) { + auto& state = ctx.state; + return EmitExt(state, TypeF32(state), GLSLstd450Fract, {arg0}); +} + +uint32_t EmitFPSin(ValueEmitContext& ctx, uint32_t arg0) { + auto& state = ctx.state; + auto source = EmitTrigCycleF32(state, arg0, true); + source = Binary(state, spv::OpFMul, TypeF32(state), source, ConstantF32(state, 0x40c90fdbu)); + return EmitExt(state, TypeF32(state), GLSLstd450Sin, {source}); +} + +uint32_t EmitFPCos(ValueEmitContext& ctx, uint32_t arg0) { + auto& state = ctx.state; + auto source = EmitTrigCycleF32(state, arg0, false); + source = Binary(state, spv::OpFMul, TypeF32(state), source, ConstantF32(state, 0x40c90fdbu)); + return EmitExt(state, TypeF32(state), GLSLstd450Cos, {source}); } } // namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterFlow.cpp b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterFlow.cpp index bc2bc3eda..b4451c062 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterFlow.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterFlow.cpp @@ -1,4 +1,4 @@ -#include "graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h" +#include "graphics/shader/recompiler/backend/spirv/spirvEmitterInstructions.h" #include @@ -426,7 +426,9 @@ uint32_t ConvertPositionToClipSpace(EmitterState& state, uint32_t position) { return converted; } -void EmitExport(ValueEmitContext& ctx, const IR::Inst& inst) { +} // namespace + +void EmitSetAttribute(ValueEmitContext& ctx, const IR::Inst& inst) { auto& state = ctx.state; const auto& exp = ctx.Export(inst); const auto exec = ctx.Arg(inst, 1); @@ -515,179 +517,176 @@ void EmitExport(ValueEmitContext& ctx, const IR::Inst& inst) { }); } -} // namespace +uint32_t EmitIdentity(ValueEmitContext&, uint32_t value) { + return value; +} -bool EmitValueFlow(ValueEmitContext& ctx, const IR::Inst& inst) { +void EmitVoid(ValueEmitContext&) {} + +void EmitBarrier(ValueEmitContext& ctx) { + auto& state = ctx.state; + const auto semantics = + spv::MemorySemanticsAcquireReleaseMask | spv::MemorySemanticsWorkgroupMemoryMask; + state.builder.AddFunction({spv::OpControlBarrier, ConstantU32(state, spv::ScopeWorkgroup), + ConstantU32(state, spv::ScopeWorkgroup), + ConstantU32(state, semantics)}); +} + +uint32_t EmitMeshDrawParameter(ValueEmitContext& ctx, const IR::Inst& inst) { + auto& state = ctx.state; + const auto result = state.builder.AllocateId(); + const auto index = inst.Arg(0).U32(); + if (state.program.stage != ShaderType::Mesh || index >= IR::PushData::MeshDrawDwordCount) { + ctx.Fail(inst, "invalid mesh draw parameter"); + } + const auto pointer = state.builder.AllocateId(); + state.builder.AddFunction({spv::OpAccessChain, TypePushConstantElementPointer(state), pointer, + state.push_constant_variable, ConstantU32(state, 0), + ConstantU32(state, index)}); + state.builder.AddFunction({spv::OpLoad, TypeU32(state), result, pointer}); + return result; +} + +uint32_t EmitGetUserData(ValueEmitContext& ctx, IR::ScalarReg reg) { auto& state = ctx.state; - switch (inst.GetOpcode()) { - case IR::ValueOpcode::Identity: ctx.Define(inst, ctx.Arg(inst, 0)); return true; - case IR::ValueOpcode::Void: - case IR::ValueOpcode::Reference: - case IR::ValueOpcode::ReferenceU32: - case IR::ValueOpcode::ControlNop: - case IR::ValueOpcode::Waitcnt: - case IR::ValueOpcode::Sendmsg: - case IR::ValueOpcode::TtraceData: - case IR::ValueOpcode::InstPrefetch: return true; - case IR::ValueOpcode::Barrier: { - const auto semantics = - spv::MemorySemanticsAcquireReleaseMask | spv::MemorySemanticsWorkgroupMemoryMask; - state.builder.AddFunction( - {spv::OpControlBarrier, ConstantU32(state, spv::ScopeWorkgroup), - ConstantU32(state, spv::ScopeWorkgroup), ConstantU32(state, semantics)}); - return true; - } - case IR::ValueOpcode::MeshAllocate: EmitMeshAllocate(ctx, inst); return true; - case IR::ValueOpcode::MeshDrawParameter: { - const auto index = inst.Arg(0).U32(); - if (state.program.stage != ShaderType::Mesh || - index >= IR::PushData::MeshDrawDwordCount) { - ctx.Fail(inst, "invalid mesh draw parameter"); - } - const auto pointer = state.builder.AllocateId(); - state.builder.AddFunction({spv::OpAccessChain, TypePushConstantElementPointer(state), - pointer, state.push_constant_variable, ConstantU32(state, 0), - ConstantU32(state, index)}); - state.builder.AddFunction({spv::OpLoad, TypeU32(state), ctx.Result(inst), pointer}); - return true; - } - case IR::ValueOpcode::GetUserData: { - const auto reg = inst.Arg(0).ScalarRegister(); - uint32_t dword = 0; - if (!UserDataDwordIndex(state, reg, dword)) { - ctx.Define(inst, ConstantU32(state, 0)); - } else { - ctx.Define(inst, EmitShaderDataDwordLoad(state, dword)); - } - return true; - } - case IR::ValueOpcode::GetBuiltin: - ctx.Define(inst, EmitBuiltinU32(ctx, static_cast(inst.Arg(0).U32()), - inst.Arg(1).U32())); - return true; - case IR::ValueOpcode::UndefU1: - case IR::ValueOpcode::UndefU8: - case IR::ValueOpcode::UndefU16: - case IR::ValueOpcode::UndefU32: - case IR::ValueOpcode::UndefU64: - state.builder.AddFunction({spv::OpUndef, ctx.TypeId(inst.GetType()), ctx.Result(inst)}); - return true; - case IR::ValueOpcode::DppMoveU32: { - const auto flags = inst.Flags(); - const auto target = EmitDppTargetLane(state, flags.control); - const auto shuffled = ctx.Shuffle(inst, 0, target.lane); - if (flags.fetch_inactive) { - ctx.Define(inst, shuffled); - return true; - } - const auto ballot = ctx.Ballot(inst.Arg(1)); - const auto source_active = EmitBallotLaneActiveBool(state, ballot, target.lane); - const auto can_fetch = state.builder.AllocateId(); - state.builder.AddFunction( - {spv::OpLogicalAnd, TypeBool(state), can_fetch, target.valid, source_active}); - ctx.Emit(inst, spv::OpSelect, IR::Type::U32, - {can_fetch, shuffled, ConstantU32(state, 0)}); - return true; - } - case IR::ValueOpcode::DppUpdateU32: { - const auto flags = inst.Flags(); - const auto write = EmitDppWriteCondition(ctx, flags, ctx.Arg(inst, 2)); - ctx.Emit(inst, spv::OpSelect, IR::Type::U32, - {write, ctx.Arg(inst, 0), ctx.Arg(inst, 1)}); - return true; - } - case IR::ValueOpcode::WqmU64: - ctx.Define(inst, EmitWqmU64(ctx.state, ctx.Arg(inst, 0))); - return true; - case IR::ValueOpcode::LaneId: - ctx.Define(inst, EmitSubgroupLocalInvocationId(state)); - return true; - case IR::ValueOpcode::Ballot: ctx.Define(inst, ctx.Ballot(inst.Arg(0))); return true; - case IR::ValueOpcode::ReadFirstLane: { - const auto ballot = ctx.Ballot(inst.Arg(1)); - const auto lane = ctx.FirstLane(ballot); - ctx.Define(inst, ctx.Shuffle(inst, 0, lane)); - return true; - } - case IR::ValueOpcode::ReadLane: - ctx.Define(inst, ctx.Shuffle(inst, 0, ctx.Arg(inst, 1))); - return true; - case IR::ValueOpcode::WriteLane: { - const auto hit = state.builder.AllocateId(); - state.builder.AddFunction({spv::OpIEqual, TypeBool(state), hit, - EmitSubgroupLocalInvocationId(state), ctx.Arg(inst, 2)}); - ctx.Emit(inst, spv::OpSelect, IR::Type::U32, {hit, ctx.Arg(inst, 1), ctx.Arg(inst, 0)}); - return true; - } - case IR::ValueOpcode::Permlane16U32: { - const auto flags = inst.Flags(); - const auto subid = EmitSubgroupLocalInvocationId(state); - const auto row = state.builder.AllocateId(); - const auto row_value = state.builder.AllocateId(); - const auto lane = state.builder.AllocateId(); - const auto lane8 = state.builder.AllocateId(); - const auto shift = state.builder.AllocateId(); - const auto upper = state.builder.AllocateId(); - const auto selected = state.builder.AllocateId(); - const auto shifted = state.builder.AllocateId(); - const auto index = state.builder.AllocateId(); - const auto target = state.builder.AllocateId(); - state.builder.AddFunction( - {spv::OpBitwiseAnd, TypeU32(state), row, subid, ConstantU32(state, 0xfffffff0u)}); - if (flags.x16) { - state.builder.AddFunction( - {spv::OpBitwiseXor, TypeU32(state), row_value, row, ConstantU32(state, 16)}); - } else { - state.builder.AddFunction({spv::OpCopyObject, TypeU32(state), row_value, row}); - } - state.builder.AddFunction( - {spv::OpBitwiseAnd, TypeU32(state), lane, subid, ConstantU32(state, 15)}); - state.builder.AddFunction( - {spv::OpBitwiseAnd, TypeU32(state), lane8, lane, ConstantU32(state, 7)}); - state.builder.AddFunction( - {spv::OpShiftLeftLogical, TypeU32(state), shift, lane8, ConstantU32(state, 2)}); - state.builder.AddFunction( - {spv::OpUGreaterThanEqual, TypeBool(state), upper, lane, ConstantU32(state, 8)}); - state.builder.AddFunction({spv::OpSelect, TypeU32(state), selected, upper, - ctx.Arg(inst, 2), ctx.Arg(inst, 1)}); - state.builder.AddFunction( - {spv::OpShiftRightLogical, TypeU32(state), shifted, selected, shift}); - state.builder.AddFunction( - {spv::OpBitwiseAnd, TypeU32(state), index, shifted, ConstantU32(state, 15)}); - state.builder.AddFunction({spv::OpBitwiseOr, TypeU32(state), target, row_value, index}); - const auto shuffled = ctx.Shuffle(inst, 0, target); - uint32_t result = shuffled; - if (!flags.fetch_inactive) { - const auto source_exec = ctx.Shuffle(inst, 3, target); - result = state.builder.AllocateId(); - state.builder.AddFunction({spv::OpSelect, TypeU32(state), result, source_exec, - shuffled, ConstantU32(state, 0)}); - } - ctx.Define(inst, result); - return true; - } - case IR::ValueOpcode::GetAttribute: - ctx.Define(inst, EmitAttribute(ctx, inst.Arg(0).U32(), inst.Arg(1).U32())); - return true; - case IR::ValueOpcode::GetInterpolationParameter: - ctx.Define(inst, EmitInterpolationParameter(ctx, inst.Arg(0).U32(), inst.Arg(1).U32(), - inst.Arg(2).U32())); - return true; - case IR::ValueOpcode::SetAttribute: EmitExport(ctx, inst); return true; - case IR::ValueOpcode::GetShaderBase: - // Guest S_GETPC values stay shader-relative in SPIR-V, matching the runtime ABI. The - // runtime descriptor evaluator supplies the mapped shader base for host-side planning. - ctx.Define(inst, ctx.Def(IR::Value(uint64_t {0}))); - return true; - case IR::ValueOpcode::GetSrtResource: - case IR::ValueOpcode::GetBufferResource: - case IR::ValueOpcode::GetAddressResource: - case IR::ValueOpcode::GetScratchResource: - case IR::ValueOpcode::GetImageResource: - case IR::ValueOpcode::GetSamplerResource: - case IR::ValueOpcode::MakeImageAddress: return true; - default: return false; + + uint32_t dword = 0; + if (!UserDataDwordIndex(state, reg, dword)) { + return ConstantU32(state, 0); + } else { + return EmitShaderDataDwordLoad(state, dword); } } +uint32_t EmitGetBuiltin(ValueEmitContext& ctx, IR::Value kind, IR::Value index) { + return EmitBuiltinU32(ctx, static_cast(kind.U32()), index.U32()); +} + +uint32_t EmitUndefU1(ValueEmitContext& ctx, const IR::Inst& inst) { + auto& state = ctx.state; + const auto result = state.builder.AllocateId(); + state.builder.AddFunction({spv::OpUndef, ctx.TypeId(inst.GetType()), result}); + return result; +} + +uint32_t EmitDppMoveU32(ValueEmitContext& ctx, const IR::Inst& inst) { + auto& state = ctx.state; + const auto flags = inst.Flags(); + const auto target = EmitDppTargetLane(state, flags.control); + const auto shuffled = ctx.Shuffle(inst, 0, target.lane); + if (flags.fetch_inactive) { + return shuffled; + } + const auto ballot = ctx.Ballot(inst.Arg(1)); + const auto source_active = EmitBallotLaneActiveBool(state, ballot, target.lane); + const auto can_fetch = state.builder.AllocateId(); + state.builder.AddFunction( + {spv::OpLogicalAnd, TypeBool(state), can_fetch, target.valid, source_active}); + return EmitNative(ctx, can_fetch, shuffled, + ConstantU32(state, 0)); +} + +uint32_t EmitDppUpdateU32(ValueEmitContext& ctx, const IR::Inst& inst) { + const auto flags = inst.Flags(); + const auto write = EmitDppWriteCondition(ctx, flags, ctx.Arg(inst, 2)); + return EmitNative(ctx, write, ctx.Arg(inst, 0), ctx.Arg(inst, 1)); +} + +uint32_t EmitWqmU64(ValueEmitContext& ctx, uint32_t value) { + return EmitWqmU64(ctx.state, value); +} + +uint32_t EmitLaneId(ValueEmitContext& ctx) { + auto& state = ctx.state; + return EmitSubgroupLocalInvocationId(state); +} + +uint32_t EmitBallot(ValueEmitContext& ctx, IR::Value predicate) { + return ctx.Ballot(predicate); +} + +uint32_t EmitReadFirstLane(ValueEmitContext& ctx, const IR::Inst& inst) { + const auto ballot = ctx.Ballot(inst.Arg(1)); + const auto lane = ctx.FirstLane(ballot); + return ctx.Shuffle(inst, 0, lane); +} + +uint32_t EmitReadLane(ValueEmitContext& ctx, const IR::Inst& inst) { + return ctx.Shuffle(inst, 0, ctx.Arg(inst, 1)); +} + +uint32_t EmitWriteLane(ValueEmitContext& ctx, const IR::Inst& inst) { + auto& state = ctx.state; + const auto hit = state.builder.AllocateId(); + state.builder.AddFunction({spv::OpIEqual, TypeBool(state), hit, + EmitSubgroupLocalInvocationId(state), ctx.Arg(inst, 2)}); + return EmitNative(ctx, hit, ctx.Arg(inst, 1), ctx.Arg(inst, 0)); +} + +uint32_t EmitPermlane16U32(ValueEmitContext& ctx, const IR::Inst& inst) { + auto& state = ctx.state; + const auto flags = inst.Flags(); + const auto subid = EmitSubgroupLocalInvocationId(state); + const auto row = state.builder.AllocateId(); + const auto row_value = state.builder.AllocateId(); + const auto lane = state.builder.AllocateId(); + const auto lane8 = state.builder.AllocateId(); + const auto shift = state.builder.AllocateId(); + const auto upper = state.builder.AllocateId(); + const auto selected = state.builder.AllocateId(); + const auto shifted = state.builder.AllocateId(); + const auto index = state.builder.AllocateId(); + const auto target = state.builder.AllocateId(); + state.builder.AddFunction( + {spv::OpBitwiseAnd, TypeU32(state), row, subid, ConstantU32(state, 0xfffffff0u)}); + if (flags.x16) { + state.builder.AddFunction( + {spv::OpBitwiseXor, TypeU32(state), row_value, row, ConstantU32(state, 16)}); + } else { + state.builder.AddFunction({spv::OpCopyObject, TypeU32(state), row_value, row}); + } + state.builder.AddFunction( + {spv::OpBitwiseAnd, TypeU32(state), lane, subid, ConstantU32(state, 15)}); + state.builder.AddFunction( + {spv::OpBitwiseAnd, TypeU32(state), lane8, lane, ConstantU32(state, 7)}); + state.builder.AddFunction( + {spv::OpShiftLeftLogical, TypeU32(state), shift, lane8, ConstantU32(state, 2)}); + state.builder.AddFunction( + {spv::OpUGreaterThanEqual, TypeBool(state), upper, lane, ConstantU32(state, 8)}); + state.builder.AddFunction( + {spv::OpSelect, TypeU32(state), selected, upper, ctx.Arg(inst, 2), ctx.Arg(inst, 1)}); + state.builder.AddFunction({spv::OpShiftRightLogical, TypeU32(state), shifted, selected, shift}); + state.builder.AddFunction( + {spv::OpBitwiseAnd, TypeU32(state), index, shifted, ConstantU32(state, 15)}); + state.builder.AddFunction({spv::OpBitwiseOr, TypeU32(state), target, row_value, index}); + const auto shuffled = ctx.Shuffle(inst, 0, target); + uint32_t result = shuffled; + if (!flags.fetch_inactive) { + const auto source_exec = ctx.Shuffle(inst, 3, target); + result = state.builder.AllocateId(); + state.builder.AddFunction( + {spv::OpSelect, TypeU32(state), result, source_exec, shuffled, ConstantU32(state, 0)}); + } + return result; +} + +uint32_t EmitGetAttribute(ValueEmitContext& ctx, const IR::Inst& inst) { + return EmitAttribute(ctx, inst.Arg(0).U32(), inst.Arg(1).U32()); +} + +uint32_t EmitGetInterpolationParameter(ValueEmitContext& ctx, const IR::Inst& inst) { + return EmitInterpolationParameter(ctx, inst.Arg(0).U32(), inst.Arg(1).U32(), inst.Arg(2).U32()); +} + +uint32_t EmitGetShaderBase(ValueEmitContext& ctx) { + // Guest S_GETPC values stay shader-relative in SPIR-V, matching the runtime ABI. The + // runtime descriptor evaluator supplies the mapped shader base for host-side planning. + return ctx.Def(IR::Value(uint64_t {0})); +} + + +void EmitUnreachable(ValueEmitContext& ctx, const IR::Inst& inst) { + ctx.Fail(inst, "must be lowered before SPIR-V emission"); +} + } // namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterImage.cpp b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterImage.cpp index 2644e0bbe..8952d4b54 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterImage.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterImage.cpp @@ -1,5 +1,5 @@ #include "graphics/guest_gpu/gpu_format.h" -#include "graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h" +#include "graphics/shader/recompiler/backend/spirv/spirvEmitterInstructions.h" #include "graphics/shader/recompiler/frontend/decode/ImageOps.h" #include @@ -556,23 +556,19 @@ uint32_t ImageAtomicOpcode(IR::ValueOpcode opcode) { } // namespace -bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) { +void EmitImage(ValueEmitContext& ctx, const IR::Inst& inst) { const auto op = inst.GetOpcode(); const auto image_info = IR::ImageOpcodeInfoOf(op); - if (image_info.access == IR::ImageAccess::None) { - return false; - } auto& state = ctx.state; const auto& mem = ctx.Memory(inst); const auto image_arg = inst.Arg(0); ctx.ResourceIndex(image_arg, IR::ValueOpcode::GetImageResource); const auto& image = state.program.info.images.at(mem.resource); const auto* address = ctx.ImageAddress(inst.Arg(image_info.needs_sampler ? 2 : 1)); - if (address == nullptr) return true; if (op == IR::ValueOpcode::ImageQueryDimensions) { state.builder.RequireCapability(spv::CapabilityImageQuery); ctx.Define(inst, QueryDimensions(ctx, mem, *address)); - return true; + return; } if (op == IR::ValueOpcode::ImageQueryLod) { state.builder.RequireCapability(spv::CapabilityImageQuery); @@ -594,7 +590,7 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) { state.builder.AddFunction({spv::OpCompositeConstruct, TypeU32Vector(state, 4), result, values[0], values[1], values[2], values[3]}); ctx.Define(inst, result); - return true; + return; } if (op == IR::ValueOpcode::ImageRead) { const auto dimension = image.dimension; @@ -623,7 +619,7 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) { return ResultVector(ctx, UnpackImageTexel(ctx, mem, color), numeric_class, false, mem); })); - return true; + return; } if (op == IR::ValueOpcode::ImageWrite) { const bool uint_image = image.numeric_class == Prospero::TextureNumericClass::Uint; @@ -637,7 +633,7 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) { const auto texel = StoreTexel(ctx, mem, ctx.Arg(inst, 2), uint_image); EmitStorageImageWrite(state, mem.resource, mip_lod, coord, texel); }); - return true; + return; } if (op == IR::ValueOpcode::ImageSampleRaw || op == IR::ValueOpcode::ImageGatherRaw) { const auto dimension = image.dimension; @@ -648,7 +644,7 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) { if (dref && state.program.info.images[mem.resource].conversion_format != Prospero::BufferFormat::kInvalid) { ctx.Fail(inst, "uses depth comparison with a packed integer image"); - return true; + return; } const auto coord = CoordF32(ctx, mem, *address, layout.coord, dimension_info.coordinate_components); @@ -658,16 +654,16 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) { HasFlag(mem, Decoder::ImageSampleFlagOffset) || HasFlag(mem, Decoder::ImageSampleFlagGatherHorizontal)) { ctx.Fail(inst, "has an unsupported 1D gather variant"); - return true; + return; } const auto sample = EmitOneDimensionalGatherLz(ctx, mem, coord, numeric_class); ctx.Define(inst, ResultVector(ctx, UnpackImageGather(ctx, mem, sample), numeric_class, false, mem, true)); - return true; + return; } if (dimension == ImageDimension::Dim1DArray) { ctx.Fail(inst, "has an unsupported 1D-array gather"); - return true; + return; } const auto sampled = MakeSampledImage(state, mem.resource, mem.sampler); const auto sample = state.builder.AllocateId(); @@ -709,7 +705,7 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) { } ctx.Define(inst, ResultVector(ctx, UnpackImageGather(ctx, mem, sample), result_numeric_class, false, mem, true)); - return true; + return; } const bool explicit_lod = HasFlag(mem, Decoder::ImageSampleFlagDerivative) || HasFlag(mem, Decoder::ImageSampleFlagLod) || @@ -770,7 +766,7 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) { result = UnpackImageTexel(ctx, mem, sample); } ctx.Define(inst, ResultVector(ctx, result, numeric_class, dref, mem)); - return true; + return; } const auto* handle = image_arg.ResolveInstruction(); const auto* source = image.source < state.program.descriptor_sources.size() @@ -779,13 +775,13 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) { if (handle == nullptr || source == nullptr || !source->indirect_image.has_value() || source->indirect_image->key_arg >= handle->NumArgs()) { ctx.Fail(inst, "has invalid indirect image key provenance"); - return true; + return; } const auto key = ctx.Def(handle->Arg(source->indirect_image->key_arg)); if (state.flattened_srt_variable == 0 || image.indirect_search_iterations == 0u || image.indirect_resources.size() < 2u) { ctx.Fail(inst, "has no indirect image runtime mapping"); - return true; + return; } const auto LoadMapping = [&](uint32_t index) { const auto pointer = state.builder.AllocateId(); @@ -867,7 +863,7 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) { result = UnpackImageTexel(ctx, mem, result); } ctx.Define(inst, ResultVector(ctx, result, numeric_class, dref, mem)); - return true; + return; } const auto atomic_opcode = ImageAtomicOpcode(op); if (atomic_opcode != 0u) { @@ -888,9 +884,9 @@ bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst) { EmitDeviceAtomicMemoryBarrier(state); return old; })); - return true; + return; } - return false; + ctx.Fail(inst, "has no image SPIR-V emitter"); } } // namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterInstructions.h b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterInstructions.h new file mode 100644 index 000000000..ed72601ed --- /dev/null +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterInstructions.h @@ -0,0 +1,296 @@ +#pragma once + +#include "graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h" + +namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter { +// Direct SPIR-V operations share emission as well as argument/result marshalling. +template +uint32_t EmitNative(ValueEmitContext& ctx, Args... args) { + const auto result = ctx.state.builder.AllocateId(); + ctx.state.builder.AddFunction({opcode, ctx.TypeId(type), result, args...}); + return result; +} +#define EMIT_NATIVE(name, opcode, type, ...) \ + inline constexpr auto Emit##name = EmitNative; +EMIT_NATIVE(BitCastU16F16, OpBitcast, U32, uint32_t) +inline constexpr auto EmitBitCastF16U16 = EmitBitCastU16F16; +inline constexpr auto EmitConvertU32U16 = EmitBitCastU16F16; +inline constexpr auto EmitConvertU32U8 = EmitBitCastU16F16; +inline constexpr auto EmitBitCastU32F32 = EmitBitCastU16F16; +EMIT_NATIVE(BitCastF32U32, OpBitcast, F32, uint32_t) +uint32_t EmitConvertU16U32(ValueEmitContext& ctx, uint32_t arg0); +uint32_t EmitConvertU8U32(ValueEmitContext& ctx, uint32_t arg0); +uint32_t EmitConvertF16F32(ValueEmitContext& ctx, uint32_t arg0); +uint32_t EmitConvertF32F16(ValueEmitContext& ctx, uint32_t arg0); +uint32_t EmitConvertS32F32(ValueEmitContext& ctx, uint32_t arg0); +uint32_t EmitConvertU32F32(ValueEmitContext& ctx, uint32_t arg0); +uint32_t EmitConvertF32S32(ValueEmitContext& ctx, uint32_t arg0); +EMIT_NATIVE(ConvertF32U32, OpConvertUToF, F32, uint32_t) +EMIT_NATIVE(CompositeConstructU64, OpCompositeConstruct, U64, uint32_t, uint32_t) +EMIT_NATIVE(CompositeConstructU32x2, OpCompositeConstruct, U32x2, uint32_t, uint32_t) +EMIT_NATIVE(CompositeConstructU32x3, OpCompositeConstruct, U32x3, uint32_t, uint32_t, uint32_t) +EMIT_NATIVE(CompositeConstructF32x2, OpCompositeConstruct, F32x2, uint32_t, uint32_t) +EMIT_NATIVE(CompositeConstructU32x4, OpCompositeConstruct, U32x4, uint32_t, uint32_t, uint32_t, + uint32_t) +uint32_t EmitCompositeExtractU64(ValueEmitContext& ctx, uint32_t arg0, IR::Value arg1); +inline constexpr auto EmitCompositeExtractU32x2 = EmitCompositeExtractU64; +inline constexpr auto EmitCompositeExtractU32x3 = EmitCompositeExtractU64; +inline constexpr auto EmitCompositeExtractU32x4 = EmitCompositeExtractU64; +uint32_t EmitPackHalf2x16(ValueEmitContext& ctx, uint32_t arg0); +uint32_t EmitPackSnorm2x16(ValueEmitContext& ctx, uint32_t arg0); +uint32_t EmitPackUnorm2x16(ValueEmitContext& ctx, uint32_t arg0); +uint32_t EmitPackFloat2x16Rtz(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1); +uint32_t EmitFPAbs32(ValueEmitContext& ctx, uint32_t arg0); +uint32_t EmitFPNeg32(ValueEmitContext& ctx, uint32_t arg0); +uint32_t EmitFPSaturate32(ValueEmitContext& ctx, uint32_t arg0); +EMIT_NATIVE(BitFieldInsert, OpBitFieldInsert, U32, uint32_t, uint32_t, uint32_t, uint32_t) +EMIT_NATIVE(BitFieldUExtract, OpBitFieldUExtract, U32, uint32_t, uint32_t, uint32_t) +EMIT_NATIVE(BitFieldSExtract, OpBitFieldSExtract, U32, uint32_t, uint32_t, uint32_t) +EMIT_NATIVE(SelectU1, OpSelect, U1, uint32_t, uint32_t, uint32_t) +EMIT_NATIVE(SelectU32, OpSelect, U32, uint32_t, uint32_t, uint32_t) +EMIT_NATIVE(SelectF32, OpSelect, F32, uint32_t, uint32_t, uint32_t) +EMIT_NATIVE(IAdd32, OpIAdd, U32, uint32_t, uint32_t) +EMIT_NATIVE(ISub32, OpISub, U32, uint32_t, uint32_t) +EMIT_NATIVE(IMul32, OpIMul, U32, uint32_t, uint32_t) +EMIT_NATIVE(UDiv32, OpUDiv, U32, uint32_t, uint32_t) +uint32_t EmitIAdd64(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1); +uint32_t EmitISub64(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1); +uint32_t EmitIMul64(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1); +EMIT_NATIVE(IAddCarry32, OpIAddCarry, U32x2, uint32_t, uint32_t) +uint32_t EmitSMulHi(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1); +uint32_t EmitUMulHi(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1); +uint32_t EmitIAbs32(ValueEmitContext& ctx, uint32_t arg0); +EMIT_NATIVE(ShiftLeftLogical32, OpShiftLeftLogical, U32, uint32_t, uint32_t) +EMIT_NATIVE(ShiftRightLogical32, OpShiftRightLogical, U32, uint32_t, uint32_t) +EMIT_NATIVE(ShiftRightArithmetic32, OpShiftRightArithmetic, U32, uint32_t, uint32_t) +uint32_t EmitShiftLeftLogical64(ValueEmitContext& ctx, uint32_t arg0, IR::Value arg1); +uint32_t EmitShiftRightLogical64(ValueEmitContext& ctx, uint32_t arg0, IR::Value arg1); +uint32_t EmitShiftRightArithmetic64(ValueEmitContext& ctx, uint32_t arg0, IR::Value arg1); +EMIT_NATIVE(BitwiseAnd32, OpBitwiseAnd, U32, uint32_t, uint32_t) +EMIT_NATIVE(BitwiseOr32, OpBitwiseOr, U32, uint32_t, uint32_t) +EMIT_NATIVE(BitwiseXor32, OpBitwiseXor, U32, uint32_t, uint32_t) +EMIT_NATIVE(BitwiseNot32, OpNot, U32, uint32_t) +uint32_t EmitBitwiseAnd64(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1); +EMIT_NATIVE(BitReverse32, OpBitReverse, U32, uint32_t) +EMIT_NATIVE(BitCount32, OpBitCount, U32, uint32_t) +uint32_t EmitBitCount64(ValueEmitContext& ctx, uint32_t arg0); +uint32_t EmitFindILsb32(ValueEmitContext& ctx, uint32_t arg0); +uint32_t EmitFindUMsb32(ValueEmitContext& ctx, uint32_t arg0); +uint32_t EmitFindUMsb64(ValueEmitContext& ctx, uint32_t arg0); +uint32_t EmitSMin32(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1); +uint32_t EmitSMax32(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1); +uint32_t EmitUMin32(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1); +uint32_t EmitUMax32(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1); +uint32_t EmitSMinTri32(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1, uint32_t arg2); +uint32_t EmitSMaxTri32(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1, uint32_t arg2); +uint32_t EmitUMinTri32(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1, uint32_t arg2); +uint32_t EmitUMaxTri32(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1, uint32_t arg2); +uint32_t EmitSMedTri32(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1, uint32_t arg2); +uint32_t EmitUMedTri32(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1, uint32_t arg2); +EMIT_NATIVE(SLessThan32, OpSLessThan, U1, uint32_t, uint32_t) +EMIT_NATIVE(ULessThan32, OpULessThan, U1, uint32_t, uint32_t) +EMIT_NATIVE(IEqual32, OpIEqual, U1, uint32_t, uint32_t) +EMIT_NATIVE(SLessThanEqual32, OpSLessThanEqual, U1, uint32_t, uint32_t) +EMIT_NATIVE(ULessThanEqual32, OpULessThanEqual, U1, uint32_t, uint32_t) +EMIT_NATIVE(SGreaterThan32, OpSGreaterThan, U1, uint32_t, uint32_t) +EMIT_NATIVE(UGreaterThan32, OpUGreaterThan, U1, uint32_t, uint32_t) +EMIT_NATIVE(INotEqual32, OpINotEqual, U1, uint32_t, uint32_t) +EMIT_NATIVE(SGreaterThanEqual32, OpSGreaterThanEqual, U1, uint32_t, uint32_t) +EMIT_NATIVE(UGreaterThanEqual32, OpUGreaterThanEqual, U1, uint32_t, uint32_t) +uint32_t EmitIEqual64(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1); +uint32_t EmitINotEqual64(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1); +uint32_t EmitULessThan64(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1); +uint32_t EmitSLessThan64(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1); +uint32_t EmitUGreaterThan64(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1); +EMIT_NATIVE(LogicalOr, OpLogicalOr, U1, uint32_t, uint32_t) +EMIT_NATIVE(LogicalAnd, OpLogicalAnd, U1, uint32_t, uint32_t) +EMIT_NATIVE(LogicalXor, OpLogicalNotEqual, U1, uint32_t, uint32_t) +EMIT_NATIVE(LogicalNot, OpLogicalNot, U1, uint32_t) +EMIT_NATIVE(FPOrdEqual32, OpFOrdEqual, U1, uint32_t, uint32_t) +EMIT_NATIVE(FPUnordEqual32, OpFUnordEqual, U1, uint32_t, uint32_t) +EMIT_NATIVE(FPOrdNotEqual32, OpFOrdNotEqual, U1, uint32_t, uint32_t) +EMIT_NATIVE(FPUnordNotEqual32, OpFUnordNotEqual, U1, uint32_t, uint32_t) +EMIT_NATIVE(FPOrdLessThan32, OpFOrdLessThan, U1, uint32_t, uint32_t) +EMIT_NATIVE(FPUnordLessThan32, OpFUnordLessThan, U1, uint32_t, uint32_t) +EMIT_NATIVE(FPOrdGreaterThan32, OpFOrdGreaterThan, U1, uint32_t, uint32_t) +EMIT_NATIVE(FPUnordGreaterThan32, OpFUnordGreaterThan, U1, uint32_t, uint32_t) +EMIT_NATIVE(FPOrdLessThanEqual32, OpFOrdLessThanEqual, U1, uint32_t, uint32_t) +EMIT_NATIVE(FPUnordLessThanEqual32, OpFUnordLessThanEqual, U1, uint32_t, uint32_t) +EMIT_NATIVE(FPOrdGreaterThanEqual32, OpFOrdGreaterThanEqual, U1, uint32_t, uint32_t) +EMIT_NATIVE(FPUnordGreaterThanEqual32, OpFUnordGreaterThanEqual, U1, uint32_t, uint32_t) +uint32_t EmitFPIsNan32(ValueEmitContext& ctx, uint32_t arg0); +uint32_t EmitFPCmpClass32(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1); +EMIT_NATIVE(FPAdd32, OpFAdd, F32, uint32_t, uint32_t) +EMIT_NATIVE(FPSub32, OpFSub, F32, uint32_t, uint32_t) +EMIT_NATIVE(FPMul32, OpFMul, F32, uint32_t, uint32_t) +uint32_t EmitFPFma32(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1, uint32_t arg2); +uint32_t EmitFPMin32(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1); +uint32_t EmitFPMax32(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1); +uint32_t EmitFPMinTri32(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1, uint32_t arg2); +uint32_t EmitFPMaxTri32(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1, uint32_t arg2); +uint32_t EmitFPMedTri32(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1, uint32_t arg2); +uint32_t EmitFPRecip32(ValueEmitContext& ctx, uint32_t arg0); +uint32_t EmitFPRecipIFlag32(ValueEmitContext& ctx, uint32_t arg0); +uint32_t EmitFPRecipSqrt32(ValueEmitContext& ctx, uint32_t arg0); +uint32_t EmitFPSqrt(ValueEmitContext& ctx, uint32_t arg0); +uint32_t EmitFPExp2(ValueEmitContext& ctx, uint32_t arg0); +uint32_t EmitFPLog2(ValueEmitContext& ctx, uint32_t arg0); +uint32_t EmitFPLdexp(ValueEmitContext& ctx, uint32_t arg0, uint32_t arg1); +uint32_t EmitFPRoundEven32(ValueEmitContext& ctx, uint32_t arg0); +uint32_t EmitFPFloor32(ValueEmitContext& ctx, uint32_t arg0); +uint32_t EmitFPCeil32(ValueEmitContext& ctx, uint32_t arg0); +uint32_t EmitFPTrunc32(ValueEmitContext& ctx, uint32_t arg0); +uint32_t EmitFPFract32(ValueEmitContext& ctx, uint32_t arg0); +uint32_t EmitFPSin(ValueEmitContext& ctx, uint32_t arg0); +uint32_t EmitFPCos(ValueEmitContext& ctx, uint32_t arg0); +#undef EMIT_NATIVE + +uint32_t EmitIdentity(ValueEmitContext& ctx, uint32_t value); +void EmitVoid(ValueEmitContext&); +inline constexpr auto EmitReference = EmitVoid; +inline constexpr auto EmitReferenceU32 = EmitVoid; +inline constexpr auto EmitControlNop = EmitVoid; +inline constexpr auto EmitWaitcnt = EmitVoid; +inline constexpr auto EmitSendmsg = EmitVoid; +inline constexpr auto EmitTtraceData = EmitVoid; +inline constexpr auto EmitInstPrefetch = EmitVoid; +void EmitBarrier(ValueEmitContext& ctx); +void EmitMeshAllocate(ValueEmitContext& ctx, const IR::Inst& inst); +uint32_t EmitMeshDrawParameter(ValueEmitContext& ctx, const IR::Inst& inst); +uint32_t EmitGetUserData(ValueEmitContext& ctx, IR::ScalarReg reg); +uint32_t EmitGetBuiltin(ValueEmitContext& ctx, IR::Value kind, IR::Value index); +uint32_t EmitUndefU1(ValueEmitContext& ctx, const IR::Inst& inst); +inline constexpr auto EmitUndefU8 = EmitUndefU1; +inline constexpr auto EmitUndefU16 = EmitUndefU1; +inline constexpr auto EmitUndefU32 = EmitUndefU1; +inline constexpr auto EmitUndefU64 = EmitUndefU1; +uint32_t EmitDppMoveU32(ValueEmitContext& ctx, const IR::Inst& inst); +uint32_t EmitDppUpdateU32(ValueEmitContext& ctx, const IR::Inst& inst); +uint32_t EmitWqmU64(ValueEmitContext& ctx, uint32_t value); +uint32_t EmitLaneId(ValueEmitContext& ctx); +uint32_t EmitBallot(ValueEmitContext& ctx, IR::Value predicate); +uint32_t EmitReadFirstLane(ValueEmitContext& ctx, const IR::Inst& inst); +uint32_t EmitReadLane(ValueEmitContext& ctx, const IR::Inst& inst); +uint32_t EmitWriteLane(ValueEmitContext& ctx, const IR::Inst& inst); +uint32_t EmitPermlane16U32(ValueEmitContext& ctx, const IR::Inst& inst); +uint32_t EmitGetAttribute(ValueEmitContext& ctx, const IR::Inst& inst); +uint32_t EmitGetInterpolationParameter(ValueEmitContext& ctx, const IR::Inst& inst); +void EmitSetAttribute(ValueEmitContext& ctx, const IR::Inst& inst); +uint32_t EmitGetShaderBase(ValueEmitContext& ctx); +inline constexpr auto EmitGetSrtResource = EmitVoid; +inline constexpr auto EmitGetBufferResource = EmitGetSrtResource; +inline constexpr auto EmitGetAddressResource = EmitGetSrtResource; +inline constexpr auto EmitGetScratchResource = EmitGetSrtResource; +inline constexpr auto EmitGetImageResource = EmitGetSrtResource; +inline constexpr auto EmitGetSamplerResource = EmitGetSrtResource; +inline constexpr auto EmitMakeImageAddress = EmitGetSrtResource; +void EmitMemory(ValueEmitContext& ctx, const IR::Inst& inst); +inline constexpr auto EmitBpermuteU32 = EmitMemory; +inline constexpr auto EmitReadConst = EmitMemory; +inline constexpr auto EmitReadConstBuffer = EmitMemory; +inline constexpr auto EmitLoadAddressU8 = EmitMemory; +inline constexpr auto EmitLoadAddressU16 = EmitMemory; +inline constexpr auto EmitLoadAddressU32 = EmitMemory; +inline constexpr auto EmitStoreAddressU8 = EmitMemory; +inline constexpr auto EmitStoreAddressU16 = EmitMemory; +inline constexpr auto EmitStoreAddressU32 = EmitMemory; +inline constexpr auto EmitLoadBufferU8 = EmitMemory; +inline constexpr auto EmitLoadBufferU16 = EmitMemory; +inline constexpr auto EmitLoadBufferU32 = EmitMemory; +inline constexpr auto EmitLoadBufferU32x2 = EmitMemory; +inline constexpr auto EmitLoadBufferU32x3 = EmitMemory; +inline constexpr auto EmitLoadBufferU32x4 = EmitMemory; +inline constexpr auto EmitStoreBufferU8 = EmitMemory; +inline constexpr auto EmitStoreBufferU16 = EmitMemory; +inline constexpr auto EmitStoreBufferU32 = EmitMemory; +inline constexpr auto EmitStoreBufferU32x2 = EmitMemory; +inline constexpr auto EmitStoreBufferU32x3 = EmitMemory; +inline constexpr auto EmitStoreBufferU32x4 = EmitMemory; +inline constexpr auto EmitBufferAtomicSwap32 = EmitMemory; +inline constexpr auto EmitBufferAtomicCmpSwap32 = EmitMemory; +inline constexpr auto EmitBufferAtomicSwap64 = EmitMemory; +inline constexpr auto EmitBufferAtomicIAdd32 = EmitMemory; +inline constexpr auto EmitBufferAtomicISub32 = EmitMemory; +inline constexpr auto EmitBufferAtomicSMin32 = EmitMemory; +inline constexpr auto EmitBufferAtomicUMin32 = EmitMemory; +inline constexpr auto EmitBufferAtomicSMax32 = EmitMemory; +inline constexpr auto EmitBufferAtomicUMax32 = EmitMemory; +inline constexpr auto EmitBufferAtomicAnd32 = EmitMemory; +inline constexpr auto EmitBufferAtomicOr32 = EmitMemory; +inline constexpr auto EmitBufferAtomicOr64 = EmitMemory; +inline constexpr auto EmitBufferAtomicXor32 = EmitMemory; +inline constexpr auto EmitBufferAtomicFMin32 = EmitMemory; +inline constexpr auto EmitBufferAtomicFMax32 = EmitMemory; +inline constexpr auto EmitLoadSharedU8 = EmitMemory; +inline constexpr auto EmitLoadSharedU16 = EmitMemory; +inline constexpr auto EmitLoadSharedU32 = EmitMemory; +inline constexpr auto EmitLoadSharedU32x2 = EmitMemory; +inline constexpr auto EmitLoadSharedU32x3 = EmitMemory; +inline constexpr auto EmitLoadSharedU32x4 = EmitMemory; +inline constexpr auto EmitWriteSharedU8 = EmitMemory; +inline constexpr auto EmitWriteSharedU16 = EmitMemory; +inline constexpr auto EmitWriteSharedU32 = EmitMemory; +inline constexpr auto EmitWriteSharedU32x2 = EmitMemory; +inline constexpr auto EmitWriteSharedU32x3 = EmitMemory; +inline constexpr auto EmitWriteSharedU32x4 = EmitMemory; +inline constexpr auto EmitSharedAtomicFMin32 = EmitMemory; +inline constexpr auto EmitSharedAtomicFMax32 = EmitMemory; +inline constexpr auto EmitSharedAtomicSwap32 = EmitMemory; +inline constexpr auto EmitSharedAtomicIAdd32 = EmitMemory; +inline constexpr auto EmitSharedAtomicISub32 = EmitMemory; +inline constexpr auto EmitSharedAtomicInc32 = EmitMemory; +inline constexpr auto EmitSharedAtomicDec32 = EmitMemory; +inline constexpr auto EmitSharedAtomicSMin32 = EmitMemory; +inline constexpr auto EmitSharedAtomicUMin32 = EmitMemory; +inline constexpr auto EmitSharedAtomicSMax32 = EmitMemory; +inline constexpr auto EmitSharedAtomicUMax32 = EmitMemory; +inline constexpr auto EmitSharedAtomicAnd32 = EmitMemory; +inline constexpr auto EmitSharedAtomicOr32 = EmitMemory; +inline constexpr auto EmitSharedAtomicXor32 = EmitMemory; +inline constexpr auto EmitDataAppend = EmitMemory; +inline constexpr auto EmitDataConsume = EmitMemory; +inline constexpr auto EmitSwizzleU32 = EmitMemory; +void EmitImage(ValueEmitContext& ctx, const IR::Inst& inst); +inline constexpr auto EmitImageQueryDimensions = EmitImage; +inline constexpr auto EmitImageQueryLod = EmitImage; +inline constexpr auto EmitImageRead = EmitImage; +inline constexpr auto EmitImageWrite = EmitImage; +inline constexpr auto EmitImageSampleRaw = EmitImage; +inline constexpr auto EmitImageGatherRaw = EmitImage; +inline constexpr auto EmitImageAtomicSwap32 = EmitImage; +inline constexpr auto EmitImageAtomicIAdd32 = EmitImage; +inline constexpr auto EmitImageAtomicUMin32 = EmitImage; +inline constexpr auto EmitImageAtomicUMax32 = EmitImage; +inline constexpr auto EmitImageAtomicAnd32 = EmitImage; +inline constexpr auto EmitImageAtomicOr32 = EmitImage; +inline constexpr auto EmitImageAtomicXor32 = EmitImage; +void EmitUnreachable(ValueEmitContext& ctx, const IR::Inst& inst); +inline constexpr auto EmitPhi = EmitUnreachable; +inline constexpr auto EmitGetThreadBitScalarRegister = EmitUnreachable; +inline constexpr auto EmitSetThreadBitScalarRegister = EmitUnreachable; +inline constexpr auto EmitGetScalarMaskTag = EmitUnreachable; +inline constexpr auto EmitSetScalarMaskTag = EmitUnreachable; +inline constexpr auto EmitGetScalarRegister = EmitUnreachable; +inline constexpr auto EmitSetScalarRegister = EmitUnreachable; +inline constexpr auto EmitGetVectorRegister = EmitUnreachable; +inline constexpr auto EmitSetVectorRegister = EmitUnreachable; +inline constexpr auto EmitGetGotoVariable = EmitUnreachable; +inline constexpr auto EmitSetGotoVariable = EmitUnreachable; +inline constexpr auto EmitGetScc = EmitUnreachable; +inline constexpr auto EmitSetScc = EmitUnreachable; +inline constexpr auto EmitGetExec = EmitUnreachable; +inline constexpr auto EmitSetExec = EmitUnreachable; +inline constexpr auto EmitGetExecLo = EmitUnreachable; +inline constexpr auto EmitSetExecLo = EmitUnreachable; +inline constexpr auto EmitGetExecHi = EmitUnreachable; +inline constexpr auto EmitSetExecHi = EmitUnreachable; +inline constexpr auto EmitGetVcc = EmitUnreachable; +inline constexpr auto EmitSetVcc = EmitUnreachable; +inline constexpr auto EmitGetVccLo = EmitUnreachable; +inline constexpr auto EmitSetVccLo = EmitUnreachable; +inline constexpr auto EmitGetVccHi = EmitUnreachable; +inline constexpr auto EmitSetVccHi = EmitUnreachable; +inline constexpr auto EmitGetM0 = EmitUnreachable; +inline constexpr auto EmitSetM0 = EmitUnreachable; + +} // namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h index 8396db3fd..01a420149 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h @@ -196,8 +196,6 @@ struct ValueEmitContext { uint32_t Shuffle(const IR::Inst& inst, size_t index, uint32_t lane); uint32_t Result(const IR::Inst& inst); uint32_t TypeId(IR::Type type) const; - uint32_t Emit(const IR::Inst& inst, uint32_t opcode, IR::Type type, - std::initializer_list args); uint32_t Define(const IR::Inst& inst, uint32_t value); uint32_t ResourceIndex(IR::Value value, IR::ValueOpcode opcode); const IR::Inst* ImageAddress(IR::Value value); @@ -490,14 +488,6 @@ uint32_t EmitFAbsValue(EmitterState& state, uint32_t value); uint32_t EmitF16BitsToF32(EmitterState& state, uint32_t bits); -bool EmitValueAlu(ValueEmitContext& ctx, const IR::Inst& inst); - -bool EmitValueFlow(ValueEmitContext& ctx, const IR::Inst& inst); - -bool EmitValueMemory(ValueEmitContext& ctx, const IR::Inst& inst); - -bool EmitValueImage(ValueEmitContext& ctx, const IR::Inst& inst); - void EmitProgram(EmitterState& state); void DefineGetBdaPointer(EmitterState& state); diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterMemory.cpp b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterMemory.cpp index bf3f82f7f..0ba050a56 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterMemory.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterMemory.cpp @@ -1,4 +1,4 @@ -#include "graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h" +#include "graphics/shader/recompiler/backend/spirv/spirvEmitterInstructions.h" #include "graphics/host_gpu/renderer/cache/bufferCache.h" @@ -1044,7 +1044,7 @@ void DefineGetBdaPointer(EmitterState& state) { state.builder.AddFunction({spv::OpFunctionEnd}); } -bool EmitValueMemory(ValueEmitContext& ctx, const IR::Inst& inst) { +void EmitMemory(ValueEmitContext& ctx, const IR::Inst& inst) { auto& state = ctx.state; const auto op = inst.GetOpcode(); const auto buffer_components = IR::BufferComponentCount(op); @@ -1052,11 +1052,11 @@ bool EmitValueMemory(ValueEmitContext& ctx, const IR::Inst& inst) { const auto access = IR::BufferAccessOf(op); if (access == IR::BufferAccess::Read) { ctx.Define(inst, LoadWideBuffer(ctx, inst, buffer_components)); - return true; + return; } if (access == IR::BufferAccess::Write) { StoreWideBuffer(ctx, inst, buffer_components); - return true; + return; } } const auto shared_components = IR::SharedComponentCount(op); @@ -1066,23 +1066,23 @@ bool EmitValueMemory(ValueEmitContext& ctx, const IR::Inst& inst) { } else { StoreWideShared(ctx, inst, shared_components); } - return true; + return; } if ((op == IR::ValueOpcode::LoadAddressU32 || op == IR::ValueOpcode::ReadConstBuffer) && ctx.Memory(inst).planning_only) { - return true; + return; } if (op == IR::ValueOpcode::ReadConst) { if (state.flattened_srt_variable == 0) { ctx.Fail(inst, "requires the flattened SRT descriptor"); - return true; + return; } const auto pointer = state.builder.AllocateId(); state.builder.AddFunction({spv::OpAccessChain, TypeStorageBufferElementPointer(state), pointer, state.flattened_srt_variable, ConstantU32(state, 0), ctx.Arg(inst, 1)}); - ctx.Emit(inst, spv::OpLoad, IR::Type::U32, {pointer}); - return true; + ctx.Define(inst, EmitNative(ctx, pointer)); + return; } if (op == IR::ValueOpcode::ReadConstBuffer) { auto mem = ctx.Memory(inst); @@ -1101,13 +1101,13 @@ bool EmitValueMemory(ValueEmitContext& ctx, const IR::Inst& inst) { EmitMemoryElementPointer(state, access, element)}); return value; })); - return true; + return; } const auto address_info = IR::AddressOpcodeInfoOf(op); const bool load_address = address_info.access == IR::AddressAccess::Read; if (load_address && ctx.Memory(inst).kind != IR::ResourceKind::Scratch) { ctx.Define(inst, LoadBda(ctx, inst, ctx.Memory(inst), address_info.data_bits)); - return true; + return; } const bool load_buffer = op == IR::ValueOpcode::LoadBufferU8 || op == IR::ValueOpcode::LoadBufferU16 || @@ -1127,7 +1127,7 @@ bool EmitValueMemory(ValueEmitContext& ctx, const IR::Inst& inst) { else value = LoadWord(ctx, inst, mem); ctx.Define(inst, value); - return true; + return; } const bool store_address = address_info.access == IR::AddressAccess::Write; const bool store_buffer = op == IR::ValueOpcode::StoreBufferU8 || @@ -1146,35 +1146,35 @@ bool EmitValueMemory(ValueEmitContext& ctx, const IR::Inst& inst) { StoreSubword(ctx, inst, mem, 16); else StoreWord(ctx, inst, mem); - return true; + return; } const auto atomic_opcode = SpirvAtomicOpcode(op); if (atomic_opcode != 0 && inst.GetType() == IR::Type::U64) { ctx.Define(inst, EmitBufferAtomic64(ctx, inst, ctx.Memory(inst))); - return true; + return; } if (atomic_opcode != 0) { ctx.Define(inst, EmitAtomic(ctx, inst, ctx.Memory(inst))); - return true; + return; } if (op == IR::ValueOpcode::BufferAtomicFMin32 || op == IR::ValueOpcode::BufferAtomicFMax32) { ctx.Define(inst, FloatAtomic(ctx, inst, ctx.Memory(inst), op == IR::ValueOpcode::BufferAtomicFMax32)); - return true; + return; } if (op == IR::ValueOpcode::SharedAtomicInc32 || op == IR::ValueOpcode::SharedAtomicDec32) { const auto replacement = op == IR::ValueOpcode::SharedAtomicInc32 ? AtomicIncrement : AtomicDecrement; ctx.Define(inst, EmitAtomicUpdate(ctx, inst, ctx.Memory(inst), replacement)); - return true; + return; } if (op == IR::ValueOpcode::SharedAtomicFMin32 || op == IR::ValueOpcode::SharedAtomicFMax32) { SharedFloatAtomic(ctx, inst, ctx.Memory(inst), op == IR::ValueOpcode::SharedAtomicFMax32); - return true; + return; } if (op == IR::ValueOpcode::DataAppend || op == IR::ValueOpcode::DataConsume) { ctx.Define(inst, AppendConsume(ctx, inst, op == IR::ValueOpcode::DataAppend)); - return true; + return; } if (op == IR::ValueOpcode::SwizzleU32 || op == IR::ValueOpcode::BpermuteU32) { uint32_t source = ctx.Arg(inst, 0); @@ -1197,9 +1197,9 @@ bool EmitValueMemory(ValueEmitContext& ctx, const IR::Inst& inst) { target = Binary(state, spv::OpBitwiseOr, TypeU32(state), base, index); } ctx.Define(inst, EmitDsMaskedLaneRead(state, source, target, ctx.Arg(inst, 2))); - return true; + return; } - return false; + ctx.Fail(inst, "has no memory SPIR-V emitter"); } } // namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterProgram.cpp b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterProgram.cpp index 69b6b4a7e..0d3f7de59 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterProgram.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterProgram.cpp @@ -1,4 +1,4 @@ -#include "graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h" +#include "graphics/shader/recompiler/backend/spirv/spirvEmitterInstructions.h" #include "common/assert.h" @@ -6,7 +6,9 @@ #include #include #include +#include #include +#include namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter { namespace { @@ -227,12 +229,46 @@ uint32_t EmitDispatcherNextPc(ValueEmitContext& ctx, const DispatcherFunctionSta } } -void EmitDirectInstruction(ValueEmitContext& ctx, const IR::Inst& inst) { - if (EmitValueFlow(ctx, inst) || EmitValueAlu(ctx, inst) || EmitValueMemory(ctx, inst) || - EmitValueImage(ctx, inst)) { - return; +template +decltype(auto) Arg(ValueEmitContext& ctx, const IR::Inst& inst, size_t index) { + if constexpr (std::is_same_v) { + return inst; + } else if constexpr (std::is_same_v) { + return inst.Arg(index); + } else if constexpr (std::is_same_v) { + return inst.Arg(index).ScalarRegister(); + } else { + static_assert(std::is_same_v); + return ctx.Def(inst.Arg(index)); + } +} + +template +void Invoke(Return (*emit)(ValueEmitContext&, Args...), ValueEmitContext& ctx, + const IR::Inst& inst) { + // A full instruction keeps metadata and predicated/lane operand loads lazy. + constexpr bool has_inst = (std::is_same_v || ...); + [&](std::index_sequence) { + const auto call = [&] { + return emit(ctx, Arg(ctx, inst, I - (has_inst && I != 0))...); + }; + if constexpr (std::is_void_v) { + call(); + } else { + static_assert(std::is_same_v); + ctx.Define(inst, call()); + } + }(std::index_sequence_for {}); +} + +void EmitDirectInstruction(ValueEmitContext& ctx, const IR::Inst& inst) { + switch (inst.GetOpcode()) { +#define VALUE_OPCODE(name, ...) \ + case IR::ValueOpcode::name: return Invoke(Emit##name, ctx, inst); +#include "graphics/shader/recompiler/ir/opcodes/ValueOpcodes.inc" +#undef VALUE_OPCODE + default: ctx.Fail(inst, "has no direct SPIR-V emitter"); } - ctx.Fail(inst, "has no direct SPIR-V emitter"); } void EmitStructuredInstruction(ValueEmitContext& ctx, StructuredFunctionState& structured, @@ -534,16 +570,6 @@ uint32_t ValueEmitContext::Result(const IR::Inst& inst) { return id; } -uint32_t ValueEmitContext::Emit(const IR::Inst& inst, uint32_t opcode, IR::Type type, - std::initializer_list args) { - const auto type_id = TypeId(type); - const auto result = Result(inst); - std::vector words {opcode, type_id, result}; - words.insert(words.end(), args.begin(), args.end()); - state.builder.AddFunction(words); - return result; -} - uint32_t ValueEmitContext::Define(const IR::Inst& inst, uint32_t value) { if (const auto found = definitions.find(&inst); found != definitions.end()) { if (found->second != value) { diff --git a/tests/shaderCfgTests.cpp b/tests/shaderCfgTests.cpp index e9ff82eb5..f4c18c7f4 100644 --- a/tests/shaderCfgTests.cpp +++ b/tests/shaderCfgTests.cpp @@ -8689,8 +8689,14 @@ void TestNewShaderRecompilerDispatcherSpillsU32x3() { definition.Emit(IR::ValueOpcode::CompositeConstructU32x3, {IR::Value(1u), IR::Value(2u), IR::Value(3u)}); IR::IREmitter use(program.blocks[2]); - use.Emit(IR::ValueOpcode::CompositeExtractU32x3, {vector, IR::Value(2u)}); - use.Emit(IR::ValueOpcode::CompositeExtractU32x3, {vector, IR::Value(1u)}); + const auto high = + use.Emit(IR::ValueOpcode::CompositeExtractU32x3, {vector, IR::Value(2u)}); + const auto middle = + use.Emit(IR::ValueOpcode::CompositeExtractU32x3, {vector, IR::Value(1u)}); + // Composite indices stay literal; arithmetic immediates become constant IDs. + // Resolving either arithmetic input must reuse the same dispatcher load. + const auto sum = use.Emit(IR::ValueOpcode::IAdd32, {high, IR::Value(7u)}); + use.Emit(IR::ValueOpcode::IAdd32, {sum, middle}); auto spirv = ShaderRecompiler::Spirv::EmitProgram(program, options.input_info);