Merge branch 'shadps4-emu:main' into jane/buffer-gc-run

This commit is contained in:
collinmcg 2026-03-07 13:16:56 -06:00 committed by GitHub
commit 59dd371c53
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 104 additions and 114 deletions

View File

@ -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

View File

@ -25,8 +25,8 @@ enum HideCursorState : int { Never, Idle, Always };
enum GpuReadbacksMode : int {
Disabled,
Low,
High,
Relaxed,
Precise,
};
void load(const std::filesystem::path& path, bool is_game_specific = false);

View File

@ -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) {

View File

@ -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;
}

View File

@ -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

View File

@ -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;

View File

@ -333,6 +333,9 @@ static bool match(std::string_view str, std::string_view pattern) {
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;

View File

@ -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<IR::F32, 4> comps;
for (u32 i = 0; i < 4; i++) {

View File

@ -86,6 +86,7 @@ struct ImageResource {
} else {
const auto raw = info.template ReadUdSharp<u128>(sharp_idx);
std::memcpy(&image, &raw, sizeof(raw));
image.pitch = image.width;
}
if (!image.Valid()) {
LOG_DEBUG(Render_Vulkan, "Encountered invalid image sharp");

View File

@ -95,7 +95,7 @@ public:
}
if constexpr (type == Type::CPU) {
UpdateProtection<!enable, false>();
} else if (Config::getReadbacksMode() == Config::GpuReadbacksMode::High) {
} else if (Config::getReadbacksMode() == Config::GpuReadbacksMode::Precise) {
UpdateProtection<enable, true>();
}
}

View File

@ -461,33 +461,36 @@ static std::pair<u32, u32> 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<vk::ImageCopy, 8> image_copies;
boost::container::small_vector<vk::ImageCopy, 8> 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);

View File

@ -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;

View File

@ -297,14 +297,6 @@ std::tuple<ImageId, int, int> 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.