Compare commits
1 commit
master
...
custom-ser
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
954b64f3f4 |
18 changed files with 384 additions and 319 deletions
|
|
@ -183,8 +183,7 @@ option(YUZU_USE_QT_MULTIMEDIA "Use QtMultimedia for Camera" OFF)
|
|||
option(YUZU_USE_QT_WEB_ENGINE "Use QtWebEngine for web applet implementation" OFF)
|
||||
set(YUZU_QT_MIRROR "" CACHE STRING "What mirror to use for downloading the bundled Qt libraries")
|
||||
|
||||
# FreeBSD doesn't have a cubeb port yet, vendoring it isn't required (it's optional anyways)
|
||||
cmake_dependent_option(ENABLE_CUBEB "Enables the cubeb audio backend" ON "NOT PLATFORM_FREEBSD" OFF)
|
||||
option(ENABLE_CUBEB "Enables the cubeb audio backend" ON)
|
||||
|
||||
set(EXT_DEFAULT OFF)
|
||||
if (MSVC OR ANDROID)
|
||||
|
|
@ -199,8 +198,7 @@ cmake_dependent_option(YUZU_USE_EXTERNAL_FFMPEG "Build FFmpeg from source" "${PL
|
|||
# sirit
|
||||
option(YUZU_USE_BUNDLED_SIRIT "Download bundled sirit" ${EXT_DEFAULT})
|
||||
|
||||
# Re-allow on FreeBSD once its on mainline ports
|
||||
cmake_dependent_option(ENABLE_LIBUSB "Enable the use of LibUSB" ON "WIN32 OR PLATFORM_LINUX OR APPLE" OFF)
|
||||
cmake_dependent_option(ENABLE_LIBUSB "Enable the use of LibUSB" ON "NOT ANDROID" OFF)
|
||||
|
||||
cmake_dependent_option(ENABLE_OPENGL "Enable OpenGL" ON "NOT WIN32 OR NOT ARCHITECTURE_arm64" OFF)
|
||||
mark_as_advanced(FORCE ENABLE_OPENGL)
|
||||
|
|
|
|||
|
|
@ -10,3 +10,4 @@ This handbook is primarily aimed at the end-user - baking useful knowledge for e
|
|||
- **[Platforms and Architectures](user/Architectures.md)**
|
||||
- **[Testing](user/Testing.md)**
|
||||
- **[Data, savefiles and storage](user/Storage.md)**
|
||||
- **[Native Application Development](user/Development.md)**
|
||||
|
|
|
|||
12
docs/user/Development.md
Normal file
12
docs/user/Development.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# User Handbook - Native Application Development
|
||||
|
||||
Debugging on physical hardware can get tedious and time consuming. Users are empowered with the debugging capabilities of the emulator to ensure their applications run as-is on the system. To the greatest extent possible atleast.
|
||||
|
||||
## Debugging
|
||||
|
||||
**Standard key prefix**: Allows to redirect the key manager to a file other than `prod.keys` (for example `other` would redirect to `other.keys`). This is useful for testing multiple keysets. Default is `prod`.
|
||||
|
||||
**Changing serial**: Very basic way to set debug values for the serial (and battery number). Developers do not need to write the full serial as it will be writen in-place (that is, it will be filled with the default serial and then overwrite the serial from the beginning).
|
||||
- Battery serial: `YUZU0EMULATOR14022024`
|
||||
- Board serial: `YUZ10000000001`
|
||||
If the user were to set their board serial as `ABC`, then it will be written in-place and the resulting serial would be `ABC10000000001`. There are no underlying checks to ensure correctness of serials other than a hard limit of 16-characters for both.
|
||||
|
|
@ -735,10 +735,11 @@ struct Values {
|
|||
Setting<bool> disable_web_applet{linkage, true, "disable_web_applet", Category::Debugging};
|
||||
|
||||
// Miscellaneous
|
||||
Setting<std::string> serial_battery{linkage, std::string(), "serial_battery", Category::Miscellaneous};
|
||||
Setting<std::string> serial_unit{linkage, std::string(), "serial_unit", Category::Miscellaneous};
|
||||
Setting<std::string> log_filter{linkage, "*:Info", "log_filter", Category::Miscellaneous};
|
||||
Setting<bool> log_flush_line{linkage, false, "flush_line", Category::Miscellaneous, Specialization::Default, true, true};
|
||||
Setting<bool> censor_username{linkage, true, "censor_username", Category::Miscellaneous};
|
||||
Setting<bool> use_dev_keys{linkage, false, "use_dev_keys", Category::Miscellaneous};
|
||||
Setting<bool> first_launch{linkage, true, "first_launch", Category::Miscellaneous};
|
||||
|
||||
// Network
|
||||
|
|
|
|||
|
|
@ -640,32 +640,20 @@ KeyManager::KeyManager() {
|
|||
|
||||
void KeyManager::ReloadKeys() {
|
||||
// Initialize keys
|
||||
const auto yuzu_keys_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::KeysDir);
|
||||
|
||||
if (!Common::FS::CreateDir(yuzu_keys_dir)) {
|
||||
const auto keys_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::KeysDir);
|
||||
if (!Common::FS::CreateDir(keys_dir))
|
||||
LOG_ERROR(Core, "Failed to create the keys directory.");
|
||||
}
|
||||
|
||||
if (Settings::values.use_dev_keys) {
|
||||
dev_mode = true;
|
||||
LoadFromFile(yuzu_keys_dir / "dev.keys_autogenerated", false);
|
||||
LoadFromFile(yuzu_keys_dir / "dev.keys", false);
|
||||
} else {
|
||||
dev_mode = false;
|
||||
LoadFromFile(yuzu_keys_dir / "prod.keys_autogenerated", false);
|
||||
LoadFromFile(yuzu_keys_dir / "prod.keys", false);
|
||||
}
|
||||
|
||||
LoadFromFile(yuzu_keys_dir / "title.keys_autogenerated", true);
|
||||
LoadFromFile(yuzu_keys_dir / "title.keys", true);
|
||||
LoadFromFile(yuzu_keys_dir / "console.keys_autogenerated", false);
|
||||
LoadFromFile(yuzu_keys_dir / "console.keys", false);
|
||||
LoadFromFile(keys_dir / "prod.keys_autogenerated", false);
|
||||
LoadFromFile(keys_dir / "prod.keys", false);
|
||||
LoadFromFile(keys_dir / "title.keys_autogenerated", true);
|
||||
LoadFromFile(keys_dir / "title.keys", true);
|
||||
LoadFromFile(keys_dir / "console.keys_autogenerated", false);
|
||||
LoadFromFile(keys_dir / "console.keys", false);
|
||||
}
|
||||
|
||||
static bool ValidCryptoRevisionString(std::string_view base, size_t begin, size_t length) {
|
||||
if (base.size() < begin + length) {
|
||||
if (base.size() < begin + length)
|
||||
return false;
|
||||
}
|
||||
return std::all_of(base.begin() + begin, base.begin() + begin + length,
|
||||
[](u8 c) { return std::isxdigit(c); });
|
||||
}
|
||||
|
|
@ -850,9 +838,8 @@ void KeyManager::WriteKeyToFile(KeyCategory category, std::string_view keyname,
|
|||
const auto yuzu_keys_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::KeysDir);
|
||||
|
||||
std::string filename = "title.keys_autogenerated";
|
||||
|
||||
if (category == KeyCategory::Standard) {
|
||||
filename = dev_mode ? "dev.keys_autogenerated" : "prod.keys_autogenerated";
|
||||
filename = "prod.keys_autogenerated";
|
||||
} else if (category == KeyCategory::Console) {
|
||||
filename = "console.keys_autogenerated";
|
||||
}
|
||||
|
|
@ -947,17 +934,10 @@ void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) {
|
|||
}
|
||||
|
||||
bool KeyManager::KeyFileExists(bool title) {
|
||||
const auto yuzu_keys_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::KeysDir);
|
||||
|
||||
if (title) {
|
||||
return Common::FS::Exists(yuzu_keys_dir / "title.keys");
|
||||
}
|
||||
|
||||
if (Settings::values.use_dev_keys) {
|
||||
return Common::FS::Exists(yuzu_keys_dir / "dev.keys");
|
||||
}
|
||||
|
||||
return Common::FS::Exists(yuzu_keys_dir / "prod.keys");
|
||||
const auto keys_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::KeysDir);
|
||||
if (title)
|
||||
return Common::FS::Exists(keys_dir / "title.keys");
|
||||
return Common::FS::Exists(keys_dir / "prod.keys");
|
||||
}
|
||||
|
||||
void KeyManager::DeriveSDSeedLazy() {
|
||||
|
|
|
|||
|
|
@ -311,7 +311,6 @@ private:
|
|||
std::array<u8, 576> eticket_extended_kek{};
|
||||
RSAKeyPair<2048> eticket_rsa_keypair{};
|
||||
|
||||
bool dev_mode;
|
||||
void LoadFromFile(const std::filesystem::path& file_path, bool is_title_keys);
|
||||
|
||||
template <size_t Size>
|
||||
|
|
|
|||
|
|
@ -948,15 +948,23 @@ Result ISystemSettingsServer::SetPrimaryAlbumStorage(PrimaryAlbumStorage primary
|
|||
|
||||
Result ISystemSettingsServer::GetBatteryLot(Out<BatteryLot> out_battery_lot) {
|
||||
LOG_INFO(Service_SET, "called");
|
||||
|
||||
*out_battery_lot = {"YUZU0EMULATOR14022024"};
|
||||
if (auto const s = ::Settings::values.serial_battery.GetValue(); !s.empty()) {
|
||||
auto const max_size = out_battery_lot->lot_number.size();
|
||||
auto const end = s.size() > max_size ? s.begin() + max_size : s.end();
|
||||
std::copy(s.begin(), s.end(), out_battery_lot->lot_number.begin());
|
||||
}
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ISystemSettingsServer::GetSerialNumber(Out<SerialNumber> out_console_serial) {
|
||||
LOG_INFO(Service_SET, "called");
|
||||
|
||||
*out_console_serial = {"YUZ10000000001"};
|
||||
if (auto const s = ::Settings::values.serial_unit.GetValue(); !s.empty()) {
|
||||
auto const max_size = out_console_serial->serial_number.size();
|
||||
auto const end = s.size() > max_size ? s.begin() + max_size : s.end();
|
||||
std::copy(s.begin(), s.end(), out_console_serial->serial_number.begin());
|
||||
}
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
|
@ -1058,10 +1066,9 @@ Result ISystemSettingsServer::SetDeviceNickName(
|
|||
}
|
||||
|
||||
Result ISystemSettingsServer::GetProductModel(Out<u32> out_product_model) {
|
||||
const u32 product_model = 1;
|
||||
|
||||
// Most certainly should be 1 -- definitely should not be 2, but it's worth tinkering with anyways
|
||||
u32 const product_model = 1;
|
||||
LOG_WARNING(Service_SET, "(STUBBED) called, product_model={}", product_model);
|
||||
|
||||
*out_product_model = product_model;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
|
@ -1324,52 +1331,44 @@ Result ISystemSettingsServer::SetPanelCrcMode(s32 panel_crc_mode) {
|
|||
}
|
||||
|
||||
void ISystemSettingsServer::SetupSettings() {
|
||||
auto system_dir =
|
||||
Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) / "system/save/8000000000000050";
|
||||
auto system_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) / "system/save/8000000000000050";
|
||||
if (!LoadSettingsFile(system_dir, []() { return DefaultSystemSettings(); })) {
|
||||
ASSERT(false);
|
||||
}
|
||||
|
||||
auto private_dir =
|
||||
Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) / "system/save/8000000000000052";
|
||||
auto private_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) / "system/save/8000000000000052";
|
||||
if (!LoadSettingsFile(private_dir, []() { return DefaultPrivateSettings(); })) {
|
||||
ASSERT(false);
|
||||
}
|
||||
|
||||
auto device_dir =
|
||||
Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) / "system/save/8000000000000053";
|
||||
auto device_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) / "system/save/8000000000000053";
|
||||
if (!LoadSettingsFile(device_dir, []() { return DefaultDeviceSettings(); })) {
|
||||
ASSERT(false);
|
||||
}
|
||||
|
||||
auto appln_dir =
|
||||
Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) / "system/save/8000000000000054";
|
||||
auto appln_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) / "system/save/8000000000000054";
|
||||
if (!LoadSettingsFile(appln_dir, []() { return DefaultApplnSettings(); })) {
|
||||
ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
void ISystemSettingsServer::StoreSettings() {
|
||||
auto system_dir =
|
||||
Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) / "system/save/8000000000000050";
|
||||
auto system_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) / "system/save/8000000000000050";
|
||||
if (!StoreSettingsFile(system_dir, m_system_settings)) {
|
||||
LOG_ERROR(Service_SET, "Failed to store System settings");
|
||||
}
|
||||
|
||||
auto private_dir =
|
||||
Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) / "system/save/8000000000000052";
|
||||
auto private_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) / "system/save/8000000000000052";
|
||||
if (!StoreSettingsFile(private_dir, m_private_settings)) {
|
||||
LOG_ERROR(Service_SET, "Failed to store Private settings");
|
||||
}
|
||||
|
||||
auto device_dir =
|
||||
Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) / "system/save/8000000000000053";
|
||||
auto device_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) / "system/save/8000000000000053";
|
||||
if (!StoreSettingsFile(device_dir, m_device_settings)) {
|
||||
LOG_ERROR(Service_SET, "Failed to store Device settings");
|
||||
}
|
||||
|
||||
auto appln_dir =
|
||||
Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) / "system/save/8000000000000054";
|
||||
auto appln_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) / "system/save/8000000000000054";
|
||||
if (!StoreSettingsFile(appln_dir, m_appln_settings)) {
|
||||
LOG_ERROR(Service_SET, "Failed to store ApplLn settings");
|
||||
}
|
||||
|
|
@ -1380,9 +1379,8 @@ void ISystemSettingsServer::StoreSettingsThreadFunc(std::stop_token stop_token)
|
|||
|
||||
while (Common::StoppableTimedWait(stop_token, std::chrono::minutes(1))) {
|
||||
std::scoped_lock l{m_save_needed_mutex};
|
||||
if (!std::exchange(m_save_needed, false)) {
|
||||
if (!std::exchange(m_save_needed, false))
|
||||
continue;
|
||||
}
|
||||
StoreSettings();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,3 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2022 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
|
|
@ -569,49 +566,37 @@ void EmitIR<IR::Opcode::FPVectorRecipStepFused64>(oaknut::CodeGenerator& code, E
|
|||
EmitThreeOpArranged<64>(code, ctx, inst, [&](auto Vresult, auto Va, auto Vb) { code.FRECPS(Vresult, Va, Vb); });
|
||||
}
|
||||
|
||||
/// @brief Assembly thunk "parameters" are in template parameters
|
||||
/// TODO: we have space for a 5th parameter? :)
|
||||
template<typename FPT, FP::RoundingMode rounding_mode, bool exact>
|
||||
static void EmitIRVectorRoundInt16Thunk(VectorArray<FPT>& output, const VectorArray<FPT>& input, FP::FPCR fpcr, FP::FPSR& fpsr) {
|
||||
for (size_t i = 0; i < output.size(); ++i)
|
||||
output[i] = FPT(FP::FPRoundInt<FPT>(input[i], fpcr, rounding_mode, exact, fpsr));
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::FPVectorRoundInt16>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
const auto rounding = FP::RoundingMode(inst->GetArg(1).GetU8());
|
||||
const auto rounding = static_cast<FP::RoundingMode>(inst->GetArg(1).GetU8());
|
||||
const bool exact = inst->GetArg(2).GetU1();
|
||||
// Don't even think about making this a LUT -- it's bad
|
||||
using FPT = u16; // Yes it's u16, no fsize madness
|
||||
switch (rounding) {
|
||||
case FP::RoundingMode::ToNearest_TieEven:
|
||||
exact
|
||||
? EmitTwoOpFallback<3>(code, ctx, inst, EmitIRVectorRoundInt16Thunk<FPT, FP::RoundingMode::ToNearest_TieEven, true>)
|
||||
: EmitTwoOpFallback<3>(code, ctx, inst, EmitIRVectorRoundInt16Thunk<FPT, FP::RoundingMode::ToNearest_TieEven, false>);
|
||||
break;
|
||||
case FP::RoundingMode::TowardsPlusInfinity:
|
||||
exact
|
||||
? EmitTwoOpFallback<3>(code, ctx, inst, EmitIRVectorRoundInt16Thunk<FPT, FP::RoundingMode::TowardsPlusInfinity, true>)
|
||||
: EmitTwoOpFallback<3>(code, ctx, inst, EmitIRVectorRoundInt16Thunk<FPT, FP::RoundingMode::TowardsPlusInfinity, false>);
|
||||
break;
|
||||
case FP::RoundingMode::TowardsMinusInfinity:
|
||||
exact
|
||||
? EmitTwoOpFallback<3>(code, ctx, inst, EmitIRVectorRoundInt16Thunk<FPT, FP::RoundingMode::TowardsMinusInfinity, true>)
|
||||
: EmitTwoOpFallback<3>(code, ctx, inst, EmitIRVectorRoundInt16Thunk<FPT, FP::RoundingMode::TowardsMinusInfinity, false>);
|
||||
break;
|
||||
case FP::RoundingMode::TowardsZero:
|
||||
exact
|
||||
? EmitTwoOpFallback<3>(code, ctx, inst, EmitIRVectorRoundInt16Thunk<FPT, FP::RoundingMode::TowardsZero, true>)
|
||||
: EmitTwoOpFallback<3>(code, ctx, inst, EmitIRVectorRoundInt16Thunk<FPT, FP::RoundingMode::TowardsZero, false>);
|
||||
break;
|
||||
case FP::RoundingMode::ToNearest_TieAwayFromZero:
|
||||
exact
|
||||
? EmitTwoOpFallback<3>(code, ctx, inst, EmitIRVectorRoundInt16Thunk<FPT, FP::RoundingMode::ToNearest_TieAwayFromZero, true>)
|
||||
: EmitTwoOpFallback<3>(code, ctx, inst, EmitIRVectorRoundInt16Thunk<FPT, FP::RoundingMode::ToNearest_TieAwayFromZero, false>);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
using rounding_list = mp::list<
|
||||
mp::lift_value<FP::RoundingMode::ToNearest_TieEven>,
|
||||
mp::lift_value<FP::RoundingMode::TowardsPlusInfinity>,
|
||||
mp::lift_value<FP::RoundingMode::TowardsMinusInfinity>,
|
||||
mp::lift_value<FP::RoundingMode::TowardsZero>,
|
||||
mp::lift_value<FP::RoundingMode::ToNearest_TieAwayFromZero>>;
|
||||
using exact_list = mp::list<std::true_type, std::false_type>;
|
||||
|
||||
static const auto lut = Common::GenerateLookupTableFromList(
|
||||
[]<typename I>(I) {
|
||||
using FPT = u16;
|
||||
return std::pair{
|
||||
mp::lower_to_tuple_v<I>,
|
||||
Common::FptrCast(
|
||||
[](VectorArray<FPT>& output, const VectorArray<FPT>& input, FP::FPCR fpcr, FP::FPSR& fpsr) {
|
||||
constexpr FP::RoundingMode rounding_mode = mp::get<0, I>::value;
|
||||
constexpr bool exact = mp::get<1, I>::value;
|
||||
|
||||
for (size_t i = 0; i < output.size(); ++i) {
|
||||
output[i] = static_cast<FPT>(FP::FPRoundInt<FPT>(input[i], fpcr, rounding_mode, exact, fpsr));
|
||||
}
|
||||
})};
|
||||
},
|
||||
mp::cartesian_product<rounding_list, exact_list>{});
|
||||
|
||||
EmitTwoOpFallback<3>(code, ctx, inst, lut.at(std::make_tuple(rounding, exact)));
|
||||
}
|
||||
|
||||
template<>
|
||||
|
|
|
|||
|
|
@ -1071,42 +1071,57 @@ void EmitX64::EmitFPRecipStepFused64(EmitContext& ctx, IR::Inst* inst) {
|
|||
EmitFPRecipStepFused<64>(code, ctx, inst);
|
||||
}
|
||||
|
||||
/// @brief An assembly thunk for the general rounding of FP
|
||||
/// Conciously making this be a template, because the cost of comparing "fsize" for every
|
||||
/// round is not worth it. Remember we are making only 3 copies (u16,u32,u64)
|
||||
/// Extra args layout:
|
||||
/// 0-7 rounding_mode
|
||||
/// 8-15 exact (!= 0)
|
||||
template<typename FPT>
|
||||
static u64 EmitFPRoundThunk(u64 input, FP::FPSR& fpsr, FP::FPCR fpcr, u32 extra_args) {
|
||||
auto const exact = ((extra_args >> 16) & 0xff) != 0;
|
||||
auto const rounding_mode = FP::RoundingMode((extra_args >> 8) & 0xff);
|
||||
return FP::FPRoundInt<FPT>(FPT(input), fpcr, rounding_mode, exact, fpsr);
|
||||
}
|
||||
|
||||
static void EmitFPRound(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst, size_t fsize) {
|
||||
auto const rounding_mode = FP::RoundingMode(inst->GetArg(1).GetU8());
|
||||
bool const exact = inst->GetArg(2).GetU1();
|
||||
auto const round_imm = ConvertRoundingModeToX64Immediate(rounding_mode);
|
||||
const auto rounding_mode = static_cast<FP::RoundingMode>(inst->GetArg(1).GetU8());
|
||||
const bool exact = inst->GetArg(2).GetU1();
|
||||
const auto round_imm = ConvertRoundingModeToX64Immediate(rounding_mode);
|
||||
|
||||
if (fsize != 16 && code.HasHostFeature(HostFeature::SSE41) && round_imm && !exact) {
|
||||
fsize == 64
|
||||
? FPTwoOp<64>(code, ctx, inst, [&](Xbyak::Xmm result) { code.roundsd(result, result, *round_imm); })
|
||||
: FPTwoOp<32>(code, ctx, inst, [&](Xbyak::Xmm result) { code.roundss(result, result, *round_imm); });
|
||||
} else {
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
// See EmitFPRoundThunk
|
||||
auto const extra_args = u32(rounding_mode) | (u32(exact) << 8);
|
||||
ctx.reg_alloc.HostCall(inst, args[0]);
|
||||
code.lea(code.ABI_PARAM2, code.ptr[code.ABI_JIT_PTR + code.GetJitStateInfo().offsetof_fpsr_exc]);
|
||||
code.mov(code.ABI_PARAM3.cvt32(), ctx.FPCR().Value());
|
||||
code.mov(code.ABI_PARAM4.cvt32(), extra_args);
|
||||
switch (fsize) {
|
||||
case 64: code.CallFunction(EmitFPRoundThunk<u64>); break;
|
||||
case 32: code.CallFunction(EmitFPRoundThunk<u32>); break;
|
||||
case 16: code.CallFunction(EmitFPRoundThunk<u16>); break;
|
||||
default: UNREACHABLE();
|
||||
if (fsize == 64) {
|
||||
FPTwoOp<64>(code, ctx, inst, [&](Xbyak::Xmm result) {
|
||||
code.roundsd(result, result, *round_imm);
|
||||
});
|
||||
} else {
|
||||
FPTwoOp<32>(code, ctx, inst, [&](Xbyak::Xmm result) {
|
||||
code.roundss(result, result, *round_imm);
|
||||
});
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
using fsize_list = mp::list<mp::lift_value<size_t(16)>,
|
||||
mp::lift_value<size_t(32)>,
|
||||
mp::lift_value<size_t(64)>>;
|
||||
using rounding_list = mp::list<
|
||||
mp::lift_value<FP::RoundingMode::ToNearest_TieEven>,
|
||||
mp::lift_value<FP::RoundingMode::TowardsPlusInfinity>,
|
||||
mp::lift_value<FP::RoundingMode::TowardsMinusInfinity>,
|
||||
mp::lift_value<FP::RoundingMode::TowardsZero>,
|
||||
mp::lift_value<FP::RoundingMode::ToNearest_TieAwayFromZero>>;
|
||||
using exact_list = mp::list<std::true_type, std::false_type>;
|
||||
|
||||
static const auto lut = Common::GenerateLookupTableFromList(
|
||||
[]<typename I>(I) {
|
||||
return std::pair{
|
||||
mp::lower_to_tuple_v<I>,
|
||||
Common::FptrCast(
|
||||
[](u64 input, FP::FPSR& fpsr, FP::FPCR fpcr) {
|
||||
constexpr size_t fsize = mp::get<0, I>::value;
|
||||
constexpr FP::RoundingMode rounding_mode = mp::get<1, I>::value;
|
||||
constexpr bool exact = mp::get<2, I>::value;
|
||||
using InputSize = mcl::unsigned_integer_of_size<fsize>;
|
||||
|
||||
return FP::FPRoundInt<InputSize>(static_cast<InputSize>(input), fpcr, rounding_mode, exact, fpsr);
|
||||
})};
|
||||
},
|
||||
mp::cartesian_product<fsize_list, rounding_list, exact_list>{});
|
||||
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
ctx.reg_alloc.HostCall(inst, args[0]);
|
||||
code.lea(code.ABI_PARAM2, code.ptr[code.ABI_JIT_PTR + code.GetJitStateInfo().offsetof_fpsr_exc]);
|
||||
code.mov(code.ABI_PARAM3.cvt32(), ctx.FPCR().Value());
|
||||
code.CallFunction(lut.at(std::make_tuple(fsize, rounding_mode, exact)));
|
||||
}
|
||||
|
||||
void EmitX64::EmitFPRoundInt16(EmitContext& ctx, IR::Inst* inst) {
|
||||
|
|
@ -1606,29 +1621,12 @@ void EmitX64::EmitFPDoubleToSingle(EmitContext& ctx, IR::Inst* inst) {
|
|||
}
|
||||
}
|
||||
|
||||
/// @brief Thunk used in assembly
|
||||
/// The extra args is conformed as follows:
|
||||
/// 0-7 fbits
|
||||
/// 8-15 rounding
|
||||
/// 16-23 isize
|
||||
/// 24-31 unsigned_ (!= 0)
|
||||
/// Better than spamming thousands of templates aye?
|
||||
template<size_t fsize>
|
||||
static u64 EmitFPToFixedThunk(u64 input, FP::FPSR& fpsr, FP::FPCR fpcr, u32 extra_args) {
|
||||
using FPT = mcl::unsigned_integer_of_size<fsize>;
|
||||
auto const unsigned_ = ((extra_args >> 24) & 0xff) != 0;
|
||||
auto const isize = ((extra_args >> 16) & 0xff);
|
||||
auto const rounding = FP::RoundingMode((extra_args >> 8) & 0xff);
|
||||
auto const fbits = ((extra_args >> 0) & 0xff);
|
||||
return FP::FPToFixed<FPT>(isize, FPT(input), fbits, unsigned_, fpcr, rounding, fpsr);
|
||||
}
|
||||
|
||||
template<size_t fsize, bool unsigned_, size_t isize>
|
||||
static void EmitFPToFixed(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
|
||||
const size_t fbits = args[1].GetImmediateU8();
|
||||
const auto rounding_mode = FP::RoundingMode(args[2].GetImmediateU8());
|
||||
const auto rounding_mode = static_cast<FP::RoundingMode>(args[2].GetImmediateU8());
|
||||
|
||||
if constexpr (fsize != 16) {
|
||||
const auto round_imm = ConvertRoundingModeToX64Immediate(rounding_mode);
|
||||
|
|
@ -1734,14 +1732,34 @@ static void EmitFPToFixed(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
|
|||
return;
|
||||
}
|
||||
}
|
||||
// See EmitFPToFixedThunk
|
||||
auto const extra_args = (u32(unsigned_) << 24) | (u32(isize) << 16)
|
||||
| (u32(rounding_mode) << 8) | (u32(fbits));
|
||||
|
||||
using fbits_list = mp::lift_sequence<std::make_index_sequence<isize + 1>>;
|
||||
using rounding_list = mp::list<
|
||||
mp::lift_value<FP::RoundingMode::ToNearest_TieEven>,
|
||||
mp::lift_value<FP::RoundingMode::TowardsPlusInfinity>,
|
||||
mp::lift_value<FP::RoundingMode::TowardsMinusInfinity>,
|
||||
mp::lift_value<FP::RoundingMode::TowardsZero>,
|
||||
mp::lift_value<FP::RoundingMode::ToNearest_TieAwayFromZero>>;
|
||||
|
||||
static const auto lut = Common::GenerateLookupTableFromList(
|
||||
[]<typename I>(I) {
|
||||
return std::pair{
|
||||
mp::lower_to_tuple_v<I>,
|
||||
Common::FptrCast(
|
||||
[](u64 input, FP::FPSR& fpsr, FP::FPCR fpcr) {
|
||||
constexpr size_t fbits = mp::get<0, I>::value;
|
||||
constexpr FP::RoundingMode rounding_mode = mp::get<1, I>::value;
|
||||
using FPT = mcl::unsigned_integer_of_size<fsize>;
|
||||
|
||||
return FP::FPToFixed<FPT>(isize, static_cast<FPT>(input), fbits, unsigned_, fpcr, rounding_mode, fpsr);
|
||||
})};
|
||||
},
|
||||
mp::cartesian_product<fbits_list, rounding_list>{});
|
||||
|
||||
ctx.reg_alloc.HostCall(inst, args[0]);
|
||||
code.lea(code.ABI_PARAM2, code.ptr[code.ABI_JIT_PTR + code.GetJitStateInfo().offsetof_fpsr_exc]);
|
||||
code.mov(code.ABI_PARAM3.cvt32(), ctx.FPCR().Value());
|
||||
code.mov(code.ABI_PARAM4.cvt32(), extra_args);
|
||||
code.CallFunction(EmitFPToFixedThunk<fsize>);
|
||||
code.CallFunction(lut.at(std::make_tuple(fbits, rounding_mode)));
|
||||
}
|
||||
|
||||
void EmitX64::EmitFPDoubleToFixedS16(EmitContext& ctx, IR::Inst* inst) {
|
||||
|
|
|
|||
|
|
@ -439,9 +439,9 @@ void EmitThreeOpVectorOperation(BlockOfCode& code, EmitContext& ctx, IR::Inst* i
|
|||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
void EmitTwoOpFallbackWithoutRegAlloc(BlockOfCode& code, EmitContext& ctx, Xbyak::Xmm result, Xbyak::Xmm arg1, F lambda, bool fpcr_controlled) {
|
||||
const auto fn = static_cast<mcl::equivalent_function_type<F>*>(lambda);
|
||||
template<typename Lambda>
|
||||
void EmitTwoOpFallbackWithoutRegAlloc(BlockOfCode& code, EmitContext& ctx, Xbyak::Xmm result, Xbyak::Xmm arg1, Lambda lambda, bool fpcr_controlled) {
|
||||
const auto fn = static_cast<mcl::equivalent_function_type<Lambda>*>(lambda);
|
||||
|
||||
const u32 fpcr = ctx.FPCR(fpcr_controlled).Value();
|
||||
|
||||
|
|
@ -459,8 +459,8 @@ void EmitTwoOpFallbackWithoutRegAlloc(BlockOfCode& code, EmitContext& ctx, Xbyak
|
|||
code.add(rsp, stack_space + ABI_SHADOW_SPACE);
|
||||
}
|
||||
|
||||
template<size_t fpcr_controlled_arg_index = 1, typename F>
|
||||
void EmitTwoOpFallback(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst, F lambda) {
|
||||
template<size_t fpcr_controlled_arg_index = 1, typename Lambda>
|
||||
void EmitTwoOpFallback(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst, Lambda lambda) {
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
const Xbyak::Xmm arg1 = ctx.reg_alloc.UseXmm(args[0]);
|
||||
const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm();
|
||||
|
|
@ -665,14 +665,8 @@ void EmitX64::EmitFPVectorEqual64(EmitContext& ctx, IR::Inst* inst) {
|
|||
ctx.reg_alloc.DefineValue(inst, a);
|
||||
}
|
||||
|
||||
template<FP::RoundingMode rounding_mode>
|
||||
static void EmitFPVectorFromHalf32Thunk(VectorArray<u32>& output, const VectorArray<u16>& input, FP::FPCR fpcr, FP::FPSR& fpsr) {
|
||||
for (size_t i = 0; i < output.size(); ++i)
|
||||
output[i] = FP::FPConvert<u32, u16>(input[i], fpcr, rounding_mode, fpsr);
|
||||
}
|
||||
|
||||
void EmitX64::EmitFPVectorFromHalf32(EmitContext& ctx, IR::Inst* inst) {
|
||||
const auto rounding_mode = FP::RoundingMode(inst->GetArg(1).GetU8());
|
||||
const auto rounding_mode = static_cast<FP::RoundingMode>(inst->GetArg(1).GetU8());
|
||||
const bool fpcr_controlled = inst->GetArg(2).GetU1();
|
||||
|
||||
if (code.HasHostFeature(HostFeature::F16C) && !ctx.FPCR().AHP() && !ctx.FPCR().FZ16()) {
|
||||
|
|
@ -685,27 +679,32 @@ void EmitX64::EmitFPVectorFromHalf32(EmitContext& ctx, IR::Inst* inst) {
|
|||
ForceToDefaultNaN<32>(code, ctx.FPCR(fpcr_controlled), result);
|
||||
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
} else {
|
||||
switch (rounding_mode) {
|
||||
case FP::RoundingMode::ToNearest_TieEven:
|
||||
EmitTwoOpFallback<2>(code, ctx, inst, EmitFPVectorFromHalf32Thunk<FP::RoundingMode::ToNearest_TieEven>);
|
||||
break;
|
||||
case FP::RoundingMode::TowardsPlusInfinity:
|
||||
EmitTwoOpFallback<2>(code, ctx, inst, EmitFPVectorFromHalf32Thunk<FP::RoundingMode::TowardsPlusInfinity>);
|
||||
break;
|
||||
case FP::RoundingMode::TowardsMinusInfinity:
|
||||
EmitTwoOpFallback<2>(code, ctx, inst, EmitFPVectorFromHalf32Thunk<FP::RoundingMode::TowardsMinusInfinity>);
|
||||
break;
|
||||
case FP::RoundingMode::TowardsZero:
|
||||
EmitTwoOpFallback<2>(code, ctx, inst, EmitFPVectorFromHalf32Thunk<FP::RoundingMode::TowardsZero>);
|
||||
break;
|
||||
case FP::RoundingMode::ToNearest_TieAwayFromZero:
|
||||
EmitTwoOpFallback<2>(code, ctx, inst, EmitFPVectorFromHalf32Thunk<FP::RoundingMode::ToNearest_TieAwayFromZero>);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
using rounding_list = mp::list<
|
||||
mp::lift_value<FP::RoundingMode::ToNearest_TieEven>,
|
||||
mp::lift_value<FP::RoundingMode::TowardsPlusInfinity>,
|
||||
mp::lift_value<FP::RoundingMode::TowardsMinusInfinity>,
|
||||
mp::lift_value<FP::RoundingMode::TowardsZero>,
|
||||
mp::lift_value<FP::RoundingMode::ToNearest_TieAwayFromZero>>;
|
||||
|
||||
static const auto lut = Common::GenerateLookupTableFromList(
|
||||
[]<typename I>(I) {
|
||||
return std::pair{
|
||||
mp::lower_to_tuple_v<I>,
|
||||
Common::FptrCast(
|
||||
[](VectorArray<u32>& output, const VectorArray<u16>& input, FP::FPCR fpcr, FP::FPSR& fpsr) {
|
||||
constexpr FP::RoundingMode rounding_mode = mp::get<0, I>::value;
|
||||
|
||||
for (size_t i = 0; i < output.size(); ++i) {
|
||||
output[i] = FP::FPConvert<u32, u16>(input[i], fpcr, rounding_mode, fpsr);
|
||||
}
|
||||
})};
|
||||
},
|
||||
mp::cartesian_product<rounding_list>{});
|
||||
|
||||
EmitTwoOpFallback<2>(code, ctx, inst, lut.at(std::make_tuple(rounding_mode)));
|
||||
}
|
||||
|
||||
void EmitX64::EmitFPVectorFromSignedFixed32(EmitContext& ctx, IR::Inst* inst) {
|
||||
|
|
@ -1682,26 +1681,25 @@ void EmitX64::EmitFPVectorRecipStepFused64(EmitContext& ctx, IR::Inst* inst) {
|
|||
EmitRecipStepFused<64>(code, ctx, inst);
|
||||
}
|
||||
|
||||
template<typename FPT, FP::RoundingMode rounding_mode, bool exact>
|
||||
static void EmitFPVectorRoundIntThunk(VectorArray<FPT>& output, const VectorArray<FPT>& input, FP::FPCR fpcr, FP::FPSR& fpsr) {
|
||||
for (size_t i = 0; i < output.size(); ++i)
|
||||
output[i] = FPT(FP::FPRoundInt<FPT>(input[i], fpcr, rounding_mode, exact, fpsr));
|
||||
}
|
||||
|
||||
template<size_t fsize>
|
||||
void EmitFPVectorRoundInt(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
const auto rounding = FP::RoundingMode(inst->GetArg(1).GetU8());
|
||||
const auto rounding = static_cast<FP::RoundingMode>(inst->GetArg(1).GetU8());
|
||||
const bool exact = inst->GetArg(2).GetU1();
|
||||
|
||||
if constexpr (fsize != 16) {
|
||||
if (code.HasHostFeature(HostFeature::SSE41) && rounding != FP::RoundingMode::ToNearest_TieAwayFromZero && !exact) {
|
||||
const u8 round_imm = [&]() -> u8 {
|
||||
switch (rounding) {
|
||||
case FP::RoundingMode::ToNearest_TieEven: return 0b00;
|
||||
case FP::RoundingMode::TowardsPlusInfinity: return 0b10;
|
||||
case FP::RoundingMode::TowardsMinusInfinity: return 0b01;
|
||||
case FP::RoundingMode::TowardsZero: return 0b11;
|
||||
default: UNREACHABLE();
|
||||
case FP::RoundingMode::ToNearest_TieEven:
|
||||
return 0b00;
|
||||
case FP::RoundingMode::TowardsPlusInfinity:
|
||||
return 0b10;
|
||||
case FP::RoundingMode::TowardsMinusInfinity:
|
||||
return 0b01;
|
||||
case FP::RoundingMode::TowardsZero:
|
||||
return 0b11;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}();
|
||||
|
||||
|
|
@ -1713,37 +1711,32 @@ void EmitFPVectorRoundInt(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
|
|||
}
|
||||
}
|
||||
|
||||
// Do not make a LUT out of this, let the compiler do it's thing
|
||||
using FPT = mcl::unsigned_integer_of_size<fsize>;
|
||||
switch (rounding) {
|
||||
case FP::RoundingMode::ToNearest_TieEven:
|
||||
exact
|
||||
? EmitTwoOpFallback<3>(code, ctx, inst, EmitFPVectorRoundIntThunk<FPT, FP::RoundingMode::ToNearest_TieEven, true>)
|
||||
: EmitTwoOpFallback<3>(code, ctx, inst, EmitFPVectorRoundIntThunk<FPT, FP::RoundingMode::ToNearest_TieEven, false>);
|
||||
break;
|
||||
case FP::RoundingMode::TowardsPlusInfinity:
|
||||
exact
|
||||
? EmitTwoOpFallback<3>(code, ctx, inst, EmitFPVectorRoundIntThunk<FPT, FP::RoundingMode::TowardsPlusInfinity, true>)
|
||||
: EmitTwoOpFallback<3>(code, ctx, inst, EmitFPVectorRoundIntThunk<FPT, FP::RoundingMode::TowardsPlusInfinity, false>);
|
||||
break;
|
||||
case FP::RoundingMode::TowardsMinusInfinity:
|
||||
exact
|
||||
? EmitTwoOpFallback<3>(code, ctx, inst, EmitFPVectorRoundIntThunk<FPT, FP::RoundingMode::TowardsMinusInfinity, true>)
|
||||
: EmitTwoOpFallback<3>(code, ctx, inst, EmitFPVectorRoundIntThunk<FPT, FP::RoundingMode::TowardsMinusInfinity, false>);
|
||||
break;
|
||||
case FP::RoundingMode::TowardsZero:
|
||||
exact
|
||||
? EmitTwoOpFallback<3>(code, ctx, inst, EmitFPVectorRoundIntThunk<FPT, FP::RoundingMode::TowardsZero, true>)
|
||||
: EmitTwoOpFallback<3>(code, ctx, inst, EmitFPVectorRoundIntThunk<FPT, FP::RoundingMode::TowardsZero, false>);
|
||||
break;
|
||||
case FP::RoundingMode::ToNearest_TieAwayFromZero:
|
||||
exact
|
||||
? EmitTwoOpFallback<3>(code, ctx, inst, EmitFPVectorRoundIntThunk<FPT, FP::RoundingMode::ToNearest_TieAwayFromZero, true>)
|
||||
: EmitTwoOpFallback<3>(code, ctx, inst, EmitFPVectorRoundIntThunk<FPT, FP::RoundingMode::ToNearest_TieAwayFromZero, false>);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
using rounding_list = mp::list<
|
||||
mp::lift_value<FP::RoundingMode::ToNearest_TieEven>,
|
||||
mp::lift_value<FP::RoundingMode::TowardsPlusInfinity>,
|
||||
mp::lift_value<FP::RoundingMode::TowardsMinusInfinity>,
|
||||
mp::lift_value<FP::RoundingMode::TowardsZero>,
|
||||
mp::lift_value<FP::RoundingMode::ToNearest_TieAwayFromZero>>;
|
||||
using exact_list = mp::list<std::true_type, std::false_type>;
|
||||
|
||||
static const auto lut = Common::GenerateLookupTableFromList(
|
||||
[]<typename I>(I) {
|
||||
using FPT = mcl::unsigned_integer_of_size<fsize>; // WORKAROUND: For issue 678 on MSVC
|
||||
return std::pair{
|
||||
mp::lower_to_tuple_v<I>,
|
||||
Common::FptrCast(
|
||||
[](VectorArray<FPT>& output, const VectorArray<FPT>& input, FP::FPCR fpcr, FP::FPSR& fpsr) {
|
||||
constexpr FP::RoundingMode rounding_mode = mp::get<0, I>::value;
|
||||
constexpr bool exact = mp::get<1, I>::value;
|
||||
|
||||
for (size_t i = 0; i < output.size(); ++i) {
|
||||
output[i] = static_cast<FPT>(FP::FPRoundInt<FPT>(input[i], fpcr, rounding_mode, exact, fpsr));
|
||||
}
|
||||
})};
|
||||
},
|
||||
mp::cartesian_product<rounding_list, exact_list>{});
|
||||
|
||||
EmitTwoOpFallback<3>(code, ctx, inst, lut.at(std::make_tuple(rounding, exact)));
|
||||
}
|
||||
|
||||
void EmitX64::EmitFPVectorRoundInt16(EmitContext& ctx, IR::Inst* inst) {
|
||||
|
|
@ -1972,14 +1965,8 @@ void EmitX64::EmitFPVectorSub64(EmitContext& ctx, IR::Inst* inst) {
|
|||
EmitThreeOpVectorOperation<64, DefaultIndexer>(code, ctx, inst, &Xbyak::CodeGenerator::subpd);
|
||||
}
|
||||
|
||||
template<FP::RoundingMode rounding_mode>
|
||||
static void EmitFPVectorToHalf32Thunk(VectorArray<u16>& output, const VectorArray<u32>& input, FP::FPCR fpcr, FP::FPSR& fpsr) {
|
||||
for (size_t i = 0; i < output.size(); ++i)
|
||||
output[i] = i < input.size() ? FP::FPConvert<u16, u32>(input[i], fpcr, rounding_mode, fpsr) : 0;
|
||||
}
|
||||
|
||||
void EmitX64::EmitFPVectorToHalf32(EmitContext& ctx, IR::Inst* inst) {
|
||||
const auto rounding_mode = FP::RoundingMode(inst->GetArg(1).GetU8());
|
||||
const auto rounding_mode = static_cast<FP::RoundingMode>(inst->GetArg(1).GetU8());
|
||||
const bool fpcr_controlled = inst->GetArg(2).GetU1();
|
||||
|
||||
if (code.HasHostFeature(HostFeature::F16C) && !ctx.FPCR().AHP() && !ctx.FPCR().FZ16()) {
|
||||
|
|
@ -1989,38 +1976,40 @@ void EmitX64::EmitFPVectorToHalf32(EmitContext& ctx, IR::Inst* inst) {
|
|||
const Xbyak::Xmm result = ctx.reg_alloc.UseScratchXmm(args[0]);
|
||||
|
||||
ForceToDefaultNaN<32>(code, ctx.FPCR(fpcr_controlled), result);
|
||||
code.vcvtps2ph(result, result, u8(*round_imm));
|
||||
code.vcvtps2ph(result, result, static_cast<u8>(*round_imm));
|
||||
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
} else {
|
||||
switch (rounding_mode) {
|
||||
case FP::RoundingMode::ToNearest_TieEven:
|
||||
EmitTwoOpFallback<2>(code, ctx, inst, EmitFPVectorToHalf32Thunk<FP::RoundingMode::ToNearest_TieEven>);
|
||||
break;
|
||||
case FP::RoundingMode::TowardsPlusInfinity:
|
||||
EmitTwoOpFallback<2>(code, ctx, inst, EmitFPVectorToHalf32Thunk<FP::RoundingMode::TowardsPlusInfinity>);
|
||||
break;
|
||||
case FP::RoundingMode::TowardsMinusInfinity:
|
||||
EmitTwoOpFallback<2>(code, ctx, inst, EmitFPVectorToHalf32Thunk<FP::RoundingMode::TowardsMinusInfinity>);
|
||||
break;
|
||||
case FP::RoundingMode::TowardsZero:
|
||||
EmitTwoOpFallback<2>(code, ctx, inst, EmitFPVectorToHalf32Thunk<FP::RoundingMode::TowardsZero>);
|
||||
break;
|
||||
case FP::RoundingMode::ToNearest_TieAwayFromZero:
|
||||
EmitTwoOpFallback<2>(code, ctx, inst, EmitFPVectorToHalf32Thunk<FP::RoundingMode::ToNearest_TieAwayFromZero>);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Assembly thunk; just remember not to specialise too much otherwise i-cache death!
|
||||
// template<typename FPT, size_t fbits, FP::RoundingMode rounding_mode>
|
||||
// static void EmitFPVectorToFixedThunk(VectorArray<FPT>& output, const VectorArray<FPT>& input, FP::FPCR fpcr, FP::FPSR& fpsr) {
|
||||
// for (size_t i = 0; i < output.size(); ++i)
|
||||
// output[i] = FPT(FP::FPToFixed<FPT>(fsize, input[i], fbits, unsigned_, fpcr, rounding_mode, fpsr));
|
||||
// }
|
||||
using rounding_list = mp::list<
|
||||
mp::lift_value<FP::RoundingMode::ToNearest_TieEven>,
|
||||
mp::lift_value<FP::RoundingMode::TowardsPlusInfinity>,
|
||||
mp::lift_value<FP::RoundingMode::TowardsMinusInfinity>,
|
||||
mp::lift_value<FP::RoundingMode::TowardsZero>,
|
||||
mp::lift_value<FP::RoundingMode::ToNearest_TieAwayFromZero>>;
|
||||
|
||||
static const auto lut = Common::GenerateLookupTableFromList(
|
||||
[]<typename I>(I) {
|
||||
return std::pair{
|
||||
mp::lower_to_tuple_v<I>,
|
||||
Common::FptrCast(
|
||||
[](VectorArray<u16>& output, const VectorArray<u32>& input, FP::FPCR fpcr, FP::FPSR& fpsr) {
|
||||
constexpr FP::RoundingMode rounding_mode = mp::get<0, I>::value;
|
||||
|
||||
for (size_t i = 0; i < output.size(); ++i) {
|
||||
if (i < input.size()) {
|
||||
output[i] = FP::FPConvert<u16, u32>(input[i], fpcr, rounding_mode, fpsr);
|
||||
} else {
|
||||
output[i] = 0;
|
||||
}
|
||||
}
|
||||
})};
|
||||
},
|
||||
mp::cartesian_product<rounding_list>{});
|
||||
|
||||
EmitTwoOpFallback<2>(code, ctx, inst, lut.at(std::make_tuple(rounding_mode)));
|
||||
}
|
||||
|
||||
template<size_t fsize, bool unsigned_>
|
||||
void EmitFPVectorToFixed(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
|
|
@ -2119,7 +2108,7 @@ void EmitFPVectorToFixed(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
|
|||
FCODE(andp)(tmp, xmm0);
|
||||
FCODE(subp)(src, tmp);
|
||||
perform_conversion(src);
|
||||
ICODE(psll)(xmm0, u8(fsize - 1));
|
||||
ICODE(psll)(xmm0, static_cast<u8>(fsize - 1));
|
||||
FCODE(orp)(src, xmm0);
|
||||
|
||||
// Saturate to max
|
||||
|
|
@ -2127,7 +2116,7 @@ void EmitFPVectorToFixed(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
|
|||
}
|
||||
} else {
|
||||
using FPT = mcl::unsigned_integer_of_size<fsize>; // WORKAROUND: For issue 678 on MSVC
|
||||
constexpr u64 integer_max = FPT((std::numeric_limits<std::conditional_t<unsigned_, FPT, std::make_signed_t<FPT>>>::max)());
|
||||
constexpr u64 integer_max = static_cast<FPT>((std::numeric_limits<std::conditional_t<unsigned_, FPT, std::make_signed_t<FPT>>>::max)());
|
||||
|
||||
code.movaps(xmm0, GetVectorOf<fsize, float_upper_limit_signed>(code));
|
||||
FCODE(cmplep)(xmm0, src);
|
||||
|
|
@ -2149,18 +2138,22 @@ void EmitFPVectorToFixed(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
|
|||
mp::lift_value<FP::RoundingMode::TowardsZero>,
|
||||
mp::lift_value<FP::RoundingMode::ToNearest_TieAwayFromZero>>;
|
||||
|
||||
static const auto lut = Common::GenerateLookupTableFromList([]<typename I>(I) {
|
||||
using FPT = mcl::unsigned_integer_of_size<fsize>; // WORKAROUND: For issue 678 on MSVC
|
||||
return std::pair{
|
||||
mp::lower_to_tuple_v<I>,
|
||||
Common::FptrCast([](VectorArray<FPT>& output, const VectorArray<FPT>& input, FP::FPCR fpcr, FP::FPSR& fpsr) {
|
||||
constexpr size_t fbits = mp::get<0, I>::value;
|
||||
constexpr FP::RoundingMode rounding_mode = mp::get<1, I>::value;
|
||||
for (size_t i = 0; i < output.size(); ++i)
|
||||
output[i] = FPT(FP::FPToFixed<FPT>(fsize, input[i], fbits, unsigned_, fpcr, rounding_mode, fpsr));
|
||||
})
|
||||
};
|
||||
}, mp::cartesian_product<fbits_list, rounding_list>{});
|
||||
static const auto lut = Common::GenerateLookupTableFromList(
|
||||
[]<typename I>(I) {
|
||||
using FPT = mcl::unsigned_integer_of_size<fsize>; // WORKAROUND: For issue 678 on MSVC
|
||||
return std::pair{
|
||||
mp::lower_to_tuple_v<I>,
|
||||
Common::FptrCast(
|
||||
[](VectorArray<FPT>& output, const VectorArray<FPT>& input, FP::FPCR fpcr, FP::FPSR& fpsr) {
|
||||
constexpr size_t fbits = mp::get<0, I>::value;
|
||||
constexpr FP::RoundingMode rounding_mode = mp::get<1, I>::value;
|
||||
|
||||
for (size_t i = 0; i < output.size(); ++i) {
|
||||
output[i] = static_cast<FPT>(FP::FPToFixed<FPT>(fsize, input[i], fbits, unsigned_, fpcr, rounding_mode, fpsr));
|
||||
}
|
||||
})};
|
||||
},
|
||||
mp::cartesian_product<fbits_list, rounding_list>{});
|
||||
|
||||
EmitTwoOpFallback<3>(code, ctx, inst, lut.at(std::make_tuple(fbits, rounding)));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,3 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2018 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
|
|
|
|||
|
|
@ -40,7 +40,9 @@ inline auto GenerateLookupTableFromList(Function f, mcl::mp::list<Values...>) {
|
|||
using PairT = std::common_type_t<std::invoke_result_t<Function, Values>...>;
|
||||
#endif
|
||||
using MapT = mcl::mp::apply<std::map, PairT>;
|
||||
|
||||
static_assert(mcl::is_instance_of_template_v<std::pair, PairT>);
|
||||
|
||||
const std::initializer_list<PairT> pair_array{f(Values{})...};
|
||||
return MapT(pair_array.begin(), pair_array.end());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,3 @@
|
|||
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# SPDX-FileCopyrightText: 2018 yuzu Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -86,7 +83,7 @@ if (ENABLE_LIBUSB)
|
|||
drivers/gc_adapter.h
|
||||
)
|
||||
target_link_libraries(input_common PRIVATE libusb::usb)
|
||||
target_compile_definitions(input_common PRIVATE ENABLE_LIBUSB)
|
||||
target_compile_definitions(input_common PRIVATE HAVE_LIBUSB)
|
||||
endif()
|
||||
|
||||
create_target_directory_groups(input_common)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,3 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2017 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -22,7 +19,7 @@
|
|||
#include "input_common/input_poller.h"
|
||||
#include "input_common/main.h"
|
||||
|
||||
#ifdef ENABLE_LIBUSB
|
||||
#ifdef HAVE_LIBUSB
|
||||
#include "input_common/drivers/gc_adapter.h"
|
||||
#endif
|
||||
#ifdef HAVE_SDL2
|
||||
|
|
@ -79,7 +76,7 @@ struct InputSubsystem::Impl {
|
|||
RegisterEngine("keyboard", keyboard);
|
||||
RegisterEngine("mouse", mouse);
|
||||
RegisterEngine("touch", touch_screen);
|
||||
#ifdef ENABLE_LIBUSB
|
||||
#ifdef HAVE_LIBUSB
|
||||
RegisterEngine("gcpad", gcadapter);
|
||||
#endif
|
||||
RegisterEngine("cemuhookudp", udp_client);
|
||||
|
|
@ -113,7 +110,7 @@ struct InputSubsystem::Impl {
|
|||
UnregisterEngine(keyboard);
|
||||
UnregisterEngine(mouse);
|
||||
UnregisterEngine(touch_screen);
|
||||
#ifdef ENABLE_LIBUSB
|
||||
#ifdef HAVE_LIBUSB
|
||||
UnregisterEngine(gcadapter);
|
||||
#endif
|
||||
UnregisterEngine(udp_client);
|
||||
|
|
@ -148,7 +145,7 @@ struct InputSubsystem::Impl {
|
|||
auto android_devices = android->GetInputDevices();
|
||||
devices.insert(devices.end(), android_devices.begin(), android_devices.end());
|
||||
#endif
|
||||
#ifdef ENABLE_LIBUSB
|
||||
#ifdef HAVE_LIBUSB
|
||||
auto gcadapter_devices = gcadapter->GetInputDevices();
|
||||
devices.insert(devices.end(), gcadapter_devices.begin(), gcadapter_devices.end());
|
||||
#endif
|
||||
|
|
@ -181,7 +178,7 @@ struct InputSubsystem::Impl {
|
|||
return android;
|
||||
}
|
||||
#endif
|
||||
#ifdef ENABLE_LIBUSB
|
||||
#ifdef HAVE_LIBUSB
|
||||
if (engine == gcadapter->GetEngineName()) {
|
||||
return gcadapter;
|
||||
}
|
||||
|
|
@ -266,7 +263,7 @@ struct InputSubsystem::Impl {
|
|||
return true;
|
||||
}
|
||||
#endif
|
||||
#ifdef ENABLE_LIBUSB
|
||||
#ifdef HAVE_LIBUSB
|
||||
if (engine == gcadapter->GetEngineName()) {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -297,7 +294,7 @@ struct InputSubsystem::Impl {
|
|||
#ifdef ANDROID
|
||||
android->BeginConfiguration();
|
||||
#endif
|
||||
#ifdef ENABLE_LIBUSB
|
||||
#ifdef HAVE_LIBUSB
|
||||
gcadapter->BeginConfiguration();
|
||||
#endif
|
||||
udp_client->BeginConfiguration();
|
||||
|
|
@ -313,7 +310,7 @@ struct InputSubsystem::Impl {
|
|||
#ifdef ANDROID
|
||||
android->EndConfiguration();
|
||||
#endif
|
||||
#ifdef ENABLE_LIBUSB
|
||||
#ifdef HAVE_LIBUSB
|
||||
gcadapter->EndConfiguration();
|
||||
#endif
|
||||
udp_client->EndConfiguration();
|
||||
|
|
@ -346,7 +343,7 @@ struct InputSubsystem::Impl {
|
|||
std::shared_ptr<VirtualAmiibo> virtual_amiibo;
|
||||
std::shared_ptr<VirtualGamepad> virtual_gamepad;
|
||||
|
||||
#ifdef ENABLE_LIBUSB
|
||||
#ifdef HAVE_LIBUSB
|
||||
std::shared_ptr<GCAdapter> gcadapter;
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -78,11 +78,11 @@ IR::Value MakeCoords(TranslatorVisitor& v, IR::Reg reg, Type type) {
|
|||
case Type::_1D:
|
||||
case Type::_1D_BUFFER:
|
||||
return v.X(reg);
|
||||
case Type::_1D_ARRAY:
|
||||
case Type::ARRAY_1D:
|
||||
return v.ir.CompositeConstruct(v.X(reg), array(1));
|
||||
case Type::_2D:
|
||||
return v.ir.CompositeConstruct(v.X(reg), v.X(reg + 1));
|
||||
case Type::_2D_ARRAY:
|
||||
case Type::ARRAY_2D:
|
||||
return v.ir.CompositeConstruct(v.X(reg), v.X(reg + 1), array(2));
|
||||
case Type::_3D:
|
||||
return v.ir.CompositeConstruct(v.X(reg), v.X(reg + 1), v.X(reg + 2));
|
||||
|
|
|
|||
|
|
@ -243,7 +243,7 @@ Device::Device(Core::Frontend::EmuWindow& emu_window) {
|
|||
}
|
||||
|
||||
if (shader_backend == Settings::ShaderBackend::Glsl && is_nvidia) {
|
||||
const std::string driver_version = version.substr(13);
|
||||
const std::string_view driver_version = version.substr(13);
|
||||
const int version_major =
|
||||
std::atoi(driver_version.substr(0, driver_version.find(".")).data());
|
||||
if (version_major >= 495) {
|
||||
|
|
|
|||
|
|
@ -39,20 +39,30 @@ void ConfigureDebug::SetConfiguration() {
|
|||
ui->toggle_gdbstub->setChecked(Settings::values.use_gdbstub.GetValue());
|
||||
ui->gdbport_spinbox->setEnabled(Settings::values.use_gdbstub.GetValue());
|
||||
ui->gdbport_spinbox->setValue(Settings::values.gdbstub_port.GetValue());
|
||||
ui->toggle_console->setEnabled(runtime_lock);
|
||||
ui->toggle_console->setChecked(UISettings::values.show_console.GetValue());
|
||||
ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter.GetValue()));
|
||||
ui->flush_line->setChecked(Settings::values.log_flush_line.GetValue());
|
||||
ui->censor_username->setChecked(Settings::values.censor_username.GetValue());
|
||||
ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args.GetValue()));
|
||||
ui->fs_access_log->setEnabled(runtime_lock);
|
||||
ui->fs_access_log->setChecked(Settings::values.enable_fs_access_log.GetValue());
|
||||
ui->reporting_services->setChecked(Settings::values.reporting_services.GetValue());
|
||||
ui->dump_audio_commands->setChecked(Settings::values.dump_audio_commands.GetValue());
|
||||
ui->quest_flag->setChecked(Settings::values.quest_flag.GetValue());
|
||||
ui->use_debug_asserts->setChecked(Settings::values.use_debug_asserts.GetValue());
|
||||
ui->use_auto_stub->setChecked(Settings::values.use_auto_stub.GetValue());
|
||||
ui->enable_all_controllers->setChecked(Settings::values.enable_all_controllers.GetValue());
|
||||
ui->extended_logging->setChecked(Settings::values.extended_logging.GetValue());
|
||||
ui->perform_vulkan_check->setChecked(Settings::values.perform_vulkan_check.GetValue());
|
||||
ui->disable_web_applet->setChecked(Settings::values.disable_web_applet.GetValue());
|
||||
|
||||
// Immutable after starting
|
||||
ui->serial_battery_edit->setEnabled(runtime_lock);
|
||||
ui->serial_battery_edit->setText(QString::fromStdString(Settings::values.serial_battery.GetValue()));
|
||||
ui->serial_board_edit->setEnabled(runtime_lock);
|
||||
ui->serial_board_edit->setText(QString::fromStdString(Settings::values.serial_unit.GetValue()));
|
||||
ui->homebrew_args_edit->setEnabled(runtime_lock);
|
||||
ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args.GetValue()));
|
||||
ui->toggle_console->setEnabled(runtime_lock);
|
||||
ui->toggle_console->setChecked(UISettings::values.show_console.GetValue());
|
||||
ui->fs_access_log->setEnabled(runtime_lock);
|
||||
ui->fs_access_log->setChecked(Settings::values.enable_fs_access_log.GetValue());
|
||||
ui->enable_renderdoc_hotkey->setEnabled(runtime_lock);
|
||||
ui->enable_renderdoc_hotkey->setChecked(Settings::values.enable_renderdoc_hotkey.GetValue());
|
||||
ui->disable_buffer_reorder->setEnabled(runtime_lock);
|
||||
|
|
@ -73,9 +83,6 @@ void ConfigureDebug::SetConfiguration() {
|
|||
ui->disable_macro_hle->setChecked(Settings::values.disable_macro_hle.GetValue());
|
||||
ui->disable_loop_safety_checks->setEnabled(runtime_lock);
|
||||
ui->disable_loop_safety_checks->setChecked(Settings::values.disable_shader_loop_safety_checks.GetValue());
|
||||
ui->extended_logging->setChecked(Settings::values.extended_logging.GetValue());
|
||||
ui->perform_vulkan_check->setChecked(Settings::values.perform_vulkan_check.GetValue());
|
||||
ui->disable_web_applet->setChecked(Settings::values.disable_web_applet.GetValue());
|
||||
|
||||
#ifndef YUZU_USE_QT_WEB_ENGINE
|
||||
ui->disable_web_applet->setVisible(false);
|
||||
|
|
|
|||
|
|
@ -467,7 +467,87 @@
|
|||
<string>Debugging</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="6" column="0">
|
||||
<widget class="QWidget" name="serial_battery_widget" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_11">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_12">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Battery Serial:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="serial_battery_edit"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QWidget" name="serial_board_widget" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_12">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_13">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Unit Serial:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="serial_board_edit"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<spacer name="verticalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
|
|
@ -483,7 +563,7 @@
|
|||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="dump_audio_commands">
|
||||
<property name="toolTip">
|
||||
<string>Enable this to output the latest generated audio command list to the console. Only affects games using the audio renderer.</string>
|
||||
|
|
@ -493,14 +573,14 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="flush_line">
|
||||
<property name="text">
|
||||
<string>Flush log output on each line</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<item row="3" column="0">
|
||||
<widget class="QCheckBox" name="fs_access_log">
|
||||
<property name="text">
|
||||
<string>Enable FS Access Log</string>
|
||||
|
|
@ -514,7 +594,7 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<item row="5" column="0">
|
||||
<widget class="QCheckBox" name="censor_username">
|
||||
<property name="text">
|
||||
<string>Censor username in logs</string>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue