diff --git a/.ci/build-linux-aarch64.sh b/.ci/build-linux-aarch64.sh old mode 100644 new mode 100755 index 4a98002659..50247786fe --- a/.ci/build-linux-aarch64.sh +++ b/.ci/build-linux-aarch64.sh @@ -12,9 +12,16 @@ git submodule -q update --init $(awk '/path/ && !/llvm/ && !/opencv/ { print $3 mkdir build && cd build || exit 1 -export CC="${CLANG_BINARY}" -export CXX="${CLANGXX_BINARY}" -export LINKER="${LLD_BINARY}" +if [ "$COMPILER" = "gcc" ]; then + # These are set in the dockerfile + export CC="${GCC_BINARY}" + export CXX="${GXX_BINARY}" + export LINKER=gold +else + export CC="${CLANG_BINARY}" + export CXX="${CLANGXX_BINARY}" + export LINKER="${LLD_BINARY}" +fi export CFLAGS="$CFLAGS -fuse-ld=${LINKER}" export CXXFLAGS="$CXXFLAGS -fuse-ld=${LINKER}" diff --git a/.ci/setup-windows.sh b/.ci/setup-windows.sh index 07433d3505..a8fcec17d4 100755 --- a/.ci/setup-windows.sh +++ b/.ci/setup-windows.sh @@ -103,16 +103,24 @@ COMM_HASH=$(git rev-parse --short=8 HEAD) # Format the above into filenames if [ -n "$PR_NUMBER" ]; then AVVER="${COMM_TAG}-${COMM_HASH}" - BUILD="rpcs3-v${AVVER}_win64.7z" + BUILD_RAW="rpcs3-v${AVVER}_win64" + BUILD="${BUILD_RAW}.7z" else AVVER="${COMM_TAG}-${COMM_COUNT}" - BUILD="rpcs3-v${AVVER}-${COMM_HASH}_win64.7z" + BUILD_RAW="rpcs3-v${AVVER}-${COMM_HASH}_win64" + BUILD="${BUILD_RAW}.7z" fi # BRANCH is used for experimental build warnings for pr builds, used in main_window.cpp. # BUILD is the name of the release artifact +# BUILD_RAW is just filename # AVVER is used for GitHub releases, it is the version number. BRANCH="${REPO_NAME}/${REPO_BRANCH}" -echo "BRANCH=$BRANCH" > .ci/ci-vars.env -echo "BUILD=$BUILD" >> .ci/ci-vars.env -echo "AVVER=$AVVER" >> .ci/ci-vars.env + +# SC2129 +{ + echo "BRANCH=$BRANCH" + echo "BUILD=$BUILD" + echo "BUILD_RAW=$BUILD_RAW" + echo "AVVER=$AVVER" +} >> .ci/ci-vars.env diff --git a/.github/workflows/rpcs3.yml b/.github/workflows/rpcs3.yml new file mode 100644 index 0000000000..76619d766b --- /dev/null +++ b/.github/workflows/rpcs3.yml @@ -0,0 +1,165 @@ +name: Build RPCS3 + +on: + push: + paths-ignore: + - '.cirrus.yml' + - '.azure-pipelines.yml' + - 'README.md' + pull_request: + paths-ignore: + - '.cirrus.yml' + - '.azure-pipelines.yml' + - 'README.md' + workflow_dispatch: + +concurrency: + group: ${{ github.ref }}-${{ github.event_name }} + cancel-in-progress: true + +env: + BUILD_REPOSITORY_NAME: ${{ github.repository }} + BUILD_SOURCEBRANCHNAME: ${{ github.ref_name }} + BUILD_ARTIFACTSTAGINGDIRECTORY: ${{ github.workspace }}/build + +jobs: + Linux_Build: + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-24.04 + docker_img: "rpcs3/rpcs3-ci-jammy:1.0" + build_sh: "/rpcs3/.ci/build-linux.sh" + compiler: clang + - os: ubuntu-24.04 + docker_img: "rpcs3/rpcs3-ci-jammy:1.0" + build_sh: "/rpcs3/.ci/build-linux.sh" + compiler: gcc + - os: ubuntu-24.04-arm + docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.0" + build_sh: "/rpcs3/.ci/build-linux-aarch64.sh" + compiler: clang + runs-on: ${{ matrix.os }} + env: + CCACHE_DIR: ${{ github.workspace }}/ccache + CI_HAS_ARTIFACTS: true + DEPLOY_APPIMAGE: true + APPDIR: "/rpcs3/build/appdir" + ARTDIR: "/root/artifacts" + RELEASE_MESSAGE: "/rpcs3/GitHubReleaseMessage.txt" + COMPILER: ${{ matrix.compiler }} + + steps: + - name: Checkout repository + uses: actions/checkout@main + with: + fetch-depth: 0 + + - name: Setup Cache + uses: actions/cache@main + with: + path: ${{ env.CCACHE_DIR }} + key: ${{ runner.os }}-ccache-${{ matrix.compiler }} + + - name: Docker setup and build + run: | + docker pull --quiet ${{ matrix.docker_img }} + docker run \ + -v $PWD:/rpcs3 \ + --env-file .ci/docker.env \ + -v ${{ env.CCACHE_DIR }}:/root/.ccache \ + -v ${{ github.workspace }}/artifacts:/root/artifacts \ + ${{ matrix.docker_img }} \ + ${{ matrix.build_sh }} + + - name: Upload artifacts + #TODO: Upload artifact to release repository + #condition for release + #if: | + # github.event_name != 'pull_request' && + # github.repository == 'RPCS3/rpcs3' && + # github.ref == 'refs/heads/master' && + # matrix.compiler == 'clang' + uses: actions/upload-artifact@main + with: + name: RPCS3 for Linux (${{ runner.arch }}, ${{ matrix.compiler }}) + path: ${{ env.BUILD_ARTIFACTSTAGINGDIRECTORY }}/*.AppImage + compression-level: 0 + + Windows_Build: + runs-on: windows-2025 + env: + COMPILER: msvc + QT_VER_MAIN: '6' + QT_VER: '6.8.1' + QT_VER_MSVC: 'msvc2022' + QT_DATE: '202411221531' + VULKAN_VER: '1.3.268.0' + VULKAN_SDK_SHA: '8459ef49bd06b697115ddd3d97c9aec729e849cd775f5be70897718a9b3b9db5' + CACHE_DIR: ./cache + steps: + + - name: Checkout repository + uses: actions/checkout@main + with: + fetch-depth: 0 + + - name: Setup env + run: | + echo "QTDIR=C:\Qt\${{ env.QT_VER }}\${{ env.QT_VER_MSVC }}_64" >> ${{ github.env }} + echo "VULKAN_SDK=C:\VulkanSDK\${{ env.VULKAN_VER }}" >> ${{ github.env }} + + - name: Get Cache Keys + shell: bash + run: .ci/get_keys-windows.sh + + - name: Setup Cache + uses: actions/cache@main + with: + path: ${{ env.CACHE_DIR }} + key: "${{ runner.os }}-${{ env.COMPILER }}-${{ env.QT_VER }}-${{ env.VULKAN_SDK_SHA }}-${{ hashFiles('llvm.lock') }}-${{ hashFiles('glslang.lock') }}" + restore-keys: ${{ runner.os }}-${{ env.COMPILER }} + + - name: Download and unpack dependencies + shell: bash + run: .ci/setup-windows.sh + + - name: Export Variables + shell: bash + run: | + while IFS='=' read -r key val; do + # Skip lines that are empty or start with '#' + [[ -z "$key" || "$key" =~ ^# ]] && continue + echo "$key=$val" >> "${{ github.env }}" + done < .ci/ci-vars.env + + - name: Add msbuild to PATH + uses: microsoft/setup-msbuild@main + + - name: Compile RPCS3 + run: msbuild rpcs3.sln /p:Configuration=Release /p:Platform=x64 + + - name: Pack up build artifacts + shell: bash + run: | + mkdir -p ${{ env.BUILD_ARTIFACTSTAGINGDIRECTORY }} + .ci/deploy-windows.sh + + - name: Upload artifacts (7z) + #TODO: Upload artifact to release repository + #condition for release + #if: | + # github.event_name != 'pull_request' && + # github.repository == 'RPCS3/rpcs3' && + # github.ref == 'refs/heads/master' + uses: actions/upload-artifact@main + with: + name: RPCS3 for Windows (MSVC) + # 7z + # 7z.sha256 + path: | + ${{ env.BUILD_ARTIFACTSTAGINGDIRECTORY }}/${{ env.BUILD }} + ${{ env.BUILD_ARTIFACTSTAGINGDIRECTORY }}/${{ env.BUILD }}.sha256 + compression-level: 0 + if-no-files-found: error diff --git a/.gitmodules b/.gitmodules index 50cdb67ab6..9f453de899 100644 --- a/.gitmodules +++ b/.gitmodules @@ -28,10 +28,6 @@ path = 3rdparty/pugixml url = ../../zeux/pugixml.git ignore = dirty -[submodule "3rdparty/xxHash"] - path = 3rdparty/xxHash - url = ../../Cyan4973/xxHash.git - ignore = dirty [submodule "3rdparty/yaml-cpp"] path = 3rdparty/yaml-cpp/yaml-cpp url = ../../RPCS3/yaml-cpp.git diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt index 4c2c6a35ac..4625c0e81e 100644 --- a/3rdparty/CMakeLists.txt +++ b/3rdparty/CMakeLists.txt @@ -97,19 +97,6 @@ target_link_libraries(3rdparty_glslang INTERFACE SPIRV) add_subdirectory(yaml-cpp) -# xxHash -if (USE_SYSTEM_XXHASH) - pkg_check_modules(XXHASH REQUIRED IMPORTED_TARGET libxxhash) - add_library(xxhash INTERFACE) - target_link_libraries(xxhash INTERFACE PkgConfig::XXHASH) -else() - set(XXHASH_BUNDLED_MODE ON) - set(XXHASH_BUILD_XXHSUM OFF) - set(BUILD_SHARED_LIBS OFF CACHE BOOL "Make xxHash build static libs") - add_subdirectory(xxHash/cmake_unofficial EXCLUDE_FROM_ALL) - target_include_directories(xxhash INTERFACE xxHash) -endif() - # OpenGL find_package(OpenGL REQUIRED OPTIONAL_COMPONENTS EGL) @@ -364,7 +351,6 @@ add_library(3rdparty::flatbuffers ALIAS 3rdparty_flatbuffers) add_library(3rdparty::pugixml ALIAS pugixml) add_library(3rdparty::glslang ALIAS 3rdparty_glslang) add_library(3rdparty::yaml-cpp ALIAS yaml-cpp) -add_library(3rdparty::xxhash ALIAS xxhash) add_library(3rdparty::hidapi ALIAS 3rdparty_hidapi) add_library(3rdparty::libpng ALIAS ${LIBPNG_TARGET}) add_library(3rdparty::opengl ALIAS 3rdparty_opengl) diff --git a/3rdparty/bcdec/bcdec.hpp b/3rdparty/bcdec/bcdec.hpp new file mode 100644 index 0000000000..9fac43d0fb --- /dev/null +++ b/3rdparty/bcdec/bcdec.hpp @@ -0,0 +1,170 @@ +// Based on https://github.com/iOrange/bcdec/blob/963c5e56b7a335e066cff7d16a3de75f4e8ad366/bcdec.h +// provides functions to decompress blocks of BC compressed images +// +// ------------------------------------------------------------------------------ +// +// MIT LICENSE +// =========== +// Copyright (c) 2022 Sergii Kudlai +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// ------------------------------------------------------------------------------ + +#pragma once + +#include + +static void bcdec__color_block(const u8* compressedBlock, u8* dstColors, int destinationPitch, bool onlyOpaqueMode) { + u16 c0, c1; + u32 refColors[4]; /* 0xAABBGGRR */ + u32 colorIndices; + u32 r0, g0, b0, r1, g1, b1, r, g, b; + + c0 = *reinterpret_cast(compressedBlock); + c1 = *(reinterpret_cast(compressedBlock) + 1); + + /* Unpack 565 ref colors */ + r0 = (c0 >> 11) & 0x1F; + g0 = (c0 >> 5) & 0x3F; + b0 = c0 & 0x1F; + + r1 = (c1 >> 11) & 0x1F; + g1 = (c1 >> 5) & 0x3F; + b1 = c1 & 0x1F; + + /* Expand 565 ref colors to 888 */ + r = (r0 * 527 + 23) >> 6; + g = (g0 * 259 + 33) >> 6; + b = (b0 * 527 + 23) >> 6; + refColors[0] = 0xFF000000 | (r << 16) | (g << 8) | b; + + r = (r1 * 527 + 23) >> 6; + g = (g1 * 259 + 33) >> 6; + b = (b1 * 527 + 23) >> 6; + refColors[1] = 0xFF000000 | (r << 16) | (g << 8) | b; + + if (c0 > c1 || onlyOpaqueMode) + { /* Standard BC1 mode (also BC3 color block uses ONLY this mode) */ + /* color_2 = 2/3*color_0 + 1/3*color_1 + color_3 = 1/3*color_0 + 2/3*color_1 */ + r = ((2 * r0 + r1) * 351 + 61) >> 7; + g = ((2 * g0 + g1) * 2763 + 1039) >> 11; + b = ((2 * b0 + b1) * 351 + 61) >> 7; + refColors[2] = 0xFF000000 | (r << 16) | (g << 8) | b; + + r = ((r0 + r1 * 2) * 351 + 61) >> 7; + g = ((g0 + g1 * 2) * 2763 + 1039) >> 11; + b = ((b0 + b1 * 2) * 351 + 61) >> 7; + refColors[3] = 0xFF000000 | (r << 16) | (g << 8) | b; + } + else + { /* Quite rare BC1A mode */ + /* color_2 = 1/2*color_0 + 1/2*color_1; + color_3 = 0; */ + r = ((r0 + r1) * 1053 + 125) >> 8; + g = ((g0 + g1) * 4145 + 1019) >> 11; + b = ((b0 + b1) * 1053 + 125) >> 8; + refColors[2] = 0xFF000000 | (r << 16) | (g << 8) | b; + + refColors[3] = 0x00000000; + } + + colorIndices = *reinterpret_cast(compressedBlock + 4); + + /* Fill out the decompressed color block */ + for (int i = 0; i < 4; ++i) + { + for (int j = 0; j < 4; ++j) + { + int idx = colorIndices & 0x03; + *reinterpret_cast(dstColors + j * 4) = refColors[idx]; + colorIndices >>= 2; + } + + dstColors += destinationPitch; + } +} + +static void bcdec__sharp_alpha_block(const u16* alpha, u8* decompressed, int destinationPitch) { + for (int i = 0; i < 4; ++i) + { + for (int j = 0; j < 4; ++j) + { + decompressed[j * 4] = ((alpha[i] >> (4 * j)) & 0x0F) * 17; + } + decompressed += destinationPitch; + } +} + +static void bcdec__smooth_alpha_block(const u8* compressedBlock, u8* decompressed, int destinationPitch) { + u8 alpha[8]; + u64 block = *reinterpret_cast(compressedBlock); + u64 indices; + + alpha[0] = block & 0xFF; + alpha[1] = (block >> 8) & 0xFF; + + if (alpha[0] > alpha[1]) + { + /* 6 interpolated alpha values. */ + alpha[2] = (6 * alpha[0] + alpha[1]) / 7; /* 6/7*alpha_0 + 1/7*alpha_1 */ + alpha[3] = (5 * alpha[0] + 2 * alpha[1]) / 7; /* 5/7*alpha_0 + 2/7*alpha_1 */ + alpha[4] = (4 * alpha[0] + 3 * alpha[1]) / 7; /* 4/7*alpha_0 + 3/7*alpha_1 */ + alpha[5] = (3 * alpha[0] + 4 * alpha[1]) / 7; /* 3/7*alpha_0 + 4/7*alpha_1 */ + alpha[6] = (2 * alpha[0] + 5 * alpha[1]) / 7; /* 2/7*alpha_0 + 5/7*alpha_1 */ + alpha[7] = ( alpha[0] + 6 * alpha[1]) / 7; /* 1/7*alpha_0 + 6/7*alpha_1 */ + } + else + { + /* 4 interpolated alpha values. */ + alpha[2] = (4 * alpha[0] + alpha[1]) / 5; /* 4/5*alpha_0 + 1/5*alpha_1 */ + alpha[3] = (3 * alpha[0] + 2 * alpha[1]) / 5; /* 3/5*alpha_0 + 2/5*alpha_1 */ + alpha[4] = (2 * alpha[0] + 3 * alpha[1]) / 5; /* 2/5*alpha_0 + 3/5*alpha_1 */ + alpha[5] = ( alpha[0] + 4 * alpha[1]) / 5; /* 1/5*alpha_0 + 4/5*alpha_1 */ + alpha[6] = 0x00; + alpha[7] = 0xFF; + } + + indices = block >> 16; + for (int i = 0; i < 4; ++i) + { + for (int j = 0; j < 4; ++j) + { + decompressed[j * 4] = alpha[indices & 0x07]; + indices >>= 3; + } + decompressed += destinationPitch; + } +} + +static inline void bcdec_bc1(const u8* compressedBlock, u8* decompressedBlock, int destinationPitch) { + bcdec__color_block(compressedBlock, decompressedBlock, destinationPitch, false); +} + +static inline void bcdec_bc2(const u8* compressedBlock, u8* decompressedBlock, int destinationPitch) { + bcdec__color_block(compressedBlock + 8, decompressedBlock, destinationPitch, true); + bcdec__sharp_alpha_block(reinterpret_cast(compressedBlock), decompressedBlock + 3, destinationPitch); +} + +static inline void bcdec_bc3(const u8* compressedBlock, u8* decompressedBlock, int destinationPitch) { + bcdec__color_block(compressedBlock + 8, decompressedBlock, destinationPitch, true); + bcdec__smooth_alpha_block(compressedBlock, decompressedBlock + 3, destinationPitch); +} + diff --git a/3rdparty/xxHash b/3rdparty/xxHash deleted file mode 160000 index e626a72bc2..0000000000 --- a/3rdparty/xxHash +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e626a72bc2321cd320e953a0ccf1584cad60f363 diff --git a/3rdparty/xxhash.vcxproj b/3rdparty/xxhash.vcxproj deleted file mode 100644 index 6985e3205a..0000000000 --- a/3rdparty/xxhash.vcxproj +++ /dev/null @@ -1,65 +0,0 @@ - - - - - Debug - x64 - - - Release - x64 - - - - {939FE206-1182-ABC3-1234-FEAB88E98404} - - - - - - StaticLibrary - Unicode - - - true - - - false - - - - x64 - - - - - - - - - - - - - - - - - - MaxSpeed - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/buildfiles/msvc/rpcs3_default.props b/buildfiles/msvc/rpcs3_default.props index 77aca3d0dd..4f5862ca84 100644 --- a/buildfiles/msvc/rpcs3_default.props +++ b/buildfiles/msvc/rpcs3_default.props @@ -3,7 +3,7 @@ - .\;..\;..\3rdparty\asmjit\asmjit\src;..\..\3rdparty\yaml-cpp\yaml-cpp\include;..\3rdparty\ffmpeg\include;$(VC_IncludePath);$(WindowsSDK_IncludePath);$(UniversalCRT_IncludePath);..\3rdparty\libpng\libpng;..\3rdparty\GL;..\3rdparty\stblib\stb;..\3rdparty\openal\openal-soft\include\AL;..\3rdparty\pugixml\src;..\3rdparty\hidapi\hidapi;..\3rdparty\Optional;..\3rdparty\xxhash + .\;..\;..\3rdparty\asmjit\asmjit\src;..\..\3rdparty\yaml-cpp\yaml-cpp\include;..\3rdparty\ffmpeg\include;$(VC_IncludePath);$(WindowsSDK_IncludePath);$(UniversalCRT_IncludePath);..\3rdparty\libpng\libpng;..\3rdparty\GL;..\3rdparty\stblib\stb;..\3rdparty\openal\openal-soft\include\AL;..\3rdparty\pugixml\src;..\3rdparty\hidapi\hidapi $(SolutionDir)build\lib\$(Configuration)-$(Platform)\ $(SolutionDir)build\lib\$(Configuration)-$(Platform)\;$(UniversalCRT_LibraryPath_x64);$(LibraryPath) $(SolutionDir)build\tmp\$(ProjectName)-$(Configuration)-$(Platform)\ @@ -23,7 +23,7 @@ /utf-8 /Zc:throwingNew- /constexpr:steps16777216 %(AdditionalOptions) - xxhash.lib;ws2_32.lib;Iphlpapi.lib;Bcrypt.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib + ws2_32.lib;Iphlpapi.lib;Bcrypt.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib ..\3rdparty\ffmpeg\lib\windows\x86_64 8388608 1048576 diff --git a/rpcs3.sln b/rpcs3.sln index 26bc66179b..4b920a68e0 100644 --- a/rpcs3.sln +++ b/rpcs3.sln @@ -10,7 +10,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "emucore", "rpcs3\emucore.vc {3C67A2FF-4710-402A-BE3E-31B0CB0576DF} = {3C67A2FF-4710-402A-BE3E-31B0CB0576DF} {5228F863-E0DD-4DE7-AA7B-5C52B14CD4D0} = {5228F863-E0DD-4DE7-AA7B-5C52B14CD4D0} {8846A9AA-5539-4C91-8301-F54260E1A07A} = {8846A9AA-5539-4C91-8301-F54260E1A07A} - {939FE206-1182-ABC3-1234-FEAB88E98404} = {939FE206-1182-ABC3-1234-FEAB88E98404} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "llvm_build", "3rdparty\llvm\llvm_build.vcxproj", "{8BC303AB-25BE-4276-8E57-73F171B2D672}" @@ -67,8 +66,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rpcs3", "rpcs3\rpcs3.vcxpro EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hidapi", "3rdparty\hidapi\hidapi.vcxproj", "{A107C21C-418A-4697-BB10-20C3AA60E2E4}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xxhash", "3rdparty\xxhash.vcxproj", "{939FE206-1182-ABC3-1234-FEAB88E98404}" -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libusb-1.0 (static)", "3rdparty\libusb\libusb_static.vcxproj", "{349EE8F9-7D25-4909-AAF5-FF3FADE72187}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wolfssl", "3rdparty\wolfssl\wolfssl.vcxproj", "{73973223-5EE8-41CA-8E88-1D60E89A237B}" @@ -161,10 +158,6 @@ Global {A107C21C-418A-4697-BB10-20C3AA60E2E4}.Debug|x64.Build.0 = Debug|x64 {A107C21C-418A-4697-BB10-20C3AA60E2E4}.Release|x64.ActiveCfg = Release|x64 {A107C21C-418A-4697-BB10-20C3AA60E2E4}.Release|x64.Build.0 = Release|x64 - {939FE206-1182-ABC3-1234-FEAB88E98404}.Debug|x64.ActiveCfg = Debug|x64 - {939FE206-1182-ABC3-1234-FEAB88E98404}.Debug|x64.Build.0 = Debug|x64 - {939FE206-1182-ABC3-1234-FEAB88E98404}.Release|x64.ActiveCfg = Release|x64 - {939FE206-1182-ABC3-1234-FEAB88E98404}.Release|x64.Build.0 = Release|x64 {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug|x64.ActiveCfg = Debug|x64 {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug|x64.Build.0 = Debug|x64 {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release|x64.ActiveCfg = Release|x64 @@ -236,7 +229,6 @@ Global {3EE5F075-B546-42C4-B6A8-E3CCEF38B78D} = {10FBF193-D532-4CCF-B875-4C7091A7F6C2} {FDC361C5-7734-493B-8CFB-037308B35122} = {6C3B64A0-8F8A-4DC4-8C0B-D71EBEED7FA8} {A107C21C-418A-4697-BB10-20C3AA60E2E4} = {6C3B64A0-8F8A-4DC4-8C0B-D71EBEED7FA8} - {939FE206-1182-ABC3-1234-FEAB88E98404} = {6C3B64A0-8F8A-4DC4-8C0B-D71EBEED7FA8} {349EE8F9-7D25-4909-AAF5-FF3FADE72187} = {6C3B64A0-8F8A-4DC4-8C0B-D71EBEED7FA8} {73973223-5EE8-41CA-8E88-1D60E89A237B} = {6C3B64A0-8F8A-4DC4-8C0B-D71EBEED7FA8} {DA6F56B4-06A4-441D-AD70-AC5A7D51FADB} = {6C3B64A0-8F8A-4DC4-8C0B-D71EBEED7FA8} diff --git a/rpcs3/Emu/CMakeLists.txt b/rpcs3/Emu/CMakeLists.txt index 0b78942ac9..cd4cdb2a46 100644 --- a/rpcs3/Emu/CMakeLists.txt +++ b/rpcs3/Emu/CMakeLists.txt @@ -641,7 +641,7 @@ target_link_libraries(rpcs3_emu 3rdparty::vulkan 3rdparty::glew 3rdparty::libusb 3rdparty::wolfssl PRIVATE - 3rdparty::glslang 3rdparty::xxhash + 3rdparty::glslang ) if(APPLE) diff --git a/rpcs3/Emu/Cell/Modules/cellMic.cpp b/rpcs3/Emu/Cell/Modules/cellMic.cpp index 86e4b8bafa..98e4692927 100644 --- a/rpcs3/Emu/Cell/Modules/cellMic.cpp +++ b/rpcs3/Emu/Cell/Modules/cellMic.cpp @@ -8,6 +8,7 @@ #include #include +#include #include "3rdparty/OpenAL/openal-soft/include/AL/alext.h" @@ -575,6 +576,28 @@ bool microphone_device::has_data() const return mic_registered && mic_opened && mic_started && (rbuf_raw.has_data() || rbuf_dsp.has_data()); } +f32 microphone_device::calculate_energy_level() +{ + const auto& buffer = device_type == microphone_handler::real_singstar ? ::at32(devices, 0).buf : temp_buf; + const size_t num_samples = buffer.size() / sizeof(s16); + + f64 sum_squares = 0.0; + + for (usz i = 0; i < num_samples; i++) + { + const be_t sample = read_from_ptr>(buffer, i * sizeof(s16)); + const f64 normalized_sample = static_cast(sample) / -std::numeric_limits::min(); + sum_squares += normalized_sample * normalized_sample; + } + + const f32 rms = std::sqrt(sum_squares / num_samples); + const f32 decibels_max = 90.0f; + const f32 decibels_relative = 20.0f * std::log10(std::max(rms, 0.00001f)); + const f32 decibels = decibels_max + (decibels_relative * 0.5f); + + return std::clamp(decibels, 40.0f, decibels_max); +} + u32 microphone_device::capture_audio() { ensure(sample_size > 0); @@ -1089,7 +1112,7 @@ error_code cellMicGetSignalState(s32 dev_num, CellMicSignalState sig_state, vm:: *fval = 1.0f; // No gain applied break; case CELLMIC_SIGSTATE_MICENG: - *fval = 40.0f; // 40 decibels + *fval = device.calculate_energy_level(); break; case CELLMIC_SIGSTATE_SPKENG: *fval = 10.0f; // 10 decibels diff --git a/rpcs3/Emu/Cell/Modules/cellMic.h b/rpcs3/Emu/Cell/Modules/cellMic.h index b00848218c..f17b954278 100644 --- a/rpcs3/Emu/Cell/Modules/cellMic.h +++ b/rpcs3/Emu/Cell/Modules/cellMic.h @@ -289,6 +289,8 @@ public: void update_audio(); bool has_data() const; + f32 calculate_energy_level(); + bool is_registered() const { return mic_registered; } bool is_opened() const { return mic_opened; } bool is_started() const { return mic_started; } diff --git a/rpcs3/Emu/NP/np_handler.cpp b/rpcs3/Emu/NP/np_handler.cpp index 6c2ac08b48..85e9d96c89 100644 --- a/rpcs3/Emu/NP/np_handler.cpp +++ b/rpcs3/Emu/NP/np_handler.cpp @@ -854,6 +854,10 @@ namespace np room_msg_cb_ctx = 0; room_msg_cb_arg = {}; + presence_self.pr_status = {}; + presence_self.pr_data = {}; + presence_self.advertised = false; + if (g_cfg.net.psn_status == np_psn_status::psn_rpcn) { rpcn_log.notice("Setting RPCN state to disconnected!"); @@ -1157,21 +1161,23 @@ namespace np auto notifications = rpcn->get_notifications(); for (auto& notif : notifications) { + vec_stream noti_data(notif.second); + switch (notif.first) { - case rpcn::NotificationType::UserJoinedRoom: notif_user_joined_room(notif.second); break; - case rpcn::NotificationType::UserLeftRoom: notif_user_left_room(notif.second); break; - case rpcn::NotificationType::RoomDestroyed: notif_room_destroyed(notif.second); break; - case rpcn::NotificationType::UpdatedRoomDataInternal: notif_updated_room_data_internal(notif.second); break; - case rpcn::NotificationType::UpdatedRoomMemberDataInternal: notif_updated_room_member_data_internal(notif.second); break; - case rpcn::NotificationType::RoomMessageReceived: notif_room_message_received(notif.second); break; - case rpcn::NotificationType::SignalingHelper: notif_signaling_helper(notif.second); break; - case rpcn::NotificationType::MemberJoinedRoomGUI: notif_member_joined_room_gui(notif.second); break; - case rpcn::NotificationType::MemberLeftRoomGUI: notif_member_left_room_gui(notif.second); break; - case rpcn::NotificationType::RoomDisappearedGUI: notif_room_disappeared_gui(notif.second); break; - case rpcn::NotificationType::RoomOwnerChangedGUI: notif_room_owner_changed_gui(notif.second); break; - case rpcn::NotificationType::UserKickedGUI: notif_user_kicked_gui(notif.second); break; - case rpcn::NotificationType::QuickMatchCompleteGUI: notif_quickmatch_complete_gui(notif.second); break; + case rpcn::NotificationType::UserJoinedRoom: notif_user_joined_room(noti_data); break; + case rpcn::NotificationType::UserLeftRoom: notif_user_left_room(noti_data); break; + case rpcn::NotificationType::RoomDestroyed: notif_room_destroyed(noti_data); break; + case rpcn::NotificationType::UpdatedRoomDataInternal: notif_updated_room_data_internal(noti_data); break; + case rpcn::NotificationType::UpdatedRoomMemberDataInternal: notif_updated_room_member_data_internal(noti_data); break; + case rpcn::NotificationType::RoomMessageReceived: notif_room_message_received(noti_data); break; + case rpcn::NotificationType::SignalingHelper: notif_signaling_helper(noti_data); break; + case rpcn::NotificationType::MemberJoinedRoomGUI: notif_member_joined_room_gui(noti_data); break; + case rpcn::NotificationType::MemberLeftRoomGUI: notif_member_left_room_gui(noti_data); break; + case rpcn::NotificationType::RoomDisappearedGUI: notif_room_disappeared_gui(noti_data); break; + case rpcn::NotificationType::RoomOwnerChangedGUI: notif_room_owner_changed_gui(noti_data); break; + case rpcn::NotificationType::UserKickedGUI: notif_user_kicked_gui(noti_data); break; + case rpcn::NotificationType::QuickMatchCompleteGUI: notif_quickmatch_complete_gui(noti_data); break; default: fmt::throw_exception("Unknown notification(%d) received!", notif.first); break; } } @@ -1536,7 +1542,7 @@ namespace np } } - if (send_update && is_psn_active) + if (is_psn_active && (!presence_self.advertised || send_update)) { std::lock_guard lock(mutex_rpcn); @@ -1545,6 +1551,7 @@ namespace np return; } + presence_self.advertised = true; rpcn->send_presence(presence_self.pr_com_id, presence_self.pr_title, presence_self.pr_status, presence_self.pr_comment, presence_self.pr_data); } } diff --git a/rpcs3/Emu/NP/np_handler.h b/rpcs3/Emu/NP/np_handler.h index 4e0352c332..c7a086f224 100644 --- a/rpcs3/Emu/NP/np_handler.h +++ b/rpcs3/Emu/NP/np_handler.h @@ -293,22 +293,22 @@ namespace np bool error_and_disconnect(const std::string& error_msg); // Notification handlers - void notif_user_joined_room(std::vector& data); - void notif_user_left_room(std::vector& data); - void notif_room_destroyed(std::vector& data); - void notif_updated_room_data_internal(std::vector& data); - void notif_updated_room_member_data_internal(std::vector& data); - void notif_signaling_helper(std::vector& data); - void notif_room_message_received(std::vector& data); + void notif_user_joined_room(vec_stream& noti); + void notif_user_left_room(vec_stream& noti); + void notif_room_destroyed(vec_stream& noti); + void notif_updated_room_data_internal(vec_stream& noti); + void notif_updated_room_member_data_internal(vec_stream& noti); + void notif_signaling_helper(vec_stream& noti); + void notif_room_message_received(vec_stream& noti); - void generic_gui_notification_handler(std::vector& data, std::string_view name, s32 notification_type); + void generic_gui_notification_handler(vec_stream& noti, std::string_view name, s32 notification_type); - void notif_member_joined_room_gui(std::vector& data); - void notif_member_left_room_gui(std::vector& data); - void notif_room_disappeared_gui(std::vector& data); - void notif_room_owner_changed_gui(std::vector& data); - void notif_user_kicked_gui(std::vector& data); - void notif_quickmatch_complete_gui(std::vector& data); + void notif_member_joined_room_gui(vec_stream& noti); + void notif_member_left_room_gui(vec_stream& noti); + void notif_room_disappeared_gui(vec_stream& noti); + void notif_room_owner_changed_gui(vec_stream& noti); + void notif_user_kicked_gui(vec_stream& noti); + void notif_quickmatch_complete_gui(vec_stream& noti); // Reply handlers void reply_get_world_list(u32 req_id, rpcn::ErrorType error, vec_stream& reply); @@ -509,6 +509,7 @@ namespace np std::string pr_status; std::string pr_comment; std::vector pr_data; + atomic_t advertised = false; } presence_self; player_history& get_player_and_set_timestamp(const SceNpId& npid, u64 timestamp); diff --git a/rpcs3/Emu/NP/np_notifications.cpp b/rpcs3/Emu/NP/np_notifications.cpp index e051c64d65..55b74ec761 100644 --- a/rpcs3/Emu/NP/np_notifications.cpp +++ b/rpcs3/Emu/NP/np_notifications.cpp @@ -13,9 +13,8 @@ LOG_CHANNEL(rpcn_log, "rpcn"); namespace np { - void np_handler::notif_user_joined_room(std::vector& data) + void np_handler::notif_user_joined_room(vec_stream& noti) { - vec_stream noti(data); const auto* notification = noti.get_flatbuffer(); if (noti.is_error()) @@ -72,9 +71,8 @@ namespace np } } - void np_handler::notif_user_left_room(std::vector& data) + void np_handler::notif_user_left_room(vec_stream& noti) { - vec_stream noti(data); u64 room_id = noti.get(); const auto* update_info = noti.get_flatbuffer(); @@ -112,9 +110,8 @@ namespace np } } - void np_handler::notif_room_destroyed(std::vector& data) + void np_handler::notif_room_destroyed(vec_stream& noti) { - vec_stream noti(data); u64 room_id = noti.get(); const auto* update_info = noti.get_flatbuffer(); @@ -146,9 +143,8 @@ namespace np } } - void np_handler::notif_updated_room_data_internal(std::vector& data) + void np_handler::notif_updated_room_data_internal(vec_stream& noti) { - vec_stream noti(data); SceNpMatching2RoomId room_id = noti.get(); const auto* update_info = noti.get_flatbuffer(); @@ -182,9 +178,8 @@ namespace np } } - void np_handler::notif_updated_room_member_data_internal(std::vector& data) + void np_handler::notif_updated_room_member_data_internal(vec_stream& noti) { - vec_stream noti(data); SceNpMatching2RoomId room_id = noti.get(); const auto* update_info = noti.get_flatbuffer(); @@ -221,9 +216,8 @@ namespace np } } - void np_handler::notif_room_message_received(std::vector& data) + void np_handler::notif_room_message_received(vec_stream& noti) { - vec_stream noti(data); u64 room_id = noti.get(); u16 member_id = noti.get(); const auto* message_info = noti.get_flatbuffer(); @@ -254,29 +248,28 @@ namespace np } } - void np_handler::notif_signaling_helper(std::vector& data) + void np_handler::notif_signaling_helper(vec_stream& noti) { - vec_stream noti(data); - const u32 addr_p2p = noti.get(); - const u32 port_p2p = noti.get(); - const std::string str_npid = noti.get_string(false); + const auto* matching_info = noti.get_flatbuffer(); - if (noti.is_error()) + if (noti.is_error() || !matching_info->addr() || !matching_info->npid() || !matching_info->addr()->ip()) { rpcn_log.error("Received faulty SignalingHelper notification"); return; } SceNpId npid_p2p; - string_to_npid(str_npid, npid_p2p); + string_to_npid(matching_info->npid()->string_view(), npid_p2p); + + const u32 addr_p2p = register_ip(matching_info->addr()->ip()); + const u16 port_p2p = matching_info->addr()->port(); auto& sigh = g_fxo->get>(); sigh.send_information_packets(addr_p2p, port_p2p, npid_p2p); } - void np_handler::generic_gui_notification_handler(std::vector& data, std::string_view name, s32 notification_type) + void np_handler::generic_gui_notification_handler(vec_stream& noti, std::string_view name, s32 notification_type) { - vec_stream noti(data); const auto* update_info = noti.get_flatbuffer(); if (noti.is_error()) @@ -329,36 +322,35 @@ namespace np ctx->queue_callback(req_id, notification_type, 0); } - void np_handler::notif_member_joined_room_gui(std::vector& data) + void np_handler::notif_member_joined_room_gui(vec_stream& noti) { - return generic_gui_notification_handler(data, "MemberJoinedRoomGUI", SCE_NP_MATCHING_EVENT_ROOM_UPDATE_NEW_MEMBER); + return generic_gui_notification_handler(noti, "MemberJoinedRoomGUI", SCE_NP_MATCHING_EVENT_ROOM_UPDATE_NEW_MEMBER); } - void np_handler::notif_member_left_room_gui(std::vector& data) + void np_handler::notif_member_left_room_gui(vec_stream& noti) { - return generic_gui_notification_handler(data, "MemberLeftRoomGUI", SCE_NP_MATCHING_EVENT_ROOM_UPDATE_MEMBER_LEAVE); + return generic_gui_notification_handler(noti, "MemberLeftRoomGUI", SCE_NP_MATCHING_EVENT_ROOM_UPDATE_MEMBER_LEAVE); } - void np_handler::notif_room_disappeared_gui(std::vector& data) + void np_handler::notif_room_disappeared_gui(vec_stream& noti) { - return generic_gui_notification_handler(data, "RoomDisappearedGUI", SCE_NP_MATCHING_EVENT_ROOM_DISAPPEARED); + return generic_gui_notification_handler(noti, "RoomDisappearedGUI", SCE_NP_MATCHING_EVENT_ROOM_DISAPPEARED); } - void np_handler::notif_room_owner_changed_gui(std::vector& data) + void np_handler::notif_room_owner_changed_gui(vec_stream& noti) { - return generic_gui_notification_handler(data, "RoomOwnerChangedGUI", SCE_NP_MATCHING_EVENT_ROOM_UPDATE_OWNER_CHANGE); + return generic_gui_notification_handler(noti, "RoomOwnerChangedGUI", SCE_NP_MATCHING_EVENT_ROOM_UPDATE_OWNER_CHANGE); } - void np_handler::notif_user_kicked_gui(std::vector& data) + void np_handler::notif_user_kicked_gui(vec_stream& noti) { - return generic_gui_notification_handler(data, "UserKickedGUI", SCE_NP_MATCHING_EVENT_ROOM_KICKED); + return generic_gui_notification_handler(noti, "UserKickedGUI", SCE_NP_MATCHING_EVENT_ROOM_KICKED); } void gui_epilog(const shared_ptr& ctx); - void np_handler::notif_quickmatch_complete_gui(std::vector& data) + void np_handler::notif_quickmatch_complete_gui(vec_stream& noti) { - vec_stream noti(data); const auto* update_info = noti.get_flatbuffer(); if (noti.is_error()) diff --git a/rpcs3/Emu/NP/np_requests.cpp b/rpcs3/Emu/NP/np_requests.cpp index 1fc8a0c1e2..23e18f1353 100644 --- a/rpcs3/Emu/NP/np_requests.cpp +++ b/rpcs3/Emu/NP/np_requests.cpp @@ -252,7 +252,7 @@ namespace np case rpcn::ErrorType::RoomPasswordMismatch: error_code = SCE_NP_MATCHING2_SERVER_ERROR_PASSWORD_MISMATCH; break; case rpcn::ErrorType::RoomGroupFull: error_code = SCE_NP_MATCHING2_SERVER_ERROR_GROUP_FULL; break; case rpcn::ErrorType::RoomGroupJoinLabelNotFound: error_code = SCE_NP_MATCHING2_SERVER_ERROR_NO_SUCH_GROUP; break; - default: fmt::throw_exception("Unexpected error in reply to JoinRoom: %d", static_cast(error)); ; + default: fmt::throw_exception("Unexpected error in reply to JoinRoom: %d", static_cast(error)); } if (error_code != 0) @@ -334,8 +334,9 @@ namespace np switch (error) { case rpcn::ErrorType::NoError: break; + case rpcn::ErrorType::NotFound: error_code = SCE_NP_MATCHING2_SERVER_ERROR_NO_SUCH_ROOM; break; // Unsure if this should return another error(missing user in room has no appropriate error code) case rpcn::ErrorType::RoomMissing: error_code = SCE_NP_MATCHING2_SERVER_ERROR_NO_SUCH_ROOM; break; - default: fmt::throw_exception("Unexpected error in reply to LeaveRoom: %d", static_cast(error)); ; + default: fmt::throw_exception("Unexpected error in reply to LeaveRoom: %d", static_cast(error)); } if (error_code != CELL_OK) @@ -549,7 +550,7 @@ namespace np { case rpcn::ErrorType::NoError: break; case rpcn::ErrorType::RoomMissing: error_code = SCE_NP_MATCHING2_SERVER_ERROR_NO_SUCH_ROOM; break; - default: fmt::throw_exception("Unexpected error in reply to GetRoomDataInternal: %d", static_cast(error)); ; + default: fmt::throw_exception("Unexpected error in reply to GetRoomDataInternal: %d", static_cast(error)); } cb_info_opt->queue_callback(req_id, 0, error_code, 0); @@ -583,7 +584,7 @@ namespace np case rpcn::ErrorType::NoError: break; case rpcn::ErrorType::RoomMissing: error_code = SCE_NP_MATCHING2_SERVER_ERROR_NO_SUCH_ROOM; break; case rpcn::ErrorType::NotFound: error_code = SCE_NP_MATCHING2_SERVER_ERROR_NO_SUCH_USER; break; - default: fmt::throw_exception("Unexpected error in reply to GetRoomMemberDataInternal: %d", static_cast(error)); ; + default: fmt::throw_exception("Unexpected error in reply to GetRoomMemberDataInternal: %d", static_cast(error)); } if (error_code != CELL_OK) @@ -1081,7 +1082,7 @@ namespace np case rpcn::ErrorType::NotFound: trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_RANKING_STORE_NOT_FOUND); break; case rpcn::ErrorType::ScoreInvalid: trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_INVALID_SCORE); break; case rpcn::ErrorType::ScoreHasData: trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_GAME_DATA_ALREADY_EXISTS); break; - default: fmt::throw_exception("Unexpected error in reply to RecordScoreData: %d", static_cast(error)); ; + default: fmt::throw_exception("Unexpected error in reply to RecordScoreData: %d", static_cast(error)); } } @@ -1134,7 +1135,7 @@ namespace np { case rpcn::ErrorType::NoError: break; case rpcn::ErrorType::NotFound: score_trans->set_result_and_wake(SCE_NP_COMMUNITY_SERVER_ERROR_RANKING_GAME_DATA_MASTER_NOT_FOUND); return; - default: fmt::throw_exception("Unexpected error in reply to GetScoreData: %d", static_cast(error)); ; + default: fmt::throw_exception("Unexpected error in reply to GetScoreData: %d", static_cast(error)); } auto* tdata = std::get_if(&score_trans->tdata); diff --git a/rpcs3/Emu/NP/rpcn_client.cpp b/rpcs3/Emu/NP/rpcn_client.cpp index 0f0c9d871c..66b7a30ffe 100644 --- a/rpcs3/Emu/NP/rpcn_client.cpp +++ b/rpcs3/Emu/NP/rpcn_client.cpp @@ -599,7 +599,11 @@ namespace rpcn duration = std::min(duration, duration_ipv6); } - sem_rpcn.try_acquire_for(duration); + // Expected to fail unless rpcn is terminated + // The check is there to nuke a msvc warning + if (!sem_rpcn.try_acquire_for(duration)) + { + } } } } diff --git a/rpcs3/Emu/RSX/Capture/rsx_capture.cpp b/rpcs3/Emu/RSX/Capture/rsx_capture.cpp index bfd47df73d..3ac2dd929e 100644 --- a/rpcs3/Emu/RSX/Capture/rsx_capture.cpp +++ b/rpcs3/Emu/RSX/Capture/rsx_capture.cpp @@ -6,33 +6,33 @@ #include "Emu/RSX/RSXThread.h" #include "Emu/Memory/vm.h" -#include "xxhash.h" - namespace rsx { namespace capture { - void insert_mem_block_in_map(std::unordered_set& mem_changes, frame_capture_data::memory_block&& block, frame_capture_data::memory_block_data&& data) + void insert_mem_block_in_map(u64& indexer, std::unordered_set& mem_changes, frame_capture_data::memory_block&& block, frame_capture_data::memory_block_data&& data) { if (!data.data.empty()) { - u64 data_hash = XXH64(data.data.data(), data.data.size(), 0); + const auto [it, inserted] = frame_capture.memory_data_map.try_emplace(data, 0); + u64& data_hash = it->second; + + if (inserted) + { + data_hash = ++indexer; + } + block.data_state = data_hash; - auto it = frame_capture.memory_data_map.find(data_hash); - if (it != frame_capture.memory_data_map.end()) - { - if (it->second.data != data.data) - // screw this - fmt::throw_exception("Memory map hash collision detected...cant capture"); - } - else - frame_capture.memory_data_map.insert(std::make_pair(data_hash, std::move(data))); + const auto [block_it, inserted_block] = frame_capture.memory_map.try_emplace(block, 0); + u64& block_hash = block_it->second; + + if (inserted_block) + { + block_hash = ++indexer; + } - u64 block_hash = XXH64(&block, sizeof(frame_capture_data::memory_block), 0); mem_changes.insert(block_hash); - if (frame_capture.memory_map.find(block_hash) == frame_capture.memory_map.end()) - frame_capture.memory_map.insert(std::make_pair(block_hash, std::move(block))); } } @@ -48,6 +48,7 @@ namespace rsx // shove the mem_changes onto the last issued command std::unordered_set& mem_changes = frame_capture.replay_commands.back().memory_state; + u64& mem_indexer = frame_capture.memory_indexer; // capture fragment shader mem const auto [program_offset, program_location] = method_registers.shader_program_address(); @@ -63,7 +64,7 @@ namespace rsx frame_capture_data::memory_block_data block_data; block_data.data.resize(ucode_size + program_start); std::memcpy(block_data.data.data(), vm::base(addr), ucode_size + program_start); - insert_mem_block_in_map(mem_changes, std::move(block), std::move(block_data)); + insert_mem_block_in_map(mem_indexer, mem_changes, std::move(block), std::move(block_data)); // vertex shader is passed in registers, so it can be ignored @@ -90,7 +91,7 @@ namespace rsx frame_capture_data::memory_block_data block_data; block_data.data.resize(texSize); std::memcpy(block_data.data.data(), vm::base(texaddr), texSize); - insert_mem_block_in_map(mem_changes, std::move(block), std::move(block_data)); + insert_mem_block_in_map(mem_indexer, mem_changes, std::move(block), std::move(block_data)); } // save vertex texture mem @@ -116,7 +117,7 @@ namespace rsx frame_capture_data::memory_block_data block_data; block_data.data.resize(texSize); std::memcpy(block_data.data.data(), vm::base(texaddr), texSize); - insert_mem_block_in_map(mem_changes, std::move(block), std::move(block_data)); + insert_mem_block_in_map(mem_indexer, mem_changes, std::move(block), std::move(block_data)); } // save vertex buffer memory @@ -154,7 +155,7 @@ namespace rsx frame_capture_data::memory_block_data block_data; block_data.data.resize(bufferSize); std::memcpy(block_data.data.data(), vm::base(addr + (range.first * vertStride)), bufferSize); - insert_mem_block_in_map(mem_changes, std::move(block), std::move(block_data)); + insert_mem_block_in_map(mem_indexer, mem_changes, std::move(block), std::move(block_data)); } while (method_registers.current_draw_clause.next()); } @@ -193,7 +194,7 @@ namespace rsx frame_capture_data::memory_block_data block_data; block_data.data.resize(bufferSize); std::memcpy(block_data.data.data(), vm::base(idxAddr), bufferSize); - insert_mem_block_in_map(mem_changes, std::move(block), std::move(block_data)); + insert_mem_block_in_map(mem_indexer, mem_changes, std::move(block), std::move(block_data)); switch (index_type) { @@ -256,7 +257,7 @@ namespace rsx frame_capture_data::memory_block_data block_data; block_data.data.resize(bufferSize); std::memcpy(block_data.data.data(), vm::base(addr + (min_index * vertStride)), bufferSize); - insert_mem_block_in_map(mem_changes, std::move(block), std::move(block_data)); + insert_mem_block_in_map(mem_indexer, mem_changes, std::move(block), std::move(block_data)); } } } @@ -308,7 +309,7 @@ namespace rsx frame_capture_data::memory_block_data block_data; block_data.data.resize(src_size); std::memcpy(block_data.data.data(), pixels_src, src_size); - insert_mem_block_in_map(replay_command.memory_state, std::move(block), std::move(block_data)); + insert_mem_block_in_map(frame_capture.memory_indexer, replay_command.memory_state, std::move(block), std::move(block_data)); capture_display_tile_state(rsx, replay_command); } @@ -340,14 +341,17 @@ namespace rsx src += in_pitch; } - insert_mem_block_in_map(replay_command.memory_state, std::move(block), std::move(block_data)); + insert_mem_block_in_map(frame_capture.memory_indexer, replay_command.memory_state, std::move(block), std::move(block_data)); capture_display_tile_state(rsx, replay_command); } void capture_display_tile_state(thread* rsx, frame_capture_data::replay_command& replay_command) { + u64& mem_indexer = frame_capture.memory_indexer; + frame_capture_data::display_buffers_state dbstate; dbstate.count = rsx->display_buffers_count; + // should this only happen on flip? for (u32 i = 0; i < rsx->display_buffers_count; ++i) { @@ -358,9 +362,13 @@ namespace rsx dbstate.buffers[i].pitch = db.pitch; } - const u64 dbnum = XXH64(&dbstate, sizeof(frame_capture_data::display_buffers_state), 0); - if (frame_capture.display_buffers_map.find(dbnum) == frame_capture.display_buffers_map.end()) - frame_capture.display_buffers_map.insert(std::make_pair(dbnum, std::move(dbstate))); + const auto [db_it, db_inserted] = frame_capture.display_buffers_map.try_emplace(dbstate, 0); + u64& dbnum = db_it->second; + + if (db_inserted) + { + dbnum = ++mem_indexer; + } // todo: hook tile call sys_rsx call or something frame_capture_data::tile_state tilestate; @@ -386,10 +394,13 @@ namespace rsx zcstate.status1 = rsx->zculls[i].bound ? u32{zc.status1} : 0; } - const u64 tsnum = XXH64(&tilestate, sizeof(frame_capture_data::tile_state), 0); + const auto [ts_it, ts_inserted] = frame_capture.tile_map.try_emplace(tilestate, 0); + u64& tsnum = ts_it->second; - if (frame_capture.tile_map.find(tsnum) == frame_capture.tile_map.end()) - frame_capture.tile_map.insert(std::make_pair(tsnum, std::move(tilestate))); + if (ts_inserted) + { + tsnum = ++mem_indexer; + } replay_command.display_buffer_state = dbnum; replay_command.tile_state = tsnum; diff --git a/rpcs3/Emu/RSX/Capture/rsx_replay.cpp b/rpcs3/Emu/RSX/Capture/rsx_replay.cpp index 88bd6f9028..bb558744aa 100644 --- a/rpcs3/Emu/RSX/Capture/rsx_replay.cpp +++ b/rpcs3/Emu/RSX/Capture/rsx_replay.cpp @@ -106,26 +106,26 @@ namespace rsx // apply memory needed for command for (const auto& state : replay_cmd.memory_state) { - auto it = frame->memory_map.find(state); + auto it = std::find_if(frame->memory_map.begin(), frame->memory_map.end(), FN(x.second == state)); if (it == frame->memory_map.end()) fmt::throw_exception("requested memory state for command not found in memory_map"); - const auto& memblock = it->second; - auto it_data = frame->memory_data_map.find(it->second.data_state); + const auto& memblock = it->first; + auto it_data = std::find_if(frame->memory_data_map.begin(), frame->memory_data_map.end(), FN(x.second == memblock.data_state)); if (it_data == frame->memory_data_map.end()) fmt::throw_exception("requested memory data state for command not found in memory_data_map"); - const auto& data_block = it_data->second; + const auto& data_block = it_data->first; std::memcpy(vm::base(get_address(memblock.offset, memblock.location)), data_block.data.data(), data_block.data.size()); } if (replay_cmd.display_buffer_state != 0 && replay_cmd.display_buffer_state != cs.display_buffer_hash) { - auto it = frame->display_buffers_map.find(replay_cmd.display_buffer_state); + auto it = std::find_if(frame->display_buffers_map.begin(), frame->display_buffers_map.end(), FN(x.second == replay_cmd.display_buffer_state)); if (it == frame->display_buffers_map.end()) fmt::throw_exception("requested display buffer for command not found"); - const auto& dbstate = it->second; + const auto& dbstate = it->first; for (u32 i = 0; i < dbstate.count; ++i) { const auto& buf = dbstate.buffers[i]; @@ -136,16 +136,17 @@ namespace rsx sys_rsx_context_attribute(context_id, 0x104, i, u64{dbstate.buffers[i].width} << 32 | dbstate.buffers[i].height, u64{dbstate.buffers[i].pitch} << 32 | dbstate.buffers[i].offset, 0); } + cs.display_buffer_hash = replay_cmd.display_buffer_state; } if (replay_cmd.tile_state != 0 && replay_cmd.tile_state != cs.tile_hash) { - auto it = frame->tile_map.find(replay_cmd.tile_state); + auto it = std::find_if(frame->tile_map.begin(), frame->tile_map.end(), FN(x.second == replay_cmd.tile_state)); if (it == frame->tile_map.end()) fmt::throw_exception("requested tile state command not found"); - const auto& tstate = it->second; + const auto& tstate = it->first; for (u32 i = 0; i < limits::tiles_count; ++i) { const auto& ti = tstate.tiles[i]; diff --git a/rpcs3/Emu/RSX/Capture/rsx_replay.h b/rpcs3/Emu/RSX/Capture/rsx_replay.h index 891542f5c5..f11f2e08e9 100644 --- a/rpcs3/Emu/RSX/Capture/rsx_replay.h +++ b/rpcs3/Emu/RSX/Capture/rsx_replay.h @@ -11,7 +11,7 @@ namespace rsx enum : u32 { c_fc_magic = "RRC"_u32, - c_fc_version = 0x5, + c_fc_version = 0x6, }; struct frame_capture_data @@ -92,19 +92,60 @@ namespace rsx u32 version = c_fc_version; u32 LE_format = std::endian::little == std::endian::native; - // hashmap of holding various states for tile - std::unordered_map tile_map; - // hashmap of various memory 'changes' that can be applied to ps3 memory - std::unordered_map memory_map; - // hashmap of memory blocks that can be applied, this is split from above for size decrease - std::unordered_map memory_data_map; - // display buffer state map - std::unordered_map display_buffers_map; - // actual command queue to hold everything above + struct bitwise_hasher + { + template + usz operator()(const T& key) const noexcept + { + if constexpr (!!(requires (const T& a) { a.data[0]; })) + { + return std::hash{}(std::string_view{ reinterpret_cast(key.data.data()), key.data.size() * sizeof(key.data[0]) }); + } + + return std::hash{}(std::string_view{ reinterpret_cast(&key), sizeof(key) }); + } + + template + bool operator()(const T& keya, const T& keyb) const noexcept + { + if constexpr (!!(requires (const T& a) { a.data[0]; })) + { + if (keya.data.size() != keyb.data.size()) + { + return false; + } + + return std::equal(keya.data.begin(), keya.data.end(), keyb.data.begin()); + } + + return std::equal(reinterpret_cast(&keya), reinterpret_cast(&keya) + sizeof(T), reinterpret_cast(&keyb)); + } + }; + + template + using uno_bit_map = std::unordered_map; + + // Hashmap of holding various states for tile + uno_bit_map tile_map; + + // Hashmap of various memory 'changes' that can be applied to ps3 memory + uno_bit_map memory_map; + + // Hashmap of memory blocks that can be applied, this is split from above for size decrease + uno_bit_map memory_data_map; + + // Display buffer state map + uno_bit_map display_buffers_map; + + // Actual command queue to hold everything above std::vector replay_commands; + // Initial registers state at the beginning of the capture rsx::rsx_state reg_state; + // Indexer for memory blocks + u64 memory_indexer = 0x1234; + void reset() { magic = c_fc_magic; diff --git a/rpcs3/Emu/RSX/Common/TextureUtils.cpp b/rpcs3/Emu/RSX/Common/TextureUtils.cpp index 05146eb4a3..f2bc571e5f 100644 --- a/rpcs3/Emu/RSX/Common/TextureUtils.cpp +++ b/rpcs3/Emu/RSX/Common/TextureUtils.cpp @@ -3,6 +3,7 @@ #include "TextureUtils.h" #include "../RSXThread.h" #include "../rsx_utils.h" +#include "3rdparty/bcdec/bcdec.hpp" #include "util/asm.hpp" @@ -497,6 +498,63 @@ struct copy_rgb655_block_swizzled } }; +struct copy_decoded_bc1_block +{ + static void copy_mipmap_level(std::span dst, std::span src, u16 width_in_block, u16 row_count, u16 depth, u32 dst_pitch_in_block, u32 src_pitch_in_block) + { + u32 src_offset = 0, dst_offset = 0, destinationPitch = dst_pitch_in_block * 4; + for (u32 row = 0; row < row_count * depth; row++) + { + for (u32 col = 0; col < width_in_block; col++) { + const u8* compressedBlock = reinterpret_cast(&src[src_offset + col]); + u8* decompressedBlock = reinterpret_cast(&dst[dst_offset + col * 4]); + bcdec_bc1(compressedBlock, decompressedBlock, destinationPitch); + } + + src_offset += src_pitch_in_block; + dst_offset += destinationPitch; + } + } +}; + +struct copy_decoded_bc2_block +{ + static void copy_mipmap_level(std::span dst, std::span src, u16 width_in_block, u16 row_count, u16 depth, u32 dst_pitch_in_block, u32 src_pitch_in_block) + { + u32 src_offset = 0, dst_offset = 0, destinationPitch = dst_pitch_in_block * 4; + for (u32 row = 0; row < row_count * depth; row++) + { + for (u32 col = 0; col < width_in_block; col++) { + const u8* compressedBlock = reinterpret_cast(&src[src_offset + col]); + u8* decompressedBlock = reinterpret_cast(&dst[dst_offset + col * 4]); + bcdec_bc2(compressedBlock, decompressedBlock, destinationPitch); + } + + src_offset += src_pitch_in_block; + dst_offset += destinationPitch; + } + } +}; + +struct copy_decoded_bc3_block +{ + static void copy_mipmap_level(std::span dst, std::span src, u16 width_in_block, u16 row_count, u16 depth, u32 dst_pitch_in_block, u32 src_pitch_in_block) + { + u32 src_offset = 0, dst_offset = 0, destinationPitch = dst_pitch_in_block * 4; + for (u32 row = 0; row < row_count * depth; row++) + { + for (u32 col = 0; col < width_in_block; col++) { + const u8* compressedBlock = reinterpret_cast(&src[src_offset + col]); + u8* decompressedBlock = reinterpret_cast(&dst[dst_offset + col * 4]); + bcdec_bc3(compressedBlock, decompressedBlock, destinationPitch); + } + + src_offset += src_pitch_in_block; + dst_offset += destinationPitch; + } + } +}; + namespace { /** @@ -952,6 +1010,12 @@ namespace rsx case CELL_GCM_TEXTURE_COMPRESSED_DXT1: { + if (!caps.supports_dxt) + { + copy_decoded_bc1_block::copy_mipmap_level(dst_buffer.as_span(), src_layout.data.as_span(), w, h, depth, get_row_pitch_in_block(w, caps.alignment), src_layout.pitch_in_block); + break; + } + const bool is_3d = depth > 1; const bool is_po2 = utils::is_power_of_2(src_layout.width_in_texel) && utils::is_power_of_2(src_layout.height_in_texel); @@ -981,8 +1045,22 @@ namespace rsx } case CELL_GCM_TEXTURE_COMPRESSED_DXT23: + { + if (!caps.supports_dxt) + { + copy_decoded_bc2_block::copy_mipmap_level(dst_buffer.as_span(), src_layout.data.as_span(), w, h, depth, get_row_pitch_in_block(w, caps.alignment), src_layout.pitch_in_block); + break; + } + [[fallthrough]]; + } case CELL_GCM_TEXTURE_COMPRESSED_DXT45: { + if (!caps.supports_dxt) + { + copy_decoded_bc3_block::copy_mipmap_level(dst_buffer.as_span(), src_layout.data.as_span(), w, h, depth, get_row_pitch_in_block(w, caps.alignment), src_layout.pitch_in_block); + break; + } + const bool is_3d = depth > 1; const bool is_po2 = utils::is_power_of_2(src_layout.width_in_texel) && utils::is_power_of_2(src_layout.height_in_texel); @@ -1094,7 +1172,7 @@ namespace rsx return result; } - bool is_compressed_host_format(u32 texture_format) + bool is_compressed_host_format(const texture_uploader_capabilities& caps, u32 texture_format) { switch (texture_format) { @@ -1129,7 +1207,7 @@ namespace rsx case CELL_GCM_TEXTURE_COMPRESSED_DXT1: case CELL_GCM_TEXTURE_COMPRESSED_DXT23: case CELL_GCM_TEXTURE_COMPRESSED_DXT45: - return true; + return caps.supports_dxt; } 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 f03667042c..2b9552bc7a 100644 --- a/rpcs3/Emu/RSX/Common/TextureUtils.h +++ b/rpcs3/Emu/RSX/Common/TextureUtils.h @@ -227,6 +227,7 @@ namespace rsx bool supports_vtc_decoding; bool supports_hw_deswizzle; bool supports_zero_copy; + bool supports_dxt; usz alignment; }; @@ -252,7 +253,7 @@ namespace rsx u8 get_format_block_size_in_bytes(rsx::surface_color_format format); u8 get_format_block_size_in_bytes(rsx::surface_depth_format2 format); - bool is_compressed_host_format(u32 format); // Returns true for host-compressed formats (DXT) + bool is_compressed_host_format(const texture_uploader_capabilities& caps, u32 format); // Returns true for host-compressed formats (DXT) 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); diff --git a/rpcs3/Emu/RSX/GL/GLTexture.cpp b/rpcs3/Emu/RSX/GL/GLTexture.cpp index 74ffd7bf7b..36b4ffafa6 100644 --- a/rpcs3/Emu/RSX/GL/GLTexture.cpp +++ b/rpcs3/Emu/RSX/GL/GLTexture.cpp @@ -70,6 +70,7 @@ namespace gl GLenum get_sized_internal_format(u32 texture_format) { + const bool supports_dxt = get_driver_caps().EXT_texture_compression_s3tc_supported; switch (texture_format) { case CELL_GCM_TEXTURE_B8: return GL_R8; @@ -92,9 +93,9 @@ namespace gl case CELL_GCM_TEXTURE_D1R5G5B5: return GL_BGR5_A1; case CELL_GCM_TEXTURE_D8R8G8B8: return GL_BGRA8; case CELL_GCM_TEXTURE_Y16_X16_FLOAT: return GL_RG16F; - case CELL_GCM_TEXTURE_COMPRESSED_DXT1: return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; - case CELL_GCM_TEXTURE_COMPRESSED_DXT23: return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; - case CELL_GCM_TEXTURE_COMPRESSED_DXT45: return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + case CELL_GCM_TEXTURE_COMPRESSED_DXT1: return supports_dxt ? GL_COMPRESSED_RGBA_S3TC_DXT1_EXT : GL_BGRA8; + case CELL_GCM_TEXTURE_COMPRESSED_DXT23: return supports_dxt ? GL_COMPRESSED_RGBA_S3TC_DXT3_EXT : GL_BGRA8; + case CELL_GCM_TEXTURE_COMPRESSED_DXT45: return supports_dxt ? GL_COMPRESSED_RGBA_S3TC_DXT5_EXT : GL_BGRA8; case CELL_GCM_TEXTURE_COMPRESSED_HILO8: return GL_RG8; case CELL_GCM_TEXTURE_COMPRESSED_HILO_S8: return GL_RG8_SNORM; case CELL_GCM_TEXTURE_COMPRESSED_B8R8_G8R8: return GL_BGRA8; @@ -105,6 +106,7 @@ namespace gl std::tuple get_format_type(u32 texture_format) { + const bool supports_dxt = get_driver_caps().EXT_texture_compression_s3tc_supported; switch (texture_format) { case CELL_GCM_TEXTURE_B8: return std::make_tuple(GL_RED, GL_UNSIGNED_BYTE); @@ -127,9 +129,9 @@ namespace gl case CELL_GCM_TEXTURE_D1R5G5B5: return std::make_tuple(GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV); case CELL_GCM_TEXTURE_D8R8G8B8: return std::make_tuple(GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV); case CELL_GCM_TEXTURE_Y16_X16_FLOAT: return std::make_tuple(GL_RG, GL_HALF_FLOAT); - case CELL_GCM_TEXTURE_COMPRESSED_DXT1: return std::make_tuple(GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, GL_UNSIGNED_BYTE); - case CELL_GCM_TEXTURE_COMPRESSED_DXT23: return std::make_tuple(GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_UNSIGNED_BYTE); - case CELL_GCM_TEXTURE_COMPRESSED_DXT45: return std::make_tuple(GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_UNSIGNED_BYTE); + case CELL_GCM_TEXTURE_COMPRESSED_DXT1: return std::make_tuple(supports_dxt ? GL_COMPRESSED_RGBA_S3TC_DXT1_EXT : GL_BGRA, GL_UNSIGNED_BYTE); + case CELL_GCM_TEXTURE_COMPRESSED_DXT23: return std::make_tuple(supports_dxt ? GL_COMPRESSED_RGBA_S3TC_DXT3_EXT : GL_BGRA, GL_UNSIGNED_BYTE); + case CELL_GCM_TEXTURE_COMPRESSED_DXT45: return std::make_tuple(supports_dxt ? GL_COMPRESSED_RGBA_S3TC_DXT5_EXT : GL_BGRA, GL_UNSIGNED_BYTE); case CELL_GCM_TEXTURE_COMPRESSED_HILO8: return std::make_tuple(GL_RG, GL_UNSIGNED_BYTE); case CELL_GCM_TEXTURE_COMPRESSED_HILO_S8: return std::make_tuple(GL_RG, GL_BYTE); case CELL_GCM_TEXTURE_COMPRESSED_B8R8_G8R8: return std::make_tuple(GL_BGRA, GL_UNSIGNED_BYTE); @@ -587,6 +589,7 @@ namespace gl .supports_vtc_decoding = false, .supports_hw_deswizzle = driver_caps.ARB_compute_shader_supported, .supports_zero_copy = false, + .supports_dxt = driver_caps.EXT_texture_compression_s3tc_supported, .alignment = 4 }; @@ -596,7 +599,7 @@ namespace gl glBindBuffer(GL_PIXEL_UNPACK_BUFFER, GL_NONE); glBindBuffer(GL_PIXEL_PACK_BUFFER, GL_NONE); - if (rsx::is_compressed_host_format(format)) [[likely]] + if (rsx::is_compressed_host_format(caps, format)) [[likely]] { caps.supports_vtc_decoding = driver_caps.vendor_NVIDIA; unpack_settings.apply(); @@ -687,13 +690,13 @@ namespace gl if (driver_caps.ARB_compute_shader_supported) { u64 row_pitch = rsx::align2(layout.width_in_block * block_size_in_bytes, caps.alignment); - if (!rsx::is_compressed_host_format(format)) - { - // Handle emulated compressed formats with host unpack (R8G8 compressed) - row_pitch = std::max(row_pitch, dst->pitch()); - } - image_linear_size = row_pitch * layout.height_in_block * layout.depth; + // We're in the "else" branch, so "is_compressed_host_format()" is always false. + // Handle emulated compressed formats with host unpack (R8G8 compressed) + row_pitch = std::max(row_pitch, dst->pitch()); + + // FIXME: Double-check this logic; it seems like we should always use texels both here and for row_pitch. + image_linear_size = row_pitch * layout.height_in_texel * layout.depth; compute_scratch_mem = { nullptr, g_compute_decode_buffer.alloc(static_cast(image_linear_size), 256) }; compute_scratch_mem.first = reinterpret_cast(static_cast(compute_scratch_mem.second)); @@ -815,7 +818,8 @@ namespace gl // Calculate staging buffer size rsx::simple_array data_upload_buf; - if (rsx::is_compressed_host_format(gcm_format)) + rsx::texture_uploader_capabilities caps { .supports_dxt = gl::get_driver_caps().EXT_texture_compression_s3tc_supported }; + if (rsx::is_compressed_host_format(caps, gcm_format)) { const auto& desc = subresources_layout[0]; const u32 texture_data_sz = desc.width_in_block * desc.height_in_block * desc.depth * rsx::get_format_block_size_in_bytes(gcm_format); diff --git a/rpcs3/Emu/RSX/GL/glutils/capabilities.cpp b/rpcs3/Emu/RSX/GL/glutils/capabilities.cpp index 0c8de71786..9a5cbf9c12 100644 --- a/rpcs3/Emu/RSX/GL/glutils/capabilities.cpp +++ b/rpcs3/Emu/RSX/GL/glutils/capabilities.cpp @@ -33,7 +33,7 @@ namespace gl void capabilities::initialize() { - int find_count = 18; + int find_count = 19; int ext_count = 0; glGetIntegerv(GL_NUM_EXTENSIONS, &ext_count); @@ -178,6 +178,13 @@ namespace gl find_count--; continue; } + + if (check(ext_name, "GL_EXT_texture_compression_s3tc")) + { + EXT_texture_compression_s3tc_supported = true; + find_count--; + continue; + } } // Set GLSL version diff --git a/rpcs3/Emu/RSX/GL/glutils/capabilities.h b/rpcs3/Emu/RSX/GL/glutils/capabilities.h index 801a426d80..70ac5e9ae2 100644 --- a/rpcs3/Emu/RSX/GL/glutils/capabilities.h +++ b/rpcs3/Emu/RSX/GL/glutils/capabilities.h @@ -41,6 +41,7 @@ namespace gl bool NV_depth_buffer_float_supported = false; bool NV_fragment_shader_barycentric_supported = false; bool ARB_shader_texture_image_samples = false; + bool EXT_texture_compression_s3tc_supported = false; bool vendor_INTEL = false; // has broken GLSL compiler bool vendor_AMD = false; // has broken ARB_multidraw diff --git a/rpcs3/Emu/RSX/VK/VKCompute.cpp b/rpcs3/Emu/RSX/VK/VKCompute.cpp index 7cda0a8ff9..637642d8bf 100644 --- a/rpcs3/Emu/RSX/VK/VKCompute.cpp +++ b/rpcs3/Emu/RSX/VK/VKCompute.cpp @@ -80,6 +80,7 @@ namespace vk case vk::driver_vendor::LAVAPIPE: case vk::driver_vendor::V3DV: case vk::driver_vendor::PANVK: + case vk::driver_vendor::ARM_MALI: // TODO: Actually bench this. Using 32 for now to match other common configurations. case vk::driver_vendor::DOZEN: // Actual optimal size depends on the D3D device. Use 32 since it should work well on both AMD and NVIDIA diff --git a/rpcs3/Emu/RSX/VK/VKFormats.cpp b/rpcs3/Emu/RSX/VK/VKFormats.cpp index 4e270cc72d..f766a70e79 100644 --- a/rpcs3/Emu/RSX/VK/VKFormats.cpp +++ b/rpcs3/Emu/RSX/VK/VKFormats.cpp @@ -1,5 +1,6 @@ #include "stdafx.h" #include "VKFormats.h" +#include "VKHelpers.h" #include "vkutils/device.h" #include "vkutils/image.h" @@ -193,6 +194,7 @@ namespace vk VkFormat get_compatible_sampler_format(const gpu_formats_support& support, u32 format) { + const bool supports_dxt = vk::get_current_renderer()->get_texture_compression_bc_support(); switch (format) { #ifndef __APPLE__ @@ -213,9 +215,9 @@ namespace vk #endif case CELL_GCM_TEXTURE_B8: return VK_FORMAT_R8_UNORM; case CELL_GCM_TEXTURE_A8R8G8B8: return VK_FORMAT_B8G8R8A8_UNORM; - case CELL_GCM_TEXTURE_COMPRESSED_DXT1: return VK_FORMAT_BC1_RGBA_UNORM_BLOCK; - case CELL_GCM_TEXTURE_COMPRESSED_DXT23: return VK_FORMAT_BC2_UNORM_BLOCK; - case CELL_GCM_TEXTURE_COMPRESSED_DXT45: return VK_FORMAT_BC3_UNORM_BLOCK; + case CELL_GCM_TEXTURE_COMPRESSED_DXT1: return supports_dxt ? VK_FORMAT_BC1_RGBA_UNORM_BLOCK : VK_FORMAT_B8G8R8A8_UNORM; + case CELL_GCM_TEXTURE_COMPRESSED_DXT23: return supports_dxt ? VK_FORMAT_BC2_UNORM_BLOCK : VK_FORMAT_B8G8R8A8_UNORM; + case CELL_GCM_TEXTURE_COMPRESSED_DXT45: return supports_dxt ? VK_FORMAT_BC3_UNORM_BLOCK : VK_FORMAT_B8G8R8A8_UNORM; case CELL_GCM_TEXTURE_G8B8: return VK_FORMAT_R8G8_UNORM; case CELL_GCM_TEXTURE_DEPTH24_D8: return support.d24_unorm_s8? VK_FORMAT_D24_UNORM_S8_UINT : VK_FORMAT_D32_SFLOAT_S8_UINT; case CELL_GCM_TEXTURE_DEPTH24_D8_FLOAT: return VK_FORMAT_D32_SFLOAT_S8_UINT; diff --git a/rpcs3/Emu/RSX/VK/VKHelpers.cpp b/rpcs3/Emu/RSX/VK/VKHelpers.cpp index a0d0b643d1..ffd020979c 100644 --- a/rpcs3/Emu/RSX/VK/VKHelpers.cpp +++ b/rpcs3/Emu/RSX/VK/VKHelpers.cpp @@ -145,6 +145,9 @@ namespace vk case driver_vendor::PANVK: // Needs more testing break; + case driver_vendor::ARM_MALI: + // Needs more testing + break; default: rsx_log.warning("Unsupported device: %s", gpu_name); } diff --git a/rpcs3/Emu/RSX/VK/VKRenderTargets.h b/rpcs3/Emu/RSX/VK/VKRenderTargets.h index e7df9325c1..64ff78533a 100644 --- a/rpcs3/Emu/RSX/VK/VKRenderTargets.h +++ b/rpcs3/Emu/RSX/VK/VKRenderTargets.h @@ -188,6 +188,7 @@ namespace vk case driver_vendor::V3DV: case driver_vendor::HONEYKRISP: case driver_vendor::PANVK: + case driver_vendor::ARM_MALI: break; } diff --git a/rpcs3/Emu/RSX/VK/VKTexture.cpp b/rpcs3/Emu/RSX/VK/VKTexture.cpp index 1f6be63c78..d8d06420da 100644 --- a/rpcs3/Emu/RSX/VK/VKTexture.cpp +++ b/rpcs3/Emu/RSX/VK/VKTexture.cpp @@ -929,7 +929,7 @@ namespace vk return *pcmd; } - static const std::pair calculate_upload_pitch(int format, u32 heap_align, vk::image* dst_image, const rsx::subresource_layout& layout) + static const std::pair calculate_upload_pitch(int format, u32 heap_align, vk::image* dst_image, const rsx::subresource_layout& layout, const rsx::texture_uploader_capabilities& caps) { u32 block_in_pixel = rsx::get_format_block_size_in_texel(format); u8 block_size_in_bytes = rsx::get_format_block_size_in_bytes(format); @@ -950,7 +950,7 @@ namespace vk // We have row_pitch in source coordinates. But some formats have a software decode step which can affect this packing! // For such formats, the packed pitch on src does not match packed pitch on dst - if (!rsx::is_compressed_host_format(format)) + if (!rsx::is_compressed_host_format(caps, format)) { const auto host_texel_width = vk::get_format_texel_width(dst_image->format()); const auto host_packed_pitch = host_texel_width * layout.width_in_texel; @@ -977,7 +977,8 @@ namespace vk VkImageAspectFlags flags, vk::data_heap &upload_heap, u32 heap_align, rsx::flags32_t image_setup_flags) { const bool requires_depth_processing = (dst_image->aspect() & VK_IMAGE_ASPECT_STENCIL_BIT) || (format == CELL_GCM_TEXTURE_DEPTH16_FLOAT); - rsx::texture_uploader_capabilities caps{ .alignment = heap_align }; + auto pdev = vk::get_current_renderer(); + rsx::texture_uploader_capabilities caps{ .supports_dxt = pdev->get_texture_compression_bc_support(), .alignment = heap_align }; rsx::texture_memory_info opt{}; bool check_caps = true; @@ -997,11 +998,11 @@ namespace vk for (const rsx::subresource_layout &layout : subresource_layout) { - const auto [row_pitch, upload_pitch_in_texel] = calculate_upload_pitch(format, heap_align, dst_image, layout); + const auto [row_pitch, upload_pitch_in_texel] = calculate_upload_pitch(format, heap_align, dst_image, layout, caps); caps.alignment = row_pitch; // Calculate estimated memory utilization for this subresource - image_linear_size = row_pitch * layout.height_in_block * layout.depth; + image_linear_size = row_pitch * layout.depth * (rsx::is_compressed_host_format(caps, format) ? layout.height_in_block : layout.height_in_texel); // Only do GPU-side conversion if occupancy is good if (check_caps) diff --git a/rpcs3/Emu/RSX/VK/vkutils/chip_class.h b/rpcs3/Emu/RSX/VK/vkutils/chip_class.h index a905b7cc07..07fbb9a1c9 100644 --- a/rpcs3/Emu/RSX/VK/vkutils/chip_class.h +++ b/rpcs3/Emu/RSX/VK/vkutils/chip_class.h @@ -55,7 +55,8 @@ namespace vk NVK, V3DV, HONEYKRISP, - PANVK + PANVK, + ARM_MALI }; driver_vendor get_driver_vendor(); diff --git a/rpcs3/Emu/RSX/VK/vkutils/device.cpp b/rpcs3/Emu/RSX/VK/vkutils/device.cpp index 9a4471b784..207c1155c3 100644 --- a/rpcs3/Emu/RSX/VK/vkutils/device.cpp +++ b/rpcs3/Emu/RSX/VK/vkutils/device.cpp @@ -134,6 +134,10 @@ namespace vk // So far only AMD is known to remap image view and border color together. Mark as not required. custom_border_color_support.require_border_color_remap = get_driver_vendor() != driver_vendor::AMD; } + + // v3dv and PanVK support BC1-BC3 which is all we require, support is reported as false since not all formats are supported + optional_features_support.texture_compression_bc = features.textureCompressionBC + || get_driver_vendor() == driver_vendor::V3DV || get_driver_vendor() == driver_vendor::PANVK; } void physical_device::get_physical_device_properties(bool allow_extensions) @@ -303,9 +307,13 @@ namespace vk } if (gpu_name.find("Panfrost") != umax) - { + { // e.g. "Mali-G610 (Panfrost)" return driver_vendor::PANVK; } + else if (gpu_name.find("Mali") != umax) + { // e.g. "Mali-G610", hence "else" + return driver_vendor::ARM_MALI; + } return driver_vendor::unknown; } @@ -336,6 +344,8 @@ namespace vk return driver_vendor::HONEYKRISP; case VK_DRIVER_ID_MESA_PANVK: return driver_vendor::PANVK; + case VK_DRIVER_ID_ARM_PROPRIETARY: + return driver_vendor::ARM_MALI; default: // Mobile? return driver_vendor::unknown; @@ -471,8 +481,7 @@ namespace vk // Enable hardware features manually // Currently we require: // 1. Anisotropic sampling - // 2. DXT support - // 3. Indexable storage buffers + // 2. Indexable storage buffers VkPhysicalDeviceFeatures enabled_features{}; if (pgpu->shader_types_support.allow_float16) { @@ -566,7 +575,7 @@ namespace vk // enabled_features.shaderCullDistance = VK_TRUE; // Alt notation of clip distance enabled_features.samplerAnisotropy = VK_TRUE; - enabled_features.textureCompressionBC = VK_TRUE; + enabled_features.textureCompressionBC = pgpu->optional_features_support.texture_compression_bc; enabled_features.shaderStorageBufferArrayDynamicIndexing = VK_TRUE; // Optionally disable unsupported stuff @@ -659,19 +668,6 @@ namespace vk enabled_features.logicOp = VK_FALSE; } - if (!pgpu->features.textureCompressionBC && pgpu->get_driver_vendor() == driver_vendor::V3DV) - { - // v3dv supports BC1-BC3 which is all we require, support is reported as false since not all formats are supported - rsx_log.error("Your GPU running on the V3DV driver does not support full texture block compression. Graphics may not render correctly."); - enabled_features.textureCompressionBC = VK_FALSE; - } - - if (!pgpu->features.textureCompressionBC && pgpu->get_driver_vendor() == driver_vendor::PANVK) - { - rsx_log.error("Your GPU running on the PANVK driver does not support full texture block compression. Graphics may not render correctly."); - enabled_features.textureCompressionBC = VK_FALSE; - } - VkDeviceCreateInfo device = {}; device.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; device.pNext = nullptr; diff --git a/rpcs3/Emu/RSX/VK/vkutils/device.h b/rpcs3/Emu/RSX/VK/vkutils/device.h index 98f97e6e19..febd132d7e 100644 --- a/rpcs3/Emu/RSX/VK/vkutils/device.h +++ b/rpcs3/Emu/RSX/VK/vkutils/device.h @@ -92,6 +92,7 @@ namespace vk bool synchronization_2 = false; bool unrestricted_depth_range = false; bool extended_device_fault = false; + bool texture_compression_bc = false; } optional_features_support; friend class render_device; @@ -190,6 +191,7 @@ namespace vk bool get_barycoords_support() const { return pgpu->optional_features_support.barycentric_coords; } bool get_synchronization2_support() const { return pgpu->optional_features_support.synchronization_2; } bool get_extended_device_fault_support() const { return pgpu->optional_features_support.extended_device_fault; } + bool get_texture_compression_bc_support() const { return pgpu->optional_features_support.texture_compression_bc; } u64 get_descriptor_update_after_bind_support() const { return pgpu->descriptor_indexing_support.update_after_bind_mask; } u32 get_descriptor_max_draw_calls() const { return pgpu->descriptor_max_draw_calls; } diff --git a/rpcs3/util/serialization.hpp b/rpcs3/util/serialization.hpp index 120c70c606..5e139e05f8 100644 --- a/rpcs3/util/serialization.hpp +++ b/rpcs3/util/serialization.hpp @@ -6,7 +6,7 @@ namespace utils { template - concept FastRandomAccess = requires (T& obj) + concept FastRandomAccess = requires (const T& obj) { std::data(obj)[std::size(obj)]; }; @@ -24,13 +24,13 @@ namespace utils }; template - concept TupleAlike = requires () + concept TupleAlike = (!FastRandomAccess) && requires () { - std::tuple_size>::value; + std::tuple_size>::value; }; template - concept ListAlike = requires (T& obj) { obj.insert(obj.end(), std::declval()); }; + concept ListAlike = requires (std::remove_cvref_t& obj) { obj.insert(obj.end(), std::declval()); }; struct serial; @@ -249,6 +249,15 @@ public: return raw_serialize(std::addressof(obj), sizeof(obj)); } + template + static constexpr usz c_tup_size = std::tuple_size_v, std::remove_cvref_t, std::tuple<>>>; + + template + static std::remove_cvref_t& as_nonconst(T&& arg) noexcept + { + return const_cast&>(static_cast(arg)); + } + // std::vector, std::basic_string // Discourage using std::pair/tuple with vectors because it eliminates the possibility of bitwise optimization template requires FastRandomAccess && ListAlike && (!TupleAlike) @@ -327,9 +336,24 @@ public: { for (auto&& value : obj) { - if (!serialize(value)) + if constexpr (c_tup_size == 2) { - return false; + if (!serialize(as_nonconst(std::get<0>(value)))) + { + return false; + } + + if (!serialize(as_nonconst(std::get<1>(value)))) + { + return false; + } + } + else + { + if (!serialize(value)) + { + return false; + } } } @@ -347,9 +371,24 @@ public: for (auto&& value : obj) { - if (!serialize(value)) + if constexpr (c_tup_size == 2) { - return false; + if (!serialize(as_nonconst(std::get<0>(value)))) + { + return false; + } + + if (!serialize(as_nonconst(std::get<1>(value)))) + { + return false; + } + } + else + { + if (!serialize(value)) + { + return false; + } } } @@ -412,7 +451,7 @@ public: } // std::pair, std::tuple - template requires TupleAlike && (!FastRandomAccess) + template requires TupleAlike bool serialize(T& obj) { return serialize_tuple(obj); @@ -423,7 +462,7 @@ public: bool operator()(Args&&... args) noexcept { return ((AUDIT(!std::is_const_v> || is_writing()) - , serialize(const_cast&>(static_cast(args)))), ...); + , serialize(as_nonconst(args))), ...); } // Code style utility, for when utils::serial is a pointer for example @@ -519,30 +558,48 @@ public: AUDIT(!is_writing()); using type = std::remove_const_t; + using not_tuple_t = std::conditional_t, char, type>; if constexpr (Bitcopy) { - u8 buf[sizeof(type)]{}; + u8 buf[sizeof(not_tuple_t)]{}; ensure(raw_serialize(buf, sizeof(buf))); - return std::bit_cast(buf); - } - else if constexpr (std::is_constructible_v>) - { - return type(stx::exact_t(*this)); - } - else if constexpr (std::is_constructible_v) - { - type value{}; - ensure(serialize(value)); - return value; + return std::bit_cast(buf); } else if constexpr (TupleAlike) { - static_assert(std::tuple_size_v == 2, "Unimplemented tuple serialization!"); + constexpr usz tup_size = c_tup_size; - auto first = operator std::remove_cvref_t(std::declval()))>(); - return type{ std::move(first) - , operator std::remove_cvref_t(std::declval()))> }; + static_assert(tup_size == 2 || tup_size == 4, "Unimplemented tuple serialization!"); + + using first_t = std::remove_cvref_t(0, tup_size - 1)>(std::declval()))>; + using second_t = std::remove_cvref_t(1, tup_size - 1)>(std::declval()))>; + using third_t = std::remove_cvref_t(2, tup_size - 1)>(std::declval()))>; + using fourth_t = std::remove_cvref_t(3, tup_size - 1)>(std::declval()))>; + + first_t first = this->operator first_t(); + + if constexpr (tup_size == 4) + { + second_t second = this->operator second_t(); + third_t third = this->operator third_t(); + + return type{ std::move(first), std::move(second), std::move(third), this->operator fourth_t() }; + } + else + { + return type{ std::move(first), this->operator second_t() }; + } + } + else if constexpr (std::is_constructible_v>) + { + return not_tuple_t(stx::exact_t(*this)); + } + else if constexpr (std::is_constructible_v) + { + not_tuple_t value{}; + ensure(serialize(value)); + return value; } }