diff --git a/.ci/setup-llvm.sh b/.ci/setup-llvm.sh index d296d2a3e4..5d06222f04 100644 --- a/.ci/setup-llvm.sh +++ b/.ci/setup-llvm.sh @@ -1,7 +1,7 @@ #!/bin/sh -ex # Resource/dependency URLs -CCACHE_URL="https://github.com/ccache/ccache/releases/download/v4.11.2/ccache-4.11.2-windows-x86_64.zip" +CCACHE_URL="https://github.com/ccache/ccache/releases/download/v4.12.3/ccache-4.12.3-windows-x86_64.zip" DEP_URLS=" \ $CCACHE_URL" diff --git a/.ci/setup-windows.sh b/.ci/setup-windows.sh index aade55fc95..cee6d624de 100755 --- a/.ci/setup-windows.sh +++ b/.ci/setup-windows.sh @@ -17,7 +17,7 @@ QT_SVG_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qtsvg${QT_SUFFIX}" QT_TRANSLATIONS_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qttranslations${QT_SUFFIX}" LLVMLIBS_URL="https://github.com/RPCS3/llvm-mirror/releases/download/custom-build-win-${LLVM_VER}/llvmlibs_mt.7z" VULKAN_SDK_URL="https://www.dropbox.com/scl/fi/sjjh0fc4ld281pjbl2xzu/VulkanSDK-${VULKAN_VER}-Installer.exe?rlkey=f6wzc0lvms5vwkt2z3qabfv9d&dl=1" -CCACHE_URL="https://github.com/ccache/ccache/releases/download/v4.11.2/ccache-4.11.2-windows-x86_64.zip" +CCACHE_URL="https://github.com/ccache/ccache/releases/download/v4.12.3/ccache-4.12.3-windows-x86_64.zip" DEP_URLS=" \ $QT_BASE_URL \ diff --git a/.github/workflows/llvm.yml b/.github/workflows/llvm.yml index e3e3e76c50..3ed584437a 100644 --- a/.github/workflows/llvm.yml +++ b/.github/workflows/llvm.yml @@ -20,7 +20,7 @@ jobs: runs-on: windows-2025 env: COMPILER: msvc - CCACHE_SHA: '1f39f3ad5aae3fe915e99ad1302633bc8f6718e58fa7c0de2b0ba7e080f0f08c' + CCACHE_SHA: '859141059ac950e1e8cd042c66f842f26b9e3a62a1669a69fe6ba180cb58bbdf' CCACHE_BIN_DIR: 'C:\ccache_bin' CCACHE_DIR: 'C:\ccache' CCACHE_INODECACHE: 'true' diff --git a/.github/workflows/rpcs3.yml b/.github/workflows/rpcs3.yml index fbf7892b96..05a806f345 100644 --- a/.github/workflows/rpcs3.yml +++ b/.github/workflows/rpcs3.yml @@ -33,23 +33,23 @@ jobs: matrix: include: - os: ubuntu-24.04 - docker_img: "rpcs3/rpcs3-ci-jammy:1.7" + docker_img: "rpcs3/rpcs3-ci-jammy:1.8" 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.7" + docker_img: "rpcs3/rpcs3-ci-jammy:1.8" build_sh: "/rpcs3/.ci/build-linux.sh" compiler: gcc - os: ubuntu-24.04-arm - docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.7" + docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.8" 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.7" + docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.8" build_sh: "/rpcs3/.ci/build-linux-aarch64.sh" compiler: gcc name: RPCS3 Linux ${{ matrix.os }} ${{ matrix.compiler }} @@ -137,7 +137,7 @@ jobs: runs-on: macos-14 env: CCACHE_DIR: /tmp/ccache_dir - QT_VER: '6.10.1' + QT_VER: '6.10.2' QT_VER_MAIN: '6' LLVM_COMPILER_VER: '21' RELEASE_MESSAGE: ../GitHubReleaseMessage.txt @@ -216,13 +216,13 @@ jobs: env: COMPILER: msvc QT_VER_MAIN: '6' - QT_VER: '6.10.1' + QT_VER: '6.10.2' QT_VER_MSVC: 'msvc2022' - QT_DATE: '202511161843' + QT_DATE: '202601261212' LLVM_VER: '19.1.7' VULKAN_VER: '1.3.268.0' VULKAN_SDK_SHA: '8459ef49bd06b697115ddd3d97c9aec729e849cd775f5be70897718a9b3b9db5' - CCACHE_SHA: '1f39f3ad5aae3fe915e99ad1302633bc8f6718e58fa7c0de2b0ba7e080f0f08c' + CCACHE_SHA: '859141059ac950e1e8cd042c66f842f26b9e3a62a1669a69fe6ba180cb58bbdf' CCACHE_BIN_DIR: 'C:\ccache_bin' CCACHE_DIR: 'C:\ccache' CCACHE_INODECACHE: 'true' diff --git a/3rdparty/7zip/7zip b/3rdparty/7zip/7zip index 5e96a82794..839151eaaa 160000 --- a/3rdparty/7zip/7zip +++ b/3rdparty/7zip/7zip @@ -1 +1 @@ -Subproject commit 5e96a8279489832924056b1fa82f29d5837c9469 +Subproject commit 839151eaaad24771892afaae6bac690e31e58384 diff --git a/3rdparty/FAudio b/3rdparty/FAudio index 633bdb772a..e67d761ead 160000 --- a/3rdparty/FAudio +++ b/3rdparty/FAudio @@ -1 +1 @@ -Subproject commit 633bdb772a593104414b4b103ec752567d57c3c1 +Subproject commit e67d761ead486de3e69fa11705456bf94df734ca diff --git a/3rdparty/GL/glext.h b/3rdparty/GL/glext.h index 276a962a96..16c26be10f 100644 --- a/3rdparty/GL/glext.h +++ b/3rdparty/GL/glext.h @@ -6,7 +6,7 @@ extern "C" { #endif /* -** Copyright 2013-2020 The Khronos Group Inc. +** Copyright 2013-2026 The Khronos Group Inc. ** SPDX-License-Identifier: MIT ** ** This header is generated from the Khronos OpenGL / OpenGL ES XML @@ -32,7 +32,7 @@ extern "C" { #define GLAPI extern #endif -#define GL_GLEXT_VERSION 20250203 +#define GL_GLEXT_VERSION 20260126 #include @@ -7358,6 +7358,47 @@ GLAPI void APIENTRY glFogCoordPointerEXT (GLenum type, GLsizei stride, const voi #endif #endif /* GL_EXT_fog_coord */ +#ifndef GL_EXT_fragment_shading_rate +#define GL_EXT_fragment_shading_rate 1 +#define GL_SHADING_RATE_1X1_PIXELS_EXT 0x96A6 +#define GL_SHADING_RATE_1X2_PIXELS_EXT 0x96A7 +#define GL_SHADING_RATE_2X1_PIXELS_EXT 0x96A8 +#define GL_SHADING_RATE_2X2_PIXELS_EXT 0x96A9 +#define GL_SHADING_RATE_1X4_PIXELS_EXT 0x96AA +#define GL_SHADING_RATE_4X1_PIXELS_EXT 0x96AB +#define GL_SHADING_RATE_4X2_PIXELS_EXT 0x96AC +#define GL_SHADING_RATE_2X4_PIXELS_EXT 0x96AD +#define GL_SHADING_RATE_4X4_PIXELS_EXT 0x96AE +#define GL_SHADING_RATE_EXT 0x96D0 +#define GL_SHADING_RATE_ATTACHMENT_EXT 0x96D1 +#define GL_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_EXT 0x96D2 +#define GL_FRAGMENT_SHADING_RATE_COMBINER_OP_REPLACE_EXT 0x96D3 +#define GL_FRAGMENT_SHADING_RATE_COMBINER_OP_MIN_EXT 0x96D4 +#define GL_FRAGMENT_SHADING_RATE_COMBINER_OP_MAX_EXT 0x96D5 +#define GL_FRAGMENT_SHADING_RATE_COMBINER_OP_MUL_EXT 0x96D6 +#define GL_MIN_FRAGMENT_SHADING_RATE_ATTACHMENT_TEXEL_WIDTH_EXT 0x96D7 +#define GL_MAX_FRAGMENT_SHADING_RATE_ATTACHMENT_TEXEL_WIDTH_EXT 0x96D8 +#define GL_MIN_FRAGMENT_SHADING_RATE_ATTACHMENT_TEXEL_HEIGHT_EXT 0x96D9 +#define GL_MAX_FRAGMENT_SHADING_RATE_ATTACHMENT_TEXEL_HEIGHT_EXT 0x96DA +#define GL_MAX_FRAGMENT_SHADING_RATE_ATTACHMENT_TEXEL_ASPECT_RATIO_EXT 0x96DB +#define GL_MAX_FRAGMENT_SHADING_RATE_ATTACHMENT_LAYERS_EXT 0x96DC +#define GL_FRAGMENT_SHADING_RATE_WITH_SHADER_DEPTH_STENCIL_WRITES_SUPPORTED_EXT 0x96DD +#define GL_FRAGMENT_SHADING_RATE_WITH_SAMPLE_MASK_SUPPORTED_EXT 0x96DE +#define GL_FRAGMENT_SHADING_RATE_ATTACHMENT_WITH_DEFAULT_FRAMEBUFFER_SUPPORTED_EXT 0x96DF +#define GL_FRAGMENT_SHADING_RATE_NON_TRIVIAL_COMBINERS_SUPPORTED_EXT 0x8F6F +#define GL_FRAGMENT_SHADING_RATE_PRIMITIVE_RATE_WITH_MULTI_VIEWPORT_SUPPORTED_EXT 0x9780 +typedef void (APIENTRYP PFNGLGETFRAGMENTSHADINGRATESEXTPROC) (GLsizei samples, GLsizei maxCount, GLsizei *count, GLenum *shadingRates); +typedef void (APIENTRYP PFNGLSHADINGRATEEXTPROC) (GLenum rate); +typedef void (APIENTRYP PFNGLSHADINGRATECOMBINEROPSEXTPROC) (GLenum combinerOp0, GLenum combinerOp1); +typedef void (APIENTRYP PFNGLFRAMEBUFFERSHADINGRATEEXTPROC) (GLenum target, GLenum attachment, GLuint texture, GLint baseLayer, GLsizei numLayers, GLsizei texelWidth, GLsizei texelHeight); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetFragmentShadingRatesEXT (GLsizei samples, GLsizei maxCount, GLsizei *count, GLenum *shadingRates); +GLAPI void APIENTRY glShadingRateEXT (GLenum rate); +GLAPI void APIENTRY glShadingRateCombinerOpsEXT (GLenum combinerOp0, GLenum combinerOp1); +GLAPI void APIENTRY glFramebufferShadingRateEXT (GLenum target, GLenum attachment, GLuint texture, GLint baseLayer, GLsizei numLayers, GLsizei texelWidth, GLsizei texelHeight); +#endif +#endif /* GL_EXT_fragment_shading_rate */ + #ifndef GL_EXT_framebuffer_blit #define GL_EXT_framebuffer_blit 1 #define GL_READ_FRAMEBUFFER_EXT 0x8CA8 @@ -7816,6 +7857,86 @@ GLAPI void APIENTRY glImportMemoryWin32NameEXT (GLuint memory, GLuint64 size, GL #endif #endif /* GL_EXT_memory_object_win32 */ +#ifndef GL_EXT_mesh_shader +#define GL_EXT_mesh_shader 1 +#define GL_MESH_SHADER_EXT 0x9559 +#define GL_TASK_SHADER_EXT 0x955A +#define GL_MAX_MESH_UNIFORM_BLOCKS_EXT 0x8E60 +#define GL_MAX_MESH_TEXTURE_IMAGE_UNITS_EXT 0x8E61 +#define GL_MAX_MESH_IMAGE_UNIFORMS_EXT 0x8E62 +#define GL_MAX_MESH_UNIFORM_COMPONENTS_EXT 0x8E63 +#define GL_MAX_MESH_ATOMIC_COUNTER_BUFFERS_EXT 0x8E64 +#define GL_MAX_MESH_ATOMIC_COUNTERS_EXT 0x8E65 +#define GL_MAX_MESH_SHADER_STORAGE_BLOCKS_EXT 0x8E66 +#define GL_MAX_COMBINED_MESH_UNIFORM_COMPONENTS_EXT 0x8E67 +#define GL_MAX_TASK_UNIFORM_BLOCKS_EXT 0x8E68 +#define GL_MAX_TASK_TEXTURE_IMAGE_UNITS_EXT 0x8E69 +#define GL_MAX_TASK_IMAGE_UNIFORMS_EXT 0x8E6A +#define GL_MAX_TASK_UNIFORM_COMPONENTS_EXT 0x8E6B +#define GL_MAX_TASK_ATOMIC_COUNTER_BUFFERS_EXT 0x8E6C +#define GL_MAX_TASK_ATOMIC_COUNTERS_EXT 0x8E6D +#define GL_MAX_TASK_SHADER_STORAGE_BLOCKS_EXT 0x8E6E +#define GL_MAX_COMBINED_TASK_UNIFORM_COMPONENTS_EXT 0x8E6F +#define GL_MAX_TASK_WORK_GROUP_TOTAL_COUNT_EXT 0x9740 +#define GL_MAX_MESH_WORK_GROUP_TOTAL_COUNT_EXT 0x9741 +#define GL_MAX_MESH_WORK_GROUP_INVOCATIONS_EXT 0x9757 +#define GL_MAX_TASK_WORK_GROUP_INVOCATIONS_EXT 0x9759 +#define GL_MAX_TASK_PAYLOAD_SIZE_EXT 0x9742 +#define GL_MAX_TASK_SHARED_MEMORY_SIZE_EXT 0x9743 +#define GL_MAX_MESH_SHARED_MEMORY_SIZE_EXT 0x9744 +#define GL_MAX_TASK_PAYLOAD_AND_SHARED_MEMORY_SIZE_EXT 0x9745 +#define GL_MAX_MESH_PAYLOAD_AND_SHARED_MEMORY_SIZE_EXT 0x9746 +#define GL_MAX_MESH_OUTPUT_MEMORY_SIZE_EXT 0x9747 +#define GL_MAX_MESH_PAYLOAD_AND_OUTPUT_MEMORY_SIZE_EXT 0x9748 +#define GL_MAX_MESH_OUTPUT_VERTICES_EXT 0x9538 +#define GL_MAX_MESH_OUTPUT_PRIMITIVES_EXT 0x9756 +#define GL_MAX_MESH_OUTPUT_COMPONENTS_EXT 0x9749 +#define GL_MAX_MESH_OUTPUT_LAYERS_EXT 0x974A +#define GL_MAX_MESH_MULTIVIEW_VIEW_COUNT_EXT 0x9557 +#define GL_MESH_OUTPUT_PER_VERTEX_GRANULARITY_EXT 0x92DF +#define GL_MESH_OUTPUT_PER_PRIMITIVE_GRANULARITY_EXT 0x9543 +#define GL_MAX_PREFERRED_TASK_WORK_GROUP_INVOCATIONS_EXT 0x974B +#define GL_MAX_PREFERRED_MESH_WORK_GROUP_INVOCATIONS_EXT 0x974C +#define GL_MESH_PREFERS_LOCAL_INVOCATION_VERTEX_OUTPUT_EXT 0x974D +#define GL_MESH_PREFERS_LOCAL_INVOCATION_PRIMITIVE_OUTPUT_EXT 0x974E +#define GL_MESH_PREFERS_COMPACT_VERTEX_OUTPUT_EXT 0x974F +#define GL_MESH_PREFERS_COMPACT_PRIMITIVE_OUTPUT_EXT 0x9750 +#define GL_MAX_TASK_WORK_GROUP_COUNT_EXT 0x9751 +#define GL_MAX_MESH_WORK_GROUP_COUNT_EXT 0x9752 +#define GL_MAX_MESH_WORK_GROUP_SIZE_EXT 0x9758 +#define GL_MAX_TASK_WORK_GROUP_SIZE_EXT 0x975A +#define GL_MESH_WORK_GROUP_SIZE_EXT 0x953E +#define GL_TASK_WORK_GROUP_SIZE_EXT 0x953F +#define GL_MESH_VERTICES_OUT_EXT 0x9579 +#define GL_MESH_PRIMITIVES_OUT_EXT 0x957A +#define GL_MESH_OUTPUT_TYPE_EXT 0x957B +#define GL_UNIFORM_BLOCK_REFERENCED_BY_MESH_SHADER_EXT 0x959C +#define GL_UNIFORM_BLOCK_REFERENCED_BY_TASK_SHADER_EXT 0x959D +#define GL_REFERENCED_BY_MESH_SHADER_EXT 0x95A0 +#define GL_REFERENCED_BY_TASK_SHADER_EXT 0x95A1 +#define GL_TASK_SHADER_INVOCATIONS_EXT 0x9753 +#define GL_MESH_SHADER_INVOCATIONS_EXT 0x9754 +#define GL_MESH_PRIMITIVES_GENERATED_EXT 0x9755 +#define GL_MESH_SHADER_BIT_EXT 0x00000040 +#define GL_TASK_SHADER_BIT_EXT 0x00000080 +#define GL_MESH_SUBROUTINE_EXT 0x957C +#define GL_TASK_SUBROUTINE_EXT 0x957D +#define GL_MESH_SUBROUTINE_UNIFORM_EXT 0x957E +#define GL_TASK_SUBROUTINE_UNIFORM_EXT 0x957F +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_MESH_SHADER_EXT 0x959E +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TASK_SHADER_EXT 0x959F +typedef void (APIENTRYP PFNGLDRAWMESHTASKSEXTPROC) (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z); +typedef void (APIENTRYP PFNGLDRAWMESHTASKSINDIRECTEXTPROC) (GLintptr indirect); +typedef void (APIENTRYP PFNGLMULTIDRAWMESHTASKSINDIRECTEXTPROC) (GLintptr indirect, GLsizei drawcount, GLsizei stride); +typedef void (APIENTRYP PFNGLMULTIDRAWMESHTASKSINDIRECTCOUNTEXTPROC) (GLintptr indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawMeshTasksEXT (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z); +GLAPI void APIENTRY glDrawMeshTasksIndirectEXT (GLintptr indirect); +GLAPI void APIENTRY glMultiDrawMeshTasksIndirectEXT (GLintptr indirect, GLsizei drawcount, GLsizei stride); +GLAPI void APIENTRY glMultiDrawMeshTasksIndirectCountEXT (GLintptr indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +#endif +#endif /* GL_EXT_mesh_shader */ + #ifndef GL_EXT_misc_attribute #define GL_EXT_misc_attribute 1 #endif /* GL_EXT_misc_attribute */ diff --git a/3rdparty/cubeb/cubeb b/3rdparty/cubeb/cubeb index e495bee4cd..484857522c 160000 --- a/3rdparty/cubeb/cubeb +++ b/3rdparty/cubeb/cubeb @@ -1 +1 @@ -Subproject commit e495bee4cd630c9f99907a764e16edba37a4b564 +Subproject commit 484857522c73318c06f18ba0a3e17525fa98c608 diff --git a/3rdparty/libpng/libpng b/3rdparty/libpng/libpng index 4e3f57d50f..c3e304954a 160000 --- a/3rdparty/libpng/libpng +++ b/3rdparty/libpng/libpng @@ -1 +1 @@ -Subproject commit 4e3f57d50f552841550a36eabbb3fbcecacb7750 +Subproject commit c3e304954a9cfd154bc0dfbfea2b01cd61d6546d diff --git a/3rdparty/protobuf/CMakeLists.txt b/3rdparty/protobuf/CMakeLists.txt index 6e36be5716..6c4b35e99d 100644 --- a/3rdparty/protobuf/CMakeLists.txt +++ b/3rdparty/protobuf/CMakeLists.txt @@ -2,8 +2,8 @@ add_library(3rdparty_protobuf INTERFACE) if (USE_SYSTEM_PROTOBUF) pkg_check_modules(PROTOBUF REQUIRED IMPORTED_TARGET protobuf>=33.0.0) target_link_libraries(3rdparty_protobuf INTERFACE PkgConfig::PROTOBUF) - set(PROTOBUF_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../rpcs3/Emu/NP/generated/") - execute_process(COMMAND protoc --cpp_out="${PROTOBUF_DIR}" --proto_path="${PROTOBUF_DIR}" np2_structs.proto RESULT_VARIABLE PROTOBUF_CMD_ERROR) + set(PROTOBUF_DIR "${CMAKE_SOURCE_DIR}/rpcs3/Emu/NP/generated") + execute_process(COMMAND protoc --cpp_out=${PROTOBUF_DIR} --proto_path=${PROTOBUF_DIR} np2_structs.proto RESULT_VARIABLE PROTOBUF_CMD_ERROR) if(PROTOBUF_CMD_ERROR AND NOT PROTOBUF_CMD_ERROR EQUAL 0) message(FATAL_ERROR "protoc failed to regenerate protobuf files.") endif() diff --git a/3rdparty/yaml-cpp/yaml-cpp b/3rdparty/yaml-cpp/yaml-cpp index 456c68f452..05c44fcd18 160000 --- a/3rdparty/yaml-cpp/yaml-cpp +++ b/3rdparty/yaml-cpp/yaml-cpp @@ -1 +1 @@ -Subproject commit 456c68f452da09d8ca84b375faa2b1397713eaba +Subproject commit 05c44fcd18074836e21e1eda9fc02b3a4a1529b5 diff --git a/3rdparty/yaml-cpp/yaml-cpp.vcxproj b/3rdparty/yaml-cpp/yaml-cpp.vcxproj index b1b732727c..4d10c90d7e 100644 --- a/3rdparty/yaml-cpp/yaml-cpp.vcxproj +++ b/3rdparty/yaml-cpp/yaml-cpp.vcxproj @@ -76,6 +76,7 @@ + diff --git a/3rdparty/yaml-cpp/yaml-cpp.vcxproj.filters b/3rdparty/yaml-cpp/yaml-cpp.vcxproj.filters index 60c75fa23e..f4b553ad72 100644 --- a/3rdparty/yaml-cpp/yaml-cpp.vcxproj.filters +++ b/3rdparty/yaml-cpp/yaml-cpp.vcxproj.filters @@ -94,5 +94,8 @@ Source Files + + Source Files + \ No newline at end of file diff --git a/BUILDING.md b/BUILDING.md index 26295d0a16..597621e810 100644 --- a/BUILDING.md +++ b/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.1](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.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) - [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. `\6.10.1\msvc2022_64\` +- add and set the `QTDIR` environment variable, e.g. `\6.10.2\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. `\6.10.1\msvc2022_64\` +- add and set the `Qt6_ROOT` environment variable to the **Qt** libs path, e.g. `\6.10.2\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.1](https://www.qt.io/download-qt-installer) +- [Qt 6.10.2](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) @@ -95,7 +95,7 @@ sudo apt-get install cmake #### Fedora - sudo dnf install alsa-lib-devel cmake ninja-build glew glew-devel libatomic libevdev-devel libudev-devel openal-devel qt6-qtbase-devel qt6-qtbase-private-devel vulkan-devel pipewire-jack-audio-connection-kit-devel qt6-qtmultimedia-devel qt6-qtsvg-devel llvm-devel + sudo dnf install alsa-lib-devel cmake ninja-build glew glew-devel libatomic libevdev-devel libudev-devel openal-soft-devel qt6-qtbase-devel qt6-qtbase-private-devel vulkan-devel pipewire-jack-audio-connection-kit-devel qt6-qtmultimedia-devel qt6-qtsvg-devel llvm-devel libcurl-devel #### OpenSUSE @@ -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. `\6.10.1\msvc2022_64`, version will fill in automatically +2) add the path to your Qt installation with compiler e.g. `\6.10.2\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**) diff --git a/CMakeLists.txt b/CMakeLists.txt index bf16ca4983..50cf946fda 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,12 +13,12 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON) if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 11) - message(FATAL_ERROR "RPCS3 requires at least gcc-11.") + if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 13) + message(FATAL_ERROR "RPCS3 requires at least gcc-13.") endif() elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 12.0) - message(FATAL_ERROR "RPCS3 requires at least clang-12.0.") + if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.0) + message(FATAL_ERROR "RPCS3 requires at least clang-19.0.") endif() endif() diff --git a/Utilities/File.cpp b/Utilities/File.cpp index 30e6414675..d2adb22c61 100644 --- a/Utilities/File.cpp +++ b/Utilities/File.cpp @@ -398,12 +398,11 @@ namespace fs class windows_file final : public file_base { HANDLE m_handle; - atomic_t m_pos; + atomic_t m_pos {0}; public: windows_file(HANDLE handle) : m_handle(handle) - , m_pos(0) { } @@ -417,10 +416,10 @@ namespace fs stat_t get_stat() override { - FILE_BASIC_INFO basic_info; + FILE_BASIC_INFO basic_info {}; ensure(GetFileInformationByHandleEx(m_handle, FileBasicInfo, &basic_info, sizeof(FILE_BASIC_INFO))); // "file::stat" - stat_t info; + stat_t info {}; info.is_directory = (basic_info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; info.is_writable = (basic_info.FileAttributes & FILE_ATTRIBUTE_READONLY) == 0; info.size = this->size(); @@ -441,7 +440,7 @@ namespace fs bool trunc(u64 length) override { - FILE_END_OF_FILE_INFO _eof; + FILE_END_OF_FILE_INFO _eof {}; _eof.EndOfFile.QuadPart = length; if (!SetFileInformationByHandle(m_handle, FileEndOfFileInfo, &_eof, sizeof(_eof))) @@ -563,6 +562,7 @@ namespace fs u64 size() override { + // NOTE: this can fail if we access a mounted empty drive (e.g. after unmounting an iso). LARGE_INTEGER size; ensure(GetFileSizeEx(m_handle, &size)); // "file::size" @@ -579,7 +579,7 @@ namespace fs file_id id{"windows_file"}; id.data.resize(sizeof(FILE_ID_INFO)); - FILE_ID_INFO info; + FILE_ID_INFO info {}; if (!GetFileInformationByHandleEx(m_handle, FileIdInfo, &info, sizeof(info))) { @@ -625,7 +625,7 @@ namespace fs struct ::stat file_info; ensure(::fstat(m_fd, &file_info) == 0); // "file::stat" - stat_t info; + stat_t info {}; info.is_directory = S_ISDIR(file_info.st_mode); info.is_writable = file_info.st_mode & 0200; // HACK: approximation info.size = file_info.st_size; @@ -1656,6 +1656,16 @@ fs::file::file(const std::string& path, bs_t mode) return; } + // Check if the handle is actually valid. + // This can fail on empty mounted drives (e.g. with ERROR_NOT_READY or ERROR_INVALID_FUNCTION). + BY_HANDLE_FILE_INFORMATION info; + if (!GetFileInformationByHandle(handle, &info)) + { + CloseHandle(handle); + g_tls_error = to_error(GetLastError()); + return; + } + m_file = std::make_unique(handle); #else int flags = O_CLOEXEC; // Ensures all files are closed on execl for auto updater diff --git a/Utilities/File.h b/Utilities/File.h index 7e6356da7b..dd2db42a46 100644 --- a/Utilities/File.h +++ b/Utilities/File.h @@ -66,13 +66,13 @@ namespace fs // File attributes (TODO) struct stat_t { - bool is_directory; - bool is_symlink; - bool is_writable; - u64 size; - s64 atime; - s64 mtime; - s64 ctime; + bool is_directory = false; + bool is_symlink = false; + bool is_writable = false; + u64 size = 0; + s64 atime = 0; + s64 mtime = 0; + s64 ctime = 0; using enable_bitcopy = std::true_type; diff --git a/Utilities/JITASM.cpp b/Utilities/JITASM.cpp index acb5f40b04..90c09bb0bf 100644 --- a/Utilities/JITASM.cpp +++ b/Utilities/JITASM.cpp @@ -14,6 +14,10 @@ #define CAN_OVERCOMMIT #endif +#if defined(__APPLE__) +#include +#endif + LOG_CHANNEL(jit_log, "JIT"); void jit_announce(uptr func, usz size, std::string_view name) diff --git a/Utilities/JITLLVM.cpp b/Utilities/JITLLVM.cpp index 166ee7cec2..34e1572185 100644 --- a/Utilities/JITLLVM.cpp +++ b/Utilities/JITLLVM.cpp @@ -688,6 +688,30 @@ jit_compiler::jit_compiler(const std::unordered_map& _link, co mem = std::make_unique(std::move(symbols_cement)); } + std::vector attributes; + +#if defined(ARCH_ARM64) + if (utils::has_sha3()) + attributes.push_back("+sha3"); + else + attributes.push_back("-sha3"); + + if (utils::has_dotprod()) + attributes.push_back("+dotprod"); + else + attributes.push_back("-dotprod"); + + if (utils::has_sve()) + attributes.push_back("+sve"); + else + attributes.push_back("-sve"); + + if (utils::has_sve2()) + attributes.push_back("+sve2"); + else + attributes.push_back("-sve2"); +#endif + { m_engine.reset(llvm::EngineBuilder(std::move(null_mod)) .setErrorStr(&result) @@ -699,6 +723,7 @@ jit_compiler::jit_compiler(const std::unordered_map& _link, co //.setCodeModel(llvm::CodeModel::Large) #endif .setRelocationModel(llvm::Reloc::Model::PIC_) + .setMAttrs(attributes) .setMCPU(m_cpu) .create()); } diff --git a/Utilities/Thread.cpp b/Utilities/Thread.cpp index 810b8fd7c5..08dce12315 100644 --- a/Utilities/Thread.cpp +++ b/Utilities/Thread.cpp @@ -8,7 +8,6 @@ #include "Emu/RSX/RSXThread.h" #include "Thread.h" #include "Utilities/JIT.h" -#include #include #ifdef ARCH_ARM64 diff --git a/Utilities/Thread.h b/Utilities/Thread.h index 02e5db56ff..7cd9a7c7ea 100644 --- a/Utilities/Thread.h +++ b/Utilities/Thread.h @@ -4,6 +4,7 @@ #include "util/atomic.hpp" #include "util/shared_ptr.hpp" +#include #include // Hardware core layout diff --git a/buildfiles/msvc/common_default.props b/buildfiles/msvc/common_default.props index 04f6502839..bfddbb5465 100644 --- a/buildfiles/msvc/common_default.props +++ b/buildfiles/msvc/common_default.props @@ -13,7 +13,7 @@ stdcpplatest - stdcpp20 + stdcpp23 _SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING=1;_HAS_EXCEPTIONS=0;%(PreprocessorDefinitions) false -d2FH4- %(AdditionalOptions) diff --git a/rpcs3/CMakeLists.txt b/rpcs3/CMakeLists.txt index 6e5c2cd635..5ab98ab419 100644 --- a/rpcs3/CMakeLists.txt +++ b/rpcs3/CMakeLists.txt @@ -8,7 +8,7 @@ include(${CMAKE_CURRENT_SOURCE_DIR}/git-version.cmake) include(ConfigureCompiler) include(CheckFunctionExists) -set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD 23) if(UNIX AND NOT APPLE AND NOT ANDROID) add_compile_definitions(DATADIR="${CMAKE_INSTALL_FULL_DATADIR}/rpcs3") diff --git a/rpcs3/Emu/Cell/Modules/cellMic.cpp b/rpcs3/Emu/Cell/Modules/cellMic.cpp index 0724b48927..0c58d2ec7e 100644 --- a/rpcs3/Emu/Cell/Modules/cellMic.cpp +++ b/rpcs3/Emu/Cell/Modules/cellMic.cpp @@ -634,7 +634,7 @@ u32 microphone_device::capture_audio() if (ALCenum err = alcGetError(micdevice.device); err != ALC_NO_ERROR) { cellMic.error("Error getting number of captured samples of device '%s' (error=%s)", micdevice.name, fmt::alc_error{micdevice.device, err}); - return CELL_MICIN_ERROR_FATAL; + return 0; } num_samples = std::min(num_samples, samples_in); diff --git a/rpcs3/Emu/Cell/Modules/cellMic.h b/rpcs3/Emu/Cell/Modules/cellMic.h index 88a2f4d937..a07cec23a8 100644 --- a/rpcs3/Emu/Cell/Modules/cellMic.h +++ b/rpcs3/Emu/Cell/Modules/cellMic.h @@ -238,7 +238,7 @@ public: if (over_size > Size) { m_tail += (over_size - Size); - if (m_tail > Size) + if (m_tail >= Size) m_tail -= Size; m_used = Size; diff --git a/rpcs3/Emu/Cell/Modules/cellSaveData.cpp b/rpcs3/Emu/Cell/Modules/cellSaveData.cpp index a4d160fdcb..7878e86642 100644 --- a/rpcs3/Emu/Cell/Modules/cellSaveData.cpp +++ b/rpcs3/Emu/Cell/Modules/cellSaveData.cpp @@ -876,39 +876,42 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v // Sort the entries { - const u32 order = setList->sortOrder; const u32 type = setList->sortType; - std::sort(save_entries.begin(), save_entries.end(), [order, type](const SaveDataEntry& entry1, const SaveDataEntry& entry2) -> bool + auto comp = [type](const SaveDataEntry& entry1, const SaveDataEntry& entry2) -> bool { const bool mtime_lower = entry1.mtime < entry2.mtime; const bool mtime_equal = entry1.mtime == entry2.mtime; const bool subtitle_lower = entry1.subtitle < entry2.subtitle; const bool subtitle_equal = entry1.subtitle == entry2.subtitle; - const bool revert_order = order == CELL_SAVEDATA_SORTORDER_DESCENT; if (type == CELL_SAVEDATA_SORTTYPE_MODIFIEDTIME) { if (mtime_equal) { - return subtitle_lower != revert_order; + return subtitle_lower; } - return mtime_lower != revert_order; + return mtime_lower; } else if (type == CELL_SAVEDATA_SORTTYPE_SUBTITLE) { if (subtitle_equal) { - return mtime_lower != revert_order; + return mtime_lower; } - return subtitle_lower != revert_order; + return subtitle_lower; } ensure(false); return true; - }); + }; + + if (setList->sortOrder == CELL_SAVEDATA_SORTORDER_ASCENT) + std::sort(save_entries.begin(), save_entries.end(), comp); + else + std::sort(save_entries.rbegin(), save_entries.rend(), comp); } // Fill the listGet->dirList array diff --git a/rpcs3/Emu/Cell/Modules/cellVdec.cpp b/rpcs3/Emu/Cell/Modules/cellVdec.cpp index 5cf5e64a51..3205afd786 100644 --- a/rpcs3/Emu/Cell/Modules/cellVdec.cpp +++ b/rpcs3/Emu/Cell/Modules/cellVdec.cpp @@ -1462,7 +1462,12 @@ error_code cellVdecGetPicItem(ppu_thread& ppu, u32 handle, vm::pptr picInfo; + union + { + CellVdecAvcInfo avcInfo; + CellVdecDivxInfo divxInfo; + CellVdecMpeg2Info mpeg2Info; + } picInfo; }; AVFrame* frame{}; diff --git a/rpcs3/Emu/Cell/Modules/sceNp.cpp b/rpcs3/Emu/Cell/Modules/sceNp.cpp index 2a2075f0ca..3eee01cf9a 100644 --- a/rpcs3/Emu/Cell/Modules/sceNp.cpp +++ b/rpcs3/Emu/Cell/Modules/sceNp.cpp @@ -1199,7 +1199,7 @@ error_code _sceNpBasicSendMessage(vm::cptr to, vm::cptr data, u32 .msgFeatures = {}, .data = std::vector(static_cast(data.get_ptr()), static_cast(data.get_ptr()) + size)}; std::set npids; - npids.insert(std::string(to->handle.data)); + npids.insert(np::npid_to_string(*to)); nph.send_message(msg_data, npids); @@ -1228,7 +1228,7 @@ error_code sceNpBasicSendMessageGui(ppu_thread& ppu, vm::cptrmsgId, msg->mainType, msg->subType, msg->msgFeatures, msg->count, msg->npids); for (u32 i = 0; i < msg->count && msg->npids; i++) { - sceNp.trace("sceNpBasicSendMessageGui: NpId[%d] = %s", i, static_cast(&msg->npids[i].handle.data[0])); + sceNp.trace("sceNpBasicSendMessageGui: NpId[%d] = %s", i, np::npid_to_string(msg->npids[i])); } sceNp.notice("sceNpBasicSendMessageGui: subject: %s", msg->subject); sceNp.notice("sceNpBasicSendMessageGui: body: %s", msg->body); @@ -1398,7 +1398,7 @@ error_code sceNpBasicSendMessageGui(ppu_thread& ppu, vm::cptrcount; i++) { - npids.insert(std::string(msg->npids[i].handle.data)); + npids.insert(np::npid_to_string(msg->npids[i])); } } @@ -7144,7 +7144,7 @@ error_code sceNpUtilCanonicalizeNpIdForPsp(vm::ptr npId) error_code sceNpUtilCmpNpId(vm::ptr id1, vm::ptr id2) { - sceNp.trace("sceNpUtilCmpNpId(id1=*0x%x(%s), id2=*0x%x(%s))", id1, id1 ? id1->handle.data : "", id2, id2 ? id2->handle.data : ""); + sceNp.trace("sceNpUtilCmpNpId(id1=*0x%x(%s), id2=*0x%x(%s))", id1, id1 ? np::npid_to_string(*id1) : std::string(), id2, id2 ? np::npid_to_string(*id2) : std::string()); if (!id1 || !id2) { diff --git a/rpcs3/Emu/Cell/lv2/sys_net.cpp b/rpcs3/Emu/Cell/lv2/sys_net.cpp index 827a4c98f2..27731e44ca 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_net.cpp @@ -992,7 +992,7 @@ error_code sys_net_bnet_sendto(ppu_thread& ppu, s32 s, vm::cptr buf, u32 l fmt::throw_exception("sys_net_bnet_sendto(s=%d): unknown flags (0x%x)", flags); } - if (addr && addrlen < 8) + if (addr && addrlen < sizeof(sys_net_sockaddr)) { sys_net.error("sys_net_bnet_sendto(s=%d): bad addrlen (%u)", s, addrlen); return -SYS_NET_EINVAL; diff --git a/rpcs3/Emu/Cell/lv2/sys_net.h b/rpcs3/Emu/Cell/lv2/sys_net.h index b1f9ef7268..68e7041df0 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net.h +++ b/rpcs3/Emu/Cell/lv2/sys_net.h @@ -107,7 +107,7 @@ enum lv2_ip_option : s32 SYS_NET_IP_MULTICAST_LOOP = 11, SYS_NET_IP_ADD_MEMBERSHIP = 12, SYS_NET_IP_DROP_MEMBERSHIP = 13, - SYS_NET_IP_TTLCHK = 23, + SYS_NET_IP_TTLCHK = 23, // This is probably the equivalent of IP_MINTTL on FreeBSD SYS_NET_IP_MAXTTL = 24, SYS_NET_IP_DONTFRAG = 26 }; diff --git a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.cpp b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.cpp index d420f23cc8..2eb47d7b55 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.cpp @@ -551,12 +551,14 @@ std::tuple lv2_socket_native::getsockopt(s32 } case SYS_NET_IP_TTLCHK: { - sys_net.error("sys_net_bnet_getsockopt(IPPROTO_IP, SYS_NET_IP_TTLCHK): stubbed option"); + out_val._int = min_ttl; + out_len = sizeof(s32); return {CELL_OK, out_val, out_len}; } case SYS_NET_IP_MAXTTL: { - sys_net.error("sys_net_bnet_getsockopt(IPPROTO_IP, SYS_NET_IP_MAXTTL): stubbed option"); + out_val._int = max_ttl; + out_len = sizeof(s32); return {CELL_OK, out_val, out_len}; } case SYS_NET_IP_DONTFRAG: @@ -834,13 +836,13 @@ s32 lv2_socket_native::setsockopt(s32 level, s32 optname, const std::vector& } case SYS_NET_IP_TTLCHK: { - sys_net.error("sys_net_bnet_setsockopt(s=%d, IPPROTO_IP): Stubbed option (0x%x) (SYS_NET_IP_TTLCHK)", lv2_id, optname); - break; + min_ttl = native_int; + return {}; } case SYS_NET_IP_MAXTTL: { - sys_net.error("sys_net_bnet_setsockopt(s=%d, IPPROTO_IP): Stubbed option (0x%x) (SYS_NET_IP_MAXTTL)", lv2_id, optname); - break; + max_ttl = native_int; + return {}; } case SYS_NET_IP_DONTFRAG: { @@ -910,7 +912,7 @@ std::optional, sys_net_sockaddr>> lv2_socket_nat { auto& nph = g_fxo->get>(); const auto packet = dnshook.get_dns_packet(lv2_id); - ensure(packet.size() < len); + ensure(packet.size() <= len); memcpy(res_buf.data(), packet.data(), packet.size()); native_addr.ss_family = AF_INET; (reinterpret_cast<::sockaddr_in*>(&native_addr))->sin_port = std::bit_cast>(53); // htons(53) @@ -1069,18 +1071,20 @@ std::optional lv2_socket_native::sendmsg(s32 flags, const sys_net_msghdr& m return {-SYS_NET_ECONNRESET}; } + std::vector buf_copy; for (int i = 0; i < msg.msg_iovlen; i++) { auto iov_base = msg.msg_iov[i].iov_base; const u32 len = msg.msg_iov[i].iov_len; - const std::vector buf_copy(vm::_ptr(iov_base.addr()), vm::_ptr(iov_base.addr()) + len); + const auto* src = vm::_ptr(iov_base.addr()); + buf_copy.insert(buf_copy.end(), src, src + len); + } - native_result = ::send(native_socket, reinterpret_cast(buf_copy.data()), ::narrow(buf_copy.size()), native_flags); + native_result = ::send(native_socket, reinterpret_cast(buf_copy.data()), ::narrow(buf_copy.size()), native_flags); - if (native_result >= 0) - { - return {native_result}; - } + if (native_result >= 0) + { + return {native_result}; } result = get_last_error(!so_nbio && (flags & SYS_NET_MSG_DONTWAIT) == 0); @@ -1232,16 +1236,16 @@ bool lv2_socket_native::is_socket_connected() return false; } - fd_set readfds, writefds; - struct timeval timeout{0, 0}; // Zero timeout + pollfd pfd{}; + pfd.fd = native_socket; + pfd.events = POLLIN | POLLOUT; - FD_ZERO(&readfds); - FD_ZERO(&writefds); - FD_SET(native_socket, &readfds); - FD_SET(native_socket, &writefds); - - // Use select to check for readability and writability - const int result = ::select(1, &readfds, &writefds, NULL, &timeout); + // Use poll to check for readability and writability +#ifdef _WIN32 + const int result = WSAPoll(&pfd, 1, 0); +#else + const int result = ::poll(&pfd, 1, 0); +#endif if (result < 0) { @@ -1250,5 +1254,5 @@ bool lv2_socket_native::is_socket_connected() } // Socket is connected if it's readable or writable - return FD_ISSET(native_socket, &readfds) || FD_ISSET(native_socket, &writefds); + return (pfd.revents & (POLLIN | POLLOUT)) != 0; } diff --git a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.h b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.h index cf07dfcb76..84f4218cbd 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.h +++ b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_native.h @@ -70,6 +70,10 @@ private: s32 so_reuseaddr = 0; s32 so_reuseport = 0; #endif + // Those values come from FreeBSD + s32 min_ttl = 1; + s32 max_ttl = 64; + u16 bound_port = 0; bool feign_tcp_conn_failure = false; // Savestate load related }; diff --git a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2ps.cpp b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2ps.cpp index c0790a2e33..771402809e 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2ps.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2ps.cpp @@ -112,7 +112,6 @@ public: // reply is late, increases rtt auto& msg = it->second; - const auto addr = msg.dst_addr.sin_addr.s_addr; rtt_info rtt = rtts[msg.sock_id]; // Only increases rtt once per loop(in case a big number of packets are sent at once) if (!rtt_increased.count(msg.sock_id)) @@ -120,7 +119,7 @@ public: rtt.num_retries += 1; // Increases current rtt by 10% rtt.rtt_time += (rtt.rtt_time / 10); - rtts[addr] = rtt; + rtts[msg.sock_id] = rtt; rtt_increased.emplace(msg.sock_id); } @@ -625,7 +624,7 @@ std::tuple, sys_net_sockaddr> lv2_socket_p2ps: sys_net_sockaddr ps3_addr{}; auto* paddr = reinterpret_cast(&ps3_addr); - lv2_socket_p2ps* sock_client = reinterpret_cast(idm::check_unlocked(p2ps_client)); + auto sock_client = static_cast>(idm::get_unlocked(p2ps_client)); { std::lock_guard lock(sock_client->mutex); paddr->sin_family = SYS_NET_AF_INET; diff --git a/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.cpp b/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.cpp index 61039c7856..67be0df63b 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_net/nt_p2p_port.cpp @@ -249,8 +249,9 @@ bool nt_p2p_port::recv_data() auto& bound_sockets = ::at32(bound_p2p_vports, dst_vport); - for (const auto sock_id : bound_sockets) + for (auto it = bound_sockets.begin(); it != bound_sockets.end();) { + s32 sock_id = *it; const auto sock = idm::check(sock_id, [&](lv2_socket& sock) { ensure(sock.get_type() == SYS_NET_SOCK_DGRAM_P2P); @@ -262,12 +263,17 @@ bool nt_p2p_port::recv_data() if (!sock) { sys_net.error("Socket %d found in bound_p2p_vports didn't exist!", sock_id); - bound_sockets.erase(sock_id); + it = bound_sockets.erase(it); if (bound_sockets.empty()) { bound_p2p_vports.erase(dst_vport); + break; } } + else + { + it++; + } } return true; diff --git a/rpcs3/Emu/Cell/lv2/sys_time.cpp b/rpcs3/Emu/Cell/lv2/sys_time.cpp index 539bb9dedf..a71a11ba31 100644 --- a/rpcs3/Emu/Cell/lv2/sys_time.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_time.cpp @@ -118,6 +118,7 @@ static int clock_gettime(int clk_id, struct timespec* tp) #ifndef _WIN32 +#include #include static struct timespec start_time = []() diff --git a/rpcs3/Emu/Cell/lv2/sys_usbd.cpp b/rpcs3/Emu/Cell/lv2/sys_usbd.cpp index 64f27227d3..e1c3caf9d7 100644 --- a/rpcs3/Emu/Cell/lv2/sys_usbd.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_usbd.cpp @@ -556,6 +556,8 @@ usb_handler_thread::usb_handler_thread() switch (g_cfg.audio.microphone_type) { + case microphone_handler::null: + break; case microphone_handler::standard: usb_devices.push_back(std::make_shared(0, get_new_location(), MicType::Logitech)); break; diff --git a/rpcs3/Emu/Io/MouseHandler.cpp b/rpcs3/Emu/Io/MouseHandler.cpp index 9fd37463cd..7c4edcb449 100644 --- a/rpcs3/Emu/Io/MouseHandler.cpp +++ b/rpcs3/Emu/Io/MouseHandler.cpp @@ -33,12 +33,12 @@ void MouseHandlerBase::save(utils::serial& ar) bool MouseHandlerBase::is_time_for_update(double elapsed_time_ms) { - steady_clock::time_point now = steady_clock::now(); - const double elapsed_ms = (now - last_update).count() / 1'000'000.; + const steady_clock::time_point now = steady_clock::now(); + const double elapsed_ms = (now - m_last_update).count() / 1'000'000.; if (elapsed_ms > elapsed_time_ms) { - last_update = now; + m_last_update = now; return true; } return false; diff --git a/rpcs3/Emu/Io/MouseHandler.h b/rpcs3/Emu/Io/MouseHandler.h index 6a77f44d0e..4b31a6a60d 100644 --- a/rpcs3/Emu/Io/MouseHandler.h +++ b/rpcs3/Emu/Io/MouseHandler.h @@ -128,7 +128,7 @@ class MouseHandlerBase protected: MouseInfo m_info{}; std::vector m_mice; - steady_clock::time_point last_update{}; + steady_clock::time_point m_last_update{}; bool is_time_for_update(double elapsed_time_ms = 10.0); // 4-10 ms, let's use 10 for now diff --git a/rpcs3/Emu/NP/clans_client.cpp b/rpcs3/Emu/NP/clans_client.cpp index da3f506c91..e2d9fa9de0 100644 --- a/rpcs3/Emu/NP/clans_client.cpp +++ b/rpcs3/Emu/NP/clans_client.cpp @@ -464,7 +464,7 @@ namespace clan clan.append_child("ticket").text().set(ticket.c_str()); clan.append_child("id").text().set(clan_id); - const std::string jid_str = fmt::format(JID_FORMAT, np_id.handle.data); + const std::string jid_str = fmt::format(JID_FORMAT, np::npid_to_string(np_id)); clan.append_child("jid").text().set(jid_str.c_str()); pugi::xml_document response = pugi::xml_document(); @@ -656,7 +656,7 @@ namespace clan clan.append_child("ticket").text().set(ticket.c_str()); clan.append_child("id").text().set(clan_id); - const std::string jid_str = fmt::format(JID_FORMAT, np_id.handle.data); + const std::string jid_str = fmt::format(JID_FORMAT, np::npid_to_string(np_id)); clan.append_child("jid").text().set(jid_str.c_str()); pugi::xml_document response = pugi::xml_document(); @@ -674,7 +674,7 @@ namespace clan clan.append_child("ticket").text().set(ticket.c_str()); clan.append_child("id").text().set(clan_id); - const std::string jid_str = fmt::format(JID_FORMAT, np_id.handle.data); + const std::string jid_str = fmt::format(JID_FORMAT, np::npid_to_string(np_id)); clan.append_child("jid").text().set(jid_str.c_str()); pugi::xml_document response = pugi::xml_document(); @@ -832,7 +832,7 @@ namespace clan clan.append_child("ticket").text().set(ticket.c_str()); clan.append_child("id").text().set(clan_id); - const std::string jid_str = fmt::format(JID_FORMAT, np_id.handle.data); + const std::string jid_str = fmt::format(JID_FORMAT, np::npid_to_string(np_id)); clan.append_child("jid").text().set(jid_str.c_str()); pugi::xml_document response = pugi::xml_document(); @@ -850,7 +850,7 @@ namespace clan clan.append_child("ticket").text().set(ticket.c_str()); clan.append_child("id").text().set(clan_id); - const std::string jid_str = fmt::format(JID_FORMAT, np_id.handle.data); + const std::string jid_str = fmt::format(JID_FORMAT, np::npid_to_string(np_id)); clan.append_child("jid").text().set(jid_str.c_str()); pugi::xml_document response = pugi::xml_document(); @@ -868,7 +868,7 @@ namespace clan clan.append_child("ticket").text().set(ticket.c_str()); clan.append_child("id").text().set(clan_id); - const std::string jid_str = fmt::format(JID_FORMAT, np_id.handle.data); + const std::string jid_str = fmt::format(JID_FORMAT, np::npid_to_string(np_id)); clan.append_child("jid").text().set(jid_str.c_str()); pugi::xml_document response = pugi::xml_document(); @@ -902,7 +902,7 @@ namespace clan clan.append_child("id").text().set(clan_id); pugi::xml_node role = clan.append_child("onlinename"); - role.text().set(nph.get_npid().handle.data); + role.text().set(np::npid_to_string(nph.get_npid()).c_str()); pugi::xml_node description = clan.append_child("description"); description.text().set(info.description); @@ -990,7 +990,7 @@ namespace clan clan.append_child("ticket").text().set(ticket.c_str()); clan.append_child("id").text().set(clan_id); - const std::string jid_str = fmt::format(JID_FORMAT, np_id.handle.data); + const std::string jid_str = fmt::format(JID_FORMAT, np::npid_to_string(np_id)); clan.append_child("jid").text().set(jid_str.c_str()); pugi::xml_document response = pugi::xml_document(); @@ -1008,7 +1008,7 @@ namespace clan clan.append_child("ticket").text().set(ticket.c_str()); clan.append_child("id").text().set(clan_id); - const std::string jid_str = fmt::format(JID_FORMAT, np_id.handle.data); + const std::string jid_str = fmt::format(JID_FORMAT, np::npid_to_string(np_id)); clan.append_child("jid").text().set(jid_str.c_str()); pugi::xml_node role_node = clan.append_child("role"); diff --git a/rpcs3/Emu/NP/np_cache.cpp b/rpcs3/Emu/NP/np_cache.cpp index 04223da545..4731e0cede 100644 --- a/rpcs3/Emu/NP/np_cache.cpp +++ b/rpcs3/Emu/NP/np_cache.cpp @@ -377,7 +377,7 @@ namespace np if (!rooms.contains(room_id)) { - np_cache.error("np_cache::get_memberid cache miss room_id: room_id(%d)/npid(%s)", room_id, static_cast(npid.handle.data)); + np_cache.error("np_cache::get_memberid cache miss room_id: room_id(%d)/npid(%s)", room_id, np::npid_to_string(npid)); return std::nullopt; } @@ -389,7 +389,7 @@ namespace np return id; } - np_cache.error("np_cache::get_memberid cache miss member_id: room_id(%d)/npid(%s)", room_id, static_cast(npid.handle.data)); + np_cache.error("np_cache::get_memberid cache miss member_id: room_id(%d)/npid(%s)", room_id, np::npid_to_string(npid)); return std::nullopt; } diff --git a/rpcs3/Emu/NP/np_contexts.cpp b/rpcs3/Emu/NP/np_contexts.cpp index 46d773d127..f43d02c362 100644 --- a/rpcs3/Emu/NP/np_contexts.cpp +++ b/rpcs3/Emu/NP/np_contexts.cpp @@ -46,7 +46,7 @@ error_code generic_async_transaction_context::wait_for_completion() return *result; } - completion_cond.wait(lock); + completion_cond.wait(lock, [this] { return result.has_value(); }); return *result; } diff --git a/rpcs3/Emu/NP/np_handler.cpp b/rpcs3/Emu/NP/np_handler.cpp index c4db3350c0..1fc9342329 100644 --- a/rpcs3/Emu/NP/np_handler.cpp +++ b/rpcs3/Emu/NP/np_handler.cpp @@ -1024,7 +1024,7 @@ namespace np } } - nph_log.notice("basic_event: event:%d, from:%s(%s), size:%d", *event, static_cast(from->userId.handle.data), static_cast(from->name.data), *size); + nph_log.notice("basic_event: event:%d, from:%s(%s), size:%d", *event, np::npid_to_string(from->userId), static_cast(from->name.data), *size); return CELL_OK; } @@ -1361,7 +1361,7 @@ namespace np player_history& np_handler::get_player_and_set_timestamp(const SceNpId& npid, u64 timestamp) { - std::string npid_str = std::string(npid.handle.data); + std::string npid_str = np::npid_to_string(npid); if (!players_history.contains(npid_str)) { @@ -1641,7 +1641,7 @@ namespace np return SCE_NP_BASIC_ERROR_NOT_CONNECTED; } - auto friend_infos = rpcn->get_friend_presence_by_npid(std::string(npid.handle.data)); + auto friend_infos = rpcn->get_friend_presence_by_npid(np::npid_to_string(npid)); if (!friend_infos) { return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT; diff --git a/rpcs3/Emu/NP/np_helpers.cpp b/rpcs3/Emu/NP/np_helpers.cpp index 79f61ca627..39c59afdb0 100644 --- a/rpcs3/Emu/NP/np_helpers.cpp +++ b/rpcs3/Emu/NP/np_helpers.cpp @@ -89,6 +89,13 @@ namespace np // npid->reserved[0] = 1; } + std::string npid_to_string(const SceNpId& npid) + { + char npid_str[17]{}; + std::memcpy(npid_str, npid.handle.data, 16); + return std::string(npid_str); + } + void string_to_online_name(std::string_view str, SceNpOnlineName& online_name) { memset(&online_name, 0, sizeof(online_name)); diff --git a/rpcs3/Emu/NP/np_helpers.h b/rpcs3/Emu/NP/np_helpers.h index c33b4ca001..a51499ca64 100644 --- a/rpcs3/Emu/NP/np_helpers.h +++ b/rpcs3/Emu/NP/np_helpers.h @@ -13,6 +13,7 @@ namespace np std::optional string_to_communication_id(std::string_view str); void string_to_npid(std::string_view str, SceNpId& npid); + std::string npid_to_string(const SceNpId& npid); void string_to_online_name(std::string_view str, SceNpOnlineName& online_name); void string_to_avatar_url(std::string_view str, SceNpAvatarUrl& avatar_url); void strings_to_userinfo(std::string_view npid, std::string_view online_name, std::string_view avatar_url, SceNpUserInfo& user_info); diff --git a/rpcs3/Emu/NP/np_notifications.cpp b/rpcs3/Emu/NP/np_notifications.cpp index d5756a8351..6d6d511ee0 100644 --- a/rpcs3/Emu/NP/np_notifications.cpp +++ b/rpcs3/Emu/NP/np_notifications.cpp @@ -41,7 +41,7 @@ namespace np return; } - rpcn_log.notice("Received notification that user %s(%d) joined the room(%d)", notif_data->roomMemberDataInternal->userInfo.npId.handle.data, notif_data->roomMemberDataInternal->memberId, room_id); + rpcn_log.notice("Received notification that user %s(%d) joined the room(%d)", np::npid_to_string(notif_data->roomMemberDataInternal->userInfo.npId), notif_data->roomMemberDataInternal->memberId, room_id); extra_nps::print_SceNpMatching2RoomMemberDataInternal(notif_data->roomMemberDataInternal.get_ptr()); // We initiate signaling if necessary @@ -54,7 +54,7 @@ namespace np const u16 member_id = notif_data->roomMemberDataInternal->memberId; const SceNpId& npid = notif_data->roomMemberDataInternal->userInfo.npId; - rpcn_log.notice("Join notification told to connect to member(%d=%s) of room(%d): %s:%d", member_id, reinterpret_cast(npid.handle.data), room_id, ip_to_string(addr_p2p), port_p2p); + rpcn_log.notice("Join notification told to connect to member(%d=%s) of room(%d): %s:%d", member_id, np::npid_to_string(npid), room_id, ip_to_string(addr_p2p), port_p2p); // Attempt Signaling auto& sigh = g_fxo->get>(); @@ -98,7 +98,7 @@ namespace np return; } - rpcn_log.notice("Received notification that user %s(%d) left the room(%d)", notif_data->roomMemberDataInternal->userInfo.npId.handle.data, notif_data->roomMemberDataInternal->memberId, room_id); + rpcn_log.notice("Received notification that user %s(%d) left the room(%d)", np::npid_to_string(notif_data->roomMemberDataInternal->userInfo.npId), notif_data->roomMemberDataInternal->memberId, room_id); extra_nps::print_SceNpMatching2RoomMemberDataInternal(notif_data->roomMemberDataInternal.get_ptr()); if (room_event_cb) @@ -204,7 +204,7 @@ namespace np return; } - rpcn_log.notice("Received notification that user's %s(%d) room (%d) data was updated", notif_data->newRoomMemberDataInternal->userInfo.npId.handle.data, notif_data->newRoomMemberDataInternal->memberId, room_id); + rpcn_log.notice("Received notification that user's %s(%d) room (%d) data was updated", np::npid_to_string(notif_data->newRoomMemberDataInternal->userInfo.npId), notif_data->newRoomMemberDataInternal->memberId, room_id); extra_nps::print_SceNpMatching2RoomMemberDataInternal(notif_data->newRoomMemberDataInternal.get_ptr()); if (room_event_cb) diff --git a/rpcs3/Emu/NP/np_requests.cpp b/rpcs3/Emu/NP/np_requests.cpp index 20199889dc..ed7023cc89 100644 --- a/rpcs3/Emu/NP/np_requests.cpp +++ b/rpcs3/Emu/NP/np_requests.cpp @@ -310,7 +310,7 @@ namespace np if (npid_res != CELL_OK) continue; - rpcn_log.notice("JoinRoomResult told to connect to member(%d=%s) of room(%d): %s:%d", member_id, reinterpret_cast(npid_p2p->handle.data), room_id, ip_to_string(addr_p2p), port_p2p); + rpcn_log.notice("JoinRoomResult told to connect to member(%d=%s) of room(%d): %s:%d", member_id, np::npid_to_string(*npid_p2p), room_id, ip_to_string(addr_p2p), port_p2p); // Attempt Signaling auto& sigh = g_fxo->get>(); @@ -951,13 +951,16 @@ namespace np { thread_base::set_name("NP Trans Worker"); - auto res = trans_ctx->wake_cond.wait_for(lock, std::chrono::microseconds(trans_ctx->timeout)); + bool has_value = trans_ctx->wake_cond.wait_for(lock, std::chrono::microseconds(trans_ctx->timeout), [&] + { + return trans_ctx->result.has_value(); + }); { std::lock_guard lock_threads(this->mutex_async_transactions); this->async_transactions.erase(req_id); } - if (res == std::cv_status::timeout) + if (!has_value) { trans_ctx->result = SCE_NP_COMMUNITY_ERROR_TIMEOUT; return; diff --git a/rpcs3/Emu/NP/np_structs_extra.cpp b/rpcs3/Emu/NP/np_structs_extra.cpp index 58770c450f..6774b20bc4 100644 --- a/rpcs3/Emu/NP/np_structs_extra.cpp +++ b/rpcs3/Emu/NP/np_structs_extra.cpp @@ -2,6 +2,7 @@ #include "stdafx.h" #include #include "np_structs_extra.h" +#include "np_helpers.h" LOG_CHANNEL(sceNp); LOG_CHANNEL(sceNp2); @@ -13,7 +14,7 @@ namespace extra_nps void print_SceNpUserInfo2(const SceNpUserInfo2* user) { sceNp2.warning("SceNpUserInfo2:"); - sceNp2.warning("npid: %s", static_cast(user->npId.handle.data)); + sceNp2.warning("npid: %s", np::npid_to_string(user->npId)); sceNp2.warning("onlineName: *0x%x(%s)", user->onlineName, user->onlineName ? static_cast(user->onlineName->data) : ""); sceNp2.warning("avatarUrl: *0x%x(%s)", user->avatarUrl, user->avatarUrl ? static_cast(user->avatarUrl->data) : ""); } @@ -208,7 +209,7 @@ namespace extra_nps { sceNp2.warning("SceNpMatching2RoomMemberDataInternal:"); sceNp2.warning("next: *0x%x", member->next); - sceNp2.warning("npId: %s", member->userInfo.npId.handle.data); + sceNp2.warning("npId: %s", np::npid_to_string(member->userInfo.npId)); sceNp2.warning("onlineName: %s", member->userInfo.onlineName ? member->userInfo.onlineName->data : ""); sceNp2.warning("avatarUrl: %s", member->userInfo.avatarUrl ? member->userInfo.avatarUrl->data : ""); sceNp2.warning("joinDate: %lld", member->joinDate.tick); @@ -460,7 +461,7 @@ namespace extra_nps void print_SceNpScoreRankData(const SceNpScoreRankData* data) { sceNp.warning("sceNpScoreRankData:"); - sceNp.warning("npId: %s", static_cast(data->npId.handle.data)); + sceNp.warning("npId: %s", np::npid_to_string(data->npId)); sceNp.warning("onlineName: %s", static_cast(data->onlineName.data)); sceNp.warning("pcId: %d", data->pcId); sceNp.warning("serialRank: %d", data->serialRank); @@ -474,7 +475,7 @@ namespace extra_nps void print_SceNpScoreRankData_deprecated(const SceNpScoreRankData_deprecated* data) { sceNp.warning("sceNpScoreRankData_deprecated:"); - sceNp.warning("npId: %s", static_cast(data->npId.handle.data)); + sceNp.warning("npId: %s", np::npid_to_string(data->npId)); sceNp.warning("onlineName: %s", static_cast(data->onlineName.data)); sceNp.warning("serialRank: %d", data->serialRank); sceNp.warning("rank: %d", data->rank); @@ -542,7 +543,7 @@ namespace extra_nps void print_SceNpUserInfo(const SceNpUserInfo* data) { - sceNp.warning("userId: %s", data->userId.handle.data); + sceNp.warning("userId: %s", np::npid_to_string(data->userId)); sceNp.warning("name: %s", data->name.data); sceNp.warning("icon: %s", data->icon.data); } @@ -576,7 +577,7 @@ namespace extra_nps if (data->kick_actor) { - sceNp.warning("kick_actor: %s", data->kick_actor->handle.data); + sceNp.warning("kick_actor: %s", np::npid_to_string(*data->kick_actor)); } sceNp.warning("opt: 0x%x", data->kick_actor); diff --git a/rpcs3/Emu/NP/pb_helpers.cpp b/rpcs3/Emu/NP/pb_helpers.cpp index 41b0ca4579..83170426ff 100644 --- a/rpcs3/Emu/NP/pb_helpers.cpp +++ b/rpcs3/Emu/NP/pb_helpers.cpp @@ -263,7 +263,7 @@ namespace np for (u32 i = 0; i < room_info->memberList.membersNum; i++) { SceNpMatching2RoomMemberDataInternal* sce_member = &room_info->memberList.members[i]; - if (strcmp(sce_member->userInfo.npId.handle.data, npid.handle.data) == 0) + if (strncmp(sce_member->userInfo.npId.handle.data, npid.handle.data, 16) == 0) { room_info->memberList.me = room_info->memberList.members + i; edata.add_relocation(room_info->memberList.me); diff --git a/rpcs3/Emu/NP/rpcn_client.cpp b/rpcs3/Emu/NP/rpcn_client.cpp index c1126b8698..86a688705e 100644 --- a/rpcs3/Emu/NP/rpcn_client.cpp +++ b/rpcs3/Emu/NP/rpcn_client.cpp @@ -896,7 +896,7 @@ namespace rpcn return error_and_disconnect("Failed to send all the bytes"); } - res = 0; + continue; } n_sent += res; } @@ -1055,6 +1055,8 @@ namespace rpcn found = found->ai_next; } + freeaddrinfo(addr_info); + if (!found_ipv4) { rpcn_log.error("connect: Failed to find IPv4 for %s", host); @@ -1156,7 +1158,7 @@ namespace rpcn if (!connected || terminate) { state = rpcn_state::failure_other; - return true; + return false; } if (received_version != RPCN_PROTOCOL_VERSION) @@ -1467,7 +1469,7 @@ namespace rpcn return error; } - bool rpcn_client::add_friend(const std::string& friend_username) + std::optional rpcn_client::add_friend(const std::string& friend_username) { std::vector data; std::copy(friend_username.begin(), friend_username.end(), std::back_inserter(data)); @@ -1478,19 +1480,18 @@ namespace rpcn std::vector packet_data; if (!forge_send_reply(CommandType::AddFriend, req_id, data, packet_data)) { - return false; + return std::nullopt; } vec_stream reply(packet_data); - auto error = static_cast(reply.get()); + const auto error = static_cast(reply.get()); - if (error != rpcn::ErrorType::NoError) - { - return false; - } + if (error == ErrorType::NoError) + rpcn_log.success("add_friend(\"%s\") succeeded", friend_username); + else + rpcn_log.error("add_friend(\"%s\") failed with error: %s", error); - rpcn_log.success("You have successfully added \"%s\" as a friend", friend_username); - return true; + return error; } bool rpcn_client::remove_friend(const std::string& friend_username) @@ -1755,7 +1756,7 @@ namespace rpcn { continue; } - pb_req.add_alloweduser(req->allowedUser[i].handle.data); + pb_req.add_alloweduser(np::npid_to_string(req->allowedUser[i])); } } @@ -1767,7 +1768,7 @@ namespace rpcn { continue; } - pb_req.add_blockeduser(req->blockedUser[i].handle.data); + pb_req.add_blockeduser(np::npid_to_string(req->blockedUser[i])); } } @@ -2266,7 +2267,7 @@ namespace rpcn for (usz i = 0; i < npids.size(); i++) { auto* npid_entry = pb_req.add_npids(); - npid_entry->set_npid(static_cast(npids[i].first.handle.data)); + npid_entry->set_npid(np::npid_to_string(npids[i].first)); npid_entry->set_pcid(npids[i].second); } @@ -2317,7 +2318,7 @@ namespace rpcn { np2_structs::GetScoreGameDataRequest pb_req; pb_req.set_boardid(board_id); - pb_req.set_npid(reinterpret_cast(npid.handle.data)); + pb_req.set_npid(np::npid_to_string(npid)); pb_req.set_pcid(pc_id); std::string serialized; @@ -2413,7 +2414,7 @@ namespace rpcn if (option->isLastChangedAuthorId) { - pb_req.set_islastchangedauthorid(option->isLastChangedAuthorId->handle.data); + pb_req.set_islastchangedauthorid(np::npid_to_string(*option->isLastChangedAuthorId)); } } @@ -2442,7 +2443,7 @@ namespace rpcn if (option->isLastChangedAuthorId) { - pb_req.set_islastchangedauthorid(option->isLastChangedAuthorId->handle.data); + pb_req.set_islastchangedauthorid(np::npid_to_string(*option->isLastChangedAuthorId)); } if (option->compareValue) @@ -2498,7 +2499,7 @@ namespace rpcn if (option->isLastChangedAuthorId) { - pb_req.set_islastchangedauthorid(option->isLastChangedAuthorId->handle.data); + pb_req.set_islastchangedauthorid(np::npid_to_string(*option->isLastChangedAuthorId)); } } diff --git a/rpcs3/Emu/NP/rpcn_client.h b/rpcs3/Emu/NP/rpcn_client.h index 035dc9cf3c..377cd9c898 100644 --- a/rpcs3/Emu/NP/rpcn_client.h +++ b/rpcs3/Emu/NP/rpcn_client.h @@ -79,6 +79,14 @@ public: res.push_back(vec[i]); i++; } + + // Make sure we hit terminating 0 + if (i >= vec.size()) + { + error = true; + return {}; + } + i++; if (!empty && res.empty()) @@ -293,7 +301,7 @@ namespace rpcn ErrorType send_reset_token(std::string_view npid, std::string_view email); ErrorType reset_password(std::string_view npid, std::string_view token, std::string_view password); ErrorType delete_account(); - bool add_friend(const std::string& friend_username); + std::optional add_friend(const std::string& friend_username); bool remove_friend(const std::string& friend_username); u32 get_num_friends(); diff --git a/rpcs3/Emu/NP/signaling_handler.cpp b/rpcs3/Emu/NP/signaling_handler.cpp index 2e4ac5df56..97a2673e0f 100644 --- a/rpcs3/Emu/NP/signaling_handler.cpp +++ b/rpcs3/Emu/NP/signaling_handler.cpp @@ -256,9 +256,7 @@ void signaling_handler::process_incoming_messages() addr.s_addr = op_addr; char ip_str[16]; inet_ntop(AF_INET, &addr, ip_str, sizeof(ip_str)); - std::string_view npid(sp->npid.handle.data); - - sign_log.trace("SP %s from %s:%d(npid: %s)", sp->command, ip_str, op_port, npid); + sign_log.trace("SP %s from %s:%d(npid: %s)", sp->command, ip_str, op_port, np::npid_to_string(sp->npid)); } bool reply = false, schedule_repeat = false; @@ -426,9 +424,10 @@ void signaling_handler::operator()() if (sig.sig_info->time_last_msg_recvd < now - 60s && cmd != signal_info) { // We had no connection to opponent for 60 seconds, consider the connection dead + auto retire_info = sig.sig_info; sign_log.notice("Timeout disconnection"); - update_si_status(sig.sig_info, SCE_NP_SIGNALING_CONN_STATUS_INACTIVE, SCE_NP_SIGNALING_ERROR_TIMEOUT); - retire_packet(sig.sig_info, signal_ping); // Retire ping packet if necessary + update_si_status(retire_info, SCE_NP_SIGNALING_CONN_STATUS_INACTIVE, SCE_NP_SIGNALING_ERROR_TIMEOUT); + retire_packet(retire_info, signal_ping); // Retire ping packet if necessary break; // qpackets has been emptied of all packets for this user so we're requeuing } @@ -674,9 +673,7 @@ std::shared_ptr signaling_handler::get_signaling_ptr(const signa { u32 conn_id; - char npid_buf[17]{}; - memcpy(npid_buf, sp->npid.handle.data, 16); - std::string npid(npid_buf); + std::string npid = np::npid_to_string(sp->npid); if (!npid_to_conn_id.contains(npid)) return nullptr; @@ -784,7 +781,7 @@ void signaling_handler::send_information_packets(u32 addr, u16 port, const SceNp u32 signaling_handler::get_always_conn_id(const SceNpId& npid) { - std::string npid_str(reinterpret_cast(npid.handle.data)); + std::string npid_str = np::npid_to_string(npid); if (npid_to_conn_id.contains(npid_str)) return ::at32(npid_to_conn_id, npid_str); @@ -810,9 +807,8 @@ u32 signaling_handler::init_sig1(const SceNpId& npid) sig_peers[conn_id]->conn_status = SCE_NP_SIGNALING_CONN_STATUS_PENDING; // Request peer infos from RPCN - std::string npid_str(reinterpret_cast(npid.handle.data)); auto& nph = g_fxo->get>(); - nph.req_sign_infos(npid_str, conn_id); + nph.req_sign_infos(np::npid_to_string(npid), conn_id); } return conn_id; @@ -839,7 +835,7 @@ std::optional signaling_handler::get_conn_id_from_npid(const SceNpId& npid) { std::lock_guard lock(data_mutex); - std::string npid_str(reinterpret_cast(npid.handle.data)); + std::string npid_str = np::npid_to_string(npid); if (npid_to_conn_id.contains(npid_str)) return ::at32(npid_to_conn_id, npid_str); diff --git a/rpcs3/Emu/RSX/Common/TextureUtils.cpp b/rpcs3/Emu/RSX/Common/TextureUtils.cpp index 96f87111ff..7fe0431baf 100644 --- a/rpcs3/Emu/RSX/Common/TextureUtils.cpp +++ b/rpcs3/Emu/RSX/Common/TextureUtils.cpp @@ -1200,27 +1200,59 @@ namespace rsx fmt::throw_exception("Unknown format 0x%x", texture_format); } - bool is_int8_remapped_format(u32 format) + rsx::flags32_t get_format_features(u32 texture_format) { - switch (format) + switch (texture_format) { + case CELL_GCM_TEXTURE_B8: + case CELL_GCM_TEXTURE_A1R5G5B5: + case CELL_GCM_TEXTURE_A4R4G4B4: + case CELL_GCM_TEXTURE_R5G6B5: + case CELL_GCM_TEXTURE_A8R8G8B8: + case CELL_GCM_TEXTURE_COMPRESSED_DXT1: + case CELL_GCM_TEXTURE_COMPRESSED_DXT23: + case CELL_GCM_TEXTURE_COMPRESSED_DXT45: + case CELL_GCM_TEXTURE_G8B8: + case CELL_GCM_TEXTURE_COMPRESSED_B8R8_G8R8: + case CELL_GCM_TEXTURE_COMPRESSED_R8B8_R8G8: + case CELL_GCM_TEXTURE_R6G5B5: + case CELL_GCM_TEXTURE_R5G5B5A1: + case CELL_GCM_TEXTURE_D1R5G5B5: + case CELL_GCM_TEXTURE_D8R8G8B8: + // Base texture formats - everything is supported + return RSX_FORMAT_FEATURE_SIGNED_COMPONENTS | RSX_FORMAT_FEATURE_GAMMA_CORRECTION | RSX_FORMAT_FEATURE_BIASED_NORMALIZATION; + case CELL_GCM_TEXTURE_DEPTH24_D8: case CELL_GCM_TEXTURE_DEPTH24_D8_FLOAT: case CELL_GCM_TEXTURE_DEPTH16: case CELL_GCM_TEXTURE_DEPTH16_FLOAT: + // Depth textures will hang the hardware if BX2 or GAMMA is active. ARGB8_SIGNED has no impact. + // UNSIGNED_REMAP=BIASED works on all formats including the float variants. + return RSX_FORMAT_FEATURE_BIASED_NORMALIZATION; + case CELL_GCM_TEXTURE_X16: + // X16 - GAMMA causes hangs. ARGB8_SIGNED is ignored. UNSIGNED_REMAP=BIASED works. + return RSX_FORMAT_FEATURE_BIASED_NORMALIZATION | RSX_FORMAT_FEATURE_16BIT_CHANNELS; case CELL_GCM_TEXTURE_Y16_X16: + // X16 | Y16 - GAMMA causes hangs. ARGB8_SIGNED works. UNSIGNED_REMAP=BIASED also works. + return RSX_FORMAT_FEATURE_SIGNED_COMPONENTS | RSX_FORMAT_FEATURE_BIASED_NORMALIZATION | RSX_FORMAT_FEATURE_16BIT_CHANNELS; + case CELL_GCM_TEXTURE_COMPRESSED_HILO8: + // GAMMA causes GPU hangs. ARGB8_SIGNED is ignored. UNSIGNED_REMAP=BIASED works. + return RSX_FORMAT_FEATURE_BIASED_NORMALIZATION; + case CELL_GCM_TEXTURE_COMPRESSED_HILO_S8: + // GAMMA causes hangs. Other flags ignored. + return 0; + case CELL_GCM_TEXTURE_W16_Z16_Y16_X16_FLOAT: case CELL_GCM_TEXTURE_W32_Z32_Y32_X32_FLOAT: case CELL_GCM_TEXTURE_X32_FLOAT: case CELL_GCM_TEXTURE_Y16_X16_FLOAT: - // NOTE: Special data formats (XY, HILO, DEPTH) are not RGB formats - return false; - default: - return true; + // Floating point textures. Nothing works. + return 0; } + fmt::throw_exception("Unknown format 0x%x", texture_format); } /** diff --git a/rpcs3/Emu/RSX/Common/TextureUtils.h b/rpcs3/Emu/RSX/Common/TextureUtils.h index cc40305721..c32152fecd 100644 --- a/rpcs3/Emu/RSX/Common/TextureUtils.h +++ b/rpcs3/Emu/RSX/Common/TextureUtils.h @@ -9,6 +9,8 @@ namespace rsx { + using flags32_t = u32; + enum texture_upload_context : u32 { shader_read = 1, @@ -125,6 +127,32 @@ namespace rsx using namespace format_class_; + enum format_features : u8 + { + RSX_FORMAT_FEATURE_SIGNED_COMPONENTS = (1 << 0), + RSX_FORMAT_FEATURE_BIASED_NORMALIZATION = (1 << 1), + RSX_FORMAT_FEATURE_GAMMA_CORRECTION = (1 << 2), + RSX_FORMAT_FEATURE_16BIT_CHANNELS = (1 << 3), // Complements RSX_FORMAT_FEATURE_SIGNED_COMPONENTS + }; + + using enum format_features; + + struct texture_format_ex + { + texture_format_ex() = default; + texture_format_ex(u32 bits) + : format_bits(bits) + {} + + bool valid() const { return format_bits != 0; } + u32 format() const { return format_bits & ~(CELL_GCM_TEXTURE_LN | CELL_GCM_TEXTURE_UN); } + + //private: + u32 format_bits = 0; + u32 features = 0; + u32 texel_remap_control = 0; + }; + // Sampled image descriptor class sampled_image_descriptor_base { @@ -167,6 +195,7 @@ namespace rsx u64 surface_cache_tag = 0; texcoord_xform_t texcoord_xform; + texture_format_ex format_ex; }; struct typeless_xfer @@ -257,7 +286,12 @@ namespace rsx u8 get_format_sample_count(rsx::surface_antialiasing antialias); u32 get_max_depth_value(rsx::surface_depth_format2 format); bool is_depth_stencil_format(rsx::surface_depth_format2 format); - bool is_int8_remapped_format(u32 format); // Returns true if the format is treated as INT8 by the RSX remapper. + + /** + * Format feature support. There is not simple format to determine what is supported here, results are from hw tests + * Returns a bitmask of supported features. + */ + rsx::flags32_t get_format_features(u32 texture_format); /** * Returns number of texel rows encoded in one pitch-length line of bytes diff --git a/rpcs3/Emu/RSX/GL/GLDraw.cpp b/rpcs3/Emu/RSX/GL/GLDraw.cpp index e8ea4bc714..475507ee78 100644 --- a/rpcs3/Emu/RSX/GL/GLDraw.cpp +++ b/rpcs3/Emu/RSX/GL/GLDraw.cpp @@ -2,6 +2,7 @@ #include "GLGSRender.h" #include "../rsx_methods.h" #include "../Common/BufferUtils.h" +#include "../Program/GLSLCommon.h" #include "Emu/RSX/NV47/HW/context_accessors.define.h" @@ -315,6 +316,8 @@ void GLGSRender::load_texture_env() if (sampler_state->validate()) { + sampler_state->format_ex = tex.format_ex(); + if (m_textures_dirty[i]) { m_fs_sampler_states[i].apply(tex, fs_sampler_state[i].get()); @@ -324,12 +327,17 @@ void GLGSRender::load_texture_env() m_graphics_state |= rsx::fragment_program_state_dirty; } - if (const auto texture_format = tex.format() & ~(CELL_GCM_TEXTURE_UN | CELL_GCM_TEXTURE_LN); - sampler_state->format_class != rsx::classify_format(texture_format) && - (texture_format == CELL_GCM_TEXTURE_A8R8G8B8 || texture_format == CELL_GCM_TEXTURE_D8R8G8B8)) + const auto texture_format = sampler_state->format_ex.format(); + // Depth format redirected to BGRA8 resample stage. Do not filter to avoid bits leaking. + // If accurate graphics are desired, force a bitcast to COLOR as a workaround. + const bool is_depth_reconstructed = sampler_state->format_class != rsx::classify_format(texture_format) && + (texture_format == CELL_GCM_TEXTURE_A8R8G8B8 || texture_format == CELL_GCM_TEXTURE_D8R8G8B8); + // SNORM conversion required in shader. Do not interpolate to avoid introducing discontinuities due to how negative numbers work + const bool is_snorm = (sampler_state->format_ex.texel_remap_control & rsx::texture_control_bits::SEXT_MASK) != 0; + + if (is_depth_reconstructed || is_snorm) { // Depth format redirected to BGRA8 resample stage. Do not filter to avoid bits leaking. - // If accurate graphics are desired, force a bitcast to COLOR as a workaround. m_fs_sampler_states[i].set_parameteri(GL_TEXTURE_MIN_FILTER, GL_NEAREST); m_fs_sampler_states[i].set_parameteri(GL_TEXTURE_MAG_FILTER, GL_NEAREST); } diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.cpp b/rpcs3/Emu/RSX/GL/GLGSRender.cpp index acf258cc56..ebff202303 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.cpp +++ b/rpcs3/Emu/RSX/GL/GLGSRender.cpp @@ -1351,7 +1351,7 @@ void GLGSRender::notify_tile_unbound(u32 tile) } } -bool GLGSRender::release_GCM_label(u32 address, u32 args) +bool GLGSRender::release_GCM_label(u32 type, u32 address, u32 args) { if (!backend_config.supports_host_gpu_labels) { @@ -1360,7 +1360,7 @@ bool GLGSRender::release_GCM_label(u32 address, u32 args) auto host_ctx = ensure(m_host_dma_ctrl->host_ctx()); - if (host_ctx->texture_loads_completed()) + if (type == NV4097_TEXTURE_READ_SEMAPHORE_RELEASE && host_ctx->texture_loads_completed()) { // We're about to poll waiting for GPU state, ensure the context is still valid. gl::check_state(); diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.h b/rpcs3/Emu/RSX/GL/GLGSRender.h index 5627216055..a05eb0bf3d 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.h +++ b/rpcs3/Emu/RSX/GL/GLGSRender.h @@ -206,7 +206,7 @@ public: void discard_occlusion_query(rsx::reports::occlusion_query_info* query) override; // DMA - bool release_GCM_label(u32 address, u32 data) override; + bool release_GCM_label(u32 type, u32 address, u32 data) override; void enqueue_host_context_write(u32 offset, u32 size, const void* data); void on_guest_texture_read(); diff --git a/rpcs3/Emu/RSX/NV47/HW/nv3089.cpp b/rpcs3/Emu/RSX/NV47/HW/nv3089.cpp index 111611d887..6ac502b462 100644 --- a/rpcs3/Emu/RSX/NV47/HW/nv3089.cpp +++ b/rpcs3/Emu/RSX/NV47/HW/nv3089.cpp @@ -4,6 +4,7 @@ #include "Emu/RSX/RSXThread.h" #include "Emu/RSX/Core/RSXReservationLock.hpp" #include "Emu/RSX/Common/tiled_dma_copy.hpp" +#include "Emu/RSX/Host/MM.h" #include "context_accessors.define.h" @@ -581,9 +582,11 @@ namespace rsx const u16 out_h = REGS(ctx)->blit_engine_output_height(); // Lock here. RSX cannot execute any locking operations from this point, including ZCULL read barriers + const u32 read_length = src.pitch * src.height; + const u32 write_length = dst.pitch * dst.clip_height; auto res = ::rsx::reservation_lock( - dst.rsx_address, dst.pitch * dst.clip_height, - src.rsx_address, src.pitch * src.height); + dst.rsx_address, write_length, + src.rsx_address, read_length); if (!g_cfg.video.force_cpu_blit_processing && (dst.dma == CELL_GCM_CONTEXT_DMA_MEMORY_FRAME_BUFFER || src.dma == CELL_GCM_CONTEXT_DMA_MEMORY_FRAME_BUFFER) && @@ -593,6 +596,14 @@ namespace rsx return; } + // Conservative MM flush + rsx::simple_array flush_mm_ranges = + { + utils::address_range64::start_length(reinterpret_cast(dst.pixels), write_length), + utils::address_range64::start_length(reinterpret_cast(src.pixels), read_length) + }; + rsx::mm_flush(flush_mm_ranges); + std::vector mirror_tmp; bool src_is_temp = false; @@ -619,7 +630,7 @@ namespace rsx const bool interpolate = in_inter == blit_engine::transfer_interpolator::foh; auto real_dst = dst.pixels; - const auto tiled_region = RSX(ctx)->get_tiled_memory_region(utils::address_range32::start_length(dst.rsx_address, dst.pitch * dst.clip_height)); + const auto tiled_region = RSX(ctx)->get_tiled_memory_region(utils::address_range32::start_length(dst.rsx_address, write_length)); std::vector tmp; if (tiled_region) diff --git a/rpcs3/Emu/RSX/NV47/HW/nv406e.cpp b/rpcs3/Emu/RSX/NV47/HW/nv406e.cpp index cbb04d140a..4307cc8289 100644 --- a/rpcs3/Emu/RSX/NV47/HW/nv406e.cpp +++ b/rpcs3/Emu/RSX/NV47/HW/nv406e.cpp @@ -86,7 +86,7 @@ namespace rsx RSX(ctx)->performance_counters.idle_time += (get_system_time() - start); } - void semaphore_release(context* ctx, u32 /*reg*/, u32 arg) + void semaphore_release(context* ctx, u32 reg, u32 arg) { const u32 offset = REGS(ctx)->semaphore_offset_406e(); @@ -122,7 +122,7 @@ namespace rsx arg = 1; } - util::write_gcm_label(ctx, addr, arg); + util::write_gcm_label(ctx, reg, addr, arg); } } } diff --git a/rpcs3/Emu/RSX/NV47/HW/nv4097.cpp b/rpcs3/Emu/RSX/NV47/HW/nv4097.cpp index 929925bcb1..17ee040f8c 100644 --- a/rpcs3/Emu/RSX/NV47/HW/nv4097.cpp +++ b/rpcs3/Emu/RSX/NV47/HW/nv4097.cpp @@ -690,7 +690,7 @@ namespace rsx }); } - void texture_read_semaphore_release(context* ctx, u32 /*reg*/, u32 arg) + void texture_read_semaphore_release(context* ctx, u32 reg, u32 arg) { // Pipeline barrier seems to be equivalent to a SHADER_READ stage barrier. // Ideally the GPU only needs to have cached all textures declared up to this point before writing the label. @@ -715,15 +715,15 @@ namespace rsx if (g_cfg.video.strict_rendering_mode) [[ unlikely ]] { - util::write_gcm_label(ctx, addr, arg); + util::write_gcm_label(ctx, reg, addr, arg); } else { - util::write_gcm_label(ctx, addr, arg); + util::write_gcm_label(ctx, reg, addr, arg); } } - void back_end_write_semaphore_release(context* ctx, u32 /*reg*/, u32 arg) + void back_end_write_semaphore_release(context* ctx, u32 reg, u32 arg) { // Full pipeline barrier. GPU must flush pipeline before writing the label @@ -744,7 +744,7 @@ namespace rsx } const u32 val = (arg & 0xff00ff00) | ((arg & 0xff) << 16) | ((arg >> 16) & 0xff); - util::write_gcm_label(ctx, addr, val); + util::write_gcm_label(ctx, reg, addr, val); } void sync(context* ctx, u32, u32) diff --git a/rpcs3/Emu/RSX/NV47/HW/nv47_sync.hpp b/rpcs3/Emu/RSX/NV47/HW/nv47_sync.hpp index 9f39f84c00..fe2522c31c 100644 --- a/rpcs3/Emu/RSX/NV47/HW/nv47_sync.hpp +++ b/rpcs3/Emu/RSX/NV47/HW/nv47_sync.hpp @@ -13,13 +13,13 @@ namespace rsx namespace util { template - static void write_gcm_label(context* ctx, u32 address, u32 data) + static void write_gcm_label(context* ctx, u32 type, u32 address, u32 data) { const bool is_flip_sema = (address == (RSX(ctx)->label_addr + 0x10) || address == (RSX(ctx)->device_addr + 0x30)); if (!is_flip_sema) { // First, queue the GPU work. If it flushes the queue for us, the following routines will be faster. - const bool handled = RSX(ctx)->get_backend_config().supports_host_gpu_labels && RSX(ctx)->release_GCM_label(address, data); + const bool handled = RSX(ctx)->get_backend_config().supports_host_gpu_labels && RSX(ctx)->release_GCM_label(type, address, data); if (vm::_ref(address) == data) { diff --git a/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.cpp b/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.cpp index ef2029c652..c00cef47e6 100644 --- a/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.cpp +++ b/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.cpp @@ -1192,7 +1192,7 @@ bool FragmentProgramDecompiler::handle_tex_srb(u32 opcode) if (dst.exp_tex) { properties.has_exp_tex_op = true; - AddCode("_enable_texture_expand();"); + AddCode("_enable_texture_expand($_i);"); } // Shadow proj diff --git a/rpcs3/Emu/RSX/Program/GLSLCommon.cpp b/rpcs3/Emu/RSX/Program/GLSLCommon.cpp index 1421b179fc..ebf73e935a 100644 --- a/rpcs3/Emu/RSX/Program/GLSLCommon.cpp +++ b/rpcs3/Emu/RSX/Program/GLSLCommon.cpp @@ -337,21 +337,21 @@ namespace glsl // Declare special texture control flags program_common::define_glsl_constants(OS, { - { "GAMMA_R_BIT " , rsx::texture_control_bits::GAMMA_R }, - { "GAMMA_G_BIT " , rsx::texture_control_bits::GAMMA_G }, - { "GAMMA_B_BIT " , rsx::texture_control_bits::GAMMA_B }, - { "GAMMA_A_BIT " , rsx::texture_control_bits::GAMMA_A }, - { "EXPAND_R_BIT" , rsx::texture_control_bits::EXPAND_R }, - { "EXPAND_G_BIT" , rsx::texture_control_bits::EXPAND_G }, - { "EXPAND_B_BIT" , rsx::texture_control_bits::EXPAND_B }, - { "EXPAND_A_BIT" , rsx::texture_control_bits::EXPAND_A }, - { "SEXT_R_BIT" , rsx::texture_control_bits::SEXT_R }, - { "SEXT_G_BIT" , rsx::texture_control_bits::SEXT_G }, - { "SEXT_B_BIT" , rsx::texture_control_bits::SEXT_B }, - { "SEXT_A_BIT" , rsx::texture_control_bits::SEXT_A }, - { "WRAP_S_BIT", rsx::texture_control_bits::WRAP_S }, - { "WRAP_T_BIT", rsx::texture_control_bits::WRAP_T }, - { "WRAP_R_BIT", rsx::texture_control_bits::WRAP_R }, + { "GAMMA_R_BIT ", rsx::texture_control_bits::GAMMA_R }, + { "GAMMA_G_BIT ", rsx::texture_control_bits::GAMMA_G }, + { "GAMMA_B_BIT ", rsx::texture_control_bits::GAMMA_B }, + { "GAMMA_A_BIT ", rsx::texture_control_bits::GAMMA_A }, + { "EXPAND_R_BIT", rsx::texture_control_bits::EXPAND_R }, + { "EXPAND_G_BIT", rsx::texture_control_bits::EXPAND_G }, + { "EXPAND_B_BIT", rsx::texture_control_bits::EXPAND_B }, + { "EXPAND_A_BIT", rsx::texture_control_bits::EXPAND_A }, + { "SEXT_R_BIT", rsx::texture_control_bits::SEXT_R }, + { "SEXT_G_BIT", rsx::texture_control_bits::SEXT_G }, + { "SEXT_B_BIT", rsx::texture_control_bits::SEXT_B }, + { "SEXT_A_BIT", rsx::texture_control_bits::SEXT_A }, + { "WRAP_S_BIT", rsx::texture_control_bits::WRAP_S }, + { "WRAP_T_BIT", rsx::texture_control_bits::WRAP_T }, + { "WRAP_R_BIT", rsx::texture_control_bits::WRAP_R }, { "ALPHAKILL ", rsx::texture_control_bits::ALPHAKILL }, { "RENORMALIZE ", rsx::texture_control_bits::RENORMALIZE }, @@ -360,7 +360,12 @@ namespace glsl { "FILTERED_MAG_BIT", rsx::texture_control_bits::FILTERED_MAG }, { "FILTERED_MIN_BIT", rsx::texture_control_bits::FILTERED_MIN }, { "INT_COORDS_BIT ", rsx::texture_control_bits::UNNORMALIZED_COORDS }, - { "CLAMP_COORDS_BIT", rsx::texture_control_bits::CLAMP_TEXCOORDS_BIT } + { "CLAMP_COORDS_BIT", rsx::texture_control_bits::CLAMP_TEXCOORDS_BIT }, + + { "FORMAT_FEATURE_SIGNED_BIT", rsx::texture_control_bits::FF_SIGNED_BIT }, + { "FORMAT_FEATURE_GAMMA_BIT", rsx::texture_control_bits::FF_GAMMA_BIT }, + { "FORMAT_FEATURE_BIASED_RENORMALIZATION_BIT", rsx::texture_control_bits::FF_BIASED_RENORM_BIT }, + { "FORMAT_FEATURE_16BIT_CHANNELS_BIT", rsx::texture_control_bits::FF_16BIT_CHANNELS_BIT } }); if (props.require_texture_expand) diff --git a/rpcs3/Emu/RSX/Program/GLSLCommon.h b/rpcs3/Emu/RSX/Program/GLSLCommon.h index 942374436e..ae22464f12 100644 --- a/rpcs3/Emu/RSX/Program/GLSLCommon.h +++ b/rpcs3/Emu/RSX/Program/GLSLCommon.h @@ -37,12 +37,17 @@ namespace rsx WRAP_S, WRAP_T, WRAP_R, + FF_SIGNED_BIT, + FF_BIASED_RENORM_BIT, + FF_GAMMA_BIT, + FF_16BIT_CHANNELS_BIT, GAMMA_CTRL_MASK = (1 << GAMMA_R) | (1 << GAMMA_G) | (1 << GAMMA_B) | (1 << GAMMA_A), EXPAND_MASK = (1 << EXPAND_R) | (1 << EXPAND_G) | (1 << EXPAND_B) | (1 << EXPAND_A), EXPAND_OFFSET = EXPAND_A, SEXT_MASK = (1 << SEXT_R) | (1 << SEXT_G) | (1 << SEXT_B) | (1 << SEXT_A), - SEXT_OFFSET = SEXT_A + SEXT_OFFSET = SEXT_A, + FORMAT_FEATURES_OFFSET = FF_SIGNED_BIT, }; enum ROP_control_bits : u32 diff --git a/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXFragmentTextureOps.glsl b/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXFragmentTextureOps.glsl index f0457f0034..43dcf9e6eb 100644 --- a/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXFragmentTextureOps.glsl +++ b/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXFragmentTextureOps.glsl @@ -20,13 +20,34 @@ R"( #define SEXT_MASK (SEXT_R_MASK | SEXT_G_MASK | SEXT_B_MASK | SEXT_A_MASK) #define FILTERED_MASK (FILTERED_MAG_BIT | FILTERED_MIN_BIT) +#define FORMAT_FEATURE_SIGNED (1 << FORMAT_FEATURE_SIGNED_BIT) +#define FORMAT_FEATURE_GAMMA (1 << FORMAT_FEATURE_GAMMA_BIT) +#define FORMAT_FEATURE_BIASED_RENORMALIZATION (1 << FORMAT_FEATURE_BIASED_RENORMALIZATION_BIT) +#define FORMAT_FEATURE_16BIT_CHANNELS (1 << FORMAT_FEATURE_16BIT_CHANNELS_BIT) +#define FORMAT_FEATURE_MASK (FORMAT_FEATURE_SIGNED | FORMAT_FEATURE_GAMMA | FORMAT_FEATURE_BIASED_RENORMALIZATION | FORMAT_FEATURE_16BIT_CHANNELS) + #ifdef _ENABLE_TEXTURE_EXPAND + // NOTE: BX2 expansion overrides GAMMA correction uint _texture_flag_override = 0; - #define _enable_texture_expand() _texture_flag_override = SIGN_EXPAND_MASK - #define _disable_texture_expand() _texture_flag_override = 0 - #define TEX_FLAGS(index) (TEX_PARAM(index).flags | _texture_flag_override) + uint _texture_flag_erase = 0; + bool _texture_bx2_active = false; + #define _enable_texture_expand(index) \ + do { \ + if (_test_bit(TEX_PARAM(index).flags, FORMAT_FEATURE_BIASED_RENORMALIZATION_BIT)) { \ + _texture_flag_override = SIGN_EXPAND_MASK & (_get_bits(TEX_PARAM(index).remap, 16, 4) << EXPAND_A_BIT); \ + _texture_flag_erase = GAMMA_CTRL_MASK; \ + _texture_bx2_active = true; \ + } \ + } while (false) + #define _disable_texture_expand() \ + do { \ + _texture_flag_override = 0; \ + _texture_flag_erase = 0; \ + _texture_bx2_active = false; \ + } while (false) + #define TEX_FLAGS(index) ((TEX_PARAM(index).flags & ~(_texture_flag_erase)) | _texture_flag_override) #else - #define TEX_FLAGS(index) TEX_PARAM(index).flags + #define TEX_FLAGS(index) (TEX_PARAM(index).flags) #endif #define TEX_NAME(index) tex##index @@ -175,15 +196,24 @@ vec4 _texcoord_xform_shadow(const in vec4 coord4, const in sampler_info params) vec4 _sext_unorm8x4(const in vec4 x) { // TODO: Handle clamped sign-extension - const vec4 bits = floor(fma(x, vec4(255.f), vec4(0.5f))); - const bvec4 sign_check = lessThan(bits, vec4(128.f)); - const vec4 ret = _select(bits - 256.f, bits, sign_check); - return ret / 127.f; + const uint shift = 32 - 8; // sext 8-bit value into 32-bit container + const uvec4 ubits = uvec4(floor(fma(x, vec4(255.f), vec4(0.5f)))); + const ivec4 ibits = ivec4(ubits << shift); + return (ibits >> shift) / 127.f; +} + +vec4 _sext_unorm16x4(const in vec4 x) +{ + // TODO: Handle clamped sign-extension + const uint shift = 32 - 16; // sext 16-bit value into 32-bit container + const uvec4 ubits = uvec4(floor(fma(x, vec4(65535.f), vec4(0.5f)))); + const ivec4 ibits = ivec4(ubits << shift); + return (ibits >> shift) / 32767.f; } vec4 _process_texel(in vec4 rgba, const in uint control_bits) { - if (control_bits == 0) + if ((control_bits & ~FORMAT_FEATURE_MASK) == 0u) { return rgba; } @@ -210,31 +240,46 @@ vec4 _process_texel(in vec4 rgba, const in uint control_bits) uvec4 mask; vec4 convert; - uint op_mask = control_bits & uint(SIGN_EXPAND_MASK); - if (op_mask != 0u) - { - // Expand to signed normalized by decompressing the signal - mask = uvec4(op_mask) & uvec4(EXPAND_R_MASK, EXPAND_G_MASK, EXPAND_B_MASK, EXPAND_A_MASK); - convert = (rgba * 2.f - 1.f); - rgba = _select(rgba, convert, notEqual(mask, uvec4(0))); - } + uint op_mask = control_bits & uint(SEXT_MASK); + uint ch_mask = 0xFu; - op_mask = control_bits & uint(SEXT_MASK); if (op_mask != 0u) { // Sign-extend the input signal mask = uvec4(op_mask) & uvec4(SEXT_R_MASK, SEXT_G_MASK, SEXT_B_MASK, SEXT_A_MASK); - convert = _sext_unorm8x4(rgba); + if (_test_bit(control_bits, FORMAT_FEATURE_16BIT_CHANNELS_BIT)) + convert = _sext_unorm16x4(rgba); + else + convert = _sext_unorm8x4(rgba); rgba = _select(rgba, convert, notEqual(mask, uvec4(0))); + ch_mask &= ~(op_mask >> SEXT_A_BIT); } - op_mask = control_bits & uint(GAMMA_CTRL_MASK); + op_mask = control_bits & uint(GAMMA_CTRL_MASK) & (ch_mask << GAMMA_A_BIT); if (op_mask != 0u) { // Gamma correction mask = uvec4(op_mask) & uvec4(GAMMA_R_MASK, GAMMA_G_MASK, GAMMA_B_MASK, GAMMA_A_MASK); convert = srgb_to_linear(rgba); - return _select(rgba, convert, notEqual(mask, uvec4(0))); + rgba = _select(rgba, convert, notEqual(mask, uvec4(0))); + ch_mask &= ~(op_mask >> GAMMA_A_BIT); + } + + op_mask = control_bits & uint(SIGN_EXPAND_MASK) & (ch_mask << EXPAND_A_BIT); + if (op_mask != 0u) + { + // Expand to signed normalized by decompressing the signal + mask = uvec4(op_mask) & uvec4(EXPAND_R_MASK, EXPAND_G_MASK, EXPAND_B_MASK, EXPAND_A_MASK); +#ifdef _ENABLE_TEXTURE_EXPAND + if (_texture_bx2_active) + convert = (rgba * 2.f - 1.f); + else +#endif + if (_test_bit(control_bits, FORMAT_FEATURE_16BIT_CHANNELS_BIT)) + convert = (floor(fma(rgba, vec4(65535.f), vec4(0.5f))) - 32768.f) / 32767.f; + else + convert = (floor(fma(rgba, vec4(255.f), vec4(0.5f))) - 128.f) / 127.f; + rgba = _select(rgba, convert, notEqual(mask, uvec4(0))); } return rgba; diff --git a/rpcs3/Emu/RSX/Program/GLSLTypes.h b/rpcs3/Emu/RSX/Program/GLSLTypes.h index 5b1b61396e..8f7cb6fa03 100644 --- a/rpcs3/Emu/RSX/Program/GLSLTypes.h +++ b/rpcs3/Emu/RSX/Program/GLSLTypes.h @@ -9,7 +9,7 @@ namespace glsl glsl_compute_program = 2, // Meta - glsl_invalid_program = 0xff + glsl_invalid_program = 7 }; enum glsl_rules : unsigned char diff --git a/rpcs3/Emu/RSX/Program/ProgramStateCache.cpp b/rpcs3/Emu/RSX/Program/ProgramStateCache.cpp index 32476dc62c..6e1f93f33a 100644 --- a/rpcs3/Emu/RSX/Program/ProgramStateCache.cpp +++ b/rpcs3/Emu/RSX/Program/ProgramStateCache.cpp @@ -1,5 +1,6 @@ #include "stdafx.h" #include "ProgramStateCache.h" +#include "FragmentProgramDecompiler.h" #include "Emu/system_config.h" #include "Emu/RSX/Core/RSXDriverState.h" #include "util/sysinfo.hpp" @@ -637,58 +638,51 @@ fragment_program_utils::fragment_program_metadata fragment_program_utils::analys while (true) { const auto inst = v128::loadu(instBuffer, index); + const auto d0 = OPDEST::from_be32(inst._u32[0]); + const auto opcode = static_cast(d0.opcode); - // Check for opcode high bit which indicates a branch instructions (opcode 0x40...0x45) - if (inst._u32[2] & (1 << 23)) + switch (opcode) { + case RSX_FP_OPCODE_TEX: + case RSX_FP_OPCODE_TEXBEM: + case RSX_FP_OPCODE_TXP: + case RSX_FP_OPCODE_TXPBEM: + case RSX_FP_OPCODE_TXD: + case RSX_FP_OPCODE_TXB: + case RSX_FP_OPCODE_TXL: + result.referenced_textures_mask |= (1 << d0.tex_num); + result.has_tex_bx2_conv |= !!d0.exp_tex; + break; + case RSX_FP_OPCODE_PK4: + case RSX_FP_OPCODE_UP4: + case RSX_FP_OPCODE_PK2: + case RSX_FP_OPCODE_UP2: + case RSX_FP_OPCODE_PKB: + case RSX_FP_OPCODE_UPB: + case RSX_FP_OPCODE_PK16: + case RSX_FP_OPCODE_UP16: + case RSX_FP_OPCODE_PKG: + case RSX_FP_OPCODE_UPG: + result.has_pack_instructions = true; + break; + case RSX_FP_OPCODE_BRK: + case RSX_FP_OPCODE_CAL: + case RSX_FP_OPCODE_IFE: + case RSX_FP_OPCODE_LOOP: + case RSX_FP_OPCODE_REP: + case RSX_FP_OPCODE_RET: // NOTE: Jump instructions are not yet proved to work outside of loops and if/else blocks // Otherwise we would need to follow the execution chain result.has_branch_instructions = true; + break; } - else - { - const u32 opcode = (inst._u32[0] >> 16) & 0x3F; - if (opcode) - { - switch (opcode) - { - case RSX_FP_OPCODE_TEX: - case RSX_FP_OPCODE_TEXBEM: - case RSX_FP_OPCODE_TXP: - case RSX_FP_OPCODE_TXPBEM: - case RSX_FP_OPCODE_TXD: - case RSX_FP_OPCODE_TXB: - case RSX_FP_OPCODE_TXL: - { - //Bits 17-20 of word 1, swapped within u16 sections - //Bits 16-23 are swapped into the upper 8 bits (24-31) - const u32 tex_num = (inst._u32[0] >> 25) & 15; - result.referenced_textures_mask |= (1 << tex_num); - break; - } - case RSX_FP_OPCODE_PK4: - case RSX_FP_OPCODE_UP4: - case RSX_FP_OPCODE_PK2: - case RSX_FP_OPCODE_UP2: - case RSX_FP_OPCODE_PKB: - case RSX_FP_OPCODE_UPB: - case RSX_FP_OPCODE_PK16: - case RSX_FP_OPCODE_UP16: - case RSX_FP_OPCODE_PKG: - case RSX_FP_OPCODE_UPG: - { - result.has_pack_instructions = true; - break; - } - } - } - if (is_any_src_constant(inst)) - { - //Instruction references constant, skip one slot occupied by data - index++; - result.program_constants_buffer_length += 16; - } + if (rsx::assembler::FP::get_operand_count(opcode) > 0 && + is_any_src_constant(inst)) + { + // Instruction references constant, skip one slot occupied by data + index++; + result.program_constants_buffer_length += 16; } index++; diff --git a/rpcs3/Emu/RSX/Program/ProgramStateCache.h b/rpcs3/Emu/RSX/Program/ProgramStateCache.h index efd5dd326a..ce5be4a425 100644 --- a/rpcs3/Emu/RSX/Program/ProgramStateCache.h +++ b/rpcs3/Emu/RSX/Program/ProgramStateCache.h @@ -59,6 +59,7 @@ namespace program_hash_util bool has_pack_instructions; bool has_branch_instructions; + bool has_tex_bx2_conv; bool is_nop_shader; // Does this affect Z-pass testing??? }; diff --git a/rpcs3/Emu/RSX/Program/RSXFragmentProgram.h b/rpcs3/Emu/RSX/Program/RSXFragmentProgram.h index d93ec760e6..7bb8517b85 100644 --- a/rpcs3/Emu/RSX/Program/RSXFragmentProgram.h +++ b/rpcs3/Emu/RSX/Program/RSXFragmentProgram.h @@ -54,6 +54,14 @@ union OPDEST u32 : 9; u32 write_mask : 4; }; + + static OPDEST from_be32(u32 be_word) + { + const u32 _hex = + ((be_word & 0x00FF00FF) << 8) | + ((be_word & 0xFF00FF00) >> 8); + return OPDEST{ .HEX = _hex }; + } }; union SRC0 diff --git a/rpcs3/Emu/RSX/RSXTexture.cpp b/rpcs3/Emu/RSX/RSXTexture.cpp index 52f8183545..6f8eec0e3b 100644 --- a/rpcs3/Emu/RSX/RSXTexture.cpp +++ b/rpcs3/Emu/RSX/RSXTexture.cpp @@ -2,8 +2,24 @@ #include "RSXTexture.h" #include "rsx_utils.h" +#include "Common/TextureUtils.h" +#include "Program/GLSLCommon.h" #include "Emu/system_config.h" +#include "util/simd.hpp" + +#if !defined(_MSC_VER) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wold-style-cast" +#endif + +#if defined(ARCH_ARM64) +#if !defined(_MSC_VER) +#pragma GCC diagnostic ignored "-Wstrict-aliasing" +#endif +#undef FORCE_INLINE +#include "Emu/CPU/sse2neon.h" +#endif namespace rsx { @@ -49,6 +65,65 @@ namespace rsx return ((registers[NV4097_SET_TEXTURE_FORMAT + (m_index * 8)] >> 8) & 0xff); } + texture_format_ex fragment_texture::format_ex() const + { + const auto format_bits = format(); + const auto base_format = format_bits & ~(CELL_GCM_TEXTURE_UN | CELL_GCM_TEXTURE_LN); + const auto format_features = rsx::get_format_features(base_format); + if (format_features == 0) + { + return { format_bits }; + } + + // NOTE: The unsigned_remap=bias flag being set flags the texture as being compressed normal (2n-1 / BX2) (UE3) + // NOTE: The ARGB8_signed flag means to reinterpret the raw bytes as signed. This is different than unsigned_remap=bias which does range decompression. + // This is a separate method of setting the format to signed mode without doing so per-channel + // Precedence = SNORM > GAMMA > UNSIGNED_REMAP/BX2 + // Games using mixed flags: (See Resistance 3 for GAMMA/BX2 relationship, UE3 for BX2 effect) + u32 argb_signed_ = 0; + u32 unsigned_remap_ = 0; + u32 gamma_ = 0; + + if (format_features & RSX_FORMAT_FEATURE_SIGNED_COMPONENTS) + { + // Tests show this is applied pre-readout. It's just a property of the incoming bytes and is therefore subject to remap. + argb_signed_ = decoded_remap().shuffle_mask_bits(argb_signed()); + } + + if (format_features & RSX_FORMAT_FEATURE_GAMMA_CORRECTION) + { + // Tests show this is applied post-readout. It's a property of the final value stored in the register and is not remapped. + // NOTE: GAMMA correction has no algorithmic effect on constants (0 and 1) so we need not mask it out for correctness. + gamma_ = gamma() & ~(argb_signed_); + } + + if (format_features & RSX_FORMAT_FEATURE_BIASED_NORMALIZATION) + { + // The renormalization flag applies to all channels. It is weaker than the other flags. + // This applies on input and is subject to remap overrides + if (unsigned_remap() == CELL_GCM_TEXTURE_UNSIGNED_REMAP_BIASED) + { + unsigned_remap_ = decoded_remap().shuffle_mask_bits(0xFu) & ~(argb_signed_ | gamma_); + } + } + + u32 format_convert = gamma_; + + // The options are mutually exclusive + ensure((argb_signed_ & gamma_) == 0); + ensure((argb_signed_ & unsigned_remap_) == 0); + ensure((gamma_ & unsigned_remap_) == 0); + + // NOTE: Hardware tests show that remapping bypasses the channel swizzles completely + format_convert |= (argb_signed_ << texture_control_bits::SEXT_OFFSET); + format_convert |= (unsigned_remap_ << texture_control_bits::EXPAND_OFFSET); + + texture_format_ex result { format_bits }; + result.features = format_features; + result.texel_remap_control = format_convert; + return result; + } + bool fragment_texture::is_compressed_format() const { int texture_format = format() & ~(CELL_GCM_TEXTURE_LN | CELL_GCM_TEXTURE_UN); @@ -291,7 +366,68 @@ namespace rsx u32 fragment_texture::border_color() const { - return registers[NV4097_SET_TEXTURE_BORDER_COLOR + (m_index * 8)]; + const u32 raw = registers[NV4097_SET_TEXTURE_BORDER_COLOR + (m_index * 8)]; + const u32 sext = argb_signed(); + + if (!sext) [[ likely ]] + { + return raw; + } + + // Border color is broken on PS3. The SNORM behavior is completely broken and behaves like BIASED renormalization instead. + // To solve the mismatch, we need to first do a bit expansion on the value then store it as sign extended. The second part is a natural part of numbers on a binary system, so we only need to do the former. + // Note that the input color is in BE order (BGRA) so we reverse the mask to match. + static constexpr u32 expand4_lut[16] = + { + 0x00000000u, // 0000 + 0xFF000000u, // 0001 + 0x00FF0000u, // 0010 + 0xFFFF0000u, // 0011 + 0x0000FF00u, // 0100 + 0xFF00FF00u, // 0101 + 0x00FFFF00u, // 0110 + 0xFFFFFF00u, // 0111 + 0x000000FFu, // 1000 + 0xFF0000FFu, // 1001 + 0x00FF00FFu, // 1010 + 0xFFFF00FFu, // 1011 + 0x0000FFFFu, // 1100 + 0xFF00FFFFu, // 1101 + 0x00FFFFFFu, // 1110 + 0xFFFFFFFFu // 1111 + }; + + // Bit pattern expand + const u32 mask = expand4_lut[sext]; + + // Now we perform the compensation operation + // BIAS operation = (V - 128 / 127) + + // Load + const __m128i _0 = _mm_setzero_si128(); + const __m128i _128 = _mm_set1_epi32(128); + + // Explode the bytes. + __m128i v = _mm_cvtsi32_si128(raw); + v = _mm_unpacklo_epi8(v, _0); + v = _mm_unpacklo_epi16(v, _0); + + // Conversion: x = (y - 128) + v = _mm_sub_epi32(v, _128); + + // Convert to signed encoding (reverse sext) + v = _mm_slli_epi32(v, 24); + v = _mm_srli_epi32(v, 24); + + // Pack down + v = _mm_packs_epi32(v, _0); + v = _mm_packus_epi16(v, _0); + + // Read + const u32 conv = _mm_cvtsi128_si32(v); + + // Merge + return (conv & mask) | (raw & ~mask); } color4f fragment_texture::remapped_border_color() const diff --git a/rpcs3/Emu/RSX/RSXTexture.h b/rpcs3/Emu/RSX/RSXTexture.h index 5517a39e3f..3b0ccdebce 100644 --- a/rpcs3/Emu/RSX/RSXTexture.h +++ b/rpcs3/Emu/RSX/RSXTexture.h @@ -4,6 +4,8 @@ namespace rsx { + struct texture_format_ex; + class fragment_texture { protected: @@ -33,6 +35,7 @@ namespace rsx // cubemap as a separate dimension. rsx::texture_dimension_extended get_extended_texture_dimension() const; u8 format() const; + texture_format_ex format_ex() const; bool is_compressed_format() const; u16 mipmap() const; diff --git a/rpcs3/Emu/RSX/RSXThread.cpp b/rpcs3/Emu/RSX/RSXThread.cpp index 44e8d8fb3f..5bfc60ee2a 100644 --- a/rpcs3/Emu/RSX/RSXThread.cpp +++ b/rpcs3/Emu/RSX/RSXThread.cpp @@ -2321,55 +2321,17 @@ namespace rsx } } - if (rsx::is_int8_remapped_format(format)) + if (const auto& format_ex = sampler_descriptors[i]->format_ex; format_ex.features != 0) { - // Special operations applied to 8-bit formats such as gamma correction and sign conversion - // NOTE: The unsigned_remap=bias flag being set flags the texture as being compressed normal (2n-1 / BX2) (UE3) - // NOTE: The ARGB8_signed flag means to reinterpret the raw bytes as signed. This is different than unsigned_remap=bias which does range decompression. - // This is a separate method of setting the format to signed mode without doing so per-channel - // Precedence = SNORM > GAMMA > UNSIGNED_REMAP (See Resistance 3 for GAMMA/BX2 relationship, UE3 for BX2 effect) + texture_control |= format_ex.texel_remap_control; + texture_control |= format_ex.features << texture_control_bits::FORMAT_FEATURES_OFFSET; - const u32 argb8_signed = tex.argb_signed(); // _SNROM - const u32 gamma = tex.gamma() & ~argb8_signed; // _SRGB - const u32 unsigned_remap = (tex.unsigned_remap() == CELL_GCM_TEXTURE_UNSIGNED_REMAP_NORMAL)? 0u : (~(gamma | argb8_signed) & 0xF); // _BX2 - u32 argb8_convert = gamma; - - // The options are mutually exclusive - ensure((argb8_signed & gamma) == 0); - ensure((argb8_signed & unsigned_remap) == 0); - ensure((gamma & unsigned_remap) == 0); - - // Helper function to apply a per-channel mask based on an input mask - const auto apply_sign_convert_mask = [&](u32 mask, u32 bit_offset) + if (current_fp_metadata.has_tex_bx2_conv) { - // TODO: Use actual remap mask to account for 0 and 1 overrides in default mapping - // TODO: Replace this clusterfuck of texture control with matrix transformation - const auto remap_ctrl = (tex.remap() >> 8) & 0xAA; - if (remap_ctrl == 0xAA) - { - argb8_convert |= (mask & 0xFu) << bit_offset; - return; - } - - if ((remap_ctrl & 0x03) == 0x02) argb8_convert |= (mask & 0x1u) << bit_offset; - if ((remap_ctrl & 0x0C) == 0x08) argb8_convert |= (mask & 0x2u) << bit_offset; - if ((remap_ctrl & 0x30) == 0x20) argb8_convert |= (mask & 0x4u) << bit_offset; - if ((remap_ctrl & 0xC0) == 0x80) argb8_convert |= (mask & 0x8u) << bit_offset; - }; - - if (argb8_signed) - { - // Apply integer sign extension from uint8 to sint8 and renormalize - apply_sign_convert_mask(argb8_signed, texture_control_bits::SEXT_OFFSET); + const u32 remap_hi = tex.decoded_remap().shuffle_mask_bits(0xFu); + current_fragment_program.texture_params[i].remap &= ~(0xFu << 16u); + current_fragment_program.texture_params[i].remap |= (remap_hi << 16u); } - - if (unsigned_remap) - { - // Apply sign expansion, compressed normal-map style (2n - 1) - apply_sign_convert_mask(unsigned_remap, texture_control_bits::EXPAND_OFFSET); - } - - texture_control |= argb8_convert; } current_fragment_program.texture_params[i].control = texture_control; diff --git a/rpcs3/Emu/RSX/RSXThread.h b/rpcs3/Emu/RSX/RSXThread.h index 4f965dee80..4b2de0acc4 100644 --- a/rpcs3/Emu/RSX/RSXThread.h +++ b/rpcs3/Emu/RSX/RSXThread.h @@ -380,7 +380,7 @@ namespace rsx flags32_t read_barrier(u32 memory_address, u32 memory_range, bool unconditional); virtual void write_barrier(u32 /*memory_address*/, u32 /*memory_range*/) {} virtual void sync_hint(FIFO::interrupt_hint hint, reports::sync_hint_payload_t payload); - virtual bool release_GCM_label(u32 /*address*/, u32 /*value*/) { return false; } + virtual bool release_GCM_label(u32 /*type*/, u32 /*address*/, u32 /*value*/) { return false; } protected: diff --git a/rpcs3/Emu/RSX/VK/VKDraw.cpp b/rpcs3/Emu/RSX/VK/VKDraw.cpp index 59f9bfb40a..7a9d6aae6e 100644 --- a/rpcs3/Emu/RSX/VK/VKDraw.cpp +++ b/rpcs3/Emu/RSX/VK/VKDraw.cpp @@ -1,5 +1,6 @@ #include "stdafx.h" #include "../Common/BufferUtils.h" +#include "../Program/GLSLCommon.h" #include "../rsx_methods.h" #include "VKAsyncScheduler.h" @@ -275,7 +276,7 @@ void VKGSRender::load_texture_env() auto get_border_color = [&](const rsx::Texture auto& tex) { - return m_device->get_custom_border_color_support().require_border_color_remap + return m_device->get_custom_border_color_support().require_border_color_remap ? tex.remapped_border_color() : rsx::decode_border_color(tex.border_color()); }; @@ -307,6 +308,8 @@ void VKGSRender::load_texture_env() if (sampler_state->validate()) { + sampler_state->format_ex = tex.format_ex(); + if (sampler_state->is_cyclic_reference) { check_for_cyclic_refs |= true; @@ -324,7 +327,7 @@ void VKGSRender::load_texture_env() f32 min_lod = 0.f, max_lod = 0.f; f32 lod_bias = 0.f; - const u32 texture_format = tex.format() & ~(CELL_GCM_TEXTURE_UN | CELL_GCM_TEXTURE_LN); + const u32 texture_format = sampler_state->format_ex.format(); VkBool32 compare_enabled = VK_FALSE; VkCompareOp depth_compare_mode = VK_COMPARE_OP_NEVER; @@ -350,7 +353,8 @@ void VKGSRender::load_texture_env() if (sampler_state->format_class == RSX_FORMAT_CLASS_COLOR) [[likely]] { // Most PS3-like formats can be linearly filtered without problem - can_sample_linear = true; + // Exclude textures that require SNORM conversion however + can_sample_linear = (sampler_state->format_ex.texel_remap_control & rsx::texture_control_bits::SEXT_MASK) == 0; } else if (sampler_state->format_class != rsx::classify_format(texture_format) && (texture_format == CELL_GCM_TEXTURE_A8R8G8B8 || texture_format == CELL_GCM_TEXTURE_D8R8G8B8)) diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.cpp b/rpcs3/Emu/RSX/VK/VKGSRender.cpp index cba661a64b..8d90f9a09f 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.cpp +++ b/rpcs3/Emu/RSX/VK/VKGSRender.cpp @@ -1541,7 +1541,7 @@ std::pair VKGSRender::map_host_object_data( return { m_host_dma_ctrl->host_ctx(), m_host_object_data->value }; } -bool VKGSRender::release_GCM_label(u32 address, u32 args) +bool VKGSRender::release_GCM_label(u32 type, u32 address, u32 args) { if (!backend_config.supports_host_gpu_labels) { @@ -1550,7 +1550,7 @@ bool VKGSRender::release_GCM_label(u32 address, u32 args) auto host_ctx = ensure(m_host_dma_ctrl->host_ctx()); - if (host_ctx->texture_loads_completed()) + if (type == NV4097_TEXTURE_READ_SEMAPHORE_RELEASE && host_ctx->texture_loads_completed()) { // All texture loads already seen by the host GPU // Wait for all previously submitted labels to be flushed @@ -1572,13 +1572,10 @@ bool VKGSRender::release_GCM_label(u32 address, u32 args) const auto release_event_id = host_ctx->on_label_acquire(); + vk::insert_global_memory_barrier(*m_current_command_buffer); + if (host_ctx->has_unflushed_texture_loads()) { - if (vk::is_renderpass_open(*m_current_command_buffer)) - { - vk::end_renderpass(*m_current_command_buffer); - } - vkCmdUpdateBuffer(*m_current_command_buffer, mapping.second->value, mapping.first, 4, &write_data); flush_command_queue(); } diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.h b/rpcs3/Emu/RSX/VK/VKGSRender.h index faae1bb78f..f9feedc35a 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.h +++ b/rpcs3/Emu/RSX/VK/VKGSRender.h @@ -221,7 +221,7 @@ private: void frame_context_cleanup(vk::frame_context_t *ctx); void advance_queued_frames(); void present(vk::frame_context_t *ctx); - void reinitialize_swapchain(); + bool reinitialize_swapchain(); vk::viewable_image* get_present_source(vk::present_surface_info* info, const rsx::avconf& avconfig); @@ -254,7 +254,7 @@ public: // Sync void write_barrier(u32 address, u32 range) override; void sync_hint(rsx::FIFO::interrupt_hint hint, rsx::reports::sync_hint_payload_t payload) override; - bool release_GCM_label(u32 address, u32 data) override; + bool release_GCM_label(u32 type, u32 address, u32 data) override; void begin_occlusion_query(rsx::reports::occlusion_query_info* query) override; void end_occlusion_query(rsx::reports::occlusion_query_info* query) override; diff --git a/rpcs3/Emu/RSX/VK/VKPresent.cpp b/rpcs3/Emu/RSX/VK/VKPresent.cpp index f9fbb114ed..5761a99120 100644 --- a/rpcs3/Emu/RSX/VK/VKPresent.cpp +++ b/rpcs3/Emu/RSX/VK/VKPresent.cpp @@ -33,7 +33,7 @@ namespace } } -void VKGSRender::reinitialize_swapchain() +bool VKGSRender::reinitialize_swapchain() { m_swapchain_dims.width = m_frame->client_width(); m_swapchain_dims.height = m_frame->client_height(); @@ -44,7 +44,7 @@ void VKGSRender::reinitialize_swapchain() if (m_swapchain_dims.width == 0 || m_swapchain_dims.height == 0) { swapchain_unavailable = true; - return; + return false; } // NOTE: This operation will create a hard sync point @@ -97,7 +97,7 @@ void VKGSRender::reinitialize_swapchain() { rsx_log.warning("Swapchain initialization failed. Request ignored [%dx%d]", m_swapchain_dims.width, m_swapchain_dims.height); swapchain_unavailable = true; - return; + return false; } // Re-initialize CPU frame contexts @@ -135,6 +135,7 @@ void VKGSRender::reinitialize_swapchain() swapchain_unavailable = false; should_reinitialize_swapchain = false; + return true; } void VKGSRender::present(vk::frame_context_t *ctx) @@ -426,11 +427,32 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info) if (swapchain_unavailable || should_reinitialize_swapchain) { - reinitialize_swapchain(); + // Reinitializing the swapchain is a failable operation. However, not all failures are fatal (e.g minimized window). + // In the worst case, we can have the driver refuse to create the swapchain while we already deleted the previous one. + // In such scenarios, we have to retry a few times before giving up as we cannot proceed without a swapchain. + for (int i = 0; i < 10; ++i) + { + if (reinitialize_swapchain() || m_current_frame) + { + // If m_current_frame exists, then the initialization failure is non-fatal. Proceed as usual. + break; + } + + if (Emu.IsStopped()) + { + m_frame->flip(m_context); + rsx::thread::flip(info); + return; + } + + std::this_thread::sleep_for(100ms); + } } m_profiler.start(); + ensure(m_current_frame, "Invalid swapchain setup. Resizing the game window failed."); + if (m_current_frame == &m_aux_frame_context) { m_current_frame = &m_frame_context_storage[m_current_queue_index]; @@ -582,6 +604,7 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info) rsx_log.warning("vkAcquireNextImageKHR failed with VK_ERROR_OUT_OF_DATE_KHR. Flip request ignored until surface is recreated."); swapchain_unavailable = true; reinitialize_swapchain(); + ensure(m_current_frame, "Could not reinitialize swapchain after VK_ERROR_OUT_OF_DATE_KHR signal!"); continue; default: vk::die_with_error(status); diff --git a/rpcs3/Emu/RSX/VK/vkutils/sampler.cpp b/rpcs3/Emu/RSX/VK/vkutils/sampler.cpp index 073fc514d0..53066e2f4a 100644 --- a/rpcs3/Emu/RSX/VK/vkutils/sampler.cpp +++ b/rpcs3/Emu/RSX/VK/vkutils/sampler.cpp @@ -182,16 +182,8 @@ namespace vk return found == m_generic_sampler_pool.end() ? nullptr : found->second.get(); } - const auto block = m_custom_color_sampler_pool.equal_range(key.base_key); - for (auto it = block.first; it != block.second; ++it) - { - if (it->second->key.border_color_key == key.border_color_key) - { - return it->second.get(); - } - } - - return nullptr; + const auto found = m_custom_color_sampler_pool.find(key); + return found == m_custom_color_sampler_pool.end() ? nullptr : found->second.get(); } cached_sampler_object_t* sampler_pool_t::emplace(const sampler_pool_key_t& key, std::unique_ptr& object) @@ -204,7 +196,7 @@ namespace vk return iterator->second.get(); } - const auto [iterator, _unused] = m_custom_color_sampler_pool.emplace(key.base_key, std::move(object)); + const auto [iterator, _unused] = m_custom_color_sampler_pool.emplace(key, std::move(object)); return iterator->second.get(); } diff --git a/rpcs3/Emu/RSX/VK/vkutils/sampler.h b/rpcs3/Emu/RSX/VK/vkutils/sampler.h index f1d81d542f..232f1073d7 100644 --- a/rpcs3/Emu/RSX/VK/vkutils/sampler.h +++ b/rpcs3/Emu/RSX/VK/vkutils/sampler.h @@ -64,6 +64,22 @@ namespace vk { u64 base_key; u64 border_color_key; + + bool operator == (const sampler_pool_key_t& that) const + { + return this->base_key == that.base_key && + this->border_color_key == that.border_color_key; + } + }; + + struct sampler_pool_key_hash + { + size_t operator()(const vk::sampler_pool_key_t& k) const noexcept + { + usz result = k.base_key; + result ^= k.border_color_key + 0x9e3779b97f4a7c15ULL + (result << 6) + (result >> 2); + return result; + } }; struct cached_sampler_object_t : public vk::sampler, public rsx::ref_counted @@ -75,7 +91,7 @@ namespace vk class sampler_pool_t { std::unordered_map> m_generic_sampler_pool; - std::unordered_map> m_custom_color_sampler_pool; + std::unordered_map, sampler_pool_key_hash> m_custom_color_sampler_pool; public: diff --git a/rpcs3/Emu/RSX/color_utils.h b/rpcs3/Emu/RSX/color_utils.h index fd3156bd3e..40335c07e3 100644 --- a/rpcs3/Emu/RSX/color_utils.h +++ b/rpcs3/Emu/RSX/color_utils.h @@ -39,6 +39,34 @@ namespace rsx return remapped; } + + /** + * Remap color channel bits based on a remap vector. The output is a normalized selector of each color channel with spread. + * The input bits are an action selector. e.g a mask of channels that need to be interpreted as SNORM or BX2 + * The output is a final mask on which post-sampling channels the operation applies to. + * Examples: + * - If we have remap as [ 1 R R R ] and mask of R (0010) then we get 1110. Remapper spreads 'R' action to all channels where it should apply. + */ + u32 shuffle_mask_bits(u32 bits) const + { + if (!bits || encoded == RSX_TEXTURE_REMAP_IDENTITY) [[likely]] + { + return bits; + } + + u32 result = 0; + for (u8 channel = 0; channel < 4; ++channel) + { + if (control_map[channel] != CELL_GCM_TEXTURE_REMAP_REMAP || // Channel not read from input + (bits & (1u << channel_map[channel])) == 0) // Input channel is not enabled in the mask + { + continue; + } + result |= (1u << channel); + } + return result; + } + template requires std::is_integral_v || std::is_floating_point_v std::array remap(const std::array& components) const diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index b204cc38b9..717e4ab59a 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -252,7 +252,7 @@ void init_fxo_for_exec(utils::serial* ar, bool full = false) // Reserved area if (!load_and_check_reserved(*ar, advance)) { - sys_log.error("Potential failure to load savestate: padding buyes are not 0. %s", *ar); + sys_log.error("Potential failure to load savestate: padding bytes are not 0. %s", *ar); } } } @@ -310,7 +310,7 @@ static void fixup_settings(const psf::registry* _psf) if (g_cfg.net.net_active == np_internet_status::disabled && g_cfg.net.psn_status != np_psn_status::disabled) { - sys_log.warning("Net status was set to disconnected so psn status was disabled"); + sys_log.warning("Net status was set to disconnected so PSN status was disabled."); g_cfg.net.psn_status.set(np_psn_status::disabled); } } @@ -4642,7 +4642,7 @@ game_boot_result Emulator::InsertDisc(const std::string& path) else { // TODO: find out where other discs are mounted - sys_log.todo("Mounting non-ps2/ps3 disc in dev_bdvd. Is this correct? (path='%s')", disc_root); + sys_log.todo("Mounting non-PS2/PS3 disc in dev_bdvd. Is this correct? (path='%s')", disc_root); ensure(vfs::mount("/dev_bdvd", disc_root)); } diff --git a/rpcs3/Emu/system_config.h b/rpcs3/Emu/system_config.h index 518e6eeb6e..aaa889ce1f 100644 --- a/rpcs3/Emu/system_config.h +++ b/rpcs3/Emu/system_config.h @@ -248,7 +248,7 @@ struct cfg_root : cfg::node cfg::string audio_device{ this, "Audio Device", "@@@default@@@", true }; cfg::_int<0, 200> volume{ this, "Master Volume", 100, true }; cfg::_bool enable_buffering{ this, "Enable Buffering", true, true }; - cfg::_int <4, 250> desired_buffer_duration{ this, "Desired Audio Buffer Duration", 100, true }; + cfg::_int <4, 250> desired_buffer_duration{ this, "Desired Audio Buffer Duration", 34, true }; cfg::_bool enable_time_stretching{ this, "Enable Time Stretching", false, true }; cfg::_bool disable_sampling_skip{ this, "Disable Sampling Skip", false, true }; cfg::_int<0, 100> time_stretching_threshold{ this, "Time Stretching Threshold", 75, true }; diff --git a/rpcs3/Emu/system_utils.hpp b/rpcs3/Emu/system_utils.hpp index d8f4f59d8a..fd8717f4f9 100644 --- a/rpcs3/Emu/system_utils.hpp +++ b/rpcs3/Emu/system_utils.hpp @@ -3,6 +3,7 @@ #include "util/types.hpp" #include #include +#include enum class game_content_type { diff --git a/rpcs3/Input/mouse_gyro_handler.cpp b/rpcs3/Input/mouse_gyro_handler.cpp new file mode 100644 index 0000000000..6f1c7cd637 --- /dev/null +++ b/rpcs3/Input/mouse_gyro_handler.cpp @@ -0,0 +1,146 @@ +#include "mouse_gyro_handler.h" + +#include +#include +#include +#include + +#include + +void mouse_gyro_handler::clear() +{ + active = false; + reset = false; + gyro_x = DEFAULT_MOTION_X; + gyro_y = DEFAULT_MOTION_Y; + gyro_z = DEFAULT_MOTION_Z; +} + +bool mouse_gyro_handler::toggle_enabled() +{ + enabled = !enabled; + clear(); + return enabled; +} + +void mouse_gyro_handler::set_gyro_active() +{ + active = true; +} + +void mouse_gyro_handler::set_gyro_reset() +{ + active = false; + reset = true; +} + +void mouse_gyro_handler::set_gyro_xz(s32 off_x, s32 off_y) +{ + if (!active) + return; + + gyro_x = static_cast(std::clamp(off_x, 0, DEFAULT_MOTION_X * 2 - 1)); + gyro_z = static_cast(std::clamp(off_y, 0, DEFAULT_MOTION_Z * 2 - 1)); +} + +void mouse_gyro_handler::set_gyro_y(s32 steps) +{ + if (!active) + return; + + gyro_y = static_cast(std::clamp(gyro_y + steps, 0, DEFAULT_MOTION_Y * 2 - 1)); +} + +void mouse_gyro_handler::handle_event(QEvent* ev, const QWindow& win) +{ + if (!enabled) + return; + + // Mouse-based motion input. + // Captures mouse events while the game window is focused. + // Updates motion sensor values via mouse position and mouse wheel while RMB is held. + // Intentionally independent of chosen pad configuration. + switch (ev->type()) + { + case QEvent::MouseButtonPress: + { + auto* e = static_cast(ev); + if (e->button() == Qt::RightButton) + { + // Enable mouse-driven gyro emulation while RMB is held. + set_gyro_active(); + } + break; + } + case QEvent::MouseButtonRelease: + { + auto* e = static_cast(ev); + if (e->button() == Qt::RightButton) + { + // Disable gyro emulation and request a one-shot motion reset. + set_gyro_reset(); + } + break; + } + case QEvent::MouseMove: + { + auto* e = static_cast(ev); + + // Track cursor offset from window center. + const QPoint center(win.width() / 2, win.height() / 2); + const QPoint cur = e->position().toPoint(); + + const s32 off_x = cur.x() - center.x() + DEFAULT_MOTION_X; + const s32 off_y = cur.y() - center.y() + DEFAULT_MOTION_Z; + + // Determine motion from relative mouse position while gyro emulation is active. + set_gyro_xz(off_x, off_y); + + break; + } + case QEvent::Wheel: + { + auto* e = static_cast(ev); + + // Track mouse wheel steps. + const s32 steps = e->angleDelta().y() / 120; + + // Accumulate mouse wheel steps while gyro emulation is active. + set_gyro_y(steps); + + break; + } + default: + { + break; + } + } +} + +void mouse_gyro_handler::apply_gyro(const std::shared_ptr& pad) +{ + if (!enabled) + return; + + if (!pad || !pad->is_connected()) + return; + + // Inject mouse-based motion sensor values into pad sensors for gyro emulation. + // The Qt frontend maps cursor offset and wheel input to absolute motion values while RMB is held. + if (reset) + { + // RMB released → reset motion + pad->m_sensors[0].m_value = DEFAULT_MOTION_X; + pad->m_sensors[1].m_value = DEFAULT_MOTION_Y; + pad->m_sensors[2].m_value = DEFAULT_MOTION_Z; + clear(); + } + else + { + // RMB held → accumulate motion + // Axes have been chosen as tested in Sly 4 minigames. Top-down view motion uses X/Z axes. + pad->m_sensors[0].m_value = gyro_x; // Mouse X → Motion X + pad->m_sensors[1].m_value = gyro_y; // Mouse Wheel → Motion Y + pad->m_sensors[2].m_value = gyro_z; // Mouse Y → Motion Z + } +} diff --git a/rpcs3/Input/mouse_gyro_handler.h b/rpcs3/Input/mouse_gyro_handler.h new file mode 100644 index 0000000000..97a745d919 --- /dev/null +++ b/rpcs3/Input/mouse_gyro_handler.h @@ -0,0 +1,33 @@ +#pragma once + +#include "util/types.hpp" +#include "util/atomic.hpp" +#include "Emu/Io/pad_types.h" + +class QEvent; +class QWindow; + +// Mouse-based motion sensor emulation state. +class mouse_gyro_handler +{ +private: + atomic_t enabled = false; // Whether mouse-based gyro emulation mode has been enabled by using the associated hotkey + + atomic_t active = false; // Whether right mouse button is currently held (gyro active) + atomic_t reset = false; // One-shot reset request on right mouse button release + atomic_t gyro_x = DEFAULT_MOTION_X; // Accumulated from mouse X position relative to center + atomic_t gyro_y = DEFAULT_MOTION_Y; // Accumulated from mouse wheel delta + atomic_t gyro_z = DEFAULT_MOTION_Z; // Accumulated from mouse Y position relative to center + + void set_gyro_active(); + void set_gyro_reset(); + void set_gyro_xz(s32 off_x, s32 off_y); + void set_gyro_y(s32 steps); + +public: + void clear(); + bool toggle_enabled(); + + void handle_event(QEvent* ev, const QWindow& win); + void apply_gyro(const std::shared_ptr& pad); +}; diff --git a/rpcs3/Input/pad_thread.cpp b/rpcs3/Input/pad_thread.cpp index afc5a73fce..55652bc229 100644 --- a/rpcs3/Input/pad_thread.cpp +++ b/rpcs3/Input/pad_thread.cpp @@ -81,6 +81,9 @@ void pad_thread::Init() { std::lock_guard lock(pad::g_pad_mutex); + // Reset mouse-based gyro state + m_mouse_gyro.clear(); + // Cache old settings if possible std::array pad_settings; for (u32 i = 0; i < CELL_PAD_MAX_PORT_NUM; i++) // max 7 pads @@ -606,6 +609,10 @@ void pad_thread::operator()() if (Emu.IsRunning()) { update_pad_states(); + + // Apply mouse-based gyro emulation. + // Intentionally bound to Player 1 only. + m_mouse_gyro.apply_gyro(m_pads[0]); } m_info.now_connect = connected_devices + num_ldd_pad; @@ -624,15 +631,18 @@ void pad_thread::operator()() if (!pad->is_connected()) continue; - for (const auto& button : pad->m_buttons) + for (const Button& button : pad->m_buttons) { if (button.m_pressed && ( - button.m_outKeyCode == CELL_PAD_CTRL_CROSS || - button.m_outKeyCode == CELL_PAD_CTRL_CIRCLE || - button.m_outKeyCode == CELL_PAD_CTRL_TRIANGLE || - button.m_outKeyCode == CELL_PAD_CTRL_SQUARE || - button.m_outKeyCode == CELL_PAD_CTRL_START || - button.m_outKeyCode == CELL_PAD_CTRL_SELECT)) + (button.m_offset == CELL_PAD_BTN_OFFSET_DIGITAL1 && ( + button.m_outKeyCode == CELL_PAD_CTRL_START || + button.m_outKeyCode == CELL_PAD_CTRL_SELECT)) || + (button.m_offset == CELL_PAD_BTN_OFFSET_DIGITAL2 && ( + button.m_outKeyCode == CELL_PAD_CTRL_CROSS || + button.m_outKeyCode == CELL_PAD_CTRL_CIRCLE || + button.m_outKeyCode == CELL_PAD_CTRL_TRIANGLE || + button.m_outKeyCode == CELL_PAD_CTRL_SQUARE)) + )) { any_button_pressed = true; break; @@ -669,7 +679,7 @@ void pad_thread::operator()() break; } - for (const auto& button : pad->m_buttons) + for (const Button& button : pad->m_buttons) { if (button.m_offset == CELL_PAD_BTN_OFFSET_DIGITAL1 && button.m_outKeyCode == CELL_PAD_CTRL_PS && button.m_pressed) { @@ -728,7 +738,7 @@ void pad_thread::operator()() if (!pad->is_connected()) continue; - for (const auto& button : pad->m_buttons) + for (const Button& button : pad->m_buttons) { if (button.m_offset == CELL_PAD_BTN_OFFSET_DIGITAL1 && button.m_outKeyCode == CELL_PAD_CTRL_START && button.m_pressed) { diff --git a/rpcs3/Input/pad_thread.h b/rpcs3/Input/pad_thread.h index 7b0e0b79fb..20f53e9034 100644 --- a/rpcs3/Input/pad_thread.h +++ b/rpcs3/Input/pad_thread.h @@ -5,6 +5,7 @@ #include "Emu/Io/pad_types.h" #include "Emu/Io/pad_config.h" #include "Emu/Io/pad_config_types.h" +#include "Input/mouse_gyro_handler.h" #include "Utilities/mutex.h" #include @@ -41,6 +42,8 @@ public: static auto constexpr thread_name = "Pad Thread"sv; + mouse_gyro_handler& get_mouse_gyro() { return m_mouse_gyro; } + protected: void Init(); void InitLddPad(u32 handle, const u32* port_status); @@ -67,6 +70,8 @@ private: bool m_resume_emulation_flag = false; bool m_ps_button_pressed = false; atomic_t m_home_menu_open = false; + + mouse_gyro_handler m_mouse_gyro; }; namespace pad diff --git a/rpcs3/Loader/ISO.h b/rpcs3/Loader/ISO.h index 3af4732aad..588e4b8491 100644 --- a/rpcs3/Loader/ISO.h +++ b/rpcs3/Loader/ISO.h @@ -13,16 +13,16 @@ void unload_iso(); struct iso_extent_info { - u64 start; - u64 size; + u64 start = 0; + u64 size = 0; }; struct iso_fs_metadata { std::string name; - s64 time; - bool is_directory; - bool has_multiple_extents; + s64 time = 0; + bool is_directory = false; + bool has_multiple_extents = false; std::vector extents; u64 size() const; @@ -30,7 +30,7 @@ struct iso_fs_metadata struct iso_fs_node { - iso_fs_metadata metadata; + iso_fs_metadata metadata {}; std::vector> children; }; @@ -38,7 +38,7 @@ class iso_file : public fs::file_base { private: fs::file m_file; - iso_fs_metadata m_meta; + iso_fs_metadata m_meta {}; u64 m_pos = 0; std::pair get_extent_pos(u64 pos) const; @@ -80,7 +80,7 @@ class iso_archive { private: std::string m_path; - iso_fs_node m_root; + iso_fs_node m_root {}; fs::file m_file; public: diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj index 5fbf0491e9..9749f60fcd 100644 --- a/rpcs3/rpcs3.vcxproj +++ b/rpcs3/rpcs3.vcxproj @@ -194,6 +194,7 @@ + @@ -1079,6 +1080,7 @@ $(QTDIR)\bin\moc.exe;%(FullPath) $(QTDIR)\bin\moc.exe;%(FullPath) + diff --git a/rpcs3/rpcs3.vcxproj.filters b/rpcs3/rpcs3.vcxproj.filters index 100a9b1d8f..a011ddf62e 100644 --- a/rpcs3/rpcs3.vcxproj.filters +++ b/rpcs3/rpcs3.vcxproj.filters @@ -1272,6 +1272,9 @@ Generated Files\Release + + Io + @@ -1511,6 +1514,9 @@ Io\camera + + Io + diff --git a/rpcs3/rpcs3qt/CMakeLists.txt b/rpcs3/rpcs3qt/CMakeLists.txt index a8fc4c5886..b59d6f7a11 100644 --- a/rpcs3/rpcs3qt/CMakeLists.txt +++ b/rpcs3/rpcs3qt/CMakeLists.txt @@ -157,6 +157,7 @@ add_library(rpcs3_ui STATIC ../Input/hid_pad_handler.cpp ../Input/keyboard_pad_handler.cpp ../Input/mm_joystick_handler.cpp + ../Input/mouse_gyro_handler.cpp ../Input/pad_thread.cpp ../Input/product_info.cpp ../Input/ps_move_calibration.cpp diff --git a/rpcs3/rpcs3qt/downloader.cpp b/rpcs3/rpcs3qt/downloader.cpp index dd861da73e..ae97dc8f77 100644 --- a/rpcs3/rpcs3qt/downloader.cpp +++ b/rpcs3/rpcs3qt/downloader.cpp @@ -99,7 +99,7 @@ void downloader::start(const std::string& url, bool follow_location, bool show_p // The downloader's signals are expected to be disconnected and customized before start is called. // Therefore we need to (re)connect its signal(s) here and not in the constructor. - connect(this, &downloader::signal_buffer_update, this, &downloader::handle_buffer_update); + connect(this, &downloader::signal_buffer_update, this, &downloader::handle_buffer_update, static_cast(Qt::QueuedConnection | Qt::UniqueConnection)); if (show_progress_dialog) { @@ -169,7 +169,7 @@ usz downloader::update_buffer(char* data, usz size) const auto old_size = m_curl_buf.size(); const auto new_size = old_size + size; m_curl_buf.resize(static_cast(new_size)); - memcpy(m_curl_buf.data() + old_size, data, size); + std::memcpy(m_curl_buf.data() + old_size, data, size); int max = 0; @@ -197,6 +197,5 @@ void downloader::handle_buffer_update(int size, int max) const { m_progress_dialog->SetRange(0, max > 0 ? max : m_progress_dialog->maximum()); m_progress_dialog->SetValue(size); - QApplication::processEvents(); } } diff --git a/rpcs3/rpcs3qt/emu_settings.cpp b/rpcs3/rpcs3qt/emu_settings.cpp index 20bb58ba93..ccb2308509 100644 --- a/rpcs3/rpcs3qt/emu_settings.cpp +++ b/rpcs3/rpcs3qt/emu_settings.cpp @@ -25,8 +25,41 @@ namespace { static NEVER_INLINE void emit_data(YAML::Emitter& out, const YAML::Node& node) { - // TODO - out << node; + if (!node || node.IsNull()) + { + // I chose to output a null when nothing is present so that recursive YAML Value calls can be matched to a null value instead of nothing + out << YAML::Null; + return; + } + if (node.IsMap()) + { + std::vector keys; + keys.reserve(node.size()); + // generate vector of strings to be sorted using the as function from YAML documentation + for (const auto& pair : node) + { + keys.push_back(pair.first.as()); + } + std::sort(keys.begin(), keys.end()); + // recursively generate sorted maps + // alternative implementations could have stops at specified recursion levels or maybe just the first two levels would be sorted + out << YAML::BeginMap; + for (const std::string& key : keys) + { + out << YAML::Key << key; + out << YAML::Value; + emit_data(out, node[key]); + } + out << YAML::EndMap; + } + // alternatively: an else statement could be used however I wanted to follow a similar format to the += operator so the YAML Undefined class can be ignored + else if (node.IsScalar() || node.IsSequence()) + { + out << node; + } + // this exists to preserve the same functionality as before where Undefined nodes would still be output, can be removed or consolidated with the else if branch + else + out << node; } // Incrementally load YAML diff --git a/rpcs3/rpcs3qt/gs_frame.cpp b/rpcs3/rpcs3qt/gs_frame.cpp index db66ce68a4..6557168cce 100644 --- a/rpcs3/rpcs3qt/gs_frame.cpp +++ b/rpcs3/rpcs3qt/gs_frame.cpp @@ -19,6 +19,7 @@ #include "Emu/RSX/Overlays/overlay_message.h" #include "Emu/Io/interception.h" #include "Emu/Io/recording_config.h" +#include "Input/pad_thread.h" #include #include @@ -402,6 +403,15 @@ void gs_frame::handle_shortcut(gui::shortcuts::shortcut shortcut_key, const QKey audio::change_volume(-5); break; } + case gui::shortcuts::shortcut::gw_toggle_mouse_gyro: + { + if (auto* pad_thr = pad::get_pad_thread(true)) + { + const bool mouse_gyro_enabled = pad_thr->get_mouse_gyro().toggle_enabled(); + gui_log.notice("Mouse-based gyro emulation %s", mouse_gyro_enabled ? "enabled" : "disabled"); + } + break; + } default: { break; @@ -1216,6 +1226,16 @@ bool gs_frame::event(QEvent* ev) // This will make the cursor visible again if it was hidden by the mouse idle timeout handle_cursor(visibility(), false, false, true); } + + // Handle events for mouse-based gyro emulation. + if (Emu.IsRunning()) + { + if (auto* pad_thr = pad::get_pad_thread(true)) + { + pad_thr->get_mouse_gyro().handle_event(ev, *this); + } + } + return QWindow::event(ev); } diff --git a/rpcs3/rpcs3qt/log_frame.cpp b/rpcs3/rpcs3qt/log_frame.cpp index 6d99791799..cf570eea7a 100644 --- a/rpcs3/rpcs3qt/log_frame.cpp +++ b/rpcs3/rpcs3qt/log_frame.cpp @@ -163,11 +163,6 @@ log_frame::log_frame(std::shared_ptr _gui_settings, QWidget* paren CreateAndConnectActions(); LoadSettings(); - if (m_ansi_tty) - { - m_tty_ansi_highlighter = new AnsiHighlighter(m_tty->document()); - } - m_timer = new QTimer(this); connect(m_timer, &QTimer::timeout, this, &log_frame::UpdateUI); } @@ -225,7 +220,7 @@ void log_frame::CreateAndConnectActions() // I, for one, welcome our lambda overlord // It's either this or a signal mapper // Then, probably making a list of these actions so that it's easier to iterate to generate the mapper. - auto l_initAct = [this](QAction* act, logs::level logLevel) + const auto l_initAct = [this](QAction* act, logs::level logLevel) { act->setCheckable(true); @@ -298,7 +293,7 @@ void log_frame::CreateAndConnectActions() if (m_ansi_tty && !m_tty_ansi_highlighter) { - m_tty_ansi_highlighter = new AnsiHighlighter(m_tty->document()); + m_tty_ansi_highlighter = new AnsiHighlighter(m_tty); } else if (!m_ansi_tty && m_tty_ansi_highlighter) { @@ -607,6 +602,12 @@ void log_frame::RepaintTextColors() html.replace(old_style, new_style); m_log->document()->setHtml(html); + + if (m_tty_ansi_highlighter) + { + m_tty_ansi_highlighter->update_colors(m_tty); + m_tty_ansi_highlighter->rehighlight(); + } } void log_frame::UpdateUI() diff --git a/rpcs3/rpcs3qt/log_viewer.cpp b/rpcs3/rpcs3qt/log_viewer.cpp index 3a566937a7..b69c07901e 100644 --- a/rpcs3/rpcs3qt/log_viewer.cpp +++ b/rpcs3/rpcs3qt/log_viewer.cpp @@ -28,7 +28,8 @@ LOG_CHANNEL(gui_log, "GUI"); log_viewer::log_viewer(std::shared_ptr gui_settings) : m_gui_settings(std::move(gui_settings)) { - setWindowTitle(tr("Log Viewer")); + update_title(); + setObjectName("log_viewer"); setAttribute(Qt::WA_DeleteOnClose); setAttribute(Qt::WA_StyledBackground); @@ -59,15 +60,33 @@ log_viewer::log_viewer(std::shared_ptr gui_settings) connect(m_log_text, &QWidget::customContextMenuRequested, this, &log_viewer::show_context_menu); } +void log_viewer::update_title() +{ + QString suffix; + + if (!m_filter_term.isEmpty()) + { + suffix = tr(" | Filter '%0'").arg(m_filter_term); + } + + if (!m_exclude_term.isEmpty()) + { + suffix += tr(" | Exclude '%0'").arg(m_exclude_term); + } + + setWindowTitle(tr("Log Viewer%0").arg(suffix)); +} + void log_viewer::show_context_menu(const QPoint& pos) { QMenu menu; - QAction* clear = new QAction(tr("&Clear")); - QAction* copy = new QAction(tr("&Copy")); - QAction* open = new QAction(tr("&Open log file")); - QAction* save = new QAction(tr("&Save filtered log")); - QAction* filter = new QAction(tr("&Filter log")); - QAction* config = new QAction(tr("&Check config")); + QAction* clear = new QAction(tr("&Clear")); + QAction* copy = new QAction(tr("&Copy")); + QAction* open = new QAction(tr("&Open log file")); + QAction* save = new QAction(tr("&Save filtered log")); + QAction* filter = new QAction(tr("&Filter log%0").arg(m_filter_term.isEmpty() ? "" : QString(" (%0)").arg(m_filter_term))); + QAction* exclude = new QAction(tr("&Exclude%0").arg(m_exclude_term.isEmpty() ? "" : QString(" (%0)").arg(m_exclude_term))); + QAction* config = new QAction(tr("&Check config")); QAction* timestamps = new QAction(tr("&Show Timestamps")); timestamps->setCheckable(true); @@ -91,7 +110,7 @@ void log_viewer::show_context_menu(const QPoint& pos) QAction* trace_act = new QAction(tr("Trace"), log_level_acts); log_level_acts->setExclusive(false); - auto init_action = [this](QAction* act, logs::level logLevel) + const auto init_action = [this](QAction* act, logs::level logLevel) { act->setCheckable(true); act->setChecked(m_log_levels.test(static_cast(logLevel))); @@ -120,6 +139,7 @@ void log_viewer::show_context_menu(const QPoint& pos) menu.addAction(open); menu.addAction(config); menu.addAction(filter); + menu.addAction(exclude); menu.addAction(save); menu.addSeparator(); menu.addAction(timestamps); @@ -187,7 +207,22 @@ void log_viewer::show_context_menu(const QPoint& pos) connect(filter, &QAction::triggered, this, [this]() { - m_filter_term = QInputDialog::getText(this, tr("Filter log"), tr("Enter text"), QLineEdit::EchoMode::Normal, m_filter_term); + bool ok = false; + QString filter_term = QInputDialog::getText(this, tr("Filter log"), tr("Enter text"), QLineEdit::EchoMode::Normal, m_filter_term, &ok); + if (!ok) return; + m_filter_term = std::move(filter_term); + update_title(); + filter_log(); + }); + + connect(exclude, &QAction::triggered, this, [this]() + { + bool ok = false; + QString exclude_term = QInputDialog::getText(this, tr("Exclude"), tr("Enter text (comma separated)"), QLineEdit::EchoMode::Normal, m_exclude_term, &ok); + if (!ok) return; + m_exclude_term = std::move(exclude_term); + m_exclude_terms = m_exclude_term.split(',', Qt::SkipEmptyParts); + update_title(); filter_log(); }); @@ -309,7 +344,7 @@ void log_viewer::filter_log() if (!m_log_levels.test(static_cast(logs::level::notice))) excluded_log_levels.push_back("·! "); if (!m_log_levels.test(static_cast(logs::level::trace))) excluded_log_levels.push_back("·T "); - if (m_filter_term.isEmpty() && excluded_log_levels.empty() && m_show_timestamps && m_show_threads && !m_last_actions_only) + if (m_filter_term.isEmpty() && m_exclude_terms.isEmpty() && excluded_log_levels.empty() && m_show_timestamps && m_show_threads && !m_last_actions_only) { set_text_and_keep_position(m_full_log); return; @@ -322,44 +357,49 @@ void log_viewer::filter_log() const auto add_line = [this, &result, &excluded_log_levels, ×tamp_regexp, &thread_regexp](QString& line) { - bool exclude_line = false; - - for (const QString& log_level_prefix : excluded_log_levels) + if (!line.isEmpty()) { - if (line.startsWith(log_level_prefix)) + for (QStringView log_level_prefix : excluded_log_levels) { - exclude_line = true; - break; + if (line.startsWith(log_level_prefix)) + { + return; + } + } + + for (QStringView term : m_exclude_terms) + { + if (line.contains(term)) + { + return; + } } } - if (exclude_line) + if (!m_filter_term.isEmpty() && !line.contains(m_filter_term)) { return; } - if (m_filter_term.isEmpty() || line.contains(m_filter_term)) + if (line.isEmpty()) { - if (line.isEmpty()) - { - result += "\n"; - return; - } + result += "\n"; + return; + } - if (!m_show_timestamps) - { - line.remove(timestamp_regexp); - } + if (!m_show_timestamps) + { + line.remove(timestamp_regexp); + } - if (!m_show_threads) - { - line.remove(thread_regexp); - } + if (!m_show_threads) + { + line.remove(thread_regexp); + } - if (!line.isEmpty()) - { - result += line + "\n"; - } + if (!line.isEmpty()) + { + result += line + "\n"; } }; diff --git a/rpcs3/rpcs3qt/log_viewer.h b/rpcs3/rpcs3qt/log_viewer.h index 85ece2688b..d6ba2ff2a4 100644 --- a/rpcs3/rpcs3qt/log_viewer.h +++ b/rpcs3/rpcs3qt/log_viewer.h @@ -23,6 +23,7 @@ private Q_SLOTS: void show_context_menu(const QPoint& pos); private: + void update_title(); void set_text_and_keep_position(const QString& text); void filter_log(); bool is_valid_file(const QMimeData& md, bool save = false); @@ -30,6 +31,8 @@ private: std::shared_ptr m_gui_settings; QString m_path_last; QString m_filter_term; + QString m_exclude_term; + QStringList m_exclude_terms; QString m_full_log; QPlainTextEdit* m_log_text; LogHighlighter* m_log_highlighter; diff --git a/rpcs3/rpcs3qt/qt_utils.cpp b/rpcs3/rpcs3qt/qt_utils.cpp index a957dba11c..73dbaa87b6 100644 --- a/rpcs3/rpcs3qt/qt_utils.cpp +++ b/rpcs3/rpcs3qt/qt_utils.cpp @@ -173,21 +173,45 @@ namespace gui } return res; } - - QColor get_foreground_color() + + QColor get_foreground_color(QWidget* widget) { + if (widget) + { + widget->ensurePolished(); + return widget->palette().color(QPalette::ColorRole::WindowText); + } + QLabel dummy_color; dummy_color.ensurePolished(); return dummy_color.palette().color(QPalette::ColorRole::WindowText); } - QColor get_background_color() + QColor get_background_color(QWidget* widget) { + if (widget) + { + widget->ensurePolished(); + return widget->palette().color(QPalette::ColorRole::Window); + } + QLabel dummy_color; dummy_color.ensurePolished(); return dummy_color.palette().color(QPalette::ColorRole::Window); } + QColor adjust_color_for_background(const QColor& fg, const QColor& bg) + { + const int diff = fg.lightness() - bg.lightness(); + + if (std::abs(diff) >= 40) + { + return fg; + } + + return (bg.lightness() < 128) ? fg.lighter(180) : fg.darker(180); + } + QColor get_label_color(const QString& object_name, const QColor& fallback_light, const QColor& fallback_dark, QPalette::ColorRole color_role) { if (!gui::custom_stylesheet_active || !gui::stylesheet.contains(object_name)) diff --git a/rpcs3/rpcs3qt/qt_utils.h b/rpcs3/rpcs3qt/qt_utils.h index c08c5665a7..98e0eb8cd6 100644 --- a/rpcs3/rpcs3qt/qt_utils.h +++ b/rpcs3/rpcs3qt/qt_utils.h @@ -68,11 +68,14 @@ namespace gui // Returns a list of all base names of files in dir whose complete file names contain one of the given name_filters QStringList get_dir_entries(const QDir& dir, const QStringList& name_filters, bool full_path = false); - // Returns the foreground color of QLabel with respect to the current light/dark mode. - QColor get_foreground_color(); + // Returns the foreground color of QLabel or the given widget with respect to the current light/dark mode. + QColor get_foreground_color(QWidget* widget = nullptr); - // Returns the background color of QLabel with respect to the current light/dark mode. - QColor get_background_color(); + // Returns the background color of QLabel or the given widget with respect to the current light/dark mode. + QColor get_background_color(QWidget* widget = nullptr); + + // Returns an adjusted color with better contrast, depending on the background. + QColor adjust_color_for_background(const QColor& fg, const QColor& bg); // Returns the color specified by its color_role for the QLabels with object_name QColor get_label_color(const QString& object_name, const QColor& fallback_light, const QColor& fallback_dark, QPalette::ColorRole color_role = QPalette::WindowText); diff --git a/rpcs3/rpcs3qt/rpcn_settings_dialog.cpp b/rpcs3/rpcs3qt/rpcn_settings_dialog.cpp index 53097f29fc..6de6007b31 100644 --- a/rpcs3/rpcs3qt/rpcn_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/rpcn_settings_dialog.cpp @@ -1262,13 +1262,10 @@ rpcn_friends_dialog::rpcn_friends_dialog(QWidget* parent) connect(accept_request_action, &QAction::triggered, this, [this, str_sel_friend]() { - if (!m_rpcn->add_friend(str_sel_friend)) - { - QMessageBox::critical(this, tr("Error adding a friend!"), tr("An error occurred while trying to add a friend!"), QMessageBox::Ok); - } - else + if (add_friend_with_error_dialog(str_sel_friend)) { QMessageBox::information(this, tr("Friend added!"), tr("You've successfully added a friend!"), QMessageBox::Ok); + return; } }); @@ -1304,11 +1301,8 @@ rpcn_friends_dialog::rpcn_friends_dialog(QWidget* parent) connect(send_friend_request_action, &QAction::triggered, this, [this, str_sel_friend]() { - if (!m_rpcn->add_friend(str_sel_friend)) - { - QMessageBox::critical(this, tr("Error sending a friend request!"), tr("An error occurred while trying to send a friend request!"), QMessageBox::Ok); + if (!add_friend_with_error_dialog(str_sel_friend)) return; - } QString qstr_friend = QString::fromStdString(str_sel_friend); add_update_list(m_lst_requests, qstr_friend, m_icon_request_sent, QVariant(false)); @@ -1341,11 +1335,7 @@ rpcn_friends_dialog::rpcn_friends_dialog(QWidget* parent) QMessageBox::critical(this, tr("Error validating username!"), tr("The username you entered is invalid!"), QMessageBox::Ok); } - if (!m_rpcn->add_friend(str_friend_username)) - { - QMessageBox::critical(this, tr("Error adding friend!"), tr("An error occurred while adding a friend!"), QMessageBox::Ok); - } - else + if (add_friend_with_error_dialog(str_friend_username)) { add_update_list(m_lst_requests, QString::fromStdString(str_friend_username), m_icon_request_sent, QVariant(false)); QMessageBox::information(this, tr("Friend added!"), tr("Friend was successfully added!"), QMessageBox::Ok); @@ -1360,6 +1350,42 @@ rpcn_friends_dialog::~rpcn_friends_dialog() m_rpcn->remove_friend_cb(friend_callback, this); } +bool rpcn_friends_dialog::add_friend_with_error_dialog(const std::string& friend_username) +{ + QString err_msg; + const auto opt_error = m_rpcn->add_friend(friend_username); + + if (opt_error.has_value()) + { + const auto error = opt_error.value(); + + if (error != rpcn::ErrorType::NoError) + { + switch (error) + { + case rpcn::ErrorType::NotFound: err_msg = tr("The specified username does not exist."); break; + case rpcn::ErrorType::InvalidInput: err_msg = tr("You cannot add yourself as a friend."); break; + case rpcn::ErrorType::Blocked: err_msg = tr("You or the other user have the other blocked."); break; + case rpcn::ErrorType::AlreadyFriend: err_msg = tr("You are already friends with this user."); break; + case rpcn::ErrorType::DbFail: err_msg = tr("A database error occurred. Please try again later."); break; + default: err_msg = tr("An unexpected error occurred."); break; + } + } + } + else + { + err_msg = tr("Failed to send the friend request."); + } + + if (!err_msg.isEmpty()) + { + QMessageBox::critical(this, tr("Friend Request Failed"), err_msg, QMessageBox::Ok); + return false; + } + + return true; +} + bool rpcn_friends_dialog::is_ok() const { return m_rpcn_ok; diff --git a/rpcs3/rpcs3qt/rpcn_settings_dialog.h b/rpcs3/rpcs3qt/rpcn_settings_dialog.h index 89d8253220..c28828e410 100644 --- a/rpcs3/rpcs3qt/rpcn_settings_dialog.h +++ b/rpcs3/rpcs3qt/rpcn_settings_dialog.h @@ -121,6 +121,7 @@ public: private: void add_update_list(QListWidget* list, const QString& name, const QIcon& icon, const QVariant& data); void remove_list(QListWidget* list, const QString& name); + bool add_friend_with_error_dialog(const std::string& friend_username); private Q_SLOTS: void add_update_friend(const QString& name, bool status); diff --git a/rpcs3/rpcs3qt/shortcut_settings.cpp b/rpcs3/rpcs3qt/shortcut_settings.cpp index 039f493dae..64feb94777 100644 --- a/rpcs3/rpcs3qt/shortcut_settings.cpp +++ b/rpcs3/rpcs3qt/shortcut_settings.cpp @@ -37,6 +37,7 @@ void fmt_class_string::format(std::string& out, u64 arg) case shortcut::gw_mute_unmute: return "gw_mute_unmute"; case shortcut::gw_volume_up: return "gw_volume_up"; case shortcut::gw_volume_down: return "gw_volume_down"; + case shortcut::gw_toggle_mouse_gyro: return "gw_toggle_mouse_gyro"; case shortcut::count: return "count"; } @@ -88,6 +89,7 @@ shortcut_settings::shortcut_settings() { shortcut::gw_mute_unmute, shortcut_info{ "gw_mute_unmute", tr("Mute/Unmute Audio"), "Ctrl+Shift+M", shortcut_handler_id::game_window, false } }, { shortcut::gw_volume_up, shortcut_info{ "gw_volume_up", tr("Volume Up"), "Ctrl+Shift++", shortcut_handler_id::game_window, true } }, { shortcut::gw_volume_down, shortcut_info{ "gw_volume_down", tr("Volume Down"), "Ctrl+Shift+-", shortcut_handler_id::game_window, true } }, + { shortcut::gw_toggle_mouse_gyro, shortcut_info{ "gw_toggle_mouse_gyro", tr("Toggle Mouse-based Gyro"), "Ctrl+G", shortcut_handler_id::game_window, false } }, }) { } diff --git a/rpcs3/rpcs3qt/shortcut_settings.h b/rpcs3/rpcs3qt/shortcut_settings.h index db6458accb..be14ee1e30 100644 --- a/rpcs3/rpcs3qt/shortcut_settings.h +++ b/rpcs3/rpcs3qt/shortcut_settings.h @@ -46,6 +46,7 @@ namespace gui gw_mute_unmute, gw_volume_up, gw_volume_down, + gw_toggle_mouse_gyro, count }; diff --git a/rpcs3/rpcs3qt/syntax_highlighter.cpp b/rpcs3/rpcs3qt/syntax_highlighter.cpp index 2fd43033d5..a15e36c3a4 100644 --- a/rpcs3/rpcs3qt/syntax_highlighter.cpp +++ b/rpcs3/rpcs3qt/syntax_highlighter.cpp @@ -184,12 +184,37 @@ GlslHighlighter::GlslHighlighter(QTextDocument* parent) : Highlighter(parent) commentEndExpression = QRegularExpression("\\*/"); } -AnsiHighlighter::AnsiHighlighter(QTextDocument* parent) : Highlighter(parent) +AnsiHighlighter::AnsiHighlighter(QPlainTextEdit* text_edit) + : Highlighter(text_edit ? text_edit->document() : nullptr) { - m_escape_format.setForeground(Qt::darkGray); - m_escape_format.setFontItalic(true); + update_colors(text_edit); +} - m_foreground_color = gui::utils::get_foreground_color(); +void AnsiHighlighter::update_colors(QPlainTextEdit* text_edit) +{ + m_foreground_color = gui::utils::get_foreground_color(text_edit); + m_background_color = gui::utils::get_background_color(text_edit); + + m_foreground_colors[30] = gui::utils::adjust_color_for_background(Qt::black, m_background_color); + m_foreground_colors[31] = gui::utils::adjust_color_for_background(Qt::red, m_background_color); + m_foreground_colors[32] = gui::utils::adjust_color_for_background(Qt::darkGreen, m_background_color); + m_foreground_colors[33] = gui::utils::adjust_color_for_background(Qt::darkYellow, m_background_color); + m_foreground_colors[34] = gui::utils::adjust_color_for_background(Qt::darkBlue, m_background_color); + m_foreground_colors[35] = gui::utils::adjust_color_for_background(Qt::darkMagenta, m_background_color); + m_foreground_colors[36] = gui::utils::adjust_color_for_background(Qt::darkCyan, m_background_color); + m_foreground_colors[37] = gui::utils::adjust_color_for_background(Qt::lightGray, m_background_color); + m_foreground_colors[39] = m_foreground_color; + m_foreground_colors[90] = gui::utils::adjust_color_for_background(Qt::darkGray, m_background_color); + m_foreground_colors[91] = gui::utils::adjust_color_for_background(Qt::red, m_background_color); + m_foreground_colors[92] = gui::utils::adjust_color_for_background(Qt::green, m_background_color); + m_foreground_colors[93] = gui::utils::adjust_color_for_background(Qt::yellow, m_background_color); + m_foreground_colors[94] = gui::utils::adjust_color_for_background(Qt::blue, m_background_color); + m_foreground_colors[95] = gui::utils::adjust_color_for_background(Qt::magenta, m_background_color); + m_foreground_colors[96] = gui::utils::adjust_color_for_background(Qt::cyan, m_background_color); + m_foreground_colors[97] = gui::utils::adjust_color_for_background(Qt::white, m_background_color); + + m_escape_format.setForeground(gui::utils::adjust_color_for_background(Qt::darkGray, m_background_color)); + m_escape_format.setFontItalic(true); } void AnsiHighlighter::highlightBlock(const QString& text) @@ -235,39 +260,26 @@ void AnsiHighlighter::highlightBlock(const QString& text) if (!ok) continue; switch (code) { - case 0: - current_format = QTextCharFormat(); - current_format.setForeground(m_foreground_color); - break; - case 1: - current_format.setFontWeight(QFont::Bold); - break; - case 3: - current_format.setFontItalic(true); - break; - case 4: - current_format.setFontUnderline(true); - break; - case 30: current_format.setForeground(Qt::black); break; - case 31: current_format.setForeground(Qt::red); break; - case 32: current_format.setForeground(Qt::darkGreen); break; - case 33: current_format.setForeground(Qt::darkYellow); break; - case 34: current_format.setForeground(Qt::darkBlue); break; - case 35: current_format.setForeground(Qt::darkMagenta); break; - case 36: current_format.setForeground(Qt::darkCyan); break; - case 37: current_format.setForeground(Qt::lightGray); break; - case 39: current_format.setForeground(m_foreground_color); break; - case 90: current_format.setForeground(Qt::darkGray); break; - case 91: current_format.setForeground(Qt::red); break; - case 92: current_format.setForeground(Qt::green); break; - case 93: current_format.setForeground(Qt::yellow); break; - case 94: current_format.setForeground(Qt::blue); break; - case 95: current_format.setForeground(Qt::magenta); break; - case 96: current_format.setForeground(Qt::cyan); break; - case 97: current_format.setForeground(Qt::white); break; + case 0: + current_format = QTextCharFormat(); + current_format.setForeground(m_foreground_color); + break; + case 1: + current_format.setFontWeight(QFont::Bold); + break; + case 3: + current_format.setFontItalic(true); + break; + case 4: + current_format.setFontUnderline(true); + break; + default: // Background and extended colors not yet handled - default: - break; + if (const auto it = m_foreground_colors.find(code); it != m_foreground_colors.cend()) + { + current_format.setForeground(it->second); + } + break; } } } @@ -278,5 +290,7 @@ void AnsiHighlighter::highlightBlock(const QString& text) // Apply remaining format if (pos < text.length()) + { setFormat(pos, text.length() - pos, current_format); + } } diff --git a/rpcs3/rpcs3qt/syntax_highlighter.h b/rpcs3/rpcs3qt/syntax_highlighter.h index 3854059dd7..d94d600dd6 100644 --- a/rpcs3/rpcs3qt/syntax_highlighter.h +++ b/rpcs3/rpcs3qt/syntax_highlighter.h @@ -3,6 +3,9 @@ #include #include #include +#include + +#include // Inspired by https://doc.qt.io/qt-5/qtwidgets-richtext-syntaxhighlighter-example.html @@ -59,7 +62,9 @@ class AnsiHighlighter : public Highlighter Q_OBJECT public: - explicit AnsiHighlighter(QTextDocument* parent = nullptr); + explicit AnsiHighlighter(QPlainTextEdit* text_edit); + + void update_colors(QPlainTextEdit* text_edit); protected: const QRegularExpression ansi_re = QRegularExpression("\x1b\\[[0-9;]*m"); @@ -67,6 +72,8 @@ protected: QTextCharFormat m_escape_format; QColor m_foreground_color; + QColor m_background_color; + std::map m_foreground_colors; void highlightBlock(const QString& text) override; }; diff --git a/rpcs3/tests/test_fmt.cpp b/rpcs3/tests/test_fmt.cpp index 95c31d17c6..069051bd1a 100644 --- a/rpcs3/tests/test_fmt.cpp +++ b/rpcs3/tests/test_fmt.cpp @@ -1,3 +1,4 @@ +#include #include #include "Utilities/StrUtil.h" diff --git a/rpcs3/util/asm.hpp b/rpcs3/util/asm.hpp index df773dafb3..fbe6c03dcb 100644 --- a/rpcs3/util/asm.hpp +++ b/rpcs3/util/asm.hpp @@ -185,7 +185,7 @@ namespace utils inline void pause() { #if defined(ARCH_ARM64) - __asm__ volatile("yield"); + __asm__ volatile("isb" ::: "memory"); #elif defined(ARCH_X64) _mm_pause(); #else diff --git a/rpcs3/util/atomic.hpp b/rpcs3/util/atomic.hpp index 4503eaac61..f77aad7825 100644 --- a/rpcs3/util/atomic.hpp +++ b/rpcs3/util/atomic.hpp @@ -1011,7 +1011,12 @@ struct atomic_storage : atomic_storage static inline T exchange(T& dest, T value) { __atomic_thread_fence(__ATOMIC_ACQ_REL); + // GCC has recently started thinking using this instrinsic is breaking strict aliasing rules + // TODO: remove if this ever get fixed in GCC + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wstrict-aliasing" return std::bit_cast(__sync_lock_test_and_set(reinterpret_cast(&dest), std::bit_cast(value))); + #pragma GCC diagnostic pop } static inline void store(T& dest, T value) diff --git a/rpcs3/util/media_utils.cpp b/rpcs3/util/media_utils.cpp index 571eaa74fd..284f6eb758 100644 --- a/rpcs3/util/media_utils.cpp +++ b/rpcs3/util/media_utils.cpp @@ -575,34 +575,28 @@ namespace utils return; } - // Prepare resampler - av.swr = swr_alloc(); - if (!av.swr) - { - media_log.error("audio_decoder: Failed to allocate resampler for stream #%u in file '%s'", stream_index, path); - has_error = true; - return; - } - const int dst_channels = 2; const AVChannelLayout dst_channel_layout = AV_CHANNEL_LAYOUT_STEREO; const AVSampleFormat dst_format = AV_SAMPLE_FMT_FLT; - int set_err = 0; - if ((set_err = av_opt_set_int(av.swr, "in_channel_count", stream->codecpar->ch_layout.nb_channels, 0)) || - (set_err = av_opt_set_int(av.swr, "out_channel_count", dst_channels, 0)) || - (set_err = av_opt_set_chlayout(av.swr, "in_channel_layout", &stream->codecpar->ch_layout, 0)) || - (set_err = av_opt_set_chlayout(av.swr, "out_channel_layout", &dst_channel_layout, 0)) || - (set_err = av_opt_set_int(av.swr, "in_sample_rate", stream->codecpar->sample_rate, 0)) || - (set_err = av_opt_set_int(av.swr, "out_sample_rate", sample_rate, 0)) || - (set_err = av_opt_set_sample_fmt(av.swr, "in_sample_fmt", static_cast(stream->codecpar->format), 0)) || - (set_err = av_opt_set_sample_fmt(av.swr, "out_sample_fmt", dst_format, 0))) + const int set_err = swr_alloc_set_opts2(&av.swr, &dst_channel_layout, dst_format, + sample_rate, &stream->codecpar->ch_layout, + static_cast(stream->codecpar->format), + stream->codecpar->sample_rate, 0, nullptr); + if (set_err < 0) { media_log.error("audio_decoder: Failed to set resampler options: Error: %d='%s'", set_err, av_error_to_string(set_err)); has_error = true; return; } + if (!av.swr) + { + media_log.error("audio_decoder: Failed to allocate resampler for stream #%u in file '%s'", stream_index, path); + has_error = true; + return; + } + if (int err = swr_init(av.swr); err < 0 || !swr_is_initialized(av.swr)) { media_log.error("audio_decoder: Resampler has not been properly initialized: %d='%s'", err, av_error_to_string(err)); diff --git a/rpcs3/util/sysinfo.cpp b/rpcs3/util/sysinfo.cpp index d060f27e22..eece173cad 100755 --- a/rpcs3/util/sysinfo.cpp +++ b/rpcs3/util/sysinfo.cpp @@ -16,9 +16,15 @@ #else #include #include -#ifndef __APPLE__ +#ifdef __APPLE__ +#include +#else #include #include +#if defined(ARCH_ARM64) && defined(__linux__) +#include +#include +#endif #endif #endif @@ -444,6 +450,103 @@ u32 utils::get_rep_movsb_threshold() return g_value; } +#ifdef ARCH_ARM64 + +bool utils::has_neon() +{ + static const bool g_value = []() -> bool + { +#if defined(__linux__) + return (getauxval(AT_HWCAP) & HWCAP_ASIMD) != 0; +#elif defined(__APPLE__) + int val = 0; + size_t len = sizeof(val); + sysctlbyname("hw.optional.AdvSIMD", &val, &len, nullptr, 0); + int val_legacy = 0; + size_t len_legacy = sizeof(val_legacy); + sysctlbyname("hw.optional.neon", &val_legacy, &len_legacy, nullptr, 0); + return val != 0 || val_legacy != 0; +#elif defined(_WIN32) + return IsProcessorFeaturePresent(PF_ARM_VFP_32_REGISTERS_AVAILABLE) != 0; +#endif + }(); + return g_value; +} + +bool utils::has_sha3() +{ + static const bool g_value = []() -> bool + { +#if defined(__linux__) + return (getauxval(AT_HWCAP) & HWCAP_SHA3) != 0; +#elif defined(__APPLE__) + int val = 0; + size_t len = sizeof(val); + sysctlbyname("hw.optional.arm.FEAT_SHA3", &val, &len, nullptr, 0); + return val != 0; +#elif defined(_WIN32) + return IsProcessorFeaturePresent(PF_ARM_SHA3_INSTRUCTIONS_AVAILABLE) != 0; +#endif + }(); + return g_value; +} + +bool utils::has_dotprod() +{ + static const bool g_value = []() -> bool + { +#if defined(__linux__) + return (getauxval(AT_HWCAP) & HWCAP_ASIMDDP) != 0; +#elif defined(__APPLE__) + int val = 0; + size_t len = sizeof(val); + sysctlbyname("hw.optional.arm.FEAT_DotProd", &val, &len, nullptr, 0); + return val != 0; +#elif defined(_WIN32) + return IsProcessorFeaturePresent(PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE) != 0; +#endif + }(); + return g_value; +} + +bool utils::has_sve() +{ + static const bool g_value = []() -> bool + { +#if defined(__linux__) + return (getauxval(AT_HWCAP) & HWCAP_SVE) != 0; +#elif defined(__APPLE__) + int val = 0; + size_t len = sizeof(val); + sysctlbyname("hw.optional.arm.FEAT_SVE", &val, &len, nullptr, 0); + return val != 0; +#elif defined(_WIN32) + return IsProcessorFeaturePresent(PF_ARM_SVE_INSTRUCTIONS_AVAILABLE) != 0; +#endif + }(); + return g_value; +} + +bool utils::has_sve2() +{ + static const bool g_value = []() -> bool + { +#if defined(__linux__) + return (getauxval(AT_HWCAP2) & HWCAP2_SVE2) != 0; +#elif defined(__APPLE__) + int val = 0; + size_t len = sizeof(val); + sysctlbyname("hw.optional.arm.FEAT_SVE2", &val, &len, nullptr, 0); + return val != 0; +#elif defined(_WIN32) + return IsProcessorFeaturePresent(PF_ARM_SVE2_INSTRUCTIONS_AVAILABLE) != 0; +#endif + }(); + return g_value; +} + +#endif + std::string utils::get_cpu_brand() { #if defined(ARCH_X64) @@ -496,6 +599,17 @@ std::string utils::get_system_info() { fmt::append(result, " | TSC: Disabled"); } +#ifdef ARCH_ARM64 + + if (has_neon()) + { + result += " | Neon"; + } + else + { + fmt::throw_exception("Neon support not present"); + } +#else if (has_avx()) { @@ -562,6 +676,7 @@ std::string utils::get_system_info() { result += " | TSX disabled via microcode"; } +#endif return result; } diff --git a/rpcs3/util/sysinfo.hpp b/rpcs3/util/sysinfo.hpp index fd7e810f67..d9bd0c6660 100755 --- a/rpcs3/util/sysinfo.hpp +++ b/rpcs3/util/sysinfo.hpp @@ -54,7 +54,17 @@ namespace utils bool has_appropriate_um_wait(); bool has_um_wait(); +#ifdef ARCH_ARM64 + bool has_neon(); + bool has_sha3(); + + bool has_dotprod(); + + bool has_sve(); + + bool has_sve2(); +#endif std::string get_cpu_brand(); std::string get_system_info(); diff --git a/rpcs3/util/types.hpp b/rpcs3/util/types.hpp index 9be3118f4d..21a2ad1826 100644 --- a/rpcs3/util/types.hpp +++ b/rpcs3/util/types.hpp @@ -13,6 +13,7 @@ #include #include #include +#include #if defined(__SSE2__) || defined(_M_X64) || defined(_M_AMD64) || defined(__x86_64__) || defined(__amd64__) #define ARCH_X64 1