Compare commits

..

1 Commits

Author SHA1 Message Date
lollipoponthestick dc04115e87 [renderer, android] Make loseless scaling flow runtime-editable and add QS UI (#4381)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

This PR modifies the loseless scaling rendering hook to actually be runtime editable, allowing users much easier time experimenting with diffrent values
It also adds a (WIP) Android QS impl

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4381
2026-09-09 05:47:32 +02:00
146 changed files with 2380 additions and 2749 deletions
@@ -114,7 +114,9 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
name: Int, name: Int,
container: ViewGroup, container: ViewGroup,
setting: BooleanSetting setting: BooleanSetting,
isEnabled: Boolean = true,
onValueChanged: ((Boolean) -> Unit)? = null
) { ) {
val inflater = LayoutInflater.from(emulationFragment.requireContext()) val inflater = LayoutInflater.from(emulationFragment.requireContext())
val itemView = inflater.inflate(R.layout.item_quick_settings_menu, container, false) val itemView = inflater.inflate(R.layout.item_quick_settings_menu, container, false)
@@ -125,15 +127,21 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
titleView.text = YuzuApplication.appContext.getString(name) titleView.text = YuzuApplication.appContext.getString(name)
switchContainer.visibility = View.VISIBLE switchContainer.visibility = View.VISIBLE
switchContainer.isEnabled = isEnabled
switchView.isChecked = setting.getBoolean() switchView.isChecked = setting.getBoolean()
switchView.isEnabled = isEnabled
itemView.alpha = if (isEnabled) 1.0f else 0.5f
switchView.setOnCheckedChangeListener { _, isChecked -> switchView.setOnCheckedChangeListener { _, isChecked ->
setting.setBoolean(isChecked) setting.setBoolean(isChecked)
saveSettings() saveSettings()
onValueChanged?.invoke(isChecked)
} }
switchContainer.setOnClickListener { switchContainer.setOnClickListener {
switchView.toggle() if (switchView.isEnabled) {
switchView.toggle()
}
} }
container.addView(itemView) container.addView(itemView)
} }
@@ -177,7 +185,9 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
setting: AbstractSetting, setting: AbstractSetting,
minValue: Int = 0, minValue: Int = 0,
maxValue: Int = 100, maxValue: Int = 100,
units: String = "" units: String = "",
isEnabled: Boolean = true,
onValueChanged: ((Int) -> Unit)? = null
) { ) {
val inflater = LayoutInflater.from(emulationFragment.requireContext()) val inflater = LayoutInflater.from(emulationFragment.requireContext())
val itemView = inflater.inflate(R.layout.item_quick_settings_menu, container, false) val itemView = inflater.inflate(R.layout.item_quick_settings_menu, container, false)
@@ -190,10 +200,13 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
titleView.text = YuzuApplication.appContext.getString(name) titleView.text = YuzuApplication.appContext.getString(name)
sliderContainer.visibility = View.VISIBLE sliderContainer.visibility = View.VISIBLE
sliderContainer.isEnabled = isEnabled
slider.valueFrom = minValue.toFloat() slider.valueFrom = minValue.toFloat()
slider.valueTo = maxValue.toFloat() slider.valueTo = maxValue.toFloat()
slider.stepSize = 1f slider.stepSize = 1f
slider.isEnabled = isEnabled
itemView.alpha = if (isEnabled) 1.0f else 0.5f
val currentValue = when (setting) { val currentValue = when (setting) {
is AbstractShortSetting -> setting.getShort(needsGlobal = false).toInt() is AbstractShortSetting -> setting.getShort(needsGlobal = false).toInt()
is AbstractIntSetting -> setting.getInt(needsGlobal = false) is AbstractIntSetting -> setting.getInt(needsGlobal = false)
@@ -204,8 +217,8 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
val displayValue = "${slider.value.toInt()}$units" val displayValue = "${slider.value.toInt()}$units"
valueDisplay.text = displayValue valueDisplay.text = displayValue
slider.addOnChangeListener { _, value, chanhed -> slider.addOnChangeListener { _, value, changed ->
if (chanhed) { if (changed) {
val intValue = value.toInt() val intValue = value.toInt()
when (setting) { when (setting) {
is AbstractShortSetting -> setting.setShort(intValue.toShort()) is AbstractShortSetting -> setting.setShort(intValue.toShort())
@@ -213,6 +226,7 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
} }
saveSettings() saveSettings()
valueDisplay.text = "$intValue$units" valueDisplay.text = "$intValue$units"
onValueChanged?.invoke(intValue)
} }
} }
@@ -92,6 +92,7 @@ import org.yuzu.yuzu_emu.utils.GameIconUtils
import org.yuzu.yuzu_emu.utils.GpuDriverHelper import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.InputHandler import org.yuzu.yuzu_emu.utils.InputHandler
import org.yuzu.yuzu_emu.utils.Log import org.yuzu.yuzu_emu.utils.Log
import org.yuzu.yuzu_emu.utils.LosslessScalingHelper
import org.yuzu.yuzu_emu.utils.NativeConfig import org.yuzu.yuzu_emu.utils.NativeConfig
import org.yuzu.yuzu_emu.utils.NativeFreedrenoConfig import org.yuzu.yuzu_emu.utils.NativeFreedrenoConfig
import org.yuzu.yuzu_emu.utils.ViewUtils import org.yuzu.yuzu_emu.utils.ViewUtils
@@ -1157,6 +1158,36 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
quickSettings.addDivider(container) quickSettings.addDivider(container)
val frameGenAvailable = LosslessScalingHelper.isInstalled() &&
LosslessScalingHelper.isSupportedByGpu()
val frameGenEnabled = BooleanSetting.RENDERER_FRAME_GEN.getBoolean()
val usesFixedMultiplier =
IntSetting.RENDERER_FRAME_GEN_TARGET_RATE.getInt() == 0
quickSettings.addBooleanSetting(
R.string.frame_gen,
container,
BooleanSetting.RENDERER_FRAME_GEN,
isEnabled = frameGenAvailable
) {
NativeLibrary.applySettings()
addQuickSettings()
}
quickSettings.addSliderSetting(
R.string.frame_gen_multiplier,
container,
IntSetting.RENDERER_FRAME_GEN_MULTIPLIER,
minValue = 2,
maxValue = 4,
units = "x",
isEnabled = frameGenAvailable && frameGenEnabled && usesFixedMultiplier
) {
NativeLibrary.applySettings()
}
quickSettings.addDivider(container)
quickSettings.addIntSetting( quickSettings.addIntSetting(
R.string.renderer_accuracy, R.string.renderer_accuracy,
container, container,
@@ -22,6 +22,7 @@
#include "input_common/main.h" #include "input_common/main.h"
#include "jni/emu_window/emu_window.h" #include "jni/emu_window/emu_window.h"
#include "jni/native.h" #include "jni/native.h"
#include "video_core/renderer_base.h"
void EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) { void EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) {
if (!surface) { if (!surface) {
@@ -125,10 +126,14 @@ float EmuWindow_Android::GetFrameTimeVerifiedHint() const {
} }
float EmuWindow_Android::GetPresentedFrameMultiplier() { float EmuWindow_Android::GetPresentedFrameMultiplier() {
if (!Settings::values.frame_gen.GetValue()) { if (EmulationSession::GetInstance().IsRunning()) {
return 1.0f; const VideoCore::FrameGenConfig config =
EmulationSession::GetInstance().System().Renderer().Settings().GetFrameGenConfig();
return config.enabled ? static_cast<float>(config.multiplier) : 1.0f;
} }
return static_cast<float>(std::clamp<u32>(Settings::values.frame_gen_multiplier.GetValue(), 2, 4)); return Settings::values.frame_gen.GetValue()
? static_cast<float>(Settings::FrameGenMultiplier())
: 1.0f;
} }
float EmuWindow_Android::GetFrameRateHint() const { float EmuWindow_Android::GetFrameRateHint() const {
@@ -3,7 +3,7 @@
<!-- General application strings --> <!-- General application strings -->
<string name="app_name" translatable="false">Eden</string> <string name="app_name" translatable="false">Eden</string>
<string name="app_disclaimer">This software will run games for the Nintendo Switch game console. No game titles or keys are included.&lt;br /&gt;&lt;br /&gt;Before you begin, please locate your <![CDATA[<b> prod.keys </b>]]> file on your device storage.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/QuickStart.md\">Learn more</a>]]></string> <string name="app_disclaimer">This software will run games for the Nintendo Switch game console. No game titles or keys are included.&lt;br /&gt;&lt;br /&gt;Before you begin, please locate your <![CDATA[<b> prod.keys </b>]]> file on your device storage.&lt;br /&gt;&lt;br /&gt;<![CDATA[<a href=\"https://yuzu-mirror.github.io/help/quickstart\">Learn more</a>]]></string>
<string name="notice_notification_channel_name">Notices and errors</string> <string name="notice_notification_channel_name">Notices and errors</string>
<string name="notice_notification_channel_id" translatable="false">noticesAndErrors</string> <string name="notice_notification_channel_id" translatable="false">noticesAndErrors</string>
<string name="notice_notification_channel_description">Shows notifications when something goes wrong.</string> <string name="notice_notification_channel_description">Shows notifications when something goes wrong.</string>
@@ -933,7 +933,7 @@
<string name="loader_error_file_not_found">ROM file does not exist</string> <string name="loader_error_file_not_found">ROM file does not exist</string>
<string name="loader_requires_firmware">Game Requires Firmware</string> <string name="loader_requires_firmware">Game Requires Firmware</string>
<string name="loader_requires_firmware_description"><![CDATA[This game may require firmware to function properly, and you don\'t have any installed. Please install firmware before launching, or press \"OK\" to launch anyways.]]></string> <string name="loader_requires_firmware_description"><![CDATA[The game you are trying to launch requires firmware to boot or to get past the opening menu. Please <a href=\"https://yuzu-mirror.github.io/help/quickstart\"> dump and install firmware</a>, or press \"OK\" to launch anyways.]]></string>
<!-- Intent Launch strings --> <!-- Intent Launch strings -->
<string name="searching_for_game">Searching for game...</string> <string name="searching_for_game">Searching for game...</string>
+1 -1
View File
@@ -12,7 +12,7 @@
namespace AudioCore { namespace AudioCore {
AudioCore::AudioCore(Core::System& system) { AudioCore::AudioCore(Core::System& system) {
audio_manager.emplace(system); audio_manager.emplace();
CreateSinks(); CreateSinks();
// Must be created after the sinks // Must be created after the sinks
adsp.emplace(system, *output_sink); adsp.emplace(system, *output_sink);
+15 -12
View File
@@ -15,12 +15,12 @@
namespace AudioCore::AudioIn { namespace AudioCore::AudioIn {
Manager::Manager(Core::System& system) { Manager::Manager(Core::System& system_) : system{system_} {
std::iota(session_ids.begin(), session_ids.end(), 0); std::iota(session_ids.begin(), session_ids.end(), 0);
num_free_sessions = MaxInSessions; num_free_sessions = MaxInSessions;
} }
Result Manager::AcquireSessionId(Core::System& system, size_t& session_id) { Result Manager::AcquireSessionId(size_t& session_id) {
if (num_free_sessions == 0) { if (num_free_sessions == 0) {
LOG_ERROR(Service_Audio, "All 4 AudioIn sessions are in use, cannot create any more"); LOG_ERROR(Service_Audio, "All 4 AudioIn sessions are in use, cannot create any more");
return Service::Audio::ResultOutOfSessions; return Service::Audio::ResultOutOfSessions;
@@ -31,7 +31,7 @@ Result Manager::AcquireSessionId(Core::System& system, size_t& session_id) {
return ResultSuccess; return ResultSuccess;
} }
void Manager::ReleaseSessionId(Core::System& system, const size_t session_id) { void Manager::ReleaseSessionId(const size_t session_id) {
std::scoped_lock l{mutex}; std::scoped_lock l{mutex};
LOG_DEBUG(Service_Audio, "Freeing AudioIn session {}", session_id); LOG_DEBUG(Service_Audio, "Freeing AudioIn session {}", session_id);
session_ids[free_session_id] = session_id; session_ids[free_session_id] = session_id;
@@ -41,20 +41,21 @@ void Manager::ReleaseSessionId(Core::System& system, const size_t session_id) {
applet_resource_user_ids[session_id] = 0; applet_resource_user_ids[session_id] = 0;
} }
Result Manager::LinkToManager(Core::System& system) { Result Manager::LinkToManager() {
std::scoped_lock l{mutex}; std::scoped_lock l{mutex};
if (!linked_to_manager) { if (!linked_to_manager) {
system.AudioCore().GetAudioManager().SetInManager(this, &Manager::BufferReleaseAndRegister); system.AudioCore().GetAudioManager().SetInManager(std::bind(&Manager::BufferReleaseAndRegister, this));
linked_to_manager = true; linked_to_manager = true;
} }
return ResultSuccess; return ResultSuccess;
} }
void Manager::Start(Core::System& system) { void Manager::Start() {
if (sessions_started) { if (sessions_started) {
return; return;
} }
std::scoped_lock l{mutex}; std::scoped_lock l{mutex};
for (auto& session : sessions) { for (auto& session : sessions) {
if (session) { if (session) {
@@ -65,19 +66,21 @@ void Manager::Start(Core::System& system) {
sessions_started = true; sessions_started = true;
} }
void Manager::BufferReleaseAndRegister(void *data, Core::System& system) noexcept { void Manager::BufferReleaseAndRegister() {
Manager* this_ = (Manager*)data; std::scoped_lock l{mutex};
std::scoped_lock l{this_->mutex}; for (auto& session : sessions) {
for (auto& session : this_->sessions) {
if (session != nullptr) { if (session != nullptr) {
session->ReleaseAndRegisterBuffers(); session->ReleaseAndRegisterBuffers();
} }
} }
} }
u32 Manager::GetDeviceNames(Core::System& system, std::span<Renderer::AudioDevice::AudioDeviceName> names, [[maybe_unused]] const bool filter) { u32 Manager::GetDeviceNames(std::span<Renderer::AudioDevice::AudioDeviceName> names,
[[maybe_unused]] const bool filter) {
std::scoped_lock l{mutex}; std::scoped_lock l{mutex};
LinkToManager(system);
LinkToManager();
auto input_devices{Sink::GetDeviceListForSink(Settings::values.sink_id.GetValue(), true)}; auto input_devices{Sink::GetDeviceListForSink(Settings::values.sink_id.GetValue(), true)};
if (!input_devices.empty() && !names.empty()) { if (!input_devices.empty() && !names.empty()) {
names[0] = Renderer::AudioDevice::AudioDeviceName("Uac"); names[0] = Renderer::AudioDevice::AudioDeviceName("Uac");
+11 -10
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -33,29 +30,31 @@ public:
* @param session_id - Output session_id. * @param session_id - Output session_id.
* @return Result code. * @return Result code.
*/ */
Result AcquireSessionId(Core::System& system, size_t& session_id); Result AcquireSessionId(size_t& session_id);
/** /**
* Release a session id on close. * Release a session id on close.
* *
* @param session_id - Session id to free. * @param session_id - Session id to free.
*/ */
void ReleaseSessionId(Core::System& system, const size_t session_id); void ReleaseSessionId(size_t session_id);
/** /**
* Link the audio in manager to the main audio manager. * Link the audio in manager to the main audio manager.
* *
* @return Result code. * @return Result code.
*/ */
Result LinkToManager(Core::System& system); Result LinkToManager();
/** /**
* Start the audio in manager. * Start the audio in manager.
*/ */
void Start(Core::System& system); void Start();
/// @brief Callback function, called by the audio manager when the audio in event is signalled. /**
static void BufferReleaseAndRegister(void *data, Core::System& system) noexcept; * Callback function, called by the audio manager when the audio in event is signalled.
*/
void BufferReleaseAndRegister();
/** /**
* Get a list of audio in device names. * Get a list of audio in device names.
@@ -65,8 +64,10 @@ public:
* *
* @return Number of names written. * @return Number of names written.
*/ */
u32 GetDeviceNames(Core::System& system, std::span<Renderer::AudioDevice::AudioDeviceName> names, bool filter); u32 GetDeviceNames(std::span<Renderer::AudioDevice::AudioDeviceName> names, bool filter);
/// Core system
Core::System& system;
/// Array of session ids /// Array of session ids
std::array<size_t, MaxInSessions> session_ids{}; std::array<size_t, MaxInSessions> session_ids{};
/// Array of resource user ids /// Array of resource user ids
+5 -7
View File
@@ -11,8 +11,8 @@
namespace AudioCore { namespace AudioCore {
AudioManager::AudioManager(Core::System& system) { AudioManager::AudioManager() {
thread = std::jthread([&](std::stop_token stop_token) { thread = std::jthread([this](std::stop_token stop_token) {
Common::SetCurrentThreadName("AudioManager"); Common::SetCurrentThreadName("AudioManager");
std::unique_lock l{events.GetAudioEventLock()}; std::unique_lock l{events.GetAudioEventLock()};
events.ClearEvents(); events.ClearEvents();
@@ -25,7 +25,7 @@ AudioManager::AudioManager(Core::System& system) {
const auto event_type = Event::Type(i); const auto event_type = Event::Type(i);
if (events.CheckAudioEventSet(event_type) || timed_out) { if (events.CheckAudioEventSet(event_type) || timed_out) {
if (buffer_events[i]) { if (buffer_events[i]) {
buffer_events[i](buffer_data[i], system); buffer_events[i]();
} }
} }
events.SetAudioEvent(event_type, false); events.SetAudioEvent(event_type, false);
@@ -42,13 +42,12 @@ void AudioManager::Shutdown() {
} }
} }
Result AudioManager::SetOutManager(void *data, BufferEventFunc buffer_func) { Result AudioManager::SetOutManager(BufferEventFunc buffer_func) {
if (thread.joinable()) { if (thread.joinable()) {
std::scoped_lock l{lock}; std::scoped_lock l{lock};
const auto index{events.GetManagerIndex(Event::Type::AudioOutManager)}; const auto index{events.GetManagerIndex(Event::Type::AudioOutManager)};
if (buffer_events[index] == nullptr) { if (buffer_events[index] == nullptr) {
buffer_events[index] = std::move(buffer_func); buffer_events[index] = std::move(buffer_func);
buffer_data[index] = data;
needs_update = true; needs_update = true;
events.SetAudioEvent(Event::Type::AudioOutManager, true); events.SetAudioEvent(Event::Type::AudioOutManager, true);
} }
@@ -57,13 +56,12 @@ Result AudioManager::SetOutManager(void *data, BufferEventFunc buffer_func) {
return Service::Audio::ResultOperationFailed; return Service::Audio::ResultOperationFailed;
} }
Result AudioManager::SetInManager(void *data, BufferEventFunc buffer_func) { Result AudioManager::SetInManager(BufferEventFunc buffer_func) {
if (thread.joinable()) { if (thread.joinable()) {
std::scoped_lock l{lock}; std::scoped_lock l{lock};
const auto index{events.GetManagerIndex(Event::Type::AudioInManager)}; const auto index{events.GetManagerIndex(Event::Type::AudioInManager)};
if (buffer_events[index] == nullptr) { if (buffer_events[index] == nullptr) {
buffer_events[index] = std::move(buffer_func); buffer_events[index] = std::move(buffer_func);
buffer_data[index] = data;
needs_update = true; needs_update = true;
events.SetAudioEvent(Event::Type::AudioInManager, true); events.SetAudioEvent(Event::Type::AudioInManager, true);
} }
+18 -16
View File
@@ -16,10 +16,6 @@
#include "audio_core/audio_event.h" #include "audio_core/audio_event.h"
namespace Core {
class System;
}
union Result; union Result;
namespace AudioCore { namespace AudioCore {
@@ -38,24 +34,31 @@ namespace AudioCore {
* This is only used by audio in and audio out. * This is only used by audio in and audio out.
*/ */
class AudioManager { class AudioManager {
using BufferEventFunc = void (*)(void *data, Core::System& system) noexcept; using BufferEventFunc = std::function<void()>;
public: public:
explicit AudioManager(Core::System& system); explicit AudioManager();
/** /**
* Shutdown the audio manager. * Shutdown the audio manager.
*/ */
void Shutdown(); void Shutdown();
/// Register the out manager, keeping a function to be called when the out event is signalled. /**
/// @param buffer_func - Function to be called on signal. * Register the out manager, keeping a function to be called when the out event is signalled.
/// @return Result code. *
Result SetOutManager(void *data, BufferEventFunc buffer_func); * @param buffer_func - Function to be called on signal.
* @return Result code.
*/
Result SetOutManager(BufferEventFunc buffer_func);
/// Register the in manager, keeping a function to be called when the in event is signalled. /**
/// @param buffer_func - Function to be called on signal. * Register the in manager, keeping a function to be called when the in event is signalled.
/// @return Result code. *
Result SetInManager(void *data, BufferEventFunc buffer_func); * @param buffer_func - Function to be called on signal.
* @return Result code.
*/
Result SetInManager(BufferEventFunc buffer_func);
/** /**
* Set an event to signalled, and signal the thread. * Set an event to signalled, and signal the thread.
@@ -70,9 +73,8 @@ private:
bool needs_update{}; bool needs_update{};
/// Events to be set and signalled /// Events to be set and signalled
Event events{}; Event events{};
/// Callbacks (and user data) for each manager /// Callbacks for each manager
std::array<BufferEventFunc, 3> buffer_events{}; std::array<BufferEventFunc, 3> buffer_events{};
std::array<void*, 3> buffer_data{};
/// General lock /// General lock
std::mutex lock{}; std::mutex lock{};
/// Main thread for waiting and callbacks /// Main thread for waiting and callbacks
+26 -18
View File
@@ -14,12 +14,12 @@
namespace AudioCore::AudioOut { namespace AudioCore::AudioOut {
Manager::Manager(Core::System& system) { Manager::Manager(Core::System& system_) : system{system_} {
std::iota(session_ids.begin(), session_ids.end(), 0); std::iota(session_ids.begin(), session_ids.end(), 0);
num_free_sessions = MaxOutSessions; num_free_sessions = MaxOutSessions;
} }
Result Manager::AcquireSessionId(Core::System& system, size_t& session_id) { Result Manager::AcquireSessionId(size_t& session_id) {
if (num_free_sessions == 0) { if (num_free_sessions == 0) {
LOG_ERROR(Service_Audio, "All 12 Audio Out sessions are in use, cannot create any more"); LOG_ERROR(Service_Audio, "All 12 Audio Out sessions are in use, cannot create any more");
return Service::Audio::ResultOutOfSessions; return Service::Audio::ResultOutOfSessions;
@@ -30,7 +30,7 @@ Result Manager::AcquireSessionId(Core::System& system, size_t& session_id) {
return ResultSuccess; return ResultSuccess;
} }
void Manager::ReleaseSessionId(Core::System& system, const size_t session_id) { void Manager::ReleaseSessionId(const size_t session_id) {
std::scoped_lock l{mutex}; std::scoped_lock l{mutex};
LOG_DEBUG(Service_Audio, "Freeing AudioOut session {}", session_id); LOG_DEBUG(Service_Audio, "Freeing AudioOut session {}", session_id);
session_ids[free_session_id] = session_id; session_ids[free_session_id] = session_id;
@@ -40,36 +40,44 @@ void Manager::ReleaseSessionId(Core::System& system, const size_t session_id) {
applet_resource_user_ids[session_id] = 0; applet_resource_user_ids[session_id] = 0;
} }
Result Manager::LinkToManager(Core::System& system) { Result Manager::LinkToManager() {
std::scoped_lock l{mutex}; std::scoped_lock l{mutex};
if (!linked_to_manager) { if (!linked_to_manager) {
system.AudioCore().GetAudioManager().SetOutManager(this, &Manager::BufferReleaseAndRegister); system.AudioCore().GetAudioManager().SetOutManager(std::bind(&Manager::BufferReleaseAndRegister, this));
linked_to_manager = true; linked_to_manager = true;
} }
return ResultSuccess; return ResultSuccess;
} }
void Manager::Start(Core::System& system) { void Manager::Start() {
if (!sessions_started) { if (sessions_started) {
std::scoped_lock l{mutex}; return;
for (auto& session : sessions) {
if (session) {
session->StartSession();
}
}
sessions_started = true;
} }
std::scoped_lock l{mutex};
for (auto& session : sessions) {
if (session) {
session->StartSession();
}
}
sessions_started = true;
} }
void Manager::BufferReleaseAndRegister(void *data, Core::System& system) noexcept { void Manager::BufferReleaseAndRegister() {
Manager* this_ = (Manager*)data; std::scoped_lock l{mutex};
std::scoped_lock l{this_->mutex}; for (auto& session : sessions) {
for (auto& session : this_->sessions) {
if (session != nullptr) { if (session != nullptr) {
session->ReleaseAndRegisterBuffers(); session->ReleaseAndRegisterBuffers();
} }
} }
} }
u32 Manager::GetAudioOutDeviceNames(
std::vector<Renderer::AudioDevice::AudioDeviceName>& names) const {
names.emplace_back("DeviceOut");
return 1;
}
} // namespace AudioCore::AudioOut } // namespace AudioCore::AudioOut
+15 -8
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -32,32 +29,42 @@ public:
* @param session_id - Output session_id. * @param session_id - Output session_id.
* @return Result code. * @return Result code.
*/ */
Result AcquireSessionId(Core::System& system, size_t& session_id); Result AcquireSessionId(size_t& session_id);
/** /**
* Release a session id on close. * Release a session id on close.
* *
* @param session_id - Session id to free. * @param session_id - Session id to free.
*/ */
void ReleaseSessionId(Core::System& system, const size_t session_id); void ReleaseSessionId(size_t session_id);
/** /**
* Link this manager to the main audio manager. * Link this manager to the main audio manager.
* *
* @return Result code. * @return Result code.
*/ */
Result LinkToManager(Core::System& system); Result LinkToManager();
/** /**
* Start the audio out manager. * Start the audio out manager.
*/ */
void Start(Core::System& system); void Start();
/** /**
* Callback function, called by the audio manager when the audio out event is signalled. * Callback function, called by the audio manager when the audio out event is signalled.
*/ */
static void BufferReleaseAndRegister(void* data, Core::System& system) noexcept; void BufferReleaseAndRegister();
/**
* Get a list of audio out device names.
*
* @param names - Output container to write names to.
* @return Number of names written.
*/
u32 GetAudioOutDeviceNames(std::vector<Renderer::AudioDevice::AudioDeviceName>& names) const;
/// Core system
Core::System& system;
/// Array of session ids /// Array of session ids
std::array<size_t, MaxOutSessions> session_ids{}; std::array<size_t, MaxOutSessions> session_ids{};
/// Array of resource user ids /// Array of resource user ids
+3 -8
View File
@@ -1,20 +1,15 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "audio_core/audio_render_manager.h" #include "audio_core/audio_render_manager.h"
#include "audio_core/common/audio_renderer_parameter.h" #include "audio_core/common/audio_renderer_parameter.h"
#include "audio_core/renderer/system_manager.h"
#include "audio_core/common/feature_support.h" #include "audio_core/common/feature_support.h"
#include "core/core.h" #include "core/core.h"
namespace AudioCore::Renderer { namespace AudioCore::Renderer {
Manager::Manager(Core::System& system_) Manager::Manager(Core::System& system_)
: system_manager{std::make_unique<SystemManager>(system_)} : system{system_}, system_manager{std::make_unique<SystemManager>(system)} {
{
std::iota(session_ids.begin(), session_ids.end(), 0); std::iota(session_ids.begin(), session_ids.end(), 0);
} }
@@ -64,11 +59,11 @@ u32 Manager::GetSessionCount() const {
return session_count; return session_count;
} }
bool Manager::AddSystem(Renderer::System& system_) { bool Manager::AddSystem(System& system_) {
return system_manager->Add(system_); return system_manager->Add(system_);
} }
bool Manager::RemoveSystem(Renderer::System& system_) { bool Manager::RemoveSystem(System& system_) {
return system_manager->Remove(system_); return system_manager->Remove(system_);
} }
+4 -5
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -74,7 +71,7 @@ public:
* @param system - The system to add. * @param system - The system to add.
* @return True if the system was successfully added, otherwise false. * @return True if the system was successfully added, otherwise false.
*/ */
bool AddSystem(Renderer::System& system); bool AddSystem(System& system);
/** /**
* Remove a renderer system from the manager. * Remove a renderer system from the manager.
@@ -82,7 +79,7 @@ public:
* @param system - The system to remove. * @param system - The system to remove.
* @return True if the system was successfully removed, otherwise false. * @return True if the system was successfully removed, otherwise false.
*/ */
bool RemoveSystem(Renderer::System& system); bool RemoveSystem(System& system);
/** /**
* Free a session id when the system wants to shut down. * Free a session id when the system wants to shut down.
@@ -92,6 +89,8 @@ public:
void ReleaseSessionId(s32 session_id); void ReleaseSessionId(s32 session_id);
private: private:
/// Core system
Core::System& system;
/// Session ids, -1 when in use /// Session ids, -1 when in use
std::array<s32, MaxRendererSessions> session_ids{}; std::array<s32, MaxRendererSessions> session_ids{};
/// Number of active renderers /// Number of active renderers
+20 -24
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -11,43 +8,42 @@
namespace AudioCore::AudioIn { namespace AudioCore::AudioIn {
In::In(Core::System& system_, Manager& manager_, Kernel::KEvent* event_, size_t session_id_) In::In(Core::System& system_, Manager& manager_, Kernel::KEvent* event_, size_t session_id_)
: manager{manager_}, parent_mutex{manager.mutex}, event{event_} : manager{manager_}, parent_mutex{manager.mutex}, event{event_}, system{system_, event,
, audio_system{system_, event, session_id_} session_id_} {}
{}
void In::Free(Core::System& system) { void In::Free() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
manager.ReleaseSessionId(system, audio_system.GetSessionId()); manager.ReleaseSessionId(system.GetSessionId());
} }
System& In::GetSystem() { System& In::GetSystem() {
return audio_system; return system;
} }
AudioIn::State In::GetState() { AudioIn::State In::GetState() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return audio_system.GetState(); return system.GetState();
} }
Result In::StartSystem() { Result In::StartSystem() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return audio_system.Start(); return system.Start();
} }
void In::StartSession() { void In::StartSession() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
audio_system.StartSession(); system.StartSession();
} }
Result In::StopSystem() { Result In::StopSystem() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return audio_system.Stop(); return system.Stop();
} }
Result In::AppendBuffer(const AudioInBuffer& buffer, u64 tag) { Result In::AppendBuffer(const AudioInBuffer& buffer, u64 tag) {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
if (audio_system.AppendBuffer(buffer, tag)) { if (system.AppendBuffer(buffer, tag)) {
return ResultSuccess; return ResultSuccess;
} }
return Service::Audio::ResultBufferCountReached; return Service::Audio::ResultBufferCountReached;
@@ -55,20 +51,20 @@ Result In::AppendBuffer(const AudioInBuffer& buffer, u64 tag) {
void In::ReleaseAndRegisterBuffers() { void In::ReleaseAndRegisterBuffers() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
if (audio_system.GetState() == State::Started) { if (system.GetState() == State::Started) {
audio_system.ReleaseBuffers(); system.ReleaseBuffers();
audio_system.RegisterBuffers(); system.RegisterBuffers();
} }
} }
bool In::FlushAudioInBuffers() { bool In::FlushAudioInBuffers() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return audio_system.FlushAudioInBuffers(); return system.FlushAudioInBuffers();
} }
u32 In::GetReleasedBuffers(std::span<u64> tags) { u32 In::GetReleasedBuffers(std::span<u64> tags) {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return audio_system.GetReleasedBuffers(tags); return system.GetReleasedBuffers(tags);
} }
Kernel::KReadableEvent& In::GetBufferEvent() { Kernel::KReadableEvent& In::GetBufferEvent() {
@@ -78,27 +74,27 @@ Kernel::KReadableEvent& In::GetBufferEvent() {
f32 In::GetVolume() const { f32 In::GetVolume() const {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return audio_system.GetVolume(); return system.GetVolume();
} }
void In::SetVolume(f32 volume) { void In::SetVolume(f32 volume) {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
audio_system.SetVolume(volume); system.SetVolume(volume);
} }
bool In::ContainsAudioBuffer(u64 tag) const { bool In::ContainsAudioBuffer(u64 tag) const {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return audio_system.ContainsAudioBuffer(tag); return system.ContainsAudioBuffer(tag);
} }
u32 In::GetBufferCount() const { u32 In::GetBufferCount() const {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return audio_system.GetBufferCount(); return system.GetBufferCount();
} }
u64 In::GetPlayedSampleCount() const { u64 In::GetPlayedSampleCount() const {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return audio_system.GetPlayedSampleCount(); return system.GetPlayedSampleCount();
} }
} // namespace AudioCore::AudioIn } // namespace AudioCore::AudioIn
+2 -5
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -33,7 +30,7 @@ public:
/** /**
* Free this audio in from the audio in manager. * Free this audio in from the audio in manager.
*/ */
void Free(Core::System& system); void Free();
/** /**
* Get this audio in's system. * Get this audio in's system.
@@ -144,7 +141,7 @@ private:
/// Buffer event, signalled when buffers are ready to be released /// Buffer event, signalled when buffers are ready to be released
Kernel::KEvent* event; Kernel::KEvent* event;
/// Main audio in system /// Main audio in system
System audio_system; System system;
}; };
} // namespace AudioCore::AudioIn } // namespace AudioCore::AudioIn
+20 -24
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -11,43 +8,42 @@
namespace AudioCore::AudioOut { namespace AudioCore::AudioOut {
Out::Out(Core::System& system_, Manager& manager_, Kernel::KEvent* event_, size_t session_id_) Out::Out(Core::System& system_, Manager& manager_, Kernel::KEvent* event_, size_t session_id_)
: manager{manager_}, parent_mutex{manager.mutex}, event{event_} : manager{manager_}, parent_mutex{manager.mutex}, event{event_}, system{system_, event,
, audio_system{system_, event, session_id_} session_id_} {}
{}
void Out::Free(Core::System& system) { void Out::Free() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
manager.ReleaseSessionId(system, audio_system.GetSessionId()); manager.ReleaseSessionId(system.GetSessionId());
} }
System& Out::GetSystem() { System& Out::GetSystem() {
return audio_system; return system;
} }
AudioOut::State Out::GetState() { AudioOut::State Out::GetState() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return audio_system.GetState(); return system.GetState();
} }
Result Out::StartSystem() { Result Out::StartSystem() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return audio_system.Start(); return system.Start();
} }
void Out::StartSession() { void Out::StartSession() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
audio_system.StartSession(); system.StartSession();
} }
Result Out::StopSystem() { Result Out::StopSystem() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return audio_system.Stop(); return system.Stop();
} }
Result Out::AppendBuffer(const AudioOutBuffer& buffer, const u64 tag) { Result Out::AppendBuffer(const AudioOutBuffer& buffer, const u64 tag) {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
if (audio_system.AppendBuffer(buffer, tag)) { if (system.AppendBuffer(buffer, tag)) {
return ResultSuccess; return ResultSuccess;
} }
return Service::Audio::ResultBufferCountReached; return Service::Audio::ResultBufferCountReached;
@@ -55,20 +51,20 @@ Result Out::AppendBuffer(const AudioOutBuffer& buffer, const u64 tag) {
void Out::ReleaseAndRegisterBuffers() { void Out::ReleaseAndRegisterBuffers() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
if (audio_system.GetState() == State::Started) { if (system.GetState() == State::Started) {
audio_system.ReleaseBuffers(); system.ReleaseBuffers();
audio_system.RegisterBuffers(); system.RegisterBuffers();
} }
} }
bool Out::FlushAudioOutBuffers() { bool Out::FlushAudioOutBuffers() {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return audio_system.FlushAudioOutBuffers(); return system.FlushAudioOutBuffers();
} }
u32 Out::GetReleasedBuffers(std::span<u64> tags) { u32 Out::GetReleasedBuffers(std::span<u64> tags) {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return audio_system.GetReleasedBuffers(tags); return system.GetReleasedBuffers(tags);
} }
Kernel::KReadableEvent& Out::GetBufferEvent() { Kernel::KReadableEvent& Out::GetBufferEvent() {
@@ -78,27 +74,27 @@ Kernel::KReadableEvent& Out::GetBufferEvent() {
f32 Out::GetVolume() const { f32 Out::GetVolume() const {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return audio_system.GetVolume(); return system.GetVolume();
} }
void Out::SetVolume(const f32 volume) { void Out::SetVolume(const f32 volume) {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
audio_system.SetVolume(volume); system.SetVolume(volume);
} }
bool Out::ContainsAudioBuffer(const u64 tag) const { bool Out::ContainsAudioBuffer(const u64 tag) const {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return audio_system.ContainsAudioBuffer(tag); return system.ContainsAudioBuffer(tag);
} }
u32 Out::GetBufferCount() const { u32 Out::GetBufferCount() const {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return audio_system.GetBufferCount(); return system.GetBufferCount();
} }
u64 Out::GetPlayedSampleCount() const { u64 Out::GetPlayedSampleCount() const {
std::scoped_lock l{parent_mutex}; std::scoped_lock l{parent_mutex};
return audio_system.GetPlayedSampleCount(); return system.GetPlayedSampleCount();
} }
} // namespace AudioCore::AudioOut } // namespace AudioCore::AudioOut
+2 -5
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -33,7 +30,7 @@ public:
/** /**
* Free this audio out from the audio out manager. * Free this audio out from the audio out manager.
*/ */
void Free(Core::System& system); void Free();
/** /**
* Get this audio out's system. * Get this audio out's system.
@@ -144,7 +141,7 @@ private:
/// Buffer event, signalled when buffers are ready to be released /// Buffer event, signalled when buffers are ready to be released
Kernel::KEvent* event; Kernel::KEvent* event;
/// Main audio out system /// Main audio out system
System audio_system; System system;
}; };
} // namespace AudioCore::AudioOut } // namespace AudioCore::AudioOut
+23 -18
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -16,48 +13,56 @@
namespace AudioCore::Renderer { namespace AudioCore::Renderer {
Renderer::Renderer(Core::System& system_, Manager& manager_, Kernel::KEvent* rendered_event) Renderer::Renderer(Core::System& system_, Manager& manager_, Kernel::KEvent* rendered_event)
: system{system_}, manager{manager_} : core{system_}, manager{manager_}, system{system_, rendered_event} {}
, audio_system{system_, rendered_event}
{}
Result Renderer::Initialize(const AudioRendererParameterInternal& params, Kernel::KTransferMemory* transfer_memory, const u64 transfer_memory_size, Kernel::KProcess* process_handle, const u64 applet_resource_user_id, const s32 session_id) { Result Renderer::Initialize(const AudioRendererParameterInternal& params,
Kernel::KTransferMemory* transfer_memory,
const u64 transfer_memory_size, Kernel::KProcess* process_handle,
const u64 applet_resource_user_id, const s32 session_id) {
if (params.execution_mode == ExecutionMode::Auto) { if (params.execution_mode == ExecutionMode::Auto) {
if (!manager.AddSystem(audio_system)) { if (!manager.AddSystem(system)) {
LOG_ERROR(Service_Audio, "Both Audio Render sessions are in use, cannot create any more"); LOG_ERROR(Service_Audio,
"Both Audio Render sessions are in use, cannot create any more");
return Service::Audio::ResultOutOfSessions; return Service::Audio::ResultOutOfSessions;
} }
system_registered = true; system_registered = true;
} }
initialized = true; initialized = true;
audio_system.Initialize(params, transfer_memory, transfer_memory_size, process_handle, applet_resource_user_id, session_id); system.Initialize(params, transfer_memory, transfer_memory_size, process_handle,
applet_resource_user_id, session_id);
return ResultSuccess; return ResultSuccess;
} }
void Renderer::Finalize() { void Renderer::Finalize() {
auto const session_id{audio_system.GetSessionId()}; auto session_id{system.GetSessionId()};
audio_system.Finalize();
system.Finalize();
if (system_registered) { if (system_registered) {
manager.RemoveSystem(audio_system); manager.RemoveSystem(system);
system_registered = false; system_registered = false;
} }
manager.ReleaseSessionId(session_id); manager.ReleaseSessionId(session_id);
} }
System& Renderer::GetSystem() { System& Renderer::GetSystem() {
return audio_system; return system;
} }
void Renderer::Start() { void Renderer::Start() {
audio_system.Start(); system.Start();
} }
void Renderer::Stop() { void Renderer::Stop() {
audio_system.Stop(); system.Stop();
} }
Result Renderer::RequestUpdate(std::span<const u8> input, std::span<u8> performance, std::span<u8> output) { Result Renderer::RequestUpdate(std::span<const u8> input, std::span<u8> performance,
return audio_system.Update(input, performance, output); std::span<u8> output) {
return system.Update(input, performance, output);
} }
} // namespace AudioCore::Renderer } // namespace AudioCore::Renderer
+2 -5
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -87,7 +84,7 @@ public:
private: private:
/// System core /// System core
Core::System& system; Core::System& core;
/// Manager this renderer is registered with /// Manager this renderer is registered with
Manager& manager; Manager& manager;
/// Is the audio renderer initialized? /// Is the audio renderer initialized?
@@ -95,7 +92,7 @@ private:
/// Is the system registered with the manager? /// Is the system registered with the manager?
bool system_registered{}; bool system_registered{};
/// Audio render system, main driver of audio rendering /// Audio render system, main driver of audio rendering
System audio_system; System system;
}; };
} // namespace Renderer } // namespace Renderer
+3 -2
View File
@@ -89,6 +89,7 @@ add_library(
param_package.h param_package.h
parent_of_member.h parent_of_member.h
point.h point.h
quaternion.h
range_map.h range_map.h
range_mutex.h range_mutex.h
range_sets.h range_sets.h
@@ -108,8 +109,6 @@ add_library(
settings_setting.h settings_setting.h
slot_vector.h slot_vector.h
socket_types.h socket_types.h
sparse_large_vector.cpp
sparse_large_vector.h
spin_lock.h spin_lock.h
stb.cpp stb.cpp
stb.h stb.h
@@ -139,6 +138,8 @@ add_library(
uuid.cpp uuid.cpp
uuid.h uuid.h
vector_math.h vector_math.h
virtual_buffer.cpp
virtual_buffer.h
zstd_compression.cpp zstd_compression.cpp
zstd_compression.h zstd_compression.h
fs/ryujinx_compat.h fs/ryujinx_compat.cpp fs/ryujinx_compat.h fs/ryujinx_compat.cpp
+1
View File
@@ -9,6 +9,7 @@
#include "common/assert.h" #include "common/assert.h"
#include "common/fiber.h" #include "common/fiber.h"
#include "common/virtual_buffer.h"
#include <boost/context/detail/fcontext.hpp> #include <boost/context/detail/fcontext.hpp>
+14 -41
View File
@@ -117,6 +117,7 @@ template <typename T>
static void GetFuncAddress(Common::DynamicLibrary& dll, const char* name, T& pfn) { static void GetFuncAddress(Common::DynamicLibrary& dll, const char* name, T& pfn) {
if (!dll.GetSymbol(name, &pfn)) { if (!dll.GetSymbol(name, &pfn)) {
LOG_CRITICAL(HW_Memory, "Failed to load {}", name); LOG_CRITICAL(HW_Memory, "Failed to load {}", name);
throw std::bad_alloc{};
} }
} }
@@ -178,14 +179,6 @@ public:
Release(); Release();
} }
void* Allocate(size_t size) {
auto* ptr = VirtualAlloc(nullptr, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (ptr == nullptr) {
LOG_CRITICAL(HW_Memory, "Failed to allocate fallback buffer with size {:#x}, error {}", size, GetLastError());
}
return ptr;
}
void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms) { void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms) {
std::unique_lock lock{placeholder_mutex}; std::unique_lock lock{placeholder_mutex};
if (!IsNiechePlaceholder(virtual_offset, length)) { if (!IsNiechePlaceholder(virtual_offset, length)) {
@@ -406,10 +399,6 @@ private:
// For managarm: see https://github.com/managarm/managarm/issues/1370 // For managarm: see https://github.com/managarm/managarm/issues/1370
#else // ^^^ Windows ^^^ vvv POSIX vvv #else // ^^^ Windows ^^^ vvv POSIX vvv
#ifndef MAP_NOCORE
#define MAP_NOCORE 0
#endif
#ifdef ARCHITECTURE_arm64 #ifdef ARCHITECTURE_arm64
static void* ChooseVirtualBase(size_t virtual_size) { static void* ChooseVirtualBase(size_t virtual_size) {
@@ -434,7 +423,7 @@ static void* ChooseVirtualBase(size_t virtual_size) {
// Note: we may be able to take advantage of MAP_FIXED_NOREPLACE here. // Note: we may be able to take advantage of MAP_FIXED_NOREPLACE here.
void* map_pointer = void* map_pointer =
mmap(reinterpret_cast<void*>(hint_address), virtual_size, PROT_READ | PROT_WRITE, mmap(reinterpret_cast<void*>(hint_address), virtual_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_NOCORE, -1, 0); MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
// If we successfully mapped, we're done. // If we successfully mapped, we're done.
if (reinterpret_cast<uintptr_t>(map_pointer) == hint_address) { if (reinterpret_cast<uintptr_t>(map_pointer) == hint_address) {
@@ -454,11 +443,11 @@ static void* ChooseVirtualBase(size_t virtual_size) {
static void* ChooseVirtualBase(size_t virtual_size) { static void* ChooseVirtualBase(size_t virtual_size) {
#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) || defined(__sun__) || defined(__HAIKU__) || defined(__managarm__) || defined(__AIX__) #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) || defined(__sun__) || defined(__HAIKU__) || defined(__managarm__) || defined(__AIX__)
void* virtual_base = mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_ALIGNED_SUPER | MAP_NOCORE, -1, 0); void* virtual_base = mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_ALIGNED_SUPER, -1, 0);
if (virtual_base != MAP_FAILED) if (virtual_base != MAP_FAILED)
return virtual_base; return virtual_base;
#endif #endif
return mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_NOCORE, -1, 0); return mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
} }
#endif #endif
@@ -552,13 +541,13 @@ public:
} }
if (use_anon) { if (use_anon) {
LOG_WARNING(Common_Memory, "Using private mappings instead of shared ones"); LOG_WARNING(Common_Memory, "Using private mappings instead of shared ones");
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_NOCORE, -1, 0)); backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0));
if (fd > 0) { if (fd > 0) {
fd = -1; fd = -1;
close(fd); close(fd);
} }
} else { } else {
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_NOCORE, fd, 0)); backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
} }
if (backing_base == MAP_FAILED) { if (backing_base == MAP_FAILED) {
LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno)); LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno));
@@ -582,14 +571,6 @@ public:
Release(); Release();
} }
void* Allocate(size_t size) {
auto* ptr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
if (ptr == MAP_FAILED) {
LOG_CRITICAL(HW_Memory, "Failed to allocate fallback buffer with size {:#x}, {}", size, strerror(errno));
}
return ptr;
}
void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms) { void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms) {
// Intersect the range with our address space. // Intersect the range with our address space.
AdjustMap(&virtual_offset, &length); AdjustMap(&virtual_offset, &length);
@@ -710,10 +691,12 @@ HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_)
{ {
#if defined(__OPENORBIS__) || defined(__managarm__) #if defined(__OPENORBIS__) || defined(__managarm__)
LOG_WARNING(HW_Memory, "Platform doesn't support fastmem"); LOG_WARNING(HW_Memory, "Platform doesn't support fastmem");
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)); fallback_buffer.emplace(backing_size);
backing_base = fallback_buffer->data();
virtual_base = nullptr; virtual_base = nullptr;
#else #else
// Try to allocate a fastmem arena. // Try to allocate a fastmem arena.
// The implementation will fail with std::bad_alloc on errors.
impl = std::make_unique<HostMemory::Impl>(AlignUp(backing_size, PageAlignment), AlignUp(virtual_size, PageAlignment) + HugePageSize); impl = std::make_unique<HostMemory::Impl>(AlignUp(backing_size, PageAlignment), AlignUp(virtual_size, PageAlignment) + HugePageSize);
if (impl->Init()) { if (impl->Init()) {
backing_base = impl->backing_base; backing_base = impl->backing_base;
@@ -724,26 +707,16 @@ HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_)
virtual_base_offset = virtual_base - impl->virtual_base; virtual_base_offset = virtual_base - impl->virtual_base;
} }
} else { } else {
LOG_WARNING(HW_Memory, "Platform can support fastmem, but can't create it");
fallback_buffer = true;
backing_base = static_cast<u8*>(impl->Allocate(backing_size));
virtual_base = nullptr;
impl.reset(); impl.reset();
LOG_WARNING(HW_Memory, "Platform can support fastmem, but can't create it");
fallback_buffer.emplace(backing_size);
backing_base = fallback_buffer->data();
virtual_base = nullptr;
} }
#endif #endif
} }
HostMemory::~HostMemory() { HostMemory::~HostMemory() = default;
#ifdef _WIN32
if (fallback_buffer) {
VirtualFree(backing_base, backing_size, MEM_RELEASE);
}
#else
if (fallback_buffer) {
munmap(backing_base, backing_size);
}
#endif
}
HostMemory::HostMemory(HostMemory&&) noexcept = default; HostMemory::HostMemory(HostMemory&&) noexcept = default;
+2 -1
View File
@@ -10,6 +10,7 @@
#include <optional> #include <optional>
#include "common/common_funcs.h" #include "common/common_funcs.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/virtual_buffer.h"
namespace Common { namespace Common {
@@ -85,7 +86,7 @@ private:
u8* virtual_base{}; u8* virtual_base{};
size_t virtual_base_offset{}; size_t virtual_base_offset{};
// Windows requires it for kernels whom lack proper support for some functions! // Windows requires it for kernels whom lack proper support for some functions!
bool fallback_buffer{false}; std::optional<Common::VirtualBuffer<u8>> fallback_buffer;
}; };
} // namespace Common } // namespace Common
+33 -5
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
@@ -13,11 +13,39 @@ PageTable::PageTable() = default;
PageTable::~PageTable() noexcept = default; PageTable::~PageTable() noexcept = default;
void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page_bits) { bool PageTable::BeginTraversal(TraversalEntry* out_entry, TraversalContext* out_context,
auto const num_page_table_entries = 1ULL << (address_space_width_in_bits - page_bits); Common::ProcessAddress address) const {
entries.ResizeAndClear(num_page_table_entries); out_context->next_offset = GetInteger(address);
out_context->next_page = address / page_size;
return this->ContinueTraversal(out_entry, out_context);
}
bool PageTable::ContinueTraversal(TraversalEntry* out_entry, TraversalContext* context) const {
// Setup invalid defaults.
out_entry->phys_addr = 0;
out_entry->block_size = page_size;
// Validate that we can read the actual entry.
if (auto const page = context->next_page; page < entries.size()) {
// Validate that the entry is mapped.
if (auto const paddr = entries[page].addr; paddr != 0) {
// Populate the results.
out_entry->phys_addr = paddr + context->next_offset;
context->next_page += 1;
context->next_offset += page_size;
return true;
}
}
context->next_page += 1;
context->next_offset += page_size;
return false;
}
void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits) {
auto const num_page_table_entries = 1ULL << (address_space_width_in_bits - page_size_in_bits);
entries.resize(num_page_table_entries);
current_address_space_width_in_bits = address_space_width_in_bits; current_address_space_width_in_bits = address_space_width_in_bits;
current_page_bits = page_bits; page_size = 1ULL << page_size_in_bits;
} }
} // namespace Common } // namespace Common
+56 -64
View File
@@ -9,22 +9,22 @@
#include <atomic> #include <atomic>
#include "common/common_types.h" #include "common/common_types.h"
#include "common/sparse_large_vector.h"
#include "common/typed_address.h" #include "common/typed_address.h"
#include "common/virtual_buffer.h"
namespace Common { namespace Common {
enum class PageType : u8 { enum class PageType : u8 {
/// Page is unmapped and should cause an access error. /// Page is unmapped and should cause an access error.
Unmapped = 0b00, Unmapped,
/// Page is mapped to regular memory. This is the only type you can get pointers to. /// Page is mapped to regular memory. This is the only type you can get pointers to.
Memory = 0b01, Memory,
/// Page is mapped to regular memory, but inaccessible from CPU fastmem and must use /// Page is mapped to regular memory, but inaccessible from CPU fastmem and must use
/// the callbacks. /// the callbacks.
DebugMemory = 0b10, DebugMemory,
/// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
/// invalidation /// invalidation
RasterizerCachedMemory = 0b11, RasterizerCachedMemory,
}; };
/** /**
@@ -42,86 +42,57 @@ struct PageTable {
u64 next_offset{}; u64 next_offset{};
}; };
/// Masks out bits reserved for attribute tagging. /// Number of bits reserved for attribute tagging.
static constexpr u64 ATTRIBUTE_MASK = ((1ULL << 44) - 1) << 12; /// This can be at most the guaranteed alignment of the pointers in the page table.
static constexpr int ATTRIBUTE_BITS = 2;
/// Specifies sign bit for page table entries.
static constexpr u64 SIGN_BIT = 45 + 12; // 44 bits of data + page offset
/** /**
* Atomic tuple of host pointer, page type, and block id. * Pair of host pointer and page type attribute.
* This uses the lower bits of a given pointer to store the attributes. * This uses the lower bits of a given pointer to store the attribute tag.
* Writing and reading the pointer attribute pair is guaranteed to be atomic for the same method * Writing and reading the pointer attribute pair is guaranteed to be atomic for the same method
* call. In other words, they are guaranteed to be synchronized at all times. * call. In other words, they are guaranteed to be synchronized at all times.
*/ */
class PageEntryData { class PageInfo {
public: public:
struct Data {
Data(bool marked_, PageType type_, u16 block_, u64 page_)
: marked(static_cast<u64>(marked_) & 0b1)
, type(static_cast<u64>(type_) & ((1ULL << 2) - 1))
, block(static_cast<u64>(block_) & ((1ULL << 9) - 1))
, page((page_ >> 12) & ((1ULL << 45) - 1))
, block2((static_cast<u64>(block_) >> 9) & ((1ULL << 7) - 1)) {}
u64 marked : 1;
u64 type : 2;
u64 block : 9;
u64 page : 45; // 44 bits of actual data (64 - page offset (12) - reserved (8)) + a sign bit
u64 block2 : 7;
};
[[nodiscard]] Data Raw() const noexcept {
return std::bit_cast<Data>(data_raw.load(std::memory_order_relaxed));
}
/// Returns the page pointer /// Returns the page pointer
[[nodiscard]] uintptr_t Pointer(bool ignored_marked = false) const noexcept { [[nodiscard]] uintptr_t Pointer() const noexcept {
return ExtractPointer(std::bit_cast<Data>(data_raw.load(std::memory_order_relaxed)), ignored_marked); return ExtractPointer(raw.load(std::memory_order_relaxed));
} }
/// Returns the page type attribute /// Returns the page type attribute
[[nodiscard]] PageType Type() const noexcept { [[nodiscard]] PageType Type() const noexcept {
return static_cast<PageType>(std::bit_cast<Data>(data_raw.load(std::memory_order_relaxed)).type); return ExtractType(raw.load(std::memory_order_relaxed));
}
/// Returns the block identifier.
[[nodiscard]] u16 Block() const noexcept {
return ExtractBlock(std::bit_cast<Data>(data_raw.load(std::memory_order_relaxed)));
} }
/// Returns the page pointer and attribute pair, extracted from the same atomic read /// Returns the page pointer and attribute pair, extracted from the same atomic read
[[nodiscard]] std::tuple<uintptr_t, PageType, u16> PointerTypeBlock(bool ignore_marked = false) const noexcept { [[nodiscard]] std::pair<uintptr_t, PageType> PointerType() const noexcept {
const auto non_atomic_raw = std::bit_cast<Data>(data_raw.load(std::memory_order_relaxed)); const uintptr_t non_atomic_raw = raw.load(std::memory_order_relaxed);
return {ExtractPointer(non_atomic_raw, ignore_marked), static_cast<PageType>(non_atomic_raw.type), ExtractBlock(non_atomic_raw)}; return {ExtractPointer(non_atomic_raw), ExtractType(non_atomic_raw)};
} }
/// Write page info atomically /// Returns the raw representation of the page information.
constexpr void Store(bool marked, PageType type, u16 block, uintptr_t pointer) noexcept { /// Use ExtractPointer and ExtractType to unpack the value.
data_raw.store(std::bit_cast<u64>(Data{marked, type, block, pointer})); [[nodiscard]] uintptr_t Raw() const noexcept {
return raw.load(std::memory_order_relaxed);
} }
constexpr void MarkRasterizerCached() noexcept { /// Write a page pointer and type pair atomically
data_raw.fetch_or(0b111); void Store(uintptr_t pointer, PageType type) noexcept {
} raw.store(pointer | uintptr_t(type));
constexpr void MarkDebug(u64 ptr, u16 block) noexcept {
Store(true, PageType::DebugMemory, block, ptr);
} }
/// Unpack a pointer from a page info raw representation /// Unpack a pointer from a page info raw representation
[[nodiscard]] static uintptr_t ExtractPointer(Data raw, bool ignore_marked = false) noexcept { [[nodiscard]] static uintptr_t ExtractPointer(uintptr_t raw) noexcept {
return raw.marked && !ignore_marked ? 0 return raw & (~uintptr_t{0} << ATTRIBUTE_BITS);
// shift raw.page's fake sign bit to the actual sign bit, then sign extend
: ((s64)(raw.page << (64 - 44))) >> (64 - 44 - 12);
} }
[[nodiscard]] static u16 ExtractBlock(Data raw) noexcept { /// Unpack a page type from a page info raw representation
return static_cast<u16>(raw.block | (raw.block2 << 9)); [[nodiscard]] static PageType ExtractType(uintptr_t raw) noexcept {
return static_cast<PageType>(raw & ((uintptr_t{1} << ATTRIBUTE_BITS) - 1));
} }
private: private:
std::atomic<u64> data_raw; std::atomic<uintptr_t> raw;
static_assert(sizeof(Data) == sizeof(std::atomic<u64>));
}; };
PageTable(); PageTable();
@@ -129,8 +100,13 @@ struct PageTable {
PageTable(const PageTable&) = delete; PageTable(const PageTable&) = delete;
PageTable& operator=(const PageTable&) = delete; PageTable& operator=(const PageTable&) = delete;
PageTable(PageTable&&) noexcept = delete;
PageTable& operator=(PageTable&&) noexcept = delete; PageTable(PageTable&&) noexcept = default;
PageTable& operator=(PageTable&&) noexcept = default;
bool BeginTraversal(TraversalEntry* out_entry, TraversalContext* out_context,
Common::ProcessAddress address) const;
bool ContinueTraversal(TraversalEntry* out_entry, TraversalContext* context) const;
/** /**
* Resizes the page table to be able to accommodate enough pages within * Resizes the page table to be able to accommodate enough pages within
@@ -145,14 +121,30 @@ struct PageTable {
return current_address_space_width_in_bits; return current_address_space_width_in_bits;
} }
bool GetPhysicalAddress(Common::PhysicalAddress* out_phys_addr,
Common::ProcessAddress virt_addr) const {
if (virt_addr > (1ULL << this->GetAddressSpaceBits())) {
return false;
}
*out_phys_addr = entries[virt_addr / page_size].addr + GetInteger(virt_addr);
return true;
}
/// Vector of memory pointers backing each page. An entry can only be non-null if the /// Vector of memory pointers backing each page. An entry can only be non-null if the
/// corresponding attribute element is of type `Memory`. /// corresponding attribute element is of type `Memory`.
SparseLargeVector<PageEntryData> entries; struct PageEntryData {
static_assert(sizeof(PageEntryData) == 8); PageInfo ptr;
u64 block;
u64 addr;
u64 padding;
};
VirtualBuffer<PageEntryData> entries;
static_assert(sizeof(PageEntryData) == 32);
u8* fastmem_arena{}; u8* fastmem_arena{};
std::size_t current_address_space_width_in_bits{}; std::size_t current_address_space_width_in_bits{};
std::size_t current_page_bits{}; std::size_t page_size{};
}; };
} // namespace Common } // namespace Common
+79
View File
@@ -0,0 +1,79 @@
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/vector_math.h"
namespace Common {
template <typename T>
class Quaternion {
public:
Vec3<T> xyz;
T w{};
[[nodiscard]] Quaternion<decltype(-T{})> Inverse() const {
return {-xyz, w};
}
[[nodiscard]] Quaternion<decltype(T{} + T{})> operator+(const Quaternion& other) const {
return {xyz + other.xyz, w + other.w};
}
[[nodiscard]] Quaternion<decltype(T{} - T{})> operator-(const Quaternion& other) const {
return {xyz - other.xyz, w - other.w};
}
[[nodiscard]] Quaternion<decltype(T{} * T{} - T{} * T{})> operator*(
const Quaternion& other) const {
return {xyz * other.w + other.xyz * w + Cross(xyz, other.xyz),
w * other.w - Dot(xyz, other.xyz)};
}
[[nodiscard]] Quaternion<T> Normalized() const {
T length = std::sqrt(xyz.Length2() + w * w);
return {xyz / length, w / length};
}
[[nodiscard]] std::array<decltype(-T{}), 16> ToMatrix() const {
const T x2 = xyz[0] * xyz[0];
const T y2 = xyz[1] * xyz[1];
const T z2 = xyz[2] * xyz[2];
const T xy = xyz[0] * xyz[1];
const T wz = w * xyz[2];
const T xz = xyz[0] * xyz[2];
const T wy = w * xyz[1];
const T yz = xyz[1] * xyz[2];
const T wx = w * xyz[0];
return {1.0f - 2.0f * (y2 + z2),
2.0f * (xy + wz),
2.0f * (xz - wy),
0.0f,
2.0f * (xy - wz),
1.0f - 2.0f * (x2 + z2),
2.0f * (yz + wx),
0.0f,
2.0f * (xz + wy),
2.0f * (yz - wx),
1.0f - 2.0f * (x2 + y2),
0.0f,
0.0f,
0.0f,
0.0f,
1.0f};
}
};
template <typename T>
[[nodiscard]] auto QuaternionRotate(const Quaternion<T>& q, const Vec3<T>& v) {
return v + 2 * Cross(q.xyz, Cross(q.xyz, v) + v * q.w);
}
[[nodiscard]] inline Quaternion<float> MakeQuaternion(const Vec3<float>& axis, float angle) {
return {axis * std::sin(angle / 2), std::cos(angle / 2)};
}
} // namespace Common
+7 -7
View File
@@ -389,7 +389,7 @@ struct Values {
true}; true};
SwitchableSetting<bool> frame_gen{linkage, false, "frame_gen", Category::Renderer, SwitchableSetting<bool> frame_gen{linkage, false, "frame_gen", Category::Renderer,
Specialization::Default, true, false}; Specialization::Default, true, true};
SwitchableSetting<u32, true> frame_gen_multiplier{linkage, SwitchableSetting<u32, true> frame_gen_multiplier{linkage,
2, 2,
@@ -399,7 +399,7 @@ struct Values {
Category::Renderer, Category::Renderer,
Specialization::Countable, Specialization::Countable,
true, true,
false, true,
&frame_gen}; &frame_gen};
SwitchableSetting<u32, true> frame_gen_target_rate{linkage, SwitchableSetting<u32, true> frame_gen_target_rate{linkage,
@@ -419,7 +419,7 @@ struct Values {
Category::Renderer, Category::Renderer,
Specialization::Default, Specialization::Default,
true, true,
false, true,
&frame_gen}; &frame_gen};
SwitchableSetting<u32, true> frame_gen_flow_scale{linkage, SwitchableSetting<u32, true> frame_gen_flow_scale{linkage,
@@ -442,11 +442,11 @@ struct Values {
Category::Renderer, Category::Renderer,
Specialization::Countable, Specialization::Countable,
true, true,
false, true,
&frame_gen}; &frame_gen};
SwitchableSetting<bool> frame_gen_fp16{linkage, true, "frame_gen_fp16", Category::Renderer, SwitchableSetting<bool> frame_gen_fp16{linkage, true, "frame_gen_fp16", Category::Renderer,
Specialization::Default, true, false, &frame_gen}; Specialization::Default, true, true, &frame_gen};
SwitchableSetting<bool> frame_gen_dump_flow{linkage, false, "frame_gen_dump_flow", SwitchableSetting<bool> frame_gen_dump_flow{linkage, false, "frame_gen_dump_flow",
Category::Renderer}; Category::Renderer};
@@ -858,7 +858,7 @@ struct Values {
SwitchableSetting<std::string> program_args{linkage, SwitchableSetting<std::string> program_args{linkage,
std::string(), std::string(),
"program_args", "program_args",
Category::Debugging, Category::System,
Specialization::Default, Specialization::Default,
true, // save_ - persist in config file true, // save_ - persist in config file
false}; // runtime_modifiable_ - startup-only false}; // runtime_modifiable_ - startup-only
@@ -904,7 +904,7 @@ struct Values {
0, 0,
65535, 65535,
"debug_knobs", "debug_knobs",
Category::Debugging, Category::System,
Specialization::Countable, Specialization::Countable,
true, true,
true}; true};
-143
View File
@@ -1,143 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* virtual_buffer.cpp */
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#ifdef _WIN32
#include <windows.h>
#include <mutex>
#else
#include <sys/mman.h>
#endif
#include "common/alignment.h"
#include "common/assert.h"
#include "common/sparse_large_vector.h"
namespace Common {
#ifdef _WIN32
static std::vector<std::pair<u64, u64>> vector_regions {};
// Workaround for handling non-commited memory accessed by Dynarmic; usually result of an error
static LONG WINAPI FakePageFaultHandler(PEXCEPTION_POINTERS info) {
DWORD code = info->ExceptionRecord->ExceptionCode;
u64 exception_addr = reinterpret_cast<u64>(info->ExceptionRecord->ExceptionAddress);
if (code != EXCEPTION_ACCESS_VIOLATION) {
// Not our problem
return EXCEPTION_CONTINUE_SEARCH;
}
u64 addr = 0, addr2 = 0;
for (auto region: vector_regions) {
auto addr_shifted = exception_addr >> HostPageBits;
if (region.first <= addr_shifted && addr_shifted <= region.second) {
addr = addr_shifted;
}
// Page-boundary accesses
if (auto addr_ = (exception_addr + 0x40) >> HostPageBits; addr_ != addr_shifted && region.first <= addr_ && addr_ <= region.second) {
addr2 = addr_;
}
if (addr != 0 || addr2 != 0) {
break;
}
}
if (addr == 0 && addr2 == 0) {
// Not our problem
return EXCEPTION_CONTINUE_SEARCH;
}
LOG_ERROR(HW_Memory, "Accessing an unallocated region of a SparseLargeVector at {:#x}; this shouldn't happen and is likely a Dynarmic error!", exception_addr);
// Commit this region
if (addr != 0) {
if (!CommitVectorPage(addr << HostPageBits, false)) {
return EXCEPTION_CONTINUE_SEARCH;
}
}
// Commit next region if needed
if (addr2 != 0) {
if (!CommitVectorPage(addr2 << HostPageBits, false)) {
return EXCEPTION_CONTINUE_SEARCH;
}
}
return EXCEPTION_CONTINUE_EXECUTION;
}
bool CommitVectorPage(uintptr_t addr, bool write) noexcept {
MEMORY_BASIC_INFORMATION info {};
auto res = VirtualQuery(reinterpret_cast<void*>(addr), &info, sizeof(info));
if (res == 0) {
LOG_CRITICAL(HW_Memory, "Failed to query large buffer region at {:#x} with error {}, will try committing anyway", addr, GetLastError());
} else if (info.State != MEM_RESERVE) {
LOG_ERROR(HW_Memory, "Tried to commit an unreserved large buffer region at {:#x} that is not mapped or is already committed (state {:#x})", addr, info.State);
return false;
}
auto perm = write ? PAGE_READWRITE : PAGE_READONLY;
void* res2 = VirtualAlloc(reinterpret_cast<LPVOID>(addr), HostPageSize, MEM_COMMIT, perm);
if (res2 == nullptr) {
LOG_ERROR(HW_Memory, "Failed to commit large buffer region at {:#x}, error {}", addr, GetLastError());
return false;
}
return true;
}
#endif
#ifndef MAP_NOCORE
#define MAP_NOCORE 0
#endif
void* AllocateMemoryPages(std::size_t size) noexcept {
if (auto page = HostPageSize; size % page != 0) {
LOG_WARNING(HW_Memory, "Allocating unaligned large vector with size {:#x}; aligning to {} page size", size, page);
size = AlignUp(size, page);
}
#ifdef _WIN32
// We will never use this memory entirely so instead of committing it up front let's just reserve it and commit each page individually
void* base = VirtualAlloc(nullptr, size, MEM_RESERVE, PAGE_READWRITE);
if (base != nullptr) {
vector_regions.emplace_back(reinterpret_cast<u64>(base), reinterpret_cast<u64>(base) + size);
static std::once_flag flag;
std::call_once(flag, []() { AddVectoredExceptionHandler(1, FakePageFaultHandler); });
} else {
// Try committing everything instead??
LOG_WARNING(HW_Memory, "Failed to reserve large vector region with error {}, trying to commit instead..", GetLastError());
base = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE);
}
ASSERT_MSG(base, "Failed to reserve {:#x} sized region with error {}", size, GetLastError());
#else
void* base = mmap(nullptr, size, PROT_READ, MAP_ANON | MAP_PRIVATE | MAP_NOCORE, -1, 0);
if (base == MAP_FAILED)
base = nullptr;
ASSERT_MSG(base, "Failed to allocate {:#x} sized region with error {}", size, strerror(errno));
#endif
return base;
}
void FreeMemoryPages(void* base, [[maybe_unused]] std::size_t size) noexcept {
if (auto page = HostPageSize; size % page != 0) {
size = AlignUp(size, page);
}
if (!base)
return;
#ifdef _WIN32
ASSERT(VirtualFree(base, 0, MEM_RELEASE));
#else
ASSERT(munmap(base, size) == 0);
#endif
}
} // namespace Common
-194
View File
@@ -1,194 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* virtual_buffer.h */
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <atomic>
#include <bit>
#include <utility>
#include <vector>
#ifndef _WIN32
#include <unistd.h>
#include <sys/mman.h>
#endif
#include "common/alignment.h"
#include "common/assert.h"
namespace Common {
#ifdef _WIN32
constexpr u64 HostPageSize = 0x1000;
constexpr u64 HostPageBits = 12;
constexpr u64 HostPageMask = ~(HostPageSize - 1);
bool CommitVectorPage(uintptr_t addr, bool write) noexcept;
#else
const u64 HostPageSize = sysconf(_SC_PAGESIZE);
const u64 HostPageBits = std::countr_zero(HostPageSize);
const u64 HostPageMask = ~(HostPageSize - 1);
#endif
void* AllocateMemoryPages(std::size_t size) noexcept;
void FreeMemoryPages(void* base, std::size_t size) noexcept;
/// A large page-aligned buffer that has optimized memory usage for zero-writes.
template <typename T>
requires std::is_trivially_copyable_v<T>
class SparseLargeVector final {
public:
constexpr SparseLargeVector() = default;
explicit SparseLargeVector(std::size_t count) noexcept
: alloc_size{count * sizeof(T)}
{
base_ptr = static_cast<T*>(AllocateMemoryPages(alloc_size));
// each item in vector holds information for 64 pages
auto denom = HostPageSize * 64;
committed_pages = std::vector<std::atomic<u64>>((alloc_size + denom - 1) / denom);
}
~SparseLargeVector() noexcept {
FreeMemoryPages(base_ptr, alloc_size);
}
SparseLargeVector(const SparseLargeVector&) = delete;
SparseLargeVector& operator=(const SparseLargeVector&) = delete;
SparseLargeVector(SparseLargeVector&& other) = delete;
SparseLargeVector& operator=(SparseLargeVector&& other) = delete;
void ResizeAndClear(std::size_t count) noexcept {
if (auto const new_size = count * sizeof(T); new_size != alloc_size) {
FreeMemoryPages(base_ptr, alloc_size);
alloc_size = new_size;
base_ptr = static_cast<T*>(AllocateMemoryPages(alloc_size));
auto denom = HostPageSize * 64;
committed_pages = std::vector<std::atomic<u64>>((alloc_size + denom - 1) / denom);
}
}
/// Returns a reference to the value of the requested index and allocates memory if needed.
T& GetAndFault(std::size_t index) noexcept {
if (index > alloc_size / sizeof(T)) {
UNREACHABLE_MSG("Out of bounds RW access on SparseLargeVector @ {}", index);
}
if (!IsCommittedPage(index)) {
CommitPage(index);
}
return base_ptr[index];
}
/// Returns a reference to the value of the requested index if initialized, or will otherwise return a zero-initialized object.
const T& GetOrDefault(std::size_t index) const {
#ifdef _WIN32
if (!IsCommittedPage(index)) {
return *reinterpret_cast<const T*>(&default_val);
}
#endif
// On non-Windows, OS page table should optimize this by pointing to a zero page if unallocated.
return base_ptr[index];
}
void Set(std::size_t index, const T& value) noexcept {
if (index > alloc_size / sizeof(T)) {
LOG_CRITICAL(Common_Memory, "Out of bounds write on SparseLargeVector @ {}", index);
return;
}
if (!IsCommittedPage(index))
CommitPage(index);
base_ptr[index] = value;
}
void ZeroRegion(std::size_t start, std::size_t end_) noexcept {
u64 base = reinterpret_cast<u64>(&base_ptr[start]);
const u64 end = reinterpret_cast<u64>(&base_ptr[end_]);
const u64 end_page = AlignUp(base, HostPageSize);
const u64 first_size = (std::min)(end_page, end) - base;
if (IsCommittedPage(start / sizeof(T))) {
std::memset(reinterpret_cast<void*>(base), 0, first_size);
}
if (end <= end_page)
return;
base = end_page;
for (u64 page = base; page < end; page += HostPageSize) {
if (!IsCommittedPage((page - reinterpret_cast<u64>(base_ptr)) / sizeof(T))) {
continue;
}
std::memset(reinterpret_cast<void*>(page), 0, (std::min)( HostPageSize, end - page));
}
}
constexpr void CommitRegion(size_t index, size_t end_) {
const u64 base = static_cast<u64>(index) * sizeof(T);
const u64 end = static_cast<u64>(end_) * sizeof(T);
for (u64 page = AlignDown(base, HostPageSize); page < end; page += HostPageSize) {
if (!IsCommittedPage(page / sizeof(T))) {
CommitPage(page / sizeof(T));
}
}
}
constexpr T& GetUnchecked(size_t index) {
return base_ptr[index];
}
[[nodiscard]] constexpr const T& operator[](std::size_t index) const noexcept {
return GetOrDefault(index);
}
[[nodiscard]] constexpr const T* data() const noexcept {
return base_ptr;
}
[[nodiscard]] constexpr std::size_t size() const noexcept {
return alloc_size / sizeof(T);
}
private:
[[nodiscard]] constexpr bool IsCommittedPage(std::size_t index) const noexcept {
if (index > alloc_size / sizeof(T)) {
LOG_CRITICAL(Common_Memory, "Out of bounds access on large vector @ {}", index);
return false;
}
auto page = (index * sizeof(T)) >> HostPageBits;
auto val = committed_pages[page >> 6].load(std::memory_order_acquire);
return (val >> (page & 63)) & 1;
}
constexpr void CommitPage(std::size_t index) noexcept {
auto page_index = (index * sizeof(T)) >> HostPageBits;
auto page = reinterpret_cast<uintptr_t>(base_ptr + index) & HostPageMask;
#if defined(_WIN32)
CommitVectorPage(page, true);
#else
mprotect(reinterpret_cast<void*>(page), HostPageSize, PROT_READ | PROT_WRITE);
#endif
committed_pages[page_index >> 6].fetch_or(1ULL << (page_index & 63), std::memory_order_release);
}
std::size_t alloc_size{};
T* base_ptr{};
std::vector<std::atomic<u64>> committed_pages{};
#ifdef _WIN32
const std::array<u8, sizeof(T)> default_val{};
#endif
};
} // namespace Common
+717 -93
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2014 Tony Wasserka // SPDX-FileCopyrightText: 2014 Tony Wasserka
@@ -7,128 +7,752 @@
#pragma once #pragma once
#ifdef __ARM_NEON
#include <arm_neon.h>
#endif
#include <cmath> #include <cmath>
#include <type_traits> #include <type_traits>
namespace Common { namespace Common {
template <typename T, size_t N> template <typename T>
class Vec { class Vec2;
template <typename T>
class Vec3;
template <typename T>
class Vec4;
template <typename T>
class Vec2 {
public: public:
std::array<T, N> elems{}; T x{};
T y{};
constexpr Vec() = default; constexpr Vec2() = default;
constexpr Vec(T e0) noexcept : elems{e0} {} constexpr Vec2(const T& x_, const T& y_) : x(x_), y(y_) {}
constexpr Vec(T e0, T e1) noexcept : elems{e0, e1} {}
constexpr Vec(T e0, T e1, T e2) noexcept : elems{e0, e1, e2} {}
constexpr Vec(T e0, T e1, T e2, T e4) noexcept : elems{e0, e1, e2, e4} {}
//explicit constexpr Vec(const std::initializer_list<T> elems_) noexcept : elems{elems_} {}
[[nodiscard]] constexpr Vec<decltype(T{} + T{}), N> operator+(const Vec o) const noexcept { template <typename T2>
Vec<decltype(T{} + T{}), N> r{}; [[nodiscard]] constexpr Vec2<T2> Cast() const {
for (size_t i = 0; i < N; ++i) return Vec2<T2>(static_cast<T2>(x), static_cast<T2>(y));
r.elems[i] = elems[i] + o.elems[i];
return r;
} }
constexpr Vec<T, N> operator+=(const Vec<T, N> o) noexcept { return *this = *this + o; }
[[nodiscard]] constexpr Vec<decltype(T{} - T{}), N> operator-(const Vec o) const noexcept { [[nodiscard]] static constexpr Vec2 AssignToAll(const T& f) {
Vec<decltype(T{} - T{}), N> r{}; return Vec2{f, f};
for (size_t i = 0; i < N; ++i) }
r.elems[i] = elems[i] - o.elems[i];
return r; [[nodiscard]] constexpr Vec2<decltype(T{} + T{})> operator+(const Vec2& other) const {
return {x + other.x, y + other.y};
}
constexpr Vec2& operator+=(const Vec2& other) {
x += other.x;
y += other.y;
return *this;
}
[[nodiscard]] constexpr Vec2<decltype(T{} - T{})> operator-(const Vec2& other) const {
return {x - other.x, y - other.y};
}
constexpr Vec2& operator-=(const Vec2& other) {
x -= other.x;
y -= other.y;
return *this;
} }
constexpr Vec<T, N> operator-=(const Vec<T, N> o) noexcept { return *this = *this - o; }
template <typename U = T> template <typename U = T>
[[nodiscard]] constexpr Vec<std::enable_if_t<std::is_signed_v<U>, U>, N> operator-() const noexcept { [[nodiscard]] constexpr Vec2<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
Vec<U, N> r{}; return {-x, -y};
for (size_t i = 0; i < N; ++i) }
r.elems[i] = -elems[i]; [[nodiscard]] constexpr Vec2<decltype(T{} * T{})> operator*(const Vec2& other) const {
return r; return {x * other.x, y * other.y};
} }
[[nodiscard]] constexpr Vec<decltype(T{} * T{}), N> operator*(const Vec o) const noexcept {
Vec<decltype(T{} * T{}), N> r{};
for (size_t i = 0; i < N; ++i)
r.elems[i] = elems[i] * o.elems[i];
return r;
}
template <typename V> template <typename V>
[[nodiscard]] constexpr Vec<decltype(T{} * V{}), N> operator*(const V f) const noexcept { [[nodiscard]] constexpr Vec2<decltype(T{} * V{})> operator*(const V& f) const {
using TV = decltype(T{} * V{}); using TV = decltype(T{} * V{});
using C = std::common_type_t<T, V>; using C = std::common_type_t<T, V>;
Vec<TV, N> r{};
for (size_t i = 0; i < N; ++i) return {
r.elems[i] = TV(C(elems[i]) * C(f)); static_cast<TV>(static_cast<C>(x) * static_cast<C>(f)),
return r; static_cast<TV>(static_cast<C>(y) * static_cast<C>(f)),
};
} }
template <typename V>
constexpr Vec<T, N> operator*=(const V f) noexcept { return *this = *this * f; }
template <typename V> template <typename V>
[[nodiscard]] constexpr Vec<decltype(T{} / V{}), N> operator/(const V f) const noexcept { constexpr Vec2& operator*=(const V& f) {
*this = *this * f;
return *this;
}
template <typename V>
[[nodiscard]] constexpr Vec2<decltype(T{} / V{})> operator/(const V& f) const {
using TV = decltype(T{} / V{}); using TV = decltype(T{} / V{});
using C = std::common_type_t<T, V>; using C = std::common_type_t<T, V>;
Vec<TV, N> r{};
for (size_t i = 0; i < N; ++i)
r.elems[i] = TV(C(elems[i]) / C(f));
return r;
}
template <typename V>
constexpr Vec<T, N> operator/=(const V f) noexcept { return *this = *this / f; }
[[nodiscard]] constexpr T Length2() const noexcept {
T r{};
for (size_t i = 0; i < N; ++i)
r += elems[i] * elems[i];
return r;
}
// Only implemented for T=float
[[nodiscard]] T Length() const { return T(std::sqrt(float(Length2()))); }
[[nodiscard]] Vec<T, N> Normalized() const { return *this / Length(); }
[[nodiscard]] constexpr T& operator[](std::size_t i) noexcept { return elems[i]; }
[[nodiscard]] constexpr const T& operator[](std::size_t i) const noexcept { return elems[i]; }
[[nodiscard]] std::array<decltype(-T{}), 16> ToMatrix() const {
const T x2 = elems[0] * elems[0];
const T y2 = elems[1] * elems[1];
const T z2 = elems[2] * elems[2];
const T xy = elems[0] * elems[1];
const T wz = elems[3] * elems[2];
const T xz = elems[0] * elems[2];
const T wy = elems[3] * elems[1];
const T yz = elems[1] * elems[2];
const T wx = elems[3] * elems[0];
return { return {
1.0f - 2.0f * (y2 + z2), static_cast<TV>(static_cast<C>(x) / static_cast<C>(f)),
2.0f * (xy + wz), static_cast<TV>(static_cast<C>(y) / static_cast<C>(f)),
2.0f * (xz - wy),
0.0f,
2.0f * (xy - wz),
1.0f - 2.0f * (x2 + z2),
2.0f * (yz + wx),
0.0f,
2.0f * (xz + wy),
2.0f * (yz - wx),
1.0f - 2.0f * (x2 + y2),
0.0f,
0.0f,
0.0f,
0.0f,
1.0f
}; };
} }
template <typename V>
constexpr Vec2& operator/=(const V& f) {
*this = *this / f;
return *this;
}
[[nodiscard]] constexpr T Length2() const {
return x * x + y * y;
}
// Only implemented for T=float
[[nodiscard]] float Length() const;
[[nodiscard]] float Normalize(); // returns the previous length, which is often useful
[[nodiscard]] constexpr T& operator[](std::size_t i) {
return *((&x) + i);
}
[[nodiscard]] constexpr const T& operator[](std::size_t i) const {
return *((&x) + i);
}
constexpr void SetZero() {
x = 0;
y = 0;
}
// Common aliases: UV (texel coordinates), ST (texture coordinates)
[[nodiscard]] constexpr T& u() {
return x;
}
[[nodiscard]] constexpr T& v() {
return y;
}
[[nodiscard]] constexpr T& s() {
return x;
}
[[nodiscard]] constexpr T& t() {
return y;
}
[[nodiscard]] constexpr const T& u() const {
return x;
}
[[nodiscard]] constexpr const T& v() const {
return y;
}
[[nodiscard]] constexpr const T& s() const {
return x;
}
[[nodiscard]] constexpr const T& t() const {
return y;
}
// swizzlers - create a subvector of specific components
[[nodiscard]] constexpr Vec2 yx() const {
return Vec2(y, x);
}
[[nodiscard]] constexpr Vec2 vu() const {
return Vec2(y, x);
}
[[nodiscard]] constexpr Vec2 ts() const {
return Vec2(y, x);
}
}; };
template <typename T, size_t N, typename V> template <typename T, typename V>
[[nodiscard]] constexpr Vec<T, N> operator*(const V f, const Vec<T, N> v) noexcept { [[nodiscard]] constexpr Vec2<T> operator*(const V& f, const Vec2<T>& vec) {
using C = std::common_type_t<T, V>; using C = std::common_type_t<T, V>;
Vec<T, N> r{};
for (size_t i = 0; i < N; ++i) return Vec2<T>(static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.x)),
r.elems[i] = T(C(f) * C(v.elems[i])); static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.y)));
return r; }
using Vec2f = Vec2<float>;
template <>
inline float Vec2<float>::Length() const {
return std::sqrt(x * x + y * y);
}
template <>
inline float Vec2<float>::Normalize() {
float length = Length();
*this /= length;
return length;
}
template <typename T>
class Vec3 {
public:
T x{};
T y{};
T z{};
constexpr Vec3() = default;
constexpr Vec3(const T& x_, const T& y_, const T& z_) : x(x_), y(y_), z(z_) {}
template <typename T2>
[[nodiscard]] constexpr Vec3<T2> Cast() const {
return Vec3<T2>(static_cast<T2>(x), static_cast<T2>(y), static_cast<T2>(z));
}
[[nodiscard]] static constexpr Vec3 AssignToAll(const T& f) {
return Vec3(f, f, f);
}
[[nodiscard]] constexpr Vec3<decltype(T{} + T{})> operator+(const Vec3& other) const {
return {x + other.x, y + other.y, z + other.z};
}
constexpr Vec3& operator+=(const Vec3& other) {
x += other.x;
y += other.y;
z += other.z;
return *this;
}
[[nodiscard]] constexpr Vec3<decltype(T{} - T{})> operator-(const Vec3& other) const {
return {x - other.x, y - other.y, z - other.z};
}
constexpr Vec3& operator-=(const Vec3& other) {
x -= other.x;
y -= other.y;
z -= other.z;
return *this;
}
template <typename U = T>
[[nodiscard]] constexpr Vec3<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
return {-x, -y, -z};
}
[[nodiscard]] constexpr Vec3<decltype(T{} * T{})> operator*(const Vec3& other) const {
return {x * other.x, y * other.y, z * other.z};
}
template <typename V>
[[nodiscard]] constexpr Vec3<decltype(T{} * V{})> operator*(const V& f) const {
using TV = decltype(T{} * V{});
using C = std::common_type_t<T, V>;
return {
static_cast<TV>(static_cast<C>(x) * static_cast<C>(f)),
static_cast<TV>(static_cast<C>(y) * static_cast<C>(f)),
static_cast<TV>(static_cast<C>(z) * static_cast<C>(f)),
};
}
template <typename V>
constexpr Vec3& operator*=(const V& f) {
*this = *this * f;
return *this;
}
template <typename V>
[[nodiscard]] constexpr Vec3<decltype(T{} / V{})> operator/(const V& f) const {
using TV = decltype(T{} / V{});
using C = std::common_type_t<T, V>;
return {
static_cast<TV>(static_cast<C>(x) / static_cast<C>(f)),
static_cast<TV>(static_cast<C>(y) / static_cast<C>(f)),
static_cast<TV>(static_cast<C>(z) / static_cast<C>(f)),
};
}
template <typename V>
constexpr Vec3& operator/=(const V& f) {
*this = *this / f;
return *this;
}
void RotateFromOrigin(float roll, float pitch, float yaw) {
float temp = y;
y = std::cos(roll) * y - std::sin(roll) * z;
z = std::sin(roll) * temp + std::cos(roll) * z;
temp = x;
x = std::cos(pitch) * x + std::sin(pitch) * z;
z = -std::sin(pitch) * temp + std::cos(pitch) * z;
temp = x;
x = std::cos(yaw) * x - std::sin(yaw) * y;
y = std::sin(yaw) * temp + std::cos(yaw) * y;
}
[[nodiscard]] constexpr T Length2() const {
return x * x + y * y + z * z;
}
// Only implemented for T=float
[[nodiscard]] float Length() const;
[[nodiscard]] Vec3 Normalized() const;
[[nodiscard]] float Normalize(); // returns the previous length, which is often useful
[[nodiscard]] constexpr T& operator[](std::size_t i) {
return *((&x) + i);
}
[[nodiscard]] constexpr const T& operator[](std::size_t i) const {
return *((&x) + i);
}
constexpr void SetZero() {
x = 0;
y = 0;
z = 0;
}
// Common aliases: UVW (texel coordinates), RGB (colors), STQ (texture coordinates)
[[nodiscard]] constexpr T& u() {
return x;
}
[[nodiscard]] constexpr T& v() {
return y;
}
[[nodiscard]] constexpr T& w() {
return z;
}
[[nodiscard]] constexpr T& r() {
return x;
}
[[nodiscard]] constexpr T& g() {
return y;
}
[[nodiscard]] constexpr T& b() {
return z;
}
[[nodiscard]] constexpr T& s() {
return x;
}
[[nodiscard]] constexpr T& t() {
return y;
}
[[nodiscard]] constexpr T& q() {
return z;
}
[[nodiscard]] constexpr const T& u() const {
return x;
}
[[nodiscard]] constexpr const T& v() const {
return y;
}
[[nodiscard]] constexpr const T& w() const {
return z;
}
[[nodiscard]] constexpr const T& r() const {
return x;
}
[[nodiscard]] constexpr const T& g() const {
return y;
}
[[nodiscard]] constexpr const T& b() const {
return z;
}
[[nodiscard]] constexpr const T& s() const {
return x;
}
[[nodiscard]] constexpr const T& t() const {
return y;
}
[[nodiscard]] constexpr const T& q() const {
return z;
}
// swizzlers - create a subvector of specific components
// e.g. Vec2 uv() { return Vec2(x,y); }
// _DEFINE_SWIZZLER2 defines a single such function, DEFINE_SWIZZLER2 defines all of them for all
// component names (x<->r) and permutations (xy<->yx)
#define _DEFINE_SWIZZLER2(a, b, name) \
[[nodiscard]] constexpr Vec2<T> name() const { return Vec2<T>(a, b); }
#define DEFINE_SWIZZLER2(a, b, a2, b2, a3, b3, a4, b4) \
_DEFINE_SWIZZLER2(a, b, a##b); \
_DEFINE_SWIZZLER2(a, b, a2##b2); \
_DEFINE_SWIZZLER2(a, b, a3##b3); \
_DEFINE_SWIZZLER2(a, b, a4##b4); \
_DEFINE_SWIZZLER2(b, a, b##a); \
_DEFINE_SWIZZLER2(b, a, b2##a2); \
_DEFINE_SWIZZLER2(b, a, b3##a3); \
_DEFINE_SWIZZLER2(b, a, b4##a4)
DEFINE_SWIZZLER2(x, y, r, g, u, v, s, t);
DEFINE_SWIZZLER2(x, z, r, b, u, w, s, q);
DEFINE_SWIZZLER2(y, z, g, b, v, w, t, q);
#undef DEFINE_SWIZZLER2
#undef _DEFINE_SWIZZLER2
};
template <typename T, typename V>
[[nodiscard]] constexpr Vec3<T> operator*(const V& f, const Vec3<T>& vec) {
using C = std::common_type_t<T, V>;
return Vec3<T>(static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.x)),
static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.y)),
static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.z)));
}
template <>
inline float Vec3<float>::Length() const {
return std::sqrt(x * x + y * y + z * z);
}
template <>
inline Vec3<float> Vec3<float>::Normalized() const {
return *this / Length();
}
template <>
inline float Vec3<float>::Normalize() {
float length = Length();
*this /= length;
return length;
}
using Vec3f = Vec3<float>;
template <typename T>
class Vec4 {
public:
T x{};
T y{};
T z{};
T w{};
constexpr Vec4() = default;
constexpr Vec4(const T& x_, const T& y_, const T& z_, const T& w_)
: x(x_), y(y_), z(z_), w(w_) {}
template <typename T2>
[[nodiscard]] constexpr Vec4<T2> Cast() const {
return Vec4<T2>(static_cast<T2>(x), static_cast<T2>(y), static_cast<T2>(z),
static_cast<T2>(w));
}
[[nodiscard]] static constexpr Vec4 AssignToAll(const T& f) {
return Vec4(f, f, f, f);
}
[[nodiscard]] constexpr Vec4<decltype(T{} + T{})> operator+(const Vec4& other) const {
return {x + other.x, y + other.y, z + other.z, w + other.w};
}
constexpr Vec4& operator+=(const Vec4& other) {
x += other.x;
y += other.y;
z += other.z;
w += other.w;
return *this;
}
[[nodiscard]] constexpr Vec4<decltype(T{} - T{})> operator-(const Vec4& other) const {
return {x - other.x, y - other.y, z - other.z, w - other.w};
}
constexpr Vec4& operator-=(const Vec4& other) {
x -= other.x;
y -= other.y;
z -= other.z;
w -= other.w;
return *this;
}
template <typename U = T>
[[nodiscard]] constexpr Vec4<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
return {-x, -y, -z, -w};
}
[[nodiscard]] constexpr Vec4<decltype(T{} * T{})> operator*(const Vec4& other) const {
return {x * other.x, y * other.y, z * other.z, w * other.w};
}
template <typename V>
[[nodiscard]] constexpr Vec4<decltype(T{} * V{})> operator*(const V& f) const {
using TV = decltype(T{} * V{});
using C = std::common_type_t<T, V>;
return {
static_cast<TV>(static_cast<C>(x) * static_cast<C>(f)),
static_cast<TV>(static_cast<C>(y) * static_cast<C>(f)),
static_cast<TV>(static_cast<C>(z) * static_cast<C>(f)),
static_cast<TV>(static_cast<C>(w) * static_cast<C>(f)),
};
}
template <typename V>
constexpr Vec4& operator*=(const V& f) {
*this = *this * f;
return *this;
}
template <typename V>
[[nodiscard]] constexpr Vec4<decltype(T{} / V{})> operator/(const V& f) const {
using TV = decltype(T{} / V{});
using C = std::common_type_t<T, V>;
return {
static_cast<TV>(static_cast<C>(x) / static_cast<C>(f)),
static_cast<TV>(static_cast<C>(y) / static_cast<C>(f)),
static_cast<TV>(static_cast<C>(z) / static_cast<C>(f)),
static_cast<TV>(static_cast<C>(w) / static_cast<C>(f)),
};
}
template <typename V>
constexpr Vec4& operator/=(const V& f) {
*this = *this / f;
return *this;
}
[[nodiscard]] constexpr T Length2() const {
return x * x + y * y + z * z + w * w;
}
[[nodiscard]] constexpr T& operator[](std::size_t i) {
return *((&x) + i);
}
[[nodiscard]] constexpr const T& operator[](std::size_t i) const {
return *((&x) + i);
}
constexpr void SetZero() {
x = 0;
y = 0;
z = 0;
w = 0;
}
// Common alias: RGBA (colors)
[[nodiscard]] constexpr T& r() {
return x;
}
[[nodiscard]] constexpr T& g() {
return y;
}
[[nodiscard]] constexpr T& b() {
return z;
}
[[nodiscard]] constexpr T& a() {
return w;
}
[[nodiscard]] constexpr const T& r() const {
return x;
}
[[nodiscard]] constexpr const T& g() const {
return y;
}
[[nodiscard]] constexpr const T& b() const {
return z;
}
[[nodiscard]] constexpr const T& a() const {
return w;
}
// Swizzlers - Create a subvector of specific components
// e.g. Vec2 uv() { return Vec2(x,y); }
// _DEFINE_SWIZZLER2 defines a single such function
// DEFINE_SWIZZLER2_COMP1 defines one-component functions for all component names (x<->r)
// DEFINE_SWIZZLER2_COMP2 defines two component functions for all component names (x<->r) and
// permutations (xy<->yx)
#define _DEFINE_SWIZZLER2(a, b, name) \
[[nodiscard]] constexpr Vec2<T> name() const { return Vec2<T>(a, b); }
#define DEFINE_SWIZZLER2_COMP1(a, a2) \
_DEFINE_SWIZZLER2(a, a, a##a); \
_DEFINE_SWIZZLER2(a, a, a2##a2)
#define DEFINE_SWIZZLER2_COMP2(a, b, a2, b2) \
_DEFINE_SWIZZLER2(a, b, a##b); \
_DEFINE_SWIZZLER2(a, b, a2##b2); \
_DEFINE_SWIZZLER2(b, a, b##a); \
_DEFINE_SWIZZLER2(b, a, b2##a2)
DEFINE_SWIZZLER2_COMP2(x, y, r, g);
DEFINE_SWIZZLER2_COMP2(x, z, r, b);
DEFINE_SWIZZLER2_COMP2(x, w, r, a);
DEFINE_SWIZZLER2_COMP2(y, z, g, b);
DEFINE_SWIZZLER2_COMP2(y, w, g, a);
DEFINE_SWIZZLER2_COMP2(z, w, b, a);
DEFINE_SWIZZLER2_COMP1(x, r);
DEFINE_SWIZZLER2_COMP1(y, g);
DEFINE_SWIZZLER2_COMP1(z, b);
DEFINE_SWIZZLER2_COMP1(w, a);
#undef DEFINE_SWIZZLER2_COMP1
#undef DEFINE_SWIZZLER2_COMP2
#undef _DEFINE_SWIZZLER2
#define _DEFINE_SWIZZLER3(a, b, c, name) \
[[nodiscard]] constexpr Vec3<T> name() const { return Vec3<T>(a, b, c); }
#define DEFINE_SWIZZLER3_COMP1(a, a2) \
_DEFINE_SWIZZLER3(a, a, a, a##a##a); \
_DEFINE_SWIZZLER3(a, a, a, a2##a2##a2)
#define DEFINE_SWIZZLER3_COMP3(a, b, c, a2, b2, c2) \
_DEFINE_SWIZZLER3(a, b, c, a##b##c); \
_DEFINE_SWIZZLER3(a, c, b, a##c##b); \
_DEFINE_SWIZZLER3(b, a, c, b##a##c); \
_DEFINE_SWIZZLER3(b, c, a, b##c##a); \
_DEFINE_SWIZZLER3(c, a, b, c##a##b); \
_DEFINE_SWIZZLER3(c, b, a, c##b##a); \
_DEFINE_SWIZZLER3(a, b, c, a2##b2##c2); \
_DEFINE_SWIZZLER3(a, c, b, a2##c2##b2); \
_DEFINE_SWIZZLER3(b, a, c, b2##a2##c2); \
_DEFINE_SWIZZLER3(b, c, a, b2##c2##a2); \
_DEFINE_SWIZZLER3(c, a, b, c2##a2##b2); \
_DEFINE_SWIZZLER3(c, b, a, c2##b2##a2)
DEFINE_SWIZZLER3_COMP3(x, y, z, r, g, b);
DEFINE_SWIZZLER3_COMP3(x, y, w, r, g, a);
DEFINE_SWIZZLER3_COMP3(x, z, w, r, b, a);
DEFINE_SWIZZLER3_COMP3(y, z, w, g, b, a);
DEFINE_SWIZZLER3_COMP1(x, r);
DEFINE_SWIZZLER3_COMP1(y, g);
DEFINE_SWIZZLER3_COMP1(z, b);
DEFINE_SWIZZLER3_COMP1(w, a);
#undef DEFINE_SWIZZLER3_COMP1
#undef DEFINE_SWIZZLER3_COMP3
#undef _DEFINE_SWIZZLER3
};
template <typename T, typename V>
[[nodiscard]] constexpr Vec4<decltype(V{} * T{})> operator*(const V& f, const Vec4<T>& vec) {
using TV = decltype(V{} * T{});
using C = std::common_type_t<T, V>;
return {
static_cast<TV>(static_cast<C>(f) * static_cast<C>(vec.x)),
static_cast<TV>(static_cast<C>(f) * static_cast<C>(vec.y)),
static_cast<TV>(static_cast<C>(f) * static_cast<C>(vec.z)),
static_cast<TV>(static_cast<C>(f) * static_cast<C>(vec.w)),
};
}
using Vec4f = Vec4<float>;
template <typename T>
constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec2<T>& a, const Vec2<T>& b) {
return a.x * b.x + a.y * b.y;
}
template <typename T>
[[nodiscard]] constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec3<T>& a, const Vec3<T>& b) {
return a.x * b.x + a.y * b.y + a.z * b.z;
}
template <typename T>
[[nodiscard]] constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec4<T>& a, const Vec4<T>& b) {
return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
}
template <>
[[nodiscard]] inline float Dot(const Vec4<float>& a, const Vec4<float>& b) {
#ifdef __ARM_NEON
float32x4_t va = vld1q_f32(&a.x);
float32x4_t vb = vld1q_f32(&b.x);
float32x4_t result = vmulq_f32(va, vb);
#if defined(__aarch64__) // Use vaddvq_f32 in ARMv8 architectures
return vaddvq_f32(result);
#else // Use manual addition for older architectures
float32x2_t sum2 = vadd_f32(vget_high_f32(result), vget_low_f32(result));
return vget_lane_f32(vpadd_f32(sum2, sum2), 0);
#endif
#else
return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
#endif
}
template <typename T>
[[nodiscard]] constexpr Vec3<decltype(T{} * T{} - T{} * T{})> Cross(const Vec3<T>& a,
const Vec3<T>& b) {
return {a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x};
}
// linear interpolation via float: 0.0=begin, 1.0=end
template <typename X>
[[nodiscard]] constexpr decltype(X{} * float{} + X{} * float{}) Lerp(const X& begin, const X& end,
const float t) {
return begin * (1.f - t) + end * t;
}
// linear interpolation via int: 0=begin, base=end
template <typename X, int base>
[[nodiscard]] constexpr decltype((X{} * int{} + X{} * int{}) / base) LerpInt(const X& begin,
const X& end,
const int t) {
return (begin * (base - t) + end * t) / base;
}
// bilinear interpolation. s is for interpolating x00-x01 and x10-x11, and t is for the second
// interpolation.
template <typename X>
[[nodiscard]] constexpr auto BilinearInterp(const X& x00, const X& x01, const X& x10, const X& x11,
const float s, const float t) {
auto y0 = Lerp(x00, x01, s);
auto y1 = Lerp(x10, x11, s);
return Lerp(y0, y1, t);
}
// Utility vector factories
template <typename T>
[[nodiscard]] constexpr Vec2<T> MakeVec(const T& x, const T& y) {
return Vec2<T>{x, y};
}
template <typename T>
[[nodiscard]] constexpr Vec3<T> MakeVec(const T& x, const T& y, const T& z) {
return Vec3<T>{x, y, z};
}
template <typename T>
[[nodiscard]] constexpr Vec4<T> MakeVec(const T& x, const T& y, const Vec2<T>& zw) {
return MakeVec(x, y, zw[0], zw[1]);
}
template <typename T>
[[nodiscard]] constexpr Vec3<T> MakeVec(const Vec2<T>& xy, const T& z) {
return MakeVec(xy[0], xy[1], z);
}
template <typename T>
[[nodiscard]] constexpr Vec3<T> MakeVec(const T& x, const Vec2<T>& yz) {
return MakeVec(x, yz[0], yz[1]);
}
template <typename T>
[[nodiscard]] constexpr Vec4<T> MakeVec(const T& x, const T& y, const T& z, const T& w) {
return Vec4<T>{x, y, z, w};
}
template <typename T>
[[nodiscard]] constexpr Vec4<T> MakeVec(const Vec2<T>& xy, const T& z, const T& w) {
return MakeVec(xy[0], xy[1], z, w);
}
template <typename T>
[[nodiscard]] constexpr Vec4<T> MakeVec(const T& x, const Vec2<T>& yz, const T& w) {
return MakeVec(x, yz[0], yz[1], w);
}
// NOTE: This has priority over "Vec2<Vec2<T>> MakeVec(const Vec2<T>& x, const Vec2<T>& y)".
// Even if someone wanted to use an odd object like Vec2<Vec2<T>>, the compiler would error
// out soon enough due to misuse of the returned structure.
template <typename T>
[[nodiscard]] constexpr Vec4<T> MakeVec(const Vec2<T>& xy, const Vec2<T>& zw) {
return MakeVec(xy[0], xy[1], zw[0], zw[1]);
}
template <typename T>
[[nodiscard]] constexpr Vec4<T> MakeVec(const Vec3<T>& xyz, const T& w) {
return MakeVec(xyz[0], xyz[1], xyz[2], w);
}
template <typename T>
[[nodiscard]] constexpr Vec4<T> MakeVec(const T& x, const Vec3<T>& yzw) {
return MakeVec(x, yzw[0], yzw[1], yzw[2]);
} }
} // namespace Common } // namespace Common
+44
View File
@@ -0,0 +1,44 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#ifdef _WIN32
#include <windows.h>
#else
#include <sys/mman.h>
#endif
#include "common/assert.h"
#include "common/virtual_buffer.h"
namespace Common {
void* AllocateMemoryPages(std::size_t size) noexcept {
#ifdef _WIN32
void* base = VirtualAlloc(nullptr, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (base == nullptr) {
// Probably failing to reserve is less likely than failing to commit
base = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE);
}
#else
void* base = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
if (base == MAP_FAILED)
base = nullptr;
#endif
ASSERT(base);
return base;
}
void FreeMemoryPages(void* base, [[maybe_unused]] std::size_t size) noexcept {
if (!base)
return;
#ifdef _WIN32
ASSERT(VirtualFree(base, 0, MEM_RELEASE));
#else
ASSERT(munmap(base, size) == 0);
#endif
}
} // namespace Common
+84
View File
@@ -0,0 +1,84 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <utility>
namespace Common {
void* AllocateMemoryPages(std::size_t size) noexcept;
void FreeMemoryPages(void* base, std::size_t size) noexcept;
template <typename T>
class VirtualBuffer final {
public:
// TODO: Uncomment this and change Common::PageTable::PageInfo to be trivially constructible
// using std::atomic_ref once libc++ has support for it
// static_assert(
// std::is_trivially_constructible_v<T>,
// "T must be trivially constructible, as non-trivial constructors will not be executed "
// "with the current allocator");
constexpr VirtualBuffer() = default;
explicit VirtualBuffer(std::size_t count) noexcept
: alloc_size{count * sizeof(T)}
{
base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size));
}
~VirtualBuffer() noexcept {
FreeMemoryPages(base_ptr, alloc_size);
}
VirtualBuffer(const VirtualBuffer&) = delete;
VirtualBuffer& operator=(const VirtualBuffer&) = delete;
VirtualBuffer(VirtualBuffer&& other) noexcept
: alloc_size{std::exchange(other.alloc_size, 0)}
, base_ptr{std::exchange(other.base_ptr, nullptr)}
{}
VirtualBuffer& operator=(VirtualBuffer&& other) noexcept {
alloc_size = std::exchange(other.alloc_size, 0);
base_ptr = std::exchange(other.base_ptr, nullptr);
return *this;
}
void resize(std::size_t count) noexcept {
if (auto const new_size = count * sizeof(T); new_size != alloc_size) {
FreeMemoryPages(base_ptr, alloc_size);
alloc_size = new_size;
base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size));
}
}
[[nodiscard]] constexpr const T& operator[](std::size_t index) const noexcept {
return base_ptr[index];
}
[[nodiscard]] constexpr T& operator[](std::size_t index) noexcept {
return base_ptr[index];
}
[[nodiscard]] constexpr T* data() noexcept {
return base_ptr;
}
[[nodiscard]] constexpr const T* data() const noexcept {
return base_ptr;
}
[[nodiscard]] constexpr std::size_t size() const noexcept {
return alloc_size / sizeof(T);
}
private:
std::size_t alloc_size{};
T* base_ptr{};
};
} // namespace Common
+6
View File
@@ -711,6 +711,8 @@ add_library(core STATIC
hle/service/kernel_helpers.h hle/service/kernel_helpers.h
hle/service/lbl/lbl.cpp hle/service/lbl/lbl.cpp
hle/service/lbl/lbl.h hle/service/lbl/lbl.h
hle/service/ldn/client_process_monitor.cpp
hle/service/ldn/client_process_monitor.h
hle/service/ldn/lan_discovery.cpp hle/service/ldn/lan_discovery.cpp
hle/service/ldn/lan_discovery.h hle/service/ldn/lan_discovery.h
hle/service/ldn/ldn.cpp hle/service/ldn/ldn.cpp
@@ -830,6 +832,8 @@ add_library(core STATIC
hle/service/ns/system_update_control.h hle/service/ns/system_update_control.h
hle/service/ns/system_update_interface.cpp hle/service/ns/system_update_interface.cpp
hle/service/ns/system_update_interface.h hle/service/ns/system_update_interface.h
hle/service/ns/vulnerability_manager_interface.cpp
hle/service/ns/vulnerability_manager_interface.h
hle/service/nvdrv/core/container.cpp hle/service/nvdrv/core/container.cpp
hle/service/nvdrv/core/container.h hle/service/nvdrv/core/container.h
hle/service/nvdrv/core/heap_mapper.cpp hle/service/nvdrv/core/heap_mapper.cpp
@@ -1063,6 +1067,8 @@ add_library(core STATIC
hle/service/sockets/sockets.h hle/service/sockets/sockets.h
hle/service/sockets/sockets_translate.cpp hle/service/sockets/sockets_translate.cpp
hle/service/sockets/sockets_translate.h hle/service/sockets/sockets_translate.h
hle/service/spl/csrng.cpp
hle/service/spl/csrng.h
hle/service/spl/spl.cpp hle/service/spl/spl.cpp
hle/service/spl/spl.h hle/service/spl/spl.h
hle/service/spl/spl_module.cpp hle/service/spl/spl_module.cpp
+5 -13
View File
@@ -172,12 +172,12 @@ void ArmDynarmic32::MakeJit(Common::PageTable* page_table) {
if (page_table) { if (page_table) {
constexpr size_t PageBits = 12; constexpr size_t PageBits = 12;
constexpr size_t NumPageTableEntries = 1 << (32 - PageBits); constexpr size_t NumPageTableEntries = 1 << (32 - PageBits);
constexpr size_t PageLog2Stride = 5;
static_assert(1 << PageLog2Stride == sizeof(Common::PageTable::PageEntryData));
// Dynarmic will not write to the page table, const_cast is safe here config.page_table = reinterpret_cast<std::array<std::uint8_t*, NumPageTableEntries>*>(page_table->entries.data());
config.page_table = reinterpret_cast<std::array<std::uint8_t*, NumPageTableEntries>*>( config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS;
const_cast<Common::PageTable::PageEntryData*>(page_table->entries.data())); config.page_table_log2_stride = PageLog2Stride;
config.page_table_pointer_mask = Common::PageTable::ATTRIBUTE_MASK;
config.page_table_marked_bit = 0;
config.absolute_offset_page_table = true; config.absolute_offset_page_table = true;
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128; config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
config.only_detect_misalignment_via_page_table_on_page_boundary = true; config.only_detect_misalignment_via_page_table_on_page_boundary = true;
@@ -188,13 +188,6 @@ void ArmDynarmic32::MakeJit(Common::PageTable* page_table) {
config.fastmem_exclusive_access = config.fastmem_pointer != std::nullopt; config.fastmem_exclusive_access = config.fastmem_pointer != std::nullopt;
config.recompile_on_exclusive_fastmem_failure = true; config.recompile_on_exclusive_fastmem_failure = true;
if (reinterpret_cast<u64>(m_system.DeviceMemory().buffer.BackingBasePointer() +
Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize()) < (1ULL << 39)) {
// Systems like FreeBSD allocate memory really low by default, and since we pack our page table entries,
// we have to manually sign extend when our actual pointer is negative.
config.page_table_sign_extension = Common::PageTable::SIGN_BIT;
}
} }
// Multi-process state // Multi-process state
@@ -427,7 +420,6 @@ void ArmDynarmic32::SignalInterrupt(Kernel::KThread* thread) {
} }
void ArmDynarmic32::ClearInstructionCache() { void ArmDynarmic32::ClearInstructionCache() {
m_cb->last_code_addr = u64(-1);
m_jit->ClearCache(); m_jit->ClearCache();
} }
+6 -13
View File
@@ -211,12 +211,13 @@ void ArmDynarmic64::MakeJit(Common::PageTable* page_table, std::size_t address_s
// Memory // Memory
if (page_table) { if (page_table) {
// Dynarmic will not write to the page table, const_cast is safe here constexpr size_t PageLog2Stride = 5;
config.page_table = reinterpret_cast<void**>( static_assert(1 << PageLog2Stride == sizeof(Common::PageTable::PageEntryData));
const_cast<Common::PageTable::PageEntryData*>(page_table->entries.data()));
config.page_table = reinterpret_cast<void**>(page_table->entries.data());
config.page_table_address_space_bits = std::uint32_t(address_space_bits); config.page_table_address_space_bits = std::uint32_t(address_space_bits);
config.page_table_pointer_mask = Common::PageTable::ATTRIBUTE_MASK; config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS;
config.page_table_marked_bit = 0; config.page_table_log2_stride = PageLog2Stride;
config.silently_mirror_page_table = false; config.silently_mirror_page_table = false;
config.absolute_offset_page_table = true; config.absolute_offset_page_table = true;
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128; config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
@@ -230,13 +231,6 @@ void ArmDynarmic64::MakeJit(Common::PageTable* page_table, std::size_t address_s
config.fastmem_exclusive_access = config.fastmem_pointer != std::nullopt; config.fastmem_exclusive_access = config.fastmem_pointer != std::nullopt;
config.recompile_on_exclusive_fastmem_failure = true; config.recompile_on_exclusive_fastmem_failure = true;
if (reinterpret_cast<u64>(m_system.DeviceMemory().buffer.BackingBasePointer() +
Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize()) < (1ULL << 39)) {
// Systems like FreeBSD allocate memory really low by default, and since we pack our page table entries,
// we have to manually sign extend when our actual pointer is negative.
config.page_table_sign_extension = Common::PageTable::SIGN_BIT;
}
} }
// Multi-process state // Multi-process state
@@ -453,7 +447,6 @@ void ArmDynarmic64::SignalInterrupt(Kernel::KThread* thread) {
} }
void ArmDynarmic64::ClearInstructionCache() { void ArmDynarmic64::ClearInstructionCache() {
m_cb->last_code_addr = u64(-1);
m_jit->ClearCache(); m_jit->ClearCache();
} }
+1 -4
View File
@@ -121,10 +121,7 @@ union Exclusive {
constexpr explicit Exclusive(u32 raw_) : raw{raw_} {} constexpr explicit Exclusive(u32 raw_) : raw{raw_} {}
constexpr bool Verify() { constexpr bool Verify() {
if (this->GetSig() != 0x10) return false; return this->GetSig() == 0x10;
const bool pair = decltype(l)::ExtractValue(raw) & 1;
const bool fixed_rt2 = decltype(rt2)::ExtractValue(raw) == 0b11111;
return pair || fixed_rt2;
} }
constexpr u32 GetSig() { constexpr u32 GetSig() {
+2 -8
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -29,11 +26,8 @@ public:
template <typename T> template <typename T>
Common::PhysicalAddress GetPhysicalAddr(const T* ptr) const { Common::PhysicalAddress GetPhysicalAddr(const T* ptr) const {
return GetPhysicalAddr(reinterpret_cast<uintptr_t>(ptr)); return (reinterpret_cast<uintptr_t>(ptr) -
} reinterpret_cast<uintptr_t>(buffer.BackingBasePointer())) +
Common::PhysicalAddress GetPhysicalAddr(uintptr_t ptr) const {
return (ptr - reinterpret_cast<uintptr_t>(buffer.BackingBasePointer())) +
DramMemoryMap::Base; DramMemoryMap::Base;
} }
+5 -5
View File
@@ -18,7 +18,7 @@
#include "common/common_types.h" #include "common/common_types.h"
#include "common/range_mutex.h" #include "common/range_mutex.h"
#include "common/scratch_buffer.h" #include "common/scratch_buffer.h"
#include "common/sparse_large_vector.h" #include "common/virtual_buffer.h"
namespace Core { namespace Core {
@@ -178,8 +178,8 @@ private:
u32 continuity_tracker; u32 continuity_tracker;
u32 compressed_physical_ptr; u32 compressed_physical_ptr;
}; };
Common::SparseLargeVector<u32> compressed_device_addr; Common::VirtualBuffer<u32> compressed_device_addr;
Common::SparseLargeVector<TrackedEntry> tracked_entries; Common::VirtualBuffer<TrackedEntry> tracked_entries;
// Process memory interfaces // Process memory interfaces
@@ -200,8 +200,8 @@ private:
return std::make_pair(asid, address); return std::make_pair(asid, address);
} }
constexpr void InsertCPUBacking(size_t page_index, VAddr address, Asid asid) { void InsertCPUBacking(size_t page_index, VAddr address, Asid asid) {
tracked_entries.GetUnchecked(page_index).cpu_backing_address = address | (asid.id << asid_start_bit); tracked_entries[page_index].cpu_backing_address = address | (asid.id << asid_start_bit);
} }
std::array<TranslationEntry, 4> t_slot{}; std::array<TranslationEntry, 4> t_slot{};
+27 -22
View File
@@ -177,6 +177,17 @@ DeviceMemoryManager<Traits>::DeviceMemoryManager(const DeviceMemory& device_memo
{ {
impl = std::make_unique<DeviceMemoryManagerAllocator<Traits>>(); impl = std::make_unique<DeviceMemoryManagerAllocator<Traits>>();
cached_pages = std::make_unique<CachedPages>(); cached_pages = std::make_unique<CachedPages>();
const size_t total_virtual = device_as_size >> Memory::YUZU_PAGEBITS;
for (size_t i = 0; i < total_virtual; i++) {
tracked_entries[i].compressed_physical_ptr = 0;
tracked_entries[i].continuity_tracker = 1;
tracked_entries[i].cpu_backing_address = 0;
}
const size_t total_phys = 1ULL << ((Settings::values.memory_layout_mode.GetValue() == Settings::MemoryLayout::Memory_4Gb ? physical_min_bits : physical_max_bits) - Memory::YUZU_PAGEBITS);
for (size_t i = 0; i < total_phys; i++) {
compressed_device_addr[i] = 0;
}
} }
template <typename Traits> template <typename Traits>
@@ -209,28 +220,26 @@ void DeviceMemoryManager<Traits>::Map(DAddr address, VAddr virtual_address, size
size_t start_page_d = address >> Memory::YUZU_PAGEBITS; size_t start_page_d = address >> Memory::YUZU_PAGEBITS;
size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS; size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS;
std::scoped_lock lk(mapping_guard); std::scoped_lock lk(mapping_guard);
tracked_entries.CommitRegion(start_page_d, start_page_d + num_pages);
for (size_t i = 0; i < num_pages; i++) { for (size_t i = 0; i < num_pages; i++) {
const VAddr new_vaddress = virtual_address + i * Memory::YUZU_PAGESIZE; const VAddr new_vaddress = virtual_address + i * Memory::YUZU_PAGESIZE;
auto* ptr = process_memory->GetPointerSilent(Common::ProcessAddress(new_vaddress)); auto* ptr = process_memory->GetPointerSilent(Common::ProcessAddress(new_vaddress));
if (ptr == nullptr) [[unlikely]] { if (ptr == nullptr) [[unlikely]] {
tracked_entries.GetUnchecked(start_page_d + i).compressed_physical_ptr = 0; tracked_entries[start_page_d + i].compressed_physical_ptr = 0;
continue; continue;
} }
auto phys_addr = static_cast<u32>(GetRawPhysicalAddr(ptr) >> Memory::YUZU_PAGEBITS) + 1U; auto phys_addr = static_cast<u32>(GetRawPhysicalAddr(ptr) >> Memory::YUZU_PAGEBITS) + 1U;
tracked_entries.GetUnchecked(start_page_d + i).compressed_physical_ptr = phys_addr; tracked_entries[start_page_d + i].compressed_physical_ptr = phys_addr;
InsertCPUBacking(start_page_d + i, new_vaddress, asid); InsertCPUBacking(start_page_d + i, new_vaddress, asid);
const u32 base_dev = compressed_device_addr[phys_addr - 1U]; const u32 base_dev = compressed_device_addr[phys_addr - 1U];
const u32 new_dev = static_cast<u32>(start_page_d + i); const u32 new_dev = static_cast<u32>(start_page_d + i);
if (base_dev == 0) [[likely]] { if (base_dev == 0) [[likely]] {
compressed_device_addr.GetAndFault(phys_addr - 1U) = new_dev; compressed_device_addr[phys_addr - 1U] = new_dev;
continue; continue;
} }
u32 start_id = base_dev & MULTI_MASK; u32 start_id = base_dev & MULTI_MASK;
if ((base_dev >> MULTI_FLAG_BITS) == 0) { if ((base_dev >> MULTI_FLAG_BITS) == 0) {
start_id = impl->multi_dev_address.Register(base_dev); start_id = impl->multi_dev_address.Register(base_dev);
compressed_device_addr.GetAndFault(phys_addr - 1U) = MULTI_FLAG | start_id; compressed_device_addr[phys_addr - 1U] = MULTI_FLAG | start_id;
} }
impl->multi_dev_address.Register(new_dev, start_id); impl->multi_dev_address.Register(new_dev, start_id);
} }
@@ -246,26 +255,24 @@ void DeviceMemoryManager<Traits>::Unmap(DAddr address, size_t size) {
size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS; size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS;
device_inter->InvalidateRegion(address, size); device_inter->InvalidateRegion(address, size);
std::scoped_lock lk(mapping_guard); std::scoped_lock lk(mapping_guard);
tracked_entries.CommitRegion(start_page_d, start_page_d + num_pages); // should already be committed, but just in case
for (size_t i = 0; i < num_pages; i++) { for (size_t i = 0; i < num_pages; i++) {
auto& entry = tracked_entries.GetUnchecked(start_page_d + i); auto phys_addr = tracked_entries[start_page_d + i].compressed_physical_ptr;
auto phys_addr = entry.compressed_physical_ptr; tracked_entries[start_page_d + i].compressed_physical_ptr = 0;
entry.compressed_physical_ptr = 0; tracked_entries[start_page_d + i].cpu_backing_address = 0;
entry.cpu_backing_address = 0;
if (phys_addr != 0) [[likely]] { if (phys_addr != 0) [[likely]] {
u32& base_dev = compressed_device_addr.GetAndFault(phys_addr - 1U); const u32 base_dev = compressed_device_addr[phys_addr - 1U];
if ((base_dev >> MULTI_FLAG_BITS) == 0) [[likely]] { if ((base_dev >> MULTI_FLAG_BITS) == 0) [[likely]] {
base_dev = 0; compressed_device_addr[phys_addr - 1] = 0;
continue; continue;
} }
const auto [more_entries, new_start] = impl->multi_dev_address.Unregister( const auto [more_entries, new_start] = impl->multi_dev_address.Unregister(
static_cast<u32>(start_page_d + i), base_dev & MULTI_MASK); static_cast<u32>(start_page_d + i), base_dev & MULTI_MASK);
if (!more_entries) { if (!more_entries) {
base_dev = impl->multi_dev_address.ReleaseEntry(new_start); compressed_device_addr[phys_addr - 1] =
impl->multi_dev_address.ReleaseEntry(new_start);
continue; continue;
} }
base_dev = new_start | MULTI_FLAG; compressed_device_addr[phys_addr - 1] = new_start | MULTI_FLAG;
} }
} }
t_slot = {}; t_slot = {};
@@ -278,8 +285,6 @@ void DeviceMemoryManager<Traits>::TrackContinuityImpl(DAddr address, VAddr virtu
size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS; size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS;
uintptr_t last_ptr = 0; uintptr_t last_ptr = 0;
size_t page_count = 1; size_t page_count = 1;
tracked_entries.CommitRegion(start_page_d, start_page_d + num_pages);
for (size_t i = num_pages; i > 0; i--) { for (size_t i = num_pages; i > 0; i--) {
size_t index = i - 1; size_t index = i - 1;
const VAddr new_vaddress = virtual_address + index * Memory::YUZU_PAGESIZE; const VAddr new_vaddress = virtual_address + index * Memory::YUZU_PAGESIZE;
@@ -291,14 +296,14 @@ void DeviceMemoryManager<Traits>::TrackContinuityImpl(DAddr address, VAddr virtu
page_count = 1; page_count = 1;
} }
last_ptr = new_ptr; last_ptr = new_ptr;
tracked_entries.GetUnchecked(start_page_d + index).continuity_tracker = static_cast<u32>(page_count) - 1; tracked_entries[start_page_d + index].continuity_tracker = static_cast<u32>(page_count);
} }
} }
template <typename Traits> template <typename Traits>
u8* DeviceMemoryManager<Traits>::GetSpan(const DAddr src_addr, const std::size_t size) { u8* DeviceMemoryManager<Traits>::GetSpan(const DAddr src_addr, const std::size_t size) {
size_t page_index = src_addr >> page_bits; size_t page_index = src_addr >> page_bits;
size_t subbits = src_addr & page_mask; size_t subbits = src_addr & page_mask;
if ((static_cast<size_t>(tracked_entries[page_index].continuity_tracker+1) << page_bits) >= size + subbits) { if ((static_cast<size_t>(tracked_entries[page_index].continuity_tracker) << page_bits) >= size + subbits) {
return GetPointer<u8>(src_addr); return GetPointer<u8>(src_addr);
} }
return nullptr; return nullptr;
@@ -308,7 +313,7 @@ template <typename Traits>
const u8* DeviceMemoryManager<Traits>::GetSpan(const DAddr src_addr, const std::size_t size) const { const u8* DeviceMemoryManager<Traits>::GetSpan(const DAddr src_addr, const std::size_t size) const {
size_t page_index = src_addr >> page_bits; size_t page_index = src_addr >> page_bits;
size_t subbits = src_addr & page_mask; size_t subbits = src_addr & page_mask;
if ((static_cast<size_t>(tracked_entries[page_index].continuity_tracker+1) << page_bits) >= size + subbits) { if ((static_cast<size_t>(tracked_entries[page_index].continuity_tracker) << page_bits) >= size + subbits) {
return GetPointer<u8>(src_addr); return GetPointer<u8>(src_addr);
} }
return nullptr; return nullptr;
@@ -378,7 +383,7 @@ void DeviceMemoryManager<Traits>::WalkBlock(DAddr addr, std::size_t size, auto o
std::size_t page_index = addr >> Memory::YUZU_PAGEBITS; std::size_t page_index = addr >> Memory::YUZU_PAGEBITS;
std::size_t page_offset = addr & Memory::YUZU_PAGEMASK; std::size_t page_offset = addr & Memory::YUZU_PAGEMASK;
while (remaining_size) { while (remaining_size) {
const size_t next_pages = std::size_t(tracked_entries[page_index].continuity_tracker+1); const size_t next_pages = std::size_t(tracked_entries[page_index].continuity_tracker);
const std::size_t copy_amount = (std::min)((next_pages << Memory::YUZU_PAGEBITS) - page_offset, remaining_size); const std::size_t copy_amount = (std::min)((next_pages << Memory::YUZU_PAGEBITS) - page_offset, remaining_size);
const auto current_vaddr = u64((page_index << Memory::YUZU_PAGEBITS) + page_offset); const auto current_vaddr = u64((page_index << Memory::YUZU_PAGEBITS) + page_offset);
SCOPE_EXIT{ SCOPE_EXIT{
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -124,7 +121,6 @@ size_t HierarchicalIntegrityVerificationStorage::Read(u8* buffer, size_t size,
} }
size_t HierarchicalIntegrityVerificationStorage::GetSize() const { size_t HierarchicalIntegrityVerificationStorage::GetSize() const {
ASSERT(m_data_size >= 0);
return m_data_size; return m_data_size;
} }
+5 -12
View File
@@ -120,7 +120,7 @@ std::array<u8, 32> IPSwitchCompiler::GetBuildID() const {
static IPSwitchRecord EscapeStringSequences(std::string_view sv) { static IPSwitchRecord EscapeStringSequences(std::string_view sv) {
IPSwitchRecord r{}; IPSwitchRecord r{};
for (auto it = sv.cbegin(); it < sv.cend(); ) { for (auto it = sv.cbegin(); it != sv.cend(); ) {
if (*it == '\\' && it + 1 < sv.cend()) { if (*it == '\\' && it + 1 < sv.cend()) {
switch (it[1]) { switch (it[1]) {
case 'a': r.data[r.count] = '\a'; break; case 'a': r.data[r.count] = '\a'; break;
@@ -198,8 +198,6 @@ void IPSwitchCompiler::Parse(std::span<u8 const> bytes) {
LOG_WARNING(Loader, "Unknown flag {}", line); LOG_WARNING(Loader, "Unknown flag {}", line);
break; break;
} }
} else if (patches.empty()) {
LOG_WARNING(Loader, "Invalid line not in a patch {}", line);
} else { } else {
size_t offset = size_t(std::strtoul(line.data(), nullptr, 16)); size_t offset = size_t(std::strtoul(line.data(), nullptr, 16));
offset += size_t(offset_shift); offset += size_t(offset_shift);
@@ -255,30 +253,25 @@ void IPSwitchCompiler::Parse(std::span<u8 const> bytes) {
// now make a nominal preprocessed line: remove comments // now make a nominal preprocessed line: remove comments
char quote = '\0'; char quote = '\0';
auto const sline_start = p; auto const sline_start = p;
auto last_char = p;
for (; p < sline.cend(); ) { for (; p < sline.cend(); ) {
// we dont check for "//", IPS checks for '/' only... // we dont check for "//", IPS checks for '/' only...
if (std::isspace(*p)) { if ((!quote && p[0] == '/')
++p;
} else if ((!quote && p[0] == '/')
|| (!quote && p[0] == '#')) { || (!quote && p[0] == '#')) {
break; break;
} else if (p[0] == '\"' || p[0] == '\'') { } else if (p[0] == '\"' || p[0] == '\'') {
quote = (p[0] == quote) ? '\0' : p[0]; quote = (p[0] == quote) ? '\0' : p[0];
++p; ++p;
last_char = p;
} else if (p + 1 < sline.cend() && p[0] == '\\') { } else if (p + 1 < sline.cend() && p[0] == '\\') {
p += 2; p += 2;
last_char = p;
} else { } else {
++p; ++p;
last_char = p;
} }
} }
// now we have the preprocessed string ;) // now we have the preprocessed string ;)
std::string_view const pp_str(sline_start, last_char); std::string_view pp_str(sline_start, p);
if (pp_str.size() > 0 && !parse_line(pp_str)) if (pp_str.size() > 0 && !parse_line(pp_str)) {
break; break;
}
} }
} }
} }
+40 -70
View File
@@ -635,36 +635,6 @@ Result KPageTableBase::CheckMemoryState(const KMemoryInfo& info, KMemoryState st
R_SUCCEED(); R_SUCCEED();
} }
bool KPageTableBase::BeginTraversal(const Common::PageTable &impl, TraversalEntry *out_entry, TraversalContext *out_context,
Common::ProcessAddress address) const {
out_context->next_offset = GetInteger(address);
out_context->next_page = GetInteger(address) >> PageBits;
return ContinueTraversal(impl, out_entry, out_context);
}
bool KPageTableBase::ContinueTraversal(const Common::PageTable &impl, TraversalEntry *out_entry,
TraversalContext *context) const {
// Setup invalid defaults.
out_entry->phys_addr = 0;
out_entry->block_size = PageSize;
// Validate that we can read the actual entry.
if (auto const page = context->next_page; page < impl.entries.size()) {
// Validate that the entry is mapped.
if (auto const paddr = impl.entries[page].Pointer(true); paddr != 0) {
// Populate the results and return true
out_entry->phys_addr = GetInteger(m_system.DeviceMemory().GetPhysicalAddr(paddr + context->next_offset));
context->next_page += 1;
context->next_offset += PageSize;
return true;
}
}
context->next_page += 1;
context->next_offset += PageSize;
// Otherwise return false
return false;
}
Result KPageTableBase::CheckMemoryStateContiguous(size_t* out_blocks_needed, KProcessAddress addr, Result KPageTableBase::CheckMemoryStateContiguous(size_t* out_blocks_needed, KProcessAddress addr,
size_t size, KMemoryState state_mask, size_t size, KMemoryState state_mask,
KMemoryState state, KMemoryPermission perm_mask, KMemoryState state, KMemoryPermission perm_mask,
@@ -970,7 +940,7 @@ Result KPageTableBase::QueryMappingImpl(KProcessAddress* out, KPhysicalAddress a
size_t tot_size = 0; size_t tot_size = 0;
next_valid = next_valid =
BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), region_start); impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), region_start);
next_entry.block_size = next_entry.block_size =
(next_entry.block_size - (GetInteger(region_start) & (next_entry.block_size - 1))); (next_entry.block_size - (GetInteger(region_start) & (next_entry.block_size - 1)));
@@ -1006,7 +976,7 @@ Result KPageTableBase::QueryMappingImpl(KProcessAddress* out, KPhysicalAddress a
break; break;
} }
next_valid = ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context)); next_valid = impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
} }
// Check the last entry. // Check the last entry.
@@ -1784,7 +1754,7 @@ Result KPageTableBase::MakePageGroup(KPageGroup& pg, KProcessAddress addr, size_
// Begin traversal. // Begin traversal.
TraversalContext context; TraversalContext context;
TraversalEntry next_entry; TraversalEntry next_entry;
R_UNLESS(BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), addr), R_UNLESS(impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), addr),
ResultInvalidCurrentMemory); ResultInvalidCurrentMemory);
// Prepare tracking variables. // Prepare tracking variables.
@@ -1794,7 +1764,7 @@ Result KPageTableBase::MakePageGroup(KPageGroup& pg, KProcessAddress addr, size_
// Iterate, adding to group as we go. // Iterate, adding to group as we go.
while (tot_size < size) { while (tot_size < size) {
R_UNLESS(ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context)), R_UNLESS(impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)),
ResultInvalidCurrentMemory); ResultInvalidCurrentMemory);
if (next_entry.phys_addr != (cur_addr + cur_size)) { if (next_entry.phys_addr != (cur_addr + cur_size)) {
@@ -1858,7 +1828,7 @@ bool KPageTableBase::IsValidPageGroup(const KPageGroup& pg, KProcessAddress addr
// Begin traversal. // Begin traversal.
TraversalContext context; TraversalContext context;
TraversalEntry next_entry; TraversalEntry next_entry;
if (!BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), addr)) { if (!impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), addr)) {
return false; return false;
} }
@@ -1869,7 +1839,7 @@ bool KPageTableBase::IsValidPageGroup(const KPageGroup& pg, KProcessAddress addr
// Iterate, comparing expected to actual. // Iterate, comparing expected to actual.
while (tot_size < size) { while (tot_size < size) {
if (!ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context))) { if (!impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context))) {
return false; return false;
} }
@@ -1926,7 +1896,7 @@ Result KPageTableBase::GetContiguousMemoryRangeWithState(
// Begin a traversal. // Begin a traversal.
TraversalContext context; TraversalContext context;
TraversalEntry cur_entry = {.phys_addr = 0, .block_size = 0}; TraversalEntry cur_entry = {.phys_addr = 0, .block_size = 0};
R_UNLESS(BeginTraversal(impl, std::addressof(cur_entry), std::addressof(context), address), R_UNLESS(impl.BeginTraversal(std::addressof(cur_entry), std::addressof(context), address),
ResultInvalidCurrentMemory); ResultInvalidCurrentMemory);
// Traverse until we have enough size or we aren't contiguous any more. // Traverse until we have enough size or we aren't contiguous any more.
@@ -1935,7 +1905,7 @@ Result KPageTableBase::GetContiguousMemoryRangeWithState(
for (contig_size = for (contig_size =
cur_entry.block_size - (GetInteger(phys_address) & (cur_entry.block_size - 1)); cur_entry.block_size - (GetInteger(phys_address) & (cur_entry.block_size - 1));
contig_size < size; contig_size += cur_entry.block_size) { contig_size < size; contig_size += cur_entry.block_size) {
if (!ContinueTraversal(impl, std::addressof(cur_entry), std::addressof(context))) { if (!impl.ContinueTraversal(std::addressof(cur_entry), std::addressof(context))) {
break; break;
} }
if (cur_entry.phys_addr != phys_address + contig_size) { if (cur_entry.phys_addr != phys_address + contig_size) {
@@ -2364,7 +2334,7 @@ Result KPageTableBase::QueryPhysicalAddress(Svc::lp64::PhysicalMemoryInfo* out,
TraversalContext context; TraversalContext context;
TraversalEntry next_entry; TraversalEntry next_entry;
bool traverse_valid = bool traverse_valid =
BeginTraversal(m_impl, std::addressof(next_entry), std::addressof(context), virt_addr); m_impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), virt_addr);
R_UNLESS(traverse_valid, ResultInvalidCurrentMemory); R_UNLESS(traverse_valid, ResultInvalidCurrentMemory);
// Set tracking variables. // Set tracking variables.
@@ -2375,7 +2345,7 @@ Result KPageTableBase::QueryPhysicalAddress(Svc::lp64::PhysicalMemoryInfo* out,
while (true) { while (true) {
// Continue the traversal. // Continue the traversal.
traverse_valid = traverse_valid =
ContinueTraversal(m_impl, std::addressof(next_entry), std::addressof(context)); m_impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
if (!traverse_valid) { if (!traverse_valid) {
break; break;
} }
@@ -2597,7 +2567,7 @@ Result KPageTableBase::UnmapIoRegion(KProcessAddress dst_address, KPhysicalAddre
TraversalContext context; TraversalContext context;
TraversalEntry next_entry; TraversalEntry next_entry;
ASSERT( ASSERT(
BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), dst_address)); impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), dst_address));
// Check that the physical region matches. // Check that the physical region matches.
R_UNLESS(next_entry.phys_addr == phys_addr, ResultInvalidMemoryRegion); R_UNLESS(next_entry.phys_addr == phys_addr, ResultInvalidMemoryRegion);
@@ -2607,7 +2577,7 @@ Result KPageTableBase::UnmapIoRegion(KProcessAddress dst_address, KPhysicalAddre
next_entry.block_size - (GetInteger(phys_addr) & (next_entry.block_size - 1)); next_entry.block_size - (GetInteger(phys_addr) & (next_entry.block_size - 1));
checked_size < size; checked_size += next_entry.block_size) { checked_size < size; checked_size += next_entry.block_size) {
// Continue the traversal. // Continue the traversal.
ASSERT(ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context))); ASSERT(impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)));
// Check that the physical region matches. // Check that the physical region matches.
R_UNLESS(next_entry.phys_addr == phys_addr + checked_size, ResultInvalidMemoryRegion); R_UNLESS(next_entry.phys_addr == phys_addr + checked_size, ResultInvalidMemoryRegion);
@@ -3059,7 +3029,7 @@ Result KPageTableBase::InvalidateProcessDataCache(KProcessAddress address, size_
TraversalContext context; TraversalContext context;
TraversalEntry next_entry; TraversalEntry next_entry;
bool traverse_valid = bool traverse_valid =
BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), address); impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), address);
R_UNLESS(traverse_valid, ResultInvalidCurrentMemory); R_UNLESS(traverse_valid, ResultInvalidCurrentMemory);
// Prepare tracking variables. // Prepare tracking variables.
@@ -3071,7 +3041,7 @@ Result KPageTableBase::InvalidateProcessDataCache(KProcessAddress address, size_
while (tot_size < size) { while (tot_size < size) {
// Continue the traversal. // Continue the traversal.
traverse_valid = traverse_valid =
ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context)); impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
R_UNLESS(traverse_valid, ResultInvalidCurrentMemory); R_UNLESS(traverse_valid, ResultInvalidCurrentMemory);
if (next_entry.phys_addr != (cur_addr + cur_size)) { if (next_entry.phys_addr != (cur_addr + cur_size)) {
@@ -3159,7 +3129,7 @@ Result KPageTableBase::ReadDebugMemory(KProcessAddress dst_address, KProcessAddr
TraversalContext context; TraversalContext context;
TraversalEntry next_entry; TraversalEntry next_entry;
bool traverse_valid = bool traverse_valid =
BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), src_address); impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), src_address);
R_UNLESS(traverse_valid, ResultInvalidCurrentMemory); R_UNLESS(traverse_valid, ResultInvalidCurrentMemory);
// Prepare tracking variables. // Prepare tracking variables.
@@ -3197,7 +3167,7 @@ Result KPageTableBase::ReadDebugMemory(KProcessAddress dst_address, KProcessAddr
while (tot_size < size) { while (tot_size < size) {
// Continue the traversal. // Continue the traversal.
traverse_valid = traverse_valid =
ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context)); impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
ASSERT(traverse_valid); ASSERT(traverse_valid);
if (next_entry.phys_addr != (cur_addr + cur_size)) { if (next_entry.phys_addr != (cur_addr + cur_size)) {
@@ -3255,7 +3225,7 @@ Result KPageTableBase::WriteDebugMemory(KProcessAddress dst_address, KProcessAdd
TraversalContext context; TraversalContext context;
TraversalEntry next_entry; TraversalEntry next_entry;
bool traverse_valid = bool traverse_valid =
BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), dst_address); impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), dst_address);
R_UNLESS(traverse_valid, ResultInvalidCurrentMemory); R_UNLESS(traverse_valid, ResultInvalidCurrentMemory);
// Prepare tracking variables. // Prepare tracking variables.
@@ -3297,7 +3267,7 @@ Result KPageTableBase::WriteDebugMemory(KProcessAddress dst_address, KProcessAdd
while (tot_size < size) { while (tot_size < size) {
// Continue the traversal. // Continue the traversal.
traverse_valid = traverse_valid =
ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context)); impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
ASSERT(traverse_valid); ASSERT(traverse_valid);
if (next_entry.phys_addr != (cur_addr + cur_size)) { if (next_entry.phys_addr != (cur_addr + cur_size)) {
@@ -3758,7 +3728,7 @@ Result KPageTableBase::CopyMemoryFromLinearToUser(
TraversalContext context; TraversalContext context;
TraversalEntry next_entry; TraversalEntry next_entry;
bool traverse_valid = bool traverse_valid =
BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), src_addr); impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), src_addr);
ASSERT(traverse_valid); ASSERT(traverse_valid);
// Prepare tracking variables. // Prepare tracking variables.
@@ -3798,7 +3768,7 @@ Result KPageTableBase::CopyMemoryFromLinearToUser(
while (tot_size < size) { while (tot_size < size) {
// Continue the traversal. // Continue the traversal.
traverse_valid = traverse_valid =
ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context)); impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
ASSERT(traverse_valid); ASSERT(traverse_valid);
if (next_entry.phys_addr != (cur_addr + cur_size)) { if (next_entry.phys_addr != (cur_addr + cur_size)) {
@@ -3852,7 +3822,7 @@ Result KPageTableBase::CopyMemoryFromLinearToKernel(
TraversalContext context; TraversalContext context;
TraversalEntry next_entry; TraversalEntry next_entry;
bool traverse_valid = bool traverse_valid =
BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), src_addr); impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), src_addr);
ASSERT(traverse_valid); ASSERT(traverse_valid);
// Prepare tracking variables. // Prepare tracking variables.
@@ -3875,7 +3845,7 @@ Result KPageTableBase::CopyMemoryFromLinearToKernel(
while (tot_size < size) { while (tot_size < size) {
// Continue the traversal. // Continue the traversal.
traverse_valid = traverse_valid =
ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context)); impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
ASSERT(traverse_valid); ASSERT(traverse_valid);
if (next_entry.phys_addr != (cur_addr + cur_size)) { if (next_entry.phys_addr != (cur_addr + cur_size)) {
@@ -3932,7 +3902,7 @@ Result KPageTableBase::CopyMemoryFromUserToLinear(
TraversalContext context; TraversalContext context;
TraversalEntry next_entry; TraversalEntry next_entry;
bool traverse_valid = bool traverse_valid =
BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), dst_addr); impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), dst_addr);
ASSERT(traverse_valid); ASSERT(traverse_valid);
// Prepare tracking variables. // Prepare tracking variables.
@@ -3971,7 +3941,7 @@ Result KPageTableBase::CopyMemoryFromUserToLinear(
while (tot_size < size) { while (tot_size < size) {
// Continue the traversal. // Continue the traversal.
traverse_valid = traverse_valid =
ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context)); impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
ASSERT(traverse_valid); ASSERT(traverse_valid);
if (next_entry.phys_addr != (cur_addr + cur_size)) { if (next_entry.phys_addr != (cur_addr + cur_size)) {
@@ -4027,7 +3997,7 @@ Result KPageTableBase::CopyMemoryFromKernelToLinear(KProcessAddress dst_addr, si
TraversalContext context; TraversalContext context;
TraversalEntry next_entry; TraversalEntry next_entry;
bool traverse_valid = bool traverse_valid =
BeginTraversal(impl, std::addressof(next_entry), std::addressof(context), dst_addr); impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), dst_addr);
ASSERT(traverse_valid); ASSERT(traverse_valid);
// Prepare tracking variables. // Prepare tracking variables.
@@ -4050,7 +4020,7 @@ Result KPageTableBase::CopyMemoryFromKernelToLinear(KProcessAddress dst_addr, si
while (tot_size < size) { while (tot_size < size) {
// Continue the traversal. // Continue the traversal.
traverse_valid = traverse_valid =
ContinueTraversal(impl, std::addressof(next_entry), std::addressof(context)); impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
ASSERT(traverse_valid); ASSERT(traverse_valid);
if (next_entry.phys_addr != (cur_addr + cur_size)) { if (next_entry.phys_addr != (cur_addr + cur_size)) {
@@ -4119,10 +4089,10 @@ Result KPageTableBase::CopyMemoryFromHeapToHeap(
bool traverse_valid; bool traverse_valid;
// Begin traversal. // Begin traversal.
traverse_valid = BeginTraversal(src_impl, std::addressof(src_next_entry), traverse_valid = src_impl.BeginTraversal(std::addressof(src_next_entry),
std::addressof(src_context), src_addr); std::addressof(src_context), src_addr);
ASSERT(traverse_valid); ASSERT(traverse_valid);
traverse_valid = BeginTraversal(dst_impl, std::addressof(dst_next_entry), traverse_valid = dst_impl.BeginTraversal(std::addressof(dst_next_entry),
std::addressof(dst_context), dst_addr); std::addressof(dst_context), dst_addr);
ASSERT(traverse_valid); ASSERT(traverse_valid);
@@ -4157,7 +4127,7 @@ Result KPageTableBase::CopyMemoryFromHeapToHeap(
if (ofs + cur_copy_size != size) { if (ofs + cur_copy_size != size) {
if (cur_src_addr + cur_min_size == cur_src_block_addr + cur_src_size) { if (cur_src_addr + cur_min_size == cur_src_block_addr + cur_src_size) {
// Continue the src traversal. // Continue the src traversal.
traverse_valid = ContinueTraversal(src_impl, std::addressof(src_next_entry), traverse_valid = src_impl.ContinueTraversal(std::addressof(src_next_entry),
std::addressof(src_context)); std::addressof(src_context));
ASSERT(traverse_valid); ASSERT(traverse_valid);
@@ -4168,7 +4138,7 @@ Result KPageTableBase::CopyMemoryFromHeapToHeap(
if (cur_dst_addr + cur_min_size == if (cur_dst_addr + cur_min_size ==
dst_next_entry.phys_addr + dst_next_entry.block_size) { dst_next_entry.phys_addr + dst_next_entry.block_size) {
// Continue the dst traversal. // Continue the dst traversal.
traverse_valid = ContinueTraversal(dst_impl, std::addressof(dst_next_entry), traverse_valid = dst_impl.ContinueTraversal(std::addressof(dst_next_entry),
std::addressof(dst_context)); std::addressof(dst_context));
ASSERT(traverse_valid); ASSERT(traverse_valid);
@@ -4253,10 +4223,10 @@ Result KPageTableBase::CopyMemoryFromHeapToHeapWithoutCheckDestination(
bool traverse_valid; bool traverse_valid;
// Begin traversal. // Begin traversal.
traverse_valid = BeginTraversal(src_impl, std::addressof(src_next_entry), traverse_valid = src_impl.BeginTraversal(std::addressof(src_next_entry),
std::addressof(src_context), src_addr); std::addressof(src_context), src_addr);
ASSERT(traverse_valid); ASSERT(traverse_valid);
traverse_valid = BeginTraversal(dst_impl, std::addressof(dst_next_entry), traverse_valid = dst_impl.BeginTraversal(std::addressof(dst_next_entry),
std::addressof(dst_context), dst_addr); std::addressof(dst_context), dst_addr);
ASSERT(traverse_valid); ASSERT(traverse_valid);
@@ -4291,7 +4261,7 @@ Result KPageTableBase::CopyMemoryFromHeapToHeapWithoutCheckDestination(
if (ofs + cur_copy_size != size) { if (ofs + cur_copy_size != size) {
if (cur_src_addr + cur_min_size == cur_src_block_addr + cur_src_size) { if (cur_src_addr + cur_min_size == cur_src_block_addr + cur_src_size) {
// Continue the src traversal. // Continue the src traversal.
traverse_valid = ContinueTraversal(src_impl, std::addressof(src_next_entry), traverse_valid = src_impl.ContinueTraversal(std::addressof(src_next_entry),
std::addressof(src_context)); std::addressof(src_context));
ASSERT(traverse_valid); ASSERT(traverse_valid);
@@ -4302,7 +4272,7 @@ Result KPageTableBase::CopyMemoryFromHeapToHeapWithoutCheckDestination(
if (cur_dst_addr + cur_min_size == if (cur_dst_addr + cur_min_size ==
dst_next_entry.phys_addr + dst_next_entry.block_size) { dst_next_entry.phys_addr + dst_next_entry.block_size) {
// Continue the dst traversal. // Continue the dst traversal.
traverse_valid = ContinueTraversal(dst_impl, std::addressof(dst_next_entry), traverse_valid = dst_impl.ContinueTraversal(std::addressof(dst_next_entry),
std::addressof(dst_context)); std::addressof(dst_context));
ASSERT(traverse_valid); ASSERT(traverse_valid);
@@ -4577,7 +4547,7 @@ Result KPageTableBase::SetupForIpcServer(KProcessAddress* out_addr, size_t size,
// Begin traversal. // Begin traversal.
TraversalContext context; TraversalContext context;
TraversalEntry next_entry; TraversalEntry next_entry;
bool traverse_valid = BeginTraversal(src_impl, std::addressof(next_entry), bool traverse_valid = src_impl.BeginTraversal(std::addressof(next_entry),
std::addressof(context), aligned_src_start); std::addressof(context), aligned_src_start);
ASSERT(traverse_valid); ASSERT(traverse_valid);
@@ -4627,7 +4597,7 @@ Result KPageTableBase::SetupForIpcServer(KProcessAddress* out_addr, size_t size,
// If the block's size was one page, we may need to continue traversal. // If the block's size was one page, we may need to continue traversal.
if (cur_block_size == 0 && aligned_src_size > PageSize) { if (cur_block_size == 0 && aligned_src_size > PageSize) {
traverse_valid = traverse_valid =
ContinueTraversal(src_impl, std::addressof(next_entry), std::addressof(context)); src_impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
ASSERT(traverse_valid); ASSERT(traverse_valid);
cur_block_addr = next_entry.phys_addr; cur_block_addr = next_entry.phys_addr;
@@ -4640,7 +4610,7 @@ Result KPageTableBase::SetupForIpcServer(KProcessAddress* out_addr, size_t size,
while (aligned_src_start + tot_block_size < mapping_src_end) { while (aligned_src_start + tot_block_size < mapping_src_end) {
// Continue the traversal. // Continue the traversal.
traverse_valid = traverse_valid =
ContinueTraversal(src_impl, std::addressof(next_entry), std::addressof(context)); src_impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
ASSERT(traverse_valid); ASSERT(traverse_valid);
// Process the block. // Process the block.
@@ -4683,7 +4653,7 @@ Result KPageTableBase::SetupForIpcServer(KProcessAddress* out_addr, size_t size,
if (mapped_block_end + cur_block_size < aligned_src_end && if (mapped_block_end + cur_block_size < aligned_src_end &&
cur_block_size == last_block_size) { cur_block_size == last_block_size) {
traverse_valid = traverse_valid =
ContinueTraversal(src_impl, std::addressof(next_entry), std::addressof(context)); src_impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
ASSERT(traverse_valid); ASSERT(traverse_valid);
cur_block_addr = next_entry.phys_addr; cur_block_addr = next_entry.phys_addr;
@@ -5631,7 +5601,7 @@ Result KPageTableBase::UnmapProcessMemory(KProcessAddress dst_address, size_t si
ContiguousRangeInfo(KPageTableBase& pt, KProcessAddress address, size_t size) ContiguousRangeInfo(KPageTableBase& pt, KProcessAddress address, size_t size)
: m_pt(pt), m_remaining_size(size) { : m_pt(pt), m_remaining_size(size) {
// Begin a traversal. // Begin a traversal.
ASSERT(m_pt.BeginTraversal(m_pt.GetImpl(), std::addressof(m_entry), ASSERT(m_pt.GetImpl().BeginTraversal(std::addressof(m_entry),
std::addressof(m_context), address)); std::addressof(m_context), address));
// Setup tracking fields. // Setup tracking fields.
@@ -5662,7 +5632,7 @@ Result KPageTableBase::UnmapProcessMemory(KProcessAddress dst_address, size_t si
void DetermineContiguousBlockExtents() { void DetermineContiguousBlockExtents() {
// Continue traversing until we're not contiguous, or we have enough. // Continue traversing until we're not contiguous, or we have enough.
while (m_cur_size < m_remaining_size) { while (m_cur_size < m_remaining_size) {
ASSERT(m_pt.ContinueTraversal(m_pt.GetImpl(), std::addressof(m_entry), ASSERT(m_pt.GetImpl().ContinueTraversal(std::addressof(m_entry),
std::addressof(m_context))); std::addressof(m_context)));
// If we're not contiguous, we're done. // If we're not contiguous, we're done.
+1 -12
View File
@@ -370,10 +370,6 @@ private:
size_t num_pages, size_t alignment, size_t offset, size_t num_pages, size_t alignment, size_t offset,
size_t guard_pages) const; size_t guard_pages) const;
bool BeginTraversal(const Common::PageTable& impl, TraversalEntry* out_entry, TraversalContext* out_context,
Common::ProcessAddress address) const;
bool ContinueTraversal(const Common::PageTable& impl, TraversalEntry* out_entry, TraversalContext* context) const;
Result CheckMemoryStateContiguous(size_t* out_blocks_needed, KProcessAddress addr, size_t size, Result CheckMemoryStateContiguous(size_t* out_blocks_needed, KProcessAddress addr, size_t size,
KMemoryState state_mask, KMemoryState state, KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm, KMemoryPermission perm_mask, KMemoryPermission perm,
@@ -478,14 +474,7 @@ private:
// Validate pre-conditions. // Validate pre-conditions.
ASSERT(this->IsLockedByCurrentThread()); ASSERT(this->IsLockedByCurrentThread());
if (virt_addr > (1ULL << m_address_space_width)) { return this->GetImpl().GetPhysicalAddress(out, virt_addr);
return false;
}
*out = m_system.DeviceMemory().GetPhysicalAddr(
this->GetImpl().entries[GetInteger(virt_addr) >> PageBits].Pointer(true) + GetInteger(virt_addr));
return true;
} }
public: public:
+6 -7
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-late // SPDX-License-Identifier: GPL-2.0-or-late
@@ -2501,15 +2501,14 @@ void Call(Core::System& system, u32 imm) {
auto& process = GetCurrentProcess(kernel); auto& process = GetCurrentProcess(kernel);
std::array<uint64_t, 8> args; std::array<uint64_t, 8> args;
kernel.CurrentPhysicalCore().SaveSvcArguments(process, args); kernel.CurrentPhysicalCore().SaveSvcArguments(process, args);
//kernel.EnterSVCProfile();
LOG_TRACE(Kernel_SVC, "{} [0]={:#x} [1]={:#x} [2]={:#x} [3]={:#x} [4]={:#x} [5]={:#x} [6]={:#x}", LOG_TRACE(Kernel_SVC, "{} [0]={:#x} [1]={:#x} [2]={:#x} [3]={:#x} [4]={:#x} [5]={:#x} [6]={:#x}",
imm, GetArg64(args, 0), GetArg64(args, 1), GetArg64(args, 2), imm, GetArg32(args, 0), GetArg32(args, 1), GetArg32(args, 2),
GetArg64(args, 3), GetArg64(args, 4), GetArg64(args, 5), GetArg64(args, 6)); GetArg32(args, 3), GetArg32(args, 4), GetArg32(args, 5), GetArg32(args, 6));
if (process.Is64Bit()) { //kernel.EnterSVCProfile();
if (process.Is64Bit())
Call64(system, imm, args); Call64(system, imm, args);
} else { else
Call32(system, imm, args); Call32(system, imm, args);
}
//kernel.ExitSVCProfile(); //kernel.ExitSVCProfile();
kernel.CurrentPhysicalCore().LoadSvcArguments(process, args); kernel.CurrentPhysicalCore().LoadSvcArguments(process, args);
} }
+72 -58
View File
@@ -10,15 +10,17 @@
#include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_resource_limit.h" #include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/svc.h" #include "core/hle/kernel/svc.h"
#include "core/hle/kernel/svc_results.h"
#include "core/hle/kernel/svc_version.h"
namespace Kernel::Svc { namespace Kernel::Svc {
/// Gets system/memory information for the current process /// Gets system/memory information for the current process
Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle handle, u64 info_sub_id) { Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle handle,
LOG_TRACE(Kernel_SVC, "called info_id={:#x}, info_sub_id={:#x}, handle={:#08x}", info_id_type, info_sub_id, handle); u64 info_sub_id) {
u32 info_id = u32(info_id_type); LOG_TRACE(Kernel_SVC, "called info_id={:#x}, info_sub_id={:#x}, handle={:#08x}",
info_id_type, info_sub_id, handle);
u32 info_id = static_cast<u32>(info_id_type);
switch (info_id_type) { switch (info_id_type) {
case InfoType::CoreMask: case InfoType::CoreMask:
case InfoType::PriorityMask: case InfoType::PriorityMask:
@@ -51,123 +53,152 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle
case InfoType::CoreMask: case InfoType::CoreMask:
*result = process->GetCoreMask(); *result = process->GetCoreMask();
R_SUCCEED(); R_SUCCEED();
case InfoType::PriorityMask: case InfoType::PriorityMask:
*result = process->GetPriorityMask(); *result = process->GetPriorityMask();
R_SUCCEED(); R_SUCCEED();
case InfoType::AliasRegionAddress: case InfoType::AliasRegionAddress:
*result = GetInteger(process->GetPageTable().GetAliasRegionStart()); *result = GetInteger(process->GetPageTable().GetAliasRegionStart());
R_SUCCEED(); R_SUCCEED();
case InfoType::AliasRegionSize: case InfoType::AliasRegionSize:
*result = process->GetPageTable().GetAliasRegionSize(); *result = process->GetPageTable().GetAliasRegionSize();
R_SUCCEED(); R_SUCCEED();
case InfoType::HeapRegionAddress: case InfoType::HeapRegionAddress:
*result = GetInteger(process->GetPageTable().GetHeapRegionStart()); *result = GetInteger(process->GetPageTable().GetHeapRegionStart());
R_SUCCEED(); R_SUCCEED();
case InfoType::HeapRegionSize: case InfoType::HeapRegionSize:
*result = process->GetPageTable().GetHeapRegionSize(); *result = process->GetPageTable().GetHeapRegionSize();
R_SUCCEED(); R_SUCCEED();
case InfoType::AslrRegionAddress: case InfoType::AslrRegionAddress:
*result = GetInteger(process->GetPageTable().GetAliasCodeRegionStart()); *result = GetInteger(process->GetPageTable().GetAliasCodeRegionStart());
R_SUCCEED(); R_SUCCEED();
case InfoType::AslrRegionSize: case InfoType::AslrRegionSize:
*result = process->GetPageTable().GetAliasCodeRegionSize(); *result = process->GetPageTable().GetAliasCodeRegionSize();
R_SUCCEED(); R_SUCCEED();
case InfoType::StackRegionAddress: case InfoType::StackRegionAddress:
*result = GetInteger(process->GetPageTable().GetStackRegionStart()); *result = GetInteger(process->GetPageTable().GetStackRegionStart());
R_SUCCEED(); R_SUCCEED();
case InfoType::StackRegionSize: case InfoType::StackRegionSize:
*result = process->GetPageTable().GetStackRegionSize(); *result = process->GetPageTable().GetStackRegionSize();
R_SUCCEED(); R_SUCCEED();
case InfoType::TotalMemorySize: case InfoType::TotalMemorySize:
*result = process->GetTotalUserPhysicalMemorySize(system.Kernel()); *result = process->GetTotalUserPhysicalMemorySize(system.Kernel());
R_SUCCEED(); R_SUCCEED();
case InfoType::UsedMemorySize: case InfoType::UsedMemorySize:
*result = process->GetUsedUserPhysicalMemorySize(system.Kernel()); *result = process->GetUsedUserPhysicalMemorySize(system.Kernel());
R_SUCCEED(); R_SUCCEED();
case InfoType::SystemResourceSizeTotal: case InfoType::SystemResourceSizeTotal:
*result = process->GetTotalSystemResourceSize(); *result = process->GetTotalSystemResourceSize();
R_SUCCEED(); R_SUCCEED();
case InfoType::SystemResourceSizeUsed: case InfoType::SystemResourceSizeUsed:
*result = process->GetUsedSystemResourceSize(); *result = process->GetUsedSystemResourceSize();
R_SUCCEED(); R_SUCCEED();
case InfoType::ProgramId: case InfoType::ProgramId:
*result = process->GetProgramId(); *result = process->GetProgramId();
R_SUCCEED(); R_SUCCEED();
case InfoType::UserExceptionContextAddress: case InfoType::UserExceptionContextAddress:
*result = GetInteger(process->GetProcessLocalRegionAddress()); *result = GetInteger(process->GetProcessLocalRegionAddress());
R_SUCCEED(); R_SUCCEED();
case InfoType::TotalNonSystemMemorySize: case InfoType::TotalNonSystemMemorySize:
*result = process->GetTotalNonSystemUserPhysicalMemorySize(system.Kernel()); *result = process->GetTotalNonSystemUserPhysicalMemorySize(system.Kernel());
R_SUCCEED(); R_SUCCEED();
case InfoType::UsedNonSystemMemorySize: case InfoType::UsedNonSystemMemorySize:
*result = process->GetUsedNonSystemUserPhysicalMemorySize(system.Kernel()); *result = process->GetUsedNonSystemUserPhysicalMemorySize(system.Kernel());
R_SUCCEED(); R_SUCCEED();
case InfoType::IsApplication: case InfoType::IsApplication:
*result = process->IsApplication(); *result = process->IsApplication();
R_SUCCEED(); R_SUCCEED();
case InfoType::FreeThreadCount: case InfoType::FreeThreadCount:
if (KResourceLimit* resource_limit = process->GetResourceLimit(); if (KResourceLimit* resource_limit = process->GetResourceLimit();
resource_limit != nullptr) { resource_limit != nullptr) {
const auto current_value = resource_limit->GetCurrentValue(Svc::LimitableResource::ThreadCountMax); const auto current_value =
const auto limit_value = resource_limit->GetLimitValue(Svc::LimitableResource::ThreadCountMax); resource_limit->GetCurrentValue(Svc::LimitableResource::ThreadCountMax);
const auto limit_value =
resource_limit->GetLimitValue(Svc::LimitableResource::ThreadCountMax);
*result = limit_value - current_value; *result = limit_value - current_value;
} else { } else {
*result = 0; *result = 0;
} }
R_SUCCEED(); R_SUCCEED();
case InfoType::AliasRegionExtraSize: { case InfoType::AliasRegionExtraSize: {
R_UNLESS(info_sub_id == 0, ResultInvalidCombination); if (info_sub_id != 0) {
return ResultInvalidCombination;
}
KProcess* current_process = GetCurrentProcessPointer(system.Kernel()); KProcess* current_process = GetCurrentProcessPointer(system.Kernel());
*result = current_process->GetPageTable().GetAliasRegionExtraSize(); *result = current_process->GetPageTable().GetAliasRegionExtraSize();
R_SUCCEED(); R_SUCCEED();
} }
default: default:
break; break;
} }
LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id={:#016x}", info_id); LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id={:#016x}", info_id);
R_THROW(ResultInvalidEnumValue); R_THROW(ResultInvalidEnumValue);
} }
case InfoType::DebuggerAttached: case InfoType::DebuggerAttached:
*result = 0; *result = 0;
R_SUCCEED(); R_SUCCEED();
case InfoType::ResourceLimit: { case InfoType::ResourceLimit: {
R_UNLESS(handle == 0, ResultInvalidHandle); R_UNLESS(handle == 0, ResultInvalidHandle);
R_UNLESS(info_sub_id == 0, ResultInvalidCombination); R_UNLESS(info_sub_id == 0, ResultInvalidCombination);
KProcess* const current_process = GetCurrentProcessPointer(system.Kernel()); KProcess* const current_process = GetCurrentProcessPointer(system.Kernel());
KHandleTable& handle_table = current_process->GetHandleTable(); KHandleTable& handle_table = current_process->GetHandleTable();
if (auto const resource_limit = current_process->GetResourceLimit(); resource_limit) { const auto resource_limit = current_process->GetResourceLimit();
Handle resource_handle{}; if (!resource_limit) {
R_TRY(handle_table.Add(system.Kernel(), std::addressof(resource_handle), resource_limit));
*result = resource_handle;
} else {
*result = Svc::InvalidHandle; *result = Svc::InvalidHandle;
// Yes, the kernel considers this a successful operation.
R_SUCCEED();
} }
// Yes, the kernel considers this a successful operation either way.
Handle resource_handle{};
R_TRY(handle_table.Add(system.Kernel(), std::addressof(resource_handle), resource_limit));
*result = resource_handle;
R_SUCCEED(); R_SUCCEED();
} }
case InfoType::RandomEntropy: case InfoType::RandomEntropy:
R_UNLESS(handle == 0, ResultInvalidHandle); R_UNLESS(handle == 0, ResultInvalidHandle);
R_UNLESS(info_sub_id < 4, ResultInvalidCombination); R_UNLESS(info_sub_id < 4, ResultInvalidCombination);
*result = GetCurrentProcess(system.Kernel()).GetRandomEntropy(info_sub_id); *result = GetCurrentProcess(system.Kernel()).GetRandomEntropy(info_sub_id);
R_SUCCEED(); R_SUCCEED();
case InfoType::InitialProcessIdRange: {
LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query privileged process id bounds, returned 0/64"); case InfoType::InitialProcessIdRange:
R_UNLESS(handle == InvalidHandle, ResultInvalidHandle); LOG_WARNING(Kernel_SVC,
switch (InitialProcessIdRangeInfo(info_sub_id)) { "(STUBBED) Attempted to query privileged process id bounds, returned 0");
case InitialProcessIdRangeInfo::Minimum: *result = 0;
*result = 0; //todo R_SUCCEED();
R_SUCCEED();
case InitialProcessIdRangeInfo::Maximum:
*result = 64; //todo
R_SUCCEED();
default:
R_THROW(ResultInvalidCombination);
}
}
case InfoType::ThreadTickCount: { case InfoType::ThreadTickCount: {
constexpr u64 num_cpus = 4; constexpr u64 num_cpus = 4;
if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) { if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) {
LOG_ERROR(Kernel_SVC, "Core count is out of range, expected {} but got {}", num_cpus, info_sub_id); LOG_ERROR(Kernel_SVC, "Core count is out of range, expected {} but got {}", num_cpus,
info_sub_id);
R_THROW(ResultInvalidCombination); R_THROW(ResultInvalidCombination);
} }
@@ -175,7 +206,8 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle
.GetHandleTable() .GetHandleTable()
.GetObject<KThread>(system.Kernel(), Handle(handle)); .GetObject<KThread>(system.Kernel(), Handle(handle));
if (thread.IsNull()) { if (thread.IsNull()) {
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle={:#08x}", Handle(handle)); LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle={:#08x}",
static_cast<Handle>(handle));
R_THROW(ResultInvalidHandle); R_THROW(ResultInvalidHandle);
} }
@@ -188,6 +220,7 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle
u64 out_ticks = 0; u64 out_ticks = 0;
if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) { if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) {
const u64 thread_ticks = current_thread->GetCpuTime(); const u64 thread_ticks = current_thread->GetCpuTime();
out_ticks = thread_ticks + (core_timing.GetClockTicks() - prev_ctx_ticks); out_ticks = thread_ticks + (core_timing.GetClockTicks() - prev_ctx_ticks);
} else if (same_thread && info_sub_id == system.Kernel().CurrentPhysicalCoreIndex()) { } else if (same_thread && info_sub_id == system.Kernel().CurrentPhysicalCoreIndex()) {
out_ticks = core_timing.GetClockTicks() - prev_ctx_ticks; out_ticks = core_timing.GetClockTicks() - prev_ctx_ticks;
@@ -197,40 +230,19 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle
R_SUCCEED(); R_SUCCEED();
} }
case InfoType::IdleTickCount: { case InfoType::IdleTickCount: {
// Verify the requested core is valid.
const bool core_valid =
(info_sub_id == 0xFFFFFFFFFFFFFFFF)
|| (info_sub_id == u64(system.Kernel().CurrentPhysicalCoreIndex()));
R_UNLESS(core_valid, ResultInvalidCombination);
// Verify the input handle is invalid. // Verify the input handle is invalid.
R_UNLESS(handle == InvalidHandle, ResultInvalidHandle); R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
// Verify the requested core is valid.
const bool core_valid =
(info_sub_id == 0xFFFFFFFFFFFFFFFF) ||
(info_sub_id == static_cast<u64>(system.Kernel().CurrentPhysicalCoreIndex()));
R_UNLESS(core_valid, ResultInvalidCombination);
// Get the idle tick count. // Get the idle tick count.
*result = system.Kernel().CurrentScheduler()->GetIdleThread()->GetCpuTime(); *result = system.Kernel().CurrentScheduler()->GetIdleThread()->GetCpuTime();
R_SUCCEED(); R_SUCCEED();
} }
case InfoType::MesosphereMeta: {
enum MesosphereMetaInfo : u64 {
KernelVersion = 0,
IsKTraceEnabled = 1,
IsSingleStepEnabled = 2,
};
R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
switch (MesosphereMetaInfo(info_sub_id)) {
case MesosphereMetaInfo::KernelVersion:
*result = Kernel::Svc::SupportedKernelVersion;
R_SUCCEED();
case MesosphereMetaInfo::IsKTraceEnabled:
*result = 0;
R_SUCCEED();
case MesosphereMetaInfo::IsSingleStepEnabled:
*result = 0;
R_SUCCEED();
default:
R_THROW(ResultInvalidCombination);
}
}
case InfoType::MesosphereCurrentProcess: { case InfoType::MesosphereCurrentProcess: {
// Verify the input handle is invalid. // Verify the input handle is invalid.
R_UNLESS(handle == InvalidHandle, ResultInvalidHandle); R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
@@ -243,11 +255,13 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle
KHandleTable& handle_table = current_process->GetHandleTable(); KHandleTable& handle_table = current_process->GetHandleTable();
// Get a new handle for the current process. // Get a new handle for the current process.
Handle tmp{}; Handle tmp;
R_TRY(handle_table.Add(system.Kernel(), std::addressof(tmp), current_process)); R_TRY(handle_table.Add(system.Kernel(), std::addressof(tmp), current_process));
// Set the output. // Set the output.
*result = tmp; *result = tmp;
// We succeeded.
R_SUCCEED(); R_SUCCEED();
} }
default: default:
+1 -2
View File
@@ -1387,8 +1387,7 @@ public:
{5, &ACC_U1::GetProfile, "GetProfile"}, {5, &ACC_U1::GetProfile, "GetProfile"},
{6, nullptr, "GetProfileDigest"}, {6, nullptr, "GetProfileDigest"},
{50, &ACC_U1::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, {50, &ACC_U1::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
{51, &ACC_U1::TrySelectUserWithoutInteractionDeprecated, "TrySelectUserWithoutInteractionDeprecated"}, {51, &ACC_U1::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
{52, &ACC_U1::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
{60, &ACC_U1::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, {60, &ACC_U1::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"},
{99, nullptr, "DebugActivateOpenContextRetention"}, {99, nullptr, "DebugActivateOpenContextRetention"},
{100, nullptr, "GetUserRegistrationNotifier"}, {100, nullptr, "GetUserRegistrationNotifier"},
-1
View File
@@ -154,7 +154,6 @@ enum class AppletMessage : u32 {
DetectLongPressingCaptureButton = 91, DetectLongPressingCaptureButton = 91,
AlbumScreenShotTaken = 92, AlbumScreenShotTaken = 92,
AlbumRecordingSaved = 93, AlbumRecordingSaved = 93,
StartupLogoDisappeared = 95,
}; };
enum class LibraryAppletMode : u32 { enum class LibraryAppletMode : u32 {
-1
View File
@@ -91,7 +91,6 @@ struct Applet {
// Common state // Common state
bool sleep_lock_enabled{}; bool sleep_lock_enabled{};
bool vr_mode_enabled{}; bool vr_mode_enabled{};
bool vr_mode_enabled_3d{};
bool lcd_backlight_off_enabled{}; bool lcd_backlight_off_enabled{};
APM::CpuBoostMode boost_mode{}; APM::CpuBoostMode boost_mode{};
bool request_exit_to_library_applet_at_execute_next_program_enabled{}; bool request_exit_to_library_applet_at_execute_next_program_enabled{};
@@ -73,16 +73,20 @@ Result DisplayLayerManager::CreateManagedDisplayLayer(u64* out_layer_id) {
R_TRY(m_manager_display_service->CreateManagedLayer( R_TRY(m_manager_display_service->CreateManagedLayer(
out_layer_id, 0, display_id, Service::AppletResourceUserId{m_process->GetProcessId()})); out_layer_id, 0, display_id, Service::AppletResourceUserId{m_process->GetProcessId()}));
m_manager_display_service->SetLayerVisibility(m_visible, *out_layer_id);
(void)m_display_service->GetContainer()->SetLayerStackMask(*out_layer_id,
this->GetLayerStackMask());
if (m_applet_id != AppletId::Application) { if (m_applet_id != AppletId::Application) {
(void)m_manager_display_service->SetLayerBlending(m_blending_enabled, *out_layer_id); (void)m_manager_display_service->SetLayerBlending(m_blending_enabled, *out_layer_id);
if (m_applet_id == AppletId::OverlayDisplay) { if (m_applet_id == AppletId::OverlayDisplay) {
(void)m_manager_display_service->SetLayerZIndex(-1, *out_layer_id); (void)m_manager_display_service->SetLayerZIndex(Overlay, *out_layer_id);
(void)m_display_service->GetContainer()->SetLayerIsOverlay(*out_layer_id, true); (void)m_display_service->GetContainer()->SetLayerIsOverlay(*out_layer_id, true);
} else { } else {
(void)m_manager_display_service->SetLayerZIndex(1, *out_layer_id); (void)m_manager_display_service->SetLayerZIndex(Foreground, *out_layer_id);
} }
} }
(void)m_display_service->GetContainer()->SetLayerZIndex(*out_layer_id, true);
m_managed_display_layers.emplace(*out_layer_id); m_managed_display_layers.emplace(*out_layer_id);
R_SUCCEED(); R_SUCCEED();
@@ -122,11 +126,12 @@ Result DisplayLayerManager::IsSystemBufferSharingEnabled() {
// Ensure the overlay layer is visible // Ensure the overlay layer is visible
m_manager_display_service->SetLayerVisibility(m_visible, m_system_shared_layer_id); m_manager_display_service->SetLayerVisibility(m_visible, m_system_shared_layer_id);
(void)m_display_service->GetContainer()->SetLayerStackMask(m_system_shared_layer_id,
this->GetLayerStackMask());
m_manager_display_service->SetLayerBlending(m_blending_enabled, m_system_shared_layer_id); m_manager_display_service->SetLayerBlending(m_blending_enabled, m_system_shared_layer_id);
s32 initial_z = 1; s32 initial_z = Foreground;
(void)m_display_service->GetContainer()->SetLayerZIndex(m_system_shared_layer_id, true);
if (m_applet_id == AppletId::OverlayDisplay) { if (m_applet_id == AppletId::OverlayDisplay) {
initial_z = -1; initial_z = Overlay;
(void)m_display_service->GetContainer()->SetLayerIsOverlay(m_system_shared_layer_id, true); (void)m_display_service->GetContainer()->SetLayerIsOverlay(m_system_shared_layer_id, true);
} }
m_manager_display_service->SetLayerZIndex(initial_z, m_system_shared_layer_id); m_manager_display_service->SetLayerZIndex(initial_z, m_system_shared_layer_id);
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
@@ -73,7 +73,8 @@ Result IAllSystemAppletProxiesService::OpenLibraryAppletProxy(
Result IAllSystemAppletProxiesService::OpenOverlayAppletProxy( Result IAllSystemAppletProxiesService::OpenOverlayAppletProxy(
Out<SharedPointer<IOverlayAppletProxy>> out_overlay_applet_proxy, ClientProcessId pid, Out<SharedPointer<IOverlayAppletProxy>> out_overlay_applet_proxy, ClientProcessId pid,
InCopyHandle<Kernel::KProcess> process_handle) { InCopyHandle<Kernel::KProcess> process_handle,
InLargeData<AppletAttribute, BufferAttr_HipcMapAlias> attribute) {
LOG_WARNING(Service_AM, "called"); LOG_WARNING(Service_AM, "called");
if (const auto applet = this->GetAppletFromProcessId(pid); applet) { if (const auto applet = this->GetAppletFromProcessId(pid); applet) {
@@ -88,7 +89,8 @@ Result IAllSystemAppletProxiesService::OpenOverlayAppletProxy(
Result IAllSystemAppletProxiesService::OpenSystemApplicationProxy( Result IAllSystemAppletProxiesService::OpenSystemApplicationProxy(
Out<SharedPointer<IApplicationProxy>> out_system_application_proxy, ClientProcessId pid, Out<SharedPointer<IApplicationProxy>> out_system_application_proxy, ClientProcessId pid,
InCopyHandle<Kernel::KProcess> process_handle) { InCopyHandle<Kernel::KProcess> process_handle,
InLargeData<AppletAttribute, BufferAttr_HipcMapAlias> attribute) {
LOG_DEBUG(Service_AM, "called"); LOG_DEBUG(Service_AM, "called");
if (const auto applet = this->GetAppletFromProcessId(pid); applet) { if (const auto applet = this->GetAppletFromProcessId(pid); applet) {
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
@@ -36,13 +36,15 @@ private:
InCopyHandle<Kernel::KProcess> process_handle, InCopyHandle<Kernel::KProcess> process_handle,
InLargeData<AppletAttribute, BufferAttr_HipcMapAlias> attribute); InLargeData<AppletAttribute, BufferAttr_HipcMapAlias> attribute);
Result OpenOverlayAppletProxy(Out<SharedPointer<IOverlayAppletProxy>> out_overlay_applet_proxy, Result OpenOverlayAppletProxy(Out<SharedPointer<IOverlayAppletProxy>> out_overlay_applet_proxy,
ClientProcessId pid, InCopyHandle<Kernel::KProcess> process_handle); ClientProcessId pid, InCopyHandle<Kernel::KProcess> process_handle,
InLargeData<AppletAttribute, BufferAttr_HipcMapAlias> attribute);
Result OpenLibraryAppletProxyOld( Result OpenLibraryAppletProxyOld(
Out<SharedPointer<ILibraryAppletProxy>> out_library_applet_proxy, ClientProcessId pid, Out<SharedPointer<ILibraryAppletProxy>> out_library_applet_proxy, ClientProcessId pid,
InCopyHandle<Kernel::KProcess> process_handle); InCopyHandle<Kernel::KProcess> process_handle);
Result OpenSystemApplicationProxy( Result OpenSystemApplicationProxy(
Out<SharedPointer<IApplicationProxy>> out_system_application_proxy, ClientProcessId pid, Out<SharedPointer<IApplicationProxy>> out_system_application_proxy, ClientProcessId pid,
InCopyHandle<Kernel::KProcess> process_handle); InCopyHandle<Kernel::KProcess> process_handle,
InLargeData<AppletAttribute, BufferAttr_HipcMapAlias> attribute);
Result GetSystemProcessCommonFunctions(); Result GetSystemProcessCommonFunctions();
Result GetAppletAlternativeFunctions(); Result GetAppletAlternativeFunctions();
@@ -39,7 +39,7 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_, std::shared_ptr<Ap
{14, nullptr, "GetWakeupCount"}, //11.0.0+ {14, nullptr, "GetWakeupCount"}, //11.0.0+
{15, nullptr, "Unknown15"}, //19.0.0+ {15, nullptr, "Unknown15"}, //19.0.0+
{20, D<&ICommonStateGetter::PushToGeneralChannel>, "PushToGeneralChannel"}, {20, D<&ICommonStateGetter::PushToGeneralChannel>, "PushToGeneralChannel"},
{30, D<&ICommonStateGetter::GetHomeButtonReaderLockAccessor>, "GetHomeButtonReaderLockAccessor"}, {30, nullptr, "GetHomeButtonReaderLockAccessor"},
{31, D<&ICommonStateGetter::GetReaderLockAccessorEx>, "GetReaderLockAccessorEx"}, //2.0.0+ {31, D<&ICommonStateGetter::GetReaderLockAccessorEx>, "GetReaderLockAccessorEx"}, //2.0.0+
{32, D<&ICommonStateGetter::GetWriterLockAccessorEx>, "GetWriterLockAccessorEx"}, //7.0.0+ {32, D<&ICommonStateGetter::GetWriterLockAccessorEx>, "GetWriterLockAccessorEx"}, //7.0.0+
{40, nullptr, "GetCradleFwVersion"}, //2.0.0+ {40, nullptr, "GetCradleFwVersion"}, //2.0.0+
@@ -65,7 +65,7 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_, std::shared_ptr<Ap
{100, D<&ICommonStateGetter::SetHandlingHomeButtonShortPressedEnabled>, "SetHandlingHomeButtonShortPressedEnabled"}, {100, D<&ICommonStateGetter::SetHandlingHomeButtonShortPressedEnabled>, "SetHandlingHomeButtonShortPressedEnabled"},
{110, nullptr, "OpenMyGpuErrorHandler"}, {110, nullptr, "OpenMyGpuErrorHandler"},
{120, D<&ICommonStateGetter::GetAppletLaunchedHistory>, "GetAppletLaunchedHistory"}, //13.0.0+ {120, D<&ICommonStateGetter::GetAppletLaunchedHistory>, "GetAppletLaunchedHistory"}, //13.0.0+
{130, D<&ICommonStateGetter::EnableStartupLogoDisappearedMessage>, "EnableStartupLogoDisappearedMessage"}, //21.0.0+ {130, nullptr, "Unknown130"}, //21.0.0+
{200, D<&ICommonStateGetter::GetOperationModeSystemInfo>, "GetOperationModeSystemInfo"}, {200, D<&ICommonStateGetter::GetOperationModeSystemInfo>, "GetOperationModeSystemInfo"},
{300, D<&ICommonStateGetter::GetSettingsPlatformRegion>, "GetSettingsPlatformRegion"}, {300, D<&ICommonStateGetter::GetSettingsPlatformRegion>, "GetSettingsPlatformRegion"},
{400, nullptr, "ActivateMigrationService"}, {400, nullptr, "ActivateMigrationService"},
@@ -74,17 +74,14 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_, std::shared_ptr<Ap
{501, nullptr, "SuppressDisablingSleepTemporarily"}, {501, nullptr, "SuppressDisablingSleepTemporarily"},
{502, nullptr, "IsSleepEnabled"}, {502, nullptr, "IsSleepEnabled"},
{503, nullptr, "IsDisablingSleepSuppressed"}, {503, nullptr, "IsDisablingSleepSuppressed"},
{600, nullptr, "SetHidInputMagnificationForApplication"}, //20.0.0+ {600, nullptr, "Unknown600"}, //20.0.0+
{610, D<&ICommonStateGetter::Unknown610>, "Unknown610"}, //21.0.0+ {610, D<&ICommonStateGetter::Unknown610>, "Unknown610"}, //21.0.0+
{611, D<&ICommonStateGetter::Unknown611>, "Unknown611"}, //22.0.0+ {611, D<&ICommonStateGetter::Unknown611>, "Unknown611"}, //22.0.0+
{900, D<&ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled>, "SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled"}, //11.0.0+ {900, D<&ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled>, "SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled"}, //11.0.0+
{910, nullptr, "GetLaunchRequiredTick"}, //17.0.0+ {910, nullptr, "GetLaunchRequiredTick"}, //17.0.0+
{1000, D<&ICommonStateGetter::BeginVrMode3d>, "BeginVrMode3d"}, //19.0.0+ {1000, nullptr, "BeginVrMode3d"}, //19.0.0+
{1001, D<&ICommonStateGetter::EndVrMode3d>, "EndVrMode3d"}, //19.0.0+ {1001, nullptr, "EndVrMode3d"}, //19.0.0+
{1002, D<&ICommonStateGetter::IsVrModeEnabled3d>, "IsVrModeEnabled3d"}, //19.0.0+ {1002, nullptr, "IsVrModeEnabled3d"}, //19.0.0+
{1003, D<&ICommonStateGetter::GetVrLaboGoggleViewport>, "GetVrLaboGoggleViewport"}, //21.0.0+
{1004, D<&ICommonStateGetter::GetPanelPhysicalSizeForSpecificTitle>, "GetPanelPhysicalSizeForSpecificTitle"}, //21.0.0+
{1005, D<&ICommonStateGetter::GetPanelResolutionForSpecificTitle>, "GetPanelResolutionForSpecificTitle"}, //21.0.0+
}; };
// clang-format on // clang-format on
@@ -314,12 +311,6 @@ Result ICommonStateGetter::PerformSystemButtonPressingIfInFocus(SystemButtonType
R_SUCCEED(); R_SUCCEED();
} }
Result ICommonStateGetter::EnableStartupLogoDisappearedMessage() {
LOG_WARNING(Service_AM, "(STUBBED) called");
m_applet->lifecycle_manager.PushUnorderedMessage(system.Kernel(), AppletMessage::StartupLogoDisappeared);
R_SUCCEED();
}
Result ICommonStateGetter::GetOperationModeSystemInfo(Out<u32> out_operation_mode_system_info) { Result ICommonStateGetter::GetOperationModeSystemInfo(Out<u32> out_operation_mode_system_info) {
LOG_WARNING(Service_AM, "(STUBBED) called"); LOG_WARNING(Service_AM, "(STUBBED) called");
*out_operation_mode_system_info = 0; *out_operation_mode_system_info = 0;
@@ -364,12 +355,6 @@ Result ICommonStateGetter::PushToGeneralChannel(SharedPointer<IStorage> storage)
R_SUCCEED(); R_SUCCEED();
} }
Result ICommonStateGetter::GetHomeButtonReaderLockAccessor(Out<SharedPointer<ILockAccessor>> out_lock_accessor) {
LOG_DEBUG(Service_AM, "called");
*out_lock_accessor = std::make_shared<ILockAccessor>(system);
R_SUCCEED();
}
Result ICommonStateGetter::SetHandlingHomeButtonShortPressedEnabled(bool enabled) { Result ICommonStateGetter::SetHandlingHomeButtonShortPressedEnabled(bool enabled) {
LOG_DEBUG(Service_AM, "called, enabled={} applet_id={}", enabled, m_applet->applet_id); LOG_DEBUG(Service_AM, "called, enabled={} applet_id={}", enabled, m_applet->applet_id);
@@ -378,58 +363,14 @@ Result ICommonStateGetter::SetHandlingHomeButtonShortPressedEnabled(bool enabled
R_SUCCEED(); R_SUCCEED();
} }
Result ICommonStateGetter::Unknown610(u64 unk) { Result ICommonStateGetter::Unknown610() {
LOG_WARNING(Service_AM, "(STUBBED) called"); LOG_WARNING(Service_AM, "(STUBBED) called");
R_SUCCEED(); R_SUCCEED();
} }
Result ICommonStateGetter::Unknown611(u8 unk) { Result ICommonStateGetter::Unknown611() {
LOG_WARNING(Service_AM, "(STUBBED) called"); LOG_WARNING(Service_AM, "(STUBBED) called");
R_SUCCEED(); R_SUCCEED();
} }
Result ICommonStateGetter::BeginVrMode3d() {
std::scoped_lock lk{m_applet->lock};
m_applet->vr_mode_enabled_3d = true;
LOG_WARNING(Service_AM, "VR Mode is {}", m_applet->vr_mode_enabled_3d ? "on" : "off");
R_SUCCEED();
}
Result ICommonStateGetter::EndVrMode3d() {
std::scoped_lock lk{m_applet->lock};
m_applet->vr_mode_enabled_3d = false;
LOG_WARNING(Service_AM, "VR Mode is {}", m_applet->vr_mode_enabled_3d ? "on" : "off");
R_SUCCEED();
}
Result ICommonStateGetter::IsVrModeEnabled3d(Out<bool> out_is_vr_mode_enabled_3d) {
LOG_WARNING(Service_AM, "(STUBBED) called");
std::scoped_lock lk{m_applet->lock};
*out_is_vr_mode_enabled_3d = m_applet->vr_mode_enabled_3d;
R_SUCCEED();
}
Result ICommonStateGetter::GetVrLaboGoggleViewport(Out<s32> out_x, Out<s32> out_y, Out<s32> out_width, Out<s32> out_height) {
LOG_WARNING(Service_AM, "(STUBBED) called");
*out_x = 0;
*out_y = 0;
*out_width = 1280;
*out_height = 720;
R_SUCCEED();
}
Result ICommonStateGetter::GetPanelPhysicalSizeForSpecificTitle(Out<f32> out_width, Out<f32> out_height) {
LOG_WARNING(Service_AM, "(STUBBED) called");
*out_width = 137250.0f / 1000.0f;
*out_height = 77200.0f / 1000.0f;
R_SUCCEED();
}
Result ICommonStateGetter::GetPanelResolutionForSpecificTitle(Out<s32> out_width, Out<s32> out_height) {
LOG_WARNING(Service_AM, "(STUBBED) called");
*out_width = 1280;
*out_height = 720;
R_SUCCEED();
}
} // namespace Service::AM } // namespace Service::AM
@@ -56,23 +56,15 @@ private:
Result GetDefaultDisplayResolution(Out<s32> out_width, Out<s32> out_height); Result GetDefaultDisplayResolution(Out<s32> out_width, Out<s32> out_height);
Result GetBuiltInDisplayType(Out<s32> out_display_type); Result GetBuiltInDisplayType(Out<s32> out_display_type);
Result PerformSystemButtonPressingIfInFocus(SystemButtonType type); Result PerformSystemButtonPressingIfInFocus(SystemButtonType type);
Result EnableStartupLogoDisappearedMessage();
Result GetOperationModeSystemInfo(Out<u32> out_operation_mode_system_info); Result GetOperationModeSystemInfo(Out<u32> out_operation_mode_system_info);
Result GetAppletLaunchedHistory(Out<s32> out_count, Result GetAppletLaunchedHistory(Out<s32> out_count,
OutArray<AppletId, BufferAttr_HipcMapAlias> out_applet_ids); OutArray<AppletId, BufferAttr_HipcMapAlias> out_applet_ids);
Result GetSettingsPlatformRegion(Out<Set::PlatformRegion> out_settings_platform_region); Result GetSettingsPlatformRegion(Out<Set::PlatformRegion> out_settings_platform_region);
Result SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled(); Result SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled();
Result PushToGeneralChannel(SharedPointer<IStorage> storage); // cmd 20 Result PushToGeneralChannel(SharedPointer<IStorage> storage); // cmd 20
Result GetHomeButtonReaderLockAccessor(Out<SharedPointer<ILockAccessor>> out_lock_accessor);
Result SetHandlingHomeButtonShortPressedEnabled(bool enabled); Result SetHandlingHomeButtonShortPressedEnabled(bool enabled);
Result Unknown610(u64 unk); Result Unknown610();
Result Unknown611(u8 unk); Result Unknown611();
Result BeginVrMode3d();
Result EndVrMode3d();
Result IsVrModeEnabled3d(Out<bool> out_is_vr_mode_enabled_3d);
Result GetVrLaboGoggleViewport(Out<s32> out_x, Out<s32> out_y, Out<s32> out_width, Out<s32> out_height);
Result GetPanelPhysicalSizeForSpecificTitle(Out<f32> out_width, Out<f32> out_height);
Result GetPanelResolutionForSpecificTitle(Out<s32> out_width, Out<s32> out_height);
void SetCpuBoostMode(HLERequestContext& ctx); void SetCpuBoostMode(HLERequestContext& ctx);
+5 -5
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -29,14 +26,17 @@ IStorage::~IStorage() = default;
Result IStorage::Open(Out<SharedPointer<IStorageAccessor>> out_storage_accessor) { Result IStorage::Open(Out<SharedPointer<IStorageAccessor>> out_storage_accessor) {
LOG_DEBUG(Service_AM, "called"); LOG_DEBUG(Service_AM, "called");
R_UNLESS(m_impl->GetHandle() == nullptr, AM::ResultInvalidStorageType); R_UNLESS(m_impl->GetHandle() == nullptr, AM::ResultInvalidStorageType);
*out_storage_accessor = std::make_shared<IStorageAccessor>(system, m_impl); *out_storage_accessor = std::make_shared<IStorageAccessor>(system, m_impl);
R_SUCCEED(); R_SUCCEED();
} }
Result IStorage::OpenTransferStorage(Out<SharedPointer<ITransferStorageAccessor>> out_transfer_storage_accessor) { Result IStorage::OpenTransferStorage(
LOG_DEBUG(Service_AM, "called"); Out<SharedPointer<ITransferStorageAccessor>> out_transfer_storage_accessor) {
R_UNLESS(m_impl->GetHandle() != nullptr, AM::ResultInvalidStorageType); R_UNLESS(m_impl->GetHandle() != nullptr, AM::ResultInvalidStorageType);
*out_transfer_storage_accessor = std::make_shared<ITransferStorageAccessor>(system, m_impl); *out_transfer_storage_accessor = std::make_shared<ITransferStorageAccessor>(system, m_impl);
R_SUCCEED(); R_SUCCEED();
} }
+1 -1
View File
@@ -50,7 +50,7 @@ IAudioIn::IAudioIn(Core::System& system_, Manager& manager, size_t session_id,
} }
IAudioIn::~IAudioIn() { IAudioIn::~IAudioIn() {
impl->Free(system); impl->Free();
service_context.CloseEvent(event); service_context.CloseEvent(event);
process->Close(system.Kernel()); process->Close(system.Kernel());
} }
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -68,7 +65,7 @@ Result IAudioInManager::OpenAudioInAuto(
Result IAudioInManager::ListAudioInsAutoFiltered( Result IAudioInManager::ListAudioInsAutoFiltered(
OutArray<AudioDeviceName, BufferAttr_HipcAutoSelect> out_audio_ins, Out<u32> out_count) { OutArray<AudioDeviceName, BufferAttr_HipcAutoSelect> out_audio_ins, Out<u32> out_count) {
LOG_DEBUG(Service_Audio, "called"); LOG_DEBUG(Service_Audio, "called");
*out_count = impl->GetDeviceNames(system, out_audio_ins, true); *out_count = impl->GetDeviceNames(out_audio_ins, true);
R_SUCCEED(); R_SUCCEED();
} }
@@ -93,8 +90,8 @@ Result IAudioInManager::OpenAudioInProtocolSpecified(
size_t new_session_id{}; size_t new_session_id{};
R_TRY(impl->LinkToManager(system)); R_TRY(impl->LinkToManager());
R_TRY(impl->AcquireSessionId(system, new_session_id)); R_TRY(impl->AcquireSessionId(new_session_id));
LOG_DEBUG(Service_Audio, "Opening new AudioIn, session_id={}, free sessions={}", new_session_id, LOG_DEBUG(Service_Audio, "Opening new AudioIn, session_id={}, free sessions={}", new_session_id,
impl->num_free_sessions); impl->num_free_sessions);
+1 -1
View File
@@ -46,7 +46,7 @@ IAudioOut::IAudioOut(Core::System& system_, Manager& manager, size_t session_id,
} }
IAudioOut::~IAudioOut() { IAudioOut::~IAudioOut() {
impl->Free(system); impl->Free();
service_context.CloseEvent(event); service_context.CloseEvent(event);
process->Close(system.Kernel()); process->Close(system.Kernel());
} }
@@ -77,8 +77,8 @@ Result IAudioOutManager::OpenAudioOutAuto(
} }
size_t new_session_id{}; size_t new_session_id{};
R_TRY(impl->LinkToManager(system)); R_TRY(impl->LinkToManager());
R_TRY(impl->AcquireSessionId(system, new_session_id)); R_TRY(impl->AcquireSessionId(new_session_id));
const auto device_name = Common::StringFromBuffer(name[0].name); const auto device_name = Common::StringFromBuffer(name[0].name);
LOG_DEBUG(Service_Audio, "Opening new AudioOut, sessionid={}, free sessions={}", new_session_id, LOG_DEBUG(Service_Audio, "Opening new AudioOut, sessionid={}, free sessions={}", new_session_id,
@@ -798,7 +798,7 @@ void LoopProcess(Core::System& system) {
const auto FileSystemProxyFactory = [&] { return std::make_shared<FSP_SRV>(system); }; const auto FileSystemProxyFactory = [&] { return std::make_shared<FSP_SRV>(system); };
server_manager->RegisterNamedService("fsp-ldr", std::make_shared<FSP_LDR>(system)); server_manager->RegisterNamedService("fsp-ldr", std::make_shared<FSP_LDR>(system));
server_manager->RegisterNamedService("fsp-pr", std::make_shared<FSP_PR>(system)); server_manager->RegisterNamedService("fsp:pr", std::make_shared<FSP_PR>(system));
server_manager->RegisterNamedService("fsp-srv", std::move(FileSystemProxyFactory)); server_manager->RegisterNamedService("fsp-srv", std::move(FileSystemProxyFactory));
ServerManager::RunServer(std::move(server_manager)); ServerManager::RunServer(std::move(server_manager));
} }
+7 -25
View File
@@ -32,6 +32,7 @@ public:
RegisterHandlers(functions); RegisterHandlers(functions);
} }
~I2CSession() override = default; ~I2CSession() override = default;
Result Send(InBuffer<BufferAttr_HipcMapAlias> in_data, u32 transaction_option) { Result Send(InBuffer<BufferAttr_HipcMapAlias> in_data, u32 transaction_option) {
LOG_WARNING(Service, "(stubbed) topt={}", transaction_option); LOG_WARNING(Service, "(stubbed) topt={}", transaction_option);
R_THROW(ResultUnknown); R_THROW(ResultUnknown);
@@ -49,40 +50,21 @@ public:
: ServiceFramework{system_, "i2c"} : ServiceFramework{system_, "i2c"}
{ {
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{0, C<&I2C::OpenSessionForDev>, "OpenSessionForDev"}, {0, nullptr, "OpenSessionForDev"},
{1, C<&I2C::OpenSession>, "OpenSession"}, {1, C<&I2C::OpenSession>, "OpenSession"},
{2, C<&I2C::HasDevice>, "HasDevice"}, {2, nullptr, "HasDevice"},
{3, C<&I2C::HasDeviceForDev>, "HasDeviceForDev"}, {3, nullptr, "HasDeviceForDev"},
{4, C<&I2C::OpenSession2>, "OpenSession2"}, {4, nullptr, "OpenSession2"},
}; };
RegisterHandlers(functions); RegisterHandlers(functions);
} }
~I2C() override = default; ~I2C() override = default;
Result OpenSessionForDev(OutInterface<I2CSession> out_session, s32 bus_idx, u32 slave_address, u32 addressing_mode, u32 speed_mode) {
Result OpenSession(I2CDevice device, OutInterface<I2CSession> out_session) {
LOG_DEBUG(Service, "(stubbed)"); LOG_DEBUG(Service, "(stubbed)");
*out_session = std::make_shared<I2CSession>(system); *out_session = std::make_shared<I2CSession>(system);
R_SUCCEED(); R_SUCCEED();
} }
Result OpenSession(OutInterface<I2CSession> out_session, I2CDevice device) {
LOG_DEBUG(Service, "(stubbed)");
*out_session = std::make_shared<I2CSession>(system);
R_SUCCEED();
}
Result HasDevice(Out<bool> out_has_device, I2CDevice device) {
LOG_DEBUG(Service, "(stubbed)");
*out_has_device = false;
R_SUCCEED();
}
Result HasDeviceForDev(Out<bool> out_has_device, I2CDevice device) {
LOG_DEBUG(Service, "(stubbed)");
*out_has_device = false;
R_SUCCEED();
}
Result OpenSession2(OutInterface<I2CSession> out_session, u32 device_code) {
LOG_DEBUG(Service, "(stubbed) device_code={}", device_code);
*out_session = std::make_shared<I2CSession>(system);
R_SUCCEED();
}
}; };
void LoopProcess(Core::System& system) { void LoopProcess(Core::System& system) {
@@ -0,0 +1,27 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/ldn/ldn_results.h"
#include "core/hle/service/ldn/client_process_monitor.h"
#include "core/hle/service/ipc_helpers.h"
namespace Service::LDN {
IClientProcessMonitor::IClientProcessMonitor(Core::System& system_)
: ServiceFramework{system_, "IClientProcessMonitor"} {
static const FunctionInfo functions[] = {
{0, D<&IClientProcessMonitor::RegisterClient>, "RegisterClient"},
};
RegisterHandlers(functions);
}
IClientProcessMonitor::~IClientProcessMonitor() = default;
Result IClientProcessMonitor::RegisterClient() {
LOG_WARNING(Service_LDN, "(STUBBED) called");
R_SUCCEED();
}
} // namespace Service::LDN
@@ -0,0 +1,25 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Service::LDN {
class IClientProcessMonitor final
: public ServiceFramework<IClientProcessMonitor> {
public:
explicit IClientProcessMonitor(Core::System& system_);
~IClientProcessMonitor() override;
private:
Result RegisterClient();
};
} // namespace Service::LDN
+3 -21
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
@@ -7,6 +7,7 @@
#include "core/core.h" #include "core/core.h"
#include "core/hle/service/cmif_serialization.h" #include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/ldn/ldn.h" #include "core/hle/service/ldn/ldn.h"
#include "core/hle/service/ldn/client_process_monitor.h"
#include "core/hle/service/ldn/monitor_service.h" #include "core/hle/service/ldn/monitor_service.h"
#include "core/hle/service/ldn/sf_monitor_service.h" #include "core/hle/service/ldn/sf_monitor_service.h"
#include "core/hle/service/ldn/sf_service.h" #include "core/hle/service/ldn/sf_service.h"
@@ -16,26 +17,6 @@
namespace Service::LDN { namespace Service::LDN {
class IClientProcessMonitor final
: public ServiceFramework<IClientProcessMonitor> {
public:
explicit IClientProcessMonitor(Core::System& system_)
: ServiceFramework{system_, "IClientProcessMonitor"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, D<&IClientProcessMonitor::RegisterClient>, "RegisterClient"},
};
// clang-format on
RegisterHandlers(functions);
}
~IClientProcessMonitor() override = default;
private:
Result RegisterClient(ClientProcessId pid) {
LOG_WARNING(Service_LDN, "(STUBBED) called");
R_SUCCEED();
}
};
class IMonitorServiceCreator final : public ServiceFramework<IMonitorServiceCreator> { class IMonitorServiceCreator final : public ServiceFramework<IMonitorServiceCreator> {
public: public:
explicit IMonitorServiceCreator(Core::System& system_) : ServiceFramework{system_, "ldn:m"} { explicit IMonitorServiceCreator(Core::System& system_) : ServiceFramework{system_, "ldn:m"} {
@@ -44,6 +25,7 @@ public:
{0, C<&IMonitorServiceCreator::CreateMonitorService>, "CreateMonitorService"} {0, C<&IMonitorServiceCreator::CreateMonitorService>, "CreateMonitorService"}
}; };
// clang-format on // clang-format on
RegisterHandlers(functions); RegisterHandlers(functions);
} }
@@ -139,7 +139,7 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
{405, nullptr, "ListApplicationControlCacheEntryInfo"}, {405, nullptr, "ListApplicationControlCacheEntryInfo"},
{406, nullptr, "GetApplicationControlProperty"}, {406, nullptr, "GetApplicationControlProperty"},
{407, &IApplicationManagerInterface::ListApplicationTitle, "ListApplicationTitle"}, {407, &IApplicationManagerInterface::ListApplicationTitle, "ListApplicationTitle"},
{408, &IApplicationManagerInterface::ListApplicationIcon, "ListApplicationIcon"}, {408, nullptr, "ListApplicationIcon"},
{411, nullptr, "Unknown411"}, //19.0.0+ {411, nullptr, "Unknown411"}, //19.0.0+
{412, nullptr, "Unknown412"}, //19.0.0+ {412, nullptr, "Unknown412"}, //19.0.0+
{413, nullptr, "Unknown413"}, //19.0.0+ {413, nullptr, "Unknown413"}, //19.0.0+
@@ -894,9 +894,4 @@ void IApplicationManagerInterface::ListApplicationTitle(HLERequestContext& ctx)
IReadOnlyApplicationControlDataInterface(system).ListApplicationTitle(ctx); IReadOnlyApplicationControlDataInterface(system).ListApplicationTitle(ctx);
} }
void IApplicationManagerInterface::ListApplicationIcon(HLERequestContext& ctx) {
LOG_DEBUG(Service_NS, "called");
IReadOnlyApplicationControlDataInterface(system).ListApplicationIcon(ctx);
}
} // namespace Service::NS } // namespace Service::NS
@@ -78,7 +78,6 @@ public:
void PushApplicationRecord(HLERequestContext& ctx); void PushApplicationRecord(HLERequestContext& ctx);
void ListApplicationTitle(HLERequestContext& ctx); void ListApplicationTitle(HLERequestContext& ctx);
void ListApplicationIcon(HLERequestContext& ctx);
private: private:
KernelHelpers::ServiceContext service_context; KernelHelpers::ServiceContext service_context;
+1 -40
View File
@@ -10,9 +10,7 @@
#include "core/hle/service/ns/query_service.h" #include "core/hle/service/ns/query_service.h"
#include "core/hle/service/ns/service_getter_interface.h" #include "core/hle/service/ns/service_getter_interface.h"
#include "core/hle/service/ns/system_update_interface.h" #include "core/hle/service/ns/system_update_interface.h"
#include "core/hle/service/ipc_helpers.h" #include "core/hle/service/ns/vulnerability_manager_interface.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/server_manager.h" #include "core/hle/service/server_manager.h"
namespace Service::NS { namespace Service::NS {
@@ -41,43 +39,6 @@ public:
} }
}; };
class IVulnerabilityManagerInterface final
: public ServiceFramework<IVulnerabilityManagerInterface> {
public:
explicit IVulnerabilityManagerInterface(Core::System& system_)
: ServiceFramework{system_, "ns:vm"} {
// clang-format off
static const FunctionInfo functions[] = {
{1200, D<&IVulnerabilityManagerInterface::NeedsUpdateVulnerability>, "NeedsUpdateVulnerability"},
{1201, nullptr, "UpdateSafeSystemVersionForDebug"},
{1202, nullptr, "GetSafeSystemVersion"},
{3100, D<&IVulnerabilityManagerInterface::GetSafeSystemVersionCheckInfo>, "GetSafeSystemVersionCheckInfo"},
{3101, nullptr, "RequestUpdateSafeSystemVersionCheckInfo"},
{3102, D<&IVulnerabilityManagerInterface::ResetSafeSystemVersionCheckInfo>, "ResetSafeSystemVersionCheckInfo"},
};
// clang-format on
RegisterHandlers(functions);
}
~IVulnerabilityManagerInterface() override = default;
Result NeedsUpdateVulnerability(Out<bool> out_needs_update_vulnerability) {
LOG_WARNING(Service_NS, "(STUBBED)");
*out_needs_update_vulnerability = false;
R_SUCCEED();
}
Result GetSafeSystemVersionCheckInfo(Out<std::array<u8, 0x20>> out_system_version_check_info) {
LOG_WARNING(Service_NS, "(STUBBED)");
*out_system_version_check_info = {};
R_SUCCEED();
}
Result ResetSafeSystemVersionCheckInfo() {
LOG_WARNING(Service_NS, "(STUBBED)");
R_SUCCEED();
}
};
void LoopProcess(Core::System& system) { void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system); auto server_manager = std::make_unique<ServerManager>(system);
@@ -10,17 +10,13 @@
#include <string> #include <string>
#include "common/stb.h" #include "common/stb.h"
#include "common/logging.h"
#include "common/settings.h" #include "common/settings.h"
#include "core/file_sys/control_metadata.h" #include "core/file_sys/control_metadata.h"
#include "core/file_sys/patch_manager.h" #include "core/file_sys/patch_manager.h"
#include "core/file_sys/vfs/vfs.h" #include "core/file_sys/vfs/vfs.h"
#include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_transfer_memory.h" #include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/result.h"
#include "core/hle/service/cmif_serialization.h" #include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/hle_ipc.h"
#include "core/hle/service/ns/language.h" #include "core/hle/service/ns/language.h"
#include "core/hle/service/ns/ns_types.h" #include "core/hle/service/ns/ns_types.h"
#include "core/hle/service/ns/ns_results.h" #include "core/hle/service/ns/ns_results.h"
@@ -76,26 +72,24 @@ void SanitizeJPEGImageSize(std::vector<u8>& image) {
// IAsyncValue implementation for ListApplicationTitle // IAsyncValue implementation for ListApplicationTitle
// https://switchbrew.org/wiki/NS_services#ListApplicationTitle // https://switchbrew.org/wiki/NS_services#ListApplicationTitle
class IAsyncValue final : public ServiceFramework<IAsyncValue> { class IAsyncValueForListApplicationTitle final : public ServiceFramework<IAsyncValueForListApplicationTitle> {
public: public:
explicit IAsyncValue(Core::System& system_, s32 offset, s32 size) explicit IAsyncValueForListApplicationTitle(Core::System& system_, s32 offset, s32 size)
: ServiceFramework{system_, "IAsyncValue"} : ServiceFramework{system_, "IAsyncValue"}, service_context{system_, "IAsyncValue"},
, service_context{system_, "IAsyncValue"} data_offset{offset}, data_size{size} {
, data_offset{offset}
, data_size{size}
{
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{0, D<&IAsyncValue::GetSize>, "GetSize"}, {0, &IAsyncValueForListApplicationTitle::GetSize, "GetSize"},
{1, D<&IAsyncValue::Get>, "Get"}, {1, &IAsyncValueForListApplicationTitle::Get, "Get"},
{2, D<&IAsyncValue::Cancel>, "Cancel"}, {2, &IAsyncValueForListApplicationTitle::Cancel, "Cancel"},
{3, D<&IAsyncValue::GetErrorContext>, "GetErrorContext"}, {3, &IAsyncValueForListApplicationTitle::GetErrorContext, "GetErrorContext"},
}; };
RegisterHandlers(functions); RegisterHandlers(functions);
completion_event = service_context.CreateEvent("IAsyncValue:Completion"); completion_event = service_context.CreateEvent("IAsyncValue:Completion");
completion_event->GetReadableEvent().Signal(system.Kernel()); completion_event->GetReadableEvent().Signal(system.Kernel());
} }
~IAsyncValue() override { ~IAsyncValueForListApplicationTitle() override {
service_context.CloseEvent(completion_event); service_context.CloseEvent(completion_event);
} }
@@ -104,24 +98,35 @@ public:
} }
private: private:
Result GetSize(Out<s64> out_data_size) { void GetSize(HLERequestContext& ctx) {
LOG_DEBUG(Service_NS, "called"); LOG_DEBUG(Service_NS, "called");
*out_data_size = data_size; IPC::ResponseBuilder rb{ctx, 4};
R_SUCCEED(); rb.Push(ResultSuccess);
rb.Push<s64>(data_size);
} }
Result Get(OutBuffer<BufferAttr_HipcMapAlias> out_data_offset) {
void Get(HLERequestContext& ctx) {
LOG_DEBUG(Service_NS, "called"); LOG_DEBUG(Service_NS, "called");
std::memcpy(out_data_offset.data(), &data_offset, sizeof(s32)); std::vector<u8> buffer(sizeof(s32));
R_SUCCEED(); std::memcpy(buffer.data(), &data_offset, sizeof(s32));
ctx.WriteBuffer(buffer);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
} }
Result Cancel() {
void Cancel(HLERequestContext& ctx) {
LOG_DEBUG(Service_NS, "called"); LOG_DEBUG(Service_NS, "called");
R_SUCCEED(); IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
} }
Result GetErrorContext() {
void GetErrorContext(HLERequestContext& ctx) {
LOG_DEBUG(Service_NS, "called"); LOG_DEBUG(Service_NS, "called");
R_SUCCEED(); IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
} }
KernelHelpers::ServiceContext service_context; KernelHelpers::ServiceContext service_context;
Kernel::KEvent* completion_event{}; Kernel::KEvent* completion_event{};
s32 data_offset; s32 data_offset;
@@ -139,7 +144,6 @@ IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterfa
{3, nullptr, "ConvertLanguageCodeToApplicationLanguage"}, {3, nullptr, "ConvertLanguageCodeToApplicationLanguage"},
{4, nullptr, "SelectApplicationDesiredLanguage"}, {4, nullptr, "SelectApplicationDesiredLanguage"},
{5, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlData2>, "GetApplicationControlData"}, {5, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlData2>, "GetApplicationControlData"},
{10, &IReadOnlyApplicationControlDataInterface::ListApplicationIcon, "ListApplicationIcon"},
{13, &IReadOnlyApplicationControlDataInterface::ListApplicationTitle, "ListApplicationTitle"}, {13, &IReadOnlyApplicationControlDataInterface::ListApplicationTitle, "ListApplicationTitle"},
{19, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlData3>, "GetApplicationControlData"}, {19, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlData3>, "GetApplicationControlData"},
}; };
@@ -156,7 +160,8 @@ Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData(
LOG_INFO(Service_NS, "called with control_source={}, application_id={:016X}", LOG_INFO(Service_NS, "called with control_source={}, application_id={:016X}",
application_control_source, application_id); application_control_source, application_id);
const FileSys::PatchManager pm{application_id, system.GetFileSystemController(), system.GetContentProvider()}; const FileSys::PatchManager pm{application_id, system.GetFileSystemController(),
system.GetContentProvider()};
const auto control = pm.GetControlMetadata(); const auto control = pm.GetControlMetadata();
const auto size = out_buffer.size(); const auto size = out_buffer.size();
@@ -164,7 +169,8 @@ Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData(
const auto total_size = sizeof(FileSys::RawNACP) + icon_size; const auto total_size = sizeof(FileSys::RawNACP) + icon_size;
if (size < total_size) { if (size < total_size) {
LOG_ERROR(Service_NS, "output buffer is too small! (actual={:016X}, expected_min=0x4000)", size); LOG_ERROR(Service_NS, "output buffer is too small! (actual={:016X}, expected_min=0x4000)",
size);
R_THROW(ResultUnknown); R_THROW(ResultUnknown);
} }
@@ -172,7 +178,8 @@ Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData(
const auto bytes = control.first->GetRawBytes(); const auto bytes = control.first->GetRawBytes();
std::memcpy(out_buffer.data(), bytes.data(), bytes.size()); std::memcpy(out_buffer.data(), bytes.data(), bytes.size());
} else { } else {
LOG_WARNING(Service_NS, "missing NACP data for application_id={:016X}, defaulting to zero", application_id); LOG_WARNING(Service_NS, "missing NACP data for application_id={:016X}, defaulting to zero",
application_id);
std::memset(out_buffer.data(), 0, sizeof(FileSys::RawNACP)); std::memset(out_buffer.data(), 0, sizeof(FileSys::RawNACP));
} }
@@ -197,12 +204,15 @@ Result IReadOnlyApplicationControlDataInterface::GetApplicationDesiredLanguage(
// Convert to application language, get priority list // Convert to application language, get priority list
const auto application_language = ConvertToApplicationLanguage(language_code); const auto application_language = ConvertToApplicationLanguage(language_code);
if (application_language == std::nullopt) { if (application_language == std::nullopt) {
LOG_ERROR(Service_NS, "Could not convert application language! language_code={}", language_code); LOG_ERROR(Service_NS, "Could not convert application language! language_code={}",
language_code);
R_THROW(Service::NS::ResultApplicationLanguageNotFound); R_THROW(Service::NS::ResultApplicationLanguageNotFound);
} }
const auto priority_list = GetApplicationLanguagePriorityList(*application_language); const auto priority_list = GetApplicationLanguagePriorityList(*application_language);
if (!priority_list) { if (!priority_list) {
LOG_ERROR(Service_NS, "Could not find application language priorities! application_language={}", *application_language); LOG_ERROR(Service_NS,
"Could not find application language priorities! application_language={}",
*application_language);
R_THROW(Service::NS::ResultApplicationLanguageNotFound); R_THROW(Service::NS::ResultApplicationLanguageNotFound);
} }
@@ -246,7 +256,8 @@ Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData2(
const auto nacp_size = sizeof(FileSys::RawNACP); const auto nacp_size = sizeof(FileSys::RawNACP);
if (size < nacp_size) { if (size < nacp_size) {
LOG_ERROR(Service_NS, "output buffer is too small! (actual={:016X}, expected_min={:08X})", size, nacp_size); LOG_ERROR(Service_NS, "output buffer is too small! (actual={:016X}, expected_min={:08X})",
size, nacp_size);
R_THROW(ResultUnknown); R_THROW(ResultUnknown);
} }
@@ -297,81 +308,63 @@ Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData2(
R_SUCCEED(); R_SUCCEED();
} }
void IReadOnlyApplicationControlDataInterface::ListApplicationIcon(HLERequestContext& ctx) {
LOG_WARNING(Service_NS, "(stubbed)"); void IReadOnlyApplicationControlDataInterface::ListApplicationTitle(HLERequestContext& ctx) {
/*
IPC::RequestParser rp{ctx};
auto control_source = rp.PopRaw<u8>();
rp.Skip(7, false);
auto transfer_memory_size = rp.Pop<u64>();
*/
const auto app_ids_buffer = ctx.ReadBuffer(); const auto app_ids_buffer = ctx.ReadBuffer();
const u64 app_count = app_ids_buffer.size() / sizeof(u64); const size_t app_count = app_ids_buffer.size() / sizeof(u64);
std::vector<u64> application_ids(app_count);
if (app_count > 0) {
std::memcpy(application_ids.data(), app_ids_buffer.data(), app_count * sizeof(u64));
}
auto t_mem_obj = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(ctx.GetCopyHandle(0)); auto t_mem_obj = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(ctx.GetCopyHandle(0));
auto* t_mem = t_mem_obj.GetPointerUnsafe(); auto* t_mem = t_mem_obj.GetPointerUnsafe();
size_t out_length = 0;
constexpr size_t title_entry_size = sizeof(FileSys::LanguageEntry);
const size_t total_data_size = app_count * title_entry_size;
constexpr s32 data_offset = 0;
if (t_mem != nullptr && t_mem->GetOwner() != nullptr && app_count > 0) { if (t_mem != nullptr && t_mem->GetOwner() != nullptr && app_count > 0) {
auto& memory = t_mem->GetOwner()->GetMemory(); auto& memory = t_mem->GetOwner()->GetMemory();
const auto t_mem_address = t_mem->GetSourceAddress(); const auto t_mem_address = t_mem->GetSourceAddress();
// u64 - app count
memory.WriteBlock(t_mem_address + out_length, &app_count, sizeof(u64));
out_length += sizeof(u64);
// [list of u64] - size of icons
for (size_t i = 0; i < app_count; ++i) {
const u64 app_id = app_ids_buffer[i];
const FileSys::PatchManager pm{app_id, system.GetFileSystemController(), system.GetContentProvider()};
const auto control = pm.GetControlMetadata();
u64 full_size = control.second->GetSize();
memory.WriteBlock(t_mem_address + out_length, &full_size, sizeof(u64));
out_length += sizeof(u64);
}
// [list of raw icon data]
std::vector<u8> full_icon_data;
for (size_t i = 0; i < app_count; ++i) {
const u64 app_id = app_ids_buffer[i];
const FileSys::PatchManager pm{app_id, system.GetFileSystemController(), system.GetContentProvider()};
const auto control = pm.GetControlMetadata();
auto const full_size = control.second->GetSize();
if (full_size > 0) {
full_icon_data.resize(full_size);
control.second->Read(full_icon_data.data(), full_size, 0);
memory.WriteBlock(t_mem_address + out_length, full_icon_data.data(), full_size);
out_length += full_size;
}
}
}
auto async_value = std::make_shared<IAsyncValue>(system, 0, s32(out_length));
IPC::ResponseBuilder rb{ctx, 2, 1, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(ctx, async_value->ReadableEvent());
rb.PushIpcInterface(ctx, std::move(async_value));
}
void IReadOnlyApplicationControlDataInterface::ListApplicationTitle(HLERequestContext& ctx) {
const auto app_ids_buffer = ctx.ReadBuffer();
const size_t app_count = app_ids_buffer.size() / sizeof(u64);
auto t_mem_obj = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(ctx.GetCopyHandle(0));
auto* t_mem = t_mem_obj.GetPointerUnsafe();
constexpr size_t title_entry_size = sizeof(FileSys::LanguageEntry);
const size_t total_data_size = app_count * title_entry_size;
constexpr s32 data_offset = 0;
if (t_mem != nullptr && app_count > 0) {
auto& memory = system.ApplicationMemory();
const auto t_mem_address = t_mem->GetSourceAddress();
for (size_t i = 0; i < app_count; ++i) { for (size_t i = 0; i < app_count; ++i) {
const u64 app_id = app_ids_buffer[i]; const u64 app_id = application_ids[i];
const FileSys::PatchManager pm{app_id, system.GetFileSystemController(), system.GetContentProvider()}; const FileSys::PatchManager pm{app_id, system.GetFileSystemController(),
system.GetContentProvider()};
const auto control = pm.GetControlMetadata(); const auto control = pm.GetControlMetadata();
FileSys::LanguageEntry entry{}; FileSys::LanguageEntry entry{};
if (control.first != nullptr) { if (control.first != nullptr) {
entry = control.first->GetLanguageEntry(); entry = control.first->GetLanguageEntry();
} }
const size_t offset = i * title_entry_size; const size_t offset = i * title_entry_size;
memory.WriteBlock(t_mem_address + offset, &entry, title_entry_size); memory.WriteBlock(t_mem_address + offset, &entry, title_entry_size);
} }
} }
auto async_value = std::make_shared<IAsyncValue>(system, data_offset, s32(total_data_size));
auto async_value = std::make_shared<IAsyncValueForListApplicationTitle>(
system, data_offset, static_cast<s32>(total_data_size));
IPC::ResponseBuilder rb{ctx, 2, 1, 1}; IPC::ResponseBuilder rb{ctx, 2, 1, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushCopyObjects(ctx, async_value->ReadableEvent()); rb.PushCopyObjects(ctx, async_value->ReadableEvent());
rb.PushIpcInterface(ctx, std::move(async_value)); rb.PushIpcInterface(ctx, std::move(async_value));
} }
Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData3(OutBuffer<BufferAttr_HipcMapAlias> out_buffer, Out<u32> out_flags_a, Out<u32> out_flags_b, Out<u32> out_actual_size, ApplicationControlSource application_control_source, u8 flag1, u8 flag2, u64 application_id) { Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData3(
OutBuffer<BufferAttr_HipcMapAlias> out_buffer, Out<u32> out_flags_a, Out<u32> out_flags_b,
Out<u32> out_actual_size, ApplicationControlSource application_control_source, u8 flag1, u8 flag2, u64 application_id) {
LOG_INFO(Service_NS, "called with control_source={}, flags=({:02X},{:02X}), application_id={:016X}", LOG_INFO(Service_NS, "called with control_source={}, flags=({:02X},{:02X}), application_id={:016X}",
application_control_source, flag1, flag2, application_id); application_control_source, flag1, flag2, application_id);
@@ -34,7 +34,6 @@ public:
u8 flag1, u8 flag1,
u8 flag2, u8 flag2,
u64 application_id); u64 application_id);
void ListApplicationIcon(HLERequestContext& ctx);
void ListApplicationTitle(HLERequestContext& ctx); void ListApplicationTitle(HLERequestContext& ctx);
Result GetApplicationControlData3( Result GetApplicationControlData3(
OutBuffer<BufferAttr_HipcMapAlias> out_buffer, OutBuffer<BufferAttr_HipcMapAlias> out_buffer,
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
@@ -16,8 +16,10 @@ IReadOnlyApplicationRecordInterface::IReadOnlyApplicationRecordInterface(Core::S
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{0, D<&IReadOnlyApplicationRecordInterface::HasApplicationRecord>, "HasApplicationRecord"}, {0, D<&IReadOnlyApplicationRecordInterface::HasApplicationRecord>, "HasApplicationRecord"},
{1, nullptr, "NotifyApplicationFailure"}, {1, nullptr, "NotifyApplicationFailure"},
{2, D<&IReadOnlyApplicationRecordInterface::IsDataCorruptedResult>, "IsDataCorruptedResult"}, {2, D<&IReadOnlyApplicationRecordInterface::IsDataCorruptedResult>,
{3, D<&IReadOnlyApplicationRecordInterface::ListApplicationRecord>, "ListApplicationRecord"}, "IsDataCorruptedResult"},
{3, D<&IReadOnlyApplicationRecordInterface::ListApplicationRecord>,
"ListApplicationRecord"},
}; };
// clang-format on // clang-format on
@@ -0,0 +1,31 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/ns/vulnerability_manager_interface.h"
namespace Service::NS {
IVulnerabilityManagerInterface::IVulnerabilityManagerInterface(Core::System& system_)
: ServiceFramework{system_, "ns:vm"} {
// clang-format off
static const FunctionInfo functions[] = {
{1200, D<&IVulnerabilityManagerInterface::NeedsUpdateVulnerability>, "NeedsUpdateVulnerability"},
{1201, nullptr, "UpdateSafeSystemVersionForDebug"},
{1202, nullptr, "GetSafeSystemVersion"},
};
// clang-format on
RegisterHandlers(functions);
}
IVulnerabilityManagerInterface::~IVulnerabilityManagerInterface() = default;
Result IVulnerabilityManagerInterface::NeedsUpdateVulnerability(
Out<bool> out_needs_update_vulnerability) {
LOG_WARNING(Service_NS, "(STUBBED) called");
*out_needs_update_vulnerability = false;
R_SUCCEED();
}
} // namespace Service::NS
@@ -0,0 +1,21 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"
namespace Service::NS {
class IVulnerabilityManagerInterface final
: public ServiceFramework<IVulnerabilityManagerInterface> {
public:
explicit IVulnerabilityManagerInterface(Core::System& system_);
~IVulnerabilityManagerInterface() override;
private:
Result NeedsUpdateVulnerability(Out<bool> out_needs_update_vulnerability);
};
} // namespace Service::NS
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -33,15 +30,15 @@ struct DeviceSettings {
INSERT_PADDING_BYTES(0x20); // Reserved INSERT_PADDING_BYTES(0x20); // Reserved
// nn::settings::system::ConsoleSixAxisSensorAccelerationBias // nn::settings::system::ConsoleSixAxisSensorAccelerationBias
Common::Vec<f32, 3> console_six_axis_sensor_acceleration_bias; Common::Vec3<f32> console_six_axis_sensor_acceleration_bias;
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityBias // nn::settings::system::ConsoleSixAxisSensorAngularVelocityBias
Common::Vec<f32, 3> console_six_axis_sensor_angular_velocity_bias; Common::Vec3<f32> console_six_axis_sensor_angular_velocity_bias;
// nn::settings::system::ConsoleSixAxisSensorAccelerationGain // nn::settings::system::ConsoleSixAxisSensorAccelerationGain
std::array<u8, 0x24> console_six_axis_sensor_acceleration_gain; std::array<u8, 0x24> console_six_axis_sensor_acceleration_gain;
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityGain // nn::settings::system::ConsoleSixAxisSensorAngularVelocityGain
std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_gain; std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_gain;
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityTimeBias // nn::settings::system::ConsoleSixAxisSensorAngularVelocityTimeBias
Common::Vec<f32, 3> console_six_axis_sensor_angular_velocity_time_bias; Common::Vec3<f32> console_six_axis_sensor_angular_velocity_time_bias;
// nn::settings::system::ConsoleSixAxisSensorAngularAcceleration // nn::settings::system::ConsoleSixAxisSensorAngularAcceleration
std::array<u8, 0x24> console_six_axis_sensor_angular_acceleration; std::array<u8, 0x24> console_six_axis_sensor_angular_acceleration;
}; };
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
@@ -153,15 +153,15 @@ struct SystemSettings {
INSERT_PADDING_BYTES(0x7FF8); // Reserved INSERT_PADDING_BYTES(0x7FF8); // Reserved
// nn::settings::system::ConsoleSixAxisSensorAccelerationBias // nn::settings::system::ConsoleSixAxisSensorAccelerationBias
Common::Vec<f32, 3> console_six_axis_sensor_acceleration_bias; Common::Vec3<f32> console_six_axis_sensor_acceleration_bias;
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityBias // nn::settings::system::ConsoleSixAxisSensorAngularVelocityBias
Common::Vec<f32, 3> console_six_axis_sensor_angular_velocity_bias; Common::Vec3<f32> console_six_axis_sensor_angular_velocity_bias;
// nn::settings::system::ConsoleSixAxisSensorAccelerationGain // nn::settings::system::ConsoleSixAxisSensorAccelerationGain
std::array<u8, 0x24> console_six_axis_sensor_acceleration_gain; std::array<u8, 0x24> console_six_axis_sensor_acceleration_gain;
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityGain // nn::settings::system::ConsoleSixAxisSensorAngularVelocityGain
std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_gain; std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_gain;
// nn::settings::system::ConsoleSixAxisSensorAngularVelocityTimeBias // nn::settings::system::ConsoleSixAxisSensorAngularVelocityTimeBias
Common::Vec<f32, 3> console_six_axis_sensor_angular_velocity_time_bias; Common::Vec3<f32> console_six_axis_sensor_angular_velocity_time_bias;
// nn::settings::system::ConsoleSixAxisSensorAngularAcceleration // nn::settings::system::ConsoleSixAxisSensorAngularAcceleration
std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_acceleration; std::array<u8, 0x24> console_six_axis_sensor_angular_velocity_acceleration;
INSERT_PADDING_BYTES(0x70); // Reserved INSERT_PADDING_BYTES(0x70); // Reserved
+18
View File
@@ -0,0 +1,18 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/spl/csrng.h"
namespace Service::SPL {
CSRNG::CSRNG(Core::System& system_, std::shared_ptr<Module> module_)
: Interface(system_, std::move(module_), "csrng") {
static const FunctionInfo functions[] = {
{0, &CSRNG::GenerateRandomBytes, "GenerateRandomBytes"},
};
RegisterHandlers(functions);
}
CSRNG::~CSRNG() = default;
} // namespace Service::SPL
+20
View File
@@ -0,0 +1,20 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/service/spl/spl_module.h"
namespace Core {
class System;
}
namespace Service::SPL {
class CSRNG final : public Module::Interface {
public:
explicit CSRNG(Core::System& system_, std::shared_ptr<Module> module_);
~CSRNG() override;
};
} // namespace Service::SPL
+1 -12
View File
@@ -13,6 +13,7 @@
#include "core/hle/api_version.h" #include "core/hle/api_version.h"
#include "core/hle/service/ipc_helpers.h" #include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/server_manager.h" #include "core/hle/service/server_manager.h"
#include "core/hle/service/spl/csrng.h"
#include "core/hle/service/spl/spl.h" #include "core/hle/service/spl/spl.h"
#include "core/hle/service/spl/spl_module.h" #include "core/hle/service/spl/spl_module.h"
@@ -202,18 +203,6 @@ Result Module::Interface::GetConfigImpl(u64* out_config, ConfigItem config_item)
} }
} }
class CSRNG final : public Module::Interface {
public:
explicit CSRNG(Core::System& system_, std::shared_ptr<Module> module_)
: Interface(system_, std::move(module_), "csrng") {
static const FunctionInfo functions[] = {
{0, &CSRNG::GenerateRandomBytes, "GenerateRandomBytes"},
};
RegisterHandlers(functions);
}
~CSRNG() override = default;
};
void LoopProcess(Core::System& system) { void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system); auto server_manager = std::make_unique<ServerManager>(system);
auto module = std::make_shared<Module>(); auto module = std::make_shared<Module>();
@@ -13,7 +13,6 @@
#include "core/hle/service/vi/manager_display_service.h" #include "core/hle/service/vi/manager_display_service.h"
#include "core/hle/service/vi/system_display_service.h" #include "core/hle/service/vi/system_display_service.h"
#include "core/hle/service/vi/vi_results.h" #include "core/hle/service/vi/vi_results.h"
#include "service_creator.h"
namespace Service::VI { namespace Service::VI {
@@ -39,7 +38,6 @@ IApplicationDisplayService::IApplicationDisplayService(Core::System& system_,
{2031, C<&IApplicationDisplayService::DestroyStrayLayer>, "DestroyStrayLayer"}, {2031, C<&IApplicationDisplayService::DestroyStrayLayer>, "DestroyStrayLayer"},
{2101, C<&IApplicationDisplayService::SetLayerScalingMode>, "SetLayerScalingMode"}, {2101, C<&IApplicationDisplayService::SetLayerScalingMode>, "SetLayerScalingMode"},
{2102, C<&IApplicationDisplayService::ConvertScalingMode>, "ConvertScalingMode"}, {2102, C<&IApplicationDisplayService::ConvertScalingMode>, "ConvertScalingMode"},
{2103, C<&IApplicationDisplayService::Cmd2103>, "Cmd2103"},
{2450, C<&IApplicationDisplayService::GetIndirectLayerImageMap>, "GetIndirectLayerImageMap"}, {2450, C<&IApplicationDisplayService::GetIndirectLayerImageMap>, "GetIndirectLayerImageMap"},
{2451, nullptr, "GetIndirectLayerImageCropMap"}, {2451, nullptr, "GetIndirectLayerImageCropMap"},
{2460, C<&IApplicationDisplayService::GetIndirectLayerImageRequiredMemoryInfo>, "GetIndirectLayerImageRequiredMemoryInfo"}, {2460, C<&IApplicationDisplayService::GetIndirectLayerImageRequiredMemoryInfo>, "GetIndirectLayerImageRequiredMemoryInfo"},
@@ -291,11 +289,6 @@ Result IApplicationDisplayService::ConvertScalingMode(Out<ConvertedScaleMode> ou
} }
} }
Result IApplicationDisplayService::Cmd2103(Out<std::array<u8, 0x18>> out_unk18) {
LOG_WARNING(Service_VI, "(stubbed)");
R_SUCCEED();
}
Result IApplicationDisplayService::GetIndirectLayerImageMap( Result IApplicationDisplayService::GetIndirectLayerImageMap(
Out<u64> out_size, Out<u64> out_stride, Out<u64> out_size, Out<u64> out_stride,
OutBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias> out_buffer, OutBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias> out_buffer,
@@ -64,7 +64,6 @@ public:
Result GetDisplayVsyncEvent(OutCopyHandle<Kernel::KReadableEvent> out_vsync_event, Result GetDisplayVsyncEvent(OutCopyHandle<Kernel::KReadableEvent> out_vsync_event,
u64 display_id); u64 display_id);
Result ConvertScalingMode(Out<ConvertedScaleMode> out_scaling_mode, NintendoScaleMode mode); Result ConvertScalingMode(Out<ConvertedScaleMode> out_scaling_mode, NintendoScaleMode mode);
Result Cmd2103(Out<std::array<u8, 0x18>> out_unk18);
Result GetIndirectLayerImageMap( Result GetIndirectLayerImageMap(
Out<u64> out_size, Out<u64> out_stride, Out<u64> out_size, Out<u64> out_stride,
OutBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias> out_buffer, OutBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias> out_buffer,
@@ -300,6 +300,28 @@ Result SharedBufferManager::CreateSession(Kernel::KProcess* owner_process, u64*
} }
} }
// Claim a presentation slot range.
u32 slot_base = 0;
std::array<bool, SharedBufferMaxSessions> in_use{};
for (const auto& [existing_aruid, existing] : m_sessions) {
const u32 index = existing.presentation_slot_base / SharedBufferSlotsPerSession;
if (index < in_use.size())
in_use[index] = true;
}
u32 index = 0;
while (index < in_use.size() && in_use[index])
index++;
if (index >= in_use.size()) {
LOG_ERROR(Service_VI, "Out of shared buffer presentation slots ({} sessions)", SharedBufferMaxSessions);
R_THROW(VI::ResultOperationFailed);
}
slot_base = index * SharedBufferSlotsPerSession;
// Map into process. // Map into process.
Common::ProcessAddress map_address{}; Common::ProcessAddress map_address{};
R_TRY(MapSharedBufferIntoProcessAddressSpace(std::addressof(map_address), m_buffer_page_group, R_TRY(MapSharedBufferIntoProcessAddressSpace(std::addressof(map_address), m_buffer_page_group,
@@ -308,6 +330,7 @@ Result SharedBufferManager::CreateSession(Kernel::KProcess* owner_process, u64*
// Create new session. // Create new session.
auto [it, was_emplaced] = m_sessions.emplace(aruid, SharedBufferSession{}); auto [it, was_emplaced] = m_sessions.emplace(aruid, SharedBufferSession{});
auto& session = it->second; auto& session = it->second;
session.presentation_slot_base = slot_base;
auto& container = m_nvdrv->GetContainer(); auto& container = m_nvdrv->GetContainer();
session.session_id = container.OpenSession(owner_process); session.session_id = container.OpenSession(owner_process);
+39 -42
View File
@@ -101,10 +101,8 @@ struct Memory::Impl {
} }
u64 protect_bytes = 0, protect_begin = 0; u64 protect_bytes = 0, protect_begin = 0;
current_page_table->entries.CommitRegion(vaddr >> YUZU_PAGEBITS, (vaddr + size) >> YUZU_PAGEBITS);
for (u64 addr = vaddr; addr < vaddr + size; addr += YUZU_PAGESIZE) { for (u64 addr = vaddr; addr < vaddr + size; addr += YUZU_PAGESIZE) {
const Common::PageType page_type = current_page_table->entries.GetUnchecked(addr >> YUZU_PAGEBITS).Type(); const Common::PageType page_type = current_page_table->entries[addr >> YUZU_PAGEBITS].ptr.Type();
switch (page_type) { switch (page_type) {
case Common::PageType::RasterizerCachedMemory: case Common::PageType::RasterizerCachedMemory:
if (protect_bytes > 0) { if (protect_bytes > 0) {
@@ -125,14 +123,16 @@ struct Memory::Impl {
} }
[[nodiscard]] u8* GetPointerFromRasterizerCachedMemory(u64 vaddr) const { [[nodiscard]] u8* GetPointerFromRasterizerCachedMemory(u64 vaddr) const {
if (u64 paddr = current_page_table->entries[vaddr >> YUZU_PAGEBITS].Pointer(true); paddr) Common::PhysicalAddress const paddr = current_page_table->entries[vaddr >> YUZU_PAGEBITS].addr;
return reinterpret_cast<u8*>(paddr) + vaddr; if (paddr)
return system.DeviceMemory().GetPointer<u8>(paddr + vaddr);
return {}; return {};
} }
[[nodiscard]] u8* GetPointerFromDebugMemory(u64 vaddr) const { [[nodiscard]] u8* GetPointerFromDebugMemory(u64 vaddr) const {
if (u64 paddr = current_page_table->entries[vaddr >> YUZU_PAGEBITS].Pointer(true); paddr) const Common::PhysicalAddress paddr = current_page_table->entries[vaddr >> YUZU_PAGEBITS].addr;
return reinterpret_cast<u8*>(paddr) + vaddr; if (paddr != 0)
return system.DeviceMemory().GetPointer<u8>(paddr + vaddr);
return {}; return {};
} }
@@ -243,12 +243,10 @@ struct Memory::Impl {
std::size_t page_index = addr >> YUZU_PAGEBITS; std::size_t page_index = addr >> YUZU_PAGEBITS;
std::size_t page_offset = addr & YUZU_PAGEMASK; std::size_t page_offset = addr & YUZU_PAGEMASK;
bool user_accessible = true; bool user_accessible = true;
current_page_table->entries.CommitRegion(page_index, page_index + (size >> YUZU_PAGEBITS) + 1);
while (remaining_size != 0) { while (remaining_size != 0) {
const std::size_t copy_amount = (std::min)(std::size_t(YUZU_PAGESIZE) - page_offset, remaining_size); const std::size_t copy_amount = (std::min)(std::size_t(YUZU_PAGESIZE) - page_offset, remaining_size);
const auto current_vaddr = u64((page_index << YUZU_PAGEBITS) + page_offset); const auto current_vaddr = u64((page_index << YUZU_PAGEBITS) + page_offset);
const auto [pointer, type, _] = current_page_table->entries.GetUnchecked(page_index).PointerTypeBlock(); const auto [pointer, type] = current_page_table->entries[page_index].ptr.PointerType();
switch (type) { switch (type) {
case Common::PageType::Unmapped: { case Common::PageType::Unmapped: {
user_accessible = false; user_accessible = false;
@@ -299,10 +297,10 @@ struct Memory::Impl {
} }
[[nodiscard]] inline const u8* GetSpan(const VAddr addr, const std::size_t size) const noexcept { [[nodiscard]] inline const u8* GetSpan(const VAddr addr, const std::size_t size) const noexcept {
return (current_page_table->entries[addr >> YUZU_PAGEBITS].Block() == current_page_table->entries[(addr + size) >> YUZU_PAGEBITS].Block()) ? GetPointerSilent(addr) : nullptr; return (current_page_table->entries[addr >> YUZU_PAGEBITS].block == current_page_table->entries[(addr + size) >> YUZU_PAGEBITS].block) ? GetPointerSilent(addr) : nullptr;
} }
[[nodiscard]] inline u8* GetSpan(const VAddr addr, const std::size_t size) noexcept { [[nodiscard]] inline u8* GetSpan(const VAddr addr, const std::size_t size) noexcept {
return (current_page_table->entries[addr >> YUZU_PAGEBITS].Block() == current_page_table->entries[(addr + size) >> YUZU_PAGEBITS].Block()) ? GetPointerSilent(addr) : nullptr; return (current_page_table->entries[addr >> YUZU_PAGEBITS].block == current_page_table->entries[(addr + size) >> YUZU_PAGEBITS].block) ? GetPointerSilent(addr) : nullptr;
} }
bool WriteBlockImpl(const Common::ProcessAddress addr, const void* buffer, const std::size_t size, bool unsafe) { bool WriteBlockImpl(const Common::ProcessAddress addr, const void* buffer, const std::size_t size, bool unsafe) {
@@ -406,14 +404,11 @@ struct Memory::Impl {
// The region is at a granularity of CPU pages. // The region is at a granularity of CPU pages.
const u64 num_pages = ((vaddr + size - 1) >> YUZU_PAGEBITS) - (vaddr >> YUZU_PAGEBITS) + 1; const u64 num_pages = ((vaddr + size - 1) >> YUZU_PAGEBITS) - (vaddr >> YUZU_PAGEBITS) + 1;
current_page_table->entries.CommitRegion(vaddr >> YUZU_PAGEBITS, (vaddr >> YUZU_PAGEBITS) + num_pages);
for (u64 i = 0; i < num_pages; ++i, vaddr += YUZU_PAGESIZE) { for (u64 i = 0; i < num_pages; ++i, vaddr += YUZU_PAGESIZE) {
auto& entry = current_page_table->entries.GetUnchecked(vaddr >> YUZU_PAGEBITS); const Common::PageType page_type = current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Type();
const auto [pointer, type, block] = entry.PointerTypeBlock(true);
if (debug) { if (debug) {
// Switch page type to debug if now debug // Switch page type to debug if now debug
switch (type) { switch (page_type) {
case Common::PageType::Unmapped: case Common::PageType::Unmapped:
ASSERT(false && "Attempted to mark unmapped pages as debug"); ASSERT(false && "Attempted to mark unmapped pages as debug");
break; break;
@@ -422,14 +417,14 @@ struct Memory::Impl {
// Page is already marked. // Page is already marked.
break; break;
case Common::PageType::Memory: case Common::PageType::Memory:
entry.MarkDebug(pointer, block); current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Store(0, Common::PageType::DebugMemory);
break; break;
default: default:
UNREACHABLE(); UNREACHABLE();
} }
} else { } else {
// Switch page type to non-debug if now non-debug // Switch page type to non-debug if now non-debug
switch (type) { switch (page_type) {
case Common::PageType::Unmapped: case Common::PageType::Unmapped:
ASSERT(false && "Attempted to mark unmapped pages as non-debug"); ASSERT(false && "Attempted to mark unmapped pages as non-debug");
break; break;
@@ -438,7 +433,8 @@ struct Memory::Impl {
// Don't mess with already non-debug or rasterizer memory. // Don't mess with already non-debug or rasterizer memory.
break; break;
case Common::PageType::DebugMemory: { case Common::PageType::DebugMemory: {
entry.Store(false, Common::PageType::Memory, block, pointer); u8* const pointer = GetPointerFromDebugMemory(vaddr & ~YUZU_PAGEMASK);
current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Store(uintptr_t(pointer) - (vaddr & ~YUZU_PAGEMASK), Common::PageType::Memory);
break; break;
} }
default: default:
@@ -470,10 +466,8 @@ struct Memory::Impl {
// is different). This assumes the specified GPU address region is contiguous as well. // is different). This assumes the specified GPU address region is contiguous as well.
const u64 num_pages = ((vaddr + size - 1) >> YUZU_PAGEBITS) - (vaddr >> YUZU_PAGEBITS) + 1; const u64 num_pages = ((vaddr + size - 1) >> YUZU_PAGEBITS) - (vaddr >> YUZU_PAGEBITS) + 1;
current_page_table->entries.CommitRegion(vaddr >> YUZU_PAGEBITS, (vaddr >> YUZU_PAGEBITS) + num_pages);
for (u64 i = 0; i < num_pages; ++i, vaddr += YUZU_PAGESIZE) { for (u64 i = 0; i < num_pages; ++i, vaddr += YUZU_PAGESIZE) {
auto& entry = current_page_table->entries.GetUnchecked(vaddr >> YUZU_PAGEBITS); const Common::PageType page_type= current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Type();
const Common::PageType page_type = entry.Type();
if (cached) { if (cached) {
// Switch page type to cached if now cached // Switch page type to cached if now cached
switch (page_type) { switch (page_type) {
@@ -483,7 +477,7 @@ struct Memory::Impl {
break; break;
case Common::PageType::DebugMemory: case Common::PageType::DebugMemory:
case Common::PageType::Memory: case Common::PageType::Memory:
entry.MarkRasterizerCached(); current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Store(0, Common::PageType::RasterizerCachedMemory);
break; break;
case Common::PageType::RasterizerCachedMemory: case Common::PageType::RasterizerCachedMemory:
// There can be more than one GPU region mapped per CPU region, so it's common // There can be more than one GPU region mapped per CPU region, so it's common
@@ -505,13 +499,13 @@ struct Memory::Impl {
// that this area is already unmarked as cached. // that this area is already unmarked as cached.
break; break;
case Common::PageType::RasterizerCachedMemory: { case Common::PageType::RasterizerCachedMemory: {
if (auto [ptr, _, block] = entry.PointerTypeBlock(true); ptr == 0) { if (u8* const pointer = GetPointerFromRasterizerCachedMemory(vaddr & ~YUZU_PAGEMASK); pointer == nullptr) {
// It's possible that this function has been called while updating the // It's possible that this function has been called while updating the
// pagetable after unmapping a VMA. In that case the underlying VMA will no // pagetable after unmapping a VMA. In that case the underlying VMA will no
// longer exist, and we should just leave the pagetable entry blank. // longer exist, and we should just leave the pagetable entry blank.
entry.Store(false, Common::PageType::Unmapped, block, 0); current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Store(0, Common::PageType::Unmapped);
} else { } else {
entry.Store(false, Common::PageType::Memory, block, ptr); current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Store(uintptr_t(pointer) - (vaddr & ~YUZU_PAGEMASK), Common::PageType::Memory);
} }
break; break;
} }
@@ -545,18 +539,22 @@ struct Memory::Impl {
ASSERT_MSG(type != Common::PageType::Memory, ASSERT_MSG(type != Common::PageType::Memory,
"Mapping memory page without a pointer @ {:016x}", base * YUZU_PAGESIZE); "Mapping memory page without a pointer @ {:016x}", base * YUZU_PAGESIZE);
page_table.entries.ZeroRegion(base, end);
} else {
auto current_block = block_count.fetch_add(1, std::memory_order_relaxed);
ASSERT(current_block != 65535);
page_table.entries.CommitRegion(base, end);
while (base != end) { while (base != end) {
auto host_ptr = reinterpret_cast<u64>(system.DeviceMemory().GetPointer<u8>(target)) - (base << YUZU_PAGEBITS);; page_table.entries[base].ptr.Store(0, type);
auto& entry = page_table.entries.GetUnchecked(base); page_table.entries[base].addr = 0;
page_table.entries[base].block = 0;
base += 1;
}
} else {
auto orig_base = base;
while (base != end) {
auto host_ptr = uintptr_t(system.DeviceMemory().GetPointer<u8>(target)) - (base << YUZU_PAGEBITS);
auto backing = GetInteger(target) - (base << YUZU_PAGEBITS);
page_table.entries[base].ptr.Store(host_ptr, type);
page_table.entries[base].addr = backing;
page_table.entries[base].block = orig_base << YUZU_PAGEBITS;
entry.Store(false, type, current_block, host_ptr); ASSERT_MSG(page_table.entries[base].ptr.Pointer(),
ASSERT_MSG(page_table.entries[base].Pointer(),
"memory mapping base yield a nullptr within the table"); "memory mapping base yield a nullptr within the table");
base += 1; base += 1;
@@ -571,11 +569,11 @@ struct Memory::Impl {
vaddr &= 0xffffffffffffULL; vaddr &= 0xffffffffffffULL;
if (AddressSpaceContains(*current_page_table, vaddr, 1)) [[likely]] { if (AddressSpaceContains(*current_page_table, vaddr, 1)) [[likely]] {
// Avoid adding any extra logic to this fast-path block // Avoid adding any extra logic to this fast-path block
const auto raw = current_page_table->entries[vaddr >> YUZU_PAGEBITS].Raw(); const uintptr_t raw_pointer = current_page_table->entries[vaddr >> YUZU_PAGEBITS].ptr.Raw();
if (auto pointer = Common::PageTable::PageEntryData::ExtractPointer(raw); pointer) [[likely]] { if (const uintptr_t pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) [[likely]] {
return reinterpret_cast<u8*>(pointer + vaddr); return reinterpret_cast<u8*>(pointer + vaddr);
} else { } else {
switch (static_cast<Common::PageType>(raw.type)) { switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) {
case Common::PageType::Memory: case Common::PageType::Memory:
ASSERT_MSG(false, "Mapped memory page without a pointer @ {:#016x}", vaddr); ASSERT_MSG(false, "Mapped memory page without a pointer @ {:#016x}", vaddr);
return nullptr; return nullptr;
@@ -775,7 +773,6 @@ struct Memory::Impl {
#else #else
Common::HostMemory* host_buffer{}; Common::HostMemory* host_buffer{};
#endif #endif
std::atomic<u16> block_count = 0;
}; };
Memory::Memory(Core::System& system_) : system{system_} { Memory::Memory(Core::System& system_) : system{system_} {
@@ -814,7 +811,7 @@ bool Memory::IsValidVirtualAddress(const Common::ProcessAddress vaddr) const {
if (page >= page_table.entries.size()) { if (page >= page_table.entries.size()) {
return false; return false;
} }
const auto [pointer, type, _] = page_table.entries[page].PointerTypeBlock(); const auto [pointer, type] = page_table.entries[page].ptr.PointerType();
return pointer != 0 || type == Common::PageType::RasterizerCachedMemory || return pointer != 0 || type == Common::PageType::RasterizerCachedMemory ||
type == Common::PageType::DebugMemory; type == Common::PageType::DebugMemory;
} }
@@ -371,10 +371,8 @@ EmitConfig A32AddressSpace::GetEmitConfig() {
.page_table_pointer = std::bit_cast<u64>(conf.page_table), .page_table_pointer = std::bit_cast<u64>(conf.page_table),
.page_table_address_space_bits = 32, .page_table_address_space_bits = 32,
.page_table_pointer_mask = conf.page_table_pointer_mask, .page_table_pointer_mask_bits = conf.page_table_pointer_mask_bits,
.page_table_log2_stride = conf.page_table_log2_stride, .page_table_log2_stride = conf.page_table_log2_stride,
.page_table_marked_bit = conf.page_table_marked_bit,
.page_table_sign_extension = conf.page_table_sign_extension,
.silently_mirror_page_table = true, .silently_mirror_page_table = true,
.absolute_offset_page_table = conf.absolute_offset_page_table, .absolute_offset_page_table = conf.absolute_offset_page_table,
.detect_misaligned_access_via_page_table = conf.detect_misaligned_access_via_page_table, .detect_misaligned_access_via_page_table = conf.detect_misaligned_access_via_page_table,
@@ -545,10 +545,8 @@ EmitConfig A64AddressSpace::GetEmitConfig() {
.page_table_pointer = std::bit_cast<u64>(conf.page_table), .page_table_pointer = std::bit_cast<u64>(conf.page_table),
.page_table_address_space_bits = conf.page_table_address_space_bits, .page_table_address_space_bits = conf.page_table_address_space_bits,
.page_table_pointer_mask = conf.page_table_pointer_mask, .page_table_pointer_mask_bits = conf.page_table_pointer_mask_bits,
.page_table_log2_stride = conf.page_table_log2_stride, .page_table_log2_stride = conf.page_table_log2_stride,
.page_table_marked_bit = conf.page_table_marked_bit,
.page_table_sign_extension = conf.page_table_sign_extension,
.silently_mirror_page_table = conf.silently_mirror_page_table, .silently_mirror_page_table = conf.silently_mirror_page_table,
.absolute_offset_page_table = conf.absolute_offset_page_table, .absolute_offset_page_table = conf.absolute_offset_page_table,
.detect_misaligned_access_via_page_table = conf.detect_misaligned_access_via_page_table, .detect_misaligned_access_via_page_table = conf.detect_misaligned_access_via_page_table,
@@ -128,10 +128,8 @@ struct EmitConfig {
// Page table // Page table
u64 page_table_pointer; u64 page_table_pointer;
std::size_t page_table_address_space_bits; std::size_t page_table_address_space_bits;
u64 page_table_pointer_mask; int page_table_pointer_mask_bits;
std::size_t page_table_log2_stride; std::size_t page_table_log2_stride;
std::optional<std::uint8_t> page_table_marked_bit;
std::optional<std::uint8_t> page_table_sign_extension;
bool silently_mirror_page_table; bool silently_mirror_page_table;
bool absolute_offset_page_table; bool absolute_offset_page_table;
u8 detect_misaligned_access_via_page_table; u8 detect_misaligned_access_via_page_table;
@@ -273,18 +273,9 @@ std::pair<oaknut::XReg, oaknut::XReg> InlinePageTableEmitVAddrLookup(oaknut::Cod
// load x0 = *<(u8*)pagetable + index> // load x0 = *<(u8*)pagetable + index>
code.LDR(Xscratch0, Xpagetable, Xscratch0); code.LDR(Xscratch0, Xpagetable, Xscratch0);
if (ctx.conf.page_table_marked_bit) { if (ctx.conf.page_table_pointer_mask_bits != 0) {
// check for marked bit const u64 mask = u64(~u64(0)) << ctx.conf.page_table_pointer_mask_bits;
code.TBNZ(Xscratch0, *ctx.conf.page_table_marked_bit, *fallback); code.AND(Xscratch0, Xscratch0, mask);
}
if (ctx.conf.page_table_pointer_mask != 0) {
code.AND(Xscratch0, Xscratch0, ctx.conf.page_table_pointer_mask);
}
// TODO: combine this with page_table_pointer_mask
if (ctx.conf.page_table_sign_extension) {
code.SBFM(Xscratch0, Xscratch0, 0, *ctx.conf.page_table_sign_extension);
} }
code.CBZ(Xscratch0, *fallback); code.CBZ(Xscratch0, *fallback);
@@ -9,7 +9,6 @@
#pragma once #pragma once
#include <bit> #include <bit>
#include <utility>
#include "dynarmic/backend/x64/xbyak.h" #include "dynarmic/backend/x64/xbyak.h"
#include "dynarmic/backend/x64/a32_emit_x64.h" #include "dynarmic/backend/x64/a32_emit_x64.h"
@@ -79,40 +78,27 @@ Xbyak::RegExp EmitVAddrLookup(BlockOfCode& code, EmitContext& ctx, size_t bitsiz
template<> template<>
[[maybe_unused]] Xbyak::RegExp EmitVAddrLookup<A32EmitContext>(BlockOfCode& code, A32EmitContext& ctx, size_t bitsize, Xbyak::Label& abort, Xbyak::Reg64 vaddr) { [[maybe_unused]] Xbyak::RegExp EmitVAddrLookup<A32EmitContext>(BlockOfCode& code, A32EmitContext& ctx, size_t bitsize, Xbyak::Label& abort, Xbyak::Reg64 vaddr) {
const Xbyak::Reg64 page = ctx.reg_alloc.ScratchGpr(code); const Xbyak::Reg64 page = ctx.reg_alloc.ScratchGpr(code);
const Xbyak::Reg64 tmp = ctx.conf.absolute_offset_page_table && ctx.conf.page_table_pointer_mask == 0 ? page : ctx.reg_alloc.ScratchGpr(code); const Xbyak::Reg32 tmp = ctx.conf.absolute_offset_page_table ? page.cvt32() : ctx.reg_alloc.ScratchGpr(code).cvt32();
EmitDetectMisalignedVAddr(code, ctx, bitsize, abort, vaddr, tmp); EmitDetectMisalignedVAddr(code, ctx, bitsize, abort, vaddr, tmp.cvt64());
code.mov(tmp, vaddr); // TODO: This code assumes vaddr has been zext from 32-bits to 64-bits.
code.mov(tmp, vaddr.cvt32());
code.shr(tmp, int(page_table_const_bits)); code.shr(tmp, int(page_table_const_bits));
code.shl(tmp, int(ctx.conf.page_table_log2_stride)); code.shl(tmp, int(ctx.conf.page_table_log2_stride));
code.mov(page, qword[r14 + tmp.cvt64()]); code.mov(page, qword[r14 + tmp.cvt64()]);
if (ctx.conf.page_table_pointer_mask_bits == 0) {
// check for marked bit, use as unmapped if marked
if (ctx.conf.page_table_marked_bit) {
code.bt(page, *ctx.conf.page_table_marked_bit);
code.jc(abort, code.T_NEAR);
}
// mask away attributes
if (ctx.conf.page_table_pointer_mask == 0) {
code.test(page, page); code.test(page, page);
} else if (std::in_range<s32>(ctx.conf.page_table_pointer_mask)) {
code.and_(page, ctx.conf.page_table_pointer_mask);
} else { } else {
code.mov(tmp, ctx.conf.page_table_pointer_mask); code.and_(page, ~u32(0) << ctx.conf.page_table_pointer_mask_bits);
code.and_(page, tmp);
} }
if (ctx.conf.page_table_sign_extension) {
code.shl(page, *ctx.conf.page_table_sign_extension);
code.sar(page, *ctx.conf.page_table_sign_extension);
}
code.jz(abort, code.T_NEAR); code.jz(abort, code.T_NEAR);
if (ctx.conf.absolute_offset_page_table) { if (ctx.conf.absolute_offset_page_table) {
return page + vaddr; return page + vaddr;
} }
code.mov(tmp, vaddr); code.mov(tmp, vaddr.cvt32());
code.and_(tmp, u32(page_table_const_mask)); code.and_(tmp, static_cast<u32>(page_table_const_mask));
return page + tmp.cvt64(); return page + tmp.cvt64();
} }
@@ -122,7 +108,7 @@ template<>
const size_t unused_top_bits = 64 - ctx.conf.page_table_address_space_bits; const size_t unused_top_bits = 64 - ctx.conf.page_table_address_space_bits;
const Xbyak::Reg64 page = ctx.reg_alloc.ScratchGpr(code); const Xbyak::Reg64 page = ctx.reg_alloc.ScratchGpr(code);
const Xbyak::Reg64 tmp = ctx.conf.absolute_offset_page_table && ctx.conf.page_table_pointer_mask == 0 ? page : ctx.reg_alloc.ScratchGpr(code); const Xbyak::Reg64 tmp = ctx.conf.absolute_offset_page_table ? page : ctx.reg_alloc.ScratchGpr(code);
EmitDetectMisalignedVAddr(code, ctx, bitsize, abort, vaddr, tmp); EmitDetectMisalignedVAddr(code, ctx, bitsize, abort, vaddr, tmp);
@@ -157,26 +143,11 @@ template<>
code.shl(tmp, int(ctx.conf.page_table_log2_stride)); code.shl(tmp, int(ctx.conf.page_table_log2_stride));
code.mov(page, qword[r14 + tmp]); code.mov(page, qword[r14 + tmp]);
if (ctx.conf.page_table_pointer_mask_bits == 0) {
// check for marked bit, use as unmapped if marked
if (ctx.conf.page_table_marked_bit) {
code.bt(page, *ctx.conf.page_table_marked_bit);
code.jc(abort, code.T_NEAR);
}
// mask away attributes
if (ctx.conf.page_table_pointer_mask == 0) {
code.test(page, page); code.test(page, page);
} else if (std::in_range<s32>(ctx.conf.page_table_pointer_mask)) {
code.and_(page, ctx.conf.page_table_pointer_mask);
} else { } else {
code.mov(tmp, ctx.conf.page_table_pointer_mask); code.and_(page, ~u32(0) << ctx.conf.page_table_pointer_mask_bits);
code.and_(page, tmp);
} }
if (ctx.conf.page_table_sign_extension) {
code.shl(page, *ctx.conf.page_table_sign_extension);
code.sar(page, *ctx.conf.page_table_sign_extension);
}
code.jz(abort, code.T_NEAR); code.jz(abort, code.T_NEAR);
if (ctx.conf.absolute_offset_page_table) { if (ctx.conf.absolute_offset_page_table) {
return page + vaddr; return page + vaddr;
@@ -159,23 +159,14 @@ struct UserConfig {
/// Maximum size is limited by the maximum length of a x86_64 / arm64 jump. /// Maximum size is limited by the maximum length of a x86_64 / arm64 jump.
std::uint32_t code_cache_size = 128 * 1024 * 1024; // bytes std::uint32_t code_cache_size = 128 * 1024 * 1024; // bytes
/// Applies a bit mask to the bits in host pointers from the page table. /// Masks out the first N bits in host pointers from the page table.
/// The intention behind this is to allow users of Dynarmic to pack attributes in the /// The intention behind this is to allow users of Dynarmic to pack attributes in the
/// same integer and update the pointer attribute pair atomically. /// same integer and update the pointer attribute pair atomically.
/// If the configured value is ~(0b111ULL), all pointers will be forcefully aligned to 8 bytes. /// If the configured value is 3, all pointers will be forcefully aligned to 8 bytes.
std::uint64_t page_table_pointer_mask = 0; std::int32_t page_table_pointer_mask_bits = 0;
/// Log2 of the size per page entry, value should be either 3 or 4 // Log2 of the size per page entry, value should be either 3 or 4
std::uint32_t page_table_log2_stride = 3; std::size_t page_table_log2_stride = 3;
/// Setting this value has Dynarmic check the specified bit of the page pointer provided by page table.
/// If the bit is set to 1, Dynarmic will treat it as unmapped.
/// This bit should be included as part of `page_table_pointer_mask_bits`.
std::optional<std::uint8_t> page_table_marked_bit = std::nullopt;
/// If this value is set, Dynarmic will sign extend the page table pointer by this bit.
/// Useful for compacting bits into the page table and should be used as part of `page_table_pointer_mask`.
std::optional<std::uint8_t> page_table_sign_extension = std::nullopt;
/// Select the architecture version to use. /// Select the architecture version to use.
/// There are minor behavioural differences between versions. /// There are minor behavioural differences between versions.
@@ -173,23 +173,14 @@ struct UserConfig {
/// This is only used if page_table is not nullptr. /// This is only used if page_table is not nullptr.
std::uint32_t page_table_address_space_bits = 36; std::uint32_t page_table_address_space_bits = 36;
/// Applies a bit mask to the bits in host pointers from the page table. /// Masks out the first N bits in host pointers from the page table.
/// The intention behind this is to allow users of Dynarmic to pack attributes in the /// The intention behind this is to allow users of Dynarmic to pack attributes in the
/// same integer and update the pointer attribute pair atomically. /// same integer and update the pointer attribute pair atomically.
/// If the configured value is ~(0b111ULL), all pointers will be forcefully aligned to 8 bytes. /// If the configured value is 3, all pointers will be forcefully aligned to 8 bytes.
std::uint64_t page_table_pointer_mask = 0; std::int32_t page_table_pointer_mask_bits = 0;
/// Log2 of the size per page entry, value should be either 3 or 4 // Log2 of the size per page entry, value should be either 3 or 4
std::uint32_t page_table_log2_stride = 3; std::size_t page_table_log2_stride = 3;
/// Setting this value has Dynarmic check the specified bit of the page pointer provided by page table.
/// If the bit is set to 1, Dynarmic will treat it as unmapped.
/// This bit should be included as part of `page_table_pointer_mask`.
std::optional<std::uint8_t> page_table_marked_bit = std::nullopt;
/// If this value is set, Dynarmic will sign extend the page table pointer by this bit.
/// Useful for compacting bits into the page table and should be used as part of `page_table_pointer_mask`.
std::optional<std::uint8_t> page_table_sign_extension = std::nullopt;
/// Counter-timer frequency register. The value of the register is not interpreted by /// Counter-timer frequency register. The value of the register is not interpreted by
/// dynarmic. /// dynarmic.
+2
View File
@@ -321,6 +321,8 @@ static void RunTestInstance(Dynarmic::A32::Jit& jit,
uni_env.PadCodeMem(); uni_env.PadCodeMem();
jit_env.modified_memory.clear(); jit_env.modified_memory.clear();
uni_env.modified_memory.clear(); uni_env.modified_memory.clear();
jit_env.interrupts.clear();
uni_env.interrupts.clear();
jit.Regs() = regs; jit.Regs() = regs;
jit.ExtRegs() = vecs; jit.ExtRegs() = vecs;
@@ -176,6 +176,8 @@ static void RunTestInstance(Dynarmic::A64::Jit& jit, A64Unicorn& uni, A64TestEnv
uni_env.code_mem_start_address = instructions_start; uni_env.code_mem_start_address = instructions_start;
jit_env.modified_memory.clear(); jit_env.modified_memory.clear();
uni_env.modified_memory.clear(); uni_env.modified_memory.clear();
jit_env.interrupts.clear();
uni_env.interrupts.clear();
const u64 initial_sp = RandInt<u64>(0x30'0000'0000, 0x40'0000'0000) * 4; const u64 initial_sp = RandInt<u64>(0x30'0000'0000, 0x40'0000'0000) * 4;
+10 -4
View File
@@ -282,17 +282,20 @@ int main(int argc, char** argv) {
return 1; return 1;
} }
const char* const hex_instruction = [s = argv[2]] { const char* const hex_instruction = [argv] {
if (strlen(s) > 2 && s[0] == '0' && s[1] == 'x') if (strlen(argv[2]) > 2 && argv[2][0] == '0' && argv[2][1] == 'x') {
return s + 2; return argv[2] + 2;
return s; }
return argv[2];
}(); }();
if (strlen(hex_instruction) > 8) { if (strlen(hex_instruction) > 8) {
fmt::print("hex string too long\n"); fmt::print("hex string too long\n");
return 1; return 1;
} }
const u32 instruction = strtol(hex_instruction, nullptr, 16); const u32 instruction = strtol(hex_instruction, nullptr, 16);
if (strcmp(argv[1], "a32") == 0) { if (strcmp(argv[1], "a32") == 0) {
PrintA32Instruction(instruction); PrintA32Instruction(instruction);
} else if (strcmp(argv[1], "a64") == 0) { } else if (strcmp(argv[1], "a64") == 0) {
@@ -303,11 +306,13 @@ int main(int argc, char** argv) {
fmt::print("Invalid mode: {}\nValid values: a32, a64, thumb\n", argv[1]); fmt::print("Invalid mode: {}\nValid values: a32, a64, thumb\n", argv[1]);
return 1; return 1;
} }
if (argc == 4) { if (argc == 4) {
if (strcmp(argv[3], "-exec") != 0) { if (strcmp(argv[3], "-exec") != 0) {
fmt::print("Invalid option {}\n", argv[3]); fmt::print("Invalid option {}\n", argv[3]);
return 1; return 1;
} }
if (strcmp(argv[1], "a32") == 0) { if (strcmp(argv[1], "a32") == 0) {
ExecuteA32Instruction(instruction); ExecuteA32Instruction(instruction);
} else { } else {
@@ -315,5 +320,6 @@ int main(int argc, char** argv) {
return 1; return 1;
} }
} }
return 0; return 0;
} }
+24 -40
View File
@@ -354,8 +354,6 @@ u32 GenRandomA64Inst(u64 pc, bool is_last_inst) {
"MSR_reg", "MSR_reg",
"MSR_imm", "MSR_imm",
"MRS", "MRS",
// generates F16
"FCADD_vec",
}; };
for (const auto& [fn, bitstring] : list) { for (const auto& [fn, bitstring] : list) {
@@ -442,6 +440,7 @@ void RunTestInstance(Dynarmic::A32::Jit& jit,
std::copy(instructions.begin(), instructions.end(), jit_env.code_mem.begin() + num_words); std::copy(instructions.begin(), instructions.end(), jit_env.code_mem.begin() + num_words);
jit_env.PadCodeMem(); jit_env.PadCodeMem();
jit_env.modified_memory.clear(); jit_env.modified_memory.clear();
jit_env.interrupts.clear();
jit.Regs() = regs; jit.Regs() = regs;
jit.ExtRegs() = vecs; jit.ExtRegs() = vecs;
@@ -511,6 +510,7 @@ void RunTestInstance(Dynarmic::A64::Jit& jit,
jit_env.code_mem.emplace_back(0x14000000); // B . jit_env.code_mem.emplace_back(0x14000000); // B .
jit_env.code_mem_start_address = start_address; jit_env.code_mem_start_address = start_address;
jit_env.modified_memory.clear(); jit_env.modified_memory.clear();
jit_env.interrupts.clear();
jit.SetRegisters(regs); jit.SetRegisters(regs);
jit.SetVectors(vecs); jit.SetVectors(vecs);
@@ -536,52 +536,36 @@ void RunTestInstance(Dynarmic::A64::Jit& jit,
fmt::print("initial_regs:"); fmt::print("initial_regs:");
for (u64 i : regs) for (u64 i : regs)
fmt::print(" {:016x}", i); fmt::print(" {:016x}", i);
fmt::print( fmt::print("\n");
"\n" fmt::print("initial_vecs:");
"initial_vecs:"
);
for (auto i : vecs) for (auto i : vecs)
fmt::print(" {:016x}:{:016x}", i[0], i[1]); fmt::print(" {:016x}:{:016x}", i[0], i[1]);
fmt::print( fmt::print("\n");
"\n" fmt::print("initial_sp: {:016x}\n", initial_sp);
"initial_sp: {:016x}\n" fmt::print("initial_pstate: {:08x}\n", pstate);
"initial_pstate: {:08x}\n" fmt::print("initial_fpcr: {:08x}\n", fpcr);
"initial_fpcr: {:08x}\n" fmt::print("final_regs:");
"final_regs:"
, initial_sp
, pstate
, fpcr
);
for (u64 i : jit.GetRegisters()) for (u64 i : jit.GetRegisters())
fmt::print(" {:016x}", i); fmt::print(" {:016x}", i);
fmt::print( fmt::print("\n");
"\n" fmt::print("final_vecs:");
"final_vecs:"
);
for (auto i : jit.GetVectors()) for (auto i : jit.GetVectors())
fmt::print(" {:016x}:{:016x}", i[0], i[1]); fmt::print(" {:016x}:{:016x}", i[0], i[1]);
fmt::print( fmt::print("\n");
"\n" fmt::print("final_sp: {:016x}\n", jit.GetSP());
"final_sp: {:016x}\n" fmt::print("final_pc: {:016x}\n", jit.GetPC());
"final_pc: {:016x}\n" fmt::print("final_pstate: {:08x}\n", jit.GetPstate());
"final_pstate: {:08x}\n" fmt::print("final_fpcr: {:08x}\n", jit.GetFpcr());
"final_fpcr: {:08x}\n" fmt::print("final_qc : {}\n", FP::FPSR{jit.GetFpsr()}.QC());
"final_qc : {}\n" fmt::print("mod_mem:");
"mod_mem:"
, jit.GetSP()
, jit.GetPC()
, jit.GetPstate()
, jit.GetFpcr()
, FP::FPSR{jit.GetFpsr()}.QC()
);
for (auto [addr, value] : jit_env.modified_memory) for (auto [addr, value] : jit_env.modified_memory)
fmt::print(" {:08x}:{:02x}", addr, value); fmt::print(" {:08x}:{:02x}", addr, value);
fmt::print( fmt::print("\n");
"\n" fmt::print("interrupts:\n");
"===\n" for (const auto& i : jit_env.interrupts)
"{}" std::puts(i.c_str());
, jit.Disassemble() fmt::print("===\n");
); fmt::print("{}", jit.Disassemble());
} }
} }
+2
View File
@@ -99,6 +99,7 @@ void RunTestInstance(Dynarmic::A32::Jit& jit,
std::copy(instructions.begin(), instructions.end(), jit_env.code_mem.begin() + num_words); std::copy(instructions.begin(), instructions.end(), jit_env.code_mem.begin() + num_words);
jit_env.PadCodeMem(); jit_env.PadCodeMem();
jit_env.modified_memory.clear(); jit_env.modified_memory.clear();
jit_env.interrupts.clear();
jit.Regs() = regs; jit.Regs() = regs;
jit.ExtRegs() = vecs; jit.ExtRegs() = vecs;
@@ -191,6 +192,7 @@ void RunTestInstance(A64::Jit& jit,
jit_env.code_mem.emplace_back(0x14000000); // B . jit_env.code_mem.emplace_back(0x14000000); // B .
jit_env.code_mem_start_address = start_address; jit_env.code_mem_start_address = start_address;
jit_env.modified_memory.clear(); jit_env.modified_memory.clear();
jit_env.interrupts.clear();
jit.SetRegisters(regs); jit.SetRegisters(regs);
jit.SetVectors(vecs); jit.SetVectors(vecs);
+11 -21
View File
@@ -299,17 +299,12 @@ void Config::ReadDataStorageValues() {
void Config::ReadDebuggingValues() { void Config::ReadDebuggingValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Debugging)); BeginGroup(Settings::TranslateCategory(Settings::Category::Debugging));
if (global) { // Intentionally not using the QT default setting as this is intended to be changed in the ini
// Intentionally not using the QT default setting as this is intended to be changed in the ini Settings::values.record_frame_times =
Settings::values.record_frame_times = ReadBooleanSetting(std::string("record_frame_times"), std::make_optional(false));
ReadBooleanSetting(std::string("record_frame_times"), std::make_optional(false));
ReadCategory(Settings::Category::Debugging); ReadCategory(Settings::Category::Debugging);
ReadCategory(Settings::Category::DebuggingGraphics); ReadCategory(Settings::Category::DebuggingGraphics);
} else {
ReadSettingGeneric(&Settings::values.program_args);
ReadSettingGeneric(&Settings::values.debug_knobs);
}
EndGroup(); EndGroup();
} }
@@ -420,12 +415,12 @@ void Config::ReadLibraryAppletValues() {
void Config::ReadValues() { void Config::ReadValues() {
if (global) { if (global) {
ReadDataStorageValues(); ReadDataStorageValues();
ReadDebuggingValues();
ReadDisabledAddOnValues(); ReadDisabledAddOnValues();
ReadServiceValues(); ReadServiceValues();
ReadWebServiceValues(); ReadWebServiceValues();
ReadMiscellaneousValues(); ReadMiscellaneousValues();
} }
ReadDebuggingValues();
ReadLibraryAppletValues(); ReadLibraryAppletValues();
ReadNetworkValues(); ReadNetworkValues();
ReadControlValues(); ReadControlValues();
@@ -516,13 +511,13 @@ void Config::SaveValues() {
if (global) { if (global) {
LOG_DEBUG(Config, "Saving global generic configuration values"); LOG_DEBUG(Config, "Saving global generic configuration values");
SaveDataStorageValues(); SaveDataStorageValues();
SaveDebuggingValues();
SaveDisabledAddOnValues(); SaveDisabledAddOnValues();
SaveWebServiceValues(); SaveWebServiceValues();
SaveMiscellaneousValues(); SaveMiscellaneousValues();
} else { } else {
LOG_DEBUG(Config, "Saving only generic configuration values"); LOG_DEBUG(Config, "Saving only generic configuration values");
} }
SaveDebuggingValues();
SaveLibraryAppletValues(); SaveLibraryAppletValues();
SaveNetworkValues(); SaveNetworkValues();
SaveControlValues(); SaveControlValues();
@@ -605,16 +600,11 @@ void Config::SaveDataStorageValues() {
void Config::SaveDebuggingValues() { void Config::SaveDebuggingValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Debugging)); BeginGroup(Settings::TranslateCategory(Settings::Category::Debugging));
if (global) { // Intentionally not using the QT default setting as this is intended to be changed in the ini
// Intentionally not using the QT default setting as this is intended to be changed in the ini WriteBooleanSetting(std::string("record_frame_times"), Settings::values.record_frame_times);
WriteBooleanSetting(std::string("record_frame_times"), Settings::values.record_frame_times);
WriteCategory(Settings::Category::Debugging); WriteCategory(Settings::Category::Debugging);
WriteCategory(Settings::Category::DebuggingGraphics); WriteCategory(Settings::Category::DebuggingGraphics);
} else {
WriteSettingGeneric(&Settings::values.program_args);
WriteSettingGeneric(&Settings::values.debug_knobs);
}
EndGroup(); EndGroup();
} }
+2 -2
View File
@@ -170,12 +170,12 @@ void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
auto& emulated = console.motion_values.emulated; auto& emulated = console.motion_values.emulated;
raw_status = TransformToMotion(callback); raw_status = TransformToMotion(callback);
emulated.SetAcceleration(Common::Vec<f32, 3>{ emulated.SetAcceleration(Common::Vec3f{
raw_status.accel.x.value, raw_status.accel.x.value,
raw_status.accel.y.value, raw_status.accel.y.value,
raw_status.accel.z.value, raw_status.accel.z.value,
}); });
emulated.SetGyroscope(Common::Vec<f32, 3>{ emulated.SetGyroscope(Common::Vec3f{
raw_status.gyro.x.value, raw_status.gyro.x.value,
raw_status.gyro.y.value, raw_status.gyro.y.value,
raw_status.gyro.z.value, raw_status.gyro.z.value,
+7 -6
View File
@@ -18,6 +18,7 @@
#include "common/input.h" #include "common/input.h"
#include "common/param_package.h" #include "common/param_package.h"
#include "common/point.h" #include "common/point.h"
#include "common/quaternion.h"
#include "common/vector_math.h" #include "common/vector_math.h"
#include "hid_core/frontend/motion_input.h" #include "hid_core/frontend/motion_input.h"
#include "hid_core/hid_types.h" #include "hid_core/hid_types.h"
@@ -42,12 +43,12 @@ using TouchValues = std::array<Common::Input::TouchStatus, MaxTouchDevices>;
// Contains all motion related data that is used on the services // Contains all motion related data that is used on the services
struct ConsoleMotion { struct ConsoleMotion {
Common::Vec<f32, 3> accel{}; Common::Vec3f accel{};
Common::Vec<f32, 3> gyro{}; Common::Vec3f gyro{};
Common::Vec<f32, 3> rotation{}; Common::Vec3f rotation{};
std::array<Common::Vec<f32, 3>, 3> orientation{}; std::array<Common::Vec3f, 3> orientation{};
Common::Vec<f32, 4> quaternion{}; Common::Quaternion<f32> quaternion{};
Common::Vec<f32, 3> gyro_bias{}; Common::Vec3f gyro_bias{};
f32 verticalization_error{}; f32 verticalization_error{};
bool is_at_rest{}; bool is_at_rest{};
}; };
@@ -1051,12 +1051,12 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
auto& emulated = controller.motion_values[index].emulated; auto& emulated = controller.motion_values[index].emulated;
raw_status = TransformToMotion(callback); raw_status = TransformToMotion(callback);
emulated.SetAcceleration(Common::Vec<f32, 3>{ emulated.SetAcceleration(Common::Vec3f{
raw_status.accel.x.value, raw_status.accel.x.value,
raw_status.accel.y.value, raw_status.accel.y.value,
raw_status.accel.z.value, raw_status.accel.z.value,
}); });
emulated.SetGyroscope(Common::Vec<f32, 3>{ emulated.SetGyroscope(Common::Vec3f{
raw_status.gyro.x.value, raw_status.gyro.x.value,
raw_status.gyro.y.value, raw_status.gyro.y.value,
raw_status.gyro.z.value, raw_status.gyro.z.value,
+5 -5
View File
@@ -107,11 +107,11 @@ struct RingSensorForce {
using NfcState = Common::Input::NfcStatus; using NfcState = Common::Input::NfcStatus;
struct ControllerMotion { struct ControllerMotion {
Common::Vec<f32, 3> accel{}; Common::Vec3f accel{};
Common::Vec<f32, 3> gyro{}; Common::Vec3f gyro{};
Common::Vec<f32, 3> rotation{}; Common::Vec3f rotation{};
Common::Vec<f32, 3> euler{}; Common::Vec3f euler{};
std::array<Common::Vec<f32, 3>, 3> orientation{}; std::array<Common::Vec3f, 3> orientation{};
bool is_at_rest{}; bool is_at_rest{};
}; };
+90 -90
View File
@@ -26,19 +26,20 @@ void MotionInput::SetPID(f32 new_kp, f32 new_ki, f32 new_kd) {
kd = new_kd; kd = new_kd;
} }
void MotionInput::SetAcceleration(const Common::Vec<f32, 3>& acceleration) { void MotionInput::SetAcceleration(const Common::Vec3f& acceleration) {
accel = acceleration; accel = acceleration;
accel[0] = std::clamp(accel[0], -AccelMaxValue, AccelMaxValue);
accel[1] = std::clamp(accel[1], -AccelMaxValue, AccelMaxValue); accel.x = std::clamp(accel.x, -AccelMaxValue, AccelMaxValue);
accel[2] = std::clamp(accel[2], -AccelMaxValue, AccelMaxValue); accel.y = std::clamp(accel.y, -AccelMaxValue, AccelMaxValue);
accel.z = std::clamp(accel.z, -AccelMaxValue, AccelMaxValue);
} }
void MotionInput::SetGyroscope(const Common::Vec<f32, 3>& gyroscope) { void MotionInput::SetGyroscope(const Common::Vec3f& gyroscope) {
gyro = gyroscope - gyro_bias; gyro = gyroscope - gyro_bias;
gyro[0] = std::clamp(gyro[0], -GyroMaxValue, GyroMaxValue); gyro.x = std::clamp(gyro.x, -GyroMaxValue, GyroMaxValue);
gyro[1] = std::clamp(gyro[1], -GyroMaxValue, GyroMaxValue); gyro.y = std::clamp(gyro.y, -GyroMaxValue, GyroMaxValue);
gyro[2] = std::clamp(gyro[2], -GyroMaxValue, GyroMaxValue); gyro.z = std::clamp(gyro.z, -GyroMaxValue, GyroMaxValue);
// Auto adjust gyro_bias to minimize drift // Auto adjust gyro_bias to minimize drift
if (!IsMoving(IsAtRestRelaxed)) { if (!IsMoving(IsAtRestRelaxed)) {
@@ -58,25 +59,25 @@ void MotionInput::SetGyroscope(const Common::Vec<f32, 3>& gyroscope) {
} }
} }
void MotionInput::SetQuaternion(const Common::Vec<f32, 4>& quaternion) { void MotionInput::SetQuaternion(const Common::Quaternion<f32>& quaternion) {
quat = quaternion; quat = quaternion;
} }
void MotionInput::SetEulerAngles(const Common::Vec<f32, 3>& euler_angles) { void MotionInput::SetEulerAngles(const Common::Vec3f& euler_angles) {
const float cr = std::cos(euler_angles[0] * 0.5f); const float cr = std::cos(euler_angles.x * 0.5f);
const float sr = std::sin(euler_angles[0] * 0.5f); const float sr = std::sin(euler_angles.x * 0.5f);
const float cp = std::cos(euler_angles[1] * 0.5f); const float cp = std::cos(euler_angles.y * 0.5f);
const float sp = std::sin(euler_angles[1] * 0.5f); const float sp = std::sin(euler_angles.y * 0.5f);
const float cy = std::cos(euler_angles[2] * 0.5f); const float cy = std::cos(euler_angles.z * 0.5f);
const float sy = std::sin(euler_angles[2] * 0.5f); const float sy = std::sin(euler_angles.z * 0.5f);
quat[3] = cr * cp * cy + sr * sp * sy; quat.w = cr * cp * cy + sr * sp * sy;
quat[0] = sr * cp * cy - cr * sp * sy; quat.xyz.x = sr * cp * cy - cr * sp * sy;
quat[1] = cr * sp * cy + sr * cp * sy; quat.xyz.y = cr * sp * cy + sr * cp * sy;
quat[2] = cr * cp * sy - sr * sp * cy; quat.xyz.z = cr * cp * sy - sr * sp * cy;
} }
void MotionInput::SetGyroBias(const Common::Vec<f32, 3>& bias) { void MotionInput::SetGyroBias(const Common::Vec3f& bias) {
gyro_bias = bias; gyro_bias = bias;
} }
@@ -97,7 +98,7 @@ void MotionInput::ResetRotations() {
} }
void MotionInput::ResetQuaternion() { void MotionInput::ResetQuaternion() {
quat = Common::Vec<f32, 4>{0.0f, 0.0f, -1.0f, 0.0f}; quat = {{0.0f, 0.0f, -1.0f}, 0.0f};
} }
bool MotionInput::IsMoving(f32 sensitivity) const { bool MotionInput::IsMoving(f32 sensitivity) const {
@@ -136,10 +137,10 @@ void MotionInput::UpdateOrientation(u64 elapsed_time) {
ResetOrientation(); ResetOrientation();
} }
// Short name local variable for readability // Short name local variable for readability
f32 q1 = quat[3]; f32 q1 = quat.w;
f32 q2 = quat[0]; f32 q2 = quat.xyz[0];
f32 q3 = quat[1]; f32 q3 = quat.xyz[1];
f32 q4 = quat[2]; f32 q4 = quat.xyz[2];
const auto sample_period = static_cast<f32>(elapsed_time) / 1000000.0f; const auto sample_period = static_cast<f32>(elapsed_time) / 1000000.0f;
// Ignore invalid elapsed time // Ignore invalid elapsed time
@@ -149,23 +150,23 @@ void MotionInput::UpdateOrientation(u64 elapsed_time) {
const auto normal_accel = accel.Normalized(); const auto normal_accel = accel.Normalized();
auto rad_gyro = gyro * std::numbers::pi_v<float> * 2.f; auto rad_gyro = gyro * std::numbers::pi_v<float> * 2.f;
const f32 swap = rad_gyro[0]; const f32 swap = rad_gyro.x;
rad_gyro[0] = rad_gyro[1]; rad_gyro.x = rad_gyro.y;
rad_gyro[1] = -swap; rad_gyro.y = -swap;
rad_gyro[2] = -rad_gyro[2]; rad_gyro.z = -rad_gyro.z;
// Clear gyro values if there is no gyro present // Clear gyro values if there is no gyro present
if (only_accelerometer) { if (only_accelerometer) {
rad_gyro[0] = 0; rad_gyro.x = 0;
rad_gyro[1] = 0; rad_gyro.y = 0;
rad_gyro[2] = 0; rad_gyro.z = 0;
} }
// Ignore drift correction if acceleration is not reliable // Ignore drift correction if acceleration is not reliable
if (accel.Length() >= 0.75f && accel.Length() <= 1.25f) { if (accel.Length() >= 0.75f && accel.Length() <= 1.25f) {
const f32 ax = -normal_accel[0]; const f32 ax = -normal_accel.x;
const f32 ay = normal_accel[1]; const f32 ay = normal_accel.y;
const f32 az = -normal_accel[2]; const f32 az = -normal_accel.z;
// Estimated direction of gravity // Estimated direction of gravity
const f32 vx = 2.0f * (q2 * q4 - q1 * q3); const f32 vx = 2.0f * (q2 * q4 - q1 * q3);
@@ -173,7 +174,7 @@ void MotionInput::UpdateOrientation(u64 elapsed_time) {
const f32 vz = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4; const f32 vz = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4;
// Error is cross product between estimated direction and measured direction of gravity // Error is cross product between estimated direction and measured direction of gravity
const Common::Vec<f32, 3> new_real_error{ const Common::Vec3f new_real_error = {
az * vx - ax * vz, az * vx - ax * vz,
ay * vz - az * vy, ay * vz - az * vy,
ax * vy - ay * vx, ax * vy - ay * vx,
@@ -201,16 +202,16 @@ void MotionInput::UpdateOrientation(u64 elapsed_time) {
rad_gyro += 10.0f * kd * derivative_error; rad_gyro += 10.0f * kd * derivative_error;
// Emulate gyro values for games that need them // Emulate gyro values for games that need them
gyro[0] = -rad_gyro[1]; gyro.x = -rad_gyro.y;
gyro[1] = rad_gyro[0]; gyro.y = rad_gyro.x;
gyro[2] = -rad_gyro[2]; gyro.z = -rad_gyro.z;
UpdateRotation(elapsed_time); UpdateRotation(elapsed_time);
} }
} }
const f32 gx = rad_gyro[1]; const f32 gx = rad_gyro.y;
const f32 gy = rad_gyro[0]; const f32 gy = rad_gyro.x;
const f32 gz = rad_gyro[2]; const f32 gz = rad_gyro.z;
// Integrate rate of change of quaternion // Integrate rate of change of quaternion
const f32 pa = q2; const f32 pa = q2;
@@ -221,58 +222,57 @@ void MotionInput::UpdateOrientation(u64 elapsed_time) {
q3 = pb + (q1 * gy - pa * gz + pc * gx) * (0.5f * sample_period); q3 = pb + (q1 * gy - pa * gz + pc * gx) * (0.5f * sample_period);
q4 = pc + (q1 * gz + pa * gy - pb * gx) * (0.5f * sample_period); q4 = pc + (q1 * gz + pa * gy - pb * gx) * (0.5f * sample_period);
quat[3] = q1; quat.w = q1;
quat[0] = q2; quat.xyz[0] = q2;
quat[1] = q3; quat.xyz[1] = q3;
quat[2] = q4; quat.xyz[2] = q4;
quat = quat.Normalized(); quat = quat.Normalized();
} }
std::array<Common::Vec<f32, 3>, 3> MotionInput::GetOrientation() const { std::array<Common::Vec3f, 3> MotionInput::GetOrientation() const {
const Common::Vec<f32, 4> quad{ const Common::Quaternion<float> quad{
-quat[1], .xyz = {-quat.xyz[1], -quat.xyz[0], -quat.w},
-quat[0], .w = -quat.xyz[2],
-quat[3],
-quat[2],
}; };
const std::array<f32, 16> matrix4x4 = quad.ToMatrix(); const std::array<float, 16> matrix4x4 = quad.ToMatrix();
return {Common::Vec<f32, 3>(matrix4x4[0], matrix4x4[1], -matrix4x4[2]),
Common::Vec<f32, 3>(matrix4x4[4], matrix4x4[5], -matrix4x4[6]), return {Common::Vec3f(matrix4x4[0], matrix4x4[1], -matrix4x4[2]),
Common::Vec<f32, 3>(-matrix4x4[8], -matrix4x4[9], matrix4x4[10])}; Common::Vec3f(matrix4x4[4], matrix4x4[5], -matrix4x4[6]),
Common::Vec3f(-matrix4x4[8], -matrix4x4[9], matrix4x4[10])};
} }
Common::Vec<f32, 3> MotionInput::GetAcceleration() const { Common::Vec3f MotionInput::GetAcceleration() const {
return accel; return accel;
} }
Common::Vec<f32, 3> MotionInput::GetGyroscope() const { Common::Vec3f MotionInput::GetGyroscope() const {
return gyro; return gyro;
} }
Common::Vec<f32, 3> MotionInput::GetGyroBias() const { Common::Vec3f MotionInput::GetGyroBias() const {
return gyro_bias; return gyro_bias;
} }
Common::Vec<f32, 4> MotionInput::GetQuaternion() const { Common::Quaternion<f32> MotionInput::GetQuaternion() const {
return quat; return quat;
} }
Common::Vec<f32, 3> MotionInput::GetRotations() const { Common::Vec3f MotionInput::GetRotations() const {
return rotations; return rotations;
} }
Common::Vec<f32, 3> MotionInput::GetEulerAngles() const { Common::Vec3f MotionInput::GetEulerAngles() const {
// roll (x-axis rotation) // roll (x-axis rotation)
const float sinr_cosp = 2 * (quat[3] * quat[0] + quat[1] * quat[2]); const float sinr_cosp = 2 * (quat.w * quat.xyz.x + quat.xyz.y * quat.xyz.z);
const float cosr_cosp = 1 - 2 * (quat[0] * quat[0] + quat[1] * quat[1]); const float cosr_cosp = 1 - 2 * (quat.xyz.x * quat.xyz.x + quat.xyz.y * quat.xyz.y);
// pitch (y-axis rotation) // pitch (y-axis rotation)
const float sinp = std::sqrt(1 + 2 * (quat[3] * quat[1] - quat[0] * quat[2])); const float sinp = std::sqrt(1 + 2 * (quat.w * quat.xyz.y - quat.xyz.x * quat.xyz.z));
const float cosp = std::sqrt(1 - 2 * (quat[3] * quat[1] - quat[0] * quat[2])); const float cosp = std::sqrt(1 - 2 * (quat.w * quat.xyz.y - quat.xyz.x * quat.xyz.z));
// yaw (z-axis rotation) // yaw (z-axis rotation)
const float siny_cosp = 2 * (quat[3] * quat[2] + quat[0] * quat[1]); const float siny_cosp = 2 * (quat.w * quat.xyz.z + quat.xyz.x * quat.xyz.y);
const float cosy_cosp = 1 - 2 * (quat[1] * quat[1] + quat[2] * quat[2]); const float cosy_cosp = 1 - 2 * (quat.xyz.y * quat.xyz.y + quat.xyz.z * quat.xyz.z);
return { return {
std::atan2(sinr_cosp, cosr_cosp), std::atan2(sinr_cosp, cosr_cosp),
@@ -285,13 +285,13 @@ void MotionInput::ResetOrientation() {
if (!reset_enabled || only_accelerometer) { if (!reset_enabled || only_accelerometer) {
return; return;
} }
if (!IsMoving(IsAtRestRelaxed) && accel[2] <= -0.9f) { if (!IsMoving(IsAtRestRelaxed) && accel.z <= -0.9f) {
++reset_counter; ++reset_counter;
if (reset_counter > 900) { if (reset_counter > 900) {
quat[3] = 0; quat.w = 0;
quat[0] = 0; quat.xyz[0] = 0;
quat[1] = 0; quat.xyz[1] = 0;
quat[2] = -1; quat.xyz[2] = -1;
SetOrientationFromAccelerometer(); SetOrientationFromAccelerometer();
integral_error = {}; integral_error = {};
reset_counter = 0; reset_counter = 0;
@@ -309,15 +309,15 @@ void MotionInput::SetOrientationFromAccelerometer() {
while (!IsCalibrated(0.01f) && ++iterations < 100) { while (!IsCalibrated(0.01f) && ++iterations < 100) {
// Short name local variable for readability // Short name local variable for readability
f32 q1 = quat[3]; f32 q1 = quat.w;
f32 q2 = quat[0]; f32 q2 = quat.xyz[0];
f32 q3 = quat[1]; f32 q3 = quat.xyz[1];
f32 q4 = quat[2]; f32 q4 = quat.xyz[2];
Common::Vec<f32, 3> rad_gyro; Common::Vec3f rad_gyro;
const f32 ax = -normal_accel[0]; const f32 ax = -normal_accel.x;
const f32 ay = normal_accel[1]; const f32 ay = normal_accel.y;
const f32 az = -normal_accel[2]; const f32 az = -normal_accel.z;
// Estimated direction of gravity // Estimated direction of gravity
const f32 vx = 2.0f * (q2 * q4 - q1 * q3); const f32 vx = 2.0f * (q2 * q4 - q1 * q3);
@@ -325,7 +325,7 @@ void MotionInput::SetOrientationFromAccelerometer() {
const f32 vz = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4; const f32 vz = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4;
// Error is cross product between estimated direction and measured direction of gravity // Error is cross product between estimated direction and measured direction of gravity
const Common::Vec<f32, 3> new_real_error = { const Common::Vec3f new_real_error = {
az * vx - ax * vz, az * vx - ax * vz,
ay * vz - az * vy, ay * vz - az * vy,
ax * vy - ay * vx, ax * vy - ay * vx,
@@ -338,9 +338,9 @@ void MotionInput::SetOrientationFromAccelerometer() {
rad_gyro += 5.0f * ki * integral_error; rad_gyro += 5.0f * ki * integral_error;
rad_gyro += 10.0f * kd * derivative_error; rad_gyro += 10.0f * kd * derivative_error;
const f32 gx = rad_gyro[1]; const f32 gx = rad_gyro.y;
const f32 gy = rad_gyro[0]; const f32 gy = rad_gyro.x;
const f32 gz = rad_gyro[2]; const f32 gz = rad_gyro.z;
// Integrate rate of change of quaternion // Integrate rate of change of quaternion
const f32 pa = q2; const f32 pa = q2;
@@ -351,10 +351,10 @@ void MotionInput::SetOrientationFromAccelerometer() {
q3 = pb + (q1 * gy - pa * gz + pc * gx) * (0.5f * sample_period); q3 = pb + (q1 * gy - pa * gz + pc * gx) * (0.5f * sample_period);
q4 = pc + (q1 * gz + pa * gy - pb * gx) * (0.5f * sample_period); q4 = pc + (q1 * gz + pa * gy - pb * gx) * (0.5f * sample_period);
quat[3] = q1; quat.w = q1;
quat[0] = q2; quat.xyz[0] = q2;
quat[1] = q3; quat.xyz[1] = q3;
quat[2] = q4; quat.xyz[2] = q4;
quat = quat.Normalized(); quat = quat.Normalized();
} }
} }
+21 -23
View File
@@ -1,12 +1,10 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once
#include "common/common_types.h" #include "common/common_types.h"
#include "common/quaternion.h"
#include "common/vector_math.h" #include "common/vector_math.h"
namespace Core::HID { namespace Core::HID {
@@ -36,11 +34,11 @@ public:
MotionInput& operator=(MotionInput&&) = default; MotionInput& operator=(MotionInput&&) = default;
void SetPID(f32 new_kp, f32 new_ki, f32 new_kd); void SetPID(f32 new_kp, f32 new_ki, f32 new_kd);
void SetAcceleration(const Common::Vec<f32, 3>& acceleration); void SetAcceleration(const Common::Vec3f& acceleration);
void SetGyroscope(const Common::Vec<f32, 3>& gyroscope); void SetGyroscope(const Common::Vec3f& gyroscope);
void SetQuaternion(const Common::Vec<f32, 4>& quaternion); void SetQuaternion(const Common::Quaternion<f32>& quaternion);
void SetEulerAngles(const Common::Vec<f32, 3>& euler_angles); void SetEulerAngles(const Common::Vec3f& euler_angles);
void SetGyroBias(const Common::Vec<f32, 3>& bias); void SetGyroBias(const Common::Vec3f& bias);
void SetGyroThreshold(f32 threshold); void SetGyroThreshold(f32 threshold);
/// Applies a modifier on top of the normal gyro threshold /// Applies a modifier on top of the normal gyro threshold
@@ -55,13 +53,13 @@ public:
void Calibrate(); void Calibrate();
[[nodiscard]] std::array<Common::Vec<f32, 3>, 3> GetOrientation() const; [[nodiscard]] std::array<Common::Vec3f, 3> GetOrientation() const;
[[nodiscard]] Common::Vec<f32, 3> GetAcceleration() const; [[nodiscard]] Common::Vec3f GetAcceleration() const;
[[nodiscard]] Common::Vec<f32, 3> GetGyroscope() const; [[nodiscard]] Common::Vec3f GetGyroscope() const;
[[nodiscard]] Common::Vec<f32, 3> GetGyroBias() const; [[nodiscard]] Common::Vec3f GetGyroBias() const;
[[nodiscard]] Common::Vec<f32, 3> GetRotations() const; [[nodiscard]] Common::Vec3f GetRotations() const;
[[nodiscard]] Common::Vec<f32, 4> GetQuaternion() const; [[nodiscard]] Common::Quaternion<f32> GetQuaternion() const;
[[nodiscard]] Common::Vec<f32, 3> GetEulerAngles() const; [[nodiscard]] Common::Vec3f GetEulerAngles() const;
[[nodiscard]] bool IsMoving(f32 sensitivity) const; [[nodiscard]] bool IsMoving(f32 sensitivity) const;
[[nodiscard]] bool IsCalibrated(f32 sensitivity) const; [[nodiscard]] bool IsCalibrated(f32 sensitivity) const;
@@ -77,24 +75,24 @@ private:
f32 kd; f32 kd;
// PID errors // PID errors
Common::Vec<f32, 3> real_error; Common::Vec3f real_error;
Common::Vec<f32, 3> integral_error; Common::Vec3f integral_error;
Common::Vec<f32, 3> derivative_error; Common::Vec3f derivative_error;
// Quaternion containing the device orientation // Quaternion containing the device orientation
Common::Vec<f32, 4> quat; Common::Quaternion<f32> quat;
// Number of full rotations in each axis // Number of full rotations in each axis
Common::Vec<f32, 3> rotations; Common::Vec3f rotations;
// Acceleration vector measurement in G force // Acceleration vector measurement in G force
Common::Vec<f32, 3> accel; Common::Vec3f accel;
// Gyroscope vector measurement in radians/s. // Gyroscope vector measurement in radians/s.
Common::Vec<f32, 3> gyro; Common::Vec3f gyro;
// Vector to be subtracted from gyro measurements // Vector to be subtracted from gyro measurements
Common::Vec<f32, 3> gyro_bias; Common::Vec3f gyro_bias;
// Minimum gyro amplitude to detect if the device is moving // Minimum gyro amplitude to detect if the device is moving
f32 gyro_threshold = 0.0f; f32 gyro_threshold = 0.0f;
+4 -4
View File
@@ -609,10 +609,10 @@ static_assert(sizeof(SixAxisSensorAttribute) == 4, "SixAxisSensorAttribute is an
struct SixAxisSensorState { struct SixAxisSensorState {
s64 delta_time{}; s64 delta_time{};
s64 sampling_number{}; s64 sampling_number{};
Common::Vec<f32, 3> accel{}; Common::Vec3f accel{};
Common::Vec<f32, 3> gyro{}; Common::Vec3f gyro{};
Common::Vec<f32, 3> rotation{}; Common::Vec3f rotation{};
std::array<Common::Vec<f32, 3>, 3> orientation{}; std::array<Common::Vec3f, 3> orientation{};
SixAxisSensorAttribute attribute{}; SixAxisSensorAttribute attribute{};
INSERT_PADDING_BYTES(4); // Reserved INSERT_PADDING_BYTES(4); // Reserved
}; };

Some files were not shown because too many files have changed in this diff Show More