mirror of
https://github.com/RPCS3/rpcs3.git
synced 2026-04-08 18:41:30 -06:00
Merge branch 'master' into fix/apple-arm64-memory-decommit-race
This commit is contained in:
commit
8ffba04e77
@ -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
|
||||
|
||||
@ -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}"
|
||||
|
||||
14
.github/workflows/rpcs3.yml
vendored
14
.github/workflows/rpcs3.yml
vendored
@ -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
3
.gitignore
vendored
@ -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
2
3rdparty/FAudio
vendored
@ -1 +1 @@
|
||||
Subproject commit dc034fc671b07bbd14e8410d5dd6be6da38fdf6d
|
||||
Subproject commit 0372329dbb56e7814d0dea7b6eafa7a613bd8042
|
||||
2
3rdparty/SoundTouch/soundtouch
vendored
2
3rdparty/SoundTouch/soundtouch
vendored
@ -1 +1 @@
|
||||
Subproject commit 3982730833b6daefe77dcfb32b5c282851640c17
|
||||
Subproject commit a0fba77b6f9cfbdb71f8bbec58b6ac4e5e3b1097
|
||||
3
3rdparty/curl/CMakeLists.txt
vendored
3
3rdparty/curl/CMakeLists.txt
vendored
@ -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()
|
||||
|
||||
2
3rdparty/libpng/libpng
vendored
2
3rdparty/libpng/libpng
vendored
@ -1 +1 @@
|
||||
Subproject commit c3e304954a9cfd154bc0dfbfea2b01cd61d6546d
|
||||
Subproject commit d5515b5b8be3901aac04e5bd8bd5c89f287bcd33
|
||||
2
3rdparty/libsdl-org/SDL
vendored
2
3rdparty/libsdl-org/SDL
vendored
@ -1 +1 @@
|
||||
Subproject commit 683181b47cfabd293e3ea409f838915b8297a4fd
|
||||
Subproject commit 5848e584a1b606de26e3dbd1c7e4ecbc34f807a6
|
||||
2
3rdparty/protobuf/CMakeLists.txt
vendored
2
3rdparty/protobuf/CMakeLists.txt
vendored
@ -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)
|
||||
|
||||
2
3rdparty/wolfssl/wolfssl
vendored
2
3rdparty/wolfssl/wolfssl
vendored
@ -1 +1 @@
|
||||
Subproject commit b077c81eb635392e694ccedbab8b644297ec0285
|
||||
Subproject commit 922d04b3568c6428a9fb905ddee3ef5a68db3108
|
||||
3
3rdparty/zlib/CMakeLists.txt
vendored
3
3rdparty/zlib/CMakeLists.txt
vendored
@ -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)
|
||||
|
||||
10
BUILDING.md
10
BUILDING.md
@ -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**)
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -683,6 +683,7 @@ namespace fs
|
||||
notempty,
|
||||
readonly,
|
||||
isdir,
|
||||
notdir,
|
||||
toolong,
|
||||
nospace,
|
||||
xdev,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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 <>
|
||||
|
||||
@ -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)
|
||||
{
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -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
@ -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
|
||||
{
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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>>();
|
||||
|
||||
|
||||
@ -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<>
|
||||
|
||||
@ -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();
|
||||
}
|
||||
};
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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)); \
|
||||
|
||||
@ -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
@ -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)>
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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 },
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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 };
|
||||
|
||||
|
||||
@ -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)
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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();
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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 });
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 };
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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>();
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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());
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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();
|
||||
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
{
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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; }
|
||||
|
||||
|
||||
@ -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
Loading…
Reference in New Issue
Block a user