From 0b4102c4bd4f5d2e95a61c9599377f82ee494b4a Mon Sep 17 00:00:00 2001 From: wildcard Date: Sun, 5 Oct 2025 23:11:40 +0200 Subject: [PATCH 01/17] [Vulkan] Phase 1: Descriptor Set caching --- .../renderer_vulkan/vk_descriptor_pool.cpp | 25 ++++++++++++++++-- .../renderer_vulkan/vk_descriptor_pool.h | 11 +++++++- .../renderer_vulkan/vk_graphics_pipeline.cpp | 26 ++++++++++++++----- .../renderer_vulkan/vk_graphics_pipeline.h | 3 ++- .../renderer_vulkan/vk_rasterizer.cpp | 2 ++ .../renderer_vulkan/vk_rasterizer.h | 2 +- 6 files changed, 58 insertions(+), 11 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp b/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp index 3af9758a31..e6b9662c85 100644 --- a/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp +++ b/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp @@ -96,9 +96,30 @@ VkDescriptorSet DescriptorAllocator::Commit() { const size_t index = CommitResource(); return sets[index / SETS_GROW_RATE][index % SETS_GROW_RATE]; } - +VkDescriptorSet DescriptorAllocator::CommitWithTracking(u64 current_frame, const void* descriptor_data) { + const size_t index = CommitResource(); + const size_t group = index / SETS_GROW_RATE; + const size_t slot = index % SETS_GROW_RATE; + set_states[group][slot].set = sets[group][slot]; + set_states[group][slot].last_update_frame = current_frame; + set_states[group][slot].last_data_ptr = descriptor_data; + return set_states[group][slot].set; + } +bool DescriptorAllocator::NeedsUpdate(VkDescriptorSet set, u64 current_frame, const void* descriptor_data) const { + for (const auto& group : set_states) { + for (const auto& st : group) { + if (st.set == set) { + // Update if pointer changed or the set hasn't been updated this frame + return st.last_data_ptr != descriptor_data || st.last_update_frame != current_frame; + } + } + } + return true; + } void DescriptorAllocator::Allocate(size_t begin, size_t end) { - sets.push_back(AllocateDescriptors(end - begin)); + const size_t count = end - begin; + sets.push_back(AllocateDescriptors(count)); + set_states.emplace_back(count); // create parallel state storage } vk::DescriptorSets DescriptorAllocator::AllocateDescriptors(size_t count) { diff --git a/src/video_core/renderer_vulkan/vk_descriptor_pool.h b/src/video_core/renderer_vulkan/vk_descriptor_pool.h index 4aada5a006..0a8d1afaa9 100644 --- a/src/video_core/renderer_vulkan/vk_descriptor_pool.h +++ b/src/video_core/renderer_vulkan/vk_descriptor_pool.h @@ -6,7 +6,7 @@ #include #include #include - +#include "common/common_types.h" #include "shader_recompiler/shader_info.h" #include "video_core/renderer_vulkan/vk_resource_pool.h" #include "video_core/vulkan_common/vulkan_wrapper.h" @@ -44,7 +44,15 @@ public: DescriptorAllocator(const DescriptorAllocator&) = delete; VkDescriptorSet Commit(); + // commit + remember when/with what we last updated this set + struct DescriptorSetState { + VkDescriptorSet set = VK_NULL_HANDLE; + u64 last_update_frame = 0; + const void* last_data_ptr = nullptr; // fast pointer compare + }; + VkDescriptorSet CommitWithTracking(u64 current_frame, const void* descriptor_data); + bool NeedsUpdate(VkDescriptorSet set, u64 current_frame, const void* descriptor_data) const; private: explicit DescriptorAllocator(const Device& device_, MasterSemaphore& master_semaphore_, DescriptorBank& bank_, VkDescriptorSetLayout layout_); @@ -58,6 +66,7 @@ private: VkDescriptorSetLayout layout{}; std::vector sets; + std::vector> set_states; }; class DescriptorPool { diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 745389213e..c18571c54e 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -513,7 +513,25 @@ void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling, const bool update_rescaling{scheduler.UpdateRescaling(is_rescaling)}; const bool bind_pipeline{scheduler.UpdateGraphicsPipeline(this)}; const void* const descriptor_data{guest_descriptor_queue.UpdateData()}; - scheduler.Record([this, descriptor_data, bind_pipeline, rescaling_data = rescaling.Data(), + + // allocate/bind descriptor set and only update if needed + VkDescriptorSet descriptor_set = VK_NULL_HANDLE; + bool needs_update = false; + if (descriptor_set_layout) { + if (uses_push_descriptor) { + // handled below via push descriptor + } else { + descriptor_set = descriptor_allocator.CommitWithTracking(current_frame_number, descriptor_data); + needs_update = descriptor_allocator.NeedsUpdate(descriptor_set, current_frame_number, descriptor_data); + if (needs_update) { + const vk::Device& dev{device.GetLogical()}; + dev.UpdateDescriptorSet(descriptor_set, *descriptor_update_template, descriptor_data); + } else { + } + } + } + + scheduler.Record([this, descriptor_data, descriptor_set, bind_pipeline, rescaling_data = rescaling.Data(), is_rescaling, update_rescaling, uses_render_area = render_area.uses_render_area, render_area_data = render_area.words](vk::CommandBuffer cmdbuf) { @@ -539,12 +557,8 @@ void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling, return; } if (uses_push_descriptor) { - cmdbuf.PushDescriptorSetWithTemplateKHR(*descriptor_update_template, *pipeline_layout, - 0, descriptor_data); + cmdbuf.PushDescriptorSetWithTemplateKHR(*descriptor_update_template, *pipeline_layout, 0, descriptor_data); } else { - const VkDescriptorSet descriptor_set{descriptor_allocator.Commit()}; - const vk::Device& dev{device.GetLogical()}; - dev.UpdateDescriptorSet(descriptor_set, *descriptor_update_template, descriptor_data); cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline_layout, 0, descriptor_set, nullptr); } diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index 650c8e07ed..e81b2ac1b6 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -116,7 +116,7 @@ public: maxwell3d = maxwell3d_; gpu_memory = gpu_memory_; } - + void SetFrameNumber(u64 frame) { current_frame_number = frame; } private: template bool ConfigureImpl(bool is_indexed); @@ -160,6 +160,7 @@ private: std::mutex build_mutex; std::atomic_bool is_built{false}; bool uses_push_descriptor{false}; + u64 current_frame_number{0}; }; } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 70ca9583f9..71533d44e9 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -217,6 +217,7 @@ void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) { std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; // update engine as channel may be different. pipeline->SetEngine(maxwell3d, gpu_memory); + pipeline->SetFrameNumber(current_frame_number); if (!pipeline->Configure(is_indexed)) return; @@ -759,6 +760,7 @@ void RasterizerVulkan::FlushCommands() { } void RasterizerVulkan::TickFrame() { + current_frame_number++; draw_counter = 0; guest_descriptor_queue.TickFrame(); compute_pass_descriptor_queue.TickFrame(); diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 30780b9cbd..bcfb3ac450 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -160,7 +160,7 @@ private: void UpdateDynamicStates(); void HandleTransformFeedback(); - + u64 current_frame_number{0}; void UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateScissorsState(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateDepthBias(Tegra::Engines::Maxwell3D::Regs& regs); From bc1d093fe9e5ee4ddee02e3beb43480f5ee2ead9 Mon Sep 17 00:00:00 2001 From: Aleksandr Popovich Date: Mon, 6 Oct 2025 03:08:00 +0200 Subject: [PATCH 02/17] [frontend] add 1.25x resolution option (#2566) It sits at 900p or 1350p. Signed-off-by: Aleksandr Popovich Co-authored-by: crueter Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2566 Reviewed-by: MaranBr Reviewed-by: crueter Co-authored-by: Aleksandr Popovich Co-committed-by: Aleksandr Popovich --- src/android/app/src/main/res/values/arrays.xml | 2 ++ src/android/app/src/main/res/values/strings.xml | 1 + src/common/settings.cpp | 4 ++++ src/common/settings_enums.h | 2 +- src/qt_common/shared_translation.cpp | 1 + 5 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml index 1b66c191d3..ec94730348 100644 --- a/src/android/app/src/main/res/values/arrays.xml +++ b/src/android/app/src/main/res/values/arrays.xml @@ -180,6 +180,7 @@ @string/resolution_half @string/resolution_three_quarter @string/resolution_one + @string/resolution_five_quarter @string/resolution_three_half @string/resolution_two @string/resolution_three @@ -202,6 +203,7 @@ 5 6 7 + 8 diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 2a5cc48bb1..2f6587d136 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -996,6 +996,7 @@ 0.5X (360p/540p) 0.75X (540p/810p) 1X (720p/1080p) + 1.25X (900p/1350p) 1.5X (1080p/1620p) 2X (1440p/2160p) (Slow) 3X (2160p/3240p) (Slow) diff --git a/src/common/settings.cpp b/src/common/settings.cpp index b41f4c75f5..b849d7cb6a 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -301,6 +301,10 @@ void TranslateResolutionInfo(ResolutionSetup setup, ResolutionScalingInfo& info) info.up_scale = 3; info.down_shift = 1; break; + case ResolutionSetup::Res5_4X: + info.up_scale = 5; + info.down_shift = 2; + break; case ResolutionSetup::Res2X: info.up_scale = 2; info.down_shift = 0; diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h index 0e5a08d845..6644bc01cc 100644 --- a/src/common/settings_enums.h +++ b/src/common/settings_enums.h @@ -142,7 +142,7 @@ ENUM(MemoryLayout, Memory_4Gb, Memory_6Gb, Memory_8Gb, Memory_10Gb, Memory_12Gb) ENUM(ConfirmStop, Ask_Always, Ask_Based_On_Game, Ask_Never); ENUM(FullscreenMode, Borderless, Exclusive); ENUM(NvdecEmulation, Off, Cpu, Gpu); -ENUM(ResolutionSetup, Res1_4X, Res1_2X, Res3_4X, Res1X, Res3_2X, Res2X, Res3X, Res4X, Res5X, Res6X, Res7X, Res8X); +ENUM(ResolutionSetup, Res1_4X, Res1_2X, Res3_4X, Res1X, Res5_4X, Res3_2X, Res2X, Res3X, Res4X, Res5X, Res6X, Res7X, Res8X); ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, Spline1, Gaussian, Lanczos, ScaleForce, Fsr, Area, MaxEnum); ENUM(AntiAliasing, None, Fxaa, Smaa, MaxEnum); ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch); diff --git a/src/qt_common/shared_translation.cpp b/src/qt_common/shared_translation.cpp index 8f5d929b74..5b8622e00a 100644 --- a/src/qt_common/shared_translation.cpp +++ b/src/qt_common/shared_translation.cpp @@ -540,6 +540,7 @@ std::unique_ptr ComboboxEnumeration(QObject* parent) PAIR(ResolutionSetup, Res1_2X, tr("0.5X (360p/540p) [EXPERIMENTAL]")), PAIR(ResolutionSetup, Res3_4X, tr("0.75X (540p/810p) [EXPERIMENTAL]")), PAIR(ResolutionSetup, Res1X, tr("1X (720p/1080p)")), + PAIR(ResolutionSetup, Res5_4X, tr("1.25X (900p/1350p) [EXPERIMENTAL]")), PAIR(ResolutionSetup, Res3_2X, tr("1.5X (1080p/1620p) [EXPERIMENTAL]")), PAIR(ResolutionSetup, Res2X, tr("2X (1440p/2160p)")), PAIR(ResolutionSetup, Res3X, tr("3X (2160p/3240p)")), From 5d4cfe195b9d221a0b7c7195dd0fd0f8d38b1838 Mon Sep 17 00:00:00 2001 From: Ribbit Date: Mon, 6 Oct 2025 17:39:32 +0200 Subject: [PATCH 03/17] [vk] Fix Vulkan Upload & Present Barriers for Spec Compliance (#2681) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The barrier before the CPU-upload copy was using VK_PIPELINE_STAGE_HOST_BIT. Validation rules only allow HOST as the source stage if you’re also specifying host-side access flags; inside a command buffer the GPU isn’t executing “host work,” so pairing that stage with the usual image layout transition is technically invalid. Switching the source stage to VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT keeps the ordering guarantee we need and satisfies the spec, while the actual host visibility is still handled correctly by the preceding vmaFlushAllocation. Co-authored-by: Ribbit Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2681 Reviewed-by: MaranBr Reviewed-by: Shinmegumi Co-authored-by: Ribbit Co-committed-by: Ribbit --- src/video_core/renderer_vulkan/present/layer.cpp | 2 +- src/video_core/renderer_vulkan/vk_present_manager.cpp | 4 ++-- src/video_core/renderer_vulkan/vk_texture_cache.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/video_core/renderer_vulkan/present/layer.cpp b/src/video_core/renderer_vulkan/present/layer.cpp index 5676dfe62a..fee19a69c2 100644 --- a/src/video_core/renderer_vulkan/present/layer.cpp +++ b/src/video_core/renderer_vulkan/present/layer.cpp @@ -332,7 +332,7 @@ void Layer::UpdateRawImage(const Tegra::FramebufferConfig& framebuffer, size_t i write_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; write_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, read_barrier); cmdbuf.CopyBufferToImage(*buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copy); cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, diff --git a/src/video_core/renderer_vulkan/vk_present_manager.cpp b/src/video_core/renderer_vulkan/vk_present_manager.cpp index 0b29ad1389..161f6c8b9f 100644 --- a/src/video_core/renderer_vulkan/vk_present_manager.cpp +++ b/src/video_core/renderer_vulkan/vk_present_manager.cpp @@ -412,7 +412,7 @@ void PresentManager::CopyToSwapchainImpl(Frame* frame) { .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, - .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT, + .dstAccessMask = 0, .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, .newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -460,7 +460,7 @@ void PresentManager::CopyToSwapchainImpl(Frame* frame) { MakeImageCopy(frame->width, frame->height, extent.width, extent.height)); } - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, {}, + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, {}, {}, {}, post_barriers); cmdbuf.End(); diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 575651905e..50a73ea76d 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -1068,7 +1068,7 @@ void TextureCacheRuntime::ReinterpretImage(Image& dst, Image& src, cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, READ_BARRIER, {}, middle_out_barrier); - cmdbuf.CopyBufferToImage(copy_buffer, dst_image, VK_IMAGE_LAYOUT_GENERAL, vk_out_copies); + cmdbuf.CopyBufferToImage(copy_buffer, dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, vk_out_copies); cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, {}, {}, post_barriers); }); From 6a4fa11ac3ad837caac5f6c305641f57fe4ecf73 Mon Sep 17 00:00:00 2001 From: crueter Date: Tue, 7 Oct 2025 01:31:36 +0200 Subject: [PATCH 04/17] [externals] only build spirv-tools as static (#2680) Upstream forcefully builds the shared lib and also builds shared and static separately for... some reason. I made a PR that fixes that, so for now let's use it here should speed up build on Android and Windows Signed-off-by: crueter Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2680 Reviewed-by: CamilleLaVey Reviewed-by: MaranBr --- .ci/android/build.sh | 4 ++-- externals/cpmfile.json | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.ci/android/build.sh b/.ci/android/build.sh index 4b4c9c0834..836faa38d5 100755 --- a/.ci/android/build.sh +++ b/.ci/android/build.sh @@ -13,8 +13,8 @@ fi cd src/android chmod +x ./gradlew -./gradlew assembleRelease -./gradlew bundleRelease +./gradlew assembleMainlineRelease +./gradlew bundleMainlineRelease if [ ! -z "${ANDROID_KEYSTORE_B64}" ]; then rm "${ANDROID_KEYSTORE_FILE}" diff --git a/externals/cpmfile.json b/externals/cpmfile.json index aa3a97c128..dde8c22d5f 100644 --- a/externals/cpmfile.json +++ b/externals/cpmfile.json @@ -117,9 +117,9 @@ }, "spirv-tools": { "package": "SPIRV-Tools", - "repo": "KhronosGroup/SPIRV-Tools", - "tag": "v%VERSION%", - "hash": "b17940433ced72e004c5eeffd7dd411b6afcc6a52ee31de6427d88edceb8172369be8ec8bf5b65708a78bf41fdae264d554aa7750b2209831679ab36bc867567", + "repo": "crueter/SPIRV-Tools", + "sha": "2fa2d44485", + "hash": "45b198be1d09974ccb2438e8bfa5683f23a0421b058297c28eacfd77e454ec2cf87e77850eddd202efff34b004d8d6b4d12e9615e59bd72be904c196f5eb2169", "git_version": "2025.4", "options": [ "SPIRV_SKIP_EXECUTABLES ON" From badd913bee87bde630e1e4d4c6182b027ed0830f Mon Sep 17 00:00:00 2001 From: crueter Date: Tue, 7 Oct 2025 01:32:09 +0200 Subject: [PATCH 05/17] [desktop] fix save data location, orphaned profiles finder (#2678) Previously, if the user had their NAND in a nonstandard location, profiles.dat would be read from the standard Eden path and thus return effectively garbage data. What this would result in is: - The Qt profile manager would be completely nonfunctional - "Open Save Data Location" would put you into the completely wrong place - Games would read from incorrect locations for their saves To solve this, I made it so that profiles.dat is re-read *after* QtConfig initializes. It's not the perfect solution, but it works. Additionally, this adds an orphaned profiles finder: - walks through the save folders in nand/user/save/000.../ - for each subdirectory, checks to see if profiles.dat contains a corresponding UUID - If not, the profile is "orphaned". It may contain legit save data, so let the user decide how to handle it (famous last words) - Empty profiles are just removed. If they really matter, they're instantly recreated anyways. The orphaned profiles check runs right *after* the decryption keys check, but before the game list ever gets populated Signed-off-by: crueter Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2678 Reviewed-by: CamilleLaVey Reviewed-by: MaranBr --- docs/user/Architectures.md | 2 + src/common/fs/fs_types.h | 4 + src/core/file_sys/savedata_factory.cpp | 5 -- src/core/hle/service/acc/profile_manager.cpp | 92 +++++++++++++++++++- src/core/hle/service/acc/profile_manager.h | 5 ++ src/qt_common/qt_content_util.cpp | 38 ++++++++ src/qt_common/qt_content_util.h | 3 + src/qt_common/qt_game_util.cpp | 15 ++-- src/qt_common/qt_game_util.h | 3 +- src/yuzu/main.cpp | 5 +- src/yuzu/migration_worker.cpp | 1 - 11 files changed, 156 insertions(+), 17 deletions(-) diff --git a/docs/user/Architectures.md b/docs/user/Architectures.md index 6d60716684..240feb666d 100644 --- a/docs/user/Architectures.md +++ b/docs/user/Architectures.md @@ -63,6 +63,8 @@ While all modern Linux distributions are supported (Fedora >40, Ubuntu >24.04, D Intel and Nvidia GPU support is limited. AMD (RADV) drivers receive first-class testing and are known to provide the most stable Eden experience possible. +Wayland is not recommended. Testing has shown significantly worse performance on most Wayland compositors compared to X11, alongside mysterious bugs and compatibility errors. For now, set `QT_QPA_PLATFORM=xcb` when running Eden, or pass `-platform xcb` to the launch arguments. + ## Windows Windows 10 and 11 are supported. Support for Windows 8.x is unknown, and Windows 7 support is unlikely to ever be added. diff --git a/src/common/fs/fs_types.h b/src/common/fs/fs_types.h index 900f85d24e..7b7359fa6f 100644 --- a/src/common/fs/fs_types.h +++ b/src/common/fs/fs_types.h @@ -1,8 +1,12 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include #include #include "common/common_funcs.h" diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp index edf51e74de..dda8d526d3 100644 --- a/src/core/file_sys/savedata_factory.cpp +++ b/src/core/file_sys/savedata_factory.cpp @@ -4,7 +4,6 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include #include "common/assert.h" #include "common/common_types.h" #include "common/logging/log.h" @@ -129,10 +128,6 @@ std::string SaveDataFactory::GetFullPath(ProgramId program_id, VirtualDir dir, std::string out = GetSaveDataSpaceIdPath(space); - LOG_INFO(Common_Filesystem, "Save ID: {:016X}", save_id); - LOG_INFO(Common_Filesystem, "User ID[1]: {:016X}", user_id[1]); - LOG_INFO(Common_Filesystem, "User ID[0]: {:016X}", user_id[0]); - switch (type) { case SaveDataType::System: return fmt::format("{}save/{:016X}/{:016X}{:016X}", out, save_id, user_id[1], user_id[0]); diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp index a4394046fa..4a892f7c65 100644 --- a/src/core/hle/service/acc/profile_manager.cpp +++ b/src/core/hle/service/acc/profile_manager.cpp @@ -4,13 +4,17 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include +#include +#include #include #include #include "common/fs/file.h" #include "common/fs/fs.h" +#include "common/fs/fs_types.h" #include "common/fs/path_util.h" #include #include "common/settings.h" @@ -90,6 +94,11 @@ bool ProfileManager::RemoveProfileAtIndex(std::size_t index) { return true; } +void ProfileManager::RemoveAllProfiles() +{ + profiles = {}; +} + /// Helper function to register a user to the system Result ProfileManager::AddUser(const ProfileInfo& user) { if (!AddToProfiles(user)) { @@ -259,8 +268,9 @@ void ProfileManager::CloseUser(UUID uuid) { /// Gets all valid user ids on the system UserIDArray ProfileManager::GetAllUsers() const { UserIDArray output{}; - std::ranges::transform(profiles, output.begin(), - [](const ProfileInfo& p) { return p.user_uuid; }); + std::ranges::transform(profiles, output.begin(), [](const ProfileInfo& p) { + return p.user_uuid; + }); return output; } @@ -387,18 +397,19 @@ bool ProfileManager::SetProfileBaseAndData(Common::UUID uuid, const ProfileBase& void ProfileManager::ParseUserSaveFile() { const auto save_path(FS::GetEdenPath(FS::EdenPath::NANDDir) / ACC_SAVE_AVATORS_BASE_PATH / "profiles.dat"); + const FS::IOFile save(save_path, FS::FileAccessMode::Read, FS::FileType::BinaryFile); if (!save.IsOpen()) { LOG_WARNING(Service_ACC, "Failed to load profile data from save data... Generating new " - "user 'eden' with random UUID."); + "user 'Eden' with random UUID."); return; } ProfileDataRaw data; if (!save.ReadObject(data)) { LOG_WARNING(Service_ACC, "profiles.dat is smaller than expected... Generating new user " - "'eden' with random UUID."); + "'Eden' with random UUID."); return; } @@ -471,6 +482,79 @@ void ProfileManager::WriteUserSaveFile() { is_save_needed = false; } +void ProfileManager::ResetUserSaveFile() +{ + RemoveAllProfiles(); + ParseUserSaveFile(); +} + +std::vector ProfileManager::FindOrphanedProfiles() +{ + std::vector good_uuids; + + for (const ProfileInfo& p : profiles) { + std::string uuid_string = [p]() -> std::string { + auto uuid = p.user_uuid; + + // "ignore" invalid uuids + if (uuid.IsInvalid()) { + return "0"; + } + + auto user_id = uuid.AsU128(); + + return fmt::format("{:016X}{:016X}", user_id[1], user_id[0]); + }(); + + good_uuids.emplace_back(uuid_string); + } + + // TODO: fetch save_id programmatically + const auto path = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) + / "user/save/0000000000000000"; + + std::vector orphaned_profiles; + + Common::FS::IterateDirEntries( + path, + [&good_uuids, &orphaned_profiles](const std::filesystem::directory_entry& entry) -> bool { + const std::string uuid = entry.path().stem().string(); + + // first off, we should always clear empty profiles + // 99% of the time these are useless. If not, they are recreated anyways... + namespace fs = std::filesystem; + + const auto is_empty = [&entry]() -> bool { + try { + for (const auto& file : fs::recursive_directory_iterator(entry.path())) { + if (file.is_regular_file()) { + return true; + } + } + } catch (const fs::filesystem_error& e) { + // if we get an error--no worries, just pretend it's not empty + return false; + } + return false; + }(); + + if (!is_empty) { + fs::remove_all(entry); + return true; + } + + // if profiles.dat contains the UUID--all good + // if not--it's an orphaned profile and should be resolved by the user + if (std::find(good_uuids.begin(), good_uuids.end(), uuid) == good_uuids.end()) { + orphaned_profiles.emplace_back(uuid); + } + return true; + }, + Common::FS::DirEntryFilter::Directory); + + return orphaned_profiles; +} + void ProfileManager::SetUserPosition(u64 position, Common::UUID uuid) { auto idxOpt = GetUserIndex(uuid); if (!idxOpt) diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h index d64e42715c..b164ed011a 100644 --- a/src/core/hle/service/acc/profile_manager.h +++ b/src/core/hle/service/acc/profile_manager.h @@ -103,10 +103,15 @@ public: void WriteUserSaveFile(); + void ResetUserSaveFile(); + + std::vector FindOrphanedProfiles(); + private: void ParseUserSaveFile(); std::optional AddToProfiles(const ProfileInfo& profile); bool RemoveProfileAtIndex(std::size_t index); + void RemoveAllProfiles(); bool is_save_needed{}; std::array profiles{}; diff --git a/src/qt_common/qt_content_util.cpp b/src/qt_common/qt_content_util.cpp index e4625aa423..2f659cf1b2 100644 --- a/src/qt_common/qt_content_util.cpp +++ b/src/qt_common/qt_content_util.cpp @@ -1,8 +1,10 @@ // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later +#include "qt_common/qt_game_util.h" #include "qt_content_util.h" #include "common/fs/fs.h" +#include "core/hle/service/acc/profile_manager.h" #include "frontend_common/content_manager.h" #include "frontend_common/firmware_manager.h" #include "qt_common/qt_common.h" @@ -310,4 +312,40 @@ void VerifyInstalledContents() { } } +void FixProfiles() +{ + // Reset user save files after config is initialized and migration is done. + // Doing it at init time causes profiles to read from the wrong place entirely if NAND dir is not default + // TODO: better solution + system->GetProfileManager().ResetUserSaveFile(); + std::vector orphaned = system->GetProfileManager().FindOrphanedProfiles(); + + // no orphaned dirs--all good :) + if (orphaned.empty()) + return; + + // otherwise, let the user know + QString qorphaned; + + // max. of 8 orphaned profiles is fair, I think + // 33 = 32 (UUID) + 1 (\n) + qorphaned.reserve(8 * 33); + + for (const std::string& s : orphaned) { + qorphaned += "\n" + QString::fromStdString(s); + } + + QtCommon::Frontend::Critical( + tr("Orphaned Profiles Detected!"), + tr("UNEXPECTED BAD THINGS MAY HAPPEN IF YOU DON'T READ THIS!\n" + "Eden has detected the following save directories with no attached profile:\n" + "%1\n\n" + "Click \"OK\" to open your save folder and fix up your profiles.\n" + "Hint: copy the contents of the largest or last-modified folder elsewhere, " + "delete all orphaned profiles, and move your copied contents to the good profile.") + .arg(qorphaned)); + + QtCommon::Game::OpenSaveFolder(); +} + } // namespace QtCommon::Content diff --git a/src/qt_common/qt_content_util.h b/src/qt_common/qt_content_util.h index b572c1c4a3..b95e78c0a0 100644 --- a/src/qt_common/qt_content_util.h +++ b/src/qt_common/qt_content_util.h @@ -45,5 +45,8 @@ void InstallKeys(); // Content // void VerifyGameContents(const std::string &game_path); void VerifyInstalledContents(); + +// Profiles // +void FixProfiles(); } #endif // QT_CONTENT_UTIL_H diff --git a/src/qt_common/qt_game_util.cpp b/src/qt_common/qt_game_util.cpp index 5d0b4d8ae7..ac922ea967 100644 --- a/src/qt_common/qt_game_util.cpp +++ b/src/qt_common/qt_game_util.cpp @@ -178,6 +178,12 @@ void OpenNANDFolder() OpenEdenFolder(Common::FS::EdenPath::NANDDir); } +void OpenSaveFolder() +{ + const auto path = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) / "user/save/0000000000000000"; + QDesktopServices::openUrl(QUrl::fromLocalFile(QString::fromStdString(path.string()))); +} + void OpenSDMCFolder() { OpenEdenFolder(Common::FS::EdenPath::SDMCDir); @@ -379,21 +385,21 @@ void RemoveCacheStorage(u64 program_id) } // Metadata // -void ResetMetadata() +void ResetMetadata(bool show_message) { const QString title = tr("Reset Metadata Cache"); if (!Common::FS::Exists(Common::FS::GetEdenPath(Common::FS::EdenPath::CacheDir) / "game_list/")) { - QtCommon::Frontend::Warning(rootObject, title, tr("The metadata cache is already empty.")); + if (show_message) QtCommon::Frontend::Warning(rootObject, title, tr("The metadata cache is already empty.")); } else if (Common::FS::RemoveDirRecursively( Common::FS::GetEdenPath(Common::FS::EdenPath::CacheDir) / "game_list")) { - QtCommon::Frontend::Information(rootObject, + if (show_message) QtCommon::Frontend::Information(rootObject, title, tr("The operation completed successfully.")); UISettings::values.is_game_list_reload_pending.exchange(true); } else { - QtCommon::Frontend::Warning( + if (show_message) QtCommon::Frontend::Warning( rootObject, title, tr("The metadata cache couldn't be deleted. It might be in use or non-existent.")); @@ -573,5 +579,4 @@ void CreateHomeMenuShortcut(ShortcutTarget target) { CreateShortcut(game_path, QLaunchId, "Switch Home Menu", target, "-qlaunch", false); } - } // namespace QtCommon::Game diff --git a/src/qt_common/qt_game_util.h b/src/qt_common/qt_game_util.h index 0a21208659..5c6bb24910 100644 --- a/src/qt_common/qt_game_util.h +++ b/src/qt_common/qt_game_util.h @@ -52,6 +52,7 @@ bool MakeShortcutIcoPath(const u64 program_id, void OpenEdenFolder(const Common::FS::EdenPath &path); void OpenRootDataFolder(); void OpenNANDFolder(); +void OpenSaveFolder(); void OpenSDMCFolder(); void OpenModFolder(); void OpenLogFolder(); @@ -67,7 +68,7 @@ void RemoveCustomConfiguration(u64 program_id, const std::string& game_path); void RemoveCacheStorage(u64 program_id); // Metadata // -void ResetMetadata(); +void ResetMetadata(bool show_message = true); // Shortcuts // void CreateShortcut(const std::string& game_path, diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 901a39cc9f..44ed29f141 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -545,6 +545,9 @@ GMainWindow::GMainWindow(bool has_broken_vulkan) // Gen keys if necessary OnCheckFirmwareDecryption(); + // Check for orphaned profiles and reset profile data if necessary + QtCommon::Content::FixProfiles(); + game_list->LoadCompatibilityList(); // force reload on first load to ensure add-ons get updated game_list->PopulateAsync(UISettings::values.game_dirs); @@ -3947,7 +3950,7 @@ void GMainWindow::OnToggleStatusBar() { void GMainWindow::OnGameListRefresh() { // Resets metadata cache and reloads - QtCommon::Game::ResetMetadata(); + QtCommon::Game::ResetMetadata(false); game_list->RefreshGameDirectory(); SetFirmwareVersion(); } diff --git a/src/yuzu/migration_worker.cpp b/src/yuzu/migration_worker.cpp index 42ec006026..95f205ec0c 100644 --- a/src/yuzu/migration_worker.cpp +++ b/src/yuzu/migration_worker.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include "common/fs/path_util.h" From 62369aa2d5cfd55da00a4174d5bffdf367f31eea Mon Sep 17 00:00:00 2001 From: Ribbit Date: Tue, 7 Oct 2025 03:26:54 +0200 Subject: [PATCH 06/17] [vk] StreamBuffer Changes (#2684) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tighter ring allocation + sync tracking with correct wrap/no-wrap, alignment, and per-range sync_ticks; update head/tail only when needed (wrap to 0 if tail won’t fit). Adds safer sizing under debug tools by preferring device-local host-visible heaps with fallback. Expected result: fewer stalls, steadier reuse, higher upload throughput. Co-authored-by: Ribbit Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2684 Reviewed-by: MaranBr Reviewed-by: Lizzie Reviewed-by: CamilleLaVey Co-authored-by: Ribbit Co-committed-by: Ribbit --- .../vk_staging_buffer_pool.cpp | 60 ++++++++++++++----- 1 file changed, 46 insertions(+), 14 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp index 08513d1534..ecc4f77dc7 100644 --- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp +++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp @@ -33,19 +33,29 @@ constexpr VkDeviceSize MAX_STREAM_BUFFER_SIZE = 128_MiB; size_t GetStreamBufferSize(const Device& device) { VkDeviceSize size{0}; if (device.HasDebuggingToolAttached()) { - ForEachDeviceLocalHostVisibleHeap(device, [&size](size_t index, VkMemoryHeap& heap) { + bool found_heap = false; + ForEachDeviceLocalHostVisibleHeap(device, [&size, &found_heap](size_t /*index*/, VkMemoryHeap& heap) { size = (std::max)(size, heap.size); + found_heap = true; }); - // If rebar is not supported, cut the max heap size to 40%. This will allow 2 captures to be - // loaded at the same time in RenderDoc. If rebar is supported, this shouldn't be an issue - // as the heap will be much larger. - if (size <= 256_MiB) { + // If no suitable heap was found fall back to the default cap to avoid creating a zero-sized stream buffer. + if (!found_heap) { + size = MAX_STREAM_BUFFER_SIZE; + } else if (size <= 256_MiB) { + // If rebar is not supported, cut the max heap size to 40%. This will allow 2 captures to be + // loaded at the same time in RenderDoc. If rebar is supported, this shouldn't be an issue + // as the heap will be much larger. size = size * 40 / 100; } } else { size = MAX_STREAM_BUFFER_SIZE; } - return (std::min)(Common::AlignUp(size, MAX_ALIGNMENT), MAX_STREAM_BUFFER_SIZE); + + // Clamp to the configured maximum, align up for safety, and ensure a sane minimum so + // region_size (stream_buffer_size / NUM_SYNCS) never becomes zero. + const VkDeviceSize aligned = (std::min)(Common::AlignUp(size, MAX_ALIGNMENT), MAX_STREAM_BUFFER_SIZE); + const VkDeviceSize min_size = MAX_ALIGNMENT * StagingBufferPool::NUM_SYNCS; + return static_cast((std::max)(aligned, min_size)); } } // Anonymous namespace @@ -106,31 +116,53 @@ void StagingBufferPool::TickFrame() { } StagingBufferRef StagingBufferPool::GetStreamBuffer(size_t size) { - if (AreRegionsActive(Region(free_iterator) + 1, - (std::min)(Region(iterator + size) + 1, NUM_SYNCS))) { + const size_t aligned_size = Common::AlignUp(size, MAX_ALIGNMENT); + const bool wraps = iterator + size >= stream_buffer_size; + const size_t new_iterator = + wraps ? aligned_size : Common::AlignUp(iterator + size, MAX_ALIGNMENT); + const size_t begin_region = wraps ? 0 : Region(iterator); + const size_t last_byte = new_iterator == 0 ? 0 : new_iterator - 1; + const size_t end_region = (std::min)(Region(last_byte) + 1, NUM_SYNCS); + const size_t guard_begin = (std::min)(Region(free_iterator) + 1, NUM_SYNCS); + + if (!wraps) { + if (guard_begin < end_region && AreRegionsActive(guard_begin, end_region)) { + // Avoid waiting for the previous usages to be free + return GetStagingBuffer(size, MemoryUsage::Upload); + } + } else if (guard_begin < NUM_SYNCS && AreRegionsActive(guard_begin, NUM_SYNCS)) { // Avoid waiting for the previous usages to be free return GetStagingBuffer(size, MemoryUsage::Upload); } + const u64 current_tick = scheduler.CurrentTick(); std::fill(sync_ticks.begin() + Region(used_iterator), sync_ticks.begin() + Region(iterator), current_tick); used_iterator = iterator; - free_iterator = (std::max)(free_iterator, iterator + size); - if (iterator + size >= stream_buffer_size) { + if (wraps) { std::fill(sync_ticks.begin() + Region(used_iterator), sync_ticks.begin() + NUM_SYNCS, current_tick); used_iterator = 0; iterator = 0; free_iterator = size; - - if (AreRegionsActive(0, Region(size) + 1)) { + const size_t head_last_byte = aligned_size == 0 ? 0 : aligned_size - 1; + const size_t head_end_region = (std::min)(Region(head_last_byte) + 1, NUM_SYNCS); + if (AreRegionsActive(0, head_end_region)) { // Avoid waiting for the previous usages to be free return GetStagingBuffer(size, MemoryUsage::Upload); } } - const size_t offset = iterator; - iterator = Common::AlignUp(iterator + size, MAX_ALIGNMENT); + + std::fill(sync_ticks.begin() + begin_region, sync_ticks.begin() + end_region, current_tick); + + const size_t offset = wraps ? 0 : iterator; + iterator = new_iterator; + + if (!wraps) { + free_iterator = (std::max)(free_iterator, offset + size); + } + return StagingBufferRef{ .buffer = *stream_buffer, .offset = static_cast(offset), From dbeae7add0bbc0242be84664641d8d20edbecdac Mon Sep 17 00:00:00 2001 From: lizzie Date: Tue, 7 Oct 2025 06:35:57 +0200 Subject: [PATCH 07/17] [vk, ogl] Add VK_QCOM ZTC, Bspline, Mitchell filter weights, add MMPX filter (#2577) Adds native support for QCOM cubic filter weights, and for devices whom do not support said weights, just implement them in shaders TODO: ZTC filter is wrong!? Signed-off-by: lizzie Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2577 Reviewed-by: crueter Reviewed-by: MaranBr Reviewed-by: CamilleLaVey Co-authored-by: lizzie Co-committed-by: lizzie --- docs/User.md | 2 +- .../app/src/main/res/values/arrays.xml | 8 ++ .../app/src/main/res/values/strings.xml | 4 + src/common/settings_enums.h | 2 +- src/qt_common/shared_translation.cpp | 4 + src/qt_common/shared_translation.h | 4 + src/video_core/host_shaders/CMakeLists.txt | 4 + .../host_shaders/present_bicubic.frag | 71 ++++------ .../host_shaders/present_bspline.frag | 35 +++++ .../host_shaders/present_mitchell.frag | 35 +++++ src/video_core/host_shaders/present_mmpx.frag | 131 ++++++++++++++++++ .../host_shaders/present_zero_tangent.frag | 35 +++++ .../renderer_opengl/gl_blit_screen.cpp | 13 ++ .../renderer_opengl/present/filters.cpp | 24 ++++ .../renderer_opengl/present/filters.h | 4 + .../renderer_vulkan/present/filters.cpp | 38 ++++- .../renderer_vulkan/present/filters.h | 3 +- .../renderer_vulkan/present/util.cpp | 13 +- src/video_core/renderer_vulkan/present/util.h | 2 +- .../renderer_vulkan/vk_blit_screen.cpp | 15 +- src/video_core/vulkan_common/vulkan_device.h | 8 +- 21 files changed, 395 insertions(+), 60 deletions(-) create mode 100644 src/video_core/host_shaders/present_bspline.frag create mode 100644 src/video_core/host_shaders/present_mitchell.frag create mode 100644 src/video_core/host_shaders/present_mmpx.frag create mode 100644 src/video_core/host_shaders/present_zero_tangent.frag diff --git a/docs/User.md b/docs/User.md index ba5d1b3eb0..67f81eadb6 100644 --- a/docs/User.md +++ b/docs/User.md @@ -7,4 +7,4 @@ This handbook is primarily aimed at the end-user - baking useful knowledge for e - **[The Basics](user/Basics.md)** - **[Audio](user/Audio.md)** - **[Graphics](user/Graphics.md)** -- **[Platforms and Architectures](user/Architectures.md)** \ No newline at end of file +- **[Platforms and Architectures](user/Architectures.md)** diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml index ec94730348..7e44750909 100644 --- a/src/android/app/src/main/res/values/arrays.xml +++ b/src/android/app/src/main/res/values/arrays.xml @@ -253,12 +253,16 @@ @string/scaling_filter_nearest_neighbor @string/scaling_filter_bilinear @string/scaling_filter_bicubic + @string/scaling_filter_zero_tangent + @string/scaling_filter_bspline + @string/scaling_filter_mitchell @string/scaling_filter_spline1 @string/scaling_filter_gaussian @string/scaling_filter_lanczos @string/scaling_filter_scale_force @string/scaling_filter_fsr @string/scaling_filter_area + @string/scaling_filter_mmpx @@ -271,6 +275,10 @@ 6 7 8 + 9 + 10 + 11 + 12 diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 2f6587d136..cff0bab2b5 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -1018,6 +1018,10 @@ ScaleForce AMD FidelityFX™ Super Resolution Area + Zero-Tangent + B-Spline + Mitchell + MMPX None diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h index 6644bc01cc..3fcdf08256 100644 --- a/src/common/settings_enums.h +++ b/src/common/settings_enums.h @@ -143,7 +143,7 @@ ENUM(ConfirmStop, Ask_Always, Ask_Based_On_Game, Ask_Never); ENUM(FullscreenMode, Borderless, Exclusive); ENUM(NvdecEmulation, Off, Cpu, Gpu); ENUM(ResolutionSetup, Res1_4X, Res1_2X, Res3_4X, Res1X, Res5_4X, Res3_2X, Res2X, Res3X, Res4X, Res5X, Res6X, Res7X, Res8X); -ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, Spline1, Gaussian, Lanczos, ScaleForce, Fsr, Area, MaxEnum); +ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, ZeroTangent, BSpline, Mitchell, Spline1, Gaussian, Lanczos, ScaleForce, Fsr, Area, Mmpx, MaxEnum); ENUM(AntiAliasing, None, Fxaa, Smaa, MaxEnum); ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch); ENUM(ConsoleMode, Handheld, Docked); diff --git a/src/qt_common/shared_translation.cpp b/src/qt_common/shared_translation.cpp index 5b8622e00a..f926f506e7 100644 --- a/src/qt_common/shared_translation.cpp +++ b/src/qt_common/shared_translation.cpp @@ -555,12 +555,16 @@ std::unique_ptr ComboboxEnumeration(QObject* parent) PAIR(ScalingFilter, NearestNeighbor, tr("Nearest Neighbor")), PAIR(ScalingFilter, Bilinear, tr("Bilinear")), PAIR(ScalingFilter, Bicubic, tr("Bicubic")), + PAIR(ScalingFilter, ZeroTangent, tr("Zero-Tangent")), + PAIR(ScalingFilter, BSpline, tr("B-Spline")), + PAIR(ScalingFilter, Mitchell, tr("Mitchell")), PAIR(ScalingFilter, Spline1, tr("Spline-1")), PAIR(ScalingFilter, Gaussian, tr("Gaussian")), PAIR(ScalingFilter, Lanczos, tr("Lanczos")), PAIR(ScalingFilter, ScaleForce, tr("ScaleForce")), PAIR(ScalingFilter, Fsr, tr("AMD FidelityFX™️ Super Resolution")), PAIR(ScalingFilter, Area, tr("Area")), + PAIR(ScalingFilter, Mmpx, tr("MMPX")), }}); translations->insert({Settings::EnumMetadata::Index(), { diff --git a/src/qt_common/shared_translation.h b/src/qt_common/shared_translation.h index c9216c2daa..a25887bb87 100644 --- a/src/qt_common/shared_translation.h +++ b/src/qt_common/shared_translation.h @@ -38,6 +38,9 @@ static const std::map scaling_filter_texts_map {Settings::ScalingFilter::Bilinear, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bilinear"))}, {Settings::ScalingFilter::Bicubic, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bicubic"))}, + {Settings::ScalingFilter::ZeroTangent, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Zero-Tangent"))}, + {Settings::ScalingFilter::BSpline, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "B-Spline"))}, + {Settings::ScalingFilter::Mitchell, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Mitchell"))}, {Settings::ScalingFilter::Spline1, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Spline-1"))}, {Settings::ScalingFilter::Gaussian, @@ -48,6 +51,7 @@ static const std::map scaling_filter_texts_map QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "ScaleForce"))}, {Settings::ScalingFilter::Fsr, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FSR"))}, {Settings::ScalingFilter::Area, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Area"))}, + {Settings::ScalingFilter::Mmpx, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "MMPX"))}, }; static const std::map use_docked_mode_texts_map = { diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index c14b44a45a..9f7b9edd5a 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -44,9 +44,13 @@ set(SHADER_FILES pitch_unswizzle.comp present_area.frag present_bicubic.frag + present_zero_tangent.frag + present_bspline.frag + present_mitchell.frag present_gaussian.frag present_lanczos.frag present_spline1.frag + present_mmpx.frag queries_prefix_scan_sum.comp queries_prefix_scan_sum_nosubgroups.comp resolve_conditional_render.comp diff --git a/src/video_core/host_shaders/present_bicubic.frag b/src/video_core/host_shaders/present_bicubic.frag index a9d9d40a38..5347fd2ef7 100644 --- a/src/video_core/host_shaders/present_bicubic.frag +++ b/src/video_core/host_shaders/present_bicubic.frag @@ -1,56 +1,37 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later - #version 460 core - - layout (location = 0) in vec2 frag_tex_coord; - layout (location = 0) out vec4 color; - layout (binding = 0) uniform sampler2D color_texture; - -vec4 cubic(float v) { - vec4 n = vec4(1.0, 2.0, 3.0, 4.0) - v; - vec4 s = n * n * n; - float x = s.x; - float y = s.y - 4.0 * s.x; - float z = s.z - 4.0 * s.y + 6.0 * s.x; - float w = 6.0 - x - y - z; - return vec4(x, y, z, w) * (1.0 / 6.0); +vec4 cubic(float x) { + float x2 = x * x; + float x3 = x2 * x; + return vec4(1.0, x, x2, x3) * transpose(mat4x4( + 0.0, 2.0, 0.0, 0.0, + -1.0, 0.0, 1.0, 0.0, + 2.0, -5.0, 4.0, -1.0, + -1.0, 3.0, -3.0, 1.0 + ) * (1.0 / 2.0)); } - -vec4 textureBicubic( sampler2D textureSampler, vec2 texCoords ) { - - vec2 texSize = textureSize(textureSampler, 0); - vec2 invTexSize = 1.0 / texSize; - - texCoords = texCoords * texSize - 0.5; - - vec2 fxy = fract(texCoords); - texCoords -= fxy; - - vec4 xcubic = cubic(fxy.x); - vec4 ycubic = cubic(fxy.y); - - vec4 c = texCoords.xxyy + vec2(-0.5, +1.5).xyxy; - - vec4 s = vec4(xcubic.xz + xcubic.yw, ycubic.xz + ycubic.yw); - vec4 offset = c + vec4(xcubic.yw, ycubic.yw) / s; - - offset *= invTexSize.xxyy; - - vec4 sample0 = texture(textureSampler, offset.xz); - vec4 sample1 = texture(textureSampler, offset.yz); - vec4 sample2 = texture(textureSampler, offset.xw); - vec4 sample3 = texture(textureSampler, offset.yw); - - float sx = s.x / (s.x + s.y); - float sy = s.z / (s.z + s.w); - - return mix(mix(sample3, sample2, sx), mix(sample1, sample0, sx), sy); +vec4 textureBicubic(sampler2D samp, vec2 uv) { + vec2 tex_size = vec2(textureSize(samp, 0)); + vec2 cc_tex = uv * tex_size - 0.5f; + vec2 fex = cc_tex - floor(cc_tex); + vec4 xcubic = cubic(fex.x); + vec4 ycubic = cubic(fex.y); + vec4 c = floor(cc_tex).xxyy + vec2(-0.5f, 1.5f).xyxy; + vec4 z = vec4(xcubic.yw, ycubic.yw); + vec4 s = vec4(xcubic.xz, ycubic.xz) + z; + vec4 offset = (c + z / s) * (1.0f / tex_size).xxyy; + vec2 n = vec2(s.x / (s.x + s.y), s.z / (s.z + s.w)); + return mix( + mix(texture(samp, offset.yw), texture(samp, offset.xw), n.x), + mix(texture(samp, offset.yz), texture(samp, offset.xz), n.x), + n.y); } - void main() { color = textureBicubic(color_texture, frag_tex_coord); } diff --git a/src/video_core/host_shaders/present_bspline.frag b/src/video_core/host_shaders/present_bspline.frag new file mode 100644 index 0000000000..f229de6030 --- /dev/null +++ b/src/video_core/host_shaders/present_bspline.frag @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later +#version 460 core +layout (location = 0) in vec2 frag_tex_coord; +layout (location = 0) out vec4 color; +layout (binding = 0) uniform sampler2D color_texture; +vec4 cubic(float x) { + float x2 = x * x; + float x3 = x2 * x; + return vec4(1.0, x, x2, x3) * transpose(mat4x4( + 1.0, 4.0, 1.0, 0.0, + -3.0, 0.0, 3.0, 0.0, + 3.0, -6.0, 3.0, 0.0, + -1.0, 3.0, -3.0, 1.0 + ) * (1.0 / 6.0)); +} +vec4 textureBicubic(sampler2D samp, vec2 uv) { + vec2 tex_size = vec2(textureSize(samp, 0)); + vec2 cc_tex = uv * tex_size - 0.5f; + vec2 fex = cc_tex - floor(cc_tex); + vec4 xcubic = cubic(fex.x); + vec4 ycubic = cubic(fex.y); + vec4 c = floor(cc_tex).xxyy + vec2(-0.5f, 1.5f).xyxy; + vec4 z = vec4(xcubic.yw, ycubic.yw); + vec4 s = vec4(xcubic.xz, ycubic.xz) + z; + vec4 offset = (c + z / s) * (1.0f / tex_size).xxyy; + vec2 n = vec2(s.x / (s.x + s.y), s.z / (s.z + s.w)); + return mix( + mix(texture(samp, offset.yw), texture(samp, offset.xw), n.x), + mix(texture(samp, offset.yz), texture(samp, offset.xz), n.x), + n.y); +} +void main() { + color = textureBicubic(color_texture, frag_tex_coord); +} diff --git a/src/video_core/host_shaders/present_mitchell.frag b/src/video_core/host_shaders/present_mitchell.frag new file mode 100644 index 0000000000..4ca65cd6f0 --- /dev/null +++ b/src/video_core/host_shaders/present_mitchell.frag @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later +#version 460 core +layout (location = 0) in vec2 frag_tex_coord; +layout (location = 0) out vec4 color; +layout (binding = 0) uniform sampler2D color_texture; +vec4 cubic(float x) { + float x2 = x * x; + float x3 = x2 * x; + return vec4(1.0, x, x2, x3) * transpose(mat4x4( + 1.0, 16.0, 1.0, 0.0, + -9.0, 0.0, 9.0, 0.0, + 15.0, -36.0, 27.0, -6.0, + -7.0, 21.0, -21.0, 7.0 + ) * (1.0 / 18.0)); +} +vec4 textureBicubic(sampler2D samp, vec2 uv) { + vec2 tex_size = vec2(textureSize(samp, 0)); + vec2 cc_tex = uv * tex_size - 0.5f; + vec2 fex = cc_tex - floor(cc_tex); + vec4 xcubic = cubic(fex.x); + vec4 ycubic = cubic(fex.y); + vec4 c = floor(cc_tex).xxyy + vec2(-0.5f, 1.5f).xyxy; + vec4 z = vec4(xcubic.yw, ycubic.yw); + vec4 s = vec4(xcubic.xz, ycubic.xz) + z; + vec4 offset = (c + z / s) * (1.0f / tex_size).xxyy; + vec2 n = vec2(s.x / (s.x + s.y), s.z / (s.z + s.w)); + return mix( + mix(texture(samp, offset.yw), texture(samp, offset.xw), n.x), + mix(texture(samp, offset.yz), texture(samp, offset.xz), n.x), + n.y); +} +void main() { + color = textureBicubic(color_texture, frag_tex_coord); +} diff --git a/src/video_core/host_shaders/present_mmpx.frag b/src/video_core/host_shaders/present_mmpx.frag new file mode 100644 index 0000000000..6c2c05a63a --- /dev/null +++ b/src/video_core/host_shaders/present_mmpx.frag @@ -0,0 +1,131 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright 2023 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#version 460 core +layout(location = 0) in vec2 tex_coord; +layout(location = 0) out vec4 frag_color; +layout(binding = 0) uniform sampler2D tex; + +#define src(x, y) texture(tex, coord + vec2(x, y) * 1.0 / source_size) + +float luma(vec4 col) { + return dot(col.rgb, vec3(0.2126, 0.7152, 0.0722)) * (1.0 - col.a); +} + +bool same(vec4 B, vec4 A0) { + return all(equal(B, A0)); +} + +bool notsame(vec4 B, vec4 A0) { + return any(notEqual(B, A0)); +} + +bool all_eq2(vec4 B, vec4 A0, vec4 A1) { + return (same(B,A0) && same(B,A1)); +} + +bool all_eq3(vec4 B, vec4 A0, vec4 A1, vec4 A2) { + return (same(B,A0) && same(B,A1) && same(B,A2)); +} + +bool all_eq4(vec4 B, vec4 A0, vec4 A1, vec4 A2, vec4 A3) { + return (same(B,A0) && same(B,A1) && same(B,A2) && same(B,A3)); +} + +bool any_eq3(vec4 B, vec4 A0, vec4 A1, vec4 A2) { + return (same(B,A0) || same(B,A1) || same(B,A2)); +} + +bool none_eq2(vec4 B, vec4 A0, vec4 A1) { + return (notsame(B,A0) && notsame(B,A1)); +} + +bool none_eq4(vec4 B, vec4 A0, vec4 A1, vec4 A2, vec4 A3) { + return (notsame(B,A0) && notsame(B,A1) && notsame(B,A2) && notsame(B,A3)); +} + +void main() +{ + vec2 source_size = vec2(textureSize(tex, 0)); + vec2 pos = fract(tex_coord * source_size) - vec2(0.5, 0.5); + vec2 coord = tex_coord - pos / source_size; + + vec4 E = src(0.0,0.0); + + vec4 A = src(-1.0,-1.0); + vec4 B = src(0.0,-1.0); + vec4 C = src(1.0,-1.0); + + vec4 D = src(-1.0,0.0); + vec4 F = src(1.0,0.0); + + vec4 G = src(-1.0,1.0); + vec4 H = src(0.0,1.0); + vec4 I = src(1.0,1.0); + + vec4 J = E; + vec4 K = E; + vec4 L = E; + vec4 M = E; + + frag_color = E; + + if(same(E,A) && same(E,B) && same(E,C) && same(E,D) && same(E,F) && same(E,G) && same(E,H) && same(E,I)) return; + + vec4 P = src(0.0,2.0); + vec4 Q = src(-2.0,0.0); + vec4 R = src(2.0,0.0); + vec4 S = src(0.0,2.0); + + float Bl = luma(B); + float Dl = luma(D); + float El = luma(E); + float Fl = luma(F); + float Hl = luma(H); + + if (((same(D,B) && notsame(D,H) && notsame(D,F))) && ((El>=Dl) || same(E,A)) && any_eq3(E,A,C,G) && ((El=Bl) || same(E,C)) && any_eq3(E,A,C,I) && ((El=Hl) || same(E,G)) && any_eq3(E,A,G,I) && ((El=Fl) || same(E,I)) && any_eq3(E,C,G,I) && ((El MakeBicubic(const Device& device) { HostShaders::PRESENT_BICUBIC_FRAG); } +std::unique_ptr MakeMitchell(const Device& device) { + return std::make_unique(device, CreateBilinearSampler(), + HostShaders::PRESENT_MITCHELL_FRAG); +} + +std::unique_ptr MakeZeroTangent(const Device& device) { + return std::make_unique(device, CreateBilinearSampler(), + HostShaders::PRESENT_ZERO_TANGENT_FRAG); +} + +std::unique_ptr MakeBSpline(const Device& device) { + return std::make_unique(device, CreateBilinearSampler(), + HostShaders::PRESENT_BSPLINE_FRAG); +} + std::unique_ptr MakeGaussian(const Device& device) { return std::make_unique(device, CreateBilinearSampler(), HostShaders::PRESENT_GAUSSIAN_FRAG); @@ -60,4 +79,9 @@ std::unique_ptr MakeArea(const Device& device) { HostShaders::PRESENT_AREA_FRAG); } +std::unique_ptr MakeMmpx(const Device& device) { + return std::make_unique(device, CreateNearestNeighborSampler(), + HostShaders::PRESENT_MMPX_FRAG); +} + } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/present/filters.h b/src/video_core/renderer_opengl/present/filters.h index 7b38ac56bc..187d0f1298 100644 --- a/src/video_core/renderer_opengl/present/filters.h +++ b/src/video_core/renderer_opengl/present/filters.h @@ -17,10 +17,14 @@ namespace OpenGL { std::unique_ptr MakeNearestNeighbor(const Device& device); std::unique_ptr MakeBilinear(const Device& device); std::unique_ptr MakeBicubic(const Device& device); +std::unique_ptr MakeZeroTangent(const Device& device); +std::unique_ptr MakeMitchell(const Device& device); +std::unique_ptr MakeBSpline(const Device& device); std::unique_ptr MakeGaussian(const Device& device); std::unique_ptr MakeSpline1(const Device& device); std::unique_ptr MakeLanczos(const Device& device); std::unique_ptr MakeScaleForce(const Device& device); std::unique_ptr MakeArea(const Device& device); +std::unique_ptr MakeMmpx(const Device& device); } // namespace OpenGL diff --git a/src/video_core/renderer_vulkan/present/filters.cpp b/src/video_core/renderer_vulkan/present/filters.cpp index e0f2b26f84..0a28ea6349 100644 --- a/src/video_core/renderer_vulkan/present/filters.cpp +++ b/src/video_core/renderer_vulkan/present/filters.cpp @@ -7,6 +7,8 @@ // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include +#include "common/assert.h" #include "common/common_types.h" #include "video_core/host_shaders/present_area_frag_spv.h" @@ -14,6 +16,10 @@ #include "video_core/host_shaders/present_gaussian_frag_spv.h" #include "video_core/host_shaders/present_lanczos_frag_spv.h" #include "video_core/host_shaders/present_spline1_frag_spv.h" +#include "video_core/host_shaders/present_mitchell_frag_spv.h" +#include "video_core/host_shaders/present_bspline_frag_spv.h" +#include "video_core/host_shaders/present_zero_tangent_frag_spv.h" +#include "video_core/host_shaders/present_mmpx_frag_spv.h" #include "video_core/host_shaders/vulkan_present_frag_spv.h" #include "video_core/host_shaders/vulkan_present_scaleforce_fp16_frag_spv.h" #include "video_core/host_shaders/vulkan_present_scaleforce_fp32_frag_spv.h" @@ -52,13 +58,28 @@ std::unique_ptr MakeSpline1(const Device& device, VkFormat fram BuildShader(device, PRESENT_SPLINE1_FRAG_SPV)); } -std::unique_ptr MakeBicubic(const Device& device, VkFormat frame_format) { +std::unique_ptr MakeBicubic(const Device& device, VkFormat frame_format, VkCubicFilterWeightsQCOM qcom_weights) { // No need for handrolled shader -- if the VK impl can do it for us ;) - if (device.IsExtFilterCubicSupported()) - return std::make_unique(device, frame_format, CreateCubicSampler(device), - BuildShader(device, VULKAN_PRESENT_FRAG_SPV)); - return std::make_unique(device, frame_format, CreateBilinearSampler(device), - BuildShader(device, PRESENT_BICUBIC_FRAG_SPV)); + // Catmull-Rom is default bicubic for all implementations... + if (device.IsExtFilterCubicSupported() && (device.IsQcomFilterCubicWeightsSupported() || qcom_weights == VK_CUBIC_FILTER_WEIGHTS_CATMULL_ROM_QCOM)) { + return std::make_unique(device, frame_format, CreateCubicSampler(device, + qcom_weights), BuildShader(device, VULKAN_PRESENT_FRAG_SPV)); + } else { + return std::make_unique(device, frame_format, CreateBilinearSampler(device), [&](){ + switch (qcom_weights) { + case VK_CUBIC_FILTER_WEIGHTS_CATMULL_ROM_QCOM: + return BuildShader(device, PRESENT_BICUBIC_FRAG_SPV); + case VK_CUBIC_FILTER_WEIGHTS_ZERO_TANGENT_CARDINAL_QCOM: + return BuildShader(device, PRESENT_ZERO_TANGENT_FRAG_SPV); + case VK_CUBIC_FILTER_WEIGHTS_B_SPLINE_QCOM: + return BuildShader(device, PRESENT_BSPLINE_FRAG_SPV); + case VK_CUBIC_FILTER_WEIGHTS_MITCHELL_NETRAVALI_QCOM: + return BuildShader(device, PRESENT_MITCHELL_FRAG_SPV); + default: + UNREACHABLE(); + } + }()); + } } std::unique_ptr MakeGaussian(const Device& device, VkFormat frame_format) { @@ -81,4 +102,9 @@ std::unique_ptr MakeArea(const Device& device, VkFormat frame_f BuildShader(device, PRESENT_AREA_FRAG_SPV)); } +std::unique_ptr MakeMmpx(const Device& device, VkFormat frame_format) { + return std::make_unique(device, frame_format, CreateNearestNeighborSampler(device), + BuildShader(device, PRESENT_MMPX_FRAG_SPV)); +} + } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/filters.h b/src/video_core/renderer_vulkan/present/filters.h index 015bffc8a5..afc3ba29a0 100644 --- a/src/video_core/renderer_vulkan/present/filters.h +++ b/src/video_core/renderer_vulkan/present/filters.h @@ -17,11 +17,12 @@ class MemoryAllocator; std::unique_ptr MakeNearestNeighbor(const Device& device, VkFormat frame_format); std::unique_ptr MakeBilinear(const Device& device, VkFormat frame_format); -std::unique_ptr MakeBicubic(const Device& device, VkFormat frame_format); +std::unique_ptr MakeBicubic(const Device& device, VkFormat frame_format, VkCubicFilterWeightsQCOM qcom_weights); std::unique_ptr MakeSpline1(const Device& device, VkFormat frame_format); std::unique_ptr MakeGaussian(const Device& device, VkFormat frame_format); std::unique_ptr MakeLanczos(const Device& device, VkFormat frame_format); std::unique_ptr MakeScaleForce(const Device& device, VkFormat frame_format); std::unique_ptr MakeArea(const Device& device, VkFormat frame_format); +std::unique_ptr MakeMmpx(const Device& device, VkFormat frame_format); } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/util.cpp b/src/video_core/renderer_vulkan/present/util.cpp index 0b1a89eec0..29a1c34976 100644 --- a/src/video_core/renderer_vulkan/present/util.cpp +++ b/src/video_core/renderer_vulkan/present/util.cpp @@ -624,8 +624,8 @@ vk::Sampler CreateNearestNeighborSampler(const Device& device) { return device.GetLogical().CreateSampler(ci_nn); } -vk::Sampler CreateCubicSampler(const Device& device) { - const VkSamplerCreateInfo ci_nn{ +vk::Sampler CreateCubicSampler(const Device& device, VkCubicFilterWeightsQCOM qcom_weights) { + VkSamplerCreateInfo ci_nn{ .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, .pNext = nullptr, .flags = 0, @@ -645,7 +645,14 @@ vk::Sampler CreateCubicSampler(const Device& device) { .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK, .unnormalizedCoordinates = VK_FALSE, }; - + const VkSamplerCubicWeightsCreateInfoQCOM ci_qcom_nn{ + .sType = VK_STRUCTURE_TYPE_SAMPLER_CUBIC_WEIGHTS_CREATE_INFO_QCOM, + .pNext = nullptr, + .cubicWeights = qcom_weights + }; + // If not specified, assume Catmull-Rom + if (qcom_weights != VK_CUBIC_FILTER_WEIGHTS_CATMULL_ROM_QCOM) + ci_nn.pNext = &ci_qcom_nn; return device.GetLogical().CreateSampler(ci_nn); } diff --git a/src/video_core/renderer_vulkan/present/util.h b/src/video_core/renderer_vulkan/present/util.h index 11810352df..38cc6203c5 100644 --- a/src/video_core/renderer_vulkan/present/util.h +++ b/src/video_core/renderer_vulkan/present/util.h @@ -57,7 +57,7 @@ VkWriteDescriptorSet CreateWriteDescriptorSet(std::vector VkDescriptorSet set, u32 binding); vk::Sampler CreateBilinearSampler(const Device& device); vk::Sampler CreateNearestNeighborSampler(const Device& device); -vk::Sampler CreateCubicSampler(const Device& device); +vk::Sampler CreateCubicSampler(const Device& device, VkCubicFilterWeightsQCOM qcom_weights); void BeginRenderPass(vk::CommandBuffer& cmdbuf, VkRenderPass render_pass, VkFramebuffer framebuffer, VkExtent2D extent); diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index b720bcded3..0f54dd5ade 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp @@ -7,6 +7,7 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include "video_core/framebuffer_config.h" #include "video_core/present.h" #include "video_core/renderer_vulkan/present/filters.h" @@ -41,7 +42,16 @@ void BlitScreen::SetWindowAdaptPass() { window_adapt = MakeNearestNeighbor(device, swapchain_view_format); break; case Settings::ScalingFilter::Bicubic: - window_adapt = MakeBicubic(device, swapchain_view_format); + window_adapt = MakeBicubic(device, swapchain_view_format, VK_CUBIC_FILTER_WEIGHTS_CATMULL_ROM_QCOM); + break; + case Settings::ScalingFilter::ZeroTangent: + window_adapt = MakeBicubic(device, swapchain_view_format, VK_CUBIC_FILTER_WEIGHTS_ZERO_TANGENT_CARDINAL_QCOM); + break; + case Settings::ScalingFilter::BSpline: + window_adapt = MakeBicubic(device, swapchain_view_format, VK_CUBIC_FILTER_WEIGHTS_B_SPLINE_QCOM); + break; + case Settings::ScalingFilter::Mitchell: + window_adapt = MakeBicubic(device, swapchain_view_format, VK_CUBIC_FILTER_WEIGHTS_MITCHELL_NETRAVALI_QCOM); break; case Settings::ScalingFilter::Spline1: window_adapt = MakeSpline1(device, swapchain_view_format); @@ -58,6 +68,9 @@ void BlitScreen::SetWindowAdaptPass() { case Settings::ScalingFilter::Area: window_adapt = MakeArea(device, swapchain_view_format); break; + case Settings::ScalingFilter::Mmpx: + window_adapt = MakeMmpx(device, swapchain_view_format); + break; case Settings::ScalingFilter::Fsr: case Settings::ScalingFilter::Bilinear: default: diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index bd54144480..cb13f28523 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -89,7 +89,8 @@ VK_DEFINE_HANDLE(VmaAllocator) EXTENSION(NV, VIEWPORT_ARRAY2, viewport_array2) \ EXTENSION(NV, VIEWPORT_SWIZZLE, viewport_swizzle) \ EXTENSION(EXT, DESCRIPTOR_INDEXING, descriptor_indexing) \ - EXTENSION(EXT, FILTER_CUBIC, filter_cubic) + EXTENSION(EXT, FILTER_CUBIC, filter_cubic) \ + EXTENSION(QCOM, FILTER_CUBIC_WEIGHTS, filter_cubic_weights) // Define extensions which must be supported. #define FOR_EACH_VK_MANDATORY_EXTENSION(EXTENSION_NAME) \ @@ -558,6 +559,11 @@ public: return extensions.filter_cubic; } + /// Returns true if the device supports VK_QCOM_filter_cubic_weights + bool IsQcomFilterCubicWeightsSupported() const { + return extensions.filter_cubic_weights; + } + /// Returns true if the device supports VK_EXT_line_rasterization. bool IsExtLineRasterizationSupported() const { return extensions.line_rasterization; From cf0628af466f1297c87e73b2bfe973a97f12dd04 Mon Sep 17 00:00:00 2001 From: lizzie Date: Tue, 7 Oct 2025 06:38:22 +0200 Subject: [PATCH 08/17] [compat] improve thread naming logic (#271) Signed-off-by: lizzie Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/271 Reviewed-by: MaranBr Reviewed-by: CamilleLaVey Co-authored-by: lizzie Co-committed-by: lizzie --- src/common/thread.cpp | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/common/thread.cpp b/src/common/thread.cpp index da1fa9b6c5..2de7465a22 100644 --- a/src/common/thread.cpp +++ b/src/common/thread.cpp @@ -1,6 +1,5 @@ // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later - // SPDX-FileCopyrightText: 2013 Dolphin Emulator Project // SPDX-FileCopyrightText: 2014 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -18,9 +17,8 @@ #else #if defined(__Bitrig__) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__) #include -#else -#include #endif +#include #include #endif #ifndef _WIN32 @@ -93,33 +91,35 @@ void SetCurrentThreadName(const char* name) { #else // !MSVC_VER, so must be POSIX threads // MinGW with the POSIX threading model does not support pthread_setname_np -#if !defined(_WIN32) || defined(_MSC_VER) void SetCurrentThreadName(const char* name) { + // See for reference + // https://gitlab.freedesktop.org/mesa/mesa/-/blame/main/src/util/u_thread.c?ref_type=heads#L75 #ifdef __APPLE__ pthread_setname_np(name); +#elif defined(__HAIKU__) + rename_thread(find_thread(NULL), name); #elif defined(__Bitrig__) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__) pthread_set_name_np(pthread_self(), name); #elif defined(__NetBSD__) pthread_setname_np(pthread_self(), "%s", (void*)name); -#elif defined(__linux__) - // Linux limits thread names to 15 characters and will outright reject any - // attempt to set a longer name with ERANGE. - std::string truncated(name, (std::min)(strlen(name), static_cast(15))); - if (int e = pthread_setname_np(pthread_self(), truncated.c_str())) { - errno = e; - LOG_ERROR(Common, "Failed to set thread name to '{}': {}", truncated, GetLastErrorMsg()); +#elif defined(__linux__) || defined(__CYGWIN__) || defined(__sun__) || defined(__glibc__) || defined(__managarm__) + int ret = pthread_setname_np(pthread_self(), name); + if (ret == ERANGE) { + // Linux limits thread names to 15 characters and will outright reject any + // attempt to set a longer name with ERANGE. + char buf[16]; + size_t const len = std::min(std::strlen(name), sizeof(buf) - 1); + std::memcpy(buf, name, len); + buf[len] = '\0'; + pthread_setname_np(pthread_self(), buf); } +#elif !defined(_WIN32) || defined(_MSC_VER) + // mingw stub + (void)name; #else pthread_setname_np(pthread_self(), name); #endif } -#endif - -#if defined(_WIN32) -void SetCurrentThreadName(const char* name) { - // Do Nothing on MingW -} -#endif #endif From 9acb6006b8586303304102deb42b4f7dc462fe53 Mon Sep 17 00:00:00 2001 From: Caio Oliveira Date: Wed, 8 Oct 2025 01:04:18 +0200 Subject: [PATCH 09/17] [ci] improve ccache and add support on Android (#2673) * disable PCH * fix missing headers after disabling PCH * add support to extra cmake flags on Android building * remove debug symbols on Release building (also fixing ccache on windows) Signed-off-by: Caio Oliveira Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2673 Reviewed-by: crueter Co-authored-by: Caio Oliveira Co-committed-by: Caio Oliveira --- CMakeLists.txt | 7 +++++++ docs/build/Android.md | 1 + externals/CMakeLists.txt | 14 ++++++++------ src/CMakeLists.txt | 12 +++--------- src/android/app/build.gradle.kts | 5 ++++- src/dynarmic/CMakeLists.txt | 12 ++++++------ src/dynarmic/src/dynarmic/backend/arm64/abi.h | 1 + src/dynarmic/src/dynarmic/common/assert.h | 6 ++++++ src/dynarmic/src/dynarmic/common/memory_pool.h | 4 ++++ src/dynarmic/src/dynarmic/ir/opt_passes.cpp | 1 + 10 files changed, 41 insertions(+), 22 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f436c0a183..1b69782a23 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -287,6 +287,13 @@ if (ANDROID) set(CMAKE_POLICY_VERSION_MINIMUM 3.5) # Workaround for Oboe endif() +# We need to downgrade debug info (/Zi -> /Z7) to use an older but more cacheable format +# See https://github.com/nanoant/CMakePCHCompiler/issues/21 +if(WIN32 AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) + string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") + string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}") +endif() + # Default to a Release build get_property(IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) if (NOT IS_MULTI_CONFIG AND NOT CMAKE_BUILD_TYPE) diff --git a/docs/build/Android.md b/docs/build/Android.md index c8ff3a3b1e..f511f71370 100644 --- a/docs/build/Android.md +++ b/docs/build/Android.md @@ -33,6 +33,7 @@ Eden by default will be cloned into - 4. Navigate to `eden/src/android`. 5. Then Build with `./gradlew assembleRelWithDebInfo`. 6. To build the optimised build use `./gradlew assembleGenshinSpoofRelWithDebInfo`. +7. You can pass extra variables to cmake via `-PYUZU_ANDROID_ARGS="-D..."` ### Script A convenience script for building is provided in `.ci/android/build.sh`. The built APK can be put into an `artifacts` directory via `.ci/android/package.sh`. On Windows, these must be done in the Git Bash or MinGW terminal. diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 434e6fb100..2da461fd5c 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -155,12 +155,14 @@ if (YUZU_USE_BUNDLED_SIRIT) AddJsonPackage(sirit-ci) else() AddJsonPackage(sirit) - if(MSVC AND USE_CCACHE AND sirit_ADDED) - get_target_property(_opts sirit COMPILE_OPTIONS) - list(FILTER _opts EXCLUDE REGEX "/Zi") - list(APPEND _opts "/Z7") - set_target_properties(siritobj PROPERTIES COMPILE_OPTIONS "${_opts}") - elseif(MSVC AND CXX_CLANG) + # Change to old-but-more-cacheable debug info on Windows + if (WIN32 AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) + get_target_property(sirit_opts sirit COMPILE_OPTIONS) + list(FILTER sirit_opts EXCLUDE REGEX "/Zi") + list(APPEND sirit_opts "/Z7") + set_target_properties(sirit PROPERTIES COMPILE_OPTIONS "${sirit_opts}") + endif() + if(MSVC AND CXX_CLANG) target_compile_options(siritobj PRIVATE -Wno-error=unused-command-line-argument) endif() endif() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 88470c4c42..0f3c5cfd4b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -101,15 +101,9 @@ if (MSVC AND NOT CXX_CLANG) ) endif() - if (USE_CCACHE OR YUZU_USE_PRECOMPILED_HEADERS) - # when caching, we need to use /Z7 to downgrade debug info to use an older but more cacheable format - # Precompiled headers are deleted if not using /Z7. See https://github.com/nanoant/CMakePCHCompiler/issues/21 - add_compile_options(/Z7) - # Avoid D9025 warning - string(REPLACE "/Zi" "" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") - string(REPLACE "/Zi" "" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}") - else() - add_compile_options(/Zi) + if (WIN32 AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) + string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") + string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}") endif() if (ARCHITECTURE_x86_64) diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts index c85da039cb..31db36199a 100644 --- a/src/android/app/build.gradle.kts +++ b/src/android/app/build.gradle.kts @@ -75,6 +75,8 @@ android { externalNativeBuild { cmake { + val extraCMakeArgs = (project.findProperty("YUZU_ANDROID_ARGS") as String?)?.split("\\s+".toRegex()) ?: emptyList() + arguments.addAll(listOf( "-DENABLE_QT=0", // Don't use QT "-DENABLE_SDL2=0", // Don't use SDL @@ -87,7 +89,8 @@ android { "-DCMAKE_EXPORT_COMPILE_COMMANDS=ON", "-DBUILD_TESTING=OFF", "-DYUZU_TESTS=OFF", - "-DDYNARMIC_TESTS=OFF" + "-DDYNARMIC_TESTS=OFF", + *extraCMakeArgs.toTypedArray() )) abiFilters("arm64-v8a") diff --git a/src/dynarmic/CMakeLists.txt b/src/dynarmic/CMakeLists.txt index 6b3308fb54..e5345ef458 100644 --- a/src/dynarmic/CMakeLists.txt +++ b/src/dynarmic/CMakeLists.txt @@ -25,11 +25,7 @@ option(DYNARMIC_IGNORE_ASSERTS "Ignore asserts" OFF) option(DYNARMIC_TESTS_USE_UNICORN "Enable fuzzing tests against unicorn" OFF) CMAKE_DEPENDENT_OPTION(DYNARMIC_USE_LLVM "Support disassembly of jitted x86_64 code using LLVM" OFF "NOT YUZU_DISABLE_LLVM" OFF) -if (PLATFORM_OPENBSD) - option(DYNARMIC_USE_PRECOMPILED_HEADERS "Use precompiled headers" OFF) -else() - option(DYNARMIC_USE_PRECOMPILED_HEADERS "Use precompiled headers" ON) -endif() +option(DYNARMIC_USE_PRECOMPILED_HEADERS "Use precompiled headers" OFF) option(DYNARMIC_INSTALL "Install dynarmic headers and CMake files" OFF) option(DYNARMIC_USE_BUNDLED_EXTERNALS "Use all bundled externals (useful when e.g. cross-compiling)" OFF) @@ -81,7 +77,6 @@ if (MSVC) /wd4592 # Symbol will be dynamically initialized (implementation limitation) /permissive- # Stricter C++ standards conformance /MP - /Zi /Zo /EHsc /Zc:externConstexpr # Allows external linkage for variables declared "extern constexpr", as the standard permits. @@ -91,6 +86,11 @@ if (MSVC) /bigobj # Increase number of sections in .obj files /DNOMINMAX) + if (WIN32 AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) + string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") + string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}") + endif() + if (DYNARMIC_WARNINGS_AS_ERRORS) list(APPEND DYNARMIC_CXX_FLAGS /WX) diff --git a/src/dynarmic/src/dynarmic/backend/arm64/abi.h b/src/dynarmic/src/dynarmic/backend/arm64/abi.h index ca7c9187db..635d64f062 100644 --- a/src/dynarmic/src/dynarmic/backend/arm64/abi.h +++ b/src/dynarmic/src/dynarmic/backend/arm64/abi.h @@ -14,6 +14,7 @@ #include #include "dynarmic/common/common_types.h" +#include "dynarmic/common/assert.h" #include #include "dynarmic/common/always_false.h" diff --git a/src/dynarmic/src/dynarmic/common/assert.h b/src/dynarmic/src/dynarmic/common/assert.h index 9973b8948d..0a3cb5331d 100644 --- a/src/dynarmic/src/dynarmic/common/assert.h +++ b/src/dynarmic/src/dynarmic/common/assert.h @@ -23,6 +23,12 @@ template } \ }()) #endif +#ifndef ASSERT_FALSE +#define ASSERT_FALSE(...) \ + ([&]() { \ + assert_terminate("false", __VA_ARGS__); \ + }()) +#endif #ifndef ASSERT #define ASSERT(_a_) ASSERT_MSG(_a_, "") diff --git a/src/dynarmic/src/dynarmic/common/memory_pool.h b/src/dynarmic/src/dynarmic/common/memory_pool.h index c99316e107..d0a5223db3 100644 --- a/src/dynarmic/src/dynarmic/common/memory_pool.h +++ b/src/dynarmic/src/dynarmic/common/memory_pool.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + /* This file is part of the dynarmic project. * Copyright (c) 2016 MerryMage * SPDX-License-Identifier: 0BSD @@ -6,6 +9,7 @@ #pragma once #include +#include #include namespace Dynarmic::Common { diff --git a/src/dynarmic/src/dynarmic/ir/opt_passes.cpp b/src/dynarmic/src/dynarmic/ir/opt_passes.cpp index e9175f0e6b..25afde9b5d 100644 --- a/src/dynarmic/src/dynarmic/ir/opt_passes.cpp +++ b/src/dynarmic/src/dynarmic/ir/opt_passes.cpp @@ -6,6 +6,7 @@ * SPDX-License-Identifier: 0BSD */ +#include #include #include From acd7d792a34a809cf4e7f9270b3cee605fb6c35e Mon Sep 17 00:00:00 2001 From: Gamer64 Date: Wed, 8 Oct 2025 02:08:13 +0200 Subject: [PATCH 10/17] [hle] Stubbed QueryLastPlayTime (#389) Ported from Torzu, made by Jarrod Norwell. Co-authored-by: Jarrod Norwell Co-authored-by: Gamer64 <76565986+Gamer64ytb@users.noreply.github.com> Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/389 Reviewed-by: crueter Reviewed-by: MaranBr Co-authored-by: Gamer64 Co-committed-by: Gamer64 --- src/core/hle/service/ns/query_service.cpp | 14 +++++++++++++- src/core/hle/service/ns/query_service.h | 8 ++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/core/hle/service/ns/query_service.cpp b/src/core/hle/service/ns/query_service.cpp index 1384005415..a4632cb6c8 100644 --- a/src/core/hle/service/ns/query_service.cpp +++ b/src/core/hle/service/ns/query_service.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -29,7 +32,7 @@ IQueryService::IQueryService(Core::System& system_) : ServiceFramework{system_, {14, nullptr, "QueryRecentlyPlayedApplication"}, {15, nullptr, "GetRecentlyPlayedApplicationUpdateEvent"}, {16, nullptr, "QueryApplicationPlayStatisticsByUserAccountIdForSystemV0"}, - {17, nullptr, "QueryLastPlayTime"}, + {17, D<&IQueryService::QueryLastPlayTime>, "QueryLastPlayTime"}, {18, nullptr, "QueryApplicationPlayStatisticsForSystem"}, {19, nullptr, "QueryApplicationPlayStatisticsByUserAccountIdForSystem"}, }; @@ -53,4 +56,13 @@ Result IQueryService::QueryPlayStatisticsByApplicationIdAndUserAccountId( R_SUCCEED(); } +Result IQueryService::QueryLastPlayTime( + Out out_entries, u8 unknown, + OutArray out_last_play_times, + InArray application_ids) { + *out_entries = 1; + *out_last_play_times = {}; + R_SUCCEED(); +} + } // namespace Service::NS diff --git a/src/core/hle/service/ns/query_service.h b/src/core/hle/service/ns/query_service.h index c4c82b752e..ba1cddd4ca 100644 --- a/src/core/hle/service/ns/query_service.h +++ b/src/core/hle/service/ns/query_service.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -23,6 +26,8 @@ struct PlayStatistics { }; static_assert(sizeof(PlayStatistics) == 0x28, "PlayStatistics is an invalid size"); +struct LastPlayTime {}; + class IQueryService final : public ServiceFramework { public: explicit IQueryService(Core::System& system_); @@ -31,6 +36,9 @@ public: private: Result QueryPlayStatisticsByApplicationIdAndUserAccountId( Out out_play_statistics, bool unknown, u64 application_id, Uid account_id); + Result QueryLastPlayTime(Out out_entries, u8 unknown, + OutArray out_last_play_times, + InArray application_ids); }; } // namespace Service::NS From db65f107689f853141fd2cf4cb7b6b55b24aff3e Mon Sep 17 00:00:00 2001 From: Ribbit Date: Wed, 8 Oct 2025 04:01:24 +0200 Subject: [PATCH 11/17] [vk] Unify RAII in Vulkan (#2679) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR consolidates Vulkan RAII on video_core/vulkan_common/vulkan_wrapper.h’s vk::Handle and remove the unused duplicate src/video_core/vulkan_common/vulkan_raii.h, reducing confusion and maintenance. Swapchain now uses RAII‑managed per‑image semaphores and clears them in Destroy(), providing correct present synchronization and automatic cleanup. Expected result: simpler lifetimes, fewer leak risks, and more stable presentation with negligible overhead. Co-authored-by: Ribbit Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2679 Reviewed-by: MaranBr Reviewed-by: CamilleLaVey Co-authored-by: Ribbit Co-committed-by: Ribbit --- .../features/settings/model/BooleanSetting.kt | 2 - .../settings/model/view/SettingsItem.kt | 8 +- .../settings/ui/SettingsFragmentPresenter.kt | 1 - .../app/src/main/res/values-ar/strings.xml | 2 - .../app/src/main/res/values-ckb/strings.xml | 2 - .../app/src/main/res/values-cs/strings.xml | 2 - .../app/src/main/res/values-de/strings.xml | 2 - .../app/src/main/res/values-es/strings.xml | 2 - .../app/src/main/res/values-fa/strings.xml | 2 - .../app/src/main/res/values-fr/strings.xml | 2 - .../app/src/main/res/values-he/strings.xml | 2 - .../app/src/main/res/values-hu/strings.xml | 2 - .../app/src/main/res/values-id/strings.xml | 2 - .../app/src/main/res/values-it/strings.xml | 2 - .../app/src/main/res/values-ja/strings.xml | 2 - .../app/src/main/res/values-ko/strings.xml | 2 - .../app/src/main/res/values-nb/strings.xml | 2 - .../app/src/main/res/values-pl/strings.xml | 2 - .../src/main/res/values-pt-rBR/strings.xml | 2 - .../src/main/res/values-pt-rPT/strings.xml | 2 - .../app/src/main/res/values-ru/strings.xml | 2 - .../app/src/main/res/values-sr/strings.xml | 2 - .../app/src/main/res/values-uk/strings.xml | 2 - .../app/src/main/res/values-vi/strings.xml | 2 - .../src/main/res/values-zh-rCN/strings.xml | 2 - .../src/main/res/values-zh-rTW/strings.xml | 2 - .../app/src/main/res/values/strings.xml | 2 - src/common/settings.h | 1 - src/qt_common/shared_translation.cpp | 7 +- .../renderer_vulkan/renderer_vulkan.cpp | 9 - .../renderer_vulkan/renderer_vulkan.h | 10 +- .../renderer_vulkan/vk_swapchain.cpp | 1 + src/video_core/vulkan_common/vulkan_raii.h | 231 ------------------ .../vulkan_common/vulkan_wrapper.cpp | 11 +- src/video_core/vulkan_common/vulkan_wrapper.h | 66 ++--- 35 files changed, 42 insertions(+), 353 deletions(-) delete mode 100644 src/video_core/vulkan_common/vulkan_raii.h 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 638e1101db..b26fb1dec5 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 @@ -51,7 +51,6 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting { SOC_OVERLAY_BACKGROUND("soc_overlay_background"), - ENABLE_RAII("enable_raii"), FRAME_INTERPOLATION("frame_interpolation"), // FRAME_SKIPPING("frame_skipping"), @@ -71,7 +70,6 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting { DEBUG_FLUSH_BY_LINE("flush_line"), USE_LRU_CACHE("use_lru_cache"); - external fun isRaiiEnabled(): Boolean // external fun isFrameSkippingEnabled(): Boolean external fun isFrameInterpolationEnabled(): Boolean 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 5f7f7a43f9..ebc726225a 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 @@ -229,13 +229,6 @@ abstract class SettingsItem( override fun reset() = BooleanSetting.USE_DOCKED_MODE.reset() } - put( - SwitchSetting( - BooleanSetting.ENABLE_RAII, - titleId = R.string.enable_raii, - descriptionId = R.string.enable_raii_description - ) - ) put( SwitchSetting( BooleanSetting.FRAME_INTERPOLATION, @@ -833,3 +826,4 @@ abstract class SettingsItem( } } } + 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 715baec72f..0d882a7f01 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 @@ -462,7 +462,6 @@ class SettingsFragmentPresenter( add(IntSetting.RENDERER_SAMPLE_SHADING_FRACTION.key) add(HeaderSetting(R.string.veil_renderer)) - add(BooleanSetting.ENABLE_RAII.key) add(BooleanSetting.RENDERER_EARLY_RELEASE_FENCES.key) add(IntSetting.DMA_ACCURACY.key) add(BooleanSetting.BUFFER_REORDER_DISABLE.key) diff --git a/src/android/app/src/main/res/values-ar/strings.xml b/src/android/app/src/main/res/values-ar/strings.xml index 4b58d5f851..388afd88cd 100644 --- a/src/android/app/src/main/res/values-ar/strings.xml +++ b/src/android/app/src/main/res/values-ar/strings.xml @@ -64,8 +64,6 @@ امتدادات GPU العارض - RAII - طريقة لإدارة الموارد تلقائيًا في فولكان تضمن الإفراج الصحيح عن الموارد عندما لا تكون هناك حاجة إليها، ولكن قد تسبب تعطل الألعاب المجمعة. وحدة المعالجة المركزية والذاكرة حجاب عدن إعدادات تجريبية لتحسين الأداء والقدرة. قد تسبب هذه الإعدادات شاشات سوداء أو مشاكل أخرى في اللعبة. diff --git a/src/android/app/src/main/res/values-ckb/strings.xml b/src/android/app/src/main/res/values-ckb/strings.xml index 25fcf6acef..2ab4af16ad 100644 --- a/src/android/app/src/main/res/values-ckb/strings.xml +++ b/src/android/app/src/main/res/values-ckb/strings.xml @@ -65,8 +65,6 @@ پاشکۆکانی GPU رێندرەر - RAII - ڕێگایەکی بەڕێوەبردنی سەرچاوەکان بە خۆکار لە ڤولکان کە دڵنیای دەکاتەوە لە ئازادکردنی گونجاوی سەرچاوەکان کاتێک کە چیتر پێویستیان نییە، بەڵام لەوانەیە ببێتە هۆی کەوتنی یارییە کۆکراوەکان. CPU و بیرگە حجاب عدن ڕێکخستنە تاقیکارییەکان بۆ باشترکردنی کارایی و توانا. ئەم ڕێکخستنانە لەوانەیە ببێتە هۆی شاشە ڕەشەکان یان کێشەیتری یاری. diff --git a/src/android/app/src/main/res/values-cs/strings.xml b/src/android/app/src/main/res/values-cs/strings.xml index 8d7e274464..ad8f89ffc5 100644 --- a/src/android/app/src/main/res/values-cs/strings.xml +++ b/src/android/app/src/main/res/values-cs/strings.xml @@ -64,8 +64,6 @@ Rozšíření GPU Renderer - RAII - Metoda automatické správy prostředků ve Vulkanu, která zajišťuje správné uvolnění prostředků, když již nejsou potřeba, ale může způsobit pády v balených hrách. CPU a paměť Edenův závoj Experimentální nastavení pro zlepšení výkonu a schopností. Tato nastavení mohou způsobit černé obrazovky nebo další herní problémy. diff --git a/src/android/app/src/main/res/values-de/strings.xml b/src/android/app/src/main/res/values-de/strings.xml index 146fa60fed..cf12a2244f 100644 --- a/src/android/app/src/main/res/values-de/strings.xml +++ b/src/android/app/src/main/res/values-de/strings.xml @@ -65,8 +65,6 @@ GPU-Erweiterungen Renderer - RAII - Eine Methode zur automatischen Ressourcenverwaltung in Vulkan, die eine ordnungsgemäße Freigabe von Ressourcen gewährleistet, wenn sie nicht mehr benötigt werden, aber bei gebündelten Spielen Abstürze verursachen kann. CPU und Speicher Edens Schleier Experimentelle Einstellungen zur Verbesserung der Leistung und Funktionalität. Diese Einstellungen können schwarze Bildschirme oder andere Spielprobleme verursachen. diff --git a/src/android/app/src/main/res/values-es/strings.xml b/src/android/app/src/main/res/values-es/strings.xml index 28a93f005b..eff563b7ea 100644 --- a/src/android/app/src/main/res/values-es/strings.xml +++ b/src/android/app/src/main/res/values-es/strings.xml @@ -65,8 +65,6 @@ Extensiones de GPU Renderizador - RAII - Un método de gestión automática de recursos en Vulkan que garantiza la liberación adecuada de recursos cuando ya no son necesarios, pero puede causar fallos en juegos empaquetados. CPU y memoria Velo de Edén Configuraciones experimentales para mejorar el rendimiento y la capacidad. Estas configuraciones pueden causar pantallas negras u otros problemas en el juego. diff --git a/src/android/app/src/main/res/values-fa/strings.xml b/src/android/app/src/main/res/values-fa/strings.xml index b30f67292a..205662b182 100644 --- a/src/android/app/src/main/res/values-fa/strings.xml +++ b/src/android/app/src/main/res/values-fa/strings.xml @@ -65,8 +65,6 @@ افزونه‌های GPU رندرر - RAII - روشی برای مدیریت خودکار منابع در ولکان که تضمین می‌کند منابع به درستی آزاد شوند وقتی دیگر مورد نیاز نیستند، اما ممکن است باعث کرش شدن بازی‌های بسته‌بندی شده شود. پردازنده و حافظه پرده عدن تنظیمات آزمایشی برای بهبود عملکرد و قابلیت. این تنظیمات ممکن است باعث نمایش صفحه سیاه یا سایر مشکلات بازی شود. diff --git a/src/android/app/src/main/res/values-fr/strings.xml b/src/android/app/src/main/res/values-fr/strings.xml index f4c741aecc..12c93fd76c 100644 --- a/src/android/app/src/main/res/values-fr/strings.xml +++ b/src/android/app/src/main/res/values-fr/strings.xml @@ -65,8 +65,6 @@ Extensions GPU Rendu - RAII - Une méthode de gestion automatique des ressources dans Vulkan qui assure la libération correcte des ressources lorsqu\'elles ne sont plus nécessaires, mais peut provoquer des plantages dans les jeux regroupés. CPU et mémoire Voile d\'Eden Paramètres expérimentaux pour améliorer les performances et les capacités. Ces paramètres peuvent causer des écrans noirs ou d\'autres problèmes de jeu. diff --git a/src/android/app/src/main/res/values-he/strings.xml b/src/android/app/src/main/res/values-he/strings.xml index 6c5a877a66..9efaf05085 100644 --- a/src/android/app/src/main/res/values-he/strings.xml +++ b/src/android/app/src/main/res/values-he/strings.xml @@ -65,8 +65,6 @@ הרחבות GPU רנדרר - RAII - שיטה לניהול אוטומטי של משאבים ב-Vulkan המבטיחה שחרור נכון של משאבים כאשר הם כבר לא נחוצים, אך עלולה לגרום לקריסות במשחקים מאוגדים. מעבד וזיכרון עדן וייל הגדרות ניסיוניות לשיפור ביצועים ויכולות. הגדרות אלו עלולות לגרום למסכים שחורים או לבעיות אחרות במשחק. diff --git a/src/android/app/src/main/res/values-hu/strings.xml b/src/android/app/src/main/res/values-hu/strings.xml index 6c4f428086..20b222818f 100644 --- a/src/android/app/src/main/res/values-hu/strings.xml +++ b/src/android/app/src/main/res/values-hu/strings.xml @@ -65,8 +65,6 @@ GPU kiterjesztések Megjelenítő - RAII - A Vulkan erőforrás-kezelési módszere, amely biztosítja az erőforrások megfelelő felszabadítását, ha már nincs rájuk szükség, de csomagolt játékok összeomlását okozhatja. CPU és memória Eden Fátyla Kísérleti beállítások a teljesítmény és képesség javításához. Ezek a beállítások fekete képernyőket vagy más játékproblémákat okozhatnak. diff --git a/src/android/app/src/main/res/values-id/strings.xml b/src/android/app/src/main/res/values-id/strings.xml index 8e89132815..83db153bec 100644 --- a/src/android/app/src/main/res/values-id/strings.xml +++ b/src/android/app/src/main/res/values-id/strings.xml @@ -65,8 +65,6 @@ Ekstensi GPU Renderer - RAII - Metode manajemen sumber daya otomatis di Vulkan yang memastikan pelepasan sumber daya yang tepat ketika tidak lagi diperlukan, tetapi dapat menyebabkan crash pada game yang dibundel. CPU dan Memori Eden\'s Veil Pengaturan eksperimental untuk meningkatkan kinerja dan kemampuan. Pengaturan ini dapat menyebabkan layar hitam atau masalah game lainnya. diff --git a/src/android/app/src/main/res/values-it/strings.xml b/src/android/app/src/main/res/values-it/strings.xml index 339bae8883..7d5c118441 100644 --- a/src/android/app/src/main/res/values-it/strings.xml +++ b/src/android/app/src/main/res/values-it/strings.xml @@ -65,8 +65,6 @@ Estensioni GPU Renderer - RAII - Un metodo di gestione automatica delle risorse in Vulkan che garantisce il corretto rilascio delle risorse quando non sono più necessarie, ma può causare crash nei giochi in bundle. CPU e Memoria Velo di Eden Impostazioni sperimentali per migliorare prestazioni e capacità. Queste impostazioni possono causare schermate nere o altri problemi di gioco. diff --git a/src/android/app/src/main/res/values-ja/strings.xml b/src/android/app/src/main/res/values-ja/strings.xml index 4fc9f135e4..0d0c37c78f 100644 --- a/src/android/app/src/main/res/values-ja/strings.xml +++ b/src/android/app/src/main/res/values-ja/strings.xml @@ -65,8 +65,6 @@ GPU拡張機能 レンダラー - RAII - Vulkanにおける自動リソース管理の方法で、不要になったリソースを適切に解放しますが、バンドルされたゲームでクラッシュを引き起こす可能性があります。 CPUとメモリ エデンのベール パフォーマンスと機能を向上させる実験的な設定。これらの設定は黒画面やその他のゲームの問題を引き起こす可能性があります。 diff --git a/src/android/app/src/main/res/values-ko/strings.xml b/src/android/app/src/main/res/values-ko/strings.xml index ebad3409d7..ff0af4fc43 100644 --- a/src/android/app/src/main/res/values-ko/strings.xml +++ b/src/android/app/src/main/res/values-ko/strings.xml @@ -65,8 +65,6 @@ GPU 확장 기능 렌더러 - RAII - Vulkan에서 자동 리소스 관리를 위한 방법으로, 더 이상 필요하지 않은 리소스를 적절히 해제하지만 번들된 게임에서 충돌을 일으킬 수 있습니다. CPU 및 메모리 에덴의 베일 성능 및 기능을 향상시키기 위한 실험적 설정. 이 설정은 검은 화면 또는 기타 게임 문제를 일으킬 수 있습니다. diff --git a/src/android/app/src/main/res/values-nb/strings.xml b/src/android/app/src/main/res/values-nb/strings.xml index 4a5f6f2efb..313d8797c3 100644 --- a/src/android/app/src/main/res/values-nb/strings.xml +++ b/src/android/app/src/main/res/values-nb/strings.xml @@ -65,8 +65,6 @@ GPU-utvidelser Renderer - RAII - En metode for automatisk ressurshåndtering i Vulkan som sikrer riktig frigjøring av ressurser når de ikke lenger trengs, men kan føre til krasj i bundlede spill. CPU og minne Edens slør Eksperimentelle innstillinger for å forbedre ytelse og funksjonalitet. Disse innstillingene kan forårsake svarte skjermer eller andre spillproblemer. diff --git a/src/android/app/src/main/res/values-pl/strings.xml b/src/android/app/src/main/res/values-pl/strings.xml index d1bc789aa9..4ebe24e1c9 100644 --- a/src/android/app/src/main/res/values-pl/strings.xml +++ b/src/android/app/src/main/res/values-pl/strings.xml @@ -65,8 +65,6 @@ Rozszerzenia GPU Renderer - RAII - Metoda automatycznego zarządzania zasobami w Vulkanie, która zapewnia prawidłowe zwalnianie zasobów, gdy nie są już potrzebne, ale może powodować awarie w pakietowych grach. Procesor i pamięć Zasłona Edenu Eksperymentalne ustawienia poprawiające wydajność i możliwości. Te ustawienia mogą powodować czarne ekrany lub inne problemy z grą. diff --git a/src/android/app/src/main/res/values-pt-rBR/strings.xml b/src/android/app/src/main/res/values-pt-rBR/strings.xml index bad95a18e5..2f5f4c4b5b 100644 --- a/src/android/app/src/main/res/values-pt-rBR/strings.xml +++ b/src/android/app/src/main/res/values-pt-rBR/strings.xml @@ -65,8 +65,6 @@ Extensões da GPU Renderizador - RAII - Um método de gerenciamento automático de recursos no Vulkan que garante a liberação adequada de recursos quando não são mais necessários, mas pode causar falhas em jogos empacotados. CPU e Memória Véu do Éden Configurações experimentais para melhorar desempenho e capacidade. Essas configurações podem causar telas pretas ou outros problemas no jogo. diff --git a/src/android/app/src/main/res/values-pt-rPT/strings.xml b/src/android/app/src/main/res/values-pt-rPT/strings.xml index 0b57eebab6..084fe1c82d 100644 --- a/src/android/app/src/main/res/values-pt-rPT/strings.xml +++ b/src/android/app/src/main/res/values-pt-rPT/strings.xml @@ -65,8 +65,6 @@ Extensões da GPU Renderizador - RAII - Um método de gestão automática de recursos no Vulkan que garante a libertação adequada de recursos quando já não são necessários, mas pode causar falhas em jogos empacotados. CPU e Memória Véu do Éden Definições experimentais para melhorar o desempenho e capacidade. Estas definições podem causar ecrãs pretos ou outros problemas no jogo. diff --git a/src/android/app/src/main/res/values-ru/strings.xml b/src/android/app/src/main/res/values-ru/strings.xml index 53f49b91cb..0938b9c18f 100644 --- a/src/android/app/src/main/res/values-ru/strings.xml +++ b/src/android/app/src/main/res/values-ru/strings.xml @@ -72,8 +72,6 @@ Настройки в Покров Эдема являются экспериментальными и могут вызывать проблемы. Если ваша игра не запускается, отключите все расширения. В разработке: Пропуск кадров Включите или отключите пропуск кадров для повышения производительности за счет уменьшения количества отображаемых кадров. Эта функция находится в разработке и будет включена в будущих версиях. - RAII - Метод автоматического управления ресурсами в Vulkan, который обеспечивает правильное освобождение ресурсов при их ненадобности, но может вызывать сбои в бандл-играх. Улучшенная синхронизация кадров Обеспечивает плавную и стабильную подачу кадров за счет синхронизации их времени, уменьшая подтормаживания и неравномерную анимацию. Идеально для игр с нестабильным временем кадров или микро-подтормаживаниями во время игры. Ранний релиз ограждений diff --git a/src/android/app/src/main/res/values-sr/strings.xml b/src/android/app/src/main/res/values-sr/strings.xml index 3f2de72f8b..35ef07f3a6 100644 --- a/src/android/app/src/main/res/values-sr/strings.xml +++ b/src/android/app/src/main/res/values-sr/strings.xml @@ -81,8 +81,6 @@ Побољшава текстуру и руковање међуспремника, као и преводилачки слој Маквелл. Подржани од стране неких Вулкана 1.1 ГПУ-а и сви Вулкан 1.2+ ГПУ. Рендерер - RAII - Метод аутоматског управљања ресурсима у Vulkan-у који осигурава правилно ослобађање ресурса када више нису потребни, али може изазвати падове у пакованим играма. Побољшани оквирни пејсинг Осигурава глатку и доследан испоруку оквира синхронизацијом времена између оквира, смањење муцања и неуједначене анимације. Идеално за игре које доживљавају временски оквир нестабилност или микро-штитнике током играња. Ranije oslobađanje ograda diff --git a/src/android/app/src/main/res/values-uk/strings.xml b/src/android/app/src/main/res/values-uk/strings.xml index b5eee4992c..b22c30999b 100644 --- a/src/android/app/src/main/res/values-uk/strings.xml +++ b/src/android/app/src/main/res/values-uk/strings.xml @@ -70,8 +70,6 @@ Експериментальні налаштування для покращення продуктивності та сумісності. Ці налаштування можуть викликати збої, зокрема чорний екран. Експериментальні налаштування Налаштування Завіси Eden є експериментальними та можуть спричинити проблеми. Якщо ваша гра не запускається — вимкніть усі розширення. - RAII - Метод автоматичного керування ресурсами у Vulkan, який забезпечує правильне звільнення ресурсів після завершення їх використання, проте він може спричинити збої в ігрових збірниках. В розробці: Пропуск кадрів Увімкніть або вимкніть пропуск кадрів для покращення продуктивності за рахунок зменшення кількості візуалізованих кадрів. Ця функція ще розробляється та буде доступна у майбутніх версіях. Покращена синхронізація кадрів diff --git a/src/android/app/src/main/res/values-vi/strings.xml b/src/android/app/src/main/res/values-vi/strings.xml index 1a34509f5c..171b4ea116 100644 --- a/src/android/app/src/main/res/values-vi/strings.xml +++ b/src/android/app/src/main/res/values-vi/strings.xml @@ -65,8 +65,6 @@ Tiện ích mở rộng GPU Trình kết xuất - RAII - Phương pháp quản lý tài nguyên tự động trong Vulkan đảm bảo giải phóng tài nguyên đúng cách khi không còn cần thiết, nhưng có thể gây ra sự cố trong các trò chơi được đóng gói. CPU và Bộ nhớ Mành che của Eden Cài đặt thử nghiệm để cải thiện hiệu suất và khả năng. Những cài đặt này có thể gây ra màn hình đen hoặc các vấn đề khác trong trò chơi. diff --git a/src/android/app/src/main/res/values-zh-rCN/strings.xml b/src/android/app/src/main/res/values-zh-rCN/strings.xml index 6fa40b8727..daa2143beb 100644 --- a/src/android/app/src/main/res/values-zh-rCN/strings.xml +++ b/src/android/app/src/main/res/values-zh-rCN/strings.xml @@ -65,8 +65,6 @@ GPU扩展 渲染器 - RAII - Vulkan中的一种自动资源管理方法,确保在不再需要时正确释放资源,但可能导致捆绑游戏崩溃。 CPU和内存 伊甸之幕 实验性设置以提高性能和能力。这些设置可能会导致黑屏或其他游戏问题。 diff --git a/src/android/app/src/main/res/values-zh-rTW/strings.xml b/src/android/app/src/main/res/values-zh-rTW/strings.xml index b73ec8ccaa..67f3f268a8 100644 --- a/src/android/app/src/main/res/values-zh-rTW/strings.xml +++ b/src/android/app/src/main/res/values-zh-rTW/strings.xml @@ -65,8 +65,6 @@ GPU擴充功能 渲染器 - RAII - Vulkan中的一種自動資源管理方法,確保在不再需要時正確釋放資源,但可能導致捆綁遊戲崩潰。 CPU與記憶體 伊甸之幕 實驗性設定以提高效能和能力。這些設定可能會導致黑屏或其他遊戲問題。 diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index cff0bab2b5..00206a5df5 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -109,8 +109,6 @@ The intensity of the sample shading pass. Higher values improve quality more but also reduce performance to a greater extent. Renderer - RAII - A method of automatic resource management in Vulkan that ensures proper release of resources when they are no longer needed, but may cause crashes in bundled games. Enhanced Frame Pacing Ensures smooth and consistent frame delivery by synchronizing the timing between frames, reducing stuttering and uneven animation. Ideal for games that experience frame timing instability or micro-stutters during gameplay. Release Fences Early diff --git a/src/common/settings.h b/src/common/settings.h index 59e75d3ee0..c6b52f7ba3 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -336,7 +336,6 @@ struct Values { "shader_backend", Category::Renderer, Specialization::RuntimeList}; SwitchableSetting vulkan_device{linkage, 0, "vulkan_device", Category::Renderer, Specialization::RuntimeList}; - SwitchableSetting enable_raii{linkage, false, "enable_raii", Category::Renderer}; #ifdef __ANDROID__ SwitchableSetting frame_interpolation{linkage, true, "frame_interpolation", Category::Renderer, Specialization::RuntimeList}; diff --git a/src/qt_common/shared_translation.cpp b/src/qt_common/shared_translation.cpp index f926f506e7..0b40ca9904 100644 --- a/src/qt_common/shared_translation.cpp +++ b/src/qt_common/shared_translation.cpp @@ -320,12 +320,6 @@ std::unique_ptr InitializeTranslations(QObject* parent) tr("Improves rendering of transparency effects in specific games.")); // Renderer (Extensions) - INSERT(Settings, - enable_raii, - tr("RAII"), - tr("A method of automatic resource management in Vulkan " - "that ensures proper release of resources " - "when they are no longer needed, but may cause crashes in bundled games.")); INSERT(Settings, dyna_state, tr("Extended Dynamic State"), @@ -721,3 +715,4 @@ std::unique_ptr ComboboxEnumeration(QObject* parent) return translations; } } // namespace ConfigurationShared + diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 6f3a0e4cd1..e6e72cdca7 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -164,15 +164,6 @@ try PresentFiltersForAppletCapture) , rasterizer(render_window, gpu, device_memory, device, memory_allocator, state_tracker, scheduler) { - // Initialize RAII wrappers after creating the main objects - if (Settings::values.enable_raii.GetValue()) { - managed_instance = MakeManagedInstance(instance, dld); - if (Settings::values.renderer_debug) { - managed_debug_messenger = MakeManagedDebugUtilsMessenger(debug_messenger, instance, dld); - } - managed_surface = MakeManagedSurface(surface, instance, dld); - } - if (Settings::values.renderer_force_max_clock.GetValue() && device.ShouldBoostClocks()) { turbo_mode.emplace(instance, dld); scheduler.RegisterOnSubmit([this] { turbo_mode->QueueSubmitted(); }); diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h index c1e6d5db7f..4fb88b29de 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.h +++ b/src/video_core/renderer_vulkan/renderer_vulkan.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -20,7 +23,6 @@ #include "video_core/vulkan_common/vulkan_device.h" #include "video_core/vulkan_common/vulkan_memory_allocator.h" #include "video_core/vulkan_common/vulkan_wrapper.h" -#include "video_core/vulkan_common/vulkan_raii.h" namespace Core::Memory { class Memory; @@ -78,16 +80,10 @@ private: // Keep original handles for compatibility with existing code vk::Instance instance; - // RAII wrapper for instance - ManagedInstance managed_instance; vk::DebugUtilsMessenger debug_messenger; - // RAII wrapper for debug messenger - ManagedDebugUtilsMessenger managed_debug_messenger; vk::SurfaceKHR surface; - // RAII wrapper for surface - ManagedSurface managed_surface; Device device; MemoryAllocator memory_allocator; diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp index 3b35e28c05..fdd2de2379 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.cpp +++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp @@ -351,6 +351,7 @@ void Swapchain::CreateSemaphores() { void Swapchain::Destroy() { frame_index = 0; present_semaphores.clear(); + render_semaphores.clear(); swapchain.reset(); } diff --git a/src/video_core/vulkan_common/vulkan_raii.h b/src/video_core/vulkan_common/vulkan_raii.h deleted file mode 100644 index cf5e268b68..0000000000 --- a/src/video_core/vulkan_common/vulkan_raii.h +++ /dev/null @@ -1,231 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2025 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include -#include -#include - -#include "common/logging/log.h" - -#include "video_core/vulkan_common/vulkan_wrapper.h" - -namespace Vulkan { - -/** - * RAII wrapper for Vulkan resources. - * Automatically manages the lifetime of Vulkan objects using RAII principles. - */ -template -class VulkanRaii { -public: - using DeleterFunc = std::function; - - // Default constructor - creates a null handle - VulkanRaii() : handle{}, deleter{}, dispatch{} {} - - // Constructor with handle and deleter - VulkanRaii(T handle_, DeleterFunc deleter_, const Dispatch& dispatch_, const char* resource_name = "Vulkan resource") - : handle{handle_}, deleter{std::move(deleter_)}, dispatch{dispatch_} { - LOG_DEBUG(Render_Vulkan, "RAII wrapper created for {}", resource_name); - } - - // Move constructor - VulkanRaii(VulkanRaii&& other) noexcept - : handle{std::exchange(other.handle, VK_NULL_HANDLE)}, - deleter{std::move(other.deleter)}, - dispatch{other.dispatch} { - } - - // Move assignment - VulkanRaii& operator=(VulkanRaii&& other) noexcept { - if (this != &other) { - cleanup(); - handle = std::exchange(other.handle, VK_NULL_HANDLE); - deleter = std::move(other.deleter); - dispatch = other.dispatch; - } - return *this; - } - - // Destructor - automatically cleans up the resource - ~VulkanRaii() { - cleanup(); - } - - // Disallow copying - VulkanRaii(const VulkanRaii&) = delete; - VulkanRaii& operator=(const VulkanRaii&) = delete; - - // Get the underlying handle - T get() const noexcept { - return handle; - } - - // Check if the handle is valid - bool valid() const noexcept { - return handle != VK_NULL_HANDLE; - } - - // Release ownership of the handle without destroying it - T release() noexcept { - return std::exchange(handle, VK_NULL_HANDLE); - } - - // Reset the handle (destroying the current one if it exists) - void reset(T new_handle = VK_NULL_HANDLE, DeleterFunc new_deleter = {}) { - cleanup(); - handle = new_handle; - deleter = std::move(new_deleter); - } - - // Implicit conversion to handle type - operator T() const noexcept { - return handle; - } - - // Dereference operator for pointer-like access - T operator->() const noexcept { - return handle; - } - -private: - // Optimized cleanup function - void cleanup() noexcept { - if (handle != VK_NULL_HANDLE && deleter) { - deleter(handle, dispatch); - handle = VK_NULL_HANDLE; - } - } - - T handle; - DeleterFunc deleter; - Dispatch dispatch; -}; - -// Common type aliases for Vulkan RAII wrappers with clearer names -using ManagedInstance = VulkanRaii; -using ManagedDevice = VulkanRaii; -using ManagedSurface = VulkanRaii; -using ManagedSwapchain = VulkanRaii; -using ManagedCommandPool = VulkanRaii; -using ManagedBuffer = VulkanRaii; -using ManagedImage = VulkanRaii; -using ManagedImageView = VulkanRaii; -using ManagedSampler = VulkanRaii; -using ManagedShaderModule = VulkanRaii; -using ManagedPipeline = VulkanRaii; -using ManagedPipelineLayout = VulkanRaii; -using ManagedDescriptorSetLayout = VulkanRaii; -using ManagedDescriptorPool = VulkanRaii; -using ManagedSemaphore = VulkanRaii; -using ManagedFence = VulkanRaii; -using ManagedDebugUtilsMessenger = VulkanRaii; - -// Helper functions to create RAII wrappers - -/** - * Creates an RAII wrapper for a Vulkan instance - */ -inline ManagedInstance MakeManagedInstance(const vk::Instance& instance, const vk::InstanceDispatch& dispatch) { - auto deleter = [](VkInstance handle, const vk::InstanceDispatch& dld) { - dld.vkDestroyInstance(handle, nullptr); - }; - return ManagedInstance(*instance, deleter, dispatch, "VkInstance"); -} - -/** - * Creates an RAII wrapper for a Vulkan device - */ -inline ManagedDevice MakeManagedDevice(const vk::Device& device, const vk::DeviceDispatch& dispatch) { - auto deleter = [](VkDevice handle, const vk::DeviceDispatch& dld) { - dld.vkDestroyDevice(handle, nullptr); - }; - return ManagedDevice(*device, deleter, dispatch, "VkDevice"); -} - -/** - * Creates an RAII wrapper for a Vulkan surface - */ -inline ManagedSurface MakeManagedSurface(const vk::SurfaceKHR& surface, const vk::Instance& instance, const vk::InstanceDispatch& dispatch) { - auto deleter = [instance_ptr = *instance](VkSurfaceKHR handle, const vk::InstanceDispatch& dld) { - dld.vkDestroySurfaceKHR(instance_ptr, handle, nullptr); - }; - return ManagedSurface(*surface, deleter, dispatch, "VkSurfaceKHR"); -} - -/** - * Creates an RAII wrapper for a Vulkan debug messenger - */ -inline ManagedDebugUtilsMessenger MakeManagedDebugUtilsMessenger(const vk::DebugUtilsMessenger& messenger, - const vk::Instance& instance, - const vk::InstanceDispatch& dispatch) { - auto deleter = [instance_ptr = *instance](VkDebugUtilsMessengerEXT handle, const vk::InstanceDispatch& dld) { - dld.vkDestroyDebugUtilsMessengerEXT(instance_ptr, handle, nullptr); - }; - return ManagedDebugUtilsMessenger(*messenger, deleter, dispatch, "VkDebugUtilsMessengerEXT"); -} - -/** - * Creates an RAII wrapper for a Vulkan swapchain - */ -inline ManagedSwapchain MakeManagedSwapchain(VkSwapchainKHR swapchain_handle, VkDevice device_handle, const vk::DeviceDispatch& dispatch) { - auto deleter = [device_handle](VkSwapchainKHR handle, const vk::DeviceDispatch& dld) { - dld.vkDestroySwapchainKHR(device_handle, handle, nullptr); - }; - return ManagedSwapchain(swapchain_handle, deleter, dispatch, "VkSwapchainKHR"); -} - -/** - * Creates an RAII wrapper for a Vulkan buffer - */ -inline ManagedBuffer MakeManagedBuffer(VkBuffer buffer_handle, VkDevice device_handle, const vk::DeviceDispatch& dispatch) { - auto deleter = [device_handle](VkBuffer handle, const vk::DeviceDispatch& dld) { - dld.vkDestroyBuffer(device_handle, handle, nullptr); - }; - return ManagedBuffer(buffer_handle, deleter, dispatch, "VkBuffer"); -} - -/** - * Creates an RAII wrapper for a Vulkan image - */ -inline ManagedImage MakeManagedImage(VkImage image_handle, VkDevice device_handle, const vk::DeviceDispatch& dispatch) { - auto deleter = [device_handle](VkImage handle, const vk::DeviceDispatch& dld) { - dld.vkDestroyImage(device_handle, handle, nullptr); - }; - return ManagedImage(image_handle, deleter, dispatch, "VkImage"); -} - -/** - * Creates an RAII wrapper for a Vulkan image view - */ -inline ManagedImageView MakeManagedImageView(VkImageView view_handle, VkDevice device_handle, const vk::DeviceDispatch& dispatch) { - auto deleter = [device_handle](VkImageView handle, const vk::DeviceDispatch& dld) { - dld.vkDestroyImageView(device_handle, handle, nullptr); - }; - return ManagedImageView(view_handle, deleter, dispatch, "VkImageView"); -} - -/** - * Creates an RAII wrapper for a Vulkan semaphore - */ -inline ManagedSemaphore MakeManagedSemaphore(VkSemaphore semaphore_handle, VkDevice device_handle, const vk::DeviceDispatch& dispatch) { - auto deleter = [device_handle](VkSemaphore handle, const vk::DeviceDispatch& dld) { - dld.vkDestroySemaphore(device_handle, handle, nullptr); - }; - return ManagedSemaphore(semaphore_handle, deleter, dispatch, "VkSemaphore"); -} - -/** - * Creates an RAII wrapper for a Vulkan fence - */ -inline ManagedFence MakeManagedFence(VkFence fence_handle, VkDevice device_handle, const vk::DeviceDispatch& dispatch) { - auto deleter = [device_handle](VkFence handle, const vk::DeviceDispatch& dld) { - dld.vkDestroyFence(device_handle, handle, nullptr); - }; - return ManagedFence(fence_handle, deleter, dispatch, "VkFence"); -} - -} // namespace Vulkan \ No newline at end of file diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp index 949b91499d..b77d01711a 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.cpp +++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp @@ -12,7 +12,6 @@ #include "common/common_types.h" #include "common/logging/log.h" -#include "common/settings.h" #include "video_core/vulkan_common/vk_enum_string_helper.h" #include "video_core/vulkan_common/vma.h" #include "video_core/vulkan_common/vulkan_wrapper.h" @@ -311,10 +310,7 @@ const char* Exception::what() const noexcept { } void Destroy(VkInstance instance, const InstanceDispatch& dld) noexcept { - // FIXME: A double free occurs here if RAII is enabled. - if (!Settings::values.enable_raii.GetValue()) { - dld.vkDestroyInstance(instance, nullptr); - } + dld.vkDestroyInstance(instance, nullptr); } void Destroy(VkDevice device, const InstanceDispatch& dld) noexcept { @@ -417,10 +413,7 @@ void Destroy(VkInstance instance, VkDebugReportCallbackEXT handle, } void Destroy(VkInstance instance, VkSurfaceKHR handle, const InstanceDispatch& dld) noexcept { - // FIXME: A double free occurs here if RAII is enabled. - if (!Settings::values.enable_raii.GetValue()) { - dld.vkDestroySurfaceKHR(instance, handle, nullptr); - } + dld.vkDestroySurfaceKHR(instance, handle, nullptr); } VkResult Free(VkDevice device, VkDescriptorPool handle, Span sets, diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h index 6501094f05..39396b3279 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.h +++ b/src/video_core/vulkan_common/vulkan_wrapper.h @@ -516,7 +516,7 @@ public: } /// Returns true when there's a held object. - operator bool() const noexcept { + explicit operator bool() const noexcept { return handle != nullptr; } @@ -627,7 +627,7 @@ class Instance : public Handle { public: /// Creates a Vulkan instance. /// @throw Exception on initialization error. - static Instance Create(u32 version, Span layers, Span extensions, + [[nodiscard]] static Instance Create(u32 version, Span layers, Span extensions, InstanceDispatch& dispatch); /// Enumerates physical devices. @@ -637,12 +637,12 @@ public: /// Creates a debug callback messenger. /// @throw Exception on creation failure. - DebugUtilsMessenger CreateDebugUtilsMessenger( + [[nodiscard]] DebugUtilsMessenger CreateDebugUtilsMessenger( const VkDebugUtilsMessengerCreateInfoEXT& create_info) const; /// Creates a debug report callback. /// @throw Exception on creation failure. - DebugReportCallback CreateDebugReportCallback( + [[nodiscard]] DebugReportCallback CreateDebugReportCallback( const VkDebugReportCallbackCreateInfoEXT& create_info) const; /// Returns dispatch table. @@ -986,58 +986,60 @@ class Device : public Handle { using Handle::Handle; public: - static Device Create(VkPhysicalDevice physical_device, Span queues_ci, - Span enabled_extensions, const void* next, - DeviceDispatch& dispatch); + [[nodiscard]] static Device Create(VkPhysicalDevice physical_device, + Span queues_ci, + Span enabled_extensions, const void* next, + DeviceDispatch& dispatch); - Queue GetQueue(u32 family_index) const noexcept; + [[nodiscard]] Queue GetQueue(u32 family_index) const noexcept; - BufferView CreateBufferView(const VkBufferViewCreateInfo& ci) const; + [[nodiscard]] BufferView CreateBufferView(const VkBufferViewCreateInfo& ci) const; - ImageView CreateImageView(const VkImageViewCreateInfo& ci) const; + [[nodiscard]] ImageView CreateImageView(const VkImageViewCreateInfo& ci) const; - Semaphore CreateSemaphore() const; + [[nodiscard]] Semaphore CreateSemaphore() const; - Semaphore CreateSemaphore(const VkSemaphoreCreateInfo& ci) const; + [[nodiscard]] Semaphore CreateSemaphore(const VkSemaphoreCreateInfo& ci) const; - Fence CreateFence(const VkFenceCreateInfo& ci) const; + [[nodiscard]] Fence CreateFence(const VkFenceCreateInfo& ci) const; - DescriptorPool CreateDescriptorPool(const VkDescriptorPoolCreateInfo& ci) const; + [[nodiscard]] DescriptorPool CreateDescriptorPool(const VkDescriptorPoolCreateInfo& ci) const; - RenderPass CreateRenderPass(const VkRenderPassCreateInfo& ci) const; + [[nodiscard]] RenderPass CreateRenderPass(const VkRenderPassCreateInfo& ci) const; - DescriptorSetLayout CreateDescriptorSetLayout(const VkDescriptorSetLayoutCreateInfo& ci) const; + [[nodiscard]] DescriptorSetLayout CreateDescriptorSetLayout( + const VkDescriptorSetLayoutCreateInfo& ci) const; - PipelineCache CreatePipelineCache(const VkPipelineCacheCreateInfo& ci) const; + [[nodiscard]] PipelineCache CreatePipelineCache(const VkPipelineCacheCreateInfo& ci) const; - PipelineLayout CreatePipelineLayout(const VkPipelineLayoutCreateInfo& ci) const; + [[nodiscard]] PipelineLayout CreatePipelineLayout(const VkPipelineLayoutCreateInfo& ci) const; - Pipeline CreateGraphicsPipeline(const VkGraphicsPipelineCreateInfo& ci, - VkPipelineCache cache = nullptr) const; + [[nodiscard]] Pipeline CreateGraphicsPipeline(const VkGraphicsPipelineCreateInfo& ci, + VkPipelineCache cache = nullptr) const; - Pipeline CreateComputePipeline(const VkComputePipelineCreateInfo& ci, - VkPipelineCache cache = nullptr) const; + [[nodiscard]] Pipeline CreateComputePipeline(const VkComputePipelineCreateInfo& ci, + VkPipelineCache cache = nullptr) const; - Sampler CreateSampler(const VkSamplerCreateInfo& ci) const; + [[nodiscard]] Sampler CreateSampler(const VkSamplerCreateInfo& ci) const; - Framebuffer CreateFramebuffer(const VkFramebufferCreateInfo& ci) const; + [[nodiscard]] Framebuffer CreateFramebuffer(const VkFramebufferCreateInfo& ci) const; - CommandPool CreateCommandPool(const VkCommandPoolCreateInfo& ci) const; + [[nodiscard]] CommandPool CreateCommandPool(const VkCommandPoolCreateInfo& ci) const; - DescriptorUpdateTemplate CreateDescriptorUpdateTemplate( + [[nodiscard]] DescriptorUpdateTemplate CreateDescriptorUpdateTemplate( const VkDescriptorUpdateTemplateCreateInfo& ci) const; - QueryPool CreateQueryPool(const VkQueryPoolCreateInfo& ci) const; + [[nodiscard]] QueryPool CreateQueryPool(const VkQueryPoolCreateInfo& ci) const; - ShaderModule CreateShaderModule(const VkShaderModuleCreateInfo& ci) const; + [[nodiscard]] ShaderModule CreateShaderModule(const VkShaderModuleCreateInfo& ci) const; - Event CreateEvent() const; + [[nodiscard]] Event CreateEvent() const; - SwapchainKHR CreateSwapchainKHR(const VkSwapchainCreateInfoKHR& ci) const; + [[nodiscard]] SwapchainKHR CreateSwapchainKHR(const VkSwapchainCreateInfoKHR& ci) const; - DeviceMemory TryAllocateMemory(const VkMemoryAllocateInfo& ai) const noexcept; + [[nodiscard]] DeviceMemory TryAllocateMemory(const VkMemoryAllocateInfo& ai) const noexcept; - DeviceMemory AllocateMemory(const VkMemoryAllocateInfo& ai) const; + [[nodiscard]] DeviceMemory AllocateMemory(const VkMemoryAllocateInfo& ai) const; VkMemoryRequirements GetBufferMemoryRequirements(VkBuffer buffer, void* pnext = nullptr) const noexcept; From 8078990b9b95c5bb2c432abc9012c2738f8a451a Mon Sep 17 00:00:00 2001 From: Ribbit Date: Wed, 8 Oct 2025 05:39:08 +0200 Subject: [PATCH 12/17] [vk] Fast UBO: fix tracking, resize heuristics, add debug guard (#2695) Co-authored-by: Ribbit Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2695 Reviewed-by: CamilleLaVey Co-authored-by: Ribbit Co-committed-by: Ribbit --- src/core/device_memory_manager.h | 6 +++ src/core/device_memory_manager.inc | 26 ++++++++++++ src/video_core/buffer_cache/buffer_cache.h | 42 ++++++++++--------- .../buffer_cache/buffer_cache_base.h | 5 ++- .../vk_staging_buffer_pool.cpp | 26 +++++++----- .../renderer_vulkan/vk_staging_buffer_pool.h | 4 ++ 6 files changed, 76 insertions(+), 33 deletions(-) diff --git a/src/core/device_memory_manager.h b/src/core/device_memory_manager.h index 6dcf7bb228..192c6e5c01 100644 --- a/src/core/device_memory_manager.h +++ b/src/core/device_memory_manager.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -109,6 +112,9 @@ public: void ReadBlock(DAddr address, void* dest_pointer, size_t size); void ReadBlockUnsafe(DAddr address, void* dest_pointer, size_t size); +#ifdef YUZU_DEBUG + bool ReadBlockFastChecked(DAddr address, void* dest_pointer, size_t size); +#endif void WriteBlock(DAddr address, const void* src_pointer, size_t size); void WriteBlockUnsafe(DAddr address, const void* src_pointer, size_t size); diff --git a/src/core/device_memory_manager.inc b/src/core/device_memory_manager.inc index 52dff5df9a..3629579c09 100644 --- a/src/core/device_memory_manager.inc +++ b/src/core/device_memory_manager.inc @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -467,6 +470,29 @@ void DeviceMemoryManager::ReadBlockUnsafe(DAddr address, void* dest_poin }); } +#ifdef YUZU_DEBUG +template +bool DeviceMemoryManager::ReadBlockFastChecked(DAddr address, void* dest_pointer, + size_t size) { + bool success = true; + WalkBlock( + address, size, + [&](size_t copy_amount, DAddr current_vaddr) { + LOG_CRITICAL(Render, "DeviceMemory OOB/unmapped: addr=0x{:x} size={}", current_vaddr, + size); + std::memset(dest_pointer, 0, copy_amount); + success = false; + }, + [&](size_t copy_amount, const u8* const src_ptr) { + std::memcpy(dest_pointer, src_ptr, copy_amount); + }, + [&](const std::size_t copy_amount) { + dest_pointer = static_cast(dest_pointer) + copy_amount; + }); + return success; +} +#endif + template void DeviceMemoryManager::WriteBlockUnsafe(DAddr address, const void* src_pointer, size_t size) { diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 5223afe937..388c8034c5 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -386,11 +386,10 @@ void BufferCache

::BindHostComputeBuffers() { template void BufferCache

::SetUniformBuffersState(const std::array& mask, const UniformBufferSizes* sizes) { - if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) { - if (channel_state->enabled_uniform_buffer_masks != mask) { - if constexpr (IS_OPENGL) { - channel_state->fast_bound_uniform_buffers.fill(0); - } + const bool mask_changed = channel_state->enabled_uniform_buffer_masks != mask; + if (mask_changed) { + channel_state->fast_bound_uniform_buffers.fill(0); + if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) { channel_state->dirty_uniform_buffers.fill(~u32{0}); channel_state->uniform_buffer_binding_sizes.fill({}); } @@ -806,7 +805,7 @@ void BufferCache

::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32 channel_state->uniform_buffer_binding_sizes[stage][binding_index] != size; if (should_fast_bind) { // We only have to bind when the currently bound buffer is not the fast version - channel_state->fast_bound_uniform_buffers[stage] |= 1U << binding_index; + channel_state->fast_bound_uniform_buffers[stage] |= 1u << binding_index; channel_state->uniform_buffer_binding_sizes[stage][binding_index] = size; runtime.BindFastUniformBuffer(stage, binding_index, size); } @@ -815,13 +814,22 @@ void BufferCache

::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32 return; } } - if constexpr (IS_OPENGL) { - channel_state->fast_bound_uniform_buffers[stage] |= 1U << binding_index; - channel_state->uniform_buffer_binding_sizes[stage][binding_index] = size; - } + channel_state->fast_bound_uniform_buffers[stage] |= 1u << binding_index; + channel_state->uniform_buffer_binding_sizes[stage][binding_index] = size; // Stream buffer path to avoid stalling on non-Nvidia drivers or Vulkan const std::span span = runtime.BindMappedUniformBuffer(stage, binding_index, size); +#ifdef YUZU_DEBUG + ASSERT(binding_index < NUM_GRAPHICS_UNIFORM_BUFFERS); + ASSERT(span.size() >= size && "UBO stream span too small"); + if (!device_memory.ReadBlockFastChecked(device_addr, span.data(), size)) { + LOG_CRITICAL(Render, "DeviceMemory OOB/unmapped: addr=0x{:x} size={}", device_addr, size); + channel_state->fast_bound_uniform_buffers[stage] &= ~(1u << binding_index); + ASSERT(false); + return; + } +#else device_memory.ReadBlockUnsafe(device_addr, span.data(), size); +#endif return; } // Classic cached path @@ -830,7 +838,8 @@ void BufferCache

::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32 } // Skip binding if it's not needed and if the bound buffer is not the fast version // This exists to avoid instances where the fast buffer is bound and a GPU write happens - needs_bind |= HasFastUniformBufferBound(stage, binding_index); + const bool was_fast_bound = HasFastUniformBufferBound(stage, binding_index); + needs_bind |= was_fast_bound; if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) { needs_bind |= channel_state->uniform_buffer_binding_sizes[stage][binding_index] != size; } @@ -839,9 +848,6 @@ void BufferCache

::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32 } const u32 offset = buffer.Offset(device_addr); if constexpr (IS_OPENGL) { - // Fast buffer will be unbound - channel_state->fast_bound_uniform_buffers[stage] &= ~(1U << binding_index); - // Mark the index as dirty if offset doesn't match const bool is_copy_bind = offset != 0 && !runtime.SupportsNonZeroUniformOffset(); channel_state->dirty_uniform_buffers[stage] |= (is_copy_bind ? 1U : 0U) << index; @@ -855,6 +861,7 @@ void BufferCache

::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32 } else { runtime.BindUniformBuffer(buffer, offset, size); } + channel_state->fast_bound_uniform_buffers[stage] &= ~(1u << binding_index); } template @@ -1789,12 +1796,7 @@ std::span BufferCache

::ImmediateBuffer(size_t wanted_capacity) { template bool BufferCache

::HasFastUniformBufferBound(size_t stage, u32 binding_index) const noexcept { - if constexpr (IS_OPENGL) { - return ((channel_state->fast_bound_uniform_buffers[stage] >> binding_index) & 1) != 0; - } else { - // Only OpenGL has fast uniform buffers - return false; - } + return ((channel_state->fast_bound_uniform_buffers[stage] >> binding_index) & 1u) != 0; } template diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h index 486d19fb79..09631ffd83 100644 --- a/src/video_core/buffer_cache/buffer_cache_base.h +++ b/src/video_core/buffer_cache/buffer_cache_base.h @@ -53,6 +53,7 @@ constexpr u32 NUM_COMPUTE_UNIFORM_BUFFERS = 8; constexpr u32 NUM_STORAGE_BUFFERS = 16; constexpr u32 NUM_TEXTURE_BUFFERS = 32; constexpr u32 NUM_STAGES = 5; +static_assert(NUM_GRAPHICS_UNIFORM_BUFFERS <= 32, "fast bitmask must fit u32"); using UniformBufferSizes = std::array, NUM_STAGES>; using ComputeUniformBufferSizes = std::array; @@ -137,8 +138,8 @@ public: u32 written_compute_texture_buffers = 0; u32 image_compute_texture_buffers = 0; - std::array uniform_cache_hits{}; - std::array uniform_cache_shots{}; + std::array uniform_cache_hits{}; + std::array uniform_cache_shots{}; u32 uniform_buffer_skip_cache_size = DEFAULT_SKIP_CACHE_SIZE; diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp index ecc4f77dc7..0fbe707b04 100644 --- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp +++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp @@ -25,12 +25,12 @@ namespace { using namespace Common::Literals; -// Maximum potential alignment of a Vulkan buffer -constexpr VkDeviceSize MAX_ALIGNMENT = 256; +// Minimum alignment we want to enforce for the streaming ring +constexpr VkDeviceSize MIN_STREAM_ALIGNMENT = 256; // Stream buffer size in bytes constexpr VkDeviceSize MAX_STREAM_BUFFER_SIZE = 128_MiB; -size_t GetStreamBufferSize(const Device& device) { +size_t GetStreamBufferSize(const Device& device, VkDeviceSize alignment) { VkDeviceSize size{0}; if (device.HasDebuggingToolAttached()) { bool found_heap = false; @@ -53,8 +53,9 @@ size_t GetStreamBufferSize(const Device& device) { // Clamp to the configured maximum, align up for safety, and ensure a sane minimum so // region_size (stream_buffer_size / NUM_SYNCS) never becomes zero. - const VkDeviceSize aligned = (std::min)(Common::AlignUp(size, MAX_ALIGNMENT), MAX_STREAM_BUFFER_SIZE); - const VkDeviceSize min_size = MAX_ALIGNMENT * StagingBufferPool::NUM_SYNCS; + const VkDeviceSize aligned = + (std::min)(Common::AlignUp(size, alignment), MAX_STREAM_BUFFER_SIZE); + const VkDeviceSize min_size = alignment * StagingBufferPool::NUM_SYNCS; return static_cast((std::max)(aligned, min_size)); } } // Anonymous namespace @@ -62,8 +63,10 @@ size_t GetStreamBufferSize(const Device& device) { StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& memory_allocator_, Scheduler& scheduler_) : device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_}, - stream_buffer_size{GetStreamBufferSize(device)}, region_size{stream_buffer_size / - StagingBufferPool::NUM_SYNCS} { + stream_alignment{std::max(device_.GetUniformBufferAlignment(), + MIN_STREAM_ALIGNMENT)}, + stream_buffer_size{GetStreamBufferSize(device_, stream_alignment)}, + region_size{stream_buffer_size / StagingBufferPool::NUM_SYNCS} { VkBufferCreateInfo stream_ci = { .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .pNext = nullptr, @@ -116,10 +119,11 @@ void StagingBufferPool::TickFrame() { } StagingBufferRef StagingBufferPool::GetStreamBuffer(size_t size) { - const size_t aligned_size = Common::AlignUp(size, MAX_ALIGNMENT); + const size_t alignment = static_cast(stream_alignment); + const size_t aligned_size = Common::AlignUp(size, alignment); const bool wraps = iterator + size >= stream_buffer_size; const size_t new_iterator = - wraps ? aligned_size : Common::AlignUp(iterator + size, MAX_ALIGNMENT); + wraps ? aligned_size : Common::AlignUp(iterator + size, alignment); const size_t begin_region = wraps ? 0 : Region(iterator); const size_t last_byte = new_iterator == 0 ? 0 : new_iterator - 1; const size_t end_region = (std::min)(Region(last_byte) + 1, NUM_SYNCS); @@ -145,7 +149,7 @@ StagingBufferRef StagingBufferPool::GetStreamBuffer(size_t size) { current_tick); used_iterator = 0; iterator = 0; - free_iterator = size; + free_iterator = aligned_size; const size_t head_last_byte = aligned_size == 0 ? 0 : aligned_size - 1; const size_t head_end_region = (std::min)(Region(head_last_byte) + 1, NUM_SYNCS); if (AreRegionsActive(0, head_end_region)) { @@ -160,7 +164,7 @@ StagingBufferRef StagingBufferPool::GetStreamBuffer(size_t size) { iterator = new_iterator; if (!wraps) { - free_iterator = (std::max)(free_iterator, offset + size); + free_iterator = (std::max)(free_iterator, offset + aligned_size); } return StagingBufferRef{ diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h index f63a203272..5c40ca069f 100644 --- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h +++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later @@ -102,6 +105,7 @@ private: MemoryAllocator& memory_allocator; Scheduler& scheduler; + VkDeviceSize stream_alignment; vk::Buffer stream_buffer; std::span stream_pointer; VkDeviceSize stream_buffer_size; From 954c17c18a5e6a39c4f7edd80bbb4a1e0507d4e0 Mon Sep 17 00:00:00 2001 From: crueter Date: Wed, 8 Oct 2025 06:39:58 +0200 Subject: [PATCH 13/17] [frontend] change order of filters to match append rules (#2696) otherwise, FSR would change to Gaussian etc. in general, sans resolution, new enum values should always be appended Signed-off-by: crueter Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2696 Reviewed-by: CamilleLaVey Reviewed-by: Lizzie --- src/android/app/src/main/res/values/arrays.xml | 8 ++++---- src/common/settings_enums.h | 2 +- src/qt_common/shared_translation.cpp | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml index 7e44750909..2150d401db 100644 --- a/src/android/app/src/main/res/values/arrays.xml +++ b/src/android/app/src/main/res/values/arrays.xml @@ -253,16 +253,16 @@ @string/scaling_filter_nearest_neighbor @string/scaling_filter_bilinear @string/scaling_filter_bicubic - @string/scaling_filter_zero_tangent - @string/scaling_filter_bspline - @string/scaling_filter_mitchell - @string/scaling_filter_spline1 @string/scaling_filter_gaussian @string/scaling_filter_lanczos @string/scaling_filter_scale_force @string/scaling_filter_fsr @string/scaling_filter_area @string/scaling_filter_mmpx + @string/scaling_filter_zero_tangent + @string/scaling_filter_bspline + @string/scaling_filter_mitchell + @string/scaling_filter_spline1 diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h index 3fcdf08256..ccf6f1cfb2 100644 --- a/src/common/settings_enums.h +++ b/src/common/settings_enums.h @@ -143,7 +143,7 @@ ENUM(ConfirmStop, Ask_Always, Ask_Based_On_Game, Ask_Never); ENUM(FullscreenMode, Borderless, Exclusive); ENUM(NvdecEmulation, Off, Cpu, Gpu); ENUM(ResolutionSetup, Res1_4X, Res1_2X, Res3_4X, Res1X, Res5_4X, Res3_2X, Res2X, Res3X, Res4X, Res5X, Res6X, Res7X, Res8X); -ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, ZeroTangent, BSpline, Mitchell, Spline1, Gaussian, Lanczos, ScaleForce, Fsr, Area, Mmpx, MaxEnum); +ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, Gaussian, Lanczos, ScaleForce, Fsr, Area, ZeroTangent, BSpline, Mitchell, Spline1, Mmpx, MaxEnum); ENUM(AntiAliasing, None, Fxaa, Smaa, MaxEnum); ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch); ENUM(ConsoleMode, Handheld, Docked); diff --git a/src/qt_common/shared_translation.cpp b/src/qt_common/shared_translation.cpp index 0b40ca9904..054d28e8e2 100644 --- a/src/qt_common/shared_translation.cpp +++ b/src/qt_common/shared_translation.cpp @@ -549,16 +549,16 @@ std::unique_ptr ComboboxEnumeration(QObject* parent) PAIR(ScalingFilter, NearestNeighbor, tr("Nearest Neighbor")), PAIR(ScalingFilter, Bilinear, tr("Bilinear")), PAIR(ScalingFilter, Bicubic, tr("Bicubic")), - PAIR(ScalingFilter, ZeroTangent, tr("Zero-Tangent")), - PAIR(ScalingFilter, BSpline, tr("B-Spline")), - PAIR(ScalingFilter, Mitchell, tr("Mitchell")), - PAIR(ScalingFilter, Spline1, tr("Spline-1")), PAIR(ScalingFilter, Gaussian, tr("Gaussian")), PAIR(ScalingFilter, Lanczos, tr("Lanczos")), PAIR(ScalingFilter, ScaleForce, tr("ScaleForce")), PAIR(ScalingFilter, Fsr, tr("AMD FidelityFX™️ Super Resolution")), PAIR(ScalingFilter, Area, tr("Area")), PAIR(ScalingFilter, Mmpx, tr("MMPX")), + PAIR(ScalingFilter, ZeroTangent, tr("Zero-Tangent")), + PAIR(ScalingFilter, BSpline, tr("B-Spline")), + PAIR(ScalingFilter, Mitchell, tr("Mitchell")), + PAIR(ScalingFilter, Spline1, tr("Spline-1")), }}); translations->insert({Settings::EnumMetadata::Index(), { From 3c6ef765af9b946240cedf053e593c0fc86a3f01 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Thu, 9 Oct 2025 21:37:27 +0200 Subject: [PATCH 14/17] revert [vk] Fast UBO: fix tracking, resize heuristics, add debug guard (#2695) (#2706) revert [vk] Fast UBO: fix tracking, resize heuristics, add debug guard (#2695) Well, stuff showed up after testing phase, that showed us this change break SMO and some mods after being merged directly into master, we will keep stuying why happens this and add a better handling later. Co-authored-by: Ribbit Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2695 Reviewed-by: CamilleLaVey Co-authored-by: Ribbit Co-committed-by: Ribbit Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2706 Co-authored-by: CamilleLaVey Co-committed-by: CamilleLaVey --- src/core/device_memory_manager.h | 6 --- src/core/device_memory_manager.inc | 26 ------------ src/video_core/buffer_cache/buffer_cache.h | 42 +++++++++---------- .../buffer_cache/buffer_cache_base.h | 5 +-- .../vk_staging_buffer_pool.cpp | 26 +++++------- .../renderer_vulkan/vk_staging_buffer_pool.h | 4 -- 6 files changed, 33 insertions(+), 76 deletions(-) diff --git a/src/core/device_memory_manager.h b/src/core/device_memory_manager.h index 192c6e5c01..6dcf7bb228 100644 --- a/src/core/device_memory_manager.h +++ b/src/core/device_memory_manager.h @@ -1,6 +1,3 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -112,9 +109,6 @@ public: void ReadBlock(DAddr address, void* dest_pointer, size_t size); void ReadBlockUnsafe(DAddr address, void* dest_pointer, size_t size); -#ifdef YUZU_DEBUG - bool ReadBlockFastChecked(DAddr address, void* dest_pointer, size_t size); -#endif void WriteBlock(DAddr address, const void* src_pointer, size_t size); void WriteBlockUnsafe(DAddr address, const void* src_pointer, size_t size); diff --git a/src/core/device_memory_manager.inc b/src/core/device_memory_manager.inc index 3629579c09..52dff5df9a 100644 --- a/src/core/device_memory_manager.inc +++ b/src/core/device_memory_manager.inc @@ -1,6 +1,3 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -470,29 +467,6 @@ void DeviceMemoryManager::ReadBlockUnsafe(DAddr address, void* dest_poin }); } -#ifdef YUZU_DEBUG -template -bool DeviceMemoryManager::ReadBlockFastChecked(DAddr address, void* dest_pointer, - size_t size) { - bool success = true; - WalkBlock( - address, size, - [&](size_t copy_amount, DAddr current_vaddr) { - LOG_CRITICAL(Render, "DeviceMemory OOB/unmapped: addr=0x{:x} size={}", current_vaddr, - size); - std::memset(dest_pointer, 0, copy_amount); - success = false; - }, - [&](size_t copy_amount, const u8* const src_ptr) { - std::memcpy(dest_pointer, src_ptr, copy_amount); - }, - [&](const std::size_t copy_amount) { - dest_pointer = static_cast(dest_pointer) + copy_amount; - }); - return success; -} -#endif - template void DeviceMemoryManager::WriteBlockUnsafe(DAddr address, const void* src_pointer, size_t size) { diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 388c8034c5..5223afe937 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -386,10 +386,11 @@ void BufferCache

::BindHostComputeBuffers() { template void BufferCache

::SetUniformBuffersState(const std::array& mask, const UniformBufferSizes* sizes) { - const bool mask_changed = channel_state->enabled_uniform_buffer_masks != mask; - if (mask_changed) { - channel_state->fast_bound_uniform_buffers.fill(0); - if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) { + if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) { + if (channel_state->enabled_uniform_buffer_masks != mask) { + if constexpr (IS_OPENGL) { + channel_state->fast_bound_uniform_buffers.fill(0); + } channel_state->dirty_uniform_buffers.fill(~u32{0}); channel_state->uniform_buffer_binding_sizes.fill({}); } @@ -805,7 +806,7 @@ void BufferCache

::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32 channel_state->uniform_buffer_binding_sizes[stage][binding_index] != size; if (should_fast_bind) { // We only have to bind when the currently bound buffer is not the fast version - channel_state->fast_bound_uniform_buffers[stage] |= 1u << binding_index; + channel_state->fast_bound_uniform_buffers[stage] |= 1U << binding_index; channel_state->uniform_buffer_binding_sizes[stage][binding_index] = size; runtime.BindFastUniformBuffer(stage, binding_index, size); } @@ -814,22 +815,13 @@ void BufferCache

::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32 return; } } - channel_state->fast_bound_uniform_buffers[stage] |= 1u << binding_index; - channel_state->uniform_buffer_binding_sizes[stage][binding_index] = size; + if constexpr (IS_OPENGL) { + channel_state->fast_bound_uniform_buffers[stage] |= 1U << binding_index; + channel_state->uniform_buffer_binding_sizes[stage][binding_index] = size; + } // Stream buffer path to avoid stalling on non-Nvidia drivers or Vulkan const std::span span = runtime.BindMappedUniformBuffer(stage, binding_index, size); -#ifdef YUZU_DEBUG - ASSERT(binding_index < NUM_GRAPHICS_UNIFORM_BUFFERS); - ASSERT(span.size() >= size && "UBO stream span too small"); - if (!device_memory.ReadBlockFastChecked(device_addr, span.data(), size)) { - LOG_CRITICAL(Render, "DeviceMemory OOB/unmapped: addr=0x{:x} size={}", device_addr, size); - channel_state->fast_bound_uniform_buffers[stage] &= ~(1u << binding_index); - ASSERT(false); - return; - } -#else device_memory.ReadBlockUnsafe(device_addr, span.data(), size); -#endif return; } // Classic cached path @@ -838,8 +830,7 @@ void BufferCache

::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32 } // Skip binding if it's not needed and if the bound buffer is not the fast version // This exists to avoid instances where the fast buffer is bound and a GPU write happens - const bool was_fast_bound = HasFastUniformBufferBound(stage, binding_index); - needs_bind |= was_fast_bound; + needs_bind |= HasFastUniformBufferBound(stage, binding_index); if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) { needs_bind |= channel_state->uniform_buffer_binding_sizes[stage][binding_index] != size; } @@ -848,6 +839,9 @@ void BufferCache

::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32 } const u32 offset = buffer.Offset(device_addr); if constexpr (IS_OPENGL) { + // Fast buffer will be unbound + channel_state->fast_bound_uniform_buffers[stage] &= ~(1U << binding_index); + // Mark the index as dirty if offset doesn't match const bool is_copy_bind = offset != 0 && !runtime.SupportsNonZeroUniformOffset(); channel_state->dirty_uniform_buffers[stage] |= (is_copy_bind ? 1U : 0U) << index; @@ -861,7 +855,6 @@ void BufferCache

::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32 } else { runtime.BindUniformBuffer(buffer, offset, size); } - channel_state->fast_bound_uniform_buffers[stage] &= ~(1u << binding_index); } template @@ -1796,7 +1789,12 @@ std::span BufferCache

::ImmediateBuffer(size_t wanted_capacity) { template bool BufferCache

::HasFastUniformBufferBound(size_t stage, u32 binding_index) const noexcept { - return ((channel_state->fast_bound_uniform_buffers[stage] >> binding_index) & 1u) != 0; + if constexpr (IS_OPENGL) { + return ((channel_state->fast_bound_uniform_buffers[stage] >> binding_index) & 1) != 0; + } else { + // Only OpenGL has fast uniform buffers + return false; + } } template diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h index 09631ffd83..486d19fb79 100644 --- a/src/video_core/buffer_cache/buffer_cache_base.h +++ b/src/video_core/buffer_cache/buffer_cache_base.h @@ -53,7 +53,6 @@ constexpr u32 NUM_COMPUTE_UNIFORM_BUFFERS = 8; constexpr u32 NUM_STORAGE_BUFFERS = 16; constexpr u32 NUM_TEXTURE_BUFFERS = 32; constexpr u32 NUM_STAGES = 5; -static_assert(NUM_GRAPHICS_UNIFORM_BUFFERS <= 32, "fast bitmask must fit u32"); using UniformBufferSizes = std::array, NUM_STAGES>; using ComputeUniformBufferSizes = std::array; @@ -138,8 +137,8 @@ public: u32 written_compute_texture_buffers = 0; u32 image_compute_texture_buffers = 0; - std::array uniform_cache_hits{}; - std::array uniform_cache_shots{}; + std::array uniform_cache_hits{}; + std::array uniform_cache_shots{}; u32 uniform_buffer_skip_cache_size = DEFAULT_SKIP_CACHE_SIZE; diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp index 0fbe707b04..ecc4f77dc7 100644 --- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp +++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp @@ -25,12 +25,12 @@ namespace { using namespace Common::Literals; -// Minimum alignment we want to enforce for the streaming ring -constexpr VkDeviceSize MIN_STREAM_ALIGNMENT = 256; +// Maximum potential alignment of a Vulkan buffer +constexpr VkDeviceSize MAX_ALIGNMENT = 256; // Stream buffer size in bytes constexpr VkDeviceSize MAX_STREAM_BUFFER_SIZE = 128_MiB; -size_t GetStreamBufferSize(const Device& device, VkDeviceSize alignment) { +size_t GetStreamBufferSize(const Device& device) { VkDeviceSize size{0}; if (device.HasDebuggingToolAttached()) { bool found_heap = false; @@ -53,9 +53,8 @@ size_t GetStreamBufferSize(const Device& device, VkDeviceSize alignment) { // Clamp to the configured maximum, align up for safety, and ensure a sane minimum so // region_size (stream_buffer_size / NUM_SYNCS) never becomes zero. - const VkDeviceSize aligned = - (std::min)(Common::AlignUp(size, alignment), MAX_STREAM_BUFFER_SIZE); - const VkDeviceSize min_size = alignment * StagingBufferPool::NUM_SYNCS; + const VkDeviceSize aligned = (std::min)(Common::AlignUp(size, MAX_ALIGNMENT), MAX_STREAM_BUFFER_SIZE); + const VkDeviceSize min_size = MAX_ALIGNMENT * StagingBufferPool::NUM_SYNCS; return static_cast((std::max)(aligned, min_size)); } } // Anonymous namespace @@ -63,10 +62,8 @@ size_t GetStreamBufferSize(const Device& device, VkDeviceSize alignment) { StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& memory_allocator_, Scheduler& scheduler_) : device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_}, - stream_alignment{std::max(device_.GetUniformBufferAlignment(), - MIN_STREAM_ALIGNMENT)}, - stream_buffer_size{GetStreamBufferSize(device_, stream_alignment)}, - region_size{stream_buffer_size / StagingBufferPool::NUM_SYNCS} { + stream_buffer_size{GetStreamBufferSize(device)}, region_size{stream_buffer_size / + StagingBufferPool::NUM_SYNCS} { VkBufferCreateInfo stream_ci = { .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .pNext = nullptr, @@ -119,11 +116,10 @@ void StagingBufferPool::TickFrame() { } StagingBufferRef StagingBufferPool::GetStreamBuffer(size_t size) { - const size_t alignment = static_cast(stream_alignment); - const size_t aligned_size = Common::AlignUp(size, alignment); + const size_t aligned_size = Common::AlignUp(size, MAX_ALIGNMENT); const bool wraps = iterator + size >= stream_buffer_size; const size_t new_iterator = - wraps ? aligned_size : Common::AlignUp(iterator + size, alignment); + wraps ? aligned_size : Common::AlignUp(iterator + size, MAX_ALIGNMENT); const size_t begin_region = wraps ? 0 : Region(iterator); const size_t last_byte = new_iterator == 0 ? 0 : new_iterator - 1; const size_t end_region = (std::min)(Region(last_byte) + 1, NUM_SYNCS); @@ -149,7 +145,7 @@ StagingBufferRef StagingBufferPool::GetStreamBuffer(size_t size) { current_tick); used_iterator = 0; iterator = 0; - free_iterator = aligned_size; + free_iterator = size; const size_t head_last_byte = aligned_size == 0 ? 0 : aligned_size - 1; const size_t head_end_region = (std::min)(Region(head_last_byte) + 1, NUM_SYNCS); if (AreRegionsActive(0, head_end_region)) { @@ -164,7 +160,7 @@ StagingBufferRef StagingBufferPool::GetStreamBuffer(size_t size) { iterator = new_iterator; if (!wraps) { - free_iterator = (std::max)(free_iterator, offset + aligned_size); + free_iterator = (std::max)(free_iterator, offset + size); } return StagingBufferRef{ diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h index 5c40ca069f..f63a203272 100644 --- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h +++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h @@ -1,6 +1,3 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later @@ -105,7 +102,6 @@ private: MemoryAllocator& memory_allocator; Scheduler& scheduler; - VkDeviceSize stream_alignment; vk::Buffer stream_buffer; std::span stream_pointer; VkDeviceSize stream_buffer_size; From bfffafe68bea923ddc56bb1b7da73d549865dc11 Mon Sep 17 00:00:00 2001 From: MaranBr Date: Fri, 10 Oct 2025 01:36:55 +0200 Subject: [PATCH 15/17] [common] Change web offline applet default setting to HLE (#2705) This prevents some games from ignoring the disable web applet setting. Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2705 Co-authored-by: MaranBr Co-committed-by: MaranBr --- src/common/settings.h | 2 +- src/yuzu/configuration/configure_debug.cpp | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/common/settings.h b/src/common/settings.h index c6b52f7ba3..dd9b03f28e 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -161,7 +161,7 @@ struct Values { Category::LibraryApplet}; Setting photo_viewer_applet_mode{ linkage, AppletMode::LLE, "photo_viewer_applet_mode", Category::LibraryApplet}; - Setting offline_web_applet_mode{linkage, AppletMode::LLE, "offline_web_applet_mode", + Setting offline_web_applet_mode{linkage, AppletMode::HLE, "offline_web_applet_mode", Category::LibraryApplet}; Setting login_share_applet_mode{linkage, AppletMode::HLE, "login_share_applet_mode", Category::LibraryApplet}; diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp index 18f629f639..b825348760 100644 --- a/src/yuzu/configuration/configure_debug.cpp +++ b/src/yuzu/configuration/configure_debug.cpp @@ -83,8 +83,7 @@ void ConfigureDebug::SetConfiguration() { #ifdef YUZU_USE_QT_WEB_ENGINE ui->disable_web_applet->setChecked(UISettings::values.disable_web_applet.GetValue()); #else - ui->disable_web_applet->setEnabled(false); - ui->disable_web_applet->setText(tr("Web applet not compiled")); + ui->disable_web_applet->setVisible(false); #endif } From b6241e4148d4a7bccedd3081707106edc58ff365 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Fri, 10 Oct 2025 01:55:43 +0200 Subject: [PATCH 16/17] revert [vk] StreamBuffer Changes (#2684) (#2707) revert [vk] StreamBuffer Changes (#2684) Streambuffer changes did broke stuff in other games that got out of our scope of testing, we're going to study this changes in the future for better graphic stability. Co-authored-by: Ribbit Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2684 Reviewed-by: MaranBr Reviewed-by: Lizzie Reviewed-by: CamilleLaVey Co-authored-by: Ribbit Co-committed-by: Ribbit Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2707 Reviewed-by: MaranBr Co-authored-by: CamilleLaVey Co-committed-by: CamilleLaVey --- .../vk_staging_buffer_pool.cpp | 60 +++++-------------- 1 file changed, 14 insertions(+), 46 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp index ecc4f77dc7..08513d1534 100644 --- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp +++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp @@ -33,29 +33,19 @@ constexpr VkDeviceSize MAX_STREAM_BUFFER_SIZE = 128_MiB; size_t GetStreamBufferSize(const Device& device) { VkDeviceSize size{0}; if (device.HasDebuggingToolAttached()) { - bool found_heap = false; - ForEachDeviceLocalHostVisibleHeap(device, [&size, &found_heap](size_t /*index*/, VkMemoryHeap& heap) { + ForEachDeviceLocalHostVisibleHeap(device, [&size](size_t index, VkMemoryHeap& heap) { size = (std::max)(size, heap.size); - found_heap = true; }); - // If no suitable heap was found fall back to the default cap to avoid creating a zero-sized stream buffer. - if (!found_heap) { - size = MAX_STREAM_BUFFER_SIZE; - } else if (size <= 256_MiB) { - // If rebar is not supported, cut the max heap size to 40%. This will allow 2 captures to be - // loaded at the same time in RenderDoc. If rebar is supported, this shouldn't be an issue - // as the heap will be much larger. + // If rebar is not supported, cut the max heap size to 40%. This will allow 2 captures to be + // loaded at the same time in RenderDoc. If rebar is supported, this shouldn't be an issue + // as the heap will be much larger. + if (size <= 256_MiB) { size = size * 40 / 100; } } else { size = MAX_STREAM_BUFFER_SIZE; } - - // Clamp to the configured maximum, align up for safety, and ensure a sane minimum so - // region_size (stream_buffer_size / NUM_SYNCS) never becomes zero. - const VkDeviceSize aligned = (std::min)(Common::AlignUp(size, MAX_ALIGNMENT), MAX_STREAM_BUFFER_SIZE); - const VkDeviceSize min_size = MAX_ALIGNMENT * StagingBufferPool::NUM_SYNCS; - return static_cast((std::max)(aligned, min_size)); + return (std::min)(Common::AlignUp(size, MAX_ALIGNMENT), MAX_STREAM_BUFFER_SIZE); } } // Anonymous namespace @@ -116,53 +106,31 @@ void StagingBufferPool::TickFrame() { } StagingBufferRef StagingBufferPool::GetStreamBuffer(size_t size) { - const size_t aligned_size = Common::AlignUp(size, MAX_ALIGNMENT); - const bool wraps = iterator + size >= stream_buffer_size; - const size_t new_iterator = - wraps ? aligned_size : Common::AlignUp(iterator + size, MAX_ALIGNMENT); - const size_t begin_region = wraps ? 0 : Region(iterator); - const size_t last_byte = new_iterator == 0 ? 0 : new_iterator - 1; - const size_t end_region = (std::min)(Region(last_byte) + 1, NUM_SYNCS); - const size_t guard_begin = (std::min)(Region(free_iterator) + 1, NUM_SYNCS); - - if (!wraps) { - if (guard_begin < end_region && AreRegionsActive(guard_begin, end_region)) { - // Avoid waiting for the previous usages to be free - return GetStagingBuffer(size, MemoryUsage::Upload); - } - } else if (guard_begin < NUM_SYNCS && AreRegionsActive(guard_begin, NUM_SYNCS)) { + if (AreRegionsActive(Region(free_iterator) + 1, + (std::min)(Region(iterator + size) + 1, NUM_SYNCS))) { // Avoid waiting for the previous usages to be free return GetStagingBuffer(size, MemoryUsage::Upload); } - const u64 current_tick = scheduler.CurrentTick(); std::fill(sync_ticks.begin() + Region(used_iterator), sync_ticks.begin() + Region(iterator), current_tick); used_iterator = iterator; + free_iterator = (std::max)(free_iterator, iterator + size); - if (wraps) { + if (iterator + size >= stream_buffer_size) { std::fill(sync_ticks.begin() + Region(used_iterator), sync_ticks.begin() + NUM_SYNCS, current_tick); used_iterator = 0; iterator = 0; free_iterator = size; - const size_t head_last_byte = aligned_size == 0 ? 0 : aligned_size - 1; - const size_t head_end_region = (std::min)(Region(head_last_byte) + 1, NUM_SYNCS); - if (AreRegionsActive(0, head_end_region)) { + + if (AreRegionsActive(0, Region(size) + 1)) { // Avoid waiting for the previous usages to be free return GetStagingBuffer(size, MemoryUsage::Upload); } } - - std::fill(sync_ticks.begin() + begin_region, sync_ticks.begin() + end_region, current_tick); - - const size_t offset = wraps ? 0 : iterator; - iterator = new_iterator; - - if (!wraps) { - free_iterator = (std::max)(free_iterator, offset + size); - } - + const size_t offset = iterator; + iterator = Common::AlignUp(iterator + size, MAX_ALIGNMENT); return StagingBufferRef{ .buffer = *stream_buffer, .offset = static_cast(offset), From 3656253262fd1c22e5a1ab43ae373173e571e11d Mon Sep 17 00:00:00 2001 From: crueter Date: Fri, 10 Oct 2025 05:59:31 +0200 Subject: [PATCH 17/17] [acc] do not consider system profile as orphaned (#2708) Profile 00000000000000000000000000000000 is apparently needed for acnh, etc Signed-off-by: crueter Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2708 --- src/core/hle/service/acc/profile_manager.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp index 4a892f7c65..12ea5f7aa1 100644 --- a/src/core/hle/service/acc/profile_manager.cpp +++ b/src/core/hle/service/acc/profile_manager.cpp @@ -509,6 +509,9 @@ std::vector ProfileManager::FindOrphanedProfiles() good_uuids.emplace_back(uuid_string); } + // used for acnh, etc + good_uuids.emplace_back("00000000000000000000000000000000"); + // TODO: fetch save_id programmatically const auto path = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) / "user/save/0000000000000000";