diff --git a/src/graphics/host_gpu/renderer/shaders.cpp b/src/graphics/host_gpu/renderer/shaders.cpp index 4e62361..a01f639 100644 --- a/src/graphics/host_gpu/renderer/shaders.cpp +++ b/src/graphics/host_gpu/renderer/shaders.cpp @@ -22,7 +22,7 @@ namespace Libs::Graphics { -//IDK: maybe we can remove it? +// IDK: maybe we can remove it? constexpr uint32_t kTemporaryPs5BufferFormat121 = 121u; 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].offset = b.attr_offsets[ai]; - uint32_t attr_size = 4; - auto registers_num = vs_input_info->resources_dst[index].registers_num; - auto used_components = (vs_input_info->resource_fetch_components[index] > 0 - ? vs_input_info->resource_fetch_components[index] - : registers_num); + uint32_t attr_size = 4; + auto registers_num = vs_input_info->resources_dst[index].registers_num; + auto used_components = (vs_input_info->resource_fetch_components[index] > 0 + ? vs_input_info->resource_fetch_components[index] + : registers_num); + const auto raw_components = VertexRawComponentCount( + vs_input_info->resources[index].DstSelXYZW(), static_cast(registers_num)); GetInputFormat(vs_input_info->resources[index], &input_attr[index].format, &attr_size, - static_cast(used_components)); + raw_components); if (graphics_debug_dump_enabled()) { 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].SwizzleEnabled()); - auto log_unsupported_vertex_swizzle = [index, attr_size, registers_num]( - uint32_t swizzle, uint32_t expected) { - static bool logged = false; - if (!logged) { - LOGF( - "VertexInput: temporary: accepting unsupported dst swizzle at attr %" PRIu32 - " (attr_size=%" PRIu32 ", regs=%" PRIu32 ", swizzle=0x%03" PRIx32 - ", expected=0x%03" PRIx32 ")\n", - static_cast(index), attr_size, registers_num, swizzle, expected); - logged = true; + if (registers_num < 1 || registers_num > 4) { + EXIT("invalid vertex destination register count: %u\n", registers_num); + } + for (uint32_t component = 0; component < static_cast(registers_num); + component++) { + const auto selector = + GetDstSel(vs_input_info->resources[index].DstSelXYZW(), component); + if (DecodeVertexDstSelector(selector).kind == VertexDstSelectKind::Reserved) { + EXIT("unsupported reserved vertex dst selector: attr=%u component=%u " + "selector=%u\n", + static_cast(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"); } } } diff --git a/src/graphics/shader/recompiler/ShaderDecoder.h b/src/graphics/shader/recompiler/ShaderDecoder.h index a726aee..ab01cdf 100644 --- a/src/graphics/shader/recompiler/ShaderDecoder.h +++ b/src/graphics/shader/recompiler/ShaderDecoder.h @@ -639,6 +639,8 @@ struct Instruction { bool slc = false; bool idxen = 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; uint32_t branch_target = 0; struct { diff --git a/src/graphics/shader/recompiler/VectorAluOps.cpp b/src/graphics/shader/recompiler/VectorAluOps.cpp index f111656..ef45bbe 100644 --- a/src/graphics/shader/recompiler/VectorAluOps.cpp +++ b/src/graphics/shader/recompiler/VectorAluOps.cpp @@ -1103,9 +1103,8 @@ bool DecodeVopcSdwa(uint32_t pc, std::span code, uint32_t word_i !DecodeScalarSource(vsrc1 + (fields.s1 == 0u ? 256u : 0u), pc, &inst->src1, error)) { return false; } - if (IsVopcCompareExec(inst->opcode)) { - inst->dst.kind = OperandKind::ExecLo; - } else if (fields.sd == 0u) { + inst->compare_exec = IsVopcCompareExec(inst->opcode); + if (fields.sd == 0u) { inst->dst.kind = OperandKind::VccLo; } else if (!DecodeScalarDestination(fields.sdst, pc, &inst->dst, error)) { return false; @@ -1142,7 +1141,8 @@ bool DecodeVopcDpp(uint32_t pc, std::span code, uint32_t word_in !DecodeVectorGpr(vsrc1, &inst->src1, error)) { 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.absolute = ((modifier >> 21u) & 0x1u) != 0u; inst->src0.dpp = true; @@ -1532,7 +1532,8 @@ bool DecodeVopc(uint32_t pc, std::span code, uint32_t word_index inst->family = Family::VOPC; inst->opcode_id = opcode; inst->opcode = Lookup(VOPC_OPS, static_cast(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); switch (src0) { @@ -1618,9 +1619,8 @@ bool DecodeVop3(uint32_t pc, std::span code, uint32_t word_index return true; } bool dst_ok = true; - if (compare_exec) { - inst->dst.kind = OperandKind::ExecLo; - } else if (scalar_dst) { + inst->compare_exec = compare_exec; + if (scalar_dst) { // VOP3A uses VDST for VOPC and the scalar-destination lane-read opcodes. dst_ok = DecodeScalarDestination(vdst, pc, &inst->dst, error); } else { diff --git a/src/graphics/shader/recompiler/shaderIR/ShaderIR.cpp b/src/graphics/shader/recompiler/shaderIR/ShaderIR.cpp index c062f29..4977921 100644 --- a/src/graphics/shader/recompiler/shaderIR/ShaderIR.cpp +++ b/src/graphics/shader/recompiler/shaderIR/ShaderIR.cpp @@ -1165,6 +1165,22 @@ bool LowerImplemented(const Decoder::Instruction& decoded, BasicBlock* block, st } 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); } diff --git a/src/graphics/shader/recompiler/spirvEmitter/spirvEmitterModule.cpp b/src/graphics/shader/recompiler/spirvEmitter/spirvEmitterModule.cpp index ce75be3..f44d729 100644 --- a/src/graphics/shader/recompiler/spirvEmitter/spirvEmitterModule.cpp +++ b/src/graphics/shader/recompiler/spirvEmitter/spirvEmitterModule.cpp @@ -94,8 +94,12 @@ uint32_t VertexParameterComponentCount(const EmitterState& state, const InputBin input.location < ShaderVertexInputInfo::RES_MAX && input.location < static_cast(state.vertex_input_info->resources_num) && state.vertex_input_info->resources_dst[input.location].registers_num > 0) { - count = static_cast( + const auto& descriptor = state.vertex_input_info->resources[input.location]; + const auto dst_count = static_cast( 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); } diff --git a/src/graphics/shader/recompiler/spirvEmitter/spirvEmitterValues.cpp b/src/graphics/shader/recompiler/spirvEmitter/spirvEmitterValues.cpp index 44758a0..d8b7e6c 100644 --- a/src/graphics/shader/recompiler/spirvEmitter/spirvEmitterValues.cpp +++ b/src/graphics/shader/recompiler/spirvEmitter/spirvEmitterValues.cpp @@ -450,7 +450,25 @@ uint32_t EmitVertexParameterComponentU32(EmitterState* state, const InputBinding uint32_t component) { const auto count = VertexParameterComponentCount(*state, input); 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(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); } @@ -462,7 +480,7 @@ uint32_t EmitVertexParameterComponentU32(EmitterState* state, const InputBinding const auto pointer_type = VertexParameterScalarPointerType(*state, kind); const auto pointer = state->builder.AllocateId(); 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}); } diff --git a/src/graphics/shader/shader.h b/src/graphics/shader/shader.h index b0f9322..9868307 100644 --- a/src/graphics/shader/shader.h +++ b/src/graphics/shader/shader.h @@ -64,6 +64,41 @@ inline uint8_t GetDstSel(uint32_t swizzle, uint32_t channel) { 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(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(selection.source_component) + 1u; + count = count > source_count ? count : source_count; + } + } + return count; +} + struct ShaderVertexInputInfo { static constexpr int RES_MAX = 32; @@ -96,26 +131,26 @@ struct ShaderComputeInputInfo { }; struct ShaderPixelInputInfo { - uint32_t interpolator_settings[32] = {0}; - uint32_t input_num = 0; - uint32_t ps_system_input_base = 0; - uint8_t target_output_mode[8] = {}; - std::array target_export_mapping = {}; - uint32_t mrt_output_mask = 0; - uint32_t descriptor_set = 0; - bool ps_pos_x = false; - bool ps_pos_y = false; - bool ps_pos_xy = false; - bool ps_pos_z = false; - bool ps_pos_w = false; - bool ps_front_face = false; - bool ps_no_perspective = false; - bool ps_pixel_kill_enable = false; - bool ps_depth_export_enable = false; - bool ps_sample_mask_export_enable = false; - bool ps_early_z = false; - bool ps_execute_on_noop = false; - ShaderStageRuntime stage; + uint32_t interpolator_settings[32] = {0}; + uint32_t input_num = 0; + uint32_t ps_system_input_base = 0; + uint8_t target_output_mode[8] = {}; + std::array target_export_mapping = {}; + uint32_t mrt_output_mask = 0; + uint32_t descriptor_set = 0; + bool ps_pos_x = false; + bool ps_pos_y = false; + bool ps_pos_xy = false; + bool ps_pos_z = false; + bool ps_pos_w = false; + bool ps_front_face = false; + bool ps_no_perspective = false; + bool ps_pixel_kill_enable = false; + bool ps_depth_export_enable = false; + bool ps_sample_mask_export_enable = false; + bool ps_early_z = false; + bool ps_execute_on_noop = false; + ShaderStageRuntime stage; bool HasPositionInput() const { return ps_pos_x || ps_pos_y || ps_pos_z || ps_pos_w; } }; diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 0e4b967..17dfc81 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -5700,24 +5700,24 @@ TestCase VectorVop3FloatCompareNegSourceModifier() { {O::VMovB32, O::VCmpLtF32, O::BufferStoreDword, O::SEndpgm}}; } -TestCase VectorVop3CmpxWritesExecMask() { +TestCase VectorVop3CmpxWritesScalarDstAndExecMask() { using O = ShaderOpcode; std::vector code; - AppendVMovU32(&code, 0, 2); - AppendVMovU32(&code, 1, 1); - AppendVMovU32(&code, 2, 0); - AppendVMovU32(&code, 30, 0); - AppendVop3(&code, 0xd1, 5, Vgpr(0), Vgpr(1)); // v_cmpx_lt_u32, false - AppendVMovU32(&code, 2, 7); - AppendBufferStoreDword(&code, 2, 30); - AppendEnd(&code); + AppendVMovU32(&code, 0, 1); + AppendVMovU32(&code, 1, 1); + AppendVMovU32(&code, 2, 7); + AppendVMovU32(&code, 30, 0); + AppendVop3(&code, 0xd2, 5, Vgpr(0), Vgpr(1)); // v_cmpx_eq_u32 s[5:6], true + code.push_back(EncodeSop1(0x0a, 126, 5)); // s_wqm_b64 exec, s[5:6] + AppendBufferStoreDword(&code, 2, 30); + AppendEnd(&code); - return {"VectorVop3CmpxWritesExecMask", - code, - {0}, - {0}, - {O::VMovB32, O::VCmpxLtU32, O::BufferStoreDword, O::SEndpgm}}; + return {"VectorVop3CmpxWritesScalarDstAndExecMask", + code, + {0}, + {7}, + {O::VMovB32, O::VCmpxEqU32, O::SWqmB64, O::BufferStoreDword, O::SEndpgm}}; } TestCase VectorVopcSdwaCmpxWritesExecMask() { @@ -5741,6 +5741,28 @@ TestCase VectorVopcSdwaCmpxWritesExecMask() { {O::VMovB32, O::VCmpxLtU32, O::BufferStoreDword, O::SEndpgm}}; } +TestCase VectorVopcCmpxPreservesVccForWqm() { + using O = ShaderOpcode; + + std::vector 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() { using O = ShaderOpcode; @@ -8638,8 +8660,9 @@ std::vector MakeCases() { AddCase(Vop3CndmaskAllowsDataSourceModifier); AddCase(VectorCompareExecOps); AddCase(VectorVop3FloatCompareNegSourceModifier); - AddCase(VectorVop3CmpxWritesExecMask); + AddCase(VectorVop3CmpxWritesScalarDstAndExecMask); AddCase(VectorVopcSdwaCmpxWritesExecMask); + AddCase(VectorVopcCmpxPreservesVccForWqm); AddCase(VectorCompareInvertedMaskSelect); AddCase(BranchSelect); AddCase(SimpleLoop); diff --git a/tests/ShaderVertexMetadataTests.cpp b/tests/ShaderVertexMetadataTests.cpp index d6a710a..8951e69 100644 --- a/tests/ShaderVertexMetadataTests.cpp +++ b/tests/ShaderVertexMetadataTests.cpp @@ -9,85 +9,122 @@ namespace { using namespace Libs::Graphics; -void Check(bool value, const char* text) { - if (!value) { - std::fprintf(stderr, "ShaderVertexMetadataTests: failed: %s\n", text); - std::abort(); - } +void Check(bool value, const char *text) { + if (!value) { + std::fprintf(stderr, "ShaderVertexMetadataTests: failed: %s\n", text); + std::abort(); + } } struct Fixture { - std::array(AgcDirectResourceType::Last) + 1> offsets {}; - ShaderUserData user_data {}; - ShaderSemantic semantic {}; - ShaderMappedData mapped {}; + std::array(AgcDirectResourceType::Last) + 1> + offsets{}; + ShaderUserData user_data{}; + ShaderSemantic semantic{}; + ShaderMappedData mapped{}; - Fixture() { - offsets.fill(AGC_ILLEGAL_DIRECT_OFFSET); - offsets[static_cast(AgcDirectResourceType::PtrVertexBufferTable)] = 2; - offsets[static_cast(AgcDirectResourceType::PtrVertexAttribDescTable)] = 4; - user_data.direct_resource_offset = offsets.data(); - user_data.direct_resource_count = static_cast(offsets.size()); - mapped.user_data = &user_data; - mapped.input_semantics = &semantic; - mapped.num_input_semantics = 1; - } + Fixture() { + offsets.fill(AGC_ILLEGAL_DIRECT_OFFSET); + offsets[static_cast(AgcDirectResourceType::PtrVertexBufferTable)] = + 2; + offsets[static_cast( + AgcDirectResourceType::PtrVertexAttribDescTable)] = 4; + user_data.direct_resource_offset = offsets.data(); + user_data.direct_resource_count = static_cast(offsets.size()); + mapped.user_data = &user_data; + mapped.input_semantics = &semantic; + mapped.num_input_semantics = 1; + } }; -void CheckRejected(const ShaderMappedData& data, const char* text) { - ShaderVertexMetadata output; - output.vertex_buffer_reg = 37; - output.vertex_attrib_reg = 41; - output.input_semantics_count = 7; - std::string error; - Check(!ShaderReadVertexMetadata(data, 64, &output, &error), text); - Check(!error.empty(), "metadata rejection omitted its diagnostic"); - Check(output.vertex_buffer_reg == 37 && output.vertex_attrib_reg == 41 && - output.input_semantics_count == 7, - "metadata rejection changed the prior output"); +void CheckRejected(const ShaderMappedData &data, const char *text) { + ShaderVertexMetadata output; + output.vertex_buffer_reg = 37; + output.vertex_attrib_reg = 41; + output.input_semantics_count = 7; + std::string error; + Check(!ShaderReadVertexMetadata(data, 64, &output, &error), text); + Check(!error.empty(), "metadata rejection omitted its diagnostic"); + Check(output.vertex_buffer_reg == 37 && output.vertex_attrib_reg == 41 && + output.input_semantics_count == 7, + "metadata rejection changed the prior output"); } void TestValidAndInvalidMetadata() { - Fixture fixture; - ShaderVertexMetadata output; - std::string error; - Check(ShaderReadVertexMetadata(fixture.mapped, 64, &output, &error), - "valid AGC vertex metadata was rejected"); - Check(output.vertex_buffer_reg == 2 && output.vertex_attrib_reg == 4 && - output.input_semantics_count == 1, - "valid AGC vertex metadata was decoded incorrectly"); + Fixture fixture; + ShaderVertexMetadata output; + std::string error; + Check(ShaderReadVertexMetadata(fixture.mapped, 64, &output, &error), + "valid AGC vertex metadata was rejected"); + Check(output.vertex_buffer_reg == 2 && output.vertex_attrib_reg == 4 && + output.input_semantics_count == 1, + "valid AGC vertex metadata was decoded incorrectly"); - auto missing_header = fixture.mapped; - missing_header.user_data = nullptr; - CheckRejected(missing_header, "missing AGC user-data header was accepted"); + auto missing_header = fixture.mapped; + missing_header.user_data = nullptr; + CheckRejected(missing_header, "missing AGC user-data header was accepted"); - Fixture missing_offsets; - missing_offsets.user_data.direct_resource_offset = nullptr; - CheckRejected(missing_offsets.mapped, "missing direct-resource offsets were accepted"); + Fixture missing_offsets; + missing_offsets.user_data.direct_resource_offset = nullptr; + CheckRejected(missing_offsets.mapped, + "missing direct-resource offsets were accepted"); - Fixture excessive_resources; - excessive_resources.user_data.direct_resource_count = - static_cast(excessive_resources.offsets.size() + 1); - CheckRejected(excessive_resources.mapped, "excessive direct-resource count was accepted"); + Fixture excessive_resources; + excessive_resources.user_data.direct_resource_count = + static_cast(excessive_resources.offsets.size() + 1); + CheckRejected(excessive_resources.mapped, + "excessive direct-resource count was accepted"); - Fixture excessive_semantics; - excessive_semantics.mapped.num_input_semantics = ShaderVertexInputInfo::RES_MAX + 1; - CheckRejected(excessive_semantics.mapped, "excessive vertex semantic count was accepted"); + Fixture excessive_semantics; + excessive_semantics.mapped.num_input_semantics = + ShaderVertexInputInfo::RES_MAX + 1; + CheckRejected(excessive_semantics.mapped, + "excessive vertex semantic count was accepted"); - Fixture excessive_register; - excessive_register.offsets[ - static_cast(AgcDirectResourceType::PtrVertexBufferTable)] = 63; - CheckRejected(excessive_register.mapped, "out-of-domain vertex table SGPR was accepted"); + Fixture excessive_register; + excessive_register.offsets[static_cast( + AgcDirectResourceType::PtrVertexBufferTable)] = 63; + CheckRejected(excessive_register.mapped, + "out-of-domain vertex table SGPR was accepted"); - Fixture missing_semantics; - missing_semantics.mapped.input_semantics = nullptr; - CheckRejected(missing_semantics.mapped, "missing vertex semantic array was accepted"); + Fixture missing_semantics; + missing_semantics.mapped.input_semantics = nullptr; + 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 int main() { - TestValidAndInvalidMetadata(); - std::puts("ShaderVertexMetadataTests: all cases passed"); - return 0; + TestValidAndInvalidMetadata(); + TestVertexDstSelectors(); + std::puts("ShaderVertexMetadataTests: all cases passed"); + return 0; } diff --git a/tests/shaderCfgTests.cpp b/tests/shaderCfgTests.cpp index 3ac27dd..f00d2a7 100644 --- a/tests/shaderCfgTests.cpp +++ b/tests/shaderCfgTests.cpp @@ -1473,9 +1473,11 @@ void TestNewShaderRecompilerMoreAluFamilies() { "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"), "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") && - Common::ContainsStr(result.ir_dump, "CompareMaskLtU32 exec_lo"), - "new decoder did not route V_CMPX SDWA destination to exec"); + Check(Common::ContainsStr(result.decoded_dump, "v_cmpx_lt_u32 vcc_lo, v5.sdwa(sel=4") && + Common::ContainsStr(result.ir_dump, "CompareMaskLtU32 vcc_lo") && + 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"), "new decoder did not decode old-backed V_CMP_NLT_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"); Check(Common::ContainsStr(result.ir_dump, "CompareGtF32"), "VOPC float compare did not lower to IR"); - Check(Common::ContainsStr(result.ir_dump, "CompareMaskGtF32 exec_lo"), - "VOPC float compare-and-mask did not lower to exec mask IR"); - Check(Common::ContainsStr(result.ir_dump, "CompareMaskGtU32 exec_lo"), - "VOPC uint compare-and-mask did not lower to exec mask IR"); + Check(Common::ContainsStr(result.ir_dump, "CompareMaskGtF32 vcc_lo") && + Common::ContainsStr(result.ir_dump, "MoveU32 exec_lo, vcc_lo"), + "VOPC float compare-and-mask did not lower to VCC plus EXEC 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"), "VOPC false compare did not lower to shared IR"); 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"); Check(Common::ContainsStr(result.ir_dump, "CompareUnordGeF32 vcc_lo, v6, v6"), "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"); - 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"); - 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"); - 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"); - 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"); - 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"); Check(Common::ContainsStr(result.ir_dump, "CompareFalse vcc_lo, v5, v5"), "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"); Check(Common::ContainsStr(result.ir_dump, "CompareLtU16"), "unsigned halfword less-than compare did not lower to IR"); - Check(Common::ContainsStr(result.ir_dump, "CompareMaskGtI32 exec_lo"), - "signed compare-and-mask did not lower to exec mask IR"); + Check(Common::ContainsStr(result.ir_dump, "CompareMaskGtI32 vcc_lo") && + 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, 177), "SPIR-V binary does not contain OpSLessThan"); Check(SpirvContainsOpcode(result.spirv, 202),