Merge branch 'master' into fix/apple-arm64-memory-decommit-race

This commit is contained in:
kd-11 2026-04-05 18:50:10 +03:00 committed by GitHub
commit 8ffba04e77
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
172 changed files with 7458 additions and 2815 deletions

View File

@ -13,6 +13,7 @@ export HOMEBREW_NO_AUTO_UPDATE=1
export HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1
export HOMEBREW_NO_ENV_HINTS=1
export HOMEBREW_NO_INSTALL_CLEANUP=1
brew update
brew install -f --overwrite --quiet ccache "llvm@$LLVM_COMPILER_VER"
brew link -f --overwrite --quiet "llvm@$LLVM_COMPILER_VER"
if [ "$AARCH64" -eq 1 ]; then

View File

@ -6,7 +6,7 @@
QT_HOST="http://qt.mirror.constant.com/"
QT_URL_VER=$(echo "$QT_VER" | sed "s/\.//g")
QT_VER_MSVC_UP=$(echo "${QT_VER_MSVC}" | tr '[:lower:]' '[:upper:]')
QT_PREFIX="online/qtsdkrepository/windows_x86/desktop/qt${QT_VER_MAIN}_${QT_URL_VER}/qt${QT_VER_MAIN}_${QT_URL_VER}/qt.qt${QT_VER_MAIN}.${QT_URL_VER}."
QT_PREFIX="online/qtsdkrepository/windows_x86/desktop/qt${QT_VER_MAIN}_${QT_URL_VER}/qt${QT_VER_MAIN}_${QT_URL_VER}_${QT_VER_MSVC}_64/qt.qt${QT_VER_MAIN}.${QT_URL_VER}."
QT_PREFIX_2="win64_${QT_VER_MSVC}_64/${QT_VER}-0-${QT_DATE}"
QT_SUFFIX="-Windows-Windows_11_24H2-${QT_VER_MSVC_UP}-Windows-Windows_11_24H2-X86_64.7z"
QT_BASE_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qtbase${QT_SUFFIX}"

View File

@ -30,23 +30,23 @@ jobs:
matrix:
include:
- os: ubuntu-24.04
docker_img: "rpcs3/rpcs3-ci-jammy:1.9"
docker_img: "rpcs3/rpcs3-ci-jammy:1.10"
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.9"
docker_img: "rpcs3/rpcs3-ci-jammy:1.10"
build_sh: "/rpcs3/.ci/build-linux.sh"
compiler: gcc
- os: ubuntu-24.04-arm
docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.9"
docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.10"
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.9"
docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.10"
build_sh: "/rpcs3/.ci/build-linux-aarch64.sh"
compiler: gcc
name: RPCS3 Linux ${{ matrix.os }} ${{ matrix.compiler }}
@ -134,7 +134,7 @@ jobs:
runs-on: macos-14
env:
CCACHE_DIR: /tmp/ccache_dir
QT_VER: '6.10.2'
QT_VER: '6.11.0'
QT_VER_MAIN: '6'
LLVM_COMPILER_VER: '21'
RELEASE_MESSAGE: ../GitHubReleaseMessage.txt
@ -213,9 +213,9 @@ jobs:
env:
COMPILER: msvc
QT_VER_MAIN: '6'
QT_VER: '6.10.2'
QT_VER: '6.11.0'
QT_VER_MSVC: 'msvc2022'
QT_DATE: '202601261212'
QT_DATE: '202603180535'
LLVM_VER: '19.1.7'
VULKAN_VER: '1.3.268.0'
VULKAN_SDK_SHA: '8459ef49bd06b697115ddd3d97c9aec729e849cd775f5be70897718a9b3b9db5'

3
.gitignore vendored
View File

@ -69,6 +69,9 @@ CMakeSettings.json
*PVS-Studio*
PVS/*
# Zed Editor files
.zed/*
# Ignore other system generated files
x64/*
rpcs3/x64/*

2
3rdparty/FAudio vendored

@ -1 +1 @@
Subproject commit dc034fc671b07bbd14e8410d5dd6be6da38fdf6d
Subproject commit 0372329dbb56e7814d0dea7b6eafa7a613bd8042

@ -1 +1 @@
Subproject commit 3982730833b6daefe77dcfb32b5c282851640c17
Subproject commit a0fba77b6f9cfbdb71f8bbec58b6ac4e5e3b1097

View File

@ -18,9 +18,6 @@ else()
set(USE_LIBIDN2 OFF CACHE BOOL "Use libidn2 for IDN support") # Disabled because MacOS CI doesn't work otherwise
set(CURL_CA_PATH "none" CACHE STRING "Location of default CA path. Set 'none' to disable or 'auto' for auto-detection. Defaults to 'auto'.")
option(CURL_DISABLE_INSTALL "Disable installation targets" ON)
if(USE_MSVC_STATIC_CRT)
set(CURL_STATIC_CRT ON CACHE BOOL "Use static crt to build curl")
endif()
if(WIN32)
set(ENABLE_UNICODE ON CACHE BOOL "enable Unicode")
endif()

@ -1 +1 @@
Subproject commit c3e304954a9cfd154bc0dfbfea2b01cd61d6546d
Subproject commit d5515b5b8be3901aac04e5bd8bd5c89f287bcd33

@ -1 +1 @@
Subproject commit 683181b47cfabd293e3ea409f838915b8297a4fd
Subproject commit 5848e584a1b606de26e3dbd1c7e4ecbc34f807a6

View File

@ -20,6 +20,8 @@ else()
option(protobuf_DISABLE_RTTI "Remove runtime type information in the binaries" OFF)
option(protobuf_FORCE_FETCH_DEPENDENCIES "Force all dependencies to be downloaded from GitHub. Local installations will be ignored." OFF)
option(protobuf_LOCAL_DEPENDENCIES_ONLY "Prevent downloading any dependencies from GitHub. If this option is set, the dependency must be available locally as an installed package." OFF)
option(protobuf_BUILD_SHARED_LIBS "Build Shared Libraries" OFF)
option(protobuf_MSVC_STATIC_RUNTIME "Link static runtime libraries" OFF)
add_subdirectory(protobuf EXCLUDE_FROM_ALL)
target_include_directories(3rdparty_protobuf SYSTEM INTERFACE protobuf/src)

@ -1 +1 @@
Subproject commit b077c81eb635392e694ccedbab8b644297ec0285
Subproject commit 922d04b3568c6428a9fb905ddee3ef5a68db3108

View File

@ -6,7 +6,8 @@ if (USE_SYSTEM_ZLIB)
target_link_libraries(3rdparty_zlib INTERFACE ZLIB::ZLIB)
target_compile_definitions(3rdparty_zlib INTERFACE -DZLIB_CONST=1)
else()
option(ZLIB_BUILD_EXAMPLES "Enable Zlib Examples" OFF)
option(ZLIB_BUILD_TESTING "Enable Zlib Examples as tests" OFF)
option(ZLIB_BUILD_SHARED "Enable building zlib shared library" OFF)
message(STATUS "RPCS3: Using builtin ZLIB")
set(SKIP_INSTALL_ALL ON)
add_subdirectory(zlib EXCLUDE_FROM_ALL)

View File

@ -20,26 +20,26 @@ The following tools are required to build RPCS3 on Windows 10 or later:
with standalone **CMake** tool.
- [Python 3.6+](https://www.python.org/downloads/) (add to PATH)
- [Qt 6.10.2](https://www.qt.io/download-qt-installer) In case you can't download from the official installer, you can use [Another Qt installer](https://github.com/miurahr/aqtinstall) (In that case you will need to manually add the "qtmultimedia" module when installing Qt)
- [Qt 6.11.0](https://www.qt.io/download-qt-installer) In case you can't download from the official installer, you can use [Another Qt installer](https://github.com/miurahr/aqtinstall) (In that case you will need to manually add the "qtmultimedia" module when installing Qt)
- [Vulkan SDK 1.3.268.0](https://vulkan.lunarg.com/sdk/home) (see "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/windows/getting_started.html)) for now future SDKs don't work. You need precisely 1.3.268.0.
The `sln` solution available only on **Visual Studio** is the preferred building solution. It easily allows to build the **RPCS3** application in `Release` and `Debug` mode.
In order to build **RPCS3** with the `sln` solution (with **Visual Studio**), **Qt** libs need to be detected. To detect the libs:
- add and set the `QTDIR` environment variable, e.g. `<QtInstallFolder>\6.10.2\msvc2022_64\`
- add and set the `QTDIR` environment variable, e.g. `<QtInstallFolder>\6.11.0\msvc2022_64\`
- or use the [Visual Studio Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.QtVisualStudioTools2022)
**NOTE:** If you have issues with the **Visual Studio Qt Plugin**, you may want to uninstall it and install the [Legacy Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.LEGACYQtVisualStudioTools2022) instead.
In order to build **RPCS3** with the `CMake` solution (with both **Visual Studio** and standalone **CMake** tool):
- add and set the `Qt6_ROOT` environment variable to the **Qt** libs path, e.g. `<QtInstallFolder>\6.10.2\msvc2022_64\`
- add and set the `Qt6_ROOT` environment variable to the **Qt** libs path, e.g. `<QtInstallFolder>\6.11.0\msvc2022_64\`
### Linux
These are the essentials tools to build RPCS3 on Linux. Some of them can be installed through your favorite package manager:
- Clang 17+ or GCC 13+
- [CMake 3.28.0+](https://www.cmake.org/download/)
- [Qt 6.10.2](https://www.qt.io/download-qt-installer)
- [Qt 6.11.0](https://www.qt.io/download-qt-installer)
- [Vulkan SDK 1.3.268.0](https://vulkan.lunarg.com/sdk/home) (See "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/linux/getting_started.html)) for now future SDKs don't work. You need precisely 1.3.268.0.
- [SDL3](https://github.com/libsdl-org/SDL/releases) (for the FAudio backend)
@ -123,7 +123,7 @@ Start **Visual Studio**, click on `Open a project or solution` and select the `r
##### Configuring the Qt Plugin (if used)
1) go to `Extensions->Qt VS Tools->Qt Versions`
2) add the path to your Qt installation with compiler e.g. `<QtInstallFolder>\6.10.2\msvc2022_64`, version will fill in automatically
2) add the path to your Qt installation with compiler e.g. `<QtInstallFolder>\6.11.0\msvc2022_64`, version will fill in automatically
3) go to `Extensions->Qt VS Tools->Options->Legacy Project Format`. (Only available in the **Legacy Qt Plugin**)
4) set `Build: Run pre-build setup` to `true`. (Only available in the **Legacy Qt Plugin**)

View File

@ -86,50 +86,7 @@ if(CMAKE_BUILD_TYPE MATCHES "Debug" AND NOT MSVC)
endif()
if(MSVC)
option(USE_MSVC_STATIC_CRT "Use static MSVC C runtime" OFF)
# TODO(cjj19970505@live.cn)
# DiscordRPC binary in 3rdparty is compiled /MT
# So theoretically we should enable DiscordRPC in Release and static CRT build
# since we might encounter some rumtime issues when more than one CRT version are presented.
# https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-library-features?view=msvc-160#what-problems-exist-if-an-application-uses-more-than-one-crt-version
# Add other DiscordRPC binaries(compiled with /MTd, /MD, /MDd) or compile it from source may address this issue.
if(NOT IS_MULTI_CONFIG)
if(NOT(CMAKE_BUILD_TYPE MATCHES "Release" AND USE_MSVC_STATIC_CRT))
set(USE_DISCORD_RPC OFF CACHE BOOL "Discord RPC is only available in Release and static CRT build." FORCE)
endif()
endif()
if(USE_MSVC_STATIC_CRT)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
else()
# though doc ( https://cmake.org/cmake/help/latest/variable/CMAKE_MSVC_RUNTIME_LIBRARY.html )
# says if that property is not set then CMake uses the default value MultiThreaded$<$<CONFIG:Debug>:Debug>DLL
# to select a MSVC runtime library.
# But yaml-cpp set /MT(d) if CMAKE_MSVC_RUNTIME_LIBRARY is undefined
# So we have to define it explicitly
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
endif()
# TODO(cjj19970505@live.cn)
# offical QT uses dynamic CRT.
# When building our lib with static CRT and debug build type
# and linking with Qt with dynamic CRT and debug build,
# error is encountered in runtime (which is expected).
# But building our lib with static CRT and release build type,
# and linking with Qt with dynamic CRT and release build seems to be working,
# which is the same config with VS solution.
# (though technically it might still have some hidden errors).
# So we allow static CRT in both relase and debug build, but prompt warning in debug build.
# For more info:
# https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-library-features?view=msvc-160#what-problems-exist-if-an-application-uses-more-than-one-crt-version
# https://wiki.qt.io/Technical_FAQ#Why_does_a_statically_built_Qt_use_the_dynamic_Visual_Studio_runtime_libraries_.3F_Do_I_need_to_deploy_those_with_my_application_.3F
if(USE_MSVC_STATIC_CRT)
if(IS_MULTI_CONFIG OR CMAKE_BUILD_TYPE MATCHES "Debug")
message(AUTHOR_WARNING "Debug build currently can not work with static CRT.")
endif()
endif()
add_compile_options(/MP)
add_compile_options("$<$<COMPILE_LANGUAGE:C,CXX>:/MP>")
endif()
if(NOT CMAKE_SIZEOF_VOID_P EQUAL 8)
@ -142,7 +99,7 @@ if(APPLE AND CMAKE_OSX_ARCHITECTURES STREQUAL "arm64")
endif()
if(MSVC)
add_compile_options(/wd4530 /utf-8) # C++ exception handler used, but unwind semantics are not enabled
add_compile_options("$<$<COMPILE_LANGUAGE:C,CXX>:/wd4530;/utf-8>") # C++ exception handler used, but unwind semantics are not enabled
endif()
add_subdirectory(3rdparty)
@ -161,10 +118,6 @@ if (NOT FOUND_LTO EQUAL -1)
message(FATAL_ERROR "RPCS3 doesn't support building with LTO, use -DDISABLE_LTO=TRUE to force-disable it")
endif()
if(NOT WIN32)
add_compile_options(-pthread)
endif()
## Look for Gamemode if its installed on Linux
if(LINUX)
## User chooses whether to Enable GameMode features or not

View File

@ -393,7 +393,7 @@ namespace cfg
void set(const s64& value)
{
ensure(value >= Min && value <= Max);
if (value < Min || value > Max) fmt::throw_exception("'%s': value %d out of bounds (min=%d, max=%d)", m_name, value, Min, Max);
m_value = static_cast<int_type>(value);
}
@ -484,7 +484,7 @@ namespace cfg
void set(const f64& value)
{
ensure(value >= Min && value <= Max);
if (value < Min || value > Max) fmt::throw_exception("'%s': value %d out of bounds (min=%d, max=%d)", m_name, value, Min, Max);
m_value = static_cast<float_type>(value);
}
@ -571,7 +571,7 @@ namespace cfg
void set(const u64& value)
{
ensure(value >= Min && value <= Max);
if (value < Min || value > Max) fmt::throw_exception("'%s': value %d out of bounds (min=%d, max=%d)", m_name, value, Min, Max);
m_value = static_cast<int_type>(value);
}

View File

@ -166,6 +166,7 @@ static fs::error to_error(int e)
case ENOTEMPTY: return fs::error::notempty;
case EROFS: return fs::error::readonly;
case EISDIR: return fs::error::isdir;
case ENOTDIR: return fs::error::notdir;
case ENOSPC: return fs::error::nospace;
case EXDEV: return fs::error::xdev;
default: return fs::error::unknown;
@ -2833,6 +2834,7 @@ void fmt_class_string<fs::error>::format(std::string& out, u64 arg)
case fs::error::notempty: return "Not empty";
case fs::error::readonly: return "Read only";
case fs::error::isdir: return "Is a directory";
case fs::error::notdir: return "Not a directory";
case fs::error::toolong: return "Path too long";
case fs::error::nospace: return "Not enough space on the device";
case fs::error::xdev: return "Device mismatch";

View File

@ -683,6 +683,7 @@ namespace fs
notempty,
readonly,
isdir,
notdir,
toolong,
nospace,
xdev,

View File

@ -493,6 +493,10 @@ inline FT build_function_asm(std::string_view name, F&& builder, ::jit_runtime*
return reinterpret_cast<FT>(uptr(result));
}
#if defined(__INTELLISENSE__) && !defined(LLVM_AVAILABLE)
#define LLVM_AVAILABLE
#endif
#ifdef LLVM_AVAILABLE
namespace llvm

View File

@ -107,7 +107,7 @@ thread_local u64 g_tls_fault_rsx = 0;
thread_local u64 g_tls_fault_spu = 0;
thread_local u64 g_tls_wait_time = 0;
thread_local u64 g_tls_wait_fail = 0;
thread_local bool g_tls_access_violation_recovered = false;
thread_local u64 g_tls_access_violation_recovered = umax;
extern thread_local std::string(*g_tls_log_prefix)();
namespace stx
@ -1269,7 +1269,7 @@ namespace rsx
extern std::function<bool(u32 addr, bool is_writing)> g_access_violation_handler;
}
bool handle_access_violation(u32 addr, bool is_writing, ucontext_t* context) noexcept
bool handle_access_violation(u32 addr, bool is_writing, bool is_exec, ucontext_t* context) noexcept
{
g_tls_fault_all++;
@ -1305,7 +1305,7 @@ bool handle_access_violation(u32 addr, bool is_writing, ucontext_t* context) noe
}
} spu_protection{cpu};
if (addr < RAW_SPU_BASE_ADDR && vm::check_addr(addr) && rsx::g_access_violation_handler)
if (!is_exec && addr < RAW_SPU_BASE_ADDR && vm::check_addr(addr) && rsx::g_access_violation_handler)
{
bool state_changed = false;
@ -1371,7 +1371,7 @@ bool handle_access_violation(u32 addr, bool is_writing, ucontext_t* context) noe
{
auto thread = idm::get_unlocked<named_thread<spu_thread>>(spu_thread::find_raw_spu((addr - RAW_SPU_BASE_ADDR) / RAW_SPU_OFFSET));
if (!thread)
if (!thread || is_exec)
{
break;
}
@ -1503,7 +1503,9 @@ bool handle_access_violation(u32 addr, bool is_writing, ucontext_t* context) noe
static_cast<void>(context);
#endif /* ARCH_ */
if (vm::check_addr(addr, is_writing ? vm::page_writable : vm::page_readable))
const auto required_page_perms = (is_writing ? vm::page_writable : vm::page_readable) + (is_exec ? vm::page_executable : 0);
if (vm::check_addr(addr, required_page_perms))
{
return true;
}
@ -1511,9 +1513,7 @@ bool handle_access_violation(u32 addr, bool is_writing, ucontext_t* context) noe
// Hack: allocate memory in case the emulator is stopping
const auto hack_alloc = [&]()
{
g_tls_access_violation_recovered = true;
if (vm::check_addr(addr, is_writing ? vm::page_writable : vm::page_readable))
if (vm::check_addr(addr, required_page_perms))
{
return true;
}
@ -1525,17 +1525,45 @@ bool handle_access_violation(u32 addr, bool is_writing, ucontext_t* context) noe
return false;
}
extern void ppu_register_range(u32 addr, u32 size);
bool reprotected = false;
if (vm::writer_lock mlock; area->flags & vm::preallocated || vm::check_addr(addr, 0))
{
// For allocated memory with protection lower than required (such as protection::no or read-only while writing to it)
utils::memory_protect(vm::base(addr & -0x1000), 0x1000, utils::protection::rw);
reprotected = true;
}
if (reprotected)
{
if (is_exec && !vm::check_addr(addr, vm::page_executable))
{
ppu_register_range(addr & -0x10000, 0x10000);
}
g_tls_access_violation_recovered = addr;
return true;
}
return area->falloc(addr & -0x10000, 0x10000) || vm::check_addr(addr, is_writing ? vm::page_writable : vm::page_readable);
const bool allocated = area->falloc(addr & -0x10000, 0x10000);
if (allocated)
{
if (is_exec && !vm::check_addr(addr, vm::page_executable))
{
ppu_register_range(addr & -0x10000, 0x10000);
}
g_tls_access_violation_recovered = addr;
return true;
}
return false;
};
if (cpu && (cpu->get_class() == thread_class::ppu || cpu->get_class() == thread_class::spu))
if (cpu && (cpu->get_class() == thread_class::ppu || cpu->get_class() == thread_class::spu) && !is_exec)
{
vm::temporary_unlock(*cpu);
u32 pf_port_id = 0;
@ -1678,7 +1706,7 @@ bool handle_access_violation(u32 addr, bool is_writing, ucontext_t* context) noe
if (cpu->get_class() == thread_class::spu)
{
if (!g_tls_access_violation_recovered)
if (g_tls_access_violation_recovered != addr)
{
vm_log.notice("\n%s", dump_useful_thread_info());
vm_log.always()("[%s] Access violation %s location 0x%x (%s)", cpu->get_name(), is_writing ? "writing" : "reading", addr, (is_writing && vm::check_addr(addr)) ? "read-only memory" : "unmapped memory");
@ -1714,10 +1742,10 @@ bool handle_access_violation(u32 addr, bool is_writing, ucontext_t* context) noe
// Note: a thread may access violate more than once after hack_alloc recovery
// Do not log any further access violations in this case.
if (!g_tls_access_violation_recovered)
if (g_tls_access_violation_recovered != addr)
{
vm_log.notice("\n%s", dump_useful_thread_info());
vm_log.fatal("Access violation %s location 0x%x (%s)", is_writing ? "writing" : (cpu && cpu->get_class() == thread_class::ppu && cpu->get_pc() == addr ? "executing" : "reading"), addr, (is_writing && vm::check_addr(addr)) ? "read-only memory" : "unmapped memory");
vm_log.fatal("Access violation %s location 0x%x (%s)", is_writing ? "writing" : (is_exec ? "executing" : "reading"), addr, (is_writing && vm::check_addr(addr)) ? "read-only memory" : "unmapped memory");
}
while (Emu.IsPausedOrReady())
@ -1766,8 +1794,13 @@ bool handle_access_violation(u32 addr, bool is_writing, ucontext_t* context) noe
}
}
if (Emu.IsStopped() && !hack_alloc())
if (Emu.IsStopped())
{
while (!hack_alloc())
{
thread_ctrl::wait_for(1000);
}
return false;
}
@ -1806,6 +1839,7 @@ static LONG exception_handler(PEXCEPTION_POINTERS pExp) noexcept
if (pExp->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION && !is_executing)
{
u32 addr = 0;
bool is_exec = false;
if (auto [addr0, ok] = vm::try_get_addr(ptr); ok)
{
@ -1813,14 +1847,21 @@ static LONG exception_handler(PEXCEPTION_POINTERS pExp) noexcept
}
else if (const usz exec64 = (ptr - vm::g_exec_addr) / 2; exec64 <= u32{umax})
{
is_exec = true;
addr = static_cast<u32>(exec64);
}
else
else if (const usz exec64 = (ptr - vm::g_exec_addr - vm::g_exec_addr_seg_offset); exec64 <= u32{umax})
{
is_exec = true;
addr = static_cast<u32>(exec64);
}
else
{
std::this_thread::sleep_for(1ms);
return EXCEPTION_CONTINUE_SEARCH;
}
if (thread_ctrl::get_current() && handle_access_violation(addr, is_writing, pExp->ContextRecord))
if (thread_ctrl::get_current() && handle_access_violation(addr, is_writing, is_exec, pExp->ContextRecord))
{
return EXCEPTION_CONTINUE_EXECUTION;
}
@ -2027,12 +2068,13 @@ static void signal_handler(int /*sig*/, siginfo_t* info, void* uct) noexcept
#endif
const u64 exec64 = (reinterpret_cast<u64>(info->si_addr) - reinterpret_cast<u64>(vm::g_exec_addr)) / 2;
const u64 exec64_2 = (reinterpret_cast<u64>(info->si_addr) - reinterpret_cast<u64>(vm::g_exec_addr)) - vm::g_exec_addr_seg_offset;
const auto cause = is_executing ? "executing" : is_writing ? "writing" : "reading";
if (auto [addr, ok] = vm::try_get_addr(info->si_addr); ok && !is_executing)
{
// Try to process access violation
if (thread_ctrl::get_current() && handle_access_violation(addr, is_writing, context))
if (thread_ctrl::get_current() && handle_access_violation(addr, is_writing, false, context))
{
return;
}
@ -2040,7 +2082,14 @@ static void signal_handler(int /*sig*/, siginfo_t* info, void* uct) noexcept
if (exec64 < 0x100000000ull && !is_executing)
{
if (thread_ctrl::get_current() && handle_access_violation(static_cast<u32>(exec64), is_writing, context))
if (thread_ctrl::get_current() && handle_access_violation(static_cast<u32>(exec64), is_writing, true, context))
{
return;
}
}
else if (exec64_2 < 0x100000000ull && !is_executing)
{
if (thread_ctrl::get_current() && handle_access_violation(static_cast<u32>(exec64_2), is_writing, true, context))
{
return;
}
@ -2359,7 +2408,7 @@ thread_base::native_entry thread_base::finalize(u64 _self) noexcept
g_tls_fault_spu = 0;
g_tls_wait_time = 0;
g_tls_wait_fail = 0;
g_tls_access_violation_recovered = false;
g_tls_access_violation_recovered = umax;
g_tls_log_prefix = []() -> std::string { return {}; };
@ -2830,6 +2879,16 @@ void thread_base::exec()
}
}
if (auto [total, current] = utils::get_memory_usage(); total - current <= 256 * 1024 * 1024)
{
if (reason_buf.empty())
{
reason_buf = std::string{reason};
}
fmt::append(reason_buf, " (Possible RAM deficiency: free RAM: %dMB)", (total - current) / (1024 * 1024));
}
if (!reason_buf.empty())
{
reason = reason_buf;

View File

@ -96,11 +96,6 @@ else()
# This hides our LLVM from mesa's LLVM, otherwise we get some unresolvable conflicts.
add_link_options(-Wl,--exclude-libs,ALL)
elseif(WIN32)
add_compile_definitions(__STDC_FORMAT_MACROS=1)
# Workaround for mingw64 (MSYS2)
add_link_options(-Wl,--allow-multiple-definition)
# Increase stack limit to 8 MB
add_link_options(-Wl,--stack -Wl,8388608)
endif()

View File

@ -1,4 +1,3 @@
set(WOLFSSL_LIBRARY ON)
set(WOLFSSL_INCLUDE_DIR ON)
set(WOLFSSL_LIBRARIES wolfssl)
set(WOLFSSL_LIBRARY wolfssl)
set(WOLFSSL_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/3rdparty/wolfssl)
set(WOLFSSL_FOUND TRUE)

View File

@ -3,9 +3,9 @@ if(USE_SYSTEM_ZLIB)
find_package(ZLIB)
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR})
else()
add_library(ZLIB::ZLIB INTERFACE IMPORTED)
add_library(ZLIB::ZLIB STATIC IMPORTED)
set_target_properties(ZLIB::ZLIB PROPERTIES
INTERFACE_LINK_LIBRARIES zlibstatic
IMPORTED_LOCATION "${CMAKE_BINARY_DIR}/3rdparty/zlib/zlib/libzlibstatic.a"
INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}/3rdparty/zlib/zlib;${CMAKE_BINARY_DIR}/3rdparty/zlib/zlib")
set(ZLIB_FOUND TRUE)
endif()

View File

@ -87,12 +87,6 @@ if (NOT ANDROID)
message(FATAL_ERROR "RPCS3 requires either X11 or Wayland (or both) for Vulkan.")
endif()
if(UNIX)
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
find_package(Threads REQUIRED)
target_link_libraries(rpcs3_lib PRIVATE Threads::Threads)
endif()
if(WIN32)
target_link_libraries(rpcs3_lib PRIVATE ws2_32 Iphlpapi Winmm Psapi gdi32 setupapi)
else()

View File

@ -645,6 +645,9 @@ if(TARGET 3rdparty_vulkan)
endif()
endif()
if(NOT WIN32)
set(THREADS_PREFER_PTHREAD_FLAG ON)
endif()
find_package(Threads REQUIRED)
target_link_libraries(rpcs3_emu

View File

@ -888,6 +888,14 @@ bool cpu_thread::check_state() noexcept
store = true;
}
if (flags & cpu_flag::req_exit)
{
// A request for the thread to quit has been made
flags -= cpu_flag::req_exit;
flags += cpu_flag::exit;
store = true;
}
// Can't process dbg_step if we only paused temporarily
if (cpu_can_stop && flags & cpu_flag::dbg_step)
{
@ -1157,13 +1165,13 @@ void cpu_thread::notify()
cpu_thread& cpu_thread::operator=(thread_state)
{
if (state & cpu_flag::exit)
if (state & (cpu_flag::exit + cpu_flag::req_exit))
{
// Must be notified elsewhere or self-raised
return *this;
}
const auto old = state.fetch_add(cpu_flag::exit);
const auto old = state.fetch_add(cpu_flag::req_exit);
if (old & cpu_flag::wait && old.none_of(cpu_flag::again + cpu_flag::exit))
{
@ -1322,8 +1330,9 @@ extern std::shared_ptr<CPUDisAsm> make_disasm(const cpu_thread* cpu, shared_ptr<
void cpu_thread::dump_all(std::string& ret) const
{
std::any func_data;
std::any misc_data;
ret += dump_misc();
dump_misc(ret, misc_data);
ret += '\n';
dump_regs(ret, func_data);
ret += '\n';
@ -1371,9 +1380,9 @@ std::vector<std::pair<u32, u32>> cpu_thread::dump_callstack_list() const
return {};
}
std::string cpu_thread::dump_misc() const
void cpu_thread::dump_misc(std::string& ret, std::any& /*custom_data*/) const
{
return fmt::format("%s[0x%x]; State: %s\n", get_class() == thread_class::ppu ? "PPU" : get_class() == thread_class::spu ? "SPU" : "RSX", id, state.load());
fmt::append(ret, "%s[0x%x]; State: %s\n", get_class() == thread_class::ppu ? "PPU" : get_class() == thread_class::spu ? "SPU" : "RSX", id, state.load());
}
bool cpu_thread::suspend_work::push(cpu_thread* _this) noexcept

View File

@ -29,6 +29,7 @@ enum class cpu_flag : u32
yield, // Thread is being requested to yield its execution time if it's running
preempt, // Thread is being requested to preempt the execution of all CPU threads
req_exit, // Request the thread to exit
dbg_global_pause, // Emulation paused
dbg_pause, // Thread paused
dbg_step, // Thread forced to pause after one step (one instruction, etc)
@ -39,7 +40,7 @@ enum class cpu_flag : u32
// Test stopped state
constexpr bool is_stopped(bs_t<cpu_flag> state)
{
return !!(state & (cpu_flag::stop + cpu_flag::exit + cpu_flag::again));
return !!(state & (cpu_flag::stop + cpu_flag::exit + cpu_flag::again + cpu_flag::req_exit));
}
// Test paused state
@ -176,7 +177,7 @@ public:
virtual std::vector<std::pair<u32, u32>> dump_callstack_list() const;
// Get CPU dump of misc information
virtual std::string dump_misc() const;
virtual void dump_misc(std::string& ret, std::any& /*custom_data*/) const;
// Thread entry point function
virtual void cpu_task() = 0;

View File

@ -210,7 +210,7 @@ void cpu_translator::initialize(llvm::LLVMContext& context, llvm::ExecutionEngin
#endif
}
llvm::Value* cpu_translator::bitcast(llvm::Value* val, llvm::Type* type) const
llvm::Value* cpu_translator::bitcast(llvm::Value* val, llvm::Type* type, std::source_location src_loc) const
{
uint s1 = type->getScalarSizeInBits();
uint s2 = val->getType()->getScalarSizeInBits();
@ -222,15 +222,69 @@ llvm::Value* cpu_translator::bitcast(llvm::Value* val, llvm::Type* type) const
if (s1 != s2)
{
fmt::throw_exception("cpu_translator::bitcast(): incompatible type sizes (%u vs %u)", s1, s2);
fmt::throw_exception("cpu_translator::bitcast(): incompatible type sizes (%u vs %u)\nCalled from: %s", s1, s2, src_loc);
}
if (const auto c1 = llvm::dyn_cast<llvm::Constant>(val))
if (val->getType() == type)
{
return val;
}
llvm::CastInst* i;
llvm::Value* source_val = val;
// Try to reuse older bitcasts
while ((i = llvm::dyn_cast_or_null<llvm::CastInst>(source_val)) && i->getOpcode() == llvm::Instruction::BitCast)
{
source_val = i->getOperand(0);
if (source_val->getType() == type)
{
return source_val;
}
}
for (auto it = source_val->use_begin(); it != source_val->use_end(); ++it)
{
llvm::Value* it_val = *it;
if (!it_val)
{
continue;
}
llvm::CastInst* bci = llvm::dyn_cast_or_null<llvm::CastInst>(it_val);
// Walk through bitcasts
while (bci && bci->getOpcode() == llvm::Instruction::BitCast)
{
if (bci->getParent() != m_ir->GetInsertBlock())
{
break;
}
if (bci->getType() == type)
{
return bci;
}
if (bci->use_begin() == bci->use_end())
{
break;
}
bci = llvm::dyn_cast_or_null<llvm::CastInst>(*bci->use_begin());
}
}
// Do bitcast on the source
if (const auto c1 = llvm::dyn_cast<llvm::Constant>(source_val))
{
return ensure(llvm::ConstantFoldCastOperand(llvm::Instruction::BitCast, c1, type, m_module->getDataLayout()));
}
return m_ir->CreateBitCast(val, type);
return m_ir->CreateBitCast(source_val, type);
}
template <>

View File

@ -43,6 +43,7 @@
#include <functional>
#include <unordered_map>
#include <source_location>
// Helper function
llvm::Value* peek_through_bitcasts(llvm::Value*);
@ -567,6 +568,32 @@ struct llvm_placeholder_t
}
};
template <typename T, typename U = llvm_common_t<llvm_value_t<T>>>
struct llvm_place_stealer_t
{
// TODO: placeholder extracting actual constant values (u64, f64, vector, etc)
using type = T;
static constexpr bool is_ok = true;
llvm::Value* eval(llvm::IRBuilder<>*) const
{
return nullptr;
}
std::tuple<> match(llvm::Value*& value, llvm::Module*) const
{
if (value && value->getType() == llvm_value_t<T>::get_type(value->getContext()))
{
return {};
}
value = nullptr;
return {};
}
};
template <typename T, bool ForceSigned = false>
struct llvm_const_int
{
@ -3213,7 +3240,7 @@ public:
}
// Bitcast with immediate constant folding
llvm::Value* bitcast(llvm::Value* val, llvm::Type* type) const;
llvm::Value* bitcast(llvm::Value* val, llvm::Type* type, std::source_location src_loc = std::source_location::current()) const;
template <typename T>
llvm::Value* bitcast(llvm::Value* val)
@ -3227,6 +3254,12 @@ public:
return {};
}
template <typename T>
static llvm_place_stealer_t<T> match_stealer()
{
return {};
}
template <typename T>
requires requires { typename llvm_common_t<T>; }
static auto match_expr(llvm::Value* v, llvm::Module* _m, T&& expr)
@ -3951,6 +3984,15 @@ public:
erase_stores({args.value...});
}
// Debug breakpoint
void debugtrap()
{
const auto _rty = llvm::Type::getVoidTy(m_context);
const auto type = llvm::FunctionType::get(_rty, {}, false);
const auto func = llvm::cast<llvm::Function>(m_ir->GetInsertBlock()->getParent()->getParent()->getOrInsertFunction("llvm.debugtrap", type).getCallee());
m_ir->CreateCall(func);
}
template <typename T, typename U>
static auto pshufb(T&& a, U&& b)
{

View File

@ -261,7 +261,7 @@ void LpcmDecContext::exec(ppu_thread& ppu)
savestate = lpcm_dec_state::waiting_for_output_mutex_lock;
output_mutex_lock:
error_occurred |= static_cast<u32>(sys_mutex_lock(ppu, output_mutex, 0) != CELL_OK);
error_occurred |= static_cast<u32>(lv2_syscall<sys_mutex_lock>(ppu, output_mutex, 0) != CELL_OK);
if (ppu.state & cpu_flag::again)
{
@ -273,7 +273,7 @@ void LpcmDecContext::exec(ppu_thread& ppu)
savestate = lpcm_dec_state::waiting_for_output_cond_wait;
output_cond_wait:
ensure(sys_cond_wait(ppu, output_consumed, 0) == CELL_OK); // Error code isn't checked on LLE
ensure(lv2_syscall<sys_cond_wait>(ppu, output_consumed, 0) == CELL_OK); // Error code isn't checked on LLE
if (ppu.state & cpu_flag::again)
{
@ -287,7 +287,7 @@ void LpcmDecContext::exec(ppu_thread& ppu)
savestate = lpcm_dec_state::queue_mutex_lock;
queue_mutex_lock:
ensure(sys_mutex_lock(ppu, queue_mutex, 0) == CELL_OK); // Error code isn't checked on LLE
ensure(lv2_syscall<sys_mutex_lock>(ppu, queue_mutex, 0) == CELL_OK); // Error code isn't checked on LLE
if (ppu.state & cpu_flag::again)
{
@ -296,7 +296,7 @@ void LpcmDecContext::exec(ppu_thread& ppu)
cmd_queue.pop(cmd);
ensure(sys_mutex_unlock(ppu, queue_mutex) == CELL_OK); // Error code isn't checked on LLE
ensure(lv2_syscall<sys_mutex_unlock>(ppu, queue_mutex) == CELL_OK); // Error code isn't checked on LLE
cellAdec.trace("Command type: %d", static_cast<u32>(cmd.type.get()));
@ -307,7 +307,7 @@ void LpcmDecContext::exec(ppu_thread& ppu)
{
case LpcmDecCmdType::start_seq:
// LLE sends a command to the SPU thread. The SPU thread consumes the command without doing anything, however
error_occurred |= static_cast<u32>(sys_mutex_unlock(ppu, output_mutex) != CELL_OK);
error_occurred |= static_cast<u32>(lv2_syscall<sys_mutex_unlock>(ppu, output_mutex) != CELL_OK);
break;
case LpcmDecCmdType::end_seq:
@ -324,11 +324,11 @@ void LpcmDecContext::exec(ppu_thread& ppu)
// Doesn't do anything else
notify_seq_done.cbFunc(ppu, notify_seq_done.cbArg);
error_occurred |= static_cast<u32>(sys_mutex_unlock(ppu, output_mutex) != CELL_OK);
error_occurred |= static_cast<u32>(lv2_syscall<sys_mutex_unlock>(ppu, output_mutex) != CELL_OK);
break;
}
case LpcmDecCmdType::close:
ensure(sys_mutex_unlock(ppu, output_mutex) == CELL_OK); // Error code isn't checked on LLE
ensure(lv2_syscall<sys_mutex_unlock>(ppu, output_mutex) == CELL_OK); // Error code isn't checked on LLE
return;
case LpcmDecCmdType::decode_au:
@ -685,7 +685,7 @@ void LpcmDecContext::exec(ppu_thread& ppu)
notify_au_done.cbFunc(ppu, cmd.pcm_handle, notify_au_done.cbArg);
output_locked = true;
error_occurred |= static_cast<u32>(sys_mutex_unlock(ppu, output_mutex) != CELL_OK);
error_occurred |= static_cast<u32>(lv2_syscall<sys_mutex_unlock>(ppu, output_mutex) != CELL_OK);
const vm::var<CellAdecLpcmInfo> bsi_info{{ lpcm_param->channelNumber, lpcm_param->sampleRate, static_cast<u32>(output_size) }};
@ -703,14 +703,14 @@ error_code LpcmDecContext::send_command(ppu_thread& ppu, auto&&... args)
{
ppu.state += cpu_flag::wait;
if (error_code ret = sys_mutex_lock(ppu, queue_size_mutex, 0); ret != CELL_OK)
if (error_code ret = lv2_syscall<sys_mutex_lock>(ppu, queue_size_mutex, 0); ret != CELL_OK)
{
return ret;
}
if (cmd_queue.full())
{
ensure(sys_mutex_unlock(ppu, queue_size_mutex) == CELL_OK); // Error code isn't checked on LLE
ensure(lv2_syscall<sys_mutex_unlock>(ppu, queue_size_mutex) == CELL_OK); // Error code isn't checked on LLE
return CELL_ADEC_ERROR_BUSY;
}
@ -720,39 +720,39 @@ error_code LpcmDecContext::send_command(ppu_thread& ppu, auto&&... args)
*lpcm_param = { args... };
}
if (error_code ret = sys_mutex_lock(ppu, queue_mutex, 0); ret != CELL_OK)
if (error_code ret = lv2_syscall<sys_mutex_lock>(ppu, queue_mutex, 0); ret != CELL_OK)
{
ensure(sys_mutex_unlock(ppu, queue_size_mutex) == CELL_OK); // Error code isn't checked on LLE
ensure(lv2_syscall<sys_mutex_unlock>(ppu, queue_size_mutex) == CELL_OK); // Error code isn't checked on LLE
return ret;
}
cmd_queue.emplace(type, std::forward<decltype(args)>(args)...);
if (error_code ret = sys_mutex_unlock(ppu, queue_mutex); ret != CELL_OK
if (error_code ret = lv2_syscall<sys_mutex_unlock>(ppu, queue_mutex); ret != CELL_OK
|| (ret = cmd_available.release(ppu)) != CELL_OK)
{
ensure(sys_mutex_unlock(ppu, queue_size_mutex) == CELL_OK); // Error code isn't checked on LLE
ensure(lv2_syscall<sys_mutex_unlock>(ppu, queue_size_mutex) == CELL_OK); // Error code isn't checked on LLE
return ret;
}
return sys_mutex_unlock(ppu, queue_size_mutex);
return lv2_syscall<sys_mutex_unlock>(ppu, queue_size_mutex);
}
inline error_code LpcmDecContext::release_output(ppu_thread& ppu)
{
if (error_code ret = sys_mutex_lock(ppu, output_mutex, 0); ret != CELL_OK)
if (error_code ret = lv2_syscall<sys_mutex_lock>(ppu, output_mutex, 0); ret != CELL_OK)
{
return ret;
}
output_locked = false;
if (error_code ret = sys_cond_signal(ppu, output_consumed); ret != CELL_OK)
if (error_code ret = lv2_syscall<sys_cond_signal>(ppu, output_consumed); ret != CELL_OK)
{
return ret; // LLE doesn't unlock the mutex
}
return sys_mutex_unlock(ppu, output_mutex);
return lv2_syscall<sys_mutex_unlock>(ppu, output_mutex);
}
void lpcmDecEntry(ppu_thread& ppu, vm::ptr<LpcmDecContext> lpcm_dec)
@ -820,13 +820,13 @@ error_code _CellAdecCoreOpOpenExt_lpcm(ppu_thread& ppu, vm::ptr<LpcmDecContext>
const vm::var<sys_mutex_attribute_t> queue_mutex_attr{{ SYS_SYNC_PRIORITY, SYS_SYNC_NOT_RECURSIVE, SYS_SYNC_NOT_PROCESS_SHARED, SYS_SYNC_NOT_ADAPTIVE, 0, 0, 0, { "_adem06"_u64 } }};
const vm::var<sys_cond_attribute_t> cond_attr{{ SYS_SYNC_NOT_PROCESS_SHARED, 0, 0, { "_adec03"_u64 } }};
error_code ret = sys_mutex_create(ppu, handle.ptr(&LpcmDecContext::queue_size_mutex), mutex_attr);
ret = ret ? ret : sys_cond_create(ppu, handle.ptr(&LpcmDecContext::queue_size_cond), handle->queue_size_mutex, cond_attr);
ret = ret ? ret : sys_mutex_create(ppu, handle.ptr(&LpcmDecContext::unk_mutex), mutex_attr);
ret = ret ? ret : sys_cond_create(ppu, handle.ptr(&LpcmDecContext::unk_cond), handle->unk_mutex, cond_attr);
ret = ret ? ret : sys_mutex_create(ppu, handle.ptr(&LpcmDecContext::output_mutex), output_mutex_attr);
ret = ret ? ret : sys_cond_create(ppu, handle.ptr(&LpcmDecContext::output_consumed), handle->output_mutex, cond_attr);
ret = ret ? ret : sys_mutex_create(ppu, handle.ptr(&LpcmDecContext::queue_mutex), queue_mutex_attr);
error_code ret = lv2_syscall<sys_mutex_create>(ppu, handle.ptr(&LpcmDecContext::queue_size_mutex), mutex_attr);
ret = ret ? ret : lv2_syscall<sys_cond_create>(ppu, handle.ptr(&LpcmDecContext::queue_size_cond), handle->queue_size_mutex, cond_attr);
ret = ret ? ret : lv2_syscall<sys_mutex_create>(ppu, handle.ptr(&LpcmDecContext::unk_mutex), mutex_attr);
ret = ret ? ret : lv2_syscall<sys_cond_create>(ppu, handle.ptr(&LpcmDecContext::unk_cond), handle->unk_mutex, cond_attr);
ret = ret ? ret : lv2_syscall<sys_mutex_create>(ppu, handle.ptr(&LpcmDecContext::output_mutex), output_mutex_attr);
ret = ret ? ret : lv2_syscall<sys_cond_create>(ppu, handle.ptr(&LpcmDecContext::output_consumed), handle->output_mutex, cond_attr);
ret = ret ? ret : lv2_syscall<sys_mutex_create>(ppu, handle.ptr(&LpcmDecContext::queue_mutex), queue_mutex_attr);
ret = ret ? ret : handle->release_output(ppu);
ret = ret ? ret : handle->cmd_available.init(ppu, handle.ptr(&LpcmDecContext::cmd_available), 0);
ret = ret ? ret : handle->reserved2.init(ppu, handle.ptr(&LpcmDecContext::reserved2), 0);
@ -844,8 +844,8 @@ error_code _CellAdecCoreOpOpenExt_lpcm(ppu_thread& ppu, vm::ptr<LpcmDecContext>
const auto entry = g_fxo->get<ppu_function_manager>().func_addr(FIND_FUNC(lpcmDecEntry));
ret = ppu_execute<&sys_ppu_thread_create>(ppu, handle.ptr(&LpcmDecContext::thread_id), entry, handle.addr(), +res->ppuThreadPriority, +res->ppuThreadStackSize, SYS_PPU_THREAD_CREATE_JOINABLE, +_name);
ret = ret ? ret : sys_mutex_create(ppu, handle.ptr(&LpcmDecContext::spurs_queue_pop_mutex), mutex_attr);
ret = ret ? ret : sys_mutex_create(ppu, handle.ptr(&LpcmDecContext::spurs_queue_push_mutex), mutex_attr);
ret = ret ? ret : lv2_syscall<sys_mutex_create>(ppu, handle.ptr(&LpcmDecContext::spurs_queue_pop_mutex), mutex_attr);
ret = ret ? ret : lv2_syscall<sys_mutex_create>(ppu, handle.ptr(&LpcmDecContext::spurs_queue_push_mutex), mutex_attr);
return ret;
}
@ -865,8 +865,8 @@ error_code _CellAdecCoreOpClose_lpcm(ppu_thread& ppu, vm::ptr<LpcmDecContext> ha
cellAdec.notice("_CellAdecCoreOpClose_lpcm(handle=*0x%x)", handle);
if (error_code ret = sys_mutex_lock(ppu, handle->queue_size_mutex, 0); ret != CELL_OK
|| (ret = sys_mutex_lock(ppu, handle->queue_mutex, 0)) != CELL_OK)
if (error_code ret = lv2_syscall<sys_mutex_lock>(ppu, handle->queue_size_mutex, 0); ret != CELL_OK
|| (ret = lv2_syscall<sys_mutex_lock>(ppu, handle->queue_mutex, 0)) != CELL_OK)
{
return ret;
}
@ -875,14 +875,14 @@ error_code _CellAdecCoreOpClose_lpcm(ppu_thread& ppu, vm::ptr<LpcmDecContext> ha
{
handle->cmd_queue.emplace(LpcmDecCmdType::close);
if (error_code ret = sys_mutex_unlock(ppu, handle->queue_mutex); ret != CELL_OK)
if (error_code ret = lv2_syscall<sys_mutex_unlock>(ppu, handle->queue_mutex); ret != CELL_OK)
{
return ret; // LLE doesn't unlock the queue size mutex
}
if (error_code ret = handle->cmd_available.release(ppu); ret != CELL_OK)
{
ensure(sys_mutex_unlock(ppu, handle->queue_size_mutex) == CELL_OK); // Error code isn't checked on LLE
ensure(lv2_syscall<sys_mutex_unlock>(ppu, handle->queue_size_mutex) == CELL_OK); // Error code isn't checked on LLE
return ret;
}
}
@ -893,29 +893,29 @@ error_code _CellAdecCoreOpClose_lpcm(ppu_thread& ppu, vm::ptr<LpcmDecContext> ha
cmd.type = LpcmDecCmdType::close;
}
if (error_code ret = sys_mutex_unlock(ppu, handle->queue_mutex); ret != CELL_OK)
if (error_code ret = lv2_syscall<sys_mutex_unlock>(ppu, handle->queue_mutex); ret != CELL_OK)
{
return ret; // LLE doesn't unlock the queue size mutex
}
}
error_code ret = sys_mutex_unlock(ppu, handle->queue_size_mutex);
error_code ret = lv2_syscall<sys_mutex_unlock>(ppu, handle->queue_size_mutex);
ret = ret ? ret : handle->release_output(ppu);
vm::var<u64> thread_ret;
ret = ret ? ret : sys_ppu_thread_join(ppu, static_cast<u32>(handle->thread_id), +thread_ret);
ret = ret ? ret : lv2_syscall<sys_ppu_thread_join>(ppu, static_cast<u32>(handle->thread_id), +thread_ret);
ret = ret ? ret : sys_cond_destroy(ppu, handle->queue_size_cond);
ret = ret ? ret : sys_cond_destroy(ppu, handle->unk_cond);
ret = ret ? ret : sys_cond_destroy(ppu, handle->output_consumed);
ret = ret ? ret : sys_mutex_destroy(ppu, handle->queue_mutex);
ret = ret ? ret : sys_mutex_destroy(ppu, handle->queue_size_mutex);
ret = ret ? ret : sys_mutex_destroy(ppu, handle->unk_mutex);
ret = ret ? ret : sys_mutex_destroy(ppu, handle->output_mutex);
ret = ret ? ret : lv2_syscall<sys_cond_destroy>(ppu, handle->queue_size_cond);
ret = ret ? ret : lv2_syscall<sys_cond_destroy>(ppu, handle->unk_cond);
ret = ret ? ret : lv2_syscall<sys_cond_destroy>(ppu, handle->output_consumed);
ret = ret ? ret : lv2_syscall<sys_mutex_destroy>(ppu, handle->queue_mutex);
ret = ret ? ret : lv2_syscall<sys_mutex_destroy>(ppu, handle->queue_size_mutex);
ret = ret ? ret : lv2_syscall<sys_mutex_destroy>(ppu, handle->unk_mutex);
ret = ret ? ret : lv2_syscall<sys_mutex_destroy>(ppu, handle->output_mutex);
ret = ret ? ret : handle->cmd_available.finalize(ppu);
ret = ret ? ret : handle->reserved2.finalize(ppu);
ret = ret ? ret : sys_mutex_destroy(ppu, handle->spurs_queue_pop_mutex);
ret = ret ? ret : sys_mutex_destroy(ppu, handle->spurs_queue_push_mutex);
ret = ret ? ret : lv2_syscall<sys_mutex_destroy>(ppu, handle->spurs_queue_pop_mutex);
ret = ret ? ret : lv2_syscall<sys_mutex_destroy>(ppu, handle->spurs_queue_push_mutex);
return ret;
}
@ -1091,11 +1091,11 @@ error_code AdecContext::set_pcm_item(s32 pcm_handle, vm::ptr<void> pcm_addr, u32
error_code AdecContext::link_frame(ppu_thread& ppu, s32 pcm_handle)
{
ensure(sys_mutex_lock(ppu, mutex, 0) == CELL_OK); // Error code isn't checked on LLE
ensure(lv2_syscall<sys_mutex_lock>(ppu, mutex, 0) == CELL_OK); // Error code isn't checked on LLE
if (verify_pcm_handle(pcm_handle) == static_cast<s32>(CELL_ADEC_ERROR_FATAL))
{
ensure(sys_mutex_unlock(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE
ensure(lv2_syscall<sys_mutex_unlock>(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE
return CELL_ADEC_ERROR_FATAL;
}
@ -1115,27 +1115,27 @@ error_code AdecContext::link_frame(ppu_thread& ppu, s32 pcm_handle)
}
else
{
ensure(sys_mutex_unlock(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE
ensure(lv2_syscall<sys_mutex_unlock>(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE
return CELL_ADEC_ERROR_FATAL;
}
ensure(sys_mutex_unlock(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE
ensure(lv2_syscall<sys_mutex_unlock>(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE
return CELL_OK;
}
error_code AdecContext::unlink_frame(ppu_thread& ppu, s32 pcm_handle)
{
ensure(sys_mutex_lock(ppu, mutex, 0) == CELL_OK); // Error code isn't checked on LLE
ensure(lv2_syscall<sys_mutex_lock>(ppu, mutex, 0) == CELL_OK); // Error code isn't checked on LLE
if (verify_pcm_handle(pcm_handle) == static_cast<s32>(CELL_ADEC_ERROR_FATAL))
{
ensure(sys_mutex_unlock(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE
ensure(lv2_syscall<sys_mutex_unlock>(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE
return CELL_ADEC_ERROR_FATAL;
}
if (frames_head == -1 || frames_tail == -1)
{
ensure(sys_mutex_unlock(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE
ensure(lv2_syscall<sys_mutex_unlock>(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE
return CELL_ADEC_ERROR_FATAL;
}
@ -1146,7 +1146,7 @@ error_code AdecContext::unlink_frame(ppu_thread& ppu, s32 pcm_handle)
{
if (pcm_handle != frames_tail)
{
ensure(sys_mutex_unlock(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE
ensure(lv2_syscall<sys_mutex_unlock>(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE
return CELL_ADEC_ERROR_FATAL;
}
@ -1171,7 +1171,7 @@ error_code AdecContext::unlink_frame(ppu_thread& ppu, s32 pcm_handle)
frames[prev].next = next;
}
ensure(sys_mutex_unlock(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE
ensure(lv2_syscall<sys_mutex_unlock>(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE
return CELL_OK;
}
@ -1557,7 +1557,7 @@ error_code adecOpen(ppu_thread& ppu, vm::ptr<CellAdecType> type, vm::cptr<CellAd
frames[i].prev = 0;
}
ensure(sys_mutex_create(ppu, _this.ptr(&AdecContext::mutex), _this.ptr(&AdecContext::mutex_attribute)) == CELL_OK); // Error code isn't checked on LLE
ensure(lv2_syscall<sys_mutex_create>(ppu, _this.ptr(&AdecContext::mutex), _this.ptr(&AdecContext::mutex_attribute)) == CELL_OK); // Error code isn't checked on LLE
*handle = _this;
@ -1626,7 +1626,7 @@ error_code cellAdecClose(ppu_thread& ppu, vm::ptr<AdecContext> handle)
return ret;
}
if (error_code ret = sys_mutex_destroy(ppu, handle->mutex); ret != CELL_OK)
if (error_code ret = lv2_syscall<sys_mutex_destroy>(ppu, handle->mutex); ret != CELL_OK)
{
return ret;
}

View File

@ -485,6 +485,19 @@ struct AdecFrame
CHECK_SIZE(AdecFrame, 0x68);
template <auto Syscall>
static auto lv2_syscall(ppu_thread& ppu, auto&&... args)
{
const auto ret = Syscall(ppu, std::forward<decltype(args)>(args)...);
if (ppu.test_stopped())
{
ppu.state += cpu_flag::again;
}
return ret;
}
class AdecOutputQueue
{
struct entry
@ -511,10 +524,10 @@ public:
this->size = 0;
const vm::var<sys_mutex_attribute_t> mutex_attr = {{ SYS_SYNC_PRIORITY, SYS_SYNC_NOT_RECURSIVE, SYS_SYNC_NOT_PROCESS_SHARED, SYS_SYNC_NOT_ADAPTIVE, 0, 0, 0, { "_adem07"_u64 } }};
ensure(sys_mutex_create(ppu, _this.ptr(&AdecOutputQueue::mutex), mutex_attr) == CELL_OK); // Error code isn't checked on LLE
ensure(lv2_syscall<sys_mutex_create>(ppu, _this.ptr(&AdecOutputQueue::mutex), mutex_attr) == CELL_OK); // Error code isn't checked on LLE
const vm::var<sys_cond_attribute_t> cond_attr = {{ SYS_SYNC_NOT_PROCESS_SHARED, 0, 0, { "_adec05"_u64 } }};
ensure(sys_cond_create(ppu, _this.ptr(&AdecOutputQueue::cond), mutex, cond_attr) == CELL_OK); // Error code isn't checked on LLE
ensure(lv2_syscall<sys_cond_create>(ppu, _this.ptr(&AdecOutputQueue::cond), mutex, cond_attr) == CELL_OK); // Error code isn't checked on LLE
for (s32 i = 0; i < 4; i++)
{
@ -524,12 +537,12 @@ public:
error_code finalize(ppu_thread& ppu) const
{
if (error_code ret = sys_cond_destroy(ppu, cond); ret != CELL_OK)
if (error_code ret = lv2_syscall<sys_cond_destroy>(ppu, cond); ret != CELL_OK)
{
return ret;
}
if (error_code ret = sys_mutex_destroy(ppu, mutex); ret != CELL_OK)
if (error_code ret = lv2_syscall<sys_mutex_destroy>(ppu, mutex); ret != CELL_OK)
{
return ret;
}
@ -539,11 +552,11 @@ public:
error_code push(ppu_thread& ppu, vm::ptr<CellAdecPcmItem> pcm_item, s32 pcm_handle)
{
ensure(sys_mutex_lock(ppu, mutex, 0) == CELL_OK); // Error code isn't checked on LLE
ensure(lv2_syscall<sys_mutex_lock>(ppu, mutex, 0) == CELL_OK); // Error code isn't checked on LLE
if (entries[back].state != 0xff)
{
ensure(sys_mutex_unlock(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE
ensure(lv2_syscall<sys_mutex_unlock>(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE
return true; // LLE returns the result of the comparison above
}
@ -554,13 +567,13 @@ public:
back = (back + 1) & 3;
size++;
ensure(sys_mutex_unlock(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE
ensure(lv2_syscall<sys_mutex_unlock>(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE
return CELL_OK;
}
const entry* pop(ppu_thread& ppu)
{
ensure(sys_mutex_lock(ppu, mutex, 0) == CELL_OK); // Error code isn't checked on LLE
ensure(lv2_syscall<sys_mutex_lock>(ppu, mutex, 0) == CELL_OK); // Error code isn't checked on LLE
if (ppu.state & cpu_flag::again) // Savestate was created while waiting on the mutex
{
@ -569,7 +582,7 @@ public:
if (entries[front].state == 0xff)
{
ensure(sys_mutex_unlock(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE
ensure(lv2_syscall<sys_mutex_unlock>(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE
return nullptr;
}
@ -581,15 +594,15 @@ public:
front = (front + 1) & 3;
size--;
ensure(sys_mutex_unlock(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE
ensure(lv2_syscall<sys_mutex_unlock>(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE
return ret;
}
const entry& peek(ppu_thread& ppu) const
{
ensure(sys_mutex_lock(ppu, mutex, 0) == CELL_OK); // Error code isn't checked on LLE
ensure(lv2_syscall<sys_mutex_lock>(ppu, mutex, 0) == CELL_OK); // Error code isn't checked on LLE
const entry& ret = entries[front];
ensure(sys_mutex_unlock(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE
ensure(lv2_syscall<sys_mutex_unlock>(ppu, mutex) == CELL_OK); // Error code isn't checked on LLE
return ret;
}
};
@ -708,39 +721,39 @@ public:
const vm::var<sys_mutex_attribute_t> mutex_attr{{ SYS_SYNC_PRIORITY, SYS_SYNC_NOT_RECURSIVE, SYS_SYNC_NOT_PROCESS_SHARED, SYS_SYNC_NOT_ADAPTIVE, 0, 0, 0, { "_adem01"_u64 } }};
const vm::var<sys_cond_attribute_t> cond_attr{{ SYS_SYNC_NOT_PROCESS_SHARED, 0, 0, { "_adec01"_u64 } }};
if (error_code ret = sys_mutex_create(ppu, _this.ptr(&LpcmDecSemaphore::mutex), mutex_attr); ret != CELL_OK)
if (error_code ret = lv2_syscall<sys_mutex_create>(ppu, _this.ptr(&LpcmDecSemaphore::mutex), mutex_attr); ret != CELL_OK)
{
return ret;
}
return sys_cond_create(ppu, _this.ptr(&LpcmDecSemaphore::cond), mutex, cond_attr);
return lv2_syscall<sys_cond_create>(ppu, _this.ptr(&LpcmDecSemaphore::cond), mutex, cond_attr);
}
error_code finalize(ppu_thread& ppu) const
{
if (error_code ret = sys_cond_destroy(ppu, cond); ret != CELL_OK)
if (error_code ret = lv2_syscall<sys_cond_destroy>(ppu, cond); ret != CELL_OK)
{
return ret;
}
return sys_mutex_destroy(ppu, mutex);
return lv2_syscall<sys_mutex_destroy>(ppu, mutex);
}
error_code release(ppu_thread& ppu)
{
if (error_code ret = sys_mutex_lock(ppu, mutex, 0); ret != CELL_OK)
if (error_code ret = lv2_syscall<sys_mutex_lock>(ppu, mutex, 0); ret != CELL_OK)
{
return ret;
}
value++;
if (error_code ret = sys_cond_signal(ppu, cond); ret != CELL_OK)
if (error_code ret = lv2_syscall<sys_cond_signal>(ppu, cond); ret != CELL_OK)
{
return ret; // LLE doesn't unlock the mutex
}
return sys_mutex_unlock(ppu, mutex);
return lv2_syscall<sys_mutex_unlock>(ppu, mutex);
}
error_code acquire(ppu_thread& ppu, lpcm_dec_state& savestate)
@ -752,7 +765,7 @@ public:
savestate = lpcm_dec_state::waiting_for_cmd_mutex_lock;
if (error_code ret = sys_mutex_lock(ppu, mutex, 0); ret != CELL_OK)
if (error_code ret = lv2_syscall<sys_mutex_lock>(ppu, mutex, 0); ret != CELL_OK)
{
return ret;
}
@ -767,7 +780,7 @@ public:
savestate = lpcm_dec_state::waiting_for_cmd_cond_wait;
cond_wait:
if (error_code ret = sys_cond_wait(ppu, cond, 0); ret != CELL_OK)
if (error_code ret = lv2_syscall<sys_cond_wait>(ppu, cond, 0); ret != CELL_OK)
{
return ret; // LLE doesn't unlock the mutex
}
@ -780,7 +793,7 @@ public:
value--;
return sys_mutex_unlock(ppu, mutex);
return lv2_syscall<sys_mutex_unlock>(ppu, mutex);
}
};

View File

@ -295,7 +295,7 @@ void AtracXdecContext::exec(ppu_thread& ppu)
{
savestate = atracxdec_state::initial;
ensure(sys_mutex_lock(ppu, queue_mutex, 0) == CELL_OK);
ensure(lv2_syscall<sys_mutex_lock>(ppu, queue_mutex, 0) == CELL_OK);
if (ppu.state & cpu_flag::again)
{
@ -310,24 +310,24 @@ void AtracXdecContext::exec(ppu_thread& ppu)
savestate = atracxdec_state::waiting_for_cmd;
label1_wait_for_cmd_state:
ensure(sys_cond_wait(ppu, queue_not_empty, 0) == CELL_OK);
ensure(lv2_syscall<sys_cond_wait>(ppu, queue_not_empty, 0) == CELL_OK);
if (ppu.state & cpu_flag::again)
{
return;
}
ensure(sys_mutex_unlock(ppu, queue_mutex) == CELL_OK);
ensure(lv2_syscall<sys_mutex_unlock>(ppu, queue_mutex) == CELL_OK);
}
cmd_queue.pop(cmd);
ensure(sys_mutex_unlock(ppu, queue_mutex) == CELL_OK);
ensure(lv2_syscall<sys_mutex_unlock>(ppu, queue_mutex) == CELL_OK);
savestate = atracxdec_state::checking_run_thread_1;
label2_check_run_thread_1_state:
ensure(sys_mutex_lock(ppu, run_thread_mutex, 0) == CELL_OK);
ensure(lv2_syscall<sys_mutex_lock>(ppu, run_thread_mutex, 0) == CELL_OK);
if (ppu.state & cpu_flag::again)
{
@ -336,11 +336,11 @@ void AtracXdecContext::exec(ppu_thread& ppu)
if (!run_thread)
{
ensure(sys_mutex_unlock(ppu, run_thread_mutex) == CELL_OK);
ensure(lv2_syscall<sys_mutex_unlock>(ppu, run_thread_mutex) == CELL_OK);
return;
}
ensure(sys_mutex_unlock(ppu, run_thread_mutex) == CELL_OK);
ensure(lv2_syscall<sys_mutex_unlock>(ppu, run_thread_mutex) == CELL_OK);
savestate = atracxdec_state::executing_cmd;
label3_execute_cmd_state:
@ -392,7 +392,7 @@ void AtracXdecContext::exec(ppu_thread& ppu)
cellAtracXdec.trace("Waiting for output to be consumed...");
ensure(sys_mutex_lock(ppu, output_mutex, 0) == CELL_OK);
ensure(lv2_syscall<sys_mutex_lock>(ppu, output_mutex, 0) == CELL_OK);
if (ppu.state & cpu_flag::again)
{
@ -404,7 +404,7 @@ void AtracXdecContext::exec(ppu_thread& ppu)
savestate = atracxdec_state::waiting_for_output;
label4_wait_for_output_state:
ensure(sys_cond_wait(ppu, output_consumed, 0) == CELL_OK);
ensure(lv2_syscall<sys_cond_wait>(ppu, output_consumed, 0) == CELL_OK);
if (ppu.state & cpu_flag::again)
{
@ -417,7 +417,7 @@ void AtracXdecContext::exec(ppu_thread& ppu)
savestate = atracxdec_state::checking_run_thread_2;
label5_check_run_thread_2_state:
ensure(sys_mutex_lock(ppu, run_thread_mutex, 0) == CELL_OK);
ensure(lv2_syscall<sys_mutex_lock>(ppu, run_thread_mutex, 0) == CELL_OK);
if (ppu.state & cpu_flag::again)
{
@ -426,12 +426,12 @@ void AtracXdecContext::exec(ppu_thread& ppu)
if (!run_thread)
{
ensure(sys_mutex_unlock(ppu, run_thread_mutex) == CELL_OK);
ensure(sys_mutex_unlock(ppu, output_mutex) == CELL_OK);
ensure(lv2_syscall<sys_mutex_unlock>(ppu, run_thread_mutex) == CELL_OK);
ensure(lv2_syscall<sys_mutex_unlock>(ppu, output_mutex) == CELL_OK);
return;
}
ensure(sys_mutex_unlock(ppu, run_thread_mutex) == CELL_OK);
ensure(lv2_syscall<sys_mutex_unlock>(ppu, run_thread_mutex) == CELL_OK);
savestate = atracxdec_state::decoding;
label6_decode_state:
@ -645,7 +645,7 @@ void AtracXdecContext::exec(ppu_thread& ppu)
notify_au_done.cbFunc(ppu, cmd.pcm_handle, notify_au_done.cbArg);
output_locked = true;
ensure(sys_mutex_unlock(ppu, output_mutex) == CELL_OK);
ensure(lv2_syscall<sys_mutex_unlock>(ppu, output_mutex) == CELL_OK);
const u32 output_size = decoded_samples_num * (decoder.bw_pcm & 0x7fu) * decoder.nch_out;
@ -680,7 +680,7 @@ error_code AtracXdecContext::send_command(ppu_thread& ppu, auto&&... args)
if (!signal)
{
ensure(sys_mutex_lock(ppu, queue_mutex, 0) == CELL_OK);
ensure(lv2_syscall<sys_mutex_lock>(ppu, queue_mutex, 0) == CELL_OK);
if (ppu.state & cpu_flag::again)
{
@ -692,23 +692,23 @@ error_code AtracXdecContext::send_command(ppu_thread& ppu, auto&&... args)
// Close command is only sent if the queue is empty on LLE
if (!cmd_queue.empty())
{
ensure(sys_mutex_unlock(ppu, queue_mutex) == CELL_OK);
ensure(lv2_syscall<sys_mutex_unlock>(ppu, queue_mutex) == CELL_OK);
return {};
}
}
if (cmd_queue.full())
{
ensure(sys_mutex_unlock(ppu, queue_mutex) == CELL_OK);
ensure(lv2_syscall<sys_mutex_unlock>(ppu, queue_mutex) == CELL_OK);
return CELL_ADEC_ERROR_ATX_BUSY;
}
cmd_queue.emplace(std::forward<AtracXdecCmdType>(type), std::forward<decltype(args)>(args)...);
ensure(sys_mutex_unlock(ppu, queue_mutex) == CELL_OK);
ensure(lv2_syscall<sys_mutex_unlock>(ppu, queue_mutex) == CELL_OK);
}
ensure(sys_cond_signal(ppu, queue_not_empty) == CELL_OK);
ensure(lv2_syscall<sys_cond_signal>(ppu, queue_not_empty) == CELL_OK);
if (ppu.state & cpu_flag::again)
{
@ -779,25 +779,25 @@ error_code _CellAdecCoreOpOpenExt_atracx(ppu_thread& ppu, vm::ptr<AtracXdecConte
const vm::var<sys_mutex_attribute_t> mutex_attr{{ SYS_SYNC_PRIORITY, SYS_SYNC_NOT_RECURSIVE, SYS_SYNC_NOT_PROCESS_SHARED, SYS_SYNC_NOT_ADAPTIVE, 0, 0, 0, { "_atd001"_u64 } }};
const vm::var<sys_cond_attribute_t> cond_attr{{ SYS_SYNC_NOT_PROCESS_SHARED, 0, 0, { "_atd002"_u64 } }};
ensure(sys_mutex_create(ppu, handle.ptr(&AtracXdecContext::queue_mutex), mutex_attr) == CELL_OK);
ensure(sys_cond_create(ppu, handle.ptr(&AtracXdecContext::queue_not_empty), handle->queue_mutex, cond_attr) == CELL_OK);
ensure(lv2_syscall<sys_mutex_create>(ppu, handle.ptr(&AtracXdecContext::queue_mutex), mutex_attr) == CELL_OK);
ensure(lv2_syscall<sys_cond_create>(ppu, handle.ptr(&AtracXdecContext::queue_not_empty), handle->queue_mutex, cond_attr) == CELL_OK);
mutex_attr->name_u64 = "_atd003"_u64;
cond_attr->name_u64 = "_atd004"_u64;
ensure(sys_mutex_create(ppu, handle.ptr(&AtracXdecContext::run_thread_mutex), mutex_attr) == CELL_OK);
ensure(sys_cond_create(ppu, handle.ptr(&AtracXdecContext::run_thread_cond), handle->run_thread_mutex, cond_attr) == CELL_OK);
ensure(lv2_syscall<sys_mutex_create>(ppu, handle.ptr(&AtracXdecContext::run_thread_mutex), mutex_attr) == CELL_OK);
ensure(lv2_syscall<sys_cond_create>(ppu, handle.ptr(&AtracXdecContext::run_thread_cond), handle->run_thread_mutex, cond_attr) == CELL_OK);
mutex_attr->name_u64 = "_atd005"_u64;
cond_attr->name_u64 = "_atd006"_u64;
ensure(sys_mutex_create(ppu, handle.ptr(&AtracXdecContext::output_mutex), mutex_attr) == CELL_OK);
ensure(sys_cond_create(ppu, handle.ptr(&AtracXdecContext::output_consumed), handle->output_mutex, cond_attr) == CELL_OK);
ensure(lv2_syscall<sys_mutex_create>(ppu, handle.ptr(&AtracXdecContext::output_mutex), mutex_attr) == CELL_OK);
ensure(lv2_syscall<sys_cond_create>(ppu, handle.ptr(&AtracXdecContext::output_consumed), handle->output_mutex, cond_attr) == CELL_OK);
ensure(sys_mutex_lock(ppu, handle->output_mutex, 0) == CELL_OK);
ensure(lv2_syscall<sys_mutex_lock>(ppu, handle->output_mutex, 0) == CELL_OK);
handle->output_locked = false;
ensure(sys_cond_signal(ppu, handle->output_consumed) == CELL_OK);
ensure(sys_mutex_unlock(ppu, handle->output_mutex) == CELL_OK);
ensure(lv2_syscall<sys_cond_signal>(ppu, handle->output_consumed) == CELL_OK);
ensure(lv2_syscall<sys_mutex_unlock>(ppu, handle->output_mutex) == CELL_OK);
const vm::var<char[]> _name = vm::make_str("HLE ATRAC3plus decoder");
const auto entry = g_fxo->get<ppu_function_manager>().func_addr(FIND_FUNC(atracXdecEntry));
@ -829,26 +829,26 @@ error_code _CellAdecCoreOpClose_atracx(ppu_thread& ppu, vm::ptr<AtracXdecContext
ensure(!!handle); // Not checked on LLE
ensure(sys_mutex_lock(ppu, handle->run_thread_mutex, 0) == CELL_OK);
ensure(lv2_syscall<sys_mutex_lock>(ppu, handle->run_thread_mutex, 0) == CELL_OK);
handle->run_thread = false;
ensure(sys_mutex_unlock(ppu, handle->run_thread_mutex) == CELL_OK);
ensure(lv2_syscall<sys_mutex_unlock>(ppu, handle->run_thread_mutex) == CELL_OK);
handle->send_command<AtracXdecCmdType::close>(ppu);
ensure(sys_mutex_lock(ppu, handle->output_mutex, 0) == CELL_OK);
ensure(lv2_syscall<sys_mutex_lock>(ppu, handle->output_mutex, 0) == CELL_OK);
handle->output_locked = false;
ensure(sys_mutex_unlock(ppu, handle->output_mutex) == CELL_OK);
ensure(sys_cond_signal(ppu, handle->output_consumed) == CELL_OK);
ensure(lv2_syscall<sys_mutex_unlock>(ppu, handle->output_mutex) == CELL_OK);
ensure(lv2_syscall<sys_cond_signal>(ppu, handle->output_consumed) == CELL_OK);
vm::var<u64> thread_ret;
ensure(sys_ppu_thread_join(ppu, static_cast<u32>(handle->thread_id), +thread_ret) == CELL_OK);
ensure(lv2_syscall<sys_ppu_thread_join>(ppu, static_cast<u32>(handle->thread_id), +thread_ret) == CELL_OK);
error_code ret = sys_cond_destroy(ppu, handle->queue_not_empty);
ret = ret ? ret : sys_cond_destroy(ppu, handle->run_thread_cond);
ret = ret ? ret : sys_cond_destroy(ppu, handle->output_consumed);
ret = ret ? ret : sys_mutex_destroy(ppu, handle->queue_mutex);
ret = ret ? ret : sys_mutex_destroy(ppu, handle->run_thread_mutex);
ret = ret ? ret : sys_mutex_destroy(ppu, handle->output_mutex);
error_code ret = lv2_syscall<sys_cond_destroy>(ppu, handle->queue_not_empty);
ret = ret ? ret : lv2_syscall<sys_cond_destroy>(ppu, handle->run_thread_cond);
ret = ret ? ret : lv2_syscall<sys_cond_destroy>(ppu, handle->output_consumed);
ret = ret ? ret : lv2_syscall<sys_mutex_destroy>(ppu, handle->queue_mutex);
ret = ret ? ret : lv2_syscall<sys_mutex_destroy>(ppu, handle->run_thread_mutex);
ret = ret ? ret : lv2_syscall<sys_mutex_destroy>(ppu, handle->output_mutex);
return ret != CELL_OK ? static_cast<error_code>(CELL_ADEC_ERROR_FATAL) : CELL_OK;
}
@ -921,7 +921,7 @@ error_code _CellAdecCoreOpReleasePcm_atracx(ppu_thread& ppu, vm::ptr<AtracXdecCo
if (!signal)
{
ensure(sys_mutex_lock(ppu, handle->output_mutex, 0) == CELL_OK);
ensure(lv2_syscall<sys_mutex_lock>(ppu, handle->output_mutex, 0) == CELL_OK);
if (ppu.state & cpu_flag::again)
{
@ -931,7 +931,7 @@ error_code _CellAdecCoreOpReleasePcm_atracx(ppu_thread& ppu, vm::ptr<AtracXdecCo
handle->output_locked = false;
}
ensure(sys_cond_signal(ppu, handle->output_consumed) == CELL_OK);
ensure(lv2_syscall<sys_cond_signal>(ppu, handle->output_consumed) == CELL_OK);
if (ppu.state & cpu_flag::again)
{
@ -939,7 +939,7 @@ error_code _CellAdecCoreOpReleasePcm_atracx(ppu_thread& ppu, vm::ptr<AtracXdecCo
return {};
}
ensure(sys_mutex_unlock(ppu, handle->output_mutex) == CELL_OK);
ensure(lv2_syscall<sys_mutex_unlock>(ppu, handle->output_mutex) == CELL_OK);
return CELL_OK;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,8 @@
#pragma once
#include "Emu/Memory/vm_ptr.h"
#include "cellPamf.h"
#include "Emu/Cell/ErrorCodes.h"
#include "Utilities/BitField.h"
// Error Codes
enum CellDmuxError :u32
@ -18,6 +19,10 @@ enum CellDmuxStreamType : s32
CELL_DMUX_STREAM_TYPE_UNDEF = 0,
CELL_DMUX_STREAM_TYPE_PAMF = 1,
CELL_DMUX_STREAM_TYPE_TERMINATOR = 2,
// Only used in cellSail
CELL_DMUX_STREAM_TYPE_MP4 = 0x81,
CELL_DMUX_STREAM_TYPE_AVI = 0x82
};
enum CellDmuxMsgType : s32
@ -48,13 +53,14 @@ struct CellDmuxEsMsg
struct CellDmuxType
{
be_t<s32> streamType; // CellDmuxStreamType
be_t<u32> reserved[2];
be_t<s32> reserved1;
be_t<s32> reserved2;
};
struct CellDmuxType2
{
be_t<s32> streamType; // CellDmuxStreamType
be_t<u32> streamSpecificInfo;
be_t<s32> streamType;
vm::bcptr<void> streamSpecificInfo;
};
struct CellDmuxResource
@ -73,8 +79,8 @@ struct CellDmuxResourceEx
be_t<u32> memSize;
be_t<u32> ppuThreadPriority;
be_t<u32> ppuThreadStackSize;
be_t<u32> spurs_addr;
u8 priority[8];
vm::bptr<void> spurs; // CellSpurs*
be_t<u64, 1> priority;
be_t<u32> maxContention;
};
@ -85,33 +91,23 @@ struct CellDmuxResourceSpurs
be_t<u32> maxContention;
};
/*
struct CellDmuxResource2Ex
{
b8 isResourceEx; //true
CellDmuxResourceEx resourceEx;
};
struct CellDmuxResource2NoEx
{
b8 isResourceEx; //false
CellDmuxResource resource;
};
*/
struct CellDmuxResource2
{
b8 isResourceEx;
be_t<u32> memAddr;
be_t<u32> memSize;
be_t<u32> ppuThreadPriority;
be_t<u32> ppuThreadStackSize;
be_t<u32> shit[4];
union
{
CellDmuxResource resource;
CellDmuxResourceEx resourceEx;
};
};
using CellDmuxCbMsg = u32(u32 demuxerHandle, vm::cptr<CellDmuxMsg> demuxerMsg, vm::ptr<void> cbArg);
struct DmuxContext;
struct DmuxEsContext;
using CellDmuxCbEsMsg = u32(u32 demuxerHandle, u32 esHandle, vm::cptr<CellDmuxEsMsg> esMsg, vm::ptr<void> cbArg);
using CellDmuxCbMsg = u32(vm::ptr<DmuxContext> demuxerHandle, vm::cptr<CellDmuxMsg> demuxerMsg, vm::ptr<void> cbArg);
using CellDmuxCbEsMsg = u32(vm::ptr<DmuxContext> demuxerHandle, vm::ptr<DmuxEsContext> esHandle, vm::cptr<CellDmuxEsMsg> esMsg, vm::ptr<void> cbArg);
// Used for internal callbacks as well
template <typename F>
@ -177,6 +173,70 @@ struct DmuxAuInfo
be_t<u32> specific_info_size;
};
struct DmuxAuQueueElement
{
be_t<u32> index;
u8 unk; // unused
DmuxAuInfo au_info;
};
CHECK_SIZE(DmuxAuQueueElement, 0x38);
enum DmuxState : u32
{
DMUX_STOPPED = 1 << 0,
DMUX_RUNNING = 1 << 1,
};
struct alignas(0x10) DmuxContext // CellDmuxHandle = DmuxContext*
{
vm::bptr<DmuxContext> _this;
be_t<u32> _this_size;
be_t<u32> version;
be_t<u32> dmux_state;
CellDmuxType dmux_type;
CellDmuxCb dmux_cb;
b8 stream_is_set;
vm::bptr<void> core_handle;
be_t<u32> version_; // Same value as 'version'
be_t<u64> user_data;
be_t<s32> max_enabled_es_num;
be_t<s32> enabled_es_num;
be_t<u32> _dx_mhd; // sys_mutex_t
u8 reserved[0x7c];
};
CHECK_SIZE_ALIGN(DmuxContext, 0xc0, 0x10);
struct alignas(0x10) DmuxEsContext // CellDmuxEsHandle = DmuxEsContext*
{
be_t<u32> _dx_mes; // sys_mutex_t
be_t<u32> is_enabled;
be_t<u32> error_mem_size;
be_t<u32> error_count;
vm::bptr<void> error_mem_addr;
vm::bptr<DmuxEsContext> _this;
be_t<u32> _this_size;
be_t<s32> _this_index;
vm::bptr<DmuxContext> dmux_handle;
CellDmuxEsCb es_cb;
vm::bptr<void> core_es_handle;
bf_t<be_t<u32>, 0, 1> flush_started;
struct
{
be_t<s32> max_size;
be_t<s32> allocated_size;
be_t<s32> size;
be_t<s32> front;
be_t<s32> back;
be_t<s32> allocated_back;
}
au_queue;
};
CHECK_SIZE_ALIGN(DmuxEsContext, 0x50, 0x10);
using DmuxNotifyDemuxDone = error_code(vm::ptr<void>, u32, vm::ptr<void>);
using DmuxNotifyFatalErr = error_code(vm::ptr<void>, u32, vm::ptr<void>);
using DmuxNotifyProgEndCode = error_code(vm::ptr<void>, vm::ptr<void>);
@ -194,10 +254,10 @@ using CellDmuxCoreOpSetStream = error_code(vm::ptr<void>, vm::cptr<void>, u32, b
using CellDmuxCoreOpReleaseAu = error_code(vm::ptr<void>, vm::ptr<void>, u32);
using CellDmuxCoreOpQueryEsAttr = error_code(vm::cptr<void>, vm::cptr<void>, vm::ptr<CellDmuxPamfEsAttr>);
using CellDmuxCoreOpEnableEs = error_code(vm::ptr<void>, vm::cptr<void>, vm::cptr<CellDmuxEsResource>, vm::cptr<DmuxCb<DmuxEsNotifyAuFound>>, vm::cptr<DmuxCb<DmuxEsNotifyFlushDone>>, vm::cptr<void>, vm::pptr<void>);
using CellDmuxCoreOpDisableEs = u32(vm::ptr<void>);
using CellDmuxCoreOpFlushEs = u32(vm::ptr<void>);
using CellDmuxCoreOpResetEs = u32(vm::ptr<void>);
using CellDmuxCoreOpResetStreamAndWaitDone = u32(vm::ptr<void>);
using CellDmuxCoreOpDisableEs = error_code(vm::ptr<void>);
using CellDmuxCoreOpFlushEs = error_code(vm::ptr<void>);
using CellDmuxCoreOpResetEs = error_code(vm::ptr<void>);
using CellDmuxCoreOpResetStreamAndWaitDone = error_code(vm::ptr<void>);
struct CellDmuxCoreOps
{

View File

@ -10,6 +10,7 @@
#include "cellDmuxPamf.h"
#include <ranges>
#include <climits>
vm::gvar<CellDmuxCoreOps> g_cell_dmux_core_ops_pamf;
vm::gvar<CellDmuxCoreOps> g_cell_dmux_core_ops_raw_es;
@ -998,6 +999,10 @@ void dmux_pamf_spu_context::operator()() // cellSpursMain()
ensure(stream_info_queue->pop(stream_info));
set_stream({ stream_info.stream_addr.get_ptr(), stream_info.stream_size }, stream_info.continuity);
// Delay demuxing a bit
// Prevents White Knight Chronicles II FMVs from freezing, since events are otherwise fired before the game has finished initializing FMV playback
thread_ctrl::wait_for(1'500);
}
process_next_pack();
@ -1153,6 +1158,19 @@ void dmux_pamf_spu_context::save(utils::serial& ar)
// PPU thread
template <auto Syscall>
static auto lv2_syscall(ppu_thread& ppu, auto&&... args)
{
const auto ret = Syscall(ppu, std::forward<decltype(args)>(args)...);
if (ppu.test_stopped())
{
ppu.state += cpu_flag::again;
}
return ret;
}
template <DmuxPamfCommandType type>
void DmuxPamfContext::send_spu_command_and_wait(ppu_thread& ppu, bool waiting_for_spu_state, auto&&... cmd_params)
{
@ -1194,7 +1212,7 @@ error_code DmuxPamfContext::wait_au_released_or_stream_reset(ppu_thread& ppu, u6
goto label1_waiting_for_au_released_state;
}
if (sys_mutex_lock(ppu, mutex, 0) != CELL_OK)
if (lv2_syscall<sys_mutex_lock>(ppu, mutex, 0) != CELL_OK)
{
return CELL_DMUX_PAMF_ERROR_FATAL;
}
@ -1213,9 +1231,9 @@ error_code DmuxPamfContext::wait_au_released_or_stream_reset(ppu_thread& ppu, u6
savestate = dmux_pamf_state::waiting_for_au_released;
label1_waiting_for_au_released_state:
if (sys_cond_wait(ppu, cond, 0) != CELL_OK)
if (lv2_syscall<sys_cond_wait>(ppu, cond, 0) != CELL_OK)
{
sys_mutex_unlock(ppu, mutex);
lv2_syscall<sys_mutex_unlock>(ppu, mutex);
return CELL_DMUX_PAMF_ERROR_FATAL;
}
@ -1233,13 +1251,13 @@ error_code DmuxPamfContext::wait_au_released_or_stream_reset(ppu_thread& ppu, u6
au_released_bitset = 0;
return sys_mutex_unlock(ppu, mutex) != CELL_OK ? static_cast<error_code>(CELL_DMUX_PAMF_ERROR_FATAL) : CELL_OK;
return lv2_syscall<sys_mutex_unlock>(ppu, mutex) != CELL_OK ? static_cast<error_code>(CELL_DMUX_PAMF_ERROR_FATAL) : CELL_OK;
}
template <bool reset>
error_code DmuxPamfContext::set_au_reset(ppu_thread& ppu)
{
if (sys_mutex_lock(ppu, mutex, 0) != CELL_OK)
if (lv2_syscall<sys_mutex_lock>(ppu, mutex, 0) != CELL_OK)
{
return CELL_DMUX_PAMF_ERROR_FATAL;
}
@ -1251,7 +1269,7 @@ error_code DmuxPamfContext::set_au_reset(ppu_thread& ppu)
std::ranges::for_each(elementary_streams | std::views::filter([](auto es){ return !!es; }), [](auto& reset_next_au) { reset_next_au = reset; }, &DmuxPamfElementaryStream::reset_next_au);
return sys_mutex_unlock(ppu, mutex) == CELL_OK ? static_cast<error_code>(CELL_OK) : CELL_DMUX_PAMF_ERROR_FATAL;
return lv2_syscall<sys_mutex_unlock>(ppu, mutex) == CELL_OK ? static_cast<error_code>(CELL_OK) : CELL_DMUX_PAMF_ERROR_FATAL;
}
template <typename F>
@ -1357,7 +1375,7 @@ void DmuxPamfContext::exec(ppu_thread& ppu)
savestate = dmux_pamf_state::starting_demux_done;
label4_starting_demux_done_state:
if (sys_mutex_lock(ppu, mutex, 0) != CELL_OK)
if (lv2_syscall<sys_mutex_lock>(ppu, mutex, 0) != CELL_OK)
{
savestate = dmux_pamf_state::starting_demux_done_mutex_lock_error;
label5_starting_demux_done_mutex_lock_error_state:
@ -1369,7 +1387,7 @@ void DmuxPamfContext::exec(ppu_thread& ppu)
sequence_state = DmuxPamfSequenceState::resetting;
if (sys_mutex_unlock(ppu, mutex) != CELL_OK)
if (lv2_syscall<sys_mutex_unlock>(ppu, mutex) != CELL_OK)
{
savestate = dmux_pamf_state::starting_demux_done_mutex_unlock_error;
label6_starting_demux_done_mutex_unlock_error_state:
@ -1422,7 +1440,7 @@ void DmuxPamfContext::exec(ppu_thread& ppu)
{
case DmuxPamfEventType::au_found:
{
if (sys_mutex_lock(ppu, mutex, 0) != CELL_OK)
if (lv2_syscall<sys_mutex_lock>(ppu, mutex, 0) != CELL_OK)
{
savestate = dmux_pamf_state::sending_fatal_err;
continue;
@ -1437,7 +1455,7 @@ void DmuxPamfContext::exec(ppu_thread& ppu)
// If the elementary stream of the found access unit is not enabled, don't do anything
if (!es || es->_this.get_ptr() != es || es->es_id != event.au_found.user_data)
{
if (sys_mutex_unlock(ppu, mutex) != CELL_OK)
if (lv2_syscall<sys_mutex_unlock>(ppu, mutex) != CELL_OK)
{
savestate = dmux_pamf_state::sending_fatal_err;
continue;
@ -1465,7 +1483,7 @@ void DmuxPamfContext::exec(ppu_thread& ppu)
es->reset_next_au = false;
}
if (sys_mutex_unlock(ppu, mutex) != CELL_OK)
if (lv2_syscall<sys_mutex_unlock>(ppu, mutex) != CELL_OK)
{
savestate = dmux_pamf_state::sending_fatal_err;
continue;
@ -1491,7 +1509,7 @@ void DmuxPamfContext::exec(ppu_thread& ppu)
es->au_specific_info[2] = read_from_ptr<bf_t<u8, 6, 2>>(event.au_found.stream_header_buf, 1);
}
if (sys_mutex_unlock(ppu, mutex) != CELL_OK)
if (lv2_syscall<sys_mutex_unlock>(ppu, mutex) != CELL_OK)
{
savestate = dmux_pamf_state::sending_fatal_err;
continue;
@ -1536,7 +1554,7 @@ void DmuxPamfContext::exec(ppu_thread& ppu)
savestate = dmux_pamf_state::demux_done_mutex_lock;
label15_demux_done_mutex_lock_state:
if (sys_mutex_lock(ppu, mutex, 0) != CELL_OK)
if (lv2_syscall<sys_mutex_lock>(ppu, mutex, 0) != CELL_OK)
{
savestate = dmux_pamf_state::sending_fatal_err;
continue;
@ -1551,9 +1569,9 @@ void DmuxPamfContext::exec(ppu_thread& ppu)
savestate = dmux_pamf_state::demux_done_cond_signal;
label16_demux_done_cond_signal_state:
if (sys_cond_signal_all(ppu, cond) != CELL_OK)
if (lv2_syscall<sys_cond_signal_all>(ppu, cond) != CELL_OK)
{
sys_mutex_unlock(ppu, mutex);
lv2_syscall<sys_mutex_unlock>(ppu, mutex);
savestate = dmux_pamf_state::sending_fatal_err;
continue;
@ -1562,7 +1580,7 @@ void DmuxPamfContext::exec(ppu_thread& ppu)
RETURN_ON_CPU_FLAG_AGAIN();
}
if (sys_mutex_unlock(ppu, mutex) != CELL_OK)
if (lv2_syscall<sys_mutex_unlock>(ppu, mutex) != CELL_OK)
{
savestate = dmux_pamf_state::sending_fatal_err;
continue;
@ -1577,7 +1595,7 @@ void DmuxPamfContext::exec(ppu_thread& ppu)
}
case DmuxPamfEventType::flush_done:
{
if (sys_mutex_lock(ppu, mutex, 0) != CELL_OK)
if (lv2_syscall<sys_mutex_lock>(ppu, mutex, 0) != CELL_OK)
{
savestate = dmux_pamf_state::sending_fatal_err;
continue;
@ -1588,7 +1606,7 @@ void DmuxPamfContext::exec(ppu_thread& ppu)
DmuxPamfElementaryStream* const es = find_es(event.flush_done.stream_id, event.flush_done.private_stream_id);
const bool valid = es && es->_this.get_ptr() == es && es->es_id == event.flush_done.user_data;
if (sys_mutex_unlock(ppu, mutex) != CELL_OK)
if (lv2_syscall<sys_mutex_unlock>(ppu, mutex) != CELL_OK)
{
savestate = dmux_pamf_state::sending_fatal_err;
continue;
@ -1631,7 +1649,7 @@ void DmuxPamfContext::exec(ppu_thread& ppu)
savestate = dmux_pamf_state::resuming_demux_mutex_lock;
label17_resuming_demux_mutex_lock_state:
if (sys_mutex_lock(ppu, mutex, 0) != CELL_OK)
if (lv2_syscall<sys_mutex_lock>(ppu, mutex, 0) != CELL_OK)
{
savestate = dmux_pamf_state::sending_fatal_err;
continue;
@ -1658,7 +1676,7 @@ void DmuxPamfContext::exec(ppu_thread& ppu)
ensure(cmd_result_queue.pop());
}
if (sys_mutex_unlock(ppu, mutex) != CELL_OK)
if (lv2_syscall<sys_mutex_unlock>(ppu, mutex) != CELL_OK)
{
savestate = dmux_pamf_state::sending_fatal_err;
continue;
@ -1964,8 +1982,8 @@ error_code DmuxPamfContext::open(ppu_thread& ppu, const CellDmuxPamfResource& re
const vm::var<sys_mutex_attribute_t> mutex_attr = {{ SYS_SYNC_PRIORITY, SYS_SYNC_NOT_RECURSIVE, SYS_SYNC_NOT_PROCESS_SHARED, SYS_SYNC_NOT_ADAPTIVE, 0, 0, 0, { "_dxpmtx"_u64 } }};
const vm::var<sys_cond_attribute_t> cond_attr = {{ SYS_SYNC_NOT_PROCESS_SHARED, 0, 0, { "_dxpcnd"_u64 } }};
if (sys_mutex_create(ppu, _this.ptr(&DmuxPamfContext::mutex), mutex_attr) != CELL_OK
|| sys_cond_create(ppu, _this.ptr(&DmuxPamfContext::cond), _this->mutex, cond_attr) != CELL_OK)
if (lv2_syscall<sys_mutex_create>(ppu, _this.ptr(&DmuxPamfContext::mutex), mutex_attr) != CELL_OK
|| lv2_syscall<sys_cond_create>(ppu, _this.ptr(&DmuxPamfContext::cond), _this->mutex, cond_attr) != CELL_OK)
{
return CELL_DMUX_PAMF_ERROR_FATAL;
}
@ -2078,8 +2096,8 @@ error_code DmuxPamfContext::close(ppu_thread& ppu)
g_fxo->get<lv2_memory_container>().free(0x40000);
}
if (sys_cond_destroy(ppu, cond) != CELL_OK
|| sys_mutex_destroy(ppu, mutex) != CELL_OK)
if (lv2_syscall<sys_cond_destroy>(ppu, cond) != CELL_OK
|| lv2_syscall<sys_mutex_destroy>(ppu, mutex) != CELL_OK)
{
return CELL_DMUX_PAMF_ERROR_FATAL;
}
@ -2117,7 +2135,7 @@ error_code DmuxPamfContext::reset_stream(ppu_thread& ppu)
switch (savestate)
{
case 0:
if (sys_mutex_lock(ppu, mutex, 0) != CELL_OK)
if (lv2_syscall<sys_mutex_lock>(ppu, mutex, 0) != CELL_OK)
{
return CELL_DMUX_PAMF_ERROR_FATAL;
}
@ -2130,7 +2148,7 @@ error_code DmuxPamfContext::reset_stream(ppu_thread& ppu)
if (sequence_state != DmuxPamfSequenceState::running)
{
return sys_mutex_unlock(ppu, mutex) == CELL_OK ? static_cast<error_code>(CELL_OK) : CELL_DMUX_PAMF_ERROR_FATAL;
return lv2_syscall<sys_mutex_unlock>(ppu, mutex) == CELL_OK ? static_cast<error_code>(CELL_OK) : CELL_DMUX_PAMF_ERROR_FATAL;
}
[[fallthrough]];
@ -2148,9 +2166,9 @@ error_code DmuxPamfContext::reset_stream(ppu_thread& ppu)
[[fallthrough]];
case 2:
if (const error_code ret = sys_cond_signal_to(ppu, cond, static_cast<u32>(thread_id)); ret != CELL_OK && ret != static_cast<s32>(CELL_EPERM))
if (const error_code ret = lv2_syscall<sys_cond_signal_to>(ppu, cond, static_cast<u32>(thread_id)); ret != CELL_OK && ret != static_cast<s32>(CELL_EPERM))
{
sys_mutex_unlock(ppu, mutex);
lv2_syscall<sys_mutex_unlock>(ppu, mutex);
return CELL_DMUX_PAMF_ERROR_FATAL;
}
@ -2160,7 +2178,7 @@ error_code DmuxPamfContext::reset_stream(ppu_thread& ppu)
return {};
}
return sys_mutex_unlock(ppu, mutex) == CELL_OK ? static_cast<error_code>(CELL_OK) : CELL_DMUX_PAMF_ERROR_FATAL;
return lv2_syscall<sys_mutex_unlock>(ppu, mutex) == CELL_OK ? static_cast<error_code>(CELL_OK) : CELL_DMUX_PAMF_ERROR_FATAL;
default:
fmt::throw_exception("Unexpected savestate value: 0x%x", savestate);
@ -2215,7 +2233,7 @@ error_code _CellDmuxCoreOpCreateThread(ppu_thread& ppu, vm::ptr<CellDmuxPamfHand
error_code DmuxPamfContext::join_thread(ppu_thread& ppu)
{
if (sys_mutex_lock(ppu, mutex, 0) != CELL_OK)
if (lv2_syscall<sys_mutex_lock>(ppu, mutex, 0) != CELL_OK)
{
return CELL_DMUX_PAMF_ERROR_FATAL;
}
@ -2226,12 +2244,12 @@ error_code DmuxPamfContext::join_thread(ppu_thread& ppu)
send_spu_command_and_wait<DmuxPamfCommandType::close>(ppu, false);
if (sys_mutex_unlock(ppu, mutex) != CELL_OK)
if (lv2_syscall<sys_mutex_unlock>(ppu, mutex) != CELL_OK)
{
return CELL_DMUX_PAMF_ERROR_FATAL;
}
return sys_ppu_thread_join(ppu, static_cast<u32>(thread_id), +vm::var<u64>{}) == CELL_OK ? static_cast<error_code>(CELL_OK) : CELL_DMUX_PAMF_ERROR_FATAL;
return lv2_syscall<sys_ppu_thread_join>(ppu, static_cast<u32>(thread_id), +vm::var<u64>{}) == CELL_OK ? static_cast<error_code>(CELL_OK) : CELL_DMUX_PAMF_ERROR_FATAL;
}
error_code _CellDmuxCoreOpJoinThread(ppu_thread& ppu, vm::ptr<CellDmuxPamfHandle> handle)
@ -2264,7 +2282,7 @@ error_code DmuxPamfContext::set_stream(ppu_thread& ppu, vm::cptr<u8> stream_addr
if (!waiting_for_spu_state)
{
if (sys_mutex_lock(ppu, mutex, 0) != CELL_OK)
if (lv2_syscall<sys_mutex_lock>(ppu, mutex, 0) != CELL_OK)
{
return CELL_DMUX_PAMF_ERROR_FATAL;
}
@ -2279,7 +2297,7 @@ error_code DmuxPamfContext::set_stream(ppu_thread& ppu, vm::cptr<u8> stream_addr
if (!stream_info_queue.emplace(stream_address, stream_size, user_data, !discontinuity, raw_es))
{
return sys_mutex_unlock(ppu, mutex) == CELL_OK ? CELL_DMUX_PAMF_ERROR_BUSY : CELL_DMUX_PAMF_ERROR_FATAL;
return lv2_syscall<sys_mutex_unlock>(ppu, mutex) == CELL_OK ? CELL_DMUX_PAMF_ERROR_BUSY : CELL_DMUX_PAMF_ERROR_FATAL;
}
}
@ -2293,7 +2311,7 @@ error_code DmuxPamfContext::set_stream(ppu_thread& ppu, vm::cptr<u8> stream_addr
sequence_state = DmuxPamfSequenceState::running;
return sys_mutex_unlock(ppu, mutex) == CELL_OK ? static_cast<error_code>(CELL_OK) : CELL_DMUX_PAMF_ERROR_FATAL;
return lv2_syscall<sys_mutex_unlock>(ppu, mutex) == CELL_OK ? static_cast<error_code>(CELL_OK) : CELL_DMUX_PAMF_ERROR_FATAL;
}
template <bool raw_es>
@ -2320,7 +2338,7 @@ error_code DmuxPamfElementaryStream::release_au(ppu_thread& ppu, vm::ptr<u8> au_
switch (savestate)
{
case 0:
if (sys_mutex_lock(ppu, demuxer->mutex, 0) != CELL_OK)
if (lv2_syscall<sys_mutex_lock>(ppu, demuxer->mutex, 0) != CELL_OK)
{
return CELL_DMUX_PAMF_ERROR_FATAL;
}
@ -2346,9 +2364,9 @@ error_code DmuxPamfElementaryStream::release_au(ppu_thread& ppu, vm::ptr<u8> au_
[[fallthrough]];
case 2:
if (const error_code ret = sys_cond_signal_to(ppu, demuxer->cond, static_cast<u32>(demuxer->thread_id)); ret != CELL_OK && ret != static_cast<s32>(CELL_EPERM))
if (const error_code ret = lv2_syscall<sys_cond_signal_to>(ppu, demuxer->cond, static_cast<u32>(demuxer->thread_id)); ret != CELL_OK && ret != static_cast<s32>(CELL_EPERM))
{
sys_mutex_unlock(ppu, demuxer->mutex);
lv2_syscall<sys_mutex_unlock>(ppu, demuxer->mutex);
return CELL_DMUX_PAMF_ERROR_FATAL;
}
@ -2358,7 +2376,7 @@ error_code DmuxPamfElementaryStream::release_au(ppu_thread& ppu, vm::ptr<u8> au_
return {};
}
return sys_mutex_unlock(ppu, demuxer->mutex) == CELL_OK ? static_cast<error_code>(CELL_OK) : CELL_DMUX_PAMF_ERROR_FATAL;
return lv2_syscall<sys_mutex_unlock>(ppu, demuxer->mutex) == CELL_OK ? static_cast<error_code>(CELL_OK) : CELL_DMUX_PAMF_ERROR_FATAL;
default:
fmt::throw_exception("Unexpected savestate value: 0x%x", savestate);
@ -2461,7 +2479,7 @@ error_code DmuxPamfContext::enable_es(ppu_thread& ppu, u16 stream_id, u16 privat
return CELL_DMUX_PAMF_ERROR_ARG;
}
if (const error_code ret = sys_mutex_lock(ppu, mutex, 0); ret != CELL_OK)
if (const error_code ret = lv2_syscall<sys_mutex_lock>(ppu, mutex, 0); ret != CELL_OK)
{
return CELL_DMUX_PAMF_ERROR_FATAL;
}
@ -2476,13 +2494,13 @@ error_code DmuxPamfContext::enable_es(ppu_thread& ppu, u16 stream_id, u16 privat
if (enabled_es_num == max_enabled_es_num)
{
return sys_mutex_unlock(ppu, mutex) == CELL_OK ? CELL_DMUX_PAMF_ERROR_NO_MEMORY : CELL_DMUX_PAMF_ERROR_FATAL;
return lv2_syscall<sys_mutex_unlock>(ppu, mutex) == CELL_OK ? CELL_DMUX_PAMF_ERROR_NO_MEMORY : CELL_DMUX_PAMF_ERROR_FATAL;
}
if (find_es(stream_id, private_stream_id))
{
// Elementary stream is already enabled
return sys_mutex_unlock(ppu, mutex) == CELL_OK ? CELL_DMUX_PAMF_ERROR_ARG : CELL_DMUX_PAMF_ERROR_FATAL;
return lv2_syscall<sys_mutex_unlock>(ppu, mutex) == CELL_OK ? CELL_DMUX_PAMF_ERROR_ARG : CELL_DMUX_PAMF_ERROR_FATAL;
}
}
@ -2560,7 +2578,7 @@ error_code DmuxPamfContext::enable_es(ppu_thread& ppu, u16 stream_id, u16 privat
enabled_es_num++;
if (sys_mutex_unlock(ppu, mutex) != CELL_OK)
if (lv2_syscall<sys_mutex_unlock>(ppu, mutex) != CELL_OK)
{
return CELL_DMUX_PAMF_ERROR_FATAL;
}
@ -2573,7 +2591,7 @@ template <bool raw_es>
error_code _CellDmuxCoreOpEnableEs(ppu_thread& ppu, vm::ptr<CellDmuxPamfHandle> handle, vm::cptr<void> esFilterId, vm::cptr<CellDmuxEsResource> esResource, vm::cptr<DmuxCb<DmuxEsNotifyAuFound>> notifyAuFound,
vm::cptr<DmuxCb<DmuxEsNotifyFlushDone>> notifyFlushDone, vm::cptr<void> esSpecificInfo, vm::pptr<CellDmuxPamfEsHandle> esHandle)
{
cellDmuxPamf.notice("_CellDmuxCoreOpEnableEs<raw_es=%d>(handle=*0x%x, esFilterId=*0x%x, esResource=*0x%x, notifyAuFound=*0x%x, notifyFlushDone=*0x%x, esSpecificInfo=*0x%x, esHandle)",
cellDmuxPamf.notice("_CellDmuxCoreOpEnableEs<raw_es=%d>(handle=*0x%x, esFilterId=*0x%x, esResource=*0x%x, notifyAuFound=*0x%x, notifyFlushDone=*0x%x, esSpecificInfo=*0x%x, esHandle=**0x%x)",
raw_es, handle, esFilterId, esResource, notifyAuFound, notifyFlushDone, esSpecificInfo, esHandle);
if (!handle || !esFilterId || !esResource || !esResource->memAddr || esResource->memSize == 0u || !notifyAuFound || !notifyAuFound->cbFunc || !notifyAuFound->cbArg || !notifyFlushDone || !notifyFlushDone->cbFunc || !notifyFlushDone->cbArg)
@ -2614,7 +2632,7 @@ error_code DmuxPamfElementaryStream::disable_es(ppu_thread& ppu)
switch (savestate)
{
case 0:
if (sys_mutex_lock(ppu, dmux->mutex, 0) != CELL_OK)
if (lv2_syscall<sys_mutex_lock>(ppu, dmux->mutex, 0) != CELL_OK)
{
return CELL_DMUX_PAMF_ERROR_FATAL;
}
@ -2628,7 +2646,7 @@ error_code DmuxPamfElementaryStream::disable_es(ppu_thread& ppu)
if (!dmux->find_es(stream_id, private_stream_id))
{
// Elementary stream is already disabled
return sys_mutex_unlock(ppu, dmux->mutex) == CELL_OK ? CELL_DMUX_PAMF_ERROR_ARG : CELL_DMUX_PAMF_ERROR_FATAL;
return lv2_syscall<sys_mutex_unlock>(ppu, dmux->mutex) == CELL_OK ? CELL_DMUX_PAMF_ERROR_ARG : CELL_DMUX_PAMF_ERROR_FATAL;
}
[[fallthrough]];
@ -2659,9 +2677,9 @@ error_code DmuxPamfElementaryStream::disable_es(ppu_thread& ppu)
[[fallthrough]];
case 2:
if (const error_code ret = sys_cond_signal_to(ppu, dmux->cond, static_cast<u32>(dmux->thread_id)); ret != CELL_OK && ret != static_cast<s32>(CELL_EPERM))
if (const error_code ret = lv2_syscall<sys_cond_signal_to>(ppu, dmux->cond, static_cast<u32>(dmux->thread_id)); ret != CELL_OK && ret != static_cast<s32>(CELL_EPERM))
{
sys_mutex_unlock(ppu, dmux->mutex);
lv2_syscall<sys_mutex_unlock>(ppu, dmux->mutex);
return CELL_DMUX_PAMF_ERROR_FATAL;
}
@ -2671,7 +2689,7 @@ error_code DmuxPamfElementaryStream::disable_es(ppu_thread& ppu)
return {};
}
return sys_mutex_unlock(ppu, dmux->mutex) == CELL_OK ? static_cast<error_code>(CELL_OK) : CELL_DMUX_PAMF_ERROR_FATAL;
return lv2_syscall<sys_mutex_unlock>(ppu, dmux->mutex) == CELL_OK ? static_cast<error_code>(CELL_OK) : CELL_DMUX_PAMF_ERROR_FATAL;
default:
fmt::throw_exception("Unexpected savestate value: 0x%x", savestate);
@ -2698,7 +2716,7 @@ error_code DmuxPamfElementaryStream::flush_es(ppu_thread& ppu) const
if (!waiting_for_spu_state)
{
if (sys_mutex_lock(ppu, demuxer->mutex, 0) != CELL_OK)
if (lv2_syscall<sys_mutex_lock>(ppu, demuxer->mutex, 0) != CELL_OK)
{
return CELL_DMUX_PAMF_ERROR_FATAL;
}
@ -2718,7 +2736,7 @@ error_code DmuxPamfElementaryStream::flush_es(ppu_thread& ppu) const
return {};
}
return sys_mutex_unlock(ppu, demuxer->mutex) == CELL_OK ? static_cast<error_code>(CELL_OK) : CELL_DMUX_PAMF_ERROR_FATAL;
return lv2_syscall<sys_mutex_unlock>(ppu, demuxer->mutex) == CELL_OK ? static_cast<error_code>(CELL_OK) : CELL_DMUX_PAMF_ERROR_FATAL;
}
error_code _CellDmuxCoreOpFlushEs(ppu_thread& ppu, vm::ptr<CellDmuxPamfEsHandle> esHandle)
@ -2741,7 +2759,7 @@ error_code DmuxPamfElementaryStream::reset_es(ppu_thread& ppu) const
if (!waiting_for_spu_state)
{
if (sys_mutex_lock(ppu, demuxer->mutex, 0) != CELL_OK)
if (lv2_syscall<sys_mutex_lock>(ppu, demuxer->mutex, 0) != CELL_OK)
{
return CELL_DMUX_PAMF_ERROR_FATAL;
}
@ -2761,7 +2779,7 @@ error_code DmuxPamfElementaryStream::reset_es(ppu_thread& ppu) const
return {};
}
return sys_mutex_unlock(ppu, demuxer->mutex) == CELL_OK ? static_cast<error_code>(CELL_OK) : CELL_DMUX_PAMF_ERROR_FATAL;
return lv2_syscall<sys_mutex_unlock>(ppu, demuxer->mutex) == CELL_OK ? static_cast<error_code>(CELL_OK) : CELL_DMUX_PAMF_ERROR_FATAL;
}
error_code _CellDmuxCoreOpResetEs(ppu_thread& ppu, vm::ptr<CellDmuxPamfEsHandle> esHandle)
@ -2797,7 +2815,7 @@ error_code DmuxPamfContext::reset_stream_and_wait_done(ppu_thread& ppu)
return {};
}
if (sys_mutex_lock(ppu, mutex, 0) != CELL_OK)
if (lv2_syscall<sys_mutex_lock>(ppu, mutex, 0) != CELL_OK)
{
return CELL_DMUX_PAMF_ERROR_FATAL;
}
@ -2809,9 +2827,9 @@ error_code DmuxPamfContext::reset_stream_and_wait_done(ppu_thread& ppu)
while (sequence_state != DmuxPamfSequenceState::dormant)
{
if (sys_cond_wait(ppu, cond, 0) != CELL_OK)
if (lv2_syscall<sys_cond_wait>(ppu, cond, 0) != CELL_OK)
{
sys_mutex_unlock(ppu, mutex);
lv2_syscall<sys_mutex_unlock>(ppu, mutex);
return CELL_DMUX_PAMF_ERROR_FATAL;
}
@ -2821,7 +2839,7 @@ error_code DmuxPamfContext::reset_stream_and_wait_done(ppu_thread& ppu)
}
}
return sys_mutex_unlock(ppu, mutex) == CELL_OK ? static_cast<error_code>(CELL_OK) : CELL_DMUX_PAMF_ERROR_FATAL;
return lv2_syscall<sys_mutex_unlock>(ppu, mutex) == CELL_OK ? static_cast<error_code>(CELL_OK) : CELL_DMUX_PAMF_ERROR_FATAL;
}
error_code _CellDmuxCoreOpResetStreamAndWaitDone(ppu_thread& ppu, vm::ptr<CellDmuxPamfHandle> handle)

View File

@ -1774,6 +1774,12 @@ public:
shared_mutex mutex;
gem_tracker& operator=(thread_state) noexcept
{
wake_up_tracker();
return *this;
}
private:
atomic_t<u32> m_wake_up_tracker = 0;
atomic_t<u32> m_tracker_done = 0;

View File

@ -192,7 +192,46 @@ error_code cellNetCtlDelHandler(s32 hid)
error_code cellNetCtlGetInfo(s32 code, vm::ptr<CellNetCtlInfo> info)
{
cellNetCtl.warning("cellNetCtlGetInfo(code=0x%x (%s), info=*0x%x)", code, InfoCodeToName(code), info);
bool log_it_once = false;
switch (code)
{
case CELL_NET_CTL_INFO_ETHER_ADDR:
case CELL_NET_CTL_INFO_DEVICE:
case CELL_NET_CTL_INFO_MTU:
case CELL_NET_CTL_INFO_LINK_TYPE:
case CELL_NET_CTL_INFO_IP_CONFIG:
case CELL_NET_CTL_INFO_IP_ADDRESS:
case CELL_NET_CTL_INFO_NETMASK:
case CELL_NET_CTL_INFO_DEFAULT_ROUTE:
case CELL_NET_CTL_INFO_HTTP_PROXY_CONFIG:
case CELL_NET_CTL_INFO_UPNP_CONFIG:
{
log_it_once = true;
break;
}
default:
{
break;
}
}
bool log_it = true;
if (log_it_once && vm::check_addr(info.addr()))
{
struct logged_t
{
std::array<atomic_t<bool>, 256> logged_code{};
};
if (g_fxo->get<logged_t>().logged_code[::narrow<u8>(code)].exchange(true))
{
log_it = false;
}
}
(log_it ? cellNetCtl.warning : cellNetCtl.trace)("cellNetCtlGetInfo(code=0x%x (%s), info=*0x%x)", code, InfoCodeToName(code), info);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();

View File

@ -5,14 +5,6 @@
#include <bitset>
#include "cellPamf.h"
const std::function<bool()> SQUEUE_ALWAYS_EXIT = []() { return true; };
const std::function<bool()> SQUEUE_NEVER_EXIT = []() { return false; };
bool squeue_test_exit()
{
return Emu.IsStopped();
}
LOG_CHANNEL(cellPamf);
template<>

View File

@ -595,345 +595,3 @@ struct CellPamfReader
CHECK_SIZE(CellPamfReader, 128);
error_code cellPamfReaderInitialize(vm::ptr<CellPamfReader> pSelf, vm::cptr<PamfHeader> pAddr, u64 fileSize, u32 attribute);
#include <mutex>
#include <condition_variable>
extern const std::function<bool()> SQUEUE_ALWAYS_EXIT;
extern const std::function<bool()> SQUEUE_NEVER_EXIT;
bool squeue_test_exit();
// TODO: eliminate this boolshit
template<typename T, u32 sq_size = 256>
class squeue_t
{
struct squeue_sync_var_t
{
struct
{
u32 position : 31;
u32 pop_lock : 1;
};
struct
{
u32 count : 31;
u32 push_lock : 1;
};
};
atomic_t<squeue_sync_var_t> m_sync;
mutable std::mutex m_rcv_mutex;
mutable std::mutex m_wcv_mutex;
mutable std::condition_variable m_rcv;
mutable std::condition_variable m_wcv;
T m_data[sq_size];
enum squeue_sync_var_result : u32
{
SQSVR_OK = 0,
SQSVR_LOCKED = 1,
SQSVR_FAILED = 2,
};
public:
squeue_t()
: m_sync(squeue_sync_var_t{})
{
}
static u32 get_max_size()
{
return sq_size;
}
bool is_full() const
{
return m_sync.load().count == sq_size;
}
bool push(const T& data, const std::function<bool()>& test_exit)
{
u32 pos = 0;
while (u32 res = m_sync.atomic_op([&pos](squeue_sync_var_t& sync) -> u32
{
ensure(sync.count <= sq_size);
ensure(sync.position < sq_size);
if (sync.push_lock)
{
return SQSVR_LOCKED;
}
if (sync.count == sq_size)
{
return SQSVR_FAILED;
}
sync.push_lock = 1;
pos = sync.position + sync.count;
return SQSVR_OK;
}))
{
if (res == SQSVR_FAILED && (test_exit() || squeue_test_exit()))
{
return false;
}
std::unique_lock<std::mutex> wcv_lock(m_wcv_mutex);
m_wcv.wait_for(wcv_lock, std::chrono::milliseconds(1));
}
m_data[pos >= sq_size ? pos - sq_size : pos] = data;
m_sync.atomic_op([](squeue_sync_var_t& sync)
{
ensure(sync.count <= sq_size);
ensure(sync.position < sq_size);
ensure(!!sync.push_lock);
sync.push_lock = 0;
sync.count++;
});
m_rcv.notify_one();
m_wcv.notify_one();
return true;
}
bool push(const T& data, const volatile bool* do_exit)
{
return push(data, [do_exit]() { return do_exit && *do_exit; });
}
bool push(const T& data)
{
return push(data, SQUEUE_NEVER_EXIT);
}
bool try_push(const T& data)
{
return push(data, SQUEUE_ALWAYS_EXIT);
}
bool pop(T& data, const std::function<bool()>& test_exit)
{
u32 pos = 0;
while (u32 res = m_sync.atomic_op([&pos](squeue_sync_var_t& sync) -> u32
{
ensure(sync.count <= sq_size);
ensure(sync.position < sq_size);
if (!sync.count)
{
return SQSVR_FAILED;
}
if (sync.pop_lock)
{
return SQSVR_LOCKED;
}
sync.pop_lock = 1;
pos = sync.position;
return SQSVR_OK;
}))
{
if (res == SQSVR_FAILED && (test_exit() || squeue_test_exit()))
{
return false;
}
std::unique_lock<std::mutex> rcv_lock(m_rcv_mutex);
m_rcv.wait_for(rcv_lock, std::chrono::milliseconds(1));
}
data = m_data[pos];
m_sync.atomic_op([](squeue_sync_var_t& sync)
{
ensure(sync.count <= sq_size);
ensure(sync.position < sq_size);
ensure(!!sync.pop_lock);
sync.pop_lock = 0;
sync.position++;
sync.count--;
if (sync.position == sq_size)
{
sync.position = 0;
}
});
m_rcv.notify_one();
m_wcv.notify_one();
return true;
}
bool pop(T& data, const volatile bool* do_exit)
{
return pop(data, [do_exit]() { return do_exit && *do_exit; });
}
bool pop(T& data)
{
return pop(data, SQUEUE_NEVER_EXIT);
}
bool try_pop(T& data)
{
return pop(data, SQUEUE_ALWAYS_EXIT);
}
bool peek(T& data, u32 start_pos, const std::function<bool()>& test_exit)
{
ensure(start_pos < sq_size);
u32 pos = 0;
while (u32 res = m_sync.atomic_op([&pos, start_pos](squeue_sync_var_t& sync) -> u32
{
ensure(sync.count <= sq_size);
ensure(sync.position < sq_size);
if (sync.count <= start_pos)
{
return SQSVR_FAILED;
}
if (sync.pop_lock)
{
return SQSVR_LOCKED;
}
sync.pop_lock = 1;
pos = sync.position + start_pos;
return SQSVR_OK;
}))
{
if (res == SQSVR_FAILED && (test_exit() || squeue_test_exit()))
{
return false;
}
std::unique_lock<std::mutex> rcv_lock(m_rcv_mutex);
m_rcv.wait_for(rcv_lock, std::chrono::milliseconds(1));
}
data = m_data[pos >= sq_size ? pos - sq_size : pos];
m_sync.atomic_op([](squeue_sync_var_t& sync)
{
ensure(sync.count <= sq_size);
ensure(sync.position < sq_size);
ensure(!!sync.pop_lock);
sync.pop_lock = 0;
});
m_rcv.notify_one();
return true;
}
bool peek(T& data, u32 start_pos, const volatile bool* do_exit)
{
return peek(data, start_pos, [do_exit]() { return do_exit && *do_exit; });
}
bool peek(T& data, u32 start_pos = 0)
{
return peek(data, start_pos, SQUEUE_NEVER_EXIT);
}
bool try_peek(T& data, u32 start_pos = 0)
{
return peek(data, start_pos, SQUEUE_ALWAYS_EXIT);
}
class squeue_data_t
{
T* const m_data;
const u32 m_pos;
const u32 m_count;
squeue_data_t(T* data, u32 pos, u32 count)
: m_data(data)
, m_pos(pos)
, m_count(count)
{
}
public:
T& operator [] (u32 index)
{
ensure(index < m_count);
index += m_pos;
index = index < sq_size ? index : index - sq_size;
return m_data[index];
}
};
void process(void(*proc)(squeue_data_t data))
{
u32 pos, count;
while (m_sync.atomic_op([&pos, &count](squeue_sync_var_t& sync) -> u32
{
ensure(sync.count <= sq_size);
ensure(sync.position < sq_size);
if (sync.pop_lock || sync.push_lock)
{
return SQSVR_LOCKED;
}
pos = sync.position;
count = sync.count;
sync.pop_lock = 1;
sync.push_lock = 1;
return SQSVR_OK;
}))
{
std::unique_lock<std::mutex> rcv_lock(m_rcv_mutex);
m_rcv.wait_for(rcv_lock, std::chrono::milliseconds(1));
}
proc(squeue_data_t(m_data, pos, count));
m_sync.atomic_op([](squeue_sync_var_t& sync)
{
ensure(sync.count <= sq_size);
ensure(sync.position < sq_size);
ensure(!!sync.pop_lock);
ensure(!!sync.push_lock);
sync.pop_lock = 0;
sync.push_lock = 0;
});
m_wcv.notify_one();
m_rcv.notify_one();
}
void clear()
{
while (m_sync.atomic_op([](squeue_sync_var_t& sync) -> u32
{
ensure(sync.count <= sq_size);
ensure(sync.position < sq_size);
if (sync.pop_lock || sync.push_lock)
{
return SQSVR_LOCKED;
}
sync.pop_lock = 1;
sync.push_lock = 1;
return SQSVR_OK;
}))
{
std::unique_lock<std::mutex> rcv_lock(m_rcv_mutex);
m_rcv.wait_for(rcv_lock, std::chrono::milliseconds(1));
}
m_sync.exchange({});
m_wcv.notify_one();
m_rcv.notify_one();
}
};

View File

@ -1267,6 +1267,11 @@ struct SceNpOnlineId
{
char data[SCE_NET_NP_ONLINEID_MAX_LENGTH + 1]; // char term;
char dummy[3];
bool operator<(const SceNpOnlineId& other) const
{
return memcmp(data, other.data, sizeof(data)) < 0;
}
};
// NP ID structure
@ -1283,6 +1288,11 @@ struct SceNpId
};
u8 reserved[8];
bool operator<(const SceNpId& other) const
{
return handle < other.handle;
}
};
CHECK_SIZE_ALIGN(SceNpId, 0x24, 1);
@ -1689,12 +1699,22 @@ struct SceNpLobbyId
{
u8 opt[28];
u8 reserved[8];
bool operator<(const SceNpLobbyId& other) const
{
return memcmp(opt, other.opt, sizeof(opt)) < 0;
}
};
struct SceNpRoomId
{
u8 opt[28];
u8 reserved[8];
bool operator<(const SceNpRoomId& other) const
{
return memcmp(opt, other.opt, sizeof(opt)) < 0;
}
};
struct SceNpMatchingAttr

View File

@ -1286,7 +1286,7 @@ extern bool ppu_patch(u32 addr, u32 value)
{
if (addr % 4)
{
ppu_log.fatal("Patch failed at 0x%x: unanligned memory address.", addr);
ppu_log.fatal("Patch failed at 0x%x: unaligned memory address.", addr);
return false;
}
@ -1364,9 +1364,7 @@ void ppu_thread::dump_regs(std::string& ret, std::any& custom_data) const
u32 preferred_cr_field_index = 7;
};
dump_registers_data_t* func_data = nullptr;
func_data = std::any_cast<dump_registers_data_t>(&custom_data);
dump_registers_data_t* func_data = std::any_cast<dump_registers_data_t>(&custom_data);
if (!func_data)
{
@ -2039,9 +2037,9 @@ std::vector<std::pair<u32, u32>> ppu_thread::dump_callstack_list() const
return call_stack_list;
}
std::string ppu_thread::dump_misc() const
void ppu_thread::dump_misc(std::string& ret, std::any& custom_data) const
{
std::string ret = cpu_thread::dump_misc();
cpu_thread::dump_misc(ret, custom_data);
if (ack_suspend)
{
@ -2096,7 +2094,6 @@ std::string ppu_thread::dump_misc() const
{
ret += '\n';
}
return ret;
}
void ppu_thread::dump_all(std::string& ret) const

View File

@ -145,7 +145,7 @@ public:
virtual void dump_regs(std::string&, std::any& custom_data) const override;
virtual std::string dump_callstack() const override;
virtual std::vector<std::pair<u32, u32>> dump_callstack_list() const override;
virtual std::string dump_misc() const override;
virtual void dump_misc(std::string& ret, std::any& custom_data) const override;
virtual void dump_all(std::string&) const override;
virtual void cpu_task() override final;
virtual void cpu_sleep() override;

View File

@ -340,7 +340,7 @@ Function* PPUTranslator::GetSymbolResolver(const ppu_module<lv2_obj>& info)
const auto ftype = FunctionType::get(get_type<void>(), {
get_type<u8*>(), // Exec base
m_ir->getPtrTy(), // PPU context
get_type<u8*>(), // PPU context
get_type<u64>(), // Segment address (for PRX)
get_type<u8*>(), // Memory base
get_type<u64>(), // r0
@ -386,7 +386,7 @@ Function* PPUTranslator::GetSymbolResolver(const ppu_module<lv2_obj>& info)
const auto addr_array = new GlobalVariable(*m_module, addr_array_type, false, GlobalValue::PrivateLinkage, ConstantDataArray::get(m_context, vec_addrs));
// Create an array of function pointers
const auto func_table_type = ArrayType::get(m_ir->getPtrTy(), functions.size());
const auto func_table_type = ArrayType::get(get_type<u8*>(), functions.size());
const auto init_func_table = ConstantArray::get(func_table_type, functions);
const auto func_table = new GlobalVariable(*m_module, func_table_type, false, GlobalVariable::PrivateLinkage, init_func_table);
@ -413,7 +413,7 @@ Function* PPUTranslator::GetSymbolResolver(const ppu_module<lv2_obj>& info)
const auto func_pc = ZExt(m_ir->CreateLoad(ptr_inst->getResultElementType(), ptr_inst), get_type<u64>());
ptr_inst = dyn_cast<GetElementPtrInst>(m_ir->CreateGEP(func_table->getValueType(), func_table, {m_ir->getInt64(0), index_value}));
assert(ptr_inst->getResultElementType() == m_ir->getPtrTy());
assert(ptr_inst->getResultElementType() == get_type<u8*>());
const auto faddr = m_ir->CreateLoad(ptr_inst->getResultElementType(), ptr_inst);
const auto pos_32 = m_reloc ? m_ir->CreateAdd(func_pc, m_seg0) : func_pc;
@ -622,7 +622,7 @@ void PPUTranslator::CallFunction(u64 target, Value* indirect)
const auto pos = m_ir->CreateShl(indirect, 1);
const auto ptr = m_ir->CreatePtrAdd(m_exec, pos);
const auto val = m_ir->CreateLoad(get_type<u64>(), ptr);
callee = FunctionCallee(type, m_ir->CreateIntToPtr(val, m_ir->getPtrTy()));
callee = FunctionCallee(type, m_ir->CreateIntToPtr(val, get_type<u8*>()));
// Load new segment address
const auto seg_base_ptr = m_ir->CreatePtrAdd(m_exec, m_ir->getInt64(vm::g_exec_addr_seg_offset));
@ -5414,7 +5414,7 @@ MDNode* PPUTranslator::CheckBranchProbability(u32 bo)
void PPUTranslator::build_interpreter()
{
#define BUILD_VEC_INST(i) { \
m_function = llvm::cast<llvm::Function>(m_module->getOrInsertFunction("op_" #i, get_type<void>(), m_ir->getPtrTy()).getCallee()); \
m_function = llvm::cast<llvm::Function>(m_module->getOrInsertFunction("op_" #i, get_type<void>(), get_type<u8*>()).getCallee()); \
std::fill(std::begin(m_globals), std::end(m_globals), nullptr); \
std::fill(std::begin(m_locals), std::end(m_locals), nullptr); \
IRBuilder<> irb(BasicBlock::Create(m_context, "__entry", m_function)); \

View File

@ -13,6 +13,7 @@ struct spu_itype
static constexpr struct quadrop_tag{} _quadrop{}; // 4-op Instructions
static constexpr struct xfloat_tag{} xfloat{}; // Instructions producing xfloat values
static constexpr struct zregmod_tag{} zregmod{}; // Instructions not modifying any GPR
static constexpr struct pure_tag{} pure{}; // Instructions that always produce the same values as long as arguments are equal
enum class type : unsigned char
{
@ -158,6 +159,15 @@ struct spu_itype
CUFLT,
FRDS, // xfloat_tag last
CFLTS,
CFLTU,
FCEQ,
FCMEQ,
FCGT,
FCMGT, // floating_tag last
FSCRWR,
FSCRRD,
DFA,
DFS,
DFM,
@ -167,20 +177,11 @@ struct spu_itype
DFNMA,
FESD,
CFLTS,
CFLTU,
FCEQ,
FCMEQ,
FCGT,
FCMGT,
FSCRWR,
FSCRRD,
DFCEQ,
DFCMEQ,
DFCGT,
DFCMGT,
DFTSV, // floating_tag last
DFTSV,
SHLH, // shiftrot_tag first
SHLHI,
@ -248,10 +249,10 @@ struct spu_itype
return value >= BR && value <= BISL;
}
// Test for floating point instruction
// Test for floating point instruction (32-bit float)
friend constexpr bool operator &(type value, floating_tag)
{
return value >= FMA && value <= DFTSV;
return value >= FMA && value <= FCMGT;
}
// Test for 4-op instruction
@ -301,8 +302,16 @@ struct spu_itype
{
return (value >= HEQ && value <= STQR) || (value >= BR && value <= BIHNZ);
}
// Test for instructions which always produce the same values as long as arguments and immediate values are equal
friend constexpr bool operator &(type value, pure_tag)
{
return (value >= ILH && value <= CLGTI);
}
};
using spu_itype_t = spu_itype::type;
struct spu_iflag
{
enum
@ -528,6 +537,8 @@ struct spu_iflag
}
};
using spu_iflag_t = spu_iflag::flag;
#define NAME(x) static constexpr const char& x = *#x
struct spu_iname

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -24,6 +24,20 @@ union spu_opcode_t
bf_t<u32, 7, 16> i16; // 9..24
bf_t<s32, 7, 16> si16; // 9..24, signed
bf_t<u32, 7, 18> i18; // 7..24
// For 16-bit instructions in the context of 32-bits
u32 duplicate_si10() const
{
const u32 _16 = static_cast<u16>(static_cast<s16>(si10));
return (_16 << 16) | _16;
}
// For 8-bit instructions in the context of 32-bits
u32 duplicate_duplicate_si10() const
{
const u32 _8 = static_cast<u8>(si10 & 0xff);
return (_8 << 24) | (_8 << 16) | (_8 << 8) | _8;
}
};
constexpr u32 spu_branch_target(u32 pc, u32 imm = 0)
@ -42,6 +56,7 @@ constexpr u32 spu_decode(u32 inst)
}
std::array<u32, 2> op_branch_targets(u32 pc, spu_opcode_t op);
std::tuple<u32, std::array<u32, 3>, u32> op_register_targets(u32 /*pc*/, spu_opcode_t op);
// SPU decoder object. D provides functions. T is function pointer type returned.
template <typename D, typename T = decltype(&D::UNK)>

View File

@ -4,12 +4,24 @@
#include "Utilities/lockless.h"
#include "Utilities/address_range.h"
#include "SPUThread.h"
#include "SPUAnalyser.h"
#include <vector>
#include <bitset>
#include <memory>
#include <string>
#include <deque>
// std::bitset
template <typename CT, typename T>
requires requires(std::remove_cvref_t<CT>& x, T&& y) { x.count(); x.test(y); x.flip(y); }
[[nodiscard]] constexpr bool at32(CT&& container, T&& index, std::source_location src_loc = std::source_location::current())
{
const usz csv = container.size();
if (csv <= std::forward<T>(index)) [[unlikely]]
fmt::raw_range_error(src_loc, format_object_simplified(index), csv);
return container[std::forward<T>(index)];
}
// Helper class
class spu_cache
{
@ -201,6 +213,25 @@ public:
__bitset_enum_max
};
enum compare_direction : u32
{
CMP_TURNAROUND_FLAG = 0x1,
CMP_NEGATE_FLAG = 0x100,
CMP_SLESS = 0,
CMP_SGREATER = CMP_SLESS | CMP_TURNAROUND_FLAG,
CMP_EQUAL,
CMP_EQUAL2 = CMP_EQUAL | CMP_TURNAROUND_FLAG,
CMP_LLESS,
CMP_LGREATER = CMP_LLESS | CMP_TURNAROUND_FLAG,
CMP_SGREATER_EQUAL = CMP_SLESS | CMP_NEGATE_FLAG,
CMP_SLOWER_EQUAL = CMP_SGREATER | CMP_NEGATE_FLAG,
CMP_NOT_EQUAL = CMP_EQUAL | CMP_NEGATE_FLAG,
CMP_NOT_EQUAL2 = CMP_NOT_EQUAL | CMP_TURNAROUND_FLAG,
CMP_LGREATER_EQUAL = CMP_LLESS | CMP_NEGATE_FLAG,
CMP_LLOWER_EQUAL = CMP_LGREATER | CMP_NEGATE_FLAG,
CMP_UNKNOWN,
};
struct reg_state_t
{
bs_t<vf> flag{+vf::is_null};
@ -273,6 +304,406 @@ public:
static u32 alloc_tag(bool reset = false) noexcept;
};
struct reduced_loop_t
{
bool active = false; // Single block loop detected
bool failed = false;
u32 loop_pc = SPU_LS_SIZE;
u32 loop_end = SPU_LS_SIZE;
// False: single-block loop
// True: loop with a trailing block of aftermath (iteration update) stuff (like for (u32 i = 0; i < 10; /*update*/ i++))
bool is_two_block_loop = false;
bool has_cond_state = false;
// Loop stay-in state requirement
u64 cond_val_mask = umax;
u64 cond_val_min = 0;
u64 cond_val_size = 0;
compare_direction cond_val_compare{};
u64 cond_val_incr = 0;
bool cond_val_incr_is_immediate = false;
u64 cond_val_register_argument_idx = umax;
u64 cond_val_register_idx = umax;
bool cond_val_incr_before_cond = false;
bool cond_val_incr_before_cond_taken_in_account = false;
bool cond_val_is_immediate = false;
// Loop attributes
bool is_constant_expression = false;
bool is_secret = false;
struct supplemental_condition_t
{
u64 immediate_value = umax;
u64 type_size = 0;
compare_direction val_compare{};
};
// Supplemental loop condition:
// Inner conditions that depend on extrnal values (not produced inside the loop)
// all should evaluate to false in order for the optimization to work (at the moment)
// So succeeding can be treated linearly
u64 expected_sup_conds = 0;
u64 current_sup_conds_index = 0;
std::vector<supplemental_condition_t> sup_conds;
void take_cond_val_incr_before_cond_into_account()
{
if (cond_val_is_immediate && cond_val_incr_before_cond_taken_in_account && !cond_val_incr_before_cond_taken_in_account)
{
cond_val_min -= cond_val_incr;
cond_val_min &= cond_val_mask;
cond_val_incr_before_cond_taken_in_account = true;
}
}
std::bitset<s_reg_max> loop_args;
std::bitset<s_reg_max> loop_dicts;
std::bitset<s_reg_max> loop_writes;
std::bitset<s_reg_max> loop_may_update;
std::bitset<s_reg_max> gpr_not_nans;
struct origin_t
{
std::bitset<s_reg_max + 1> regs{};
u32 modified = 0;
spu_itype_t mod1_type = spu_itype::UNK;
spu_itype_t mod2_type = spu_itype::UNK;
spu_itype_t mod3_type = spu_itype::UNK;
u32 IMM = 0;
private:
// Internal, please access using fixed order
spu_itype_t access_type(u32 i) const
{
if (i > modified)
{
return spu_itype::UNK;
}
switch (i)
{
case 1: return mod1_type;
case 2: return mod2_type;
case 3: return mod3_type;
default: return spu_itype::UNK;
}
return spu_itype::UNK;
}
public:
spu_itype_t reverse1_type()
{
return access_type(modified);
}
spu_itype_t reverse2_type()
{
return access_type(modified - 1);
}
spu_itype_t reverse3_type()
{
return access_type(modified - 2);
}
origin_t& join_with_this(const origin_t& rhs)
{
regs |= rhs.regs;
return *this;
}
origin_t& join_with_this(u32 rhs)
{
regs.set(rhs);
return *this;
}
origin_t& add_register_origin(u32 reg_val)
{
regs.set(reg_val);
return *this;
}
bool is_single_reg_access(u32 reg_val) const
{
if (!modified)
{
return true;
}
return regs.count() == 1 && ::at32(regs, reg_val);
}
bool is_loop_dictator(u32 reg_val, bool test_predictable = false, bool should_predictable = true) const
{
if (!modified)
{
return false;
}
if (regs.count() >= 1 && ::at32(regs, reg_val))
{
if (!test_predictable)
{
return true;
}
if (modified > 1)
{
return should_predictable ^ true;
}
switch (mod1_type)
{
case spu_itype::A:
{
if (regs.count() == 2)
{
return should_predictable;
}
return should_predictable ^ true;
}
case spu_itype::AI:
case spu_itype::AHI:
{
if (IMM && regs.count() == 1)
{
return should_predictable;
}
return should_predictable ^ true;
}
default: break;
}
return should_predictable ^ true;
}
return false;
}
bool is_predictable_loop_dictator(u32 reg_val) const
{
return is_loop_dictator(reg_val, true, true);
}
bool is_non_predictable_loop_dictator(u32 reg_val) const
{
return is_loop_dictator(reg_val, true, false);
}
bool is_null(u32 reg_val) const noexcept
{
if (modified)
{
return false;
}
if (regs.count() - (::at32(regs, reg_val) ? 1 : 0))
{
return false;
}
return true;
}
origin_t& add_instruction_modifier(spu_itype_t inst_type, u32 imm = 0)
{
if (inst_type == spu_itype::UNK)
{
mod1_type = spu_itype::UNK;
mod2_type = spu_itype::UNK;
mod3_type = spu_itype::UNK;
IMM = umax;
modified = 1;
return *this;
}
if (modified == 1)
{
if (modified == 3)
{
mod1_type = spu_itype::UNK;
mod2_type = spu_itype::UNK;
mod3_type = spu_itype::UNK;
IMM = umax;
modified = 1;
return *this;
}
bool is_ok = false;
switch (inst_type)
{
case spu_itype::XSBH:
{
const auto prev_type = modified == 1 ? mod1_type : mod2_type;
is_ok &= mod1_type == spu_itype::CEQB || mod1_type == spu_itype::CEQBI || mod1_type == spu_itype::CGTB || mod1_type == spu_itype::CGTBI || mod1_type == spu_itype::CLGTB || mod1_type == spu_itype::CLGTBI;
break;
}
case spu_itype::ANDI:
{
const auto prev_type = modified == 1 ? mod1_type : mod2_type;
is_ok &= mod1_type == spu_itype::CEQB || mod1_type == spu_itype::CEQBI || mod1_type == spu_itype::CGTB || mod1_type == spu_itype::CGTBI || mod1_type == spu_itype::CLGTB || mod1_type == spu_itype::CLGTBI;
is_ok &= (spu_opcode_t{imm}.si10 & 0xff) == 0xff;
break;
}
case spu_itype::CEQ:
case spu_itype::CEQH:
case spu_itype::CEQB:
case spu_itype::CGT:
case spu_itype::CGTH:
case spu_itype::CGTB:
case spu_itype::CLGT:
case spu_itype::CLGTH:
case spu_itype::CLGTB:
{
is_ok = modified == 1 && (mod1_type == spu_itype::AI || mod1_type == spu_itype::AHI);
IMM = imm;
break;
}
case spu_itype::CEQI:
case spu_itype::CEQHI:
case spu_itype::CEQBI:
case spu_itype::CGTI:
case spu_itype::CGTHI:
case spu_itype::CGTBI:
case spu_itype::CLGTI:
case spu_itype::CLGTHI:
case spu_itype::CLGTBI:
{
is_ok = modified == 1 && (mod1_type == spu_itype::AI || mod1_type == spu_itype::AHI);
IMM = spu_opcode_t{imm}.si10;
break;
}
}
if (!is_ok)
{
mod1_type = spu_itype::UNK;
mod2_type = spu_itype::UNK;
mod3_type = spu_itype::UNK;
IMM = umax;
modified = 1;
return *this;
}
(modified == 1 ? mod2_type : mod3_type) = inst_type;
modified++;
return *this;
}
mod1_type = inst_type;
modified = 1;
switch (inst_type)
{
case spu_itype::AHI:
{
IMM = spu_opcode_t{imm}.duplicate_si10();
return *this;
}
case spu_itype::AI:
case spu_itype::ORI:
case spu_itype::XORI:
case spu_itype::ANDI:
case spu_itype::CEQI:
case spu_itype::CEQHI:
case spu_itype::CEQBI:
case spu_itype::CGTI:
case spu_itype::CGTHI:
case spu_itype::CGTBI:
case spu_itype::CLGTI:
case spu_itype::CLGTHI:
case spu_itype::CLGTBI:
{
IMM = spu_opcode_t{imm}.si10;
return *this;
}
case spu_itype::ILA:
{
IMM = spu_opcode_t{imm}.i18;
return *this;
}
case spu_itype::IOHL:
case spu_itype::ILH:
case spu_itype::ILHU:
{
IMM = spu_opcode_t{imm}.i16;
return *this;
}
default:
{
IMM = imm;
break;
}
}
return *this;
}
};
static origin_t make_reg(u32 reg_val) noexcept
{
origin_t org{};
org.add_register_origin(reg_val);
return org;
}
const origin_t* find_reg(u32 reg_val) const noexcept
{
for (auto& pair : regs)
{
if (pair.first == reg_val)
{
return &pair.second;
}
}
return nullptr;
}
origin_t* find_reg(u32 reg_val) noexcept
{
return const_cast<origin_t*>(std::as_const(*this).find_reg(reg_val));
}
bool is_reg_null(u32 reg_val) const noexcept
{
if (const auto reg_found = find_reg(reg_val))
{
return reg_found->is_null(reg_val);
}
return true;
}
bool is_gpr_not_NaN_hint(u32 i) const noexcept
{
return ::at32(gpr_not_nans, i);
}
origin_t get_reg(u32 reg_val) noexcept
{
const auto org = find_reg(reg_val);
return org ? *org : regs.emplace_back(reg_val, std::remove_reference_t<decltype(*org)>{}).second;
}
std::vector<std::pair<u8, origin_t>> regs;
// Return old state for error reporting
reduced_loop_t discard()
{
const reduced_loop_t old = *this;
*this = reduced_loop_t{};
return old;
}
};
protected:
spu_runtime* m_spurt{};
@ -326,8 +757,14 @@ protected:
// Set if the initial register value in this block may be xfloat
std::bitset<s_reg_max> reg_maybe_xf{};
// Bit mask of the registers used (before modified)
std::bitset<s_reg_max> reg_use{};
// Set if register is used in floating pont instruction
std::bitset<s_reg_max> reg_maybe_float{};
// Set if register is used as shuffle mask
std::bitset<s_reg_max> reg_maybe_shuffle_mask{};
// Number of times registers are used (before modified)
std::array<u32, s_reg_max> reg_use{};
// Bit mask of the trivial (u32 x 4) constant value resulting in this block
std::bitset<s_reg_max> reg_const{};
@ -391,18 +828,23 @@ protected:
putllc16,
putllc0,
rchcnt_loop,
reduced_loop,
};
std::vector<inst_attr> m_inst_attrs;
struct pattern_info
{
u64 info;
// Info via integral
u64 info{};
// Info via additional erased-typed pointer
std::shared_ptr<void> info_ptr;
};
std::unordered_map<u32, pattern_info> m_patterns;
std::map<u32, pattern_info> m_patterns;
void add_pattern(inst_attr attr, u32 start, u64 info);
void add_pattern(inst_attr attr, u32 start, u64 info, std::shared_ptr<void> info_ptr = nullptr);
private:
// For private use
@ -435,7 +877,7 @@ public:
spu_program analyse(const be_t<u32>* ls, u32 entry_point, std::map<u32, std::vector<u32>>* out_target_list = nullptr);
// Print analyser internal state
void dump(const spu_program& result, std::string& out);
void dump(const spu_program& result, std::string& out, u32 block_min = 0, u32 block_max = SPU_LS_SIZE);
// Get SPU Runtime
spu_runtime& get_runtime()

View File

@ -495,7 +495,8 @@ void do_cell_atomic_128_store(u32 addr, const void* to_write);
extern thread_local u64 g_tls_fault_spu;
const spu_decoder<spu_itype> s_spu_itype;
const extern spu_decoder<spu_itype> g_spu_itype;
const extern spu_decoder<spu_iflag> g_spu_iflag;
namespace vm
{
@ -598,7 +599,7 @@ std::array<u32, 2> op_branch_targets(u32 pc, spu_opcode_t op)
{
std::array<u32, 2> res{spu_branch_target(pc + 4), umax};
switch (const auto type = s_spu_itype.decode(op.opcode))
switch (const auto type = g_spu_itype.decode(op.opcode))
{
case spu_itype::BR:
case spu_itype::BRA:
@ -639,6 +640,54 @@ std::array<u32, 2> op_branch_targets(u32 pc, spu_opcode_t op)
return res;
}
std::tuple<u32, std::array<u32, 3>, u32> op_register_targets(u32 /*pc*/, spu_opcode_t op)
{
std::tuple<u32, std::array<u32, 3>, u32> result{u32{umax}, std::array<u32, 3>{128, 128, 128}, op.opcode};
const auto type = g_spu_itype.decode(op.opcode);
if (type & spu_itype::zregmod)
{
std::get<2>(result) = 0;
return result;
}
std::get<0>(result) = type & spu_itype::_quadrop ? op.rt4 : op.rt;
spu_opcode_t op_masked = op;
if (type & spu_itype::_quadrop)
{
op_masked.rt4 = 0;
}
else
{
op_masked.rt = 0;
}
std::get<2>(result) = op_masked.opcode;
if (auto iflags = g_spu_iflag.decode(op.opcode))
{
if (+iflags & +spu_iflag::use_ra)
{
std::get<1>(result)[0] = op.ra;
}
if (+iflags & +spu_iflag::use_rb)
{
std::get<1>(result)[1] = op.rb;
}
if (+iflags & +spu_iflag::use_rc)
{
std::get<1>(result)[2] = op.rc;
}
}
return result;
}
void spu_int_ctrl_t::set(u64 ints)
{
// leave only enabled interrupts
@ -988,7 +1037,7 @@ std::vector<std::pair<u32, u32>> spu_thread::dump_callstack_list() const
passed[i / 4] = true;
const spu_opcode_t op{_ref<u32>(i)};
const auto type = s_spu_itype.decode(op.opcode);
const auto type = g_spu_itype.decode(op.opcode);
if (start == 0 && type == spu_itype::STQD && op.ra == 1u && op.rt == 0u)
{
@ -1090,11 +1139,62 @@ std::vector<std::pair<u32, u32>> spu_thread::dump_callstack_list() const
return call_stack_list;
}
std::string spu_thread::dump_misc() const
void spu_thread::dump_misc(std::string& ret, std::any& custom_data) const
{
std::string ret = cpu_thread::dump_misc();
cpu_thread::dump_misc(ret, custom_data);
fmt::append(ret, "Block Weight: %u (Retreats: %u)", block_counter, block_failure);
struct dump_misc_data_t
{
u32 cpu_id = umax;
u64 last_read_time = umax;
u64 last_block_counter = umax;
u64 update_count = 0;
std::pair<u64, u64> update(u64 current_block_counter, u64 current_timestamp = get_system_time())
{
const u64 diff_time = current_timestamp <= last_read_time ? 0 : current_timestamp - last_read_time;
const u64 diff_block = current_block_counter <= last_block_counter ? 0 : current_block_counter - last_block_counter;
if (last_read_time == umax || update_count >= 1000)
{
last_read_time = current_timestamp;
last_block_counter = current_block_counter;
update_count = 0;
}
else if (diff_time >= 100000 && diff_block >= 100)
{
// Update values to measure rate (but not fully so rate can be measured later)
last_read_time += diff_time / 10 * 9;
last_block_counter += diff_block / 10 * 9;
update_count++;
}
return {diff_time, diff_block};
}
};
dump_misc_data_t* func_data = std::any_cast<dump_misc_data_t>(&custom_data);
if (!func_data)
{
custom_data.reset();
custom_data = std::make_any<dump_misc_data_t>();
func_data = ensure(std::any_cast<dump_misc_data_t>(&custom_data));
}
if (func_data->cpu_id != this->id)
{
*func_data = {};
func_data->cpu_id = this->id;
}
const u64 current_block_counter = atomic_storage<u64>::load(block_counter);
const auto [diff_time, diff_block] = func_data->update(current_block_counter);
const u64 rate_of_diff = diff_block ? std::max<u64>(1, utils::rational_mul<u64>(diff_block, 1'000'000, std::max<u64>(diff_time, 1))) : 0;
fmt::append(ret, "Block Weight: log10(%u/second): %.1f (Retreats: %u)", rate_of_diff, std::log10(std::max<u64>(rate_of_diff, 10)), block_failure);
if (u64 hash = atomic_storage<u64>::load(block_hash))
{
@ -1145,8 +1245,6 @@ std::string spu_thread::dump_misc() const
break;
}
}
return ret;
}
void spu_thread::cpu_on_stop()
@ -3761,7 +3859,7 @@ bool spu_thread::is_exec_code(u32 addr, std::span<const u8> ls_ptr, u32 base_add
const u32 addr0 = spu_branch_target(addr);
const spu_opcode_t op{read_from_ptr<be_t<u32>>(ls_ptr, addr0 - base_addr)};
const auto type = s_spu_itype.decode(op.opcode);
const auto type = g_spu_itype.decode(op.opcode);
if (type == spu_itype::UNK || !op.opcode)
{
@ -3907,7 +4005,7 @@ bool spu_thread::is_exec_code(u32 addr, std::span<const u8> ls_ptr, u32 base_add
// Test the validity of a single instruction of the optional target
// This function can't be too slow and is unlikely to improve results by a great deal
const u32 op0 = read_from_ptr<be_t<u32>>(ls_ptr, route_pc - base_addr);
const spu_itype::type type0 = s_spu_itype.decode(op0);
const spu_itype::type type0 = g_spu_itype.decode(op0);
if (type0 == spu_itype::UNK || !op0)
{
@ -6878,7 +6976,7 @@ spu_exec_object spu_thread::capture_memory_as_elf(std::span<spu_memory_segment_d
const u32 op = read_from_ptr<be_t<u32>>(all_data, pc0 - 4);
// Try to find function entry (if they are placed sequentially search for BI $LR of previous function)
if (!op || op == 0x35000000u || s_spu_itype.decode(op) == spu_itype::UNK)
if (!op || op == 0x35000000u || g_spu_itype.decode(op) == spu_itype::UNK)
{
if (is_exec_code(pc0, { all_data.data(), SPU_LS_SIZE }))
break;

View File

@ -630,7 +630,7 @@ public:
virtual void dump_regs(std::string&, std::any& custom_data) const override;
virtual std::string dump_callstack() const override;
virtual std::vector<std::pair<u32, u32>> dump_callstack_list() const override;
virtual std::string dump_misc() const override;
virtual void dump_misc(std::string& ret, std::any& custom_data) const override;
virtual void cpu_task() override final;
virtual void cpu_on_stop() override;
virtual void cpu_return() override;

View File

@ -140,6 +140,32 @@ bool verify_mself(const fs::file& mself_file)
return true;
}
// TODO: May not be thread-safe (or even, process-safe)
bool has_non_directory_components(std::string_view path)
{
std::string path0{path};
while (true)
{
const std::string sub_path = fs::get_parent_dir(path0);
if (sub_path.size() >= path0.size())
{
break;
}
fs::stat_t stat{};
if (fs::get_stat(sub_path, stat))
{
return !stat.is_directory;
}
path0 = std::move(sub_path);
}
return false;
}
lv2_fs_mount_info_map::lv2_fs_mount_info_map()
{
for (auto mp = &g_mp_sys_dev_root; mp; mp = mp->next) // Scan and keep track of pre-mounted devices
@ -899,11 +925,18 @@ lv2_file::open_raw_result_t lv2_file::open_raw(const std::string& local_path, s3
switch (auto error = fs::g_tls_error)
{
case fs::error::notdir: return {CELL_ENOTDIR};
case fs::error::noent: return {CELL_ENOENT};
default: sys_fs.error("lv2_file::open(): unknown error %s", error); break;
}
default:
{
if (has_non_directory_components(local_path))
{
return {CELL_ENOTDIR};
}
return {CELL_EIO};
fmt::throw_exception("unknown error %s", error);
}
}
}
if (flags & CELL_FS_O_MSELF && !verify_mself(file))
@ -1374,8 +1407,12 @@ error_code sys_fs_opendir(ppu_thread& ppu, vm::cptr<char> path, vm::ptr<u32> fd)
}
default:
{
sys_fs.error("sys_fs_opendir(): unknown error %s", error);
return {CELL_EIO, path};
if (has_non_directory_components(local_path))
{
return { CELL_ENOTDIR, path };
}
fmt::throw_exception("unknown error %s", error);
}
}
}
@ -1558,6 +1595,10 @@ error_code sys_fs_stat(ppu_thread& ppu, vm::cptr<char> path, vm::ptr<CellFsStat>
{
switch (auto error = fs::g_tls_error)
{
case fs::error::notdir:
{
return { CELL_ENOTDIR, path};
}
case fs::error::noent:
{
// Try to analyse split file (TODO)
@ -1597,8 +1638,12 @@ error_code sys_fs_stat(ppu_thread& ppu, vm::cptr<char> path, vm::ptr<CellFsStat>
}
default:
{
sys_fs.error("sys_fs_stat(): unknown error %s", error);
return {CELL_EIO, path};
if (has_non_directory_components(local_path))
{
return { CELL_ENOTDIR, path };
}
fmt::throw_exception("unknown error %s", error);
}
}
}
@ -1724,6 +1769,10 @@ error_code sys_fs_mkdir(ppu_thread& ppu, vm::cptr<char> path, s32 mode)
{
switch (auto error = fs::g_tls_error)
{
case fs::error::notdir:
{
return { CELL_ENOTDIR, path};
}
case fs::error::noent:
{
return {mp == &g_mp_sys_dev_hdd1 ? sys_fs.warning : sys_fs.error, CELL_ENOENT, path};
@ -1732,10 +1781,16 @@ error_code sys_fs_mkdir(ppu_thread& ppu, vm::cptr<char> path, s32 mode)
{
return {sys_fs.warning, CELL_EEXIST, path};
}
default: sys_fs.error("sys_fs_mkdir(): unknown error %s", error);
}
default:
{
if (has_non_directory_components(local_path))
{
return { CELL_ENOTDIR, path };
}
return {CELL_EIO, path}; // ???
fmt::throw_exception("unknown error %s", error);
}
}
}
sys_fs.notice("sys_fs_mkdir(): directory %s created", path);
@ -1795,12 +1850,19 @@ error_code sys_fs_rename(ppu_thread& ppu, vm::cptr<char> from, vm::cptr<char> to
{
switch (auto error = fs::g_tls_error)
{
case fs::error::notdir: return {CELL_ENOTDIR, from};
case fs::error::noent: return {CELL_ENOENT, from};
case fs::error::exist: return {CELL_EEXIST, to};
default: sys_fs.error("sys_fs_rename(): unknown error %s", error);
}
default:
{
if (has_non_directory_components(local_from))
{
return {CELL_ENOTDIR, from};
}
return {CELL_EIO, from}; // ???
fmt::throw_exception("unknown error %s", error);
}
}
}
sys_fs.notice("sys_fs_rename(): %s renamed to %s", from, to);
@ -1850,12 +1912,19 @@ error_code sys_fs_rmdir(ppu_thread& ppu, vm::cptr<char> path)
{
switch (auto error = fs::g_tls_error)
{
case fs::error::notdir: return {CELL_ENOTDIR, path};
case fs::error::noent: return {CELL_ENOENT, path};
case fs::error::notempty: return {CELL_ENOTEMPTY, path};
default: sys_fs.error("sys_fs_rmdir(): unknown error %s", error);
}
default:
{
if (has_non_directory_components(local_path))
{
return { CELL_ENOTDIR, path };
}
return {CELL_EIO, path}; // ???
fmt::throw_exception("unknown error %s", error);
}
}
}
sys_fs.notice("sys_fs_rmdir(): directory %s removed", path);
@ -1906,14 +1975,24 @@ error_code sys_fs_unlink(ppu_thread& ppu, vm::cptr<char> path)
{
switch (auto error = fs::g_tls_error)
{
case fs::error::notdir:
{
return { CELL_ENOTDIR, path };
}
case fs::error::noent:
{
return {mp == &g_mp_sys_dev_hdd1 ? sys_fs.warning : sys_fs.error, CELL_ENOENT, path};
}
default: sys_fs.error("sys_fs_unlink(): unknown error %s", error);
}
default:
{
if (has_non_directory_components(local_path))
{
return { CELL_ENOTDIR, path };
}
return {CELL_EIO, path}; // ???
fmt::throw_exception("unknown error %s", error);
}
}
}
sys_fs.notice("sys_fs_unlink(): file %s deleted", path);
@ -2632,10 +2711,8 @@ error_code sys_fs_lseek(ppu_thread& ppu, u32 fd, s64 offset, s32 whence, vm::ptr
switch (auto error = fs::g_tls_error)
{
case fs::error::inval: return {CELL_EINVAL, "fd=%u, offset=0x%x, whence=%d", fd, offset, whence};
default: sys_fs.error("sys_fs_lseek(): unknown error %s", error);
default: fmt::throw_exception("unknown error %s", error);
}
return CELL_EIO; // ???
}
lock.unlock();
@ -2751,10 +2828,16 @@ error_code sys_fs_get_block_size(ppu_thread& ppu, vm::cptr<char> path, vm::ptr<u
{
case fs::error::exist: return {CELL_EISDIR, path};
case fs::error::noent: return {CELL_ENOENT, path};
default: sys_fs.error("sys_fs_get_block_size(): unknown error %s", error);
}
default:
{
if (has_non_directory_components(local_path))
{
return { CELL_ENOTDIR, path };
}
return {CELL_EIO, path}; // ???
fmt::throw_exception("unknown error %s", error);
}
}
}
static_cast<void>(ppu.test_stopped());
@ -2805,14 +2888,24 @@ error_code sys_fs_truncate(ppu_thread& ppu, vm::cptr<char> path, u64 size)
{
switch (auto error = fs::g_tls_error)
{
case fs::error::notdir:
{
return { CELL_ENOTDIR, path};
}
case fs::error::noent:
{
return {mp == &g_mp_sys_dev_hdd1 ? sys_fs.warning : sys_fs.error, CELL_ENOENT, path};
}
default: sys_fs.error("sys_fs_truncate(): unknown error %s", error);
}
default:
{
if (has_non_directory_components(local_path))
{
return { CELL_ENOTDIR, path };
}
return {CELL_EIO, path}; // ???
fmt::throw_exception("unknown error %s", error);
}
}
}
return CELL_OK;
@ -2858,10 +2951,11 @@ error_code sys_fs_ftruncate(ppu_thread& ppu, u32 fd, u64 size)
switch (auto error = fs::g_tls_error)
{
case fs::error::ok:
default: sys_fs.error("sys_fs_ftruncate(): unknown error %s", error);
default:
{
fmt::throw_exception("unknown error %s", error);
}
}
return CELL_EIO; // ???
}
return CELL_OK;
@ -2907,6 +3001,10 @@ error_code sys_fs_chmod(ppu_thread&, vm::cptr<char> path, s32 mode)
{
switch (auto error = fs::g_tls_error)
{
case fs::error::notdir:
{
return { CELL_ENOTDIR, path};
}
case fs::error::noent:
{
// Try to locate split files
@ -2920,8 +3018,12 @@ error_code sys_fs_chmod(ppu_thread&, vm::cptr<char> path, s32 mode)
}
default:
{
sys_fs.error("sys_fs_chmod(): unknown error %s", error);
return {CELL_EIO, path};
if (has_non_directory_components(local_path))
{
return { CELL_ENOTDIR, path };
}
fmt::throw_exception("unknown error %s", error);
}
}
}
@ -3053,14 +3155,24 @@ error_code sys_fs_utime(ppu_thread& ppu, vm::cptr<char> path, vm::cptr<CellFsUti
{
switch (auto error = fs::g_tls_error)
{
case fs::error::notdir:
{
return { CELL_ENOTDIR, path};
}
case fs::error::noent:
{
return {mp == &g_mp_sys_dev_hdd1 ? sys_fs.warning : sys_fs.error, CELL_ENOENT, path};
}
default: sys_fs.error("sys_fs_utime(): unknown error %s", error);
}
default:
{
if (has_non_directory_components(local_path))
{
return { CELL_ENOTDIR, path };
}
return {CELL_EIO, path}; // ???
fmt::throw_exception("unknown error %s", error);
}
}
}
return CELL_OK;

View File

@ -63,8 +63,8 @@ extern const std::map<std::string_view, int> g_prx_list
{ "libcelpenc.sprx", 0 },
{ "libddpdec.sprx", 0 },
{ "libdivxdec.sprx", 0 },
{ "libdmux.sprx", 0 },
{ "libdmuxpamf.sprx", 0 },
{ "libdmux.sprx", 1 },
{ "libdmuxpamf.sprx", 1 },
{ "libdtslbrdec.sprx", 0 },
{ "libfiber.sprx", 0 },
{ "libfont.sprx", 0 },

View File

@ -229,6 +229,9 @@ void usb_device_gametablet::interrupt_transfer(u32 buf_size, u8* buf, u32 /*endp
case CELL_PAD_CTRL_LEFT:
left = true;
break;
case CELL_PAD_CTRL_PS:
gt.btn_ps |= 1;
break;
default:
break;
}
@ -249,9 +252,6 @@ void usb_device_gametablet::interrupt_transfer(u32 buf_size, u8* buf, u32 /*endp
case CELL_PAD_CTRL_TRIANGLE:
gt.btn_triangle |= 1;
break;
case CELL_PAD_CTRL_PS:
gt.btn_ps |= 1;
break;
default:
break;
}

View File

@ -13,13 +13,13 @@ struct cfg_recording final : cfg::node
node_video(cfg::node* _this) : cfg::node(_this, "Video") {}
cfg::uint<0, 60> framerate{this, "Framerate", 30};
cfg::uint<0, 7680> width{this, "Width", 1280};
cfg::uint<0, 4320> height{this, "Height", 720};
cfg::uint<640, 7680> width{this, "Width", 1280};
cfg::uint<360, 4320> height{this, "Height", 720};
cfg::uint<0, 192> pixel_format{this, "AVPixelFormat", 0}; // AVPixelFormat::AV_PIX_FMT_YUV420P
cfg::uint<0, 0xFFFF> video_codec{this, "AVCodecID", 12}; // AVCodecID::AV_CODEC_ID_MPEG4
cfg::uint<0, 25000000> video_bps{this, "Video Bitrate", 4000000};
cfg::uint<0, 5> max_b_frames{this, "Max B-Frames", 2};
cfg::uint<0, 20> gop_size{this, "Group of Pictures Size", 12};
cfg::uint<1'000'000, 60'000'000> video_bps{this, "Video Bitrate", 4'000'000};
cfg::uint<0, 3> max_b_frames{this, "Max B-Frames", 2};
cfg::uint<1, 120> gop_size{this, "Group of Pictures Size", 30};
} video{ this };
@ -28,7 +28,7 @@ struct cfg_recording final : cfg::node
node_audio(cfg::node* _this) : cfg::node(_this, "Audio") {}
cfg::uint<0x10000, 0x17000> audio_codec{this, "AVCodecID", 86018}; // AVCodecID::AV_CODEC_ID_AAC
cfg::uint<0, 25000000> audio_bps{this, "Audio Bitrate", 320000};
cfg::uint<64'000, 320'000> audio_bps{this, "Audio Bitrate", 192'000};
} audio{ this };

View File

@ -132,7 +132,7 @@ namespace rsx
free_rsx_memory(Traits::get(sink));
}
Traits::clone_surface(cmd, sink, region.source, new_address, region);
Traits::clone_surface(cmd, sink, region.source, new_address, region, region.source->resolution_scaling_config);
allocate_rsx_memory(Traits::get(sink));
if (invalidated) [[unlikely]]
@ -398,6 +398,7 @@ namespace rsx
surface_antialiasing antialias,
usz width, usz height, usz pitch,
u8 bpp,
const rsx::surface_scaling_config_t& scaling_config,
Args&&... extra_params)
{
surface_storage_type old_surface_storage;
@ -448,7 +449,7 @@ namespace rsx
}
}
if (Traits::surface_matches_properties(surface, format, width, height, antialias))
if (Traits::surface_matches_properties(surface, format, width, height, antialias, scaling_config))
{
if (!pitch_compatible)
{
@ -495,7 +496,7 @@ namespace rsx
for (auto It = invalidated_resources.begin(); It != invalidated_resources.end(); It++)
{
auto &surface = *It;
if (Traits::surface_matches_properties(surface, format, width, height, antialias, true))
if (Traits::surface_matches_properties(surface, format, width, height, antialias, scaling_config, true))
{
new_surface_storage = std::move(surface);
Traits::notify_surface_reused(new_surface_storage);
@ -531,7 +532,7 @@ namespace rsx
if (!new_surface)
{
ensure(store);
new_surface_storage = Traits::create_new_surface(address, format, width, height, pitch, antialias, std::forward<Args>(extra_params)...);
new_surface_storage = Traits::create_new_surface(address, format, width, height, pitch, antialias, scaling_config, std::forward<Args>(extra_params)...);
new_surface = Traits::get(new_surface_storage);
Traits::prepare_surface_for_drawing(command_list, new_surface);
allocate_rsx_memory(new_surface);
@ -842,11 +843,13 @@ namespace rsx
surface_color_format color_format,
surface_antialiasing antialias,
usz width, usz height, usz pitch,
const rsx::surface_scaling_config_t& scaling_config,
Args&&... extra_params)
{
return bind_surface_address<false>(
command_list, address, color_format, antialias,
width, height, pitch, get_format_block_size_in_bytes(color_format),
scaling_config,
std::forward<Args>(extra_params)...);
}
@ -857,12 +860,14 @@ namespace rsx
surface_depth_format2 depth_format,
surface_antialiasing antialias,
usz width, usz height, usz pitch,
const rsx::surface_scaling_config_t& scaling_config,
Args&&... extra_params)
{
return bind_surface_address<true>(
command_list, address, depth_format, antialias,
width, height, pitch,
get_format_block_size_in_bytes(depth_format),
scaling_config,
std::forward<Args>(extra_params)...);
}
@ -969,6 +974,7 @@ namespace rsx
surface_raster_type raster_type,
const std::array<u32, 4> &surface_addresses, u32 address_z,
const std::array<u32, 4> &surface_pitch, u32 zeta_pitch,
const rsx::surface_scaling_config_t& scaling_config,
Args&&... extra_params)
{
u32 clip_width = clip_horizontal_reg;
@ -998,7 +1004,7 @@ namespace rsx
m_bound_render_targets[surface_index] = std::make_pair(surface_addresses[surface_index],
bind_address_as_render_targets(command_list, surface_addresses[surface_index], color_format, antialias,
clip_width, clip_height, surface_pitch[surface_index], std::forward<Args>(extra_params)...));
clip_width, clip_height, surface_pitch[surface_index], scaling_config, std::forward<Args>(extra_params)...));
m_bound_render_target_ids.push_back(surface_index);
}
@ -1014,7 +1020,7 @@ namespace rsx
{
m_bound_depth_stencil = std::make_pair(address_z,
bind_address_as_depth_stencil(command_list, address_z, depth_format, antialias,
clip_width, clip_height, zeta_pitch, std::forward<Args>(extra_params)...));
clip_width, clip_height, zeta_pitch, scaling_config, std::forward<Args>(extra_params)...));
}
else
{
@ -1463,5 +1469,113 @@ namespace rsx
}
}
}
void sync_scaling_config(command_list_type cmd, const rsx::surface_scaling_config_t& active_config)
{
auto process_list_function = [&](surface_ranged_map& data, const utils::address_range32& range)
{
std::vector<surface_type> surfaces_to_clone;
for (auto It = data.begin_range(range); It != data.end();)
{
auto surface = Traits::get(It->second);
if (surface->get_resolution_scaling_config() == active_config)
{
++It;
continue;
}
// Perform a test scaling and check if anything is different after scaling
// There are many cases where this will avoid creating new surfaces
const auto [new_w, new_h] = rsx::apply_resolution_scale<true>(
active_config,
surface->template get_surface_width<>(),
surface->template get_surface_height<>());
if (new_w == surface->width() && new_h == surface->height())
{
// Not affected by resolution scale. Just update the details and move on.
surface->resolution_scaling_config = active_config;
++It;
continue;
}
surfaces_to_clone.push_back(surface);
// Invalidate the previous surface
invalidate(It->second);
It = data.erase(It);
}
for (auto& surface : surfaces_to_clone)
{
// Enqueue the memory transfer
surface_storage_type sink{};
deferred_clipped_region<surface_type> copy{};
copy.width = surface->template get_surface_width<>();
copy.height = surface->template get_surface_height<>();
copy.transfer_scale_x = 1.f;
copy.transfer_scale_y = 1.f;
copy.target = nullptr;
copy.source = surface;
Traits::clone_surface(cmd, sink, surface, surface->base_addr, copy, active_config);
allocate_rsx_memory(Traits::get(sink));
// Replace with the new one
auto new_surface = Traits::get(sink);
ensure(copy.target == new_surface);
data.emplace(surface->get_memory_range(), std::move(sink));
// Force barrier to reduce VRAM pressure
new_surface->memory_barrier(cmd, rsx::surface_access::memory_read);
}
};
const auto rtt_bind_backup = m_bound_render_targets;
const auto dsv_bind_backup = m_bound_depth_stencil;
// Unbind everything. We'll restore it later
for (auto& rtt_bind : m_bound_render_targets)
{
rtt_bind = {};
}
m_bound_depth_stencil = {};
process_list_function(m_render_targets_storage, m_render_targets_memory_range);
process_list_function(m_depth_stencil_storage, m_depth_stencil_memory_range);
// Restore bindings.
for (int i = 0; i < 4; ++i)
{
const auto address = rtt_bind_backup[i].first;
if (!address)
{
continue;
}
auto rtt = m_render_targets_storage.find(address);
ensure(rtt != m_render_targets_storage.end());
m_bound_render_targets[i] =
{
address,
Traits::get(rtt->second)
};
}
if (const auto ds_address = dsv_bind_backup.first)
{
auto ds = m_depth_stencil_storage.find(ds_address);
ensure(ds != m_depth_stencil_storage.end());
m_bound_depth_stencil =
{
ds_address,
Traits::get(ds->second)
};
}
}
};
}

View File

@ -88,18 +88,18 @@ namespace rsx
auto dst_h = std::get<3>(region);
// Apply resolution scale if needed
if (g_cfg.video.resolution_scale_percent != 100)
{
auto src = static_cast<T>(source);
auto src = static_cast<T>(source);
std::tie(src_w, src_h) = rsx::apply_resolution_scale<true>(
src->resolution_scaling_config,
src_w, src_h,
src->template get_surface_width<rsx::surface_metrics::pixels>(),
src->template get_surface_height<rsx::surface_metrics::pixels>());
std::tie(src_w, src_h) = rsx::apply_resolution_scale<true>(src_w, src_h,
src->template get_surface_width<rsx::surface_metrics::pixels>(),
src->template get_surface_height<rsx::surface_metrics::pixels>());
std::tie(dst_w, dst_h) = rsx::apply_resolution_scale<true>(dst_w, dst_h,
target_surface->template get_surface_width<rsx::surface_metrics::pixels>(),
target_surface->template get_surface_height<rsx::surface_metrics::pixels>());
}
std::tie(dst_w, dst_h) = rsx::apply_resolution_scale<true>(
target_surface->resolution_scaling_config,
dst_w, dst_h,
target_surface->template get_surface_width<rsx::surface_metrics::pixels>(),
target_surface->template get_surface_height<rsx::surface_metrics::pixels>());
width = src_w;
height = src_h;
@ -146,6 +146,9 @@ namespace rsx
u8 samples_x = 1;
u8 samples_y = 1;
// Scaling configuration
surface_scaling_config_t resolution_scaling_config;
rsx::address_range32 memory_range;
std::unique_ptr<typename std::remove_pointer_t<image_storage_type>> resolve_surface;
@ -303,6 +306,11 @@ namespace rsx
format_info.gcm_depth_format = format;
}
void set_resolution_scaling_config(const surface_scaling_config_t& config)
{
resolution_scaling_config = config;
}
inline rsx::surface_color_format get_surface_color_format() const
{
return format_info.gcm_color_format;
@ -323,6 +331,11 @@ namespace rsx
);
}
inline const rsx::surface_scaling_config_t& get_resolution_scaling_config() const
{
return resolution_scaling_config;
}
inline bool dirty() const
{
return (state_flags != rsx::surface_state_flags::ready) || !old_contents.empty();
@ -541,10 +554,16 @@ namespace rsx
}
// Apply resolution scale if needed
if (g_cfg.video.resolution_scale_percent != 100)
if (resolution_scaling_config.scale_percent != 100 ||
region.source->resolution_scaling_config.scale_percent != 100)
{
auto [src_width, src_height] = rsx::apply_resolution_scale<true>(slice.width, slice.height, slice.source->width(), slice.source->height());
auto [dst_width, dst_height] = rsx::apply_resolution_scale<true>(slice.width, slice.height, slice.target->width(), slice.target->height());
const auto& src_res_scale = region.source->resolution_scaling_config;
const auto& dst_res_scale = resolution_scaling_config;
const auto src_surface = ensure(dynamic_cast<const render_target_descriptor*>(slice.source));
const auto dst_surface = ensure(dynamic_cast<const render_target_descriptor*>(slice.target));
auto [src_width, src_height] = rsx::apply_resolution_scale<true>(src_res_scale, slice.width, slice.height, src_surface->get_surface_width(), src_surface->get_surface_height());
auto [dst_width, dst_height] = rsx::apply_resolution_scale<true>(dst_res_scale, slice.width, slice.height, dst_surface->get_surface_width(), dst_surface->get_surface_height());
slice.transfer_scale_x *= f32(dst_width) / src_width;
slice.transfer_scale_y *= f32(dst_height) / src_height;
@ -552,8 +571,8 @@ namespace rsx
slice.width = src_width;
slice.height = src_height;
std::tie(slice.src_x, slice.src_y) = rsx::apply_resolution_scale<false>(slice.src_x, slice.src_y, slice.source->width(), slice.source->height());
std::tie(slice.dst_x, slice.dst_y) = rsx::apply_resolution_scale<false>(slice.dst_x, slice.dst_y, slice.target->width(), slice.target->height());
std::tie(slice.src_x, slice.src_y) = rsx::apply_resolution_scale<false>(src_res_scale, slice.src_x, slice.src_y, src_surface->get_surface_width(), src_surface->get_surface_height());
std::tie(slice.dst_x, slice.dst_y) = rsx::apply_resolution_scale<false>(dst_res_scale, slice.dst_x, slice.dst_y, dst_surface->get_surface_width(), dst_surface->get_surface_height());
}
}

View File

@ -2413,9 +2413,13 @@ namespace rsx
// 2. The image has to have been generated on the GPU (fbo or blit target only)
rsx::simple_array<copy_region_descriptor> sections;
const bool use_upscaling = (result.upload_context == rsx::texture_upload_context::framebuffer_storage && g_cfg.video.resolution_scale_percent != 100);
const bool use_upscaling = (result.upload_context == rsx::texture_upload_context::framebuffer_storage);
auto to_surface_type = [](const copy_region_descriptor& rgn) -> typename surface_store_type::surface_type
{
return static_cast<typename surface_store_type::surface_type>(rgn.src);
};
if (!helpers::append_mipmap_level(sections, result, attributes, 0, use_upscaling, attributes)) [[unlikely]]
if (!helpers::append_mipmap_level(to_surface_type, sections, result, attributes, 0, use_upscaling, attributes)) [[unlikely]]
{
// Abort if mip0 is not compatible
return result;
@ -2445,7 +2449,7 @@ namespace rsx
options, range, extended_dimension, m_rtts, std::forward<Args>(extras)...);
if (!ret.validate() ||
!helpers::append_mipmap_level(sections, ret, attr2, subsurface, use_upscaling, attributes))
!helpers::append_mipmap_level(to_surface_type, sections, ret, attr2, subsurface, use_upscaling, attributes))
{
// Abort
break;
@ -2778,7 +2782,7 @@ namespace rsx
surf->template get_surface_height<rsx::surface_metrics::pixels>() != surf->height())
{
// Must go through a scaling operation due to resolution scaling being present
ensure(g_cfg.video.resolution_scale_percent != 100);
ensure(src_subres.surface->resolution_scaling_config.scale_percent != 100);
use_null_region = false;
}
}
@ -3389,8 +3393,8 @@ namespace rsx
{
const auto surface_width = src_subres.surface->template get_surface_width<rsx::surface_metrics::pixels>();
const auto surface_height = src_subres.surface->template get_surface_height<rsx::surface_metrics::pixels>();
std::tie(src_area.x1, src_area.y1) = rsx::apply_resolution_scale<false>(src_area.x1, src_area.y1, surface_width, surface_height);
std::tie(src_area.x2, src_area.y2) = rsx::apply_resolution_scale<true>(src_area.x2, src_area.y2, surface_width, surface_height);
std::tie(src_area.x1, src_area.y1) = rsx::apply_resolution_scale<false>(src_subres.surface->resolution_scaling_config, src_area.x1, src_area.y1, surface_width, surface_height);
std::tie(src_area.x2, src_area.y2) = rsx::apply_resolution_scale<true>(src_subres.surface->resolution_scaling_config, src_area.x2, src_area.y2, surface_width, surface_height);
// The resource is of surface type; possibly disabled AA emulation
src_subres.surface->transform_blit_coordinates(rsx::surface_access::transfer_read, src_area);
@ -3400,8 +3404,8 @@ namespace rsx
{
const auto surface_width = dst_subres.surface->template get_surface_width<rsx::surface_metrics::pixels>();
const auto surface_height = dst_subres.surface->template get_surface_height<rsx::surface_metrics::pixels>();
std::tie(dst_area.x1, dst_area.y1) = rsx::apply_resolution_scale<false>(dst_area.x1, dst_area.y1, surface_width, surface_height);
std::tie(dst_area.x2, dst_area.y2) = rsx::apply_resolution_scale<true>(dst_area.x2, dst_area.y2, surface_width, surface_height);
std::tie(dst_area.x1, dst_area.y1) = rsx::apply_resolution_scale<false>(dst_subres.surface->resolution_scaling_config, dst_area.x1, dst_area.y1, surface_width, surface_height);
std::tie(dst_area.x2, dst_area.y2) = rsx::apply_resolution_scale<true>(dst_subres.surface->resolution_scaling_config, dst_area.x2, dst_area.y2, surface_width, surface_height);
// The resource is of surface type; possibly disabled AA emulation
dst_subres.surface->transform_blit_coordinates(rsx::surface_access::transfer_write, dst_area);

View File

@ -357,11 +357,11 @@ namespace rsx
const auto surface_width = section.surface->template get_surface_width<rsx::surface_metrics::pixels>();
const auto surface_height = section.surface->template get_surface_height<rsx::surface_metrics::pixels>();
const auto [src_width, src_height] = rsx::apply_resolution_scale<true>(section.src_area.width, h, surface_width, surface_height);
const auto [dst_width, dst_height] = rsx::apply_resolution_scale<true>(section.dst_area.width, h, attr.width, attr.height);
const auto [src_width, src_height] = rsx::apply_resolution_scale<true>(section.surface->resolution_scaling_config, section.src_area.width, h, surface_width, surface_height);
const auto [dst_width, dst_height] = rsx::apply_resolution_scale<true>(section.surface->resolution_scaling_config, section.dst_area.width, h, attr.width, attr.height);
std::tie(src_x, src_y) = rsx::apply_resolution_scale<false>(src_x, src_y, surface_width, surface_height);
std::tie(dst_x, dst_y) = rsx::apply_resolution_scale<false>(dst_x, dst_y, attr.width, attr.height);
std::tie(src_x, src_y) = rsx::apply_resolution_scale<false>(section.surface->resolution_scaling_config, src_x, src_y, surface_width, surface_height);
std::tie(dst_x, dst_y) = rsx::apply_resolution_scale<false>(section.surface->resolution_scaling_config, dst_x, dst_y, attr.width, attr.height);
section.surface->memory_barrier(cmd, rsx::surface_access::transfer_read);
@ -430,8 +430,10 @@ namespace rsx
if (scaling)
{
// Since output is upscaled, also upscale on dst
const auto [_dst_x, _dst_y] = rsx::apply_resolution_scale<false>(static_cast<u16>(dst_offset.x), static_cast<u16>(dst_y - dst_slice_begin), attr.width, attr.height);
const auto [_dst_w, _dst_h] = rsx::apply_resolution_scale<true>(dst_w, height, attr.width, attr.height);
const auto& scaling_config = rsx::get_current_renderer()->resolution_scaling_config;
const auto [_dst_x, _dst_y] = rsx::apply_resolution_scale<false>(scaling_config, static_cast<u16>(dst_offset.x), static_cast<u16>(dst_y - dst_slice_begin), attr.width, attr.height);
const auto [_dst_w, _dst_h] = rsx::apply_resolution_scale<true>(scaling_config, dst_w, height, attr.width, attr.height);
out.push_back
({
@ -660,10 +662,10 @@ namespace rsx
bool is_depth = texptr->is_depth_surface();
auto attr2 = attr;
if (rsx::get_resolution_scale_percent() != 100)
if (texptr->resolution_scaling_config.scale_percent != 100)
{
const auto [scaled_w, scaled_h] = rsx::apply_resolution_scale<true>(attr.width, attr.height, surface_width, surface_height);
const auto [unused, scaled_slice_h] = rsx::apply_resolution_scale<false>(RSX_SURFACE_DIMENSION_IGNORED, attr.slice_h, surface_width, surface_height);
const auto [scaled_w, scaled_h] = rsx::apply_resolution_scale<true>(texptr->resolution_scaling_config, attr.width, attr.height, surface_width, surface_height);
const auto [unused, scaled_slice_h] = rsx::apply_resolution_scale<false>(texptr->resolution_scaling_config, RSX_SURFACE_DIMENSION_IGNORED, attr.slice_h, surface_width, surface_height);
attr2.width = scaled_w;
attr2.height = scaled_h;
attr2.slice_h = scaled_slice_h;
@ -841,7 +843,8 @@ namespace rsx
}
// If this method was called, there is no easy solution, likely means atlas gather is needed
const auto [scaled_w, scaled_h] = rsx::apply_resolution_scale(attr2.width, attr2.height);
const auto& scaling_config = rsx::get_current_renderer()->resolution_scaling_config;
const auto [scaled_w, scaled_h] = rsx::apply_resolution_scale(scaling_config, attr2.width, attr2.height);
const auto format_class = classify_format(attr2.gcm_format);
const auto upload_context = (fbos.empty()) ? texture_upload_context::shader_read : texture_upload_context::framebuffer_storage;
@ -892,14 +895,15 @@ namespace rsx
return result;
}
template<typename sampled_image_descriptor, typename copy_region_descriptor_type>
template<typename to_surface_type_converter, typename sampled_image_descriptor, typename copy_region_descriptor_type>
bool append_mipmap_level(
to_surface_type_converter&& as_surface_type, // Cast function to surface type
rsx::simple_array<copy_region_descriptor_type>& sections, // Destination list
const sampled_image_descriptor& level, // Descriptor for the image level being checked
const image_section_attributes_t& attr, // Attributes of image level
u8 mipmap_level, // Level index
bool apply_upscaling, // Whether to upscale the results or not
const image_section_attributes_t& level0_attr) // Attributes of the first mipmap level
const sampled_image_descriptor& level, // Descriptor for the image level being checked
const image_section_attributes_t& attr, // Attributes of image level
u8 mipmap_level, // Level index
bool apply_upscaling, // Whether to upscale the results or not
const image_section_attributes_t& level0_attr) // Attributes of the first mipmap level
{
if (level.image_handle)
{
@ -916,7 +920,8 @@ namespace rsx
// Calculate transfer dimensions from attr
if (level.upload_context == rsx::texture_upload_context::framebuffer_storage) [[likely]]
{
std::tie(mip.src_w, mip.src_h) = rsx::apply_resolution_scale<true>(attr.width, attr.height);
auto rtv = as_surface_type(mip);
std::tie(mip.src_w, mip.src_h) = rsx::apply_resolution_scale<true>(rtv->resolution_scaling_config, attr.width, attr.height);
}
else
{
@ -964,7 +969,9 @@ namespace rsx
if (apply_upscaling)
{
auto& mip = sections.back();
std::tie(mip.dst_w, mip.dst_h) = rsx::apply_resolution_scale<true>(mip.dst_w, mip.dst_h, level0_attr.width, level0_attr.height);
std::tie(mip.dst_w, mip.dst_h) = rsx::apply_resolution_scale<true>(
as_surface_type(mip)->resolution_scaling_config,
mip.dst_w, mip.dst_h, level0_attr.width, level0_attr.height);
}
return true;

View File

@ -49,7 +49,7 @@ namespace rsx
}
}
std::string framebuffer_statistics_t::to_string(bool squash) const
std::string framebuffer_statistics_t::to_string(const surface_scaling_config_t& scaling_config, bool squash) const
{
// Format is sorted by sample count
struct sorted_message_t
@ -70,7 +70,7 @@ namespace rsx
for (const auto& [aa_mode, stat] : data)
{
auto real_stat = stat;
std::tie(real_stat.width, real_stat.height) = apply_resolution_scale(stat.width, stat.height);
std::tie(real_stat.width, real_stat.height) = apply_resolution_scale(scaling_config, stat.width, stat.height);
real_stats.push_back(real_stat);
sorted_message_t msg;

View File

@ -12,6 +12,8 @@ namespace rsx
{
enum class surface_antialiasing : u8;
struct surface_scaling_config_t;
struct framebuffer_dimensions_t
{
u16 width;
@ -42,7 +44,7 @@ namespace rsx
void add(u16 width, u16 height, rsx::surface_antialiasing aa);
// Returns a formatted string representing the statistics collected over the frame.
std::string to_string(bool squash) const;
std::string to_string(const surface_scaling_config_t& scaling_config, bool squash) const;
};
struct frame_statistics_t

View File

@ -699,7 +699,9 @@ namespace rsx
const auto window_origin = REGS(m_ctx)->shader_window_origin();
const u32 window_height = REGS(m_ctx)->shader_window_height();
const auto pixel_center = REGS(m_ctx)->pixel_center();
const f32 resolution_scale = (window_height <= static_cast<u32>(g_cfg.video.min_scalable_dimension)) ? 1.f : rsx::get_resolution_scale();
const f32 resolution_scale = (window_height <= RSX(m_ctx)->resolution_scaling_config.min_scalable_dimension)
? 1.f
: RSX(m_ctx)->resolution_scaling_config.scale_factor();
payload.wpos_scale = (window_origin == rsx::window_origin::top) ? (1.f / resolution_scale) : (-1.f / resolution_scale);
payload.wpos_bias[0] = 0.f;

View File

@ -227,7 +227,7 @@ void GLGSRender::update_draw_state()
case rsx::primitive_type::lines:
case rsx::primitive_type::line_loop:
case rsx::primitive_type::line_strip:
gl_state.line_width(rsx::method_registers.line_width() * rsx::get_resolution_scale());
gl_state.line_width(rsx::method_registers.line_width() * resolution_scaling_config.scale_factor());
gl_state.enable(rsx::method_registers.line_smooth_enabled(), GL_LINE_SMOOTH);
break;
default:

View File

@ -73,6 +73,7 @@ void GLGSRender::set_viewport()
{
// NOTE: scale offset matrix already contains the viewport transformation
const auto [clip_width, clip_height] = rsx::apply_resolution_scale<true>(
resolution_scaling_config,
rsx::method_registers.surface_clip_width(), rsx::method_registers.surface_clip_height());
glViewport(0, 0, clip_width, clip_height);
@ -936,7 +937,7 @@ void GLGSRender::load_program_env()
m_draw_processor.fill_scale_offset_data(buf, false);
m_draw_processor.fill_user_clip_data(buf + 64);
*(reinterpret_cast<u32*>(buf + 68)) = rsx::method_registers.transform_branch_bits();
*(reinterpret_cast<f32*>(buf + 72)) = rsx::method_registers.point_size() * rsx::get_resolution_scale();
*(reinterpret_cast<f32*>(buf + 72)) = rsx::method_registers.point_size() * resolution_scaling_config.scale_factor();
*(reinterpret_cast<f32*>(buf + 76)) = rsx::method_registers.clip_min();
*(reinterpret_cast<f32*>(buf + 80)) = rsx::method_registers.clip_max();

View File

@ -399,8 +399,15 @@ namespace gl
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<f32>(viewport.width()), static_cast<f32>(viewport.height()), static_cast<f32>(viewport.x1), static_cast<f32>(viewport.y1));
program_handle.uniforms["ui_scale"] = color4f(static_cast<f32>(ui.virtual_width), static_cast<f32>(ui.virtual_height), 1.f, 1.f);
ui.set_render_viewport(
static_cast<u16>(std::min<u32>(viewport.width(), std::numeric_limits<u16>::max())),
static_cast<u16>(std::min<u32>(viewport.height(), std::numeric_limits<u16>::max()))
);
const auto ui_scale = color4f(static_cast<f32>(ui.virtual_width), static_cast<f32>(ui.virtual_height), 1.f, 1.f);
const auto ui_viewport = color4f(static_cast<f32>(viewport.width()), static_cast<f32>(viewport.height()), static_cast<f32>(viewport.x1), static_cast<f32>(viewport.y1));
program_handle.uniforms["viewport"] = ui_viewport;
program_handle.uniforms["ui_scale"] = ui_scale;
saved_sampler_state save_30(30, m_sampler);
saved_sampler_state save_31(31, m_sampler);
@ -458,12 +465,24 @@ namespace gl
.texture_mode(texture_mode)
.clip_fragments(cmd.config.clip_region)
.pulse_glow(cmd.config.pulse_glow)
.set_sdf(cmd.config.sdf_config.func)
.get();
program_handle.uniforms["timestamp"] = cmd.config.get_sinus_value();
program_handle.uniforms["albedo"] = cmd.config.color;
program_handle.uniforms["clip_bounds"] = cmd.config.clip_rect;
program_handle.uniforms["blur_intensity"] = static_cast<f32>(cmd.config.blur_strength);
if (cmd.config.sdf_config.func != rsx::overlays::sdf_function::none)
{
auto sdf_config = cmd.config.sdf_config;
sdf_config.transform(static_cast<areaf>(viewport).flipped_vertical(), {ui_scale.x, ui_scale.y});
program_handle.uniforms["sdf_params"] = color4f(sdf_config.hx, sdf_config.hy, sdf_config.br, sdf_config.bw);
program_handle.uniforms["sdf_origin"] = color2f(sdf_config.cx, sdf_config.cy);
program_handle.uniforms["sdf_border_color"] = sdf_config.border_color;
}
overlay_pass::run(cmd_, viewport, target, gl::image_aspect::color, true);
}

View File

@ -95,6 +95,7 @@ gl::texture* GLGSRender::get_present_source(gl::present_surface_info* info, cons
image = section.surface->get_surface(rsx::surface_access::transfer_read);
std::tie(info->width, info->height) = rsx::apply_resolution_scale<true>(
resolution_scaling_config,
std::min(surface_width, info->width),
std::min(surface_height, info->height));
}
@ -225,7 +226,7 @@ void GLGSRender::flip(const rsx::display_flip_info_t& info)
if (avconfig.stereo_enabled) [[unlikely]]
{
const auto [unused, min_expected_height] = rsx::apply_resolution_scale<true>(RSX_SURFACE_DIMENSION_IGNORED, buffer_height + 30);
const auto [unused, min_expected_height] = rsx::apply_resolution_scale<true>(resolution_scaling_config, RSX_SURFACE_DIMENSION_IGNORED, buffer_height + 30);
if (image_to_flip->height() < min_expected_height)
{
// Get image for second eye
@ -240,7 +241,7 @@ void GLGSRender::flip(const rsx::display_flip_info_t& info)
else
{
// Account for possible insets
const auto [unused2, scaled_buffer_height] = rsx::apply_resolution_scale<true>(RSX_SURFACE_DIMENSION_IGNORED, buffer_height);
const auto [unused2, scaled_buffer_height] = rsx::apply_resolution_scale<true>(resolution_scaling_config, RSX_SURFACE_DIMENSION_IGNORED, buffer_height);
buffer_height = std::min<u32>(image_to_flip->height() - min_expected_height, scaled_buffer_height);
}
}
@ -316,9 +317,11 @@ void GLGSRender::flip(const rsx::display_flip_info_t& info)
// Lock to avoid modification during run-update chain
std::lock_guard lock(*m_overlay_manager);
const areau display_area = {0, 0, static_cast<u32>(m_frame->client_width()), static_cast<u32>(m_frame->client_height())};
for (const auto& view : m_overlay_manager->get_views())
{
m_ui_renderer.run(cmd, aspect_ratio, target, *view.get(), flip_vertically);
const areau render_area = view->use_window_space ? display_area : aspect_ratio;
m_ui_renderer.run(cmd, render_area, target, *view.get(), flip_vertically);
}
}
};
@ -477,7 +480,7 @@ void GLGSRender::flip(const rsx::display_flip_info_t& info)
"Texture uploads: %11u (%u from CPU - %02u%%, %u copies avoided)\n"
"Vertex cache hits: %9u/%u (%u%%)\n"
"Program cache lookup ellision: %u/%u (%u%%)",
info.stats.framebuffer_stats.to_string(!backend_config.supports_hw_msaa),
info.stats.framebuffer_stats.to_string(resolution_scaling_config, !backend_config.supports_hw_msaa),
get_load(), info.stats.draw_calls, info.stats.setup_time, info.stats.vertex_upload_time,
info.stats.textures_upload_time, info.stats.draw_exec_time, num_dirty_textures, texture_memory_size,
num_flushes, num_misses, cache_miss_ratio, num_unavoidable, num_mispredict, num_speculate,
@ -514,6 +517,19 @@ void GLGSRender::flip(const rsx::display_flip_info_t& info)
m_frame->flip(m_context);
rsx::thread::flip(info);
// Data sync
const rsx::surface_scaling_config_t active_res_scaling_config =
{
.scale_percent = static_cast<u16>(g_cfg.video.resolution_scale_percent),
.min_scalable_dimension = static_cast<u16>(g_cfg.video.min_scalable_dimension),
};
if (active_res_scaling_config != this->resolution_scaling_config)
{
m_rtts.sync_scaling_config(cmd, active_res_scaling_config);
this->resolution_scaling_config = active_res_scaling_config;
}
// Cleanup
m_gl_texture_cache.on_frame_end();
m_vertex_cache->purge();

View File

@ -141,7 +141,8 @@ void GLGSRender::init_buffers(rsx::framebuffer_creation_context context, bool /*
m_framebuffer_layout.width, m_framebuffer_layout.height,
m_framebuffer_layout.target, m_framebuffer_layout.aa_mode, m_framebuffer_layout.raster_type,
m_framebuffer_layout.color_addresses, m_framebuffer_layout.zeta_address,
m_framebuffer_layout.actual_color_pitch, m_framebuffer_layout.actual_zeta_pitch);
m_framebuffer_layout.actual_color_pitch, m_framebuffer_layout.actual_zeta_pitch,
resolution_scaling_config);
std::array<GLuint, 4> color_targets;
GLuint depth_stencil_target;
@ -448,7 +449,7 @@ void gl::render_target::load_memory(gl::command_context& cmd)
subres.data = { vm::get_super_ptr<const std::byte>(base_addr), static_cast<std::span<const std::byte>::size_type>(rsx_pitch * surface_height * samples_y) };
// TODO: MSAA support
if (g_cfg.video.resolution_scale_percent == 100 && spp == 1) [[likely]]
if (resolution_scaling_config.scale_percent == 100 && spp == 1) [[likely]]
{
gl::upload_texture(cmd, this, get_gcm_format(), is_swizzled, { subres });
}

View File

@ -98,7 +98,7 @@ namespace gl
bool matches_dimensions(u16 _width, u16 _height) const
{
//Use forward scaling to account for rounding and clamping errors
const auto [scaled_w, scaled_h] = rsx::apply_resolution_scale<true>(_width, _height);
const auto [scaled_w, scaled_h] = rsx::apply_resolution_scale<true>(resolution_scaling_config, _width, _height);
return (scaled_w == width()) && (scaled_h == height());
}
@ -138,11 +138,12 @@ struct gl_render_target_traits
u32 address,
rsx::surface_color_format surface_color_format,
usz width, usz height, usz pitch,
rsx::surface_antialiasing antialias
rsx::surface_antialiasing antialias,
const rsx::surface_scaling_config_t& resolution_scaling_config
)
{
auto format = rsx::internals::surface_color_format_to_gl(surface_color_format);
const auto [width_, height_] = rsx::apply_resolution_scale<true>(static_cast<u16>(width), static_cast<u16>(height));
const auto [width_, height_] = rsx::apply_resolution_scale<true>(resolution_scaling_config, static_cast<u16>(width), static_cast<u16>(height));
u8 samples;
rsx::surface_sample_layout sample_layout;
@ -162,6 +163,7 @@ struct gl_render_target_traits
result->set_name(fmt::format("RTV_%u@0x%x", result->id(), address));
result->set_aa_mode(antialias);
result->set_resolution_scaling_config(resolution_scaling_config);
result->set_native_pitch(static_cast<u32>(width) * get_format_block_size_in_bytes(surface_color_format) * result->samples_x);
result->set_surface_dimensions(static_cast<u16>(width), static_cast<u16>(height), static_cast<u32>(pitch));
result->set_format(surface_color_format);
@ -182,11 +184,12 @@ struct gl_render_target_traits
u32 address,
rsx::surface_depth_format2 surface_depth_format,
usz width, usz height, usz pitch,
rsx::surface_antialiasing antialias
rsx::surface_antialiasing antialias,
const rsx::surface_scaling_config_t& resolution_scaling_config
)
{
auto format = rsx::internals::surface_depth_format_to_gl(surface_depth_format);
const auto [width_, height_] = rsx::apply_resolution_scale<true>(static_cast<u16>(width), static_cast<u16>(height));
const auto [width_, height_] = rsx::apply_resolution_scale<true>(resolution_scaling_config, static_cast<u16>(width), static_cast<u16>(height));
u8 samples;
rsx::surface_sample_layout sample_layout;
@ -206,6 +209,7 @@ struct gl_render_target_traits
result->set_name(fmt::format("DSV_%u@0x%x", result->id(), address));
result->set_aa_mode(antialias);
result->set_resolution_scaling_config(resolution_scaling_config);
result->set_surface_dimensions(static_cast<u16>(width), static_cast<u16>(height), static_cast<u32>(pitch));
result->set_format(surface_depth_format);
result->set_native_pitch(static_cast<u32>(width) * get_format_block_size_in_bytes(surface_depth_format) * result->samples_x);
@ -225,13 +229,17 @@ struct gl_render_target_traits
void clone_surface(
gl::command_context& cmd,
std::unique_ptr<gl::render_target>& sink, gl::render_target* ref,
u32 address, barrier_descriptor_t& prev)
u32 address, barrier_descriptor_t& prev,
const rsx::surface_scaling_config_t& scaling_config)
{
if (!sink)
{
auto internal_format = static_cast<GLenum>(ref->get_internal_format());
const auto [new_w, new_h] = rsx::apply_resolution_scale<true>(prev.width, prev.height,
ref->get_surface_width<rsx::surface_metrics::pixels>(), ref->get_surface_height<rsx::surface_metrics::pixels>());
const auto [new_w, new_h] = rsx::apply_resolution_scale<true>(
scaling_config,
prev.width, prev.height,
ref->get_surface_width<rsx::surface_metrics::pixels>(),
ref->get_surface_height<rsx::surface_metrics::pixels>());
sink = std::make_unique<gl::render_target>(new_w, new_h, ref->samples(), internal_format, ref->format_class());
sink->add_ref();
@ -240,6 +248,9 @@ struct gl_render_target_traits
sink->state_flags = rsx::surface_state_flags::erase_bkgnd;
sink->format_info = ref->format_info;
sink->sample_layout = ref->sample_layout;
sink->resolution_scaling_config = scaling_config;
sink->set_name(fmt::format("SINK_%u@0x%x", sink->id(), address));
sink->set_spp(ref->get_spp());
sink->set_native_pitch(static_cast<u32>(prev.width) * ref->get_bpp() * ref->samples_x);
@ -375,6 +386,7 @@ struct gl_render_target_traits
gl::texture::internal_format format,
usz width, usz height,
rsx::surface_antialiasing antialias,
const rsx::surface_scaling_config_t& scaling_config,
bool check_refs = false)
{
if (check_refs && surface->has_refs())
@ -382,7 +394,8 @@ struct gl_render_target_traits
return surface->get_internal_format() == format &&
surface->get_spp() == get_format_sample_count(antialias) &&
surface->matches_dimensions(static_cast<u16>(width), static_cast<u16>(height));
surface->matches_dimensions(static_cast<u16>(width), static_cast<u16>(height)) &&
surface->resolution_scaling_config == scaling_config;
}
static
@ -391,10 +404,11 @@ struct gl_render_target_traits
rsx::surface_color_format format,
usz width, usz height,
rsx::surface_antialiasing antialias,
const rsx::surface_scaling_config_t& scaling_config,
bool check_refs=false)
{
const auto internal_fmt = rsx::internals::surface_color_format_to_gl(format).internal_format;
return int_surface_matches_properties(surface, internal_fmt, width, height, antialias, check_refs);
return int_surface_matches_properties(surface, internal_fmt, width, height, antialias, scaling_config, check_refs);
}
static
@ -403,10 +417,11 @@ struct gl_render_target_traits
rsx::surface_depth_format2 format,
usz width, usz height,
rsx::surface_antialiasing antialias,
const rsx::surface_scaling_config_t& scaling_config,
bool check_refs = false)
{
const auto internal_fmt = rsx::internals::surface_depth_format_to_gl(format).internal_format;
return int_surface_matches_properties(surface, internal_fmt, width, height, antialias, check_refs);
return int_surface_matches_properties(surface, internal_fmt, width, height, antialias, scaling_config, check_refs);
}
static

View File

@ -717,169 +717,168 @@ namespace gl
}
}
}
return;
}
else
std::pair<void*, u32> upload_scratch_mem = {}, compute_scratch_mem = {};
image_memory_requirements mem_info;
pixel_buffer_layout mem_layout;
std::span<std::byte> dst_buffer = staging_buffer;
u8 block_size_in_bytes = rsx::get_format_block_size_in_bytes(format);
u64 image_linear_size = staging_buffer.size();
const auto min_required_buffer_size = std::max<u64>(utils::align(image_linear_size * 4, 0x100000), 16 * 0x100000);
if (driver_caps.ARB_compute_shader_supported)
{
std::pair<void*, u32> upload_scratch_mem = {}, compute_scratch_mem = {};
image_memory_requirements mem_info;
pixel_buffer_layout mem_layout;
std::span<std::byte> dst_buffer = staging_buffer;
u8 block_size_in_bytes = rsx::get_format_block_size_in_bytes(format);
u64 image_linear_size = staging_buffer.size();
const auto min_required_buffer_size = std::max<u64>(utils::align(image_linear_size * 4, 0x100000), 16 * 0x100000);
if (driver_caps.ARB_compute_shader_supported)
if (g_upload_transfer_buffer.size() < static_cast<GLsizeiptr>(min_required_buffer_size))
{
if (g_upload_transfer_buffer.size() < static_cast<GLsizeiptr>(min_required_buffer_size))
{
g_upload_transfer_buffer.remove();
g_upload_transfer_buffer.create(gl::buffer::target::pixel_unpack, min_required_buffer_size);
}
if (g_compute_decode_buffer.size() < min_required_buffer_size)
{
g_compute_decode_buffer.remove();
g_compute_decode_buffer.create(gl::buffer::target::ssbo, min_required_buffer_size);
}
g_upload_transfer_buffer.remove();
g_upload_transfer_buffer.create(gl::buffer::target::pixel_unpack, min_required_buffer_size);
}
for (const rsx::subresource_layout& layout : input_layouts)
if (g_compute_decode_buffer.size() < min_required_buffer_size)
{
if (driver_caps.ARB_compute_shader_supported)
g_compute_decode_buffer.remove();
g_compute_decode_buffer.create(gl::buffer::target::ssbo, min_required_buffer_size);
}
}
for (const rsx::subresource_layout& layout : input_layouts)
{
if (driver_caps.ARB_compute_shader_supported)
{
u64 row_pitch = rsx::align2<u64, u64>(layout.width_in_block * block_size_in_bytes, caps.alignment);
// We're in the "else" branch, so "is_compressed_host_format()" is always false.
// Handle emulated compressed formats with host unpack (R8G8 compressed)
row_pitch = std::max<u64>(row_pitch, dst->pitch());
// FIXME: Double-check this logic; it seems like we should always use texels both here and for row_pitch.
image_linear_size = row_pitch * layout.height_in_texel * layout.depth;
compute_scratch_mem = { nullptr, g_compute_decode_buffer.alloc(static_cast<u32>(image_linear_size), 256) };
compute_scratch_mem.first = reinterpret_cast<void*>(static_cast<uintptr_t>(compute_scratch_mem.second));
g_upload_transfer_buffer.reserve_storage_on_heap(static_cast<u32>(image_linear_size));
upload_scratch_mem = g_upload_transfer_buffer.alloc_from_heap(static_cast<u32>(image_linear_size), 256);
dst_buffer = { reinterpret_cast<std::byte*>(upload_scratch_mem.first), image_linear_size };
}
rsx::io_buffer io_buf = dst_buffer;
caps.supports_hw_deswizzle = (is_swizzled && driver_caps.ARB_compute_shader_supported && image_linear_size > 1024);
auto op = upload_texture_subresource(io_buf, layout, format, is_swizzled, caps);
// Define upload region
coord3u region;
region.x = 0;
region.y = 0;
region.z = layout.layer;
region.width = layout.width_in_texel;
region.height = layout.height_in_texel;
region.depth = layout.depth;
if (!driver_caps.ARB_compute_shader_supported)
{
unpack_settings.swap_bytes(op.require_swap);
dst->copy_from(staging_buffer, static_cast<texture::format>(gl_format), static_cast<texture::type>(gl_type), layout.level, region, unpack_settings);
continue;
}
// 0. Preconf
mem_layout.alignment = static_cast<u8>(caps.alignment);
mem_layout.swap_bytes = op.require_swap;
mem_layout.format = gl_format;
mem_layout.type = gl_type;
mem_layout.block_size = block_size_in_bytes;
// 2. Upload memory to GPU
if (!op.require_deswizzle)
{
g_upload_transfer_buffer.unmap();
g_upload_transfer_buffer.copy_to(&g_compute_decode_buffer.get(), upload_scratch_mem.second, compute_scratch_mem.second, image_linear_size);
}
else
{
// 2.1 Copy data to deswizzle buf
if (g_deswizzle_scratch_buffer.size() < min_required_buffer_size)
{
u64 row_pitch = rsx::align2<u64, u64>(layout.width_in_block * block_size_in_bytes, caps.alignment);
// We're in the "else" branch, so "is_compressed_host_format()" is always false.
// Handle emulated compressed formats with host unpack (R8G8 compressed)
row_pitch = std::max<u64>(row_pitch, dst->pitch());
// FIXME: Double-check this logic; it seems like we should always use texels both here and for row_pitch.
image_linear_size = row_pitch * layout.height_in_texel * layout.depth;
compute_scratch_mem = { nullptr, g_compute_decode_buffer.alloc(static_cast<u32>(image_linear_size), 256) };
compute_scratch_mem.first = reinterpret_cast<void*>(static_cast<uintptr_t>(compute_scratch_mem.second));
g_upload_transfer_buffer.reserve_storage_on_heap(static_cast<u32>(image_linear_size));
upload_scratch_mem = g_upload_transfer_buffer.alloc_from_heap(static_cast<u32>(image_linear_size), 256);
dst_buffer = { reinterpret_cast<std::byte*>(upload_scratch_mem.first), image_linear_size };
g_deswizzle_scratch_buffer.remove();
g_deswizzle_scratch_buffer.create(gl::buffer::target::ssbo, min_required_buffer_size);
}
rsx::io_buffer io_buf = dst_buffer;
caps.supports_hw_deswizzle = (is_swizzled && driver_caps.ARB_compute_shader_supported && image_linear_size > 1024);
auto op = upload_texture_subresource(io_buf, layout, format, is_swizzled, caps);
u32 deswizzle_data_offset = g_deswizzle_scratch_buffer.alloc(static_cast<u32>(image_linear_size), 256);
g_upload_transfer_buffer.unmap();
g_upload_transfer_buffer.copy_to(&g_deswizzle_scratch_buffer.get(), upload_scratch_mem.second, deswizzle_data_offset, static_cast<u32>(image_linear_size));
// Define upload region
coord3u region;
region.x = 0;
region.y = 0;
region.z = layout.layer;
region.width = layout.width_in_texel;
region.height = layout.height_in_texel;
region.depth = layout.depth;
// 2.2 Apply compute transform to deswizzle input and dump it in compute_scratch_mem
const auto block_size = op.element_size * op.block_length;
if (driver_caps.ARB_compute_shader_supported)
if (op.require_swap)
{
// 0. Preconf
mem_layout.alignment = static_cast<u8>(caps.alignment);
mem_layout.swap_bytes = op.require_swap;
mem_layout.format = gl_format;
mem_layout.type = gl_type;
mem_layout.block_size = block_size_in_bytes;
mem_layout.swap_bytes = false;
// 2. Upload memory to GPU
if (!op.require_deswizzle)
switch (op.element_size)
{
g_upload_transfer_buffer.unmap();
g_upload_transfer_buffer.copy_to(&g_compute_decode_buffer.get(), upload_scratch_mem.second, compute_scratch_mem.second, image_linear_size);
case 1:
do_deswizzle_transformation<u8, true>(cmd, block_size,
&g_compute_decode_buffer.get(), compute_scratch_mem.second, &g_deswizzle_scratch_buffer.get(), deswizzle_data_offset,
static_cast<u32>(image_linear_size), layout.width_in_texel, layout.height_in_texel, layout.depth);
break;
case 2:
do_deswizzle_transformation<u16, true>(cmd, block_size,
&g_compute_decode_buffer.get(), compute_scratch_mem.second, &g_deswizzle_scratch_buffer.get(), deswizzle_data_offset,
static_cast<u32>(image_linear_size), layout.width_in_texel, layout.height_in_texel, layout.depth);
break;
case 4:
do_deswizzle_transformation<u32, true>(cmd, block_size,
&g_compute_decode_buffer.get(), compute_scratch_mem.second, &g_deswizzle_scratch_buffer.get(), deswizzle_data_offset,
static_cast<u32>(image_linear_size), layout.width_in_texel, layout.height_in_texel, layout.depth);
break;
default:
fmt::throw_exception("Unimplemented element size deswizzle");
}
else
{
// 2.1 Copy data to deswizzle buf
if (g_deswizzle_scratch_buffer.size() < min_required_buffer_size)
{
g_deswizzle_scratch_buffer.remove();
g_deswizzle_scratch_buffer.create(gl::buffer::target::ssbo, min_required_buffer_size);
}
u32 deswizzle_data_offset = g_deswizzle_scratch_buffer.alloc(static_cast<u32>(image_linear_size), 256);
g_upload_transfer_buffer.unmap();
g_upload_transfer_buffer.copy_to(&g_deswizzle_scratch_buffer.get(), upload_scratch_mem.second, deswizzle_data_offset, static_cast<u32>(image_linear_size));
// 2.2 Apply compute transform to deswizzle input and dump it in compute_scratch_mem
const auto block_size = op.element_size * op.block_length;
if (op.require_swap)
{
mem_layout.swap_bytes = false;
switch (op.element_size)
{
case 1:
do_deswizzle_transformation<u8, true>(cmd, block_size,
&g_compute_decode_buffer.get(), compute_scratch_mem.second, &g_deswizzle_scratch_buffer.get(), deswizzle_data_offset,
static_cast<u32>(image_linear_size), layout.width_in_texel, layout.height_in_texel, layout.depth);
break;
case 2:
do_deswizzle_transformation<u16, true>(cmd, block_size,
&g_compute_decode_buffer.get(), compute_scratch_mem.second, &g_deswizzle_scratch_buffer.get(), deswizzle_data_offset,
static_cast<u32>(image_linear_size), layout.width_in_texel, layout.height_in_texel, layout.depth);
break;
case 4:
do_deswizzle_transformation<u32, true>(cmd, block_size,
&g_compute_decode_buffer.get(), compute_scratch_mem.second, &g_deswizzle_scratch_buffer.get(), deswizzle_data_offset,
static_cast<u32>(image_linear_size), layout.width_in_texel, layout.height_in_texel, layout.depth);
break;
default:
fmt::throw_exception("Unimplemented element size deswizzle");
}
}
else
{
switch (op.element_size)
{
case 1:
do_deswizzle_transformation<u8, false>(cmd, block_size,
&g_compute_decode_buffer.get(), compute_scratch_mem.second, &g_deswizzle_scratch_buffer.get(), deswizzle_data_offset,
static_cast<u32>(image_linear_size), layout.width_in_texel, layout.height_in_texel, layout.depth);
break;
case 2:
do_deswizzle_transformation<u16, false>(cmd, block_size,
&g_compute_decode_buffer.get(), compute_scratch_mem.second, &g_deswizzle_scratch_buffer.get(), deswizzle_data_offset,
static_cast<u32>(image_linear_size), layout.width_in_texel, layout.height_in_texel, layout.depth);
break;
case 4:
do_deswizzle_transformation<u32, false>(cmd, block_size,
&g_compute_decode_buffer.get(), compute_scratch_mem.second, &g_deswizzle_scratch_buffer.get(), deswizzle_data_offset,
static_cast<u32>(image_linear_size), layout.width_in_texel, layout.height_in_texel, layout.depth);
break;
default:
fmt::throw_exception("Unimplemented element size deswizzle");
}
}
// Barrier
g_deswizzle_scratch_buffer.push_barrier(deswizzle_data_offset, static_cast<u32>(image_linear_size));
}
// 3. Update configuration
mem_info.image_size_in_texels = image_linear_size / block_size_in_bytes;
mem_info.image_size_in_bytes = image_linear_size;
mem_info.memory_required = 0;
// 4. Dispatch compute routines
copy_buffer_to_image(cmd, mem_layout, &g_compute_decode_buffer.get(), dst, compute_scratch_mem.first, layout.level, region, &mem_info);
// Barrier
g_compute_decode_buffer.push_barrier(compute_scratch_mem.second, static_cast<u32>(image_linear_size));
}
else
{
unpack_settings.swap_bytes(op.require_swap);
dst->copy_from(staging_buffer, static_cast<texture::format>(gl_format), static_cast<texture::type>(gl_type), layout.level, region, unpack_settings);
switch (op.element_size)
{
case 1:
do_deswizzle_transformation<u8, false>(cmd, block_size,
&g_compute_decode_buffer.get(), compute_scratch_mem.second, &g_deswizzle_scratch_buffer.get(), deswizzle_data_offset,
static_cast<u32>(image_linear_size), layout.width_in_texel, layout.height_in_texel, layout.depth);
break;
case 2:
do_deswizzle_transformation<u16, false>(cmd, block_size,
&g_compute_decode_buffer.get(), compute_scratch_mem.second, &g_deswizzle_scratch_buffer.get(), deswizzle_data_offset,
static_cast<u32>(image_linear_size), layout.width_in_texel, layout.height_in_texel, layout.depth);
break;
case 4:
do_deswizzle_transformation<u32, false>(cmd, block_size,
&g_compute_decode_buffer.get(), compute_scratch_mem.second, &g_deswizzle_scratch_buffer.get(), deswizzle_data_offset,
static_cast<u32>(image_linear_size), layout.width_in_texel, layout.height_in_texel, layout.depth);
break;
default:
fmt::throw_exception("Unimplemented element size deswizzle");
}
}
// Barrier
g_deswizzle_scratch_buffer.push_barrier(deswizzle_data_offset, static_cast<u32>(image_linear_size));
}
// 3. Update configuration
mem_info.image_size_in_texels = image_linear_size / block_size_in_bytes;
mem_info.image_size_in_bytes = image_linear_size;
mem_info.memory_required = 0;
// 4. Dispatch compute routines
copy_buffer_to_image(cmd, mem_layout, &g_compute_decode_buffer.get(), dst, compute_scratch_mem.first, layout.level, region, &mem_info);
// Barrier
g_compute_decode_buffer.push_barrier(compute_scratch_mem.second, static_cast<u32>(image_linear_size));
}
}

View File

@ -14,20 +14,20 @@ namespace gl
{
struct pixel_buffer_layout
{
GLenum format;
GLenum type;
u32 row_length;
u8 block_size;
bool swap_bytes;
u8 alignment;
GLenum format = GL_RGBA;
GLenum type = GL_UNSIGNED_BYTE;
u32 row_length = 0;
u8 block_size = 0;
bool swap_bytes = false;
u8 alignment = 0;
u8 reserved;
};
struct image_memory_requirements
{
u64 image_size_in_texels;
u64 image_size_in_bytes;
u64 memory_required;
u64 image_size_in_texels = 0;
u64 image_size_in_bytes = 0;
u64 memory_required = 0;
};
struct clear_cmd_info

View File

@ -287,6 +287,7 @@ namespace gl
u32 transfer_width = width;
u32 transfer_height = height;
u32 transfer_x = 0, transfer_y = 0;
u16 resolution_scale_percent = 100;
if (context == rsx::texture_upload_context::framebuffer_storage)
{
@ -295,9 +296,10 @@ namespace gl
target_texture = surface->get_surface(rsx::surface_access::transfer_read);
transfer_width *= surface->samples_x;
transfer_height *= surface->samples_y;
resolution_scale_percent = surface->resolution_scaling_config.scale_percent;
}
if ((rsx::get_resolution_scale_percent() != 100 && context == rsx::texture_upload_context::framebuffer_storage) ||
if ((resolution_scale_percent != 100 && context == rsx::texture_upload_context::framebuffer_storage) ||
(vram_texture->pitch() != rsx_pitch))
{
areai src_area = { 0, 0, 0, 0 };

View File

@ -633,9 +633,17 @@ namespace rsx
case 2:
break;
default:
rsx_log.error("Unknown render mode %d", mode);
{
struct logged_t
{
atomic_t<u8> logged_cause[256]{};
};
const auto& is_error = ::at32(g_fxo->get<logged_t>().logged_cause, mode).try_inc(10);
(is_error ? rsx_log.error : rsx_log.trace)("Unknown render mode %d", mode);
return;
}
}
const u32 offset = arg & 0xffffff;
auto address_ptr = util::get_report_data_impl(ctx, offset);

View File

@ -16,7 +16,7 @@ namespace rsx
{
static constexpr u16 menu_entry_height = 40;
static constexpr u16 menu_entry_margin = 30;
static constexpr u16 menu_checkbox_size = 20;
static constexpr u16 menu_checkbox_size = 24;
static constexpr u16 element_height = 25;
enum class page_navigation

View File

@ -26,7 +26,7 @@ namespace rsx
{
if (!suspend_mode)
{
Emu.after_kill_callback = []() { Emu.Restart(); };
Emu.after_kill_callback = []() { Emu.Restart(true, false); };
// Make sure we keep the game window opened
Emu.SetContinuousMode(true);

View File

@ -152,16 +152,16 @@ namespace rsx
home_menu_settings_video::home_menu_settings_video(s16 x, s16 y, u16 width, u16 height, bool use_separators, home_menu_page* parent)
: home_menu_settings_page(x, y, width, height, use_separators, parent, get_localized_string(localized_string_id::HOME_MENU_SETTINGS_VIDEO))
{
add_unsigned_slider(&g_cfg.video.resolution_scale_percent, localized_string_id::HOME_MENU_SETTINGS_VIDEO_RESOLUTION_SCALE_PERCENT, "%", 25);
add_unsigned_slider(&g_cfg.video.min_scalable_dimension, localized_string_id::HOME_MENU_SETTINGS_VIDEO_RESOLUTION_SCALE_THRESHOLD, "px", 1);
add_dropdown(&g_cfg.video.vsync, localized_string_id::HOME_MENU_SETTINGS_VIDEO_VSYNC);
add_dropdown(&g_cfg.video.frame_limit, localized_string_id::HOME_MENU_SETTINGS_VIDEO_FRAME_LIMIT);
add_unsigned_slider(&g_cfg.video.anisotropic_level_override, localized_string_id::HOME_MENU_SETTINGS_VIDEO_ANISOTROPIC_OVERRIDE, "x", 2, {{0, "Auto"}}, {14});
add_dropdown(&g_cfg.video.output_scaling, localized_string_id::HOME_MENU_SETTINGS_VIDEO_OUTPUT_SCALING);
if (g_cfg.video.renderer == video_renderer::vulkan && g_cfg.video.output_scaling == output_scaling_mode::fsr)
{
add_unsigned_slider(&g_cfg.video.rcas_sharpening_intensity, localized_string_id::HOME_MENU_SETTINGS_VIDEO_RCAS_SHARPENING, " %", 1);
}
add_unsigned_slider(&g_cfg.video.rcas_sharpening_intensity, localized_string_id::HOME_MENU_SETTINGS_VIDEO_RCAS_SHARPENING, " %", 1);
add_checkbox(&g_cfg.video.stretch_to_display_area, localized_string_id::HOME_MENU_SETTINGS_VIDEO_STRETCH_TO_DISPLAY);
@ -221,6 +221,7 @@ namespace rsx
home_menu_settings_overlays::home_menu_settings_overlays(s16 x, s16 y, u16 width, u16 height, bool use_separators, home_menu_page* parent)
: home_menu_settings_page(x, y, width, height, use_separators, parent, get_localized_string(localized_string_id::HOME_MENU_SETTINGS_OVERLAYS))
{
add_checkbox(&g_cfg.misc.play_music_during_boot, localized_string_id::HOME_MENU_SETTINGS_OVERLAYS_PLAY_MUSIC_DURING_BOOT);
add_checkbox(&g_cfg.misc.show_trophy_popups, localized_string_id::HOME_MENU_SETTINGS_OVERLAYS_SHOW_TROPHY_POPUPS);
add_checkbox(&g_cfg.misc.show_rpcn_popups, localized_string_id::HOME_MENU_SETTINGS_OVERLAYS_SHOW_RPCN_POPUPS);
add_checkbox(&g_cfg.misc.show_shader_compilation_hint, localized_string_id::HOME_MENU_SETTINGS_OVERLAYS_SHOW_SHADER_COMPILATION_HINT);
@ -229,6 +230,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.misc.show_fatal_error_hints, localized_string_id::HOME_MENU_SETTINGS_OVERLAYS_SHOW_FATAL_ERROR_HINTS);
add_checkbox(&g_cfg.video.record_with_overlays, localized_string_id::HOME_MENU_SETTINGS_OVERLAYS_RECORD_WITH_OVERLAYS);
apply_layout();
@ -252,10 +254,11 @@ namespace rsx
add_dropdown(&g_cfg.video.perf_overlay.position, localized_string_id::HOME_MENU_SETTINGS_PERFORMANCE_OVERLAY_POSITION);
add_checkbox(&g_cfg.video.perf_overlay.center_x, localized_string_id::HOME_MENU_SETTINGS_PERFORMANCE_OVERLAY_CENTER_X);
add_checkbox(&g_cfg.video.perf_overlay.center_y, localized_string_id::HOME_MENU_SETTINGS_PERFORMANCE_OVERLAY_CENTER_Y);
add_unsigned_slider(&g_cfg.video.perf_overlay.margin_x, localized_string_id::HOME_MENU_SETTINGS_PERFORMANCE_OVERLAY_MARGIN_X, " px", 1);
add_unsigned_slider(&g_cfg.video.perf_overlay.margin_y, localized_string_id::HOME_MENU_SETTINGS_PERFORMANCE_OVERLAY_MARGIN_Y, " px", 1);
add_float_slider(&g_cfg.video.perf_overlay.margin_x, localized_string_id::HOME_MENU_SETTINGS_PERFORMANCE_OVERLAY_MARGIN_X, " %", 0.25f);
add_float_slider(&g_cfg.video.perf_overlay.margin_y, localized_string_id::HOME_MENU_SETTINGS_PERFORMANCE_OVERLAY_MARGIN_Y, " %", 0.25f);
add_unsigned_slider(&g_cfg.video.perf_overlay.font_size, localized_string_id::HOME_MENU_SETTINGS_PERFORMANCE_OVERLAY_FONT_SIZE, " px", 1);
add_unsigned_slider(&g_cfg.video.perf_overlay.opacity, localized_string_id::HOME_MENU_SETTINGS_PERFORMANCE_OVERLAY_OPACITY, " %", 1);
add_checkbox(&g_cfg.video.perf_overlay.perf_overlay_use_window_space, localized_string_id::HOME_MENU_SETTINGS_PERFORMANCE_OVERLAY_USE_WINDOW_SPACE);
apply_layout();
}

View File

@ -10,8 +10,6 @@ namespace rsx
{
namespace overlays
{
void play_sound(sound_effect sound, std::optional<f32> volume);
struct home_menu_settings : public home_menu_page
{
public:

View File

@ -84,7 +84,7 @@ namespace rsx::overlays
void switchbox::set_size(u16 w, u16 h)
{
const u16 dim = std::min(w, h);
const u16 dim = std::max<u16>(std::min(w, h), 14);
box_layout::set_size(w, h);
clear_items();
@ -93,21 +93,17 @@ namespace rsx::overlays
m_back_ellipse = nullptr;
m_front_circle = nullptr;
if (dim < 4)
{
return;
}
auto ellipse_part = std::make_unique<rounded_rect>();
auto circle_part = std::make_unique<rounded_rect>();
auto circle_part = std::make_unique<ellipse>();
ellipse_part->set_size(dim * 2, dim / 2);
ellipse_part->set_pos(0, dim / 4);
ellipse_part->radius = dim / 4;
ellipse_part->set_size(dim * 2, dim);
ellipse_part->set_padding(1);
ellipse_part->set_pos(0, 0);
ellipse_part->border_radius = (dim - 4) / 2; // Avoid perfect capsule shape since we want a border and perfect capsules can have a false border along the midline due to subgroup shenanigans
circle_part->set_size(dim, dim);
circle_part->set_padding(4);
circle_part->set_pos(0, 0);
circle_part->radius = dim / 2;
m_back_ellipse = add_element(ellipse_part);
m_front_circle = add_element(circle_part);
@ -130,13 +126,18 @@ namespace rsx::overlays
if (m_is_checked)
{
m_back_ellipse->back_color = this->fore_color * 0.5f;
m_back_ellipse->border_color.a = 0.f;
m_back_ellipse->border_size = 0;
m_back_ellipse->back_color = this->fore_color * 0.75f;
m_back_ellipse->back_color.a = 1.f;
m_front_circle->back_color = this->fore_color;
m_front_circle->back_color = color4f(1.f);
m_front_circle->set_pos(this->x + m_front_circle->w, this->y);
}
else
{
m_back_ellipse->border_color = this->back_color * 0.75f;
m_back_ellipse->border_color.a = 1.f;
m_back_ellipse->border_size = 1;
m_back_ellipse->back_color = this->back_color * 0.5f;
m_back_ellipse->back_color.a = 1.f;
m_front_circle->back_color = this->back_color;

View File

@ -54,6 +54,43 @@ namespace rsx
return result;
}
void compiled_resource::sdf_config_t::transform(const areaf& target_viewport, const sizef& virtual_viewport)
{
const f32 scale_x = target_viewport.width() / virtual_viewport.width;
const f32 scale_y = target_viewport.height() / virtual_viewport.height;
// Ideally the average should match the x and y scaling but arithmetic drift shifts the values around a bit.
// Also we need a way to define perfect circles when the aspect ratio is not respected.
const f32 scale_av = (scale_x + scale_y) / 2;
hx *= scale_x;
hy *= scale_y;
br *= scale_av;
bw *= scale_av;
// Border radius clamp
br = std::min({ br, hx, hy });
// Compute the function's origin. Account for flipped viewports as well.
if (target_viewport.x2 < target_viewport.x1)
{
cx = target_viewport.width() - (cx * scale_x) + target_viewport.x2;
}
else
{
cx = cx * scale_x + target_viewport.x1;
}
if (target_viewport.y2 < target_viewport.y1)
{
cy = target_viewport.height() - (cy * scale_y) + target_viewport.y2;
}
else
{
cy = cy * scale_y + target_viewport.y1;
}
}
image_info::image_info(const std::string& filename, bool grayscaled)
{
fs::file f(filename, fs::read + fs::isfile);
@ -259,6 +296,12 @@ namespace rsx
{
v += vertex(x_offset, y_offset, 0.f, 0.f);
}
if (draw_commands[n].config.sdf_config.func != sdf_function::none)
{
draw_commands[n].config.sdf_config.cx += x_offset;
draw_commands[n].config.sdf_config.cy += y_offset;
}
}
}
@ -275,6 +318,12 @@ namespace rsx
v += vertex(x_offset, y_offset, 0.f, 0.f);
}
if (draw_commands[n].config.sdf_config.func != sdf_function::none)
{
draw_commands[n].config.sdf_config.cx += x_offset;
draw_commands[n].config.sdf_config.cy += y_offset;
}
draw_commands[n].config.clip_rect = clip_rect;
draw_commands[n].config.clip_region = true;
}
@ -382,25 +431,25 @@ namespace rsx
m_is_compiled = false;
}
void overlay_element::set_text(const std::string& text)
void overlay_element::set_text(std::string_view text)
{
std::u32string new_text = utf8_to_u32string(text);
const bool is_dirty = this->text != new_text;
this->text = std::move(new_text);
if (is_dirty)
{
this->text = std::move(new_text);
m_is_compiled = false;
}
}
void overlay_element::set_unicode_text(const std::u32string& text)
void overlay_element::set_unicode_text(std::u32string_view text)
{
const bool is_dirty = this->text != text;
this->text = text;
if (is_dirty)
{
this->text = text;
m_is_compiled = false;
}
}
@ -585,6 +634,25 @@ namespace rsx
return result;
}
void overlay_element::configure_sdf(compiled_resource::command_config& config, sdf_function func)
{
const f32 rx = static_cast<f32>(x) + padding_left;
const f32 rw = static_cast<f32>(w) - (padding_left + padding_right);
const f32 ry = static_cast<f32>(y) + padding_top;
const f32 rh = static_cast<f32>(h) - (padding_top + padding_bottom);
config.sdf_config.func = func;
config.sdf_config.cx = rx + (rw / 2.f);
config.sdf_config.cy = ry + (rh / 2.f);
config.sdf_config.hx = rw / 2.f;
config.sdf_config.hy = rh / 2.f;
config.sdf_config.br = 0.f;
config.sdf_config.bw = border_size;
config.sdf_config.border_color = border_color;
config.disable_vertex_snap = true;
}
compiled_resource& overlay_element::get_compiled()
{
if (is_compiled())
@ -609,6 +677,14 @@ namespace rsx
config.pulse_sinus_offset = pulse_sinus_offset;
config.pulse_speed_modifier = pulse_speed_modifier;
if (border_size != 0 &&
border_color.a > 0.f &&
w > border_size &&
h > border_size)
{
configure_sdf(config, sdf_function::box);
}
auto& verts = compiled_resources_temp.draw_commands.front().verts;
verts.resize(4);
@ -1095,82 +1171,33 @@ namespace rsx
return compiled_resources;
}
#ifdef __APPLE__
if (true)
#else
if (radius == 0 || radius > (w / 2))
#endif
overlay_element::get_compiled();
auto& config = compiled_resources.draw_commands.front().config;
configure_sdf(config, sdf_function::rounded_box);
config.sdf_config.br = std::min({ static_cast<f32>(border_radius), config.sdf_config.hx, config.sdf_config.hy });
m_is_compiled = true;
return compiled_resources;
}
compiled_resource& ellipse::get_compiled()
{
if (is_compiled())
{
return compiled_resources;
}
compiled_resources.clear();
if (!is_visible())
{
// Invalid radius
compiled_resources = overlay_element::get_compiled();
m_is_compiled = true;
return compiled_resources;
}
compiled_resource compiled_resources_temp = {};
compiled_resources_temp.append({}); // Bg horizontal mid
compiled_resources_temp.append({}); // Bg horizontal top
compiled_resources_temp.append({}); // Bg horizontal bottom
compiled_resources_temp.append({}); // Bg upper-left
compiled_resources_temp.append({}); // Bg lower-left
compiled_resources_temp.append({}); // Bg upper-right
compiled_resources_temp.append({}); // Bg lower-right
for (auto& draw_cmd : compiled_resources_temp.draw_commands)
{
auto& config = draw_cmd.config;
config.color = back_color;
config.disable_vertex_snap = true;
config.pulse_glow = pulse_effect_enabled;
config.pulse_sinus_offset = pulse_sinus_offset;
config.pulse_speed_modifier = pulse_speed_modifier;
}
auto& bg0 = compiled_resources_temp.draw_commands[0];
auto& bg1 = compiled_resources_temp.draw_commands[1];
auto& bg2 = compiled_resources_temp.draw_commands[2];
bg0.verts.emplace_back(f32(x), f32(y + radius), 0.f, 0.f);
bg0.verts.emplace_back(f32(x + w), f32(y + radius), 0.f, 0.f);
bg0.verts.emplace_back(f32(x), f32(y + h) - radius, 0.f, 0.f);
bg0.verts.emplace_back(f32(x + w), f32(y + h) - radius, 0.f, 0.f);
bg1.verts.emplace_back(f32(x + radius), f32(y), 0.f, 0.f);
bg1.verts.emplace_back(f32(x + w) - radius, f32(y), 0.f, 0.f);
bg1.verts.emplace_back(f32(x + radius), f32(y + radius), 0.f, 0.f);
bg1.verts.emplace_back(f32(x + w) - radius, f32(y + radius), 0.f, 0.f);
bg2.verts.emplace_back(f32(x + radius), f32(y + h) - radius, 0.f, 0.f);
bg2.verts.emplace_back(f32(x + w) - radius, f32(y + h) - radius, 0.f, 0.f);
bg2.verts.emplace_back(f32(x + radius), f32(y + h), 0.f, 0.f);
bg2.verts.emplace_back(f32(x + w) - radius, f32(y + h), 0.f, 0.f);
// Generate the quadrants
const f32 corners[4][2] =
{
{ f32(x + radius), f32(y + radius) },
{ f32(x + radius), f32(y + h) - radius },
{ f32(x + w) - radius, f32(y + radius) },
{ f32(x + w) - radius, f32(y + h) - radius }
};
const f32 radius_f = static_cast<f32>(radius);
const f32 scale[4][2] =
{
{ -radius_f, -radius_f },
{ -radius_f, +radius_f },
{ +radius_f, -radius_f },
{ +radius_f, +radius_f }
};
for (int i = 0; i < 4; ++i)
{
auto& command = compiled_resources_temp.draw_commands[i + 3];
command.config.primitives = rsx::overlays::primitive_type::triangle_fan;
command.verts = generate_unit_quadrant(num_control_points, corners[i], scale[i]);
}
compiled_resources.add(std::move(compiled_resources_temp), margin_left, margin_top);
rounded_rect::get_compiled();
auto& config = compiled_resources.draw_commands.front().config;
configure_sdf(config, sdf_function::ellipse);
m_is_compiled = true;
return compiled_resources;

View File

@ -31,6 +31,14 @@ namespace rsx
triangle_fan = 4
};
enum class sdf_function : u8
{
none = 0,
ellipse,
box,
rounded_box,
};
struct image_info_base
{
int w = 0, h = 0, channels = 0;
@ -95,6 +103,25 @@ namespace rsx
struct compiled_resource
{
struct sdf_config_t
{
sdf_function func = sdf_function::none;
f32 cx; // Center x
f32 cy; // Center y
f32 hx; // Half-size in X
f32 hy; // Half-size in Y
f32 br; // Border radius
f32 bw; // Border width
color4f border_color;
// Transform a SDF definition from one reference frame to another
// Target viewport - your actual render area
// Virtual viewport - the internal design viewport
void transform(const areaf& target_viewport, const sizef& virtual_viewport);
};
struct command_config
{
primitive_type primitives = primitive_type::quad_list;
@ -105,6 +132,8 @@ namespace rsx
f32 pulse_sinus_offset = 0.0f; // The current pulse offset
f32 pulse_speed_modifier = 0.005f;
sdf_config_t sdf_config;
areaf clip_rect = {};
bool clip_region = false;
@ -171,6 +200,9 @@ namespace rsx
f32 pulse_sinus_offset = 0.0f; // The current pulse offset
f32 pulse_speed_modifier = 0.005f;
u8 border_size = 0;
color4f border_color = { 0.f, 0.f, 0.f, 1.f };
// Analog to command_config::get_sinus_value
// Apply modifier for sinus pulse. Resets the pulse. For example:
// 0 -> reset to 0.5 rising
@ -210,8 +242,8 @@ namespace rsx
// NOTE: Functions as a simple position offset. Top left corner is the anchor.
virtual void set_margin(u16 left, u16 top);
virtual void set_margin(u16 margin);
virtual void set_text(const std::string& text);
virtual void set_unicode_text(const std::u32string& text);
virtual void set_text(std::string_view text);
virtual void set_unicode_text(std::u32string_view text);
void set_text(localized_string_id id);
void set_text(const localized_string& container);
virtual void set_font(const char* font_name, u16 font_size);
@ -237,6 +269,8 @@ namespace rsx
protected:
bool m_is_compiled = false; // Only use m_is_compiled as a getter in is_compiled() if possible
void configure_sdf(compiled_resource::command_config& config, sdf_function func);
};
struct layout_container : public overlay_element
@ -316,13 +350,18 @@ namespace rsx
struct rounded_rect : public overlay_element
{
u8 radius = 5;
u8 num_control_points = 8; // Smoothness control
u16 border_radius = 5;
using overlay_element::overlay_element;
compiled_resource& get_compiled() override;
};
struct ellipse : public rounded_rect
{
using rounded_rect::rounded_rect;
compiled_resource& get_compiled() override;
};
struct image_view : public overlay_element
{
protected:

View File

@ -95,12 +95,12 @@ namespace rsx
}
}
void edit_text::set_text(const std::string& text)
void edit_text::set_text(std::string_view text)
{
set_unicode_text(utf8_to_u32string(text));
}
void edit_text::set_unicode_text(const std::u32string& text)
void edit_text::set_unicode_text(std::u32string_view text)
{
value = text;

View File

@ -26,8 +26,8 @@ namespace rsx
using label::label;
void set_text(const std::string& text) override;
void set_unicode_text(const std::u32string& text) override;
void set_text(std::string_view text) override;
void set_unicode_text(std::u32string_view text) override;
void set_placeholder(const std::u32string& placeholder_text);

View File

@ -17,7 +17,7 @@ namespace rsx
scroll_indicator_grip->set_pos(1, 0);
scroll_indicator_grip->set_size(5, 5);
scroll_indicator_grip->radius = 2;
scroll_indicator_grip->border_radius = 2;
scroll_indicator_track->set_size(7, height);
m_scroll_indicator = std::make_unique<box_layout>();

View File

@ -90,7 +90,7 @@ namespace rsx
return m_loc_id == id;
}
bool message_item::text_matches(const std::u32string& text) const
bool message_item::text_matches(std::u32string_view text) const
{
return m_text.text == text;
}

View File

@ -31,7 +31,7 @@ namespace rsx
compiled_resource& get_compiled() override;
bool id_matches(localized_string_id id) const;
bool text_matches(const std::u32string& text) const;
bool text_matches(std::u32string_view text) const;
void set_label_text(const std::string& text);

View File

@ -6,6 +6,7 @@
#include "Emu/Cell/PPUThread.h"
#include <algorithm>
#include <cmath>
#include <utility>
#include "util/cpu_stats.hpp"
@ -93,7 +94,6 @@ namespace rsx
{
// left, top, right, bottom
const areau padding { m_padding, m_padding - std::min<u32>(4, m_padding), m_padding, m_padding };
const positionu margin { m_margin_x, m_margin_y };
positionu pos;
u16 graph_width = 0;
@ -116,6 +116,26 @@ namespace rsx
graph_height += m_padding;
}
const u16 overlay_width = std::max(m_body.w, graph_width);
const u16 overlay_height = static_cast<u16>(m_body.h + graph_height);
const auto percent_to_margin_px = [](f32 margin_percent, u16 virtual_size, u16 overlay_size) -> u32
{
if (overlay_size >= virtual_size)
{
return 0;
}
const u32 max_margin = virtual_size - overlay_size;
const u32 margin_px = static_cast<u32>(std::lround((std::clamp(margin_percent, 0.0f, 100.0f) / 100.0f) * max_margin));
return std::min(margin_px, max_margin);
};
const positionu margin
{
percent_to_margin_px(m_margin_x, m_virtual_width, overlay_width),
percent_to_margin_px(m_margin_y, m_virtual_height, overlay_height)
};
switch (m_quadrant)
{
case screen_quadrant::top_left:
@ -123,27 +143,27 @@ namespace rsx
pos.y = margin.y;
break;
case screen_quadrant::top_right:
pos.x = virtual_width - std::max(m_body.w, graph_width) - margin.x;
pos.x = m_virtual_width - overlay_width - margin.x;
pos.y = margin.y;
break;
case screen_quadrant::bottom_left:
pos.x = margin.x;
pos.y = virtual_height - m_body.h - graph_height - margin.y;
pos.y = m_virtual_height - overlay_height - margin.y;
break;
case screen_quadrant::bottom_right:
pos.x = virtual_width - std::max(m_body.w, graph_width) - margin.x;
pos.y = virtual_height - m_body.h - graph_height - margin.y;
pos.x = m_virtual_width - overlay_width - margin.x;
pos.y = m_virtual_height - overlay_height - margin.y;
break;
}
if (m_center_x)
{
pos.x = (virtual_width - std::max(m_body.w, graph_width)) / 2;
pos.x = overlay_width >= m_virtual_width ? 0 : (m_virtual_width - overlay_width) / 2;
}
if (m_center_y)
{
pos.y = (virtual_height - m_body.h - graph_height) / 2;
pos.y = overlay_height >= m_virtual_height ? 0 : (m_virtual_height - overlay_height) / 2;
}
elm.set_pos(pos.x, pos.y);
@ -381,7 +401,7 @@ namespace rsx
m_force_repaint = true;
}
void perf_metrics_overlay::set_margins(u32 margin_x, u32 margin_y, bool center_x, bool center_y)
void perf_metrics_overlay::set_margins(f32 margin_x, f32 margin_y, bool center_x, bool center_y)
{
if (m_margin_x == margin_x && m_margin_y == margin_y && m_center_x == center_x && m_center_y == center_y)
return;
@ -431,6 +451,38 @@ namespace rsx
m_force_update = true;
}
void perf_metrics_overlay::set_render_viewport(u16 width, u16 height)
{
u16 new_virtual_width = virtual_width;
u16 new_virtual_height = virtual_height;
if (use_window_space && width > 0 && height > 0)
{
const double scale_x = static_cast<double>(width) / virtual_width;
const double scale_y = static_cast<double>(height) / virtual_height;
const double scale = std::min(scale_x, scale_y);
new_virtual_width = static_cast<u16>(std::min<u32>(
static_cast<u32>(std::lround(width / scale)),
std::numeric_limits<u16>::max()));
new_virtual_height = static_cast<u16>(std::min<u32>(
static_cast<u32>(std::lround(height / scale)),
std::numeric_limits<u16>::max()));
}
if (m_virtual_width == new_virtual_width && m_virtual_height == new_virtual_height)
return;
m_virtual_width = new_virtual_width;
m_virtual_height = new_virtual_height;
if (m_is_initialised)
{
reset_transforms();
}
}
void perf_metrics_overlay::update(u64 /*timestamp_us*/)
{
const auto elapsed_update = m_update_timer.GetElapsedTimeInMilliSec();
@ -896,7 +948,8 @@ namespace rsx
perf_overlay->set_update_interval(perf_settings.update_interval);
perf_overlay->set_font(perf_settings.font);
perf_overlay->set_font_size(perf_settings.font_size);
perf_overlay->set_margins(perf_settings.margin_x, perf_settings.margin_y, perf_settings.center_x.get(), perf_settings.center_y.get());
perf_overlay->set_margins(static_cast<f32>(perf_settings.margin_x.get()), static_cast<f32>(perf_settings.margin_y.get()), perf_settings.center_x.get(), perf_settings.center_y.get());
perf_overlay->use_window_space = perf_settings.perf_overlay_use_window_space.get();
perf_overlay->set_opacity(perf_settings.opacity / 100.f);
perf_overlay->set_body_colors(perf_settings.color_body, perf_settings.background_body);
perf_overlay->set_title_colors(perf_settings.color_title, perf_settings.background_title);

View File

@ -37,10 +37,12 @@ namespace rsx
u32 m_frames{};
std::string m_font{};
u16 m_font_size{};
u32 m_margin_x{}; // horizontal distance to the screen border relative to the screen_quadrant in px
u32 m_margin_y{}; // vertical distance to the screen border relative to the screen_quadrant in px
f32 m_margin_x{}; // horizontal distance to the screen border relative to the screen_quadrant in percent of the window width
f32 m_margin_y{}; // vertical distance to the screen border relative to the screen_quadrant in percent of the window height
u32 m_padding{}; // space between overlay elements
f32 m_opacity{}; // 0..1
u16 m_virtual_width{virtual_width};
u16 m_virtual_height{virtual_height};
bool m_center_x{}; // center the overlay horizontally
bool m_center_y{}; // center the overlay vertically
@ -96,11 +98,14 @@ namespace rsx
void set_update_interval(u32 update_interval);
void set_font(std::string font);
void set_font_size(u16 font_size);
void set_margins(u32 margin_x, u32 margin_y, bool center_x, bool center_y);
void set_margins(f32 margin_x, f32 margin_y, bool center_x, bool center_y);
void set_opacity(f32 opacity);
void set_body_colors(std::string color, std::string background);
void set_title_colors(std::string color, std::string background);
void force_next_update();
void set_render_viewport(u16 width, u16 height) override;
u16 get_virtual_width() const override { return m_virtual_width; }
u16 get_virtual_height() const override { return m_virtual_height; }
void update(u64 timestamp_us) override;

View File

@ -58,7 +58,7 @@ namespace rsx
set_pos(x + dx, y + dy);
}
void progress_bar::set_text(const std::string& str)
void progress_bar::set_text(std::string_view str)
{
text_view.set_text(str);
text_view.align_text(text_align::center);

View File

@ -24,7 +24,7 @@ namespace rsx
void set_pos(s16 _x, s16 _y) override;
void set_size(u16 _w, u16 _h) override;
void translate(s16 dx, s16 dy) override;
void set_text(const std::string& str) override;
void set_text(std::string_view str) override;
compiled_resource& get_compiled() override;
};

View File

@ -144,7 +144,7 @@ namespace rsx::overlays
auto background = std::make_unique<rounded_rect>();
background->set_size(w, h);
background->radius = std::min(h / 4, 5);
background->border_radius = std::min(h / 4, 5);
background->back_color = color4f(0.3f, 0.3f, 0.3f, 1.0f);
const u16 arrow_size = std::min<u16>(h / 2, max_dropdown_arrow_dimension);

View File

@ -8,7 +8,7 @@ namespace rsx::overlays
constexpr u16 slider_indicator_radius = 8;
constexpr u16 slider_indicator_dia = slider_indicator_radius * 2;
constexpr const char* slider_label_font_family = "Arial";
constexpr int slider_label_font_size = 10;
constexpr int slider_label_font_size = 11;
void slider::init()
{
@ -29,25 +29,27 @@ namespace rsx::overlays
// Base components
auto background = std::make_unique<rounded_rect>();
auto foreground = std::make_unique<rounded_rect>();
auto indicator = std::make_unique<rounded_rect>();
auto indicator = std::make_unique<ellipse>();
auto value_label = std::make_unique<label>();
auto min_label = std::make_unique<label>();
auto max_label = std::make_unique<label>();
indicator->radius = slider_indicator_radius;
indicator->set_size(slider_indicator_dia, slider_indicator_dia);
indicator->border_radius = slider_indicator_radius;
indicator->set_size(slider_indicator_dia + 2, slider_indicator_dia + 2);
indicator->set_padding(2);
indicator->set_pos(0, -2);
indicator->back_color = color4f(1.f);
background->radius = slider_rail_thickness / 2;
background->border_radius = slider_rail_thickness / 2;
background->back_color = this->back_color;
background->back_color.a = 1.f;
background->set_size(w, slider_rail_thickness);
background->set_pos(0, (slider_indicator_dia / 2) - background->radius);
background->set_pos(0, (slider_indicator_dia / 2) - background->border_radius);
foreground->radius = slider_cover_thickness / 2;
foreground->border_radius = slider_cover_thickness / 2;
foreground->back_color = this->fore_color;
foreground->set_size(0, slider_cover_thickness);
foreground->set_pos(0, (slider_indicator_dia / 2) - foreground->radius);
foreground->set_pos(0, (slider_indicator_dia / 2) - foreground->border_radius);
value_label->set_padding(2);
value_label->set_font(slider_label_font_family, slider_label_font_size);

View File

@ -60,7 +60,7 @@ static auto s_ascii_lowering_map = []()
}();
template<typename F>
void process_multibyte(const std::string& s, F&& func)
void process_multibyte(std::string_view s, F&& func)
{
const usz end = s.length();
for (usz index = 0; index < end; ++index)
@ -110,7 +110,7 @@ void process_multibyte(const std::string& s, F&& func)
}
}
std::string utf8_to_ascii8(const std::string& utf8_string)
std::string utf8_to_ascii8(std::string_view utf8_string)
{
std::string out;
out.reserve(utf8_string.length());
@ -135,7 +135,7 @@ std::string utf8_to_ascii8(const std::string& utf8_string)
return out;
}
std::string utf16_to_ascii8(const std::u16string& utf16_string)
std::string utf16_to_ascii8(std::u16string_view utf16_string)
{
// Strip extended codes, map to '#' instead (placeholder)
std::string out;
@ -152,7 +152,7 @@ std::string utf16_to_ascii8(const std::u16string& utf16_string)
return out;
}
std::u16string ascii8_to_utf16(const std::string& ascii_string)
std::u16string ascii8_to_utf16(std::string_view ascii_string)
{
std::u16string out;
out.reserve(ascii_string.length());
@ -168,7 +168,7 @@ std::u16string ascii8_to_utf16(const std::string& ascii_string)
return out;
}
std::u32string utf8_to_u32string(const std::string& utf8_string)
std::u32string utf8_to_u32string(std::string_view utf8_string)
{
std::u32string result;
result.reserve(utf8_string.size());
@ -181,7 +181,7 @@ std::u32string utf8_to_u32string(const std::string& utf8_string)
return result;
}
std::u16string u32string_to_utf16(const std::u32string& utf32_string)
std::u16string u32string_to_utf16(std::u32string_view utf32_string)
{
std::u16string result;
result.reserve(utf32_string.size());
@ -194,7 +194,7 @@ std::u16string u32string_to_utf16(const std::u32string& utf32_string)
return result;
}
std::u32string utf16_to_u32string(const std::u16string& utf16_string)
std::u32string utf16_to_u32string(std::u16string_view utf16_string)
{
std::u32string result;
result.reserve(utf16_string.size());

View File

@ -218,9 +218,9 @@ void operator < (const vector3_base<T>& lhs, T rhs)
using vector3i = vector3_base<int>;
using vector3f = vector3_base<float>;
std::string utf8_to_ascii8(const std::string& utf8_string);
std::string utf16_to_ascii8(const std::u16string& utf16_string);
std::u16string ascii8_to_utf16(const std::string& ascii_string);
std::u32string utf8_to_u32string(const std::string& utf8_string);
std::u16string u32string_to_utf16(const std::u32string& utf32_string);
std::u32string utf16_to_u32string(const std::u16string& utf16_string);
std::string utf8_to_ascii8(std::string_view utf8_string);
std::string utf16_to_ascii8(std::u16string_view utf16_string);
std::u16string ascii8_to_utf16(std::string_view ascii_string);
std::u32string utf8_to_u32string(std::string_view utf8_string);
std::u16string u32string_to_utf16(std::u32string_view utf32_string);
std::u32string utf16_to_u32string(std::u16string_view utf16_string);

View File

@ -289,6 +289,7 @@ namespace rsx
handle_button_press(pad_button::R3, !!(digital1 & CELL_PAD_CTRL_R3), pad_index);
handle_button_press(pad_button::select, !!(digital1 & CELL_PAD_CTRL_SELECT), pad_index);
handle_button_press(pad_button::start, !!(digital1 & CELL_PAD_CTRL_START), pad_index);
handle_button_press(pad_button::ps, !!(digital1 & CELL_PAD_CTRL_PS), pad_index);
}
//if (pad->ldd_data.len > CELL_PAD_BTN_OFFSET_DIGITAL2)
@ -303,7 +304,6 @@ namespace rsx
handle_button_press(pad_button::R1, !!(digital2 & CELL_PAD_CTRL_R1), pad_index);
handle_button_press(pad_button::L2, !!(digital2 & CELL_PAD_CTRL_L2), pad_index);
handle_button_press(pad_button::R2, !!(digital2 & CELL_PAD_CTRL_R2), pad_index);
handle_button_press(pad_button::ps, !!(digital2 & CELL_PAD_CTRL_PS), pad_index);
}
const auto handle_ldd_stick_input = [&](s32 offset, pad_button id_small, pad_button id_large)
@ -372,6 +372,9 @@ namespace rsx
case CELL_PAD_CTRL_START:
button_id = pad_button::start;
break;
case CELL_PAD_CTRL_PS:
button_id = pad_button::ps;
break;
default:
break;
}
@ -404,9 +407,6 @@ namespace rsx
case CELL_PAD_CTRL_R2:
button_id = pad_button::R2;
break;
case CELL_PAD_CTRL_PS:
button_id = pad_button::ps;
break;
default:
break;
}

View File

@ -46,6 +46,7 @@ namespace rsx
static constexpr u16 virtual_width = 1280;
static constexpr u16 virtual_height = 720;
bool use_window_space = false;
u32 min_refresh_duration_us = 16600;
atomic_t<bool> visible = false;
@ -57,6 +58,9 @@ namespace rsx
virtual compiled_resource get_compiled() = 0;
void refresh() const;
virtual u16 get_virtual_width() const { return virtual_width; }
virtual u16 get_virtual_height() const { return virtual_height; }
virtual void set_render_viewport(u16 /*width*/, u16 /*height*/) {}
};
// Interactable UI element

View File

@ -343,7 +343,8 @@ namespace rsx::assembler::FP
std::unordered_set<u32> inputs;
SRC_Common src { .HEX = instruction->bytecode[operand + 1] };
if (src.reg_type != RSX_FP_REGISTER_TYPE_TEMP)
if (src.reg_type != RSX_FP_REGISTER_TYPE_TEMP &&
src.reg_type != RSX_FP_REGISTER_TYPE_INPUT)
{
return 0;
}

View File

@ -418,6 +418,11 @@ namespace glsl
enabled_options.push_back("_ENABLE_DEPTH_FORMAT_RECONSTRUCTION");
}
if (props.require_msaa_ops)
{
enabled_options.push_back("_ENABLE_TEXTURE_MULTISAMPLE");
}
program_common::define_glsl_switches(OS, enabled_options);
enabled_options.clear();

View File

@ -13,6 +13,11 @@ R"(
#define SAMPLER_MODE_FONT3D 2
#define SAMPLER_MODE_TEXTURE2D 3
#define SDF_DISABLED 0
#define SDF_ELLIPSE 1
#define SDF_BOX 2
#define SDF_ROUND_BOX 3
#ifdef VULKAN
layout(set=0, binding=0) uniform sampler2D fs0;
layout(set=0, binding=1) uniform sampler2DArray fs1;
@ -34,11 +39,17 @@ layout(%push_block) uniform FragmentConfiguration
uint fragment_config;
float timestamp;
float blur_intensity;
vec4 sdf_params;
vec4 sdf_origin;
vec4 sdf_border_color;
};
#else
uniform uint fragment_config;
uniform float timestamp;
uniform float blur_intensity;
uniform vec4 sdf_params;
uniform vec2 sdf_origin;
uniform vec4 sdf_border_color;
#endif
struct config_t
@ -46,6 +57,7 @@ struct config_t
bool clip_fragments;
bool use_pulse_glow;
uint sampler_mode;
uint sdf;
};
config_t unpack_fragment_options()
@ -54,9 +66,64 @@ config_t unpack_fragment_options()
result.clip_fragments = bitfieldExtract(fragment_config, 0, 1) != 0;
result.use_pulse_glow = bitfieldExtract(fragment_config, 1, 1) != 0;
result.sampler_mode = bitfieldExtract(fragment_config, 2, 2);
result.sdf = bitfieldExtract(fragment_config, 4, 2);
return result;
}
vec4 SDF_blend(
const in float sd,
const in float border_width,
const in vec4 inner_color,
const in vec4 border_color,
const in vec4 outer_color)
{
// Crucially, we need to get the derivative without subracting the border width.
// Subtracting the border width makes the function non-continuous and makes the jaggies hard to get rid of.
float fw = fwidth(sd);
// Compute the two transition points. The inner edge is of course biased by the border amount as the clamping point
// Treat smoothstep as fancy clamp where e0 < x < e1
float a = smoothstep(-border_width + fw, -border_width - fw, sd); // inner edge transition
float b = smoothstep(fw, -fw, sd); // outer edge transition
// Mix the 3 colors with the transition values.
vec4 color = mix(outer_color, border_color, b);
color = mix(color, inner_color, a);
return color;
}
float SDF_fn(const in uint sdf)
{
const vec2 p = floor(gl_FragCoord.xy) - sdf_origin.xy; // Screen-spac distance
const vec2 hs = sdf_params.xy; // Half size
const float r = sdf_params.z; // Radius (for round box, ellipses use half size instead)
vec2 v; // Scratch
float d; // Distance calculated
switch (sdf)
{
case SDF_ELLIPSE:
// Slightly inaccurate hack, but good enough for classification and allows oval shapes
d = length(p / hs) - 1.f;
// Now we need to correct for the border because the circle was scaled down to a unit
return d * length(hs);
case SDF_BOX:
// Insanity, reduced junction of 3 functions
// If for each axis the axis-aligned distance = D then you can select/clamp each axis separately by doing a max(D, 0) on all dimensions
// Length then does the squareroot transformation.
// The second term is to add back the inner distance which is useful for rendering borders
v = abs(p) - hs;
return length(max(v, 0.f)) + min(max(v.x, v.y), 0.0);
case SDF_ROUND_BOX:
// Modified BOX SDF.
// The half box size is shrunk by R in it's diagonal, but we add radius back into the output to bias the output again
v = abs(p) - (hs - r);
return length(max(v, 0.f)) + min(max(v.x, v.y), 0.0) - r;
default:
return -1.f;
}
}
vec4 blur_sample(sampler2D tex, vec2 coord, vec2 tex_offset)
{
vec2 coords[9];
@ -125,6 +192,13 @@ void main()
diff_color.a *= (sin(timestamp) + 1.f) * 0.5f;
}
if (config.sdf != SDF_DISABLED)
{
const float border_w = sdf_params.w; // Border width
const float d = SDF_fn(config.sdf);
diff_color = SDF_blend(d, border_w, diff_color, sdf_border_color, vec4(0.));
}
switch (config.sampler_mode)
{
default:

View File

@ -46,7 +46,7 @@ R"(
_texture_bx2_active = false; \
} while (false)
#define TEX_FLAGS(index) ((TEX_PARAM(index).flags & ~(_texture_flag_erase)) | _texture_flag_override)
#elif defined(_ENABLE_TEXTURE_ALPHA_KILL) || defined(_ENABLE_FORMAT_CONVERSION) || defined(_ENABLE_DEPTH_FORMAT_RECONSTRUCTION)
#elif defined(_ENABLE_TEXTURE_ALPHA_KILL) || defined(_ENABLE_FORMAT_CONVERSION) || defined(_ENABLE_DEPTH_FORMAT_RECONSTRUCTION) || defined(_ENABLE_TEXTURE_MULTISAMPLE)
#define TEX_FLAGS(index) (TEX_PARAM(index).flags)
#else
#define TEX_FLAGS(index) 0

View File

@ -23,7 +23,8 @@ namespace rsx
{
fragment_clip_bit = 0,
pulse_glow_bit = 1,
sampling_mode_bit = 2
sampling_mode_bit = 2,
sdf_func_offset_bit = 4
};
public:
@ -51,6 +52,13 @@ namespace rsx
return *this;
}
fragment_options& set_sdf(sdf_function func)
{
value &= ~(0x3 << e_offsets::sdf_func_offset_bit);
value |= (static_cast<u32>(func) << e_offsets::sdf_func_offset_bit);
return *this;
}
u32 get() const
{
return value;
@ -74,6 +82,13 @@ namespace rsx
}
}
void set_bits(u32 offset, u32 count, u32 set)
{
const u32 mask = (0xffffffffu >> (32 - count)) << offset;
value &= ~mask;
value |= set;
}
public:
vertex_options& disable_vertex_snap(bool enable)
{

View File

@ -712,9 +712,12 @@ namespace rsx
{
m_overlay_manager = g_fxo->init<rsx::overlays::display_manager>(0);
if (const std::string audio_path = Emu.GetSfoDir(true) + "/SND0.AT3"; fs::is_file(audio_path))
if (g_cfg.misc.play_music_during_boot)
{
m_overlay_manager->start_audio(audio_path);
if (const std::string audio_path = Emu.GetSfoDir(true) + "/SND0.AT3"; fs::is_file(audio_path))
{
m_overlay_manager->start_audio(audio_path);
}
}
}
@ -996,6 +999,12 @@ namespace rsx
fifo_ctrl = std::make_unique<::rsx::FIFO::FIFO_control>(this);
fifo_ctrl->set_get(ctrl->get);
resolution_scaling_config =
{
.scale_percent = static_cast<u16>(g_cfg.video.resolution_scale_percent),
.min_scalable_dimension = static_cast<u16>(g_cfg.video.min_scalable_dimension),
};
last_guest_flip_timestamp = get_system_time() - 1000000;
vblank_count = 0;
@ -1862,6 +1871,7 @@ namespace rsx
}
default:
rsx_log.fatal("Unhandled framebuffer option changed 0x%x", opt);
break;
}
}
@ -1940,8 +1950,8 @@ namespace rsx
m_graphics_state.set(rsx::rtt_config_valid);
}
std::tie(region.x1, region.y1) = rsx::apply_resolution_scale<false>(x1, y1, m_framebuffer_layout.width, m_framebuffer_layout.height);
std::tie(region.x2, region.y2) = rsx::apply_resolution_scale<true>(x2, y2, m_framebuffer_layout.width, m_framebuffer_layout.height);
std::tie(region.x1, region.y1) = rsx::apply_resolution_scale<false>(resolution_scaling_config, x1, y1, m_framebuffer_layout.width, m_framebuffer_layout.height);
std::tie(region.x2, region.y2) = rsx::apply_resolution_scale<true>(resolution_scaling_config, x2, y2, m_framebuffer_layout.width, m_framebuffer_layout.height);
return true;
}
@ -2768,9 +2778,9 @@ namespace rsx
recovered_fifo_cmds_history.push({fifo_ctrl->last_cmd(), current_time});
}
std::string thread::dump_misc() const
void thread::dump_misc(std::string& ret, std::any& custom_data) const
{
std::string ret = cpu_thread::dump_misc();
cpu_thread::dump_misc(ret, custom_data);
const auto flags = +state;
@ -2783,8 +2793,6 @@ namespace rsx
{
fmt::append(ret, "\n");
}
return ret;
}
std::vector<std::pair<u32, u32>> thread::dump_callstack_list() const

View File

@ -122,7 +122,7 @@ namespace rsx
std::unique_ptr<FIFO::FIFO_control> fifo_ctrl;
atomic_t<bool> rsx_thread_running{ false };
std::vector<std::pair<u32, u32>> dump_callstack_list() const override;
std::string dump_misc() const override;
void dump_misc(std::string& ret, std::any& custom_data) const override;
protected:
FIFO::flattening_helper m_flattener;
@ -215,6 +215,8 @@ namespace rsx
atomic_bitmask_t<flip_request> async_flip_requested{};
u8 async_flip_buffer{ 0 };
surface_scaling_config_t resolution_scaling_config{};
void capture_frame(const std::string& name);
const backend_configuration& get_backend_config() const { return backend_config; }

View File

@ -335,7 +335,7 @@ namespace rsx
auto scale_result = [](u32 value)
{
const auto scale = rsx::get_resolution_scale_percent();
const auto scale = get_current_renderer()->resolution_scaling_config.scale_percent;
const auto result = (value * 10000ull) / (scale * scale);
return std::max(1u, static_cast<u32>(result));
};

Some files were not shown because too many files have changed in this diff Show More