Compare commits
271 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2cd7238e76 | |||
| 21f0043b27 | |||
| b7599c0f30 | |||
| d8494ef1ae | |||
| 727b32e886 | |||
| 8124efa505 | |||
| a5cff4d797 | |||
| d1e5828644 | |||
| 18d73d6d76 | |||
| 4a3a24dcc1 | |||
| 591159c4a9 | |||
| 7baf959975 | |||
| 3f02935844 | |||
| f98251a05a | |||
| 80f0741103 | |||
| e28707055b | |||
| 698c3415ea | |||
| daf43989fc | |||
| c7586de280 | |||
| 506b9deec5 | |||
| 1fc0191311 | |||
| 1f9e04f72d | |||
| a71bdc761e | |||
| 9d8f4d5cfa | |||
| ee87fdc869 | |||
| bf63a18c5f | |||
| ddba30988b | |||
| b0e2c959eb | |||
| 28bec8e1bf | |||
| 86702186f2 | |||
| 82b1a2bd7a | |||
| d36cf71a2d | |||
| 1dd1062be1 | |||
| c0e3b86064 | |||
| 6210a8491f | |||
| 33a4f05ffa | |||
| a74d3005f8 | |||
| bca833dad7 | |||
| f981e05908 | |||
| b5abd777b0 | |||
| b2c9add47e | |||
| a401a192b8 | |||
| ad1b007dd1 | |||
| 71e35c8b4d | |||
| 2e504b2dac | |||
| 6e9df9e452 | |||
| 8aa5899196 | |||
| b6608ad31d | |||
| edb39039dd | |||
| bacf518189 | |||
| 1244044647 | |||
| b960ce1426 | |||
| c55a889c23 | |||
| 829af30568 | |||
| a3007e11ca | |||
| 9a981b5292 | |||
| 4464a6c3f6 | |||
| d30ac20fad | |||
| 7bb2d94e53 | |||
| 7ff4509858 | |||
| f2997e4c00 | |||
| 133e9d4705 | |||
| 501ca4bcf5 | |||
| 9212942724 | |||
| 683c1a6512 | |||
| 9b0d33048c | |||
| bc8bf2c329 | |||
| f0002501f7 | |||
| c4cc0154be | |||
| cba4c3cdc4 | |||
| c51d3b5465 | |||
| e179adc4a0 | |||
| 47f3740a70 | |||
| 2880370047 | |||
| 61a055a1c6 | |||
| 64b4cfa59f | |||
| c799ffd223 | |||
| 2ec039365f | |||
| 26e731b487 | |||
| 34bae90820 | |||
| b55a052a22 | |||
| 011aabe9ed | |||
| a17a6527f6 | |||
| 122c6256ca | |||
| 73aaff1b29 | |||
| c7fbc16357 | |||
| bc728db15b | |||
| 2eebbd307d | |||
| dc851a729e | |||
| 6007fd630f | |||
| cb0ecb2afb | |||
| 84a785ea67 | |||
| cc1e4d2194 | |||
| b2d84dcebd | |||
| 958d77ba52 | |||
| c4b38869ab | |||
| fd1968dd22 | |||
| 88b80ebdf5 | |||
| 2445ab8d8e | |||
| 0e1931f178 | |||
| 3e923b4993 | |||
| 8181498d86 | |||
| 57dd611111 | |||
| b3162bd41c | |||
| cd2adbad9a | |||
| 99730ac4f9 | |||
| d2ab3383ad | |||
| 3b15a6b39e | |||
| 651e58f443 | |||
| 769f9e33e9 | |||
| cf77bb7cfc | |||
| c75b76ddf5 | |||
| 4dec8d7925 | |||
| 934e7b614c | |||
| 416b14de72 | |||
| b87099c790 | |||
| 052725fdc7 | |||
| f90b79791f | |||
| 4c32c95b33 | |||
| c00d47cc99 | |||
| ea53c7dc85 | |||
| 2b923c681e | |||
| 7a8e918519 | |||
| d99ae3809d | |||
| 51e6d0a336 | |||
| 228844c017 | |||
| 87e628a9e9 | |||
| e6885e25b5 | |||
| f31ffc4596 | |||
| 4114f00c05 | |||
| 8f7209f886 | |||
| d80146c704 | |||
| 577f379a12 | |||
| 3d73915358 | |||
| a5317d50a5 | |||
| 143bd6cc80 | |||
| 5cc8f970c0 | |||
| c9058280e0 | |||
| 5af50cfd55 | |||
| d846142f0c | |||
| 0ba0f9d2b9 | |||
| f4f9c1865e | |||
| c40439ae6b | |||
| 683fa2a392 | |||
| 246bf1df64 | |||
| 9a868e9239 | |||
| ab3cde1939 | |||
| bec3e156fb | |||
| 086afbbaa5 | |||
| cd4d73114c | |||
| 14ca506774 | |||
| 984bf67d84 | |||
| 2b3ae8b638 | |||
| bb9ae18c71 | |||
| ce01457510 | |||
| d1a46b491e | |||
| 31df99f7d6 | |||
| 5e11b2b056 | |||
| d106934336 | |||
| 68718361a7 | |||
| c887865d54 | |||
| dc48944979 | |||
| d22b8b1d3a | |||
| 56619b20cf | |||
| 12ebf77e22 | |||
| 845af8bd96 | |||
| 680f08c2b8 | |||
| 82bac4173e | |||
| 8a8fda3e02 | |||
| 1c5b685398 | |||
| e95084f138 | |||
| e12d268662 | |||
| 6a3f17cd36 | |||
| db0774a81b | |||
| 34615d3a74 | |||
| 189499c10b | |||
| de613a8a98 | |||
| c0369b2e10 | |||
| 07ebbb6c84 | |||
| 73ed657e00 | |||
| b37217b181 | |||
| b02e6e222f | |||
| c52abed4d3 | |||
| 9e57efe82c | |||
| 77ab872bec | |||
| 27acebc5f5 | |||
| 2755d9941c | |||
| 488982f424 | |||
| 086e12c6ca | |||
| 9b19f16698 | |||
| b13fd68848 | |||
| 3e51426379 | |||
| 105781fa76 | |||
| d2be12bb07 | |||
| fdc15e12c4 | |||
| e548743cbf | |||
| 7499f875a6 | |||
| cdef752a9c | |||
| 1483941bea | |||
| 9cc15be7f2 | |||
| 453e1bfaec | |||
| 82439327fa | |||
| f60002e87d | |||
| 9fc6382909 | |||
| ebad08aa97 | |||
| 599f1dd157 | |||
| bb5ce67d57 | |||
| f948ce399e | |||
| 954c60947d | |||
| 98b6783c05 | |||
| 0894d2886a | |||
| 4995b4abe3 | |||
| 35ef19cfc8 | |||
| 09824a718f | |||
| f4e85af53c | |||
| 74a909cfbc | |||
| 0bfdfd8433 | |||
| 1f5cf776b2 | |||
| cdd6840826 | |||
| ab27ee4cf4 | |||
| 6211295155 | |||
| befd7ceb89 | |||
| 2cead6f328 | |||
| 4e3d58f75a | |||
| 24fddf1ded | |||
| 87cd65ff03 | |||
| 219e3d61f8 | |||
| 4ade06f36f | |||
| 786510a937 | |||
| 0c6df39a45 | |||
| 648ad0cd90 | |||
| cd0d43382f | |||
| 98b730c806 | |||
| 4b787b22c8 | |||
| 5c6f5a1610 | |||
| 0b8d5f7b98 | |||
| 314d481dcd | |||
| 0980c68e6f | |||
| 983bfa2088 | |||
| 3a9928de65 | |||
| 1fd5fb2f7e | |||
| 4ac88fa8d3 | |||
| 5f8f9e33f1 | |||
| f0c71ae2ae | |||
| 0c7ecdc954 | |||
| 73a687df7d | |||
| b683110e72 | |||
| 3840015e7d | |||
| 639b8112b6 | |||
| 2bf17f5d3c | |||
| 4823d4c32a | |||
| c8c035eeef | |||
| 155bd09fd0 | |||
| f546e5a8ef | |||
| 2ccb0c8f42 | |||
| 05303ce6c6 | |||
| d8f3029952 | |||
| 09c28f75b3 | |||
| 57671e267d | |||
| b692108f1e | |||
| 2815aecd0c | |||
| b9f2b1e7c6 | |||
| bd9ba7ef1f | |||
| a015bd1ebd | |||
| d6cf167d52 | |||
| 3fc2194fd5 | |||
| 72c14994f0 | |||
| fddb6a31a7 | |||
| 278ae7763a | |||
| 5cafaef0a9 | |||
| fcd297ffb2 |
+11
-11
@@ -1,9 +1,9 @@
|
||||
#!/bin/sh -ex
|
||||
|
||||
# Setup Qt variables
|
||||
export QT_BASE_DIR=/opt/qt${QTVERMIN}
|
||||
export PATH=$QT_BASE_DIR/bin:$PATH
|
||||
export LD_LIBRARY_PATH=$QT_BASE_DIR/lib/x86_64-linux-gnu:$QT_BASE_DIR/lib
|
||||
export QT_BASE_DIR=/opt/qt"${QTVERMIN}"
|
||||
export PATH="$QT_BASE_DIR"/bin:"$PATH"
|
||||
export LD_LIBRARY_PATH="$QT_BASE_DIR"/lib/x86_64-linux-gnu:"$QT_BASE_DIR"/lib
|
||||
|
||||
if [ -z "$CIRRUS_CI" ]; then
|
||||
cd rpcs3 || exit 1
|
||||
@@ -23,19 +23,19 @@ mkdir build && cd build || exit 1
|
||||
|
||||
if [ "$COMPILER" = "gcc" ]; then
|
||||
# These are set in the dockerfile
|
||||
export CC=${GCC_BINARY}
|
||||
export CXX=${GXX_BINARY}
|
||||
export CC="${GCC_BINARY}"
|
||||
export CXX="${GXX_BINARY}"
|
||||
export LINKER=gold
|
||||
# We need to set the following variables for LTO to link properly
|
||||
export AR=/usr/bin/gcc-ar-$GCCVER
|
||||
export RANLIB=/usr/bin/gcc-ranlib-$GCCVER
|
||||
export AR=/usr/bin/gcc-ar-"$GCCVER"
|
||||
export RANLIB=/usr/bin/gcc-ranlib-"$GCCVER"
|
||||
export CFLAGS="-fuse-linker-plugin"
|
||||
else
|
||||
export CC=${CLANG_BINARY}
|
||||
export CXX=${CLANGXX_BINARY}
|
||||
export CC="${CLANG_BINARY}"
|
||||
export CXX="${CLANGXX_BINARY}"
|
||||
export LINKER=lld
|
||||
export AR=/usr/bin/llvm-ar-$LLVMVER
|
||||
export RANLIB=/usr/bin/llvm-ranlib-$LLVMVER
|
||||
export AR=/usr/bin/llvm-ar-"$LLVMVER"
|
||||
export RANLIB=/usr/bin/llvm-ranlib-"$LLVMVER"
|
||||
fi
|
||||
|
||||
export CFLAGS="$CFLAGS -fuse-ld=${LINKER}"
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ if [ "$DEPLOY_APPIMAGE" = "true" ]; then
|
||||
ls ./appdir/usr/lib/
|
||||
rm -r ./appdir/usr/share/doc
|
||||
cp "$(readlink -f /lib/x86_64-linux-gnu/libnsl.so.1)" ./appdir/usr/lib/libnsl.so.1
|
||||
export PATH=/rpcs3/build/squashfs-root/usr/bin/:${PATH}
|
||||
export PATH=/rpcs3/build/squashfs-root/usr/bin/:"${PATH}"
|
||||
|
||||
# Embed newer libstdc++ for distros that don't come with it (ubuntu 16.04)
|
||||
mkdir -p appdir/usr/optional/ ; mkdir -p appdir/usr/optional/libstdc++/
|
||||
|
||||
+2
-2
@@ -61,7 +61,7 @@ windows_task:
|
||||
|
||||
linux_task:
|
||||
container:
|
||||
image: rpcs3/rpcs3-ci-bionic:1.3
|
||||
image: rpcs3/rpcs3-ci-bionic:1.5
|
||||
cpu: 4
|
||||
memory: 16G
|
||||
env:
|
||||
@@ -108,7 +108,7 @@ freebsd_task:
|
||||
matrix:
|
||||
- name: Cirrus FreeBSD
|
||||
freebsd_instance:
|
||||
image_family: freebsd-13-0
|
||||
image_family: freebsd-13-1
|
||||
cpu: 8
|
||||
memory: 8G
|
||||
env:
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
---
|
||||
name: Regression report
|
||||
about: If something used to work before, but now it doesn't
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## Please do not ask for help or report compatibility regressions here, use [RPCS3 Discord server](https://discord.me/RPCS3) or [forums](https://forums.rpcs3.net/) instead.
|
||||
|
||||
## Quick summary
|
||||
Please briefly describe what has stopped working correctly.
|
||||
|
||||
## Details
|
||||
Please describe the regression as accurately as possible.
|
||||
|
||||
#### 0. Make sure you're running with settings as close to default as possible
|
||||
* **Do NOT enable any emulator game patches when reporting issues**
|
||||
* Only change settings that are required for the game to work
|
||||
|
||||
#### 1. Please provide _exact_ build (or commit) information that introduced the regression you're reporting.
|
||||
* Please see [How to find the build that caused a regression](https://wiki.rpcs3.net/index.php?title=Help:Using_different_versions_of_RPCS3#How_to_find_the_build_that_caused_a_regression.3F) in our wiki.
|
||||
* You can find [History of RPCS3 builds](https://rpcs3.net/compatibility?b) here.
|
||||
|
||||
#### 2. Please attach TWO logs:
|
||||
* One for the build with regression.
|
||||
* One for the build that works as expected.
|
||||
|
||||
##### How to obtain RPCS3's log:
|
||||
* Run the game until you find the regression.
|
||||
* Completely close RPCS3, or move to the next step in case it has crashed.
|
||||
* Locate RPCS3's log file:
|
||||
+ ```RPCS3.log``` (It can show up as just RPCS3 and have a notepad icon)
|
||||
or
|
||||
+ ```RPCS3.log.gz``` (It can show up as RPCS3.log and have a zip or rar icon)
|
||||

|
||||
On Linux it will be in ```~/.cache/rpcs3/```.
|
||||
On Windows it will be near the executable.
|
||||
* Attach the log:
|
||||
+ Drag and drop your log file into the issue.
|
||||
+ Or upload it to the cloud, such as Dropbox, Mega etc.
|
||||
|
||||
#### 3. If you describe graphical regression, please provide an RSX capture and a RenderDoc capture that demonstrate it.
|
||||
* To create an RSX capture, use _Create_ _RSX_ _Capture_ under _Utilities_.
|
||||
Captures will be stored in RPCS3 folder → captures.
|
||||
+ Compress your capture with 7z, Rar etc.
|
||||
And drag and drop it into the issue.
|
||||
+ Or upload it to the cloud, such as Dropbox, Mega etc.
|
||||
* To create a RenderDoc capture, please refer to [RenderDoc's documentation](https://renderdoc.org/docs/how/how_capture_frame.html).
|
||||
|
||||
#### 4. Please attach screenshots of your problem.
|
||||
* Enable performance overlay with at least medium level of detail.
|
||||
You can find it in _Emulator_ tab in _Settings_.
|
||||
|
||||
#### 5. Please provide comparison with real PS3.
|
||||
|
||||
#### 6. Please provide your system configuration:
|
||||
* OS
|
||||
* CPU
|
||||
* GPU
|
||||
* Driver version
|
||||
* etc.
|
||||
|
||||
#### Please include.
|
||||
* Anything else you deem to be important
|
||||
@@ -0,0 +1,93 @@
|
||||
name: Regression report
|
||||
description: If something used to work before, but now it doesn't
|
||||
title: "[Regression] Enter a title here"
|
||||
labels: []
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
# Summary
|
||||
Please do not ask for help or report compatibility regressions here, use [RPCS3 Discord server](https://discord.me/RPCS3) or [forums](https://forums.rpcs3.net/) instead.
|
||||
- type: textarea
|
||||
id: quick-summary
|
||||
attributes:
|
||||
label: Quick summary
|
||||
description: Please briefly describe what has stopped working correctly.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: details
|
||||
attributes:
|
||||
label: Details
|
||||
description: Please describe the regression as accurately as possible. Include screenshots if neccessary.
|
||||
validations:
|
||||
required: false
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
# Identify the regressed build
|
||||
Please provide the _exact_ build (or commit) information that introduced the regression you're reporting.
|
||||
* Please see [How to find the build that caused a regression](https://wiki.rpcs3.net/index.php?title=Help:Using_different_versions_of_RPCS3#How_to_find_the_build_that_caused_a_regression.3F) in our wiki.
|
||||
* You can find [History of RPCS3 builds](https://rpcs3.net/compatibility?b) here.
|
||||
|
||||
Make sure you're running with settings as close to default as possible
|
||||
* **Do NOT enable any emulator game patches when reporting issues**
|
||||
* Only change settings that are required for the game to work
|
||||
- type: input
|
||||
id: regressed-build
|
||||
attributes:
|
||||
label: Build with regression
|
||||
description: Provide _exact_ build (or commit) information that introduced the regression you're reporting.
|
||||
placeholder: v0.0.23-13948-31df99f7
|
||||
validations:
|
||||
required: true
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
# Log files
|
||||
Obtaining the log file:
|
||||
* Run the game until you find the regression.
|
||||
* Completely close RPCS3 and locate the log file.
|
||||
|
||||
RPCS3's Log file will be ```RPCS3.log.gz``` (sometimes shows as RPCS3.log with zip icon) or ```RPCS3.log``` (sometimes shows as RPCS3 wtih notepad icon).
|
||||
* On Windows it will be in the RPCS3 directory near the executable
|
||||
* On Linux it will be in ```~/.cache/rpcs3/```
|
||||
* On MacOS it will be in ```~/Library/Caches/rpcs3```. If you're unable to locate it copy paste the path in Spotlight and hit enter.
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: Attach two log files
|
||||
description: |
|
||||
Attach one file for the build with regression and another for a build that works.
|
||||
Drag & drop the files into this input field, or upload them to another service (f.ex. Dropbox, Mega) and provide a link.
|
||||
validations:
|
||||
required: true
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
# Other details
|
||||
If you describe a graphical regression, please provide an RSX capture and/or a RenderDoc capture that demonstrate it.
|
||||
* To create an RSX capture, use _Create_ _RSX_ _Capture_ under _Utilities_.
|
||||
* Captures will be stored in RPCS3 folder → captures.
|
||||
* To create a RenderDoc capture, please refer to [RenderDoc's documentation](https://renderdoc.org/docs/how/how_capture_frame.html).
|
||||
- type: textarea
|
||||
id: captures
|
||||
attributes:
|
||||
label: Attach capture files for visual issues
|
||||
description: Compress your capture with 7z, Rar etc. and attach it here, or upload it to the cloud (Dropbox, Mega etc) and add a link to it.
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: system-info
|
||||
attributes:
|
||||
label: System configuration
|
||||
description: Provide information about your system, such as operating system, CPU and GPU model, GPU driver version and other information that describes your system configuration.
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: other-details
|
||||
attributes:
|
||||
label: Other details
|
||||
description: Include anything else you deem to be important.
|
||||
validations:
|
||||
required: false
|
||||
@@ -1,59 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: If something doesn't work correctly in RPCS3
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## Please do not ask for help or report compatibility regressions here, use [RPCS3 Discord server](https://discord.me/RPCS3) or [forums](https://forums.rpcs3.net/) instead.
|
||||
|
||||
## Quick summary
|
||||
Please briefly describe what is not working correctly.
|
||||
|
||||
## Details
|
||||
|
||||
Please describe the problem as accurately as possible.
|
||||
|
||||
#### 0. Make sure you're running with settings as close to default as possible
|
||||
* **Do NOT enable any emulator game patches when reporting issues**
|
||||
* Only change settings that are required for the game to work
|
||||
|
||||
#### 1. Please attach RPCS3's log.
|
||||
* Run the game until you find the issue.
|
||||
* Completely close RPCS3, or move to the next step in case it has crashed.
|
||||
* Locate RPCS3's log file:
|
||||
+ ```RPCS3.log``` (It can show up as just RPCS3 and have a notepad icon)
|
||||
or
|
||||
+ ```RPCS3.log.gz``` (It can show up as RPCS3.log and have a zip or rar icon)
|
||||

|
||||
On Linux it will be in ```~/.cache/rpcs3/```.
|
||||
On Windows it will be near the executable.
|
||||
* Attach the log:
|
||||
+ Drag and drop your log file into the issue.
|
||||
+ Or upload it to the cloud, such as Dropbox, Mega etc.
|
||||
|
||||
#### 2. If you describe graphical issue, please provide an RSX capture and a RenderDoc capture that demonstrate it.
|
||||
* To create an RSX capture, use _Create_ _RSX_ _Capture_ under _Utilities_.
|
||||
Captures will be stored in RPCS3 folder → captures.
|
||||
+ Compress your capture with 7z, Rar etc.
|
||||
And drag and drop it into the issue.
|
||||
+ Or upload it to the cloud, such as Dropbox, Mega etc.
|
||||
* To create a RenderDoc capture, please refer to [RenderDoc's documentation](https://renderdoc.org/docs/how/how_capture_frame.html).
|
||||
|
||||
#### 3. Please attach screenshots of your problem.
|
||||
* Enable performance overlay with at least medium level of detail.
|
||||
You can find it in _Emulator_ tab in _Settings_.
|
||||
|
||||
#### 4. Please provide comparison with real PS3.
|
||||
|
||||
#### 5. Please provide your system configuration:
|
||||
* OS
|
||||
* CPU
|
||||
* GPU
|
||||
* Driver version
|
||||
* etc.
|
||||
|
||||
#### Please include.
|
||||
* Anything else you deem to be important
|
||||
@@ -0,0 +1,78 @@
|
||||
name: Bug report
|
||||
description: If something doesn't work correctly in RPCS3
|
||||
title: "Enter a title here"
|
||||
labels: []
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
# Summary
|
||||
Please do not ask for help or report compatibility regressions here, use [RPCS3 Discord server](https://discord.me/RPCS3) or [forums](https://forums.rpcs3.net/) instead.
|
||||
- type: textarea
|
||||
id: quick-summary
|
||||
attributes:
|
||||
label: Quick summary
|
||||
description: Please briefly describe what is not working correctly.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: details
|
||||
attributes:
|
||||
label: Details
|
||||
description: |
|
||||
Please describe the problem as accurately as possible.
|
||||
Provide a comparison with a real PS3, if possible.
|
||||
validations:
|
||||
required: false
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
# Log files
|
||||
|
||||
Provide a log file that includes the bug you're reporting.
|
||||
|
||||
Obtaining the log file:
|
||||
* Run the game until you find the regression.
|
||||
* Completely close RPCS3 and locate the log file.
|
||||
|
||||
RPCS3's Log file will be ```RPCS3.log.gz``` (sometimes shows as RPCS3.log with zip icon) or ```RPCS3.log``` (sometimes shows as RPCS3 wtih notepad icon).
|
||||
* On Windows it will be in the RPCS3 directory near the executable
|
||||
* On Linux it will be in ```~/.cache/rpcs3/```
|
||||
* On MacOS it will be in ```~/Library/Caches/rpcs3```. If you're unable to locate it copy paste the path in Spotlight and hit enter.
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: Attach a log file
|
||||
description: |
|
||||
Drag & drop the files into this input field, or upload them to another service (f.ex. Dropbox, Mega) and provide a link.
|
||||
validations:
|
||||
required: true
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
# Other details
|
||||
If you describe a graphical issue, please provide an RSX capture and/or a RenderDoc capture that demonstrate it.
|
||||
* To create an RSX capture, use _Create_ _RSX_ _Capture_ under _Utilities_.
|
||||
* Captures will be stored in RPCS3 folder → captures.
|
||||
* To create a RenderDoc capture, please refer to [RenderDoc's documentation](https://renderdoc.org/docs/how/how_capture_frame.html).
|
||||
- type: textarea
|
||||
id: captures
|
||||
attributes:
|
||||
label: Attach capture files for visual issues
|
||||
description: Compress your capture with 7z, Rar etc. and attach it here, or upload it to the cloud (Dropbox, Mega etc) and add a link to it.
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: system-info
|
||||
attributes:
|
||||
label: System configuration
|
||||
description: Provide information about your system, such as operating system, CPU and GPU model, GPU driver version and other information that describes your system configuration.
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: other-details
|
||||
attributes:
|
||||
label: Other details
|
||||
description: Include anything else you deem to be important.
|
||||
validations:
|
||||
required: false
|
||||
@@ -1,41 +0,0 @@
|
||||
---
|
||||
name: Feature Request
|
||||
about: If RPCS3 lacks a feature you would like to see
|
||||
title: '[Feature Request] Enter Feature Title Here'
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## Please do not ask for help or report compatibility regressions here, use [RPCS3 Discord server](https://discord.me/RPCS3) or [forums](https://forums.rpcs3.net/) instead.
|
||||
|
||||
## Quick summary
|
||||
Please briefly describe what feature you would like RPCS3 to have.
|
||||
|
||||
## Details
|
||||
|
||||
Please describe the feature as accurately as possible.
|
||||
|
||||
#### 1. Please describe, what part of RPCS3 would be affected by your feature:
|
||||
* Gameplay
|
||||
* Debugging
|
||||
* UI
|
||||
* Patches
|
||||
* Installation
|
||||
* Github
|
||||
* CI
|
||||
* etc.
|
||||
|
||||
#### 2. Please tell us, why your feature is important to RPCS3.
|
||||
|
||||
#### 3. Please attach screenshots of the feature implemented in other projects.
|
||||
|
||||
#### 4. Please provide your system configuration:
|
||||
* OS
|
||||
* CPU
|
||||
* GPU
|
||||
* Driver version
|
||||
* etc.
|
||||
|
||||
#### Please include.
|
||||
* Anything else you deem to be important
|
||||
@@ -0,0 +1,36 @@
|
||||
name: Feature request
|
||||
description: If RPCS3 lacks a feature you would like to see
|
||||
title: "[Feature request] Enter a title here"
|
||||
labels: []
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Please do not ask for help or report compatibility regressions here, use [RPCS3 Discord server](https://discord.me/RPCS3) or [forums](https://forums.rpcs3.net/) instead.
|
||||
- type: textarea
|
||||
id: quick-summary
|
||||
attributes:
|
||||
label: Quick summary
|
||||
description: Please briefly describe what feature you would like RPCS3 to have.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: details
|
||||
attributes:
|
||||
label: Details
|
||||
description: Please describe the feature as accurately as possible.
|
||||
validations:
|
||||
required: false
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Please include the following information:
|
||||
* What part of RPCS3 would be affected by your feature? (Gameplay, Debugging, UI, Patches, Installation, CI etc.)
|
||||
* Why your feature is important to RPCS3.
|
||||
* If the feature is implemented in other projects, attach screenshots.
|
||||
* If this feature is something that a game is trying to use, upload a log file for it.
|
||||
|
||||
RPCS3's Log file will be ```RPCS3.log.gz``` (sometimes shows as RPCS3.log with zip icon) or ```RPCS3.log``` (sometimes shows as RPCS3 wtih notepad icon).
|
||||
* On Windows it will be in the RPCS3 directory near the executable
|
||||
* On Linux it will be in ```~/.cache/rpcs3/```
|
||||
* On MacOS it will be in ```~/Library/Caches/rpcs3```. If you're unable to locate it copy paste the path in Spotlight and hit enter.
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
name: Advanced
|
||||
about: For developers and experienced users only
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## Please do not ask for help or report compatibility regressions here, use [RPCS3 Discord server](https://discord.me/RPCS3) or [forums](https://forums.rpcs3.net/) instead.
|
||||
|
||||
You're using the advanced template. You're expected to know what to write in order to fill in all the required information for proper report.
|
||||
|
||||
If you're unsure on what to do, please return back to the issue type selection and choose different category.
|
||||
Vendored
+9
-5
@@ -1,5 +1,5 @@
|
||||
/* 7zTypes.h -- Basic types
|
||||
2021-12-25 : Igor Pavlov : Public domain */
|
||||
2022-04-01 : Igor Pavlov : Public domain */
|
||||
|
||||
#ifndef __7Z_TYPES_H
|
||||
#define __7Z_TYPES_H
|
||||
@@ -133,10 +133,6 @@ typedef int WRes;
|
||||
#define MY__E_ERROR_NEGATIVE_SEEK MY_HRESULT_FROM_errno_CONST_ERROR(EINVAL)
|
||||
*/
|
||||
|
||||
// gcc / clang : (sizeof(long) == sizeof(void*)) in 32/64 bits
|
||||
typedef long INT_PTR;
|
||||
typedef unsigned long UINT_PTR;
|
||||
|
||||
#define TEXT(quote) quote
|
||||
|
||||
#define FILE_ATTRIBUTE_READONLY 0x0001
|
||||
@@ -520,6 +516,14 @@ struct ISzAlloc
|
||||
|
||||
#endif
|
||||
|
||||
#define k_PropVar_TimePrec_0 0
|
||||
#define k_PropVar_TimePrec_Unix 1
|
||||
#define k_PropVar_TimePrec_DOS 2
|
||||
#define k_PropVar_TimePrec_HighPrec 3
|
||||
#define k_PropVar_TimePrec_Base 16
|
||||
#define k_PropVar_TimePrec_100ns (k_PropVar_TimePrec_Base + 7)
|
||||
#define k_PropVar_TimePrec_1ns (k_PropVar_TimePrec_Base + 9)
|
||||
|
||||
EXTERN_C_END
|
||||
|
||||
#endif
|
||||
|
||||
Vendored
+5
-5
@@ -1,7 +1,7 @@
|
||||
#define MY_VER_MAJOR 21
|
||||
#define MY_VER_MINOR 07
|
||||
#define MY_VER_MAJOR 22
|
||||
#define MY_VER_MINOR 01
|
||||
#define MY_VER_BUILD 0
|
||||
#define MY_VERSION_NUMBERS "21.07"
|
||||
#define MY_VERSION_NUMBERS "22.01"
|
||||
#define MY_VERSION MY_VERSION_NUMBERS
|
||||
|
||||
#ifdef MY_CPU_NAME
|
||||
@@ -10,12 +10,12 @@
|
||||
#define MY_VERSION_CPU MY_VERSION
|
||||
#endif
|
||||
|
||||
#define MY_DATE "2021-12-26"
|
||||
#define MY_DATE "2022-07-15"
|
||||
#undef MY_COPYRIGHT
|
||||
#undef MY_VERSION_COPYRIGHT_DATE
|
||||
#define MY_AUTHOR_NAME "Igor Pavlov"
|
||||
#define MY_COPYRIGHT_PD "Igor Pavlov : Public domain"
|
||||
#define MY_COPYRIGHT_CR "Copyright (c) 1999-2021 Igor Pavlov"
|
||||
#define MY_COPYRIGHT_CR "Copyright (c) 1999-2022 Igor Pavlov"
|
||||
|
||||
#ifdef USE_COPYRIGHT_CR
|
||||
#define MY_COPYRIGHT MY_COPYRIGHT_CR
|
||||
|
||||
Vendored
+10
-7
@@ -1,5 +1,5 @@
|
||||
/* CpuArch.h -- CPU specific code
|
||||
2021-07-13 : Igor Pavlov : Public domain */
|
||||
2022-07-15 : Igor Pavlov : Public domain */
|
||||
|
||||
#ifndef __CPU_ARCH_H
|
||||
#define __CPU_ARCH_H
|
||||
@@ -123,12 +123,15 @@ MY_CPU_64BIT means that processor can work with 64-bit registers.
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(__sparc64__)
|
||||
#define MY_CPU_NAME "sparc64"
|
||||
#define MY_CPU_64BIT
|
||||
#elif defined(__sparc__)
|
||||
#define MY_CPU_NAME "sparc"
|
||||
/* #define MY_CPU_32BIT */
|
||||
#if defined(__riscv) \
|
||||
|| defined(__riscv__)
|
||||
#if __riscv_xlen == 32
|
||||
#define MY_CPU_NAME "riscv32"
|
||||
#elif __riscv_xlen == 64
|
||||
#define MY_CPU_NAME "riscv64"
|
||||
#else
|
||||
#define MY_CPU_NAME "riscv"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
Vendored
+6
-2
@@ -1,5 +1,5 @@
|
||||
/* DllSecur.c -- DLL loading security
|
||||
2021-12-25 : Igor Pavlov : Public domain */
|
||||
2022-07-15 : Igor Pavlov : Public domain */
|
||||
|
||||
#include "Precomp.h"
|
||||
|
||||
@@ -11,6 +11,10 @@
|
||||
|
||||
#ifndef UNDER_CE
|
||||
|
||||
#if defined(__GNUC__) && (__GNUC__ >= 8)
|
||||
#pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
#endif
|
||||
|
||||
typedef BOOL (WINAPI *Func_SetDefaultDllDirectories)(DWORD DirectoryFlags);
|
||||
|
||||
#define MY_LOAD_LIBRARY_SEARCH_USER_DIRS 0x400
|
||||
@@ -34,7 +38,7 @@ static const char * const g_Dlls =
|
||||
#endif
|
||||
|
||||
// #define MY_CAST_FUNC (void(*)())
|
||||
#define MY_CAST_FUNC
|
||||
#define MY_CAST_FUNC
|
||||
|
||||
void My_SetDefaultDllDirectories()
|
||||
{
|
||||
|
||||
Vendored
+4
-7
@@ -1,5 +1,5 @@
|
||||
/* LzmaEnc.c -- LZMA Encoder
|
||||
2021-11-18: Igor Pavlov : Public domain */
|
||||
2022-07-15: Igor Pavlov : Public domain */
|
||||
|
||||
#include "Precomp.h"
|
||||
|
||||
@@ -2970,6 +2970,7 @@ const Byte *LzmaEnc_GetCurBuf(CLzmaEncHandle pp)
|
||||
}
|
||||
|
||||
|
||||
// (desiredPackSize == 0) is not allowed
|
||||
SRes LzmaEnc_CodeOneMemBlock(CLzmaEncHandle pp, BoolInt reInit,
|
||||
Byte *dest, size_t *destLen, UInt32 desiredPackSize, UInt32 *unpackSize)
|
||||
{
|
||||
@@ -2990,14 +2991,10 @@ SRes LzmaEnc_CodeOneMemBlock(CLzmaEncHandle pp, BoolInt reInit,
|
||||
if (reInit)
|
||||
LzmaEnc_Init(p);
|
||||
LzmaEnc_InitPrices(p);
|
||||
|
||||
nowPos64 = p->nowPos64;
|
||||
RangeEnc_Init(&p->rc);
|
||||
p->rc.outStream = &outStream.vt;
|
||||
|
||||
if (desiredPackSize == 0)
|
||||
return SZ_ERROR_OUTPUT_EOF;
|
||||
|
||||
nowPos64 = p->nowPos64;
|
||||
|
||||
res = LzmaEnc_CodeOneBlock(p, desiredPackSize, *unpackSize);
|
||||
|
||||
*unpackSize = (UInt32)(p->nowPos64 - nowPos64);
|
||||
|
||||
Vendored
+1
-1
Submodule 3rdparty/FAudio updated: 2faf0dbdb2...e787ed7655
+24
-3
@@ -153,6 +153,20 @@ typedef int64_t khronos_int64_t;
|
||||
typedef uint64_t khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
/*
|
||||
* To support platform where unsigned long cannot be used interchangeably with
|
||||
* inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t.
|
||||
* Ideally, we could just use (u)intptr_t everywhere, but this could result in
|
||||
* ABI breakage if khronos_uintptr_t is changed from unsigned long to
|
||||
* unsigned long long or similar (this results in different C++ name mangling).
|
||||
* To avoid changes for existing platforms, we restrict usage of intptr_t to
|
||||
* platforms where the size of a pointer is larger than the size of long.
|
||||
*/
|
||||
#if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__)
|
||||
#if __SIZEOF_POINTER__ > __SIZEOF_LONG__
|
||||
#define KHRONOS_USE_INTPTR_T
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#elif defined(__VMS ) || defined(__sgi)
|
||||
|
||||
@@ -235,14 +249,21 @@ typedef unsigned short int khronos_uint16_t;
|
||||
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
|
||||
* to be the only LLP64 architecture in current use.
|
||||
*/
|
||||
#ifdef _WIN64
|
||||
#ifdef KHRONOS_USE_INTPTR_T
|
||||
typedef intptr_t khronos_intptr_t;
|
||||
typedef uintptr_t khronos_uintptr_t;
|
||||
#elif defined(_WIN64)
|
||||
typedef signed long long int khronos_intptr_t;
|
||||
typedef unsigned long long int khronos_uintptr_t;
|
||||
typedef signed long long int khronos_ssize_t;
|
||||
typedef unsigned long long int khronos_usize_t;
|
||||
#else
|
||||
typedef signed long int khronos_intptr_t;
|
||||
typedef unsigned long int khronos_uintptr_t;
|
||||
#endif
|
||||
|
||||
#if defined(_WIN64)
|
||||
typedef signed long long int khronos_ssize_t;
|
||||
typedef unsigned long long int khronos_usize_t;
|
||||
#else
|
||||
typedef signed long int khronos_ssize_t;
|
||||
typedef unsigned long int khronos_usize_t;
|
||||
#endif
|
||||
Vendored
+36
-2
@@ -32,9 +32,9 @@ extern "C" {
|
||||
#define GLAPI extern
|
||||
#endif
|
||||
|
||||
#define GL_GLEXT_VERSION 20210420
|
||||
#define GL_GLEXT_VERSION 20220530
|
||||
|
||||
#include "khrplatform.h"
|
||||
#include <KHR/khrplatform.h>
|
||||
|
||||
/* Generated C header for:
|
||||
* API: gl
|
||||
@@ -8214,6 +8214,10 @@ GLAPI void APIENTRY glMemoryBarrierEXT (GLbitfield barriers);
|
||||
#define GL_EXT_shader_integer_mix 1
|
||||
#endif /* GL_EXT_shader_integer_mix */
|
||||
|
||||
#ifndef GL_EXT_shader_samples_identical
|
||||
#define GL_EXT_shader_samples_identical 1
|
||||
#endif /* GL_EXT_shader_samples_identical */
|
||||
|
||||
#ifndef GL_EXT_shadow_funcs
|
||||
#define GL_EXT_shadow_funcs 1
|
||||
#endif /* GL_EXT_shadow_funcs */
|
||||
@@ -8625,6 +8629,36 @@ GLAPI void APIENTRY glTextureNormalEXT (GLenum mode);
|
||||
#define GL_RGBA_SNORM 0x8F93
|
||||
#endif /* GL_EXT_texture_snorm */
|
||||
|
||||
#ifndef GL_EXT_texture_storage
|
||||
#define GL_EXT_texture_storage 1
|
||||
#define GL_TEXTURE_IMMUTABLE_FORMAT_EXT 0x912F
|
||||
#define GL_RGBA32F_EXT 0x8814
|
||||
#define GL_RGB32F_EXT 0x8815
|
||||
#define GL_ALPHA32F_EXT 0x8816
|
||||
#define GL_LUMINANCE32F_EXT 0x8818
|
||||
#define GL_LUMINANCE_ALPHA32F_EXT 0x8819
|
||||
#define GL_RGBA16F_EXT 0x881A
|
||||
#define GL_RGB16F_EXT 0x881B
|
||||
#define GL_ALPHA16F_EXT 0x881C
|
||||
#define GL_LUMINANCE16F_EXT 0x881E
|
||||
#define GL_LUMINANCE_ALPHA16F_EXT 0x881F
|
||||
#define GL_BGRA8_EXT 0x93A1
|
||||
#define GL_R8_EXT 0x8229
|
||||
#define GL_RG8_EXT 0x822B
|
||||
#define GL_R32F_EXT 0x822E
|
||||
#define GL_RG32F_EXT 0x8230
|
||||
#define GL_R16F_EXT 0x822D
|
||||
#define GL_RG16F_EXT 0x822F
|
||||
typedef void (APIENTRYP PFNGLTEXSTORAGE1DEXTPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width);
|
||||
typedef void (APIENTRYP PFNGLTEXSTORAGE2DEXTPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height);
|
||||
typedef void (APIENTRYP PFNGLTEXSTORAGE3DEXTPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth);
|
||||
#ifdef GL_GLEXT_PROTOTYPES
|
||||
GLAPI void APIENTRY glTexStorage1DEXT (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width);
|
||||
GLAPI void APIENTRY glTexStorage2DEXT (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height);
|
||||
GLAPI void APIENTRY glTexStorage3DEXT (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth);
|
||||
#endif
|
||||
#endif /* GL_EXT_texture_storage */
|
||||
|
||||
#ifndef GL_EXT_texture_swizzle
|
||||
#define GL_EXT_texture_swizzle 1
|
||||
#define GL_TEXTURE_SWIZZLE_R_EXT 0x8E42
|
||||
|
||||
Vendored
+18
-18
@@ -747,12 +747,12 @@ AF1 sharpness){
|
||||
// Immediate constants for peak range.
|
||||
AF2 peakC=AF2(1.0,-1.0*4.0);
|
||||
// Limiters, these need to be high precision RCPs.
|
||||
AF1 hitMinR=mn4R*ARcpF1(AF1_(4.0)*mx4R);
|
||||
AF1 hitMinG=mn4G*ARcpF1(AF1_(4.0)*mx4G);
|
||||
AF1 hitMinB=mn4B*ARcpF1(AF1_(4.0)*mx4B);
|
||||
AF1 hitMaxR=(peakC.x-mx4R)*ARcpF1(AF1_(4.0)*mn4R+peakC.y);
|
||||
AF1 hitMaxG=(peakC.x-mx4G)*ARcpF1(AF1_(4.0)*mn4G+peakC.y);
|
||||
AF1 hitMaxB=(peakC.x-mx4B)*ARcpF1(AF1_(4.0)*mn4B+peakC.y);
|
||||
AF1 hitMinR=min(mn4R,eR)*ARcpF1(AF1_(4.0)*mx4R);
|
||||
AF1 hitMinG=min(mn4G,eG)*ARcpF1(AF1_(4.0)*mx4G);
|
||||
AF1 hitMinB=min(mn4B,eB)*ARcpF1(AF1_(4.0)*mx4B);
|
||||
AF1 hitMaxR=(peakC.x-max(mx4R,eR))*ARcpF1(AF1_(4.0)*mn4R+peakC.y);
|
||||
AF1 hitMaxG=(peakC.x-max(mx4G,eG))*ARcpF1(AF1_(4.0)*mn4G+peakC.y);
|
||||
AF1 hitMaxB=(peakC.x-max(mx4B,eB))*ARcpF1(AF1_(4.0)*mn4B+peakC.y);
|
||||
AF1 lobeR=max(-hitMinR,hitMaxR);
|
||||
AF1 lobeG=max(-hitMinG,hitMaxG);
|
||||
AF1 lobeB=max(-hitMinB,hitMaxB);
|
||||
@@ -845,12 +845,12 @@ AF1 sharpness){
|
||||
// Immediate constants for peak range.
|
||||
AH2 peakC=AH2(1.0,-1.0*4.0);
|
||||
// Limiters, these need to be high precision RCPs.
|
||||
AH1 hitMinR=mn4R*ARcpH1(AH1_(4.0)*mx4R);
|
||||
AH1 hitMinG=mn4G*ARcpH1(AH1_(4.0)*mx4G);
|
||||
AH1 hitMinB=mn4B*ARcpH1(AH1_(4.0)*mx4B);
|
||||
AH1 hitMaxR=(peakC.x-mx4R)*ARcpH1(AH1_(4.0)*mn4R+peakC.y);
|
||||
AH1 hitMaxG=(peakC.x-mx4G)*ARcpH1(AH1_(4.0)*mn4G+peakC.y);
|
||||
AH1 hitMaxB=(peakC.x-mx4B)*ARcpH1(AH1_(4.0)*mn4B+peakC.y);
|
||||
AH1 hitMinR=min(mn4R,eR)*ARcpH1(AH1_(4.0)*mx4R);
|
||||
AH1 hitMinG=min(mn4G,eG)*ARcpH1(AH1_(4.0)*mx4G);
|
||||
AH1 hitMinB=min(mn4B,eB)*ARcpH1(AH1_(4.0)*mx4B);
|
||||
AH1 hitMaxR=(peakC.x-max(mx4R,eR))*ARcpH1(AH1_(4.0)*mn4R+peakC.y);
|
||||
AH1 hitMaxG=(peakC.x-max(mx4G,eG))*ARcpH1(AH1_(4.0)*mn4G+peakC.y);
|
||||
AH1 hitMaxB=(peakC.x-max(mx4B,eB))*ARcpH1(AH1_(4.0)*mn4B+peakC.y);
|
||||
AH1 lobeR=max(-hitMinR,hitMaxR);
|
||||
AH1 lobeG=max(-hitMinG,hitMaxG);
|
||||
AH1 lobeB=max(-hitMinB,hitMaxB);
|
||||
@@ -963,12 +963,12 @@ AF1 sharpness){
|
||||
// Immediate constants for peak range.
|
||||
AH2 peakC=AH2(1.0,-1.0*4.0);
|
||||
// Limiters, these need to be high precision RCPs.
|
||||
AH2 hitMinR=mn4R*ARcpH2(AH2_(4.0)*mx4R);
|
||||
AH2 hitMinG=mn4G*ARcpH2(AH2_(4.0)*mx4G);
|
||||
AH2 hitMinB=mn4B*ARcpH2(AH2_(4.0)*mx4B);
|
||||
AH2 hitMaxR=(peakC.x-mx4R)*ARcpH2(AH2_(4.0)*mn4R+peakC.y);
|
||||
AH2 hitMaxG=(peakC.x-mx4G)*ARcpH2(AH2_(4.0)*mn4G+peakC.y);
|
||||
AH2 hitMaxB=(peakC.x-mx4B)*ARcpH2(AH2_(4.0)*mn4B+peakC.y);
|
||||
AH2 hitMinR=min(mn4R,eR)*ARcpH2(AH2_(4.0)*mx4R);
|
||||
AH2 hitMinG=min(mn4G,eG)*ARcpH2(AH2_(4.0)*mx4G);
|
||||
AH2 hitMinB=min(mn4B,eB)*ARcpH2(AH2_(4.0)*mx4B);
|
||||
AH2 hitMaxR=(peakC.x-max(mx4R,eR))*ARcpH2(AH2_(4.0)*mn4R+peakC.y);
|
||||
AH2 hitMaxG=(peakC.x-max(mx4G,eG))*ARcpH2(AH2_(4.0)*mn4G+peakC.y);
|
||||
AH2 hitMaxB=(peakC.x-max(mx4B,eB))*ARcpH2(AH2_(4.0)*mn4B+peakC.y);
|
||||
AH2 lobeR=max(-hitMinR,hitMaxR);
|
||||
AH2 lobeG=max(-hitMinG,hitMaxG);
|
||||
AH2 lobeB=max(-hitMinB,hitMaxB);
|
||||
|
||||
Vendored
+1
-1
@@ -4,7 +4,7 @@ include(ExternalProject)
|
||||
|
||||
ExternalProject_Add(moltenvk
|
||||
GIT_REPOSITORY https://github.com/KhronosGroup/MoltenVK.git
|
||||
GIT_TAG 1236d2f
|
||||
GIT_TAG 1112005
|
||||
BUILD_IN_SOURCE 1
|
||||
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK
|
||||
CONFIGURE_COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK/fetchDependencies" --macos
|
||||
|
||||
Vendored
-7
@@ -1,7 +0,0 @@
|
||||
diff --git a/ExternalRevisions/SPIRV-Cross_repo_revision b/ExternalRevisions/SPIRV-Cross_repo_revision
|
||||
index 777346a0..c1971b45 100644
|
||||
--- a/ExternalRevisions/SPIRV-Cross_repo_revision
|
||||
+++ b/ExternalRevisions/SPIRV-Cross_repo_revision
|
||||
@@ -1 +1 @@
|
||||
-0d4ce028bf8b8a94d325dc1e1c20446153ba19c4
|
||||
+44c3333a1c315ead00c24f7aef5fa8a7ccf49299
|
||||
Vendored
+9
-9
@@ -39,28 +39,28 @@
|
||||
<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" -DSPIRV_WERROR=OFF -DSPIRV-Headers_SOURCE_DIR=$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)/SPIRV-Headers').Replace('\', '/')) SPIRV-Tools -B build</CmakeReleaseCLI>
|
||||
cmake -G $(CmakeGenerator) -A x64 -DCMAKE_BUILD_TYPE="Release" -DSPIRV_WERROR=OFF -DSPIRV-Headers_SOURCE_DIR="$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)/SPIRV-Headers').Replace('\', '/'))" SPIRV-Tools -B build</CmakeReleaseCLI>
|
||||
<CmakeDebugCLI>call vsdevcmd.bat -arch=amd64
|
||||
cmake -G $(CmakeGenerator) -A x64 -DCMAKE_BUILD_TYPE="Debug" -DSPIRV_WERROR=OFF -DSPIRV-Headers_SOURCE_DIR=$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)/SPIRV-Headers').Replace('\', '/')) -S SPIRV-Tools -B build</CmakeDebugCLI>
|
||||
cmake -G $(CmakeGenerator) -A x64 -DCMAKE_BUILD_TYPE="Debug" -DSPIRV_WERROR=OFF -DSPIRV-Headers_SOURCE_DIR="$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)/SPIRV-Headers').Replace('\', '/'))" -S SPIRV-Tools -B build</CmakeDebugCLI>
|
||||
<PropsAbsPath>$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\..\buildfiles\msvc\common_default.props'))</PropsAbsPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<NMakeBuildCommandLine>$(CmakeReleaseCLI)
|
||||
msbuild.exe build\ALL_BUILD.vcxproj /t:build /p:Configuration=Release /p:ForceImportBeforeCppTargets=$(PropsAbsPath) /m</NMakeBuildCommandLine>
|
||||
msbuild.exe build\ALL_BUILD.vcxproj /t:build /p:Configuration=Release /p:ForceImportBeforeCppTargets="$(PropsAbsPath)" /m</NMakeBuildCommandLine>
|
||||
<NMakeReBuildCommandLine>$(CmakeReleaseCLI)
|
||||
msbuild.exe build\ALL_BUILD.vcxproj /t:rebuild /p:Configuration=Release /p:ForceImportBeforeCppTargets=$(PropsAbsPath) /m</NMakeReBuildCommandLine>
|
||||
msbuild.exe build\ALL_BUILD.vcxproj /t:rebuild /p:Configuration=Release /p:ForceImportBeforeCppTargets="$(PropsAbsPath)" /m</NMakeReBuildCommandLine>
|
||||
<NMakeCleanCommandLine>$(CmakeReleaseCLI)
|
||||
msbuild.exe build\ALL_BUILD.vcxproj /t:clean /p:Configuration=Release /p:ForceImportBeforeCppTargets=$(PropsAbsPath) /m</NMakeCleanCommandLine>
|
||||
msbuild.exe build\ALL_BUILD.vcxproj /t:clean /p:Configuration=Release /p:ForceImportBeforeCppTargets="$(PropsAbsPath)" /m</NMakeCleanCommandLine>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<NMakeBuildCommandLine>$(CmakeDebugCLI)
|
||||
msbuild.exe build\ALL_BUILD.vcxproj /t:build /p:Configuration=Debug /p:ForceImportBeforeCppTargets=$(PropsAbsPath) /m
|
||||
msbuild.exe build\ALL_BUILD.vcxproj /t:build /p:Configuration=Debug /p:ForceImportBeforeCppTargets="$(PropsAbsPath)" /m
|
||||
</NMakeBuildCommandLine>
|
||||
<NMakeReBuildCommandLine>$(CmakeDebugCLI)
|
||||
msbuild.exe build\ALL_BUILD.vcxproj /t:rebuild /p:Configuration=Debug /p:ForceImportBeforeCppTargets=$(PropsAbsPath) /m
|
||||
msbuild.exe build\ALL_BUILD.vcxproj /t:rebuild /p:Configuration=Debug /p:ForceImportBeforeCppTargets="$(PropsAbsPath)" /m
|
||||
</NMakeReBuildCommandLine>
|
||||
<NMakeCleanCommandLine>$(CmakeDebugCLI)
|
||||
msbuild.exe build\ALL_BUILD.vcxproj /t:clean /p:Configuration=Debug /p:ForceImportBeforeCppTargets=$(PropsAbsPath) /m
|
||||
msbuild.exe build\ALL_BUILD.vcxproj /t:clean /p:Configuration=Debug /p:ForceImportBeforeCppTargets="$(PropsAbsPath)" /m
|
||||
</NMakeCleanCommandLine>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
@@ -68,4 +68,4 @@
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
Vendored
+1
-1
@@ -27,7 +27,7 @@ target_compile_definitions(soundtouch PUBLIC
|
||||
SOUNDTOUCH_FLOAT_SAMPLES;
|
||||
)
|
||||
|
||||
target_compile_options(soundtouch PUBLIC "-w")
|
||||
target_compile_options(soundtouch PRIVATE "-w")
|
||||
|
||||
if (CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86|X86|amd64|AMD64|em64t|EM64T)")
|
||||
target_compile_definitions(soundtouch PUBLIC
|
||||
|
||||
+1276
-1275
File diff suppressed because it is too large
Load Diff
BIN
Binary file not shown.
Vendored
+1
-1
Submodule 3rdparty/asmjit/asmjit updated: fc2a5d82f7...06d0badec5
Vendored
+5
-3
@@ -62,12 +62,13 @@
|
||||
<ClCompile Include="asmjit\src\asmjit\arm\a64builder.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\arm\a64compiler.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\arm\a64emithelper.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\arm\a64formatter.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\arm\a64func.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\arm\a64instapi.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\arm\a64instdb.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\arm\a64operand.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\arm\a64rapass.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\arm\armformatter.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\arm\armfunc.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="asmjit\src\asmjit\core\api-build_p.h" />
|
||||
@@ -134,11 +135,14 @@
|
||||
<ClInclude Include="asmjit\src\asmjit\x86\x86opcode_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\x86\x86operand.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\x86\x86rapass_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64archtraits_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64assembler.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64builder.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64compiler.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64emithelper_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64emitter.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64formatter_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64func_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64globals.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64instapi_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64instdb.h" />
|
||||
@@ -146,9 +150,7 @@
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64operand.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64rapass_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64utils.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\armarchtraits_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\armformatter_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\armfunc_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\armglobals.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\armoperand.h" />
|
||||
</ItemGroup>
|
||||
|
||||
+5
-3
@@ -52,12 +52,13 @@
|
||||
<ClCompile Include="asmjit\src\asmjit\arm\a64builder.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\arm\a64compiler.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\arm\a64emithelper.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\arm\a64formatter.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\arm\a64func.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\arm\a64instapi.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\arm\a64instdb.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\arm\a64operand.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\arm\a64rapass.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\arm\armformatter.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\arm\armfunc.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="asmjit\src\asmjit\core\api-build_p.h" />
|
||||
@@ -124,11 +125,14 @@
|
||||
<ClInclude Include="asmjit\src\asmjit\x86\x86opcode_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\x86\x86operand.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\x86\x86rapass_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64archtraits_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64assembler.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64builder.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64compiler.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64emithelper_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64emitter.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64formatter_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64func_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64globals.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64instapi_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64instdb.h" />
|
||||
@@ -136,9 +140,7 @@
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64operand.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64rapass_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64utils.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\armarchtraits_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\armformatter_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\armfunc_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\armglobals.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\armoperand.h" />
|
||||
</ItemGroup>
|
||||
|
||||
Vendored
+1
-1
Submodule 3rdparty/cubeb/cubeb updated: 708f52cccf...dc511c6b35
Vendored
+1
@@ -12,6 +12,7 @@ else()
|
||||
option(CURL_USE_WOLFSSL "enable wolfSSL for SSL/TLS" ON)
|
||||
option(HTTP_ONLY "disables all protocols except HTTP (This overrides all CURL_DISABLE_* options)" ON)
|
||||
option(CURL_ZLIB "Set to ON to enable building curl with zlib support." OFF)
|
||||
option(USE_LIBIDN2 "Use libidn2 for IDN support" OFF) # Disabled because MacOs CI doesn't work otherwise
|
||||
set(CURL_CA_PATH "none" CACHE STRING "Location of default CA path. Set 'none' to disable or 'auto' for auto-detection. Defaults to 'auto'.")
|
||||
if(USE_MSVC_STATIC_CRT)
|
||||
set(CURL_STATIC_CRT ON CACHE BOOL "Use static crt to build curl" FORCE)
|
||||
|
||||
Vendored
+1
-1
Submodule 3rdparty/curl/curl updated: 462196e6b4...45ac4d0194
Vendored
+3
@@ -98,6 +98,7 @@
|
||||
<ClCompile Include="curl\lib\escape.c" />
|
||||
<ClCompile Include="curl\lib\file.c" />
|
||||
<ClCompile Include="curl\lib\fileinfo.c" />
|
||||
<ClCompile Include="curl\lib\fopen.c" />
|
||||
<ClCompile Include="curl\lib\formdata.c" />
|
||||
<ClCompile Include="curl\lib\ftp.c" />
|
||||
<ClCompile Include="curl\lib\ftplistparser.c" />
|
||||
@@ -279,9 +280,11 @@
|
||||
<ClInclude Include="curl\lib\dynbuf.h" />
|
||||
<ClInclude Include="curl\lib\easyif.h" />
|
||||
<ClInclude Include="curl\lib\easyoptions.h" />
|
||||
<ClInclude Include="curl\lib\easy_lock.h" />
|
||||
<ClInclude Include="curl\lib\escape.h" />
|
||||
<ClInclude Include="curl\lib\file.h" />
|
||||
<ClInclude Include="curl\lib\fileinfo.h" />
|
||||
<ClInclude Include="curl\lib\fopen.h" />
|
||||
<ClInclude Include="curl\lib\formdata.h" />
|
||||
<ClInclude Include="curl\lib\ftp.h" />
|
||||
<ClInclude Include="curl\lib\ftplistparser.h" />
|
||||
|
||||
+9
@@ -480,6 +480,9 @@
|
||||
<ClCompile Include="curl\lib\timediff.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="curl\lib\fopen.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="curl\include\curl\curl.h">
|
||||
@@ -965,6 +968,12 @@
|
||||
<ClInclude Include="curl\lib\timediff.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="curl\lib\easy_lock.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="curl\lib\fopen.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="curl\lib\libcurl.rc">
|
||||
|
||||
Vendored
+7
-7
@@ -46,21 +46,21 @@
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<NMakeBuildCommandLine>$(CmakeReleaseCLI)
|
||||
msbuild.exe build\ALL_BUILD.vcxproj /t:build /p:Configuration=Release /p:ForceImportBeforeCppTargets=$(PropsAbsPath) /m</NMakeBuildCommandLine>
|
||||
msbuild.exe build\ALL_BUILD.vcxproj /t:build /p:Configuration=Release /p:ForceImportBeforeCppTargets="$(PropsAbsPath)" /m</NMakeBuildCommandLine>
|
||||
<NMakeReBuildCommandLine>$(CmakeReleaseCLI)
|
||||
msbuild.exe build\ALL_BUILD.vcxproj /t:rebuild /p:Configuration=Release /p:ForceImportBeforeCppTargets=$(PropsAbsPath) /m</NMakeReBuildCommandLine>
|
||||
msbuild.exe build\ALL_BUILD.vcxproj /t:rebuild /p:Configuration=Release /p:ForceImportBeforeCppTargets="$(PropsAbsPath)" /m</NMakeReBuildCommandLine>
|
||||
<NMakeCleanCommandLine>$(CmakeReleaseCLI)
|
||||
msbuild.exe build\ALL_BUILD.vcxproj /t:clean /p:Configuration=Release /p:ForceImportBeforeCppTargets=$(PropsAbsPath) /m</NMakeCleanCommandLine>
|
||||
msbuild.exe build\ALL_BUILD.vcxproj /t:clean /p:Configuration=Release /p:ForceImportBeforeCppTargets="$(PropsAbsPath)" /m</NMakeCleanCommandLine>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<NMakeBuildCommandLine>$(CmakeDebugCLI)
|
||||
msbuild.exe build\ALL_BUILD.vcxproj /t:build /p:Configuration=Debug /p:ForceImportBeforeCppTargets=$(PropsAbsPath) /m
|
||||
msbuild.exe build\ALL_BUILD.vcxproj /t:build /p:Configuration=Debug /p:ForceImportBeforeCppTargets="$(PropsAbsPath)" /m
|
||||
</NMakeBuildCommandLine>
|
||||
<NMakeReBuildCommandLine>$(CmakeDebugCLI)
|
||||
msbuild.exe build\ALL_BUILD.vcxproj /t:rebuild /p:Configuration=Debug /p:ForceImportBeforeCppTargets=$(PropsAbsPath) /m
|
||||
msbuild.exe build\ALL_BUILD.vcxproj /t:rebuild /p:Configuration=Debug /p:ForceImportBeforeCppTargets="$(PropsAbsPath)" /m
|
||||
</NMakeReBuildCommandLine>
|
||||
<NMakeCleanCommandLine>$(CmakeDebugCLI)
|
||||
msbuild.exe build\ALL_BUILD.vcxproj /t:clean /p:Configuration=Debug /p:ForceImportBeforeCppTargets=$(PropsAbsPath) /m
|
||||
msbuild.exe build\ALL_BUILD.vcxproj /t:clean /p:Configuration=Debug /p:ForceImportBeforeCppTargets="$(PropsAbsPath)" /m
|
||||
</NMakeCleanCommandLine>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
@@ -68,4 +68,4 @@
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
Vendored
+1
-1
@@ -19,7 +19,7 @@ else()
|
||||
set(WOLFSSL_CHACHA "yes" CACHE INTERNAL "")
|
||||
set(WOLFSSL_FILESYSTEM "yes" CACHE INTERNAL "")
|
||||
set(WOLFSSL_PWDBASED "yes" CACHE INTERNAL "")
|
||||
set(WOLFSSL_FAST_MATH "no" CACHE INTERNAL "")
|
||||
set(WOLFSSL_FAST_MATH "yes" CACHE INTERNAL "")
|
||||
set(WOLFSSL_EXAMPLES "no" CACHE INTERNAL "")
|
||||
set(WOLFSSL_CRYPT_TESTS "no" CACHE INTERNAL "")
|
||||
set(WOLFSSL_ASYNC_THREADS "no" CACHE INTERNAL "")
|
||||
|
||||
@@ -43,6 +43,8 @@
|
||||
#define HAVE_ECC
|
||||
#define ECC_SHAMIR
|
||||
#define ECC_TIMING_RESISTANT
|
||||
#define USE_FAST_MATH
|
||||
#define FP_MAX_BITS 8192
|
||||
|
||||
/* UTF-8 aware filesystem functions for Windows */
|
||||
#define WOLFSSL_USER_FILESYSTEM
|
||||
|
||||
Vendored
+1
-1
Submodule 3rdparty/wolfssl/wolfssl updated: e722c15be8...57aac1c50b
+4
-3
@@ -102,9 +102,10 @@ git submodule update --init
|
||||
|
||||
#### Configuring the Qt plugin (if used)
|
||||
|
||||
1) Go to the Qt5 menu and edit Qt5 options.
|
||||
2) Add the path to your Qt installation with compiler e.g. `<QtInstallFolder>\5.15.2\msvc2019_64`.
|
||||
3) While selecting the rpcs3qt project, go to Qt5->Project Setting and select the version you added.
|
||||
1) Go to `Extensions->Qt VS Tools->Qt Versions`.
|
||||
2) Add the path to your Qt installation with compiler e.g. `<QtInstallFolder>\5.15.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`.
|
||||
|
||||
#### Building the projects
|
||||
|
||||
|
||||
+33
-3
@@ -71,6 +71,10 @@ namespace fs
|
||||
s64 atime;
|
||||
s64 mtime;
|
||||
s64 ctime;
|
||||
|
||||
using enable_bitcopy = std::true_type;
|
||||
|
||||
constexpr bool operator==(const stat_t&) const = default;
|
||||
};
|
||||
|
||||
// Helper, layout is equal to iovec struct
|
||||
@@ -105,6 +109,8 @@ namespace fs
|
||||
: stat_t{}
|
||||
{
|
||||
}
|
||||
|
||||
using enable_bitcopy = std::false_type;
|
||||
};
|
||||
|
||||
// Directory handle base
|
||||
@@ -681,9 +687,10 @@ namespace fs
|
||||
T obj;
|
||||
u64 pos;
|
||||
|
||||
container_stream(T&& obj)
|
||||
container_stream(T&& obj, const stat_t& init_stat = {})
|
||||
: obj(std::forward<T>(obj))
|
||||
, pos(0)
|
||||
, m_stat(init_stat)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -694,6 +701,7 @@ namespace fs
|
||||
bool trunc(u64 length) override
|
||||
{
|
||||
obj.resize(length);
|
||||
update_time(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -708,6 +716,7 @@ namespace fs
|
||||
{
|
||||
std::copy(obj.cbegin() + pos, obj.cbegin() + pos + max, static_cast<value_type*>(buffer));
|
||||
pos = pos + max;
|
||||
update_time();
|
||||
return max;
|
||||
}
|
||||
}
|
||||
@@ -743,6 +752,7 @@ namespace fs
|
||||
obj.insert(obj.end(), src + overlap, src + size);
|
||||
pos += size;
|
||||
|
||||
if (size) update_time(true);
|
||||
return size;
|
||||
}
|
||||
|
||||
@@ -767,13 +777,33 @@ namespace fs
|
||||
{
|
||||
return obj.size();
|
||||
}
|
||||
|
||||
stat_t stat() override
|
||||
{
|
||||
return m_stat;
|
||||
}
|
||||
|
||||
private:
|
||||
stat_t m_stat{};
|
||||
|
||||
void update_time(bool write = false)
|
||||
{
|
||||
// TODO: Accurate timestamps
|
||||
m_stat.atime++;
|
||||
|
||||
if (write)
|
||||
{
|
||||
m_stat.mtime = std::max(m_stat.atime, ++m_stat.mtime);
|
||||
m_stat.ctime = m_stat.mtime;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
file make_stream(T&& container = T{})
|
||||
file make_stream(T&& container = T{}, const stat_t& stat = stat_t{})
|
||||
{
|
||||
file result;
|
||||
result.reset(std::make_unique<container_stream<T>>(std::forward<T>(container)));
|
||||
result.reset(std::make_unique<container_stream<T>>(std::forward<T>(container), stat));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#include "mutex.h"
|
||||
#include "util/vm.hpp"
|
||||
#include "util/asm.hpp"
|
||||
#include "util/v128.hpp"
|
||||
#include "util/simd.hpp"
|
||||
#include <charconv>
|
||||
#include <zlib.h>
|
||||
|
||||
@@ -351,6 +353,435 @@ asmjit::inline_runtime::~inline_runtime()
|
||||
utils::memory_protect(m_data, m_size, utils::protection::rx);
|
||||
}
|
||||
|
||||
#if defined(ARCH_X64)
|
||||
asmjit::simd_builder::simd_builder(CodeHolder* ch) noexcept
|
||||
: native_asm(ch)
|
||||
{
|
||||
_init(true);
|
||||
consts[~v128()] = this->newLabel();
|
||||
}
|
||||
|
||||
asmjit::simd_builder::~simd_builder()
|
||||
{
|
||||
}
|
||||
|
||||
void asmjit::simd_builder::_init(bool full)
|
||||
{
|
||||
if (full && utils::has_avx512_icl())
|
||||
{
|
||||
v0 = x86::zmm0;
|
||||
v1 = x86::zmm1;
|
||||
v2 = x86::zmm2;
|
||||
v3 = x86::zmm3;
|
||||
v4 = x86::zmm4;
|
||||
v5 = x86::zmm5;
|
||||
vsize = 64;
|
||||
}
|
||||
else if (full && utils::has_avx2())
|
||||
{
|
||||
v0 = x86::ymm0;
|
||||
v1 = x86::ymm1;
|
||||
v2 = x86::ymm2;
|
||||
v3 = x86::ymm3;
|
||||
v4 = x86::ymm4;
|
||||
v5 = x86::ymm5;
|
||||
vsize = 32;
|
||||
}
|
||||
else
|
||||
{
|
||||
v0 = x86::xmm0;
|
||||
v1 = x86::xmm1;
|
||||
v2 = x86::xmm2;
|
||||
v3 = x86::xmm3;
|
||||
v4 = x86::xmm4;
|
||||
v5 = x86::xmm5;
|
||||
vsize = 16;
|
||||
}
|
||||
|
||||
if (full && utils::has_avx512())
|
||||
{
|
||||
vmask = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
vmask = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void asmjit::simd_builder::operator()() noexcept
|
||||
{
|
||||
for (auto&& [x, y] : consts)
|
||||
{
|
||||
this->align(AlignMode::kData, 16);
|
||||
this->bind(y);
|
||||
this->embed(&x, 16);
|
||||
}
|
||||
}
|
||||
|
||||
void asmjit::simd_builder::vec_cleanup_ret()
|
||||
{
|
||||
if (utils::has_avx() && vsize > 16)
|
||||
this->vzeroupper();
|
||||
this->ret();
|
||||
}
|
||||
|
||||
void asmjit::simd_builder::vec_set_all_zeros(const Operand& v)
|
||||
{
|
||||
x86::Xmm reg(v.id());
|
||||
if (utils::has_avx())
|
||||
this->vpxor(reg, reg, reg);
|
||||
else
|
||||
this->xorps(reg, reg);
|
||||
}
|
||||
|
||||
void asmjit::simd_builder::vec_set_all_ones(const Operand& v)
|
||||
{
|
||||
x86::Xmm reg(v.id());
|
||||
if (x86::Zmm zr(v.id()); zr == v)
|
||||
this->vpternlogd(zr, zr, zr, 0xff);
|
||||
else if (x86::Ymm yr(v.id()); yr == v)
|
||||
this->vpcmpeqd(yr, yr, yr);
|
||||
else if (utils::has_avx())
|
||||
this->vpcmpeqd(reg, reg, reg);
|
||||
else
|
||||
this->pcmpeqd(reg, reg);
|
||||
}
|
||||
|
||||
void asmjit::simd_builder::vec_set_const(const Operand& v, const v128& val)
|
||||
{
|
||||
if (!val._u)
|
||||
return vec_set_all_zeros(v);
|
||||
if (!~val._u)
|
||||
return vec_set_all_ones(v);
|
||||
else
|
||||
{
|
||||
Label co = consts[val];
|
||||
if (!co.isValid())
|
||||
co = consts[val] = this->newLabel();
|
||||
if (x86::Zmm zr(v.id()); zr == v)
|
||||
this->vbroadcasti32x4(zr, x86::oword_ptr(co));
|
||||
else if (x86::Ymm yr(v.id()); yr == v)
|
||||
this->vbroadcasti128(yr, x86::oword_ptr(co));
|
||||
else if (utils::has_avx())
|
||||
this->vmovaps(x86::Xmm(v.id()), x86::oword_ptr(co));
|
||||
else
|
||||
this->movaps(x86::Xmm(v.id()), x86::oword_ptr(co));
|
||||
}
|
||||
}
|
||||
|
||||
void asmjit::simd_builder::vec_clobbering_test(u32 esize, const Operand& v, const Operand& rhs)
|
||||
{
|
||||
if (esize == 64)
|
||||
{
|
||||
this->emit(x86::Inst::kIdVptestmd, x86::k0, v, rhs);
|
||||
this->ktestw(x86::k0, x86::k0);
|
||||
}
|
||||
else if (esize == 32)
|
||||
{
|
||||
this->emit(x86::Inst::kIdVptest, v, rhs);
|
||||
}
|
||||
else if (esize == 16 && utils::has_sse41())
|
||||
{
|
||||
this->emit(x86::Inst::kIdPtest, v, rhs);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (v != rhs)
|
||||
this->emit(x86::Inst::kIdPand, v, rhs);
|
||||
if (esize == 16)
|
||||
this->emit(x86::Inst::kIdPacksswb, v, v);
|
||||
this->emit(x86::Inst::kIdMovq, x86::rax, v);
|
||||
if (esize == 16 || esize == 8)
|
||||
this->test(x86::rax, x86::rax);
|
||||
else if (esize == 4)
|
||||
this->test(x86::eax, x86::eax);
|
||||
else if (esize == 2)
|
||||
this->test(x86::ax, x86::ax);
|
||||
else if (esize == 1)
|
||||
this->test(x86::al, x86::al);
|
||||
else
|
||||
fmt::throw_exception("Unimplemented");
|
||||
}
|
||||
}
|
||||
|
||||
asmjit::x86::Mem asmjit::simd_builder::ptr_scale_for_vec(u32 esize, const x86::Gp& base, const x86::Gp& index)
|
||||
{
|
||||
switch (ensure(esize))
|
||||
{
|
||||
case 1: return x86::ptr(base, index, 0, 0);
|
||||
case 2: return x86::ptr(base, index, 1, 0);
|
||||
case 4: return x86::ptr(base, index, 2, 0);
|
||||
case 8: return x86::ptr(base, index, 3, 0);
|
||||
default: fmt::throw_exception("Bad esize");
|
||||
}
|
||||
}
|
||||
|
||||
void asmjit::simd_builder::vec_load_unaligned(u32 esize, const Operand& v, const x86::Mem& src)
|
||||
{
|
||||
ensure(std::has_single_bit(esize));
|
||||
ensure(std::has_single_bit(vsize));
|
||||
|
||||
if (esize == 2)
|
||||
{
|
||||
ensure(vsize >= 2);
|
||||
if (vsize == 2)
|
||||
vec_set_all_zeros(v);
|
||||
if (vsize == 2 && utils::has_avx())
|
||||
this->emit(x86::Inst::kIdVpinsrw, x86::Xmm(v.id()), x86::Xmm(v.id()), src, Imm(0));
|
||||
else if (vsize == 2)
|
||||
this->emit(x86::Inst::kIdPinsrw, v, src, Imm(0));
|
||||
else if (vmask && vmask < 8)
|
||||
this->emit(x86::Inst::kIdVmovdqu16, v, src);
|
||||
else
|
||||
return vec_load_unaligned(vsize, v, src);
|
||||
}
|
||||
else if (esize == 4)
|
||||
{
|
||||
ensure(vsize >= 4);
|
||||
if (vsize == 4 && utils::has_avx())
|
||||
this->emit(x86::Inst::kIdVmovd, x86::Xmm(v.id()), src);
|
||||
else if (vsize == 4)
|
||||
this->emit(x86::Inst::kIdMovd, v, src);
|
||||
else if (vmask && vmask < 8)
|
||||
this->emit(x86::Inst::kIdVmovdqu32, v, src);
|
||||
else
|
||||
return vec_load_unaligned(vsize, v, src);
|
||||
}
|
||||
else if (esize == 8)
|
||||
{
|
||||
ensure(vsize >= 8);
|
||||
if (vsize == 8 && utils::has_avx())
|
||||
this->emit(x86::Inst::kIdVmovq, x86::Xmm(v.id()), src);
|
||||
else if (vsize == 8)
|
||||
this->emit(x86::Inst::kIdMovq, v, src);
|
||||
else if (vmask && vmask < 8)
|
||||
this->emit(x86::Inst::kIdVmovdqu64, v, src);
|
||||
else
|
||||
return vec_load_unaligned(vsize, v, src);
|
||||
}
|
||||
else if (esize >= 16)
|
||||
{
|
||||
ensure(vsize >= 16);
|
||||
if (utils::has_avx())
|
||||
this->emit(x86::Inst::kIdVmovdqu, v, src);
|
||||
else
|
||||
this->emit(x86::Inst::kIdMovups, v, src);
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::throw_exception("Unimplemented");
|
||||
}
|
||||
}
|
||||
|
||||
void asmjit::simd_builder::vec_store_unaligned(u32 esize, const Operand& v, const x86::Mem& dst)
|
||||
{
|
||||
ensure(std::has_single_bit(esize));
|
||||
ensure(std::has_single_bit(vsize));
|
||||
|
||||
if (esize == 2)
|
||||
{
|
||||
ensure(vsize >= 2);
|
||||
if (vsize == 2 && utils::has_avx())
|
||||
this->emit(x86::Inst::kIdVpextrw, dst, x86::Xmm(v.id()), Imm(0));
|
||||
else if (vsize == 2 && utils::has_sse41())
|
||||
this->emit(x86::Inst::kIdPextrw, dst, v, Imm(0));
|
||||
else if (vsize == 2)
|
||||
this->push(x86::rax), this->pextrw(x86::eax, x86::Xmm(v.id()), 0), this->mov(dst, x86::ax), this->pop(x86::rax);
|
||||
else if ((vmask && vmask < 8) || vsize >= 64)
|
||||
this->emit(x86::Inst::kIdVmovdqu16, dst, v);
|
||||
else
|
||||
return vec_store_unaligned(vsize, v, dst);
|
||||
}
|
||||
else if (esize == 4)
|
||||
{
|
||||
ensure(vsize >= 4);
|
||||
if (vsize == 4 && utils::has_avx())
|
||||
this->emit(x86::Inst::kIdVmovd, dst, x86::Xmm(v.id()));
|
||||
else if (vsize == 4)
|
||||
this->emit(x86::Inst::kIdMovd, dst, v);
|
||||
else if ((vmask && vmask < 8) || vsize >= 64)
|
||||
this->emit(x86::Inst::kIdVmovdqu32, dst, v);
|
||||
else
|
||||
return vec_store_unaligned(vsize, v, dst);
|
||||
}
|
||||
else if (esize == 8)
|
||||
{
|
||||
ensure(vsize >= 8);
|
||||
if (vsize == 8 && utils::has_avx())
|
||||
this->emit(x86::Inst::kIdVmovq, dst, x86::Xmm(v.id()));
|
||||
else if (vsize == 8)
|
||||
this->emit(x86::Inst::kIdMovq, dst, v);
|
||||
else if ((vmask && vmask < 8) || vsize >= 64)
|
||||
this->emit(x86::Inst::kIdVmovdqu64, dst, v);
|
||||
else
|
||||
return vec_store_unaligned(vsize, v, dst);
|
||||
}
|
||||
else if (esize >= 16)
|
||||
{
|
||||
ensure(vsize >= 16);
|
||||
if ((vmask && vmask < 8) || vsize >= 64)
|
||||
this->emit(x86::Inst::kIdVmovdqu64, dst, v); // Not really needed
|
||||
else if (utils::has_avx())
|
||||
this->emit(x86::Inst::kIdVmovdqu, dst, v);
|
||||
else
|
||||
this->emit(x86::Inst::kIdMovups, dst, v);
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::throw_exception("Unimplemented");
|
||||
}
|
||||
}
|
||||
|
||||
void asmjit::simd_builder::_vec_binary_op(x86::Inst::Id sse_op, x86::Inst::Id vex_op, x86::Inst::Id evex_op, const Operand& dst, const Operand& lhs, const Operand& rhs)
|
||||
{
|
||||
if (utils::has_avx())
|
||||
{
|
||||
if (vex_op == x86::Inst::kIdNone || this->_extraReg.isReg())
|
||||
{
|
||||
this->evex().emit(evex_op, dst, lhs, rhs);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->emit(vex_op, dst, lhs, rhs);
|
||||
}
|
||||
}
|
||||
else if (dst == lhs)
|
||||
{
|
||||
this->emit(sse_op, dst, rhs);
|
||||
}
|
||||
else if (dst == rhs)
|
||||
{
|
||||
fmt::throw_exception("Unimplemented");
|
||||
}
|
||||
else
|
||||
{
|
||||
this->emit(x86::Inst::kIdMovaps, dst, lhs);
|
||||
this->emit(sse_op, dst, rhs);
|
||||
}
|
||||
}
|
||||
|
||||
void asmjit::simd_builder::vec_umin(u32 esize, const Operand& dst, const Operand& lhs, const Operand& rhs)
|
||||
{
|
||||
using enum x86::Inst::Id;
|
||||
if (esize == 2)
|
||||
{
|
||||
if (utils::has_sse41())
|
||||
return _vec_binary_op(kIdPminuw, kIdVpminuw, kIdVpminuw, dst, lhs, rhs);
|
||||
}
|
||||
else if (esize == 4)
|
||||
{
|
||||
if (utils::has_sse41())
|
||||
return _vec_binary_op(kIdPminud, kIdVpminud, kIdVpminud, dst, lhs, rhs);
|
||||
}
|
||||
|
||||
fmt::throw_exception("Unimplemented");
|
||||
}
|
||||
|
||||
void asmjit::simd_builder::vec_umax(u32 esize, const Operand& dst, const Operand& lhs, const Operand& rhs)
|
||||
{
|
||||
using enum x86::Inst::Id;
|
||||
if (esize == 2)
|
||||
{
|
||||
if (utils::has_sse41())
|
||||
return _vec_binary_op(kIdPmaxuw, kIdVpmaxuw, kIdVpmaxuw, dst, lhs, rhs);
|
||||
}
|
||||
else if (esize == 4)
|
||||
{
|
||||
if (utils::has_sse41())
|
||||
return _vec_binary_op(kIdPmaxud, kIdVpmaxud, kIdVpmaxud, dst, lhs, rhs);
|
||||
}
|
||||
|
||||
fmt::throw_exception("Unimplemented");
|
||||
}
|
||||
|
||||
void asmjit::simd_builder::vec_umin_horizontal_i128(u32 esize, const x86::Gp& dst, const Operand& src, const Operand& tmp)
|
||||
{
|
||||
using enum x86::Inst::Id;
|
||||
if (!utils::has_sse41())
|
||||
{
|
||||
fmt::throw_exception("Unimplemented");
|
||||
}
|
||||
|
||||
ensure(src != tmp);
|
||||
|
||||
if (esize == 2)
|
||||
{
|
||||
this->emit(utils::has_avx() ? kIdVphminposuw : kIdPhminposuw, x86::Xmm(tmp.id()), x86::Xmm(src.id()));
|
||||
this->emit(utils::has_avx() ? kIdVpextrw : kIdPextrw, dst, x86::Xmm(tmp.id()), Imm(0));
|
||||
}
|
||||
else if (esize == 4)
|
||||
{
|
||||
if (utils::has_avx())
|
||||
{
|
||||
this->vpsrldq(x86::Xmm(tmp.id()), x86::Xmm(src.id()), 8);
|
||||
this->vpminud(x86::Xmm(tmp.id()), x86::Xmm(tmp.id()), x86::Xmm(src.id()));
|
||||
this->vpsrldq(x86::Xmm(src.id()), x86::Xmm(tmp.id()), 4);
|
||||
this->vpminud(x86::Xmm(src.id()), x86::Xmm(src.id()), x86::Xmm(tmp.id()));
|
||||
this->vmovd(dst.r32(), x86::Xmm(src.id()));
|
||||
}
|
||||
else
|
||||
{
|
||||
this->movdqa(x86::Xmm(tmp.id()), x86::Xmm(src.id()));
|
||||
this->psrldq(x86::Xmm(tmp.id()), 8);
|
||||
this->pminud(x86::Xmm(tmp.id()), x86::Xmm(src.id()));
|
||||
this->movdqa(x86::Xmm(src.id()), x86::Xmm(tmp.id()));
|
||||
this->psrldq(x86::Xmm(src.id()), 4);
|
||||
this->pminud(x86::Xmm(src.id()), x86::Xmm(tmp.id()));
|
||||
this->movd(dst.r32(), x86::Xmm(src.id()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::throw_exception("Unimplemented");
|
||||
}
|
||||
}
|
||||
|
||||
void asmjit::simd_builder::vec_umax_horizontal_i128(u32 esize, const x86::Gp& dst, const Operand& src, const Operand& tmp)
|
||||
{
|
||||
using enum x86::Inst::Id;
|
||||
if (!utils::has_sse41())
|
||||
{
|
||||
fmt::throw_exception("Unimplemented");
|
||||
}
|
||||
|
||||
ensure(src != tmp);
|
||||
|
||||
if (esize == 2)
|
||||
{
|
||||
vec_set_all_ones(x86::Xmm(tmp.id()));
|
||||
vec_xor(esize, x86::Xmm(tmp.id()), x86::Xmm(tmp.id()), x86::Xmm(src.id()));
|
||||
this->emit(utils::has_avx() ? kIdVphminposuw : kIdPhminposuw, x86::Xmm(tmp.id()), x86::Xmm(tmp.id()));
|
||||
this->emit(utils::has_avx() ? kIdVpextrw : kIdPextrw, dst, x86::Xmm(tmp.id()), Imm(0));
|
||||
this->not_(dst.r16());
|
||||
}
|
||||
else if (esize == 4)
|
||||
{
|
||||
if (utils::has_avx())
|
||||
{
|
||||
this->vpsrldq(x86::Xmm(tmp.id()), x86::Xmm(src.id()), 8);
|
||||
this->vpmaxud(x86::Xmm(tmp.id()), x86::Xmm(tmp.id()), x86::Xmm(src.id()));
|
||||
this->vpsrldq(x86::Xmm(src.id()), x86::Xmm(tmp.id()), 4);
|
||||
this->vpmaxud(x86::Xmm(src.id()), x86::Xmm(src.id()), x86::Xmm(tmp.id()));
|
||||
this->vmovd(dst.r32(), x86::Xmm(src.id()));
|
||||
}
|
||||
else
|
||||
{
|
||||
this->movdqa(x86::Xmm(tmp.id()), x86::Xmm(src.id()));
|
||||
this->psrldq(x86::Xmm(tmp.id()), 8);
|
||||
this->pmaxud(x86::Xmm(tmp.id()), x86::Xmm(src.id()));
|
||||
this->movdqa(x86::Xmm(src.id()), x86::Xmm(tmp.id()));
|
||||
this->psrldq(x86::Xmm(src.id()), 4);
|
||||
this->pmaxud(x86::Xmm(src.id()), x86::Xmm(tmp.id()));
|
||||
this->movd(dst.r32(), x86::Xmm(src.id()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::throw_exception("Unimplemented");
|
||||
}
|
||||
}
|
||||
#endif /* X86 */
|
||||
|
||||
#ifdef LLVM_AVAILABLE
|
||||
|
||||
#include <unordered_map>
|
||||
@@ -996,6 +1427,11 @@ bool jit_compiler::check(const std::string& path)
|
||||
return false;
|
||||
}
|
||||
|
||||
void jit_compiler::update_global_mapping(const std::string& name, u64 addr)
|
||||
{
|
||||
m_engine->updateGlobalMapping(name, addr);
|
||||
}
|
||||
|
||||
void jit_compiler::fin()
|
||||
{
|
||||
m_engine->finalizeObject();
|
||||
|
||||
+161
-34
@@ -42,6 +42,7 @@
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <util/v128.hpp>
|
||||
|
||||
#if defined(ARCH_X64)
|
||||
using native_asm = asmjit::x86::Assembler;
|
||||
@@ -211,40 +212,136 @@ namespace asmjit
|
||||
}
|
||||
|
||||
#if defined(ARCH_X64)
|
||||
template <uint Size>
|
||||
struct native_vec;
|
||||
|
||||
template <>
|
||||
struct native_vec<16> { using type = x86::Xmm; };
|
||||
|
||||
template <>
|
||||
struct native_vec<32> { using type = x86::Ymm; };
|
||||
|
||||
template <>
|
||||
struct native_vec<64> { using type = x86::Zmm; };
|
||||
|
||||
template <uint Size>
|
||||
using native_vec_t = typename native_vec<Size>::type;
|
||||
|
||||
// if (count > step) { for (; ctr < (count - step); ctr += step) {...} count -= ctr; }
|
||||
inline void build_incomplete_loop(native_asm& c, auto ctr, auto count, u32 step, auto&& build)
|
||||
struct simd_builder : native_asm
|
||||
{
|
||||
asmjit::Label body = c.newLabel();
|
||||
asmjit::Label exit = c.newLabel();
|
||||
std::unordered_map<v128, Label> consts;
|
||||
|
||||
ensure((step & (step - 1)) == 0);
|
||||
c.cmp(count, step);
|
||||
c.jbe(exit);
|
||||
c.sub(count, step);
|
||||
c.align(asmjit::AlignMode::kCode, 16);
|
||||
c.bind(body);
|
||||
build();
|
||||
c.add(ctr, step);
|
||||
c.sub(count, step);
|
||||
c.ja(body);
|
||||
c.add(count, step);
|
||||
c.bind(exit);
|
||||
}
|
||||
Operand v0, v1, v2, v3, v4, v5;
|
||||
|
||||
uint vsize = 16;
|
||||
uint vmask = 0;
|
||||
|
||||
simd_builder(CodeHolder* ch) noexcept;
|
||||
~simd_builder();
|
||||
|
||||
void operator()() noexcept;
|
||||
|
||||
void _init(bool full);
|
||||
void vec_cleanup_ret();
|
||||
void vec_set_all_zeros(const Operand& v);
|
||||
void vec_set_all_ones(const Operand& v);
|
||||
void vec_set_const(const Operand& v, const v128& value);
|
||||
void vec_clobbering_test(u32 esize, const Operand& v, const Operand& rhs);
|
||||
|
||||
// return x86::ptr(base, ctr, X, 0) where X is set for esize accordingly
|
||||
x86::Mem ptr_scale_for_vec(u32 esize, const x86::Gp& base, const x86::Gp& index);
|
||||
|
||||
void vec_load_unaligned(u32 esize, const Operand& v, const x86::Mem& src);
|
||||
void vec_store_unaligned(u32 esize, const Operand& v, const x86::Mem& dst);
|
||||
void vec_partial_move(u32 esize, const Operand& dst, const Operand& src);
|
||||
|
||||
void _vec_binary_op(x86::Inst::Id sse_op, x86::Inst::Id vex_op, x86::Inst::Id evex_op, const Operand& dst, const Operand& lhs, const Operand& rhs);
|
||||
|
||||
void vec_shuffle_xi8(const Operand& dst, const Operand& lhs, const Operand& rhs)
|
||||
{
|
||||
using enum x86::Inst::Id;
|
||||
_vec_binary_op(kIdPshufb, kIdVpshufb, kIdVpshufb, dst, lhs, rhs);
|
||||
}
|
||||
|
||||
void vec_xor(u32, const Operand& dst, const Operand& lhs, const Operand& rhs)
|
||||
{
|
||||
using enum x86::Inst::Id;
|
||||
_vec_binary_op(kIdPxor, kIdVpxor, kIdVpxord, dst, lhs, rhs);
|
||||
}
|
||||
|
||||
void vec_or(u32, const Operand& dst, const Operand& lhs, const Operand& rhs)
|
||||
{
|
||||
using enum x86::Inst::Id;
|
||||
_vec_binary_op(kIdPor, kIdVpor, kIdVpord, dst, lhs, rhs);
|
||||
}
|
||||
|
||||
void vec_umin(u32 esize, const Operand& dst, const Operand& lhs, const Operand& rhs);
|
||||
void vec_umax(u32 esize, const Operand& dst, const Operand& lhs, const Operand& rhs);
|
||||
|
||||
void vec_umin_horizontal_i128(u32 esize, const x86::Gp& dst, const Operand& src, const Operand& tmp);
|
||||
void vec_umax_horizontal_i128(u32 esize, const x86::Gp& dst, const Operand& src, const Operand& tmp);
|
||||
|
||||
simd_builder& keep_if_not_masked()
|
||||
{
|
||||
if (vmask && vmask < 8)
|
||||
{
|
||||
this->k(x86::KReg(vmask));
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
simd_builder& zero_if_not_masked()
|
||||
{
|
||||
if (vmask && vmask < 8)
|
||||
{
|
||||
this->k(x86::KReg(vmask));
|
||||
this->z();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void build_loop(u32 esize, auto reg_ctr, auto reg_cnt, auto&& build, auto&& reduce)
|
||||
{
|
||||
ensure((esize & (esize - 1)) == 0);
|
||||
ensure(esize <= vsize);
|
||||
|
||||
Label body = this->newLabel();
|
||||
Label next = this->newLabel();
|
||||
Label exit = this->newLabel();
|
||||
|
||||
const u32 step = vsize / esize;
|
||||
|
||||
this->xor_(reg_ctr.r32(), reg_ctr.r32()); // Reset counter reg
|
||||
this->sub(reg_cnt, step);
|
||||
this->jb(next); // If count < step, skip main loop body
|
||||
this->align(AlignMode::kCode, 16);
|
||||
this->bind(body);
|
||||
build();
|
||||
this->add(reg_ctr, step);
|
||||
this->sub(reg_cnt, step);
|
||||
this->ja(body);
|
||||
this->bind(next);
|
||||
if (!vmask)
|
||||
reduce();
|
||||
this->add(reg_cnt, step);
|
||||
this->jz(exit);
|
||||
|
||||
if (vmask)
|
||||
{
|
||||
// Build single last iteration (masked)
|
||||
this->bzhi(reg_cnt, x86::Mem(consts[~u128()], 0), reg_cnt);
|
||||
this->kmovq(x86::k7, reg_cnt);
|
||||
vmask = 7;
|
||||
build();
|
||||
vmask = -1;
|
||||
reduce();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Build tail loop (reduced vector width)
|
||||
Label body = this->newLabel();
|
||||
this->align(AlignMode::kCode, 16);
|
||||
this->bind(body);
|
||||
const uint vsz = vsize / step;
|
||||
this->_init(false);
|
||||
vsize = vsz;
|
||||
build();
|
||||
this->_init(true);
|
||||
this->inc(reg_ctr);
|
||||
this->sub(reg_cnt, 1);
|
||||
this->ja(body);
|
||||
}
|
||||
|
||||
this->bind(exit);
|
||||
}
|
||||
};
|
||||
|
||||
// for (; count > 0; ctr++, count--)
|
||||
inline void build_loop(native_asm& c, auto ctr, auto count, auto&& build)
|
||||
@@ -262,19 +359,40 @@ namespace asmjit
|
||||
c.ja(body);
|
||||
c.bind(exit);
|
||||
}
|
||||
|
||||
inline void maybe_flush_lbr(native_asm& c, uint count = 2)
|
||||
{
|
||||
// Workaround for bad LBR callstacks which happen in some situations (mainly TSX) - execute additional RETs
|
||||
Label next = c.newLabel();
|
||||
c.lea(x86::rcx, x86::qword_ptr(next));
|
||||
|
||||
for (u32 i = 0; i < count; i++)
|
||||
{
|
||||
c.push(x86::rcx);
|
||||
c.sub(x86::rcx, 16);
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < count; i++)
|
||||
{
|
||||
c.ret();
|
||||
c.align(asmjit::AlignMode::kCode, 16);
|
||||
}
|
||||
|
||||
c.bind(next);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Build runtime function with asmjit::X86Assembler
|
||||
template <typename FT, typename Asm = native_asm, typename F>
|
||||
inline FT build_function_asm(std::string_view name, F&& builder)
|
||||
inline FT build_function_asm(std::string_view name, F&& builder, ::jit_runtime* custom_runtime = nullptr)
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
pthread_jit_write_protect_np(false);
|
||||
#endif
|
||||
using namespace asmjit;
|
||||
|
||||
auto& rt = get_global_runtime();
|
||||
auto& rt = custom_runtime ? *custom_runtime : get_global_runtime();
|
||||
|
||||
CodeHolder code;
|
||||
code.init(rt.environment());
|
||||
@@ -312,6 +430,12 @@ inline FT build_function_asm(std::string_view name, F&& builder)
|
||||
builder(compiler, args);
|
||||
}
|
||||
|
||||
if constexpr (std::is_invocable_r_v<void, Asm>)
|
||||
{
|
||||
// Finalization
|
||||
compiler();
|
||||
}
|
||||
|
||||
const auto result = rt._add(&code);
|
||||
jit_announce(result, code.codeSize(), name);
|
||||
return reinterpret_cast<FT>(uptr(result));
|
||||
@@ -362,6 +486,9 @@ public:
|
||||
// Add object (path to obj file)
|
||||
void add(const std::string& path);
|
||||
|
||||
// Update global mapping for a single value
|
||||
void update_global_mapping(const std::string& name, u64 addr);
|
||||
|
||||
// Check object file
|
||||
static bool check(const std::string& path);
|
||||
|
||||
|
||||
+19
-3
@@ -5,6 +5,8 @@
|
||||
#include "util/logs.hpp"
|
||||
#include "util/v128.hpp"
|
||||
|
||||
#include <locale>
|
||||
#include <codecvt>
|
||||
#include <algorithm>
|
||||
#include <string_view>
|
||||
#include "Thread.h"
|
||||
@@ -13,8 +15,6 @@
|
||||
#include <Windows.h>
|
||||
#else
|
||||
#include <errno.h>
|
||||
#include <locale>
|
||||
#include <codecvt>
|
||||
#endif
|
||||
|
||||
std::string wchar_to_utf8(std::wstring_view src)
|
||||
@@ -26,11 +26,17 @@ std::string wchar_to_utf8(std::wstring_view src)
|
||||
WideCharToMultiByte(CP_UTF8, 0, src.data(), src.size(), utf8_string.data(), tmp_size, nullptr, nullptr);
|
||||
return utf8_string;
|
||||
#else
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> converter{};
|
||||
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converter{};
|
||||
return converter.to_bytes(src.data());
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string utf16_to_utf8(std::u16string_view src)
|
||||
{
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> converter{};
|
||||
return converter.to_bytes(src.data());
|
||||
}
|
||||
|
||||
std::wstring utf8_to_wchar(std::string_view src)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
@@ -609,3 +615,13 @@ bool fmt::match(const std::string& source, const std::string& mask)
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string get_file_extension(const std::string& file_path)
|
||||
{
|
||||
if (usz dotpos = file_path.find_last_of('.'); dotpos != std::string::npos && dotpos + 1 < file_path.size())
|
||||
{
|
||||
return file_path.substr(dotpos + 1);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
std::wstring utf8_to_wchar(std::string_view src);
|
||||
std::string wchar_to_utf8(std::wstring_view src);
|
||||
std::string utf16_to_utf8(std::u16string_view src);
|
||||
|
||||
// Copy null-terminated string from a std::string or a char array to a char array with truncation
|
||||
template <typename D, typename T>
|
||||
@@ -26,6 +27,9 @@ bool try_to_int64(s64* out, std::string_view value, s64 min, s64 max);
|
||||
// Convert string to unsigned integer
|
||||
bool try_to_uint64(u64* out, std::string_view value, u64 min, u64 max);
|
||||
|
||||
// Get the file extension of a file path ("png", "jpg", etc.)
|
||||
std::string get_file_extension(const std::string& file_path);
|
||||
|
||||
namespace fmt
|
||||
{
|
||||
std::string replace_all(std::string_view src, std::string_view from, std::string_view to, usz count = -1);
|
||||
|
||||
+12
-16
@@ -8,7 +8,6 @@
|
||||
#include "Thread.h"
|
||||
#include "Utilities/JIT.h"
|
||||
#include <thread>
|
||||
#include <sstream>
|
||||
#include <cfenv>
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -17,6 +16,7 @@
|
||||
#include <process.h>
|
||||
#include <sysinfoapi.h>
|
||||
#else
|
||||
#define _GNU_SOURCE
|
||||
#ifdef __APPLE__
|
||||
#define _XOPEN_SOURCE
|
||||
#define __USE_GNU
|
||||
@@ -96,14 +96,6 @@ extern thread_local std::string(*g_tls_log_prefix)();
|
||||
// Report error and call std::abort(), defined in main.cpp
|
||||
[[noreturn]] void report_fatal_error(std::string_view);
|
||||
|
||||
template <>
|
||||
void fmt_class_string<std::thread::id>::format(std::string& out, u64 arg)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << get_object(arg);
|
||||
out += ss.str();
|
||||
}
|
||||
|
||||
std::string dump_useful_thread_info()
|
||||
{
|
||||
thread_local volatile bool guard = false;
|
||||
@@ -1248,16 +1240,18 @@ bool handle_access_violation(u32 addr, bool is_writing, ucontext_t* context) noe
|
||||
|
||||
const auto cpu = get_current_cpu_thread();
|
||||
|
||||
if (rsx::g_access_violation_handler)
|
||||
if (addr < RAW_SPU_BASE_ADDR && vm::check_addr(addr) && rsx::g_access_violation_handler)
|
||||
{
|
||||
bool state_changed = false;
|
||||
|
||||
if (cpu)
|
||||
{
|
||||
vm::temporary_unlock(*cpu);
|
||||
state_changed = vm::temporary_unlock(*cpu);
|
||||
}
|
||||
|
||||
bool handled = rsx::g_access_violation_handler(addr, is_writing);
|
||||
|
||||
if (cpu && (cpu->state += cpu_flag::temp, cpu->test_stopped()))
|
||||
if (state_changed && (cpu->state += cpu_flag::temp, cpu->test_stopped()))
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -1536,14 +1530,14 @@ bool handle_access_violation(u32 addr, bool is_writing, ucontext_t* context) noe
|
||||
// If we fail due to being busy, wait a bit and try again.
|
||||
while (static_cast<u32>(sending_error) == CELL_EBUSY)
|
||||
{
|
||||
thread_ctrl::wait_for(1000);
|
||||
sending_error = sys_event_port_send(pf_port_id, data1, data2, data3);
|
||||
|
||||
if (cpu->is_stopped())
|
||||
{
|
||||
sending_error = {};
|
||||
break;
|
||||
}
|
||||
|
||||
thread_ctrl::wait_for(1000);
|
||||
sending_error = sys_event_port_send(pf_port_id, data1, data2, data3);
|
||||
}
|
||||
|
||||
if (sending_error)
|
||||
@@ -1644,7 +1638,7 @@ static void append_thread_name(std::string& msg)
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::append(msg, "Thread id = %s.\n", std::this_thread::get_id());
|
||||
fmt::append(msg, "Thread id = %u.\n", thread_ctrl::get_tid());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3139,6 +3133,8 @@ u64 thread_ctrl::get_tid()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return GetCurrentThreadId();
|
||||
#elif defined(__linux__)
|
||||
return syscall(SYS_gettid);
|
||||
#else
|
||||
return reinterpret_cast<u64>(pthread_self());
|
||||
#endif
|
||||
|
||||
+9
-1
@@ -635,13 +635,21 @@ public:
|
||||
// Join thread by thread_state::finished
|
||||
named_thread& operator=(thread_state s)
|
||||
{
|
||||
bool notify_sync = false;
|
||||
|
||||
if (s >= thread_state::aborting && thread::m_sync.fetch_op([](u64& v){ return !(v & 3) && (v |= 1); }).second)
|
||||
{
|
||||
notify_sync = true;
|
||||
}
|
||||
|
||||
if constexpr (std::is_assignable_v<Context&, thread_state>)
|
||||
{
|
||||
static_cast<Context&>(*this) = s;
|
||||
}
|
||||
|
||||
if (s >= thread_state::aborting && thread::m_sync.fetch_op([](u64& v){ return !(v & 3) && (v |= 1); }).second)
|
||||
if (notify_sync)
|
||||
{
|
||||
// Notify after context abortion has been made so all conditions for wake-up be satisfied by the time of notification
|
||||
thread::m_sync.notify_one(1);
|
||||
}
|
||||
|
||||
|
||||
+10
-11
@@ -1,37 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include "util/types.hpp"
|
||||
#include "util/vm.hpp"
|
||||
#include "StrFmt.h"
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
namespace utils
|
||||
{
|
||||
class address_range_vector;
|
||||
|
||||
|
||||
/**
|
||||
* Constexprs
|
||||
* Helpers
|
||||
*/
|
||||
constexpr inline u32 page_start(u32 addr)
|
||||
static inline u32 page_start(u32 addr)
|
||||
{
|
||||
return addr & ~4095u;
|
||||
return addr & ~(c_page_size - 1);
|
||||
}
|
||||
|
||||
constexpr inline u32 next_page(u32 addr)
|
||||
static inline u32 next_page(u32 addr)
|
||||
{
|
||||
return page_start(addr + 4096u);
|
||||
return page_start(addr) + c_page_size;
|
||||
}
|
||||
|
||||
constexpr inline u32 page_end(u32 addr)
|
||||
static inline u32 page_end(u32 addr)
|
||||
{
|
||||
return next_page(addr) - 1;
|
||||
}
|
||||
|
||||
constexpr inline u32 is_page_aligned(u32 addr)
|
||||
static inline u32 is_page_aligned(u32 val)
|
||||
{
|
||||
return page_start(addr) == addr;
|
||||
return (val & (c_page_size - 1)) == 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -186,7 +185,7 @@ namespace utils
|
||||
|
||||
bool is_page_range() const
|
||||
{
|
||||
return (valid() && start % 4096u == 0 && length() % 4096u == 0);
|
||||
return (valid() && is_page_aligned(start) && is_page_aligned(length()));
|
||||
}
|
||||
|
||||
address_range to_page_range() const
|
||||
|
||||
+2
-2
@@ -371,12 +371,12 @@ public:
|
||||
bool bit_test_reset(uint bit) = delete;
|
||||
bool bit_test_invert(uint bit) = delete;
|
||||
|
||||
bool all_of(bs_t arg)
|
||||
bool all_of(bs_t arg) const
|
||||
{
|
||||
return base::load().all_of(arg);
|
||||
}
|
||||
|
||||
bool none_of(bs_t arg)
|
||||
bool none_of(bs_t arg) const
|
||||
{
|
||||
return base::load().none_of(arg);
|
||||
}
|
||||
|
||||
@@ -700,6 +700,12 @@ template<typename T>
|
||||
struct size3_base
|
||||
{
|
||||
T width, height, depth;
|
||||
|
||||
template<typename NT>
|
||||
explicit constexpr operator size3_base<NT>() const
|
||||
{
|
||||
return{ static_cast<NT>(width), static_cast<NT>(height), static_cast<NT>(depth) };
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
|
||||
+29
-3
@@ -6,14 +6,27 @@ void shared_mutex::imp_lock_shared(u32 val)
|
||||
{
|
||||
ensure(val < c_err); // "shared_mutex underflow"
|
||||
|
||||
// Try to steal the notification bit
|
||||
if (val & c_sig && m_value.compare_exchange(val, val - c_sig + 1))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
busy_wait();
|
||||
|
||||
if (try_lock_shared())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const u32 old = m_value;
|
||||
|
||||
if (old & c_sig && m_value.compare_and_swap_test(old, old - c_sig + 1))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
busy_wait();
|
||||
}
|
||||
|
||||
// Acquire writer lock and downgrade
|
||||
@@ -75,11 +88,24 @@ void shared_mutex::imp_lock(u32 val)
|
||||
{
|
||||
ensure(val < c_err); // "shared_mutex underflow"
|
||||
|
||||
// Try to steal the notification bit
|
||||
if (val & c_sig && m_value.compare_exchange(val, val - c_sig + c_one))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
busy_wait();
|
||||
|
||||
if (!m_value && try_lock())
|
||||
const u32 old = m_value;
|
||||
|
||||
if (!old && try_lock())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (old & c_sig && m_value.compare_and_swap_test(old, old - c_sig + c_one))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -171,6 +171,11 @@ public:
|
||||
{
|
||||
return m_value.load() < c_one - 1;
|
||||
}
|
||||
|
||||
bool has_waiters() const
|
||||
{
|
||||
return m_value.load() > c_one;
|
||||
}
|
||||
};
|
||||
|
||||
// Simplified shared (reader) lock implementation.
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
#include "stdafx.h"
|
||||
#include "stack_trace.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
#define DBGHELP_TRANSLATE_TCHAR
|
||||
#include <DbgHelp.h>
|
||||
#include <codecvt>
|
||||
#else
|
||||
#include <execinfo.h>
|
||||
#endif
|
||||
|
||||
namespace utils
|
||||
{
|
||||
#ifdef _WIN32
|
||||
std::string wstr_to_utf8(LPWSTR data, int str_len)
|
||||
{
|
||||
if (!str_len)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
// Calculate size
|
||||
const auto length = WideCharToMultiByte(CP_UTF8, 0, data, str_len, NULL, 0, NULL, NULL);
|
||||
|
||||
// Convert
|
||||
std::vector<char> out(length + 1, 0);
|
||||
WideCharToMultiByte(CP_UTF8, 0, data, str_len, out.data(), length, NULL, NULL);
|
||||
return out.data();
|
||||
}
|
||||
|
||||
std::vector<void*> get_backtrace(int max_depth)
|
||||
{
|
||||
std::vector<void*> result = {};
|
||||
|
||||
const auto hProcess = ::GetCurrentProcess();
|
||||
const auto hThread = ::GetCurrentThread();
|
||||
|
||||
CONTEXT context{};
|
||||
RtlCaptureContext(&context);
|
||||
|
||||
STACKFRAME64 stack = {};
|
||||
stack.AddrPC.Offset = context.Rip;
|
||||
stack.AddrPC.Mode = AddrModeFlat;
|
||||
stack.AddrStack.Offset = context.Rsp;
|
||||
stack.AddrStack.Mode = AddrModeFlat;
|
||||
stack.AddrFrame.Offset = context.Rbp;
|
||||
stack.AddrFrame.Mode = AddrModeFlat;
|
||||
|
||||
while (max_depth--)
|
||||
{
|
||||
if (!StackWalk64(
|
||||
IMAGE_FILE_MACHINE_AMD64,
|
||||
hProcess,
|
||||
hThread,
|
||||
&stack,
|
||||
&context,
|
||||
NULL,
|
||||
SymFunctionTableAccess64,
|
||||
SymGetModuleBase64,
|
||||
NULL))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
result.push_back(reinterpret_cast<void*>(stack.AddrPC.Offset));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::string> get_backtrace_symbols(const std::vector<void*>& stack)
|
||||
{
|
||||
std::vector<std::string> result = {};
|
||||
std::vector<u8> symbol_buf(sizeof(SYMBOL_INFO) + sizeof(TCHAR) * 256);
|
||||
|
||||
const auto hProcess = ::GetCurrentProcess();
|
||||
|
||||
auto sym = reinterpret_cast<SYMBOL_INFO*>(symbol_buf.data());
|
||||
sym->SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||
sym->MaxNameLen = 256;
|
||||
|
||||
IMAGEHLP_LINEW64 line_info{};
|
||||
line_info.SizeOfStruct = sizeof(IMAGEHLP_LINEW64);
|
||||
|
||||
SymInitialize(hProcess, NULL, TRUE);
|
||||
SymSetOptions(SYMOPT_LOAD_LINES);
|
||||
|
||||
for (const auto& pointer : stack)
|
||||
{
|
||||
DWORD64 unused;
|
||||
SymFromAddr(hProcess, reinterpret_cast<DWORD64>(pointer), &unused, sym);
|
||||
|
||||
if (sym->NameLen)
|
||||
{
|
||||
const auto function_name = wstr_to_utf8(sym->Name, static_cast<int>(sym->NameLen));
|
||||
|
||||
// Attempt to get file and line information if available
|
||||
DWORD unused2;
|
||||
if (SymGetLineFromAddrW(hProcess, reinterpret_cast<DWORD64>(pointer), &unused2, &line_info))
|
||||
{
|
||||
const auto full_path = fmt::format("%s:%u %s", wstr_to_utf8(line_info.FileName, -1), line_info.LineNumber, function_name);
|
||||
result.push_back(full_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.push_back(function_name);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.push_back(fmt::format("rpcs3@0xp", pointer));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
#else
|
||||
std::vector<void*> get_backtrace(int max_depth)
|
||||
{
|
||||
std::vector<void*> result(max_depth);
|
||||
int depth = backtrace(result.data(), max_depth);
|
||||
|
||||
result.resize(depth);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::string> get_backtrace_symbols(const std::vector<void*>& stack)
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
result.reserve(stack.size());
|
||||
|
||||
const auto symbols = backtrace_symbols(stack.data(), static_cast<int>(stack.size()));
|
||||
for (usz i = 0; i < stack.size(); ++i)
|
||||
{
|
||||
result.push_back(symbols[i]);
|
||||
}
|
||||
|
||||
free(symbols);
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
#include <util/types.hpp>
|
||||
#include <util/logs.hpp>
|
||||
|
||||
namespace utils
|
||||
{
|
||||
std::vector<void*> get_backtrace(int max_depth = 255);
|
||||
std::vector<std::string> get_backtrace_symbols(const std::vector<void*>& stack);
|
||||
|
||||
FORCE_INLINE void print_trace(logs::channel& logger, int max_depth = 255)
|
||||
{
|
||||
const auto trace = get_backtrace(max_depth);
|
||||
const auto lines = get_backtrace_symbols(trace);
|
||||
|
||||
for (const auto& line : lines)
|
||||
{
|
||||
logger.error("%s", line);
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -99,13 +99,13 @@ inline int futex(volatile void* uaddr, int futex_op, uint val, const timespec* t
|
||||
|
||||
if (!timeout)
|
||||
{
|
||||
rec.cv.wait(lock, [&] { return !rec.mask; });
|
||||
rec.cv.wait(lock, FN(!rec.mask));
|
||||
}
|
||||
else if (futex_op == FUTEX_WAIT)
|
||||
{
|
||||
const auto nsec = std::chrono::nanoseconds(timeout->tv_nsec + timeout->tv_sec * 1000000000ull);
|
||||
|
||||
if (!rec.cv.wait_for(lock, nsec, [&] { return !rec.mask; }))
|
||||
if (!rec.cv.wait_for(lock, nsec, FN(!rec.mask)))
|
||||
{
|
||||
res = -1;
|
||||
errno = ETIMEDOUT;
|
||||
|
||||
@@ -3,6 +3,7 @@ YoRHa Theme for RPCS3
|
||||
by Ani @ https://github.com/AniLeo
|
||||
r1 (2018.02.27)
|
||||
r2 (2021.08.28)
|
||||
r3 (2022.08.20)
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -26,7 +27,7 @@ aea993
|
||||
|
||||
/* Every widget */
|
||||
QWidget {
|
||||
font-family: SCE-PS3 Rodin LATIN;
|
||||
font-family: "SCE-PS3 Rodin LATIN";
|
||||
font-size: 9.00pt;
|
||||
|
||||
color: #292929;
|
||||
@@ -98,6 +99,9 @@ QToolTip {
|
||||
}
|
||||
|
||||
/* Table headers */
|
||||
QHeaderView {
|
||||
color: #b3ac98;
|
||||
}
|
||||
QHeaderView::section {
|
||||
text-transform: uppercase;
|
||||
background: #4d4940;
|
||||
@@ -109,22 +113,32 @@ QHeaderView::section {
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* All other Tabs */
|
||||
/* All Tabs */
|
||||
QTabBar {
|
||||
border-bottom: 0.05em solid #4d4940;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
QTabBar::tab {
|
||||
background: transparent;
|
||||
width: 4.80em;
|
||||
padding-left: 0.50em;
|
||||
padding-right: 0.50em;
|
||||
padding-top: 0.25em;
|
||||
padding-bottom: 0.25em;
|
||||
padding-top: 0.65em;
|
||||
padding-bottom: 0.65em;
|
||||
margin-right: 0.25em;
|
||||
font-size: 10.5pt;
|
||||
font-weight: 550;
|
||||
}
|
||||
QTabBar::tab:last {
|
||||
margin-right: 0em;
|
||||
}
|
||||
QTabBar::tab:!selected:hover {
|
||||
background: transparent;
|
||||
color: #4d4940;
|
||||
}
|
||||
QTabBar::tab::selected {
|
||||
background: #4d4940;
|
||||
color: #aea993;
|
||||
border-bottom-style: solid;
|
||||
}
|
||||
|
||||
/* Checkboxes */
|
||||
@@ -153,29 +167,35 @@ QCheckBox::indicator::disabled, QRadioButton::indicator::disabled {
|
||||
background-color: #828790; /* Gray */
|
||||
}
|
||||
|
||||
/* Combo Boxes */
|
||||
QComboBox {
|
||||
/* Combo Boxes, Datetime dropdown */
|
||||
QComboBox, QDateTimeEdit, QLineEdit {
|
||||
background: transparent;
|
||||
color: #4d4940;
|
||||
border: 0.05em solid #1e1e1e;
|
||||
border-radius: 0.15em;
|
||||
padding-bottom: 0.2em;
|
||||
padding-left: 0.4em;
|
||||
padding-top: 0.1em;
|
||||
padding-bottom: 0.1em;
|
||||
}
|
||||
QComboBox QAbstractItemView {
|
||||
background: #b3ac98;
|
||||
}
|
||||
QComboBox::disabled {
|
||||
QComboBox::disabled, QDateTimeEdit::disabled, QLineEdit::disabled {
|
||||
background: #828790;
|
||||
color: #4d4940;
|
||||
}
|
||||
/* Text boxes need less left padding than combo boxes */
|
||||
QComboBox {
|
||||
padding-left: 0.4em;
|
||||
}
|
||||
QLineEdit {
|
||||
padding-left: 0.2em;
|
||||
}
|
||||
|
||||
/* Group Boxes (Settings Dialog) */
|
||||
QGroupBox {
|
||||
margin-top: 1em;
|
||||
border: 0.05em solid #4d4940;
|
||||
text-transform: uppercase;
|
||||
font-size: 9.00pt;
|
||||
}
|
||||
QGroupBox::title {
|
||||
subcontrol-origin: margin;
|
||||
@@ -236,6 +256,10 @@ QDockWidget::close-button, QDockWidget::float-button {
|
||||
QTabWidget::pane {
|
||||
border: 0em solid #4d4940;
|
||||
}
|
||||
/* Center tabs */
|
||||
QTabWidget::tab-bar {
|
||||
alignment: center;
|
||||
}
|
||||
|
||||
/* Top menu bar */
|
||||
QMenuBar {
|
||||
@@ -284,6 +308,12 @@ QToolButton::hover {
|
||||
background-color: #b3ac98;
|
||||
}
|
||||
|
||||
/* Calendar widget */
|
||||
QCalendarWidget QWidget{
|
||||
background: #b3ac98;
|
||||
color: #4d4940;
|
||||
}
|
||||
|
||||
|
||||
/*** RPCS3 Specifics ***/
|
||||
|
||||
@@ -303,40 +333,24 @@ QWidget#trophy_manager, QWidget#cg_disasm, QWidget#log_viewer, QMainWindow#main_
|
||||
border: 0.05em solid #4d4940;
|
||||
color: #292929;
|
||||
font-family: none;
|
||||
font-size: 9.50pt;
|
||||
font-size: 9.5pt;
|
||||
}
|
||||
|
||||
/* Pad Settings, Emulator Settings Dialogs: Tabs */
|
||||
#pad_settings_dialog QTabWidget::tab-bar {
|
||||
alignment: center;
|
||||
/* Align the log widget tabs to the left */
|
||||
QTabWidget#tab_widget_log::tab-bar {
|
||||
alignment: left;
|
||||
}
|
||||
QTabBar#tab_bar_settings, #pad_settings_dialog QTabBar {
|
||||
border-bottom: 0.05em solid #4d4940;
|
||||
text-transform: uppercase;
|
||||
|
||||
/* Restore log section QTabBar to small sizes */
|
||||
QTabBar#tab_bar_log {
|
||||
border-bottom: 0;
|
||||
}
|
||||
QTabBar::tab#tab_bar_settings, #pad_settings_dialog QTabBar::tab {
|
||||
background: transparent;
|
||||
width: 5.20em;
|
||||
padding-left: 0.50em;
|
||||
padding-right: 0.50em;
|
||||
padding-top: 0.65em;
|
||||
padding-bottom: 0.65em;
|
||||
margin-right: 0.25em;
|
||||
font-size: 10.5pt;
|
||||
font-weight: 550;
|
||||
}
|
||||
QTabBar::tab:last#tab_bar_settings, #pad_settings_dialog QTabBar::tab:last {
|
||||
margin-right: 0em;
|
||||
}
|
||||
QTabBar::tab:!selected:hover#tab_bar_settings, #pad_settings_dialog QTabBar::tab:!selected:hover {
|
||||
background: transparent;
|
||||
color: #4d4940;
|
||||
}
|
||||
QTabBar::tab::selected#tab_bar_settings, #pad_settings_dialog QTabBar::tab::selected {
|
||||
background: #4d4940;
|
||||
color: #aea993;
|
||||
border-bottom-style: solid;
|
||||
margin-top: 0.15em;
|
||||
QTabBar#tab_bar_log::tab {
|
||||
width: 2.00em;
|
||||
padding: 0.25em;
|
||||
font-size: 9.0pt;
|
||||
font-weight: 400;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
/* Pad Settings: Controller Image */
|
||||
@@ -362,6 +376,7 @@ QLineEdit#mw_searchbar {
|
||||
margin-left: 0.7em;
|
||||
color: #4d4940;
|
||||
font-size: 10.25pt;
|
||||
border: 0em;
|
||||
}
|
||||
|
||||
/* Set Theme UI colors */
|
||||
@@ -440,3 +455,11 @@ QWidget#trophy_notification_frame {
|
||||
QSlider#sizeSlider::groove:horizontal {
|
||||
height: 1.5em;
|
||||
}
|
||||
|
||||
/*
|
||||
On Patch Creator, it's the patch validation label
|
||||
since we can't avoid repainting color, let's bold it instead
|
||||
*/
|
||||
QLabel#validLabel {
|
||||
font-weight: 600;
|
||||
}
|
||||
Binary file not shown.
@@ -26,11 +26,17 @@ struct loaded_npdrm_keys
|
||||
}
|
||||
|
||||
// TODO: Check if correct for ELF files usage
|
||||
u128 last_key() const
|
||||
u128 last_key(usz backwards = 0) const
|
||||
{
|
||||
backwards++;
|
||||
const usz pos = dec_keys_pos;
|
||||
return pos ? dec_keys[(pos - 1) % std::size(dec_keys)].load() : u128{};
|
||||
return pos >= backwards ? dec_keys[(pos - backwards) % std::size(dec_keys)].load() : u128{};
|
||||
}
|
||||
|
||||
SAVESTATE_INIT_POS(2);
|
||||
loaded_npdrm_keys() = default;
|
||||
loaded_npdrm_keys(utils::serial& ar);
|
||||
void save(utils::serial& ar);
|
||||
};
|
||||
|
||||
struct NPD_HEADER
|
||||
|
||||
+3
-1
@@ -53,11 +53,13 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Emu\Audio\Cubeb\CubebBackend.h" />
|
||||
<ClInclude Include="Emu\Audio\Cubeb\cubeb_enumerator.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Emu\Audio\Cubeb\CubebBackend.cpp" />
|
||||
<ClCompile Include="Emu\Audio\Cubeb\cubeb_enumerator.cpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -10,10 +10,16 @@
|
||||
<ClCompile Include="Emu\Audio\Cubeb\CubebBackend.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Emu\Audio\Cubeb\cubeb_enumerator.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Emu\Audio\Cubeb\CubebBackend.h">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Emu\Audio\Cubeb\cubeb_enumerator.h">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -6,10 +6,16 @@
|
||||
|
||||
AudioBackend::AudioBackend() {}
|
||||
|
||||
void AudioBackend::SetErrorCallback(std::function<void()> cb)
|
||||
void AudioBackend::SetWriteCallback(std::function<u32(u32 /* byte_cnt */, void* /* buffer */)> cb)
|
||||
{
|
||||
std::lock_guard lock(m_error_cb_mutex);
|
||||
m_error_callback = cb;
|
||||
std::lock_guard lock(m_cb_mutex);
|
||||
m_write_callback = cb;
|
||||
}
|
||||
|
||||
void AudioBackend::SetStateCallback(std::function<void(AudioStateEvent)> cb)
|
||||
{
|
||||
std::lock_guard lock(m_state_cb_mutex);
|
||||
m_state_callback = cb;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -134,3 +140,24 @@ AudioChannelCnt AudioBackend::get_max_channel_count(u32 device_index)
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
AudioChannelCnt AudioBackend::convert_channel_count(u64 raw)
|
||||
{
|
||||
switch (raw)
|
||||
{
|
||||
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("Usupported channel count");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,12 @@ enum class AudioChannelCnt : u32
|
||||
SURROUND_7_1 = 8,
|
||||
};
|
||||
|
||||
enum class AudioStateEvent : u32
|
||||
{
|
||||
UNSPECIFIED_ERROR,
|
||||
DEFAULT_DEVICE_CHANGED,
|
||||
};
|
||||
|
||||
class AudioBackend
|
||||
{
|
||||
public:
|
||||
@@ -60,19 +66,21 @@ public:
|
||||
virtual std::string_view GetName() const = 0;
|
||||
|
||||
// (Re)create output stream with new parameters. Blocks until data callback returns.
|
||||
// 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(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) = 0;
|
||||
|
||||
// Reset backend state. Blocks until data callback returns.
|
||||
virtual void Close() = 0;
|
||||
|
||||
// Sets write callback. It's called when backend requests new data to be sent.
|
||||
// Callback should return number of submitted bytes. Calling other backend functions from callback is unsafe.
|
||||
virtual void SetWriteCallback(std::function<u32(u32 /* byte_cnt */, void* /* buffer */)> cb) = 0;
|
||||
virtual void SetWriteCallback(std::function<u32(u32 /* byte_cnt */, void* /* buffer */)> cb);
|
||||
|
||||
// Sets error callback. It's called when backend detects uncorrectable error condition in audio chain.
|
||||
// Sets error callback. It's called when backend detects event in audio chain that needs immediate attention.
|
||||
// Calling other backend functions from callback is unsafe.
|
||||
virtual void SetErrorCallback(std::function<void()> cb);
|
||||
virtual void SetStateCallback(std::function<void(AudioStateEvent)> cb);
|
||||
|
||||
/*
|
||||
* All functions below require that Open() was called prior.
|
||||
@@ -101,6 +109,11 @@ public:
|
||||
*/
|
||||
virtual bool Operational() { return true; }
|
||||
|
||||
/*
|
||||
* This virtual method should be reimplemented if backend can report device changes
|
||||
*/
|
||||
virtual bool DefaultDeviceChanged() { return false; }
|
||||
|
||||
/*
|
||||
* Helper methods
|
||||
*/
|
||||
@@ -145,6 +158,11 @@ public:
|
||||
*/
|
||||
static AudioChannelCnt get_max_channel_count(u32 device_index);
|
||||
|
||||
/*
|
||||
* Converts raw channel count to value usable by backends
|
||||
*/
|
||||
static AudioChannelCnt convert_channel_count(u64 raw);
|
||||
|
||||
/*
|
||||
* Downmix audio stream.
|
||||
*/
|
||||
@@ -208,8 +226,11 @@ protected:
|
||||
AudioFreq m_sampling_rate = AudioFreq::FREQ_48K;
|
||||
AudioChannelCnt m_channels = AudioChannelCnt::STEREO;
|
||||
|
||||
shared_mutex m_error_cb_mutex{};
|
||||
std::function<void()> m_error_callback{};
|
||||
shared_mutex m_cb_mutex{};
|
||||
std::function<u32(u32, void *)> m_write_callback{};
|
||||
|
||||
shared_mutex m_state_cb_mutex{};
|
||||
std::function<void(AudioStateEvent)> m_state_callback{};
|
||||
|
||||
bool m_playing = false;
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include "util/logs.hpp"
|
||||
#include "Emu/Audio/audio_device_enumerator.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
@@ -15,8 +16,7 @@ CubebBackend::CubebBackend()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// Cubeb requires COM to be initialized on the thread calling cubeb_init on Windows
|
||||
HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
||||
if (SUCCEEDED(hr))
|
||||
if (HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED); SUCCEEDED(hr))
|
||||
{
|
||||
m_com_init_success = true;
|
||||
}
|
||||
@@ -24,11 +24,20 @@ CubebBackend::CubebBackend()
|
||||
|
||||
if (int err = cubeb_init(&m_ctx, "RPCS3", nullptr))
|
||||
{
|
||||
Cubeb.error("cubeb_init() failed: 0x%08x", err);
|
||||
Cubeb.error("cubeb_init() failed: %i", err);
|
||||
m_ctx = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
if (int err = cubeb_register_device_collection_changed(m_ctx, CUBEB_DEVICE_TYPE_OUTPUT, device_collection_changed_cb, this))
|
||||
{
|
||||
Cubeb.error("cubeb_register_device_collection_changed() failed: %i", err);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_dev_collection_cb_enabled = true;
|
||||
}
|
||||
|
||||
Cubeb.notice("Using backend %s", cubeb_get_backend_id(m_ctx));
|
||||
}
|
||||
|
||||
@@ -36,6 +45,14 @@ CubebBackend::~CubebBackend()
|
||||
{
|
||||
Close();
|
||||
|
||||
if (m_dev_collection_cb_enabled)
|
||||
{
|
||||
if (int err = cubeb_register_device_collection_changed(m_ctx, CUBEB_DEVICE_TYPE_OUTPUT, nullptr, nullptr))
|
||||
{
|
||||
Cubeb.error("cubeb_register_device_collection_changed() failed: %i", err);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_ctx)
|
||||
{
|
||||
cubeb_destroy(m_ctx);
|
||||
@@ -56,11 +73,16 @@ bool CubebBackend::Initialized()
|
||||
|
||||
bool CubebBackend::Operational()
|
||||
{
|
||||
std::lock_guard lock(m_error_cb_mutex);
|
||||
return m_stream != nullptr && !m_reset_req;
|
||||
return m_stream != nullptr && !m_reset_req.observe();
|
||||
}
|
||||
|
||||
bool CubebBackend::Open(AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt)
|
||||
bool CubebBackend::DefaultDeviceChanged()
|
||||
{
|
||||
std::lock_guard lock{m_dev_sw_mutex};
|
||||
return !m_reset_req.observe() && m_default_dev_changed;
|
||||
}
|
||||
|
||||
bool CubebBackend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt)
|
||||
{
|
||||
if (!Initialized())
|
||||
{
|
||||
@@ -69,11 +91,39 @@ bool CubebBackend::Open(AudioFreq freq, AudioSampleSize sample_size, AudioChanne
|
||||
}
|
||||
|
||||
std::lock_guard lock(m_cb_mutex);
|
||||
std::lock_guard dev_sw_lock{m_dev_sw_mutex};
|
||||
CloseUnlocked();
|
||||
|
||||
const bool use_default_device = dev_id.empty() || dev_id == audio_device_enumerator::DEFAULT_DEV_ID;
|
||||
auto [dev_handle, dev_ident, dev_ch_cnt] = GetDevice(use_default_device ? "" : dev_id);
|
||||
|
||||
if (!dev_handle)
|
||||
{
|
||||
if (use_default_device)
|
||||
{
|
||||
std::tie(dev_handle, dev_ident, dev_ch_cnt) = GetDefaultDeviceAlt(freq, sample_size, ch_cnt);
|
||||
|
||||
if (!dev_handle)
|
||||
{
|
||||
Cubeb.error("Cannot detect default device. Channel count detection unavailable.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Cubeb.error("Device with id=%s not found", dev_id);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (dev_ch_cnt == 0)
|
||||
{
|
||||
Cubeb.error("Device reported invalid channel count, using stereo instead");
|
||||
dev_ch_cnt = 2;
|
||||
}
|
||||
|
||||
m_sampling_rate = freq;
|
||||
m_sample_size = sample_size;
|
||||
m_channels = ch_cnt;
|
||||
m_channels = static_cast<AudioChannelCnt>(std::min(static_cast<u32>(convert_channel_count(dev_ch_cnt)), static_cast<u32>(ch_cnt)));
|
||||
full_sample_size = get_channels() * get_sample_size();
|
||||
|
||||
cubeb_stream_params stream_param{};
|
||||
@@ -82,46 +132,50 @@ bool CubebBackend::Open(AudioFreq freq, AudioSampleSize sample_size, AudioChanne
|
||||
stream_param.channels = get_channels();
|
||||
stream_param.layout = [&]()
|
||||
{
|
||||
switch (ch_cnt)
|
||||
switch (m_channels)
|
||||
{
|
||||
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:
|
||||
ensure(false);
|
||||
return CUBEB_LAYOUT_UNDEFINED;
|
||||
fmt::throw_exception("Invalid audio channel count");
|
||||
}
|
||||
}();
|
||||
stream_param.prefs = m_dev_collection_cb_enabled && dev_handle ? CUBEB_STREAM_PREF_DISABLE_DEVICE_SWITCHING : CUBEB_STREAM_PREF_NONE;
|
||||
|
||||
u32 min_latency{};
|
||||
if (int err = cubeb_get_min_latency(m_ctx, &stream_param, &min_latency))
|
||||
{
|
||||
Cubeb.error("cubeb_get_min_latency() failed: 0x%08x", err);
|
||||
Cubeb.error("cubeb_get_min_latency() failed: %i", err);
|
||||
min_latency = 0;
|
||||
}
|
||||
|
||||
const u32 stream_latency = std::max(static_cast<u32>(AUDIO_MIN_LATENCY * get_sampling_rate()), min_latency);
|
||||
if (int err = cubeb_stream_init(m_ctx, &m_stream, "Main stream", nullptr, nullptr, nullptr, &stream_param, stream_latency, data_cb, state_cb, this))
|
||||
|
||||
if (int err = cubeb_stream_init(m_ctx, &m_stream, "Main stream", nullptr, nullptr, dev_handle, &stream_param, stream_latency, data_cb, state_cb, this))
|
||||
{
|
||||
Cubeb.error("cubeb_stream_init() failed: 0x%08x", err);
|
||||
Cubeb.error("cubeb_stream_init() failed: %i", err);
|
||||
m_stream = nullptr;
|
||||
}
|
||||
else if (int err = cubeb_stream_start(m_stream))
|
||||
{
|
||||
Cubeb.error("cubeb_stream_start() failed: 0x%08x", err);
|
||||
CloseUnlocked();
|
||||
}
|
||||
else if (int err = cubeb_stream_set_volume(m_stream, 1.0))
|
||||
{
|
||||
Cubeb.error("cubeb_stream_set_volume() failed: 0x%08x", err);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_stream == nullptr)
|
||||
if (int err = cubeb_stream_start(m_stream))
|
||||
{
|
||||
Cubeb.error("Failed to open audio backend. Make sure that no other application is running that might block audio access (e.g. Netflix).");
|
||||
Cubeb.error("cubeb_stream_start() failed: %i", err);
|
||||
CloseUnlocked();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (int err = cubeb_stream_set_volume(m_stream, 1.0))
|
||||
{
|
||||
Cubeb.error("cubeb_stream_set_volume() failed: %i", err);
|
||||
}
|
||||
|
||||
if (use_default_device)
|
||||
{
|
||||
m_current_device = dev_ident;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -131,7 +185,7 @@ void CubebBackend::CloseUnlocked()
|
||||
{
|
||||
if (int err = cubeb_stream_stop(m_stream))
|
||||
{
|
||||
Cubeb.error("cubeb_stream_stop() failed: 0x%08x", err);
|
||||
Cubeb.error("cubeb_stream_stop() failed: %i", err);
|
||||
}
|
||||
|
||||
cubeb_stream_destroy(m_stream);
|
||||
@@ -140,11 +194,15 @@ void CubebBackend::CloseUnlocked()
|
||||
|
||||
m_playing = false;
|
||||
m_last_sample.fill(0);
|
||||
|
||||
m_default_dev_changed = false;
|
||||
m_current_device.clear();
|
||||
}
|
||||
|
||||
void CubebBackend::Close()
|
||||
{
|
||||
std::lock_guard lock(m_cb_mutex);
|
||||
std::lock_guard dev_sw_lock{m_dev_sw_mutex};
|
||||
CloseUnlocked();
|
||||
}
|
||||
|
||||
@@ -177,12 +235,6 @@ void CubebBackend::Pause()
|
||||
m_last_sample.fill(0);
|
||||
}
|
||||
|
||||
void CubebBackend::SetWriteCallback(std::function<u32(u32, void *)> cb)
|
||||
{
|
||||
std::lock_guard lock(m_cb_mutex);
|
||||
m_write_callback = cb;
|
||||
}
|
||||
|
||||
f64 CubebBackend::GetCallbackFrameLen()
|
||||
{
|
||||
if (m_stream == nullptr)
|
||||
@@ -194,19 +246,130 @@ f64 CubebBackend::GetCallbackFrameLen()
|
||||
u32 stream_latency{};
|
||||
if (int err = cubeb_stream_get_latency(m_stream, &stream_latency))
|
||||
{
|
||||
Cubeb.error("cubeb_stream_get_latency() failed: 0x%08x", err);
|
||||
Cubeb.error("cubeb_stream_get_latency() failed: %i", err);
|
||||
stream_latency = 0;
|
||||
}
|
||||
|
||||
return std::max<f64>(AUDIO_MIN_LATENCY, static_cast<f64>(stream_latency) / get_sampling_rate());
|
||||
}
|
||||
|
||||
std::tuple<cubeb_devid, std::string, u32> CubebBackend::GetDevice(std::string_view dev_id)
|
||||
{
|
||||
const bool default_dev = dev_id.empty();
|
||||
|
||||
cubeb_device_collection dev_collection{};
|
||||
if (int err = cubeb_enumerate_devices(m_ctx, CUBEB_DEVICE_TYPE_OUTPUT, &dev_collection))
|
||||
{
|
||||
Cubeb.error("cubeb_enumerate_devices() failed: %i", err);
|
||||
return {};
|
||||
}
|
||||
|
||||
if (dev_collection.count == 0)
|
||||
{
|
||||
Cubeb.error("No output devices available");
|
||||
if (int err = cubeb_device_collection_destroy(m_ctx, &dev_collection))
|
||||
{
|
||||
Cubeb.error("cubeb_device_collection_destroy() failed: %i", err);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::tuple<cubeb_devid, std::string, u32> result{};
|
||||
|
||||
for (u64 dev_idx = 0; dev_idx < dev_collection.count; dev_idx++)
|
||||
{
|
||||
const cubeb_device_info& dev_info = dev_collection.device[dev_idx];
|
||||
const std::string dev_ident{dev_info.device_id};
|
||||
|
||||
if (dev_ident.empty())
|
||||
{
|
||||
Cubeb.error("device_id is missing from device");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (default_dev)
|
||||
{
|
||||
if (dev_info.preferred & CUBEB_DEVICE_PREF_MULTIMEDIA)
|
||||
{
|
||||
result = {dev_info.devid, dev_ident, dev_info.max_channels};
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (dev_ident == dev_id)
|
||||
{
|
||||
result = {dev_info.devid, dev_ident, dev_info.max_channels};
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (int err = cubeb_device_collection_destroy(m_ctx, &dev_collection))
|
||||
{
|
||||
Cubeb.error("cubeb_device_collection_destroy() failed: %i", err);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
std::tuple<cubeb_devid, std::string, u32> CubebBackend::GetDefaultDeviceAlt(AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt)
|
||||
{
|
||||
cubeb_stream_params param =
|
||||
{
|
||||
.format = sample_size == AudioSampleSize::S16 ? CUBEB_SAMPLE_S16NE : CUBEB_SAMPLE_FLOAT32NE,
|
||||
.rate = static_cast<u32>(freq),
|
||||
.channels = static_cast<u32>(ch_cnt),
|
||||
.layout = CUBEB_LAYOUT_UNDEFINED,
|
||||
.prefs = CUBEB_STREAM_PREF_DISABLE_DEVICE_SWITCHING
|
||||
};
|
||||
|
||||
u32 min_latency{};
|
||||
if (int err = cubeb_get_min_latency(m_ctx, ¶m, &min_latency))
|
||||
{
|
||||
Cubeb.error("cubeb_get_min_latency() failed: %i", err);
|
||||
min_latency = 100;
|
||||
}
|
||||
|
||||
cubeb_stream* tmp_stream{};
|
||||
static auto dummy_data_cb = [](cubeb_stream*, void*, void const*, void*, long) -> long { return 0; };
|
||||
static auto dummy_state_cb = [](cubeb_stream*, void*, cubeb_state) {};
|
||||
|
||||
if (int err = cubeb_stream_init(m_ctx, &tmp_stream, "Default device detector", nullptr, nullptr, nullptr, ¶m, min_latency, dummy_data_cb, dummy_state_cb, nullptr))
|
||||
{
|
||||
Cubeb.error("cubeb_stream_init() failed: %i", err);
|
||||
return {};
|
||||
}
|
||||
|
||||
cubeb_device* crnt_dev{};
|
||||
|
||||
if (int err = cubeb_stream_get_current_device(tmp_stream, &crnt_dev))
|
||||
{
|
||||
Cubeb.error("cubeb_stream_get_current_device() failed: %i", err);
|
||||
cubeb_stream_destroy(tmp_stream);
|
||||
return {};
|
||||
}
|
||||
|
||||
const std::string out_dev_name{crnt_dev->output_name};
|
||||
|
||||
if (int err = cubeb_stream_device_destroy(tmp_stream, crnt_dev))
|
||||
{
|
||||
Cubeb.error("cubeb_stream_device_destroy() failed: %i", err);
|
||||
}
|
||||
|
||||
cubeb_stream_destroy(tmp_stream);
|
||||
|
||||
if (out_dev_name.empty())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
return GetDevice(out_dev_name);
|
||||
}
|
||||
|
||||
long CubebBackend::data_cb(cubeb_stream* /* stream */, void* user_ptr, void const* /* input_buffer */, void* output_buffer, long nframes)
|
||||
{
|
||||
CubebBackend* const cubeb = static_cast<CubebBackend*>(user_ptr);
|
||||
std::unique_lock lock(cubeb->m_cb_mutex, std::defer_lock);
|
||||
|
||||
if (nframes && lock.try_lock() && cubeb->m_write_callback && cubeb->m_playing)
|
||||
if (nframes && !cubeb->m_reset_req.observe() && lock.try_lock() && cubeb->m_write_callback && cubeb->m_playing)
|
||||
{
|
||||
const u32 sample_size = cubeb->full_sample_size.observe();
|
||||
const u32 bytes_req = nframes * sample_size;
|
||||
@@ -241,12 +404,51 @@ void CubebBackend::state_cb(cubeb_stream* /* stream */, void* user_ptr, cubeb_st
|
||||
{
|
||||
Cubeb.error("Stream entered error state");
|
||||
|
||||
std::lock_guard lock(cubeb->m_error_cb_mutex);
|
||||
cubeb->m_reset_req = true;
|
||||
std::lock_guard lock(cubeb->m_state_cb_mutex);
|
||||
|
||||
if (cubeb->m_error_callback)
|
||||
if (!cubeb->m_reset_req.test_and_set() && cubeb->m_state_callback)
|
||||
{
|
||||
cubeb->m_error_callback();
|
||||
cubeb->m_state_callback(AudioStateEvent::UNSPECIFIED_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CubebBackend::device_collection_changed_cb(cubeb* /* context */, void* user_ptr)
|
||||
{
|
||||
CubebBackend* const cubeb = static_cast<CubebBackend*>(user_ptr);
|
||||
|
||||
Cubeb.notice("Device collection changed");
|
||||
std::lock_guard lock{cubeb->m_dev_sw_mutex};
|
||||
|
||||
// Non default device is used (or default device cannot be detected)
|
||||
if (cubeb->m_current_device.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto [handle, dev_id, ch_cnt] = cubeb->GetDevice();
|
||||
if (!handle)
|
||||
{
|
||||
std::tie(handle, dev_id, ch_cnt) = cubeb->GetDefaultDeviceAlt(cubeb->m_sampling_rate, cubeb->m_sample_size, cubeb->m_channels);
|
||||
}
|
||||
|
||||
std::lock_guard cb_lock{cubeb->m_state_cb_mutex};
|
||||
|
||||
if (!handle)
|
||||
{
|
||||
// No devices available
|
||||
if (!cubeb->m_reset_req.test_and_set() && cubeb->m_state_callback)
|
||||
{
|
||||
cubeb->m_state_callback(AudioStateEvent::UNSPECIFIED_ERROR);
|
||||
}
|
||||
}
|
||||
else if (!cubeb->m_reset_req.observe() && dev_id != cubeb->m_current_device)
|
||||
{
|
||||
cubeb->m_default_dev_changed = true;
|
||||
|
||||
if (cubeb->m_state_callback)
|
||||
{
|
||||
cubeb->m_state_callback(AudioStateEvent::DEFAULT_DEVICE_CHANGED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,11 +20,11 @@ public:
|
||||
|
||||
bool Initialized() override;
|
||||
bool Operational() override;
|
||||
bool DefaultDeviceChanged() override;
|
||||
|
||||
bool Open(AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt) override;
|
||||
bool Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt) override;
|
||||
void Close() override;
|
||||
|
||||
void SetWriteCallback(std::function<u32(u32, void *)> cb) override;
|
||||
f64 GetCallbackFrameLen() override;
|
||||
|
||||
void Play() override;
|
||||
@@ -39,16 +39,24 @@ private:
|
||||
bool m_com_init_success = false;
|
||||
#endif
|
||||
|
||||
shared_mutex m_cb_mutex{};
|
||||
std::function<u32(u32, void *)> m_write_callback{};
|
||||
std::array<u8, sizeof(float) * static_cast<u32>(AudioChannelCnt::SURROUND_7_1)> m_last_sample{};
|
||||
atomic_t<u8> full_sample_size = 0;
|
||||
|
||||
bool m_reset_req = false;
|
||||
atomic_t<bool> m_reset_req = false;
|
||||
|
||||
shared_mutex m_dev_sw_mutex{};
|
||||
std::string m_current_device{};
|
||||
bool m_default_dev_changed = false;
|
||||
|
||||
bool m_dev_collection_cb_enabled = false;
|
||||
|
||||
// Cubeb callbacks
|
||||
static long data_cb(cubeb_stream* stream, void* user_ptr, void const* input_buffer, void* output_buffer, long nframes);
|
||||
static void state_cb(cubeb_stream* stream, void* user_ptr, cubeb_state state);
|
||||
static void device_collection_changed_cb(cubeb* context, void* user_ptr);
|
||||
|
||||
void CloseUnlocked();
|
||||
|
||||
std::tuple<cubeb_devid, std::string /* dev_ident */, u32 /* ch_cnt */> GetDevice(std::string_view dev_id = "");
|
||||
std::tuple<cubeb_devid, std::string /* dev_ident */, u32 /* ch_cnt */> GetDefaultDeviceAlt(AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
#include "Emu/Audio/Cubeb/cubeb_enumerator.h"
|
||||
#include "util/logs.hpp"
|
||||
#include <algorithm>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
#include <system_error>
|
||||
#endif
|
||||
|
||||
LOG_CHANNEL(cubeb_dev_enum);
|
||||
|
||||
cubeb_enumerator::cubeb_enumerator() : audio_device_enumerator()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// Cubeb requires COM to be initialized on the thread calling cubeb_init on Windows
|
||||
HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
com_init_success = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (int err = cubeb_init(&ctx, "RPCS3 device enumeration", nullptr))
|
||||
{
|
||||
cubeb_dev_enum.error("cubeb_init() failed: %i", err);
|
||||
ctx = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
cubeb_enumerator::~cubeb_enumerator()
|
||||
{
|
||||
if (ctx)
|
||||
{
|
||||
cubeb_destroy(ctx);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
if (com_init_success)
|
||||
{
|
||||
CoUninitialize();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
std::vector<audio_device_enumerator::audio_device> cubeb_enumerator::get_output_devices()
|
||||
{
|
||||
if (ctx == nullptr)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
cubeb_device_collection dev_collection{};
|
||||
if (int err = cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_OUTPUT, &dev_collection))
|
||||
{
|
||||
cubeb_dev_enum.error("cubeb_enumerate_devices() failed: %i", err);
|
||||
return {};
|
||||
}
|
||||
|
||||
if (dev_collection.count == 0)
|
||||
{
|
||||
cubeb_dev_enum.error("No output devices available");
|
||||
if (int err = cubeb_device_collection_destroy(ctx, &dev_collection))
|
||||
{
|
||||
cubeb_dev_enum.error("cubeb_device_collection_destroy() failed: %i", err);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<audio_device> device_list{};
|
||||
|
||||
for (u64 dev_idx = 0; dev_idx < dev_collection.count; dev_idx++)
|
||||
{
|
||||
const cubeb_device_info& dev_info = dev_collection.device[dev_idx];
|
||||
|
||||
if (dev_info.state == CUBEB_DEVICE_STATE_UNPLUGGED)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
audio_device dev =
|
||||
{
|
||||
.id = std::string{dev_info.device_id},
|
||||
.name = std::string{dev_info.friendly_name},
|
||||
.max_ch = dev_info.max_channels
|
||||
};
|
||||
|
||||
if (dev.id.empty())
|
||||
{
|
||||
cubeb_dev_enum.warning("Empty device id - skipping");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (dev.name.empty())
|
||||
{
|
||||
dev.name = dev.id;
|
||||
}
|
||||
|
||||
cubeb_dev_enum.notice("Found device: id=%s, name=%s, max_ch=%d", dev.id, dev.name, dev.max_ch);
|
||||
device_list.emplace_back(dev);
|
||||
}
|
||||
|
||||
if (int err = cubeb_device_collection_destroy(ctx, &dev_collection))
|
||||
{
|
||||
cubeb_dev_enum.error("cubeb_device_collection_destroy() failed: %i", err);
|
||||
}
|
||||
|
||||
std::sort(device_list.begin(), device_list.end(), [](audio_device_enumerator::audio_device a, audio_device_enumerator::audio_device b)
|
||||
{
|
||||
return a.name < b.name;
|
||||
});
|
||||
|
||||
return device_list;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "Emu/Audio/audio_device_enumerator.h"
|
||||
|
||||
#include "cubeb/cubeb.h"
|
||||
|
||||
class cubeb_enumerator final : public audio_device_enumerator
|
||||
{
|
||||
public:
|
||||
|
||||
cubeb_enumerator();
|
||||
~cubeb_enumerator() override;
|
||||
|
||||
std::vector<audio_device> get_output_devices() override;
|
||||
|
||||
private:
|
||||
|
||||
cubeb* ctx{};
|
||||
#ifdef _WIN32
|
||||
bool com_init_success = false;
|
||||
#endif
|
||||
};
|
||||
@@ -6,6 +6,8 @@
|
||||
#include "FAudioBackend.h"
|
||||
#include "Emu/system_config.h"
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/Audio/audio_device_enumerator.h"
|
||||
#include "Utilities/StrUtil.h"
|
||||
|
||||
LOG_CHANNEL(FAudio_, "FAudio");
|
||||
|
||||
@@ -117,11 +119,10 @@ bool FAudioBackend::Initialized()
|
||||
|
||||
bool FAudioBackend::Operational()
|
||||
{
|
||||
std::lock_guard lock(m_error_cb_mutex);
|
||||
return m_source_voice != nullptr && !m_reset_req;
|
||||
return m_source_voice != nullptr && !m_reset_req.observe();
|
||||
}
|
||||
|
||||
bool FAudioBackend::Open(AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt)
|
||||
bool FAudioBackend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt)
|
||||
{
|
||||
if (!Initialized())
|
||||
{
|
||||
@@ -132,9 +133,37 @@ bool FAudioBackend::Open(AudioFreq freq, AudioSampleSize sample_size, AudioChann
|
||||
std::lock_guard lock(m_cb_mutex);
|
||||
CloseUnlocked();
|
||||
|
||||
const bool use_default_dev = dev_id.empty() || dev_id == audio_device_enumerator::DEFAULT_DEV_ID;
|
||||
u64 devid{};
|
||||
if (!use_default_dev)
|
||||
{
|
||||
if (!try_to_uint64(&devid, dev_id, 0, UINT32_MAX))
|
||||
{
|
||||
FAudio_.error("Invalid device id - %s", dev_id);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (u32 res = FAudio_CreateMasteringVoice(m_instance, &m_master_voice, FAUDIO_DEFAULT_CHANNELS, FAUDIO_DEFAULT_SAMPLERATE, 0, static_cast<u32>(devid), nullptr))
|
||||
{
|
||||
FAudio_.error("FAudio_CreateMasteringVoice() failed(0x%08x)", res);
|
||||
m_master_voice = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
FAudioVoiceDetails vd{};
|
||||
FAudioVoice_GetVoiceDetails(m_master_voice, &vd);
|
||||
|
||||
if (vd.InputChannels == 0)
|
||||
{
|
||||
FAudio_.error("Channel count of 0 is invalid");
|
||||
CloseUnlocked();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_sampling_rate = freq;
|
||||
m_sample_size = sample_size;
|
||||
m_channels = ch_cnt;
|
||||
m_channels = static_cast<AudioChannelCnt>(std::min(static_cast<u32>(convert_channel_count(vd.InputChannels)), static_cast<u32>(ch_cnt)));;
|
||||
|
||||
FAudioWaveFormatEx waveformatex;
|
||||
waveformatex.wFormatTag = get_convert_to_s16() ? FAUDIO_FORMAT_PCM : FAUDIO_FORMAT_IEEE_FLOAT;
|
||||
@@ -153,43 +182,30 @@ bool FAudioBackend::Open(AudioFreq freq, AudioSampleSize sample_size, AudioChann
|
||||
OnLoopEnd = nullptr;
|
||||
OnVoiceError = nullptr;
|
||||
|
||||
if (u32 res = FAudio_CreateMasteringVoice(m_instance, &m_master_voice, FAUDIO_DEFAULT_CHANNELS, FAUDIO_DEFAULT_SAMPLERATE, 0, 0, nullptr))
|
||||
{
|
||||
FAudio_.error("FAudio_CreateMasteringVoice() failed(0x%08x)", res);
|
||||
m_master_voice = nullptr;
|
||||
}
|
||||
else if (u32 res = FAudio_CreateSourceVoice(m_instance, &m_source_voice, &waveformatex, 0, FAUDIO_DEFAULT_FREQ_RATIO, this, nullptr, nullptr))
|
||||
if (u32 res = FAudio_CreateSourceVoice(m_instance, &m_source_voice, &waveformatex, 0, FAUDIO_DEFAULT_FREQ_RATIO, this, nullptr, nullptr))
|
||||
{
|
||||
FAudio_.error("FAudio_CreateSourceVoice() failed(0x%08x)", res);
|
||||
CloseUnlocked();
|
||||
return false;
|
||||
}
|
||||
else if (u32 res = FAudioSourceVoice_Start(m_source_voice, 0, FAUDIO_COMMIT_NOW))
|
||||
|
||||
if (u32 res = FAudioSourceVoice_Start(m_source_voice, 0, FAUDIO_COMMIT_NOW))
|
||||
{
|
||||
FAudio_.error("FAudioSourceVoice_Start() failed(0x%08x)", res);
|
||||
CloseUnlocked();
|
||||
return false;
|
||||
}
|
||||
else if (u32 res = FAudioVoice_SetVolume(m_source_voice, 1.0f, FAUDIO_COMMIT_NOW))
|
||||
|
||||
if (u32 res = FAudioVoice_SetVolume(m_source_voice, 1.0f, FAUDIO_COMMIT_NOW))
|
||||
{
|
||||
FAudio_.error("FAudioVoice_SetVolume() failed(0x%08x)", res);
|
||||
}
|
||||
|
||||
if (m_source_voice == nullptr)
|
||||
{
|
||||
FAudio_.error("Failed to open audio backend. Make sure that no other application is running that might block audio access (e.g. Netflix).");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_data_buf.resize(get_sampling_rate() * get_sample_size() * get_channels() * INTERNAL_BUF_SIZE_MS * static_cast<u32>(FAUDIO_DEFAULT_FREQ_RATIO) / 1000);
|
||||
m_data_buf.resize(get_sampling_rate() * get_sample_size() * get_channels() * INTERNAL_BUF_SIZE_MS / 1000);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FAudioBackend::SetWriteCallback(std::function<u32(u32, void *)> cb)
|
||||
{
|
||||
std::lock_guard lock(m_cb_mutex);
|
||||
m_write_callback = cb;
|
||||
}
|
||||
|
||||
f64 FAudioBackend::GetCallbackFrameLen()
|
||||
{
|
||||
constexpr f64 _10ms = 0.01;
|
||||
@@ -218,7 +234,7 @@ void FAudioBackend::OnVoiceProcessingPassStart_func(FAudioVoiceCallback *cb_obj,
|
||||
FAudioBackend *faudio = static_cast<FAudioBackend *>(cb_obj);
|
||||
|
||||
std::unique_lock lock(faudio->m_cb_mutex, std::defer_lock);
|
||||
if (BytesRequired && lock.try_lock() && faudio->m_write_callback && faudio->m_playing)
|
||||
if (BytesRequired && !faudio->m_reset_req.observe() && lock.try_lock() && faudio->m_write_callback && faudio->m_playing)
|
||||
{
|
||||
ensure(BytesRequired <= faudio->m_data_buf.size(), "FAudio internal buffer is too small. Report to developers!");
|
||||
|
||||
@@ -250,11 +266,10 @@ void FAudioBackend::OnCriticalError_func(FAudioEngineCallback *cb_obj, u32 Error
|
||||
|
||||
FAudioBackend *faudio = static_cast<FAudioBackend *>(cb_obj);
|
||||
|
||||
std::lock_guard lock(faudio->m_error_cb_mutex);
|
||||
faudio->m_reset_req = true;
|
||||
std::lock_guard lock(faudio->m_state_cb_mutex);
|
||||
|
||||
if (faudio->m_error_callback)
|
||||
if (!faudio->m_reset_req.test_and_set() && faudio->m_state_callback)
|
||||
{
|
||||
faudio->m_error_callback();
|
||||
faudio->m_state_callback(AudioStateEvent::UNSPECIFIED_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,10 +24,9 @@ public:
|
||||
bool Initialized() override;
|
||||
bool Operational() override;
|
||||
|
||||
bool Open(AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt) override;
|
||||
bool Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt) override;
|
||||
void Close() override;
|
||||
|
||||
void SetWriteCallback(std::function<u32(u32, void *)> cb) override;
|
||||
f64 GetCallbackFrameLen() override;
|
||||
|
||||
void Play() override;
|
||||
@@ -40,12 +39,10 @@ private:
|
||||
FAudioMasteringVoice* m_master_voice{};
|
||||
FAudioSourceVoice* m_source_voice{};
|
||||
|
||||
shared_mutex m_cb_mutex{};
|
||||
std::function<u32(u32, void *)> m_write_callback{};
|
||||
std::vector<u8> m_data_buf{};
|
||||
std::array<u8, sizeof(float) * static_cast<u32>(AudioChannelCnt::SURROUND_7_1)> m_last_sample{};
|
||||
|
||||
bool m_reset_req = false;
|
||||
atomic_t<bool> m_reset_req = false;
|
||||
|
||||
// FAudio voice callbacks
|
||||
static void OnVoiceProcessingPassStart_func(FAudioVoiceCallback *cb_obj, u32 BytesRequired);
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
#ifndef HAVE_FAUDIO
|
||||
#error "FAudio support disabled but still being built."
|
||||
#endif
|
||||
|
||||
#include "Emu/Audio/FAudio/faudio_enumerator.h"
|
||||
#include <array>
|
||||
#include <algorithm>
|
||||
#include "Utilities/StrUtil.h"
|
||||
#include "util/logs.hpp"
|
||||
|
||||
LOG_CHANNEL(faudio_dev_enum);
|
||||
|
||||
faudio_enumerator::faudio_enumerator() : audio_device_enumerator()
|
||||
{
|
||||
FAudio *tmp{};
|
||||
|
||||
if (u32 res = FAudioCreate(&tmp, 0, FAUDIO_DEFAULT_PROCESSOR))
|
||||
{
|
||||
faudio_dev_enum.error("FAudioCreate() failed(0x%08x)", res);
|
||||
return;
|
||||
}
|
||||
|
||||
// All succeeded, "commit"
|
||||
instance = tmp;
|
||||
}
|
||||
|
||||
faudio_enumerator::~faudio_enumerator()
|
||||
{
|
||||
if (instance != nullptr)
|
||||
{
|
||||
FAudio_StopEngine(instance);
|
||||
FAudio_Release(instance);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<audio_device_enumerator::audio_device> faudio_enumerator::get_output_devices()
|
||||
{
|
||||
if (!instance)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
u32 dev_cnt{};
|
||||
if (u32 res = FAudio_GetDeviceCount(instance, &dev_cnt))
|
||||
{
|
||||
faudio_dev_enum.error("FAudio_GetDeviceCount() failed(0x%08x)", res);
|
||||
return {};
|
||||
}
|
||||
|
||||
if (dev_cnt == 0)
|
||||
{
|
||||
faudio_dev_enum.warning("No devices available");
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<audio_device> device_list{};
|
||||
|
||||
for (u32 dev_idx = 0; dev_idx < dev_cnt; dev_idx++)
|
||||
{
|
||||
FAudioDeviceDetails dev_info{};
|
||||
|
||||
if (u32 res = FAudio_GetDeviceDetails(instance, dev_idx, &dev_info))
|
||||
{
|
||||
faudio_dev_enum.error("FAudio_GetDeviceDetails() failed(0x%08x)", res);
|
||||
continue;
|
||||
}
|
||||
|
||||
audio_device dev =
|
||||
{
|
||||
.id = std::to_string(dev_idx),
|
||||
.name = utf16_to_utf8(std::bit_cast<char16_t*>(&dev_info.DisplayName[0])),
|
||||
.max_ch = dev_info.OutputFormat.Format.nChannels
|
||||
};
|
||||
|
||||
if (dev.name.empty())
|
||||
{
|
||||
dev.name = "Device " + dev.id;
|
||||
}
|
||||
|
||||
faudio_dev_enum.notice("Found device: id=%s, name=%s, max_ch=%d", dev.id, dev.name, dev.max_ch);
|
||||
device_list.emplace_back(dev);
|
||||
}
|
||||
|
||||
std::sort(device_list.begin(), device_list.end(), [](audio_device_enumerator::audio_device a, audio_device_enumerator::audio_device b)
|
||||
{
|
||||
return a.name < b.name;
|
||||
});
|
||||
|
||||
return device_list;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef HAVE_FAUDIO
|
||||
#error "FAudio support disabled but still being built."
|
||||
#endif
|
||||
|
||||
#include "Emu/Audio/audio_device_enumerator.h"
|
||||
#include "FAudio.h"
|
||||
|
||||
class faudio_enumerator final : public audio_device_enumerator
|
||||
{
|
||||
public:
|
||||
|
||||
faudio_enumerator();
|
||||
~faudio_enumerator() override;
|
||||
|
||||
std::vector<audio_device> get_output_devices() override;
|
||||
|
||||
private:
|
||||
|
||||
FAudio* instance{};
|
||||
};
|
||||
@@ -10,18 +10,15 @@ public:
|
||||
|
||||
std::string_view GetName() const override { return "Null"sv; }
|
||||
|
||||
bool Open(AudioFreq /* freq */, AudioSampleSize /* sample_size */, AudioChannelCnt /* ch_cnt */) override
|
||||
bool Open(std::string_view /* dev_id */, AudioFreq /* freq */, AudioSampleSize /* sample_size */, AudioChannelCnt /* ch_cnt */) override
|
||||
{
|
||||
Close();
|
||||
return true;
|
||||
}
|
||||
void Close() override { m_playing = false; }
|
||||
|
||||
void SetWriteCallback(std::function<u32(u32, void *)> /* cb */) override {};
|
||||
f64 GetCallbackFrameLen() override { return 0.01; };
|
||||
|
||||
void SetErrorCallback(std::function<void()> /* cb */) override {};
|
||||
|
||||
void Play() override { m_playing = true; }
|
||||
void Pause() override { m_playing = false; }
|
||||
bool IsPlaying() override { return m_playing; }
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "Emu/Audio/audio_device_enumerator.h"
|
||||
|
||||
class null_enumerator final : public audio_device_enumerator
|
||||
{
|
||||
public:
|
||||
|
||||
null_enumerator() {};
|
||||
~null_enumerator() override {};
|
||||
|
||||
std::vector<audio_device> get_output_devices() override { return {}; }
|
||||
};
|
||||
@@ -5,6 +5,8 @@
|
||||
#include <algorithm>
|
||||
#include "util/logs.hpp"
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/Audio/audio_device_enumerator.h"
|
||||
#include "Utilities/StrUtil.h"
|
||||
|
||||
#include "XAudio2Backend.h"
|
||||
#include <Windows.h>
|
||||
@@ -14,42 +16,91 @@
|
||||
|
||||
LOG_CHANNEL(XAudio);
|
||||
|
||||
template <>
|
||||
void fmt_class_string<ERole>::format(std::string& out, u64 arg)
|
||||
{
|
||||
format_enum(out, arg, [](auto value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case eConsole: return "eConsole";
|
||||
case eMultimedia: return "eMultimedia";
|
||||
case eCommunications: return "eCommunications";
|
||||
}
|
||||
|
||||
return unknown;
|
||||
});
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<EDataFlow>::format(std::string& out, u64 arg)
|
||||
{
|
||||
format_enum(out, arg, [](auto value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case eRender: return "eRender";
|
||||
case eCapture: return "eCapture";
|
||||
case eAll: return "eAll";
|
||||
}
|
||||
|
||||
return unknown;
|
||||
});
|
||||
}
|
||||
|
||||
XAudio2Backend::XAudio2Backend()
|
||||
: AudioBackend()
|
||||
{
|
||||
Microsoft::WRL::ComPtr<IXAudio2> instance;
|
||||
Microsoft::WRL::ComPtr<IXAudio2> instance{};
|
||||
Microsoft::WRL::ComPtr<IMMDeviceEnumerator> enumerator{};
|
||||
|
||||
// In order to prevent errors on CreateMasteringVoice, apparently we need CoInitializeEx according to:
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/xaudio2fx/nf-xaudio2fx-xaudio2createvolumemeter
|
||||
HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
||||
if (SUCCEEDED(hr))
|
||||
if (HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED); SUCCEEDED(hr))
|
||||
{
|
||||
m_com_init_success = true;
|
||||
}
|
||||
|
||||
hr = XAudio2Create(instance.GetAddressOf(), 0, XAUDIO2_USE_DEFAULT_PROCESSOR);
|
||||
if (FAILED(hr))
|
||||
if (HRESULT hr = XAudio2Create(instance.GetAddressOf(), 0, XAUDIO2_USE_DEFAULT_PROCESSOR); FAILED(hr))
|
||||
{
|
||||
XAudio.error("XAudio2Create() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
hr = instance->RegisterForCallbacks(this);
|
||||
if (FAILED(hr))
|
||||
if (HRESULT hr = instance->RegisterForCallbacks(this); FAILED(hr))
|
||||
{
|
||||
// Some error recovery functionality will be lost, but otherwise backend is operational
|
||||
XAudio.error("RegisterForCallbacks() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
|
||||
}
|
||||
|
||||
// Try to register a listener for device changes
|
||||
if (HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(enumerator.GetAddressOf())); FAILED(hr))
|
||||
{
|
||||
XAudio.error("CoCreateInstance() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
|
||||
return;
|
||||
}
|
||||
|
||||
if (HRESULT hr = enumerator->RegisterEndpointNotificationCallback(this); FAILED(hr))
|
||||
{
|
||||
XAudio.error("RegisterEndpointNotificationCallback() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
|
||||
return;
|
||||
}
|
||||
|
||||
// All succeeded, "commit"
|
||||
m_xaudio2_instance = std::move(instance);
|
||||
m_device_enumerator = std::move(enumerator);
|
||||
}
|
||||
|
||||
XAudio2Backend::~XAudio2Backend()
|
||||
{
|
||||
Close();
|
||||
|
||||
if (m_device_enumerator != nullptr)
|
||||
{
|
||||
m_device_enumerator->UnregisterEndpointNotificationCallback(this);
|
||||
m_device_enumerator = nullptr;
|
||||
}
|
||||
|
||||
if (m_xaudio2_instance != nullptr)
|
||||
{
|
||||
m_xaudio2_instance->StopEngine();
|
||||
@@ -69,14 +120,13 @@ bool XAudio2Backend::Initialized()
|
||||
|
||||
bool XAudio2Backend::Operational()
|
||||
{
|
||||
std::lock_guard lock(m_error_cb_mutex);
|
||||
return m_source_voice != nullptr && !m_reset_req.observe();
|
||||
}
|
||||
|
||||
if (m_dev_listener.output_device_changed())
|
||||
{
|
||||
m_reset_req = true;
|
||||
}
|
||||
|
||||
return m_source_voice != nullptr && !m_reset_req;
|
||||
bool XAudio2Backend::DefaultDeviceChanged()
|
||||
{
|
||||
std::lock_guard lock{m_dev_sw_mutex};
|
||||
return !m_reset_req.observe() && m_default_dev_changed;
|
||||
}
|
||||
|
||||
void XAudio2Backend::Play()
|
||||
@@ -114,11 +164,15 @@ void XAudio2Backend::CloseUnlocked()
|
||||
|
||||
m_playing = false;
|
||||
m_last_sample.fill(0);
|
||||
|
||||
m_default_dev_changed = false;
|
||||
m_current_device.clear();
|
||||
}
|
||||
|
||||
void XAudio2Backend::Close()
|
||||
{
|
||||
std::lock_guard lock(m_cb_mutex);
|
||||
std::lock_guard dev_sw_lock{m_dev_sw_mutex};
|
||||
CloseUnlocked();
|
||||
}
|
||||
|
||||
@@ -144,7 +198,7 @@ void XAudio2Backend::Pause()
|
||||
}
|
||||
}
|
||||
|
||||
bool XAudio2Backend::Open(AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt)
|
||||
bool XAudio2Backend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt)
|
||||
{
|
||||
if (!Initialized())
|
||||
{
|
||||
@@ -153,11 +207,57 @@ bool XAudio2Backend::Open(AudioFreq freq, AudioSampleSize sample_size, AudioChan
|
||||
}
|
||||
|
||||
std::lock_guard lock(m_cb_mutex);
|
||||
std::lock_guard dev_sw_lock{m_dev_sw_mutex};
|
||||
CloseUnlocked();
|
||||
|
||||
const bool use_default_device = dev_id.empty() || dev_id == audio_device_enumerator::DEFAULT_DEV_ID;
|
||||
std::string selected_dev_id{};
|
||||
|
||||
if (use_default_device)
|
||||
{
|
||||
Microsoft::WRL::ComPtr<IMMDevice> default_dev{};
|
||||
if (HRESULT hr = m_device_enumerator->GetDefaultAudioEndpoint(eRender, eConsole, default_dev.GetAddressOf()); FAILED(hr))
|
||||
{
|
||||
XAudio.error("GetDefaultAudioEndpoint() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
|
||||
return false;
|
||||
}
|
||||
|
||||
LPWSTR default_id{};
|
||||
if (HRESULT hr = default_dev->GetId(&default_id); FAILED(hr))
|
||||
{
|
||||
XAudio.error("GetId() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
|
||||
return false;
|
||||
}
|
||||
|
||||
selected_dev_id = wchar_to_utf8(std::wstring_view{default_id});
|
||||
CoTaskMemFree(default_id);
|
||||
if (selected_dev_id.empty())
|
||||
{
|
||||
XAudio.error("Default device id is empty");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (HRESULT hr = m_xaudio2_instance->CreateMasteringVoice(&m_master_voice, 0, 0, 0, utf8_to_wchar(use_default_device ? selected_dev_id : dev_id).c_str()); FAILED(hr))
|
||||
{
|
||||
XAudio.error("CreateMasteringVoice() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
|
||||
m_master_voice = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
XAUDIO2_VOICE_DETAILS vd{};
|
||||
m_master_voice->GetVoiceDetails(&vd);
|
||||
|
||||
if (vd.InputChannels == 0)
|
||||
{
|
||||
XAudio.error("Channel count 0 is invalid");
|
||||
CloseUnlocked();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_sampling_rate = freq;
|
||||
m_sample_size = sample_size;
|
||||
m_channels = ch_cnt;
|
||||
m_channels = static_cast<AudioChannelCnt>(std::min(static_cast<u32>(convert_channel_count(vd.InputChannels)), static_cast<u32>(ch_cnt)));
|
||||
|
||||
WAVEFORMATEX waveformatex{};
|
||||
waveformatex.wFormatTag = get_convert_to_s16() ? WAVE_FORMAT_PCM : WAVE_FORMAT_IEEE_FLOAT;
|
||||
@@ -168,65 +268,56 @@ bool XAudio2Backend::Open(AudioFreq freq, AudioSampleSize sample_size, AudioChan
|
||||
waveformatex.wBitsPerSample = get_sample_size() * 8;
|
||||
waveformatex.cbSize = 0;
|
||||
|
||||
if (HRESULT hr = m_xaudio2_instance->CreateMasteringVoice(&m_master_voice); FAILED(hr))
|
||||
{
|
||||
XAudio.error("CreateMasteringVoice() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
|
||||
m_master_voice = nullptr;
|
||||
}
|
||||
else if (HRESULT hr = m_xaudio2_instance->CreateSourceVoice(&m_source_voice, &waveformatex, 0, XAUDIO2_DEFAULT_FREQ_RATIO, this); FAILED(hr))
|
||||
if (HRESULT hr = m_xaudio2_instance->CreateSourceVoice(&m_source_voice, &waveformatex, 0, XAUDIO2_DEFAULT_FREQ_RATIO, this); FAILED(hr))
|
||||
{
|
||||
XAudio.error("CreateSourceVoice() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
|
||||
CloseUnlocked();
|
||||
return false;
|
||||
}
|
||||
else if (HRESULT hr = m_source_voice->Start(); FAILED(hr))
|
||||
|
||||
if (HRESULT hr = m_source_voice->Start(); FAILED(hr))
|
||||
{
|
||||
XAudio.error("Start() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
|
||||
CloseUnlocked();
|
||||
return false;
|
||||
}
|
||||
else if (HRESULT hr = m_source_voice->SetVolume(1.0f); FAILED(hr))
|
||||
|
||||
if (HRESULT hr = m_source_voice->SetVolume(1.0f); FAILED(hr))
|
||||
{
|
||||
XAudio.error("SetVolume() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
|
||||
}
|
||||
|
||||
if (m_source_voice == nullptr)
|
||||
m_data_buf.resize(get_sampling_rate() * get_sample_size() * get_channels() * INTERNAL_BUF_SIZE_MS / 1000);
|
||||
|
||||
if (use_default_device)
|
||||
{
|
||||
XAudio.error("Failed to open audio backend. Make sure that no other application is running that might block audio access (e.g. Netflix).");
|
||||
return false;
|
||||
m_current_device = selected_dev_id;
|
||||
}
|
||||
|
||||
m_data_buf.resize(get_sampling_rate() * get_sample_size() * get_channels() * INTERNAL_BUF_SIZE_MS * static_cast<u32>(XAUDIO2_DEFAULT_FREQ_RATIO) / 1000);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void XAudio2Backend::SetWriteCallback(std::function<u32(u32, void *)> cb)
|
||||
{
|
||||
std::lock_guard lock(m_cb_mutex);
|
||||
m_write_callback = cb;
|
||||
}
|
||||
|
||||
f64 XAudio2Backend::GetCallbackFrameLen()
|
||||
{
|
||||
constexpr f64 _10ms = 0.01;
|
||||
|
||||
if (m_source_voice == nullptr)
|
||||
if (m_xaudio2_instance == nullptr)
|
||||
{
|
||||
XAudio.error("GetCallbackFrameLen() called uninitialized");
|
||||
return _10ms;
|
||||
}
|
||||
|
||||
void *ext;
|
||||
Microsoft::WRL::ComPtr<IXAudio2Extension> xaudio_ext{};
|
||||
f64 min_latency{};
|
||||
|
||||
const HRESULT hr = m_xaudio2_instance->QueryInterface(IID_IXAudio2Extension, &ext);
|
||||
if (FAILED(hr))
|
||||
if (HRESULT hr = m_xaudio2_instance->QueryInterface(IID_IXAudio2Extension, std::bit_cast<void**>(xaudio_ext.GetAddressOf())); FAILED(hr))
|
||||
{
|
||||
XAudio.error("QueryInterface() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
|
||||
}
|
||||
else
|
||||
{
|
||||
u32 samples_per_q = 0, freq = 0;
|
||||
static_cast<IXAudio2Extension *>(ext)->GetProcessingQuantum(&samples_per_q, &freq);
|
||||
xaudio_ext->GetProcessingQuantum(&samples_per_q, &freq);
|
||||
|
||||
if (freq)
|
||||
{
|
||||
@@ -240,7 +331,7 @@ f64 XAudio2Backend::GetCallbackFrameLen()
|
||||
void XAudio2Backend::OnVoiceProcessingPassStart(UINT32 BytesRequired)
|
||||
{
|
||||
std::unique_lock lock(m_cb_mutex, std::defer_lock);
|
||||
if (BytesRequired && lock.try_lock() && m_write_callback && m_playing)
|
||||
if (BytesRequired && !m_reset_req.observe() && lock.try_lock() && m_write_callback && m_playing)
|
||||
{
|
||||
ensure(BytesRequired <= m_data_buf.size(), "XAudio internal buffer is too small. Report to developers!");
|
||||
|
||||
@@ -270,11 +361,53 @@ void XAudio2Backend::OnCriticalError(HRESULT Error)
|
||||
{
|
||||
XAudio.error("OnCriticalError() called: %s (0x%08x)", std::system_category().message(Error), static_cast<u32>(Error));
|
||||
|
||||
std::lock_guard lock(m_error_cb_mutex);
|
||||
m_reset_req = true;
|
||||
std::lock_guard lock(m_state_cb_mutex);
|
||||
|
||||
if (m_error_callback)
|
||||
if (!m_reset_req.test_and_set() && m_state_callback)
|
||||
{
|
||||
m_error_callback();
|
||||
m_state_callback(AudioStateEvent::UNSPECIFIED_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT XAudio2Backend::OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR new_default_device_id)
|
||||
{
|
||||
XAudio.notice("OnDefaultDeviceChanged(flow=%s, role=%s, new_default_device_id=0x%x)", flow, role, new_default_device_id);
|
||||
|
||||
if (!new_default_device_id)
|
||||
{
|
||||
XAudio.notice("OnDefaultDeviceChanged(): new_default_device_id empty");
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Listen only for one device role, otherwise we're going to receive more than one notification for flow type
|
||||
if (role != eConsole)
|
||||
{
|
||||
XAudio.notice("OnDefaultDeviceChanged(): we don't care about this device");
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
std::lock_guard lock{m_dev_sw_mutex};
|
||||
|
||||
// Non default device is used
|
||||
if (m_current_device.empty())
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
const std::string new_device_id = wchar_to_utf8(std::wstring_view{new_default_device_id});
|
||||
|
||||
if (flow == eRender || flow == eAll)
|
||||
{
|
||||
if (!m_reset_req.observe() && new_device_id != m_current_device)
|
||||
{
|
||||
m_default_dev_changed = true;
|
||||
|
||||
if (m_state_callback)
|
||||
{
|
||||
m_state_callback(AudioStateEvent::DEFAULT_DEVICE_CHANGED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
#include <memory>
|
||||
#include "Utilities/mutex.h"
|
||||
#include "Emu/Audio/AudioBackend.h"
|
||||
#include "Emu/Audio/audio_device_listener.h"
|
||||
|
||||
#include <xaudio2redist.h>
|
||||
#include <wrl/client.h>
|
||||
#include <MMDeviceAPI.h>
|
||||
|
||||
class XAudio2Backend final : public AudioBackend, public IXAudio2VoiceCallback, public IXAudio2EngineCallback
|
||||
class XAudio2Backend final : public AudioBackend, public IXAudio2VoiceCallback, public IXAudio2EngineCallback, public IMMNotificationClient
|
||||
{
|
||||
public:
|
||||
XAudio2Backend();
|
||||
@@ -25,11 +25,11 @@ public:
|
||||
|
||||
bool Initialized() override;
|
||||
bool Operational() override;
|
||||
bool DefaultDeviceChanged() override;
|
||||
|
||||
bool Open(AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt) override;
|
||||
bool Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt) override;
|
||||
void Close() override;
|
||||
|
||||
void SetWriteCallback(std::function<u32(u32, void *)> cb) override;
|
||||
f64 GetCallbackFrameLen() override;
|
||||
|
||||
void Play() override;
|
||||
@@ -43,14 +43,16 @@ private:
|
||||
IXAudio2SourceVoice* m_source_voice{};
|
||||
bool m_com_init_success = false;
|
||||
|
||||
shared_mutex m_cb_mutex{};
|
||||
std::function<u32(u32, void *)> m_write_callback{};
|
||||
Microsoft::WRL::ComPtr<IMMDeviceEnumerator> m_device_enumerator{};
|
||||
|
||||
shared_mutex m_dev_sw_mutex{};
|
||||
std::string m_current_device{};
|
||||
bool m_default_dev_changed = false;
|
||||
|
||||
std::vector<u8> m_data_buf{};
|
||||
std::array<u8, sizeof(float) * static_cast<u32>(AudioChannelCnt::SURROUND_7_1)> m_last_sample{};
|
||||
|
||||
bool m_reset_req = false;
|
||||
|
||||
audio_device_listener m_dev_listener{};
|
||||
atomic_t<bool> m_reset_req = false;
|
||||
|
||||
// XAudio voice callbacks
|
||||
void OnVoiceProcessingPassStart(UINT32 BytesRequired) override;
|
||||
@@ -66,5 +68,15 @@ private:
|
||||
void OnProcessingPassEnd() override {};
|
||||
void OnCriticalError(HRESULT Error) override;
|
||||
|
||||
// IMMNotificationClient callbacks
|
||||
IFACEMETHODIMP_(ULONG) AddRef() override { return 1; };
|
||||
IFACEMETHODIMP_(ULONG) Release() override { return 1; };
|
||||
IFACEMETHODIMP QueryInterface(REFIID /*iid*/, void** /*object*/) override { return E_NOINTERFACE; };
|
||||
IFACEMETHODIMP OnPropertyValueChanged(LPCWSTR /*device_id*/, const PROPERTYKEY /*key*/) override { return S_OK; };
|
||||
IFACEMETHODIMP OnDeviceAdded(LPCWSTR /*device_id*/) override { return S_OK; };
|
||||
IFACEMETHODIMP OnDeviceRemoved(LPCWSTR /*device_id*/) override { return S_OK; };
|
||||
IFACEMETHODIMP OnDeviceStateChanged(LPCWSTR /*device_id*/, DWORD /*new_state*/) override { return S_OK; };
|
||||
IFACEMETHODIMP OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR new_default_device_id) override;
|
||||
|
||||
void CloseUnlocked();
|
||||
};
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
#ifndef _WIN32
|
||||
#error "XAudio2 can only be built on Windows."
|
||||
#endif
|
||||
|
||||
#include "Emu/Audio/XAudio2/xaudio2_enumerator.h"
|
||||
#include "util/logs.hpp"
|
||||
#include "Utilities/StrUtil.h"
|
||||
#include <algorithm>
|
||||
|
||||
#include <wrl/client.h>
|
||||
#include <Windows.h>
|
||||
#include <system_error>
|
||||
#include <mmdeviceapi.h>
|
||||
#include <Functiondiscoverykeys_devpkey.h>
|
||||
|
||||
LOG_CHANNEL(xaudio_dev_enum);
|
||||
|
||||
xaudio2_enumerator::xaudio2_enumerator() : audio_device_enumerator()
|
||||
{
|
||||
}
|
||||
|
||||
xaudio2_enumerator::~xaudio2_enumerator()
|
||||
{
|
||||
}
|
||||
|
||||
std::vector<audio_device_enumerator::audio_device> xaudio2_enumerator::get_output_devices()
|
||||
{
|
||||
Microsoft::WRL::ComPtr<IMMDeviceEnumerator> devEnum{};
|
||||
if (HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(devEnum.GetAddressOf())); FAILED(hr))
|
||||
{
|
||||
xaudio_dev_enum.error("CoCreateInstance() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
|
||||
return {};
|
||||
}
|
||||
|
||||
Microsoft::WRL::ComPtr<IMMDeviceCollection> devices{};
|
||||
if (HRESULT hr = devEnum->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE | DEVICE_STATE_DISABLED, &devices); FAILED(hr))
|
||||
{
|
||||
xaudio_dev_enum.error("EnumAudioEndpoints() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
|
||||
return {};
|
||||
}
|
||||
|
||||
UINT count = 0;
|
||||
if (HRESULT hr = devices->GetCount(&count); FAILED(hr))
|
||||
{
|
||||
xaudio_dev_enum.error("devices->GetCount() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
|
||||
return {};
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
{
|
||||
xaudio_dev_enum.warning("No devices available");
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<audio_device> device_list{};
|
||||
|
||||
for (UINT dev_idx = 0; dev_idx < count; dev_idx++)
|
||||
{
|
||||
Microsoft::WRL::ComPtr<IMMDevice> endpoint{};
|
||||
if (HRESULT hr = devices->Item(dev_idx, endpoint.GetAddressOf()); FAILED(hr))
|
||||
{
|
||||
xaudio_dev_enum.error("devices->Item() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
|
||||
continue;
|
||||
}
|
||||
|
||||
LPWSTR id = nullptr;
|
||||
if (HRESULT hr = endpoint->GetId(&id); FAILED(hr))
|
||||
{
|
||||
xaudio_dev_enum.error("endpoint->GetId() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (std::wstring_view{id}.empty())
|
||||
{
|
||||
xaudio_dev_enum.error("Empty device id - skipping");
|
||||
CoTaskMemFree(id);
|
||||
continue;
|
||||
}
|
||||
|
||||
audio_device dev{};
|
||||
dev.id = wchar_to_utf8(id);
|
||||
|
||||
CoTaskMemFree(id);
|
||||
|
||||
Microsoft::WRL::ComPtr<IPropertyStore> props{};
|
||||
if (HRESULT hr = endpoint->OpenPropertyStore(STGM_READ, props.GetAddressOf()); FAILED(hr))
|
||||
{
|
||||
xaudio_dev_enum.error("endpoint->OpenPropertyStore() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
|
||||
continue;
|
||||
}
|
||||
|
||||
PROPVARIANT var;
|
||||
PropVariantInit(&var);
|
||||
|
||||
if (HRESULT hr = props->GetValue(PKEY_Device_FriendlyName, &var); FAILED(hr))
|
||||
{
|
||||
xaudio_dev_enum.error("props->GetValue() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
|
||||
PropVariantClear(&var);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (var.vt != VT_LPWSTR)
|
||||
{
|
||||
PropVariantClear(&var);
|
||||
continue;
|
||||
}
|
||||
|
||||
dev.name = wchar_to_utf8(var.pwszVal);
|
||||
if (dev.name.empty())
|
||||
{
|
||||
dev.name = dev.id;
|
||||
}
|
||||
|
||||
PropVariantClear(&var);
|
||||
dev.max_ch = 2;
|
||||
|
||||
if (HRESULT hr = props->GetValue(PKEY_AudioEngine_DeviceFormat, &var); SUCCEEDED(hr))
|
||||
{
|
||||
if (var.vt == VT_BLOB)
|
||||
{
|
||||
if (var.blob.cbSize == sizeof(PCMWAVEFORMAT))
|
||||
{
|
||||
const PCMWAVEFORMAT* pcm = std::bit_cast<const PCMWAVEFORMAT*>(var.blob.pBlobData);
|
||||
dev.max_ch = pcm->wf.nChannels;
|
||||
}
|
||||
else if (var.blob.cbSize >= sizeof(WAVEFORMATEX))
|
||||
{
|
||||
const WAVEFORMATEX* wfx = std::bit_cast<const WAVEFORMATEX*>(var.blob.pBlobData);
|
||||
if (var.blob.cbSize >= sizeof(WAVEFORMATEX) + wfx->cbSize || wfx->wFormatTag == WAVE_FORMAT_PCM)
|
||||
{
|
||||
dev.max_ch = wfx->nChannels;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
xaudio_dev_enum.error("props->GetValue() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
|
||||
}
|
||||
|
||||
PropVariantClear(&var);
|
||||
|
||||
xaudio_dev_enum.notice("Found device: id=%s, name=%s, max_ch=%d", dev.id, dev.name, dev.max_ch);
|
||||
device_list.emplace_back(dev);
|
||||
}
|
||||
|
||||
std::sort(device_list.begin(), device_list.end(), [](audio_device_enumerator::audio_device a, audio_device_enumerator::audio_device b)
|
||||
{
|
||||
return a.name < b.name;
|
||||
});
|
||||
|
||||
return device_list;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef _WIN32
|
||||
#error "XAudio2 can only be built on Windows."
|
||||
#endif
|
||||
|
||||
#include "Emu/Audio/audio_device_enumerator.h"
|
||||
|
||||
class xaudio2_enumerator final : public audio_device_enumerator
|
||||
{
|
||||
public:
|
||||
|
||||
xaudio2_enumerator();
|
||||
~xaudio2_enumerator() override;
|
||||
|
||||
std::vector<audio_device> get_output_devices() override;
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "util/types.hpp"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
class audio_device_enumerator
|
||||
{
|
||||
public:
|
||||
|
||||
static constexpr std::string_view DEFAULT_DEV_ID = "@@@default@@@";
|
||||
|
||||
struct audio_device
|
||||
{
|
||||
std::string id{};
|
||||
std::string name{};
|
||||
usz max_ch{};
|
||||
};
|
||||
|
||||
audio_device_enumerator() {};
|
||||
|
||||
virtual ~audio_device_enumerator() = default;
|
||||
|
||||
// Enumerate available output devices.
|
||||
virtual std::vector<audio_device> get_output_devices() = 0;
|
||||
};
|
||||
@@ -1,147 +0,0 @@
|
||||
#include "stdafx.h"
|
||||
#include "audio_device_listener.h"
|
||||
#include "util/logs.hpp"
|
||||
#include "Utilities/StrUtil.h"
|
||||
#include "Emu/Cell/Modules/cellAudio.h"
|
||||
#include "Emu/IdManager.h"
|
||||
|
||||
LOG_CHANNEL(IO);
|
||||
|
||||
audio_device_listener::audio_device_listener()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
m_com_init_success = true;
|
||||
}
|
||||
|
||||
// Try to register a listener for device changes
|
||||
hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&m_device_enumerator));
|
||||
if (hr != S_OK)
|
||||
{
|
||||
IO.error("CoCreateInstance() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
|
||||
}
|
||||
else if (m_device_enumerator)
|
||||
{
|
||||
hr = m_device_enumerator->RegisterEndpointNotificationCallback(this);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
IO.error("RegisterEndpointNotificationCallback() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
|
||||
m_device_enumerator->Release();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
IO.error("Device enumerator invalid");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
audio_device_listener::~audio_device_listener()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (m_device_enumerator != nullptr)
|
||||
{
|
||||
m_device_enumerator->UnregisterEndpointNotificationCallback(this);
|
||||
m_device_enumerator->Release();
|
||||
}
|
||||
|
||||
if (m_com_init_success)
|
||||
{
|
||||
CoUninitialize();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool audio_device_listener::input_device_changed()
|
||||
{
|
||||
return m_input_device_changed.test_and_reset();
|
||||
}
|
||||
|
||||
bool audio_device_listener::output_device_changed()
|
||||
{
|
||||
return m_output_device_changed.test_and_reset();
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
template <>
|
||||
void fmt_class_string<ERole>::format(std::string& out, u64 arg)
|
||||
{
|
||||
format_enum(out, arg, [](auto value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case eConsole: return "eConsole";
|
||||
case eMultimedia: return "eMultimedia";
|
||||
case eCommunications: return "eCommunications";
|
||||
}
|
||||
|
||||
return unknown;
|
||||
});
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<EDataFlow>::format(std::string& out, u64 arg)
|
||||
{
|
||||
format_enum(out, arg, [](auto value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case eRender: return "eRender";
|
||||
case eCapture: return "eCapture";
|
||||
case eAll: return "eAll";
|
||||
}
|
||||
|
||||
return unknown;
|
||||
});
|
||||
}
|
||||
|
||||
HRESULT audio_device_listener::OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR new_default_device_id)
|
||||
{
|
||||
IO.notice("OnDefaultDeviceChanged(flow=%s, role=%s, new_default_device_id=0x%x)", flow, role, new_default_device_id);
|
||||
|
||||
if (!new_default_device_id)
|
||||
{
|
||||
IO.notice("OnDefaultDeviceChanged(): new_default_device_id empty");
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Listen only for one device role, otherwise we're going to receive more than one
|
||||
// notification for flow type
|
||||
if (role != eConsole)
|
||||
{
|
||||
IO.notice("OnDefaultDeviceChanged(): we don't care about this device");
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
const std::wstring tmp(new_default_device_id);
|
||||
const std::string new_device_id = wchar_to_utf8(tmp.c_str());
|
||||
|
||||
if (flow == eRender || flow == eAll)
|
||||
{
|
||||
if (output_device_id != new_device_id)
|
||||
{
|
||||
output_device_id = new_device_id;
|
||||
|
||||
IO.warning("Default output device changed: new device = '%s'", output_device_id);
|
||||
|
||||
m_output_device_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (flow == eCapture || flow == eAll)
|
||||
{
|
||||
if (input_device_id != new_device_id)
|
||||
{
|
||||
input_device_id = new_device_id;
|
||||
|
||||
IO.warning("Default input device changed: new device = '%s'", input_device_id);
|
||||
|
||||
m_input_device_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
#endif
|
||||
@@ -1,42 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <MMDeviceAPI.h>
|
||||
#endif
|
||||
|
||||
#include "util/atomic.hpp"
|
||||
|
||||
#ifdef _WIN32
|
||||
class audio_device_listener : public IMMNotificationClient
|
||||
#else
|
||||
class audio_device_listener
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
audio_device_listener();
|
||||
~audio_device_listener();
|
||||
|
||||
bool input_device_changed();
|
||||
bool output_device_changed();
|
||||
|
||||
private:
|
||||
#ifdef _WIN32
|
||||
std::string input_device_id;
|
||||
std::string output_device_id;
|
||||
|
||||
IFACEMETHODIMP_(ULONG) AddRef() override { return 1; };
|
||||
IFACEMETHODIMP_(ULONG) Release() override { return 1; };
|
||||
IFACEMETHODIMP QueryInterface(REFIID /*iid*/, void** /*object*/) override { return S_OK; };
|
||||
IFACEMETHODIMP OnPropertyValueChanged(LPCWSTR /*device_id*/, const PROPERTYKEY /*key*/) override { return S_OK; };
|
||||
IFACEMETHODIMP OnDeviceAdded(LPCWSTR /*device_id*/) override { return S_OK; };
|
||||
IFACEMETHODIMP OnDeviceRemoved(LPCWSTR /*device_id*/) override { return S_OK; };
|
||||
IFACEMETHODIMP OnDeviceStateChanged(LPCWSTR /*device_id*/, DWORD /*new_state*/) override { return S_OK; };
|
||||
IFACEMETHODIMP OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR new_default_device_id) override;
|
||||
|
||||
IMMDeviceEnumerator* m_device_enumerator = nullptr;
|
||||
bool m_com_init_success = false;
|
||||
#endif
|
||||
|
||||
atomic_t<bool> m_input_device_changed = false;
|
||||
atomic_t<bool> m_output_device_changed = false;
|
||||
};
|
||||
@@ -2,6 +2,7 @@ add_library(rpcs3_emu
|
||||
cache_utils.cpp
|
||||
IdManager.cpp
|
||||
localized_string.cpp
|
||||
savestate_utils.cpp
|
||||
System.cpp
|
||||
system_config.cpp
|
||||
system_config_types.cpp
|
||||
@@ -58,6 +59,7 @@ target_sources(rpcs3_emu PRIVATE
|
||||
../../Utilities/rXml.cpp
|
||||
../../Utilities/sema.cpp
|
||||
../../Utilities/simple_ringbuf.cpp
|
||||
../../Utilities/stack_trace.cpp
|
||||
../../Utilities/StrFmt.cpp
|
||||
../../Utilities/Thread.cpp
|
||||
../../Utilities/version.cpp
|
||||
@@ -118,17 +120,20 @@ target_sources(rpcs3_emu PRIVATE
|
||||
|
||||
# Audio
|
||||
target_sources(rpcs3_emu PRIVATE
|
||||
Audio/audio_device_listener.cpp
|
||||
Audio/audio_resampler.cpp
|
||||
Audio/AudioDumper.cpp
|
||||
Audio/AudioBackend.cpp
|
||||
Audio/Cubeb/CubebBackend.cpp
|
||||
Audio/Cubeb/cubeb_enumerator.cpp
|
||||
)
|
||||
|
||||
if(USE_FAUDIO)
|
||||
find_package(SDL2)
|
||||
if(SDL2_FOUND AND NOT SDL2_VERSION VERSION_LESS 2.0.9)
|
||||
target_sources(rpcs3_emu PRIVATE Audio/FAudio/FAudioBackend.cpp)
|
||||
target_sources(rpcs3_emu PRIVATE
|
||||
Audio/FAudio/FAudioBackend.cpp
|
||||
Audio/FAudio/faudio_enumerator.cpp
|
||||
)
|
||||
target_link_libraries(rpcs3_emu PUBLIC 3rdparty::faudio)
|
||||
endif()
|
||||
endif()
|
||||
@@ -139,6 +144,7 @@ if(WIN32)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /DELAYLOAD:xaudio2_9redist.dll")
|
||||
target_sources(rpcs3_emu PRIVATE
|
||||
Audio/XAudio2/XAudio2Backend.cpp
|
||||
Audio/XAudio2/xaudio2_enumerator.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -259,6 +265,7 @@ target_sources(rpcs3_emu PRIVATE
|
||||
Cell/Modules/cellMsgDialog.cpp
|
||||
Cell/Modules/cellMusic.cpp
|
||||
Cell/Modules/cellMusicDecode.cpp
|
||||
Cell/Modules/cellMusicSelectionContext.cpp
|
||||
Cell/Modules/cellMusicExport.cpp
|
||||
Cell/Modules/cellNetAoi.cpp
|
||||
Cell/Modules/cellNetCtl.cpp
|
||||
@@ -430,6 +437,7 @@ target_sources(rpcs3_emu PRIVATE
|
||||
RSX/Null/NullGSRender.cpp
|
||||
RSX/Overlays/overlay_animation.cpp
|
||||
RSX/Overlays/overlay_controls.cpp
|
||||
RSX/Overlays/overlay_cursor.cpp
|
||||
RSX/Overlays/overlay_edit_text.cpp
|
||||
RSX/Overlays/overlay_fonts.cpp
|
||||
RSX/Overlays/overlay_list_view.cpp
|
||||
@@ -457,10 +465,14 @@ target_sources(rpcs3_emu PRIVATE
|
||||
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/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/GLCommonDecompiler.cpp
|
||||
RSX/GL/GLCompute.cpp
|
||||
RSX/GL/GLDraw.cpp
|
||||
|
||||
+112
-7
@@ -49,6 +49,7 @@ void fmt_class_string<cpu_flag>::format(std::string& out, u64 arg)
|
||||
case cpu_flag::pause: return "p";
|
||||
case cpu_flag::suspend: return "s";
|
||||
case cpu_flag::ret: return "ret";
|
||||
case cpu_flag::again: return "a";
|
||||
case cpu_flag::signal: return "sig";
|
||||
case cpu_flag::memory: return "mem";
|
||||
case cpu_flag::pending: return "pend";
|
||||
@@ -699,6 +700,12 @@ bool cpu_thread::check_state() noexcept
|
||||
store = true;
|
||||
}
|
||||
|
||||
if (flags & cpu_flag::notify)
|
||||
{
|
||||
flags -= cpu_flag::notify;
|
||||
store = true;
|
||||
}
|
||||
|
||||
// Can't process dbg_step if we only paused temporarily
|
||||
if (cpu_can_stop && flags & cpu_flag::dbg_step)
|
||||
{
|
||||
@@ -720,7 +727,7 @@ bool cpu_thread::check_state() noexcept
|
||||
}
|
||||
|
||||
// Atomically clean wait flag and escape
|
||||
if (!(flags & (cpu_flag::exit + cpu_flag::ret + cpu_flag::stop)))
|
||||
if (!is_stopped(flags) && flags.none_of(cpu_flag::ret))
|
||||
{
|
||||
// Check pause flags which hold thread inside check_state (ignore suspend/debug flags on cpu_flag::temp)
|
||||
if (flags & (cpu_flag::pause + cpu_flag::memory) || (cpu_can_stop && flags & (cpu_flag::dbg_global_pause + cpu_flag::dbg_pause + cpu_flag::suspend)))
|
||||
@@ -768,16 +775,16 @@ bool cpu_thread::check_state() noexcept
|
||||
cpu_counter::add(this);
|
||||
}
|
||||
|
||||
constexpr auto pending_and_temp = (cpu_flag::pending + cpu_flag::temp);
|
||||
|
||||
if ((state0 & pending_and_temp) == cpu_flag::pending)
|
||||
if (cpu_can_stop && state0 & cpu_flag::pending)
|
||||
{
|
||||
// Execute pending work
|
||||
cpu_work();
|
||||
|
||||
if ((state1 ^ state) - pending_and_temp)
|
||||
if ((state1 ^ state) - cpu_flag::pending)
|
||||
{
|
||||
// Work could have changed flags
|
||||
// Reset internal flags as if check_state() has just been called
|
||||
cpu_sleep_called = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -874,8 +881,19 @@ void cpu_thread::notify()
|
||||
|
||||
cpu_thread& cpu_thread::operator=(thread_state)
|
||||
{
|
||||
state += cpu_flag::exit;
|
||||
state.notify_one(cpu_flag::exit);
|
||||
if (state & cpu_flag::exit)
|
||||
{
|
||||
// Must be notified elsewhere or self-raised
|
||||
return *this;
|
||||
}
|
||||
|
||||
const auto old = state.fetch_add(cpu_flag::exit);
|
||||
|
||||
if (old & cpu_flag::wait && old.none_of(cpu_flag::again + cpu_flag::exit))
|
||||
{
|
||||
state.notify_one(cpu_flag::exit);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -945,6 +963,24 @@ u32* cpu_thread::get_pc2()
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
cpu_thread* cpu_thread::get_next_cpu()
|
||||
{
|
||||
switch (id_type())
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
return static_cast<ppu_thread*>(this)->next_cpu;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
return static_cast<spu_thread*>(this)->next_cpu;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<CPUDisAsm> make_disasm(const cpu_thread* cpu);
|
||||
|
||||
void cpu_thread::dump_all(std::string& ret) const
|
||||
@@ -1195,3 +1231,72 @@ u32 CPUDisAsm::DisAsmBranchTarget(s32 /*imm*/)
|
||||
// Unused
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern bool try_lock_spu_threads_in_a_state_compatible_with_savestates()
|
||||
{
|
||||
const u64 start = get_system_time();
|
||||
|
||||
// Attempt to lock for half a second, if somehow takes longer abort it
|
||||
do
|
||||
{
|
||||
if (cpu_thread::suspend_all(nullptr, {}, []()
|
||||
{
|
||||
return idm::select<named_thread<spu_thread>>([](u32, spu_thread& spu)
|
||||
{
|
||||
if (!spu.unsavable)
|
||||
{
|
||||
if (Emu.IsPaused())
|
||||
{
|
||||
// If emulation is paused, we can only hope it's already in a state compatible with savestates
|
||||
return !!(spu.state & (cpu_flag::dbg_global_pause + cpu_flag::dbg_pause));
|
||||
}
|
||||
else
|
||||
{
|
||||
ensure(!spu.state.test_and_set(cpu_flag::dbg_global_pause));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}).ret;
|
||||
}))
|
||||
{
|
||||
if (Emu.IsPaused())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// It's faster to lock once
|
||||
reader_lock lock(id_manager::g_mutex);
|
||||
|
||||
idm::select<named_thread<spu_thread>>([](u32, spu_thread& spu)
|
||||
{
|
||||
spu.state -= cpu_flag::dbg_global_pause;
|
||||
}, idm::unlocked);
|
||||
|
||||
// For faster signalling, first remove state flags then batch notifications
|
||||
idm::select<named_thread<spu_thread>>([](u32, spu_thread& spu)
|
||||
{
|
||||
if (spu.state & cpu_flag::wait)
|
||||
{
|
||||
spu.state.notify_one();
|
||||
}
|
||||
}, idm::unlocked);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
} while (std::this_thread::yield(), get_system_time() - start <= 500'000);
|
||||
|
||||
idm::select<named_thread<spu_thread>>([&](u32, named_thread<spu_thread>& spu)
|
||||
{
|
||||
if (spu.state.test_and_reset(cpu_flag::dbg_global_pause))
|
||||
{
|
||||
spu.state.notify_one();
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -19,10 +19,12 @@ enum class cpu_flag : u32
|
||||
pause, // Thread suspended by suspend_all technique
|
||||
suspend, // Thread suspended
|
||||
ret, // Callback return requested
|
||||
again, // Thread must complete the syscall after deserialization
|
||||
signal, // Thread received a signal (HLE)
|
||||
memory, // Thread must unlock memory mutex
|
||||
pending, // Thread has postponed work
|
||||
pending_recheck, // Thread needs to recheck if there is pending work before ::pending removal
|
||||
notify, // Flag meant solely to allow atomic notification on state without changing other flags
|
||||
|
||||
dbg_global_pause, // Emulation paused
|
||||
dbg_pause, // Thread paused
|
||||
@@ -34,7 +36,7 @@ enum class cpu_flag : u32
|
||||
// Test stopped state
|
||||
constexpr bool is_stopped(bs_t<cpu_flag> state)
|
||||
{
|
||||
return !!(state & (cpu_flag::stop + cpu_flag::exit));
|
||||
return !!(state & (cpu_flag::stop + cpu_flag::exit + cpu_flag::again));
|
||||
}
|
||||
|
||||
// Test paused state
|
||||
@@ -139,6 +141,7 @@ public:
|
||||
|
||||
u32 get_pc() const;
|
||||
u32* get_pc2(); // Last PC before stepping for the debugger (may be null)
|
||||
cpu_thread* get_next_cpu(); // Access next_cpu member if the is one
|
||||
|
||||
void notify();
|
||||
cpu_thread& operator=(thread_state);
|
||||
@@ -172,7 +175,7 @@ public:
|
||||
virtual void cpu_sleep() {}
|
||||
|
||||
// Callback for cpu_flag::pending
|
||||
virtual void cpu_work() {}
|
||||
virtual void cpu_work() { state -= cpu_flag::pending + cpu_flag::pending_recheck; }
|
||||
|
||||
// Callback for cpu_flag::ret
|
||||
virtual void cpu_return() {}
|
||||
|
||||
@@ -32,7 +32,21 @@ cpu_translator::cpu_translator(llvm::Module* _module, bool is_be)
|
||||
|
||||
if (m_use_ssse3)
|
||||
{
|
||||
#if defined(ARCH_X64)
|
||||
return m_ir->CreateCall(get_intrinsic(llvm::Intrinsic::x86_ssse3_pshuf_b_128), {data0, index});
|
||||
#elif defined(ARCH_ARM64)
|
||||
// Modified from sse2neon
|
||||
// movi v2.16b, #143
|
||||
// and v1.16b, v1.16b, v2.16b
|
||||
// tbl v0.16b, { v0.16b }, v1.16b
|
||||
auto mask = llvm::ConstantInt::get(get_type<u8[16]>(), 0x8F);
|
||||
auto and_mask = llvm::ConstantInt::get(get_type<bool[16]>(), true);
|
||||
auto vec_len = llvm::ConstantInt::get(get_type<u32>(), 16);
|
||||
auto index_masked = m_ir->CreateCall(get_intrinsic<u8[16]>(llvm::Intrinsic::vp_and), {index, mask, and_mask, vec_len});
|
||||
return m_ir->CreateCall(get_intrinsic<u8[16]>(llvm::Intrinsic::aarch64_neon_tbl1), {data0, index_masked});
|
||||
#else
|
||||
#error "Unimplemented"
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -140,6 +154,14 @@ void cpu_translator::initialize(llvm::LLVMContext& context, llvm::ExecutionEngin
|
||||
m_use_avx512_icl = true;
|
||||
m_use_vnni = true;
|
||||
}
|
||||
|
||||
// Aarch64 CPUs
|
||||
if (cpu == "cyclone" || cpu.contains("cortex"))
|
||||
{
|
||||
m_use_fma = true;
|
||||
// AVX does not use intrinsics so far
|
||||
m_use_avx = true;
|
||||
}
|
||||
}
|
||||
|
||||
llvm::Value* cpu_translator::bitcast(llvm::Value* val, llvm::Type* type) const
|
||||
|
||||
@@ -2896,12 +2896,8 @@ protected:
|
||||
bool m_is_be;
|
||||
|
||||
// Allow PSHUFB intrinsic
|
||||
#ifdef ARCH_X64
|
||||
bool m_use_ssse3 = true;
|
||||
#else
|
||||
// TODO: fix the pshufb arm64 native impl using TBL instruction
|
||||
bool m_use_ssse3 = false;
|
||||
#endif
|
||||
|
||||
// Allow FMA
|
||||
bool m_use_fma = false;
|
||||
|
||||
@@ -2979,6 +2975,7 @@ public:
|
||||
m_engine->updateGlobalMapping({lame.data(), lame.size()}, reinterpret_cast<uptr>(_func));
|
||||
|
||||
const auto inst = m_ir->CreateCall(func, {args...});
|
||||
inst->setTailCallKind(llvm::CallInst::TCK_NoTail);
|
||||
#ifdef _WIN32
|
||||
inst->setCallingConv(llvm::CallingConv::Win64);
|
||||
#endif
|
||||
|
||||
@@ -39,6 +39,8 @@ public:
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
ENABLE_BITWISE_SERIALIZATION;
|
||||
};
|
||||
|
||||
enum CellNotAnError : s32
|
||||
|
||||
@@ -88,6 +88,8 @@ enum : u32
|
||||
|
||||
struct alignas(16) spu_mfc_cmd
|
||||
{
|
||||
ENABLE_BITWISE_SERIALIZATION;
|
||||
|
||||
MFC cmd;
|
||||
u8 tag;
|
||||
u16 size;
|
||||
|
||||
@@ -66,11 +66,24 @@ void cell_audio_config::reset(bool backend_changed)
|
||||
|
||||
const AudioFreq freq = AudioFreq::FREQ_48K;
|
||||
const AudioSampleSize sample_size = raw.convert_to_s16 ? AudioSampleSize::S16 : AudioSampleSize::FLOAT;
|
||||
const auto [ch_cnt, downmix] = AudioBackend::get_channel_count_and_downmixer(0); // CELL_AUDIO_OUT_PRIMARY
|
||||
const f64 cb_frame_len = backend->Open(freq, sample_size, ch_cnt) ? backend->GetCallbackFrameLen() : 0.0;
|
||||
|
||||
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;
|
||||
|
||||
if (backend->Open(raw.audio_device, freq, sample_size, req_ch_cnt))
|
||||
{
|
||||
cb_frame_len = backend->GetCallbackFrameLen();
|
||||
ch_cnt = backend->get_channels();
|
||||
}
|
||||
else
|
||||
{
|
||||
cellAudio.error("Failed to open audio backend. Make sure that no other application is running that might block audio access (e.g. Netflix).");
|
||||
}
|
||||
|
||||
audio_downmix = downmix;
|
||||
audio_channels = static_cast<u32>(ch_cnt);
|
||||
backend_ch_cnt = AudioChannelCnt{ch_cnt};
|
||||
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;
|
||||
audio_sample_size = static_cast<u32>(sample_size);
|
||||
@@ -143,7 +156,7 @@ audio_ringbuffer::audio_ringbuffer(cell_audio_config& _cfg)
|
||||
return cfg.audio_min_buffer_duration;
|
||||
}();
|
||||
|
||||
cb_ringbuf.set_buf_size(static_cast<u32>(cfg.audio_channels * cfg.audio_sampling_rate * cfg.audio_sample_size * buffer_dur_mult));
|
||||
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));
|
||||
backend->SetWriteCallback(std::bind(&audio_ringbuffer::backend_write_callback, this, std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
@@ -191,7 +204,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 * cfg.audio_channels);
|
||||
const u64 ringbuf_samples = cb_ringbuf.get_used_size() / (cfg.audio_sample_size * static_cast<u32>(cfg.backend_ch_cnt));
|
||||
|
||||
if (cfg.time_stretching_enabled)
|
||||
{
|
||||
@@ -252,7 +265,7 @@ void audio_ringbuffer::process_resampled_data()
|
||||
{
|
||||
if (!cfg.time_stretching_enabled) return;
|
||||
|
||||
const auto samples = resampler.get_samples(static_cast<u32>(cb_ringbuf.get_free_size() / (cfg.audio_sample_size * cfg.audio_channels)));
|
||||
const auto samples = resampler.get_samples(static_cast<u32>(cb_ringbuf.get_free_size() / (cfg.audio_sample_size * static_cast<u32>(cfg.backend_ch_cnt))));
|
||||
commit_data(samples.first, samples.second);
|
||||
}
|
||||
|
||||
@@ -263,12 +276,48 @@ void audio_ringbuffer::commit_data(f32* buf, u32 sample_cnt)
|
||||
// Dump audio if enabled
|
||||
m_dump.WriteData(buf, sample_cnt * static_cast<u32>(AudioSampleSize::FLOAT));
|
||||
|
||||
if (cfg.backend->get_convert_to_s16())
|
||||
if (cfg.backend_ch_cnt < AudioChannelCnt{cfg.audio_channels})
|
||||
{
|
||||
AudioBackend::convert_to_s16(sample_cnt, buf, buf);
|
||||
if (AudioChannelCnt{cfg.audio_channels} == AudioChannelCnt::SURROUND_7_1)
|
||||
{
|
||||
if (cfg.backend_ch_cnt == AudioChannelCnt::SURROUND_5_1)
|
||||
{
|
||||
AudioBackend::downmix<AudioChannelCnt::SURROUND_7_1, AudioChannelCnt::SURROUND_5_1>(sample_cnt, buf, buf);
|
||||
}
|
||||
else if (cfg.backend_ch_cnt == AudioChannelCnt::STEREO)
|
||||
{
|
||||
AudioBackend::downmix<AudioChannelCnt::SURROUND_7_1, AudioChannelCnt::STEREO>(sample_cnt, buf, buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::throw_exception("Invalid downmix combination: %u -> %u", cfg.audio_channels, static_cast<u32>(cfg.backend_ch_cnt));
|
||||
}
|
||||
}
|
||||
else if (AudioChannelCnt{cfg.audio_channels} == AudioChannelCnt::SURROUND_5_1)
|
||||
{
|
||||
if (cfg.backend_ch_cnt == AudioChannelCnt::STEREO)
|
||||
{
|
||||
AudioBackend::downmix<AudioChannelCnt::SURROUND_5_1, AudioChannelCnt::STEREO>(sample_cnt, buf, buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::throw_exception("Invalid downmix combination: %u -> %u", cfg.audio_channels, static_cast<u32>(cfg.backend_ch_cnt));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::throw_exception("Invalid downmix combination: %u -> %u", cfg.audio_channels, static_cast<u32>(cfg.backend_ch_cnt));
|
||||
}
|
||||
}
|
||||
|
||||
cb_ringbuf.push(buf, sample_cnt * cfg.audio_sample_size);
|
||||
const u32 sample_cnt_out = sample_cnt / cfg.audio_channels * static_cast<u32>(cfg.backend_ch_cnt);
|
||||
|
||||
if (cfg.backend->get_convert_to_s16())
|
||||
{
|
||||
AudioBackend::convert_to_s16(sample_cnt_out, buf, buf);
|
||||
}
|
||||
|
||||
cb_ringbuf.push(buf, sample_cnt_out * cfg.audio_sample_size);
|
||||
}
|
||||
|
||||
void audio_ringbuffer::play()
|
||||
@@ -342,6 +391,52 @@ void audio_port::tag(s32 offset)
|
||||
prev_touched_tag_nr = -1;
|
||||
}
|
||||
|
||||
cell_audio_thread::cell_audio_thread(utils::serial& ar)
|
||||
: cell_audio_thread()
|
||||
{
|
||||
ar(init);
|
||||
|
||||
if (!init)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ar(key_count, event_period);
|
||||
|
||||
keys.resize(ar);
|
||||
|
||||
for (key_info& k : keys)
|
||||
{
|
||||
ar(k.start_period, k.flags, k.source);
|
||||
k.port = lv2_event_queue::load_ptr(ar, k.port);
|
||||
}
|
||||
|
||||
ar(ports);
|
||||
}
|
||||
|
||||
void cell_audio_thread::save(utils::serial& ar)
|
||||
{
|
||||
ar(init);
|
||||
|
||||
if (!init)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
USING_SERIALIZATION_VERSION(cellAudio);
|
||||
|
||||
ar(key_count, event_period);
|
||||
ar(keys.size());
|
||||
|
||||
for (const key_info& k : keys)
|
||||
{
|
||||
ar(k.start_period, k.flags, k.source);
|
||||
lv2_event_queue::save_ptr(ar, k.port.get());
|
||||
}
|
||||
|
||||
ar(ports);
|
||||
}
|
||||
|
||||
std::tuple<u32, u32, u32, u32> cell_audio_thread::count_port_buffer_tags()
|
||||
{
|
||||
AUDIT(cfg.buffering_enabled);
|
||||
@@ -511,6 +606,7 @@ void cell_audio_thread::advance(u64 timestamp)
|
||||
|
||||
for (u32 i = 0; i < queue_count; i++)
|
||||
{
|
||||
lv2_obj::notify_all_t notify;
|
||||
queues[i]->send(event_sources[i], 0, 0, 0);
|
||||
}
|
||||
}
|
||||
@@ -521,6 +617,7 @@ namespace audio
|
||||
{
|
||||
return
|
||||
{
|
||||
.audio_device = g_cfg.audio.audio_device,
|
||||
.buffering_enabled = static_cast<bool>(g_cfg.audio.enable_buffering),
|
||||
.desired_buffer_duration = g_cfg.audio.desired_buffer_duration,
|
||||
.enable_time_stretching = static_cast<bool>(g_cfg.audio.enable_time_stretching),
|
||||
@@ -546,6 +643,7 @@ namespace audio
|
||||
|
||||
if (const auto raw = g_audio.cfg.raw;
|
||||
force_reset ||
|
||||
raw.audio_device != new_raw.audio_device ||
|
||||
raw.desired_buffer_duration != new_raw.desired_buffer_duration ||
|
||||
raw.buffering_enabled != new_raw.buffering_enabled ||
|
||||
raw.time_stretching_threshold != new_raw.time_stretching_threshold ||
|
||||
@@ -615,6 +713,11 @@ void cell_audio_thread::operator()()
|
||||
|
||||
thread_ctrl::scoped_priority high_prio(+1);
|
||||
|
||||
while (Emu.IsPaused())
|
||||
{
|
||||
thread_ctrl::wait_for(5000);
|
||||
}
|
||||
|
||||
u32 untouched_expected = 0;
|
||||
|
||||
// Main cellAudio loop
|
||||
@@ -648,6 +751,13 @@ void cell_audio_thread::operator()()
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ringbuffer->device_changed())
|
||||
{
|
||||
cellAudio.warning("Default device changed, attempting to switch...");
|
||||
update_config(false);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m_backend_failed)
|
||||
{
|
||||
cellAudio.warning("Backend recovered");
|
||||
|
||||
@@ -189,12 +189,23 @@ struct audio_port
|
||||
f32 last_tag_value[PORT_BUFFER_TAG_COUNT] = { 0 };
|
||||
|
||||
void tag(s32 offset = 0);
|
||||
|
||||
audio_port() = default;
|
||||
|
||||
// Handle copy ctor of atomic var
|
||||
audio_port(const audio_port& r)
|
||||
{
|
||||
std::memcpy(this, &r, sizeof(r));
|
||||
}
|
||||
|
||||
ENABLE_BITWISE_SERIALIZATION;
|
||||
};
|
||||
|
||||
struct cell_audio_config
|
||||
{
|
||||
struct raw_config
|
||||
{
|
||||
std::string audio_device{};
|
||||
bool buffering_enabled = false;
|
||||
s64 desired_buffer_duration = 0;
|
||||
bool enable_time_stretching = false;
|
||||
@@ -211,6 +222,7 @@ 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;
|
||||
u32 audio_channels = 0;
|
||||
u32 audio_sampling_rate = 0;
|
||||
u32 audio_block_period = 0;
|
||||
@@ -333,6 +345,11 @@ public:
|
||||
return backend->Operational();
|
||||
}
|
||||
|
||||
bool device_changed() const
|
||||
{
|
||||
return backend->DefaultDeviceChanged();
|
||||
}
|
||||
|
||||
std::string_view get_backend_name() const
|
||||
{
|
||||
return backend->GetName();
|
||||
@@ -366,7 +383,7 @@ public:
|
||||
atomic_t<audio_backend_update> m_update_configuration = audio_backend_update::NONE;
|
||||
|
||||
shared_mutex mutex{};
|
||||
atomic_t<u32> init = 0;
|
||||
atomic_t<u8> init = 0;
|
||||
|
||||
u32 key_count = 0;
|
||||
u8 event_period = 0;
|
||||
@@ -390,10 +407,14 @@ public:
|
||||
bool m_backend_failed = false;
|
||||
bool m_audio_should_restart = false;
|
||||
|
||||
cell_audio_thread();
|
||||
|
||||
void operator()();
|
||||
|
||||
SAVESTATE_INIT_POS(9);
|
||||
|
||||
cell_audio_thread();
|
||||
cell_audio_thread(utils::serial& ar);
|
||||
void save(utils::serial& ar);
|
||||
|
||||
audio_port* open_port();
|
||||
|
||||
static constexpr auto thread_name = "cellAudio Thread"sv;
|
||||
|
||||
@@ -178,6 +178,23 @@ audio_out_configuration::audio_out_configuration()
|
||||
cellSysutil.notice("cellAudioOut: initial secondary output configuration: channels=%d, encoder=%d, downmixer=%d", secondary_output.channels, secondary_output.encoder, secondary_output.downmixer);
|
||||
}
|
||||
|
||||
audio_out_configuration::audio_out_configuration(utils::serial& ar)
|
||||
: audio_out_configuration()
|
||||
{
|
||||
// Load configuartion (ar is reading)
|
||||
save(ar);
|
||||
}
|
||||
|
||||
void audio_out_configuration::save(utils::serial& ar)
|
||||
{
|
||||
GET_OR_USE_SERIALIZATION_VERSION(ar.is_writing(), cellAudioOut);
|
||||
|
||||
for (auto& state : out)
|
||||
{
|
||||
ar(state.state, state.channels, state.encoder, state.downmixer, state.copy_control, state.sound_modes, state.sound_mode);
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<AudioChannelCnt, AudioChannelCnt> audio_out_configuration::audio_out::get_channel_count_and_downmixer() const
|
||||
{
|
||||
std::pair<AudioChannelCnt, AudioChannelCnt> ret;
|
||||
|
||||
@@ -135,6 +135,8 @@ struct CellAudioOutSoundMode
|
||||
u8 fs;
|
||||
u8 reserved;
|
||||
be_t<u32> layout;
|
||||
|
||||
ENABLE_BITWISE_SERIALIZATION;
|
||||
};
|
||||
|
||||
struct CellAudioOutDeviceInfo
|
||||
@@ -217,5 +219,8 @@ struct audio_out_configuration
|
||||
|
||||
std::array<audio_out, 2> out;
|
||||
|
||||
SAVESTATE_INIT_POS(8.9); // Is a dependency of cellAudio
|
||||
audio_out_configuration();
|
||||
audio_out_configuration(utils::serial& ar);
|
||||
void save(utils::serial& ar);
|
||||
};
|
||||
|
||||
@@ -131,6 +131,25 @@ static const char* get_camera_attr_name(s32 value)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
camera_context::camera_context(utils::serial& ar)
|
||||
{
|
||||
save(ar);
|
||||
}
|
||||
|
||||
void camera_context::save(utils::serial& ar)
|
||||
{
|
||||
ar(init);
|
||||
|
||||
if (!init)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GET_OR_USE_SERIALIZATION_VERSION(ar.is_writing(), cellCamera);
|
||||
|
||||
ar(notify_data_map, start_timestamp, read_mode, is_streaming, is_attached, is_open, info, attr, frame_num);
|
||||
}
|
||||
|
||||
static bool check_dev_num(s32 dev_num)
|
||||
{
|
||||
return dev_num == 0;
|
||||
|
||||
@@ -375,6 +375,8 @@ struct CellCameraInfoEx
|
||||
be_t<u32> container;
|
||||
be_t<s32> read_mode;
|
||||
vm::bptr<u8> pbuf[2];
|
||||
|
||||
ENABLE_BITWISE_SERIALIZATION;
|
||||
};
|
||||
|
||||
struct CellCameraReadEx
|
||||
@@ -392,6 +394,8 @@ class camera_context
|
||||
{
|
||||
u64 source;
|
||||
u64 flag;
|
||||
|
||||
ENABLE_BITWISE_SERIALIZATION;
|
||||
};
|
||||
|
||||
public:
|
||||
@@ -433,14 +437,23 @@ public:
|
||||
struct attr_t
|
||||
{
|
||||
u32 v1, v2;
|
||||
|
||||
ENABLE_BITWISE_SERIALIZATION;
|
||||
};
|
||||
|
||||
attr_t attr[500]{};
|
||||
atomic_t<bool> has_new_frame = false;
|
||||
atomic_t<u32> frame_num = 0;
|
||||
atomic_t<u32> frame_timestamp = 0;
|
||||
atomic_t<u32> bytes_read = 0;
|
||||
|
||||
atomic_t<u32> init = 0;
|
||||
atomic_t<u8> init = 0;
|
||||
|
||||
SAVESTATE_INIT_POS(16);
|
||||
|
||||
camera_context() = default;
|
||||
camera_context(utils::serial& ar);
|
||||
void save(utils::serial& ar);
|
||||
|
||||
static constexpr auto thread_name = "Camera Thread"sv;
|
||||
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
#include "stdafx.h"
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
#include "Emu/IdManager.h"
|
||||
#include "Emu/localized_string.h"
|
||||
#include "cellSysutil.h"
|
||||
#include "cellCrossController.h"
|
||||
#include "cellMsgDialog.h"
|
||||
|
||||
|
||||
LOG_CHANNEL(cellCrossController);
|
||||
@@ -33,15 +37,117 @@ void fmt_class_string<CellCrossControllerError>::format(std::string& out, u64 ar
|
||||
});
|
||||
}
|
||||
|
||||
void finish_callback(ppu_thread& ppu, s32 button_type, vm::ptr<void> userdata); // Forward declaration
|
||||
|
||||
struct cross_controller
|
||||
{
|
||||
atomic_t<s32> status{0};
|
||||
std::unique_ptr<named_thread<std::function<void()>>> connection_thread;
|
||||
vm::ptr<CellCrossControllerCallback> callback = vm::null;
|
||||
vm::ptr<void> userdata = vm::null;
|
||||
|
||||
void on_connection_established(s32 status)
|
||||
{
|
||||
ensure(!!callback);
|
||||
|
||||
close_msg_dialog();
|
||||
|
||||
sysutil_register_cb([=](ppu_thread& ppu) -> s32
|
||||
{
|
||||
callback(ppu, CELL_CROSS_CONTROLLER_STATUS_FINALIZED, status, vm::null, userdata);
|
||||
return CELL_OK;
|
||||
});
|
||||
}
|
||||
|
||||
void run_thread(vm::cptr<CellCrossControllerPackageInfo> pPkgInfo)
|
||||
{
|
||||
ensure(!!pPkgInfo);
|
||||
ensure(!!callback);
|
||||
|
||||
const std::string msg = fmt::format("%s\n\n%s",
|
||||
get_localized_string(localized_string_id::CELL_CROSS_CONTROLLER_MSG, pPkgInfo->pTitle.get_ptr()),
|
||||
get_localized_string(localized_string_id::CELL_CROSS_CONTROLLER_FW_MSG));
|
||||
|
||||
vm::bptr<CellMsgDialogCallback> msg_dialog_callback = vm::null;
|
||||
msg_dialog_callback.set(g_fxo->get<ppu_function_manager>().func_addr(FIND_FUNC(finish_callback)));
|
||||
|
||||
// TODO: Show icons from comboplay_plugin.rco in dialog. Maybe use a new dialog or add an optional icon to this one.
|
||||
error_code res = open_msg_dialog(false, CELL_MSGDIALOG_TYPE_DISABLE_CANCEL_OFF, vm::make_str(msg), msg_dialog_callback, userdata);
|
||||
|
||||
sysutil_register_cb([=](ppu_thread& ppu) -> s32
|
||||
{
|
||||
callback(ppu, CELL_CROSS_CONTROLLER_STATUS_INITIALIZED, res == CELL_OK ? CELL_OK : CELL_CROSS_CONTROLLER_ERROR_INTERNAL, vm::null, userdata);
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
status = CELL_CROSS_CONTROLLER_STATUS_INITIALIZED;
|
||||
|
||||
connection_thread = std::make_unique<named_thread<std::function<void()>>>(fmt::format("Cross-Controller Thread"), [this]()
|
||||
{
|
||||
while (thread_ctrl::state() != thread_state::aborting)
|
||||
{
|
||||
if (Emu.IsPaused())
|
||||
{
|
||||
thread_ctrl::wait_for(10'000);
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: establish connection to PS Vita
|
||||
if (false)
|
||||
{
|
||||
on_connection_established(CELL_OK);
|
||||
}
|
||||
|
||||
thread_ctrl::wait_for(1000);
|
||||
}
|
||||
|
||||
status = CELL_CROSS_CONTROLLER_STATUS_FINALIZED;
|
||||
});
|
||||
}
|
||||
|
||||
void stop_thread()
|
||||
{
|
||||
if (connection_thread)
|
||||
{
|
||||
auto& thread = *connection_thread;
|
||||
thread = thread_state::aborting;
|
||||
thread();
|
||||
connection_thread.reset();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
void finish_callback(ppu_thread& ppu, s32 button_type, vm::ptr<void> userdata)
|
||||
{
|
||||
cross_controller& cc = g_fxo->get<cross_controller>();
|
||||
|
||||
// This function should only be called when the user canceled the dialog
|
||||
ensure(cc.callback && button_type == CELL_MSGDIALOG_BUTTON_ESCAPE);
|
||||
|
||||
cc.callback(ppu, CELL_CROSS_CONTROLLER_STATUS_FINALIZED, CELL_CROSS_CONTROLLER_ERROR_CANCEL, vm::null, userdata);
|
||||
cc.stop_thread();
|
||||
}
|
||||
|
||||
error_code cellCrossControllerInitialize(vm::cptr<CellCrossControllerParam> pParam, vm::cptr<CellCrossControllerPackageInfo> pPkgInfo, vm::ptr<CellCrossControllerCallback> cb, vm::ptr<void> userdata) // LittleBigPlanet 2 and 3
|
||||
{
|
||||
cellCrossController.todo("cellCrossControllerInitialize(pParam=*0x%x, pPkgInfo=*0x%x, cb=*0x%x, userdata=*0x%x)", pParam, pPkgInfo, cb, userdata);
|
||||
|
||||
// TODO
|
||||
//if (something)
|
||||
//{
|
||||
// return CELL_CROSS_CONTROLLER_ERROR_INVALID_STATE;
|
||||
//}
|
||||
if (pParam)
|
||||
{
|
||||
cellCrossController.notice("cellCrossControllerInitialize: pParam: pPackageFileName=%s, pSignatureFileName=%s, pIconFileName=%s", pParam->pPackageFileName, pParam->pSignatureFileName, pParam->pIconFileName);
|
||||
}
|
||||
|
||||
if (pPkgInfo)
|
||||
{
|
||||
cellCrossController.notice("cellCrossControllerInitialize: pPkgInfo: pTitle=%s, pTitleId=%s, pAppVer=%s", pPkgInfo->pTitle, pPkgInfo->pTitleId, pPkgInfo->pAppVer);
|
||||
}
|
||||
|
||||
cross_controller& cc = g_fxo->get<cross_controller>();
|
||||
|
||||
if (cc.status == CELL_CROSS_CONTROLLER_STATUS_INITIALIZED) // TODO: confirm this logic
|
||||
{
|
||||
return CELL_CROSS_CONTROLLER_ERROR_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (!pParam || !pPkgInfo)
|
||||
{
|
||||
@@ -73,13 +179,9 @@ error_code cellCrossControllerInitialize(vm::cptr<CellCrossControllerParam> pPar
|
||||
return CELL_CROSS_CONTROLLER_ERROR_INVALID_VALUE;
|
||||
}
|
||||
|
||||
sysutil_register_cb([=](ppu_thread& ppu) -> s32
|
||||
{
|
||||
cb(ppu, CELL_CROSS_CONTROLLER_STATUS_INITIALIZED, CELL_OK, vm::null, userdata);
|
||||
cb(ppu, CELL_CROSS_CONTROLLER_STATUS_FINALIZED, CELL_OK, vm::null, userdata);
|
||||
|
||||
return CELL_OK;
|
||||
});
|
||||
cc.callback = cb;
|
||||
cc.userdata = userdata;
|
||||
cc.run_thread(pPkgInfo);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -88,4 +190,7 @@ error_code cellCrossControllerInitialize(vm::cptr<CellCrossControllerParam> pPar
|
||||
DECLARE(ppu_module_manager::cellCrossController)("cellCrossController", []()
|
||||
{
|
||||
REG_FUNC(cellCrossController, cellCrossControllerInitialize);
|
||||
|
||||
// Helper Function
|
||||
REG_HIDDEN_FUNC(finish_callback);
|
||||
});
|
||||
|
||||
@@ -165,6 +165,7 @@ public:
|
||||
static const u32 id_base = 1;
|
||||
static const u32 id_step = 1;
|
||||
static const u32 id_count = 1023;
|
||||
SAVESTATE_INIT_POS(34);
|
||||
|
||||
ElementaryStream(Demuxer* dmux, u32 addr, u32 size, u32 fidMajor, u32 fidMinor, u32 sup1, u32 sup2, vm::ptr<CellDmuxCbEsMsg> cbFunc, u32 cbArg, u32 spec);
|
||||
|
||||
|
||||
+327
-156
@@ -9,6 +9,7 @@
|
||||
#include "Emu/system_config.h"
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/IdManager.h"
|
||||
#include "Emu/RSX/Overlays/overlay_cursor.h"
|
||||
#include "Input/pad_thread.h"
|
||||
|
||||
#include <cmath> // for fmod
|
||||
@@ -94,7 +95,7 @@ public:
|
||||
|
||||
static constexpr auto thread_name = "Gem Thread"sv;
|
||||
|
||||
atomic_t<u32> state = 0;
|
||||
atomic_t<u8> state = 0;
|
||||
|
||||
struct gem_color
|
||||
{
|
||||
@@ -107,6 +108,23 @@ public:
|
||||
g = std::clamp(g_, 0.0f, 1.0f);
|
||||
b = std::clamp(b_, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
static inline const gem_color& get_default_color(u32 gem_num)
|
||||
{
|
||||
static const gem_color gold = gem_color(1.0f, 0.85f, 0.0f);
|
||||
static const gem_color green = gem_color(0.0f, 1.0f, 0.0f);
|
||||
static const gem_color red = gem_color(1.0f, 0.0f, 0.0f);
|
||||
static const gem_color pink = gem_color(0.9f, 0.0f, 0.5f);
|
||||
|
||||
switch (gem_num)
|
||||
{
|
||||
case 0: return green;
|
||||
case 1: return gold;
|
||||
case 2: return red;
|
||||
case 3: return pink;
|
||||
default: fmt::throw_exception("unexpected gem_num %d", gem_num);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct gem_controller
|
||||
@@ -131,6 +149,8 @@ public:
|
||||
u64 calibration_start_us{0}; // The start timestamp of the calibration in microseconds
|
||||
|
||||
static constexpr u64 calibration_time_us = 500000; // The calibration supposedly takes 0.5 seconds (500000 microseconds)
|
||||
|
||||
ENABLE_BITWISE_SERIALIZATION;
|
||||
};
|
||||
|
||||
CellGemAttribute attribute = {};
|
||||
@@ -181,6 +201,11 @@ public:
|
||||
|
||||
void reset_controller(u32 gem_num)
|
||||
{
|
||||
if (gem_num >= CELL_GEM_MAX_NUM)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (g_cfg.io.move)
|
||||
{
|
||||
case move_handler::fake:
|
||||
@@ -194,14 +219,41 @@ public:
|
||||
break;
|
||||
}
|
||||
|
||||
gem_controller& controller = controllers.at(gem_num);
|
||||
controller = {};
|
||||
controller.sphere_rgb = gem_color::get_default_color(gem_num);
|
||||
|
||||
// Assign status and port number
|
||||
if (gem_num < connected_controllers)
|
||||
{
|
||||
controllers[gem_num] = {};
|
||||
controllers[gem_num].status = CELL_GEM_STATUS_READY;
|
||||
controllers[gem_num].port = 7u - gem_num;
|
||||
controller.status = CELL_GEM_STATUS_READY;
|
||||
controller.port = CELL_PAD_MAX_PORT_NUM - gem_num;
|
||||
}
|
||||
}
|
||||
|
||||
gem_config_data() = default;
|
||||
|
||||
SAVESTATE_INIT_POS(15);
|
||||
|
||||
void save(utils::serial& ar)
|
||||
{
|
||||
ar(state);
|
||||
|
||||
if (!state)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GET_OR_USE_SERIALIZATION_VERSION(ar.is_writing(), cellGem);
|
||||
|
||||
ar(attribute, vc_attribute, status_flags, enable_pitch_correction, inertial_counter, controllers
|
||||
, connected_controllers, update_started, camera_frame, memory_ptr, start_timestamp);
|
||||
}
|
||||
|
||||
gem_config_data(utils::serial& ar)
|
||||
{
|
||||
save(ar);
|
||||
}
|
||||
};
|
||||
|
||||
static inline int32_t cellGemGetVideoConvertSize(s32 output_format)
|
||||
@@ -394,6 +446,102 @@ static bool check_gem_num(const u32 gem_num)
|
||||
return gem_num < CELL_GEM_MAX_NUM;
|
||||
}
|
||||
|
||||
static inline void draw_overlay_cursor(u32 gem_num, const gem_config::gem_controller& controller, s32 x_pos, s32 y_pos, s32 x_max, s32 y_max)
|
||||
{
|
||||
const u16 x = static_cast<u16>(x_pos / (x_max / static_cast<f32>(rsx::overlays::overlay::virtual_width)));
|
||||
const u16 y = static_cast<u16>(y_pos / (y_max / static_cast<f32>(rsx::overlays::overlay::virtual_height)));
|
||||
|
||||
// Note: We shouldn't use sphere_rgb here. The game will set it to black in many cases.
|
||||
const gem_config_data::gem_color& rgb = gem_config_data::gem_color::get_default_color(gem_num);
|
||||
const color4f color = { rgb.r, rgb.g, rgb.b, 0.85f };
|
||||
|
||||
rsx::overlays::set_cursor(rsx::overlays::cursor_offset::cell_gem + gem_num, x, y, color, 2'000'000, false);
|
||||
}
|
||||
|
||||
static inline void pos_to_gem_image_state(u32 gem_num, const gem_config::gem_controller& controller, vm::ptr<CellGemImageState>& gem_image_state, s32 x_pos, s32 y_pos, s32 x_max, s32 y_max)
|
||||
{
|
||||
const auto& shared_data = g_fxo->get<gem_camera_shared>();
|
||||
|
||||
if (x_max <= 0) x_max = shared_data.width;
|
||||
if (y_max <= 0) y_max = shared_data.height;
|
||||
|
||||
const f32 scaling_width = x_max / static_cast<f32>(shared_data.width);
|
||||
const f32 scaling_height = y_max / static_cast<f32>(shared_data.height);
|
||||
const f32 mmPerPixel = CELL_GEM_SPHERE_RADIUS_MM / controller.radius;
|
||||
|
||||
// Image coordinates in pixels
|
||||
const f32 image_x = static_cast<f32>(x_pos) / scaling_width;
|
||||
const f32 image_y = static_cast<f32>(y_pos) / scaling_height;
|
||||
|
||||
// Centered image coordinates in pixels
|
||||
const f32 centered_x = image_x - (shared_data.width / 2.f);
|
||||
const f32 centered_y = (shared_data.height / 2.f) - image_y; // Image coordinates increase downwards, so we have to invert this
|
||||
|
||||
// Camera coordinates in mm (centered, so it's the same as world coordinates)
|
||||
const f32 camera_x = centered_x * mmPerPixel;
|
||||
const f32 camera_y = centered_y * mmPerPixel;
|
||||
|
||||
// Image coordinates in pixels
|
||||
gem_image_state->u = image_x;
|
||||
gem_image_state->v = image_y;
|
||||
|
||||
// Projected camera coordinates in mm
|
||||
gem_image_state->projectionx = camera_x / controller.distance;
|
||||
gem_image_state->projectiony = camera_y / controller.distance;
|
||||
|
||||
if (g_cfg.io.show_move_cursor)
|
||||
{
|
||||
draw_overlay_cursor(gem_num, controller, x_pos, y_pos, x_max, y_max);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void pos_to_gem_state(u32 gem_num, const gem_config::gem_controller& controller, vm::ptr<CellGemState>& gem_state, s32 x_pos, s32 y_pos, s32 x_max, s32 y_max)
|
||||
{
|
||||
const auto& shared_data = g_fxo->get<gem_camera_shared>();
|
||||
|
||||
if (x_max <= 0) x_max = shared_data.width;
|
||||
if (y_max <= 0) y_max = shared_data.height;
|
||||
|
||||
const f32 scaling_width = x_max / static_cast<f32>(shared_data.width);
|
||||
const f32 scaling_height = y_max / static_cast<f32>(shared_data.height);
|
||||
const f32 mmPerPixel = CELL_GEM_SPHERE_RADIUS_MM / controller.radius;
|
||||
|
||||
// Image coordinates in pixels
|
||||
const f32 image_x = static_cast<f32>(x_pos) / scaling_width;
|
||||
const f32 image_y = static_cast<f32>(y_pos) / scaling_height;
|
||||
|
||||
// Centered image coordinates in pixels
|
||||
const f32 centered_x = image_x - (shared_data.width / 2.f);
|
||||
const f32 centered_y = (shared_data.height / 2.f) - image_y; // Image coordinates increase downwards, so we have to invert this
|
||||
|
||||
// Camera coordinates in mm (centered, so it's the same as world coordinates)
|
||||
const f32 camera_x = centered_x * mmPerPixel;
|
||||
const f32 camera_y = centered_y * mmPerPixel;
|
||||
|
||||
// World coordinates in mm
|
||||
gem_state->pos[0] = camera_x;
|
||||
gem_state->pos[1] = camera_y;
|
||||
gem_state->pos[2] = static_cast<f32>(controller.distance);
|
||||
gem_state->pos[3] = 0.f;
|
||||
|
||||
gem_state->quat[0] = 320.f - image_x;
|
||||
gem_state->quat[1] = (y_pos / scaling_width) - 180.f;
|
||||
gem_state->quat[2] = 1200.f;
|
||||
|
||||
// TODO: calculate handle position based on our world coordinate and the angles
|
||||
gem_state->handle_pos[0] = camera_x;
|
||||
gem_state->handle_pos[1] = camera_y;
|
||||
gem_state->handle_pos[2] = static_cast<f32>(controller.distance + 10);
|
||||
gem_state->handle_pos[3] = 0.f;
|
||||
|
||||
if (g_cfg.io.show_move_cursor)
|
||||
{
|
||||
draw_overlay_cursor(gem_num, controller, x_pos, y_pos, x_max, y_max);
|
||||
}
|
||||
}
|
||||
|
||||
extern bool is_input_allowed();
|
||||
|
||||
/**
|
||||
* \brief Maps Move controller data (digital buttons, and analog Trigger data) to DS3 pad input.
|
||||
* Unavoidably buttons conflict with DS3 mappings, which is problematic for some games.
|
||||
@@ -402,80 +550,146 @@ static bool check_gem_num(const u32 gem_num)
|
||||
* \param analog_t Analog value of Move's Trigger. Currently mapped to R2.
|
||||
* \return true on success, false if port_no controller is invalid
|
||||
*/
|
||||
static bool ds3_input_to_pad(const u32 port_no, be_t<u16>& digital_buttons, be_t<u16>& analog_t)
|
||||
static void ds3_input_to_pad(const u32 port_no, be_t<u16>& digital_buttons, be_t<u16>& analog_t)
|
||||
{
|
||||
digital_buttons = 0;
|
||||
analog_t = 0;
|
||||
|
||||
if (!is_input_allowed())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard lock(pad::g_pad_mutex);
|
||||
|
||||
const auto handler = pad::get_current_handler();
|
||||
auto& pad = handler->GetPads()[port_no];
|
||||
const auto& pad = handler->GetPads().at(port_no);
|
||||
|
||||
if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
|
||||
return false;
|
||||
|
||||
for (Button& button : pad->m_buttons)
|
||||
{
|
||||
// here we check btns, and set pad accordingly
|
||||
if (button.m_offset == CELL_PAD_BTN_OFFSET_DIGITAL2)
|
||||
{
|
||||
if (button.m_pressed) pad->m_digital_2 |= button.m_outKeyCode;
|
||||
else pad->m_digital_2 &= ~button.m_outKeyCode;
|
||||
return;
|
||||
}
|
||||
|
||||
for (const Button& button : pad->m_buttons)
|
||||
{
|
||||
if (!button.m_pressed)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// here we check btns, and set pad accordingly
|
||||
if (button.m_offset == CELL_PAD_BTN_OFFSET_DIGITAL1)
|
||||
{
|
||||
switch (button.m_outKeyCode)
|
||||
{
|
||||
case CELL_PAD_CTRL_START:
|
||||
digital_buttons |= CELL_GEM_CTRL_START;
|
||||
break;
|
||||
case CELL_PAD_CTRL_SELECT:
|
||||
digital_buttons |= CELL_GEM_CTRL_SELECT;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (button.m_offset == CELL_PAD_BTN_OFFSET_DIGITAL2)
|
||||
{
|
||||
switch (button.m_outKeyCode)
|
||||
{
|
||||
case CELL_PAD_CTRL_SQUARE:
|
||||
pad->m_press_square = button.m_value;
|
||||
digital_buttons |= CELL_GEM_CTRL_SQUARE;
|
||||
break;
|
||||
case CELL_PAD_CTRL_CROSS:
|
||||
pad->m_press_cross = button.m_value;
|
||||
digital_buttons |= CELL_GEM_CTRL_CROSS;
|
||||
break;
|
||||
case CELL_PAD_CTRL_CIRCLE:
|
||||
pad->m_press_circle = button.m_value;
|
||||
digital_buttons |= CELL_GEM_CTRL_CIRCLE;
|
||||
break;
|
||||
case CELL_PAD_CTRL_TRIANGLE:
|
||||
pad->m_press_triangle = button.m_value;
|
||||
digital_buttons |= CELL_GEM_CTRL_TRIANGLE;
|
||||
break;
|
||||
case CELL_PAD_CTRL_R1:
|
||||
pad->m_press_R1 = button.m_value;
|
||||
break;
|
||||
case CELL_PAD_CTRL_L1:
|
||||
pad->m_press_L1 = button.m_value;
|
||||
digital_buttons |= CELL_GEM_CTRL_MOVE;
|
||||
break;
|
||||
case CELL_PAD_CTRL_R2:
|
||||
pad->m_press_R2 = button.m_value;
|
||||
digital_buttons |= CELL_GEM_CTRL_T;
|
||||
analog_t = std::max<u16>(analog_t, button.m_value);
|
||||
break;
|
||||
case CELL_PAD_CTRL_L2:
|
||||
pad->m_press_L2 = button.m_value;
|
||||
default:
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
digital_buttons = 0;
|
||||
constexpr u16 ds3_max_x = 255;
|
||||
constexpr u16 ds3_max_y = 255;
|
||||
|
||||
// map the Move key to R1 and the Trigger to R2
|
||||
static inline void ds3_get_stick_values(const std::shared_ptr<Pad>& pad, s32& x_pos, s32& y_pos)
|
||||
{
|
||||
x_pos = 0;
|
||||
y_pos = 0;
|
||||
|
||||
if (pad->m_press_R1)
|
||||
digital_buttons |= CELL_GEM_CTRL_MOVE;
|
||||
if (pad->m_press_R2)
|
||||
digital_buttons |= CELL_GEM_CTRL_T;
|
||||
for (const AnalogStick& stick : pad->m_sticks)
|
||||
{
|
||||
switch (stick.m_offset)
|
||||
{
|
||||
case CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X:
|
||||
x_pos = stick.m_value;
|
||||
break;
|
||||
case CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y:
|
||||
y_pos = stick.m_value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pad->m_press_cross)
|
||||
digital_buttons |= CELL_GEM_CTRL_CROSS;
|
||||
if (pad->m_press_circle)
|
||||
digital_buttons |= CELL_GEM_CTRL_CIRCLE;
|
||||
if (pad->m_press_square)
|
||||
digital_buttons |= CELL_GEM_CTRL_SQUARE;
|
||||
if (pad->m_press_triangle)
|
||||
digital_buttons |= CELL_GEM_CTRL_TRIANGLE;
|
||||
if (pad->m_digital_1)
|
||||
digital_buttons |= CELL_GEM_CTRL_SELECT;
|
||||
if (pad->m_digital_2)
|
||||
digital_buttons |= CELL_GEM_CTRL_START;
|
||||
static void ds3_pos_to_gem_image_state(const u32 port_no, const gem_config::gem_controller& controller, vm::ptr<CellGemImageState>& gem_image_state)
|
||||
{
|
||||
if (!is_input_allowed())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
analog_t = pad->m_press_R2;
|
||||
std::lock_guard lock(pad::g_pad_mutex);
|
||||
|
||||
return true;
|
||||
const auto handler = pad::get_current_handler();
|
||||
const auto& pad = handler->GetPads().at(port_no);
|
||||
|
||||
if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
s32 ds3_pos_x, ds3_pos_y;
|
||||
ds3_get_stick_values(pad, ds3_pos_x, ds3_pos_y);
|
||||
|
||||
pos_to_gem_image_state(port_no, controller, gem_image_state, ds3_pos_x, ds3_pos_y, ds3_max_x, ds3_max_y);
|
||||
}
|
||||
|
||||
static void ds3_pos_to_gem_state(const u32 port_no, const gem_config::gem_controller& controller, vm::ptr<CellGemState>& gem_state)
|
||||
{
|
||||
if (!is_input_allowed())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard lock(pad::g_pad_mutex);
|
||||
|
||||
const auto handler = pad::get_current_handler();
|
||||
const auto& pad = handler->GetPads().at(port_no);
|
||||
|
||||
if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
s32 ds3_pos_x, ds3_pos_y;
|
||||
ds3_get_stick_values(pad, ds3_pos_x, ds3_pos_y);
|
||||
|
||||
pos_to_gem_state(port_no, controller, gem_state, ds3_pos_x, ds3_pos_y, ds3_max_x, ds3_max_y);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -486,15 +700,24 @@ static bool ds3_input_to_pad(const u32 port_no, be_t<u16>& digital_buttons, be_t
|
||||
* \param ext External data to modify
|
||||
* \return true on success, false if port_no controller is invalid
|
||||
*/
|
||||
static bool ds3_input_to_ext(const u32 port_no, const gem_config::gem_controller& controller, CellGemExtPortData& ext)
|
||||
static void ds3_input_to_ext(const u32 port_no, const gem_config::gem_controller& controller, CellGemExtPortData& ext)
|
||||
{
|
||||
ext = {};
|
||||
|
||||
if (!is_input_allowed())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard lock(pad::g_pad_mutex);
|
||||
|
||||
const auto handler = pad::get_current_handler();
|
||||
auto& pad = handler->GetPads()[port_no];
|
||||
const auto& pad = handler->GetPads().at(port_no);
|
||||
|
||||
if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
|
||||
return false;
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ext.status = 0; // CELL_GEM_EXT_CONNECTED | CELL_GEM_EXT_EXT0 | CELL_GEM_EXT_EXT1
|
||||
ext.analog_left_x = pad->m_analog_left_x; // HACK: these pad members are actually only set in cellPad
|
||||
@@ -513,8 +736,6 @@ static bool ds3_input_to_ext(const u32 port_no, const gem_config::gem_controller
|
||||
// xxxxx010: Firing mode selector is in position 2.
|
||||
// xxxxx100: Firing mode selector is in position 3.
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -528,16 +749,27 @@ static bool ds3_input_to_ext(const u32 port_no, const gem_config::gem_controller
|
||||
*/
|
||||
static bool mouse_input_to_pad(const u32 mouse_no, be_t<u16>& digital_buttons, be_t<u16>& analog_t)
|
||||
{
|
||||
digital_buttons = 0;
|
||||
analog_t = 0;
|
||||
|
||||
if (!is_input_allowed())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto& handler = g_fxo->get<MouseHandlerBase>();
|
||||
|
||||
std::scoped_lock lock(handler.mutex);
|
||||
|
||||
// Make sure that the mouse handler is initialized
|
||||
handler.Init(std::min<u32>(g_fxo->get<gem_config>().attribute.max_connect, CELL_GEM_MAX_NUM));
|
||||
|
||||
if (mouse_no >= handler.GetMice().size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& mouse_data = handler.GetMice().at(0);
|
||||
const auto& mouse_data = handler.GetMice().at(mouse_no);
|
||||
const auto is_pressed = [&mouse_data](MouseButtonCodes button) -> bool { return !!(mouse_data.buttons & button); };
|
||||
|
||||
digital_buttons = 0;
|
||||
@@ -571,102 +803,52 @@ static bool mouse_input_to_pad(const u32 mouse_no, be_t<u16>& digital_buttons, b
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool mouse_pos_to_gem_image_state(const u32 mouse_no, const gem_config::gem_controller& controller, vm::ptr<CellGemImageState>& gem_image_state)
|
||||
static void mouse_pos_to_gem_image_state(const u32 mouse_no, const gem_config::gem_controller& controller, vm::ptr<CellGemImageState>& gem_image_state)
|
||||
{
|
||||
if (!gem_image_state || !is_input_allowed())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto& handler = g_fxo->get<MouseHandlerBase>();
|
||||
|
||||
std::scoped_lock lock(handler.mutex);
|
||||
|
||||
if (!gem_image_state || mouse_no >= handler.GetMice().size())
|
||||
// Make sure that the mouse handler is initialized
|
||||
handler.Init(std::min<u32>(g_fxo->get<gem_config>().attribute.max_connect, CELL_GEM_MAX_NUM));
|
||||
|
||||
if (mouse_no >= handler.GetMice().size())
|
||||
{
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& mouse = handler.GetMice().at(0);
|
||||
const auto& shared_data = g_fxo->get<gem_camera_shared>();
|
||||
const auto& mouse = handler.GetMice().at(mouse_no);
|
||||
|
||||
s32 mouse_width = mouse.x_max;
|
||||
if (mouse_width <= 0) mouse_width = shared_data.width;
|
||||
s32 mouse_height = mouse.y_max;
|
||||
if (mouse_height <= 0) mouse_height = shared_data.height;
|
||||
const f32 scaling_width = mouse_width / static_cast<f32>(shared_data.width);
|
||||
const f32 scaling_height = mouse_height / static_cast<f32>(shared_data.height);
|
||||
const f32 mmPerPixel = CELL_GEM_SPHERE_RADIUS_MM / controller.radius;
|
||||
|
||||
// Image coordinates in pixels
|
||||
const f32 image_x = static_cast<f32>(mouse.x_pos) / scaling_width;
|
||||
const f32 image_y = static_cast<f32>(mouse.y_pos) / scaling_height;
|
||||
|
||||
// Centered image coordinates in pixels
|
||||
const f32 centered_x = image_x - (shared_data.width / 2.f);
|
||||
const f32 centered_y = (shared_data.height / 2.f) - image_y; // Image coordinates increase downwards, so we have to invert this
|
||||
|
||||
// Camera coordinates in mm (centered, so it's the same as world coordinates)
|
||||
const f32 camera_x = centered_x * mmPerPixel;
|
||||
const f32 camera_y = centered_y * mmPerPixel;
|
||||
|
||||
// Image coordinates in pixels
|
||||
gem_image_state->u = image_x;
|
||||
gem_image_state->v = image_y;
|
||||
|
||||
// Projected camera coordinates in mm
|
||||
gem_image_state->projectionx = camera_x / controller.distance;
|
||||
gem_image_state->projectiony = camera_y / controller.distance;
|
||||
|
||||
return true;
|
||||
pos_to_gem_image_state(mouse_no, controller, gem_image_state, mouse.x_pos, mouse.y_pos, mouse.x_max, mouse.y_max);
|
||||
}
|
||||
|
||||
static bool mouse_pos_to_gem_state(const u32 mouse_no, const gem_config::gem_controller& controller, vm::ptr<CellGemState>& gem_state)
|
||||
static void mouse_pos_to_gem_state(const u32 mouse_no, const gem_config::gem_controller& controller, vm::ptr<CellGemState>& gem_state)
|
||||
{
|
||||
if (!gem_state || !is_input_allowed())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto& handler = g_fxo->get<MouseHandlerBase>();
|
||||
|
||||
std::scoped_lock lock(handler.mutex);
|
||||
|
||||
if (!gem_state || mouse_no >= handler.GetMice().size())
|
||||
// Make sure that the mouse handler is initialized
|
||||
handler.Init(std::min<u32>(g_fxo->get<gem_config>().attribute.max_connect, CELL_GEM_MAX_NUM));
|
||||
|
||||
if (mouse_no >= handler.GetMice().size())
|
||||
{
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& mouse = handler.GetMice().at(0);
|
||||
const auto& shared_data = g_fxo->get<gem_camera_shared>();
|
||||
|
||||
s32 mouse_width = mouse.x_max;
|
||||
if (mouse_width <= 0) mouse_width = shared_data.width;
|
||||
s32 mouse_height = mouse.y_max;
|
||||
if (mouse_height <= 0) mouse_height = shared_data.height;
|
||||
const f32 scaling_width = mouse_width / static_cast<f32>(shared_data.width);
|
||||
const f32 scaling_height = mouse_height / static_cast<f32>(shared_data.height);
|
||||
const f32 mmPerPixel = CELL_GEM_SPHERE_RADIUS_MM / controller.radius;
|
||||
const auto& mouse = handler.GetMice().at(mouse_no);
|
||||
|
||||
// Image coordinates in pixels
|
||||
const f32 image_x = static_cast<f32>(mouse.x_pos) / scaling_width;
|
||||
const f32 image_y = static_cast<f32>(mouse.y_pos) / scaling_height;
|
||||
|
||||
// Centered image coordinates in pixels
|
||||
const f32 centered_x = image_x - (shared_data.width / 2.f);
|
||||
const f32 centered_y = (shared_data.height / 2.f) - image_y; // Image coordinates increase downwards, so we have to invert this
|
||||
|
||||
// Camera coordinates in mm (centered, so it's the same as world coordinates)
|
||||
const f32 camera_x = centered_x * mmPerPixel;
|
||||
const f32 camera_y = centered_y * mmPerPixel;
|
||||
|
||||
// World coordinates in mm
|
||||
gem_state->pos[0] = camera_x;
|
||||
gem_state->pos[1] = camera_y;
|
||||
gem_state->pos[2] = static_cast<f32>(controller.distance);
|
||||
gem_state->pos[3] = 0.f;
|
||||
|
||||
gem_state->quat[0] = 320.f - image_x;
|
||||
gem_state->quat[1] = (mouse.y_pos / scaling_width) - 180.f;
|
||||
gem_state->quat[2] = 1200.f;
|
||||
|
||||
// TODO: calculate handle position based on our world coordinate and the angles
|
||||
gem_state->handle_pos[0] = camera_x;
|
||||
gem_state->handle_pos[1] = camera_y;
|
||||
gem_state->handle_pos[2] = static_cast<f32>(controller.distance + 10);
|
||||
gem_state->handle_pos[3] = 0.f;
|
||||
|
||||
return true;
|
||||
pos_to_gem_state(mouse_no, controller, gem_state, mouse.x_pos, mouse.y_pos, mouse.x_max, mouse.y_max);
|
||||
}
|
||||
|
||||
// *********************
|
||||
@@ -696,11 +878,8 @@ error_code cellGemCalibrate(u32 gem_num)
|
||||
return CELL_EBUSY;
|
||||
}
|
||||
|
||||
if (g_cfg.io.move == move_handler::fake || g_cfg.io.move == move_handler::mouse)
|
||||
{
|
||||
gem.controllers[gem_num].is_calibrating = true;
|
||||
gem.controllers[gem_num].calibration_start_us = get_guest_system_time();
|
||||
}
|
||||
gem.controllers[gem_num].is_calibrating = true;
|
||||
gem.controllers[gem_num].calibration_start_us = get_guest_system_time();
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -854,7 +1033,7 @@ error_code cellGemEnd(ppu_thread& ppu)
|
||||
|
||||
if (gem.state.compare_and_swap_test(1, 0))
|
||||
{
|
||||
if (u32 addr = gem.memory_ptr)
|
||||
if (u32 addr = std::exchange(gem.memory_ptr, 0))
|
||||
{
|
||||
sys_memory_free(ppu, addr);
|
||||
}
|
||||
@@ -1051,6 +1230,8 @@ error_code cellGemGetImageState(u32 gem_num, vm::ptr<CellGemImageState> gem_imag
|
||||
return CELL_GEM_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
*gem_image_state = {};
|
||||
|
||||
if (g_cfg.io.move == move_handler::fake || g_cfg.io.move == move_handler::mouse)
|
||||
{
|
||||
auto& shared_data = g_fxo->get<gem_camera_shared>();
|
||||
@@ -1063,10 +1244,7 @@ error_code cellGemGetImageState(u32 gem_num, vm::ptr<CellGemImageState> gem_imag
|
||||
|
||||
if (g_cfg.io.move == move_handler::fake)
|
||||
{
|
||||
gem_image_state->u = 0;
|
||||
gem_image_state->v = 0;
|
||||
gem_image_state->projectionx = 1;
|
||||
gem_image_state->projectiony = 1;
|
||||
ds3_pos_to_gem_image_state(gem_num, gem.controllers[gem_num], gem_image_state);
|
||||
}
|
||||
else if (g_cfg.io.move == move_handler::mouse)
|
||||
{
|
||||
@@ -1100,6 +1278,8 @@ error_code cellGemGetInertialState(u32 gem_num, u32 state_flag, u64 timestamp, v
|
||||
return CELL_GEM_TIME_OUT_OF_RANGE;
|
||||
}
|
||||
|
||||
*inertial_state = {};
|
||||
|
||||
if (g_cfg.io.move == move_handler::fake || g_cfg.io.move == move_handler::mouse)
|
||||
{
|
||||
ds3_input_to_ext(gem_num, gem.controllers[gem_num], inertial_state->ext);
|
||||
@@ -1187,7 +1367,7 @@ error_code cellGemGetRGB(u32 gem_num, vm::ptr<float> r, vm::ptr<float> g, vm::pt
|
||||
return CELL_GEM_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
auto& sphere_color = gem.controllers[gem_num].sphere_rgb;
|
||||
const gem_config_data::gem_color& sphere_color = gem.controllers[gem_num].sphere_rgb;
|
||||
*r = sphere_color.r;
|
||||
*g = sphere_color.g;
|
||||
*b = sphere_color.b;
|
||||
@@ -1260,6 +1440,8 @@ error_code cellGemGetState(u32 gem_num, u32 flag, u64 time_parameter, vm::ptr<Ce
|
||||
return CELL_GEM_TIME_OUT_OF_RANGE;
|
||||
}
|
||||
|
||||
*gem_state = {};
|
||||
|
||||
if (g_cfg.io.move == move_handler::fake || g_cfg.io.move == move_handler::mouse)
|
||||
{
|
||||
ds3_input_to_ext(gem_num, gem.controllers[gem_num], gem_state->ext);
|
||||
@@ -1269,7 +1451,6 @@ error_code cellGemGetState(u32 gem_num, u32 flag, u64 time_parameter, vm::ptr<Ce
|
||||
if (gem.controllers[gem_num].enabled_tracking)
|
||||
tracking_flags |= CELL_GEM_TRACKING_FLAG_POSITION_TRACKED;
|
||||
|
||||
*gem_state = {};
|
||||
gem_state->tracking_flags = tracking_flags;
|
||||
gem_state->timestamp = (get_guest_system_time() - gem.start_timestamp);
|
||||
gem_state->camera_pitch_angle = 0.f;
|
||||
@@ -1278,6 +1459,7 @@ error_code cellGemGetState(u32 gem_num, u32 flag, u64 time_parameter, vm::ptr<Ce
|
||||
if (g_cfg.io.move == move_handler::fake)
|
||||
{
|
||||
ds3_input_to_pad(gem_num, gem_state->pad.digitalbuttons, gem_state->pad.analog_T);
|
||||
ds3_pos_to_gem_state(gem_num, gem.controllers[gem_num], gem_state);
|
||||
}
|
||||
else if (g_cfg.io.move == move_handler::mouse)
|
||||
{
|
||||
@@ -1457,14 +1639,6 @@ error_code cellGemInit(ppu_thread& ppu, vm::cptr<CellGemAttribute> attribute)
|
||||
gem.status_flags = 0;
|
||||
gem.attribute = *attribute;
|
||||
|
||||
if (g_cfg.io.move == move_handler::mouse)
|
||||
{
|
||||
// init mouse handler
|
||||
auto& handler = g_fxo->get<MouseHandlerBase>();
|
||||
|
||||
handler.Init(std::min<u32>(attribute->max_connect, CELL_GEM_MAX_NUM));
|
||||
}
|
||||
|
||||
for (int gem_num = 0; gem_num < CELL_GEM_MAX_NUM; gem_num++)
|
||||
{
|
||||
gem.reset_controller(gem_num);
|
||||
@@ -1494,16 +1668,13 @@ error_code cellGemInvalidateCalibration(s32 gem_num)
|
||||
return CELL_GEM_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (g_cfg.io.move == move_handler::fake || g_cfg.io.move == move_handler::mouse)
|
||||
{
|
||||
gem.controllers[gem_num].calibrated_magnetometer = false;
|
||||
gem.controllers[gem_num].calibrated_magnetometer = false;
|
||||
|
||||
// TODO: does this really stop an ongoing calibration ?
|
||||
gem.controllers[gem_num].is_calibrating = false;
|
||||
gem.controllers[gem_num].calibration_start_us = 0;
|
||||
// TODO: does this really stop an ongoing calibration ?
|
||||
gem.controllers[gem_num].is_calibrating = false;
|
||||
gem.controllers[gem_num].calibration_start_us = 0;
|
||||
|
||||
// TODO: gem.status_flags (probably not changed)
|
||||
}
|
||||
// TODO: gem.status_flags (probably not changed)
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -171,6 +171,8 @@ struct CellGemAttribute
|
||||
be_t<u32> memory_ptr;
|
||||
be_t<u32> spurs_addr;
|
||||
u8 spu_priorities[8];
|
||||
|
||||
ENABLE_BITWISE_SERIALIZATION;
|
||||
};
|
||||
|
||||
struct CellGemCameraState
|
||||
@@ -180,6 +182,8 @@ struct CellGemCameraState
|
||||
be_t<f32> gain;
|
||||
be_t<f32> pitch_angle;
|
||||
be_t<f32> pitch_angle_estimate;
|
||||
|
||||
ENABLE_BITWISE_SERIALIZATION;
|
||||
};
|
||||
|
||||
struct CellGemExtPortData
|
||||
@@ -270,4 +274,6 @@ struct CellGemVideoConvertAttribute
|
||||
vm::bptr<u8> buffer_memory;
|
||||
vm::bptr<u8> video_data_out;
|
||||
u8 alpha;
|
||||
|
||||
ENABLE_BITWISE_SERIALIZATION;
|
||||
};
|
||||
|
||||
@@ -39,61 +39,134 @@ void fmt_class_string<CellGifDecError>::format(std::string& out, u64 arg)
|
||||
});
|
||||
}
|
||||
|
||||
// cellGifDec aliases (only for cellGifDec.cpp)
|
||||
using PPMainHandle = vm::pptr<GifDecoder>;
|
||||
using PMainHandle = vm::ptr<GifDecoder>;
|
||||
using PThreadInParam = vm::cptr<CellGifDecThreadInParam>;
|
||||
using PThreadOutParam = vm::ptr<CellGifDecThreadOutParam>;
|
||||
using PExtThreadInParam = vm::cptr<CellGifDecExtThreadInParam>;
|
||||
using PExtThreadOutParam = vm::ptr<CellGifDecExtThreadOutParam>;
|
||||
using PPSubHandle = vm::pptr<GifStream>;
|
||||
using PSubHandle = vm::ptr<GifStream>;
|
||||
using PSrc = vm::cptr<CellGifDecSrc>;
|
||||
using POpenInfo = vm::ptr<CellGifDecOpnInfo>;
|
||||
using PInfo = vm::ptr<CellGifDecInfo>;
|
||||
using PInParam = vm::cptr<CellGifDecInParam>;
|
||||
using POutParam = vm::ptr<CellGifDecOutParam>;
|
||||
using PDataCtrlParam = vm::cptr<CellGifDecDataCtrlParam>;
|
||||
using PDataOutInfo = vm::ptr<CellGifDecDataOutInfo>;
|
||||
|
||||
error_code cellGifDecCreate(PPMainHandle mainHandle, PThreadInParam threadInParam, PThreadOutParam threadOutParam)
|
||||
error_code cellGifDecCreate(vm::ptr<GifDecoder> mainHandle, vm::cptr<CellGifDecThreadInParam> threadInParam, vm::ptr<CellGifDecThreadOutParam> threadOutParam)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellGifDec);
|
||||
cellGifDec.todo("cellGifDecCreate(mainHandle=*0x%x, threadInParam=*0x%x, threadOutParam=*0x%x)", mainHandle, threadInParam, threadOutParam);
|
||||
|
||||
if (!mainHandle)
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_ARG;
|
||||
}
|
||||
|
||||
*mainHandle = {};
|
||||
|
||||
if (!threadOutParam || !threadInParam || !threadInParam->cbCtrlMallocFunc || !threadInParam->cbCtrlFreeFunc ||
|
||||
(threadInParam->spuThreadEnable != CELL_GIFDEC_SPU_THREAD_DISABLE &&
|
||||
(threadInParam->spuThreadEnable != CELL_GIFDEC_SPU_THREAD_ENABLE ||
|
||||
threadInParam->ppuThreadPriority > 3071 ||
|
||||
threadInParam->spuThreadPriority > 255)))
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_ARG;
|
||||
}
|
||||
|
||||
threadOutParam->gifCodecVersion = 0x240000;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellGifDecExtCreate(PPMainHandle mainHandle, PThreadInParam threadInParam, PThreadOutParam threadOutParam, PExtThreadInParam extThreadInParam, PExtThreadOutParam extThreadOutParam)
|
||||
error_code cellGifDecExtCreate(vm::ptr<GifDecoder> mainHandle, vm::cptr<CellGifDecThreadInParam> threadInParam, vm::ptr<CellGifDecThreadOutParam> threadOutParam, vm::cptr<CellGifDecExtThreadInParam> extThreadInParam, vm::ptr<CellGifDecExtThreadOutParam> extThreadOutParam)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellGifDec);
|
||||
cellGifDec.todo("cellGifDecExtCreate(mainHandle=*0x%x, threadInParam=*0x%x, threadOutParam=*0x%x, extThreadInParam=*0x%x, extThreadOutParam=*0x%x)", mainHandle, threadInParam, threadOutParam, extThreadInParam, extThreadOutParam);
|
||||
|
||||
if (!mainHandle)
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_ARG;
|
||||
}
|
||||
|
||||
*mainHandle = {};
|
||||
|
||||
if (!threadOutParam || !extThreadOutParam || !extThreadInParam || !threadInParam || !threadInParam->cbCtrlMallocFunc || !threadInParam->cbCtrlFreeFunc ||
|
||||
(threadInParam->spuThreadEnable != CELL_GIFDEC_SPU_THREAD_DISABLE && threadInParam->spuThreadEnable != CELL_GIFDEC_SPU_THREAD_ENABLE))
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_ARG;
|
||||
}
|
||||
|
||||
if (threadInParam->spuThreadEnable == CELL_GIFDEC_SPU_THREAD_ENABLE && !extThreadInParam->spurs)
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_ARG;
|
||||
}
|
||||
|
||||
if (extThreadInParam->maxContention == 0u || extThreadInParam->maxContention >= 8u)
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_ARG;
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < 8; i++)
|
||||
{
|
||||
if (extThreadInParam->priority[i] > 15)
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_ARG;
|
||||
}
|
||||
}
|
||||
|
||||
threadOutParam->gifCodecVersion = 0x240000;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellGifDecOpen(PMainHandle mainHandle, PPSubHandle subHandle, PSrc src, POpenInfo openInfo)
|
||||
error_code cellGifDecOpen(vm::ptr<GifDecoder> mainHandle, vm::pptr<GifStream> subHandle, vm::cptr<CellGifDecSrc> src, vm::ptr<CellGifDecOpnInfo> openInfo)
|
||||
{
|
||||
cellGifDec.warning("cellGifDecOpen(mainHandle=*0x%x, subHandle=**0x%x, src=*0x%x, openInfo=*0x%x)", mainHandle, subHandle, src, openInfo);
|
||||
|
||||
GifStream current_subHandle;
|
||||
if (!mainHandle || !subHandle)
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_ARG;
|
||||
}
|
||||
|
||||
if (false) // TODO
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_SEQ;
|
||||
}
|
||||
|
||||
if (!openInfo || !src)
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_ARG;
|
||||
}
|
||||
|
||||
GifStream current_subHandle{};
|
||||
current_subHandle.fd = 0;
|
||||
current_subHandle.src = *src;
|
||||
|
||||
switch (src->srcSelect)
|
||||
{
|
||||
case CELL_GIFDEC_BUFFER:
|
||||
{
|
||||
if (!src->streamPtr || !src->streamSize)
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_ARG;
|
||||
}
|
||||
|
||||
current_subHandle.fileSize = src->streamSize;
|
||||
break;
|
||||
|
||||
}
|
||||
case CELL_GIFDEC_FILE:
|
||||
{
|
||||
if (!src->fileName)
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_OPEN_FILE;
|
||||
}
|
||||
|
||||
// Get file descriptor and size
|
||||
const auto real_path = vfs::get(src->fileName.get_ptr());
|
||||
const std::string real_path = vfs::get(src->fileName.get_ptr());
|
||||
fs::file file_s(real_path);
|
||||
if (!file_s) return CELL_GIFDEC_ERROR_OPEN_FILE;
|
||||
if (!file_s)
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_OPEN_FILE;
|
||||
}
|
||||
|
||||
if (src->fileOffset < 0)
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_ARG;
|
||||
}
|
||||
|
||||
current_subHandle.fileSize = file_s.size();
|
||||
current_subHandle.fd = idm::make<lv2_fs_object, lv2_file>(src->fileName.get_ptr(), std::move(file_s), 0, 0, real_path);
|
||||
break;
|
||||
}
|
||||
default: break; // TODO
|
||||
default:
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_ARG;
|
||||
}
|
||||
}
|
||||
|
||||
subHandle->set(vm::alloc(sizeof(GifStream), vm::main));
|
||||
@@ -103,16 +176,103 @@ error_code cellGifDecOpen(PMainHandle mainHandle, PPSubHandle subHandle, PSrc sr
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellGifDecExtOpen()
|
||||
error_code cellGifDecExtOpen(vm::ptr<GifDecoder> mainHandle, vm::pptr<GifStream> subHandle, vm::cptr<CellGifDecSrc> src, vm::ptr<CellGifDecOpnInfo> openInfo, vm::cptr<CellGifDecCbCtrlStrm> cbCtrlStrm)
|
||||
{
|
||||
cellGifDec.todo("cellGifDecExtOpen()");
|
||||
cellGifDec.todo("cellGifDecExtOpen(mainHandle=*0x%x, subHandle=*0x%x, src=*0x%x, openInfo=*0x%x, cbCtrlStrm=*0x%x)", mainHandle, subHandle, src, openInfo, cbCtrlStrm);
|
||||
|
||||
if (!mainHandle || !subHandle)
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_ARG;
|
||||
}
|
||||
|
||||
if (false) // TODO
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_SEQ;
|
||||
}
|
||||
|
||||
if (!openInfo || !src)
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_ARG;
|
||||
}
|
||||
|
||||
GifStream current_subHandle{};
|
||||
current_subHandle.fd = 0;
|
||||
current_subHandle.src = *src;
|
||||
|
||||
switch (src->srcSelect)
|
||||
{
|
||||
case CELL_GIFDEC_BUFFER:
|
||||
{
|
||||
if (!src->streamPtr || !src->streamSize)
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_ARG;
|
||||
}
|
||||
|
||||
current_subHandle.fileSize = src->streamSize;
|
||||
break;
|
||||
}
|
||||
case CELL_GIFDEC_FILE:
|
||||
{
|
||||
if (!src->fileName)
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_OPEN_FILE;
|
||||
}
|
||||
|
||||
// Get file descriptor and size
|
||||
const std::string real_path = vfs::get(src->fileName.get_ptr());
|
||||
fs::file file_s(real_path);
|
||||
if (!file_s)
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_OPEN_FILE;
|
||||
}
|
||||
|
||||
if (src->fileOffset < 0)
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_ARG;
|
||||
}
|
||||
|
||||
current_subHandle.fileSize = file_s.size();
|
||||
current_subHandle.fd = idm::make<lv2_fs_object, lv2_file>(src->fileName.get_ptr(), std::move(file_s), 0, 0, real_path);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_ARG;
|
||||
}
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellGifDecReadHeader(PMainHandle mainHandle, PSubHandle subHandle, PInfo info)
|
||||
error_code cellGifDecReadHeader(vm::ptr<GifDecoder> mainHandle, vm::ptr<GifStream> subHandle, vm::ptr<CellGifDecInfo> info)
|
||||
{
|
||||
cellGifDec.warning("cellGifDecReadHeader(mainHandle=*0x%x, subHandle=*0x%x, info=*0x%x)", mainHandle, subHandle, info);
|
||||
|
||||
if (!mainHandle)
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_ARG;
|
||||
}
|
||||
|
||||
if (false) // TODO: check main handle
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_SEQ;
|
||||
}
|
||||
|
||||
if (!subHandle)
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_ARG;
|
||||
}
|
||||
|
||||
if (false) // TODO: check sub handle
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_SEQ;
|
||||
}
|
||||
|
||||
if (!info)
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_ARG;
|
||||
}
|
||||
|
||||
const u32& fd = subHandle->fd;
|
||||
CellGifDecInfo& current_info = subHandle->info;
|
||||
|
||||
@@ -122,9 +282,10 @@ error_code cellGifDecReadHeader(PMainHandle mainHandle, PSubHandle subHandle, PI
|
||||
switch (subHandle->src.srcSelect)
|
||||
{
|
||||
case CELL_GIFDEC_BUFFER:
|
||||
{
|
||||
std::memcpy(buffer, subHandle->src.streamPtr.get_ptr(), sizeof(buffer));
|
||||
break;
|
||||
|
||||
}
|
||||
case CELL_GIFDEC_FILE:
|
||||
{
|
||||
auto file = idm::get<lv2_fs_object, lv2_file>(fd);
|
||||
@@ -156,20 +317,71 @@ error_code cellGifDecReadHeader(PMainHandle mainHandle, PSubHandle subHandle, PI
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellGifDecExtReadHeader()
|
||||
error_code cellGifDecExtReadHeader(vm::ptr<GifDecoder> mainHandle, vm::cptr<GifStream> subHandle, vm::ptr<CellGifDecInfo> info, vm::ptr<CellGifDecExtInfo> extInfo)
|
||||
{
|
||||
cellGifDec.todo("cellGifDecExtReadHeader()");
|
||||
cellGifDec.todo("cellGifDecExtReadHeader(mainHandle=*0x%x, subHandle=*0x%x, info=*0x%x, extInfo=*0x%x)", mainHandle, subHandle, info, extInfo);
|
||||
|
||||
if (!mainHandle)
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_ARG;
|
||||
}
|
||||
|
||||
if (false) // TODO: check main handle
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_SEQ;
|
||||
}
|
||||
|
||||
if (!subHandle)
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_ARG;
|
||||
}
|
||||
|
||||
if (false) // TODO: check sub handle
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_SEQ;
|
||||
}
|
||||
|
||||
if (!info || !extInfo)
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_ARG;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellGifDecSetParameter(PMainHandle mainHandle, PSubHandle subHandle, PInParam inParam, POutParam outParam)
|
||||
error_code cellGifDecSetParameter(vm::ptr<GifDecoder> mainHandle, vm::ptr<GifStream> subHandle, vm::cptr<CellGifDecInParam> inParam, vm::ptr<CellGifDecOutParam> outParam)
|
||||
{
|
||||
cellGifDec.warning("cellGifDecSetParameter(mainHandle=*0x%x, subHandle=*0x%x, inParam=*0x%x, outParam=*0x%x)", mainHandle, subHandle, inParam, outParam);
|
||||
|
||||
if (!mainHandle)
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_ARG;
|
||||
}
|
||||
|
||||
if (false) // TODO: check main handle
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_SEQ;
|
||||
}
|
||||
|
||||
if (!subHandle)
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_ARG;
|
||||
}
|
||||
|
||||
if (false) // TODO: check sub handle
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_SEQ;
|
||||
}
|
||||
|
||||
if (!inParam || !outParam)
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_ARG;
|
||||
}
|
||||
|
||||
CellGifDecInfo& current_info = subHandle->info;
|
||||
CellGifDecOutParam& current_outParam = subHandle->outParam;
|
||||
|
||||
current_outParam.outputWidthByte = (current_info.SWidth * current_info.SColorResolution * 3)/8;
|
||||
current_outParam.outputWidthByte = (current_info.SWidth * current_info.SColorResolution * 3) / 8;
|
||||
current_outParam.outputWidth = current_info.SWidth;
|
||||
current_outParam.outputHeight = current_info.SHeight;
|
||||
current_outParam.outputColorSpace = inParam->colorSpace;
|
||||
@@ -179,24 +391,98 @@ error_code cellGifDecSetParameter(PMainHandle mainHandle, PSubHandle subHandle,
|
||||
case CELL_GIFDEC_ARGB: current_outParam.outputComponents = 4; break;
|
||||
default: return CELL_GIFDEC_ERROR_ARG; // Not supported color space
|
||||
}
|
||||
current_outParam.outputBitDepth = 0; // Unimplemented
|
||||
current_outParam.useMemorySpace = 0; // Unimplemented
|
||||
current_outParam.outputBitDepth = 0; // Unimplemented
|
||||
current_outParam.useMemorySpace = 0; // Unimplemented
|
||||
|
||||
*outParam = current_outParam;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellGifDecExtSetParameter()
|
||||
error_code cellGifDecExtSetParameter(vm::ptr<GifDecoder> mainHandle, vm::ptr<GifStream> subHandle, vm::cptr<CellGifDecInParam> inParam, vm::ptr<CellGifDecOutParam> outParam, vm::cptr<CellGifDecExtInParam> extInParam, vm::ptr<CellGifDecExtOutParam> extOutParam)
|
||||
{
|
||||
cellGifDec.todo("cellGifDecExtSetParameter()");
|
||||
cellGifDec.todo("cellGifDecExtSetParameter(mainHandle=*0x%x, subHandle=*0x%x, inParam=*0x%x, outParam=*0x%x, extInParam=*0x%x, extOutParam=*0x%x)", mainHandle, subHandle, inParam, outParam, extInParam, extOutParam);
|
||||
|
||||
if (!mainHandle)
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_ARG;
|
||||
}
|
||||
|
||||
if (false) // TODO: check main handle
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_SEQ;
|
||||
}
|
||||
|
||||
if (!subHandle)
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_ARG;
|
||||
}
|
||||
|
||||
if (false) // TODO: check sub handle
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_SEQ;
|
||||
}
|
||||
|
||||
if (!inParam || !outParam)
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_ARG;
|
||||
}
|
||||
|
||||
CellGifDecInfo& current_info = subHandle->info;
|
||||
CellGifDecOutParam& current_outParam = subHandle->outParam;
|
||||
|
||||
current_outParam.outputWidthByte = (current_info.SWidth * current_info.SColorResolution * 3) / 8;
|
||||
current_outParam.outputWidth = current_info.SWidth;
|
||||
current_outParam.outputHeight = current_info.SHeight;
|
||||
current_outParam.outputColorSpace = inParam->colorSpace;
|
||||
switch (current_outParam.outputColorSpace)
|
||||
{
|
||||
case CELL_GIFDEC_RGBA:
|
||||
case CELL_GIFDEC_ARGB: current_outParam.outputComponents = 4; break;
|
||||
default: return CELL_GIFDEC_ERROR_ARG; // Not supported color space
|
||||
}
|
||||
current_outParam.outputBitDepth = 0; // Unimplemented
|
||||
current_outParam.useMemorySpace = 0; // Unimplemented
|
||||
|
||||
*outParam = current_outParam;
|
||||
|
||||
if (!extInParam || extInParam->bufferMode != CELL_GIFDEC_LINE_MODE || !extOutParam)
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_ARG;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellGifDecDecodeData(PMainHandle mainHandle, PSubHandle subHandle, vm::ptr<u8> data, PDataCtrlParam dataCtrlParam, PDataOutInfo dataOutInfo)
|
||||
error_code cellGifDecDecodeData(vm::ptr<GifDecoder> mainHandle, vm::cptr<GifStream> subHandle, vm::ptr<u8> data, vm::cptr<CellGifDecDataCtrlParam> dataCtrlParam, vm::ptr<CellGifDecDataOutInfo> dataOutInfo)
|
||||
{
|
||||
cellGifDec.warning("cellGifDecDecodeData(mainHandle=*0x%x, subHandle=*0x%x, data=*0x%x, dataCtrlParam=*0x%x, dataOutInfo=*0x%x)", mainHandle, subHandle, data, dataCtrlParam, dataOutInfo);
|
||||
|
||||
if (!mainHandle)
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_ARG;
|
||||
}
|
||||
|
||||
if (false) // TODO: check main handle
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_SEQ;
|
||||
}
|
||||
|
||||
if (!subHandle)
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_ARG;
|
||||
}
|
||||
|
||||
if (false) // TODO: check sub handle
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_SEQ;
|
||||
}
|
||||
|
||||
if (!dataOutInfo || !dataCtrlParam)
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_ARG;
|
||||
}
|
||||
|
||||
dataOutInfo->status = CELL_GIFDEC_DEC_STATUS_STOP;
|
||||
|
||||
const u32 fd = subHandle->fd;
|
||||
@@ -306,16 +592,57 @@ error_code cellGifDecDecodeData(PMainHandle mainHandle, PSubHandle subHandle, vm
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellGifDecExtDecodeData()
|
||||
error_code cellGifDecExtDecodeData(vm::ptr<GifDecoder> mainHandle, vm::cptr<GifStream> subHandle, vm::ptr<u8> data, vm::cptr<CellGifDecDataCtrlParam> dataCtrlParam, vm::ptr<CellGifDecDataOutInfo> dataOutInfo, vm::cptr<CellGifDecCbCtrlDisp> cbCtrlDisp, vm::ptr<CellGifDecDispParam> dispParam)
|
||||
{
|
||||
cellGifDec.todo("cellGifDecExtDecodeData()");
|
||||
cellGifDec.todo("cellGifDecExtDecodeData(mainHandle=*0x%x, subHandle=*0x%x, data=*0x%x, dataCtrlParam=*0x%x, dataOutInfo=*0x%x, cbCtrlDisp=*0x%x, dispParam=*0x%x)", mainHandle, subHandle, data, dataCtrlParam, dataOutInfo, cbCtrlDisp, dispParam);
|
||||
|
||||
if (!mainHandle)
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_ARG;
|
||||
}
|
||||
|
||||
if (false) // TODO: check main handle
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_SEQ;
|
||||
}
|
||||
|
||||
if (!subHandle)
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_ARG;
|
||||
}
|
||||
|
||||
if (false) // TODO: check sub handle
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_SEQ;
|
||||
}
|
||||
|
||||
if (!dataOutInfo || !dataCtrlParam)
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_ARG;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellGifDecClose(PMainHandle mainHandle, PSubHandle subHandle)
|
||||
error_code cellGifDecClose(vm::ptr<GifDecoder> mainHandle, vm::cptr<GifStream> subHandle)
|
||||
{
|
||||
cellGifDec.warning("cellGifDecClose(mainHandle=*0x%x, subHandle=*0x%x)", mainHandle, subHandle);
|
||||
|
||||
if (!mainHandle)
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_ARG;
|
||||
}
|
||||
|
||||
if (false) // TODO: check main handle
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_SEQ;
|
||||
}
|
||||
|
||||
if (!subHandle)
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_ARG;
|
||||
}
|
||||
|
||||
idm::remove<lv2_fs_object, lv2_file>(subHandle->fd);
|
||||
|
||||
vm::dealloc(subHandle.addr());
|
||||
@@ -323,9 +650,20 @@ error_code cellGifDecClose(PMainHandle mainHandle, PSubHandle subHandle)
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellGifDecDestroy(PMainHandle mainHandle)
|
||||
error_code cellGifDecDestroy(vm::ptr<GifDecoder> mainHandle)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellGifDec);
|
||||
cellGifDec.todo("cellGifDecDestroy(mainHandle=*0x%x)", mainHandle);
|
||||
|
||||
if (!mainHandle)
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_ARG;
|
||||
}
|
||||
|
||||
if (false) // TODO: check main handle
|
||||
{
|
||||
return CELL_GIFDEC_ERROR_SEQ;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -52,6 +52,23 @@ enum CellGifDecDecodeStatus : s32
|
||||
CELL_GIFDEC_DEC_STATUS_STOP = 1, // Decoding was stopped
|
||||
};
|
||||
|
||||
enum CellGifDecBufferMode : s32
|
||||
{
|
||||
CELL_GIFDEC_LINE_MODE = 1
|
||||
};
|
||||
|
||||
enum CellGifDecSpuMode : s32
|
||||
{
|
||||
CELL_GIFDEC_RECEIVE_EVENT = 0,
|
||||
CELL_GIFDEC_TRYRECEIVE_EVENT = 1
|
||||
};
|
||||
|
||||
enum CellGifDecInterlaceMode : s32
|
||||
{
|
||||
CELL_GIFDEC_NO_INTERLACE = 0,
|
||||
CELL_GIFDEC_INTERLACE = 1
|
||||
};
|
||||
|
||||
// Callbacks for memory management
|
||||
using CellGifDecCbControlMalloc = vm::ptr<void>(u32 size, vm::ptr<void> cbCtrlMallocArg);
|
||||
using CellGifDecCbControlFree = s32(vm::ptr<void> ptr, vm::ptr<void> cbCtrlFreeArg);
|
||||
@@ -151,6 +168,71 @@ struct CellGifDecDataCtrlParam
|
||||
be_t<u64> outputBytesPerLine;
|
||||
};
|
||||
|
||||
struct CellGifDecExtInfo
|
||||
{
|
||||
vm::bptr<u64> reserved;
|
||||
};
|
||||
|
||||
struct CellGifDecStrmInfo
|
||||
{
|
||||
be_t<u32> decodedStrmSize;
|
||||
};
|
||||
|
||||
struct CellGifDecStrmParam
|
||||
{
|
||||
vm::bptr<void> strmPtr;
|
||||
be_t<u32> strmSize;
|
||||
};
|
||||
|
||||
using CellGifDecCbControlStream = s32(vm::ptr<CellGifDecStrmInfo> strmInfo, vm::ptr<CellGifDecStrmParam> strmParam, vm::ptr<void> cbCtrlStrmArg);
|
||||
|
||||
struct CellGifDecCbCtrlStrm
|
||||
{
|
||||
vm::bptr<CellGifDecCbControlStream> cbCtrlStrmFunc;
|
||||
vm::bptr<void> cbCtrlStrmArg;
|
||||
};
|
||||
|
||||
struct CellGifDecExtInParam
|
||||
{
|
||||
be_t<s32> bufferMode; // CellGifDecBufferMode
|
||||
be_t<u32> outputCounts;
|
||||
be_t<s32> spuMode; // CellGifDecSpuMode
|
||||
};
|
||||
|
||||
struct CellGifDecExtOutParam
|
||||
{
|
||||
be_t<u64> outputWidthByte;
|
||||
be_t<u32> outputHeight;
|
||||
};
|
||||
|
||||
struct CellGifDecDispParam
|
||||
{
|
||||
vm::bptr<void> nextOutputImage;
|
||||
};
|
||||
|
||||
struct CellGifDecDispInfo
|
||||
{
|
||||
be_t<u64> outputFrameWidthByte;
|
||||
be_t<u32> outputFrameHeight;
|
||||
be_t<u64> outputStartXByte;
|
||||
be_t<u32> outputStartY;
|
||||
be_t<u64> outputWidthByte;
|
||||
be_t<u32> outputHeight;
|
||||
be_t<u32> outputBitDepth;
|
||||
be_t<u32> outputComponents;
|
||||
be_t<u32> scanPassCount;
|
||||
vm::bptr<void> outputImage;
|
||||
be_t<s32> interlaceFlag; // CellGifDecInterlaceMode
|
||||
};
|
||||
|
||||
using CellGifDecCbControlDisp = s32(vm::ptr<CellGifDecDispInfo> dispInfo, vm::ptr<CellGifDecDispParam> dispParam, vm::ptr<void> cbCtrlDispArg);
|
||||
|
||||
struct CellGifDecCbCtrlDisp
|
||||
{
|
||||
vm::bptr<CellGifDecCbControlDisp> cbCtrlDispFunc;
|
||||
vm::bptr<void> cbCtrlDispArg;
|
||||
};
|
||||
|
||||
// Custom structs
|
||||
struct GifDecoder
|
||||
{
|
||||
@@ -158,9 +240,9 @@ struct GifDecoder
|
||||
|
||||
struct GifStream
|
||||
{
|
||||
u32 fd;
|
||||
u64 fileSize;
|
||||
CellGifDecInfo info;
|
||||
CellGifDecOutParam outParam;
|
||||
CellGifDecSrc src;
|
||||
u32 fd{};
|
||||
u64 fileSize{};
|
||||
CellGifDecInfo info{};
|
||||
CellGifDecOutParam outParam{};
|
||||
CellGifDecSrc src{};
|
||||
};
|
||||
|
||||
@@ -727,7 +727,7 @@ error_code cellHttpUtilFormUrlDecode(vm::ptr<u8> out, u32 size, vm::cptr<char> i
|
||||
return CELL_HTTP_UTIL_ERROR_INVALID_URI;
|
||||
}
|
||||
|
||||
const auto FUN_00036710 = [](b8 c)
|
||||
const auto check_char = [](b8 c)
|
||||
{
|
||||
u32 utmp = static_cast<u32>(c);
|
||||
s32 stmp = utmp - 48;
|
||||
@@ -746,8 +746,8 @@ error_code cellHttpUtilFormUrlDecode(vm::ptr<u8> out, u32 size, vm::cptr<char> i
|
||||
return stmp;
|
||||
};
|
||||
|
||||
const s32 tmp1 = FUN_00036710(c2);
|
||||
const s32 tmp2 = FUN_00036710(c3);
|
||||
const s32 tmp1 = check_char(c2);
|
||||
const s32 tmp2 = check_char(c3);
|
||||
|
||||
if (tmp1 < 0 || tmp2 < 0)
|
||||
{
|
||||
|
||||
@@ -110,6 +110,7 @@ struct CellJpgDecSubHandle
|
||||
static const u32 id_base = 1;
|
||||
static const u32 id_step = 1;
|
||||
static const u32 id_count = 1023;
|
||||
SAVESTATE_INIT_POS(35);
|
||||
|
||||
u32 fd;
|
||||
u64 fileSize;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "stdafx.h"
|
||||
#include "Emu/IdManager.h"
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
|
||||
#include "Emu/Io/KeyboardHandler.h"
|
||||
@@ -8,6 +9,8 @@
|
||||
extern void libio_sys_config_init();
|
||||
extern void libio_sys_config_end();
|
||||
|
||||
extern bool is_input_allowed();
|
||||
|
||||
LOG_CHANNEL(sys_io);
|
||||
|
||||
template<>
|
||||
@@ -31,6 +34,33 @@ void fmt_class_string<CellKbError>::format(std::string& out, u64 arg)
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
KeyboardHandlerBase::KeyboardHandlerBase(utils::serial* ar)
|
||||
{
|
||||
if (!ar)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
(*ar)(m_info.max_connect);
|
||||
|
||||
if (m_info.max_connect)
|
||||
{
|
||||
Emu.DeferDeserialization([this]()
|
||||
{
|
||||
Init(m_info.max_connect);
|
||||
init.init();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardHandlerBase::save(utils::serial& ar)
|
||||
{
|
||||
const auto inited = init.access();
|
||||
|
||||
ar(inited ? m_info.max_connect : 0);
|
||||
}
|
||||
|
||||
error_code cellKbInit(u32 max_connect)
|
||||
{
|
||||
sys_io.warning("cellKbInit(max_connect=%d)", max_connect);
|
||||
@@ -305,7 +335,7 @@ error_code cellKbRead(u32 port_no, vm::ptr<CellKbData> data)
|
||||
|
||||
KbData& current_data = handler.GetData(port_no);
|
||||
|
||||
if (current_info.is_null_handler || (current_info.info & CELL_KB_INFO_INTERCEPTED))
|
||||
if (current_info.is_null_handler || (current_info.info & CELL_KB_INFO_INTERCEPTED) || !is_input_allowed())
|
||||
{
|
||||
data->led = 0;
|
||||
data->mkey = 0;
|
||||
|
||||
@@ -92,9 +92,15 @@ void mic_context::operator()()
|
||||
m_counter++;
|
||||
|
||||
// Process signals
|
||||
const auto process_signals = [this]() -> bool
|
||||
{
|
||||
std::lock_guard lock(mutex);
|
||||
|
||||
if (mic_list.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto& mic_entry : mic_list)
|
||||
{
|
||||
auto& mic = mic_entry.second;
|
||||
@@ -103,16 +109,25 @@ void mic_context::operator()()
|
||||
|
||||
auto mic_queue = lv2_event_queue::find(event_queue_key);
|
||||
if (!mic_queue)
|
||||
continue;
|
||||
|
||||
for (auto& mic_entry : mic_list)
|
||||
{
|
||||
auto& mic = mic_entry.second;
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const auto& [dev_num, mic] : mic_list)
|
||||
{
|
||||
if (mic.has_data())
|
||||
{
|
||||
mic_queue->send(0, CELLMIC_DATA, mic_entry.first, 0);
|
||||
mic_queue->send(0, CELLMIC_DATA, dev_num, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
// Get mic input and sleep if mics are idle
|
||||
if (!process_signals())
|
||||
{
|
||||
thread_ctrl::wait_for(100000);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user