mirror of
https://github.com/RPCS3/rpcs3.git
synced 2026-10-12 02:09:17 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd814f8c92 | ||
|
|
354e1170ae | ||
|
|
ca3e5b268e | ||
|
|
27f0e879d3 | ||
|
|
ed195ef077 | ||
|
|
f1a858f351 | ||
|
|
b57cca4ae4 | ||
|
|
75ec6a8dca | ||
|
|
76de5ba764 | ||
|
|
ff162b9c99 | ||
|
|
5017397c4e | ||
|
|
4c52ab803c | ||
|
|
d277003ef7 | ||
|
|
c2abae56cd | ||
|
|
a5bbb23273 | ||
|
|
a2c78dd32c | ||
|
|
a7fe472583 | ||
|
|
bad72a3844 | ||
|
|
81431604ec | ||
|
|
884652109c | ||
|
|
709016a3ab | ||
|
|
50c78cb0e1 | ||
|
|
d4a37a1dd4 | ||
|
|
cf9133abed | ||
|
|
b6ee57853f | ||
|
|
b778355c68 | ||
|
|
831a078e3f | ||
|
|
0d999c08c4 | ||
|
|
d3abc6eee3 | ||
|
|
942feb64a3 | ||
|
|
5e8ba021ac | ||
|
|
22d9cd0424 | ||
|
|
dbc0ab4821 | ||
|
|
4bd4fc6a5e | ||
|
|
9173ff1657 | ||
|
|
4add14cb72 | ||
|
|
7a3deb649f | ||
|
|
a1deb2921c | ||
|
|
3ac6ce68d4 | ||
|
|
7919f44b9b | ||
|
|
0705c1b0c4 | ||
|
|
709acc39ca | ||
|
|
d3ef2822f8 | ||
|
|
3164e6fe46 | ||
|
|
24fbafdb1d | ||
|
|
428599535b | ||
|
|
79822e1ac5 | ||
|
|
4332190805 | ||
|
|
0e44b93c69 | ||
|
|
7a24caf715 | ||
|
|
04dd333f8d | ||
|
|
969913e3a7 | ||
|
|
2e1e416c35 | ||
|
|
e59af03ae2 | ||
|
|
84b905dd69 | ||
|
|
429f1fbcbb | ||
|
|
22f8cb040a | ||
|
|
88569563d8 | ||
|
|
28b3deaf8e | ||
|
|
9e2b54e6ac | ||
|
|
696fdc3059 | ||
|
|
2484984a31 | ||
|
|
cb4914754a | ||
|
|
b39b7a23dc | ||
|
|
11e147c646 | ||
|
|
1e14e7521b | ||
|
|
a013b60cde | ||
|
|
24ba41a3e1 | ||
|
|
7a90d09cfe | ||
|
|
5ecfc95c13 | ||
|
|
8604f1c5d8 | ||
|
|
8b05c8cc1f | ||
|
|
d75543a5b1 | ||
|
|
2aeb08f929 | ||
|
|
85c59207f7 |
@@ -8,7 +8,7 @@ sed -i '' 's/quarterly/latest/' /etc/pkg/FreeBSD.conf
|
||||
export ASSUME_ALWAYS_YES=true
|
||||
pkg info # debug
|
||||
|
||||
# WITH_LLVM
|
||||
# WITH_LLVM and Clang compiler
|
||||
pkg install "llvm$LLVM_COMPILER_VER"
|
||||
|
||||
# Mandatory dependencies (qtX-base is pulled via qtX-multimedia)
|
||||
|
||||
+11
-11
@@ -30,23 +30,23 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-24.04
|
||||
docker_img: "rpcs3/rpcs3-ci-jammy:1.14"
|
||||
docker_img: "rpcs3/rpcs3-ci-jammy:2.0"
|
||||
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.14"
|
||||
docker_img: "rpcs3/rpcs3-ci-jammy:2.0"
|
||||
build_sh: "/rpcs3/.ci/build-linux.sh"
|
||||
compiler: gcc
|
||||
- os: ubuntu-24.04-arm
|
||||
docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.14"
|
||||
docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:2.0"
|
||||
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.14"
|
||||
docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:2.0"
|
||||
build_sh: "/rpcs3/.ci/build-linux-aarch64.sh"
|
||||
compiler: gcc
|
||||
name: RPCS3 Linux ${{ matrix.os }} ${{ matrix.compiler }}
|
||||
@@ -136,7 +136,7 @@ jobs:
|
||||
CCACHE_DIR: /tmp/ccache_dir
|
||||
QT_VER: '6.11.1'
|
||||
QT_VER_MAIN: '6'
|
||||
LLVM_COMPILER_VER: '21'
|
||||
LLVM_COMPILER_VER: '22'
|
||||
RELEASE_MESSAGE: ../GitHubReleaseMessage.txt
|
||||
UPLOAD_COMMIT_HASH: ${{ matrix.UPLOAD_COMMIT_HASH }}
|
||||
UPLOAD_REPO_FULL_NAME: ${{ matrix.UPLOAD_REPO_FULL_NAME }}
|
||||
@@ -215,7 +215,7 @@ jobs:
|
||||
QT_VER: '6.11.1'
|
||||
QT_VER_MSVC: 'msvc2022'
|
||||
QT_DATE: '202605090529'
|
||||
LLVM_VER: '19.1.7'
|
||||
LLVM_VER: '22.1.8'
|
||||
VULKAN_VER: '1.4.341.1'
|
||||
VULKAN_SDK_SHA: 'bcf2d75aa9556889ab974858666e20b3655b6055a0db704ccb47279ff33b5bfe'
|
||||
CCACHE_SHA: '859141059ac950e1e8cd042c66f842f26b9e3a62a1669a69fe6ba180cb58bbdf'
|
||||
@@ -443,10 +443,10 @@ jobs:
|
||||
env:
|
||||
CCACHE_DIR: ${{ github.workspace }}/ccache
|
||||
QT_VER_MAIN: '6'
|
||||
LLVM_COMPILER_VER: '-devel'
|
||||
CC: 'clang-devel'
|
||||
CXX: 'clang++-devel'
|
||||
LLVM_CONFIG: 'llvm-config-devel'
|
||||
LLVM_COMPILER_VER: '22'
|
||||
LLVM_CONFIG: 'llvm-config22'
|
||||
CC: 'clang22'
|
||||
CXX: 'clang++22'
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
@@ -469,7 +469,7 @@ jobs:
|
||||
envs: 'QT_VER_MAIN LLVM_COMPILER_VER CCACHE_DIR CC CXX LLVM_CONFIG'
|
||||
usesh: true
|
||||
copyback: false
|
||||
release: "14.3"
|
||||
release: "14.4"
|
||||
run: .ci/install-freebsd.sh && .ci/build-freebsd.sh
|
||||
|
||||
- name: Save Build Ccache
|
||||
|
||||
Vendored
+35
-2
@@ -49,11 +49,44 @@
|
||||
<PropertyGroup Label="UserMacros">
|
||||
<CmakeReleaseCLI>call vsdevcmd.bat -arch=amd64
|
||||
cd "$(SolutionDir)build\tmp\$(ProjectName)-$(Configuration)-$(Platform)"
|
||||
cmake -G Ninja -DCMAKE_CXX_COMPILER="cl.exe" -DCMAKE_C_COMPILER="cl.exe" -DCMAKE_BUILD_TYPE="Release" -DCMAKE_INSTALL_PREFIX="./Release" -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded -DCMAKE_SYSTEM_VERSION=10.0 -DLIBTYPE=STATIC -DFORCE_STATIC_VCRT=true -DALSOFT_UTILS=false -DALSOFT_EXAMPLES=false -DALSOFT_INSTALL=false -DALSOFT_INSTALL_CONFIG=false -DALSOFT_INSTALL_HRTF_DATA=false -DALSOFT_INSTALL_AMBDEC_PRESETS=false -DALSOFT_INSTALL_EXAMPLES=false -DALSOFT_INSTALL_UTILS=false "$(SolutionDir)3rdparty\OpenAL\openal-soft"
|
||||
cmake -G Ninja ^
|
||||
-DCMAKE_CXX_COMPILER="cl.exe" ^
|
||||
-DCMAKE_C_COMPILER="cl.exe" ^
|
||||
-DCMAKE_BUILD_TYPE="Release" ^
|
||||
-DCMAKE_INSTALL_PREFIX="./Release" ^
|
||||
-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded ^
|
||||
-DCMAKE_SYSTEM_VERSION=10.0 ^
|
||||
-DLIBTYPE=STATIC ^
|
||||
-DFORCE_STATIC_VCRT=true ^
|
||||
-DALSOFT_UTILS=false ^
|
||||
-DALSOFT_EXAMPLES=false ^
|
||||
-DALSOFT_INSTALL=false ^
|
||||
-DALSOFT_INSTALL_CONFIG=false ^
|
||||
-DALSOFT_INSTALL_HRTF_DATA=false ^
|
||||
-DALSOFT_INSTALL_AMBDEC_PRESETS=false ^
|
||||
-DALSOFT_INSTALL_EXAMPLES=false ^
|
||||
-DALSOFT_INSTALL_UTILS=false ^
|
||||
"$(SolutionDir)3rdparty\OpenAL\openal-soft"
|
||||
</CmakeReleaseCLI>
|
||||
<CmakeDebugCLI>call vsdevcmd.bat -arch=amd64
|
||||
cd "$(SolutionDir)build\tmp\$(ProjectName)-$(Configuration)-$(Platform)"
|
||||
cmake -G Ninja -DCMAKE_CXX_COMPILER="cl.exe" -DCMAKE_C_COMPILER="cl.exe" -DCMAKE_BUILD_TYPE="Debug" -DCMAKE_INSTALL_PREFIX="./Debug" -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDebug -DCMAKE_SYSTEM_VERSION=10.0 -DLIBTYPE=STATIC -DALSOFT_UTILS=false -DALSOFT_EXAMPLES=false -DALSOFT_INSTALL=false -DALSOFT_INSTALL_CONFIG=false -DALSOFT_INSTALL_HRTF_DATA=false -DALSOFT_INSTALL_AMBDEC_PRESETS=false -DALSOFT_INSTALL_EXAMPLES=false -DALSOFT_INSTALL_UTILS=false "$(SolutionDir)3rdparty\OpenAL\openal-soft"
|
||||
cmake -G Ninja ^
|
||||
-DCMAKE_CXX_COMPILER="cl.exe" ^
|
||||
-DCMAKE_C_COMPILER="cl.exe" ^
|
||||
-DCMAKE_BUILD_TYPE="Debug" ^
|
||||
-DCMAKE_INSTALL_PREFIX="./Debug" ^
|
||||
-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDebug ^
|
||||
-DCMAKE_SYSTEM_VERSION=10.0 ^
|
||||
-DLIBTYPE=STATIC ^
|
||||
-DALSOFT_UTILS=false ^
|
||||
-DALSOFT_EXAMPLES=false ^
|
||||
-DALSOFT_INSTALL=false ^
|
||||
-DALSOFT_INSTALL_CONFIG=false ^
|
||||
-DALSOFT_INSTALL_HRTF_DATA=false ^
|
||||
-DALSOFT_INSTALL_AMBDEC_PRESETS=false ^
|
||||
-DALSOFT_INSTALL_EXAMPLES=false ^
|
||||
-DALSOFT_INSTALL_UTILS=false ^
|
||||
"$(SolutionDir)3rdparty\OpenAL\openal-soft"
|
||||
</CmakeDebugCLI>
|
||||
<CmakeCopyCLI>
|
||||
echo Copying..
|
||||
|
||||
Vendored
+1
-1
Submodule 3rdparty/fusion/fusion updated: 0954f3f67f...015d684942
Vendored
+22
-2
@@ -39,10 +39,30 @@
|
||||
<PropertyGroup Label="UserMacros">
|
||||
<CmakeGenerator>"Visual Studio $(VisualStudioVersion.Substring(0,2))"</CmakeGenerator>
|
||||
<CmakeReleaseCLI>call vsdevcmd.bat -arch=amd64
|
||||
cmake -G $(CmakeGenerator) -A x64 -DCMAKE_BUILD_TYPE="Release" -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded -DGLSLANG_TESTS=OFF -DENABLE_GLSLANG_BINARIES=OFF -DBUILD_EXTERNAL=OFF -DENABLE_SPVREMAPPER=OFF -DENABLE_HLSL=OFF -DENABLE_OPT=OFF -S glslang -B "$(SolutionDir)build\tmp\$(ProjectName)-$(Configuration)-$(Platform)"
|
||||
cmake -G $(CmakeGenerator) -A x64 ^
|
||||
-DCMAKE_BUILD_TYPE="Release" ^
|
||||
-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded ^
|
||||
-DGLSLANG_TESTS=OFF ^
|
||||
-DENABLE_GLSLANG_BINARIES=OFF ^
|
||||
-DBUILD_EXTERNAL=OFF ^
|
||||
-DENABLE_SPVREMAPPER=OFF ^
|
||||
-DENABLE_HLSL=OFF ^
|
||||
-DENABLE_OPT=OFF ^
|
||||
-S glslang ^
|
||||
-B "$(SolutionDir)build\tmp\$(ProjectName)-$(Configuration)-$(Platform)"
|
||||
</CmakeReleaseCLI>
|
||||
<CmakeDebugCLI>call vsdevcmd.bat -arch=amd64
|
||||
cmake -G $(CmakeGenerator) -A x64 -DCMAKE_BUILD_TYPE="Debug" -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDebug -DGLSLANG_TESTS=OFF -DENABLE_GLSLANG_BINARIES=OFF -DBUILD_EXTERNAL=OFF -DENABLE_SPVREMAPPER=OFF -DENABLE_HLSL=OFF -DENABLE_OPT=OFF -S glslang -B "$(SolutionDir)build\tmp\$(ProjectName)-$(Configuration)-$(Platform)"
|
||||
cmake -G $(CmakeGenerator) -A x64 ^
|
||||
-DCMAKE_BUILD_TYPE="Debug" ^
|
||||
-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDebug ^
|
||||
-DGLSLANG_TESTS=OFF ^
|
||||
-DENABLE_GLSLANG_BINARIES=OFF ^
|
||||
-DBUILD_EXTERNAL=OFF ^
|
||||
-DENABLE_SPVREMAPPER=OFF ^
|
||||
-DENABLE_HLSL=OFF ^
|
||||
-DENABLE_OPT=OFF ^
|
||||
-S glslang ^
|
||||
-B "$(SolutionDir)build\tmp\$(ProjectName)-$(Configuration)-$(Platform)"
|
||||
</CmakeDebugCLI>
|
||||
<CmakeCopyCLI>
|
||||
echo Copying..
|
||||
|
||||
Vendored
+1
-1
@@ -51,7 +51,7 @@ if(WITH_LLVM)
|
||||
|
||||
set(STATIC_LINK_LLVM ON CACHE BOOL "Link against LLVM statically. This will get set to ON if you build LLVM from the submodule." FORCE)
|
||||
|
||||
find_package(LLVM 19.1 CONFIG)
|
||||
find_package(LLVM 22.1 CONFIG)
|
||||
if(NOT LLVM_FOUND)
|
||||
message(FATAL_ERROR "Couldn't build LLVM from the submodule. You might need to run `git submodule update --init`")
|
||||
endif()
|
||||
|
||||
Vendored
+1
-1
Submodule 3rdparty/llvm/llvm updated: cd708029e0...ca7933e47d
Vendored
+56
-2
@@ -40,11 +40,65 @@
|
||||
<PropertyGroup Label="UserMacros">
|
||||
<CmakeReleaseCLI>call vsdevcmd.bat -arch=amd64
|
||||
cd "$(SolutionDir)build\tmp\llvm_build-$(Configuration)-$(Platform)"
|
||||
cmake -G Ninja -DCMAKE_CXX_COMPILER="cl.exe" -DCMAKE_C_COMPILER="cl.exe" -DCMAKE_BUILD_TYPE="Release" -DCMAKE_INSTALL_PREFIX="$(SolutionDir)build\lib\$(Configuration)-$(Platform)\llvm_build" -DCMAKE_SYSTEM_VERSION=10.0 -DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=$(WindowsTargetPlatformVersion) -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded -DLLVM_TARGETS_TO_BUILD=X86 -DLLVM_DEFAULT_TARGET_TRIPLE:STRING=x86_64-pc-windows-msvc -DLLVM_HOST_TRIPLE:STRING=x86_64-pc-windows-msvc -DLLVM_BUILD_RUNTIME=OFF -DLLVM_BUILD_TOOLS=OFF -DLLVM_BUILD_BENCHMARKS=OFF -DLLVM_BUILD_DOCS=OFF -DLLVM_BUILD_EXAMPLES=OFF -DLLVM_BUILD_TESTS=OFF\ -DLLVM_INCLUDE_TOOLS=OFF -DLLVM_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_BENCHMARKS=OFF -DLLVM_INCLUDE_UTILS=OFF -DLLVM_INSTALL_UTILS=OFF -DLLVM_ENABLE_DIA_SDK=OFF -DLLVM_ENABLE_PDB=OFF -DLLVM_USE_INTEL_JITEVENTS=ON "$(SolutionDir)3rdparty\llvm\llvm\llvm"
|
||||
cmake -G Ninja ^
|
||||
-DCMAKE_CXX_COMPILER="cl.exe" ^
|
||||
-DCMAKE_C_COMPILER="cl.exe" ^
|
||||
-DCMAKE_BUILD_TYPE="Release" ^
|
||||
-DCMAKE_INSTALL_PREFIX="$(SolutionDir)build\lib\$(Configuration)-$(Platform)\llvm_build" ^
|
||||
-DCMAKE_SYSTEM_VERSION=10.0 ^
|
||||
-DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=$(WindowsTargetPlatformVersion) ^
|
||||
-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded ^
|
||||
-DLLVM_TARGETS_TO_BUILD=X86 ^
|
||||
-DLLVM_DEFAULT_TARGET_TRIPLE:STRING=x86_64-pc-windows-msvc ^
|
||||
-DLLVM_HOST_TRIPLE:STRING=x86_64-pc-windows-msvc ^
|
||||
-DLLVM_BUILD_RUNTIME=OFF ^
|
||||
-DLLVM_BUILD_TOOLS=OFF ^
|
||||
-DLLVM_BUILD_BENCHMARKS=OFF ^
|
||||
-DLLVM_BUILD_DOCS=OFF ^
|
||||
-DLLVM_BUILD_EXAMPLES=OFF ^
|
||||
-DLLVM_BUILD_TESTS=OFF ^
|
||||
-DLLVM_INCLUDE_TOOLS=OFF ^
|
||||
-DLLVM_INCLUDE_DOCS=OFF ^
|
||||
-DLLVM_INCLUDE_EXAMPLES=OFF ^
|
||||
-DLLVM_INCLUDE_TESTS=OFF ^
|
||||
-DLLVM_INCLUDE_BENCHMARKS=OFF ^
|
||||
-DLLVM_INCLUDE_UTILS=OFF ^
|
||||
-DLLVM_INSTALL_UTILS=OFF ^
|
||||
-DLLVM_ENABLE_DIA_SDK=OFF ^
|
||||
-DLLVM_ENABLE_PDB=OFF ^
|
||||
-DLLVM_USE_INTEL_JITEVENTS=ON ^
|
||||
"$(SolutionDir)3rdparty\llvm\llvm\llvm"
|
||||
</CmakeReleaseCLI>
|
||||
<CmakeDebugCLI>call vsdevcmd.bat -arch=amd64
|
||||
cd "$(SolutionDir)build\tmp\llvm_build-$(Configuration)-$(Platform)"
|
||||
cmake -G Ninja -DCMAKE_CXX_COMPILER="cl.exe" -DCMAKE_C_COMPILER="cl.exe" -DCMAKE_BUILD_TYPE="Debug" -DCMAKE_INSTALL_PREFIX="$(SolutionDir)build\lib\$(Configuration)-$(Platform)\llvm_build" -DCMAKE_SYSTEM_VERSION=10.0 -DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=$(WindowsTargetPlatformVersion) -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDebugDLL -DLLVM_TARGETS_TO_BUILD=X86 -DLLVM_DEFAULT_TARGET_TRIPLE:STRING=x86_64-pc-windows-msvc -DLLVM_HOST_TRIPLE:STRING=x86_64-pc-windows-msvc -DLLVM_BUILD_RUNTIME=OFF -DLLVM_BUILD_TOOLS=OFF -DLLVM_BUILD_BENCHMARKS=OFF -DLLVM_BUILD_DOCS=OFF -DLLVM_BUILD_EXAMPLES=OFF -DLLVM_BUILD_TESTS=OFF\ -DLLVM_INCLUDE_TOOLS=OFF -DLLVM_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_BENCHMARKS=OFF -DLLVM_INCLUDE_UTILS=OFF -DLLVM_INSTALL_UTILS=OFF -DLLVM_ENABLE_DIA_SDK=OFF -DLLVM_ENABLE_PDB=OFF -DLLVM_USE_INTEL_JITEVENTS=ON "$(SolutionDir)3rdparty\llvm\llvm\llvm"
|
||||
cmake -G Ninja ^
|
||||
-DCMAKE_CXX_COMPILER="cl.exe" ^
|
||||
-DCMAKE_C_COMPILER="cl.exe" ^
|
||||
-DCMAKE_BUILD_TYPE="Debug" ^
|
||||
-DCMAKE_INSTALL_PREFIX="$(SolutionDir)build\lib\$(Configuration)-$(Platform)\llvm_build" ^
|
||||
-DCMAKE_SYSTEM_VERSION=10.0 ^
|
||||
-DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=$(WindowsTargetPlatformVersion) ^
|
||||
-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDebugDLL ^
|
||||
-DLLVM_TARGETS_TO_BUILD=X86 ^
|
||||
-DLLVM_DEFAULT_TARGET_TRIPLE:STRING=x86_64-pc-windows-msvc ^
|
||||
-DLLVM_HOST_TRIPLE:STRING=x86_64-pc-windows-msvc ^
|
||||
-DLLVM_BUILD_RUNTIME=OFF ^
|
||||
-DLLVM_BUILD_TOOLS=OFF ^
|
||||
-DLLVM_BUILD_BENCHMARKS=OFF ^
|
||||
-DLLVM_BUILD_DOCS=OFF ^
|
||||
-DLLVM_BUILD_EXAMPLES=OFF ^
|
||||
-DLLVM_BUILD_TESTS=OFF ^
|
||||
-DLLVM_INCLUDE_TOOLS=OFF ^
|
||||
-DLLVM_INCLUDE_DOCS=OFF ^
|
||||
-DLLVM_INCLUDE_EXAMPLES=OFF ^
|
||||
-DLLVM_INCLUDE_TESTS=OFF ^
|
||||
-DLLVM_INCLUDE_BENCHMARKS=OFF ^
|
||||
-DLLVM_INCLUDE_UTILS=OFF ^
|
||||
-DLLVM_INSTALL_UTILS=OFF ^
|
||||
-DLLVM_ENABLE_DIA_SDK=OFF ^
|
||||
-DLLVM_ENABLE_PDB=OFF ^
|
||||
-DLLVM_USE_INTEL_JITEVENTS=ON ^
|
||||
"$(SolutionDir)3rdparty\llvm\llvm\llvm"
|
||||
</CmakeDebugCLI>
|
||||
<CmakeCleanCLI>
|
||||
echo Cleaning..
|
||||
|
||||
+56
-2
@@ -40,11 +40,65 @@
|
||||
<PropertyGroup Label="UserMacros">
|
||||
<CmakeReleaseCLI>call vsdevcmd.bat -arch=amd64
|
||||
cd "$(SolutionDir)build\tmp\llvm_build-$(Configuration)-$(Platform)"
|
||||
cmake -G Ninja -DCMAKE_CXX_COMPILER="clang-cl.exe" -DCMAKE_C_COMPILER="clang-cl.exe" -DCMAKE_BUILD_TYPE="Release" -DCMAKE_INSTALL_PREFIX="$(SolutionDir)build\lib\$(Configuration)-$(Platform)\llvm_build" -DCMAKE_SYSTEM_VERSION=10.0 -DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=$(WindowsTargetPlatformVersion) -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded -DLLVM_TARGETS_TO_BUILD=X86 -DLLVM_DEFAULT_TARGET_TRIPLE:STRING=x86_64-pc-windows-msvc -DLLVM_HOST_TRIPLE:STRING=x86_64-pc-windows-msvc -DLLVM_BUILD_RUNTIME=OFF -DLLVM_BUILD_TOOLS=OFF -DLLVM_BUILD_BENCHMARKS=OFF -DLLVM_BUILD_DOCS=OFF -DLLVM_BUILD_EXAMPLES=OFF -DLLVM_BUILD_TESTS=OFF\ -DLLVM_INCLUDE_TOOLS=OFF -DLLVM_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_BENCHMARKS=OFF -DLLVM_INCLUDE_UTILS=OFF -DLLVM_INSTALL_UTILS=OFF -DLLVM_ENABLE_DIA_SDK=OFF -DLLVM_ENABLE_PDB=OFF -DLLVM_USE_INTEL_JITEVENTS=ON "$(SolutionDir)3rdparty\llvm\llvm\llvm"
|
||||
cmake -G Ninja ^
|
||||
-DCMAKE_CXX_COMPILER="clang-cl.exe" ^
|
||||
-DCMAKE_C_COMPILER="clang-cl.exe" ^
|
||||
-DCMAKE_BUILD_TYPE="Release" ^
|
||||
-DCMAKE_INSTALL_PREFIX="$(SolutionDir)build\lib\$(Configuration)-$(Platform)\llvm_build" ^
|
||||
-DCMAKE_SYSTEM_VERSION=10.0 ^
|
||||
-DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=$(WindowsTargetPlatformVersion) ^
|
||||
-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded ^
|
||||
-DLLVM_TARGETS_TO_BUILD=X86 ^
|
||||
-DLLVM_DEFAULT_TARGET_TRIPLE:STRING=x86_64-pc-windows-msvc ^
|
||||
-DLLVM_HOST_TRIPLE:STRING=x86_64-pc-windows-msvc ^
|
||||
-DLLVM_BUILD_RUNTIME=OFF ^
|
||||
-DLLVM_BUILD_TOOLS=OFF ^
|
||||
-DLLVM_BUILD_BENCHMARKS=OFF ^
|
||||
-DLLVM_BUILD_DOCS=OFF ^
|
||||
-DLLVM_BUILD_EXAMPLES=OFF ^
|
||||
-DLLVM_BUILD_TESTS=OFF ^
|
||||
-DLLVM_INCLUDE_TOOLS=OFF ^
|
||||
-DLLVM_INCLUDE_DOCS=OFF ^
|
||||
-DLLVM_INCLUDE_EXAMPLES=OFF ^
|
||||
-DLLVM_INCLUDE_TESTS=OFF ^
|
||||
-DLLVM_INCLUDE_BENCHMARKS=OFF ^
|
||||
-DLLVM_INCLUDE_UTILS=OFF ^
|
||||
-DLLVM_INSTALL_UTILS=OFF ^
|
||||
-DLLVM_ENABLE_DIA_SDK=OFF ^
|
||||
-DLLVM_ENABLE_PDB=OFF ^
|
||||
-DLLVM_USE_INTEL_JITEVENTS=ON ^
|
||||
"$(SolutionDir)3rdparty\llvm\llvm\llvm"
|
||||
</CmakeReleaseCLI>
|
||||
<CmakeDebugCLI>call vsdevcmd.bat -arch=amd64
|
||||
cd "$(SolutionDir)build\tmp\llvm_build-$(Configuration)-$(Platform)"
|
||||
cmake -G Ninja -DCMAKE_CXX_COMPILER="clang-cl.exe" -DCMAKE_C_COMPILER="clang-cl.exe" -DCMAKE_BUILD_TYPE="Debug" -DCMAKE_INSTALL_PREFIX="$(SolutionDir)build\lib\$(Configuration)-$(Platform)\llvm_build" -DCMAKE_SYSTEM_VERSION=10.0 -DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=$(WindowsTargetPlatformVersion) -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDebugDLL -DLLVM_TARGETS_TO_BUILD=X86 -DLLVM_DEFAULT_TARGET_TRIPLE:STRING=x86_64-pc-windows-msvc -DLLVM_HOST_TRIPLE:STRING=x86_64-pc-windows-msvc -DLLVM_BUILD_RUNTIME=OFF -DLLVM_BUILD_TOOLS=OFF -DLLVM_BUILD_BENCHMARKS=OFF -DLLVM_BUILD_DOCS=OFF -DLLVM_BUILD_EXAMPLES=OFF -DLLVM_BUILD_TESTS=OFF\ -DLLVM_INCLUDE_TOOLS=OFF -DLLVM_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_BENCHMARKS=OFF -DLLVM_INCLUDE_UTILS=OFF -DLLVM_INSTALL_UTILS=OFF -DLLVM_ENABLE_DIA_SDK=OFF -DLLVM_ENABLE_PDB=OFF -DLLVM_USE_INTEL_JITEVENTS=ON "$(SolutionDir)3rdparty\llvm\llvm\llvm"
|
||||
cmake -G Ninja ^
|
||||
-DCMAKE_CXX_COMPILER="clang-cl.exe" ^
|
||||
-DCMAKE_C_COMPILER="clang-cl.exe" ^
|
||||
-DCMAKE_BUILD_TYPE="Debug" ^
|
||||
-DCMAKE_INSTALL_PREFIX="$(SolutionDir)build\lib\$(Configuration)-$(Platform)\llvm_build" ^
|
||||
-DCMAKE_SYSTEM_VERSION=10.0 ^
|
||||
-DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=$(WindowsTargetPlatformVersion) ^
|
||||
-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDebugDLL ^
|
||||
-DLLVM_TARGETS_TO_BUILD=X86 ^
|
||||
-DLLVM_DEFAULT_TARGET_TRIPLE:STRING=x86_64-pc-windows-msvc ^
|
||||
-DLLVM_HOST_TRIPLE:STRING=x86_64-pc-windows-msvc ^
|
||||
-DLLVM_BUILD_RUNTIME=OFF ^
|
||||
-DLLVM_BUILD_TOOLS=OFF ^
|
||||
-DLLVM_BUILD_BENCHMARKS=OFF ^
|
||||
-DLLVM_BUILD_DOCS=OFF ^
|
||||
-DLLVM_BUILD_EXAMPLES=OFF ^
|
||||
-DLLVM_BUILD_TESTS=OFF ^
|
||||
-DLLVM_INCLUDE_TOOLS=OFF ^
|
||||
-DLLVM_INCLUDE_DOCS=OFF ^
|
||||
-DLLVM_INCLUDE_EXAMPLES=OFF ^
|
||||
-DLLVM_INCLUDE_TESTS=OFF ^
|
||||
-DLLVM_INCLUDE_BENCHMARKS=OFF ^
|
||||
-DLLVM_INCLUDE_UTILS=OFF ^
|
||||
-DLLVM_INSTALL_UTILS=OFF ^
|
||||
-DLLVM_ENABLE_DIA_SDK=OFF ^
|
||||
-DLLVM_ENABLE_PDB=OFF ^
|
||||
-DLLVM_USE_INTEL_JITEVENTS=ON ^
|
||||
"$(SolutionDir)3rdparty\llvm\llvm\llvm"
|
||||
</CmakeDebugCLI>
|
||||
<CmakeCleanCLI>
|
||||
echo Cleaning..
|
||||
|
||||
+44
-2
@@ -41,12 +41,54 @@
|
||||
<CmakeReleaseCLI>call vsdevcmd.bat -arch=amd64
|
||||
if not exist "$(SolutionDir)build\tmp\protobuf_build-$(Configuration)-$(Platform)" mkdir "$(SolutionDir)build\tmp\protobuf_build-$(Configuration)-$(Platform)"
|
||||
cd "$(SolutionDir)build\tmp\protobuf_build-$(Configuration)-$(Platform)"
|
||||
cmake -G Ninja -DCMAKE_CXX_COMPILER="cl.exe" -DCMAKE_C_COMPILER="cl.exe" -DCMAKE_CXX_STANDARD=20 -DCMAKE_BUILD_TYPE="Release" -DCMAKE_INSTALL_PREFIX="$(SolutionDir)build\lib\$(Configuration)-$(Platform)\protobuf_build" -DCMAKE_SYSTEM_VERSION=10.0 -DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=$(WindowsTargetPlatformVersion) -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded -Dprotobuf_INSTALL=ON -Dprotobuf_BUILD_TESTS=OFF -Dprotobuf_BUILD_CONFORMANCE=OFF -Dprotobuf_BUILD_EXAMPLES=OFF -Dprotobuf_BUILD_PROTOBUF_BINARIES=ON -Dprotobuf_BUILD_PROTOC_BINARIES=OFF -Dprotobuf_BUILD_LIBPROTOC=OFF -Dprotobuf_BUILD_LIBUPB=OFF -Dprotobuf_ALLOW_CCACHE=ON -Dprotobuf_FORCE_FETCH_DEPENDENCIES=ON -Dprotobuf_WITH_ZLIB=OFF -Dprotobuf_MSVC_STATIC_RUNTIME=ON "$(SolutionDir)3rdparty\protobuf\protobuf"
|
||||
cmake -G Ninja ^
|
||||
-DCMAKE_CXX_COMPILER="cl.exe" ^
|
||||
-DCMAKE_C_COMPILER="cl.exe" ^
|
||||
-DCMAKE_CXX_STANDARD=20 ^
|
||||
-DCMAKE_BUILD_TYPE="Release" ^
|
||||
-DCMAKE_INSTALL_PREFIX="$(SolutionDir)build\lib\$(Configuration)-$(Platform)\protobuf_build" ^
|
||||
-DCMAKE_SYSTEM_VERSION=10.0 ^
|
||||
-DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=$(WindowsTargetPlatformVersion) ^
|
||||
-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded ^
|
||||
-Dprotobuf_INSTALL=ON ^
|
||||
-Dprotobuf_BUILD_TESTS=OFF ^
|
||||
-Dprotobuf_BUILD_CONFORMANCE=OFF ^
|
||||
-Dprotobuf_BUILD_EXAMPLES=OFF ^
|
||||
-Dprotobuf_BUILD_PROTOBUF_BINARIES=ON ^
|
||||
-Dprotobuf_BUILD_PROTOC_BINARIES=OFF ^
|
||||
-Dprotobuf_BUILD_LIBPROTOC=OFF ^
|
||||
-Dprotobuf_BUILD_LIBUPB=OFF ^
|
||||
-Dprotobuf_ALLOW_CCACHE=ON ^
|
||||
-Dprotobuf_FORCE_FETCH_DEPENDENCIES=ON ^
|
||||
-Dprotobuf_WITH_ZLIB=OFF ^
|
||||
-Dprotobuf_MSVC_STATIC_RUNTIME=ON ^
|
||||
"$(SolutionDir)3rdparty\protobuf\protobuf"
|
||||
</CmakeReleaseCLI>
|
||||
<CmakeDebugCLI>call vsdevcmd.bat -arch=amd64
|
||||
if not exist "$(SolutionDir)build\tmp\protobuf_build-$(Configuration)-$(Platform)" mkdir "$(SolutionDir)build\tmp\protobuf_build-$(Configuration)-$(Platform)"
|
||||
cd "$(SolutionDir)build\tmp\protobuf_build-$(Configuration)-$(Platform)"
|
||||
cmake -G Ninja -DCMAKE_CXX_COMPILER="cl.exe" -DCMAKE_C_COMPILER="cl.exe" -DCMAKE_CXX_STANDARD=20 -DCMAKE_BUILD_TYPE="Debug" -DCMAKE_INSTALL_PREFIX="$(SolutionDir)build\lib\$(Configuration)-$(Platform)\protobuf_build" -DCMAKE_SYSTEM_VERSION=10.0 -DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=$(WindowsTargetPlatformVersion) -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDebugDLL -Dprotobuf_INSTALL=ON -Dprotobuf_BUILD_TESTS=OFF -Dprotobuf_BUILD_CONFORMANCE=OFF -Dprotobuf_BUILD_EXAMPLES=OFF -Dprotobuf_BUILD_PROTOBUF_BINARIES=ON -Dprotobuf_BUILD_PROTOC_BINARIES=OFF -Dprotobuf_BUILD_LIBPROTOC=OFF -Dprotobuf_BUILD_LIBUPB=OFF -Dprotobuf_ALLOW_CCACHE=ON -Dprotobuf_FORCE_FETCH_DEPENDENCIES=ON -Dprotobuf_WITH_ZLIB=OFF -Dprotobuf_MSVC_STATIC_RUNTIME=OFF "$(SolutionDir)3rdparty\protobuf\protobuf"
|
||||
cmake -G Ninja ^
|
||||
-DCMAKE_CXX_COMPILER="cl.exe" ^
|
||||
-DCMAKE_C_COMPILER="cl.exe" ^
|
||||
-DCMAKE_CXX_STANDARD=20 ^
|
||||
-DCMAKE_BUILD_TYPE="Debug" ^
|
||||
-DCMAKE_INSTALL_PREFIX="$(SolutionDir)build\lib\$(Configuration)-$(Platform)\protobuf_build" ^
|
||||
-DCMAKE_SYSTEM_VERSION=10.0 ^
|
||||
-DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=$(WindowsTargetPlatformVersion) ^
|
||||
-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDebugDLL ^
|
||||
-Dprotobuf_INSTALL=ON ^
|
||||
-Dprotobuf_BUILD_TESTS=OFF ^
|
||||
-Dprotobuf_BUILD_CONFORMANCE=OFF ^
|
||||
-Dprotobuf_BUILD_EXAMPLES=OFF ^
|
||||
-Dprotobuf_BUILD_PROTOBUF_BINARIES=ON ^
|
||||
-Dprotobuf_BUILD_PROTOC_BINARIES=OFF ^
|
||||
-Dprotobuf_BUILD_LIBPROTOC=OFF ^
|
||||
-Dprotobuf_BUILD_LIBUPB=OFF ^
|
||||
-Dprotobuf_ALLOW_CCACHE=ON ^
|
||||
-Dprotobuf_FORCE_FETCH_DEPENDENCIES=ON ^
|
||||
-Dprotobuf_WITH_ZLIB=OFF ^
|
||||
-Dprotobuf_MSVC_STATIC_RUNTIME=OFF ^
|
||||
"$(SolutionDir)3rdparty\protobuf\protobuf"
|
||||
</CmakeDebugCLI>
|
||||
<CmakeCleanCLI>
|
||||
echo Cleaning..
|
||||
|
||||
+1
-1
@@ -132,7 +132,7 @@ Start **Visual Studio**, click on `Open a project or solution` and select the `r
|
||||
**NOTE:** The recommended build configuration is `Release`. (On older revisions: `Release - LLVM`)
|
||||
|
||||
To speed up the compilation time, you may want to download and extract to `<rpcs3_root>\build\lib_ext\<$(Configuration)>-x64` (e.g. `c:\rpcs3\build\lib_ext\Release-x64`; the path needs to be created) the following precompiled lib:
|
||||
- [LLVM libs](https://github.com/RPCS3/llvm-mirror/releases/download/custom-build-win-19.1.7/llvmlibs_mt.7z)
|
||||
- [LLVM libs](https://github.com/RPCS3/llvm-mirror/releases/download/custom-build-win-22.1.8/llvmlibs_mt.7z)
|
||||
|
||||
**NOTES:**
|
||||
- `<$(Configuration)>` can assume values `Release` or `Debug`.
|
||||
|
||||
@@ -707,6 +707,11 @@ namespace cfg
|
||||
return *m_value.load().get();
|
||||
}
|
||||
|
||||
std::string get() const
|
||||
{
|
||||
return *m_value.load().get();
|
||||
}
|
||||
|
||||
std::string def_to_string() const override
|
||||
{
|
||||
return def;
|
||||
|
||||
@@ -26,6 +26,7 @@ std::string g_android_cache_dir;
|
||||
|
||||
#include <cwchar>
|
||||
#include <Windows.h>
|
||||
#include <winioctl.h>
|
||||
|
||||
static std::unique_ptr<wchar_t[]> to_wchar(std::string_view source)
|
||||
{
|
||||
@@ -1963,6 +1964,17 @@ fs::native_handle fs::file::get_handle() const
|
||||
#endif
|
||||
}
|
||||
|
||||
bool fs::set_sparse([[maybe_unused]] const fs::file& file)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
FILE_SET_SPARSE_BUFFER sparse{TRUE};
|
||||
DWORD returned = 0;
|
||||
return DeviceIoControl(file.get_handle(), FSCTL_SET_SPARSE, &sparse, sizeof(sparse), nullptr, 0, &returned, nullptr) != FALSE;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
fs::file_id fs::file::get_id() const
|
||||
{
|
||||
if (m_file)
|
||||
|
||||
@@ -496,6 +496,9 @@ namespace fs
|
||||
}
|
||||
};
|
||||
|
||||
// Enable sparse-file semantics when required by the host platform.
|
||||
bool set_sparse(const file& file);
|
||||
|
||||
class dir final
|
||||
{
|
||||
std::unique_ptr<dir_base> m_dir{};
|
||||
|
||||
@@ -707,11 +707,7 @@ jit_compiler::jit_compiler(const std::unordered_map<std::string, u64>& _link, st
|
||||
std::string result;
|
||||
|
||||
auto null_mod = std::make_unique<llvm::Module> ("null_", *m_context);
|
||||
#if LLVM_VERSION_MAJOR >= 21 && (LLVM_VERSION_MINOR >= 1 || LLVM_VERSION_MAJOR >= 22)
|
||||
null_mod->setTargetTriple(llvm::Triple(jit_compiler::triple1()));
|
||||
#else
|
||||
null_mod->setTargetTriple(jit_compiler::triple1());
|
||||
#endif
|
||||
|
||||
std::unique_ptr<llvm::RTDyldMemoryManager> mem;
|
||||
|
||||
@@ -725,11 +721,7 @@ jit_compiler::jit_compiler(const std::unordered_map<std::string, u64>& _link, st
|
||||
else
|
||||
{
|
||||
mem = std::make_unique<MemoryManager2>(std::move(symbols_cement));
|
||||
#if LLVM_VERSION_MAJOR >= 21 && (LLVM_VERSION_MINOR >= 1 || LLVM_VERSION_MAJOR >= 22)
|
||||
null_mod->setTargetTriple(llvm::Triple(jit_compiler::triple2()));
|
||||
#else
|
||||
null_mod->setTargetTriple(jit_compiler::triple2());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
+25
-3
@@ -161,7 +161,7 @@ namespace fmt
|
||||
std::string_view trim_back_sv(std::string_view source, std::string_view values = " \t");
|
||||
|
||||
template <typename T>
|
||||
std::string merge(const T& source, std::string_view separator)
|
||||
std::string merge(const T& source, std::string_view separator, bool is_skip_empty = false)
|
||||
{
|
||||
if (source.empty())
|
||||
{
|
||||
@@ -180,6 +180,23 @@ namespace fmt
|
||||
auto it = source.begin();
|
||||
auto end = source.end();
|
||||
|
||||
if (is_skip_empty)
|
||||
{
|
||||
for (; it != end; ++it)
|
||||
{
|
||||
if (it->empty()) continue;
|
||||
|
||||
if (!result.empty() && !separator.empty())
|
||||
{
|
||||
result.append(separator);
|
||||
}
|
||||
|
||||
result.append(*it);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
for (--end; it != end; ++it)
|
||||
{
|
||||
result.append(*it);
|
||||
@@ -192,7 +209,7 @@ namespace fmt
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::string merge(std::initializer_list<T> sources, std::string_view separator)
|
||||
std::string merge(std::initializer_list<T> sources, std::string_view separator, bool is_skip_empty = false)
|
||||
{
|
||||
if (!sources.size())
|
||||
{
|
||||
@@ -213,6 +230,11 @@ namespace fmt
|
||||
|
||||
for (const auto& v : sources)
|
||||
{
|
||||
if (v.empty()) continue;
|
||||
|
||||
std::string sub = fmt::merge(v, separator, is_skip_empty);
|
||||
if (sub.empty()) continue;
|
||||
|
||||
if (first)
|
||||
{
|
||||
first = false;
|
||||
@@ -222,7 +244,7 @@ namespace fmt
|
||||
result.append(separator);
|
||||
}
|
||||
|
||||
result.append(fmt::merge(v, separator));
|
||||
result.append(std::move(sub));
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
+403
-9
@@ -1250,20 +1250,296 @@ usz get_x64_access_size(x64_context* context, x64_op_t op, x64_reg_t reg, usz d_
|
||||
|
||||
#elif defined(ARCH_ARM64)
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#ifdef _WIN32
|
||||
#define RIP(context) (reinterpret_cast<CONTEXT*>((context))->Pc)
|
||||
#define GPR(context, index) (reinterpret_cast<CONTEXT*>((context))->X[index])
|
||||
#elif defined(__APPLE__)
|
||||
// https://github.com/bombela/backward-cpp/issues/200
|
||||
#define RIP(context) ((context)->uc_mcontext->__ss.__pc)
|
||||
#define GPR(context, index) ((context)->uc_mcontext->__ss.__x[(index)])
|
||||
#elif defined(__FreeBSD__)
|
||||
#define RIP(context) ((context)->uc_mcontext.mc_gpregs.gp_elr)
|
||||
#define GPR(context, index) ((context)->uc_mcontext.mc_gpregs.gp_x[(index)])
|
||||
#elif defined(__NetBSD__)
|
||||
#define RIP(context) ((context)->uc_mcontext.__gregs[_REG_PC])
|
||||
#define GPR(context, index) ((context)->uc_mcontext.__gregs[(index)])
|
||||
#elif defined(__OpenBSD__)
|
||||
#define RIP(context) ((context)->sc_elr)
|
||||
#define GPR(context, index) ((context)->sc_x[(index)])
|
||||
#else
|
||||
#define RIP(context) ((context)->uc_mcontext.pc)
|
||||
#define GPR(context, index) ((context)->uc_mcontext.regs[(index)])
|
||||
#endif
|
||||
|
||||
#endif /* ARCH_ */
|
||||
enum mem_a64_op_t
|
||||
{
|
||||
A64_INVALID = 0,
|
||||
A64_LOAD,
|
||||
A64_STORE,
|
||||
};
|
||||
|
||||
struct a64_mem_info_t
|
||||
{
|
||||
mem_a64_op_t op;
|
||||
u32 mem_size; // Bytes accessed in memory
|
||||
u32 reg_size; // Register width (4 or 8 bytes)
|
||||
u32 reg_num;
|
||||
bool reg_signed;
|
||||
};
|
||||
|
||||
a64_mem_info_t decode_a64_mem_inst(u32 inst)
|
||||
{
|
||||
a64_mem_info_t r{ A64_INVALID, 0, 0, inst % 32, false };
|
||||
|
||||
// Exclude SIMD/FP loads/stores
|
||||
if ((inst >> 26) & 1)
|
||||
{
|
||||
return r;
|
||||
}
|
||||
|
||||
// Scalar load/store immediate, unsigned offset variants only:
|
||||
// size[31:30]
|
||||
// V[26]
|
||||
// opc[23:22]
|
||||
// class bits[29:24] = 111001
|
||||
if ((inst & 0x3B000000) == 0x39000000)
|
||||
{
|
||||
const u32 size = (inst >> 30) & 3;
|
||||
const u32 opc = (inst >> 22) & 3;
|
||||
|
||||
r.mem_size = 1u << size;
|
||||
|
||||
switch (opc)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
// STR
|
||||
r.op = A64_STORE;
|
||||
r.reg_size = r.mem_size;
|
||||
return r;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
// LDR unsigned zero-extend
|
||||
// size=3 (64-bit) -> Xt; everything else -> Wt
|
||||
r.op = A64_LOAD;
|
||||
r.reg_size = (size == 3) ? 8u : 4u;
|
||||
r.reg_signed = false;
|
||||
return r;
|
||||
}
|
||||
case 2:
|
||||
case 3:
|
||||
{
|
||||
if (size == 3)
|
||||
{
|
||||
return r;
|
||||
}
|
||||
|
||||
if (size == 2 && opc == 3)
|
||||
{
|
||||
// Invalid LDRSW
|
||||
return r;
|
||||
}
|
||||
|
||||
// LDRSB/LDRSH/LDRSW
|
||||
// size determines extension type:
|
||||
// 00 LDRSB
|
||||
// 01 LDRSH
|
||||
// 10 LDRSW
|
||||
r.op = A64_LOAD;
|
||||
|
||||
if (size == 2)
|
||||
{
|
||||
// LDUSW
|
||||
r.reg_size = 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
// LDRSB/LDRSH
|
||||
// opc=2 -> Wt, opc=3 -> Xt
|
||||
r.reg_size = (opc == 3) ? 4 : 8;
|
||||
}
|
||||
|
||||
r.reg_signed = true;
|
||||
return r;
|
||||
}
|
||||
default:
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
// Scalar load/store unscaled immediate (LDUR/STUR)
|
||||
// size[31:30]
|
||||
// V[26]
|
||||
// opc[23:22]
|
||||
if ((inst & 0x3B200C00u) == 0x38000000u)
|
||||
{
|
||||
const u32 size = (inst >> 30) & 3;
|
||||
const u32 opc = (inst >> 22) & 3;
|
||||
|
||||
r.mem_size = 1u << size;
|
||||
|
||||
switch (opc)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
// STURB/STURH/STUR Wt/STUR Xt
|
||||
r.op = A64_STORE;
|
||||
|
||||
// Source register width
|
||||
r.reg_size = r.mem_size;
|
||||
return r;
|
||||
}
|
||||
|
||||
case 1:
|
||||
{
|
||||
// LDURB/LDURH/LDUR Wt/LDUR Xt
|
||||
r.op = A64_LOAD;
|
||||
|
||||
// Destination register width
|
||||
r.reg_size = (size == 3) ? 8 : 4;
|
||||
r.reg_signed = false;
|
||||
return r;
|
||||
}
|
||||
|
||||
case 2:
|
||||
case 3:
|
||||
{
|
||||
// LDURSB/LDURSH/LDURSW
|
||||
if (size == 3)
|
||||
{
|
||||
return r;
|
||||
}
|
||||
|
||||
r.op = A64_LOAD;
|
||||
r.reg_signed = true;
|
||||
|
||||
if (size == 2)
|
||||
{
|
||||
// LDURSW
|
||||
r.reg_size = 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
// LDURSB/LDURSH
|
||||
// opc=2 -> Wt, opc=3 -> Xt
|
||||
r.reg_size = (opc == 3) ? 4 : 8;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
default:
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Literal loads:
|
||||
//
|
||||
// LDR Wt, label
|
||||
// LDR Xt, label
|
||||
// LDRSW Xt, label
|
||||
//
|
||||
|
||||
// This is not needed for MMIO (which is the only use for this function)
|
||||
|
||||
// if ((inst & 0x3B000000) == 0x18000000)
|
||||
// {
|
||||
// u32 opc = (inst >> 30) & 3;
|
||||
|
||||
// r.op = A64_LOAD;
|
||||
|
||||
// switch (opc)
|
||||
// {
|
||||
// case 0: // LDR Wt literal
|
||||
// {
|
||||
// r.mem_size = 4;
|
||||
// r.reg_size = 4;
|
||||
// return r;
|
||||
// }
|
||||
// case 1: // LDR Xt literal
|
||||
// {
|
||||
// r.mem_size = 8;
|
||||
// r.reg_size = 8;
|
||||
// return r;
|
||||
// }
|
||||
// case 2: // LDRSW literal
|
||||
// {
|
||||
// r.mem_size = 4;
|
||||
// r.reg_size = 8;
|
||||
// r.reg_signed = true;
|
||||
// return r;
|
||||
// }
|
||||
// default:
|
||||
// {
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void put_a64_reg_value(ucontext_t* context, u32 reg_index, u32 reg_size, bool reg_signed, u32 mem_size, u64 value)
|
||||
{
|
||||
ensure(mem_size == 1 || mem_size == 2 || mem_size == 4 || mem_size == 8);
|
||||
ensure(reg_size == 1 || reg_size == 2 || reg_size == 4 || reg_size == 8);
|
||||
ensure(reg_size >= mem_size);
|
||||
ensure(reg_index < 32);
|
||||
|
||||
if (reg_index == 31)
|
||||
{
|
||||
// XZR "register"
|
||||
ensure(false);
|
||||
}
|
||||
|
||||
auto make_mask = [](u32 bytes) -> u64
|
||||
{
|
||||
if (bytes == 8)
|
||||
{
|
||||
return umax;
|
||||
}
|
||||
|
||||
const u64 bits = bytes * 8;
|
||||
return (u64{1} << bits) - 1;
|
||||
};
|
||||
|
||||
// Mask for sign-extending the value
|
||||
const u64 sign_bit = value & (make_mask(mem_size) / 2 + 1);
|
||||
const u64 sign_mask = (reg_signed && sign_bit != 0 && reg_size > mem_size) ? (make_mask(reg_size) & ~make_mask(mem_size)) : 0;
|
||||
|
||||
u64 temp_reg_value = 0;
|
||||
temp_reg_value |= (value & make_mask(mem_size)); // Set value (adjusted by size)
|
||||
temp_reg_value |= sign_mask; // Apply sign-extension
|
||||
GPR(context, reg_index) = temp_reg_value;
|
||||
}
|
||||
|
||||
u64 get_a64_reg_value(ucontext_t* context, u32 reg_index, u32 reg_size)
|
||||
{
|
||||
ensure(reg_size == 1 || reg_size == 2 || reg_size == 4 || reg_size == 8);
|
||||
ensure(reg_index < 32);
|
||||
|
||||
if (reg_index == 31)
|
||||
{
|
||||
// XZR "register"
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto make_mask = [](u32 bytes) -> u64
|
||||
{
|
||||
if (bytes == 8)
|
||||
{
|
||||
return umax;
|
||||
}
|
||||
|
||||
const u64 bits = bytes * 8;
|
||||
return (u64{1} << bits) - 1;
|
||||
};
|
||||
|
||||
return (GPR(context, reg_index) & make_mask(reg_size));
|
||||
}
|
||||
|
||||
#endif /* ARCH_ARM64 */
|
||||
|
||||
namespace rsx
|
||||
{
|
||||
@@ -1478,11 +1754,11 @@ bool handle_access_violation(u32 addr, bool is_writing, bool is_exec, ucontext_t
|
||||
|
||||
if (a_size == 4)
|
||||
{
|
||||
value = stx::se_storage<u32>::swap(value);
|
||||
value = std::bit_cast<be_t<u32>>(value);
|
||||
}
|
||||
else if (a_size == 2)
|
||||
{
|
||||
value = stx::se_storage<u16>::swap(value);
|
||||
value = std::bit_cast<be_t<u16>>(static_cast<u16>(value));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1556,8 +1832,126 @@ bool handle_access_violation(u32 addr, bool is_writing, bool is_exec, ucontext_t
|
||||
g_tls_fault_spu++;
|
||||
return true;
|
||||
} while (0);
|
||||
#else
|
||||
static_cast<void>(context);
|
||||
#elif defined(ARCH_ARM64)
|
||||
const u8* const code = reinterpret_cast<u8*>(RIP(context));
|
||||
|
||||
const u32 instruction = read_from_ptr_unsafe<u32>(code);
|
||||
|
||||
const auto [op, mem_size, reg_size, reg_index, reg_signed] = decode_a64_mem_inst(instruction);
|
||||
|
||||
auto report_opcode = [&]()
|
||||
{
|
||||
sig_log.error("decode_a64_mem_inst(%p): unsupported opcode: %s", code, +std::bit_cast<be_t<u32>>(instruction));
|
||||
};
|
||||
|
||||
if (0x1'0000'0000ull - addr < mem_size)
|
||||
{
|
||||
sig_log.error("Invalid mem_size (0x%llx)", mem_size);
|
||||
report_opcode();
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if address is RawSPU MMIO register
|
||||
do if (addr - RAW_SPU_BASE_ADDR < (6 * RAW_SPU_OFFSET) && (addr % RAW_SPU_OFFSET) >= RAW_SPU_PROB_OFFSET)
|
||||
{
|
||||
auto thread = idm::get_unlocked<named_thread<spu_thread>>(spu_thread::find_raw_spu((addr - RAW_SPU_BASE_ADDR) / RAW_SPU_OFFSET));
|
||||
|
||||
if (!thread || is_exec)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (!mem_size)
|
||||
{
|
||||
sig_log.error("Invalid or unsupported instruction (reg=%d, mem_size=%lld, reg_size=0x%llx)", reg_index, mem_size, reg_size);
|
||||
report_opcode();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool handled = true;
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case A64_LOAD:
|
||||
{
|
||||
u32 value;
|
||||
const u32 addr_aligned = addr & -4;
|
||||
|
||||
if (addr % 4 + mem_size > 4)
|
||||
{
|
||||
handled = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (is_writing || !thread->read_reg(addr_aligned, value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Adjust value for 8-bit and 16-bit reads
|
||||
value >>= ((4 - mem_size) * 8) - ((addr % 4) * 8);
|
||||
value &= mem_size == 4 ? u32{umax} : ((1u << (mem_size * 8)) - 1);
|
||||
|
||||
if (mem_size == 4)
|
||||
{
|
||||
value = std::bit_cast<be_t<u32>>(value);
|
||||
}
|
||||
else if (mem_size == 2)
|
||||
{
|
||||
value = std::bit_cast<be_t<u16>>(static_cast<u16>(value));
|
||||
}
|
||||
else
|
||||
{
|
||||
ensure(mem_size == 1);
|
||||
}
|
||||
|
||||
// Update register value
|
||||
put_a64_reg_value(context, reg_index, reg_size, reg_signed, mem_size, value);
|
||||
break;
|
||||
}
|
||||
case A64_STORE:
|
||||
{
|
||||
if (mem_size != 4)
|
||||
{
|
||||
// Might be unimplemented, such as writing MFC proxy EAL+EAH using 64-bit store
|
||||
handled = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!is_writing)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const u64 reg_value = get_a64_reg_value(context, reg_index, reg_size);
|
||||
const u32 val32 = static_cast<u32>(reg_value);
|
||||
if (!thread->write_reg(addr, std::bit_cast<be_t<u32>>(val32)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
sig_log.error("Invalid or unsupported operation (reg=%d, mem_size=%lld, reg_size=0x%llx)", reg_index, mem_size, reg_size);
|
||||
report_opcode();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!handled)
|
||||
{
|
||||
sig_log.error("Invalid or unsupported operation (reg=%d, mem_size=%lld, reg_size=0x%llx)", reg_index, mem_size, reg_size);
|
||||
report_opcode();
|
||||
break;
|
||||
}
|
||||
|
||||
// skip processed instruction
|
||||
RIP(context) = reinterpret_cast<std::remove_cvref_t<decltype(RIP(context))>>(reinterpret_cast<const char*>(RIP(context)) + 4);
|
||||
g_tls_fault_spu++;
|
||||
return true;
|
||||
} while (0);
|
||||
#endif /* ARCH_ */
|
||||
|
||||
const auto required_page_perms = (is_writing ? vm::page_writable : vm::page_readable) + (is_exec ? vm::page_executable : 0);
|
||||
@@ -2133,7 +2527,7 @@ static void signal_handler(int /*sig*/, siginfo_t* info, void* uct) noexcept
|
||||
}
|
||||
|
||||
#else
|
||||
const u32 insn = is_executing ? 0 : *reinterpret_cast<u32*>(RIP(context));
|
||||
const u32 insn = is_executing ? 0 : read_from_ptr_unsafe<u32>(RIP(context));
|
||||
const bool is_writing =
|
||||
(insn & 0xbfff0000) == 0x0c000000 || // STR <Wt>, [<Xn>, #<imm>] (store word with immediate offset)
|
||||
(insn & 0xbfe00000) == 0x0c800000 || // STP <Wt1>, <Wt2>, [<Xn>, #<imm>] (store pair of registers with immediate offset)
|
||||
@@ -2339,8 +2733,8 @@ void thread_base::start()
|
||||
pthread_attr_t attrs;
|
||||
pthread_t thread_id{};
|
||||
struct sched_param sp;
|
||||
memset(&sp, 0, sizeof(struct sched_param));
|
||||
sp.sched_priority=99;
|
||||
memset(&sp, 0, sizeof(struct sched_param));
|
||||
sp.sched_priority=99;
|
||||
pthread_attr_init(&attrs);
|
||||
pthread_attr_setstacksize(&attrs, 0x800000);
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
avutil.lib;
|
||||
swresample.lib;
|
||||
swscale.lib;
|
||||
LLVMABI.lib;
|
||||
LLVMAggressiveInstCombine.lib;
|
||||
LLVMAnalysis.lib;
|
||||
LLVMAsmParser.lib;
|
||||
@@ -44,9 +45,10 @@
|
||||
LLVMBitReader.lib;
|
||||
LLVMBitstreamReader.lib;
|
||||
LLVMBitWriter.lib;
|
||||
LLVMCAS.lib;
|
||||
LLVMCFGuard.lib;
|
||||
LLVMCGData.lib;
|
||||
LLVMCodeGen.lib;
|
||||
LLVMCodeGenData.lib;
|
||||
LLVMCodeGenTypes.lib;
|
||||
LLVMCore.lib;
|
||||
LLVMCoroutines.lib;
|
||||
@@ -55,12 +57,15 @@
|
||||
LLVMDebugInfoCodeView.lib;
|
||||
LLVMDebuginfod.lib;
|
||||
LLVMDebugInfoDWARF.lib;
|
||||
LLVMDebugInfoDWARFLowLevel.lib;
|
||||
LLVMDebugInfoGSYM.lib;
|
||||
LLVMDebugInfoLogicalView.lib;
|
||||
LLVMDebugInfoMSF.lib;
|
||||
LLVMDebugInfoPDB.lib;
|
||||
LLVMDemangle.lib;
|
||||
LLVMDlltoolDriver.lib;
|
||||
LLVMDTLTO.lib;
|
||||
LLVMDWARFCFIChecker.lib;
|
||||
LLVMDWARFLinker.lib;
|
||||
LLVMDWARFLinkerClassic.lib;
|
||||
LLVMDWARFLinkerParallel.lib;
|
||||
@@ -68,6 +73,8 @@
|
||||
LLVMExecutionEngine.lib;
|
||||
LLVMExtensions.lib;
|
||||
LLVMFileCheck.lib;
|
||||
LLVMFrontendAtomic.lib;
|
||||
LLVMFrontendDirective.lib;
|
||||
LLVMFrontendDriver.lib;
|
||||
LLVMFrontendHLSL.lib;
|
||||
LLVMFrontendOffloading.lib;
|
||||
@@ -107,6 +114,7 @@
|
||||
LLVMOrcShared.lib;
|
||||
LLVMOrcTargetProcess.lib;
|
||||
LLVMPasses.lib;
|
||||
LLVMPlugins.lib;
|
||||
LLVMProfileData.lib;
|
||||
LLVMRemarks.lib;
|
||||
LLVMRuntimeDyld.lib;
|
||||
@@ -114,12 +122,14 @@
|
||||
LLVMScalarOpts.lib;
|
||||
LLVMSelectionDAG.lib;
|
||||
LLVMSupport.lib;
|
||||
LLVMSupportLSP.lib;
|
||||
LLVMSymbolize.lib;
|
||||
LLVMTableGen.lib;
|
||||
LLVMTableGenBasic.lib;
|
||||
LLVMTableGenCommon.lib;
|
||||
LLVMTarget.lib;
|
||||
LLVMTargetParser.lib;
|
||||
LLVMTelemetry.lib;
|
||||
LLVMTextAPI.lib;
|
||||
LLVMTextAPIBinaryReader.lib;
|
||||
LLVMTransformUtils.lib;
|
||||
|
||||
+48
-4
@@ -255,7 +255,27 @@ bool package_reader::read_metadata()
|
||||
if (packet.size == sizeof(m_metadata.package_type))
|
||||
{
|
||||
archive_read(&m_metadata.package_type, sizeof(m_metadata.package_type));
|
||||
|
||||
std::vector<std::string> package_flags;
|
||||
if (m_metadata.package_type & pkg_flag::PKG_FLAG_0x01) package_flags.push_back("0x01");
|
||||
if (m_metadata.package_type & pkg_flag::PKG_FLAG_EBOOT) package_flags.push_back("EBOOT");
|
||||
if (m_metadata.package_type & pkg_flag::PKG_FLAG_REQUIRE_LICENSE) package_flags.push_back("REQUIRE_LICENSE");
|
||||
if (m_metadata.package_type & pkg_flag::PKG_FLAG_HDD_MC) package_flags.push_back("HDD_MC");
|
||||
if (m_metadata.package_type & pkg_flag::PKG_FLAG_PATCH) package_flags.push_back("PATCH");
|
||||
if (m_metadata.package_type & pkg_flag::PKG_FLAG_0x20) package_flags.push_back("0x20");
|
||||
if (m_metadata.package_type & pkg_flag::PKG_FLAG_RENAME_DIRECTORY) package_flags.push_back("RENAME_DIRECTORY");
|
||||
if (m_metadata.package_type & pkg_flag::PKG_FLAG_EDAT) package_flags.push_back("EDAT");
|
||||
if (m_metadata.package_type & pkg_flag::PKG_FLAG_0x100) package_flags.push_back("0x100");
|
||||
if (m_metadata.package_type & pkg_flag::PKG_FLAG_EMULATOR) package_flags.push_back("EMULATOR");
|
||||
if (m_metadata.package_type & pkg_flag::PKG_FLAG_VSH_MODULE) package_flags.push_back("VSH_MODULE");
|
||||
if (m_metadata.package_type & pkg_flag::PKG_FLAG_DISC_BOUND) package_flags.push_back("DISC_BOUND");
|
||||
if (m_metadata.package_type & pkg_flag::PKG_FLAG_UNKNOWN) package_flags.push_back("UNKNOWN");
|
||||
if (m_metadata.package_type & pkg_flag::PKG_FLAG_PS_VITA_CARD) package_flags.push_back("PS_VITA_CARD");
|
||||
if (m_metadata.package_type & pkg_flag::PKG_FLAG_PS_VITA_NON_GAME) package_flags.push_back("PS_VITA_NON_GAME");
|
||||
if (m_metadata.package_type & pkg_flag::PKG_FLAG_0x8000) package_flags.push_back("0x8000");
|
||||
|
||||
pkg_log.notice("Metadata: Package Type = 0x%x = %d", m_metadata.package_type, m_metadata.package_type);
|
||||
pkg_log.notice("Metadata: Package Flags = %s", package_flags.empty() ? "{}" : fmt::merge(package_flags, ", "));
|
||||
continue;
|
||||
}
|
||||
else
|
||||
@@ -653,7 +673,6 @@ bool package_reader::read_param_sfo()
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: maybe also check if VERSION matches
|
||||
package_install_result package_reader::check_target_app_version() const
|
||||
{
|
||||
if (!m_is_valid)
|
||||
@@ -698,7 +717,13 @@ package_install_result package_reader::check_target_app_version() const
|
||||
{
|
||||
// We are unable to compare anything with the target app version
|
||||
pkg_log.error("A target app version is required (%s), but no PARAM.SFO was found for %s. (path='%s', error=%s)", target_app_ver, title_id, sfo_path, fs::g_tls_error);
|
||||
return {package_install_result::error_type::app_version, {std::string(target_app_ver)}};
|
||||
return {
|
||||
.error = package_install_result::error_type::app_version,
|
||||
.version = {
|
||||
.app_ver = std::string(app_ver),
|
||||
.expected = std::string(target_app_ver)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// There is nothing we need to compare, so we may install the package
|
||||
@@ -727,6 +752,12 @@ package_install_result package_reader::check_target_app_version() const
|
||||
|
||||
if (target_app_ver.empty())
|
||||
{
|
||||
if (!(m_metadata.package_type & pkg_flag::PKG_FLAG_PATCH))
|
||||
{
|
||||
// This should be a DLC. Let's allow DLCs even with smaller APP_VER.
|
||||
return {package_install_result::error_type::no_error};
|
||||
}
|
||||
|
||||
// This is most likely the first patch. Let's make sure its version is high enough for the installed game.
|
||||
|
||||
const double new_version = std::strtod(app_ver.data(), &ev1);
|
||||
@@ -744,7 +775,13 @@ package_install_result package_reader::check_target_app_version() const
|
||||
}
|
||||
|
||||
pkg_log.error("The new app version (%s) is smaller than the installed app version (%s)", app_ver, installed_app_ver);
|
||||
return {package_install_result::error_type::app_version, {std::string(app_ver), std::string(installed_app_ver)}};
|
||||
return {
|
||||
.error = package_install_result::error_type::app_version,
|
||||
.version = {
|
||||
.app_ver = std::string(app_ver),
|
||||
.installed = std::string(installed_app_ver)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Check if the installed app version matches the target app version
|
||||
@@ -764,7 +801,14 @@ package_install_result package_reader::check_target_app_version() const
|
||||
}
|
||||
|
||||
pkg_log.error("The installed app version (%s) does not match the target app version (%s)", installed_app_ver, target_app_ver);
|
||||
return {package_install_result::error_type::app_version, {std::string(target_app_ver), std::string(installed_app_ver)}};
|
||||
return {
|
||||
.error = package_install_result::error_type::app_version,
|
||||
.version = {
|
||||
.app_ver = std::string(app_ver),
|
||||
.expected = std::string(target_app_ver),
|
||||
.installed = std::string(installed_app_ver)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
bool package_reader::set_install_path()
|
||||
|
||||
+34
-11
@@ -42,7 +42,7 @@ enum : u32
|
||||
PKG_FILE_ENTRY_KNOWN_BITS = 0xff | PKG_FILE_ENTRY_PSP | PKG_FILE_ENTRY_OVERWRITE,
|
||||
};
|
||||
|
||||
enum : u32
|
||||
enum pkg_content_type : u32
|
||||
{
|
||||
PKG_CONTENT_TYPE_UNKNOWN_1 = 0x01, // ?
|
||||
PKG_CONTENT_TYPE_UNKNOWN_2 = 0x02, // ?
|
||||
@@ -63,18 +63,39 @@ enum : u32
|
||||
PKG_CONTENT_TYPE_VMC = 0x11, // VMC
|
||||
PKG_CONTENT_TYPE_PS2_CLASSIC = 0x12, // ?PS2Classic? Seen on PS2 classic
|
||||
PKG_CONTENT_TYPE_UNKNOWN_5 = 0x13, // ?
|
||||
PKG_CONTENT_TYPE_PSP_REMASTERED = 0x14, // ?
|
||||
PKG_CONTENT_TYPE_PSP_REMASTERED = 0x14, // PSP Remastered
|
||||
PKG_CONTENT_TYPE_PSP2_GD = 0x15, // PSVita Game Data
|
||||
PKG_CONTENT_TYPE_PSP2_AC = 0x16, // PSVita Additional Content
|
||||
PKG_CONTENT_TYPE_PSP2_LA = 0x17, // PSVita LiveArea
|
||||
PKG_CONTENT_TYPE_PSM_1 = 0x18, // PSVita PSM ?
|
||||
PKG_CONTENT_TYPE_PSM = 0x18, // PSVita PSM
|
||||
PKG_CONTENT_TYPE_WT = 0x19, // Web TV ?
|
||||
PKG_CONTENT_TYPE_UNKNOWN_6 = 0x1A, // ?
|
||||
PKG_CONTENT_TYPE_UNKNOWN_7 = 0x1B, // ?
|
||||
PKG_CONTENT_TYPE_UNKNOWN_8 = 0x1C, // ?
|
||||
PKG_CONTENT_TYPE_PSM_2 = 0x1D, // PSVita PSM ?
|
||||
PKG_CONTENT_TYPE_UNKNOWN_9 = 0x1E, // ?
|
||||
PKG_CONTENT_TYPE_PS4_GD = 0x1A, // PS4 GameData
|
||||
PKG_CONTENT_TYPE_PS4_AC = 0x1B, // PS4 Additional Content
|
||||
PKG_CONTENT_TYPE_PS4_AL = 0x1C, // PS4 Additional License
|
||||
PKG_CONTENT_TYPE_PSM_UNITY = 0x1D, // PSVita PSM for Unity
|
||||
PKG_CONTENT_TYPE_PS4_DP = 0x1E, // PS4 Delta patch
|
||||
PKG_CONTENT_TYPE_PSP2_THEME = 0x1F, // PSVita Theme
|
||||
PKG_CONTENT_TYPE_PS5_GAME_DATA = 0x20, // PS5 GameData
|
||||
};
|
||||
|
||||
enum pkg_flag : u32
|
||||
{
|
||||
PKG_FLAG_0x01 = 0x01,
|
||||
PKG_FLAG_EBOOT = 0x02,
|
||||
PKG_FLAG_REQUIRE_LICENSE = 0x04,
|
||||
PKG_FLAG_HDD_MC = 0x08,
|
||||
PKG_FLAG_PATCH = 0x10,
|
||||
PKG_FLAG_0x20 = 0x20,
|
||||
PKG_FLAG_RENAME_DIRECTORY = 0x40,
|
||||
PKG_FLAG_EDAT = 0x80,
|
||||
PKG_FLAG_0x100 = 0x100,
|
||||
PKG_FLAG_EMULATOR = 0x200,
|
||||
PKG_FLAG_VSH_MODULE = 0x400,
|
||||
PKG_FLAG_DISC_BOUND = 0x800,
|
||||
PKG_FLAG_UNKNOWN = 0x1000,
|
||||
PKG_FLAG_PS_VITA_CARD = 0x2000,
|
||||
PKG_FLAG_PS_VITA_NON_GAME = 0x4000,
|
||||
PKG_FLAG_0x8000 = 0x8000,
|
||||
};
|
||||
|
||||
// Structs
|
||||
@@ -305,10 +326,11 @@ struct package_install_result
|
||||
app_version,
|
||||
other
|
||||
} error = error_type::no_error;
|
||||
struct version
|
||||
struct versions
|
||||
{
|
||||
std::string app_ver;
|
||||
std::string expected;
|
||||
std::string found;
|
||||
std::string installed;
|
||||
} version;
|
||||
};
|
||||
|
||||
@@ -348,6 +370,7 @@ public:
|
||||
|
||||
bool is_valid() const { return m_is_valid; }
|
||||
const PKGHeader& get_header() const { return m_header; }
|
||||
const PKGMetaData& get_metadata() const { return m_metadata; }
|
||||
package_install_result check_target_app_version() const;
|
||||
static package_install_result extract_data(std::deque<package_reader>& readers, std::deque<std::string>& bootable_paths);
|
||||
const psf::registry& get_psf() const { return m_psf; }
|
||||
@@ -357,7 +380,7 @@ public:
|
||||
|
||||
void abort_extract();
|
||||
|
||||
fs::file &file()
|
||||
fs::file& file()
|
||||
{
|
||||
return m_file;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,17 @@ if(USE_ASAN)
|
||||
set_source_files_properties(../../Utilities/Thread.cpp PROPERTIES COMPILE_DEFINITIONS USE_ASAN)
|
||||
endif()
|
||||
|
||||
# GCC overemits weak type information when using LTO
|
||||
# This causes an issue with LLVM 22 which is build without RTTI
|
||||
# Issue is specifically related to vfs::ProxyFileSystem
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
set_source_files_properties(
|
||||
Cell/PPUThread.cpp
|
||||
Cell/SPULLVMRecompiler.cpp
|
||||
PROPERTIES COMPILE_OPTIONS -fno-lto
|
||||
)
|
||||
endif()
|
||||
|
||||
if(NOT USE_SYSTEM_OPENAL)
|
||||
target_compile_definitions(rpcs3_emu PUBLIC AL_LIBTYPE_STATIC)
|
||||
endif()
|
||||
|
||||
+133
-12
@@ -71,11 +71,7 @@ cpu_translator::cpu_translator(llvm::Module* _module, bool is_be)
|
||||
result = m_ir->CreateInsertElement(v, m_ir->CreateExtractElement(data0, m_ir->CreateExtractElement(mask, i)), i);
|
||||
v->addIncoming(result, loop);
|
||||
m_ir->CreateCondBr(m_ir->CreateICmpULT(i, m_ir->getInt32(16)), loop, next);
|
||||
#if LLVM_VERSION_MAJOR >= 21 || (LLVM_VERSION_MAJOR == 20 && LLVM_VERSION_MINOR >= 1)
|
||||
m_ir->SetInsertPoint(next->getFirstNonPHIIt());
|
||||
#else
|
||||
m_ir->SetInsertPoint(next->getFirstNonPHI());
|
||||
#endif
|
||||
result = m_ir->CreateSelect(m_ir->CreateICmpSLT(index, zeros), zeros, result);
|
||||
|
||||
return result;
|
||||
@@ -260,9 +256,7 @@ llvm::Value* cpu_translator::bitcast(llvm::Value* val, llvm::Type* type, std::so
|
||||
}
|
||||
|
||||
// Skip use iteration for values that don't have use lists
|
||||
#if LLVM_VERSION_MAJOR >= 21
|
||||
if (source_val->hasUseList())
|
||||
#endif
|
||||
{
|
||||
for (llvm::Value* it_val : source_val->uses())
|
||||
{
|
||||
@@ -287,12 +281,10 @@ llvm::Value* cpu_translator::bitcast(llvm::Value* val, llvm::Type* type, std::so
|
||||
}
|
||||
|
||||
// Check if bci has use list before accessing use_begin()
|
||||
#if LLVM_VERSION_MAJOR >= 21
|
||||
if (!bci->hasUseList())
|
||||
{
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (bci->use_begin() == bci->use_end())
|
||||
{
|
||||
@@ -317,6 +309,9 @@ llvm::Value* cpu_translator::bitcast(llvm::Value* val, llvm::Type* type, std::so
|
||||
template <>
|
||||
std::pair<bool, v128> cpu_translator::get_const_vector<v128>(llvm::Value* c, u32 _pos, u32 _line)
|
||||
{
|
||||
// Bitcasts do not matter
|
||||
c = peek_through_bitcasts(c);
|
||||
|
||||
v128 result{};
|
||||
|
||||
if (!llvm::isa<llvm::Constant>(c))
|
||||
@@ -581,10 +576,8 @@ void cpu_translator::erase_stores(llvm::ArrayRef<llvm::Value*> args)
|
||||
for (auto v : args)
|
||||
{
|
||||
// Skip use iteration for values that don't have use lists
|
||||
#if LLVM_VERSION_MAJOR >= 21
|
||||
if (!v->hasUseList())
|
||||
continue;
|
||||
#endif
|
||||
|
||||
for (llvm::Value* i : v->uses())
|
||||
{
|
||||
@@ -594,10 +587,8 @@ void cpu_translator::erase_stores(llvm::ArrayRef<llvm::Value*> args)
|
||||
while (i && (bci = llvm::dyn_cast<llvm::CastInst>(i)) && bci->getOpcode() == llvm::Instruction::BitCast)
|
||||
{
|
||||
// Check if bci has use list before accessing use_begin()
|
||||
#if LLVM_VERSION_MAJOR >= 21
|
||||
if (!bci->hasUseList())
|
||||
break;
|
||||
#endif
|
||||
|
||||
i = *bci->use_begin();
|
||||
}
|
||||
@@ -610,4 +601,134 @@ void cpu_translator::erase_stores(llvm::ArrayRef<llvm::Value*> args)
|
||||
}
|
||||
}
|
||||
|
||||
llvm::KnownBits cpu_translator::get_known_bits_fallback(llvm::Value* value)
|
||||
{
|
||||
// TODO: Improve it - add support for integer addition/subtraction and more stuff
|
||||
|
||||
const auto type = value->getType();
|
||||
|
||||
if (!type->isVectorTy())
|
||||
{
|
||||
if (llvm::isa<llvm::IntegerType>(type))
|
||||
{
|
||||
if (auto bin_inst = llvm::dyn_cast<llvm::BinaryOperator>(value))
|
||||
{
|
||||
llvm::Value* lhs = ensure(bin_inst->getOperand(0));
|
||||
llvm::Value* rhs = ensure(bin_inst->getOperand(1));
|
||||
|
||||
llvm::ConstantInt* constant_value = llvm::dyn_cast<llvm::ConstantInt>(rhs) ? llvm::dyn_cast<llvm::ConstantInt>(rhs) : llvm::dyn_cast<llvm::ConstantInt>(lhs);
|
||||
|
||||
if (!constant_value)
|
||||
{
|
||||
return llvm::KnownBits(type->getScalarSizeInBits());
|
||||
}
|
||||
|
||||
if (bin_inst->getOpcode() == llvm::Instruction::Or)
|
||||
{
|
||||
llvm::KnownBits ret(type->getScalarSizeInBits());
|
||||
ret.One = constant_value->getValue();
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (bin_inst->getOpcode() == llvm::Instruction::And)
|
||||
{
|
||||
llvm::KnownBits ret(type->getScalarSizeInBits());
|
||||
ret.Zero = constant_value->getValue();
|
||||
ret.Zero.flipAllBits();
|
||||
return ret;
|
||||
}
|
||||
|
||||
return llvm::KnownBits(type->getScalarSizeInBits());
|
||||
}
|
||||
}
|
||||
|
||||
fmt::throw_exception("Bad KnownBits type: i%ux", type->getScalarSizeInBits());
|
||||
}
|
||||
|
||||
if (auto v = llvm::cast<llvm::FixedVectorType>(type); v->getScalarSizeInBits() * v->getNumElements() != 128)
|
||||
{
|
||||
// Unsupported
|
||||
return llvm::KnownBits(type->getScalarSizeInBits());
|
||||
}
|
||||
|
||||
const auto original_value = peek_through_bitcasts(value);
|
||||
|
||||
auto bin_inst = llvm::dyn_cast<llvm::BinaryOperator>(original_value);
|
||||
|
||||
if (!bin_inst)
|
||||
{
|
||||
return llvm::KnownBits(type->getScalarSizeInBits());
|
||||
}
|
||||
|
||||
llvm::Value* lhs = ensure(bin_inst->getOperand(0));
|
||||
llvm::Value* rhs = ensure(bin_inst->getOperand(1));
|
||||
|
||||
llvm::Value* constant_value = llvm::dyn_cast<llvm::ConstantDataVector>(rhs) ? llvm::dyn_cast<llvm::ConstantDataVector>(rhs) : llvm::dyn_cast<llvm::ConstantDataVector>(lhs);
|
||||
|
||||
if (!constant_value)
|
||||
{
|
||||
return llvm::KnownBits(value->getType()->getScalarSizeInBits());
|
||||
}
|
||||
|
||||
const auto [ok, v128_const] = get_const_vector(constant_value, -1);
|
||||
|
||||
ensure(ok);
|
||||
|
||||
llvm::APInt all_lanes{};
|
||||
llvm::APInt any_lanes{};
|
||||
|
||||
auto combine_bits = [&](const auto& array, u32 size)
|
||||
{
|
||||
auto all = +array[0];
|
||||
auto any = +array[0];
|
||||
|
||||
for (u32 i = 1; i < size; i++)
|
||||
{
|
||||
all &= +array[i];
|
||||
any |= +array[i];
|
||||
}
|
||||
|
||||
return std::make_pair(all, any);
|
||||
};
|
||||
|
||||
if (type->getScalarType()->isIntegerTy(8))
|
||||
{
|
||||
const auto [all, any] = combine_bits(v128_const._u8, 16);
|
||||
all_lanes = llvm::APInt(8, all);
|
||||
any_lanes = llvm::APInt(8, any);
|
||||
}
|
||||
else if (type->getScalarType()->isIntegerTy(16))
|
||||
{
|
||||
const auto [all, any] = combine_bits(v128_const._u16, 8);
|
||||
all_lanes = llvm::APInt(16, all);
|
||||
any_lanes = llvm::APInt(16, any);
|
||||
}
|
||||
else if (type->getScalarType()->isIntegerTy(32))
|
||||
{
|
||||
const auto [all, any] = combine_bits(v128_const._u32, 4);
|
||||
all_lanes = llvm::APInt(32, all);
|
||||
any_lanes = llvm::APInt(32, any);
|
||||
}
|
||||
else // if (type->getScalarType()->isIntegerTy(64))
|
||||
{
|
||||
return llvm::KnownBits(type->getScalarSizeInBits());
|
||||
}
|
||||
|
||||
if (bin_inst->getOpcode() == llvm::Instruction::Or)
|
||||
{
|
||||
llvm::KnownBits ret(type->getScalarSizeInBits());
|
||||
ret.One = all_lanes;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (bin_inst->getOpcode() == llvm::Instruction::And)
|
||||
{
|
||||
llvm::KnownBits ret(type->getScalarSizeInBits());
|
||||
ret.Zero = any_lanes;
|
||||
ret.Zero.flipAllBits();
|
||||
return ret;
|
||||
}
|
||||
|
||||
return llvm::KnownBits(type->getScalarSizeInBits());
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -27,9 +27,7 @@
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/Target/TargetMachine.h"
|
||||
#include "llvm/Support/KnownBits.h"
|
||||
#if LLVM_VERSION_MAJOR >= 21
|
||||
#include "llvm/Support/KnownFPClass.h"
|
||||
#endif
|
||||
#include "llvm/Analysis/SimplifyQuery.h"
|
||||
#include "llvm/Analysis/ConstantFolding.h"
|
||||
#include "llvm/Analysis/ValueTracking.h"
|
||||
@@ -1180,11 +1178,7 @@ struct llvm_fshl
|
||||
static llvm::Function* get_fshl(llvm::IRBuilder<>* ir)
|
||||
{
|
||||
const auto _module = ir->GetInsertBlock()->getParent()->getParent();
|
||||
#if LLVM_VERSION_MAJOR >= 21 || (LLVM_VERSION_MAJOR == 20 && LLVM_VERSION_MINOR >= 1)
|
||||
return llvm::Intrinsic::getOrInsertDeclaration(_module, llvm::Intrinsic::fshl, {llvm_value_t<T>::get_type(ir->getContext())});
|
||||
#else
|
||||
return llvm::Intrinsic::getDeclaration(_module, llvm::Intrinsic::fshl, {llvm_value_t<T>::get_type(ir->getContext())});
|
||||
#endif
|
||||
}
|
||||
|
||||
static llvm::Value* fold(llvm::IRBuilder<>* ir, llvm::Value* v1, llvm::Value* v2, llvm::Value* v3)
|
||||
@@ -1256,11 +1250,7 @@ struct llvm_fshr
|
||||
static llvm::Function* get_fshr(llvm::IRBuilder<>* ir)
|
||||
{
|
||||
const auto _module = ir->GetInsertBlock()->getParent()->getParent();
|
||||
#if LLVM_VERSION_MAJOR >= 21 || (LLVM_VERSION_MAJOR == 20 && LLVM_VERSION_MINOR >= 1)
|
||||
return llvm::Intrinsic::getOrInsertDeclaration(_module, llvm::Intrinsic::fshr, {llvm_value_t<T>::get_type(ir->getContext())});
|
||||
#else
|
||||
return llvm::Intrinsic::getDeclaration(_module, llvm::Intrinsic::fshr, {llvm_value_t<T>::get_type(ir->getContext())});
|
||||
#endif
|
||||
}
|
||||
|
||||
static llvm::Value* fold(llvm::IRBuilder<>* ir, llvm::Value* v1, llvm::Value* v2, llvm::Value* v3)
|
||||
@@ -2366,11 +2356,7 @@ struct llvm_add_sat
|
||||
static llvm::Function* get_add_sat(llvm::IRBuilder<>* ir)
|
||||
{
|
||||
const auto _module = ir->GetInsertBlock()->getParent()->getParent();
|
||||
#if LLVM_VERSION_MAJOR >= 21 || (LLVM_VERSION_MAJOR == 20 && LLVM_VERSION_MINOR >= 1)
|
||||
return llvm::Intrinsic::getOrInsertDeclaration(_module, intr, {llvm_value_t<T>::get_type(ir->getContext())});
|
||||
#else
|
||||
return llvm::Intrinsic::getDeclaration(_module, intr, {llvm_value_t<T>::get_type(ir->getContext())});
|
||||
#endif
|
||||
}
|
||||
|
||||
llvm::Value* eval(llvm::IRBuilder<>* ir) const
|
||||
@@ -2453,11 +2439,7 @@ struct llvm_sub_sat
|
||||
static llvm::Function* get_sub_sat(llvm::IRBuilder<>* ir)
|
||||
{
|
||||
const auto _module = ir->GetInsertBlock()->getParent()->getParent();
|
||||
#if LLVM_VERSION_MAJOR >= 21 || (LLVM_VERSION_MAJOR == 20 && LLVM_VERSION_MINOR >= 1)
|
||||
return llvm::Intrinsic::getOrInsertDeclaration(_module, intr, {llvm_value_t<T>::get_type(ir->getContext())});
|
||||
#else
|
||||
return llvm::Intrinsic::getDeclaration(_module, intr, {llvm_value_t<T>::get_type(ir->getContext())});
|
||||
#endif
|
||||
}
|
||||
|
||||
llvm::Value* eval(llvm::IRBuilder<>* ir) const
|
||||
@@ -3804,11 +3786,7 @@ public:
|
||||
llvm::Function* get_intrinsic(llvm::Intrinsic::ID id)
|
||||
{
|
||||
const auto _module = m_ir->GetInsertBlock()->getParent()->getParent();
|
||||
#if LLVM_VERSION_MAJOR >= 21 || (LLVM_VERSION_MAJOR == 20 && LLVM_VERSION_MINOR >= 1)
|
||||
return llvm::Intrinsic::getOrInsertDeclaration(_module, id, {get_type<Types>()...});
|
||||
#else
|
||||
return llvm::Intrinsic::getDeclaration(_module, id, {get_type<Types>()...});
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T1, typename T2, typename T3>
|
||||
@@ -3852,13 +3830,8 @@ public:
|
||||
const auto data1 = b.eval(m_ir);
|
||||
const auto data2 = c.eval(m_ir);
|
||||
|
||||
#if LLVM_VERSION_MAJOR >= 22
|
||||
// LLVM 22+ changed the intrinsic signature from v4i32 to v16i8 for operands 2 and 3
|
||||
result.value = m_ir->CreateCall(get_intrinsic(llvm::Intrinsic::x86_avx512_vpdpbusd_128),
|
||||
{data0, m_ir->CreateBitCast(data1, get_type<u8[16]>()), m_ir->CreateBitCast(data2, get_type<u8[16]>())});
|
||||
#else
|
||||
result.value = m_ir->CreateCall(get_intrinsic(llvm::Intrinsic::x86_avx512_vpdpbusd_128), {data0, data1, data2});
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -4271,10 +4244,55 @@ template <typename T1, typename T2, typename T3>
|
||||
template <typename T = v128>
|
||||
llvm::Constant* make_const_vector(T, llvm::Type*, u32 = __builtin_LINE());
|
||||
|
||||
// IR is emitted in a single pass: phi nodes may still be missing their back-edge incoming
|
||||
// values, so any known bits computeKnownBits derives through a phi are unsound for the
|
||||
// final IR. Whether a phi is complete cannot be queried (the CFG edges from not-yet-emitted
|
||||
// predecessors don't exist either), so reject every value whose bits may derive from a phi.
|
||||
static bool is_known_bits_safe(llvm::Value* value)
|
||||
{
|
||||
llvm::SmallPtrSet<const llvm::Value*, 32> visited;
|
||||
llvm::SmallVector<const llvm::Value*, 32> worklist{value};
|
||||
|
||||
while (!worklist.empty())
|
||||
{
|
||||
const llvm::Value* v = worklist.pop_back_val();
|
||||
|
||||
if (!visited.insert(v).second)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (llvm::isa<llvm::PHINode>(v) || visited.size() > 256)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Loads don't propagate operand bits; constants and arguments are leaves
|
||||
if (auto i = llvm::dyn_cast<llvm::Instruction>(v); i && !llvm::isa<llvm::LoadInst>(i))
|
||||
{
|
||||
for (const llvm::Use& op : i->operands())
|
||||
{
|
||||
worklist.push_back(op.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
llvm::KnownBits get_known_bits_fallback(llvm::Value* value);
|
||||
|
||||
template <typename T>
|
||||
llvm::KnownBits get_known_bits(T a)
|
||||
{
|
||||
return llvm::computeKnownBits(a.eval(m_ir), m_module->getDataLayout());
|
||||
llvm::Value* value = a.eval(m_ir);
|
||||
|
||||
if (!is_known_bits_safe(value))
|
||||
{
|
||||
return get_known_bits_fallback(value);
|
||||
}
|
||||
|
||||
return llvm::computeKnownBits(value, m_module->getDataLayout());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -4288,12 +4306,8 @@ template <typename T1, typename T2, typename T3>
|
||||
{
|
||||
static_assert(depth <= llvm::MaxAnalysisRecursionDepth, "Depth parameter can only decrease search. Default is max.");
|
||||
|
||||
#if LLVM_VERSION_MAJOR >= 21
|
||||
const llvm::SimplifyQuery SQ(m_module->getDataLayout());
|
||||
return llvm::computeKnownFPClass(a.eval(m_ir), interested_classes, SQ, llvm::MaxAnalysisRecursionDepth - depth);
|
||||
#else
|
||||
return llvm::computeKnownFPClass(a.eval(m_ir), m_module->getDataLayout(), interested_classes, llvm::MaxAnalysisRecursionDepth - depth);
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -1290,11 +1290,11 @@ error_code cellRecOpen(vm::cptr<char> pDirName, vm::cptr<char> pFileName, vm::cp
|
||||
rec.encoder->set_path(vfs::get(rec.param.filename));
|
||||
rec.encoder->set_framerate(rec.fps);
|
||||
rec.encoder->set_video_bitrate(rec.video_bps);
|
||||
rec.encoder->set_video_codec(rec.video_codec_id);
|
||||
rec.encoder->set_video_codec(rec.video_codec_id, "");
|
||||
rec.encoder->set_sample_rate(rec.sample_rate);
|
||||
rec.encoder->set_audio_channels(rec.channels);
|
||||
rec.encoder->set_audio_bitrate(rec.audio_bps);
|
||||
rec.encoder->set_audio_codec(rec.audio_codec_id);
|
||||
rec.encoder->set_audio_codec(rec.audio_codec_id, "");
|
||||
rec.encoder->set_output_format(rec.output_format);
|
||||
|
||||
sysutil_register_cb([&rec](ppu_thread& ppu) -> s32
|
||||
|
||||
@@ -5188,7 +5188,7 @@ bool ppu_initialize(const ppu_module<lv2_obj>& info, bool check_only, u64 file_s
|
||||
settings += ppu_settings::daz_and_ftz;
|
||||
|
||||
// Write version, hash, CPU, settings
|
||||
fmt::append(obj_name, "v7-kusa-%s-%s-%s.obj", fmt::base57(output, 16), fmt::base57(settings), jit_compiler::cpu(g_cfg.core.llvm_cpu.to_string()));
|
||||
fmt::append(obj_name, "v8-kusa-%s-%s-%s.obj", fmt::base57(output, 16), fmt::base57(settings), jit_compiler::cpu(g_cfg.core.llvm_cpu.to_string()));
|
||||
}
|
||||
|
||||
if (cpu ? cpu->state.all_of(cpu_flag::exit) : Emu.IsStopped())
|
||||
@@ -5586,11 +5586,7 @@ static void ppu_initialize2(jit_compiler& jit, const ppu_module<lv2_obj>& module
|
||||
std::unique_ptr<Module> _module = std::make_unique<Module>(obj_name, jit.get_context());
|
||||
|
||||
// Initialize target
|
||||
#if LLVM_VERSION_MAJOR >= 21 && (LLVM_VERSION_MINOR >= 1 || LLVM_VERSION_MAJOR >= 22)
|
||||
_module->setTargetTriple(Triple(jit_compiler::triple1()));
|
||||
#else
|
||||
_module->setTargetTriple(jit_compiler::triple1());
|
||||
#endif
|
||||
_module->setDataLayout(jit.get_engine().getTargetMachine()->createDataLayout());
|
||||
|
||||
// Initialize translator
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "Emu/IdManager.h"
|
||||
#include "Loader/ELF.h"
|
||||
#include "util/asm.hpp"
|
||||
#include "timers.hpp"
|
||||
|
||||
#include "SPUThread.h"
|
||||
|
||||
@@ -39,8 +40,67 @@ bool spu_thread::read_reg(const u32 addr, u32& value)
|
||||
{
|
||||
const u32 offset = addr - (RAW_SPU_BASE_ADDR + RAW_SPU_OFFSET * index) - RAW_SPU_PROB_OFFSET;
|
||||
|
||||
spu_log.trace("RawSPU[%u]: Read32(0x%x, offset=0x%x)", index, addr, offset);
|
||||
raw_spu_log_stats_t stats{};
|
||||
stats.mmio_offset = offset;
|
||||
|
||||
const auto [old_stats, is_changed] = mmio_stats.fetch_op([&](raw_spu_log_stats_t& old)
|
||||
{
|
||||
if (old.mmio_offset == offset)
|
||||
{
|
||||
const u64 current = get_system_time();
|
||||
|
||||
if (current - old.mmio_time >= 1500)
|
||||
{
|
||||
old.mmio_time = current;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
old.mmio_offset = offset;
|
||||
return true;
|
||||
});
|
||||
|
||||
std::string log_message = fmt::format("RawSPU[%u]: read_reg(0x%x, offset=0x%x)", index, addr, offset);
|
||||
|
||||
if (is_changed)
|
||||
{
|
||||
spu_log.trace("%s", log_message);
|
||||
}
|
||||
|
||||
struct logger_end_t
|
||||
{
|
||||
u32 offset;
|
||||
const u32* value;
|
||||
atomic_t<raw_spu_log_stats_t>* stats;
|
||||
std::string log_message;
|
||||
|
||||
~logger_end_t() noexcept
|
||||
{
|
||||
const auto [old, value_changed] = stats->fetch_op([&](raw_spu_log_stats_t& old)
|
||||
{
|
||||
if (old.mmio_offset == offset)
|
||||
{
|
||||
if (old.mmio_value != *value)
|
||||
{
|
||||
const u64 current = get_system_time();
|
||||
old.mmio_time = current;
|
||||
old.mmio_value = *value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
if (value_changed)
|
||||
{
|
||||
spu_log.trace("%s: value=0x%x", log_message, *value);
|
||||
}
|
||||
}
|
||||
} logger_end{offset, &value, &mmio_stats, std::move(log_message)};
|
||||
|
||||
switch (offset)
|
||||
{
|
||||
case MFC_CMDStatus_offs:
|
||||
@@ -202,7 +262,7 @@ bool spu_thread::read_reg(const u32 addr, u32& value)
|
||||
}
|
||||
}
|
||||
|
||||
spu_log.error("RawSPU[%u]: Read32(0x%x): unknown/illegal offset (0x%x)", index, addr, offset);
|
||||
spu_log.error("RawSPU[%u]: read_reg(0x%x): unknown/illegal offset (0x%x)", index, addr, offset);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -210,7 +270,30 @@ bool spu_thread::write_reg(const u32 addr, const u32 value)
|
||||
{
|
||||
const u32 offset = addr - (RAW_SPU_BASE_ADDR + RAW_SPU_OFFSET * index) - RAW_SPU_PROB_OFFSET;
|
||||
|
||||
spu_log.trace("RawSPU[%u]: Write32(0x%x, offset=0x%x, value=0x%x)", index, addr, offset, value);
|
||||
const auto [old_stats, is_changed] = mmio_stats.fetch_op([&](raw_spu_log_stats_t& old)
|
||||
{
|
||||
if (old.mmio_offset == offset && old.mmio_value == value)
|
||||
{
|
||||
const u64 current = get_system_time();
|
||||
|
||||
if (current - old.mmio_time >= 500)
|
||||
{
|
||||
old.mmio_time = current;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
old.mmio_offset = offset;
|
||||
old.mmio_value = value;
|
||||
return true;
|
||||
});
|
||||
|
||||
if (is_changed)
|
||||
{
|
||||
spu_log.trace("RawSPU[%u]: write_reg(0x%x, offset=0x%x, value=0x%x)", index, addr, offset, value);
|
||||
}
|
||||
|
||||
switch (offset)
|
||||
{
|
||||
@@ -365,7 +448,7 @@ bool spu_thread::write_reg(const u32 addr, const u32 value)
|
||||
}
|
||||
}
|
||||
|
||||
spu_log.error("RawSPU[%u]: Write32(0x%x, value=0x%x): unknown/illegal offset (0x%x)", index, addr, value, offset);
|
||||
spu_log.error("RawSPU[%u]: write_reg(0x%x, value=0x%x): unknown/illegal offset (0x%x)", index, addr, value, offset);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -567,6 +567,10 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator
|
||||
return _ptr(m_thread, ::offset32(offset_args...));
|
||||
}
|
||||
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wnonnull"
|
||||
#endif
|
||||
template <typename T>
|
||||
T* spu_mem_attr(T* inst)
|
||||
{
|
||||
@@ -600,6 +604,9 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator
|
||||
|
||||
return inst;
|
||||
}
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
// Return default register type
|
||||
llvm::Type* get_reg_type(u32 index)
|
||||
@@ -1733,11 +1740,7 @@ public:
|
||||
|
||||
// Create LLVM module
|
||||
std::unique_ptr<Module> _module = std::make_unique<Module>(m_hash + ".obj", m_context);
|
||||
#if LLVM_VERSION_MAJOR >= 21 && (LLVM_VERSION_MINOR >= 1 || LLVM_VERSION_MAJOR >= 22)
|
||||
_module->setTargetTriple(Triple(jit_compiler::triple2()));
|
||||
#else
|
||||
_module->setTargetTriple(jit_compiler::triple2());
|
||||
#endif
|
||||
_module->setDataLayout(m_jit.get_engine().getTargetMachine()->createDataLayout());
|
||||
m_module = _module.get();
|
||||
|
||||
@@ -3663,11 +3666,7 @@ public:
|
||||
{
|
||||
if (b2 != bqbi)
|
||||
{
|
||||
#if LLVM_VERSION_MAJOR >= 21 || (LLVM_VERSION_MAJOR == 20 && LLVM_VERSION_MINOR >= 1)
|
||||
auto ins = &*b2->block->getFirstNonPHIIt();
|
||||
#else
|
||||
auto ins = b2->block->getFirstNonPHI();
|
||||
#endif
|
||||
|
||||
if (b2->bb->preds.size() == 1)
|
||||
{
|
||||
@@ -4039,11 +4038,7 @@ public:
|
||||
|
||||
// Create LLVM module
|
||||
std::unique_ptr<Module> _module = std::make_unique<Module>("spu_interpreter.obj", m_context);
|
||||
#if LLVM_VERSION_MAJOR >= 21 && (LLVM_VERSION_MINOR >= 1 || LLVM_VERSION_MAJOR >= 22)
|
||||
_module->setTargetTriple(Triple(jit_compiler::triple2()));
|
||||
#else
|
||||
_module->setTargetTriple(jit_compiler::triple2());
|
||||
#endif
|
||||
_module->setDataLayout(m_jit.get_engine().getTargetMachine()->createDataLayout());
|
||||
m_module = _module.get();
|
||||
|
||||
@@ -7805,6 +7800,8 @@ public:
|
||||
// Avoid pessimation when full clamping isn't needed
|
||||
if (m_use_avx512 && !(known.isKnownNeverNaN() && (known.isKnownNeverPosInfinity() || known.isKnownNeverNegInfinity())))
|
||||
{
|
||||
// Fails to clamp SNaN
|
||||
// Normally doesn't cause issues as SNaN frequently gets quieted beforehand
|
||||
return eval(vrangeps(v, fsplat<f32[4]>(std::bit_cast<f32, u32>(0x7f7fffff)), 0x2, 0xff));
|
||||
}
|
||||
|
||||
@@ -8847,13 +8844,17 @@ public:
|
||||
const auto a = bitcast<u32[4]>(value<f32[4]>(ci->getOperand(0)));
|
||||
const auto b = bitcast<u32[4]>(value<f32[4]>(ci->getOperand(1)));
|
||||
|
||||
// The AVX512 path in `clamp_smax` doesn't properly clamp SNaN
|
||||
// Normally this is fine, but it can cause issues if we generate them from normalized inputs
|
||||
|
||||
const auto base = (b & 0x007ffc00u) << 9; // Base fraction
|
||||
const auto ymul = (b & 0x3ff) * (a & 0x7ffff); // Step fraction * Y fraction (fixed point at 2^-32)
|
||||
const auto comparison = (ymul > base); // Should exponent be adjusted?
|
||||
const auto bnew = (base - ymul) >> (zext<u32[4]>(comparison) ^ 9); // Shift one less bit if exponent is adjusted
|
||||
const auto base_result = (b & 0xff800000u) | (bnew & ~0xff800000u); // Inject old sign and exponent
|
||||
const auto adjustment = bitcast<u32[4]>(sext<s32[4]>(comparison)) & (1 << 23); // exponent adjustement for negative bnew
|
||||
return clamp_smax(eval(bitcast<f32[4]>(base_result - adjustment)));
|
||||
const auto comparison = bitcast<u32[4]>(sext<s32[4]>(ymul > base)); // Should exponent be adjusted?
|
||||
const auto bnew = (base - ymul) >> (comparison + 9); // Shift one less bit if exponent is adjusted
|
||||
const auto adjustment = comparison & (1 << 23); // exponent adjustement for negative bnew
|
||||
const auto adjust_expo = (b & 0xff800000u) - adjustment;
|
||||
const auto result_expo = clamp_smax(eval(bitcast<f32[4]>(adjust_expo)));
|
||||
return bitcast<f32[4]>(bitcast<u32[4]>(result_expo) | (bnew & 0x007fffffu)); // clamped overwrites mantissa
|
||||
});
|
||||
|
||||
const auto [a, b] = get_vrs<f32[4]>(op.ra, op.rb);
|
||||
@@ -8892,15 +8893,13 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
b = eval(b | fix_exponent | a_sign);
|
||||
|
||||
const auto base = (b & 0x007ffc00u) << 9; // Base fraction
|
||||
const auto ymul = (b & 0x3ff) * (a & 0x7ffff); // Step fraction * Y fraction (fixed point at 2^-32)
|
||||
const auto comparison = (ymul > base); // Should exponent be adjusted?
|
||||
const auto bnew = (base - ymul) >> (zext<u32[4]>(comparison) ^ 9); // Shift one less bit if exponent is adjusted
|
||||
const auto base_result = (b & 0xff800000u) | (bnew & ~0xff800000u); // Inject old sign and exponent
|
||||
const auto adjustment = bitcast<u32[4]>(sext<s32[4]>(comparison)) & (1 << 23); // exponent adjustement for negative bnew
|
||||
return clamp_smax(eval(bitcast<f32[4]>(base_result - adjustment)));
|
||||
const auto comparison = bitcast<u32[4]>(sext<s32[4]>(ymul > base)); // Should exponent be adjusted?
|
||||
const auto bnew = (base - ymul) >> (comparison + 9); // Shift one less bit if exponent is adjusted
|
||||
const auto adjust_expo = (fix_exponent | a_sign) - (comparison & (1 << 23)); // exponent adjustement for negative bnew
|
||||
const auto result_expo = clamp_smax(eval(bitcast<f32[4]>(adjust_expo)));
|
||||
return bitcast<f32[4]>(bitcast<u32[4]>(result_expo) | (bnew & 0x007fffffu)); // clamped overwrites mantissa
|
||||
});
|
||||
|
||||
register_intrinsic("spu_rsqrte", [&](llvm::CallInst* ci)
|
||||
@@ -8923,15 +8922,15 @@ public:
|
||||
final_fraction = eval(insert(final_fraction, i, r_fraction));
|
||||
}
|
||||
|
||||
const auto b = eval(final_fraction | final_exponent);
|
||||
const auto b = final_fraction;
|
||||
|
||||
const auto base = (b & 0x007ffc00u) << 9; // Base fraction
|
||||
const auto ymul = (b & 0x3ff) * (a & 0x7ffff); // Step fraction * Y fraction (fixed point at 2^-32)
|
||||
const auto comparison = (ymul > base); // Should exponent be adjusted?
|
||||
const auto bnew = (base - ymul) >> (zext<u32[4]>(comparison) ^ 9); // Shift one less bit if exponent is adjusted
|
||||
const auto base_result = (b & 0xff800000u) | (bnew & ~0xff800000u); // Inject old sign and exponent
|
||||
const auto adjustment = bitcast<u32[4]>(sext<s32[4]>(comparison)) & (1 << 23); // exponent adjustement for negative bnew
|
||||
return clamp_smax(eval(bitcast<f32[4]>(base_result - adjustment)));
|
||||
const auto comparison = bitcast<u32[4]>(sext<s32[4]>(ymul > base)); // Should exponent be adjusted?
|
||||
const auto bnew = (base - ymul) >> (comparison + 9); // Shift one less bit if exponent is adjusted
|
||||
const auto adjust_expo = final_exponent - (comparison & (1 << 23)); // exponent adjustement for negative bnew
|
||||
const auto result_expo = clamp_positive_smax(eval(bitcast<f32[4]>(adjust_expo)));
|
||||
return bitcast<f32[4]>(bitcast<u32[4]>(result_expo) | (bnew & 0x007fffffu)); // clamped overwrites mantissa
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -826,6 +826,15 @@ public:
|
||||
u64 mfc_dump_idx = 0;
|
||||
static constexpr u32 max_mfc_dump_idx = 4096;
|
||||
|
||||
struct alignas(16) raw_spu_log_stats_t
|
||||
{
|
||||
u32 mmio_offset = umax;
|
||||
u32 mmio_value = umax;
|
||||
u64 mmio_time = umax;
|
||||
};
|
||||
|
||||
atomic_t<raw_spu_log_stats_t> mmio_stats{};
|
||||
|
||||
bool in_cpu_work = false;
|
||||
bool allow_interrupts_in_cpu_work = false;
|
||||
u8 cpu_work_iteration_count = 0;
|
||||
|
||||
@@ -191,13 +191,15 @@ error_code sys_rsx_memory_allocate(cpu_thread& cpu, vm::ptr<u32> mem_handle, vm:
|
||||
|
||||
cpu.state += cpu_flag::wait;
|
||||
|
||||
if (vm::falloc(rsx::constants::local_mem_base, size, vm::video))
|
||||
const u32 mem_size = static_cast<u32>(size);
|
||||
|
||||
if (vm::falloc(rsx::constants::local_mem_base, mem_size, vm::video))
|
||||
{
|
||||
rsx::get_current_renderer()->local_mem_size = size;
|
||||
rsx::get_current_renderer()->local_mem_size = mem_size;
|
||||
|
||||
if (u32 addr = rsx::get_current_renderer()->driver_info)
|
||||
{
|
||||
vm::_ptr<RsxDriverInfo>(addr)->memory_size = size;
|
||||
vm::_ptr<RsxDriverInfo>(addr)->memory_size = mem_size;
|
||||
}
|
||||
|
||||
*mem_addr = rsx::constants::local_mem_base;
|
||||
@@ -435,7 +437,7 @@ error_code sys_rsx_context_iomap(cpu_thread& cpu, u32 context_id, u64 io, u64 ea
|
||||
}
|
||||
|
||||
// Wait until we have no active RSX locks and reserve iomap for use. Must do so before acquiring vm lock to avoid deadlocks
|
||||
rsx::reservation_lock<true> rsx_lock(ea, size);
|
||||
rsx::reservation_lock<true> rsx_lock(::narrow<u32>(ea), static_cast<u32>(size));
|
||||
|
||||
vm::writer_lock rlock;
|
||||
|
||||
@@ -446,7 +448,7 @@ error_code sys_rsx_context_iomap(cpu_thread& cpu, u32 context_id, u64 io, u64 ea
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
|
||||
if ((addr == ea || !(addr % 0x1000'0000)) && idm::check_unlocked<sys_vm_t>(sys_vm_t::find_id(addr)))
|
||||
if ((addr == ea || !(addr % 0x1000'0000)) && idm::check_unlocked<sys_vm_t>(sys_vm_t::find_id(::narrow<u32>(addr))))
|
||||
{
|
||||
// Virtual memory is disallowed
|
||||
return CELL_EINVAL;
|
||||
|
||||
@@ -976,10 +976,11 @@ void PadDevice::reset_orientation()
|
||||
// Initialize Fusion
|
||||
ahrs = std::make_shared<FusionAhrs>();
|
||||
FusionAhrsInitialise(ahrs.get());
|
||||
ahrs->settings.convention = FusionConvention::FusionConventionEnu;
|
||||
ahrs->settings.gain = 0.0f; // If gain is set, the algorithm tries to adjust the orientation over time.
|
||||
FusionAhrsSetSettings(ahrs.get(), &ahrs->settings);
|
||||
FusionAhrsRestart(ahrs.get());
|
||||
|
||||
FusionAhrsSettings settings = fusionAhrsDefaultSettings;
|
||||
settings.convention = FusionConvention::FusionConventionEnu;
|
||||
settings.gain = 0.0f; // If gain is set, the algorithm tries to adjust the orientation over time.
|
||||
FusionAhrsSetSettings(ahrs.get(), &settings);
|
||||
}
|
||||
|
||||
void PadDevice::update_orientation(ps_move_data& move_data)
|
||||
@@ -996,7 +997,7 @@ void PadDevice::update_orientation(ps_move_data& move_data)
|
||||
|
||||
// The ps move handler's axis may differ from the Fusion axis, so we have to map them correctly.
|
||||
// Don't ask how the axis work. It's basically been trial and error.
|
||||
ensure(ahrs->settings.convention == FusionConvention::FusionConventionEnu); // East-North-Up
|
||||
ensure(ahrs->convention == FusionConvention::FusionConventionEnu); // East-North-Up
|
||||
|
||||
const FusionVector accelerometer{
|
||||
.axis {
|
||||
|
||||
@@ -16,7 +16,8 @@ struct cfg_recording final : cfg::node
|
||||
cfg::uint<640, 7680> width{this, "Width", 1280};
|
||||
cfg::uint<360, 4320> height{this, "Height", 720};
|
||||
cfg::uint<0, 192> pixel_format{this, "AVPixelFormat", 0}; // AVPixelFormat::AV_PIX_FMT_YUV420P
|
||||
cfg::uint<0, 0xFFFF> video_codec{this, "AVCodecID", 12}; // AVCodecID::AV_CODEC_ID_MPEG4
|
||||
cfg::string codec_name{this, "Video Codec", ""};
|
||||
cfg::uint<0, 0xFFFF> codec_id{this, "AVCodecID", 12}; // AVCodecID::AV_CODEC_ID_MPEG4
|
||||
cfg::uint<1'000'000, 60'000'000> video_bps{this, "Video Bitrate", 4'000'000};
|
||||
cfg::uint<0, 3> max_b_frames{this, "Max B-Frames", 2};
|
||||
cfg::uint<1, 120> gop_size{this, "Group of Pictures Size", 30};
|
||||
@@ -27,7 +28,8 @@ struct cfg_recording final : cfg::node
|
||||
{
|
||||
node_audio(cfg::node* _this) : cfg::node(_this, "Audio") {}
|
||||
|
||||
cfg::uint<0x10000, 0x17000> audio_codec{this, "AVCodecID", 86018}; // AVCodecID::AV_CODEC_ID_AAC
|
||||
cfg::string codec_name{this, "Audio Codec", ""};
|
||||
cfg::uint<0x10000, 0x17000> codec_id{this, "AVCodecID", 86018}; // AVCodecID::AV_CODEC_ID_AAC
|
||||
cfg::uint<64'000, 320'000> audio_bps{this, "Audio Bitrate", 192'000};
|
||||
|
||||
} audio{ this };
|
||||
|
||||
@@ -107,6 +107,22 @@ void usb_device_passthrough::patch_descriptors()
|
||||
device._device.idVendor = 0x12BA; // SCEA
|
||||
device._device.idProduct = 0x2530; // Harmonix Real Guitar
|
||||
break;
|
||||
case 0x3138: // Harmonix MPA in Drums Mode - Wii
|
||||
device._device.idVendor = 0x12BA; // SCEA
|
||||
device._device.idProduct = 0x0218; // Harmonix MPA in Drums Mode
|
||||
break;
|
||||
case 0x3338: // Harmonix MPA in Keyboard Mode - Wii
|
||||
device._device.idVendor = 0x12BA; // SCEA
|
||||
device._device.idProduct = 0x2338; // Harmonix MPA in Keyboard Mode
|
||||
break;
|
||||
case 0x3438: // Harmonix MPA in Button Guitar Mode - Wii
|
||||
device._device.idVendor = 0x12BA; // SCEA
|
||||
device._device.idProduct = 0x2438; // Harmonix MPA in Button Guitar Mode
|
||||
break;
|
||||
case 0x3538: // Harmonix MPA in Real Guitar Mode - Wii
|
||||
device._device.idVendor = 0x12BA; // SCEA
|
||||
device._device.idProduct = 0x2538; // Harmonix MPA in Real Guitar Mode
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1045,6 +1045,8 @@ namespace np
|
||||
|
||||
void np_handler::set_message_selected(SceNpBasicAttachmentDataId id, u64 msg_id)
|
||||
{
|
||||
std::lock_guard lock(m_mutex_selected_messages);
|
||||
|
||||
switch (id)
|
||||
{
|
||||
case SCE_NP_BASIC_SELECTED_INVITATION_DATA:
|
||||
@@ -1060,6 +1062,8 @@ namespace np
|
||||
|
||||
std::optional<shared_ptr<std::pair<std::string, message_data>>> np_handler::get_message_selected(SceNpBasicAttachmentDataId id)
|
||||
{
|
||||
std::lock_guard lock(m_mutex_selected_messages);
|
||||
|
||||
switch (id)
|
||||
{
|
||||
case SCE_NP_BASIC_SELECTED_INVITATION_DATA:
|
||||
@@ -1079,6 +1083,8 @@ namespace np
|
||||
|
||||
void np_handler::clear_message_selected(SceNpBasicAttachmentDataId id)
|
||||
{
|
||||
std::lock_guard lock(m_mutex_selected_messages);
|
||||
|
||||
switch (id)
|
||||
{
|
||||
case SCE_NP_BASIC_SELECTED_INVITATION_DATA:
|
||||
@@ -1100,6 +1106,29 @@ namespace np
|
||||
get_rpcn()->send_message(msg_data, npids);
|
||||
}
|
||||
|
||||
bool np_handler::select_invitation(u64 msg_id)
|
||||
{
|
||||
const auto message = get_message(msg_id);
|
||||
|
||||
if (!message || message.value()->second.mainType != SCE_NP_BASIC_MESSAGE_MAIN_TYPE_INVITE)
|
||||
{
|
||||
rpcn_log.error("Cannot select invalid invitation: msg_id=%d", msg_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
set_message_selected(SCE_NP_BASIC_SELECTED_INVITATION_DATA, msg_id);
|
||||
|
||||
if (sysutil_send_system_cmd(CELL_SYSUTIL_NP_INVITATION_SELECTED, 0) <= 0)
|
||||
{
|
||||
clear_message_selected(SCE_NP_BASIC_SELECTED_INVITATION_DATA);
|
||||
rpcn_log.error("Failed to notify the game about selected invitation: msg_id=%d", msg_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
get_rpcn()->mark_message_used(msg_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
void np_handler::operator()()
|
||||
{
|
||||
if (g_cfg.net.psn_status != np_psn_status::psn_rpcn)
|
||||
|
||||
@@ -155,6 +155,7 @@ namespace np
|
||||
std::optional<shared_ptr<std::pair<std::string, message_data>>> get_message_selected(SceNpBasicAttachmentDataId id);
|
||||
void clear_message_selected(SceNpBasicAttachmentDataId id);
|
||||
void send_message(const message_data& msg_data, const std::set<std::string>& npids);
|
||||
bool select_invitation(u64 msg_id);
|
||||
|
||||
// Those should probably be under match2 ctx
|
||||
vm::ptr<SceNpMatching2RoomEventCallback> room_event_cb{}; // Room events
|
||||
@@ -440,6 +441,7 @@ namespace np
|
||||
gui_cache_manager gui_cache;
|
||||
|
||||
// Messages related
|
||||
shared_mutex m_mutex_selected_messages;
|
||||
std::optional<u64> selected_invite_id{};
|
||||
std::optional<u64> selected_message_id{};
|
||||
|
||||
|
||||
@@ -470,7 +470,7 @@ namespace
|
||||
for (; (i + step) <= count; i += step, vec_ptr++)
|
||||
{
|
||||
_mm_stream_si128(vec_ptr, values);
|
||||
_mm_add_epi16(values, vec_step);
|
||||
values = _mm_add_epi16(values, vec_step);
|
||||
}
|
||||
#endif
|
||||
for (; i < count; ++i)
|
||||
|
||||
@@ -749,27 +749,15 @@ std::tuple<u16, u16, u8> get_height_depth_layer(const RsxTextureType &tex)
|
||||
}
|
||||
fmt::throw_exception("Unsupported texture dimension");
|
||||
}
|
||||
}
|
||||
|
||||
template<typename RsxTextureType>
|
||||
std::vector<rsx::subresource_layout> get_subresources_layout_impl(const RsxTextureType &texture)
|
||||
// Common implementation for packing texture definitions into subresource layout arrays
|
||||
std::vector<rsx::subresource_layout> get_subresources_layout_impl(
|
||||
const std::byte* pixels,
|
||||
u32 format,
|
||||
u16 w, u16 h, u16 depth,
|
||||
u8 layer, u16 mipmap_count, u32 pitch,
|
||||
bool is_swizzled, bool has_border)
|
||||
{
|
||||
u16 w = texture.width();
|
||||
u16 h;
|
||||
u16 depth;
|
||||
u8 layer;
|
||||
|
||||
std::tie(h, depth, layer) = get_height_depth_layer(texture);
|
||||
|
||||
const auto format = texture.format() & ~(CELL_GCM_TEXTURE_LN | CELL_GCM_TEXTURE_UN);
|
||||
auto pitch = texture.pitch();
|
||||
|
||||
const u32 texaddr = rsx::get_address(texture.offset(), texture.location());
|
||||
auto pixels = vm::_ptr<const std::byte>(texaddr);
|
||||
|
||||
const bool is_swizzled = !(texture.format() & CELL_GCM_TEXTURE_LN);
|
||||
const bool has_border = !texture.border_type();
|
||||
|
||||
if (!is_swizzled)
|
||||
{
|
||||
if (const auto packed_pitch = rsx::get_format_packed_pitch(format, w, has_border, false); pitch < packed_pitch) [[unlikely]]
|
||||
@@ -790,10 +778,10 @@ std::vector<rsx::subresource_layout> get_subresources_layout_impl(const RsxTextu
|
||||
switch (format)
|
||||
{
|
||||
case CELL_GCM_TEXTURE_B8:
|
||||
return get_subresources_layout_impl<1, u8>(pixels, w, h, depth, layer, texture.get_exact_mipmap_count(), pitch, !is_swizzled, has_border);
|
||||
return get_subresources_layout_impl<1, u8>(pixels, w, h, depth, layer, mipmap_count, pitch, !is_swizzled, has_border);
|
||||
case CELL_GCM_TEXTURE_COMPRESSED_B8R8_G8R8:
|
||||
case CELL_GCM_TEXTURE_COMPRESSED_R8B8_R8G8:
|
||||
return get_subresources_layout_impl<2, u32>(pixels, w, h, depth, layer, texture.get_exact_mipmap_count(), pitch, !is_swizzled, has_border);
|
||||
return get_subresources_layout_impl<2, u32>(pixels, w, h, depth, layer, mipmap_count, pitch, !is_swizzled, has_border);
|
||||
case CELL_GCM_TEXTURE_COMPRESSED_HILO8:
|
||||
case CELL_GCM_TEXTURE_COMPRESSED_HILO_S8:
|
||||
case CELL_GCM_TEXTURE_DEPTH16:
|
||||
@@ -806,7 +794,7 @@ std::vector<rsx::subresource_layout> get_subresources_layout_impl(const RsxTextu
|
||||
case CELL_GCM_TEXTURE_R6G5B5:
|
||||
case CELL_GCM_TEXTURE_G8B8:
|
||||
case CELL_GCM_TEXTURE_X16:
|
||||
return get_subresources_layout_impl<1, u16>(pixels, w, h, depth, layer, texture.get_exact_mipmap_count(), pitch, !is_swizzled, has_border);
|
||||
return get_subresources_layout_impl<1, u16>(pixels, w, h, depth, layer, mipmap_count, pitch, !is_swizzled, has_border);
|
||||
case CELL_GCM_TEXTURE_DEPTH24_D8: // Untested
|
||||
case CELL_GCM_TEXTURE_DEPTH24_D8_FLOAT: // Untested
|
||||
case CELL_GCM_TEXTURE_D8R8G8B8:
|
||||
@@ -814,19 +802,36 @@ std::vector<rsx::subresource_layout> get_subresources_layout_impl(const RsxTextu
|
||||
case CELL_GCM_TEXTURE_Y16_X16:
|
||||
case CELL_GCM_TEXTURE_Y16_X16_FLOAT:
|
||||
case CELL_GCM_TEXTURE_X32_FLOAT:
|
||||
return get_subresources_layout_impl<1, u32>(pixels, w, h, depth, layer, texture.get_exact_mipmap_count(), pitch, !is_swizzled, has_border);
|
||||
return get_subresources_layout_impl<1, u32>(pixels, w, h, depth, layer, mipmap_count, pitch, !is_swizzled, has_border);
|
||||
case CELL_GCM_TEXTURE_W16_Z16_Y16_X16_FLOAT:
|
||||
return get_subresources_layout_impl<1, u64>(pixels, w, h, depth, layer, texture.get_exact_mipmap_count(), pitch, !is_swizzled, has_border);
|
||||
return get_subresources_layout_impl<1, u64>(pixels, w, h, depth, layer, mipmap_count, pitch, !is_swizzled, has_border);
|
||||
case CELL_GCM_TEXTURE_W32_Z32_Y32_X32_FLOAT:
|
||||
return get_subresources_layout_impl<1, u128>(pixels, w, h, depth, layer, texture.get_exact_mipmap_count(), pitch, !is_swizzled, has_border);
|
||||
return get_subresources_layout_impl<1, u128>(pixels, w, h, depth, layer, mipmap_count, pitch, !is_swizzled, has_border);
|
||||
case CELL_GCM_TEXTURE_COMPRESSED_DXT1:
|
||||
return get_subresources_layout_impl<4, u64>(pixels, w, h, depth, layer, texture.get_exact_mipmap_count(), pitch, !is_swizzled, false);
|
||||
return get_subresources_layout_impl<4, u64>(pixels, w, h, depth, layer, mipmap_count, pitch, !is_swizzled, false);
|
||||
case CELL_GCM_TEXTURE_COMPRESSED_DXT23:
|
||||
case CELL_GCM_TEXTURE_COMPRESSED_DXT45:
|
||||
return get_subresources_layout_impl<4, u128>(pixels, w, h, depth, layer, texture.get_exact_mipmap_count(), pitch, !is_swizzled, false);
|
||||
return get_subresources_layout_impl<4, u128>(pixels, w, h, depth, layer, mipmap_count, pitch, !is_swizzled, false);
|
||||
}
|
||||
fmt::throw_exception("Wrong format 0x%x", format);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename RsxTextureType>
|
||||
std::vector<rsx::subresource_layout> get_subresources_layout_impl(const RsxTextureType &texture)
|
||||
{
|
||||
const auto [h, depth, layer] = get_height_depth_layer(texture);
|
||||
const u32 texaddr = rsx::get_address(texture.offset(), texture.location());
|
||||
|
||||
return get_subresources_layout_impl(
|
||||
vm::_ptr<const std::byte>(texaddr),
|
||||
texture.format() & ~(CELL_GCM_TEXTURE_LN | CELL_GCM_TEXTURE_UN),
|
||||
texture.width(), h, depth, layer,
|
||||
texture.get_exact_mipmap_count(),
|
||||
texture.pitch(),
|
||||
!(texture.format() & CELL_GCM_TEXTURE_LN),
|
||||
!texture.border_type());
|
||||
}
|
||||
|
||||
namespace rsx
|
||||
{
|
||||
@@ -866,6 +871,40 @@ namespace rsx
|
||||
return get_subresources_layout_impl(texture);
|
||||
}
|
||||
|
||||
std::vector<rsx::subresource_layout> get_subresources_layout(const rsx::image_section_attributes_t& attrs, rsx::texture_dimension_extended type)
|
||||
{
|
||||
u16 height = attrs.height;
|
||||
u16 depth = 1;
|
||||
u8 layer = 1;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case rsx::texture_dimension_extended::texture_dimension_1d:
|
||||
height = 1;
|
||||
break;
|
||||
case rsx::texture_dimension_extended::texture_dimension_2d:
|
||||
break;
|
||||
case rsx::texture_dimension_extended::texture_dimension_cubemap:
|
||||
layer = 6;
|
||||
break;
|
||||
case rsx::texture_dimension_extended::texture_dimension_3d:
|
||||
depth = attrs.depth;
|
||||
break;
|
||||
default:
|
||||
fmt::throw_exception("Unsupported texture dimension");
|
||||
}
|
||||
|
||||
// NOTE: This variant goes through the hv pointer.
|
||||
return get_subresources_layout_impl(
|
||||
vm::get_super_ptr<const std::byte>(attrs.address),
|
||||
attrs.gcm_format,
|
||||
attrs.width, height, depth, layer,
|
||||
attrs.mipmaps,
|
||||
attrs.pitch,
|
||||
attrs.swizzled,
|
||||
false);
|
||||
}
|
||||
|
||||
texture_memory_info upload_texture_subresource(rsx::io_buffer& dst_buffer, const rsx::subresource_layout& src_layout, int format, bool is_swizzled, texture_uploader_capabilities& caps)
|
||||
{
|
||||
u16 w = src_layout.width_in_block;
|
||||
@@ -1703,6 +1742,48 @@ namespace rsx
|
||||
}
|
||||
}
|
||||
|
||||
rsx::surface_color_format get_compatible_surface_color_format(u32 gcm_format)
|
||||
{
|
||||
switch (gcm_format)
|
||||
{
|
||||
case CELL_GCM_TEXTURE_R5G6B5:
|
||||
return rsx::surface_color_format::r5g6b5;
|
||||
case CELL_GCM_TEXTURE_A8R8G8B8:
|
||||
return rsx::surface_color_format::a8b8g8r8;
|
||||
case CELL_GCM_TEXTURE_W16_Z16_Y16_X16_FLOAT:
|
||||
return rsx::surface_color_format::w16z16y16x16;
|
||||
case CELL_GCM_TEXTURE_W32_Z32_Y32_X32_FLOAT:
|
||||
return rsx::surface_color_format::w32z32y32x32;
|
||||
case CELL_GCM_TEXTURE_A1R5G5B5:
|
||||
return rsx::surface_color_format::x1r5g5b5_o1r5g5b5;
|
||||
case CELL_GCM_TEXTURE_B8:
|
||||
return rsx::surface_color_format::b8;
|
||||
case CELL_GCM_TEXTURE_G8B8:
|
||||
return rsx::surface_color_format::g8b8;
|
||||
case CELL_GCM_TEXTURE_X32_FLOAT:
|
||||
return rsx::surface_color_format::x32;
|
||||
default:
|
||||
fmt::throw_exception("Unhandled surface format 0x%x", gcm_format);
|
||||
}
|
||||
}
|
||||
|
||||
rsx::surface_depth_format2 get_compatible_surface_depth_format(u32 gcm_format)
|
||||
{
|
||||
switch (gcm_format)
|
||||
{
|
||||
case RSX_FORMAT_CLASS_DEPTH16_UNORM:
|
||||
return rsx::surface_depth_format2::z16_uint;
|
||||
case RSX_FORMAT_CLASS_DEPTH24_UNORM_X8_PACK32:
|
||||
return rsx::surface_depth_format2::z24s8_uint;
|
||||
case RSX_FORMAT_CLASS_DEPTH16_FLOAT:
|
||||
return rsx::surface_depth_format2::z16_float;
|
||||
case RSX_FORMAT_CLASS_DEPTH24_FLOAT_X8_PACK32:
|
||||
return rsx::surface_depth_format2::z24s8_float;
|
||||
default:
|
||||
fmt::throw_exception("Invalid depth format 0x%x", gcm_format);
|
||||
}
|
||||
}
|
||||
|
||||
u32 get_max_depth_value(rsx::surface_depth_format2 format)
|
||||
{
|
||||
return get_format_block_size_in_bytes(format) == 2 ? 0xFFFF : 0xFFFFFF;
|
||||
|
||||
@@ -284,6 +284,21 @@ namespace rsx
|
||||
usz alignment;
|
||||
};
|
||||
|
||||
struct image_section_attributes_t
|
||||
{
|
||||
u32 address;
|
||||
u32 gcm_format;
|
||||
u32 pitch;
|
||||
u16 width;
|
||||
u16 height;
|
||||
u16 depth;
|
||||
u16 mipmaps;
|
||||
u16 slice_h;
|
||||
u8 bpp;
|
||||
bool swizzled;
|
||||
bool edge_clamped;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get size to store texture in a linear fashion.
|
||||
* Storage is assumed to use a rowPitchAlignment boundary for every row of texture.
|
||||
@@ -298,6 +313,7 @@ namespace rsx
|
||||
*/
|
||||
std::vector<subresource_layout> get_subresources_layout(const rsx::fragment_texture &texture);
|
||||
std::vector<subresource_layout> get_subresources_layout(const rsx::vertex_texture &texture);
|
||||
std::vector<subresource_layout> get_subresources_layout(const image_section_attributes_t& attrs, texture_dimension_extended type);
|
||||
|
||||
texture_memory_info upload_texture_subresource(rsx::io_buffer& dst_buffer, const subresource_layout &src_layout, int format, bool is_swizzled, texture_uploader_capabilities& caps);
|
||||
|
||||
@@ -350,6 +366,9 @@ namespace rsx
|
||||
std::pair<u32, bool> get_compatible_gcm_format(rsx::surface_color_format format);
|
||||
std::pair<u32, bool> get_compatible_gcm_format(rsx::surface_depth_format2 format);
|
||||
|
||||
rsx::surface_color_format get_compatible_surface_color_format(u32 gcm_format);
|
||||
rsx::surface_depth_format2 get_compatible_surface_depth_format(u32 gcm_format);
|
||||
|
||||
format_class classify_format(rsx::surface_depth_format2 format);
|
||||
format_class classify_format(u32 gcm_format);
|
||||
|
||||
|
||||
@@ -152,6 +152,7 @@ namespace rsx
|
||||
|
||||
utils::address_range32 cache_range;
|
||||
bool do_not_cache = false;
|
||||
bool force_bg_load = false;
|
||||
|
||||
deferred_subresource() = default;
|
||||
|
||||
@@ -481,8 +482,7 @@ namespace rsx
|
||||
/**
|
||||
* Virtual Methods
|
||||
*/
|
||||
virtual image_view_type create_temporary_subresource_view(commandbuffer_type&, image_resource_type* src, u32 gcm_format, u16 x, u16 y, u16 w, u16 h, const texture_channel_remap_t& remap_vector) = 0;
|
||||
virtual image_view_type create_temporary_subresource_view(commandbuffer_type&, image_storage_type* src, u32 gcm_format, u16 x, u16 y, u16 w, u16 h, const texture_channel_remap_t& remap_vector) = 0;
|
||||
virtual image_view_type create_temporary_subresource_view(commandbuffer_type&, const deferred_subresource& desc) = 0;
|
||||
virtual void release_temporary_subresource(image_view_type rsc) = 0;
|
||||
virtual section_storage_type* create_new_texture(commandbuffer_type&, const address_range32 &rsx_range, u16 width, u16 height, u16 depth, u16 mipmaps, u32 pitch, u32 gcm_format,
|
||||
rsx::texture_upload_context context, rsx::texture_dimension_extended type, bool swizzled, component_order swizzle_flags, rsx::flags32_t flags) = 0;
|
||||
@@ -491,10 +491,10 @@ namespace rsx
|
||||
virtual section_storage_type* create_nul_section(commandbuffer_type&, const address_range32 &rsx_range, const image_section_attributes_t& attrs, const GCM_tile_reference& tile, bool memory_load) = 0;
|
||||
virtual void set_component_order(section_storage_type& section, u32 gcm_format, component_order expected) = 0;
|
||||
virtual void insert_texture_barrier(commandbuffer_type&, image_storage_type* tex, bool strong_ordering = true) = 0;
|
||||
virtual image_view_type generate_cubemap_from_images(commandbuffer_type&, u32 gcm_format, u16 size, const rsx::simple_array<copy_region_descriptor>& sources, const texture_channel_remap_t& remap_vector) = 0;
|
||||
virtual image_view_type generate_3d_from_2d_images(commandbuffer_type&, u32 gcm_format, u16 width, u16 height, u16 depth, const rsx::simple_array<copy_region_descriptor>& sources, const texture_channel_remap_t& remap_vector) = 0;
|
||||
virtual image_view_type generate_atlas_from_images(commandbuffer_type&, u32 gcm_format, u16 width, u16 height, const rsx::simple_array<copy_region_descriptor>& sections_to_copy, const texture_channel_remap_t& remap_vector) = 0;
|
||||
virtual image_view_type generate_2d_mipmaps_from_images(commandbuffer_type&, u32 gcm_format, u16 width, u16 height, const rsx::simple_array<copy_region_descriptor>& sections_to_copy, const texture_channel_remap_t& remap_vector) = 0;
|
||||
virtual image_view_type generate_cubemap_from_images(commandbuffer_type&, const deferred_subresource& desc) = 0;
|
||||
virtual image_view_type generate_3d_from_2d_images(commandbuffer_type&, const deferred_subresource& desc) = 0;
|
||||
virtual image_view_type generate_atlas_from_images(commandbuffer_type&, const deferred_subresource& desc) = 0;
|
||||
virtual image_view_type generate_2d_mipmaps_from_images(commandbuffer_type&, const deferred_subresource& desc) = 0;
|
||||
virtual void update_image_contents(commandbuffer_type&, image_view_type dst, image_resource_type src, u16 width, u16 height) = 0;
|
||||
virtual bool render_target_format_is_compatible(image_storage_type* tex, u32 gcm_format) = 0;
|
||||
virtual void prepare_for_dma_transfers(commandbuffer_type&) = 0;
|
||||
@@ -1709,7 +1709,8 @@ namespace rsx
|
||||
if (found_desc.external_handle != desc.external_handle ||
|
||||
found_desc.op != desc.op ||
|
||||
found_desc.x != desc.x || found_desc.y != desc.y ||
|
||||
found_desc.width != desc.width || found_desc.height != desc.height)
|
||||
found_desc.width != desc.width || found_desc.height != desc.height ||
|
||||
found_desc.gcm_format != desc.gcm_format)
|
||||
continue;
|
||||
|
||||
if (desc.op == deferred_request_command::copy_image_dynamic)
|
||||
@@ -1726,7 +1727,7 @@ namespace rsx
|
||||
{
|
||||
case deferred_request_command::cubemap_gather:
|
||||
{
|
||||
result = generate_cubemap_from_images(cmd, desc.gcm_format, desc.width, desc.sections_to_copy, desc.remap);
|
||||
result = generate_cubemap_from_images(cmd, desc);
|
||||
break;
|
||||
}
|
||||
case deferred_request_command::cubemap_unwrap:
|
||||
@@ -1761,12 +1762,15 @@ namespace rsx
|
||||
}
|
||||
}
|
||||
|
||||
result = generate_cubemap_from_images(cmd, desc.gcm_format, desc.width, sections, desc.remap);
|
||||
auto unwrap_desc = desc;
|
||||
unwrap_desc.sections_to_copy = std::move(sections);
|
||||
|
||||
result = generate_cubemap_from_images(cmd, unwrap_desc);
|
||||
break;
|
||||
}
|
||||
case deferred_request_command::_3d_gather:
|
||||
{
|
||||
result = generate_3d_from_2d_images(cmd, desc.gcm_format, desc.width, desc.height, desc.depth, desc.sections_to_copy, desc.remap);
|
||||
result = generate_3d_from_2d_images(cmd, desc);
|
||||
break;
|
||||
}
|
||||
case deferred_request_command::_3d_unwrap:
|
||||
@@ -1792,24 +1796,27 @@ namespace rsx
|
||||
};
|
||||
}
|
||||
|
||||
result = generate_3d_from_2d_images(cmd, desc.gcm_format, desc.width, desc.height, desc.depth, sections, desc.remap);
|
||||
auto unwrap_desc = desc;
|
||||
unwrap_desc.sections_to_copy = std::move(sections);
|
||||
|
||||
result = generate_3d_from_2d_images(cmd, unwrap_desc);
|
||||
break;
|
||||
}
|
||||
case deferred_request_command::atlas_gather:
|
||||
case deferred_request_command::blit_image_static:
|
||||
{
|
||||
result = generate_atlas_from_images(cmd, desc.gcm_format, desc.width, desc.height, desc.sections_to_copy, desc.remap);
|
||||
result = generate_atlas_from_images(cmd, desc);
|
||||
break;
|
||||
}
|
||||
case deferred_request_command::copy_image_static:
|
||||
case deferred_request_command::copy_image_dynamic:
|
||||
{
|
||||
result = create_temporary_subresource_view(cmd, &desc.external_handle, desc.gcm_format, desc.x, desc.y, desc.width, desc.height, desc.remap);
|
||||
result = create_temporary_subresource_view(cmd, desc);
|
||||
break;
|
||||
}
|
||||
case deferred_request_command::mipmap_gather:
|
||||
{
|
||||
result = generate_2d_mipmaps_from_images(cmd, desc.gcm_format, desc.width, desc.height, desc.sections_to_copy, desc.remap);
|
||||
result = generate_2d_mipmaps_from_images(cmd, desc);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -2111,19 +2118,15 @@ namespace rsx
|
||||
return {};
|
||||
}
|
||||
|
||||
bool result_is_valid;
|
||||
if (_pool == 0 && !g_cfg.video.write_color_buffers && !g_cfg.video.write_depth_buffer)
|
||||
bool result_is_valid = result.atlas_covers_target_area(section_count == 1 ? 99 : 90);
|
||||
if (_pool == 0 && !result_is_valid && !g_cfg.video.write_color_buffers && !g_cfg.video.write_depth_buffer)
|
||||
{
|
||||
// HACK: Avoid WCB requirement for some games with wrongly declared sampler dimensions.
|
||||
// TODO: Some games may render a small region (e.g 1024x256x2) and sample a huge texture (e.g 1024x1024).
|
||||
// Avoid WCB requirement for some games with wrongly declared sampler dimensions.
|
||||
// Some games may render a small region (e.g 1024x256x2) and sample a huge texture (e.g 1024x1024).
|
||||
// Seen in APF2k8 - this causes missing bits to be reuploaded from CPU which can cause WCB requirement.
|
||||
// Properly fix this by introducing partial data upload into the surface cache in such cases and making RCB/RDB
|
||||
// enabled by default. Blit engine already handles this correctly.
|
||||
// We work around the issue by forcing a background data load on the subresource to fill the missing data hole.
|
||||
result_is_valid = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
result_is_valid = result.atlas_covers_target_area(section_count == 1 ? 99 : 90);
|
||||
result.external_subresource_desc.force_bg_load = true;
|
||||
}
|
||||
|
||||
if (!result_is_valid)
|
||||
|
||||
@@ -46,21 +46,6 @@ namespace rsx
|
||||
blit_image_static, // Variant of the copy command that does scaling instead of copying
|
||||
};
|
||||
|
||||
struct image_section_attributes_t
|
||||
{
|
||||
u32 address;
|
||||
u32 gcm_format;
|
||||
u32 pitch;
|
||||
u16 width;
|
||||
u16 height;
|
||||
u16 depth;
|
||||
u16 mipmaps;
|
||||
u16 slice_h;
|
||||
u8 bpp;
|
||||
bool swizzled;
|
||||
bool edge_clamped;
|
||||
};
|
||||
|
||||
struct blit_op_result
|
||||
{
|
||||
bool succeeded = false;
|
||||
@@ -248,7 +233,7 @@ namespace rsx
|
||||
}
|
||||
|
||||
template<typename commandbuffer_type, typename section_storage_type, typename copy_region_type, typename surface_store_list_type>
|
||||
void gather_texture_slices(
|
||||
bool gather_texture_slices(
|
||||
commandbuffer_type& cmd,
|
||||
rsx::simple_array<copy_region_type>& out,
|
||||
const surface_store_list_type& fbos,
|
||||
@@ -535,6 +520,8 @@ namespace rsx
|
||||
rsx_log.warning("Could not gather textures into an atlas; using CPU fallback...");
|
||||
}
|
||||
}
|
||||
|
||||
return found_slices == count;
|
||||
}
|
||||
|
||||
template<typename render_target_type>
|
||||
@@ -761,12 +748,13 @@ namespace rsx
|
||||
}
|
||||
|
||||
texptr->memory_barrier(cmd, rsx::surface_access::transfer_read);
|
||||
auto format_class = rsx::classify_format(attr2.gcm_format);
|
||||
|
||||
if (extended_dimension == rsx::texture_dimension_extended::texture_dimension_3d)
|
||||
{
|
||||
return{ texptr->get_surface(rsx::surface_access::transfer_read), deferred_request_command::_3d_unwrap,
|
||||
attr2, {},
|
||||
texture_upload_context::framebuffer_storage, texptr->format_class(), scale,
|
||||
texture_upload_context::framebuffer_storage, format_class, scale,
|
||||
rsx::texture_dimension_extended::texture_dimension_3d, decoded_remap };
|
||||
}
|
||||
|
||||
@@ -774,7 +762,7 @@ namespace rsx
|
||||
|
||||
return{ texptr->get_surface(rsx::surface_access::transfer_read), deferred_request_command::cubemap_unwrap,
|
||||
attr2, {},
|
||||
texture_upload_context::framebuffer_storage, texptr->format_class(), scale,
|
||||
texture_upload_context::framebuffer_storage, format_class, scale,
|
||||
rsx::texture_dimension_extended::texture_dimension_cubemap, decoded_remap };
|
||||
}
|
||||
|
||||
@@ -858,7 +846,7 @@ namespace rsx
|
||||
upload_context, format_class, scale,
|
||||
rsx::texture_dimension_extended::texture_dimension_cubemap, decoded_remap };
|
||||
|
||||
gather_texture_slices(cmd, desc.external_subresource_desc.sections_to_copy, fbos, local, attr, 6, is_depth);
|
||||
desc.external_subresource_desc.force_bg_load = !gather_texture_slices(cmd, desc.external_subresource_desc.sections_to_copy, fbos, local, attr, 6, is_depth);
|
||||
return desc;
|
||||
}
|
||||
else if (extended_dimension == rsx::texture_dimension_extended::texture_dimension_3d && attr.depth > 1)
|
||||
@@ -871,7 +859,7 @@ namespace rsx
|
||||
upload_context, format_class, scale,
|
||||
rsx::texture_dimension_extended::texture_dimension_3d, decoded_remap };
|
||||
|
||||
gather_texture_slices(cmd, desc.external_subresource_desc.sections_to_copy, fbos, local, attr, attr.depth, is_depth);
|
||||
desc.external_subresource_desc.force_bg_load = !gather_texture_slices(cmd, desc.external_subresource_desc.sections_to_copy, fbos, local, attr, attr.depth, is_depth);
|
||||
return desc;
|
||||
}
|
||||
|
||||
@@ -890,7 +878,7 @@ namespace rsx
|
||||
attr2, {}, upload_context, format_class,
|
||||
scale, rsx::texture_dimension_extended::texture_dimension_2d, decoded_remap };
|
||||
|
||||
gather_texture_slices(cmd, result.external_subresource_desc.sections_to_copy, fbos, local, attr, 1, is_depth);
|
||||
result.external_subresource_desc.force_bg_load = !gather_texture_slices(cmd, result.external_subresource_desc.sections_to_copy, fbos, local, attr, 1, is_depth);
|
||||
result.simplify();
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -494,6 +494,58 @@ void GLGSRender::bind_texture_env()
|
||||
gl::command_context cmd{ gl_state };
|
||||
const bool is_interpreter = m_shader_interpreter.is_interpreter(m_program);
|
||||
|
||||
auto decay_view_for_interpreter = [&](
|
||||
const rsx::image_section_attributes_t& attr,
|
||||
gl::texture_cache::sampled_image_descriptor* desc,
|
||||
gl::texture_view* base,
|
||||
const rsx::texture_channel_remap_t& decoded_remap,
|
||||
bool is_msaa,
|
||||
bool is_redirected) -> gl::texture_view*
|
||||
{
|
||||
if (!is_msaa && !is_redirected)
|
||||
{
|
||||
return base;
|
||||
}
|
||||
|
||||
if (is_redirected && desc->image_type > rsx::texture_dimension_extended::texture_dimension_2d)
|
||||
{
|
||||
// Cannot handle redirect on 3D or cubemap with the interpreter.
|
||||
const auto target = gl::get_target(desc->image_type);
|
||||
return m_null_textures[target]->get_view(rsx::default_remap_vector);
|
||||
}
|
||||
|
||||
using deferred_subresource_t = gl::texture_cache::deferred_subresource;
|
||||
auto image = static_cast<gl::viewable_image*>(base->image());
|
||||
|
||||
if (is_msaa)
|
||||
{
|
||||
// MSAA resolve
|
||||
auto rtt = gl::as_rtt(base->image());
|
||||
rtt->memory_barrier(cmd, rsx::surface_access::transfer_read);
|
||||
image = rtt->get_surface(rsx::surface_access::transfer_read);
|
||||
}
|
||||
|
||||
if (is_redirected)
|
||||
{
|
||||
// Force bitcast
|
||||
deferred_subresource_t flatten_op{};
|
||||
flatten_op.address = desc->ref_address;
|
||||
flatten_op.external_handle = image;
|
||||
flatten_op.op = desc->is_cyclic_reference
|
||||
? rsx::deferred_request_command::copy_image_dynamic
|
||||
: rsx::deferred_request_command::copy_image_static;
|
||||
flatten_op.width = flatten_op.external_handle->width();
|
||||
flatten_op.height = flatten_op.external_handle->height();
|
||||
flatten_op.depth = 1;
|
||||
flatten_op.gcm_format = desc->format_ex.format();
|
||||
flatten_op.remap = decoded_remap;
|
||||
flatten_op.cache_range = utils::address_range32::start_length(desc->ref_address, attr.pitch * attr.height);
|
||||
return m_gl_texture_cache.create_temporary_subresource(cmd, flatten_op);
|
||||
}
|
||||
|
||||
return image->get_view(decoded_remap, base->aspect());
|
||||
};
|
||||
|
||||
for (u32 textures_ref = current_fp_metadata.referenced_textures_mask, i = 0; textures_ref; textures_ref >>= 1, ++i)
|
||||
{
|
||||
if (!(textures_ref & 1))
|
||||
@@ -505,8 +557,8 @@ void GLGSRender::bind_texture_env()
|
||||
gl::texture_view* stencil_mirror = nullptr;
|
||||
auto sampler_state = static_cast<gl::texture_cache::sampled_image_descriptor*>(fs_sampler_state[i].get());
|
||||
|
||||
if (rsx::method_registers.fragment_textures[i].enabled() &&
|
||||
sampler_state->validate())
|
||||
auto& tex = rsx::method_registers.fragment_textures[i];
|
||||
if (tex.enabled() && sampler_state->validate())
|
||||
{
|
||||
if (primary_view = sampler_state->image_handle; !primary_view) [[unlikely]]
|
||||
{
|
||||
@@ -533,7 +585,26 @@ void GLGSRender::bind_texture_env()
|
||||
|
||||
if (is_interpreter) [[ unlikely ]]
|
||||
{
|
||||
m_shader_interpreter.bind_fragment_texture(i, primary_view->handle(), *sampler_state);
|
||||
// Interpreter does not support MSAA or DEPTH->RGBA conversion a.k.a aspect redirection
|
||||
auto view = primary_view;
|
||||
if (primary_view->aspect() != gl::image_aspect::color || primary_view->image()->samples() != 1)
|
||||
{
|
||||
const auto mask = (1u << i);
|
||||
const bool is_redirected = !!(current_fragment_program.texture_state.redirected_textures & mask);
|
||||
const bool is_msaa = !!(current_fragment_program.texture_state.multisampled_textures & mask);
|
||||
if (is_redirected || is_msaa)
|
||||
{
|
||||
view = decay_view_for_interpreter(
|
||||
tex.attributes(),
|
||||
sampler_state,
|
||||
primary_view,
|
||||
tex.decoded_remap(),
|
||||
is_msaa,
|
||||
is_redirected);
|
||||
}
|
||||
}
|
||||
|
||||
m_shader_interpreter.bind_fragment_texture(i, view->handle(), *sampler_state);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -576,7 +647,15 @@ void GLGSRender::bind_texture_env()
|
||||
|
||||
if (is_interpreter)
|
||||
{
|
||||
m_shader_interpreter.flush_texture_bindings();
|
||||
if (current_fp_metadata.referenced_textures_mask)
|
||||
{
|
||||
m_shader_interpreter.flush_fragment_texture_bindings();
|
||||
}
|
||||
|
||||
if (current_vp_metadata.referenced_textures_mask)
|
||||
{
|
||||
m_shader_interpreter.flush_vertex_texture_bindings();
|
||||
}
|
||||
}
|
||||
|
||||
if (current_fragment_program.ctrl & RSX_SHADER_CONTROL_EMULATE_DEPTH_COMPARE)
|
||||
|
||||
@@ -295,7 +295,7 @@ namespace gl
|
||||
}
|
||||
}
|
||||
|
||||
u32 len;
|
||||
u32 len {};
|
||||
ParamArray arr;
|
||||
std::string shader_str;
|
||||
RSXFragmentProgram frag;
|
||||
@@ -408,6 +408,13 @@ namespace gl
|
||||
" uvec4 fp_instructions[];\n"
|
||||
"};\n\n";
|
||||
|
||||
const ::glsl::shader_properties properties
|
||||
{
|
||||
.domain = ::glsl::program_domain::glsl_fragment_program,
|
||||
.require_lit_emulation = true,
|
||||
};
|
||||
|
||||
::glsl::insert_glsl_legacy_function(builder, properties);
|
||||
builder << program_common::interpreter::get_fragment_interpreter();
|
||||
const std::string s = builder.str();
|
||||
|
||||
@@ -486,10 +493,15 @@ namespace gl
|
||||
data->prog->uniforms[0] = GL_STREAM_BUFFER_START + 0;
|
||||
data->prog->uniforms[1] = GL_STREAM_BUFFER_START + 1;
|
||||
|
||||
// Initialize texture bindings
|
||||
if (compiler_options & COMPILER_OPT_ENABLE_TEXTURES)
|
||||
{
|
||||
// Initialize texture bindings
|
||||
flush_texture_bindings(data->prog.get());
|
||||
flush_fragment_texture_bindings(data->prog.get());
|
||||
}
|
||||
|
||||
if (compiler_options & COMPILER_OPT_ENABLE_VTX_TEXTURES)
|
||||
{
|
||||
flush_vertex_texture_bindings(data->prog.get());
|
||||
}
|
||||
|
||||
data->flags &= ~CACHED_PIPE_UNINITIALIZED;
|
||||
@@ -518,7 +530,7 @@ namespace gl
|
||||
}
|
||||
}
|
||||
|
||||
void shader_interpreter::flush_texture_bindings(glsl::program* program)
|
||||
void shader_interpreter::flush_fragment_texture_bindings(glsl::program* program)
|
||||
{
|
||||
using enum rsx::texture_dimension_extended;
|
||||
|
||||
@@ -529,9 +541,15 @@ namespace gl
|
||||
}
|
||||
|
||||
const bool is_bound_interpreter = m_current_interpreter && m_current_interpreter->prog.get() == program;
|
||||
const u32 dirty_mask = is_bound_interpreter
|
||||
? 0xff
|
||||
: m_texture_bindings.dirty;
|
||||
u32 dirty_mask = m_texture_bindings.dirty;
|
||||
|
||||
if (is_bound_interpreter) [[ likely ]]
|
||||
{
|
||||
// Check if we even support textures
|
||||
dirty_mask = (m_current_interpreter->build_compiler_options & COMPILER_OPT_ENABLE_TEXTURES)
|
||||
? 0xff
|
||||
: 0x0;
|
||||
}
|
||||
|
||||
if (!dirty_mask)
|
||||
{
|
||||
@@ -557,4 +575,9 @@ namespace gl
|
||||
m_texture_bindings.dirty = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void shader_interpreter::flush_vertex_texture_bindings(glsl::program* program)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,7 +132,8 @@ namespace gl
|
||||
|
||||
// Update texture bindings based on the incoming descriptor structures
|
||||
void bind_fragment_texture(int i, handle64_t handle, const rsx::sampled_image_descriptor_base& descriptor);
|
||||
void flush_texture_bindings(glsl::program* program = nullptr);
|
||||
void flush_fragment_texture_bindings(glsl::program* program = nullptr);
|
||||
void flush_vertex_texture_bindings(glsl::program* program = nullptr);
|
||||
|
||||
glsl::program* get(const interpreter::program_metadata& fp_metadata, u32 vp_ctrl, u32 fp_ctrl);
|
||||
bool is_interpreter(const glsl::program* program) const;
|
||||
|
||||
@@ -664,6 +664,12 @@ namespace gl
|
||||
|
||||
for (const rsx::subresource_layout& layout : input_layouts)
|
||||
{
|
||||
if (layout.level >= dst->levels())
|
||||
{
|
||||
rsx_log.error("Invalid subresource definition for the output texture. Mip level does not exist.");
|
||||
continue;
|
||||
}
|
||||
|
||||
rsx::io_buffer io_buf = staging_buffer;
|
||||
upload_texture_subresource(io_buf, layout, format, is_swizzled, caps);
|
||||
|
||||
@@ -741,6 +747,12 @@ namespace gl
|
||||
|
||||
for (const rsx::subresource_layout& layout : input_layouts)
|
||||
{
|
||||
if (layout.level >= dst->levels())
|
||||
{
|
||||
rsx_log.error("Invalid subresource definition for the output texture. Mip level does not exist.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (driver_caps.ARB_compute_shader_supported)
|
||||
{
|
||||
u64 row_pitch = rsx::align2<u64, u64>(layout.width_in_block * block_size_in_bytes, caps.alignment);
|
||||
|
||||
@@ -189,6 +189,12 @@ namespace gl
|
||||
return dst->get_view(remap);
|
||||
}
|
||||
|
||||
void texture_cache::initialize_subresource_from_memory(gl::command_context& cmd, gl::texture* dst, const deferred_subresource& desc, rsx::texture_dimension_extended type) const
|
||||
{
|
||||
const auto subresources_layout = rsx::get_subresources_layout(desc, type);
|
||||
gl::upload_texture(cmd, dst, desc.gcm_format, desc.swizzled, subresources_layout);
|
||||
}
|
||||
|
||||
void texture_cache::copy_transfer_regions_impl(gl::command_context& cmd, gl::texture* dst_image, const rsx::simple_array<copy_region_descriptor>& sources) const
|
||||
{
|
||||
const auto dst_bpp = dst_image->pitch() / dst_image->width();
|
||||
|
||||
@@ -494,6 +494,8 @@ namespace gl
|
||||
m_temporary_surfaces.clear();
|
||||
}
|
||||
|
||||
void initialize_subresource_from_memory(gl::command_context& cmd, gl::texture* dst, const deferred_subresource& desc, rsx::texture_dimension_extended type) const;
|
||||
|
||||
gl::texture_view* create_temporary_subresource_impl(gl::command_context& cmd, gl::texture* src, GLenum sized_internal_fmt, GLenum dst_type, u32 gcm_format,
|
||||
u16 x, u16 y, u16 width, u16 height, u16 depth, u8 mipmaps, const rsx::texture_channel_remap_t& remap, bool copy);
|
||||
|
||||
@@ -571,56 +573,67 @@ namespace gl
|
||||
|
||||
protected:
|
||||
|
||||
gl::texture_view* create_temporary_subresource_view(gl::command_context& cmd, gl::texture** src, u32 gcm_format, u16 x, u16 y, u16 w, u16 h,
|
||||
const rsx::texture_channel_remap_t& remap_vector) override
|
||||
gl::texture_view* create_temporary_subresource_view(gl::command_context& cmd, const deferred_subresource& desc) override
|
||||
{
|
||||
return create_temporary_subresource_impl(cmd, *src, GL_NONE, GL_TEXTURE_2D, gcm_format, x, y, w, h, 1, 1, remap_vector, true);
|
||||
return create_temporary_subresource_impl(cmd, desc.external_handle, static_cast<GLenum>(desc.external_handle->get_internal_format()),
|
||||
GL_TEXTURE_2D, desc.gcm_format, desc.x, desc.y, desc.width, desc.height, 1, 1, desc.remap, true);
|
||||
}
|
||||
|
||||
gl::texture_view* create_temporary_subresource_view(gl::command_context& cmd, gl::texture* src, u32 gcm_format, u16 x, u16 y, u16 w, u16 h,
|
||||
const rsx::texture_channel_remap_t& remap_vector) override
|
||||
gl::texture_view* generate_cubemap_from_images(gl::command_context& cmd, const deferred_subresource& desc) override
|
||||
{
|
||||
return create_temporary_subresource_impl(cmd, src, static_cast<GLenum>(src->get_internal_format()),
|
||||
GL_TEXTURE_2D, gcm_format, x, y, w, h, 1, 1, remap_vector, true);
|
||||
}
|
||||
auto _template = get_template_from_collection_impl(desc.sections_to_copy);
|
||||
const u8 mip_count = 1 + desc.sections_to_copy.reduce(0, FN(std::max<u8>(x, y.level)));
|
||||
auto result = create_temporary_subresource_impl(cmd, _template, GL_NONE, GL_TEXTURE_CUBE_MAP, desc.gcm_format, 0, 0, desc.width, desc.height, 1, mip_count, desc.remap, false);
|
||||
|
||||
gl::texture_view* generate_cubemap_from_images(gl::command_context& cmd, u32 gcm_format, u16 size, const rsx::simple_array<copy_region_descriptor>& sources, const rsx::texture_channel_remap_t& remap_vector) override
|
||||
{
|
||||
auto _template = get_template_from_collection_impl(sources);
|
||||
const u8 mip_count = 1 + sources.reduce(0, FN(std::max<u8>(x, y.level)));
|
||||
auto result = create_temporary_subresource_impl(cmd, _template, GL_NONE, GL_TEXTURE_CUBE_MAP, gcm_format, 0, 0, size, size, 1, mip_count, remap_vector, false);
|
||||
if (desc.force_bg_load)
|
||||
{
|
||||
initialize_subresource_from_memory(cmd, result->image(), desc, rsx::texture_dimension_extended::texture_dimension_cubemap);
|
||||
}
|
||||
|
||||
copy_transfer_regions_impl(cmd, result->image(), sources);
|
||||
copy_transfer_regions_impl(cmd, result->image(), desc.sections_to_copy);
|
||||
return result;
|
||||
}
|
||||
|
||||
gl::texture_view* generate_3d_from_2d_images(gl::command_context& cmd, u32 gcm_format, u16 width, u16 height, u16 depth, const rsx::simple_array<copy_region_descriptor>& sources, const rsx::texture_channel_remap_t& remap_vector) override
|
||||
gl::texture_view* generate_3d_from_2d_images(gl::command_context& cmd, const deferred_subresource& desc) override
|
||||
{
|
||||
auto _template = get_template_from_collection_impl(sources);
|
||||
auto result = create_temporary_subresource_impl(cmd, _template, GL_NONE, GL_TEXTURE_3D, gcm_format, 0, 0, width, height, depth, 1, remap_vector, false);
|
||||
auto _template = get_template_from_collection_impl(desc.sections_to_copy);
|
||||
auto result = create_temporary_subresource_impl(cmd, _template, GL_NONE, GL_TEXTURE_3D, desc.gcm_format, 0, 0, desc.width, desc.height, desc.depth, 1, desc.remap, false);
|
||||
|
||||
copy_transfer_regions_impl(cmd, result->image(), sources);
|
||||
if (desc.force_bg_load)
|
||||
{
|
||||
initialize_subresource_from_memory(cmd, result->image(), desc, rsx::texture_dimension_extended::texture_dimension_3d);
|
||||
}
|
||||
|
||||
copy_transfer_regions_impl(cmd, result->image(), desc.sections_to_copy);
|
||||
return result;
|
||||
}
|
||||
|
||||
gl::texture_view* generate_atlas_from_images(gl::command_context& cmd, u32 gcm_format, u16 width, u16 height, const rsx::simple_array<copy_region_descriptor>& sections_to_copy,
|
||||
const rsx::texture_channel_remap_t& remap_vector) override
|
||||
gl::texture_view* generate_atlas_from_images(gl::command_context& cmd, const deferred_subresource& desc) override
|
||||
{
|
||||
auto _template = get_template_from_collection_impl(sections_to_copy);
|
||||
auto result = create_temporary_subresource_impl(cmd, _template, GL_NONE, GL_TEXTURE_2D, gcm_format, 0, 0, width, height, 1, 1, remap_vector, false);
|
||||
auto _template = get_template_from_collection_impl(desc.sections_to_copy);
|
||||
auto result = create_temporary_subresource_impl(cmd, _template, GL_NONE, GL_TEXTURE_2D, desc.gcm_format, 0, 0, desc.width, desc.height, 1, 1, desc.remap, false);
|
||||
|
||||
copy_transfer_regions_impl(cmd, result->image(), sections_to_copy);
|
||||
if (desc.force_bg_load)
|
||||
{
|
||||
initialize_subresource_from_memory(cmd, result->image(), desc, rsx::texture_dimension_extended::texture_dimension_2d);
|
||||
}
|
||||
|
||||
copy_transfer_regions_impl(cmd, result->image(), desc.sections_to_copy);
|
||||
return result;
|
||||
}
|
||||
|
||||
gl::texture_view* generate_2d_mipmaps_from_images(gl::command_context& cmd, u32 gcm_format, u16 width, u16 height, const rsx::simple_array<copy_region_descriptor>& sections_to_copy,
|
||||
const rsx::texture_channel_remap_t& remap_vector) override
|
||||
gl::texture_view* generate_2d_mipmaps_from_images(gl::command_context& cmd, const deferred_subresource& desc) override
|
||||
{
|
||||
const auto mipmaps = ::narrow<u8>(sections_to_copy.size());
|
||||
auto _template = get_template_from_collection_impl(sections_to_copy);
|
||||
auto result = create_temporary_subresource_impl(cmd, _template, GL_NONE, GL_TEXTURE_2D, gcm_format, 0, 0, width, height, 1, mipmaps, remap_vector, false);
|
||||
const auto mipmaps = ::narrow<u8>(desc.sections_to_copy.size());
|
||||
auto _template = get_template_from_collection_impl(desc.sections_to_copy);
|
||||
auto result = create_temporary_subresource_impl(cmd, _template, GL_NONE, GL_TEXTURE_2D, desc.gcm_format, 0, 0, desc.width, desc.height, 1, mipmaps, desc.remap, false);
|
||||
|
||||
copy_transfer_regions_impl(cmd, result->image(), sections_to_copy);
|
||||
if (desc.force_bg_load)
|
||||
{
|
||||
initialize_subresource_from_memory(cmd, result->image(), desc, rsx::texture_dimension_extended::texture_dimension_2d);
|
||||
}
|
||||
|
||||
copy_transfer_regions_impl(cmd, result->image(), desc.sections_to_copy);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "stdafx.h"
|
||||
#include "../overlay_manager.h"
|
||||
#include "overlay_friends_list_dialog.h"
|
||||
#include "Emu/NP/np_handler.h"
|
||||
#include "Emu/NP/rpcn_config.h"
|
||||
#include "Emu/vfs_config.h"
|
||||
|
||||
@@ -14,12 +15,14 @@ namespace rsx
|
||||
dlg->callback_handler(ntype, username, status);
|
||||
}
|
||||
|
||||
void game_invite_callback(void* param, const shared_ptr<std::pair<std::string, message_data>> new_msg, u64 msg_id)
|
||||
{
|
||||
auto* dlg = static_cast<friends_list_dialog*>(param);
|
||||
dlg->message_callback_handler(new_msg, msg_id);
|
||||
}
|
||||
|
||||
friends_list_dialog::friends_list_entry::friends_list_entry(friends_list_dialog_page page, std::string_view username, const rpcn::friend_online_data& data)
|
||||
{
|
||||
std::unique_ptr<overlay_element> image = std::make_unique<image_view>();
|
||||
image->set_size(160, 110);
|
||||
image->set_padding(36, 36, 11, 11); // Square image, 88x88
|
||||
|
||||
std::string avatar_path = g_cfg_vfs.get_dev_flash() + "vsh/resource/explore/user/";
|
||||
std::string text;
|
||||
|
||||
@@ -44,13 +47,17 @@ namespace rsx
|
||||
}
|
||||
break;
|
||||
}
|
||||
case friends_list_dialog_page::invites:
|
||||
case friends_list_dialog_page::friend_requests:
|
||||
{
|
||||
// We use "online" to show whether an invite was sent or received
|
||||
avatar_path += data.online ? "012.png" : "011.png";
|
||||
text = get_localized_string(data.online ? localized_string_id::HOME_MENU_FRIENDS_REQUEST_RECEIVED : localized_string_id::HOME_MENU_FRIENDS_REQUEST_SENT);
|
||||
break;
|
||||
}
|
||||
case friends_list_dialog_page::game_invites:
|
||||
{
|
||||
fmt::throw_exception("Unexpected game invitation in friend entry constructor");
|
||||
}
|
||||
case friends_list_dialog_page::blocked:
|
||||
{
|
||||
avatar_path += "010.png";
|
||||
@@ -59,6 +66,30 @@ namespace rsx
|
||||
}
|
||||
}
|
||||
|
||||
init(username, text, std::move(avatar_path));
|
||||
}
|
||||
|
||||
friends_list_dialog::friends_list_entry::friends_list_entry(std::string_view username, const message_data& message)
|
||||
{
|
||||
std::string text = message.subject;
|
||||
|
||||
if (!message.body.empty())
|
||||
{
|
||||
if (!text.empty())
|
||||
text += '\n';
|
||||
|
||||
text += message.body;
|
||||
}
|
||||
|
||||
init(username, text, g_cfg_vfs.get_dev_flash() + "vsh/resource/explore/user/012.png");
|
||||
}
|
||||
|
||||
void friends_list_dialog::friends_list_entry::init(std::string_view username, std::string_view text, std::string avatar_path)
|
||||
{
|
||||
std::unique_ptr<overlay_element> image = std::make_unique<image_view>();
|
||||
image->set_size(160, 110);
|
||||
image->set_padding(36, 36, 11, 11); // Square image, 88x88
|
||||
|
||||
if (fs::exists(avatar_path))
|
||||
{
|
||||
icon_data = std::make_unique<image_info>(avatar_path);
|
||||
@@ -180,7 +211,9 @@ namespace rsx
|
||||
if (!m_list || m_list->m_items.empty())
|
||||
break;
|
||||
|
||||
if (button_press == pad_button::triangle && m_current_page != friends_list_dialog_page::invites)
|
||||
if (button_press == pad_button::triangle &&
|
||||
m_current_page != friends_list_dialog_page::friend_requests &&
|
||||
m_current_page != friends_list_dialog_page::game_invites)
|
||||
break;
|
||||
|
||||
const usz index = static_cast<usz>(m_list->get_selected_index());
|
||||
@@ -222,7 +255,7 @@ namespace rsx
|
||||
}
|
||||
break;
|
||||
}
|
||||
case friends_list_dialog_page::invites:
|
||||
case friends_list_dialog_page::friend_requests:
|
||||
{
|
||||
// Get selected user
|
||||
usz user_index = 0;
|
||||
@@ -279,6 +312,40 @@ namespace rsx
|
||||
}
|
||||
break;
|
||||
}
|
||||
case friends_list_dialog_page::game_invites:
|
||||
{
|
||||
if (index >= m_game_invites.size() || !m_message_box || m_message_box->visible)
|
||||
break;
|
||||
|
||||
const auto& [message_id, message] = ::at32(m_game_invites, index);
|
||||
ensure(message);
|
||||
|
||||
const localized_string_id prompt = button_press == pad_button::cross ?
|
||||
localized_string_id::HOME_MENU_FRIENDS_ACCEPT_GAME_INVITE_MSG :
|
||||
localized_string_id::HOME_MENU_FRIENDS_REJECT_GAME_INVITE_MSG;
|
||||
|
||||
if (button_press == pad_button::cross)
|
||||
{
|
||||
m_message_box->show(get_localized_string(prompt, message->first.c_str()), [this, message_id]()
|
||||
{
|
||||
if (g_fxo->get<named_thread<np::np_handler>>().select_invitation(message_id))
|
||||
{
|
||||
remove_game_invite(message_id);
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
m_message_box->show(get_localized_string(prompt, message->first.c_str()), [this, message_id]()
|
||||
{
|
||||
m_rpcn->mark_message_used(message_id);
|
||||
remove_game_invite(message_id);
|
||||
});
|
||||
}
|
||||
|
||||
refresh();
|
||||
break;
|
||||
}
|
||||
case friends_list_dialog_page::blocked:
|
||||
{
|
||||
// Get selected user
|
||||
@@ -315,8 +382,9 @@ namespace rsx
|
||||
case pad_button::square:
|
||||
switch (m_current_page)
|
||||
{
|
||||
case friends_list_dialog_page::friends: m_current_page = friends_list_dialog_page::invites; break;
|
||||
case friends_list_dialog_page::invites: m_current_page = friends_list_dialog_page::blocked; break;
|
||||
case friends_list_dialog_page::friends: m_current_page = friends_list_dialog_page::friend_requests; break;
|
||||
case friends_list_dialog_page::friend_requests: m_current_page = friends_list_dialog_page::game_invites; break;
|
||||
case friends_list_dialog_page::game_invites: m_current_page = friends_list_dialog_page::blocked; break;
|
||||
case friends_list_dialog_page::blocked: m_current_page = friends_list_dialog_page::friends; break;
|
||||
}
|
||||
m_list_dirty = true;
|
||||
@@ -382,7 +450,8 @@ namespace rsx
|
||||
switch (m_current_page)
|
||||
{
|
||||
case friends_list_dialog_page::friends: title_id = localized_string_id::HOME_MENU_FRIENDS; break;
|
||||
case friends_list_dialog_page::invites: title_id = localized_string_id::HOME_MENU_FRIENDS_REQUESTS; break;
|
||||
case friends_list_dialog_page::friend_requests: title_id = localized_string_id::HOME_MENU_FRIENDS_REQUESTS; break;
|
||||
case friends_list_dialog_page::game_invites: title_id = localized_string_id::HOME_MENU_FRIENDS_GAME_INVITES; break;
|
||||
case friends_list_dialog_page::blocked: title_id = localized_string_id::HOME_MENU_FRIENDS_BLOCKED; break;
|
||||
}
|
||||
m_description->set_text(get_localized_string(title_id));
|
||||
@@ -404,16 +473,20 @@ namespace rsx
|
||||
{
|
||||
result.add(m_list->get_compiled());
|
||||
|
||||
if (!m_list->m_items.empty() && m_current_page == friends_list_dialog_page::invites)
|
||||
if (!m_list->m_items.empty())
|
||||
{
|
||||
// Get selected user
|
||||
const usz index = static_cast<usz>(m_list->get_selected_index());
|
||||
|
||||
if (index < m_friend_data.requests_received.size())
|
||||
if (m_current_page == friends_list_dialog_page::friend_requests && index < m_friend_data.requests_received.size())
|
||||
{
|
||||
m_extra_btn.set_text(get_localized_string(localized_string_id::HOME_MENU_FRIENDS_REJECT_REQUEST));
|
||||
result.add(m_extra_btn.get_compiled());
|
||||
}
|
||||
else if (m_current_page == friends_list_dialog_page::game_invites && index < m_game_invites.size())
|
||||
{
|
||||
m_extra_btn.set_text(get_localized_string(localized_string_id::HOME_MENU_FRIENDS_REJECT_GAME_INVITE));
|
||||
result.add(m_extra_btn.get_compiled());
|
||||
}
|
||||
}
|
||||
}
|
||||
result.add(m_page_btn.get_compiled());
|
||||
@@ -447,10 +520,38 @@ namespace rsx
|
||||
}
|
||||
}
|
||||
|
||||
void friends_list_dialog::message_callback_handler(const shared_ptr<std::pair<std::string, message_data>>& message, u64 message_id)
|
||||
{
|
||||
std::lock_guard lock(m_list_mutex);
|
||||
|
||||
if (std::none_of(m_game_invite_messages.cbegin(), m_game_invite_messages.cend(), [message_id](const game_invite& invite)
|
||||
{
|
||||
return invite.first == message_id;
|
||||
}))
|
||||
{
|
||||
m_game_invite_messages.emplace_back(message_id, message);
|
||||
m_list_dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
void friends_list_dialog::remove_game_invite(u64 message_id)
|
||||
{
|
||||
std::lock_guard lock(m_list_mutex);
|
||||
const auto has_message_id = [message_id](const game_invite& invite)
|
||||
{
|
||||
return invite.first == message_id;
|
||||
};
|
||||
|
||||
std::erase_if(m_game_invite_messages, has_message_id);
|
||||
std::erase_if(m_game_invites, has_message_id);
|
||||
m_list_dirty = true;
|
||||
}
|
||||
|
||||
void friends_list_dialog::reload()
|
||||
{
|
||||
std::vector<std::unique_ptr<overlay_element>> entries;
|
||||
std::string selected_user;
|
||||
u64 selected_message_id = 0;
|
||||
s32 selected_index = 0;
|
||||
bool rpcn_connected = true;
|
||||
|
||||
@@ -474,7 +575,7 @@ namespace rsx
|
||||
}
|
||||
break;
|
||||
}
|
||||
case friends_list_dialog_page::invites:
|
||||
case friends_list_dialog_page::friend_requests:
|
||||
{
|
||||
for (const std::string& username : m_friend_data.requests_received)
|
||||
{
|
||||
@@ -497,6 +598,14 @@ namespace rsx
|
||||
}
|
||||
break;
|
||||
}
|
||||
case friends_list_dialog_page::game_invites:
|
||||
{
|
||||
if (old_index >= 0 && static_cast<usz>(old_index) < m_game_invites.size())
|
||||
{
|
||||
selected_message_id = ::at32(m_game_invites, old_index).first;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case friends_list_dialog_page::blocked:
|
||||
{
|
||||
for (const std::string& username : m_friend_data.blocked)
|
||||
@@ -578,7 +687,7 @@ namespace rsx
|
||||
}
|
||||
break;
|
||||
}
|
||||
case friends_list_dialog_page::invites:
|
||||
case friends_list_dialog_page::friend_requests:
|
||||
{
|
||||
for (const std::string& username : m_friend_data.requests_received)
|
||||
{
|
||||
@@ -602,6 +711,38 @@ namespace rsx
|
||||
}
|
||||
break;
|
||||
}
|
||||
case friends_list_dialog_page::game_invites:
|
||||
{
|
||||
m_game_invites.clear();
|
||||
|
||||
if (!rpcn_connected)
|
||||
break;
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
if (!nph.basic_handler_registered)
|
||||
break;
|
||||
|
||||
const SceNpCommunicationId context = nph.get_basic_handler_context();
|
||||
|
||||
for (const auto& [message_id, message] : m_game_invite_messages)
|
||||
{
|
||||
ensure(message);
|
||||
|
||||
if (std::strncmp(message->second.commId.data, context.data, sizeof(context.data) - 1) != 0)
|
||||
continue;
|
||||
|
||||
if (message_id == selected_message_id)
|
||||
{
|
||||
selected_index = ::size32(entries);
|
||||
}
|
||||
|
||||
m_game_invites.emplace_back(message_id, message);
|
||||
std::unique_ptr<overlay_element> entry = std::make_unique<friends_list_entry>(message->first, message->second);
|
||||
entries.emplace_back(std::move(entry));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case friends_list_dialog_page::blocked:
|
||||
{
|
||||
for (const std::string& username : m_friend_data.blocked)
|
||||
@@ -656,7 +797,20 @@ namespace rsx
|
||||
|
||||
m_rpcn = rpcn::rpcn_client::get_instance(0);
|
||||
|
||||
{
|
||||
std::lock_guard lock(m_list_mutex);
|
||||
m_game_invite_messages.clear();
|
||||
m_game_invites.clear();
|
||||
m_list_dirty = true;
|
||||
}
|
||||
|
||||
m_rpcn->register_friend_cb(friend_callback, this);
|
||||
const auto game_invites = m_rpcn->get_messages_and_register_cb(SCE_NP_BASIC_MESSAGE_MAIN_TYPE_INVITE, true, game_invite_callback, this);
|
||||
|
||||
for (const auto& [message_id, message] : game_invites)
|
||||
{
|
||||
message_callback_handler(message, message_id);
|
||||
}
|
||||
|
||||
m_description->set_text(get_localized_string(localized_string_id::HOME_MENU_FRIENDS));
|
||||
m_description->auto_resize();
|
||||
@@ -681,6 +835,9 @@ namespace rsx
|
||||
notify->wait(0, atomic_wait_timeout{1'000'000});
|
||||
}
|
||||
|
||||
m_rpcn->remove_message_cb(game_invite_callback, this);
|
||||
m_rpcn->remove_friend_cb(friend_callback, this);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,8 @@ namespace rsx
|
||||
enum class friends_list_dialog_page
|
||||
{
|
||||
friends,
|
||||
invites,
|
||||
friend_requests,
|
||||
game_invites,
|
||||
blocked
|
||||
};
|
||||
|
||||
@@ -24,11 +25,15 @@ namespace rsx
|
||||
{
|
||||
private:
|
||||
std::unique_ptr<image_info> icon_data;
|
||||
void init(std::string_view username, std::string_view text, std::string avatar_path);
|
||||
|
||||
public:
|
||||
friends_list_entry(friends_list_dialog_page page, std::string_view username, const rpcn::friend_online_data& data);
|
||||
friends_list_entry(std::string_view username, const message_data& message);
|
||||
};
|
||||
|
||||
using game_invite = std::pair<u64, shared_ptr<std::pair<std::string, message_data>>>;
|
||||
|
||||
std::mutex m_list_mutex;
|
||||
std::vector<u32> m_entry_ids;
|
||||
std::unique_ptr<overlay_element> m_dim_background;
|
||||
@@ -42,11 +47,14 @@ namespace rsx
|
||||
|
||||
std::shared_ptr<rpcn::rpcn_client> m_rpcn;
|
||||
rpcn::friend_data m_friend_data;
|
||||
std::vector<game_invite> m_game_invite_messages;
|
||||
std::vector<game_invite> m_game_invites;
|
||||
atomic_t<bool> m_list_dirty { true };
|
||||
atomic_t<friends_list_dialog_page> m_current_page { friends_list_dialog_page::friends };
|
||||
atomic_t<friends_list_dialog_page> m_last_page { friends_list_dialog_page::friends };
|
||||
|
||||
void reload();
|
||||
void remove_game_invite(u64 message_id);
|
||||
|
||||
public:
|
||||
friends_list_dialog();
|
||||
@@ -59,6 +67,7 @@ namespace rsx
|
||||
error_code show(bool enable_overlay, std::function<void(s32 status)> on_close);
|
||||
|
||||
void callback_handler(rpcn::NotificationType ntype, const std::string& username, bool status);
|
||||
void message_callback_handler(const shared_ptr<std::pair<std::string, message_data>>& message, u64 message_id);
|
||||
|
||||
static bool rpcn_configured();
|
||||
};
|
||||
|
||||
@@ -103,6 +103,13 @@ namespace rsx
|
||||
m_description->auto_resize();
|
||||
m_description->back_color.a = 0.f;
|
||||
|
||||
m_sort_button = std::make_unique<image_button>();
|
||||
m_sort_button->set_text(localized_string_id::HOME_MENU_TROPHY_SORT_GAME_DEFAULT);
|
||||
m_sort_button->set_image_resource(resource_config::standard_image_resource::triangle);
|
||||
m_sort_button->set_size(120, 30);
|
||||
m_sort_button->set_pos(560, trophy_list_y + trophy_list_h + 20);
|
||||
m_sort_button->set_font("Arial", 16);
|
||||
|
||||
m_show_hidden_trophies_button = std::make_unique<image_button>();
|
||||
m_show_hidden_trophies_button->set_text(m_show_hidden_trophies ? localized_string_id::HOME_MENU_TROPHY_HIDE_HIDDEN_TROPHIES : localized_string_id::HOME_MENU_TROPHY_SHOW_HIDDEN_TROPHIES);
|
||||
m_show_hidden_trophies_button->set_image_resource(resource_config::standard_image_resource::square);
|
||||
@@ -139,6 +146,19 @@ namespace rsx
|
||||
m_show_hidden_trophies = !m_show_hidden_trophies;
|
||||
m_list_dirty = true;
|
||||
break;
|
||||
case pad_button::triangle:
|
||||
{
|
||||
// Cycle through sort modes
|
||||
switch (m_sort_mode)
|
||||
{
|
||||
case trophy_sort_mode::game_default: m_sort_mode = trophy_sort_mode::not_earned; break;
|
||||
case trophy_sort_mode::not_earned: m_sort_mode = trophy_sort_mode::earned_date; break;
|
||||
case trophy_sort_mode::earned_date: m_sort_mode = trophy_sort_mode::grade; break;
|
||||
case trophy_sort_mode::grade: m_sort_mode = trophy_sort_mode::game_default; break;
|
||||
}
|
||||
m_list_dirty = true;
|
||||
break;
|
||||
}
|
||||
case pad_button::dpad_up:
|
||||
case pad_button::ls_up:
|
||||
m_list->select_previous();
|
||||
@@ -189,6 +209,20 @@ namespace rsx
|
||||
m_show_hidden_trophies_last = m_show_hidden_trophies;
|
||||
}
|
||||
|
||||
if (m_sort_mode_last != m_sort_mode)
|
||||
{
|
||||
localized_string_id sort_label_id;
|
||||
switch (m_sort_mode)
|
||||
{
|
||||
case trophy_sort_mode::not_earned: sort_label_id = localized_string_id::HOME_MENU_TROPHY_SORT_NOT_EARNED; break;
|
||||
case trophy_sort_mode::earned_date: sort_label_id = localized_string_id::HOME_MENU_TROPHY_SORT_EARNED_DATE; break;
|
||||
case trophy_sort_mode::grade: sort_label_id = localized_string_id::HOME_MENU_TROPHY_SORT_GRADE; break;
|
||||
default: sort_label_id = localized_string_id::HOME_MENU_TROPHY_SORT_GAME_DEFAULT; break;
|
||||
}
|
||||
m_sort_button->set_text(sort_label_id);
|
||||
m_sort_mode_last = m_sort_mode;
|
||||
}
|
||||
|
||||
compiled_resource result;
|
||||
result.add(m_dim_background->get_compiled());
|
||||
if (m_list_dirty.exchange(false))
|
||||
@@ -200,6 +234,7 @@ namespace rsx
|
||||
result.add(m_list->get_compiled());
|
||||
}
|
||||
result.add(m_description->get_compiled());
|
||||
result.add(m_sort_button->get_compiled());
|
||||
result.add(m_show_hidden_trophies_button->get_compiled());
|
||||
|
||||
fade_animation.apply(result);
|
||||
@@ -311,13 +346,10 @@ namespace rsx
|
||||
|
||||
rsx_log.trace("Reloading Trophy List Overlay with %s %s", m_trophy_data->game_name, m_trophy_data->path);
|
||||
|
||||
std::string selected_trophy;
|
||||
s32 selected_index = 0;
|
||||
s32 selected_index = m_list ? m_list->get_selected_index() : 0;
|
||||
const overlay_element* old_trophy = m_list ? m_list->get_selected_entry() : nullptr;
|
||||
const s32 old_trophy_id = old_trophy ? static_cast<const trophy_list_entry*>(old_trophy)->trophy_id : 0;
|
||||
|
||||
std::vector<std::unique_ptr<overlay_element>> entries;
|
||||
|
||||
const int all_trophies = m_trophy_data->trop_usr->GetTrophiesCount();
|
||||
const int unlocked_trophies = m_trophy_data->trop_usr->GetUnlockedTrophiesCount();
|
||||
const int percentage = (all_trophies > 0) ? (100 * unlocked_trophies / all_trophies) : 0;
|
||||
@@ -331,6 +363,17 @@ namespace rsx
|
||||
const std::string hidden_title = get_localized_string(localized_string_id::HOME_MENU_TROPHY_HIDDEN_TITLE);
|
||||
const std::string hidden_description = get_localized_string(localized_string_id::HOME_MENU_TROPHY_HIDDEN_DESCRIPTION);
|
||||
|
||||
struct trophy_entry_data
|
||||
{
|
||||
SceNpTrophyDetails details{};
|
||||
std::string icon_path;
|
||||
bool unlocked = false;
|
||||
bool platinum_relevant = false;
|
||||
u64 timestamp = 0;
|
||||
};
|
||||
|
||||
std::vector<trophy_entry_data> trophy_entries;
|
||||
|
||||
for (std::shared_ptr<rXmlNode> n = trophy_base ? trophy_base->GetChildren() : nullptr; n; n = n->GetNext())
|
||||
{
|
||||
// Only show trophies.
|
||||
@@ -340,44 +383,43 @@ namespace rsx
|
||||
}
|
||||
|
||||
// Get data (stolen graciously from sceNpTrophy.cpp)
|
||||
SceNpTrophyDetails details{};
|
||||
details.trophyId = atoi(n->GetAttribute("id").c_str());
|
||||
details.hidden = n->GetAttribute("hidden")[0] == 'y';
|
||||
trophy_entry_data entry{};
|
||||
entry.details.trophyId = atoi(n->GetAttribute("id").c_str());
|
||||
entry.details.hidden = n->GetAttribute("hidden")[0] == 'y';
|
||||
|
||||
const bool unlocked = m_trophy_data->trop_usr->GetTrophyUnlockState(details.trophyId);
|
||||
const bool hide_trophy = details.hidden && !unlocked && !m_show_hidden_trophies;
|
||||
entry.unlocked = m_trophy_data->trop_usr->GetTrophyUnlockState(entry.details.trophyId);
|
||||
entry.timestamp = m_trophy_data->trop_usr->GetTrophyTimestamp(entry.details.trophyId);
|
||||
|
||||
if (details.trophyId == old_trophy_id)
|
||||
const bool hide_trophy = entry.details.hidden && !entry.unlocked && !m_show_hidden_trophies;
|
||||
if (hide_trophy)
|
||||
{
|
||||
// Select this entry if the trophy is visible. Use the previous index otherwise.
|
||||
const s32 index = static_cast<s32>(entries.size());
|
||||
selected_index = hide_trophy ? std::max(0, index - 1) : index;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (hide_trophy)
|
||||
if (m_sort_mode == trophy_sort_mode::not_earned && entry.unlocked)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get platinum link id (we assume there only exists one platinum trophy per game for now)
|
||||
const s32 platinum_link_id = atoi(n->GetAttribute("pid").c_str());
|
||||
const bool platinum_relevant = platinum_link_id >= 0;
|
||||
entry.platinum_relevant = platinum_link_id >= 0;
|
||||
|
||||
// Get trophy type
|
||||
switch (n->GetAttribute("ttype")[0])
|
||||
{
|
||||
case 'B': details.trophyGrade = SCE_NP_TROPHY_GRADE_BRONZE; break;
|
||||
case 'S': details.trophyGrade = SCE_NP_TROPHY_GRADE_SILVER; break;
|
||||
case 'G': details.trophyGrade = SCE_NP_TROPHY_GRADE_GOLD; break;
|
||||
case 'P': details.trophyGrade = SCE_NP_TROPHY_GRADE_PLATINUM; break;
|
||||
case 'B': entry.details.trophyGrade = SCE_NP_TROPHY_GRADE_BRONZE; break;
|
||||
case 'S': entry.details.trophyGrade = SCE_NP_TROPHY_GRADE_SILVER; break;
|
||||
case 'G': entry.details.trophyGrade = SCE_NP_TROPHY_GRADE_GOLD; break;
|
||||
case 'P': entry.details.trophyGrade = SCE_NP_TROPHY_GRADE_PLATINUM; break;
|
||||
default: rsx_log.warning("Unknown trophy grade %s", n->GetAttribute("ttype")); break;
|
||||
}
|
||||
|
||||
// Get name and detail
|
||||
if (details.hidden && !unlocked)
|
||||
if (entry.details.hidden && !entry.unlocked)
|
||||
{
|
||||
strcpy_trunc(details.name, hidden_title);
|
||||
strcpy_trunc(details.description, hidden_description);
|
||||
strcpy_trunc(entry.details.name, hidden_title);
|
||||
strcpy_trunc(entry.details.description, hidden_description);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -386,19 +428,50 @@ namespace rsx
|
||||
const std::string name = n2->GetName();
|
||||
if (name == "name")
|
||||
{
|
||||
strcpy_trunc(details.name, n2->GetNodeContent());
|
||||
strcpy_trunc(entry.details.name, n2->GetNodeContent());
|
||||
}
|
||||
else if (name == "detail")
|
||||
{
|
||||
strcpy_trunc(details.description, n2->GetNodeContent());
|
||||
strcpy_trunc(entry.details.description, n2->GetNodeContent());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto icon_path_it = m_trophy_data->trophy_image_paths.find(details.trophyId);
|
||||
const auto icon_path_it = m_trophy_data->trophy_image_paths.find(entry.details.trophyId);
|
||||
entry.icon_path = (icon_path_it != m_trophy_data->trophy_image_paths.cend()) ? icon_path_it->second : "";
|
||||
|
||||
std::unique_ptr<overlay_element> entry = std::make_unique<trophy_list_entry>(details, icon_path_it != m_trophy_data->trophy_image_paths.cend() ? icon_path_it->second : "", !unlocked, platinum_relevant);
|
||||
entries.emplace_back(std::move(entry));
|
||||
trophy_entries.emplace_back(std::move(entry));
|
||||
}
|
||||
|
||||
switch (m_sort_mode)
|
||||
{
|
||||
case trophy_sort_mode::earned_date:
|
||||
std::stable_sort(trophy_entries.begin(), trophy_entries.end(), [](const trophy_entry_data& a, const trophy_entry_data& b)
|
||||
{
|
||||
if (a.unlocked != b.unlocked) return a.unlocked > b.unlocked;
|
||||
return a.timestamp > b.timestamp;
|
||||
});
|
||||
break;
|
||||
case trophy_sort_mode::grade:
|
||||
std::stable_sort(trophy_entries.begin(), trophy_entries.end(), [](const trophy_entry_data& a, const trophy_entry_data& b)
|
||||
{
|
||||
return a.details.trophyGrade < b.details.trophyGrade;
|
||||
});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<overlay_element>> entries;
|
||||
|
||||
for (const trophy_entry_data& entry : trophy_entries)
|
||||
{
|
||||
if (entry.details.trophyId == old_trophy_id)
|
||||
{
|
||||
selected_index = static_cast<s32>(entries.size());
|
||||
}
|
||||
|
||||
entries.emplace_back(std::make_unique<trophy_list_entry>(entry.details, entry.icon_path, !entry.unlocked, entry.platinum_relevant));
|
||||
}
|
||||
|
||||
// Recreate list
|
||||
|
||||
@@ -20,6 +20,14 @@ namespace rsx
|
||||
std::string path;
|
||||
};
|
||||
|
||||
enum class trophy_sort_mode
|
||||
{
|
||||
game_default,
|
||||
not_earned,
|
||||
earned_date,
|
||||
grade,
|
||||
};
|
||||
|
||||
struct trophy_list_dialog : public user_interface
|
||||
{
|
||||
private:
|
||||
@@ -29,6 +37,7 @@ namespace rsx
|
||||
std::unique_ptr<overlay_element> m_dim_background;
|
||||
std::unique_ptr<list_view> m_list;
|
||||
std::unique_ptr<label> m_description;
|
||||
std::unique_ptr<image_button> m_sort_button;
|
||||
std::unique_ptr<image_button> m_show_hidden_trophies_button;
|
||||
|
||||
animation_color_interpolate fade_animation;
|
||||
@@ -37,6 +46,8 @@ namespace rsx
|
||||
atomic_t<bool> m_list_dirty { true };
|
||||
bool m_show_hidden_trophies = false;
|
||||
bool m_show_hidden_trophies_last = false;
|
||||
trophy_sort_mode m_sort_mode = trophy_sort_mode::game_default;
|
||||
trophy_sort_mode m_sort_mode_last = trophy_sort_mode::game_default;
|
||||
|
||||
public:
|
||||
trophy_list_dialog();
|
||||
|
||||
@@ -98,8 +98,7 @@ std::string CgBinaryDisasm::GetCondDisAsm() const
|
||||
else if (swizzle == ".yyyy"sv) swizzle = ".y";
|
||||
else if (swizzle == ".zzzz"sv) swizzle = ".z";
|
||||
else if (swizzle == ".wwww"sv) swizzle = ".w";
|
||||
|
||||
if (swizzle == ".xyzw"sv)
|
||||
else if (swizzle == ".xyzw"sv)
|
||||
{
|
||||
swizzle.clear();
|
||||
}
|
||||
@@ -229,7 +228,7 @@ template<typename T> std::string CgBinaryDisasm::GetSrcDisAsm(T src)
|
||||
void CgBinaryDisasm::TaskFP()
|
||||
{
|
||||
m_size = 0;
|
||||
u32* data = reinterpret_cast<u32*>(&m_buffer[m_offset]);
|
||||
const u32* data = reinterpret_cast<const u32*>(&m_buffer[m_offset]);
|
||||
ensure((m_buffer.size() - m_offset) % sizeof(u32) == 0);
|
||||
|
||||
enum
|
||||
|
||||
@@ -173,11 +173,9 @@ void CgBinaryDisasm::BuildShaderBody(bool include_glsl)
|
||||
std::vector<u32> be_data;
|
||||
|
||||
// Swap bytes. FP decompiler expects input in BE
|
||||
for (u32* ptr = reinterpret_cast<u32*>(m_buffer.data() + m_offset),
|
||||
*end = reinterpret_cast<u32*>(m_buffer.data() + m_buffer.size());
|
||||
ptr < end; ++ptr)
|
||||
for (usz i = m_offset; i < m_buffer.size(); i += sizeof(u32))
|
||||
{
|
||||
be_data.push_back(std::bit_cast<be_t<u32>>(*ptr));
|
||||
be_data.push_back(std::bit_cast<be_t<u32>>(read_from_ptr<u32>(m_buffer, i)));
|
||||
}
|
||||
|
||||
RSXFragmentProgram rsx_prog;
|
||||
@@ -209,13 +207,13 @@ void CgBinaryDisasm::BuildShaderBody(bool include_glsl)
|
||||
CgBinaryParameterOffset offset = prog.parameterArray;
|
||||
for (u32 i = 0; i < prog.parameterCount; i++)
|
||||
{
|
||||
auto& vparam = GetCgRef<CgBinaryParameter>(offset);
|
||||
const auto& vparam = GetCgRef<CgBinaryParameter>(offset);
|
||||
|
||||
std::string param_type = GetCgParamType(vparam.type) + " ";
|
||||
std::string param_name = GetCgParamName(vparam.name) + " ";
|
||||
std::string param_res = GetCgParamRes(vparam.res) + " ";
|
||||
std::string param_semantic = GetCgParamSemantic(vparam.semantic) + " ";
|
||||
std::string param_const = GetCgParamValue(vparam.embeddedConst, vparam.name);
|
||||
const std::string param_type = GetCgParamType(vparam.type) + " ";
|
||||
const std::string param_name = GetCgParamName(vparam.name) + " ";
|
||||
const std::string param_res = GetCgParamRes(vparam.res) + " ";
|
||||
const std::string param_semantic = GetCgParamSemantic(vparam.semantic) + " ";
|
||||
const std::string param_const = GetCgParamValue(vparam.embeddedConst, vparam.name);
|
||||
|
||||
fmt::append(m_arb_shader, "#%d%s%s%s%s\n", i, param_type, param_name, param_semantic, param_const);
|
||||
|
||||
@@ -226,7 +224,7 @@ void CgBinaryDisasm::BuildShaderBody(bool include_glsl)
|
||||
m_offset = prog.ucode;
|
||||
ensure((m_buffer.size() - m_offset) % sizeof(u32) == 0);
|
||||
|
||||
u32* vdata = reinterpret_cast<u32*>(&m_buffer[m_offset]);
|
||||
const u32* vdata = reinterpret_cast<const u32*>(&m_buffer[m_offset]);
|
||||
m_data.resize(prog.ucodeSize / sizeof(u32));
|
||||
std::memcpy(m_data.data(), vdata, prog.ucodeSize);
|
||||
TaskVP();
|
||||
|
||||
@@ -199,7 +199,7 @@ public:
|
||||
template<typename T>
|
||||
T& GetCgRef(const u32 offset)
|
||||
{
|
||||
return reinterpret_cast<T&>(m_buffer[offset]);
|
||||
return reinterpret_cast<T&>(::at32(m_buffer, offset));
|
||||
}
|
||||
|
||||
static std::string GetCgParamType(u32 type);
|
||||
|
||||
@@ -4,14 +4,12 @@
|
||||
|
||||
void CgBinaryDisasm::AddScaCodeDisasm(const std::string& code)
|
||||
{
|
||||
ensure((m_sca_opcode < 21));
|
||||
m_arb_shader += rsx_vp_sca_op_names[m_sca_opcode] + code + " ";
|
||||
m_arb_shader += ::at32(rsx_vp_sca_op_names, m_sca_opcode) + code + " ";
|
||||
}
|
||||
|
||||
void CgBinaryDisasm::AddVecCodeDisasm(const std::string& code)
|
||||
{
|
||||
ensure((m_vec_opcode < 26));
|
||||
m_arb_shader += rsx_vp_vec_op_names[m_vec_opcode] + code + " ";
|
||||
m_arb_shader += ::at32(rsx_vp_vec_op_names, m_vec_opcode) + code + " ";
|
||||
}
|
||||
|
||||
std::string CgBinaryDisasm::GetMaskDisasm(bool is_sca) const
|
||||
@@ -107,8 +105,6 @@ std::string CgBinaryDisasm::GetDSTDisasm(bool is_sca) const
|
||||
|
||||
std::string CgBinaryDisasm::GetSRCDisasm(const u32 n) const
|
||||
{
|
||||
ensure(n < 3);
|
||||
|
||||
std::string ret;
|
||||
|
||||
static constexpr std::array<std::string_view, 16> reg_table =
|
||||
@@ -121,11 +117,13 @@ std::string CgBinaryDisasm::GetSRCDisasm(const u32 n) const
|
||||
"in_tc4", "in_tc5", "in_tc6", "in_tc7"
|
||||
};
|
||||
|
||||
switch (src[n].reg_type)
|
||||
const SRC& s = ::at32(src, n);
|
||||
|
||||
switch (s.reg_type)
|
||||
{
|
||||
case 1: //temp
|
||||
ret += 'R';
|
||||
ret += std::to_string(src[n].tmp_src);
|
||||
ret += std::to_string(s.tmp_src);
|
||||
break;
|
||||
case 2: //input
|
||||
if (d1.input_src < reg_table.size())
|
||||
@@ -143,7 +141,7 @@ std::string CgBinaryDisasm::GetSRCDisasm(const u32 n) const
|
||||
break;
|
||||
|
||||
default:
|
||||
rsx_log.fatal("Bad src%u reg type: %d", n, u32{ src[n].reg_type });
|
||||
rsx_log.fatal("Bad src%u reg type: %d", n, u32{ s.reg_type });
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -152,10 +150,10 @@ std::string CgBinaryDisasm::GetSRCDisasm(const u32 n) const
|
||||
std::string swizzle;
|
||||
swizzle.reserve(5);
|
||||
swizzle += '.';
|
||||
swizzle += f[src[n].swz_x];
|
||||
swizzle += f[src[n].swz_y];
|
||||
swizzle += f[src[n].swz_z];
|
||||
swizzle += f[src[n].swz_w];
|
||||
swizzle += f[s.swz_x];
|
||||
swizzle += f[s.swz_y];
|
||||
swizzle += f[s.swz_z];
|
||||
swizzle += f[s.swz_w];
|
||||
|
||||
if (swizzle == ".xxxx") swizzle = ".x";
|
||||
else if (swizzle == ".yyyy") swizzle = ".y";
|
||||
@@ -178,7 +176,7 @@ std::string CgBinaryDisasm::GetSRCDisasm(const u32 n) const
|
||||
}
|
||||
|
||||
if (abs) ret = "|" + ret + "|";
|
||||
if (src[n].neg) ret = "-" + ret;
|
||||
if (s.neg) ret = "-" + ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -960,13 +960,13 @@ std::string FragmentProgramDecompiler::BuildCode()
|
||||
}
|
||||
|
||||
OS <<
|
||||
"#define _builtin_lit lit_legacy\n"
|
||||
"#define _builtin_log2 log2\n"
|
||||
"#define _builtin_normalize(x) (length(x) > 0? normalize(x) : x)\n" // HACK!! Workaround for some games that generate NaNs unless texture filtering exactly matches PS3 (BFBC)
|
||||
"#define _builtin_sqrt(x) sqrt(abs(x))\n"
|
||||
"#define _builtin_rcp(x) (1. / x)\n"
|
||||
"#define _builtin_rsq(x) (1. / _builtin_sqrt(x))\n"
|
||||
"#define _builtin_div(x, y) (x / y)\n";
|
||||
"#define _builtin_div(x, y) (x / y)\n"
|
||||
"#define _builtin_lerp mix\n";
|
||||
|
||||
if (device_props.has_low_precision_rounding)
|
||||
{
|
||||
@@ -1118,8 +1118,11 @@ bool FragmentProgramDecompiler::handle_sct_scb(u32 opcode)
|
||||
SetDst("_builtin_lit($0)");
|
||||
properties.has_lit_op = true;
|
||||
return true;
|
||||
case RSX_FP_OPCODE_LIF: SetDst("$Ty(1.0, $0.y, ($0.y > 0 ? exp2($0.w) : 0.0), 1.0)", OPFLAGS::op_extern); return true;
|
||||
case RSX_FP_OPCODE_LRP: SetDst("$Ty($2 * (1 - $0) + $1 * $0)", OPFLAGS::skip_type_cast); return true;
|
||||
case RSX_FP_OPCODE_LIF:
|
||||
SetDst("_builtin_lif($0)");
|
||||
properties.has_lit_op = true;
|
||||
return true;
|
||||
case RSX_FP_OPCODE_LRP: SetDst("_builtin_lerp($2, $1, $0)", OPFLAGS::skip_type_cast); return true;
|
||||
case RSX_FP_OPCODE_LG2: SetDst("_builtin_log2($0.x).xxxx"); return true;
|
||||
// Pack operations. See https://www.khronos.org/registry/OpenGL/extensions/NV/NV_fragment_program.txt
|
||||
// PK2 = PK2H (2 16-bit floats)
|
||||
|
||||
@@ -43,8 +43,6 @@ namespace glsl
|
||||
{
|
||||
switch (elementCount)
|
||||
{
|
||||
default:
|
||||
abort();
|
||||
case 1:
|
||||
return "float";
|
||||
case 2:
|
||||
@@ -53,6 +51,8 @@ namespace glsl
|
||||
return "vec3";
|
||||
case 4:
|
||||
return "vec4";
|
||||
default:
|
||||
fmt::throw_exception("Unexpected element count %d", elementCount);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,8 +60,6 @@ namespace glsl
|
||||
{
|
||||
switch (elementCount)
|
||||
{
|
||||
default:
|
||||
abort();
|
||||
case 1:
|
||||
return "float16_t";
|
||||
case 2:
|
||||
@@ -70,6 +68,8 @@ namespace glsl
|
||||
return "f16vec3";
|
||||
case 4:
|
||||
return "f16vec4";
|
||||
default:
|
||||
fmt::throw_exception("Unexpected element count %d", elementCount);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -474,8 +474,6 @@ namespace glsl
|
||||
{
|
||||
switch (f)
|
||||
{
|
||||
default:
|
||||
abort();
|
||||
case FUNCTION::DP2:
|
||||
return "$Ty(dot($0.xy, $1.xy))";
|
||||
case FUNCTION::DP2A:
|
||||
@@ -578,6 +576,8 @@ namespace glsl
|
||||
return "textureLod($t, $0.xyz, 0)";
|
||||
case FUNCTION::VERTEX_TEXTURE_FETCH2DMS:
|
||||
return "texelFetch($t, ivec2($0.xy * textureSize($t)), 0)";
|
||||
default:
|
||||
fmt::throw_exception("Unexpected function request: %d", static_cast<int>(f));
|
||||
}
|
||||
|
||||
rsx_log.error("Unexpected function request: %d", static_cast<int>(f));
|
||||
|
||||
@@ -101,6 +101,7 @@ uint ur0, ur1; // GP unsigned register (scalar)
|
||||
uvec4 uvr0; // GP unsigned register (vector)
|
||||
bvec4 bvr0, bvr1; // GP boolean register (vector)
|
||||
float sr0; // GP scalar register
|
||||
int ir0; // GP scalar register (scalar)
|
||||
|
||||
vec4 vrr; // value return (dst register)
|
||||
vec4 s0, s1, s2; // instruction src (src0, src1, src2)
|
||||
@@ -439,17 +440,6 @@ void write_dst(const in vec4 value)
|
||||
uvr0 = uvec4(uint(1 << 9), uint(1 << 10), uint(1 << 11), uint(1 << 12));
|
||||
bvr0 = bvec4(uvr0 & inst.words.xxxx);
|
||||
|
||||
if (TEST_INST_BIT(0, 8)) // SET COND
|
||||
{
|
||||
ur0 = GET_INST_BITS(1, 30, 1);
|
||||
reg_mov(cc[ur0], value, bvr0);
|
||||
}
|
||||
|
||||
if (TEST_INST_BIT(0, 30)) // NO DEST
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ur1 = GET_INST_BITS(2, 28, 3);
|
||||
sr0 = modifier_scale[ur1];
|
||||
vr0 = value * sr0;
|
||||
@@ -467,6 +457,18 @@ void write_dst(const in vec4 value)
|
||||
bvr0 = bvec4(uvec4(bvr0) & uvec4(bvr1));
|
||||
}
|
||||
|
||||
// FIXME - HWTEST: Are CC registers affected by scale and SAT modifiers?
|
||||
if (TEST_INST_BIT(0, 8)) // SET COND
|
||||
{
|
||||
ur0 = GET_INST_BITS(1, 30, 1);
|
||||
reg_mov(cc[ur0], vr0, bvr0);
|
||||
}
|
||||
|
||||
if (TEST_INST_BIT(0, 30)) // NO DEST
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ur1 = GET_INST_BITS(0, 1, 6);
|
||||
if (TEST_INST_BIT(0, 7))
|
||||
{
|
||||
@@ -484,14 +486,18 @@ void initialize()
|
||||
// NOTE: Register count is the number of 'full' registers that will be consumed. Hardware seems to do some renaming.
|
||||
// NOTE: Attempting to zero-initialize all the registers will slow things to a crawl!
|
||||
|
||||
uint register_count = GET_BITS(shader_control, 24, 6);
|
||||
ur0 = 0, ur1 = 0;
|
||||
while (register_count > 0)
|
||||
{
|
||||
regs32[ur0++] = vr_zero;
|
||||
regs16[ur1++] = vr_zero;
|
||||
regs16[ur1++] = vr_zero;
|
||||
register_count--;
|
||||
const uint register_count = GET_BITS(shader_control, 24, 6);
|
||||
const uint regs32_count = min(register_count, 48u);
|
||||
const uint regs16_count = min(register_count << 1u, 48u);
|
||||
|
||||
ur0 = regs32_count;
|
||||
while (ur0 > 0) {
|
||||
regs32[--ur0] = vr_zero;
|
||||
}
|
||||
|
||||
ur0 = regs16_count;
|
||||
while (ur0 > 0) {
|
||||
regs16[--ur0] = vr_zero;
|
||||
}
|
||||
|
||||
// Fog coord
|
||||
@@ -609,23 +615,24 @@ void main()
|
||||
//case RSX_FP_OPCODE_CAL:
|
||||
// Function call not yet found in the wild for this hw class
|
||||
case RSX_FP_OPCODE_RET:
|
||||
inst.end = true;
|
||||
if (check_cond()) inst.end = true;
|
||||
continue;
|
||||
case RSX_FP_OPCODE_IFE:
|
||||
ur0 = GET_INST_BITS(2, 0, 31); // ELSE addr
|
||||
if (check_cond())
|
||||
{
|
||||
// Go down IF path
|
||||
if (inst.words.z < inst.words.w)
|
||||
// We've entered the IF block. Set up an exit trap to skip the ELSE block.
|
||||
if (ur0 < inst.words.w) // If ELSE address is before ENDIF address..
|
||||
{
|
||||
test_addr = int(inst.words.z >> 2);
|
||||
jump_addr = int(inst.words.w >> 2);
|
||||
test_addr = int(ur0 >> 2u); // When we reach ELSE block...
|
||||
jump_addr = int(inst.words.w >> 2u); // Jump to ENDIF
|
||||
}
|
||||
// If simple IF..ENDIF, do nothing
|
||||
}
|
||||
else
|
||||
{
|
||||
// Go to ELSE path
|
||||
ip = int(inst.words.z >> 2);
|
||||
// Go to ELSE path. If ELSE is not provided, it matches ENDIF address.
|
||||
ip = int(ur0 >> 2u);
|
||||
inst_length = 0;
|
||||
}
|
||||
continue;
|
||||
@@ -633,18 +640,29 @@ void main()
|
||||
case RSX_FP_OPCODE_REP:
|
||||
if (check_cond())
|
||||
{
|
||||
counter = int(GET_INST_BITS(2, 2, 8) - GET_INST_BITS(2, 10, 8));
|
||||
counter /= int(GET_INST_BITS(2, 19, 8));
|
||||
loop_start_addr = ip + 1;
|
||||
loop_end_addr = int(inst.words.w >> 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
ip = int(inst.words.w >> 2);
|
||||
inst_length = 0;
|
||||
ur0 = GET_INST_BITS(2, 10, 8); // Start
|
||||
ur1 = GET_INST_BITS(2, 2, 8); // End
|
||||
if (ur1 > ur0)
|
||||
{
|
||||
counter = int(ur1 - ur0 - 1); // RANGE
|
||||
ir0 = int(GET_INST_BITS(2, 19, 8)); // STEP
|
||||
counter = max(counter, 0) / max(ir0, 1); // ITERATIONS
|
||||
|
||||
loop_start_addr = ip + 1;
|
||||
loop_end_addr = int(inst.words.w >> 2);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Failed cond check or 0 iterations encoded
|
||||
ip = int(inst.words.w >> 2);
|
||||
inst_length = 0;
|
||||
continue;
|
||||
case RSX_FP_OPCODE_BRK:
|
||||
if (!check_cond())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (loop_end_addr > 0)
|
||||
{
|
||||
ip = loop_end_addr;
|
||||
@@ -694,7 +712,7 @@ void main()
|
||||
case RSX_FP_OPCODE_RCP:
|
||||
vrr = (1.f / s0.xxxx); break;
|
||||
case RSX_FP_OPCODE_RSQ:
|
||||
vrr = inversesqrt(s0.xxxx); break;
|
||||
vrr = inversesqrt(abs(s0.x)).xxxx; break;
|
||||
case RSX_FP_OPCODE_EX2:
|
||||
vrr = exp2(s0.xxxx); break;
|
||||
case RSX_FP_OPCODE_LG2:
|
||||
@@ -708,7 +726,11 @@ void main()
|
||||
case RSX_FP_OPCODE_SIN:
|
||||
vrr = sin(s0.xxxx); break;
|
||||
case RSX_FP_OPCODE_NRM:
|
||||
vrr.xyz = normalize(s0.xyz); break;
|
||||
vrr = normalize(s0.xyz).xyzz; break;
|
||||
case RSX_FP_OPCODE_LIT:
|
||||
vrr = _builtin_lit(s0); break;
|
||||
case RSX_FP_OPCODE_LIF:
|
||||
vrr = _builtin_lif(s0); break;
|
||||
|
||||
#ifdef WITH_TEXTURES
|
||||
case RSX_FP_OPCODE_TEX:
|
||||
@@ -723,7 +745,7 @@ void main()
|
||||
case RSX_FP_OPCODE_PK4:
|
||||
vrr = vec4(uintBitsToFloat(packSnorm4x8(s0))); break;
|
||||
case RSX_FP_OPCODE_PK16:
|
||||
vrr = vec4(uintBitsToFloat(packSnorm2x16(s0.xy))); break;
|
||||
vrr = vec4(uintBitsToFloat(packUnorm2x16(s0.xy))); break;
|
||||
case RSX_FP_OPCODE_PKG:
|
||||
// Should be similar to PKB but with gamma correction, see description of PK4UBG in khronos page
|
||||
case RSX_FP_OPCODE_PKB:
|
||||
@@ -733,7 +755,7 @@ void main()
|
||||
case RSX_FP_OPCODE_UP4:
|
||||
vrr = unpackSnorm4x8(floatBitsToUint(s0.x)); break;
|
||||
case RSX_FP_OPCODE_UP16:
|
||||
vrr = unpackSnorm2x16(floatBitsToUint(s0.x)).xyxy; break;
|
||||
vrr = unpackUnorm2x16(floatBitsToUint(s0.x)).xyxy; break;
|
||||
case RSX_FP_OPCODE_UPG:
|
||||
// Same as UPB with gamma correction
|
||||
case RSX_FP_OPCODE_UPB:
|
||||
@@ -784,7 +806,7 @@ void main()
|
||||
vrr = s0 / s1.xxxx; break;
|
||||
case RSX_FP_OPCODE_DIVSQ:
|
||||
bvr0 = bvec4(s0);
|
||||
sr0 = inversesqrt(s1.x);
|
||||
sr0 = inversesqrt(abs(s1.x));
|
||||
vr0 = s0 * sr0;
|
||||
vrr = select(s0, vr0, bvr0);
|
||||
break;
|
||||
@@ -814,18 +836,19 @@ void main()
|
||||
case RSX_FP_OPCODE_MAD:
|
||||
vrr = fma(s0, s1, s2); break;
|
||||
case RSX_FP_OPCODE_LRP:
|
||||
vrr = mix(s1, s2, s0); break;
|
||||
vrr = mix(s2, s1, s0); break;
|
||||
case RSX_FP_OPCODE_DP2A:
|
||||
vrr = dot(s0.xy, s1.xy).xxxx + s2.xxxx; break;
|
||||
default:
|
||||
// Fallback - just write zero
|
||||
vrr = vr_zero;
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
// Other
|
||||
case RSX_FP_OPCODE_BEM:
|
||||
case RSX_FP_OPCODE_BEMLUM:
|
||||
case RSX_FP_OPCODE_LIT:
|
||||
case RSX_FP_OPCODE_LIF:
|
||||
case RSX_FP_OPCODE_TIMESWTEX:
|
||||
//Other (missing in HW)
|
||||
//case RSX_FP_OPCODE_BEM:
|
||||
//case RSX_FP_OPCODE_BEMLUM:
|
||||
//case RSX_FP_OPCODE_TIMESWTEX:
|
||||
#endif
|
||||
write_dst(vrr);
|
||||
}
|
||||
|
||||
@@ -62,6 +62,8 @@ layout(location=0) out vec4 dest[16];
|
||||
|
||||
#define reg_mov(d, s, m) d = mix(d, s, m)
|
||||
|
||||
#define MAX_STACK_DEPTH 8
|
||||
|
||||
struct D0
|
||||
{
|
||||
uint addr_swz;
|
||||
@@ -230,28 +232,36 @@ vec4 get_cond()
|
||||
return shuffle(cc[d0.cond_reg_sel_1], d0.swizzle);
|
||||
}
|
||||
|
||||
void write_sca(in float value)
|
||||
void write_sca(in vec4 value)
|
||||
{
|
||||
if (d0.saturate)
|
||||
{
|
||||
value = clamp(value, 0, 1);
|
||||
}
|
||||
|
||||
if (d3.sca_dst_tmp == 0x3f)
|
||||
const bool write_out_reg = !d0.vec_result;
|
||||
const bool write_gpr_reg = d3.sca_dst_tmp != 0x3f;
|
||||
|
||||
// NOTE: SCA does not support double register writes.
|
||||
if (write_gpr_reg)
|
||||
{
|
||||
if (!d0.vec_result)
|
||||
{
|
||||
reg_mov(dest[d3.dst], vec4(value), d3.sca_mask);
|
||||
}
|
||||
else
|
||||
{
|
||||
reg_mov(cc[d0.cond_reg_sel_1], vec4(value), d3.sca_mask);
|
||||
}
|
||||
// Write to GPR. Most likely scenario.
|
||||
reg_mov(temp[d3.sca_dst_tmp], value, d3.sca_mask);
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
||||
if (write_out_reg)
|
||||
{
|
||||
reg_mov(temp[d3.sca_dst_tmp], vec4(value), d3.sca_mask);
|
||||
if (d3.dst < 16)
|
||||
{
|
||||
reg_mov(dest[d3.dst], value, d3.sca_mask);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Not writing to temp or out register. Update CC
|
||||
reg_mov(cc[d0.cond_reg_sel_1], value, d3.sca_mask);
|
||||
}
|
||||
|
||||
void write_vec(in vec4 value)
|
||||
@@ -268,22 +278,32 @@ void write_vec(in vec4 value)
|
||||
write_mask = bvec4(uvec4(write_mask) & uvec4(mask));
|
||||
}
|
||||
|
||||
if (d0.dst_tmp == 0x3f && !d0.vec_result)
|
||||
if (d0.dst_tmp != 0x3f)
|
||||
{
|
||||
reg_mov(cc[d0.cond_reg_sel_1], value, write_mask);
|
||||
// GPR write
|
||||
reg_mov(temp[d0.dst_tmp], value, write_mask);
|
||||
|
||||
if (!d0.vec_result)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Fallthrough - VEC engine allows double write to one output register and one GPR
|
||||
}
|
||||
else
|
||||
|
||||
if (d0.vec_result)
|
||||
{
|
||||
if (d0.vec_result && d3.dst < 16)
|
||||
if (d3.dst < 16)
|
||||
{
|
||||
reg_mov(dest[d3.dst], value, write_mask);
|
||||
}
|
||||
|
||||
if (d0.dst_tmp != 0x3f)
|
||||
{
|
||||
reg_mov(temp[d0.dst_tmp], value, write_mask);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Writes to neither GPR nor OUT reg. Update CC
|
||||
reg_mov(cc[d0.cond_reg_sel_1], value, write_mask);
|
||||
|
||||
}
|
||||
|
||||
void write_output(const in int oid, const in int mask_bit)
|
||||
@@ -308,9 +328,10 @@ ivec4 read_addr_reg()
|
||||
|
||||
int branch_addr()
|
||||
{
|
||||
uint addr_h = GET_BITS(instr.z, 0, 6);
|
||||
uint addr_l = GET_BITS(instr.w, 29, 3);
|
||||
return int((addr_h << 3) + addr_l);
|
||||
const uint addr_2 = GET_BITS(instr.x, 23, 1); // Allows indexing beyond 512 instructions. Some games push upto 544 instructions on CEX firmware.
|
||||
const uint addr_1 = GET_BITS(instr.z, 0, 6);
|
||||
const uint addr_0 = GET_BITS(instr.w, 29, 3);
|
||||
return int((addr_2 << 9) + (addr_1 << 3) + addr_0); // NOTE: Encoded jump addresses are already rebased by the VP analyser pass.
|
||||
}
|
||||
|
||||
bool static_branch()
|
||||
@@ -410,13 +431,13 @@ void main()
|
||||
dest[i] = vec4(0., 0., 0., 1.);
|
||||
}
|
||||
|
||||
int callstack[8];
|
||||
int callstack[MAX_STACK_DEPTH];
|
||||
int stack_ptr = 0;
|
||||
int current_instruction = 0;
|
||||
int current_instruction = int(entry - base_address);
|
||||
|
||||
d3.end = false;
|
||||
|
||||
while (current_instruction < 512)
|
||||
while (current_instruction >= 0 && current_instruction < 544) // NOTE: CEX can hold upto 544 instructions
|
||||
{
|
||||
if (d3.end)
|
||||
{
|
||||
@@ -442,7 +463,7 @@ void main()
|
||||
|
||||
if (vec_opcode == RSX_VEC_OPCODE_ARL)
|
||||
{
|
||||
a[d0.dst_tmp] = ivec4(read_src(0));
|
||||
a[d0.dst_tmp & 0x1u] = ivec4(read_src(0));
|
||||
}
|
||||
else if (vec_opcode != RSX_VEC_OPCODE_NOP)
|
||||
{
|
||||
@@ -475,7 +496,13 @@ void main()
|
||||
write_vec(value);
|
||||
}
|
||||
|
||||
if (sca_opcode != RSX_SCA_OPCODE_NOP)
|
||||
if (sca_opcode == RSX_SCA_OPCODE_LIT)
|
||||
{
|
||||
// Ridiculously hacky instruction. It is a vector opcode, not scalar
|
||||
// TODO: More HWTEST
|
||||
write_sca(_builtin_lit(read_src(2)));
|
||||
}
|
||||
else if (sca_opcode != RSX_SCA_OPCODE_NOP)
|
||||
{
|
||||
float value = read_src(2).x;
|
||||
switch (sca_opcode)
|
||||
@@ -486,7 +513,6 @@ void main()
|
||||
case RSX_SCA_OPCODE_RSQ: value = 1.0 / sqrt(value); break;
|
||||
case RSX_SCA_OPCODE_EXP: value = exp(value); break;
|
||||
case RSX_SCA_OPCODE_LOG: value = log(value); break;
|
||||
//case RSX_SCA_OPCODE_LIT: value = lit_legacy(value); break;
|
||||
case RSX_SCA_OPCODE_LG2: value = log2(value); break;
|
||||
case RSX_SCA_OPCODE_EX2: value = exp2(value); break;
|
||||
case RSX_SCA_OPCODE_SIN: value = sin(value); break;
|
||||
@@ -494,7 +520,7 @@ void main()
|
||||
|
||||
case RSX_SCA_OPCODE_BRA:
|
||||
// Jump by address register
|
||||
if (dynamic_branch()) current_instruction = int(read_addr_reg().x);
|
||||
if (dynamic_branch()) current_instruction = int(read_addr_reg().x - base_address);
|
||||
continue;
|
||||
case RSX_SCA_OPCODE_BRI:
|
||||
// Jump immediate
|
||||
@@ -504,7 +530,8 @@ void main()
|
||||
// Call immediate
|
||||
if (dynamic_branch())
|
||||
{
|
||||
callstack[stack_ptr] = current_instruction;
|
||||
if (stack_ptr == MAX_STACK_DEPTH) return;
|
||||
callstack[stack_ptr] = current_instruction; // Already incremented, points to next
|
||||
stack_ptr++;
|
||||
current_instruction = branch_addr();
|
||||
}
|
||||
@@ -517,8 +544,7 @@ void main()
|
||||
if (dynamic_branch())
|
||||
{
|
||||
if (stack_ptr == 0) return;
|
||||
current_instruction = callstack[stack_ptr];
|
||||
stack_ptr--;
|
||||
current_instruction = callstack[--stack_ptr];
|
||||
}
|
||||
continue;
|
||||
case RSX_SCA_OPCODE_BRB:
|
||||
@@ -532,6 +558,7 @@ void main()
|
||||
// Call by boolean mask
|
||||
if (static_branch())
|
||||
{
|
||||
if (stack_ptr == MAX_STACK_DEPTH) return;
|
||||
callstack[stack_ptr] = current_instruction;
|
||||
stack_ptr++;
|
||||
current_instruction = branch_addr();
|
||||
@@ -541,7 +568,7 @@ void main()
|
||||
//case RSX_SCA_OPCODE_POP:
|
||||
}
|
||||
|
||||
write_sca(value);
|
||||
write_sca(vec4(value));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
R"(
|
||||
vec4 texelFetch2DMS(in _MSAA_SAMPLER_TYPE_ tex, const in vec2 sample_count, const in ivec2 icoords, const in ivec2 offset)
|
||||
vec4 texelFetch2DMS(in _MSAA_SAMPLER_TYPE_ tex, const in ivec2 clamp_bounds, const in vec2 sample_count, const in ivec2 icoords, const in ivec2 offset)
|
||||
{
|
||||
const vec2 resolve_coords = vec2(icoords + offset);
|
||||
const vec2 aa_coords = floor(resolve_coords / sample_count); // AA coords = real_coords / sample_count
|
||||
const vec2 sample_loc = fma(aa_coords, -sample_count, resolve_coords); // Sample ID = real_coords % sample_count
|
||||
const vec2 resolve_coords = vec2(clamp(icoords + offset, ivec2(0), clamp_bounds)); // Clamp to edge. Input offset is always zero or positive.
|
||||
const vec2 aa_coords = floor(resolve_coords / sample_count); // AA coords = real_coords / sample_count
|
||||
const vec2 sample_loc = fma(aa_coords, -sample_count, resolve_coords); // Sample ID = real_coords % sample_count
|
||||
const float sample_index = fma(sample_loc.y, sample_count.y, sample_loc.x);
|
||||
|
||||
// TODO: Hack. Filtering will break when sampling sub-pixel sample ids in wrap mode.
|
||||
return texelFetch(tex, ivec2(aa_coords), int(sample_index));
|
||||
}
|
||||
|
||||
@@ -14,9 +16,10 @@ vec4 sampleTexture2DMS(in _MSAA_SAMPLER_TYPE_ tex, const in vec2 coords, const i
|
||||
const vec2 scaled_coords = _texcoord_xform(coords, tex_params);
|
||||
const vec2 normalized_coords = texture2DMSCoord(scaled_coords, flags);
|
||||
const vec2 sample_count = vec2(2., textureSamples(tex) * 0.5);
|
||||
const vec2 image_size = textureSize(tex) * sample_count;
|
||||
const ivec2 image_size = ivec2(textureSize(tex) * sample_count);
|
||||
const ivec2 clamp_bounds = image_size - ivec2(1);
|
||||
const ivec2 icoords = ivec2(normalized_coords * image_size);
|
||||
const vec4 sample0 = texelFetch2DMS(tex, sample_count, icoords, ivec2(0));
|
||||
const vec4 sample0 = texelFetch2DMS(tex, clamp_bounds, sample_count, icoords, ivec2(0));
|
||||
|
||||
if (_get_bits(flags, FILTERED_MAG_BIT, 2) == 0)
|
||||
{
|
||||
@@ -35,7 +38,7 @@ vec4 sampleTexture2DMS(in _MSAA_SAMPLER_TYPE_ tex, const in vec2 coords, const i
|
||||
|
||||
vec4 a, b;
|
||||
float factor;
|
||||
const vec4 sample2 = texelFetch2DMS(tex, sample_count, icoords, ivec2(0, 1)); // Top left
|
||||
const vec4 sample2 = texelFetch2DMS(tex, clamp_bounds, sample_count, icoords, ivec2(0, 1)); // Top left
|
||||
|
||||
if (no_filter.x)
|
||||
{
|
||||
@@ -46,21 +49,21 @@ vec4 sampleTexture2DMS(in _MSAA_SAMPLER_TYPE_ tex, const in vec2 coords, const i
|
||||
else
|
||||
{
|
||||
// Filter required, sample more data
|
||||
const vec4 sample1 = texelFetch2DMS(tex, sample_count, icoords, ivec2(1, 0)); // Bottom right
|
||||
const vec4 sample3 = texelFetch2DMS(tex, sample_count, icoords, ivec2(1, 1)); // Top right
|
||||
const vec4 sample1 = texelFetch2DMS(tex, clamp_bounds, sample_count, icoords, ivec2(1, 0)); // Bottom right
|
||||
const vec4 sample3 = texelFetch2DMS(tex, clamp_bounds, sample_count, icoords, ivec2(1, 1)); // Top right
|
||||
|
||||
if (actual_step.x > uv_step.x)
|
||||
{
|
||||
// Downscale in X, centered
|
||||
const vec3 weights = compute2x2DownsampleWeights(normalized_coords.x, uv_step.x, actual_step.x);
|
||||
|
||||
const vec4 sample4 = texelFetch2DMS(tex, sample_count, icoords, ivec2(2, 0)); // Further bottom right
|
||||
a = fma(sample0, weights.xxxx, sample1 * weights.y) + (sample4 * weights.z); // Weighted sum
|
||||
const vec4 sample4 = texelFetch2DMS(tex, clamp_bounds, sample_count, icoords, ivec2(2, 0)); // Further bottom right
|
||||
a = fma(sample0, weights.xxxx, sample1 * weights.y) + (sample4 * weights.z); // Weighted sum
|
||||
|
||||
if (!no_filter.y)
|
||||
{
|
||||
const vec4 sample5 = texelFetch2DMS(tex, sample_count, icoords, ivec2(2, 1)); // Further top right
|
||||
b = fma(sample2, weights.xxxx, sample3 * weights.y) + (sample5 * weights.z); // Weighted sum
|
||||
const vec4 sample5 = texelFetch2DMS(tex, clamp_bounds, sample_count, icoords, ivec2(2, 1)); // Further top right
|
||||
b = fma(sample2, weights.xxxx, sample3 * weights.y) + (sample5 * weights.z); // Weighted sum
|
||||
}
|
||||
}
|
||||
else if (actual_step.x < uv_step.x)
|
||||
|
||||
@@ -12,15 +12,31 @@ R"(
|
||||
#define CMP_FIXUP(a) (a)
|
||||
#endif
|
||||
|
||||
#define _builtin_approx_pow(a, b) exp2((b) * log2(a))
|
||||
|
||||
#ifdef _ENABLE_LIT_EMULATION
|
||||
vec4 lit_legacy(const in vec4 val)
|
||||
// LIT is well documented in NV extension documents. See https://registry.khronos.org/OpenGL/extensions/NV/NV_vertex_program.txt
|
||||
// In RSXFP the LIT instruction is unimplemented in hw. In it's place we have LIF and full LIT is emulated using at least 3 instructions.
|
||||
vec4 _builtin_lit(const in vec4 values)
|
||||
{
|
||||
vec4 clamped_val = vec4(max(val.xy, vec2(0.)), val.zw);
|
||||
return vec4(
|
||||
1.,
|
||||
clamped_val.x,
|
||||
exp2(clamped_val.w * log2(max(clamped_val.y, 0.0000000001))) * sign(clamped_val.x),
|
||||
1.);
|
||||
// We clamp t.y to 1e-10 to avoid NaN in approx_pow when y=0 and w=0. This matches the spec's pow(x, 0) == 1 requirement.
|
||||
vec4 t = vec4(max(values.xy, vec2(0.f, 1e-10)), values.zw);
|
||||
return vec4(1.f,
|
||||
t.x,
|
||||
t.x > 0.f ? _builtin_approx_pow(t.y, t.w) : 0.f,
|
||||
1.f);
|
||||
}
|
||||
|
||||
// LIT d, t on RSXFP becomes:
|
||||
// t.xy = max(t.xy, 0)
|
||||
// t.w = t.w * log2(t.y)
|
||||
// LIF d, t
|
||||
vec4 _builtin_lif(const in vec4 t)
|
||||
{
|
||||
return vec4(1.f,
|
||||
t.y,
|
||||
t.y > 0.f ? exp2(t.w) : 0.f,
|
||||
1.f);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -120,9 +120,9 @@ namespace program_common::interpreter
|
||||
|
||||
// Prepare outputs
|
||||
interpreter_variants_t result;
|
||||
for (const auto& vs_opt : vs_masks)
|
||||
for (const u32 vs_opt : vs_masks)
|
||||
{
|
||||
for (const auto& fs_opt : fs_masks)
|
||||
for (const u32 fs_opt : fs_masks)
|
||||
{
|
||||
interpreter_pipeline_variant_t variant{};
|
||||
variant.vs_opts.shader_opt = vs_opt;
|
||||
|
||||
@@ -25,12 +25,12 @@ std::string VertexProgramDecompiler::GetMask(bool is_sca) const
|
||||
return ret.empty() || ret == "xyzw" ? "" : ("." + ret);
|
||||
}
|
||||
|
||||
std::string VertexProgramDecompiler::GetVecMask()
|
||||
std::string VertexProgramDecompiler::GetVecMask() const
|
||||
{
|
||||
return GetMask(false);
|
||||
}
|
||||
|
||||
std::string VertexProgramDecompiler::GetScaMask()
|
||||
std::string VertexProgramDecompiler::GetScaMask() const
|
||||
{
|
||||
return GetMask(true);
|
||||
}
|
||||
@@ -486,7 +486,7 @@ std::string VertexProgramDecompiler::Decompile()
|
||||
{
|
||||
// Call function
|
||||
// NOTE: Addresses are assumed to have been patched
|
||||
m_call_stack.push(i+1);
|
||||
m_call_stack.push(i + 1);
|
||||
AddCode(condition);
|
||||
AddCode("{");
|
||||
m_cur_instr->open_scopes++;
|
||||
@@ -660,7 +660,7 @@ std::string VertexProgramDecompiler::Decompile()
|
||||
case RSX_SCA_OPCODE_EXP: SetDSTSca("exp($s)"); break;
|
||||
case RSX_SCA_OPCODE_LOG: SetDSTSca("log($s)"); break;
|
||||
case RSX_SCA_OPCODE_LIT:
|
||||
SetDSTSca("lit_legacy($s)");
|
||||
SetDSTSca("_builtin_lit($s)");
|
||||
properties.has_lit_op = true;
|
||||
break;
|
||||
case RSX_SCA_OPCODE_BRA:
|
||||
@@ -680,8 +680,8 @@ std::string VertexProgramDecompiler::Decompile()
|
||||
//TODO
|
||||
rsx_log.error("BRA opcode found in subroutine!");
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case RSX_SCA_OPCODE_BRI: // works differently (BRI o[1].x(TR) L0;)
|
||||
{
|
||||
if (m_call_stack.empty())
|
||||
@@ -701,8 +701,8 @@ std::string VertexProgramDecompiler::Decompile()
|
||||
//TODO
|
||||
rsx_log.error("BRI opcode found in subroutine!");
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case RSX_SCA_OPCODE_CAL:
|
||||
// works same as BRI
|
||||
AddCode("//CAL");
|
||||
|
||||
@@ -67,8 +67,8 @@ struct VertexProgramDecompiler
|
||||
|
||||
static std::string NotZeroPositive(const std::string& code);
|
||||
std::string GetMask(bool is_sca) const;
|
||||
std::string GetVecMask();
|
||||
std::string GetScaMask();
|
||||
std::string GetVecMask() const;
|
||||
std::string GetScaMask() const;
|
||||
std::string GetDST(bool is_sca = false);
|
||||
std::string GetSRC(u32 n);
|
||||
std::string GetTex();
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace rsx
|
||||
std::memcpy(dst, src, sizeof(TIU_slot) * 4); return;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
const auto start = std::countr_zero(mask);
|
||||
const auto end = 16 - std::countl_zero(mask);
|
||||
@@ -174,7 +174,7 @@ namespace rsx
|
||||
}
|
||||
|
||||
// Check for any hits
|
||||
for (auto& idx : constant_ids)
|
||||
for (u16 idx : constant_ids)
|
||||
{
|
||||
if (idx >= first_index && idx <= last_index)
|
||||
{
|
||||
|
||||
@@ -16,14 +16,14 @@ namespace rsx
|
||||
{
|
||||
struct TIU_slot
|
||||
{
|
||||
float scale[3];
|
||||
float bias[3];
|
||||
float clamp_min[2];
|
||||
float clamp_max[2];
|
||||
u32 remap;
|
||||
u32 control;
|
||||
float scale[3] {};
|
||||
float bias[3] {};
|
||||
float clamp_min[2] {};
|
||||
float clamp_max[2] {};
|
||||
u32 remap {};
|
||||
u32 control {};
|
||||
}
|
||||
slots_[16]; // QT headers will collide with any variable named 'slots' because reasons
|
||||
slots_[16] {}; // QT headers will collide with any variable named 'slots' because reasons
|
||||
|
||||
TIU_slot& operator[](u32 index) { return slots_[index]; }
|
||||
|
||||
|
||||
@@ -455,6 +455,21 @@ namespace rsx
|
||||
return registers[NV4097_SET_TEXTURE_CONTROL3 + m_index] & 0xfffff;
|
||||
}
|
||||
|
||||
image_section_attributes_t fragment_texture::attributes() const
|
||||
{
|
||||
const auto _format = format() & ~(CELL_GCM_TEXTURE_UN | CELL_GCM_TEXTURE_LN);
|
||||
return {
|
||||
.address = offset(),
|
||||
.gcm_format = _format,
|
||||
.pitch = pitch(),
|
||||
.width = width(),
|
||||
.height = height(),
|
||||
.depth = depth(),
|
||||
.mipmaps = mipmap(),
|
||||
.bpp = rsx::get_format_block_size_in_bytes(_format)
|
||||
};
|
||||
}
|
||||
|
||||
u32 vertex_texture::offset() const
|
||||
{
|
||||
return registers[NV4097_SET_VERTEX_TEXTURE_OFFSET + (m_index * 8)] & 0x7FFFFFFF;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
namespace rsx
|
||||
{
|
||||
struct texture_format_ex;
|
||||
struct image_section_attributes_t;
|
||||
|
||||
class fragment_texture
|
||||
{
|
||||
@@ -85,6 +86,8 @@ namespace rsx
|
||||
|
||||
u16 depth() const;
|
||||
u32 pitch() const;
|
||||
|
||||
image_section_attributes_t attributes() const;
|
||||
};
|
||||
|
||||
class vertex_texture
|
||||
|
||||
@@ -829,6 +829,59 @@ bool VKGSRender::bind_interpreter_texture_env()
|
||||
|
||||
bool out_of_memory = false;
|
||||
|
||||
auto decay_view_for_interpreter = [&](
|
||||
const rsx::image_section_attributes_t& attr,
|
||||
vk::texture_cache::sampled_image_descriptor* desc,
|
||||
vk::image_view* base,
|
||||
const rsx::texture_channel_remap_t& decoded_remap,
|
||||
bool is_msaa,
|
||||
bool is_redirected) -> vk::image_view*
|
||||
{
|
||||
if (!is_msaa && !is_redirected)
|
||||
{
|
||||
return base;
|
||||
}
|
||||
|
||||
if (is_redirected && desc->image_type > rsx::texture_dimension_extended::texture_dimension_2d)
|
||||
{
|
||||
// Cannot handle redirect on 3D or cubemap with the interpreter.
|
||||
auto view_type = vk::get_view_type(desc->image_type);
|
||||
return vk::null_image_view(*m_current_command_buffer, view_type);
|
||||
}
|
||||
|
||||
using deferred_subresource_t = vk::texture_cache::deferred_subresource;
|
||||
auto image = static_cast<vk::viewable_image*>(base->image());
|
||||
|
||||
if (is_msaa)
|
||||
{
|
||||
// MSAA resolve
|
||||
auto rtt = vk::as_rtt(base->image());
|
||||
rtt->memory_barrier(*m_current_command_buffer, rsx::surface_access::transfer_read);
|
||||
image = rtt->get_surface(rsx::surface_access::transfer_read);
|
||||
}
|
||||
|
||||
if (is_redirected)
|
||||
{
|
||||
// Force bitcast
|
||||
deferred_subresource_t flatten_op{};
|
||||
flatten_op.address = desc->ref_address;
|
||||
flatten_op.external_handle = image;
|
||||
flatten_op.op = desc->is_cyclic_reference
|
||||
? rsx::deferred_request_command::copy_image_dynamic
|
||||
: rsx::deferred_request_command::copy_image_static;
|
||||
flatten_op.width = flatten_op.external_handle->width();
|
||||
flatten_op.height = flatten_op.external_handle->height();
|
||||
flatten_op.depth = 1;
|
||||
flatten_op.gcm_format = desc->format_ex.format();
|
||||
flatten_op.remap = decoded_remap;
|
||||
flatten_op.cache_range = utils::address_range32::start_length(desc->ref_address, attr.pitch * attr.height);
|
||||
return m_texture_cache.create_temporary_subresource(*m_current_command_buffer, flatten_op);
|
||||
}
|
||||
|
||||
image->change_layout(*m_current_command_buffer, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
return image->get_view(decoded_remap, base->info.subresourceRange.aspectMask);
|
||||
};
|
||||
|
||||
for (u32 textures_ref = current_fp_metadata.referenced_textures_mask, i = 0; textures_ref; textures_ref >>= 1, ++i)
|
||||
{
|
||||
if (!(textures_ref & 1))
|
||||
@@ -837,8 +890,8 @@ bool VKGSRender::bind_interpreter_texture_env()
|
||||
vk::image_view* view = nullptr;
|
||||
auto sampler_state = static_cast<vk::texture_cache::sampled_image_descriptor*>(fs_sampler_state[i].get());
|
||||
|
||||
if (rsx::method_registers.fragment_textures[i].enabled() &&
|
||||
sampler_state->validate())
|
||||
auto& tex = rsx::method_registers.fragment_textures[i];
|
||||
if (tex.enabled() && sampler_state->validate())
|
||||
{
|
||||
if (view = sampler_state->image_handle; !view)
|
||||
{
|
||||
@@ -848,18 +901,49 @@ bool VKGSRender::bind_interpreter_texture_env()
|
||||
out_of_memory = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
}
|
||||
|
||||
if (!view)
|
||||
{
|
||||
// OOM or disabled texture
|
||||
continue;
|
||||
}
|
||||
|
||||
auto primary_view = view;
|
||||
|
||||
// Flatten MSAA and DEPTH24S8 redirects
|
||||
if (view->image()->samples() > 1 || view->info.subresourceRange.aspectMask != VK_IMAGE_ASPECT_COLOR_BIT)
|
||||
{
|
||||
const auto mask = (1u << i);
|
||||
const bool is_redirected = !!(current_fragment_program.texture_state.redirected_textures & mask);
|
||||
const bool is_msaa = !!(current_fragment_program.texture_state.multisampled_textures & mask);
|
||||
if (is_redirected || is_msaa)
|
||||
{
|
||||
validate_image_layout_for_read_access(*m_current_command_buffer, view, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, sampler_state);
|
||||
view = decay_view_for_interpreter(
|
||||
tex.attributes(),
|
||||
sampler_state,
|
||||
view,
|
||||
tex.decoded_remap(),
|
||||
is_msaa,
|
||||
is_redirected);
|
||||
|
||||
if (!view)
|
||||
{
|
||||
// OOM
|
||||
out_of_memory = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (view)
|
||||
if (primary_view == view)
|
||||
{
|
||||
const int offsets[] = { 0, 16, 48, 32 };
|
||||
auto& sampled_image_info = texture_env[offsets[static_cast<u32>(sampler_state->image_type)] + i];
|
||||
sampled_image_info = { *view, *fs_sampler_handles[i] };
|
||||
validate_image_layout_for_read_access(*m_current_command_buffer, view, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, sampler_state);
|
||||
}
|
||||
|
||||
const int offsets[] = { 0, 16, 48, 32 };
|
||||
auto& sampled_image_info = texture_env[offsets[static_cast<u32>(sampler_state->image_type)] + i];
|
||||
sampled_image_info = { *view, *fs_sampler_handles[i] };
|
||||
}
|
||||
|
||||
m_shader_interpreter.update_fragment_textures(texture_env);
|
||||
|
||||
@@ -285,10 +285,11 @@ namespace vk
|
||||
}
|
||||
}
|
||||
|
||||
[[maybe_unused]] ::glsl::shader_properties properties{};
|
||||
properties.domain = ::glsl::program_domain::glsl_fragment_program;
|
||||
properties.require_depth_conversion = true;
|
||||
properties.require_wpos = true;
|
||||
::glsl::shader_properties properties
|
||||
{
|
||||
.domain = ::glsl::program_domain::glsl_fragment_program,
|
||||
.require_lit_emulation = true,
|
||||
};
|
||||
|
||||
u32 len;
|
||||
ParamArray arr;
|
||||
@@ -417,6 +418,7 @@ namespace vk
|
||||
" uint rop_control = fs_contexts[_fs_context_offset].rop_control;\n"
|
||||
" float alpha_ref = fs_contexts[_fs_context_offset].alpha_ref;\n\n";
|
||||
|
||||
::glsl::insert_glsl_legacy_function(builder, properties);
|
||||
builder << program_common::interpreter::get_fragment_interpreter();
|
||||
const std::string s = builder.str();
|
||||
|
||||
|
||||
@@ -1003,6 +1003,12 @@ namespace vk
|
||||
|
||||
for (const rsx::subresource_layout &layout : subresource_layout)
|
||||
{
|
||||
if (layout.level >= dst_image->mipmaps())
|
||||
{
|
||||
rsx_log.error("Invalid subresource definition for the output texture. Mip level does not exist.");
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto [row_pitch, upload_pitch_in_texel] = calculate_upload_pitch(format, heap_align, dst_image, layout, caps);
|
||||
caps.alignment = row_pitch;
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "VKTextureCache.h"
|
||||
#include "VKCompute.h"
|
||||
#include "VKAsyncScheduler.h"
|
||||
#include "vkutils/data_heap.h"
|
||||
|
||||
#include "util/asm.hpp"
|
||||
|
||||
@@ -755,26 +756,19 @@ namespace vk
|
||||
return view;
|
||||
}
|
||||
|
||||
vk::image_view* texture_cache::create_temporary_subresource_view(vk::command_buffer& cmd, vk::image* source, u32 gcm_format,
|
||||
u16 x, u16 y, u16 w, u16 h, const rsx::texture_channel_remap_t& remap_vector)
|
||||
vk::image_view* texture_cache::create_temporary_subresource_view(vk::command_buffer& cmd, const deferred_subresource& desc)
|
||||
{
|
||||
return create_temporary_subresource_view_impl(cmd, source, source->info.imageType, VK_IMAGE_VIEW_TYPE_2D,
|
||||
gcm_format, x, y, w, h, 1, 1, remap_vector, true);
|
||||
return create_temporary_subresource_view_impl(cmd, desc.external_handle, desc.external_handle->info.imageType, VK_IMAGE_VIEW_TYPE_2D,
|
||||
desc.gcm_format, desc.x, desc.y, desc.width, desc.height, 1, 1, desc.remap, true);
|
||||
}
|
||||
|
||||
vk::image_view* texture_cache::create_temporary_subresource_view(vk::command_buffer& cmd, vk::image** source, u32 gcm_format,
|
||||
u16 x, u16 y, u16 w, u16 h, const rsx::texture_channel_remap_t& remap_vector)
|
||||
{
|
||||
return create_temporary_subresource_view(cmd, *source, gcm_format, x, y, w, h, remap_vector);
|
||||
}
|
||||
|
||||
vk::image_view* texture_cache::generate_cubemap_from_images(vk::command_buffer& cmd, u32 gcm_format, u16 size,
|
||||
const rsx::simple_array<copy_region_descriptor>& sections_to_copy, const rsx::texture_channel_remap_t& remap_vector)
|
||||
vk::image_view* texture_cache::generate_cubemap_from_images(vk::command_buffer& cmd, const deferred_subresource& desc)
|
||||
{
|
||||
const auto& sections_to_copy = desc.sections_to_copy;
|
||||
auto _template = get_template_from_collection_impl(sections_to_copy);
|
||||
const u8 mip_count = 1 + sections_to_copy.reduce(0, FN(std::max<u8>(x, y.level)));
|
||||
auto result = create_temporary_subresource_view_impl(cmd, _template, VK_IMAGE_TYPE_2D,
|
||||
VK_IMAGE_VIEW_TYPE_CUBE, gcm_format, 0, 0, size, size, 1, mip_count, remap_vector, false);
|
||||
VK_IMAGE_VIEW_TYPE_CUBE, desc.gcm_format, 0, 0, desc.width, desc.height, 1, mip_count, desc.remap, false);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
@@ -787,7 +781,12 @@ namespace vk
|
||||
VkImageSubresourceRange dst_range = { dst_aspect, 0, mip_count, 0, 6 };
|
||||
vk::change_image_layout(cmd, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, dst_range);
|
||||
|
||||
if (!(dst_aspect & VK_IMAGE_ASPECT_DEPTH_BIT))
|
||||
if (desc.force_bg_load)
|
||||
{
|
||||
// The memory load covers the whole image, no need to clear it first
|
||||
initialize_subresource_from_memory(cmd, image, desc, rsx::texture_dimension_extended::texture_dimension_cubemap);
|
||||
}
|
||||
else if (!(dst_aspect & VK_IMAGE_ASPECT_DEPTH_BIT))
|
||||
{
|
||||
VkClearColorValue clear = {};
|
||||
vkCmdClearColorImage(cmd, image->value, image->current_layout, &clear, 1, &dst_range);
|
||||
@@ -812,12 +811,12 @@ namespace vk
|
||||
return result;
|
||||
}
|
||||
|
||||
vk::image_view* texture_cache::generate_3d_from_2d_images(vk::command_buffer& cmd, u32 gcm_format, u16 width, u16 height, u16 depth,
|
||||
const rsx::simple_array<copy_region_descriptor>& sections_to_copy, const rsx::texture_channel_remap_t& remap_vector)
|
||||
vk::image_view* texture_cache::generate_3d_from_2d_images(vk::command_buffer& cmd, const deferred_subresource& desc)
|
||||
{
|
||||
const auto& sections_to_copy = desc.sections_to_copy;
|
||||
auto _template = get_template_from_collection_impl(sections_to_copy);
|
||||
auto result = create_temporary_subresource_view_impl(cmd, _template, VK_IMAGE_TYPE_3D,
|
||||
VK_IMAGE_VIEW_TYPE_3D, gcm_format, 0, 0, width, height, depth, 1, remap_vector, false);
|
||||
VK_IMAGE_VIEW_TYPE_3D, desc.gcm_format, 0, 0, desc.width, desc.height, desc.depth, 1, desc.remap, false);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
@@ -830,7 +829,12 @@ namespace vk
|
||||
VkImageSubresourceRange dst_range = { dst_aspect, 0, 1, 0, 1 };
|
||||
vk::change_image_layout(cmd, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, dst_range);
|
||||
|
||||
if (!(dst_aspect & VK_IMAGE_ASPECT_DEPTH_BIT))
|
||||
if (desc.force_bg_load)
|
||||
{
|
||||
// The memory load covers the whole image, no need to clear it first
|
||||
initialize_subresource_from_memory(cmd, image, desc, rsx::texture_dimension_extended::texture_dimension_3d);
|
||||
}
|
||||
else if (!(dst_aspect & VK_IMAGE_ASPECT_DEPTH_BIT))
|
||||
{
|
||||
VkClearColorValue clear = {};
|
||||
vkCmdClearColorImage(cmd, image->value, image->current_layout, &clear, 1, &dst_range);
|
||||
@@ -855,12 +859,12 @@ namespace vk
|
||||
return result;
|
||||
}
|
||||
|
||||
vk::image_view* texture_cache::generate_atlas_from_images(vk::command_buffer& cmd, u32 gcm_format, u16 width, u16 height,
|
||||
const rsx::simple_array<copy_region_descriptor>& sections_to_copy, const rsx::texture_channel_remap_t& remap_vector)
|
||||
vk::image_view* texture_cache::generate_atlas_from_images(vk::command_buffer& cmd, const deferred_subresource& desc)
|
||||
{
|
||||
const auto& sections_to_copy = desc.sections_to_copy;
|
||||
auto _template = get_template_from_collection_impl(sections_to_copy);
|
||||
auto result = create_temporary_subresource_view_impl(cmd, _template, VK_IMAGE_TYPE_2D,
|
||||
VK_IMAGE_VIEW_TYPE_2D, gcm_format, 0, 0, width, height, 1, 1, remap_vector, false);
|
||||
VK_IMAGE_VIEW_TYPE_2D, desc.gcm_format, 0, 0, desc.width, desc.height, 1, 1, desc.remap, false);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
@@ -873,7 +877,12 @@ namespace vk
|
||||
VkImageSubresourceRange dst_range = { dst_aspect, 0, 1, 0, 1 };
|
||||
vk::change_image_layout(cmd, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, dst_range);
|
||||
|
||||
if (sections_to_copy[0].dst_w != width || sections_to_copy[0].dst_h != height)
|
||||
if (desc.force_bg_load)
|
||||
{
|
||||
// The memory load covers the whole image, no need to clear it first
|
||||
initialize_subresource_from_memory(cmd, image, desc, rsx::texture_dimension_extended::texture_dimension_2d);
|
||||
}
|
||||
else if (sections_to_copy[0].dst_w != desc.width || sections_to_copy[0].dst_h != desc.height)
|
||||
{
|
||||
if (!(dst_aspect & VK_IMAGE_ASPECT_DEPTH_BIT))
|
||||
{
|
||||
@@ -901,13 +910,13 @@ namespace vk
|
||||
return result;
|
||||
}
|
||||
|
||||
vk::image_view* texture_cache::generate_2d_mipmaps_from_images(vk::command_buffer& cmd, u32 gcm_format, u16 width, u16 height,
|
||||
const rsx::simple_array<copy_region_descriptor>& sections_to_copy, const rsx::texture_channel_remap_t& remap_vector)
|
||||
vk::image_view* texture_cache::generate_2d_mipmaps_from_images(vk::command_buffer& cmd, const deferred_subresource& desc)
|
||||
{
|
||||
const auto& sections_to_copy = desc.sections_to_copy;
|
||||
const auto mipmaps = ::narrow<u8>(sections_to_copy.size());
|
||||
auto _template = get_template_from_collection_impl(sections_to_copy);
|
||||
auto result = create_temporary_subresource_view_impl(cmd, _template, VK_IMAGE_TYPE_2D,
|
||||
VK_IMAGE_VIEW_TYPE_2D, gcm_format, 0, 0, width, height, 1, mipmaps, remap_vector, false);
|
||||
VK_IMAGE_VIEW_TYPE_2D, desc.gcm_format, 0, 0, desc.width, desc.height, 1, mipmaps, desc.remap, false);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
@@ -920,7 +929,12 @@ namespace vk
|
||||
VkImageSubresourceRange dst_range = { dst_aspect, 0, mipmaps, 0, 1 };
|
||||
vk::change_image_layout(cmd, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, dst_range);
|
||||
|
||||
if (!(dst_aspect & VK_IMAGE_ASPECT_DEPTH_BIT))
|
||||
if (desc.force_bg_load)
|
||||
{
|
||||
// The memory load covers the whole image, no need to clear it first
|
||||
initialize_subresource_from_memory(cmd, image, desc, rsx::texture_dimension_extended::texture_dimension_2d);
|
||||
}
|
||||
else if (!(dst_aspect & VK_IMAGE_ASPECT_DEPTH_BIT))
|
||||
{
|
||||
VkClearColorValue clear = {};
|
||||
vkCmdClearColorImage(cmd, image->value, image->current_layout, &clear, 1, &dst_range);
|
||||
@@ -955,6 +969,14 @@ namespace vk
|
||||
vk::get_resource_manager()->dispose(disposable);
|
||||
}
|
||||
|
||||
void texture_cache::initialize_subresource_from_memory(vk::command_buffer& cmd, vk::image* dst, const deferred_subresource& desc, rsx::texture_dimension_extended type) const
|
||||
{
|
||||
const auto subresources_layout = rsx::get_subresources_layout(desc, type);
|
||||
const u16 layer_count = (type == rsx::texture_dimension_extended::texture_dimension_cubemap) ? 6 : 1;
|
||||
vk::upload_image(cmd, dst, subresources_layout, desc.gcm_format, desc.swizzled, layer_count,
|
||||
dst->aspect(), *vk::get_upload_heap(), desc.pitch, vk::upload_contents_inline);
|
||||
}
|
||||
|
||||
void texture_cache::update_image_contents(vk::command_buffer& cmd, vk::image_view* dst_view, vk::image* src, u16 width, u16 height)
|
||||
{
|
||||
rsx::simple_array<copy_region_descriptor> region =
|
||||
|
||||
@@ -456,26 +456,20 @@ namespace vk
|
||||
vk::image_view* create_temporary_subresource_view_impl(vk::command_buffer& cmd, vk::image* source, VkImageType image_type, VkImageViewType view_type,
|
||||
u32 gcm_format, u16 x, u16 y, u16 w, u16 h, u16 d, u8 mips, const rsx::texture_channel_remap_t& remap_vector, bool copy);
|
||||
|
||||
vk::image_view* create_temporary_subresource_view(vk::command_buffer& cmd, vk::image* source, u32 gcm_format,
|
||||
u16 x, u16 y, u16 w, u16 h, const rsx::texture_channel_remap_t& remap_vector) override;
|
||||
vk::image_view* create_temporary_subresource_view(vk::command_buffer& cmd, const deferred_subresource& desc) override;
|
||||
|
||||
vk::image_view* create_temporary_subresource_view(vk::command_buffer& cmd, vk::image** source, u32 gcm_format,
|
||||
u16 x, u16 y, u16 w, u16 h, const rsx::texture_channel_remap_t& remap_vector) override;
|
||||
vk::image_view* generate_cubemap_from_images(vk::command_buffer& cmd, const deferred_subresource& desc) override;
|
||||
|
||||
vk::image_view* generate_cubemap_from_images(vk::command_buffer& cmd, u32 gcm_format, u16 size,
|
||||
const rsx::simple_array<copy_region_descriptor>& sections_to_copy, const rsx::texture_channel_remap_t& remap_vector) override;
|
||||
vk::image_view* generate_3d_from_2d_images(vk::command_buffer& cmd, const deferred_subresource& desc) override;
|
||||
|
||||
vk::image_view* generate_3d_from_2d_images(vk::command_buffer& cmd, u32 gcm_format, u16 width, u16 height, u16 depth,
|
||||
const rsx::simple_array<copy_region_descriptor>& sections_to_copy, const rsx::texture_channel_remap_t& remap_vector) override;
|
||||
vk::image_view* generate_atlas_from_images(vk::command_buffer& cmd, const deferred_subresource& desc) override;
|
||||
|
||||
vk::image_view* generate_atlas_from_images(vk::command_buffer& cmd, u32 gcm_format, u16 width, u16 height,
|
||||
const rsx::simple_array<copy_region_descriptor>& sections_to_copy, const rsx::texture_channel_remap_t& remap_vector) override;
|
||||
|
||||
vk::image_view* generate_2d_mipmaps_from_images(vk::command_buffer& cmd, u32 gcm_format, u16 width, u16 height,
|
||||
const rsx::simple_array<copy_region_descriptor>& sections_to_copy, const rsx::texture_channel_remap_t& remap_vector) override;
|
||||
vk::image_view* generate_2d_mipmaps_from_images(vk::command_buffer& cmd, const deferred_subresource& desc) override;
|
||||
|
||||
void release_temporary_subresource(vk::image_view* view) override;
|
||||
|
||||
void initialize_subresource_from_memory(vk::command_buffer& cmd, vk::image* dst, const deferred_subresource& desc, rsx::texture_dimension_extended type) const;
|
||||
|
||||
void update_image_contents(vk::command_buffer& cmd, vk::image_view* dst_view, vk::image* src, u16 width, u16 height) override;
|
||||
|
||||
cached_texture_section* create_new_texture(vk::command_buffer& cmd, const utils::address_range32& rsx_range, u16 width, u16 height, u16 depth, u16 mipmaps, u32 pitch,
|
||||
|
||||
@@ -4869,7 +4869,9 @@ utils::serial* Emulator::DeserialManager() const
|
||||
|
||||
bool Emulator::IsVsh()
|
||||
{
|
||||
return g_ps3_process_info.self_info.valid && (g_ps3_process_info.self_info.prog_id_hdr.program_authority_id >> 36 == 0x1070000); // Not only VSH but also most CoreOS LV2 SELFs need the special treatment
|
||||
const auto process = &g_ps3_process_info;
|
||||
|
||||
return process->self_info.valid && (process->self_info.prog_id_hdr.program_authority_id == 0x10700005FF000001L); // VSH.self ID
|
||||
}
|
||||
|
||||
bool Emulator::IsValidSfb(const std::string& path)
|
||||
|
||||
@@ -189,6 +189,7 @@ enum class localized_string_id
|
||||
HOME_MENU_RESUME,
|
||||
HOME_MENU_FRIENDS,
|
||||
HOME_MENU_FRIENDS_REQUESTS,
|
||||
HOME_MENU_FRIENDS_GAME_INVITES,
|
||||
HOME_MENU_FRIENDS_BLOCKED,
|
||||
HOME_MENU_FRIENDS_STATUS_ONLINE,
|
||||
HOME_MENU_FRIENDS_STATUS_OFFLINE,
|
||||
@@ -202,6 +203,9 @@ enum class localized_string_id
|
||||
HOME_MENU_FRIENDS_CANCEL_REQUEST_MSG,
|
||||
HOME_MENU_FRIENDS_REJECT_REQUEST_MSG,
|
||||
HOME_MENU_FRIENDS_REJECT_REQUEST,
|
||||
HOME_MENU_FRIENDS_ACCEPT_GAME_INVITE_MSG,
|
||||
HOME_MENU_FRIENDS_REJECT_GAME_INVITE_MSG,
|
||||
HOME_MENU_FRIENDS_REJECT_GAME_INVITE,
|
||||
HOME_MENU_FRIENDS_NEXT_LIST,
|
||||
HOME_MENU_SETTINGS,
|
||||
HOME_MENU_SETTINGS_SAVE,
|
||||
@@ -303,6 +307,10 @@ enum class localized_string_id
|
||||
HOME_MENU_TROPHY_GRADE_SILVER,
|
||||
HOME_MENU_TROPHY_GRADE_GOLD,
|
||||
HOME_MENU_TROPHY_GRADE_PLATINUM,
|
||||
HOME_MENU_TROPHY_SORT_GAME_DEFAULT,
|
||||
HOME_MENU_TROPHY_SORT_NOT_EARNED,
|
||||
HOME_MENU_TROPHY_SORT_EARNED_DATE,
|
||||
HOME_MENU_TROPHY_SORT_GRADE,
|
||||
|
||||
AUDIO_MUTED,
|
||||
AUDIO_UNMUTED,
|
||||
|
||||
@@ -187,7 +187,6 @@ struct cfg_root : cfg::node
|
||||
node_vk(cfg::node* _this) : cfg::node(_this, "Vulkan") {}
|
||||
|
||||
cfg::string adapter{ this, "Adapter" };
|
||||
cfg::_bool force_primitive_restart{ this, "Force primitive restart flag" };
|
||||
cfg::_enum<vk_exclusive_fs_mode> exclusive_fullscreen_mode{ this, "Exclusive Fullscreen Mode", vk_exclusive_fs_mode::unspecified};
|
||||
cfg::_bool asynchronous_texture_streaming{ this, "Asynchronous Texture Streaming", false };
|
||||
cfg::_enum<vk_gpu_scheduler_mode> asynchronous_scheduler{ this, "Asynchronous Queue Scheduler", vk_gpu_scheduler_mode::safe };
|
||||
|
||||
+49
-31
@@ -70,9 +70,12 @@ DYNAMIC_IMPORT("ntdll.dll", NtSetTimerResolution, NTSTATUS(ULONG DesiredResoluti
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/system_config.h"
|
||||
#include "Emu/system_utils.hpp"
|
||||
#include "Emu/savestate_utils.hpp"
|
||||
#include "Emu/RSX/Overlays/overlay_message.h"
|
||||
|
||||
#include <thread>
|
||||
#include <charconv>
|
||||
#include <regex>
|
||||
|
||||
#include "util/sysinfo.hpp"
|
||||
|
||||
@@ -384,36 +387,37 @@ private:
|
||||
};
|
||||
|
||||
// Arguments that force a headless application (need to be checked in create_application)
|
||||
constexpr auto arg_headless = "headless";
|
||||
constexpr auto arg_decrypt = "decrypt";
|
||||
constexpr auto arg_headless = "headless";
|
||||
constexpr auto arg_decrypt = "decrypt";
|
||||
|
||||
// Arguments that can be used with a gui application
|
||||
constexpr auto arg_no_gui = "no-gui";
|
||||
constexpr auto arg_fullscreen = "fullscreen"; // only useful with no-gui
|
||||
constexpr auto arg_gs_screen = "game-screen";
|
||||
constexpr auto arg_high_dpi = "hidpi";
|
||||
constexpr auto arg_rounding = "dpi-rounding";
|
||||
constexpr auto arg_styles = "styles";
|
||||
constexpr auto arg_style = "style";
|
||||
constexpr auto arg_stylesheet = "stylesheet";
|
||||
constexpr auto arg_config = "config";
|
||||
constexpr auto arg_input_config = "input-config"; // only useful with no-gui
|
||||
constexpr auto arg_q_debug = "qDebug";
|
||||
constexpr auto arg_error = "error";
|
||||
constexpr auto arg_updating = "updating";
|
||||
constexpr auto arg_user_id = "user-id";
|
||||
constexpr auto arg_installfw = "installfw";
|
||||
constexpr auto arg_installpkg = "installpkg";
|
||||
constexpr auto arg_savestate = "savestate";
|
||||
constexpr auto arg_rsx_capture = "rsx-capture";
|
||||
constexpr auto arg_timer = "high-res-timer";
|
||||
constexpr auto arg_verbose_curl = "verbose-curl";
|
||||
constexpr auto arg_any_location = "allow-any-location";
|
||||
constexpr auto arg_codecs = "codecs";
|
||||
constexpr auto arg_no_gui = "no-gui";
|
||||
constexpr auto arg_fullscreen = "fullscreen"; // only useful with no-gui
|
||||
constexpr auto arg_gs_screen = "game-screen";
|
||||
constexpr auto arg_high_dpi = "hidpi";
|
||||
constexpr auto arg_rounding = "dpi-rounding";
|
||||
constexpr auto arg_styles = "styles";
|
||||
constexpr auto arg_style = "style";
|
||||
constexpr auto arg_stylesheet = "stylesheet";
|
||||
constexpr auto arg_config = "config";
|
||||
constexpr auto arg_input_config = "input-config"; // only useful with no-gui
|
||||
constexpr auto arg_q_debug = "qDebug";
|
||||
constexpr auto arg_error = "error";
|
||||
constexpr auto arg_updating = "updating";
|
||||
constexpr auto arg_user_id = "user-id";
|
||||
constexpr auto arg_installfw = "installfw";
|
||||
constexpr auto arg_installpkg = "installpkg";
|
||||
constexpr auto arg_savestate = "savestate";
|
||||
constexpr auto arg_last_savestate = "last-savestate";
|
||||
constexpr auto arg_rsx_capture = "rsx-capture";
|
||||
constexpr auto arg_timer = "high-res-timer";
|
||||
constexpr auto arg_verbose_curl = "verbose-curl";
|
||||
constexpr auto arg_any_location = "allow-any-location";
|
||||
constexpr auto arg_codecs = "codecs";
|
||||
|
||||
#ifdef _WIN32
|
||||
constexpr auto arg_stdout = "stdout";
|
||||
constexpr auto arg_stderr = "stderr";
|
||||
constexpr auto arg_stdout = "stdout";
|
||||
constexpr auto arg_stderr = "stderr";
|
||||
#endif
|
||||
|
||||
constexpr auto arg_emulation_barrier = "";
|
||||
@@ -839,6 +843,8 @@ int run_rpcs3(int argc, char** argv)
|
||||
parser.addOption(user_id_option);
|
||||
const QCommandLineOption savestate_option(arg_savestate, "Path for directly loading a savestate.", "path", "");
|
||||
parser.addOption(savestate_option);
|
||||
const QCommandLineOption last_savestate_option(arg_last_savestate, "Loading the last savestate of a game.", "path", "Title-ID or path");
|
||||
parser.addOption(last_savestate_option);
|
||||
const QCommandLineOption rsx_capture_option(arg_rsx_capture, "Path for directly loading an rsx capture.", "path", "");
|
||||
parser.addOption(rsx_capture_option);
|
||||
parser.addOption(QCommandLineOption(arg_q_debug, "Log qDebug to RPCS3.log."));
|
||||
@@ -1196,14 +1202,26 @@ int run_rpcs3(int argc, char** argv)
|
||||
}
|
||||
}
|
||||
|
||||
if (parser.isSet(arg_savestate))
|
||||
if (parser.isSet(arg_savestate) || parser.isSet(arg_last_savestate))
|
||||
{
|
||||
const std::string savestate_path = parser.value(savestate_option).toStdString();
|
||||
sys_log.notice("Booting savestate from command line: %s", savestate_path);
|
||||
std::string savestate_path;
|
||||
|
||||
if (!fs::is_file(savestate_path))
|
||||
if (parser.isSet(arg_savestate))
|
||||
{
|
||||
report_fatal_error(fmt::format("No savestate file found: %s", savestate_path));
|
||||
savestate_path = parser.value(savestate_option).toStdString();
|
||||
sys_log.notice("Booting savestate from command line: path='%s'", savestate_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::string serial_or_path = parser.value(last_savestate_option).toStdString();
|
||||
const bool is_serial = std::regex_match(serial_or_path, std::regex(R"(^[A-Z]{4}\d{5}$)"));
|
||||
savestate_path = get_savestate_file(is_serial ? serial_or_path : "", is_serial ? "" : serial_or_path, 1);
|
||||
sys_log.notice("Booting last savestate from command line: game='%s', path='%s'", serial_or_path, savestate_path);
|
||||
}
|
||||
|
||||
if (!is_savestate_compatible(savestate_path))
|
||||
{
|
||||
report_fatal_error(fmt::format("No savestate file found or savestate not compatible: path='%s'", savestate_path));
|
||||
}
|
||||
|
||||
Emu.CallFromMainThread([path = savestate_path]()
|
||||
|
||||
@@ -230,6 +230,9 @@
|
||||
<ClCompile Include="QTGeneratedFiles\Debug\moc_anaglyph_settings_dialog.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="QTGeneratedFiles\Debug\moc_guest_memory_dumper.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="QTGeneratedFiles\Debug\moc_breakpoint_list.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
@@ -542,6 +545,9 @@
|
||||
<ClCompile Include="QTGeneratedFiles\Release\moc_anaglyph_settings_dialog.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="QTGeneratedFiles\Release\moc_guest_memory_dumper.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="QTGeneratedFiles\Release\moc_breakpoint_list.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
@@ -845,6 +851,7 @@
|
||||
<ClCompile Include="rpcs3qt\call_stack_list.cpp" />
|
||||
<ClCompile Include="rpcs3qt\camera_settings_dialog.cpp" />
|
||||
<ClCompile Include="rpcs3qt\emu_settings_type.cpp" />
|
||||
<ClCompile Include="rpcs3qt\guest_memory_dumper.cpp" />
|
||||
<ClCompile Include="rpcs3qt\gui_game_info.cpp" />
|
||||
<ClCompile Include="rpcs3qt\log_level_dialog.cpp" />
|
||||
<ClCompile Include="rpcs3qt\permissions.cpp" />
|
||||
@@ -1208,6 +1215,16 @@
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtConcurrent"</Command>
|
||||
</CustomBuild>
|
||||
<CustomBuild Include="rpcs3qt\guest_memory_dumper.h">
|
||||
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
|
||||
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing %(Identity)...</Message>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtConcurrent"</Command>
|
||||
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
|
||||
<Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Moc%27ing %(Identity)...</Message>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtConcurrent"</Command>
|
||||
</CustomBuild>
|
||||
<ClInclude Include="rpcs3qt\breakpoint_handler.h" />
|
||||
<CustomBuild Include="rpcs3qt\breakpoint_list.h">
|
||||
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
|
||||
|
||||
@@ -585,6 +585,12 @@
|
||||
<ClCompile Include="QTGeneratedFiles\Release\moc_anaglyph_settings_dialog.cpp">
|
||||
<Filter>Generated Files\Release</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="QTGeneratedFiles\Debug\moc_guest_memory_dumper.cpp">
|
||||
<Filter>Generated Files\Debug</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="QTGeneratedFiles\Release\moc_guest_memory_dumper.cpp">
|
||||
<Filter>Generated Files\Release</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="QTGeneratedFiles\Debug\moc_breakpoint_list.cpp">
|
||||
<Filter>Generated Files\Debug</Filter>
|
||||
</ClCompile>
|
||||
@@ -1314,6 +1320,9 @@
|
||||
<ClCompile Include="rpcs3qt\anaglyph_settings_dialog.cpp">
|
||||
<Filter>Gui\settings</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rpcs3qt\guest_memory_dumper.cpp">
|
||||
<Filter>Gui\utils</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Input\ds4_pad_handler.h">
|
||||
@@ -1565,7 +1574,7 @@
|
||||
<ClInclude Include="rpcs3qt\steam_utils.h">
|
||||
<Filter>Gui\utils</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="rpcs3qt\anaglyph_settings_dialog.h">
|
||||
<ClInclude Include="rpcs3qt\render_creator.h">
|
||||
<Filter>Gui\settings</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
@@ -1783,9 +1792,6 @@
|
||||
<CustomBuild Include="rpcs3qt\microphone_creator.h">
|
||||
<Filter>Gui\settings</Filter>
|
||||
</CustomBuild>
|
||||
<CustomBuild Include="rpcs3qt\render_creator.h">
|
||||
<Filter>Gui\settings</Filter>
|
||||
</CustomBuild>
|
||||
<CustomBuild Include="rpcs3qt\patch_manager_dialog.h">
|
||||
<Filter>Gui\patch manager</Filter>
|
||||
</CustomBuild>
|
||||
@@ -1927,6 +1933,15 @@
|
||||
<CustomBuild Include="rpcs3qt\game_list_actions.h">
|
||||
<Filter>Gui\game list</Filter>
|
||||
</CustomBuild>
|
||||
<CustomBuild Include="rpcs3qt\content_integrity.h">
|
||||
<Filter>Gui\game list</Filter>
|
||||
</CustomBuild>
|
||||
<CustomBuild Include="rpcs3qt\anaglyph_settings_dialog.h">
|
||||
<Filter>Gui\settings</Filter>
|
||||
</CustomBuild>
|
||||
<CustomBuild Include="rpcs3qt\guest_memory_dumper.h">
|
||||
<Filter>Gui\utils</Filter>
|
||||
</CustomBuild>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Image Include="rpcs3.ico" />
|
||||
@@ -2082,9 +2097,4 @@
|
||||
<Filter>buildfiles\cmake</Filter>
|
||||
</Text>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<CustomBuild Include="rpcs3qt\content_integrity.h">
|
||||
<Filter>Gui\game list</Filter>
|
||||
</CustomBuild>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -26,7 +26,7 @@ namespace rpcs3
|
||||
|
||||
const utils::version& get_version()
|
||||
{
|
||||
static constexpr utils::version version{ 0, 0, 41, utils::version_type::alpha, 1, RPCS3_GIT_VERSION };
|
||||
static constexpr utils::version version{ 0, 0, 42, utils::version_type::alpha, 1, RPCS3_GIT_VERSION };
|
||||
return version;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ add_library(rpcs3_ui STATIC
|
||||
game_list_grid.cpp
|
||||
game_list_grid_item.cpp
|
||||
game_list_table.cpp
|
||||
guest_memory_dumper.cpp
|
||||
gui_application.cpp
|
||||
gl_gs_frame.cpp
|
||||
gs_frame.cpp
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "game_compatibility.h"
|
||||
#include "gui_application.h"
|
||||
#include "gui_settings.h"
|
||||
#include "downloader.h"
|
||||
#include "localized.h"
|
||||
@@ -268,12 +269,13 @@ compat::package_info game_compatibility::GetPkgInfo(const QString& pkg_path, con
|
||||
|
||||
const psf::registry& psf = reader.get_psf();
|
||||
|
||||
// TODO: localization of title and changelog
|
||||
const std::string title_key = "TITLE";
|
||||
const std::string changelog_key = "paramhip";
|
||||
const std::string localized_title_key = fmt::format("TITLE_%02d", gui_application::get_language_id());
|
||||
const std::string localized_changelog_key = fmt::format("paramhip_%02d", gui_application::get_language_id());
|
||||
|
||||
std::string_view localized_title = psf::get_string(psf, localized_title_key);
|
||||
|
||||
info.path = pkg_path;
|
||||
info.title = QString::fromStdString(std::string(psf::get_string(psf, title_key))); // Let's read this from the psf first
|
||||
info.title = QString::fromStdString(std::string(localized_title.empty() ? psf::get_string(psf, "TITLE") : localized_title));
|
||||
info.title_id = QString::fromStdString(std::string(psf::get_string(psf, "TITLE_ID")));
|
||||
info.category = QString::fromStdString(std::string(psf::get_string(psf, "CATEGORY")));
|
||||
info.version = QString::fromStdString(std::string(psf::get_string(psf, "APP_VER")));
|
||||
@@ -298,15 +300,13 @@ compat::package_info game_compatibility::GetPkgInfo(const QString& pkg_path, con
|
||||
if (info.category == "GD")
|
||||
{
|
||||
// For now let's assume that PS3 Game Data packages are always updates or DLC.
|
||||
// Update packages always seem to have an APP_VER, so let's say it's a DLC otherwise.
|
||||
// Ideally this would simply be the package content type, but I am too lazy to implement this right now.
|
||||
if (info.version.isEmpty())
|
||||
if (reader.get_metadata().package_type & pkg_flag::PKG_FLAG_PATCH)
|
||||
{
|
||||
info.type = compat::package_type::dlc;
|
||||
info.type = compat::package_type::update;
|
||||
}
|
||||
else
|
||||
{
|
||||
info.type = compat::package_type::update;
|
||||
info.type = compat::package_type::dlc;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -327,12 +327,12 @@ compat::package_info game_compatibility::GetPkgInfo(const QString& pkg_path, con
|
||||
{
|
||||
if (info.version.toStdString() == package.version)
|
||||
{
|
||||
if (const std::string localized_title = package.get_title(title_key); !localized_title.empty())
|
||||
if (const std::string localized_title = package.get_title(localized_title_key); !localized_title.empty())
|
||||
{
|
||||
info.title = QString::fromStdString(localized_title);
|
||||
}
|
||||
|
||||
if (const std::string localized_changelog = package.get_changelog(changelog_key); !localized_changelog.empty())
|
||||
if (const std::string localized_changelog = package.get_changelog(localized_changelog_key); !localized_changelog.empty())
|
||||
{
|
||||
info.changelog = QString::fromStdString(localized_changelog);
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace compat
|
||||
std::vector<pkg_changelog> changelogs;
|
||||
std::vector<pkg_title> titles;
|
||||
|
||||
std::string get_changelog(const std::string& type) const
|
||||
std::string get_changelog(std::string_view type) const
|
||||
{
|
||||
if (const auto it = std::find_if(changelogs.cbegin(), changelogs.cend(), [type](const pkg_changelog& cl) { return cl.type == type; });
|
||||
it != changelogs.cend())
|
||||
@@ -50,7 +50,7 @@ namespace compat
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string get_title(const std::string& type) const
|
||||
std::string get_title(std::string_view type) const
|
||||
{
|
||||
if (const auto it = std::find_if(titles.cbegin(), titles.cend(), [type](const pkg_title& t) { return t.type == type; });
|
||||
it != titles.cend())
|
||||
|
||||
@@ -327,9 +327,15 @@ void game_list_context_menu::show_single_selection_context_menu(const game_info&
|
||||
manage_game_menu->addSeparator();
|
||||
|
||||
// Hide/rename game in game list
|
||||
QAction* hide_serial = manage_game_menu->addAction(tr("&Hide In Game List"));
|
||||
hide_serial->setCheckable(true);
|
||||
hide_serial->setChecked(m_game_list_frame->hidden_list().contains(QString::fromStdString(serial)));
|
||||
QAction* hide_hidden_serial = manage_game_menu->addAction(tr("&Hide Game In Game List"));
|
||||
hide_hidden_serial->setCheckable(true);
|
||||
hide_hidden_serial->setChecked(m_game_list_frame->hidden_list().contains(QString::fromStdString(serial)));
|
||||
QAction* hide_broken_serial = manage_game_menu->addAction(tr("&Hide Broken Game In Game List"));
|
||||
hide_broken_serial->setCheckable(true);
|
||||
hide_broken_serial->setChecked(m_game_list_frame->broken_list().contains(QString::fromStdString(serial)));
|
||||
QAction* hide_completed_serial = manage_game_menu->addAction(tr("&Hide Completed Game In Game List"));
|
||||
hide_completed_serial->setCheckable(true);
|
||||
hide_completed_serial->setChecked(m_game_list_frame->completed_list().contains(QString::fromStdString(serial)));
|
||||
QAction* rename_title = manage_game_menu->addAction(tr("&Rename In Game List"));
|
||||
|
||||
// Edit tooltip notes/reset time played
|
||||
@@ -694,7 +700,7 @@ void game_list_context_menu::show_single_selection_context_menu(const game_info&
|
||||
m_game_list_frame->ShowCustomConfigIcon(gameinfo);
|
||||
}
|
||||
});
|
||||
connect(hide_serial, &QAction::triggered, m_game_list_frame, [this, serial = QString::fromStdString(serial)](bool checked)
|
||||
connect(hide_hidden_serial, &QAction::triggered, m_game_list_frame, [this, serial = QString::fromStdString(serial)](bool checked)
|
||||
{
|
||||
if (checked)
|
||||
m_game_list_frame->hidden_list().insert(serial);
|
||||
@@ -704,6 +710,26 @@ void game_list_context_menu::show_single_selection_context_menu(const game_info&
|
||||
m_gui_settings->SetValue(gui::gl_hidden_list, QStringList(m_game_list_frame->hidden_list().values()));
|
||||
m_game_list_frame->Refresh();
|
||||
});
|
||||
connect(hide_broken_serial, &QAction::triggered, m_game_list_frame, [this, serial = QString::fromStdString(serial)](bool checked)
|
||||
{
|
||||
if (checked)
|
||||
m_game_list_frame->broken_list().insert(serial);
|
||||
else
|
||||
m_game_list_frame->broken_list().remove(serial);
|
||||
|
||||
m_gui_settings->SetValue(gui::gl_broken_list, QStringList(m_game_list_frame->broken_list().values()));
|
||||
m_game_list_frame->Refresh();
|
||||
});
|
||||
connect(hide_completed_serial, &QAction::triggered, m_game_list_frame, [this, serial = QString::fromStdString(serial)](bool checked)
|
||||
{
|
||||
if (checked)
|
||||
m_game_list_frame->completed_list().insert(serial);
|
||||
else
|
||||
m_game_list_frame->completed_list().remove(serial);
|
||||
|
||||
m_gui_settings->SetValue(gui::gl_completed_list, QStringList(m_game_list_frame->completed_list().values()));
|
||||
m_game_list_frame->Refresh();
|
||||
});
|
||||
connect(create_cpu_cache, &QAction::triggered, m_game_list_frame, [this, gameinfo]
|
||||
{
|
||||
if (m_gui_settings->GetBootConfirmation(m_game_list_frame))
|
||||
@@ -925,10 +951,10 @@ void game_list_context_menu::show_multi_selection_context_menu(const std::vector
|
||||
manage_game_menu->addSeparator();
|
||||
|
||||
// Hide game in game list
|
||||
QAction* hide_serial = manage_game_menu->addAction(tr("&Hide In Game List"));
|
||||
connect(hide_serial, &QAction::triggered, m_game_list_frame, [this, games]()
|
||||
QAction* hide_hidden_serial = manage_game_menu->addAction(tr("&Hide Game In Game List"));
|
||||
connect(hide_hidden_serial, &QAction::triggered, m_game_list_frame, [this, games]()
|
||||
{
|
||||
if (QMessageBox::question(m_game_list_frame, tr("Confirm Hiding"), tr("Hide in game list?")) != QMessageBox::Yes)
|
||||
if (QMessageBox::question(m_game_list_frame, tr("Confirm Hiding"), tr("Hide game in game list?")) != QMessageBox::Yes)
|
||||
return;
|
||||
|
||||
for (const auto& game : games)
|
||||
@@ -939,10 +965,38 @@ void game_list_context_menu::show_multi_selection_context_menu(const std::vector
|
||||
m_gui_settings->SetValue(gui::gl_hidden_list, QStringList(m_game_list_frame->hidden_list().values()));
|
||||
m_game_list_frame->Refresh();
|
||||
});
|
||||
QAction* hide_broken_serial = manage_game_menu->addAction(tr("&Hide Broken Game In Game List"));
|
||||
connect(hide_broken_serial, &QAction::triggered, m_game_list_frame, [this, games]()
|
||||
{
|
||||
if (QMessageBox::question(m_game_list_frame, tr("Confirm Hiding"), tr("Hide broken game in game list?")) != QMessageBox::Yes)
|
||||
return;
|
||||
|
||||
for (const auto& game : games)
|
||||
{
|
||||
m_game_list_frame->broken_list().insert(QString::fromStdString(game->info.serial));
|
||||
}
|
||||
|
||||
m_gui_settings->SetValue(gui::gl_broken_list, QStringList(m_game_list_frame->broken_list().values()));
|
||||
m_game_list_frame->Refresh();
|
||||
});
|
||||
QAction* hide_completed_serial = manage_game_menu->addAction(tr("&Hide Completed Game In Game List"));
|
||||
connect(hide_completed_serial, &QAction::triggered, m_game_list_frame, [this, games]()
|
||||
{
|
||||
if (QMessageBox::question(m_game_list_frame, tr("Confirm Hiding"), tr("Hide completed game in game list?")) != QMessageBox::Yes)
|
||||
return;
|
||||
|
||||
for (const auto& game : games)
|
||||
{
|
||||
m_game_list_frame->completed_list().insert(QString::fromStdString(game->info.serial));
|
||||
}
|
||||
|
||||
m_gui_settings->SetValue(gui::gl_completed_list, QStringList(m_game_list_frame->completed_list().values()));
|
||||
m_game_list_frame->Refresh();
|
||||
});
|
||||
|
||||
// Show game in game list
|
||||
QAction* show_serial = manage_game_menu->addAction(tr("&Show In Game List"));
|
||||
connect(show_serial, &QAction::triggered, m_game_list_frame, [this, games]()
|
||||
QAction* show_hidden_serial = manage_game_menu->addAction(tr("&Show Hidden Game In Game List"));
|
||||
connect(show_hidden_serial, &QAction::triggered, m_game_list_frame, [this, games]()
|
||||
{
|
||||
for (const auto& game : games)
|
||||
{
|
||||
@@ -952,6 +1006,28 @@ void game_list_context_menu::show_multi_selection_context_menu(const std::vector
|
||||
m_gui_settings->SetValue(gui::gl_hidden_list, QStringList(m_game_list_frame->hidden_list().values()));
|
||||
m_game_list_frame->Refresh();
|
||||
});
|
||||
QAction* show_broken_serial = manage_game_menu->addAction(tr("&Show Broken Game In Game List"));
|
||||
connect(show_broken_serial, &QAction::triggered, m_game_list_frame, [this, games]()
|
||||
{
|
||||
for (const auto& game : games)
|
||||
{
|
||||
m_game_list_frame->broken_list().remove(QString::fromStdString(game->info.serial));
|
||||
}
|
||||
|
||||
m_gui_settings->SetValue(gui::gl_broken_list, QStringList(m_game_list_frame->broken_list().values()));
|
||||
m_game_list_frame->Refresh();
|
||||
});
|
||||
QAction* show_completed_serial = manage_game_menu->addAction(tr("&Show Completed Game In Game List"));
|
||||
connect(show_completed_serial, &QAction::triggered, m_game_list_frame, [this, games]()
|
||||
{
|
||||
for (const auto& game : games)
|
||||
{
|
||||
m_game_list_frame->completed_list().remove(QString::fromStdString(game->info.serial));
|
||||
}
|
||||
|
||||
m_gui_settings->SetValue(gui::gl_completed_list, QStringList(m_game_list_frame->completed_list().values()));
|
||||
m_game_list_frame->Refresh();
|
||||
});
|
||||
|
||||
manage_game_menu->addSeparator();
|
||||
|
||||
|
||||
@@ -55,6 +55,8 @@ game_list_frame::game_list_frame(std::shared_ptr<gui_settings> gui_settings, std
|
||||
m_col_sort_order = m_gui_settings->GetValue(gui::gl_sortAsc).toBool() ? Qt::AscendingOrder : Qt::DescendingOrder;
|
||||
m_sort_column = m_gui_settings->GetValue(gui::gl_sortCol).toInt();
|
||||
m_hidden_list = gui::utils::list_to_set(m_gui_settings->GetValue(gui::gl_hidden_list).toStringList());
|
||||
m_broken_list = gui::utils::list_to_set(m_gui_settings->GetValue(gui::gl_broken_list).toStringList());
|
||||
m_completed_list = gui::utils::list_to_set(m_gui_settings->GetValue(gui::gl_completed_list).toStringList());
|
||||
|
||||
m_old_layout_is_list = m_is_list_layout;
|
||||
|
||||
@@ -336,7 +338,9 @@ bool game_list_frame::IsEntryVisible(const game_info& game, bool search_fallback
|
||||
};
|
||||
|
||||
const QString serial = QString::fromStdString(game->info.serial);
|
||||
const bool is_visible = m_show_hidden || !m_hidden_list.contains(serial);
|
||||
const bool is_visible = (m_show_hidden || !m_hidden_list.contains(serial)) &&
|
||||
(m_show_broken || !m_broken_list.contains(serial)) &&
|
||||
(m_show_completed || !m_completed_list.contains(serial));
|
||||
return is_visible && matches_category() && SearchMatchesApp(QString::fromStdString(game->info.name), serial, search_fallback);
|
||||
}
|
||||
|
||||
@@ -1067,9 +1071,13 @@ void game_list_frame::OnRefreshFinished()
|
||||
return title1.toLower() < title2.toLower();
|
||||
});
|
||||
|
||||
// clean up hidden games list
|
||||
// clean up hidden lists (Hide, Broken, Completed)
|
||||
m_hidden_list.intersect(m_serials);
|
||||
m_gui_settings->SetValue(gui::gl_hidden_list, QStringList(m_hidden_list.values()));
|
||||
m_broken_list.intersect(m_serials);
|
||||
m_gui_settings->SetValue(gui::gl_broken_list, QStringList(m_broken_list.values()));
|
||||
m_completed_list.intersect(m_serials);
|
||||
m_gui_settings->SetValue(gui::gl_completed_list, QStringList(m_completed_list.values()));
|
||||
m_serials.clear();
|
||||
m_path_list.clear();
|
||||
m_path_entries.clear();
|
||||
@@ -1285,6 +1293,16 @@ void game_list_frame::SetShowHidden(bool show)
|
||||
m_show_hidden = show;
|
||||
}
|
||||
|
||||
void game_list_frame::SetShowBroken(bool show)
|
||||
{
|
||||
m_show_broken = show;
|
||||
}
|
||||
|
||||
void game_list_frame::SetShowCompleted(bool show)
|
||||
{
|
||||
m_show_completed = show;
|
||||
}
|
||||
|
||||
void game_list_frame::SetListMode(bool is_list)
|
||||
{
|
||||
m_old_layout_is_list = m_is_list_layout;
|
||||
|
||||
@@ -56,6 +56,10 @@ public:
|
||||
|
||||
void SetShowHidden(bool show);
|
||||
|
||||
void SetShowBroken(bool show);
|
||||
|
||||
void SetShowCompleted(bool show);
|
||||
|
||||
content_integrity* GetIsoIntegrity() const { return ensure(m_iso_integrity); }
|
||||
content_integrity* GetPsnContentIntegrity() const { return ensure(m_psn_content_integrity); }
|
||||
content_integrity* GetPsnDlcIntegrity() const { return ensure(m_psn_dlc_integrity); }
|
||||
@@ -70,6 +74,8 @@ public:
|
||||
std::map<QString, QString>& notes() { return m_notes; }
|
||||
std::map<QString, QString>& titles() { return m_titles; }
|
||||
QSet<QString>& hidden_list() { return m_hidden_list; }
|
||||
QSet<QString>& broken_list() { return m_broken_list; }
|
||||
QSet<QString>& completed_list() { return m_completed_list; }
|
||||
|
||||
bool IsEntryVisible(const game_info& game, bool search_fallback = false) const;
|
||||
|
||||
@@ -206,6 +212,10 @@ private:
|
||||
QFutureWatcher<void> m_refresh_watcher;
|
||||
QSet<QString> m_hidden_list;
|
||||
bool m_show_hidden{false};
|
||||
QSet<QString> m_broken_list;
|
||||
bool m_show_broken{false};
|
||||
QSet<QString> m_completed_list;
|
||||
bool m_show_completed{false};
|
||||
|
||||
// Search
|
||||
QString m_search_text;
|
||||
|
||||
@@ -527,7 +527,7 @@ void gs_frame::toggle_recording()
|
||||
m_video_encoder->set_path(video_path);
|
||||
m_video_encoder->set_framerate(g_cfg_recording.video.framerate);
|
||||
m_video_encoder->set_video_bitrate(g_cfg_recording.video.video_bps);
|
||||
m_video_encoder->set_video_codec(g_cfg_recording.video.video_codec);
|
||||
m_video_encoder->set_video_codec(g_cfg_recording.video.codec_id, g_cfg_recording.video.codec_name.get());
|
||||
m_video_encoder->set_max_b_frames(g_cfg_recording.video.max_b_frames);
|
||||
m_video_encoder->set_gop_size(g_cfg_recording.video.gop_size);
|
||||
m_video_encoder->set_output_format(output_format);
|
||||
@@ -557,7 +557,7 @@ void gs_frame::toggle_recording()
|
||||
}
|
||||
|
||||
m_video_encoder->set_audio_bitrate(g_cfg_recording.audio.audio_bps);
|
||||
m_video_encoder->set_audio_codec(g_cfg_recording.audio.audio_codec);
|
||||
m_video_encoder->set_audio_codec(g_cfg_recording.audio.codec_id, g_cfg_recording.audio.codec_name.get());
|
||||
m_video_encoder->encode();
|
||||
|
||||
if (m_video_encoder->has_error)
|
||||
|
||||
@@ -0,0 +1,479 @@
|
||||
#include "stdafx.h"
|
||||
#include "guest_memory_dumper.h"
|
||||
#include "progress_dialog.h"
|
||||
|
||||
#include "Emu/Cell/PPUThread.h"
|
||||
#include "Emu/Cell/SPUThread.h"
|
||||
#include "Emu/Memory/vm.h"
|
||||
#include "Emu/RSX/RSXThread.h"
|
||||
#include "Emu/System.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QDir>
|
||||
#include <QElapsedTimer>
|
||||
#include <QFile>
|
||||
#include <QFileDialog>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
#include <QStorageInfo>
|
||||
|
||||
LOG_CHANNEL(gui_log, "GUI");
|
||||
|
||||
extern void qt_events_aware_op(int repeat_duration_ms, std::function<bool()> wrapped_op);
|
||||
|
||||
guest_memory_dumper::guest_memory_dumper(QWidget* parent, bool delete_later)
|
||||
: QObject(parent), m_parent(parent), m_delete_later(delete_later)
|
||||
{
|
||||
}
|
||||
|
||||
// Emu.Pause() only requests the pause: every emulated thread drains into its
|
||||
// wait or stop state asynchronously. The dump must not begin until each PPU,
|
||||
// SPU and RSX thread has actually settled, or it could capture torn state.
|
||||
bool guest_memory_dumper::emulated_processors_quiesced()
|
||||
{
|
||||
bool quiesced = true;
|
||||
const auto check_cpu = [&](u32, cpu_thread& cpu)
|
||||
{
|
||||
const auto state = +cpu.state;
|
||||
if (!::is_stopped(state) && !(state & cpu_flag::wait))
|
||||
{
|
||||
quiesced = false;
|
||||
}
|
||||
};
|
||||
|
||||
if (g_fxo->is_init<id_manager::id_map<named_thread<ppu_thread>>>())
|
||||
{
|
||||
idm::select<named_thread<ppu_thread>>(check_cpu);
|
||||
}
|
||||
|
||||
if (g_fxo->is_init<id_manager::id_map<named_thread<spu_thread>>>())
|
||||
{
|
||||
idm::select<named_thread<spu_thread>>(check_cpu);
|
||||
}
|
||||
|
||||
if (const auto rsx = g_fxo->try_get<rsx::thread>())
|
||||
{
|
||||
check_cpu(0, *rsx);
|
||||
}
|
||||
|
||||
return quiesced;
|
||||
}
|
||||
|
||||
QString guest_memory_dumper::hex_u64(u64 value, int width)
|
||||
{
|
||||
return QStringLiteral("0x%1").arg(value, width, 16, QLatin1Char('0'));
|
||||
}
|
||||
|
||||
void guest_memory_dumper::dump_guest_memory()
|
||||
{
|
||||
if (Emu.IsStopped() || Emu.IsStarting())
|
||||
{
|
||||
QMessageBox::warning(m_parent, tr("Dump Guest Memory"), tr("Start a game before creating a guest-memory dump."));
|
||||
return;
|
||||
}
|
||||
|
||||
const QString default_parent = QString::fromStdString(fs::get_config_dir() + "guest_memory_dumps");
|
||||
QDir().mkpath(default_parent);
|
||||
|
||||
QString title_id = QString::fromStdString(Emu.GetTitleID());
|
||||
if (title_id.isEmpty())
|
||||
{
|
||||
title_id = QStringLiteral("RPCS3");
|
||||
}
|
||||
|
||||
// Remove the output folder again unless the dump ran to completion, so a
|
||||
// failed or cancelled dump does not leave an empty folder or a partial
|
||||
// image behind. This is declared before the output file so it destructs
|
||||
// after the file is closed.
|
||||
struct cleanup_guard
|
||||
{
|
||||
QDir dir;
|
||||
bool enabled = false;
|
||||
bool keep = false;
|
||||
~cleanup_guard()
|
||||
{
|
||||
if (enabled && !keep && !dir.removeRecursively())
|
||||
{
|
||||
gui_log.error("Could not remove incomplete guest memory dump folder: %s", dir.absolutePath());
|
||||
}
|
||||
}
|
||||
} cleanup;
|
||||
|
||||
QString picker_parent = default_parent;
|
||||
QDir output;
|
||||
fs::file guest_file;
|
||||
bool sparse_image = false;
|
||||
|
||||
while (!sparse_image)
|
||||
{
|
||||
const QString selected_parent = QFileDialog::getExistingDirectory(m_parent, tr("Select Guest Memory Dump Folder"), picker_parent, QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
|
||||
|
||||
if (selected_parent.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// The native folder dialog runs its own event loop, so the game may have
|
||||
// stopped while the user was choosing a destination.
|
||||
if (Emu.IsStopped() || Emu.IsStarting())
|
||||
{
|
||||
QMessageBox::warning(m_parent, tr("Dump Guest Memory"), tr("The game stopped before the guest-memory dump could begin."));
|
||||
return;
|
||||
}
|
||||
|
||||
const QString timestamp = QDateTime::currentDateTimeUtc().toString(QStringLiteral("yyyyMMdd_HHmmss_zzz"));
|
||||
const QString folder_stem = QStringLiteral("%1_guest_memory_%2").arg(title_id, timestamp);
|
||||
QDir parent(selected_parent);
|
||||
QString folder_name = folder_stem;
|
||||
|
||||
for (u32 suffix = 2; parent.exists(folder_name); suffix++)
|
||||
{
|
||||
folder_name = QStringLiteral("%1_%2").arg(folder_stem).arg(suffix);
|
||||
}
|
||||
|
||||
if (!parent.mkpath(folder_name))
|
||||
{
|
||||
QMessageBox::critical(m_parent, tr("Dump Guest Memory"), tr("Could not create the output folder:\n%0").arg(parent.filePath(folder_name)));
|
||||
return;
|
||||
}
|
||||
|
||||
output.setPath(parent.filePath(folder_name));
|
||||
cleanup.dir = output;
|
||||
cleanup.enabled = true;
|
||||
|
||||
const QString guest_path = output.filePath(QStringLiteral("guest_memory.bin"));
|
||||
if (!guest_file.open(guest_path.toStdString(), fs::rewrite))
|
||||
{
|
||||
QMessageBox::critical(m_parent, tr("Dump Guest Memory"), tr("Could not create:\n%0").arg(guest_path));
|
||||
return;
|
||||
}
|
||||
|
||||
// POSIX file systems create holes for unwritten ranges by default; Windows
|
||||
// needs the sparse attribute set explicitly.
|
||||
sparse_image = fs::set_sparse(guest_file);
|
||||
|
||||
if (!sparse_image)
|
||||
{
|
||||
guest_file.close();
|
||||
if (!output.removeRecursively())
|
||||
{
|
||||
QMessageBox::critical(m_parent, tr("Dump Guest Memory"), tr("The selected destination does not support sparse files, and the incomplete output folder could not be removed:\n%0").arg(output.absolutePath()));
|
||||
return;
|
||||
}
|
||||
|
||||
cleanup.enabled = false;
|
||||
QMessageBox message(QMessageBox::Critical, tr("Dump Guest Memory"), tr("The selected destination does not support sparse files. Choose a location on an NTFS volume."), QMessageBox::NoButton, m_parent);
|
||||
QPushButton* choose_button = message.addButton(tr("Choose Another Location"), QMessageBox::AcceptRole);
|
||||
message.addButton(QMessageBox::Cancel);
|
||||
message.setDefaultButton(choose_button);
|
||||
message.exec();
|
||||
|
||||
if (message.clickedButton() != choose_button)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
picker_parent = selected_parent;
|
||||
}
|
||||
}
|
||||
|
||||
const bool resume_after_dump = Emu.IsRunning();
|
||||
|
||||
if (resume_after_dump && !Emu.Pause(false, false))
|
||||
{
|
||||
QMessageBox::critical(m_parent, tr("Dump Guest Memory"), tr("Could not pause emulation for a consistent memory snapshot."));
|
||||
return;
|
||||
}
|
||||
|
||||
struct resume_guard
|
||||
{
|
||||
bool enabled = false;
|
||||
~resume_guard()
|
||||
{
|
||||
if (enabled)
|
||||
{
|
||||
Emu.Resume();
|
||||
}
|
||||
}
|
||||
} resume{resume_after_dump};
|
||||
|
||||
bool quiesced = false;
|
||||
QElapsedTimer pause_timer;
|
||||
pause_timer.start();
|
||||
qt_events_aware_op(5, [&]()
|
||||
{
|
||||
quiesced = emulated_processors_quiesced();
|
||||
return quiesced || pause_timer.elapsed() >= 5000;
|
||||
});
|
||||
|
||||
if (!quiesced)
|
||||
{
|
||||
QMessageBox::critical(m_parent, tr("Dump Guest Memory"), tr("One or more emulated processors did not stop within five seconds. No dump was created."));
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<guest_memory_region> regions;
|
||||
guest_memory_region current{};
|
||||
|
||||
const auto finish_region = [&]()
|
||||
{
|
||||
if (current.size)
|
||||
{
|
||||
regions.emplace_back(current);
|
||||
current = {};
|
||||
}
|
||||
};
|
||||
|
||||
// Walk the guest page table and coalesce contiguous pages with identical
|
||||
// flags into regions, so the manifest mirrors the guest memory map instead
|
||||
// of listing a million single pages.
|
||||
for (u32 page = 0; page < guest_address_space_size / guest_page_size; page++)
|
||||
{
|
||||
const u32 address = static_cast<u32>(static_cast<u64>(page) * guest_page_size);
|
||||
const auto [allocated, flags] = vm::get_addr_flags(address);
|
||||
|
||||
if (!allocated)
|
||||
{
|
||||
finish_region();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (current.size && (current.flags != flags || static_cast<u64>(current.start) + current.size != address))
|
||||
{
|
||||
finish_region();
|
||||
}
|
||||
|
||||
if (!current.size)
|
||||
{
|
||||
current.start = address;
|
||||
current.flags = flags;
|
||||
}
|
||||
|
||||
current.size += guest_page_size;
|
||||
}
|
||||
|
||||
finish_region();
|
||||
|
||||
// SPU local store is also part of the guest map (at each thread's
|
||||
// vm_offset), but a per thread copy with id, pc and type recorded in the
|
||||
// manifest is much easier to analyze than digging it out of the main image.
|
||||
std::vector<spu_local_store_dump> spu_dumps;
|
||||
if (g_fxo->is_init<id_manager::id_map<named_thread<spu_thread>>>())
|
||||
{
|
||||
idm::select<named_thread<spu_thread>>([&](u32 id, spu_thread& spu)
|
||||
{
|
||||
spu_local_store_dump dump;
|
||||
dump.id = id;
|
||||
dump.lv2_id = spu.lv2_id;
|
||||
dump.index = spu.index;
|
||||
dump.pc = spu.pc;
|
||||
dump.vm_offset = spu.vm_offset();
|
||||
dump.type = static_cast<u32>(spu.get_type());
|
||||
dump.name = spu.get_name();
|
||||
spu_dumps.emplace_back(std::move(dump));
|
||||
});
|
||||
}
|
||||
|
||||
u64 guest_bytes = 0;
|
||||
for (const auto& region : regions)
|
||||
{
|
||||
guest_bytes += region.size;
|
||||
}
|
||||
|
||||
const u64 total_bytes = guest_bytes + static_cast<u64>(spu_dumps.size()) * SPU_LS_SIZE;
|
||||
|
||||
// Preflight the destination. A sparse image costs roughly the allocated
|
||||
// bytes, so warn when even that plus some margin does not fit.
|
||||
const QStorageInfo storage(output.absolutePath());
|
||||
const u64 available = storage.bytesAvailable() > 0 ? static_cast<u64>(storage.bytesAvailable()) : 0;
|
||||
|
||||
if (const u64 needed = total_bytes + (64ull << 20); available < needed)
|
||||
{
|
||||
if (QMessageBox::question(m_parent, tr("Dump Guest Memory"), tr("The destination may not have enough free space (roughly %0 MiB needed, %1 MiB available).\n\nContinue anyway?").arg(needed >> 20).arg(available >> 20)) != QMessageBox::Yes)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
atomic_t<u64> completed_bytes{0};
|
||||
progress_dialog progress(tr("Dump Guest Memory"), tr("Dumping PS3 guest memory..."), tr("Cancel"), 0, 1000, false, m_parent);
|
||||
progress.setCancelButton(nullptr);
|
||||
progress.setMinimumDuration(0);
|
||||
|
||||
if (!guest_file.trunc(guest_address_space_size))
|
||||
{
|
||||
QMessageBox::critical(m_parent, tr("Dump Guest Memory"), tr("Could not size the 4 GB address image."));
|
||||
return;
|
||||
}
|
||||
|
||||
// Read through the sudo mirror: it maps every allocated page as read/write,
|
||||
// so pages the guest keeps protected (reservation notification, no access
|
||||
// areas) are captured as well instead of faulting or being skipped. Copy
|
||||
// into ordinary host memory before passing data to fs::file, and perform all
|
||||
// guest-memory reads and file writes off the UI thread.
|
||||
enum class dump_write_error
|
||||
{
|
||||
none,
|
||||
guest_seek,
|
||||
guest_write,
|
||||
spu_write,
|
||||
};
|
||||
|
||||
dump_write_error write_error = dump_write_error::none;
|
||||
u64 error_address = 0;
|
||||
QString error_path;
|
||||
const QString output_path = output.absolutePath();
|
||||
|
||||
named_thread dump_thread("Guest Memory Dumper", [&]()
|
||||
{
|
||||
std::vector<u8> middle_buffer(dump_io_chunk_size);
|
||||
|
||||
for (const auto& region : regions)
|
||||
{
|
||||
if (guest_file.seek(region.start) != region.start)
|
||||
{
|
||||
write_error = dump_write_error::guest_seek;
|
||||
error_address = region.start;
|
||||
return;
|
||||
}
|
||||
|
||||
for (u64 offset = 0; offset < region.size;)
|
||||
{
|
||||
const u64 chunk_size = std::min(dump_io_chunk_size, region.size - offset);
|
||||
const u64 address = static_cast<u64>(region.start) + offset;
|
||||
|
||||
std::memcpy(middle_buffer.data(), vm::g_sudo_addr + address, chunk_size);
|
||||
if (guest_file.write(middle_buffer.data(), chunk_size) != chunk_size)
|
||||
{
|
||||
write_error = dump_write_error::guest_write;
|
||||
error_address = address;
|
||||
return;
|
||||
}
|
||||
|
||||
offset += chunk_size;
|
||||
completed_bytes.fetch_add(chunk_size);
|
||||
}
|
||||
}
|
||||
|
||||
guest_file.close();
|
||||
|
||||
QDir worker_output(output_path);
|
||||
for (const auto& dump : spu_dumps)
|
||||
{
|
||||
const QString filename = QStringLiteral("spu_%1_ls.bin").arg(dump.id, 8, 16, QLatin1Char('0'));
|
||||
const QString file_path = worker_output.filePath(filename);
|
||||
std::memcpy(middle_buffer.data(), vm::g_sudo_addr + dump.vm_offset, SPU_LS_SIZE);
|
||||
fs::file file(file_path.toStdString(), fs::rewrite);
|
||||
|
||||
if (!file || file.write(middle_buffer.data(), SPU_LS_SIZE) != SPU_LS_SIZE)
|
||||
{
|
||||
write_error = dump_write_error::spu_write;
|
||||
error_path = file_path;
|
||||
return;
|
||||
}
|
||||
|
||||
completed_bytes.fetch_add(SPU_LS_SIZE);
|
||||
}
|
||||
});
|
||||
|
||||
qt_events_aware_op(10, [&]()
|
||||
{
|
||||
const u64 completed = completed_bytes.load();
|
||||
progress.SetValue(total_bytes ? static_cast<int>(completed * 1000 / total_bytes) : 1000);
|
||||
return static_cast<thread_state>(dump_thread) == thread_state::finished;
|
||||
});
|
||||
dump_thread();
|
||||
|
||||
if (write_error == dump_write_error::guest_seek)
|
||||
{
|
||||
QMessageBox::critical(m_parent, tr("Dump Guest Memory"), tr("Could not seek to guest address %0 in the output file.").arg(hex_u64(error_address)));
|
||||
return;
|
||||
}
|
||||
|
||||
if (write_error == dump_write_error::guest_write)
|
||||
{
|
||||
QMessageBox::critical(m_parent, tr("Dump Guest Memory"), tr("Writing failed at guest address %0. The dump is incomplete.").arg(hex_u64(error_address)));
|
||||
return;
|
||||
}
|
||||
|
||||
if (write_error == dump_write_error::spu_write)
|
||||
{
|
||||
QMessageBox::critical(m_parent, tr("Dump Guest Memory"), tr("Could not write SPU local store:\n%0").arg(error_path));
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonArray spu_json;
|
||||
for (const auto& dump : spu_dumps)
|
||||
{
|
||||
const QString filename = QStringLiteral("spu_%1_ls.bin").arg(dump.id, 8, 16, QLatin1Char('0'));
|
||||
QJsonObject item;
|
||||
item.insert(QStringLiteral("file"), filename);
|
||||
item.insert(QStringLiteral("id"), hex_u64(dump.id));
|
||||
item.insert(QStringLiteral("lv2_id"), hex_u64(dump.lv2_id));
|
||||
item.insert(QStringLiteral("index"), static_cast<int>(dump.index));
|
||||
item.insert(QStringLiteral("pc"), hex_u64(dump.pc, 5));
|
||||
item.insert(QStringLiteral("vm_offset"), hex_u64(dump.vm_offset));
|
||||
item.insert(QStringLiteral("type"), static_cast<int>(dump.type));
|
||||
item.insert(QStringLiteral("name"), QString::fromStdString(dump.name));
|
||||
item.insert(QStringLiteral("size"), static_cast<int>(SPU_LS_SIZE));
|
||||
spu_json.append(item);
|
||||
}
|
||||
|
||||
QJsonArray region_json;
|
||||
for (const auto& region : regions)
|
||||
{
|
||||
QJsonObject item;
|
||||
item.insert(QStringLiteral("start"), hex_u64(region.start));
|
||||
item.insert(QStringLiteral("end_exclusive"), hex_u64(static_cast<u64>(region.start) + region.size));
|
||||
item.insert(QStringLiteral("size"), static_cast<double>(region.size));
|
||||
item.insert(QStringLiteral("file_offset"), hex_u64(region.start));
|
||||
item.insert(QStringLiteral("raw_page_flags"), static_cast<int>(region.flags));
|
||||
item.insert(QStringLiteral("readable"), !!(region.flags & vm::page_readable));
|
||||
item.insert(QStringLiteral("writable"), !!(region.flags & vm::page_writable));
|
||||
item.insert(QStringLiteral("executable"), !!(region.flags & vm::page_executable));
|
||||
region_json.append(item);
|
||||
}
|
||||
|
||||
QJsonObject manifest;
|
||||
manifest.insert(QStringLiteral("format"), QStringLiteral("rpcs3-guest-memory-dump"));
|
||||
manifest.insert(QStringLiteral("version"), 1);
|
||||
manifest.insert(QStringLiteral("created_utc"), QDateTime::currentDateTimeUtc().toString(Qt::ISODateWithMs));
|
||||
manifest.insert(QStringLiteral("title"), QString::fromStdString(Emu.GetTitle()));
|
||||
manifest.insert(QStringLiteral("title_id"), QString::fromStdString(Emu.GetTitleID()));
|
||||
manifest.insert(QStringLiteral("address_space_file"), QStringLiteral("guest_memory.bin"));
|
||||
manifest.insert(QStringLiteral("address_space_size"), hex_u64(guest_address_space_size, 9));
|
||||
manifest.insert(QStringLiteral("page_size"), static_cast<int>(guest_page_size));
|
||||
manifest.insert(QStringLiteral("allocated_bytes"), static_cast<double>(guest_bytes));
|
||||
manifest.insert(QStringLiteral("file_offset_equals_guest_address"), true);
|
||||
manifest.insert(QStringLiteral("sparse_image"), sparse_image);
|
||||
manifest.insert(QStringLiteral("regions"), region_json);
|
||||
manifest.insert(QStringLiteral("spu_local_stores"), spu_json);
|
||||
|
||||
QFile manifest_file(output.filePath(QStringLiteral("manifest.json")));
|
||||
if (!manifest_file.open(QIODevice::WriteOnly | QIODevice::Truncate) || manifest_file.write(QJsonDocument(manifest).toJson(QJsonDocument::Indented)) < 0)
|
||||
{
|
||||
QMessageBox::critical(m_parent, tr("Dump Guest Memory"), tr("Could not write the dump manifest."));
|
||||
return;
|
||||
}
|
||||
|
||||
manifest_file.close();
|
||||
cleanup.keep = true;
|
||||
progress.SetValue(1000);
|
||||
gui_log.success("Guest memory dump complete: %s (%u regions, %u SPU local stores, %u bytes allocated)", output.absolutePath(), regions.size(), spu_dumps.size(), guest_bytes);
|
||||
|
||||
if (resume.enabled)
|
||||
{
|
||||
Emu.Resume();
|
||||
resume.enabled = false;
|
||||
}
|
||||
|
||||
QMessageBox::information(m_parent, tr("Dump Guest Memory"), tr("Guest memory dump completed.\n\n%0\n\nThe 4 GB guest_memory.bin file is sparse: its file offset equals the PS3 guest address, while only allocated pages consume disk space.").arg(output.absolutePath()));
|
||||
|
||||
if (m_delete_later)
|
||||
{
|
||||
deleteLater();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include "util/types.hpp"
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
class guest_memory_dumper : QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
guest_memory_dumper(QWidget* parent, bool delete_later = false);
|
||||
virtual ~guest_memory_dumper() {}
|
||||
|
||||
void dump_guest_memory();
|
||||
|
||||
private:
|
||||
QWidget* m_parent = nullptr;
|
||||
bool m_delete_later = false;
|
||||
|
||||
static constexpr u64 guest_address_space_size = 0x1'0000'0000ull;
|
||||
static constexpr u32 guest_page_size = 0x1000;
|
||||
static constexpr u64 dump_io_chunk_size = 4 * 1024 * 1024;
|
||||
|
||||
struct guest_memory_region
|
||||
{
|
||||
u32 start = 0;
|
||||
u64 size = 0;
|
||||
u8 flags = 0;
|
||||
};
|
||||
|
||||
struct spu_local_store_dump
|
||||
{
|
||||
u32 id = 0;
|
||||
u32 lv2_id = 0;
|
||||
u32 index = 0;
|
||||
u32 pc = 0;
|
||||
u32 vm_offset = 0;
|
||||
u32 type = 0;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
static bool emulated_processors_quiesced();
|
||||
static QString hex_u64(u64 value, int width = 8);
|
||||
};
|
||||
@@ -222,7 +222,11 @@ namespace gui
|
||||
const gui_save gl_textFactor = gui_save(game_list, "textFactor", qreal{2.0});
|
||||
const gui_save gl_marginFactor = gui_save(game_list, "marginFactor", qreal{0.09});
|
||||
const gui_save gl_show_hidden = gui_save(game_list, "show_hidden", false);
|
||||
const gui_save gl_show_broken = gui_save(game_list, "show_broken", false);
|
||||
const gui_save gl_show_completed = gui_save(game_list, "show_completed", false);
|
||||
const gui_save gl_hidden_list = gui_save(game_list, "hidden_list", QStringList());
|
||||
const gui_save gl_broken_list = gui_save(game_list, "broken_list", QStringList());
|
||||
const gui_save gl_completed_list = gui_save(game_list, "completed_list", QStringList());
|
||||
const gui_save gl_draw_compat = gui_save(game_list, "draw_compat", false);
|
||||
const gui_save gl_pref_gd_icon = gui_save(game_list, "pref_gd_icon", false);
|
||||
const gui_save gl_custom_icon = gui_save(game_list, "custom_icon", true);
|
||||
@@ -294,6 +298,8 @@ namespace gui
|
||||
const gui_save tr_show_locked = gui_save(trophy, "show_locked", true);
|
||||
const gui_save tr_show_unlocked = gui_save(trophy, "show_unlocked", true);
|
||||
const gui_save tr_show_hidden = gui_save(trophy, "show_hidden", false);
|
||||
const gui_save tr_show_broken = gui_save(trophy, "show_broken", false);
|
||||
const gui_save tr_show_completed = gui_save(trophy, "show_completed", false);
|
||||
const gui_save tr_show_bronze = gui_save(trophy, "show_bronze", true);
|
||||
const gui_save tr_show_silver = gui_save(trophy, "show_silver", true);
|
||||
const gui_save tr_show_gold = gui_save(trophy, "show_gold", true);
|
||||
|
||||
@@ -208,6 +208,7 @@ private:
|
||||
case localized_string_id::HOME_MENU_RESUME: return tr("Resume Game");
|
||||
case localized_string_id::HOME_MENU_FRIENDS: return tr("Friends");
|
||||
case localized_string_id::HOME_MENU_FRIENDS_REQUESTS: return tr("Pending Friend Requests");
|
||||
case localized_string_id::HOME_MENU_FRIENDS_GAME_INVITES: return tr("Game Invitations");
|
||||
case localized_string_id::HOME_MENU_FRIENDS_BLOCKED: return tr("Blocked Users");
|
||||
case localized_string_id::HOME_MENU_FRIENDS_STATUS_ONLINE: return tr("Online");
|
||||
case localized_string_id::HOME_MENU_FRIENDS_STATUS_OFFLINE: return tr("Offline");
|
||||
@@ -221,6 +222,9 @@ private:
|
||||
case localized_string_id::HOME_MENU_FRIENDS_CANCEL_REQUEST_MSG: return tr("Cancel Request?\n\n%0").arg(std::forward<Args>(args)...);
|
||||
case localized_string_id::HOME_MENU_FRIENDS_REJECT_REQUEST_MSG: return tr("Reject Request?\n\n%0").arg(std::forward<Args>(args)...);
|
||||
case localized_string_id::HOME_MENU_FRIENDS_REJECT_REQUEST: return tr("Reject Request");
|
||||
case localized_string_id::HOME_MENU_FRIENDS_ACCEPT_GAME_INVITE_MSG: return tr("Accept game invitation from %0?").arg(std::forward<Args>(args)...);
|
||||
case localized_string_id::HOME_MENU_FRIENDS_REJECT_GAME_INVITE_MSG: return tr("Reject game invitation from %0?").arg(std::forward<Args>(args)...);
|
||||
case localized_string_id::HOME_MENU_FRIENDS_REJECT_GAME_INVITE: return tr("Reject Invitation");
|
||||
case localized_string_id::HOME_MENU_FRIENDS_NEXT_LIST: return tr("Next list");
|
||||
case localized_string_id::HOME_MENU_RESTART: return tr("Restart Game");
|
||||
case localized_string_id::HOME_MENU_SETTINGS: return tr("Settings");
|
||||
@@ -323,6 +327,10 @@ private:
|
||||
case localized_string_id::HOME_MENU_TROPHY_GRADE_SILVER: return tr("Silver", "Trophy type");
|
||||
case localized_string_id::HOME_MENU_TROPHY_GRADE_GOLD: return tr("Gold", "Trophy type");
|
||||
case localized_string_id::HOME_MENU_TROPHY_GRADE_PLATINUM: return tr("Platinum", "Trophy type");
|
||||
case localized_string_id::HOME_MENU_TROPHY_SORT_GAME_DEFAULT: return tr("Sort: Game Default");
|
||||
case localized_string_id::HOME_MENU_TROPHY_SORT_NOT_EARNED: return tr("Sort: Not Earned");
|
||||
case localized_string_id::HOME_MENU_TROPHY_SORT_EARNED_DATE: return tr("Sort: Earned Date");
|
||||
case localized_string_id::HOME_MENU_TROPHY_SORT_GRADE: return tr("Sort: Grade");
|
||||
case localized_string_id::AUDIO_MUTED: return tr("Audio muted", "Audio");
|
||||
case localized_string_id::AUDIO_UNMUTED: return tr("Audio unmuted", "Audio");
|
||||
case localized_string_id::AUDIO_CHANGED: return tr("Volume changed to %0", "Audio").arg(std::forward<Args>(args)...);
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
#include "sound_effect_manager_dialog.h"
|
||||
#include "recording_settings_dialog.h"
|
||||
#include "config_database.h"
|
||||
#include "guest_memory_dumper.h"
|
||||
|
||||
#include <thread>
|
||||
#include <unordered_set>
|
||||
@@ -1246,13 +1247,13 @@ bool main_window::HandlePackageInstallation(main_window* mw, QStringList file_pa
|
||||
{
|
||||
std::string resolved_path = Emu.GetCallbacks().resolve_path(it->first);
|
||||
|
||||
if (resolved_path.empty() || claimed_paths.count(resolved_path))
|
||||
if (resolved_path.empty() || claimed_paths.contains(resolved_path))
|
||||
{
|
||||
it = paths.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
claimed_paths.emplace(std::move(resolved_path));
|
||||
claimed_paths.insert(std::move(resolved_path));
|
||||
it++;
|
||||
}
|
||||
}
|
||||
@@ -1325,23 +1326,27 @@ bool main_window::HandlePackageInstallation(main_window* mw, QStringList file_pa
|
||||
}
|
||||
|
||||
const bool has_expected = !result.version.expected.empty();
|
||||
const bool has_found = !result.version.found.empty();
|
||||
if (has_expected && has_found)
|
||||
const bool has_installed = !result.version.installed.empty();
|
||||
|
||||
if (has_expected && has_installed)
|
||||
{
|
||||
QMessageBox::warning(mw, tr("Warning!"), tr("Package cannot be installed on top of the current data.\nUpdate is for version %1, but you have version %2.\n\nTried to install: %3")
|
||||
.arg(QString::fromStdString(result.version.expected)).arg(QString::fromStdString(result.version.found)).arg(package->path));
|
||||
QMessageBox::warning(mw, tr("Warning!"), tr("Package cannot be installed on top of the current data.\nUpdate with version %0 is for version %1, but you have version %2.\n\nTried to install: %3")
|
||||
.arg(QString::fromStdString(result.version.app_ver)).arg(QString::fromStdString(result.version.expected)).arg(QString::fromStdString(result.version.installed)).arg(package->path));
|
||||
}
|
||||
else if (has_expected)
|
||||
{
|
||||
QMessageBox::warning(mw, tr("Warning!"), tr("Package cannot be installed on top of the current data.\nUpdate is for version %1, but you don't have any data installed.\n\nTried to install: %2")
|
||||
.arg(QString::fromStdString(result.version.expected)).arg(package->path));
|
||||
QMessageBox::warning(mw, tr("Warning!"), tr("Package cannot be installed on top of the current data.\nUpdate with version %0 is for version %1, but you don't have any data installed.\n\nTried to install: %2")
|
||||
.arg(QString::fromStdString(result.version.app_ver)).arg(QString::fromStdString(result.version.expected)).arg(package->path));
|
||||
}
|
||||
else if (has_installed)
|
||||
{
|
||||
QMessageBox::warning(mw, tr("Warning!"), tr("Package cannot be installed on top of the current data.\nUpdate has version %0, but you already have version %1.\n\nTried to install: %2")
|
||||
.arg(QString::fromStdString(result.version.app_ver)).arg(QString::fromStdString(result.version.installed)).arg(package->path));
|
||||
}
|
||||
else
|
||||
{
|
||||
// probably unreachable
|
||||
const QString found = has_found ? tr("version %1").arg(QString::fromStdString(result.version.found)) : tr("no data installed");
|
||||
QMessageBox::warning(mw, tr("Warning!"), tr("Package cannot be installed on top of the current data.\nUpdate is for unknown version, but you have version %1.\n\nTried to install: %2")
|
||||
.arg(QString::fromStdString(result.version.expected)).arg(found).arg(package->path));
|
||||
QMessageBox::warning(mw, tr("Warning!"), tr("Package cannot be installed on top of the current data.\nAn unexpected error occured.\n\nTried to install: %0").arg(package->path));
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -2192,6 +2197,7 @@ void main_window::EnableMenus(bool enabled) const
|
||||
// Tools
|
||||
ui->toolskernel_explorerAct->setEnabled(enabled);
|
||||
ui->toolsmemory_viewerAct->setEnabled(enabled);
|
||||
ui->toolsDumpGuestMemoryAct->setEnabled(enabled);
|
||||
ui->toolsRsxDebuggerAct->setEnabled(enabled);
|
||||
ui->toolsSystemCommandsAct->setEnabled(enabled);
|
||||
ui->actionCreate_RSX_Capture->setEnabled(enabled);
|
||||
@@ -3387,6 +3393,12 @@ void main_window::CreateConnects()
|
||||
idm::make<memory_viewer_handle>(this, make_basic_ppu_disasm());
|
||||
});
|
||||
|
||||
connect(ui->toolsDumpGuestMemoryAct, &QAction::triggered, this, [this]()
|
||||
{
|
||||
guest_memory_dumper* dumper = new guest_memory_dumper(this, true);
|
||||
dumper->dump_guest_memory();
|
||||
});
|
||||
|
||||
connect(ui->toolsRsxDebuggerAct, &QAction::triggered, this, [this]
|
||||
{
|
||||
rsx_debugger* rsx = new rsx_debugger(m_gui_settings);
|
||||
@@ -3461,6 +3473,20 @@ void main_window::CreateConnects()
|
||||
m_game_list_frame->Refresh();
|
||||
});
|
||||
|
||||
connect(ui->showBrokenEntriesAct, &QAction::triggered, this, [this](bool checked)
|
||||
{
|
||||
m_gui_settings->SetValue(gui::gl_show_broken, checked);
|
||||
m_game_list_frame->SetShowBroken(checked);
|
||||
m_game_list_frame->Refresh();
|
||||
});
|
||||
|
||||
connect(ui->showCompletedEntriesAct, &QAction::triggered, this, [this](bool checked)
|
||||
{
|
||||
m_gui_settings->SetValue(gui::gl_show_completed, checked);
|
||||
m_game_list_frame->SetShowCompleted(checked);
|
||||
m_game_list_frame->Refresh();
|
||||
});
|
||||
|
||||
connect(ui->showCompatibilityInGridAct, &QAction::triggered, m_game_list_frame, &game_list_frame::SetShowCompatibilityInGrid);
|
||||
|
||||
connect(ui->refreshGameListAct, &QAction::triggered, this, [this]
|
||||
@@ -3909,6 +3935,12 @@ void main_window::ConfigureGuiFromSettings()
|
||||
ui->showHiddenEntriesAct->setChecked(m_gui_settings->GetValue(gui::gl_show_hidden).toBool());
|
||||
m_game_list_frame->SetShowHidden(ui->showHiddenEntriesAct->isChecked()); // prevent GetValue in m_game_list_frame->LoadSettings
|
||||
|
||||
ui->showBrokenEntriesAct->setChecked(m_gui_settings->GetValue(gui::gl_show_broken).toBool());
|
||||
m_game_list_frame->SetShowBroken(ui->showBrokenEntriesAct->isChecked()); // prevent GetValue in m_game_list_frame->LoadSettings
|
||||
|
||||
ui->showCompletedEntriesAct->setChecked(m_gui_settings->GetValue(gui::gl_show_completed).toBool());
|
||||
m_game_list_frame->SetShowCompleted(ui->showCompletedEntriesAct->isChecked()); // prevent GetValue in m_game_list_frame->LoadSettings
|
||||
|
||||
ui->showCompatibilityInGridAct->setChecked(m_gui_settings->GetValue(gui::gl_draw_compat).toBool());
|
||||
ui->actionPreferGameDataIcons->setChecked(m_gui_settings->GetValue(gui::gl_pref_gd_icon).toBool());
|
||||
ui->showCustomIconsAct->setChecked(m_gui_settings->GetValue(gui::gl_custom_icon).toBool());
|
||||
|
||||
@@ -365,6 +365,7 @@
|
||||
<addaction name="separator"/>
|
||||
<addaction name="toolsSystemCommandsAct"/>
|
||||
<addaction name="toolsmemory_viewerAct"/>
|
||||
<addaction name="toolsDumpGuestMemoryAct"/>
|
||||
<addaction name="toolskernel_explorerAct"/>
|
||||
<addaction name="toolsRsxDebuggerAct"/>
|
||||
<addaction name="separator"/>
|
||||
@@ -428,6 +429,8 @@
|
||||
<addaction name="separator"/>
|
||||
<addaction name="showGameListAct"/>
|
||||
<addaction name="showHiddenEntriesAct"/>
|
||||
<addaction name="showBrokenEntriesAct"/>
|
||||
<addaction name="showCompletedEntriesAct"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="menuGame_List_View"/>
|
||||
<addaction name="menuGame_List_Grid_Mode"/>
|
||||
@@ -759,6 +762,17 @@
|
||||
<string>Memory Viewer</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="toolsDumpGuestMemoryAct">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Dump Guest Memory</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Dump all allocated PS3 guest memory and live SPU local stores</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="toolsRsxDebuggerAct">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
@@ -1183,6 +1197,21 @@
|
||||
<string>Show Hidden Entries</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="showBrokenEntriesAct">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Show Broken Entries</string>
|
||||
</property>
|
||||
</action> <action name="showCompletedEntriesAct">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Show Completed Entries</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionopen_rsx_capture">
|
||||
<property name="text">
|
||||
<string>Open RSX Capture</string>
|
||||
|
||||
@@ -24,6 +24,8 @@ extern "C" {
|
||||
|
||||
LOG_CHANNEL(cfg_log, "CFG");
|
||||
|
||||
constexpr int codec_name_role = Qt::UserRole + 1;
|
||||
|
||||
static std::vector<const AVCodec*> get_video_codecs(const AVOutputFormat* fmt)
|
||||
{
|
||||
std::vector<const AVCodec*> codecs;
|
||||
@@ -142,36 +144,42 @@ recording_settings_dialog::recording_settings_dialog(QWidget* parent)
|
||||
{
|
||||
if (!codec) continue;
|
||||
|
||||
const std::string name = codec->long_name ? codec->long_name : avcodec_get_name(codec->id);
|
||||
const char* codec_name = avcodec_get_name(codec->id);
|
||||
const std::string name = codec->long_name ? codec->long_name : codec_name;
|
||||
ui->combo_video_codec->addItem(QString::fromStdString(name), static_cast<int>(codec->id));
|
||||
ui->combo_video_codec->setItemData(ui->combo_video_codec->findData(static_cast<int>(codec->id)), QString(codec_name), codec_name_role);
|
||||
}
|
||||
|
||||
for (const AVCodec* codec : m_audio_codecs)
|
||||
{
|
||||
if (!codec) continue;
|
||||
|
||||
const std::string name = codec->long_name ? codec->long_name : avcodec_get_name(codec->id);
|
||||
const char* codec_name = avcodec_get_name(codec->id);
|
||||
const std::string name = codec->long_name ? codec->long_name : codec_name;
|
||||
ui->combo_audio_codec->addItem(QString::fromStdString(name), static_cast<int>(codec->id));
|
||||
ui->combo_audio_codec->setItemData(ui->combo_audio_codec->findData(static_cast<int>(codec->id)), QString(codec_name), codec_name_role);
|
||||
}
|
||||
|
||||
connect(ui->combo_video_codec, &QComboBox::currentIndexChanged, this, [this](int index)
|
||||
{
|
||||
const QVariant var = ui->combo_video_codec->itemData(index);
|
||||
if (var.canConvert<int>())
|
||||
const QVariant codec_id = ui->combo_video_codec->itemData(index);
|
||||
const QVariant codec_name = ui->combo_video_codec->itemData(index, codec_name_role);
|
||||
if (codec_id.canConvert<int>() && codec_name.canConvert<QString>())
|
||||
{
|
||||
const int codec_id = var.toInt();
|
||||
g_cfg_recording.video.video_codec.set(codec_id);
|
||||
g_cfg_recording.video.codec_name.set(codec_name.toString().toStdString());
|
||||
g_cfg_recording.video.codec_id.set(codec_id.toInt());
|
||||
update_preset();
|
||||
}
|
||||
});
|
||||
|
||||
connect(ui->combo_audio_codec, &QComboBox::currentIndexChanged, this, [this](int index)
|
||||
{
|
||||
const QVariant var = ui->combo_audio_codec->itemData(index);
|
||||
if (var.canConvert<int>())
|
||||
const QVariant codec_id = ui->combo_audio_codec->itemData(index);
|
||||
const QVariant codec_name = ui->combo_audio_codec->itemData(index, codec_name_role);
|
||||
if (codec_id.canConvert<int>() && codec_name.canConvert<QString>())
|
||||
{
|
||||
const int codec_id = var.toInt();
|
||||
g_cfg_recording.audio.audio_codec.set(codec_id);
|
||||
g_cfg_recording.audio.codec_name.set(codec_name.toString().toStdString());
|
||||
g_cfg_recording.audio.codec_id.set(codec_id.toInt());
|
||||
update_preset();
|
||||
}
|
||||
});
|
||||
@@ -279,8 +287,25 @@ void recording_settings_dialog::update_ui()
|
||||
|
||||
ui->combo_resolution->setCurrentIndex(ui->combo_resolution->findData(QVariant::fromValue(QPair<int, int>(g_cfg_recording.video.width.get(), g_cfg_recording.video.height.get()))));
|
||||
ui->combo_framerate->setCurrentIndex(ui->combo_framerate->findData(static_cast<int>(g_cfg_recording.video.framerate.get())));
|
||||
ui->combo_video_codec->setCurrentIndex(ui->combo_video_codec->findData(static_cast<int>(g_cfg_recording.video.video_codec.get())));
|
||||
ui->combo_audio_codec->setCurrentIndex(ui->combo_audio_codec->findData(static_cast<int>(g_cfg_recording.audio.audio_codec.get())));
|
||||
|
||||
if (const int index = ui->combo_video_codec->findData(QString::fromStdString(g_cfg_recording.video.codec_name.get()), codec_name_role); index >= 0)
|
||||
{
|
||||
ui->combo_video_codec->setCurrentIndex(index);
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->combo_video_codec->setCurrentIndex(ui->combo_video_codec->findData(static_cast<int>(g_cfg_recording.video.codec_id.get())));
|
||||
}
|
||||
|
||||
if (const int index = ui->combo_audio_codec->findData(QString::fromStdString(g_cfg_recording.audio.codec_name.get()), codec_name_role); index >= 0)
|
||||
{
|
||||
ui->combo_audio_codec->setCurrentIndex(index);
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->combo_audio_codec->setCurrentIndex(ui->combo_audio_codec->findData(static_cast<int>(g_cfg_recording.audio.codec_id.get())));
|
||||
}
|
||||
|
||||
ui->spinbox_video_bitrate->setValue(g_cfg_recording.video.video_bps);
|
||||
ui->spinbox_audio_bitrate->setValue(g_cfg_recording.audio.audio_bps);
|
||||
ui->spinbox_gop_size->setValue(g_cfg_recording.video.gop_size);
|
||||
@@ -295,8 +320,14 @@ void recording_settings_dialog::update_ui()
|
||||
ui->spinbox_gop_size->blockSignals(false);
|
||||
ui->spinbox_max_b_frames->blockSignals(false);
|
||||
|
||||
const auto get_codec_name = [](const std::vector<const AVCodec*>& codecs, u32 id)
|
||||
const auto get_codec_name = [](const std::vector<const AVCodec*>& codecs, u32 id, const std::string& codec_name)
|
||||
{
|
||||
if (const AVCodec* codec = avcodec_find_encoder_by_name(codec_name.c_str()))
|
||||
{
|
||||
const std::string name = codec->long_name ? codec->long_name : avcodec_get_name(codec->id);
|
||||
return name;
|
||||
}
|
||||
|
||||
for (const AVCodec* codec : codecs)
|
||||
{
|
||||
if (codec && codec->id == static_cast<AVCodecID>(id))
|
||||
@@ -323,9 +354,9 @@ void recording_settings_dialog::update_ui()
|
||||
fmt::format("%d x %d\n%d fps\n%s\n%d\n%s\n%d\n%d\n%d",
|
||||
g_cfg_recording.video.width.get(), g_cfg_recording.video.height.get(),
|
||||
g_cfg_recording.video.framerate.get(),
|
||||
get_codec_name(m_video_codecs, g_cfg_recording.video.video_codec.get()),
|
||||
get_codec_name(m_video_codecs, g_cfg_recording.video.codec_id.get(), g_cfg_recording.video.codec_name.get()),
|
||||
g_cfg_recording.video.video_bps.get(),
|
||||
get_codec_name(m_audio_codecs, g_cfg_recording.audio.audio_codec.get()),
|
||||
get_codec_name(m_audio_codecs, g_cfg_recording.audio.codec_id.get(), g_cfg_recording.audio.codec_name.get()),
|
||||
g_cfg_recording.audio.audio_bps.get(),
|
||||
g_cfg_recording.video.gop_size.get(),
|
||||
g_cfg_recording.video.max_b_frames.get()
|
||||
@@ -340,10 +371,12 @@ void recording_settings_dialog::select_preset(quality_preset preset, cfg_recordi
|
||||
return;
|
||||
}
|
||||
|
||||
cfg.audio.audio_codec.set(static_cast<u32>(AVCodecID::AV_CODEC_ID_AAC));
|
||||
cfg.audio.codec_name.set("");
|
||||
cfg.audio.codec_id.set(static_cast<u32>(AVCodecID::AV_CODEC_ID_AAC));
|
||||
cfg.audio.audio_bps.set(192'000); // 192 kbps
|
||||
|
||||
cfg.video.video_codec.set(static_cast<u32>(AVCodecID::AV_CODEC_ID_MPEG4));
|
||||
cfg.video.codec_name.set("");
|
||||
cfg.video.codec_id.set(static_cast<u32>(AVCodecID::AV_CODEC_ID_MPEG4));
|
||||
cfg.video.pixel_format.set(static_cast<u32>(::AV_PIX_FMT_YUV420P));
|
||||
|
||||
switch (preset)
|
||||
@@ -437,11 +470,13 @@ recording_settings_dialog::quality_preset recording_settings_dialog::current_pre
|
||||
g_cfg_recording.video.width.get() == cfg.video.width.get() &&
|
||||
g_cfg_recording.video.height.get() == cfg.video.height.get() &&
|
||||
g_cfg_recording.video.pixel_format.get() == cfg.video.pixel_format.get() &&
|
||||
g_cfg_recording.video.video_codec.get() == cfg.video.video_codec.get() &&
|
||||
g_cfg_recording.video.codec_name.get() == cfg.video.codec_name.get() &&
|
||||
g_cfg_recording.video.codec_id.get() == cfg.video.codec_id.get() &&
|
||||
g_cfg_recording.video.video_bps.get() == cfg.video.video_bps.get() &&
|
||||
g_cfg_recording.video.max_b_frames.get() == cfg.video.max_b_frames.get() &&
|
||||
g_cfg_recording.video.gop_size.get() == cfg.video.gop_size.get() &&
|
||||
g_cfg_recording.audio.audio_codec.get() == cfg.audio.audio_codec.get() &&
|
||||
g_cfg_recording.audio.codec_name.get() == cfg.audio.codec_name.get() &&
|
||||
g_cfg_recording.audio.codec_id.get() == cfg.audio.codec_id.get() &&
|
||||
g_cfg_recording.audio.audio_bps.get() == cfg.audio.audio_bps.get())
|
||||
{
|
||||
return preset;
|
||||
|
||||
@@ -596,7 +596,7 @@ void save_manager_dialog::OnEntriesRemove()
|
||||
return;
|
||||
}
|
||||
|
||||
if (QMessageBox::question(this, tr("Delete Confirmation"), tr("Are you sure you want to delete these %n items?", "", rows.size()), QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes)
|
||||
if (QMessageBox::question(this, tr("Delete Confirmation"), tr("Are you sure you want to delete these %n items?", "", static_cast<int>(rows.size())), QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes)
|
||||
{
|
||||
// Sort descending so removeRow() doesn't shift remaining indices.
|
||||
std::sort(rows.begin(), rows.end(), std::greater<int>());
|
||||
|
||||
@@ -1079,21 +1079,21 @@ void trophy_manager_dialog::StartTrophyLoadThreads()
|
||||
for (int i = 0; i < count; ++i)
|
||||
indices.append(i);
|
||||
|
||||
QFutureWatcher<void> futureWatcher;
|
||||
QFutureWatcher<void> future_watcher;
|
||||
|
||||
progress_dialog progressDialog(tr("Loading trophies"), tr("Loading trophy data, please wait..."), tr("Cancel"), 0, 1, false, this, Qt::Dialog | Qt::WindowTitleHint | Qt::CustomizeWindowHint);
|
||||
progress_dialog progress_dlg(tr("Loading trophies"), tr("Loading trophy data, please wait..."), tr("Cancel"), 0, 1, false, this, Qt::Dialog | Qt::WindowTitleHint | Qt::CustomizeWindowHint);
|
||||
|
||||
connect(&futureWatcher, &QFutureWatcher<void>::progressRangeChanged, &progressDialog, &QProgressDialog::setRange);
|
||||
connect(&futureWatcher, &QFutureWatcher<void>::progressValueChanged, &progressDialog, &QProgressDialog::setValue);
|
||||
connect(&futureWatcher, &QFutureWatcher<void>::finished, this, [this]() { RepaintUI(true); });
|
||||
connect(&progressDialog, &QProgressDialog::canceled, this, [this, &futureWatcher]()
|
||||
connect(&future_watcher, &QFutureWatcher<void>::progressRangeChanged, &progress_dlg, &QProgressDialog::setRange);
|
||||
connect(&future_watcher, &QFutureWatcher<void>::progressValueChanged, &progress_dlg, &QProgressDialog::setValue);
|
||||
connect(&future_watcher, &QFutureWatcher<void>::finished, this, [this]() { RepaintUI(true); });
|
||||
connect(&progress_dlg, &QProgressDialog::canceled, this, [this, &future_watcher]()
|
||||
{
|
||||
futureWatcher.cancel();
|
||||
future_watcher.cancel();
|
||||
close(); // It's pointless to show an empty window
|
||||
});
|
||||
|
||||
atomic_t<usz> error_count{};
|
||||
futureWatcher.setFuture(QtConcurrent::map(indices, [this, &error_count, &folder_list](const int& i)
|
||||
future_watcher.setFuture(QtConcurrent::map(indices, [this, &error_count, &folder_list](const int& i)
|
||||
{
|
||||
const std::string dir_name = folder_list.value(i).toStdString();
|
||||
gui_log.trace("Loading trophy dir: %s", dir_name);
|
||||
@@ -1106,9 +1106,9 @@ void trophy_manager_dialog::StartTrophyLoadThreads()
|
||||
}
|
||||
}));
|
||||
|
||||
progressDialog.exec();
|
||||
progress_dlg.exec();
|
||||
|
||||
futureWatcher.waitForFinished();
|
||||
future_watcher.waitForFinished();
|
||||
|
||||
if (error_count != 0)
|
||||
{
|
||||
|
||||
@@ -571,11 +571,36 @@ namespace fmt
|
||||
EXPECT_EQ("a"s, fmt::merge(vec{"a"}, "-"));
|
||||
EXPECT_EQ("a"s, fmt::merge(vec{"a"}, " *-* "));
|
||||
|
||||
EXPECT_EQ("a"s, fmt::merge(vec{"a", ""}, ""));
|
||||
EXPECT_EQ("a "s, fmt::merge(vec{"a", ""}, " "));
|
||||
EXPECT_EQ("a-"s, fmt::merge(vec{"a", ""}, "-"));
|
||||
EXPECT_EQ("a *-* "s, fmt::merge(vec{"a", ""}, " *-* "));
|
||||
|
||||
EXPECT_EQ("a"s, fmt::merge(vec{"a", "", ""}, ""));
|
||||
EXPECT_EQ("a "s, fmt::merge(vec{"a", "", ""}, " "));
|
||||
EXPECT_EQ("a--"s, fmt::merge(vec{"a", "", ""}, "-"));
|
||||
EXPECT_EQ("a *-* *-* "s, fmt::merge(vec{"a", "", ""}, " *-* "));
|
||||
|
||||
EXPECT_EQ("a"s, fmt::merge(vec{"", "a"}, ""));
|
||||
EXPECT_EQ(" a"s, fmt::merge(vec{"", "a"}, " "));
|
||||
EXPECT_EQ("-a"s, fmt::merge(vec{"", "a"}, "-"));
|
||||
EXPECT_EQ(" *-* a"s, fmt::merge(vec{"", "a"}, " *-* "));
|
||||
|
||||
EXPECT_EQ("ab"s, fmt::merge(vec{"a", "b"}, ""));
|
||||
EXPECT_EQ("a b"s, fmt::merge(vec{"a", "b"}, " "));
|
||||
EXPECT_EQ("a-b"s, fmt::merge(vec{"a", "b"}, "-"));
|
||||
EXPECT_EQ("a *-* b"s, fmt::merge(vec{"a", "b"}, " *-* "));
|
||||
|
||||
EXPECT_EQ("ab"s, fmt::merge(vec{"a", "", "b"}, ""));
|
||||
EXPECT_EQ("a b"s, fmt::merge(vec{"a", "", "b"}, " "));
|
||||
EXPECT_EQ("a--b"s, fmt::merge(vec{"a", "", "b"}, "-"));
|
||||
EXPECT_EQ("a *-* *-* b"s, fmt::merge(vec{"a", "", "b"}, " *-* "));
|
||||
|
||||
EXPECT_EQ("ab"s, fmt::merge(vec{"a", "", "", "b"}, ""));
|
||||
EXPECT_EQ("a b"s, fmt::merge(vec{"a", "", "", "b"}, " "));
|
||||
EXPECT_EQ("a---b"s, fmt::merge(vec{"a", "", "", "b"}, "-"));
|
||||
EXPECT_EQ("a *-* *-* *-* b"s, fmt::merge(vec{"a", "", "", "b"}, " *-* "));
|
||||
|
||||
EXPECT_EQ("abc"s, fmt::merge(vec{"a", "b", "c"}, ""));
|
||||
EXPECT_EQ("a b c"s, fmt::merge(vec{"a", "b", "c"}, " "));
|
||||
EXPECT_EQ("a-b-c"s, fmt::merge(vec{"a", "b", "c"}, "-"));
|
||||
@@ -597,6 +622,16 @@ namespace fmt
|
||||
EXPECT_EQ("a"s, fmt::merge(lst{vec{"a"}}, "-"));
|
||||
EXPECT_EQ("a"s, fmt::merge(lst{vec{"a"}}, " *-* "));
|
||||
|
||||
EXPECT_EQ("a"s, fmt::merge(lst{vec{"a"}, vec{}}, ""));
|
||||
EXPECT_EQ("a"s, fmt::merge(lst{vec{"a"}, vec{}}, " "));
|
||||
EXPECT_EQ("a"s, fmt::merge(lst{vec{"a"}, vec{}}, "-"));
|
||||
EXPECT_EQ("a"s, fmt::merge(lst{vec{"a"}, vec{}}, " *-* "));
|
||||
|
||||
EXPECT_EQ("a"s, fmt::merge(lst{vec{}, vec{"a"}}, ""));
|
||||
EXPECT_EQ("a"s, fmt::merge(lst{vec{}, vec{"a"}}, " "));
|
||||
EXPECT_EQ("a"s, fmt::merge(lst{vec{}, vec{"a"}}, "-"));
|
||||
EXPECT_EQ("a"s, fmt::merge(lst{vec{}, vec{"a"}}, " *-* "));
|
||||
|
||||
EXPECT_EQ("ab"s, fmt::merge(lst{vec{"a", "b"}}, ""));
|
||||
EXPECT_EQ("a b"s, fmt::merge(lst{vec{"a", "b"}}, " "));
|
||||
EXPECT_EQ("a-b"s, fmt::merge(lst{vec{"a", "b"}}, "-"));
|
||||
@@ -612,6 +647,11 @@ namespace fmt
|
||||
EXPECT_EQ("a-b"s, fmt::merge(lst{vec{"a"}, vec{"b"}}, "-"));
|
||||
EXPECT_EQ("a *-* b"s, fmt::merge(lst{vec{"a"}, vec{"b"}}, " *-* "));
|
||||
|
||||
EXPECT_EQ("ab"s, fmt::merge(lst{vec{"a"}, vec{}, vec{"b"}}, ""));
|
||||
EXPECT_EQ("a b"s, fmt::merge(lst{vec{"a"}, vec{}, vec{"b"}}, " "));
|
||||
EXPECT_EQ("a-b"s, fmt::merge(lst{vec{"a"}, vec{}, vec{"b"}}, "-"));
|
||||
EXPECT_EQ("a *-* b"s, fmt::merge(lst{vec{"a"}, vec{}, vec{"b"}}, " *-* "));
|
||||
|
||||
EXPECT_EQ("abc"s, fmt::merge(lst{vec{"a"}, vec{"b"}, vec{"c"}}, ""));
|
||||
EXPECT_EQ("a b c"s, fmt::merge(lst{vec{"a"}, vec{"b"}, vec{"c"}}, " "));
|
||||
EXPECT_EQ("a-b-c"s, fmt::merge(lst{vec{"a"}, vec{"b"}, vec{"c"}}, "-"));
|
||||
@@ -621,6 +661,129 @@ namespace fmt
|
||||
EXPECT_EQ("a 1 b 2"s, fmt::merge(lst{vec{"a", "1"}, vec{"b", "2"}}, " "));
|
||||
EXPECT_EQ("a-1-b-2"s, fmt::merge(lst{vec{"a", "1"}, vec{"b", "2"}}, "-"));
|
||||
EXPECT_EQ("a *-* 1 *-* b *-* 2"s, fmt::merge(lst{vec{"a", "1"}, vec{"b", "2"}}, " *-* "));
|
||||
|
||||
EXPECT_EQ("a1b2"s, fmt::merge(lst{vec{"a", "", "1"}, vec{"b", "2", ""}}, ""));
|
||||
EXPECT_EQ("a 1 b 2 "s, fmt::merge(lst{vec{"a", "", "1"}, vec{"b", "2", ""}}, " "));
|
||||
EXPECT_EQ("a--1-b-2-"s, fmt::merge(lst{vec{"a", "", "1"}, vec{"b", "2", ""}}, "-"));
|
||||
EXPECT_EQ("a *-* *-* 1 *-* b *-* 2 *-* "s, fmt::merge(lst{vec{"a", "", "1"}, vec{"b", "2", ""}}, " *-* "));
|
||||
}
|
||||
|
||||
TEST(StrUtil, Merge_SkipEmpty)
|
||||
{
|
||||
using vec = std::vector<std::string>;
|
||||
using lst = std::initializer_list<std::vector<std::string>>;
|
||||
|
||||
// Vector of strings
|
||||
EXPECT_EQ(""s, fmt::merge(vec{}, "", true));
|
||||
EXPECT_EQ(""s, fmt::merge(vec{}, " ", true));
|
||||
EXPECT_EQ(""s, fmt::merge(vec{}, "-", true));
|
||||
EXPECT_EQ(""s, fmt::merge(vec{}, " *-* ", true));
|
||||
|
||||
EXPECT_EQ(""s, fmt::merge(vec{""}, "", true));
|
||||
EXPECT_EQ(""s, fmt::merge(vec{""}, " ", true));
|
||||
EXPECT_EQ(""s, fmt::merge(vec{""}, "-", true));
|
||||
EXPECT_EQ(""s, fmt::merge(vec{""}, " *-* ", true));
|
||||
|
||||
EXPECT_EQ("a"s, fmt::merge(vec{"a"}, "", true));
|
||||
EXPECT_EQ("a"s, fmt::merge(vec{"a"}, " ", true));
|
||||
EXPECT_EQ("a"s, fmt::merge(vec{"a"}, "-", true));
|
||||
EXPECT_EQ("a"s, fmt::merge(vec{"a"}, " *-* ", true));
|
||||
|
||||
EXPECT_EQ("a"s, fmt::merge(vec{"a", ""}, "", true));
|
||||
EXPECT_EQ("a"s, fmt::merge(vec{"a", ""}, " ", true));
|
||||
EXPECT_EQ("a"s, fmt::merge(vec{"a", ""}, "-", true));
|
||||
EXPECT_EQ("a"s, fmt::merge(vec{"a", ""}, " *-* ", true));
|
||||
|
||||
EXPECT_EQ("a"s, fmt::merge(vec{"a", "", ""}, "", true));
|
||||
EXPECT_EQ("a"s, fmt::merge(vec{"a", "", ""}, " ", true));
|
||||
EXPECT_EQ("a"s, fmt::merge(vec{"a", "", ""}, "-", true));
|
||||
EXPECT_EQ("a"s, fmt::merge(vec{"a", "", ""}, " *-* ", true));
|
||||
|
||||
EXPECT_EQ("a"s, fmt::merge(vec{"", "a"}, "", true));
|
||||
EXPECT_EQ("a"s, fmt::merge(vec{"", "a"}, " ", true));
|
||||
EXPECT_EQ("a"s, fmt::merge(vec{"", "a"}, "-", true));
|
||||
EXPECT_EQ("a"s, fmt::merge(vec{"", "a"}, " *-* ", true));
|
||||
|
||||
EXPECT_EQ("ab"s, fmt::merge(vec{"a", "b"}, "", true));
|
||||
EXPECT_EQ("a b"s, fmt::merge(vec{"a", "b"}, " ", true));
|
||||
EXPECT_EQ("a-b"s, fmt::merge(vec{"a", "b"}, "-", true));
|
||||
EXPECT_EQ("a *-* b"s, fmt::merge(vec{"a", "b"}, " *-* ", true));
|
||||
|
||||
EXPECT_EQ("ab"s, fmt::merge(vec{"a", "", "b"}, "", true));
|
||||
EXPECT_EQ("a b"s, fmt::merge(vec{"a", "", "b"}, " ", true));
|
||||
EXPECT_EQ("a-b"s, fmt::merge(vec{"a", "", "b"}, "-", true));
|
||||
EXPECT_EQ("a *-* b"s, fmt::merge(vec{"a", "", "b"}, " *-* ", true));
|
||||
|
||||
EXPECT_EQ("ab"s, fmt::merge(vec{"a", "", "", "b"}, "", true));
|
||||
EXPECT_EQ("a b"s, fmt::merge(vec{"a", "", "", "b"}, " ", true));
|
||||
EXPECT_EQ("a-b"s, fmt::merge(vec{"a", "", "", "b"}, "-", true));
|
||||
EXPECT_EQ("a *-* b"s, fmt::merge(vec{"a", "", "", "b"}, " *-* ", true));
|
||||
|
||||
EXPECT_EQ("abc"s, fmt::merge(vec{"a", "b", "c"}, "", true));
|
||||
EXPECT_EQ("a b c"s, fmt::merge(vec{"a", "b", "c"}, " ", true));
|
||||
EXPECT_EQ("a-b-c"s, fmt::merge(vec{"a", "b", "c"}, "-", true));
|
||||
EXPECT_EQ("a *-* b *-* c"s, fmt::merge(vec{"a", "b", "c"}, " *-* ", true));
|
||||
|
||||
// Initializer list of vector of strings
|
||||
EXPECT_EQ(""s, fmt::merge(lst{}, "", true));
|
||||
EXPECT_EQ(""s, fmt::merge(lst{}, " ", true));
|
||||
EXPECT_EQ(""s, fmt::merge(lst{}, "-", true));
|
||||
EXPECT_EQ(""s, fmt::merge(lst{}, " *-* ", true));
|
||||
|
||||
EXPECT_EQ(""s, fmt::merge(lst{vec{}}, "", true));
|
||||
EXPECT_EQ(""s, fmt::merge(lst{vec{}}, " ", true));
|
||||
EXPECT_EQ(""s, fmt::merge(lst{vec{}}, "-", true));
|
||||
EXPECT_EQ(""s, fmt::merge(lst{vec{}}, " *-* ", true));
|
||||
|
||||
EXPECT_EQ("a"s, fmt::merge(lst{vec{"a"}}, "", true));
|
||||
EXPECT_EQ("a"s, fmt::merge(lst{vec{"a"}}, " ", true));
|
||||
EXPECT_EQ("a"s, fmt::merge(lst{vec{"a"}}, "-", true));
|
||||
EXPECT_EQ("a"s, fmt::merge(lst{vec{"a"}}, " *-* ", true));
|
||||
|
||||
EXPECT_EQ("a"s, fmt::merge(lst{vec{"a"}, vec{}}, "", true));
|
||||
EXPECT_EQ("a"s, fmt::merge(lst{vec{"a"}, vec{}}, " ", true));
|
||||
EXPECT_EQ("a"s, fmt::merge(lst{vec{"a"}, vec{}}, "-", true));
|
||||
EXPECT_EQ("a"s, fmt::merge(lst{vec{"a"}, vec{}}, " *-* ", true));
|
||||
|
||||
EXPECT_EQ("a"s, fmt::merge(lst{vec{}, vec{"a"}}, "", true));
|
||||
EXPECT_EQ("a"s, fmt::merge(lst{vec{}, vec{"a"}}, " ", true));
|
||||
EXPECT_EQ("a"s, fmt::merge(lst{vec{}, vec{"a"}}, "-", true));
|
||||
EXPECT_EQ("a"s, fmt::merge(lst{vec{}, vec{"a"}}, " *-* ", true));
|
||||
|
||||
EXPECT_EQ("ab"s, fmt::merge(lst{vec{"a", "b"}}, "", true));
|
||||
EXPECT_EQ("a b"s, fmt::merge(lst{vec{"a", "b"}}, " ", true));
|
||||
EXPECT_EQ("a-b"s, fmt::merge(lst{vec{"a", "b"}}, "-", true));
|
||||
EXPECT_EQ("a *-* b"s, fmt::merge(lst{vec{"a", "b"}}, " *-* ", true));
|
||||
|
||||
EXPECT_EQ("abc"s, fmt::merge(lst{vec{"a", "b", "c"}}, "", true));
|
||||
EXPECT_EQ("a b c"s, fmt::merge(lst{vec{"a", "b", "c"}}, " ", true));
|
||||
EXPECT_EQ("a-b-c"s, fmt::merge(lst{vec{"a", "b", "c"}}, "-", true));
|
||||
EXPECT_EQ("a *-* b *-* c"s, fmt::merge(lst{vec{"a", "b", "c"}}, " *-* ", true));
|
||||
|
||||
EXPECT_EQ("ab"s, fmt::merge(lst{vec{"a"}, vec{"b"}}, "", true));
|
||||
EXPECT_EQ("a b"s, fmt::merge(lst{vec{"a"}, vec{"b"}}, " ", true));
|
||||
EXPECT_EQ("a-b"s, fmt::merge(lst{vec{"a"}, vec{"b"}}, "-", true));
|
||||
EXPECT_EQ("a *-* b"s, fmt::merge(lst{vec{"a"}, vec{"b"}}, " *-* ", true));
|
||||
|
||||
EXPECT_EQ("ab"s, fmt::merge(lst{vec{"a"}, vec{}, vec{"b"}}, "", true));
|
||||
EXPECT_EQ("a b"s, fmt::merge(lst{vec{"a"}, vec{}, vec{"b"}}, " ", true));
|
||||
EXPECT_EQ("a-b"s, fmt::merge(lst{vec{"a"}, vec{}, vec{"b"}}, "-", true));
|
||||
EXPECT_EQ("a *-* b"s, fmt::merge(lst{vec{"a"}, vec{}, vec{"b"}}, " *-* ", true));
|
||||
|
||||
EXPECT_EQ("abc"s, fmt::merge(lst{vec{"a"}, vec{"b"}, vec{"c"}}, "", true));
|
||||
EXPECT_EQ("a b c"s, fmt::merge(lst{vec{"a"}, vec{"b"}, vec{"c"}}, " ", true));
|
||||
EXPECT_EQ("a-b-c"s, fmt::merge(lst{vec{"a"}, vec{"b"}, vec{"c"}}, "-", true));
|
||||
EXPECT_EQ("a *-* b *-* c"s, fmt::merge(lst{vec{"a"}, vec{"b"}, vec{"c"}}, " *-* ", true));
|
||||
|
||||
EXPECT_EQ("a1b2"s, fmt::merge(lst{vec{"a", "1"}, vec{"b", "2"}}, "", true));
|
||||
EXPECT_EQ("a 1 b 2"s, fmt::merge(lst{vec{"a", "1"}, vec{"b", "2"}}, " ", true));
|
||||
EXPECT_EQ("a-1-b-2"s, fmt::merge(lst{vec{"a", "1"}, vec{"b", "2"}}, "-", true));
|
||||
EXPECT_EQ("a *-* 1 *-* b *-* 2"s, fmt::merge(lst{vec{"a", "1"}, vec{"b", "2"}}, " *-* ", true));
|
||||
|
||||
EXPECT_EQ("a1b2"s, fmt::merge(lst{vec{"a", "", "1"}, vec{"b", "2", ""}}, "", true));
|
||||
EXPECT_EQ("a 1 b 2"s, fmt::merge(lst{vec{"a", "", "1"}, vec{"b", "2", ""}}, " ", true));
|
||||
EXPECT_EQ("a-1-b-2"s, fmt::merge(lst{vec{"a", "", "1"}, vec{"b", "2", ""}}, "-", true));
|
||||
EXPECT_EQ("a *-* 1 *-* b *-* 2"s, fmt::merge(lst{vec{"a", "", "1"}, vec{"b", "2", ""}}, " *-* ", true));
|
||||
}
|
||||
|
||||
TEST(StrUtil, GetFileExtension)
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
// Giga SPU analyser regression: a brsl whose return address is a stop-trap leaves
|
||||
// a dangling target edge after block cleanup, which the reg-state walk must not
|
||||
// dereference. The SPU program below is made-up data (not from any game).
|
||||
namespace
|
||||
namespace test_spu
|
||||
{
|
||||
constexpr u32 SPU_STOP = 0x00000000u; // stop 0x0 — the no-return trap word
|
||||
|
||||
@@ -37,7 +37,6 @@ namespace
|
||||
{
|
||||
return 0x1a8u << 21;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(SpuAnalyserGiga, ReturnToStopTrapDoesNotRangeCheckFail)
|
||||
{
|
||||
@@ -242,3 +241,5 @@ TEST(SpuAnalyserGiga, ReDecodeConstPropCompletes)
|
||||
|
||||
g_cfg.core.spu_block_size.set(saved);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+22
-24
@@ -93,6 +93,13 @@ namespace utils
|
||||
media_log.notice("av_log: %s", msg);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
T media_info::get_metadata([[maybe_unused]] const std::string& key, const T& def) const
|
||||
{
|
||||
static_assert("unimplemented");
|
||||
return def;
|
||||
}
|
||||
|
||||
template <>
|
||||
std::string media_info::get_metadata(const std::string& key, const std::string& def) const
|
||||
{
|
||||
@@ -799,9 +806,10 @@ namespace utils
|
||||
m_out_format = std::move(format);
|
||||
}
|
||||
|
||||
void video_encoder::set_video_codec(s32 codec_id)
|
||||
void video_encoder::set_video_codec(s32 codec_id, std::string_view codec_name)
|
||||
{
|
||||
m_video_codec_id = codec_id;
|
||||
m_video_codec_name = codec_name;
|
||||
}
|
||||
|
||||
void video_encoder::set_max_b_frames(s32 max_b_frames)
|
||||
@@ -829,9 +837,10 @@ namespace utils
|
||||
m_audio_bitrate_bps = bitrate;
|
||||
}
|
||||
|
||||
void video_encoder::set_audio_codec(s32 codec_id)
|
||||
void video_encoder::set_audio_codec(s32 codec_id, std::string_view codec_name)
|
||||
{
|
||||
m_audio_codec_id = codec_id;
|
||||
m_audio_codec_name = codec_name;
|
||||
}
|
||||
|
||||
void video_encoder::pause(bool flush)
|
||||
@@ -992,9 +1001,12 @@ namespace utils
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
const AVCodecID video_codec = static_cast<AVCodecID>(m_video_codec_id);
|
||||
const AVCodecID audio_codec = static_cast<AVCodecID>(m_audio_codec_id);
|
||||
const AVOutputFormat* out_format = find_format(video_codec, audio_codec);
|
||||
const AVCodec* selected_video_codec = avcodec_find_encoder_by_name(m_video_codec_name.c_str());
|
||||
const AVCodec* selected_audio_codec = avcodec_find_encoder_by_name(m_audio_codec_name.c_str());
|
||||
|
||||
const AVCodecID video_codec_id = selected_video_codec ? selected_video_codec->id : static_cast<AVCodecID>(m_video_codec_id);
|
||||
const AVCodecID audio_codec_id = selected_audio_codec ? selected_audio_codec->id : static_cast<AVCodecID>(m_audio_codec_id);
|
||||
const AVOutputFormat* out_format = find_format(video_codec_id, audio_codec_id);
|
||||
|
||||
if (out_format)
|
||||
{
|
||||
@@ -1002,7 +1014,7 @@ namespace utils
|
||||
}
|
||||
else
|
||||
{
|
||||
media_log.error("video_encoder: Could not find a format for the requested video_codec %d and audio_codec %d", m_video_codec_id, m_audio_codec_id);
|
||||
media_log.error("video_encoder: Could not find a format for the requested video_codec '%s' (ID=%d) and audio_codec '%s' (ID=%d)", m_video_codec_name, m_video_codec_id, m_audio_codec_name, m_audio_codec_id);
|
||||
|
||||
// Fallback to some other codec
|
||||
for (const AVCodec* video_codec : video_codecs)
|
||||
@@ -1014,6 +1026,8 @@ namespace utils
|
||||
if (out_format)
|
||||
{
|
||||
media_log.success("video_encoder: Found fallback output format '%s'", out_format->name);
|
||||
selected_video_codec = video_codec;
|
||||
selected_audio_codec = audio_codec;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1046,27 +1060,11 @@ namespace utils
|
||||
return;
|
||||
}
|
||||
|
||||
const auto create_context = [this, &av](bool is_video) -> bool
|
||||
const auto create_context = [this, &av, &selected_video_codec, &selected_audio_codec](bool is_video) -> bool
|
||||
{
|
||||
const std::string type = is_video ? "video" : "audio";
|
||||
scoped_av::ctx& ctx = is_video ? av.video : av.audio;
|
||||
|
||||
if (is_video)
|
||||
{
|
||||
if (!(ctx.codec = avcodec_find_encoder(av.format_context->oformat->video_codec)))
|
||||
{
|
||||
media_log.error("video_encoder: avcodec_find_encoder for video failed. video_codec=%d", static_cast<int>(av.format_context->oformat->video_codec));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(ctx.codec = avcodec_find_encoder(av.format_context->oformat->audio_codec)))
|
||||
{
|
||||
media_log.error("video_encoder: avcodec_find_encoder for audio failed. audio_codec=%d", static_cast<int>(av.format_context->oformat->audio_codec));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
ctx.codec = is_video ? selected_video_codec : selected_audio_codec;
|
||||
|
||||
if (!(ctx.stream = avformat_new_stream(av.format_context, nullptr)))
|
||||
{
|
||||
|
||||
@@ -111,13 +111,13 @@ namespace utils
|
||||
void set_framerate(u32 framerate);
|
||||
void set_video_bitrate(u32 bitrate);
|
||||
void set_output_format(frame_format format);
|
||||
void set_video_codec(s32 codec_id);
|
||||
void set_video_codec(s32 codec_id, std::string_view codec_name);
|
||||
void set_max_b_frames(s32 max_b_frames);
|
||||
void set_gop_size(s32 gop_size);
|
||||
void set_sample_rate(u32 sample_rate);
|
||||
void set_audio_channels(u32 channels);
|
||||
void set_audio_bitrate(u32 bitrate);
|
||||
void set_audio_codec(s32 codec_id);
|
||||
void set_audio_codec(s32 codec_id, std::string_view codec_name);
|
||||
void pause(bool flush = true) override;
|
||||
void stop(bool flush = true) override;
|
||||
void resume() override;
|
||||
@@ -135,6 +135,7 @@ namespace utils
|
||||
// Video parameters
|
||||
u32 m_video_bitrate_bps = 0;
|
||||
s32 m_video_codec_id = 12; // AV_CODEC_ID_MPEG4
|
||||
std::string m_video_codec_name;
|
||||
s32 m_max_b_frames = 2;
|
||||
s32 m_gop_size = 12;
|
||||
frame_format m_out_format{};
|
||||
@@ -143,5 +144,6 @@ namespace utils
|
||||
u32 m_channels = 2;
|
||||
u32 m_audio_bitrate_bps = 320000;
|
||||
s32 m_audio_codec_id = 86018; // AV_CODEC_ID_AAC
|
||||
std::string m_audio_codec_name;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1069,7 +1069,7 @@ static const bool s_tsc_freq_evaluated = []() -> bool
|
||||
{
|
||||
// Sleep between first and last sample
|
||||
#ifdef _WIN32
|
||||
Sleep(sleep_time_ms);
|
||||
Sleep(static_cast<DWORD>(sleep_time_ms));
|
||||
#else
|
||||
usleep(sleep_time_ms * 1000);
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user