mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 10:56:58 +02:00
shader: implement V_COS_F16
This commit is contained in:
@@ -179,6 +179,7 @@ enum class Opcode {
|
||||
V_CEIL_F16,
|
||||
V_TRUNC_F16,
|
||||
V_RNDNE_F16,
|
||||
V_COS_F16,
|
||||
V_SIN_F32,
|
||||
V_COS_F32,
|
||||
V_NOT_B32,
|
||||
|
||||
@@ -137,6 +137,7 @@ constexpr OpcodeMap VOP1_OPCODE_LIST[] = {
|
||||
{0x5cu, Opcode::V_CEIL_F16},
|
||||
{0x5du, Opcode::V_TRUNC_F16},
|
||||
{0x5eu, Opcode::V_RNDNE_F16},
|
||||
{0x61u, Opcode::V_COS_F16},
|
||||
};
|
||||
|
||||
constexpr auto VOP1_OPS = Detail::MakeOpcodeTable<0x100>(VOP1_OPCODE_LIST);
|
||||
@@ -188,6 +189,7 @@ constexpr OpcodeMap VOP3_ENCODED_VOP1_OPCODE_LIST[] = {
|
||||
{0x5cu, Opcode::V_CEIL_F16},
|
||||
{0x5du, Opcode::V_TRUNC_F16},
|
||||
{0x5eu, Opcode::V_RNDNE_F16},
|
||||
{0x61u, Opcode::V_COS_F16},
|
||||
};
|
||||
|
||||
constexpr auto VOP3_ENCODED_VOP1_OPS = Detail::MakeOpcodeTable<0x80>(VOP3_ENCODED_VOP1_OPCODE_LIST);
|
||||
@@ -482,6 +484,7 @@ bool IsVop1FloatSourceOpcode(Opcode opcode) {
|
||||
case Opcode::V_CEIL_F16:
|
||||
case Opcode::V_TRUNC_F16:
|
||||
case Opcode::V_RNDNE_F16:
|
||||
case Opcode::V_COS_F16:
|
||||
case Opcode::V_SIN_F32:
|
||||
case Opcode::V_COS_F32: return true;
|
||||
default: return false;
|
||||
@@ -548,6 +551,8 @@ constexpr Vop1SdwaRule VOP1_SDWA_RULES[] = {
|
||||
SdwaSelWords() | SdwaSelFull(), false},
|
||||
{Opcode::V_RNDNE_F16, SdwaSelWords() | SdwaSelFull(), SdwaSelWords(),
|
||||
SdwaSelWords() | SdwaSelFull(), false},
|
||||
{Opcode::V_COS_F16, SdwaSelWords() | SdwaSelFull(), SdwaSelWords(),
|
||||
SdwaSelWords() | SdwaSelFull(), true},
|
||||
{Opcode::V_CVT_U32_F32, SdwaSelFull(), SdwaSelBytes() | SdwaSelWords(), SdwaSelFull(), false},
|
||||
{Opcode::V_CVT_I32_F32, SdwaSelFull(), SdwaSelBytes() | SdwaSelWords(), SdwaSelFull(), false},
|
||||
{Opcode::V_CVT_RPI_I32_F32, SdwaSelFull(), SdwaSelBytes() | SdwaSelWords(), SdwaSelFull(),
|
||||
@@ -832,6 +837,7 @@ bool IsVop1FloatResultOpcode(Opcode opcode) {
|
||||
case Opcode::V_RSQ_F16:
|
||||
case Opcode::V_LOG_F16:
|
||||
case Opcode::V_EXP_F16:
|
||||
case Opcode::V_COS_F16:
|
||||
case Opcode::V_SIN_F32:
|
||||
case Opcode::V_COS_F32: return true;
|
||||
default: return false;
|
||||
|
||||
@@ -63,6 +63,30 @@ bool Translator::Float16Unary(const Decoder::Instruction& inst, IR::ValueOpcode
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Translator::V_COS_F16(const Decoder::Instruction& inst) {
|
||||
const auto argument = ReadF16AsF32(inst.src0);
|
||||
auto result = IR::F32(ir.Emit(IR::ValueOpcode::FPCos, {argument}));
|
||||
|
||||
// The architectural quarter-cycle result is exactly zero. Evaluating cos(pi/2) with a
|
||||
// rounded F32 PI can otherwise produce a half-precision subnormal instead.
|
||||
const auto fraction = IR::F32(ir.Emit(IR::ValueOpcode::FPFract32, {argument}));
|
||||
const auto quarter = IR::U1(
|
||||
ir.Emit(IR::ValueOpcode::FPOrdEqual32, {fraction, IR::Value::F32(0.25f)}));
|
||||
const auto three_quarters = IR::U1(
|
||||
ir.Emit(IR::ValueOpcode::FPOrdEqual32, {fraction, IR::Value::F32(0.75f)}));
|
||||
result = IR::F32(ir.Emit(IR::ValueOpcode::SelectF32,
|
||||
{ir.LogicalOr(quarter, three_quarters), IR::Value::F32(0.0f), result}));
|
||||
|
||||
const auto magnitude =
|
||||
ir.BitwiseAnd(ir.BitCastU32(argument), IR::U32(IR::Value(0x7fffffffu)));
|
||||
const auto infinite = ir.IEqual(magnitude, IR::U32(IR::Value(0x7f800000u)));
|
||||
result = ApplyF32ResultModifiers(inst.dst, result);
|
||||
const auto bits = PackHalf2x16(result, IR::F32(IR::Value::F32(0.0f)));
|
||||
const auto invalid = IR::U32(IR::Value(inst.dst.clamp ? 0u : 0xfe00u));
|
||||
WriteU16(DestinationOperand(inst), ir.Select(infinite, invalid, bits));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Translator::Float16Binary(const Decoder::Instruction& inst, IR::ValueOpcode opcode,
|
||||
bool reverse) {
|
||||
const auto lhs = ReadF16AsF32(reverse ? inst.src1 : inst.src0);
|
||||
|
||||
@@ -149,6 +149,7 @@ private:
|
||||
bool quiet_snan);
|
||||
bool Float16Unary(const Decoder::Instruction& inst, IR::ValueOpcode opcode,
|
||||
bool invalid_negative);
|
||||
bool V_COS_F16(const Decoder::Instruction& inst);
|
||||
bool Float16Binary(const Decoder::Instruction& inst, IR::ValueOpcode opcode, bool reverse);
|
||||
bool Float16Ternary(const Decoder::Instruction& inst, IR::ValueOpcode opcode, bool accumulator,
|
||||
bool mix);
|
||||
|
||||
@@ -365,6 +365,7 @@ bool Translator::EmitVector(const Decoder::Instruction& inst, std::string* error
|
||||
case O::V_CEIL_F16: return Float16Unary(inst, IR::ValueOpcode::FPCeil32, false);
|
||||
case O::V_TRUNC_F16: return Float16Unary(inst, IR::ValueOpcode::FPTrunc32, false);
|
||||
case O::V_RNDNE_F16: return Float16Unary(inst, IR::ValueOpcode::FPRoundEven32, false);
|
||||
case O::V_COS_F16: return V_COS_F16(inst);
|
||||
case O::V_MIN3_F16: return Float16Ternary(inst, IR::ValueOpcode::FPMinTri32, false, false);
|
||||
case O::V_MAX3_F16: return Float16Ternary(inst, IR::ValueOpcode::FPMaxTri32, false, false);
|
||||
case O::V_MED3_F16: return Float16Ternary(inst, IR::ValueOpcode::FPMedTri32, false, false);
|
||||
|
||||
@@ -11896,6 +11896,7 @@ CoverageClass ClassifyOpcode(ShaderOpcode opcode,
|
||||
case Opcode::V_RSQ_F16:
|
||||
case Opcode::V_LOG_F16:
|
||||
case Opcode::V_EXP_F16:
|
||||
case Opcode::V_COS_F16:
|
||||
case Opcode::V_MAD_MIXLO_F16:
|
||||
case Opcode::V_MAD_MIXHI_F16:
|
||||
case Opcode::DS_MIN_F32:
|
||||
@@ -14489,6 +14490,45 @@ TestCase VectorSpecialF16Ops() {
|
||||
O::BUFFER_STORE_DWORD, O::S_ENDPGM}};
|
||||
}
|
||||
|
||||
TestCase VectorCosF16CapturedSdwaAndEdges() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
std::vector<u32> code;
|
||||
AppendVMovLiteral(&code, 4, 0x34001234u); // high=+0.25h, low sentinel
|
||||
code.push_back(0x7e08c2f9u);
|
||||
code.push_back(0x00051504u); // captured v_cos_f16 v4.word1, v4.word1
|
||||
AppendVMovLiteral(&code, 5, 0x00008000u); // -0.0h
|
||||
code.push_back(EncodeVop1(0x61, 6, Vgpr(5)));
|
||||
AppendVMovLiteral(&code, 7, 0x00007bffu); // max finite
|
||||
code.push_back(EncodeVop1(0x61, 8, Vgpr(7)));
|
||||
AppendVMovLiteral(&code, 9, 0x00007c00u); // +inf
|
||||
code.push_back(EncodeVop1(0x61, 10, Vgpr(9)));
|
||||
AppendVMovLiteral(&code, 11, 0x00000001u); // minimum positive denormal
|
||||
code.push_back(EncodeVop1(0x61, 12, Vgpr(11)));
|
||||
AppendVMovLiteral(&code, 13, 0x00003800u); // +0.5h
|
||||
code.push_back(EncodeVop1(0x61, 14, Vgpr(13)));
|
||||
for (u32 i = 0; i < 6; i++) {
|
||||
AppendStoreVgpr(&code, 4 + i * 2, i);
|
||||
}
|
||||
AppendEnd(&code);
|
||||
|
||||
TestCase test{"VectorCosF16CapturedSdwaAndEdges",
|
||||
code,
|
||||
{},
|
||||
{0x00001234u, 0x00003c00u, 0x00003c00u, 0x0000fe00u,
|
||||
0x00003c00u, 0x0000bc00u},
|
||||
{O::V_MOV_B32, O::V_COS_F16, O::BUFFER_STORE_DWORD,
|
||||
O::S_ENDPGM}};
|
||||
test.decoded_counts = {{"V_COS_F16", 6},
|
||||
{"V_COS_F16 v4.sdwa(sel=5,sext=0), "
|
||||
"v4.sdwa(sel=5,sext=0)",
|
||||
1}};
|
||||
test.ir_counts = {{" = FPCos ", 6}};
|
||||
test.required_spirv = {" Fract ", " Cos ", " PackHalf2x16 "};
|
||||
test.forbidden_spirv = {"OpCapability Float16"};
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase VectorWritelaneIgnoresExecMask() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
@@ -20688,6 +20728,7 @@ std::vector<TestCase> MakeCases() {
|
||||
AddCase(VectorCvtU16F16Sdwa);
|
||||
AddCase(VectorMinMaxMed3F16Ops);
|
||||
AddCase(VectorSpecialF16Ops);
|
||||
AddCase(VectorCosF16CapturedSdwaAndEdges);
|
||||
AddCase(VectorWritelaneIgnoresExecMask);
|
||||
AddCase(VectorReadlaneFromInactiveWrittenLane);
|
||||
AddCase(VectorLaneWave32RuntimeSelectorWraps);
|
||||
|
||||
@@ -4031,6 +4031,22 @@ void TestNewShaderDecoderArchitecture() {
|
||||
ffbh_i32.src0.reg == 14u,
|
||||
"decoder rejected the captured VOP1 V_FFBH_I32 instruction");
|
||||
|
||||
const uint32_t cos_f16_code[] = {0x7e08c2f9u, 0x00051504u};
|
||||
Instruction cos_f16;
|
||||
Check(DecodeInstruction(cos_f16_code, 0u, cos_f16, &error), error.c_str());
|
||||
Check(cos_f16.family == Family::VOP1 &&
|
||||
cos_f16.opcode == Opcode::V_COS_F16 &&
|
||||
cos_f16.word_count == 2u && cos_f16.src_count == 1u &&
|
||||
cos_f16.dst.kind == OperandKind::Vgpr && cos_f16.dst.reg == 4u &&
|
||||
cos_f16.dst.sdwa_sel == 5u &&
|
||||
cos_f16.dst.sdwa_dst_unused == 2u && !cos_f16.dst.clamp &&
|
||||
cos_f16.dst.omod == 0u &&
|
||||
cos_f16.src0.kind == OperandKind::Vgpr &&
|
||||
cos_f16.src0.reg == 4u && cos_f16.src0.sdwa_sel == 5u &&
|
||||
!cos_f16.src0.sdwa_sext && !cos_f16.src0.negate &&
|
||||
!cos_f16.src0.absolute,
|
||||
"decoder rejected the captured VOP1 SDWA V_COS_F16 instruction");
|
||||
|
||||
const uint32_t literal_code[] = {EncodeVop1(0x01, 2, 255u), 0x12345678u};
|
||||
Instruction literal;
|
||||
Check(DecodeInstruction(literal_code, 0u, literal, &error), error.c_str());
|
||||
|
||||
Reference in New Issue
Block a user