Compare commits

...

108 Commits

Author SHA1 Message Date
JordanTheToaster
730e6fa737 GameDB: Simple 2000 Vol 92 Fixes 2025-06-06 23:09:26 +02:00
PCSX2 Bot
cdfcd9fddd [ci skip] Qt: Update Base Translation. 2025-06-06 02:31:08 +02:00
refractionpcsx2
b6930c10b9 GS/HW: Clean up target download formats 2025-06-05 13:51:21 +02:00
refractionpcsx2
852734580d GameDB: Update Harry Potter fixes. 2025-06-05 13:51:21 +02:00
refractionpcsx2
d1dc6a9c1d GS/HW: Add 16bit to 8bit conversion shader 2025-06-05 13:51:21 +02:00
Ty
4fa6d3ed3f GSRunner: Add type to shutdown message code 2025-06-04 20:25:32 -04:00
TellowKrinkle
92e190ad6c GSRunner: Fix surfaceless run on macOS 2025-06-04 20:25:32 -04:00
TellowKrinkle
da4fcffef4 Input: Fix crash when shutting down without initializing input 2025-06-04 20:25:32 -04:00
TellowKrinkle
1a5731dd8e MacOS: Better handle directories of non-bundle applications 2025-06-04 20:25:32 -04:00
TellowKrinkle
e764c5cd4e GSRunner: macOS support 2025-06-04 20:25:32 -04:00
TellowKrinkle
e23b247947 GSRunner: Use separate CPU thread 2025-06-04 20:25:32 -04:00
PCSX2 Bot
3d7792436f [ci skip] Qt: Update Base Translation. 2025-06-04 20:03:38 -04:00
chaoticgd
d8187fbea4 Deps: Specify minimum version of KDDockWidgets 2025-06-04 20:02:02 -04:00
chaoticgd
02259ad0a5 Debugger: Add include required for older versions of KDDockWidgets 2025-06-04 20:02:02 -04:00
TellowKrinkle
220a68df9a GS: Warn on texture replacement folder with wrong case 2025-06-04 19:58:41 -04:00
TellowKrinkle
2ced24f69e GS: Create texture dump directory if it doesn't exist 2025-06-04 19:58:41 -04:00
TellowKrinkle
ec91d0dc74 GS: Formatting 2025-06-04 19:58:41 -04:00
TheLastRar
46874f4673 Qt: Add workaround for incorrectly tinted icons after theme switch 2025-06-04 19:39:18 -04:00
TheLastRar
9eac47dc6c Qt: Fix selected gamelist icons being wrong colour after theme switch 2025-06-04 19:39:18 -04:00
RedDevilus
9e3fd5c2e0 CI: Fix flatpak
Try to fix build failing
2025-06-04 20:19:46 +01:00
refractionpcsx2
ae4be6e2b1 GS/CRC: Remove CRC for Simple 2000 Series Vol. 114, update GameDB Fixes 2025-06-04 18:35:00 +02:00
refractionpcsx2
434df49a7d GS/HW: Clear matched target on HW Move 2025-06-04 18:35:00 +02:00
JordanTheToaster
c939c0fcd5 GameDB: Re-add CRC to Death by Degrees 2025-06-04 18:33:36 +02:00
Berylskid
952c39f324 GameDB: Remove SoftwareRendererFMVHack from Armored Core 2 2025-06-03 13:44:40 +02:00
PCSX2 Bot
0fea7e2a70 [ci skip] Qt: Update Base Translation. 2025-06-03 03:40:13 +02:00
lightningterror
c1baab68d0 GS: Better handle hazards when dx12/vk device creation fails.
VK/DX12: Move CreateNullTexture before reading shader resource.
Fixes null pointer dereference.

VK: Check if vertex buffer is valid before binding.
Fixes vertex buffer validation error null handle.
2025-06-02 21:28:41 +02:00
PCSX2 Bot
ccef18f7a9 [ci skip] PAD: Update to latest controller database. 2025-06-02 19:08:36 +02:00
TheLastRar
8cb056bde3 Qt: Use DevicePixelRatioChange to detect DPR changes 2025-06-02 14:58:00 +02:00
TheLastRar
6ecaaee9e0 QtUtils: Remove redundant method 2025-06-02 14:58:00 +02:00
TheLastRar
c5f916bda0 Qt: Fix DPI icon scaling in various settings windows 2025-06-02 14:58:00 +02:00
TheLastRar
52a9a4649c Qt: Improve handling of DPI 2025-06-02 14:58:00 +02:00
refractionpcsx2
d2219b4dbd GS/TC: On RT->Z dst_match delete on format change if not a shuffle 2025-06-02 14:52:46 +02:00
refractionpcsx2
155f603245 GS/HW: Don't make new scaled targets on shuffles if source is downscaled 2025-06-02 14:52:46 +02:00
refractionpcsx2
cb7630a6ab GS/HW: Create new targets on shuffles when no target found
And get rid of some valid sizing thing which is just insane
2025-06-02 14:52:46 +02:00
refractionpcsx2
284fba1ce3 GS/TC: Don't allow Tex in RT 8bit textures from C24
C24 has no alpha channel, 8bit requires all.  This is still allowed for shuffles as that depends which channel it's reading.
2025-06-02 14:52:46 +02:00
refractionpcsx2
aa4bd6c88c GS/HW: Replace frame target if dirty data matches old format. 2025-06-02 14:52:46 +02:00
refractionpcsx2
623993930b GS/HW: Adjust depth size on clear if overlapping by 1 pixel
This is developer misunderstanding of how to use the SCISSOR register, which adds 1 on to the value when it's used, but they put 512x512, when they wanted 512x512.
2025-06-02 14:52:46 +02:00
refractionpcsx2
dbf2c854c6 GS/TC: Adopt valid rgb/alpha from preload merged targets 2025-06-02 14:52:46 +02:00
refractionpcsx2
25351bc05c GS/TC: Correct valid area checks for target combining 2025-06-02 14:52:46 +02:00
refractionpcsx2
27cc5f499c GS/HW: Fix up alpha blending checks 2025-06-02 14:52:46 +02:00
refractionpcsx2
b7c2f39a17 GS/TC: Further matching parameters on preload and tex in rt 2025-06-02 14:52:46 +02:00
refractionpcsx2
3541c1ccf8 GS/TC: Simplify and improve P8 texture conversion inside target 2025-06-02 14:52:46 +02:00
refractionpcsx2
7a05738d11 GS/TC: Allow matching on source if TEX == RT 2025-06-02 14:52:46 +02:00
refractionpcsx2
686220ae0c GS/TC: Improve copying of dst matched data 2025-06-02 14:52:46 +02:00
refractionpcsx2
cf380d36b9 GS/TC: Fix inside target alignment check for ExactTarget lookup 2025-06-02 14:52:46 +02:00
lightningterror
50bc0193ac Core: Bump savestate version.
[SAVEVERSION+]
2025-06-02 01:30:50 +02:00
refractionpcsx2
2162a72831 GS: Bump GS Dump version and add transfer parameters to dump 2025-06-02 00:40:03 +02:00
refractionpcsx2
313666f85b GS: Store entire GS transfer state at TRXDIR write 2025-06-02 00:40:03 +02:00
TheTechnician27
e9d79263b4 UI: Fix Discord Rich Presence not activating in FSUI 2025-06-01 23:21:39 +02:00
JordanTheToaster
4ede6d65fd GameDB: Fix half right issue with Xtreme Bowling 2025-06-01 13:28:25 -04:00
PCSX2 Bot
14d2eee371 [ci skip] Qt: Update Base Translation. 2025-05-31 20:46:33 -04:00
chaoticgd
717f370be0 Debugger: Improve DockTabBar ownership workaround 2025-05-31 03:30:13 +02:00
refractionpcsx2
d05e4b9727 GS/HW: Adjust SpriteNoGaps check for vertical strips 2025-05-31 03:25:47 +02:00
TheTechnician27
697c53b4d8 UI: Standardize order of option groups 2025-05-31 02:51:20 +02:00
refractionpcsx2
d9b58ec3ce GS/HW: Invalidate single columns on small writes when formats mismatch 2025-05-31 02:49:35 +02:00
refractionpcsx2
6c8b37d7ca GS: Add column sizing to psm format information 2025-05-31 02:49:35 +02:00
refractionpcsx2
39f43e766d GameDB: Add Tex in RT to Sand Grain Studios games 2025-05-31 02:37:16 +02:00
refractionpcsx2
5379a13944 GS/HW: Read back 16bit target if read as 8H 2025-05-31 02:37:16 +02:00
chaoticgd
cd0c1607ef GameList: Clip flag and compatibility pixmaps to the available space 2025-05-31 02:35:34 +02:00
chaoticgd
26a4f71385 GameList: Prevent "Invalid" entry type appearing in filter list 2025-05-31 02:35:34 +02:00
chaoticgd
06b3e6ad71 GameList: Fix region flag icons for non-English languages 2025-05-31 02:35:34 +02:00
lightningterror
79d22a8d77 GS/HW: Add/adjust logs for failed texture creation. 2025-05-31 02:04:12 +02:00
lightningterror
9914212600 GS/GL: Add another hazard check, colclip hw.
Bonus, add missing texture copies info when doing shader copies.
2025-05-31 02:04:12 +02:00
lightningterror
765f55e67b GS/TC: Fine tune Frame buffer conversion. 2025-05-31 01:55:40 +02:00
PCSX2 Bot
2d922cc035 [ci skip] Qt: Update Base Translation. 2025-05-30 15:57:49 +02:00
TellowKrinkle
42e0625ab3 Interpreter: Fix FTOI on negative numbers 2025-05-29 13:15:09 +02:00
TellowKrinkle
c72e894fc7 iR5900: Faster FTOI 2025-05-29 13:15:09 +02:00
JordanTheToaster
5bc2342d47 MemoryCardFile: Fix memory card sorting on Linux 2025-05-29 13:07:27 +02:00
Ty
ea2b0b5e59 CI: Fix a regression for flathub uploads 2025-05-28 19:22:47 -04:00
refractionpcsx2
d70cc0221a GS/DX12: Fix HDR copy scissor area 2025-05-28 22:32:12 +02:00
refractionpcsx2
b12587b44e GameDB: Add Tex in RT for Bard's Tale 2025-05-28 18:33:27 +01:00
refractionpcsx2
c4708bdc35 GameDB: Add Tex in RT to required games 2025-05-28 18:01:49 +02:00
refractionpcsx2
1fa2c0bf50 GS/TC: Replace half right with Tex in RT, only update needed dirty 2025-05-28 18:01:49 +02:00
lightningterror
b4c70d357a GS/HW: Restore old coverage after updating mip layers. 2025-05-28 18:00:45 +02:00
PCSX2 Bot
f18262ee96 [ci skip] PAD: Update to latest controller database. 2025-05-26 21:18:58 +02:00
refractionpcsx2
c1f1761482 GS/HW: Invalidate cleared area if overlapping existing dirty 2025-05-26 13:45:47 +02:00
PCSX2 Bot
067c3eea16 [ci skip] Qt: Update Base Translation. 2025-05-26 02:03:40 +02:00
KamFretoZ
6957cc7001 FSUI: Fix save state duplicate entry 2025-05-26 01:46:30 +02:00
PCSX2 Bot
7dea23eea8 [ci skip] Qt: Update Base Translation. 2025-05-24 20:10:08 -04:00
TheTechnician27
319ec1f774 OSD: Fix performance overlay overwriting dump stats when shifted left 2025-05-24 12:33:32 +02:00
JohnSmith774
2079532e83 GameDB: Add memcard filters for some NTSC-J titles. (#12708)
Add memcard filters for OutRun2 SP - SPECIAL TOURS.
Add memcard filters for Another Century's Episode 2 Special Vocal Version.
Add memcard filters for Armored Core - Last Raven.
2025-05-24 12:32:03 +02:00
Mrlinkwii
de1d646fe9 github-workflows: Fix a broken link. 2025-05-24 12:30:08 +02:00
JordanTheToaster
4bc3ab6285 OSD: Add VSync to the OSD 2025-05-24 05:04:39 +02:00
JordanTheToaster
1adc9cbb49 GameDB: Various Fixes Part 4 (one with cheese) 2025-05-24 05:04:39 +02:00
refractionpcsx2
7cdf5eefc9 GS/TC: On clear delete overlapping depth targets 2025-05-24 05:03:48 +02:00
refractionpcsx2
c7ee72647d GS/HW: Improve texture shuffle/copy detection 2025-05-23 20:33:30 +02:00
Imre Eilertsen
5434af348e GameDB: Add listings for PSX Update Disc. 2025-05-23 13:31:40 +02:00
GovanifY
169cea0f01 docs: update in order to redirect from the GH wiki to our website 2025-05-22 17:43:42 +02:00
lightningterror
a8f3ec8184 GSClut: Get rid of m_read.dirty assert in GetAlphaMinMax32.
Replace it with a log.
2025-05-21 03:40:22 +02:00
lightningterror
7796b2a12b GS/HW: Add sanity/hazard checks for DATE and Texture barriers.
When checking for full barrier, also check if texture barriers
are supported.

Get rid of preprocessSel in DX11, turn off full barrier and related
stuff when not supported in rendererhw instead.

Check if StencilDate types are actually enabled, don't want to turn
it on for any others.
2025-05-21 03:40:22 +02:00
RedDevilus
d9cffd58ba GameDB: Add missing variants + fixes
Some games are missing some gamefixes or other type of fixes. Also added comment for like Energy Airforce where a legacy code never had a comment and removed mtvu for some variant + need to check in future to amend properly. Also missing entry SLPS-25875:
  name: "xxxHolic - Shigatsu Tsuitachi no Izayoi Sowa [Best Collection]"
2025-05-21 00:56:32 +02:00
refractionpcsx2
e566386240 GS/TC: only kill old misaligned targets on preload from previous frames. 2025-05-20 20:21:05 +02:00
refractionpcsx2
45af1e172f GS/HW: Improve shuffle pre-detection 2025-05-20 20:21:05 +02:00
refractionpcsx2
768b2e52a6 GS/TC: Don't allow Tex in RT 32bit target use as 16bit if not a shuffle 2025-05-20 20:21:05 +02:00
Ziemas
7abd2009b0 debugger: fix thread view row lookup 2025-05-20 11:57:11 -04:00
chaoticgd
46f075e891 DebugTools: Fix uninitialized variable in breakpoint code 2025-05-19 22:17:54 -04:00
lightningterror
832c381ac4 GS/HW: Allow partial depth copy on dx12. 2025-05-19 22:10:42 +02:00
SquishyLeaf
e021282264 CI: Fix script building universal dependencies on macOS
- Skip arm64 binaries when looking for x86_64 binaries to merge

- Change Qt download link to archive

- Build universal binaries for libjpegturbo, PlutoVG and PlutoSVG
2025-05-19 14:21:14 -04:00
PCSX2 Bot
2a60d385c6 [ci skip] PAD: Update to latest controller database. 2025-05-19 18:42:36 +02:00
refractionpcsx2
4d37e35675 GS/TC: Delete dirty rt's in src lookup + usert in rt on 3 draw old rt's 2025-05-18 11:13:35 +02:00
refractionpcsx2
df3868a280 GS/HW: Avoid target height mistakes on shuffles + Update new src == rt 2025-05-18 11:13:35 +02:00
lightningterror
0799bb8cf1 GS/DX: DX requires a copy to sample the depth buffer. 2025-05-17 22:54:04 +02:00
lightningterror
69048dede4 GS/DX11: Merge CloneTexture with CopyRect.
Unified between renderers, easier to make shared changes.
2025-05-17 22:54:04 +02:00
lightningterror
76df6d1f43 GS/GL: Check for texture creation hazard for fb copy. 2025-05-17 22:54:04 +02:00
refractionpcsx2
2d03b21f2b Formatting: Clean up some if spaces 2025-05-17 22:47:38 +02:00
refractionpcsx2
62cbd44933 GS/HW: Fix up offset Z behaviour + work even when RT isn't offset. 2025-05-17 22:47:38 +02:00
refractionpcsx2
44c8f6d8b0 GS/HW: Correct valid area for depth when taking alpha from rt. Add Tex in RT to Area 51 2025-05-17 22:47:38 +02:00
refractionpcsx2
f3fc1dd59c GS/HW: Fix some regressions relating to overlapping targets and valid rect + rgb 2025-05-17 22:47:38 +02:00
107 changed files with 3738 additions and 2105 deletions

View File

@@ -1,54 +0,0 @@
# So you want to contribute to PCSX2? Great
As a first step, please review these links as they'll help you understand how the development of PCSX2 works.
* [Just Starting Out](#just-starting-out)
* [Issue Reporting](#issue-reporting)
* [Pull Request Guidelines](#pull-request-guidelines)
* [General Documentation And Coding Strategies](#general-documentation-and-coding-strategies)
* [Tasks](#tasks)
## Just Starting Out
* If you're unfamilar with git, check out this [brief introduction to Git](https://github.com/PCSX2/pcsx2/wiki/07-Git-survival-guide)
* [How to build PCSX2 for Windows](https://github.com/PCSX2/pcsx2/wiki/12-Building-on-Windows)
* [How to build PCSX2 for Linux](https://github.com/PCSX2/pcsx2/wiki/10-Building-on-Linux)
## Issue Reporting
* [How to write a useful issue](https://github.com/PCSX2/pcsx2/wiki/How-to-create-useful-and-valid-issues)
## Pull Request Guidelines
The following is a list of *general* style recommendations that will make reviewing and merging easier:
* Commit Messages
* Please try to prefix your commit message, indicating what area of the project was modified.
* For example `gs: message...`.
* Looking at the project's commit history will help with keeping prefixes consistent overtime, *there is no strictly enforced list*.
* Try to keep messages brief and informative
* Remove unnecessary commits and squash commits together when appropriate.
* If you are not familiar with rebasing with git, check out the following resources:
* CLI - https://thoughtbot.com/blog/git-interactive-rebase-squash-amend-rewriting-history
* GUI (SourceTree) - https://www.atlassian.com/blog/sourcetree/interactive-rebase-sourcetree
* Code Styling and Formatting
* [Consult the style guide](https://github.com/tadanokojin/pcsx2/blob/coding-guide/pcsx2/Docs/Coding_Guidelines.md)
* Run `clang-format` using the configuration file in the root of the repository
* Visual Studio Setup - https://devblogs.microsoft.com/cppblog/clangformat-support-in-visual-studio-2017-15-7-preview-1/
* IMPORTANT - if you are running `clang-format` on unrelated changes (ie. formatting an entire file), please do so in a separate commit.
* If you cannot scope your `clang-format` to just your changes and do not want to format unrelated code. Try your best to stick with the existing formatting already established in the file in question.
## General Documentation And Coding Strategies
* [Commenting Etiquette](https://github.com/PCSX2/pcsx2/wiki/06-Commenting-Etiquette)
* [Coding style](https://github.com/PCSX2/pcsx2/wiki/Code-Formatting-Guidelines)
* [More comprehensive style-guide (Currently in Draft)](https://github.com/tadanokojin/pcsx2/blob/coding-guide/pcsx2/Docs/Coding_Guidelines.md)
## Tasks
* [Issues](https://github.com/PCSX2/pcsx2/issues)

View File

@@ -66,6 +66,10 @@ body:
Performance issues as a result of not meeting our hardware requirements are not valid.
Please read our troubleshooting pages and our issue reporting guide.
- [Troubleshooting page](https://pcsx2.net/docs/category/troubleshooting).
- [Issue reporting guide](https://pcsx2.net/docs/troubleshooting/identify).
We are **not** accepting issues related to the **libretro** core. The libretro core is being maintained separately at this time
- type: input
id: rev

View File

@@ -132,7 +132,7 @@ jobs:
- name: Push to Flathub (beta)
if: ${{ inputs.publish == true && (inputs.stableBuild == false || inputs.stableBuild == 'false') }}
uses: flatpak/flatpak-github-actions/flatpak-builder@10a3c29f0162516f0f68006be14c92f34bd4fa6c
uses: flatpak/flatpak-github-actions/flat-manager@10a3c29f0162516f0f68006be14c92f34bd4fa6c
with:
flat-manager-url: https://hub.flathub.org/
repository: beta
@@ -141,7 +141,7 @@ jobs:
- name: Push to Flathub (stable)
if: ${{ inputs.publish == true && (inputs.stableBuild == true || inputs.stableBuild == 'true') }}
uses: flatpak/flatpak-github-actions/flatpak-builder@10a3c29f0162516f0f68006be14c92f34bd4fa6c
uses: flatpak/flatpak-github-actions/flat-manager@10a3c29f0162516f0f68006be14c92f34bd4fa6c
with:
flat-manager-url: https://hub.flathub.org/
repository: stable

View File

@@ -11,10 +11,9 @@
},
"sources": [
{
"type": "git",
"type": "git",
"url": "https://github.com/sammycage/plutovg.git",
"tag": "v0.0.13",
"sha256": "5e4712cf873b0c7829a4a6157763e2ad3ac49164"
"tag": "v0.0.13"
}
],
"cleanup": [

View File

@@ -12,10 +12,9 @@
},
"sources": [
{
"type": "git",
"type": "git",
"url": "https://github.com/sammycage/plutosvg.git",
"tag": "v0.0.6",
"sha256": "c5388fa96feca1f1376a3d0485d5e35159452707"
"tag": "v0.0.6"
}
],
"cleanup": [

View File

@@ -11,7 +11,7 @@ merge_binaries() {
"
pushd "$X86DIR"
for X86BIN in $(find . -type f \( -name '*.dylib' -o -name '*.a' -o -perm +111 \)); do
if file "$X86DIR/$X86BIN" | grep "Mach-O " >/dev/null; then
if file "$X86DIR/$X86BIN" | grep "Mach-O.*x86_64" >/dev/null; then
ARMBIN="${ARMDIR}/${X86BIN}"
echo "Merge $ARMBIN to $X86BIN..."
lipo -create "$X86BIN" "$ARMBIN" -o "$X86BIN"
@@ -112,11 +112,11 @@ curl -C - -L \
-O "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-$LIBWEBP.tar.gz" \
-O "https://ffmpeg.org/releases/ffmpeg-$FFMPEG.tar.xz" \
-O "https://github.com/KhronosGroup/MoltenVK/archive/refs/tags/v$MOLTENVK.tar.gz" \
-O "https://download.qt.io/official_releases/qt/${QT%.*}/$QT/submodules/qtbase-everywhere-src-$QT.tar.xz" \
-O "https://download.qt.io/official_releases/qt/${QT%.*}/$QT/submodules/qtimageformats-everywhere-src-$QT.tar.xz" \
-O "https://download.qt.io/official_releases/qt/${QT%.*}/$QT/submodules/qtsvg-everywhere-src-$QT.tar.xz" \
-O "https://download.qt.io/official_releases/qt/${QT%.*}/$QT/submodules/qttools-everywhere-src-$QT.tar.xz" \
-O "https://download.qt.io/official_releases/qt/${QT%.*}/$QT/submodules/qttranslations-everywhere-src-$QT.tar.xz" \
-O "https://download.qt.io/archive/qt/${QT%.*}/$QT/submodules/qtbase-everywhere-src-$QT.tar.xz" \
-O "https://download.qt.io/archive/qt/${QT%.*}/$QT/submodules/qtimageformats-everywhere-src-$QT.tar.xz" \
-O "https://download.qt.io/archive/qt/${QT%.*}/$QT/submodules/qtsvg-everywhere-src-$QT.tar.xz" \
-O "https://download.qt.io/archive/qt/${QT%.*}/$QT/submodules/qttools-everywhere-src-$QT.tar.xz" \
-O "https://download.qt.io/archive/qt/${QT%.*}/$QT/submodules/qttranslations-everywhere-src-$QT.tar.xz" \
-o "shaderc-$SHADERC.tar.gz" "https://github.com/google/shaderc/archive/refs/tags/v$SHADERC.tar.gz" \
-o "shaderc-glslang-$SHADERC_GLSLANG.tar.gz" "https://github.com/KhronosGroup/glslang/archive/$SHADERC_GLSLANG.tar.gz" \
-o "shaderc-spirv-headers-$SHADERC_SPIRVHEADERS.tar.gz" "https://github.com/KhronosGroup/SPIRV-Headers/archive/$SHADERC_SPIRVHEADERS.tar.gz" \
@@ -213,8 +213,11 @@ echo "Installing libjpegturbo..."
rm -fr "libjpeg-turbo-$LIBJPEGTURBO"
tar xf "libjpeg-turbo-$LIBJPEGTURBO.tar.gz"
cd "libjpeg-turbo-$LIBJPEGTURBO"
cmake "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_ARM64" -DENABLE_STATIC=OFF -DENABLE_SHARED=ON -B build-arm64
cmake "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_X64" -DENABLE_STATIC=OFF -DENABLE_SHARED=ON -B build
make -C build "-j$NPROCS"
cmake "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_ARM64" -DENABLE_STATIC=OFF -DENABLE_SHARED=ON -B build-arm64
make -C build-arm64 "-j$NPROCS"
merge_binaries $(realpath build) $(realpath build-arm64)
make -C build install
cd ..
@@ -380,7 +383,7 @@ echo "Building PlutoVG..."
rm -fr "plutovg-$PLUTOVG"
tar xf "plutovg-$PLUTOVG.tar.gz"
cd "plutovg-$PLUTOVG"
cmake "${CMAKE_COMMON[@]}" -DBUILD_SHARED_LIBS=ON -DPLUTOVG_BUILD_EXAMPLES=OFF -B build
cmake "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_UNIVERSAL" -DBUILD_SHARED_LIBS=ON -DPLUTOVG_BUILD_EXAMPLES=OFF -B build
make -C build "-j$NPROCS"
make -C build install
cd ..
@@ -389,7 +392,7 @@ echo "Building PlutoSVG..."
rm -fr "plutosvg-$PLUTOSVG"
tar xf "plutosvg-$PLUTOSVG.tar.gz"
cd "plutosvg-$PLUTOSVG"
cmake "${CMAKE_COMMON[@]}" -DBUILD_SHARED_LIBS=ON -DPLUTOSVG_ENABLE_FREETYPE=ON -DPLUTOSVG_BUILD_EXAMPLES=OFF -B build
cmake "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_UNIVERSAL" -DBUILD_SHARED_LIBS=ON -DPLUTOSVG_ENABLE_FREETYPE=ON -DPLUTOSVG_BUILD_EXAMPLES=OFF -B build
make -C build "-j$NPROCS"
make -C build install
cd ..

View File

@@ -66,7 +66,7 @@ const embed = new MessageEmbed()
.setDescription("To download the latest or previous builds, [visit the official downloads page](https://pcsx2.net/downloads/).")
.addFields(
{ name: 'Version', value: releaseInfo.tag_name, inline: true },
{ name: 'Installation Steps', value: '[See Here](https://github.com/PCSX2/pcsx2/wiki/Nightly-Build-Usage-Guide)', inline: true },
{ name: 'Installation Steps', value: '[See Here](https://pcsx2.net/docs/category/setup)', inline: true },
{ name: 'Included Changes', value: releaseInfo.body, inline: false }
);
console.log(embed);

View File

@@ -19,7 +19,7 @@ jobs:
pr-message: |-
## Thank you for submitting a contribution to PCSX2
As this is your first pull request, [please be aware of the contributing guidelines](https://github.com/PCSX2/pcsx2/blob/master/.github/CONTRIBUTING.md).
As this is your first pull request, [please be aware of the contributing guidelines](https://pcsx2.net/docs/contributing/).
Additionally, as per recent changes in GitHub Actions, your pull request will need to be approved by a maintainer before GitHub Actions can run against it. [You can find more information about this change here.](https://github.blog/2021-04-22-github-actions-update-helping-maintainers-combat-bad-actors/)

View File

@@ -62,10 +62,12 @@ endif()
# gsrunner
if(ENABLE_GSRUNNER)
if (NOT WIN32)
message(WARNING "GSRunner is only supported on Windows and may not build on your system")
if (NOT WIN32 AND NOT APPLE)
message(WARNING "GSRunner is only supported on Windows and macOS and may not build on your system")
endif()
add_subdirectory(pcsx2-gsrunner)
else()
add_subdirectory(pcsx2-gsrunner EXCLUDE_FROM_ALL)
endif()
#-------------------------------------------------------------------------------

View File

@@ -24,4 +24,4 @@ Please note that a BIOS dump from a legitimately-owned PS2 console is required t
PCSX2 supports translation into other languages using [Crowdin](https://crowdin.com/project/pcsx2-emulator).
See the [Contributing Guidelines](https://github.com/PCSX2/pcsx2/blob/master/.github/CONTRIBUTING.md) or visit the [GitHub Wiki](https://github.com/PCSX2/pcsx2/wiki) for more info on how to contribute.
See the [Contribution Guide](https://pcsx2.net/docs/contributing/) for more info on how to contribute.

File diff suppressed because it is too large Load Diff

View File

@@ -154,6 +154,7 @@
03000000120c0000f10e000000000000,Brook PS2 Adapter,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
03000000120c0000310c000000000000,Brook Super Converter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,
03000000d81d00000b00000000000000,Buffalo BSGP1601 Series,a:b5,b:b3,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b4,y:b2,platform:Windows,
030000005a1c00002400000000000000,Capcom Home Arcade Controller,a:b3,b:b4,back:b7,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b6,x:b0,y:b1,platform:Windows,
030000005b1c00002400000000000000,Capcom Home Arcade Controller,a:b3,b:b4,back:b7,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b6,x:b0,y:b1,platform:Windows,
030000005b1c00002500000000000000,Capcom Home Arcade Controller,a:b3,b:b4,back:b7,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b6,x:b0,y:b1,platform:Windows,
030000006d04000042c2000000000000,ChillStream,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
@@ -336,6 +337,7 @@
03000000790000004e95000000000000,Hyperkin N64 Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a5,righty:a2,start:b9,platform:Windows,
03000000242e00006a48000000000000,Hyperkin RetroN Sq,a:b3,b:b7,back:b5,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,leftshoulder:b0,rightshoulder:b1,start:b4,x:b2,y:b6,platform:Windows,
03000000242f00000a20000000000000,Hyperkin Scout,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows,
03000000242e00000a20000000000000,Hyperkin Scout Premium SNES Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows,
03000000242e00006a38000000000000,Hyperkin Trooper 2,a:b0,b:b1,back:b4,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b3,start:b5,platform:Windows,
03000000d81d00000e00000000000000,iBuffalo AC02 Arcade Joystick,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b11,righttrigger:b3,rightx:a2,righty:a5,start:b8,x:b4,y:b5,platform:Windows,
03000000d81d00000f00000000000000,iBuffalo BSGP1204 Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
@@ -418,7 +420,7 @@
03000000242f00007300000000000000,Mayflash Magic NS,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows,
0300000079000000d218000000000000,Mayflash Magic NS,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
03000000d620000010a7000000000000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000242e0000f500000000000000,Mayflash N64 Adapter,a:b2,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a5,start:b9,platform:Windows,
03000000242f0000f500000000000000,Mayflash N64 Adapter,a:b2,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a5,start:b9,platform:Windows,
03000000242f0000f400000000000000,Mayflash N64 Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a5,start:b9,platform:Windows,
03000000790000007918000000000000,Mayflash N64 Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,righttrigger:b7,rightx:a3,righty:a2,start:b8,platform:Windows,
030000008f0e00001030000000000000,Mayflash Saturn Adapter,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,lefttrigger:b7,rightshoulder:b6,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows,
@@ -484,7 +486,7 @@
030000008916000000fd000000000000,Onza TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
03000000d62000006d57000000000000,OPP PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
030000006b14000001a1000000000000,Orange Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Windows,
0300000009120000072f000000000000,OrangeFox86 DreamPicoPort,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:-a2,leftx:a0,lefty:a1,righttrigger:-a5,start:b11,x:b3,y:b4,platform:Windows,
0300000009120000072f000000000000,OrangeFox86 DreamPicoPort,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:-a2,leftx:a0,lefty:a1,righttrigger:-a5,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
03000000362800000100000000000000,OUYA Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Windows,
03000000120c0000f60e000000000000,P4 Gamepad,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b7,rightshoulder:b4,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,
03000000790000002201000000000000,PC Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
@@ -691,6 +693,7 @@
03000000317300000100000000000000,Sony DualShock 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,
03000000666600006706000000000000,Sony PlayStation Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Windows,
03000000e30500009605000000000000,Sony PlayStation Adapter,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,
03000000fe1400002a23000000000000,Sony PlayStation Adapter,a:b0,b:b1,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,x:b2,y:b3,platform:Windows,
030000004c050000da0c000000000000,Sony PlayStation Classic Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,
03000000632500002306000000000000,Sony PlayStation Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Windows,
03000000f0250000c183000000000000,Sony PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
@@ -1008,6 +1011,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000007e0500001720000001000000,NSO SNES Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b15,start:b9,x:b2,y:b3,platform:Mac OS X,
03000000550900001472000025050000,NVIDIA Controller,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Mac OS X,
030000004b120000014d000000010000,Nyko Airflo EX,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Mac OS X,
0300000009120000072f000000010000,OrangeFox86 DreamPicoPort,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a2,leftx:a0,lefty:a1,righttrigger:a5,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Mac OS X,
030000006f0e00000901000002010000,PDP PS3 Versus Fighting,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,
030000008f0e00000300000000000000,Piranha Xtreme PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Mac OS X,
03000000d620000011a7000000020000,PowerA Core Plus Gamecube Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,
@@ -1100,6 +1104,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
050000005769696d6f74652028303000,Wii Remote,a:b4,b:b5,back:b7,dpdown:b3,dpleft:b0,dpright:b1,dpup:b2,guide:b8,leftshoulder:b11,lefttrigger:b12,leftx:a0,lefty:a1,start:b6,x:b10,y:b9,platform:Mac OS X,
050000005769696d6f74652028313800,Wii U Pro Controller,a:b16,b:b15,back:b7,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b8,leftshoulder:b19,leftstick:b23,lefttrigger:b21,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b24,righttrigger:b22,rightx:a2,righty:a3,start:b6,x:b18,y:b17,platform:Mac OS X,
030000005e0400008e02000000000000,Xbox 360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
030000005e0400008e02000010010000,Xbox 360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4~,start:b8,x:b2,y:b3,platform:Mac OS X,
030000006f0e00000104000000000000,Xbox 360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
03000000c6240000045d000000000000,Xbox 360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
030000005e0400000a0b000000000000,Xbox Adaptive Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
@@ -1278,7 +1283,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000006f0e00008401000011010000,Faceoff Deluxe Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
030000006f0e00008101000011010000,Faceoff Deluxe Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
030000006f0e00008001000011010000,Faceoff Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
03005036852100000201000010010000,Final Fantasy XIV Online Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
03000000852100000201000010010000,FF GP1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
05000000b40400001224000001010000,Flydigi APEX 4,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b4,leftstick:b10,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b20,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
03000000b40400001124000011010000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b14,paddle1:b2,paddle2:b5,paddle3:b16,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000b40400001224000011010000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b2,paddle1:b16,paddle2:b17,paddle3:b14,paddle4:b15,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
@@ -1601,6 +1606,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
0300132d9b2800006500000001010000,Raphnet GameCube Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux,
030000009b2800003200000001010000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux,
030000009b2800006000000001010000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux,
030000009b2800006100000001010000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Linux,
030000009b2800008000000020020000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Linux,
030000009b2800008000000001010000,Raphnet Wii Classic Adapter V3,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Linux,
03000000f8270000bf0b000011010000,Razer Kishi,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
@@ -1766,6 +1772,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
050000005e040000e002000003090000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
050000005e040000fd02000003090000,Xbox One Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
050000005e040000fd02000030110000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
060000005e040000dd02000003020000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
050000005e040000e302000002090000,Xbox One Elite,a:b0,b:b1,back:b136,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
050000005e040000220b000013050000,Xbox One Elite 2 Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
050000005e040000050b000002090000,Xbox One Elite Series 2,a:b0,b:b1,back:b136,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
@@ -1796,6 +1803,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
060000005e040000120b00000b050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
060000005e040000120b00000d050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
060000005e040000120b00000f050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
050000005e040000130b000022050000,Xbox Series X Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
050000005e040000200b000013050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
050000005e040000200b000017050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
050000005e040000220b000017050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,

View File

@@ -313,6 +313,127 @@ float ps_convert_rgb5a1_float16_biln(PS_INPUT input) : SV_Depth
SAMPLE_RGBA_DEPTH_BILN(rgb5a1_to_depth16);
}
PS_OUTPUT ps_convert_rgb5a1_8i(PS_INPUT input)
{
PS_OUTPUT output;
// Convert a RGB5A1 texture into a 8 bits packed texture
// Input column: 16x2 RGB5A1 pixels
// 0: 16 RGBA
// 1: 16 RGBA
// Output column: 16x4 Index pixels
// 0: 16 R5G2
// 1: 16 R5G2
// 2: 16 G2B5A1
// 3: 16 G2B5A1
uint2 pos = uint2(input.p.xy);
// Collapse separate R G B A areas into their base pixel
uint2 column = (pos & ~uint2(0u, 3u)) / uint2(1,2);
uint2 subcolumn = (pos & uint2(0u, 1u));
column.x -= (column.x / 128) * 64;
column.y += (column.y / 32) * 32;
uint PSM = uint(DOFFSET);
// Deal with swizzling differences
if ((PSM & 0x8) != 0) // PSMCT16S
{
if ((pos.x & 32) != 0)
{
column.y += 32; // 4 columns high times 4 to get bottom 4 blocks
column.x &= ~32;
}
if ((pos.x & 64) != 0)
{
column.x -= 32;
}
if (((pos.x & 16) != 0) != ((pos.y & 16) != 0))
{
column.x ^= 16;
column.y ^= 8;
}
if ((PSM & 0x30) != 0) // PSMZ16S - Untested but hopefully ok if anything uses it.
{
column.x ^= 32;
column.y ^= 16;
}
}
else // PSMCT16
{
if ((pos.y & 32) != 0)
{
column.y -= 16;
column.x += 32;
}
if ((pos.x & 96) != 0)
{
uint multi = (pos.x & 96) / 32;
column.y += 16 * multi; // 4 columns high times 4 to get bottom 4 blocks
column.x -= (pos.x & 96);
}
if (((pos.x & 16) != 0) != ((pos.y & 16) != 0))
{
column.x ^= 16;
column.y ^= 8;
}
if ((PSM & 0x30) != 0) // PSMZ16 - Untested but hopefully ok if anything uses it.
{
column.x ^= 32;
column.y ^= 32;
}
}
uint2 coord = column | subcolumn;
// Compensate for potentially differing page pitch.
uint SBW = uint(EMODA);
uint DBW = uint(EMODC);
uint2 block_xy = coord / uint2(64,64);
uint block_num = (block_xy.y * (DBW / 128)) + block_xy.x;
uint2 block_offset = uint2((block_num % (SBW / 64)) * 64, (block_num / (SBW / 64)) * 64);
coord = (coord % uint2(64, 64)) + block_offset;
// Apply offset to cols 1 and 2
uint is_col23 = pos.y & 4u;
uint is_col13 = pos.y & 2u;
uint is_col12 = is_col23 ^ (is_col13 << 1);
coord.x ^= is_col12; // If cols 1 or 2, flip bit 3 of x
float ScaleFactor = BGColor.x;
if (floor(ScaleFactor) != ScaleFactor)
coord = uint2(float2(coord) * ScaleFactor);
else
coord *= uint(ScaleFactor);
float4 pixel = Texture.Load(int3(int2(coord), 0));
uint4 denorm_c = (uint4)(pixel * 255.5f);
if ((pos.y & 2u) == 0u)
{
uint red = (denorm_c.r >> 3) & 0x1Fu;
uint green = (denorm_c.g >> 3) & 0x1Fu;
float sel0 = (float)(((green << 5) | red) & 0xFF) / 255.0f;
output.c = (float4)(sel0);
}
else
{
uint green = (denorm_c.g >> 3) & 0x1Fu;
uint blue = (denorm_c.b >> 3) & 0x1Fu;
uint alpha = denorm_c.a & 0x80u;
float sel0 = (float)((alpha | (blue << 2) | (green >> 3)) & 0xFF) / 255.0f;
output.c = (float4)(sel0);
}
return output;
}
PS_OUTPUT ps_convert_rgba_8i(PS_INPUT input)
{
PS_OUTPUT output;

View File

@@ -237,9 +237,131 @@ void ps_convert_rgb5a1_float16_biln()
}
#endif
#ifdef ps_convert_rgb5a1_8i
uniform uint SBW;
uniform uint DBW;
uniform uint PSM;
uniform float ScaleFactor;
void ps_convert_rgb5a1_8i()
{
// Convert a RGB5A1 texture into a 8 bits packed texture
// Input column: 16x2 RGB5A1 pixels
// 0: 16 RGBA
// 1: 16 RGBA
// Output column: 16x4 Index pixels
// 0: 16 R5G2
// 1: 16 R5G2
// 2: 16 G2B5A1
// 3: 16 G2B5A1
uvec2 pos = uvec2(gl_FragCoord.xy);
// Collapse separate R G B A areas into their base pixel
uvec2 column = (pos & ~uvec2(0u, 3u)) / uvec2(1,2);
uvec2 subcolumn = (pos & uvec2(0u, 1u));
column.x -= (column.x / 128) * 64;
column.y += (column.y / 32) * 32;
// Deal with swizzling differences
if ((PSM & 0x8) != 0) // PSMCT16S
{
if ((pos.x & 32) != 0)
{
column.y += 32; // 4 columns high times 4 to get bottom 4 blocks
column.x &= ~32;
}
if ((pos.x & 64) != 0)
{
column.x -= 32;
}
if (((pos.x & 16) != 0) != ((pos.y & 16) != 0))
{
column.x ^= 16;
column.y ^= 8;
}
if ((PSM & 0x30) != 0) // PSMZ16S - Untested but hopefully ok if anything uses it.
{
column.x ^= 32;
column.y ^= 16;
}
}
else // PSMCT16
{
if ((pos.y & 32) != 0)
{
column.y -= 16;
column.x += 32;
}
if ((pos.x & 96) != 0)
{
uint multi = (pos.x & 96) / 32;
column.y += 16 * multi; // 4 columns high times 4 to get bottom 4 blocks
column.x -= (pos.x & 96);
}
if (((pos.x & 16) != 0) != ((pos.y & 16) != 0))
{
column.x ^= 16;
column.y ^= 8;
}
if ((PSM & 0x30) != 0) // PSMZ16 - Untested but hopefully ok if anything uses it.
{
column.x ^= 32;
column.y ^= 32;
}
}
uvec2 coord = column | subcolumn;
// Compensate for potentially differing page pitch.
uvec2 block_xy = coord / uvec2(64u, 64u);
uint block_num = (block_xy.y * (DBW / 128u)) + block_xy.x;
uvec2 block_offset = uvec2((block_num % (SBW / 64u)) * 64u, (block_num / (SBW / 64u)) * 64u);
coord = (coord % uvec2(64u, 64u)) + block_offset;
// Apply offset to cols 1 and 2
uint is_col23 = pos.y & 4u;
uint is_col13 = pos.y & 2u;
uint is_col12 = is_col23 ^ (is_col13 << 1);
coord.x ^= is_col12; // If cols 1 or 2, flip bit 3 of x
if (floor(ScaleFactor) != ScaleFactor)
coord = uvec2(vec2(coord) * ScaleFactor);
else
coord *= uvec2(ScaleFactor);
vec4 pixel = texelFetch(TextureSampler, ivec2(coord), 0);
uvec4 denorm_c = uvec4(pixel * 255.5f);
if ((pos.y & 2u) == 0u)
{
uint red = (denorm_c.r >> 3) & 0x1Fu;
uint green = (denorm_c.g >> 3) & 0x1Fu;
float sel0 = float(((green << 5) | red) & 0xFF) / 255.0f;
SV_Target0 = vec4(sel0);
}
else
{
uint green = (denorm_c.g >> 3) & 0x1Fu;
uint blue = (denorm_c.b >> 3) & 0x1Fu;
uint alpha = denorm_c.a & 0x80u;
float sel0 = float((alpha | (blue << 2) | (green >> 3)) & 0xFF) / 255.0f;
SV_Target0 = vec4(sel0);
}
}
#endif
#ifdef ps_convert_rgba_8i
uniform uint SBW;
uniform uint DBW;
uniform uint PSM;
uniform float ScaleFactor;
void ps_convert_rgba_8i()

View File

@@ -307,12 +307,138 @@ void ps_convert_rgb5a1_float16_biln()
}
#endif
#ifdef ps_convert_rgb5a1_8i
layout(push_constant) uniform cb10
{
uint SBW;
uint DBW;
uint PSM;
float cb_pad1;
float ScaleFactor;
vec3 cb_pad2;
};
void ps_convert_rgb5a1_8i()
{
// Convert a RGB5A1 texture into a 8 bits packed texture
// Input column: 16x2 RGB5A1 pixels
// 0: 16 RGBA
// 1: 16 RGBA
// Output column: 16x4 Index pixels
// 0: 16 R5G2
// 1: 16 R5G2
// 2: 16 G2B5A1
// 3: 16 G2B5A1
uvec2 pos = uvec2(gl_FragCoord.xy);
// Collapse separate R G B A areas into their base pixel
uvec2 column = (pos & ~uvec2(0u, 3u)) / uvec2(1,2);
uvec2 subcolumn = (pos & uvec2(0u, 1u));
column.x -= (column.x / 128) * 64;
column.y += (column.y / 32) * 32;
// Deal with swizzling differences
if ((PSM & 0x8) != 0) // PSMCT16S
{
if ((pos.x & 32) != 0)
{
column.y += 32; // 4 columns high times 4 to get bottom 4 blocks
column.x &= ~32;
}
if ((pos.x & 64) != 0)
{
column.x -= 32;
}
if (((pos.x & 16) != 0) != ((pos.y & 16) != 0))
{
column.x ^= 16;
column.y ^= 8;
}
if ((PSM & 0x30) != 0) // PSMZ16S - Untested but hopefully ok if anything uses it.
{
column.x ^= 32;
column.y ^= 16;
}
}
else // PSMCT16
{
if ((pos.y & 32) != 0)
{
column.y -= 16;
column.x += 32;
}
if ((pos.x & 96) != 0)
{
uint multi = (pos.x & 96) / 32;
column.y += 16 * multi; // 4 columns high times 4 to get bottom 4 blocks
column.x -= (pos.x & 96);
}
if (((pos.x & 16) != 0) != ((pos.y & 16) != 0))
{
column.x ^= 16;
column.y ^= 8;
}
if ((PSM & 0x30) != 0) // PSMZ16 - Untested but hopefully ok if anything uses it.
{
column.x ^= 32;
column.y ^= 32;
}
}
uvec2 coord = column | subcolumn;
// Compensate for potentially differing page pitch.
uvec2 block_xy = coord / uvec2(64u, 64u);
uint block_num = (block_xy.y * (DBW / 128u)) + block_xy.x;
uvec2 block_offset = uvec2((block_num % (SBW / 64u)) * 64u, (block_num / (SBW / 64u)) * 64u);
coord = (coord % uvec2(64u, 64u)) + block_offset;
// Apply offset to cols 1 and 2
uint is_col23 = pos.y & 4u;
uint is_col13 = pos.y & 2u;
uint is_col12 = is_col23 ^ (is_col13 << 1);
coord.x ^= is_col12; // If cols 1 or 2, flip bit 3 of x
if (floor(ScaleFactor) != ScaleFactor)
coord = uvec2(vec2(coord) * ScaleFactor);
else
coord *= uvec2(ScaleFactor);
vec4 pixel = texelFetch(samp0, ivec2(coord), 0);
uvec4 denorm_c = uvec4(pixel * 255.5f);
if ((pos.y & 2u) == 0u)
{
uint red = (denorm_c.r >> 3) & 0x1Fu;
uint green = (denorm_c.g >> 3) & 0x1Fu;
float sel0 = float(((green << 5) | red) & 0xFF) / 255.0f;
o_col0 = vec4(sel0);
}
else
{
uint green = (denorm_c.g >> 3) & 0x1Fu;
uint blue = (denorm_c.b >> 3) & 0x1Fu;
uint alpha = denorm_c.a & 0x80u;
float sel0 = float((alpha | (blue << 2) | (green >> 3)) & 0xFF) / 255.0f;
o_col0 = vec4(sel0);
}
}
#endif
#ifdef ps_convert_rgba_8i
layout(push_constant) uniform cb10
{
uint SBW;
uint DBW;
uvec2 cb_pad1;
uint PSM;
float cb_pad1;
float ScaleFactor;
vec3 cb_pad2;
};

View File

@@ -7,7 +7,7 @@ include(GNUInstallDirs)
# Misc option
#-------------------------------------------------------------------------------
option(ENABLE_TESTS "Enables building the unit tests" ON)
option(ENABLE_GSRUNNER "Enables building the GSRunner" OFF)
option(ENABLE_GSRUNNER "Enables building the GSRunner by default. It can still be built with `make pcsx2-gsrunner` otherwise." OFF)
option(LTO_PCSX2_CORE "Enable LTO/IPO/LTCG on the subset of pcsx2 that benefits most from it but not anything else")
option(USE_VTUNE "Plug VTUNE to profile GS JIT.")
option(PACKAGE_MODE "Use this option to ease packaging of PCSX2 (developer/distribution option)")

View File

@@ -120,7 +120,7 @@ add_subdirectory(3rdparty/demangler EXCLUDE_FROM_ALL)
add_subdirectory(3rdparty/ccc EXCLUDE_FROM_ALL)
# The docking system for the debugger.
find_package(KDDockWidgets-qt6 REQUIRED)
find_package(KDDockWidgets-qt6 2.0.0 REQUIRED)
# Add an extra include path to work around a broken include directive.
# TODO: Remove this the next time we update KDDockWidgets.
get_target_property(KDDOCKWIDGETS_INCLUDE_DIRECTORY KDAB::kddockwidgets INTERFACE_INCLUDE_DIRECTORIES)

View File

@@ -31,6 +31,19 @@ namespace CocoaTools
bool DelayedLaunch(std::string_view file);
/// Open a Finder window to the given URL
bool ShowInFinder(std::string_view file);
/// Get the path to the resources directory of the current application
std::optional<std::string> GetResourcePath();
/// Create a window
void* CreateWindow(std::string_view title, uint32_t width, uint32_t height);
/// Destroy a window
void DestroyWindow(void* window);
/// Make a WindowInfo from the given window
void GetWindowInfoFromWindow(WindowInfo* wi, void* window);
/// Run cocoa event loop
void RunCocoaEventLoop(bool wait_forever = false);
/// Posts an event to the main telling `RunCocoaEventLoop(true)` to exit
void StopMainThreadEventLoop();
}
#endif // __APPLE__

View File

@@ -224,5 +224,103 @@ bool CocoaTools::DelayedLaunch(std::string_view file)
bool CocoaTools::ShowInFinder(std::string_view file)
{
return [[NSWorkspace sharedWorkspace] selectFile:NSStringFromStringView(file)
inFileViewerRootedAtPath:nil];
inFileViewerRootedAtPath:@""];
}
std::optional<std::string> CocoaTools::GetResourcePath()
{ @autoreleasepool {
if (NSBundle* bundle = [NSBundle mainBundle])
{
NSString* rsrc = [bundle resourcePath];
NSString* root = [bundle bundlePath];
if ([rsrc isEqualToString:root])
rsrc = [rsrc stringByAppendingString:@"/resources"];
return [rsrc UTF8String];
}
return std::nullopt;
}}
// MARK: - GSRunner
void* CocoaTools::CreateWindow(std::string_view title, u32 width, u32 height)
{
if (!NSApp)
{
[NSApplication sharedApplication];
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
[NSApp finishLaunching];
}
constexpr NSWindowStyleMask style = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable;
NSScreen* mainScreen = [NSScreen mainScreen];
// Center the window on the screen, because why not
NSRect screenFrame = [mainScreen frame];
NSRect viewFrame = screenFrame;
viewFrame.size = NSMakeSize(width, height);
viewFrame.origin.x += (screenFrame.size.width - viewFrame.size.width) / 2;
viewFrame.origin.y += (screenFrame.size.height - viewFrame.size.height) / 2;
NSWindow* window = [[NSWindow alloc]
initWithContentRect:viewFrame
styleMask:style
backing:NSBackingStoreBuffered
defer:NO];
[window setTitle:NSStringFromStringView(title)];
[window makeKeyAndOrderFront:window];
return (__bridge_retained void*)window;
}
void CocoaTools::DestroyWindow(void* window)
{
(void)(__bridge_transfer NSWindow*)window;
}
void CocoaTools::GetWindowInfoFromWindow(WindowInfo* wi, void* cf_window)
{
if (cf_window)
{
NSWindow* window = (__bridge NSWindow*)cf_window;
float scale = [window backingScaleFactor];
NSView* view = [window contentView];
NSRect dims = [view frame];
wi->type = WindowInfo::Type::MacOS;
wi->window_handle = (__bridge void*)view;
wi->surface_width = dims.size.width * scale;
wi->surface_height = dims.size.height * scale;
wi->surface_scale = scale;
}
else
{
wi->type = WindowInfo::Type::Surfaceless;
}
}
static constexpr short STOP_EVENT_LOOP = 0x100;
void CocoaTools::RunCocoaEventLoop(bool forever)
{
NSDate* end = forever ? [NSDate distantFuture] : [NSDate distantPast];
[NSApplication sharedApplication]; // Ensure NSApp is initialized
while (true)
{ @autoreleasepool {
NSEvent* ev = [NSApp nextEventMatchingMask:NSEventMaskAny
untilDate:end
inMode:NSDefaultRunLoopMode
dequeue:YES];
if (!ev || ([ev type] == NSEventTypeApplicationDefined && [ev subtype] == STOP_EVENT_LOOP))
break;
[NSApp sendEvent:ev];
}}
}
void CocoaTools::StopMainThreadEventLoop()
{ @autoreleasepool {
NSEvent* ev = [NSEvent otherEventWithType:NSEventTypeApplicationDefined
location:{}
modifierFlags:0
timestamp:0
windowNumber:0
context:nil
subtype:STOP_EVENT_LOOP
data1:0
data2:0];
[NSApp postEvent:ev atStart:NO];
}}

View File

@@ -16,6 +16,7 @@
#include "fmt/format.h"
#include "common/Assertions.h"
#include "common/CocoaTools.h"
#include "common/Console.h"
#include "common/CrashHandler.h"
#include "common/FileSystem.h"
@@ -56,7 +57,8 @@ namespace GSRunner
static bool CreatePlatformWindow();
static void DestroyPlatformWindow();
static std::optional<WindowInfo> GetPlatformWindowInfo();
static void PumpPlatformMessages();
static void PumpPlatformMessages(bool forever = false);
static void StopPlatformMessagePump();
} // namespace GSRunner
static constexpr u32 WINDOW_WIDTH = 640;
@@ -809,6 +811,22 @@ void GSRunner::DumpStats()
#define main real_main
#endif
static void CPUThreadMain(VMBootParameters* params) {
if (VMManager::Initialize(*params))
{
// run until end
GSDumpReplayer::SetLoopCount(s_loop_count);
VMManager::SetState(VMState::Running);
while (VMManager::GetState() == VMState::Running)
VMManager::Execute();
VMManager::Shutdown(false);
GSRunner::DumpStats();
}
VMManager::Internal::CPUThreadShutdown();
GSRunner::StopPlatformMessagePump();
}
int main(int argc, char* argv[])
{
CrashHandler::Install();
@@ -837,16 +855,9 @@ int main(int argc, char* argv[])
VMManager::ApplySettings();
GSDumpReplayer::SetIsDumpRunner(true);
if (VMManager::Initialize(params))
{
// run until end
GSDumpReplayer::SetLoopCount(s_loop_count);
VMManager::SetState(VMState::Running);
while (VMManager::GetState() == VMState::Running)
VMManager::Execute();
VMManager::Shutdown(false);
GSRunner::DumpStats();
}
std::thread cputhread(CPUThreadMain, &params);
GSRunner::PumpPlatformMessages(/*forever=*/true);
cputhread.join();
VMManager::Internal::CPUThreadShutdown();
GSRunner::DestroyPlatformWindow();
@@ -859,9 +870,6 @@ void Host::PumpMessagesOnCPUThread()
// update GS thread copy of frame number
MTGS::RunOnGSThread([frame_number = GSDumpReplayer::GetFrameNumber()]() { s_dump_frame_number = frame_number; });
MTGS::RunOnGSThread([loop_number = GSDumpReplayer::GetLoopCount()]() { s_loop_number = loop_number; });
// process any window messages (but we shouldn't really have any)
GSRunner::PumpPlatformMessages();
}
s32 Host::Internal::GetTranslatedStringImpl(
@@ -975,16 +983,32 @@ std::optional<WindowInfo> GSRunner::GetPlatformWindowInfo()
return wi;
}
void GSRunner::PumpPlatformMessages()
static constexpr int SHUTDOWN_MSG = WM_APP + 0x100;
static DWORD MainThreadID;
void GSRunner::PumpPlatformMessages(bool forever)
{
MSG msg;
while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
while (true)
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == SHUTDOWN_MSG)
return;
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
if (!forever)
return;
WaitMessage();
}
}
void GSRunner::StopPlatformMessagePump()
{
PostThreadMessageW(MainThreadID, SHUTDOWN_MSG, 0, 0);
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
return DefWindowProcW(hwnd, msg, wParam, lParam);
@@ -1003,7 +1027,51 @@ int wmain(int argc, wchar_t** argv)
u8_argptrs.push_back(u8_args[i].data());
u8_argptrs.push_back(nullptr);
MainThreadID = GetCurrentThreadId();
return real_main(argc, u8_argptrs.data());
}
#endif // _WIN32
#elif defined(__APPLE__)
static void* s_window;
static WindowInfo s_wi;
bool GSRunner::CreatePlatformWindow()
{
pxAssertRel(!s_window, "Tried to create window when there already was one!");
s_window = CocoaTools::CreateWindow("PCSX2 GS Runner", WINDOW_WIDTH, WINDOW_HEIGHT);
CocoaTools::GetWindowInfoFromWindow(&s_wi, s_window);
PumpPlatformMessages();
return s_window;
}
void GSRunner::DestroyPlatformWindow()
{
if (s_window) {
CocoaTools::DestroyWindow(s_window);
s_window = nullptr;
}
}
std::optional<WindowInfo> GSRunner::GetPlatformWindowInfo()
{
WindowInfo wi;
if (s_window)
wi = s_wi;
else
wi.type = WindowInfo::Type::Surfaceless;
return wi;
}
void GSRunner::PumpPlatformMessages(bool forever)
{
CocoaTools::RunCocoaEventLoop(forever);
}
void GSRunner::StopPlatformMessagePump()
{
CocoaTools::StopMainThreadEventLoop();
}
#endif // _WIN32 / __APPLE__

View File

@@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-3.0+
#include "CoverDownloadDialog.h"
#include "QtUtils.h"
#include "pcsx2/GameList.h"
@@ -11,7 +12,7 @@ CoverDownloadDialog::CoverDownloadDialog(QWidget* parent /*= nullptr*/)
: QDialog(parent)
{
m_ui.setupUi(this);
m_ui.coverIcon->setPixmap(QIcon::fromTheme("artboard-2-line").pixmap(32));
QtUtils::SetScalableIcon(m_ui.coverIcon, QIcon::fromTheme(QStringLiteral("artboard-2-line")), QSize(32, 32));
updateEnabled();
connect(m_ui.start, &QPushButton::clicked, this, &CoverDownloadDialog::onStartClicked);

View File

@@ -149,7 +149,9 @@ DockTabBar::DockTabBar(KDDockWidgets::Core::TabBar* controller, QWidget* parent)
// that ends up taking ownerhsip of the style for the entire application!
if (QProxyStyle* proxy_style = qobject_cast<QProxyStyle*>(style()))
{
proxy_style->baseStyle()->setParent(qApp);
if (proxy_style->baseStyle() == qApp->style())
proxy_style->baseStyle()->setParent(qApp);
proxy_style->setBaseStyle(QStyleFactory::create(qApp->style()->name()));
}
}

View File

@@ -10,6 +10,7 @@
#include <kddockwidgets/Config.h>
#include <kddockwidgets/core/Group.h>
#include <kddockwidgets/core/Platform.h>
#include <kddockwidgets/core/indicators/SegmentedDropIndicatorOverlay.h>
#include <kddockwidgets/qtwidgets/ViewFactory.h>

View File

@@ -65,16 +65,17 @@ void ThreadView::openContextMenu(QPoint pos)
void ThreadView::onDoubleClick(const QModelIndex& index)
{
auto real_index = m_proxy_model->mapToSource(index);
switch (index.column())
{
case ThreadModel::ThreadColumns::ENTRY:
{
goToInMemoryView(m_model->data(index, Qt::UserRole).toUInt(), true);
goToInDisassembler(m_model->data(real_index, Qt::UserRole).toUInt(), true);
break;
}
default: // Default to PC
{
QModelIndex pc_index = m_model->index(index.row(), ThreadModel::ThreadColumns::PC);
QModelIndex pc_index = m_model->index(real_index.row(), ThreadModel::ThreadColumns::PC);
goToInDisassembler(m_model->data(pc_index, Qt::UserRole).toUInt(), true);
break;
}

View File

@@ -52,12 +52,12 @@ DisplayWidget::~DisplayWidget()
int DisplayWidget::scaledWindowWidth() const
{
return std::max(static_cast<int>(std::ceil(static_cast<qreal>(width()) * QtUtils::GetDevicePixelRatioForWidget(this))), 1);
return std::max(static_cast<int>(std::ceil(static_cast<qreal>(width()) * devicePixelRatioF())), 1);
}
int DisplayWidget::scaledWindowHeight() const
{
return std::max(static_cast<int>(std::ceil(static_cast<qreal>(height()) * QtUtils::GetDevicePixelRatioForWidget(this))), 1);
return std::max(static_cast<int>(std::ceil(static_cast<qreal>(height()) * devicePixelRatioF())), 1);
}
std::optional<WindowInfo> DisplayWidget::getWindowInfo()
@@ -268,7 +268,7 @@ bool DisplayWidget::event(QEvent* event)
if (!m_relative_mouse_enabled)
{
const qreal dpr = QtUtils::GetDevicePixelRatioForWidget(this);
const qreal dpr = devicePixelRatioF();
const QPoint mouse_pos = mouse_event->pos();
const float scaled_x = static_cast<float>(static_cast<qreal>(mouse_pos.x()) * dpr);
@@ -349,13 +349,12 @@ bool DisplayWidget::event(QEvent* event)
return true;
}
// According to https://bugreports.qt.io/browse/QTBUG-95925 the recommended practice for handling DPI change is responding to paint events
case QEvent::Paint:
case QEvent::DevicePixelRatioChange:
case QEvent::Resize:
{
QWidget::event(event);
const float dpr = QtUtils::GetDevicePixelRatioForWidget(this);
const float dpr = devicePixelRatioF();
const u32 scaled_width = static_cast<u32>(std::max(static_cast<int>(std::ceil(static_cast<qreal>(width()) * dpr)), 1));
const u32 scaled_height = static_cast<u32>(std::max(static_cast<int>(std::ceil(static_cast<qreal>(height()) * dpr)), 1));

View File

@@ -25,17 +25,17 @@ static constexpr int COVER_ART_HEIGHT = 512;
static constexpr int COVER_ART_SPACING = 32;
static constexpr int MIN_COVER_CACHE_SIZE = 256;
static int DPRScale(int size, float dpr)
static int DPRScale(int size, qreal dpr)
{
return static_cast<int>(static_cast<float>(size) * dpr);
return static_cast<int>(static_cast<qreal>(size) * dpr);
}
static int DPRUnscale(int size, float dpr)
static int DPRUnscale(int size, qreal dpr)
{
return static_cast<int>(static_cast<float>(size) / dpr);
return static_cast<int>(static_cast<qreal>(size) / dpr);
}
static void resizeAndPadPixmap(QPixmap* pm, int expected_width, int expected_height, float dpr)
static void resizeAndPadPixmap(QPixmap* pm, int expected_width, int expected_height, qreal dpr)
{
const int dpr_expected_width = DPRScale(expected_width, dpr);
const int dpr_expected_height = DPRScale(expected_height, dpr);
@@ -71,9 +71,8 @@ static void resizeAndPadPixmap(QPixmap* pm, int expected_width, int expected_hei
}
static QPixmap createPlaceholderImage(const QPixmap& placeholder_pixmap, int width, int height, float scale,
const std::string& title)
qreal dpr, const std::string& title)
{
const float dpr = qApp->devicePixelRatio();
QPixmap pm(placeholder_pixmap.copy());
pm.setDevicePixelRatio(dpr);
if (pm.isNull())
@@ -113,9 +112,10 @@ const char* GameListModel::getColumnName(Column col)
return s_column_names[static_cast<int>(col)];
}
GameListModel::GameListModel(float cover_scale, bool show_cover_titles, QObject* parent /* = nullptr */)
GameListModel::GameListModel(float cover_scale, bool show_cover_titles, qreal dpr, QObject* parent /* = nullptr */)
: QAbstractTableModel(parent)
, m_show_titles_for_covers(show_cover_titles)
, m_dpr{dpr}
{
loadSettings();
loadCommonImages();
@@ -160,6 +160,13 @@ void GameListModel::updateCacheSize(int width, int height)
m_cover_pixmap_cache.SetMaxCapacity(static_cast<int>(std::max(num_columns * num_rows, MIN_COVER_CACHE_SIZE)));
}
void GameListModel::setDevicePixelRatio(qreal dpr)
{
m_dpr = dpr;
loadCommonImages();
refreshCovers();
}
void GameListModel::loadOrGenerateCover(const GameList::Entry* ge)
{
// Why this counter: Every time we change the cover scale, we increment the counter variable. This way if the scale is changed
@@ -173,12 +180,11 @@ void GameListModel::loadOrGenerateCover(const GameList::Entry* ge)
const std::string cover_path(GameList::GetCoverImagePathForEntry(&entry));
if (!cover_path.empty())
{
const float dpr = qApp->devicePixelRatio();
image = QPixmap(QString::fromStdString(cover_path));
if (!image.isNull())
{
image.setDevicePixelRatio(dpr);
resizeAndPadPixmap(&image, getCoverArtWidth(), getCoverArtHeight(), dpr);
image.setDevicePixelRatio(m_dpr);
resizeAndPadPixmap(&image, getCoverArtWidth(), getCoverArtHeight(), m_dpr);
}
}
}
@@ -186,7 +192,7 @@ void GameListModel::loadOrGenerateCover(const GameList::Entry* ge)
const std::string& title = entry.GetTitle(m_prefer_english_titles);
if (image.isNull())
image = createPlaceholderImage(m_placeholder_pixmap, getCoverArtWidth(), getCoverArtHeight(), m_cover_scale, title);
image = createPlaceholderImage(m_placeholder_pixmap, getCoverArtWidth(), getCoverArtHeight(), m_cover_scale, m_dpr, title);
if (m_cover_scale_counter.load(std::memory_order_acquire) != counter)
image = {};
@@ -440,7 +446,7 @@ bool GameListModel::titlesLessThan(int left_row, int right_row) const
const GameList::Entry* left = GameList::GetEntryByIndex(left_row);
const GameList::Entry* right = GameList::GetEntryByIndex(right_row);
return QtHost::LocaleSensitiveCompare(QString::fromStdString(left->GetTitleSort(m_prefer_english_titles)),
QString::fromStdString(right->GetTitleSort(m_prefer_english_titles))) < 0;
QString::fromStdString(right->GetTitleSort(m_prefer_english_titles))) < 0;
}
bool GameListModel::lessThan(const QModelIndex& left_index, const QModelIndex& right_index, int column) const
@@ -569,16 +575,16 @@ QIcon GameListModel::getIconForType(GameList::EntryType type)
QIcon GameListModel::getIconForRegion(GameList::Region region)
{
return QIcon(
QStringLiteral("%1/icons/flags/%2.svg").arg(QtHost::GetResourcesBasePath()).arg(GameList::RegionToString(region)));
QStringLiteral("%1/icons/flags/%2.svg").arg(QtHost::GetResourcesBasePath()).arg(GameList::RegionToString(region, false)));
}
void GameListModel::loadThemeSpecificImages()
{
for (u32 type = 0; type < static_cast<u32>(GameList::EntryType::Count); type++)
m_type_pixmaps[type] = getIconForType(static_cast<GameList::EntryType>(type)).pixmap(QSize(24, 24));
m_type_pixmaps[type] = getIconForType(static_cast<GameList::EntryType>(type)).pixmap(QSize(24, 24), m_dpr);
for (u32 i = 0; i < static_cast<u32>(GameList::Region::Count); i++)
m_region_pixmaps[i] = getIconForRegion(static_cast<GameList::Region>(i)).pixmap(QSize(36, 26));
m_region_pixmaps[i] = getIconForRegion(static_cast<GameList::Region>(i)).pixmap(QSize(36, 26), m_dpr);
}
void GameListModel::loadCommonImages()
@@ -587,7 +593,7 @@ void GameListModel::loadCommonImages()
const QString base_path(QtHost::GetResourcesBasePath());
for (u32 i = 1; i < GameList::CompatibilityRatingCount; i++)
m_compatibility_pixmaps[i].load(QStringLiteral("%1/icons/star-%2.svg").arg(base_path).arg(i - 1));
m_compatibility_pixmaps[i] = QIcon((QStringLiteral("%1/icons/star-%2.svg").arg(base_path).arg(i - 1))).pixmap(QSize(88, 16), m_dpr);
m_placeholder_pixmap.load(QStringLiteral("%1/cover-placeholder.png").arg(base_path));
}

View File

@@ -43,7 +43,7 @@ public:
static QIcon getIconForType(GameList::EntryType type);
static QIcon getIconForRegion(GameList::Region region);
GameListModel(float cover_scale, bool show_cover_titles, QObject* parent = nullptr);
GameListModel(float cover_scale, bool show_cover_titles, qreal dpr, QObject* parent = nullptr);
~GameListModel();
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
@@ -71,6 +71,8 @@ public:
void refreshCovers();
void updateCacheSize(int width, int height);
void setDevicePixelRatio(qreal dpr);
Q_SIGNALS:
void coverScaleChanged();
@@ -94,6 +96,7 @@ private:
std::array<QPixmap, static_cast<u32>(GameList::Region::Count)> m_region_pixmaps;
QPixmap m_placeholder_pixmap;
QPixmap m_loading_pixmap;
qreal m_dpr;
std::array<QPixmap, static_cast<int>(GameList::CompatibilityRatingCount)> m_compatibility_pixmaps;
mutable LRUCache<std::string, QPixmap> m_cover_pixmap_cache;

View File

@@ -128,22 +128,25 @@ namespace
const int pix_width = static_cast<int>(pix.width() / pix.devicePixelRatio());
const int pix_height = static_cast<int>(pix.height() / pix.devicePixelRatio());
// Clip the pixmaps so they don't extend outside the column
painter->save();
painter->setClipRect(option.rect);
// Draw the icon, using code derived from QItemDelegate::drawDecoration()
const bool enabled = option.state & QStyle::State_Enabled;
const QPoint p = QPoint((r.width() - pix_width) / 2, (r.height() - pix_height) / 2);
if (option.state & QStyle::State_Selected)
{
// See QItemDelegate::selectedPixmap()
QString key = QString::fromStdString(fmt::format("{:016X}-{:d}", pix.cacheKey(), enabled));
// See QItemDelegate::selectedPixmap()
QColor color = option.palette.color(enabled ? QPalette::Normal : QPalette::Disabled, QPalette::Highlight);
color.setAlphaF(0.3f);
QString key = QString::fromStdString(fmt::format("{:016X}-{:d}-{:08X}", pix.cacheKey(), enabled, color.rgba()));
QPixmap pm;
if (!QPixmapCache::find(key, &pm))
{
QImage img = pix.toImage().convertToFormat(QImage::Format_ARGB32_Premultiplied);
QColor color = option.palette.color(enabled ? QPalette::Normal : QPalette::Disabled,
QPalette::Highlight);
color.setAlphaF(0.3f);
QPainter tinted_painter(&img);
tinted_painter.setCompositionMode(QPainter::CompositionMode_SourceAtop);
tinted_painter.fillRect(0, 0, img.width(), img.height(), color);
@@ -159,6 +162,9 @@ namespace
{
painter->drawPixmap(r.topLeft() + p, pix);
}
// Restore the old clip path.
painter->restore();
}
};
} // namespace
@@ -174,22 +180,27 @@ void GameListWidget::initialize()
{
const float cover_scale = Host::GetBaseFloatSettingValue("UI", "GameListCoverArtScale", 0.45f);
const bool show_cover_titles = Host::GetBaseBoolSettingValue("UI", "GameListShowCoverTitles", true);
m_model = new GameListModel(cover_scale, show_cover_titles, this);
m_model = new GameListModel(cover_scale, show_cover_titles, devicePixelRatioF(), this);
m_model->updateCacheSize(width(), height());
m_sort_model = new GameListSortModel(m_model);
m_sort_model->setSourceModel(m_model);
m_ui.setupUi(this);
for (u32 type = 0; type < static_cast<u32>(GameList::EntryType::Count); type++)
{
m_ui.filterType->addItem(GameListModel::getIconForType(static_cast<GameList::EntryType>(type)),
qApp->translate("GameList", GameList::EntryTypeToDisplayString(static_cast<GameList::EntryType>(type))));
if (type != static_cast<u32>(GameList::EntryType::Invalid))
{
m_ui.filterType->addItem(GameListModel::getIconForType(static_cast<GameList::EntryType>(type)),
GameList::EntryTypeToString(static_cast<GameList::EntryType>(type), true));
}
}
for (u32 region = 0; region < static_cast<u32>(GameList::Region::Count); region++)
{
m_ui.filterRegion->addItem(GameListModel::getIconForRegion(static_cast<GameList::Region>(region)),
qApp->translate("GameList", GameList::RegionToString(static_cast<GameList::Region>(region))));
GameList::RegionToString(static_cast<GameList::Region>(region), true));
}
connect(m_ui.viewGameList, &QPushButton::clicked, this, &GameListWidget::showGameList);
@@ -547,6 +558,18 @@ void GameListWidget::resizeEvent(QResizeEvent* event)
m_model->updateCacheSize(width(), height());
}
bool GameListWidget::event(QEvent* event)
{
if (event->type() == QEvent::DevicePixelRatioChange)
{
m_model->setDevicePixelRatio(devicePixelRatioF());
QWidget::event(event);
return true;
}
return QWidget::event(event);
}
void GameListWidget::resizeTableViewColumnsToFit()
{
QtUtils::ResizeColumnsForTableView(m_table_view, {

View File

@@ -93,6 +93,7 @@ public Q_SLOTS:
protected:
void resizeEvent(QResizeEvent* event);
bool event(QEvent* event) override;
private:
void loadTableViewColumnVisibilitySettings();

View File

@@ -103,9 +103,9 @@
<addaction name="actionMemoryCardSettings"/>
<addaction name="actionDEV9Settings"/>
<addaction name="actionFolderSettings"/>
<addaction name="actionAchievementSettings"/>
<addaction name="actionControllerSettings"/>
<addaction name="actionHotkeySettings"/>
<addaction name="actionAchievementSettings"/>
<addaction name="separator"/>
<addaction name="actionAddGameDirectory"/>
<addaction name="actionScanForNewGames"/>

View File

@@ -254,15 +254,6 @@ namespace QtUtils
widget->resize(width, height);
}
qreal GetDevicePixelRatioForWidget(const QWidget* widget)
{
const QScreen* screen_for_ratio = widget->screen();
if (!screen_for_ratio)
screen_for_ratio = QGuiApplication::primaryScreen();
return screen_for_ratio ? screen_for_ratio->devicePixelRatio() : static_cast<qreal>(1);
}
std::optional<WindowInfo> GetWindowInfoForWidget(QWidget* widget)
{
WindowInfo wi;
@@ -303,7 +294,7 @@ namespace QtUtils
}
#endif
const qreal dpr = GetDevicePixelRatioForWidget(widget);
const qreal dpr = widget->devicePixelRatioF();
wi.surface_width = static_cast<u32>(static_cast<qreal>(widget->width()) * dpr);
wi.surface_height = static_cast<u32>(static_cast<qreal>(widget->height()) * dpr);
wi.surface_scale = static_cast<float>(dpr);
@@ -374,4 +365,37 @@ namespace QtUtils
return true;
}
class IconVariableDpiFilter : QObject
{
public:
explicit IconVariableDpiFilter(QLabel* lbl, const QIcon& icon, const QSize& size, QObject* parent = nullptr)
: QObject(parent)
, m_lbl{lbl}
, m_icn{icon}
, m_size{size}
{
lbl->installEventFilter(this);
m_lbl->setPixmap(m_icn.pixmap(m_size, m_lbl->devicePixelRatioF()));
}
protected:
bool eventFilter(QObject* object, QEvent* event) override
{
if (object == m_lbl && event->type() == QEvent::DevicePixelRatioChange)
m_lbl->setPixmap(m_icn.pixmap(m_size, m_lbl->devicePixelRatioF()));
// Don't block the event
return false;
}
private:
QLabel* m_lbl;
QIcon m_icn;
QSize m_size;
};
void SetScalableIcon(QLabel* lbl, const QIcon& icon, const QSize& size)
{
new IconVariableDpiFilter(lbl, icon, size, lbl);
}
} // namespace QtUtils

View File

@@ -20,6 +20,7 @@ class QAction;
class QComboBox;
class QFileInfo;
class QFrame;
class QIcon;
class QLabel;
class QKeyEvent;
class QSlider;
@@ -82,9 +83,6 @@ namespace QtUtils
/// Adjusts the fixed size for a window if it's not resizeable.
void ResizePotentiallyFixedSizeWindow(QWidget* widget, int width, int height);
/// Returns the pixel ratio/scaling factor for a widget.
qreal GetDevicePixelRatioForWidget(const QWidget* widget);
/// Returns the common window info structure for a Qt widget.
std::optional<WindowInfo> GetWindowInfoForWidget(QWidget* widget);
@@ -102,4 +100,8 @@ namespace QtUtils
bool IsLightTheme(const QPalette& palette);
bool IsCompositorManagerRunning();
/// Sets the scalable icon to a given label (svg icons, or icons with multiple size pixmaps)
/// The icon will then be reloaded on DPR changes using an event filter
void SetScalableIcon(QLabel* lbl, const QIcon& icon, const QSize& size);
} // namespace QtUtils

View File

@@ -3,6 +3,7 @@
#include "AchievementLoginDialog.h"
#include "QtHost.h"
#include "QtUtils.h"
#include "pcsx2/Achievements.h"
@@ -15,7 +16,7 @@ AchievementLoginDialog::AchievementLoginDialog(QWidget* parent, Achievements::Lo
, m_reason(reason)
{
m_ui.setupUi(this);
m_ui.loginIcon->setPixmap(QIcon::fromTheme("login-box-line").pixmap(32));
QtUtils::SetScalableIcon(m_ui.loginIcon, QIcon::fromTheme(QStringLiteral("login-box-line")), QSize(32, 32));
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
// Adjust text if needed based on reason.
@@ -116,7 +117,6 @@ void AchievementLoginDialog::processLoginResult(bool result, const QString& mess
g_emu_thread->resetVM();
}
}
}
done(0);

View File

@@ -342,7 +342,7 @@ void AudioSettingsWidget::onExpansionSettingsClicked()
QDialog dlg(QtUtils::GetRootWidget(this));
Ui::AudioExpansionSettingsDialog dlgui;
dlgui.setupUi(&dlg);
dlgui.icon->setPixmap(QIcon::fromTheme(QStringLiteral("volume-up-line")).pixmap(32, 32));
QtUtils::SetScalableIcon(dlgui.icon, QIcon::fromTheme(QStringLiteral("volume-up-line")), QSize(32, 32));
SettingsInterface* sif = m_dialog->getSettingsInterface();
SettingWidgetBinder::BindWidgetToIntSetting(sif, dlgui.blockSize, "SPU2/Output", "ExpandBlockSize",
@@ -431,7 +431,7 @@ void AudioSettingsWidget::onStretchSettingsClicked()
QDialog dlg(QtUtils::GetRootWidget(this));
Ui::AudioStretchSettingsDialog dlgui;
dlgui.setupUi(&dlg);
dlgui.icon->setPixmap(QIcon::fromTheme(QStringLiteral("volume-up-line")).pixmap(32, 32));
QtUtils::SetScalableIcon(dlgui.icon, QIcon::fromTheme(QStringLiteral("volume-up-line")), QSize(32, 32));
SettingsInterface* sif = m_dialog->getSettingsInterface();
SettingWidgetBinder::BindWidgetToIntSetting(sif, dlgui.sequenceLength, "SPU2/Output", "StretchSequenceLengthMS",

View File

@@ -170,7 +170,7 @@ ControllerMouseSettingsDialog::ControllerMouseSettingsDialog(QWidget* parent, Co
SettingsInterface* sif = dialog->getProfileSettingsInterface();
m_ui.icon->setPixmap(QIcon::fromTheme(QStringLiteral("mouse-line")).pixmap(32, 32));
QtUtils::SetScalableIcon(m_ui.icon, QIcon::fromTheme(QStringLiteral("mouse-line")), QSize(32, 32));
ControllerSettingWidgetBinder::BindWidgetToInputProfileFloat(sif, m_ui.pointerXSpeedSlider, "Pad", "PointerXSpeed", 40.0f);
ControllerSettingWidgetBinder::BindWidgetToInputProfileFloat(sif, m_ui.pointerYSpeedSlider, "Pad", "PointerYSpeed", 40.0f);
@@ -202,11 +202,10 @@ ControllerMappingSettingsDialog::ControllerMappingSettingsDialog(ControllerSetti
SettingsInterface* sif = parent->getProfileSettingsInterface();
m_ui.icon->setPixmap(QIcon::fromTheme(QStringLiteral("settings-3-line")).pixmap(32, 32));
QtUtils::SetScalableIcon(m_ui.icon, QIcon::fromTheme(QStringLiteral("settings-3-line")), QSize(32, 32));
ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.ignoreInversion, "InputSources", "IgnoreInversion", false);
connect(m_ui.buttonBox->button(QDialogButtonBox::Close), &QPushButton::clicked, this, &QDialog::accept);
}
ControllerMappingSettingsDialog::~ControllerMappingSettingsDialog() = default;

View File

@@ -33,7 +33,7 @@ GameSummaryWidget::GameSummaryWidget(const GameList::Entry* entry, SettingsWindo
for (int i = 0; i < m_ui.region->count(); i++)
{
m_ui.region->setItemIcon(i,
QIcon(QStringLiteral("%1/icons/flags/%2.svg").arg(base_path).arg(GameList::RegionToString(static_cast<GameList::Region>(i)))));
QIcon(QStringLiteral("%1/icons/flags/%2.svg").arg(base_path).arg(GameList::RegionToString(static_cast<GameList::Region>(i), false))));
}
m_entry_path = entry->path;
@@ -73,16 +73,17 @@ void GameSummaryWidget::populateDetails(const GameList::Entry* entry)
m_ui.type->setCurrentIndex(static_cast<int>(entry->type));
m_ui.region->setCurrentIndex(static_cast<int>(entry->region));
//: First arg is a GameList compat; second is a string with space followed by star rating OR empty if Unknown compat
m_ui.compatibility->setText(tr("%0%1")
.arg(GameList::EntryCompatibilityRatingToString(entry->compatibility_rating))
.arg([entry]() {
if (entry->compatibility_rating == GameList::CompatibilityRating::Unknown)
return QStringLiteral("");
m_ui.compatibility->setText(
tr("%0%1")
.arg(GameList::EntryCompatibilityRatingToString(entry->compatibility_rating, true))
.arg([entry]() {
if (entry->compatibility_rating == GameList::CompatibilityRating::Unknown)
return QStringLiteral("");
const qsizetype compatibility_value = static_cast<qsizetype>(entry->compatibility_rating);
//: First arg is filled-in stars for game compatibility; second is empty stars; should be swapped for RTL languages
return tr(" %0%1").arg(QStringLiteral("").repeated(compatibility_value - 1)).arg(QStringLiteral("").repeated(6 - compatibility_value));
}()));
const qsizetype compatibility_value = static_cast<qsizetype>(entry->compatibility_rating);
//: First arg is filled-in stars for game compatibility; second is empty stars; should be swapped for RTL languages
return tr(" %0%1").arg(QStringLiteral("").repeated(compatibility_value - 1)).arg(QStringLiteral("").repeated(6 - compatibility_value));
}()));
int row = 0;
m_ui.detailsFormLayout->getWidgetPosition(m_ui.titleSort, &row, nullptr);
@@ -156,7 +157,7 @@ void GameSummaryWidget::onDiscPathChanged(const QString& value)
// force rescan of elf to update the serial
g_main_window->rescanFile(m_entry_path);
auto lock = GameList::GetLock();
const GameList::Entry* entry = GameList::GetEntryForPath(m_entry_path.c_str());
if (entry)

View File

@@ -9,6 +9,7 @@
#include <QtWidgets/QPushButton>
#include "Settings/MemoryCardCreateDialog.h"
#include "QtUtils.h"
#include "pcsx2/SIO/Memcard/MemoryCardFile.h"
@@ -16,7 +17,7 @@ MemoryCardCreateDialog::MemoryCardCreateDialog(QWidget* parent /* = nullptr */)
: QDialog(parent)
{
m_ui.setupUi(this);
m_ui.icon->setPixmap(QIcon::fromTheme("memcard-line").pixmap(m_ui.icon->width()));
QtUtils::SetScalableIcon(m_ui.icon, QIcon::fromTheme(QStringLiteral("memcard-line")), QSize(m_ui.icon->width(), m_ui.icon->width()));
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);

View File

@@ -10,6 +10,7 @@
#include <QtCore/QFile>
#include <QtGui/QPalette>
#include <QtGui/QPixmapCache>
#include <QtWidgets/QApplication>
#include <QtWidgets/QStyle>
#include <QtWidgets/QStyleFactory>
@@ -43,6 +44,12 @@ void QtHost::UpdateApplicationTheme()
SetStyleFromSettings();
SetIconThemeFromStyle();
// Qt generates tinted versions of icons and stores them in QPixmapCache
// The key used does not seem to include the theme (or tint colour).
// This can cause icons tinted for wrong theme to be used for selected/disabled.
// As a workaround, reset the pixmap cache to clear icons tinted for the old theme.
QPixmapCache::clear();
}
bool QtHost::IsDarkApplicationTheme()

File diff suppressed because it is too large Load Diff

View File

@@ -1455,7 +1455,7 @@ static uint cdvdStartSeek(uint newsector, CDVD_MODE_TYPE mode, bool transition_t
}
isSeeking = true;
}
else if(!drive_speed_change_cycles)
else if (!drive_speed_change_cycles)
{
CDVD_LOG("CdSeek Begin > Contiguous block without seek - delta=%d sectors", delta);

View File

@@ -222,7 +222,7 @@ bool IOCtlSrc::ReadDVDInfo()
{
Console.Warning("IOCTL_DVD_READ_STRUCTURE not supported");
}
else if(GetLastError() != ERROR_UNRECOGNIZED_MEDIA) // ERROR_UNRECOGNIZED_MEDIA means probably a CD or no disc
else if (GetLastError() != ERROR_UNRECOGNIZED_MEDIA) // ERROR_UNRECOGNIZED_MEDIA means probably a CD or no disc
{
Console.Warning("IOCTL Unknown Error %d", GetLastError());
}

View File

@@ -1237,9 +1237,16 @@ fixup_file_properties(PCSX2)
force_include_last(PCSX2_FLAGS "/(usr|local)/include/?$")
if (APPLE)
find_library(APPKIT_LIBRARY AppKit)
find_library(IOKIT_LIBRARY IOKit)
find_library(METAL_LIBRARY Metal)
find_library(QUARTZCORE_LIBRARY QuartzCore)
target_link_libraries(PCSX2_FLAGS INTERFACE ${METAL_LIBRARY} ${QUARTZCORE_LIBRARY})
target_link_libraries(PCSX2_FLAGS INTERFACE
${APPKIT_LIBRARY}
${IOKIT_LIBRARY}
${METAL_LIBRARY}
${QUARTZCORE_LIBRARY}
)
endif()
set_property(GLOBAL PROPERTY PCSX2_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})

View File

@@ -33,7 +33,7 @@ struct BreakPoint
bool temporary = false;
bool stepping = false;
bool hasCond;
bool hasCond = false;
BreakPointCond cond;
BreakPointCpu cpu;

View File

@@ -236,7 +236,7 @@ static __ri void DmaExec( void (*func)(), u32 mem, u32 value )
cpuClearInt( 10 );
QueuedDMA._u16 &= ~(1 << 10); //Clear any queued DMA requests for this channel
}
else if(channel == 2)
else if (channel == 2)
{
cpuClearInt( 11 );
QueuedDMA._u16 &= ~(1 << 11); //Clear any queued DMA requests for this channel
@@ -274,7 +274,7 @@ static __ri void DmaExec( void (*func)(), u32 mem, u32 value )
{
func();
}
else if(reg.chcr.STR)
else if (reg.chcr.STR)
{
//DevCon.Warning(L"32bit %s DMA Start while DMAC Disabled\n", ChcrName(mem));
QueuedDMA._u16 |= (1 << ChannelNumber(mem)); //Queue the DMA up to be started then the DMA's are Enabled and or the Suspend is lifted

View File

@@ -20,7 +20,11 @@ There are a number of ways to help the project, whether it be bug reporting, gam
* Want to make changes to emulator code? [Check out the issue tracker](https://github.com/PCSX2/pcsx2/issues), or [fork the PCSX2 repository and work on your own ideas](https://github.com/PCSX2/pcsx2).
* Want to patch games? [Check out the cheats and patches forum thread for inspiration](https://forums.pcsx2.net/Thread-Post-your-PCSX2-cheats-patches-here). There are other threads to find as well, [such as those dedicated to 60 FPS patches](https://forums.pcsx2.net/Thread-60-fps-codes) or [widescreen patches](https://forums.pcsx2.net/Thread-PCSX2-Widescreen-Game-Patches).
<<<<<<< HEAD
* Want to report bugs you have discovered in your games? Head over to [the Bug Reporting section of the PCSX2 forums](https://forums.pcsx2.net/Forum-Bug-reporting), to [the PCSX2 Discord](https://pcsx2.net/discord), or to [the GitHub issues section](https://github.com/PCSX2/pcsx2/issues).
=======
* Want to report bugs you have discovered in your games? [Head over to the Bug Reporting section of the PCSX2 forums](https://forums.pcsx2.net/Forum-Bug-reporting). Also check out our [Issue reporting guide](https://pcsx2.net/docs/troubleshooting/identify) for info on how to better report to us issues.
>>>>>>> 091a4ee5a (docs: update in order to redirect from the GH wiki to our website)
* Want to update us on the compatibility of your games? [Take a look at the Public Compatibility List on the PCSX2 forums](https://forums.pcsx2.net/Forum-Public-compatibility-list)
* Want to improve the PCSX2 wiki? [Here is how to contribute](https://wiki.pcsx2.net/How_to_contribute)
@@ -120,9 +124,7 @@ If you are using a disc:
Fast Boot, enabled by default, will directly mount and launch the game without first launching the PS2 BIOS. You may disable this in Settings > BIOS > Fast Boot if you wish to see the BIOS startup animation or if Fast Boot is causing an issue.
### Question 18: How do I build the PCSX2 source code?
* [Windows build guide](https://github.com/PCSX2/pcsx2/wiki/12-Building-on-Windows)
* [Linux build guide](https://github.com/PCSX2/pcsx2/wiki/10-Building-on-Linux)
* [macOS build guide](https://github.com/PCSX2/pcsx2/wiki/11-Building-on-MacOS)
Have a nice [building guide](https://pcsx2.net/docs/advanced/building) :) .
### Question 19: When will the next version be released?
It will be released when it is ready. Please don't waste your time and ours asking when.

View File

@@ -249,7 +249,6 @@ void CTC1() {
void CVT_S() {
_FdValf_ = (float)_FsValSl_;
_FdValf_ = fpuDouble( _FdValUl_ );
}
void CVT_W() {

View File

@@ -339,7 +339,7 @@ void GSClut::Read(const GIFRegTEX0& TEX0)
break;
}
}
else if(TEX0.CPSM == PSMCT16 || TEX0.CPSM == PSMCT16S)
else if (TEX0.CPSM == PSMCT16 || TEX0.CPSM == PSMCT16S)
{
switch(TEX0.PSM)
{
@@ -462,7 +462,8 @@ void GSClut::GetAlphaMinMax32(int& amin_out, int& amax_out)
{
// call only after Read32
pxAssert(!m_read.dirty);
if (m_read.dirty)
GL_INS("GSClut: GetAlphaMinMax32 m_read.dirty");
if (m_read.adirty)
{

View File

@@ -70,6 +70,7 @@ GSLocalMemory::GSLocalMemory()
psm.wfa = &GSLocalMemory::WritePixel32;
psm.bpp = psm.trbpp = 32;
psm.pal = 0;
psm.cs = GSVector2i(8, 2);
psm.bs = GSVector2i(8, 8);
psm.pgs = GSVector2i(64, 32);
psm.msk = 0xff;
@@ -197,6 +198,11 @@ GSLocalMemory::GSLocalMemory()
m_psm[PSMCT16].fmt = m_psm[PSMZ16].fmt = PSM_FMT_16;
m_psm[PSMCT16S].fmt = m_psm[PSMZ16S].fmt = PSM_FMT_16;
m_psm[PSGPU24].cs = GSVector2i(16, 2);
m_psm[PSMCT16].cs = m_psm[PSMCT16S].bs = GSVector2i(16, 2);
m_psm[PSMT8].cs = GSVector2i(16, 4);
m_psm[PSMT4].cs = GSVector2i(32, 4);
m_psm[PSMZ16].cs = m_psm[PSMZ16S].bs = GSVector2i(16, 2);
m_psm[PSGPU24].bs = GSVector2i(16, 8);
m_psm[PSMCT16].bs = m_psm[PSMCT16S].bs = GSVector2i(16, 8);

View File

@@ -460,7 +460,7 @@ public:
readTexture rtx, rtxP;
readTextureBlock rtxb, rtxbP;
u16 bpp, trbpp, pal, fmt;
GSVector2i bs, pgs;
GSVector2i cs, bs, pgs;
u8 msk, depth;
u32 fmsk;
};

View File

@@ -32,7 +32,7 @@ static __fi bool IsFirstProvokingVertex()
return (GSIsHardwareRenderer() && !g_gs_device->Features().provoking_vertex_last);
}
constexpr int GSState::GetSaveStateSize()
constexpr int GSState::GetSaveStateSize(int version)
{
int size = 0;
@@ -78,6 +78,19 @@ constexpr int GSState::GetSaveStateSize()
size += sizeof(m_tr.x);
size += sizeof(m_tr.y);
if (version >= 9)
{
size += sizeof(m_tr.w);
size += sizeof(m_tr.h);
size += sizeof(m_tr.m_blit);
size += sizeof(m_tr.m_pos);
size += sizeof(m_tr.m_reg);
size += sizeof(m_tr.rect);
size += sizeof(m_tr.total);
size += sizeof(m_tr.start);
size += sizeof(m_tr.end);
size += sizeof(m_tr.write);
}
size += GSLocalMemory::m_vmsize;
size += (sizeof(GIFPath::tag) + sizeof(GIFPath::reg)) * 4 /* std::size(GSState::m_path) */; // std::size won't work without an instance.
size += sizeof(m_q);
@@ -1445,10 +1458,10 @@ void GSState::GIFRegHandlerTRXDIR(const GIFReg* RESTRICT r)
switch (m_env.TRXDIR.XDIR)
{
case 0: // host -> local
m_tr.Init(m_env.TRXPOS.DSAX, m_env.TRXPOS.DSAY, m_env.BITBLTBUF, true);
m_tr.Init(m_env.TRXPOS, m_env.TRXREG, m_env.BITBLTBUF, true);
break;
case 1: // local -> host
m_tr.Init(m_env.TRXPOS.SSAX, m_env.TRXPOS.SSAY, m_env.BITBLTBUF, false);
m_tr.Init(m_env.TRXPOS, m_env.TRXREG, m_env.BITBLTBUF, false);
break;
case 2: // local -> local
CheckWriteOverlap(true, true);
@@ -1549,16 +1562,13 @@ void GSState::FlushWrite()
GSVector4i r;
r.left = m_env.TRXPOS.DSAX;
r.top = m_env.TRXPOS.DSAY;
r.right = r.left + m_env.TRXREG.RRW;
r.bottom = r.top + m_env.TRXREG.RRH;
r = m_tr.rect;
InvalidateVideoMem(m_env.BITBLTBUF, r);
const GSLocalMemory::writeImage wi = GSLocalMemory::m_psm[m_env.BITBLTBUF.DPSM].wi;
wi(m_mem, m_tr.x, m_tr.y, &m_tr.buff[m_tr.start], len, m_env.BITBLTBUF, m_env.TRXPOS, m_env.TRXREG);
wi(m_mem, m_tr.x, m_tr.y, &m_tr.buff[m_tr.start], len, m_tr.m_blit, m_tr.m_pos, m_tr.m_reg);
m_tr.start += len;
@@ -1931,12 +1941,9 @@ void GSState::Write(const u8* mem, int len)
if (m_env.TRXDIR.XDIR == 3)
return;
const int w = m_env.TRXREG.RRW;
const int h = m_env.TRXREG.RRH;
CheckWriteOverlap(true, false);
if (!m_tr.Update(w, h, GSLocalMemory::m_psm[m_env.BITBLTBUF.DPSM].trbpp, len))
if (!m_tr.Update(m_tr.w, m_tr.h, GSLocalMemory::m_psm[m_tr.m_blit.DPSM].trbpp, len))
{
m_env.TRXDIR.XDIR = 3;
return;
@@ -1949,10 +1956,7 @@ void GSState::Write(const u8* mem, int len)
{
GSVector4i r;
r.left = m_env.TRXPOS.DSAX;
r.top = m_env.TRXPOS.DSAY;
r.right = r.left + m_env.TRXREG.RRW;
r.bottom = r.top + m_env.TRXREG.RRH;
r = m_tr.rect;
s_last_transfer_draw_n = s_n;
// Store the transfer for preloading new RT's.
@@ -1974,15 +1978,15 @@ void GSState::Write(const u8* mem, int len)
GL_CACHE("Write! %u ... => 0x%x W:%d F:%s (DIR %d%d), dPos(%d %d) size(%d %d) draw %d", s_transfer_n,
blit.DBP, blit.DBW, psm_str(blit.DPSM),
m_env.TRXPOS.DIRX, m_env.TRXPOS.DIRY,
m_env.TRXPOS.DSAX, m_env.TRXPOS.DSAY, w, h, s_n);
m_tr.m_pos.DIRX, m_tr.m_pos.DIRY,
m_tr.x, m_tr.y, m_tr.w, m_tr.h, s_n);
if (len >= m_tr.total)
{
// received all data in one piece, no need to buffer it
InvalidateVideoMem(blit, r);
psm.wi(m_mem, m_tr.x, m_tr.y, mem, m_tr.total, blit, m_env.TRXPOS, m_env.TRXREG);
psm.wi(m_mem, m_tr.x, m_tr.y, mem, m_tr.total, blit, m_tr.m_pos, m_tr.m_reg);
m_tr.start = m_tr.end = m_tr.total;
@@ -2260,7 +2264,7 @@ void GSState::Move()
vm[doff] = (vm[doff] & 0xff000000) | (vm[soff] & 0x00ffffff);
});
}
else // if(spsm.trbpp == 16)
else // if (spsm.trbpp == 16)
{
u16* vm = m_mem.vm16();
copy(dpo.assertSizesMatch(GSLocalMemory::swizzle16), spo.assertSizesMatch(GSLocalMemory::swizzle16), [vm](u32 doff, u32 soff)
@@ -2334,7 +2338,7 @@ void GSState::ReadLocalMemoryUnsync(u8* mem, int qwc, GIFRegBITBLTBUF BITBLTBUF,
GSTransferBuffer tb;
if(m_tr.end >= m_tr.total || m_tr.write == true)
tb.Init(TRXPOS.SSAX, TRXPOS.SSAY, BITBLTBUF, false);
tb.Init(TRXPOS, TRXREG, BITBLTBUF, false);
int len = qwc * 16;
if (!tb.Update(w, h, bpp, len))
@@ -2578,13 +2582,14 @@ static void ReadState(T* dst, u8*& src, size_t len = sizeof(T))
int GSState::Freeze(freezeData* fd, bool sizeonly)
{
const u32 version = STATE_VERSION;
if (sizeonly)
{
fd->size = GetSaveStateSize();
fd->size = GetSaveStateSize(version);
return 0;
}
if (!fd->data || fd->size < GetSaveStateSize())
if (!fd->data || fd->size < GetSaveStateSize(version))
return -1;
Flush(GSFlushReason::SAVESTATE);
@@ -2593,7 +2598,6 @@ int GSState::Freeze(freezeData* fd, bool sizeonly)
ReadbackTextureCache();
u8* data = fd->data;
const u32 version = STATE_VERSION;
WriteState(data, &version);
WriteState(data, &m_env.PRIM);
@@ -2636,6 +2640,18 @@ int GSState::Freeze(freezeData* fd, bool sizeonly)
data += sizeof(GIFReg); // obsolite
WriteState(data, &m_tr.x);
WriteState(data, &m_tr.y);
// Version 9 up.
WriteState(data, &m_tr.w);
WriteState(data, &m_tr.h);
WriteState(data, &m_tr.m_blit);
WriteState(data, &m_tr.m_pos);
WriteState(data, &m_tr.m_reg);
WriteState(data, &m_tr.rect);
WriteState(data, &m_tr.total);
WriteState(data, &m_tr.start);
WriteState(data, &m_tr.end);
WriteState(data, &m_tr.write);
// End of version 9 changes.
WriteState(data, m_mem.m_vm8, m_mem.m_vmsize);
for (GIFPath& path : m_path)
@@ -2663,15 +2679,15 @@ int GSState::Defrost(const freezeData* fd)
if (!fd || !fd->data || fd->size == 0)
return -1;
if (fd->size < GetSaveStateSize())
return -1;
u8* data = fd->data;
u32 version;
ReadState(&version, data);
if (fd->size < GetSaveStateSize(version))
return -1;
if (version > STATE_VERSION)
{
Console.Error("GS: Savestate version is incompatible. Load aborted.");
@@ -2701,12 +2717,6 @@ int GSState::Defrost(const freezeData* fd)
ReadState(&m_env.TRXPOS, data);
ReadState(&m_env.TRXREG, data);
ReadState(&m_env.TRXREG, data); // obsolete
// Technically this value ought to be saved like m_tr.x/y (break
// compatibility) but so far only a single game (Motocross Mania) really
// depends on this value (i.e != BITBLTBUF) Savestates are likely done at
// VSYNC, so not in the middle of a texture transfer, therefore register
// will be set again properly
m_tr.m_blit = m_env.BITBLTBUF;
for (int i = 0; i < 2; i++)
{
@@ -2742,9 +2752,36 @@ int GSState::Defrost(const freezeData* fd)
data += sizeof(GIFReg); // obsolite
ReadState(&m_tr.x, data);
ReadState(&m_tr.y, data);
ReadState(m_mem.m_vm8, data, m_mem.m_vmsize);
m_tr.total = 0; // TODO: restore transfer state
if (version >= 9)
{
ReadState(&m_tr.w, data);
ReadState(&m_tr.h, data);
ReadState(&m_tr.m_blit, data);
ReadState(&m_tr.m_pos, data);
ReadState(&m_tr.m_reg, data);
ReadState(&m_tr.rect, data);
ReadState(&m_tr.total, data);
ReadState(&m_tr.start, data);
ReadState(&m_tr.end, data);
ReadState(&m_tr.write, data);
}
else
{
m_tr.w = m_env.TRXREG.RRW;
m_tr.h = m_env.TRXREG.RRH;
m_tr.m_blit = m_env.BITBLTBUF;
m_tr.m_pos = m_env.TRXPOS;
m_tr.m_reg = m_env.TRXREG;
// Assume the last transfer was a write (but nuke it).
m_tr.rect = GSVector4i(m_env.TRXPOS.DSAX, m_env.TRXPOS.DSAY, m_env.TRXPOS.DSAX + m_tr.w, m_env.TRXPOS.DSAY + m_tr.h);
m_tr.total = 0;
m_tr.start = 0;
m_tr.end = 0;
m_tr.write = true;
}
ReadState(m_mem.m_vm8, data, m_mem.m_vmsize);
for (GIFPath& path : m_path)
{
@@ -3140,7 +3177,8 @@ bool GSState::SpriteDrawWithoutGaps()
}
else
{
if ((std::abs(dpX - first_dpX) >= 16 && (i + 2) < m_vertex.next) || std::abs(this_start_X - last_pX) >= 16)
const int dpY = v[i + 1].XYZ.Y - v[i].XYZ.Y;
if ((std::abs(dpY - first_dpY) >= 16 && (i + 2) < m_vertex.next) || std::abs(this_start_X - last_pX) >= 16)
return false;
}
}
@@ -4562,14 +4600,19 @@ GSState::GSTransferBuffer::~GSTransferBuffer()
_aligned_free(buff);
}
void GSState::GSTransferBuffer::Init(int tx, int ty, const GIFRegBITBLTBUF& blit, bool is_write)
void GSState::GSTransferBuffer::Init(GIFRegTRXPOS& TRXPOS, GIFRegTRXREG& TRXREG, const GIFRegBITBLTBUF& blit, bool is_write)
{
x = tx;
y = ty;
x = is_write ? TRXPOS.DSAX : TRXPOS.SSAX;
y = is_write ? TRXPOS.DSAY : TRXPOS.SSAY;
w = TRXREG.RRW;
h = TRXREG.RRH;
rect = GSVector4i(x, y, x + w, y + h);
total = 0;
start = 0;
end = 0;
m_blit = blit;
m_pos = TRXPOS;
m_reg = TRXREG;
write = is_write;
}

View File

@@ -21,7 +21,7 @@ public:
GSState();
virtual ~GSState();
static constexpr int GetSaveStateSize();
static constexpr int GetSaveStateSize(int version);
private:
// RESTRICT prevents multiple loads of the same part of the register when accessing its bitfields (the compiler is happy to know that memory writes in-between will not go there)
@@ -108,15 +108,19 @@ private:
struct GSTransferBuffer
{
int x = 0, y = 0;
int w = 0, h = 0;
int start = 0, end = 0, total = 0;
u8* buff = nullptr;
GSVector4i rect = GSVector4i::zero();
GIFRegBITBLTBUF m_blit = {};
GIFRegTRXPOS m_pos = {};
GIFRegTRXREG m_reg = {};
bool write = false;
GSTransferBuffer();
~GSTransferBuffer();
void Init(int tx, int ty, const GIFRegBITBLTBUF& blit, bool write);
void Init(GIFRegTRXPOS& TRXPOS, GIFRegTRXREG& TRXREG, const GIFRegBITBLTBUF& blit, bool is_write);
bool Update(int tw, int th, int bpp, int& len);
} m_tr;
@@ -259,7 +263,7 @@ public:
static int s_last_transfer_draw_n;
static int s_transfer_n;
static constexpr u32 STATE_VERSION = 8;
static constexpr u32 STATE_VERSION = 9;
enum REG_DIRTY
{

View File

@@ -68,6 +68,7 @@ const char* shaderName(ShaderConvert value)
case ShaderConvert::DEPTH_COPY: return "ps_depth_copy";
case ShaderConvert::DOWNSAMPLE_COPY: return "ps_downsample_copy";
case ShaderConvert::RGBA_TO_8I: return "ps_convert_rgba_8i";
case ShaderConvert::RGB5A1_TO_8I: return "ps_convert_rgb5a1_8i";
case ShaderConvert::CLUT_4: return "ps_convert_clut_4";
case ShaderConvert::CLUT_8: return "ps_convert_clut_8";
case ShaderConvert::YUV: return "ps_yuv";

View File

@@ -44,6 +44,7 @@ enum class ShaderConvert
DEPTH_COPY,
DOWNSAMPLE_COPY,
RGBA_TO_8I,
RGB5A1_TO_8I,
CLUT_4,
CLUT_8,
YUV,

View File

@@ -1236,36 +1236,18 @@ void GSDevice11::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r,
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
D3D11_BOX box = {(UINT)r.left, (UINT)r.top, 0U, (UINT)r.right, (UINT)r.bottom, 1U};
D3D11_BOX box = {static_cast<UINT>(r.left), static_cast<UINT>(r.top), 0U, static_cast<UINT>(r.right), static_cast<UINT>(r.bottom), 1U};
// DX api isn't happy if we pass a box for depth copy
// It complains that depth/multisample must be a full copy
// and asks us to use a NULL for the box
// DX11 doesn't support partial depth copy so we need to
// either pass a nullptr D3D11_BOX for a full depth copy or use CopyResource instead.
// Alternatively use shader copy StretchRect, or full depth copy with
// adjusting the scissor and UVs in the shader.
const bool depth = (sTex->GetType() == GSTexture::Type::DepthStencil);
auto pBox = depth ? nullptr : &box;
const u32 x = depth ? 0 : destX;
const u32 y = depth ? 0 : destY;
m_ctx->CopySubresourceRegion(*(GSTexture11*)dTex, 0, destX, destY, 0, *(GSTexture11*)sTex, 0, pBox);
}
void GSDevice11::CloneTexture(GSTexture* src, GSTexture** dest, const GSVector4i& rect)
{
pxAssertMsg(src->GetType() == GSTexture::Type::DepthStencil || src->GetType() == GSTexture::Type::RenderTarget, "Source is RT or DS.");
CommitClear(src);
const int w = src->GetWidth();
const int h = src->GetHeight();
if (src->GetType() == GSTexture::Type::DepthStencil)
{
// DX11 requires that you copy the entire depth buffer.
*dest = CreateDepthStencil(w, h, src->GetFormat(), false);
CopyRect(src, *dest, GSVector4i(0, 0, w, h), 0, 0);
}
else
{
*dest = CreateRenderTarget(w, h, src->GetFormat(), false);
CopyRect(src, *dest, rect, rect.left, rect.top);
}
m_ctx->CopySubresourceRegion(*(GSTexture11*)dTex, 0, x, y, 0, *(GSTexture11*)sTex, 0, pBox);
}
void GSDevice11::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ShaderConvert shader, bool linear)
@@ -1435,14 +1417,14 @@ void GSDevice11::ConvertToIndexedTexture(GSTexture* sTex, float sScale, u32 offs
{
float scale;
float pad1[3];
u32 SBW, DBW, pad3;
u32 SBW, DBW, SPSM;
};
const Uniforms cb = {sScale, {}, SBW, DBW};
const Uniforms cb = {sScale, {}, SBW, DBW, SPSM};
m_ctx->UpdateSubresource(m_merge.cb.get(), 0, nullptr, &cb, 0, 0);
const GSVector4 dRect(0, 0, dTex->GetWidth(), dTex->GetHeight());
const ShaderConvert shader = ShaderConvert::RGBA_TO_8I;
const ShaderConvert shader = ((SPSM & 0xE) == 0) ? ShaderConvert::RGBA_TO_8I : ShaderConvert::RGB5A1_TO_8I;
StretchRect(sTex, GSVector4::zero(), dTex, dRect, m_convert.ps[static_cast<int>(shader)].get(), m_merge.cb.get(), nullptr, false);
}
@@ -2495,20 +2477,9 @@ D3D_SHADER_MACRO* GSDevice11::ShaderMacro::GetPtr()
return (D3D_SHADER_MACRO*)mout.data();
}
/// Checks that we weren't sent things we declared we don't support
/// Clears things we don't support that can be quietly disabled
static void preprocessSel(GSDevice11::PSSelector& sel)
{
pxAssert(sel.write_rg == 0); // Not supported, shouldn't be sent
}
void GSDevice11::RenderHW(GSHWDrawConfig& config)
{
pxAssert(!config.require_full_barrier); // We always specify no support so it shouldn't request this
preprocessSel(config.ps);
GSVector2i rtsize = (config.rt ? config.rt : config.ds)->GetSize();
const GSVector2i rtsize = (config.rt ? config.rt : config.ds)->GetSize();
GSTexture* colclip_rt = g_gs_device->GetColorClipTexture();
if (colclip_rt)
@@ -2536,15 +2507,17 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
{
config.colclip_update_area = config.drawarea;
const GSVector4 dRect = GSVector4((config.colclip_mode == GSHWDrawConfig::ColClipMode::ConvertOnly) ? GSVector4i::loadh(rtsize) : config.drawarea);
const GSVector4 sRect = dRect / GSVector4(rtsize.x, rtsize.y).xyxy();
colclip_rt = CreateRenderTarget(rtsize.x, rtsize.y, GSTexture::Format::ColorClip);
if (!colclip_rt)
{
Console.Warning("D3D11: Failed to allocate ColorClip render target, aborting draw.");
return;
}
g_gs_device->SetColorClipTexture(colclip_rt);
// Warning: StretchRect must be called before BeginScene otherwise
// vertices will be overwritten. Trust me you don't want to do that.
const GSVector4 dRect = GSVector4((config.colclip_mode == GSHWDrawConfig::ColClipMode::ConvertOnly) ? GSVector4i::loadh(rtsize) : config.drawarea);
const GSVector4 sRect = dRect / GSVector4(rtsize.x, rtsize.y).xyxy();
StretchRect(config.rt, sRect, colclip_rt, dRect, ShaderConvert::COLCLIP_INIT, false);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
}
@@ -2555,12 +2528,16 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
{
primid_tex = CreateRenderTarget(rtsize.x, rtsize.y, GSTexture::Format::PrimID, false);
if (!primid_tex)
{
Console.WriteLn("D3D11: Failed to allocate DATE image, aborting draw.");
return;
}
StretchRect(colclip_rt ? colclip_rt : config.rt, GSVector4(config.drawarea) / GSVector4(rtsize).xyxy(),
primid_tex, GSVector4(config.drawarea), m_date.primid_init_ps[static_cast<u8>(config.datm)].get(), nullptr, false);
}
else if (config.destination_alpha != GSHWDrawConfig::DestinationAlphaMode::Off)
else if (config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::Stencil ||
config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::StencilOne)
{
const GSVector4 src = GSVector4(config.drawarea) / GSVector4(config.ds->GetSize()).xyxy();
const GSVector4 dst = src * 2.0f - 1.0f;
@@ -2630,21 +2607,39 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
PSSetShaderResource(1, config.pal);
}
GSTexture* rt_copy = nullptr;
if (config.require_one_barrier || (config.tex && config.tex == config.rt)) // Used as "bind rt" flag when texture barrier is unsupported.
GSTexture* draw_rt_clone = nullptr;
if (config.require_one_barrier || (config.tex && config.tex == config.rt))
{
// Bind the RT.This way special effect can use it.
// Do not always bind the rt when it's not needed,
// only bind it when effects use it such as fbmask emulation currently
// because we copy the frame buffer and it is quite slow.
CloneTexture(colclip_rt ? colclip_rt : config.rt, &rt_copy, config.drawarea);
if (rt_copy)
// Requires a copy of the RT.
// Used as "bind rt" flag when texture barrier is unsupported for tex is fb.
draw_rt_clone = CreateTexture(rtsize.x, rtsize.y, 1, colclip_rt ? GSTexture::Format::ColorClip : GSTexture::Format::Color, true);
if (draw_rt_clone)
{
CopyRect(colclip_rt ? colclip_rt : config.rt, draw_rt_clone, config.drawarea, config.drawarea.left, config.drawarea.top);
if (config.require_one_barrier)
PSSetShaderResource(2, rt_copy);
PSSetShaderResource(2, draw_rt_clone);
if (config.tex && config.tex == config.rt)
PSSetShaderResource(0, rt_copy);
PSSetShaderResource(0, draw_rt_clone);
}
else
Console.Warning("D3D11: Failed to allocate temp texture for RT copy.");
}
GSTexture* draw_ds_clone = nullptr;
if (config.tex && config.tex == config.ds)
{
// DX requires a copy when sampling the depth buffer.
draw_ds_clone = CreateDepthStencil(rtsize.x, rtsize.y, config.ds->GetFormat(), false);
if (draw_ds_clone)
{
CopyRect(config.ds, draw_ds_clone, config.drawarea, config.drawarea.left, config.drawarea.top);
PSSetShaderResource(0, draw_ds_clone);
}
else
Console.Warning("D3D11: Failed to allocate temp texture for DS copy.");
}
SetupVS(config.vs, &config.cb_vs);
@@ -2681,7 +2676,6 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
if (config.alpha_second_pass.enable)
{
preprocessSel(config.alpha_second_pass.ps);
if (config.cb_ps.FogColor_AREF.a != config.alpha_second_pass.ps_aref)
{
config.cb_ps.FogColor_AREF.a = config.alpha_second_pass.ps_aref;
@@ -2697,8 +2691,12 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
DrawIndexedPrimitive();
}
if (rt_copy)
Recycle(rt_copy);
if (draw_rt_clone)
Recycle(draw_rt_clone);
if (draw_ds_clone)
Recycle(draw_ds_clone);
if (primid_tex)
Recycle(primid_tex);

View File

@@ -291,7 +291,6 @@ public:
std::unique_ptr<GSDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GSTexture::Format format) override;
void CommitClear(GSTexture* t);
void CloneTexture(GSTexture* src, GSTexture** dest, const GSVector4i& rect);
void CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r, u32 destX, u32 destY) override;

View File

@@ -703,11 +703,17 @@ bool GSDevice12::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
if (!AcquireWindow(true) || (m_window_info.type != WindowInfo::Type::Surfaceless && !CreateSwapChain()))
return false;
if (!CreateNullTexture())
{
Host::ReportErrorAsync("GS", "Failed to create dummy texture");
return false;
}
{
std::optional<std::string> shader = ReadShaderSource("shaders/dx11/tfx.fx");
if (!shader.has_value())
{
Host::ReportErrorAsync("GS", "Failed to read shaders/dx11/tfx.fxf.");
Host::ReportErrorAsync("GS", "Failed to read shaders/dx11/tfx.fx.");
return false;
}
@@ -717,12 +723,6 @@ bool GSDevice12::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
if (!m_shader_cache.Open(m_feature_level, GSConfig.UseDebugDevice))
Console.Warning("D3D12: Shader cache failed to open.");
if (!CreateNullTexture())
{
Host::ReportErrorAsync("GS", "Failed to create dummy texture");
return false;
}
if (!CreateRootSignatures())
{
Host::ReportErrorAsync("GS", "Failed to create pipeline layouts");
@@ -1486,15 +1486,15 @@ void GSDevice12::ConvertToIndexedTexture(
{
float scale;
float pad1[3];
u32 SBW, DBW, pad2;
u32 SBW, DBW, SPSM;
};
const Uniforms cb = {sScale, {}, SBW, DBW};
const Uniforms cb = {sScale, {}, SBW, DBW, SPSM};
SetUtilityRootSignature();
SetUtilityPushConstants(&cb, sizeof(cb));
const GSVector4 dRect(0, 0, dTex->GetWidth(), dTex->GetHeight());
const ShaderConvert shader = ShaderConvert::RGBA_TO_8I;
const ShaderConvert shader = ((SPSM & 0xE) == 0) ? ShaderConvert::RGBA_TO_8I : ShaderConvert::RGB5A1_TO_8I;
DoStretchRect(static_cast<GSTexture12*>(sTex), GSVector4::zero(), static_cast<GSTexture12*>(dTex), dRect,
m_convert[static_cast<int>(shader)].get(), false, true);
}
@@ -3828,6 +3828,7 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
GSTexture12* draw_rt = static_cast<GSTexture12*>(config.rt);
GSTexture12* draw_ds = static_cast<GSTexture12*>(config.ds);
GSTexture12* draw_rt_clone = nullptr;
GSTexture12* draw_ds_clone = nullptr;
// Align the render area to 128x128, hopefully avoiding render pass restarts for small render area changes (e.g. Ratchet and Clank).
const GSVector2i rtsize(config.rt ? config.rt->GetSize() : config.ds->GetSize());
@@ -3848,7 +3849,7 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
colclip_rt->TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
draw_rt = static_cast<GSTexture12*>(config.rt);
OMSetRenderTargets(draw_rt, draw_ds, config.scissor);
OMSetRenderTargets(draw_rt, draw_ds, config.colclip_update_area);
// if this target was cleared and never drawn to, perform the clear as part of the resolve here.
BeginRenderPass(GetLoadOpForTexture(draw_rt), D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE,
@@ -3883,7 +3884,7 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
// bind textures before checking the render pass, in case we need to transition them
if (config.tex)
{
PSSetShaderResource(0, config.tex, config.tex != config.rt);
PSSetShaderResource(0, config.tex, config.tex != config.rt && config.tex != config.ds);
PSSetSampler(config.sampler);
}
if (config.pal)
@@ -3907,15 +3908,16 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
}
}
if (config.require_one_barrier || (config.tex && config.tex == config.rt)) // Used as "bind rt" flag when texture barrier is unsupported.
if (config.require_one_barrier || (config.tex && config.tex == config.rt))
{
// requires a copy of the RT
// Requires a copy of the RT.
// Used as "bind rt" flag when texture barrier is unsupported for tex is fb.
draw_rt_clone = static_cast<GSTexture12*>(CreateTexture(rtsize.x, rtsize.y, 1, colclip_rt ? GSTexture::Format::ColorClip : GSTexture::Format::Color, true));
if (draw_rt_clone)
{
EndRenderPass();
GL_PUSH("Copy RT to temp texture for fbmask {%d,%d %dx%d}", config.drawarea.left, config.drawarea.top,
GL_PUSH("D3D12: Copy RT to temp texture {%d,%d %dx%d}", config.drawarea.left, config.drawarea.top,
config.drawarea.width(), config.drawarea.height());
draw_rt_clone->SetState(GSTexture::State::Invalidated);
@@ -3925,6 +3927,27 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
if (config.tex && config.tex == config.rt)
PSSetShaderResource(0, draw_rt_clone, true);
}
else
Console.Warning("D3D12: Failed to allocate temp texture for RT copy.");
}
if (config.tex && config.tex == config.ds)
{
// DX requires a copy when sampling the depth buffer.
draw_ds_clone = static_cast<GSTexture12*>(CreateDepthStencil(rtsize.x, rtsize.y, config.ds->GetFormat(), false));
if (draw_ds_clone)
{
EndRenderPass();
GL_PUSH("D3D12: Copy DS to temp texture {%d,%d %dx%d}", config.drawarea.left, config.drawarea.top,
config.drawarea.width(), config.drawarea.height());
draw_ds_clone->SetState(GSTexture::State::Invalidated);
CopyRect(config.ds, draw_ds_clone, config.drawarea, config.drawarea.left, config.drawarea.top);
PSSetShaderResource(0, draw_ds_clone, true);
}
else
Console.Warning("D3D12: Failed to allocate temp texture for DS copy.");
}
// Switch to colclip target for colclip hw rendering
@@ -3939,7 +3962,7 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
colclip_rt = static_cast<GSTexture12*>(CreateRenderTarget(rtsize.x, rtsize.y, GSTexture::Format::ColorClip, false));
if (!colclip_rt)
{
Console.WriteLn("D3D12: Failed to allocate ColorClip render target, aborting draw.");
Console.Warning("D3D12: Failed to allocate ColorClip render target, aborting draw.");
if (date_image)
Recycle(date_image);
@@ -4019,6 +4042,7 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
// rt -> colclip hw blit if enabled
if (colclip_rt && (config.colclip_mode == GSHWDrawConfig::ColClipMode::ConvertOnly || config.colclip_mode == GSHWDrawConfig::ColClipMode::ConvertAndResolve) && config.rt->GetState() == GSTexture::State::Dirty)
{
OMSetRenderTargets(draw_rt, draw_ds, GSVector4i::loadh(rtsize));
SetUtilityTexture(static_cast<GSTexture12*>(config.rt), m_point_sampler_cpu);
SetPipeline(m_colclip_setup_pipelines[pipe.ds].get());
@@ -4028,8 +4052,10 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
GL_POP();
}
// Restore original scissor, not sure if needed since the render pass has already been started. But to be safe.
OMSetRenderTargets(draw_rt, draw_ds, config.scissor);
}
// VB/IB upload, if we did DATE setup and it's not colclip hw this has already been done
SetPrimitiveTopology(s_primitive_topology_mapping[static_cast<u8>(config.topology)]);
if (!date_image || colclip_rt)
@@ -4073,6 +4099,9 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
if (draw_rt_clone)
Recycle(draw_rt_clone);
if (draw_ds_clone)
Recycle(draw_ds_clone);
if (date_image)
Recycle(date_image);
@@ -4089,7 +4118,7 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
colclip_rt->TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
draw_rt = static_cast<GSTexture12*>(config.rt);
OMSetRenderTargets(draw_rt, draw_ds, config.scissor);
OMSetRenderTargets(draw_rt, draw_ds, config.colclip_update_area);
// if this target was cleared and never drawn to, perform the clear as part of the resolve here.
BeginRenderPass(GetLoadOpForTexture(draw_rt), D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE,

View File

@@ -557,26 +557,6 @@ bool GSHwHack::GSC_TalesofSymphonia(GSRendererHW& r, int& skip)
return true;
}
bool GSHwHack::GSC_Simple2000Vol114(GSRendererHW& r, int& skip)
{
if (skip == 0)
{
if (!s_nativeres && RTME == 0 && (RFBP == 0x1500) && (RTBP0 == 0x2c97 || RTBP0 == 0x2ace || RTBP0 == 0x03d0 || RTBP0 == 0x2448) && (RFBMSK == 0x0000))
{
// Don't enable hack on native res if crc is below aggressive.
// Upscaling issues, removes glow/blur effect which fixes ghosting.
skip = 1;
}
if (RTME && (RFBP == 0x0e00) && (RTBP0 == 0x1000) && (RFBMSK == 0x0000))
{
// Depth shadows.
skip = 1;
}
}
return true;
}
bool GSHwHack::GSC_UrbanReign(GSRendererHW& r, int& skip)
{
if (skip == 0)
@@ -1419,7 +1399,6 @@ const GSHwHack::Entry<GSRendererHW::GSC_Ptr> GSHwHack::s_get_skip_count_function
CRC_F(GSC_SacredBlaze),
CRC_F(GSC_GuitarHero),
CRC_F(GSC_SakuraWarsSoLongMyLove),
CRC_F(GSC_Simple2000Vol114),
CRC_F(GSC_SFEX3),
CRC_F(GSC_DTGames),
CRC_F(GSC_TalesOfLegendia),

View File

@@ -21,7 +21,6 @@ public:
static bool GSC_SakuraWarsSoLongMyLove(GSRendererHW& r, int& skip);
static bool GSC_UltramanFightingEvolution(GSRendererHW& r, int& skip);
static bool GSC_TalesofSymphonia(GSRendererHW& r, int& skip);
static bool GSC_Simple2000Vol114(GSRendererHW& r, int& skip);
static bool GSC_UrbanReign(GSRendererHW& r, int& skip);
static bool GSC_SteambotChronicles(GSRendererHW& r, int& skip);
static bool GSC_BlueTongueGames(GSRendererHW& r, int& skip);

View File

@@ -1037,7 +1037,7 @@ float GSRendererHW::GetTextureScaleFactor()
return GetUpscaleMultiplier();
}
GSVector2i GSRendererHW::GetValidSize(const GSTextureCache::Source* tex)
GSVector2i GSRendererHW::GetValidSize(const GSTextureCache::Source* tex, const bool is_shuffle)
{
// Don't blindly expand out to the scissor size if we're not drawing to it.
// e.g. Burnout 3, God of War II, etc.
@@ -1088,10 +1088,9 @@ GSVector2i GSRendererHW::GetValidSize(const GSTextureCache::Source* tex)
// Early detection of texture shuffles. These double the input height because they're interpreting 64x32 C32 pages as 64x64 C16.
// Why? Well, we don't want to be doubling the heights of targets, but also we don't want to align C32 targets to 64 instead of 32.
// Yumeria's text breaks, and GOW goes to 512x448 instead of 512x416 if we don't.
const bool possible_texture_shuffle =
(tex && m_vt.m_primclass == GS_SPRITE_CLASS && frame_psm.bpp == 16 &&
const bool possible_texture_shuffle = tex && m_vt.m_primclass == GS_SPRITE_CLASS && frame_psm.bpp == 16 &&
GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].bpp == 16 &&
(tex->m_32_bits_fmt ||
(is_shuffle || (tex->m_32_bits_fmt ||
(m_cached_ctx.TEX0.TBP0 != m_cached_ctx.FRAME.Block() && IsOpaque() && !(m_context->TEX1.MMIN & 1) &&
m_cached_ctx.FRAME.FBMSK && g_texture_cache->Has32BitTarget(m_cached_ctx.FRAME.Block()))));
if (possible_texture_shuffle)
@@ -1128,9 +1127,9 @@ GSVector2i GSRendererHW::GetValidSize(const GSTextureCache::Source* tex)
return GSVector2i(width, height);
}
GSVector2i GSRendererHW::GetTargetSize(const GSTextureCache::Source* tex, const bool can_expand)
GSVector2i GSRendererHW::GetTargetSize(const GSTextureCache::Source* tex, const bool can_expand, const bool is_shuffle)
{
const GSVector2i valid_size = GetValidSize(tex);
const GSVector2i valid_size = GetValidSize(tex, is_shuffle);
return g_texture_cache->GetTargetSize(m_cached_ctx.FRAME.Block(), m_cached_ctx.FRAME.FBW, m_cached_ctx.FRAME.PSM, valid_size.x, valid_size.y, can_expand);
}
@@ -1600,6 +1599,13 @@ void GSRendererHW::FinishSplitClear()
m_split_clear_color = 0;
}
bool GSRendererHW::NeedsBlending()
{
const u32 temp_fbmask = (GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].bpp == 16) ? 0x00F8F8F8 : 0x00FFFFFF;
const bool FBMASK_skip = (m_cached_ctx.FRAME.FBMSK & temp_fbmask) == temp_fbmask;
return PRIM->ABE && !FBMASK_skip;
}
bool GSRendererHW::IsRTWritten()
{
const GIFRegTEST TEST = m_cached_ctx.TEST;
@@ -1638,12 +1644,12 @@ bool GSRendererHW::IsUsingCsInBlend()
{
const GIFRegALPHA ALPHA = m_context->ALPHA;
const bool blend_zero = (ALPHA.A == ALPHA.B || (ALPHA.C == 2 && ALPHA.FIX == 0) || (ALPHA.C == 0 && GetAlphaMinMax().max == 0));
return (PRIM->ABE && ((ALPHA.IsUsingCs() && !blend_zero) || m_context->ALPHA.D == 0));
return (NeedsBlending() && ((ALPHA.IsUsingCs() && !blend_zero) || m_context->ALPHA.D == 0));
}
bool GSRendererHW::IsUsingAsInBlend()
{
return (PRIM->ABE && m_context->ALPHA.IsUsingAs() && GetAlphaMinMax().max != 0);
return (NeedsBlending() && m_context->ALPHA.IsUsingAs() && GetAlphaMinMax().max != 0);
}
bool GSRendererHW::ChannelsSharedTEX0FRAME()
{
@@ -1907,7 +1913,7 @@ void GSRendererHW::SwSpriteRender()
const GSOffset spo = m_mem.GetOffset(m_context->TEX0.TBP0, m_context->TEX0.TBW, m_context->TEX0.PSM);
const GSOffset& dpo = m_context->offset.fb;
const bool alpha_blending_enabled = PRIM->ABE;
const bool alpha_blending_enabled = NeedsBlending();
const GSVertex& v = m_index.tail > 0 ? m_vertex.buff[m_index.buff[m_index.tail - 1]] : GSVertex(); // Last vertex if any.
const GSVector4i vc = GSVector4i(v.RGBAQ.R, v.RGBAQ.G, v.RGBAQ.B, v.RGBAQ.A) // 0x000000AA000000BB000000GG000000RR
@@ -2494,7 +2500,7 @@ void GSRendererHW::Draw()
return;
}
m_process_texture = PRIM->TME && !(PRIM->ABE && m_context->ALPHA.IsBlack() && !m_cached_ctx.TEX0.TCC) && !(no_rt && (!m_cached_ctx.TEST.ATE || m_cached_ctx.TEST.ATST <= ATST_ALWAYS));
m_process_texture = PRIM->TME && !(NeedsBlending() && m_context->ALPHA.IsBlack() && !m_cached_ctx.TEX0.TCC) && !(no_rt && (!m_cached_ctx.TEST.ATE || m_cached_ctx.TEST.ATST <= ATST_ALWAYS));
// We trigger the sw prim render here super early, to avoid creating superfluous render targets.
if (CanUseSwPrimRender(no_rt, no_ds, draw_sprite_tex && m_process_texture) && SwPrimRender(*this, true, true))
@@ -2860,7 +2866,8 @@ void GSRendererHW::Draw()
(m_vt.m_primclass == GS_SPRITE_CLASS || (m_vt.m_primclass == GS_TRIANGLE_CLASS && (m_index.tail % 6) == 0 && TrianglesAreQuads(true) && m_index.tail > 6)))
{
// Tail check is to make sure we have enough strips to go all the way across the page, or if it's using a region clamp could be used to draw strips.
if (GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].bpp == 16 && (m_index.tail >= (m_cached_ctx.TEX0.TBW * 2) || m_cached_ctx.CLAMP.WMS > CLAMP_CLAMP || m_cached_ctx.CLAMP.WMT > CLAMP_CLAMP))
if (GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].bpp == 16 &&
(m_index.tail >= (m_cached_ctx.TEX0.TBW * 2) || m_cached_ctx.TEX0.TBP0 == m_cached_ctx.FRAME.Block() || m_cached_ctx.CLAMP.WMS > CLAMP_CLAMP || m_cached_ctx.CLAMP.WMT > CLAMP_CLAMP))
{
const GSVertex* v = &m_vertex.buff[0];
@@ -2884,31 +2891,55 @@ void GSRendererHW::Draw()
}
// It's possible it's writing to an old 32bit target, but is actually just a 16bit copy, so let's make sure it's actually using a mask.
if (!shuffle_target && m_cached_ctx.FRAME.FBMSK)
if (!shuffle_target)
{
// FBW is going to be wrong for channel shuffling into a new target, so take it from the source.
FRAME_TEX0.U64 = 0;
FRAME_TEX0.TBP0 = m_cached_ctx.FRAME.Block();
FRAME_TEX0.TBW = m_cached_ctx.FRAME.FBW;
FRAME_TEX0.PSM = m_cached_ctx.FRAME.PSM;
bool shuffle_channel_reads = !m_cached_ctx.FRAME.FBMSK;
const u32 increment = (m_vt.m_primclass == GS_TRIANGLE_CLASS) ? 3 : 2;
const GSVertex* v = &m_vertex.buff[0];
GSTextureCache::Target* tgt = g_texture_cache->LookupTarget(FRAME_TEX0, GSVector2i(m_vt.m_max.p.x, m_vt.m_max.p.y), GetTextureScaleFactor(), GSTextureCache::RenderTarget, false,
fm, false, false, false, false, GSVector4i::zero(), true);
if (shuffle_channel_reads)
{
for (u32 i = 0; i < m_index.tail; i += increment)
{
const int first_u = (PRIM->FST ? v[i].U : static_cast<int>(v[i].ST.S / v[(increment == 2) ? i + 1 : i].RGBAQ.Q)) >> 4;
const int second_u = (PRIM->FST ? v[i + 1].U : static_cast<int>(v[i + 1].ST.S / v[i + 1].RGBAQ.Q)) >> 4;
const int vector_width = std::abs(v[i + 1].XYZ.X - v[i].XYZ.X) / 16;
const int tex_width = std::abs(second_u - first_u);
// & 7 just a quicker way of doing % 8
if ((vector_width & 7) != 0 || (tex_width & 7) != 0 || tex_width != vector_width)
{
shuffle_channel_reads = false;
break;
}
}
}
if (m_cached_ctx.FRAME.FBMSK || shuffle_channel_reads)
{
// FBW is going to be wrong for channel shuffling into a new target, so take it from the source.
FRAME_TEX0.U64 = 0;
FRAME_TEX0.TBP0 = m_cached_ctx.FRAME.Block();
FRAME_TEX0.TBW = m_cached_ctx.FRAME.FBW;
FRAME_TEX0.PSM = m_cached_ctx.FRAME.PSM;
if (tgt)
shuffle_target = tgt->m_32_bits_fmt;
GSTextureCache::Target* tgt = g_texture_cache->FindOverlappingTarget(FRAME_TEX0.TBP0, GSLocalMemory::GetEndBlockAddress(FRAME_TEX0.TBP0, FRAME_TEX0.TBW, FRAME_TEX0.PSM, m_r));
tgt = nullptr;
if (tgt)
shuffle_target = tgt->m_32_bits_fmt;
else
shuffle_target = shuffle_channel_reads;
tgt = nullptr;
}
}
}
possible_shuffle = !no_rt && (((shuffle_target /*&& GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].bpp == 16*/) /*|| (m_cached_ctx.FRAME.Block() == m_cached_ctx.TEX0.TBP0 && ((m_cached_ctx.TEX0.PSM & 0x6) || m_cached_ctx.FRAME.PSM != m_cached_ctx.TEX0.PSM))*/) || IsPossibleChannelShuffle());
const bool need_aem_color = GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].trbpp <= 24 && GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].pal == 0 && ((PRIM->ABE && m_context->ALPHA.C == 0) || IsDiscardingDstAlpha()) && m_draw_env->TEXA.AEM;
const bool need_aem_color = GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].trbpp <= 24 && GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].pal == 0 && ((NeedsBlending() && m_context->ALPHA.C == 0) || IsDiscardingDstAlpha()) && m_draw_env->TEXA.AEM;
const u32 color_mask = (m_vt.m_max.c > GSVector4i::zero()).mask();
const bool texture_function_color = m_cached_ctx.TEX0.TFX == TFX_DECAL || (color_mask & 0xFFF) || (m_cached_ctx.TEX0.TFX > TFX_DECAL && (color_mask & 0xF000));
const bool texture_function_alpha = m_cached_ctx.TEX0.TFX != TFX_MODULATE || (color_mask & 0xF000);
const bool req_color = (texture_function_color && (!PRIM->ABE || (PRIM->ABE && IsUsingCsInBlend())) && (possible_shuffle || (m_cached_ctx.FRAME.FBMSK & (fm_mask & 0x00FFFFFF)) != (fm_mask & 0x00FFFFFF))) || need_aem_color;
const bool alpha_used = (GSUtil::GetChannelMask(m_context->TEX0.PSM) == 0x8 || (m_context->TEX0.TCC && texture_function_alpha)) && ((PRIM->ABE && IsUsingAsInBlend()) || (m_cached_ctx.TEST.ATE && m_cached_ctx.TEST.ATST > ATST_ALWAYS) || (possible_shuffle || (m_cached_ctx.FRAME.FBMSK & (fm_mask & 0xFF000000)) != (fm_mask & 0xFF000000)));
const bool req_color = (texture_function_color && (!PRIM->ABE || GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].bpp < 16 || (NeedsBlending() && IsUsingCsInBlend())) && (possible_shuffle || (m_cached_ctx.FRAME.FBMSK & (fm_mask & 0x00FFFFFF)) != (fm_mask & 0x00FFFFFF))) || need_aem_color;
const bool alpha_used = (GSUtil::GetChannelMask(m_context->TEX0.PSM) == 0x8 || (m_context->TEX0.TCC && texture_function_alpha)) && ((NeedsBlending() && IsUsingAsInBlend()) || (m_cached_ctx.TEST.ATE && m_cached_ctx.TEST.ATST > ATST_ALWAYS) || (possible_shuffle || (m_cached_ctx.FRAME.FBMSK & (fm_mask & 0xFF000000)) != (fm_mask & 0xFF000000)));
const bool req_alpha = (GSUtil::GetChannelMask(m_context->TEX0.PSM) & 0x8) && alpha_used;
// TODO: Be able to send an alpha of 1.0 (blended with vertex alpha maybe?) so we can avoid sending the texture, since we don't always need it.
@@ -2942,7 +2973,13 @@ void GSRendererHW::Draw()
if (possible_shuffle && GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].bpp != 16)
possible_shuffle &= draw_uses_target;
possible_shuffle &= src && (src->m_from_target != nullptr || (m_skip && possible_shuffle));
const bool shuffle_source = possible_shuffle && src && ((src->m_from_target != nullptr && GSLocalMemory::m_psm[src->m_from_target->m_TEX0.PSM].bpp != 16) || m_skip);
if (!shuffle_source && possible_shuffle)
{
const bool is_16bit_copy = m_cached_ctx.TEX0.TBP0 != m_cached_ctx.FRAME.Block() && shuffle_target && IsOpaque() && !(context->TEX1.MMIN & 1) && !src->m_32_bits_fmt && m_cached_ctx.FRAME.FBMSK;
possible_shuffle &= is_16bit_copy || (m_cached_ctx.TEX0.TBP0 == m_cached_ctx.FRAME.Block() && shuffle_target);
}
// We don't know the alpha range of direct sources when we first tried to optimize the alpha test.
// Moving the texture lookup before the ATST optimization complicates things a lot, so instead,
// recompute it, and everything derived from it again if it changes.
@@ -2984,11 +3021,11 @@ void GSRendererHW::Draw()
// Urban Reign trolls by scissoring a draw to a target at 0x0-0x117F to 378x449 which ends up the size being rounded up to 640x480
// causing the buffer to expand to around 0x1400, which makes a later framebuffer at 0x1180 to fail to be created correctly.
// We can cheese this by checking if the Z is masked and the resultant colour is going to be black anyway.
const bool output_black = PRIM->ABE && ((m_context->ALPHA.A == 1 && m_context->ALPHA.D > 1) || (m_context->ALPHA.IsBlack() && m_context->ALPHA.D != 1)) && m_draw_env->COLCLAMP.CLAMP == 1;
const bool output_black = NeedsBlending() && ((m_context->ALPHA.A == 1 && m_context->ALPHA.D > 1) || (m_context->ALPHA.IsBlack() && m_context->ALPHA.D != 1)) && m_draw_env->COLCLAMP.CLAMP == 1;
const bool can_expand = !(m_cached_ctx.ZBUF.ZMSK && output_black);
// Estimate size based on the scissor rectangle and height cache.
GSVector2i t_size = GetTargetSize(src, can_expand);
GSVector2i t_size = GetTargetSize(src, can_expand, possible_shuffle);
const GSVector4i t_size_rect = GSVector4i::loadh(t_size);
// Ensure draw rect is clamped to framebuffer size. Necessary for updating valid area.
@@ -3015,8 +3052,8 @@ void GSRendererHW::Draw()
{
// if it's directly copying keep the scale - Ratchet and clank hits this, stops edge garbage happening.
// Keep it to small targets of 256 or lower.
if (scale_draw == -1 && src && src->m_from_target && src->m_from_target->m_downscaled && static_cast<int>(m_cached_ctx.FRAME.FBW * 64) <= (PCRTCDisplays.GetResolution().x >> 1) &&
(GSVector4i(m_vt.m_min.p).xyxy() == GSVector4i(m_vt.m_min.t).xyxy()).alltrue() && (GSVector4i(m_vt.m_max.p).xyxy() == GSVector4i(m_vt.m_max.t).xyxy()).alltrue())
if (scale_draw == -1 && src && src->m_from_target && src->m_from_target->m_downscaled && ((static_cast<int>(m_cached_ctx.FRAME.FBW * 64) <= (PCRTCDisplays.GetResolution().x >> 1) &&
(GSVector4i(m_vt.m_min.p).xyxy() == GSVector4i(m_vt.m_min.t).xyxy()).alltrue() && (GSVector4i(m_vt.m_max.p).xyxy() == GSVector4i(m_vt.m_max.t).xyxy()).alltrue()) || possible_shuffle))
{
target_scale = src->m_from_target->GetScale();
scale_draw = 1;
@@ -3077,7 +3114,7 @@ void GSRendererHW::Draw()
if (!ds && m_cached_ctx.FRAME.FBP != m_cached_ctx.ZBUF.ZBP)
{
ds = g_texture_cache->CreateTarget(ZBUF_TEX0, t_size, GetValidSize(src), target_scale, GSTextureCache::DepthStencil,
ds = g_texture_cache->CreateTarget(ZBUF_TEX0, t_size, GetValidSize(src, possible_shuffle), target_scale, GSTextureCache::DepthStencil,
true, 0, false, force_preload, preserve_depth, m_r, src);
if (!ds) [[unlikely]]
{
@@ -3228,7 +3265,7 @@ void GSRendererHW::Draw()
return;
}
rt = g_texture_cache->CreateTarget(FRAME_TEX0, t_size, GetValidSize(src), (GSConfig.UserHacks_NativeScaling != GSNativeScaling::Off && scale_draw < 0 && is_possible_mem_clear != ClearType::NormalClear) ? src->m_from_target->GetScale() : target_scale,
rt = g_texture_cache->CreateTarget(FRAME_TEX0, t_size, GetValidSize(src, possible_shuffle), (GSConfig.UserHacks_NativeScaling != GSNativeScaling::Off && scale_draw < 0 && is_possible_mem_clear != ClearType::NormalClear) ? src->m_from_target->GetScale() : target_scale,
GSTextureCache::RenderTarget, true, fm, false, force_preload, preserve_rt_color || possible_shuffle, lookup_rect, src);
if (!rt) [[unlikely]]
@@ -3242,6 +3279,49 @@ void GSRendererHW::Draw()
{
rt->UpdateValidity(GSVector4i::loadh(GSVector2i(GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].pgs.x, GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].pgs.y)), true);
}
if (src && !src->m_from_target && GSLocalMemory::m_psm[src->m_TEX0.PSM].bpp == GSLocalMemory::m_psm[m_context->FRAME.PSM].bpp &&
(GSUtil::GetChannelMask(src->m_TEX0.PSM) & GSUtil::GetChannelMask(m_context->FRAME.PSM)) != 0)
{
const u32 draw_end = GSLocalMemory::GetEndBlockAddress(m_cached_ctx.FRAME.Block(), m_cached_ctx.FRAME.FBW, m_cached_ctx.FRAME.PSM, m_r) + 1;
const u32 draw_start = GSLocalMemory::GetStartBlockAddress(m_cached_ctx.FRAME.Block(), m_cached_ctx.FRAME.FBW, m_cached_ctx.FRAME.PSM, m_r);
if (draw_start <= src->m_TEX0.TBP0 && draw_end > src->m_TEX0.TBP0)
{
g_texture_cache->ReplaceSourceTexture(src, rt->GetTexture(), rt->GetScale(), rt->GetUnscaledSize(), nullptr, true);
src->m_from_target = rt;
src->m_from_target_TEX0 = rt->m_TEX0;
src->m_target_direct = true;
src->m_shared_texture = true;
src->m_target = true;
src->m_texture = rt->m_texture;
src->m_32_bits_fmt = rt->m_32_bits_fmt;
src->m_valid_rect = rt->m_valid;
src->m_alpha_minmax.first = rt->m_alpha_min;
src->m_alpha_minmax.second = rt->m_alpha_max;
const int target_width = std::max(FRAME_TEX0.TBW, 1U);
const int page_offset = (src->m_TEX0.TBP0 - rt->m_TEX0.TBP0) >> 5;
const int vertical_page_offset = page_offset / target_width;
const int horizontal_page_offset = page_offset - (vertical_page_offset * target_width);
if (vertical_page_offset)
{
const int height = std::max(rt->m_valid.w, possible_shuffle ? (m_r.w / 2) : m_r.w);
src->m_region.SetY(vertical_page_offset * GSLocalMemory::m_psm[rt->m_TEX0.PSM].pgs.y, height);
}
if (horizontal_page_offset)
src->m_region.SetX(horizontal_page_offset * GSLocalMemory::m_psm[rt->m_TEX0.PSM].pgs.x, target_width * GSLocalMemory::m_psm[rt->m_TEX0.PSM].pgs.x);
if (rt->m_dirty.empty())
{
RGBAMask rgba_mask;
rgba_mask._u32 = GSUtil::GetChannelMask(rt->m_TEX0.PSM);
g_texture_cache->AddDirtyRectTarget(rt, m_r, FRAME_TEX0.PSM, FRAME_TEX0.TBW, rgba_mask, GSLocalMemory::m_psm[FRAME_TEX0.PSM].trbpp >= 16);
}
}
}
}
else if (rt->m_TEX0.TBP0 != m_cached_ctx.FRAME.Block())
{
@@ -3348,85 +3428,134 @@ void GSRendererHW::Draw()
t_size.x = rt->m_unscaled_size.x - horizontal_offset;
t_size.y = rt->m_unscaled_size.y - vertical_offset;
}
// Z isn't offset but RT is, so we need a temp Z to align it, hopefully nothing will ever write to the Z too, right??
if (ds && vertical_offset && (m_cached_ctx.ZBUF.Block() - ds->m_TEX0.TBP0) != (m_cached_ctx.FRAME.Block() - rt->m_TEX0.TBP0))
// Don't resize if the BPP don't match.
GSVector2i new_size = GetValidSize(src, possible_shuffle);
if (new_size.x > rt->m_unscaled_size.x || new_size.y > rt->m_unscaled_size.y)
{
const u32 new_width = std::max(new_size.x, rt->m_unscaled_size.x);
const u32 new_height = std::max(new_size.y, rt->m_unscaled_size.y);
//DevCon.Warning("HW: Resizing texture %d x %d draw %d", rt->m_unscaled_size.x, new_height, s_n);
rt->ResizeTexture(new_width, new_height);
}
else if ((IsPageCopy() || is_possible_mem_clear) && m_r.width() <= frame_psm.pgs.x && m_r.height() <= frame_psm.pgs.y)
{
const int get_next_ctx = m_env.PRIM.CTXT;
const GSDrawingContext& next_ctx = m_env.CTXT[get_next_ctx];
GSVector4i update_valid = GSVector4i::loadh(GSVector2i(horizontal_offset + GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].pgs.x, GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].pgs.y + vertical_offset));
rt->UpdateValidity(update_valid, true);
if (is_possible_mem_clear)
{
m_using_temp_z = true;
const int z_vertical_offset = ((static_cast<int>(m_cached_ctx.ZBUF.Block() - ds->m_TEX0.TBP0) / 32) / std::max(rt->m_TEX0.TBW, 1U)) * GSLocalMemory::m_psm[m_cached_ctx.ZBUF.PSM].pgs.y;
if (g_texture_cache->GetTemporaryZ() != nullptr)
if ((horizontal_offset + GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].pgs.x) >= static_cast<int>(rt->m_TEX0.TBW * 64) && next_ctx.FRAME.Block() == (m_cached_ctx.FRAME.Block() + 0x20))
{
GSTextureCache::TempZAddress z_address_info = g_texture_cache->GetTemporaryZInfo();
if (ds->m_TEX0.TBP0 != z_address_info.ZBP || z_address_info.offset != static_cast<u32>(vertical_offset - z_vertical_offset))
g_texture_cache->InvalidateTemporaryZ();
else if (!m_r.rintersect(z_address_info.rect_since + GSVector4i(0, z_address_info.offset, 0, z_address_info.offset)).rempty() && m_cached_ctx.TEST.ZTST > ZTST_ALWAYS)
{
GL_CACHE("HW: RT in RT Updating Z copy on draw %d z_offset %d", s_n, z_address_info.offset);
GSVector4i dRect = GSVector4i(z_address_info.rect_since.x * ds->m_scale, (z_address_info.offset + z_address_info.rect_since.y) * ds->m_scale, (z_address_info.rect_since.z + (1.0f / ds->m_scale)) * ds->m_scale, (z_address_info.offset + z_address_info.rect_since.w + (1.0f / ds->m_scale)) * ds->m_scale);
g_gs_device->StretchRect(ds->m_texture, GSVector4(z_address_info.rect_since.x / static_cast<float>(ds->m_unscaled_size.x), z_address_info.rect_since.y / static_cast<float>(ds->m_unscaled_size.y), (z_address_info.rect_since.z + (1.0f / ds->m_scale)) / static_cast<float>(ds->m_unscaled_size.x), (z_address_info.rect_since.w + (1.0f / ds->m_scale)) / static_cast<float>(ds->m_unscaled_size.y)), g_texture_cache->GetTemporaryZ(), GSVector4(dRect), ShaderConvert::DEPTH_COPY, false);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
z_address_info.rect_since = GSVector4i::zero();
g_texture_cache->SetTemporaryZInfo(z_address_info);
}
}
if (g_texture_cache->GetTemporaryZ() == nullptr)
{
m_temp_z_full_copy = false;
const u32 vertical_size = std::max(rt->m_unscaled_size.y, ds->m_unscaled_size.y);
const GSVector4i dRect = GSVector4i(0, vertical_offset * ds->m_scale, ds->m_unscaled_size.x * ds->m_scale, (vertical_offset + ds->m_unscaled_size.y - z_vertical_offset) * ds->m_scale);
const int new_height = std::max(static_cast<int>(vertical_size * ds->m_scale), dRect.w);
if (GSTexture* tex = g_gs_device->CreateDepthStencil(ds->m_unscaled_size.x * ds->m_scale, new_height, GSTexture::Format::DepthStencil, true))
{
GL_CACHE("HW: RT in RT Z copy on draw %d z_vert_offset %d z_offset %d", s_n, z_vertical_offset, vertical_offset);
g_gs_device->StretchRect(ds->m_texture, GSVector4(0.0f, z_vertical_offset / static_cast<float>(ds->m_unscaled_size.y), 1.0f, (ds->m_unscaled_size.y - z_vertical_offset) / static_cast<float>(ds->m_unscaled_size.y)), tex, GSVector4(dRect), ShaderConvert::DEPTH_COPY, false);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
g_texture_cache->SetTemporaryZ(tex);
g_texture_cache->SetTemporaryZInfo(ds->m_TEX0.TBP0, vertical_offset - z_vertical_offset);
t_size.y = std::max(new_height, t_size.y);
}
else
{
DevCon.Warning("HW: Temporary depth buffer creation failed.");
m_using_temp_z = false;
}
update_valid.x = 0;
update_valid.z = GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].pgs.x;
update_valid.y += GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].pgs.y;
update_valid.w += GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].pgs.y;
rt->UpdateValidity(update_valid, true);
}
}
}
// Don't resize if the BPP don't match.
if (frame_psm.bpp == GSLocalMemory::m_psm[rt->m_TEX0.PSM].bpp)
}
// Z or RT are offset from each other, so we need a temp Z to align it
if (ds && rt && (m_cached_ctx.ZBUF.Block() - ds->m_TEX0.TBP0) != (m_cached_ctx.FRAME.Block() - rt->m_TEX0.TBP0))
{
m_using_temp_z = true;
const int page_offset = (static_cast<int>(m_cached_ctx.ZBUF.Block() - ds->m_TEX0.TBP0) / 32);
const int z_vertical_offset = (page_offset / std::max(ds->m_TEX0.TBW, 1U)) * GSLocalMemory::m_psm[m_cached_ctx.ZBUF.PSM].pgs.y;
const int z_horizontal_offset = (page_offset % std::max(ds->m_TEX0.TBW, 1U)) * GSLocalMemory::m_psm[m_cached_ctx.ZBUF.PSM].pgs.x;
if (g_texture_cache->GetTemporaryZ() != nullptr)
{
if (m_r.w > rt->m_unscaled_size.y || m_r.z > rt->m_unscaled_size.x)
GSTextureCache::TempZAddress z_address_info = g_texture_cache->GetTemporaryZInfo();
const int old_z_vertical_offset = (page_offset / std::max(ds->m_TEX0.TBW, 1U)) * GSLocalMemory::m_psm[m_cached_ctx.ZBUF.PSM].pgs.y;
const int old_z_horizontal_offset = (page_offset % std::max(ds->m_TEX0.TBW, 1U)) * GSLocalMemory::m_psm[m_cached_ctx.ZBUF.PSM].pgs.x;
if (ds->m_TEX0.TBP0 != z_address_info.ZBP || z_address_info.offset != page_offset)
g_texture_cache->InvalidateTemporaryZ();
else if (!m_r.rintersect(z_address_info.rect_since).rempty() && m_cached_ctx.TEST.ZTST > ZTST_ALWAYS)
{
const u32 new_height = std::max(m_r.w, rt->m_unscaled_size.y);
const u32 new_width = std::max(m_r.z, rt->m_unscaled_size.x);
GL_CACHE("HW: RT in RT Updating Z copy on draw %d z_offset %d", s_n, z_address_info.offset);
GSVector4 sRect = GSVector4(z_address_info.rect_since.x / static_cast<float>(ds->m_unscaled_size.x), z_address_info.rect_since.y / static_cast<float>(ds->m_unscaled_size.y), (z_address_info.rect_since.z + (1.0f / ds->m_scale)) / static_cast<float>(ds->m_unscaled_size.x), (z_address_info.rect_since.w + (1.0f / ds->m_scale)) / static_cast<float>(ds->m_unscaled_size.y));
GSVector4i dRect = GSVector4i((old_z_horizontal_offset + z_address_info.rect_since.x) * ds->m_scale, (old_z_vertical_offset + z_address_info.rect_since.y) * ds->m_scale, (old_z_horizontal_offset + z_address_info.rect_since.z + (1.0f / ds->m_scale)) * ds->m_scale, (old_z_vertical_offset + z_address_info.rect_since.w + (1.0f / ds->m_scale)) * ds->m_scale);
//DevCon.Warning("HW: Resizing texture %d x %d draw %d", rt->m_unscaled_size.x, new_height, s_n);
rt->ResizeTexture(new_width, new_height);
sRect = sRect.min(GSVector4(1.0f));
dRect = dRect.min_u32(GSVector4i(ds->m_unscaled_size.x * ds->m_scale, ds->m_unscaled_size.y * ds->m_scale).xyxy());
const bool frame_masked = ((m_cached_ctx.FRAME.FBMSK & frame_psm.fmsk) == frame_psm.fmsk) || (m_cached_ctx.TEST.ATE && m_cached_ctx.TEST.ATST == ATST_NEVER && !(m_cached_ctx.TEST.AFAIL & AFAIL_FB_ONLY));
rt->UpdateValidity(m_r, !frame_masked);
rt->UpdateDrawn(m_r, !frame_masked);
g_gs_device->StretchRect(ds->m_texture, sRect, g_texture_cache->GetTemporaryZ(), GSVector4(dRect), ShaderConvert::DEPTH_COPY, false);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
z_address_info.rect_since = GSVector4i::zero();
g_texture_cache->SetTemporaryZInfo(z_address_info);
}
else if ((IsPageCopy() || is_possible_mem_clear) && m_r.width() <= frame_psm.pgs.x && m_r.height() <= frame_psm.pgs.y)
}
if (g_texture_cache->GetTemporaryZ() == nullptr)
{
m_temp_z_full_copy = false;
const int get_next_ctx = m_env.PRIM.CTXT;
const GSDrawingContext& next_ctx = m_env.CTXT[get_next_ctx];
const int rt_page_offset = (static_cast<int>(m_cached_ctx.FRAME.Block() - rt->m_TEX0.TBP0) / 32);
const int vertical_page_offset = (rt_page_offset / std::max(static_cast<int>(rt->m_TEX0.TBW), 1));
const int vertical_offset = vertical_page_offset * frame_psm.pgs.y;
const int horizontal_offset = (rt_page_offset - (vertical_page_offset * std::max(static_cast<int>(rt->m_TEX0.TBW), 1))) * frame_psm.pgs.x;
const u32 horizontal_size = std::max(rt->m_unscaled_size.x, ds->m_unscaled_size.x);
const u32 vertical_size = std::max(rt->m_unscaled_size.y, ds->m_unscaled_size.y);
GSVector4i dRect = GSVector4i(horizontal_offset * ds->m_scale, vertical_offset * ds->m_scale, ds->m_unscaled_size.x * ds->m_scale, ds->m_unscaled_size.y * ds->m_scale);
const int new_height = std::max(static_cast<int>(vertical_size * ds->m_scale), dRect.w);
const int new_width = std::max(static_cast<int>(horizontal_size * ds->m_scale), dRect.z);
const int height_diff = new_height - (ds->m_unscaled_size.y * ds->m_scale);
const int width_diff = new_width - (ds->m_unscaled_size.x * ds->m_scale);
if (GSTexture* tex = g_gs_device->CreateDepthStencil(new_width, new_height, GSTexture::Format::DepthStencil, true))
{
const int get_next_ctx = m_env.PRIM.CTXT;
const GSDrawingContext& next_ctx = m_env.CTXT[get_next_ctx];
GSVector4i update_valid = GSVector4i::loadh(GSVector2i(horizontal_offset + GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].pgs.x, GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].pgs.y + vertical_offset));
rt->UpdateValidity(update_valid, true);
if (is_possible_mem_clear)
GSVector4 sRect = GSVector4(static_cast<float>(z_horizontal_offset) / static_cast<float>(ds->m_unscaled_size.x), static_cast<float>(z_vertical_offset) / static_cast<float>(ds->m_unscaled_size.y), 1.0f - (static_cast<float>(horizontal_offset - z_horizontal_offset) / static_cast<float>(ds->m_unscaled_size.x)), 1.0f - (static_cast<float>(vertical_offset - z_vertical_offset) / static_cast<float>(ds->m_unscaled_size.y)));
const bool restricted_copy = !(((next_ctx.ZBUF.ZBP == m_context->ZBUF.ZBP && next_ctx.FRAME.FBP == m_context->FRAME.FBP)) && !(IsPossibleChannelShuffle() && !IsPageCopy()));
if (!restricted_copy)
{
if ((horizontal_offset + GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].pgs.x) >= static_cast<int>(rt->m_TEX0.TBW * 64) && next_ctx.FRAME.Block() == (m_cached_ctx.FRAME.Block() + 0x20))
if (height_diff)
{
update_valid.x = 0;
update_valid.z = GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].pgs.x;
update_valid.y += GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].pgs.y;
update_valid.w += GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].pgs.y;
rt->UpdateValidity(update_valid, true);
const int adjust = std::min(height_diff, (vertical_offset - z_vertical_offset));
sRect.w += static_cast<float>(adjust) / static_cast<float>(ds->m_unscaled_size.y);
dRect.w += adjust;
}
if (width_diff)
{
const int adjust = std::min(width_diff, (horizontal_offset - z_horizontal_offset));
sRect.z += static_cast<float>(adjust) / static_cast<float>(ds->m_unscaled_size.x);
dRect.z += adjust;
}
}
else
{
// m_r already has horizontal_offset (rt offset) applied)
dRect = GSVector4i(m_r.x * ds->m_scale, m_r.y * ds->m_scale, ((1 + m_r.z) * ds->m_scale), ((1 + m_r.w) * ds->m_scale));
sRect = GSVector4(static_cast<float>((m_r.x - horizontal_offset) + z_horizontal_offset) / static_cast<float>(ds->m_unscaled_size.x), static_cast<float>((m_r.y - vertical_offset) + z_vertical_offset) / static_cast<float>(ds->m_unscaled_size.y), (static_cast<float>((m_r.z - horizontal_offset) + z_horizontal_offset) + 1.0f) / static_cast<float>(ds->m_unscaled_size.x), (static_cast<float>((m_r.w - vertical_offset) + z_vertical_offset) + 1.0f) / static_cast<float>(ds->m_unscaled_size.y));
}
// No point in copying more width than the width of the draw, it's going to be wasted (could still be tall, though).
sRect.z = std::min(sRect.z, sRect.x + ((1.0f * ds->m_scale) + (static_cast<float>(m_cached_ctx.FRAME.FBW * 64)) / static_cast<float>(ds->m_unscaled_size.x)));
dRect.z = std::min(dRect.z, dRect.x + static_cast<int>(1 * ds->m_scale) + static_cast<int>(static_cast<float>(m_cached_ctx.FRAME.FBW * 64) * ds->m_scale));
GL_CACHE("HW: RT in RT Z copy on draw %d z_vert_offset %d", s_n, page_offset);
g_gs_device->StretchRect(ds->m_texture, sRect, tex, GSVector4(dRect), ShaderConvert::DEPTH_COPY, false);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
g_texture_cache->SetTemporaryZ(tex);
g_texture_cache->SetTemporaryZInfo(ds->m_TEX0.TBP0, page_offset);
t_size.y = std::max(static_cast<int>(new_height / ds->m_scale), t_size.y);
}
else
{
DevCon.Warning("HW: Temporary depth buffer creation failed.");
m_using_temp_z = false;
}
}
}
@@ -3484,7 +3613,7 @@ void GSRendererHW::Draw()
// This should never happen, but just to be safe..
if (!ds)
{
ds = g_texture_cache->CreateTarget(ZBUF_TEX0, t_size, GetValidSize(src), target_scale, GSTextureCache::DepthStencil,
ds = g_texture_cache->CreateTarget(ZBUF_TEX0, t_size, GetValidSize(src, possible_shuffle), target_scale, GSTextureCache::DepthStencil,
true, 0, false, force_preload, preserve_depth, m_r, src);
if (!ds) [[unlikely]]
{
@@ -3681,6 +3810,9 @@ void GSRendererHW::Draw()
const GSVector4 tmin = m_vt.m_min.t;
const GSVector4 tmax = m_vt.m_max.t;
// Backup original coverage.
const GSVector4i coverage = tmm.coverage;
for (int layer = m_lod.x + 1; layer <= m_lod.y; layer++)
{
const GIFRegTEX0 MIP_TEX0(GetTex0Layer(layer));
@@ -3702,6 +3834,9 @@ void GSRendererHW::Draw()
src->m_texture->ClearMipmapGenerationFlag();
m_vt.m_min.t = tmin;
m_vt.m_max.t = tmax;
// Restore original coverage.
tmm.coverage = coverage;
}
}
@@ -3759,10 +3894,13 @@ void GSRendererHW::Draw()
m_index.tail = 2;
}
}
const bool blending_cd = PRIM->ABE && !m_context->ALPHA.IsOpaque();
const bool blending_cd = NeedsBlending() && !m_context->ALPHA.IsOpaque();
bool valid_width_change = false;
if (rt && ((!is_possible_mem_clear || blending_cd) || rt->m_TEX0.PSM != FRAME_TEX0.PSM) && !m_in_target_draw)
{
if (rt->m_TEX0.TBW != FRAME_TEX0.TBW && !m_cached_ctx.ZBUF.ZMSK && (m_cached_ctx.FRAME.FBMSK & 0xFF000000))
valid_width_change = rt->m_TEX0.TBW != FRAME_TEX0.TBW;
if (valid_width_change && !m_cached_ctx.ZBUF.ZMSK && (m_cached_ctx.FRAME.FBMSK & 0xFF000000))
{
// Alpha could be a font, and since the width is changing it's no longer valid.
// Be careful of downsize copies or other effects, checking Z MSK should hopefully be enough.. (Okami).
@@ -3776,6 +3914,13 @@ void GSRendererHW::Draw()
FRAME_TEX0.TBP0 = rt->m_TEX0.TBP0;
rt->m_TEX0 = FRAME_TEX0;
}
if (valid_width_change)
{
GSVector4i new_valid_width = rt->m_valid;
new_valid_width.z = std::min(new_valid_width.z, static_cast<int>(rt->m_TEX0.TBW) * 64);
rt->ResizeValidity(new_valid_width);
}
}
if (ds && (!is_possible_mem_clear || ds->m_TEX0.PSM != ZBUF_TEX0.PSM || (rt && ds->m_TEX0.TBW != rt->m_TEX0.TBW)) && !m_in_target_draw)
@@ -3785,7 +3930,18 @@ void GSRendererHW::Draw()
ZBUF_TEX0.TBP0 = ds->m_TEX0.TBP0;
ds->m_TEX0 = ZBUF_TEX0;
}
if (valid_width_change)
{
GSVector4i new_valid_width = ds->m_valid;
new_valid_width.z = std::min(new_valid_width.z, static_cast<int>(ds->m_TEX0.TBW) * 64);
ds->ResizeValidity(new_valid_width);
}
}
if (rt)
g_texture_cache->CombineAlignedInsideTargets(rt, src);
if (ds)
g_texture_cache->CombineAlignedInsideTargets(ds, src);
}
else if (!m_texture_shuffle)
{
@@ -3793,7 +3949,7 @@ void GSRendererHW::Draw()
// The FBW should also be okay, since it's coming from the source.
if (rt)
{
const bool update_fbw = (FRAME_TEX0.TBW != rt->m_TEX0.TBW || rt->m_TEX0.TBW == 1) && !m_in_target_draw && (m_channel_shuffle && src->m_target) && (!PRIM->ABE || IsOpaque() || m_context->ALPHA.IsBlack());
const bool update_fbw = (FRAME_TEX0.TBW != rt->m_TEX0.TBW || rt->m_TEX0.TBW == 1) && !m_in_target_draw && (m_channel_shuffle && src->m_target) && (!NeedsBlending() || IsOpaque() || m_context->ALPHA.IsBlack());
rt->m_TEX0.TBW = update_fbw ? ((src && src->m_from_target && src->m_32_bits_fmt) ? src->m_from_target->m_TEX0.TBW : FRAME_TEX0.TBW) : std::max(rt->m_TEX0.TBW, FRAME_TEX0.TBW);
rt->m_TEX0.PSM = FRAME_TEX0.PSM;
}
@@ -3862,7 +4018,7 @@ void GSRendererHW::Draw()
}
// NFS Undercover does a draw with double width of the actual width 1280x240, which functions the same as doubling the height.
// Ignore single page/0 page stuff, that's just gonna get silly
else if (buffer_width > 64 && update_rect.z > buffer_width)
else if (m_texture_shuffle && buffer_width > 64 && update_rect.z > buffer_width)
{
update_rect.w *= static_cast<float>(update_rect.z) / static_cast<float>(buffer_width);
update_rect.z = buffer_width;
@@ -3936,8 +4092,13 @@ void GSRendererHW::Draw()
const bool rt_update = can_update_size || (m_texture_shuffle && (src && rt && src->m_from_target != rt));
// If it's updating from a texture shuffle, limit the size to the source size.
if (rt_update && !can_update_size && src->m_from_target)
update_rect = update_rect.rintersect(src->m_from_target->m_valid);
if (rt_update && !can_update_size)
{
if(src->m_from_target)
update_rect = update_rect.rintersect(src->m_from_target->m_valid);
update_rect = update_rect.rintersect(GSVector4i::loadh(GSVector2i(new_w, new_h)));
}
// if frame is masked or afailing always to never write frame, wanna make sure we don't touch it. This might happen if DATE or Alpha Test is being used to write to Z.
const bool frame_masked = ((m_cached_ctx.FRAME.FBMSK & frame_psm.fmsk) == frame_psm.fmsk) || (m_cached_ctx.TEST.ATE && m_cached_ctx.TEST.ATST == ATST_NEVER && !(m_cached_ctx.TEST.AFAIL & AFAIL_FB_ONLY));
@@ -4013,9 +4174,22 @@ void GSRendererHW::Draw()
// Dark cloud writes to 424 when the buffer is only 416 high, but masks the Z.
// Updating the valid causes the Z to overlap the framebuffer, which is obviously incorrect.
const bool z_masked = m_cached_ctx.ZBUF.ZMSK;
const bool z_update = can_update_size && !z_masked;
ds->UpdateValidity(m_r, !z_masked && (can_update_size || m_r.w <= (resolution.y * 2)));
ds->UpdateDrawn(m_r, !z_masked && (can_update_size || m_r.w <= (resolution.y * 2)));
if (rt && m_using_temp_z)
{
const GSLocalMemory::psm_t& z_psm = GSLocalMemory::m_psm[m_cached_ctx.ZBUF.PSM];
const int vertical_offset = ((static_cast<int>(m_cached_ctx.FRAME.Block() - rt->m_TEX0.TBP0) / 32) / std::max(static_cast<int>(rt->m_TEX0.TBW), 1)) * frame_psm.pgs.y;
const int z_vertical_offset = ((static_cast<int>(m_cached_ctx.ZBUF.Block() - ds->m_TEX0.TBP0) / 32) / std::max(static_cast<int>(ds->m_TEX0.TBW), 1)) * z_psm.pgs.y;
const GSVector4i ds_rect = m_r - GSVector4i(vertical_offset - z_vertical_offset);
ds->UpdateValidity(ds_rect, z_update && (can_update_size || (ds_rect.w <= (resolution.y * 2) && !m_texture_shuffle)));
ds->UpdateDrawn(ds_rect, z_update && (can_update_size || (ds_rect.w <= (resolution.y * 2) && !m_texture_shuffle)));
}
else
{
ds->UpdateValidity(m_r, z_update && (can_update_size || m_r.w <= (resolution.y * 2)));
ds->UpdateDrawn(m_r, z_update && (can_update_size || m_r.w <= (resolution.y * 2)));
}
if (!new_rect && new_height && old_end_block != ds->m_end_block)
{
@@ -4240,54 +4414,64 @@ void GSRendererHW::Draw()
}
if (zm != 0xffffffff && ds)
if (ds)
{
const bool z_masked = m_cached_ctx.ZBUF.ZMSK;
const bool was_written = zm != 0xffffffff && m_cached_ctx.DepthWrite();
//ds->m_valid = ds->m_valid.runion(r);
// Limit to 2x the vertical height of the resolution (for double buffering)
ds->UpdateValidity(real_rect, !z_masked && (can_update_size || (real_rect.w <= (resolution.y * 2) && !m_texture_shuffle)));
if (m_using_temp_z)
{
if (m_cached_ctx.DepthWrite())
{
const int get_next_ctx = m_env.PRIM.CTXT;
const GSDrawingContext& next_ctx = m_env.CTXT[get_next_ctx];
if ((m_state_flush_reason != CONTEXTCHANGE) || (next_ctx.ZBUF.ZBP == m_context->ZBUF.ZBP && next_ctx.FRAME.FBP == m_context->FRAME.FBP))
{
m_temp_z_full_copy = true;
}
else
{
const int vertical_offset = ((static_cast<int>(m_cached_ctx.FRAME.Block() - rt->m_TEX0.TBP0) / 32) / std::max(static_cast<int>(rt->m_TEX0.TBW), 1)) * frame_psm.pgs.y;
const int z_vertical_offset = ((static_cast<int>(m_cached_ctx.ZBUF.Block() - ds->m_TEX0.TBP0) / 32) / std::max(rt->m_TEX0.TBW, 1U)) * GSLocalMemory::m_psm[m_cached_ctx.ZBUF.PSM].pgs.y;
if (!m_temp_z_full_copy)
{
const GSVector4i dRect = GSVector4i(real_rect.x * ds->m_scale, (z_vertical_offset + (real_rect.y - vertical_offset)) * ds->m_scale, (real_rect.z + (1.0f / ds->m_scale)) * ds->m_scale, (z_vertical_offset + (real_rect.w + (1.0f / ds->m_scale) - vertical_offset)) * ds->m_scale);
const GSVector4 sRect = GSVector4((real_rect.x * ds->m_scale) / static_cast<float>(g_texture_cache->GetTemporaryZ()->GetWidth()), static_cast<float>(real_rect.y * ds->m_scale) / static_cast<float>(g_texture_cache->GetTemporaryZ()->GetHeight()), ((real_rect.z + (1.0f / ds->m_scale)) * ds->m_scale) / static_cast<float>(g_texture_cache->GetTemporaryZ()->GetWidth()),
static_cast<float>((real_rect.w + (1.0f / ds->m_scale)) * ds->m_scale) / static_cast<float>(g_texture_cache->GetTemporaryZ()->GetHeight()));
GL_CACHE("HW: RT in RT Z copy back draw %d z_vert_offset %d z_offset %d", s_n, z_vertical_offset, vertical_offset);
g_gs_device->StretchRect(g_texture_cache->GetTemporaryZ(), sRect, ds->m_texture, GSVector4(dRect), ShaderConvert::DEPTH_COPY, false);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
}
else
{
const GSVector4i dRect = GSVector4i(0, ds->m_valid.y * ds->m_scale, ds->m_valid.z * ds->m_scale, ds->m_valid.w * ds->m_scale);
const GSVector4 sRect = GSVector4((ds->m_valid.x * ds->m_scale) / static_cast<float>(g_texture_cache->GetTemporaryZ()->GetWidth()), static_cast<float>((ds->m_valid.y + vertical_offset) * ds->m_scale) / static_cast<float>(g_texture_cache->GetTemporaryZ()->GetHeight()), ((ds->m_valid.z + (1.0f / ds->m_scale)) * ds->m_scale) / static_cast<float>(g_texture_cache->GetTemporaryZ()->GetWidth()),
static_cast<float>(((ds->m_valid.w + vertical_offset) + (1.0f / ds->m_scale)) * ds->m_scale) / static_cast<float>(g_texture_cache->GetTemporaryZ()->GetHeight()));
GL_CACHE("HW: RT in RT Z copy back draw %d z_vert_offset %d z_offset %d", s_n, z_vertical_offset, vertical_offset);
g_gs_device->StretchRect(g_texture_cache->GetTemporaryZ(), sRect, ds->m_texture, GSVector4(dRect), ShaderConvert::DEPTH_COPY, false);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
}
const int get_next_ctx = m_env.PRIM.CTXT;
const GSDrawingContext& next_ctx = m_env.CTXT[get_next_ctx];
const int z_vertical_offset = ((static_cast<int>(m_cached_ctx.ZBUF.Block() - ds->m_TEX0.TBP0) / 32) / std::max(rt->m_TEX0.TBW, 1U)) * GSLocalMemory::m_psm[m_cached_ctx.ZBUF.PSM].pgs.y;
const int z_horizontal_offset = ((static_cast<int>(m_cached_ctx.ZBUF.Block() - ds->m_TEX0.TBP0) / 32) % std::max(rt->m_TEX0.TBW, 1U)) * GSLocalMemory::m_psm[m_cached_ctx.ZBUF.PSM].pgs.x;
const int vertical_offset = ((static_cast<int>(m_cached_ctx.FRAME.Block() - rt->m_TEX0.TBP0) / 32) / std::max(static_cast<int>(rt->m_TEX0.TBW), 1)) * frame_psm.pgs.y;
const int horizontal_offset = ((static_cast<int>(m_cached_ctx.FRAME.Block() - rt->m_TEX0.TBP0) / 32) % std::max(static_cast<int>(rt->m_TEX0.TBW), 1)) * frame_psm.pgs.x;
m_temp_z_full_copy = false;
if (was_written)
{
const GSVector4i ds_real_rect = real_rect - GSVector4i(vertical_offset - z_vertical_offset);
ds->UpdateValidity(ds_real_rect, !z_masked && (can_update_size || (ds_real_rect.w <= (resolution.y * 2) && !m_texture_shuffle)));
}
if (((m_state_flush_reason != CONTEXTCHANGE) || (next_ctx.ZBUF.ZBP == m_context->ZBUF.ZBP && next_ctx.FRAME.FBP == m_context->FRAME.FBP)) && !(IsPossibleChannelShuffle() && !IsPageCopy()))
{
m_temp_z_full_copy |= was_written;
}
else
{
if (!m_temp_z_full_copy && was_written)
{
GSVector4i dRect = GSVector4i((z_horizontal_offset + (real_rect.x - horizontal_offset)) * ds->m_scale, (z_vertical_offset + (real_rect.y - vertical_offset)) * ds->m_scale, ((z_horizontal_offset + real_rect.z + (1.0f / ds->m_scale)) - horizontal_offset) * ds->m_scale, (z_vertical_offset + (real_rect.w + (1.0f / ds->m_scale) - vertical_offset)) * ds->m_scale);
GSVector4 sRect = GSVector4((real_rect.x * ds->m_scale) / static_cast<float>(g_texture_cache->GetTemporaryZ()->GetWidth()), static_cast<float>(real_rect.y * ds->m_scale) / static_cast<float>(g_texture_cache->GetTemporaryZ()->GetHeight()), ((real_rect.z + (1.0f / ds->m_scale)) * ds->m_scale) / static_cast<float>(g_texture_cache->GetTemporaryZ()->GetWidth()),
static_cast<float>((real_rect.w + (1.0f / ds->m_scale)) * ds->m_scale) / static_cast<float>(g_texture_cache->GetTemporaryZ()->GetHeight()));
GL_CACHE("HW: RT in RT Z copy back draw %d z_vert_offset %d rt_vert_offset %d z_horz_offset %d rt_horz_offset %d", s_n, z_vertical_offset, vertical_offset, z_horizontal_offset, horizontal_offset);
g_gs_device->StretchRect(g_texture_cache->GetTemporaryZ(), sRect, ds->m_texture, GSVector4(dRect), ShaderConvert::DEPTH_COPY, false);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
}
else if (m_temp_z_full_copy)
{
GSVector4i dRect = GSVector4i((ds->m_valid.x + z_horizontal_offset) * ds->m_scale, (ds->m_valid.y + z_vertical_offset) * ds->m_scale, (ds->m_valid.z + z_horizontal_offset + (1.0f / ds->m_scale)) * ds->m_scale, (ds->m_valid.w + z_vertical_offset + (1.0f / ds->m_scale)) * ds->m_scale);
GSVector4 sRect = GSVector4(((ds->m_valid.x + horizontal_offset) * ds->m_scale) / static_cast<float>(g_texture_cache->GetTemporaryZ()->GetWidth()), static_cast<float>((ds->m_valid.y + vertical_offset) * ds->m_scale) / static_cast<float>(g_texture_cache->GetTemporaryZ()->GetHeight()), (((ds->m_valid.z + horizontal_offset) + (1.0f / ds->m_scale)) * ds->m_scale) / static_cast<float>(g_texture_cache->GetTemporaryZ()->GetWidth()),
static_cast<float>((ds->m_valid.w + vertical_offset + (1.0f / ds->m_scale)) * ds->m_scale) / static_cast<float>(g_texture_cache->GetTemporaryZ()->GetHeight()));
GL_CACHE("HW: RT in RT Z copy back draw %d z_vert_offset %d z_offset %d", s_n, z_vertical_offset, vertical_offset);
g_gs_device->StretchRect(g_texture_cache->GetTemporaryZ(), sRect, ds->m_texture, GSVector4(dRect), ShaderConvert::DEPTH_COPY, false);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
}
m_temp_z_full_copy = false;
}
}
else if (m_cached_ctx.DepthWrite() && g_texture_cache->GetTemporaryZ() != nullptr)
else if (was_written && g_texture_cache->GetTemporaryZ() != nullptr)
{
ds->UpdateValidity(real_rect, !z_masked && (can_update_size || (real_rect.w <= (resolution.y * 2) && !m_texture_shuffle)));
ds->UpdateDrawn(real_rect, !z_masked && (can_update_size || (real_rect.w <= (resolution.y * 2) && !m_texture_shuffle)));
GSTextureCache::TempZAddress z_address_info = g_texture_cache->GetTemporaryZInfo();
if (ds->m_TEX0.TBP0 == z_address_info.ZBP)
{
@@ -5028,7 +5212,7 @@ __ri bool GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool
m_r = GSVector4i(m_r.x & ~(frame_psm.pgs.x - 1), m_r.y & ~(frame_psm.pgs.y - 1), (m_r.z + (frame_psm.pgs.x - 1)) & ~(frame_psm.pgs.x - 1), (m_r.w + (frame_psm.pgs.y - 1)) & ~(frame_psm.pgs.y - 1));
// Hitman suffers from this, not sure on the exact scenario at the moment, but we need the barrier.
if (PRIM->ABE && m_context->ALPHA.IsCdInBlend())
if (NeedsBlending() && m_context->ALPHA.IsCdInBlend())
{
// Needed to enable IsFeedbackLoop.
m_conf.ps.channel_fb = 1;
@@ -5098,14 +5282,11 @@ void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, const boo
const bool AA1 = PRIM->AA1 && (m_vt.m_primclass == GS_LINE_CLASS || m_vt.m_primclass == GS_TRIANGLE_CLASS);
// PABE: Check condition early as an optimization, no blending when As < 128.
// For Cs*As + Cd*(1 - As) if As is 128 then blending can be disabled as well.
const bool PABE_skip = PRIM->ABE && m_draw_env->PABE.PABE &&
const bool PABE_skip = m_draw_env->PABE.PABE &&
((GetAlphaMinMax().max < 128) || (GetAlphaMinMax().max == 128 && ALPHA.A == 0 && ALPHA.B == 1 && ALPHA.C == 0 && ALPHA.D == 1));
// FBMASK: Color is not written, no need to do blending.
const u32 temp_fbmask = m_conf.ps.dst_fmt == GSLocalMemory::PSM_FMT_16 ? 0x00F8F8F8 : 0x00FFFFFF;
const bool FBMASK_skip = (m_cached_ctx.FRAME.FBMSK & temp_fbmask) == temp_fbmask;
// No blending or coverage anti-aliasing so early exit
if (FBMASK_skip || PABE_skip || !(PRIM->ABE || AA1))
if (PABE_skip || !(NeedsBlending() || AA1))
{
m_conf.blend = {};
m_conf.ps.no_color1 = true;
@@ -5890,10 +6071,9 @@ void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, const boo
// Switch DATE_PRIMID with DATE_BARRIER in such cases to ensure accuracy.
// No mix of COLCLIP + sw blend + DATE_PRIMID, neither sw fbmask + DATE_PRIMID.
// Note: Do the swap in the end, saves the expensive draw splitting/barriers when mixed software blending is used.
if (sw_blending && DATE_PRIMID && m_conf.require_full_barrier)
if (sw_blending && DATE_PRIMID && features.texture_barrier && m_conf.require_full_barrier)
{
GL_PERF("DATE: Swap DATE_PRIMID with DATE_BARRIER");
m_conf.require_full_barrier = true;
DATE_PRIMID = false;
DATE_BARRIER = true;
}
@@ -6555,6 +6735,7 @@ __ri void GSRendererHW::HandleTextureHazards(const GSTextureCache::Target* rt, c
const GSVector4 src_rect = GSVector4(copy_range) / GSVector4(src_unscaled_size).xyxy();
const GSVector4 dst_rect = (GSVector4(copy_range) - GSVector4(offset).xyxy()) * scale;
g_gs_device->StretchRect(src_target->m_texture, src_rect, src_copy.get(), dst_rect,
src_target->m_texture->IsDepthStencil() ? ShaderConvert::DEPTH_COPY : ShaderConvert::COPY, false);
}
@@ -6828,7 +7009,7 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
}
const int afail_type = m_cached_ctx.TEST.GetAFAIL(m_cached_ctx.FRAME.PSM);
if (m_cached_ctx.TEST.ATE && ((afail_type != AFAIL_FB_ONLY && afail_type != AFAIL_RGB_ONLY) || !PRIM->ABE || !IsUsingAsInBlend()))
if (m_cached_ctx.TEST.ATE && ((afail_type != AFAIL_FB_ONLY && afail_type != AFAIL_RGB_ONLY) || !NeedsBlending() || !IsUsingAsInBlend()))
{
const int aref = static_cast<int>(m_cached_ctx.TEST.AREF);
CorrectATEAlphaMinMax(m_cached_ctx.TEST.ATST, aref);
@@ -7307,7 +7488,7 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
if (m_conf.destination_alpha >= GSHWDrawConfig::DestinationAlphaMode::Stencil &&
m_conf.destination_alpha <= GSHWDrawConfig::DestinationAlphaMode::StencilOne && !m_conf.ds)
{
const bool is_one_barrier = (m_conf.require_full_barrier && (m_prim_overlap == PRIM_OVERLAP_NO || m_conf.ps.shuffle || m_channel_shuffle));
const bool is_one_barrier = (features.texture_barrier && m_conf.require_full_barrier && (m_prim_overlap == PRIM_OVERLAP_NO || m_conf.ps.shuffle || m_channel_shuffle));
if ((temp_ds.reset(g_gs_device->CreateDepthStencil(m_conf.rt->GetWidth(), m_conf.rt->GetHeight(), GSTexture::Format::DepthStencil, false)), temp_ds))
{
m_conf.ds = temp_ds.get();
@@ -7483,11 +7664,17 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
pxAssert(!m_conf.require_full_barrier || !m_conf.ps.colclip_hw);
// Swap full barrier for one barrier when there's no overlap, or a shuffle.
if (m_conf.require_full_barrier && (m_prim_overlap == PRIM_OVERLAP_NO || m_conf.ps.shuffle || m_channel_shuffle))
if (features.texture_barrier && m_conf.require_full_barrier && (m_prim_overlap == PRIM_OVERLAP_NO || m_conf.ps.shuffle || m_channel_shuffle))
{
m_conf.require_full_barrier = false;
m_conf.require_one_barrier = true;
}
else if (!features.texture_barrier)
{
// These shouldn't be enabled if texture barriers aren't supported, make sure they are off.
m_conf.ps.write_rg = 0;
m_conf.require_full_barrier = false;
}
// rs
const GSVector4i hacked_scissor = m_channel_shuffle ? GSVector4i::cxpr(0, 0, 1024, 1024) : m_context->scissor.in;
@@ -7759,7 +7946,7 @@ GSRendererHW::CLUTDrawTestResult GSRendererHW::PossibleCLUTDrawAggressive()
if (m_cached_ctx.TEST.ATE)
return CLUTDrawTestResult::NotCLUTDraw;
if (PRIM->ABE)
if (NeedsBlending())
return CLUTDrawTestResult::NotCLUTDraw;
if (m_context->TEX1.MXL)
@@ -7822,13 +8009,13 @@ bool GSRendererHW::CanUseSwPrimRender(bool no_rt, bool no_ds, bool draw_sprite_t
return false;
}
const bool need_aem_color = GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].trbpp <= 24 && GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].pal == 0 && ((PRIM->ABE && m_context->ALPHA.C == 0) || IsDiscardingDstAlpha()) && m_draw_env->TEXA.AEM;
const bool need_aem_color = GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].trbpp <= 24 && GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].pal == 0 && ((NeedsBlending() && m_context->ALPHA.C == 0) || IsDiscardingDstAlpha()) && m_draw_env->TEXA.AEM;
const u32 color_mask = (m_vt.m_max.c > GSVector4i::zero()).mask();
const bool texture_function_color = m_cached_ctx.TEX0.TFX == TFX_DECAL || (color_mask & 0xFFF) || (m_cached_ctx.TEX0.TFX > TFX_DECAL && (color_mask & 0xF000));
const bool texture_function_alpha = m_cached_ctx.TEX0.TFX != TFX_MODULATE || (color_mask & 0xF000);
const u32 fm_mask = GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].fmsk;
const bool req_color = (texture_function_color && (!PRIM->ABE || (PRIM->ABE && IsUsingCsInBlend())) && (m_cached_ctx.FRAME.FBMSK & (fm_mask & 0x00FFFFFF)) != (fm_mask & 0x00FFFFFF)) || need_aem_color;
const bool alpha_used = (GSUtil::GetChannelMask(m_context->TEX0.PSM) == 0x8 || (m_context->TEX0.TCC && texture_function_alpha)) && ((PRIM->ABE && IsUsingAsInBlend()) || (m_cached_ctx.TEST.ATE && m_cached_ctx.TEST.ATST > ATST_ALWAYS) || (m_cached_ctx.FRAME.FBMSK & (fm_mask & 0xFF000000)) != (fm_mask & 0xFF000000));
const bool req_color = (texture_function_color && (!PRIM->ABE || GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].bpp < 16 || (NeedsBlending() && IsUsingCsInBlend())) && (m_cached_ctx.FRAME.FBMSK & (fm_mask & 0x00FFFFFF)) != (fm_mask & 0x00FFFFFF)) || need_aem_color;
const bool alpha_used = (GSUtil::GetChannelMask(m_context->TEX0.PSM) == 0x8 || (m_context->TEX0.TCC && texture_function_alpha)) && ((NeedsBlending() && IsUsingAsInBlend()) || (m_cached_ctx.TEST.ATE && m_cached_ctx.TEST.ATST > ATST_ALWAYS) || (m_cached_ctx.FRAME.FBMSK & (fm_mask & 0xFF000000)) != (fm_mask & 0xFF000000));
const bool req_alpha = (GSUtil::GetChannelMask(m_context->TEX0.PSM) & 0x8) && alpha_used;
if ((req_color && !src_target->m_valid_rgb) || (req_alpha && (!src_target->m_valid_alpha_low || !src_target->m_valid_alpha_high)))
@@ -8952,10 +9139,7 @@ void GSRendererHW::EndHLEHardwareDraw(bool force_copy_on_hazard /* = false */)
return;
}
// DX11 can't partial copy depth textures.
const GSVector4i copy_rect = (src->IsDepthStencil() && !features.test_and_sample_depth) ?
src->GetRect() :
config.drawarea.rintersect(src->GetRect());
const GSVector4i copy_rect = config.drawarea.rintersect(src->GetRect());
g_gs_device->CopyRect(src, copy, copy_rect - copy_rect.xyxy(), copy_rect.x, copy_rect.y);
config.tex = copy;
}

View File

@@ -129,6 +129,7 @@ private:
bool ContinueSplitClear();
void FinishSplitClear();
bool NeedsBlending();
bool IsRTWritten();
bool IsDepthAlwaysPassing();
bool IsUsingCsInBlend();
@@ -222,8 +223,8 @@ public:
GSVector4i ComputeBoundingBox(const GSVector2i& rtsize, float rtscale);
void MergeSprite(GSTextureCache::Source* tex);
float GetTextureScaleFactor() override;
GSVector2i GetValidSize(const GSTextureCache::Source* tex = nullptr);
GSVector2i GetTargetSize(const GSTextureCache::Source* tex = nullptr, const bool can_expand = true);
GSVector2i GetValidSize(const GSTextureCache::Source* tex = nullptr, const bool is_shuffle = false);
GSVector2i GetTargetSize(const GSTextureCache::Source* tex = nullptr, const bool can_expand = true, const bool is_shuffle = false);
void Reset(bool hardware_reset) override;
void UpdateSettings(const Pcsx2Config::GSOptions& old_config) override;

View File

@@ -621,9 +621,12 @@ void GSTextureCache::DirtyRectByPage(u32 sbp, u32 spsm, u32 sbw, Target* t, GSVe
if (!(src_info->bpp == dst_info->bpp))
{
const int src_bpp = src_info->bpp;
const bool column_align = !block_offset && src_r.z <= src_info->cs.x && src_r.w <= src_info->cs.y && src_info->depth == dst_info->depth;
if (block_offset)
in_rect = in_rect.ralign<Align_Outside>(src_info->bs);
else if (column_align)
in_rect = in_rect.ralign<Align_Outside>(src_info->cs);
else
in_rect = in_rect.ralign<Align_Outside>(src_info->pgs);
@@ -639,7 +642,10 @@ void GSTextureCache::DirtyRectByPage(u32 sbp, u32 spsm, u32 sbw, Target* t, GSVe
// Translate back to the new(dst) format.
in_rect = GSVector4i(in_pages.x * src_info->pgs.x, in_pages.y * src_info->pgs.y, in_pages.z * src_info->pgs.x, in_pages.w * src_info->pgs.y);
in_rect += GSVector4i(in_blocks.x * src_info->bs.x, in_blocks.y * src_info->bs.y, in_blocks.z * src_info->bs.x, in_blocks.w * src_info->bs.y);
if (column_align)
in_rect += GSVector4i(in_blocks.x * src_info->cs.x, in_blocks.y * src_info->cs.y, in_blocks.z * src_info->cs.x, in_blocks.w * src_info->cs.y);
else
in_rect += GSVector4i(in_blocks.x * src_info->bs.x, in_blocks.y * src_info->bs.y, in_blocks.z * src_info->bs.x, in_blocks.w * src_info->bs.y);
if (in_rect.rempty())
return;
@@ -1107,17 +1113,15 @@ GSTextureCache::Source* GSTextureCache::LookupDepthSource(const bool is_depth, c
if (dst->m_unscaled_size != t->m_unscaled_size)
{
t->ResizeTexture(t->m_unscaled_size.x, t->m_unscaled_size.y);
t->m_valid = dst->m_valid;
}
CopyRGBFromDepthToColor(t, dst);
}
t->m_valid = t->m_valid.runion(dst->m_valid);
dst = t;
if (GSUtil::GetChannelMask(TEX0.PSM) & 0x8)
t->UnscaleRTAlpha();
// Don't need to de-RTA here as we were actually copying the RGB over, preserving the existing alpha.
inside_target = false;
break;
}
@@ -1234,7 +1238,6 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
}
Target* dst = nullptr;
bool half_right = false;
int x_offset = 0;
int y_offset = 0;
@@ -1410,21 +1413,36 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
if (rect_clean)
{
bool can_use = true;
for (auto& dirty : t->m_dirty)
{
const GSVector4i dirty_rect = dirty.GetDirtyRect(t->m_TEX0, t->m_TEX0.PSM != dirty.psm);
if (!dirty_rect.rintersect(new_rect).rempty())
{
rect_clean = false;
partial |= !new_rect.rintersect(dirty_rect).eq(new_rect) || dirty_rect.eq(new_rect);
if(!dirty_rect.rintersect(t->m_valid).eq(t->m_valid) || GSUtil::GetChannelMask(t->m_TEX0.PSM) != t->m_dirty.GetDirtyChannels())
partial |= !new_rect.rintersect(dirty_rect).eq(new_rect) || dirty_rect.eq(new_rect);
else // Nothing is valid anymore, kill it.
{
can_use = false;
}
break;
}
}
if (!can_use)
{
InvalidateSourcesFromTarget(t);
i = list.erase(i);
delete t;
continue;
}
}
const u32 channel_mask = GSUtil::GetChannelMask(psm);
const u32 channels = t->m_dirty.GetDirtyChannels() & channel_mask;
const bool dirty_overlap = !t->m_dirty.GetTotalRect(t->m_TEX0, t->m_unscaled_size).rintersect(new_rect).rempty();
// If the source is reading the rt, make sure it's big enough.
if (!possible_shuffle && t && GSUtil::HasCompatibleBits(psm, t->m_TEX0.PSM)&& real_fmt_match)
{
@@ -1441,7 +1459,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
ResizeTarget(t, resize_rect, bp, psm, bw);
}
// If not all channels are clean/dirty or only part of the rect is dirty, we need to update the target.
if (((channels & channel_mask) != channel_mask || partial))
if (dirty_overlap && ((channels & channel_mask) != channel_mask || partial))
{
t->Update();
rect_clean = true;
@@ -1576,14 +1594,14 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
// DevCon.Warning("Expected %x Got %x shuffle %d draw %d", psm, t_psm, possible_shuffle, GSState::s_n);
if (match)
{
// It is a complex to convert the code in shader. As a reference, let's do it on the CPU, it will be slow but
// 1/ it just works :)
// 2/ even with upscaling
// 3/ for both Direct3D and OpenGL
if (psm == PSMT4 || (GSConfig.UserHacks_CPUFBConversion && psm == PSMT8))
// It is a complex to convert the code in shader. As a reference, let's do it on the CPU,
// it will be slow but can work even with upscaling, also fine tune it so it's not enabled when not needed.
if (psm == PSMT4 || (GSConfig.UserHacks_CPUFBConversion && psm == PSMT8 && (!possible_shuffle || GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp != 32)) ||
(psm == PSMT8H && GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp == 16))
{
// Forces 4-bit and 8-bit frame buffer conversion to be done on the CPU instead of the GPU, but performance will be slower.
// There is no dedicated shader to handle 4-bit conversion (Stuntman has been confirmed to use 4-bit).
// There is no dedicated shader to handle 4-bit conversion (Beyond Good and Evil and Stuntman).
// Note: Stuntman no longer hits the PSMT4 code path.
// Direct3D10/11 and OpenGL support 8-bit fb conversion but don't render some corner cases properly (Harry Potter games).
// The hack can fix glitches in some games.
if (!t->m_drawn_since_read.rempty())
@@ -1599,8 +1617,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
{
const bool outside_target = !t->Overlaps(bp, bw, psm, r);
// We don't have a shader for this.
if (!possible_shuffle && TEX0.PSM == PSMT8 && ((GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp != 32) || outside_target))
if (!possible_shuffle && TEX0.PSM == PSMT8 && outside_target)
{
continue;
}
@@ -1626,26 +1643,10 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
}
}
}
else if ((t->m_TEX0.TBW >= 16) && GSUtil::HasSharedBits(bp, psm, t->m_TEX0.TBP0 + t->m_TEX0.TBW * 0x10, t->m_TEX0.PSM))
{
// Detect half of the render target (fix snow engine game)
// Target Page (8KB) have always a width of 64 pixels
// Half of the Target is TBW/2 pages * 8KB / (1 block * 256B) = 0x10
if (!t->HasValidBitsForFormat(psm, req_color, req_alpha, t->m_TEX0.TBW == TEX0.TBW) && !(possible_shuffle && GSLocalMemory::m_psm[psm].bpp == 16 && GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp == 32))
continue;
half_right = true;
dst = t;
found_t = true;
tex_merge_rt = false;
x_offset = 0;
y_offset = 0;
break;
}
// Make sure the texture actually is INSIDE the RT, it's possibly not valid if it isn't.
// Also check BP >= TBP, create source isn't equpped to expand it backwards and all data comes from the target. (GH3)
else if (GSConfig.UserHacks_TextureInsideRt >= GSTextureInRtMode::InsideTargets &&
(GSLocalMemory::m_psm[color_psm].bpp >= 16 || (/*possible_shuffle &&*/ GSLocalMemory::m_psm[color_psm].bpp == 8 && GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp == 32)) && // Channel shuffles or non indexed lookups.
(GSLocalMemory::m_psm[color_psm].bpp >= 16 || (/*possible_shuffle &&*/ GSLocalMemory::m_psm[color_psm].bpp == 8 && GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp >= 16)) && // Channel shuffles or non indexed lookups.
t->m_age <= 1 && (!found_t || t->m_last_draw > dst->m_last_draw) /*&& CanTranslate(bp, bw, psm, block_boundary_rect, t->m_TEX0.TBP0, t->m_TEX0.PSM, t->m_TEX0.TBW)*/)
{
u32 rt_tbw = std::max(1U, t->m_TEX0.TBW);
@@ -1663,9 +1664,10 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
}
// Keep note that 2 bw is basically 1 normal page, as bw is in 64 pixels, and 8bit pages are 128 pixels wide, aka 2 bw.
// Also check for 4HH/HL and 8H which use the alpha channel, if the page order is wrong this can cause problems as well (Jak X font).
else if (!possible_shuffle && GSLocalMemory::m_psm[psm].trbpp <= 8 && GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp == 32 &&
(!(block_boundary_rect.w <= GSLocalMemory::m_psm[psm].pgs.y && ((GSLocalMemory::m_psm[psm].bpp == 32) ? bw : ((bw + 1) / 2)) <= t->m_TEX0.TBW) &&
!(((GSLocalMemory::m_psm[psm].bpp == 32) ? bw : ((bw + 1) / 2)) == rt_tbw)))
else if (!possible_shuffle && GSLocalMemory::m_psm[psm].trbpp <= 8 && (GSUtil::GetChannelMask(t->m_TEX0.PSM) != 0xF ||
((GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp != 16 || GSLocalMemory::m_psm[psm].bpp < 16) && (!(block_boundary_rect.w <= GSLocalMemory::m_psm[psm].pgs.y &&
((GSLocalMemory::m_psm[psm].bpp == 32) ? bw : ((bw + 1) / 2)) <= t->m_TEX0.TBW) &&
!(((GSLocalMemory::m_psm[psm].bpp == 32) ? bw : ((bw + 1) / 2)) == rt_tbw)))))
{
DbgCon.Warning("BP %x - 8bit bad match for target bp %x bw %d src %d format %d", bp, t->m_TEX0.TBP0, t->m_TEX0.TBW, bw, t->m_TEX0.PSM);
continue;
@@ -1683,22 +1685,27 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
u32 src_psm = psm;
// If the input is C16 and it's actually a shuffle of 32bits we need to correct the size.
if ((tex_color_psm & 0xF) <= PSMCT24 && (psm & 0x7) == PSMCT16 && possible_shuffle)
if ((tex_color_psm & 0xF) <= PSMCT24 && (psm & 0x7) == PSMCT16)
{
src_psm = t->m_TEX0.PSM;
// If it's taking double width for the shuffle, half that.
if (src_bw == (rt_tbw * 2))
if (possible_shuffle)
{
src_bw = rt_tbw;
src_psm = t->m_TEX0.PSM;
// If it's taking double width for the shuffle, half that.
if (src_bw == (rt_tbw * 2))
{
src_bw = rt_tbw;
rect.x /= 2;
rect.z /= 2;
}
else
{
rect.y /= 2;
rect.w /= 2;
rect.x /= 2;
rect.z /= 2;
}
else
{
rect.y /= 2;
rect.w /= 2;
}
}
else // Formats are not compatible for normal draws, only shuffles.
continue;
}
if (bp > t->m_TEX0.TBP0)
{
@@ -1713,7 +1720,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
// If the sizing is completely wrong on the frame vs the source when reading from alpha then it's likely the target has 2 different sizes for rgb and alpha.
// This is just changing the target width for the rect translation, it has no bearing on the actual source read or the target itself.
// Hitman Blood Money is an example of this in the theatre.
const u32 rt_tbw = (possible_shuffle || bw == 1 || GSUtil::GetChannelMask(psm) != 0x8 || frame.FBW <= bw || frame.FBW == t->m_TEX0.TBW) ? t->m_TEX0.TBW : frame.FBW;
const u32 rt_tbw = (possible_shuffle || bw == 1 || GSUtil::GetChannelMask(psm) != 0x8 || frame.FBW <= bw || frame.FBW == t->m_TEX0.TBW || bw == t->m_TEX0.TBW) ? t->m_TEX0.TBW : frame.FBW;
const bool can_translate = CanTranslate(bp, bw, src_psm, new_rect, t->m_TEX0.TBP0, t->m_TEX0.PSM, rt_tbw);
if (can_translate)
@@ -1766,7 +1773,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
continue;
// Be careful of shuffles where it can shuffle the width of the target, even though it may not have all been drawn to.
if (!possible_shuffle && !t->Inside(bp, bw, psm, block_boundary_rect))
if (!possible_shuffle && frame.Block() != TEX0.TBP0 && !t->Inside(bp, bw, psm, block_boundary_rect))
continue;
x_offset = rect.x;
@@ -1980,14 +1987,15 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
src = CreateMergedSource(TEX0, TEXA, region, dst->m_scale);
}
GSVector4i rect = r;
if (!src)
{
#ifdef ENABLE_OGL_DEBUG
if (dst)
{
GL_CACHE("TC: dst %s hit (%s, OFF <%d,%d>): (0x%x, %s)",
GL_CACHE("TC: dst %s hit (OFF <%d,%d>): (0x%x, %s)",
to_string(dst->m_type),
half_right ? "half" : "full",
x_offset,
y_offset,
TEX0.TBP0,
@@ -1998,8 +2006,43 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
GL_CACHE("TC: src miss (0x%x, 0x%x, %s)", TEX0.TBP0, psm_s.pal > 0 ? TEX0.CBP : 0, psm_str(TEX0.PSM));
}
#endif
// This is for the condition where the target doesn't exist on a shuffle and it needs to load from memory.
// The Godfather clears the depth buffer with a normal clear, so our depth target gets deleted, then because it finds no target
// it assumes it really is 16bits, causing the texture to be full of garbage, and our shuffle handling becomes a mess.
// In this case it's actually C24, but let's just assume it means C32, it shouldn't matter in this case.
GIFRegTEX0 src_TEX0 = TEX0;
if (possible_shuffle && !dst && psm_s.bpp == 16)
{
if (frame.FBW == src_TEX0.TBW && frame.FBW <= 14)
{
rect.y /= 2;
rect.w /= 2;
}
else
{
rect.x /= 2;
rect.z /= 2;
}
if (TEX0.TBP0 == frame.Block())
{
GIFRegTEX0 target_TEX0;
target_TEX0.TBP0 = frame.Block();
target_TEX0.PSM = PSMCT32;
target_TEX0.TBW = frame.FBW;
src = CreateSource(TEX0, TEXA, dst, half_right, x_offset, y_offset, lod, &r, gpu_clut, region);
if (target_TEX0.TBW > 14)
target_TEX0.TBW /= 2;
dst = g_texture_cache->CreateTarget(target_TEX0, GSVector2i(rect.z, rect.w), GSVector2i(rect.z, rect.w), GSRendererHW::GetInstance()->GetUpscaleMultiplier(),
GSLocalMemory::m_psm[TEX0.PSM].depth ? DepthStencil : RenderTarget, true, 0, false, true, possible_shuffle, rect, nullptr);
}
else
{
src_TEX0.PSM = PSMCT32;
}
}
src = CreateSource(src_TEX0, TEXA, dst, x_offset, y_offset, lod, &rect, gpu_clut, region);
if (!src) [[unlikely]]
return nullptr;
}
@@ -2064,7 +2107,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
AttachPaletteToSource(src, psm_s.pal, true, true);
}
src->Update(r);
src->Update(rect);
return src;
}
@@ -2088,6 +2131,10 @@ GSVector2i GSTextureCache::ScaleRenderTargetSize(const GSVector2i& sz, float sca
void GSTextureCache::CombineAlignedInsideTargets(Target* target, GSTextureCache::Source* src)
{
// Don't combine targets if Tex in RT is off, it will just fail to find them and make a new one, causing a loop of copies.
if (GSConfig.UserHacks_TextureInsideRt < GSTextureInRtMode::InsideTargets)
return;
auto& list = m_dst[target->m_type];
for (auto i = list.begin(); i != list.end();)
@@ -2121,12 +2168,17 @@ void GSTextureCache::CombineAlignedInsideTargets(Target* target, GSTextureCache:
const u32 horizontal_offset = page_offset * t_psm.pgs.x;
const GSVector4i target_drect_unscaled = t->m_drawn_since_read + GSVector4i(horizontal_offset, vertical_offset).xyxy();
const GSVector4 source_rect = GSVector4(t->m_drawn_since_read) / (GSVector4(t->m_unscaled_size) * t->GetScale());
const GSVector4 source_rect = GSVector4(t->m_drawn_since_read) / (GSVector4(t->m_unscaled_size).xyxy() * t->GetScale());
const GSVector4 target_drect = GSVector4(target_drect_unscaled) * target->m_scale;
const bool valid_color = t->m_valid_rgb;
const bool valid_alpha = (t->m_valid_alpha_high | t->m_valid_alpha_low) && (GSUtil::GetChannelMask(t->m_TEX0.PSM) & 0x8);
target->m_valid_alpha_high |= t->m_valid_alpha_high;
target->m_valid_alpha_low |= t->m_valid_alpha_low;
GL_CACHE("Combining %x-%x in to %x-%x draw %d", t->m_TEX0.TBP0, t->m_end_block, target->m_TEX0.TBP0, target->m_end_block, GSState::s_n);
g_gs_device->StretchRect(t->m_texture, source_rect, target->m_texture, target_drect, valid_color, valid_color, valid_color, valid_alpha, (target->m_type == RenderTarget) ? ShaderConvert::COPY : ShaderConvert::DEPTH_COPY);
target->UpdateValidity(target_drect_unscaled);
@@ -2270,7 +2322,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
dst->m_32_bits_fmt |= (psm_s.bpp != 16);
break;
}
else if(!(src && src->m_from_target == t))
else if (!(src && src->m_from_target == t))
{
GL_INS("TC: Deleting RT BP 0x%x BW %d PSM %s due to change in target", t->m_TEX0.TBP0, t->m_TEX0.TBW, psm_str(t->m_TEX0.PSM));
InvalidateSourcesFromTarget(t);
@@ -2289,10 +2341,11 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
const bool width_match = (t->m_TEX0.TBW == TEX0.TBW || (TEX0.TBW == 1 && draw_rect.w <= GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs.y));
const bool ds_offset = !ds || offset != 0;
const bool is_double_buffer = TEX0.TBP0 == ((((t->m_end_block + 1) - t->m_TEX0.TBP0) / 2) + t->m_TEX0.TBP0);
const bool source_match = src && src->m_TEX0.TBP0 == bp && src->m_TEX0.TBW == TEX0.TBW && src->m_from_target && src->m_from_target == t;
// if it's a shuffle, some games tend to offset back by a page, such as Tomb Raider, for no disernable reason, but it then causes problems.
// This can also happen horizontally (Catwoman moves everything one page left with shuffles), but this is too messy to deal with right now.
const bool overlaps = t->Overlaps(bp, TEX0.TBW, TEX0.PSM, min_rect) || (is_shuffle && src && GSLocalMemory::m_psm[src->m_TEX0.PSM].bpp == 8 && t->Overlaps(bp, TEX0.TBW, TEX0.PSM, min_rect + GSVector4i(0, 0, 0, 32)));
if (no_target_or_newer && is_aligned_ok && width_match && overlaps && (is_shuffle || ds_offset || is_double_buffer))
if (source_match || (no_target_or_newer && is_aligned_ok && width_match && overlaps && (is_shuffle || ds_offset || is_double_buffer)))
{
const GSLocalMemory::psm_t& s_psm = GSLocalMemory::m_psm[TEX0.PSM];
@@ -2333,11 +2386,11 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
lookup_rect = lookup_rect & GSVector4i(~8);
const GSVector4i translated_rect = GSVector4i(0, 0, 0, 0).max_i32(TranslateAlignedRectByPage(t, TEX0.TBP0, TEX0.PSM, TEX0.TBW, lookup_rect));
const GSVector4i dirty_rect = t->m_dirty.empty() ? GSVector4i::zero() : t->m_dirty.GetTotalRect(t->m_TEX0, t->m_unscaled_size).rintersect(t->m_valid);
const GSVector4i dirty_rect = t->m_dirty.empty() ? GSVector4i::zero() : t->m_dirty.GetTotalRect(t->m_TEX0, t->m_unscaled_size);
const bool all_dirty = dirty_rect.eq(t->m_valid);
if (!is_shuffle && !t->m_dirty.empty() && (!preserve_alpha && !preserve_rgb) && (GSState::s_n - 1) != t->m_last_draw)
if (!is_shuffle && !dirty_rect.rempty() && (!preserve_alpha && !preserve_rgb) && (GSState::s_n - 3) > t->m_last_draw)
{
GL_INS("TC: Deleting RT BP 0x%x BW %d PSM %s due to dirty areas not preserved (Likely change in target)", t->m_TEX0.TBP0, t->m_TEX0.TBW, psm_str(t->m_TEX0.PSM));
InvalidateSourcesFromTarget(t);
@@ -2347,7 +2400,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
continue;
}
if (!all_dirty && ((translated_rect.w <= t->m_valid.w) || widthpage_offset == 0 || (GSState::s_n - 1) == t->m_last_draw))
if (!all_dirty && ((translated_rect.w <= t->m_valid.w) || widthpage_offset == 0 || (GSState::s_n - 3) <= t->m_last_draw))
{
if (TEX0.TBW == t->m_TEX0.TBW && !is_shuffle && widthpage_offset == 0 && ((min_rect.w + 63) / 64) > 1)
{
@@ -2399,11 +2452,24 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
// If frame is old and dirty, probably modified by the EE, so kill the wrong dimension version.
if (!t->m_dirty.empty())
{
DevCon.Warning("Wanted %x psm %x bw %x, got %x psm %x bw %x, deleting", TEX0.TBP0, TEX0.PSM, TEX0.TBW, t->m_TEX0.TBP0, t->m_TEX0.PSM, t->m_TEX0.TBW);
InvalidateSourcesFromTarget(t);
i = list.erase(i);
delete t;
continue;
const GSVector4i dirty_rect = t->m_dirty.GetTotalRect(t->m_TEX0, t->m_unscaled_size);
// It's dirty with the data we want at the right width, so just change it to that.
// Prince of Persia - Sands of Time
if (t->m_dirty.size() == 1 && t->m_dirty[0].bw == TEX0.TBW)
{
t->m_TEX0.TBW = TEX0.TBW;
t->m_valid = dirty_rect;
t->m_end_block = GSLocalMemory::GetEndBlockAddress(t->m_TEX0.TBP0, t->m_TEX0.TBW, t->m_TEX0.PSM, t->m_valid);
t->m_drawn_since_read = GSVector4i::zero();
}
else
{
DevCon.Warning("Wanted %x psm %x bw %x, got %x psm %x bw %x, deleting", TEX0.TBP0, TEX0.PSM, TEX0.TBW, t->m_TEX0.TBP0, t->m_TEX0.PSM, t->m_TEX0.TBW);
InvalidateSourcesFromTarget(t);
i = list.erase(i);
delete t;
continue;
}
}
}
dst = t;
@@ -2593,7 +2659,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
src->m_target_direct = false;
src->m_shared_texture = false;
if(!req_copy)
if (!req_copy)
dst->ResizeTexture(new_size.x, new_size.y, true, true, GSVector4i(dRect), true);
else
{
@@ -2643,7 +2709,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
if ((!is_shuffle && (GSLocalMemory::m_psm[dst->m_TEX0.PSM].bpp != GSLocalMemory::m_psm[TEX0.PSM].bpp || GSLocalMemory::m_psm[dst->m_TEX0.PSM].depth != GSLocalMemory::m_psm[TEX0.PSM].depth)) ||
(is_shuffle && GSLocalMemory::m_psm[dst->m_TEX0.PSM].bpp == 16))
{
if(GSLocalMemory::m_psm[dst->m_TEX0.PSM].depth != GSLocalMemory::m_psm[TEX0.PSM].depth || dst->m_TEX0.TBW != TEX0.TBW)
if (GSLocalMemory::m_psm[dst->m_TEX0.PSM].depth != GSLocalMemory::m_psm[TEX0.PSM].depth || dst->m_TEX0.TBW != TEX0.TBW)
dst->m_32_bits_fmt = GSLocalMemory::m_psm[TEX0.PSM].bpp != 16;
if (!is_shuffle || (is_shuffle && GSLocalMemory::m_psm[dst->m_TEX0.PSM].bpp == 16))
@@ -2663,7 +2729,9 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
// If our RGB was invalidated, we need to pull it from depth.
// Terminator 3 will reuse our dst_matched target with the RGB masked, then later use the full ARGB area, so we need to update the depth.
const bool preserve_target = preserve_rgb || preserve_alpha;
if (type == RenderTarget && (preserve_target || !dst->m_valid.rintersect(draw_rect).eq(dst->m_valid)) &&
const u32 mask = GSLocalMemory::m_psm[TEX0.PSM].fmsk;
if ((preserve_target || !dst->m_valid.rintersect(draw_rect).eq(dst->m_valid)) &&
!dst->m_valid_rgb && !FullRectDirty(dst, 0x7) &&
(GSLocalMemory::m_psm[TEX0.PSM].trbpp < 24 || fbmask != 0x00FFFFFFu))
{
@@ -2672,28 +2740,55 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
if (!is_frame)
{
GL_CACHE("TC: Attempt to repopulate RGB for %s[%x]", to_string(type), dst->m_TEX0.TBP0);
for (Target* dst_match : m_dst[DepthStencil])
for (Target* dst_match : m_dst[1 - type])
{
if (dst_match->m_TEX0.TBP0 != TEX0.TBP0 || !dst_match->m_valid_rgb)
if (dst_match->m_TEX0.TBP0 != dst->m_TEX0.TBP0 || !dst_match->m_valid_rgb)
continue;
dst->m_was_dst_matched = true;
dst->m_TEX0.TBW = dst_match->m_TEX0.TBW;
// Force the valid rect to the new size in case of shrinkage.
dst->m_valid = dst_match->m_valid;
dst->UpdateValidity(dst_match->m_valid);
if (!CopyRGBFromDepthToColor(dst, dst_match))
if (type == RenderTarget)
{
// Needed new texture and memory allocation failed.
return nullptr;
dst_match->m_valid_rgb = (fbmask & mask) == (mask & 0x00FFFFFFu);
dst->m_was_dst_matched = true;
if (!CopyRGBFromDepthToColor(dst, dst_match))
{
// Needed new texture and memory allocation failed.
return nullptr;
}
}
else
{
dst_match->m_valid_rgb &= (fbmask & mask) == (mask & 0x00FFFFFFu);
dst->Update();
if (!dst->ResizeTexture(dst_match->m_unscaled_size.x, dst_match->m_unscaled_size.y))
{
// Needed new texture and memory allocation failed.
return nullptr;
}
const ShaderConvert shader = (GSLocalMemory::m_psm[dst->m_TEX0.PSM].trbpp == 16) ? ShaderConvert::RGB5A1_TO_FLOAT16 :
(GSLocalMemory::m_psm[dst->m_TEX0.PSM].trbpp == 32) ? ShaderConvert::RGBA8_TO_FLOAT32 :
ShaderConvert::RGBA8_TO_FLOAT24;
g_gs_device->StretchRect(dst_match->m_texture, GSVector4(0, 0, 1, 1),
dst->m_texture, GSVector4(dst->GetUnscaledRect()) * GSVector4(dst->GetScale()), shader, false);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
dst_match->m_valid_rgb = !used;
dst_match->m_was_dst_matched = true;
dst->m_valid_rgb = true;
dst->m_32_bits_fmt = dst_match->m_32_bits_fmt;
}
break;
}
}
if (!dst->m_valid_rgb)
if (!dst->m_valid_rgb && ((fbmask & 0x00FFFFFF) & mask) != (mask & 0x00FFFFFF))
{
GL_CACHE("TC: Cannot find RGB target for %s[%x], clearing.", to_string(type), dst->m_TEX0.TBP0);
@@ -2775,11 +2870,11 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
for (auto i = rev_list.begin(); i != rev_list.end(); ++i)
{
Target* t = *i;
// Don't pull in targets without valid lower 24 bits, it makes no sense to convert them.
// Don't pull in targets without valid lower 24 bits unless the Z is 32bits and the alpha is valid, it makes no sense to convert them otherwise.
// FIXME: Technically the difference in size is fine, but if the target gets reinterpreted, the hw renderer doesn't rearrange the target.
// This does cause some extra uploads in some games (like Burnout), but without this, bad data gets displayed in games like Transformers.
if (bp != t->m_TEX0.TBP0 || !t->m_valid_rgb || (!is_shuffle && t->m_TEX0.TBW != TEX0.TBW &&
(possible_clear || ((~GSLocalMemory::m_psm[t->m_TEX0.PSM].fmsk | fbmask) == 0xffffffff))))
if (bp != t->m_TEX0.TBP0 || (!t->m_valid_rgb && (!(GSUtil::GetChannelMask(TEX0.PSM) & 0x8) || !(t->m_valid_alpha_low || t->m_valid_alpha_high))) ||
(!is_shuffle && t->m_TEX0.TBW != TEX0.TBW && (possible_clear || ((~GSLocalMemory::m_psm[t->m_TEX0.PSM].fmsk | fbmask) == 0xffffffff))))
{
continue;
}
@@ -2804,7 +2899,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
const GSLocalMemory::psm_t& t_psm_s = GSLocalMemory::m_psm[t->m_TEX0.PSM];
if (t_psm_s.bpp != psm_s.bpp)
{
bool remove_target = possible_clear;
bool remove_target = possible_clear || (used && !is_shuffle);
// If we have a BW change, and it's not a multiple of 2 (for a shuffle), the game's going to get a jigsaw
// puzzle of pages and can't be expecting to have legitimate data. Tokimeki Memorial 3 reuses a BW 17
@@ -3014,9 +3109,8 @@ GSTextureCache::Target* GSTextureCache::CreateTarget(GIFRegTEX0 TEX0, const GSVe
if (!is_frame)
{
// Not having this valid could make things explode, but I do enjoy watching the world burn (and this is actually more correct).
dst->m_valid_rgb =true;
const u32 mask = GSLocalMemory::m_psm[TEX0.PSM].fmsk;
dst->m_valid_rgb |= GSLocalMemory::m_psm[TEX0.PSM].depth || ((fbmask & 0x00FFFFFF) & mask) != (mask & 0x00FFFFFF) || (dst->m_dirty.GetDirtyChannels() & 0x7);
// If there is an opposite target without valid RGB, we need to match them up
auto& rev_list = m_dst[1 - type];
@@ -3025,9 +3119,6 @@ GSTextureCache::Target* GSTextureCache::CreateTarget(GIFRegTEX0 TEX0, const GSVe
Target* const rev_t = *j;
if (rev_t->m_TEX0.TBP0 == dst->m_TEX0.TBP0 && GSLocalMemory::m_psm[rev_t->m_TEX0.PSM].bpp == GSLocalMemory::m_psm[dst->m_TEX0.PSM].bpp)
{
if (GSLocalMemory::m_psm[rev_t->m_TEX0.PSM].trbpp == 24 && ((fbmask & 0x00FFFFFF) & mask) == (mask & 0x00FFFFFF))
dst->m_valid_rgb = false;
if (!rev_t->m_valid_rgb && dst->m_valid_rgb)
rev_t->m_was_dst_matched = true;
@@ -3041,8 +3132,8 @@ GSTextureCache::Target* GSTextureCache::CreateTarget(GIFRegTEX0 TEX0, const GSVe
// If the alpha is masked and preloaded, we need to say it's valid else textures might fail to use the whole texture if RGB is valid.
if (((fbmask & 0xFF000000) & mask) != (mask & 0xFF000000) && bpp != 24)
{
dst->m_valid_alpha_high = (~(fbmask & mask) & 0xf0000000) & mask;
dst->m_valid_alpha_low = (~(fbmask & mask) & 0x0f000000) & mask;
dst->m_valid_alpha_high |= ((~(fbmask & mask) & 0xf0000000) & mask) != 0;
dst->m_valid_alpha_low |= ((~(fbmask & mask) & 0x0f000000) & mask) != 0;
if (bpp == 16)
dst->m_valid_alpha_low = dst->m_valid_alpha_high;
@@ -3065,8 +3156,8 @@ GSTextureCache::Target* GSTextureCache::CreateTarget(GIFRegTEX0 TEX0, const GSVe
// If the format, and location doesn't overlap
if (TEX0.TBP0 == iter->blit.DBP && GSUtil::HasCompatibleBits(iter->blit.DPSM, TEX0.PSM) && (iter->blit.DBW == dst->m_TEX0.TBW || (transfer_end >= tex_end && (iter->blit.DBW * 64) == iter->rect.z)))
{
dst->m_valid_alpha_high = iter->blit.DPSM != PSMT4HL;
dst->m_valid_alpha_low = iter->blit.DPSM != PSMT4HH;
dst->m_valid_alpha_high |= iter->blit.DPSM != PSMT4HL;
dst->m_valid_alpha_low |= iter->blit.DPSM != PSMT4HH;
break;
}
@@ -3248,6 +3339,12 @@ bool GSTextureCache::PreloadTarget(GIFRegTEX0 TEX0, const GSVector2i& size, cons
}
}
const GSVector4i dst_valid = dst->m_valid.rempty() ? GSVector4i::loadh(valid_size) : dst->m_valid;
u32 dst_end_block = GSLocalMemory::GetEndBlockAddress(dst->m_TEX0.TBP0, dst->m_TEX0.TBW, dst->m_TEX0.PSM, dst_valid);
if (dst_end_block < dst->m_TEX0.TBP0)
dst_end_block += MAX_BLOCKS;
// Can't do channel writes to depth targets, and DirectX can't partial copy depth targets.
if (psm_s.depth == 0)
{
@@ -3259,30 +3356,26 @@ bool GSTextureCache::PreloadTarget(GIFRegTEX0 TEX0, const GSVector2i& size, cons
auto j = i;
Target* t = *j;
if (dst != t && t->m_TEX0.PSM == dst->m_TEX0.PSM && t->Overlaps(dst->m_TEX0.TBP0, dst->m_TEX0.TBW, dst->m_TEX0.PSM, dst->m_valid) &&
if (dst != t && t->m_TEX0.PSM == dst->m_TEX0.PSM && t->Overlaps(dst->m_TEX0.TBP0, dst->m_TEX0.TBW, dst->m_TEX0.PSM, dst_valid) &&
static_cast<int>(((t->m_TEX0.TBP0 - dst->m_TEX0.TBP0) / 32) % std::max(dst->m_TEX0.TBW, 1U)) <= std::max(0, static_cast<int>(dst->m_TEX0.TBW - t->m_TEX0.TBW)))
{
const u32 buffer_width = std::max(1U, dst->m_TEX0.TBW);
if (buffer_width != std::max(1U, t->m_TEX0.TBW))
{
i++;
// Check if this got messed with at some point, if it did just nuke it.
if (t->m_valid.width() == dst->m_valid.width())
if (!preserve_target && t->m_age > 0)
{
// Not correct, but it's better than a null reference.
// Probably best we don't poke the beast if it's being used as the current source.
if (src && src->m_target_direct && src->m_from_target == t)
{
DevCon.Warning("Replacing source target, texture may be invalid");
src->m_texture = dst->m_texture;
src->m_from_target = dst;
}
continue;
InvalidateSourcesFromTarget(t);
i = list.erase(j);
delete t;
}
else
i++;
continue;
}
// If the two targets are misaligned, it's likely a relocation, so we can just kill the old target.
@@ -3306,10 +3399,10 @@ bool GSTextureCache::PreloadTarget(GIFRegTEX0 TEX0, const GSVector2i& size, cons
return hw_clear.value_or(false);
}
// The new texture is behind it but engulfs the whole thing, shrink the new target so it grows in the HW Draw resize.
else if (dst->m_TEX0.TBP0 < t->m_TEX0.TBP0 && (dst->UnwrappedEndBlock() + 1) > t->m_TEX0.TBP0)
else if (dst->m_TEX0.TBP0 < t->m_TEX0.TBP0 && dst_end_block > t->m_TEX0.TBP0)
{
const int rt_pages = ((t->UnwrappedEndBlock() + 1) - t->m_TEX0.TBP0) >> 5;
const int overlapping_pages = std::min(rt_pages, static_cast<int>((dst->UnwrappedEndBlock() + 1) - t->m_TEX0.TBP0) >> 5);
const int overlapping_pages = std::min(rt_pages, static_cast<int>(dst_end_block - t->m_TEX0.TBP0) >> 5);
const int overlapping_pages_height = ((overlapping_pages + (buffer_width - 1)) / buffer_width) * GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs.y;
if (overlapping_pages_height == 0 || (overlapping_pages % buffer_width))
@@ -3347,6 +3440,10 @@ bool GSTextureCache::PreloadTarget(GIFRegTEX0 TEX0, const GSVector2i& size, cons
t->Update();
dst->Update();
dst->m_valid_rgb |= t->m_valid_rgb;
dst->m_valid_alpha_low |= t->m_valid_alpha_low;
dst->m_valid_alpha_high |= t->m_valid_alpha_high;
// Clamp it if it gets too small, shouldn't happen but stranger things have happened.
if (copy_width < 0)
{
@@ -3400,14 +3497,40 @@ bool GSTextureCache::PreloadTarget(GIFRegTEX0 TEX0, const GSVector2i& size, cons
{
auto j = i;
Target* t = *j;
if (t != dst && t->Overlaps(dst->m_TEX0.TBP0, dst->m_TEX0.TBW, dst->m_TEX0.PSM, dst->m_valid) && GSUtil::HasSharedBits(dst->m_TEX0.PSM, t->m_TEX0.PSM))
if (t != dst && t->Overlaps(dst->m_TEX0.TBP0, dst->m_TEX0.TBW, dst->m_TEX0.PSM, dst_valid) && GSUtil::HasSharedBits(dst->m_TEX0.PSM, t->m_TEX0.PSM))
{
if (dst->m_TEX0.TBP0 > t->m_TEX0.TBP0 && (((dst->m_TEX0.TBP0 - t->m_TEX0.TBP0) >> 5) % std::max(t->m_TEX0.TBW, 1U)) == 0)
if (dst->m_TEX0.TBP0 > t->m_TEX0.TBP0 && dst->m_TEX0.TBW == t->m_TEX0.TBW &&
((((dst->m_TEX0.TBP0 - t->m_TEX0.TBP0) >> 5) % std::max(t->m_TEX0.TBW, 1U)) + (dst_valid.z / 64)) <= dst->m_TEX0.TBW)
{
int height_adjust = (((dst->m_TEX0.TBP0 - t->m_TEX0.TBP0) >> 5) / std::max(t->m_TEX0.TBW, 1U)) * GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs.y;
// Probably a render target which was previously a Z.
if (GSConfig.UserHacks_TextureInsideRt >= GSTextureInRtMode::InsideTargets && t->Inside(dst->m_TEX0.TBP0, dst->m_TEX0.TBW, dst->m_TEX0.PSM, dst->m_valid) &&
GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp == GSLocalMemory::m_psm[dst->m_TEX0.PSM].bpp)
{
dst->m_TEX0.TBP0 = t->m_TEX0.TBP0;
dst->m_valid = t->m_valid;
dst->m_drawn_since_read = t->m_drawn_since_read;
dst->m_end_block = t->m_end_block;
dst->m_valid_rgb = true;
t->m_valid_rgb = false;
t->m_was_dst_matched = true;
t->m_valid.w = std::min(height_adjust, t->m_valid.w);
t->ResizeValidity(t->m_valid);
dst->ResizeTexture(t->m_unscaled_size.x, t->m_unscaled_size.y);
const ShaderConvert shader = (GSLocalMemory::m_psm[dst->m_TEX0.PSM].trbpp == 16) ? ShaderConvert::RGB5A1_TO_FLOAT16 :
(GSLocalMemory::m_psm[dst->m_TEX0.PSM].trbpp == 32) ? ShaderConvert::RGBA8_TO_FLOAT32 : ShaderConvert::RGBA8_TO_FLOAT24;
g_gs_device->StretchRect(t->m_texture, GSVector4(0,0,1,1),
dst->m_texture, GSVector4(t->GetUnscaledRect()) * GSVector4(dst->GetScale()), shader, false);
break;
}
else
{
const int height_adjust = (((dst->m_TEX0.TBP0 - t->m_TEX0.TBP0) >> 5) / std::max(t->m_TEX0.TBW, 1U)) * GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs.y;
t->m_valid.w = std::min(height_adjust, t->m_valid.w);
t->ResizeValidity(t->m_valid);
}
}
else if (dst->m_TEX0.TBP0 < t->m_TEX0.TBP0 && (((t->m_TEX0.TBP0 - dst->m_TEX0.TBP0) >> 5) % std::max(t->m_TEX0.TBW, 1U)) == 0)
{
@@ -3420,15 +3543,15 @@ bool GSTextureCache::PreloadTarget(GIFRegTEX0 TEX0, const GSVector2i& size, cons
continue;
}
int height_adjust = ((((dst->m_end_block + 1) - t->m_TEX0.TBP0) >> 5) / std::max(t->m_TEX0.TBW, 1U)) * GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs.y;
const int height_adjust = (((dst_end_block - t->m_TEX0.TBP0) >> 5) / std::max(t->m_TEX0.TBW, 1U)) * GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs.y;
if (height_adjust < t->m_unscaled_size.y)
{
t->m_TEX0.TBP0 = dst->m_end_block + 1;
t->m_TEX0.TBP0 = dst_end_block;
t->m_valid.w -= height_adjust;
t->ResizeValidity(t->m_valid);
GSTexture* tex = (type == RenderTarget) ?
GSTexture* tex = (t->m_type == RenderTarget) ?
g_gs_device->CreateRenderTarget(t->m_texture->GetWidth(),
t->m_texture->GetHeight(), GSTexture::Format::Color, true) :
g_gs_device->CreateDepthStencil(t->m_texture->GetWidth(),
@@ -3454,7 +3577,7 @@ bool GSTextureCache::PreloadTarget(GIFRegTEX0 TEX0, const GSVector2i& size, cons
{
if (src && src->m_target && src->m_from_target == t)
{
src->m_from_target = t;
src->m_from_target = nullptr;
src->m_texture = t->m_texture;
src->m_target_direct = false;
src->m_shared_texture = false;
@@ -3918,24 +4041,47 @@ void GSTextureCache::InvalidateContainedTargets(u32 start_bp, u32 end_bp, u32 wr
continue;
}
const u32 offset = (std::abs(static_cast<int>(start_bp - t->m_TEX0.TBP0)) >> 5) % std::max(1U, t->m_TEX0.TBW);
// If not fully contained but they are aligned and or clean, just dirty the area.
if (start_bp != t->m_TEX0.TBP0 && (t->m_TEX0.TBP0 < start_bp || t->UnwrappedEndBlock() > end_bp) && (offset == 0 || t->m_dirty.size() == 0))
if (type != DepthStencil && start_bp != t->m_TEX0.TBP0 && (t->m_TEX0.TBP0 < start_bp || t->UnwrappedEndBlock() > end_bp))
{
if (write_bw == t->m_TEX0.TBW && GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp == GSLocalMemory::m_psm[write_psm].bpp)
{
const u32 page_offset = ((end_bp - start_bp) >> 5);
const u32 end_width = write_bw * 64;
const u32 end_height = ((page_offset / std::max(write_bw, 1U)) * GSLocalMemory::m_psm[write_psm].pgs.y) + GSLocalMemory::m_psm[write_psm].pgs.y;
const GSVector4i r = GSVector4i(0, 0, end_width, end_height);
const GSVector4i invalidate_r = TranslateAlignedRectByPage(t, start_bp, write_psm, write_bw, r, false).rintersect(t->m_valid); // it is invalidation but we need a real rect.
RGBAMask mask;
mask._u32 = GSUtil::GetChannelMask(write_psm);
AddDirtyRectTarget(t, invalidate_r, t->m_TEX0.PSM, t->m_TEX0.TBW, mask, false);
}
const u32 offset = (std::abs(static_cast<int>(start_bp - t->m_TEX0.TBP0)) >> 5) % std::max(1U, t->m_TEX0.TBW);
const GSVector4i dirty_rect = t->m_dirty.GetTotalRect(t->m_TEX0, t->m_unscaled_size).rintersect(t->m_valid);
const u32 end_page_offset = ((end_bp - start_bp) >> 5);
const u32 end_width = write_bw * 64;
const u32 end_height = ((end_page_offset / std::max(write_bw, 1U)) * GSLocalMemory::m_psm[write_psm].pgs.y) + GSLocalMemory::m_psm[write_psm].pgs.y;
const GSVector4i r = GSVector4i(0, 0, end_width, end_height);
const GSVector4i invalidate_r = TranslateAlignedRectByPage(t, start_bp, write_psm, write_bw, r, false).rintersect(t->m_valid); // it is invalidation but we need a real rect.
++i;
continue;
if (offset == 0 || dirty_rect.rempty() || !dirty_rect.rintersect(invalidate_r).rempty())
{
if (write_bw == t->m_TEX0.TBW && GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp == GSLocalMemory::m_psm[write_psm].bpp)
{
RGBAMask mask;
mask._u32 = GSUtil::GetChannelMask(write_psm);
AddDirtyRectTarget(t, invalidate_r, t->m_TEX0.PSM, t->m_TEX0.TBW, mask, false);
}
++i;
continue;
}
}
// This is an annoying edge case where developers don't know how to use SCISSOR correctly, so it's one pixel over size, making the end block too late.
// In this case we *don't* want to nuke the depth, but just adjust the size so it's not 1 pixel over.
// Prince of Persia - Sands of Time suffers from this.
if (type == DepthStencil && t->m_TEX0.TBP0 < start_bp && t->m_end_block > start_bp)
{
const GSVector4i masked_valid = GSVector4i(t->m_valid.x, t->m_valid.y, t->m_valid.z & ~1, t->m_valid.w & ~1);
const u32 reduced_endblock = GSLocalMemory::GetEndBlockAddress(t->m_TEX0.TBP0, t->m_TEX0.TBW, t->m_TEX0.PSM, masked_valid);
if (reduced_endblock <= start_bp)
{
t->ResizeValidity(masked_valid);
t->ResizeDrawn(masked_valid);
++i;
continue;
}
}
InvalidateSourcesFromTarget(t);
@@ -4660,8 +4806,17 @@ bool GSTextureCache::Move(u32 SBP, u32 SBW, u32 SPSM, int sx, int sy, u32 DBP, u
if (!dst)
dst = CreateTarget(new_TEX0, target_size, target_size, src->m_scale, src->m_type);
else // Expand if necessary (Silent hill 4 takes an old target which is smaller).
{
dst->ResizeTexture(std::max(dst->m_unscaled_size.x, target_size.x), std::max(dst->m_unscaled_size.y, target_size.y));
// If it was matched to an old target, make sure to clear the other type and update its information.
if (dst->m_was_dst_matched)
{
g_texture_cache->InvalidateVideoMemType(GSTextureCache::DepthStencil - dst->m_type, dst->m_TEX0.TBP0);
dst->m_TEX0 = new_TEX0;
}
}
if (!dst)
return false;
@@ -5033,7 +5188,7 @@ GSTextureCache::Target* GSTextureCache::GetExactTarget(u32 BP, u32 BW, int type,
{
Target* t = *it;
const u32 tgt_bw = std::max(t->m_TEX0.TBW, 1U);
if ((t->m_TEX0.TBP0 == BP || (GSConfig.UserHacks_TextureInsideRt >= GSTextureInRtMode::InsideTargets && t->m_TEX0.TBP0 < BP && ((BP >> 5) % tgt_bw) == 0)) && tgt_bw == BW && t->UnwrappedEndBlock() >= end_bp)
if ((t->m_TEX0.TBP0 == BP || (GSConfig.UserHacks_TextureInsideRt >= GSTextureInRtMode::InsideTargets && t->m_TEX0.TBP0 < BP && (((BP - t->m_TEX0.TBP0) >> 5) % tgt_bw) == 0)) && tgt_bw == BW && t->UnwrappedEndBlock() >= end_bp)
{
rts.MoveFront(it.Index());
return t;
@@ -5340,7 +5495,7 @@ void GSTextureCache::IncAge()
}
//Fixme: Several issues in here. Not handling depth stencil, pitch conversion doesnt work.
GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, Target* dst, bool half_right, int x_offset, int y_offset, const GSVector2i* lod, const GSVector4i* src_range, GSTexture* gpu_clut, SourceRegion region)
GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, Target* dst, int x_offset, int y_offset, const GSVector2i* lod, const GSVector4i* src_range, GSTexture* gpu_clut, SourceRegion region)
{
const GSLocalMemory::psm_t& psm = GSLocalMemory::m_psm[TEX0.PSM];
Source* src = new Source(TEX0, TEXA);
@@ -5378,7 +5533,10 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
const int w = static_cast<int>(std::ceil(scale * tw));
const int h = static_cast<int>(std::ceil(scale * th));
dst->Update();
const GSVector4i read_rect = GSVector4i(x_offset, y_offset, x_offset + tw, y_offset + th);
// Do this first as we could be adding in alpha from an upgraded 24bit target. if the rect intersects a dirty area.
if (!dst->m_dirty.empty() && !read_rect.rintersect(dst->m_dirty.GetTotalRect(dst->m_TEX0, dst->m_unscaled_size)).rempty())
dst->Update();
// If we have a source larger than the target (from tex-in-rt), texelFetch() for target region will return black.
if constexpr (force_target_copy)
@@ -5437,7 +5595,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
src->m_unscaled_size = dst->m_unscaled_size;
src->m_shared_texture = true;
if(channel_shuffle)
if (channel_shuffle)
m_temporary_source = src;
}
@@ -5507,7 +5665,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
if (is_8bits)
{
GL_INS("TC: Reading RT as a packed-indexed 8 bits format");
shader = ShaderConvert::RGBA_TO_8I;
shader = GSLocalMemory::m_psm[dst->m_TEX0.PSM].bpp == 16 ? ShaderConvert::RGB5A1_TO_8I : ShaderConvert::RGBA_TO_8I;
}
#ifdef ENABLE_OGL_DEBUG
@@ -5575,11 +5733,13 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
}
else
{
const GSLocalMemory::psm_t& s_psm = GSLocalMemory::m_psm[TEX0.PSM];
const GSLocalMemory::psm_t& t_psm = GSLocalMemory::m_psm[dst->m_TEX0.PSM];
u32 dst_pages = (dst->m_unscaled_size.x / t_psm.pgs.x) * (dst->m_unscaled_size.y / t_psm.pgs.y);
src->m_unscaled_size.x = std::max(static_cast<int>(TEX0.TBW) * (s_psm.pgs.x / 2), (s_psm.pgs.x / 2));
src->m_unscaled_size.y = std::max(static_cast<int>(dst_pages / std::max((TEX0.TBW / 2U), 1U)) * s_psm.pgs.y, s_psm.pgs.y);
// We're inside the target, so conversion needs to happen on the entire target so we can offset properly.
src->m_unscaled_size.x = dst->m_unscaled_size.x * 2;
if (GSLocalMemory::m_psm[dst->m_TEX0.PSM].bpp == 32)
src->m_unscaled_size.y = dst->m_unscaled_size.y * 2;
else
src->m_unscaled_size.y = dst->m_unscaled_size.y;
new_size.x = src->m_unscaled_size.x;
new_size.y = src->m_unscaled_size.y;
}
@@ -5657,34 +5817,11 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
const bool use_texture = (shader == ShaderConvert::COPY && !source_rect_empty);
GSVector4i region_rect = GSVector4i(0, 0, tw, th);
if (half_right)
{
// You typically hit this code in snow engine game. Dstsize is the size of of Dx/GL RT
// which is set to some arbitrary number. h/w are based on the input texture
// so the only reliable way to find the real size of the target is to use the TBW value.
const int half_width = static_cast<int>(dst->m_TEX0.TBW * (64 / 2));
if (half_width < dst->m_unscaled_size.x)
{
const int copy_width = std::min(half_width, dst->m_unscaled_size.x - half_width);
region_rect = GSVector4i(half_width, 0, half_width + copy_width, th);
GL_CACHE("TC: Half right fix: %d,%d => %d,%d", region_rect.x, region_rect.y, region_rect.z, region_rect.w);
sRect = sRect.blend32<5>(GSVector4i(GSVector4(region_rect.rintersect(dst->GetUnscaledRect())) * GSVector4(dst->m_scale)));
new_size.x = sRect.width();
src->m_unscaled_size.x = copy_width;
}
else
{
DevCon.Error("TC: Invalid half-right copy with width %d from %dx%d texture", half_width * 2, dst->m_unscaled_size.x, dst->m_unscaled_size.y);
}
}
// Assuming everything matches up, instead of copying the target, we can just sample it directly.
// It's the same as doing the copy first, except we save GPU time.
// TODO: We still need to copy if the TBW is mismatched. Except when TBW <= 1 (Jak 2).
const GSVector2i dst_texture_size = dst->m_texture->GetSize();
if ((!half_right || region_rect.z >= dst->m_unscaled_size.x) && // not a smaller subsample
use_texture && // not reinterpreting the RT
if (use_texture && // not reinterpreting the RT
!force_target_copy)
{
// sample the target directly
@@ -5766,28 +5903,20 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
sTex = dst->m_texture;
}
const u32 destination_tbw = (dst->m_TEX0.TBP0 == TEX0.TBP0) ? (std::max<u32>(TEX0.TBW, 1u) * 64) : std::max<u32>(dst->m_TEX0.TBW, 1u) * 128;
g_gs_device->ConvertToIndexedTexture(sTex, dst->m_scale, x_offset, y_offset,
std::max<u32>(dst->m_TEX0.TBW, 1u) * 64, dst->m_TEX0.PSM, dTex,
std::max<u32>(TEX0.TBW, 1u) * 64, TEX0.PSM);
destination_tbw, TEX0.PSM);
// Adjust the region for the newly translated rect.
u32 const dst_y_height = GSLocalMemory::m_psm[dst->m_TEX0.PSM].pgs.y;
u32 const src_y_height = GSLocalMemory::m_psm[TEX0.PSM].pgs.y;
u32 const dst_page_offset = (y_offset / dst_y_height) * std::max(dst->m_TEX0.TBW, 1U);
y_offset = (dst_page_offset / (std::max(TEX0.TBW / 2U, 1U))) * src_y_height;
// Adjust to match a PSMT8 texture (coordinates are double C32, we shouldn't be converting from anything else).
x_offset *= 2;
if (GSLocalMemory::m_psm[dst->m_TEX0.PSM].bpp == 32)
y_offset *= 2;
u32 const src_page_width = GSLocalMemory::m_psm[TEX0.PSM].pgs.x;
x_offset = (x_offset / GSLocalMemory::m_psm[dst->m_TEX0.PSM].pgs.x) * GSLocalMemory::m_psm[TEX0.PSM].pgs.x;
if (x_offset >= static_cast<int>(std::max(TEX0.TBW, 1U) * src_page_width))
{
const u32 adjust = x_offset / src_page_width;
y_offset += adjust * GSLocalMemory::m_psm[TEX0.PSM].pgs.y;
x_offset -= src_page_width * adjust;
}
src->m_region.SetX(x_offset, x_offset + tw);
src->m_region.SetY(y_offset, y_offset + th);
if(!GSConfig.UserHacks_NativePaletteDraw)
if (!GSConfig.UserHacks_NativePaletteDraw)
m_temporary_source = src;
}
else
@@ -6569,6 +6698,8 @@ void GSTextureCache::Read(Target* t, const GSVector4i& r)
{
case PSMCT32:
case PSMCT24:
case PSMZ32:
case PSMZ24:
{
// If we're downloading a depth buffer that's been reinterpreted as a color
// format, convert it to integer. The format/swizzle is likely wrong, but it's
@@ -6594,27 +6725,11 @@ void GSTextureCache::Read(Target* t, const GSVector4i& r)
case PSMCT16:
case PSMCT16S:
{
fmt = GSTexture::Format::UInt16;
ps_shader = is_depth ? ShaderConvert::FLOAT32_TO_16_BITS : ShaderConvert::RGBA8_TO_16_BITS;
dltex = &m_uint16_download_texture;
}
break;
case PSMZ32:
case PSMZ24:
{
fmt = GSTexture::Format::UInt32;
ps_shader = ShaderConvert::FLOAT32_TO_32_BITS;
dltex = &m_uint32_download_texture;
}
break;
case PSMZ16:
case PSMZ16S:
{
fmt = GSTexture::Format::UInt16;
ps_shader = ShaderConvert::FLOAT32_TO_16_BITS;
ps_shader = is_depth ? ShaderConvert::FLOAT32_TO_16_BITS : ShaderConvert::RGBA8_TO_16_BITS;
dltex = &m_uint16_download_texture;
}
break;
@@ -6777,7 +6892,7 @@ GSTextureCache::Source::~Source()
// to recycle.
if (!m_shared_texture && !m_from_hash_cache && m_texture)
{
if(m_from_target)
if (m_from_target)
g_texture_cache->m_target_memory_usage -= m_texture->GetMemUsage();
else
g_texture_cache->m_source_memory_usage -= m_texture->GetMemUsage();

View File

@@ -209,7 +209,7 @@ public:
struct TempZAddress
{
u32 ZBP;
u32 offset;
int offset;
GSVector4i rect_since;
};
@@ -441,7 +441,7 @@ protected:
std::unique_ptr<GSDownloadTexture> m_uint16_download_texture;
std::unique_ptr<GSDownloadTexture> m_uint32_download_texture;
Source* CreateSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, Target* t, bool half_right, int x_offset, int y_offset, const GSVector2i* lod, const GSVector4i* src_range, GSTexture* gpu_clut, SourceRegion region);
Source* CreateSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, Target* t, int x_offset, int y_offset, const GSVector2i* lod, const GSVector4i* src_range, GSTexture* gpu_clut, SourceRegion region);
bool PreloadTarget(GIFRegTEX0 TEX0, const GSVector2i& size, const GSVector2i& valid_size, bool is_frame,
bool preload, bool preserve_target, const GSVector4i draw_rect, Target* dst, GSTextureCache::Source* src = nullptr);

View File

@@ -270,56 +270,57 @@ std::string GSTextureReplacements::GetDumpFilename(const TextureName& name, u32
return ret;
const std::string game_dir(GetGameTextureDirectory());
if (!FileSystem::DirectoryExists(game_dir.c_str()))
const std::string game_subdir(Path::Combine(game_dir, TEXTURE_DUMP_SUBDIRECTORY_NAME));
if (!FileSystem::DirectoryExists(game_subdir.c_str()))
{
// create both dumps and replacements
if (!FileSystem::CreateDirectoryPath(game_dir.c_str(), false) ||
!FileSystem::EnsureDirectoryExists(Path::Combine(game_dir, "dumps").c_str(), false) ||
!FileSystem::EnsureDirectoryExists(Path::Combine(game_dir, "replacements").c_str(), false))
!FileSystem::EnsureDirectoryExists(game_subdir.c_str(), false) ||
!FileSystem::EnsureDirectoryExists(Path::Combine(game_dir, TEXTURE_REPLACEMENT_SUBDIRECTORY_NAME).c_str(), false))
{
// if it fails to create, we're not going to be able to use it anyway
return ret;
}
}
const std::string game_subdir(Path::Combine(game_dir, TEXTURE_DUMP_SUBDIRECTORY_NAME));
std::string filename;
if (name.HasRegion())
{
if (name.HasPalette())
{
filename = (level > 0) ?
StringUtil::StdStringFromFormat(TEXTURE_FILENAME_REGION_CLUT_FORMAT_STRING "-mip%u.png",
name.TEX0Hash, name.CLUTHash, name.region_width, name.region_height, name.bits, level) :
StringUtil::StdStringFromFormat(TEXTURE_FILENAME_REGION_CLUT_FORMAT_STRING ".png",
name.TEX0Hash, name.CLUTHash, name.region_width, name.region_height, name.bits);
filename = (level > 0)
? StringUtil::StdStringFromFormat(TEXTURE_FILENAME_REGION_CLUT_FORMAT_STRING "-mip%u.png",
name.TEX0Hash, name.CLUTHash, name.region_width, name.region_height, name.bits, level)
: StringUtil::StdStringFromFormat(TEXTURE_FILENAME_REGION_CLUT_FORMAT_STRING ".png",
name.TEX0Hash, name.CLUTHash, name.region_width, name.region_height, name.bits);
}
else
{
filename = (level > 0) ? StringUtil::StdStringFromFormat(
TEXTURE_FILENAME_REGION_FORMAT_STRING "-mip%u.png", name.TEX0Hash,
name.region_width, name.region_height, name.bits, level) :
StringUtil::StdStringFromFormat(
TEXTURE_FILENAME_REGION_FORMAT_STRING ".png", name.TEX0Hash,
name.region_width, name.region_height, name.bits);
filename = (level > 0)
? StringUtil::StdStringFromFormat(TEXTURE_FILENAME_REGION_FORMAT_STRING "-mip%u.png",
name.TEX0Hash, name.region_width, name.region_height, name.bits, level)
: StringUtil::StdStringFromFormat(TEXTURE_FILENAME_REGION_FORMAT_STRING ".png",
name.TEX0Hash, name.region_width, name.region_height, name.bits);
}
}
else
{
if (name.HasPalette())
{
filename = (level > 0) ? StringUtil::StdStringFromFormat(TEXTURE_FILENAME_CLUT_FORMAT_STRING "-mip%u.png",
name.TEX0Hash, name.CLUTHash, name.bits, level) :
StringUtil::StdStringFromFormat(TEXTURE_FILENAME_CLUT_FORMAT_STRING ".png",
name.TEX0Hash, name.CLUTHash, name.bits);
filename = (level > 0)
? StringUtil::StdStringFromFormat(TEXTURE_FILENAME_CLUT_FORMAT_STRING "-mip%u.png",
name.TEX0Hash, name.CLUTHash, name.bits, level)
: StringUtil::StdStringFromFormat(TEXTURE_FILENAME_CLUT_FORMAT_STRING ".png",
name.TEX0Hash, name.CLUTHash, name.bits);
}
else
{
filename = (level > 0) ? StringUtil::StdStringFromFormat(
TEXTURE_FILENAME_FORMAT_STRING "-mip%u.png", name.TEX0Hash, name.bits, level) :
StringUtil::StdStringFromFormat(
TEXTURE_FILENAME_FORMAT_STRING ".png", name.TEX0Hash, name.bits);
filename = (level > 0)
? StringUtil::StdStringFromFormat(TEXTURE_FILENAME_FORMAT_STRING "-mip%u.png",
name.TEX0Hash, name.bits, level)
: StringUtil::StdStringFromFormat(TEXTURE_FILENAME_FORMAT_STRING ".png",
name.TEX0Hash, name.bits);
}
}
@@ -349,6 +350,28 @@ void GSTextureReplacements::GameChanged()
ClearDumpedTextureList();
}
/// If the given file exists in the given directory, but with a different case than the original file, write its path to `*output` and return true.
static bool GetWrongCasePath(std::string* output, const char* dir, std::string_view file, FileSystem::FindResultsArray* reuseme)
{
if (FileSystem::FindFiles(dir, "*", FILESYSTEM_FIND_FOLDERS | FILESYSTEM_FIND_HIDDEN_FILES, reuseme))
{
for (const FILESYSTEM_FIND_DATA& fd : *reuseme)
{
std::string_view name = Path::GetFileName(fd.FileName);
if (name.size() != file.size())
continue;
if (0 == strncmp(name.data(), file.data(), name.size()))
continue;
if (0 == StringUtil::Strncasecmp(name.data(), file.data(), name.size()))
{
*output = fd.FileName;
return true;
}
}
}
return false;
}
void GSTextureReplacements::ReloadReplacementMap()
{
SyncWorkerThread();
@@ -368,9 +391,27 @@ void GSTextureReplacements::ReloadReplacementMap()
if (s_current_serial.empty() || !GSConfig.LoadTextureReplacements)
return;
const std::string replacement_dir(Path::Combine(GetGameTextureDirectory(), TEXTURE_REPLACEMENT_SUBDIRECTORY_NAME));
const std::string texture_dir = GetGameTextureDirectory();
const std::string replacement_dir(Path::Combine(texture_dir, TEXTURE_REPLACEMENT_SUBDIRECTORY_NAME));
FileSystem::FindResultsArray files;
// For some reason texture pack authors think it's a good idea to rename the replacements directory to something with the wrong case...
std::string wrong_case_path;
const std::string* right_case_path = nullptr;
if (GetWrongCasePath(&wrong_case_path, EmuFolders::Textures.c_str(), s_current_serial, &files))
right_case_path = &texture_dir;
else if (GetWrongCasePath(&wrong_case_path, texture_dir.c_str(), TEXTURE_REPLACEMENT_SUBDIRECTORY_NAME, &files))
right_case_path = &replacement_dir;
if (right_case_path)
{
Host::AddKeyedOSDMessage("TextureReplacementDirCaseMismatch",
fmt::format(TRANSLATE_FS("TextureReplacement", "Texture replacement directory {} will not work on case sensitive filesystems.\n"
"Rename it to {} to remove this warning."),
wrong_case_path, *right_case_path),
Host::OSD_WARNING_DURATION);
}
if (!FileSystem::FindFiles(replacement_dir.c_str(), "*", FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_HIDDEN_FILES | FILESYSTEM_FIND_RECURSIVE, &files))
return;

View File

@@ -1713,12 +1713,12 @@ void GSDeviceMTL::UpdateCLUTTexture(GSTexture* sTex, float sScale, u32 offsetX,
void GSDeviceMTL::ConvertToIndexedTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, u32 SBW, u32 SPSM, GSTexture* dTex, u32 DBW, u32 DPSM)
{ @autoreleasepool {
const ShaderConvert shader = ShaderConvert::RGBA_TO_8I;
const ShaderConvert shader = ((SPSM & 0xE) == 0) ? ShaderConvert::RGBA_TO_8I : ShaderConvert::RGB5A1_TO_8I;
id<MTLRenderPipelineState> pipeline = m_convert_pipeline[static_cast<int>(shader)];
if (!pipeline)
[NSException raise:@"StretchRect Missing Pipeline" format:@"No pipeline for %d", static_cast<int>(shader)];
GSMTLIndexedConvertPSUniform uniform = { sScale, SBW, DBW };
GSMTLIndexedConvertPSUniform uniform = { sScale, SBW, DBW, SPSM };
const GSVector4 dRect(0, 0, dTex->GetWidth(), dTex->GetHeight());
DoStretchRect(sTex, GSVector4::zero(), dTex, dRect, pipeline, false, LoadAction::DontCareIfFull, &uniform, sizeof(uniform));

View File

@@ -4,12 +4,19 @@
#include "GSMTLDeviceInfo.h"
#include "GS/GS.h"
#include "common/Console.h"
#include "common/Path.h"
#ifdef __APPLE__
static id<MTLLibrary> loadMainLibrary(id<MTLDevice> dev, NSString* name)
{
NSString* path = [[NSBundle mainBundle] pathForResource:name ofType:@"metallib"];
if (!path)
{
std::string ssname = std::string([name UTF8String]) + ".metallib";
std::string sspath = Path::Combine(EmuFolders::Resources, ssname);
path = [[NSString alloc] initWithBytes:sspath.data() length:sspath.length() encoding:NSUTF8StringEncoding];
}
return path ? [dev newLibraryWithFile:path error:nullptr] : nullptr;
}
@@ -24,6 +31,8 @@ static MRCOwned<id<MTLLibrary>> loadMainLibrary(id<MTLDevice> dev)
if (@available(macOS 10.14, iOS 12.0, *))
if (id<MTLLibrary> lib = loadMainLibrary(dev, @"Metal21"))
return MRCTransfer(lib);
if (id<MTLLibrary> lib = loadMainLibrary(dev, @"default"))
return MRCTransfer(lib);
return MRCTransfer([dev newDefaultLibrary]);
}

View File

@@ -64,6 +64,7 @@ struct GSMTLIndexedConvertPSUniform
float scale;
uint sbw;
uint dbw;
uint psm;
};
struct GSMTLDownsamplePSUniform

View File

@@ -296,6 +296,121 @@ fragment DepthOut ps_convert_rgb5a1_float16_biln(ConvertShaderData data [[stage_
return res.sample_biln<rgb5a1_to_depth16>(data.t);
}
fragment float4 ps_convert_rgb5a1_8i(ConvertShaderData data [[stage_in]], DirectReadTextureIn<float> res,
constant GSMTLIndexedConvertPSUniform& uniform [[buffer(GSMTLBufferIndexUniforms)]])
{
// Convert a RGB5A1 texture into a 8 bits packed texture
// Input column: 16x2 RGB5A1 pixels
// 0: 16 RGBA
// 1: 16 RGBA
// Output column: 16x4 Index pixels
// 0: 16 R5G2
// 1: 16 R5G2
// 2: 16 G2B5A1
// 3: 16 G2B5A1
uint2 pos = uint2(data.p.xy);
// Collapse separate R G B A areas into their base pixel
uint2 column = (pos & ~uint2(0u, 3u)) / uint2(1,2);
uint2 subcolumn = (pos & uint2(0u, 1u));
column.x -= (column.x / 128) * 64;
column.y += (column.y / 32) * 32;
// Deal with swizzling differences
if ((uniform.psm & 0x8) != 0) // PSMCT16S
{
if ((pos.x & 32) != 0)
{
column.y += 32; // 4 columns high times 4 to get bottom 4 blocks
column.x &= ~32;
}
if ((pos.x & 64) != 0)
{
column.x -= 32;
}
if (((pos.x & 16) != 0) != ((pos.y & 16) != 0))
{
column.x ^= 16;
column.y ^= 8;
}
if ((uniform.psm & 0x30) != 0) // PSMZ16S - Untested but hopefully ok if anything uses it.
{
column.x ^= 32;
column.y ^= 16;
}
}
else // PSMCT16
{
if ((pos.y & 32) != 0)
{
column.y -= 16;
column.x += 32;
}
if ((pos.x & 96) != 0)
{
uint multi = (pos.x & 96) / 32;
column.y += 16 * multi; // 4 columns high times 4 to get bottom 4 blocks
column.x -= (pos.x & 96);
}
if (((pos.x & 16) != 0) != ((pos.y & 16) != 0))
{
column.x ^= 16;
column.y ^= 8;
}
if ((uniform.psm & 0x30) != 0) // PSMZ16 - Untested but hopefully ok if anything uses it.
{
column.x ^= 32;
column.y ^= 32;
}
}
uint2 coord = column | subcolumn;
// Compensate for potentially differing page pitch.
uint2 block_xy = coord / uint2(64, 64);
uint block_num = (block_xy.y * (uniform.dbw / 128)) + block_xy.x;
uint2 block_offset = uint2((block_num % (uniform.sbw / 64)) * 64, (block_num / (uniform.sbw / 64)) * 64);
coord = (coord % uint2(64, 64)) + block_offset;
// Apply offset to cols 1 and 2
uint is_col23 = pos.y & 4;
uint is_col13 = pos.y & 2;
uint is_col12 = is_col23 ^ (is_col13 << 1);
coord.x ^= is_col12; // If cols 1 or 2, flip bit 3 of x
if (any(floor(uniform.scale) != uniform.scale))
coord = uint2(float2(coord) * uniform.scale);
else
coord = mul24(coord, uint2(uniform.scale));
float4 pixel = res.tex.read(coord);
uint4 denorm_c = (uint4)(pixel * 255.5f);
if ((pos.y & 2u) == 0u)
{
uint red = (denorm_c.r >> 3) & 0x1Fu;
uint green = (denorm_c.g >> 3) & 0x1Fu;
float sel0 = (float)(((green << 5) | red) & 0xFF) / 255.0f;
return float4(sel0);
}
else
{
uint green = (denorm_c.g >> 3) & 0x1Fu;
uint blue = (denorm_c.b >> 3) & 0x1Fu;
uint alpha = denorm_c.a & 0x80u;
float sel0 = (float)((alpha | (blue << 2) | (green >> 3)) & 0xFF) / 255.0f;
return float4(sel0);
}
}
fragment float4 ps_convert_rgba_8i(ConvertShaderData data [[stage_in]], DirectReadTextureIn<float> res,
constant GSMTLIndexedConvertPSUniform& uniform [[buffer(GSMTLBufferIndexUniforms)]])
{

View File

@@ -350,10 +350,11 @@ bool GSDeviceOGL::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
return false;
m_convert.ps[i].SetFormattedName("Convert pipe %s", name);
if (static_cast<ShaderConvert>(i) == ShaderConvert::RGBA_TO_8I)
if (static_cast<ShaderConvert>(i) == ShaderConvert::RGBA_TO_8I || static_cast<ShaderConvert>(i) == ShaderConvert::RGB5A1_TO_8I)
{
m_convert.ps[i].RegisterUniform("SBW");
m_convert.ps[i].RegisterUniform("DBW");
m_convert.ps[i].RegisterUniform("PSM");
m_convert.ps[i].RegisterUniform("ScaleFactor");
}
else if (static_cast<ShaderConvert>(i) == ShaderConvert::YUV)
@@ -1594,12 +1595,13 @@ void GSDeviceOGL::ConvertToIndexedTexture(GSTexture* sTex, float sScale, u32 off
{
CommitClear(sTex, false);
const ShaderConvert shader = ShaderConvert::RGBA_TO_8I;
const ShaderConvert shader = ((SPSM & 0xE) == 0) ? ShaderConvert::RGBA_TO_8I : ShaderConvert::RGB5A1_TO_8I;
GLProgram& prog = m_convert.ps[static_cast<int>(shader)];
prog.Bind();
prog.Uniform1ui(0, SBW);
prog.Uniform1ui(1, DBW);
prog.Uniform1f(2, sScale);
prog.Uniform1ui(2, SPSM);
prog.Uniform1f(3, sScale);
OMSetDepthStencilState(m_convert.dss);
OMSetBlendState(false);
@@ -2424,7 +2426,7 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
const GSVector4 dRect(config.colclip_update_area);
const GSVector4 sRect = dRect / GSVector4(size.x, size.y).xyxy();
StretchRect(colclip_rt, sRect, config.rt, dRect, ShaderConvert::COLCLIP_RESOLVE, false);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
Recycle(colclip_rt);
g_gs_device->SetColorClipTexture(nullptr);
@@ -2444,6 +2446,14 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
config.colclip_update_area = config.drawarea;
colclip_rt = CreateRenderTarget(rtsize.x, rtsize.y, GSTexture::Format::ColorClip, false);
if (!colclip_rt)
{
Console.Warning("GL: Failed to allocate ColorClip render target, aborting draw.");
return;
}
OMSetRenderTargets(colclip_rt, config.ds, nullptr);
g_gs_device->SetColorClipTexture(colclip_rt);
@@ -2451,6 +2461,7 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
const GSVector4 dRect = GSVector4((config.colclip_mode == GSHWDrawConfig::ColClipMode::ConvertOnly) ? GSVector4i::loadh(rtsize) : config.drawarea);
const GSVector4 sRect = dRect / GSVector4(rtsize.x, rtsize.y).xyxy();
StretchRect(config.rt, sRect, colclip_rt, dRect, ShaderConvert::COLCLIP_INIT, false);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
}
}
@@ -2462,6 +2473,11 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
break; // No setup
case GSHWDrawConfig::DestinationAlphaMode::PrimIDTracking:
primid_texture = InitPrimDateTexture(colclip_rt ? colclip_rt : config.rt, config.drawarea, config.datm);
if (!primid_texture)
{
Console.WriteLn("GL: Failed to allocate DATE image, aborting draw.");
return;
}
break;
case GSHWDrawConfig::DestinationAlphaMode::StencilOne:
if (m_features.texture_barrier)
@@ -2489,12 +2505,17 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
if (config.require_one_barrier && !m_features.texture_barrier)
{
// Requires a copy of the RT
// Requires a copy of the RT.
draw_rt_clone = CreateTexture(rtsize.x, rtsize.y, 1, colclip_rt ? GSTexture::Format::ColorClip : GSTexture::Format::Color, true);
GL_PUSH("Copy RT to temp texture for fbmask {%d,%d %dx%d}",
config.drawarea.left, config.drawarea.top,
config.drawarea.width(), config.drawarea.height());
CopyRect(colclip_rt ? colclip_rt : config.rt, draw_rt_clone, config.drawarea, config.drawarea.left, config.drawarea.top);
if (draw_rt_clone)
{
GL_PUSH("GL: Copy RT to temp texture {%d,%d %dx%d}",
config.drawarea.left, config.drawarea.top,
config.drawarea.width(), config.drawarea.height());
CopyRect(colclip_rt ? colclip_rt : config.rt, draw_rt_clone, config.drawarea, config.drawarea.left, config.drawarea.top);
}
else
Console.Warning("GL: Failed to allocate temp texture for RT copy.");
}
IASetVertexBuffer(config.verts, config.nverts);
@@ -2563,7 +2584,7 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
if (check_barrier && ((config.tex && (config.tex == config.ds || config.tex == config.rt)) || ((psel.ps.IsFeedbackLoop() || psel.ps.blend_c == 1) && GLState::rt == config.rt)))
{
// Ensure all depth writes are finished before sampling
GL_INS("Texture barrier to flush depth or rt before reading");
GL_INS("GL: Texture barrier to flush depth or rt before reading");
glTextureBarrier();
}
// additional non-pipeline config stuff
@@ -2710,7 +2731,7 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
const GSVector4 dRect(config.colclip_update_area);
const GSVector4 sRect = dRect / GSVector4(size.x, size.y).xyxy();
StretchRect(colclip_rt, sRect, config.rt, dRect, ShaderConvert::COLCLIP_RESOLVE, false);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
Recycle(colclip_rt);
g_gs_device->SetColorClipTexture(nullptr);

View File

@@ -1491,7 +1491,7 @@ __ri void GSDrawScanline::CDrawScanline(int pixels, int left, int top, const GSV
case 2: break;
}
if(!(sel.fpsm == 1 && sel.abc == 1))
if (!(sel.fpsm == 1 && sel.abc == 1))
{
switch(sel.abc)
{

View File

@@ -562,7 +562,7 @@ L("loop");
L("step");
// if(steps <= 0) break;
// if (steps <= 0) break;
if (!m_sel.edge)
{
@@ -768,7 +768,7 @@ void GSDrawScanlineCodeGenerator::Init()
cvttps2dq(xym6, vt);
// s = vti.xxxx() + m_local.d[skip].s;
// t = vti.yyyy(); if(!sprite) t += m_local.d[skip].t;
// t = vti.yyyy(); if (!sprite) t += m_local.d[skip].t;
pshufd(s, xym6, _MM_SHUFFLE(0, 0, 0, 0));
pshufd(t, xym6, _MM_SHUFFLE(1, 1, 1, 1));
@@ -925,7 +925,7 @@ void GSDrawScanlineCodeGenerator::Step()
// GSVector4i stq = m_local.d4.stq;
// s += stq.xxxx();
// if(!sprite) t += st.yyyy();
// if (!sprite) t += st.yyyy();
broadcasti128(stq, _rip_local_d(stq));
@@ -2096,7 +2096,7 @@ void GSDrawScanlineCodeGenerator::AlphaTFX()
clamp16(_ga, tmp);
// if(!tcc) gat = gat.mix16(ga.srl16(7));
// if (!tcc) gat = gat.mix16(ga.srl16(7));
if (!m_sel.tcc)
{
@@ -2109,7 +2109,7 @@ void GSDrawScanlineCodeGenerator::AlphaTFX()
case TFX_DECAL:
// if(!tcc) gat = gat.mix16(ga.srl16(7));
// if (!tcc) gat = gat.mix16(ga.srl16(7));
if (!m_sel.tcc)
{
// GSVector4i ga = iip ? gaf : m_local.c.ga;
@@ -2139,7 +2139,7 @@ void GSDrawScanlineCodeGenerator::AlphaTFX()
case TFX_HIGHLIGHT2:
// if(!tcc) gat = gat.mix16(ga.srl16(7));
// if (!tcc) gat = gat.mix16(ga.srl16(7));
if (!m_sel.tcc)
{
@@ -2990,8 +2990,8 @@ void GSDrawScanlineCodeGenerator::WritePixel(const XYm& src_, const AddressReg&
{
if (fast)
{
// if(fzm & 0x0f) GSVector4i::storel(&vm16[addr + 0], fs);
// if(fzm & 0xf0) GSVector4i::storeh(&vm16[addr + 8], fs);
// if (fzm & 0x0f) GSVector4i::storel(&vm16[addr + 0], fs);
// if (fzm & 0xf0) GSVector4i::storeh(&vm16[addr + 8], fs);
test(mask, 0x0000000f << shift);
je("@f");
@@ -3020,10 +3020,10 @@ void GSDrawScanlineCodeGenerator::WritePixel(const XYm& src_, const AddressReg&
}
else
{
// if(fzm & 0x03) WritePixel(fpsm, &vm16[addr + 0], fs.extract32<0>());
// if(fzm & 0x0c) WritePixel(fpsm, &vm16[addr + 2], fs.extract32<1>());
// if(fzm & 0x30) WritePixel(fpsm, &vm16[addr + 8], fs.extract32<2>());
// if(fzm & 0xc0) WritePixel(fpsm, &vm16[addr + 10], fs.extract32<3>());
// if (fzm & 0x03) WritePixel(fpsm, &vm16[addr + 0], fs.extract32<0>());
// if (fzm & 0x0c) WritePixel(fpsm, &vm16[addr + 2], fs.extract32<1>());
// if (fzm & 0x30) WritePixel(fpsm, &vm16[addr + 8], fs.extract32<2>());
// if (fzm & 0xc0) WritePixel(fpsm, &vm16[addr + 10], fs.extract32<3>());
test(mask, 0x00000003 << shift);
je("@f");

View File

@@ -158,7 +158,7 @@ void GSDrawScanlineCodeGenerator::Generate()
Label exit;
armAsm->Bind(&m_step_label);
// if(steps <= 0) break;
// if (steps <= 0) break;
if (!m_sel.edge)
{
@@ -332,7 +332,7 @@ void GSDrawScanlineCodeGenerator::Init()
armAsm->Fcvtzs(v6.V4S(), v4.V4S());
// s = vti.xxxx() + m_local.d[skip].s;
// t = vti.yyyy(); if(!sprite) t += m_local.d[skip].t;
// t = vti.yyyy(); if (!sprite) t += m_local.d[skip].t;
armAsm->Dup(_temp_s.V4S(), v6.V4S(), 0);
armAsm->Dup(_temp_t.V4S(), v6.V4S(), 1);
@@ -485,7 +485,7 @@ void GSDrawScanlineCodeGenerator::Step()
// GSVector4i stq = m_local.d4.stq;
// s += stq.xxxx();
// if(!sprite) t += stq.yyyy();
// if (!sprite) t += stq.yyyy();
armAsm->Dup(_vscratch.V4S(), _d4_stq.V4S(), 0);
if (m_sel.prim != GS_SPRITE_CLASS || m_sel.mmin)
@@ -1394,7 +1394,7 @@ void GSDrawScanlineCodeGenerator::AlphaTFX()
modulate16(v6, _temp_ga, 1);
clamp16(v6, v3);
// if(!tcc) gat = gat.mix16(ga.srl16(7));
// if (!tcc) gat = gat.mix16(ga.srl16(7));
if (!m_sel.tcc)
{
@@ -1407,7 +1407,7 @@ void GSDrawScanlineCodeGenerator::AlphaTFX()
case TFX_DECAL:
// if(!tcc) gat = gat.mix16(ga.srl16(7));
// if (!tcc) gat = gat.mix16(ga.srl16(7));
if (!m_sel.tcc)
{
@@ -1435,7 +1435,7 @@ void GSDrawScanlineCodeGenerator::AlphaTFX()
case TFX_HIGHLIGHT2:
// if(!tcc) gat = gat.mix16(ga.srl16(7));
// if (!tcc) gat = gat.mix16(ga.srl16(7));
if (!m_sel.tcc)
{
@@ -2158,8 +2158,8 @@ void GSDrawScanlineCodeGenerator::WritePixel(const VRegister& src, const Registe
{
if (fast)
{
// if(fzm & 0x0f) GSVector4i::storel(&vm16[addr + 0], fs);
// if(fzm & 0xf0) GSVector4i::storeh(&vm16[addr + 8], fs);
// if (fzm & 0x0f) GSVector4i::storel(&vm16[addr + 0], fs);
// if (fzm & 0xf0) GSVector4i::storeh(&vm16[addr + 8], fs);
Label skip_low, skip_high;
armAsm->Lsl(_wscratch, addr, 1); // *2
@@ -2177,10 +2177,10 @@ void GSDrawScanlineCodeGenerator::WritePixel(const VRegister& src, const Registe
}
else
{
// if(fzm & 0x03) WritePixel(fpsm, &vm16[addr + 0], fs.extract32<0>());
// if(fzm & 0x0c) WritePixel(fpsm, &vm16[addr + 2], fs.extract32<1>());
// if(fzm & 0x30) WritePixel(fpsm, &vm16[addr + 8], fs.extract32<2>());
// if(fzm & 0xc0) WritePixel(fpsm, &vm16[addr + 10], fs.extract32<3>());
// if (fzm & 0x03) WritePixel(fpsm, &vm16[addr + 0], fs.extract32<0>());
// if (fzm & 0x0c) WritePixel(fpsm, &vm16[addr + 2], fs.extract32<1>());
// if (fzm & 0x30) WritePixel(fpsm, &vm16[addr + 8], fs.extract32<2>());
// if (fzm & 0xc0) WritePixel(fpsm, &vm16[addr + 10], fs.extract32<3>());
Label skip_0, skip_1, skip_2, skip_3;

View File

@@ -444,9 +444,9 @@ void GSRasterizer::DrawTriangle(const GSVertexSW* vertex, const u16* index)
m1 = (y0011 == y1221).mask() & 7;
// if(i == 0) => y0 < y1 < y2
// if(i == 1) => y0 == y1 < y2
// if(i == 4) => y0 < y1 == y2
// if (i == 0) => y0 < y1 < y2
// if (i == 1) => y0 == y1 < y2
// if (i == 4) => y0 < y1 == y2
if (m1 == 7) // y0 == y1 == y2
return;
@@ -623,9 +623,9 @@ void GSRasterizer::DrawTriangle(const GSVertexSW* vertex, const u16* index)
m1 = (y0011 == y1221).mask() & 7;
// if(i == 0) => y0 < y1 < y2
// if(i == 1) => y0 == y1 < y2
// if(i == 4) => y0 < y1 == y2
// if (i == 0) => y0 < y1 < y2
// if (i == 1) => y0 == y1 < y2
// if (i == 4) => y0 < y1 == y2
if (m1 == 7)
return; // y0 == y1 == y2

View File

@@ -94,7 +94,7 @@ void GSRendererSW::VSync(u32 field, bool registers_written, bool idle_frame)
m_tc->IncAge();
m_draw_transfers.clear();
// if((m_perfmon.GetFrame() & 255) == 0) m_rl->PrintStats();
// if ((m_perfmon.GetFrame() & 255) == 0) m_rl->PrintStats();
}
GSTexture* GSRendererSW::GetOutput(int i, float& scale, int& y_offset)

View File

@@ -515,7 +515,7 @@ void GSSetupPrimCodeGenerator::Color()
pshufd(xym1, xym0, _MM_SHUFFLE(1, 0, 3, 2));
punpcklwd(xym0, xym1);
// if(!tme) c = c.srl16(7);
// if (!tme) c = c.srl16(7);
if (m_sel.tfx == TFX_NONE)
{

View File

@@ -322,7 +322,7 @@ void GSSetupPrimCodeGenerator::Color()
armAsm->Ext(v1.V16B(), v0.V16B(), v0.V16B(), 8);
armAsm->Zip1(v0.V8H(), v0.V8H(), v1.V8H());
// if(!tme) c = c.srl16(7);
// if (!tme) c = c.srl16(7);
if (m_sel.tfx == TFX_NONE)
armAsm->Ushr(v0.V8H(), v0.V8H(), 7);

View File

@@ -2059,6 +2059,12 @@ bool GSDeviceVK::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
return false;
}
if (!CreateNullTexture())
{
Host::ReportErrorAsync("GS", "Failed to create dummy texture");
return false;
}
{
std::optional<std::string> shader = ReadShaderSource("shaders/vulkan/tfx.glsl");
if (!shader.has_value())
@@ -2070,12 +2076,6 @@ bool GSDeviceVK::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
m_tfx_source = std::move(*shader);
}
if (!CreateNullTexture())
{
Host::ReportErrorAsync("GS", "Failed to create dummy texture");
return false;
}
if (!CreatePipelineLayouts())
{
Host::ReportErrorAsync("GS", "Failed to create pipeline layouts");
@@ -3158,15 +3158,16 @@ void GSDeviceVK::ConvertToIndexedTexture(
{
u32 SBW;
u32 DBW;
u32 pad1[2];
u32 PSM;
u32 pad1[1];
float ScaleFactor;
float pad2[3];
};
const Uniforms uniforms = {SBW, DBW, {}, sScale, {}};
const Uniforms uniforms = {SBW, DBW, SPSM, {}, sScale, {}};
SetUtilityPushConstants(&uniforms, sizeof(uniforms));
const ShaderConvert shader = ShaderConvert::RGBA_TO_8I;
const ShaderConvert shader = ((SPSM & 0xE) == 0) ? ShaderConvert::RGBA_TO_8I : ShaderConvert::RGB5A1_TO_8I;
const GSVector4 dRect(0, 0, dTex->GetWidth(), dTex->GetHeight());
DoStretchRect(static_cast<GSTextureVK*>(sTex), GSVector4::zero(), static_cast<GSTextureVK*>(dTex), dRect,
m_convert[static_cast<int>(shader)], false, true);
@@ -5291,8 +5292,12 @@ void GSDeviceVK::SetPipeline(VkPipeline pipeline)
void GSDeviceVK::SetInitialState(VkCommandBuffer cmdbuf)
{
const VkDeviceSize buffer_offset = 0;
vkCmdBindVertexBuffers(cmdbuf, 0, 1, m_vertex_stream_buffer.GetBufferPtr(), &buffer_offset);
VkBuffer buffer = *m_vertex_stream_buffer.GetBufferPtr();
if (buffer != VK_NULL_HANDLE)
{
constexpr VkDeviceSize buffer_offset = 0;
vkCmdBindVertexBuffers(cmdbuf, 0, 1, &buffer, &buffer_offset);
}
}
__ri void GSDeviceVK::ApplyBaseState(u32 flags, VkCommandBuffer cmdbuf)
@@ -5625,7 +5630,7 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
date_image = SetupPrimitiveTrackingDATE(config);
if (!date_image)
{
Console.WriteLn("Failed to allocate DATE image, aborting draw.");
Console.WriteLn("VK: Failed to allocate DATE image, aborting draw.");
return;
}
@@ -5726,12 +5731,14 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
{
EndRenderPass();
GL_PUSH("Copy RT to temp texture for fbmask {%d,%d %dx%d}", config.drawarea.left, config.drawarea.top,
GL_PUSH("VK: Copy RT to temp texture for fbmask {%d,%d %dx%d}", config.drawarea.left, config.drawarea.top,
config.drawarea.width(), config.drawarea.height());
CopyRect(draw_rt, draw_rt_clone, config.drawarea, config.drawarea.left, config.drawarea.top);
PSSetShaderResource(2, draw_rt_clone, true);
}
else
Console.Warning("VK: Failed to allocate temp texture for RT copy.");
}
// Switch to colclip target for colclip hw rendering
@@ -5744,7 +5751,7 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
colclip_rt = static_cast<GSTextureVK*>(CreateRenderTarget(rtsize.x, rtsize.y, GSTexture::Format::ColorClip, false));
if (!colclip_rt)
{
Console.WriteLn("Failed to allocate ColorClip render target, aborting draw.");
Console.Warning("VK: Failed to allocate ColorClip render target, aborting draw.");
if (date_image)
Recycle(date_image);

View File

@@ -379,8 +379,8 @@ void GSDumpReplayer::RenderUI()
do \
{ \
text_size = font->CalcTextSizeA(font->FontSize, std::numeric_limits<float>::max(), -1.0f, (text), nullptr, nullptr); \
dl->AddText(font, font->FontSize, ImVec2(margin + shadow_offset, position_y + shadow_offset), IM_COL32(0, 0, 0, 100), (text)); \
dl->AddText(font, font->FontSize, ImVec2(margin, position_y), color, (text)); \
dl->AddText(font, font->FontSize, ImVec2(GSConfig.OsdPerformancePos == OsdOverlayPos::TopLeft ? ImGuiManager::GetWindowWidth() - margin - text_size.x + shadow_offset : margin + shadow_offset, position_y + shadow_offset), IM_COL32(0, 0, 0, 100), (text)); \
dl->AddText(font, font->FontSize, ImVec2(GSConfig.OsdPerformancePos == OsdOverlayPos::TopLeft ? ImGuiManager::GetWindowWidth() - margin - text_size.x : margin, position_y), color, (text)); \
position_y += text_size.y + spacing; \
} while (0)

View File

@@ -91,42 +91,98 @@ static std::recursive_mutex s_mutex;
static GameList::CacheMap s_cache_map;
static std::FILE* s_cache_write_stream = nullptr;
const char* GameList::EntryTypeToString(EntryType type)
const char* GameList::EntryTypeToString(EntryType type, bool translate)
{
static std::array<const char*, static_cast<int>(EntryType::Count)> names = {{"PS2Disc", "PS1Disc", "ELF"}};
return names[static_cast<int>(type)];
static constexpr std::array<const char*, static_cast<int>(EntryType::Count)> names = {
TRANSLATE_NOOP("GameList", "PS2 Disc"),
TRANSLATE_NOOP("GameList", "PS1 Disc"),
TRANSLATE_NOOP("GameList", "ELF"),
TRANSLATE_NOOP("GameList", "Invalid"),
};
const char* name = names.at(static_cast<int>(type));
if (translate)
name = TRANSLATE("GameList", name);
return name;
}
const char* GameList::EntryTypeToDisplayString(EntryType type)
const char* GameList::RegionToString(Region region, bool translate)
{
static std::array<const char*, static_cast<int>(EntryType::Count)> names = {{TRANSLATE("GameList", "PS2 Disc"), TRANSLATE("GameList", "PS1 Disc"), TRANSLATE("GameList", "ELF")}};
return names[static_cast<int>(type)];
static constexpr std::array<const char*, static_cast<int>(Region::Count)> names = {
TRANSLATE_NOOP("GameList", "NTSC-B"),
TRANSLATE_NOOP("GameList", "NTSC-C"),
TRANSLATE_NOOP("GameList", "NTSC-HK"),
TRANSLATE_NOOP("GameList", "NTSC-J"),
TRANSLATE_NOOP("GameList", "NTSC-K"),
TRANSLATE_NOOP("GameList", "NTSC-T"),
TRANSLATE_NOOP("GameList", "NTSC-U"),
TRANSLATE_NOOP("GameList", "Other"),
TRANSLATE_NOOP("GameList", "PAL-A"),
TRANSLATE_NOOP("GameList", "PAL-AF"),
TRANSLATE_NOOP("GameList", "PAL-AU"),
TRANSLATE_NOOP("GameList", "PAL-BE"),
TRANSLATE_NOOP("GameList", "PAL-E"),
TRANSLATE_NOOP("GameList", "PAL-F"),
TRANSLATE_NOOP("GameList", "PAL-FI"),
TRANSLATE_NOOP("GameList", "PAL-G"),
TRANSLATE_NOOP("GameList", "PAL-GR"),
TRANSLATE_NOOP("GameList", "PAL-I"),
TRANSLATE_NOOP("GameList", "PAL-IN"),
TRANSLATE_NOOP("GameList", "PAL-M"),
TRANSLATE_NOOP("GameList", "PAL-NL"),
TRANSLATE_NOOP("GameList", "PAL-NO"),
TRANSLATE_NOOP("GameList", "PAL-P"),
TRANSLATE_NOOP("GameList", "PAL-PL"),
TRANSLATE_NOOP("GameList", "PAL-R"),
TRANSLATE_NOOP("GameList", "PAL-S"),
TRANSLATE_NOOP("GameList", "PAL-SC"),
TRANSLATE_NOOP("GameList", "PAL-SW"),
TRANSLATE_NOOP("GameList", "PAL-SWI"),
TRANSLATE_NOOP("GameList", "PAL-UK"),
};
const char* name = names.at(static_cast<int>(region));
if (translate)
name = TRANSLATE("GameList", name);
return name;
}
const char* GameList::RegionToString(Region region)
const char* GameList::EntryCompatibilityRatingToString(CompatibilityRating rating, bool translate)
{
static std::array<const char*, static_cast<int>(Region::Count)> names = {{"NTSC-B", "NTSC-C", "NTSC-HK", "NTSC-J", "NTSC-K", "NTSC-T",
"NTSC-U", TRANSLATE("GameList", "Other"), "PAL-A", "PAL-AF", "PAL-AU", "PAL-BE", "PAL-E", "PAL-F", "PAL-FI", "PAL-G", "PAL-GR", "PAL-I", "PAL-IN", "PAL-M",
"PAL-NL", "PAL-NO", "PAL-P", "PAL-PL", "PAL-R", "PAL-S", "PAL-SC", "PAL-SW", "PAL-SWI", "PAL-UK"}};
return names[static_cast<int>(region)];
}
const char* GameList::EntryCompatibilityRatingToString(CompatibilityRating rating)
{
// clang-format off
const char* name = "";
switch (rating)
{
case CompatibilityRating::Unknown: return TRANSLATE("GameList", "Unknown");
case CompatibilityRating::Nothing: return TRANSLATE("GameList", "Nothing");
case CompatibilityRating::Intro: return TRANSLATE("GameList", "Intro");
case CompatibilityRating::Menu: return TRANSLATE("GameList", "Menu");
case CompatibilityRating::InGame: return TRANSLATE("GameList", "In-Game");
case CompatibilityRating::Playable: return TRANSLATE("GameList", "Playable");
case CompatibilityRating::Perfect: return TRANSLATE("GameList", "Perfect");
default: return "";
case CompatibilityRating::Unknown:
name = TRANSLATE_NOOP("GameList", "Unknown");
break;
case CompatibilityRating::Nothing:
name = TRANSLATE_NOOP("GameList", "Nothing");
break;
case CompatibilityRating::Intro:
name = TRANSLATE_NOOP("GameList", "Intro");
break;
case CompatibilityRating::Menu:
name = TRANSLATE_NOOP("GameList", "Menu");
break;
case CompatibilityRating::InGame:
name = TRANSLATE_NOOP("GameList", "In-Game");
break;
case CompatibilityRating::Playable:
name = TRANSLATE_NOOP("GameList", "Playable");
break;
case CompatibilityRating::Perfect:
name = TRANSLATE_NOOP("GameList", "Perfect");
break;
default:
return "";
}
// clang-format on
if (translate)
name = TRANSLATE("GameList", name);
return name;
}
bool GameList::IsScannableFilename(const std::string_view path)
@@ -591,15 +647,18 @@ void GameList::ScanDirectory(const char* path, bool recursive, bool only_cache,
Console.WriteLn("Scanning %s%s", path, recursive ? " (recursively)" : "");
progress->PushState();
progress->SetStatusText(fmt::format(
recursive ? TRANSLATE_FS("GameList", "Scanning directory {} (recursively)...") :
TRANSLATE_FS("GameList", "Scanning directory {}..."),
path).c_str());
if (recursive)
progress->SetStatusText(
fmt::format(TRANSLATE_FS("GameList", "Scanning directory {} (recursively)..."), path).c_str());
else
progress->SetStatusText(
fmt::format(TRANSLATE_FS("GameList", "Scanning directory {}..."), path).c_str());
FileSystem::FindResultsArray files;
FileSystem::FindFiles(path, "*",
recursive ? (FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_HIDDEN_FILES | FILESYSTEM_FIND_RECURSIVE) :
(FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_HIDDEN_FILES),
(FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_HIDDEN_FILES),
&files, progress);
u32 files_scanned = 0;
@@ -622,7 +681,7 @@ void GameList::ScanDirectory(const char* path, bool recursive, bool only_cache,
}
const std::string_view filename = Path::GetFileName(ffd.FileName);
progress->SetStatusText(fmt::format(TRANSLATE_FS("GameList","Scanning {}..."), filename.data()).c_str());
progress->SetStatusText(fmt::format(TRANSLATE_FS("GameList", "Scanning {}..."), filename.data()).c_str());
ScanFile(std::move(ffd.FileName), ffd.ModificationTime, lock, played_time_map, custom_attributes_ini);
progress->SetProgressValue(files_scanned);
}
@@ -1300,7 +1359,7 @@ bool GameList::DownloadCovers(const std::vector<std::string>& url_templates, boo
continue;
}
progress->SetStatusText(fmt::format(TRANSLATE_FS("GameList","Downloading cover for {0} [{1}]..."), entry->title, entry->serial).c_str());
progress->SetStatusText(fmt::format(TRANSLATE_FS("GameList", "Downloading cover for {0} [{1}]..."), entry->title, entry->serial).c_str());
}
// we could actually do a few in parallel here...

View File

@@ -104,10 +104,9 @@ namespace GameList
__fi bool IsDisc() const { return (type == EntryType::PS1Disc || type == EntryType::PS2Disc); }
};
const char* EntryTypeToString(EntryType type);
const char* EntryTypeToDisplayString(EntryType type);
const char* RegionToString(Region region);
const char* EntryCompatibilityRatingToString(CompatibilityRating rating);
const char* EntryTypeToString(EntryType type, bool translate);
const char* RegionToString(Region region, bool translate);
const char* EntryCompatibilityRatingToString(CompatibilityRating rating, bool translate);
/// Fills in boot parameters (iso or elf) based on the game list entry.
void FillBootParametersForEntry(VMBootParameters* params, const Entry* entry);

View File

@@ -10,9 +10,9 @@
#define ipumsk( src ) ( (src) & 0xff )
#define ipucase( src ) case ipumsk(src)
#define IPU_INT_TO( cycles ) if(!(cpuRegs.interrupt & (1<<4))) CPU_INT( DMAC_TO_IPU, cycles )
#define IPU_INT_TO( cycles ) if (!(cpuRegs.interrupt & (1<<4))) CPU_INT( DMAC_TO_IPU, cycles )
#define IPU_INT_FROM( cycles ) CPU_INT( DMAC_FROM_IPU, cycles )
#define IPU_INT_PROCESS( cycles ) if(!(cpuRegs.interrupt & (1 << IPU_PROCESS))) CPU_INT( IPU_PROCESS, cycles )
#define IPU_INT_PROCESS( cycles ) if (!(cpuRegs.interrupt & (1 << IPU_PROCESS))) CPU_INT( IPU_PROCESS, cycles )
//
// Bitfield Structures
//

View File

@@ -203,10 +203,10 @@ namespace FullscreenUI
Graphics,
Audio,
MemoryCard,
Folders,
Achievements,
Controller,
Hotkey,
Achievements,
Folders,
Advanced,
Patches,
Cheats,
@@ -322,10 +322,10 @@ namespace FullscreenUI
static void DrawGraphicsSettingsPage(SettingsInterface* bsi, bool show_advanced_settings);
static void DrawAudioSettingsPage();
static void DrawMemoryCardSettingsPage();
static void DrawFoldersSettingsPage();
static void DrawAchievementsSettingsPage(std::unique_lock<std::mutex>& settings_lock);
static void DrawControllerSettingsPage();
static void DrawHotkeySettingsPage();
static void DrawAchievementsSettingsPage(std::unique_lock<std::mutex>& settings_lock);
static void DrawFoldersSettingsPage();
static void DrawAdvancedSettingsPage();
static void DrawPatchesOrCheatsSettingsPage(bool cheats);
static void DrawGameFixesSettingsPage();
@@ -3067,10 +3067,10 @@ void FullscreenUI::DrawSettingsWindow()
ICON_PF_PICTURE,
ICON_PF_SOUND,
ICON_PF_MEMORY_CARD,
ICON_FA_FOLDER_OPEN,
ICON_FA_TROPHY,
ICON_PF_GAMEPAD_ALT,
ICON_PF_KEYBOARD_ALT,
ICON_FA_TROPHY,
ICON_FA_FOLDER_OPEN,
ICON_FA_EXCLAMATION_TRIANGLE,
};
static constexpr const char* per_game_icons[] = {
@@ -3090,10 +3090,10 @@ void FullscreenUI::DrawSettingsWindow()
SettingsPage::Graphics,
SettingsPage::Audio,
SettingsPage::MemoryCard,
SettingsPage::Folders,
SettingsPage::Achievements,
SettingsPage::Controller,
SettingsPage::Hotkey,
SettingsPage::Achievements,
SettingsPage::Folders,
SettingsPage::Advanced,
};
static constexpr SettingsPage per_game_pages[] = {
@@ -3114,10 +3114,10 @@ void FullscreenUI::DrawSettingsWindow()
FSUI_NSTR("Graphics Settings"),
FSUI_NSTR("Audio Settings"),
FSUI_NSTR("Memory Card Settings"),
FSUI_NSTR("Folder Settings"),
FSUI_NSTR("Achievements Settings"),
FSUI_NSTR("Controller Settings"),
FSUI_NSTR("Hotkey Settings"),
FSUI_NSTR("Achievements Settings"),
FSUI_NSTR("Folder Settings"),
FSUI_NSTR("Advanced Settings"),
FSUI_NSTR("Patches"),
FSUI_NSTR("Cheats"),
@@ -3240,20 +3240,20 @@ void FullscreenUI::DrawSettingsWindow()
DrawMemoryCardSettingsPage();
break;
case SettingsPage::Controller:
DrawControllerSettingsPage();
break;
case SettingsPage::Hotkey:
DrawHotkeySettingsPage();
case SettingsPage::Folders:
DrawFoldersSettingsPage();
break;
case SettingsPage::Achievements:
DrawAchievementsSettingsPage(lock);
break;
case SettingsPage::Folders:
DrawFoldersSettingsPage();
case SettingsPage::Controller:
DrawControllerSettingsPage();
break;
case SettingsPage::Hotkey:
DrawHotkeySettingsPage();
break;
case SettingsPage::Patches:
@@ -3316,15 +3316,15 @@ void FullscreenUI::DrawSummarySettingsPage()
CopyTextToClipboard(FSUI_STR("Game serial copied to clipboard."), s_game_settings_entry->serial);
if (MenuButton(FSUI_ICONSTR(ICON_FA_CODE, "CRC"), fmt::format("{:08X}", s_game_settings_entry->crc).c_str(), true))
CopyTextToClipboard(FSUI_STR("Game CRC copied to clipboard."), fmt::format("{:08X}", s_game_settings_entry->crc));
if (MenuButton(FSUI_ICONSTR(ICON_FA_BOX, "Type"), GameList::EntryTypeToDisplayString(s_game_settings_entry->type), true))
CopyTextToClipboard(FSUI_STR("Game type copied to clipboard."), GameList::EntryTypeToDisplayString(s_game_settings_entry->type));
if (MenuButton(FSUI_ICONSTR(ICON_FA_GLOBE, "Region"), GameList::RegionToString(s_game_settings_entry->region), true))
CopyTextToClipboard(FSUI_STR("Game region copied to clipboard."), GameList::RegionToString(s_game_settings_entry->region));
if (MenuButton(FSUI_ICONSTR(ICON_FA_BOX, "Type"), GameList::EntryTypeToString(s_game_settings_entry->type, true), true))
CopyTextToClipboard(FSUI_STR("Game type copied to clipboard."), GameList::EntryTypeToString(s_game_settings_entry->type, true));
if (MenuButton(FSUI_ICONSTR(ICON_FA_GLOBE, "Region"), GameList::RegionToString(s_game_settings_entry->region, true), true))
CopyTextToClipboard(FSUI_STR("Game region copied to clipboard."), GameList::RegionToString(s_game_settings_entry->region, true));
if (MenuButton(FSUI_ICONSTR(ICON_FA_STAR, "Compatibility Rating"),
GameList::EntryCompatibilityRatingToString(s_game_settings_entry->compatibility_rating), true))
GameList::EntryCompatibilityRatingToString(s_game_settings_entry->compatibility_rating, true), true))
{
CopyTextToClipboard(FSUI_STR("Game compatibility copied to clipboard."),
GameList::EntryCompatibilityRatingToString(s_game_settings_entry->compatibility_rating));
GameList::EntryCompatibilityRatingToString(s_game_settings_entry->compatibility_rating, true));
}
if (MenuButton(FSUI_ICONSTR(ICON_FA_FOLDER_OPEN, "Path"), s_game_settings_entry->path.c_str(), true))
CopyTextToClipboard(FSUI_STR("Game path copied to clipboard."), s_game_settings_entry->path);
@@ -3559,7 +3559,7 @@ void FullscreenUI::DrawInterfaceSettingsPage()
MenuHeading(FSUI_CSTR("Integration"));
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_USER_CIRCLE, "Enable Discord Presence"),
FSUI_CSTR("Shows the game you are currently playing as part of your profile on Discord."), "UI", "DiscordPresence", false);
FSUI_CSTR("Shows the game you are currently playing as part of your profile on Discord."), "EmuCore", "EnableDiscordPresence", false);
MenuHeading(FSUI_CSTR("Game Display"));
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_TV, "Start Fullscreen"),
@@ -5738,9 +5738,12 @@ u32 FullscreenUI::PopulateSaveStateListEntries(const std::string& title, const s
if (InitializeSaveStateListEntry(&li, title, serial, crc, i) || !s_save_state_selector_loading)
s_save_state_selector_slots.push_back(std::move(li));
SaveStateListEntry bli;
if (InitializeSaveStateListEntry(&bli, title, serial, crc, i, true) || !s_save_state_selector_loading)
s_save_state_selector_slots.push_back(std::move(bli));
if (s_save_state_selector_loading)
{
SaveStateListEntry bli;
if (InitializeSaveStateListEntry(&bli, title, serial, crc, i, true))
s_save_state_selector_slots.push_back(std::move(bli));
}
}
return static_cast<u32>(s_save_state_selector_slots.size());
@@ -6456,9 +6459,9 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size)
summary.clear();
if (entry->serial.empty())
fmt::format_to(std::back_inserter(summary), "{} - ", GameList::RegionToString(entry->region));
fmt::format_to(std::back_inserter(summary), "{} - ", GameList::RegionToString(entry->region, true));
else
fmt::format_to(std::back_inserter(summary), "{} - {} - ", entry->serial, GameList::RegionToString(entry->region));
fmt::format_to(std::back_inserter(summary), "{} - {} - ", entry->serial, GameList::RegionToString(entry->region, true));
const std::string_view filename(Path::GetFileName(entry->path));
summary.append(filename);
@@ -6560,12 +6563,12 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size)
// region
{
std::string flag_texture(fmt::format("icons/flags/{}.svg", GameList::RegionToString(selected_entry->region)));
std::string flag_texture(fmt::format("icons/flags/{}.svg", GameList::RegionToString(selected_entry->region, false)));
ImGui::TextUnformatted(FSUI_CSTR("Region: "));
ImGui::SameLine();
DrawCachedSvgTextureAsync(flag_texture, LayoutScale(23.0f, 16.0f), SvgScaling::Fit);
ImGui::SameLine();
ImGui::Text(" (%s)", GameList::RegionToString(selected_entry->region));
ImGui::Text(" (%s)", GameList::RegionToString(selected_entry->region, true));
}
// compatibility
@@ -6576,7 +6579,7 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size)
DrawSvgTexture(s_game_compatibility_textures[static_cast<u32>(selected_entry->compatibility_rating) - 1].get(), LayoutScale(64.0f, 16.0f));
ImGui::SameLine();
}
ImGui::Text(" (%s)", GameList::EntryCompatibilityRatingToString(selected_entry->compatibility_rating));
ImGui::Text(" (%s)", GameList::EntryCompatibilityRatingToString(selected_entry->compatibility_rating, true));
// play time
ImGui::TextUnformatted(
@@ -7302,7 +7305,7 @@ void FullscreenUI::DrawAchievementsSettingsPage(std::unique_lock<std::mutex>& se
};
OpenFileSelector(FSUI_ICONSTR(ICON_FA_FOLDER_OPEN, "Select Leaderboard Submit Sound"), false, std::move(callback), GetAudioFileFilters());
}
MenuHeading(FSUI_CSTR("Account"));
if (bsi->ContainsValue("Achievements", "Token"))
{
@@ -7897,10 +7900,10 @@ TRANSLATE_NOOP("FullscreenUI", "Emulation Settings");
TRANSLATE_NOOP("FullscreenUI", "Graphics Settings");
TRANSLATE_NOOP("FullscreenUI", "Audio Settings");
TRANSLATE_NOOP("FullscreenUI", "Memory Card Settings");
TRANSLATE_NOOP("FullscreenUI", "Folder Settings");
TRANSLATE_NOOP("FullscreenUI", "Achievements Settings");
TRANSLATE_NOOP("FullscreenUI", "Controller Settings");
TRANSLATE_NOOP("FullscreenUI", "Hotkey Settings");
TRANSLATE_NOOP("FullscreenUI", "Achievements Settings");
TRANSLATE_NOOP("FullscreenUI", "Folder Settings");
TRANSLATE_NOOP("FullscreenUI", "Advanced Settings");
TRANSLATE_NOOP("FullscreenUI", "Patches");
TRANSLATE_NOOP("FullscreenUI", "Cheats");

View File

@@ -394,6 +394,8 @@ __ri void ImGuiManager::DrawSettingsOverlay(float scale, float margin, float spa
APPEND("IVU ");
if (EmuConfig.Speedhacks.vuThread)
APPEND("MTVU ");
if (EmuConfig.GS.VsyncEnable)
APPEND("VSYNC ");
APPEND("EER={} EEC={} VUR={} VUC={} VQS={} ", static_cast<unsigned>(EmuConfig.Cpu.FPUFPCR.GetRoundMode()),
EmuConfig.Cpu.Recompiler.GetEEClampMode(), static_cast<unsigned>(EmuConfig.Cpu.VU0FPCR.GetRoundMode()),

View File

@@ -1615,7 +1615,7 @@ void InputManager::CloseSources()
{
for (u32 i = FIRST_EXTERNAL_INPUT_SOURCE; i < LAST_EXTERNAL_INPUT_SOURCE; i++)
{
if (s_input_sources[i]->IsInitialized())
if (s_input_sources[i] && s_input_sources[i]->IsInitialized())
{
s_input_sources[i]->Shutdown();
}

View File

@@ -190,7 +190,7 @@ static void execI()
#if 0
static long int print_me = 0;
// Based on cycle
// if( cpuRegs.cycle > 0x4f24d714 )
// if ( cpuRegs.cycle > 0x4f24d714 )
// Or dump from a particular PC (useful to debug handler/syscall)
if (pc == 0x80000000)
{

View File

@@ -20,7 +20,7 @@
#define G_GC(reg)
#endif
#define SUM_FLAG if(gteFLAG & 0x7F87E000) gteFLAG |= 0x80000000;
#define SUM_FLAG if (gteFLAG & 0x7F87E000) gteFLAG |= 0x80000000;
#ifdef _MSC_VER_
#pragma warning(disable:4244)

View File

@@ -356,7 +356,7 @@ void ba0W16(u32 mem, u16 value)
s_ba_command_executing = true;
s_ba_error_detected = false;
}
else if(s_ba[0x2] == 0x42) // Read Mode
else if (s_ba[0x2] == 0x42) // Read Mode
{
int size = (s_ba[masked_mem] & 0xF);
@@ -406,7 +406,7 @@ u16 ba0R16(u32 mem)
void MyMemCheck(u32 mem)
{
if( mem == 0x1c02f2a0 )
if ( mem == 0x1c02f2a0 )
Console.WriteLn("yo; (mem == 0x1c02f2a0) in MyMemCheck...");
}

View File

@@ -1898,6 +1898,7 @@ Pcsx2Config::Pcsx2Config()
UseSavestateSelector = true;
BackupSavestate = true;
WarnAboutUnsafeSettings = true;
EnableDiscordPresence = false;
ManuallySetRealTimeClock = false;
// To be moved to FileMemoryCard pluign (someday)
@@ -2083,17 +2084,16 @@ void Pcsx2Config::ClearInvalidPerGameConfiguration(SettingsInterface* si)
void EmuFolders::SetAppRoot()
{
std::string program_path = FileSystem::GetProgramPath();
Console.WriteLnFmt("Program Path: {}", program_path);
AppRoot = Path::Canonicalize(Path::GetDirectory(program_path));
#ifdef __APPLE__
const auto bundle_path = CocoaTools::GetNonTranslocatedBundlePath();
if (bundle_path.has_value())
{
// On macOS, override with the bundle path if launched from a bundle.
program_path = bundle_path.value();
AppRoot = StringUtil::EndsWithNoCase(*bundle_path, ".app") ? Path::GetDirectory(*bundle_path) : *bundle_path;
}
#endif
Console.WriteLnFmt("Program Path: {}", program_path);
AppRoot = Path::Canonicalize(Path::GetDirectory(program_path));
// logging of directories in case something goes wrong super early
Console.WriteLnFmt("AppRoot Directory: {}", AppRoot);
@@ -2110,8 +2110,10 @@ bool EmuFolders::SetResourcesDirectory()
#endif
#else
// On macOS, this is in the bundle resources directory.
const std::string program_path = FileSystem::GetProgramPath();
Resources = Path::Canonicalize(Path::Combine(Path::GetDirectory(program_path), "../Resources"));
if (auto resources = CocoaTools::GetResourcePath())
Resources = *resources;
else
Resources = Path::Combine(AppRoot, "resources");
#endif
Console.WriteLnFmt("Resources Directory: {}", Resources);

View File

@@ -256,7 +256,7 @@ void iopTestIntc()
// Note: No need to set the iop's branch delta here, since the EE
// will run an IOP branch test regardless.
}
else if( !iopEventTestIsActive )
else if ( !iopEventTestIsActive )
psxSetNextBranchDelta( 2 );
}

View File

@@ -127,9 +127,9 @@ __ri void cpuException(u32 code, u32 bd)
cpuUpdateOperationMode();
return;
}
else if((code & 0x38000) == 0x10000)
else if ((code & 0x38000) == 0x10000)
offset = 0x80; //Performance Counter
else if((code & 0x38000) == 0x18000)
else if ((code & 0x38000) == 0x18000)
offset = 0x100; //Debug
else
Console.Error("Unknown Level 2 Exception!! Cause %x", code);

View File

@@ -887,6 +887,7 @@ std::vector<AvailableMcdInfo> FileMcd_GetAvailableCards(bool include_in_use_card
}
}
std::sort(mcds.begin(), mcds.end(), [](auto& a, auto& b) { return a.name < b.name; });
return mcds;
}

View File

@@ -1117,7 +1117,7 @@ static void RegWrite_Core(u16 value)
if (!thiscore.DmaMode && !(thiscore.Regs.STATX & 0x400))
thiscore.Regs.STATX &= ~0x80;
else if(!oldDmaMode && thiscore.DmaMode)
else if (!oldDmaMode && thiscore.DmaMode)
thiscore.Regs.STATX |= 0x80;
thiscore.ActiveTSA = thiscore.TSA;

View File

@@ -25,7 +25,7 @@ enum class FreezeAction
// [SAVEVERSION+]
// This informs the auto updater that the users savestates will be invalidated.
static const u32 g_SaveVersion = (0x9A53 << 16) | 0x0000;
static const u32 g_SaveVersion = (0x9A54 << 16) | 0x0000;
// the freezing data between submodules and core

View File

@@ -3,4 +3,4 @@
/// Version number for GS and other shaders. Increment whenever any of the contents of the
/// shaders change, to invalidate the cache.
static constexpr u32 SHADER_CACHE_VERSION = 65;
static constexpr u32 SHADER_CACHE_VERSION = 66;

View File

@@ -164,7 +164,7 @@ static int jo_processDU(jo_bits_t *bits, float A[64], const unsigned char htdc[9
jo_writeBits(bits, run, 6);
if (AC < -127) {
jo_writeBits(bits, 128, 12);
} else if(AC > 127) {
} else if (AC > 127) {
jo_writeBits(bits, 0, 12);
}
code = AC & 0xFFF;

View File

@@ -879,12 +879,10 @@ static __fi u32 floatToInt(u32 uvalue)
float fvalue = std::bit_cast<float>(uvalue);
if (Offset)
fvalue *= std::bit_cast<float>(0x3f800000 + (Offset << 23));
s32 svalue = std::bit_cast<s32>(fvalue);
uvalue = std::bit_cast<u32>(fvalue);
if (svalue >= static_cast<s32>(0x4f000000))
return 0x7fffffff;
else if (svalue <= static_cast<s32>(0xcf000000))
return 0x80000000;
if ((uvalue & 0x7f800000) >= 0x4f000000)
return (uvalue & 0x80000000) ? 0x80000000 : 0x7fffffff;
else
return static_cast<u32>(static_cast<s32>(fvalue));
}

View File

@@ -93,7 +93,7 @@ _vifT static __fi bool vifTransfer(u32 *data, int size, bool TTE) {
if(!vifXch.qwc)
vifX.inprogress &= ~0x1;
else if(vifX.irqoffset.value != 0)
else if (vifX.irqoffset.value != 0)
vifX.irqoffset.enabled = true;
}
else

View File

@@ -70,12 +70,12 @@ mem8_t iopHwRead8_Page1( u32 addr )
DevCon.Warning( "HwRead8 from Counter16 [ignored] @ 0x%08x = 0x%02x", addr, psxHu8(addr) );
ret = psxHu8( addr );
}
else if( masked_addr >= 0x480 && masked_addr < 0x4a0 )
else if ( masked_addr >= 0x480 && masked_addr < 0x4a0 )
{
DevCon.Warning( "HwRead8 from Counter32 [ignored] @ 0x%08x = 0x%02x", addr, psxHu8(addr) );
ret = psxHu8( addr );
}
else if( (masked_addr >= pgmsk(HW_USB_START)) && (masked_addr < pgmsk(HW_USB_END)) )
else if ( (masked_addr >= pgmsk(HW_USB_START)) && (masked_addr < pgmsk(HW_USB_END)) )
{
ret = USBread8( addr );
PSXHW_LOG( "HwRead8 from USB @ 0x%08x = 0x%02x", addr, ret );
@@ -179,7 +179,7 @@ static __fi T _HwRead_16or32_Page1( u32 addr )
// ------------------------------------------------------------------------
// Counters, 32-bit varieties!
//
else if( masked_addr >= 0x480 && masked_addr < 0x4b0 )
else if ( masked_addr >= 0x480 && masked_addr < 0x4b0 )
{
int cntidx = (( masked_addr >> 4 ) & 0xf) - 5;
switch( masked_addr & 0xf )
@@ -218,14 +218,14 @@ static __fi T _HwRead_16or32_Page1( u32 addr )
// ------------------------------------------------------------------------
// USB, with both 16 and 32 bit interfaces
//
else if( (masked_addr >= pgmsk(HW_USB_START)) && (masked_addr < pgmsk(HW_USB_END)) )
else if ( (masked_addr >= pgmsk(HW_USB_START)) && (masked_addr < pgmsk(HW_USB_END)) )
{
ret = (sizeof(T) == 2) ? USBread16( addr ) : USBread32( addr );
}
// ------------------------------------------------------------------------
// SPU2, accessible in 16 bit mode only!
//
else if( masked_addr >= pgmsk(HW_SPU2_START) && masked_addr < pgmsk(HW_SPU2_END) )
else if ( masked_addr >= pgmsk(HW_SPU2_START) && masked_addr < pgmsk(HW_SPU2_END) )
{
if( sizeof(T) == 2 )
ret = SPU2read( addr );
@@ -238,7 +238,7 @@ static __fi T _HwRead_16or32_Page1( u32 addr )
// ------------------------------------------------------------------------
// PS1 GPU access
//
else if( (masked_addr >= pgmsk(HW_PS1_GPU_START)) && (masked_addr < pgmsk(HW_PS1_GPU_END)) )
else if ( (masked_addr >= pgmsk(HW_PS1_GPU_START)) && (masked_addr < pgmsk(HW_PS1_GPU_END)) )
{
// todo: psx mode: this is new
if( sizeof(T) == 2 )
@@ -418,7 +418,7 @@ mem32_t iopHwRead32_Page8( u32 addr )
ret = g_Sio2.send3[parm];
Sio2Log.WriteLn("%s(%08X) SIO2 SEND3 Read (%08X)", __FUNCTION__, addr, ret);
}
else if( masked_addr < 0x260 )
else if ( masked_addr < 0x260 )
{
// SIO2 Send commands alternate registers. First reg maps to Send1, second
// to Send2, third to Send1, etc. And the following clever code does this:
@@ -426,7 +426,7 @@ mem32_t iopHwRead32_Page8( u32 addr )
ret = (masked_addr & 4) ? g_Sio2.send2[parm] : g_Sio2.send1[parm];
Sio2Log.WriteLn("%s(%08X) SIO2 SEND1/2 Read (%08X)", __FUNCTION__, addr, ret);
}
else if( masked_addr <= 0x280 )
else if ( masked_addr <= 0x280 )
{
switch( masked_addr )
{
@@ -471,7 +471,7 @@ mem32_t iopHwRead32_Page8( u32 addr )
break;
}
}
else if( masked_addr >= pgmsk(HW_FW_START) && masked_addr <= pgmsk(HW_FW_END) )
else if ( masked_addr >= pgmsk(HW_FW_START) && masked_addr <= pgmsk(HW_FW_END) )
{
ret = FWread32( addr );
} else {

View File

@@ -104,12 +104,12 @@ void iopHwWrite8_Page1( u32 addr, mem8_t val )
DbgCon.Warning( "HwWrite8 to Counter16 [ignored] @ addr 0x%08x = 0x%02x", addr, psxHu8(addr) );
psxHu8( addr ) = val;
}
else if( masked_addr >= 0x480 && masked_addr < 0x4a0 )
else if ( masked_addr >= 0x480 && masked_addr < 0x4a0 )
{
DbgCon.Warning( "HwWrite8 to Counter32 [ignored] @ addr 0x%08x = 0x%02x", addr, psxHu8(addr) );
psxHu8( addr ) = val;
}
else if( (masked_addr >= pgmsk(HW_USB_START)) && (masked_addr < pgmsk(HW_USB_END)) )
else if ( (masked_addr >= pgmsk(HW_USB_START)) && (masked_addr < pgmsk(HW_USB_END)) )
{
USBwrite8( addr, val );
}
@@ -219,7 +219,7 @@ static __fi void _HwWrite_16or32_Page1( u32 addr, T val )
// ------------------------------------------------------------------------
// Counters, 32-bit varieties!
//
else if( masked_addr >= 0x480 && masked_addr < 0x4b0 )
else if ( masked_addr >= 0x480 && masked_addr < 0x4b0 )
{
int cntidx = (( masked_addr >> 4 ) & 0xf) - 5;
switch( masked_addr & 0xf )
@@ -252,14 +252,14 @@ static __fi void _HwWrite_16or32_Page1( u32 addr, T val )
// ------------------------------------------------------------------------
// USB, with both 16 and 32 bit interfaces
//
else if( (masked_addr >= pgmsk(HW_USB_START)) && (masked_addr < pgmsk(HW_USB_END)) )
else if ( (masked_addr >= pgmsk(HW_USB_START)) && (masked_addr < pgmsk(HW_USB_END)) )
{
if( sizeof(T) == 2 ) USBwrite16( addr, val ); else USBwrite32( addr, val );
}
// ------------------------------------------------------------------------
// SPU2, accessible in 16 bit mode only!
//
else if( (masked_addr >= pgmsk(HW_SPU2_START)) && (masked_addr < pgmsk(HW_SPU2_END)) )
else if ( (masked_addr >= pgmsk(HW_SPU2_START)) && (masked_addr < pgmsk(HW_SPU2_END)) )
{
if( sizeof(T) == 2 )
SPU2write( addr, val );
@@ -272,7 +272,7 @@ static __fi void _HwWrite_16or32_Page1( u32 addr, T val )
// ------------------------------------------------------------------------
// PS1 GPU access
//
else if( (masked_addr >= pgmsk(HW_PS1_GPU_START)) && (masked_addr < pgmsk(HW_PS1_GPU_END)) )
else if ( (masked_addr >= pgmsk(HW_PS1_GPU_START)) && (masked_addr < pgmsk(HW_PS1_GPU_END)) )
{
// todo: psx mode: this is new
if( sizeof(T) == 2 )
@@ -599,7 +599,7 @@ void iopHwWrite32_Page8( u32 addr, mem32_t val )
const int parm = (masked_addr - 0x200) / 4;
g_Sio2.SetSend3(parm, val);
}
else if( masked_addr < 0x260 )
else if ( masked_addr < 0x260 )
{
// SIO2 Send commands alternate registers. First reg maps to Send1, second
// to Send2, third to Send1, etc. And the following clever code does this:
@@ -617,7 +617,7 @@ void iopHwWrite32_Page8( u32 addr, mem32_t val )
g_Sio2.send1[parm] = val;
}
}
else if( masked_addr <= 0x280 )
else if ( masked_addr <= 0x280 )
{
switch( masked_addr )
{
@@ -661,7 +661,7 @@ void iopHwWrite32_Page8( u32 addr, mem32_t val )
break;
}
}
else if( masked_addr >= pgmsk(HW_FW_START) && masked_addr <= pgmsk(HW_FW_END) )
else if ( masked_addr >= pgmsk(HW_FW_START) && masked_addr <= pgmsk(HW_FW_END) )
{
FWwrite32( addr, val );
}

View File

@@ -161,7 +161,7 @@ static __ri const char* _ioplog_GetHwName( u32 addr, T val )
default: return "Invalid Counter";
}
}
else if( addr >= 0x1f801480 && addr < 0x1f8014b0 )
else if ( addr >= 0x1f801480 && addr < 0x1f8014b0 )
{
switch( addr & 0xf )
{
@@ -174,20 +174,20 @@ static __ri const char* _ioplog_GetHwName( u32 addr, T val )
default: return "Invalid Counter";
}
}
else if( (addr >= HW_USB_START) && (addr < HW_USB_END) )
else if ( (addr >= HW_USB_START) && (addr < HW_USB_END) )
{
return "USB";
}
else if( (addr >= HW_SPU2_START) && (addr < HW_SPU2_END) )
else if ( (addr >= HW_SPU2_START) && (addr < HW_SPU2_END) )
{
return "SPU2";
}
else if( addr >= pgmsk(HW_FW_START) && addr <= pgmsk(HW_FW_END) )
else if ( addr >= pgmsk(HW_FW_START) && addr <= pgmsk(HW_FW_END) )
{
return "FW";
}
else if( addr >= 0x1f808200 && addr < 0x1f808240 ) { return "SIO2 param"; }
else if( addr >= 0x1f808240 && addr < 0x1f808260 ) { return "SIO2 send"; }
else if ( addr >= 0x1f808200 && addr < 0x1f808240 ) { return "SIO2 param"; }
else if ( addr >= 0x1f808240 && addr < 0x1f808260 ) { return "SIO2 send"; }
return NULL; //"Unknown";
}

View File

@@ -648,7 +648,7 @@ static OperandType vtlbUnmappedPReadSm(u32 addr) {
}
return 0;
}
static RETURNS_R128 vtlbUnmappedPReadLg(u32 addr) { vtlb_BusError(addr, 0); if(!CHECK_EEREC && CHECK_CACHE && CheckCache(addr)){ return readCache128(addr, false); } return r128_zero(); }
static RETURNS_R128 vtlbUnmappedPReadLg(u32 addr) { vtlb_BusError(addr, 0); if (!CHECK_EEREC && CHECK_CACHE && CheckCache(addr)){ return readCache128(addr, false); } return r128_zero(); }
template <typename OperandType>
static void vtlbUnmappedPWriteSm(u32 addr, OperandType data) {
@@ -663,7 +663,7 @@ static void vtlbUnmappedPWriteSm(u32 addr, OperandType data) {
}
}
}
static void TAKES_R128 vtlbUnmappedPWriteLg(u32 addr, r128 data) { vtlb_BusError(addr, 1); if(!CHECK_EEREC && CHECK_CACHE && CheckCache(addr)) { writeCache128(addr, reinterpret_cast<mem128_t*>(&data) /*Safe??*/, false); }}
static void TAKES_R128 vtlbUnmappedPWriteLg(u32 addr, r128 data) { vtlb_BusError(addr, 1); if (!CHECK_EEREC && CHECK_CACHE && CheckCache(addr)) { writeCache128(addr, reinterpret_cast<mem128_t*>(&data) /*Safe??*/, false); }}
// clang-format on
// --------------------------------------------------------------------------------------

Some files were not shown because too many files have changed in this diff Show More