mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-16 01:51:17 +00:00
2501 lines
98 KiB
C#
2501 lines
98 KiB
C#
// Copyright (C) 2026 SharpEmu Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
namespace SharpEmu.Libs.Agc;
|
|
|
|
internal static partial class Gen5SpirvTranslator
|
|
{
|
|
private sealed partial class CompilationContext
|
|
{
|
|
private bool TryEmitVectorAlu(
|
|
Gen5ShaderInstruction instruction,
|
|
out string error)
|
|
{
|
|
error = string.Empty;
|
|
if (instruction.Opcode == "VNop")
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (instruction.Opcode.StartsWith("VCmp", StringComparison.Ordinal))
|
|
{
|
|
return TryEmitVectorCompare(instruction, out error);
|
|
}
|
|
|
|
if (!TryGetVectorDestination(instruction, out var destination))
|
|
{
|
|
error = "missing vector destination";
|
|
return false;
|
|
}
|
|
|
|
uint result;
|
|
switch (instruction.Opcode)
|
|
{
|
|
case "VMovB32":
|
|
case "VReadlaneB32":
|
|
case "VReadfirstlaneB32":
|
|
result = GetRawSource(instruction, 0);
|
|
break;
|
|
case "VCndmaskB32":
|
|
{
|
|
var condition = instruction.Sources.Count > 2
|
|
? IsCurrentLaneSet(GetRawSource64(instruction, 2))
|
|
: Load(_boolType, _vcc);
|
|
result = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_uintType,
|
|
condition,
|
|
GetRawSource(instruction, 1),
|
|
GetRawSource(instruction, 0));
|
|
break;
|
|
}
|
|
case "VCvtU32F32":
|
|
result = _module.AddInstruction(
|
|
SpirvOp.ConvertFToU,
|
|
_uintType,
|
|
GetFloatSource(instruction, 0));
|
|
break;
|
|
case "VCvtI32F32":
|
|
case "VCvtRpiI32F32":
|
|
case "VCvtFlrI32F32":
|
|
{
|
|
var source = GetFloatSource(instruction, 0);
|
|
if (instruction.Opcode == "VCvtRpiI32F32")
|
|
{
|
|
source = Ext(9, _floatType, source);
|
|
}
|
|
else if (instruction.Opcode == "VCvtFlrI32F32")
|
|
{
|
|
source = Ext(8, _floatType, source);
|
|
}
|
|
|
|
result = Bitcast(
|
|
_uintType,
|
|
_module.AddInstruction(SpirvOp.ConvertFToS, _intType, source));
|
|
break;
|
|
}
|
|
case "VCvtF32I32":
|
|
{
|
|
var signed = Bitcast(_intType, GetRawSource(instruction, 0));
|
|
result = Bitcast(
|
|
_uintType,
|
|
_module.AddInstruction(SpirvOp.ConvertSToF, _floatType, signed));
|
|
break;
|
|
}
|
|
case "VCvtF32U32":
|
|
result = Bitcast(
|
|
_uintType,
|
|
_module.AddInstruction(
|
|
SpirvOp.ConvertUToF,
|
|
_floatType,
|
|
GetRawSource(instruction, 0)));
|
|
break;
|
|
case "VCvtF32Ubyte0":
|
|
case "VCvtF32Ubyte1":
|
|
case "VCvtF32Ubyte2":
|
|
case "VCvtF32Ubyte3":
|
|
{
|
|
var shift = (uint)(instruction.Opcode[^1] - '0') * 8;
|
|
var raw = ShiftRightLogical(GetRawSource(instruction, 0), UInt(shift));
|
|
raw = BitwiseAnd(raw, UInt(0xFF));
|
|
result = Bitcast(
|
|
_uintType,
|
|
_module.AddInstruction(SpirvOp.ConvertUToF, _floatType, raw));
|
|
break;
|
|
}
|
|
case "VCvtF16F32":
|
|
{
|
|
var vector = _module.AddInstruction(
|
|
SpirvOp.CompositeConstruct,
|
|
_vec2Type,
|
|
GetFloatSource(instruction, 0),
|
|
Float(0));
|
|
result = BitwiseAnd(Ext(58, _uintType, vector), UInt(0xFFFF));
|
|
break;
|
|
}
|
|
case "VCvtF32F16":
|
|
{
|
|
var unpacked = Ext(62, _vec2Type, GetRawSource(instruction, 0));
|
|
var value = _module.AddInstruction(
|
|
SpirvOp.CompositeExtract,
|
|
_floatType,
|
|
unpacked,
|
|
0);
|
|
result = Bitcast(_uintType, value);
|
|
break;
|
|
}
|
|
case "VCvtOffF32I4":
|
|
result = EmitCvtOffF32I4(instruction);
|
|
break;
|
|
case "VCvtPkU8F32":
|
|
{
|
|
var converted = _module.AddInstruction(
|
|
SpirvOp.ConvertFToU,
|
|
_uintType,
|
|
GetFloatSource(instruction, 0));
|
|
var offset = ShiftLeftLogical(
|
|
BitwiseAnd(GetRawSource(instruction, 1), UInt(3)),
|
|
UInt(3));
|
|
result = _module.AddInstruction(
|
|
SpirvOp.BitFieldInsert,
|
|
_uintType,
|
|
GetRawSource(instruction, 2),
|
|
converted,
|
|
offset,
|
|
UInt(8));
|
|
break;
|
|
}
|
|
case "VRcpF32":
|
|
case "VRcpIflagF32":
|
|
result = EmitFloatResult(
|
|
instruction,
|
|
_module.AddInstruction(
|
|
SpirvOp.FDiv,
|
|
_floatType,
|
|
Float(1),
|
|
GetFloatSource(instruction, 0)));
|
|
break;
|
|
case "VLogF32":
|
|
result = EmitFloatResult(
|
|
instruction,
|
|
Ext(30, _floatType, GetFloatSource(instruction, 0)));
|
|
break;
|
|
case "VLdexpF32":
|
|
result = EmitFloatResult(
|
|
instruction,
|
|
Ext(
|
|
53,
|
|
_floatType,
|
|
GetFloatSource(instruction, 0),
|
|
Bitcast(_intType, GetRawSource(instruction, 1))));
|
|
break;
|
|
case "VExpF32":
|
|
result = EmitFloatResult(
|
|
instruction,
|
|
Ext(29, _floatType, GetFloatSource(instruction, 0)));
|
|
break;
|
|
case "VRsqF32":
|
|
result = EmitFloatResult(
|
|
instruction,
|
|
Ext(32, _floatType, GetFloatSource(instruction, 0)));
|
|
break;
|
|
case "VFractF32":
|
|
result = EmitFloatResult(
|
|
instruction,
|
|
Ext(10, _floatType, GetFloatSource(instruction, 0)));
|
|
break;
|
|
case "VTruncF32":
|
|
result = EmitFloatResult(
|
|
instruction,
|
|
Ext(3, _floatType, GetFloatSource(instruction, 0)));
|
|
break;
|
|
case "VCeilF32":
|
|
result = EmitFloatResult(
|
|
instruction,
|
|
Ext(9, _floatType, GetFloatSource(instruction, 0)));
|
|
break;
|
|
case "VRndneF32":
|
|
result = EmitFloatResult(
|
|
instruction,
|
|
Ext(2, _floatType, GetFloatSource(instruction, 0)));
|
|
break;
|
|
case "VFloorF32":
|
|
result = EmitFloatResult(
|
|
instruction,
|
|
Ext(8, _floatType, GetFloatSource(instruction, 0)));
|
|
break;
|
|
case "VSqrtF32":
|
|
result = EmitFloatResult(
|
|
instruction,
|
|
Ext(31, _floatType, GetFloatSource(instruction, 0)));
|
|
break;
|
|
case "VSinF32":
|
|
result = EmitFloatResult(
|
|
instruction,
|
|
Ext(
|
|
13,
|
|
_floatType,
|
|
_module.AddInstruction(
|
|
SpirvOp.FMul,
|
|
_floatType,
|
|
GetFloatSource(instruction, 0),
|
|
Float(MathF.Tau))));
|
|
break;
|
|
case "VCosF32":
|
|
result = EmitFloatResult(
|
|
instruction,
|
|
Ext(
|
|
14,
|
|
_floatType,
|
|
_module.AddInstruction(
|
|
SpirvOp.FMul,
|
|
_floatType,
|
|
GetFloatSource(instruction, 0),
|
|
Float(MathF.Tau))));
|
|
break;
|
|
case "VAddF32":
|
|
result = EmitFloatBinary(instruction, SpirvOp.FAdd);
|
|
break;
|
|
case "VSubF32":
|
|
result = EmitFloatBinary(instruction, SpirvOp.FSub);
|
|
break;
|
|
case "VSubrevF32":
|
|
result = EmitFloatBinary(instruction, SpirvOp.FSub, reverse: true);
|
|
break;
|
|
case "VMulF32":
|
|
result = EmitFloatBinary(instruction, SpirvOp.FMul);
|
|
break;
|
|
case "VMinF32":
|
|
result = EmitFloatExtBinary(instruction, 37);
|
|
break;
|
|
case "VMaxF32":
|
|
result = EmitFloatExtBinary(instruction, 40);
|
|
break;
|
|
case "VMadF32":
|
|
case "VFmaF32":
|
|
case "VMadMkF32":
|
|
case "VMadAkF32":
|
|
case "VFmamkF32":
|
|
case "VFmaakF32":
|
|
result = EmitFloatResult(
|
|
instruction,
|
|
Ext(
|
|
50,
|
|
_floatType,
|
|
GetFloatSource(instruction, 0),
|
|
GetFloatSource(instruction, 1),
|
|
GetFloatSource(instruction, 2)));
|
|
break;
|
|
case "VMacF32":
|
|
case "VFmacF32":
|
|
{
|
|
var addend = Bitcast(_floatType, LoadV(destination));
|
|
result = EmitFloatResult(
|
|
instruction,
|
|
Ext(
|
|
50,
|
|
_floatType,
|
|
GetFloatSource(instruction, 0),
|
|
GetFloatSource(instruction, 1),
|
|
addend));
|
|
break;
|
|
}
|
|
case "VMin3F32":
|
|
result = EmitFloatTernaryExt(instruction, 37);
|
|
break;
|
|
case "VMax3F32":
|
|
result = EmitFloatTernaryExt(instruction, 40);
|
|
break;
|
|
case "VAndB32":
|
|
result = EmitIntegerBinary(instruction, SpirvOp.BitwiseAnd);
|
|
break;
|
|
case "VOrB32":
|
|
result = EmitIntegerBinary(instruction, SpirvOp.BitwiseOr);
|
|
break;
|
|
case "VXorB32":
|
|
result = EmitIntegerBinary(instruction, SpirvOp.BitwiseXor);
|
|
break;
|
|
case "VNotB32":
|
|
result = _module.AddInstruction(
|
|
SpirvOp.Not,
|
|
_uintType,
|
|
GetRawSource(instruction, 0));
|
|
break;
|
|
case "VBfrevB32":
|
|
result = _module.AddInstruction(
|
|
SpirvOp.BitReverse,
|
|
_uintType,
|
|
GetRawSource(instruction, 0));
|
|
break;
|
|
case "VFfblB32":
|
|
result = Bitcast(
|
|
_uintType,
|
|
Ext(
|
|
73,
|
|
_intType,
|
|
Bitcast(_intType, GetRawSource(instruction, 0))));
|
|
break;
|
|
case "VAddI32":
|
|
case "VAddU32":
|
|
result = EmitIntegerBinary(instruction, SpirvOp.IAdd);
|
|
break;
|
|
case "VAddcU32":
|
|
result = EmitAddWithCarry(instruction);
|
|
break;
|
|
case "VSubI32":
|
|
case "VSubU32":
|
|
result = EmitIntegerBinary(instruction, SpirvOp.ISub);
|
|
break;
|
|
case "VSubrevI32":
|
|
case "VSubrevU32":
|
|
result = EmitIntegerBinary(instruction, SpirvOp.ISub, reverse: true);
|
|
break;
|
|
case "VSubbU32":
|
|
result = EmitSubtractWithBorrow(instruction, reverse: false);
|
|
break;
|
|
case "VSubbrevU32":
|
|
result = EmitSubtractWithBorrow(instruction, reverse: true);
|
|
break;
|
|
case "VMulLoU32":
|
|
case "VMulLoI32":
|
|
case "VMulU32U24":
|
|
result = EmitIntegerBinary(instruction, SpirvOp.IMul);
|
|
break;
|
|
case "VMulHiU32":
|
|
case "VMulHiU32U24":
|
|
{
|
|
var left = GetRawSource(instruction, 0);
|
|
var right = GetRawSource(instruction, 1);
|
|
if (instruction.Opcode == "VMulHiU32U24")
|
|
{
|
|
left = BitwiseAnd(left, UInt(0x00FF_FFFF));
|
|
right = BitwiseAnd(right, UInt(0x00FF_FFFF));
|
|
}
|
|
|
|
var wideLeft = _module.AddInstruction(
|
|
SpirvOp.UConvert,
|
|
_ulongType,
|
|
left);
|
|
var wideRight = _module.AddInstruction(
|
|
SpirvOp.UConvert,
|
|
_ulongType,
|
|
right);
|
|
var product = _module.AddInstruction(
|
|
SpirvOp.IMul,
|
|
_ulongType,
|
|
wideLeft,
|
|
wideRight);
|
|
result = _module.AddInstruction(
|
|
SpirvOp.UConvert,
|
|
_uintType,
|
|
ShiftRightLogical64(
|
|
product,
|
|
_module.Constant64(_ulongType, 32)));
|
|
break;
|
|
}
|
|
case "VMulHiI32":
|
|
{
|
|
var wideLeft = _module.AddInstruction(
|
|
SpirvOp.SConvert,
|
|
_longType,
|
|
Bitcast(_intType, GetRawSource(instruction, 0)));
|
|
var wideRight = _module.AddInstruction(
|
|
SpirvOp.SConvert,
|
|
_longType,
|
|
Bitcast(_intType, GetRawSource(instruction, 1)));
|
|
var product = _module.AddInstruction(
|
|
SpirvOp.IMul,
|
|
_longType,
|
|
wideLeft,
|
|
wideRight);
|
|
result = _module.AddInstruction(
|
|
SpirvOp.UConvert,
|
|
_uintType,
|
|
ShiftRightLogical64(
|
|
Bitcast(_ulongType, product),
|
|
_module.Constant64(_ulongType, 32)));
|
|
break;
|
|
}
|
|
case "VBcntU32B32":
|
|
result = IAdd(
|
|
_module.AddInstruction(
|
|
SpirvOp.BitCount,
|
|
_uintType,
|
|
GetRawSource(instruction, 0)),
|
|
GetRawSource(instruction, 1));
|
|
break;
|
|
case "VMadU32U24":
|
|
{
|
|
var left = BitwiseAnd(
|
|
GetRawSource(instruction, 0),
|
|
UInt(0x00FF_FFFF));
|
|
var right = BitwiseAnd(
|
|
GetRawSource(instruction, 1),
|
|
UInt(0x00FF_FFFF));
|
|
result = IAdd(
|
|
_module.AddInstruction(
|
|
SpirvOp.IMul,
|
|
_uintType,
|
|
left,
|
|
right),
|
|
GetRawSource(instruction, 2));
|
|
break;
|
|
}
|
|
case "VMadU32U16":
|
|
{
|
|
var left = BitwiseAnd(
|
|
GetRawSource(instruction, 0),
|
|
UInt(0xFFFF));
|
|
var right = BitwiseAnd(
|
|
GetRawSource(instruction, 1),
|
|
UInt(0xFFFF));
|
|
result = IAdd(
|
|
_module.AddInstruction(
|
|
SpirvOp.IMul,
|
|
_uintType,
|
|
left,
|
|
right),
|
|
GetRawSource(instruction, 2));
|
|
break;
|
|
}
|
|
case "VLshrB32":
|
|
result = EmitIntegerBinary(instruction, SpirvOp.ShiftRightLogical);
|
|
break;
|
|
case "VLshrrevB32":
|
|
result = EmitIntegerBinary(
|
|
instruction,
|
|
SpirvOp.ShiftRightLogical,
|
|
reverse: true);
|
|
break;
|
|
case "VLshlB32":
|
|
result = EmitIntegerBinary(instruction, SpirvOp.ShiftLeftLogical);
|
|
break;
|
|
case "VLshlrevB32":
|
|
result = EmitIntegerBinary(
|
|
instruction,
|
|
SpirvOp.ShiftLeftLogical,
|
|
reverse: true);
|
|
break;
|
|
case "VAshrI32":
|
|
case "VAshrrevI32":
|
|
{
|
|
var reverse = instruction.Opcode == "VAshrrevI32";
|
|
var left = GetRawSource(instruction, reverse ? 1 : 0);
|
|
var right = GetRawSource(instruction, reverse ? 0 : 1);
|
|
result = ShiftRightArithmetic(left, right);
|
|
break;
|
|
}
|
|
case "VLshlAddU32":
|
|
{
|
|
var shifted = ShiftLeftLogical(
|
|
GetRawSource(instruction, 0),
|
|
BitwiseAnd(GetRawSource(instruction, 1), UInt(31)));
|
|
result = IAdd(shifted, GetRawSource(instruction, 2));
|
|
break;
|
|
}
|
|
case "VLshlOrU32":
|
|
{
|
|
var shifted = ShiftLeftLogical(
|
|
GetRawSource(instruction, 0),
|
|
BitwiseAnd(GetRawSource(instruction, 1), UInt(31)));
|
|
result = BitwiseOr(
|
|
shifted,
|
|
GetRawSource(instruction, 2));
|
|
break;
|
|
}
|
|
case "VAndOrB32":
|
|
result = BitwiseOr(
|
|
BitwiseAnd(
|
|
GetRawSource(instruction, 0),
|
|
GetRawSource(instruction, 1)),
|
|
GetRawSource(instruction, 2));
|
|
break;
|
|
case "VOr3U32":
|
|
result = BitwiseOr(
|
|
BitwiseOr(
|
|
GetRawSource(instruction, 0),
|
|
GetRawSource(instruction, 1)),
|
|
GetRawSource(instruction, 2));
|
|
break;
|
|
case "VPermlane16B32":
|
|
result = EmitPermlane16(instruction, exchangeRows: false);
|
|
break;
|
|
case "VPermlanex16B32":
|
|
result = EmitPermlane16(instruction, exchangeRows: true);
|
|
break;
|
|
case "VAddLshlU32":
|
|
{
|
|
var added = IAdd(
|
|
GetRawSource(instruction, 0),
|
|
GetRawSource(instruction, 1));
|
|
result = ShiftLeftLogical(added, GetRawSource(instruction, 2));
|
|
break;
|
|
}
|
|
case "VAdd3U32":
|
|
result = IAdd(
|
|
IAdd(
|
|
GetRawSource(instruction, 0),
|
|
GetRawSource(instruction, 1)),
|
|
GetRawSource(instruction, 2));
|
|
break;
|
|
case "VMinU32":
|
|
result = Ext(
|
|
38,
|
|
_uintType,
|
|
GetRawSource(instruction, 0),
|
|
GetRawSource(instruction, 1));
|
|
break;
|
|
case "VMaxU32":
|
|
result = Ext(
|
|
41,
|
|
_uintType,
|
|
GetRawSource(instruction, 0),
|
|
GetRawSource(instruction, 1));
|
|
break;
|
|
case "VMin3U32":
|
|
result = Ext(
|
|
38,
|
|
_uintType,
|
|
Ext(
|
|
38,
|
|
_uintType,
|
|
GetRawSource(instruction, 0),
|
|
GetRawSource(instruction, 1)),
|
|
GetRawSource(instruction, 2));
|
|
break;
|
|
case "VMax3U32":
|
|
result = Ext(
|
|
41,
|
|
_uintType,
|
|
Ext(
|
|
41,
|
|
_uintType,
|
|
GetRawSource(instruction, 0),
|
|
GetRawSource(instruction, 1)),
|
|
GetRawSource(instruction, 2));
|
|
break;
|
|
case "VMinI32":
|
|
case "VMaxI32":
|
|
{
|
|
var signedResult = Ext(
|
|
instruction.Opcode == "VMinI32" ? 39u : 42u,
|
|
_intType,
|
|
Bitcast(_intType, GetRawSource(instruction, 0)),
|
|
Bitcast(_intType, GetRawSource(instruction, 1)));
|
|
result = Bitcast(_uintType, signedResult);
|
|
break;
|
|
}
|
|
case "VMin3I32":
|
|
case "VMax3I32":
|
|
{
|
|
var operation = instruction.Opcode == "VMin3I32" ? 39u : 42u;
|
|
var left = Bitcast(
|
|
_intType,
|
|
GetRawSource(instruction, 0));
|
|
var middle = Bitcast(
|
|
_intType,
|
|
GetRawSource(instruction, 1));
|
|
var right = Bitcast(
|
|
_intType,
|
|
GetRawSource(instruction, 2));
|
|
result = Bitcast(
|
|
_uintType,
|
|
Ext(
|
|
operation,
|
|
_intType,
|
|
Ext(operation, _intType, left, middle),
|
|
right));
|
|
break;
|
|
}
|
|
case "VMed3U32":
|
|
{
|
|
var left = GetRawSource(instruction, 0);
|
|
var middle = GetRawSource(instruction, 1);
|
|
var right = GetRawSource(instruction, 2);
|
|
var low = Ext(38, _uintType, left, middle);
|
|
var high = Ext(41, _uintType, left, middle);
|
|
result = Ext(
|
|
41,
|
|
_uintType,
|
|
low,
|
|
Ext(38, _uintType, high, right));
|
|
break;
|
|
}
|
|
case "VMed3I32":
|
|
{
|
|
var left = Bitcast(_intType, GetRawSource(instruction, 0));
|
|
var middle = Bitcast(_intType, GetRawSource(instruction, 1));
|
|
var right = Bitcast(_intType, GetRawSource(instruction, 2));
|
|
var low = Ext(39, _intType, left, middle);
|
|
var high = Ext(42, _intType, left, middle);
|
|
result = Bitcast(
|
|
_uintType,
|
|
Ext(
|
|
42,
|
|
_intType,
|
|
low,
|
|
Ext(39, _intType, high, right)));
|
|
break;
|
|
}
|
|
case "VMed3F32":
|
|
{
|
|
var left = GetFloatSource(instruction, 0);
|
|
var middle = GetFloatSource(instruction, 1);
|
|
var right = GetFloatSource(instruction, 2);
|
|
var low = Ext(37, _floatType, left, middle);
|
|
var high = Ext(40, _floatType, left, middle);
|
|
result = EmitFloatResult(
|
|
instruction,
|
|
Ext(
|
|
40,
|
|
_floatType,
|
|
low,
|
|
Ext(37, _floatType, high, right)));
|
|
break;
|
|
}
|
|
case "VCubeidF32":
|
|
result = EmitCubeCoordinate(instruction, CubeCoordinate.Id);
|
|
break;
|
|
case "VCubescF32":
|
|
result = EmitCubeCoordinate(instruction, CubeCoordinate.Sc);
|
|
break;
|
|
case "VCubetcF32":
|
|
result = EmitCubeCoordinate(instruction, CubeCoordinate.Tc);
|
|
break;
|
|
case "VCubemaF32":
|
|
result = EmitCubeCoordinate(instruction, CubeCoordinate.Ma);
|
|
break;
|
|
case "VAddCoU32":
|
|
{
|
|
var left = GetRawSource(instruction, 0);
|
|
var right = GetRawSource(instruction, 1);
|
|
result = IAdd(left, right);
|
|
var carry = _module.AddInstruction(
|
|
SpirvOp.ULessThan,
|
|
_boolType,
|
|
result,
|
|
left);
|
|
StoreCarryOut(instruction, carry);
|
|
break;
|
|
}
|
|
case "VSubCoU32":
|
|
case "VSubrevCoU32":
|
|
{
|
|
var reverse = instruction.Opcode == "VSubrevCoU32";
|
|
var left = GetRawSource(instruction, reverse ? 1 : 0);
|
|
var right = GetRawSource(instruction, reverse ? 0 : 1);
|
|
result = _module.AddInstruction(SpirvOp.ISub, _uintType, left, right);
|
|
var borrow = _module.AddInstruction(
|
|
SpirvOp.ULessThan,
|
|
_boolType,
|
|
left,
|
|
right);
|
|
StoreCarryOut(instruction, borrow);
|
|
break;
|
|
}
|
|
case "VBfeU32":
|
|
{
|
|
var width = BitwiseAnd(GetRawSource(instruction, 2), UInt(31));
|
|
result = _module.AddInstruction(
|
|
SpirvOp.BitFieldUExtract,
|
|
_uintType,
|
|
GetRawSource(instruction, 0),
|
|
BitwiseAnd(GetRawSource(instruction, 1), UInt(31)),
|
|
width);
|
|
break;
|
|
}
|
|
case "VBfiB32":
|
|
{
|
|
var mask = GetRawSource(instruction, 0);
|
|
var insert = GetRawSource(instruction, 1);
|
|
var source = GetRawSource(instruction, 2);
|
|
result = _module.AddInstruction(
|
|
SpirvOp.BitwiseOr,
|
|
_uintType,
|
|
BitwiseAnd(mask, insert),
|
|
BitwiseAnd(
|
|
_module.AddInstruction(SpirvOp.Not, _uintType, mask),
|
|
source));
|
|
break;
|
|
}
|
|
case "VCvtPkrtzF16F32":
|
|
{
|
|
var first = TruncateFloat32ForPack(GetFloatSource(instruction, 0));
|
|
var second = TruncateFloat32ForPack(GetFloatSource(instruction, 1));
|
|
var vector = _module.AddInstruction(
|
|
SpirvOp.CompositeConstruct,
|
|
_vec2Type,
|
|
first,
|
|
second);
|
|
result = Ext(58, _uintType, vector);
|
|
break;
|
|
}
|
|
case "VCvtPkU16U32":
|
|
case "VCvtPkI16I32":
|
|
result = BitwiseOr(
|
|
BitwiseAnd(GetRawSource(instruction, 0), UInt(0xFFFF)),
|
|
ShiftLeftLogical(
|
|
BitwiseAnd(GetRawSource(instruction, 1), UInt(0xFFFF)),
|
|
UInt(16)));
|
|
break;
|
|
default:
|
|
error = $"unsupported vector opcode {instruction.Opcode}";
|
|
return false;
|
|
}
|
|
|
|
StoreV(destination, result);
|
|
return true;
|
|
}
|
|
|
|
private bool TryEmitVectorCompare(
|
|
Gen5ShaderInstruction instruction,
|
|
out string error)
|
|
{
|
|
error = string.Empty;
|
|
uint condition = _module.ConstantBool(false);
|
|
var opcode = instruction.Opcode;
|
|
if (opcode is "VCmpClassF32" or "VCmpxClassF32")
|
|
{
|
|
var source = GetFloatSource(instruction, 0);
|
|
var raw = GetRawSource(instruction, 0);
|
|
var mask = GetRawSource(instruction, 1);
|
|
var negative = IsNotZero(BitwiseAnd(raw, UInt(0x8000_0000)));
|
|
var positive = _module.AddInstruction(
|
|
SpirvOp.LogicalNot,
|
|
_boolType,
|
|
negative);
|
|
var nan = _module.AddInstruction(SpirvOp.IsNan, _boolType, source);
|
|
var infinity =
|
|
_module.AddInstruction(SpirvOp.IsInf, _boolType, source);
|
|
var zero = _module.AddInstruction(
|
|
SpirvOp.FOrdEqual,
|
|
_boolType,
|
|
source,
|
|
Float(0));
|
|
var absolute = Ext(4, _floatType, source);
|
|
var nonzero = _module.AddInstruction(
|
|
SpirvOp.FOrdGreaterThan,
|
|
_boolType,
|
|
absolute,
|
|
Float(0));
|
|
var belowNormal = _module.AddInstruction(
|
|
SpirvOp.FOrdLessThan,
|
|
_boolType,
|
|
absolute,
|
|
Bitcast(_floatType, UInt(0x0080_0000)));
|
|
var subnormal = _module.AddInstruction(
|
|
SpirvOp.LogicalAnd,
|
|
_boolType,
|
|
nonzero,
|
|
belowNormal);
|
|
var special = _module.AddInstruction(
|
|
SpirvOp.LogicalOr,
|
|
_boolType,
|
|
nan,
|
|
_module.AddInstruction(
|
|
SpirvOp.LogicalOr,
|
|
_boolType,
|
|
infinity,
|
|
_module.AddInstruction(
|
|
SpirvOp.LogicalOr,
|
|
_boolType,
|
|
zero,
|
|
subnormal)));
|
|
var normal = _module.AddInstruction(
|
|
SpirvOp.LogicalNot,
|
|
_boolType,
|
|
special);
|
|
|
|
uint MaskedClass(uint bits, uint value)
|
|
{
|
|
var enabled = IsNotZero(BitwiseAnd(mask, UInt(bits)));
|
|
return _module.AddInstruction(
|
|
SpirvOp.LogicalAnd,
|
|
_boolType,
|
|
enabled,
|
|
value);
|
|
}
|
|
|
|
uint SignedClass(uint negativeBit, uint positiveBit, uint value)
|
|
{
|
|
var negativeClass = MaskedClass(
|
|
negativeBit,
|
|
_module.AddInstruction(
|
|
SpirvOp.LogicalAnd,
|
|
_boolType,
|
|
negative,
|
|
value));
|
|
var positiveClass = MaskedClass(
|
|
positiveBit,
|
|
_module.AddInstruction(
|
|
SpirvOp.LogicalAnd,
|
|
_boolType,
|
|
positive,
|
|
value));
|
|
return _module.AddInstruction(
|
|
SpirvOp.LogicalOr,
|
|
_boolType,
|
|
negativeClass,
|
|
positiveClass);
|
|
}
|
|
|
|
condition = MaskedClass(0x003, nan);
|
|
condition = _module.AddInstruction(
|
|
SpirvOp.LogicalOr,
|
|
_boolType,
|
|
condition,
|
|
SignedClass(0x004, 0x200, infinity));
|
|
condition = _module.AddInstruction(
|
|
SpirvOp.LogicalOr,
|
|
_boolType,
|
|
condition,
|
|
SignedClass(0x008, 0x100, normal));
|
|
condition = _module.AddInstruction(
|
|
SpirvOp.LogicalOr,
|
|
_boolType,
|
|
condition,
|
|
SignedClass(0x010, 0x080, subnormal));
|
|
condition = _module.AddInstruction(
|
|
SpirvOp.LogicalOr,
|
|
_boolType,
|
|
condition,
|
|
SignedClass(0x020, 0x040, zero));
|
|
}
|
|
else if (opcode is "VCmpFF32" or "VCmpxFF32" or "VCmpFI32" or "VCmpFU32")
|
|
{
|
|
condition = _module.ConstantBool(false);
|
|
}
|
|
else if (opcode is "VCmpTruF32" or "VCmpxTruF32" or "VCmpTI32" or "VCmpTU32")
|
|
{
|
|
condition = _module.ConstantBool(true);
|
|
}
|
|
else if (opcode is not ("VCmpClassF32" or "VCmpxClassF32") &&
|
|
opcode.EndsWith("F32", StringComparison.Ordinal))
|
|
{
|
|
var left = GetFloatSource(instruction, 0);
|
|
var right = GetFloatSource(instruction, 1);
|
|
var operation = opcode switch
|
|
{
|
|
"VCmpLtF32" or "VCmpxLtF32" => SpirvOp.FOrdLessThan,
|
|
"VCmpEqF32" or "VCmpxEqF32" => SpirvOp.FOrdEqual,
|
|
"VCmpLeF32" or "VCmpxLeF32" => SpirvOp.FOrdLessThanEqual,
|
|
"VCmpGtF32" or "VCmpxGtF32" => SpirvOp.FOrdGreaterThan,
|
|
"VCmpLgF32" or "VCmpxLgF32" => SpirvOp.FOrdNotEqual,
|
|
"VCmpGeF32" or "VCmpxGeF32" => SpirvOp.FOrdGreaterThanEqual,
|
|
"VCmpNeqF32" or "VCmpxNeqF32" => SpirvOp.FUnordNotEqual,
|
|
"VCmpNltF32" or "VCmpxNltF32" => SpirvOp.FUnordGreaterThanEqual,
|
|
"VCmpNleF32" or "VCmpxNleF32" => SpirvOp.FUnordGreaterThan,
|
|
"VCmpNgtF32" or "VCmpxNgtF32" => SpirvOp.FUnordLessThanEqual,
|
|
"VCmpNgeF32" or "VCmpxNgeF32" => SpirvOp.FUnordLessThan,
|
|
_ => SpirvOp.Nop,
|
|
};
|
|
if (operation == SpirvOp.Nop)
|
|
{
|
|
error = $"unsupported float compare {opcode}";
|
|
return false;
|
|
}
|
|
|
|
condition = _module.AddInstruction(operation, _boolType, left, right);
|
|
}
|
|
else if (opcode is not ("VCmpClassF32" or "VCmpxClassF32"))
|
|
{
|
|
var left = GetRawSource(instruction, 0);
|
|
var right = GetRawSource(instruction, 1);
|
|
var signed = opcode.EndsWith("I32", StringComparison.Ordinal);
|
|
if (signed)
|
|
{
|
|
left = Bitcast(_intType, left);
|
|
right = Bitcast(_intType, right);
|
|
}
|
|
|
|
var operation = opcode switch
|
|
{
|
|
"VCmpEqI32" or "VCmpxEqI32" or
|
|
"VCmpEqU32" or "VCmpxEqU32" => SpirvOp.IEqual,
|
|
"VCmpNeI32" or "VCmpxNeI32" or
|
|
"VCmpNeU32" or "VCmpxNeU32" => SpirvOp.INotEqual,
|
|
"VCmpLtI32" or "VCmpxLtI32" => SpirvOp.SLessThan,
|
|
"VCmpLeI32" or "VCmpxLeI32" => SpirvOp.SLessThanEqual,
|
|
"VCmpGtI32" or "VCmpxGtI32" => SpirvOp.SGreaterThan,
|
|
"VCmpGeI32" or "VCmpxGeI32" => SpirvOp.SGreaterThanEqual,
|
|
"VCmpLtU32" or "VCmpxLtU32" => SpirvOp.ULessThan,
|
|
"VCmpLeU32" or "VCmpxLeU32" => SpirvOp.ULessThanEqual,
|
|
"VCmpGtU32" or "VCmpxGtU32" => SpirvOp.UGreaterThan,
|
|
"VCmpGeU32" or "VCmpxGeU32" => SpirvOp.UGreaterThanEqual,
|
|
_ => SpirvOp.Nop,
|
|
};
|
|
if (operation == SpirvOp.Nop)
|
|
{
|
|
error = $"unsupported integer compare {opcode}";
|
|
return false;
|
|
}
|
|
|
|
condition = _module.AddInstruction(operation, _boolType, left, right);
|
|
}
|
|
|
|
StoreWaveMask(106, condition);
|
|
if (opcode.StartsWith("VCmpx", StringComparison.Ordinal))
|
|
{
|
|
var active = _module.AddInstruction(
|
|
SpirvOp.LogicalAnd,
|
|
_boolType,
|
|
Load(_boolType, _exec),
|
|
condition);
|
|
StoreWaveMask(126, active);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private bool TryEmitScalarAlu(
|
|
Gen5ShaderInstruction instruction,
|
|
out string error)
|
|
{
|
|
error = string.Empty;
|
|
if (instruction.Encoding == Gen5ShaderEncoding.Sopc)
|
|
{
|
|
return TryEmitScalarCompare(instruction, out error);
|
|
}
|
|
|
|
if (instruction.Destinations.Count == 0 ||
|
|
instruction.Destinations[0].Kind != Gen5OperandKind.ScalarRegister)
|
|
{
|
|
error = "missing scalar destination";
|
|
return false;
|
|
}
|
|
|
|
var destination = instruction.Destinations[0].Value;
|
|
if (instruction.Encoding == Gen5ShaderEncoding.Sopk)
|
|
{
|
|
var immediate = unchecked((uint)(short)(instruction.Words[0] & 0xFFFF));
|
|
if (instruction.Opcode.StartsWith("SCmpk", StringComparison.Ordinal))
|
|
{
|
|
return TryEmitScalarCompareK(instruction, destination, immediate, out error);
|
|
}
|
|
|
|
var current = LoadS(destination);
|
|
var value = instruction.Opcode switch
|
|
{
|
|
"SMovkI32" => UInt(immediate),
|
|
"SAddkI32" => IAdd(current, UInt(immediate)),
|
|
"SMulkI32" => _module.AddInstruction(
|
|
SpirvOp.IMul,
|
|
_uintType,
|
|
current,
|
|
UInt(immediate)),
|
|
_ => 0u,
|
|
};
|
|
if (value == 0)
|
|
{
|
|
error = $"unsupported scalar immediate {instruction.Opcode}";
|
|
return false;
|
|
}
|
|
|
|
StoreS(destination, value);
|
|
return true;
|
|
}
|
|
|
|
if (instruction.Opcode == "SGetpcB64")
|
|
{
|
|
var pc = _state.Program.Address +
|
|
instruction.Pc +
|
|
(ulong)(instruction.Words.Count * sizeof(uint));
|
|
StoreS(destination, UInt((uint)pc));
|
|
StoreS(destination + 1, UInt((uint)(pc >> 32)));
|
|
return true;
|
|
}
|
|
|
|
if (instruction.Opcode.EndsWith("B64", StringComparison.Ordinal) ||
|
|
instruction.Opcode is "SWqmB64" or "SBfeU64" or "SBfeI64")
|
|
{
|
|
return TryEmitScalar64(instruction, destination, out error);
|
|
}
|
|
|
|
var left = GetRawSource(instruction, 0);
|
|
uint result;
|
|
switch (instruction.Opcode)
|
|
{
|
|
case "SMovB32":
|
|
result = left;
|
|
break;
|
|
case "SNotB32":
|
|
result = _module.AddInstruction(SpirvOp.Not, _uintType, left);
|
|
StoreS(destination, result);
|
|
Store(_scc, IsNotZero(result));
|
|
return true;
|
|
case "SBrevB32":
|
|
result = _module.AddInstruction(SpirvOp.BitReverse, _uintType, left);
|
|
StoreS(destination, result);
|
|
Store(_scc, IsNotZero(result));
|
|
return true;
|
|
case "SBcnt1I32B32":
|
|
result = _module.AddInstruction(SpirvOp.BitCount, _uintType, left);
|
|
StoreS(destination, result);
|
|
Store(_scc, IsNotZero(result));
|
|
return true;
|
|
case "SFF1I32B32":
|
|
result = Ext(73, _uintType, left);
|
|
StoreS(destination, result);
|
|
Store(_scc, IsNotZero(result));
|
|
return true;
|
|
case "SBitset1B32":
|
|
result = _module.AddInstruction(
|
|
SpirvOp.BitFieldInsert,
|
|
_uintType,
|
|
LoadS(destination),
|
|
UInt(1),
|
|
BitwiseAnd(left, UInt(31)),
|
|
UInt(1));
|
|
StoreS(destination, result);
|
|
return true;
|
|
default:
|
|
{
|
|
if (instruction.Sources.Count < 2)
|
|
{
|
|
error = $"missing scalar source for {instruction.Opcode}";
|
|
return false;
|
|
}
|
|
|
|
var right = GetRawSource(instruction, 1);
|
|
switch (instruction.Opcode)
|
|
{
|
|
case "SAddU32":
|
|
result = IAdd(left, right);
|
|
Store(_scc, _module.AddInstruction(
|
|
SpirvOp.ULessThan,
|
|
_boolType,
|
|
result,
|
|
left));
|
|
break;
|
|
case "SSubU32":
|
|
result = _module.AddInstruction(
|
|
SpirvOp.ISub,
|
|
_uintType,
|
|
left,
|
|
right);
|
|
Store(_scc, _module.AddInstruction(
|
|
SpirvOp.UGreaterThan,
|
|
_boolType,
|
|
right,
|
|
left));
|
|
break;
|
|
case "SAddI32":
|
|
result = IAdd(left, right);
|
|
Store(_scc, SignedAddOverflow(left, right, result));
|
|
break;
|
|
case "SSubI32":
|
|
result = _module.AddInstruction(
|
|
SpirvOp.ISub,
|
|
_uintType,
|
|
left,
|
|
right);
|
|
Store(_scc, SignedSubOverflow(left, right, result));
|
|
break;
|
|
case "SAddcU32":
|
|
{
|
|
var carryIn = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_uintType,
|
|
Load(_boolType, _scc),
|
|
UInt(1),
|
|
UInt(0));
|
|
var partial = IAdd(left, right);
|
|
result = IAdd(partial, carryIn);
|
|
var firstCarry = _module.AddInstruction(
|
|
SpirvOp.ULessThan,
|
|
_boolType,
|
|
partial,
|
|
left);
|
|
var secondCarry = _module.AddInstruction(
|
|
SpirvOp.ULessThan,
|
|
_boolType,
|
|
result,
|
|
partial);
|
|
Store(
|
|
_scc,
|
|
_module.AddInstruction(
|
|
SpirvOp.LogicalOr,
|
|
_boolType,
|
|
firstCarry,
|
|
secondCarry));
|
|
break;
|
|
}
|
|
case "SSubbU32":
|
|
{
|
|
var borrow = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_uintType,
|
|
Load(_boolType, _scc),
|
|
UInt(1),
|
|
UInt(0));
|
|
var partial = _module.AddInstruction(
|
|
SpirvOp.ISub,
|
|
_uintType,
|
|
left,
|
|
right);
|
|
result = _module.AddInstruction(
|
|
SpirvOp.ISub,
|
|
_uintType,
|
|
partial,
|
|
borrow);
|
|
var firstBorrow = _module.AddInstruction(
|
|
SpirvOp.UGreaterThan,
|
|
_boolType,
|
|
right,
|
|
left);
|
|
var secondBorrow = _module.AddInstruction(
|
|
SpirvOp.LogicalAnd,
|
|
_boolType,
|
|
_module.AddInstruction(
|
|
SpirvOp.IEqual,
|
|
_boolType,
|
|
borrow,
|
|
UInt(1)),
|
|
_module.AddInstruction(
|
|
SpirvOp.IEqual,
|
|
_boolType,
|
|
right,
|
|
left));
|
|
Store(
|
|
_scc,
|
|
_module.AddInstruction(
|
|
SpirvOp.LogicalOr,
|
|
_boolType,
|
|
firstBorrow,
|
|
secondBorrow));
|
|
break;
|
|
}
|
|
case "SMulI32":
|
|
result = _module.AddInstruction(
|
|
SpirvOp.IMul,
|
|
_uintType,
|
|
left,
|
|
right);
|
|
break;
|
|
case "SMulHiU32":
|
|
{
|
|
var wideLeft = _module.AddInstruction(
|
|
SpirvOp.UConvert,
|
|
_ulongType,
|
|
left);
|
|
var wideRight = _module.AddInstruction(
|
|
SpirvOp.UConvert,
|
|
_ulongType,
|
|
right);
|
|
var product = _module.AddInstruction(
|
|
SpirvOp.IMul,
|
|
_ulongType,
|
|
wideLeft,
|
|
wideRight);
|
|
result = _module.AddInstruction(
|
|
SpirvOp.UConvert,
|
|
_uintType,
|
|
ShiftRightLogical64(
|
|
product,
|
|
_module.Constant64(_ulongType, 32)));
|
|
break;
|
|
}
|
|
case "SMulHiI32":
|
|
{
|
|
var wideLeft = _module.AddInstruction(
|
|
SpirvOp.SConvert,
|
|
_longType,
|
|
Bitcast(_intType, left));
|
|
var wideRight = _module.AddInstruction(
|
|
SpirvOp.SConvert,
|
|
_longType,
|
|
Bitcast(_intType, right));
|
|
var product = _module.AddInstruction(
|
|
SpirvOp.IMul,
|
|
_longType,
|
|
wideLeft,
|
|
wideRight);
|
|
result = _module.AddInstruction(
|
|
SpirvOp.UConvert,
|
|
_uintType,
|
|
ShiftRightLogical64(
|
|
Bitcast(_ulongType, product),
|
|
_module.Constant64(_ulongType, 32)));
|
|
break;
|
|
}
|
|
case "SAndB32":
|
|
result = BitwiseAnd(left, right);
|
|
Store(_scc, IsNotZero(result));
|
|
break;
|
|
case "SOrB32":
|
|
result = _module.AddInstruction(
|
|
SpirvOp.BitwiseOr,
|
|
_uintType,
|
|
left,
|
|
right);
|
|
Store(_scc, IsNotZero(result));
|
|
break;
|
|
case "SXorB32":
|
|
result = _module.AddInstruction(
|
|
SpirvOp.BitwiseXor,
|
|
_uintType,
|
|
left,
|
|
right);
|
|
Store(_scc, IsNotZero(result));
|
|
break;
|
|
case "SAndn2B32":
|
|
result = BitwiseAnd(
|
|
left,
|
|
_module.AddInstruction(SpirvOp.Not, _uintType, right));
|
|
Store(_scc, IsNotZero(result));
|
|
break;
|
|
case "SOrn2B32":
|
|
result = _module.AddInstruction(
|
|
SpirvOp.BitwiseOr,
|
|
_uintType,
|
|
left,
|
|
_module.AddInstruction(SpirvOp.Not, _uintType, right));
|
|
Store(_scc, IsNotZero(result));
|
|
break;
|
|
case "SNandB32":
|
|
result = _module.AddInstruction(
|
|
SpirvOp.Not,
|
|
_uintType,
|
|
BitwiseAnd(left, right));
|
|
Store(_scc, IsNotZero(result));
|
|
break;
|
|
case "SNorB32":
|
|
result = _module.AddInstruction(
|
|
SpirvOp.Not,
|
|
_uintType,
|
|
_module.AddInstruction(
|
|
SpirvOp.BitwiseOr,
|
|
_uintType,
|
|
left,
|
|
right));
|
|
Store(_scc, IsNotZero(result));
|
|
break;
|
|
case "SXnorB32":
|
|
result = _module.AddInstruction(
|
|
SpirvOp.Not,
|
|
_uintType,
|
|
_module.AddInstruction(
|
|
SpirvOp.BitwiseXor,
|
|
_uintType,
|
|
left,
|
|
right));
|
|
Store(_scc, IsNotZero(result));
|
|
break;
|
|
case "SLshlB32":
|
|
result = ShiftLeftLogical(left, right);
|
|
Store(_scc, IsNotZero(result));
|
|
break;
|
|
case "SLshrB32":
|
|
result = ShiftRightLogical(
|
|
left,
|
|
BitwiseAnd(right, UInt(31)));
|
|
Store(_scc, IsNotZero(result));
|
|
break;
|
|
case "SAshrI32":
|
|
result = ShiftRightArithmetic(left, right);
|
|
Store(_scc, IsNotZero(result));
|
|
break;
|
|
case "SBfmB32":
|
|
result = _module.AddInstruction(
|
|
SpirvOp.BitFieldInsert,
|
|
_uintType,
|
|
UInt(0),
|
|
UInt(uint.MaxValue),
|
|
BitwiseAnd(right, UInt(31)),
|
|
BitwiseAnd(left, UInt(31)));
|
|
break;
|
|
case "SBfeU32":
|
|
case "SBfeI32":
|
|
{
|
|
var offset = BitwiseAnd(right, UInt(31));
|
|
var requestedWidth = BitwiseAnd(
|
|
ShiftRightLogical(right, UInt(16)),
|
|
UInt(0x7F));
|
|
var remaining = _module.AddInstruction(
|
|
SpirvOp.ISub,
|
|
_uintType,
|
|
UInt(32),
|
|
offset);
|
|
var width = Ext(
|
|
38,
|
|
_uintType,
|
|
requestedWidth,
|
|
remaining);
|
|
result = instruction.Opcode == "SBfeI32"
|
|
? Bitcast(
|
|
_uintType,
|
|
_module.AddInstruction(
|
|
SpirvOp.BitFieldSExtract,
|
|
_intType,
|
|
Bitcast(_intType, left),
|
|
offset,
|
|
width))
|
|
: _module.AddInstruction(
|
|
SpirvOp.BitFieldUExtract,
|
|
_uintType,
|
|
left,
|
|
offset,
|
|
width);
|
|
Store(_scc, IsNotZero(result));
|
|
break;
|
|
}
|
|
case "SAbsdiffI32":
|
|
{
|
|
var wideLeft = _module.AddInstruction(
|
|
SpirvOp.SConvert,
|
|
_longType,
|
|
Bitcast(_intType, left));
|
|
var wideRight = _module.AddInstruction(
|
|
SpirvOp.SConvert,
|
|
_longType,
|
|
Bitcast(_intType, right));
|
|
var difference = _module.AddInstruction(
|
|
SpirvOp.ISub,
|
|
_longType,
|
|
wideLeft,
|
|
wideRight);
|
|
result = _module.AddInstruction(
|
|
SpirvOp.UConvert,
|
|
_uintType,
|
|
Ext(5, _longType, difference));
|
|
Store(_scc, IsNotZero(result));
|
|
break;
|
|
}
|
|
case "SPackLlB32B16":
|
|
result = BitwiseOr(
|
|
BitwiseAnd(left, UInt(0xFFFF)),
|
|
ShiftLeftLogical(right, UInt(16)));
|
|
break;
|
|
case "SPackLhB32B16":
|
|
result = BitwiseOr(
|
|
BitwiseAnd(left, UInt(0xFFFF)),
|
|
BitwiseAnd(right, UInt(0xFFFF0000)));
|
|
break;
|
|
case "SPackHhB32B16":
|
|
result = BitwiseOr(
|
|
ShiftRightLogical(left, UInt(16)),
|
|
BitwiseAnd(right, UInt(0xFFFF0000)));
|
|
break;
|
|
case "SCselectB32":
|
|
result = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_uintType,
|
|
Load(_boolType, _scc),
|
|
left,
|
|
right);
|
|
break;
|
|
case "SMinU32":
|
|
result = Ext(38, _uintType, left, right);
|
|
Store(
|
|
_scc,
|
|
_module.AddInstruction(
|
|
SpirvOp.ULessThan,
|
|
_boolType,
|
|
left,
|
|
right));
|
|
break;
|
|
case "SMinI32":
|
|
result = Bitcast(
|
|
_uintType,
|
|
Ext(39, _intType, Bitcast(_intType, left), Bitcast(_intType, right)));
|
|
Store(
|
|
_scc,
|
|
_module.AddInstruction(
|
|
SpirvOp.SLessThan,
|
|
_boolType,
|
|
Bitcast(_intType, left),
|
|
Bitcast(_intType, right)));
|
|
break;
|
|
case "SMaxU32":
|
|
result = Ext(41, _uintType, left, right);
|
|
Store(
|
|
_scc,
|
|
_module.AddInstruction(
|
|
SpirvOp.UGreaterThan,
|
|
_boolType,
|
|
left,
|
|
right));
|
|
break;
|
|
case "SMaxI32":
|
|
result = Bitcast(
|
|
_uintType,
|
|
Ext(42, _intType, Bitcast(_intType, left), Bitcast(_intType, right)));
|
|
Store(
|
|
_scc,
|
|
_module.AddInstruction(
|
|
SpirvOp.SGreaterThan,
|
|
_boolType,
|
|
Bitcast(_intType, left),
|
|
Bitcast(_intType, right)));
|
|
break;
|
|
case "SLshl1AddU32":
|
|
case "SLshl2AddU32":
|
|
case "SLshl3AddU32":
|
|
case "SLshl4AddU32":
|
|
{
|
|
var shift = (uint)(instruction.Opcode[5] - '0');
|
|
result = IAdd(
|
|
ShiftLeftLogical(left, UInt(shift)),
|
|
right);
|
|
break;
|
|
}
|
|
default:
|
|
error = $"unsupported scalar opcode {instruction.Opcode}";
|
|
return false;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
StoreS(destination, result);
|
|
return true;
|
|
}
|
|
|
|
private bool TryEmitScalarCompare(
|
|
Gen5ShaderInstruction instruction,
|
|
out string error)
|
|
{
|
|
error = string.Empty;
|
|
if (instruction.Sources.Count < 2)
|
|
{
|
|
error = "missing scalar compare source";
|
|
return false;
|
|
}
|
|
|
|
var left = GetRawSource(instruction, 0);
|
|
var right = GetRawSource(instruction, 1);
|
|
if (instruction.Opcode is "SBitcmp0B32" or "SBitcmp1B32")
|
|
{
|
|
var shifted = ShiftRightLogical(
|
|
left,
|
|
BitwiseAnd(right, UInt(31)));
|
|
var isSet = IsNotZero(BitwiseAnd(shifted, UInt(1)));
|
|
Store(
|
|
_scc,
|
|
instruction.Opcode == "SBitcmp1B32"
|
|
? isSet
|
|
: _module.AddInstruction(
|
|
SpirvOp.LogicalNot,
|
|
_boolType,
|
|
isSet));
|
|
return true;
|
|
}
|
|
|
|
var operation = instruction.Opcode switch
|
|
{
|
|
"SCmpEqI32" or "SCmpEqU32" => SpirvOp.IEqual,
|
|
"SCmpLgI32" or "SCmpLgU32" => SpirvOp.INotEqual,
|
|
"SCmpGtI32" => SpirvOp.SGreaterThan,
|
|
"SCmpGeI32" => SpirvOp.SGreaterThanEqual,
|
|
"SCmpLtI32" => SpirvOp.SLessThan,
|
|
"SCmpLeI32" => SpirvOp.SLessThanEqual,
|
|
"SCmpGtU32" => SpirvOp.UGreaterThan,
|
|
"SCmpGeU32" => SpirvOp.UGreaterThanEqual,
|
|
"SCmpLtU32" => SpirvOp.ULessThan,
|
|
"SCmpLeU32" => SpirvOp.ULessThanEqual,
|
|
_ => SpirvOp.Nop,
|
|
};
|
|
if (operation == SpirvOp.Nop)
|
|
{
|
|
error = $"unsupported scalar compare {instruction.Opcode}";
|
|
return false;
|
|
}
|
|
|
|
if (instruction.Opcode.EndsWith("I32", StringComparison.Ordinal))
|
|
{
|
|
left = Bitcast(_intType, left);
|
|
right = Bitcast(_intType, right);
|
|
}
|
|
|
|
Store(_scc, _module.AddInstruction(operation, _boolType, left, right));
|
|
return true;
|
|
}
|
|
|
|
private bool TryEmitScalarCompareK(
|
|
Gen5ShaderInstruction instruction,
|
|
uint destination,
|
|
uint immediate,
|
|
out string error)
|
|
{
|
|
error = string.Empty;
|
|
var left = LoadS(destination);
|
|
var right = UInt(immediate);
|
|
var operation = instruction.Opcode switch
|
|
{
|
|
"SCmpkEqI32" or "SCmpkEqU32" => SpirvOp.IEqual,
|
|
"SCmpkLgI32" or "SCmpkLgU32" => SpirvOp.INotEqual,
|
|
"SCmpkGtI32" => SpirvOp.SGreaterThan,
|
|
"SCmpkGeI32" => SpirvOp.SGreaterThanEqual,
|
|
"SCmpkLtI32" => SpirvOp.SLessThan,
|
|
"SCmpkLeI32" => SpirvOp.SLessThanEqual,
|
|
"SCmpkGtU32" => SpirvOp.UGreaterThan,
|
|
"SCmpkGeU32" => SpirvOp.UGreaterThanEqual,
|
|
"SCmpkLtU32" => SpirvOp.ULessThan,
|
|
"SCmpkLeU32" => SpirvOp.ULessThanEqual,
|
|
_ => SpirvOp.Nop,
|
|
};
|
|
if (operation == SpirvOp.Nop)
|
|
{
|
|
error = $"unsupported scalar immediate compare {instruction.Opcode}";
|
|
return false;
|
|
}
|
|
|
|
if (instruction.Opcode.EndsWith("I32", StringComparison.Ordinal))
|
|
{
|
|
left = Bitcast(_intType, left);
|
|
right = Bitcast(_intType, right);
|
|
}
|
|
|
|
Store(_scc, _module.AddInstruction(operation, _boolType, left, right));
|
|
return true;
|
|
}
|
|
|
|
private bool TryEmitScalar64(
|
|
Gen5ShaderInstruction instruction,
|
|
uint destination,
|
|
out string error)
|
|
{
|
|
error = string.Empty;
|
|
var left = GetRawSource64(instruction, 0);
|
|
if (instruction.Opcode.EndsWith("SaveexecB64", StringComparison.Ordinal))
|
|
{
|
|
var oldExec = BooleanToLaneMask(Load(_boolType, _exec));
|
|
var notLeft = _module.AddInstruction(SpirvOp.Not, _ulongType, left);
|
|
var newExec = instruction.Opcode switch
|
|
{
|
|
"SAndSaveexecB64" => _module.AddInstruction(
|
|
SpirvOp.BitwiseAnd, _ulongType, oldExec, left),
|
|
"SOrSaveexecB64" => _module.AddInstruction(
|
|
SpirvOp.BitwiseOr, _ulongType, oldExec, left),
|
|
"SXorSaveexecB64" => _module.AddInstruction(
|
|
SpirvOp.BitwiseXor, _ulongType, oldExec, left),
|
|
"SAndn2SaveexecB64" => _module.AddInstruction(
|
|
SpirvOp.BitwiseAnd,
|
|
_ulongType,
|
|
left,
|
|
_module.AddInstruction(
|
|
SpirvOp.Not,
|
|
_ulongType,
|
|
oldExec)),
|
|
"SAndn1SaveexecB64" => _module.AddInstruction(
|
|
SpirvOp.BitwiseAnd,
|
|
_ulongType,
|
|
notLeft,
|
|
oldExec),
|
|
"SOrn1SaveexecB64" => _module.AddInstruction(
|
|
SpirvOp.BitwiseOr,
|
|
_ulongType,
|
|
notLeft,
|
|
oldExec),
|
|
"SOrn2SaveexecB64" => _module.AddInstruction(
|
|
SpirvOp.BitwiseOr,
|
|
_ulongType,
|
|
left,
|
|
_module.AddInstruction(
|
|
SpirvOp.Not,
|
|
_ulongType,
|
|
oldExec)),
|
|
"SNandSaveexecB64" => _module.AddInstruction(
|
|
SpirvOp.Not,
|
|
_ulongType,
|
|
_module.AddInstruction(
|
|
SpirvOp.BitwiseAnd,
|
|
_ulongType,
|
|
left,
|
|
oldExec)),
|
|
"SNorSaveexecB64" => _module.AddInstruction(
|
|
SpirvOp.Not,
|
|
_ulongType,
|
|
_module.AddInstruction(
|
|
SpirvOp.BitwiseOr,
|
|
_ulongType,
|
|
left,
|
|
oldExec)),
|
|
"SXnorSaveexecB64" => _module.AddInstruction(
|
|
SpirvOp.Not,
|
|
_ulongType,
|
|
_module.AddInstruction(
|
|
SpirvOp.BitwiseXor,
|
|
_ulongType,
|
|
left,
|
|
oldExec)),
|
|
_ => 0u,
|
|
};
|
|
if (newExec == 0)
|
|
{
|
|
error =
|
|
$"unsupported scalar 64-bit opcode {instruction.Opcode}";
|
|
return false;
|
|
}
|
|
|
|
StoreS64(destination, oldExec);
|
|
StoreS64(126, newExec);
|
|
Store(_scc, IsNotZero64(newExec));
|
|
return true;
|
|
}
|
|
|
|
if (instruction.Opcode is "SLshlB64" or "SLshrB64")
|
|
{
|
|
if (instruction.Sources.Count < 2)
|
|
{
|
|
error = "missing scalar 64-bit shift source";
|
|
return false;
|
|
}
|
|
|
|
var shift = _module.AddInstruction(
|
|
SpirvOp.UConvert,
|
|
_ulongType,
|
|
GetRawSource(instruction, 1));
|
|
var shiftedValue = instruction.Opcode == "SLshlB64"
|
|
? ShiftLeftLogical64(left, shift)
|
|
: ShiftRightLogical64(left, shift);
|
|
StoreS64(destination, shiftedValue);
|
|
Store(_scc, IsNotZero64(shiftedValue));
|
|
return true;
|
|
}
|
|
|
|
if (instruction.Opcode is "SBfeU64" or "SBfeI64")
|
|
{
|
|
if (instruction.Sources.Count < 2)
|
|
{
|
|
error = "missing scalar 64-bit bitfield source";
|
|
return false;
|
|
}
|
|
|
|
var control = GetRawSource(instruction, 1);
|
|
var offset = BitwiseAnd(control, UInt(63));
|
|
var requestedWidth = BitwiseAnd(
|
|
ShiftRightLogical(control, UInt(16)),
|
|
UInt(0x7F));
|
|
var remaining = _module.AddInstruction(
|
|
SpirvOp.ISub,
|
|
_uintType,
|
|
UInt(64),
|
|
offset);
|
|
var width = Ext(
|
|
38,
|
|
_uintType,
|
|
requestedWidth,
|
|
remaining);
|
|
var offset64 = _module.AddInstruction(
|
|
SpirvOp.UConvert,
|
|
_ulongType,
|
|
offset);
|
|
var width64 = _module.AddInstruction(
|
|
SpirvOp.UConvert,
|
|
_ulongType,
|
|
width);
|
|
var one64 = _module.Constant64(_ulongType, 1);
|
|
var shifted = ShiftRightLogical64(left, offset64);
|
|
var partialMask = _module.AddInstruction(
|
|
SpirvOp.ISub,
|
|
_ulongType,
|
|
ShiftLeftLogical64(one64, width64),
|
|
one64);
|
|
var fullWidth = _module.AddInstruction(
|
|
SpirvOp.IEqual,
|
|
_boolType,
|
|
width,
|
|
UInt(64));
|
|
var mask = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_ulongType,
|
|
fullWidth,
|
|
_module.Constant64(_ulongType, ulong.MaxValue),
|
|
partialMask);
|
|
var extracted = _module.AddInstruction(
|
|
SpirvOp.BitwiseAnd,
|
|
_ulongType,
|
|
shifted,
|
|
mask);
|
|
if (instruction.Opcode == "SBfeI64")
|
|
{
|
|
var signShift = _module.AddInstruction(
|
|
SpirvOp.ISub,
|
|
_uintType,
|
|
width,
|
|
UInt(1));
|
|
var signBit = ShiftLeftLogical64(
|
|
one64,
|
|
_module.AddInstruction(
|
|
SpirvOp.UConvert,
|
|
_ulongType,
|
|
signShift));
|
|
var signExtended = _module.AddInstruction(
|
|
SpirvOp.ISub,
|
|
_ulongType,
|
|
_module.AddInstruction(
|
|
SpirvOp.BitwiseXor,
|
|
_ulongType,
|
|
extracted,
|
|
signBit),
|
|
signBit);
|
|
extracted = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_ulongType,
|
|
_module.AddInstruction(
|
|
SpirvOp.IEqual,
|
|
_boolType,
|
|
width,
|
|
UInt(0)),
|
|
_module.Constant64(_ulongType, 0),
|
|
signExtended);
|
|
}
|
|
|
|
StoreS64(destination, extracted);
|
|
Store(_scc, IsNotZero64(extracted));
|
|
return true;
|
|
}
|
|
|
|
uint value;
|
|
if (instruction.Opcode is "SMovB64" or "SWqmB64")
|
|
{
|
|
value = left;
|
|
}
|
|
else if (instruction.Opcode == "SNotB64")
|
|
{
|
|
value = _module.AddInstruction(SpirvOp.Not, _ulongType, left);
|
|
}
|
|
else
|
|
{
|
|
if (instruction.Sources.Count < 2)
|
|
{
|
|
error = "missing scalar 64-bit source";
|
|
return false;
|
|
}
|
|
|
|
var right = GetRawSource64(instruction, 1);
|
|
value = instruction.Opcode switch
|
|
{
|
|
"SAndB64" => _module.AddInstruction(
|
|
SpirvOp.BitwiseAnd, _ulongType, left, right),
|
|
"SOrB64" => _module.AddInstruction(
|
|
SpirvOp.BitwiseOr, _ulongType, left, right),
|
|
"SXorB64" => _module.AddInstruction(
|
|
SpirvOp.BitwiseXor, _ulongType, left, right),
|
|
"SNandB64" => _module.AddInstruction(
|
|
SpirvOp.Not,
|
|
_ulongType,
|
|
_module.AddInstruction(
|
|
SpirvOp.BitwiseAnd, _ulongType, left, right)),
|
|
"SNorB64" => _module.AddInstruction(
|
|
SpirvOp.Not,
|
|
_ulongType,
|
|
_module.AddInstruction(
|
|
SpirvOp.BitwiseOr, _ulongType, left, right)),
|
|
"SXnorB64" => _module.AddInstruction(
|
|
SpirvOp.Not,
|
|
_ulongType,
|
|
_module.AddInstruction(
|
|
SpirvOp.BitwiseXor, _ulongType, left, right)),
|
|
"SAndn1B64" => _module.AddInstruction(
|
|
SpirvOp.BitwiseAnd,
|
|
_ulongType,
|
|
_module.AddInstruction(SpirvOp.Not, _ulongType, left),
|
|
right),
|
|
"SAndn2B64" => _module.AddInstruction(
|
|
SpirvOp.BitwiseAnd,
|
|
_ulongType,
|
|
left,
|
|
_module.AddInstruction(SpirvOp.Not, _ulongType, right)),
|
|
"SOrn1B64" => _module.AddInstruction(
|
|
SpirvOp.BitwiseOr,
|
|
_ulongType,
|
|
_module.AddInstruction(SpirvOp.Not, _ulongType, left),
|
|
right),
|
|
"SOrn2B64" => _module.AddInstruction(
|
|
SpirvOp.BitwiseOr,
|
|
_ulongType,
|
|
left,
|
|
_module.AddInstruction(SpirvOp.Not, _ulongType, right)),
|
|
"SCselectB64" => _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_ulongType,
|
|
Load(_boolType, _scc),
|
|
left,
|
|
right),
|
|
_ => 0,
|
|
};
|
|
if (value == 0)
|
|
{
|
|
error = $"unsupported scalar 64-bit opcode {instruction.Opcode}";
|
|
return false;
|
|
}
|
|
}
|
|
|
|
StoreS64(destination, value);
|
|
if (instruction.Opcode is
|
|
"SNotB64" or
|
|
"SAndB64" or
|
|
"SOrB64" or
|
|
"SXorB64" or
|
|
"SAndn1B64" or
|
|
"SAndn2B64" or
|
|
"SOrn1B64" or
|
|
"SOrn2B64" or
|
|
"SNandB64" or
|
|
"SNorB64" or
|
|
"SXnorB64")
|
|
{
|
|
Store(_scc, IsNotZero64(value));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private uint GetRawSource(
|
|
Gen5ShaderInstruction instruction,
|
|
int sourceIndex)
|
|
{
|
|
if ((uint)sourceIndex >= instruction.Sources.Count)
|
|
{
|
|
throw new InvalidOperationException($"missing source {sourceIndex}");
|
|
}
|
|
|
|
var operand = instruction.Sources[sourceIndex];
|
|
uint value = operand.Kind switch
|
|
{
|
|
Gen5OperandKind.VectorRegister => LoadV(operand.Value),
|
|
Gen5OperandKind.ScalarRegister => LoadS(operand.Value),
|
|
Gen5OperandKind.LiteralConstant => UInt(operand.Value),
|
|
Gen5OperandKind.EncodedConstant when TryDecodeInlineConstant(
|
|
operand.Value,
|
|
out var inline) => UInt(inline),
|
|
_ => throw new InvalidOperationException($"unsupported source {operand}"),
|
|
};
|
|
|
|
if (instruction.Control is Gen5SdwaControl sdwa)
|
|
{
|
|
var selector = sourceIndex switch
|
|
{
|
|
0 => sdwa.Source0Select,
|
|
1 => sdwa.Source1Select,
|
|
_ => 6u,
|
|
};
|
|
value = selector switch
|
|
{
|
|
0 => BitwiseAnd(value, UInt(0xFF)),
|
|
1 => BitwiseAnd(ShiftRightLogical(value, UInt(8)), UInt(0xFF)),
|
|
2 => BitwiseAnd(ShiftRightLogical(value, UInt(16)), UInt(0xFF)),
|
|
3 => BitwiseAnd(ShiftRightLogical(value, UInt(24)), UInt(0xFF)),
|
|
4 => BitwiseAnd(value, UInt(0xFFFF)),
|
|
5 => BitwiseAnd(ShiftRightLogical(value, UInt(16)), UInt(0xFFFF)),
|
|
_ => value,
|
|
};
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
private uint GetFloatSource(
|
|
Gen5ShaderInstruction instruction,
|
|
int sourceIndex)
|
|
{
|
|
var operand = instruction.Sources[sourceIndex];
|
|
uint value;
|
|
if (operand.Kind == Gen5OperandKind.EncodedConstant &&
|
|
operand.Value is >= 128 and <= 192)
|
|
{
|
|
value = Float(operand.Value - 128);
|
|
}
|
|
else if (operand.Kind == Gen5OperandKind.EncodedConstant &&
|
|
operand.Value is >= 193 and <= 208)
|
|
{
|
|
value = Float(-(operand.Value - 192));
|
|
}
|
|
else
|
|
{
|
|
value = Bitcast(_floatType, GetRawSource(instruction, sourceIndex));
|
|
}
|
|
|
|
uint absoluteMask = 0;
|
|
uint negateMask = 0;
|
|
switch (instruction.Control)
|
|
{
|
|
case Gen5Vop3Control control:
|
|
absoluteMask = control.AbsoluteMask;
|
|
negateMask = control.NegateMask;
|
|
break;
|
|
case Gen5SdwaControl control:
|
|
absoluteMask = control.AbsoluteMask;
|
|
negateMask = control.NegateMask;
|
|
break;
|
|
case Gen5DppControl control:
|
|
absoluteMask = control.AbsoluteMask;
|
|
negateMask = control.NegateMask;
|
|
break;
|
|
}
|
|
|
|
if ((absoluteMask & (1u << sourceIndex)) != 0)
|
|
{
|
|
value = Ext(4, _floatType, value);
|
|
}
|
|
|
|
if ((negateMask & (1u << sourceIndex)) != 0)
|
|
{
|
|
value = _module.AddInstruction(SpirvOp.FNegate, _floatType, value);
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
private uint GetRawSource64(
|
|
Gen5ShaderInstruction instruction,
|
|
int sourceIndex)
|
|
{
|
|
var operand = instruction.Sources[sourceIndex];
|
|
if (operand.Kind == Gen5OperandKind.ScalarRegister)
|
|
{
|
|
return LoadS64(operand.Value);
|
|
}
|
|
|
|
var low = GetRawSource(instruction, sourceIndex);
|
|
return _module.AddInstruction(SpirvOp.UConvert, _ulongType, low);
|
|
}
|
|
|
|
private uint LoadS64(uint register)
|
|
{
|
|
var low = _module.AddInstruction(SpirvOp.UConvert, _ulongType, LoadS(register));
|
|
var high = _module.AddInstruction(
|
|
SpirvOp.UConvert,
|
|
_ulongType,
|
|
LoadS(register + 1));
|
|
high = ShiftLeftLogical64(high, _module.Constant64(_ulongType, 32));
|
|
return _module.AddInstruction(SpirvOp.BitwiseOr, _ulongType, low, high);
|
|
}
|
|
|
|
private void StoreS64(uint register, uint value)
|
|
{
|
|
StoreS(
|
|
register,
|
|
_module.AddInstruction(SpirvOp.UConvert, _uintType, value));
|
|
var high = ShiftRightLogical64(
|
|
value,
|
|
_module.Constant64(_ulongType, 32));
|
|
StoreS(
|
|
register + 1,
|
|
_module.AddInstruction(SpirvOp.UConvert, _uintType, high));
|
|
}
|
|
|
|
private uint EmitFloatBinary(
|
|
Gen5ShaderInstruction instruction,
|
|
SpirvOp operation,
|
|
bool reverse = false)
|
|
{
|
|
var left = GetFloatSource(instruction, reverse ? 1 : 0);
|
|
var right = GetFloatSource(instruction, reverse ? 0 : 1);
|
|
return EmitFloatResult(
|
|
instruction,
|
|
_module.AddInstruction(operation, _floatType, left, right));
|
|
}
|
|
|
|
private uint EmitFloatExtBinary(
|
|
Gen5ShaderInstruction instruction,
|
|
uint operation) =>
|
|
EmitFloatResult(
|
|
instruction,
|
|
Ext(
|
|
operation,
|
|
_floatType,
|
|
GetFloatSource(instruction, 0),
|
|
GetFloatSource(instruction, 1)));
|
|
|
|
private uint EmitFloatTernaryExt(
|
|
Gen5ShaderInstruction instruction,
|
|
uint operation)
|
|
{
|
|
var first = Ext(
|
|
operation,
|
|
_floatType,
|
|
GetFloatSource(instruction, 0),
|
|
GetFloatSource(instruction, 1));
|
|
return EmitFloatResult(
|
|
instruction,
|
|
Ext(operation, _floatType, first, GetFloatSource(instruction, 2)));
|
|
}
|
|
|
|
private uint EmitIntegerBinary(
|
|
Gen5ShaderInstruction instruction,
|
|
SpirvOp operation,
|
|
bool reverse = false)
|
|
{
|
|
var left = GetRawSource(instruction, reverse ? 1 : 0);
|
|
var right = GetRawSource(instruction, reverse ? 0 : 1);
|
|
if (operation == SpirvOp.ShiftLeftLogical)
|
|
{
|
|
return ShiftLeftLogical(left, right);
|
|
}
|
|
|
|
if (operation == SpirvOp.ShiftRightLogical)
|
|
{
|
|
return ShiftRightLogical(left, right);
|
|
}
|
|
|
|
if (operation == SpirvOp.ShiftRightArithmetic)
|
|
{
|
|
return ShiftRightArithmetic(left, right);
|
|
}
|
|
|
|
return _module.AddInstruction(operation, _uintType, left, right);
|
|
}
|
|
|
|
private enum CubeCoordinate
|
|
{
|
|
Id,
|
|
Sc,
|
|
Tc,
|
|
Ma,
|
|
}
|
|
|
|
private uint EmitCvtOffF32I4(Gen5ShaderInstruction instruction)
|
|
{
|
|
var index = BitwiseAnd(GetRawSource(instruction, 0), UInt(15));
|
|
ReadOnlySpan<float> table =
|
|
[
|
|
0.0f,
|
|
0.0625f,
|
|
0.1250f,
|
|
0.1875f,
|
|
0.2500f,
|
|
0.3125f,
|
|
0.3750f,
|
|
0.4375f,
|
|
-0.5000f,
|
|
-0.4375f,
|
|
-0.3750f,
|
|
-0.3125f,
|
|
-0.2500f,
|
|
-0.1875f,
|
|
-0.1250f,
|
|
-0.0625f,
|
|
];
|
|
|
|
var result = UInt(BitConverter.SingleToUInt32Bits(table[^1]));
|
|
for (var tableIndex = table.Length - 2; tableIndex >= 0; tableIndex--)
|
|
{
|
|
var matches = _module.AddInstruction(
|
|
SpirvOp.IEqual,
|
|
_boolType,
|
|
index,
|
|
UInt((uint)tableIndex));
|
|
result = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_uintType,
|
|
matches,
|
|
UInt(BitConverter.SingleToUInt32Bits(table[tableIndex])),
|
|
result);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private uint EmitCubeCoordinate(
|
|
Gen5ShaderInstruction instruction,
|
|
CubeCoordinate coordinate)
|
|
{
|
|
var x = GetFloatSource(instruction, 0);
|
|
var y = GetFloatSource(instruction, 1);
|
|
var z = GetFloatSource(instruction, 2);
|
|
var nx = _module.AddInstruction(SpirvOp.FNegate, _floatType, x);
|
|
var ny = _module.AddInstruction(SpirvOp.FNegate, _floatType, y);
|
|
var nz = _module.AddInstruction(SpirvOp.FNegate, _floatType, z);
|
|
var ax = Ext(4, _floatType, x);
|
|
var ay = Ext(4, _floatType, y);
|
|
var az = Ext(4, _floatType, z);
|
|
var amaxXY = Ext(40, _floatType, ax, ay);
|
|
var amax = Ext(40, _floatType, az, amaxXY);
|
|
var ma = _module.AddInstruction(
|
|
SpirvOp.FMul,
|
|
_floatType,
|
|
Float(2),
|
|
amax);
|
|
if (coordinate == CubeCoordinate.Ma)
|
|
{
|
|
return EmitFloatResult(instruction, ma);
|
|
}
|
|
|
|
var isZMax = _module.AddInstruction(
|
|
SpirvOp.FOrdGreaterThanEqual,
|
|
_boolType,
|
|
az,
|
|
amaxXY);
|
|
var yGreaterOrEqualX = _module.AddInstruction(
|
|
SpirvOp.FOrdGreaterThanEqual,
|
|
_boolType,
|
|
ay,
|
|
ax);
|
|
var isYMax = _module.AddInstruction(
|
|
SpirvOp.LogicalAnd,
|
|
_boolType,
|
|
_module.AddInstruction(SpirvOp.LogicalNot, _boolType, isZMax),
|
|
yGreaterOrEqualX);
|
|
if (coordinate == CubeCoordinate.Id)
|
|
{
|
|
var isZNeg = _module.AddInstruction(
|
|
SpirvOp.FOrdLessThan,
|
|
_boolType,
|
|
z,
|
|
Float(0));
|
|
var isYNeg = _module.AddInstruction(
|
|
SpirvOp.FOrdLessThan,
|
|
_boolType,
|
|
y,
|
|
Float(0));
|
|
var isXNeg = _module.AddInstruction(
|
|
SpirvOp.FOrdLessThan,
|
|
_boolType,
|
|
x,
|
|
Float(0));
|
|
var zCase = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_floatType,
|
|
isZNeg,
|
|
Float(5),
|
|
Float(4));
|
|
var yCase = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_floatType,
|
|
isYNeg,
|
|
Float(3),
|
|
Float(2));
|
|
var xCase = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_floatType,
|
|
isXNeg,
|
|
Float(1),
|
|
Float(0));
|
|
var xyCase = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_floatType,
|
|
yGreaterOrEqualX,
|
|
yCase,
|
|
xCase);
|
|
return EmitFloatResult(
|
|
instruction,
|
|
_module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_floatType,
|
|
isZMax,
|
|
zCase,
|
|
xyCase));
|
|
}
|
|
|
|
if (coordinate == CubeCoordinate.Sc)
|
|
{
|
|
var isZNeg = _module.AddInstruction(
|
|
SpirvOp.FOrdLessThan,
|
|
_boolType,
|
|
z,
|
|
Float(0));
|
|
var isXNeg = _module.AddInstruction(
|
|
SpirvOp.FOrdLessThan,
|
|
_boolType,
|
|
x,
|
|
Float(0));
|
|
var zCase = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_floatType,
|
|
isZNeg,
|
|
nx,
|
|
x);
|
|
var xCase = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_floatType,
|
|
isXNeg,
|
|
z,
|
|
nz);
|
|
var nonZCase = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_floatType,
|
|
isYMax,
|
|
x,
|
|
xCase);
|
|
return EmitFloatResult(
|
|
instruction,
|
|
_module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_floatType,
|
|
isZMax,
|
|
zCase,
|
|
nonZCase));
|
|
}
|
|
|
|
var tcIsYNeg = _module.AddInstruction(
|
|
SpirvOp.FOrdLessThan,
|
|
_boolType,
|
|
y,
|
|
Float(0));
|
|
var tcYCase = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_floatType,
|
|
tcIsYNeg,
|
|
nz,
|
|
z);
|
|
return EmitFloatResult(
|
|
instruction,
|
|
_module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_floatType,
|
|
isYMax,
|
|
tcYCase,
|
|
ny));
|
|
}
|
|
|
|
private uint EmitAddWithCarry(Gen5ShaderInstruction instruction)
|
|
{
|
|
var left = GetRawSource(instruction, 0);
|
|
var right = GetRawSource(instruction, 1);
|
|
var carryIn = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_uintType,
|
|
Load(_boolType, _vcc),
|
|
UInt(1),
|
|
UInt(0));
|
|
var partial = IAdd(left, right);
|
|
var result = IAdd(partial, carryIn);
|
|
var carry = _module.AddInstruction(
|
|
SpirvOp.LogicalOr,
|
|
_boolType,
|
|
_module.AddInstruction(SpirvOp.ULessThan, _boolType, partial, left),
|
|
_module.AddInstruction(SpirvOp.ULessThan, _boolType, result, partial));
|
|
StoreWaveMask(106, carry);
|
|
return result;
|
|
}
|
|
|
|
private uint EmitSubtractWithBorrow(
|
|
Gen5ShaderInstruction instruction,
|
|
bool reverse)
|
|
{
|
|
var left = GetRawSource(instruction, reverse ? 1 : 0);
|
|
var right = GetRawSource(instruction, reverse ? 0 : 1);
|
|
var borrowIn = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_uintType,
|
|
Load(_boolType, _vcc),
|
|
UInt(1),
|
|
UInt(0));
|
|
var partial = _module.AddInstruction(SpirvOp.ISub, _uintType, left, right);
|
|
var result = _module.AddInstruction(
|
|
SpirvOp.ISub,
|
|
_uintType,
|
|
partial,
|
|
borrowIn);
|
|
var borrow = _module.AddInstruction(
|
|
SpirvOp.LogicalOr,
|
|
_boolType,
|
|
_module.AddInstruction(SpirvOp.ULessThan, _boolType, left, right),
|
|
_module.AddInstruction(
|
|
SpirvOp.ULessThan,
|
|
_boolType,
|
|
partial,
|
|
borrowIn));
|
|
StoreWaveMask(106, borrow);
|
|
return result;
|
|
}
|
|
|
|
private void StoreCarryOut(
|
|
Gen5ShaderInstruction instruction,
|
|
uint carry)
|
|
{
|
|
if (instruction.Control is Gen5Vop3Control { ScalarDestination: { } register })
|
|
{
|
|
StoreS(
|
|
register,
|
|
_module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_uintType,
|
|
carry,
|
|
UInt(1),
|
|
UInt(0)));
|
|
if (register == 106)
|
|
{
|
|
StoreWaveMask(106, carry);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
StoreWaveMask(106, carry);
|
|
}
|
|
|
|
private uint EmitPermlane16(
|
|
Gen5ShaderInstruction instruction,
|
|
bool exchangeRows)
|
|
{
|
|
var value = GetRawSource(instruction, 0);
|
|
var selectorLow = GetRawSource(instruction, 1);
|
|
var selectorHigh = GetRawSource(instruction, 2);
|
|
var lane = Load(_uintType, _subgroupInvocationIdInput);
|
|
var localLane = BitwiseAnd(lane, UInt(15));
|
|
var lowHalf = _module.AddInstruction(
|
|
SpirvOp.ULessThan,
|
|
_boolType,
|
|
localLane,
|
|
UInt(8));
|
|
var lowShift = ShiftLeftLogical(localLane, UInt(2));
|
|
var highLane = _module.AddInstruction(
|
|
SpirvOp.ISub,
|
|
_uintType,
|
|
localLane,
|
|
UInt(8));
|
|
var highShift = ShiftLeftLogical(highLane, UInt(2));
|
|
var lowSelector = BitwiseAnd(
|
|
ShiftRightLogical(selectorLow, lowShift),
|
|
UInt(15));
|
|
var highSelector = BitwiseAnd(
|
|
ShiftRightLogical(selectorHigh, highShift),
|
|
UInt(15));
|
|
var selector = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_uintType,
|
|
lowHalf,
|
|
lowSelector,
|
|
highSelector);
|
|
var rowBase = BitwiseAnd(lane, UInt(0xFFFF_FFF0));
|
|
if (exchangeRows)
|
|
{
|
|
rowBase = BitwiseXor(rowBase, UInt(16));
|
|
}
|
|
|
|
var targetLane = IAdd(rowBase, selector);
|
|
return _module.AddInstruction(
|
|
SpirvOp.GroupNonUniformShuffle,
|
|
_uintType,
|
|
UInt(3),
|
|
value,
|
|
targetLane);
|
|
}
|
|
|
|
private uint EmitFloatResult(
|
|
Gen5ShaderInstruction instruction,
|
|
uint value)
|
|
{
|
|
uint outputModifier = 0;
|
|
var clamp = false;
|
|
switch (instruction.Control)
|
|
{
|
|
case Gen5Vop3Control control:
|
|
outputModifier = control.OutputModifier;
|
|
clamp = control.Clamp;
|
|
break;
|
|
case Gen5SdwaControl control:
|
|
outputModifier = control.OutputModifier;
|
|
clamp = control.Clamp;
|
|
break;
|
|
}
|
|
|
|
value = outputModifier switch
|
|
{
|
|
1 => _module.AddInstruction(SpirvOp.FMul, _floatType, value, Float(2)),
|
|
2 => _module.AddInstruction(SpirvOp.FMul, _floatType, value, Float(4)),
|
|
3 => _module.AddInstruction(SpirvOp.FMul, _floatType, value, Float(0.5f)),
|
|
_ => value,
|
|
};
|
|
if (clamp)
|
|
{
|
|
value = Ext(43, _floatType, value, Float(0), Float(1));
|
|
}
|
|
|
|
return Bitcast(_uintType, value);
|
|
}
|
|
|
|
private uint TruncateFloat32ForPack(uint value)
|
|
{
|
|
var raw = BitwiseAnd(
|
|
Bitcast(_uintType, value),
|
|
UInt(0xFFFF_E000));
|
|
return Bitcast(_floatType, raw);
|
|
}
|
|
|
|
private uint Ext(uint operation, uint resultType, params uint[] operands)
|
|
{
|
|
var values = new uint[2 + operands.Length];
|
|
values[0] = _glsl;
|
|
values[1] = operation;
|
|
operands.CopyTo(values, 2);
|
|
return _module.AddInstruction(SpirvOp.ExtInst, resultType, values);
|
|
}
|
|
|
|
private uint IsNotZero(uint value) =>
|
|
_module.AddInstruction(SpirvOp.INotEqual, _boolType, value, UInt(0));
|
|
|
|
private uint IsNotZero64(uint value) =>
|
|
_module.AddInstruction(
|
|
SpirvOp.INotEqual,
|
|
_boolType,
|
|
value,
|
|
_module.Constant64(_ulongType, 0));
|
|
|
|
private uint SignBit(uint value) =>
|
|
ShiftRightLogical(value, UInt(31));
|
|
|
|
private uint SignedAddOverflow(uint left, uint right, uint result)
|
|
{
|
|
var leftSign = SignBit(left);
|
|
var rightSign = SignBit(right);
|
|
var resultSign = SignBit(result);
|
|
var sameSourceSign = _module.AddInstruction(
|
|
SpirvOp.IEqual,
|
|
_boolType,
|
|
leftSign,
|
|
rightSign);
|
|
var resultSignChanged = _module.AddInstruction(
|
|
SpirvOp.INotEqual,
|
|
_boolType,
|
|
leftSign,
|
|
resultSign);
|
|
return _module.AddInstruction(
|
|
SpirvOp.LogicalAnd,
|
|
_boolType,
|
|
sameSourceSign,
|
|
resultSignChanged);
|
|
}
|
|
|
|
private uint SignedSubOverflow(uint left, uint right, uint result)
|
|
{
|
|
var leftSign = SignBit(left);
|
|
var rightSign = SignBit(right);
|
|
var resultSign = SignBit(result);
|
|
var differentSourceSign = _module.AddInstruction(
|
|
SpirvOp.INotEqual,
|
|
_boolType,
|
|
leftSign,
|
|
rightSign);
|
|
var resultSignChanged = _module.AddInstruction(
|
|
SpirvOp.INotEqual,
|
|
_boolType,
|
|
leftSign,
|
|
resultSign);
|
|
return _module.AddInstruction(
|
|
SpirvOp.LogicalAnd,
|
|
_boolType,
|
|
differentSourceSign,
|
|
resultSignChanged);
|
|
}
|
|
|
|
private static bool TryDecodeInlineConstant(uint encoded, out uint value)
|
|
{
|
|
if (encoded == 125)
|
|
{
|
|
value = 0;
|
|
return true;
|
|
}
|
|
|
|
if (encoded is >= 128 and <= 192)
|
|
{
|
|
value = encoded - 128;
|
|
return true;
|
|
}
|
|
|
|
if (encoded is >= 193 and <= 208)
|
|
{
|
|
value = unchecked((uint)-(int)(encoded - 192));
|
|
return true;
|
|
}
|
|
|
|
var floatingPoint = encoded switch
|
|
{
|
|
240 => 0.5f,
|
|
241 => -0.5f,
|
|
242 => 1.0f,
|
|
243 => -1.0f,
|
|
244 => 2.0f,
|
|
245 => -2.0f,
|
|
246 => 4.0f,
|
|
247 => -4.0f,
|
|
248 => 1.0f / (2.0f * MathF.PI),
|
|
_ => float.NaN,
|
|
};
|
|
if (float.IsNaN(floatingPoint))
|
|
{
|
|
value = 0;
|
|
return false;
|
|
}
|
|
|
|
value = BitConverter.SingleToUInt32Bits(floatingPoint);
|
|
return true;
|
|
}
|
|
}
|
|
}
|