mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-28 00:12:40 +00:00
Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d2413fc2eb | ||
|
|
96eb681b90 | ||
|
|
26cce9909a | ||
|
|
5af370c257 | ||
|
|
67d7db685a | ||
|
|
bb98f26433 | ||
|
|
cc44a43ebc | ||
|
|
2a18e66a4c |
No files matched your search
@@ -164,6 +164,10 @@ bool PlayGoHackEnabled() {
|
||||
return g_config->playgo_hack_enabled;
|
||||
}
|
||||
|
||||
bool SkipNoticeScreen() {
|
||||
return g_config->skip_notice_screen;
|
||||
}
|
||||
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
bool RedZoneProtectionEnabled() {
|
||||
return g_config->red_zone_protection_enabled;
|
||||
|
||||
@@ -76,6 +76,7 @@ struct ConfigOptions {
|
||||
bool tessellation_enabled = false;
|
||||
bool trophy_enabled = true;
|
||||
bool playgo_hack_enabled = false;
|
||||
bool skip_notice_screen = false;
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
bool red_zone_protection_enabled = false;
|
||||
#endif
|
||||
@@ -126,6 +127,7 @@ bool ReadbackLinearImagesEnabled();
|
||||
bool TessellationEnabled();
|
||||
bool TrophyEnabled();
|
||||
bool PlayGoHackEnabled();
|
||||
bool SkipNoticeScreen();
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
bool RedZoneProtectionEnabled();
|
||||
#endif
|
||||
|
||||
@@ -184,7 +184,6 @@ static HW::RenderControl DecodeRenderControl(uint32_t value) {
|
||||
KYTY_PM4_GET(value, DB_RENDER_CONTROL, COPY_STENCIL_TO_COLOR) != 0;
|
||||
r.copy_centroid = KYTY_PM4_GET(value, DB_RENDER_CONTROL, COPY_CENTROID) != 0;
|
||||
r.copy_sample = KYTY_PM4_GET(value, DB_RENDER_CONTROL, COPY_SAMPLE);
|
||||
EXIT_NOT_IMPLEMENTED(r.copy_depth_to_color || r.copy_stencil_to_color);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -195,6 +195,37 @@ std::mutex g_trophy_notification_mutex;
|
||||
std::deque<TrophyNotification> g_trophy_notifications;
|
||||
std::atomic<bool> g_trophy_notification_active {false};
|
||||
|
||||
std::vector<std::string_view> WrapTrophyTitle(std::string_view text, float font_size, float width) {
|
||||
std::vector<std::string_view> lines;
|
||||
auto* const font = ImGui::GetFont();
|
||||
for (;;) {
|
||||
const auto newline = text.find('\n');
|
||||
const auto paragraph = text.substr(0, newline);
|
||||
auto line = paragraph.substr(0, 0);
|
||||
for (auto start = paragraph.find_first_not_of(" \t\r"); start != std::string_view::npos;) {
|
||||
const auto end = std::min(paragraph.find_first_of(" \t\r", start), paragraph.size());
|
||||
const auto word = paragraph.substr(start, end - start);
|
||||
const std::string_view candidate(line.empty() ? word.begin() : line.begin(),
|
||||
word.end());
|
||||
// Keep an oversized word intact; the toast's clip rectangle handles overflow.
|
||||
if (!line.empty() && font->CalcTextSizeA(font_size, std::numeric_limits<float>::max(),
|
||||
0.0f, candidate.data(),
|
||||
candidate.data() + candidate.size()).x > width) {
|
||||
lines.push_back(line);
|
||||
line = word;
|
||||
} else {
|
||||
line = candidate;
|
||||
}
|
||||
start = paragraph.find_first_not_of(" \t\r", end);
|
||||
}
|
||||
lines.push_back(line);
|
||||
if (newline == std::string_view::npos) {
|
||||
return lines;
|
||||
}
|
||||
text.remove_prefix(newline + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void ClearInputEvents() {
|
||||
std::scoped_lock lock(g_input_mutex);
|
||||
g_input_events.clear();
|
||||
@@ -1101,7 +1132,26 @@ struct SystemOverlay::Impl {
|
||||
0.65f, 1.25f);
|
||||
const float width = std::max(
|
||||
0.0f, std::min(520.0f * screen_scale, static_cast<float>(frame_extent.width) - 32.0f));
|
||||
const float height = 104.0f * screen_scale;
|
||||
constexpr std::array GRADE_COLORS {
|
||||
IM_COL32(161, 225, 245, 255), IM_COL32(255, 207, 82, 255), IM_COL32(211, 220, 232, 255),
|
||||
IM_COL32(211, 142, 91, 255)};
|
||||
const int grade = trophy_notification->grade;
|
||||
const bool has_grade = grade > 0 && grade <= static_cast<int>(GRADE_COLORS.size());
|
||||
const float pad = 10.0f * screen_scale;
|
||||
const float base_height = 104.0f * screen_scale;
|
||||
const float icon_size = base_height - 2.0f * pad;
|
||||
const float text_offset = trophy_notification_image != nullptr
|
||||
? pad + icon_size + 18.0f * screen_scale
|
||||
: 20.0f * screen_scale;
|
||||
const float title_offset = text_offset + (has_grade ? 30.0f * screen_scale : 0.0f);
|
||||
const float title_size = 26.0f * screen_scale;
|
||||
const float line_height = 30.0f * screen_scale;
|
||||
const float text_padding = 16.0f * screen_scale;
|
||||
const auto title_lines = WrapTrophyTitle(trophy_notification->name, title_size,
|
||||
width - title_offset - text_padding);
|
||||
const float block_height =
|
||||
static_cast<float>(title_lines.size()) * line_height + 28.0f * screen_scale;
|
||||
const float height = std::max(base_height, block_height + 2.0f * text_padding);
|
||||
const float rounding = 14.0f * screen_scale;
|
||||
const float fade_in = std::clamp(age / 0.22f, 0.0f, 1.0f);
|
||||
const float fade_out = std::clamp((TOAST_LIFETIME - age) / 0.4f, 0.0f, 1.0f);
|
||||
@@ -1121,26 +1171,19 @@ struct SystemOverlay::Impl {
|
||||
draw->AddRect(top_left, bottom_right, fade(255, 255, 255, 28), rounding,
|
||||
ImDrawFlags_RoundCornersAll, 1.0f);
|
||||
|
||||
const float pad = 10.0f * screen_scale;
|
||||
const float icon_size = height - 2.0f * pad;
|
||||
float text_left = top_left.x + 20.0f * screen_scale;
|
||||
const float text_left = top_left.x + text_offset;
|
||||
if (trophy_notification_image != nullptr) {
|
||||
const ImVec2 icon_min {top_left.x + pad, top_left.y + pad};
|
||||
const ImVec2 icon_min {top_left.x + pad, top_left.y + (height - icon_size) * 0.5f};
|
||||
const ImVec2 icon_max {icon_min.x + icon_size, icon_min.y + icon_size};
|
||||
draw->AddImageRounded(trophy_notification_image->GetTexRef(), icon_min, icon_max,
|
||||
{0, 0}, {1, 1}, fade(255, 255, 255, 255), 8.0f * screen_scale);
|
||||
draw->AddRect(icon_min, icon_max, fade(255, 255, 255, 40), 8.0f * screen_scale,
|
||||
ImDrawFlags_RoundCornersAll, 1.0f);
|
||||
text_left = icon_max.x + 18.0f * screen_scale;
|
||||
}
|
||||
|
||||
constexpr std::array GRADE_COLORS {IM_COL32(161, 225, 245, 255), IM_COL32(255, 207, 82, 255),
|
||||
IM_COL32(211, 220, 232, 255),
|
||||
IM_COL32(211, 142, 91, 255)};
|
||||
const int grade = trophy_notification->grade;
|
||||
const float title_y = top_left.y + height * 0.5f - 25.0f * screen_scale;
|
||||
float title_x = text_left;
|
||||
if (grade > 0 && grade <= static_cast<int>(GRADE_COLORS.size())) {
|
||||
const float title_y = top_left.y + (height - block_height) * 0.5f;
|
||||
const float title_x = top_left.x + title_offset;
|
||||
if (has_grade) {
|
||||
// Small cup glyph tinted with the trophy grade.
|
||||
const ImU32 cup = ImGui::GetColorU32(GRADE_COLORS[grade - 1], alpha);
|
||||
const float u = screen_scale;
|
||||
@@ -1155,12 +1198,15 @@ struct SystemOverlay::Impl {
|
||||
draw->AddRectFilled({cx - 1.5f * u, cy + 2.0f * u}, {cx + 1.5f * u, cy + 7.0f * u}, cup);
|
||||
draw->AddRectFilled({cx - 5.0f * u, cy + 7.0f * u}, {cx + 5.0f * u, cy + 10.0f * u}, cup,
|
||||
1.5f * u);
|
||||
title_x += 30.0f * u;
|
||||
}
|
||||
draw->PushClipRect(top_left, {bottom_right.x - 16.0f * screen_scale, bottom_right.y}, true);
|
||||
draw->AddText(nullptr, 26.0f * screen_scale, {title_x, title_y},
|
||||
fade(255, 255, 255, 255), trophy_notification->name.c_str());
|
||||
draw->AddText(nullptr, 20.0f * screen_scale, {text_left, title_y + 38.0f * screen_scale},
|
||||
draw->PushClipRect(top_left, {bottom_right.x - text_padding, bottom_right.y}, true);
|
||||
float line_y = title_y;
|
||||
for (const auto line : title_lines) {
|
||||
draw->AddText(nullptr, title_size, {title_x, line_y}, fade(255, 255, 255, 255),
|
||||
line.data(), line.data() + line.size());
|
||||
line_y += line_height;
|
||||
}
|
||||
draw->AddText(nullptr, 20.0f * screen_scale, {text_left, line_y + 8.0f * screen_scale},
|
||||
fade(176, 180, 192, 255), "Trophy earned!");
|
||||
draw->PopClipRect();
|
||||
}
|
||||
|
||||
@@ -716,18 +716,38 @@ uint32_t FormattedLoad(ValueEmitContext& ctx, const IR::Inst& inst, const IR::Me
|
||||
return LoadWordPrepared(ctx, inst, RebaseRawComponent(mem, 0u), resource);
|
||||
}
|
||||
const auto plan = PrepareFormattedMemory(ctx, inst, mem, resource, info);
|
||||
return EmitValueOrDefaultIfCondition(
|
||||
const auto value = EmitValueOrDefaultIfCondition(
|
||||
ctx.state, plan.in_bounds, TypeU32(ctx.state),
|
||||
FormattedOutOfBoundsValue(ctx, mem, plan, 1u),
|
||||
[&]() { return LoadFormattedInBounds(ctx, mem, plan, 0u); });
|
||||
if (mem.data_bits != 16u) {
|
||||
return value;
|
||||
}
|
||||
if (info.type == Format::ComponentType::Uint) {
|
||||
return EmitUMin32(ctx.state, value, ConstantU32(ctx.state, 0xffffu));
|
||||
}
|
||||
if (info.type == Format::ComponentType::Sint) {
|
||||
const auto lower = EmitSMax32(ctx.state, value, ConstantU32(ctx.state, 0xffff8000u));
|
||||
return EmitSMin32(ctx.state, lower, ConstantU32(ctx.state, 0x7fffu));
|
||||
}
|
||||
return EmitConvertF16F32(ctx.state, EmitBitCastF32U32(ctx.state, value));
|
||||
});
|
||||
}
|
||||
|
||||
uint32_t EncodeFormattedStoreComponent(ValueEmitContext& ctx,
|
||||
const Format::BufferFormatInfo& info,
|
||||
uint32_t component, uint32_t data) {
|
||||
uint32_t component, uint32_t data, uint32_t data_bits) {
|
||||
auto& state = ctx.state;
|
||||
const auto bits = info.component_bits[component];
|
||||
if (data_bits == 16u) {
|
||||
if (info.type == Format::ComponentType::Uint) {
|
||||
data = EmitBitFieldUExtract(state, data, ConstantU32(state, 0), ConstantU32(state, 16));
|
||||
} else if (info.type == Format::ComponentType::Sint) {
|
||||
data = EmitBitFieldSExtract(state, data, ConstantU32(state, 0), ConstantU32(state, 16));
|
||||
} else {
|
||||
data = EmitBitCastU32F32(state, EmitF16BitsToF32(state, data));
|
||||
}
|
||||
}
|
||||
const bool normalized = info.type == Format::ComponentType::Unorm ||
|
||||
info.type == Format::ComponentType::Snorm;
|
||||
const bool scaled = info.type == Format::ComponentType::Uscaled ||
|
||||
@@ -779,7 +799,7 @@ void StoreFormattedPrepared(ValueEmitContext& ctx, const IR::Inst& inst,
|
||||
value, data, component);
|
||||
}
|
||||
}
|
||||
value = EncodeFormattedStoreComponent(ctx, info, component, value);
|
||||
value = EncodeFormattedStoreComponent(ctx, info, component, value, mem.data_bits);
|
||||
const auto bits = info.component_bits[component];
|
||||
if (info.packed_bitfield) {
|
||||
packed = EmitBitFieldInsert(
|
||||
|
||||
@@ -63,6 +63,8 @@ constexpr MemoryOpcodeInfo MUBUF_OPCODE_LIST[] = {
|
||||
{0x50u, Opcode::BUFFER_ATOMIC_SWAP_X2, 2, 32},
|
||||
{0x59u, Opcode::BUFFER_ATOMIC_AND_X2, 2, 32},
|
||||
{0x5au, Opcode::BUFFER_ATOMIC_OR_X2, 2, 32},
|
||||
{0x80u, Opcode::BUFFER_LOAD_FORMAT_D16_X, 1, 16, false, false, true},
|
||||
{0x84u, Opcode::BUFFER_STORE_FORMAT_D16_X, 1, 16, false, false, true},
|
||||
};
|
||||
|
||||
constexpr MemoryOpcodeInfo MTBUF_OPCODE_LIST[] = {
|
||||
@@ -296,6 +298,9 @@ 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_X) {
|
||||
inst.dst.sdwa_sel = 4u;
|
||||
}
|
||||
DecodeVectorGpr(vaddr, inst.src0);
|
||||
DecodeScalarSource(srsrc * 4u, pc, inst.src1);
|
||||
DecodeScalarSource(soffset, pc, inst.src2);
|
||||
|
||||
@@ -618,10 +618,12 @@ std::string InstructionToString(const Instruction& inst) {
|
||||
case Opcode::BUFFER_LOAD_FORMAT_XY:
|
||||
case Opcode::BUFFER_LOAD_FORMAT_XYZ:
|
||||
case Opcode::BUFFER_LOAD_FORMAT_XYZW:
|
||||
case Opcode::BUFFER_LOAD_FORMAT_D16_X:
|
||||
case Opcode::BUFFER_STORE_FORMAT_X:
|
||||
case Opcode::BUFFER_STORE_FORMAT_XY:
|
||||
case Opcode::BUFFER_STORE_FORMAT_XYZ:
|
||||
case Opcode::BUFFER_STORE_FORMAT_XYZW:
|
||||
case Opcode::BUFFER_STORE_FORMAT_D16_X:
|
||||
case Opcode::BUFFER_LOAD_UBYTE:
|
||||
case Opcode::BUFFER_LOAD_USHORT:
|
||||
case Opcode::BUFFER_LOAD_DWORD:
|
||||
|
||||
@@ -269,6 +269,7 @@ enum class Opcode {
|
||||
V_MIN3_I32,
|
||||
V_MIN3_U32,
|
||||
V_MIN3_F16,
|
||||
V_MIN3_U16,
|
||||
V_MAX3_F32,
|
||||
V_MAX3_I32,
|
||||
V_MAX3_U32,
|
||||
@@ -455,19 +456,34 @@ enum class Opcode {
|
||||
V_CMP_NE_U32,
|
||||
V_CMP_GE_U32,
|
||||
V_CMP_T_U32,
|
||||
V_CMP_F_I64,
|
||||
V_CMP_EQ_I64,
|
||||
V_CMP_LT_I64,
|
||||
V_CMP_LE_I64,
|
||||
V_CMP_NE_I64,
|
||||
V_CMP_T_I64,
|
||||
V_CMP_F_U64,
|
||||
V_CMP_LT_U64,
|
||||
V_CMP_EQ_U64,
|
||||
V_CMP_LE_U64,
|
||||
V_CMP_GT_U64,
|
||||
V_CMP_NE_U64,
|
||||
V_CMP_GE_U64,
|
||||
V_CMP_T_U64,
|
||||
V_CMPX_F_I64,
|
||||
V_CMPX_LT_I64,
|
||||
V_CMPX_EQ_I64,
|
||||
V_CMPX_LE_I64,
|
||||
V_CMPX_NE_I64,
|
||||
V_CMPX_T_I64,
|
||||
V_CMPX_F_U64,
|
||||
V_CMPX_LT_U64,
|
||||
V_CMPX_EQ_U64,
|
||||
V_CMPX_LE_U64,
|
||||
V_CMPX_GT_U64,
|
||||
V_CMPX_NE_U64,
|
||||
V_CMPX_GE_U64,
|
||||
V_CMPX_T_U64,
|
||||
V_CMPX_LT_U32,
|
||||
V_CMPX_EQ_U32,
|
||||
V_CMPX_LE_U32,
|
||||
@@ -490,10 +506,12 @@ enum class Opcode {
|
||||
BUFFER_LOAD_FORMAT_XY,
|
||||
BUFFER_LOAD_FORMAT_XYZ,
|
||||
BUFFER_LOAD_FORMAT_XYZW,
|
||||
BUFFER_LOAD_FORMAT_D16_X,
|
||||
BUFFER_STORE_FORMAT_X,
|
||||
BUFFER_STORE_FORMAT_XY,
|
||||
BUFFER_STORE_FORMAT_XYZ,
|
||||
BUFFER_STORE_FORMAT_XYZW,
|
||||
BUFFER_STORE_FORMAT_D16_X,
|
||||
BUFFER_LOAD_UBYTE,
|
||||
BUFFER_LOAD_SBYTE,
|
||||
BUFFER_LOAD_USHORT,
|
||||
|
||||
@@ -265,10 +265,18 @@ constexpr VopcOpcodeInfo VOPC_OPCODE_LIST[] = {
|
||||
{0xc7u, Opcode::V_CMP_T_U32}, {0xa2u, Opcode::V_CMP_EQ_I64, false},
|
||||
{0xa1u, Opcode::V_CMP_LT_I64, false}, {0xa3u, Opcode::V_CMP_LE_I64, false},
|
||||
{0xa5u, Opcode::V_CMP_NE_I64, false},
|
||||
{0xa0u, Opcode::V_CMP_F_I64, false}, {0xa7u, Opcode::V_CMP_T_I64, false},
|
||||
{0xb0u, Opcode::V_CMPX_F_I64, false}, {0xb1u, Opcode::V_CMPX_LT_I64, false},
|
||||
{0xb2u, Opcode::V_CMPX_EQ_I64, false}, {0xb3u, Opcode::V_CMPX_LE_I64, false},
|
||||
{0xb7u, Opcode::V_CMPX_T_I64, false},
|
||||
{0xb5u, Opcode::V_CMPX_NE_I64, false}, {0xd1u, Opcode::V_CMPX_LT_U32},
|
||||
{0xd2u, Opcode::V_CMPX_EQ_U32}, {0xd3u, Opcode::V_CMPX_LE_U32},
|
||||
{0xd4u, Opcode::V_CMPX_GT_U32}, {0xd5u, Opcode::V_CMPX_NE_U32},
|
||||
{0xd6u, Opcode::V_CMPX_GE_U32}, {0xe1u, Opcode::V_CMP_LT_U64, false},
|
||||
{0xe0u, Opcode::V_CMP_F_U64, false}, {0xe7u, Opcode::V_CMP_T_U64, false},
|
||||
{0xf0u, Opcode::V_CMPX_F_U64, false}, {0xf1u, Opcode::V_CMPX_LT_U64, false},
|
||||
{0xf2u, Opcode::V_CMPX_EQ_U64, false}, {0xf4u, Opcode::V_CMPX_GT_U64, false},
|
||||
{0xf6u, Opcode::V_CMPX_GE_U64, false}, {0xf7u, Opcode::V_CMPX_T_U64, false},
|
||||
{0xe2u, Opcode::V_CMP_EQ_U64, false}, {0xe3u, Opcode::V_CMP_LE_U64, false},
|
||||
{0xe4u, Opcode::V_CMP_GT_U64, false}, {0xe5u, Opcode::V_CMP_NE_U64, false},
|
||||
{0xe6u, Opcode::V_CMP_GE_U64, false}, {0xf3u, Opcode::V_CMPX_LE_U64, false},
|
||||
@@ -310,6 +318,7 @@ constexpr OpcodeMap VOP3_OPCODE_LIST[] = {
|
||||
{0x152u, Opcode::V_MIN3_I32},
|
||||
{0x153u, Opcode::V_MIN3_U32},
|
||||
{0x351u, Opcode::V_MIN3_F16},
|
||||
{0x353u, Opcode::V_MIN3_U16},
|
||||
{0x154u, Opcode::V_MAX3_F32},
|
||||
{0x155u, Opcode::V_MAX3_I32},
|
||||
{0x156u, Opcode::V_MAX3_U32},
|
||||
@@ -460,8 +469,9 @@ bool IsNativeVop3F16TernaryOpcode(Opcode opcode) {
|
||||
opcode == Opcode::V_MED3_F16 || opcode == Opcode::V_FMA_F16;
|
||||
}
|
||||
|
||||
bool IsNativeVop3I16TernaryOpcode(Opcode opcode) {
|
||||
return opcode == Opcode::V_MED3_I16 || opcode == Opcode::V_MAD_I16;
|
||||
bool IsNativeVop3B16TernaryOpcode(Opcode opcode) {
|
||||
return opcode == Opcode::V_MIN3_U16 || opcode == Opcode::V_MED3_I16 ||
|
||||
opcode == Opcode::V_MAD_I16;
|
||||
}
|
||||
|
||||
bool IsNativeVop3B16BinaryOpcode(Opcode opcode) {
|
||||
@@ -1367,7 +1377,7 @@ void ApplyNativeVop3TernaryModifiers(Instruction& inst, uint32_t op_sel, uint32_
|
||||
inst.dst.sdwa_sel = ((op_sel & 0x8u) != 0) ? 5u : 4u;
|
||||
}
|
||||
|
||||
void ApplyNativeVop3I16TernarySelectors(Instruction& inst, uint32_t op_sel) {
|
||||
void ApplyNativeVop3B16TernarySelectors(Instruction& inst, uint32_t op_sel) {
|
||||
Operand* sources[] = {&inst.src0, &inst.src1, &inst.src2};
|
||||
for (uint32_t i = 0; i < 3u; i++) {
|
||||
sources[i]->op_sel = ((op_sel >> i) & 1u) != 0;
|
||||
@@ -1486,7 +1496,7 @@ bool HasUnsupportedNativeVop3Modifiers(Opcode opcode, bool permlane, bool mad_mi
|
||||
// Clamp is applied by WriteF16 for every f16 ternary; omod only for V_FMA_F16.
|
||||
return opcode != Opcode::V_FMA_F16 && omod != 0u;
|
||||
}
|
||||
if (IsNativeVop3I16TernaryOpcode(opcode)) {
|
||||
if (IsNativeVop3B16TernaryOpcode(opcode)) {
|
||||
return abs != 0u || (clamp != 0u && !clamp_modifier) || omod != 0u || neg != 0u;
|
||||
}
|
||||
if (IsNativeVop3B16BinaryOpcode(opcode)) {
|
||||
@@ -1555,9 +1565,20 @@ bool IsVopcCompareExec(Opcode opcode) {
|
||||
case Opcode::V_CMPX_GT_U32:
|
||||
case Opcode::V_CMPX_NE_U32:
|
||||
case Opcode::V_CMPX_GE_U32:
|
||||
case Opcode::V_CMPX_F_I64:
|
||||
case Opcode::V_CMPX_LT_I64:
|
||||
case Opcode::V_CMPX_EQ_I64:
|
||||
case Opcode::V_CMPX_LE_I64:
|
||||
case Opcode::V_CMPX_NE_I64:
|
||||
case Opcode::V_CMPX_T_I64:
|
||||
case Opcode::V_CMPX_F_U64:
|
||||
case Opcode::V_CMPX_LT_U64:
|
||||
case Opcode::V_CMPX_EQ_U64:
|
||||
case Opcode::V_CMPX_LE_U64:
|
||||
case Opcode::V_CMPX_GT_U64:
|
||||
case Opcode::V_CMPX_NE_U64:
|
||||
case Opcode::V_CMPX_GE_U64:
|
||||
case Opcode::V_CMPX_T_U64:
|
||||
case Opcode::V_CMPX_LT_I16:
|
||||
case Opcode::V_CMPX_EQ_I16:
|
||||
case Opcode::V_CMPX_LE_I16:
|
||||
@@ -1712,7 +1733,7 @@ void DecodeVop3(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
const bool vop3b_uses_sdst = carry_in_out || vop3b_carry_out || vop3b_mad_u64;
|
||||
const bool mad_mix = false;
|
||||
const bool f16_ternary = IsNativeVop3F16TernaryOpcode(inst.opcode);
|
||||
const bool i16_ternary = IsNativeVop3I16TernaryOpcode(inst.opcode);
|
||||
const bool b16_ternary = IsNativeVop3B16TernaryOpcode(inst.opcode);
|
||||
const bool b16_binary = IsNativeVop3B16BinaryOpcode(inst.opcode);
|
||||
const bool pack_b32_f16 = inst.opcode == Opcode::V_PACK_B32_F16;
|
||||
const bool permlane = IsPermlaneOpcode(inst.opcode);
|
||||
@@ -1823,8 +1844,8 @@ void DecodeVop3(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
||||
ApplyNativeVop3TernaryModifiers(inst, op_sel, abs, neg);
|
||||
} else if (f16_ternary) {
|
||||
ApplyNativeVop3TernaryModifiers(inst, op_sel, abs, neg);
|
||||
} else if (i16_ternary) {
|
||||
ApplyNativeVop3I16TernarySelectors(inst, op_sel);
|
||||
} else if (b16_ternary) {
|
||||
ApplyNativeVop3B16TernarySelectors(inst, op_sel);
|
||||
} else if (b16_binary) {
|
||||
ApplyNativeVop3B16BinaryModifiers(inst, op_sel);
|
||||
} else if (pack_b32_f16) {
|
||||
|
||||
@@ -31,6 +31,7 @@ void Translator::EmitIntegerCompare(const Decoder::Instruction& inst, IR::ValueO
|
||||
const bool signed_64 = opcode == IR::ValueOpcode::SLessThan64 ||
|
||||
opcode == IR::ValueOpcode::SLessThanEqual64 ||
|
||||
inst.opcode == Decoder::Opcode::V_CMP_EQ_I64 ||
|
||||
inst.opcode == Decoder::Opcode::V_CMPX_EQ_I64 ||
|
||||
inst.opcode == Decoder::Opcode::V_CMP_NE_I64 ||
|
||||
inst.opcode == Decoder::Opcode::V_CMPX_NE_I64;
|
||||
const auto read = [&](const Decoder::Operand& operand) {
|
||||
|
||||
@@ -31,11 +31,12 @@ void Translator::V_MAD_I16(const Decoder::Instruction& inst) {
|
||||
Write16Bits(DestinationOperand(inst), ir.BitwiseAnd(result, IR::U32(IR::Value(0xffffu))));
|
||||
}
|
||||
|
||||
void Translator::V_MED3_I16(const Decoder::Instruction& inst) {
|
||||
void Translator::Integer16Ternary(const Decoder::Instruction& inst, IR::ValueOpcode opcode,
|
||||
bool sign) {
|
||||
const auto result = IR::U32(ir.Emit(
|
||||
IR::ValueOpcode::SMedTri32, {ReadU16AsU32(inst.src0, true), ReadU16AsU32(inst.src1, true),
|
||||
ReadU16AsU32(inst.src2, true)}));
|
||||
Write16Bits(DestinationOperand(inst), ir.BitwiseAnd(result, IR::U32(IR::Value(0xffffu))));
|
||||
opcode, {ReadU16AsU32(inst.src0, sign), ReadU16AsU32(inst.src1, sign),
|
||||
ReadU16AsU32(inst.src2, sign)}));
|
||||
Write16Bits(DestinationOperand(inst), result);
|
||||
}
|
||||
|
||||
void Translator::PackedInteger16Shift(const Decoder::Instruction& inst, IR::ValueOpcode opcode,
|
||||
|
||||
@@ -187,7 +187,7 @@ Decoder::Operand MemorySourceAt(const Decoder::Instruction& decoded, uint32_t in
|
||||
if (decoded.family == Decoder::Family::MUBUF || decoded.family == Decoder::Family::MTBUF) {
|
||||
const bool store_or_atomic =
|
||||
(decoded.opcode >= Decoder::Opcode::BUFFER_STORE_FORMAT_X &&
|
||||
decoded.opcode <= Decoder::Opcode::BUFFER_STORE_FORMAT_XYZW) ||
|
||||
decoded.opcode <= Decoder::Opcode::BUFFER_STORE_FORMAT_D16_X) ||
|
||||
(decoded.opcode >= Decoder::Opcode::BUFFER_STORE_BYTE &&
|
||||
decoded.opcode <= Decoder::Opcode::BUFFER_STORE_DWORDX4) ||
|
||||
(decoded.opcode >= Decoder::Opcode::TBUFFER_STORE_FORMAT_X &&
|
||||
@@ -427,7 +427,7 @@ void Translator::S_LOAD(const Decoder::Instruction& inst, bool raw) {
|
||||
void Translator::BUFFER_LOAD(const Decoder::Instruction& inst) {
|
||||
const auto memory = MemoryInfoFromDecoded(inst);
|
||||
IR::ValueOpcode opcode;
|
||||
const auto bits = memory.data_bits;
|
||||
const auto bits = memory.formatted ? 32u : memory.data_bits;
|
||||
const auto sign = memory.data_signed;
|
||||
switch (bits) {
|
||||
case 8u: opcode = IR::ValueOpcode::LoadBufferU8; break;
|
||||
@@ -473,7 +473,8 @@ void Translator::BUFFER_STORE(const Decoder::Instruction& inst) {
|
||||
const auto data = ReadU32(data_src);
|
||||
IR::ValueOpcode opcode;
|
||||
IR::Value value;
|
||||
switch (memory.data_bits) {
|
||||
// Formatted stores carry packed VGPR bits; the format determines their conversion.
|
||||
switch (memory.formatted ? 32u : memory.data_bits) {
|
||||
case 8u:
|
||||
opcode = IR::ValueOpcode::StoreBufferU8;
|
||||
value = NarrowSubdword(data, 8u);
|
||||
@@ -980,6 +981,7 @@ void Translator::EmitMemory(const Decoder::Instruction& inst) {
|
||||
case Decoder::Opcode::BUFFER_LOAD_FORMAT_XY:
|
||||
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::TBUFFER_LOAD_FORMAT_X:
|
||||
case Decoder::Opcode::TBUFFER_LOAD_FORMAT_XY:
|
||||
case Decoder::Opcode::TBUFFER_LOAD_FORMAT_XYZ:
|
||||
@@ -995,6 +997,7 @@ void Translator::EmitMemory(const Decoder::Instruction& inst) {
|
||||
case Decoder::Opcode::BUFFER_STORE_FORMAT_XY:
|
||||
case Decoder::Opcode::BUFFER_STORE_FORMAT_XYZ:
|
||||
case Decoder::Opcode::BUFFER_STORE_FORMAT_XYZW:
|
||||
case Decoder::Opcode::BUFFER_STORE_FORMAT_D16_X:
|
||||
case Decoder::Opcode::TBUFFER_STORE_FORMAT_X:
|
||||
case Decoder::Opcode::TBUFFER_STORE_FORMAT_XY:
|
||||
case Decoder::Opcode::TBUFFER_STORE_FORMAT_XYZ:
|
||||
|
||||
@@ -889,9 +889,16 @@ void IncludeInstructionVectorRegisters(const Decoder::Instruction& inst, uint32_
|
||||
case Decoder::Opcode::V_CMP_GT_U64:
|
||||
case Decoder::Opcode::V_CMP_NE_U64:
|
||||
case Decoder::Opcode::V_CMP_GE_U64:
|
||||
case Decoder::Opcode::V_CMPX_LT_I64:
|
||||
case Decoder::Opcode::V_CMPX_EQ_I64:
|
||||
case Decoder::Opcode::V_CMPX_LE_I64:
|
||||
case Decoder::Opcode::V_CMPX_NE_I64:
|
||||
case Decoder::Opcode::V_CMPX_LT_U64:
|
||||
case Decoder::Opcode::V_CMPX_EQ_U64:
|
||||
case Decoder::Opcode::V_CMPX_LE_U64:
|
||||
case Decoder::Opcode::V_CMPX_GT_U64:
|
||||
case Decoder::Opcode::V_CMPX_NE_U64:
|
||||
case Decoder::Opcode::V_CMPX_GE_U64:
|
||||
include_vector(inst.src0, 2u);
|
||||
include_vector(inst.src1, 2u);
|
||||
break;
|
||||
|
||||
@@ -166,8 +166,8 @@ private:
|
||||
void FloatCube(const Decoder::Instruction& inst, uint32_t result_kind);
|
||||
void Integer16Shift(const Decoder::Instruction& inst, IR::ValueOpcode opcode, bool arithmetic);
|
||||
void Integer16Binary(const Decoder::Instruction& inst, IR::ValueOpcode opcode, bool sign);
|
||||
void Integer16Ternary(const Decoder::Instruction& inst, IR::ValueOpcode opcode, bool sign);
|
||||
void V_MAD_I16(const Decoder::Instruction& inst);
|
||||
void V_MED3_I16(const Decoder::Instruction& inst);
|
||||
void PackedInteger16Shift(const Decoder::Instruction& inst, IR::ValueOpcode opcode,
|
||||
bool arithmetic);
|
||||
void PackedInteger16Binary(const Decoder::Instruction& inst, IR::ValueOpcode opcode);
|
||||
|
||||
@@ -96,16 +96,34 @@ void Translator::EmitVector(const Decoder::Instruction& inst) {
|
||||
case O::V_CMPX_LE_I32:
|
||||
EmitIntegerCompare(inst, IR::ValueOpcode::SLessThanEqual32, IR::Type::U32, false, true);
|
||||
return;
|
||||
case O::V_CMP_F_I64:
|
||||
case O::V_CMP_F_U64: EmitCompareConstant(inst, false, false, false); return;
|
||||
case O::V_CMP_T_I64:
|
||||
case O::V_CMP_T_U64: EmitCompareConstant(inst, true, false, false); return;
|
||||
case O::V_CMPX_F_I64:
|
||||
case O::V_CMPX_F_U64: EmitCompareConstant(inst, false, false, true); return;
|
||||
case O::V_CMPX_T_I64:
|
||||
case O::V_CMPX_T_U64: EmitCompareConstant(inst, true, false, true); return;
|
||||
case O::V_CMP_EQ_I64:
|
||||
case O::V_CMP_EQ_U64:
|
||||
EmitIntegerCompare(inst, IR::ValueOpcode::IEqual64, IR::Type::U64, false, false);
|
||||
return;
|
||||
case O::V_CMPX_EQ_I64:
|
||||
case O::V_CMPX_EQ_U64:
|
||||
EmitIntegerCompare(inst, IR::ValueOpcode::IEqual64, IR::Type::U64, false, true);
|
||||
return;
|
||||
case O::V_CMP_LT_U64:
|
||||
EmitIntegerCompare(inst, IR::ValueOpcode::ULessThan64, IR::Type::U64, false, false);
|
||||
return;
|
||||
case O::V_CMPX_LT_U64:
|
||||
EmitIntegerCompare(inst, IR::ValueOpcode::ULessThan64, IR::Type::U64, false, true);
|
||||
return;
|
||||
case O::V_CMP_GT_U64:
|
||||
EmitIntegerCompare(inst, IR::ValueOpcode::UGreaterThan64, IR::Type::U64, false, false);
|
||||
return;
|
||||
case O::V_CMPX_GT_U64:
|
||||
EmitIntegerCompare(inst, IR::ValueOpcode::UGreaterThan64, IR::Type::U64, false, true);
|
||||
return;
|
||||
case O::V_CMP_NE_I64:
|
||||
case O::V_CMP_NE_U64:
|
||||
EmitIntegerCompare(inst, IR::ValueOpcode::INotEqual64, IR::Type::U64, false, false);
|
||||
@@ -118,9 +136,15 @@ void Translator::EmitVector(const Decoder::Instruction& inst) {
|
||||
case O::V_CMP_LT_I64:
|
||||
EmitIntegerCompare(inst, IR::ValueOpcode::SLessThan64, IR::Type::U64, false, false);
|
||||
return;
|
||||
case O::V_CMPX_LT_I64:
|
||||
EmitIntegerCompare(inst, IR::ValueOpcode::SLessThan64, IR::Type::U64, false, true);
|
||||
return;
|
||||
case O::V_CMP_LE_I64:
|
||||
EmitIntegerCompare(inst, IR::ValueOpcode::SLessThanEqual64, IR::Type::U64, false, false);
|
||||
return;
|
||||
case O::V_CMPX_LE_I64:
|
||||
EmitIntegerCompare(inst, IR::ValueOpcode::SLessThanEqual64, IR::Type::U64, false, true);
|
||||
return;
|
||||
case O::V_CMP_LE_U64:
|
||||
EmitIntegerCompare(inst, IR::ValueOpcode::ULessThanEqual64, IR::Type::U64, false, false);
|
||||
return;
|
||||
@@ -130,6 +154,9 @@ void Translator::EmitVector(const Decoder::Instruction& inst) {
|
||||
case O::V_CMP_GE_U64:
|
||||
EmitIntegerCompare(inst, IR::ValueOpcode::UGreaterThanEqual64, IR::Type::U64, false, false);
|
||||
return;
|
||||
case O::V_CMPX_GE_U64:
|
||||
EmitIntegerCompare(inst, IR::ValueOpcode::UGreaterThanEqual64, IR::Type::U64, false, true);
|
||||
return;
|
||||
|
||||
case O::V_CMP_EQ_U16:
|
||||
EmitInteger16Compare(inst, IR::ValueOpcode::IEqual32, false, false);
|
||||
@@ -395,7 +422,8 @@ void Translator::EmitVector(const Decoder::Instruction& inst) {
|
||||
case O::V_SUB_NC_I16: return Integer16Binary(inst, IR::ValueOpcode::ISub32, false);
|
||||
case O::V_MUL_LO_U16: return Integer16Binary(inst, IR::ValueOpcode::IMul32, false);
|
||||
case O::V_MAD_I16: return V_MAD_I16(inst);
|
||||
case O::V_MED3_I16: return V_MED3_I16(inst);
|
||||
case O::V_MED3_I16: return Integer16Ternary(inst, IR::ValueOpcode::SMedTri32, true);
|
||||
case O::V_MIN3_U16: return Integer16Ternary(inst, IR::ValueOpcode::UMinTri32, false);
|
||||
case O::V_MIN_I16: return Integer16Binary(inst, IR::ValueOpcode::SMin32, true);
|
||||
case O::V_MAX_I16: return Integer16Binary(inst, IR::ValueOpcode::SMax32, true);
|
||||
case O::V_MIN_U16: return Integer16Binary(inst, IR::ValueOpcode::UMin32, false);
|
||||
|
||||
@@ -179,6 +179,12 @@
|
||||
<property name="text"><string>Auto-hide cursor</string></property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="1">
|
||||
<widget class="QCheckBox" name="checkBox_skip_notice_screen">
|
||||
<property name="toolTip"><string>Skip startup logos and notices in supported games.</string></property>
|
||||
<property name="text"><string>Skip boot logos</string></property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -121,6 +121,7 @@ public:
|
||||
bool readback_linear_images = false;
|
||||
bool tessellation_enabled = false;
|
||||
bool trophy_enabled = true;
|
||||
bool skip_notice_screen = false;
|
||||
int vblank_frequency = 60;
|
||||
int console_language = DEFAULT_CONSOLE_LANGUAGE;
|
||||
bool vulkan_validation_enabled = false;
|
||||
@@ -154,6 +155,7 @@ public:
|
||||
readback_linear_images = other.readback_linear_images;
|
||||
tessellation_enabled = other.tessellation_enabled;
|
||||
trophy_enabled = other.trophy_enabled;
|
||||
skip_notice_screen = other.skip_notice_screen;
|
||||
vblank_frequency = other.vblank_frequency;
|
||||
console_language = other.console_language;
|
||||
vulkan_validation_enabled = other.vulkan_validation_enabled;
|
||||
|
||||
@@ -14,6 +14,7 @@ QVariantMap Configuration::GameSettings() const {
|
||||
KYTY_CFG_SET(readback_linear_images);
|
||||
KYTY_CFG_SET(tessellation_enabled);
|
||||
KYTY_CFG_SET(trophy_enabled);
|
||||
KYTY_CFG_SET(skip_notice_screen);
|
||||
KYTY_CFG_SET(vblank_frequency);
|
||||
KYTY_CFG_SET(console_language);
|
||||
KYTY_CFG_SET(vulkan_validation_enabled);
|
||||
@@ -57,6 +58,7 @@ void Configuration::ReadGameSettingsValues(const Settings& s) {
|
||||
KYTY_CFG_GET(readback_linear_images);
|
||||
KYTY_CFG_GET(tessellation_enabled);
|
||||
trophy_enabled = s.value("trophy_enabled", trophy_enabled).toBool();
|
||||
KYTY_CFG_GET(skip_notice_screen);
|
||||
vblank_frequency = s.value("vblank_frequency", vblank_frequency).toInt();
|
||||
console_language = s.value("console_language", console_language).toInt();
|
||||
if (console_language < 0 || console_language > MAX_CONSOLE_LANGUAGE) {
|
||||
|
||||
@@ -276,6 +276,7 @@ void ConfigurationEditDialog::Init(const Configuration& info) {
|
||||
m_ui->checkBox_hide_cursor->setChecked(info.hide_cursor_enabled);
|
||||
m_ui->checkBox_readback->setChecked(info.readback_linear_images);
|
||||
m_ui->checkBox_tessellation->setChecked(info.tessellation_enabled);
|
||||
m_ui->checkBox_skip_notice_screen->setChecked(info.skip_notice_screen);
|
||||
m_ui->checkBox_trophy_notifications->setChecked(info.trophy_enabled);
|
||||
m_ui->spinBox_vblank_frequency->setValue(info.vblank_frequency);
|
||||
m_ui->comboBox_console_language->clear();
|
||||
@@ -435,6 +436,7 @@ static void UpdateInfo(Configuration& info, Ui::ConfigurationEditDialog& ui, boo
|
||||
info.hide_cursor_enabled = ui.checkBox_hide_cursor->isChecked();
|
||||
info.readback_linear_images = ui.checkBox_readback->isChecked();
|
||||
info.tessellation_enabled = ui.checkBox_tessellation->isChecked();
|
||||
info.skip_notice_screen = ui.checkBox_skip_notice_screen->isChecked();
|
||||
info.trophy_enabled = ui.checkBox_trophy_notifications->isChecked();
|
||||
info.vblank_frequency = ui.spinBox_vblank_frequency->value();
|
||||
info.console_language = ui.comboBox_console_language->currentIndex();
|
||||
|
||||
@@ -246,6 +246,7 @@ static QStringList CreateEmulatorArgs(const Configuration& info) {
|
||||
}
|
||||
args << "--readback-linear-images" << BoolArg(info.readback_linear_images);
|
||||
args << "--trophy-notifications" << BoolArg(info.trophy_enabled);
|
||||
args << "--skip-notice-screen" << BoolArg(info.skip_notice_screen);
|
||||
if (info.tessellation_enabled) {
|
||||
args << "--tessellation";
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <QDateTime>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QFileInfo>
|
||||
#include <QGridLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QImage>
|
||||
#include <QLabel>
|
||||
@@ -18,6 +19,7 @@
|
||||
#include <QPainterPath>
|
||||
#include <QPixmap>
|
||||
#include <QProgressBar>
|
||||
#include <QPushButton>
|
||||
#include <QRegularExpression>
|
||||
#include <QSize>
|
||||
#include <QStringList>
|
||||
@@ -25,6 +27,8 @@
|
||||
#include <QTabWidget>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace {
|
||||
|
||||
static const QRegularExpression TrophyFilePattern(QStringLiteral("^trophy(\\d+)\\.ucp$"));
|
||||
@@ -54,17 +58,115 @@ static QString GradeToText(int grade) {
|
||||
}
|
||||
}
|
||||
|
||||
static QString TrophyTooltip(const Common::Trophies::Trophy& trophy) {
|
||||
QStringList lines {QString::fromStdString(trophy.name),
|
||||
QString::fromStdString(trophy.description),
|
||||
QObject::tr("Grade: %1").arg(GradeToText(trophy.grade))};
|
||||
if (trophy.hidden) {
|
||||
lines.append(QObject::tr("Hidden trophy"));
|
||||
QString TrophyName(int id, const Common::Trophies::Trophy& trophy, bool masked) {
|
||||
return masked ? QObject::tr("Hidden trophy")
|
||||
: trophy.name.empty() ? QObject::tr("Trophy %1").arg(id)
|
||||
: QString::fromStdString(trophy.name);
|
||||
}
|
||||
|
||||
QPixmap TrophyIcon(const Common::Trophies::Trophy& trophy, bool unlocked, bool masked, int size) {
|
||||
QPixmap pixmap;
|
||||
if (masked) {
|
||||
pixmap = QPixmap(QStringLiteral(":/icons/hidden-trophy.png"));
|
||||
} else if (pixmap.loadFromData(reinterpret_cast<const uchar*>(trophy.icon_png.data()),
|
||||
static_cast<uint>(trophy.icon_png.size())) &&
|
||||
!unlocked) {
|
||||
auto image = pixmap.toImage().convertToFormat(QImage::Format_ARGB32);
|
||||
for (int y = 0; y < image.height(); ++y) {
|
||||
auto* line = reinterpret_cast<QRgb*>(image.scanLine(y));
|
||||
for (int x = 0; x < image.width(); ++x) {
|
||||
const int gray = qGray(line[x]);
|
||||
line[x] = qRgba(gray, gray, gray, qAlpha(line[x]));
|
||||
}
|
||||
}
|
||||
pixmap = QPixmap::fromImage(image);
|
||||
}
|
||||
if (trophy.has_reward && !trophy.reward.empty()) {
|
||||
lines.append(QObject::tr("Reward: %1").arg(QString::fromStdString(trophy.reward)));
|
||||
return pixmap.isNull()
|
||||
? pixmap
|
||||
: pixmap.scaled(size, size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
}
|
||||
|
||||
QString TrophyEarnedDate(int id, const Common::Trophies::UnlockData& unlocks) {
|
||||
const auto found = unlocks.timestamps.find(id);
|
||||
if (found == unlocks.timestamps.end()) {
|
||||
return {};
|
||||
}
|
||||
return lines.join(QLatin1Char('\n'));
|
||||
return QDateTime::fromMSecsSinceEpoch(
|
||||
static_cast<qint64>((found->second - Common::Trophies::UnixEpochTick) / 1000))
|
||||
.toLocalTime()
|
||||
.toString(QStringLiteral("yyyy-MM-dd HH:mm:ss"));
|
||||
}
|
||||
|
||||
void ShowTrophyInspector(QWidget* parent, int id, const Common::Trophies::Trophy& trophy,
|
||||
bool unlocked, const QString& earned_date) {
|
||||
QDialog dialog(parent);
|
||||
dialog.setWindowTitle(QObject::tr("Trophy Details"));
|
||||
dialog.resize(640, 380);
|
||||
auto* layout = new QVBoxLayout(&dialog);
|
||||
auto* top = new QHBoxLayout;
|
||||
auto* icon = new QLabel(&dialog);
|
||||
icon->setFixedSize(148, 148);
|
||||
icon->setAlignment(Qt::AlignCenter);
|
||||
auto* text_box = new QVBoxLayout;
|
||||
auto* name = new QLabel(&dialog);
|
||||
name->setTextFormat(Qt::PlainText);
|
||||
name->setStyleSheet(QStringLiteral("font-size: 26px;"));
|
||||
name->setWordWrap(true);
|
||||
auto* subtitle = new QLabel(QObject::tr("Hidden trophy"), &dialog);
|
||||
subtitle->setStyleSheet(QStringLiteral("font-size: 16px;"));
|
||||
auto* reveal = new QPushButton(QObject::tr("Show Hidden Trophy"), &dialog);
|
||||
text_box->addWidget(name);
|
||||
text_box->addWidget(subtitle);
|
||||
text_box->addWidget(reveal, 0, Qt::AlignLeft);
|
||||
text_box->addStretch(1);
|
||||
top->addWidget(icon);
|
||||
top->addSpacing(16);
|
||||
top->addLayout(text_box, 1);
|
||||
layout->addLayout(top);
|
||||
|
||||
auto* grid = new QGridLayout;
|
||||
grid->setColumnStretch(1, 1);
|
||||
const auto make_row = [&](const QString& label, const QString& text = {}) {
|
||||
auto* key = new QLabel(label, &dialog);
|
||||
auto* value = new QLabel(text, &dialog);
|
||||
key->setStyleSheet(QStringLiteral("font-size: 17px;"));
|
||||
value->setStyleSheet(QStringLiteral("font-size: 17px;"));
|
||||
value->setTextFormat(Qt::PlainText);
|
||||
value->setWordWrap(true);
|
||||
const int row = grid->rowCount();
|
||||
grid->addWidget(key, row, 0);
|
||||
grid->addWidget(value, row, 1);
|
||||
return value;
|
||||
};
|
||||
auto* grade = make_row(QObject::tr("Grade"));
|
||||
make_row(QObject::tr("Status"), unlocked ? QObject::tr("Earned") : QObject::tr("Not earned"));
|
||||
make_row(QObject::tr("Earned date"),
|
||||
unlocked && !earned_date.isEmpty() ? earned_date : QStringLiteral("-"));
|
||||
auto* details = make_row(QObject::tr("Details"));
|
||||
auto* reward =
|
||||
trophy.has_reward && !trophy.reward.empty() ? make_row(QObject::tr("Reward")) : nullptr;
|
||||
layout->addLayout(grid);
|
||||
layout->addStretch(1);
|
||||
auto* buttons = new QDialogButtonBox(QDialogButtonBox::Close, &dialog);
|
||||
QObject::connect(buttons, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);
|
||||
layout->addWidget(buttons);
|
||||
|
||||
const auto refresh = [&](bool masked) {
|
||||
icon->setPixmap(TrophyIcon(trophy, unlocked, masked, 148));
|
||||
name->setText(TrophyName(id, trophy, masked));
|
||||
subtitle->setVisible(trophy.hidden && !masked);
|
||||
reveal->setVisible(masked);
|
||||
grade->setText(masked ? QObject::tr("Hidden trophy") : GradeToText(trophy.grade));
|
||||
details->setText(masked || trophy.description.empty()
|
||||
? QStringLiteral("-")
|
||||
: QString::fromStdString(trophy.description));
|
||||
if (reward != nullptr) {
|
||||
reward->setText(masked ? QStringLiteral("-") : QString::fromStdString(trophy.reward));
|
||||
}
|
||||
};
|
||||
QObject::connect(reveal, &QPushButton::clicked, &dialog, [&] { refresh(false); });
|
||||
refresh(trophy.hidden && !unlocked);
|
||||
dialog.exec();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -337,13 +439,13 @@ bool TrophyViewerDialog::LoadGame(const Configuration& info, const QString& runt
|
||||
}
|
||||
QStringList errors;
|
||||
for (const auto& file: trophy_files) {
|
||||
const auto package =
|
||||
auto package =
|
||||
Common::Trophies::LoadPackage(GameContent::ToPath(file), info.console_language);
|
||||
if (package.trophies.empty()) {
|
||||
errors.append(tr("Could not read trophy package %1.").arg(QFileInfo(file).fileName()));
|
||||
continue;
|
||||
}
|
||||
const auto unlocks = LoadUnlocks(info, runtime_directory, file);
|
||||
auto unlocks = LoadUnlocks(info, runtime_directory, file);
|
||||
const auto counts = Common::Trophies::GetProgress(package, unlocks);
|
||||
auto* page = new QWidget(m_tabs);
|
||||
auto* page_layout = new QVBoxLayout(page);
|
||||
@@ -395,8 +497,10 @@ bool TrophyViewerDialog::LoadGame(const Configuration& info, const QString& runt
|
||||
const auto hidden_locked = trophy.hidden && !trophy_unlocked;
|
||||
auto* item = new QListWidgetItem(list);
|
||||
item->setSizeHint(QSize(900, 96));
|
||||
item->setData(Qt::UserRole, id);
|
||||
|
||||
auto* card = new QWidget(list);
|
||||
card->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
card->setStyleSheet(
|
||||
QStringLiteral("QWidget#card { background: palette(base); border-radius: 12px; }"));
|
||||
card->setObjectName(QStringLiteral("card"));
|
||||
@@ -404,26 +508,12 @@ bool TrophyViewerDialog::LoadGame(const Configuration& info, const QString& runt
|
||||
auto* icon = new QLabel(card);
|
||||
icon->setFixedSize(76, 76);
|
||||
icon->setAlignment(Qt::AlignCenter);
|
||||
QPixmap pixmap;
|
||||
if (hidden_locked) {
|
||||
pixmap = QPixmap(QStringLiteral(":/icons/hidden-trophy.png"));
|
||||
} else if (pixmap.loadFromData(reinterpret_cast<const uchar*>(trophy.icon_png.data()),
|
||||
static_cast<uint>(trophy.icon_png.size())) &&
|
||||
!trophy_unlocked) {
|
||||
pixmap =
|
||||
QPixmap::fromImage(pixmap.toImage().convertToFormat(QImage::Format_Grayscale8));
|
||||
}
|
||||
if (!pixmap.isNull()) {
|
||||
icon->setPixmap(
|
||||
pixmap.scaled(76, 76, Qt::KeepAspectRatio, Qt::SmoothTransformation));
|
||||
}
|
||||
icon->setPixmap(TrophyIcon(trophy, trophy_unlocked, hidden_locked, 76));
|
||||
row->addWidget(icon);
|
||||
|
||||
auto* left = new QVBoxLayout;
|
||||
const auto name_text = hidden_locked ? tr("Hidden trophy")
|
||||
: trophy.name.empty() ? tr("Trophy %1").arg(id)
|
||||
: QString::fromStdString(trophy.name);
|
||||
auto* name = new QLabel(name_text, card);
|
||||
auto* left = new QVBoxLayout;
|
||||
auto* name = new QLabel(TrophyName(id, trophy, hidden_locked), card);
|
||||
name->setTextFormat(Qt::PlainText);
|
||||
name->setStyleSheet(QStringLiteral("font-size: 20px; font-weight: bold;"));
|
||||
left->addWidget(name);
|
||||
if (!hidden_locked) {
|
||||
@@ -440,15 +530,9 @@ bool TrophyViewerDialog::LoadGame(const Configuration& info, const QString& runt
|
||||
|
||||
row->addLayout(left, 2);
|
||||
|
||||
auto* right = new QVBoxLayout;
|
||||
QString date_text;
|
||||
if (const auto found = unlocks.timestamps.find(id); found != unlocks.timestamps.end()) {
|
||||
date_text = QDateTime::fromMSecsSinceEpoch(
|
||||
static_cast<qint64>(
|
||||
(found->second - Common::Trophies::UnixEpochTick) / 1000))
|
||||
.toLocalTime()
|
||||
.toString(QStringLiteral("yyyy-MM-dd HH:mm:ss"));
|
||||
} else if (trophy_unlocked) {
|
||||
auto* right = new QVBoxLayout;
|
||||
auto date_text = TrophyEarnedDate(id, unlocks);
|
||||
if (date_text.isEmpty() && trophy_unlocked) {
|
||||
date_text = tr("Earned");
|
||||
}
|
||||
auto* date = new QLabel(date_text, card);
|
||||
@@ -456,15 +540,22 @@ bool TrophyViewerDialog::LoadGame(const Configuration& info, const QString& runt
|
||||
date->setStyleSheet(QStringLiteral("font-size: 15px;"));
|
||||
auto* detail = new QLabel(
|
||||
hidden_locked ? QString {} : QString::fromStdString(trophy.description), card);
|
||||
detail->setTextFormat(Qt::PlainText);
|
||||
detail->setWordWrap(true);
|
||||
detail->setAlignment(Qt::AlignLeft | Qt::AlignTop);
|
||||
detail->setStyleSheet(QStringLiteral("font-size: 17px;"));
|
||||
right->addWidget(date);
|
||||
right->addWidget(detail, 1);
|
||||
row->addLayout(right, 3);
|
||||
card->setToolTip(hidden_locked ? name_text : TrophyTooltip(trophy));
|
||||
list->setItemWidget(item, card);
|
||||
}
|
||||
connect(list, &QListWidget::itemClicked, this,
|
||||
[this, trophies = std::move(package.trophies),
|
||||
unlocks = std::move(unlocks)](QListWidgetItem* item) {
|
||||
const int id = item->data(Qt::UserRole).toInt();
|
||||
ShowTrophyInspector(this, id, trophies.at(id), unlocks.unlocked.contains(id),
|
||||
TrophyEarnedDate(id, unlocks));
|
||||
});
|
||||
m_tabs->addTab(page, QString::fromStdString(package.title));
|
||||
}
|
||||
m_tabs->tabBar()->setVisible(m_tabs->count() > 1);
|
||||
|
||||
@@ -2388,6 +2388,12 @@ uint32_t* KYTY_SYSV_ABI AgcDcbSetShRegistersIndirect(CommandBuffer*
|
||||
return cmd;
|
||||
}
|
||||
|
||||
uint64_t KYTY_SYSV_ABI AgcDcbSetShRegistersIndirectGetSize() {
|
||||
PRINT_NAME();
|
||||
|
||||
return 5u * sizeof(uint32_t);
|
||||
}
|
||||
|
||||
uint32_t* KYTY_SYSV_ABI AgcDcbSetUcRegistersIndirect(CommandBuffer* buf,
|
||||
const volatile ShaderRegister* regs,
|
||||
uint32_t num_regs) {
|
||||
|
||||
@@ -137,6 +137,7 @@ uint32_t* KYTY_SYSV_ABI AgcDcbSetCxRegistersIndirect(CommandBuffer*
|
||||
uint32_t* KYTY_SYSV_ABI AgcDcbSetShRegistersIndirect(CommandBuffer* buf,
|
||||
const volatile ShaderRegister* regs,
|
||||
uint32_t num_regs);
|
||||
uint64_t KYTY_SYSV_ABI AgcDcbSetShRegistersIndirectGetSize();
|
||||
uint32_t* KYTY_SYSV_ABI AgcDcbSetUcRegistersIndirect(CommandBuffer* buf,
|
||||
const volatile ShaderRegister* regs,
|
||||
uint32_t num_regs);
|
||||
|
||||
@@ -95,6 +95,7 @@ LIB_DEFINE(InitAgcDriver_1) {
|
||||
LIB_FUNC("w4-d0n60hdo", Gen5::AgcDcbSetUcRegisterDirect);
|
||||
LIB_FUNC("ZvwO9euwYzc", Gen5::AgcDcbSetCxRegistersIndirect);
|
||||
LIB_FUNC("-HOOCn0JY48", Gen5::AgcDcbSetShRegistersIndirect);
|
||||
LIB_FUNC("nNlUtdDDvZ0", Gen5::AgcDcbSetShRegistersIndirectGetSize);
|
||||
LIB_FUNC("hvUfkUIQcOE", Gen5::AgcDcbSetUcRegistersIndirect);
|
||||
LIB_FUNC("GIIW2J37e70", Gen5::AgcDcbSetIndexSize);
|
||||
LIB_FUNC("l4fM9K-Lyks", Gen5::AgcDcbSetIndexBuffer);
|
||||
|
||||
@@ -206,7 +206,7 @@ static int KYTY_SYSV_ABI SystemServiceGetNoticeScreenSkipFlag(bool* value) {
|
||||
return SYSTEM_SERVICE_ERROR_PARAMETER;
|
||||
}
|
||||
|
||||
*value = false;
|
||||
*value = Config::SkipNoticeScreen();
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
@@ -87,6 +87,9 @@ static void PrintUsage() {
|
||||
::printf(
|
||||
" --trophy-notifications <true|false> Show trophy unlock toasts and play their sound.\n");
|
||||
::printf(" --playgo-hack Use the supplied PlayGo stub fallback.\n");
|
||||
::printf(
|
||||
" --skip-notice-screen <true|false> Skip startup logos and notices in supported games.\n"
|
||||
" Default: false.\n");
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
::printf(" --redzone Protect the guest SysV red zone.\n");
|
||||
#endif
|
||||
@@ -428,6 +431,11 @@ static bool ParseArgs(int argc, char* argv[], RunOptions& options, bool& show_he
|
||||
::printf("invalid boolean for %s: %s\n", arg.c_str(), value.c_str());
|
||||
return false;
|
||||
}
|
||||
} else if (arg == "--skip-notice-screen") {
|
||||
if (!ParseBool(value, options.config.skip_notice_screen)) {
|
||||
::printf("invalid boolean for %s: %s\n", arg.c_str(), value.c_str());
|
||||
return false;
|
||||
}
|
||||
} else if (arg == "--keymap") {
|
||||
const auto split = value.find('=');
|
||||
if (split == std::string::npos || split == 0 || split + 1 == value.size()) {
|
||||
|
||||
@@ -712,7 +712,7 @@ constexpr u32 EncodeVopcSdwa(u32 src0, u32 sdst = 0, u32 sd = 0,
|
||||
|
||||
constexpr u32 EncodeMubuf0(u32 opcode, u32 offset = 0, bool idxen = false,
|
||||
bool offen = true, bool glc = false) {
|
||||
return (0x38u << 26u) | ((opcode & 0x7fu) << 18u) |
|
||||
return (0x38u << 26u) | ((opcode & 0xffu) << 18u) |
|
||||
(offen ? (1u << 12u) : 0u) | (idxen ? (1u << 13u) : 0u) |
|
||||
(glc ? (1u << 14u) : 0u) | (offset & 0xfffu);
|
||||
}
|
||||
@@ -22148,6 +22148,65 @@ TestCase Vop3MadI16CapturedSelectorsAndSaturation() {
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase Vop3Min3U16CapturedAndSelectors() {
|
||||
using O = ShaderOpcode;
|
||||
struct MinCase {
|
||||
u32 lhs, rhs, third, expected;
|
||||
u32 selectors = 0;
|
||||
u32 dst = 17;
|
||||
bool active = true;
|
||||
};
|
||||
constexpr u32 a = 0x0009ea60u, b = 0x0007c350u, c = 0x00059c40u;
|
||||
const std::array<MinCase, 10> cases{{
|
||||
{a, b, c, 0x0005beefu, 14}, // Captured: min(60000, 7, 5) into v17.hi.
|
||||
{a, b, c, 0xa5a59c40u}, // Unsigned values above INT16_MAX.
|
||||
{a, b, c, 0xa5a50009u, 1},
|
||||
{a, b, c, 0xa5a50007u, 2},
|
||||
{a, b, c, 0xa5a50005u, 4},
|
||||
{a, b, c, 0x0005beefu, 15},
|
||||
{a, b, c, 0x9c40ea60u, 8, 4}, // Destination aliases source 0.
|
||||
{0xabcdffffu, 0xffff0000u, 0x98768000u, 0xa5a50000u},
|
||||
{0x0000ffffu, 0x7fffffffu, 0x8000ffffu, 0xa5a5ffffu},
|
||||
{a, b, c, 0xa5a5beefu, 14, 17, false},
|
||||
}};
|
||||
TestCase test;
|
||||
test.name = "Vop3Min3U16CapturedAndSelectors";
|
||||
for (const auto &entry : cases) {
|
||||
test.initial.insert(test.initial.end(), {entry.lhs, entry.rhs, entry.third});
|
||||
}
|
||||
test.expected = test.initial;
|
||||
auto &code = test.code;
|
||||
for (u32 i = 0; i < cases.size(); ++i) {
|
||||
const auto &entry = cases[i];
|
||||
u32 offset = i * 12u;
|
||||
for (u32 reg : {4u, 0u, 7u}) {
|
||||
AppendVMovU32(&code, 30, offset);
|
||||
AppendBufferLoadDword(&code, reg, 30); // Runtime inputs prevent folding.
|
||||
offset += 4u;
|
||||
}
|
||||
AppendVMovLiteral(&code, 17, 0xa5a5beefu);
|
||||
if (!entry.active) {
|
||||
code.push_back(EncodeSop1(0x04, 24, 126)); // Preserve the initial active lanes.
|
||||
code.push_back(EncodeSop1(0x04, 126, 128));
|
||||
}
|
||||
if (i == 0u) {
|
||||
code.insert(code.end(), {0xd7537011u, 0x041e0104u});
|
||||
} else {
|
||||
AppendVop3(&code, 0x353, entry.dst, Vgpr(4), Vgpr(0), Vgpr(7), 0,
|
||||
entry.selectors);
|
||||
}
|
||||
if (!entry.active) code.push_back(EncodeSop1(0x04, 126, 24));
|
||||
AppendStoreVgpr(&code, entry.dst, static_cast<u32>(test.initial.size()) + i);
|
||||
test.expected.push_back(entry.expected);
|
||||
}
|
||||
AppendEnd(&code);
|
||||
test.opcodes = {O::V_MOV_B32, O::BUFFER_LOAD_DWORD, O::V_MIN3_U16,
|
||||
O::S_MOV_B64, O::BUFFER_STORE_DWORD, O::S_ENDPGM};
|
||||
test.decoded_counts = {{"V_MIN3_U16", cases.size()}};
|
||||
test.required_spirv = {"OpULessThan"};
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase Vop3Med3I16Captured() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
@@ -24729,10 +24788,10 @@ TestCase VectorCompareInteger64Edges() {
|
||||
const auto lhs = pairs[i][0], rhs = pairs[i][1];
|
||||
const auto signed_lhs = std::bit_cast<int64_t>(lhs);
|
||||
const auto signed_rhs = std::bit_cast<int64_t>(rhs);
|
||||
for (const auto [opcode, value] : std::array<std::pair<u32, bool>, 5>{{
|
||||
{0xa1, signed_lhs < signed_rhs}, {0xa3, signed_lhs <= signed_rhs},
|
||||
{0xa5, lhs != rhs},
|
||||
{0xe3, lhs <= rhs}, {0xe6, lhs >= rhs}}}) {
|
||||
for (const auto [opcode, value] : std::array<std::pair<u32, bool>, 9>{{
|
||||
{0xa0, false}, {0xa1, signed_lhs < signed_rhs},
|
||||
{0xa3, signed_lhs <= signed_rhs}, {0xa5, lhs != rhs}, {0xa7, true},
|
||||
{0xe0, false}, {0xe3, lhs <= rhs}, {0xe6, lhs >= rhs}, {0xe7, true}}}) {
|
||||
test.code.push_back(EncodeVopc(opcode, Vgpr(1), 3));
|
||||
store_mask(106, value);
|
||||
AppendVop3(&test.code, opcode, 20, Vgpr(1), 8);
|
||||
@@ -24750,6 +24809,12 @@ TestCase VectorCompareInteger64Edges() {
|
||||
AppendVop3(&test.code, 0xa3, 20, Vgpr(1), 255);
|
||||
test.code.push_back(0xffffffffu);
|
||||
store_mask(20, signed_lhs <= -1);
|
||||
test.code.push_back(EncodeSop1(0x04, 10, 126));
|
||||
AppendVop3(&test.code, 0xb2, 126, 255, Vgpr(3)); // CMPX_EQ_I64 sign-extends -1.
|
||||
test.code.push_back(0xffffffffu);
|
||||
test.code.push_back(EncodeSop1(0x04, 20, 126));
|
||||
test.code.push_back(EncodeSop1(0x04, 126, 10));
|
||||
store_mask(20, -1 == signed_rhs);
|
||||
// Captured 8395e43f382309df PC 0x20cc: s[2:3] = (s[0:1] != 0) & EXEC.
|
||||
// Save the test buffer descriptor while its SGPRs hold the captured operands.
|
||||
test.code.push_back(EncodeSop1(0x04, 32, 0));
|
||||
@@ -24765,8 +24830,9 @@ TestCase VectorCompareInteger64Edges() {
|
||||
AppendEnd(&test.code);
|
||||
test.initial.resize(test.expected.size());
|
||||
test.opcodes = {O::V_MOV_B32, O::BUFFER_LOAD_DWORD, O::V_READFIRSTLANE_B32,
|
||||
O::V_CMP_LT_I64, O::V_CMP_LE_I64, O::V_CMP_EQ_I64, O::V_CMP_NE_I64,
|
||||
O::V_CMP_LE_U64, O::V_CMP_GE_U64,
|
||||
O::V_CMP_F_I64, O::V_CMP_LT_I64, O::V_CMP_LE_I64, O::V_CMP_EQ_I64,
|
||||
O::V_CMP_NE_I64, O::V_CMP_T_I64, O::V_CMPX_EQ_I64,
|
||||
O::V_CMP_F_U64, O::V_CMP_LE_U64, O::V_CMP_GE_U64, O::V_CMP_T_U64,
|
||||
O::S_MOV_B64, O::BUFFER_STORE_DWORD, O::S_ENDPGM};
|
||||
test.required_spirv = {"OpSLessThan", "OpULessThanEqual", "OpUGreaterThanEqual"};
|
||||
return test;
|
||||
@@ -24774,7 +24840,7 @@ TestCase VectorCompareInteger64Edges() {
|
||||
|
||||
TestCase VectorCompareExecWaveMasks(u32 wave_size) {
|
||||
using O = ShaderOpcode;
|
||||
// Rows exercise signed 16-bit truncation, full-width unsigned ordering, and
|
||||
// Rows exercise signed 16-bit truncation, full-width integer ordering, and
|
||||
// ordered FP64 comparisons (including NaN, signed zero, and subnormals).
|
||||
constexpr std::array<std::array<std::array<uint64_t, 2>, 8>, 3> inputs{{
|
||||
{{{0x1111ffff, 0x22220000}, {0x33338000, 0x44447fff},
|
||||
@@ -24795,8 +24861,13 @@ TestCase VectorCompareExecWaveMasks(u32 wave_size) {
|
||||
Compare{0x99, O::V_CMPX_LT_I16, 0}, Compare{0x9a, O::V_CMPX_EQ_I16, 0},
|
||||
Compare{0x9b, O::V_CMPX_LE_I16, 0}, Compare{0x9c, O::V_CMPX_GT_I16, 0},
|
||||
Compare{0x9d, O::V_CMPX_NE_I16, 0}, Compare{0x9e, O::V_CMPX_GE_I16, 0},
|
||||
Compare{0xf3, O::V_CMPX_LE_U64, 1}, Compare{0x33, O::V_CMPX_LE_F64, 2},
|
||||
Compare{0x36, O::V_CMPX_GE_F64, 2}};
|
||||
Compare{0xb0, O::V_CMPX_F_I64, 1}, Compare{0xb1, O::V_CMPX_LT_I64, 1},
|
||||
Compare{0xb2, O::V_CMPX_EQ_I64, 1}, Compare{0xb3, O::V_CMPX_LE_I64, 1},
|
||||
Compare{0xb7, O::V_CMPX_T_I64, 1}, Compare{0xf0, O::V_CMPX_F_U64, 1},
|
||||
Compare{0xf1, O::V_CMPX_LT_U64, 1}, Compare{0xf2, O::V_CMPX_EQ_U64, 1},
|
||||
Compare{0xf3, O::V_CMPX_LE_U64, 1}, Compare{0xf4, O::V_CMPX_GT_U64, 1},
|
||||
Compare{0xf6, O::V_CMPX_GE_U64, 1}, Compare{0xf7, O::V_CMPX_T_U64, 1},
|
||||
Compare{0x33, O::V_CMPX_LE_F64, 2}, Compare{0x36, O::V_CMPX_GE_F64, 2}};
|
||||
constexpr u32 low_exec = 0xeeeeeeeeu, high_exec = 0x77777777u;
|
||||
constexpr u32 vcc_lo = 0x12345678u, vcc_hi = 0x89abcdefu;
|
||||
TestCase test;
|
||||
@@ -24832,7 +24903,15 @@ TestCase VectorCompareExecWaveMasks(u32 wave_size) {
|
||||
case 0x9c: result = a > b; break;
|
||||
case 0x9d: result = a != b; break;
|
||||
case 0x9e: result = a >= b; break;
|
||||
case 0xb0: case 0xf0: result = false; break;
|
||||
case 0xb1: result = std::bit_cast<int64_t>(pair[0]) < std::bit_cast<int64_t>(pair[1]); break;
|
||||
case 0xb2: case 0xf2: result = pair[0] == pair[1]; break;
|
||||
case 0xb3: result = std::bit_cast<int64_t>(pair[0]) <= std::bit_cast<int64_t>(pair[1]); break;
|
||||
case 0xb7: case 0xf7: result = true; break;
|
||||
case 0xf1: result = pair[0] < pair[1]; break;
|
||||
case 0xf3: result = pair[0] <= pair[1]; break;
|
||||
case 0xf4: result = pair[0] > pair[1]; break;
|
||||
case 0xf6: result = pair[0] >= pair[1]; break;
|
||||
case 0x33: result = std::bit_cast<double>(pair[0]) <= std::bit_cast<double>(pair[1]); break;
|
||||
case 0x36: result = std::bit_cast<double>(pair[0]) >= std::bit_cast<double>(pair[1]); break;
|
||||
}
|
||||
@@ -28696,6 +28775,108 @@ TestCase BufferStoreFormatXSnorm16ClampsRoundsAndPreservesHalfwords() {
|
||||
return test;
|
||||
}
|
||||
|
||||
std::vector<TestCase> BufferLoadFormatD16XCases() {
|
||||
using F = Prospero::BufferFormat;
|
||||
using O = ShaderOpcode;
|
||||
struct LoadCase {
|
||||
const char *name;
|
||||
F format;
|
||||
u32 input;
|
||||
u32 expected;
|
||||
u32 selector = 4;
|
||||
u32 index = 0;
|
||||
bool active = true;
|
||||
bool overlap = false;
|
||||
};
|
||||
constexpr LoadCase inputs[] = {
|
||||
{"BufferLoadFormatD16XFloat16", F::k16Float, 0x7e63c000u, 0xabcdc000u},
|
||||
{"BufferLoadFormatD16XFloat32", F::k32Float, 0x3f000000u, 0xabcd3800u},
|
||||
{"BufferLoadFormatD16XUint32", F::k32UInt, 0x1234fffeu, 0xabcdffffu},
|
||||
{"BufferLoadFormatD16XUintAtMax", F::k32UInt, 0x0000ffffu, 0xabcdffffu},
|
||||
{"BufferLoadFormatD16XUintMax32", F::k32UInt, 0xffffffffu, 0xabcdffffu},
|
||||
{"BufferLoadFormatD16XSint32", F::k32SInt, 0xffff8001u, 0xabcd8001u},
|
||||
{"BufferLoadFormatD16XSintAboveMax", F::k32SInt, 0x00008000u, 0xabcd7fffu},
|
||||
{"BufferLoadFormatD16XSintBelowMin", F::k32SInt, 0xffff7fffu, 0xabcd8000u},
|
||||
{"BufferLoadFormatD16XSintAtMax", F::k32SInt, 0x00007fffu, 0xabcd7fffu},
|
||||
{"BufferLoadFormatD16XSintAtMin", F::k32SInt, 0xffff8000u, 0xabcd8000u},
|
||||
{"BufferLoadFormatD16XSintMax32", F::k32SInt, 0x7fffffffu, 0xabcd7fffu},
|
||||
{"BufferLoadFormatD16XSintMin32", F::k32SInt, 0x80000000u, 0xabcd8000u},
|
||||
{"BufferLoadFormatD16XUnorm8", F::k8UNorm, 0xffu, 0xabcd3c00u},
|
||||
{"BufferLoadFormatD16XSnorm16", F::k16SNorm, 0x8001u, 0xabcdbc00u},
|
||||
{"BufferLoadFormatD16XFloatOne", F::k32Float, 0u, 0xabcd3c00u, 1},
|
||||
{"BufferLoadFormatD16XOobFloatOne", F::k32Float, 0u, 0xabcd3c00u, 1, 4},
|
||||
{"BufferLoadFormatD16XOobUintOne", F::k32UInt, 0u, 0xabcd0001u, 1, 4},
|
||||
{"BufferLoadFormatD16XOobZero", F::k32Float, 0u, 0xabcd0000u, 4, 4},
|
||||
{"BufferLoadFormatD16XInactive", F::k32Float, 0u, 0xabcd1234u, 4, 0, false},
|
||||
{"BufferLoadFormatD16XOverlappingAddress", F::k16UInt, 7u, 7u, 4, 0, true, true},
|
||||
};
|
||||
std::vector<TestCase> cases;
|
||||
for (const auto &input : inputs) {
|
||||
TestCase test;
|
||||
test.name = input.name;
|
||||
AppendVMovLiteral(&test.code, 0, input.overlap ? input.index : 0xabcd1234u);
|
||||
AppendVMovU32(&test.code, 1, input.index);
|
||||
if (!input.active) {
|
||||
test.code.push_back(EncodeSop1(0x04, 126, InlineU32(0)));
|
||||
}
|
||||
test.code.push_back(EncodeMubuf0(0x80u, 0, true, false));
|
||||
test.code.push_back(EncodeMubuf1(0, 0, input.overlap ? 0 : 1));
|
||||
if (!input.active) {
|
||||
test.code.push_back(EncodeSMovB32(126, InlineU32(1)));
|
||||
test.code.push_back(EncodeSMovB32(127, InlineU32(0)));
|
||||
}
|
||||
AppendStoreVgpr(&test.code, 0, 2);
|
||||
AppendEnd(&test.code);
|
||||
test.initial = {input.input, 0xdeadbeefu, 0u, 0xcafef00du};
|
||||
test.expected = {input.input, 0xdeadbeefu, input.expected, 0xcafef00du};
|
||||
test.user_data = MakeStructuredStorageBufferData(4, 4, false, BufferFormat(input.format));
|
||||
test.user_data[3] = (test.user_data[3] & ~7u) | input.selector;
|
||||
test.has_user_data = true;
|
||||
test.opcodes = {O::V_MOV_B32, O::BUFFER_LOAD_FORMAT_D16_X,
|
||||
O::BUFFER_STORE_DWORD, O::S_ENDPGM};
|
||||
cases.push_back(std::move(test));
|
||||
}
|
||||
return cases;
|
||||
}
|
||||
|
||||
std::vector<TestCase> BufferStoreFormatD16XCases() {
|
||||
using F = Prospero::BufferFormat;
|
||||
using O = ShaderOpcode;
|
||||
struct StoreCase {
|
||||
const char *name;
|
||||
F format;
|
||||
u32 input;
|
||||
u32 expected;
|
||||
};
|
||||
constexpr StoreCase inputs[] = {
|
||||
{"BufferStoreFormatD16XFloat16", F::k16Float, 0x7e63c000u, 0xffffc000u},
|
||||
{"BufferStoreFormatD16XFloat32", F::k32Float, 0x7e633800u, 0x3f000000u},
|
||||
{"BufferStoreFormatD16XUint32", F::k32UInt, 0x7e63fffeu, 0x0000fffeu},
|
||||
{"BufferStoreFormatD16XSint32", F::k32SInt, 0x7e63fffeu, 0xfffffffeu},
|
||||
{"BufferStoreFormatD16XUnorm8", F::k8UNorm, 0x7e633800u, 0xffffff80u},
|
||||
{"BufferStoreFormatD16XSnorm16", F::k16SNorm, 0x7e63b800u, 0xffffc000u},
|
||||
{"BufferStoreFormatD16XZeroFill", F::k16_16Float, 0x7e633800u, 0x00003800u},
|
||||
};
|
||||
std::vector<TestCase> cases;
|
||||
for (const auto &input : inputs) {
|
||||
TestCase test;
|
||||
test.name = input.name;
|
||||
AppendVMovLiteral(&test.code, 66, input.input);
|
||||
AppendVMovU32(&test.code, 65, 1);
|
||||
// Match the captured CS824586f35db5c80d instruction at pc 0x484.
|
||||
test.code.push_back(EncodeMubuf0(0x84u, 0, true, false));
|
||||
test.code.push_back(EncodeMubuf1(66, 0, 65));
|
||||
AppendEnd(&test.code);
|
||||
test.initial = {0xdeadbeefu, 0xffffffffu, 0xcafef00du};
|
||||
test.expected = {0xdeadbeefu, input.expected, 0xcafef00du};
|
||||
test.user_data = MakeStructuredStorageBufferData(4, 3, false, BufferFormat(input.format));
|
||||
test.has_user_data = true;
|
||||
test.opcodes = {O::V_MOV_B32, O::BUFFER_STORE_FORMAT_D16_X, O::S_ENDPGM};
|
||||
cases.push_back(std::move(test));
|
||||
}
|
||||
return cases;
|
||||
}
|
||||
|
||||
std::vector<TestCase> FormattedStoreConversionCases() {
|
||||
using F = Prospero::BufferFormat;
|
||||
using O = ShaderOpcode;
|
||||
@@ -35337,6 +35518,7 @@ std::vector<TestCase> MakeCases() {
|
||||
AddCase(Vop3CvtPkI16I32Captured);
|
||||
AddCase(Vop3MulLoU16CapturedAndSelectors);
|
||||
AddCase(Vop3MadI16CapturedSelectorsAndSaturation);
|
||||
AddCase(Vop3Min3U16CapturedAndSelectors);
|
||||
AddCase(Vop3Med3I16Captured);
|
||||
AddCase(Vop2SdwaMinU32PreservesWordDestination);
|
||||
AddCase(VectorShiftCountsMaskLowBits);
|
||||
@@ -35527,6 +35709,12 @@ std::vector<TestCase> MakeCases() {
|
||||
AddCase(BufferStoreFormatXyzwFloat16ConvertsComponents);
|
||||
AddCase(BufferStoreFormatXyzwSnorm16CapturedSkinningVectors);
|
||||
AddCase(BufferStoreFormatXSnorm16ClampsRoundsAndPreservesHalfwords);
|
||||
for (auto &test : BufferLoadFormatD16XCases()) {
|
||||
cases.push_back(std::move(test));
|
||||
}
|
||||
for (auto &test : BufferStoreFormatD16XCases()) {
|
||||
cases.push_back(std::move(test));
|
||||
}
|
||||
for (auto &test : FormattedStoreConversionCases()) {
|
||||
cases.push_back(std::move(test));
|
||||
}
|
||||
@@ -40715,6 +40903,15 @@ int main(int argc, char **argv) {
|
||||
vulkan.CheckNativeIndirectDispatch();
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--integer64-compare-only") == 0) {
|
||||
VulkanHarness vulkan;
|
||||
RunCase(&vulkan, VectorCompareInteger64Edges());
|
||||
RunCase(&vulkan, VectorCompareExecWaveMasks(32));
|
||||
RunCase(&vulkan, VectorCompareExecWaveMasks(64));
|
||||
RunCase(&vulkan, VectorVopcCmpxNeU64CapturedExecMask());
|
||||
RunCase(&vulkan, VectorVop3CmpxNeI64CapturedExecMask());
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--new-opcodes-only") == 0) {
|
||||
VulkanHarness vulkan;
|
||||
RunCase(&vulkan, VectorCompareInteger64Edges());
|
||||
@@ -40780,6 +40977,12 @@ int main(int argc, char **argv) {
|
||||
RunCase(&vulkan, BufferStoreFormatXyzwFloat16ConvertsComponents());
|
||||
RunCase(&vulkan, BufferStoreFormatXyzwSnorm16CapturedSkinningVectors());
|
||||
RunCase(&vulkan, BufferStoreFormatXSnorm16ClampsRoundsAndPreservesHalfwords());
|
||||
for (const auto &test : BufferLoadFormatD16XCases()) {
|
||||
RunCase(&vulkan, test);
|
||||
}
|
||||
for (const auto &test : BufferStoreFormatD16XCases()) {
|
||||
RunCase(&vulkan, test);
|
||||
}
|
||||
for (const auto &test : FormattedStoreConversionCases()) {
|
||||
RunCase(&vulkan, test);
|
||||
}
|
||||
@@ -41260,6 +41463,7 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--med3-i16-only") == 0) {
|
||||
VulkanHarness vulkan;
|
||||
RunCase(&vulkan, Vop3Min3U16CapturedAndSelectors());
|
||||
RunCase(&vulkan, Vop3Med3I16Captured());
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user