diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt index eca1d00fbe..88c9fb9ab0 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt @@ -23,6 +23,7 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting { RENDERER_USE_DISK_SHADER_CACHE("use_disk_shader_cache"), RENDERER_FORCE_MAX_CLOCK("force_max_clock"), RENDERER_ASYNCHRONOUS_SHADERS("use_asynchronous_shaders"), + RENDERER_EARLY_RELEASE_FENCES("early_release_fences"), RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"), ENABLE_BUFFER_HISTORY("enable_buffer_history"), SYNC_MEMORY_OPERATIONS("sync_memory_operations"), diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt index 02289edeae..61248b35d8 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt @@ -679,6 +679,13 @@ abstract class SettingsItem( descriptionId = R.string.renderer_asynchronous_shaders_description ) ) + put( + SwitchSetting( + BooleanSetting.RENDERER_EARLY_RELEASE_FENCES, + titleId = R.string.renderer_early_release_fences, + descriptionId = R.string.renderer_early_release_fences_description + ) + ) put( SingleChoiceSetting( IntSetting.FAST_GPU_TIME, diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt index 61b86c70d0..0024ab8e80 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt @@ -285,6 +285,7 @@ class SettingsFragmentPresenter( add(BooleanSetting.SKIP_CPU_INNER_INVALIDATION.key) add(BooleanSetting.FIX_BLOOM_EFFECTS.key) add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key) + add(BooleanSetting.RENDERER_EARLY_RELEASE_FENCES.key) add(SettingsItem.GPU_UNSWIZZLE_COMBINED) add(HeaderSetting(R.string.extensions)) diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index b703575cc5..5c3d7a966e 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -510,6 +510,8 @@ Reduces bloom blur in LA/EOW (Adreno 700), removes bloom in Burnout. Warning: may cause graphical artifacts in other games. Use asynchronous shaders Compiles shaders asynchronously. This may reduce stutters but may also introduce glitches. + Release Fences Early + Fixes crashes and freezes in some games, may cause issues with Unreal Engine games. GPU Unswizzle Settings Configure GPU-based texture unswizzling parameters or disable it entirely. Adjust these settings to balance performance and texture loading quality. Enable GPU Unswizzle diff --git a/src/common/settings.h b/src/common/settings.h index de7387c4f8..7077046e34 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -546,6 +546,16 @@ struct Values { SwitchableSetting use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders", Category::RendererHacks}; +#ifdef ANDROID + SwitchableSetting early_release_fences{linkage, + false, + "early_release_fences", + Category::RendererAdvanced, + Specialization::Default, + true, + true}; +#endif + SwitchableSetting gpu_unswizzle_texture_size{linkage, GpuUnswizzleSize::Large, "gpu_unswizzle_texture_size", diff --git a/src/video_core/fence_manager.h b/src/video_core/fence_manager.h index e4c4329e81..dc7711a6cf 100644 --- a/src/video_core/fence_manager.h +++ b/src/video_core/fence_manager.h @@ -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 2020 yuzu Emulator Project @@ -72,32 +72,49 @@ public: } void SignalFence(std::function&& func) { - if constexpr (!can_async_check) { - TryReleasePendingFences(); - } + const bool delay_fence = Settings::IsGPULevelHigh(); const bool should_flush = ShouldFlush(); - const bool delay_fence = Settings::IsGPULevelHigh() || (Settings::IsGPULevelMedium() && should_flush); + #ifdef __ANDROID__ + const bool early_release_fences = Settings::values.early_release_fences.GetValue(); + #else + constexpr bool early_release_fences = false; + #endif + constexpr bool async_supported = can_async_check; CommitAsyncFlushes(); TFence new_fence = CreateFence(!should_flush); - if constexpr (can_async_check) { - guard.lock(); + std::unique_lock lock(guard, std::defer_lock); + + const bool needs_lock = (early_release_fences && delay_fence) || + (!early_release_fences && async_supported); + if (needs_lock) { + lock.lock(); + } + if (!delay_fence) { + if (early_release_fences) { + TryReleasePendingFences(); + } else if constexpr (!async_supported) { + TryReleasePendingFences(); + } } if (delay_fence) { uncommitted_operations.emplace_back(std::move(func)); } - pending_operations.emplace_back(std::move(uncommitted_operations)); + if (!uncommitted_operations.empty()) { + pending_operations.emplace_back(std::move(uncommitted_operations)); + uncommitted_operations.clear(); + } QueueFence(new_fence); if (!delay_fence) { func(); } fences.push(std::move(new_fence)); + if (needs_lock) { + lock.unlock(); + cv.notify_all(); + } if (should_flush) { rasterizer.FlushCommands(); } - if constexpr (can_async_check) { - guard.unlock(); - cv.notify_all(); - } rasterizer.InvalidateGPUCache(); } diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp index a82e2c73fa..2dce02e060 100644 --- a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp +++ b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp @@ -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 2020 yuzu Emulator Project @@ -80,9 +80,7 @@ void MasterSemaphore::Wait(u64 tick) { if (!semaphore) { // If we don't support timeline semaphores, wait for the value normally std::unique_lock lk{free_mutex}; - free_cv.wait(lk, [&] { - return gpu_tick.load(std::memory_order_acquire) >= tick; - }); + free_cv.wait(lk, [&] { return gpu_tick.load(std::memory_order_relaxed) >= tick; }); return; } @@ -218,30 +216,13 @@ void MasterSemaphore::WaitThread(std::stop_token token) { wait_queue.pop(); } -#ifdef ANDROID - VkResult status; - do { - status = fence.GetStatus(); - if (status == VK_NOT_READY) { - std::this_thread::sleep_for(std::chrono::microseconds(100)); - } - } while (status == VK_NOT_READY); - - if (status == VK_SUCCESS) { - fence.Reset(); - } else { - vk::Check(status); - continue; - } -#else fence.Wait(); fence.Reset(); -#endif { std::scoped_lock lock{free_mutex}; free_queue.push_front(std::move(fence)); - gpu_tick.store(host_tick, std::memory_order_release); + gpu_tick.store(host_tick); } free_cv.notify_all(); }