mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-21 11:26:56 +02:00
Implement vertex swizzle
This commit is contained in:
@@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
namespace Libs::Graphics {
|
namespace Libs::Graphics {
|
||||||
|
|
||||||
//IDK: maybe we can remove it?
|
// IDK: maybe we can remove it?
|
||||||
constexpr uint32_t kTemporaryPs5BufferFormat121 = 121u;
|
constexpr uint32_t kTemporaryPs5BufferFormat121 = 121u;
|
||||||
|
|
||||||
static bool NarrowInputFormat(VkFormat* format, uint32_t* size, uint32_t used_components) {
|
static bool NarrowInputFormat(VkFormat* format, uint32_t* size, uint32_t used_components) {
|
||||||
@@ -561,13 +561,15 @@ void CreatePipelineInternal(PipelineCache::GraphicsPipeline* pipeline, VkRenderP
|
|||||||
input_attr[index].location = index;
|
input_attr[index].location = index;
|
||||||
input_attr[index].offset = b.attr_offsets[ai];
|
input_attr[index].offset = b.attr_offsets[ai];
|
||||||
|
|
||||||
uint32_t attr_size = 4;
|
uint32_t attr_size = 4;
|
||||||
auto registers_num = vs_input_info->resources_dst[index].registers_num;
|
auto registers_num = vs_input_info->resources_dst[index].registers_num;
|
||||||
auto used_components = (vs_input_info->resource_fetch_components[index] > 0
|
auto used_components = (vs_input_info->resource_fetch_components[index] > 0
|
||||||
? vs_input_info->resource_fetch_components[index]
|
? vs_input_info->resource_fetch_components[index]
|
||||||
: registers_num);
|
: registers_num);
|
||||||
|
const auto raw_components = VertexRawComponentCount(
|
||||||
|
vs_input_info->resources[index].DstSelXYZW(), static_cast<uint32_t>(registers_num));
|
||||||
GetInputFormat(vs_input_info->resources[index], &input_attr[index].format, &attr_size,
|
GetInputFormat(vs_input_info->resources[index], &input_attr[index].format, &attr_size,
|
||||||
static_cast<uint32_t>(used_components));
|
raw_components);
|
||||||
|
|
||||||
if (graphics_debug_dump_enabled()) {
|
if (graphics_debug_dump_enabled()) {
|
||||||
static std::atomic_uint log_count = 0;
|
static std::atomic_uint log_count = 0;
|
||||||
@@ -602,73 +604,18 @@ void CreatePipelineInternal(PipelineCache::GraphicsPipeline* pipeline, VkRenderP
|
|||||||
EXIT_NOT_IMPLEMENTED(vs_input_info->resources[index].AddTid());
|
EXIT_NOT_IMPLEMENTED(vs_input_info->resources[index].AddTid());
|
||||||
EXIT_NOT_IMPLEMENTED(vs_input_info->resources[index].SwizzleEnabled());
|
EXIT_NOT_IMPLEMENTED(vs_input_info->resources[index].SwizzleEnabled());
|
||||||
|
|
||||||
auto log_unsupported_vertex_swizzle = [index, attr_size, registers_num](
|
if (registers_num < 1 || registers_num > 4) {
|
||||||
uint32_t swizzle, uint32_t expected) {
|
EXIT("invalid vertex destination register count: %u\n", registers_num);
|
||||||
static bool logged = false;
|
}
|
||||||
if (!logged) {
|
for (uint32_t component = 0; component < static_cast<uint32_t>(registers_num);
|
||||||
LOGF(
|
component++) {
|
||||||
"VertexInput: temporary: accepting unsupported dst swizzle at attr %" PRIu32
|
const auto selector =
|
||||||
" (attr_size=%" PRIu32 ", regs=%" PRIu32 ", swizzle=0x%03" PRIx32
|
GetDstSel(vs_input_info->resources[index].DstSelXYZW(), component);
|
||||||
", expected=0x%03" PRIx32 ")\n",
|
if (DecodeVertexDstSelector(selector).kind == VertexDstSelectKind::Reserved) {
|
||||||
static_cast<uint32_t>(index), attr_size, registers_num, swizzle, expected);
|
EXIT("unsupported reserved vertex dst selector: attr=%u component=%u "
|
||||||
logged = true;
|
"selector=%u\n",
|
||||||
|
static_cast<uint32_t>(index), component, selector);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
switch (registers_num) {
|
|
||||||
case 1: {
|
|
||||||
auto swizzle = vs_input_info->resources[index].DstSelX();
|
|
||||||
if (swizzle != DstSel(4)) {
|
|
||||||
log_unsupported_vertex_swizzle(swizzle, DstSel(4));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 2: {
|
|
||||||
auto swizzle = vs_input_info->resources[index].DstSelXY();
|
|
||||||
auto expected = (attr_size == 1 ? DstSel(4, 0) : DstSel(4, 5));
|
|
||||||
if (swizzle != expected) {
|
|
||||||
log_unsupported_vertex_swizzle(swizzle, expected);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 3: {
|
|
||||||
auto swizzle = vs_input_info->resources[index].DstSelXYZ();
|
|
||||||
auto expected =
|
|
||||||
(attr_size == 1 ? DstSel(4, 0, 0)
|
|
||||||
: (attr_size == 2 ? DstSel(4, 5, 0) : DstSel(4, 5, 6)));
|
|
||||||
if (swizzle != expected) {
|
|
||||||
log_unsupported_vertex_swizzle(swizzle, expected);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 4: {
|
|
||||||
auto swizzle = vs_input_info->resources[index].DstSelXYZW();
|
|
||||||
auto expected = DstSel(4, 5, 6, 7);
|
|
||||||
bool supported = false;
|
|
||||||
switch (attr_size) {
|
|
||||||
case 1:
|
|
||||||
expected = DstSel(4, 0, 0, 1);
|
|
||||||
supported = (swizzle == expected);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
expected = DstSel(4, 5, 0, 1);
|
|
||||||
supported = (swizzle == expected);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
expected = DstSel(4, 5, 6, 1);
|
|
||||||
supported = (swizzle == expected || swizzle == DstSel(4, 5, 6, 0));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
supported = (swizzle == expected || swizzle == DstSel(4, 5, 6, 1) ||
|
|
||||||
swizzle == DstSel(4, 5, 6, 0));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!supported) {
|
|
||||||
log_unsupported_vertex_swizzle(swizzle, expected);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: EXIT("invalid registers_num");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -639,6 +639,8 @@ struct Instruction {
|
|||||||
bool slc = false;
|
bool slc = false;
|
||||||
bool idxen = false;
|
bool idxen = false;
|
||||||
bool offen = false;
|
bool offen = false;
|
||||||
|
// V_CMPX writes the comparison result to its normal scalar destination and EXEC.
|
||||||
|
bool compare_exec = false;
|
||||||
int32_t branch_offset = 0;
|
int32_t branch_offset = 0;
|
||||||
uint32_t branch_target = 0;
|
uint32_t branch_target = 0;
|
||||||
struct {
|
struct {
|
||||||
|
|||||||
@@ -1103,9 +1103,8 @@ bool DecodeVopcSdwa(uint32_t pc, std::span<const uint32_t> code, uint32_t word_i
|
|||||||
!DecodeScalarSource(vsrc1 + (fields.s1 == 0u ? 256u : 0u), pc, &inst->src1, error)) {
|
!DecodeScalarSource(vsrc1 + (fields.s1 == 0u ? 256u : 0u), pc, &inst->src1, error)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (IsVopcCompareExec(inst->opcode)) {
|
inst->compare_exec = IsVopcCompareExec(inst->opcode);
|
||||||
inst->dst.kind = OperandKind::ExecLo;
|
if (fields.sd == 0u) {
|
||||||
} else if (fields.sd == 0u) {
|
|
||||||
inst->dst.kind = OperandKind::VccLo;
|
inst->dst.kind = OperandKind::VccLo;
|
||||||
} else if (!DecodeScalarDestination(fields.sdst, pc, &inst->dst, error)) {
|
} else if (!DecodeScalarDestination(fields.sdst, pc, &inst->dst, error)) {
|
||||||
return false;
|
return false;
|
||||||
@@ -1142,7 +1141,8 @@ bool DecodeVopcDpp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_in
|
|||||||
!DecodeVectorGpr(vsrc1, &inst->src1, error)) {
|
!DecodeVectorGpr(vsrc1, &inst->src1, error)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
inst->dst.kind = IsVopcCompareExec(inst->opcode) ? OperandKind::ExecLo : OperandKind::VccLo;
|
inst->dst.kind = OperandKind::VccLo;
|
||||||
|
inst->compare_exec = IsVopcCompareExec(inst->opcode);
|
||||||
inst->src0.negate = ((modifier >> 20u) & 0x1u) != 0u;
|
inst->src0.negate = ((modifier >> 20u) & 0x1u) != 0u;
|
||||||
inst->src0.absolute = ((modifier >> 21u) & 0x1u) != 0u;
|
inst->src0.absolute = ((modifier >> 21u) & 0x1u) != 0u;
|
||||||
inst->src0.dpp = true;
|
inst->src0.dpp = true;
|
||||||
@@ -1532,7 +1532,8 @@ bool DecodeVopc(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
|||||||
inst->family = Family::VOPC;
|
inst->family = Family::VOPC;
|
||||||
inst->opcode_id = opcode;
|
inst->opcode_id = opcode;
|
||||||
inst->opcode = Lookup(VOPC_OPS, static_cast<uint32_t>(std::size(VOPC_OPS)), opcode);
|
inst->opcode = Lookup(VOPC_OPS, static_cast<uint32_t>(std::size(VOPC_OPS)), opcode);
|
||||||
inst->dst.kind = IsVopcCompareExec(inst->opcode) ? OperandKind::ExecLo : OperandKind::VccLo;
|
inst->dst.kind = OperandKind::VccLo;
|
||||||
|
inst->compare_exec = IsVopcCompareExec(inst->opcode);
|
||||||
SetRawWords(inst, code, word_index, 1);
|
SetRawWords(inst, code, word_index, 1);
|
||||||
|
|
||||||
switch (src0) {
|
switch (src0) {
|
||||||
@@ -1618,9 +1619,8 @@ bool DecodeVop3(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool dst_ok = true;
|
bool dst_ok = true;
|
||||||
if (compare_exec) {
|
inst->compare_exec = compare_exec;
|
||||||
inst->dst.kind = OperandKind::ExecLo;
|
if (scalar_dst) {
|
||||||
} else if (scalar_dst) {
|
|
||||||
// VOP3A uses VDST for VOPC and the scalar-destination lane-read opcodes.
|
// VOP3A uses VDST for VOPC and the scalar-destination lane-read opcodes.
|
||||||
dst_ok = DecodeScalarDestination(vdst, pc, &inst->dst, error);
|
dst_ok = DecodeScalarDestination(vdst, pc, &inst->dst, error);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1165,6 +1165,22 @@ bool LowerImplemented(const Decoder::Instruction& decoded, BasicBlock* block, st
|
|||||||
}
|
}
|
||||||
|
|
||||||
block->instructions.push_back(inst);
|
block->instructions.push_back(inst);
|
||||||
|
if (decoded.compare_exec) {
|
||||||
|
// V_CMPX has two architectural destinations. Keep the comparison's normal VCC/SDST
|
||||||
|
// result, then mirror the complete lane mask to EXEC. A mask is always a 64-bit scalar pair, including wave32 shaders.
|
||||||
|
for (uint32_t i = 0; i < 2u; i++) {
|
||||||
|
Instruction move;
|
||||||
|
move.pc = decoded.pc;
|
||||||
|
move.op = Opcode::MoveU32;
|
||||||
|
move.src_count = 1;
|
||||||
|
move.src[0] = inst.dst;
|
||||||
|
move.src[0].reg.index += i;
|
||||||
|
move.dst.kind = OperandKind::Register;
|
||||||
|
move.dst.reg.file = RegisterFile::Exec;
|
||||||
|
move.dst.reg.index = i;
|
||||||
|
block->instructions.push_back(move);
|
||||||
|
}
|
||||||
|
}
|
||||||
return AppendScalarResultSccNonZero(decoded, block, error);
|
return AppendScalarResultSccNonZero(decoded, block, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -94,8 +94,12 @@ uint32_t VertexParameterComponentCount(const EmitterState& state, const InputBin
|
|||||||
input.location < ShaderVertexInputInfo::RES_MAX &&
|
input.location < ShaderVertexInputInfo::RES_MAX &&
|
||||||
input.location < static_cast<uint32_t>(state.vertex_input_info->resources_num) &&
|
input.location < static_cast<uint32_t>(state.vertex_input_info->resources_num) &&
|
||||||
state.vertex_input_info->resources_dst[input.location].registers_num > 0) {
|
state.vertex_input_info->resources_dst[input.location].registers_num > 0) {
|
||||||
count = static_cast<uint32_t>(
|
const auto& descriptor = state.vertex_input_info->resources[input.location];
|
||||||
|
const auto dst_count = static_cast<uint32_t>(
|
||||||
state.vertex_input_info->resources_dst[input.location].registers_num);
|
state.vertex_input_info->resources_dst[input.location].registers_num);
|
||||||
|
// Keep the SPIR-V interface and native VkFormat width identical. A destination
|
||||||
|
// selector may request a raw component beyond the written VGPR count (W -> X).
|
||||||
|
count = VertexRawComponentCount(descriptor.DstSelXYZW(), dst_count);
|
||||||
}
|
}
|
||||||
return std::clamp(count, 1u, 4u);
|
return std::clamp(count, 1u, 4u);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -450,7 +450,25 @@ uint32_t EmitVertexParameterComponentU32(EmitterState* state, const InputBinding
|
|||||||
uint32_t component) {
|
uint32_t component) {
|
||||||
const auto count = VertexParameterComponentCount(*state, input);
|
const auto count = VertexParameterComponentCount(*state, input);
|
||||||
const auto kind = VertexParameterScalarKind(*state, input.location);
|
const auto kind = VertexParameterScalarKind(*state, input.location);
|
||||||
if (component >= count) {
|
uint32_t source_component = component;
|
||||||
|
if (input.location < ShaderVertexInputInfo::RES_MAX &&
|
||||||
|
input.location < static_cast<uint32_t>(state->vertex_input_info->resources_num)) {
|
||||||
|
const auto& descriptor = state->vertex_input_info->resources[input.location];
|
||||||
|
const auto selection =
|
||||||
|
DecodeVertexDstSelector(GetDstSel(descriptor.DstSelXYZW(), component & 3u));
|
||||||
|
if (selection.kind == VertexDstSelectKind::Zero) {
|
||||||
|
return ConstantU32(state, 0u);
|
||||||
|
}
|
||||||
|
if (selection.kind == VertexDstSelectKind::One) {
|
||||||
|
return ConstantU32(state,
|
||||||
|
kind == VertexInputScalarKind::Float ? 0x3f800000u : 1u);
|
||||||
|
}
|
||||||
|
if (selection.kind == VertexDstSelectKind::Reserved) {
|
||||||
|
EXIT("reserved vertex destination selector\n");
|
||||||
|
}
|
||||||
|
source_component = selection.source_component;
|
||||||
|
}
|
||||||
|
if (source_component >= count) {
|
||||||
return VertexInputDefaultComponentU32(state, kind, component);
|
return VertexInputDefaultComponentU32(state, kind, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -462,7 +480,7 @@ uint32_t EmitVertexParameterComponentU32(EmitterState* state, const InputBinding
|
|||||||
const auto pointer_type = VertexParameterScalarPointerType(*state, kind);
|
const auto pointer_type = VertexParameterScalarPointerType(*state, kind);
|
||||||
const auto pointer = state->builder.AllocateId();
|
const auto pointer = state->builder.AllocateId();
|
||||||
state->builder.AddFunction({OpAccessChain, pointer_type, pointer, input.variable_id,
|
state->builder.AddFunction({OpAccessChain, pointer_type, pointer, input.variable_id,
|
||||||
ConstantU32(state, component)});
|
ConstantU32(state, source_component)});
|
||||||
state->builder.AddFunction({OpLoad, scalar_type, raw, pointer});
|
state->builder.AddFunction({OpLoad, scalar_type, raw, pointer});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -64,6 +64,41 @@ inline uint8_t GetDstSel(uint32_t swizzle, uint32_t channel) {
|
|||||||
return (swizzle >> (channel * 3u)) & 0x7u;
|
return (swizzle >> (channel * 3u)) & 0x7u;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class VertexDstSelectKind : uint8_t { Zero, One, Component, Reserved };
|
||||||
|
|
||||||
|
struct VertexDstSelection {
|
||||||
|
VertexDstSelectKind kind = VertexDstSelectKind::Reserved;
|
||||||
|
uint8_t source_component = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr VertexDstSelection DecodeVertexDstSelector(uint8_t selector) {
|
||||||
|
if (selector == 0u) {
|
||||||
|
return {VertexDstSelectKind::Zero, 0};
|
||||||
|
}
|
||||||
|
if (selector == 1u) {
|
||||||
|
return {VertexDstSelectKind::One, 0};
|
||||||
|
}
|
||||||
|
if (selector >= 4u && selector <= 7u) {
|
||||||
|
return {VertexDstSelectKind::Component, static_cast<uint8_t>(selector - 4u)};
|
||||||
|
}
|
||||||
|
return {VertexDstSelectKind::Reserved, 0};
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr uint32_t VertexRawComponentCount(uint32_t swizzle, uint32_t destination_components) {
|
||||||
|
const auto dst_count = destination_components < 1u
|
||||||
|
? 1u
|
||||||
|
: (destination_components > 4u ? 4u : destination_components);
|
||||||
|
auto count = dst_count;
|
||||||
|
for (uint32_t component = 0; component < dst_count; component++) {
|
||||||
|
const auto selection = DecodeVertexDstSelector(GetDstSel(swizzle, component));
|
||||||
|
if (selection.kind == VertexDstSelectKind::Component) {
|
||||||
|
const auto source_count = static_cast<uint32_t>(selection.source_component) + 1u;
|
||||||
|
count = count > source_count ? count : source_count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
struct ShaderVertexInputInfo {
|
struct ShaderVertexInputInfo {
|
||||||
static constexpr int RES_MAX = 32;
|
static constexpr int RES_MAX = 32;
|
||||||
|
|
||||||
@@ -96,26 +131,26 @@ struct ShaderComputeInputInfo {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct ShaderPixelInputInfo {
|
struct ShaderPixelInputInfo {
|
||||||
uint32_t interpolator_settings[32] = {0};
|
uint32_t interpolator_settings[32] = {0};
|
||||||
uint32_t input_num = 0;
|
uint32_t input_num = 0;
|
||||||
uint32_t ps_system_input_base = 0;
|
uint32_t ps_system_input_base = 0;
|
||||||
uint8_t target_output_mode[8] = {};
|
uint8_t target_output_mode[8] = {};
|
||||||
std::array<Prospero::ColorComponentMapping, 8> target_export_mapping = {};
|
std::array<Prospero::ColorComponentMapping, 8> target_export_mapping = {};
|
||||||
uint32_t mrt_output_mask = 0;
|
uint32_t mrt_output_mask = 0;
|
||||||
uint32_t descriptor_set = 0;
|
uint32_t descriptor_set = 0;
|
||||||
bool ps_pos_x = false;
|
bool ps_pos_x = false;
|
||||||
bool ps_pos_y = false;
|
bool ps_pos_y = false;
|
||||||
bool ps_pos_xy = false;
|
bool ps_pos_xy = false;
|
||||||
bool ps_pos_z = false;
|
bool ps_pos_z = false;
|
||||||
bool ps_pos_w = false;
|
bool ps_pos_w = false;
|
||||||
bool ps_front_face = false;
|
bool ps_front_face = false;
|
||||||
bool ps_no_perspective = false;
|
bool ps_no_perspective = false;
|
||||||
bool ps_pixel_kill_enable = false;
|
bool ps_pixel_kill_enable = false;
|
||||||
bool ps_depth_export_enable = false;
|
bool ps_depth_export_enable = false;
|
||||||
bool ps_sample_mask_export_enable = false;
|
bool ps_sample_mask_export_enable = false;
|
||||||
bool ps_early_z = false;
|
bool ps_early_z = false;
|
||||||
bool ps_execute_on_noop = false;
|
bool ps_execute_on_noop = false;
|
||||||
ShaderStageRuntime stage;
|
ShaderStageRuntime stage;
|
||||||
|
|
||||||
bool HasPositionInput() const { return ps_pos_x || ps_pos_y || ps_pos_z || ps_pos_w; }
|
bool HasPositionInput() const { return ps_pos_x || ps_pos_y || ps_pos_z || ps_pos_w; }
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5700,24 +5700,24 @@ TestCase VectorVop3FloatCompareNegSourceModifier() {
|
|||||||
{O::VMovB32, O::VCmpLtF32, O::BufferStoreDword, O::SEndpgm}};
|
{O::VMovB32, O::VCmpLtF32, O::BufferStoreDword, O::SEndpgm}};
|
||||||
}
|
}
|
||||||
|
|
||||||
TestCase VectorVop3CmpxWritesExecMask() {
|
TestCase VectorVop3CmpxWritesScalarDstAndExecMask() {
|
||||||
using O = ShaderOpcode;
|
using O = ShaderOpcode;
|
||||||
|
|
||||||
std::vector<u32> code;
|
std::vector<u32> code;
|
||||||
AppendVMovU32(&code, 0, 2);
|
AppendVMovU32(&code, 0, 1);
|
||||||
AppendVMovU32(&code, 1, 1);
|
AppendVMovU32(&code, 1, 1);
|
||||||
AppendVMovU32(&code, 2, 0);
|
AppendVMovU32(&code, 2, 7);
|
||||||
AppendVMovU32(&code, 30, 0);
|
AppendVMovU32(&code, 30, 0);
|
||||||
AppendVop3(&code, 0xd1, 5, Vgpr(0), Vgpr(1)); // v_cmpx_lt_u32, false
|
AppendVop3(&code, 0xd2, 5, Vgpr(0), Vgpr(1)); // v_cmpx_eq_u32 s[5:6], true
|
||||||
AppendVMovU32(&code, 2, 7);
|
code.push_back(EncodeSop1(0x0a, 126, 5)); // s_wqm_b64 exec, s[5:6]
|
||||||
AppendBufferStoreDword(&code, 2, 30);
|
AppendBufferStoreDword(&code, 2, 30);
|
||||||
AppendEnd(&code);
|
AppendEnd(&code);
|
||||||
|
|
||||||
return {"VectorVop3CmpxWritesExecMask",
|
return {"VectorVop3CmpxWritesScalarDstAndExecMask",
|
||||||
code,
|
code,
|
||||||
{0},
|
{0},
|
||||||
{0},
|
{7},
|
||||||
{O::VMovB32, O::VCmpxLtU32, O::BufferStoreDword, O::SEndpgm}};
|
{O::VMovB32, O::VCmpxEqU32, O::SWqmB64, O::BufferStoreDword, O::SEndpgm}};
|
||||||
}
|
}
|
||||||
|
|
||||||
TestCase VectorVopcSdwaCmpxWritesExecMask() {
|
TestCase VectorVopcSdwaCmpxWritesExecMask() {
|
||||||
@@ -5741,6 +5741,28 @@ TestCase VectorVopcSdwaCmpxWritesExecMask() {
|
|||||||
{O::VMovB32, O::VCmpxLtU32, O::BufferStoreDword, O::SEndpgm}};
|
{O::VMovB32, O::VCmpxLtU32, O::BufferStoreDword, O::SEndpgm}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TestCase VectorVopcCmpxPreservesVccForWqm() {
|
||||||
|
using O = ShaderOpcode;
|
||||||
|
|
||||||
|
std::vector<u32> code;
|
||||||
|
AppendVMovU32(&code, 0, 1);
|
||||||
|
AppendVMovU32(&code, 1, 1);
|
||||||
|
AppendVMovU32(&code, 2, 7);
|
||||||
|
AppendVMovU32(&code, 30, 0);
|
||||||
|
code.push_back(EncodeSop1(0x04, 106, InlineU32(0))); // s_mov_b64 vcc, 0
|
||||||
|
code.push_back(EncodeVopc(0xd2, Vgpr(0), 1)); // v_cmpx_eq_u32, true
|
||||||
|
code.push_back(EncodeSop1(0x0a, 126, 106)); // s_wqm_b64 exec, vcc
|
||||||
|
AppendBufferStoreDword(&code, 2, 30);
|
||||||
|
AppendEnd(&code);
|
||||||
|
|
||||||
|
return {"VectorVopcCmpxPreservesVccForWqm",
|
||||||
|
code,
|
||||||
|
{0},
|
||||||
|
{7},
|
||||||
|
{O::VMovB32, O::SMovB64, O::VCmpxEqU32, O::SWqmB64,
|
||||||
|
O::BufferStoreDword, O::SEndpgm}};
|
||||||
|
}
|
||||||
|
|
||||||
TestCase VectorCompareInvertedMaskSelect() {
|
TestCase VectorCompareInvertedMaskSelect() {
|
||||||
using O = ShaderOpcode;
|
using O = ShaderOpcode;
|
||||||
|
|
||||||
@@ -8638,8 +8660,9 @@ std::vector<TestCase> MakeCases() {
|
|||||||
AddCase(Vop3CndmaskAllowsDataSourceModifier);
|
AddCase(Vop3CndmaskAllowsDataSourceModifier);
|
||||||
AddCase(VectorCompareExecOps);
|
AddCase(VectorCompareExecOps);
|
||||||
AddCase(VectorVop3FloatCompareNegSourceModifier);
|
AddCase(VectorVop3FloatCompareNegSourceModifier);
|
||||||
AddCase(VectorVop3CmpxWritesExecMask);
|
AddCase(VectorVop3CmpxWritesScalarDstAndExecMask);
|
||||||
AddCase(VectorVopcSdwaCmpxWritesExecMask);
|
AddCase(VectorVopcSdwaCmpxWritesExecMask);
|
||||||
|
AddCase(VectorVopcCmpxPreservesVccForWqm);
|
||||||
AddCase(VectorCompareInvertedMaskSelect);
|
AddCase(VectorCompareInvertedMaskSelect);
|
||||||
AddCase(BranchSelect);
|
AddCase(BranchSelect);
|
||||||
AddCase(SimpleLoop);
|
AddCase(SimpleLoop);
|
||||||
|
|||||||
@@ -9,85 +9,122 @@ namespace {
|
|||||||
|
|
||||||
using namespace Libs::Graphics;
|
using namespace Libs::Graphics;
|
||||||
|
|
||||||
void Check(bool value, const char* text) {
|
void Check(bool value, const char *text) {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
std::fprintf(stderr, "ShaderVertexMetadataTests: failed: %s\n", text);
|
std::fprintf(stderr, "ShaderVertexMetadataTests: failed: %s\n", text);
|
||||||
std::abort();
|
std::abort();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Fixture {
|
struct Fixture {
|
||||||
std::array<uint16_t, static_cast<size_t>(AgcDirectResourceType::Last) + 1> offsets {};
|
std::array<uint16_t, static_cast<size_t>(AgcDirectResourceType::Last) + 1>
|
||||||
ShaderUserData user_data {};
|
offsets{};
|
||||||
ShaderSemantic semantic {};
|
ShaderUserData user_data{};
|
||||||
ShaderMappedData mapped {};
|
ShaderSemantic semantic{};
|
||||||
|
ShaderMappedData mapped{};
|
||||||
|
|
||||||
Fixture() {
|
Fixture() {
|
||||||
offsets.fill(AGC_ILLEGAL_DIRECT_OFFSET);
|
offsets.fill(AGC_ILLEGAL_DIRECT_OFFSET);
|
||||||
offsets[static_cast<size_t>(AgcDirectResourceType::PtrVertexBufferTable)] = 2;
|
offsets[static_cast<size_t>(AgcDirectResourceType::PtrVertexBufferTable)] =
|
||||||
offsets[static_cast<size_t>(AgcDirectResourceType::PtrVertexAttribDescTable)] = 4;
|
2;
|
||||||
user_data.direct_resource_offset = offsets.data();
|
offsets[static_cast<size_t>(
|
||||||
user_data.direct_resource_count = static_cast<uint16_t>(offsets.size());
|
AgcDirectResourceType::PtrVertexAttribDescTable)] = 4;
|
||||||
mapped.user_data = &user_data;
|
user_data.direct_resource_offset = offsets.data();
|
||||||
mapped.input_semantics = &semantic;
|
user_data.direct_resource_count = static_cast<uint16_t>(offsets.size());
|
||||||
mapped.num_input_semantics = 1;
|
mapped.user_data = &user_data;
|
||||||
}
|
mapped.input_semantics = &semantic;
|
||||||
|
mapped.num_input_semantics = 1;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void CheckRejected(const ShaderMappedData& data, const char* text) {
|
void CheckRejected(const ShaderMappedData &data, const char *text) {
|
||||||
ShaderVertexMetadata output;
|
ShaderVertexMetadata output;
|
||||||
output.vertex_buffer_reg = 37;
|
output.vertex_buffer_reg = 37;
|
||||||
output.vertex_attrib_reg = 41;
|
output.vertex_attrib_reg = 41;
|
||||||
output.input_semantics_count = 7;
|
output.input_semantics_count = 7;
|
||||||
std::string error;
|
std::string error;
|
||||||
Check(!ShaderReadVertexMetadata(data, 64, &output, &error), text);
|
Check(!ShaderReadVertexMetadata(data, 64, &output, &error), text);
|
||||||
Check(!error.empty(), "metadata rejection omitted its diagnostic");
|
Check(!error.empty(), "metadata rejection omitted its diagnostic");
|
||||||
Check(output.vertex_buffer_reg == 37 && output.vertex_attrib_reg == 41 &&
|
Check(output.vertex_buffer_reg == 37 && output.vertex_attrib_reg == 41 &&
|
||||||
output.input_semantics_count == 7,
|
output.input_semantics_count == 7,
|
||||||
"metadata rejection changed the prior output");
|
"metadata rejection changed the prior output");
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestValidAndInvalidMetadata() {
|
void TestValidAndInvalidMetadata() {
|
||||||
Fixture fixture;
|
Fixture fixture;
|
||||||
ShaderVertexMetadata output;
|
ShaderVertexMetadata output;
|
||||||
std::string error;
|
std::string error;
|
||||||
Check(ShaderReadVertexMetadata(fixture.mapped, 64, &output, &error),
|
Check(ShaderReadVertexMetadata(fixture.mapped, 64, &output, &error),
|
||||||
"valid AGC vertex metadata was rejected");
|
"valid AGC vertex metadata was rejected");
|
||||||
Check(output.vertex_buffer_reg == 2 && output.vertex_attrib_reg == 4 &&
|
Check(output.vertex_buffer_reg == 2 && output.vertex_attrib_reg == 4 &&
|
||||||
output.input_semantics_count == 1,
|
output.input_semantics_count == 1,
|
||||||
"valid AGC vertex metadata was decoded incorrectly");
|
"valid AGC vertex metadata was decoded incorrectly");
|
||||||
|
|
||||||
auto missing_header = fixture.mapped;
|
auto missing_header = fixture.mapped;
|
||||||
missing_header.user_data = nullptr;
|
missing_header.user_data = nullptr;
|
||||||
CheckRejected(missing_header, "missing AGC user-data header was accepted");
|
CheckRejected(missing_header, "missing AGC user-data header was accepted");
|
||||||
|
|
||||||
Fixture missing_offsets;
|
Fixture missing_offsets;
|
||||||
missing_offsets.user_data.direct_resource_offset = nullptr;
|
missing_offsets.user_data.direct_resource_offset = nullptr;
|
||||||
CheckRejected(missing_offsets.mapped, "missing direct-resource offsets were accepted");
|
CheckRejected(missing_offsets.mapped,
|
||||||
|
"missing direct-resource offsets were accepted");
|
||||||
|
|
||||||
Fixture excessive_resources;
|
Fixture excessive_resources;
|
||||||
excessive_resources.user_data.direct_resource_count =
|
excessive_resources.user_data.direct_resource_count =
|
||||||
static_cast<uint16_t>(excessive_resources.offsets.size() + 1);
|
static_cast<uint16_t>(excessive_resources.offsets.size() + 1);
|
||||||
CheckRejected(excessive_resources.mapped, "excessive direct-resource count was accepted");
|
CheckRejected(excessive_resources.mapped,
|
||||||
|
"excessive direct-resource count was accepted");
|
||||||
|
|
||||||
Fixture excessive_semantics;
|
Fixture excessive_semantics;
|
||||||
excessive_semantics.mapped.num_input_semantics = ShaderVertexInputInfo::RES_MAX + 1;
|
excessive_semantics.mapped.num_input_semantics =
|
||||||
CheckRejected(excessive_semantics.mapped, "excessive vertex semantic count was accepted");
|
ShaderVertexInputInfo::RES_MAX + 1;
|
||||||
|
CheckRejected(excessive_semantics.mapped,
|
||||||
|
"excessive vertex semantic count was accepted");
|
||||||
|
|
||||||
Fixture excessive_register;
|
Fixture excessive_register;
|
||||||
excessive_register.offsets[
|
excessive_register.offsets[static_cast<size_t>(
|
||||||
static_cast<size_t>(AgcDirectResourceType::PtrVertexBufferTable)] = 63;
|
AgcDirectResourceType::PtrVertexBufferTable)] = 63;
|
||||||
CheckRejected(excessive_register.mapped, "out-of-domain vertex table SGPR was accepted");
|
CheckRejected(excessive_register.mapped,
|
||||||
|
"out-of-domain vertex table SGPR was accepted");
|
||||||
|
|
||||||
Fixture missing_semantics;
|
Fixture missing_semantics;
|
||||||
missing_semantics.mapped.input_semantics = nullptr;
|
missing_semantics.mapped.input_semantics = nullptr;
|
||||||
CheckRejected(missing_semantics.mapped, "missing vertex semantic array was accepted");
|
CheckRejected(missing_semantics.mapped,
|
||||||
|
"missing vertex semantic array was accepted");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestVertexDstSelectors() {
|
||||||
|
const auto zero = DecodeVertexDstSelector(0);
|
||||||
|
const auto one = DecodeVertexDstSelector(1);
|
||||||
|
Check(zero.kind == VertexDstSelectKind::Zero &&
|
||||||
|
one.kind == VertexDstSelectKind::One,
|
||||||
|
"vertex constant selectors were decoded incorrectly");
|
||||||
|
for (uint8_t selector = 4; selector <= 7; selector++) {
|
||||||
|
const auto component = DecodeVertexDstSelector(selector);
|
||||||
|
Check(component.kind == VertexDstSelectKind::Component &&
|
||||||
|
component.source_component == selector - 4,
|
||||||
|
"vertex component selector was decoded incorrectly");
|
||||||
|
}
|
||||||
|
Check(DecodeVertexDstSelector(2).kind == VertexDstSelectKind::Reserved &&
|
||||||
|
DecodeVertexDstSelector(3).kind == VertexDstSelectKind::Reserved,
|
||||||
|
"reserved vertex selectors were accepted");
|
||||||
|
Check(GetDstSel(DstSel(6, 5, 4, 7), 0) == 6 &&
|
||||||
|
GetDstSel(DstSel(6, 5, 4, 7), 1) == 5 &&
|
||||||
|
GetDstSel(DstSel(6, 5, 4, 7), 2) == 4 &&
|
||||||
|
GetDstSel(DstSel(6, 5, 4, 7), 3) == 7,
|
||||||
|
"BGRA vertex selector packing was decoded incorrectly");
|
||||||
|
Check(VertexRawComponentCount(DstSel(7, 0, 0, 0), 1) == 4,
|
||||||
|
"W-to-X did not widen the raw vertex interface to four components");
|
||||||
|
Check(VertexRawComponentCount(DstSel(4, 4, 4, 4), 4) == 4 &&
|
||||||
|
VertexRawComponentCount(DstSel(4, 0, 0, 1), 4) == 4,
|
||||||
|
"replication or constant selectors changed raw vertex width");
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
TestValidAndInvalidMetadata();
|
TestValidAndInvalidMetadata();
|
||||||
std::puts("ShaderVertexMetadataTests: all cases passed");
|
TestVertexDstSelectors();
|
||||||
return 0;
|
std::puts("ShaderVertexMetadataTests: all cases passed");
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
+20
-15
@@ -1473,9 +1473,11 @@ void TestNewShaderRecompilerMoreAluFamilies() {
|
|||||||
"new decoder did not decode old-backed V_CMP_NEQ_F32");
|
"new decoder did not decode old-backed V_CMP_NEQ_F32");
|
||||||
Check(Common::ContainsStr(result.decoded_dump, "v_cmp_neq_f32 s0, 0.500000, v1"),
|
Check(Common::ContainsStr(result.decoded_dump, "v_cmp_neq_f32 s0, 0.500000, v1"),
|
||||||
"new decoder did not decode old-backed V_CMP_NEQ_F32 SDWA scalar destination");
|
"new decoder did not decode old-backed V_CMP_NEQ_F32 SDWA scalar destination");
|
||||||
Check(Common::ContainsStr(result.decoded_dump, "v_cmpx_lt_u32 exec_lo, v5.sdwa(sel=4") &&
|
Check(Common::ContainsStr(result.decoded_dump, "v_cmpx_lt_u32 vcc_lo, v5.sdwa(sel=4") &&
|
||||||
Common::ContainsStr(result.ir_dump, "CompareMaskLtU32 exec_lo"),
|
Common::ContainsStr(result.ir_dump, "CompareMaskLtU32 vcc_lo") &&
|
||||||
"new decoder did not route V_CMPX SDWA destination to exec");
|
Common::ContainsStr(result.ir_dump, "MoveU32 exec_lo, vcc_lo") &&
|
||||||
|
Common::ContainsStr(result.ir_dump, "MoveU32 exec_hi, vcc_hi"),
|
||||||
|
"new decoder did not preserve V_CMPX SDWA destination and mirror its mask to exec");
|
||||||
Check(Common::ContainsStr(result.decoded_dump, "v_cmp_nlt_f32"),
|
Check(Common::ContainsStr(result.decoded_dump, "v_cmp_nlt_f32"),
|
||||||
"new decoder did not decode old-backed V_CMP_NLT_F32");
|
"new decoder did not decode old-backed V_CMP_NLT_F32");
|
||||||
Check(Common::ContainsStr(result.decoded_dump, "v_cmpx_nge_f32"),
|
Check(Common::ContainsStr(result.decoded_dump, "v_cmpx_nge_f32"),
|
||||||
@@ -1774,10 +1776,12 @@ void TestNewShaderRecompilerMoreAluFamilies() {
|
|||||||
"VOP3-encoded VOP1 find-first-bit-low did not lower through shared IR");
|
"VOP3-encoded VOP1 find-first-bit-low did not lower through shared IR");
|
||||||
Check(Common::ContainsStr(result.ir_dump, "CompareGtF32"),
|
Check(Common::ContainsStr(result.ir_dump, "CompareGtF32"),
|
||||||
"VOPC float compare did not lower to IR");
|
"VOPC float compare did not lower to IR");
|
||||||
Check(Common::ContainsStr(result.ir_dump, "CompareMaskGtF32 exec_lo"),
|
Check(Common::ContainsStr(result.ir_dump, "CompareMaskGtF32 vcc_lo") &&
|
||||||
"VOPC float compare-and-mask did not lower to exec mask IR");
|
Common::ContainsStr(result.ir_dump, "MoveU32 exec_lo, vcc_lo"),
|
||||||
Check(Common::ContainsStr(result.ir_dump, "CompareMaskGtU32 exec_lo"),
|
"VOPC float compare-and-mask did not lower to VCC plus EXEC IR");
|
||||||
"VOPC uint compare-and-mask did not lower to exec mask IR");
|
Check(Common::ContainsStr(result.ir_dump, "CompareMaskGtU32 vcc_lo") &&
|
||||||
|
Common::ContainsStr(result.ir_dump, "MoveU32 exec_hi, vcc_hi"),
|
||||||
|
"VOPC uint compare-and-mask did not lower to VCC plus EXEC IR");
|
||||||
Check(Common::ContainsStr(result.ir_dump, "CompareFalse vcc_lo, v6, v6"),
|
Check(Common::ContainsStr(result.ir_dump, "CompareFalse vcc_lo, v6, v6"),
|
||||||
"VOPC false compare did not lower to shared IR");
|
"VOPC false compare did not lower to shared IR");
|
||||||
Check(Common::ContainsStr(result.ir_dump, "CompareTrue vcc_lo, v6, v6"),
|
Check(Common::ContainsStr(result.ir_dump, "CompareTrue vcc_lo, v6, v6"),
|
||||||
@@ -1800,17 +1804,17 @@ void TestNewShaderRecompilerMoreAluFamilies() {
|
|||||||
"VOPC SDWA unordered-not-equal compare did not lower to scalar-destination IR");
|
"VOPC SDWA unordered-not-equal compare did not lower to scalar-destination IR");
|
||||||
Check(Common::ContainsStr(result.ir_dump, "CompareUnordGeF32 vcc_lo, v6, v6"),
|
Check(Common::ContainsStr(result.ir_dump, "CompareUnordGeF32 vcc_lo, v6, v6"),
|
||||||
"VOPC unordered-greater-equal compare did not lower to shared IR");
|
"VOPC unordered-greater-equal compare did not lower to shared IR");
|
||||||
Check(Common::ContainsStr(result.ir_dump, "CompareMaskUnordLtF32 exec_lo, v6, v6"),
|
Check(Common::ContainsStr(result.ir_dump, "CompareMaskUnordLtF32 vcc_lo, v6, v6"),
|
||||||
"VOPC unordered-less compare-and-mask did not lower to shared IR");
|
"VOPC unordered-less compare-and-mask did not lower to shared IR");
|
||||||
Check(Common::ContainsStr(result.ir_dump, "CompareMaskUnordEqF32 exec_lo, v6, v6"),
|
Check(Common::ContainsStr(result.ir_dump, "CompareMaskUnordEqF32 vcc_lo, v6, v6"),
|
||||||
"VOPC unordered-equal compare-and-mask did not lower to shared IR");
|
"VOPC unordered-equal compare-and-mask did not lower to shared IR");
|
||||||
Check(Common::ContainsStr(result.ir_dump, "CompareMaskUnordLeF32 exec_lo, v6, v6"),
|
Check(Common::ContainsStr(result.ir_dump, "CompareMaskUnordLeF32 vcc_lo, v6, v6"),
|
||||||
"VOPC unordered-less-equal compare-and-mask did not lower to shared IR");
|
"VOPC unordered-less-equal compare-and-mask did not lower to shared IR");
|
||||||
Check(Common::ContainsStr(result.ir_dump, "CompareMaskUnordGtF32 exec_lo, v6, v6"),
|
Check(Common::ContainsStr(result.ir_dump, "CompareMaskUnordGtF32 vcc_lo, v6, v6"),
|
||||||
"VOPC unordered-greater compare-and-mask did not lower to shared IR");
|
"VOPC unordered-greater compare-and-mask did not lower to shared IR");
|
||||||
Check(Common::ContainsStr(result.ir_dump, "CompareMaskUnordNeF32 exec_lo, v6, v6"),
|
Check(Common::ContainsStr(result.ir_dump, "CompareMaskUnordNeF32 vcc_lo, v6, v6"),
|
||||||
"VOPC unordered-not-equal compare-and-mask did not lower to shared IR");
|
"VOPC unordered-not-equal compare-and-mask did not lower to shared IR");
|
||||||
Check(Common::ContainsStr(result.ir_dump, "CompareMaskUnordGeF32 exec_lo, v6, v6"),
|
Check(Common::ContainsStr(result.ir_dump, "CompareMaskUnordGeF32 vcc_lo, v6, v6"),
|
||||||
"VOPC unordered-greater-equal compare-and-mask did not lower to shared IR");
|
"VOPC unordered-greater-equal compare-and-mask did not lower to shared IR");
|
||||||
Check(Common::ContainsStr(result.ir_dump, "CompareFalse vcc_lo, v5, v5"),
|
Check(Common::ContainsStr(result.ir_dump, "CompareFalse vcc_lo, v5, v5"),
|
||||||
"VOPC integer false compare did not lower to shared IR");
|
"VOPC integer false compare did not lower to shared IR");
|
||||||
@@ -2687,8 +2691,9 @@ void TestNewShaderRecompilerSignedCompareAlu() {
|
|||||||
"signed halfword greater-or-equal compare did not lower to IR");
|
"signed halfword greater-or-equal compare did not lower to IR");
|
||||||
Check(Common::ContainsStr(result.ir_dump, "CompareLtU16"),
|
Check(Common::ContainsStr(result.ir_dump, "CompareLtU16"),
|
||||||
"unsigned halfword less-than compare did not lower to IR");
|
"unsigned halfword less-than compare did not lower to IR");
|
||||||
Check(Common::ContainsStr(result.ir_dump, "CompareMaskGtI32 exec_lo"),
|
Check(Common::ContainsStr(result.ir_dump, "CompareMaskGtI32 vcc_lo") &&
|
||||||
"signed compare-and-mask did not lower to exec mask IR");
|
Common::ContainsStr(result.ir_dump, "MoveU32 exec_lo, vcc_lo"),
|
||||||
|
"signed compare-and-mask did not lower to VCC plus EXEC IR");
|
||||||
Check(SpirvContainsOpcode(result.spirv, 173), "SPIR-V binary does not contain OpSGreaterThan");
|
Check(SpirvContainsOpcode(result.spirv, 173), "SPIR-V binary does not contain OpSGreaterThan");
|
||||||
Check(SpirvContainsOpcode(result.spirv, 177), "SPIR-V binary does not contain OpSLessThan");
|
Check(SpirvContainsOpcode(result.spirv, 177), "SPIR-V binary does not contain OpSLessThan");
|
||||||
Check(SpirvContainsOpcode(result.spirv, 202),
|
Check(SpirvContainsOpcode(result.spirv, 202),
|
||||||
|
|||||||
Reference in New Issue
Block a user