mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-28 17:45:39 +00:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d683010a8b | |||
| ec254a3adb | |||
| 5e10da3cac | |||
| 5322bce4b8 | |||
| 276dcdd8ea | |||
| 59254cd1e7 | |||
| 9a3af3a6a3 | |||
| b473c18d6e | |||
| c984c387d7 | |||
| 5856beac54 | |||
| 16e7e034d7 |
+1
-2
@@ -69,8 +69,7 @@ enum class IntSetting(override val key: String) : AbstractIntSetting {
|
||||
MY_PAGE_APPLET("my_page_applet_mode"),
|
||||
INPUT_OVERLAY_AUTO_HIDE("input_overlay_auto_hide"),
|
||||
OVERLAY_GRID_SIZE("overlay_grid_size"),
|
||||
GPU_LOG_RING_BUFFER_SIZE("gpu_log_ring_buffer_size"),
|
||||
PRODUCT_MODEL("product_model")
|
||||
GPU_LOG_RING_BUFFER_SIZE("gpu_log_ring_buffer_size")
|
||||
;
|
||||
|
||||
override fun getInt(needsGlobal: Boolean): Int = NativeConfig.getInt(key, needsGlobal)
|
||||
|
||||
-10
@@ -965,16 +965,6 @@ abstract class SettingsItem(
|
||||
max = 4096
|
||||
)
|
||||
)
|
||||
put(
|
||||
SpinBoxSetting(
|
||||
IntSetting.PRODUCT_MODEL,
|
||||
titleId = R.string.product_model,
|
||||
descriptionId = R.string.product_model_description,
|
||||
valueHint = R.string.product_model,
|
||||
min = 1,
|
||||
max = 2
|
||||
)
|
||||
)
|
||||
|
||||
val fastmem = object : AbstractBooleanSetting {
|
||||
override fun getBoolean(needsGlobal: Boolean): Boolean =
|
||||
|
||||
-1
@@ -1265,7 +1265,6 @@ class SettingsFragmentPresenter(
|
||||
add(BooleanSetting.GPU_LOG_MEMORY_TRACKING.key)
|
||||
add(BooleanSetting.GPU_LOG_DRIVER_DEBUG.key)
|
||||
add(IntSetting.GPU_LOG_RING_BUFFER_SIZE.key)
|
||||
add(IntSetting.PRODUCT_MODEL.key)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -133,8 +133,11 @@ class GamePropertiesFragment : Fragment() {
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
val isChangingConfigurations = activity?.isChangingConfigurations == true
|
||||
super.onDestroy()
|
||||
gamesViewModel.reloadGames(true)
|
||||
if (!isChangingConfigurations) {
|
||||
gamesViewModel.reloadGames(true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getPlayTime() {
|
||||
|
||||
@@ -473,8 +473,6 @@
|
||||
<string name="renderer_accuracy_description">يتحكم في وضع محاكاة وحدة معالجة الرسومات. تعمل معظم الألعاب بشكل جيد مع وضعي سريع أو متوازن، لكن الوضع الدقيق لا يزال مطلوبًا لبعض الألعاب. تميل الجسيمات إلى العرض بشكل صحيح فقط عند استخدام الوضع الدقيق.</string>
|
||||
<string name="dma_accuracy">دقة DMA</string>
|
||||
<string name="dma_accuracy_description">يتحكم في دقة DMA. يمكن أن تؤدي الدقة الآمنة إلى حل المشكلات في بعض الألعاب، ولكنها قد تؤثر أيضًا على الأداء في بعض الحالات. إذا لم تكن متأكدًا، فاترك هذا الخيار على الإعداد الافتراضي.</string>
|
||||
<string name="frame_pacing_mode">وضع توقيت الإطارات</string>
|
||||
<string name="frame_pacing_mode_description">يتحكم في كيفية إدارة المحاكي لسرعة الإطارات لتقليل التقطع وجعل معدل الإطارات أكثر سلاسة واتساقًا.</string>
|
||||
<string name="anisotropic_filtering">تصفية متباينة الخواص</string>
|
||||
<string name="anisotropic_filtering_description">يحسن جودة الأنسجة عند عرضها بزوايا مائلة</string>
|
||||
<string name="vram_usage_mode">وضع استخدام ذاكرة VRAM</string>
|
||||
@@ -999,13 +997,6 @@
|
||||
<string name="dma_accuracy_unsafe">غير آمن</string>
|
||||
<string name="dma_accuracy_safe">آمن</string>
|
||||
|
||||
<!-- Frame Pacing Mode -->
|
||||
<string name="frame_pacing_mode_target_Auto">تلقائي</string>
|
||||
<string name="frame_pacing_mode_target_30">30 إطارًا في الثانية</string>
|
||||
<string name="frame_pacing_mode_target_60">60 إطارًا في الثانية</string>
|
||||
<string name="frame_pacing_mode_target_90">90 إطارًا في الثانية</string>
|
||||
<string name="frame_pacing_mode_target_120">120 إطارًا في الثانية</string>
|
||||
|
||||
<!-- ASTC Recompression Method Choices -->
|
||||
<string name="astc_recompression_uncompressed">غير مضغوط</string>
|
||||
<!-- ASTC Recompression Method Choices -->
|
||||
|
||||
@@ -452,8 +452,6 @@
|
||||
<string name="renderer_accuracy_description">Určuje režim emulovaného GPU. Většina her běží bez problémů v rychlém, nebo vyváženém režimu, ale některé stále vyžadují přesný režim. Částicové efekty se většinou zobrazují korektně pouze v přesném režimu. </string>
|
||||
<string name="dma_accuracy">Přesnost DMA</string>
|
||||
<string name="dma_accuracy_description">Ovládá přesnost DMA. Bezpečná přesnost může vyřešit problémy v některých hrách, ale v některých případech může také ovlivnit výkon. Pokud si nejste jisti, použijte výchozí nastavení.</string>
|
||||
<string name="frame_pacing_mode">Režim Framepacingu</string>
|
||||
<string name="frame_pacing_mode_description">Řídí způsob jakým emulátor spravuje časování snímku aby snížil trhání a zlepšení plynulost a stabilitu snímkové frekvence.</string>
|
||||
<string name="anisotropic_filtering">Anizotropní filtrování</string>
|
||||
<string name="anisotropic_filtering_description">Zlepšuje kvalitu textur při pohledu pod úhlem</string>
|
||||
<string name="vram_usage_mode">Režim využití VRAM</string>
|
||||
|
||||
@@ -467,8 +467,6 @@
|
||||
<string name="renderer_accuracy_description">Controla el modo de la emulación de la GPU. La mayoría de los juegos se renderizan correctamente en los modos Rápido o Equilibrado, pero algunos requieren Preciso. Las partículas tienden a renderizarse correctamente solo con el modo Preciso.</string>
|
||||
<string name="dma_accuracy">Precisión de DMA</string>
|
||||
<string name="dma_accuracy_description">Controla la precisión de DMA. La precisión segura puede solucionar problemas en algunos juegos, pero también puede afectar al rendimiento en algunos casos. Si no está seguro, déjelo en Predeterminado.</string>
|
||||
<string name="frame_pacing_mode">Modo de ritmo de fotogramas</string>
|
||||
<string name="frame_pacing_mode_description">Controla cómo el emulador gestiona el ritmo de los fotogramas para reducir los tirones y hacer que la velocidad de los fotogramas sea más suave y consistente.</string>
|
||||
<string name="anisotropic_filtering">Filtrado anisotrópico</string>
|
||||
<string name="anisotropic_filtering_description">Mejora la calidad de las texturas al ser observadas desde ángulos oblicuos</string>
|
||||
<string name="vram_usage_mode">Modo de uso de VRAM</string>
|
||||
@@ -993,13 +991,6 @@
|
||||
<string name="dma_accuracy_unsafe">Inseguro</string>
|
||||
<string name="dma_accuracy_safe">Seguro</string>
|
||||
|
||||
<!-- Frame Pacing Mode -->
|
||||
<string name="frame_pacing_mode_target_Auto">Automático</string>
|
||||
<string name="frame_pacing_mode_target_30">30 FPS</string>
|
||||
<string name="frame_pacing_mode_target_60">60 FPS</string>
|
||||
<string name="frame_pacing_mode_target_90">90 FPS</string>
|
||||
<string name="frame_pacing_mode_target_120">120 FPS</string>
|
||||
|
||||
<!-- ASTC Recompression Method Choices -->
|
||||
<string name="astc_recompression_uncompressed">Sin compresión</string>
|
||||
<!-- ASTC Recompression Method Choices -->
|
||||
|
||||
@@ -468,8 +468,6 @@
|
||||
<string name="renderer_accuracy_description">Управляет режимом эмуляции графического процессора. Большинство игр нормально отображаются в режимах «Быстрый» или «Сбалансированный», но для некоторых требуется режим «Точный». Частицы обычно корректно отображаются только в режиме «Точный».</string>
|
||||
<string name="dma_accuracy">Точность DMA</string>
|
||||
<string name="dma_accuracy_description">Управляет точностью DMA. Безопасная точность может исправить проблемы в некоторых играх, но в некоторых случаях также может повлиять на производительность. Если не уверены, оставьте значение По умолчанию.</string>
|
||||
<string name="frame_pacing_mode">Режим синхронизации кадров</string>
|
||||
<string name="frame_pacing_mode_description">Управляет синхронизацией кадров в эмуляторе для уменьшения рывков и обеспечения более плавной и стабильной частоты кадров.</string>
|
||||
<string name="anisotropic_filtering">Анизотропная фильтрация</string>
|
||||
<string name="anisotropic_filtering_description">Улучшает качество текстур под углом</string>
|
||||
<string name="vram_usage_mode">Режим VRAM</string>
|
||||
@@ -994,13 +992,6 @@
|
||||
<string name="dma_accuracy_unsafe">Небезопасно</string>
|
||||
<string name="dma_accuracy_safe">Безопасный</string>
|
||||
|
||||
<!-- Frame Pacing Mode -->
|
||||
<string name="frame_pacing_mode_target_Auto">Авто</string>
|
||||
<string name="frame_pacing_mode_target_30">30 FPS</string>
|
||||
<string name="frame_pacing_mode_target_60">60 FPS</string>
|
||||
<string name="frame_pacing_mode_target_90">90 FPS</string>
|
||||
<string name="frame_pacing_mode_target_120">120 FPS</string>
|
||||
|
||||
<!-- ASTC Recompression Method Choices -->
|
||||
<string name="astc_recompression_uncompressed">Без сжатия</string>
|
||||
<!-- ASTC Recompression Method Choices -->
|
||||
|
||||
@@ -469,8 +469,6 @@
|
||||
<string name="renderer_accuracy_description">Керує режимом емуляції ГП. Більшість ігор добре візуалізуються з режимами «Швидко» або «Збалансовано», але деякі ігри можуть потребувати режиму «Точно». Частинки зазвичай правильно візуалізуються лише з режимом «Точно».</string>
|
||||
<string name="dma_accuracy">Точність DMA</string>
|
||||
<string name="dma_accuracy_description">Керує точністю DMA. Безпечна точність може виправити проблеми в деяких іграх, але в деяких випадках також може вплинути на продуктивність. Якщо не впевнені, залиште це значення за замовчуванням.</string>
|
||||
<string name="frame_pacing_mode">Режим виведення кадрів</string>
|
||||
<string name="frame_pacing_mode_description">Керує тим, як емулятор виконує виведення кадрів, щоб зменшити затримки й забезпечити плавнішу й стабільнішу частоту кадрів.</string>
|
||||
<string name="anisotropic_filtering">Анізотропне фільтрування</string>
|
||||
<string name="anisotropic_filtering_description">Покращує якість текстур під кутом.</string>
|
||||
<string name="vram_usage_mode">Режим використання VRAM</string>
|
||||
@@ -995,13 +993,6 @@
|
||||
<string name="dma_accuracy_unsafe">Небезпечно</string>
|
||||
<string name="dma_accuracy_safe">Безпечно</string>
|
||||
|
||||
<!-- Frame Pacing Mode -->
|
||||
<string name="frame_pacing_mode_target_Auto">Автоматично</string>
|
||||
<string name="frame_pacing_mode_target_30">30 к/с</string>
|
||||
<string name="frame_pacing_mode_target_60">60 к/с</string>
|
||||
<string name="frame_pacing_mode_target_90">90 к/с</string>
|
||||
<string name="frame_pacing_mode_target_120">120 к/с</string>
|
||||
|
||||
<!-- ASTC Recompression Method Choices -->
|
||||
<string name="astc_recompression_uncompressed">Без стиснення</string>
|
||||
<!-- ASTC Recompression Method Choices -->
|
||||
|
||||
@@ -462,8 +462,6 @@
|
||||
<string name="renderer_accuracy_description">控制 GPU 模拟的精确度。大部分游戏在性能或平衡模式下可以正常渲染,但部分游戏需要设置为精确。粒子效果通常只有在精确模式下才能正确显示。</string>
|
||||
<string name="dma_accuracy">DMA 精度</string>
|
||||
<string name="dma_accuracy_description">控制 DMA 精度。安全精度可以修复某些游戏中的问题,但在某些情况下也可能影响性能。如果不确定,请保留为“默认”。</string>
|
||||
<string name="frame_pacing_mode">帧同步模式</string>
|
||||
<string name="frame_pacing_mode_description">控制模拟器如何管理帧同步,以减少卡顿,使帧率表现更加平稳顺滑。</string>
|
||||
<string name="anisotropic_filtering">各向异性过滤</string>
|
||||
<string name="anisotropic_filtering_description">提高斜角的纹理质量</string>
|
||||
<string name="vram_usage_mode">显存使用模式</string>
|
||||
@@ -988,13 +986,6 @@
|
||||
<string name="dma_accuracy_unsafe">不安全</string>
|
||||
<string name="dma_accuracy_safe">安全</string>
|
||||
|
||||
<!-- Frame Pacing Mode -->
|
||||
<string name="frame_pacing_mode_target_Auto">自动</string>
|
||||
<string name="frame_pacing_mode_target_30">30 FPS</string>
|
||||
<string name="frame_pacing_mode_target_60">60 FPS</string>
|
||||
<string name="frame_pacing_mode_target_90">90 FPS</string>
|
||||
<string name="frame_pacing_mode_target_120">120 FPS</string>
|
||||
|
||||
<!-- ASTC Recompression Method Choices -->
|
||||
<string name="astc_recompression_uncompressed">不压缩</string>
|
||||
<!-- ASTC Recompression Method Choices -->
|
||||
|
||||
@@ -533,20 +533,6 @@
|
||||
<item>2</item>
|
||||
</integer-array>
|
||||
|
||||
<string-array name="framePacingModeNames">
|
||||
<item>@string/frame_pacing_mode_target_Auto</item>
|
||||
<item>@string/frame_pacing_mode_target_30</item>
|
||||
<item>@string/frame_pacing_mode_target_60</item>
|
||||
<item>@string/frame_pacing_mode_target_90</item>
|
||||
<item>@string/frame_pacing_mode_target_120</item>
|
||||
</string-array>
|
||||
<integer-array name="framePacingModeValues">
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
<item>3</item>
|
||||
<item>4</item>
|
||||
</integer-array>
|
||||
|
||||
<string-array name="appletEntries">
|
||||
<item>@string/applet_hle</item>
|
||||
|
||||
@@ -479,8 +479,6 @@
|
||||
<string name="renderer_accuracy_description">Controls the GPU emulation mode. Most games render fine with Fast or Balanced modes, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode.</string>
|
||||
<string name="dma_accuracy">DMA Accuracy</string>
|
||||
<string name="dma_accuracy_description">Controls the DMA precision accuracy. Safe precision can fix issues in some games, but it can also impact performance in some cases. If unsure, leave this on Default.</string>
|
||||
<string name="frame_pacing_mode">Frame Pacing Mode</string>
|
||||
<string name="frame_pacing_mode_description">Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent.</string>
|
||||
<string name="anisotropic_filtering">Anisotropic filtering</string>
|
||||
<string name="anisotropic_filtering_description">Improves the quality of textures when viewed at oblique angles</string>
|
||||
<string name="vram_usage_mode">VRAM Usage Mode</string>
|
||||
@@ -586,9 +584,6 @@
|
||||
<string name="gpu_log_ring_buffer_size_description">Number of recent Vulkan calls to track (default: 512)</string>
|
||||
<string name="gpu_log_ring_buffer_size_hint">64 to 4096 entries</string>
|
||||
|
||||
<string name="product_model">Product model</string>
|
||||
<string name="product_model_description">Reported product model by HLE firmware</string>
|
||||
|
||||
<string name="general">General</string>
|
||||
|
||||
<!-- Audio settings strings -->
|
||||
@@ -1041,13 +1036,6 @@
|
||||
<string name="dma_accuracy_unsafe">Unsafe</string>
|
||||
<string name="dma_accuracy_safe">Safe</string>
|
||||
|
||||
<!-- Frame Pacing Mode -->
|
||||
<string name="frame_pacing_mode_target_Auto">Auto</string>
|
||||
<string name="frame_pacing_mode_target_30">30 FPS</string>
|
||||
<string name="frame_pacing_mode_target_60">60 FPS</string>
|
||||
<string name="frame_pacing_mode_target_90">90 FPS</string>
|
||||
<string name="frame_pacing_mode_target_120">120 FPS</string>
|
||||
|
||||
<!-- ASTC Decoding Method Choices -->
|
||||
<string name="accelerate_astc_cpu" translatable="false">CPU</string>
|
||||
<string name="accelerate_astc_gpu" translatable="false">GPU</string>
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -8,10 +11,11 @@
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
AudioCore::AudioCore(Core::System& system) : audio_manager{std::make_unique<AudioManager>()} {
|
||||
AudioCore::AudioCore(Core::System& system) {
|
||||
audio_manager.emplace();
|
||||
CreateSinks();
|
||||
// Must be created after the sinks
|
||||
adsp = std::make_unique<ADSP::ADSP>(system, *output_sink);
|
||||
adsp.emplace(system, *output_sink);
|
||||
}
|
||||
|
||||
AudioCore ::~AudioCore() {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -15,10 +18,7 @@ class System;
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
class AudioManager;
|
||||
/**
|
||||
* Main audio class, stored inside the core, and holding the audio manager, all sinks, and the ADSP.
|
||||
*/
|
||||
/// @brief Main audio class, stored inside the core, and holding the audio manager, all sinks, and the ADSP.
|
||||
class AudioCore {
|
||||
public:
|
||||
explicit AudioCore(Core::System& system);
|
||||
@@ -50,27 +50,22 @@ public:
|
||||
*/
|
||||
Sink::Sink& GetInputSink();
|
||||
|
||||
/**
|
||||
* Get the ADSP.
|
||||
*
|
||||
* @return Ref to the ADSP.
|
||||
*/
|
||||
/// @brief Get the ADSP.
|
||||
/// @return Ref to the ADSP.
|
||||
ADSP::ADSP& ADSP();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Create the sinks on startup.
|
||||
*/
|
||||
/// @brief Create the sinks on startup.
|
||||
void CreateSinks();
|
||||
|
||||
/// Main audio manager for audio in/out
|
||||
std::unique_ptr<AudioManager> audio_manager;
|
||||
std::optional<AudioManager> audio_manager;
|
||||
/// Sink used for audio renderer and audio out
|
||||
std::unique_ptr<Sink::Sink> output_sink;
|
||||
/// Sink used for audio input
|
||||
std::unique_ptr<Sink::Sink> input_sink;
|
||||
/// The ADSP in the sysmodule
|
||||
std::unique_ptr<ADSP::ADSP> adsp;
|
||||
std::optional<ADSP::ADSP> adsp;
|
||||
};
|
||||
|
||||
} // namespace AudioCore
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -41,8 +44,7 @@ void Manager::ReleaseSessionId(const size_t session_id) {
|
||||
Result Manager::LinkToManager() {
|
||||
std::scoped_lock l{mutex};
|
||||
if (!linked_to_manager) {
|
||||
AudioManager& manager{system.AudioCore().GetAudioManager()};
|
||||
manager.SetInManager(std::bind(&Manager::BufferReleaseAndRegister, this));
|
||||
system.AudioCore().GetAudioManager().SetInManager(std::bind(&Manager::BufferReleaseAndRegister, this));
|
||||
linked_to_manager = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,81 +1,77 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "audio_core/audio_manager.h"
|
||||
#include "common/thread.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/audio/errors.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
AudioManager::AudioManager() {
|
||||
thread = std::jthread([this]() { ThreadFunc(); });
|
||||
thread = std::jthread([this](std::stop_token stop_token) {
|
||||
Common::SetCurrentThreadName("AudioManager");
|
||||
std::unique_lock l{events.GetAudioEventLock()};
|
||||
events.ClearEvents();
|
||||
while (!stop_token.stop_requested()) {
|
||||
const auto timed_out{events.Wait(l, std::chrono::seconds(2))};
|
||||
if (events.CheckAudioEventSet(Event::Type::Max)) {
|
||||
break;
|
||||
}
|
||||
for (size_t i = 0; i < buffer_events.size(); i++) {
|
||||
const auto event_type = Event::Type(i);
|
||||
if (events.CheckAudioEventSet(event_type) || timed_out) {
|
||||
if (buffer_events[i]) {
|
||||
buffer_events[i]();
|
||||
}
|
||||
}
|
||||
events.SetAudioEvent(event_type, false);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void AudioManager::Shutdown() {
|
||||
running = false;
|
||||
events.SetAudioEvent(Event::Type::Max, true);
|
||||
thread.join();
|
||||
if (thread.joinable()) {
|
||||
thread.request_stop();
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
Result AudioManager::SetOutManager(BufferEventFunc buffer_func) {
|
||||
if (!running) {
|
||||
return Service::Audio::ResultOperationFailed;
|
||||
if (thread.joinable()) {
|
||||
std::scoped_lock l{lock};
|
||||
const auto index{events.GetManagerIndex(Event::Type::AudioOutManager)};
|
||||
if (buffer_events[index] == nullptr) {
|
||||
buffer_events[index] = std::move(buffer_func);
|
||||
needs_update = true;
|
||||
events.SetAudioEvent(Event::Type::AudioOutManager, true);
|
||||
}
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
std::scoped_lock l{lock};
|
||||
|
||||
const auto index{events.GetManagerIndex(Event::Type::AudioOutManager)};
|
||||
if (buffer_events[index] == nullptr) {
|
||||
buffer_events[index] = std::move(buffer_func);
|
||||
needs_update = true;
|
||||
events.SetAudioEvent(Event::Type::AudioOutManager, true);
|
||||
}
|
||||
return ResultSuccess;
|
||||
return Service::Audio::ResultOperationFailed;
|
||||
}
|
||||
|
||||
Result AudioManager::SetInManager(BufferEventFunc buffer_func) {
|
||||
if (!running) {
|
||||
return Service::Audio::ResultOperationFailed;
|
||||
if (thread.joinable()) {
|
||||
std::scoped_lock l{lock};
|
||||
const auto index{events.GetManagerIndex(Event::Type::AudioInManager)};
|
||||
if (buffer_events[index] == nullptr) {
|
||||
buffer_events[index] = std::move(buffer_func);
|
||||
needs_update = true;
|
||||
events.SetAudioEvent(Event::Type::AudioInManager, true);
|
||||
}
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
std::scoped_lock l{lock};
|
||||
|
||||
const auto index{events.GetManagerIndex(Event::Type::AudioInManager)};
|
||||
if (buffer_events[index] == nullptr) {
|
||||
buffer_events[index] = std::move(buffer_func);
|
||||
needs_update = true;
|
||||
events.SetAudioEvent(Event::Type::AudioInManager, true);
|
||||
}
|
||||
return ResultSuccess;
|
||||
return Service::Audio::ResultOperationFailed;
|
||||
}
|
||||
|
||||
void AudioManager::SetEvent(const Event::Type type, const bool signalled) {
|
||||
events.SetAudioEvent(type, signalled);
|
||||
}
|
||||
|
||||
void AudioManager::ThreadFunc() {
|
||||
std::unique_lock l{events.GetAudioEventLock()};
|
||||
events.ClearEvents();
|
||||
running = true;
|
||||
|
||||
while (running) {
|
||||
const auto timed_out{events.Wait(l, std::chrono::seconds(2))};
|
||||
|
||||
if (events.CheckAudioEventSet(Event::Type::Max)) {
|
||||
break;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < buffer_events.size(); i++) {
|
||||
const auto event_type = static_cast<Event::Type>(i);
|
||||
|
||||
if (events.CheckAudioEventSet(event_type) || timed_out) {
|
||||
if (buffer_events[i]) {
|
||||
buffer_events[i]();
|
||||
}
|
||||
}
|
||||
events.SetAudioEvent(event_type, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace AudioCore
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -66,13 +69,6 @@ public:
|
||||
void SetEvent(Event::Type type, bool signalled);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Main thread, waiting on a manager signal and calling the registered function.
|
||||
*/
|
||||
void ThreadFunc();
|
||||
|
||||
/// Is the main thread running?
|
||||
std::atomic<bool> running{};
|
||||
/// Unused
|
||||
bool needs_update{};
|
||||
/// Events to be set and signalled
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -40,8 +43,7 @@ void Manager::ReleaseSessionId(const size_t session_id) {
|
||||
Result Manager::LinkToManager() {
|
||||
std::scoped_lock l{mutex};
|
||||
if (!linked_to_manager) {
|
||||
AudioManager& manager{system.AudioCore().GetAudioManager()};
|
||||
manager.SetOutManager(std::bind(&Manager::BufferReleaseAndRegister, this));
|
||||
system.AudioCore().GetAudioManager().SetOutManager(std::bind(&Manager::BufferReleaseAndRegister, this));
|
||||
linked_to_manager = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
#include <sys/random.h>
|
||||
#include <mach/vm_map.h>
|
||||
#include <mach/mach.h>
|
||||
#elif defined(__FreeBSD__)
|
||||
#include <sys/shm.h>
|
||||
#endif
|
||||
|
||||
// FreeBSD
|
||||
@@ -503,8 +505,7 @@ public:
|
||||
fd = shm_open_anon(O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, 0600);
|
||||
#elif defined(__OpenBSD__)
|
||||
fd = shm_open_anon(O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, 0600);
|
||||
#elif defined(__FreeBSD__) && __FreeBSD__ < 13
|
||||
// XXX Drop after FreeBSD 12.* reaches EOL on 2024-06-30
|
||||
#elif defined(__FreeBSD__)
|
||||
fd = shm_open(SHM_ANON, O_RDWR, 0600);
|
||||
#elif defined(__APPLE__)
|
||||
// macOS doesn't have memfd_create, use anonymous temporary file
|
||||
@@ -571,9 +572,9 @@ public:
|
||||
if (True(perms & MemoryPermission::Execute))
|
||||
prot_flags |= PROT_EXEC;
|
||||
#endif
|
||||
int flags = (fd > 0 ? MAP_SHARED : MAP_PRIVATE) | MAP_FIXED;
|
||||
int flags = (fd >= 0 ? MAP_SHARED : MAP_PRIVATE) | MAP_FIXED;
|
||||
void* ret = mmap(virtual_base + virtual_offset, length, prot_flags, flags, fd, host_offset);
|
||||
ASSERT_MSG(ret != MAP_FAILED, "mmap: {}", strerror(errno));
|
||||
ASSERT_MSG(ret != MAP_FAILED, "mmap: {} {}", strerror(errno), fd);
|
||||
}
|
||||
|
||||
void Unmap(size_t virtual_offset, size_t length) {
|
||||
@@ -587,9 +588,8 @@ public:
|
||||
auto [merged_pointer, merged_size] =
|
||||
free_manager.FreeBlock(virtual_base + virtual_offset, length);
|
||||
|
||||
void* ret = mmap(merged_pointer, merged_size, PROT_NONE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
|
||||
ASSERT_MSG(ret != MAP_FAILED, "mmap failed: {}", strerror(errno));
|
||||
void* ret = mmap(merged_pointer, merged_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
|
||||
ASSERT_MSG(ret != MAP_FAILED, "mmap: {}", strerror(errno));
|
||||
}
|
||||
|
||||
void Protect(size_t virtual_offset, size_t length, bool read, bool write, bool execute) {
|
||||
|
||||
@@ -186,7 +186,7 @@ bool IsFastmemEnabled() {
|
||||
// Only 4kb systems support host MMU right now
|
||||
// TODO: Support this
|
||||
return getpagesize() == 4096;
|
||||
#elif !defined(__APPLE__) && !defined(__ANDROID__) && !defined(_WIN32) && !defined(__linux__)
|
||||
#elif !defined(__APPLE__) && !defined(__ANDROID__) && !defined(_WIN32) && !defined(__linux__) && !defined(__FreeBSD__)
|
||||
return false;
|
||||
#else
|
||||
return true;
|
||||
|
||||
@@ -640,7 +640,6 @@ struct Values {
|
||||
SwitchableSetting<TimeZone, true> time_zone_index{linkage, TimeZone::Auto, "time_zone_index", Category::System};
|
||||
Setting<u32> serial_battery{linkage, 0, "serial_battery", Category::System};
|
||||
Setting<u32> serial_unit{linkage, 0, "serial_unit", Category::System};
|
||||
Setting<u32> product_model{linkage, 1, "product_model", Category::System};
|
||||
// Measured in seconds since epoch
|
||||
SwitchableSetting<bool> custom_rtc_enabled{linkage, false, "custom_rtc_enabled", Category::System, Specialization::Paired, true, true};
|
||||
SwitchableSetting<s64> custom_rtc{
|
||||
|
||||
@@ -186,7 +186,7 @@ void ArmDynarmic32::MakeJit(Common::PageTable* page_table) {
|
||||
config.only_detect_misalignment_via_page_table_on_page_boundary = true;
|
||||
|
||||
config.fastmem_pointer = page_table->fastmem_arena ?
|
||||
std::optional<uintptr_t>{reinterpret_cast<uintptr_t>(page_table->fastmem_arena)} :
|
||||
std::optional<uintptr_t>{uintptr_t(page_table->fastmem_arena)} :
|
||||
std::nullopt;
|
||||
|
||||
config.fastmem_exclusive_access = config.fastmem_pointer != std::nullopt;
|
||||
@@ -286,10 +286,6 @@ void ArmDynarmic32::MakeJit(Common::PageTable* page_table) {
|
||||
// Curated optimizations
|
||||
case Settings::CpuAccuracy::Auto:
|
||||
config.unsafe_optimizations = true;
|
||||
#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__sun__) || defined(__HAIKU__) || defined(__DragonFly__) || defined(__NetBSD__)
|
||||
config.fastmem_pointer = std::nullopt;
|
||||
config.fastmem_exclusive_access = false;
|
||||
#endif
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
|
||||
@@ -304,6 +300,10 @@ void ArmDynarmic32::MakeJit(Common::PageTable* page_table) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (!Settings::IsFastmemEnabled()) {
|
||||
config.fastmem_pointer = std::nullopt;
|
||||
config.fastmem_exclusive_access = false;
|
||||
}
|
||||
m_jit.emplace(config);
|
||||
}
|
||||
|
||||
|
||||
@@ -338,10 +338,6 @@ void ArmDynarmic64::MakeJit(Common::PageTable* page_table, std::size_t address_s
|
||||
// Safe optimisations
|
||||
case Settings::CpuAccuracy::Auto:
|
||||
config.unsafe_optimizations = true;
|
||||
#if !defined(__APPLE__) && !defined(__linux__) && !defined(__ANDROID__) && !defined(_WIN32)
|
||||
config.fastmem_pointer = std::nullopt;
|
||||
config.fastmem_exclusive_access = false;
|
||||
#endif
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
|
||||
config.fastmem_address_space_bits = 64;
|
||||
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
|
||||
@@ -355,6 +351,10 @@ void ArmDynarmic64::MakeJit(Common::PageTable* page_table, std::size_t address_s
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (!Settings::IsFastmemEnabled()) {
|
||||
config.fastmem_pointer = std::nullopt;
|
||||
config.fastmem_exclusive_access = false;
|
||||
}
|
||||
m_jit.emplace(config);
|
||||
}
|
||||
|
||||
|
||||
@@ -773,7 +773,11 @@ std::optional<u64> MatchAndExecuteOneInstruction(Core::Memory::Memory& memory, m
|
||||
bool was_executed = false;
|
||||
|
||||
auto decoder = Dynarmic::A64::Decode<VisitorBase>(instruction);
|
||||
was_executed = decoder.get().call(visitor, instruction);
|
||||
if (decoder) {
|
||||
was_executed = decoder->get().call(visitor, instruction);
|
||||
} else {
|
||||
was_executed = false;
|
||||
}
|
||||
return was_executed ? std::optional<u64>(pc + 4) : std::nullopt;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -347,7 +347,7 @@ struct System::Impl {
|
||||
|
||||
// Register with applet manager
|
||||
// All threads are started, begin main process execution, now that we're in the clear
|
||||
applet_manager.CreateAndInsertByFrontendAppletParameters(std::move(process), params);
|
||||
applet_manager.CreateAndInsertByFrontendAppletParameters(std::make_unique<Service::Process>(*std::move(process)), params);
|
||||
|
||||
if (Settings::values.gamecard_inserted) {
|
||||
if (Settings::values.gamecard_current_game) {
|
||||
|
||||
@@ -268,7 +268,7 @@ void AppletManager::SetWindowSystem(WindowSystem* window_system) {
|
||||
|
||||
if (Settings::values.enable_overlay && m_window_system->GetOverlayDisplayApplet() == nullptr) {
|
||||
if (auto overlay_process = CreateProcess(m_system, static_cast<u64>(AppletProgramId::OverlayDisplay), 0, 0)) {
|
||||
auto overlay_applet = std::make_shared<Applet>(m_system, std::move(overlay_process), false);
|
||||
auto overlay_applet = std::make_shared<Applet>(m_system, std::make_unique<Service::Process>(*std::move(overlay_process)), false);
|
||||
overlay_applet->program_id = static_cast<u64>(AppletProgramId::OverlayDisplay);
|
||||
overlay_applet->applet_id = AppletId::OverlayDisplay;
|
||||
overlay_applet->type = AppletType::OverlayApplet;
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <thread>
|
||||
#include "common/thread.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/am/am_types.h"
|
||||
#include "core/hle/service/am/button_poller.h"
|
||||
@@ -34,16 +36,15 @@ ButtonPressDuration ClassifyPressDuration(std::chrono::steady_clock::time_point
|
||||
|
||||
} // namespace
|
||||
|
||||
ButtonPoller::ButtonPoller(Core::System& system, WindowSystem& window_system)
|
||||
: m_window_system(window_system) {
|
||||
ButtonPoller::ButtonPoller(Core::System& system, WindowSystem& window_system) {
|
||||
// TODO: am reads this from the home button state in hid, which is controller-agnostic.
|
||||
Core::HID::ControllerUpdateCallback engine_callback{
|
||||
.on_change =
|
||||
[this](Core::HID::ControllerTriggerType type) {
|
||||
if (type == Core::HID::ControllerTriggerType::Button) {
|
||||
this->OnButtonStateChanged();
|
||||
}
|
||||
},
|
||||
.on_change = [this, &window_system](Core::HID::ControllerTriggerType type) {
|
||||
if (type == Core::HID::ControllerTriggerType::Button) {
|
||||
std::unique_lock lk{m_mutex};
|
||||
OnButtonStateChanged(window_system);
|
||||
}
|
||||
},
|
||||
.is_npad_service = true,
|
||||
};
|
||||
|
||||
@@ -52,25 +53,35 @@ ButtonPoller::ButtonPoller(Core::System& system, WindowSystem& window_system)
|
||||
m_player1 = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
|
||||
m_player1_key = m_player1->SetCallback(engine_callback);
|
||||
|
||||
m_thread = std::thread([this] { this->ThreadLoop(); });
|
||||
m_thread = std::jthread([this, &window_system](std::stop_token stop_token) {
|
||||
Common::SetCurrentThreadName("ButtonPoller");
|
||||
while (!stop_token.stop_requested()) {
|
||||
using namespace std::chrono_literals;
|
||||
std::unique_lock lk{m_mutex};
|
||||
m_cv.wait_for(lk, 50ms);
|
||||
if (stop_token.stop_requested())
|
||||
break;
|
||||
OnButtonStateChanged(window_system);
|
||||
std::this_thread::sleep_for(5ms);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ButtonPoller::~ButtonPoller() {
|
||||
m_handheld->DeleteCallback(m_handheld_key);
|
||||
m_player1->DeleteCallback(m_player1_key);
|
||||
m_stop = true;
|
||||
m_cv.notify_all();
|
||||
if (m_thread.joinable()) {
|
||||
m_thread.request_stop();
|
||||
m_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void ButtonPoller::OnButtonStateChanged() {
|
||||
std::lock_guard lk{m_mutex};
|
||||
const bool home_button =
|
||||
m_handheld->GetHomeButtons().home.Value() || m_player1->GetHomeButtons().home.Value();
|
||||
const bool capture_button = m_handheld->GetCaptureButtons().capture.Value() ||
|
||||
m_player1->GetCaptureButtons().capture.Value();
|
||||
void ButtonPoller::OnButtonStateChanged(WindowSystem& window_system) {
|
||||
auto const home_button = m_handheld->GetHomeButtons().home.Value()
|
||||
|| m_player1->GetHomeButtons().home.Value();
|
||||
auto const capture_button = m_handheld->GetCaptureButtons().capture.Value()
|
||||
|| m_player1->GetCaptureButtons().capture.Value();
|
||||
|
||||
// Buttons pressed which were not previously pressed
|
||||
if (home_button && !m_home_button_press_start) {
|
||||
@@ -90,7 +101,7 @@ void ButtonPoller::OnButtonStateChanged() {
|
||||
if (home_button && m_home_button_press_start && !m_home_button_long_sent) {
|
||||
const auto duration = ClassifyPressDuration(*m_home_button_press_start);
|
||||
if (duration != ButtonPressDuration::ShortPressing) {
|
||||
m_window_system.OnSystemButtonPress(SystemButtonType::HomeButtonLongPressing);
|
||||
window_system.OnSystemButtonPress(SystemButtonType::HomeButtonLongPressing);
|
||||
m_home_button_long_sent = true;
|
||||
}
|
||||
}
|
||||
@@ -98,7 +109,7 @@ void ButtonPoller::OnButtonStateChanged() {
|
||||
if (capture_button && m_capture_button_press_start && !m_capture_button_long_sent) {
|
||||
const auto duration = ClassifyPressDuration(*m_capture_button_press_start);
|
||||
if (duration != ButtonPressDuration::ShortPressing) {
|
||||
m_window_system.OnSystemButtonPress(SystemButtonType::CaptureButtonLongPressing);
|
||||
window_system.OnSystemButtonPress(SystemButtonType::CaptureButtonLongPressing);
|
||||
m_capture_button_long_sent = true;
|
||||
}
|
||||
}
|
||||
@@ -107,9 +118,8 @@ void ButtonPoller::OnButtonStateChanged() {
|
||||
if (!home_button && m_home_button_press_start) {
|
||||
if(!m_home_button_long_sent) {
|
||||
const auto duration = ClassifyPressDuration(*m_home_button_press_start);
|
||||
m_window_system.OnSystemButtonPress(
|
||||
duration == ButtonPressDuration::ShortPressing ? SystemButtonType::HomeButtonShortPressing
|
||||
: SystemButtonType::HomeButtonLongPressing);
|
||||
window_system.OnSystemButtonPress(duration == ButtonPressDuration::ShortPressing
|
||||
? SystemButtonType::HomeButtonShortPressing : SystemButtonType::HomeButtonLongPressing);
|
||||
}
|
||||
m_home_button_press_start = std::nullopt;
|
||||
m_home_button_long_sent = false;
|
||||
@@ -117,9 +127,8 @@ void ButtonPoller::OnButtonStateChanged() {
|
||||
if (!capture_button && m_capture_button_press_start) {
|
||||
if (!m_capture_button_long_sent) {
|
||||
const auto duration = ClassifyPressDuration(*m_capture_button_press_start);
|
||||
m_window_system.OnSystemButtonPress(
|
||||
duration == ButtonPressDuration::ShortPressing ? SystemButtonType::CaptureButtonShortPressing
|
||||
: SystemButtonType::CaptureButtonLongPressing);
|
||||
window_system.OnSystemButtonPress(duration == ButtonPressDuration::ShortPressing
|
||||
? SystemButtonType::CaptureButtonShortPressing : SystemButtonType::CaptureButtonLongPressing);
|
||||
}
|
||||
m_capture_button_press_start = std::nullopt;
|
||||
m_capture_button_long_sent = false;
|
||||
@@ -130,16 +139,4 @@ void ButtonPoller::OnButtonStateChanged() {
|
||||
// }
|
||||
}
|
||||
|
||||
void ButtonPoller::ThreadLoop() {
|
||||
using namespace std::chrono_literals;
|
||||
std::unique_lock lk{m_mutex};
|
||||
while (!m_stop) {
|
||||
m_cv.wait_for(lk, 50ms);
|
||||
if (m_stop) break;
|
||||
lk.unlock();
|
||||
OnButtonStateChanged();
|
||||
lk.lock();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
@@ -30,31 +30,23 @@ class ButtonPoller {
|
||||
public:
|
||||
explicit ButtonPoller(Core::System& system, WindowSystem& window_system);
|
||||
~ButtonPoller();
|
||||
void OnButtonStateChanged(WindowSystem& window_system);
|
||||
|
||||
private:
|
||||
void OnButtonStateChanged();
|
||||
void ThreadLoop();
|
||||
|
||||
private:
|
||||
WindowSystem& m_window_system;
|
||||
|
||||
Core::HID::EmulatedController* m_handheld{};
|
||||
int m_handheld_key{};
|
||||
Core::HID::EmulatedController* m_player1{};
|
||||
int m_player1_key{};
|
||||
|
||||
std::mutex m_mutex;
|
||||
std::condition_variable m_cv;
|
||||
std::jthread m_thread;
|
||||
std::optional<std::chrono::steady_clock::time_point> m_home_button_press_start{};
|
||||
std::optional<std::chrono::steady_clock::time_point> m_capture_button_press_start{};
|
||||
std::optional<std::chrono::steady_clock::time_point> m_power_button_press_start{};
|
||||
|
||||
bool m_home_button_long_sent{};
|
||||
bool m_capture_button_long_sent{};
|
||||
bool m_power_button_long_sent{};
|
||||
|
||||
std::thread m_thread;
|
||||
std::atomic<bool> m_stop{false};
|
||||
std::condition_variable m_cv;
|
||||
std::mutex m_mutex;
|
||||
Core::HID::EmulatedController* m_handheld{};
|
||||
Core::HID::EmulatedController* m_player1{};
|
||||
int32_t m_handheld_key{};
|
||||
int32_t m_player1_key{};
|
||||
bool m_home_button_long_sent : 1 = false;
|
||||
bool m_capture_button_long_sent : 1 = false;
|
||||
bool m_power_button_long_sent : 1 = false;
|
||||
};
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -15,12 +18,24 @@ enum class UserDataTag : u32 {
|
||||
};
|
||||
|
||||
EventObserver::EventObserver(Core::System& system, WindowSystem& window_system)
|
||||
: m_system(system), m_context(system, "am:EventObserver"), m_window_system(window_system),
|
||||
m_wakeup_event(m_context), m_wakeup_holder(m_wakeup_event.GetHandle()) {
|
||||
: m_system(system), m_context(system, "am:EventObserver")
|
||||
, m_window_system(window_system)
|
||||
, m_wakeup_event(m_context)
|
||||
, m_wakeup_holder(m_wakeup_event.GetHandle())
|
||||
{
|
||||
m_window_system.SetEventObserver(this);
|
||||
m_wakeup_holder.SetUserData(static_cast<uintptr_t>(UserDataTag::WakeupEvent));
|
||||
m_wakeup_holder.LinkToMultiWait(std::addressof(m_multi_wait));
|
||||
m_thread = std::thread([&] { this->ThreadFunc(); });
|
||||
m_thread = std::thread([this] {
|
||||
Common::SetCurrentThreadName("am:EventObserver");
|
||||
while (true) {
|
||||
auto* signaled_holder = this->WaitSignaled();
|
||||
if (!signaled_holder) {
|
||||
break;
|
||||
}
|
||||
this->Process(signaled_holder);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
EventObserver::~EventObserver() {
|
||||
@@ -146,17 +161,4 @@ void EventObserver::DestroyAppletProcessHolderLocked(ProcessHolder* holder) {
|
||||
delete holder;
|
||||
}
|
||||
|
||||
void EventObserver::ThreadFunc() {
|
||||
Common::SetCurrentThreadName("am:EventObserver");
|
||||
|
||||
while (true) {
|
||||
auto* signaled_holder = this->WaitSignaled();
|
||||
if (!signaled_holder) {
|
||||
break;
|
||||
}
|
||||
|
||||
this->Process(signaled_holder);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -41,9 +44,6 @@ private:
|
||||
private:
|
||||
void DestroyAppletProcessHolderLocked(ProcessHolder* holder);
|
||||
|
||||
private:
|
||||
void ThreadFunc();
|
||||
|
||||
private:
|
||||
// System reference and context.
|
||||
Core::System& m_system;
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <optional>
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/nca_metadata.h"
|
||||
@@ -16,7 +20,7 @@ namespace Service::AM {
|
||||
|
||||
namespace {
|
||||
|
||||
FileSys::StorageId GetStorageIdForFrontendSlot(
|
||||
[[nodiscard]] FileSys::StorageId GetStorageIdForFrontendSlot(
|
||||
std::optional<FileSys::ContentProviderUnionSlot> slot) {
|
||||
if (!slot.has_value()) {
|
||||
return FileSys::StorageId::None;
|
||||
@@ -36,31 +40,23 @@ FileSys::StorageId GetStorageIdForFrontendSlot(
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<Process> CreateProcessImpl(std::unique_ptr<Loader::AppLoader>& out_loader,
|
||||
Loader::ResultStatus& out_load_result,
|
||||
Core::System& system, FileSys::VirtualFile file,
|
||||
u64 program_id, u64 program_index) {
|
||||
[[nodiscard]] inline std::optional<Process> CreateProcessImpl(std::unique_ptr<Loader::AppLoader>& out_loader, Loader::ResultStatus& out_load_result, Core::System& system, FileSys::VirtualFile file, u64 program_id, u64 program_index) {
|
||||
// Get the appropriate loader to parse this NCA.
|
||||
out_loader = Loader::GetLoader(system, file, program_id, program_index);
|
||||
|
||||
// Ensure we have a loader which can parse the NCA.
|
||||
if (!out_loader) {
|
||||
return nullptr;
|
||||
if (out_loader) {
|
||||
// Try to load the process.
|
||||
auto process = std::make_optional<Process>(system);
|
||||
if (process->Initialize(*out_loader, out_load_result)) {
|
||||
return process;
|
||||
}
|
||||
}
|
||||
|
||||
// Try to load the process.
|
||||
auto process = std::make_unique<Process>(system);
|
||||
if (process->Initialize(*out_loader, out_load_result)) {
|
||||
return process;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
std::unique_ptr<Process> CreateProcess(Core::System& system, u64 program_id,
|
||||
u8 minimum_key_generation, u8 maximum_key_generation) {
|
||||
std::optional<Process> CreateProcess(Core::System& system, u64 program_id, u8 minimum_key_generation, u8 maximum_key_generation) {
|
||||
// Attempt to load program NCA.
|
||||
FileSys::VirtualFile nca_raw{};
|
||||
|
||||
@@ -70,7 +66,7 @@ std::unique_ptr<Process> CreateProcess(Core::System& system, u64 program_id,
|
||||
|
||||
// Ensure we retrieved a program NCA.
|
||||
if (!nca_raw) {
|
||||
return nullptr;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Ensure we have a suitable version.
|
||||
@@ -79,9 +75,8 @@ std::unique_ptr<Process> CreateProcess(Core::System& system, u64 program_id,
|
||||
if (nca.GetStatus() == Loader::ResultStatus::Success &&
|
||||
(nca.GetKeyGeneration() < minimum_key_generation ||
|
||||
nca.GetKeyGeneration() > maximum_key_generation)) {
|
||||
LOG_WARNING(Service_LDR, "Skipping program {:016X} with generation {}", program_id,
|
||||
nca.GetKeyGeneration());
|
||||
return nullptr;
|
||||
LOG_WARNING(Service_LDR, "Skipping program {:016X} with generation {}", program_id, nca.GetKeyGeneration());
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,42 +85,32 @@ std::unique_ptr<Process> CreateProcess(Core::System& system, u64 program_id,
|
||||
return CreateProcessImpl(loader, status, system, nca_raw, program_id, 0);
|
||||
}
|
||||
|
||||
std::unique_ptr<Process> CreateApplicationProcess(std::vector<u8>& out_control,
|
||||
std::unique_ptr<Loader::AppLoader>& out_loader,
|
||||
Loader::ResultStatus& out_load_result,
|
||||
Core::System& system, FileSys::VirtualFile file,
|
||||
u64 program_id, u64 program_index) {
|
||||
auto process =
|
||||
CreateProcessImpl(out_loader, out_load_result, system, file, program_id, program_index);
|
||||
if (!process) {
|
||||
return nullptr;
|
||||
std::optional<Process> CreateApplicationProcess(std::vector<u8>& out_control, std::unique_ptr<Loader::AppLoader>& out_loader, Loader::ResultStatus& out_load_result, Core::System& system, FileSys::VirtualFile file, u64 program_id, u64 program_index) {
|
||||
if (auto process = CreateProcessImpl(out_loader, out_load_result, system, file, program_id, program_index); process) {
|
||||
FileSys::NACP nacp;
|
||||
if (out_loader->ReadControlData(nacp) == Loader::ResultStatus::Success) {
|
||||
out_control = nacp.GetRawBytes();
|
||||
} else {
|
||||
out_control.resize(sizeof(FileSys::RawNACP));
|
||||
std::fill(out_control.begin(), out_control.end(), (u8) 0);
|
||||
}
|
||||
|
||||
auto& storage = system.GetContentProviderUnion();
|
||||
Service::Glue::ApplicationLaunchProperty launch{};
|
||||
launch.title_id = process->GetProgramId();
|
||||
|
||||
FileSys::PatchManager pm{launch.title_id, system.GetFileSystemController(), storage};
|
||||
launch.version = pm.GetGameVersion().value_or(0);
|
||||
|
||||
// TODO(DarkLordZach): When FSController/Game Card Support is added, if
|
||||
// current_process_game_card use correct StorageId
|
||||
launch.base_game_storage_id = GetStorageIdForFrontendSlot(storage.GetSlotForEntry(launch.title_id, FileSys::ContentRecordType::Program));
|
||||
launch.update_storage_id = GetStorageIdForFrontendSlot(storage.GetSlotForEntry(FileSys::GetUpdateTitleID(launch.title_id), FileSys::ContentRecordType::Program));
|
||||
|
||||
system.GetARPManager().Register(launch.title_id, launch, out_control);
|
||||
return process;
|
||||
}
|
||||
|
||||
FileSys::NACP nacp;
|
||||
if (out_loader->ReadControlData(nacp) == Loader::ResultStatus::Success) {
|
||||
out_control = nacp.GetRawBytes();
|
||||
} else {
|
||||
out_control.resize(sizeof(FileSys::RawNACP));
|
||||
std::fill(out_control.begin(), out_control.end(), (u8) 0);
|
||||
}
|
||||
|
||||
auto& storage = system.GetContentProviderUnion();
|
||||
Service::Glue::ApplicationLaunchProperty launch{};
|
||||
launch.title_id = process->GetProgramId();
|
||||
|
||||
FileSys::PatchManager pm{launch.title_id, system.GetFileSystemController(), storage};
|
||||
launch.version = pm.GetGameVersion().value_or(0);
|
||||
|
||||
// TODO(DarkLordZach): When FSController/Game Card Support is added, if
|
||||
// current_process_game_card use correct StorageId
|
||||
launch.base_game_storage_id = GetStorageIdForFrontendSlot(
|
||||
storage.GetSlotForEntry(launch.title_id, FileSys::ContentRecordType::Program));
|
||||
launch.update_storage_id = GetStorageIdForFrontendSlot(storage.GetSlotForEntry(
|
||||
FileSys::GetUpdateTitleID(launch.title_id), FileSys::ContentRecordType::Program));
|
||||
|
||||
system.GetARPManager().Register(launch.title_id, launch, out_control);
|
||||
|
||||
return process;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -24,12 +27,7 @@ class Process;
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
std::unique_ptr<Process> CreateProcess(Core::System& system, u64 program_id,
|
||||
u8 minimum_key_generation, u8 maximum_key_generation);
|
||||
std::unique_ptr<Process> CreateApplicationProcess(std::vector<u8>& out_control,
|
||||
std::unique_ptr<Loader::AppLoader>& out_loader,
|
||||
Loader::ResultStatus& out_load_result,
|
||||
Core::System& system, FileSys::VirtualFile file,
|
||||
u64 program_id, u64 program_index);
|
||||
std::optional<Process> CreateProcess(Core::System& system, u64 program_id, u8 minimum_key_generation, u8 maximum_key_generation);
|
||||
std::optional<Process> CreateApplicationProcess(std::vector<u8>& out_control, std::unique_ptr<Loader::AppLoader>& out_loader, Loader::ResultStatus& out_load_result, Core::System& system, FileSys::VirtualFile file, u64 program_id, u64 program_index);
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
@@ -21,8 +21,7 @@ namespace Service::AM {
|
||||
|
||||
namespace {
|
||||
|
||||
Result CreateGuestApplication(SharedPointer<IApplicationAccessor>* out_application_accessor,
|
||||
Core::System& system, WindowSystem& window_system, u64 program_id) {
|
||||
Result CreateGuestApplication(SharedPointer<IApplicationAccessor>* out_application_accessor, Core::System& system, WindowSystem& window_system, u64 program_id) {
|
||||
FileSys::VirtualFile nca_raw{};
|
||||
|
||||
// Get the program NCA from storage.
|
||||
@@ -35,11 +34,10 @@ Result CreateGuestApplication(SharedPointer<IApplicationAccessor>* out_applicati
|
||||
std::vector<u8> control;
|
||||
std::unique_ptr<Loader::AppLoader> loader;
|
||||
Loader::ResultStatus result;
|
||||
auto process =
|
||||
CreateApplicationProcess(control, loader, result, system, nca_raw, program_id, 0);
|
||||
R_UNLESS(process != nullptr, ResultUnknown);
|
||||
auto process = CreateApplicationProcess(control, loader, result, system, nca_raw, program_id, 0);
|
||||
R_UNLESS(process != std::nullopt, ResultUnknown);
|
||||
|
||||
const auto applet = std::make_shared<Applet>(system, std::move(process), true);
|
||||
const auto applet = std::make_shared<Applet>(system, std::make_unique<Service::Process>(*std::move(process)), true);
|
||||
applet->program_id = program_id;
|
||||
applet->applet_id = AppletId::Application;
|
||||
applet->type = AppletType::Application;
|
||||
@@ -47,8 +45,7 @@ Result CreateGuestApplication(SharedPointer<IApplicationAccessor>* out_applicati
|
||||
|
||||
window_system.TrackApplet(applet, true);
|
||||
|
||||
*out_application_accessor =
|
||||
std::make_shared<IApplicationAccessor>(system, applet, window_system);
|
||||
*out_application_accessor = std::make_shared<IApplicationAccessor>(system, applet, window_system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
@@ -90,12 +87,10 @@ Result IApplicationCreator::CreateSystemApplication(
|
||||
|
||||
std::vector<u8> control;
|
||||
std::unique_ptr<Loader::AppLoader> loader;
|
||||
auto process = CreateProcess(system, application_id, 1, 21);
|
||||
R_UNLESS(process != std::nullopt, ResultUnknown);
|
||||
|
||||
auto process =
|
||||
CreateProcess(system, application_id, 1, 21);
|
||||
R_UNLESS(process != nullptr, ResultUnknown);
|
||||
|
||||
const auto applet = std::make_shared<Applet>(system, std::move(process), true);
|
||||
const auto applet = std::make_shared<Applet>(system, std::make_unique<Service::Process>(*std::move(process)), true);
|
||||
applet->program_id = application_id;
|
||||
applet->applet_id = AppletId::Starter;
|
||||
applet->type = AppletType::LibraryApplet;
|
||||
@@ -103,8 +98,7 @@ Result IApplicationCreator::CreateSystemApplication(
|
||||
|
||||
m_window_system.TrackApplet(applet, true);
|
||||
|
||||
*out_application_accessor =
|
||||
std::make_shared<IApplicationAccessor>(system, applet, m_window_system);
|
||||
*out_application_accessor = std::make_shared<IApplicationAccessor>(system, applet, m_window_system);
|
||||
Core::LaunchTimestampCache::SaveLaunchTimestamp(application_id);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
@@ -121,26 +121,23 @@ std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet(Core::System& system,
|
||||
};
|
||||
|
||||
auto process = CreateProcess(system, program_id, Firmware1400, Firmware2100);
|
||||
if (!process) {
|
||||
// Couldn't initialize the guest process
|
||||
return {};
|
||||
if (process) {
|
||||
const auto applet = std::make_shared<Applet>(system, std::make_unique<Service::Process>(*std::move(process)), false);
|
||||
applet->program_id = program_id;
|
||||
applet->applet_id = applet_id;
|
||||
applet->type = AppletType::LibraryApplet;
|
||||
applet->library_applet_mode = mode;
|
||||
applet->window_visible = mode != LibraryAppletMode::AllForegroundInitiallyHidden;
|
||||
|
||||
auto broker = std::make_shared<AppletDataBroker>(system);
|
||||
applet->caller_applet = caller_applet;
|
||||
applet->caller_applet_broker = broker;
|
||||
caller_applet->child_applets.push_back(applet);
|
||||
window_system.TrackApplet(applet, false);
|
||||
return std::make_shared<ILibraryAppletAccessor>(system, broker, applet);
|
||||
}
|
||||
|
||||
const auto applet = std::make_shared<Applet>(system, std::move(process), false);
|
||||
applet->program_id = program_id;
|
||||
applet->applet_id = applet_id;
|
||||
applet->type = AppletType::LibraryApplet;
|
||||
applet->library_applet_mode = mode;
|
||||
applet->window_visible = mode != LibraryAppletMode::AllForegroundInitiallyHidden;
|
||||
|
||||
auto broker = std::make_shared<AppletDataBroker>(system);
|
||||
applet->caller_applet = caller_applet;
|
||||
applet->caller_applet_broker = broker;
|
||||
caller_applet->child_applets.push_back(applet);
|
||||
|
||||
window_system.TrackApplet(applet, false);
|
||||
|
||||
return std::make_shared<ILibraryAppletAccessor>(system, broker, applet);
|
||||
// Couldn't initialize the guest process
|
||||
return {};
|
||||
}
|
||||
|
||||
std::shared_ptr<ILibraryAppletAccessor> CreateFrontendApplet(Core::System& system,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
@@ -14,7 +14,9 @@ namespace Service::Audio {
|
||||
using namespace AudioCore::AudioOut;
|
||||
|
||||
IAudioOutManager::IAudioOutManager(Core::System& system_)
|
||||
: ServiceFramework{system_, "audout:u"}, impl{std::make_unique<Manager>(system_)} {
|
||||
: ServiceFramework{system_, "audout:u"}
|
||||
, impl(system_)
|
||||
{
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&IAudioOutManager::ListAudioOuts>, "ListAudioOuts"},
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
@@ -43,7 +43,7 @@ private:
|
||||
AudioCore::AudioOut::AudioOutParameter parameter,
|
||||
InCopyHandle<Kernel::KProcess> process_handle, ClientAppletResourceUserId aruid);
|
||||
|
||||
std::unique_ptr<AudioCore::AudioOut::Manager> impl;
|
||||
std::optional<AudioCore::AudioOut::Manager> impl;
|
||||
};
|
||||
|
||||
} // namespace Service::Audio
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -15,7 +18,9 @@ namespace Service::Audio {
|
||||
using namespace AudioCore::Renderer;
|
||||
|
||||
IAudioRendererManager::IAudioRendererManager(Core::System& system_)
|
||||
: ServiceFramework{system_, "audren:u"}, impl{std::make_unique<Manager>(system_)} {
|
||||
: ServiceFramework{system_, "audren:u"}
|
||||
, impl(system_)
|
||||
{
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&IAudioRendererManager::OpenAudioRenderer>, "OpenAudioRenderer"},
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -30,7 +33,7 @@ private:
|
||||
Result GetAudioDeviceServiceWithRevisionInfo(Out<SharedPointer<IAudioDevice>> out_audio_device,
|
||||
u32 revision, ClientAppletResourceUserId aruid);
|
||||
|
||||
std::unique_ptr<AudioCore::Renderer::Manager> impl;
|
||||
std::optional<AudioCore::Renderer::Manager> impl;
|
||||
u32 num_audio_devices{0};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -10,14 +13,6 @@
|
||||
|
||||
namespace Service {
|
||||
|
||||
Process::Process(Core::System& system)
|
||||
: m_system(system), m_process(), m_main_thread_priority(), m_main_thread_stack_size(),
|
||||
m_process_started() {}
|
||||
|
||||
Process::~Process() {
|
||||
this->Finalize();
|
||||
}
|
||||
|
||||
bool Process::Initialize(Loader::AppLoader& loader, Loader::ResultStatus& out_load_result) {
|
||||
// First, ensure we are not holding another process.
|
||||
this->Finalize();
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -22,8 +25,8 @@ namespace Service {
|
||||
|
||||
class Process {
|
||||
public:
|
||||
explicit Process(Core::System& system);
|
||||
~Process();
|
||||
inline explicit Process(Core::System& system) noexcept : m_system(system) {}
|
||||
inline ~Process() { this->Finalize(); }
|
||||
|
||||
bool Initialize(Loader::AppLoader& loader, Loader::ResultStatus& out_load_result);
|
||||
void Finalize();
|
||||
@@ -50,8 +53,8 @@ public:
|
||||
private:
|
||||
Core::System& m_system;
|
||||
Kernel::KProcess* m_process{};
|
||||
s32 m_main_thread_priority{};
|
||||
u64 m_main_thread_stack_size{};
|
||||
s32 m_main_thread_priority{};
|
||||
bool m_process_started{};
|
||||
};
|
||||
|
||||
|
||||
@@ -1158,8 +1158,8 @@ Result ISystemSettingsServer::SetDeviceNickName(
|
||||
|
||||
Result ISystemSettingsServer::GetProductModel(Out<u32> out_product_model) {
|
||||
// Most certainly should be 1 -- definitely should not be 2, but it's worth tinkering with anyways
|
||||
auto const product_model = ::Settings::values.product_model.GetValue();
|
||||
LOG_INFO(Service_SET, "called, product_model={}", product_model);
|
||||
u32 const product_model = 1;
|
||||
LOG_WARNING(Service_SET, "(STUBBED) called, product_model={}", product_model);
|
||||
*out_product_model = product_model;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
@@ -642,7 +642,7 @@ void EmitIR<IR::Opcode::ArithmeticShiftRight64>(oaknut::CodeGenerator& code, Emi
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::RotateRight32>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
void EmitIR<IR::Opcode::BitRotateRight32>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
const auto carry_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp);
|
||||
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
@@ -708,7 +708,7 @@ void EmitIR<IR::Opcode::RotateRight32>(oaknut::CodeGenerator& code, EmitContext&
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::RotateRight64>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
void EmitIR<IR::Opcode::BitRotateRight64>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
auto& operand_arg = args[0];
|
||||
auto& shift_arg = args[1];
|
||||
|
||||
@@ -209,9 +209,9 @@ void CallbackOnlyEmitExclusiveWriteMemory(oaknut::CodeGenerator& code, EmitConte
|
||||
ctx.reg_alloc.DefineAsRegister(inst, X0);
|
||||
}
|
||||
|
||||
constexpr size_t page_bits = 12;
|
||||
constexpr size_t page_size = 1 << page_bits;
|
||||
constexpr size_t page_mask = (1 << page_bits) - 1;
|
||||
constexpr size_t page_table_const_bits = 12;
|
||||
constexpr size_t page_table_const_size = 1 << page_table_const_bits;
|
||||
constexpr size_t page_table_const_mask = (1 << page_table_const_bits) - 1;
|
||||
|
||||
// This function may use Xscratch0 as a scratch register
|
||||
// Trashes NZCV
|
||||
@@ -242,28 +242,28 @@ void EmitDetectMisalignedVAddr(oaknut::CodeGenerator& code, EmitContext& ctx, oa
|
||||
code.TST(Xaddr, align_mask);
|
||||
code.B(NE, *fallback);
|
||||
} else {
|
||||
// If (addr & page_mask) > page_size - byte_size, use fallback.
|
||||
code.AND(Xscratch0, Xaddr, page_mask);
|
||||
code.CMP(Xscratch0, page_size - bitsize / 8);
|
||||
// If (addr & page_table_const_mask) > page_table_const_size - byte_size, use fallback.
|
||||
code.AND(Xscratch0, Xaddr, page_table_const_mask);
|
||||
code.CMP(Xscratch0, page_table_const_size - bitsize / 8);
|
||||
code.B(HI, *fallback);
|
||||
}
|
||||
}
|
||||
|
||||
// Outputs Xscratch0 = page_table[addr >> page_bits]
|
||||
// Outputs Xscratch0 = page_table[addr >> page_table_const_bits]
|
||||
// May use Xscratch1 as scratch register
|
||||
// Address to read/write = [ret0 + ret1], ret0 is always Xscratch0 and ret1 is either Xaddr or Xscratch1
|
||||
// Trashes NZCV
|
||||
template<size_t bitsize>
|
||||
std::pair<oaknut::XReg, oaknut::XReg> InlinePageTableEmitVAddrLookup(oaknut::CodeGenerator& code, EmitContext& ctx, oaknut::XReg Xaddr, const SharedLabel& fallback) {
|
||||
const size_t valid_page_index_bits = ctx.conf.page_table_address_space_bits - page_bits;
|
||||
const size_t valid_page_index_bits = ctx.conf.page_table_address_space_bits - page_table_const_bits;
|
||||
const size_t unused_top_bits = 64 - ctx.conf.page_table_address_space_bits;
|
||||
|
||||
EmitDetectMisalignedVAddr<bitsize>(code, ctx, Xaddr, fallback);
|
||||
|
||||
if (ctx.conf.silently_mirror_page_table || unused_top_bits == 0) {
|
||||
code.UBFX(Xscratch0, Xaddr, page_bits, valid_page_index_bits);
|
||||
code.UBFX(Xscratch0, Xaddr, page_table_const_bits, valid_page_index_bits);
|
||||
} else {
|
||||
code.LSR(Xscratch0, Xaddr, page_bits);
|
||||
code.LSR(Xscratch0, Xaddr, page_table_const_bits);
|
||||
code.TST(Xscratch0, u64(~u64(0)) << valid_page_index_bits);
|
||||
code.B(NE, *fallback);
|
||||
}
|
||||
@@ -283,7 +283,7 @@ std::pair<oaknut::XReg, oaknut::XReg> InlinePageTableEmitVAddrLookup(oaknut::Cod
|
||||
if (ctx.conf.absolute_offset_page_table) {
|
||||
return std::make_pair(Xscratch0, Xaddr);
|
||||
}
|
||||
code.AND(Xscratch1, Xaddr, page_mask);
|
||||
code.AND(Xscratch1, Xaddr, page_table_const_mask);
|
||||
return std::make_pair(Xscratch0, Xscratch1);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ constexpr size_t xmrx(size_t x) noexcept {
|
||||
}
|
||||
|
||||
struct DoNotFastmemMarkerHash {
|
||||
[[nodiscard]] constexpr size_t operator()(const DoNotFastmemMarker& value) const noexcept {
|
||||
[[nodiscard]] size_t operator()(const DoNotFastmemMarker& value) const noexcept {
|
||||
return xmrx(std::get<0>(value).Value() ^ u64(std::get<1>(value)));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2024 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
@@ -164,12 +167,12 @@ void EmitIR<IR::Opcode::ArithmeticShiftRight64>(biscuit::Assembler&, EmitContext
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::RotateRight32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
void EmitIR<IR::Opcode::BitRotateRight32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::RotateRight64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
void EmitIR<IR::Opcode::BitRotateRight64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
|
||||
@@ -663,7 +663,7 @@ void EmitX64::EmitArithmeticShiftRight64(EmitContext& ctx, IR::Inst* inst) {
|
||||
}
|
||||
}
|
||||
|
||||
void EmitX64::EmitRotateRight32(EmitContext& ctx, IR::Inst* inst) {
|
||||
void EmitX64::EmitBitRotateRight32(EmitContext& ctx, IR::Inst* inst) {
|
||||
const auto carry_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp);
|
||||
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
@@ -736,7 +736,7 @@ void EmitX64::EmitRotateRight32(EmitContext& ctx, IR::Inst* inst) {
|
||||
}
|
||||
}
|
||||
|
||||
void EmitX64::EmitRotateRight64(EmitContext& ctx, IR::Inst* inst) {
|
||||
void EmitX64::EmitBitRotateRight64(EmitContext& ctx, IR::Inst* inst) {
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
auto& operand_arg = args[0];
|
||||
auto& shift_arg = args[1];
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <bit>
|
||||
#include "dynarmic/backend/x64/xbyak.h"
|
||||
|
||||
@@ -22,9 +24,9 @@ namespace {
|
||||
|
||||
using namespace Xbyak::util;
|
||||
|
||||
constexpr size_t page_bits = 12;
|
||||
constexpr size_t page_size = 1 << page_bits;
|
||||
constexpr size_t page_mask = (1 << page_bits) - 1;
|
||||
constexpr size_t page_table_const_bits = 12;
|
||||
constexpr size_t page_table_const_size = 1 << page_table_const_bits;
|
||||
constexpr size_t page_table_const_mask = (1 << page_table_const_bits) - 1;
|
||||
|
||||
template<typename EmitContext>
|
||||
void EmitDetectMisalignedVAddr(BlockOfCode& code, EmitContext& ctx, size_t bitsize, Xbyak::Label& abort, Xbyak::Reg64 vaddr, Xbyak::Reg64 tmp) {
|
||||
@@ -50,7 +52,7 @@ void EmitDetectMisalignedVAddr(BlockOfCode& code, EmitContext& ctx, size_t bitsi
|
||||
code.test(vaddr, align_mask);
|
||||
|
||||
if (ctx.conf.only_detect_misalignment_via_page_table_on_page_boundary) {
|
||||
const u32 page_align_mask = static_cast<u32>(page_size - 1) & ~align_mask;
|
||||
const u32 page_align_mask = static_cast<u32>(page_table_const_size - 1) & ~align_mask;
|
||||
|
||||
SharedLabel detect_boundary = GenSharedLabel(), resume = GenSharedLabel();
|
||||
|
||||
@@ -83,7 +85,7 @@ template<>
|
||||
// TODO: This code assumes vaddr has been zext from 32-bits to 64-bits.
|
||||
|
||||
code.mov(tmp, vaddr.cvt32());
|
||||
code.shr(tmp, int(page_bits));
|
||||
code.shr(tmp, int(page_table_const_bits));
|
||||
code.shl(tmp, int(ctx.conf.page_table_log2_stride));
|
||||
code.mov(page, qword[r14 + tmp.cvt64()]);
|
||||
if (ctx.conf.page_table_pointer_mask_bits == 0) {
|
||||
@@ -96,13 +98,13 @@ template<>
|
||||
return page + vaddr;
|
||||
}
|
||||
code.mov(tmp, vaddr.cvt32());
|
||||
code.and_(tmp, static_cast<u32>(page_mask));
|
||||
code.and_(tmp, static_cast<u32>(page_table_const_mask));
|
||||
return page + tmp.cvt64();
|
||||
}
|
||||
|
||||
template<>
|
||||
[[maybe_unused]] Xbyak::RegExp EmitVAddrLookup<A64EmitContext>(BlockOfCode& code, A64EmitContext& ctx, size_t bitsize, Xbyak::Label& abort, Xbyak::Reg64 vaddr) {
|
||||
const size_t valid_page_index_bits = ctx.conf.page_table_address_space_bits - page_bits;
|
||||
const size_t valid_page_index_bits = ctx.conf.page_table_address_space_bits - page_table_const_bits;
|
||||
const size_t unused_top_bits = 64 - ctx.conf.page_table_address_space_bits;
|
||||
|
||||
const Xbyak::Reg64 page = ctx.reg_alloc.ScratchGpr(code);
|
||||
@@ -112,29 +114,29 @@ template<>
|
||||
|
||||
if (unused_top_bits == 0) {
|
||||
code.mov(tmp, vaddr);
|
||||
code.shr(tmp, int(page_bits));
|
||||
code.shr(tmp, int(page_table_const_bits));
|
||||
} else if (ctx.conf.silently_mirror_page_table) {
|
||||
if (valid_page_index_bits >= 32) {
|
||||
if (code.HasHostFeature(HostFeature::BMI2)) {
|
||||
const Xbyak::Reg64 bit_count = ctx.reg_alloc.ScratchGpr(code);
|
||||
code.mov(bit_count, unused_top_bits);
|
||||
code.bzhi(tmp, vaddr, bit_count);
|
||||
code.shr(tmp, int(page_bits));
|
||||
code.shr(tmp, int(page_table_const_bits));
|
||||
ctx.reg_alloc.Release(bit_count);
|
||||
} else {
|
||||
code.mov(tmp, vaddr);
|
||||
code.shl(tmp, int(unused_top_bits));
|
||||
code.shr(tmp, int(unused_top_bits + page_bits));
|
||||
code.shr(tmp, int(unused_top_bits + page_table_const_bits));
|
||||
}
|
||||
} else {
|
||||
code.mov(tmp, vaddr);
|
||||
code.shr(tmp, int(page_bits));
|
||||
code.shr(tmp, int(page_table_const_bits));
|
||||
code.and_(tmp, u32((1 << valid_page_index_bits) - 1));
|
||||
}
|
||||
} else {
|
||||
ASSERT(valid_page_index_bits < 32);
|
||||
code.mov(tmp, vaddr);
|
||||
code.shr(tmp, int(page_bits));
|
||||
code.shr(tmp, int(page_table_const_bits));
|
||||
code.test(tmp, u32(-(1 << valid_page_index_bits)));
|
||||
code.jnz(abort, code.T_NEAR);
|
||||
}
|
||||
@@ -151,7 +153,7 @@ template<>
|
||||
return page + vaddr;
|
||||
}
|
||||
code.mov(tmp, vaddr);
|
||||
code.and_(tmp, static_cast<u32>(page_mask));
|
||||
code.and_(tmp, static_cast<u32>(page_table_const_mask));
|
||||
return page + tmp;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,12 +25,12 @@ using namespace Xbyak::util;
|
||||
|
||||
namespace {
|
||||
|
||||
enum class Op {
|
||||
enum class SaturationOp {
|
||||
Add,
|
||||
Sub,
|
||||
};
|
||||
|
||||
template<Op op, size_t size, bool has_overflow_inst = false>
|
||||
template<SaturationOp op, size_t size, bool has_overflow_inst = false>
|
||||
void EmitSignedSaturatedOp(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
|
||||
@@ -51,7 +51,7 @@ void EmitSignedSaturatedOp(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst)
|
||||
|
||||
// overflow now contains 0x7F... if a was positive, or 0x80... if a was negative
|
||||
|
||||
if constexpr (op == Op::Add) {
|
||||
if constexpr (op == SaturationOp::Add) {
|
||||
code.add(result, addend);
|
||||
} else {
|
||||
code.sub(result, addend);
|
||||
@@ -75,16 +75,16 @@ void EmitSignedSaturatedOp(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst)
|
||||
ctx.reg_alloc.DefineValue(code, inst, result);
|
||||
}
|
||||
|
||||
template<Op op, size_t size>
|
||||
template<SaturationOp op, size_t size>
|
||||
void EmitUnsignedSaturatedOp(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
|
||||
Xbyak::Reg op_result = ctx.reg_alloc.UseScratchGpr(code, args[0]).changeBit(size);
|
||||
Xbyak::Reg addend = ctx.reg_alloc.UseScratchGpr(code, args[1]).changeBit(size);
|
||||
|
||||
constexpr u64 boundary = op == Op::Add ? (std::numeric_limits<mcl::unsigned_integer_of_size<size>>::max)() : 0;
|
||||
constexpr u64 boundary = op == SaturationOp::Add ? (std::numeric_limits<mcl::unsigned_integer_of_size<size>>::max)() : 0;
|
||||
|
||||
if constexpr (op == Op::Add) {
|
||||
if constexpr (op == SaturationOp::Add) {
|
||||
code.add(op_result, addend);
|
||||
} else {
|
||||
code.sub(op_result, addend);
|
||||
@@ -106,11 +106,11 @@ void EmitUnsignedSaturatedOp(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst
|
||||
} // anonymous namespace
|
||||
|
||||
void EmitX64::EmitSignedSaturatedAddWithFlag32(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitSignedSaturatedOp<Op::Add, 32, true>(code, ctx, inst);
|
||||
EmitSignedSaturatedOp<SaturationOp::Add, 32, true>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitSignedSaturatedSubWithFlag32(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitSignedSaturatedOp<Op::Sub, 32, true>(code, ctx, inst);
|
||||
EmitSignedSaturatedOp<SaturationOp::Sub, 32, true>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitSignedSaturation(EmitContext& ctx, IR::Inst* inst) {
|
||||
@@ -192,19 +192,19 @@ void EmitX64::EmitUnsignedSaturation(EmitContext& ctx, IR::Inst* inst) {
|
||||
}
|
||||
|
||||
void EmitX64::EmitSignedSaturatedAdd8(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitSignedSaturatedOp<Op::Add, 8>(code, ctx, inst);
|
||||
EmitSignedSaturatedOp<SaturationOp::Add, 8>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitSignedSaturatedAdd16(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitSignedSaturatedOp<Op::Add, 16>(code, ctx, inst);
|
||||
EmitSignedSaturatedOp<SaturationOp::Add, 16>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitSignedSaturatedAdd32(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitSignedSaturatedOp<Op::Add, 32>(code, ctx, inst);
|
||||
EmitSignedSaturatedOp<SaturationOp::Add, 32>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitSignedSaturatedAdd64(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitSignedSaturatedOp<Op::Add, 64>(code, ctx, inst);
|
||||
EmitSignedSaturatedOp<SaturationOp::Add, 64>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitSignedSaturatedDoublingMultiplyReturnHigh16(EmitContext& ctx, IR::Inst* inst) {
|
||||
@@ -256,51 +256,51 @@ void EmitX64::EmitSignedSaturatedDoublingMultiplyReturnHigh32(EmitContext& ctx,
|
||||
}
|
||||
|
||||
void EmitX64::EmitSignedSaturatedSub8(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitSignedSaturatedOp<Op::Sub, 8>(code, ctx, inst);
|
||||
EmitSignedSaturatedOp<SaturationOp::Sub, 8>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitSignedSaturatedSub16(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitSignedSaturatedOp<Op::Sub, 16>(code, ctx, inst);
|
||||
EmitSignedSaturatedOp<SaturationOp::Sub, 16>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitSignedSaturatedSub32(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitSignedSaturatedOp<Op::Sub, 32>(code, ctx, inst);
|
||||
EmitSignedSaturatedOp<SaturationOp::Sub, 32>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitSignedSaturatedSub64(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitSignedSaturatedOp<Op::Sub, 64>(code, ctx, inst);
|
||||
EmitSignedSaturatedOp<SaturationOp::Sub, 64>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitUnsignedSaturatedAdd8(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitUnsignedSaturatedOp<Op::Add, 8>(code, ctx, inst);
|
||||
EmitUnsignedSaturatedOp<SaturationOp::Add, 8>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitUnsignedSaturatedAdd16(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitUnsignedSaturatedOp<Op::Add, 16>(code, ctx, inst);
|
||||
EmitUnsignedSaturatedOp<SaturationOp::Add, 16>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitUnsignedSaturatedAdd32(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitUnsignedSaturatedOp<Op::Add, 32>(code, ctx, inst);
|
||||
EmitUnsignedSaturatedOp<SaturationOp::Add, 32>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitUnsignedSaturatedAdd64(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitUnsignedSaturatedOp<Op::Add, 64>(code, ctx, inst);
|
||||
EmitUnsignedSaturatedOp<SaturationOp::Add, 64>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitUnsignedSaturatedSub8(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitUnsignedSaturatedOp<Op::Sub, 8>(code, ctx, inst);
|
||||
EmitUnsignedSaturatedOp<SaturationOp::Sub, 8>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitUnsignedSaturatedSub16(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitUnsignedSaturatedOp<Op::Sub, 16>(code, ctx, inst);
|
||||
EmitUnsignedSaturatedOp<SaturationOp::Sub, 16>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitUnsignedSaturatedSub32(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitUnsignedSaturatedOp<Op::Sub, 32>(code, ctx, inst);
|
||||
EmitUnsignedSaturatedOp<SaturationOp::Sub, 32>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitUnsignedSaturatedSub64(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitUnsignedSaturatedOp<Op::Sub, 64>(code, ctx, inst);
|
||||
EmitUnsignedSaturatedOp<SaturationOp::Sub, 64>(code, ctx, inst);
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Backend::X64
|
||||
|
||||
@@ -5854,3 +5854,5 @@ void EmitX64::EmitZeroVector(EmitContext& ctx, IR::Inst* inst) {
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Backend::X64
|
||||
|
||||
#undef ICODE
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
@@ -52,12 +52,12 @@ void EmitVectorSaturatedNative(BlockOfCode& code, EmitContext& ctx, IR::Inst* in
|
||||
ctx.reg_alloc.DefineValue(code, inst, result);
|
||||
}
|
||||
|
||||
enum class Op {
|
||||
enum class VectorSaturationOp {
|
||||
Add,
|
||||
Sub,
|
||||
};
|
||||
|
||||
template<Op op, size_t esize>
|
||||
template<VectorSaturationOp op, size_t esize>
|
||||
void EmitVectorSignedSaturated(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
static_assert(esize == 32 || esize == 64);
|
||||
constexpr u64 msb_mask = esize == 32 ? 0x8000000080000000 : 0x8000000000000000;
|
||||
@@ -72,7 +72,7 @@ void EmitVectorSignedSaturated(BlockOfCode& code, EmitContext& ctx, IR::Inst* in
|
||||
|
||||
code.movaps(xmm0, operand1);
|
||||
|
||||
if constexpr (op == Op::Add) {
|
||||
if constexpr (op == VectorSaturationOp::Add) {
|
||||
ICODE(vpadd)(result, operand1, operand2);
|
||||
code.vpternlogd(xmm0, result, operand2, 0b00100100);
|
||||
} else {
|
||||
@@ -102,7 +102,7 @@ void EmitVectorSignedSaturated(BlockOfCode& code, EmitContext& ctx, IR::Inst* in
|
||||
const Xbyak::Xmm tmp = ctx.reg_alloc.ScratchXmm(code);
|
||||
|
||||
if (code.HasHostFeature(HostFeature::AVX)) {
|
||||
if constexpr (op == Op::Add) {
|
||||
if constexpr (op == VectorSaturationOp::Add) {
|
||||
ICODE(vpadd)(result, operand1, operand2);
|
||||
} else {
|
||||
ICODE(vpsub)(result, operand1, operand2);
|
||||
@@ -112,7 +112,7 @@ void EmitVectorSignedSaturated(BlockOfCode& code, EmitContext& ctx, IR::Inst* in
|
||||
} else {
|
||||
code.movaps(xmm0, operand1);
|
||||
code.movaps(tmp, operand1);
|
||||
if constexpr (op == Op::Add) {
|
||||
if constexpr (op == VectorSaturationOp::Add) {
|
||||
ICODE(padd)(result, operand2);
|
||||
} else {
|
||||
ICODE(psub)(result, operand2);
|
||||
@@ -121,7 +121,7 @@ void EmitVectorSignedSaturated(BlockOfCode& code, EmitContext& ctx, IR::Inst* in
|
||||
code.pxor(tmp, result);
|
||||
}
|
||||
|
||||
if constexpr (op == Op::Add) {
|
||||
if constexpr (op == VectorSaturationOp::Add) {
|
||||
code.pandn(xmm0, tmp);
|
||||
} else {
|
||||
code.pand(xmm0, tmp);
|
||||
@@ -165,7 +165,7 @@ void EmitVectorSignedSaturated(BlockOfCode& code, EmitContext& ctx, IR::Inst* in
|
||||
}
|
||||
}
|
||||
|
||||
template<Op op, size_t esize>
|
||||
template<VectorSaturationOp op, size_t esize>
|
||||
void EmitVectorUnsignedSaturated(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
static_assert(esize == 32 || esize == 64);
|
||||
|
||||
@@ -177,7 +177,7 @@ void EmitVectorUnsignedSaturated(BlockOfCode& code, EmitContext& ctx, IR::Inst*
|
||||
const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm(code);
|
||||
const Xbyak::Reg8 overflow = ctx.reg_alloc.ScratchGpr(code).cvt8();
|
||||
|
||||
if constexpr (op == Op::Add) {
|
||||
if constexpr (op == VectorSaturationOp::Add) {
|
||||
ICODE(vpadd)(result, operand1, operand2);
|
||||
ICODE(vpcmpu)(k1, result, operand1, CmpInt::LessThan);
|
||||
ICODE(vpternlog)(result | k1, result, result, u8(0xFF));
|
||||
@@ -201,7 +201,7 @@ void EmitVectorUnsignedSaturated(BlockOfCode& code, EmitContext& ctx, IR::Inst*
|
||||
const Xbyak::Reg8 overflow = ctx.reg_alloc.ScratchGpr(code).cvt8();
|
||||
const Xbyak::Xmm tmp = ctx.reg_alloc.ScratchXmm(code);
|
||||
|
||||
if constexpr (op == Op::Add) {
|
||||
if constexpr (op == VectorSaturationOp::Add) {
|
||||
if (code.HasHostFeature(HostFeature::AVX)) {
|
||||
code.vpxor(xmm0, operand1, operand2);
|
||||
code.vpand(tmp, operand1, operand2);
|
||||
@@ -250,7 +250,7 @@ void EmitVectorUnsignedSaturated(BlockOfCode& code, EmitContext& ctx, IR::Inst*
|
||||
code.setnz(overflow);
|
||||
code.or_(code.byte[code.ABI_JIT_PTR + code.GetJitStateInfo().offsetof_fpsr_qc], overflow);
|
||||
|
||||
if constexpr (op == Op::Add) {
|
||||
if constexpr (op == VectorSaturationOp::Add) {
|
||||
code.por(result, tmp);
|
||||
ctx.reg_alloc.DefineValue(code, inst, result);
|
||||
} else {
|
||||
@@ -270,11 +270,11 @@ void EmitX64::EmitVectorSignedSaturatedAdd16(EmitContext& ctx, IR::Inst* inst) {
|
||||
}
|
||||
|
||||
void EmitX64::EmitVectorSignedSaturatedAdd32(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitVectorSignedSaturated<Op::Add, 32>(code, ctx, inst);
|
||||
EmitVectorSignedSaturated<VectorSaturationOp::Add, 32>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitVectorSignedSaturatedAdd64(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitVectorSignedSaturated<Op::Add, 64>(code, ctx, inst);
|
||||
EmitVectorSignedSaturated<VectorSaturationOp::Add, 64>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitVectorSignedSaturatedSub8(EmitContext& ctx, IR::Inst* inst) {
|
||||
@@ -286,11 +286,11 @@ void EmitX64::EmitVectorSignedSaturatedSub16(EmitContext& ctx, IR::Inst* inst) {
|
||||
}
|
||||
|
||||
void EmitX64::EmitVectorSignedSaturatedSub32(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitVectorSignedSaturated<Op::Sub, 32>(code, ctx, inst);
|
||||
EmitVectorSignedSaturated<VectorSaturationOp::Sub, 32>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitVectorSignedSaturatedSub64(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitVectorSignedSaturated<Op::Sub, 64>(code, ctx, inst);
|
||||
EmitVectorSignedSaturated<VectorSaturationOp::Sub, 64>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitVectorUnsignedSaturatedAdd8(EmitContext& ctx, IR::Inst* inst) {
|
||||
@@ -302,11 +302,11 @@ void EmitX64::EmitVectorUnsignedSaturatedAdd16(EmitContext& ctx, IR::Inst* inst)
|
||||
}
|
||||
|
||||
void EmitX64::EmitVectorUnsignedSaturatedAdd32(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitVectorUnsignedSaturated<Op::Add, 32>(code, ctx, inst);
|
||||
EmitVectorUnsignedSaturated<VectorSaturationOp::Add, 32>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitVectorUnsignedSaturatedAdd64(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitVectorUnsignedSaturated<Op::Add, 64>(code, ctx, inst);
|
||||
EmitVectorUnsignedSaturated<VectorSaturationOp::Add, 64>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitVectorUnsignedSaturatedSub8(EmitContext& ctx, IR::Inst* inst) {
|
||||
@@ -318,11 +318,11 @@ void EmitX64::EmitVectorUnsignedSaturatedSub16(EmitContext& ctx, IR::Inst* inst)
|
||||
}
|
||||
|
||||
void EmitX64::EmitVectorUnsignedSaturatedSub32(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitVectorUnsignedSaturated<Op::Sub, 32>(code, ctx, inst);
|
||||
EmitVectorUnsignedSaturated<VectorSaturationOp::Sub, 32>(code, ctx, inst);
|
||||
}
|
||||
|
||||
void EmitX64::EmitVectorUnsignedSaturatedSub64(EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitVectorUnsignedSaturated<Op::Sub, 64>(code, ctx, inst);
|
||||
EmitVectorUnsignedSaturated<VectorSaturationOp::Sub, 64>(code, ctx, inst);
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Backend::X64
|
||||
|
||||
@@ -35,7 +35,7 @@ FPUnpacked FusedMulAdd(FPUnpacked addend, FPUnpacked op1, FPUnpacked op2) {
|
||||
return std::make_tuple(exponent, value);
|
||||
}();
|
||||
|
||||
if (product_value == 0) {
|
||||
if (product_value == u128(0, 0)) {
|
||||
return addend;
|
||||
}
|
||||
|
||||
@@ -55,13 +55,13 @@ FPUnpacked FusedMulAdd(FPUnpacked addend, FPUnpacked op1, FPUnpacked op2) {
|
||||
}
|
||||
|
||||
// addend < product
|
||||
const u128 result = product_value + StickyLogicalShiftRight(addend.mantissa, exp_diff - normalized_point_position);
|
||||
const u128 result = product_value + StickyLogicalShiftRight(u128(addend.mantissa, 0), exp_diff - normalized_point_position);
|
||||
return ReduceMantissa(product_sign, product_exponent, result);
|
||||
}
|
||||
|
||||
// Subtraction
|
||||
|
||||
const u128 addend_long = u128(addend.mantissa) << normalized_point_position;
|
||||
const u128 addend_long = u128(addend.mantissa, 0) << normalized_point_position;
|
||||
|
||||
bool result_sign;
|
||||
u128 result;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
@@ -137,7 +137,7 @@ u128 StickyLogicalShiftRight(u128 operand, int amount) {
|
||||
}
|
||||
|
||||
if (operand.lower != 0 || operand.upper != 0) {
|
||||
return u128(1);
|
||||
return u128(1, 0);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -22,22 +22,13 @@ struct u128 {
|
||||
u128(u128&&) = default;
|
||||
u128& operator=(const u128&) = default;
|
||||
u128& operator=(u128&&) = default;
|
||||
|
||||
u128(u64 lower_, u64 upper_)
|
||||
: lower(lower_), upper(upper_) {}
|
||||
|
||||
template<typename T>
|
||||
/* implicit */ u128(T value)
|
||||
: lower(value), upper(0) {
|
||||
static_assert(std::is_integral_v<T>);
|
||||
static_assert(mcl::bitsizeof<T> <= mcl::bitsizeof<u64>);
|
||||
}
|
||||
explicit u128(u64 lower_, u64 upper_) : lower(lower_), upper(upper_) {}
|
||||
|
||||
u64 lower = 0;
|
||||
u64 upper = 0;
|
||||
|
||||
template<size_t bit_position>
|
||||
bool Bit() const {
|
||||
[[nodiscard]] inline bool Bit() const {
|
||||
static_assert(bit_position < 128);
|
||||
if constexpr (bit_position < 64) {
|
||||
return mcl::bit::get_bit<bit_position>(lower);
|
||||
|
||||
@@ -70,14 +70,15 @@ constexpr DecodeTable<V> GetDecodeTable() {
|
||||
|
||||
/// In practice it must always suceed, otherwise something else unrelated would have gone awry
|
||||
template<typename V>
|
||||
std::reference_wrapper<const Matcher<V>> Decode(u32 instruction) {
|
||||
std::optional<std::reference_wrapper<const Matcher<V>>> Decode(u32 instruction) {
|
||||
alignas(64) static const auto table = GetDecodeTable<V>();
|
||||
const auto& subtable = table[detail::ToFastLookupIndex(instruction)];
|
||||
auto iter = std::find_if(subtable.begin(), subtable.end(), [instruction](const auto& matcher) {
|
||||
return matcher.Matches(instruction);
|
||||
});
|
||||
DEBUG_ASSERT(iter != subtable.end());
|
||||
return std::reference_wrapper<const Matcher<V>>(*iter);
|
||||
return iter != subtable.end()
|
||||
? std::optional{ std::reference_wrapper<const Matcher<V>>(*iter) }
|
||||
: std::nullopt;
|
||||
}
|
||||
|
||||
template<typename V>
|
||||
|
||||
@@ -25,7 +25,11 @@ void Translate(IR::Block& block, LocationDescriptor descriptor, MemoryReadCodeFu
|
||||
const u64 pc = visitor.ir.current_location->PC();
|
||||
if (const auto instruction = memory_read_code(pc)) {
|
||||
auto decoder = Decode<TranslatorVisitor>(*instruction);
|
||||
should_continue = decoder.get().call(visitor, *instruction);
|
||||
if (decoder) {
|
||||
should_continue = decoder->get().call(visitor, *instruction);
|
||||
} else {
|
||||
should_continue = visitor.RaiseException(Exception::UnallocatedEncoding);
|
||||
}
|
||||
} else {
|
||||
should_continue = visitor.RaiseException(Exception::NoExecuteFault);
|
||||
}
|
||||
@@ -45,13 +49,15 @@ bool TranslateSingleInstruction(IR::Block& block, LocationDescriptor descriptor,
|
||||
|
||||
bool should_continue = true;
|
||||
auto const decoder = Decode<TranslatorVisitor>(instruction);
|
||||
should_continue = decoder.get().call(visitor, instruction);
|
||||
if (decoder) {
|
||||
should_continue = decoder->get().call(visitor, instruction);
|
||||
} else {
|
||||
should_continue = false;
|
||||
}
|
||||
|
||||
visitor.ir.current_location = visitor.ir.current_location->AdvancePC(4);
|
||||
block.CycleCount()++;
|
||||
|
||||
block.SetEndLocation(*visitor.ir.current_location);
|
||||
|
||||
return should_continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -228,7 +228,7 @@ public:
|
||||
}
|
||||
|
||||
ResultAndCarry<U32> RotateRight(const U32& value_in, const U8& shift_amount, const U1& carry_in) {
|
||||
const auto result = Inst<U32>(Opcode::RotateRight32, value_in, shift_amount, carry_in);
|
||||
const auto result = Inst<U32>(Opcode::BitRotateRight32, value_in, shift_amount, carry_in);
|
||||
const auto carry_out = Inst<U1>(Opcode::GetCarryFromOp, result);
|
||||
return {result, carry_out};
|
||||
}
|
||||
@@ -265,9 +265,9 @@ public:
|
||||
|
||||
U32U64 RotateRight(const U32U64& value_in, const U8& shift_amount) {
|
||||
if (value_in.GetType() == Type::U32) {
|
||||
return Inst<U32>(Opcode::RotateRight32, value_in, shift_amount, Imm1(0));
|
||||
return Inst<U32>(Opcode::BitRotateRight32, value_in, shift_amount, Imm1(0));
|
||||
} else {
|
||||
return Inst<U64>(Opcode::RotateRight64, value_in, shift_amount);
|
||||
return Inst<U64>(Opcode::BitRotateRight64, value_in, shift_amount);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
@@ -45,8 +45,8 @@ constexpr bool IsArithmeticShift(const Opcode op) noexcept {
|
||||
|
||||
/// @brief Determines whether or not this instruction performs a logical shift.
|
||||
constexpr bool IsCircularShift(const Opcode op) noexcept {
|
||||
return op == Opcode::RotateRight32
|
||||
|| op == Opcode::RotateRight64
|
||||
return op == Opcode::BitRotateRight32
|
||||
|| op == Opcode::BitRotateRight64
|
||||
|| op == Opcode::RotateRightExtended;
|
||||
}
|
||||
|
||||
|
||||
@@ -46,8 +46,9 @@ OPCODE(LogicalShiftRight32, U32, U32,
|
||||
OPCODE(LogicalShiftRight64, U64, U64, U8 )
|
||||
OPCODE(ArithmeticShiftRight32, U32, U32, U8, U1 )
|
||||
OPCODE(ArithmeticShiftRight64, U64, U64, U8 )
|
||||
OPCODE(RotateRight32, U32, U32, U8, U1 )
|
||||
OPCODE(RotateRight64, U64, U64, U8 )
|
||||
// windows.h defines RotateRight64 and RotateRight32
|
||||
OPCODE(BitRotateRight32, U32, U32, U8, U1 )
|
||||
OPCODE(BitRotateRight64, U64, U64, U8 )
|
||||
OPCODE(RotateRightExtended, U32, U32, U1 )
|
||||
OPCODE(LogicalShiftLeftMasked32, U32, U32, U32 )
|
||||
OPCODE(LogicalShiftLeftMasked64, U64, U64, U64 )
|
||||
|
||||
@@ -1072,12 +1072,12 @@ static void ConstantPropagation(IR::Block& block) {
|
||||
ReplaceUsesWith(inst, false, Safe::ArithmeticShiftRight<u64>(inst.GetArg(0).GetU64(), inst.GetArg(1).GetU8()));
|
||||
}
|
||||
break;
|
||||
case Op::RotateRight32:
|
||||
case Op::BitRotateRight32:
|
||||
if (FoldShifts(inst)) {
|
||||
ReplaceUsesWith(inst, true, mcl::bit::rotate_right<u32>(inst.GetArg(0).GetU32(), inst.GetArg(1).GetU8()));
|
||||
}
|
||||
break;
|
||||
case Op::RotateRight64:
|
||||
case Op::BitRotateRight64:
|
||||
if (FoldShifts(inst)) {
|
||||
ReplaceUsesWith(inst, false, mcl::bit::rotate_right<u64>(inst.GetArg(0).GetU64(), inst.GetArg(1).GetU8()));
|
||||
}
|
||||
|
||||
@@ -800,6 +800,10 @@ Result NPad::DisconnectNpad(u64 aruid, Core::HID::NpadIdType npad_id) {
|
||||
auto& controller = GetControllerFromNpadIdType(aruid, npad_id);
|
||||
|
||||
auto* shared_memory = controller.shared_memory;
|
||||
if (!shared_memory) {
|
||||
LOG_WARNING(Service_HID, "DisconnectNpad: shared_memory is null for npad_id={}", npad_id);
|
||||
return ResultSuccess;
|
||||
}
|
||||
// Don't reset shared_memory->assignment_mode this value is persistent
|
||||
shared_memory->style_tag.raw = Core::HID::NpadStyleSet::None; // Zero out
|
||||
shared_memory->device_type.raw = 0;
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "common/logging.h"
|
||||
#include "common/param_package.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/thread.h"
|
||||
#include "input_common/drivers/udp_client.h"
|
||||
#include "input_common/helpers/udp_protocol.h"
|
||||
|
||||
@@ -135,6 +136,7 @@ private:
|
||||
};
|
||||
|
||||
static void SocketLoop(Socket* socket) {
|
||||
Common::SetCurrentThreadName("cemuhookWorker");
|
||||
socket->StartReceive();
|
||||
socket->StartSend(Socket::clock::now());
|
||||
socket->Loop();
|
||||
@@ -330,9 +332,11 @@ void UDPClient::OnPadData(Response::PadData data, std::size_t client) {
|
||||
}
|
||||
|
||||
void UDPClient::StartCommunication(std::size_t client, const std::string& host, u16 port) {
|
||||
SocketCallback callback{[this](Response::Version version) { OnVersion(version); },
|
||||
[this](Response::PortInfo info) { OnPortInfo(info); },
|
||||
[this, client](Response::PadData data) { OnPadData(data, client); }};
|
||||
SocketCallback callback{
|
||||
[this](Response::Version version) { OnVersion(version); },
|
||||
[this](Response::PortInfo info) { OnPortInfo(info); },
|
||||
[this, client](Response::PadData data) { OnPadData(data, client); }
|
||||
};
|
||||
LOG_INFO(Input, "Starting communication with UDP input server on {}:{}", host, port);
|
||||
clients[client].uuid = GetHostUUID(host);
|
||||
clients[client].host = host;
|
||||
@@ -570,9 +574,7 @@ bool UDPClient::IsStickInverted(const Common::ParamPackage& params) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void TestCommunication(const std::string& host, u16 port,
|
||||
const std::function<void()>& success_callback,
|
||||
const std::function<void()>& failure_callback) {
|
||||
void TestCommunication(const std::string& host, u16 port, const std::function<void()>& success_callback, const std::function<void()>& failure_callback) {
|
||||
std::thread([=] {
|
||||
Common::Event success_event;
|
||||
SocketCallback callback{
|
||||
@@ -605,40 +607,38 @@ CalibrationConfigurationJob::CalibrationConfigurationJob(
|
||||
u16 max_y{};
|
||||
|
||||
Status current_status{Status::Initialized};
|
||||
SocketCallback callback{[](Response::Version) {}, [](Response::PortInfo) {},
|
||||
[&](Response::PadData data) {
|
||||
constexpr u16 CALIBRATION_THRESHOLD = 100;
|
||||
SocketCallback callback{[](Response::Version) {}, [](Response::PortInfo) {}, [&](Response::PadData data) {
|
||||
constexpr u16 CALIBRATION_THRESHOLD = 100;
|
||||
|
||||
if (current_status == Status::Initialized) {
|
||||
// Receiving data means the communication is ready now
|
||||
current_status = Status::Ready;
|
||||
status_callback(current_status);
|
||||
}
|
||||
if (data.touch[0].is_active == 0) {
|
||||
return;
|
||||
}
|
||||
LOG_DEBUG(Input, "Current touch: {} {}", data.touch[0].x,
|
||||
data.touch[0].y);
|
||||
min_x = (std::min)(min_x, static_cast<u16>(data.touch[0].x));
|
||||
min_y = (std::min)(min_y, static_cast<u16>(data.touch[0].y));
|
||||
if (current_status == Status::Ready) {
|
||||
// First touch - min data (min_x/min_y)
|
||||
current_status = Status::Stage1Completed;
|
||||
status_callback(current_status);
|
||||
}
|
||||
if (data.touch[0].x - min_x > CALIBRATION_THRESHOLD &&
|
||||
data.touch[0].y - min_y > CALIBRATION_THRESHOLD) {
|
||||
// Set the current position as max value and finishes
|
||||
// configuration
|
||||
max_x = data.touch[0].x;
|
||||
max_y = data.touch[0].y;
|
||||
current_status = Status::Completed;
|
||||
data_callback(min_x, min_y, max_x, max_y);
|
||||
status_callback(current_status);
|
||||
if (current_status == Status::Initialized) {
|
||||
// Receiving data means the communication is ready now
|
||||
current_status = Status::Ready;
|
||||
status_callback(current_status);
|
||||
}
|
||||
if (data.touch[0].is_active == 0) {
|
||||
return;
|
||||
}
|
||||
LOG_DEBUG(Input, "Current touch: {} {}", data.touch[0].x, data.touch[0].y);
|
||||
min_x = (std::min)(min_x, u16(data.touch[0].x));
|
||||
min_y = (std::min)(min_y, u16(data.touch[0].y));
|
||||
if (current_status == Status::Ready) {
|
||||
// First touch - min data (min_x/min_y)
|
||||
current_status = Status::Stage1Completed;
|
||||
status_callback(current_status);
|
||||
}
|
||||
if (data.touch[0].x - min_x > CALIBRATION_THRESHOLD &&
|
||||
data.touch[0].y - min_y > CALIBRATION_THRESHOLD) {
|
||||
// Set the current position as max value and finishes
|
||||
// configuration
|
||||
max_x = data.touch[0].x;
|
||||
max_y = data.touch[0].y;
|
||||
current_status = Status::Completed;
|
||||
data_callback(min_x, min_y, max_x, max_y);
|
||||
status_callback(current_status);
|
||||
|
||||
complete_event.Set();
|
||||
}
|
||||
}};
|
||||
complete_event.Set();
|
||||
}
|
||||
}};
|
||||
Socket socket{host, port, std::move(callback)};
|
||||
std::thread worker_thread{SocketLoop, &socket};
|
||||
complete_event.Wait();
|
||||
|
||||
@@ -329,7 +329,6 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
|
||||
INSERT(Settings, current_user, QString(), QString());
|
||||
INSERT(Settings, serial_unit, tr("Unit Serial"), QString());
|
||||
INSERT(Settings, serial_battery, tr("Battery Serial"), QString());
|
||||
INSERT(Settings, product_model, tr("Product Model"), QString());
|
||||
INSERT(Settings, debug_knobs, tr("Debug knobs"), QString());
|
||||
|
||||
// Controls
|
||||
|
||||
@@ -142,14 +142,12 @@ Id GetCbuf(EmitContext& ctx, Id result_type, Id UniformDefinitions::*member_ptr,
|
||||
|
||||
const auto is_float = UniformDefinitions::IsFloat(member_ptr);
|
||||
const auto num_elements = UniformDefinitions::NumElements(member_ptr);
|
||||
const std::array zero_vec{
|
||||
is_float ? ctx.Const(0.0f) : ctx.Const(0u),
|
||||
is_float ? ctx.Const(0.0f) : ctx.Const(0u),
|
||||
is_float ? ctx.Const(0.0f) : ctx.Const(0u),
|
||||
is_float ? ctx.Const(0.0f) : ctx.Const(0u),
|
||||
};
|
||||
auto const zero_const = is_float ? ctx.Const(0.0f) : ctx.Const(0u);
|
||||
const std::array zero_vec{zero_const, zero_const, zero_const, zero_const};
|
||||
const Id cond = ctx.OpULessThanEqual(ctx.TypeBool(), buffer_offset, ctx.Const(0xFFFFu));
|
||||
const Id zero = ctx.OpCompositeConstruct(result_type, std::span(zero_vec.data(), num_elements));
|
||||
const Id zero = num_elements > 1
|
||||
? ctx.OpCompositeConstruct(result_type, std::span(zero_vec.data(), num_elements))
|
||||
: zero_const;
|
||||
return ctx.OpSelect(result_type, cond, val, zero);
|
||||
}
|
||||
|
||||
|
||||
@@ -126,16 +126,14 @@ public:
|
||||
current_query = nullptr;
|
||||
amend_value = 0;
|
||||
accumulation_value = 0;
|
||||
queries_prefix_scan_pass = std::make_unique<QueriesPrefixScanPass>(
|
||||
device, scheduler, descriptor_pool, compute_pass_descriptor_queue);
|
||||
queries_prefix_scan_pass.emplace(device, scheduler, descriptor_pool, compute_pass_descriptor_queue);
|
||||
|
||||
const VkBufferCreateInfo buffer_ci = {
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.size = 8,
|
||||
.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT |
|
||||
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
|
||||
.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
|
||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||
.queueFamilyIndexCount = 0,
|
||||
.pQueueFamilyIndices = nullptr,
|
||||
@@ -592,8 +590,7 @@ private:
|
||||
VideoCommon::HostQueryBase* current_query;
|
||||
bool has_started{};
|
||||
std::mutex flush_guard;
|
||||
|
||||
std::unique_ptr<QueriesPrefixScanPass> queries_prefix_scan_pass;
|
||||
std::optional<QueriesPrefixScanPass> queries_prefix_scan_pass;
|
||||
};
|
||||
|
||||
// Transform feedback queries
|
||||
@@ -1176,35 +1173,21 @@ private:
|
||||
} // namespace
|
||||
|
||||
struct QueryCacheRuntimeImpl {
|
||||
QueryCacheRuntimeImpl(QueryCacheRuntime& runtime, VideoCore::RasterizerInterface* rasterizer_,
|
||||
Tegra::MaxwellDeviceMemoryManager& device_memory_,
|
||||
Vulkan::BufferCache& buffer_cache_, const Device& device_,
|
||||
const MemoryAllocator& memory_allocator_, Scheduler& scheduler_,
|
||||
StagingBufferPool& staging_pool_,
|
||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue,
|
||||
DescriptorPool& descriptor_pool, TextureCache& texture_cache_)
|
||||
: rasterizer{rasterizer_}, device_memory{device_memory_}, buffer_cache{buffer_cache_},
|
||||
device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_},
|
||||
staging_pool{staging_pool_}, guest_streamer(0, runtime),
|
||||
sample_streamer(static_cast<size_t>(QueryType::ZPassPixelCount64), runtime, rasterizer,
|
||||
texture_cache_, device, scheduler, memory_allocator,
|
||||
compute_pass_descriptor_queue, descriptor_pool),
|
||||
tfb_streamer(static_cast<size_t>(QueryType::StreamingByteCount), runtime, device,
|
||||
scheduler, memory_allocator, staging_pool),
|
||||
primitives_succeeded_streamer(
|
||||
static_cast<size_t>(QueryType::StreamingPrimitivesSucceeded), runtime, tfb_streamer,
|
||||
device_memory_),
|
||||
primitives_needed_minus_succeeded_streamer(
|
||||
static_cast<size_t>(QueryType::StreamingPrimitivesNeededMinusSucceeded), runtime, 0u),
|
||||
hcr_setup{}, hcr_is_set{}, is_hcr_running{}, maxwell3d{} {
|
||||
QueryCacheRuntimeImpl(QueryCacheRuntime& runtime, VideoCore::RasterizerInterface* rasterizer_, Tegra::MaxwellDeviceMemoryManager& device_memory_, Vulkan::BufferCache& buffer_cache_, const Device& device_, const MemoryAllocator& memory_allocator_, Scheduler& scheduler_, StagingBufferPool& staging_pool_, ComputePassDescriptorQueue& compute_pass_descriptor_queue, DescriptorPool& descriptor_pool, TextureCache& texture_cache_)
|
||||
: rasterizer{rasterizer_}, device_memory{device_memory_}, buffer_cache{buffer_cache_}
|
||||
, device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_}
|
||||
, staging_pool{staging_pool_}, guest_streamer(0, runtime)
|
||||
, sample_streamer(size_t(QueryType::ZPassPixelCount64), runtime, rasterizer, texture_cache_, device, scheduler, memory_allocator, compute_pass_descriptor_queue, descriptor_pool)
|
||||
, tfb_streamer(size_t(QueryType::StreamingByteCount), runtime, device, scheduler, memory_allocator, staging_pool)
|
||||
, primitives_succeeded_streamer(size_t(QueryType::StreamingPrimitivesSucceeded), runtime, tfb_streamer, device_memory_)
|
||||
, primitives_needed_minus_succeeded_streamer(size_t(QueryType::StreamingPrimitivesNeededMinusSucceeded), runtime, 0u)
|
||||
, hcr_setup{}, hcr_is_set{}, is_hcr_running{}, maxwell3d{} {
|
||||
|
||||
hcr_setup.sType = VK_STRUCTURE_TYPE_CONDITIONAL_RENDERING_BEGIN_INFO_EXT;
|
||||
hcr_setup.pNext = nullptr;
|
||||
hcr_setup.flags = 0;
|
||||
|
||||
conditional_resolve_pass = std::make_unique<ConditionalRenderingResolvePass>(
|
||||
device, scheduler, descriptor_pool, compute_pass_descriptor_queue);
|
||||
|
||||
conditional_resolve_pass.emplace(device, scheduler, descriptor_pool, compute_pass_descriptor_queue);
|
||||
const VkBufferCreateInfo buffer_ci = {
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
@@ -1241,7 +1224,7 @@ struct QueryCacheRuntimeImpl {
|
||||
std::vector<std::vector<VkBufferCopy>> copies_setup;
|
||||
|
||||
// Host conditional rendering data
|
||||
std::unique_ptr<ConditionalRenderingResolvePass> conditional_resolve_pass;
|
||||
std::optional<ConditionalRenderingResolvePass> conditional_resolve_pass;
|
||||
vk::Buffer hcr_resolve_buffer;
|
||||
VkConditionalRenderingBeginInfoEXT hcr_setup;
|
||||
VkBuffer hcr_buffer;
|
||||
@@ -1253,13 +1236,7 @@ struct QueryCacheRuntimeImpl {
|
||||
Maxwell3D* maxwell3d;
|
||||
};
|
||||
|
||||
QueryCacheRuntime::QueryCacheRuntime(VideoCore::RasterizerInterface* rasterizer,
|
||||
Tegra::MaxwellDeviceMemoryManager& device_memory_,
|
||||
Vulkan::BufferCache& buffer_cache_, const Device& device_,
|
||||
const MemoryAllocator& memory_allocator_,
|
||||
Scheduler& scheduler_, StagingBufferPool& staging_pool_,
|
||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue,
|
||||
DescriptorPool& descriptor_pool, TextureCache& texture_cache_) {
|
||||
QueryCacheRuntime::QueryCacheRuntime(VideoCore::RasterizerInterface* rasterizer, Tegra::MaxwellDeviceMemoryManager& device_memory_, Vulkan::BufferCache& buffer_cache_, const Device& device_, const MemoryAllocator& memory_allocator_, Scheduler& scheduler_, StagingBufferPool& staging_pool_, ComputePassDescriptorQueue& compute_pass_descriptor_queue, DescriptorPool& descriptor_pool, TextureCache& texture_cache_) {
|
||||
impl = std::make_unique<QueryCacheRuntimeImpl>(
|
||||
*this, rasterizer, device_memory_, buffer_cache_, device_, memory_allocator_, scheduler_,
|
||||
staging_pool_, compute_pass_descriptor_queue, descriptor_pool, texture_cache_);
|
||||
@@ -1484,13 +1461,11 @@ void QueryCacheRuntime::Barriers(bool is_prebarrier) {
|
||||
impl->scheduler.RequestOutsideRenderPassOperationContext();
|
||||
if (is_prebarrier) {
|
||||
impl->scheduler.Record([](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, READ_BARRIER);
|
||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, READ_BARRIER);
|
||||
});
|
||||
} else {
|
||||
impl->scheduler.Record([](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, WRITE_BARRIER);
|
||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, WRITE_BARRIER);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1583,8 +1558,7 @@ void QueryCacheRuntime::SyncValues(std::span<SyncValuesType> values, VkBuffer ba
|
||||
}
|
||||
|
||||
impl->scheduler.RequestOutsideRenderPassOperationContext();
|
||||
impl->scheduler.Record([src_buffer, dst_buffers = std::move(impl->buffers_to_upload_to),
|
||||
vk_copies = std::move(impl->copies_setup)](vk::CommandBuffer cmdbuf) {
|
||||
impl->scheduler.Record([src_buffer, dst_buffers = std::move(impl->buffers_to_upload_to), vk_copies = std::move(impl->copies_setup)](vk::CommandBuffer cmdbuf) {
|
||||
size_t size = dst_buffers.size();
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
cmdbuf.CopyBuffer(src_buffer, dst_buffers[i].first, vk_copies[i]);
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include <variant>
|
||||
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
||||
|
||||
namespace Vulkan {
|
||||
@@ -12,20 +15,15 @@ namespace Vulkan {
|
||||
class Device;
|
||||
class Scheduler;
|
||||
|
||||
struct DescriptorUpdateEntry {
|
||||
struct Empty {};
|
||||
|
||||
union DescriptorUpdateEntry {
|
||||
DescriptorUpdateEntry() = default;
|
||||
DescriptorUpdateEntry(VkDescriptorImageInfo image_) : image{image_} {}
|
||||
DescriptorUpdateEntry(VkDescriptorBufferInfo buffer_) : buffer{buffer_} {}
|
||||
DescriptorUpdateEntry(VkBufferView texel_buffer_) : texel_buffer{texel_buffer_} {}
|
||||
|
||||
union {
|
||||
Empty empty{};
|
||||
VkDescriptorImageInfo image;
|
||||
VkDescriptorBufferInfo buffer;
|
||||
VkBufferView texel_buffer;
|
||||
};
|
||||
std::monostate empty{};
|
||||
VkDescriptorImageInfo image;
|
||||
VkDescriptorBufferInfo buffer;
|
||||
VkBufferView texel_buffer;
|
||||
};
|
||||
|
||||
class UpdateDescriptorQueue final {
|
||||
|
||||
Reference in New Issue
Block a user