Compare commits
232 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c16e176fbe | |||
| 55fccf03fa | |||
| d3f9ae4868 | |||
| debeafa36b | |||
| 39e946630d | |||
| 8d815e8ee0 | |||
| 430b57833d | |||
| e32c48d0dd | |||
| e2402b1c8f | |||
| 36abdd7114 | |||
| 79f54771b5 | |||
| 2556f84752 | |||
| c8e624850b | |||
| 56692e23f5 | |||
| b38ce9452d | |||
| fcba193a3c | |||
| 6fc7fa3b13 | |||
| ad4216a3fc | |||
| 08c3a38b67 | |||
| b8165af054 | |||
| 90b6912daa | |||
| 862727e9bf | |||
| 9e9a3262eb | |||
| c268189e38 | |||
| 94431a7a33 | |||
| b4d5b44e00 | |||
| 16f006362e | |||
| dfec577417 | |||
| 33e2f8766a | |||
| 31a080a9cb | |||
| 14e9c8954c | |||
| dff7352e2e | |||
| 30bb6c9fb9 | |||
| 7485ec2223 | |||
| 60082864a6 | |||
| dcb8f21b2b | |||
| b18a97e777 | |||
| c7d93bd470 | |||
| 7bf8917c08 | |||
| 093198e3d5 | |||
| e48bd29584 | |||
| 1444981bdc | |||
| e3f6bbfcc9 | |||
| 89922b254c | |||
| ea8918291b | |||
| 21445fa01a | |||
| 8d340eecc1 | |||
| 463826bf13 | |||
| 40eda1b972 | |||
| a5c3e303c7 | |||
| 270a21ebae | |||
| 6aa96e5f81 | |||
| 1792714901 | |||
| 54ab16ab35 | |||
| c58959eba2 | |||
| 2f53d58cd9 | |||
| e0d6f8a311 | |||
| e48ec75896 | |||
| 58d8c65990 | |||
| dd66380132 | |||
| 92dd801ecd | |||
| fba1db29b3 | |||
| 1111c1952b | |||
| 7833862342 | |||
| 78c1a6f326 | |||
| 060ddca1ab | |||
| a9fd2f76d2 | |||
| d1837b54b4 | |||
| 26746ac9ee | |||
| c019b10aa8 | |||
| 0cd316116b | |||
| c92a556093 | |||
| d6f6752af0 | |||
| d7e04e9b0a | |||
| c1aaa1bcf6 | |||
| 10fe14e783 | |||
| 0231902a69 | |||
| dbeb8cfbec | |||
| 96bd150518 | |||
| cb1fb19dea | |||
| 3b37f5b156 | |||
| a989634ba5 | |||
| d865186f23 | |||
| e63245a6dc | |||
| 682fd15441 | |||
| bbd81b9395 | |||
| cb6186ea60 | |||
| 12bfc7d56a | |||
| 7dc4ccc87d | |||
| 622894a7e3 | |||
| 965ec8ec81 | |||
| 103b2fe5fd | |||
| b6bfde6d64 | |||
| 1ec2752ed2 | |||
| a5b6b2e9b5 | |||
| f51b100a8c | |||
| 412db758e8 | |||
| f617f69be8 | |||
| b81d278aee | |||
| aee4bfa369 | |||
| 517f0e1bac | |||
| 08837d2cd0 | |||
| 9a4c823366 | |||
| 7f3ad4fc2c | |||
| 9e7b759236 | |||
| 67853b464c | |||
| 51ffd4a377 | |||
| dada2e85a2 | |||
| 2a53824b02 | |||
| 3dc1dfa4a2 | |||
| 95e0d1a560 | |||
| 8effbf7e4d | |||
| 72ad1abd39 | |||
| 2a89d87fab | |||
| 33eeee0ff2 | |||
| 3c46388be5 | |||
| d37b9497a2 | |||
| 17512f2a28 | |||
| 597a9f6dd6 | |||
| e90b399329 | |||
| 7c8be662c6 | |||
| e38b005a8b | |||
| f0abb4473e | |||
| d62e90f5b4 | |||
| 4ecf8ecd06 | |||
| 9a3b72f762 | |||
| 7285af4771 | |||
| ec37e8f0dc | |||
| 41f05f6864 | |||
| cc2fa94fa1 | |||
| 2f579311b8 | |||
| ac034040ce | |||
| 59410b9d62 | |||
| 6f665da016 | |||
| c567c92d4b | |||
| 75b2dfa66d | |||
| c40826c140 | |||
| 175aa510c8 | |||
| 236ac7d062 | |||
| 00f1b2bfa7 | |||
| a9fad83971 | |||
| 1550dc7f1f | |||
| bbb4c109d7 | |||
| a6fa091ab3 | |||
| 6a1fa88e15 | |||
| 787e79c687 | |||
| 7468d96c51 | |||
| 9c9ece3d95 | |||
| 316b3a8851 | |||
| 2023057fbb | |||
| 94c048d018 | |||
| e126af63ed | |||
| 750359d780 | |||
| 8588b2b11a | |||
| 4594c912a6 | |||
| afcfd81496 | |||
| aa06aaab8e | |||
| fdca881e34 | |||
| 81de7a5863 | |||
| efbf044ea0 | |||
| 46a1c09abe | |||
| 6a3e795d73 | |||
| e0f08cd5c1 | |||
| a539308545 | |||
| 3364a46139 | |||
| fb4e64d15e | |||
| 68349e48cd | |||
| 4dff8b16f1 | |||
| 9245565085 | |||
| 86ca381a3c | |||
| a7952e15c7 | |||
| 643f81b5db | |||
| cf5a66a204 | |||
| e05239f3d9 | |||
| 0e73b58abf | |||
| 59dc5dcd54 | |||
| adedc7c667 | |||
| 29ad568791 | |||
| 453a3c4852 | |||
| 2e824827b7 | |||
| 3393d13b67 | |||
| 1da7689526 | |||
| 806758d06f | |||
| 580f9bf03a | |||
| 707a648a4c | |||
| 213a19c9f3 | |||
| e32ed90d21 | |||
| 2f822abb47 | |||
| 1ca83bc629 | |||
| 77485747f1 | |||
| 40adf4b944 | |||
| 2aad5a7f3a | |||
| 0f29a5be9f | |||
| 397c2698ae | |||
| 0f00bb77fe | |||
| a73303a047 | |||
| 0334243a69 | |||
| bb809c865f | |||
| 20750c260e | |||
| 37dd1b3ed0 | |||
| f3ac3476cb | |||
| 67ae1c9f69 | |||
| fd583dbbc3 | |||
| d8d0af2bb3 | |||
| b176085e14 | |||
| 4cfdb71d3a | |||
| 38590e321e | |||
| a09900a64f | |||
| 4a7d982a2b | |||
| ee69468104 | |||
| 4739ab58cc | |||
| be5b982e45 | |||
| 86ad4ed6c1 | |||
| 3056a4db83 | |||
| adc8a360ad | |||
| 719dafa679 | |||
| ecf7d4e59d | |||
| 45863bc7c5 | |||
| ea0545779a | |||
| e625bab7eb | |||
| 2930d609ea | |||
| 069ca214ff | |||
| d699db2d19 | |||
| 8e15de5c83 | |||
| 69a90ef9e7 | |||
| d39361705a | |||
| 4512c10ce5 | |||
| 5e24167d6c | |||
| 64d28ba435 | |||
| 7febbdf52b | |||
| b64b98490e | |||
| f40602cc59 |
+3
-1
@@ -1,7 +1,9 @@
|
||||
#!/bin/sh -ex
|
||||
|
||||
export HOMEBREW_NO_AUTO_UPDATE=1
|
||||
brew install -f --overwrite nasm ninja git p7zip ccache pipenv #create-dmg
|
||||
export HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1
|
||||
brew unlink certifi
|
||||
brew install -f --overwrite nasm ninja p7zip ccache pipenv #create-dmg
|
||||
|
||||
#/usr/sbin/softwareupdate --install-rosetta --agree-to-license
|
||||
arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||
|
||||
+6
-1
@@ -12,11 +12,16 @@ if [ "$DEPLOY_APPIMAGE" = "true" ]; then
|
||||
curl -fsSLo linuxdeploy-plugin-checkrt.sh https://github.com/linuxdeploy/linuxdeploy-plugin-checkrt/releases/download/continuous/linuxdeploy-plugin-checkrt-x86_64.sh
|
||||
chmod +x ./linuxdeploy-plugin-checkrt.sh
|
||||
|
||||
EXTRA_QT_PLUGINS="svg;" APPIMAGE_EXTRACT_AND_RUN=1 linuxdeploy --appdir AppDir --plugin qt
|
||||
export EXTRA_PLATFORM_PLUGINS="libqwayland-egl.so;libqwayland-generic.so"
|
||||
export EXTRA_QT_PLUGINS="svg;wayland-decoration-client;wayland-graphics-integration-client;wayland-shell-integration"
|
||||
APPIMAGE_EXTRACT_AND_RUN=1 linuxdeploy --appdir AppDir --plugin qt
|
||||
|
||||
# Remove libwayland-client because it has platform-dependent exports and breaks other OSes
|
||||
rm -f ./AppDir/usr/lib/libwayland-client.so*
|
||||
|
||||
# Remove libvulkan because it causes issues with gamescope
|
||||
rm -f ./AppDir/usr/lib/libvulkan.so*
|
||||
|
||||
# Remove git directory containing local commit history file
|
||||
rm -rf ./AppDir/usr/share/rpcs3/git
|
||||
|
||||
|
||||
+3
-3
@@ -7,7 +7,7 @@ env:
|
||||
BUILD_SOURCEBRANCHNAME: $CIRRUS_BRANCH
|
||||
RPCS3_TOKEN: ENCRYPTED[!a4c3850e29ab150692286a74bec29819d25971a7ec431b86de2a35f7ed90c5b2ab3c93469f9298e30924d843599110e9!]
|
||||
QT_VER_MAIN: '6'
|
||||
QT_VER: '6.6.2'
|
||||
QT_VER: '6.6.3'
|
||||
|
||||
# windows_task:
|
||||
# matrix:
|
||||
@@ -21,7 +21,7 @@ env:
|
||||
# COMPILER: msvc
|
||||
# BUILD_ARTIFACTSTAGINGDIRECTORY: ${CIRRUS_WORKING_DIR}\artifacts\
|
||||
# QT_VER_MSVC: 'msvc2019'
|
||||
# QT_DATE: '202402121135'
|
||||
# QT_DATE: '202403191840'
|
||||
# QTDIR: C:\Qt\${QT_VER}\${QT_VER_MSVC}_64
|
||||
# VULKAN_VER: '1.3.268.0'
|
||||
# VULKAN_SDK_SHA: '8459ef49bd06b697115ddd3d97c9aec729e849cd775f5be70897718a9b3b9db5'
|
||||
@@ -56,7 +56,7 @@ env:
|
||||
|
||||
# linux_task:
|
||||
# container:
|
||||
# image: rpcs3/rpcs3-ci-focal:1.6
|
||||
# image: rpcs3/rpcs3-ci-focal:1.7
|
||||
# cpu: 4
|
||||
# memory: 16G
|
||||
# env:
|
||||
|
||||
@@ -130,6 +130,9 @@ yaml-cpp.pc
|
||||
# miniupnp
|
||||
/3rdparty/miniupnp/x64/*
|
||||
|
||||
# opencv
|
||||
/3rdparty/opencv/*
|
||||
|
||||
# llvm
|
||||
/3rdparty/llvm/llvm_build
|
||||
|
||||
|
||||
Vendored
+1
-1
Submodule 3rdparty/FAudio updated: 38e9da7264...70fec4889f
Vendored
+1
-1
Submodule 3rdparty/SoundTouch/soundtouch updated: ced3ce8d5e...394e1f58b2
Vendored
+1
-1
Submodule 3rdparty/curl/curl updated: 5ce164e0e9...de7b3e8921
Vendored
+7
-3
@@ -44,7 +44,7 @@
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
|
||||
<AdditionalIncludeDirectories>curl\include;curl\lib;extra;$(SolutionDir)3rdparty\wolfssl\wolfssl\wolfssl;$(SolutionDir)3rdparty\wolfssl\wolfssl;$(SolutionDir)3rdparty\wolfssl\extra\win32;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>HAVE_SNI;NDEBUG;BUILDING_LIBCURL;CURL_STATICLIB;USE_WOLFSSL;NO_MD4;WOLFSSL_USER_SETTINGS;USE_IPV6;SIZEOF_LONG=4;SIZEOF_LONG_LONG=8;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>WOLFSSL_ALT_CERT_CHAINS;HAVE_SNI;NDEBUG;BUILDING_LIBCURL;CURL_STATICLIB;USE_WOLFSSL;NO_MD4;WOLFSSL_USER_SETTINGS;USE_IPV6;SIZEOF_LONG=4;SIZEOF_LONG_LONG=8;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<StringPooling>true</StringPooling>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<WarningLevel>TurnOffAllWarnings</WarningLevel>
|
||||
@@ -92,9 +92,11 @@
|
||||
<ClCompile Include="curl\lib\curl_range.c" />
|
||||
<ClCompile Include="curl\lib\curl_rtmp.c" />
|
||||
<ClCompile Include="curl\lib\curl_sasl.c" />
|
||||
<ClCompile Include="curl\lib\curl_sha512_256.c" />
|
||||
<ClCompile Include="curl\lib\curl_sspi.c" />
|
||||
<ClCompile Include="curl\lib\curl_threads.c" />
|
||||
<ClCompile Include="curl\lib\curl_trc.c" />
|
||||
<ClCompile Include="curl\lib\cw-out.c" />
|
||||
<ClCompile Include="curl\lib\dict.c" />
|
||||
<ClCompile Include="curl\lib\doh.c" />
|
||||
<ClCompile Include="curl\lib\dynbuf.c" />
|
||||
@@ -156,6 +158,7 @@
|
||||
<ClCompile Include="curl\lib\psl.c" />
|
||||
<ClCompile Include="curl\lib\rand.c" />
|
||||
<ClCompile Include="curl\lib\rename.c" />
|
||||
<ClCompile Include="curl\lib\request.c" />
|
||||
<ClCompile Include="curl\lib\rtsp.c" />
|
||||
<ClCompile Include="curl\lib\select.c" />
|
||||
<ClCompile Include="curl\lib\sendf.c" />
|
||||
@@ -286,12 +289,13 @@
|
||||
<ClInclude Include="curl\lib\curl_setup.h" />
|
||||
<ClInclude Include="curl\lib\curl_setup_once.h" />
|
||||
<ClInclude Include="curl\lib\curl_sha256.h" />
|
||||
<ClInclude Include="curl\lib\curl_sha512_256.h" />
|
||||
<ClInclude Include="curl\lib\curl_sspi.h" />
|
||||
<ClInclude Include="curl\lib\curl_threads.h" />
|
||||
<ClInclude Include="curl\lib\curl_trc.h" />
|
||||
<ClInclude Include="curl\lib\cw-out.h" />
|
||||
<ClInclude Include="curl\lib\dict.h" />
|
||||
<ClInclude Include="curl\lib\doh.h" />
|
||||
<ClInclude Include="curl\lib\dotdot.h" />
|
||||
<ClInclude Include="curl\lib\dynbuf.h" />
|
||||
<ClInclude Include="curl\lib\dynhds.h" />
|
||||
<ClInclude Include="curl\lib\easyif.h" />
|
||||
@@ -341,6 +345,7 @@
|
||||
<ClInclude Include="curl\lib\psl.h" />
|
||||
<ClInclude Include="curl\lib\rand.h" />
|
||||
<ClInclude Include="curl\lib\rename.h" />
|
||||
<ClInclude Include="curl\lib\request.h" />
|
||||
<ClInclude Include="curl\lib\rtsp.h" />
|
||||
<ClInclude Include="curl\lib\select.h" />
|
||||
<ClInclude Include="curl\lib\sendf.h" />
|
||||
@@ -386,7 +391,6 @@
|
||||
<ClInclude Include="curl\lib\vauth\vauth.h" />
|
||||
<ClInclude Include="curl\lib\vquic\vquic.h" />
|
||||
<ClInclude Include="curl\lib\vssh\ssh.h" />
|
||||
<ClInclude Include="curl\lib\vssh\wolfssh.h" />
|
||||
<ClInclude Include="curl\lib\vtls\bearssl.h" />
|
||||
<ClInclude Include="curl\lib\vtls\gtls.h" />
|
||||
<ClInclude Include="curl\lib\vtls\keylog.h" />
|
||||
|
||||
+18
-6
@@ -504,6 +504,15 @@
|
||||
<ClCompile Include="curl\lib\curl_trc.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="curl\lib\request.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="curl\lib\cw-out.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="curl\lib\curl_sha512_256.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="curl\include\curl\curl.h">
|
||||
@@ -677,9 +686,6 @@
|
||||
<ClInclude Include="curl\lib\doh.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="curl\lib\dotdot.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="curl\lib\easyif.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
@@ -890,9 +896,6 @@
|
||||
<ClInclude Include="curl\lib\vssh\ssh.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="curl\lib\vssh\wolfssh.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="curl\lib\vtls\bearssl.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
@@ -1028,6 +1031,15 @@
|
||||
<ClInclude Include="curl\lib\curl_trc.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="curl\lib\request.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="curl\lib\cw-out.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="curl\lib\curl_sha512_256.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="curl\lib\libcurl.rc">
|
||||
|
||||
Vendored
+2
-2
@@ -39,9 +39,9 @@
|
||||
<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" -DLLVM_USE_CRT_DEBUG=MTd -DLLVM_USE_CRT_RELEASE=MT -DENABLE_OPT=OFF -S glslang -B build</CmakeReleaseCLI>
|
||||
cmake -G $(CmakeGenerator) -A x64 -DCMAKE_BUILD_TYPE="Release" -DLLVM_USE_CRT_DEBUG=MDd -DLLVM_USE_CRT_RELEASE=MT -DENABLE_OPT=OFF -S glslang -B build</CmakeReleaseCLI>
|
||||
<CmakeDebugCLI>call vsdevcmd.bat -arch=amd64
|
||||
cmake -G $(CmakeGenerator) -A x64 -DCMAKE_BUILD_TYPE="Debug" -DLLVM_USE_CRT_DEBUG=MTd -DLLVM_USE_CRT_RELEASE=MT -DENABLE_OPT=OFF -S glslang -B build</CmakeDebugCLI>
|
||||
cmake -G $(CmakeGenerator) -A x64 -DCMAKE_BUILD_TYPE="Debug" -DLLVM_USE_CRT_DEBUG=MDd -DLLVM_USE_CRT_RELEASE=MT -DENABLE_OPT=OFF -S glslang -B build</CmakeDebugCLI>
|
||||
<PropsAbsPath>$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\..\buildfiles\msvc\common_default.props'))</PropsAbsPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
|
||||
Vendored
+2
-2
@@ -44,12 +44,12 @@
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<OutDir>$(SolutionDir)lib/</OutDir>
|
||||
<OutDir>$(SolutionDir)lib\$(Configuration)-$(Platform)/</OutDir>
|
||||
<IntDir>$(SolutionDir)tmp\$(ProjectName)-$(Configuration)-$(Platform)/</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<OutDir>$(SolutionDir)lib/</OutDir>
|
||||
<OutDir>$(SolutionDir)lib\$(Configuration)-$(Platform)/</OutDir>
|
||||
<IntDir>$(SolutionDir)tmp\$(ProjectName)-$(Configuration)-$(Platform)/</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
|
||||
Vendored
+5
@@ -4,6 +4,11 @@ if (NOT USE_SYSTEM_LIBPNG)
|
||||
# We use libpng's static library and don't need to build the shared library and run the tests
|
||||
set(PNG_SHARED OFF CACHE BOOL "Build shared lib")
|
||||
set(PNG_TESTS OFF CACHE BOOL "Build libpng tests")
|
||||
if (NOT USE_SYSTEM_ZLIB)
|
||||
set(PNG_BUILD_ZLIB ON CACHE BOOL "Custom zlib location, else find_package is used")
|
||||
add_library(ZLIB::ZLIB INTERFACE IMPORTED)
|
||||
target_link_libraries(ZLIB::ZLIB INTERFACE 3rdparty_zlib)
|
||||
endif()
|
||||
set(SKIP_INSTALL_ALL ON)
|
||||
add_subdirectory(libpng EXCLUDE_FROM_ALL)
|
||||
target_include_directories(png_static INTERFACE "${libpng_BINARY_DIR}" "${libpng_SOURCE_DIR}")
|
||||
|
||||
Vendored
+1
-1
Submodule 3rdparty/libpng/libpng updated: 35d9f5ea52...ed217e3e60
Vendored
+18
-20
@@ -1,12 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug Library|x64">
|
||||
<Configuration>Debug Library</Configuration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release Library|x64">
|
||||
<Configuration>Release Library</Configuration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
@@ -19,11 +19,11 @@
|
||||
<Import Project="$(SolutionDir)\buildfiles\msvc\common_default.props" />
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<Import Project="$(SolutionDir)\buildfiles\msvc\common_default_macros.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug Library|x64'" Label="Configuration">
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release Library|x64'" Label="Configuration">
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
@@ -33,28 +33,28 @@
|
||||
</PropertyGroup>
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug Library|x64'" Label="PropertySheets">
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release Library|x64'" Label="PropertySheets">
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug Library|x64'">
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<CustomBuildBeforeTargets />
|
||||
<TargetName>$(ProjectName)16</TargetName>
|
||||
<OutDir>$(SolutionDir)lib/</OutDir>
|
||||
<OutDir>$(SolutionDir)lib\$(Configuration)-$(Platform)/</OutDir>
|
||||
<IntDir>$(SolutionDir)tmp\$(ProjectName)-$(Configuration)-$(Platform)/</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release Library|x64'">
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<CustomBuildBeforeTargets />
|
||||
<TargetName>$(ProjectName)16</TargetName>
|
||||
<OutDir>$(SolutionDir)lib/</OutDir>
|
||||
<OutDir>$(SolutionDir)lib\$(Configuration)-$(Platform)/</OutDir>
|
||||
<IntDir>$(SolutionDir)tmp\$(ProjectName)-$(Configuration)-$(Platform)/</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug Library|x64'">
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>$(WarningLevel)</WarningLevel>
|
||||
@@ -74,19 +74,17 @@
|
||||
<DisableSpecificWarnings>$(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
<AdditionalIncludeDirectories>$(ZLibSrcDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<TreatWarningAsError>$(TreatWarningAsError)</TreatWarningAsError>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release Library|x64'">
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>$(WarningLevel)</WarningLevel>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
@@ -115,8 +113,8 @@
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="libpng\png.c">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Library|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release Library|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="libpng\pngerror.c" />
|
||||
<ClCompile Include="libpng\pngget.c" />
|
||||
@@ -135,8 +133,8 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="libpng\scripts\pngwin.rc">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug Library|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release Library|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
|
||||
Vendored
+1
-1
Submodule 3rdparty/libsdl-org/SDL updated: 859844eae3...f461d91cd2
+1
-3
@@ -35,7 +35,7 @@
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup>
|
||||
<_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
|
||||
<OutDir>$(SolutionDir)lib/</OutDir>
|
||||
<OutDir>$(SolutionDir)lib\$(Configuration)-$(Platform)/</OutDir>
|
||||
<IntDir>$(SolutionDir)tmp\$(ProjectName)-$(Configuration)-$(Platform)/</IntDir>
|
||||
<TargetName>libusb-1.0</TargetName>
|
||||
</PropertyGroup>
|
||||
@@ -50,11 +50,9 @@
|
||||
<ClCompile Condition="'$(Configuration)'=='Debug'">
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<ClCompile Condition="'$(Configuration)'=='Release'">
|
||||
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Lib>
|
||||
<OutputFile>$(OutDir)libusb-1.0.lib</OutputFile>
|
||||
|
||||
Vendored
+6
-14
@@ -36,12 +36,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)
|
||||
|
||||
# now tries to find LLVM again
|
||||
find_package(LLVM 16.0 CONFIG)
|
||||
if(NOT LLVM_FOUND)
|
||||
set(LLVM_DIR "${CMAKE_CURRENT_BINARY_DIR}/llvm_build/lib/cmake/llvm/")
|
||||
find_package(LLVM 17.0 CONFIG)
|
||||
endif()
|
||||
if(NOT LLVM_FOUND)
|
||||
message(FATAL_ERROR "Couldn't build LLVM from the submodule. You might need to run `git submodule update --init`")
|
||||
endif()
|
||||
@@ -53,20 +48,16 @@ if(WITH_LLVM)
|
||||
set(LLVM_DIR ${CMAKE_SOURCE_DIR}/${LLVM_DIR})
|
||||
endif()
|
||||
|
||||
find_package(LLVM 16.0 CONFIG)
|
||||
if(NOT LLVM_FOUND)
|
||||
find_package(LLVM 17.0 CONFIG)
|
||||
endif()
|
||||
find_package(LLVM CONFIG)
|
||||
|
||||
if (NOT LLVM_FOUND)
|
||||
if (LLVM_VERSION AND LLVM_VERSION_MAJOR LESS 16)
|
||||
message(FATAL_ERROR "Found LLVM version ${LLVM_VERSION}. Required versions 16...17. \
|
||||
Enable BUILD_LLVM option to build LLVM from included as a git submodule.")
|
||||
endif()
|
||||
|
||||
message(FATAL_ERROR "Can't find LLVM libraries from the CMAKE_PREFIX_PATH path or LLVM_DIR. \
|
||||
Enable BUILD_LLVM option to build LLVM from included as a git submodule.")
|
||||
endif()
|
||||
if (LLVM_VERSION VERSION_LESS 16)
|
||||
message(FATAL_ERROR "Found LLVM version ${LLVM_VERSION}. Required version 16 or above. \
|
||||
Enable BUILD_LLVM option to build LLVM from included as a git submodule.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (STATIC_LINK_LLVM)
|
||||
@@ -89,6 +80,7 @@ if(WITH_LLVM)
|
||||
Core
|
||||
ExecutionEngine
|
||||
MCJIT
|
||||
Passes
|
||||
)
|
||||
else()
|
||||
set(LLVM_LIBS LLVM)
|
||||
|
||||
+2
-2
@@ -39,9 +39,9 @@
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros">
|
||||
<CmakeReleaseCLI>call vsdevcmd.bat -arch=amd64
|
||||
cmake -G Ninja -DCMAKE_CXX_COMPILER="cl.exe" -DCMAKE_C_COMPILER="cl.exe" -DCMAKE_BUILD_TYPE="Release" -DCMAKE_INSTALL_PREFIX="./Release" -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_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_BENCHMARKS=OFF -DLLVM_INCLUDE_UTILS=OFF -DCMAKE_SYSTEM_VERSION=6.1 -DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=$(WindowsTargetPlatformVersion) -DLLVM_USE_CRT_DEBUG=MTd -DLLVM_USE_CRT_RELEASE=MT -DLLVM_USE_INTEL_JITEVENTS=ON ../llvm/llvm</CmakeReleaseCLI>
|
||||
cmake -G Ninja -DCMAKE_CXX_COMPILER="cl.exe" -DCMAKE_C_COMPILER="cl.exe" -DCMAKE_BUILD_TYPE="Release" -DCMAKE_INSTALL_PREFIX="./Release" -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_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_BENCHMARKS=OFF -DLLVM_INCLUDE_UTILS=OFF -DCMAKE_SYSTEM_VERSION=6.1 -DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=$(WindowsTargetPlatformVersion) -DLLVM_USE_CRT_DEBUG=MDd -DLLVM_USE_CRT_RELEASE=MT -DLLVM_USE_INTEL_JITEVENTS=ON ../llvm/llvm</CmakeReleaseCLI>
|
||||
<CmakeDebugCLI>call vsdevcmd.bat -arch=amd64
|
||||
cmake -G Ninja -DCMAKE_CXX_COMPILER="cl.exe" -DCMAKE_C_COMPILER="cl.exe" -DCMAKE_BUILD_TYPE="Debug" -DCMAKE_INSTALL_PREFIX="./Debug" -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_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_BENCHMARKS=OFF -DLLVM_INCLUDE_UTILS=OFF -DCMAKE_SYSTEM_VERSION=6.1 -DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=$(WindowsTargetPlatformVersion) -DLLVM_USE_CRT_DEBUG=MTd -DLLVM_USE_CRT_RELEASE=MT -DLLVM_USE_INTEL_JITEVENTS=ON ../llvm/llvm</CmakeDebugCLI>
|
||||
cmake -G Ninja -DCMAKE_CXX_COMPILER="cl.exe" -DCMAKE_C_COMPILER="cl.exe" -DCMAKE_BUILD_TYPE="Debug" -DCMAKE_INSTALL_PREFIX="./Debug" -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_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_BENCHMARKS=OFF -DLLVM_INCLUDE_UTILS=OFF -DCMAKE_SYSTEM_VERSION=6.1 -DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=$(WindowsTargetPlatformVersion) -DLLVM_USE_CRT_DEBUG=MDd -DLLVM_USE_CRT_RELEASE=MT -DLLVM_USE_INTEL_JITEVENTS=ON ../llvm/llvm</CmakeDebugCLI>
|
||||
<CmakeCleanCLI>echo Cleaning..
|
||||
for /F "delims= eol=|" %%f in ('
|
||||
dir /b ^| findstr /V "[^.]*\build[^.]*\.vcxproj"') do (
|
||||
|
||||
+2
-2
@@ -39,9 +39,9 @@
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros">
|
||||
<CmakeReleaseCLI>call vsdevcmd.bat -arch=amd64
|
||||
cmake -G Ninja -DCMAKE_CXX_COMPILER="clang-cl.exe" -DCMAKE_C_COMPILER="clang-cl.exe" -DCMAKE_BUILD_TYPE="Release" -DLLVM_TARGETS_TO_BUILD=X86 -DLLVM_DEFAULT_TARGET_TRIPLE:STRING=x86_64-pc-windows-msvc -DLLVM_HOST_TRIPLE:STRING=x86_64-pc-windows-msvc -DCMAKE_INSTALL_PREFIX="./Release" -DLLVM_BUILD_RUNTIME=OFF -DLLVM_BUILD_TOOLS=OFF -DLLVM_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_BENCHMARKS=OFF -DLLVM_INCLUDE_UTILS=OFF -DCMAKE_SYSTEM_VERSION=6.1 -DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=$(WindowsTargetPlatformVersion) -DLLVM_USE_CRT_DEBUG=MTd -DLLVM_USE_CRT_RELEASE=MT -DLLVM_USE_INTEL_JITEVENTS=ON ../llvm/llvm</CmakeReleaseCLI>
|
||||
cmake -G Ninja -DCMAKE_CXX_COMPILER="clang-cl.exe" -DCMAKE_C_COMPILER="clang-cl.exe" -DCMAKE_BUILD_TYPE="Release" -DLLVM_TARGETS_TO_BUILD=X86 -DLLVM_DEFAULT_TARGET_TRIPLE:STRING=x86_64-pc-windows-msvc -DLLVM_HOST_TRIPLE:STRING=x86_64-pc-windows-msvc -DCMAKE_INSTALL_PREFIX="./Release" -DLLVM_BUILD_RUNTIME=OFF -DLLVM_BUILD_TOOLS=OFF -DLLVM_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_BENCHMARKS=OFF -DLLVM_INCLUDE_UTILS=OFF -DCMAKE_SYSTEM_VERSION=6.1 -DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=$(WindowsTargetPlatformVersion) -DLLVM_USE_CRT_DEBUG=MDd -DLLVM_USE_CRT_RELEASE=MT -DLLVM_USE_INTEL_JITEVENTS=ON ../llvm/llvm</CmakeReleaseCLI>
|
||||
<CmakeDebugCLI>call vsdevcmd.bat -arch=amd64
|
||||
cmake -G Ninja -DCMAKE_CXX_COMPILER="clang-cl.exe" -DCMAKE_C_COMPILER="clang-cl.exe" -DCMAKE_BUILD_TYPE="Debug" -DLLVM_TARGETS_TO_BUILD=X86 -DLLVM_DEFAULT_TARGET_TRIPLE:STRING=x86_64-pc-windows-msvc -DLLVM_HOST_TRIPLE:STRING=x86_64-pc-windows-msvc -DCMAKE_INSTALL_PREFIX="./Debug" -DLLVM_BUILD_RUNTIME=OFF -DLLVM_BUILD_TOOLS=OFF -DLLVM_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_BENCHMARKS=OFF -DLLVM_INCLUDE_UTILS=OFF -DCMAKE_SYSTEM_VERSION=6.1 -DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=$(WindowsTargetPlatformVersion) -DLLVM_USE_CRT_DEBUG=MTd -DLLVM_USE_CRT_RELEASE=MT -DLLVM_USE_INTEL_JITEVENTS=ON ../llvm/llvm</CmakeDebugCLI>
|
||||
cmake -G Ninja -DCMAKE_CXX_COMPILER="clang-cl.exe" -DCMAKE_C_COMPILER="clang-cl.exe" -DCMAKE_BUILD_TYPE="Debug" -DLLVM_TARGETS_TO_BUILD=X86 -DLLVM_DEFAULT_TARGET_TRIPLE:STRING=x86_64-pc-windows-msvc -DLLVM_HOST_TRIPLE:STRING=x86_64-pc-windows-msvc -DCMAKE_INSTALL_PREFIX="./Debug" -DLLVM_BUILD_RUNTIME=OFF -DLLVM_BUILD_TOOLS=OFF -DLLVM_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_BENCHMARKS=OFF -DLLVM_INCLUDE_UTILS=OFF -DCMAKE_SYSTEM_VERSION=6.1 -DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=$(WindowsTargetPlatformVersion) -DLLVM_USE_CRT_DEBUG=MDd -DLLVM_USE_CRT_RELEASE=MT -DLLVM_USE_INTEL_JITEVENTS=ON ../llvm/llvm</CmakeDebugCLI>
|
||||
<CmakeCleanCLI>echo Cleaning..
|
||||
for /F "delims= eol=|" %%f in ('
|
||||
dir /b ^| findstr /V "[^.]*\build[^.]*\.vcxproj"') do (
|
||||
|
||||
Vendored
+1
-1
Submodule 3rdparty/miniupnp/miniupnp updated: f22a96b469...7f189988a0
-2
@@ -58,7 +58,6 @@
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<AdditionalIncludeDirectories>generated;miniupnp\miniupnpc;miniupnp\miniupnpc\include</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
@@ -80,7 +79,6 @@ cscript genminiupnpcstrings.vbs</Command>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<AdditionalIncludeDirectories>generated;miniupnp\miniupnpc;miniupnp\miniupnpc\include</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>
|
||||
|
||||
Vendored
+1
@@ -18,6 +18,7 @@ else()
|
||||
set(WOLFSSL_SNI ON CACHE STRING "Enable SNI (default: disabled)")
|
||||
set(WOLFSSL_OPENSSLEXTRA ON CACHE STRING "Enable extra OpenSSL API, size+ (default: disabled)")
|
||||
set(WOLFSSL_HARDEN OFF CACHE STRING "Enable Hardened build, Enables Timing Resistance and Blinding (default: enabled)")
|
||||
set(WOLFSSL_ALT_CERT_CHAINS ON CACHE STRING "Enable support for Alternate certification chains (default: disabled)")
|
||||
|
||||
add_subdirectory(wolfssl EXCLUDE_FROM_ALL)
|
||||
|
||||
|
||||
@@ -51,6 +51,9 @@
|
||||
#define ECC_TIMING_RESISTANT
|
||||
#define USE_FAST_MATH
|
||||
#define FP_MAX_BITS 8192
|
||||
#ifndef WOLFSSL_ALT_CERT_CHAINS
|
||||
#define WOLFSSL_ALT_CERT_CHAINS
|
||||
#endif
|
||||
|
||||
/* UTF-8 aware filesystem functions for Windows */
|
||||
#define WOLFSSL_USER_FILESYSTEM
|
||||
|
||||
Vendored
+1
-1
Submodule 3rdparty/wolfssl/wolfssl updated: 66596ad9e1...8970ff4c34
Vendored
+3
-1
@@ -15,5 +15,7 @@ else()
|
||||
target_include_directories(3rdparty_zlib INTERFACE zlib ${CMAKE_CURRENT_BINARY_DIR}/zlib)
|
||||
target_compile_definitions(3rdparty_zlib INTERFACE -DZLIB_CONST=1)
|
||||
|
||||
set(ZLIB_ROOT ${CMAKE_CURRENT_BINARY_DIR}/zlib PARENT_SCOPE)
|
||||
if(NOT MSVC)
|
||||
set(ZLIB_ROOT ${CMAKE_CURRENT_BINARY_DIR}/zlib PARENT_SCOPE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
Vendored
-1
@@ -88,7 +88,6 @@
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<DisableSpecificWarnings>$(DisableSpecificWarnings);4127;4131;4242;4244</DisableSpecificWarnings>
|
||||
<TreatWarningAsError>$(TreatWarningAsError)</TreatWarningAsError>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
|
||||
+9
-7
@@ -5,15 +5,17 @@ Other instructions may be found [here](https://wiki.rpcs3.net/index.php?title=Bu
|
||||
|
||||
## Setup your environment
|
||||
|
||||
### Windows 7 or later
|
||||
### Windows 10 or later
|
||||
|
||||
* [CMake 3.28.0+](https://www.cmake.org/download/) (add to PATH)
|
||||
* [Python 3.6+](https://www.python.org/downloads/) (add to PATH)
|
||||
* [Qt 6.6.2](https://www.qt.io/download-qt-installer)
|
||||
* [Qt 6.6.3](https://www.qt.io/download-qt-installer)
|
||||
* [Visual Studio 2022](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community) (or at least Visual Studio 2019 16.11.xx+ as C++20 is not included in previous versions)
|
||||
* [Vulkan SDK 1.3.268.0](https://vulkan.lunarg.com/sdk/home) (See "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/windows/getting_started.html)) for now future SDKs don't work. You need precisely 1.3.268.0.
|
||||
|
||||
**Either add the** `QTDIR` **environment variable, e.g.** `<QtInstallFolder>\6.6.2\msvc2019_64\` **, or use the [Visual Studio Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.QtVisualStudioTools2019)**
|
||||
**Either add the** `QTDIR` **environment variable, e.g.** `<QtInstallFolder>\6.6.3\msvc2019_64\` **, or use the [Visual Studio Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.QtVisualStudioTools2019)**
|
||||
|
||||
**NOTE: If you have issues with the Qt plugin, you may want to uninstall the Qt Plugin and install the [Legacy Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.LEGACYQtVisualStudioTools2019) instead.**
|
||||
|
||||
### Linux
|
||||
|
||||
@@ -21,7 +23,7 @@ These are the essentials tools to build RPCS3 on Linux. Some of them can be inst
|
||||
|
||||
* Clang 12+ or GCC 11+
|
||||
* [CMake 3.28.0+](https://www.cmake.org/download/)
|
||||
* [Qt 6.6.2](https://www.qt.io/download-qt-installer)
|
||||
* [Qt 6.6.3](https://www.qt.io/download-qt-installer)
|
||||
* [Vulkan SDK 1.3.268.0](https://vulkan.lunarg.com/sdk/home) (See "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/linux/getting_started.html)) for now future SDKs don't work. You need precisely 1.3.268.0.
|
||||
* [SDL2](https://github.com/libsdl-org/SDL/releases) (for the FAudio backend)
|
||||
|
||||
@@ -96,9 +98,9 @@ git submodule update --init
|
||||
#### Configuring the Qt plugin (if used)
|
||||
|
||||
1) Go to `Extensions->Qt VS Tools->Qt Versions`.
|
||||
2) Add the path to your Qt installation with compiler e.g. `<QtInstallFolder>\6.6.2\msvc2019_64`, version will fill in automatically.
|
||||
3) Go to `Extensions->Qt VS Tools->Options->Legacy Project Format`.
|
||||
4) Set `Build: Run pre-build setup` to `true`.
|
||||
2) Add the path to your Qt installation with compiler e.g. `<QtInstallFolder>\6.6.3\msvc2019_64`, version will fill in automatically.
|
||||
3) Go to `Extensions->Qt VS Tools->Options->Legacy Project Format`. (Only available in the legacy Qt plugin)
|
||||
4) Set `Build: Run pre-build setup` to `true`. (Only available in the legacy Qt plugin)
|
||||
|
||||
#### Building the projects
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ struct bf_base
|
||||
{
|
||||
using type = T;
|
||||
using vtype = std::common_type_t<type>;
|
||||
using utype = typename std::make_unsigned<vtype>::type;
|
||||
using utype = std::make_unsigned_t<vtype>;
|
||||
|
||||
static constexpr bool can_be_packed = N < (sizeof(int) * 8 + (std::is_unsigned_v<vtype> ? 1 : 0)) && sizeof(vtype) > sizeof(int);
|
||||
using compact_type = std::conditional_t<can_be_packed, std::conditional_t<std::is_unsigned_v<vtype>, uint, int>, vtype>;
|
||||
|
||||
@@ -19,6 +19,7 @@ LOG_CHANNEL(jit_log, "JIT");
|
||||
void jit_announce(uptr func, usz size, std::string_view name)
|
||||
{
|
||||
#ifdef __linux__
|
||||
#if 0
|
||||
static const struct tmp_perf_map
|
||||
{
|
||||
std::string name{fmt::format("/tmp/perf-%d.map", getpid())};
|
||||
@@ -44,6 +45,7 @@ void jit_announce(uptr func, usz size, std::string_view name)
|
||||
fs::remove_file(s_map.name);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (!size)
|
||||
|
||||
@@ -30,6 +30,7 @@ LOG_CHANNEL(jit_log, "JIT");
|
||||
#pragma GCC diagnostic ignored "-Weffc++"
|
||||
#pragma GCC diagnostic ignored "-Wmissing-noreturn"
|
||||
#endif
|
||||
#include <llvm/Support/CodeGen.h>
|
||||
#include "llvm/Support/TargetSelect.h"
|
||||
#include "llvm/TargetParser/Host.h"
|
||||
#include "llvm/ExecutionEngine/ExecutionEngine.h"
|
||||
@@ -542,7 +543,11 @@ jit_compiler::jit_compiler(const std::unordered_map<std::string, u64>& _link, co
|
||||
.setErrorStr(&result)
|
||||
.setEngineKind(llvm::EngineKind::JIT)
|
||||
.setMCJITMemoryManager(std::move(mem))
|
||||
#if LLVM_VERSION_MAJOR < 18
|
||||
.setOptLevel(llvm::CodeGenOpt::Aggressive)
|
||||
#else
|
||||
.setOptLevel(llvm::CodeGenOptLevel::Aggressive)
|
||||
#endif
|
||||
.setCodeModel(flags & 0x2 ? llvm::CodeModel::Large : llvm::CodeModel::Small)
|
||||
#ifdef __APPLE__
|
||||
//.setCodeModel(llvm::CodeModel::Large)
|
||||
|
||||
@@ -518,7 +518,7 @@ struct fmt::cfmt_src
|
||||
TYPE(llong);
|
||||
TYPE(schar);
|
||||
TYPE(short);
|
||||
if (std::is_signed<char>::value) TYPE(char);
|
||||
if (std::is_signed_v<char>) TYPE(char);
|
||||
TYPE(long);
|
||||
TYPE(s128);
|
||||
|
||||
|
||||
+3
-3
@@ -54,7 +54,7 @@ struct fmt_unveil
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct fmt_unveil<T, std::enable_if_t<std::is_integral<T>::value && sizeof(T) <= 8 && alignof(T) <= 8>>
|
||||
struct fmt_unveil<T, std::enable_if_t<std::is_integral_v<T> && sizeof(T) <= 8 && alignof(T) <= 8>>
|
||||
{
|
||||
using type = T;
|
||||
|
||||
@@ -65,7 +65,7 @@ struct fmt_unveil<T, std::enable_if_t<std::is_integral<T>::value && sizeof(T) <=
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct fmt_unveil<T, std::enable_if_t<std::is_floating_point<T>::value && sizeof(T) <= 8 && alignof(T) <= 8>>
|
||||
struct fmt_unveil<T, std::enable_if_t<std::is_floating_point_v<T> && sizeof(T) <= 8 && alignof(T) <= 8>>
|
||||
{
|
||||
using type = T;
|
||||
|
||||
@@ -77,7 +77,7 @@ struct fmt_unveil<T, std::enable_if_t<std::is_floating_point<T>::value && sizeof
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct fmt_unveil<T, std::enable_if_t<std::is_enum<T>::value>>
|
||||
struct fmt_unveil<T, std::enable_if_t<std::is_enum_v<T>>>
|
||||
{
|
||||
using type = T;
|
||||
|
||||
|
||||
+19
-1
@@ -2193,7 +2193,10 @@ u64 thread_base::finalize(thread_state result_state) noexcept
|
||||
return thread_ctrl::get_name_cached();
|
||||
};
|
||||
|
||||
sig_log.notice("Thread time: %fs (%fGc); Faults: %u [rsx:%u, spu:%u]; [soft:%u hard:%u]; Switches:[vol:%u unvol:%u]; Wait:[%.3fs, spur:%u]",
|
||||
const bool is_cpu_thread = !!cpu_thread::get_current();
|
||||
auto& thread_log = (is_cpu_thread || g_tls_fault_all ? sig_log.notice : sig_log.trace);
|
||||
|
||||
thread_log("Thread time: %fs (%fGc); Faults: %u [rsx:%u, spu:%u]; [soft:%u hard:%u]; Switches:[vol:%u unvol:%u]; Wait:[%.3fs, spur:%u]",
|
||||
time / 1000000000.,
|
||||
cycles / 1000000000.,
|
||||
g_tls_fault_all,
|
||||
@@ -2640,6 +2643,21 @@ void thread_base::exec()
|
||||
sys_log.notice("\n%s", info);
|
||||
}
|
||||
|
||||
std::string reason_buf;
|
||||
|
||||
if (auto ppu = cpu_thread::get_current<ppu_thread>())
|
||||
{
|
||||
if (auto func = ppu->current_function)
|
||||
{
|
||||
fmt::append(reason_buf, "%s (PPU: %s)", reason, func);
|
||||
}
|
||||
}
|
||||
|
||||
if (!reason_buf.empty())
|
||||
{
|
||||
reason = reason_buf;
|
||||
}
|
||||
|
||||
sig_log.fatal("Thread terminated due to fatal error: %s", reason);
|
||||
|
||||
logs::listener::sync_all();
|
||||
|
||||
@@ -218,6 +218,11 @@ public:
|
||||
// Get current thread name
|
||||
static std::string get_name()
|
||||
{
|
||||
if (!g_tls_this_thread)
|
||||
{
|
||||
return "not named_thread";
|
||||
}
|
||||
|
||||
return *g_tls_this_thread->m_tname.load();
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -60,9 +60,9 @@ public:
|
||||
static constexpr usz bitmax = sizeof(T) * 8;
|
||||
static constexpr usz bitsize = static_cast<under>(T::__bitset_enum_max);
|
||||
|
||||
static_assert(std::is_enum<T>::value, "bs_t<> error: invalid type (must be enum)");
|
||||
static_assert(std::is_enum_v<T>, "bs_t<> error: invalid type (must be enum)");
|
||||
static_assert(bitsize <= bitmax, "bs_t<> error: invalid __bitset_enum_max");
|
||||
static_assert(bitsize != bitmax || std::is_unsigned<under>::value, "bs_t<> error: invalid __bitset_enum_max (sign bit)");
|
||||
static_assert(bitsize != bitmax || std::is_unsigned_v<under>, "bs_t<> error: invalid __bitset_enum_max (sign bit)");
|
||||
|
||||
// Helper function
|
||||
static constexpr under shift(T value)
|
||||
|
||||
+5
-5
@@ -38,13 +38,13 @@ jobs:
|
||||
displayName: ccache
|
||||
|
||||
- bash: |
|
||||
docker pull --quiet rpcs3/rpcs3-ci-focal:1.6
|
||||
docker pull --quiet rpcs3/rpcs3-ci-focal:1.7
|
||||
docker run \
|
||||
-v $(pwd):/rpcs3 \
|
||||
--env-file .ci/docker.env \
|
||||
-v $CCACHE_DIR:/root/.ccache \
|
||||
-v $BUILD_ARTIFACTSTAGINGDIRECTORY:/root/artifacts \
|
||||
rpcs3/rpcs3-ci-focal:1.6 \
|
||||
rpcs3/rpcs3-ci-focal:1.7 \
|
||||
/rpcs3/.ci/build-linux.sh
|
||||
displayName: Docker setup and build
|
||||
|
||||
@@ -69,9 +69,9 @@ jobs:
|
||||
variables:
|
||||
COMPILER: msvc
|
||||
QT_VER_MAIN: '6'
|
||||
QT_VER: '6.6.2'
|
||||
QT_VER: '6.6.3'
|
||||
QT_VER_MSVC: 'msvc2019'
|
||||
QT_DATE: '202402121135'
|
||||
QT_DATE: '202403191840'
|
||||
QTDIR: C:\Qt\$(QT_VER)\$(QT_VER_MSVC)_64
|
||||
VULKAN_VER: '1.3.268.0'
|
||||
VULKAN_SDK_SHA: '8459ef49bd06b697115ddd3d97c9aec729e849cd775f5be70897718a9b3b9db5'
|
||||
@@ -132,7 +132,7 @@ jobs:
|
||||
UPLOAD_REPO_FULL_NAME: "RPCS3/rpcs3-binaries-mac"
|
||||
RELEASE_MESSAGE: "../GitHubReleaseMessage.txt"
|
||||
ARTDIR: $(Build.ArtifactStagingDirectory)
|
||||
QT_VER: '6.6.2'
|
||||
QT_VER: '6.6.3'
|
||||
QT_VER_MAIN: '6'
|
||||
|
||||
pool:
|
||||
|
||||
Binary file not shown.
@@ -71,6 +71,7 @@ else()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
add_compile_options(-Werror=inconsistent-missing-override)
|
||||
add_compile_options(-Werror=delete-non-virtual-dtor)
|
||||
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
add_compile_options(-Werror=suggest-override)
|
||||
add_compile_options(-Wclobbered)
|
||||
@@ -84,7 +85,6 @@ else()
|
||||
add_compile_options(-fconstexpr-steps=16777216)
|
||||
add_compile_options(-Wno-unused-lambda-capture)
|
||||
add_compile_options(-Wno-unused-private-field)
|
||||
add_compile_options(-Wno-delete-non-virtual-dtor)
|
||||
add_compile_options(-Wno-unused-command-line-argument)
|
||||
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
add_compile_options(-Wno-class-memaccess)
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<ExceptionHandling>false</ExceptionHandling>
|
||||
<AdditionalOptions>-d2FH4- %(AdditionalOptions)</AdditionalOptions>
|
||||
<RuntimeLibrary Condition = "$(Configuration.Contains('Release'))">MultiThreaded</RuntimeLibrary>
|
||||
<RuntimeLibrary Condition = "$(Configuration.Contains('Debug'))">MultiThreadedDebug</RuntimeLibrary>
|
||||
<RuntimeLibrary Condition = "$(Configuration.Contains('Debug'))">MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalOptions>-d2:-FH4- %(AdditionalOptions)</AdditionalOptions>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<ExceptionHandling>false</ExceptionHandling>
|
||||
<AdditionalOptions>%(AdditionalOptions)</AdditionalOptions>
|
||||
<RuntimeLibrary Condition = "$(Configuration.Contains('Release'))">MultiThreaded</RuntimeLibrary>
|
||||
<RuntimeLibrary Condition = "$(Configuration.Contains('Debug'))">MultiThreadedDebug</RuntimeLibrary>
|
||||
<RuntimeLibrary Condition = "$(Configuration.Contains('Debug'))">MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalOptions>%(AdditionalOptions)</AdditionalOptions>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
<PreprocessorDefinitions>LLVM_AVAILABLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
|
||||
@@ -128,10 +128,10 @@ Global
|
||||
{EB33566E-DA7F-4D28-9077-88C0B7C77E35}.Debug|x64.Build.0 = Release|x64
|
||||
{EB33566E-DA7F-4D28-9077-88C0B7C77E35}.Release|x64.ActiveCfg = Release|x64
|
||||
{EB33566E-DA7F-4D28-9077-88C0B7C77E35}.Release|x64.Build.0 = Release|x64
|
||||
{D6973076-9317-4EF2-A0B8-B7A18AC0713E}.Debug|x64.ActiveCfg = Debug Library|x64
|
||||
{D6973076-9317-4EF2-A0B8-B7A18AC0713E}.Debug|x64.Build.0 = Debug Library|x64
|
||||
{D6973076-9317-4EF2-A0B8-B7A18AC0713E}.Release|x64.ActiveCfg = Release Library|x64
|
||||
{D6973076-9317-4EF2-A0B8-B7A18AC0713E}.Release|x64.Build.0 = Release Library|x64
|
||||
{D6973076-9317-4EF2-A0B8-B7A18AC0713E}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{D6973076-9317-4EF2-A0B8-B7A18AC0713E}.Debug|x64.Build.0 = Debug|x64
|
||||
{D6973076-9317-4EF2-A0B8-B7A18AC0713E}.Release|x64.ActiveCfg = Release|x64
|
||||
{D6973076-9317-4EF2-A0B8-B7A18AC0713E}.Release|x64.Build.0 = Release|x64
|
||||
{60F89955-91C6-3A36-8000-13C592FEC2DF}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{60F89955-91C6-3A36-8000-13C592FEC2DF}.Debug|x64.Build.0 = Debug|x64
|
||||
{60F89955-91C6-3A36-8000-13C592FEC2DF}.Release|x64.ActiveCfg = Release|x64
|
||||
|
||||
@@ -74,6 +74,7 @@ usz decrypt_binaries_t::decrypt(std::string klic_input)
|
||||
const std::string old_path = _module;
|
||||
|
||||
fs::file elf_file;
|
||||
fs::file internal_file;
|
||||
|
||||
bool invalid = false;
|
||||
usz key_it = 0;
|
||||
@@ -83,6 +84,8 @@ usz decrypt_binaries_t::decrypt(std::string klic_input)
|
||||
{
|
||||
for (; key_it < m_klics.size(); key_it++)
|
||||
{
|
||||
internal_file.close();
|
||||
|
||||
if (!elf_file.open(old_path) || !elf_file.read(file_magic))
|
||||
{
|
||||
file_magic = 0;
|
||||
@@ -106,7 +109,8 @@ usz decrypt_binaries_t::decrypt(std::string klic_input)
|
||||
case "NPD\0"_u32:
|
||||
{
|
||||
// EDAT / SDAT
|
||||
elf_file = DecryptEDAT(elf_file, old_path, key_it != 0 ? 8 : 1, reinterpret_cast<u8*>(&m_klics[key_it]));
|
||||
internal_file = std::move(elf_file);
|
||||
elf_file = DecryptEDAT(internal_file, old_path, key_it != 0 ? 8 : 1, reinterpret_cast<u8*>(&m_klics[key_it]));
|
||||
|
||||
if (!elf_file)
|
||||
{
|
||||
|
||||
@@ -139,7 +139,8 @@ bool zip(const void* src, usz size, fs::file& out, bool multi_thread_it)
|
||||
return false;
|
||||
}
|
||||
|
||||
utils::serial compressor(!multi_thread_it || size < 0x40'0000);
|
||||
utils::serial compressor;
|
||||
compressor.set_expect_little_data(!multi_thread_it || size < 0x40'0000);
|
||||
compressor.m_file_handler = make_compressed_serialization_file_handler(out);
|
||||
|
||||
std::string_view buffer_view{static_cast<const char*>(src), size};
|
||||
|
||||
@@ -33,7 +33,12 @@ u32 AudioBackend::get_sample_size() const
|
||||
|
||||
u32 AudioBackend::get_channels() const
|
||||
{
|
||||
return static_cast<std::underlying_type_t<decltype(m_channels)>>(m_channels);
|
||||
return m_channels;
|
||||
}
|
||||
|
||||
audio_channel_layout AudioBackend::get_channel_layout() const
|
||||
{
|
||||
return m_layout;
|
||||
}
|
||||
|
||||
bool AudioBackend::get_convert_to_s16() const
|
||||
@@ -141,23 +146,63 @@ AudioChannelCnt AudioBackend::get_max_channel_count(u32 device_index)
|
||||
return count;
|
||||
}
|
||||
|
||||
AudioChannelCnt AudioBackend::convert_channel_count(u64 raw)
|
||||
u32 AudioBackend::default_layout_channel_count(audio_channel_layout layout)
|
||||
{
|
||||
switch (raw)
|
||||
switch (layout)
|
||||
{
|
||||
default:
|
||||
case 8:
|
||||
return AudioChannelCnt::SURROUND_7_1;
|
||||
case 7:
|
||||
case 6:
|
||||
return AudioChannelCnt::SURROUND_5_1;
|
||||
case 5:
|
||||
case 4:
|
||||
case 3:
|
||||
case 2:
|
||||
case 1:
|
||||
return AudioChannelCnt::STEREO;
|
||||
case 0:
|
||||
fmt::throw_exception("Unsupported channel count");
|
||||
case audio_channel_layout::mono: return 1;
|
||||
case audio_channel_layout::stereo: return 2;
|
||||
case audio_channel_layout::stereo_lfe: return 3;
|
||||
case audio_channel_layout::quadraphonic: return 4;
|
||||
case audio_channel_layout::quadraphonic_lfe: return 5;
|
||||
case audio_channel_layout::surround_5_1: return 6;
|
||||
case audio_channel_layout::surround_7_1: return 8;
|
||||
default: fmt::throw_exception("Unsupported layout %d", static_cast<u32>(layout));
|
||||
}
|
||||
}
|
||||
|
||||
u32 AudioBackend::layout_channel_count(u32 channels, audio_channel_layout layout)
|
||||
{
|
||||
if (channels == 0)
|
||||
{
|
||||
fmt::throw_exception("Unsupported channel count");
|
||||
}
|
||||
|
||||
return std::min(channels, default_layout_channel_count(layout));
|
||||
}
|
||||
|
||||
audio_channel_layout AudioBackend::default_layout(u32 channels)
|
||||
{
|
||||
switch (channels)
|
||||
{
|
||||
case 1: return audio_channel_layout::mono;
|
||||
case 2: return audio_channel_layout::stereo;
|
||||
case 3: return audio_channel_layout::stereo_lfe;
|
||||
case 4: return audio_channel_layout::quadraphonic;
|
||||
case 5: return audio_channel_layout::quadraphonic_lfe;
|
||||
case 6: return audio_channel_layout::surround_5_1;
|
||||
case 7: return audio_channel_layout::surround_5_1;
|
||||
case 8: return audio_channel_layout::surround_7_1;
|
||||
default: return audio_channel_layout::stereo;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioBackend::setup_channel_layout(u32 input_channel_count, u32 output_channel_count, audio_channel_layout layout, logs::channel& log)
|
||||
{
|
||||
const u32 channels = std::min(input_channel_count, output_channel_count);
|
||||
|
||||
if (layout != audio_channel_layout::automatic && output_channel_count > input_channel_count)
|
||||
{
|
||||
log.warning("Mixing from %d to %d channels is not implemented. Falling back to automatic layout.", input_channel_count, output_channel_count);
|
||||
layout = audio_channel_layout::automatic;
|
||||
}
|
||||
|
||||
if (layout != audio_channel_layout::automatic && channels < default_layout_channel_count(layout))
|
||||
{
|
||||
log.warning("Can't use layout %s with %d channels. Falling back to automatic layout.", layout, channels);
|
||||
layout = audio_channel_layout::automatic;
|
||||
}
|
||||
|
||||
m_layout = layout == audio_channel_layout::automatic ? default_layout(channels) : layout;
|
||||
m_channels = layout_channel_count(channels, m_layout);
|
||||
}
|
||||
|
||||
+156
-45
@@ -1,8 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "util/types.hpp"
|
||||
#include "util/logs.hpp"
|
||||
#include "Utilities/mutex.h"
|
||||
#include "Utilities/StrFmt.h"
|
||||
#include "Emu/system_config_types.h"
|
||||
#include <numbers>
|
||||
|
||||
enum : u32
|
||||
@@ -30,6 +32,7 @@ enum class AudioSampleSize : u32
|
||||
S16 = sizeof(s16),
|
||||
};
|
||||
|
||||
// This enum is only used for emulation
|
||||
enum class AudioChannelCnt : u32
|
||||
{
|
||||
STEREO = 2,
|
||||
@@ -69,7 +72,7 @@ public:
|
||||
// If dev_id is empty, then default device will be selected.
|
||||
// May override channel count if device has smaller number of channels.
|
||||
// Should return 'true' on success.
|
||||
virtual bool Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt) = 0;
|
||||
virtual bool Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt, audio_channel_layout layout) = 0;
|
||||
|
||||
// Reset backend state. Blocks until data callback returns.
|
||||
virtual void Close() = 0;
|
||||
@@ -122,6 +125,7 @@ public:
|
||||
u32 get_sample_size() const;
|
||||
|
||||
u32 get_channels() const;
|
||||
audio_channel_layout get_channel_layout() const;
|
||||
|
||||
bool get_convert_to_s16() const;
|
||||
|
||||
@@ -158,51 +162,91 @@ public:
|
||||
*/
|
||||
static AudioChannelCnt get_max_channel_count(u32 device_index);
|
||||
|
||||
/*
|
||||
* Get default channel count for a layout
|
||||
*/
|
||||
static u32 default_layout_channel_count(audio_channel_layout layout);
|
||||
|
||||
/*
|
||||
* Converts raw channel count to value usable by backends
|
||||
*/
|
||||
static AudioChannelCnt convert_channel_count(u64 raw);
|
||||
static u32 layout_channel_count(u32 channels, audio_channel_layout layout);
|
||||
|
||||
/*
|
||||
* Get the default layout for raw channel count
|
||||
*/
|
||||
static audio_channel_layout default_layout(u32 channels);
|
||||
|
||||
/*
|
||||
* Downmix audio stream.
|
||||
*/
|
||||
template<AudioChannelCnt from, AudioChannelCnt to>
|
||||
template<AudioChannelCnt src_ch_cnt, audio_channel_layout dst_layout>
|
||||
static void downmix(u32 sample_cnt, const f32* src, f32* dst)
|
||||
{
|
||||
static_assert(from == AudioChannelCnt::SURROUND_5_1 || from == AudioChannelCnt::SURROUND_7_1, "Cannot downmix FROM channel count");
|
||||
static_assert(static_cast<u32>(from) > static_cast<u32>(to), "FROM channel count must be bigger than TO");
|
||||
const u32 dst_ch_cnt = default_layout_channel_count(dst_layout);
|
||||
if (static_cast<u32>(src_ch_cnt) <= dst_ch_cnt) fmt::throw_exception("src channel count must be bigger than dst channel count");
|
||||
|
||||
static constexpr f32 center_coef = std::numbers::sqrt2_v<f32> / 2;
|
||||
static constexpr f32 surround_coef = std::numbers::sqrt2_v<f32> / 2;
|
||||
|
||||
for (u32 src_sample = 0, dst_sample = 0; src_sample < sample_cnt; src_sample += static_cast<u32>(from), dst_sample += static_cast<u32>(to))
|
||||
for (u32 src_sample = 0, dst_sample = 0; src_sample < sample_cnt; src_sample += static_cast<u32>(src_ch_cnt), dst_sample += dst_ch_cnt)
|
||||
{
|
||||
const f32 left = src[src_sample + 0];
|
||||
const f32 right = src[src_sample + 1];
|
||||
const f32 center = src[src_sample + 2];
|
||||
const f32 low_freq = src[src_sample + 3];
|
||||
|
||||
if constexpr (from == AudioChannelCnt::SURROUND_5_1)
|
||||
const f32 left = src[src_sample + 0];
|
||||
const f32 right = src[src_sample + 1];
|
||||
|
||||
if constexpr (src_ch_cnt == AudioChannelCnt::STEREO)
|
||||
{
|
||||
static_assert(to == AudioChannelCnt::STEREO, "Invalid TO channel count");
|
||||
|
||||
if constexpr (dst_layout == audio_channel_layout::mono)
|
||||
{
|
||||
dst[dst_sample + 0] = left + right;
|
||||
}
|
||||
}
|
||||
else if constexpr (src_ch_cnt == AudioChannelCnt::SURROUND_5_1)
|
||||
{
|
||||
const f32 center = src[src_sample + 2];
|
||||
const f32 low_freq = src[src_sample + 3];
|
||||
const f32 side_left = src[src_sample + 4];
|
||||
const f32 side_right = src[src_sample + 5];
|
||||
|
||||
const f32 mid = center * center_coef;
|
||||
dst[dst_sample + 0] = left + mid + side_left * surround_coef;
|
||||
dst[dst_sample + 1] = right + mid + side_right * surround_coef;
|
||||
}
|
||||
else if constexpr (from == AudioChannelCnt::SURROUND_7_1)
|
||||
{
|
||||
static_assert(to == AudioChannelCnt::STEREO || to == AudioChannelCnt::SURROUND_5_1, "Invalid TO channel count");
|
||||
if constexpr (dst_layout == audio_channel_layout::quadraphonic || dst_layout == audio_channel_layout::quadraphonic_lfe)
|
||||
{
|
||||
const f32 mid = center * center_coef;
|
||||
dst[dst_sample + 0] = left + mid;
|
||||
dst[dst_sample + 1] = right + mid;
|
||||
dst[dst_sample + 2] = side_left;
|
||||
dst[dst_sample + 3] = side_right;
|
||||
|
||||
if constexpr (dst_layout == audio_channel_layout::quadraphonic_lfe)
|
||||
{
|
||||
dst[dst_sample + 4] = low_freq;
|
||||
}
|
||||
}
|
||||
else if constexpr (dst_layout == audio_channel_layout::stereo || dst_layout == audio_channel_layout::stereo_lfe)
|
||||
{
|
||||
const f32 mid = center * center_coef;
|
||||
dst[dst_sample + 0] = left + mid + side_left * surround_coef;
|
||||
dst[dst_sample + 1] = right + mid + side_right * surround_coef;
|
||||
|
||||
if constexpr (dst_layout == audio_channel_layout::stereo_lfe)
|
||||
{
|
||||
dst[dst_sample + 2] = low_freq;
|
||||
}
|
||||
}
|
||||
else if constexpr (dst_layout == audio_channel_layout::mono)
|
||||
{
|
||||
dst[dst_sample + 0] = left + right + center + side_left + side_right;
|
||||
}
|
||||
}
|
||||
else if constexpr (src_ch_cnt == AudioChannelCnt::SURROUND_7_1)
|
||||
{
|
||||
const f32 center = src[src_sample + 2];
|
||||
const f32 low_freq = src[src_sample + 3];
|
||||
const f32 rear_left = src[src_sample + 4];
|
||||
const f32 rear_right = src[src_sample + 5];
|
||||
const f32 side_left = src[src_sample + 6];
|
||||
const f32 side_right = src[src_sample + 7];
|
||||
|
||||
if constexpr (to == AudioChannelCnt::SURROUND_5_1)
|
||||
if constexpr (dst_layout == audio_channel_layout::surround_5_1)
|
||||
{
|
||||
dst[dst_sample + 0] = left;
|
||||
dst[dst_sample + 1] = right;
|
||||
@@ -211,59 +255,126 @@ public:
|
||||
dst[dst_sample + 4] = side_left + rear_left;
|
||||
dst[dst_sample + 5] = side_right + rear_right;
|
||||
}
|
||||
else
|
||||
else if constexpr (dst_layout == audio_channel_layout::quadraphonic || dst_layout == audio_channel_layout::quadraphonic_lfe)
|
||||
{
|
||||
const f32 mid = center * center_coef;
|
||||
dst[dst_sample + 0] = left + mid;
|
||||
dst[dst_sample + 1] = right + mid;
|
||||
dst[dst_sample + 2] = side_left + rear_left;
|
||||
dst[dst_sample + 3] = side_right + rear_right;
|
||||
|
||||
if constexpr (dst_layout == audio_channel_layout::quadraphonic_lfe)
|
||||
{
|
||||
dst[dst_sample + 4] = low_freq;
|
||||
}
|
||||
}
|
||||
else if constexpr (dst_layout == audio_channel_layout::stereo || dst_layout == audio_channel_layout::stereo_lfe)
|
||||
{
|
||||
const f32 mid = center * center_coef;
|
||||
dst[dst_sample + 0] = left + mid + (side_left + rear_left) * surround_coef;
|
||||
dst[dst_sample + 1] = right + mid + (side_right + rear_right) * surround_coef;
|
||||
|
||||
if constexpr (dst_layout == audio_channel_layout::stereo_lfe)
|
||||
{
|
||||
dst[dst_sample + 2] = low_freq;
|
||||
}
|
||||
}
|
||||
else if constexpr (dst_layout == audio_channel_layout::mono)
|
||||
{
|
||||
dst[dst_sample + 0] = left + right + center + side_left + rear_left + side_right + rear_right;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void downmix(u32 sample_cnt, u32 src_ch_cnt, u32 dst_ch_cnt, const f32* src, f32* dst)
|
||||
static void downmix(u32 sample_cnt, u32 src_ch_cnt, audio_channel_layout dst_layout, const f32* src, f32* dst)
|
||||
{
|
||||
const u32 dst_ch_cnt = default_layout_channel_count(dst_layout);
|
||||
|
||||
if (src_ch_cnt <= dst_ch_cnt)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (src_ch_cnt == static_cast<u32>(AudioChannelCnt::SURROUND_7_1))
|
||||
switch (src_ch_cnt)
|
||||
{
|
||||
if (dst_ch_cnt == static_cast<u32>(AudioChannelCnt::SURROUND_5_1))
|
||||
case static_cast<u32>(AudioChannelCnt::SURROUND_7_1):
|
||||
{
|
||||
switch (dst_layout)
|
||||
{
|
||||
AudioBackend::downmix<AudioChannelCnt::SURROUND_7_1, AudioChannelCnt::SURROUND_5_1>(sample_cnt, src, dst);
|
||||
}
|
||||
else if (dst_ch_cnt == static_cast<u32>(AudioChannelCnt::STEREO))
|
||||
{
|
||||
AudioBackend::downmix<AudioChannelCnt::SURROUND_7_1, AudioChannelCnt::STEREO>(sample_cnt, src, dst);
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::throw_exception("Invalid downmix combination: %u -> %u", src_ch_cnt, dst_ch_cnt);
|
||||
case audio_channel_layout::mono:
|
||||
AudioBackend::downmix<AudioChannelCnt::SURROUND_7_1, audio_channel_layout::mono>(sample_cnt, src, dst);
|
||||
break;
|
||||
case audio_channel_layout::stereo:
|
||||
AudioBackend::downmix<AudioChannelCnt::SURROUND_7_1, audio_channel_layout::stereo>(sample_cnt, src, dst);
|
||||
break;
|
||||
case audio_channel_layout::stereo_lfe:
|
||||
AudioBackend::downmix<AudioChannelCnt::SURROUND_7_1, audio_channel_layout::stereo_lfe>(sample_cnt, src, dst);
|
||||
break;
|
||||
case audio_channel_layout::quadraphonic:
|
||||
AudioBackend::downmix<AudioChannelCnt::SURROUND_7_1, audio_channel_layout::quadraphonic>(sample_cnt, src, dst);
|
||||
break;
|
||||
case audio_channel_layout::quadraphonic_lfe:
|
||||
AudioBackend::downmix<AudioChannelCnt::SURROUND_7_1, audio_channel_layout::quadraphonic_lfe>(sample_cnt, src, dst);
|
||||
break;
|
||||
case audio_channel_layout::surround_5_1:
|
||||
AudioBackend::downmix<AudioChannelCnt::SURROUND_7_1, audio_channel_layout::surround_5_1>(sample_cnt, src, dst);
|
||||
break;
|
||||
default:
|
||||
fmt::throw_exception("Invalid downmix combination: %u -> %s", src_ch_cnt, dst_layout);
|
||||
}
|
||||
break;
|
||||
}
|
||||
else if (src_ch_cnt == static_cast<u32>(AudioChannelCnt::SURROUND_5_1))
|
||||
case static_cast<u32>(AudioChannelCnt::SURROUND_5_1):
|
||||
{
|
||||
if (dst_ch_cnt == static_cast<u32>(AudioChannelCnt::STEREO))
|
||||
switch (dst_layout)
|
||||
{
|
||||
AudioBackend::downmix<AudioChannelCnt::SURROUND_5_1, AudioChannelCnt::STEREO>(sample_cnt, src, dst);
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::throw_exception("Invalid downmix combination: %u -> %u", src_ch_cnt, dst_ch_cnt);
|
||||
case audio_channel_layout::mono:
|
||||
AudioBackend::downmix<AudioChannelCnt::SURROUND_5_1, audio_channel_layout::mono>(sample_cnt, src, dst);
|
||||
break;
|
||||
case audio_channel_layout::stereo:
|
||||
AudioBackend::downmix<AudioChannelCnt::SURROUND_5_1, audio_channel_layout::stereo>(sample_cnt, src, dst);
|
||||
break;
|
||||
case audio_channel_layout::stereo_lfe:
|
||||
AudioBackend::downmix<AudioChannelCnt::SURROUND_5_1, audio_channel_layout::stereo_lfe>(sample_cnt, src, dst);
|
||||
break;
|
||||
case audio_channel_layout::quadraphonic:
|
||||
AudioBackend::downmix<AudioChannelCnt::SURROUND_5_1, audio_channel_layout::quadraphonic>(sample_cnt, src, dst);
|
||||
break;
|
||||
case audio_channel_layout::quadraphonic_lfe:
|
||||
AudioBackend::downmix<AudioChannelCnt::SURROUND_5_1, audio_channel_layout::quadraphonic_lfe>(sample_cnt, src, dst);
|
||||
break;
|
||||
default:
|
||||
fmt::throw_exception("Invalid downmix combination: %u -> %s", src_ch_cnt, dst_layout);
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
case static_cast<u32>(AudioChannelCnt::STEREO):
|
||||
{
|
||||
fmt::throw_exception("Invalid downmix combination: %u -> %u", src_ch_cnt, dst_ch_cnt);
|
||||
switch (dst_layout)
|
||||
{
|
||||
case audio_channel_layout::mono:
|
||||
AudioBackend::downmix<AudioChannelCnt::STEREO, audio_channel_layout::mono>(sample_cnt, src, dst);
|
||||
break;
|
||||
default:
|
||||
fmt::throw_exception("Invalid downmix combination: %u -> %s", src_ch_cnt, dst_layout);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
fmt::throw_exception("Invalid downmix combination: %u -> %s", src_ch_cnt, dst_layout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
void setup_channel_layout(u32 input_channel_count, u32 output_channel_count, audio_channel_layout layout, logs::channel& log);
|
||||
|
||||
AudioSampleSize m_sample_size = AudioSampleSize::FLOAT;
|
||||
AudioFreq m_sampling_rate = AudioFreq::FREQ_48K;
|
||||
AudioChannelCnt m_channels = AudioChannelCnt::STEREO;
|
||||
u32 m_channels = 2;
|
||||
audio_channel_layout m_layout = audio_channel_layout::automatic;
|
||||
|
||||
std::timed_mutex m_cb_mutex{};
|
||||
std::function<u32(u32, void *)> m_write_callback{};
|
||||
|
||||
@@ -96,7 +96,7 @@ bool CubebBackend::DefaultDeviceChanged()
|
||||
return !device.handle || device.id != m_default_device;
|
||||
}
|
||||
|
||||
bool CubebBackend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt)
|
||||
bool CubebBackend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt, audio_channel_layout layout)
|
||||
{
|
||||
if (!Initialized())
|
||||
{
|
||||
@@ -118,7 +118,7 @@ bool CubebBackend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize
|
||||
{
|
||||
if (use_default_device)
|
||||
{
|
||||
device = GetDefaultDeviceAlt(freq, sample_size, ch_cnt);
|
||||
device = GetDefaultDeviceAlt(freq, sample_size, static_cast<u32>(ch_cnt));
|
||||
|
||||
if (!device.handle)
|
||||
{
|
||||
@@ -138,6 +138,8 @@ bool CubebBackend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize
|
||||
device.ch_cnt = 2;
|
||||
}
|
||||
|
||||
Cubeb.notice("Channel count is %d", device.ch_cnt);
|
||||
|
||||
if (use_default_device)
|
||||
{
|
||||
std::lock_guard lock{m_state_cb_mutex};
|
||||
@@ -146,7 +148,9 @@ bool CubebBackend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize
|
||||
|
||||
m_sampling_rate = freq;
|
||||
m_sample_size = sample_size;
|
||||
m_channels = static_cast<AudioChannelCnt>(std::min(static_cast<u32>(convert_channel_count(device.ch_cnt)), static_cast<u32>(ch_cnt)));
|
||||
|
||||
setup_channel_layout(static_cast<u32>(ch_cnt), device.ch_cnt, layout, Cubeb);
|
||||
|
||||
full_sample_size = get_channels() * get_sample_size();
|
||||
|
||||
cubeb_stream_params stream_param{};
|
||||
@@ -155,14 +159,19 @@ bool CubebBackend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize
|
||||
stream_param.channels = get_channels();
|
||||
stream_param.layout = [&]()
|
||||
{
|
||||
switch (m_channels)
|
||||
switch (m_layout)
|
||||
{
|
||||
case AudioChannelCnt::STEREO: return CUBEB_LAYOUT_STEREO;
|
||||
case AudioChannelCnt::SURROUND_5_1: return CUBEB_LAYOUT_3F2_LFE;
|
||||
case AudioChannelCnt::SURROUND_7_1: return CUBEB_LAYOUT_3F4_LFE;
|
||||
default:
|
||||
fmt::throw_exception("Invalid audio channel count");
|
||||
case audio_channel_layout::automatic: break;
|
||||
case audio_channel_layout::mono: return CUBEB_LAYOUT_MONO;
|
||||
case audio_channel_layout::stereo: return CUBEB_LAYOUT_STEREO;
|
||||
case audio_channel_layout::stereo_lfe: return CUBEB_LAYOUT_STEREO_LFE;
|
||||
case audio_channel_layout::quadraphonic: return CUBEB_LAYOUT_QUAD;
|
||||
case audio_channel_layout::quadraphonic_lfe: return CUBEB_LAYOUT_QUAD_LFE;
|
||||
case audio_channel_layout::surround_5_1: return CUBEB_LAYOUT_3F2_LFE;
|
||||
case audio_channel_layout::surround_7_1: return CUBEB_LAYOUT_3F4_LFE;
|
||||
}
|
||||
|
||||
fmt::throw_exception("Invalid audio layout %d", static_cast<u32>(m_layout));
|
||||
}();
|
||||
stream_param.prefs = m_dev_collection_cb_enabled && device.handle ? CUBEB_STREAM_PREF_DISABLE_DEVICE_SWITCHING : CUBEB_STREAM_PREF_NONE;
|
||||
|
||||
@@ -346,7 +355,7 @@ CubebBackend::device_handle CubebBackend::GetDevice(std::string_view dev_id)
|
||||
return result;
|
||||
};
|
||||
|
||||
CubebBackend::device_handle CubebBackend::GetDefaultDeviceAlt(AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt)
|
||||
CubebBackend::device_handle CubebBackend::GetDefaultDeviceAlt(AudioFreq freq, AudioSampleSize sample_size, u32 ch_cnt)
|
||||
{
|
||||
Cubeb.notice("Starting alternative search for default device with freq=%d, sample_size=%d and ch_cnt=%d", static_cast<u32>(freq), static_cast<u32>(sample_size), static_cast<u32>(ch_cnt));
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ public:
|
||||
bool Operational() override;
|
||||
bool DefaultDeviceChanged() override;
|
||||
|
||||
bool Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt) override;
|
||||
bool Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt, audio_channel_layout layout) override;
|
||||
void Close() override;
|
||||
|
||||
f64 GetCallbackFrameLen() override;
|
||||
@@ -62,5 +62,5 @@ private:
|
||||
};
|
||||
|
||||
device_handle GetDevice(std::string_view dev_id = "");
|
||||
device_handle GetDefaultDeviceAlt(AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt);
|
||||
device_handle GetDefaultDeviceAlt(AudioFreq freq, AudioSampleSize sample_size, u32 ch_cnt);
|
||||
};
|
||||
|
||||
@@ -122,7 +122,7 @@ bool FAudioBackend::Operational()
|
||||
return m_source_voice != nullptr && !m_reset_req.observe();
|
||||
}
|
||||
|
||||
bool FAudioBackend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt)
|
||||
bool FAudioBackend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt, audio_channel_layout layout)
|
||||
{
|
||||
if (!Initialized())
|
||||
{
|
||||
@@ -161,9 +161,12 @@ bool FAudioBackend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSiz
|
||||
return false;
|
||||
}
|
||||
|
||||
FAudio_.notice("Channel count is %d", vd.InputChannels);
|
||||
|
||||
m_sampling_rate = freq;
|
||||
m_sample_size = sample_size;
|
||||
m_channels = static_cast<AudioChannelCnt>(std::min(static_cast<u32>(convert_channel_count(vd.InputChannels)), static_cast<u32>(ch_cnt)));;
|
||||
|
||||
setup_channel_layout(static_cast<u32>(ch_cnt), vd.InputChannels, layout, FAudio_);
|
||||
|
||||
FAudioWaveFormatEx waveformatex;
|
||||
waveformatex.wFormatTag = get_convert_to_s16() ? FAUDIO_FORMAT_PCM : FAUDIO_FORMAT_IEEE_FLOAT;
|
||||
|
||||
@@ -24,7 +24,7 @@ public:
|
||||
bool Initialized() override;
|
||||
bool Operational() override;
|
||||
|
||||
bool Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt) override;
|
||||
bool Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt, audio_channel_layout layout) override;
|
||||
void Close() override;
|
||||
|
||||
f64 GetCallbackFrameLen() override;
|
||||
|
||||
@@ -10,7 +10,7 @@ public:
|
||||
|
||||
std::string_view GetName() const override { return "Null"sv; }
|
||||
|
||||
bool Open(std::string_view /* dev_id */, AudioFreq /* freq */, AudioSampleSize /* sample_size */, AudioChannelCnt /* ch_cnt */) override
|
||||
bool Open(std::string_view /* dev_id */, AudioFreq /* freq */, AudioSampleSize /* sample_size */, AudioChannelCnt /* ch_cnt */, audio_channel_layout /*layout*/) override
|
||||
{
|
||||
Close();
|
||||
return true;
|
||||
|
||||
@@ -198,7 +198,7 @@ void XAudio2Backend::Pause()
|
||||
}
|
||||
}
|
||||
|
||||
bool XAudio2Backend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt)
|
||||
bool XAudio2Backend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt, audio_channel_layout layout)
|
||||
{
|
||||
if (!Initialized())
|
||||
{
|
||||
@@ -254,9 +254,12 @@ bool XAudio2Backend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSi
|
||||
return false;
|
||||
}
|
||||
|
||||
XAudio.notice("Channel count is %d", vd.InputChannels);
|
||||
|
||||
m_sampling_rate = freq;
|
||||
m_sample_size = sample_size;
|
||||
m_channels = static_cast<AudioChannelCnt>(std::min(static_cast<u32>(convert_channel_count(vd.InputChannels)), static_cast<u32>(ch_cnt)));
|
||||
|
||||
setup_channel_layout(static_cast<u32>(ch_cnt), vd.InputChannels, layout, XAudio);
|
||||
|
||||
WAVEFORMATEX waveformatex{};
|
||||
waveformatex.wFormatTag = get_convert_to_s16() ? WAVE_FORMAT_PCM : WAVE_FORMAT_IEEE_FLOAT;
|
||||
|
||||
@@ -28,7 +28,7 @@ public:
|
||||
bool Operational() override;
|
||||
bool DefaultDeviceChanged() override;
|
||||
|
||||
bool Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt) override;
|
||||
bool Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt, audio_channel_layout layout) override;
|
||||
void Close() override;
|
||||
|
||||
f64 GetCallbackFrameLen() override;
|
||||
|
||||
+68
-57
@@ -42,6 +42,7 @@ target_include_directories(rpcs3_emu
|
||||
# Utilities
|
||||
target_sources(rpcs3_emu PRIVATE
|
||||
../util/atomic.cpp
|
||||
../util/console.cpp
|
||||
../util/media_utils.cpp
|
||||
../util/video_provider.cpp
|
||||
../util/logs.cpp
|
||||
@@ -148,7 +149,7 @@ if(WIN32)
|
||||
Audio/XAudio2/XAudio2Backend.cpp
|
||||
Audio/XAudio2/xaudio2_enumerator.cpp
|
||||
)
|
||||
target_compile_definitions(rpcs3_emu PRIVATE UNICODE _UNICODE _WIN32_WINNT=0x0602)
|
||||
target_compile_definitions(rpcs3_emu PRIVATE UNICODE _UNICODE _WIN32_WINNT=0x0A00)
|
||||
endif()
|
||||
|
||||
target_link_libraries(rpcs3_emu
|
||||
@@ -394,26 +395,28 @@ target_link_libraries(rpcs3_emu
|
||||
|
||||
# Io
|
||||
target_sources(rpcs3_emu PRIVATE
|
||||
Io/Buzz.cpp
|
||||
Io/camera_config.cpp
|
||||
Io/recording_config.cpp
|
||||
Io/GameTablet.cpp
|
||||
Io/GHLtar.cpp
|
||||
Io/GunCon3.cpp
|
||||
Io/Infinity.cpp
|
||||
Io/interception.cpp
|
||||
Io/KeyboardHandler.cpp
|
||||
Io/midi_config_types.cpp
|
||||
Io/pad_config.cpp
|
||||
Io/pad_config_types.cpp
|
||||
Io/PadHandler.cpp
|
||||
Io/pad_types.cpp
|
||||
Io/PadHandler.cpp
|
||||
Io/rb3drums_config.cpp
|
||||
Io/RB3MidiDrums.cpp
|
||||
Io/RB3MidiGuitar.cpp
|
||||
Io/RB3MidiKeyboard.cpp
|
||||
Io/recording_config.cpp
|
||||
Io/Skylander.cpp
|
||||
Io/Turntable.cpp
|
||||
Io/usb_device.cpp
|
||||
Io/usb_vfs.cpp
|
||||
Io/Infinity.cpp
|
||||
Io/Skylander.cpp
|
||||
Io/GHLtar.cpp
|
||||
Io/midi_config_types.cpp
|
||||
Io/RB3MidiKeyboard.cpp
|
||||
Io/RB3MidiGuitar.cpp
|
||||
Io/RB3MidiDrums.cpp
|
||||
Io/rb3drums_config.cpp
|
||||
Io/Buzz.cpp
|
||||
Io/Turntable.cpp
|
||||
Io/usio.cpp
|
||||
)
|
||||
|
||||
@@ -446,24 +449,50 @@ target_sources(rpcs3_emu PRIVATE
|
||||
|
||||
# RSX
|
||||
target_sources(rpcs3_emu PRIVATE
|
||||
RSX/gcm_enums.cpp
|
||||
RSX/gcm_printing.cpp
|
||||
RSX/GSRender.cpp
|
||||
RSX/RSXFIFO.cpp
|
||||
RSX/rsx_methods.cpp
|
||||
RSX/rsx_vertex_data.cpp
|
||||
RSX/RSXOffload.cpp
|
||||
RSX/RSXTexture.cpp
|
||||
RSX/RSXThread.cpp
|
||||
RSX/RSXZCULL.cpp
|
||||
RSX/rsx_utils.cpp
|
||||
RSX/RSXDisAsm.cpp
|
||||
RSX/Capture/rsx_capture.cpp
|
||||
RSX/Capture/rsx_replay.cpp
|
||||
RSX/Common/BufferUtils.cpp
|
||||
RSX/Common/surface_store.cpp
|
||||
RSX/Common/TextureUtils.cpp
|
||||
RSX/Common/texture_cache.cpp
|
||||
RSX/Core/RSXContext.cpp
|
||||
RSX/gcm_enums.cpp
|
||||
RSX/gcm_printing.cpp
|
||||
RSX/GL/GLCommonDecompiler.cpp
|
||||
RSX/GL/GLCompute.cpp
|
||||
RSX/GL/GLDraw.cpp
|
||||
RSX/GL/GLFragmentProgram.cpp
|
||||
RSX/GL/GLGSRender.cpp
|
||||
RSX/GL/GLHelpers.cpp
|
||||
RSX/GL/GLOverlays.cpp
|
||||
RSX/GL/GLPipelineCompiler.cpp
|
||||
RSX/GL/GLPresent.cpp
|
||||
RSX/GL/GLRenderTargets.cpp
|
||||
RSX/GL/GLShaderInterpreter.cpp
|
||||
RSX/GL/GLTexture.cpp
|
||||
RSX/GL/GLTextureCache.cpp
|
||||
RSX/GL/glutils/blitter.cpp
|
||||
RSX/GL/glutils/buffer_object.cpp
|
||||
RSX/GL/glutils/capabilities.cpp
|
||||
RSX/GL/glutils/common.cpp
|
||||
RSX/GL/glutils/fbo.cpp
|
||||
RSX/GL/glutils/image.cpp
|
||||
RSX/GL/glutils/program.cpp
|
||||
RSX/GL/glutils/ring_buffer.cpp
|
||||
RSX/GL/glutils/sampler.cpp
|
||||
RSX/GL/GLVertexBuffers.cpp
|
||||
RSX/GL/GLVertexProgram.cpp
|
||||
RSX/GL/OpenGL.cpp
|
||||
RSX/GL/upscalers/fsr1/fsr_pass.cpp
|
||||
RSX/GSRender.cpp
|
||||
RSX/Null/NullGSRender.cpp
|
||||
RSX/NV47/FW/reg_context.cpp
|
||||
RSX/NV47/HW/common.cpp
|
||||
RSX/NV47/HW/nv0039.cpp
|
||||
RSX/NV47/HW/nv3089.cpp
|
||||
RSX/NV47/HW/nv308a.cpp
|
||||
RSX/NV47/HW/nv406e.cpp
|
||||
RSX/NV47/HW/nv4097.cpp
|
||||
RSX/Overlays/HomeMenu/overlay_home_menu.cpp
|
||||
RSX/Overlays/HomeMenu/overlay_home_menu_components.cpp
|
||||
RSX/Overlays/HomeMenu/overlay_home_menu_main_menu.cpp
|
||||
@@ -472,8 +501,10 @@ target_sources(rpcs3_emu PRIVATE
|
||||
RSX/Overlays/HomeMenu/overlay_home_menu_settings.cpp
|
||||
RSX/Overlays/Network/overlay_recvmessage_dialog.cpp
|
||||
RSX/Overlays/Network/overlay_sendmessage_dialog.cpp
|
||||
RSX/Overlays/overlays.cpp
|
||||
RSX/Overlays/overlay_animated_icon.cpp
|
||||
RSX/Overlays/overlay_animation.cpp
|
||||
RSX/Overlays/overlay_compile_notification.cpp
|
||||
RSX/Overlays/overlay_controls.cpp
|
||||
RSX/Overlays/overlay_cursor.cpp
|
||||
RSX/Overlays/overlay_debug_overlay.cpp
|
||||
@@ -489,48 +520,28 @@ target_sources(rpcs3_emu PRIVATE
|
||||
RSX/Overlays/overlay_perf_metrics.cpp
|
||||
RSX/Overlays/overlay_progress_bar.cpp
|
||||
RSX/Overlays/overlay_save_dialog.cpp
|
||||
RSX/Overlays/overlay_compile_notification.cpp
|
||||
RSX/Overlays/overlay_trophy_notification.cpp
|
||||
RSX/Overlays/overlay_user_list_dialog.cpp
|
||||
RSX/Overlays/overlay_utils.cpp
|
||||
RSX/Overlays/overlays.cpp
|
||||
RSX/Overlays/overlay_trophy_notification.cpp
|
||||
RSX/Overlays/Shaders/shader_loading_dialog.cpp
|
||||
RSX/Overlays/Shaders/shader_loading_dialog_native.cpp
|
||||
RSX/Program/CgBinaryFragmentProgram.cpp
|
||||
RSX/Program/CgBinaryVertexProgram.cpp
|
||||
RSX/Program/FragmentProgramDecompiler.cpp
|
||||
RSX/Program/GLSLCommon.cpp
|
||||
RSX/Program/program_util.cpp
|
||||
RSX/Program/ProgramStateCache.cpp
|
||||
RSX/Program/program_util.cpp
|
||||
RSX/Program/SPIRVCommon.cpp
|
||||
RSX/Program/VertexProgramDecompiler.cpp
|
||||
RSX/Capture/rsx_capture.cpp
|
||||
RSX/Capture/rsx_replay.cpp
|
||||
RSX/GL/glutils/blitter.cpp
|
||||
RSX/GL/glutils/buffer_object.cpp
|
||||
RSX/GL/glutils/capabilities.cpp
|
||||
RSX/GL/glutils/common.cpp
|
||||
RSX/GL/glutils/fbo.cpp
|
||||
RSX/GL/glutils/image.cpp
|
||||
RSX/GL/glutils/program.cpp
|
||||
RSX/GL/glutils/ring_buffer.cpp
|
||||
RSX/GL/glutils/sampler.cpp
|
||||
RSX/GL/upscalers/fsr1/fsr_pass.cpp
|
||||
RSX/GL/GLCommonDecompiler.cpp
|
||||
RSX/GL/GLCompute.cpp
|
||||
RSX/GL/GLDraw.cpp
|
||||
RSX/GL/GLFragmentProgram.cpp
|
||||
RSX/GL/GLGSRender.cpp
|
||||
RSX/GL/GLHelpers.cpp
|
||||
RSX/GL/GLOverlays.cpp
|
||||
RSX/GL/GLPipelineCompiler.cpp
|
||||
RSX/GL/GLPresent.cpp
|
||||
RSX/GL/GLRenderTargets.cpp
|
||||
RSX/GL/GLShaderInterpreter.cpp
|
||||
RSX/GL/GLTexture.cpp
|
||||
RSX/GL/GLVertexBuffers.cpp
|
||||
RSX/GL/GLVertexProgram.cpp
|
||||
RSX/GL/GLTextureCache.cpp
|
||||
RSX/GL/OpenGL.cpp
|
||||
RSX/RSXDisAsm.cpp
|
||||
RSX/RSXFIFO.cpp
|
||||
RSX/RSXOffload.cpp
|
||||
RSX/RSXTexture.cpp
|
||||
RSX/RSXThread.cpp
|
||||
RSX/RSXZCULL.cpp
|
||||
RSX/rsx_methods.cpp
|
||||
RSX/rsx_utils.cpp
|
||||
RSX/rsx_vertex_data.cpp
|
||||
)
|
||||
|
||||
if(TARGET 3rdparty_vulkan)
|
||||
|
||||
+79
-15
@@ -89,6 +89,9 @@ struct cpu_prof
|
||||
// Total number of samples
|
||||
u64 samples = 0, idle = 0;
|
||||
|
||||
// Total number of sample collected in reservation operation
|
||||
u64 reservation_samples = 0;
|
||||
|
||||
// Avoid printing replicas or when not much changed
|
||||
u64 new_samples = 0;
|
||||
|
||||
@@ -101,6 +104,7 @@ struct cpu_prof
|
||||
samples = 0;
|
||||
idle = 0;
|
||||
new_samples = 0;
|
||||
reservation_samples = 0;
|
||||
}
|
||||
|
||||
static std::string format(const std::multimap<u64, u64, std::greater<u64>>& chart, u64 samples, u64 idle, bool extended_print = false)
|
||||
@@ -140,7 +144,7 @@ struct cpu_prof
|
||||
{
|
||||
if (cpu_flag::exit - ptr->state)
|
||||
{
|
||||
profiler.notice("Thread \"%s\" [0x%08x]: %u samples, %u new (%.4f%% idle): Not enough new samples have been collected since the last print.", ptr->get_name(), ptr->id, samples, new_samples, 100. * idle / samples);
|
||||
profiler.notice("Thread \"%s\" [0x%08x]: %u samples (%.4f%% idle), %u new, %u reservation (%.4f%%): Not enough new samples have been collected since the last print.", ptr->get_name(), ptr->id, samples, 100. * idle / samples, new_samples, reservation_samples, 100. * reservation_samples / samples);
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -156,7 +160,7 @@ struct cpu_prof
|
||||
|
||||
// Print results
|
||||
const std::string results = format(chart, samples, idle);
|
||||
profiler.notice("Thread \"%s\" [0x%08x]: %u samples, %u new (%.4f%% idle):%s", ptr->get_name(), ptr->id, samples, new_samples, 100. * idle / samples, results);
|
||||
profiler.notice("Thread \"%s\" [0x%08x]: %u samples (%.4f%% idle), %u new, %u reservation (%.4f%%):\n%s", ptr->get_name(), ptr->id, samples, 100. * idle / samples, new_samples, reservation_samples, 100. * reservation_samples / samples, results);
|
||||
|
||||
new_samples = 0;
|
||||
}
|
||||
@@ -177,6 +181,7 @@ struct cpu_prof
|
||||
std::unordered_map<u64, u64, value_hash<u64>> freq;
|
||||
|
||||
u64 samples = 0, idle = 0;
|
||||
u64 reservation = 0;
|
||||
|
||||
for (auto& [_, info] : threads)
|
||||
{
|
||||
@@ -188,6 +193,7 @@ struct cpu_prof
|
||||
|
||||
samples += info.samples;
|
||||
idle += info.idle;
|
||||
reservation += info.reservation_samples;
|
||||
}
|
||||
|
||||
if (samples == idle)
|
||||
@@ -197,7 +203,7 @@ struct cpu_prof
|
||||
|
||||
if (new_samples < min_print_all_samples && thread_ctrl::state() != thread_state::aborting)
|
||||
{
|
||||
profiler.notice("All Threads: %u samples, %u new (%.4f%% idle): Not enough new samples have been collected since the last print.", samples, new_samples, 100. * idle / samples);
|
||||
profiler.notice("All Threads: %u samples (%.4f%% idle), %u new, %u reservation (%.4f%%): Not enough new samples have been collected since the last print.", samples, 100. * idle / samples, new_samples, reservation, 100. * reservation / samples);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -207,7 +213,7 @@ struct cpu_prof
|
||||
}
|
||||
|
||||
const std::string results = format(chart, samples, idle, true);
|
||||
profiler.notice("All Threads: %u samples, %u new (%.4f%% idle):%s", samples, new_samples, 100. * idle / samples, results);
|
||||
profiler.notice("All Threads: %u samples (%.4f%% idle), %u new, %u reservation (%.4f%%):%s", samples, 100. * idle / samples, new_samples, reservation, 100. * reservation / samples, results);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -281,6 +287,14 @@ struct cpu_prof
|
||||
info.freq[name]++;
|
||||
info.new_samples++;
|
||||
|
||||
if (auto spu = ptr->try_get<spu_thread>())
|
||||
{
|
||||
if (spu->raddr)
|
||||
{
|
||||
info.reservation_samples++;
|
||||
}
|
||||
}
|
||||
|
||||
// Append verification time to fixed common name 0000000...chunk-0x3fffc
|
||||
if (name >> 16 && (name & 0xffff) == 0)
|
||||
info.freq[0xffff]++;
|
||||
@@ -315,6 +329,13 @@ struct cpu_prof
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!g_cfg.core.spu_debug)
|
||||
{
|
||||
// Reduce accuracy in favor of performance when enabled alone
|
||||
thread_ctrl::wait_for(60, false);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Wait, roughly for 20µs
|
||||
thread_ctrl::wait_for(20, false);
|
||||
}
|
||||
@@ -1072,17 +1093,31 @@ void cpu_thread::add_remove_flags(bs_t<cpu_flag> to_add, bs_t<cpu_flag> to_remov
|
||||
std::string cpu_thread::get_name() const
|
||||
{
|
||||
// Downcast to correct type
|
||||
if (id_type() == 1)
|
||||
switch (id_type())
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
return thread_ctrl::get_name(*static_cast<const named_thread<ppu_thread>*>(this));
|
||||
}
|
||||
|
||||
if (id_type() == 2)
|
||||
case 2:
|
||||
{
|
||||
return thread_ctrl::get_name(*static_cast<const named_thread<spu_thread>*>(this));
|
||||
}
|
||||
default:
|
||||
{
|
||||
if (cpu_thread::get_current() == this && thread_ctrl::get_current())
|
||||
{
|
||||
return thread_ctrl::get_name();
|
||||
}
|
||||
|
||||
fmt::throw_exception("Invalid cpu_thread type");
|
||||
if (id_type() == 0x55)
|
||||
{
|
||||
return fmt::format("rsx::thread");
|
||||
}
|
||||
|
||||
return fmt::format("Invalid cpu_thread type (0x%x)", id_type());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u32 cpu_thread::get_pc() const
|
||||
@@ -1411,7 +1446,7 @@ u32 CPUDisAsm::DisAsmBranchTarget(s32 /*imm*/)
|
||||
|
||||
extern bool try_lock_spu_threads_in_a_state_compatible_with_savestates(bool revert_lock)
|
||||
{
|
||||
auto get_spus = [old_counter = u64{umax}, spu_list = std::vector<std::shared_ptr<named_thread<spu_thread>>>()](bool can_collect) mutable
|
||||
auto get_spus = [old_counter = u64{umax}, spu_list = std::vector<std::shared_ptr<named_thread<spu_thread>>>()](bool can_collect, bool force_collect) mutable
|
||||
{
|
||||
const u64 new_counter = cpu_thread::g_threads_created + cpu_thread::g_threads_deleted;
|
||||
|
||||
@@ -1422,14 +1457,34 @@ extern bool try_lock_spu_threads_in_a_state_compatible_with_savestates(bool reve
|
||||
return decltype(&spu_list){};
|
||||
}
|
||||
|
||||
const u64 current = get_system_time();
|
||||
|
||||
// Fetch SPU contexts
|
||||
spu_list.clear();
|
||||
|
||||
idm::select<named_thread<spu_thread>>([&](u32 id, spu_thread&)
|
||||
bool give_up = false;
|
||||
|
||||
idm::select<named_thread<spu_thread>>([&](u32 id, spu_thread& spu)
|
||||
{
|
||||
spu_list.emplace_back(ensure(idm::get_unlocked<named_thread<spu_thread>>(id)));
|
||||
|
||||
if (spu.current_func && spu.unsavable)
|
||||
{
|
||||
const u64 start = spu.start_time;
|
||||
|
||||
// Automatically give up if it is asleep 15 seconds or more
|
||||
if (start && current > start && current - start >= 15'000'000)
|
||||
{
|
||||
give_up = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!force_collect && give_up)
|
||||
{
|
||||
return decltype(&spu_list){};
|
||||
}
|
||||
|
||||
old_counter = new_counter;
|
||||
}
|
||||
|
||||
@@ -1437,7 +1492,7 @@ extern bool try_lock_spu_threads_in_a_state_compatible_with_savestates(bool reve
|
||||
};
|
||||
|
||||
// Attempt to lock for a second, if somehow takes longer abort it
|
||||
for (u64 start = 0, passed_count = 0; passed_count < 10;)
|
||||
for (u64 start = 0, passed_count = 0; passed_count < 15;)
|
||||
{
|
||||
if (revert_lock)
|
||||
{
|
||||
@@ -1449,7 +1504,7 @@ extern bool try_lock_spu_threads_in_a_state_compatible_with_savestates(bool reve
|
||||
{
|
||||
start = get_system_time();
|
||||
}
|
||||
else if (get_system_time() - start >= 100'000)
|
||||
else if (get_system_time() - start >= 150'000)
|
||||
{
|
||||
passed_count++;
|
||||
start = 0;
|
||||
@@ -1457,7 +1512,16 @@ extern bool try_lock_spu_threads_in_a_state_compatible_with_savestates(bool reve
|
||||
}
|
||||
|
||||
// Try to fetch SPUs out of critical section
|
||||
const auto spu_list = get_spus(true);
|
||||
const auto spu_list = get_spus(true, false);
|
||||
|
||||
if (!spu_list)
|
||||
{
|
||||
// Give up for now
|
||||
std::this_thread::sleep_for(10ms);
|
||||
passed_count++;
|
||||
start = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Avoid using suspend_all when more than 2 threads known to be unsavable
|
||||
u32 unsavable_threads = 0;
|
||||
@@ -1486,7 +1550,7 @@ extern bool try_lock_spu_threads_in_a_state_compatible_with_savestates(bool reve
|
||||
|
||||
if (cpu_thread::suspend_all(nullptr, {}, [&]()
|
||||
{
|
||||
if (!get_spus(false))
|
||||
if (!get_spus(false, true))
|
||||
{
|
||||
// Avoid locking IDM here because this is a critical section
|
||||
return true;
|
||||
@@ -1558,7 +1622,7 @@ extern bool try_lock_spu_threads_in_a_state_compatible_with_savestates(bool reve
|
||||
return true;
|
||||
}
|
||||
|
||||
for (auto& spu : *get_spus(true))
|
||||
for (auto& spu : *get_spus(true, true))
|
||||
{
|
||||
if (spu->state.test_and_reset(cpu_flag::dbg_global_pause))
|
||||
{
|
||||
|
||||
@@ -171,7 +171,7 @@ void cpu_translator::initialize(llvm::LLVMContext& context, llvm::ExecutionEngin
|
||||
cpu == "tigerlake" ||
|
||||
cpu == "rocketlake" ||
|
||||
cpu == "sapphirerapids" ||
|
||||
(cpu.startswith("znver") && cpu != "znver1" && cpu != "znver2" && cpu != "znver3"))
|
||||
(cpu.starts_with("znver") && cpu != "znver1" && cpu != "znver2" && cpu != "znver3"))
|
||||
{
|
||||
m_use_avx = true;
|
||||
m_use_fma = true;
|
||||
|
||||
@@ -125,7 +125,7 @@ using get_int_vt = typename get_int_bits<Bits>::utype;
|
||||
template <typename T = void>
|
||||
struct llvm_value_t
|
||||
{
|
||||
static_assert(std::is_same<T, void>::value, "llvm_value_t<> error: unknown type");
|
||||
static_assert(std::is_same_v<T, void>, "llvm_value_t<> error: unknown type");
|
||||
|
||||
using type = void;
|
||||
using base = llvm_value_t;
|
||||
@@ -417,7 +417,7 @@ struct llvm_value_t<f64> : llvm_value_t<void>
|
||||
template <typename T>
|
||||
struct llvm_value_t<T*> : llvm_value_t<T>
|
||||
{
|
||||
static_assert(!std::is_void<T>::value, "llvm_value_t<> error: invalid pointer to void type");
|
||||
static_assert(!std::is_void_v<T>, "llvm_value_t<> error: invalid pointer to void type");
|
||||
|
||||
using type = T*;
|
||||
using base = llvm_value_t<T>;
|
||||
@@ -3703,13 +3703,18 @@ public:
|
||||
return result;
|
||||
}
|
||||
|
||||
llvm::Value* load_const(llvm::GlobalVariable* g, llvm::Value* i, llvm::Type* type = nullptr)
|
||||
{
|
||||
return m_ir->CreateLoad(type ? type : g->getValueType(), m_ir->CreateGEP(g->getValueType(), g, {m_ir->getInt64(0), m_ir->CreateZExtOrTrunc(i, get_type<u64>())}));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
llvm::Value* load_const(llvm::GlobalVariable* g, llvm::Value* i)
|
||||
{
|
||||
return m_ir->CreateLoad(get_type<T>(), m_ir->CreateGEP(g->getValueType(), g, {m_ir->getInt64(0), m_ir->CreateZExtOrTrunc(i, get_type<u64>())}));
|
||||
return load_const(g, i, get_type<T>());
|
||||
}
|
||||
|
||||
template <typename T, typename I>
|
||||
template <typename T, typename I> requires requires () { std::declval<I>().eval(std::declval<llvm::IRBuilder<>*>()); }
|
||||
value_t<T> load_const(llvm::GlobalVariable* g, I i)
|
||||
{
|
||||
value_t<T> result;
|
||||
@@ -3717,6 +3722,12 @@ public:
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
llvm::GlobalVariable* make_local_variable(T initializing_value)
|
||||
{
|
||||
return new llvm::GlobalVariable(*m_module, get_type<T>(), false, llvm::GlobalVariable::PrivateLinkage, llvm::ConstantInt::get(get_type<T>(), initializing_value));
|
||||
}
|
||||
|
||||
template <typename R = v128>
|
||||
std::pair<bool, R> get_const_vector(llvm::Value*, u32 pos, u32 = __builtin_LINE());
|
||||
|
||||
|
||||
@@ -76,11 +76,14 @@ void cell_audio_config::reset(bool backend_changed)
|
||||
const auto& [req_ch_cnt, downmix] = AudioBackend::get_channel_count_and_downmixer(0); // CELL_AUDIO_OUT_PRIMARY
|
||||
f64 cb_frame_len = 0.0;
|
||||
u32 ch_cnt = 2;
|
||||
audio_channel_layout ch_layout = audio_channel_layout::stereo;
|
||||
|
||||
if (backend->Open(raw.audio_device, freq, sample_size, req_ch_cnt))
|
||||
if (backend->Open(raw.audio_device, freq, sample_size, req_ch_cnt, raw.channel_layout))
|
||||
{
|
||||
cb_frame_len = backend->GetCallbackFrameLen();
|
||||
ch_cnt = backend->get_channels();
|
||||
ch_layout = backend->get_channel_layout();
|
||||
cellAudio.notice("Opened audio backend (sampling_rate=%d, sample_size=%d, channels=%d, layout=%s)", backend->get_sampling_rate(), backend->get_sample_size(), backend->get_channels(), backend->get_channel_layout());
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -88,7 +91,8 @@ void cell_audio_config::reset(bool backend_changed)
|
||||
}
|
||||
|
||||
audio_downmix = downmix;
|
||||
backend_ch_cnt = AudioChannelCnt{ch_cnt};
|
||||
backend_ch_cnt = ch_cnt;
|
||||
backend_channel_layout = ch_layout;
|
||||
audio_channels = static_cast<u32>(req_ch_cnt);
|
||||
audio_sampling_rate = static_cast<u32>(freq);
|
||||
audio_block_period = AUDIO_BUFFER_SAMPLES * 1'000'000 / audio_sampling_rate;
|
||||
@@ -162,7 +166,7 @@ audio_ringbuffer::audio_ringbuffer(cell_audio_config& _cfg)
|
||||
return cfg.audio_min_buffer_duration;
|
||||
}();
|
||||
|
||||
cb_ringbuf.set_buf_size(static_cast<u32>(static_cast<u32>(cfg.backend_ch_cnt) * cfg.audio_sampling_rate * cfg.audio_sample_size * buffer_dur_mult));
|
||||
cb_ringbuf.set_buf_size(static_cast<u32>(cfg.backend_ch_cnt * cfg.audio_sampling_rate * cfg.audio_sample_size * buffer_dur_mult));
|
||||
backend->SetWriteCallback(std::bind(&audio_ringbuffer::backend_write_callback, this, std::placeholders::_1, std::placeholders::_2));
|
||||
backend->SetStateCallback(std::bind(&audio_ringbuffer::backend_state_callback, this, std::placeholders::_1));
|
||||
}
|
||||
@@ -219,7 +223,7 @@ float* audio_ringbuffer::get_current_buffer() const
|
||||
u64 audio_ringbuffer::get_enqueued_samples() const
|
||||
{
|
||||
AUDIT(cfg.buffering_enabled);
|
||||
const u64 ringbuf_samples = cb_ringbuf.get_used_size() / (cfg.audio_sample_size * static_cast<u32>(cfg.backend_ch_cnt));
|
||||
const u64 ringbuf_samples = cb_ringbuf.get_used_size() / (cfg.audio_sample_size * cfg.backend_ch_cnt);
|
||||
|
||||
if (cfg.time_stretching_enabled)
|
||||
{
|
||||
@@ -280,14 +284,14 @@ void audio_ringbuffer::process_resampled_data()
|
||||
{
|
||||
if (!cfg.time_stretching_enabled) return;
|
||||
|
||||
const auto& [buffer, samples] = resampler.get_samples(static_cast<u32>(cb_ringbuf.get_free_size() / (cfg.audio_sample_size * static_cast<u32>(cfg.backend_ch_cnt))));
|
||||
const auto& [buffer, samples] = resampler.get_samples(static_cast<u32>(cb_ringbuf.get_free_size() / (cfg.audio_sample_size * cfg.backend_ch_cnt)));
|
||||
commit_data(buffer, samples);
|
||||
}
|
||||
|
||||
void audio_ringbuffer::commit_data(f32* buf, u32 sample_cnt)
|
||||
{
|
||||
const u32 sample_cnt_in = sample_cnt * cfg.audio_channels;
|
||||
const u32 sample_cnt_out = sample_cnt * static_cast<u32>(cfg.backend_ch_cnt);
|
||||
const u32 sample_cnt_out = sample_cnt * cfg.backend_ch_cnt;
|
||||
|
||||
// Dump audio if enabled
|
||||
m_dump.WriteData(buf, sample_cnt_in * static_cast<u32>(AudioSampleSize::FLOAT));
|
||||
@@ -300,7 +304,7 @@ void audio_ringbuffer::commit_data(f32* buf, u32 sample_cnt)
|
||||
}
|
||||
|
||||
// Downmix if necessary
|
||||
AudioBackend::downmix(sample_cnt_in, cfg.audio_channels, static_cast<u32>(cfg.backend_ch_cnt), buf, buf);
|
||||
AudioBackend::downmix(sample_cnt_in, cfg.audio_channels, cfg.backend_channel_layout, buf, buf);
|
||||
|
||||
if (cfg.backend->get_convert_to_s16())
|
||||
{
|
||||
@@ -614,6 +618,7 @@ namespace audio
|
||||
.time_stretching_threshold = g_cfg.audio.time_stretching_threshold,
|
||||
.convert_to_s16 = static_cast<bool>(g_cfg.audio.convert_to_s16),
|
||||
.dump_to_file = static_cast<bool>(g_cfg.audio.dump_to_file),
|
||||
.channel_layout = g_cfg.audio.channel_layout,
|
||||
.renderer = g_cfg.audio.renderer,
|
||||
.provider = g_cfg.audio.provider
|
||||
};
|
||||
|
||||
@@ -212,6 +212,7 @@ struct cell_audio_config
|
||||
s64 time_stretching_threshold = 0;
|
||||
bool convert_to_s16 = false;
|
||||
bool dump_to_file = false;
|
||||
audio_channel_layout channel_layout = audio_channel_layout::automatic;
|
||||
audio_renderer renderer = audio_renderer::null;
|
||||
audio_provider provider = audio_provider::none;
|
||||
};
|
||||
@@ -222,7 +223,8 @@ struct cell_audio_config
|
||||
std::shared_ptr<AudioBackend> backend = nullptr;
|
||||
|
||||
AudioChannelCnt audio_downmix = AudioChannelCnt::SURROUND_7_1;
|
||||
AudioChannelCnt backend_ch_cnt = AudioChannelCnt::SURROUND_7_1;
|
||||
audio_channel_layout backend_channel_layout = audio_channel_layout::surround_7_1;
|
||||
u32 backend_ch_cnt = 8;
|
||||
u32 audio_channels = 2;
|
||||
u32 audio_sampling_rate = DEFAULT_AUDIO_SAMPLING_RATE;
|
||||
u32 audio_block_period = 0;
|
||||
|
||||
@@ -498,7 +498,7 @@ error_code cellCameraEnd()
|
||||
|
||||
error_code cellCameraOpen(s32 dev_num, vm::ptr<CellCameraInfo> info)
|
||||
{
|
||||
cellCamera.todo("cellCameraOpen(dev_num=%d, info=*0x%x)", dev_num, info);
|
||||
cellCamera.notice("cellCameraOpen(dev_num=%d, info=*0x%x)", dev_num, info);
|
||||
|
||||
if (!info)
|
||||
{
|
||||
@@ -639,7 +639,7 @@ error_code cellCameraOpenPost()
|
||||
|
||||
error_code cellCameraClose(s32 dev_num)
|
||||
{
|
||||
cellCamera.todo("cellCameraClose(dev_num=%d)", dev_num);
|
||||
cellCamera.notice("cellCameraClose(dev_num=%d)", dev_num);
|
||||
|
||||
if (error_code error = check_init_and_open(dev_num))
|
||||
{
|
||||
@@ -714,7 +714,7 @@ error_code cellCameraGetDeviceGUID(s32 dev_num, vm::ptr<u32> guid)
|
||||
|
||||
error_code cellCameraGetType(s32 dev_num, vm::ptr<s32> type)
|
||||
{
|
||||
cellCamera.todo("cellCameraGetType(dev_num=%d, type=*0x%x)", dev_num, type);
|
||||
cellCamera.trace("cellCameraGetType(dev_num=%d, type=*0x%x)", dev_num, type);
|
||||
|
||||
auto& g_camera = g_fxo->get<camera_thread>();
|
||||
|
||||
@@ -780,7 +780,7 @@ s32 cellCameraIsAvailable(s32 dev_num)
|
||||
|
||||
s32 cellCameraIsAttached(s32 dev_num)
|
||||
{
|
||||
cellCamera.warning("cellCameraIsAttached(dev_num=%d)", dev_num);
|
||||
cellCamera.trace("cellCameraIsAttached(dev_num=%d)", dev_num);
|
||||
|
||||
if (g_cfg.io.camera == camera_handler::null)
|
||||
{
|
||||
@@ -879,7 +879,7 @@ s32 cellCameraIsStarted(s32 dev_num)
|
||||
error_code cellCameraGetAttribute(s32 dev_num, s32 attrib, vm::ptr<u32> arg1, vm::ptr<u32> arg2)
|
||||
{
|
||||
const auto attr_name = get_camera_attr_name(attrib);
|
||||
cellCamera.todo("cellCameraGetAttribute(dev_num=%d, attrib=%d=%s, arg1=*0x%x, arg2=*0x%x)", dev_num, attrib, attr_name, arg1, arg2);
|
||||
cellCamera.notice("cellCameraGetAttribute(dev_num=%d, attrib=%d=%s, arg1=*0x%x, arg2=*0x%x)", dev_num, attrib, attr_name, arg1, arg2);
|
||||
|
||||
auto& g_camera = g_fxo->get<camera_thread>();
|
||||
|
||||
@@ -991,7 +991,7 @@ error_code cellCameraResetAttribute()
|
||||
|
||||
error_code cellCameraGetBufferSize(s32 dev_num, vm::ptr<CellCameraInfoEx> info)
|
||||
{
|
||||
cellCamera.todo("cellCameraGetBufferSize(dev_num=%d, info=*0x%x)", dev_num, info);
|
||||
cellCamera.notice("cellCameraGetBufferSize(dev_num=%d, info=*0x%x)", dev_num, info);
|
||||
|
||||
auto& g_camera = g_fxo->get<camera_thread>();
|
||||
|
||||
@@ -1085,7 +1085,7 @@ error_code check_get_camera_info(s32 dev_num, bool is_valid_info_struct)
|
||||
|
||||
error_code cellCameraGetBufferInfo(s32 dev_num, vm::ptr<CellCameraInfo> info)
|
||||
{
|
||||
cellCamera.todo("cellCameraGetBufferInfo(dev_num=%d, info=0x%x)", dev_num, info);
|
||||
cellCamera.notice("cellCameraGetBufferInfo(dev_num=%d, info=0x%x)", dev_num, info);
|
||||
|
||||
// called by cellCameraGetBufferInfoEx
|
||||
|
||||
@@ -1112,7 +1112,7 @@ error_code cellCameraGetBufferInfo(s32 dev_num, vm::ptr<CellCameraInfo> info)
|
||||
|
||||
error_code cellCameraGetBufferInfoEx(s32 dev_num, vm::ptr<CellCameraInfoEx> info)
|
||||
{
|
||||
cellCamera.todo("cellCameraGetBufferInfoEx(dev_num=%d, info=0x%x)", dev_num, info);
|
||||
cellCamera.notice("cellCameraGetBufferInfoEx(dev_num=%d, info=0x%x)", dev_num, info);
|
||||
|
||||
// calls cellCameraGetBufferInfo
|
||||
|
||||
@@ -1244,7 +1244,7 @@ error_code cellCameraResetPost()
|
||||
|
||||
error_code cellCameraStart(s32 dev_num)
|
||||
{
|
||||
cellCamera.todo("cellCameraStart(dev_num=%d)", dev_num);
|
||||
cellCamera.notice("cellCameraStart(dev_num=%d)", dev_num);
|
||||
|
||||
if (error_code error = check_init_and_open(dev_num))
|
||||
{
|
||||
@@ -1291,7 +1291,7 @@ error_code cellCameraStartPost()
|
||||
|
||||
error_code cellCameraRead(s32 dev_num, vm::ptr<u32> frame_num, vm::ptr<u32> bytes_read)
|
||||
{
|
||||
cellCamera.notice("cellCameraRead(dev_num=%d, frame_num=*0x%x, bytes_read=*0x%x)", dev_num, frame_num, bytes_read);
|
||||
cellCamera.trace("cellCameraRead(dev_num=%d, frame_num=*0x%x, bytes_read=*0x%x)", dev_num, frame_num, bytes_read);
|
||||
|
||||
vm::ptr<CellCameraReadEx> read_ex = vm::make_var<CellCameraReadEx>({});
|
||||
|
||||
@@ -1321,7 +1321,7 @@ error_code cellCameraRead2()
|
||||
|
||||
error_code cellCameraReadEx(s32 dev_num, vm::ptr<CellCameraReadEx> read)
|
||||
{
|
||||
cellCamera.notice("cellCameraReadEx(dev_num=%d, read=0x%x)", dev_num, read);
|
||||
cellCamera.trace("cellCameraReadEx(dev_num=%d, read=0x%x)", dev_num, read);
|
||||
|
||||
auto& g_camera = g_fxo->get<camera_thread>();
|
||||
|
||||
@@ -1421,7 +1421,7 @@ error_code cellCameraReadComplete(s32 dev_num, u32 bufnum, u32 arg2)
|
||||
|
||||
error_code cellCameraStop(s32 dev_num)
|
||||
{
|
||||
cellCamera.todo("cellCameraStop(dev_num=%d)", dev_num);
|
||||
cellCamera.notice("cellCameraStop(dev_num=%d)", dev_num);
|
||||
|
||||
if (error_code error = check_init_and_open(dev_num))
|
||||
{
|
||||
@@ -1466,7 +1466,7 @@ error_code cellCameraStopPost()
|
||||
|
||||
error_code cellCameraSetNotifyEventQueue(u64 key)
|
||||
{
|
||||
cellCamera.todo("cellCameraSetNotifyEventQueue(key=0x%x)", key);
|
||||
cellCamera.notice("cellCameraSetNotifyEventQueue(key=0x%x)", key);
|
||||
|
||||
auto& g_camera = g_fxo->get<camera_thread>();
|
||||
|
||||
@@ -1492,7 +1492,7 @@ error_code cellCameraSetNotifyEventQueue(u64 key)
|
||||
|
||||
error_code cellCameraRemoveNotifyEventQueue(u64 key)
|
||||
{
|
||||
cellCamera.todo("cellCameraRemoveNotifyEventQueue(key=0x%x)", key);
|
||||
cellCamera.notice("cellCameraRemoveNotifyEventQueue(key=0x%x)", key);
|
||||
|
||||
auto& g_camera = g_fxo->get<camera_thread>();
|
||||
|
||||
@@ -1518,7 +1518,7 @@ error_code cellCameraRemoveNotifyEventQueue(u64 key)
|
||||
|
||||
error_code cellCameraSetNotifyEventQueue2(u64 key, u64 source, u64 flag)
|
||||
{
|
||||
cellCamera.todo("cellCameraSetNotifyEventQueue2(key=0x%x, source=%d, flag=%d)", key, source, flag);
|
||||
cellCamera.notice("cellCameraSetNotifyEventQueue2(key=0x%x, source=%d, flag=%d)", key, source, flag);
|
||||
|
||||
auto& g_camera = g_fxo->get<camera_thread>();
|
||||
|
||||
@@ -1544,7 +1544,7 @@ error_code cellCameraSetNotifyEventQueue2(u64 key, u64 source, u64 flag)
|
||||
|
||||
error_code cellCameraRemoveNotifyEventQueue2(u64 key)
|
||||
{
|
||||
cellCamera.todo("cellCameraRemoveNotifyEventQueue2(key=0x%x)", key);
|
||||
cellCamera.notice("cellCameraRemoveNotifyEventQueue2(key=0x%x)", key);
|
||||
|
||||
return cellCameraRemoveNotifyEventQueue(key);
|
||||
}
|
||||
|
||||
@@ -28,6 +28,10 @@ vm::gvar<CellHddGameStatSet> g_stat_set;
|
||||
vm::gvar<CellHddGameSystemFileParam> g_file_param;
|
||||
vm::gvar<CellHddGameCBResult> g_cb_result;
|
||||
|
||||
stx::init_lock acquire_lock(stx::init_mutex& mtx, ppu_thread* ppu = nullptr);
|
||||
stx::access_lock acquire_access_lock(stx::init_mutex& mtx, ppu_thread* ppu = nullptr);
|
||||
stx::reset_lock acquire_reset_lock(stx::init_mutex& mtx, ppu_thread* ppu = nullptr);
|
||||
|
||||
template<>
|
||||
void fmt_class_string<CellGameError>::format(std::string& out, u64 arg)
|
||||
{
|
||||
@@ -206,6 +210,7 @@ void fmt_class_string<disc_change_manager::eject_state>::format(std::string& out
|
||||
{
|
||||
switch (error)
|
||||
{
|
||||
STR_CASE(disc_change_manager::eject_state::unknown);
|
||||
STR_CASE(disc_change_manager::eject_state::inserted);
|
||||
STR_CASE(disc_change_manager::eject_state::ejected);
|
||||
STR_CASE(disc_change_manager::eject_state::busy);
|
||||
@@ -215,6 +220,22 @@ void fmt_class_string<disc_change_manager::eject_state>::format(std::string& out
|
||||
});
|
||||
}
|
||||
|
||||
static bool check_system_ver(vm::cptr<char> systemVersion)
|
||||
{
|
||||
// Only allow something like "04.8300".
|
||||
// The disassembly shows that "04.83" would also be considered valid, but the initial strlen check makes this void.
|
||||
return (
|
||||
systemVersion &&
|
||||
std::strlen(systemVersion.get_ptr()) == 7 &&
|
||||
std::isdigit(systemVersion[0]) &&
|
||||
std::isdigit(systemVersion[1]) &&
|
||||
systemVersion[2] == '.' &&
|
||||
std::isdigit(systemVersion[3]) &&
|
||||
std::isdigit(systemVersion[4]) &&
|
||||
std::isdigit(systemVersion[5]) &&
|
||||
std::isdigit(systemVersion[6])
|
||||
);
|
||||
}
|
||||
|
||||
disc_change_manager::disc_change_manager()
|
||||
{
|
||||
@@ -235,8 +256,15 @@ error_code disc_change_manager::register_callbacks(vm::ptr<CellGameDiscEjectCall
|
||||
eject_callback = func_eject;
|
||||
insert_callback = func_insert;
|
||||
|
||||
Emu.GetCallbacks().enable_disc_eject(!!func_eject);
|
||||
Emu.GetCallbacks().enable_disc_insert(false);
|
||||
const bool is_disc_mounted = fs::is_dir(vfs::get("/dev_bdvd/PS3_GAME"));
|
||||
|
||||
if (state == eject_state::unknown)
|
||||
{
|
||||
state = is_disc_mounted ? eject_state::inserted : eject_state::ejected;
|
||||
}
|
||||
|
||||
Emu.GetCallbacks().enable_disc_eject(!!func_eject && is_disc_mounted);
|
||||
Emu.GetCallbacks().enable_disc_insert(!!func_insert && !is_disc_mounted);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -296,7 +324,8 @@ void disc_change_manager::eject_disc()
|
||||
ensure(vfs::unmount("/dev_ps2disc"));
|
||||
dcm.state = eject_state::ejected;
|
||||
|
||||
Emu.GetCallbacks().enable_disc_insert(true);
|
||||
// Re-enable disc insertion only if the callback is still registered
|
||||
Emu.GetCallbacks().enable_disc_insert(!!dcm.insert_callback);
|
||||
|
||||
return CELL_OK;
|
||||
});
|
||||
@@ -377,7 +406,7 @@ error_code cellHddGameCheck(ppu_thread& ppu, u32 version, vm::cptr<char> dirName
|
||||
{
|
||||
cellGame.warning("cellHddGameCheck(version=%d, dirName=%s, errDialog=%d, funcStat=*0x%x, container=%d)", version, dirName, errDialog, funcStat, container);
|
||||
|
||||
if (!dirName || !funcStat || sysutil_check_name_string(dirName.get_ptr(), 1, CELL_GAME_DIRNAME_SIZE) != 0)
|
||||
if (version != CELL_GAMEDATA_VERSION_CURRENT || !dirName || !funcStat || sysutil_check_name_string(dirName.get_ptr(), 1, CELL_GAME_DIRNAME_SIZE) != 0)
|
||||
{
|
||||
return CELL_HDDGAME_ERROR_PARAM;
|
||||
}
|
||||
@@ -414,10 +443,10 @@ error_code cellHddGameCheck(ppu_thread& ppu, u32 version, vm::cptr<char> dirName
|
||||
|
||||
const std::string local_dir = vfs::get(dir);
|
||||
|
||||
// 40 GB - 1 kilobyte. The reasoning is that many games take this number and multiply it by 1024, to get the amount of bytes. With 40GB exactly,
|
||||
// this will result in an overflow, and the size would be 0, preventing the game from running. By reducing 1 kilobyte, we make sure that even
|
||||
// 40 GB - 256 kilobytes. The reasoning is that many games take this number and multiply it by 1024, to get the amount of bytes. With 40GB exactly,
|
||||
// this will result in an overflow, and the size would be 0, preventing the game from running. By reducing 256 kilobytes, we make sure that even
|
||||
// after said overflow, the number would still be high enough to contain the game's data.
|
||||
get->hddFreeSizeKB = 40 * 1024 * 1024 - 1;
|
||||
get->hddFreeSizeKB = 40 * 1024 * 1024 - 256;
|
||||
get->isNewData = CELL_HDDGAME_ISNEWDATA_EXIST;
|
||||
get->sysSizeKB = 0; // TODO
|
||||
get->st_atime_ = 0; // TODO
|
||||
@@ -585,6 +614,11 @@ error_code cellHddGameGetSizeKB(ppu_thread& ppu, vm::ptr<u32> size)
|
||||
|
||||
cellGame.warning("cellHddGameGetSizeKB(size=*0x%x)", size);
|
||||
|
||||
if (!size)
|
||||
{
|
||||
return CELL_HDDGAME_ERROR_PARAM;
|
||||
}
|
||||
|
||||
lv2_obj::sleep(ppu);
|
||||
|
||||
const u64 start_sleep = ppu.start_time;
|
||||
@@ -619,7 +653,7 @@ error_code cellHddGameSetSystemVer(vm::cptr<char> systemVersion)
|
||||
{
|
||||
cellGame.todo("cellHddGameSetSystemVer(systemVersion=%s)", systemVersion);
|
||||
|
||||
if (!systemVersion)
|
||||
if (!check_system_ver(systemVersion))
|
||||
{
|
||||
return CELL_HDDGAME_ERROR_PARAM;
|
||||
}
|
||||
@@ -678,7 +712,7 @@ error_code cellGameDataSetSystemVer(vm::cptr<char> systemVersion)
|
||||
{
|
||||
cellGame.todo("cellGameDataSetSystemVer(systemVersion=%s)", systemVersion);
|
||||
|
||||
if (!systemVersion)
|
||||
if (!check_system_ver(systemVersion))
|
||||
{
|
||||
return CELL_GAMEDATA_ERROR_PARAM;
|
||||
}
|
||||
@@ -705,7 +739,7 @@ error_code cellGameBootCheck(vm::ptr<u32> type, vm::ptr<u32> attributes, vm::ptr
|
||||
|
||||
lv2_sleep(500);
|
||||
|
||||
const auto init = perm.init.init();
|
||||
const auto init = acquire_lock(perm.init);
|
||||
|
||||
if (!init)
|
||||
{
|
||||
@@ -754,7 +788,7 @@ error_code cellGameBootCheck(vm::ptr<u32> type, vm::ptr<u32> attributes, vm::ptr
|
||||
if (size)
|
||||
{
|
||||
// TODO: Use the free space of the computer's HDD where RPCS3 is being run.
|
||||
size->hddFreeSizeKB = 40 * 1024 * 1024 - 1; // Read explanation in cellHddGameCheck
|
||||
size->hddFreeSizeKB = 40 * 1024 * 1024 - 256; // Read explanation in cellHddGameCheck
|
||||
|
||||
// TODO: Calculate data size for HG and DG games, if necessary.
|
||||
size->sizeKB = CELL_GAME_SIZEKB_NOTCALC;
|
||||
@@ -789,7 +823,7 @@ error_code cellGamePatchCheck(vm::ptr<CellGameContentSize> size, vm::ptr<void> r
|
||||
|
||||
auto& perm = g_fxo->get<content_permission>();
|
||||
|
||||
const auto init = perm.init.init();
|
||||
const auto init = acquire_lock(perm.init);
|
||||
|
||||
if (!init)
|
||||
{
|
||||
@@ -799,7 +833,7 @@ error_code cellGamePatchCheck(vm::ptr<CellGameContentSize> size, vm::ptr<void> r
|
||||
if (size)
|
||||
{
|
||||
// TODO: Use the free space of the computer's HDD where RPCS3 is being run.
|
||||
size->hddFreeSizeKB = 40 * 1024 * 1024 - 1; // Read explanation in cellHddGameCheck
|
||||
size->hddFreeSizeKB = 40 * 1024 * 1024 - 256; // Read explanation in cellHddGameCheck
|
||||
|
||||
// TODO: Calculate data size for patch data, if necessary.
|
||||
size->sizeKB = CELL_GAME_SIZEKB_NOTCALC;
|
||||
@@ -836,7 +870,7 @@ error_code cellGameDataCheck(u32 type, vm::cptr<char> dirName, vm::ptr<CellGameC
|
||||
|
||||
auto& perm = g_fxo->get<content_permission>();
|
||||
|
||||
auto init = perm.init.init();
|
||||
auto init = acquire_lock(perm.init);
|
||||
|
||||
if (!init)
|
||||
{
|
||||
@@ -871,7 +905,7 @@ error_code cellGameDataCheck(u32 type, vm::cptr<char> dirName, vm::ptr<CellGameC
|
||||
if (size)
|
||||
{
|
||||
// TODO: Use the free space of the computer's HDD where RPCS3 is being run.
|
||||
size->hddFreeSizeKB = 40 * 1024 * 1024 - 1; // Read explanation in cellHddGameCheck
|
||||
size->hddFreeSizeKB = 40 * 1024 * 1024 - 256; // Read explanation in cellHddGameCheck
|
||||
|
||||
// TODO: Calculate data size for game data, if necessary.
|
||||
size->sizeKB = sfo.empty() ? 0 : CELL_GAME_SIZEKB_NOTCALC;
|
||||
@@ -904,7 +938,7 @@ error_code cellGameContentPermit(ppu_thread& ppu, vm::ptr<char[CELL_GAME_PATH_MA
|
||||
|
||||
auto& perm = g_fxo->get<content_permission>();
|
||||
|
||||
const auto init = perm.init.reset();
|
||||
const auto init = acquire_reset_lock(perm.init);
|
||||
|
||||
if (!init)
|
||||
{
|
||||
@@ -1004,7 +1038,7 @@ error_code cellGameDataCheckCreate2(ppu_thread& ppu, u32 version, vm::cptr<char>
|
||||
|
||||
//older sdk. it might not care about game type.
|
||||
|
||||
if (version != CELL_GAMEDATA_VERSION_CURRENT || errDialog > 1 || !funcStat || sysutil_check_name_string(dirName.get_ptr(), 1, CELL_GAME_DIRNAME_SIZE) != 0)
|
||||
if (version != CELL_GAMEDATA_VERSION_CURRENT || !funcStat || !dirName || sysutil_check_name_string(dirName.get_ptr(), 1, CELL_GAME_DIRNAME_SIZE) != 0)
|
||||
{
|
||||
return CELL_GAMEDATA_ERROR_PARAM;
|
||||
}
|
||||
@@ -1038,7 +1072,7 @@ error_code cellGameDataCheckCreate2(ppu_thread& ppu, u32 version, vm::cptr<char>
|
||||
cbGet->isNewData = new_data;
|
||||
|
||||
// TODO: Use the free space of the computer's HDD where RPCS3 is being run.
|
||||
cbGet->hddFreeSizeKB = 40 * 1024 * 1024 - 1; // Read explanation in cellHddGameCheck
|
||||
cbGet->hddFreeSizeKB = 40 * 1024 * 1024 - 256; // Read explanation in cellHddGameCheck
|
||||
|
||||
strcpy_trunc(cbGet->contentInfoPath, dir);
|
||||
strcpy_trunc(cbGet->gameDataPath, usrdir);
|
||||
@@ -1158,7 +1192,7 @@ error_code cellGameDataCheckCreate2(ppu_thread& ppu, u32 version, vm::cptr<char>
|
||||
break;
|
||||
}
|
||||
|
||||
if (errDialog == CELL_GAMEDATA_ERRDIALOG_ALWAYS) // Maybe != CELL_GAMEDATA_ERRDIALOG_NONE
|
||||
if (errDialog == CELL_GAMEDATA_ERRDIALOG_ALWAYS)
|
||||
{
|
||||
// Yield before a blocking dialog is being spawned
|
||||
lv2_obj::sleep(ppu);
|
||||
@@ -1204,7 +1238,7 @@ error_code cellGameCreateGameData(vm::ptr<CellGameSetInitParams> init, vm::ptr<c
|
||||
|
||||
auto& perm = g_fxo->get<content_permission>();
|
||||
|
||||
const auto _init = perm.init.access();
|
||||
const auto _init = acquire_access_lock(perm.init);
|
||||
|
||||
lv2_sleep(2000);
|
||||
|
||||
@@ -1319,7 +1353,7 @@ error_code cellGameDeleteGameData(vm::cptr<char> dirName)
|
||||
if (!_init)
|
||||
{
|
||||
// Or access it
|
||||
if (auto access = perm.init.access(); access)
|
||||
if (auto access = acquire_access_lock(perm.init); access)
|
||||
{
|
||||
// Cannot remove it when it is accessed by cellGameDataCheck
|
||||
// If it is HG data then resort to remove_gd for ERROR_BROKEN
|
||||
@@ -1356,7 +1390,7 @@ error_code cellGameGetParamInt(s32 id, vm::ptr<s32> value)
|
||||
|
||||
auto& perm = g_fxo->get<content_permission>();
|
||||
|
||||
const auto init = perm.init.access();
|
||||
const auto init = acquire_access_lock(perm.init);
|
||||
|
||||
if (!init)
|
||||
{
|
||||
@@ -1484,7 +1518,7 @@ error_code cellGameGetParamString(s32 id, vm::ptr<char> buf, u32 bufsize)
|
||||
|
||||
lv2_sleep(2000);
|
||||
|
||||
const auto init = perm.init.access();
|
||||
const auto init = acquire_access_lock(perm.init);
|
||||
|
||||
if (!init || perm.mode == content_permission::check_mode::not_set)
|
||||
{
|
||||
@@ -1530,7 +1564,7 @@ error_code cellGameSetParamString(s32 id, vm::cptr<char> buf)
|
||||
|
||||
auto& perm = g_fxo->get<content_permission>();
|
||||
|
||||
const auto init = perm.init.access();
|
||||
const auto init = acquire_access_lock(perm.init);
|
||||
|
||||
if (!init || perm.mode == content_permission::check_mode::not_set)
|
||||
{
|
||||
@@ -1569,7 +1603,7 @@ error_code cellGameGetSizeKB(ppu_thread& ppu, vm::ptr<s32> size)
|
||||
|
||||
auto& perm = g_fxo->get<content_permission>();
|
||||
|
||||
const auto init = perm.init.access();
|
||||
const auto init = acquire_access_lock(perm.init);
|
||||
|
||||
if (!init)
|
||||
{
|
||||
|
||||
@@ -338,11 +338,12 @@ struct disc_change_manager
|
||||
|
||||
enum class eject_state
|
||||
{
|
||||
unknown,
|
||||
inserted,
|
||||
ejected,
|
||||
busy
|
||||
};
|
||||
atomic_t<eject_state> state = eject_state::inserted;
|
||||
atomic_t<eject_state> state = eject_state::unknown;
|
||||
|
||||
error_code register_callbacks(vm::ptr<CellGameDiscEjectCallback> func_eject, vm::ptr<CellGameDiscInsertCallback> func_insert);
|
||||
error_code unregister_callbacks();
|
||||
|
||||
@@ -22,6 +22,11 @@
|
||||
|
||||
LOG_CHANNEL(cellGem);
|
||||
|
||||
static inline constexpr u32 rgba(u8 r, u8 g, u8 b, u8 a)
|
||||
{
|
||||
return ((r & 0xffu) << 24) | ((g & 0xffu) << 16) | ((b & 0xffu) << 8) | (a & 0xffu);
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<gem_btn>::format(std::string& out, u64 arg)
|
||||
{
|
||||
@@ -493,42 +498,39 @@ void gem_config_data::operator()()
|
||||
{
|
||||
constexpr u32 in_pitch = 640;
|
||||
constexpr u32 out_pitch = 640 * 4;
|
||||
u8* dst = vc_attribute.video_data_out.get_ptr();
|
||||
|
||||
for (u32 y = 0; y < 480 - 1; y += 2)
|
||||
{
|
||||
const u8* src = &video_data_in[y * in_pitch];
|
||||
const u16* src0 = reinterpret_cast<const u16*>(src);
|
||||
const u16* src1 = reinterpret_cast<const u16*>(src + in_pitch);
|
||||
|
||||
u8* dst_row = dst + y * out_pitch;
|
||||
u32* dst0 = reinterpret_cast<u32*>(dst_row);
|
||||
u32* dst1 = reinterpret_cast<u32*>(dst_row + out_pitch);
|
||||
|
||||
for (u32 x = 0; x < 640 - 1; x += 2)
|
||||
{
|
||||
const u32 in_offset = 1 * (y * 640 + x);
|
||||
const u32 out_offset = 4 * (y * 640 + x);
|
||||
const u16 top = *src0++;
|
||||
const u16 bottom = *src1++;
|
||||
|
||||
const u8 b = video_data_in[in_offset + 0];
|
||||
const u8 g0 = video_data_in[in_offset + 1];
|
||||
const u8 g1 = video_data_in[in_offset + in_pitch + 0];
|
||||
const u8 r = video_data_in[in_offset + in_pitch + 1];
|
||||
const u8 b = (top & 0xFF);
|
||||
const u8 g0 = ((top >> 8) & 0xFF);
|
||||
const u8 g1 = (bottom & 0xFF);
|
||||
const u8 r = ((bottom >> 8) & 0xFF);
|
||||
|
||||
// Top-Left
|
||||
vc_attribute.video_data_out[out_offset + 0] = r; // R
|
||||
vc_attribute.video_data_out[out_offset + 1] = g0; // G
|
||||
vc_attribute.video_data_out[out_offset + 2] = b; // B
|
||||
vc_attribute.video_data_out[out_offset + 3] = 255; // A
|
||||
*dst0++ = rgba(r, g0, b, 255);
|
||||
|
||||
// Top-Right Pixel
|
||||
vc_attribute.video_data_out[out_offset + 4] = r; // R
|
||||
vc_attribute.video_data_out[out_offset + 5] = g0; // G
|
||||
vc_attribute.video_data_out[out_offset + 6] = b; // B
|
||||
vc_attribute.video_data_out[out_offset + 7] = 255; // A
|
||||
*dst0++ = rgba(r, g0, b, 255);
|
||||
|
||||
// Bottom-Left Pixel
|
||||
vc_attribute.video_data_out[out_offset + out_pitch + 0] = r; // R
|
||||
vc_attribute.video_data_out[out_offset + out_pitch + 1] = g1; // G
|
||||
vc_attribute.video_data_out[out_offset + out_pitch + 2] = b; // B
|
||||
vc_attribute.video_data_out[out_offset + out_pitch + 3] = 255; // A
|
||||
*dst1++ = rgba(r, g1, b, 255);
|
||||
|
||||
// Bottom-Right Pixel
|
||||
vc_attribute.video_data_out[out_offset + out_pitch + 4] = r; // R
|
||||
vc_attribute.video_data_out[out_offset + out_pitch + 5] = g1; // G
|
||||
vc_attribute.video_data_out[out_offset + out_pitch + 6] = b; // B
|
||||
vc_attribute.video_data_out[out_offset + out_pitch + 7] = 255; // A
|
||||
*dst1++ = rgba(r, g1, b, 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -804,11 +806,11 @@ static void ds3_pos_to_gem_state(u32 gem_num, const gem_config::gem_controller&
|
||||
s32 ds3_pos_x, ds3_pos_y;
|
||||
ds3_get_stick_values(gem_num, pad, ds3_pos_x, ds3_pos_y);
|
||||
|
||||
if constexpr (std::is_same<T, vm::ptr<CellGemState>>::value)
|
||||
if constexpr (std::is_same_v<T, vm::ptr<CellGemState>>)
|
||||
{
|
||||
pos_to_gem_state(gem_num, controller, gem_state, ds3_pos_x, ds3_pos_y, ds3_max_x, ds3_max_y);
|
||||
}
|
||||
else if constexpr (std::is_same<T, vm::ptr<CellGemImageState>>::value)
|
||||
else if constexpr (std::is_same_v<T, vm::ptr<CellGemImageState>>)
|
||||
{
|
||||
pos_to_gem_image_state(gem_num, controller, gem_state, ds3_pos_x, ds3_pos_y, ds3_max_x, ds3_max_y);
|
||||
}
|
||||
@@ -947,11 +949,11 @@ static void mouse_pos_to_gem_state(const u32 mouse_no, const gem_config::gem_con
|
||||
|
||||
const auto& mouse = ::at32(handler.GetMice(), mouse_no);
|
||||
|
||||
if constexpr (std::is_same<T, vm::ptr<CellGemState>>::value)
|
||||
if constexpr (std::is_same_v<T, vm::ptr<CellGemState>>)
|
||||
{
|
||||
pos_to_gem_state(mouse_no, controller, gem_state, mouse.x_pos, mouse.y_pos, mouse.x_max, mouse.y_max);
|
||||
}
|
||||
else if constexpr (std::is_same<T, vm::ptr<CellGemImageState>>::value)
|
||||
else if constexpr (std::is_same_v<T, vm::ptr<CellGemImageState>>)
|
||||
{
|
||||
pos_to_gem_image_state(mouse_no, controller, gem_state, mouse.x_pos, mouse.y_pos, mouse.x_max, mouse.y_max);
|
||||
}
|
||||
@@ -1015,11 +1017,11 @@ static void gun_pos_to_gem_state(const u32 gem_no, const gem_config::gem_control
|
||||
y_max = gun.handler.get_axis_y_max(gem_no);
|
||||
}
|
||||
|
||||
if constexpr (std::is_same<T, vm::ptr<CellGemState>>::value)
|
||||
if constexpr (std::is_same_v<T, vm::ptr<CellGemState>>)
|
||||
{
|
||||
pos_to_gem_state(gem_no, controller, gem_state, x_pos, y_pos, x_max, y_max);
|
||||
}
|
||||
else if constexpr (std::is_same<T, vm::ptr<CellGemImageState>>::value)
|
||||
else if constexpr (std::is_same_v<T, vm::ptr<CellGemImageState>>)
|
||||
{
|
||||
pos_to_gem_image_state(gem_no, controller, gem_state, x_pos, y_pos, x_max, y_max);
|
||||
}
|
||||
@@ -1496,7 +1498,7 @@ error_code cellGemGetInertialState(u32 gem_num, u32 state_flag, u64 timestamp, v
|
||||
|
||||
error_code cellGemGetInfo(vm::ptr<CellGemInfo> info)
|
||||
{
|
||||
cellGem.warning("cellGemGetInfo(info=*0x%x)", info);
|
||||
cellGem.trace("cellGemGetInfo(info=*0x%x)", info);
|
||||
|
||||
auto& gem = g_fxo->get<gem_config>();
|
||||
|
||||
@@ -1638,7 +1640,7 @@ error_code cellGemGetState(u32 gem_num, u32 flag, u64 time_parameter, vm::ptr<Ce
|
||||
|
||||
if (!gem.is_controller_ready(gem_num))
|
||||
{
|
||||
return CELL_GEM_NOT_CONNECTED;
|
||||
return not_an_error(CELL_GEM_NOT_CONNECTED);
|
||||
}
|
||||
|
||||
// TODO: Get the gem state at the specified time
|
||||
|
||||
@@ -42,13 +42,16 @@ KeyboardHandlerBase::KeyboardHandlerBase(utils::serial* ar)
|
||||
return;
|
||||
}
|
||||
|
||||
(*ar)(m_info.max_connect);
|
||||
u32 max_connect = 0;
|
||||
|
||||
if (m_info.max_connect)
|
||||
(*ar)(max_connect);
|
||||
|
||||
if (max_connect)
|
||||
{
|
||||
Emu.PostponeInitCode([this]()
|
||||
Emu.PostponeInitCode([this, max_connect]()
|
||||
{
|
||||
Init(m_info.max_connect);
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
AddConsumer(keyboard_consumer::identifier::cellKb, max_connect);
|
||||
auto lk = init.init();
|
||||
});
|
||||
}
|
||||
@@ -56,9 +59,20 @@ KeyboardHandlerBase::KeyboardHandlerBase(utils::serial* ar)
|
||||
|
||||
void KeyboardHandlerBase::save(utils::serial& ar)
|
||||
{
|
||||
u32 max_connect = 0;
|
||||
const auto inited = init.access();
|
||||
|
||||
ar(inited ? m_info.max_connect : 0);
|
||||
if (inited)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
if (auto it = m_consumers.find(keyboard_consumer::identifier::cellKb); it != m_consumers.end())
|
||||
{
|
||||
max_connect = it->second.GetInfo().max_connect;
|
||||
}
|
||||
}
|
||||
|
||||
ar(max_connect);
|
||||
}
|
||||
|
||||
error_code cellKbInit(ppu_thread& ppu, u32 max_connect)
|
||||
@@ -79,7 +93,9 @@ error_code cellKbInit(ppu_thread& ppu, u32 max_connect)
|
||||
}
|
||||
|
||||
sys_config_start(ppu);
|
||||
handler.Init(std::min(max_connect, 7u));
|
||||
|
||||
std::lock_guard<std::mutex> lock(handler.m_mutex);
|
||||
handler.AddConsumer(keyboard_consumer::identifier::cellKb, std::min(max_connect, 7u));
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -95,6 +111,11 @@ error_code cellKbEnd(ppu_thread& ppu)
|
||||
if (!init)
|
||||
return CELL_KB_ERROR_UNINITIALIZED;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(handler.m_mutex);
|
||||
handler.RemoveConsumer(keyboard_consumer::identifier::cellKb);
|
||||
}
|
||||
|
||||
// TODO
|
||||
sys_config_stop(ppu);
|
||||
return CELL_OK;
|
||||
@@ -116,12 +137,13 @@ error_code cellKbClearBuf(u32 port_no)
|
||||
|
||||
std::lock_guard<std::mutex> lock(handler.m_mutex);
|
||||
|
||||
const KbInfo& current_info = handler.GetInfo();
|
||||
keyboard_consumer& consumer = handler.GetConsumer(keyboard_consumer::identifier::cellKb);
|
||||
const KbInfo& current_info = consumer.GetInfo();
|
||||
|
||||
if (port_no >= handler.GetKeyboards().size() || current_info.status[port_no] != CELL_KB_STATUS_CONNECTED)
|
||||
if (port_no >= consumer.GetKeyboards().size() || current_info.status[port_no] != CELL_KB_STATUS_CONNECTED)
|
||||
return not_an_error(CELL_KB_ERROR_NO_DEVICE);
|
||||
|
||||
KbData& current_data = handler.GetData(port_no);
|
||||
KbData& current_data = consumer.GetData(port_no);
|
||||
current_data.len = 0;
|
||||
current_data.led = 0;
|
||||
current_data.mkey = 0;
|
||||
@@ -298,8 +320,9 @@ error_code cellKbGetInfo(vm::ptr<CellKbInfo> info)
|
||||
std::memset(info.get_ptr(), 0, info.size());
|
||||
|
||||
std::lock_guard<std::mutex> lock(handler.m_mutex);
|
||||
keyboard_consumer& consumer = handler.GetConsumer(keyboard_consumer::identifier::cellKb);
|
||||
|
||||
const KbInfo& current_info = handler.GetInfo();
|
||||
const KbInfo& current_info = consumer.GetInfo();
|
||||
info->max_connect = current_info.max_connect;
|
||||
info->now_connect = current_info.now_connect;
|
||||
info->info = current_info.info;
|
||||
@@ -327,13 +350,13 @@ error_code cellKbRead(u32 port_no, vm::ptr<CellKbData> data)
|
||||
return CELL_KB_ERROR_INVALID_PARAMETER;
|
||||
|
||||
std::lock_guard<std::mutex> lock(handler.m_mutex);
|
||||
keyboard_consumer& consumer = handler.GetConsumer(keyboard_consumer::identifier::cellKb);
|
||||
const KbInfo& current_info = consumer.GetInfo();
|
||||
|
||||
const KbInfo& current_info = handler.GetInfo();
|
||||
|
||||
if (port_no >= handler.GetKeyboards().size() || current_info.status[port_no] != CELL_KB_STATUS_CONNECTED)
|
||||
if (port_no >= consumer.GetKeyboards().size() || current_info.status[port_no] != CELL_KB_STATUS_CONNECTED)
|
||||
return not_an_error(CELL_KB_ERROR_NO_DEVICE);
|
||||
|
||||
KbData& current_data = handler.GetData(port_no);
|
||||
KbData& current_data = consumer.GetData(port_no);
|
||||
|
||||
if (current_info.is_null_handler || (current_info.info & CELL_KB_INFO_INTERCEPTED) || !is_input_allowed())
|
||||
{
|
||||
@@ -355,7 +378,7 @@ error_code cellKbRead(u32 port_no, vm::ptr<CellKbData> data)
|
||||
data->keycode[i] = current_data.buttons[i].m_keyCode;
|
||||
}
|
||||
|
||||
KbConfig& current_config = handler.GetConfig(port_no);
|
||||
KbConfig& current_config = consumer.GetConfig(port_no);
|
||||
|
||||
// For single character mode to work properly we need to "flush" the buffer after reading or else we'll constantly get the same key presses with each call.
|
||||
// Actual key repeats are handled by adding a new key code to the buffer periodically. Key releases are handled in a similar fashion.
|
||||
@@ -383,12 +406,13 @@ error_code cellKbSetCodeType(u32 port_no, u32 type)
|
||||
if (port_no >= CELL_KB_MAX_KEYBOARDS || type > CELL_KB_CODETYPE_ASCII)
|
||||
return CELL_KB_ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (port_no >= handler.GetKeyboards().size())
|
||||
std::lock_guard<std::mutex> lock(handler.m_mutex);
|
||||
keyboard_consumer& consumer = handler.GetConsumer(keyboard_consumer::identifier::cellKb);
|
||||
|
||||
if (port_no >= consumer.GetKeyboards().size())
|
||||
return CELL_OK;
|
||||
|
||||
std::lock_guard<std::mutex> lock(handler.m_mutex);
|
||||
|
||||
KbConfig& current_config = handler.GetConfig(port_no);
|
||||
KbConfig& current_config = consumer.GetConfig(port_no);
|
||||
current_config.code_type = type;
|
||||
|
||||
// can also return CELL_KB_ERROR_SYS_SETTING_FAILED
|
||||
@@ -413,12 +437,13 @@ error_code cellKbSetLEDStatus(u32 port_no, u8 led)
|
||||
if (led > 7)
|
||||
return CELL_KB_ERROR_SYS_SETTING_FAILED;
|
||||
|
||||
if (port_no >= handler.GetKeyboards().size() || handler.GetInfo().status[port_no] != CELL_KB_STATUS_CONNECTED)
|
||||
std::lock_guard<std::mutex> lock(handler.m_mutex);
|
||||
keyboard_consumer& consumer = handler.GetConsumer(keyboard_consumer::identifier::cellKb);
|
||||
|
||||
if (port_no >= consumer.GetKeyboards().size() || consumer.GetInfo().status[port_no] != CELL_KB_STATUS_CONNECTED)
|
||||
return CELL_KB_ERROR_FATAL;
|
||||
|
||||
std::lock_guard<std::mutex> lock(handler.m_mutex);
|
||||
|
||||
KbData& current_data = handler.GetData(port_no);
|
||||
KbData& current_data = consumer.GetData(port_no);
|
||||
current_data.led = static_cast<u32>(led);
|
||||
|
||||
return CELL_OK;
|
||||
@@ -438,16 +463,17 @@ error_code cellKbSetReadMode(u32 port_no, u32 rmode)
|
||||
if (port_no >= CELL_KB_MAX_KEYBOARDS || rmode > CELL_KB_RMODE_PACKET)
|
||||
return CELL_KB_ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (port_no >= handler.GetKeyboards().size())
|
||||
std::lock_guard<std::mutex> lock(handler.m_mutex);
|
||||
keyboard_consumer& consumer = handler.GetConsumer(keyboard_consumer::identifier::cellKb);
|
||||
|
||||
if (port_no >= consumer.GetKeyboards().size())
|
||||
return CELL_OK;
|
||||
|
||||
std::lock_guard<std::mutex> lock(handler.m_mutex);
|
||||
|
||||
KbConfig& current_config = handler.GetConfig(port_no);
|
||||
KbConfig& current_config = consumer.GetConfig(port_no);
|
||||
current_config.read_mode = rmode;
|
||||
|
||||
// Key repeat must be disabled in packet mode. But let's just always enable it otherwise.
|
||||
Keyboard& keyboard = handler.GetKeyboards()[port_no];
|
||||
Keyboard& keyboard = consumer.GetKeyboards()[port_no];
|
||||
keyboard.m_key_repeat = rmode != CELL_KB_RMODE_PACKET;
|
||||
|
||||
// can also return CELL_KB_ERROR_SYS_SETTING_FAILED
|
||||
@@ -471,16 +497,18 @@ error_code cellKbGetConfiguration(u32 port_no, vm::ptr<CellKbConfig> config)
|
||||
|
||||
std::lock_guard<std::mutex> lock(handler.m_mutex);
|
||||
|
||||
const KbInfo& current_info = handler.GetInfo();
|
||||
keyboard_consumer& consumer = handler.GetConsumer(keyboard_consumer::identifier::cellKb);
|
||||
|
||||
if (port_no >= handler.GetKeyboards().size() || current_info.status[port_no] != CELL_KB_STATUS_CONNECTED)
|
||||
const KbInfo& current_info = consumer.GetInfo();
|
||||
|
||||
if (port_no >= consumer.GetKeyboards().size() || current_info.status[port_no] != CELL_KB_STATUS_CONNECTED)
|
||||
return not_an_error(CELL_KB_ERROR_NO_DEVICE);
|
||||
|
||||
// tests show that config is checked only after the device's status
|
||||
if (!config)
|
||||
return CELL_KB_ERROR_INVALID_PARAMETER;
|
||||
|
||||
const KbConfig& current_config = handler.GetConfig(port_no);
|
||||
const KbConfig& current_config = consumer.GetConfig(port_no);
|
||||
config->arrange = current_config.arrange;
|
||||
config->read_mode = current_config.read_mode;
|
||||
config->code_type = current_config.code_type;
|
||||
|
||||
@@ -1144,8 +1144,6 @@ error_code cellMicRemoveNotifyEventQueue(u64 key)
|
||||
|
||||
error_code cell_mic_read(s32 dev_num, vm::ptr<void> data, s32 max_bytes, /*CellMicSignalType*/u32 type)
|
||||
{
|
||||
// TODO: CELL_MICIN_ERROR_PARAM
|
||||
|
||||
auto& mic_thr = g_fxo->get<mic_thread>();
|
||||
const std::lock_guard lock(mic_thr.mutex);
|
||||
if (!mic_thr.init)
|
||||
@@ -1159,16 +1157,19 @@ error_code cell_mic_read(s32 dev_num, vm::ptr<void> data, s32 max_bytes, /*CellM
|
||||
if (!device.is_opened() || !(device.get_signal_types() & type))
|
||||
return CELL_MICIN_ERROR_NOT_OPEN;
|
||||
|
||||
if (!data)
|
||||
return not_an_error(0);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case CELLMIC_SIGTYPE_DSP: return not_an_error(device.read_dsp(vm::_ptr<u8>(data.addr()), max_bytes));
|
||||
case CELLMIC_SIGTYPE_AUX: return CELL_OK; // TODO
|
||||
case CELLMIC_SIGTYPE_AUX: return not_an_error(0); // TODO
|
||||
case CELLMIC_SIGTYPE_RAW: return not_an_error(device.read_raw(vm::_ptr<u8>(data.addr()), max_bytes));
|
||||
default:
|
||||
fmt::throw_exception("Invalid CELLMIC_SIGTYPE %d", type);
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
return not_an_error(0);
|
||||
}
|
||||
|
||||
error_code cellMicReadRaw(s32 dev_num, vm::ptr<void> data, s32 max_bytes)
|
||||
|
||||
@@ -157,9 +157,8 @@ struct CellMicInputStream
|
||||
|
||||
struct CellMicInputDefinition
|
||||
{
|
||||
// TODO: Data types
|
||||
volatile u32 uiDevId;
|
||||
CellMicInputStream data;
|
||||
be_t<u32> uiDevId;
|
||||
CellMicInputStream data;
|
||||
CellMicInputFormatI aux_format;
|
||||
CellMicInputFormatI raw_format;
|
||||
CellMicInputFormatI sig_format;
|
||||
@@ -182,13 +181,13 @@ struct CellMicStatus
|
||||
// --- End of cell definitions ---
|
||||
|
||||
|
||||
template <usz S>
|
||||
template <usz Size>
|
||||
class simple_ringbuf
|
||||
{
|
||||
public:
|
||||
simple_ringbuf()
|
||||
{
|
||||
m_container.resize(S);
|
||||
m_container.resize(Size);
|
||||
}
|
||||
|
||||
bool has_data() const
|
||||
@@ -200,19 +199,19 @@ public:
|
||||
{
|
||||
ensure(buf);
|
||||
|
||||
u32 to_read = size > m_used ? m_used : size;
|
||||
const u32 to_read = size > m_used ? m_used : size;
|
||||
if (!to_read)
|
||||
return 0;
|
||||
|
||||
u8* data = m_container.data();
|
||||
u32 new_tail = m_tail + to_read;
|
||||
u8* data = m_container.data();
|
||||
const u32 new_tail = m_tail + to_read;
|
||||
|
||||
if (new_tail >= S)
|
||||
if (new_tail >= Size)
|
||||
{
|
||||
u32 first_chunk_size = S - m_tail;
|
||||
const u32 first_chunk_size = Size - m_tail;
|
||||
std::memcpy(buf, data + m_tail, first_chunk_size);
|
||||
std::memcpy(buf + first_chunk_size, data, to_read - first_chunk_size);
|
||||
m_tail = (new_tail - S);
|
||||
m_tail = (new_tail - Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -227,30 +226,32 @@ public:
|
||||
|
||||
void write_bytes(const u8* buf, const u32 size)
|
||||
{
|
||||
ensure(size <= S);
|
||||
ensure(size <= Size);
|
||||
|
||||
if (u32 over_size = m_used + size; over_size > S)
|
||||
const u32 over_size = m_used + size;
|
||||
|
||||
if (over_size > Size)
|
||||
{
|
||||
m_tail += (over_size - S);
|
||||
if (m_tail > S)
|
||||
m_tail -= S;
|
||||
m_tail += (over_size - Size);
|
||||
if (m_tail > Size)
|
||||
m_tail -= Size;
|
||||
|
||||
m_used = S;
|
||||
m_used = Size;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_used = over_size;
|
||||
}
|
||||
|
||||
u8* data = m_container.data();
|
||||
u32 new_head = m_head + size;
|
||||
u8* data = m_container.data();
|
||||
const u32 new_head = m_head + size;
|
||||
|
||||
if (new_head >= S)
|
||||
if (new_head >= Size)
|
||||
{
|
||||
u32 first_chunk_size = S - m_head;
|
||||
const u32 first_chunk_size = Size - m_head;
|
||||
std::memcpy(data + m_head, buf, first_chunk_size);
|
||||
std::memcpy(data, buf + first_chunk_size, size - first_chunk_size);
|
||||
m_head = (new_head - S);
|
||||
m_head = (new_head - Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -250,7 +250,7 @@ error_code cellMusicGetSelectionContext(vm::ptr<CellMusicSelectionContext> conte
|
||||
|
||||
if (!music.current_selection_context)
|
||||
{
|
||||
return CELL_MUSIC2_ERROR_NO_ACTIVE_CONTENT;
|
||||
return CELL_MUSIC_ERROR_NO_ACTIVE_CONTENT;
|
||||
}
|
||||
|
||||
*context = music.current_selection_context.get();
|
||||
@@ -325,7 +325,7 @@ error_code cellMusicGetContentsId(vm::ptr<CellSearchContentId> contents_id)
|
||||
|
||||
if (!music.current_selection_context)
|
||||
{
|
||||
return CELL_MUSIC2_ERROR_NO_ACTIVE_CONTENT;
|
||||
return CELL_MUSIC_ERROR_NO_ACTIVE_CONTENT;
|
||||
}
|
||||
|
||||
return music.current_selection_context.find_content_id(contents_id);
|
||||
@@ -408,6 +408,12 @@ error_code cellMusicGetContentsId2(vm::ptr<CellSearchContentId> contents_id)
|
||||
// HACKY
|
||||
auto& music = g_fxo->get<music_state>();
|
||||
std::lock_guard lock(music.mtx);
|
||||
|
||||
if (!music.current_selection_context)
|
||||
{
|
||||
return CELL_MUSIC2_ERROR_NO_ACTIVE_CONTENT;
|
||||
}
|
||||
|
||||
return music.current_selection_context.find_content_id(contents_id);
|
||||
}
|
||||
|
||||
|
||||
@@ -425,14 +425,14 @@ error_code cellOskDialogLoadAsync(u32 container, vm::ptr<CellOskDialogParam> dia
|
||||
return;
|
||||
}
|
||||
|
||||
bool is_kook_key = false;
|
||||
bool is_hook_key = false;
|
||||
|
||||
switch (key_message.keycode)
|
||||
{
|
||||
case CELL_KEYC_NO_EVENT:
|
||||
{
|
||||
// Any shift/alt/ctrl key
|
||||
is_kook_key = key_message.mkey > 0 && (info.hook_event_mode & CELL_OSKDIALOG_EVENT_HOOK_TYPE_ONLY_MODIFIER);
|
||||
is_hook_key = key_message.mkey > 0 && (info.hook_event_mode & CELL_OSKDIALOG_EVENT_HOOK_TYPE_ONLY_MODIFIER);
|
||||
break;
|
||||
}
|
||||
case CELL_KEYC_E_ROLLOVER:
|
||||
@@ -473,18 +473,18 @@ error_code cellOskDialogLoadAsync(u32 container, vm::ptr<CellOskDialogParam> dia
|
||||
case CELL_KEYC_MUHENKAN:
|
||||
{
|
||||
// Any function key or other special key like Delete
|
||||
is_kook_key = (info.hook_event_mode & CELL_OSKDIALOG_EVENT_HOOK_TYPE_FUNCTION_KEY);
|
||||
is_hook_key = (info.hook_event_mode & CELL_OSKDIALOG_EVENT_HOOK_TYPE_FUNCTION_KEY);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
// Any regular ascii key
|
||||
is_kook_key = (info.hook_event_mode & CELL_OSKDIALOG_EVENT_HOOK_TYPE_ASCII_KEY);
|
||||
is_hook_key = (info.hook_event_mode & CELL_OSKDIALOG_EVENT_HOOK_TYPE_ASCII_KEY);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_kook_key)
|
||||
if (!is_hook_key)
|
||||
{
|
||||
cellOskDialog.notice("on_osk_key_input_entered: not a hook key: led=%d, mkey=%d, keycode=%d, hook_event_mode=%d", key_message.led, key_message.mkey, key_message.keycode, info.hook_event_mode.load());
|
||||
return;
|
||||
|
||||
@@ -107,7 +107,7 @@ void cellPad_NotifyStateChange(usz index, u64 /*state*/, bool locked)
|
||||
// NOTE 1: The state's CONNECTED bit should currently be identical to the current
|
||||
// m_port_status CONNECTED bit when called from our pad handlers.
|
||||
// NOTE 2: Make sure to propagate all other status bits to the reported status.
|
||||
const u32 new_status = pads[index]->m_port_status;
|
||||
const u32 new_status = pad->m_port_status;
|
||||
|
||||
if (~(old_status ^ new_status) & CELL_PAD_STATUS_CONNECTED)
|
||||
{
|
||||
@@ -188,17 +188,11 @@ error_code cellPadInit(ppu_thread& ppu, u32 max_connect)
|
||||
config.port_setting.fill(CELL_PAD_SETTING_PRESS_OFF | CELL_PAD_SETTING_SENSOR_OFF);
|
||||
config.reported_info = {};
|
||||
|
||||
std::array<s32, CELL_MAX_PADS> statuses{};
|
||||
|
||||
const auto handler = pad::get_current_handler();
|
||||
|
||||
const auto& pads = handler->GetPads();
|
||||
|
||||
for (usz i = 0; i < statuses.size(); ++i)
|
||||
for (usz i = 0; i < config.get_max_connect(); ++i)
|
||||
{
|
||||
if (i >= config.get_max_connect())
|
||||
break;
|
||||
|
||||
if (pads[i]->m_port_status & CELL_PAD_STATUS_CONNECTED)
|
||||
{
|
||||
send_sys_io_connect_event(i, CELL_PAD_STATUS_CONNECTED);
|
||||
@@ -255,16 +249,14 @@ error_code cellPadClearBuf(u32 port_no)
|
||||
if (!config.max_connect)
|
||||
return CELL_PAD_ERROR_UNINITIALIZED;
|
||||
|
||||
const auto handler = pad::get_current_handler();
|
||||
|
||||
if (port_no >= CELL_MAX_PADS)
|
||||
return CELL_PAD_ERROR_INVALID_PARAMETER;
|
||||
|
||||
const auto& pads = handler->GetPads();
|
||||
|
||||
if (port_no >= config.get_max_connect())
|
||||
return CELL_PAD_ERROR_NO_DEVICE;
|
||||
|
||||
const auto handler = pad::get_current_handler();
|
||||
const auto& pads = handler->GetPads();
|
||||
const auto& pad = pads[port_no];
|
||||
|
||||
if (!config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
|
||||
@@ -618,16 +610,14 @@ error_code cellPadGetData(u32 port_no, vm::ptr<CellPadData> data)
|
||||
if (!config.max_connect)
|
||||
return CELL_PAD_ERROR_UNINITIALIZED;
|
||||
|
||||
const auto handler = pad::get_current_handler();
|
||||
|
||||
if (port_no >= CELL_MAX_PADS || !data)
|
||||
return CELL_PAD_ERROR_INVALID_PARAMETER;
|
||||
|
||||
const auto& pads = handler->GetPads();
|
||||
|
||||
if (port_no >= config.get_max_connect())
|
||||
return CELL_PAD_ERROR_NO_DEVICE;
|
||||
|
||||
const auto handler = pad::get_current_handler();
|
||||
const auto& pads = handler->GetPads();
|
||||
const auto& pad = pads[port_no];
|
||||
|
||||
if (!config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
|
||||
@@ -648,11 +638,10 @@ error_code cellPadPeriphGetInfo(vm::ptr<CellPadPeriphInfo> info)
|
||||
if (!config.max_connect)
|
||||
return CELL_PAD_ERROR_UNINITIALIZED;
|
||||
|
||||
const auto handler = pad::get_current_handler();
|
||||
|
||||
if (!info)
|
||||
return CELL_PAD_ERROR_INVALID_PARAMETER;
|
||||
|
||||
const auto handler = pad::get_current_handler();
|
||||
const PadInfo& rinfo = handler->GetInfo();
|
||||
|
||||
std::memset(info.get_ptr(), 0, sizeof(CellPadPeriphInfo));
|
||||
@@ -662,11 +651,8 @@ error_code cellPadPeriphGetInfo(vm::ptr<CellPadPeriphInfo> info)
|
||||
|
||||
u32 now_connect = 0;
|
||||
|
||||
for (u32 i = 0; i < CELL_PAD_MAX_PORT_NUM; ++i)
|
||||
for (u32 i = 0; i < config.get_max_connect(); ++i)
|
||||
{
|
||||
if (i >= config.get_max_connect())
|
||||
break;
|
||||
|
||||
pad_data_internal& reported_info = config.reported_info[i];
|
||||
|
||||
info->port_status[i] = reported_info.port_status;
|
||||
@@ -703,17 +689,15 @@ error_code cellPadPeriphGetData(u32 port_no, vm::ptr<CellPadPeriphData> data)
|
||||
if (!config.max_connect)
|
||||
return CELL_PAD_ERROR_UNINITIALIZED;
|
||||
|
||||
const auto handler = pad::get_current_handler();
|
||||
|
||||
// port_no can only be 0-6 in this function
|
||||
if (port_no >= CELL_PAD_MAX_PORT_NUM || !data)
|
||||
return CELL_PAD_ERROR_INVALID_PARAMETER;
|
||||
|
||||
const auto& pads = handler->GetPads();
|
||||
|
||||
if (port_no >= config.get_max_connect())
|
||||
return CELL_PAD_ERROR_NO_DEVICE;
|
||||
|
||||
const auto handler = pad::get_current_handler();
|
||||
const auto& pads = handler->GetPads();
|
||||
const auto& pad = pads[port_no];
|
||||
|
||||
if (!config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
|
||||
@@ -738,16 +722,14 @@ error_code cellPadGetRawData(u32 port_no, vm::ptr<CellPadData> data)
|
||||
if (!config.max_connect)
|
||||
return CELL_PAD_ERROR_UNINITIALIZED;
|
||||
|
||||
const auto handler = pad::get_current_handler();
|
||||
|
||||
if (port_no >= CELL_MAX_PADS || !data)
|
||||
return CELL_PAD_ERROR_INVALID_PARAMETER;
|
||||
|
||||
const auto& pads = handler->GetPads();
|
||||
|
||||
if (port_no >= config.get_max_connect())
|
||||
return CELL_PAD_ERROR_NO_DEVICE;
|
||||
|
||||
const auto handler = pad::get_current_handler();
|
||||
const auto& pads = handler->GetPads();
|
||||
const auto& pad = pads[port_no];
|
||||
|
||||
if (!config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
|
||||
@@ -793,8 +775,6 @@ error_code cellPadSetActDirect(u32 port_no, vm::ptr<CellPadActParam> param)
|
||||
if (!config.max_connect)
|
||||
return CELL_PAD_ERROR_UNINITIALIZED;
|
||||
|
||||
const auto handler = pad::get_current_handler();
|
||||
|
||||
if (port_no >= CELL_MAX_PADS || !param)
|
||||
return CELL_PAD_ERROR_INVALID_PARAMETER;
|
||||
|
||||
@@ -809,11 +789,11 @@ error_code cellPadSetActDirect(u32 port_no, vm::ptr<CellPadActParam> param)
|
||||
}
|
||||
}
|
||||
|
||||
const auto& pads = handler->GetPads();
|
||||
|
||||
if (port_no >= config.get_max_connect())
|
||||
return CELL_PAD_ERROR_NO_DEVICE;
|
||||
|
||||
const auto handler = pad::get_current_handler();
|
||||
const auto& pads = handler->GetPads();
|
||||
const auto& pad = pads[port_no];
|
||||
|
||||
if (!config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
|
||||
@@ -839,24 +819,20 @@ error_code cellPadGetInfo(vm::ptr<CellPadInfo> info)
|
||||
if (!config.max_connect)
|
||||
return CELL_PAD_ERROR_UNINITIALIZED;
|
||||
|
||||
const auto handler = pad::get_current_handler();
|
||||
|
||||
if (!info)
|
||||
return CELL_PAD_ERROR_INVALID_PARAMETER;
|
||||
|
||||
std::memset(info.get_ptr(), 0, sizeof(CellPadInfo));
|
||||
|
||||
const auto handler = pad::get_current_handler();
|
||||
const PadInfo& rinfo = handler->GetInfo();
|
||||
info->max_connect = config.max_connect;
|
||||
info->system_info = rinfo.system_info;
|
||||
|
||||
u32 now_connect = 0;
|
||||
|
||||
for (u32 i = 0; i < CELL_MAX_PADS; ++i)
|
||||
for (u32 i = 0; i < config.get_max_connect(); ++i)
|
||||
{
|
||||
if (i >= config.get_max_connect())
|
||||
break;
|
||||
|
||||
pad_data_internal& reported_info = config.reported_info[i];
|
||||
reported_info.port_status &= ~CELL_PAD_STATUS_ASSIGN_CHANGES; // TODO: should ASSIGN flags be cleared here?
|
||||
|
||||
@@ -888,13 +864,12 @@ error_code cellPadGetInfo2(vm::ptr<CellPadInfo2> info)
|
||||
if (!config.max_connect)
|
||||
return CELL_PAD_ERROR_UNINITIALIZED;
|
||||
|
||||
const auto handler = pad::get_current_handler();
|
||||
|
||||
if (!info)
|
||||
return CELL_PAD_ERROR_INVALID_PARAMETER;
|
||||
|
||||
std::memset(info.get_ptr(), 0, sizeof(CellPadInfo2));
|
||||
|
||||
const auto handler = pad::get_current_handler();
|
||||
const PadInfo& rinfo = handler->GetInfo();
|
||||
info->max_connect = config.get_max_connect(); // Here it is forcibly clamped
|
||||
info->system_info = rinfo.system_info;
|
||||
@@ -903,11 +878,8 @@ error_code cellPadGetInfo2(vm::ptr<CellPadInfo2> info)
|
||||
|
||||
const auto& pads = handler->GetPads();
|
||||
|
||||
for (u32 i = 0; i < CELL_PAD_MAX_PORT_NUM; ++i)
|
||||
for (u32 i = 0; i < config.get_max_connect(); ++i)
|
||||
{
|
||||
if (i >= config.get_max_connect())
|
||||
break;
|
||||
|
||||
pad_data_internal& reported_info = config.reported_info[i];
|
||||
|
||||
info->port_status[i] = reported_info.port_status;
|
||||
@@ -941,16 +913,14 @@ error_code cellPadGetCapabilityInfo(u32 port_no, vm::ptr<CellPadCapabilityInfo>
|
||||
if (!config.max_connect)
|
||||
return CELL_PAD_ERROR_UNINITIALIZED;
|
||||
|
||||
const auto handler = pad::get_current_handler();
|
||||
|
||||
if (port_no >= CELL_MAX_PADS || !info)
|
||||
return CELL_PAD_ERROR_INVALID_PARAMETER;
|
||||
|
||||
const auto& pads = handler->GetPads();
|
||||
|
||||
if (port_no >= config.get_max_connect())
|
||||
return CELL_PAD_ERROR_NO_DEVICE;
|
||||
|
||||
const auto handler = pad::get_current_handler();
|
||||
const auto& pads = handler->GetPads();
|
||||
const auto& pad = pads[port_no];
|
||||
|
||||
if (!config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
|
||||
@@ -999,16 +969,14 @@ error_code cellPadInfoPressMode(u32 port_no)
|
||||
if (!config.max_connect)
|
||||
return CELL_PAD_ERROR_UNINITIALIZED;
|
||||
|
||||
const auto handler = pad::get_current_handler();
|
||||
|
||||
if (port_no >= CELL_MAX_PADS)
|
||||
return CELL_PAD_ERROR_INVALID_PARAMETER;
|
||||
|
||||
const auto& pads = handler->GetPads();
|
||||
|
||||
if (port_no >= config.get_max_connect())
|
||||
return CELL_PAD_ERROR_NO_DEVICE;
|
||||
|
||||
const auto handler = pad::get_current_handler();
|
||||
const auto& pads = handler->GetPads();
|
||||
const auto& pad = pads[port_no];
|
||||
|
||||
if (!config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
|
||||
@@ -1028,16 +996,14 @@ error_code cellPadInfoSensorMode(u32 port_no)
|
||||
if (!config.max_connect)
|
||||
return CELL_PAD_ERROR_UNINITIALIZED;
|
||||
|
||||
const auto handler = pad::get_current_handler();
|
||||
|
||||
if (port_no >= CELL_MAX_PADS)
|
||||
return CELL_PAD_ERROR_INVALID_PARAMETER;
|
||||
|
||||
const auto& pads = handler->GetPads();
|
||||
|
||||
if (port_no >= config.get_max_connect())
|
||||
return CELL_PAD_ERROR_NO_DEVICE;
|
||||
|
||||
const auto handler = pad::get_current_handler();
|
||||
const auto& pads = handler->GetPads();
|
||||
const auto& pad = pads[port_no];
|
||||
|
||||
if (!config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
|
||||
@@ -1057,17 +1023,15 @@ error_code cellPadSetPressMode(u32 port_no, u32 mode)
|
||||
if (!config.max_connect)
|
||||
return CELL_PAD_ERROR_UNINITIALIZED;
|
||||
|
||||
const auto handler = pad::get_current_handler();
|
||||
|
||||
if (port_no >= CELL_PAD_MAX_PORT_NUM)
|
||||
return CELL_PAD_ERROR_INVALID_PARAMETER;
|
||||
|
||||
const auto& pads = handler->GetPads();
|
||||
|
||||
// CELL_PAD_ERROR_NO_DEVICE is not returned in this case.
|
||||
if (port_no >= CELL_PAD_MAX_PORT_NUM)
|
||||
return CELL_OK;
|
||||
|
||||
const auto handler = pad::get_current_handler();
|
||||
const auto& pads = handler->GetPads();
|
||||
const auto& pad = pads[port_no];
|
||||
|
||||
// TODO: find out if this is checked here or later or at all
|
||||
@@ -1093,17 +1057,15 @@ error_code cellPadSetSensorMode(u32 port_no, u32 mode)
|
||||
if (!config.max_connect)
|
||||
return CELL_PAD_ERROR_UNINITIALIZED;
|
||||
|
||||
const auto handler = pad::get_current_handler();
|
||||
|
||||
if (port_no >= CELL_MAX_PADS)
|
||||
return CELL_PAD_ERROR_INVALID_PARAMETER;
|
||||
|
||||
const auto& pads = handler->GetPads();
|
||||
|
||||
// CELL_PAD_ERROR_NO_DEVICE is not returned in this case.
|
||||
if (port_no >= CELL_PAD_MAX_PORT_NUM)
|
||||
return CELL_OK;
|
||||
|
||||
const auto handler = pad::get_current_handler();
|
||||
const auto& pads = handler->GetPads();
|
||||
const auto& pad = pads[port_no];
|
||||
|
||||
// TODO: find out if this is checked here or later or at all
|
||||
|
||||
@@ -159,7 +159,7 @@ struct rec_param
|
||||
static constexpr u32 rec_framerate = 30; // Always 30 fps
|
||||
static constexpr u32 rec_channels = 2; // Always 2 channels
|
||||
|
||||
class rec_video_sink : public utils::video_sink
|
||||
class rec_video_sink final : public utils::video_sink
|
||||
{
|
||||
public:
|
||||
rec_video_sink() : utils::video_sink()
|
||||
@@ -752,7 +752,7 @@ void rec_info::start_video_provider()
|
||||
if (sample.channels > channels)
|
||||
{
|
||||
// Downmix channels
|
||||
AudioBackend::downmix(CELL_REC_AUDIO_BLOCK_SAMPLES * sample.channels, sample.channels, channels, src, reinterpret_cast<f32*>(dst_buffer.block.data()));
|
||||
AudioBackend::downmix(CELL_REC_AUDIO_BLOCK_SAMPLES * sample.channels, sample.channels, audio_channel_layout::stereo, src, reinterpret_cast<f32*>(dst_buffer.block.data()));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
+891
-691
File diff suppressed because it is too large
Load Diff
@@ -38,19 +38,34 @@ struct CellRtcDateTime
|
||||
be_t<u32> microsecond; // 0 to 999999
|
||||
};
|
||||
|
||||
error_code cellRtcTickAddYears(vm::ptr<CellRtcTick> pTick0, vm::cptr<CellRtcTick> pTick1, s32 iAdd);
|
||||
error_code cellRtcTickAddMonths(vm::ptr<CellRtcTick> pTick0, vm::cptr<CellRtcTick> pTick1, s32 lAdd);
|
||||
error_code cellRtcTickAddDays(vm::ptr<CellRtcTick> pTick0, vm::cptr<CellRtcTick> pTick1, s32 lAdd);
|
||||
error_code cellRtcTickAddHours(vm::ptr<CellRtcTick> pTick0, vm::cptr<CellRtcTick> pTick1, s32 lAdd);
|
||||
error_code cellRtcTickAddMinutes(vm::ptr<CellRtcTick> pTick0, vm::cptr<CellRtcTick> pTick1, s64 lAdd);
|
||||
error_code cellRtcTickAddSeconds(vm::ptr<CellRtcTick> pTick0, vm::cptr<CellRtcTick> pTick1, s64 lAdd);
|
||||
constexpr u64 RTC_MAGIC_OFFSET = 62135596800000000ull; // 1970-01-01 00:00:00.000000
|
||||
constexpr u64 RTC_FILETIME_OFFSET = 50491123200000000ull; // 1601-01-01 00:00:00.000000
|
||||
constexpr u64 RTC_SYSTEM_TIME_MIN = 63082281600000000ull; // 2000-01-01 00:00:00.000000
|
||||
constexpr u64 RTC_SYSTEM_TIME_MAX = 66238041599999999ull; // 2099-12-31 23:59:59.999999
|
||||
|
||||
error_code cellRtcSetTick(vm::ptr<CellRtcDateTime> pTime, vm::cptr<CellRtcTick> pTick);
|
||||
u32 cellRtcGetTickResolution();
|
||||
constexpr u32 DAYS_IN_400_YEARS = 365 * 400 + 97;
|
||||
constexpr u16 DAYS_IN_100_YEARS = 365 * 100 + 24;
|
||||
constexpr u16 DAYS_IN_4_YEARS = 365 * 4 + 1;
|
||||
|
||||
constexpr std::array<u8, 12> DAYS_IN_MONTH = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
|
||||
constexpr std::array<u8, 12> DAYS_IN_MONTH_LEAP = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
|
||||
|
||||
constexpr std::array<u16, 13> MONTH_OFFSET = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, umax };
|
||||
constexpr std::array<u16, 13> MONTH_OFFSET_LEAP = { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, umax };
|
||||
|
||||
constexpr std::array<std::string_view, 7> WEEKDAY_NAMES = { "sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday" };
|
||||
constexpr std::array<std::string_view, 12> MONTH_NAMES = { "january", "february", "march", "april", "may", "june", "july", "august", "september", "october", "november", "december" };
|
||||
constexpr std::array<std::string_view, 70> TIME_ZONE_NAMES = { "gmt", "est", "edt", "cst", "cdt", "mst", "mdt", "pst", "pdt", "nzdt", "nzst", "idle", "nzt", "aesst", "acsst", "cadt", "sadt", "aest", "east", "gst", "ligt", "acst", "sast", "cast", "awsst", "jst", "kst", "wdt", "mt", "awst", "cct", "wadt", "wst", "jt", "wast", "it", "bt", "eetdst", "eet", "cetdst", "fwt", "ist", "mest", "metdst", "sst", "bst", "cet", "dnt", "fst", "met", "mewt", "mez", "nor", "set", "swt", "wetdst", "wet", "wat", "ndt", "adt", "nft", "nst", "ast", "ydt", "hdt", "yst", "ahst", "cat", "nt", "idlw" };
|
||||
constexpr std::array<s8, 70> TIME_ZONE_VALUES = { 0, -10, -8, -12, -10, -14, -12, -16, -14, 26, 24, 24, 24, 22, 21, 21, 21, 20, 20, 20, 20, 19, 19, 19, 18, 18, 18, 18, 17, 16, 16, 16, 16, 15, 14, 7, 6, 6, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, -2, -3, -6, -5, -5, -8, -16, -18, -18, -20, -20, -22, -24 }; // In units of 30 minutes
|
||||
|
||||
error_code cellRtcTickAddMinutes(ppu_thread& ppu, vm::ptr<CellRtcTick> pTick0, vm::cptr<CellRtcTick> pTick1, s64 lAdd);
|
||||
|
||||
error_code cellRtcSetTick(ppu_thread& ppu, vm::ptr<CellRtcDateTime> pTime, vm::cptr<CellRtcTick> pTick);
|
||||
constexpr u32 cellRtcGetTickResolution();
|
||||
error_code cellRtcCheckValid(vm::cptr<CellRtcDateTime> pTime);
|
||||
error_code cellRtcGetDayOfWeek(s32 year, s32 month, s32 day);
|
||||
error_code cellRtcGetTick(vm::cptr<CellRtcDateTime> pTime, vm::ptr<CellRtcTick> pTick);
|
||||
error_code cellRtcGetCurrentTick(vm::ptr<CellRtcTick> pTick);
|
||||
s32 cellRtcGetDayOfWeek(s32 year, s32 month, s32 day);
|
||||
error_code cellRtcGetTick(ppu_thread& ppu, vm::cptr<CellRtcDateTime> pTime, vm::ptr<CellRtcTick> pTick);
|
||||
error_code cellRtcGetCurrentTick(ppu_thread& ppu, vm::ptr<CellRtcTick> pTick);
|
||||
|
||||
CellRtcDateTime tick_to_date_time(u64 tick);
|
||||
u64 date_time_to_tick(CellRtcDateTime date_time);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "Emu/VFS.h"
|
||||
#include "Emu/IdManager.h"
|
||||
#include "Emu/localized_string.h"
|
||||
#include "Emu/savestate_utils.hpp"
|
||||
#include "Emu/Cell/lv2/sys_fs.h"
|
||||
#include "Emu/Cell/lv2/sys_sync.h"
|
||||
#include "Emu/Cell/lv2/sys_process.h"
|
||||
@@ -272,6 +273,14 @@ static std::vector<SaveDataEntry> get_save_entries(const std::string& base_dir,
|
||||
|
||||
static error_code select_and_delete(ppu_thread& ppu)
|
||||
{
|
||||
std::unique_lock hle_lock(g_fxo->get<hle_locks_t>(), std::try_to_lock);
|
||||
|
||||
if (!hle_lock)
|
||||
{
|
||||
ppu.state += cpu_flag::again;
|
||||
return {};
|
||||
}
|
||||
|
||||
std::unique_lock lock(g_fxo->get<savedata_manager>().mutex, std::try_to_lock);
|
||||
|
||||
if (!lock)
|
||||
@@ -699,6 +708,14 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v
|
||||
return {CELL_SAVEDATA_ERROR_PARAM, " (error %d)", ecode};
|
||||
}
|
||||
|
||||
std::unique_lock hle_lock(g_fxo->get<hle_locks_t>(), std::try_to_lock);
|
||||
|
||||
if (!hle_lock)
|
||||
{
|
||||
ppu.state += cpu_flag::again;
|
||||
return {};
|
||||
}
|
||||
|
||||
std::unique_lock lock(g_fxo->get<savedata_manager>().mutex, std::try_to_lock);
|
||||
|
||||
if (!lock)
|
||||
@@ -1104,7 +1121,7 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v
|
||||
auto delete_save = [&]()
|
||||
{
|
||||
strcpy_trunc(doneGet->dirName, save_entries[selected].dirName);
|
||||
doneGet->hddFreeSizeKB = 40 * 1024 * 1024 - 1; // Read explanation in cellHddGameCheck
|
||||
doneGet->hddFreeSizeKB = 40 * 1024 * 1024 - 256; // Read explanation in cellHddGameCheck
|
||||
doneGet->excResult = CELL_OK;
|
||||
std::memset(doneGet->reserved, 0, sizeof(doneGet->reserved));
|
||||
|
||||
@@ -1446,7 +1463,7 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v
|
||||
// funcStat is called even if the directory doesn't exist.
|
||||
}
|
||||
|
||||
statGet->hddFreeSizeKB = 40 * 1024 * 1024 - 1; // Read explanation in cellHddGameCheck
|
||||
statGet->hddFreeSizeKB = 40 * 1024 * 1024 - 256; // Read explanation in cellHddGameCheck
|
||||
statGet->isNewData = save_entry.isNew = psf.empty();
|
||||
|
||||
statGet->dir.atime = save_entry.atime = dir_info.atime;
|
||||
|
||||
@@ -134,24 +134,31 @@ error_code cellScreenShotSetOverlayImage(vm::cptr<char> srcDir, vm::cptr<char> s
|
||||
|
||||
error_code cellScreenShotEnable()
|
||||
{
|
||||
cellScreenshot.warning("cellScreenShotEnable()");
|
||||
cellScreenshot.trace("cellScreenShotEnable()");
|
||||
|
||||
auto& manager = g_fxo->get<screenshot_manager>();
|
||||
std::lock_guard lock(manager.mutex);
|
||||
|
||||
manager.is_enabled = true;
|
||||
if (!std::exchange(manager.is_enabled, true))
|
||||
{
|
||||
cellScreenshot.warning("cellScreenShotEnable(): Enabled");
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellScreenShotDisable()
|
||||
{
|
||||
cellScreenshot.warning("cellScreenShotDisable()");
|
||||
cellScreenshot.trace("cellScreenShotDisable()");
|
||||
|
||||
auto& manager = g_fxo->get<screenshot_manager>();
|
||||
std::lock_guard lock(manager.mutex);
|
||||
|
||||
manager.is_enabled = false;
|
||||
if (std::exchange(manager.is_enabled, false))
|
||||
{
|
||||
cellScreenshot.warning("cellScreenShotDisable(): Disabled");
|
||||
}
|
||||
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -469,6 +469,7 @@ error_code cellSysutilGetSystemParamString(CellSysutilParamId id, vm::ptr<char>
|
||||
case CELL_SYSUTIL_SYSTEMPARAM_ID_NICKNAME:
|
||||
{
|
||||
copy_size = CELL_SYSUTIL_SYSTEMPARAM_NICKNAME_SIZE;
|
||||
param_str = g_cfg.sys.system_name.to_string();
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "stdafx.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
#include "Emu/IdManager.h"
|
||||
#include "util/asm.hpp"
|
||||
|
||||
#include "sceNp.h"
|
||||
#include "sceNp2.h"
|
||||
@@ -59,8 +61,56 @@ void fmt_class_string<CellSysutilAvc2AttributeId>::format(std::string& out, u64
|
||||
});
|
||||
}
|
||||
|
||||
vm::ptr<CellSysutilAvc2Callback> avc2_cb{};
|
||||
vm::ptr<void> avc2_cb_arg{};
|
||||
struct avc2_settings
|
||||
{
|
||||
avc2_settings() = default;
|
||||
|
||||
avc2_settings(const avc2_settings&) = delete;
|
||||
avc2_settings& operator=(const avc2_settings&) = delete;
|
||||
|
||||
SAVESTATE_INIT_POS(52);
|
||||
|
||||
vm::ptr<CellSysutilAvc2Callback> avc2_cb{};
|
||||
vm::ptr<void> avc2_cb_arg{};
|
||||
u32 streaming_mode = CELL_SYSUTIL_AVC2_STREAMING_MODE_NORMAL;
|
||||
u8 mic_out_stream_sharing = 0;
|
||||
u8 video_stream_sharing = 0;
|
||||
u32 total_video_bitrate = 0;
|
||||
std::set<u16> voice_muting_players;
|
||||
bool voice_muting = 1;
|
||||
bool video_muting = 1;
|
||||
bool speaker_muting = 1;
|
||||
f32 speaker_volume_level = 40.0f;
|
||||
|
||||
static bool saveable(bool /*is_writing*/) noexcept
|
||||
{
|
||||
return GET_SERIALIZATION_VERSION(cellSysutil) != 0;
|
||||
}
|
||||
|
||||
avc2_settings(utils::serial& ar) noexcept
|
||||
{
|
||||
[[maybe_unused]] const s32 version = GET_SERIALIZATION_VERSION(cellSysutil);
|
||||
|
||||
if (version == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
save(ar);
|
||||
}
|
||||
|
||||
void save(utils::serial& ar)
|
||||
{
|
||||
[[maybe_unused]] const s32 version = GET_OR_USE_SERIALIZATION_VERSION(ar.is_writing(), cellSysutil);
|
||||
|
||||
ar(avc2_cb, avc2_cb_arg, streaming_mode, mic_out_stream_sharing, video_stream_sharing, total_video_bitrate);
|
||||
|
||||
if (ar.is_writing() || version >= 2)
|
||||
{
|
||||
ar(voice_muting_players, voice_muting, video_muting, speaker_muting, speaker_volume_level);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
error_code cellSysutilAvc2GetPlayerInfo(vm::cptr<SceNpMatching2RoomMemberId> player_id, vm::ptr<CellSysutilAvc2PlayerInfo> player_info)
|
||||
{
|
||||
@@ -83,13 +133,15 @@ error_code cellSysutilAvc2JoinChat(vm::cptr<SceNpMatching2RoomId> room_id, vm::p
|
||||
|
||||
// NOTE: room_id should be null if the current mode is Direct WAN/LAN
|
||||
|
||||
auto& settings = g_fxo->get<avc2_settings>();
|
||||
|
||||
[[maybe_unused]] u64 id = 0UL;
|
||||
|
||||
if (room_id)
|
||||
{
|
||||
id = *room_id;
|
||||
}
|
||||
else if (false/*streaming_mode != CELL_SYSUTIL_AVC2_STREAMING_MODE_NORMAL*/) // TODO
|
||||
else if (settings.streaming_mode != CELL_SYSUTIL_AVC2_STREAMING_MODE_NORMAL)
|
||||
{
|
||||
return CELL_AVC2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
@@ -124,7 +176,8 @@ error_code cellSysutilAvc2GetVideoMuting(vm::ptr<u8> muting)
|
||||
if (!muting)
|
||||
return CELL_AVC2_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
*muting = 1;
|
||||
const auto& settings = g_fxo->get<avc2_settings>();
|
||||
*muting = settings.video_muting;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -172,6 +225,10 @@ error_code cellSysutilAvc2StopStreaming2(u32 mediaType)
|
||||
error_code cellSysutilAvc2SetVoiceMuting(u8 muting)
|
||||
{
|
||||
cellSysutilAvc2.todo("cellSysutilAvc2SetVoiceMuting(muting=0x%x)", muting);
|
||||
|
||||
auto& settings = g_fxo->get<avc2_settings>();
|
||||
settings.voice_muting = muting;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -181,22 +238,6 @@ error_code cellSysutilAvc2StartVoiceDetection()
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellSysutilAvc2UnloadAsync()
|
||||
{
|
||||
cellSysutilAvc2.todo("cellSysutilAvc2UnloadAsync()");
|
||||
|
||||
if (avc2_cb)
|
||||
{
|
||||
sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32
|
||||
{
|
||||
avc2_cb(cb_ppu, CELL_AVC2_EVENT_UNLOAD_SUCCEEDED, 0, avc2_cb_arg);
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellSysutilAvc2StopVoiceDetection()
|
||||
{
|
||||
cellSysutilAvc2.todo("cellSysutilAvc2StopVoiceDetection()");
|
||||
@@ -241,28 +282,13 @@ error_code cellSysutilAvc2GetAttribute(vm::ptr<CellSysutilAvc2Attribute> attr)
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellSysutilAvc2LoadAsync(SceNpMatching2ContextId ctx_id, u32 container, vm::ptr<CellSysutilAvc2Callback> callback_func, vm::ptr<void> user_data, vm::cptr<CellSysutilAvc2InitParam> init_param)
|
||||
{
|
||||
cellSysutilAvc2.warning("cellSysutilAvc2LoadAsync(ctx_id=0x%x, container=0x%x, callback_func=*0x%x, user_data=*0x%x, init_param=*0x%x)", ctx_id, container, callback_func, user_data, init_param);
|
||||
|
||||
avc2_cb = callback_func;
|
||||
avc2_cb_arg = user_data;
|
||||
|
||||
if (avc2_cb)
|
||||
{
|
||||
sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32
|
||||
{
|
||||
avc2_cb(cb_ppu, CELL_AVC2_EVENT_LOAD_SUCCEEDED, 0, avc2_cb_arg);
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellSysutilAvc2SetSpeakerVolumeLevel(f32 level)
|
||||
{
|
||||
cellSysutilAvc2.todo("cellSysutilAvc2SetSpeakerVolumeLevel(level=0x%x)", level);
|
||||
|
||||
auto& settings = g_fxo->get<avc2_settings>();
|
||||
settings.speaker_volume_level = level;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -366,12 +392,27 @@ error_code cellSysutilAvc2SetVideoMuting(u8 muting)
|
||||
if (muting > 1) // Weird check, lol
|
||||
return CELL_AVC2_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
auto& settings = g_fxo->get<avc2_settings>();
|
||||
settings.video_muting = muting;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellSysutilAvc2SetPlayerVoiceMuting(SceNpMatching2RoomMemberId member_id, u8 muting)
|
||||
{
|
||||
cellSysutilAvc2.todo("cellSysutilAvc2SetPlayerVoiceMuting(member_id=0x%x, muting=0x%x)", member_id, muting);
|
||||
|
||||
auto& settings = g_fxo->get<avc2_settings>();
|
||||
|
||||
if (muting)
|
||||
{
|
||||
settings.voice_muting_players.insert(member_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
settings.voice_muting_players.erase(member_id);
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -386,7 +427,36 @@ error_code cellSysutilAvc2Unload()
|
||||
{
|
||||
cellSysutilAvc2.todo("cellSysutilAvc2Unload()");
|
||||
|
||||
// TODO: CELL_AVC2_ERROR_NOT_INITIALIZED
|
||||
auto& settings = g_fxo->get<avc2_settings>();
|
||||
|
||||
if (!settings.avc2_cb)
|
||||
{
|
||||
return CELL_AVC2_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
settings.avc2_cb = vm::null;
|
||||
settings.avc2_cb_arg = vm::null;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellSysutilAvc2UnloadAsync()
|
||||
{
|
||||
cellSysutilAvc2.todo("cellSysutilAvc2UnloadAsync()");
|
||||
|
||||
auto& settings = g_fxo->get<avc2_settings>();
|
||||
|
||||
if (settings.avc2_cb)
|
||||
{
|
||||
sysutil_register_cb([=, avc2_cb = settings.avc2_cb, avc2_cb_arg = settings.avc2_cb_arg](ppu_thread& cb_ppu) -> s32
|
||||
{
|
||||
avc2_cb(cb_ppu, CELL_AVC2_EVENT_UNLOAD_SUCCEEDED, 0, avc2_cb_arg);
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
settings.avc2_cb = vm::null;
|
||||
settings.avc2_cb_arg = vm::null;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -410,7 +480,8 @@ error_code cellSysutilAvc2GetSpeakerVolumeLevel(vm::ptr<f32> level)
|
||||
if (!level)
|
||||
return CELL_AVC2_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
*level = 100;
|
||||
const auto& settings = g_fxo->get<avc2_settings>();
|
||||
*level = settings.speaker_volume_level;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -431,7 +502,36 @@ error_code cellSysutilAvc2MicRead(vm::ptr<void> ptr, vm::ptr<u32> pSize)
|
||||
{
|
||||
cellSysutilAvc2.todo("cellSysutilAvc2MicRead(ptr=*0x%x, pSize=*0x%x)", ptr, pSize);
|
||||
|
||||
// TODO: check arguments ?
|
||||
const auto& settings = g_fxo->get<avc2_settings>();
|
||||
|
||||
if (!settings.mic_out_stream_sharing)
|
||||
return CELL_OK;
|
||||
|
||||
if (!ptr || !pSize)
|
||||
{
|
||||
// Not checked on real hardware
|
||||
cellSysutilAvc2.warning("cellSysutilAvc2MicRead: ptr or pSize is null");
|
||||
|
||||
if (pSize)
|
||||
{
|
||||
*pSize = 0;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
// TODO: ringbuffer (holds 100ms of 16kHz single channel f32 samples)
|
||||
std::vector<u8> buf{};
|
||||
|
||||
u32 size_read = 0;
|
||||
|
||||
if (u32 size_to_read = *pSize; size_to_read > 0 && !buf.empty())
|
||||
{
|
||||
size_read = std::min(size_to_read, ::size32(buf));
|
||||
std::memcpy(ptr.get_ptr(), buf.data(), size_read);
|
||||
}
|
||||
|
||||
*pSize = size_read;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -443,7 +543,8 @@ error_code cellSysutilAvc2GetPlayerVoiceMuting(SceNpMatching2RoomMemberId member
|
||||
if (!muting)
|
||||
return CELL_AVC2_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
*muting = 0;
|
||||
const auto& settings = g_fxo->get<avc2_settings>();
|
||||
*muting = settings.voice_muting_players.contains(member_id);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -454,22 +555,24 @@ error_code cellSysutilAvc2JoinChatRequest(vm::cptr<SceNpMatching2RoomId> room_id
|
||||
|
||||
// NOTE: room_id should be null if the current mode is Direct WAN/LAN
|
||||
|
||||
const auto& settings = g_fxo->get<avc2_settings>();
|
||||
|
||||
[[maybe_unused]] u64 id = 0UL;
|
||||
|
||||
if (room_id)
|
||||
{
|
||||
id = *room_id;
|
||||
}
|
||||
else if (false/*streaming_mode != CELL_SYSUTIL_AVC2_STREAMING_MODE_NORMAL*/) // TODO
|
||||
else if (settings.streaming_mode != CELL_SYSUTIL_AVC2_STREAMING_MODE_NORMAL)
|
||||
{
|
||||
return CELL_AVC2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
// TODO: join chat
|
||||
|
||||
if (avc2_cb)
|
||||
if (settings.avc2_cb)
|
||||
{
|
||||
sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32
|
||||
sysutil_register_cb([=, avc2_cb = settings.avc2_cb, avc2_cb_arg = settings.avc2_cb_arg](ppu_thread& cb_ppu) -> s32
|
||||
{
|
||||
avc2_cb(cb_ppu, CELL_AVC2_EVENT_JOIN_SUCCEEDED, 0, avc2_cb_arg);
|
||||
return 0;
|
||||
@@ -572,9 +675,11 @@ error_code cellSysutilAvc2LeaveChatRequest()
|
||||
{
|
||||
cellSysutilAvc2.notice("cellSysutilAvc2LeaveChatRequest()");
|
||||
|
||||
if (avc2_cb)
|
||||
const auto& settings = g_fxo->get<avc2_settings>();
|
||||
|
||||
if (settings.avc2_cb)
|
||||
{
|
||||
sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32
|
||||
sysutil_register_cb([=, avc2_cb = settings.avc2_cb, avc2_cb_arg = settings.avc2_cb_arg](ppu_thread& cb_ppu) -> s32
|
||||
{
|
||||
avc2_cb(cb_ppu, CELL_AVC2_EVENT_LEAVE_SUCCEEDED, 0, avc2_cb_arg);
|
||||
return 0;
|
||||
@@ -608,7 +713,8 @@ error_code cellSysutilAvc2GetSpeakerMuting(vm::ptr<u8> muting)
|
||||
if (!muting)
|
||||
return CELL_AVC2_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
*muting = 1;
|
||||
const auto& settings = g_fxo->get<avc2_settings>();
|
||||
*muting = settings.speaker_muting;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -668,12 +774,204 @@ error_code cellSysutilAvc2LeaveChat()
|
||||
error_code cellSysutilAvc2SetSpeakerMuting(u8 muting)
|
||||
{
|
||||
cellSysutilAvc2.todo("cellSysutilAvc2SetSpeakerMuting(muting=0x%x)", muting);
|
||||
|
||||
auto& settings = g_fxo->get<avc2_settings>();
|
||||
settings.speaker_muting = muting;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellSysutilAvc2Load_shared(SceNpMatching2ContextId /*ctx_id*/, u32 /*container*/, vm::ptr<CellSysutilAvc2Callback> callback_func, vm::ptr<void> user_data, vm::cptr<CellSysutilAvc2InitParam> init_param)
|
||||
{
|
||||
if (!init_param || !init_param->avc_init_param_version ||
|
||||
!(init_param->avc_init_param_version == 100 ||
|
||||
init_param->avc_init_param_version == 110 ||
|
||||
init_param->avc_init_param_version == 120 ||
|
||||
init_param->avc_init_param_version == 130 ||
|
||||
init_param->avc_init_param_version == 140)
|
||||
)
|
||||
{
|
||||
return CELL_AVC2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
auto& settings = g_fxo->get<avc2_settings>();
|
||||
|
||||
switch (init_param->media_type)
|
||||
{
|
||||
case CELL_SYSUTIL_AVC2_VOICE_CHAT:
|
||||
{
|
||||
if (init_param->max_players < 2 ||
|
||||
init_param->max_players > 64 ||
|
||||
init_param->spu_load_average > 100 ||
|
||||
init_param->voice_param.voice_quality != CELL_SYSUTIL_AVC2_VOICE_QUALITY_NORMAL ||
|
||||
init_param->voice_param.max_speakers == 0 ||
|
||||
init_param->voice_param.max_speakers > 16
|
||||
)
|
||||
{
|
||||
return CELL_AVC2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
u32 streaming_mode = init_param->direct_streaming_mode;
|
||||
|
||||
if (init_param->avc_init_param_version >= 120)
|
||||
{
|
||||
switch (init_param->direct_streaming_mode)
|
||||
{
|
||||
case CELL_SYSUTIL_AVC2_STREAMING_MODE_NORMAL:
|
||||
streaming_mode = CELL_SYSUTIL_AVC2_STREAMING_MODE_NORMAL;
|
||||
break;
|
||||
case CELL_SYSUTIL_AVC2_STREAMING_MODE_DIRECT_WAN:
|
||||
break;
|
||||
case CELL_SYSUTIL_AVC2_STREAMING_MODE_DIRECT_LAN:
|
||||
if (init_param->streaming_mode.mode == CELL_SYSUTIL_AVC2_STREAMING_MODE_NORMAL)
|
||||
{
|
||||
settings.streaming_mode = streaming_mode;
|
||||
return CELL_AVC2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return CELL_AVC2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
}
|
||||
else if (init_param->avc_init_param_version >= 110)
|
||||
{
|
||||
switch (init_param->direct_streaming_mode)
|
||||
{
|
||||
case CELL_SYSUTIL_AVC2_STREAMING_MODE_NORMAL:
|
||||
case CELL_SYSUTIL_AVC2_STREAMING_MODE_DIRECT_WAN:
|
||||
break;
|
||||
case CELL_SYSUTIL_AVC2_STREAMING_MODE_DIRECT_LAN:
|
||||
default:
|
||||
return CELL_AVC2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
streaming_mode = settings.streaming_mode;
|
||||
}
|
||||
|
||||
settings.streaming_mode = streaming_mode;
|
||||
settings.mic_out_stream_sharing = init_param->voice_param.mic_out_stream_sharing;
|
||||
|
||||
if (!callback_func)
|
||||
{
|
||||
return CELL_AVC2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (settings.avc2_cb)
|
||||
{
|
||||
return CELL_AVC2_ERROR_ALREADY_INITIALIZED;
|
||||
}
|
||||
|
||||
settings.avc2_cb = callback_func;
|
||||
settings.avc2_cb_arg = user_data;
|
||||
break;
|
||||
}
|
||||
case CELL_SYSUTIL_AVC2_VIDEO_CHAT:
|
||||
{
|
||||
if (false) // TODO: syscall to check container
|
||||
{
|
||||
return CELL_AVC2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (init_param->avc_init_param_version <= 140)
|
||||
{
|
||||
if (false) // TODO
|
||||
{
|
||||
return CELL_AVC2_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
if (callback_func)
|
||||
{
|
||||
return CELL_AVC2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (init_param->video_param.max_video_windows == 0 ||
|
||||
init_param->video_param.max_video_windows > (init_param->video_param.frame_mode == CELL_SYSUTIL_AVC2_FRAME_MODE_NORMAL ? 6 : 16))
|
||||
{
|
||||
return CELL_AVC2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (init_param->video_param.max_video_bitrate < 1000 || init_param->video_param.max_video_bitrate > 512000)
|
||||
{
|
||||
return CELL_AVC2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (init_param->video_param.max_video_framerate == 0 || init_param->video_param.max_video_framerate > 30)
|
||||
{
|
||||
return CELL_AVC2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
s32 bitrate = 0;
|
||||
|
||||
switch (init_param->video_param.max_video_resolution)
|
||||
{
|
||||
case CELL_SYSUTIL_AVC2_VIDEO_RESOLUTION_QQVGA:
|
||||
bitrate = 76800;
|
||||
break;
|
||||
case CELL_SYSUTIL_AVC2_VIDEO_RESOLUTION_QVGA:
|
||||
bitrate = 307200;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
u32 total_bitrate = 0;
|
||||
|
||||
if (bitrate != 0)
|
||||
{
|
||||
u32 window_count = init_param->video_param.max_video_windows;
|
||||
|
||||
if (init_param->video_param.video_stream_sharing == CELL_SYSUTIL_AVC2_VIDEO_SHARING_MODE_2)
|
||||
{
|
||||
window_count++;
|
||||
}
|
||||
|
||||
total_bitrate = utils::align<u32>(window_count * bitrate, 0x100000) + 0x100000;
|
||||
}
|
||||
|
||||
settings.video_stream_sharing = init_param->video_param.video_stream_sharing;
|
||||
settings.total_video_bitrate = total_bitrate;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return CELL_AVC2_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellSysutilAvc2Load(SceNpMatching2ContextId ctx_id, u32 container, vm::ptr<CellSysutilAvc2Callback> callback_func, vm::ptr<void> user_data, vm::cptr<CellSysutilAvc2InitParam> init_param)
|
||||
{
|
||||
cellSysutilAvc2.todo("cellSysutilAvc2Load(ctx_id=0x%x, container=0x%x, callback_func=*0x%x, user_data=*0x%x, init_param=*0x%x)", ctx_id, container, callback_func, user_data, init_param);
|
||||
cellSysutilAvc2.warning("cellSysutilAvc2Load(ctx_id=0x%x, container=0x%x, callback_func=*0x%x, user_data=*0x%x, init_param=*0x%x)", ctx_id, container, callback_func, user_data, init_param);
|
||||
|
||||
error_code error = cellSysutilAvc2Load_shared(ctx_id, container, callback_func, user_data, init_param);
|
||||
if (error != CELL_OK)
|
||||
return error;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellSysutilAvc2LoadAsync(SceNpMatching2ContextId ctx_id, u32 container, vm::ptr<CellSysutilAvc2Callback> callback_func, vm::ptr<void> user_data, vm::cptr<CellSysutilAvc2InitParam> init_param)
|
||||
{
|
||||
cellSysutilAvc2.warning("cellSysutilAvc2LoadAsync(ctx_id=0x%x, container=0x%x, callback_func=*0x%x, user_data=*0x%x, init_param=*0x%x)", ctx_id, container, callback_func, user_data, init_param);
|
||||
|
||||
error_code error = cellSysutilAvc2Load_shared(ctx_id, container, callback_func, user_data, init_param);
|
||||
if (error != CELL_OK)
|
||||
return error;
|
||||
|
||||
auto& settings = g_fxo->get<avc2_settings>();
|
||||
|
||||
if (settings.avc2_cb && init_param && init_param->media_type == CELL_SYSUTIL_AVC2_VOICE_CHAT)
|
||||
{
|
||||
sysutil_register_cb([=, avc2_cb = settings.avc2_cb, avc2_cb_arg = settings.avc2_cb_arg](ppu_thread& cb_ppu) -> s32
|
||||
{
|
||||
avc2_cb(cb_ppu, CELL_AVC2_EVENT_LOAD_SUCCEEDED, 0, avc2_cb_arg);
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -718,6 +1016,39 @@ error_code cellSysutilAvc2SetAttribute(vm::cptr<CellSysutilAvc2Attribute> attr)
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellSysutilAvc2Unload2(u32 mediaType)
|
||||
{
|
||||
cellSysutilAvc2.todo("cellSysutilAvc2Unload2(mediaType=0x%x)", mediaType);
|
||||
|
||||
auto& settings = g_fxo->get<avc2_settings>();
|
||||
|
||||
switch (mediaType)
|
||||
{
|
||||
case CELL_SYSUTIL_AVC2_VOICE_CHAT:
|
||||
{
|
||||
if (!settings.avc2_cb)
|
||||
{
|
||||
return CELL_AVC2_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
// TODO: return error if the video chat is still loaded (probably CELL_AVC2_ERROR_INVALID_STATUS)
|
||||
|
||||
settings.avc2_cb = vm::null;
|
||||
settings.avc2_cb_arg = vm::null;
|
||||
break;
|
||||
}
|
||||
case CELL_SYSUTIL_AVC2_VIDEO_CHAT:
|
||||
{
|
||||
// TODO
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return CELL_AVC2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellSysutilAvc2UnloadAsync2(u32 mediaType)
|
||||
{
|
||||
cellSysutilAvc2.todo("cellSysutilAvc2UnloadAsync2(mediaType=0x%x)", mediaType);
|
||||
@@ -725,6 +1056,32 @@ error_code cellSysutilAvc2UnloadAsync2(u32 mediaType)
|
||||
if (mediaType != CELL_SYSUTIL_AVC2_VOICE_CHAT && mediaType != CELL_SYSUTIL_AVC2_VIDEO_CHAT)
|
||||
return CELL_AVC2_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
auto& settings = g_fxo->get<avc2_settings>();
|
||||
|
||||
if (settings.avc2_cb)
|
||||
{
|
||||
if (mediaType == CELL_SYSUTIL_AVC2_VOICE_CHAT)
|
||||
{
|
||||
// TODO: signal CELL_AVC2_EVENT_UNLOAD_FAILED instead if the video chat is still loaded
|
||||
}
|
||||
|
||||
sysutil_register_cb([=, avc2_cb = settings.avc2_cb, avc2_cb_arg = settings.avc2_cb_arg](ppu_thread& cb_ppu) -> s32
|
||||
{
|
||||
avc2_cb(cb_ppu, CELL_AVC2_EVENT_UNLOAD_SUCCEEDED, 0, avc2_cb_arg);
|
||||
return 0;
|
||||
});
|
||||
|
||||
if (mediaType == CELL_SYSUTIL_AVC2_VOICE_CHAT)
|
||||
{
|
||||
settings.avc2_cb = vm::null;
|
||||
settings.avc2_cb_arg = vm::null;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: unload video chat.
|
||||
}
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -757,7 +1114,8 @@ error_code cellSysutilAvc2GetVoiceMuting(vm::ptr<u8> muting)
|
||||
if (!muting)
|
||||
return CELL_AVC2_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
*muting = 1;
|
||||
const auto& settings = g_fxo->get<avc2_settings>();
|
||||
*muting = settings.voice_muting;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -774,16 +1132,6 @@ error_code cellSysutilAvc2GetScreenShowStatus(vm::ptr<u8> visible)
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellSysutilAvc2Unload2(u32 mediaType)
|
||||
{
|
||||
cellSysutilAvc2.todo("cellSysutilAvc2Unload2(mediaType=0x%x)", mediaType);
|
||||
|
||||
if (mediaType != CELL_SYSUTIL_AVC2_VOICE_CHAT && mediaType != CELL_SYSUTIL_AVC2_VIDEO_CHAT)
|
||||
return CELL_AVC2_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellSysutilAvc2GetWindowPosition(SceNpMatching2RoomMemberId member_id, vm::ptr<f32> x, vm::ptr<f32> y, vm::ptr<f32> z)
|
||||
{
|
||||
cellSysutilAvc2.todo("cellSysutilAvc2GetWindowPosition(member_id=0x%x, x=*0x%x, y=*0x%x, z=*0x%x)", member_id, x, y, z);
|
||||
|
||||
@@ -233,8 +233,8 @@ struct CellSysutilAvc2InitParam
|
||||
be_t<u16> direct_streaming_mode;
|
||||
CellSysutilAvc2StreamingModeParam streaming_mode;
|
||||
};
|
||||
be_t<u32> media_type;
|
||||
u8 reserved[18];
|
||||
be_t<u32> media_type;
|
||||
CellSysutilAvc2VoiceInitParam voice_param;
|
||||
CellSysutilAvc2VideoInitParam video_param;
|
||||
u8 reserved2[22];
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
#include "Emu/Cell/lv2/sys_sync.h"
|
||||
#include "Emu/Cell/lv2/sys_ppu_thread.h"
|
||||
#include "Emu/Cell/lv2/sys_process.h"
|
||||
#include "Emu/savestate_utils.hpp"
|
||||
#include "sysPrxForUser.h"
|
||||
#include "util/media_utils.h"
|
||||
#include "util/init_mutex.hpp"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push, 0)
|
||||
@@ -625,38 +625,16 @@ struct vdec_context final
|
||||
}
|
||||
};
|
||||
|
||||
struct vdec_creation_lock
|
||||
extern bool check_if_vdec_contexts_exist()
|
||||
{
|
||||
stx::init_mutex locked;
|
||||
bool context_exists = false;
|
||||
|
||||
vdec_creation_lock()
|
||||
idm::select<vdec_context>([&](u32, vdec_context&)
|
||||
{
|
||||
auto lk = locked.init();
|
||||
}
|
||||
};
|
||||
context_exists = true;
|
||||
});
|
||||
|
||||
extern bool try_lock_vdec_context_creation()
|
||||
{
|
||||
auto& lock = g_fxo->get<vdec_creation_lock>();
|
||||
auto reset = lock.locked.reset();
|
||||
|
||||
if (reset)
|
||||
{
|
||||
bool context_exists = false;
|
||||
|
||||
idm::select<vdec_context>([&](u32, vdec_context&)
|
||||
{
|
||||
context_exists = true;
|
||||
});
|
||||
|
||||
if (context_exists)
|
||||
{
|
||||
reset.set_init();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return context_exists;
|
||||
}
|
||||
|
||||
extern void vdecEntry(ppu_thread& ppu, u32 vid)
|
||||
@@ -890,7 +868,7 @@ static error_code vdecOpen(ppu_thread& ppu, T type, U res, vm::cptr<CellVdecCb>
|
||||
// Create decoder context
|
||||
std::shared_ptr<vdec_context> vdec;
|
||||
|
||||
if (auto access = g_fxo->get<vdec_creation_lock>().locked.access(); access)
|
||||
if (std::unique_lock lock{g_fxo->get<hle_locks_t>(), std::try_to_lock})
|
||||
{
|
||||
vdec = idm::make_ptr<vdec_context>(type->codecType, type->profileLevel, res->memAddr, res->memSize, cb->cbFunc, cb->cbArg);
|
||||
}
|
||||
@@ -943,6 +921,14 @@ error_code cellVdecClose(ppu_thread& ppu, u32 handle)
|
||||
{
|
||||
cellVdec.warning("cellVdecClose(handle=0x%x)", handle);
|
||||
|
||||
std::unique_lock lock_hle{g_fxo->get<hle_locks_t>(), std::try_to_lock};
|
||||
|
||||
if (!lock_hle)
|
||||
{
|
||||
ppu.state += cpu_flag::again;
|
||||
return {};
|
||||
}
|
||||
|
||||
auto vdec = idm::get<vdec_context>(handle);
|
||||
|
||||
if (!vdec)
|
||||
|
||||
@@ -3896,7 +3896,7 @@ error_code sceNpManagerGetAvatarUrl(vm::ptr<SceNpAvatarUrl> avatarUrl)
|
||||
|
||||
error_code sceNpManagerGetMyLanguages(vm::ptr<SceNpMyLanguages> myLanguages)
|
||||
{
|
||||
sceNp.warning("sceNpManagerGetMyLanguages(myLanguages=*0x%x)", myLanguages);
|
||||
sceNp.trace("sceNpManagerGetMyLanguages(myLanguages=*0x%x)", myLanguages);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
@@ -5834,8 +5834,8 @@ error_code scenp_score_get_ranking_by_npid(s32 transId, SceNpScoreBoardId boardI
|
||||
|
||||
std::vector<std::pair<SceNpId, s32>> npid_vec;
|
||||
|
||||
static constexpr bool is_npid = std::is_same<T, vm::cptr<SceNpId>>::value;
|
||||
static constexpr bool is_npidpcid = std::is_same<T, vm::cptr<SceNpScoreNpIdPcId>>::value;
|
||||
static constexpr bool is_npid = std::is_same_v<T, vm::cptr<SceNpId>>;
|
||||
static constexpr bool is_npidpcid = std::is_same_v<T, vm::cptr<SceNpScoreNpIdPcId>>;
|
||||
static_assert(is_npid || is_npidpcid, "T should be vm::cptr<SceNpId> or vm::cptr<SceNpScoreNpIdPcId>");
|
||||
|
||||
if constexpr (is_npid)
|
||||
|
||||
@@ -986,7 +986,7 @@ error_code sceNpTrophyGetLatestTrophies()
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpTrophyUnlockTrophy(u32 context, u32 handle, s32 trophyId, vm::ptr<u32> platinumId)
|
||||
error_code sceNpTrophyUnlockTrophy(ppu_thread& ppu, u32 context, u32 handle, s32 trophyId, vm::ptr<u32> platinumId)
|
||||
{
|
||||
sceNpTrophy.warning("sceNpTrophyUnlockTrophy(context=0x%x, handle=0x%x, trophyId=%d, platinumId=*0x%x)", context, handle, trophyId, platinumId);
|
||||
|
||||
@@ -1028,7 +1028,7 @@ error_code sceNpTrophyUnlockTrophy(u32 context, u32 handle, s32 trophyId, vm::pt
|
||||
}
|
||||
|
||||
vm::var<CellRtcTick> tick;
|
||||
if (error_code error = cellRtcGetCurrentTick(tick))
|
||||
if (error_code error = cellRtcGetCurrentTick(ppu, tick))
|
||||
{
|
||||
sceNpTrophy.error("sceNpTrophyUnlockTrophy: Failed to get timestamp: 0x%x", +error);
|
||||
}
|
||||
|
||||
@@ -241,28 +241,32 @@ protected:
|
||||
{
|
||||
fmt::append(last_opcode, "%-*s cr%d,cr%d", PadOp(), op, cr0, cr1);
|
||||
}
|
||||
void DisAsm_INT3(std::string_view op, const int i0, const int i1, const int i2)
|
||||
void DisAsm_INT3(std::string_view op, s32 i0, s32 i1, s32 i2)
|
||||
{
|
||||
fmt::append(last_opcode, "%-*s %d,%d,%d", PadOp(), op, i0, i1, i2);
|
||||
}
|
||||
void DisAsm_INT1(std::string_view op, const int i0)
|
||||
void DisAsm_INT1(std::string_view op, s32 i0)
|
||||
{
|
||||
fmt::append(last_opcode, "%-*s %d", PadOp(), op, i0);
|
||||
}
|
||||
void DisAsm_BRANCH(std::string_view op, const int pc)
|
||||
void DisAsm_BRANCH(std::string_view op, s32 pc)
|
||||
{
|
||||
fmt::append(last_opcode, "%-*s 0x%x", PadOp(), op, DisAsmBranchTarget(pc));
|
||||
}
|
||||
void DisAsm_BRANCH_A(std::string_view op, const int pc)
|
||||
void DisAsm_BRANCH_A(std::string_view op, s32 pc)
|
||||
{
|
||||
fmt::append(last_opcode, "%-*s 0x%x", PadOp(), op, pc);
|
||||
}
|
||||
void DisAsm_B2_BRANCH(std::string_view op, u32 b0, u32 b1, const int pc)
|
||||
void DisAsm_B2_BRANCH(std::string_view op, u32 b0, u32 b1, s32 pc)
|
||||
{
|
||||
fmt::append(last_opcode, "%-*s %d,%d,0x%x ", PadOp(), op, b0, b1, DisAsmBranchTarget(pc));
|
||||
}
|
||||
void DisAsm_CR_BRANCH(std::string_view op, u32 cr, const int pc)
|
||||
void DisAsm_CR_BRANCH(std::string_view op, u32 cr, s32 pc)
|
||||
{
|
||||
fmt::append(last_opcode, "%-*s cr%d,0x%x ", PadOp(), op, cr, DisAsmBranchTarget(pc));
|
||||
}
|
||||
void DisAsm_CR_BRANCH_HINT(std::string_view op, u32 cr, u32 bh)
|
||||
{
|
||||
fmt::append(last_opcode, "%-*s cr%d,0x%x ", PadOp(), op, cr, bh);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -696,7 +696,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
|
||||
const vm::cptr<void> seg_end = vm::cast(seg.addr + seg.size - 4);
|
||||
auto ptr = get_ptr<u32>(_ptr);
|
||||
|
||||
for (vm::cptr<u32> _ptr = vm::cast(seg.addr); _ptr <= seg_end; advance(_ptr, ptr, 1))
|
||||
for (; _ptr <= seg_end; advance(_ptr, ptr, 1))
|
||||
{
|
||||
const u32 value = *ptr;
|
||||
|
||||
@@ -929,6 +929,38 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
|
||||
}
|
||||
}
|
||||
|
||||
if (func_queue.empty() && segs[0].size >= 4u)
|
||||
{
|
||||
// Fallback, identify functions using callers (no jumptable detection, tail calls etc)
|
||||
ppu_log.warning("Looking for PPU functions using callers. ('%s')", name);
|
||||
|
||||
vm::cptr<u32> _ptr = vm::cast(start);
|
||||
const vm::cptr<void> seg_end = vm::cast(end - 4);
|
||||
|
||||
for (auto ptr = get_ptr<u32>(_ptr); _ptr <= seg_end; advance(_ptr, ptr, 1))
|
||||
{
|
||||
const u32 iaddr = _ptr.addr();
|
||||
|
||||
const ppu_opcode_t op{*ptr};
|
||||
const ppu_itype::type type = s_ppu_itype.decode(op.opcode);
|
||||
|
||||
if (type == ppu_itype::B && op.lk && !op.aa)
|
||||
{
|
||||
const u32 target = iaddr + op.bt24;
|
||||
|
||||
if (target >= start && target < end && target != iaddr && target != iaddr + 4)
|
||||
{
|
||||
// TODO: Check full executability
|
||||
if (s_ppu_itype.decode(get_ref<u32>(target)) != ppu_itype::UNK)
|
||||
{
|
||||
ppu_log.trace("Enqueued PPU function 0x%x using a caller at 0x%x", target, iaddr);
|
||||
add_func(target, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Main loop (func_queue may grow)
|
||||
for (usz i = 0; i < func_queue.size(); i++)
|
||||
{
|
||||
@@ -1631,7 +1663,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
|
||||
}
|
||||
}
|
||||
|
||||
ppu_log.notice("Function analysis: %zu functions (%zu enqueued)", fmap.size(), func_queue.size());
|
||||
(fmap.empty() ? ppu_log.error : ppu_log.notice)("Function analysis: %zu functions (%zu enqueued)", fmap.size(), func_queue.size());
|
||||
|
||||
// Decompose functions to basic blocks
|
||||
if (!entry && !sec_end)
|
||||
|
||||
@@ -27,8 +27,8 @@ namespace ppu_cb_detail
|
||||
struct _func_arg
|
||||
{
|
||||
static_assert(type == ARG_GENERAL, "Unknown callback argument type");
|
||||
static_assert(!std::is_pointer<T>::value, "Invalid callback argument type (pointer)");
|
||||
static_assert(!std::is_reference<T>::value, "Invalid callback argument type (reference)");
|
||||
static_assert(!std::is_pointer_v<T>, "Invalid callback argument type (pointer)");
|
||||
static_assert(!std::is_reference_v<T>, "Invalid callback argument type (reference)");
|
||||
static_assert(sizeof(T) <= 8, "Invalid callback argument type for ARG_GENERAL");
|
||||
|
||||
static inline void set_value(ppu_thread& CPU, const T& arg)
|
||||
@@ -51,7 +51,7 @@ namespace ppu_cb_detail
|
||||
template<typename T, u32 g_count, u32 f_count, u32 v_count>
|
||||
struct _func_arg<T, ARG_VECTOR, g_count, f_count, v_count>
|
||||
{
|
||||
static_assert(std::is_same<std::decay_t<T>, v128>::value, "Invalid callback argument type for ARG_VECTOR");
|
||||
static_assert(std::is_same_v<std::decay_t<T>, v128>, "Invalid callback argument type for ARG_VECTOR");
|
||||
|
||||
static inline void set_value(ppu_thread& CPU, const T& arg)
|
||||
{
|
||||
@@ -75,7 +75,7 @@ namespace ppu_cb_detail
|
||||
template<typename T, u32 g_count, u32 f_count, u32 v_count>
|
||||
struct _func_arg<T, ARG_CONTEXT, g_count, f_count, v_count>
|
||||
{
|
||||
static_assert(std::is_same<std::decay_t<T>, ppu_thread>::value, "Invalid callback argument type for ARG_CONTEXT");
|
||||
static_assert(std::is_same_v<std::decay_t<T>, ppu_thread>, "Invalid callback argument type for ARG_CONTEXT");
|
||||
|
||||
FORCE_INLINE static void set_value(ppu_thread&, const T&)
|
||||
{
|
||||
@@ -92,9 +92,9 @@ namespace ppu_cb_detail
|
||||
template<u32 g_count, u32 f_count, u32 v_count, typename T1, typename... T>
|
||||
FORCE_INLINE static bool _bind_func_args(ppu_thread& CPU, T1 arg1, T... args)
|
||||
{
|
||||
const bool is_float = std::is_floating_point<T1>::value;
|
||||
const bool is_vector = std::is_same<std::decay_t<T1>, v128>::value;
|
||||
const bool is_context = std::is_same<std::decay_t<T1>, ppu_thread>::value;
|
||||
const bool is_float = std::is_floating_point_v<T1>;
|
||||
const bool is_vector = std::is_same_v<std::decay_t<T1>, v128>;
|
||||
const bool is_context = std::is_same_v<std::decay_t<T1>, ppu_thread>;
|
||||
const bool is_general = !is_float && !is_vector && !is_context;
|
||||
|
||||
const _func_arg_type t =
|
||||
@@ -140,7 +140,7 @@ namespace ppu_cb_detail
|
||||
template<typename T>
|
||||
struct _func_res<T, ARG_VECTOR>
|
||||
{
|
||||
static_assert(std::is_same<std::decay_t<T>, v128>::value, "Invalid callback result type for ARG_VECTOR");
|
||||
static_assert(std::is_same_v<std::decay_t<T>, v128>, "Invalid callback result type for ARG_VECTOR");
|
||||
|
||||
FORCE_INLINE static T get_value(const ppu_thread& CPU)
|
||||
{
|
||||
@@ -155,10 +155,10 @@ namespace ppu_cb_detail
|
||||
{
|
||||
_func_caller<void, T...>::call(CPU, pc, rtoc, args...);
|
||||
|
||||
static_assert(!std::is_pointer<RT>::value, "Invalid callback result type (pointer)");
|
||||
static_assert(!std::is_reference<RT>::value, "Invalid callback result type (reference)");
|
||||
const bool is_float = std::is_floating_point<RT>::value;
|
||||
const bool is_vector = std::is_same<std::decay_t<RT>, v128>::value;
|
||||
static_assert(!std::is_pointer_v<RT>, "Invalid callback result type (pointer)");
|
||||
static_assert(!std::is_reference_v<RT>, "Invalid callback result type (reference)");
|
||||
const bool is_float = std::is_floating_point_v<RT>;
|
||||
const bool is_vector = std::is_same_v<std::decay_t<RT>, v128>;
|
||||
const _func_arg_type t = is_float ? ARG_FLOAT : (is_vector ? ARG_VECTOR : ARG_GENERAL);
|
||||
|
||||
return _func_res<RT, t>::get_value(CPU);
|
||||
|
||||
@@ -1488,7 +1488,7 @@ void PPUDisAsm::BCLR(ppu_opcode_t op)
|
||||
return;
|
||||
}
|
||||
|
||||
DisAsm_CR_BRANCH(final, bi / 4, bh);
|
||||
DisAsm_CR_BRANCH_HINT(final, bi / 4, bh);
|
||||
}
|
||||
|
||||
void PPUDisAsm::CRNOR(ppu_opcode_t op)
|
||||
@@ -1586,7 +1586,7 @@ void PPUDisAsm::BCCTR(ppu_opcode_t op)
|
||||
final += lk ? "ctrl"sv : "ctr"sv;
|
||||
if (sign) final += sign;
|
||||
|
||||
DisAsm_CR_BRANCH(final, bi / 4, bh);
|
||||
DisAsm_CR_BRANCH_HINT(final, bi / 4, bh);
|
||||
}
|
||||
|
||||
void PPUDisAsm::RLWIMI(ppu_opcode_t op)
|
||||
|
||||
@@ -47,8 +47,8 @@ namespace ppu_func_detail
|
||||
struct bind_arg
|
||||
{
|
||||
static_assert(type == ARG_GENERAL, "Unknown function argument type");
|
||||
static_assert(!std::is_pointer<T>::value, "Invalid function argument type (pointer)");
|
||||
static_assert(!std::is_reference<T>::value, "Invalid function argument type (reference)");
|
||||
static_assert(!std::is_pointer_v<T>, "Invalid function argument type (pointer)");
|
||||
static_assert(!std::is_reference_v<T>, "Invalid function argument type (reference)");
|
||||
static_assert(sizeof(T) <= 8, "Invalid function argument type for ARG_GENERAL");
|
||||
|
||||
static inline T get_arg(ppu_thread& ppu)
|
||||
@@ -71,7 +71,7 @@ namespace ppu_func_detail
|
||||
template<typename T, u32 g_count, u32 f_count, u32 v_count>
|
||||
struct bind_arg<T, ARG_VECTOR, g_count, f_count, v_count>
|
||||
{
|
||||
static_assert(std::is_same<std::decay_t<T>, v128>::value, "Invalid function argument type for ARG_VECTOR");
|
||||
static_assert(std::is_same_v<std::decay_t<T>, v128>, "Invalid function argument type for ARG_VECTOR");
|
||||
|
||||
static FORCE_INLINE T get_arg(ppu_thread& ppu)
|
||||
{
|
||||
@@ -93,7 +93,7 @@ namespace ppu_func_detail
|
||||
template<typename T, u32 g_count, u32 f_count, u32 v_count>
|
||||
struct bind_arg<T, ARG_CONTEXT, g_count, f_count, v_count>
|
||||
{
|
||||
static_assert(std::is_base_of<std::decay_t<T>, ppu_thread>::value, "Invalid function argument type for ARG_CONTEXT");
|
||||
static_assert(std::is_base_of_v<std::decay_t<T>, ppu_thread>, "Invalid function argument type for ARG_CONTEXT");
|
||||
|
||||
static FORCE_INLINE T& get_arg(ppu_thread& ppu)
|
||||
{
|
||||
@@ -104,7 +104,7 @@ namespace ppu_func_detail
|
||||
template<typename T, u32 g_count, u32 f_count, u32 v_count>
|
||||
struct bind_arg<T, ARG_VARIADIC, g_count, f_count, v_count>
|
||||
{
|
||||
static_assert(std::is_same<std::decay_t<T>, ppu_va_args_t>::value, "Invalid function argument type for ARG_VARIADIC");
|
||||
static_assert(std::is_same_v<std::decay_t<T>, ppu_va_args_t>, "Invalid function argument type for ARG_VARIADIC");
|
||||
|
||||
static FORCE_INLINE ppu_va_args_t get_arg(ppu_thread&)
|
||||
{
|
||||
@@ -140,7 +140,7 @@ namespace ppu_func_detail
|
||||
template<typename T>
|
||||
struct bind_result<T, ARG_VECTOR>
|
||||
{
|
||||
static_assert(std::is_same<std::decay_t<T>, v128>::value, "Invalid function result type for ARG_VECTOR");
|
||||
static_assert(std::is_same_v<std::decay_t<T>, v128>, "Invalid function result type for ARG_VECTOR");
|
||||
|
||||
static FORCE_INLINE void put_result(ppu_thread& ppu, const T& result)
|
||||
{
|
||||
@@ -192,10 +192,10 @@ namespace ppu_func_detail
|
||||
const u32 v_count = (info.last_value >> 24);
|
||||
|
||||
// TODO: check calculations
|
||||
const bool is_float = std::is_floating_point<T>::value;
|
||||
const bool is_vector = std::is_same<std::decay_t<T>, v128>::value;
|
||||
const bool is_context = std::is_base_of<std::decay_t<T>, ppu_thread>::value;
|
||||
const bool is_variadic = std::is_same<std::decay_t<T>, ppu_va_args_t>::value;
|
||||
const bool is_float = std::is_floating_point_v<T>;
|
||||
const bool is_vector = std::is_same_v<std::decay_t<T>, v128>;
|
||||
const bool is_context = std::is_base_of_v<std::decay_t<T>, ppu_thread>;
|
||||
const bool is_variadic = std::is_same_v<std::decay_t<T>, ppu_va_args_t>;
|
||||
const bool is_general = !is_float && !is_vector && !is_context && !is_variadic;
|
||||
|
||||
const arg_class t =
|
||||
@@ -216,10 +216,10 @@ namespace ppu_func_detail
|
||||
template<typename RT>
|
||||
struct result_type
|
||||
{
|
||||
static_assert(!std::is_pointer<RT>::value, "Invalid function result type (pointer)");
|
||||
static_assert(!std::is_reference<RT>::value, "Invalid function result type (reference)");
|
||||
static const bool is_float = std::is_floating_point<RT>::value;
|
||||
static const bool is_vector = std::is_same<std::decay_t<RT>, v128>::value;
|
||||
static_assert(!std::is_pointer_v<RT>, "Invalid function result type (pointer)");
|
||||
static_assert(!std::is_reference_v<RT>, "Invalid function result type (reference)");
|
||||
static const bool is_float = std::is_floating_point_v<RT>;
|
||||
static const bool is_vector = std::is_same_v<std::decay_t<RT>, v128>;
|
||||
static const arg_class value = is_float ? ARG_FLOAT : (is_vector ? ARG_VECTOR : ARG_GENERAL);
|
||||
};
|
||||
|
||||
|
||||
@@ -1646,7 +1646,7 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, bool virtual_lo
|
||||
|
||||
if (ar)
|
||||
{
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (rtype)
|
||||
@@ -2862,7 +2862,7 @@ std::pair<std::shared_ptr<lv2_overlay>, CellError> ppu_load_overlay(const ppu_ex
|
||||
}
|
||||
|
||||
// Apply the patch
|
||||
auto applied = g_fxo->get<patch_engine>().apply(!Emu.DeserialManager() ? std::string{} : hash, [ovlm](u32 addr, u32 size) { return ovlm->get_ptr<u8>(addr, size); });
|
||||
auto applied = g_fxo->get<patch_engine>().apply(!Emu.DeserialManager() ? hash : std::string{}, [ovlm](u32 addr, u32 size) { return ovlm->get_ptr<u8>(addr, size); });
|
||||
|
||||
if (!Emu.DeserialManager() && !Emu.GetTitleID().empty())
|
||||
{
|
||||
|
||||
@@ -147,7 +147,7 @@ public:
|
||||
{
|
||||
using gvar = std::decay_t<decltype(*Var)>;
|
||||
|
||||
static_assert(std::is_same<u32, typename gvar::addr_type>::value, "Static variable registration: vm::gvar<T> expected");
|
||||
static_assert(std::is_same_v<u32, typename gvar::addr_type>, "Static variable registration: vm::gvar<T> expected");
|
||||
|
||||
auto& info = access_static_variable(_module, vnid);
|
||||
|
||||
@@ -289,9 +289,9 @@ inline RT ppu_execute(ppu_thread& ppu, Args... args)
|
||||
return func(ppu, args...);
|
||||
}
|
||||
|
||||
#define BIND_FUNC_WITH_BLR(func) BIND_FUNC(func, if (cpu_flag::again - ppu.state) ppu.cia = static_cast<u32>(ppu.lr) & ~3)
|
||||
#define BIND_FUNC_WITH_BLR(func, _module) BIND_FUNC(func, if (cpu_flag::again - ppu.state) ppu.cia = static_cast<u32>(ppu.lr) & ~3; else ppu.current_module = _module)
|
||||
|
||||
#define REG_FNID(_module, nid, func) ppu_module_manager::register_static_function<&func>(#_module, ppu_select_name(#func, nid), BIND_FUNC_WITH_BLR(func), ppu_generate_id(nid))
|
||||
#define REG_FNID(_module, nid, func) ppu_module_manager::register_static_function<&func>(#_module, ppu_select_name(#func, nid), BIND_FUNC_WITH_BLR(func, #_module), ppu_generate_id(nid))
|
||||
|
||||
#define REG_FUNC(_module, func) REG_FNID(_module, #func, func)
|
||||
|
||||
@@ -299,7 +299,7 @@ inline RT ppu_execute(ppu_thread& ppu, Args... args)
|
||||
|
||||
#define REG_VAR(_module, var) REG_VNID(_module, #var, var)
|
||||
|
||||
#define REG_HIDDEN_FUNC(func) ppu_function_manager::register_function<decltype(&func), &func>(BIND_FUNC_WITH_BLR(func))
|
||||
#define REG_HIDDEN_FUNC(func) ppu_function_manager::register_function<decltype(&func), &func>(BIND_FUNC_WITH_BLR(func, ""))
|
||||
|
||||
#define REG_HIDDEN_FUNC_PURE(func) ppu_function_manager::register_function<decltype(&func), &func>(func)
|
||||
|
||||
|
||||
+387
-163
@@ -32,25 +32,26 @@
|
||||
#pragma warning(push, 0)
|
||||
#else
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wall"
|
||||
#pragma GCC diagnostic ignored "-Wextra"
|
||||
#pragma GCC diagnostic ignored "-Wold-style-cast"
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
|
||||
#pragma GCC diagnostic ignored "-Weffc++"
|
||||
#pragma GCC diagnostic ignored "-Wmissing-noreturn"
|
||||
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
|
||||
#endif
|
||||
#include "llvm/Support/FormattedStream.h"
|
||||
#include "llvm/TargetParser/Host.h"
|
||||
#include "llvm/Object/ObjectFile.h"
|
||||
#include <llvm/IR/Verifier.h>
|
||||
#include <llvm/Transforms/Utils/BasicBlockUtils.h>
|
||||
#if LLVM_VERSION_MAJOR < 17
|
||||
#include "llvm/ADT/Triple.h"
|
||||
#include <llvm/Support/FormattedStream.h>
|
||||
#include <llvm/TargetParser/Host.h>
|
||||
#include <llvm/Object/ObjectFile.h>
|
||||
#include <llvm/IR/InstIterator.h>
|
||||
#include <llvm/IR/LegacyPassManager.h>
|
||||
#include <llvm/Transforms/Scalar.h>
|
||||
#else
|
||||
#include <llvm/Analysis/CGSCCPassManager.h>
|
||||
#include <llvm/Analysis/LoopAnalysisManager.h>
|
||||
#include <llvm/Passes/PassBuilder.h>
|
||||
#include <llvm/Transforms/Scalar/EarlyCSE.h>
|
||||
#endif
|
||||
#include "llvm/IR/Verifier.h"
|
||||
#include "llvm/IR/InstIterator.h"
|
||||
#include "llvm/IR/LegacyPassManager.h"
|
||||
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
|
||||
#include "llvm/Transforms/Scalar.h"
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#else
|
||||
@@ -60,7 +61,6 @@
|
||||
#include "PPUTranslator.h"
|
||||
#endif
|
||||
|
||||
#include <thread>
|
||||
#include <cfenv>
|
||||
#include <cctype>
|
||||
#include <span>
|
||||
@@ -173,9 +173,9 @@ bool serialize<ppu_thread::cr_bits>(utils::serial& ar, typename ppu_thread::cr_b
|
||||
}
|
||||
|
||||
extern void ppu_initialize();
|
||||
extern void ppu_finalize(const ppu_module& info);
|
||||
extern void ppu_finalize(const ppu_module& info, bool force_mem_release = false);
|
||||
extern bool ppu_initialize(const ppu_module& info, bool check_only = false, u64 file_size = 0);
|
||||
static void ppu_initialize2(class jit_compiler& jit, const ppu_module& module_part, const std::string& cache_path, const std::string& obj_name);
|
||||
static void ppu_initialize2(class jit_compiler& jit, const ppu_module& module_part, const std::string& cache_path, const std::string& obj_name, const ppu_module& whole_module);
|
||||
extern bool ppu_load_exec(const ppu_exec_object&, bool virtual_load, const std::string&, utils::serial* = nullptr);
|
||||
extern std::pair<std::shared_ptr<lv2_overlay>, CellError> ppu_load_overlay(const ppu_exec_object&, bool virtual_load, const std::string& path, s64 file_offset, utils::serial* = nullptr);
|
||||
extern void ppu_unload_prx(const lv2_prx&);
|
||||
@@ -413,14 +413,14 @@ const auto ppu_recompiler_fallback_ghc = &ppu_recompiler_fallback;
|
||||
#endif
|
||||
|
||||
// Get pointer to executable cache
|
||||
static ppu_intrp_func_t* ppu_ptr(u32 addr)
|
||||
static inline u8* ppu_ptr(u32 addr)
|
||||
{
|
||||
return reinterpret_cast<ppu_intrp_func_t*>(vm::g_exec_addr + u64{addr} * 2);
|
||||
return vm::g_exec_addr + u64{addr} * 2;
|
||||
}
|
||||
|
||||
static ppu_intrp_func_t& ppu_ref(u32 addr)
|
||||
static inline ppu_intrp_func_t ppu_read(u32 addr)
|
||||
{
|
||||
return *ppu_ptr(addr);
|
||||
return read_from_ptr<ppu_intrp_func_t>(ppu_ptr(addr));
|
||||
}
|
||||
|
||||
// Get interpreter cache value
|
||||
@@ -445,7 +445,7 @@ static void ppu_fallback(ppu_thread& ppu, ppu_opcode_t op, be_t<u32>* this_op, p
|
||||
{
|
||||
const auto _pc = vm::get_addr(this_op);
|
||||
const auto _fn = ppu_cache(_pc);
|
||||
ppu_ref(_pc) = _fn;
|
||||
write_to_ptr<ppu_intrp_func_t>(ppu_ptr(_pc), _fn);
|
||||
return _fn(ppu, op, this_op, next_fn);
|
||||
}
|
||||
|
||||
@@ -463,7 +463,7 @@ void ppu_recompiler_fallback(ppu_thread& ppu)
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (uptr func = uptr(ppu_ref(ppu.cia)); (func << 16 >> 16) != reinterpret_cast<uptr>(ppu_recompiler_fallback_ghc))
|
||||
if (uptr func = uptr(ppu_read(ppu.cia)); (func << 16 >> 16) != reinterpret_cast<uptr>(ppu_recompiler_fallback_ghc))
|
||||
{
|
||||
// We found a recompiler function at cia, return
|
||||
break;
|
||||
@@ -730,11 +730,12 @@ extern void ppu_register_range(u32 addr, u32 size)
|
||||
if (g_cfg.core.ppu_decoder == ppu_decoder_type::llvm)
|
||||
{
|
||||
// Assume addr is the start of first segment of PRX
|
||||
ppu_ref(addr) = reinterpret_cast<ppu_intrp_func_t>(reinterpret_cast<uptr>(ppu_recompiler_fallback_ghc) | (seg_base << (32 + 3)));
|
||||
const uptr entry_value = reinterpret_cast<uptr>(ppu_recompiler_fallback_ghc) | (seg_base << (32 + 3));
|
||||
write_to_ptr<uptr>(ppu_ptr(addr), entry_value);
|
||||
}
|
||||
else
|
||||
{
|
||||
ppu_ref(addr) = ppu_fallback;
|
||||
write_to_ptr<ppu_intrp_func_t>(ppu_ptr(addr), ppu_fallback);
|
||||
}
|
||||
|
||||
addr += 4;
|
||||
@@ -749,7 +750,7 @@ extern void ppu_register_function_at(u32 addr, u32 size, ppu_intrp_func_t ptr =
|
||||
// Initialize specific function
|
||||
if (ptr)
|
||||
{
|
||||
ppu_ref(addr) = reinterpret_cast<ppu_intrp_func_t>((reinterpret_cast<uptr>(ptr) & 0xffff'ffff'ffffu) | (uptr(ppu_ref(addr)) & ~0xffff'ffff'ffffu));
|
||||
write_to_ptr<uptr>(ppu_ptr(addr), (reinterpret_cast<uptr>(ptr) & 0xffff'ffff'ffffu) | (uptr(ppu_read(addr)) & ~0xffff'ffff'ffffu));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -771,9 +772,9 @@ extern void ppu_register_function_at(u32 addr, u32 size, ppu_intrp_func_t ptr =
|
||||
// Initialize interpreter cache
|
||||
while (size)
|
||||
{
|
||||
if (ppu_ref(addr) != ppu_break && ppu_ref(addr) != ppu_far_jump)
|
||||
if (auto old = ppu_read(addr); old != ppu_break && old != ppu_far_jump)
|
||||
{
|
||||
ppu_ref(addr) = ppu_cache(addr);
|
||||
write_to_ptr<ppu_intrp_func_t>(ppu_ptr(addr), ppu_cache(addr));
|
||||
}
|
||||
|
||||
addr += 4;
|
||||
@@ -1132,8 +1133,8 @@ extern bool ppu_breakpoint(u32 addr, bool is_adding)
|
||||
}
|
||||
|
||||
// Remove breakpoint parameters
|
||||
ppu_intrp_func_t to_set = 0;
|
||||
ppu_intrp_func_t expected = &ppu_break;
|
||||
ppu_intrp_func_t func_original = 0;
|
||||
ppu_intrp_func_t breakpoint = &ppu_break;
|
||||
|
||||
if (u32 hle_addr{}; g_fxo->is_init<ppu_function_manager>() && (hle_addr = g_fxo->get<ppu_function_manager>().addr))
|
||||
{
|
||||
@@ -1143,31 +1144,40 @@ extern bool ppu_breakpoint(u32 addr, bool is_adding)
|
||||
if (addr % 8 == 4 && index < ppu_function_manager::get().size())
|
||||
{
|
||||
// HLE function placement
|
||||
to_set = ppu_function_manager::get()[index];
|
||||
func_original = ppu_function_manager::get()[index];
|
||||
}
|
||||
}
|
||||
|
||||
if (!to_set)
|
||||
if (!func_original)
|
||||
{
|
||||
// If not an HLE function use regular instruction function
|
||||
to_set = ppu_cache(addr);
|
||||
func_original = ppu_cache(addr);
|
||||
}
|
||||
|
||||
ppu_intrp_func_t& _ref = ppu_ref(addr);
|
||||
|
||||
if (is_adding)
|
||||
{
|
||||
// Swap if adding
|
||||
std::swap(to_set, expected);
|
||||
|
||||
if (_ref == &ppu_fallback)
|
||||
if (ppu_read(addr) == ppu_fallback)
|
||||
{
|
||||
ppu_log.error("Unregistered instruction replaced with a breakpoint at 0x%08x", addr);
|
||||
expected = ppu_fallback;
|
||||
func_original = ppu_fallback;
|
||||
}
|
||||
|
||||
if (ppu_read(addr) != func_original)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
write_to_ptr<ppu_intrp_func_t>(ppu_ptr(addr), breakpoint);
|
||||
return true;
|
||||
}
|
||||
|
||||
return atomic_storage<ppu_intrp_func_t>::compare_exchange(_ref, expected, to_set);
|
||||
if (ppu_read(addr) != breakpoint)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
write_to_ptr<ppu_intrp_func_t>(ppu_ptr(addr), func_original);
|
||||
return true;
|
||||
}
|
||||
|
||||
extern bool ppu_patch(u32 addr, u32 value)
|
||||
@@ -1199,9 +1209,9 @@ extern bool ppu_patch(u32 addr, u32 value)
|
||||
|
||||
if (is_exec)
|
||||
{
|
||||
if (ppu_ref(addr) != ppu_break && ppu_ref(addr) != ppu_fallback)
|
||||
if (auto old = ppu_read(addr); old != ppu_break && old != ppu_fallback)
|
||||
{
|
||||
ppu_ref(addr) = ppu_cache(addr);
|
||||
write_to_ptr<ppu_intrp_func_t>(ppu_ptr(addr), ppu_cache(addr));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2215,7 +2225,7 @@ void ppu_thread::cpu_sleep()
|
||||
|
||||
void ppu_thread::cpu_on_stop()
|
||||
{
|
||||
if (current_function)
|
||||
if (current_function && is_stopped())
|
||||
{
|
||||
if (start_time)
|
||||
{
|
||||
@@ -2225,10 +2235,10 @@ void ppu_thread::cpu_on_stop()
|
||||
{
|
||||
ppu_log.warning("'%s' aborted", current_function);
|
||||
}
|
||||
|
||||
current_function = {};
|
||||
}
|
||||
|
||||
current_function = {};
|
||||
|
||||
// TODO: More conditions
|
||||
if (Emu.IsStopped() && g_cfg.core.ppu_debug)
|
||||
{
|
||||
@@ -2357,7 +2367,7 @@ void ppu_thread::serialize_common(utils::serial& ar)
|
||||
|
||||
ar(gpr, fpr, cr, fpscr.bits, lr, ctr, vrsave, cia, xer, sat, nj, prio.raw().all);
|
||||
|
||||
if (cia % 4 || !vm::check_addr(cia))
|
||||
if (cia % 4 || (cia >> 28) >= 0xCu)
|
||||
{
|
||||
fmt::throw_exception("Failed to serialize PPU thread ID=0x%x (cia=0x%x, ar=%s)", this->id, cia, ar);
|
||||
}
|
||||
@@ -2380,6 +2390,12 @@ void ppu_thread::serialize_common(utils::serial& ar)
|
||||
}
|
||||
}
|
||||
|
||||
struct save_lv2_tag
|
||||
{
|
||||
atomic_t<bool> saved = false;
|
||||
atomic_t<bool> loaded = false;
|
||||
};
|
||||
|
||||
ppu_thread::ppu_thread(utils::serial& ar)
|
||||
: cpu_thread(idm::last_id()) // last_id() is showed to constructor on serialization
|
||||
, stack_size(ar)
|
||||
@@ -2388,6 +2404,8 @@ ppu_thread::ppu_thread(utils::serial& ar)
|
||||
, entry_func(std::bit_cast<ppu_func_opd_t, u64>(ar))
|
||||
, is_interrupt_thread(ar)
|
||||
{
|
||||
[[maybe_unused]] const s32 version = GET_SERIALIZATION_VERSION(ppu);
|
||||
|
||||
struct init_pushed
|
||||
{
|
||||
bool pushed = false;
|
||||
@@ -2398,6 +2416,19 @@ ppu_thread::ppu_thread(utils::serial& ar)
|
||||
syscall_history.data.resize(g_cfg.core.ppu_call_history ? syscall_history_max_size : 1);
|
||||
syscall_history.count_debug_arguments = static_cast<u32>(g_cfg.core.ppu_call_history ? std::size(syscall_history.data[0].args) : 0);
|
||||
|
||||
if (version >= 2 && !g_fxo->get<save_lv2_tag>().loaded.exchange(true))
|
||||
{
|
||||
ar(lv2_obj::g_priority_order_tag);
|
||||
}
|
||||
|
||||
if (version >= 3)
|
||||
{
|
||||
// Function and module for HLE function relocation
|
||||
// TODO: Use it
|
||||
ar.pop<std::string>();
|
||||
ar.pop<std::string>();
|
||||
}
|
||||
|
||||
serialize_common(ar);
|
||||
|
||||
// Restore jm_mask
|
||||
@@ -2427,7 +2458,41 @@ ppu_thread::ppu_thread(utils::serial& ar)
|
||||
case PPU_THREAD_STATUS_RUNNABLE:
|
||||
case PPU_THREAD_STATUS_ONPROC:
|
||||
{
|
||||
lv2_obj::awake(this);
|
||||
if (version >= 2)
|
||||
{
|
||||
const u32 order = ar.pop<u32>();
|
||||
|
||||
struct awake_pushed
|
||||
{
|
||||
bool pushed = false;
|
||||
shared_mutex dummy;
|
||||
std::map<u32, ppu_thread*> awake_ppus;
|
||||
};
|
||||
|
||||
g_fxo->get<awake_pushed>().awake_ppus[order] = this;
|
||||
|
||||
if (!std::exchange(g_fxo->get<awake_pushed>().pushed, true))
|
||||
{
|
||||
Emu.PostponeInitCode([this]()
|
||||
{
|
||||
u32 prev = umax;
|
||||
|
||||
for (auto ppu : g_fxo->get<awake_pushed>().awake_ppus)
|
||||
{
|
||||
ensure(prev + 1 == ppu.first);
|
||||
prev = ppu.first;
|
||||
lv2_obj::awake(ppu.second);
|
||||
}
|
||||
|
||||
g_fxo->get<awake_pushed>().awake_ppus.clear();
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
lv2_obj::awake(this);
|
||||
}
|
||||
|
||||
[[fallthrough]];
|
||||
}
|
||||
case PPU_THREAD_STATUS_SLEEP:
|
||||
@@ -2475,7 +2540,9 @@ ppu_thread::ppu_thread(utils::serial& ar)
|
||||
const u32 op = vm::read32(ppu.cia);
|
||||
const auto& table = g_fxo->get<ppu_interpreter_rt>();
|
||||
ppu.loaded_from_savestate = true;
|
||||
ppu.prio.raw().preserve_bit = 1;
|
||||
table.decode(op)(ppu, {op}, vm::_ptr<u32>(ppu.cia), &ppu_ret);
|
||||
ppu.prio.raw().preserve_bit = 0;
|
||||
|
||||
ppu.optional_savestate_state->clear(); // Reset to writing state
|
||||
ppu.loaded_from_savestate = false;
|
||||
@@ -2529,9 +2596,28 @@ void ppu_thread::save(utils::serial& ar)
|
||||
}
|
||||
|
||||
ar(stack_size, stack_addr, _joiner, entry, is_interrupt_thread);
|
||||
|
||||
const bool is_null = ar.m_file_handler && ar.m_file_handler->is_null();
|
||||
|
||||
if (!is_null && !g_fxo->get<save_lv2_tag>().saved.exchange(true))
|
||||
{
|
||||
ar(lv2_obj::g_priority_order_tag);
|
||||
}
|
||||
|
||||
if (current_module && current_module[0])
|
||||
{
|
||||
ar(std::string{current_module});
|
||||
ar(std::string{last_function});
|
||||
}
|
||||
else
|
||||
{
|
||||
ar(std::string{});
|
||||
ar(std::string{});
|
||||
}
|
||||
|
||||
serialize_common(ar);
|
||||
|
||||
ppu_thread_status status = lv2_obj::ppu_state(this, false);
|
||||
auto [status, order] = lv2_obj::ppu_state(this, false);
|
||||
|
||||
if (status == PPU_THREAD_STATUS_SLEEP && cpu_flag::again - state)
|
||||
{
|
||||
@@ -2541,6 +2627,11 @@ void ppu_thread::save(utils::serial& ar)
|
||||
|
||||
ar(status);
|
||||
|
||||
if (status == PPU_THREAD_STATUS_RUNNABLE || status == PPU_THREAD_STATUS_ONPROC)
|
||||
{
|
||||
ar(order);
|
||||
}
|
||||
|
||||
ar(*ppu_tname.load());
|
||||
}
|
||||
|
||||
@@ -3305,25 +3396,30 @@ static bool ppu_store_reservation(ppu_thread& ppu, u32 addr, u64 reg_value)
|
||||
//auto& cline_data = vm::_ref<spu_rdata_t>(addr);
|
||||
|
||||
data += 0;
|
||||
rsx::reservation_lock rsx_lock(addr, 128);
|
||||
|
||||
auto& super_data = *vm::get_super_ptr<spu_rdata_t>(addr);
|
||||
const bool success = [&]()
|
||||
auto range_lock = vm::alloc_range_lock();
|
||||
bool success = false;
|
||||
{
|
||||
// Full lock (heavyweight)
|
||||
// TODO: vm::check_addr
|
||||
vm::writer_lock lock(addr);
|
||||
rsx::reservation_lock rsx_lock(addr, 128);
|
||||
|
||||
if (cmp_rdata(ppu.rdata, super_data))
|
||||
auto& super_data = *vm::get_super_ptr<spu_rdata_t>(addr);
|
||||
success = [&]()
|
||||
{
|
||||
data.release(new_data);
|
||||
res += 64;
|
||||
return true;
|
||||
}
|
||||
// Full lock (heavyweight)
|
||||
// TODO: vm::check_addr
|
||||
vm::writer_lock lock(addr, range_lock);
|
||||
|
||||
res -= 64;
|
||||
return false;
|
||||
}();
|
||||
if (cmp_rdata(ppu.rdata, super_data))
|
||||
{
|
||||
data.release(new_data);
|
||||
res += 64;
|
||||
return true;
|
||||
}
|
||||
|
||||
res -= 64;
|
||||
return false;
|
||||
}();
|
||||
}
|
||||
vm::free_range_lock(range_lock);
|
||||
|
||||
return success;
|
||||
}
|
||||
@@ -3386,17 +3482,17 @@ static bool ppu_store_reservation(ppu_thread& ppu, u32 addr, u64 reg_value)
|
||||
{
|
||||
extern atomic_t<u32> liblv2_begin, liblv2_end;
|
||||
|
||||
const u32 notify = ppu.res_notify;
|
||||
|
||||
if (notify)
|
||||
{
|
||||
vm::reservation_notifier(notify).notify_all();
|
||||
ppu.res_notify = 0;
|
||||
}
|
||||
|
||||
// Avoid notifications from lwmutex or sys_spinlock
|
||||
if (ppu.cia < liblv2_begin || ppu.cia >= liblv2_end)
|
||||
if (new_data != old_data && (ppu.cia < liblv2_begin || ppu.cia >= liblv2_end))
|
||||
{
|
||||
const u32 notify = ppu.res_notify;
|
||||
|
||||
if (notify)
|
||||
{
|
||||
vm::reservation_notifier(notify).notify_all();
|
||||
ppu.res_notify = 0;
|
||||
}
|
||||
|
||||
if (!notify)
|
||||
{
|
||||
// Try to postpone notification to when PPU is asleep or join notifications on the same address
|
||||
@@ -3460,7 +3556,7 @@ namespace
|
||||
// Compiled PPU module info
|
||||
struct jit_module
|
||||
{
|
||||
std::vector<ppu_intrp_func_t> funcs;
|
||||
void(*symbol_resolver)(u8*, u64) = nullptr;
|
||||
std::shared_ptr<jit_compiler> pjit;
|
||||
bool init = false;
|
||||
};
|
||||
@@ -3499,10 +3595,18 @@ namespace
|
||||
if (found == bucket.map.end()) [[unlikely]]
|
||||
{
|
||||
ppu_log.error("Failed to remove module %s", name);
|
||||
|
||||
for (auto& buck : buckets)
|
||||
{
|
||||
for (auto& mod : buck.map)
|
||||
{
|
||||
ppu_log.notice("But there is module %s", mod.first);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
to_destroy.funcs = std::move(found->second.funcs);
|
||||
to_destroy.pjit = std::move(found->second.pjit);
|
||||
|
||||
bucket.map.erase(found);
|
||||
@@ -3612,26 +3716,47 @@ extern fs::file make_file_view(fs::file&& _file, u64 offset, u64 max_size = umax
|
||||
return file;
|
||||
}
|
||||
|
||||
extern void ppu_finalize(const ppu_module& info)
|
||||
extern void ppu_finalize(const ppu_module& info, bool force_mem_release)
|
||||
{
|
||||
if (info.name.empty())
|
||||
if (info.segs.empty())
|
||||
{
|
||||
// HLEd modules
|
||||
return;
|
||||
}
|
||||
|
||||
if (!force_mem_release && info.name.empty())
|
||||
{
|
||||
// Don't remove main module from memory
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string dev_flash = vfs::get("/dev_flash/sys/");
|
||||
|
||||
if (info.path.starts_with(dev_flash) || Emu.GetCat() == "1P")
|
||||
if (!force_mem_release && Emu.GetCat() == "1P")
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const bool may_be_elf = fmt::to_lower(info.path.substr(std::max<usz>(info.path.size(), 3) - 3)) != "prx";
|
||||
|
||||
const std::string dev_flash = vfs::get("/dev_flash/");
|
||||
|
||||
if (!may_be_elf)
|
||||
{
|
||||
if (!force_mem_release && info.path.starts_with(dev_flash + "sys/external/"))
|
||||
{
|
||||
// Don't remove dev_flash prx from memory
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (g_cfg.core.ppu_decoder != ppu_decoder_type::llvm)
|
||||
{
|
||||
// Don't remove dev_flash prx from memory
|
||||
return;
|
||||
}
|
||||
|
||||
// Get cache path for this executable
|
||||
std::string cache_path = fs::get_cache_dir() + "cache/";
|
||||
|
||||
if (!Emu.GetTitleID().empty())
|
||||
if (!info.path.starts_with(dev_flash) && !Emu.GetTitleID().empty() && Emu.GetCat() != "1P")
|
||||
{
|
||||
cache_path += Emu.GetTitleID();
|
||||
cache_path += '/';
|
||||
@@ -3864,9 +3989,18 @@ extern void ppu_precompile(std::vector<std::string>& dir_queue, std::vector<ppu_
|
||||
|
||||
lf_queue<file_info> possible_exec_file_paths;
|
||||
|
||||
::semaphore<2> ovl_sema;
|
||||
// Allow to allocate 2000 times the size of each file for the use of LLVM
|
||||
// This works very nicely with Metal Gear Solid 4 for example:
|
||||
// 2 7MB overlay files -> 14GB
|
||||
// The growth in memory requirements of LLVM is not linear with file size of course
|
||||
// But these estimates should hopefully protect RPCS3 in the coming years
|
||||
// Especially when thread count is on the rise with each CPU generation
|
||||
atomic_t<u32> file_size_limit = static_cast<u32>(std::clamp<u64>(utils::aligned_div<u64>(utils::get_total_memory(), 2000), 65536, u32{umax}));
|
||||
|
||||
named_thread_group workers("SPRX Worker ", std::min<u32>(utils::get_thread_count(), ::size32(file_queue)), [&]
|
||||
const u32 software_thread_limit = std::min<u32>(g_cfg.core.llvm_threads ? g_cfg.core.llvm_threads : u32{umax}, ::size32(file_queue));
|
||||
const u32 cpu_thread_limit = utils::get_thread_count() > 8u ? std::max<u32>(utils::get_thread_count(), 2) - 1 : utils::get_thread_count(); // One LLVM thread less
|
||||
|
||||
named_thread_group workers("SPRX Worker ", std::min<u32>(software_thread_limit, cpu_thread_limit), [&]
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
pthread_jit_write_protect_np(false);
|
||||
@@ -3874,6 +4008,7 @@ extern void ppu_precompile(std::vector<std::string>& dir_queue, std::vector<ppu_
|
||||
// Set low priority
|
||||
thread_ctrl::scoped_priority low_prio(-1);
|
||||
u32 inc_fdone = 1;
|
||||
u32 restore_mem = 0;
|
||||
|
||||
for (usz func_i = fnext++; func_i < file_queue.size(); func_i = fnext++, g_progr_fdone += std::exchange(inc_fdone, 1))
|
||||
{
|
||||
@@ -3882,6 +4017,16 @@ extern void ppu_precompile(std::vector<std::string>& dir_queue, std::vector<ppu_
|
||||
continue;
|
||||
}
|
||||
|
||||
if (restore_mem)
|
||||
{
|
||||
if (!file_size_limit.fetch_add(restore_mem))
|
||||
{
|
||||
file_size_limit.notify_all();
|
||||
}
|
||||
|
||||
restore_mem = 0;
|
||||
}
|
||||
|
||||
auto& [path, offset, file_size] = file_queue[func_i];
|
||||
|
||||
ppu_log.notice("Trying to load: %s", path);
|
||||
@@ -3913,15 +4058,53 @@ extern void ppu_precompile(std::vector<std::string>& dir_queue, std::vector<ppu_
|
||||
continue;
|
||||
}
|
||||
|
||||
auto wait_for_memory = [&]() -> bool
|
||||
{
|
||||
// Try not to process too many files at once because it seems to reduce performance and cause RAM shortages
|
||||
// Concurrently compiling more OVL or huge PRX files does not have much theoretical benefit
|
||||
while (!file_size_limit.fetch_op([&](u32& value)
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
// Allow at least one file, make 0 the "memory unavailable" sign value for atomic waiting efficiency
|
||||
const u32 new_val = static_cast<u32>(utils::sub_saturate<u64>(value, file_size));
|
||||
restore_mem = value - new_val;
|
||||
value = new_val;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Resort to waiting
|
||||
restore_mem = 0;
|
||||
return false;
|
||||
}).second)
|
||||
{
|
||||
// Wait until not 0
|
||||
file_size_limit.wait(0);
|
||||
}
|
||||
|
||||
if (Emu.IsStopped())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
elf_error prx_err{}, ovl_err{};
|
||||
|
||||
if (ppu_prx_object obj = src; (prx_err = obj, obj == elf_error::ok))
|
||||
{
|
||||
if (!wait_for_memory())
|
||||
{
|
||||
// Emulation stopped
|
||||
continue;
|
||||
}
|
||||
|
||||
if (auto prx = ppu_load_prx(obj, true, path, offset))
|
||||
{
|
||||
obj.clear(), src.close(); // Clear decrypted file and elf object memory
|
||||
ppu_initialize(*prx, false, file_size);
|
||||
ppu_finalize(*prx);
|
||||
ppu_finalize(*prx, true);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -3933,10 +4116,6 @@ extern void ppu_precompile(std::vector<std::string>& dir_queue, std::vector<ppu_
|
||||
{
|
||||
while (ovl_err == elf_error::ok)
|
||||
{
|
||||
// Try not to process too many files at once because it seems to reduce performance
|
||||
// Concurrently compiling more OVL files does not have much theoretical benefit
|
||||
std::lock_guard lock(ovl_sema);
|
||||
|
||||
if (Emu.IsStopped())
|
||||
{
|
||||
break;
|
||||
@@ -3957,6 +4136,12 @@ extern void ppu_precompile(std::vector<std::string>& dir_queue, std::vector<ppu_
|
||||
break;
|
||||
}
|
||||
|
||||
if (!wait_for_memory())
|
||||
{
|
||||
// Emulation stopped
|
||||
break;
|
||||
}
|
||||
|
||||
// Participate in thread execution limitation (takes a long time)
|
||||
if (std::lock_guard lock(g_fxo->get<jit_core_allocator>().sem); !ovlm->analyse(0, ovlm->entry, ovlm->seg0_code_end, ovlm->applied_patches, []()
|
||||
{
|
||||
@@ -3969,7 +4154,7 @@ extern void ppu_precompile(std::vector<std::string>& dir_queue, std::vector<ppu_
|
||||
|
||||
obj.clear(), src.close(); // Clear decrypted file and elf object memory
|
||||
ppu_initialize(*ovlm, false, file_size);
|
||||
ppu_finalize(*ovlm);
|
||||
ppu_finalize(*ovlm, true);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -3979,10 +4164,18 @@ extern void ppu_precompile(std::vector<std::string>& dir_queue, std::vector<ppu_
|
||||
}
|
||||
}
|
||||
|
||||
ppu_log.notice("Failed to precompile '%s' (prx: %s, ovl: %s): Attempting tratment as executable file", path, prx_err, ovl_err);
|
||||
ppu_log.notice("Failed to precompile '%s' (prx: %s, ovl: %s): Attempting compilation as executable file", path, prx_err, ovl_err);
|
||||
possible_exec_file_paths.push(path, offset, file_size);
|
||||
inc_fdone = 0;
|
||||
}
|
||||
|
||||
if (restore_mem)
|
||||
{
|
||||
if (!file_size_limit.fetch_add(restore_mem))
|
||||
{
|
||||
file_size_limit.notify_all();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Join every thread
|
||||
@@ -4070,7 +4263,7 @@ extern void ppu_precompile(std::vector<std::string>& dir_queue, std::vector<ppu_
|
||||
Emu.ConfigurePPUCache(!Emu.IsPathInsideDir(_main.path, g_cfg_vfs.get_dev_flash()));
|
||||
ppu_initialize(_main, false, file_size);
|
||||
spu_cache::initialize(false);
|
||||
ppu_finalize(_main);
|
||||
ppu_finalize(_main, true);
|
||||
_main = {};
|
||||
g_fxo->get<spu_cache>() = std::move(current_cache);
|
||||
break;
|
||||
@@ -4194,7 +4387,7 @@ extern void ppu_initialize()
|
||||
}
|
||||
}
|
||||
|
||||
const std::string firmware_sprx_path = vfs::get(dev_flash_located ? "/dev_flash/"sv : "/dev_flash/sys/"sv);
|
||||
const std::string firmware_sprx_path = vfs::get(dev_flash_located ? "/dev_flash/"sv : "/dev_flash/sys/external/"sv);
|
||||
dir_queue.emplace_back(firmware_sprx_path);
|
||||
}
|
||||
|
||||
@@ -4250,8 +4443,18 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_size)
|
||||
|
||||
for (const auto& func : info.funcs)
|
||||
{
|
||||
if (func.size && func.blocks.empty())
|
||||
{
|
||||
ppu_register_function_at(func.addr, func.size);
|
||||
}
|
||||
|
||||
for (auto& block : func.blocks)
|
||||
{
|
||||
if (!block.second)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (g_fxo->is_init<ppu_far_jumps_t>() && !g_fxo->get<ppu_far_jumps_t>().get_targets(block.first, block.second).empty())
|
||||
{
|
||||
// Replace the block with ppu_far_jump
|
||||
@@ -4264,7 +4467,7 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_size)
|
||||
if (g_cfg.core.ppu_debug && func.size && func.toc != umax && !ppu_get_far_jump(func.addr))
|
||||
{
|
||||
ppu_toc[func.addr] = func.toc;
|
||||
ppu_ref(func.addr) = &ppu_check_toc;
|
||||
write_to_ptr<ppu_intrp_func_t>(ppu_ptr(func.addr), &ppu_check_toc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4611,6 +4814,7 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_size)
|
||||
accurate_fpcc,
|
||||
accurate_vnan,
|
||||
accurate_nj_mode,
|
||||
contains_symbol_resolver,
|
||||
|
||||
__bitset_enum_max
|
||||
};
|
||||
@@ -4640,6 +4844,8 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_size)
|
||||
settings += ppu_settings::accurate_vnan, settings -= ppu_settings::fixup_vnan, fmt::throw_exception("VNAN Not implemented");
|
||||
if (g_cfg.core.ppu_use_nj_bit)
|
||||
settings += ppu_settings::accurate_nj_mode, settings -= ppu_settings::fixup_nj_denormals, fmt::throw_exception("NJ Not implemented");
|
||||
if (fpos >= info.funcs.size())
|
||||
settings += ppu_settings::contains_symbol_resolver; // Avoid invalidating all modules for this purpose
|
||||
|
||||
// Write version, hash, CPU, settings
|
||||
fmt::append(obj_name, "v6-kusa-%s-%s-%s.obj", fmt::base57(output, 16), fmt::base57(settings), jit_compiler::cpu(g_cfg.core.llvm_cpu));
|
||||
@@ -4724,16 +4930,18 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_size)
|
||||
{
|
||||
atomic_t<u32>& work_cv;
|
||||
std::vector<std::pair<std::string, ppu_module>>& workload;
|
||||
const ppu_module& main_module;
|
||||
const std::string& cache_path;
|
||||
const cpu_thread* cpu;
|
||||
|
||||
std::unique_lock<decltype(jit_core_allocator::sem)> core_lock;
|
||||
|
||||
thread_op(atomic_t<u32>& work_cv, std::vector<std::pair<std::string, ppu_module>>& workload
|
||||
, const cpu_thread* cpu, const std::string& cache_path, decltype(jit_core_allocator::sem)& sem) noexcept
|
||||
, const cpu_thread* cpu, const ppu_module& main_module, const std::string& cache_path, decltype(jit_core_allocator::sem)& sem) noexcept
|
||||
|
||||
: work_cv(work_cv)
|
||||
, workload(workload)
|
||||
, main_module(main_module)
|
||||
, cache_path(cache_path)
|
||||
, cpu(cpu)
|
||||
{
|
||||
@@ -4744,6 +4952,7 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_size)
|
||||
thread_op(const thread_op& other) noexcept
|
||||
: work_cv(other.work_cv)
|
||||
, workload(other.workload)
|
||||
, main_module(other.main_module)
|
||||
, cache_path(other.cache_path)
|
||||
, cpu(other.cpu)
|
||||
{
|
||||
@@ -4778,7 +4987,7 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_size)
|
||||
|
||||
// Use another JIT instance
|
||||
jit_compiler jit2({}, g_cfg.core.llvm_cpu, 0x1);
|
||||
ppu_initialize2(jit2, part, cache_path, obj_name);
|
||||
ppu_initialize2(jit2, part, cache_path, obj_name, i == workload.size() - 1 ? main_module : part);
|
||||
|
||||
ppu_log.success("LLVM: Compiled module %s", obj_name);
|
||||
}
|
||||
@@ -4791,7 +5000,7 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_size)
|
||||
g_watchdog_hold_ctr++;
|
||||
|
||||
named_thread_group threads(fmt::format("PPUW.%u.", ++g_fxo->get<thread_index_allocator>().index), thread_count
|
||||
, thread_op(work_cv, workload, cpu, cache_path, g_fxo->get<jit_core_allocator>().sem)
|
||||
, thread_op(work_cv, workload, cpu, info, cache_path, g_fxo->get<jit_core_allocator>().sem)
|
||||
, [&](u32 /*thread_index*/, thread_op& op)
|
||||
{
|
||||
// Allocate "core"
|
||||
@@ -4835,8 +5044,6 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_size)
|
||||
}
|
||||
}
|
||||
|
||||
progr.reset();
|
||||
|
||||
if (!is_being_used_in_emulation || (cpu ? cpu->state.all_of(cpu_flag::exit) : Emu.IsStopped()))
|
||||
{
|
||||
return compiled_new;
|
||||
@@ -4849,85 +5056,46 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_size)
|
||||
// Try to patch all single and unregistered BLRs with the same function (TODO: Maybe generalize it into PIC code detection and patching)
|
||||
ppu_intrp_func_t BLR_func = nullptr;
|
||||
|
||||
const bool is_first = jit && !jit_mod.init;
|
||||
const bool showing_only_apply_stage = !g_progr.load() && !g_progr_ptotal && !g_progr_ftotal && g_progr_ptotal.compare_and_swap_test(0, 1);
|
||||
|
||||
progr = "Applying PPU Code...";
|
||||
|
||||
if (!jit)
|
||||
{
|
||||
// No functions - nothing to do
|
||||
ensure(info.funcs.empty());
|
||||
return compiled_new;
|
||||
}
|
||||
|
||||
const bool is_first = !jit_mod.init;
|
||||
|
||||
if (is_first)
|
||||
{
|
||||
jit->fin();
|
||||
}
|
||||
|
||||
u32 index = 0;
|
||||
u32 max_count = 0;
|
||||
|
||||
for (const auto& func : info.funcs)
|
||||
if (is_first)
|
||||
{
|
||||
if (func.size)
|
||||
{
|
||||
max_count++;
|
||||
}
|
||||
jit_mod.symbol_resolver = reinterpret_cast<void(*)(u8*, u64)>(jit->get("__resolve_symbols"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ensure(jit_mod.symbol_resolver);
|
||||
}
|
||||
|
||||
u32 pending_progress = umax;
|
||||
jit_mod.symbol_resolver(vm::g_exec_addr, info.segs[0].addr);
|
||||
|
||||
bool early_exit = false;
|
||||
|
||||
// Get and install function addresses
|
||||
// Find a BLR-only function in order to copy it to all BLRs (some games need it)
|
||||
for (const auto& func : info.funcs)
|
||||
{
|
||||
if (!func.size)
|
||||
if (func.size == 4 && *info.get_ptr<u32>(func.addr) == ppu_instructions::BLR())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cpu ? cpu->state.all_of(cpu_flag::exit) : Emu.IsStopped())
|
||||
{
|
||||
// Revert partially commited changes
|
||||
jit_mod.funcs.clear();
|
||||
BLR_func = nullptr;
|
||||
early_exit = true;
|
||||
BLR_func = ppu_read(func.addr);
|
||||
break;
|
||||
}
|
||||
|
||||
const auto name = fmt::format("__0x%x", func.addr - reloc);
|
||||
|
||||
// Try to locate existing function if it is not the first time
|
||||
const auto addr = is_first ? ensure(reinterpret_cast<ppu_intrp_func_t>(jit->get(name)))
|
||||
: reinterpret_cast<ppu_intrp_func_t>(ensure(jit_mod.funcs[index]));
|
||||
|
||||
jit_mod.funcs.emplace_back(addr);
|
||||
|
||||
if (func.size == 4 && !BLR_func && *info.get_ptr<u32>(func.addr) == ppu_instructions::BLR())
|
||||
{
|
||||
BLR_func = addr;
|
||||
}
|
||||
|
||||
ppu_register_function_at(func.addr, 4, addr);
|
||||
|
||||
if (g_cfg.core.ppu_debug)
|
||||
ppu_log.trace("Installing function %s at 0x%x: %p (reloc = 0x%x)", name, func.addr, ppu_ref(func.addr), reloc);
|
||||
|
||||
index++;
|
||||
|
||||
if (pending_progress != umax)
|
||||
{
|
||||
pending_progress++;
|
||||
|
||||
if (pending_progress == 1024)
|
||||
{
|
||||
pending_progress = 0;
|
||||
g_progr_pdone++;
|
||||
}
|
||||
}
|
||||
else if (!g_progr.load() && !g_progr_ptotal && !g_progr_ftotal)
|
||||
{
|
||||
g_progr_pdone += index / 1024;
|
||||
g_progr_ptotal += max_count / 1024;
|
||||
pending_progress = index % 1024;
|
||||
progr.emplace("Applying PPU Code...");
|
||||
}
|
||||
}
|
||||
|
||||
if (is_first && !early_exit)
|
||||
if (is_first)
|
||||
{
|
||||
jit_mod.init = true;
|
||||
}
|
||||
@@ -4938,20 +5106,26 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_size)
|
||||
|
||||
for (u32 addr = info.segs[0].addr; addr < info.segs[0].addr + info.segs[0].size; addr += 4, inst_ptr++)
|
||||
{
|
||||
if (*inst_ptr == ppu_instructions::BLR() && (reinterpret_cast<uptr>(ppu_ref(addr)) << 16 >> 16) == reinterpret_cast<uptr>(ppu_recompiler_fallback_ghc))
|
||||
if (*inst_ptr == ppu_instructions::BLR() && (reinterpret_cast<uptr>(ppu_read(addr)) << 16 >> 16) == reinterpret_cast<uptr>(ppu_recompiler_fallback_ghc))
|
||||
{
|
||||
ppu_register_function_at(addr, 4, BLR_func);
|
||||
write_to_ptr<ppu_intrp_func_t>(ppu_ptr(addr), BLR_func);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (showing_only_apply_stage)
|
||||
{
|
||||
// Done
|
||||
g_progr_pdone++;
|
||||
}
|
||||
|
||||
return compiled_new;
|
||||
#else
|
||||
fmt::throw_exception("LLVM is not available in this build.");
|
||||
#endif
|
||||
}
|
||||
|
||||
static void ppu_initialize2(jit_compiler& jit, const ppu_module& module_part, const std::string& cache_path, const std::string& obj_name)
|
||||
static void ppu_initialize2(jit_compiler& jit, const ppu_module& module_part, const std::string& cache_path, const std::string& obj_name, const ppu_module& whole_module)
|
||||
{
|
||||
#ifdef LLVM_AVAILABLE
|
||||
using namespace llvm;
|
||||
@@ -4995,6 +5169,7 @@ static void ppu_initialize2(jit_compiler& jit, const ppu_module& module_part, co
|
||||
translator.build_interpreter();
|
||||
}
|
||||
|
||||
#if LLVM_VERSION_MAJOR < 17
|
||||
legacy::FunctionPassManager pm(_module.get());
|
||||
|
||||
// Basic optimizations
|
||||
@@ -5016,6 +5191,32 @@ static void ppu_initialize2(jit_compiler& jit, const ppu_module& module_part, co
|
||||
//pm.add(createAggressiveDCEPass());
|
||||
//pm.add(createCFGSimplificationPass());
|
||||
//pm.add(createLintPass()); // Check
|
||||
#else
|
||||
// Create the analysis managers.
|
||||
// These must be declared in this order so that they are destroyed in the
|
||||
// correct order due to inter-analysis-manager references.
|
||||
LoopAnalysisManager lam;
|
||||
FunctionAnalysisManager fam;
|
||||
CGSCCAnalysisManager cgam;
|
||||
ModuleAnalysisManager mam;
|
||||
|
||||
// Create the new pass manager builder.
|
||||
// Take a look at the PassBuilder constructor parameters for more
|
||||
// customization, e.g. specifying a TargetMachine or various debugging
|
||||
// options.
|
||||
PassBuilder pb;
|
||||
|
||||
// Register all the basic analyses with the managers.
|
||||
pb.registerModuleAnalyses(mam);
|
||||
pb.registerCGSCCAnalyses(cgam);
|
||||
pb.registerFunctionAnalyses(fam);
|
||||
pb.registerLoopAnalyses(lam);
|
||||
pb.crossRegisterProxies(lam, fam, cgam, mam);
|
||||
|
||||
FunctionPassManager fpm;
|
||||
// Basic optimizations
|
||||
fpm.addPass(EarlyCSEPass());
|
||||
#endif
|
||||
|
||||
// Translate functions
|
||||
for (usz fi = 0, fmax = module_part.funcs.size(); fi < fmax; fi++)
|
||||
@@ -5032,7 +5233,11 @@ static void ppu_initialize2(jit_compiler& jit, const ppu_module& module_part, co
|
||||
if (const auto func = translator.Translate(module_part.funcs[fi]))
|
||||
{
|
||||
// Run optimization passes
|
||||
#if LLVM_VERSION_MAJOR < 17
|
||||
pm.run(*func);
|
||||
#else
|
||||
fpm.run(*func, fam);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -5042,6 +5247,25 @@ static void ppu_initialize2(jit_compiler& jit, const ppu_module& module_part, co
|
||||
}
|
||||
}
|
||||
|
||||
// Run this only in one module for all functions
|
||||
if (&whole_module != &module_part)
|
||||
{
|
||||
if (const auto func = translator.GetSymbolResolver(whole_module))
|
||||
{
|
||||
// Run optimization passes
|
||||
#if LLVM_VERSION_MAJOR < 17
|
||||
pm.run(*func);
|
||||
#else
|
||||
fpm.run(*func, fam);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
Emu.Pause();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//legacy::PassManager mpm;
|
||||
|
||||
// Remove unused functions, structs, global variables, etc
|
||||
|
||||
@@ -268,7 +268,8 @@ public:
|
||||
{
|
||||
u64 all;
|
||||
bf_t<s64, 0, 13> prio; // Thread priority (0..3071) (firs 12-bits)
|
||||
bf_t<s64, 13, 51> order; // Thread enqueue order (last 52-bits)
|
||||
bf_t<s64, 13, 50> order; // Thread enqueue order (last 52-bits)
|
||||
bf_t<u64, 63, 1> preserve_bit; // Preserve value for savestates
|
||||
};
|
||||
|
||||
atomic_t<ppu_prio_t> prio{};
|
||||
@@ -294,6 +295,7 @@ public:
|
||||
u64 syscall_args[8]{0}; // Last syscall arguments stored
|
||||
const char* current_function{}; // Current function name for diagnosis, optimized for speed.
|
||||
const char* last_function{}; // Sticky copy of current_function, is not cleared on function return
|
||||
const char* current_module{}; // Current module name, for savestates.
|
||||
|
||||
const bool is_interrupt_thread; // True for interrupts-handler threads
|
||||
|
||||
@@ -383,10 +385,10 @@ struct ppu_gpr_cast_impl
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct ppu_gpr_cast_impl<T, std::enable_if_t<std::is_integral<T>::value || std::is_enum<T>::value>>
|
||||
struct ppu_gpr_cast_impl<T, std::enable_if_t<std::is_integral_v<T> || std::is_enum_v<T>>>
|
||||
{
|
||||
static_assert(sizeof(T) <= 8, "Too big integral type for ppu_gpr_cast<>()");
|
||||
static_assert(std::is_same<std::decay_t<T>, bool>::value == false, "bool type is deprecated in ppu_gpr_cast<>(), use b8 instead");
|
||||
static_assert(std::is_same_v<std::decay_t<T>, bool> == false, "bool type is deprecated in ppu_gpr_cast<>(), use b8 instead");
|
||||
|
||||
static inline u64 to(const T& value)
|
||||
{
|
||||
|
||||
@@ -274,6 +274,116 @@ Function* PPUTranslator::Translate(const ppu_function& info)
|
||||
return m_function;
|
||||
}
|
||||
|
||||
Function* PPUTranslator::GetSymbolResolver(const ppu_module& info)
|
||||
{
|
||||
m_function = cast<Function>(m_module->getOrInsertFunction("__resolve_symbols", FunctionType::get(get_type<void>(), { get_type<u8*>(), get_type<u64>() }, false)).getCallee());
|
||||
|
||||
IRBuilder<> irb(BasicBlock::Create(m_context, "__entry", m_function));
|
||||
m_ir = &irb;
|
||||
|
||||
// Instruction address is (m_addr + base)
|
||||
const u64 base = m_reloc ? m_reloc->addr : 0;
|
||||
|
||||
m_exec = m_function->getArg(0);
|
||||
m_seg0 = m_function->getArg(1);
|
||||
|
||||
const auto ftype = FunctionType::get(get_type<void>(), {
|
||||
get_type<u8*>(), // Exec base
|
||||
GetContextType()->getPointerTo(), // PPU context
|
||||
get_type<u64>(), // Segment address (for PRX)
|
||||
get_type<u8*>(), // Memory base
|
||||
get_type<u64>(), // r0
|
||||
get_type<u64>(), // r1
|
||||
get_type<u64>(), // r2
|
||||
}, false);
|
||||
|
||||
// Store function addresses in PPU jumptable using internal resolving instead of patching it externally.
|
||||
// Because, LLVM processed it extremely slow. (regression)
|
||||
// This is made in loop instead of inlined because it took tremendous amount of time to compile.
|
||||
|
||||
std::vector<u32> vec_addrs;
|
||||
vec_addrs.reserve(info.funcs.size());
|
||||
|
||||
// Create an array of function pointers
|
||||
std::vector<llvm::Constant*> functions;
|
||||
|
||||
for (const auto& f : info.funcs)
|
||||
{
|
||||
if (!f.size)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
vec_addrs.push_back(f.addr - base);
|
||||
functions.push_back(cast<Function>(m_module->getOrInsertFunction(fmt::format("__0x%x", f.addr - base), ftype).getCallee()));
|
||||
}
|
||||
|
||||
if (vec_addrs.empty())
|
||||
{
|
||||
// Possible special case for no functions (allowing the do-while optimization)
|
||||
m_ir->CreateRetVoid();
|
||||
replace_intrinsics(*m_function);
|
||||
return m_function;
|
||||
}
|
||||
|
||||
const auto addr_array_type = ArrayType::get(get_type<u32>(), vec_addrs.size());
|
||||
const auto addr_array = new GlobalVariable(*m_module, addr_array_type, false, GlobalValue::PrivateLinkage, ConstantDataArray::get(m_context, vec_addrs));
|
||||
|
||||
// Create an array of function pointers
|
||||
const auto func_table_type = ArrayType::get(ftype->getPointerTo(), info.funcs.size());
|
||||
const auto init_func_table = ConstantArray::get(func_table_type, functions);
|
||||
const auto func_table = new GlobalVariable(*m_module, func_table_type, false, GlobalVariable::PrivateLinkage, init_func_table);
|
||||
|
||||
const auto loop_block = BasicBlock::Create(m_context, "__loop", m_function);
|
||||
const auto after_loop = BasicBlock::Create(m_context, "__after_loop", m_function);
|
||||
|
||||
m_ir->CreateBr(loop_block);
|
||||
m_ir->SetInsertPoint(loop_block);
|
||||
|
||||
const auto init_index_value = m_ir->getInt64(0);
|
||||
|
||||
// Loop body
|
||||
const auto body_block = BasicBlock::Create(m_context, "__body", m_function);
|
||||
|
||||
m_ir->CreateBr(body_block); // As do-while because vec_addrs is known to be more than 0
|
||||
m_ir->SetInsertPoint(body_block);
|
||||
|
||||
const auto index_value = m_ir->CreatePHI(get_type<u64>(), 2);
|
||||
index_value->addIncoming(init_index_value, loop_block);
|
||||
|
||||
auto ptr_inst = dyn_cast<GetElementPtrInst>(m_ir->CreateGEP(addr_array->getValueType(), addr_array, {m_ir->getInt64(0), index_value}));
|
||||
assert(ptr_inst->getResultElementType() == get_type<u32>());
|
||||
|
||||
const auto func_pc = ZExt(m_ir->CreateLoad(ptr_inst->getResultElementType(), ptr_inst), get_type<u64>());
|
||||
|
||||
ptr_inst = dyn_cast<GetElementPtrInst>(m_ir->CreateGEP(func_table->getValueType(), func_table, {m_ir->getInt64(0), index_value}));
|
||||
assert(ptr_inst->getResultElementType() == ftype->getPointerTo());
|
||||
|
||||
const auto faddr = m_ir->CreateLoad(ptr_inst->getResultElementType(), ptr_inst);
|
||||
const auto faddr_int = m_ir->CreatePtrToInt(faddr, get_type<uptr>());
|
||||
const auto fval = m_ir->CreateOr(m_ir->CreateShl(m_seg0, 32 + 3), faddr_int);
|
||||
const auto pos = m_ir->CreateShl(m_reloc ? m_ir->CreateAdd(func_pc, m_seg0) : func_pc, 1);
|
||||
const auto ptr = dyn_cast<GetElementPtrInst>(m_ir->CreateGEP(get_type<u8>(), m_exec, pos));
|
||||
|
||||
// Store to jumptable
|
||||
m_ir->CreateStore(fval, ptr);
|
||||
|
||||
// Increment index and branch back to loop
|
||||
const auto post_add = m_ir->CreateAdd(index_value, m_ir->getInt64(1));
|
||||
index_value->addIncoming(post_add, body_block);
|
||||
|
||||
Value* index_check = m_ir->CreateICmpULT(post_add, m_ir->getInt64(vec_addrs.size()));
|
||||
m_ir->CreateCondBr(index_check, body_block, after_loop);
|
||||
|
||||
// Set insertion point to afterloop_block
|
||||
m_ir->SetInsertPoint(after_loop);
|
||||
|
||||
m_ir->CreateRetVoid();
|
||||
|
||||
replace_intrinsics(*m_function);
|
||||
return m_function;
|
||||
}
|
||||
|
||||
Value* PPUTranslator::VecHandleNan(Value* val)
|
||||
{
|
||||
const auto is_nan = m_ir->CreateFCmpUNO(val, val);
|
||||
@@ -2536,7 +2646,7 @@ void PPUTranslator::SUBFC(ppu_opcode_t op)
|
||||
SetGpr(op.rd, result);
|
||||
SetCarry(m_ir->CreateICmpULE(result, b));
|
||||
if (op.rc) SetCrFieldSignedCmp(0, result, m_ir->getInt64(0));
|
||||
if (op.oe) SetOverflow(Call(GetType<bool>(), m_pure_attr, "__subfc_get_ov", a, b));
|
||||
if (op.oe) UNK(op);
|
||||
}
|
||||
|
||||
void PPUTranslator::ADDC(ppu_opcode_t op)
|
||||
@@ -2547,7 +2657,7 @@ void PPUTranslator::ADDC(ppu_opcode_t op)
|
||||
SetGpr(op.rd, result);
|
||||
SetCarry(m_ir->CreateICmpULT(result, b));
|
||||
if (op.rc) SetCrFieldSignedCmp(0, result, m_ir->getInt64(0));
|
||||
if (op.oe) SetOverflow(Call(GetType<bool>(), m_pure_attr, "__addc_get_ov", a, b));
|
||||
if (op.oe) UNK(op);
|
||||
}
|
||||
|
||||
void PPUTranslator::MULHDU(ppu_opcode_t op)
|
||||
@@ -2702,7 +2812,7 @@ void PPUTranslator::SUBF(ppu_opcode_t op)
|
||||
const auto result = m_ir->CreateSub(b, a);
|
||||
SetGpr(op.rd, result);
|
||||
if (op.rc) SetCrFieldSignedCmp(0, result, m_ir->getInt64(0));
|
||||
if (op.oe) SetOverflow(Call(GetType<bool>(), m_pure_attr, "__subf_get_ov", a, b));
|
||||
if (op.oe) UNK(op);
|
||||
}
|
||||
|
||||
void PPUTranslator::LDUX(ppu_opcode_t op)
|
||||
@@ -2804,7 +2914,7 @@ void PPUTranslator::NEG(ppu_opcode_t op)
|
||||
const auto result = m_ir->CreateNeg(reg);
|
||||
SetGpr(op.rd, result);
|
||||
if (op.rc) SetCrFieldSignedCmp(0, result, m_ir->getInt64(0));
|
||||
if (op.oe) SetOverflow(Call(GetType<bool>(), m_pure_attr, "__neg_get_ov", reg));
|
||||
if (op.oe) UNK(op);
|
||||
}
|
||||
|
||||
void PPUTranslator::LBZUX(ppu_opcode_t op)
|
||||
@@ -2837,7 +2947,7 @@ void PPUTranslator::SUBFE(ppu_opcode_t op)
|
||||
SetGpr(op.rd, r2);
|
||||
SetCarry(m_ir->CreateOr(m_ir->CreateICmpULT(r1, a), m_ir->CreateICmpULT(r2, r1)));
|
||||
if (op.rc) SetCrFieldSignedCmp(0, r2, m_ir->getInt64(0));
|
||||
if (op.oe) SetOverflow(Call(GetType<bool>(), m_pure_attr, "__subfe_get_ov", a, b, c));
|
||||
if (op.oe) UNK(op);
|
||||
}
|
||||
|
||||
void PPUTranslator::ADDE(ppu_opcode_t op)
|
||||
@@ -2850,7 +2960,7 @@ void PPUTranslator::ADDE(ppu_opcode_t op)
|
||||
SetGpr(op.rd, r2);
|
||||
SetCarry(m_ir->CreateOr(m_ir->CreateICmpULT(r1, a), m_ir->CreateICmpULT(r2, r1)));
|
||||
if (op.rc) SetCrFieldSignedCmp(0, r2, m_ir->getInt64(0));
|
||||
if (op.oe) SetOverflow(Call(GetType<bool>(), m_pure_attr, "__adde_get_ov", a, b, c));
|
||||
if (op.oe) UNK(op);
|
||||
}
|
||||
|
||||
void PPUTranslator::MTOCRF(ppu_opcode_t op)
|
||||
@@ -2969,7 +3079,7 @@ void PPUTranslator::ADDZE(ppu_opcode_t op)
|
||||
SetGpr(op.rd, result);
|
||||
SetCarry(m_ir->CreateICmpULT(result, a));
|
||||
if (op.rc) SetCrFieldSignedCmp(0, result, m_ir->getInt64(0));
|
||||
if (op.oe) SetOverflow(Call(GetType<bool>(), m_pure_attr, "__addze_get_ov", a, c));
|
||||
if (op.oe) UNK(op);
|
||||
}
|
||||
|
||||
void PPUTranslator::SUBFZE(ppu_opcode_t op)
|
||||
@@ -2980,7 +3090,7 @@ void PPUTranslator::SUBFZE(ppu_opcode_t op)
|
||||
SetGpr(op.rd, result);
|
||||
SetCarry(m_ir->CreateICmpULT(result, a));
|
||||
if (op.rc) SetCrFieldSignedCmp(0, result, m_ir->getInt64(0));
|
||||
if (op.oe) SetOverflow(Call(GetType<bool>(), m_pure_attr, "__subfze_get_ov", a, c));
|
||||
if (op.oe) UNK(op);
|
||||
}
|
||||
|
||||
void PPUTranslator::STDCX(ppu_opcode_t op)
|
||||
@@ -3009,7 +3119,7 @@ void PPUTranslator::SUBFME(ppu_opcode_t op)
|
||||
SetGpr(op.rd, result);
|
||||
SetCarry(m_ir->CreateOr(c, IsNotZero(a)));
|
||||
if (op.rc) SetCrFieldSignedCmp(0, result, m_ir->getInt64(0));
|
||||
if (op.oe) SetOverflow(Call(GetType<bool>(), m_pure_attr, "__subfme_get_ov", a, c));
|
||||
if (op.oe) UNK(op);
|
||||
}
|
||||
|
||||
void PPUTranslator::MULLD(ppu_opcode_t op)
|
||||
@@ -3019,7 +3129,7 @@ void PPUTranslator::MULLD(ppu_opcode_t op)
|
||||
const auto result = m_ir->CreateMul(a, b);
|
||||
SetGpr(op.rd, result);
|
||||
if (op.rc) SetCrFieldSignedCmp(0, result, m_ir->getInt64(0));
|
||||
if (op.oe) SetOverflow(Call(GetType<bool>(), m_pure_attr, "__mulld_get_ov", a, b));
|
||||
if (op.oe) UNK(op);
|
||||
}
|
||||
|
||||
void PPUTranslator::ADDME(ppu_opcode_t op)
|
||||
@@ -3030,7 +3140,7 @@ void PPUTranslator::ADDME(ppu_opcode_t op)
|
||||
SetGpr(op.rd, result);
|
||||
SetCarry(m_ir->CreateOr(c, IsNotZero(a)));
|
||||
if (op.rc) SetCrFieldSignedCmp(0, result, m_ir->getInt64(0));
|
||||
if (op.oe) SetOverflow(Call(GetType<bool>(), m_pure_attr, "__addme_get_ov", a, c));
|
||||
if (op.oe) UNK(op);
|
||||
}
|
||||
|
||||
void PPUTranslator::MULLW(ppu_opcode_t op)
|
||||
@@ -3040,7 +3150,7 @@ void PPUTranslator::MULLW(ppu_opcode_t op)
|
||||
const auto result = m_ir->CreateMul(a, b);
|
||||
SetGpr(op.rd, result);
|
||||
if (op.rc) SetCrFieldSignedCmp(0, result, m_ir->getInt64(0));
|
||||
if (op.oe) SetOverflow(Call(GetType<bool>(), m_pure_attr, "__mullw_get_ov", a, b));
|
||||
if (op.oe) UNK(op);
|
||||
}
|
||||
|
||||
void PPUTranslator::DCBTST(ppu_opcode_t)
|
||||
|
||||
@@ -336,6 +336,7 @@ public:
|
||||
|
||||
// Parses PPU opcodes and translate them into LLVM IR
|
||||
llvm::Function* Translate(const ppu_function& info);
|
||||
llvm::Function* GetSymbolResolver(const ppu_module& info);
|
||||
|
||||
void MFVSCR(ppu_opcode_t op);
|
||||
void MTVSCR(ppu_opcode_t op);
|
||||
|
||||
@@ -802,6 +802,9 @@ void spu_cache::initialize(bool build_existing_cache)
|
||||
|
||||
compiler->init();
|
||||
|
||||
// Counter for error reporting
|
||||
u32 logged_error = 0;
|
||||
|
||||
// How much every thread compiled
|
||||
uint result = 0;
|
||||
|
||||
@@ -861,6 +864,14 @@ void spu_cache::initialize(bool build_existing_cache)
|
||||
if (func2 != func)
|
||||
{
|
||||
spu_log.error("[0x%05x] SPU Analyser failed, %u vs %u", func2.entry_point, func2.data.size(), size0);
|
||||
|
||||
if (logged_error < 2)
|
||||
{
|
||||
std::string log;
|
||||
compiler->dump(func, log);
|
||||
spu_log.notice("[0x%05x] Function: %s", func.entry_point, log);
|
||||
logged_error++;
|
||||
}
|
||||
}
|
||||
else if (!compiler->compile(std::move(func2)))
|
||||
{
|
||||
@@ -1024,7 +1035,7 @@ void spu_cache::initialize(bool build_existing_cache)
|
||||
}
|
||||
}
|
||||
|
||||
if (new_entry != umax && !spu_thread::is_exec_code(new_entry, { reinterpret_cast<const u8*>(ls.data()), SPU_LS_SIZE }))
|
||||
if (new_entry != umax && !spu_thread::is_exec_code(new_entry, { reinterpret_cast<const u8*>(ls.data()), SPU_LS_SIZE }, 0, true))
|
||||
{
|
||||
new_entry = umax;
|
||||
}
|
||||
@@ -1033,7 +1044,7 @@ void spu_cache::initialize(bool build_existing_cache)
|
||||
{
|
||||
new_entry = start_new;
|
||||
|
||||
while (new_entry < next_func && (ls[start_new / 4] < 0x3fffc || !spu_thread::is_exec_code(new_entry, { reinterpret_cast<const u8*>(ls.data()), SPU_LS_SIZE })))
|
||||
while (new_entry < next_func && (ls[start_new / 4] < 0x3fffc || !spu_thread::is_exec_code(new_entry, { reinterpret_cast<const u8*>(ls.data()), SPU_LS_SIZE }, 0, true)))
|
||||
{
|
||||
new_entry += 4;
|
||||
}
|
||||
@@ -2281,13 +2292,13 @@ std::vector<u32> spu_thread::discover_functions(u32 base_addr, std::span<const u
|
||||
calls.erase(std::remove_if(calls.begin(), calls.end(), [&](u32 caller)
|
||||
{
|
||||
// Check the validity of both the callee code and the following caller code
|
||||
return !is_exec_code(caller, ls, base_addr) || !is_exec_code(caller + 4, ls, base_addr);
|
||||
return !is_exec_code(caller, ls, base_addr, true) || !is_exec_code(caller + 4, ls, base_addr, true);
|
||||
}), calls.end());
|
||||
|
||||
branches.erase(std::remove_if(branches.begin(), branches.end(), [&](u32 caller)
|
||||
{
|
||||
// Check the validity of the callee code
|
||||
return !is_exec_code(caller, ls, base_addr);
|
||||
return !is_exec_code(caller, ls, base_addr, true);
|
||||
}), branches.end());
|
||||
|
||||
std::vector<u32> addrs;
|
||||
@@ -2303,7 +2314,56 @@ std::vector<u32> spu_thread::discover_functions(u32 base_addr, std::span<const u
|
||||
continue;
|
||||
}
|
||||
|
||||
if (std::count(calls.begin(), calls.end(), func))
|
||||
{
|
||||
// Cannot call another call instruction (link is overwritten)
|
||||
continue;
|
||||
}
|
||||
|
||||
addrs.push_back(func);
|
||||
|
||||
// Detect an "arguments passing" block, possible queue another function
|
||||
for (u32 next = func, it = 10; it && next >= base_addr && next < std::min<u32>(base_addr + ::size32(ls), 0x3FFF0); it--, next += 4)
|
||||
{
|
||||
const spu_opcode_t test_op{read_from_ptr<be_t<u32>>(ls, next - base_addr)};
|
||||
const auto type = g_spu_itype.decode(test_op.opcode);
|
||||
|
||||
if (type & spu_itype::branch && type != spu_itype::BR)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (type == spu_itype::UNK || !test_op.opcode)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (type != spu_itype::BR)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const u32 target = op_branch_targets(next, op)[0];
|
||||
|
||||
if (target == umax || addr + 4 == target || target == addr || std::count(addrs.begin(), addrs.end(), target))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Detect backwards branch to the block in examination
|
||||
if (target >= func && target <= next)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (!is_exec_code(target, ls, base_addr, true))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
addrs.push_back(target);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (u32 addr : branches)
|
||||
@@ -2317,7 +2377,52 @@ std::vector<u32> spu_thread::discover_functions(u32 base_addr, std::span<const u
|
||||
continue;
|
||||
}
|
||||
|
||||
// Search for AI R1, +x or OR R3/4, Rx, 0
|
||||
// Search for AI R1, -x in the called code
|
||||
// Reasoning: AI R1, -x means stack frame creation, this is likely be a function
|
||||
for (u32 next = func, it = 10; it && next >= base_addr && next < std::min<u32>(base_addr + ::size32(ls), 0x3FFF0); it--, next += 4)
|
||||
{
|
||||
const spu_opcode_t test_op{read_from_ptr<be_t<u32>>(ls, next - base_addr)};
|
||||
const auto type = g_spu_itype.decode(test_op.opcode);
|
||||
|
||||
if (type & spu_itype::branch)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (type == spu_itype::UNK || !test_op.opcode)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
bool is_func = false;
|
||||
|
||||
if (type == spu_itype::AI && test_op.rt == 1u && test_op.ra == 1u)
|
||||
{
|
||||
if (test_op.si10 >= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
is_func = true;
|
||||
}
|
||||
|
||||
if (!is_func)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
addr = SPU_LS_SIZE + 4; // Terminate the next condition, no further checks needed
|
||||
|
||||
if (std::count(addrs.begin(), addrs.end(), func))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
addrs.push_back(func);
|
||||
break;
|
||||
}
|
||||
|
||||
// Search for AI R1, +x or OR R3/4, Rx, 0 before the branch
|
||||
// Reasoning: AI R1, +x means stack pointer restoration, branch after that is likely a tail call
|
||||
// R3 and R4 are common function arguments because they are the first two
|
||||
for (u32 back = addr - 4, it = 10; it && back >= base_addr && back < std::min<u32>(base_addr + ::size32(ls), 0x3FFF0); it--, back -= 4)
|
||||
@@ -2394,9 +2499,9 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
|
||||
workload.push_back(entry_point);
|
||||
|
||||
std::memset(m_regmod.data(), 0xff, sizeof(m_regmod));
|
||||
std::memset(m_use_ra.data(), 0xff, sizeof(m_use_ra));
|
||||
std::memset(m_use_rb.data(), 0xff, sizeof(m_use_rb));
|
||||
std::memset(m_use_rc.data(), 0xff, sizeof(m_use_rc));
|
||||
m_use_ra.reset();
|
||||
m_use_rb.reset();
|
||||
m_use_rc.reset();
|
||||
m_targets.clear();
|
||||
m_preds.clear();
|
||||
m_preds[entry_point];
|
||||
@@ -2491,11 +2596,11 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
|
||||
if (auto iflags = g_spu_iflag.decode(data))
|
||||
{
|
||||
if (+iflags & +spu_iflag::use_ra)
|
||||
m_use_ra[pos / 4] = op.ra;
|
||||
m_use_ra.set(pos / 4);
|
||||
if (+iflags & +spu_iflag::use_rb)
|
||||
m_use_rb[pos / 4] = op.rb;
|
||||
m_use_rb.set(pos / 4);
|
||||
if (+iflags & +spu_iflag::use_rc)
|
||||
m_use_rc[pos / 4] = op.rc;
|
||||
m_use_rc.set(pos / 4);
|
||||
}
|
||||
|
||||
// Analyse instruction
|
||||
@@ -2837,7 +2942,6 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
|
||||
if (g_cfg.core.spu_block_size == spu_block_size_type::giga && !sync)
|
||||
{
|
||||
m_entry_info[target / 4] = true;
|
||||
add_block(target);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2845,13 +2949,9 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
|
||||
{
|
||||
spu_log.notice("[0x%x] At 0x%x: ignoring fixed tail call to 0x%x (SYNC)", entry_point, pos, target);
|
||||
}
|
||||
|
||||
if (target > entry_point)
|
||||
{
|
||||
limit = std::min<u32>(limit, target);
|
||||
}
|
||||
}
|
||||
|
||||
add_block(target);
|
||||
next_block();
|
||||
break;
|
||||
}
|
||||
@@ -2927,11 +3027,6 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
|
||||
m_regmod[pos / 4] = s_reg_mfc_size;
|
||||
break;
|
||||
}
|
||||
case MFC_Cmd:
|
||||
{
|
||||
m_use_rb[pos / 4] = s_reg_mfc_eal;
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
|
||||
@@ -3378,10 +3473,13 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
|
||||
reg_save = op.rt;
|
||||
}
|
||||
|
||||
for (auto* _use : {&m_use_ra, &m_use_rb, &m_use_rc})
|
||||
for (auto _use : std::initializer_list<std::pair<u32, bool>>{{op.ra, m_use_ra.test(ia / 4)}
|
||||
, {op.rb, m_use_rb.test(ia / 4)}, {op.rc, m_use_rc.test(ia / 4)}})
|
||||
{
|
||||
if (u8 reg = (*_use)[ia / 4]; reg < s_reg_max)
|
||||
if (_use.second)
|
||||
{
|
||||
const u32 reg = _use.first;
|
||||
|
||||
// Register reg use only if it happens before reg mod
|
||||
if (!block.reg_mod[reg])
|
||||
{
|
||||
@@ -3396,7 +3494,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
|
||||
}
|
||||
}
|
||||
|
||||
if (m_use_rb[ia / 4] == s_reg_mfc_eal)
|
||||
if (type == spu_itype::WRCH && op.ra == MFC_Cmd)
|
||||
{
|
||||
// Expand MFC_Cmd reg use
|
||||
for (u8 reg : {s_reg_mfc_lsa, s_reg_mfc_tag, s_reg_mfc_size})
|
||||
@@ -3441,10 +3539,6 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
|
||||
case spu_itype::BRASL:
|
||||
is_call = spu_branch_target(0, op.i16) != ia + 4;
|
||||
break;
|
||||
case spu_itype::BRA:
|
||||
is_call = true;
|
||||
is_tail = true;
|
||||
break;
|
||||
case spu_itype::BISL:
|
||||
case spu_itype::BISLED:
|
||||
is_call = true;
|
||||
@@ -4407,11 +4501,12 @@ struct spu_llvm_worker
|
||||
void operator()()
|
||||
{
|
||||
// SPU LLVM Recompiler instance
|
||||
const auto compiler = spu_recompiler_base::make_llvm_recompiler();
|
||||
compiler->init();
|
||||
std::unique_ptr<spu_recompiler_base> compiler;
|
||||
|
||||
// Fake LS
|
||||
std::vector<be_t<u32>> ls(0x10000);
|
||||
std::vector<be_t<u32>> ls;
|
||||
|
||||
bool set_relax_flag = false;
|
||||
|
||||
for (auto slice = registered.pop_all();; [&]
|
||||
{
|
||||
@@ -4425,6 +4520,12 @@ struct spu_llvm_worker
|
||||
return;
|
||||
}
|
||||
|
||||
if (set_relax_flag)
|
||||
{
|
||||
spu_thread::g_spu_work_count--;
|
||||
set_relax_flag = false;
|
||||
}
|
||||
|
||||
thread_ctrl::wait_on(utils::bless<atomic_t<u32>>(®istered)[1], 0);
|
||||
slice = registered.pop_all();
|
||||
}())
|
||||
@@ -4446,6 +4547,21 @@ struct spu_llvm_worker
|
||||
break;
|
||||
}
|
||||
|
||||
if (!compiler)
|
||||
{
|
||||
// Postponed initialization
|
||||
compiler = spu_recompiler_base::make_llvm_recompiler();
|
||||
compiler->init();
|
||||
|
||||
ls.resize(SPU_LS_SIZE / sizeof(be_t<u32>));
|
||||
}
|
||||
|
||||
if (!set_relax_flag)
|
||||
{
|
||||
spu_thread::g_spu_work_count++;
|
||||
set_relax_flag = true;
|
||||
}
|
||||
|
||||
const auto& func = *prog->second;
|
||||
|
||||
// Get data start
|
||||
@@ -4487,12 +4603,18 @@ struct spu_llvm_worker
|
||||
else
|
||||
{
|
||||
spu_log.fatal("[0x%05x] Compilation failed.", func.entry_point);
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
// Clear fake LS
|
||||
std::memset(ls.data() + start / 4, 0, 4 * (size0 - 1));
|
||||
}
|
||||
|
||||
if (set_relax_flag)
|
||||
{
|
||||
spu_thread::g_spu_work_count--;
|
||||
set_relax_flag = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -4516,6 +4638,17 @@ struct spu_llvm
|
||||
return;
|
||||
}
|
||||
|
||||
while (!registered && thread_ctrl::state() != thread_state::aborting)
|
||||
{
|
||||
// Wait for the first SPU block before launching any thread
|
||||
thread_ctrl::wait_on(utils::bless<atomic_t<u32>>(®istered)[1], 0);
|
||||
}
|
||||
|
||||
if (thread_ctrl::state() == thread_state::aborting)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// To compile (hash -> item)
|
||||
std::unordered_multimap<u64, spu_item*, value_hash<u64>> enqueued;
|
||||
|
||||
@@ -4566,19 +4699,22 @@ struct spu_llvm
|
||||
|
||||
if (uint hc = utils::get_thread_count(); hc >= 12)
|
||||
{
|
||||
worker_count = hc - 10;
|
||||
worker_count = hc - 12 + 3;
|
||||
}
|
||||
else if (hc >= 6)
|
||||
{
|
||||
worker_count = 2;
|
||||
}
|
||||
|
||||
u32 worker_index = 0;
|
||||
u32 notify_compile_count = 0;
|
||||
u32 compile_pending = 0;
|
||||
std::vector<u8> notify_compile(worker_count);
|
||||
|
||||
m_workers = make_single<named_thread_group<spu_llvm_worker>>("SPUW.", worker_count);
|
||||
auto workers_ptr = m_workers.load();
|
||||
auto& workers = *workers_ptr;
|
||||
|
||||
usz add_count = 65535;
|
||||
|
||||
while (thread_ctrl::state() != thread_state::aborting)
|
||||
{
|
||||
for (const auto& pair : registered.pop_all())
|
||||
@@ -4601,7 +4737,7 @@ struct spu_llvm
|
||||
{
|
||||
if (notify_compile[i])
|
||||
{
|
||||
(workers.begin() + (i % worker_count))->registered.notify();
|
||||
(workers.begin() + i)->registered.notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4609,9 +4745,9 @@ struct spu_llvm
|
||||
// Interrupt profiler thread and put it to sleep
|
||||
static_cast<void>(prof_mutex.reset());
|
||||
thread_ctrl::wait_on(utils::bless<atomic_t<u32>>(®istered)[1], 0);
|
||||
add_count = 65535; // Reset count
|
||||
std::fill(notify_compile.begin(), notify_compile.end(), 0); // Reset notification flags
|
||||
notify_compile_count = 0;
|
||||
compile_pending = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -4652,18 +4788,26 @@ struct spu_llvm
|
||||
{
|
||||
notify_compile[worker_index % worker_count] = 1;
|
||||
notify_compile_count++;
|
||||
}
|
||||
|
||||
if (notify_compile_count == notify_compile.size())
|
||||
compile_pending++;
|
||||
|
||||
// Notify all before queue runs out if there is considerable excess
|
||||
// Optimized that: if there are many workers, it acts soon
|
||||
// If there are only a few workers, it postpones notifications until there is some more workload
|
||||
if (notify_compile_count && std::min<u32>(7, utils::aligned_div<u32>(worker_count * 2, 3) + 2) <= compile_pending)
|
||||
{
|
||||
for (usz i = 0; i < worker_count; i++)
|
||||
{
|
||||
// Notify all
|
||||
for (usz i = 0; i < worker_count; i++)
|
||||
if (notify_compile[i])
|
||||
{
|
||||
(workers.begin() + (i % worker_count))->registered.notify();
|
||||
(workers.begin() + i)->registered.notify();
|
||||
}
|
||||
|
||||
std::fill(notify_compile.begin(), notify_compile.end(), 0); // Reset notification flags
|
||||
notify_compile_count = 0;
|
||||
}
|
||||
|
||||
std::fill(notify_compile.begin(), notify_compile.end(), 0); // Reset notification flags
|
||||
notify_compile_count = 0;
|
||||
compile_pending = 0;
|
||||
}
|
||||
|
||||
worker_index++;
|
||||
@@ -5061,3 +5205,25 @@ std::unique_ptr<spu_recompiler_base> spu_recompiler_base::make_fast_llvm_recompi
|
||||
{
|
||||
return std::make_unique<spu_fast>();
|
||||
}
|
||||
|
||||
extern std::string format_spu_func_info(u32 addr, cpu_thread* spu)
|
||||
{
|
||||
spu_thread* _spu = static_cast<spu_thread*>(spu);
|
||||
|
||||
std::unique_ptr<spu_recompiler_base> compiler = spu_recompiler_base::make_asmjit_recompiler();
|
||||
compiler->init();
|
||||
auto func = compiler->analyse(reinterpret_cast<const be_t<u32>*>(_spu->ls), addr);
|
||||
|
||||
std::string info;
|
||||
{
|
||||
sha1_context ctx;
|
||||
u8 output[20];
|
||||
|
||||
sha1_starts(&ctx);
|
||||
sha1_update(&ctx, reinterpret_cast<const u8*>(func.data.data()), func.data.size() * 4);
|
||||
sha1_finish(&ctx, output);
|
||||
fmt::append(info, "size=%d, end=0x%x, hash=%s", func.data.size(), addr + func.data.size() * 4, fmt::base57(output));
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
#include "SPUThread.h"
|
||||
#include "SPUAnalyser.h"
|
||||
#include "SPUInterpreter.h"
|
||||
#include "SPUDisAsm.h"
|
||||
#include <algorithm>
|
||||
#include <thread>
|
||||
#include <unordered_set>
|
||||
|
||||
@@ -30,26 +32,33 @@ const extern spu_decoder<spu_iflag> g_spu_iflag;
|
||||
#pragma warning(push, 0)
|
||||
#else
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wall"
|
||||
#pragma GCC diagnostic ignored "-Wextra"
|
||||
#pragma GCC diagnostic ignored "-Wold-style-cast"
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
|
||||
#pragma GCC diagnostic ignored "-Weffc++"
|
||||
#pragma GCC diagnostic ignored "-Wmissing-noreturn"
|
||||
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
|
||||
#endif
|
||||
#include <llvm/ADT/PostOrderIterator.h>
|
||||
#include <llvm/Analysis/PostDominators.h>
|
||||
#include <llvm/IR/InlineAsm.h>
|
||||
#include <llvm/IR/Verifier.h>
|
||||
#include <llvm/TargetParser/Host.h>
|
||||
#include <llvm/Transforms/Utils/BasicBlockUtils.h>
|
||||
#if LLVM_VERSION_MAJOR < 17
|
||||
#include "llvm/ADT/Triple.h"
|
||||
#include <llvm/IR/LegacyPassManager.h>
|
||||
#include <llvm/Transforms/Scalar.h>
|
||||
#include <llvm/Analysis/AliasAnalysis.h>
|
||||
#else
|
||||
#include <llvm/Analysis/CGSCCPassManager.h>
|
||||
#include <llvm/Analysis/LoopAnalysisManager.h>
|
||||
#include <llvm/IR/PassManager.h>
|
||||
#include <llvm/Passes/PassBuilder.h>
|
||||
#include <llvm/Transforms/Scalar/ADCE.h>
|
||||
#include <llvm/Transforms/Scalar/DeadStoreElimination.h>
|
||||
#include <llvm/Transforms/Scalar/EarlyCSE.h>
|
||||
#include <llvm/Transforms/Scalar/LICM.h>
|
||||
#include <llvm/Transforms/Scalar/LoopPassManager.h>
|
||||
#include <llvm/Transforms/Scalar/SimplifyCFG.h>
|
||||
#endif
|
||||
#include "llvm/TargetParser/Host.h"
|
||||
#include "llvm/IR/LegacyPassManager.h"
|
||||
#include "llvm/IR/Verifier.h"
|
||||
#include "llvm/IR/InlineAsm.h"
|
||||
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
|
||||
#include "llvm/Transforms/Scalar.h"
|
||||
#include "llvm/Analysis/PostDominators.h"
|
||||
#include "llvm/Analysis/AliasAnalysis.h"
|
||||
#include "llvm/ADT/PostOrderIterator.h"
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#else
|
||||
@@ -152,7 +161,8 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator
|
||||
// Store reordering/elimination protection
|
||||
std::array<usz, s_reg_max> store_context_last_id = fill_array<usz>(0); // Protects against illegal forward ordering
|
||||
std::array<usz, s_reg_max> store_context_first_id = fill_array<usz>(usz{umax}); // Protects against illegal past store elimination (backwards ordering is not implemented)
|
||||
std::array<usz, s_reg_max> store_context_ctr = fill_array<usz>(1); // Store barrier cointer
|
||||
std::array<usz, s_reg_max> store_context_ctr = fill_array<usz>(1); // Store barrier counter
|
||||
bool has_gpr_memory_barriers = false; // Summarizes whether GPR barriers exist this block (as if checking all store_context_ctr entries)
|
||||
|
||||
bool does_gpr_barrier_proceed_last_store(u32 i) const noexcept
|
||||
{
|
||||
@@ -494,8 +504,6 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator
|
||||
return result;
|
||||
}
|
||||
|
||||
ensure(!absolute);
|
||||
|
||||
auto& result = m_blocks[target].block;
|
||||
|
||||
if (!result)
|
||||
@@ -1050,7 +1058,7 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator
|
||||
m_ir->SetInsertPoint(check);
|
||||
update_pc(addr);
|
||||
|
||||
if (may_be_unsafe_for_savestate && std::none_of(std::begin(m_block->phi), std::end(m_block->phi), FN(!!x)))
|
||||
if (may_be_unsafe_for_savestate && m_block && m_block->bb->preds.empty())
|
||||
{
|
||||
may_be_unsafe_for_savestate = false;
|
||||
}
|
||||
@@ -1114,6 +1122,7 @@ public:
|
||||
}
|
||||
|
||||
const u32 start0 = _func.entry_point;
|
||||
const usz func_size = _func.data.size();
|
||||
|
||||
const auto add_loc = m_spurt->add_empty(std::move(_func));
|
||||
|
||||
@@ -1137,9 +1146,11 @@ public:
|
||||
|
||||
std::string log;
|
||||
|
||||
bool add_to_file = false;
|
||||
|
||||
if (auto& cache = g_fxo->get<spu_cache>(); cache && g_cfg.core.spu_cache && !add_loc->cached.exchange(1))
|
||||
{
|
||||
cache.add(func);
|
||||
add_to_file = true;
|
||||
}
|
||||
|
||||
{
|
||||
@@ -1661,10 +1672,14 @@ public:
|
||||
|
||||
std::vector<block_info*> block_q;
|
||||
block_q.reserve(m_blocks.size());
|
||||
|
||||
bool has_gpr_memory_barriers = false;
|
||||
|
||||
for (auto& [a, b] : m_blocks)
|
||||
{
|
||||
block_q.emplace_back(&b);
|
||||
bb_to_info[b.block] = &b;
|
||||
has_gpr_memory_barriers |= b.has_gpr_memory_barriers;
|
||||
}
|
||||
|
||||
for (usz bi = 0; bi < block_q.size();)
|
||||
@@ -1672,7 +1687,7 @@ public:
|
||||
auto bqbi = block_q[bi++];
|
||||
|
||||
// TODO: process all registers up to s_reg_max
|
||||
for (u32 i = 0; i < 128; i++)
|
||||
for (u32 i = 0; i <= s_reg_127; i++)
|
||||
{
|
||||
// Check if the store is beyond the last barrier
|
||||
if (auto& bs = bqbi->store[i]; bs && !bqbi->does_gpr_barrier_proceed_last_store(i))
|
||||
@@ -1721,16 +1736,28 @@ public:
|
||||
|
||||
// Find nearest common post-dominator
|
||||
llvm::BasicBlock* common_pdom = killers[0];
|
||||
|
||||
if (has_gpr_memory_barriers)
|
||||
{
|
||||
// Cannot optimize block walk-through, need to inspect all possible memory barriers in the way
|
||||
common_pdom = nullptr;
|
||||
}
|
||||
|
||||
for (auto* bbb : llvm::drop_begin(killers))
|
||||
{
|
||||
if (!common_pdom)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
common_pdom = pdt.findNearestCommonDominator(common_pdom, bbb);
|
||||
}
|
||||
|
||||
// Shortcut
|
||||
if (!pdt.dominates(common_pdom, bs->getParent()))
|
||||
if (common_pdom && !pdt.dominates(common_pdom, bs->getParent()))
|
||||
{
|
||||
common_pdom = nullptr;
|
||||
}
|
||||
|
||||
// Look for possibly-dead store in CFG starting from the exit nodes
|
||||
llvm::SetVector<llvm::BasicBlock*> work_list;
|
||||
@@ -1867,7 +1894,7 @@ public:
|
||||
|
||||
for (usz bi = 0; bi < block_q.size(); bi++)
|
||||
{
|
||||
for (u32 i = 0; i < 128; i++)
|
||||
for (u32 i = 0; i <= s_reg_127; i++)
|
||||
{
|
||||
// If store isn't erased, try to sink it
|
||||
if (auto& bs = block_q[bi]->store[i]; bs && block_q[bi]->bb->targets.size() > 1 && !block_q[bi]->does_gpr_barrier_proceed_last_store(i))
|
||||
@@ -1901,8 +1928,10 @@ public:
|
||||
auto si = llvm::cast<StoreInst>(m_ir->Insert(bs->clone()));
|
||||
if (b2->store[i] == nullptr)
|
||||
{
|
||||
// Protect against backwards ordering now
|
||||
b2->store[i] = si;
|
||||
b2->store_context_last_id[i] = 0;
|
||||
b2->store_context_first_id[i] = b2->store_context_ctr[i] + 1;
|
||||
|
||||
if (!std::count(block_q.begin() + bi, block_q.end(), b2))
|
||||
{
|
||||
@@ -2012,6 +2041,7 @@ public:
|
||||
m_function_table->eraseFromParent();
|
||||
}
|
||||
|
||||
#if LLVM_VERSION_MAJOR < 17
|
||||
// Initialize pass manager
|
||||
legacy::FunctionPassManager pm(_module.get());
|
||||
|
||||
@@ -2019,16 +2049,42 @@ public:
|
||||
pm.add(createEarlyCSEPass());
|
||||
pm.add(createCFGSimplificationPass());
|
||||
//pm.add(createNewGVNPass());
|
||||
#if LLVM_VERSION_MAJOR < 17
|
||||
pm.add(createDeadStoreEliminationPass());
|
||||
#endif
|
||||
pm.add(createLICMPass());
|
||||
#if LLVM_VERSION_MAJOR < 17
|
||||
pm.add(createAggressiveDCEPass());
|
||||
#else
|
||||
pm.add(createDeadCodeEliminationPass());
|
||||
#endif
|
||||
//pm.add(createLintPass()); // Check
|
||||
#else
|
||||
|
||||
// Create the analysis managers.
|
||||
// These must be declared in this order so that they are destroyed in the
|
||||
// correct order due to inter-analysis-manager references.
|
||||
LoopAnalysisManager lam;
|
||||
FunctionAnalysisManager fam;
|
||||
CGSCCAnalysisManager cgam;
|
||||
ModuleAnalysisManager mam;
|
||||
|
||||
// Create the new pass manager builder.
|
||||
// Take a look at the PassBuilder constructor parameters for more
|
||||
// customization, e.g. specifying a TargetMachine or various debugging
|
||||
// options.
|
||||
PassBuilder pb;
|
||||
|
||||
// Register all the basic analyses with the managers.
|
||||
pb.registerModuleAnalyses(mam);
|
||||
pb.registerCGSCCAnalyses(cgam);
|
||||
pb.registerFunctionAnalyses(fam);
|
||||
pb.registerLoopAnalyses(lam);
|
||||
pb.crossRegisterProxies(lam, fam, cgam, mam);
|
||||
|
||||
FunctionPassManager fpm;
|
||||
// Basic optimizations
|
||||
fpm.addPass(EarlyCSEPass(true));
|
||||
fpm.addPass(SimplifyCFGPass());
|
||||
fpm.addPass(DSEPass());
|
||||
fpm.addPass(createFunctionToLoopPassAdaptor(LICMPass(LICMOptions()), true));
|
||||
fpm.addPass(ADCEPass());
|
||||
#endif
|
||||
|
||||
for (auto& f : *m_module)
|
||||
{
|
||||
@@ -2038,7 +2094,11 @@ public:
|
||||
for (const auto& func : m_functions)
|
||||
{
|
||||
const auto f = func.second.fn ? func.second.fn : func.second.chunk;
|
||||
#if LLVM_VERSION_MAJOR < 17
|
||||
pm.run(*f);
|
||||
#else
|
||||
fpm.run(*f, fam);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Clear context (TODO)
|
||||
@@ -2095,6 +2155,14 @@ public:
|
||||
// Rebuild trampoline if necessary
|
||||
if (!m_spurt->rebuild_ubertrampoline(func.data[0]))
|
||||
{
|
||||
if (auto& cache = g_fxo->get<spu_cache>())
|
||||
{
|
||||
if (add_to_file)
|
||||
{
|
||||
cache.add(func);
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -2115,9 +2183,14 @@ public:
|
||||
asm("DSB ISH");
|
||||
#endif
|
||||
|
||||
if (g_fxo->get<spu_cache>().operator bool())
|
||||
if (auto& cache = g_fxo->get<spu_cache>())
|
||||
{
|
||||
spu_log.success("New block compiled successfully");
|
||||
if (add_to_file)
|
||||
{
|
||||
cache.add(func);
|
||||
}
|
||||
|
||||
spu_log.success("New SPU block compiled successfully (size=%u)", func_size);
|
||||
}
|
||||
|
||||
return fn;
|
||||
@@ -2489,24 +2562,9 @@ public:
|
||||
m_function_table->setInitializer(ConstantArray::get(ArrayType::get(if_type->getPointerTo(), 1ull << m_interp_magn), iptrs));
|
||||
m_function_table = nullptr;
|
||||
|
||||
// Initialize pass manager
|
||||
legacy::FunctionPassManager pm(_module.get());
|
||||
|
||||
// Basic optimizations
|
||||
pm.add(createEarlyCSEPass());
|
||||
pm.add(createCFGSimplificationPass());
|
||||
#if LLVM_VERSION_MAJOR < 17
|
||||
pm.add(createDeadStoreEliminationPass());
|
||||
pm.add(createAggressiveDCEPass());
|
||||
#else
|
||||
pm.add(createDeadCodeEliminationPass());
|
||||
#endif
|
||||
//pm.add(createLintPass());
|
||||
|
||||
for (auto& f : *_module)
|
||||
{
|
||||
replace_intrinsics(f);
|
||||
//pm.run(f);
|
||||
}
|
||||
|
||||
std::string log;
|
||||
@@ -2639,13 +2697,13 @@ public:
|
||||
}
|
||||
|
||||
update_pc();
|
||||
ensure_gpr_stores();
|
||||
call("spu_syscall", &exec_stop, m_thread, m_ir->getInt32(op.opcode & 0x3fff));
|
||||
|
||||
if (g_cfg.core.spu_block_size == spu_block_size_type::safe)
|
||||
{
|
||||
m_block->block_end = m_ir->GetInsertBlock();
|
||||
update_pc(m_pos + 4);
|
||||
ensure_gpr_stores();
|
||||
tail_chunk(m_dispatch);
|
||||
return;
|
||||
}
|
||||
@@ -2707,6 +2765,7 @@ public:
|
||||
{
|
||||
// Make previous stores not able to be reordered beyond this point or be deleted
|
||||
std::for_each(m_block->store_context_ctr.begin(), m_block->store_context_ctr.end(), FN(x++));
|
||||
m_block->has_gpr_memory_barriers = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2905,6 +2964,40 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
switch (op.ra)
|
||||
{
|
||||
case SPU_WrOutMbox:
|
||||
case SPU_WrOutIntrMbox:
|
||||
case SPU_RdSigNotify1:
|
||||
case SPU_RdSigNotify2:
|
||||
case SPU_RdInMbox:
|
||||
case SPU_RdEventStat:
|
||||
{
|
||||
bool loop_is_likely = op.ra == SPU_RdSigNotify1 || op.ra == SPU_RdSigNotify2;
|
||||
|
||||
for (u32 block_start : m_block->bb->preds)
|
||||
{
|
||||
if (block_start >= m_pos)
|
||||
{
|
||||
loop_is_likely = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (loop_is_likely || g_cfg.savestate.compatible_mode)
|
||||
{
|
||||
ensure_gpr_stores();
|
||||
check_state(m_pos, false);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (op.ra)
|
||||
{
|
||||
case SPU_WrOutMbox:
|
||||
@@ -3027,14 +3120,25 @@ public:
|
||||
_spu->do_mfc();
|
||||
}
|
||||
|
||||
template <bool Saveable>
|
||||
static void exec_mfc_cmd(spu_thread* _spu)
|
||||
{
|
||||
if constexpr (!Saveable)
|
||||
{
|
||||
_spu->unsavable = true;
|
||||
}
|
||||
|
||||
if (!_spu->process_mfc_cmd() || _spu->state & cpu_flag::again)
|
||||
{
|
||||
spu_runtime::g_escape(_spu);
|
||||
fmt::throw_exception("exec_mfc_cmd(): Should not abort!");
|
||||
}
|
||||
|
||||
static_cast<void>(_spu->test_stopped());
|
||||
|
||||
if constexpr (!Saveable)
|
||||
{
|
||||
_spu->unsavable = false;
|
||||
}
|
||||
}
|
||||
|
||||
void WRCH(spu_opcode_t op) //
|
||||
@@ -3213,8 +3317,17 @@ public:
|
||||
case MFC_GETLB_CMD:
|
||||
case MFC_GETLF_CMD:
|
||||
{
|
||||
m_ir->CreateBr(next);
|
||||
m_ir->SetInsertPoint(exec);
|
||||
m_ir->CreateUnreachable();
|
||||
m_ir->SetInsertPoint(fail);
|
||||
m_ir->CreateUnreachable();
|
||||
m_ir->SetInsertPoint(next);
|
||||
m_ir->CreateStore(ci, spu_ptr<u8>(&spu_thread::ch_mfc_cmd, &spu_mfc_cmd::cmd));
|
||||
update_pc();
|
||||
ensure_gpr_stores();
|
||||
[[fallthrough]];
|
||||
call("spu_exec_mfc_cmd_saveable", &exec_mfc_cmd<true>, m_thread);
|
||||
return;
|
||||
}
|
||||
case MFC_SDCRZ_CMD:
|
||||
case MFC_GETLLAR_CMD:
|
||||
@@ -3231,7 +3344,7 @@ public:
|
||||
m_ir->SetInsertPoint(next);
|
||||
m_ir->CreateStore(ci, spu_ptr<u8>(&spu_thread::ch_mfc_cmd, &spu_mfc_cmd::cmd));
|
||||
update_pc();
|
||||
call("spu_exec_mfc_cmd", &exec_mfc_cmd, m_thread);
|
||||
call("spu_exec_mfc_cmd", &exec_mfc_cmd<false>, m_thread);
|
||||
return;
|
||||
}
|
||||
case MFC_SNDSIG_CMD:
|
||||
@@ -3290,7 +3403,7 @@ public:
|
||||
}
|
||||
|
||||
m_ir->CreateStore(ci, spu_ptr<u8>(&spu_thread::ch_mfc_cmd, &spu_mfc_cmd::cmd));
|
||||
call("spu_exec_mfc_cmd", &exec_mfc_cmd, m_thread);
|
||||
call("spu_exec_mfc_cmd", &exec_mfc_cmd<false>, m_thread);
|
||||
m_ir->CreateBr(next);
|
||||
m_ir->SetInsertPoint(copy);
|
||||
|
||||
@@ -5874,55 +5987,34 @@ public:
|
||||
// This is odd since SPU code could just use the FM instruction, but it seems common enough
|
||||
if (auto [ok, data] = get_const_vector(c.value, m_pos); ok)
|
||||
{
|
||||
if (is_spu_float_zero(data, -1))
|
||||
if (is_spu_float_zero(data, 0))
|
||||
{
|
||||
return eval(a * b);
|
||||
}
|
||||
|
||||
if (!m_use_fma && is_spu_float_zero(data, +1))
|
||||
{
|
||||
return eval(a * b + fsplat<f32[4]>(0.f));
|
||||
}
|
||||
}
|
||||
|
||||
if ([&]()
|
||||
{
|
||||
if (auto [ok, data] = get_const_vector(a.value, m_pos); ok)
|
||||
{
|
||||
if (!is_spu_float_zero(data, +1))
|
||||
if (is_spu_float_zero(data, 0))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (auto [ok0, data0] = get_const_vector(b.value, m_pos); ok0)
|
||||
{
|
||||
if (is_spu_float_zero(data0, +1))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (auto [ok, data] = get_const_vector(a.value, m_pos); ok)
|
||||
if (auto [ok, data] = get_const_vector(b.value, m_pos); ok)
|
||||
{
|
||||
if (!is_spu_float_zero(data, -1))
|
||||
if (is_spu_float_zero(data, 0))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (auto [ok0, data0] = get_const_vector(b.value, m_pos); ok0)
|
||||
{
|
||||
if (is_spu_float_zero(data0, -1))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}())
|
||||
{
|
||||
// Just return the added value if both a and b is +0 or -0 (+0 and -0 arent't allowed alone)
|
||||
// Just return the added value if either a or b are +-0
|
||||
return c;
|
||||
}
|
||||
|
||||
@@ -6295,7 +6387,7 @@ public:
|
||||
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 bitcast<f32[4]>(base_result - adjustment);
|
||||
return clamp_smax(eval(bitcast<f32[4]>(base_result - adjustment)));
|
||||
});
|
||||
|
||||
const auto [a, b] = get_vrs<f32[4]>(op.ra, op.rb);
|
||||
@@ -6329,7 +6421,7 @@ public:
|
||||
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 bitcast<f32[4]>(base_result - adjustment);
|
||||
return clamp_smax(eval(bitcast<f32[4]>(base_result - adjustment)));
|
||||
});
|
||||
|
||||
register_intrinsic("spu_rsqrte", [&](llvm::CallInst* ci)
|
||||
@@ -6356,7 +6448,7 @@ public:
|
||||
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 bitcast<f32[4]>(base_result - adjustment);
|
||||
return clamp_smax(eval(bitcast<f32[4]>(base_result - adjustment)));
|
||||
});
|
||||
break;
|
||||
}
|
||||
@@ -7547,10 +7639,14 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
const auto compiled_pos = m_ir->getInt32(m_pos);
|
||||
const u32 target = spu_branch_target(0, op.i16);
|
||||
|
||||
m_block->block_end = m_ir->GetInsertBlock();
|
||||
m_ir->CreateBr(add_block(target, true));
|
||||
const auto real_pos = get_pc(m_pos);
|
||||
value_t<u32> target_val;
|
||||
target_val.value = m_ir->getInt32(target);
|
||||
m_ir->CreateCondBr(m_ir->CreateICmpEQ(real_pos, compiled_pos), add_block(target, true), add_block_indirect({}, target_val));
|
||||
}
|
||||
|
||||
void BRASL(spu_opcode_t op) //
|
||||
|
||||
@@ -202,9 +202,9 @@ protected:
|
||||
// GPR modified by the instruction (-1 = not set)
|
||||
std::array<u8, 0x10000> m_regmod;
|
||||
|
||||
std::array<u8, 0x10000> m_use_ra;
|
||||
std::array<u8, 0x10000> m_use_rb;
|
||||
std::array<u8, 0x10000> m_use_rc;
|
||||
std::bitset<0x10000> m_use_ra;
|
||||
std::bitset<0x10000> m_use_rb;
|
||||
std::bitset<0x10000> m_use_rc;
|
||||
|
||||
// List of possible targets for the instruction (entry shouldn't exist for simple instructions)
|
||||
std::unordered_map<u32, std::basic_string<u32>, value_hash<u32, 2>> m_targets;
|
||||
|
||||
+114
-11
@@ -448,11 +448,6 @@ namespace vm
|
||||
{
|
||||
if (range_lock)
|
||||
{
|
||||
if (!*range_lock)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
g_range_lock_bits[1] &= ~(1ull << (range_lock - g_range_lock_set));
|
||||
range_lock->release(0);
|
||||
return;
|
||||
@@ -1844,7 +1839,10 @@ void spu_thread::cpu_work()
|
||||
|
||||
if (has_active_local_bps)
|
||||
{
|
||||
if (local_breakpoints[pc / 4])
|
||||
const u32 pos_at = pc / 4;
|
||||
const u32 pos_bit = 1u << (pos_at % 8);
|
||||
|
||||
if (local_breakpoints[pos_at / 8] & pos_bit)
|
||||
{
|
||||
// Ignore repeatations until a different instruction is issued
|
||||
if (pc != current_bp_pc)
|
||||
@@ -4119,9 +4117,11 @@ bool spu_thread::check_mfc_interrupts(u32 next_pc)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool spu_thread::is_exec_code(u32 addr, std::span<const u8> ls_ptr, u32 base_addr)
|
||||
bool spu_thread::is_exec_code(u32 addr, std::span<const u8> ls_ptr, u32 base_addr, bool avoid_dead_code)
|
||||
{
|
||||
for (u32 i = 0; i < 30; i++)
|
||||
bool had_conditional = false;
|
||||
|
||||
for (u32 i = 0; i < 40; i++)
|
||||
{
|
||||
if (addr & ~0x3FFFC)
|
||||
{
|
||||
@@ -4142,9 +4142,32 @@ bool spu_thread::is_exec_code(u32 addr, std::span<const u8> ls_ptr, u32 base_add
|
||||
return false;
|
||||
}
|
||||
|
||||
if (type == spu_itype::STOP && op.rb)
|
||||
if (type == spu_itype::STOP)
|
||||
{
|
||||
return false;
|
||||
if (op.rb)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (avoid_dead_code)
|
||||
{
|
||||
switch (op.opcode)
|
||||
{
|
||||
case SYS_SPU_THREAD_STOP_YIELD:
|
||||
case SYS_SPU_THREAD_STOP_GROUP_EXIT:
|
||||
case SYS_SPU_THREAD_STOP_THREAD_EXIT:
|
||||
case SYS_SPU_THREAD_STOP_RECEIVE_EVENT:
|
||||
case SYS_SPU_THREAD_STOP_TRY_RECEIVE_EVENT:
|
||||
case SYS_SPU_THREAD_STOP_SWITCH_SYSTEM_MODULE:
|
||||
{
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (type & spu_itype::branch)
|
||||
@@ -4179,6 +4202,58 @@ bool spu_thread::is_exec_code(u32 addr, std::span<const u8> ls_ptr, u32 base_add
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case spu_itype::BR:
|
||||
case spu_itype::BRNZ:
|
||||
case spu_itype::BRZ:
|
||||
case spu_itype::BRHNZ:
|
||||
case spu_itype::BRHZ:
|
||||
case spu_itype::BRSL:
|
||||
{
|
||||
const s32 rel = bf_t<s32, 0, 18>::extract(static_cast<s32>(u32{op.i16} << 2));
|
||||
|
||||
if (rel == 0 && !had_conditional && avoid_dead_code)
|
||||
{
|
||||
// Infinite loop 100%, detect that as invalid code
|
||||
return false;
|
||||
}
|
||||
|
||||
// Detect "invalid" relative branches
|
||||
// Branch offsets that, although are the only way to get X code address using relative address
|
||||
// Rely on overflow/underflow of SPU memory bounds
|
||||
// Thus they would behave differently if SPU LS memory size was to increase (evolving the CELL architecture was the original plan)
|
||||
// Making them highly unlikely to be valid code
|
||||
|
||||
if (rel < 0)
|
||||
{
|
||||
if (addr < 0u - rel)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (SPU_LS_SIZE - addr <= rel + 0u)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (type == spu_itype::BRSL)
|
||||
{
|
||||
// Insert a virtual return-to-next, because it is usually a call
|
||||
results[1] = addr + 4;
|
||||
std::swap(results[1], results[0]);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (usz res_i = 1; res_i < results.size(); res_i++)
|
||||
{
|
||||
@@ -4203,6 +4278,8 @@ bool spu_thread::is_exec_code(u32 addr, std::span<const u8> ls_ptr, u32 base_add
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
had_conditional = true;
|
||||
}
|
||||
|
||||
addr = spu_branch_target(results[0]);
|
||||
@@ -5174,10 +5251,12 @@ s64 spu_thread::get_ch_value(u32 ch)
|
||||
}
|
||||
}
|
||||
|
||||
const usz seed = (utils::get_tsc() >> 8) % 100;
|
||||
|
||||
#ifdef __linux__
|
||||
const bool reservation_busy_waiting = false;
|
||||
#else
|
||||
const bool reservation_busy_waiting = ((utils::get_tsc() >> 8) % 100 + ((raddr == spurs_addr) ? 50 : 0)) < g_cfg.core.spu_reservation_busy_waiting_percentage;
|
||||
const bool reservation_busy_waiting = (seed + ((raddr == spurs_addr) ? 50u : 0u)) < g_cfg.core.spu_reservation_busy_waiting_percentage;
|
||||
#endif
|
||||
|
||||
for (; !events.count; events = get_events(mask1 & ~SPU_EVENT_LR, true, true))
|
||||
@@ -5202,6 +5281,23 @@ s64 spu_thread::get_ch_value(u32 ch)
|
||||
// Don't busy-wait with TSX - memory is sensitive
|
||||
if (g_use_rtm || !reservation_busy_waiting)
|
||||
{
|
||||
if (u32 work_count = g_spu_work_count)
|
||||
{
|
||||
const u32 true_free = utils::sub_saturate<u32>(utils::get_thread_count(), 10);
|
||||
|
||||
if (work_count > true_free)
|
||||
{
|
||||
// SPU thread count estimation
|
||||
const u32 thread_count = (group ? g_raw_spu_ctr + group->max_num : g_raw_spu_ctr + 3);
|
||||
|
||||
if (thread_count && seed % thread_count < work_count - true_free)
|
||||
{
|
||||
// Make the SPU wait longer for other threads to do the work
|
||||
thread_ctrl::wait_for(200);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef __linux__
|
||||
vm::reservation_notifier(raddr).wait(rtime, atomic_wait_timeout{50'000});
|
||||
#else
|
||||
@@ -6056,6 +6152,12 @@ bool spu_thread::stop_and_signal(u32 code)
|
||||
return true;
|
||||
}
|
||||
|
||||
case SYS_SPU_THREAD_STOP_SWITCH_SYSTEM_MODULE:
|
||||
{
|
||||
fmt::throw_exception("SYS_SPU_THREAD_STOP_SWITCH_SYSTEM_MODULE (op=0x%x, Out_MBox=%s)", code, _ref<u32>(pc), ch_out_mbox);
|
||||
return true;
|
||||
}
|
||||
|
||||
case SYS_SPU_THREAD_STOP_YIELD:
|
||||
{
|
||||
// SPU thread group yield (TODO)
|
||||
@@ -6710,3 +6812,4 @@ void fmt_class_string<spu_channel_4_t>::format(std::string& out, u64 arg)
|
||||
|
||||
DECLARE(spu_thread::g_raw_spu_ctr){};
|
||||
DECLARE(spu_thread::g_raw_spu_id){};
|
||||
DECLARE(spu_thread::g_spu_work_count){};
|
||||
|
||||
@@ -816,7 +816,7 @@ public:
|
||||
atomic_t<spu_debugger_mode> debugger_mode{};
|
||||
|
||||
// PC-based breakpoint list
|
||||
std::array<atomic_t<bool>, SPU_LS_SIZE / 4> local_breakpoints{};
|
||||
std::array<atomic_t<u8>, SPU_LS_SIZE / 4 / 8> local_breakpoints{};
|
||||
atomic_t<bool> has_active_local_bps = false;
|
||||
u32 current_bp_pc = umax;
|
||||
bool stop_flag_removal_protection = false;
|
||||
@@ -835,7 +835,7 @@ public:
|
||||
void set_events(u32 bits);
|
||||
void set_interrupt_status(bool enable);
|
||||
bool check_mfc_interrupts(u32 next_pc);
|
||||
static bool is_exec_code(u32 addr, std::span<const u8> ls_ptr, u32 base_addr = 0); // Only a hint, do not rely on it other than debugging purposes
|
||||
static bool is_exec_code(u32 addr, std::span<const u8> ls_ptr, u32 base_addr = 0, bool avoid_dead_code = false); // Only a hint, do not rely on it other than debugging purposes
|
||||
static std::vector<u32> discover_functions(u32 base_addr, std::span<const u8> ls, bool is_known_addr, u32 /*entry*/);
|
||||
u32 get_ch_count(u32 ch);
|
||||
s64 get_ch_value(u32 ch);
|
||||
@@ -889,6 +889,7 @@ public:
|
||||
|
||||
static atomic_t<u32> g_raw_spu_ctr;
|
||||
static atomic_t<u32> g_raw_spu_id[5];
|
||||
static atomic_t<u32> g_spu_work_count;
|
||||
|
||||
static u32 find_raw_spu(u32 id)
|
||||
{
|
||||
|
||||
+90
-20
@@ -55,6 +55,7 @@
|
||||
#include <deque>
|
||||
#include "util/tsc.hpp"
|
||||
#include "util/sysinfo.hpp"
|
||||
#include "util/init_mutex.hpp"
|
||||
|
||||
#if defined(ARCH_X64)
|
||||
#ifdef _MSC_VER
|
||||
@@ -267,13 +268,13 @@ const std::array<std::pair<ppu_intrp_func_t, std::string_view>, 1024> g_ppu_sysc
|
||||
BIND_SYSC(sys_event_port_connect_ipc), //140 (0x08C)
|
||||
BIND_SYSC(sys_timer_usleep), //141 (0x08D)
|
||||
BIND_SYSC(sys_timer_sleep), //142 (0x08E)
|
||||
NULL_FUNC(sys_time_set_timezone), //143 (0x08F) ROOT
|
||||
BIND_SYSC(sys_time_set_timezone), //143 (0x08F) ROOT
|
||||
BIND_SYSC(sys_time_get_timezone), //144 (0x090)
|
||||
BIND_SYSC(sys_time_get_current_time), //145 (0x091)
|
||||
NULL_FUNC(sys_time_get_system_time), //146 (0x092) ROOT
|
||||
BIND_SYSC(sys_time_set_current_time), //146 (0x092) ROOT
|
||||
BIND_SYSC(sys_time_get_timebase_frequency), //147 (0x093)
|
||||
BIND_SYSC(_sys_rwlock_trywlock), //148 (0x094)
|
||||
uns_func, //149 (0x095) UNS
|
||||
NULL_FUNC(sys_time_get_system_time), //149 (0x095)
|
||||
BIND_SYSC(sys_raw_spu_create_interrupt_tag), //150 (0x096)
|
||||
BIND_SYSC(sys_raw_spu_set_int_mask), //151 (0x097)
|
||||
BIND_SYSC(sys_raw_spu_get_int_mask), //152 (0x098)
|
||||
@@ -1108,6 +1109,61 @@ void fmt_class_string<CellError>::format(std::string& out, u64 arg)
|
||||
});
|
||||
}
|
||||
|
||||
stx::init_lock acquire_lock(stx::init_mutex& mtx, ppu_thread* ppu)
|
||||
{
|
||||
if (!ppu)
|
||||
{
|
||||
ppu = ensure(cpu_thread::get_current<ppu_thread>());
|
||||
}
|
||||
|
||||
return mtx.init([](int invoke_count, const stx::init_lock&, ppu_thread* ppu)
|
||||
{
|
||||
if (!invoke_count)
|
||||
{
|
||||
// Sleep before waiting on lock
|
||||
lv2_obj::sleep(*ppu);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Wake up after acquistion or failure to acquire
|
||||
ppu->check_state();
|
||||
}
|
||||
}, ppu);
|
||||
}
|
||||
|
||||
stx::access_lock acquire_access_lock(stx::init_mutex& mtx, ppu_thread* ppu)
|
||||
{
|
||||
if (!ppu)
|
||||
{
|
||||
ppu = ensure(cpu_thread::get_current<ppu_thread>());
|
||||
}
|
||||
|
||||
// TODO: Check if needs to wait
|
||||
return mtx.access();
|
||||
}
|
||||
|
||||
stx::reset_lock acquire_reset_lock(stx::init_mutex& mtx, ppu_thread* ppu)
|
||||
{
|
||||
if (!ppu)
|
||||
{
|
||||
ppu = ensure(cpu_thread::get_current<ppu_thread>());
|
||||
}
|
||||
|
||||
return mtx.reset([](int invoke_count, const stx::init_lock&, ppu_thread* ppu)
|
||||
{
|
||||
if (!invoke_count)
|
||||
{
|
||||
// Sleep before waiting on lock
|
||||
lv2_obj::sleep(*ppu);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Wake up after acquistion or failure to acquire
|
||||
ppu->check_state();
|
||||
}
|
||||
}, ppu);
|
||||
}
|
||||
|
||||
class ppu_syscall_usage
|
||||
{
|
||||
// Internal buffer
|
||||
@@ -1390,16 +1446,28 @@ bool lv2_obj::sleep_unlocked(cpu_thread& thread, u64 timeout, u64 current_time)
|
||||
|
||||
auto on_to_sleep_update = [&]()
|
||||
{
|
||||
std::string out = fmt::format("Threads (%d):", g_to_sleep.size());
|
||||
for (auto thread : g_to_sleep)
|
||||
if (g_to_sleep.size() > 5u)
|
||||
{
|
||||
fmt::append(out, " 0x%x,", thread->id);
|
||||
ppu_log.warning("Threads (%d)", g_to_sleep.size());
|
||||
}
|
||||
|
||||
ppu_log.warning("%s", out);
|
||||
|
||||
if (g_to_sleep.empty())
|
||||
else if (!g_to_sleep.empty())
|
||||
{
|
||||
// In case there is a deadlock (PPU threads not sleeping)
|
||||
// Print-out their IDs for further inspection (focus at 5 at max for now to avoid log spam)
|
||||
std::string out = fmt::format("Threads (%d):", g_to_sleep.size());
|
||||
for (auto thread : g_to_sleep)
|
||||
{
|
||||
fmt::append(out, " 0x%x,", thread->id);
|
||||
}
|
||||
|
||||
out.resize(out.size() - 1);
|
||||
|
||||
ppu_log.warning("%s", out);
|
||||
}
|
||||
else
|
||||
{
|
||||
ppu_log.warning("Final Thread");
|
||||
|
||||
// All threads are ready, wake threads
|
||||
Emu.CallFromMainThread([]
|
||||
{
|
||||
@@ -1725,12 +1793,14 @@ bool lv2_obj::awake_unlocked(cpu_thread* cpu, s32 prio)
|
||||
{
|
||||
if (current_ppu->prio.load().prio > lowest_new_priority)
|
||||
{
|
||||
const bool is_create_thread = current_ppu->gpr[11] == 0x35;
|
||||
|
||||
// When not being set to All timers - activate only for sys_ppu_thread_start
|
||||
if (current_ppu->gpr[11] == 0x35 || g_cfg.core.sleep_timers_accuracy == sleep_timers_accuracy_level::_all_timers)
|
||||
if (is_create_thread || g_cfg.core.sleep_timers_accuracy == sleep_timers_accuracy_level::_all_timers)
|
||||
{
|
||||
if (!current_ppu->state.test_and_set(cpu_flag::yield) || current_ppu->hw_sleep_time != 0)
|
||||
{
|
||||
current_ppu->hw_sleep_time += 35; // Seems like 35us after extensive testing
|
||||
current_ppu->hw_sleep_time += (is_create_thread ? 51 : 35);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1888,7 +1958,7 @@ void lv2_obj::make_scheduler_ready()
|
||||
lv2_obj::awake_all();
|
||||
}
|
||||
|
||||
ppu_thread_status lv2_obj::ppu_state(ppu_thread* ppu, bool lock_idm, bool lock_lv2)
|
||||
std::pair<ppu_thread_status, u32> lv2_obj::ppu_state(ppu_thread* ppu, bool lock_idm, bool lock_lv2)
|
||||
{
|
||||
std::optional<reader_lock> opt_lock[2];
|
||||
|
||||
@@ -1899,13 +1969,13 @@ ppu_thread_status lv2_obj::ppu_state(ppu_thread* ppu, bool lock_idm, bool lock_l
|
||||
|
||||
if (!Emu.IsReady() ? ppu->state.all_of(cpu_flag::stop) : ppu->stop_flag_removal_protection)
|
||||
{
|
||||
return PPU_THREAD_STATUS_IDLE;
|
||||
return { PPU_THREAD_STATUS_IDLE, 0};
|
||||
}
|
||||
|
||||
switch (ppu->joiner)
|
||||
{
|
||||
case ppu_join_status::zombie: return PPU_THREAD_STATUS_ZOMBIE;
|
||||
case ppu_join_status::exited: return PPU_THREAD_STATUS_DELETED;
|
||||
case ppu_join_status::zombie: return { PPU_THREAD_STATUS_ZOMBIE, 0};
|
||||
case ppu_join_status::exited: return { PPU_THREAD_STATUS_DELETED, 0};
|
||||
default: break;
|
||||
}
|
||||
|
||||
@@ -1930,18 +2000,18 @@ ppu_thread_status lv2_obj::ppu_state(ppu_thread* ppu, bool lock_idm, bool lock_l
|
||||
{
|
||||
if (!ppu->interrupt_thread_executing)
|
||||
{
|
||||
return PPU_THREAD_STATUS_STOP;
|
||||
return { PPU_THREAD_STATUS_STOP, 0};
|
||||
}
|
||||
|
||||
return PPU_THREAD_STATUS_SLEEP;
|
||||
return { PPU_THREAD_STATUS_SLEEP, 0 };
|
||||
}
|
||||
|
||||
if (pos >= g_cfg.core.ppu_threads + 0u)
|
||||
{
|
||||
return PPU_THREAD_STATUS_RUNNABLE;
|
||||
return { PPU_THREAD_STATUS_RUNNABLE, pos };
|
||||
}
|
||||
|
||||
return PPU_THREAD_STATUS_ONPROC;
|
||||
return { PPU_THREAD_STATUS_ONPROC, pos};
|
||||
}
|
||||
|
||||
void lv2_obj::set_future_sleep(cpu_thread* cpu)
|
||||
|
||||
@@ -74,7 +74,7 @@ error_code sys_cond_create(ppu_thread& ppu, vm::ptr<u32> cond_id, u32 mutex_id,
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
sys_cond.warning("sys_cond_create(cond_id=*0x%x, mutex_id=0x%x, attr=*0x%x)", cond_id, mutex_id, attr);
|
||||
sys_cond.trace("sys_cond_create(cond_id=*0x%x, mutex_id=0x%x, attr=*0x%x)", cond_id, mutex_id, attr);
|
||||
|
||||
auto mutex = idm::get<lv2_obj, lv2_mutex>(mutex_id);
|
||||
|
||||
@@ -87,6 +87,11 @@ error_code sys_cond_create(ppu_thread& ppu, vm::ptr<u32> cond_id, u32 mutex_id,
|
||||
|
||||
const u64 ipc_key = lv2_obj::get_key(_attr);
|
||||
|
||||
if (ipc_key)
|
||||
{
|
||||
sys_cond.warning("sys_cond_create(cond_id=*0x%x, attr=*0x%x): IPC=0x%016x", cond_id, attr, ipc_key);
|
||||
}
|
||||
|
||||
if (const auto error = lv2_obj::create<lv2_cond>(_attr.pshared, ipc_key, _attr.flags, [&]
|
||||
{
|
||||
return std::make_shared<lv2_cond>(
|
||||
@@ -108,7 +113,7 @@ error_code sys_cond_destroy(ppu_thread& ppu, u32 cond_id)
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
sys_cond.warning("sys_cond_destroy(cond_id=0x%x)", cond_id);
|
||||
sys_cond.trace("sys_cond_destroy(cond_id=0x%x)", cond_id);
|
||||
|
||||
const auto cond = idm::withdraw<lv2_obj, lv2_cond>(cond_id, [&](lv2_cond& cond) -> CellError
|
||||
{
|
||||
@@ -129,6 +134,11 @@ error_code sys_cond_destroy(ppu_thread& ppu, u32 cond_id)
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
if (cond->key)
|
||||
{
|
||||
sys_cond.warning("sys_cond_destroy(cond_id=0x%x): IPC=0x%016x", cond_id, cond->key);
|
||||
}
|
||||
|
||||
if (cond.ret)
|
||||
{
|
||||
return cond.ret;
|
||||
|
||||
@@ -29,7 +29,7 @@ error_code _sys_lwcond_create(ppu_thread& ppu, vm::ptr<u32> lwcond_id, u32 lwmut
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
sys_lwcond.warning(u8"_sys_lwcond_create(lwcond_id=*0x%x, lwmutex_id=0x%x, control=*0x%x, name=0x%llx (“%s”))", lwcond_id, lwmutex_id, control, name, lv2_obj::name_64{std::bit_cast<be_t<u64>>(name)});
|
||||
sys_lwcond.trace(u8"_sys_lwcond_create(lwcond_id=*0x%x, lwmutex_id=0x%x, control=*0x%x, name=0x%llx (“%s”))", lwcond_id, lwmutex_id, control, name, lv2_obj::name_64{std::bit_cast<be_t<u64>>(name)});
|
||||
|
||||
u32 protocol;
|
||||
|
||||
@@ -62,7 +62,7 @@ error_code _sys_lwcond_destroy(ppu_thread& ppu, u32 lwcond_id)
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
sys_lwcond.warning("_sys_lwcond_destroy(lwcond_id=0x%x)", lwcond_id);
|
||||
sys_lwcond.trace("_sys_lwcond_destroy(lwcond_id=0x%x)", lwcond_id);
|
||||
|
||||
std::shared_ptr<lv2_lwcond> _cond;
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ error_code sys_mutex_create(ppu_thread& ppu, vm::ptr<u32> mutex_id, vm::ptr<sys_
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
sys_mutex.warning("sys_mutex_create(mutex_id=*0x%x, attr=*0x%x)", mutex_id, attr);
|
||||
sys_mutex.trace("sys_mutex_create(mutex_id=*0x%x, attr=*0x%x)", mutex_id, attr);
|
||||
|
||||
if (!mutex_id || !attr)
|
||||
{
|
||||
@@ -49,6 +49,13 @@ error_code sys_mutex_create(ppu_thread& ppu, vm::ptr<u32> mutex_id, vm::ptr<sys_
|
||||
|
||||
const auto _attr = *attr;
|
||||
|
||||
const u64 ipc_key = lv2_obj::get_key(_attr);
|
||||
|
||||
if (ipc_key)
|
||||
{
|
||||
sys_mutex.warning("sys_mutex_create(mutex_id=*0x%x, attr=*0x%x): IPC=0x%016x", mutex_id, attr, ipc_key);
|
||||
}
|
||||
|
||||
switch (_attr.protocol)
|
||||
{
|
||||
case SYS_SYNC_FIFO: break;
|
||||
@@ -85,7 +92,7 @@ error_code sys_mutex_create(ppu_thread& ppu, vm::ptr<u32> mutex_id, vm::ptr<sys_
|
||||
_attr.protocol,
|
||||
_attr.recursive,
|
||||
_attr.adaptive,
|
||||
_attr.ipc_key,
|
||||
ipc_key,
|
||||
_attr.name_u64);
|
||||
}))
|
||||
{
|
||||
@@ -101,7 +108,7 @@ error_code sys_mutex_destroy(ppu_thread& ppu, u32 mutex_id)
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
sys_mutex.warning("sys_mutex_destroy(mutex_id=0x%x)", mutex_id);
|
||||
sys_mutex.trace("sys_mutex_destroy(mutex_id=0x%x)", mutex_id);
|
||||
|
||||
const auto mutex = idm::withdraw<lv2_obj, lv2_mutex>(mutex_id, [](lv2_mutex& mutex) -> CellError
|
||||
{
|
||||
@@ -126,6 +133,11 @@ error_code sys_mutex_destroy(ppu_thread& ppu, u32 mutex_id)
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
if (mutex->key)
|
||||
{
|
||||
sys_mutex.warning("sys_mutex_destroy(mutex_id=0x%x): IPC=0x%016x", mutex_id, mutex->key);
|
||||
}
|
||||
|
||||
if (mutex.ret)
|
||||
{
|
||||
return mutex.ret;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user