From 2f86f95c3f79681437e191f3d9c5365f9d0fd2ac Mon Sep 17 00:00:00 2001 From: Elad <18193363+elad335@users.noreply.github.com> Date: Wed, 22 Oct 2025 07:49:35 +0300 Subject: [PATCH 01/25] vm: Deallocate memory early, check no PS3 memory leaks --- rpcs3/Emu/Cell/SPUDisAsm.cpp | 2 +- rpcs3/Emu/Memory/vm.cpp | 19 ++++++++++++++++++- rpcs3/rpcs3qt/debugger_frame.cpp | 3 +++ rpcs3/rpcs3qt/memory_viewer_panel.cpp | 11 ++++++++++- rpcs3/rpcs3qt/memory_viewer_panel.h | 6 ++++-- 5 files changed, 36 insertions(+), 5 deletions(-) diff --git a/rpcs3/Emu/Cell/SPUDisAsm.cpp b/rpcs3/Emu/Cell/SPUDisAsm.cpp index 645a79969f..9529babb41 100644 --- a/rpcs3/Emu/Cell/SPUDisAsm.cpp +++ b/rpcs3/Emu/Cell/SPUDisAsm.cpp @@ -15,7 +15,7 @@ u32 SPUDisAsm::disasm(u32 pc) { last_opcode.clear(); - if (pc < m_start_pc || pc >= SPU_LS_SIZE) + if (!m_shm || pc < m_start_pc || pc >= SPU_LS_SIZE) { return 0; } diff --git a/rpcs3/Emu/Memory/vm.cpp b/rpcs3/Emu/Memory/vm.cpp index a3c98dabbc..5441176d6a 100644 --- a/rpcs3/Emu/Memory/vm.cpp +++ b/rpcs3/Emu/Memory/vm.cpp @@ -1329,6 +1329,16 @@ namespace vm std::vector> event_data; ensure(size == _page_unmap(it->first, size, this->flags, it->second.second.get(), unmapped ? *unmapped : event_data)); + if (it->second.second && addr < 0xE0000000) + { + if (it->second.second.use_count() != 1) + { + fmt::throw_exception("External memory usage at block 0x%x (addr=0x%x, size=0x%x)", this->addr, it->first, size); + } + + it->second.second.reset(); + } + it = next; } @@ -1338,6 +1348,8 @@ namespace vm #ifdef _WIN32 m_common->unmap_critical(vm::get_super_ptr(addr)); #endif + ensure(m_common.use_count() == 1); + m_common.reset(); } return true; @@ -1349,6 +1361,7 @@ namespace vm block_t::~block_t() { ensure(!is_valid()); + ensure(!m_common || m_common.use_count() == 1); } u32 block_t::alloc(const u32 orig_size, const std::shared_ptr* src, u32 align, u64 flags) @@ -2244,7 +2257,11 @@ namespace vm for (auto& block : g_locations) { - if (block) _unmap_block(block); + if (block) + { + _unmap_block(block); + ensure(block.use_count() == 1); + } } g_locations.clear(); diff --git a/rpcs3/rpcs3qt/debugger_frame.cpp b/rpcs3/rpcs3qt/debugger_frame.cpp index b85aa2859e..8eb4bce3fa 100644 --- a/rpcs3/rpcs3qt/debugger_frame.cpp +++ b/rpcs3/rpcs3qt/debugger_frame.cpp @@ -241,6 +241,9 @@ void debugger_frame::closeEvent(QCloseEvent* event) QDockWidget::closeEvent(event); Q_EMIT DebugFrameClosed(); + + m_spu_disasm_memory.reset(); + m_cpu.reset(); } void debugger_frame::showEvent(QShowEvent* event) diff --git a/rpcs3/rpcs3qt/memory_viewer_panel.cpp b/rpcs3/rpcs3qt/memory_viewer_panel.cpp index 533dccc4d2..951ff41087 100644 --- a/rpcs3/rpcs3qt/memory_viewer_panel.cpp +++ b/rpcs3/rpcs3qt/memory_viewer_panel.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -732,7 +733,7 @@ void* memory_viewer_panel::to_ptr(u32 addr, u32 size) const } case thread_class::spu: { - if (size <= SPU_LS_SIZE && SPU_LS_SIZE - size >= (addr % SPU_LS_SIZE)) + if (m_spu_shm && size <= SPU_LS_SIZE && SPU_LS_SIZE - size >= (addr % SPU_LS_SIZE)) { return m_spu_shm->map_self() + (addr % SPU_LS_SIZE); } @@ -961,6 +962,14 @@ void memory_viewer_panel::keyPressEvent(QKeyEvent* event) QDialog::keyPressEvent(event); } +void memory_viewer_panel::closeEvent(QCloseEvent* event) +{ + event->accept(); + m_spu_shm.reset(); + m_disasm.reset(); + m_get_cpu = [](){ return std::add_pointer_t{}; }; +} + void memory_viewer_panel::ShowImage(QWidget* parent, u32 addr, color_format format, u32 width, u32 height, bool flipv) const { u32 texel_bytes = 4; diff --git a/rpcs3/rpcs3qt/memory_viewer_panel.h b/rpcs3/rpcs3qt/memory_viewer_panel.h index 756323be97..935560cc2a 100644 --- a/rpcs3/rpcs3qt/memory_viewer_panel.h +++ b/rpcs3/rpcs3qt/memory_viewer_panel.h @@ -19,6 +19,7 @@ class QLabel; class QThread; class QHBoxLayout; class QKeyEvent; +class QCloseEvent; class cpu_thread; class CPUDisAsm; @@ -95,10 +96,10 @@ private: QHBoxLayout* m_hbox_mem_panel = nullptr; QThread* m_search_thread = nullptr; - const std::function m_get_cpu; + std::function m_get_cpu; const thread_class m_type; const std::add_pointer_t m_rsx; - const std::shared_ptr m_spu_shm; + std::shared_ptr m_spu_shm; const u32 m_addr_mask; std::shared_ptr m_disasm; @@ -119,6 +120,7 @@ private: u64 OnSearch(std::string wstr, u32 mode); void keyPressEvent(QKeyEvent* event) override; + void closeEvent(QCloseEvent* event) override; }; // Lifetime management with IDM From 311e7a99923b6e274279d7db6a879e21f3044039 Mon Sep 17 00:00:00 2001 From: kd-11 Date: Wed, 22 Oct 2025 10:39:03 +0300 Subject: [PATCH 02/25] vk: Make best-effort attempt to utilize the low-latency pool --- rpcs3/Emu/RSX/Common/ring_buffer_helper.h | 4 +-- rpcs3/Emu/RSX/VK/vkutils/data_heap.cpp | 30 ++++++++++++++++++++--- rpcs3/Emu/RSX/VK/vkutils/data_heap.h | 1 + rpcs3/Emu/RSX/VK/vkutils/memory.cpp | 10 ++++++++ rpcs3/Emu/RSX/VK/vkutils/memory.h | 1 + 5 files changed, 41 insertions(+), 5 deletions(-) diff --git a/rpcs3/Emu/RSX/Common/ring_buffer_helper.h b/rpcs3/Emu/RSX/Common/ring_buffer_helper.h index 398b355e39..2aebd546c3 100644 --- a/rpcs3/Emu/RSX/Common/ring_buffer_helper.h +++ b/rpcs3/Emu/RSX/Common/ring_buffer_helper.h @@ -76,9 +76,9 @@ public: data_heap(const data_heap&) = delete; data_heap(data_heap&&) = delete; - void init(usz heap_size, const char* buffer_name = "unnamed", usz min_guard_size=0x10000) + void init(usz heap_size, const char* buffer_name = nullptr, usz min_guard_size=0x10000) { - m_name = const_cast(buffer_name); + m_name = const_cast(buffer_name ? buffer_name : ""); m_size = heap_size; m_put_pos = 0; diff --git a/rpcs3/Emu/RSX/VK/vkutils/data_heap.cpp b/rpcs3/Emu/RSX/VK/vkutils/data_heap.cpp index 7493049639..ba1b4e79c1 100644 --- a/rpcs3/Emu/RSX/VK/vkutils/data_heap.cpp +++ b/rpcs3/Emu/RSX/VK/vkutils/data_heap.cpp @@ -22,12 +22,17 @@ namespace vk if ((flags & heap_pool_low_latency) && g_cfg.video.vk.use_rebar_upload_heap) { // Prefer uploading to BAR if low latency is desired. - m_prefer_writethrough = memory_map.device_bar_total_bytes > (2048ull * 0x100000); + const int max_usage = memory_map.device_bar_total_bytes <= (256 * 0x100000) ? 75 : 90; + m_prefer_writethrough = can_allocate_heap(memory_map.device_bar, size, max_usage); // Log it - if (m_prefer_writethrough && name) + if (m_prefer_writethrough) { - rsx_log.notice("Data heap %s will attempt to use Re-BAR memory", name); + rsx_log.notice("Data heap %s will attempt to use Re-BAR memory", m_name); + } + else + { + rsx_log.warning("Could not fit heap '%s' into Re-BAR memory", m_name); } } @@ -86,6 +91,17 @@ namespace vk VkBufferUsageFlags usage = heap->info.usage; const auto& memory_map = g_render_device->get_memory_mapping(); + if (m_prefer_writethrough) + { + const int max_usage = memory_map.device_bar_total_bytes <= (256 * 0x100000) ? 75 : 90; + m_prefer_writethrough = can_allocate_heap(memory_map.device_bar, aligned_new_size, max_usage); + + if (!m_prefer_writethrough) + { + rsx_log.warning("Could not fit heap '%s' into Re-BAR memory during reallocation", m_name); + } + } + VkFlags memory_flags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; auto memory_index = m_prefer_writethrough ? memory_map.device_bar : memory_map.host_visible_coherent; @@ -116,6 +132,14 @@ namespace vk return true; } + bool data_heap::can_allocate_heap(const vk::memory_type_info& target_heap, usz size, int max_usage_percent) + { + const auto current_usage = vmm_get_application_memory_usage(target_heap); + const auto after_usage = current_usage + size; + const auto limit = (target_heap.total_bytes() * max_usage_percent) / 100; + return after_usage < limit; + } + void* data_heap::map(usz offset, usz size) { if (!_ptr) diff --git a/rpcs3/Emu/RSX/VK/vkutils/data_heap.h b/rpcs3/Emu/RSX/VK/vkutils/data_heap.h index e804bcdc59..6732f639dc 100644 --- a/rpcs3/Emu/RSX/VK/vkutils/data_heap.h +++ b/rpcs3/Emu/RSX/VK/vkutils/data_heap.h @@ -32,6 +32,7 @@ namespace vk protected: bool grow(usz size) override; + bool can_allocate_heap(const vk::memory_type_info& target_heap, usz size, int max_usage_percent); public: std::unique_ptr heap; diff --git a/rpcs3/Emu/RSX/VK/vkutils/memory.cpp b/rpcs3/Emu/RSX/VK/vkutils/memory.cpp index ac7010f057..80ffc50295 100644 --- a/rpcs3/Emu/RSX/VK/vkutils/memory.cpp +++ b/rpcs3/Emu/RSX/VK/vkutils/memory.cpp @@ -44,6 +44,16 @@ namespace vk return type_ids.size(); } + u64 memory_type_info::total_bytes() const + { + u64 result = 0; + for (const auto& size : type_sizes) + { + result += size; + } + return result; + } + memory_type_info::operator bool() const { return !type_ids.empty(); diff --git a/rpcs3/Emu/RSX/VK/vkutils/memory.h b/rpcs3/Emu/RSX/VK/vkutils/memory.h index eda4035ade..83e09cdd30 100644 --- a/rpcs3/Emu/RSX/VK/vkutils/memory.h +++ b/rpcs3/Emu/RSX/VK/vkutils/memory.h @@ -49,6 +49,7 @@ namespace vk const_iterator end() const; u32 first() const; size_t count() const; + u64 total_bytes() const; operator bool() const; bool operator == (const memory_type_info& other) const; From ae30cb5557aa4ba85376f6dcdab430f76a679d40 Mon Sep 17 00:00:00 2001 From: Elad <18193363+elad335@users.noreply.github.com> Date: Thu, 23 Oct 2025 17:27:06 +0300 Subject: [PATCH 03/25] SaveStates/SPU-LLVM: Fix SPU Access Violations on load --- rpcs3/Emu/Cell/SPUDisAsm.cpp | 2 +- rpcs3/Emu/Cell/SPULLVMRecompiler.cpp | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/rpcs3/Emu/Cell/SPUDisAsm.cpp b/rpcs3/Emu/Cell/SPUDisAsm.cpp index 9529babb41..645a79969f 100644 --- a/rpcs3/Emu/Cell/SPUDisAsm.cpp +++ b/rpcs3/Emu/Cell/SPUDisAsm.cpp @@ -15,7 +15,7 @@ u32 SPUDisAsm::disasm(u32 pc) { last_opcode.clear(); - if (!m_shm || pc < m_start_pc || pc >= SPU_LS_SIZE) + if (pc < m_start_pc || pc >= SPU_LS_SIZE) { return 0; } diff --git a/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp b/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp index fa1d782ee4..b275773e8b 100644 --- a/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp +++ b/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp @@ -2231,6 +2231,12 @@ public: { for (auto& [a, b] : m_blocks) { + if (has_gpr_memory_barriers) + { + // Dive deeper and inspect GPR store barriers + break; + } + // Check if the store occurs before any barrier in the block if (b.store[i] && b.store[i] != bs && b.store_context_first_id[i] == 1) { From 73c984a63775aa30f63e623369fe939b7e14df1f Mon Sep 17 00:00:00 2001 From: kd-11 Date: Fri, 24 Oct 2025 13:40:07 +0300 Subject: [PATCH 04/25] vk: Filter out re-bar usage from memory pressure watchdog --- rpcs3/Emu/RSX/VK/vkutils/device.cpp | 2 ++ rpcs3/Emu/RSX/VK/vkutils/device.h | 2 ++ rpcs3/Emu/RSX/VK/vkutils/memory.cpp | 23 +++++++++++++++++++++++ rpcs3/Emu/RSX/VK/vkutils/memory.h | 8 ++++++++ 4 files changed, 35 insertions(+) diff --git a/rpcs3/Emu/RSX/VK/vkutils/device.cpp b/rpcs3/Emu/RSX/VK/vkutils/device.cpp index f5f2745ab4..e29a9b66c2 100644 --- a/rpcs3/Emu/RSX/VK/vkutils/device.cpp +++ b/rpcs3/Emu/RSX/VK/vkutils/device.cpp @@ -1010,6 +1010,8 @@ namespace vk .heap = memory_properties.memoryHeaps[i], .types = {} }); + + result.heaps.push_back({ i, memory_properties.memoryHeaps[i].flags, memory_properties.memoryHeaps[i].size }); } for (u32 i = 0; i < memory_properties.memoryTypeCount; i++) diff --git a/rpcs3/Emu/RSX/VK/vkutils/device.h b/rpcs3/Emu/RSX/VK/vkutils/device.h index 0511802aac..c121d1b20a 100644 --- a/rpcs3/Emu/RSX/VK/vkutils/device.h +++ b/rpcs3/Emu/RSX/VK/vkutils/device.h @@ -30,6 +30,8 @@ namespace vk struct memory_type_mapping { + std::vector heaps; + memory_type_info host_visible_coherent; memory_type_info device_local; memory_type_info device_bar; diff --git a/rpcs3/Emu/RSX/VK/vkutils/memory.cpp b/rpcs3/Emu/RSX/VK/vkutils/memory.cpp index 80ffc50295..2b84e1f61c 100644 --- a/rpcs3/Emu/RSX/VK/vkutils/memory.cpp +++ b/rpcs3/Emu/RSX/VK/vkutils/memory.cpp @@ -200,6 +200,23 @@ namespace vk // Allow fastest possible allocation on start set_fastest_allocation_flags(); + + // Determine the rebar heap. We will exclude it from stats + const auto& memory_map = dev.get_memory_mapping(); + if (memory_map.device_bar_total_bytes != + memory_map.device_local_total_bytes) + { + for (u32 i = 0; i < ::size32(memory_map.heaps); ++i) + { + const auto& heap = memory_map.heaps[i]; + if ((heap.flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) && + heap.size == memory_map.device_bar_total_bytes) + { + m_rebar_heap_idx = i; + break; + } + } + } } void mem_allocator_vma::destroy() @@ -313,6 +330,12 @@ namespace vk { vmaGetHeapBudgets(m_allocator, stats.data()); + // Filter out the Re-BAR heap + if (::size32(stats) > m_rebar_heap_idx) + { + stats[m_rebar_heap_idx].budget = 0; + } + float max_usage = 0.f; for (const auto& info : stats) { diff --git a/rpcs3/Emu/RSX/VK/vkutils/memory.h b/rpcs3/Emu/RSX/VK/vkutils/memory.h index 83e09cdd30..abe0c75582 100644 --- a/rpcs3/Emu/RSX/VK/vkutils/memory.h +++ b/rpcs3/Emu/RSX/VK/vkutils/memory.h @@ -59,6 +59,13 @@ namespace vk void rebalance(); }; + struct memory_heap_info + { + u32 index; + u32 flags; + u64 size; + }; + class mem_allocator_base { public: @@ -113,6 +120,7 @@ namespace vk private: VmaAllocator m_allocator; std::array stats; + u32 m_rebar_heap_idx = UINT32_MAX; }; From 3c1ebe9f4ad940b6f08de641f7b309be39fff92a Mon Sep 17 00:00:00 2001 From: Live session user Date: Fri, 3 Oct 2025 21:08:37 -0700 Subject: [PATCH 05/25] [openal-soft] Fix malloc/free in fmtlib --- 3rdparty/OpenAL/openal-soft | 2 +- 3rdparty/OpenAL/openal-soft.vcxproj | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/3rdparty/OpenAL/openal-soft b/3rdparty/OpenAL/openal-soft index dc7d7054a5..0e5e98e4ac 160000 --- a/3rdparty/OpenAL/openal-soft +++ b/3rdparty/OpenAL/openal-soft @@ -1 +1 @@ -Subproject commit dc7d7054a5b4f3bec1dc23a42fd616a0847af948 +Subproject commit 0e5e98e4ac8adae92e4f7653dd6eee17aa9c8791 diff --git a/3rdparty/OpenAL/openal-soft.vcxproj b/3rdparty/OpenAL/openal-soft.vcxproj index d309093527..7782ddc940 100644 --- a/3rdparty/OpenAL/openal-soft.vcxproj +++ b/3rdparty/OpenAL/openal-soft.vcxproj @@ -49,11 +49,11 @@ call vsdevcmd.bat -arch=amd64 cd "$(SolutionDir)build\tmp\$(ProjectName)-$(Configuration)-$(Platform)" - cmake -G Ninja -DCMAKE_CXX_COMPILER="cl.exe" -DCMAKE_C_COMPILER="cl.exe" -DCMAKE_BUILD_TYPE="Release" -DCMAKE_INSTALL_PREFIX="./Release" -DCMAKE_SYSTEM_VERSION=10.0 -DLIBTYPE=STATIC -DFORCE_STATIC_VCRT=true -DALSOFT_UTILS=false -DALSOFT_EXAMPLES=false -DALSOFT_INSTALL=false -DALSOFT_INSTALL_CONFIG=false -DALSOFT_INSTALL_HRTF_DATA=false -DALSOFT_INSTALL_AMBDEC_PRESETS=false -DALSOFT_INSTALL_EXAMPLES=false -DALSOFT_INSTALL_UTILS=false "$(SolutionDir)3rdparty\OpenAL\openal-soft" + cmake -G Ninja -DCMAKE_CXX_COMPILER="cl.exe" -DCMAKE_C_COMPILER="cl.exe" -DCMAKE_BUILD_TYPE="Release" -DCMAKE_INSTALL_PREFIX="./Release" -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded -DCMAKE_SYSTEM_VERSION=10.0 -DLIBTYPE=STATIC -DFORCE_STATIC_VCRT=true -DALSOFT_UTILS=false -DALSOFT_EXAMPLES=false -DALSOFT_INSTALL=false -DALSOFT_INSTALL_CONFIG=false -DALSOFT_INSTALL_HRTF_DATA=false -DALSOFT_INSTALL_AMBDEC_PRESETS=false -DALSOFT_INSTALL_EXAMPLES=false -DALSOFT_INSTALL_UTILS=false "$(SolutionDir)3rdparty\OpenAL\openal-soft" call vsdevcmd.bat -arch=amd64 cd "$(SolutionDir)build\tmp\$(ProjectName)-$(Configuration)-$(Platform)" - cmake -G Ninja -DCMAKE_CXX_COMPILER="cl.exe" -DCMAKE_C_COMPILER="cl.exe" -DCMAKE_BUILD_TYPE="Debug" -DCMAKE_INSTALL_PREFIX="./Debug" -DCMAKE_SYSTEM_VERSION=10.0 -DLIBTYPE=STATIC -DALSOFT_UTILS=false -DALSOFT_EXAMPLES=false -DALSOFT_INSTALL=false -DALSOFT_INSTALL_CONFIG=false -DALSOFT_INSTALL_HRTF_DATA=false -DALSOFT_INSTALL_AMBDEC_PRESETS=false -DALSOFT_INSTALL_EXAMPLES=false -DALSOFT_INSTALL_UTILS=false "$(SolutionDir)3rdparty\OpenAL\openal-soft" + cmake -G Ninja -DCMAKE_CXX_COMPILER="cl.exe" -DCMAKE_C_COMPILER="cl.exe" -DCMAKE_BUILD_TYPE="Debug" -DCMAKE_INSTALL_PREFIX="./Debug" -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDebug -DCMAKE_SYSTEM_VERSION=10.0 -DLIBTYPE=STATIC -DALSOFT_UTILS=false -DALSOFT_EXAMPLES=false -DALSOFT_INSTALL=false -DALSOFT_INSTALL_CONFIG=false -DALSOFT_INSTALL_HRTF_DATA=false -DALSOFT_INSTALL_AMBDEC_PRESETS=false -DALSOFT_INSTALL_EXAMPLES=false -DALSOFT_INSTALL_UTILS=false "$(SolutionDir)3rdparty\OpenAL\openal-soft" echo Copying.. @@ -106,4 +106,4 @@ - \ No newline at end of file + From dc53a3ba42107afff4968e235eeb7e5e90749970 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Fri, 10 Oct 2025 10:00:06 +0200 Subject: [PATCH 06/25] Update linux docker image to v.1.7 --- .ci/deploy-linux.sh | 2 +- .github/workflows/rpcs3.yml | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.ci/deploy-linux.sh b/.ci/deploy-linux.sh index f8c3d849c3..fe6174fc78 100755 --- a/.ci/deploy-linux.sh +++ b/.ci/deploy-linux.sh @@ -14,7 +14,7 @@ if [ "$DEPLOY_APPIMAGE" = "true" ]; then curl -fsSLo linuxdeploy-plugin-checkrt.sh https://github.com/darealshinji/linuxdeploy-plugin-checkrt/releases/download/continuous/linuxdeploy-plugin-checkrt.sh chmod +x ./linuxdeploy-plugin-checkrt.sh - export EXTRA_PLATFORM_PLUGINS="libqwayland-egl.so;libqwayland-generic.so" + export EXTRA_PLATFORM_PLUGINS="libqwayland.so" export EXTRA_QT_PLUGINS="svg;wayland-decoration-client;wayland-graphics-integration-client;wayland-shell-integration;waylandcompositor" APPIMAGE_EXTRACT_AND_RUN=1 linuxdeploy --appdir AppDir --plugin qt --plugin checkrt diff --git a/.github/workflows/rpcs3.yml b/.github/workflows/rpcs3.yml index 0d40d01437..dd9c6ea736 100644 --- a/.github/workflows/rpcs3.yml +++ b/.github/workflows/rpcs3.yml @@ -30,23 +30,23 @@ jobs: matrix: include: - os: ubuntu-24.04 - docker_img: "rpcs3/rpcs3-ci-jammy:1.6" + docker_img: "rpcs3/rpcs3-ci-jammy:1.7" build_sh: "/rpcs3/.ci/build-linux.sh" compiler: clang UPLOAD_COMMIT_HASH: d812f1254a1157c80fd402f94446310560f54e5f UPLOAD_REPO_FULL_NAME: "rpcs3/rpcs3-binaries-linux" - os: ubuntu-24.04 - docker_img: "rpcs3/rpcs3-ci-jammy:1.6" + docker_img: "rpcs3/rpcs3-ci-jammy:1.7" build_sh: "/rpcs3/.ci/build-linux.sh" compiler: gcc - os: ubuntu-24.04-arm - docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.6" + docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.7" build_sh: "/rpcs3/.ci/build-linux-aarch64.sh" compiler: clang UPLOAD_COMMIT_HASH: a1d35836e8d45bfc6f63c26f0a3e5d46ef622fe1 UPLOAD_REPO_FULL_NAME: "rpcs3/rpcs3-binaries-linux-arm64" - os: ubuntu-24.04-arm - docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.6" + docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.7" build_sh: "/rpcs3/.ci/build-linux-aarch64.sh" compiler: gcc name: RPCS3 Linux ${{ matrix.os }} ${{ matrix.compiler }} From 3c401e7b3dee35cffeef464f66ddf1afa93cacc3 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Fri, 17 Oct 2025 10:12:43 +0200 Subject: [PATCH 07/25] Update fusion to 1.2.9 --- 3rdparty/fusion/fusion | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/fusion/fusion b/3rdparty/fusion/fusion index 066d4a63b2..759ac5d698 160000 --- a/3rdparty/fusion/fusion +++ b/3rdparty/fusion/fusion @@ -1 +1 @@ -Subproject commit 066d4a63b2c714b20b0a8073a01fda7c5c6763f6 +Subproject commit 759ac5d698baefca53f1975a0bb1d2dcbdb9f836 From 90fb81d1d95eed008ae92fd3fb822dedd0f82855 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Mon, 3 Mar 2025 18:41:42 +0100 Subject: [PATCH 08/25] Use move semantics for present_frame --- rpcs3/Emu/RSX/GL/GLPresent.cpp | 2 +- rpcs3/Emu/RSX/GSFrameBase.h | 2 +- rpcs3/Emu/RSX/VK/VKPresent.cpp | 4 ++-- rpcs3/rpcs3qt/gs_frame.cpp | 4 ++-- rpcs3/rpcs3qt/gs_frame.h | 2 +- rpcs3/util/video_provider.cpp | 2 +- rpcs3/util/video_provider.h | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/rpcs3/Emu/RSX/GL/GLPresent.cpp b/rpcs3/Emu/RSX/GL/GLPresent.cpp index 238999ea98..ac9a8988d9 100644 --- a/rpcs3/Emu/RSX/GL/GLPresent.cpp +++ b/rpcs3/Emu/RSX/GL/GLPresent.cpp @@ -296,7 +296,7 @@ void GLGSRender::flip(const rsx::display_flip_info_t& info) } else { - m_frame->present_frame(sshot_frame, buffer_width * 4, buffer_width, buffer_height, false); + m_frame->present_frame(std::move(sshot_frame), buffer_width * 4, buffer_width, buffer_height, false); } } diff --git a/rpcs3/Emu/RSX/GSFrameBase.h b/rpcs3/Emu/RSX/GSFrameBase.h index ef17378dd3..69d2832c55 100644 --- a/rpcs3/Emu/RSX/GSFrameBase.h +++ b/rpcs3/Emu/RSX/GSFrameBase.h @@ -31,6 +31,6 @@ public: virtual display_handle_t handle() const = 0; virtual bool can_consume_frame() const = 0; - virtual void present_frame(std::vector& data, u32 pitch, u32 width, u32 height, bool is_bgra) const = 0; + virtual void present_frame(std::vector&& data, u32 pitch, u32 width, u32 height, bool is_bgra) const = 0; virtual void take_screenshot(std::vector&& sshot_data, u32 sshot_width, u32 sshot_height, bool is_bgra) = 0; }; diff --git a/rpcs3/Emu/RSX/VK/VKPresent.cpp b/rpcs3/Emu/RSX/VK/VKPresent.cpp index fa729f319c..86c814d97f 100644 --- a/rpcs3/Emu/RSX/VK/VKPresent.cpp +++ b/rpcs3/Emu/RSX/VK/VKPresent.cpp @@ -704,7 +704,7 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info) image_to_flip->pop_layout(*m_current_command_buffer); flush_command_queue(true); - auto src = sshot_vkbuf.map(0, sshot_size); + const auto src = sshot_vkbuf.map(0, sshot_size); std::vector sshot_frame(sshot_size); memcpy(sshot_frame.data(), src, sshot_size); sshot_vkbuf.unmap(); @@ -717,7 +717,7 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info) } else { - m_frame->present_frame(sshot_frame, buffer_width * 4, buffer_width, buffer_height, is_bgra); + m_frame->present_frame(std::move(sshot_frame), buffer_width * 4, buffer_width, buffer_height, is_bgra); } } } diff --git a/rpcs3/rpcs3qt/gs_frame.cpp b/rpcs3/rpcs3qt/gs_frame.cpp index 999c322fc9..5298ac2b98 100644 --- a/rpcs3/rpcs3qt/gs_frame.cpp +++ b/rpcs3/rpcs3qt/gs_frame.cpp @@ -830,10 +830,10 @@ bool gs_frame::can_consume_frame() const return video_provider.can_consume_frame(); } -void gs_frame::present_frame(std::vector& data, u32 pitch, u32 width, u32 height, bool is_bgra) const +void gs_frame::present_frame(std::vector&& data, u32 pitch, u32 width, u32 height, bool is_bgra) const { utils::video_provider& video_provider = g_fxo->get(); - video_provider.present_frame(data, pitch, width, height, is_bgra); + video_provider.present_frame(std::move(data), pitch, width, height, is_bgra); } void gs_frame::take_screenshot(std::vector&& data, u32 sshot_width, u32 sshot_height, bool is_bgra) diff --git a/rpcs3/rpcs3qt/gs_frame.h b/rpcs3/rpcs3qt/gs_frame.h index efcc131396..a00773240e 100644 --- a/rpcs3/rpcs3qt/gs_frame.h +++ b/rpcs3/rpcs3qt/gs_frame.h @@ -79,7 +79,7 @@ public: bool get_mouse_lock_state(); bool can_consume_frame() const override; - void present_frame(std::vector& data, u32 pitch, u32 width, u32 height, bool is_bgra) const override; + void present_frame(std::vector&& data, u32 pitch, u32 width, u32 height, bool is_bgra) const override; void take_screenshot(std::vector&& data, u32 sshot_width, u32 sshot_height, bool is_bgra) override; protected: diff --git a/rpcs3/util/video_provider.cpp b/rpcs3/util/video_provider.cpp index fe305c11de..8386224cb2 100644 --- a/rpcs3/util/video_provider.cpp +++ b/rpcs3/util/video_provider.cpp @@ -127,7 +127,7 @@ namespace utils return pts > m_last_video_pts_incoming; } - void video_provider::present_frame(std::vector& data, u32 pitch, u32 width, u32 height, bool is_bgra) + void video_provider::present_frame(std::vector&& data, u32 pitch, u32 width, u32 height, bool is_bgra) { if (!m_active) { diff --git a/rpcs3/util/video_provider.h b/rpcs3/util/video_provider.h index e21b5185ee..bff3f92b19 100644 --- a/rpcs3/util/video_provider.h +++ b/rpcs3/util/video_provider.h @@ -21,7 +21,7 @@ namespace utils void set_pause_time_us(usz pause_time_us); bool can_consume_frame(); - void present_frame(std::vector& data, u32 pitch, u32 width, u32 height, bool is_bgra); + void present_frame(std::vector&& data, u32 pitch, u32 width, u32 height, bool is_bgra); void present_samples(const u8* buf, u32 sample_count, u16 channels); From dd40f49e5f8733e4a0a960383be95d75eaf32335 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Mon, 3 Mar 2025 23:08:40 +0100 Subject: [PATCH 09/25] Add new option for recording overlays --- rpcs3/Emu/system_config.h | 1 + 1 file changed, 1 insertion(+) diff --git a/rpcs3/Emu/system_config.h b/rpcs3/Emu/system_config.h index 9e8f9612f6..dd3182cd11 100644 --- a/rpcs3/Emu/system_config.h +++ b/rpcs3/Emu/system_config.h @@ -182,6 +182,7 @@ struct cfg_root : cfg::node cfg::_bool disable_msl_fast_math{ this, "Disable MSL Fast Math", false }; cfg::_bool disable_async_host_memory_manager{ this, "Disable Asynchronous Memory Manager", false, true }; cfg::_enum output_scaling{ this, "Output Scaling Mode", output_scaling_mode::bilinear, true }; + cfg::_bool record_with_overlays{ this, "Record With Overlays", true, true }; struct node_vk : cfg::node { From 9550d5b67dd3cf6318ba2acfd0689314b40486a7 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Mon, 3 Mar 2025 20:15:18 +0100 Subject: [PATCH 10/25] GL: allow to record overlays --- rpcs3/Emu/RSX/GL/GLDraw.cpp | 2 +- rpcs3/Emu/RSX/GL/GLGSRender.h | 2 + rpcs3/Emu/RSX/GL/GLPresent.cpp | 111 +++++++++++++++++++++---------- rpcs3/Emu/RSX/GL/glutils/image.h | 2 +- 4 files changed, 80 insertions(+), 37 deletions(-) diff --git a/rpcs3/Emu/RSX/GL/GLDraw.cpp b/rpcs3/Emu/RSX/GL/GLDraw.cpp index 955bfbd59b..213275b9c4 100644 --- a/rpcs3/Emu/RSX/GL/GLDraw.cpp +++ b/rpcs3/Emu/RSX/GL/GLDraw.cpp @@ -485,7 +485,7 @@ void GLGSRender::bind_texture_env() } else { - auto target = gl::get_target(current_fragment_program.get_texture_dimension(i)); + const auto target = gl::get_target(current_fragment_program.get_texture_dimension(i)); cmd->bind_texture(GL_FRAGMENT_TEXTURES_START + i, target, m_null_textures[target]->id()); if (current_fragment_program.texture_state.redirected_textures & (1 << i)) diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.h b/rpcs3/Emu/RSX/GL/GLGSRender.h index 51b126cc73..465fa51584 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.h +++ b/rpcs3/Emu/RSX/GL/GLGSRender.h @@ -137,6 +137,8 @@ class GLGSRender : public GSRender, public ::rsx::reports::ZCULL_control std::unique_ptr m_flip_tex_color[2]; // Present + gl::fbo m_sshot_fbo; + std::unique_ptr m_sshot_tex; std::unique_ptr m_upscaler; output_scaling_mode m_output_scaling = output_scaling_mode::bilinear; diff --git a/rpcs3/Emu/RSX/GL/GLPresent.cpp b/rpcs3/Emu/RSX/GL/GLPresent.cpp index ac9a8988d9..cb547bf203 100644 --- a/rpcs3/Emu/RSX/GL/GLPresent.cpp +++ b/rpcs3/Emu/RSX/GL/GLPresent.cpp @@ -204,7 +204,8 @@ void GLGSRender::flip(const rsx::display_flip_info_t& info) // Enable drawing to window backbuffer gl::screen.bind(); - gl::texture *image_to_flip = nullptr, *image_to_flip2 = nullptr; + gl::texture* image_to_flip = nullptr; + gl::texture* image_to_flip2 = nullptr; if (info.buffer < display_buffers_count && buffer_width && buffer_height) { @@ -276,15 +277,86 @@ void GLGSRender::flip(const rsx::display_flip_info_t& info) gl::screen.clear(gl::buffers::color); } + if (m_overlay_manager && m_overlay_manager->has_dirty()) + { + m_overlay_manager->lock_shared(); + + std::vector uids_to_dispose; + uids_to_dispose.reserve(m_overlay_manager->get_dirty().size()); + + for (const auto& view : m_overlay_manager->get_dirty()) + { + m_ui_renderer.remove_temp_resources(view->uid); + uids_to_dispose.push_back(view->uid); + } + + m_overlay_manager->unlock_shared(); + m_overlay_manager->dispose(uids_to_dispose); + } + + const auto render_overlays = [this, &cmd](gl::texture* dst, const areau& aspect_ratio) + { + if (m_overlay_manager && m_overlay_manager->has_visible()) + { + GLuint target = 0; + + if (dst) + { + m_sshot_fbo.bind(); + m_sshot_fbo.color = dst->id(); + target = dst->id(); + } + else + { + gl::screen.bind(); + } + + // Lock to avoid modification during run-update chain + std::lock_guard lock(*m_overlay_manager); + + for (const auto& view : m_overlay_manager->get_views()) + { + m_ui_renderer.run(cmd, aspect_ratio, target, *view.get()); + } + } + }; + if (image_to_flip) { if (g_user_asked_for_screenshot || (g_recording_mode != recording_mode::stopped && m_frame->can_consume_frame())) { + static const gl::pixel_pack_settings pack_settings{}; + + gl::texture* tex = image_to_flip; + + if (g_cfg.video.record_with_overlays) + { + m_sshot_fbo.create(); + m_sshot_tex = std::make_unique( + GLenum(image_to_flip->get_target()), + image_to_flip->width(), + image_to_flip->height(), + image_to_flip->depth(), + image_to_flip->levels(), + image_to_flip->samples(), + GLenum(image_to_flip->get_internal_format()), + image_to_flip->format_class()); + + tex = m_sshot_tex.get(); + + static const position3u offset{}; + gl::g_hw_blitter->copy_image(cmd, image_to_flip, tex, 0, 0, offset, offset, { tex->width(), tex->height(), 1 }); + + render_overlays(tex, areau(0, 0, image_to_flip->width(), image_to_flip->height())); + m_sshot_fbo.remove(); + } + std::vector sshot_frame(buffer_height * buffer_width * 4); glGetError(); - gl::pixel_pack_settings pack_settings{}; - image_to_flip->copy_to(sshot_frame.data(), gl::texture::format::rgba, gl::texture::type::ubyte, pack_settings); + tex->copy_to(sshot_frame.data(), gl::texture::format::rgba, gl::texture::type::ubyte, pack_settings); + + m_sshot_tex.reset(); if (GLenum err = glGetError(); err != GL_NO_ERROR) { @@ -349,38 +421,7 @@ void GLGSRender::flip(const rsx::display_flip_info_t& info) } } - if (m_overlay_manager) - { - if (m_overlay_manager->has_dirty()) - { - m_overlay_manager->lock_shared(); - - std::vector uids_to_dispose; - uids_to_dispose.reserve(m_overlay_manager->get_dirty().size()); - - for (const auto& view : m_overlay_manager->get_dirty()) - { - m_ui_renderer.remove_temp_resources(view->uid); - uids_to_dispose.push_back(view->uid); - } - - m_overlay_manager->unlock_shared(); - m_overlay_manager->dispose(uids_to_dispose); - } - - if (m_overlay_manager->has_visible()) - { - gl::screen.bind(); - - // Lock to avoid modification during run-update chain - std::lock_guard lock(*m_overlay_manager); - - for (const auto& view : m_overlay_manager->get_views()) - { - m_ui_renderer.run(cmd, areau(aspect_ratio), 0, *view.get()); - } - } - } + render_overlays(nullptr, areau(aspect_ratio)); if (g_cfg.video.debug_overlay) { diff --git a/rpcs3/Emu/RSX/GL/glutils/image.h b/rpcs3/Emu/RSX/GL/glutils/image.h index 896c3ee088..6617caa54c 100644 --- a/rpcs3/Emu/RSX/GL/glutils/image.h +++ b/rpcs3/Emu/RSX/GL/glutils/image.h @@ -353,7 +353,7 @@ namespace gl GLenum m_aspect_flags = 0; texture* m_image_data = nullptr; - GLenum component_swizzle[4]; + GLenum component_swizzle[4] {}; texture_view() = default; From 9c512849efde7315cc2dc0e9b79eab3f23a2edaa Mon Sep 17 00:00:00 2001 From: Megamouse Date: Mon, 3 Mar 2025 22:44:43 +0100 Subject: [PATCH 11/25] Vk: allow to record overlays --- rpcs3/Emu/RSX/VK/VKFramebuffer.cpp | 10 ++--- rpcs3/Emu/RSX/VK/VKPresent.cpp | 61 ++++++++++++++++++++++-------- 2 files changed, 50 insertions(+), 21 deletions(-) diff --git a/rpcs3/Emu/RSX/VK/VKFramebuffer.cpp b/rpcs3/Emu/RSX/VK/VKFramebuffer.cpp index e0bbad628a..d1fc5a4764 100644 --- a/rpcs3/Emu/RSX/VK/VKFramebuffer.cpp +++ b/rpcs3/Emu/RSX/VK/VKFramebuffer.cpp @@ -29,9 +29,9 @@ namespace vk vk::framebuffer_holder* get_framebuffer(VkDevice dev, u16 width, u16 height, VkBool32 has_input_attachments, VkRenderPass renderpass, const std::vector& image_list) { framebuffer_storage_key key(width, height, has_input_attachments); - auto &queue = g_framebuffers_cache[key.encoded]; + auto& queue = g_framebuffers_cache[key.encoded]; - for (auto &fbo : queue) + for (const auto& fbo : queue) { if (fbo->matches(image_list, width, height)) { @@ -42,7 +42,7 @@ namespace vk std::vector> image_views; image_views.reserve(image_list.size()); - for (auto &e : image_list) + for (const auto& e : image_list) { const VkImageSubresourceRange subres = { e->aspect(), 0, 1, 0, 1 }; image_views.push_back(std::make_unique(dev, e, VK_IMAGE_VIEW_TYPE_2D, vk::default_component_map, subres)); @@ -58,9 +58,9 @@ namespace vk vk::framebuffer_holder* get_framebuffer(VkDevice dev, u16 width, u16 height, VkBool32 has_input_attachments, VkRenderPass renderpass, VkFormat format, VkImage attachment) { framebuffer_storage_key key(width, height, has_input_attachments); - auto &queue = g_framebuffers_cache[key.encoded]; + auto& queue = g_framebuffers_cache[key.encoded]; - for (const auto &e : queue) + for (const auto& e : queue) { if (e->attachments[0]->info.image == attachment) { diff --git a/rpcs3/Emu/RSX/VK/VKPresent.cpp b/rpcs3/Emu/RSX/VK/VKPresent.cpp index 86c814d97f..8ecf4514a0 100644 --- a/rpcs3/Emu/RSX/VK/VKPresent.cpp +++ b/rpcs3/Emu/RSX/VK/VKPresent.cpp @@ -613,6 +613,21 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info) } } + const bool has_overlay = (m_overlay_manager && m_overlay_manager->has_visible()); + const auto render_overlays = [&](vk::framebuffer_holder* fbo, const areau& area) + { + if (!has_overlay) return; + + // Lock to avoid modification during run-update chain + auto ui_renderer = vk::get_overlay_pass(); + std::lock_guard lock(*m_overlay_manager); + + for (const auto& view : m_overlay_manager->get_views()) + { + ui_renderer->run(*m_current_command_buffer, area, fbo, single_target_pass, m_texture_upload_buffer_ring_info, *view.get()); + } + }; + if (image_to_flip) { const bool use_full_rgb_range_output = g_cfg.video.full_rgb_range_output.get(); @@ -699,9 +714,34 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info) copy_info.imageExtent.height = buffer_height; copy_info.imageExtent.depth = 1; - image_to_flip->push_layout(*m_current_command_buffer, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); - vk::copy_image_to_buffer(*m_current_command_buffer, image_to_flip, &sshot_vkbuf, copy_info); - image_to_flip->pop_layout(*m_current_command_buffer); + std::unique_ptr tmp_tex; + vk::image* image_to_copy = image_to_flip; + + if (g_cfg.video.record_with_overlays && has_overlay) + { + const auto key = vk::get_renderpass_key(m_swapchain->get_surface_format()); + single_target_pass = vk::get_renderpass(*m_device, key); + ensure(single_target_pass != VK_NULL_HANDLE); + + tmp_tex = std::make_unique(*m_device, m_device->get_memory_mapping().device_local, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + image_to_flip->type(), image_to_flip->format(), image_to_flip->width(), image_to_flip->height(), 1, 1, image_to_flip->layers(), VK_SAMPLE_COUNT_1_BIT, image_to_flip->current_layout, + VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, + 0, VMM_ALLOCATION_POOL_UNDEFINED); + + const areai rect = areai(0, 0, buffer_width, buffer_height); + vk::copy_image(*m_current_command_buffer, image_to_flip, tmp_tex.get(), rect, rect, 1); + + vk::framebuffer_holder* sshot_fbo = vk::get_framebuffer(*m_device, buffer_width, buffer_height, VK_FALSE, single_target_pass, { tmp_tex.get() }); + sshot_fbo->add_ref(); + render_overlays(sshot_fbo, areau(rect)); + sshot_fbo->release(); + + image_to_copy = tmp_tex.get(); + } + + image_to_copy->push_layout(*m_current_command_buffer, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + vk::copy_image_to_buffer(*m_current_command_buffer, image_to_copy, &sshot_vkbuf, copy_info); + image_to_copy->pop_layout(*m_current_command_buffer); flush_command_queue(true); const auto src = sshot_vkbuf.map(0, sshot_size); @@ -709,7 +749,7 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info) memcpy(sshot_frame.data(), src, sshot_size); sshot_vkbuf.unmap(); - const bool is_bgra = image_to_flip->format() == VK_FORMAT_B8G8R8A8_UNORM; + const bool is_bgra = image_to_copy->format() == VK_FORMAT_B8G8R8A8_UNORM; if (g_user_asked_for_screenshot.exchange(false)) { @@ -722,7 +762,6 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info) } } - const bool has_overlay = (m_overlay_manager && m_overlay_manager->has_visible()); if (g_cfg.video.debug_overlay || has_overlay) { if (target_layout != VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) @@ -754,17 +793,7 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info) direct_fbo->add_ref(); - if (has_overlay) - { - // Lock to avoid modification during run-update chain - auto ui_renderer = vk::get_overlay_pass(); - std::lock_guard lock(*m_overlay_manager); - - for (const auto& view : m_overlay_manager->get_views()) - { - ui_renderer->run(*m_current_command_buffer, areau(aspect_ratio), direct_fbo, single_target_pass, m_texture_upload_buffer_ring_info, *view.get()); - } - } + render_overlays(direct_fbo, areau(aspect_ratio)); if (g_cfg.video.debug_overlay) { From 0ca4f85dc7b416d06ef0e12e976783787adebbc0 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Mon, 3 Mar 2025 23:16:04 +0100 Subject: [PATCH 12/25] overlays: add record with overlays setting to home menu --- rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_settings.cpp | 1 + rpcs3/Emu/localized_string_id.h | 1 + rpcs3/rpcs3qt/localized_emu.h | 1 + 3 files changed, 3 insertions(+) diff --git a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_settings.cpp b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_settings.cpp index 5f7959f77b..4ff3bd7eae 100644 --- a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_settings.cpp +++ b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_settings.cpp @@ -104,6 +104,7 @@ namespace rsx add_checkbox(&g_cfg.misc.show_pressure_intensity_toggle_hint, localized_string_id::HOME_MENU_SETTINGS_OVERLAYS_SHOW_PRESSURE_INTENSITY_TOGGLE_HINT); add_checkbox(&g_cfg.misc.show_analog_limiter_toggle_hint, localized_string_id::HOME_MENU_SETTINGS_OVERLAYS_SHOW_ANALOG_LIMITER_TOGGLE_HINT); add_checkbox(&g_cfg.misc.show_mouse_and_keyboard_toggle_hint, localized_string_id::HOME_MENU_SETTINGS_OVERLAYS_SHOW_MOUSE_AND_KB_TOGGLE_HINT); + add_checkbox(&g_cfg.video.record_with_overlays, localized_string_id::HOME_MENU_SETTINGS_OVERLAYS_RECORD_WITH_OVERLAYS); apply_layout(); } diff --git a/rpcs3/Emu/localized_string_id.h b/rpcs3/Emu/localized_string_id.h index 7f741f10d6..5ab9803591 100644 --- a/rpcs3/Emu/localized_string_id.h +++ b/rpcs3/Emu/localized_string_id.h @@ -245,6 +245,7 @@ enum class localized_string_id HOME_MENU_SETTINGS_OVERLAYS_SHOW_PRESSURE_INTENSITY_TOGGLE_HINT, HOME_MENU_SETTINGS_OVERLAYS_SHOW_ANALOG_LIMITER_TOGGLE_HINT, HOME_MENU_SETTINGS_OVERLAYS_SHOW_MOUSE_AND_KB_TOGGLE_HINT, + HOME_MENU_SETTINGS_OVERLAYS_RECORD_WITH_OVERLAYS, HOME_MENU_SETTINGS_PERFORMANCE_OVERLAY, HOME_MENU_SETTINGS_PERFORMANCE_OVERLAY_ENABLE, HOME_MENU_SETTINGS_PERFORMANCE_OVERLAY_ENABLE_FRAMERATE_GRAPH, diff --git a/rpcs3/rpcs3qt/localized_emu.h b/rpcs3/rpcs3qt/localized_emu.h index 1e84a51ed9..bdc46c9f6b 100644 --- a/rpcs3/rpcs3qt/localized_emu.h +++ b/rpcs3/rpcs3qt/localized_emu.h @@ -266,6 +266,7 @@ private: case localized_string_id::HOME_MENU_SETTINGS_OVERLAYS_SHOW_PRESSURE_INTENSITY_TOGGLE_HINT: return tr("Show Pressure Intensity Toggle Hint", "Overlays"); case localized_string_id::HOME_MENU_SETTINGS_OVERLAYS_SHOW_ANALOG_LIMITER_TOGGLE_HINT: return tr( "Show Analog Limiter Toggle Hint", "Overlays"); case localized_string_id::HOME_MENU_SETTINGS_OVERLAYS_SHOW_MOUSE_AND_KB_TOGGLE_HINT: return tr("Show Mouse And Keyboard Toggle Hint", "Overlays"); + case localized_string_id::HOME_MENU_SETTINGS_OVERLAYS_RECORD_WITH_OVERLAYS: return tr("Record With Overlays", "Overlays"); case localized_string_id::HOME_MENU_SETTINGS_PERFORMANCE_OVERLAY: return tr("Performance Overlay"); case localized_string_id::HOME_MENU_SETTINGS_PERFORMANCE_OVERLAY_ENABLE: return tr("Enable Performance Overlay", "Performance Overlay"); case localized_string_id::HOME_MENU_SETTINGS_PERFORMANCE_OVERLAY_ENABLE_FRAMERATE_GRAPH: return tr("Enable Framerate Graph", "Performance Overlay"); From 9dc66b46fc74ab4f536ab1bdbce5bbdf31235154 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Mon, 3 Mar 2025 23:24:20 +0100 Subject: [PATCH 13/25] overlays: add record with overlays setting to settings dialog --- rpcs3/rpcs3qt/emu_settings_type.h | 2 ++ rpcs3/rpcs3qt/settings_dialog.cpp | 10 +++++++--- rpcs3/rpcs3qt/settings_dialog.ui | 13 +++++++------ rpcs3/rpcs3qt/tooltips.h | 1 + 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/rpcs3/rpcs3qt/emu_settings_type.h b/rpcs3/rpcs3qt/emu_settings_type.h index 6062eacb54..7a68b65d2a 100644 --- a/rpcs3/rpcs3qt/emu_settings_type.h +++ b/rpcs3/rpcs3qt/emu_settings_type.h @@ -108,6 +108,7 @@ enum class emu_settings_type ForceHwMSAAResolve, DisableAsyncHostMM, UseReBAR, + RecordWithOverlays, // Performance Overlay PerfOverlayEnabled, @@ -311,6 +312,7 @@ inline static const std::map settings_location { emu_settings_type::OutputScalingMode, { "Video", "Output Scaling Mode"}}, { emu_settings_type::ForceHwMSAAResolve, { "Video", "Force Hardware MSAA Resolve"}}, { emu_settings_type::DisableAsyncHostMM, { "Video", "Disable Asynchronous Memory Manager"}}, + { emu_settings_type::RecordWithOverlays, { "Video", "Record With Overlays"}}, // Vulkan { emu_settings_type::VulkanAsyncTextureUploads, { "Video", "Vulkan", "Asynchronous Texture Streaming 2"}}, diff --git a/rpcs3/rpcs3qt/settings_dialog.cpp b/rpcs3/rpcs3qt/settings_dialog.cpp index 0696b75d87..358675d440 100644 --- a/rpcs3/rpcs3qt/settings_dialog.cpp +++ b/rpcs3/rpcs3qt/settings_dialog.cpp @@ -1873,15 +1873,16 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std SubscribeTooltip(ui->useNativeInterface, tooltips.settings.use_native_interface); #if defined(__linux__) - ui->enableGamemode->setVisible(true); -#endif #if defined(GAMEMODE_AVAILABLE) - ui->enableGamemode->setEnabled(true); m_emu_settings->EnhanceCheckBox(ui->enableGamemode, emu_settings_type::EnableGamemode); SubscribeTooltip(ui->enableGamemode, tooltips.settings.enable_gamemode); #else + ui->enableGamemode->setEnabled(false); SubscribeTooltip(ui->enableGamemode, tooltips.settings.no_gamemode); #endif +#else + ui->enableGamemode->setVisible(false); +#endif m_emu_settings->EnhanceCheckBox(ui->showShaderCompilationHint, emu_settings_type::ShowShaderCompilationHint); SubscribeTooltip(ui->showShaderCompilationHint, tooltips.settings.show_shader_compilation_hint); @@ -1904,6 +1905,9 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std m_emu_settings->EnhanceCheckBox(ui->showCaptureHints, emu_settings_type::ShowCaptureHints); SubscribeTooltip(ui->showCaptureHints, tooltips.settings.show_capture_hints); + m_emu_settings->EnhanceCheckBox(ui->recordWithOverlays, emu_settings_type::RecordWithOverlays); + SubscribeTooltip(ui->recordWithOverlays, tooltips.settings.record_with_overlays); + m_emu_settings->EnhanceCheckBox(ui->pauseDuringHomeMenu, emu_settings_type::PauseDuringHomeMenu); SubscribeTooltip(ui->pauseDuringHomeMenu, tooltips.settings.pause_during_home_menu); diff --git a/rpcs3/rpcs3qt/settings_dialog.ui b/rpcs3/rpcs3qt/settings_dialog.ui index 2ab56bd943..267c140e44 100644 --- a/rpcs3/rpcs3qt/settings_dialog.ui +++ b/rpcs3/rpcs3qt/settings_dialog.ui @@ -3067,14 +3067,15 @@ + + + + Record with overlays + + + - - false - - - false - Enable GameMode diff --git a/rpcs3/rpcs3qt/tooltips.h b/rpcs3/rpcs3qt/tooltips.h index 640a4c4988..31af8dced0 100644 --- a/rpcs3/rpcs3qt/tooltips.h +++ b/rpcs3/rpcs3qt/tooltips.h @@ -155,6 +155,7 @@ public: const QString show_mouse_and_keyboard_toggle_hint = tr("Shows mouse and keyboard toggle hint using the native overlay."); const QString show_capture_hints = tr("Shows screenshot and recording hints using the native overlay."); const QString use_native_interface = tr("Enables use of native HUD within the game window that can interact with game controllers.\nWhen disabled, regular Qt dialogs are used instead.\nCurrently, the on-screen keyboard only supports the English key layout."); + const QString record_with_overlays = tr("Enables recording with overlays.\nThis also affects screenshots."); const QString pause_during_home_menu = tr("When enabled, opening the home menu will also pause emulation.\nWhile most games pause themselves while the home menu is shown, some do not.\nIn that case it can be helpful to pause the emulation whenever the home menu is open."); const QString perf_overlay_enabled = tr("Enables or disables the performance overlay."); From 5f37f2c8dffa37cd87cf38be0553ccdce4cb075e Mon Sep 17 00:00:00 2001 From: Megamouse Date: Wed, 5 Mar 2025 00:02:41 +0100 Subject: [PATCH 14/25] VK: add memory barriers to copy_image --- rpcs3/Emu/RSX/VK/VKPresent.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/rpcs3/Emu/RSX/VK/VKPresent.cpp b/rpcs3/Emu/RSX/VK/VKPresent.cpp index 8ecf4514a0..e83c706ee4 100644 --- a/rpcs3/Emu/RSX/VK/VKPresent.cpp +++ b/rpcs3/Emu/RSX/VK/VKPresent.cpp @@ -728,9 +728,15 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info) VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, 0, VMM_ALLOCATION_POOL_UNDEFINED); + tmp_tex->change_layout(*m_current_command_buffer, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + image_to_flip->push_layout(*m_current_command_buffer, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + const areai rect = areai(0, 0, buffer_width, buffer_height); vk::copy_image(*m_current_command_buffer, image_to_flip, tmp_tex.get(), rect, rect, 1); + image_to_flip->pop_layout(*m_current_command_buffer); + tmp_tex->change_layout(*m_current_command_buffer, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + vk::framebuffer_holder* sshot_fbo = vk::get_framebuffer(*m_device, buffer_width, buffer_height, VK_FALSE, single_target_pass, { tmp_tex.get() }); sshot_fbo->add_ref(); render_overlays(sshot_fbo, areau(rect)); From e4ae5bdce15c4a02bc7946b008d59448c7b30e38 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Fri, 17 Oct 2025 08:29:54 +0200 Subject: [PATCH 15/25] GL: only create new texture when necessary --- rpcs3/Emu/RSX/GL/GLPresent.cpp | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/rpcs3/Emu/RSX/GL/GLPresent.cpp b/rpcs3/Emu/RSX/GL/GLPresent.cpp index cb547bf203..dcadb68700 100644 --- a/rpcs3/Emu/RSX/GL/GLPresent.cpp +++ b/rpcs3/Emu/RSX/GL/GLPresent.cpp @@ -332,15 +332,27 @@ void GLGSRender::flip(const rsx::display_flip_info_t& info) if (g_cfg.video.record_with_overlays) { m_sshot_fbo.create(); - m_sshot_tex = std::make_unique( - GLenum(image_to_flip->get_target()), - image_to_flip->width(), - image_to_flip->height(), - image_to_flip->depth(), - image_to_flip->levels(), - image_to_flip->samples(), - GLenum(image_to_flip->get_internal_format()), - image_to_flip->format_class()); + + if (!m_sshot_tex || + m_sshot_tex->get_target() != image_to_flip->get_target() || + m_sshot_tex->width() != image_to_flip->width() || + m_sshot_tex->height() != image_to_flip->height() || + m_sshot_tex->depth() != image_to_flip->depth() || + m_sshot_tex->levels() != image_to_flip->levels() || + m_sshot_tex->samples() != image_to_flip->samples() || + m_sshot_tex->get_internal_format() != image_to_flip->get_internal_format() || + m_sshot_tex->format_class() != image_to_flip->format_class()) + { + m_sshot_tex = std::make_unique( + GLenum(image_to_flip->get_target()), + image_to_flip->width(), + image_to_flip->height(), + image_to_flip->depth(), + image_to_flip->levels(), + image_to_flip->samples(), + GLenum(image_to_flip->get_internal_format()), + image_to_flip->format_class()); + } tex = m_sshot_tex.get(); From 9124d08fdb753c6746988aaa6544fe98d87a449c Mon Sep 17 00:00:00 2001 From: Megamouse Date: Fri, 17 Oct 2025 09:37:42 +0200 Subject: [PATCH 16/25] GL: flip overlays vertically during recordings --- rpcs3/Emu/RSX/GL/GLOverlays.cpp | 7 ++++--- rpcs3/Emu/RSX/GL/GLOverlays.h | 2 +- rpcs3/Emu/RSX/GL/GLPresent.cpp | 6 +++--- .../Program/GLSLSnippets/OverlayRenderVS.glsl | 15 ++++++++----- rpcs3/Emu/RSX/Program/RSXOverlay.h | 21 ++++++++++++++++++- rpcs3/Emu/RSX/VK/VKOverlays.cpp | 4 ++-- 6 files changed, 40 insertions(+), 15 deletions(-) diff --git a/rpcs3/Emu/RSX/GL/GLOverlays.cpp b/rpcs3/Emu/RSX/GL/GLOverlays.cpp index 193339370a..571adc9b97 100644 --- a/rpcs3/Emu/RSX/GL/GLOverlays.cpp +++ b/rpcs3/Emu/RSX/GL/GLOverlays.cpp @@ -395,7 +395,7 @@ namespace gl } } - void ui_overlay_renderer::run(gl::command_context& cmd_, const areau& viewport, GLuint target, rsx::overlays::overlay& ui) + void ui_overlay_renderer::run(gl::command_context& cmd_, const areau& viewport, GLuint target, rsx::overlays::overlay& ui, bool flip_vertically) { program_handle.uniforms["viewport"] = color4f(static_cast(viewport.width()), static_cast(viewport.height()), static_cast(viewport.x1), static_cast(viewport.y1)); program_handle.uniforms["ui_scale"] = color4f(static_cast(ui.virtual_width), static_cast(ui.virtual_height), 1.f, 1.f); @@ -445,12 +445,13 @@ namespace gl } } - rsx::overlays::vertex_options vert_opts; + rsx::overlays::vertex_options vert_opts {}; program_handle.uniforms["vertex_config"] = vert_opts .disable_vertex_snap(cmd.config.disable_vertex_snap) + .enable_vertical_flip(flip_vertically) .get(); - rsx::overlays::fragment_options draw_opts; + rsx::overlays::fragment_options draw_opts {}; program_handle.uniforms["fragment_config"] = draw_opts .texture_mode(texture_mode) .clip_fragments(cmd.config.clip_region) diff --git a/rpcs3/Emu/RSX/GL/GLOverlays.h b/rpcs3/Emu/RSX/GL/GLOverlays.h index 4399993cc7..96478a161e 100644 --- a/rpcs3/Emu/RSX/GL/GLOverlays.h +++ b/rpcs3/Emu/RSX/GL/GLOverlays.h @@ -90,7 +90,7 @@ namespace gl void emit_geometry(gl::command_context& cmd) override; - void run(gl::command_context& cmd, const areau& viewport, GLuint target, rsx::overlays::overlay& ui); + void run(gl::command_context& cmd, const areau& viewport, GLuint target, rsx::overlays::overlay& ui, bool flip_vertically); }; struct video_out_calibration_pass final : public overlay_pass diff --git a/rpcs3/Emu/RSX/GL/GLPresent.cpp b/rpcs3/Emu/RSX/GL/GLPresent.cpp index dcadb68700..e8e92a04dc 100644 --- a/rpcs3/Emu/RSX/GL/GLPresent.cpp +++ b/rpcs3/Emu/RSX/GL/GLPresent.cpp @@ -294,7 +294,7 @@ void GLGSRender::flip(const rsx::display_flip_info_t& info) m_overlay_manager->dispose(uids_to_dispose); } - const auto render_overlays = [this, &cmd](gl::texture* dst, const areau& aspect_ratio) + const auto render_overlays = [this, &cmd](gl::texture* dst, const areau& aspect_ratio, bool flip_vertically = false) { if (m_overlay_manager && m_overlay_manager->has_visible()) { @@ -316,7 +316,7 @@ void GLGSRender::flip(const rsx::display_flip_info_t& info) for (const auto& view : m_overlay_manager->get_views()) { - m_ui_renderer.run(cmd, aspect_ratio, target, *view.get()); + m_ui_renderer.run(cmd, aspect_ratio, target, *view.get(), flip_vertically); } } }; @@ -359,7 +359,7 @@ void GLGSRender::flip(const rsx::display_flip_info_t& info) static const position3u offset{}; gl::g_hw_blitter->copy_image(cmd, image_to_flip, tex, 0, 0, offset, offset, { tex->width(), tex->height(), 1 }); - render_overlays(tex, areau(0, 0, image_to_flip->width(), image_to_flip->height())); + render_overlays(tex, areau(0, 0, image_to_flip->width(), image_to_flip->height()), true); m_sshot_fbo.remove(); } diff --git a/rpcs3/Emu/RSX/Program/GLSLSnippets/OverlayRenderVS.glsl b/rpcs3/Emu/RSX/Program/GLSLSnippets/OverlayRenderVS.glsl index f0c4844aa0..c583627251 100644 --- a/rpcs3/Emu/RSX/Program/GLSLSnippets/OverlayRenderVS.glsl +++ b/rpcs3/Emu/RSX/Program/GLSLSnippets/OverlayRenderVS.glsl @@ -33,12 +33,14 @@ layout(%push_block) uniform Configuration struct config_t { bool no_vertex_snap; + bool flip_vertically; }; config_t unpack_vertex_options() { config_t result; result.no_vertex_snap = bitfieldExtract(vertex_config, 0, 1) != 0; + result.flip_vertically = bitfieldExtract(vertex_config, 1, 1) != 0; return result; } @@ -47,12 +49,14 @@ vec2 snap_to_grid(const in vec2 normalized) return floor(fma(normalized, viewport.xy, vec2(0.5))) / viewport.xy; } -vec4 clip_to_ndc(const in vec4 coord) +vec4 clip_to_ndc(const in vec4 coord, const in bool flip_vertically) { vec4 ret = (coord * ui_scale.zwzw) / ui_scale.xyxy; #ifndef VULKAN // Flip Y for OpenGL - ret.yw = 1. - ret.yw; + if (!flip_vertically) ret.yw = 1. - ret.yw; +#else + if (flip_vertically) ret.yw = 1. - ret.yw; #endif return ret; } @@ -64,12 +68,13 @@ vec4 ndc_to_window(const in vec4 coord) void main() { + config_t config = unpack_vertex_options(); + tc0.xy = in_pos.zw; color = albedo; - clip_rect = ndc_to_window(clip_to_ndc(clip_bounds)); + clip_rect = ndc_to_window(clip_to_ndc(clip_bounds, config.flip_vertically)); - vec4 pos = vec4(clip_to_ndc(in_pos).xy, 0.5, 1.); - config_t config = unpack_vertex_options(); + vec4 pos = vec4(clip_to_ndc(in_pos, config.flip_vertically).xy, 0.5, 1.); if (!config.no_vertex_snap) { diff --git a/rpcs3/Emu/RSX/Program/RSXOverlay.h b/rpcs3/Emu/RSX/Program/RSXOverlay.h index 99c51a5e92..ebb2071a8b 100644 --- a/rpcs3/Emu/RSX/Program/RSXOverlay.h +++ b/rpcs3/Emu/RSX/Program/RSXOverlay.h @@ -59,12 +59,31 @@ namespace rsx class vertex_options { + private: u32 value = 0; + void set_bit(u32 bit, bool enable) + { + if (enable) + { + value |= (1u << bit); + } + else + { + value &= ~(1u << bit); + } + } + public: vertex_options& disable_vertex_snap(bool enable) { - value = enable ? 1 : 0; + set_bit(0, enable); + return *this; + } + + vertex_options& enable_vertical_flip(bool enable) + { + set_bit(1, enable); return *this; } diff --git a/rpcs3/Emu/RSX/VK/VKOverlays.cpp b/rpcs3/Emu/RSX/VK/VKOverlays.cpp index 3307dc3a48..c7d971a6a1 100644 --- a/rpcs3/Emu/RSX/VK/VKOverlays.cpp +++ b/rpcs3/Emu/RSX/VK/VKOverlays.cpp @@ -543,7 +543,7 @@ namespace vk push_buf[14] = m_clip_region.x2; push_buf[15] = m_clip_region.y2; - rsx::overlays::vertex_options vert_opts; + rsx::overlays::vertex_options vert_opts {}; const auto vert_config = vert_opts .disable_vertex_snap(m_disable_vertex_snap) .get(); @@ -552,7 +552,7 @@ namespace vk vkCmdPushConstants(cmd, program->layout(), VK_SHADER_STAGE_VERTEX_BIT, 0, 68, push_buf); // 2. Fragment stuff - rsx::overlays::fragment_options frag_opts; + rsx::overlays::fragment_options frag_opts {}; const auto frag_config = frag_opts .texture_mode(m_texture_type) .clip_fragments(m_clip_enabled) From 3f797b2de383416be1e321145ddc3b0f266b927d Mon Sep 17 00:00:00 2001 From: Megamouse Date: Fri, 17 Oct 2025 10:05:53 +0200 Subject: [PATCH 17/25] VK: cache recording image --- rpcs3/Emu/RSX/VK/VKGSRender.cpp | 1 + rpcs3/Emu/RSX/VK/VKGSRender.h | 2 ++ rpcs3/Emu/RSX/VK/VKPresent.cpp | 33 +++++++++++++++++++++------------ 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.cpp b/rpcs3/Emu/RSX/VK/VKGSRender.cpp index 37feb1288b..8a3b63eed1 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.cpp +++ b/rpcs3/Emu/RSX/VK/VKGSRender.cpp @@ -845,6 +845,7 @@ VKGSRender::~VKGSRender() m_rtts.destroy(); m_texture_cache.destroy(); + m_overlay_recording_img.reset(); m_stencil_mirror_sampler.reset(); // Queries diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.h b/rpcs3/Emu/RSX/VK/VKGSRender.h index 47af7e78c5..6f52c9c1fc 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.h +++ b/rpcs3/Emu/RSX/VK/VKGSRender.h @@ -187,6 +187,8 @@ private: VkRenderPass m_cached_renderpass = VK_NULL_HANDLE; std::vector m_fbo_images; + std::unique_ptr m_overlay_recording_img; + //Vertex layout rsx::vertex_input_layout m_vertex_layout; diff --git a/rpcs3/Emu/RSX/VK/VKPresent.cpp b/rpcs3/Emu/RSX/VK/VKPresent.cpp index e83c706ee4..908b6c301d 100644 --- a/rpcs3/Emu/RSX/VK/VKPresent.cpp +++ b/rpcs3/Emu/RSX/VK/VKPresent.cpp @@ -469,7 +469,9 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info) } // Scan memory for required data. This is done early to optimize waiting for the driver image acquire below. - vk::viewable_image *image_to_flip = nullptr, *image_to_flip2 = nullptr; + vk::viewable_image* image_to_flip = nullptr; + vk::viewable_image* image_to_flip2 = nullptr; + if (info.buffer < display_buffers_count && buffer_width && buffer_height) { vk::present_surface_info present_info @@ -699,7 +701,7 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info) vk::buffer sshot_vkbuf(*m_device, utils::align(sshot_size, 0x100000), m_device->get_memory_mapping().host_visible_coherent, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, VK_BUFFER_USAGE_TRANSFER_DST_BIT, 0, VMM_ALLOCATION_POOL_UNDEFINED); - VkBufferImageCopy copy_info; + VkBufferImageCopy copy_info {}; copy_info.bufferOffset = 0; copy_info.bufferRowLength = 0; copy_info.bufferImageHeight = 0; @@ -714,7 +716,6 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info) copy_info.imageExtent.height = buffer_height; copy_info.imageExtent.depth = 1; - std::unique_ptr tmp_tex; vk::image* image_to_copy = image_to_flip; if (g_cfg.video.record_with_overlays && has_overlay) @@ -723,26 +724,34 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info) single_target_pass = vk::get_renderpass(*m_device, key); ensure(single_target_pass != VK_NULL_HANDLE); - tmp_tex = std::make_unique(*m_device, m_device->get_memory_mapping().device_local, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, - image_to_flip->type(), image_to_flip->format(), image_to_flip->width(), image_to_flip->height(), 1, 1, image_to_flip->layers(), VK_SAMPLE_COUNT_1_BIT, image_to_flip->current_layout, - VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, - 0, VMM_ALLOCATION_POOL_UNDEFINED); + if (!m_overlay_recording_img || + m_overlay_recording_img->type() != image_to_flip->type() || + m_overlay_recording_img->format() != image_to_flip->format() || + m_overlay_recording_img->width() != image_to_flip->width() || + m_overlay_recording_img->height() != image_to_flip->height() || + m_overlay_recording_img->layers() != image_to_flip->layers()) + { + m_overlay_recording_img = std::make_unique(*m_device, m_device->get_memory_mapping().device_local, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + image_to_flip->type(), image_to_flip->format(), image_to_flip->width(), image_to_flip->height(), 1, 1, image_to_flip->layers(), VK_SAMPLE_COUNT_1_BIT, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, + 0, VMM_ALLOCATION_POOL_UNDEFINED); + } - tmp_tex->change_layout(*m_current_command_buffer, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + m_overlay_recording_img->change_layout(*m_current_command_buffer, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); image_to_flip->push_layout(*m_current_command_buffer, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); const areai rect = areai(0, 0, buffer_width, buffer_height); - vk::copy_image(*m_current_command_buffer, image_to_flip, tmp_tex.get(), rect, rect, 1); + vk::copy_image(*m_current_command_buffer, image_to_flip, m_overlay_recording_img.get(), rect, rect, 1); image_to_flip->pop_layout(*m_current_command_buffer); - tmp_tex->change_layout(*m_current_command_buffer, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + m_overlay_recording_img->change_layout(*m_current_command_buffer, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); - vk::framebuffer_holder* sshot_fbo = vk::get_framebuffer(*m_device, buffer_width, buffer_height, VK_FALSE, single_target_pass, { tmp_tex.get() }); + vk::framebuffer_holder* sshot_fbo = vk::get_framebuffer(*m_device, buffer_width, buffer_height, VK_FALSE, single_target_pass, { m_overlay_recording_img.get() }); sshot_fbo->add_ref(); render_overlays(sshot_fbo, areau(rect)); sshot_fbo->release(); - image_to_copy = tmp_tex.get(); + image_to_copy = m_overlay_recording_img.get(); } image_to_copy->push_layout(*m_current_command_buffer, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); From 50576d044b05d4561b9d552e1f94004f083d9c8d Mon Sep 17 00:00:00 2001 From: Megamouse Date: Wed, 29 Oct 2025 08:02:18 +0100 Subject: [PATCH 18/25] Qt: fix TSX warning style --- rpcs3/rpcs3qt/settings_dialog.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/rpcs3/rpcs3qt/settings_dialog.cpp b/rpcs3/rpcs3qt/settings_dialog.cpp index 358675d440..8798e95ae6 100644 --- a/rpcs3/rpcs3qt/settings_dialog.cpp +++ b/rpcs3/rpcs3qt/settings_dialog.cpp @@ -330,7 +330,17 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std )); } - if (QMessageBox::No == QMessageBox::critical(this, title, message, QMessageBox::Yes, QMessageBox::No)) + QMessageBox mb; + mb.setWindowModality(Qt::WindowModal); + mb.setWindowTitle(title); + mb.setIcon(QMessageBox::Critical); + mb.setTextFormat(Qt::RichText); + mb.setStandardButtons(QMessageBox::Yes | QMessageBox::No); + mb.setDefaultButton(QMessageBox::No); + mb.setText(message); + mb.layout()->setSizeConstraint(QLayout::SetFixedSize); + + if (mb.exec() == QMessageBox::No) { // Reset if the messagebox was answered with no. This prevents the currentIndexChanged signal in EnhanceComboBox ui->enableTSX->setCurrentIndex(find_item(ui->enableTSX, static_cast(g_cfg.core.enable_TSX.def))); From fa8f35ea38b0c69271a2467f870d65f847db3fb5 Mon Sep 17 00:00:00 2001 From: shinra-electric <50119606+shinra-electric@users.noreply.github.com> Date: Sun, 26 Oct 2025 16:38:01 +0900 Subject: [PATCH 19/25] Bump llvm version from 19 to 21 --- .github/workflows/rpcs3.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rpcs3.yml b/.github/workflows/rpcs3.yml index dd9c6ea736..9964c045f5 100644 --- a/.github/workflows/rpcs3.yml +++ b/.github/workflows/rpcs3.yml @@ -136,7 +136,7 @@ jobs: CCACHE_DIR: /tmp/ccache_dir QT_VER: '6.7.3' QT_VER_MAIN: '6' - LLVM_COMPILER_VER: '19' + LLVM_COMPILER_VER: '21' RELEASE_MESSAGE: ../GitHubReleaseMessage.txt UPLOAD_COMMIT_HASH: ${{ matrix.UPLOAD_COMMIT_HASH }} UPLOAD_REPO_FULL_NAME: ${{ matrix.UPLOAD_REPO_FULL_NAME }} From 8ea2109b371fcbe79b17b5341fdb45df313ec64c Mon Sep 17 00:00:00 2001 From: shinra-electric <50119606+shinra-electric@users.noreply.github.com> Date: Sun, 26 Oct 2025 16:41:22 +0900 Subject: [PATCH 20/25] Workaround for issues with Python and brew libc++ linkage on x86 --- .ci/build-mac.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.ci/build-mac.sh b/.ci/build-mac.sh index aa1151a058..93553b02f9 100755 --- a/.ci/build-mac.sh +++ b/.ci/build-mac.sh @@ -8,6 +8,9 @@ export HOMEBREW_NO_INSTALL_CLEANUP=1 brew install -f --overwrite --quiet ccache pipenv "llvm@$LLVM_COMPILER_VER" brew link -f --overwrite --quiet "llvm@$LLVM_COMPILER_VER" +# shellcheck disable=SC3009 +rm /usr/local/bin/{idle3.14,pip3.14,pydoc3.14,python3.14,python3.14-config} && \ +rm /usr/local/bin/{idle3,pip3,pydoc3,python3,python3-config} arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" arch -x86_64 /usr/local/bin/brew install -f --overwrite --quiet ffmpeg@5 "llvm@$LLVM_COMPILER_VER" glew sdl3 vulkan-headers arch -x86_64 /usr/local/bin/brew link -f --overwrite --quiet "llvm@$LLVM_COMPILER_VER" ffmpeg@5 @@ -54,7 +57,8 @@ export Qt6_DIR="$WORKDIR/qt-downloader/$QT_VER/clang_64/lib/cmake/Qt$QT_VER_MAIN export SDL3_DIR="$BREW_X64_PATH/opt/sdl3/lib/cmake/SDL3" export PATH="/opt/homebrew/opt/llvm@$LLVM_COMPILER_VER/bin:$WORKDIR/qt-downloader/$QT_VER/clang_64/bin:$BREW_BIN:$BREW_SBIN:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/Library/Apple/usr/bin:$PATH" -export LDFLAGS="-L$BREW_X64_PATH/lib -Wl,-rpath,$BREW_X64_PATH/lib" +LLVMLIBC=$(brew --prefix llvm)/lib/c++ +export LDFLAGS="-L$BREW_X64_PATH/lib -Wl,-rpath,$BREW_X64_PATH/lib,-L$LLVMLIBC" export CPPFLAGS="-I$BREW_X64_PATH/include -msse -msse2 -mcx16 -no-pie -D__MAC_OS_X_VERSION_MIN_REQUIRED=140000" export CFLAGS="-D__MAC_OS_X_VERSION_MIN_REQUIRED=140000" export LIBRARY_PATH="$BREW_X64_PATH/opt/llvm@$LLVM_COMPILER_VER/lib:$BREW_X64_PATH/lib" From afffd3061a063cf86d230466aef2862ef2f6f5c1 Mon Sep 17 00:00:00 2001 From: shinra-electric <50119606+shinra-electric@users.noreply.github.com> Date: Sun, 26 Oct 2025 16:45:28 +0900 Subject: [PATCH 21/25] Make hack for removing rpath failable --- .ci/deploy-mac-arm64.sh | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.ci/deploy-mac-arm64.sh b/.ci/deploy-mac-arm64.sh index d556a9442b..d7876c93bf 100755 --- a/.ci/deploy-mac-arm64.sh +++ b/.ci/deploy-mac-arm64.sh @@ -32,10 +32,8 @@ rm -rf "rpcs3.app/Contents/Frameworks/QtPdf.framework" \ ../../.ci/optimize-mac.sh rpcs3.app # Hack -install_name_tool \ --delete_rpath /opt/homebrew/lib \ --delete_rpath /opt/homebrew/opt/llvm@$LLVM_COMPILER_VER/lib RPCS3.app/Contents/MacOS/rpcs3 -#-delete_rpath /opt/homebrew1/Cellar/sdl3/3.2.8/lib +install_name_tool -delete_rpath /opt/homebrew/lib RPCS3.app/Contents/MacOS/rpcs3 || echo "Hack for deleting rpath /opt/homebrew/lib not needed" +install_name_tool -delete_rpath /opt/homebrew/opt/llvm@$LLVM_COMPILER_VER/lib RPCS3.app/Contents/MacOS/rpcs3 || echo "Hack for deleting rpath /opt/homebrew/opt/llvm@$LLVM_COMPILER_VER/lib not needed" # Need to do this rename hack due to case insensitive filesystem mv rpcs3.app RPCS3_.app From 5ea92df4cac471636497b9d1119424046e8ca636 Mon Sep 17 00:00:00 2001 From: shinra-electric <50119606+shinra-electric@users.noreply.github.com> Date: Sun, 26 Oct 2025 16:58:02 +0900 Subject: [PATCH 22/25] Cleaner export of LDFLAGS --- .ci/build-mac.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.ci/build-mac.sh b/.ci/build-mac.sh index 93553b02f9..f5a74c303f 100755 --- a/.ci/build-mac.sh +++ b/.ci/build-mac.sh @@ -57,8 +57,8 @@ export Qt6_DIR="$WORKDIR/qt-downloader/$QT_VER/clang_64/lib/cmake/Qt$QT_VER_MAIN export SDL3_DIR="$BREW_X64_PATH/opt/sdl3/lib/cmake/SDL3" export PATH="/opt/homebrew/opt/llvm@$LLVM_COMPILER_VER/bin:$WORKDIR/qt-downloader/$QT_VER/clang_64/bin:$BREW_BIN:$BREW_SBIN:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/Library/Apple/usr/bin:$PATH" -LLVMLIBC=$(brew --prefix llvm)/lib/c++ -export LDFLAGS="-L$BREW_X64_PATH/lib -Wl,-rpath,$BREW_X64_PATH/lib,-L$LLVMLIBC" +# shellcheck disable=SC2155 +export LDFLAGS="-L$BREW_X64_PATH/lib -Wl,-rpath,$BREW_X64_PATH/lib,-L$(brew --prefix llvm)/lib/c++" export CPPFLAGS="-I$BREW_X64_PATH/include -msse -msse2 -mcx16 -no-pie -D__MAC_OS_X_VERSION_MIN_REQUIRED=140000" export CFLAGS="-D__MAC_OS_X_VERSION_MIN_REQUIRED=140000" export LIBRARY_PATH="$BREW_X64_PATH/opt/llvm@$LLVM_COMPILER_VER/lib:$BREW_X64_PATH/lib" From 8013129c5bdeb6f948ca9d0b020f6546eb40325d Mon Sep 17 00:00:00 2001 From: shinra-electric <50119606+shinra-electric@users.noreply.github.com> Date: Sun, 26 Oct 2025 18:00:16 +0900 Subject: [PATCH 23/25] Copy libc++abi to Frameworks instead of lib --- .ci/deploy-mac.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/deploy-mac.sh b/.ci/deploy-mac.sh index 55b8c5eb9a..3668d655b1 100755 --- a/.ci/deploy-mac.sh +++ b/.ci/deploy-mac.sh @@ -16,7 +16,7 @@ echo "AVVER=$AVVER" >> ../.ci/ci-vars.env cd bin mkdir "rpcs3.app/Contents/lib/" -cp "/usr/local/opt/llvm@$LLVM_COMPILER_VER/lib/c++/libc++abi.1.0.dylib" "rpcs3.app/Contents/lib/libc++abi.1.dylib" +cp "/usr/local/opt/llvm@$LLVM_COMPILER_VER/lib/c++/libc++abi.1.0.dylib" "rpcs3.app/Contents/Frameworks/libc++abi.1.dylib" cp "$(realpath /usr/local/lib/libsharpyuv.0.dylib)" "rpcs3.app/Contents/lib/libsharpyuv.0.dylib" cp "$(realpath /usr/local/lib/libintl.8.dylib)" "rpcs3.app/Contents/lib/libintl.8.dylib" From 3a6c71e52395911729b0370442d720b529961e77 Mon Sep 17 00:00:00 2001 From: shinra-electric <50119606+shinra-electric@users.noreply.github.com> Date: Sun, 26 Oct 2025 18:56:29 +0900 Subject: [PATCH 24/25] Explicitly copy libunwind for x86 --- .ci/deploy-mac.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/.ci/deploy-mac.sh b/.ci/deploy-mac.sh index 3668d655b1..15348df05a 100755 --- a/.ci/deploy-mac.sh +++ b/.ci/deploy-mac.sh @@ -17,6 +17,7 @@ cd bin mkdir "rpcs3.app/Contents/lib/" cp "/usr/local/opt/llvm@$LLVM_COMPILER_VER/lib/c++/libc++abi.1.0.dylib" "rpcs3.app/Contents/Frameworks/libc++abi.1.dylib" +cp "/usr/local/opt/llvm@$LLVM_COMPILER_VER/lib/unwind/libunwind.1.dylib" "rpcs3.app/Contents/Frameworks/libunwind.1.dylib" cp "$(realpath /usr/local/lib/libsharpyuv.0.dylib)" "rpcs3.app/Contents/lib/libsharpyuv.0.dylib" cp "$(realpath /usr/local/lib/libintl.8.dylib)" "rpcs3.app/Contents/lib/libintl.8.dylib" From ba2518f8627636f5d78ee4eb108bdcf30d7189e6 Mon Sep 17 00:00:00 2001 From: Elad <18193363+elad335@users.noreply.github.com> Date: Thu, 30 Oct 2025 20:15:17 +0200 Subject: [PATCH 25/25] cellGame: Fix cellHddGameCheck dataVersion parameter --- rpcs3/Emu/Cell/Modules/cellGame.cpp | 15 +++++++++++++-- rpcs3/Emu/Cell/Modules/cellGame.h | 18 +++++++++--------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellGame.cpp b/rpcs3/Emu/Cell/Modules/cellGame.cpp index 7c31007793..371aa2a7b3 100644 --- a/rpcs3/Emu/Cell/Modules/cellGame.cpp +++ b/rpcs3/Emu/Cell/Modules/cellGame.cpp @@ -464,6 +464,8 @@ error_code cellHddGameCheck(ppu_thread& ppu, u32 version, vm::cptr dirName { get->isNewData = CELL_HDDGAME_ISNEWDATA_NODIR; get->getParam = {}; + + cellGame.warning("cellHddGameCheck(): New data."); } else { @@ -476,13 +478,22 @@ error_code cellHddGameCheck(ppu_thread& ppu, u32 version, vm::cptr dirName if (psf.contains("RESOLUTION")) get->getParam.resolution = ::at32(psf, "RESOLUTION").as_integer(); if (psf.contains("SOUND_FORMAT")) get->getParam.soundFormat = ::at32(psf, "SOUND_FORMAT").as_integer(); if (psf.contains("TITLE")) strcpy_trunc(get->getParam.title, ::at32(psf, "TITLE").as_string()); - if (psf.contains("APP_VER")) strcpy_trunc(get->getParam.dataVersion, ::at32(psf, "APP_VER").as_string()); - if (psf.contains("TITLE_ID")) strcpy_trunc(get->getParam.titleId, ::at32(psf, "TITLE_ID").as_string()); + + // Old games do not have APP_VER key + strcpy_trunc(get->getParam.dataVersion, psf::get_string(psf, "APP_VER", psf::get_string(sfo, "VERSION", ""))); + + if (psf.contains("TITLE_ID")) + { + strcpy_trunc(get->getParam.titleId, ::at32(psf, "TITLE_ID").as_string()); + } for (u32 i = 0; i < CELL_HDDGAME_SYSP_LANGUAGE_NUM; i++) { strcpy_trunc(get->getParam.titleLang[i], psf::get_string(psf, fmt::format("TITLE_%02d", i))); } + + cellGame.warning("cellHddGameCheck(): Data exists:\nATTRIBUTE: 0x%x, RESOLUTION: 0x%x, RESOLUTION: 0x%x, SOUND_FORMAT: 0x%x, dataVersion: %s" + , get->getParam.attribute, get->getParam.resolution, get->getParam.soundFormat, get->getParam.soundFormat, std::span(reinterpret_cast(get->getParam.dataVersion), 6)); } // TODO ? diff --git a/rpcs3/Emu/Cell/Modules/cellGame.h b/rpcs3/Emu/Cell/Modules/cellGame.h index a0506cef9d..4121b7fd83 100644 --- a/rpcs3/Emu/Cell/Modules/cellGame.h +++ b/rpcs3/Emu/Cell/Modules/cellGame.h @@ -224,8 +224,8 @@ enum struct CellGameDataSystemFileParam { char title[CELL_GAMEDATA_SYSP_TITLE_SIZE]; - char titleLang[CELL_GAMEDATA_SYSP_LANGUAGE_NUM][CELL_GAMEDATA_SYSP_TITLE_SIZE]; - char titleId[CELL_GAMEDATA_SYSP_TITLEID_SIZE]; + char titleLang[CELL_GAMEDATA_SYSP_LANGUAGE_NUM][CELL_GAMEDATA_SYSP_TITLE_SIZE]; // 0x80 + char titleId[CELL_GAMEDATA_SYSP_TITLEID_SIZE]; // 0xA80 char reserved0[2]; char dataVersion[CELL_GAMEDATA_SYSP_VERSION_SIZE]; char reserved1[2]; @@ -248,13 +248,13 @@ struct CellGameDataStatGet { be_t hddFreeSizeKB; be_t isNewData; - char contentInfoPath[CELL_GAMEDATA_PATH_MAX]; - char gameDataPath[CELL_GAMEDATA_PATH_MAX]; - char reserved0[2]; - be_t st_atime_; - be_t st_mtime_; - be_t st_ctime_; - CellGameDataSystemFileParam getParam; + char contentInfoPath[CELL_GAMEDATA_PATH_MAX]; // 0x8 + char gameDataPath[CELL_GAMEDATA_PATH_MAX]; // 0x427 + char reserved0[2]; // 0x846 + be_t st_atime_; // 0x848 + be_t st_mtime_; // 0x850 + be_t st_ctime_; // 0x858 + CellGameDataSystemFileParam getParam; // 0x860 be_t sizeKB; be_t sysSizeKB; char reserved1[68];