mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-22 23:50:32 +00:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ee833674fb | |||
| 538b535676 | |||
| a67eaaaace | |||
| 9b760e7c99 | |||
| 148586d2f6 | |||
| 1ba7705279 | |||
| 84b78abe1c | |||
| c717ad0f59 | |||
| 54c3d10b86 | |||
| bdf60d79a0 | |||
| 2068b5d452 | |||
| 1b482fa99b | |||
| b6ee847947 | |||
| 6c16440996 | |||
| 09c583506b | |||
| 894add43f3 | |||
| d142b5dd6a | |||
| 0c2894eabf |
Vendored
+327
-386
File diff suppressed because it is too large
Load Diff
Vendored
+326
-383
File diff suppressed because it is too large
Load Diff
Vendored
+326
-383
File diff suppressed because it is too large
Load Diff
Vendored
+326
-383
File diff suppressed because it is too large
Load Diff
Vendored
+326
-383
File diff suppressed because it is too large
Load Diff
Vendored
+327
-384
File diff suppressed because it is too large
Load Diff
Vendored
+326
-383
File diff suppressed because it is too large
Load Diff
Vendored
+324
-381
File diff suppressed because it is too large
Load Diff
Vendored
+326
-383
File diff suppressed because it is too large
Load Diff
Vendored
+326
-383
File diff suppressed because it is too large
Load Diff
Vendored
+326
-383
File diff suppressed because it is too large
Load Diff
Vendored
+326
-383
File diff suppressed because it is too large
Load Diff
Vendored
+326
-383
File diff suppressed because it is too large
Load Diff
Vendored
+413
-470
File diff suppressed because it is too large
Load Diff
Vendored
+326
-383
File diff suppressed because it is too large
Load Diff
Vendored
+326
-383
File diff suppressed because it is too large
Load Diff
Vendored
+326
-383
File diff suppressed because it is too large
Load Diff
Vendored
+326
-383
File diff suppressed because it is too large
Load Diff
Vendored
+326
-383
File diff suppressed because it is too large
Load Diff
Vendored
+326
-383
File diff suppressed because it is too large
Load Diff
Vendored
+326
-383
File diff suppressed because it is too large
Load Diff
Vendored
+326
-383
File diff suppressed because it is too large
Load Diff
Vendored
+326
-383
File diff suppressed because it is too large
Load Diff
Vendored
+326
-383
File diff suppressed because it is too large
Load Diff
Vendored
+326
-383
File diff suppressed because it is too large
Load Diff
Vendored
+326
-383
File diff suppressed because it is too large
Load Diff
Vendored
+326
-383
File diff suppressed because it is too large
Load Diff
+1
@@ -31,6 +31,7 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
|
||||
RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"),
|
||||
ENABLE_BUFFER_HISTORY("enable_buffer_history"),
|
||||
USE_OPTIMIZED_VERTEX_BUFFERS("use_optimized_vertex_buffers"),
|
||||
ENABLE_GPU_BUFFER_READBACK("enable_gpu_buffer_readback"),
|
||||
SYNC_MEMORY_OPERATIONS("sync_memory_operations"),
|
||||
BUFFER_REORDER_DISABLE("disable_buffer_reorder"),
|
||||
RENDERER_DEBUG("debug"),
|
||||
|
||||
+7
@@ -806,6 +806,13 @@ abstract class SettingsItem(
|
||||
descriptionId = R.string.enable_buffer_history_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.ENABLE_GPU_BUFFER_READBACK,
|
||||
titleId = R.string.enable_gpu_buffer_readback,
|
||||
descriptionId = R.string.enable_gpu_buffer_readback_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.USE_OPTIMIZED_VERTEX_BUFFERS,
|
||||
|
||||
+1
@@ -292,6 +292,7 @@ class SettingsFragmentPresenter(
|
||||
add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key)
|
||||
add(BooleanSetting.RENDERER_REACTIVE_FLUSHING.key)
|
||||
add(BooleanSetting.ENABLE_BUFFER_HISTORY.key)
|
||||
add(BooleanSetting.ENABLE_GPU_BUFFER_READBACK.key)
|
||||
add(BooleanSetting.USE_OPTIMIZED_VERTEX_BUFFERS.key)
|
||||
|
||||
add(HeaderSetting(R.string.hacks))
|
||||
|
||||
@@ -573,6 +573,8 @@
|
||||
<string name="gpu_log_level_description">مستوى التفاصيل لسجلات وحدة معالجة الرسومات (كلما زاد المستوى، زادت التفاصيل وزادت التكاليف الإضافية)</string>
|
||||
<string name="gpu_log_vulkan_calls">تسجيل استدعاءات واجهة برمجة تطبيقات Vulkan</string>
|
||||
<string name="gpu_log_vulkan_calls_description">تتبع جميع استدعاءات واجهة برمجة تطبيقات Vulkan في المخزن المؤقت الحلقي</string>
|
||||
<string name="gpu_log_shader_dumps">تفريغ التظليل</string>
|
||||
<string name="gpu_log_shader_dumps_description">حفظ تظليل SPIR-V المجمع في ملفات</string>
|
||||
<string name="gpu_log_memory_tracking">تتبع ذاكرة وحدة معالجة الرسومات</string>
|
||||
<string name="gpu_log_memory_tracking_description">مراقبة تخصيصات ذاكرة وحدة معالجة الرسومات وإلغاء تخصيصها</string>
|
||||
<string name="gpu_log_driver_debug">معلومات تصحيح أخطاء برنامج التشغيل</string>
|
||||
|
||||
@@ -567,6 +567,8 @@
|
||||
<string name="gpu_log_level_description">Nivel de detalle de los registros de la GPU (más alto = más detalles, más sobrecarga)</string>
|
||||
<string name="gpu_log_vulkan_calls">Registros de llamadas del API de Vulkan</string>
|
||||
<string name="gpu_log_vulkan_calls_description">Rastrear todas las llamadas del API de Vulkan en el búfer circular</string>
|
||||
<string name="gpu_log_shader_dumps">Volcar sombreadores</string>
|
||||
<string name="gpu_log_shader_dumps_description">Guardar sombreadores compilados de SPIR-V en archivos</string>
|
||||
<string name="gpu_log_memory_tracking">Rastrear memoria de la GPU</string>
|
||||
<string name="gpu_log_memory_tracking_description">Monitorizar las asignaciones y desasignaciones de memoria de la GPU</string>
|
||||
<string name="gpu_log_driver_debug">Información de depuración del controlador</string>
|
||||
|
||||
@@ -527,6 +527,8 @@
|
||||
<string name="gpu_logging_header">Journalisation GPU</string>
|
||||
<string name="gpu_log_level">Niveau de journalisation</string>
|
||||
<string name="gpu_log_vulkan_calls">Journaliser les appels API Vulkan</string>
|
||||
<string name="gpu_log_shader_dumps">Extraire les shaders</string>
|
||||
<string name="gpu_log_shader_dumps_description">Sauvegarder le shader SPIR-V complié dans les fichiers</string>
|
||||
<string name="gpu_log_memory_tracking">Monitorer la mémoire GPU</string>
|
||||
<string name="gpu_log_memory_tracking_description">Monitorer les allocations et désallocations de la mémoire GPU</string>
|
||||
<string name="gpu_log_driver_debug">Informations de débogage du pilote</string>
|
||||
|
||||
@@ -566,6 +566,8 @@
|
||||
<string name="gpu_log_level_description">Уровень детализации логов ГПУ (больше значение = больше деталей, выше нагрузка)</string>
|
||||
<string name="gpu_log_vulkan_calls">Записывать вызовы Vulkan API</string>
|
||||
<string name="gpu_log_vulkan_calls_description">Отслеживать все вызовы Vulkan API в кольцевом буфере</string>
|
||||
<string name="gpu_log_shader_dumps">Выгрузить шейдеры</string>
|
||||
<string name="gpu_log_shader_dumps_description">Сохранять скомпилированные SPIR-V шейдеры в файлы</string>
|
||||
<string name="gpu_log_memory_tracking">Отслеживать память ГПУ</string>
|
||||
<string name="gpu_log_memory_tracking_description">Мониторить выделение и освобождение памяти ГПУ</string>
|
||||
<string name="gpu_log_driver_debug">Отладочная информация драйвера</string>
|
||||
|
||||
@@ -569,6 +569,8 @@
|
||||
<string name="gpu_log_level_description">Рівень подробиць у журналі ГП (вищий = більше подробиць, більший вплив на швидкодію)</string>
|
||||
<string name="gpu_log_vulkan_calls">Записувати виклики API Vulkan</string>
|
||||
<string name="gpu_log_vulkan_calls_description">Відстежувати всі виклики API Vulkan у кільцевому буфері</string>
|
||||
<string name="gpu_log_shader_dumps">Зберігати шейдери</string>
|
||||
<string name="gpu_log_shader_dumps_description">Зберігати до файлів скомпільовані шейдери SPIR-V</string>
|
||||
<string name="gpu_log_memory_tracking">Відстежувати пам’ять ГП</string>
|
||||
<string name="gpu_log_memory_tracking_description">Відстежувати використання і звільнення пам’яті ГП</string>
|
||||
<string name="gpu_log_driver_debug">Зневаджувальна інформація драйверів</string>
|
||||
|
||||
@@ -563,6 +563,8 @@
|
||||
<string name="gpu_log_level_description">GPU 日志的详细级别(数值越高 = 细节越多,开销越大)</string>
|
||||
<string name="gpu_log_vulkan_calls">记录 Vulkan API 调用</string>
|
||||
<string name="gpu_log_vulkan_calls_description">在环形缓冲区中跟踪所有 Vulkan API 调用</string>
|
||||
<string name="gpu_log_shader_dumps">转储着色器</string>
|
||||
<string name="gpu_log_shader_dumps_description">保存已编译的着色器 SPIR-V 到文件中</string>
|
||||
<string name="gpu_log_memory_tracking">追踪 GPU 内存</string>
|
||||
<string name="gpu_log_memory_tracking_description">监控 GPU 内存的分配和释放</string>
|
||||
<string name="gpu_log_driver_debug">驱动调试信息</string>
|
||||
|
||||
@@ -503,6 +503,8 @@
|
||||
<string name="renderer_reactive_flushing_description">Improves rendering accuracy in some games at the cost of performance.</string>
|
||||
<string name="enable_buffer_history">Enable buffer history</string>
|
||||
<string name="enable_buffer_history_description">Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games.</string>
|
||||
<string name="enable_gpu_buffer_readback">Enable GPU Buffer Readback</string>
|
||||
<string name="enable_gpu_buffer_readback_description">Preserves GPU-modified buffer data by reading it back before uploads. Some games require this to render certain effects properly. May cause issues if the hardware cannot handle the additional workload.</string>
|
||||
<string name="use_optimized_vertex_buffers">Optimized Vertex Buffers</string>
|
||||
<string name="use_optimized_vertex_buffers_description">Enables optimized vertex buffer binding for improved performance. Requires Mesa 26.0+ Turnip drivers/ QCOM drivers. Will crash on older Turnip drivers (25.3 and below).</string>
|
||||
|
||||
|
||||
+1
-1
@@ -350,7 +350,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::make_unique<Service::Process>(*std::move(process)), params);
|
||||
applet_manager.CreateAndInsertByFrontendAppletParameters(std::move(process), params);
|
||||
|
||||
if (Settings::values.gamecard_inserted) {
|
||||
if (Settings::values.gamecard_current_game) {
|
||||
|
||||
@@ -729,7 +729,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface(ensure_token_id);
|
||||
rb.PushIpcInterface(ctx, ensure_token_id);
|
||||
}
|
||||
|
||||
void LoadIdTokenCacheDeprecated(HLERequestContext& ctx) {
|
||||
@@ -921,7 +921,7 @@ void Module::Interface::GetProfile(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IProfile>(system, user_id, *profile_manager);
|
||||
rb.PushIpcInterface<IProfile>(ctx, system, user_id, *profile_manager);
|
||||
}
|
||||
|
||||
void Module::Interface::IsUserRegistrationRequestPermitted(HLERequestContext& ctx) {
|
||||
@@ -993,7 +993,7 @@ void Module::Interface::GetBaasAccountManagerForApplication(HLERequestContext& c
|
||||
LOG_DEBUG(Service_ACC, "called");
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IManagerForApplication>(system, profile_manager);
|
||||
rb.PushIpcInterface<IManagerForApplication>(ctx, system, profile_manager);
|
||||
}
|
||||
|
||||
void Module::Interface::IsUserAccountSwitchLocked(HLERequestContext& ctx) {
|
||||
@@ -1089,7 +1089,7 @@ void Module::Interface::GetProfileEditor(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IProfileEditor>(system, user_id, *profile_manager);
|
||||
rb.PushIpcInterface<IProfileEditor>(ctx, system, user_id, *profile_manager);
|
||||
}
|
||||
|
||||
void Module::Interface::GetBaasAccountAdministrator(HLERequestContext &ctx) {
|
||||
@@ -1100,7 +1100,7 @@ void Module::Interface::GetBaasAccountAdministrator(HLERequestContext &ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IAdministrator>(system, uuid);
|
||||
rb.PushIpcInterface<IAdministrator>(ctx, system, uuid);
|
||||
}
|
||||
|
||||
void Module::Interface::ListQualifiedUsers(HLERequestContext& ctx) {
|
||||
@@ -1143,7 +1143,7 @@ void Module::Interface::GetBaasAccountManagerForSystemService(HLERequestContext&
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IManagerForSystemService>(system, uuid);
|
||||
rb.PushIpcInterface<IManagerForSystemService>(ctx, system, uuid);
|
||||
}
|
||||
|
||||
void Module::Interface::StoreSaveDataThumbnailSystem(HLERequestContext& ctx) {
|
||||
|
||||
@@ -35,7 +35,7 @@ void IAsyncContext::GetSystemEvent(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(completion_event->GetReadableEvent());
|
||||
rb.PushCopyObjects(ctx, completion_event->GetReadableEvent());
|
||||
}
|
||||
|
||||
void IAsyncContext::Cancel(HLERequestContext& ctx) {
|
||||
|
||||
@@ -109,6 +109,10 @@ struct Applet {
|
||||
std::list<std::shared_ptr<Applet>> child_applets{};
|
||||
bool is_completed{};
|
||||
|
||||
std::shared_ptr<Applet> reserved_applet{};
|
||||
bool unwind_after_reserved{};
|
||||
bool is_winding{};
|
||||
|
||||
// Self state
|
||||
bool exit_locked{};
|
||||
s32 fatal_section_count{};
|
||||
|
||||
@@ -25,6 +25,13 @@ void AppletStorageChannel::Push(Kernel::KernelCore& kernel, std::shared_ptr<ISto
|
||||
m_event.Signal(kernel);
|
||||
}
|
||||
|
||||
void AppletStorageChannel::Unpop(Kernel::KernelCore& kernel, std::shared_ptr<IStorage> storage) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
m_data.emplace_front(std::move(storage));
|
||||
m_event.Signal(kernel);
|
||||
}
|
||||
|
||||
Result AppletStorageChannel::Pop(Kernel::KernelCore& kernel, std::shared_ptr<IStorage>* out_storage) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ public:
|
||||
~AppletStorageChannel();
|
||||
|
||||
void Push(Kernel::KernelCore& kernel, std::shared_ptr<IStorage> storage);
|
||||
void Unpop(Kernel::KernelCore& kernel, std::shared_ptr<IStorage> storage);
|
||||
Result Pop(Kernel::KernelCore& kernel, std::shared_ptr<IStorage>* out_storage);
|
||||
Kernel::KReadableEvent* GetEvent();
|
||||
|
||||
|
||||
@@ -267,7 +267,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::make_unique<Service::Process>(*std::move(overlay_process)), false);
|
||||
auto overlay_applet = std::make_shared<Applet>(m_system, 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,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
|
||||
|
||||
@@ -12,7 +15,10 @@ namespace Service::AM {
|
||||
|
||||
HidRegistration::HidRegistration(Core::System& system, Process& process) : m_process(process) {
|
||||
m_hid_server = system.ServiceManager().GetService<HID::IHidServer>("hid", true);
|
||||
this->RegisterCurrentProcess();
|
||||
}
|
||||
|
||||
void HidRegistration::RegisterCurrentProcess() {
|
||||
if (m_process.IsInitialized()) {
|
||||
m_hid_server->GetResourceManager()->RegisterAppletResourceUserId(m_process.GetProcessId(),
|
||||
true);
|
||||
|
||||
@@ -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,6 +27,7 @@ public:
|
||||
explicit HidRegistration(Core::System& system, Process& process);
|
||||
~HidRegistration();
|
||||
|
||||
void RegisterCurrentProcess();
|
||||
void EnableAppletToGetInput(bool enable);
|
||||
|
||||
private:
|
||||
|
||||
@@ -171,6 +171,18 @@ void LifecycleManager::SignalSystemEventIfNeeded(Kernel::KernelCore& kernel) {
|
||||
}
|
||||
}
|
||||
|
||||
void LifecycleManager::ResetForRelaunch() {
|
||||
m_unordered_messages.clear();
|
||||
|
||||
m_activity_state = ActivityState::BackgroundVisible;
|
||||
m_requested_focus_state = FocusState{};
|
||||
m_acknowledged_focus_state = FocusState{};
|
||||
m_has_focus_state_changed = true;
|
||||
|
||||
m_suspend_mode = SuspendMode::NoOverride;
|
||||
m_forced_suspend = false;
|
||||
}
|
||||
|
||||
bool LifecycleManager::PopMessage(Kernel::KernelCore& kernel, AppletMessage* out_message) {
|
||||
const auto message = this->PopMessageInOrderOfPriority();
|
||||
this->SignalSystemEventIfNeeded(kernel);
|
||||
|
||||
@@ -138,6 +138,8 @@ public:
|
||||
void PushUnorderedMessage(Kernel::KernelCore& kernel, AppletMessage message);
|
||||
bool PopMessage(Kernel::KernelCore& kernel, AppletMessage* out_message);
|
||||
|
||||
void ResetForRelaunch();
|
||||
|
||||
private:
|
||||
FocusState GetFocusStateWhileForegroundObscured() const;
|
||||
FocusState GetFocusStateWhileBackground(bool is_obscured) const;
|
||||
|
||||
@@ -40,23 +40,23 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
[[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) {
|
||||
[[nodiscard]] inline 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) {
|
||||
// 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) {
|
||||
// Try to load the process.
|
||||
auto process = std::make_optional<Process>(system);
|
||||
auto process = std::make_unique<Process>(system);
|
||||
if (process->Initialize(*out_loader, out_load_result)) {
|
||||
return process;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
std::optional<Process> CreateProcess(Core::System& system, u64 program_id, u8 minimum_key_generation, u8 maximum_key_generation) {
|
||||
std::unique_ptr<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{};
|
||||
|
||||
@@ -66,7 +66,7 @@ std::optional<Process> CreateProcess(Core::System& system, u64 program_id, u8 mi
|
||||
|
||||
// Ensure we retrieved a program NCA.
|
||||
if (!nca_raw) {
|
||||
return std::nullopt;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Ensure we have a suitable version.
|
||||
@@ -76,7 +76,7 @@ std::optional<Process> CreateProcess(Core::System& system, u64 program_id, u8 mi
|
||||
(nca.GetKeyGeneration() < minimum_key_generation ||
|
||||
nca.GetKeyGeneration() > maximum_key_generation)) {
|
||||
LOG_WARNING(Service_LDR, "Skipping program {:016X} with generation {}", program_id, nca.GetKeyGeneration());
|
||||
return std::nullopt;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ std::optional<Process> CreateProcess(Core::System& system, u64 program_id, u8 mi
|
||||
return CreateProcessImpl(loader, status, system, nca_raw, program_id, 0);
|
||||
}
|
||||
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
@@ -110,7 +110,23 @@ std::optional<Process> CreateApplicationProcess(std::vector<u8>& out_control, st
|
||||
system.GetARPManager().Register(launch.title_id, launch, out_control);
|
||||
return process;
|
||||
}
|
||||
return std::nullopt;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool ReinitializeProcess(Core::System& system, Process& process, u64 program_id) {
|
||||
auto& storage = system.GetContentProviderUnion();
|
||||
const auto nca_raw = storage.GetEntryRaw(program_id, FileSys::ContentRecordType::Program);
|
||||
if (!nca_raw) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto loader = Loader::GetLoader(system, nca_raw, program_id, 0);
|
||||
if (!loader) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Loader::ResultStatus status{};
|
||||
return process.Initialize(*loader, status);
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -27,7 +27,9 @@ class Process;
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
bool ReinitializeProcess(Core::System& system, Process& process, u64 program_id);
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -35,9 +35,9 @@ Result CreateGuestApplication(SharedPointer<IApplicationAccessor>* out_applicati
|
||||
std::unique_ptr<Loader::AppLoader> loader;
|
||||
Loader::ResultStatus result;
|
||||
auto process = CreateApplicationProcess(control, loader, result, system, nca_raw, program_id, 0);
|
||||
R_UNLESS(process != std::nullopt, ResultUnknown);
|
||||
R_UNLESS(process != nullptr, ResultUnknown);
|
||||
|
||||
const auto applet = std::make_shared<Applet>(system, std::make_unique<Service::Process>(*std::move(process)), true);
|
||||
const auto applet = std::make_shared<Applet>(system, std::move(process), true);
|
||||
applet->program_id = program_id;
|
||||
applet->applet_id = AppletId::Application;
|
||||
applet->type = AppletType::Application;
|
||||
@@ -88,9 +88,9 @@ Result IApplicationCreator::CreateSystemApplication(
|
||||
std::vector<u8> control;
|
||||
std::unique_ptr<Loader::AppLoader> loader;
|
||||
auto process = CreateProcess(system, application_id, 1, 22);
|
||||
R_UNLESS(process != std::nullopt, ResultUnknown);
|
||||
R_UNLESS(process != nullptr, ResultUnknown);
|
||||
|
||||
const auto applet = std::make_shared<Applet>(system, std::make_unique<Service::Process>(*std::move(process)), true);
|
||||
const auto applet = std::make_shared<Applet>(system, std::move(process), true);
|
||||
applet->program_id = application_id;
|
||||
applet->applet_id = AppletId::Starter;
|
||||
applet->type = AppletType::LibraryApplet;
|
||||
|
||||
@@ -75,7 +75,7 @@ ILibraryAppletAccessor::ILibraryAppletAccessor(Core::System& system_,
|
||||
{105, D<&ILibraryAppletAccessor::GetPopOutDataEvent>, "GetPopOutDataEvent"},
|
||||
{106, D<&ILibraryAppletAccessor::GetPopInteractiveOutDataEvent>, "GetPopInteractiveOutDataEvent"},
|
||||
{110, nullptr, "NeedsToExitProcess"},
|
||||
{120, nullptr, "GetLibraryAppletInfo"},
|
||||
{120, D<&ILibraryAppletAccessor::GetLibraryAppletInfo>, "GetLibraryAppletInfo"},
|
||||
{150, nullptr, "RequestForAppletToGetForeground"},
|
||||
{160, D<&ILibraryAppletAccessor::GetIndirectLayerConsumerHandle>, "GetIndirectLayerConsumerHandle"}, //2.0.0+
|
||||
{170, D<&ILibraryAppletAccessor::Unknown170>, "Unknown170"}, //22.0.0+
|
||||
@@ -218,6 +218,16 @@ Result ILibraryAppletAccessor::GetIndirectLayerConsumerHandle(Out<u64> out_handl
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ILibraryAppletAccessor::GetLibraryAppletInfo(
|
||||
Out<LibraryAppletInfo> out_library_applet_info) {
|
||||
LOG_INFO(Service_AM, "called");
|
||||
*out_library_applet_info = {
|
||||
.applet_id = m_applet->applet_id,
|
||||
.library_applet_mode = m_applet->library_applet_mode,
|
||||
};
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ILibraryAppletAccessor::Unknown170(OutCopyHandle<Kernel::KReadableEvent> out_event) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
*out_event = m_applet->unknown_event.GetHandle();
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/am/service/library_applet_self_accessor.h"
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
@@ -21,6 +22,10 @@ public:
|
||||
std::shared_ptr<Applet> applet);
|
||||
~ILibraryAppletAccessor();
|
||||
|
||||
std::shared_ptr<Applet> GetApplet() const {
|
||||
return m_applet;
|
||||
}
|
||||
|
||||
private:
|
||||
Result GetAppletStateChangedEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||
Result IsCompleted(Out<bool> out_is_completed);
|
||||
@@ -37,6 +42,7 @@ private:
|
||||
Result GetPopOutDataEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||
Result GetPopInteractiveOutDataEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||
Result GetIndirectLayerConsumerHandle(Out<u64> out_handle);
|
||||
Result GetLibraryAppletInfo(Out<LibraryAppletInfo> out_library_applet_info);
|
||||
Result Unknown170(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||
|
||||
void FrontendExecute();
|
||||
|
||||
@@ -123,7 +123,7 @@ std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet(Core::System& system,
|
||||
|
||||
auto process = CreateProcess(system, program_id, Firmware1400, Firmware2200);
|
||||
if (process) {
|
||||
const auto applet = std::make_shared<Applet>(system, std::make_unique<Service::Process>(*std::move(process)), false);
|
||||
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;
|
||||
|
||||
@@ -233,8 +233,9 @@ Result ILibraryAppletSelfAccessor::ReportVisibleErrorWithErrorContext(
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ILibraryAppletSelfAccessor::UnpopInData() {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
Result ILibraryAppletSelfAccessor::UnpopInData(SharedPointer<IStorage> storage) {
|
||||
LOG_INFO(Service_AM, "called");
|
||||
m_broker->GetInData().Unpop(system.Kernel(), storage);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ private:
|
||||
Result ReportVisibleError(ErrorCode error_code);
|
||||
Result ReportVisibleErrorWithErrorContext(
|
||||
ErrorCode error_code, InLargeData<ErrorContext, BufferAttr_HipcMapAlias> error_context);
|
||||
Result UnpopInData();
|
||||
Result UnpopInData(SharedPointer<IStorage> storage);
|
||||
Result GetMainAppletApplicationDesiredLanguage(Out<u64> out_desired_language);
|
||||
Result GetCurrentApplicationId(Out<u64> out_application_id);
|
||||
Result GetMainAppletAvailableUsers(Out<bool> out_can_select_any_user, Out<s32> out_users_count,
|
||||
|
||||
@@ -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 "core/core.h"
|
||||
#include "core/hle/service/am/applet.h"
|
||||
#include "core/hle/service/am/frontend/applets.h"
|
||||
#include "core/hle/service/am/service/library_applet_accessor.h"
|
||||
#include "core/hle/service/am/service/process_winding_controller.h"
|
||||
@@ -43,6 +45,18 @@ Result IProcessWindingController::OpenCallingLibraryApplet(
|
||||
Out<SharedPointer<ILibraryAppletAccessor>> out_calling_library_applet) {
|
||||
LOG_INFO(Service_AM, "called");
|
||||
|
||||
std::shared_ptr<Applet> reserved_applet;
|
||||
{
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
reserved_applet = std::move(m_applet->reserved_applet);
|
||||
}
|
||||
|
||||
if (reserved_applet != nullptr) {
|
||||
*out_calling_library_applet = std::make_shared<ILibraryAppletAccessor>(
|
||||
system, reserved_applet->caller_applet_broker, reserved_applet);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
const auto caller_applet = m_applet->caller_applet.lock();
|
||||
if (caller_applet == nullptr) {
|
||||
LOG_ERROR(Service_AM, "No caller applet available");
|
||||
@@ -74,22 +88,82 @@ Result IProcessWindingController::PopContext(Out<SharedPointer<IStorage>> out_co
|
||||
}
|
||||
|
||||
Result IProcessWindingController::CancelWindingReservation() {
|
||||
LOG_WARNING(Service_AM, "STUBBED");
|
||||
LOG_INFO(Service_AM, "called");
|
||||
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
m_applet->reserved_applet.reset();
|
||||
m_applet->unwind_after_reserved = false;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IProcessWindingController::WindAndDoReserved() {
|
||||
LOG_WARNING(Service_AM, "STUBBED");
|
||||
LOG_INFO(Service_AM, "called");
|
||||
|
||||
std::shared_ptr<Applet> reserved_applet;
|
||||
{
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
|
||||
reserved_applet = m_applet->reserved_applet;
|
||||
m_applet->display_layer_manager.SetWindowVisibility(false);
|
||||
m_applet->exit_locked = false;
|
||||
system.SetExitLocked(false);
|
||||
}
|
||||
|
||||
if (reserved_applet) {
|
||||
{
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
m_applet->is_winding = true;
|
||||
}
|
||||
|
||||
{
|
||||
std::scoped_lock lk{reserved_applet->lock};
|
||||
reserved_applet->window_visible = true;
|
||||
reserved_applet->process->Run();
|
||||
}
|
||||
|
||||
if (reserved_applet->frontend) {
|
||||
reserved_applet->frontend->Initialize();
|
||||
reserved_applet->frontend->Execute();
|
||||
}
|
||||
} else {
|
||||
LOG_WARNING(Service_AM, "called without a reserved applet to start");
|
||||
}
|
||||
|
||||
m_applet->process->Terminate();
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IProcessWindingController::ReserveToStartAndWaitAndUnwindThis() {
|
||||
LOG_WARNING(Service_AM, "STUBBED");
|
||||
Result IProcessWindingController::ReserveToStartAndWaitAndUnwindThis(
|
||||
SharedPointer<ILibraryAppletAccessor> reserved_applet_accessor) {
|
||||
LOG_INFO(Service_AM, "called");
|
||||
|
||||
if (reserved_applet_accessor == nullptr) {
|
||||
LOG_ERROR(Service_AM, "No applet accessor provided");
|
||||
R_THROW(ResultUnknown);
|
||||
}
|
||||
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
m_applet->reserved_applet = reserved_applet_accessor->GetApplet();
|
||||
m_applet->unwind_after_reserved = true;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IProcessWindingController::ReserveToStartAndWait() {
|
||||
LOG_WARNING(Service_AM, "STUBBED");
|
||||
Result IProcessWindingController::ReserveToStartAndWait(
|
||||
SharedPointer<ILibraryAppletAccessor> reserved_applet_accessor) {
|
||||
LOG_INFO(Service_AM, "called");
|
||||
|
||||
if (reserved_applet_accessor == nullptr) {
|
||||
LOG_ERROR(Service_AM, "No applet accessor provided");
|
||||
R_THROW(ResultUnknown);
|
||||
}
|
||||
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
m_applet->reserved_applet = reserved_applet_accessor->GetApplet();
|
||||
m_applet->unwind_after_reserved = false;
|
||||
|
||||
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
|
||||
@@ -29,8 +29,9 @@ private:
|
||||
Result PopContext(Out<SharedPointer<IStorage>> out_context);
|
||||
Result CancelWindingReservation();
|
||||
Result WindAndDoReserved();
|
||||
Result ReserveToStartAndWaitAndUnwindThis();
|
||||
Result ReserveToStartAndWait();
|
||||
Result ReserveToStartAndWaitAndUnwindThis(
|
||||
SharedPointer<ILibraryAppletAccessor> reserved_applet_accessor);
|
||||
Result ReserveToStartAndWait(SharedPointer<ILibraryAppletAccessor> reserved_applet_accessor);
|
||||
|
||||
const std::shared_ptr<Applet> m_applet;
|
||||
};
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "core/hle/service/am/applet.h"
|
||||
#include "core/hle/service/am/applet_manager.h"
|
||||
#include "core/hle/service/am/event_observer.h"
|
||||
#include "core/hle/service/am/process_creation.h"
|
||||
#include "core/hle/service/am/window_system.h"
|
||||
|
||||
namespace Service::AM {
|
||||
@@ -240,6 +241,37 @@ void WindowSystem::PruneTerminatedAppletsLocked() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// A winding applet has had its own process killed but is kept alive as a transparent slot
|
||||
// while the reserved applet running in its place finishes (see WindAndDoReserved()).
|
||||
if (applet->is_winding) {
|
||||
if (!applet->child_applets.empty()) {
|
||||
it = std::next(it);
|
||||
continue;
|
||||
}
|
||||
|
||||
const bool unwind = applet->unwind_after_reserved;
|
||||
applet->is_winding = false;
|
||||
applet->unwind_after_reserved = false;
|
||||
|
||||
if (unwind && this->RestartAppletProcessLocked(applet.get())) {
|
||||
const u64 new_aruid = applet->aruid.pid;
|
||||
const auto next = std::next(it);
|
||||
|
||||
if (new_aruid != aruid) {
|
||||
auto node = m_applets.extract(it);
|
||||
node.key() = new_aruid;
|
||||
m_applets.insert(std::move(node));
|
||||
}
|
||||
|
||||
applet->process->Run();
|
||||
m_event_observer->RequestUpdate();
|
||||
it = next;
|
||||
continue;
|
||||
}
|
||||
|
||||
applet->reserved_applet.reset();
|
||||
}
|
||||
|
||||
// Terminated, so ensure all child applets are terminated.
|
||||
if (!applet->child_applets.empty()) {
|
||||
this->TerminateChildAppletsLocked(applet.get());
|
||||
@@ -301,6 +333,28 @@ void WindowSystem::PruneTerminatedAppletsLocked() {
|
||||
}
|
||||
}
|
||||
|
||||
bool WindowSystem::RestartAppletProcessLocked(Applet* applet) {
|
||||
if (!ReinitializeProcess(m_system, *applet->process, applet->program_id)) {
|
||||
LOG_ERROR(Service_AM, "Failed to restart winding applet_id={}",
|
||||
static_cast<u32>(applet->applet_id));
|
||||
return false;
|
||||
}
|
||||
|
||||
applet->aruid.pid = applet->process->GetProcessId();
|
||||
applet->is_process_running = false;
|
||||
applet->is_completed = false;
|
||||
|
||||
applet->hid_registration.RegisterCurrentProcess();
|
||||
|
||||
applet->lifecycle_manager.ResetForRelaunch();
|
||||
applet->is_activity_runnable = false;
|
||||
|
||||
applet->launch_reason.flag = 1;
|
||||
|
||||
m_event_observer->TrackAppletProcess(*applet);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WindowSystem::LockHomeMenuIntoForegroundLocked() {
|
||||
// If the home menu is not locked into foreground, then there's nothing to do.
|
||||
if (m_home_menu == nullptr || !m_home_menu_foreground_locked) {
|
||||
@@ -352,6 +406,9 @@ void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground, b
|
||||
const bool has_obscuring_child_applets = [&] {
|
||||
for (const auto& child_applet : applet->child_applets) {
|
||||
std::scoped_lock lk2{child_applet->lock};
|
||||
if (child_applet->is_winding) {
|
||||
return true;
|
||||
}
|
||||
const auto mode = child_applet->library_applet_mode;
|
||||
if (child_applet->is_process_running && child_applet->window_visible &&
|
||||
(mode == LibraryAppletMode::AllForeground ||
|
||||
|
||||
@@ -61,6 +61,7 @@ public:
|
||||
|
||||
private:
|
||||
void PruneTerminatedAppletsLocked();
|
||||
bool RestartAppletProcessLocked(Applet* applet);
|
||||
bool LockHomeMenuIntoForegroundLocked();
|
||||
void TerminateChildAppletsLocked(Applet* applet);
|
||||
void UpdateAppletStateLocked(Applet* applet, bool is_foreground, bool overlay_blocking = false);
|
||||
|
||||
@@ -82,7 +82,7 @@ void APM::OpenSession(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<ISession>(system, controller);
|
||||
rb.PushIpcInterface<ISession>(ctx, system, controller);
|
||||
}
|
||||
|
||||
void APM::GetPerformanceMode(HLERequestContext& ctx) {
|
||||
@@ -125,7 +125,7 @@ void APM_Sys::GetPerformanceEvent(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<ISession>(system, controller);
|
||||
rb.PushIpcInterface<ISession>(ctx, system, controller);
|
||||
}
|
||||
|
||||
void APM_Sys::SetCpuBoostMode(HLERequestContext& ctx) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -45,7 +48,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IRequest>(system);
|
||||
rb.PushIpcInterface<IRequest>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -184,7 +184,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(readable_event.Signal(system.Kernel()));
|
||||
rb.PushCopyObjects(readable_event);
|
||||
rb.PushCopyObjects(ctx, readable_event);
|
||||
}
|
||||
|
||||
void Cancel(HLERequestContext& ctx) {
|
||||
@@ -400,7 +400,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(notification_event->GetReadableEvent());
|
||||
rb.PushCopyObjects(ctx, notification_event->GetReadableEvent());
|
||||
}
|
||||
|
||||
void Clear(HLERequestContext& ctx) {
|
||||
@@ -476,7 +476,7 @@ private:
|
||||
void Module::Interface::CreateFriendService(HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IFriendService>(system);
|
||||
rb.PushIpcInterface<IFriendService>(ctx, system);
|
||||
LOG_DEBUG(Service_Friend, "called");
|
||||
}
|
||||
|
||||
@@ -488,12 +488,12 @@ void Module::Interface::CreateNotificationService(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<INotificationService>(system, uuid);
|
||||
rb.PushIpcInterface<INotificationService>(ctx, system, uuid);
|
||||
}
|
||||
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& system_,
|
||||
const char* name)
|
||||
: ServiceFramework{system_, name}, module{std::move(module_)} {}
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& system_, const char* name)
|
||||
: ServiceFramework{system_, name}, module{std::move(module_)}
|
||||
{}
|
||||
|
||||
Module::Interface::~Interface() = default;
|
||||
|
||||
|
||||
@@ -279,7 +279,7 @@ void ARP_W::AcquireRegistrar(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface(registrar);
|
||||
rb.PushIpcInterface(ctx, registrar);
|
||||
}
|
||||
|
||||
void ARP_W::UnregisterApplicationInstance(HLERequestContext& ctx) {
|
||||
|
||||
@@ -28,7 +28,7 @@ void BGTC_T::OpenTaskService(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<ITaskService>(system);
|
||||
rb.PushIpcInterface<ITaskService>(ctx, system);
|
||||
}
|
||||
|
||||
ITaskService::ITaskService(Core::System& system_) : ServiceFramework{system_, "ITaskService"} {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -56,7 +59,7 @@ ECTX_AW::~ECTX_AW() = default;
|
||||
void ECTX_AW::CreateContextRegistrar(HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IContextRegistrar>(std::make_shared<IContextRegistrar>(system));
|
||||
rb.PushIpcInterface<IContextRegistrar>(ctx, system);
|
||||
}
|
||||
|
||||
} // namespace Service::Glue
|
||||
|
||||
@@ -726,7 +726,7 @@ void IHidSystemServer::AcquireConnectionTriggerTimeoutEvent(HLERequestContext& c
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(acquire_device_registered_event->GetReadableEvent());
|
||||
rb.PushCopyObjects(ctx, acquire_device_registered_event->GetReadableEvent());
|
||||
}
|
||||
|
||||
void IHidSystemServer::AcquireDeviceRegisteredEventForControllerSupport(HLERequestContext& ctx) {
|
||||
@@ -734,7 +734,7 @@ void IHidSystemServer::AcquireDeviceRegisteredEventForControllerSupport(HLEReque
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(acquire_device_registered_event->GetReadableEvent());
|
||||
rb.PushCopyObjects(ctx, acquire_device_registered_event->GetReadableEvent());
|
||||
}
|
||||
|
||||
void IHidSystemServer::GetRegisteredDevices(HLERequestContext& ctx) {
|
||||
@@ -759,7 +759,7 @@ void IHidSystemServer::AcquireUniquePadConnectionEventHandle(HLERequestContext&
|
||||
LOG_WARNING(Service_HID, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.PushCopyObjects(unique_pad_connection_event->GetReadableEvent());
|
||||
rb.PushCopyObjects(ctx, unique_pad_connection_event->GetReadableEvent());
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
@@ -776,7 +776,7 @@ void IHidSystemServer::AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestConte
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(joy_detach_event->GetReadableEvent());
|
||||
rb.PushCopyObjects(ctx, joy_detach_event->GetReadableEvent());
|
||||
}
|
||||
|
||||
void IHidSystemServer::IsUsbFullKeyControllerEnabled(HLERequestContext& ctx) {
|
||||
|
||||
@@ -31,7 +31,7 @@ class Memory;
|
||||
}
|
||||
|
||||
namespace IPC {
|
||||
class ResponseBuilder;
|
||||
struct ResponseBuilder;
|
||||
}
|
||||
|
||||
namespace Service {
|
||||
@@ -392,7 +392,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
friend class IPC::ResponseBuilder;
|
||||
friend struct IPC::ResponseBuilder;
|
||||
|
||||
void ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming);
|
||||
|
||||
|
||||
@@ -24,45 +24,7 @@ namespace IPC {
|
||||
|
||||
constexpr Result ResultSessionClosed{ErrorModule::HIPC, 301};
|
||||
|
||||
class RequestHelperBase {
|
||||
protected:
|
||||
Service::HLERequestContext* context = nullptr;
|
||||
u32* cmdbuf;
|
||||
u32 index = 0;
|
||||
|
||||
public:
|
||||
explicit RequestHelperBase(u32* command_buffer) : cmdbuf(command_buffer) {}
|
||||
|
||||
explicit RequestHelperBase(Service::HLERequestContext& ctx)
|
||||
: context(&ctx), cmdbuf(ctx.CommandBuffer()) {}
|
||||
|
||||
void Skip(u32 size_in_words, bool set_to_null) {
|
||||
if (set_to_null) {
|
||||
memset(cmdbuf + index, 0, size_in_words * sizeof(u32));
|
||||
}
|
||||
index += size_in_words;
|
||||
}
|
||||
|
||||
/**
|
||||
* Aligns the current position forward to a 16-byte boundary, padding with zeros.
|
||||
*/
|
||||
void AlignWithPadding() {
|
||||
if (index & 3) {
|
||||
Skip(static_cast<u32>(4 - (index & 3)), true);
|
||||
}
|
||||
}
|
||||
|
||||
u32 GetCurrentOffset() const {
|
||||
return index;
|
||||
}
|
||||
|
||||
void SetCurrentOffset(u32 offset) {
|
||||
index = offset;
|
||||
}
|
||||
};
|
||||
|
||||
class ResponseBuilder : public RequestHelperBase {
|
||||
public:
|
||||
struct ResponseBuilder {
|
||||
/// Flags used for customizing the behavior of ResponseBuilder
|
||||
enum class Flags : u32 {
|
||||
None = 0,
|
||||
@@ -71,14 +33,13 @@ public:
|
||||
AlwaysMoveHandles = 1,
|
||||
};
|
||||
|
||||
explicit ResponseBuilder(Service::HLERequestContext& ctx, u32 normal_params_size_,
|
||||
u32 num_handles_to_copy_ = 0, u32 num_objects_to_move_ = 0,
|
||||
Flags flags = Flags::None)
|
||||
: RequestHelperBase(ctx), normal_params_size(normal_params_size_),
|
||||
num_handles_to_copy(num_handles_to_copy_),
|
||||
num_objects_to_move(num_objects_to_move_), kernel{ctx.kernel} {
|
||||
|
||||
memset(cmdbuf, 0, sizeof(u32) * IPC::COMMAND_BUFFER_LENGTH);
|
||||
inline explicit ResponseBuilder(Service::HLERequestContext& ctx, u32 normal_params_size_, u32 num_handles_to_copy_ = 0, u32 num_objects_to_move_ = 0, Flags flags = Flags::None)
|
||||
: cmdbuf(ctx.CommandBuffer())
|
||||
, normal_params_size(normal_params_size_)
|
||||
, num_handles_to_copy(num_handles_to_copy_)
|
||||
, num_objects_to_move(num_objects_to_move_)
|
||||
{
|
||||
std::memset(cmdbuf, 0, sizeof(u32) * IPC::COMMAND_BUFFER_LENGTH);
|
||||
|
||||
IPC::CommandHeader header{};
|
||||
auto const mgr = ctx.GetManager().get();
|
||||
@@ -117,9 +78,7 @@ public:
|
||||
handle_descriptor_header.num_handles_to_copy.Assign(num_handles_to_copy_);
|
||||
handle_descriptor_header.num_handles_to_move.Assign(num_handles_to_move);
|
||||
PushRaw(handle_descriptor_header);
|
||||
|
||||
ctx.handles_offset = index;
|
||||
|
||||
Skip(num_handles_to_copy + num_handles_to_move, true);
|
||||
}
|
||||
|
||||
@@ -131,7 +90,6 @@ public:
|
||||
domain_header.num_objects = num_domain_objects;
|
||||
PushRaw(domain_header);
|
||||
}
|
||||
|
||||
IPC::DataPayloadHeader data_payload_header{};
|
||||
data_payload_header.magic = Common::MakeMagic('S', 'F', 'C', 'O');
|
||||
PushRaw(data_payload_header);
|
||||
@@ -141,34 +99,39 @@ public:
|
||||
|
||||
ctx.data_payload_offset = index;
|
||||
ctx.write_size += index;
|
||||
ctx.domain_offset = static_cast<u32>(index + raw_data_size / sizeof(u32));
|
||||
ctx.domain_offset = u32(index + raw_data_size / sizeof(u32));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void PushIpcInterface(std::shared_ptr<T> iface) {
|
||||
auto manager{context->GetManager()};
|
||||
inline void Skip(u32 size_in_words, bool set_to_null) {
|
||||
if (set_to_null) std::memset(cmdbuf + index, 0, size_in_words * sizeof(u32));
|
||||
index += size_in_words;
|
||||
}
|
||||
/// @brief Aligns the current position forward to a 16-byte boundary, padding with zeros.
|
||||
inline void AlignWithPadding() { if (index & 3) Skip(u32(4 - (index & 3)), true); }
|
||||
inline u32 GetCurrentOffset() const { return index; }
|
||||
inline void SetCurrentOffset(u32 offset) { index = offset; }
|
||||
|
||||
template <class T> inline void PushIpcInterface(Service::HLERequestContext& ctx, std::shared_ptr<T> iface) {
|
||||
auto manager = ctx.GetManager();
|
||||
if (manager->IsDomain()) {
|
||||
context->AddDomainObject(std::move(iface));
|
||||
ctx.AddDomainObject(std::move(iface));
|
||||
} else {
|
||||
ASSERT(Kernel::GetCurrentProcess(kernel).GetResourceLimit()->Reserve(kernel, Kernel::LimitableResource::SessionCountMax, 1));
|
||||
ASSERT(Kernel::GetCurrentProcess(ctx.kernel).GetResourceLimit()->Reserve(ctx.kernel, Kernel::LimitableResource::SessionCountMax, 1));
|
||||
|
||||
auto* session = Kernel::KSession::Create(kernel);
|
||||
session->Initialize(kernel, nullptr, 0);
|
||||
Kernel::KSession::Register(kernel, session);
|
||||
auto* session = Kernel::KSession::Create(ctx.kernel);
|
||||
session->Initialize(ctx.kernel, nullptr, 0);
|
||||
Kernel::KSession::Register(ctx.kernel, session);
|
||||
|
||||
auto next_manager = std::make_shared<Service::SessionRequestManager>(
|
||||
kernel, manager->GetServerManager());
|
||||
auto next_manager = std::make_shared<Service::SessionRequestManager>(ctx.kernel, manager->GetServerManager());
|
||||
next_manager->SetSessionHandler(iface);
|
||||
manager->GetServerManager().RegisterSession(&session->GetServerSession(), next_manager);
|
||||
|
||||
context->AddMoveObject(&session->GetClientSession());
|
||||
ctx.AddMoveObject(&session->GetClientSession());
|
||||
}
|
||||
}
|
||||
|
||||
template <class T, class... Args>
|
||||
void PushIpcInterface(Args&&... args) {
|
||||
PushIpcInterface<T>(std::make_shared<T>(std::forward<Args>(args)...));
|
||||
template <class T, class... Args> inline void PushIpcInterface(Service::HLERequestContext& ctx, Args&&... args) {
|
||||
PushIpcInterface<T>(ctx, std::make_shared<T>(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
void PushImpl(s8 value);
|
||||
@@ -184,59 +147,38 @@ public:
|
||||
void PushImpl(bool value);
|
||||
void PushImpl(Result value);
|
||||
|
||||
template <typename T>
|
||||
void Push(T value) {
|
||||
template <typename T> inline void Push(T value) {
|
||||
return PushImpl(value);
|
||||
}
|
||||
|
||||
template <typename First, typename... Other>
|
||||
void Push(const First& first_value, const Other&... other_values);
|
||||
|
||||
/**
|
||||
* Helper function for pushing strongly-typed enumeration values.
|
||||
*
|
||||
* @tparam Enum The enumeration type to be pushed
|
||||
*
|
||||
* @param value The value to push.
|
||||
*
|
||||
* @note The underlying size of the enumeration type is the size of the
|
||||
* data that gets pushed. e.g. "enum class SomeEnum : u16" will
|
||||
* push a u16-sized amount of data.
|
||||
*/
|
||||
template <typename Enum>
|
||||
void PushEnum(Enum value) {
|
||||
/// @brief Helper function for pushing strongly-typed enumeration values.
|
||||
/// @tparam Enum The enumeration type to be pushed
|
||||
/// @param value The value to push.
|
||||
/// @note The underlying size of the enumeration type is the size of the data that gets pushed.
|
||||
/// e.g. "enum class SomeEnum : u16" will push a u16-sized amount of data.
|
||||
template <typename Enum> inline void PushEnum(Enum value) {
|
||||
static_assert(std::is_enum_v<Enum>, "T must be an enum type within a PushEnum call.");
|
||||
static_assert(!std::is_convertible_v<Enum, int>,
|
||||
"enum type in PushEnum must be a strongly typed enum.");
|
||||
static_assert(!std::is_convertible_v<Enum, int>, "enum type in PushEnum must be a strongly typed enum.");
|
||||
Push(static_cast<std::underlying_type_t<Enum>>(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copies the content of the given trivially copyable class to the buffer as a normal
|
||||
* param
|
||||
* @note: The input class must be correctly packed/padded to fit hardware layout.
|
||||
*/
|
||||
template <typename T>
|
||||
void PushRaw(const T& value);
|
||||
/// @brief Copies the content of the given trivially copyable class to the buffer as a normal param
|
||||
/// @note: The input class must be correctly packed/padded to fit hardware layout.
|
||||
template <typename T> void PushRaw(const T& value);
|
||||
template <typename... O> void PushMoveObjects(Service::HLERequestContext& ctx, O*... pointers);
|
||||
template <typename... O> void PushMoveObjects(Service::HLERequestContext& ctx, O&... pointers);
|
||||
template <typename... O> void PushCopyObjects(Service::HLERequestContext& ctx, O*... pointers);
|
||||
template <typename... O> void PushCopyObjects(Service::HLERequestContext& ctx, O&... pointers);
|
||||
|
||||
template <typename... O>
|
||||
void PushMoveObjects(O*... pointers);
|
||||
|
||||
template <typename... O>
|
||||
void PushMoveObjects(O&... pointers);
|
||||
|
||||
template <typename... O>
|
||||
void PushCopyObjects(O*... pointers);
|
||||
|
||||
template <typename... O>
|
||||
void PushCopyObjects(O&... pointers);
|
||||
|
||||
private:
|
||||
u32* cmdbuf;
|
||||
u32 index = 0;
|
||||
u32 normal_params_size{};
|
||||
u32 num_handles_to_copy{};
|
||||
u32 num_objects_to_move{}; ///< Domain objects or move handles, context dependent
|
||||
u32 data_payload_index{};
|
||||
Kernel::KernelCore& kernel;
|
||||
};
|
||||
|
||||
/// Push ///
|
||||
@@ -251,8 +193,7 @@ inline void ResponseBuilder::PushImpl(u32 value) {
|
||||
|
||||
template <typename T>
|
||||
void ResponseBuilder::PushRaw(const T& value) {
|
||||
static_assert(std::is_trivially_copyable_v<T>,
|
||||
"It's undefined behavior to use memcpy with non-trivially copyable objects");
|
||||
static_assert(std::is_trivially_copyable_v<T>, "It's undefined behavior to use memcpy with non-trivially copyable objects");
|
||||
std::memcpy(cmdbuf + index, &value, sizeof(T));
|
||||
index += (sizeof(T) + 3) / 4; // round up to word length
|
||||
}
|
||||
@@ -272,8 +213,8 @@ inline void ResponseBuilder::PushImpl(s16 value) {
|
||||
}
|
||||
|
||||
inline void ResponseBuilder::PushImpl(s64 value) {
|
||||
PushImpl(static_cast<u32>(value));
|
||||
PushImpl(static_cast<u32>(value >> 32));
|
||||
PushImpl(u32(value));
|
||||
PushImpl(u32(value >> 32));
|
||||
}
|
||||
|
||||
inline void ResponseBuilder::PushImpl(u8 value) {
|
||||
@@ -285,8 +226,8 @@ inline void ResponseBuilder::PushImpl(u16 value) {
|
||||
}
|
||||
|
||||
inline void ResponseBuilder::PushImpl(u64 value) {
|
||||
PushImpl(static_cast<u32>(value));
|
||||
PushImpl(static_cast<u32>(value >> 32));
|
||||
PushImpl(u32(value));
|
||||
PushImpl(u32(value >> 32));
|
||||
}
|
||||
|
||||
inline void ResponseBuilder::PushImpl(float value) {
|
||||
@@ -312,90 +253,88 @@ void ResponseBuilder::Push(const First& first_value, const Other&... other_value
|
||||
}
|
||||
|
||||
template <typename... O>
|
||||
inline void ResponseBuilder::PushCopyObjects(O*... pointers) {
|
||||
inline void ResponseBuilder::PushCopyObjects(Service::HLERequestContext& ctx, O*... pointers) {
|
||||
auto objects = {pointers...};
|
||||
for (auto& object : objects) {
|
||||
context->AddCopyObject(object);
|
||||
ctx.AddCopyObject(object);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... O>
|
||||
inline void ResponseBuilder::PushCopyObjects(O&... pointers) {
|
||||
inline void ResponseBuilder::PushCopyObjects(Service::HLERequestContext& ctx, O&... pointers) {
|
||||
auto objects = {&pointers...};
|
||||
for (auto& object : objects) {
|
||||
context->AddCopyObject(object);
|
||||
ctx.AddCopyObject(object);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... O>
|
||||
inline void ResponseBuilder::PushMoveObjects(O*... pointers) {
|
||||
inline void ResponseBuilder::PushMoveObjects(Service::HLERequestContext& ctx, O*... pointers) {
|
||||
auto objects = {pointers...};
|
||||
for (auto& object : objects) {
|
||||
context->AddMoveObject(object);
|
||||
ctx.AddMoveObject(object);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... O>
|
||||
inline void ResponseBuilder::PushMoveObjects(O&... pointers) {
|
||||
inline void ResponseBuilder::PushMoveObjects(Service::HLERequestContext& ctx, O&... pointers) {
|
||||
auto objects = {&pointers...};
|
||||
for (auto& object : objects) {
|
||||
context->AddMoveObject(object);
|
||||
ctx.AddMoveObject(object);
|
||||
}
|
||||
}
|
||||
|
||||
class RequestParser : public RequestHelperBase {
|
||||
public:
|
||||
explicit RequestParser(u32* command_buffer) : RequestHelperBase(command_buffer) {}
|
||||
|
||||
explicit RequestParser(Service::HLERequestContext& ctx) : RequestHelperBase(ctx) {
|
||||
struct RequestParser {
|
||||
inline explicit RequestParser(u32* command_buffer) : cmdbuf(command_buffer) {}
|
||||
inline explicit RequestParser(Service::HLERequestContext& ctx)
|
||||
: cmdbuf(ctx.CommandBuffer())
|
||||
{
|
||||
// TIPC does not have data payload offset
|
||||
if (!ctx.IsTipc()) {
|
||||
ASSERT_MSG(ctx.GetDataPayloadOffset(), "context is incomplete");
|
||||
Skip(ctx.GetDataPayloadOffset(), false);
|
||||
}
|
||||
|
||||
// Skip the u64 command id, it's already stored in the context
|
||||
static constexpr u32 CommandIdSize = 2;
|
||||
Skip(CommandIdSize, false);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T Pop();
|
||||
inline void Skip(u32 size_in_words, bool set_to_null) {
|
||||
if (set_to_null) std::memset(cmdbuf + index, 0, size_in_words * sizeof(u32));
|
||||
index += size_in_words;
|
||||
}
|
||||
/// @brief Aligns the current position forward to a 16-byte boundary, padding with zeros.
|
||||
inline void AlignWithPadding() { if (index & 3) Skip(u32(4 - (index & 3)), true); }
|
||||
inline u32 GetCurrentOffset() const { return index; }
|
||||
inline void SetCurrentOffset(u32 offset) { index = offset; }
|
||||
|
||||
template <typename T>
|
||||
void Pop(T& value);
|
||||
|
||||
template <typename First, typename... Other>
|
||||
void Pop(First& first_value, Other&... other_values);
|
||||
template <typename T> T Pop();
|
||||
template <typename T> void Pop(T& value);
|
||||
template <typename First, typename... Other> void Pop(First& first_value, Other&... other_values);
|
||||
|
||||
template <typename T>
|
||||
T PopEnum() {
|
||||
static_assert(std::is_enum_v<T>, "T must be an enum type within a PopEnum call.");
|
||||
static_assert(!std::is_convertible_v<T, int>,
|
||||
"enum type in PopEnum must be a strongly typed enum.");
|
||||
return static_cast<T>(Pop<std::underlying_type_t<T>>());
|
||||
static_assert(!std::is_convertible_v<T, int>, "enum type in PopEnum must be a strongly typed enum.");
|
||||
return T(Pop<std::underlying_type_t<T>>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reads the next normal parameters as a struct, by copying it
|
||||
* @note: The output class must be correctly packed/padded to fit hardware layout.
|
||||
*/
|
||||
template <typename T>
|
||||
void PopRaw(T& value);
|
||||
/// @brief Reads the next normal parameters as a struct, by copying it
|
||||
/// @note: The output class must be correctly packed/padded to fit hardware layout.
|
||||
template <typename T> void PopRaw(T& value);
|
||||
|
||||
/**
|
||||
* @brief Reads the next normal parameters as a struct, by copying it into a new value
|
||||
* @note: The output class must be correctly packed/padded to fit hardware layout.
|
||||
*/
|
||||
template <typename T>
|
||||
T PopRaw();
|
||||
/// @brief Reads the next normal parameters as a struct, by copying it into a new value
|
||||
/// @note: The output class must be correctly packed/padded to fit hardware layout.
|
||||
template <typename T> T PopRaw();
|
||||
|
||||
template <class T>
|
||||
std::weak_ptr<T> PopIpcInterface() {
|
||||
ASSERT(context->GetManager()->IsDomain());
|
||||
ASSERT(context->GetDomainMessageHeader().input_object_count > 0);
|
||||
return context->GetDomainHandler<T>(Pop<u32>() - 1);
|
||||
template <class T> [[nodiscard]] std::weak_ptr<T> PopIpcInterface(Service::HLERequestContext& ctx) {
|
||||
ASSERT(ctx.GetManager()->IsDomain());
|
||||
ASSERT(ctx.GetDomainMessageHeader().input_object_count > 0);
|
||||
return ctx.GetDomainHandler<T>(Pop<u32>() - 1);
|
||||
}
|
||||
|
||||
u32* cmdbuf;
|
||||
u32 index = 0;
|
||||
};
|
||||
|
||||
/// Pop ///
|
||||
@@ -407,7 +346,7 @@ inline u32 RequestParser::Pop() {
|
||||
|
||||
template <>
|
||||
inline s32 RequestParser::Pop() {
|
||||
return static_cast<s32>(Pop<u32>());
|
||||
return s32(Pop<u32>());
|
||||
}
|
||||
|
||||
// Ignore the -Wclass-memaccess warning on memcpy for non-trivially default constructible objects.
|
||||
@@ -417,8 +356,7 @@ inline s32 RequestParser::Pop() {
|
||||
#endif
|
||||
template <typename T>
|
||||
void RequestParser::PopRaw(T& value) {
|
||||
static_assert(std::is_trivially_copyable_v<T>,
|
||||
"It's undefined behavior to use memcpy with non-trivially copyable objects");
|
||||
static_assert(std::is_trivially_copyable_v<T>, "It's undefined behavior to use memcpy with non-trivially copyable objects");
|
||||
std::memcpy(&value, cmdbuf + index, sizeof(T));
|
||||
index += (sizeof(T) + 3) / 4; // round up to word length
|
||||
}
|
||||
|
||||
@@ -353,7 +353,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<ILogger>(system);
|
||||
rb.PushIpcInterface<ILogger>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -151,7 +151,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IAm>(system);
|
||||
rb.PushIpcInterface<IAm>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -173,7 +173,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<MFIUser>(system);
|
||||
rb.PushIpcInterface<MFIUser>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -195,7 +195,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IUser>(system);
|
||||
rb.PushIpcInterface<IUser>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -217,7 +217,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<ISystem>(system);
|
||||
rb.PushIpcInterface<ISystem>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -143,7 +143,7 @@ void NfcInterface::AttachAvailabilityChangeEvent(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(GetManager()->AttachAvailabilityChangeEvent());
|
||||
rb.PushCopyObjects(ctx, GetManager()->AttachAvailabilityChangeEvent());
|
||||
}
|
||||
|
||||
void NfcInterface::StartDetection(HLERequestContext& ctx) {
|
||||
@@ -203,7 +203,7 @@ void NfcInterface::AttachActivateEvent(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(result);
|
||||
rb.PushCopyObjects(out_event);
|
||||
rb.PushCopyObjects(ctx, out_event);
|
||||
}
|
||||
|
||||
void NfcInterface::AttachDeactivateEvent(HLERequestContext& ctx) {
|
||||
@@ -217,7 +217,7 @@ void NfcInterface::AttachDeactivateEvent(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(result);
|
||||
rb.PushCopyObjects(out_event);
|
||||
rb.PushCopyObjects(ctx, out_event);
|
||||
}
|
||||
|
||||
void NfcInterface::SetNfcEnabled(HLERequestContext& ctx) {
|
||||
|
||||
@@ -157,7 +157,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IUser>(system);
|
||||
rb.PushIpcInterface<IUser>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -179,7 +179,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<ISystem>(system);
|
||||
rb.PushIpcInterface<ISystem>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -201,7 +201,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IDebug>(system);
|
||||
rb.PushIpcInterface<IDebug>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -307,7 +307,7 @@ private:
|
||||
void GetSystemEventReadableHandle(HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(evt_scan_complete->GetReadableEvent(),
|
||||
rb.PushCopyObjects(ctx, evt_scan_complete->GetReadableEvent(),
|
||||
evt_processing->GetReadableEvent());
|
||||
}
|
||||
|
||||
@@ -452,7 +452,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(event1->GetReadableEvent(), event2->GetReadableEvent());
|
||||
rb.PushCopyObjects(ctx, event1->GetReadableEvent(), event2->GetReadableEvent());
|
||||
}
|
||||
|
||||
void Cancel(HLERequestContext& ctx) {
|
||||
@@ -528,7 +528,7 @@ void IGeneralService::CreateScanRequest(HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IScanRequest>(system);
|
||||
rb.PushIpcInterface<IScanRequest>(ctx, system);
|
||||
}
|
||||
|
||||
void IGeneralService::CreateRequest(HLERequestContext& ctx) {
|
||||
@@ -537,7 +537,7 @@ void IGeneralService::CreateRequest(HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IRequest>(system);
|
||||
rb.PushIpcInterface<IRequest>(ctx, system);
|
||||
}
|
||||
|
||||
void IGeneralService::GetCurrentNetworkProfile(HLERequestContext& ctx) {
|
||||
@@ -716,7 +716,7 @@ void IGeneralService::GetNetworkProfile(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<INetworkProfile>(system);
|
||||
rb.PushIpcInterface<INetworkProfile>(ctx, system);
|
||||
}
|
||||
|
||||
void IGeneralService::SetNetworkProfile(HLERequestContext& ctx) {
|
||||
@@ -869,7 +869,7 @@ void IGeneralService::CreateTemporaryNetworkProfile(HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 6, 0, 1};
|
||||
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<INetworkProfile>(system);
|
||||
rb.PushIpcInterface<INetworkProfile>(ctx, system);
|
||||
rb.PushRaw<u128>(uuid);
|
||||
}
|
||||
|
||||
@@ -1124,7 +1124,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IGeneralService>(system);
|
||||
rb.PushIpcInterface<IGeneralService>(ctx, system);
|
||||
}
|
||||
|
||||
void CreateGeneralService(HLERequestContext& ctx) {
|
||||
@@ -1132,7 +1132,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IGeneralService>(system);
|
||||
rb.PushIpcInterface<IGeneralService>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ private:
|
||||
LOG_WARNING(Service_NIM, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IShopServiceAsync>(system);
|
||||
rb.PushIpcInterface<IShopServiceAsync>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -75,7 +75,7 @@ private:
|
||||
LOG_WARNING(Service_NIM, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IShopServiceAccessor>(system);
|
||||
rb.PushIpcInterface<IShopServiceAccessor>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -336,7 +336,7 @@ private:
|
||||
LOG_DEBUG(Service_NIM, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IShopServiceAccessServer>(system);
|
||||
rb.PushIpcInterface<IShopServiceAccessServer>(ctx, system);
|
||||
}
|
||||
|
||||
void IsLargeResourceAvailable(HLERequestContext& ctx) {
|
||||
@@ -356,7 +356,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IShopServiceAccessServer>(system);
|
||||
rb.PushIpcInterface<IShopServiceAccessServer>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -439,7 +439,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(finished_event->GetReadableEvent());
|
||||
rb.PushCopyObjects(ctx, finished_event->GetReadableEvent());
|
||||
}
|
||||
|
||||
void GetResult(HLERequestContext& ctx) {
|
||||
@@ -500,7 +500,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IEnsureNetworkClockAvailabilityService>(system);
|
||||
rb.PushIpcInterface<IEnsureNetworkClockAvailabilityService>(ctx, system);
|
||||
}
|
||||
|
||||
// TODO(ogniK): Do we need these?
|
||||
|
||||
@@ -359,8 +359,8 @@ void IReadOnlyApplicationControlDataInterface::ListApplicationTitle(HLERequestCo
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(async_value->ReadableEvent());
|
||||
rb.PushIpcInterface(std::move(async_value));
|
||||
rb.PushCopyObjects(ctx, async_value->ReadableEvent());
|
||||
rb.PushIpcInterface(ctx, std::move(async_value));
|
||||
}
|
||||
|
||||
Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData3(
|
||||
|
||||
@@ -199,7 +199,7 @@ void NVDRV::QueryEvent(HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 3, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
auto& readable_event = event->GetReadableEvent();
|
||||
rb.PushCopyObjects(readable_event);
|
||||
rb.PushCopyObjects(ctx, readable_event);
|
||||
rb.PushEnum(NvResult::Success);
|
||||
} else {
|
||||
LOG_ERROR(Service_NVDRV, "Invalid event request!");
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Core {
|
||||
@@ -28,6 +30,15 @@ public:
|
||||
inline explicit Process(Core::System& system) noexcept : m_system(system) {}
|
||||
inline ~Process() { this->Finalize(); }
|
||||
|
||||
Process(const Process&) = delete;
|
||||
Process& operator=(const Process&) = delete;
|
||||
Process& operator=(Process&&) = delete;
|
||||
inline Process(Process&& other) noexcept
|
||||
: m_system(other.m_system), m_process(std::exchange(other.m_process, nullptr)),
|
||||
m_main_thread_stack_size(std::exchange(other.m_main_thread_stack_size, 0)),
|
||||
m_main_thread_priority(std::exchange(other.m_main_thread_priority, 0)),
|
||||
m_process_started(std::exchange(other.m_process_started, false)) {}
|
||||
|
||||
bool Initialize(Loader::AppLoader& loader, Loader::ResultStatus& out_load_result);
|
||||
void Finalize();
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -124,7 +127,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IClkrstSession>(system, device_code);
|
||||
rb.PushIpcInterface<IClkrstSession>(ctx, system, device_code);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -161,7 +161,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 10, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(*process);
|
||||
rb.PushCopyObjects(ctx, *process);
|
||||
rb.PushRaw(program_location);
|
||||
rb.PushRaw(override_status);
|
||||
}
|
||||
|
||||
@@ -147,7 +147,7 @@ void IAlarmService::CreateWakeupAlarm(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<ISteadyClockAlarm>(system, m_alarms, AlarmType::WakeupAlarm);
|
||||
rb.PushIpcInterface<ISteadyClockAlarm>(ctx, system, m_alarms, AlarmType::WakeupAlarm);
|
||||
}
|
||||
|
||||
void IAlarmService::CreateBackgroundTaskAlarm(HLERequestContext& ctx) {
|
||||
@@ -155,7 +155,7 @@ void IAlarmService::CreateBackgroundTaskAlarm(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<ISteadyClockAlarm>(system, m_alarms, AlarmType::BackgroundTaskAlarm);
|
||||
rb.PushIpcInterface<ISteadyClockAlarm>(ctx, system, m_alarms, AlarmType::BackgroundTaskAlarm);
|
||||
}
|
||||
|
||||
ISteadyClockAlarm::ISteadyClockAlarm(Core::System& system_, Alarms& alarms, AlarmType type)
|
||||
@@ -179,7 +179,7 @@ void ISteadyClockAlarm::GetAlarmEvent(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(m_alarm.GetEventHandle());
|
||||
rb.PushCopyObjects(ctx, m_alarm.GetEventHandle());
|
||||
}
|
||||
|
||||
void ISteadyClockAlarm::Enable(HLERequestContext& ctx) {
|
||||
|
||||
@@ -67,7 +67,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(state_change_event->GetReadableEvent());
|
||||
rb.PushCopyObjects(ctx, state_change_event->GetReadableEvent());
|
||||
}
|
||||
|
||||
void UnbindStateChangeEvent(HLERequestContext& ctx) {
|
||||
@@ -186,7 +186,7 @@ void PSM::OpenSession(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IPsmSession>(system);
|
||||
rb.PushIpcInterface<IPsmSession>(ctx, system);
|
||||
}
|
||||
|
||||
void PSM::GetBatteryVoltageState(HLERequestContext& ctx) {
|
||||
|
||||
@@ -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-3.0-or-later
|
||||
|
||||
@@ -82,7 +85,7 @@ void TS::OpenSession(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<ISession>(system);
|
||||
rb.PushIpcInterface<ISession>(ctx, system);
|
||||
}
|
||||
|
||||
} // namespace Service::PTM
|
||||
|
||||
@@ -140,7 +140,7 @@ void SM::GetServiceCmif(HLERequestContext& ctx) {
|
||||
if (result == ResultSuccess) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
|
||||
rb.Push(result);
|
||||
rb.PushMoveObjects(client_session);
|
||||
rb.PushMoveObjects(ctx, client_session);
|
||||
} else {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
@@ -157,7 +157,7 @@ void SM::GetServiceTipc(HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
|
||||
rb.Push(result);
|
||||
rb.PushMoveObjects(result == ResultSuccess ? client_session : nullptr);
|
||||
rb.PushMoveObjects(ctx, result == ResultSuccess ? client_session : nullptr);
|
||||
}
|
||||
|
||||
static std::string PopServiceName(IPC::RequestParser& rp) {
|
||||
@@ -230,8 +230,7 @@ void SM::RegisterServiceImpl(HLERequestContext& ctx, std::string name, u32 max_s
|
||||
max_session_count, is_light);
|
||||
|
||||
Kernel::KServerPort* server_port{};
|
||||
if (const auto result = service_manager.RegisterService(std::addressof(server_port), name,
|
||||
max_session_count, nullptr);
|
||||
if (const auto result = service_manager.RegisterService(std::addressof(server_port), name, max_session_count, nullptr);
|
||||
result.IsError()) {
|
||||
LOG_ERROR(Service_SM, "failed to register service with error_code={:08X}", result.raw);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@@ -241,7 +240,7 @@ void SM::RegisterServiceImpl(HLERequestContext& ctx, std::string name, u32 max_s
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushMoveObjects(server_port);
|
||||
rb.PushMoveObjects(ctx, server_port);
|
||||
}
|
||||
|
||||
void SM::UnregisterService(HLERequestContext& ctx) {
|
||||
|
||||
@@ -60,7 +60,7 @@ void Controller::CloneCurrentObject(HLERequestContext& ctx) {
|
||||
// We succeeded.
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushMoveObjects(session->GetClientSession());
|
||||
rb.PushMoveObjects(ctx, session->GetClientSession());
|
||||
}
|
||||
|
||||
void Controller::CloneCurrentObjectEx(HLERequestContext& ctx) {
|
||||
|
||||
@@ -543,8 +543,7 @@ private:
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(res);
|
||||
if (res == ResultSuccess) {
|
||||
rb.PushIpcInterface<ISslConnection>(system, ssl_version, shared_data,
|
||||
std::move(backend));
|
||||
rb.PushIpcInterface<ISslConnection>(ctx, system, ssl_version, shared_data, std::move(backend));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -624,12 +623,11 @@ private:
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto parameters = rp.PopRaw<Parameters>();
|
||||
|
||||
LOG_WARNING(Service_SSL, "(STUBBED) called, api_version={}, pid_placeholder={}",
|
||||
parameters.ssl_version.api_version, parameters.pid_placeholder);
|
||||
LOG_WARNING(Service_SSL, "(STUBBED) called, api_version={}, pid_placeholder={}", parameters.ssl_version.api_version, parameters.pid_placeholder);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<ISslContext>(system, parameters.ssl_version);
|
||||
rb.PushIpcInterface<ISslContext>(ctx, system, parameters.ssl_version);
|
||||
}
|
||||
|
||||
void SetInterfaceVersion(HLERequestContext& ctx) {
|
||||
|
||||
@@ -155,7 +155,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IPdSession>(system);
|
||||
rb.PushIpcInterface<IPdSession>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -199,7 +199,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IPdCradleSession>(system);
|
||||
rb.PushIpcInterface<IPdCradleSession>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -50,8 +50,8 @@ NPad::NPad(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service
|
||||
auto& controller = controller_data[aruid_index][i];
|
||||
controller.device = hid_core.GetEmulatedControllerByIndex(i);
|
||||
Core::HID::ControllerUpdateCallback engine_callback{
|
||||
.on_change = [this, i](Core::HID::ControllerTriggerType type) {
|
||||
ControllerUpdate(hid_core.kernel, type, i);
|
||||
.on_change = [this, i, kernel = &hid_core.kernel](Core::HID::ControllerTriggerType type) {
|
||||
ControllerUpdate(*kernel, type, i);
|
||||
},
|
||||
.is_npad_service = true,
|
||||
};
|
||||
|
||||
@@ -332,6 +332,9 @@ void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) {
|
||||
void SetupDenormControl(const Profile& profile, const IR::Program& program, EmitContext& ctx,
|
||||
Id main_func) {
|
||||
const Info& info{program.info};
|
||||
if (profile.has_broken_fp16_float_controls && info.uses_fp16) {
|
||||
return;
|
||||
}
|
||||
if (info.uses_fp32_denorms_flush && info.uses_fp32_denorms_preserve) {
|
||||
LOG_DEBUG(Shader_SPIRV, "Fp32 denorm flush and preserve on the same shader");
|
||||
} else if (info.uses_fp32_denorms_flush) {
|
||||
@@ -432,7 +435,7 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct
|
||||
}
|
||||
if ((info.uses_subgroup_vote || info.uses_subgroup_invocation_id ||
|
||||
info.uses_subgroup_shuffles) &&
|
||||
profile.support_vote) {
|
||||
profile.support_vote && profile.SupportsSubgroupStage(ctx.stage)) {
|
||||
ctx.AddCapability(spv::Capability::GroupNonUniformBallot);
|
||||
ctx.AddCapability(spv::Capability::GroupNonUniformShuffle);
|
||||
if (!profile.warp_size_potentially_larger_than_guest) {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -15,7 +18,7 @@ Id SharedPointer(EmitContext& ctx, Id offset, u32 index_offset = 0) {
|
||||
if (index_offset > 0) {
|
||||
index = ctx.OpIAdd(ctx.U32[1], index, ctx.Const(index_offset));
|
||||
}
|
||||
return ctx.profile.support_explicit_workgroup_layout
|
||||
return ctx.uses_explicit_workgroup_layout
|
||||
? ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, ctx.u32_zero_value, index)
|
||||
: ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, index);
|
||||
}
|
||||
@@ -155,7 +158,7 @@ Id EmitSharedAtomicExchange32(EmitContext& ctx, Id offset, Id value) {
|
||||
}
|
||||
|
||||
Id EmitSharedAtomicExchange64(EmitContext& ctx, Id offset, Id value) {
|
||||
if (ctx.profile.support_int64_atomics && ctx.profile.support_explicit_workgroup_layout) {
|
||||
if (ctx.profile.support_shared_int64_atomics && ctx.uses_explicit_workgroup_layout) {
|
||||
const Id shift_id{ctx.Const(3U)};
|
||||
const Id index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift_id)};
|
||||
const Id pointer{
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -28,7 +31,7 @@ std::pair<Id, Id> ExtractArgs(EmitContext& ctx, Id offset, u32 mask, u32 count)
|
||||
} // Anonymous namespace
|
||||
|
||||
Id EmitLoadSharedU8(EmitContext& ctx, Id offset) {
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
if (ctx.uses_explicit_workgroup_layout) {
|
||||
const Id pointer{
|
||||
ctx.OpAccessChain(ctx.shared_u8, ctx.shared_memory_u8, ctx.u32_zero_value, offset)};
|
||||
return ctx.OpUConvert(ctx.U32[1], ctx.OpLoad(ctx.U8, pointer));
|
||||
@@ -39,7 +42,7 @@ Id EmitLoadSharedU8(EmitContext& ctx, Id offset) {
|
||||
}
|
||||
|
||||
Id EmitLoadSharedS8(EmitContext& ctx, Id offset) {
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
if (ctx.uses_explicit_workgroup_layout) {
|
||||
const Id pointer{
|
||||
ctx.OpAccessChain(ctx.shared_u8, ctx.shared_memory_u8, ctx.u32_zero_value, offset)};
|
||||
return ctx.OpSConvert(ctx.U32[1], ctx.OpLoad(ctx.U8, pointer));
|
||||
@@ -50,7 +53,7 @@ Id EmitLoadSharedS8(EmitContext& ctx, Id offset) {
|
||||
}
|
||||
|
||||
Id EmitLoadSharedU16(EmitContext& ctx, Id offset) {
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
if (ctx.uses_explicit_workgroup_layout) {
|
||||
const Id pointer{Pointer(ctx, ctx.shared_u16, ctx.shared_memory_u16, offset, 1)};
|
||||
return ctx.OpUConvert(ctx.U32[1], ctx.OpLoad(ctx.U16, pointer));
|
||||
} else {
|
||||
@@ -60,7 +63,7 @@ Id EmitLoadSharedU16(EmitContext& ctx, Id offset) {
|
||||
}
|
||||
|
||||
Id EmitLoadSharedS16(EmitContext& ctx, Id offset) {
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
if (ctx.uses_explicit_workgroup_layout) {
|
||||
const Id pointer{Pointer(ctx, ctx.shared_u16, ctx.shared_memory_u16, offset, 1)};
|
||||
return ctx.OpSConvert(ctx.U32[1], ctx.OpLoad(ctx.U16, pointer));
|
||||
} else {
|
||||
@@ -70,7 +73,7 @@ Id EmitLoadSharedS16(EmitContext& ctx, Id offset) {
|
||||
}
|
||||
|
||||
Id EmitLoadSharedU32(EmitContext& ctx, Id offset) {
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
if (ctx.uses_explicit_workgroup_layout) {
|
||||
const Id pointer{Pointer(ctx, ctx.shared_u32, ctx.shared_memory_u32, offset, 2)};
|
||||
return ctx.OpLoad(ctx.U32[1], pointer);
|
||||
} else {
|
||||
@@ -79,7 +82,7 @@ Id EmitLoadSharedU32(EmitContext& ctx, Id offset) {
|
||||
}
|
||||
|
||||
Id EmitLoadSharedU64(EmitContext& ctx, Id offset) {
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
if (ctx.uses_explicit_workgroup_layout) {
|
||||
const Id pointer{Pointer(ctx, ctx.shared_u32x2, ctx.shared_memory_u32x2, offset, 3)};
|
||||
return ctx.OpLoad(ctx.U32[2], pointer);
|
||||
} else {
|
||||
@@ -94,7 +97,7 @@ Id EmitLoadSharedU64(EmitContext& ctx, Id offset) {
|
||||
}
|
||||
|
||||
Id EmitLoadSharedU128(EmitContext& ctx, Id offset) {
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
if (ctx.uses_explicit_workgroup_layout) {
|
||||
const Id pointer{Pointer(ctx, ctx.shared_u32x4, ctx.shared_memory_u32x4, offset, 4)};
|
||||
return ctx.OpLoad(ctx.U32[4], pointer);
|
||||
}
|
||||
@@ -110,7 +113,7 @@ Id EmitLoadSharedU128(EmitContext& ctx, Id offset) {
|
||||
}
|
||||
|
||||
void EmitWriteSharedU8(EmitContext& ctx, Id offset, Id value) {
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
if (ctx.uses_explicit_workgroup_layout) {
|
||||
const Id pointer{
|
||||
ctx.OpAccessChain(ctx.shared_u8, ctx.shared_memory_u8, ctx.u32_zero_value, offset)};
|
||||
ctx.OpStore(pointer, ctx.OpUConvert(ctx.U8, value));
|
||||
@@ -120,7 +123,7 @@ void EmitWriteSharedU8(EmitContext& ctx, Id offset, Id value) {
|
||||
}
|
||||
|
||||
void EmitWriteSharedU16(EmitContext& ctx, Id offset, Id value) {
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
if (ctx.uses_explicit_workgroup_layout) {
|
||||
const Id pointer{Pointer(ctx, ctx.shared_u16, ctx.shared_memory_u16, offset, 1)};
|
||||
ctx.OpStore(pointer, ctx.OpUConvert(ctx.U16, value));
|
||||
} else {
|
||||
@@ -130,7 +133,7 @@ void EmitWriteSharedU16(EmitContext& ctx, Id offset, Id value) {
|
||||
|
||||
void EmitWriteSharedU32(EmitContext& ctx, Id offset, Id value) {
|
||||
Id pointer{};
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
if (ctx.uses_explicit_workgroup_layout) {
|
||||
pointer = Pointer(ctx, ctx.shared_u32, ctx.shared_memory_u32, offset, 2);
|
||||
} else {
|
||||
const Id shift{ctx.Const(2U)};
|
||||
@@ -141,7 +144,7 @@ void EmitWriteSharedU32(EmitContext& ctx, Id offset, Id value) {
|
||||
}
|
||||
|
||||
void EmitWriteSharedU64(EmitContext& ctx, Id offset, Id value) {
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
if (ctx.uses_explicit_workgroup_layout) {
|
||||
const Id pointer{Pointer(ctx, ctx.shared_u32x2, ctx.shared_memory_u32x2, offset, 3)};
|
||||
ctx.OpStore(pointer, value);
|
||||
return;
|
||||
@@ -156,7 +159,7 @@ void EmitWriteSharedU64(EmitContext& ctx, Id offset, Id value) {
|
||||
}
|
||||
|
||||
void EmitWriteSharedU128(EmitContext& ctx, Id offset, Id value) {
|
||||
if (ctx.profile.support_explicit_workgroup_layout) {
|
||||
if (ctx.uses_explicit_workgroup_layout) {
|
||||
const Id pointer{Pointer(ctx, ctx.shared_u32x4, ctx.shared_memory_u32x4, offset, 4)};
|
||||
ctx.OpStore(pointer, value);
|
||||
return;
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -10,7 +13,14 @@ Id SubgroupScope(EmitContext& ctx) {
|
||||
return ctx.Const(static_cast<u32>(spv::Scope::Subgroup));
|
||||
}
|
||||
|
||||
bool StageSupportsSubgroups(EmitContext& ctx) {
|
||||
return ctx.profile.SupportsSubgroupStage(ctx.stage);
|
||||
}
|
||||
|
||||
Id GetThreadId(EmitContext& ctx) {
|
||||
if (!StageSupportsSubgroups(ctx)) {
|
||||
return ctx.u32_zero_value;
|
||||
}
|
||||
return ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id);
|
||||
}
|
||||
|
||||
@@ -68,6 +78,9 @@ Id GetMaxThreadId(EmitContext& ctx, Id thread_id, Id clamp, Id segmentation_mask
|
||||
}
|
||||
|
||||
Id SelectValue(EmitContext& ctx, Id in_range, Id value, Id src_thread_id) {
|
||||
if (!StageSupportsSubgroups(ctx)) {
|
||||
return value;
|
||||
}
|
||||
return ctx.OpSelect(
|
||||
ctx.U32[1], in_range,
|
||||
ctx.OpGroupNonUniformShuffle(ctx.U32[1], SubgroupScope(ctx), value, src_thread_id), value);
|
||||
@@ -89,6 +102,9 @@ Id EmitLaneId(EmitContext& ctx) {
|
||||
}
|
||||
|
||||
Id EmitVoteAll(EmitContext& ctx, Id pred) {
|
||||
if (!StageSupportsSubgroups(ctx)) {
|
||||
return pred;
|
||||
}
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
return ctx.OpGroupNonUniformAll(ctx.U1, SubgroupScope(ctx), pred);
|
||||
}
|
||||
@@ -102,6 +118,9 @@ Id EmitVoteAll(EmitContext& ctx, Id pred) {
|
||||
}
|
||||
|
||||
Id EmitVoteAny(EmitContext& ctx, Id pred) {
|
||||
if (!StageSupportsSubgroups(ctx)) {
|
||||
return pred;
|
||||
}
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
return ctx.OpGroupNonUniformAny(ctx.U1, SubgroupScope(ctx), pred);
|
||||
}
|
||||
@@ -115,6 +134,9 @@ Id EmitVoteAny(EmitContext& ctx, Id pred) {
|
||||
}
|
||||
|
||||
Id EmitVoteEqual(EmitContext& ctx, Id pred) {
|
||||
if (!StageSupportsSubgroups(ctx)) {
|
||||
return ctx.true_value;
|
||||
}
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
return ctx.OpGroupNonUniformAllEqual(ctx.U1, SubgroupScope(ctx), pred);
|
||||
}
|
||||
@@ -129,6 +151,9 @@ Id EmitVoteEqual(EmitContext& ctx, Id pred) {
|
||||
}
|
||||
|
||||
Id EmitSubgroupBallot(EmitContext& ctx, Id pred) {
|
||||
if (!StageSupportsSubgroups(ctx)) {
|
||||
return ctx.OpSelect(ctx.U32[1], pred, ctx.Const(1u), ctx.u32_zero_value);
|
||||
}
|
||||
const Id ballot{ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), pred)};
|
||||
if (!ctx.profile.warp_size_potentially_larger_than_guest) {
|
||||
return ctx.OpCompositeExtract(ctx.U32[1], ballot, 0U);
|
||||
@@ -137,22 +162,37 @@ Id EmitSubgroupBallot(EmitContext& ctx, Id pred) {
|
||||
}
|
||||
|
||||
Id EmitSubgroupEqMask(EmitContext& ctx) {
|
||||
if (!StageSupportsSubgroups(ctx)) {
|
||||
return ctx.Const(1u);
|
||||
}
|
||||
return LoadMask(ctx, ctx.subgroup_mask_eq);
|
||||
}
|
||||
|
||||
Id EmitSubgroupLtMask(EmitContext& ctx) {
|
||||
if (!StageSupportsSubgroups(ctx)) {
|
||||
return ctx.u32_zero_value;
|
||||
}
|
||||
return LoadMask(ctx, ctx.subgroup_mask_lt);
|
||||
}
|
||||
|
||||
Id EmitSubgroupLeMask(EmitContext& ctx) {
|
||||
if (!StageSupportsSubgroups(ctx)) {
|
||||
return ctx.Const(1u);
|
||||
}
|
||||
return LoadMask(ctx, ctx.subgroup_mask_le);
|
||||
}
|
||||
|
||||
Id EmitSubgroupGtMask(EmitContext& ctx) {
|
||||
if (!StageSupportsSubgroups(ctx)) {
|
||||
return ctx.u32_zero_value;
|
||||
}
|
||||
return LoadMask(ctx, ctx.subgroup_mask_gt);
|
||||
}
|
||||
|
||||
Id EmitSubgroupGeMask(EmitContext& ctx) {
|
||||
if (!StageSupportsSubgroups(ctx)) {
|
||||
return ctx.Const(1u);
|
||||
}
|
||||
return LoadMask(ctx, ctx.subgroup_mask_ge);
|
||||
}
|
||||
|
||||
@@ -222,7 +262,7 @@ Id EmitShuffleButterfly(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id
|
||||
|
||||
Id EmitFSwizzleAdd(EmitContext& ctx, Id op_a, Id op_b, Id swizzle) {
|
||||
const Id three{ctx.Const(3U)};
|
||||
Id mask{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)};
|
||||
Id mask{GetThreadId(ctx)};
|
||||
mask = ctx.OpBitwiseAnd(ctx.U32[1], mask, three);
|
||||
mask = ctx.OpShiftLeftLogical(ctx.U32[1], mask, ctx.Const(1U));
|
||||
mask = ctx.OpShiftRightLogical(ctx.U32[1], swizzle, mask);
|
||||
|
||||
@@ -371,7 +371,7 @@ Id CasFunction(EmitContext& ctx, Operation operation, Id value_type) {
|
||||
Id CasLoop(EmitContext& ctx, Operation operation, Id array_pointer, Id element_pointer,
|
||||
Id value_type, Id memory_type, spv::Scope scope) {
|
||||
const bool is_shared{scope == spv::Scope::Workgroup};
|
||||
const bool is_struct{!is_shared || ctx.profile.support_explicit_workgroup_layout};
|
||||
const bool is_struct{!is_shared || ctx.uses_explicit_workgroup_layout};
|
||||
const Id cas_func{CasFunction(ctx, operation, value_type)};
|
||||
const Id zero{ctx.u32_zero_value};
|
||||
const Id scope_id{ctx.Const(static_cast<u32>(scope))};
|
||||
@@ -620,7 +620,11 @@ void EmitContext::DefineSharedMemory(const IR::Program& program) {
|
||||
|
||||
return std::make_tuple(variable, element_pointer, pointer);
|
||||
}};
|
||||
if (profile.support_explicit_workgroup_layout) {
|
||||
uses_explicit_workgroup_layout =
|
||||
profile.support_explicit_workgroup_layout &&
|
||||
(!program.info.uses_int8 || profile.support_workgroup_layout_8bit_access) &&
|
||||
(!program.info.uses_int16 || profile.support_workgroup_layout_16bit_access);
|
||||
if (uses_explicit_workgroup_layout) {
|
||||
AddExtension("SPV_KHR_workgroup_memory_explicit_layout");
|
||||
AddCapability(spv::Capability::WorkgroupMemoryExplicitLayoutKHR);
|
||||
if (program.info.uses_int8) {
|
||||
@@ -938,6 +942,10 @@ void EmitContext::DefineGlobalMemoryFunctions(const Info& info) {
|
||||
if (!info.uses_global_memory || !profile.support_int64) {
|
||||
return;
|
||||
}
|
||||
if (!profile.support_descriptor_aliasing) {
|
||||
DefineGlobalMemoryFunctionsU32Fallback(info);
|
||||
return;
|
||||
}
|
||||
using DefPtr = Id StorageDefinitions::*;
|
||||
const Id zero{u32_zero_value};
|
||||
const auto define_body{[&](DefPtr ssbo_member, Id addr, Id element_pointer, u32 shift,
|
||||
@@ -1015,6 +1023,111 @@ void EmitContext::DefineGlobalMemoryFunctions(const Info& info) {
|
||||
define(&StorageDefinitions::U32x4, storage_types.U32x4, U32[4], sizeof(u32[4]));
|
||||
}
|
||||
|
||||
void EmitContext::DefineGlobalMemoryFunctionsU32Fallback(const Info& info) {
|
||||
const Id zero{u32_zero_value};
|
||||
const auto define_body{[&](Id addr, u32 num_words, auto&& callback) {
|
||||
AddLabel();
|
||||
const size_t num_buffers{info.storage_buffers_descriptors.size()};
|
||||
for (size_t index = 0; index < num_buffers; ++index) {
|
||||
if (!info.nvn_buffer_used[index]) {
|
||||
continue;
|
||||
}
|
||||
const auto& ssbo{info.storage_buffers_descriptors[index]};
|
||||
const u32 addr_word{ssbo.cbuf_offset / 4};
|
||||
const u32 addr_lo_comp{addr_word % 4};
|
||||
const Id cbuf{cbufs[ssbo.cbuf_index].U32x4};
|
||||
const Id addr_vec_pointer{
|
||||
OpAccessChain(uniform_types.U32x4, cbuf, zero, Const(addr_word / 4))};
|
||||
const Id addr_vec{OpLoad(U32[4], addr_vec_pointer)};
|
||||
const Id addr_lo{OpCompositeExtract(U32[1], addr_vec, addr_lo_comp)};
|
||||
const Id addr_hi{OpCompositeExtract(U32[1], addr_vec, addr_lo_comp + 1U)};
|
||||
const Id unaligned_addr{
|
||||
OpBitcast(U64, OpCompositeConstruct(U32[2], addr_lo, addr_hi))};
|
||||
|
||||
const u64 ssbo_align_mask{~(profile.min_ssbo_alignment - 1U)};
|
||||
const Id ssbo_addr{OpBitwiseAnd(U64, unaligned_addr, Constant(U64, ssbo_align_mask))};
|
||||
|
||||
const u32 size_word{addr_word + 2};
|
||||
Id size_vec{addr_vec};
|
||||
if (size_word / 4 != addr_word / 4) {
|
||||
const Id size_vec_pointer{
|
||||
OpAccessChain(uniform_types.U32x4, cbuf, zero, Const(size_word / 4))};
|
||||
size_vec = OpLoad(U32[4], size_vec_pointer);
|
||||
}
|
||||
const Id ssbo_size{
|
||||
OpUConvert(U64, OpCompositeExtract(U32[1], size_vec, size_word % 4))};
|
||||
const Id ssbo_end{OpIAdd(U64, ssbo_addr, ssbo_size)};
|
||||
const Id cond{OpLogicalAnd(U1, OpUGreaterThanEqual(U1, addr, ssbo_addr),
|
||||
OpULessThan(U1, addr, ssbo_end))};
|
||||
const Id then_label{OpLabel()};
|
||||
const Id else_label{OpLabel()};
|
||||
OpSelectionMerge(else_label, spv::SelectionControlMask::MaskNone);
|
||||
OpBranchConditional(cond, then_label, else_label);
|
||||
AddLabel(then_label);
|
||||
const Id ssbo_id{ssbos[index].U32};
|
||||
const Id ssbo_offset{OpUConvert(U32[1], OpISub(U64, addr, ssbo_addr))};
|
||||
const Id base_word{OpShiftRightLogical(U32[1], ssbo_offset, Const(2U))};
|
||||
std::array<Id, 4> word_pointers{};
|
||||
for (u32 word = 0; word < num_words; ++word) {
|
||||
const Id word_index{word == 0 ? base_word
|
||||
: OpIAdd(U32[1], base_word, Const(word))};
|
||||
word_pointers[word] =
|
||||
OpAccessChain(storage_types.U32.element, ssbo_id, zero, word_index);
|
||||
}
|
||||
callback(word_pointers);
|
||||
AddLabel(else_label);
|
||||
}
|
||||
}};
|
||||
const auto define_load{[&](Id type, u32 num_words) {
|
||||
const Id function_type{TypeFunction(type, U64)};
|
||||
const Id func_id{OpFunction(type, spv::FunctionControlMask::MaskNone, function_type)};
|
||||
const Id addr{OpFunctionParameter(U64)};
|
||||
define_body(addr, num_words, [&](const std::array<Id, 4>& pointers) {
|
||||
std::array<Id, 4> words{};
|
||||
for (u32 word = 0; word < num_words; ++word) {
|
||||
words[word] = OpLoad(U32[1], pointers[word]);
|
||||
}
|
||||
switch (num_words) {
|
||||
case 1:
|
||||
OpReturnValue(words[0]);
|
||||
break;
|
||||
case 2:
|
||||
OpReturnValue(OpCompositeConstruct(type, words[0], words[1]));
|
||||
break;
|
||||
default:
|
||||
OpReturnValue(
|
||||
OpCompositeConstruct(type, words[0], words[1], words[2], words[3]));
|
||||
break;
|
||||
}
|
||||
});
|
||||
OpReturnValue(ConstantNull(type));
|
||||
OpFunctionEnd();
|
||||
return func_id;
|
||||
}};
|
||||
const auto define_write{[&](Id type, u32 num_words) {
|
||||
const Id function_type{TypeFunction(void_id, U64, type)};
|
||||
const Id func_id{OpFunction(void_id, spv::FunctionControlMask::MaskNone, function_type)};
|
||||
const Id addr{OpFunctionParameter(U64)};
|
||||
const Id data{OpFunctionParameter(type)};
|
||||
define_body(addr, num_words, [&](const std::array<Id, 4>& pointers) {
|
||||
for (u32 word = 0; word < num_words; ++word) {
|
||||
const Id value{num_words == 1 ? data : OpCompositeExtract(U32[1], data, word)};
|
||||
OpStore(pointers[word], value);
|
||||
}
|
||||
OpReturn();
|
||||
});
|
||||
OpReturn();
|
||||
OpFunctionEnd();
|
||||
return func_id;
|
||||
}};
|
||||
load_global_func_u32 = define_load(U32[1], 1);
|
||||
load_global_func_u32x2 = define_load(U32[2], 2);
|
||||
load_global_func_u32x4 = define_load(U32[4], 4);
|
||||
write_global_func_u32 = define_write(U32[1], 1);
|
||||
write_global_func_u32x2 = define_write(U32[2], 2);
|
||||
write_global_func_u32x4 = define_write(U32[4], 4);
|
||||
}
|
||||
|
||||
void EmitContext::DefineRescalingInput(const Info& info) {
|
||||
if (!info.uses_rescaling_uniform) {
|
||||
return;
|
||||
@@ -1438,7 +1551,7 @@ void EmitContext::DefineInputs(const IR::Program& program) {
|
||||
if (info.uses_is_helper_invocation) {
|
||||
is_helper_invocation = DefineInput(*this, U1, false, spv::BuiltIn::HelperInvocation);
|
||||
}
|
||||
if (info.uses_subgroup_mask) {
|
||||
if (info.uses_subgroup_mask && profile.SupportsSubgroupStage(stage)) {
|
||||
subgroup_mask_eq = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupEqMaskKHR);
|
||||
subgroup_mask_lt = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupLtMaskKHR);
|
||||
subgroup_mask_le = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupLeMaskKHR);
|
||||
@@ -1452,9 +1565,10 @@ void EmitContext::DefineInputs(const IR::Program& program) {
|
||||
Decorate(subgroup_mask_ge, spv::Decoration::Flat);
|
||||
}
|
||||
}
|
||||
if (info.uses_fswzadd || info.uses_subgroup_invocation_id || info.uses_subgroup_shuffles ||
|
||||
(profile.warp_size_potentially_larger_than_guest &&
|
||||
(info.uses_subgroup_vote || info.uses_subgroup_mask))) {
|
||||
if ((info.uses_fswzadd || info.uses_subgroup_invocation_id || info.uses_subgroup_shuffles ||
|
||||
(profile.warp_size_potentially_larger_than_guest &&
|
||||
(info.uses_subgroup_vote || info.uses_subgroup_mask))) &&
|
||||
profile.SupportsSubgroupStage(stage)) {
|
||||
AddCapability(spv::Capability::GroupNonUniform);
|
||||
subgroup_local_invocation_id =
|
||||
DefineInput(*this, U32[1], false, spv::BuiltIn::SubgroupLocalInvocationId);
|
||||
|
||||
@@ -310,6 +310,10 @@ public:
|
||||
|
||||
Id local_memory{};
|
||||
|
||||
/// True when this shader's shared memory uses SPV_KHR_workgroup_memory_explicit_layout.
|
||||
/// False when the host lacks the extension or a width this shader accesses natively.
|
||||
bool uses_explicit_workgroup_layout{};
|
||||
|
||||
Id shared_memory_u8{};
|
||||
Id shared_memory_u16{};
|
||||
Id shared_memory_u32{};
|
||||
@@ -387,6 +391,7 @@ private:
|
||||
void DefineAttributeMemAccess(const Info& info);
|
||||
void DefineWriteStorageCasLoopFunction(const Info& info);
|
||||
void DefineGlobalMemoryFunctions(const Info& info);
|
||||
void DefineGlobalMemoryFunctionsU32Fallback(const Info& info);
|
||||
void DefineRescalingInput(const Info& info);
|
||||
void DefineRescalingInputPushConstant();
|
||||
void DefineRescalingInputUniformConstant();
|
||||
|
||||
@@ -236,8 +236,11 @@ void LowerGeometryPassthrough(const IR::Program& program, const HostTranslateInf
|
||||
|
||||
IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool,
|
||||
Environment& env, Flow::CFG& cfg, const HostTranslateInfo& host_info) {
|
||||
HostTranslateInfo normalized_host_info{host_info};
|
||||
normalized_host_info.ApplyDescriptorLimitPolicy();
|
||||
|
||||
IR::Program program;
|
||||
program.syntax_list = BuildASL(inst_pool, block_pool, env, cfg, host_info);
|
||||
program.syntax_list = BuildASL(inst_pool, block_pool, env, cfg, normalized_host_info);
|
||||
program.blocks = GenerateBlocks(program.syntax_list);
|
||||
program.post_order_blocks = PostOrder(program.syntax_list.front());
|
||||
program.stage = env.ShaderStage();
|
||||
@@ -260,9 +263,9 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
|
||||
program.info.passthrough.mask[i] = ((mask[i / 32] >> (i % 32)) & 1) == 0;
|
||||
}
|
||||
|
||||
if (!host_info.support_geometry_shader_passthrough) {
|
||||
if (!normalized_host_info.support_geometry_shader_passthrough) {
|
||||
program.output_vertices = GetOutputTopologyVertices(program.output_topology);
|
||||
LowerGeometryPassthrough(program, host_info);
|
||||
LowerGeometryPassthrough(program, normalized_host_info);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -277,16 +280,16 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
|
||||
RemoveUnreachableBlocks(program);
|
||||
|
||||
// Replace instructions before the SSA rewrite
|
||||
if (!host_info.support_float64) {
|
||||
if (!normalized_host_info.support_float64) {
|
||||
Optimization::LowerFp64ToFp32(program);
|
||||
}
|
||||
if (!host_info.support_float16) {
|
||||
if (!normalized_host_info.support_float16) {
|
||||
Optimization::LowerFp16ToFp32(program);
|
||||
}
|
||||
if (!host_info.support_int64) {
|
||||
if (!normalized_host_info.support_int64) {
|
||||
Optimization::LowerInt64ToInt32(program);
|
||||
}
|
||||
if (!host_info.support_conditional_barrier) {
|
||||
if (!normalized_host_info.support_conditional_barrier) {
|
||||
Optimization::ConditionalBarrierPass(program);
|
||||
}
|
||||
Optimization::SsaRewritePass(program);
|
||||
@@ -295,8 +298,8 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
|
||||
|
||||
Optimization::PositionPass(env, program);
|
||||
|
||||
Optimization::GlobalMemoryToStorageBufferPass(program, host_info);
|
||||
Optimization::TexturePass(env, program, host_info);
|
||||
Optimization::GlobalMemoryToStorageBufferPass(program, normalized_host_info);
|
||||
Optimization::TexturePass(env, program, normalized_host_info);
|
||||
|
||||
if (Settings::values.resolution_info.active || Settings::values.rescale_hack.GetValue()) {
|
||||
Optimization::RescalingPass(program);
|
||||
@@ -306,7 +309,7 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
|
||||
Optimization::VerificationPass(program);
|
||||
}
|
||||
Optimization::CollectShaderInfoPass(env, program);
|
||||
Optimization::LayerPass(program, host_info);
|
||||
Optimization::LayerPass(program, normalized_host_info);
|
||||
Optimization::VendorWorkaroundPass(program);
|
||||
|
||||
CollectInterpolationInfo(env, program);
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Shader {
|
||||
|
||||
// Try to keep entries here to a minimum
|
||||
@@ -13,20 +15,52 @@ namespace Shader {
|
||||
|
||||
/// Misc information about the host
|
||||
struct HostTranslateInfo {
|
||||
static constexpr u32 DEFAULT_DESCRIPTOR_LIMIT = 1024;
|
||||
|
||||
u64 min_ssbo_alignment{}; ///< Minimum alignment supported by the device for SSBOs
|
||||
u32 max_per_stage_descriptor_sampled_images{}; ///< maximum sampled descriptors per stage
|
||||
u32 max_per_stage_resources{}; ///< maximum resources per stage
|
||||
u32 max_descriptor_set_samplers{};
|
||||
u32 max_descriptor_set_uniform_buffers{};
|
||||
u32 max_descriptor_set_uniform_buffers_dynamic{};
|
||||
u32 max_descriptor_set_storage_buffers{};
|
||||
u32 max_descriptor_set_storage_buffers_dynamic{};
|
||||
u32 max_descriptor_set_sampled_images{};
|
||||
u32 max_descriptor_set_storage_images{};
|
||||
u32 max_descriptor_set_input_attachements{};
|
||||
bool support_float64{}; ///< True when the device supports 64-bit floats
|
||||
bool support_float16{}; ///< True when the device supports 16-bit floats
|
||||
bool support_int64{}; ///< True when the device supports 64-bit integers
|
||||
bool needs_demote_reorder{}; ///< True when the device needs DemoteToHelperInvocation reordered
|
||||
bool support_snorm_render_buffer{}; ///< True when the device supports SNORM render buffers
|
||||
bool support_viewport_index_layer{}; ///< True when the device supports gl_Layer in VS
|
||||
u32 min_ssbo_alignment{}; ///< Minimum alignment supported by the device for SSBOs
|
||||
u32 max_per_stage_descriptor_sampled_images{1024}; ///< maximum sampled descriptors per stage
|
||||
u32 max_per_stage_resources{4096}; ///< maximum resources per stage
|
||||
u32 max_descriptor_set_sampled_images{1024}; ///< maximum sampled descriptors per set
|
||||
bool support_geometry_shader_passthrough{}; ///< True when the device supports geometry
|
||||
///< passthrough shaders
|
||||
bool support_conditional_barrier{}; ///< True when the device supports barriers in conditional
|
||||
///< control flow
|
||||
|
||||
void ApplyDescriptorLimitPolicy() noexcept {
|
||||
if (min_ssbo_alignment == 0) {
|
||||
min_ssbo_alignment = 1;
|
||||
}
|
||||
ApplyDescriptorLimitFallback(max_per_stage_descriptor_sampled_images);
|
||||
ApplyDescriptorLimitFallback(max_per_stage_resources);
|
||||
ApplyDescriptorLimitFallback(max_descriptor_set_samplers);
|
||||
ApplyDescriptorLimitFallback(max_descriptor_set_uniform_buffers);
|
||||
ApplyDescriptorLimitFallback(max_descriptor_set_uniform_buffers_dynamic);
|
||||
ApplyDescriptorLimitFallback(max_descriptor_set_storage_buffers);
|
||||
ApplyDescriptorLimitFallback(max_descriptor_set_storage_buffers_dynamic);
|
||||
ApplyDescriptorLimitFallback(max_descriptor_set_sampled_images);
|
||||
ApplyDescriptorLimitFallback(max_descriptor_set_storage_images);
|
||||
ApplyDescriptorLimitFallback(max_descriptor_set_input_attachements);
|
||||
}
|
||||
|
||||
private:
|
||||
static void ApplyDescriptorLimitFallback(u32& limit) noexcept {
|
||||
if (limit == 0) {
|
||||
limit = DEFAULT_DESCRIPTOR_LIMIT;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Shader
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -545,7 +548,7 @@ void GlobalMemoryToStorageBufferPass(IR::Program& program, const HostTranslateIn
|
||||
IR::Block* const block{storage_inst.block};
|
||||
IR::Inst* const inst{storage_inst.inst};
|
||||
const IR::U32 offset{
|
||||
StorageOffset(*block, *inst, storage_buffer, host_info.min_ssbo_alignment)};
|
||||
StorageOffset(*block, *inst, storage_buffer, u32(host_info.min_ssbo_alignment))};
|
||||
Replace(*block, *inst, index, offset);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,71 +32,62 @@ struct TextureInst {
|
||||
using TextureInstVector = boost::container::small_vector<TextureInst, 24>;
|
||||
|
||||
constexpr u32 DESCRIPTOR_SIZE = 8;
|
||||
constexpr u32 DESCRIPTOR_SIZE_SHIFT = static_cast<u32>(std::countr_zero(DESCRIPTOR_SIZE));
|
||||
constexpr u32 DYNAMIC_DESCRIPTOR_CBUF_BYTES = 16 * 1024;
|
||||
constexpr u32 MAX_DYNAMIC_DESCRIPTOR_COUNT = 1024;
|
||||
constexpr u32 DESCRIPTOR_SIZE_SHIFT = u32(std::countr_zero(DESCRIPTOR_SIZE));
|
||||
constexpr u32 DESCRIPTOR_MAX_COUNT = 1024;
|
||||
|
||||
u32 DynamicDescriptorSizeShift(const IR::U32& dynamic_offset) {
|
||||
const IR::Inst* const inst{dynamic_offset.InstRecursive()};
|
||||
if (!inst || inst->GetOpcode() != IR::Opcode::ShiftLeftLogical32) {
|
||||
const IR::Inst* const inst = dynamic_offset.InstRecursive();
|
||||
if (!inst || inst->GetOpcode() != IR::Opcode::ShiftLeftLogical32)
|
||||
return DESCRIPTOR_SIZE_SHIFT;
|
||||
}
|
||||
const IR::Value shift{inst->Arg(1)};
|
||||
if (!shift.IsImmediate()) {
|
||||
const IR::Value shift = inst->Arg(1);
|
||||
if (!shift.IsImmediate())
|
||||
return DESCRIPTOR_SIZE_SHIFT;
|
||||
}
|
||||
const u32 size_shift{shift.U32()};
|
||||
return size_shift >= DESCRIPTOR_SIZE_SHIFT && size_shift < 31 ? size_shift
|
||||
: DESCRIPTOR_SIZE_SHIFT;
|
||||
const u32 size_shift = shift.U32();
|
||||
return size_shift >= DESCRIPTOR_SIZE_SHIFT && size_shift < 31 ? size_shift : DESCRIPTOR_SIZE_SHIFT;
|
||||
}
|
||||
|
||||
u32 DynamicDescriptorCount(u32 base_offset, u32 size_shift) {
|
||||
if (size_shift >= 31 || base_offset >= DYNAMIC_DESCRIPTOR_CBUF_BYTES) {
|
||||
u32 DynamicDescriptorCount(u32 base_offset, u32 size_shift, u32 max_descriptors) {
|
||||
auto const descriptor_limit = (std::max)(1U, max_descriptors);
|
||||
auto const max_cbuf_bytes = 16 * descriptor_limit;
|
||||
if (size_shift >= 31 || base_offset >= max_cbuf_bytes)
|
||||
return 1;
|
||||
}
|
||||
const u32 stride{1U << size_shift};
|
||||
const u32 available{DYNAMIC_DESCRIPTOR_CBUF_BYTES - base_offset};
|
||||
if (available < DESCRIPTOR_SIZE) {
|
||||
auto const stride = 1U << size_shift;
|
||||
auto const available = max_cbuf_bytes - base_offset;
|
||||
if (available < DESCRIPTOR_SIZE)
|
||||
return 1;
|
||||
}
|
||||
const u32 available_count{1U + (available - DESCRIPTOR_SIZE) / stride};
|
||||
return std::min(MAX_DYNAMIC_DESCRIPTOR_COUNT, available_count);
|
||||
auto const available_count = 1U + (available - DESCRIPTOR_SIZE) / stride;
|
||||
return std::min(descriptor_limit, available_count);
|
||||
}
|
||||
|
||||
u32 SaturatingSub(u32 lhs, u32 rhs) {
|
||||
return lhs > rhs ? lhs - rhs : 0;
|
||||
}
|
||||
|
||||
template <typename Descriptors>
|
||||
u32 StaticDescriptorCount(const Descriptors& descriptors) {
|
||||
u32 count{};
|
||||
for (const auto& desc : descriptors) {
|
||||
if (desc.count <= 1) {
|
||||
count += desc.count;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
template <typename T>
|
||||
[[nodiscard]] u32 StaticDescriptorCount(T const& descriptors) noexcept {
|
||||
return std::accumulate(descriptors.cbegin(), descriptors.cend(), 0U, [](auto const& acc, auto const& e) {
|
||||
return acc + (e.count <= 1 ? e.count : 0);
|
||||
});
|
||||
}
|
||||
|
||||
u32 DynamicSampledTextureCap(const Info& info, const HostTranslateInfo& host_info,
|
||||
u32 dynamic_arrays) {
|
||||
if (dynamic_arrays == 0) {
|
||||
return MAX_DYNAMIC_DESCRIPTOR_COUNT;
|
||||
u32 DynamicSampledTextureCap(const Info& info, const HostTranslateInfo& host_info, u32 dynamic_arrays) {
|
||||
auto const sampled_limit = (std::max)(1U, std::min(host_info.max_per_stage_descriptor_sampled_images,
|
||||
host_info.max_descriptor_set_sampled_images));
|
||||
auto const resource_limit = (std::max)(1U, host_info.max_per_stage_resources);
|
||||
if (dynamic_arrays > 0) {
|
||||
auto const sampled_static_count = StaticDescriptorCount(info.texture_buffer_descriptors) + StaticDescriptorCount(info.texture_descriptors);
|
||||
auto const resource_static_count =
|
||||
NumDescriptors(info.constant_buffer_descriptors)
|
||||
+ NumDescriptors(info.storage_buffers_descriptors)
|
||||
+ sampled_static_count + NumDescriptors(info.image_buffer_descriptors)
|
||||
+ NumDescriptors(info.image_descriptors);
|
||||
auto const sampled_budget = SaturatingSub(sampled_limit, sampled_static_count);
|
||||
auto const resource_budget = SaturatingSub(resource_limit, resource_static_count);
|
||||
auto const sampled_cap = sampled_budget / dynamic_arrays;
|
||||
auto const resource_cap = resource_budget / dynamic_arrays;
|
||||
return (std::max)(1U, (std::min)(sampled_cap, resource_cap));
|
||||
}
|
||||
const u32 sampled_static_count{StaticDescriptorCount(info.texture_buffer_descriptors) +
|
||||
StaticDescriptorCount(info.texture_descriptors)};
|
||||
const u32 resource_static_count{
|
||||
NumDescriptors(info.constant_buffer_descriptors) +
|
||||
NumDescriptors(info.storage_buffers_descriptors) + sampled_static_count +
|
||||
NumDescriptors(info.image_buffer_descriptors) + NumDescriptors(info.image_descriptors)};
|
||||
const u32 sampled_limit{std::min(host_info.max_per_stage_descriptor_sampled_images,
|
||||
host_info.max_descriptor_set_sampled_images)};
|
||||
const u32 sampled_budget{SaturatingSub(sampled_limit, sampled_static_count)};
|
||||
const u32 resource_budget{SaturatingSub(host_info.max_per_stage_resources,
|
||||
resource_static_count)};
|
||||
const u32 sampled_cap{sampled_budget / dynamic_arrays};
|
||||
const u32 resource_cap{resource_budget / dynamic_arrays};
|
||||
return std::max(1U, std::min({MAX_DYNAMIC_DESCRIPTOR_COUNT, sampled_cap, resource_cap}));
|
||||
return (std::min)({DESCRIPTOR_MAX_COUNT, sampled_limit, resource_limit});
|
||||
}
|
||||
|
||||
IR::Opcode IndexedInstruction(const IR::Inst& inst) {
|
||||
@@ -304,21 +295,23 @@ static inline bool IsTexturePixelFormatIntegerCached(Environment& env,
|
||||
}
|
||||
|
||||
|
||||
std::optional<ConstBufferAddr> Track(const IR::Value& value, Environment& env);
|
||||
static inline std::optional<ConstBufferAddr> TrackCached(const IR::Value& v, Environment& env) {
|
||||
std::optional<ConstBufferAddr> Track(const IR::Value& value, Environment& env, const HostTranslateInfo& host_info);
|
||||
static inline std::optional<ConstBufferAddr> TrackCached(const IR::Value& v, Environment& env, const HostTranslateInfo& host_info) {
|
||||
if (const IR::Inst* key = v.InstRecursive()) {
|
||||
if (auto it = env.track_cache.find(key); it != env.track_cache.end()) return it->second;
|
||||
auto found = Track(v, env);
|
||||
auto found = Track(v, env, host_info);
|
||||
if (found) env.track_cache.emplace(key, *found);
|
||||
return found;
|
||||
}
|
||||
return Track(v, env);
|
||||
return Track(v, env, host_info);
|
||||
}
|
||||
|
||||
std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environment& env);
|
||||
std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environment& env, const HostTranslateInfo& host_info);
|
||||
|
||||
std::optional<ConstBufferAddr> Track(const IR::Value& value, Environment& env) {
|
||||
return IR::BreadthFirstSearch(value, [&env](const IR::Inst* inst) { return TryGetConstBuffer(inst, env); });
|
||||
std::optional<ConstBufferAddr> Track(const IR::Value& value, Environment& env, const HostTranslateInfo& host_info) {
|
||||
return IR::BreadthFirstSearch(value, [&env, &host_info](const IR::Inst* inst) {
|
||||
return TryGetConstBuffer(inst, env, host_info);
|
||||
});
|
||||
}
|
||||
|
||||
std::optional<u32> TryGetConstant(IR::Value& value, Environment& env) {
|
||||
@@ -342,13 +335,13 @@ std::optional<u32> TryGetConstant(IR::Value& value, Environment& env) {
|
||||
return ReadCbufCached(env, index_number, offset_number);
|
||||
}
|
||||
|
||||
std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environment& env) {
|
||||
std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environment& env, const HostTranslateInfo& host_info) {
|
||||
switch (inst->GetOpcode()) {
|
||||
default:
|
||||
return std::nullopt;
|
||||
case IR::Opcode::BitwiseOr32: {
|
||||
std::optional lhs{TrackCached(inst->Arg(0), env)};
|
||||
std::optional rhs{TrackCached(inst->Arg(1), env)};
|
||||
std::optional lhs{TrackCached(inst->Arg(0), env, host_info)};
|
||||
std::optional rhs{TrackCached(inst->Arg(1), env, host_info)};
|
||||
if (!lhs || !rhs) {
|
||||
return std::nullopt;
|
||||
}
|
||||
@@ -378,12 +371,11 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environme
|
||||
if (!shift.IsImmediate()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
std::optional lhs{TrackCached(inst->Arg(0), env)};
|
||||
std::optional lhs{TrackCached(inst->Arg(0), env, host_info)};
|
||||
if (lhs) {
|
||||
lhs->shift_left = shift.U32();
|
||||
}
|
||||
return lhs;
|
||||
break;
|
||||
}
|
||||
case IR::Opcode::BitwiseAnd32: {
|
||||
IR::Value op1{inst->Arg(0)};
|
||||
@@ -407,7 +399,7 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environme
|
||||
return std::nullopt;
|
||||
} while (false);
|
||||
}
|
||||
std::optional lhs{TrackCached(op1, env)};
|
||||
std::optional lhs{TrackCached(op1, env, host_info)};
|
||||
if (lhs) {
|
||||
lhs->shift_left = static_cast<u32>(std::countr_zero(op2.U32()));
|
||||
}
|
||||
@@ -453,7 +445,10 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environme
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
const u32 size_shift{DynamicDescriptorSizeShift(dynamic_offset)};
|
||||
auto const size_shift = DynamicDescriptorSizeShift(dynamic_offset);
|
||||
auto const sampled_limit = (std::max)(1U, (std::min)(host_info.max_per_stage_descriptor_sampled_images,
|
||||
host_info.max_descriptor_set_sampled_images));
|
||||
auto const resource_limit = (std::max)(1U, host_info.max_per_stage_resources);
|
||||
return ConstBufferAddr{
|
||||
.index = index.U32(),
|
||||
.offset = base_offset,
|
||||
@@ -462,15 +457,15 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environme
|
||||
.secondary_offset = 0,
|
||||
.secondary_shift_left = 0,
|
||||
.dynamic_offset = dynamic_offset,
|
||||
.count = DynamicDescriptorCount(base_offset, size_shift),
|
||||
.count = DynamicDescriptorCount(base_offset, size_shift, (std::min)({DESCRIPTOR_MAX_COUNT, sampled_limit, resource_limit})),
|
||||
.has_secondary = false,
|
||||
};
|
||||
}
|
||||
|
||||
TextureInst MakeInst(Environment& env, IR::Block* block, IR::Inst& inst) {
|
||||
TextureInst MakeInst(Environment& env, IR::Block* block, IR::Inst& inst, const HostTranslateInfo& host_info) {
|
||||
ConstBufferAddr addr;
|
||||
if (IsBindless(inst)) {
|
||||
const std::optional<ConstBufferAddr> track_addr{TrackCached(inst.Arg(0), env)};
|
||||
const std::optional<ConstBufferAddr> track_addr{TrackCached(inst.Arg(0), env, host_info)};
|
||||
|
||||
if (!track_addr) {
|
||||
throw NotImplementedException("Failed to track bindless texture constant buffer");
|
||||
@@ -506,15 +501,15 @@ u32 GetTextureHandle(Environment& env, const ConstBufferAddr& cbuf) {
|
||||
return lhs_raw | rhs_raw;
|
||||
}
|
||||
|
||||
[[maybe_unused]]TextureType ReadTextureType(Environment& env, const ConstBufferAddr& cbuf) {
|
||||
[[maybe_unused]] TextureType ReadTextureType(Environment& env, const ConstBufferAddr& cbuf) {
|
||||
return env.ReadTextureType(GetTextureHandle(env, cbuf));
|
||||
}
|
||||
|
||||
[[maybe_unused]]TexturePixelFormat ReadTexturePixelFormat(Environment& env, const ConstBufferAddr& cbuf) {
|
||||
[[maybe_unused]] TexturePixelFormat ReadTexturePixelFormat(Environment& env, const ConstBufferAddr& cbuf) {
|
||||
return env.ReadTexturePixelFormat(GetTextureHandle(env, cbuf));
|
||||
}
|
||||
|
||||
[[maybe_unused]]bool IsTexturePixelFormatInteger(Environment& env, const ConstBufferAddr& cbuf) {
|
||||
[[maybe_unused]] bool IsTexturePixelFormatInteger(Environment& env, const ConstBufferAddr& cbuf) {
|
||||
return env.IsTexturePixelFormatInteger(GetTextureHandle(env, cbuf));
|
||||
}
|
||||
|
||||
@@ -675,7 +670,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
|
||||
if (!IsTextureInstruction(inst)) {
|
||||
continue;
|
||||
}
|
||||
to_replace.push_back(MakeInst(env, block, inst));
|
||||
to_replace.push_back(MakeInst(env, block, inst, host_info));
|
||||
}
|
||||
}
|
||||
// Sort instructions to visit textures by constant buffer index, then by offset
|
||||
@@ -689,8 +684,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
|
||||
program.info.texture_descriptors,
|
||||
program.info.image_descriptors,
|
||||
};
|
||||
const u32 sampled_dynamic_cap{
|
||||
DynamicSampledTextureCap(program.info, host_info, DynamicSampledTextureArrayCount(to_replace))};
|
||||
const u32 sampled_dynamic_cap = DynamicSampledTextureCap(program.info, host_info, DynamicSampledTextureArrayCount(to_replace));
|
||||
for (TextureInst& texture_inst : to_replace) {
|
||||
// TODO: Handle arrays
|
||||
IR::Inst* const inst{texture_inst.inst};
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
|
||||
namespace Shader {
|
||||
|
||||
enum class Stage : u32;
|
||||
|
||||
struct Profile {
|
||||
u32 supported_spirv{0x00010000};
|
||||
bool unified_descriptor_binding{};
|
||||
@@ -29,12 +31,16 @@ struct Profile {
|
||||
bool support_fp32_signed_zero_nan_preserve{};
|
||||
bool support_fp64_signed_zero_nan_preserve{};
|
||||
bool support_explicit_workgroup_layout{};
|
||||
bool support_workgroup_layout_8bit_access{};
|
||||
bool support_workgroup_layout_16bit_access{};
|
||||
bool support_vote{};
|
||||
u32 supported_subgroup_stages{0x7F};
|
||||
bool support_viewport_index_layer_non_geometry{};
|
||||
bool support_viewport_mask{};
|
||||
bool support_typeless_image_loads{};
|
||||
bool support_demote_to_helper_invocation{};
|
||||
bool support_int64_atomics{};
|
||||
bool support_shared_int64_atomics{};
|
||||
bool support_derivative_control{};
|
||||
bool support_geometry_shader_passthrough{};
|
||||
bool support_native_ndc{};
|
||||
@@ -92,8 +98,11 @@ struct Profile {
|
||||
bool has_broken_robust{};
|
||||
|
||||
u64 min_ssbo_alignment{};
|
||||
|
||||
u32 max_user_clip_distances{};
|
||||
|
||||
bool SupportsSubgroupStage(Stage stage) const {
|
||||
return (supported_subgroup_stages & (1u << static_cast<u32>(stage))) != 0;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Shader
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user