early-access version 3110

This commit is contained in:
pineappleEA 2022-11-11 10:49:01 +01:00
parent 8a24ed7aff
commit f4ff98b087
9 changed files with 126 additions and 53 deletions

View File

@ -1,7 +1,7 @@
yuzu emulator early access yuzu emulator early access
============= =============
This is the source code for early-access 3109. This is the source code for early-access 3110.
## Legal Notice ## Legal Notice

View File

@ -19,27 +19,26 @@ void EmulatedConsole::ReloadFromSettings() {
} }
void EmulatedConsole::SetTouchParams() { void EmulatedConsole::SetTouchParams() {
// TODO(german77): Support any number of fingers
std::size_t index = 0; std::size_t index = 0;
// Hardcode mouse, touchscreen and cemuhook parameters // We can't use mouse as touch if native mouse is enabled
if (!Settings::values.mouse_enabled) { if (!Settings::values.mouse_enabled) {
// We can't use mouse as touch if native mouse is enabled
touch_params[index++] = Common::ParamPackage{"engine:mouse,axis_x:10,axis_y:11,button:0"}; touch_params[index++] = Common::ParamPackage{"engine:mouse,axis_x:10,axis_y:11,button:0"};
} }
touch_params[index++] = touch_params[index++] =
Common::ParamPackage{"engine:touch,axis_x:0,axis_y:1,button:0,touch_id:0"}; Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536"};
touch_params[index++] = touch_params[index++] =
Common::ParamPackage{"engine:touch,axis_x:2,axis_y:3,button:1,touch_id:1"}; Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072"};
touch_params[index++] =
Common::ParamPackage{"engine:touch,axis_x:4,axis_y:5,button:2,touch_id:2"}; for (int i = 0; i < static_cast<int>(max_active_touch_inputs); i++) {
touch_params[index++] = Common::ParamPackage touchscreen_param{};
Common::ParamPackage{"engine:touch,axis_x:6,axis_y:7,button:3,touch_id:3"}; touchscreen_param.Set("engine", "touch_from_button");
touch_params[index++] = touchscreen_param.Set("axis_x", i * 2);
Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536,touch_id:0"}; touchscreen_param.Set("axis_y", (i * 2) + 1);
touch_params[index++] = touchscreen_param.Set("button", i);
Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072,touch_id:1"}; touch_params[index++] = touchscreen_param;
}
const auto button_index = const auto button_index =
static_cast<u64>(Settings::values.touch_from_button_map_index.GetValue()); static_cast<u64>(Settings::values.touch_from_button_map_index.GetValue());
@ -47,7 +46,7 @@ void EmulatedConsole::SetTouchParams() {
// Map the rest of the fingers from touch from button configuration // Map the rest of the fingers from touch from button configuration
for (const auto& config_entry : touch_buttons) { for (const auto& config_entry : touch_buttons) {
if (index >= touch_params.size()) { if (index >= max_touch_devices) {
continue; continue;
} }
Common::ParamPackage params{config_entry}; Common::ParamPackage params{config_entry};
@ -60,7 +59,6 @@ void EmulatedConsole::SetTouchParams() {
touch_button_params.Set("button", params.Serialize()); touch_button_params.Set("button", params.Serialize());
touch_button_params.Set("x", x); touch_button_params.Set("x", x);
touch_button_params.Set("y", y); touch_button_params.Set("y", y);
touch_button_params.Set("touch_id", static_cast<int>(index));
touch_params[index] = touch_button_params; touch_params[index] = touch_button_params;
index++; index++;
} }
@ -178,12 +176,38 @@ void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
} }
void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index) { void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index) {
if (index >= console.touch_values.size()) { if (index >= max_touch_devices) {
return; return;
} }
std::unique_lock lock{mutex}; std::unique_lock lock{mutex};
console.touch_values[index] = TransformToTouch(callback); const auto touch_input = TransformToTouch(callback);
auto touch_index = GetIndexFromFingerId(index);
bool is_new_input = false;
if (!touch_index.has_value() && touch_input.pressed.value) {
touch_index = GetNextFreeIndex();
is_new_input = true;
}
// No free entries or invalid state. Ignore input
if (!touch_index.has_value()) {
return;
}
auto& touch_value = console.touch_values[touch_index.value()];
if (is_new_input) {
touch_value.pressed.value = true;
touch_value.id = static_cast<u32>(index);
}
touch_value.x = touch_input.x;
touch_value.y = touch_input.y;
if (!touch_input.pressed.value) {
touch_value.pressed.value = false;
}
if (is_configuring) { if (is_configuring) {
lock.unlock(); lock.unlock();
@ -191,11 +215,15 @@ void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, st
return; return;
} }
// TODO(german77): Remap touch id in sequential order // Touch outside allowed range. Ignore input
console.touch_state[index] = { if (touch_index.value() >= max_active_touch_inputs) {
.position = {console.touch_values[index].x.value, console.touch_values[index].y.value}, return;
.id = static_cast<u32>(console.touch_values[index].id), }
.pressed = console.touch_values[index].pressed.value,
console.touch_state[touch_index.value()] = {
.position = {touch_value.x.value, touch_value.y.value},
.id = static_cast<u32>(touch_index.value()),
.pressed = touch_input.pressed.value,
}; };
lock.unlock(); lock.unlock();
@ -222,6 +250,28 @@ TouchFingerState EmulatedConsole::GetTouch() const {
return console.touch_state; return console.touch_state;
} }
std::optional<std::size_t> EmulatedConsole::GetIndexFromFingerId(std::size_t finger_id) const {
for (std::size_t index = 0; index < max_touch_devices; ++index) {
const auto& finger = console.touch_values[index];
if (!finger.pressed.value) {
continue;
}
if (finger.id == static_cast<int>(finger_id)) {
return index;
}
}
return std::nullopt;
}
std::optional<std::size_t> EmulatedConsole::GetNextFreeIndex() const {
for (std::size_t index = 0; index < max_touch_devices; ++index) {
if (!console.touch_values[index].pressed.value) {
return index;
}
}
return std::nullopt;
}
void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) { void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) {
std::scoped_lock lock{callback_mutex}; std::scoped_lock lock{callback_mutex};
for (const auto& poller_pair : callback_list) { for (const auto& poller_pair : callback_list) {

View File

@ -7,6 +7,7 @@
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <optional>
#include <unordered_map> #include <unordered_map>
#include "common/common_funcs.h" #include "common/common_funcs.h"
@ -20,6 +21,8 @@
#include "core/hid/motion_input.h" #include "core/hid/motion_input.h"
namespace Core::HID { namespace Core::HID {
static constexpr std::size_t max_touch_devices = 32;
static constexpr std::size_t max_active_touch_inputs = 16;
struct ConsoleMotionInfo { struct ConsoleMotionInfo {
Common::Input::MotionStatus raw_status{}; Common::Input::MotionStatus raw_status{};
@ -27,13 +30,13 @@ struct ConsoleMotionInfo {
}; };
using ConsoleMotionDevices = std::unique_ptr<Common::Input::InputDevice>; using ConsoleMotionDevices = std::unique_ptr<Common::Input::InputDevice>;
using TouchDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, 16>; using TouchDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, max_touch_devices>;
using ConsoleMotionParams = Common::ParamPackage; using ConsoleMotionParams = Common::ParamPackage;
using TouchParams = std::array<Common::ParamPackage, 16>; using TouchParams = std::array<Common::ParamPackage, max_touch_devices>;
using ConsoleMotionValues = ConsoleMotionInfo; using ConsoleMotionValues = ConsoleMotionInfo;
using TouchValues = std::array<Common::Input::TouchStatus, 16>; using TouchValues = std::array<Common::Input::TouchStatus, max_touch_devices>;
struct TouchFinger { struct TouchFinger {
u64 last_touch{}; u64 last_touch{};
@ -55,7 +58,7 @@ struct ConsoleMotion {
bool is_at_rest{}; bool is_at_rest{};
}; };
using TouchFingerState = std::array<TouchFinger, 16>; using TouchFingerState = std::array<TouchFinger, max_active_touch_inputs>;
struct ConsoleStatus { struct ConsoleStatus {
// Data from input_common // Data from input_common
@ -166,6 +169,10 @@ private:
*/ */
void SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index); void SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index);
std::optional<std::size_t> GetIndexFromFingerId(std::size_t finger_id) const;
std::optional<std::size_t> GetNextFreeIndex() const;
/** /**
* Triggers a callback that something has changed on the console status * Triggers a callback that something has changed on the console status
* @param type Input type of the event to trigger * @param type Input type of the event to trigger

View File

@ -200,9 +200,6 @@ Common::Input::TouchStatus TransformToTouch(const Common::Input::CallbackStatus&
x = std::clamp(x, 0.0f, 1.0f); x = std::clamp(x, 0.0f, 1.0f);
y = std::clamp(y, 0.0f, 1.0f); y = std::clamp(y, 0.0f, 1.0f);
// Limit id to maximum number of fingers
status.id = std::clamp(status.id, 0, 16);
if (status.pressed.inverted) { if (status.pressed.inverted) {
status.pressed.value = !status.pressed.value; status.pressed.value = !status.pressed.value;
} }

View File

@ -36,8 +36,9 @@ namespace Service::HID {
// Updating period for each HID device. // Updating period for each HID device.
// Period time is obtained by measuring the number of samples in a second on HW using a homebrew // Period time is obtained by measuring the number of samples in a second on HW using a homebrew
// Correct pad_update_ns is 4ms this is overclocked to lower input lag // Correct npad_update_ns is 4ms this is overclocked to lower input lag
constexpr auto pad_update_ns = std::chrono::nanoseconds{1 * 1000 * 1000}; // (1ms, 1000Hz) constexpr auto npad_update_ns = std::chrono::nanoseconds{1 * 1000 * 1000}; // (1ms, 1000Hz)
constexpr auto default_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 1000Hz)
constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz) constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz)
constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz) constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz)
@ -75,8 +76,16 @@ IAppletResource::IAppletResource(Core::System& system_,
GetController<Controller_Stubbed>(HidController::UniquePad).SetCommonHeaderOffset(0x5A00); GetController<Controller_Stubbed>(HidController::UniquePad).SetCommonHeaderOffset(0x5A00);
// Register update callbacks // Register update callbacks
pad_update_event = Core::Timing::CreateEvent( npad_update_event = Core::Timing::CreateEvent(
"HID::UpdatePadCallback", "HID::UpdatePadCallback",
[this](std::uintptr_t user_data, s64 time,
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
const auto guard = LockService();
UpdateNpad(user_data, ns_late);
return std::nullopt;
});
default_update_event = Core::Timing::CreateEvent(
"HID::UpdateDefaultCallback",
[this](std::uintptr_t user_data, s64 time, [this](std::uintptr_t user_data, s64 time,
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
const auto guard = LockService(); const auto guard = LockService();
@ -100,7 +109,9 @@ IAppletResource::IAppletResource(Core::System& system_,
return std::nullopt; return std::nullopt;
}); });
system.CoreTiming().ScheduleLoopingEvent(pad_update_ns, pad_update_ns, pad_update_event); system.CoreTiming().ScheduleLoopingEvent(npad_update_ns, npad_update_ns, npad_update_event);
system.CoreTiming().ScheduleLoopingEvent(default_update_ns, default_update_ns,
default_update_event);
system.CoreTiming().ScheduleLoopingEvent(mouse_keyboard_update_ns, mouse_keyboard_update_ns, system.CoreTiming().ScheduleLoopingEvent(mouse_keyboard_update_ns, mouse_keyboard_update_ns,
mouse_keyboard_update_event); mouse_keyboard_update_event);
system.CoreTiming().ScheduleLoopingEvent(motion_update_ns, motion_update_ns, system.CoreTiming().ScheduleLoopingEvent(motion_update_ns, motion_update_ns,
@ -118,7 +129,8 @@ void IAppletResource::DeactivateController(HidController controller) {
} }
IAppletResource::~IAppletResource() { IAppletResource::~IAppletResource() {
system.CoreTiming().UnscheduleEvent(pad_update_event, 0); system.CoreTiming().UnscheduleEvent(npad_update_event, 0);
system.CoreTiming().UnscheduleEvent(default_update_event, 0);
system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event, 0); system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event, 0);
system.CoreTiming().UnscheduleEvent(motion_update_event, 0); system.CoreTiming().UnscheduleEvent(motion_update_event, 0);
} }
@ -144,10 +156,20 @@ void IAppletResource::UpdateControllers(std::uintptr_t user_data,
if (controller == controllers[static_cast<size_t>(HidController::Mouse)]) { if (controller == controllers[static_cast<size_t>(HidController::Mouse)]) {
continue; continue;
} }
// Npad has it's own update event
if (controller == controllers[static_cast<size_t>(HidController::NPad)]) {
continue;
}
controller->OnUpdate(core_timing); controller->OnUpdate(core_timing);
} }
} }
void IAppletResource::UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
auto& core_timing = system.CoreTiming();
controllers[static_cast<size_t>(HidController::NPad)]->OnUpdate(core_timing);
}
void IAppletResource::UpdateMouseKeyboard(std::uintptr_t user_data, void IAppletResource::UpdateMouseKeyboard(std::uintptr_t user_data,
std::chrono::nanoseconds ns_late) { std::chrono::nanoseconds ns_late) {
auto& core_timing = system.CoreTiming(); auto& core_timing = system.CoreTiming();

View File

@ -71,12 +71,14 @@ private:
void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx); void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx);
void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
void UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
void UpdateMouseKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); void UpdateMouseKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
KernelHelpers::ServiceContext& service_context; KernelHelpers::ServiceContext& service_context;
std::shared_ptr<Core::Timing::EventType> pad_update_event; std::shared_ptr<Core::Timing::EventType> npad_update_event;
std::shared_ptr<Core::Timing::EventType> default_update_event;
std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event; std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event;
std::shared_ptr<Core::Timing::EventType> motion_update_event; std::shared_ptr<Core::Timing::EventType> motion_update_event;

View File

@ -10,8 +10,8 @@ namespace InputCommon {
class TouchFromButtonDevice final : public Common::Input::InputDevice { class TouchFromButtonDevice final : public Common::Input::InputDevice {
public: public:
using Button = std::unique_ptr<Common::Input::InputDevice>; using Button = std::unique_ptr<Common::Input::InputDevice>;
TouchFromButtonDevice(Button button_, int touch_id_, float x_, float y_) TouchFromButtonDevice(Button button_, float x_, float y_)
: button(std::move(button_)), touch_id(touch_id_), x(x_), y(y_) { : button(std::move(button_)), x(x_), y(y_) {
last_button_value = false; last_button_value = false;
button->SetCallback({ button->SetCallback({
.on_change = .on_change =
@ -34,7 +34,6 @@ public:
.pressed = button_status, .pressed = button_status,
.x = {}, .x = {},
.y = {}, .y = {},
.id = touch_id,
}; };
status.x.properties = properties; status.x.properties = properties;
status.y.properties = properties; status.y.properties = properties;
@ -62,7 +61,6 @@ public:
private: private:
Button button; Button button;
bool last_button_value; bool last_button_value;
const int touch_id;
const float x; const float x;
const float y; const float y;
const Common::Input::AnalogProperties properties{0.0f, 1.0f, 0.5f, 0.0f, false}; const Common::Input::AnalogProperties properties{0.0f, 1.0f, 0.5f, 0.0f, false};
@ -73,10 +71,9 @@ std::unique_ptr<Common::Input::InputDevice> TouchFromButton::Create(
const std::string null_engine = Common::ParamPackage{{"engine", "null"}}.Serialize(); const std::string null_engine = Common::ParamPackage{{"engine", "null"}}.Serialize();
auto button = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>( auto button = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>(
params.Get("button", null_engine)); params.Get("button", null_engine));
const auto touch_id = params.Get("touch_id", 0);
const float x = params.Get("x", 0.0f) / 1280.0f; const float x = params.Get("x", 0.0f) / 1280.0f;
const float y = params.Get("y", 0.0f) / 720.0f; const float y = params.Get("y", 0.0f) / 720.0f;
return std::make_unique<TouchFromButtonDevice>(std::move(button), touch_id, x, y); return std::make_unique<TouchFromButtonDevice>(std::move(button), x, y);
} }
} // namespace InputCommon } // namespace InputCommon

View File

@ -229,13 +229,12 @@ private:
class InputFromTouch final : public Common::Input::InputDevice { class InputFromTouch final : public Common::Input::InputDevice {
public: public:
explicit InputFromTouch(PadIdentifier identifier_, int touch_id_, int button_, bool toggle_, explicit InputFromTouch(PadIdentifier identifier_, int button_, bool toggle_, bool inverted_,
bool inverted_, int axis_x_, int axis_y_, int axis_x_, int axis_y_, Common::Input::AnalogProperties properties_x_,
Common::Input::AnalogProperties properties_x_,
Common::Input::AnalogProperties properties_y_, Common::Input::AnalogProperties properties_y_,
InputEngine* input_engine_) InputEngine* input_engine_)
: identifier(identifier_), touch_id(touch_id_), button(button_), toggle(toggle_), : identifier(identifier_), button(button_), toggle(toggle_), inverted(inverted_),
inverted(inverted_), axis_x(axis_x_), axis_y(axis_y_), properties_x(properties_x_), axis_x(axis_x_), axis_y(axis_y_), properties_x(properties_x_),
properties_y(properties_y_), input_engine(input_engine_) { properties_y(properties_y_), input_engine(input_engine_) {
UpdateCallback engine_callback{[this]() { OnChange(); }}; UpdateCallback engine_callback{[this]() { OnChange(); }};
const InputIdentifier button_input_identifier{ const InputIdentifier button_input_identifier{
@ -271,8 +270,7 @@ public:
} }
Common::Input::TouchStatus GetStatus() const { Common::Input::TouchStatus GetStatus() const {
Common::Input::TouchStatus status; Common::Input::TouchStatus status{};
status.id = touch_id;
status.pressed = { status.pressed = {
.value = input_engine->GetButton(identifier, button), .value = input_engine->GetButton(identifier, button),
.inverted = inverted, .inverted = inverted,
@ -307,7 +305,6 @@ public:
private: private:
const PadIdentifier identifier; const PadIdentifier identifier;
const int touch_id;
const int button; const int button;
const bool toggle; const bool toggle;
const bool inverted; const bool inverted;
@ -919,7 +916,6 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateTriggerDevice(
std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateTouchDevice( std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateTouchDevice(
const Common::ParamPackage& params) { const Common::ParamPackage& params) {
const auto touch_id = params.Get("touch_id", 0);
const auto deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f); const auto deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f);
const auto range = std::clamp(params.Get("range", 1.0f), 0.25f, 1.50f); const auto range = std::clamp(params.Get("range", 1.0f), 0.25f, 1.50f);
const auto threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f); const auto threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f);
@ -954,8 +950,8 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateTouchDevice(
input_engine->PreSetAxis(identifier, axis_x); input_engine->PreSetAxis(identifier, axis_x);
input_engine->PreSetAxis(identifier, axis_y); input_engine->PreSetAxis(identifier, axis_y);
input_engine->PreSetButton(identifier, button); input_engine->PreSetButton(identifier, button);
return std::make_unique<InputFromTouch>(identifier, touch_id, button, toggle, inverted, axis_x, return std::make_unique<InputFromTouch>(identifier, button, toggle, inverted, axis_x, axis_y,
axis_y, properties_x, properties_y, input_engine.get()); properties_x, properties_y, input_engine.get());
} }
std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateBatteryDevice( std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateBatteryDevice(

View File

@ -342,6 +342,7 @@ GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan
const auto override_build = const auto override_build =
fmt::format(fmt::runtime(std::string(Common::g_title_bar_format_idle)), build_id); fmt::format(fmt::runtime(std::string(Common::g_title_bar_format_idle)), build_id);
const auto yuzu_build_version = override_build.empty() ? yuzu_build : override_build; const auto yuzu_build_version = override_build.empty() ? yuzu_build : override_build;
const auto processor_count = std::thread::hardware_concurrency();
LOG_INFO(Frontend, "yuzu Version: {}", yuzu_build_version); LOG_INFO(Frontend, "yuzu Version: {}", yuzu_build_version);
LogRuntimes(); LogRuntimes();
@ -361,6 +362,7 @@ GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan
} }
LOG_INFO(Frontend, "Host CPU: {}", cpu_string); LOG_INFO(Frontend, "Host CPU: {}", cpu_string);
#endif #endif
LOG_INFO(Frontend, "Host CPU Threads: {}", processor_count);
LOG_INFO(Frontend, "Host OS: {}", PrettyProductName().toStdString()); LOG_INFO(Frontend, "Host OS: {}", PrettyProductName().toStdString());
LOG_INFO(Frontend, "Host RAM: {:.2f} GiB", LOG_INFO(Frontend, "Host RAM: {:.2f} GiB",
Common::GetMemInfo().TotalPhysicalMemory / f64{1_GiB}); Common::GetMemInfo().TotalPhysicalMemory / f64{1_GiB});