From dbf23a66afaa3040fb8abcebb2c2d9f8873a8c61 Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Sat, 28 Feb 2026 23:30:19 -0600 Subject: [PATCH 1/9] fix (#4088) --- src/core/libraries/save_data/savedata.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/libraries/save_data/savedata.cpp b/src/core/libraries/save_data/savedata.cpp index a5199c297..1edb3d40b 100644 --- a/src/core/libraries/save_data/savedata.cpp +++ b/src/core/libraries/save_data/savedata.cpp @@ -330,7 +330,7 @@ static bool match(std::string_view str, std::string_view pattern) { auto pat_it = pattern.begin(); while (str_it != str.end() && pat_it != pattern.end()) { if (*pat_it == '%') { // 0 or more wildcard - for (auto str_wild_it = str_it; str_wild_it <= str.end(); ++str_wild_it) { + for (auto str_wild_it = str_it; str_wild_it < str.end(); ++str_wild_it) { if (match({str_wild_it, str.end()}, {pat_it + 1, pattern.end()})) { return true; } From 636efaf2b550d3762037ad081197f0f3dc1490bb Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Sun, 1 Mar 2026 20:49:55 +0200 Subject: [PATCH 2/9] changed readbacks mode to Relaxed,Precised (#4091) --- src/common/config.h | 4 ++-- src/video_core/buffer_cache/region_manager.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/common/config.h b/src/common/config.h index 7a351d424..0fa241c6c 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -25,8 +25,8 @@ enum HideCursorState : int { Never, Idle, Always }; enum GpuReadbacksMode : int { Disabled, - Low, - High, + Relaxed, + Precised, }; void load(const std::filesystem::path& path, bool is_game_specific = false); diff --git a/src/video_core/buffer_cache/region_manager.h b/src/video_core/buffer_cache/region_manager.h index f9da020d1..3467b6791 100644 --- a/src/video_core/buffer_cache/region_manager.h +++ b/src/video_core/buffer_cache/region_manager.h @@ -95,7 +95,7 @@ public: } if constexpr (type == Type::CPU) { UpdateProtection(); - } else if (Config::getReadbacksMode() == Config::GpuReadbacksMode::High) { + } else if (Config::getReadbacksMode() == Config::GpuReadbacksMode::Precised) { UpdateProtection(); } } From e5d7dc4090c00ffe566a3086d4033e74409e7f4a Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Sun, 1 Mar 2026 21:02:21 +0200 Subject: [PATCH 3/9] the uber fix (#4092) --- src/common/config.h | 2 +- src/video_core/buffer_cache/region_manager.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/config.h b/src/common/config.h index 0fa241c6c..b341030e0 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -26,7 +26,7 @@ enum HideCursorState : int { Never, Idle, Always }; enum GpuReadbacksMode : int { Disabled, Relaxed, - Precised, + Precise, }; void load(const std::filesystem::path& path, bool is_game_specific = false); diff --git a/src/video_core/buffer_cache/region_manager.h b/src/video_core/buffer_cache/region_manager.h index 3467b6791..ecf9406af 100644 --- a/src/video_core/buffer_cache/region_manager.h +++ b/src/video_core/buffer_cache/region_manager.h @@ -95,7 +95,7 @@ public: } if constexpr (type == Type::CPU) { UpdateProtection(); - } else if (Config::getReadbacksMode() == Config::GpuReadbacksMode::Precised) { + } else if (Config::getReadbacksMode() == Config::GpuReadbacksMode::Precise) { UpdateProtection(); } } From fc949a74497a29a4eda5ad0b4b7a78ada4a22ba1 Mon Sep 17 00:00:00 2001 From: Niram7777 Date: Mon, 2 Mar 2026 18:32:28 +0000 Subject: [PATCH 4/9] CI wget linuxdeploy retries (#4093) --- .github/linux-appimage-sdl.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/linux-appimage-sdl.sh b/.github/linux-appimage-sdl.sh index 7961f5312..d85aa6c4c 100755 --- a/.github/linux-appimage-sdl.sh +++ b/.github/linux-appimage-sdl.sh @@ -8,8 +8,8 @@ if [[ -z $GITHUB_WORKSPACE ]]; then fi # Prepare Tools for building the AppImage -wget -q https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage -wget -q https://github.com/linuxdeploy/linuxdeploy-plugin-checkrt/releases/download/continuous/linuxdeploy-plugin-checkrt-x86_64.sh +wget --waitretry=3 --read-timeout=20 --timeout=15 --tries=5 -q https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage +wget --waitretry=3 --read-timeout=20 --timeout=15 --tries=5 -q https://github.com/linuxdeploy/linuxdeploy-plugin-checkrt/releases/download/continuous/linuxdeploy-plugin-checkrt-x86_64.sh chmod a+x linuxdeploy-x86_64.AppImage chmod a+x linuxdeploy-plugin-checkrt-x86_64.sh From 1f5430e4c276dfac0286e25d4fa6470121b8910e Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Mon, 2 Mar 2026 12:35:58 -0600 Subject: [PATCH 5/9] More targeted fix (#4096) Cyberpunk's issue seems to actually come from the incrementing in the loop. It wasn't clear while debugging, but the problem is that the pattern the game supplies causes match to fail when str_wild_it hits the end, and then tries iterating past end due to the loop condition. Our pattern matching code seems broken for the case Cyberpunk triggers, but since I'm not aware of the intricacies of how real hardware behaves, best to just revert the loop condition change and instead break the loop before the broken iteration. --- src/core/libraries/save_data/savedata.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/core/libraries/save_data/savedata.cpp b/src/core/libraries/save_data/savedata.cpp index 1edb3d40b..48b086457 100644 --- a/src/core/libraries/save_data/savedata.cpp +++ b/src/core/libraries/save_data/savedata.cpp @@ -330,9 +330,12 @@ static bool match(std::string_view str, std::string_view pattern) { auto pat_it = pattern.begin(); while (str_it != str.end() && pat_it != pattern.end()) { if (*pat_it == '%') { // 0 or more wildcard - for (auto str_wild_it = str_it; str_wild_it < str.end(); ++str_wild_it) { + for (auto str_wild_it = str_it; str_wild_it <= str.end(); ++str_wild_it) { if (match({str_wild_it, str.end()}, {pat_it + 1, pattern.end()})) { return true; + } else if (str_wild_it == str.end()) { + // Avoid incrementing str_wild_it past str.end(). + break; } } return false; From 14450d330f450ce68368819711d423a5f7f456f3 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Tue, 3 Mar 2026 08:52:12 +0200 Subject: [PATCH 6/9] CopyImage stencil fixes (#4095) * stencil fixes hope it fixes driveclub * revert image copy to the one that had driveclub worked * reverted texture cache change * some more fixes and reverts * added logging for overlap again --- src/video_core/texture_cache/image.cpp | 142 +++++++----------- src/video_core/texture_cache/image_info.h | 2 +- .../texture_cache/texture_cache.cpp | 8 - 3 files changed, 56 insertions(+), 96 deletions(-) diff --git a/src/video_core/texture_cache/image.cpp b/src/video_core/texture_cache/image.cpp index 418641bc3..972f028d4 100644 --- a/src/video_core/texture_cache/image.cpp +++ b/src/video_core/texture_cache/image.cpp @@ -461,33 +461,36 @@ static std::pair SanitizeCopyLayers(const ImageInfo& src_info, const I void Image::CopyImage(Image& src_image) { const auto& src_info = src_image.info; + const u32 num_mips = std::min(src_info.resources.levels, info.resources.levels); - // Check format compatibility + // Format mismatch warning (safe but useful) if (src_info.pixel_format != info.pixel_format) { LOG_DEBUG(Render_Vulkan, - "Copy between different formats: src={}, dst={}. Color may be incorrect.", + "Copy between different formats: src={}, dst={}. " + "Result may be undefined.", vk::to_string(src_info.pixel_format), vk::to_string(info.pixel_format)); } - const u32 width = src_info.size.width; - const u32 height = src_info.size.height; + const u32 base_width = src_info.size.width; + const u32 base_height = src_info.size.height; const u32 base_depth = info.type == AmdGpu::ImageType::Color3D ? info.size.depth : src_info.size.depth; - auto [test_src_layers, test_dst_layers] = SanitizeCopyLayers(src_info, info, base_depth); - - ASSERT(test_src_layers == test_dst_layers || num_mips == 1 || - (ConvertImageType(src_info.type) != ConvertImageType(info.type) && - (test_src_layers == 1 || test_dst_layers == 1))); - + // Match sample count before copying SetBackingSamples(info.num_samples, false); src_image.SetBackingSamples(src_info.num_samples); - boost::container::small_vector image_copies; + boost::container::small_vector regions; + + const vk::ImageAspectFlags src_aspect = + src_image.aspect_mask & ~vk::ImageAspectFlagBits::eStencil; + + const vk::ImageAspectFlags dst_aspect = aspect_mask & ~vk::ImageAspectFlagBits::eStencil; const bool src_is_2d = ConvertImageType(src_info.type) == vk::ImageType::e2D; const bool src_is_3d = ConvertImageType(src_info.type) == vk::ImageType::e3D; + const bool dst_is_2d = ConvertImageType(info.type) == vk::ImageType::e2D; const bool dst_is_3d = ConvertImageType(info.type) == vk::ImageType::e3D; @@ -495,103 +498,68 @@ void Image::CopyImage(Image& src_image) { const bool is_3d_to_2d = src_is_3d && dst_is_2d; const bool is_same_type = !is_2d_to_3d && !is_3d_to_2d; - // Determine aspect mask - exclude stencil - vk::ImageAspectFlags aspect = vk::ImageAspectFlagBits::eColor; - - // For depth/stencil images, only copy the depth aspect (skip stencil) - if (src_image.aspect_mask & vk::ImageAspectFlagBits::eDepth) { - aspect = vk::ImageAspectFlagBits::eDepth; - } - for (u32 mip = 0; mip < num_mips; ++mip) { - const auto mip_w = std::max(width >> mip, 1u); - const auto mip_h = std::max(height >> mip, 1u); - const auto mip_d = std::max(base_depth >> mip, 1u); + const u32 mip_w = std::max(base_width >> mip, 1u); + const u32 mip_h = std::max(base_height >> mip, 1u); + const u32 mip_d = std::max(base_depth >> mip, 1u); auto [src_layers, dst_layers] = SanitizeCopyLayers(src_info, info, mip_d); + vk::ImageCopy region{}; + + region.srcSubresource.aspectMask = src_aspect; + region.srcSubresource.mipLevel = mip; + region.srcSubresource.baseArrayLayer = 0; + + region.dstSubresource.aspectMask = dst_aspect; + region.dstSubresource.mipLevel = mip; + region.dstSubresource.baseArrayLayer = 0; + if (is_same_type) { - u32 copy_layers = std::min(src_layers, dst_layers); - - if (src_is_3d) - src_layers = 1; - if (dst_is_3d) - dst_layers = 1; - - vk::ImageCopy copy_region = { - .srcSubresource{ - .aspectMask = aspect, - .mipLevel = mip, - .baseArrayLayer = 0, - .layerCount = copy_layers, - }, - .dstSubresource{ - .aspectMask = aspect, - .mipLevel = mip, - .baseArrayLayer = 0, - .layerCount = copy_layers, - }, - .extent = vk::Extent3D(mip_w, mip_h, mip_d), - }; - image_copies.push_back(copy_region); + // 2D->2D OR 3D->3D + if (src_is_3d) { + // 3D images must use layerCount=1 + region.srcSubresource.layerCount = 1; + region.dstSubresource.layerCount = 1; + region.extent = vk::Extent3D(mip_w, mip_h, mip_d); + } else { + // Array images + const u32 copy_layers = std::min(src_layers, dst_layers); + region.srcSubresource.layerCount = copy_layers; + region.dstSubresource.layerCount = copy_layers; + region.extent = vk::Extent3D(mip_w, mip_h, 1); + } } else if (is_2d_to_3d) { - vk::ImageCopy copy_region = { - .srcSubresource{ - .aspectMask = aspect, - .mipLevel = mip, - .baseArrayLayer = 0, - .layerCount = src_layers, - }, - .dstSubresource{ - .aspectMask = aspect, - .mipLevel = mip, - .baseArrayLayer = 0, - .layerCount = 1, - }, - .extent = vk::Extent3D(mip_w, mip_h, src_layers), - }; - image_copies.push_back(copy_region); + // 2D array -> 3D volume + region.srcSubresource.layerCount = src_layers; + region.dstSubresource.layerCount = 1; + region.extent = vk::Extent3D(mip_w, mip_h, src_layers); } else if (is_3d_to_2d) { - vk::ImageCopy copy_region = { - .srcSubresource{ - .aspectMask = aspect, - .mipLevel = mip, - .baseArrayLayer = 0, - .layerCount = 1, - }, - .dstSubresource{ - .aspectMask = aspect, - .mipLevel = mip, - .baseArrayLayer = 0, - .layerCount = dst_layers, - }, - .extent = vk::Extent3D(mip_w, mip_h, dst_layers), - }; - image_copies.push_back(copy_region); + // 3D volume -> 2D array + region.srcSubresource.layerCount = 1; + region.dstSubresource.layerCount = dst_layers; + region.extent = vk::Extent3D(mip_w, mip_h, dst_layers); } + + regions.push_back(region); } scheduler->EndRendering(); - // Remove the pipeline stage flags - they don't belong here src_image.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead, {}); Transit(vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite, {}); auto cmdbuf = scheduler->CommandBuffer(); - if (!image_copies.empty()) { - cmdbuf.copyImage(src_image.GetImage(), vk::ImageLayout::eTransferSrcOptimal, GetImage(), - vk::ImageLayout::eTransferDstOptimal, image_copies); + if (!regions.empty()) { + cmdbuf.copyImage(src_image.GetImage(), src_image.backing->state.layout, GetImage(), + backing->state.layout, regions); } - // Remove pipeline stage flags here too - src_image.Transit(vk::ImageLayout::eShaderReadOnlyOptimal, vk::AccessFlagBits2::eShaderRead, - {}); - - Transit(vk::ImageLayout::eShaderReadOnlyOptimal, vk::AccessFlagBits2::eShaderRead, {}); + Transit(vk::ImageLayout::eGeneral, + vk::AccessFlagBits2::eShaderRead | vk::AccessFlagBits2::eTransferRead, {}); } - void Image::CopyImageWithBuffer(Image& src_image, vk::Buffer buffer, u64 offset) { const auto& src_info = src_image.info; const u32 num_mips = std::min(src_info.resources.levels, info.resources.levels); diff --git a/src/video_core/texture_cache/image_info.h b/src/video_core/texture_cache/image_info.h index 0da9c8bfb..543e144d2 100644 --- a/src/video_core/texture_cache/image_info.h +++ b/src/video_core/texture_cache/image_info.h @@ -48,7 +48,7 @@ struct ImageInfo { } Extent2D BlockDim() const { const auto dim = props.is_block ? 2 : 0; - return Extent2D{size.width >> dim, size.height >> dim}; + return Extent2D{pitch >> dim, size.height >> dim}; } s32 MipOf(const ImageInfo& info) const; diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp index a8d846cfc..8163902cc 100644 --- a/src/video_core/texture_cache/texture_cache.cpp +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -297,14 +297,6 @@ std::tuple TextureCache::ResolveOverlap(const ImageInfo& imag if (image_info.guest_address == cache_image.info.guest_address) { const u32 lhs_block_size = image_info.num_bits * image_info.num_samples; const u32 rhs_block_size = cache_image.info.num_bits * cache_image.info.num_samples; - - if (image_info.pitch != cache_image.info.pitch) { - if (safe_to_delete) { - FreeImage(cache_image_id); - } - return {merged_image_id, -1, -1}; - } - if (image_info.BlockDim() != cache_image.info.BlockDim() || lhs_block_size != rhs_block_size) { // Very likely this kind of overlap is caused by allocation from a pool. From 89e74828e63680e211f36bd5ec600f2bf4be7552 Mon Sep 17 00:00:00 2001 From: Pavel <68122101+red-prig@users.noreply.github.com> Date: Thu, 5 Mar 2026 23:44:37 +0300 Subject: [PATCH 7/9] fixup r128 (#4100) --- src/shader_recompiler/frontend/translate/vector_memory.cpp | 1 + src/shader_recompiler/resource.h | 1 + 2 files changed, 2 insertions(+) diff --git a/src/shader_recompiler/frontend/translate/vector_memory.cpp b/src/shader_recompiler/frontend/translate/vector_memory.cpp index e0c64ff4a..72286b29c 100644 --- a/src/shader_recompiler/frontend/translate/vector_memory.cpp +++ b/src/shader_recompiler/frontend/translate/vector_memory.cpp @@ -458,6 +458,7 @@ void Translator::IMAGE_STORE(bool has_mip, const GcnInst& inst) { IR::TextureInstInfo info{}; info.has_lod.Assign(has_mip); info.is_array.Assign(mimg.da); + info.is_r128.Assign(mimg.r128); boost::container::static_vector comps; for (u32 i = 0; i < 4; i++) { diff --git a/src/shader_recompiler/resource.h b/src/shader_recompiler/resource.h index 5d9965105..5ae3179f6 100644 --- a/src/shader_recompiler/resource.h +++ b/src/shader_recompiler/resource.h @@ -86,6 +86,7 @@ struct ImageResource { } else { const auto raw = info.template ReadUdSharp(sharp_idx); std::memcpy(&image, &raw, sizeof(raw)); + image.pitch = image.width; } if (!image.Valid()) { LOG_DEBUG(Render_Vulkan, "Encountered invalid image sharp"); From 50d5ce9a8392bce170efca9bb7232c25bb4a8f0d Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Sat, 7 Mar 2026 08:55:17 +0200 Subject: [PATCH 8/9] fixed an issue in sceKernelRemoveExceptionHandler (#4086) * fixed an issue in sceKernelRemoveExceptionHandler * fixed comment --- src/core/libraries/kernel/threads/exception.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/core/libraries/kernel/threads/exception.cpp b/src/core/libraries/kernel/threads/exception.cpp index cf36da0cc..247c387fe 100644 --- a/src/core/libraries/kernel/threads/exception.cpp +++ b/src/core/libraries/kernel/threads/exception.cpp @@ -276,7 +276,10 @@ int PS4_SYSV_ABI sceKernelRemoveExceptionHandler(s32 signum) { return ORBIS_KERNEL_ERROR_EINVAL; } int const native_signum = OrbisToNativeSignal(signum); - ASSERT_MSG(Handlers[native_signum], "Invalid parameters"); + if (!Handlers[native_signum]) { + LOG_WARNING(Lib_Kernel, "removing non-installed handler for signum {}", signum); + return ORBIS_KERNEL_ERROR_EINVAL; + } Handlers[native_signum] = nullptr; #ifndef _WIN64 if (native_signum == SIGSEGV || native_signum == SIGBUS || native_signum == SIGILL) { From 014b11e9da8765b281b331a1edff15d12c365d7f Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Sat, 7 Mar 2026 01:15:32 -0600 Subject: [PATCH 9/9] Return an empty certificate (#4104) This is enough to get past initialization checks in Rise of the Tomb Raider. --- src/core/libraries/network/ssl2.cpp | 26 +++++++++++++------------ src/core/libraries/network/ssl2.h | 12 ++++++++++++ src/core/libraries/network/ssl2_error.h | 8 ++++++++ 3 files changed, 34 insertions(+), 12 deletions(-) create mode 100644 src/core/libraries/network/ssl2_error.h diff --git a/src/core/libraries/network/ssl2.cpp b/src/core/libraries/network/ssl2.cpp index 682095801..0b408d094 100644 --- a/src/core/libraries/network/ssl2.cpp +++ b/src/core/libraries/network/ssl2.cpp @@ -5,6 +5,7 @@ #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" #include "core/libraries/network/ssl2.h" +#include "core/libraries/network/ssl2_error.h" namespace Libraries::Ssl2 { @@ -108,8 +109,13 @@ int PS4_SYSV_ABI sceSslEnableVerifyOption() { return ORBIS_OK; } -int PS4_SYSV_ABI sceSslFreeCaCerts() { - LOG_ERROR(Lib_Ssl2, "(STUBBED) called"); +int PS4_SYSV_ABI sceSslFreeCaCerts(s32 ssl_ctx_id, OrbisSslCaCerts* certs) { + LOG_ERROR(Lib_Ssl2, "(DUMMY) called"); + if (certs == nullptr) { + return ORBIS_SSL_ERROR_INVALID_ARGUMENT; + } + delete (certs->certs); + // delete (certs->pool); return ORBIS_OK; } @@ -128,17 +134,13 @@ int PS4_SYSV_ABI sceSslGetAlpnSelected() { return ORBIS_OK; } -struct OrbisSslCaCerts { - void* certs; - u64 num; - void* pool; -}; - -int PS4_SYSV_ABI sceSslGetCaCerts(int sslCtxId, OrbisSslCaCerts* certs) { - // check if it is same as libSceSsl +int PS4_SYSV_ABI sceSslGetCaCerts(s32 ssl_ctx_id, OrbisSslCaCerts* certs) { LOG_ERROR(Lib_Ssl2, "(DUMMY) called"); - certs->certs = nullptr; - certs->num = 0; + if (certs == nullptr) { + return ORBIS_SSL_ERROR_INVALID_ARGUMENT; + } + certs->certs = new OrbisSslData{nullptr, 0}; + certs->num = 1; certs->pool = nullptr; return ORBIS_OK; } diff --git a/src/core/libraries/network/ssl2.h b/src/core/libraries/network/ssl2.h index 754dda40c..18fb205d3 100644 --- a/src/core/libraries/network/ssl2.h +++ b/src/core/libraries/network/ssl2.h @@ -10,5 +10,17 @@ class SymbolsResolver; } namespace Libraries::Ssl2 { + +struct OrbisSslData { + char* ptr; + u64 size; +}; + +struct OrbisSslCaCerts { + OrbisSslData* certs; + u64 num; + void* pool; +}; + void RegisterLib(Core::Loader::SymbolsResolver* sym); } // namespace Libraries::Ssl2 \ No newline at end of file diff --git a/src/core/libraries/network/ssl2_error.h b/src/core/libraries/network/ssl2_error.h new file mode 100644 index 000000000..03bf94256 --- /dev/null +++ b/src/core/libraries/network/ssl2_error.h @@ -0,0 +1,8 @@ +// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/libraries/error_codes.h" + +constexpr int ORBIS_SSL_ERROR_INVALID_ARGUMENT = 0x8095F007; \ No newline at end of file