Compare commits

...

13 Commits

Author SHA1 Message Date
PavelBARABANOV 7d9fcb0533 add other functions 2026-05-06 17:58:56 +03:00
PavelBARABANOV a88d8e3ebb [hle] GetFriendListForViewerV2 stub 2026-05-06 06:44:19 +03:00
PavelBARABANOV 86eae5cc41 [core] Fix qlaunch crash on second launch (#3930)
partial revert [08232ce642](https://git.eden-emu.dev/eden-emu/eden/commit/08232ce64275eaf388eba7005f4dae28fc18a0b7)

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3930
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-05-06 04:20:55 +02:00
crueter 8b9a841d99 [desktop] Fix QtCommon missing header in bootmanager.cpp (#3931)
Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3931
2026-05-06 03:27:51 +02:00
lizzie ca1fcaca3b [opengl] remove GLAD symbols from builds w/o OpenGL (#3922)
removes unused symbols from non-OpenGL builds, notably mac

I REMEMBER MAKING THIS PR A WHILE AGO but I have no record of it here, so hell lets redo it

Signed-off-by: lizzie <lizzie@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3922
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-05-06 03:23:27 +02:00
Eden CI 8f4e8c6d6a [dist, android] Update translations from Transifex for May 05 (#3929)
Automatic translation update for May 05

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3929
2026-05-05 21:48:58 +02:00
lizzie 4f4c298a39 [hle, service] fix errors related to race conditions triggering under SMG1 and SMG2 (#3927)
- service manager may add a service while someone else is finding it, so properly lock
- nvhost doesn't properly account for the fact that iterators GET FUCKING INVALIDATED
- use proper exit routine for mapping locked that failed (try/catch hell)

the last two were introduced by #3858, but the first one has been present since ???

either way, remember that ankerl map has invalidated iterators upon erase/insert, so i accounted for that and SMG1 (and probably smg2) boot fine now

Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3927
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-05-05 07:31:08 +02:00
maufeat fee603f0b9 [kernel, hle] Initial 22.0.0 kernel changes and cmd stubs (#3761)
- KProcess::Run() and CreateThread() SVC now write the current thread handle to TLS+0x110
- KPageTableBase::LockForMapDeviceAddressSpace now checks for a new KPageTableBase boolean, m_allowed_exec_device_mapping
- Stub `am` + `acc` + `settings` cmd module that needs to work for qlaunch

Thanks to: @alula and @yellows8
Source for changes: https://github.com/Atmosphere-NX/Atmosphere/pull/2744, https://switchbrew.org/, https://yls8.mtheall.com/

Co-authored-by: PavelBARABANOV <pavelbarabanov94@gmail.com>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3761
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-05-05 01:29:46 +02:00
MaranBr fc5fa7f1b2 [video_core] Reapply "Simplify TextureCache GC and remove redundant code" (#3723)
This enhances the garbage collection in TextureCache to make it more responsive and reliable during long gameplay sessions.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3723
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-05-04 21:28:43 +02:00
crueter 17c341ff6c [video_core] index rescaling metadata by descriptor (#3899) (#3924)
#3898 fix was good but exposed a rescaling metadata mismatch that can cause scaled texture descriptors to read the wrong state so this patch tries to keep them aligned with shader lookup

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3899
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: crueter <crueter@eden-emu.dev>

Co-authored-by: ryana <ryanamayque@gmail.com>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3924
2026-05-04 20:27:53 +02:00
PavelBARABANOV 2deee80f29 [android] move GENSHIN_SPOOF flag to genshinSpoof flavor (#3923)
The standard build was downloading the optimized APK and vice versa due to GENSHIN_SPOOF being incorrectly defined in the mainline flavor. This should resolve the issue.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3923
Reviewed-by: crueter <crueter@eden-emu.dev>
2026-05-04 19:54:26 +02:00
ryana 27e5cb0f12 [spirv] mark sampled image descriptor indices non-uniform (#3900)
fixes incorrect texture selection on vk when shaders use per-pixel descriptor indices, in line with #3898 so dynamic descs are no longer treated as uniform

also fixes TD;LTD spotty grass issue on SD not addressed by above pr

you can test out all the fixes here: https://git.eden-emu.dev/may/eden/src/branch/may/integrate-texture-descriptor-fixes

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3900
Reviewed-by: crueter <crueter@eden-emu.dev>
2026-05-04 18:16:11 +02:00
ryana a76c76d122 [shader_recompiler] handle dynamic texture descriptor strides (#3898)
this fixes dynamic texture descriptors that are not laid out as simple 8-byte entries

tested on steam deck/amd

notes
- DynamicDescriptorSizeShift called twice because i moved it away from the struct but doing it this way keeps the patch just in this single file than adding a new derived field in the shared struct (i also think its just a cheap recomputation anyways)
- removed cbuf scanning because i figured out how to do a bounds check statically

credits:
- Mythrax <mythrax@mytrax-rs.org> (identified the 1024 descriptor cap fix in #3897)

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3898
Reviewed-by: crueter <crueter@eden-emu.dev>
2026-05-04 18:15:55 +02:00
45 changed files with 497 additions and 270 deletions
+1 -1
View File
@@ -3403,7 +3403,7 @@ Would you like to delete the old save data?</source>
<message> <message>
<location filename="../../src/yuzu/configuration/configure_graphics_extensions.ui" line="62"/> <location filename="../../src/yuzu/configuration/configure_graphics_extensions.ui" line="62"/>
<source>Vulkan Extensions</source> <source>Vulkan Extensions</source>
<translation>Расшиерния Vulcan</translation> <translation>Расширения Vulcan</translation>
</message> </message>
<message> <message>
<location filename="../../src/yuzu/configuration/configure_graphics_extensions.cpp" line="49"/> <location filename="../../src/yuzu/configuration/configure_graphics_extensions.cpp" line="49"/>
+6 -6
View File
@@ -192,12 +192,6 @@ android {
manifestPlaceholders += mapOf("appNameBase" to "Eden") manifestPlaceholders += mapOf("appNameBase" to "Eden")
resValue("string", "app_name_suffixed", "Eden") resValue("string", "app_name_suffixed", "Eden")
externalNativeBuild {
cmake {
arguments.add("-DGENSHIN_SPOOF=ON")
}
}
ndk { ndk {
abiFilters += listOf("arm64-v8a") abiFilters += listOf("arm64-v8a")
} }
@@ -209,6 +203,12 @@ android {
resValue("string", "app_name_suffixed", "Eden Optimized") resValue("string", "app_name_suffixed", "Eden Optimized")
applicationId = "com.miHoYo.Yuanshen" applicationId = "com.miHoYo.Yuanshen"
externalNativeBuild {
cmake {
arguments.add("-DGENSHIN_SPOOF=ON")
}
}
ndk { ndk {
abiFilters += listOf("arm64-v8a") abiFilters += listOf("arm64-v8a")
} }
@@ -413,7 +413,9 @@
<string name="frame_limit_slider">Limiter le pourcentage de vitesse</string> <string name="frame_limit_slider">Limiter le pourcentage de vitesse</string>
<string name="frame_limit_slider_description">Spécifier le pourcentage pour limiter la vitesse d\'émulation. 100% correspond à la vitesse normale. Des valeurs plus élevées ou plus basses augmenteront ou diminueront la limite de vitesse.</string> <string name="frame_limit_slider_description">Spécifier le pourcentage pour limiter la vitesse d\'émulation. 100% correspond à la vitesse normale. Des valeurs plus élevées ou plus basses augmenteront ou diminueront la limite de vitesse.</string>
<string name="turbo_speed_limit">Vitesse turbo</string> <string name="turbo_speed_limit">Vitesse turbo</string>
<string name="turbo_speed_limit_description">Lorsque le mode Turbo est activé, l\'émulation s\'exécute à cette vitesse.</string>
<string name="slow_speed_limit">Vitesse lente</string> <string name="slow_speed_limit">Vitesse lente</string>
<string name="slow_speed_limit_description">Lorsque le mode Lent est activé, l\'émulation s\'exécute à cette vitesse.</string>
<string name="cpu_backend">Backend du CPU</string> <string name="cpu_backend">Backend du CPU</string>
<string name="cpu_accuracy">Précision du CPU</string> <string name="cpu_accuracy">Précision du CPU</string>
<string name="value_with_units">%1$s%2$s</string> <string name="value_with_units">%1$s%2$s</string>
@@ -489,6 +489,8 @@
<string name="enable_buffer_history">Включить историю буфера</string> <string name="enable_buffer_history">Включить историю буфера</string>
<string name="enable_buffer_history_description">Позволяет обращаться к предыдущим состояниям буфера. Эта опция может повысить качество рендеринга и стабильность производительности в некоторых играх.</string> <string name="enable_buffer_history_description">Позволяет обращаться к предыдущим состояниям буфера. Эта опция может повысить качество рендеринга и стабильность производительности в некоторых играх.</string>
<string name="use_optimized_vertex_buffers">Оптимизированные вершинные буферы</string> <string name="use_optimized_vertex_buffers">Оптимизированные вершинные буферы</string>
<string name="use_optimized_vertex_buffers_description">Включает оптимизированную привязку вершинного буфера для повышения производительности. Требует Mesa Turnip 26.0+ / QCOM драйверы. Приводит к вылету на старых версиях Turnip.</string>
<string name="hacks">Хаки</string> <string name="hacks">Хаки</string>
<string name="fast_gpu_time">Быстрое время ГПУ</string> <string name="fast_gpu_time">Быстрое время ГПУ</string>
@@ -496,6 +498,9 @@
<string name="skip_cpu_inner_invalidation">Пропустить внутреннюю инвалидацию ЦП</string> <string name="skip_cpu_inner_invalidation">Пропустить внутреннюю инвалидацию ЦП</string>
<string name="skip_cpu_inner_invalidation_description">Пропускает некоторые инвалидации кэша на стороне ЦП при обновлениях памяти, уменьшая нагрузку на процессор и повышая производительность. Может вызывать сбои в некоторых играх.</string> <string name="skip_cpu_inner_invalidation_description">Пропускает некоторые инвалидации кэша на стороне ЦП при обновлениях памяти, уменьшая нагрузку на процессор и повышая производительность. Может вызывать сбои в некоторых играх.</string>
<string name="fix_bloom_effects">Исправить эффекты размытия</string> <string name="fix_bloom_effects">Исправить эффекты размытия</string>
<string name="fix_bloom_effects_description">Частично убирает размытие в LA/EOW (Adreno A6XX - A7XX/ Turnip), полностью отключает его в Burnout. Внимание: может вызывать графические артефакты в других играх.</string>
<string name="emulate_bgr565">Эмулировать BGR565</string>
<string name="emulate_bgr565_description">Исправляет проблемы с инвертированными цветами в играх, а также со странными артефактами или некорректными тенями.</string>
<string name="renderer_asynchronous_shaders">Использовать асинхронные шейдеры</string> <string name="renderer_asynchronous_shaders">Использовать асинхронные шейдеры</string>
<string name="renderer_asynchronous_shaders_description">Компилирует шейдеры асинхронно. Это может уменьшить подтормаживания, но также может вызвать графические артефакты.</string> <string name="renderer_asynchronous_shaders_description">Компилирует шейдеры асинхронно. Это может уменьшить подтормаживания, но также может вызвать графические артефакты.</string>
<string name="gpu_unswizzle_settings">Настройки распаковки текстур (Unswizzle)</string> <string name="gpu_unswizzle_settings">Настройки распаковки текстур (Unswizzle)</string>
-1
View File
@@ -415,7 +415,6 @@ struct System::Impl {
cpu_manager.Shutdown(); cpu_manager.Shutdown();
debugger.reset(); debugger.reset();
kernel.Shutdown(); kernel.Shutdown();
device_memory.reset();
stop_event = {}; stop_event = {};
Network::RestartSocketOperations(); Network::RestartSocketOperations();
@@ -268,6 +268,7 @@ Result KPageTableBase::InitializeForProcess(Svc::CreateProcessFlag as_type, bool
// Set other basic fields. // Set other basic fields.
m_enable_aslr = enable_aslr; m_enable_aslr = enable_aslr;
m_enable_device_address_space_merge = enable_das_merge; m_enable_device_address_space_merge = enable_das_merge;
m_allowed_exec_device_mapping = false;
m_address_space_start = start; m_address_space_start = start;
m_address_space_end = end; m_address_space_end = end;
m_is_kernel = false; m_is_kernel = false;
+5
View File
@@ -223,6 +223,7 @@ private:
bool m_is_kernel{}; bool m_is_kernel{};
bool m_enable_aslr{}; bool m_enable_aslr{};
bool m_enable_device_address_space_merge{}; bool m_enable_device_address_space_merge{};
bool m_allowed_exec_device_mapping{};
KMemoryBlockSlabManager* m_memory_block_slab_manager{}; KMemoryBlockSlabManager* m_memory_block_slab_manager{};
KBlockInfoManager* m_block_info_manager{}; KBlockInfoManager* m_block_info_manager{};
KResourceLimit* m_resource_limit{}; KResourceLimit* m_resource_limit{};
@@ -255,6 +256,10 @@ public:
return m_enable_aslr; return m_enable_aslr;
} }
void AllowDeviceMappingOfExecPages() {
m_allowed_exec_device_mapping = true;
}
bool Contains(KProcessAddress addr) const { bool Contains(KProcessAddress addr) const {
return m_address_space_start <= addr && addr <= m_address_space_end - 1; return m_address_space_start <= addr && addr <= m_address_space_end - 1;
} }
+13
View File
@@ -341,6 +341,11 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params, const KPa
// Initialize capabilities. // Initialize capabilities.
R_TRY(m_capabilities.InitializeForKip(caps, std::addressof(m_page_table))); R_TRY(m_capabilities.InitializeForKip(caps, std::addressof(m_page_table)));
// Enable mapping device pages as executable on legacy processes.
if (m_capabilities.GetIntendedKernelMajorVersion() < 26) {
m_page_table.AllowDeviceMappingOfExecPages();
}
// Initialize the process id. // Initialize the process id.
m_process_id = m_kernel.CreateNewUserProcessID(); m_process_id = m_kernel.CreateNewUserProcessID();
ASSERT(InitialProcessIdMin <= m_process_id); ASSERT(InitialProcessIdMin <= m_process_id);
@@ -437,6 +442,11 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params,
// Initialize capabilities. // Initialize capabilities.
R_TRY(m_capabilities.InitializeForUser(user_caps, std::addressof(m_page_table))); R_TRY(m_capabilities.InitializeForUser(user_caps, std::addressof(m_page_table)));
// Enable mapping device pages as executable on legacy processes.
if (m_capabilities.GetIntendedKernelMajorVersion() < 26) {
m_page_table.AllowDeviceMappingOfExecPages();
}
// Initialize the process id. // Initialize the process id.
m_process_id = m_kernel.CreateNewUserProcessID(); m_process_id = m_kernel.CreateNewUserProcessID();
ASSERT(ProcessIdMin <= m_process_id); ASSERT(ProcessIdMin <= m_process_id);
@@ -989,6 +999,9 @@ Result KProcess::Run(s32 priority, size_t stack_size) {
main_thread->GetContext().r[0] = 0; main_thread->GetContext().r[0] = 0;
main_thread->GetContext().r[1] = thread_handle; main_thread->GetContext().r[1] = thread_handle;
// Pass the thread handle to the thread local region.
this->GetMemory().Write32(GetInteger(main_thread->GetTlsAddress()) + 0x110, thread_handle);
// Update our state. // Update our state.
this->ChangeState((state == State::Created) ? State::Running : State::RunningAttached); this->ChangeState((state == State::Created) ? State::Running : State::RunningAttached);
ON_RESULT_FAILURE_2 { ON_RESULT_FAILURE_2 {
@@ -33,6 +33,10 @@ public:
m_page_table.Finalize(); m_page_table.Finalize();
} }
void AllowDeviceMappingOfExecPages() {
m_page_table.AllowDeviceMappingOfExecPages();
}
[[nodiscard]] Core::Memory::Memory& GetMemory() noexcept { return m_page_table.GetMemory(); } [[nodiscard]] Core::Memory::Memory& GetMemory() noexcept { return m_page_table.GetMemory(); }
[[nodiscard]] Core::Memory::Memory const& GetMemory() const noexcept { return m_page_table.GetMemory(); } [[nodiscard]] Core::Memory::Memory const& GetMemory() const noexcept { return m_page_table.GetMemory(); }
[[nodiscard]] Common::PageTable& GetImpl() noexcept { return m_page_table.GetImpl(); } [[nodiscard]] Common::PageTable& GetImpl() noexcept { return m_page_table.GetImpl(); }
+20 -18
View File
@@ -76,11 +76,13 @@ namespace Kernel {
std::atomic_uint16_t interrupt_flag; std::atomic_uint16_t interrupt_flag;
std::atomic_uint8_t cache_maintenance_flag; std::atomic_uint8_t cache_maintenance_flag;
std::atomic_int64_t thread_cpu_time; std::atomic_int64_t thread_cpu_time;
std::atomic_uint32_t current_thread_handle;
}; };
static_assert(offsetof(ThreadLocalRegion, disable_count) == 0x100); static_assert(offsetof(ThreadLocalRegion, disable_count) == 0x100);
static_assert(offsetof(ThreadLocalRegion, interrupt_flag) == 0x102); static_assert(offsetof(ThreadLocalRegion, interrupt_flag) == 0x102);
static_assert(offsetof(ThreadLocalRegion, cache_maintenance_flag) == 0x104); static_assert(offsetof(ThreadLocalRegion, cache_maintenance_flag) == 0x104);
static_assert(offsetof(ThreadLocalRegion, thread_cpu_time) == 0x108); static_assert(offsetof(ThreadLocalRegion, thread_cpu_time) == 0x108);
static_assert(offsetof(ThreadLocalRegion, current_thread_handle) == 0x110);
class ThreadQueueImplForKThreadSleep final : public KThreadQueueWithoutEndWait { class ThreadQueueImplForKThreadSleep final : public KThreadQueueWithoutEndWait {
public: public:
@@ -128,24 +130,24 @@ namespace Kernel {
// Next, assert things based on the type. // Next, assert things based on the type.
switch (type) { switch (type) {
case ThreadType::Main: case ThreadType::Main:
ASSERT(arg == 0); ASSERT(arg == 0);
[[fallthrough]]; [[fallthrough]];
case ThreadType::User: case ThreadType::User:
ASSERT(((owner == nullptr) || ASSERT(((owner == nullptr) ||
(owner->GetCoreMask() | (1ULL << virt_core)) == owner->GetCoreMask())); (owner->GetCoreMask() | (1ULL << virt_core)) == owner->GetCoreMask()));
ASSERT(((owner == nullptr) || (prio > Svc::LowestThreadPriority) || ASSERT(((owner == nullptr) || (prio > Svc::LowestThreadPriority) ||
(owner->GetPriorityMask() | (1ULL << prio)) == owner->GetPriorityMask())); (owner->GetPriorityMask() | (1ULL << prio)) == owner->GetPriorityMask()));
break; break;
case ThreadType::HighPriority: case ThreadType::HighPriority:
case ThreadType::Dummy: case ThreadType::Dummy:
break; break;
case ThreadType::Kernel: case ThreadType::Kernel:
UNIMPLEMENTED(); UNIMPLEMENTED();
break; break;
default: default:
ASSERT_MSG(false, "KThread::Initialize: Unknown ThreadType {}", static_cast<u32>(type)); ASSERT_MSG(false, "KThread::Initialize: Unknown ThreadType {}", static_cast<u32>(type));
break; break;
} }
m_thread_type = type; m_thread_type = type;
+7 -2
View File
@@ -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-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@@ -75,7 +75,12 @@ Result CreateThread(Core::System& system, Handle* out_handle, u64 entry_point, u
KThread::Register(kernel, thread); KThread::Register(kernel, thread);
// Add the thread to the handle table. // Add the thread to the handle table.
R_RETURN(process.GetHandleTable().Add(out_handle, thread)); R_TRY(process.GetHandleTable().Add(out_handle, thread));
// Pass the thread handle to the thread local region.
process.GetMemory().Write32(GetInteger(thread->GetTlsAddress()) + 0x110, *out_handle);
R_SUCCEED();
} }
/// Starts the thread for the provided handle /// Starts the thread for the provided handle
+3 -3
View File
@@ -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-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@@ -53,8 +53,8 @@ constexpr inline u32 RequiredKernelVersion =
// This is the highest SVC version supported, to be updated on new kernel releases. // This is the highest SVC version supported, to be updated on new kernel releases.
// NOTE: Official kernel versions have SVC major = SDK major + 4, SVC minor = SDK minor. // NOTE: Official kernel versions have SVC major = SDK major + 4, SVC minor = SDK minor.
constexpr inline u32 SupportedKernelMajorVersion = ConvertToSvcMajorVersion(20); constexpr inline u32 SupportedKernelMajorVersion = ConvertToSvcMajorVersion(22);
constexpr inline u32 SupportedKernelMinorVersion = ConvertToSvcMinorVersion(5); constexpr inline u32 SupportedKernelMinorVersion = ConvertToSvcMinorVersion(2);
constexpr inline u32 SupportedKernelVersion = constexpr inline u32 SupportedKernelVersion =
EncodeKernelVersion(SupportedKernelMajorVersion, SupportedKernelMinorVersion); EncodeKernelVersion(SupportedKernelMajorVersion, SupportedKernelMinorVersion);
+8
View File
@@ -1155,6 +1155,14 @@ void Module::Interface::StoreSaveDataThumbnailSystem(HLERequestContext& ctx) {
StoreSaveDataThumbnail(ctx, uuid, tid); StoreSaveDataThumbnail(ctx, uuid, tid);
} }
void Module::Interface::GetPinCodeLength(HLERequestContext& ctx) {
LOG_WARNING(Service_ACC, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push<u32>(0);
}
void Module::Interface::StoreSaveDataThumbnail(HLERequestContext& ctx, const Common::UUID& uuid, void Module::Interface::StoreSaveDataThumbnail(HLERequestContext& ctx, const Common::UUID& uuid,
const u64 tid) { const u64 tid) {
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
+2 -1
View File
@@ -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-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
@@ -48,6 +48,7 @@ public:
void StoreSaveDataThumbnailApplication(HLERequestContext& ctx); void StoreSaveDataThumbnailApplication(HLERequestContext& ctx);
void GetBaasAccountManagerForSystemService(HLERequestContext& ctx); void GetBaasAccountManagerForSystemService(HLERequestContext& ctx);
void StoreSaveDataThumbnailSystem(HLERequestContext& ctx); void StoreSaveDataThumbnailSystem(HLERequestContext& ctx);
void GetPinCodeLength(HLERequestContext& ctx);
private: private:
Result InitializeApplicationInfoBase(); Result InitializeApplicationInfoBase();
+2 -2
View File
@@ -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-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
@@ -60,7 +60,7 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module_, std::shared_ptr<ProfileManager>
{291, nullptr, "ProxyProcedureForFloatingRegistrationWithNintendoAccount"}, {291, nullptr, "ProxyProcedureForFloatingRegistrationWithNintendoAccount"},
{299, nullptr, "SuspendBackgroundDaemon"}, {299, nullptr, "SuspendBackgroundDaemon"},
{400, nullptr, "SetPinCode"}, // 18.0.0+ {400, nullptr, "SetPinCode"}, // 18.0.0+
{401, nullptr, "GetPinCodeLength"}, // 18.0.0+ {401, &ACC_SU::GetPinCodeLength, "GetPinCodeLength"}, // 18.0.0+
{402, nullptr, "GetPinCode"}, // 18.0.0+ {402, nullptr, "GetPinCode"}, // 18.0.0+
{410, nullptr, "GetPinCodeErrorCount"}, // 18.0.0+ {410, nullptr, "GetPinCodeErrorCount"}, // 18.0.0+
{411, nullptr, "ResetPinCodeErrorCount"}, // 18.0.0+ {411, nullptr, "ResetPinCodeErrorCount"}, // 18.0.0+
+4 -1
View File
@@ -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-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -40,7 +43,7 @@ ACC_U1::ACC_U1(std::shared_ptr<Module> module_, std::shared_ptr<ProfileManager>
{152, nullptr, "LoadSignedDeviceIdentifierCacheForNintendoAccount"}, {152, nullptr, "LoadSignedDeviceIdentifierCacheForNintendoAccount"},
{190, nullptr, "GetUserLastOpenedApplication"}, {190, nullptr, "GetUserLastOpenedApplication"},
{191, nullptr, "ActivateOpenContextHolder"}, {191, nullptr, "ActivateOpenContextHolder"},
{401, nullptr, "GetPinCodeLength"}, // 18.0.0+ {401, &ACC_U1::GetPinCodeLength, "GetPinCodeLength"}, // 18.0.0+
{402, nullptr, "GetPinCode"}, // 18.0.0+ {402, nullptr, "GetPinCode"}, // 18.0.0+
{997, nullptr, "DebugInvalidateTokenCacheForUser"}, {997, nullptr, "DebugInvalidateTokenCacheForUser"},
{998, nullptr, "DebugSetUserStateClose"}, {998, nullptr, "DebugSetUserStateClose"},
@@ -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-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
@@ -92,7 +92,7 @@ Result IApplicationCreator::CreateSystemApplication(
std::unique_ptr<Loader::AppLoader> loader; std::unique_ptr<Loader::AppLoader> loader;
auto process = auto process =
CreateProcess(system, application_id, 1, 21); CreateProcess(system, application_id, 1, 22);
R_UNLESS(process != nullptr, ResultUnknown); R_UNLESS(process != nullptr, ResultUnknown);
const auto applet = std::make_shared<Applet>(system, std::move(process), true); const auto applet = std::make_shared<Applet>(system, std::move(process), true);
@@ -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-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
@@ -75,7 +75,8 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_, std::shared_ptr<Ap
{502, nullptr, "IsSleepEnabled"}, {502, nullptr, "IsSleepEnabled"},
{503, nullptr, "IsDisablingSleepSuppressed"}, {503, nullptr, "IsDisablingSleepSuppressed"},
{600, nullptr, "Unknown600"}, //20.0.0+ {600, nullptr, "Unknown600"}, //20.0.0+
{610, nullptr, "Unknown610"}, //21.0.0+ {610, D<&ICommonStateGetter::Unknown610>, "Unknown610"}, //21.0.0+
{611, D<&ICommonStateGetter::Unknown611>, "Unknown611"}, //22.0.0+
{900, D<&ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled>, "SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled"}, //11.0.0+ {900, D<&ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled>, "SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled"}, //11.0.0+
{910, nullptr, "GetLaunchRequiredTick"}, //17.0.0+ {910, nullptr, "GetLaunchRequiredTick"}, //17.0.0+
{1000, nullptr, "BeginVrMode3d"}, //19.0.0+ {1000, nullptr, "BeginVrMode3d"}, //19.0.0+
@@ -362,4 +363,14 @@ Result ICommonStateGetter::SetHandlingHomeButtonShortPressedEnabled(bool enabled
R_SUCCEED(); R_SUCCEED();
} }
Result ICommonStateGetter::Unknown610() {
LOG_WARNING(Service_AM, "(STUBBED) called");
R_SUCCEED();
}
Result ICommonStateGetter::Unknown611() {
LOG_WARNING(Service_AM, "(STUBBED) called");
R_SUCCEED();
}
} // namespace Service::AM } // namespace Service::AM
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
@@ -63,6 +63,8 @@ private:
Result SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled(); Result SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled();
Result PushToGeneralChannel(SharedPointer<IStorage> storage); // cmd 20 Result PushToGeneralChannel(SharedPointer<IStorage> storage); // cmd 20
Result SetHandlingHomeButtonShortPressedEnabled(bool enabled); Result SetHandlingHomeButtonShortPressedEnabled(bool enabled);
Result Unknown610();
Result Unknown611();
void SetCpuBoostMode(HLERequestContext& ctx); void SetCpuBoostMode(HLERequestContext& ctx);
@@ -78,6 +78,7 @@ ILibraryAppletAccessor::ILibraryAppletAccessor(Core::System& system_,
{120, nullptr, "GetLibraryAppletInfo"}, {120, nullptr, "GetLibraryAppletInfo"},
{150, nullptr, "RequestForAppletToGetForeground"}, {150, nullptr, "RequestForAppletToGetForeground"},
{160, D<&ILibraryAppletAccessor::GetIndirectLayerConsumerHandle>, "GetIndirectLayerConsumerHandle"}, //2.0.0+ {160, D<&ILibraryAppletAccessor::GetIndirectLayerConsumerHandle>, "GetIndirectLayerConsumerHandle"}, //2.0.0+
{170, D<&ILibraryAppletAccessor::Unknown170>, "Unknown170"}, //22.0.0+
}; };
// clang-format on // clang-format on
@@ -217,6 +218,12 @@ Result ILibraryAppletAccessor::GetIndirectLayerConsumerHandle(Out<u64> out_handl
R_SUCCEED(); R_SUCCEED();
} }
Result ILibraryAppletAccessor::Unknown170(OutCopyHandle<Kernel::KReadableEvent> out_event) {
LOG_WARNING(Service_AM, "(STUBBED) called");
*out_event = m_applet->unknown_event.GetHandle();
R_SUCCEED();
}
void ILibraryAppletAccessor::FrontendExecute() { void ILibraryAppletAccessor::FrontendExecute() {
if (m_applet->frontend) { if (m_applet->frontend) {
m_applet->frontend->Initialize(); m_applet->frontend->Initialize();
@@ -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-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
@@ -37,6 +37,7 @@ private:
Result GetPopOutDataEvent(OutCopyHandle<Kernel::KReadableEvent> out_event); Result GetPopOutDataEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result GetPopInteractiveOutDataEvent(OutCopyHandle<Kernel::KReadableEvent> out_event); Result GetPopInteractiveOutDataEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result GetIndirectLayerConsumerHandle(Out<u64> out_handle); Result GetIndirectLayerConsumerHandle(Out<u64> out_handle);
Result Unknown170(OutCopyHandle<Kernel::KReadableEvent> out_event);
void FrontendExecute(); void FrontendExecute();
void FrontendExecuteInteractive(); void FrontendExecuteInteractive();
@@ -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-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
@@ -118,9 +118,10 @@ std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet(Core::System& system,
Firmware1900 = 19, Firmware1900 = 19,
Firmware2000 = 20, Firmware2000 = 20,
Firmware2100 = 21, Firmware2100 = 21,
Firmware2200 = 22,
}; };
auto process = CreateProcess(system, program_id, Firmware1400, Firmware2100); auto process = CreateProcess(system, program_id, Firmware1400, Firmware2200);
if (!process) { if (!process) {
// Couldn't initialize the guest process // Couldn't initialize the guest process
return {}; return {};
+40 -19
View File
@@ -53,38 +53,53 @@ public:
{20102, nullptr, "GetFriendDetailedInfo"}, {20102, nullptr, "GetFriendDetailedInfo"},
{20103, nullptr, "SyncFriendList"}, {20103, nullptr, "SyncFriendList"},
{20104, &IFriendService::RequestSyncFriendList, "RequestSyncFriendList"}, {20104, &IFriendService::RequestSyncFriendList, "RequestSyncFriendList"},
{20105, &IFriendService::GetFriendListForViewer, "GetFriendListForViewer"}, {20105, &IFriendService::GetFriendListForViewer, "GetFriendListForViewerV1"},
{20110, nullptr, "LoadFriendSetting"}, {20106, nullptr, "UpdateFriendInfoForViewerV1"},
{20107, nullptr, "GetFriendDetailedInfoV2"}, // 20.0.0+
{20108, &IFriendService::GetFriendListForViewer, "GetFriendListForViewerV2"}, // 22.0.0+
{20109, nullptr, "UpdateFriendInfoForViewerV2"}, // 22.0.0+
{20110, nullptr, "LoadFriendSettingV1"},
{20111, nullptr, "LoadFriendSettingV2"}, // 22.0.0+
{20200, &IFriendService::GetReceivedFriendRequestCount, "GetReceivedFriendRequestCount"}, {20200, &IFriendService::GetReceivedFriendRequestCount, "GetReceivedFriendRequestCount"},
{20201, nullptr, "GetFriendRequestList"}, {20201, nullptr, "GetFriendRequestListV1"},
{20202, nullptr, "GetFriendRequestListV2"}, // 20.0.0+
{20203, nullptr, "GetFriendRequestReceivedNotificationCount"}, // 22.0.0+
{20300, nullptr, "GetFriendCandidateList"}, {20300, nullptr, "GetFriendCandidateList"},
{20301, nullptr, "GetNintendoNetworkIdInfo"}, {20301, nullptr, "GetNintendoNetworkIdInfo"},
{20302, nullptr, "GetSnsAccountLinkage"}, {20302, nullptr, "GetSnsAccountLinkage"}, // 5.0.0-19.0.1
{20303, nullptr, "GetSnsAccountProfile"}, {20303, nullptr, "GetSnsAccountProfile"}, // 5.0.0-19.0.1
{20304, nullptr, "GetSnsAccountFriendList"}, {20304, nullptr, "GetSnsAccountFriendList"}, // 5.0.0-19.0.1
{20400, nullptr, "GetBlockedUserList"}, {20400, nullptr, "GetBlockedUserListV1"},
{20401, nullptr, "SyncBlockedUserList"}, {20401, nullptr, "SyncBlockedUserList"},
{20500, nullptr, "GetProfileExtraList"}, {20402, nullptr, "GetBlockedUserListV2"}, // 20.0.0+
{20500, nullptr, "GetProfileExtraListV1"},
{20501, nullptr, "GetRelationship"}, {20501, nullptr, "GetRelationship"},
{20502, nullptr, "GetProfileExtraListV2"}, // 19.0.0+
{20600, &IFriendService::GetUserPresenceView, "GetUserPresenceViewV1"}, {20600, &IFriendService::GetUserPresenceView, "GetUserPresenceViewV1"},
{20601, &IFriendService::GetUserPresenceView, "GetUserPresenceViewV2"}, {20601, &IFriendService::GetUserPresenceView, "GetUserPresenceViewV2"}, // 19.0.0+
{20700, nullptr, "GetPlayHistoryList"}, {20700, nullptr, "GetPlayHistoryListV1"},
{20701, &IFriendService::GetPlayHistoryStatistics, "GetPlayHistoryStatistics"}, {20701, &IFriendService::GetPlayHistoryStatistics, "GetPlayHistoryStatistics"},
{20702, nullptr, "GetPlayHistoryListV2"}, // 19.0.0+
{20800, &IFriendService::LoadUserSetting, "LoadUserSettingV1"}, {20800, &IFriendService::LoadUserSetting, "LoadUserSettingV1"},
{20801, nullptr, "SyncUserSetting"}, {20801, nullptr, "SyncUserSetting"},
{20802, &IFriendService::LoadUserSetting, "LoadUserSettingV2"}, {20802, &IFriendService::LoadUserSetting, "LoadUserSettingV2"}, // 19.0.0+
{20900, &IFriendService::RequestListSummaryOverlayNotification, "RequestListSummaryOverlayNotification"}, {20900, &IFriendService::RequestListSummaryOverlayNotification, "RequestListSummaryOverlayNotification"},
{21000, nullptr, "GetExternalApplicationCatalog"}, {21000, nullptr, "GetExternalApplicationCatalog"},
{22000, nullptr, "GetReceivedFriendInvitationList"}, {22000, nullptr, "GetReceivedFriendInvitationListV1"},
{22001, nullptr, "GetReceivedFriendInvitationDetailedInfo"}, {22001, nullptr, "GetReceivedFriendInvitationDetailedInfoV1"},
{22002, nullptr, "GetReceivedFriendInvitationListV2"}, // 19.0.0+
{22003, nullptr, "GetReceivedFriendInvitationDetailedInfoV2"}, // 19.0.0+
{22010, &IFriendService::GetReceivedFriendInvitationCountCache, "GetReceivedFriendInvitationCountCache"}, {22010, &IFriendService::GetReceivedFriendInvitationCountCache, "GetReceivedFriendInvitationCountCache"},
{30100, nullptr, "DropFriendNewlyFlags"}, {30100, nullptr, "DropFriendNewlyFlags"},
{30101, nullptr, "DeleteFriend"}, {30101, nullptr, "DeleteFriend"},
{30110, nullptr, "DropFriendNewlyFlag"}, {30110, nullptr, "DropFriendNewlyFlag"},
{30120, nullptr, "ChangeFriendFavoriteFlag"}, {30120, nullptr, "ChangeFriendFavoriteFlag"},
{30121, nullptr, "ChangeFriendOnlineNotificationFlag"}, {30121, nullptr, "ChangeFriendOnlineNotificationFlag"},
{30130, nullptr, "SetFriendNote"}, // 22.0.0+
{30131, nullptr, "RequestUploadPendingNote"}, // 22.0.0+
{30190, nullptr, "RequestSyncLocalUpdates"}, // 22.0.0+
{30200, nullptr, "SendFriendRequest"}, {30200, nullptr, "SendFriendRequest"},
{30201, nullptr, "SendFriendRequestWithApplicationInfo"}, {30201, nullptr, "SendFriendRequestWithApplicationInfoV1"},
{30202, nullptr, "CancelFriendRequest"}, {30202, nullptr, "CancelFriendRequest"},
{30203, nullptr, "AcceptFriendRequest"}, {30203, nullptr, "AcceptFriendRequest"},
{30204, nullptr, "RejectFriendRequest"}, {30204, nullptr, "RejectFriendRequest"},
@@ -97,21 +112,27 @@ public:
{30215, nullptr, "SendFriendRequestWithExternalApplicationCatalogId"}, {30215, nullptr, "SendFriendRequestWithExternalApplicationCatalogId"},
{30216, nullptr, "ResendFacedFriendRequest"}, {30216, nullptr, "ResendFacedFriendRequest"},
{30217, nullptr, "SendFriendRequestWithNintendoNetworkIdInfo"}, {30217, nullptr, "SendFriendRequestWithNintendoNetworkIdInfo"},
{30300, nullptr, "GetSnsAccountLinkPageUrl"}, {30218, nullptr, "SendFriendRequestWithApplicationInfoV2"}, // 20.0.0+
{30301, nullptr, "UnlinkSnsAccount"}, {30300, nullptr, "GetSnsAccountLinkPageUrl"}, // 5.0.0-19.0.1
{30301, nullptr, "UnlinkSnsAccount"}, // 5.0.0-19.0.1
{30400, nullptr, "BlockUser"}, {30400, nullptr, "BlockUser"},
{30401, nullptr, "BlockUserWithApplicationInfo"}, {30401, nullptr, "BlockUserWithApplicationInfoV1"},
{30402, nullptr, "UnblockUser"}, {30402, nullptr, "UnblockUser"},
{30500, nullptr, "GetProfileExtraFromFriendCode"}, {30403, nullptr, "BlockUserWithApplicationInfoV2"}, // 20.0.0+
{30500, nullptr, "GetProfileExtraFromFriendCodeV1"},
{30501, nullptr, "GetProfileExtraFromFriendCodeV2"}, // 19.0.0+
{30700, nullptr, "DeletePlayHistory"}, {30700, nullptr, "DeletePlayHistory"},
{30701, nullptr, "AddPlayHistoryWithApplication"}, // 19.0.0+
{30810, nullptr, "ChangePresencePermission"}, {30810, nullptr, "ChangePresencePermission"},
{30811, nullptr, "ChangeFriendRequestReception"}, {30811, nullptr, "ChangeFriendRequestReception"},
{30812, nullptr, "ChangePlayLogPermission"}, {30812, nullptr, "ChangePlayLogPermission"},
{30820, nullptr, "IssueFriendCode"}, {30820, nullptr, "IssueFriendCode"},
{30830, nullptr, "ClearPlayLog"}, {30830, nullptr, "ClearPlayLog"},
{30900, nullptr, "SendFriendInvitation"}, {30900, nullptr, "SendFriendInvitationV1"},
{30901, nullptr, "SendFriendInvitationV2"}, // 19.0.0+
{30910, nullptr, "ReadFriendInvitation"}, {30910, nullptr, "ReadFriendInvitation"},
{30911, nullptr, "ReadAllFriendInvitations"}, {30911, nullptr, "ReadAllFriendInvitations"},
{31000, nullptr, "OpenUser"}, // 19.0.0+
{40100, nullptr, "DeleteFriendListCache"}, {40100, nullptr, "DeleteFriendListCache"},
{40400, nullptr, "DeleteBlockedUserListCache"}, {40400, nullptr, "DeleteBlockedUserListCache"},
{49900, nullptr, "DeleteNetworkServiceAccountCache"}, {49900, nullptr, "DeleteNetworkServiceAccountCache"},
@@ -199,25 +199,28 @@ NvResult nvhost_as_gpu::AllocateSpace(IoctlAllocSpace& params) {
return NvResult::Success; return NvResult::Success;
} }
void nvhost_as_gpu::FreeMappingLocked(u64 offset) { bool nvhost_as_gpu::FreeMappingLocked(u64 offset) noexcept {
auto const it = mapping_map.find(offset); if (auto const it = mapping_map.find(offset); it != mapping_map.end()) {
auto const mapping = it->second; auto const mapping = it->second;
if (!mapping.fixed) { if (!mapping.fixed) {
auto& allocator{mapping.big_page ? *vm.big_page_allocator : *vm.small_page_allocator}; auto& allocator{mapping.big_page ? *vm.big_page_allocator : *vm.small_page_allocator};
u32 page_size_bits{mapping.big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS}; u32 page_size_bits{mapping.big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS};
u32 page_size{mapping.big_page ? vm.big_page_size : VM::YUZU_PAGESIZE}; u32 page_size{mapping.big_page ? vm.big_page_size : VM::YUZU_PAGESIZE};
u64 aligned_size{Common::AlignUp(mapping.size, page_size)}; u64 aligned_size{Common::AlignUp(mapping.size, page_size)};
allocator.Free(u32(mapping.offset >> page_size_bits), u32(aligned_size >> page_size_bits)); allocator.Free(u32(mapping.offset >> page_size_bits), u32(aligned_size >> page_size_bits));
}
nvmap.UnpinHandle(mapping.handle);
// Sparse mappings shouldn't be fully unmapped, just returned to their sparse state
// Only FreeSpace can unmap them fully
if (mapping.sparse_alloc) {
gmmu->MapSparse(offset, mapping.size, mapping.big_page);
} else {
gmmu->Unmap(offset, mapping.size);
}
mapping_map.erase(offset);
return true;
} }
nvmap.UnpinHandle(mapping.handle); return false;
// Sparse mappings shouldn't be fully unmapped, just returned to their sparse state
// Only FreeSpace can unmap them fully
if (mapping.sparse_alloc) {
gmmu->MapSparse(offset, mapping.size, mapping.big_page);
} else {
gmmu->Unmap(offset, mapping.size);
}
mapping_map.erase(it);
} }
NvResult nvhost_as_gpu::FreeSpace(IoctlFreeSpace& params) { NvResult nvhost_as_gpu::FreeSpace(IoctlFreeSpace& params) {
@@ -232,7 +235,8 @@ NvResult nvhost_as_gpu::FreeSpace(IoctlFreeSpace& params) {
return NvResult::BadValue; return NvResult::BadValue;
for (const auto mapping_offset : allocation.mappings) for (const auto mapping_offset : allocation.mappings)
FreeMappingLocked(mapping_offset); if (!FreeMappingLocked(mapping_offset))
return NvResult::BadValue;
// Unset sparse flag if required // Unset sparse flag if required
if (allocation.sparse) if (allocation.sparse)
@@ -402,8 +406,8 @@ NvResult nvhost_as_gpu::UnmapBuffer(IoctlUnmapBuffer& params) {
} }
nvmap.UnpinHandle(mapping.handle); nvmap.UnpinHandle(mapping.handle);
mapping_map.erase(it); mapping_map.erase(params.offset);
map_buffer_offsets.erase(offset_it); map_buffer_offsets.erase(params.offset);
} }
return NvResult::Success; return NvResult::Success;
} }
@@ -157,7 +157,7 @@ private:
NvResult GetVARegions1(IoctlGetVaRegions& params); NvResult GetVARegions1(IoctlGetVaRegions& params);
NvResult GetVARegions3(IoctlGetVaRegions& params, std::span<VaRegion> regions); NvResult GetVARegions3(IoctlGetVaRegions& params, std::span<VaRegion> regions);
void FreeMappingLocked(u64 offset); [[nodiscard]] bool FreeMappingLocked(u64 offset) noexcept;
Module& module; Module& module;
@@ -409,6 +409,13 @@ struct AccountNotificationSettings {
static_assert(sizeof(AccountNotificationSettings) == 0x18, static_assert(sizeof(AccountNotificationSettings) == 0x18,
"AccountNotificationSettings is an invalid size"); "AccountNotificationSettings is an invalid size");
/// This is nn::settings::system::AccountUserSettings (stubbed)
struct AccountUserSettings {
std::array<u8, 0x40> data;
};
static_assert(sizeof(AccountUserSettings) == 0x40,
"AccountUserSettings is an invalid size");
/// This is nn::settings::factory::BatteryLot /// This is nn::settings::factory::BatteryLot
struct BatteryLot { struct BatteryLot {
std::array<char, 0x18> lot_number; std::array<char, 0x18> lot_number;
@@ -340,9 +340,9 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_)
{306, nullptr, "GetPinCodeReregistrationGuideAccounts"}, //20.0.0+ {306, nullptr, "GetPinCodeReregistrationGuideAccounts"}, //20.0.0+
{307, nullptr, "SetPinCodeReregistrationGuideAccounts"}, //20.0.0+ {307, nullptr, "SetPinCodeReregistrationGuideAccounts"}, //20.0.0+
{315, C<&ISystemSettingsServer::GetHttpAuthConfigs>, "GetHttpAuthConfigs"}, //21.0.0+ {315, C<&ISystemSettingsServer::GetHttpAuthConfigs>, "GetHttpAuthConfigs"}, //21.0.0+
{319, nullptr, "GetAccountUserSettings"}, //21.0.0+ {319, C<&ISystemSettingsServer::GetAccountUserSettings>, "GetAccountUserSettings"}, //21.0.0+
{320, nullptr, "SetAccountUserSettings"}, //21.0.0+ {320, nullptr, "SetAccountUserSettings"}, //21.0.0+
{321, nullptr, "GetDefaultAccountUserSettings"}, //21.0.0+ {321, C<&ISystemSettingsServer::GetDefaultAccountUserSettings>, "GetDefaultAccountUserSettings"}, //21.0.0+
}; };
// clang-format on // clang-format on
@@ -1428,6 +1428,23 @@ Result ISystemSettingsServer::GetHttpAuthConfigs(Out<s32> out_count, OutBuffer<B
R_SUCCEED(); R_SUCCEED();
} }
Result ISystemSettingsServer::GetAccountUserSettings(
Out<u32> out_count,
OutLargeData<AccountUserSettings, BufferAttr_HipcMapAlias> out_settings) {
LOG_WARNING(Service_SET, "(STUBBED) called");
*out_count = 0;
*out_settings = {};
R_SUCCEED();
}
Result ISystemSettingsServer::GetDefaultAccountUserSettings(Out<AccountUserSettings> out_settings) {
LOG_WARNING(Service_SET, "(STUBBED) called");
*out_settings = {};
R_SUCCEED();
}
void ISystemSettingsServer::SetupSettings() { void ISystemSettingsServer::SetupSettings() {
auto system_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) / "system/save/8000000000000050"; auto system_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) / "system/save/8000000000000050";
if (!LoadSettingsFile(system_dir, []() { return DefaultSystemSettings(); })) { if (!LoadSettingsFile(system_dir, []() { return DefaultSystemSettings(); })) {
@@ -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-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
@@ -161,6 +161,10 @@ public:
Result GetPanelCrcMode(Out<s32> out_panel_crc_mode); Result GetPanelCrcMode(Out<s32> out_panel_crc_mode);
Result SetPanelCrcMode(s32 panel_crc_mode); Result SetPanelCrcMode(s32 panel_crc_mode);
Result GetHttpAuthConfigs(Out<s32> out_count, OutBuffer<BufferAttr_HipcMapAlias> out_configs); Result GetHttpAuthConfigs(Out<s32> out_count, OutBuffer<BufferAttr_HipcMapAlias> out_configs);
Result GetAccountUserSettings(
Out<u32> out_count,
OutLargeData<AccountUserSettings, BufferAttr_HipcMapAlias> out_settings);
Result GetDefaultAccountUserSettings(Out<AccountUserSettings> out_settings);
private: private:
bool LoadSettingsFile(std::filesystem::path& path, auto&& default_func); bool LoadSettingsFile(std::filesystem::path& path, auto&& default_func);
+11 -9
View File
@@ -67,22 +67,24 @@ public:
Result GetServicePort(Kernel::KClientPort** out_client_port, const std::string& name); Result GetServicePort(Kernel::KClientPort** out_client_port, const std::string& name);
template <std::derived_from<SessionRequestHandler> T> template <std::derived_from<SessionRequestHandler> T>
std::shared_ptr<T> GetService(const std::string& service_name, bool block = false) const { std::shared_ptr<T> GetService(const std::string& name, bool block = false) const {
auto service = registered_services.find(service_name); std::unique_lock l{lock};
if (service == registered_services.end() && !block) { auto it = registered_services.find(name);
LOG_DEBUG(Service, "Can't find service: {}", service_name); if (it == registered_services.end() && !block) {
LOG_DEBUG(Service, "Can't find service: {}", name);
return nullptr; return nullptr;
} else if (block) { } else if (block) {
using namespace std::literals::chrono_literals; using namespace std::literals::chrono_literals;
while (service == registered_services.end()) { while (it == registered_services.end()) {
l.unlock();
Kernel::Svc::SleepThread( Kernel::Svc::SleepThread(
kernel.System(), kernel.System(),
std::chrono::duration_cast<std::chrono::nanoseconds>(100ms).count()); std::chrono::duration_cast<std::chrono::nanoseconds>(100ms).count());
service = registered_services.find(service_name); l.lock();
it = registered_services.find(name);
} }
} }
return std::static_pointer_cast<T>(it->second());
return std::static_pointer_cast<T>(service->second());
} }
void InvokeControlRequest(HLERequestContext& context); void InvokeControlRequest(HLERequestContext& context);
@@ -96,7 +98,7 @@ private:
std::unique_ptr<Controller> controller_interface; std::unique_ptr<Controller> controller_interface;
/// Map of registered services, retrieved using GetServicePort. /// Map of registered services, retrieved using GetServicePort.
std::mutex lock; mutable std::mutex lock;
ankerl::unordered_dense::map<std::string, SessionRequestHandlerFactory> registered_services; ankerl::unordered_dense::map<std::string, SessionRequestHandlerFactory> registered_services;
ankerl::unordered_dense::map<std::string, Kernel::KClientPort*> service_ports; ankerl::unordered_dense::map<std::string, Kernel::KClientPort*> service_ports;
@@ -14,6 +14,38 @@
namespace Shader::Backend::SPIRV { namespace Shader::Backend::SPIRV {
namespace { namespace {
class DescriptorIndex {
public:
explicit DescriptorIndex(EmitContext& ctx, const IR::Value& index)
: id{index.IsImmediate() ? ctx.Const(index.U32()) : ctx.Def(index)},
is_non_uniform{ctx.profile.support_sampled_image_array_nonuniform_indexing &&
!index.IsImmediate()} {
if (!is_non_uniform) {
return;
}
if (ctx.profile.supported_spirv < 0x00010400) {
ctx.AddExtension("SPV_EXT_descriptor_indexing");
}
ctx.AddCapability(spv::Capability::ShaderNonUniform);
ctx.AddCapability(spv::Capability::SampledImageArrayNonUniformIndexing);
Decorate(ctx, id);
}
Id Value() const {
return id;
}
void Decorate(EmitContext& ctx, Id object) const {
if (is_non_uniform) {
ctx.Decorate(object, spv::Decoration::NonUniform);
}
}
private:
Id id;
bool is_non_uniform;
};
class ImageOperands { class ImageOperands {
public: public:
[[maybe_unused]] static constexpr bool ImageSampleOffsetAllowed = false; [[maybe_unused]] static constexpr bool ImageSampleOffsetAllowed = false;
@@ -189,8 +221,12 @@ private:
Id Texture(EmitContext& ctx, IR::TextureInstInfo info, [[maybe_unused]] const IR::Value& index) { Id Texture(EmitContext& ctx, IR::TextureInstInfo info, [[maybe_unused]] const IR::Value& index) {
const TextureDefinition& def{ctx.textures.at(info.descriptor_index)}; const TextureDefinition& def{ctx.textures.at(info.descriptor_index)};
if (def.count > 1) { if (def.count > 1) {
const Id pointer{ctx.OpAccessChain(def.pointer_type, def.id, ctx.Def(index))}; const DescriptorIndex idx{ctx, index};
return ctx.OpLoad(def.sampled_type, pointer); const Id pointer{ctx.OpAccessChain(def.pointer_type, def.id, idx.Value())};
idx.Decorate(ctx, pointer);
const Id object{ctx.OpLoad(def.sampled_type, pointer)};
idx.Decorate(ctx, object);
return object;
} else { } else {
return ctx.OpLoad(def.sampled_type, def.id); return ctx.OpLoad(def.sampled_type, def.id);
} }
@@ -208,9 +244,14 @@ Id TextureImage(EmitContext& ctx, IR::TextureInstInfo info, const IR::Value& ind
} else { } else {
const TextureDefinition& def{ctx.textures.at(info.descriptor_index)}; const TextureDefinition& def{ctx.textures.at(info.descriptor_index)};
if (def.count > 1) { if (def.count > 1) {
const Id idx{index.IsImmediate() ? ctx.Const(index.U32()) : ctx.Def(index)}; const DescriptorIndex idx{ctx, index};
const Id ptr{ctx.OpAccessChain(def.pointer_type, def.id, idx)}; const Id ptr{ctx.OpAccessChain(def.pointer_type, def.id, idx.Value())};
return ctx.OpImage(def.image_type, ctx.OpLoad(def.sampled_type, ptr)); idx.Decorate(ctx, ptr);
const Id object{ctx.OpLoad(def.sampled_type, ptr)};
idx.Decorate(ctx, object);
const Id image{ctx.OpImage(def.image_type, object)};
idx.Decorate(ctx, image);
return image;
} }
return ctx.OpImage(def.image_type, ctx.OpLoad(def.sampled_type, def.id)); return ctx.OpImage(def.image_type, ctx.OpLoad(def.sampled_type, def.id));
} }
@@ -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-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -17,6 +20,9 @@ struct HostTranslateInfo {
bool support_snorm_render_buffer{}; ///< True when the device supports SNORM render buffers 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 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 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 bool support_geometry_shader_passthrough{}; ///< True when the device supports geometry
///< passthrough shaders ///< passthrough shaders
bool support_conditional_barrier{}; ///< True when the device supports barriers in conditional bool support_conditional_barrier{}; ///< True when the device supports barriers in conditional
+117 -12
View File
@@ -32,6 +32,71 @@ using TextureInstVector = boost::container::small_vector<TextureInst, 24>;
constexpr u32 DESCRIPTOR_SIZE = 8; constexpr u32 DESCRIPTOR_SIZE = 8;
constexpr u32 DESCRIPTOR_SIZE_SHIFT = static_cast<u32>(std::countr_zero(DESCRIPTOR_SIZE)); 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;
u32 DynamicDescriptorSizeShift(const IR::U32& dynamic_offset) {
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()) {
return 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) {
return 1;
}
const u32 stride{1U << size_shift};
const u32 available{DYNAMIC_DESCRIPTOR_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);
}
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;
}
u32 DynamicSampledTextureCap(const Info& info, const HostTranslateInfo& host_info,
u32 dynamic_arrays) {
if (dynamic_arrays == 0) {
return MAX_DYNAMIC_DESCRIPTOR_COUNT;
}
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}));
}
IR::Opcode IndexedInstruction(const IR::Inst& inst) { IR::Opcode IndexedInstruction(const IR::Inst& inst) {
switch (inst.GetOpcode()) { switch (inst.GetOpcode()) {
@@ -109,6 +174,39 @@ IR::Opcode IndexedInstruction(const IR::Inst& inst) {
} }
} }
bool IsStorageImageOpcode(IR::Opcode opcode) {
switch (opcode) {
case IR::Opcode::ImageRead:
case IR::Opcode::ImageAtomicIAdd32:
case IR::Opcode::ImageAtomicSMin32:
case IR::Opcode::ImageAtomicUMin32:
case IR::Opcode::ImageAtomicSMax32:
case IR::Opcode::ImageAtomicUMax32:
case IR::Opcode::ImageAtomicInc32:
case IR::Opcode::ImageAtomicDec32:
case IR::Opcode::ImageAtomicAnd32:
case IR::Opcode::ImageAtomicOr32:
case IR::Opcode::ImageAtomicXor32:
case IR::Opcode::ImageAtomicExchange32:
case IR::Opcode::ImageWrite:
return true;
default:
return false;
}
}
u32 DynamicSampledTextureArrayCount(const TextureInstVector& to_replace) {
u32 count{};
for (const TextureInst& inst : to_replace) {
const auto flags{inst.inst->Flags<IR::TextureInstInfo>()};
if (inst.cbuf.count > 1 && !IsStorageImageOpcode(IndexedInstruction(*inst.inst)) &&
flags.type != TextureType::Buffer) {
++count;
}
}
return count;
}
bool IsBindless(const IR::Inst& inst) { bool IsBindless(const IR::Inst& inst) {
switch (inst.GetOpcode()) { switch (inst.GetOpcode()) {
case IR::Opcode::BindlessImageSampleImplicitLod: case IR::Opcode::BindlessImageSampleImplicitLod:
@@ -354,6 +452,7 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environme
} else { } else {
return std::nullopt; return std::nullopt;
} }
const u32 size_shift{DynamicDescriptorSizeShift(dynamic_offset)};
return ConstBufferAddr{ return ConstBufferAddr{
.index = index.U32(), .index = index.U32(),
.offset = base_offset, .offset = base_offset,
@@ -362,7 +461,7 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environme
.secondary_offset = 0, .secondary_offset = 0,
.secondary_shift_left = 0, .secondary_shift_left = 0,
.dynamic_offset = dynamic_offset, .dynamic_offset = dynamic_offset,
.count = 8, .count = DynamicDescriptorCount(base_offset, size_shift),
.has_secondary = false, .has_secondary = false,
}; };
} }
@@ -589,6 +688,8 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
program.info.texture_descriptors, program.info.texture_descriptors,
program.info.image_descriptors, program.info.image_descriptors,
}; };
const u32 sampled_dynamic_cap{
DynamicSampledTextureCap(program.info, host_info, DynamicSampledTextureArrayCount(to_replace))};
for (TextureInst& texture_inst : to_replace) { for (TextureInst& texture_inst : to_replace) {
// TODO: Handle arrays // TODO: Handle arrays
IR::Inst* const inst{texture_inst.inst}; IR::Inst* const inst{texture_inst.inst};
@@ -632,6 +733,9 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
break; break;
} }
u32 index; u32 index;
const u32 size_shift{cbuf.count > 1 ? DynamicDescriptorSizeShift(cbuf.dynamic_offset)
: DESCRIPTOR_SIZE_SHIFT};
u32 count{cbuf.count};
switch (inst->GetOpcode()) { switch (inst->GetOpcode()) {
case IR::Opcode::ImageRead: case IR::Opcode::ImageRead:
case IR::Opcode::ImageAtomicIAdd32: case IR::Opcode::ImageAtomicIAdd32:
@@ -660,8 +764,8 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
.is_integer = is_integer, .is_integer = is_integer,
.cbuf_index = cbuf.index, .cbuf_index = cbuf.index,
.cbuf_offset = cbuf.offset, .cbuf_offset = cbuf.offset,
.count = cbuf.count, .count = count,
.size_shift = DESCRIPTOR_SIZE_SHIFT, .size_shift = size_shift,
}); });
} else { } else {
index = descriptors.Add(ImageDescriptor{ index = descriptors.Add(ImageDescriptor{
@@ -672,8 +776,8 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
.is_integer = is_integer, .is_integer = is_integer,
.cbuf_index = cbuf.index, .cbuf_index = cbuf.index,
.cbuf_offset = cbuf.offset, .cbuf_offset = cbuf.offset,
.count = cbuf.count, .count = count,
.size_shift = DESCRIPTOR_SIZE_SHIFT, .size_shift = size_shift,
}); });
} }
break; break;
@@ -688,10 +792,11 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
.secondary_cbuf_index = cbuf.secondary_index, .secondary_cbuf_index = cbuf.secondary_index,
.secondary_cbuf_offset = cbuf.secondary_offset, .secondary_cbuf_offset = cbuf.secondary_offset,
.secondary_shift_left = cbuf.secondary_shift_left, .secondary_shift_left = cbuf.secondary_shift_left,
.count = cbuf.count, .count = count,
.size_shift = DESCRIPTOR_SIZE_SHIFT, .size_shift = size_shift,
}); });
} else { } else {
count = std::min(count, sampled_dynamic_cap);
index = descriptors.Add(TextureDescriptor{ index = descriptors.Add(TextureDescriptor{
.type = flags.type, .type = flags.type,
.is_depth = flags.is_depth != 0, .is_depth = flags.is_depth != 0,
@@ -703,8 +808,8 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
.secondary_cbuf_index = cbuf.secondary_index, .secondary_cbuf_index = cbuf.secondary_index,
.secondary_cbuf_offset = cbuf.secondary_offset, .secondary_cbuf_offset = cbuf.secondary_offset,
.secondary_shift_left = cbuf.secondary_shift_left, .secondary_shift_left = cbuf.secondary_shift_left,
.count = cbuf.count, .count = count,
.size_shift = DESCRIPTOR_SIZE_SHIFT, .size_shift = size_shift,
}); });
} }
break; break;
@@ -712,12 +817,12 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
flags.descriptor_index.Assign(index); flags.descriptor_index.Assign(index);
inst->SetFlags(flags); inst->SetFlags(flags);
if (cbuf.count > 1) { if (count > 1) {
const auto insert_point{IR::Block::InstructionList::s_iterator_to(*inst)}; const auto insert_point{IR::Block::InstructionList::s_iterator_to(*inst)};
IR::IREmitter ir{*texture_inst.block, insert_point}; IR::IREmitter ir{*texture_inst.block, insert_point};
const IR::U32 shift{ir.Imm32(DESCRIPTOR_SIZE_SHIFT)}; const IR::U32 shift{ir.Imm32(size_shift)};
inst->SetArg(0, ir.UMin(ir.ShiftRightLogical(cbuf.dynamic_offset, shift), inst->SetArg(0, ir.UMin(ir.ShiftRightLogical(cbuf.dynamic_offset, shift),
ir.Imm32(DESCRIPTOR_SIZE - 1))); ir.Imm32(count - 1)));
} else { } else {
inst->SetArg(0, IR::Value{}); inst->SetArg(0, IR::Value{});
} }
+4
View File
@@ -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-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -45,6 +48,7 @@ struct Profile {
bool support_scaled_attributes{}; bool support_scaled_attributes{};
bool support_multi_viewport{}; bool support_multi_viewport{};
bool support_geometry_streams{}; bool support_geometry_streams{};
bool support_sampled_image_array_nonuniform_indexing{};
bool warp_size_potentially_larger_than_guest{}; bool warp_size_potentially_larger_than_guest{};
+4 -1
View File
@@ -323,7 +323,10 @@ if (ENABLE_OPENGL)
endif() endif()
target_link_libraries(video_core PUBLIC common core) target_link_libraries(video_core PUBLIC common core)
target_link_libraries(video_core PUBLIC glad shader_recompiler stb bc_decoder gpu_logging) target_link_libraries(video_core PUBLIC shader_recompiler stb bc_decoder gpu_logging)
if (ENABLE_OPENGL)
target_link_libraries(video_core PUBLIC glad)
endif()
if (YUZU_USE_EXTERNAL_FFMPEG) if (YUZU_USE_EXTERNAL_FFMPEG)
add_dependencies(video_core ffmpeg-build) add_dependencies(video_core ffmpeg-build)
@@ -189,6 +189,7 @@ inline void PushImageDescriptors(TextureCache& texture_cache,
views += num_texture_buffers; views += num_texture_buffers;
views += num_image_buffers; views += num_image_buffers;
for (const auto& desc : info.texture_descriptors) { for (const auto& desc : info.texture_descriptors) {
bool is_rescaled{};
for (u32 index = 0; index < desc.count; ++index) { for (u32 index = 0; index < desc.count; ++index) {
const VideoCommon::ImageViewId image_view_id{(views++)->id}; const VideoCommon::ImageViewId image_view_id{(views++)->id};
const VideoCommon::SamplerId sampler_id{*(samplers++)}; const VideoCommon::SamplerId sampler_id{*(samplers++)};
@@ -200,10 +201,13 @@ inline void PushImageDescriptors(TextureCache& texture_cache,
const VkSampler vk_sampler{use_fallback_sampler ? sampler.HandleWithDefaultAnisotropy() const VkSampler vk_sampler{use_fallback_sampler ? sampler.HandleWithDefaultAnisotropy()
: sampler.Handle()}; : sampler.Handle()};
guest_descriptor_queue.AddSampledImage(vk_image_view, vk_sampler); guest_descriptor_queue.AddSampledImage(vk_image_view, vk_sampler);
rescaling.PushTexture(texture_cache.IsRescaling(image_view)); const bool element_rescaled{texture_cache.IsRescaling(image_view)};
is_rescaled |= element_rescaled;
} }
rescaling.PushTexture(is_rescaled);
} }
for (const auto& desc : info.image_descriptors) { for (const auto& desc : info.image_descriptors) {
bool is_rescaled{};
for (u32 index = 0; index < desc.count; ++index) { for (u32 index = 0; index < desc.count; ++index) {
ImageView& image_view{texture_cache.GetImageView((views++)->id)}; ImageView& image_view{texture_cache.GetImageView((views++)->id)};
if (desc.is_written) { if (desc.is_written) {
@@ -211,8 +215,10 @@ inline void PushImageDescriptors(TextureCache& texture_cache,
} }
const VkImageView vk_image_view{image_view.StorageView(desc.type, desc.format)}; const VkImageView vk_image_view{image_view.StorageView(desc.type, desc.format)};
guest_descriptor_queue.AddImage(vk_image_view); guest_descriptor_queue.AddImage(vk_image_view);
rescaling.PushImage(texture_cache.IsRescaling(image_view)); const bool element_rescaled{texture_cache.IsRescaling(image_view)};
is_rescaled |= element_rescaled;
} }
rescaling.PushImage(is_rescaled);
} }
} }
@@ -127,9 +127,8 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute,
texture_cache.SynchronizeComputeDescriptors(); texture_cache.SynchronizeComputeDescriptors();
static constexpr size_t max_elements = 64; boost::container::small_vector<VideoCommon::ImageViewInOut, 64> views;
boost::container::static_vector<VideoCommon::ImageViewInOut, max_elements> views; boost::container::small_vector<VideoCommon::SamplerId, 64> samplers;
boost::container::static_vector<VideoCommon::SamplerId, max_elements> samplers;
const auto& qmd{kepler_compute.launch_description}; const auto& qmd{kepler_compute.launch_description};
const auto& cbufs{qmd.const_buffer_config}; const auto& cbufs{qmd.const_buffer_config};
@@ -412,6 +412,8 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
.support_scaled_attributes = !device.MustEmulateScaledFormats(), .support_scaled_attributes = !device.MustEmulateScaledFormats(),
.support_multi_viewport = device.SupportsMultiViewport(), .support_multi_viewport = device.SupportsMultiViewport(),
.support_geometry_streams = device.AreTransformFeedbackGeometryStreamsSupported(), .support_geometry_streams = device.AreTransformFeedbackGeometryStreamsSupported(),
.support_sampled_image_array_nonuniform_indexing =
device.IsSampledImageArrayNonUniformIndexingSupported(),
.warp_size_potentially_larger_than_guest = device.IsWarpSizePotentiallyBiggerThanGuest(), .warp_size_potentially_larger_than_guest = device.IsWarpSizePotentiallyBiggerThanGuest(),
@@ -447,6 +449,9 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
.support_snorm_render_buffer = true, .support_snorm_render_buffer = true,
.support_viewport_index_layer = device.IsExtShaderViewportIndexLayerSupported(), .support_viewport_index_layer = device.IsExtShaderViewportIndexLayerSupported(),
.min_ssbo_alignment = static_cast<u32>(device.GetStorageBufferAlignment()), .min_ssbo_alignment = static_cast<u32>(device.GetStorageBufferAlignment()),
.max_per_stage_descriptor_sampled_images = device.GetMaxPerStageDescriptorSampledImages(),
.max_per_stage_resources = device.GetMaxPerStageResources(),
.max_descriptor_set_sampled_images = device.GetMaxDescriptorSetSampledImages(),
.support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(), .support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(),
.support_conditional_barrier = device.SupportsConditionalBarriers(), .support_conditional_barrier = device.SupportsConditionalBarriers(),
}; };
+12 -127
View File
@@ -70,14 +70,10 @@ TextureCache<P>::TextureCache(Runtime& runtime_, Tegra::MaxwellDeviceMemoryManag
(std::max)((std::min)(device_local_memory - min_vacancy_critical, min_spacing_critical), (std::max)((std::min)(device_local_memory - min_vacancy_critical, min_spacing_critical),
DEFAULT_CRITICAL_MEMORY)); DEFAULT_CRITICAL_MEMORY));
minimum_memory = static_cast<u64>((device_local_memory - mem_threshold) / 2); minimum_memory = static_cast<u64>((device_local_memory - mem_threshold) / 2);
lowmemorydevice = false;
} else { } else {
expected_memory = DEFAULT_EXPECTED_MEMORY + 512_MiB; expected_memory = DEFAULT_EXPECTED_MEMORY + 512_MiB;
critical_memory = DEFAULT_CRITICAL_MEMORY + 1_GiB; critical_memory = DEFAULT_CRITICAL_MEMORY + 1_GiB;
minimum_memory = 0; minimum_memory = 0;
lowmemorydevice = true;
} }
const bool gpu_unswizzle_enabled = Settings::values.gpu_unswizzle_enabled.GetValue(); const bool gpu_unswizzle_enabled = Settings::values.gpu_unswizzle_enabled.GetValue();
@@ -122,102 +118,48 @@ void TextureCache<P>::RunGarbageCollector() {
bool aggressive_mode = false; bool aggressive_mode = false;
u64 ticks_to_destroy = 0; u64 ticks_to_destroy = 0;
size_t num_iterations = 0; size_t num_iterations = 0;
const auto Configure = [&](bool allow_aggressive) { const auto Configure = [&](bool allow_aggressive) {
high_priority_mode = total_used_memory >= expected_memory; high_priority_mode = total_used_memory >= expected_memory;
aggressive_mode = allow_aggressive && total_used_memory >= critical_memory; aggressive_mode = allow_aggressive && total_used_memory >= critical_memory;
ticks_to_destroy = aggressive_mode ? 10ULL : high_priority_mode ? 25ULL : 50ULL; ticks_to_destroy = aggressive_mode ? 10ULL : high_priority_mode ? 25ULL : 50ULL;
num_iterations = aggressive_mode ? 40 : (high_priority_mode ? 20 : 10); num_iterations = aggressive_mode ? 40 : (high_priority_mode ? 20 : 10);
}; };
const auto Cleanup = [this, &num_iterations, &high_priority_mode, &aggressive_mode](ImageId image_id) {
const auto Cleanup = [this, &num_iterations, &high_priority_mode,
&aggressive_mode](ImageId image_id) {
if (num_iterations == 0) { if (num_iterations == 0) {
return true; return true;
} }
--num_iterations;
auto& image = slot_images[image_id]; auto& image = slot_images[image_id];
// Never delete recently allocated sparse textures (within 3 frames)
const bool is_recently_allocated = image.allocation_tick >= frame_tick - 3;
if (is_recently_allocated && image.info.is_sparse) {
return false;
}
if (True(image.flags & ImageFlagBits::IsDecoding)) { if (True(image.flags & ImageFlagBits::IsDecoding)) {
// This image is still being decoded, deleting it will invalidate the slot
// used by the async decoder thread.
return false; return false;
} }
const bool must_download = image.IsSafeDownload() && False(image.flags & ImageFlagBits::BadOverlap);
// Prioritize large sparse textures for cleanup if ((!aggressive_mode && True(image.flags & ImageFlagBits::CostlyLoad)) || (!high_priority_mode && must_download)) {
const bool is_large_sparse = lowmemorydevice &&
image.info.is_sparse &&
image.guest_size_bytes >= 256_MiB;
if (!aggressive_mode && !is_large_sparse &&
True(image.flags & ImageFlagBits::CostlyLoad)) {
return false; return false;
} }
--num_iterations;
const bool must_download = if (must_download) {
image.IsSafeDownload() && False(image.flags & ImageFlagBits::BadOverlap);
if (!high_priority_mode && !is_large_sparse && must_download) {
return false;
}
if (must_download && !is_large_sparse) {
auto map = runtime.DownloadStagingBuffer(image.unswizzled_size_bytes); auto map = runtime.DownloadStagingBuffer(image.unswizzled_size_bytes);
const auto copies = FixSmallVectorADL(FullDownloadCopies(image.info)); const auto copies = FixSmallVectorADL(FullDownloadCopies(image.info));
image.DownloadMemory(map, copies); image.DownloadMemory(map, copies);
runtime.Finish(); runtime.Finish();
SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span, SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span, swizzle_data_buffer);
swizzle_data_buffer);
} }
if (True(image.flags & ImageFlagBits::Tracked)) { if (True(image.flags & ImageFlagBits::Tracked)) {
UntrackImage(image, image_id); UntrackImage(image, image_id);
} }
UnregisterImage(image_id); UnregisterImage(image_id);
DeleteImage(image_id, image.scale_tick > frame_tick + 5); DeleteImage(image_id, image.scale_tick > frame_tick + 5);
if (aggressive_mode && total_used_memory < critical_memory) {
if (total_used_memory < critical_memory) { num_iterations >>= 2;
if (aggressive_mode) { aggressive_mode = false;
// Sink the aggresiveness. } else if (high_priority_mode && total_used_memory < expected_memory) {
num_iterations >>= 2; num_iterations >>= 1;
aggressive_mode = false; high_priority_mode = false;
return false;
}
if (high_priority_mode && total_used_memory < expected_memory) {
num_iterations >>= 1;
high_priority_mode = false;
}
} }
return false; return false;
}; };
// Aggressively clear massive sparse textures
if (total_used_memory >= expected_memory) {
lru_cache.ForEachItemBelow(frame_tick, [&](ImageId image_id) {
auto& image = slot_images[image_id];
// Only target sparse textures that are old enough
if (lowmemorydevice &&
image.info.is_sparse &&
image.guest_size_bytes >= 256_MiB &&
image.allocation_tick < frame_tick - 3) {
LOG_DEBUG(HW_GPU, "GC targeting old sparse texture at 0x{:X} ({} MiB, age: {} frames)",
image.gpu_addr, image.guest_size_bytes / (1024 * 1024),
frame_tick - image.allocation_tick);
return Cleanup(image_id);
}
return false;
});
}
Configure(false); Configure(false);
lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, Cleanup); lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, Cleanup);
// If pressure is still too high, prune aggressively.
if (total_used_memory >= critical_memory) { if (total_used_memory >= critical_memory) {
Configure(true); Configure(true);
lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, Cleanup); lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, Cleanup);
@@ -1196,9 +1138,6 @@ void TextureCache<P>::RefreshContents(Image& image, ImageId image_id) {
} }
image.flags &= ~ImageFlagBits::CpuModified; image.flags &= ~ImageFlagBits::CpuModified;
if( lowmemorydevice && image.info.format == PixelFormat::BC1_RGBA_UNORM && MapSizeBytes(image) >= 256_MiB ) {
return;
}
TrackImage(image, image_id); TrackImage(image, image_id);
@@ -1619,39 +1558,6 @@ ImageId TextureCache<P>::InsertImage(const ImageInfo& info, GPUVAddr gpu_addr,
} }
} }
ASSERT_MSG(cpu_addr, "Tried to insert an image to an invalid gpu_addr=0x{:x}", gpu_addr); ASSERT_MSG(cpu_addr, "Tried to insert an image to an invalid gpu_addr=0x{:x}", gpu_addr);
// For large sparse textures, aggressively clean up old allocations at same address
if (lowmemorydevice && info.is_sparse && CalculateGuestSizeInBytes(info) >= 256_MiB) {
const auto alloc_it = image_allocs_table.find(gpu_addr);
if (alloc_it != image_allocs_table.end()) {
const ImageAllocId alloc_id = alloc_it->second;
auto& alloc_images = slot_image_allocs[alloc_id].images;
// Collect old images at this address that were created more than 2 frames ago
boost::container::small_vector<ImageId, 4> to_delete;
for (ImageId old_image_id : alloc_images) {
Image& old_image = slot_images[old_image_id];
if (old_image.info.is_sparse &&
old_image.gpu_addr == gpu_addr &&
old_image.allocation_tick < frame_tick - 2) { // Try not to delete fresh textures
to_delete.push_back(old_image_id);
}
}
// Delete old images immediately
for (ImageId old_id : to_delete) {
Image& old_image = slot_images[old_id];
LOG_DEBUG(HW_GPU, "Immediately deleting old sparse texture at 0x{:X} ({} MiB)",
gpu_addr, old_image.guest_size_bytes / (1024 * 1024));
if (True(old_image.flags & ImageFlagBits::Tracked)) {
UntrackImage(old_image, old_id);
}
UnregisterImage(old_id);
DeleteImage(old_id, true);
}
}
}
const ImageId image_id = JoinImages(info, gpu_addr, *cpu_addr); const ImageId image_id = JoinImages(info, gpu_addr, *cpu_addr);
const Image& image = slot_images[image_id]; const Image& image = slot_images[image_id];
// Using "image.gpu_addr" instead of "gpu_addr" is important because it might be different // Using "image.gpu_addr" instead of "gpu_addr" is important because it might be different
@@ -1667,27 +1573,6 @@ template <class P>
ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, DAddr cpu_addr) { ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, DAddr cpu_addr) {
ImageInfo new_info = info; ImageInfo new_info = info;
const size_t size_bytes = CalculateGuestSizeInBytes(new_info); const size_t size_bytes = CalculateGuestSizeInBytes(new_info);
// Proactive cleanup for large sparse texture allocations
if (lowmemorydevice && new_info.is_sparse && size_bytes >= 256_MiB) {
const u64 estimated_alloc_size = size_bytes;
if (total_used_memory + estimated_alloc_size >= critical_memory) {
LOG_DEBUG(HW_GPU, "Large sparse texture allocation ({} MiB) - running aggressive GC. "
"Current memory: {} MiB, Critical: {} MiB",
size_bytes / (1024 * 1024),
total_used_memory / (1024 * 1024),
critical_memory / (1024 * 1024));
RunGarbageCollector();
// If still over threshold after GC, try one more aggressive pass
if (total_used_memory + estimated_alloc_size >= critical_memory) {
LOG_DEBUG(HW_GPU, "Still critically low on memory, running second GC pass");
RunGarbageCollector();
}
}
}
const bool broken_views = runtime.HasBrokenTextureViewFormats(); const bool broken_views = runtime.HasBrokenTextureViewFormats();
const bool native_bgr = runtime.HasNativeBgr(); const bool native_bgr = runtime.HasNativeBgr();
join_overlap_ids.clear(); join_overlap_ids.clear();
@@ -478,7 +478,6 @@ private:
u64 minimum_memory; u64 minimum_memory;
u64 expected_memory; u64 expected_memory;
u64 critical_memory; u64 critical_memory;
bool lowmemorydevice = false;
size_t gpu_unswizzle_maxsize = 0; size_t gpu_unswizzle_maxsize = 0;
size_t swizzle_chunk_size = 0; size_t swizzle_chunk_size = 0;
u32 swizzle_slices_per_batch = 0; u32 swizzle_slices_per_batch = 0;
@@ -33,6 +33,7 @@ VK_DEFINE_HANDLE(VmaAllocator)
FEATURE(KHR, VariablePointer, VARIABLE_POINTERS, variable_pointer) FEATURE(KHR, VariablePointer, VARIABLE_POINTERS, variable_pointer)
#define FOR_EACH_VK_FEATURE_1_2(FEATURE) \ #define FOR_EACH_VK_FEATURE_1_2(FEATURE) \
FEATURE(EXT, DescriptorIndexing, DESCRIPTOR_INDEXING, descriptor_indexing) \
FEATURE(EXT, HostQueryReset, HOST_QUERY_RESET, host_query_reset) \ FEATURE(EXT, HostQueryReset, HOST_QUERY_RESET, host_query_reset) \
FEATURE(KHR, 8BitStorage, 8BIT_STORAGE, bit8_storage) \ FEATURE(KHR, 8BitStorage, 8BIT_STORAGE, bit8_storage) \
FEATURE(KHR, TimelineSemaphore, TIMELINE_SEMAPHORE, timeline_semaphore) FEATURE(KHR, TimelineSemaphore, TIMELINE_SEMAPHORE, timeline_semaphore)
@@ -335,6 +336,18 @@ public:
return properties.properties.limits.maxDescriptorSetUniformBuffersDynamic; return properties.properties.limits.maxDescriptorSetUniformBuffersDynamic;
} }
u32 GetMaxPerStageDescriptorSampledImages() const {
return properties.properties.limits.maxPerStageDescriptorSampledImages;
}
u32 GetMaxPerStageResources() const {
return properties.properties.limits.maxPerStageResources;
}
u32 GetMaxDescriptorSetSampledImages() const {
return properties.properties.limits.maxDescriptorSetSampledImages;
}
/// Returns float control properties of the device. /// Returns float control properties of the device.
const VkPhysicalDeviceFloatControlsPropertiesKHR& FloatControlProperties() const { const VkPhysicalDeviceFloatControlsPropertiesKHR& FloatControlProperties() const {
return properties.float_controls; return properties.float_controls;
@@ -355,6 +368,10 @@ public:
return GetDriverID() != VK_DRIVER_ID_QUALCOMM_PROPRIETARY; return GetDriverID() != VK_DRIVER_ID_QUALCOMM_PROPRIETARY;
} }
bool IsSampledImageArrayNonUniformIndexingSupported() const {
return features.descriptor_indexing.shaderSampledImageArrayNonUniformIndexing;
}
/// Returns true if the device supports float64 natively. /// Returns true if the device supports float64 natively.
bool IsFloat64Supported() const { bool IsFloat64Supported() const {
return features.features.shaderFloat64; return features.features.shaderFloat64;
+4 -1
View File
@@ -411,8 +411,11 @@ endif()
target_link_libraries(yuzu PRIVATE nlohmann_json::nlohmann_json) target_link_libraries(yuzu PRIVATE nlohmann_json::nlohmann_json)
target_link_libraries(yuzu PRIVATE common core input_common frontend_common network video_core qt_common) target_link_libraries(yuzu PRIVATE common core input_common frontend_common network video_core qt_common)
target_link_libraries(yuzu PRIVATE Boost::headers glad Qt6::Widgets Qt6::Charts Qt6::Concurrent) target_link_libraries(yuzu PRIVATE Boost::headers Qt6::Widgets Qt6::Charts Qt6::Concurrent)
target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
if (ENABLE_OPENGL)
target_link_libraries(yuzu PRIVATE glad)
endif()
if (UNIX AND NOT APPLE) if (UNIX AND NOT APPLE)
target_link_libraries(yuzu PRIVATE Qt6::DBus) target_link_libraries(yuzu PRIVATE Qt6::DBus)
+25 -9
View File
@@ -8,11 +8,17 @@
#include <string> #include <string>
#include <tuple> #include <tuple>
#include <type_traits> #include <type_traits>
#ifdef HAS_OPENGL
#include <glad/glad.h> #include <glad/glad.h>
#endif
#include <QtCore/qglobal.h> #include <QtCore/qglobal.h>
#include "common/settings_enums.h" #include "common/settings_enums.h"
#include "qt_common/config/uisettings.h" #include "qt_common/config/uisettings.h"
#include "qt_common/abstract/frontend.h"
#if YUZU_USE_QT_MULTIMEDIA #if YUZU_USE_QT_MULTIMEDIA
#include <QCamera> #include <QCamera>
#include <QImageCapture> #include <QImageCapture>
@@ -1039,11 +1045,12 @@ void GRenderWindow::InitializeNull() {
} }
bool GRenderWindow::LoadOpenGL() { bool GRenderWindow::LoadOpenGL() {
#ifdef HAS_OPENGL
auto context = CreateSharedContext(); auto context = CreateSharedContext();
auto scope = context->Acquire(); auto scope = context->Acquire();
if (!gladLoadGL()) { if (!gladLoadGL()) {
QMessageBox::warning( QtCommon::Frontend::Warning(
this, tr("Error while initializing OpenGL!"), tr("Error while initializing OpenGL!"),
tr("Your GPU may not support OpenGL, or you do not have the latest graphics driver.")); tr("Your GPU may not support OpenGL, or you do not have the latest graphics driver."));
return false; return false;
} }
@@ -1052,15 +1059,16 @@ bool GRenderWindow::LoadOpenGL() {
const QString renderer = const QString renderer =
QString::fromUtf8(reinterpret_cast<const char*>(glGetString(GL_RENDERER))); QString::fromUtf8(reinterpret_cast<const char*>(glGetString(GL_RENDERER)));
if (!GLAD_GL_VERSION_4_6) { if (!GLAD_GL_VERSION_4_6) {
QMessageBox::warning(this, tr("Error while initializing OpenGL 4.6!"), QtCommon::Frontend::Warning(
tr("Your GPU may not support OpenGL 4.6, or you do not have the " tr("Error while initializing OpenGL 4.6!"),
"latest graphics driver.<br><br>GL Renderer:<br>%1") tr("Your GPU may not support OpenGL 4.6, or you do not have the "
.arg(renderer)); "latest graphics driver.<br><br>GL Renderer:<br>%1")
.arg(renderer));
return false; return false;
} }
if (QStringList missing_ext = GetUnsupportedGLExtensions(); !missing_ext.empty()) { if (QStringList missing_ext = GetUnsupportedGLExtensions(); !missing_ext.empty()) {
QMessageBox::warning( QtCommon::Frontend::Warning(
this, tr("Error while initializing OpenGL!"), tr("Error while initializing OpenGL!"),
tr("Your GPU may not support one or more required OpenGL extensions. Please ensure you " tr("Your GPU may not support one or more required OpenGL extensions. Please ensure you "
"have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported " "have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported "
"extensions:<br>%2") "extensions:<br>%2")
@@ -1069,10 +1077,17 @@ bool GRenderWindow::LoadOpenGL() {
// Non fatal // Non fatal
} }
return true; return true;
#else
QtCommon::Frontend::Warning(
tr("Error while initializing OpenGL!"),
tr("This build doesn't have OpenGL support."));
return false;
#endif
} }
QStringList GRenderWindow::GetUnsupportedGLExtensions() const { QStringList GRenderWindow::GetUnsupportedGLExtensions() const {
QStringList missing_ext; QStringList missing_ext{};
#ifdef HAS_OPENGL
// Extensions required to support some texture formats. // Extensions required to support some texture formats.
if (!GLAD_GL_EXT_texture_compression_s3tc) if (!GLAD_GL_EXT_texture_compression_s3tc)
missing_ext.append(QStringLiteral("EXT_texture_compression_s3tc")); missing_ext.append(QStringLiteral("EXT_texture_compression_s3tc"));
@@ -1082,6 +1097,7 @@ QStringList GRenderWindow::GetUnsupportedGLExtensions() const {
LOG_ERROR(Frontend, "GPU does not support all required extensions"); LOG_ERROR(Frontend, "GPU does not support all required extensions");
for (const QString& ext : missing_ext) for (const QString& ext : missing_ext)
LOG_ERROR(Frontend, "Unsupported GL extension: {}", ext.toStdString()); LOG_ERROR(Frontend, "Unsupported GL extension: {}", ext.toStdString());
#endif
return missing_ext; return missing_ext;
} }
+13 -3
View File
@@ -15,11 +15,18 @@ function(create_resource file output filename)
file(WRITE "${PROJECT_BINARY_DIR}/dist/${output}" "const unsigned char ${filename}[] = {${filedata}};\nconst unsigned ${filename}_size = sizeof(${filename});\n") file(WRITE "${PROJECT_BINARY_DIR}/dist/${output}" "const unsigned char ${filename}[] = {${filedata}};\nconst unsigned ${filename}_size = sizeof(${filename});\n")
endfunction() endfunction()
if (ENABLE_OPENGL)
list(APPEND OPENGL_SOURCES
emu_window/emu_window_sdl2_gl.cpp
emu_window/emu_window_sdl2_gl.h
)
else()
set(OPENGL_SOURCES "")
endif()
add_executable(yuzu-cmd add_executable(yuzu-cmd
emu_window/emu_window_sdl2.cpp emu_window/emu_window_sdl2.cpp
emu_window/emu_window_sdl2.h emu_window/emu_window_sdl2.h
emu_window/emu_window_sdl2_gl.cpp
emu_window/emu_window_sdl2_gl.h
emu_window/emu_window_sdl2_null.cpp emu_window/emu_window_sdl2_null.cpp
emu_window/emu_window_sdl2_null.h emu_window/emu_window_sdl2_null.h
emu_window/emu_window_sdl2_vk.cpp emu_window/emu_window_sdl2_vk.cpp
@@ -28,10 +35,13 @@ add_executable(yuzu-cmd
sdl_config.h sdl_config.h
yuzu.cpp yuzu.cpp
yuzu.rc yuzu.rc
${OPENGL_SOURCES}
) )
target_link_libraries(yuzu-cmd PRIVATE common core input_common frontend_common video_core) target_link_libraries(yuzu-cmd PRIVATE common core input_common frontend_common video_core)
target_link_libraries(yuzu-cmd PRIVATE glad) if (ENABLE_OPENGL)
target_link_libraries(yuzu-cmd PRIVATE glad)
endif()
if (MSVC) if (MSVC)
target_link_libraries(yuzu-cmd PRIVATE getopt) target_link_libraries(yuzu-cmd PRIVATE getopt)
endif() endif()
+6 -10
View File
@@ -59,20 +59,16 @@ private:
}; };
bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() { bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() {
std::vector<std::string_view> unsupported_ext; std::vector<std::string_view> unsupported_ext{};
#ifdef HAS_OPENGL
// Extensions required to support some texture formats. // Extensions required to support some texture formats.
if (!GLAD_GL_EXT_texture_compression_s3tc) { if (!GLAD_GL_EXT_texture_compression_s3tc)
unsupported_ext.push_back("EXT_texture_compression_s3tc"); unsupported_ext.push_back("EXT_texture_compression_s3tc");
} if (!GLAD_GL_ARB_texture_compression_rgtc)
if (!GLAD_GL_ARB_texture_compression_rgtc) {
unsupported_ext.push_back("ARB_texture_compression_rgtc"); unsupported_ext.push_back("ARB_texture_compression_rgtc");
} for (const auto& extension : unsupported_ext)
for (const auto& extension : unsupported_ext) {
LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", extension); LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", extension);
} #endif
return unsupported_ext.empty(); return unsupported_ext.empty();
} }
+7
View File
@@ -31,7 +31,9 @@
#include "sdl_config.h" #include "sdl_config.h"
#include "video_core/renderer_base.h" #include "video_core/renderer_base.h"
#include "yuzu_cmd/emu_window/emu_window_sdl2.h" #include "yuzu_cmd/emu_window/emu_window_sdl2.h"
#ifdef HAS_OPENGL
#include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h" #include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h"
#endif
#include "yuzu_cmd/emu_window/emu_window_sdl2_null.h" #include "yuzu_cmd/emu_window/emu_window_sdl2_null.h"
#include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h" #include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h"
@@ -349,17 +351,22 @@ int main(int argc, char** argv) {
std::unique_ptr<EmuWindow_SDL2> emu_window; std::unique_ptr<EmuWindow_SDL2> emu_window;
switch (Settings::values.renderer_backend.GetValue()) { switch (Settings::values.renderer_backend.GetValue()) {
#ifdef HAS_OPENGL
case Settings::RendererBackend::OpenGL_GLSL: case Settings::RendererBackend::OpenGL_GLSL:
case Settings::RendererBackend::OpenGL_GLASM: case Settings::RendererBackend::OpenGL_GLASM:
case Settings::RendererBackend::OpenGL_SPIRV: case Settings::RendererBackend::OpenGL_SPIRV:
emu_window = std::make_unique<EmuWindow_SDL2_GL>(&input_subsystem, system, fullscreen); emu_window = std::make_unique<EmuWindow_SDL2_GL>(&input_subsystem, system, fullscreen);
break; break;
#endif
case Settings::RendererBackend::Vulkan: case Settings::RendererBackend::Vulkan:
emu_window = std::make_unique<EmuWindow_SDL2_VK>(&input_subsystem, system, fullscreen); emu_window = std::make_unique<EmuWindow_SDL2_VK>(&input_subsystem, system, fullscreen);
break; break;
case Settings::RendererBackend::Null: case Settings::RendererBackend::Null:
emu_window = std::make_unique<EmuWindow_SDL2_Null>(&input_subsystem, system, fullscreen); emu_window = std::make_unique<EmuWindow_SDL2_Null>(&input_subsystem, system, fullscreen);
break; break;
default:
LOG_CRITICAL(Frontend, "Invalid renderer backend");
return -1;
} }
#ifdef _WIN32 #ifdef _WIN32