mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-22 11:56:58 +02:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c5016d9455 | |||
| 753f285198 | |||
| d7c1896647 |
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <optional>
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
|
||||
namespace {
|
||||
@@ -140,6 +141,74 @@ uint32_t EmitDppWriteCondition(ValueEmitContext& ctx, const IR::DppMoveFlags& fl
|
||||
return writable;
|
||||
}
|
||||
|
||||
uint32_t LoadVertexParameter(EmitterState& state, const InputBinding& input, uint32_t vertex,
|
||||
uint32_t chan) {
|
||||
const auto pointer = state.builder.AllocateId();
|
||||
const auto value = state.builder.AllocateId();
|
||||
state.builder.AddFunction(spv::OpAccessChain,
|
||||
TypePointer(state, spv::StorageClassInput, TypeF32(state)), pointer,
|
||||
input.variable_id, ConstantU32(state, vertex),
|
||||
ConstantU32(state, chan & 3u));
|
||||
state.builder.AddFunction(spv::OpLoad, TypeF32(state), value, pointer);
|
||||
return value;
|
||||
}
|
||||
|
||||
// Interpolates a pixel input channel to f32. A half (0 low, 1 high) selects one f16 of a packed
|
||||
// FP16_INTERP_MODE dword before blending; smooth inputs read by halves are always per-vertex.
|
||||
uint32_t EmitPixelAttribute(EmitterState& state, const InputBinding& input, uint32_t attr,
|
||||
uint32_t chan, std::optional<uint32_t> half) {
|
||||
const auto select = [&](uint32_t value) {
|
||||
if (!half) {
|
||||
return value;
|
||||
}
|
||||
const auto bits = state.builder.AllocateId();
|
||||
const auto unpacked = state.builder.AllocateId();
|
||||
const auto word = state.builder.AllocateId();
|
||||
state.builder.AddFunction(spv::OpBitcast, TypeU32(state), bits, value);
|
||||
state.builder.AddFunction(spv::OpExtInst, TypeF32Vector(state, 2), unpacked,
|
||||
GlslStd450(state), GLSLstd450UnpackHalf2x16, bits);
|
||||
state.builder.AddFunction(spv::OpCompositeExtract, TypeF32(state), word, unpacked, *half);
|
||||
return word;
|
||||
};
|
||||
if (!input.per_vertex) {
|
||||
const auto vector = state.builder.AllocateId();
|
||||
const auto component = state.builder.AllocateId();
|
||||
state.builder.AddFunction(spv::OpLoad, TypeF32Vector(state, 4), vector, input.variable_id);
|
||||
state.builder.AddFunction(spv::OpCompositeExtract, TypeF32(state), component, vector,
|
||||
chan & 3u);
|
||||
return select(component);
|
||||
}
|
||||
if (PixelParameterIsFlat(state, attr)) {
|
||||
const auto vertex =
|
||||
state.input_info.pixel->parameter_mode == ShaderPixelParameterMode::LastVertex ? 2u : 0u;
|
||||
return select(LoadVertexParameter(state, input, vertex, chan));
|
||||
}
|
||||
const auto barycentric_kind = state.input_info.pixel->ps_no_perspective
|
||||
? IR::StageInputKind::BaryCoordNoPerspective
|
||||
: IR::StageInputKind::BaryCoordSmooth;
|
||||
const auto barycentric = InputVariableForKind(state, barycentric_kind);
|
||||
uint32_t sum = 0;
|
||||
for (uint32_t vertex = 0; vertex < 3u; vertex++) {
|
||||
const auto pointer = state.builder.AllocateId();
|
||||
const auto weight = state.builder.AllocateId();
|
||||
const auto product = state.builder.AllocateId();
|
||||
state.builder.AddFunction(spv::OpAccessChain,
|
||||
TypePointer(state, spv::StorageClassInput, TypeF32(state)),
|
||||
pointer, barycentric, ConstantU32(state, vertex));
|
||||
state.builder.AddFunction(spv::OpLoad, TypeF32(state), weight, pointer);
|
||||
state.builder.AddFunction(spv::OpFMul, TypeF32(state), product,
|
||||
select(LoadVertexParameter(state, input, vertex, chan)), weight);
|
||||
if (vertex == 0u) {
|
||||
sum = product;
|
||||
} else {
|
||||
const auto next = state.builder.AllocateId();
|
||||
state.builder.AddFunction(spv::OpFAdd, TypeF32(state), next, sum, product);
|
||||
sum = next;
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
uint32_t EmitAttribute(EmitterState& state, uint32_t attr, uint32_t chan) {
|
||||
const auto* input = InputBindingForParameter(state, attr);
|
||||
if (input == nullptr || input->variable_id == 0) {
|
||||
@@ -148,57 +217,9 @@ uint32_t EmitAttribute(EmitterState& state, uint32_t attr, uint32_t chan) {
|
||||
if (state.program.stage == ShaderType::Vertex || state.program.stage == ShaderType::Local) {
|
||||
return EmitVertexParameterComponentU32(state, *input, chan & 3u);
|
||||
}
|
||||
const auto load_per_vertex = [&](uint32_t vertex) {
|
||||
const auto pointer = state.builder.AllocateId();
|
||||
const auto value = state.builder.AllocateId();
|
||||
state.builder.AddFunction(
|
||||
spv::OpAccessChain, TypePointer(state, spv::StorageClassInput, TypeF32(state)), pointer,
|
||||
input->variable_id, ConstantU32(state, vertex), ConstantU32(state, chan & 3u));
|
||||
state.builder.AddFunction(spv::OpLoad, TypeF32(state), value, pointer);
|
||||
return value;
|
||||
};
|
||||
if (input->per_vertex) {
|
||||
if (PixelParameterIsFlat(state, attr)) {
|
||||
const auto value = load_per_vertex(
|
||||
state.input_info.pixel->parameter_mode == ShaderPixelParameterMode::LastVertex ? 2u : 0u);
|
||||
const auto bits = state.builder.AllocateId();
|
||||
state.builder.AddFunction(spv::OpBitcast, TypeU32(state), bits, value);
|
||||
return bits;
|
||||
}
|
||||
const auto barycentric_kind = state.input_info.pixel->ps_no_perspective
|
||||
? IR::StageInputKind::BaryCoordNoPerspective
|
||||
: IR::StageInputKind::BaryCoordSmooth;
|
||||
const auto barycentric = InputVariableForKind(state, barycentric_kind);
|
||||
uint32_t sum = 0;
|
||||
for (uint32_t vertex = 0; vertex < 3u; vertex++) {
|
||||
const auto pointer = state.builder.AllocateId();
|
||||
const auto weight = state.builder.AllocateId();
|
||||
const auto product = state.builder.AllocateId();
|
||||
state.builder.AddFunction(spv::OpAccessChain,
|
||||
TypePointer(state, spv::StorageClassInput, TypeF32(state)),
|
||||
pointer, barycentric, ConstantU32(state, vertex));
|
||||
state.builder.AddFunction(spv::OpLoad, TypeF32(state), weight, pointer);
|
||||
state.builder.AddFunction(spv::OpFMul, TypeF32(state), product, load_per_vertex(vertex),
|
||||
weight);
|
||||
if (vertex == 0u) {
|
||||
sum = product;
|
||||
} else {
|
||||
const auto next = state.builder.AllocateId();
|
||||
state.builder.AddFunction(spv::OpFAdd, TypeF32(state), next, sum, product);
|
||||
sum = next;
|
||||
}
|
||||
}
|
||||
const auto bits = state.builder.AllocateId();
|
||||
state.builder.AddFunction(spv::OpBitcast, TypeU32(state), bits, sum);
|
||||
return bits;
|
||||
}
|
||||
const auto vector = state.builder.AllocateId();
|
||||
const auto component = state.builder.AllocateId();
|
||||
const auto bits = state.builder.AllocateId();
|
||||
state.builder.AddFunction(spv::OpLoad, TypeF32Vector(state, 4), vector, input->variable_id);
|
||||
state.builder.AddFunction(spv::OpCompositeExtract, TypeF32(state), component, vector,
|
||||
chan & 3u);
|
||||
state.builder.AddFunction(spv::OpBitcast, TypeU32(state), bits, component);
|
||||
const auto value = EmitPixelAttribute(state, *input, attr, chan, std::nullopt);
|
||||
const auto bits = state.builder.AllocateId();
|
||||
state.builder.AddFunction(spv::OpBitcast, TypeU32(state), bits, value);
|
||||
return bits;
|
||||
}
|
||||
|
||||
@@ -209,25 +230,16 @@ uint32_t EmitInterpolationParameter(ValueEmitContext& ctx, uint32_t attr, uint32
|
||||
if (!input->per_vertex) {
|
||||
return EmitAttribute(ctx.state, attr, chan);
|
||||
}
|
||||
const auto load_vertex = [&](uint32_t vertex) {
|
||||
const auto pointer = state.builder.AllocateId();
|
||||
const auto value = state.builder.AllocateId();
|
||||
state.builder.AddFunction(
|
||||
spv::OpAccessChain, TypePointer(state, spv::StorageClassInput, TypeF32(state)), pointer,
|
||||
input->variable_id, ConstantU32(state, vertex), ConstantU32(state, chan & 3u));
|
||||
state.builder.AddFunction(spv::OpLoad, TypeF32(state), value, pointer);
|
||||
return value;
|
||||
};
|
||||
|
||||
uint32_t selected_vertex = (mode + 1u) % 3u;
|
||||
if (mode == 2u && PixelParameterIsFlat(state, attr)) {
|
||||
selected_vertex =
|
||||
state.input_info.pixel->parameter_mode == ShaderPixelParameterMode::LastVertex ? 2u : 0u;
|
||||
}
|
||||
uint32_t value = load_vertex(selected_vertex);
|
||||
uint32_t value = LoadVertexParameter(state, *input, selected_vertex, chan);
|
||||
if (!PixelParameterIsCustom(state, attr) && mode < 2u) {
|
||||
const auto delta = state.builder.AllocateId();
|
||||
state.builder.AddFunction(spv::OpFSub, TypeF32(state), delta, value, load_vertex(0));
|
||||
state.builder.AddFunction(spv::OpFSub, TypeF32(state), delta, value,
|
||||
LoadVertexParameter(state, *input, 0, chan));
|
||||
value = delta;
|
||||
}
|
||||
const auto bits = state.builder.AllocateId();
|
||||
@@ -853,6 +865,12 @@ uint32_t EmitGetAttribute(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
return EmitAttribute(ctx.state, inst.Arg(0).U32(), inst.Arg(1).U32());
|
||||
}
|
||||
|
||||
uint32_t EmitGetAttributeF16(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
const auto attr = inst.Arg(0).U32();
|
||||
return EmitPixelAttribute(ctx.state, *InputBindingForParameter(ctx.state, attr), attr,
|
||||
inst.Arg(1).U32(), inst.Arg(2).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());
|
||||
}
|
||||
|
||||
@@ -236,6 +236,7 @@ uint32_t EmitSubgroupUMinU32(ValueEmitContext& ctx, const IR::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 EmitGetAttributeF16(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);
|
||||
|
||||
@@ -6,13 +6,14 @@ namespace Libs::Graphics::ShaderRecompiler::Decoder {
|
||||
namespace {
|
||||
|
||||
struct MemoryOpcodeInfo {
|
||||
uint32_t encoding = 0;
|
||||
Opcode decoded = Opcode::UNSUPPORTED;
|
||||
uint32_t data_dwords = 1;
|
||||
uint32_t data_bits = 32;
|
||||
bool data_signed = false;
|
||||
bool typed = false;
|
||||
bool formatted = false;
|
||||
uint32_t encoding = 0;
|
||||
Opcode decoded = Opcode::UNSUPPORTED;
|
||||
uint32_t data_dwords = 1;
|
||||
uint32_t data_bits = 32;
|
||||
bool data_signed = false;
|
||||
bool typed = false;
|
||||
bool formatted = false;
|
||||
uint32_t data_components = 0; // D16 formats pack two components per dword
|
||||
};
|
||||
|
||||
constexpr MemoryOpcodeInfo SMEM_OPCODE_LIST[] = {
|
||||
@@ -73,9 +74,13 @@ constexpr MemoryOpcodeInfo MUBUF_OPCODE_LIST[] = {
|
||||
{0x71u, Opcode::BUFFER_GL0_INV, 0, 0},
|
||||
{0x72u, Opcode::BUFFER_GL1_INV, 0, 0},
|
||||
{0x80u, Opcode::BUFFER_LOAD_FORMAT_D16_X, 1, 16, false, false, true},
|
||||
{0x83u, Opcode::BUFFER_LOAD_FORMAT_D16_XYZW, 2, 16, false, false, true},
|
||||
{0x81u, Opcode::BUFFER_LOAD_FORMAT_D16_XY, 1, 16, false, false, true, 2},
|
||||
{0x82u, Opcode::BUFFER_LOAD_FORMAT_D16_XYZ, 2, 16, false, false, true, 3},
|
||||
{0x83u, Opcode::BUFFER_LOAD_FORMAT_D16_XYZW, 2, 16, false, false, true, 4},
|
||||
{0x84u, Opcode::BUFFER_STORE_FORMAT_D16_X, 1, 16, false, false, true},
|
||||
{0x87u, Opcode::BUFFER_STORE_FORMAT_D16_XYZW, 2, 16, false, false, true},
|
||||
{0x85u, Opcode::BUFFER_STORE_FORMAT_D16_XY, 1, 16, false, false, true, 2},
|
||||
{0x86u, Opcode::BUFFER_STORE_FORMAT_D16_XYZ, 2, 16, false, false, true, 3},
|
||||
{0x87u, Opcode::BUFFER_STORE_FORMAT_D16_XYZW, 2, 16, false, false, true, 4},
|
||||
};
|
||||
|
||||
constexpr MemoryOpcodeInfo MTBUF_OPCODE_LIST[] = {
|
||||
@@ -158,12 +163,13 @@ void ApplyMemoryInfo(Instruction& inst, const MemoryOpcodeInfo* info) {
|
||||
inst.opcode = Opcode::UNSUPPORTED;
|
||||
return;
|
||||
}
|
||||
inst.opcode = info->decoded;
|
||||
inst.data_dwords = info->data_dwords;
|
||||
inst.data_bits = info->data_bits;
|
||||
inst.data_signed = info->data_signed;
|
||||
inst.typed = info->typed;
|
||||
inst.formatted = info->formatted;
|
||||
inst.opcode = info->decoded;
|
||||
inst.data_dwords = info->data_dwords;
|
||||
inst.data_bits = info->data_bits;
|
||||
inst.data_signed = info->data_signed;
|
||||
inst.typed = info->typed;
|
||||
inst.formatted = info->formatted;
|
||||
inst.data_components = info->data_components;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -323,12 +329,7 @@ void DecodeMubuf(uint32_t pc, std::span<const uint32_t> code, uint32_t word_inde
|
||||
}
|
||||
|
||||
DecodeVectorGpr(vdata, inst.dst);
|
||||
if (inst.opcode == Opcode::BUFFER_LOAD_FORMAT_D16_XYZW ||
|
||||
inst.opcode == Opcode::BUFFER_STORE_FORMAT_D16_XYZW) {
|
||||
inst.data_components = 4u;
|
||||
}
|
||||
if (inst.opcode == Opcode::BUFFER_LOAD_FORMAT_D16_X ||
|
||||
inst.opcode == Opcode::BUFFER_LOAD_UBYTE_D16 ||
|
||||
if (inst.opcode == Opcode::BUFFER_LOAD_UBYTE_D16 ||
|
||||
inst.opcode == Opcode::BUFFER_LOAD_SHORT_D16 ||
|
||||
inst.opcode == Opcode::BUFFER_LOAD_SHORT_D16_HI) {
|
||||
inst.dst.sdwa_sel = inst.opcode == Opcode::BUFFER_LOAD_SHORT_D16_HI ? 5u : 4u;
|
||||
|
||||
@@ -636,6 +636,8 @@ std::string InstructionToString(const Instruction& inst) {
|
||||
case Opcode::BUFFER_LOAD_FORMAT_XYZ:
|
||||
case Opcode::BUFFER_LOAD_FORMAT_XYZW:
|
||||
case Opcode::BUFFER_LOAD_FORMAT_D16_X:
|
||||
case Opcode::BUFFER_LOAD_FORMAT_D16_XY:
|
||||
case Opcode::BUFFER_LOAD_FORMAT_D16_XYZ:
|
||||
case Opcode::BUFFER_LOAD_FORMAT_D16_XYZW:
|
||||
case Opcode::BUFFER_STORE_FORMAT_X:
|
||||
case Opcode::BUFFER_STORE_FORMAT_XY:
|
||||
@@ -643,6 +645,8 @@ std::string InstructionToString(const Instruction& inst) {
|
||||
case Opcode::BUFFER_STORE_FORMAT_XYZW:
|
||||
case Opcode::BUFFER_STORE_FORMAT_D16_X:
|
||||
case Opcode::BUFFER_STORE_FORMAT_D16_HI_X:
|
||||
case Opcode::BUFFER_STORE_FORMAT_D16_XY:
|
||||
case Opcode::BUFFER_STORE_FORMAT_D16_XYZ:
|
||||
case Opcode::BUFFER_STORE_FORMAT_D16_XYZW:
|
||||
case Opcode::BUFFER_LOAD_UBYTE:
|
||||
case Opcode::BUFFER_LOAD_UBYTE_D16:
|
||||
|
||||
@@ -547,6 +547,8 @@ enum class Opcode {
|
||||
BUFFER_LOAD_FORMAT_XYZ,
|
||||
BUFFER_LOAD_FORMAT_XYZW,
|
||||
BUFFER_LOAD_FORMAT_D16_X,
|
||||
BUFFER_LOAD_FORMAT_D16_XY,
|
||||
BUFFER_LOAD_FORMAT_D16_XYZ,
|
||||
BUFFER_LOAD_FORMAT_D16_XYZW,
|
||||
BUFFER_STORE_FORMAT_X,
|
||||
BUFFER_STORE_FORMAT_XY,
|
||||
@@ -554,6 +556,8 @@ enum class Opcode {
|
||||
BUFFER_STORE_FORMAT_XYZW,
|
||||
BUFFER_STORE_FORMAT_D16_X,
|
||||
BUFFER_STORE_FORMAT_D16_HI_X,
|
||||
BUFFER_STORE_FORMAT_D16_XY,
|
||||
BUFFER_STORE_FORMAT_D16_XYZ,
|
||||
BUFFER_STORE_FORMAT_D16_XYZW,
|
||||
BUFFER_LOAD_UBYTE,
|
||||
BUFFER_LOAD_UBYTE_D16,
|
||||
@@ -713,6 +717,8 @@ enum class Opcode {
|
||||
V_INTERP_P1_F32,
|
||||
V_INTERP_P2_F32,
|
||||
V_INTERP_MOV_F32,
|
||||
V_INTERP_P1LL_F16,
|
||||
V_INTERP_P2_F16,
|
||||
|
||||
S_NOP,
|
||||
S_WAITCNT,
|
||||
|
||||
@@ -391,6 +391,8 @@ constexpr OpcodeMap VOP3_OPCODE_LIST[] = {
|
||||
{0x377u, Opcode::V_PERMLANE16_B32},
|
||||
{0x378u, Opcode::V_PERMLANEX16_B32},
|
||||
{0x34bu, Opcode::V_FMA_F16},
|
||||
{0x342u, Opcode::V_INTERP_P1LL_F16},
|
||||
{0x35au, Opcode::V_INTERP_P2_F16},
|
||||
{0x36du, Opcode::V_ADD3_U32},
|
||||
{0x14fu, Opcode::V_ALIGNBYTE_B32},
|
||||
};
|
||||
@@ -1553,6 +1555,9 @@ bool HasUnsupportedNativeVop3Modifiers(Opcode opcode, bool permlane, bool mad_mi
|
||||
}
|
||||
switch (opcode) {
|
||||
case Opcode::V_LDEXP_F32: return (abs & ~1u) != 0u || op_sel != 0u || (neg & ~1u) != 0u;
|
||||
case Opcode::V_INTERP_P1LL_F16:
|
||||
case Opcode::V_INTERP_P2_F16:
|
||||
return abs != 0u || (op_sel & 0x7u) != 0u || clamp != 0u || omod != 0u || neg != 0u;
|
||||
case Opcode::V_CNDMASK_B32:
|
||||
return (abs & ~0x3u) != 0u || op_sel != 0u || clamp != 0u || omod != 0u ||
|
||||
(neg & ~0x3u) != 0u;
|
||||
@@ -1664,6 +1669,17 @@ bool IsVopcCompareExec(Opcode opcode) {
|
||||
}
|
||||
}
|
||||
|
||||
// VINTRP and VOP3 interpolation share one layout: src0 = I/J VGPR, src1 = attr, src2 = chan.
|
||||
void SetInterpolationAttribute(Instruction& inst, uint32_t attr, uint32_t chan) {
|
||||
inst.src1.kind = OperandKind::IntegerInlineConstant;
|
||||
inst.src1.value = attr;
|
||||
inst.src1.signed_val = static_cast<int32_t>(attr);
|
||||
inst.src2.kind = OperandKind::IntegerInlineConstant;
|
||||
inst.src2.value = chan;
|
||||
inst.src2.signed_val = static_cast<int32_t>(chan);
|
||||
inst.src_count = 3;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void DecodeVop2(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
|
||||
@@ -1830,6 +1846,15 @@ void DecodeVop3(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
inst.src_count = 0;
|
||||
return;
|
||||
}
|
||||
if (inst.opcode == Opcode::V_INTERP_P1LL_F16 || inst.opcode == Opcode::V_INTERP_P2_F16) {
|
||||
// SRC0 packs {attr_word, attr_chan, attr}; attr_word selects the high f16 of the dword.
|
||||
DecodeVectorGpr(vdst, inst.dst);
|
||||
DecodeScalarSource(src1, pc, inst.src0);
|
||||
SetInterpolationAttribute(inst, src0 & 0x3fu, (src0 >> 6u) & 0x3u);
|
||||
inst.src1.op_sel = (src0 & 0x100u) != 0u;
|
||||
inst.dst.sdwa_sel = (op_sel & 0x8u) != 0u ? 5u : 4u;
|
||||
return;
|
||||
}
|
||||
if (compare_exec) {
|
||||
inst.dst.kind = OperandKind::ExecLo;
|
||||
} else if (scalar_dst) {
|
||||
@@ -2004,13 +2029,7 @@ void DecodeVintrp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_ind
|
||||
} else {
|
||||
DecodeVectorGpr(vsrc, inst.src0);
|
||||
}
|
||||
inst.src1.kind = OperandKind::IntegerInlineConstant;
|
||||
inst.src1.value = attr;
|
||||
inst.src1.signed_val = static_cast<int32_t>(attr);
|
||||
inst.src2.kind = OperandKind::IntegerInlineConstant;
|
||||
inst.src2.value = chan;
|
||||
inst.src2.signed_val = static_cast<int32_t>(chan);
|
||||
inst.src_count = 3;
|
||||
SetInterpolationAttribute(inst, attr, chan);
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics::ShaderRecompiler::Decoder
|
||||
|
||||
@@ -73,14 +73,19 @@ void Translator::TranslateEmbeddedFetch(const Decoder::Instruction& inst, uint32
|
||||
}
|
||||
}
|
||||
|
||||
void Translator::V_INTERP_P1_F32() {}
|
||||
|
||||
void Translator::V_INTERP_P2_F32(const Decoder::Instruction& inst) {
|
||||
const auto value = ir.Emit(IR::ValueOpcode::GetAttribute,
|
||||
{IR::Value(inst.src1.value), IR::Value(inst.src2.value)});
|
||||
WriteOperand(inst.dst, value);
|
||||
}
|
||||
|
||||
void Translator::V_INTERP_P2_F16(const Decoder::Instruction& inst) {
|
||||
const auto value = ir.Emit(IR::ValueOpcode::GetAttributeF16,
|
||||
{IR::Value(inst.src1.value), IR::Value(inst.src2.value),
|
||||
IR::Value(uint32_t {inst.src1.op_sel})});
|
||||
WriteF16(inst.dst, IR::F32(value));
|
||||
}
|
||||
|
||||
void Translator::V_INTERP_MOV_F32(const Decoder::Instruction& inst) {
|
||||
if (inst.src0.value >= 3u) {
|
||||
EXIT("v_interp_mov_f32 mode %u is reserved at pc 0x%08x", inst.src0.value, inst.pc);
|
||||
@@ -108,8 +113,11 @@ void Translator::EXP(const Decoder::Instruction& inst) {
|
||||
|
||||
void Translator::EmitInterpolation(const Decoder::Instruction& inst) {
|
||||
switch (inst.opcode) {
|
||||
case Decoder::Opcode::V_INTERP_P1_F32: V_INTERP_P1_F32(); return;
|
||||
// P2 interpolates the whole attribute, so the P1 halves have nothing to compute.
|
||||
case Decoder::Opcode::V_INTERP_P1_F32:
|
||||
case Decoder::Opcode::V_INTERP_P1LL_F16: return;
|
||||
case Decoder::Opcode::V_INTERP_P2_F32: V_INTERP_P2_F32(inst); return;
|
||||
case Decoder::Opcode::V_INTERP_P2_F16: V_INTERP_P2_F16(inst); return;
|
||||
case Decoder::Opcode::V_INTERP_MOV_F32: V_INTERP_MOV_F32(inst); return;
|
||||
default: return FailMissingTranslation(inst);
|
||||
}
|
||||
|
||||
@@ -455,12 +455,17 @@ void Translator::BUFFER_LOAD(const Decoder::Instruction& inst) {
|
||||
AddMemoryInfo(memory, inst.pc));
|
||||
if (bits != 32u) {
|
||||
WriteOperand(inst.dst, WidenSubdword(loaded, bits, sign));
|
||||
} else if (memory.data_dwords == 1u) {
|
||||
WriteOperand(inst.dst, loaded);
|
||||
} else {
|
||||
for (uint32_t component = 0; component < memory.data_dwords; component++) {
|
||||
WriteOperand(OffsetOperand(inst.dst, component),
|
||||
ir.CompositeExtract(loaded, component));
|
||||
return;
|
||||
}
|
||||
for (uint32_t component = 0; component < memory.data_dwords; component++) {
|
||||
const auto dst = OffsetOperand(inst.dst, component);
|
||||
const auto value =
|
||||
memory.data_dwords == 1u ? IR::U32(loaded) : ir.CompositeExtract(loaded, component);
|
||||
// D16 formats with an odd component count keep the high half of their final VGPR.
|
||||
if (memory.data_bits == 16u && component * 2u + 1u == memory.component_count) {
|
||||
Write16Bits(dst, value);
|
||||
} else {
|
||||
WriteOperand(dst, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -992,6 +997,8 @@ void Translator::EmitMemory(const Decoder::Instruction& inst) {
|
||||
case Decoder::Opcode::BUFFER_LOAD_FORMAT_XYZ:
|
||||
case Decoder::Opcode::BUFFER_LOAD_FORMAT_XYZW:
|
||||
case Decoder::Opcode::BUFFER_LOAD_FORMAT_D16_X:
|
||||
case Decoder::Opcode::BUFFER_LOAD_FORMAT_D16_XY:
|
||||
case Decoder::Opcode::BUFFER_LOAD_FORMAT_D16_XYZ:
|
||||
case Decoder::Opcode::BUFFER_LOAD_FORMAT_D16_XYZW:
|
||||
case Decoder::Opcode::TBUFFER_LOAD_FORMAT_X:
|
||||
case Decoder::Opcode::TBUFFER_LOAD_FORMAT_XY:
|
||||
@@ -1012,6 +1019,8 @@ void Translator::EmitMemory(const Decoder::Instruction& inst) {
|
||||
case Decoder::Opcode::BUFFER_STORE_FORMAT_XYZW:
|
||||
case Decoder::Opcode::BUFFER_STORE_FORMAT_D16_X:
|
||||
case Decoder::Opcode::BUFFER_STORE_FORMAT_D16_HI_X:
|
||||
case Decoder::Opcode::BUFFER_STORE_FORMAT_D16_XY:
|
||||
case Decoder::Opcode::BUFFER_STORE_FORMAT_D16_XYZ:
|
||||
case Decoder::Opcode::BUFFER_STORE_FORMAT_D16_XYZW:
|
||||
case Decoder::Opcode::TBUFFER_STORE_FORMAT_X:
|
||||
case Decoder::Opcode::TBUFFER_STORE_FORMAT_XY:
|
||||
|
||||
@@ -240,8 +240,8 @@ private:
|
||||
void V_READLANE_B32(const Decoder::Instruction& inst);
|
||||
void V_WRITELANE_B32(const Decoder::Instruction& inst);
|
||||
void V_PERMLANE16_B32(const Decoder::Instruction& inst, bool x16);
|
||||
void V_INTERP_P1_F32();
|
||||
void V_INTERP_P2_F32(const Decoder::Instruction& inst);
|
||||
void V_INTERP_P2_F16(const Decoder::Instruction& inst);
|
||||
void V_INTERP_MOV_F32(const Decoder::Instruction& inst);
|
||||
void EXP(const Decoder::Instruction& inst);
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ void Translator::EmitVector(const Decoder::Instruction& inst) {
|
||||
using O = Decoder::Opcode;
|
||||
switch (inst.opcode) {
|
||||
case O::V_NOP: return;
|
||||
case O::V_INTERP_P1LL_F16:
|
||||
case O::V_INTERP_P2_F16: return EmitInterpolation(inst);
|
||||
case O::V_ADD_I32: ADD_U32(inst, true, false); return;
|
||||
case O::V_ADDC_U32: ADD_U32(inst, true, true); return;
|
||||
case O::V_SUB_I32: SUB_U32(inst, true, false); return;
|
||||
|
||||
@@ -487,7 +487,7 @@ void ValidateProgram(const Program& program, bool require_ssa) {
|
||||
const auto access = BufferAccessOf(inst.GetOpcode());
|
||||
const bool packed_formatted =
|
||||
(access == BufferAccess::Read || access == BufferAccess::Write) && memory.formatted &&
|
||||
memory.data_bits == 16u && memory.component_count == 4u && buffer_components == 2u;
|
||||
memory.data_bits == 16u && (memory.component_count + 1u) / 2u == buffer_components;
|
||||
if (buffer_components > 1u &&
|
||||
(!vector_buffer || (memory.data_bits != 32u && !packed_formatted) ||
|
||||
memory.data_dwords != buffer_components || memory.component_index != 0u)) {
|
||||
|
||||
@@ -338,6 +338,7 @@ VALUE_OPCODE(ImageAtomicXor64, U64, ImageResource, ImageAddress, U64, U1)
|
||||
VALUE_OPCODE(ImageAtomicFMin32, U32, ImageResource, ImageAddress, U32, U1)
|
||||
VALUE_OPCODE(ImageAtomicFMax32, U32, ImageResource, ImageAddress, U32, U1)
|
||||
VALUE_OPCODE(GetAttribute, U32, U32, U32)
|
||||
VALUE_OPCODE(GetAttributeF16, F32, U32, U32, U32)
|
||||
VALUE_OPCODE(GetInterpolationParameter, U32, U32, U32, U32)
|
||||
VALUE_OPCODE(SetAttribute, Void, U32x4, U1)
|
||||
|
||||
|
||||
@@ -373,7 +373,8 @@ void Visit(Program& program, ShaderStageInputInfo input_info, InputUsage& inputs
|
||||
}
|
||||
}
|
||||
switch (inst.GetOpcode()) {
|
||||
case ValueOpcode::GetAttribute: {
|
||||
case ValueOpcode::GetAttribute:
|
||||
case ValueOpcode::GetAttributeF16: {
|
||||
if (!inst.Arg(0).IsImmediate() || inst.Arg(0).GetType() != Type::U32 ||
|
||||
!inst.Arg(1).IsImmediate() || inst.Arg(1).GetType() != Type::U32) {
|
||||
return Fail("typed attribute reference is not constant");
|
||||
@@ -395,6 +396,15 @@ void Visit(Program& program, ShaderStageInputInfo input_info, InputUsage& inputs
|
||||
auto& components = inputs.components[inst.Arg(0).U32()];
|
||||
components = std::max(components, static_cast<uint8_t>(inst.Arg(1).U32() + 1u));
|
||||
}
|
||||
if (inst.GetOpcode() == ValueOpcode::GetAttributeF16) {
|
||||
if (program.stage != ShaderType::Pixel || !inst.Arg(2).IsImmediate()) {
|
||||
return Fail("f16 attribute reference is invalid");
|
||||
}
|
||||
// FP16_INTERP_MODE packs two halves per dword, so smooth inputs blend unpacked
|
||||
// vertices; flat inputs unpack the provoking vertex.
|
||||
const auto input = inst.Arg(0).U32();
|
||||
inputs.per_vertex[input] |= !ShaderPixelParameterIsFlat(*input_info.pixel, input);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ValueOpcode::GetInterpolationParameter: {
|
||||
|
||||
@@ -33076,7 +33076,7 @@ std::vector<TestCase> BufferLoadFormatD16XCases() {
|
||||
return cases;
|
||||
}
|
||||
|
||||
std::vector<TestCase> BufferLoadFormatD16XyzwCases() {
|
||||
std::vector<TestCase> BufferLoadFormatD16VectorCases() {
|
||||
using F = Prospero::BufferFormat;
|
||||
using O = ShaderOpcode;
|
||||
struct LoadCase {
|
||||
@@ -33089,6 +33089,7 @@ std::vector<TestCase> BufferLoadFormatD16XyzwCases() {
|
||||
u32 index = 0;
|
||||
bool active = true;
|
||||
bool overlap = false;
|
||||
u32 opcode = 0x83u;
|
||||
};
|
||||
constexpr LoadCase inputs[] = {
|
||||
{"BufferLoadFormatD16XyzwFloat32", F::k32_32_32_32Float,
|
||||
@@ -33118,7 +33119,26 @@ std::vector<TestCase> BufferLoadFormatD16XyzwCases() {
|
||||
{"BufferLoadFormatD16XyzwOverlappingAddress", F::k32_32_32_32Float,
|
||||
{0x3f800000u, 0x3f000000u, 0u, 0xc0000000u},
|
||||
{0x38003c00u, 0xc0000000u}, DstSel(4, 5, 6, 7), 16, 0, true, true},
|
||||
{"BufferLoadFormatD16XyFloat32", F::k32_32_32_32Float,
|
||||
{0x3f800000u, 0x3f000000u, 0u, 0xc0000000u},
|
||||
{0x38003c00u, 0xbeef5678u}, DstSel(4, 5, 6, 7), 16, 0, true, false, 0x81u},
|
||||
{"BufferLoadFormatD16XyUintSaturates", F::k32_32UInt,
|
||||
{0x10000u, 7u}, {0x0007ffffu, 0xbeef5678u}, DstSel(4, 5, 6, 7), 8, 0, true, false, 0x81u},
|
||||
{"BufferLoadFormatD16XyzFloat32", F::k32_32_32_32Float,
|
||||
{0x3f800000u, 0x3f000000u, 0xc0000000u, 0x3e800000u},
|
||||
{0x38003c00u, 0xbeefc000u}, DstSel(4, 5, 6, 7), 16, 0, true, false, 0x82u},
|
||||
{"BufferLoadFormatD16XyzSintSaturates", F::k32_32_32_32SInt,
|
||||
{0xffff7fffu, 7u, 0x8000u, 0xffff8000u},
|
||||
{0x00078000u, 0xbeef7fffu}, DstSel(4, 5, 6, 7), 16, 0, true, false, 0x82u},
|
||||
{"BufferLoadFormatD16XyzOobOne", F::k32_32_32_32Float,
|
||||
{}, {0x3c00u, 0xbeef3c00u}, DstSel(1, 4, 1, 4), 16, 1, true, false, 0x82u},
|
||||
{"BufferLoadFormatD16XyzZeroStrideFloatOne", F::k32_32_32_32Float,
|
||||
{}, {0x3c003c00u, 0xbeef0000u}, DstSel(1, 1, 4, 1), 0, 0, true, false, 0x82u},
|
||||
{"BufferLoadFormatD16XyzInactive", F::k32_32_32_32Float,
|
||||
{}, {0xabcd1234u, 0xbeef5678u}, DstSel(4, 5, 6, 7), 16, 0, false, false, 0x82u},
|
||||
};
|
||||
constexpr O formats[] = {O::BUFFER_LOAD_FORMAT_D16_XY, O::BUFFER_LOAD_FORMAT_D16_XYZ,
|
||||
O::BUFFER_LOAD_FORMAT_D16_XYZW};
|
||||
std::vector<TestCase> cases;
|
||||
for (const auto &input : inputs) {
|
||||
TestCase test;
|
||||
@@ -33128,8 +33148,8 @@ std::vector<TestCase> BufferLoadFormatD16XyzwCases() {
|
||||
AppendVMovLiteral(&test.code, 18, 0xcafef00du);
|
||||
AppendVMovU32(&test.code, 12, input.index);
|
||||
if (!input.active) test.code.push_back(EncodeSop1(0x04, 126, InlineU32(0)));
|
||||
// Captured b21e373811f28e31 pc 0x1914: e20c2000 8001100c.
|
||||
test.code.push_back(EncodeMubuf0(0x83u, 0, true, false));
|
||||
// Captured XYZW b21e373811f28e31 pc 0x1914: e20c2000 8001100c.
|
||||
test.code.push_back(EncodeMubuf0(input.opcode, 0, true, false));
|
||||
test.code.push_back(EncodeMubuf1(16, 1, input.overlap ? 16 : 12));
|
||||
if (!input.active) {
|
||||
test.code.push_back(EncodeSMovB32(126, InlineU32(1)));
|
||||
@@ -33151,7 +33171,7 @@ std::vector<TestCase> BufferLoadFormatD16XyzwCases() {
|
||||
if (input.stride != 0 && input.active) {
|
||||
test.storage_buffer_ranges = {{0, input.stride}, {0, 32}};
|
||||
}
|
||||
test.opcodes = {O::V_MOV_B32, O::BUFFER_LOAD_FORMAT_D16_XYZW,
|
||||
test.opcodes = {O::V_MOV_B32, formats[input.opcode - 0x81u],
|
||||
O::BUFFER_STORE_DWORD, O::S_ENDPGM};
|
||||
cases.push_back(std::move(test));
|
||||
}
|
||||
@@ -33195,12 +33215,23 @@ std::vector<TestCase> BufferStoreFormatD16Cases() {
|
||||
{0xff80ff7fu, 0x0080007fu}, {0x7f7f8080u}, 1, 0x87u},
|
||||
{"BufferStoreFormatD16XyzwUnorm8", F::k8_8_8_8UNorm,
|
||||
{0x3800c000u, 0x40003c00u}, {0xffff8000u}, 1, 0x87u},
|
||||
{"BufferStoreFormatD16XyFloat32", F::k32_32Float,
|
||||
{0x3800c000u}, {0xc0000000u, 0x3f000000u}, 2, 0x85u},
|
||||
{"BufferStoreFormatD16XyUint16", F::k16_16UInt, {0xfffe0001u}, {0xfffe0001u}, 1, 0x85u},
|
||||
{"BufferStoreFormatD16XyzFloat32", F::k32_32_32Float,
|
||||
{0xc0003c00u, 0xdead3800u}, {0x3f800000u, 0xc0000000u, 0x3f000000u}, 3, 0x86u},
|
||||
{"BufferStoreFormatD16XyzZeroFillsW", F::k16_16_16_16Float,
|
||||
{0xc0003c00u, 0xdead3800u}, {0xc0003c00u, 0x00003800u}, 2, 0x86u},
|
||||
{"BufferStoreFormatD16XyzUint8", F::k8_8_8_8UInt,
|
||||
{0x00020001u, 0xdead0003u}, {0x00030201u}, 1, 0x86u},
|
||||
};
|
||||
constexpr O formats[] = {O::BUFFER_STORE_FORMAT_D16_X, O::BUFFER_STORE_FORMAT_D16_XY,
|
||||
O::BUFFER_STORE_FORMAT_D16_XYZ, O::BUFFER_STORE_FORMAT_D16_XYZW};
|
||||
std::vector<TestCase> cases;
|
||||
for (const auto &input : inputs)
|
||||
for (const u32 wave : {32u, 64u})
|
||||
for (u32 mask = 0; mask < (input.opcode == 0x27u ? 2u : 1u); ++mask) {
|
||||
const bool wide = input.opcode == 0x87u;
|
||||
const bool wide = input.opcode >= 0x85u;
|
||||
const bool high = input.opcode == 0x27u;
|
||||
const bool multi_lane = wide || high;
|
||||
const bool masked = wide || mask != 0;
|
||||
@@ -33253,9 +33284,9 @@ std::vector<TestCase> BufferStoreFormatD16Cases() {
|
||||
test.user_data = MakeStructuredStorageBufferData(stride, multi_lane ? wave : 3u,
|
||||
false, BufferFormat(input.format));
|
||||
test.has_user_data = true;
|
||||
test.opcodes = {O::V_MOV_B32, high ? O::BUFFER_STORE_FORMAT_D16_HI_X :
|
||||
wide ? O::BUFFER_STORE_FORMAT_D16_XYZW :
|
||||
O::BUFFER_STORE_FORMAT_D16_X, O::S_ENDPGM};
|
||||
test.opcodes = {O::V_MOV_B32,
|
||||
high ? O::BUFFER_STORE_FORMAT_D16_HI_X : formats[input.opcode & 3u],
|
||||
O::S_ENDPGM};
|
||||
if (high) test.decoded_counts = {{"BUFFER_STORE_FORMAT_D16_HI_X", 3u}};
|
||||
cases.push_back(std::move(test));
|
||||
}
|
||||
@@ -43310,7 +43341,7 @@ std::vector<TestCase> MakeCases() {
|
||||
for (auto &test : BufferLoadFormatD16XCases()) {
|
||||
cases.push_back(std::move(test));
|
||||
}
|
||||
for (auto &test : BufferLoadFormatD16XyzwCases()) {
|
||||
for (auto &test : BufferLoadFormatD16VectorCases()) {
|
||||
cases.push_back(std::move(test));
|
||||
}
|
||||
for (auto &test : BufferStoreFormatD16Cases()) {
|
||||
@@ -49288,7 +49319,7 @@ int main(int argc, char **argv) {
|
||||
for (const auto &test : BufferLoadFormatD16XCases()) {
|
||||
RunCase(&vulkan, test);
|
||||
}
|
||||
for (const auto &test : BufferLoadFormatD16XyzwCases()) {
|
||||
for (const auto &test : BufferLoadFormatD16VectorCases()) {
|
||||
RunCase(&vulkan, test);
|
||||
}
|
||||
for (const auto &test : BufferStoreFormatD16Cases()) {
|
||||
|
||||
@@ -6832,6 +6832,50 @@ void TestCustomVintrpMovTranslation() {
|
||||
CheckSpirvBinaryValidates(mixed_linear_result.spirv);
|
||||
}
|
||||
|
||||
void TestVop3F16InterpolationTranslation() {
|
||||
// v_interp_p1ll_f16 v21, v0, attr0.w high; v_interp_p2_f16 v14, v1, attr0.w, v21 high
|
||||
// v_interp_p1ll_f16 v15, v0, attr0.z; v_interp_p2_f16 v14, v1, attr0.z, v15 (op_sel[3])
|
||||
const uint32_t shader[] = {
|
||||
EncodeVop3Word0(0x342, 21), EncodeVop3Word1(0x1c0, 256 + 0, 0),
|
||||
EncodeVop3Word0(0x35a, 14), EncodeVop3Word1(0x1c0, 256 + 1, 256 + 21),
|
||||
EncodeVop3Word0(0x342, 15), EncodeVop3Word1(0x080, 256 + 0, 0),
|
||||
EncodeVop3Word0(0x35a, 14, 8), EncodeVop3Word1(0x080, 256 + 1, 256 + 15),
|
||||
EncodeExp0(0x00, 0x1), EncodeExp1(14, 0, 0, 0),
|
||||
0xbf810000u,
|
||||
};
|
||||
ShaderPixelInputInfo ps_info{};
|
||||
ps_info.input_num = 1;
|
||||
SetIdentityInterpolatorSettings(&ps_info);
|
||||
auto options = MakeCompileOptions(ShaderType::Pixel);
|
||||
options.input_info.pixel = &ps_info;
|
||||
options.dump_ir = true;
|
||||
|
||||
auto result = RecompileForTest(shader, options);
|
||||
Check(result.ir_dump.find("GetAttributeF16 0x00000000, 0x00000003, 0x00000001") !=
|
||||
std::string::npos,
|
||||
"v_interp_p2_f16 did not read the high f16 of attr0.w");
|
||||
Check(result.ir_dump.find("GetAttributeF16 0x00000000, 0x00000002, 0x00000000") !=
|
||||
std::string::npos,
|
||||
"v_interp_p2_f16 did not read the low f16 of attr0.z");
|
||||
Check(result.decoded_dump.find("v14.sdwa(sel=5") != std::string::npos,
|
||||
"v_interp_p2_f16 op_sel[3] did not write the high half of v14");
|
||||
Check(SpirvHasDecorationValueWithDecoration(result.spirv, 30u, 0u, 5285u),
|
||||
"f16 interpolation input was not per-vertex");
|
||||
Check(SpirvExtInstCount(result.spirv, 62u) == 6u,
|
||||
"f16 interpolation did not unpack each of the three vertices per read");
|
||||
Check(SpirvInstructionOpcodeCount(result.spirv, 133u) == 6u,
|
||||
"f16 interpolation did not blend three barycentric weights per read");
|
||||
CheckSpirvBinaryValidates(result.spirv);
|
||||
|
||||
ps_info.interpolator_settings[0] = 0x00000400u;
|
||||
auto flat_result = RecompileForTest(shader, options);
|
||||
Check(SpirvExtInstCount(flat_result.spirv, 62u) == 2u &&
|
||||
!SpirvContainsCapability(flat_result.spirv, 5284u) &&
|
||||
SpirvHasDecorationValueWithDecoration(flat_result.spirv, 30u, 0u, 14u),
|
||||
"flat f16 interpolation did not unpack only the flat provoking value");
|
||||
CheckSpirvBinaryValidates(flat_result.spirv);
|
||||
}
|
||||
|
||||
void TestPerspectiveCentroidInputs() {
|
||||
constexpr std::array cases{std::array{4u, UINT32_MAX, 0u},
|
||||
std::array{5u, UINT32_MAX, 2u},
|
||||
@@ -16024,6 +16068,40 @@ void TestCapturedBufferStoreFormatD16HiXDecode() {
|
||||
"captured BUFFER_STORE_FORMAT_D16_HI_X lost its high half, index, or coherence flags");
|
||||
}
|
||||
|
||||
void TestCapturedBufferLoadD16XyzwDecode() {
|
||||
namespace Decoder = ShaderRecompiler::Decoder;
|
||||
const uint32_t code[] = {0xe20c2000u, 0x8001100cu};
|
||||
Decoder::Instruction inst;
|
||||
Decoder::DecodeInstruction(code, 0, inst);
|
||||
Check(inst.opcode == Decoder::Opcode::BUFFER_LOAD_FORMAT_D16_XYZW &&
|
||||
inst.word_count == 2 && inst.data_dwords == 2 && inst.data_bits == 16 &&
|
||||
inst.data_components == 4 && inst.formatted && !inst.typed,
|
||||
"captured D16 XYZW load lost its four components in two packed VGPRs");
|
||||
Check(inst.dst.kind == Decoder::OperandKind::Vgpr && inst.dst.reg == 16 &&
|
||||
inst.dst.sdwa_sel == 6 &&
|
||||
inst.src0.kind == Decoder::OperandKind::Vgpr && inst.src0.reg == 12 &&
|
||||
inst.src1.kind == Decoder::OperandKind::Sgpr && inst.src1.reg == 4 &&
|
||||
inst.idxen && !inst.offen,
|
||||
"captured D16 XYZW load lost its full destination VGPRs or index");
|
||||
}
|
||||
|
||||
void TestCapturedBufferLoadD16XyDecode() {
|
||||
namespace Decoder = ShaderRecompiler::Decoder;
|
||||
const uint32_t code[] = {0xe2042000u, 0x80090600u};
|
||||
Decoder::Instruction inst;
|
||||
Decoder::DecodeInstruction(code, 0, inst);
|
||||
Check(inst.opcode == Decoder::Opcode::BUFFER_LOAD_FORMAT_D16_XY &&
|
||||
inst.word_count == 2 && inst.data_dwords == 1 && inst.data_bits == 16 &&
|
||||
inst.data_components == 2 && inst.formatted && !inst.typed,
|
||||
"captured D16 XY load lost its two components in one packed VGPR");
|
||||
Check(inst.dst.kind == Decoder::OperandKind::Vgpr && inst.dst.reg == 6 &&
|
||||
inst.dst.sdwa_sel == 6 &&
|
||||
inst.src0.kind == Decoder::OperandKind::Vgpr && inst.src0.reg == 0 &&
|
||||
inst.src1.kind == Decoder::OperandKind::Sgpr && inst.src1.reg == 36 &&
|
||||
inst.idxen && !inst.offen,
|
||||
"captured D16 XY load lost its destination VGPR or index");
|
||||
}
|
||||
|
||||
void TestCapturedBufferStoreD16XyzwDecode() {
|
||||
namespace Decoder = ShaderRecompiler::Decoder;
|
||||
const uint32_t code[] = {0xe21c6000u, 0x8002000eu};
|
||||
@@ -16828,6 +16906,7 @@ int main() {
|
||||
TestNewShaderRecompilerNativeBindingPlan();
|
||||
TestNewShaderRecompilerStageInputInfo();
|
||||
TestCustomVintrpMovTranslation();
|
||||
TestVop3F16InterpolationTranslation();
|
||||
TestPerspectiveCentroidInputs();
|
||||
TestPerspectiveSampleInputs();
|
||||
TestGraphicsCreateInterpolantMapping();
|
||||
@@ -16838,6 +16917,8 @@ int main() {
|
||||
TestCapturedBufferLoadUbyteD16Decode();
|
||||
TestCapturedBufferStoreShortD16HiDecode();
|
||||
TestCapturedBufferStoreFormatD16HiXDecode();
|
||||
TestCapturedBufferLoadD16XyDecode();
|
||||
TestCapturedBufferLoadD16XyzwDecode();
|
||||
TestCapturedBufferStoreD16XyzwDecode();
|
||||
TestNewShaderRecompilerUnsupportedMemoryDecode();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user