mirror of
https://github.com/PCSX2/pcsx2.git
synced 2026-01-31 01:15:24 +01:00
Compare commits
51 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b6930c10b9 | ||
|
|
852734580d | ||
|
|
d1dc6a9c1d | ||
|
|
4fa6d3ed3f | ||
|
|
92e190ad6c | ||
|
|
da4fcffef4 | ||
|
|
1a5731dd8e | ||
|
|
e764c5cd4e | ||
|
|
e23b247947 | ||
|
|
3d7792436f | ||
|
|
d8187fbea4 | ||
|
|
02259ad0a5 | ||
|
|
220a68df9a | ||
|
|
2ced24f69e | ||
|
|
ec91d0dc74 | ||
|
|
46874f4673 | ||
|
|
9eac47dc6c | ||
|
|
9e3fd5c2e0 | ||
|
|
ae4be6e2b1 | ||
|
|
434df49a7d | ||
|
|
c939c0fcd5 | ||
|
|
952c39f324 | ||
|
|
0fea7e2a70 | ||
|
|
c1baab68d0 | ||
|
|
ccef18f7a9 | ||
|
|
8cb056bde3 | ||
|
|
6ecaaee9e0 | ||
|
|
c5f916bda0 | ||
|
|
52a9a4649c | ||
|
|
d2219b4dbd | ||
|
|
155f603245 | ||
|
|
cb7630a6ab | ||
|
|
284fba1ce3 | ||
|
|
aa4bd6c88c | ||
|
|
623993930b | ||
|
|
dbf2c854c6 | ||
|
|
25351bc05c | ||
|
|
27cc5f499c | ||
|
|
b7c2f39a17 | ||
|
|
3541c1ccf8 | ||
|
|
7a05738d11 | ||
|
|
686220ae0c | ||
|
|
cf380d36b9 | ||
|
|
50bc0193ac | ||
|
|
2162a72831 | ||
|
|
313666f85b | ||
|
|
e9d79263b4 | ||
|
|
4ede6d65fd | ||
|
|
14d2eee371 | ||
|
|
717f370be0 | ||
|
|
d05e4b9727 |
@@ -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": [
|
||||
|
||||
@@ -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": [
|
||||
|
||||
@@ -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()
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
@@ -1940,11 +1940,13 @@ SCAJ-20115:
|
||||
name: "Yoshitsune Eiyuuden"
|
||||
region: "NTSC-Unk"
|
||||
SCAJ-20116:
|
||||
name: "Death by Degrees - Tekken - Nina Williams"
|
||||
region: "NTSC-C-J"
|
||||
name: "戰慄殺機 鐵拳 - 妮娜 - 威廉斯"
|
||||
name-en: "Death by Degrees"
|
||||
region: "NTSC-C"
|
||||
gsHWFixes:
|
||||
alignSprite: 1 # Fixes FMV lines.
|
||||
getSkipCount: "GSC_NamcoGames"
|
||||
halfPixelOffset: 5 # Fixes alignment of shuffles and post processing.
|
||||
alignSprite: 1 # Fixes FMV lines.
|
||||
textureInsideRT: 1 # Fixes post shuffles.
|
||||
SCAJ-20117:
|
||||
name: "Fu-un Bakumatsu-den"
|
||||
@@ -5763,8 +5765,9 @@ SCES-52586:
|
||||
region: "PAL-E-S"
|
||||
compat: 5
|
||||
gsHWFixes:
|
||||
alignSprite: 1 # Fixes FMV lines.
|
||||
getSkipCount: "GSC_NamcoGames"
|
||||
halfPixelOffset: 5 # Fixes alignment of shuffles and post processing.
|
||||
alignSprite: 1 # Fixes FMV lines.
|
||||
textureInsideRT: 1 # Fixes post shuffles.
|
||||
SCES-52596:
|
||||
name: "This is Football 2005"
|
||||
@@ -5865,16 +5868,18 @@ SCES-53053:
|
||||
name: "Death by Degrees"
|
||||
region: "PAL-F-I"
|
||||
gsHWFixes:
|
||||
alignSprite: 1 # Fixes FMV lines.
|
||||
getSkipCount: "GSC_NamcoGames"
|
||||
halfPixelOffset: 5 # Fixes alignment of shuffles and post processing.
|
||||
alignSprite: 1 # Fixes FMV lines.
|
||||
textureInsideRT: 1 # Fixes post shuffles.
|
||||
SCES-53054:
|
||||
name: "Death by Degrees"
|
||||
region: "PAL-E-G"
|
||||
compat: 5
|
||||
gsHWFixes:
|
||||
alignSprite: 1 # Fixes FMV lines.
|
||||
getSkipCount: "GSC_NamcoGames"
|
||||
halfPixelOffset: 5 # Fixes alignment of shuffles and post processing.
|
||||
alignSprite: 1 # Fixes FMV lines.
|
||||
textureInsideRT: 1 # Fixes post shuffles.
|
||||
SCES-53055:
|
||||
name: "Eyetoy - Antigrav"
|
||||
@@ -7309,8 +7314,9 @@ SCKA-20039:
|
||||
name: "Tekken Nina Williams In Death By Degree"
|
||||
region: "NTSC-K"
|
||||
gsHWFixes:
|
||||
getSkipCount: "GSC_NamcoGames"
|
||||
halfPixelOffset: 5 # Fixes alignment of shuffles and post processing.
|
||||
alignSprite: 1 # Fixes FMV lines.
|
||||
halfPixelOffset: 2 # Aligns post effects.
|
||||
textureInsideRT: 1 # Fixes post shuffles.
|
||||
SCKA-20040:
|
||||
name: "Jak 3"
|
||||
@@ -12921,7 +12927,7 @@ SLAJ-25041:
|
||||
name: "Harry Potter and the Prisoner of Azkaban"
|
||||
region: "NTSC-C"
|
||||
gsHWFixes:
|
||||
cpuFramebufferConversion: 1 # Fixes right side of the screen from garbage textures.
|
||||
textureInsideRT: 1 # Fixes right side shadow lookup.
|
||||
halfPixelOffset: 2 # Aligns mirror reflections, removes some bloom-related ghosting.
|
||||
recommendedBlendingLevel: 4 # Improves lighting effects.
|
||||
SLAJ-25042:
|
||||
@@ -14276,8 +14282,6 @@ SLES-50078:
|
||||
SLES-50079:
|
||||
name: "Armored Core 2"
|
||||
region: "PAL-E"
|
||||
gameFixes:
|
||||
- SoftwareRendererFMVHack # Fixes random corruption.
|
||||
clampModes:
|
||||
eeClampMode: 2 # Fixes Abnormal AI behavior.
|
||||
gsHWFixes:
|
||||
@@ -16825,7 +16829,7 @@ SLES-51192:
|
||||
- EETimingHack # Fixes flickering textures.
|
||||
gsHWFixes:
|
||||
recommendedBlendingLevel: 4 # Fixes missing lighting effects.
|
||||
cpuFramebufferConversion: 1 # Fixes right side of the screen from garbage textures.
|
||||
textureInsideRT: 1 # Fixes right side shadow lookup.
|
||||
halfPixelOffset: 2 # Fixes misaligned lighting and other effects.
|
||||
texturePreloading: 0 # Performs better on certain areas like mid-game in and around the castle as the hash goes easily to 200 MB.
|
||||
SLES-51193:
|
||||
@@ -16835,7 +16839,7 @@ SLES-51193:
|
||||
- EETimingHack # Fixes flickering textures.
|
||||
gsHWFixes:
|
||||
recommendedBlendingLevel: 4 # Fixes missing lighting effects.
|
||||
cpuFramebufferConversion: 1 # Fixes right side of the screen from garbage textures.
|
||||
textureInsideRT: 1 # Fixes right side shadow lookup.
|
||||
halfPixelOffset: 2 # Fixes misaligned lighting and other effects.
|
||||
texturePreloading: 0 # Performs better on certain areas like mid-game in and around the castle as the hash goes easily to 200 MB.
|
||||
SLES-51194:
|
||||
@@ -16845,7 +16849,7 @@ SLES-51194:
|
||||
- EETimingHack # Fixes flickering textures.
|
||||
gsHWFixes:
|
||||
recommendedBlendingLevel: 4 # Fixes missing lighting effects.
|
||||
cpuFramebufferConversion: 1 # Fixes right side of the screen from garbage textures.
|
||||
textureInsideRT: 1 # Fixes right side shadow lookup.
|
||||
halfPixelOffset: 2 # Fixes misaligned lighting and other effects.
|
||||
texturePreloading: 0 # Performs better on certain areas like mid-game in and around the castle as the hash goes easily to 200 MB.
|
||||
SLES-51195:
|
||||
@@ -16855,7 +16859,7 @@ SLES-51195:
|
||||
- EETimingHack # Fixes flickering textures.
|
||||
gsHWFixes:
|
||||
recommendedBlendingLevel: 4 # Fixes missing lighting effects.
|
||||
cpuFramebufferConversion: 1 # Fixes right side of the screen from garbage textures.
|
||||
textureInsideRT: 1 # Fixes right side shadow lookup.
|
||||
halfPixelOffset: 2 # Fixes misaligned lighting and other effects.
|
||||
texturePreloading: 0 # Performs better on certain areas like mid-game in and around the castle as the hash goes easily to 200 MB.
|
||||
SLES-51196:
|
||||
@@ -16865,7 +16869,7 @@ SLES-51196:
|
||||
- EETimingHack # Fixes flickering textures.
|
||||
gsHWFixes:
|
||||
recommendedBlendingLevel: 4 # Fixes missing lighting effects.
|
||||
cpuFramebufferConversion: 1 # Fixes right side of the screen from garbage textures.
|
||||
textureInsideRT: 1 # Fixes right side shadow lookup.
|
||||
halfPixelOffset: 2 # Fixes misaligned lighting and other effects.
|
||||
texturePreloading: 0 # Performs better on certain areas like mid-game in and around the castle as the hash goes easily to 200 MB.
|
||||
SLES-51197:
|
||||
@@ -16922,7 +16926,7 @@ SLES-51214:
|
||||
- EETimingHack # Fixes flickering textures.
|
||||
gsHWFixes:
|
||||
recommendedBlendingLevel: 4 # Fixes missing lighting effects.
|
||||
cpuFramebufferConversion: 1 # Fixes right side of the screen from garbage textures.
|
||||
textureInsideRT: 1 # Fixes right side shadow lookup.
|
||||
halfPixelOffset: 2 # Fixes misaligned lighting and other effects.
|
||||
texturePreloading: 0 # Performs better on certain areas like mid-game in and around the castle as the hash goes easily to 200 MB.
|
||||
SLES-51215:
|
||||
@@ -16932,7 +16936,7 @@ SLES-51215:
|
||||
- EETimingHack # Fixes flickering textures.
|
||||
gsHWFixes:
|
||||
recommendedBlendingLevel: 4 # Fixes missing lighting effects.
|
||||
cpuFramebufferConversion: 1 # Fixes right side of the screen from garbage textures.
|
||||
textureInsideRT: 1 # Fixes right side shadow lookup.
|
||||
halfPixelOffset: 2 # Fixes misaligned lighting and other effects.
|
||||
texturePreloading: 0 # Performs better on certain areas like mid-game in and around the castle as the hash goes easily to 200 MB.
|
||||
SLES-51216:
|
||||
@@ -16942,7 +16946,7 @@ SLES-51216:
|
||||
- EETimingHack # Fixes flickering textures.
|
||||
gsHWFixes:
|
||||
recommendedBlendingLevel: 4 # Fixes missing lighting effects.
|
||||
cpuFramebufferConversion: 1 # Fixes right side of the screen from garbage textures.
|
||||
textureInsideRT: 1 # Fixes right side shadow lookup.
|
||||
halfPixelOffset: 2 # Fixes misaligned lighting and other effects.
|
||||
texturePreloading: 0 # Performs better on certain areas like mid-game in and around the castle as the hash goes easily to 200 MB.
|
||||
SLES-51217:
|
||||
@@ -16952,7 +16956,7 @@ SLES-51217:
|
||||
- EETimingHack # Fixes flickering textures.
|
||||
gsHWFixes:
|
||||
recommendedBlendingLevel: 4 # Fixes missing lighting effects.
|
||||
cpuFramebufferConversion: 1 # Fixes right side of the screen from garbage textures.
|
||||
textureInsideRT: 1 # Fixes right side shadow lookup.
|
||||
halfPixelOffset: 2 # Fixes misaligned lighting and other effects.
|
||||
texturePreloading: 0 # Performs better on certain areas like mid-game in and around the castle as the hash goes easily to 200 MB.
|
||||
SLES-51218:
|
||||
@@ -16962,7 +16966,7 @@ SLES-51218:
|
||||
- EETimingHack # Fixes flickering textures.
|
||||
gsHWFixes:
|
||||
recommendedBlendingLevel: 4 # Fixes missing lighting effects.
|
||||
cpuFramebufferConversion: 1 # Fixes right side of the screen from garbage textures.
|
||||
textureInsideRT: 1 # Fixes right side shadow lookup.
|
||||
halfPixelOffset: 2 # Fixes misaligned lighting and other effects.
|
||||
texturePreloading: 0 # Performs better on certain areas like mid-game in and around the castle as the hash goes easily to 200 MB.
|
||||
SLES-51219:
|
||||
@@ -16972,7 +16976,7 @@ SLES-51219:
|
||||
- EETimingHack # Fixes flickering textures.
|
||||
gsHWFixes:
|
||||
recommendedBlendingLevel: 4 # Fixes missing lighting effects.
|
||||
cpuFramebufferConversion: 1 # Fixes right side of the screen from garbage textures.
|
||||
textureInsideRT: 1 # Fixes right side shadow lookup.
|
||||
halfPixelOffset: 2 # Fixes misaligned lighting and other effects.
|
||||
texturePreloading: 0 # Performs better on certain areas like mid-game in and around the castle as the hash goes easily to 200 MB.
|
||||
SLES-51220:
|
||||
@@ -19892,7 +19896,7 @@ SLES-52440:
|
||||
region: "PAL-M7"
|
||||
compat: 3
|
||||
gsHWFixes:
|
||||
cpuFramebufferConversion: 1 # Fixes right side of the screen from garbage textures.
|
||||
textureInsideRT: 1 # Fixes right side shadow lookup.
|
||||
halfPixelOffset: 2 # Aligns mirror reflections, removes some bloom-related ghosting.
|
||||
recommendedBlendingLevel: 4 # Improves lighting effects.
|
||||
SLES-52444:
|
||||
@@ -20119,7 +20123,7 @@ SLES-52527:
|
||||
name: "Harry Potter og Fangen fra Azkaban"
|
||||
region: "PAL-M4"
|
||||
gsHWFixes:
|
||||
cpuFramebufferConversion: 1 # Fixes right side of the screen from garbage textures.
|
||||
textureInsideRT: 1 # Fixes right side shadow lookup.
|
||||
halfPixelOffset: 2 # Aligns mirror reflections, removes some bloom-related ghosting.
|
||||
recommendedBlendingLevel: 4 # Improves lighting effects.
|
||||
SLES-52531:
|
||||
@@ -20430,7 +20434,7 @@ SLES-52600:
|
||||
name: "Harry Potter and the Prisoner of Azkaban"
|
||||
region: "PAL-PL"
|
||||
gsHWFixes:
|
||||
cpuFramebufferConversion: 1 # Fixes right side of the screen from garbage textures.
|
||||
textureInsideRT: 1 # Fixes right side shadow lookup.
|
||||
halfPixelOffset: 2 # Aligns mirror reflections, removes some bloom-related ghosting.
|
||||
recommendedBlendingLevel: 4 # Improves lighting effects.
|
||||
SLES-52601:
|
||||
@@ -25934,6 +25938,8 @@ SLES-54364:
|
||||
SLES-54365:
|
||||
name: "AMF Xtreme Bowling 2006"
|
||||
region: "PAL-M6"
|
||||
gsHWFixes:
|
||||
textureInsideRT: 1 # Fixes half right.
|
||||
SLES-54366:
|
||||
name: "David Douillet Judo"
|
||||
region: "PAL-NL-F"
|
||||
@@ -31102,7 +31108,7 @@ SLKA-25172:
|
||||
name: "Harry Potter and the Prisoner of Azkaban"
|
||||
region: "NTSC-K"
|
||||
gsHWFixes:
|
||||
cpuFramebufferConversion: 1 # Fixes right side of the screen from garbage textures.
|
||||
textureInsideRT: 1 # Fixes right side shadow lookup.
|
||||
halfPixelOffset: 2 # Aligns mirror reflections, removes some bloom-related ghosting.
|
||||
recommendedBlendingLevel: 4 # Improves lighting effects.
|
||||
SLKA-25173:
|
||||
@@ -34786,8 +34792,9 @@ SLPM-60257:
|
||||
name-en: "Death by Degrees - Tekken - Nina Williams [Trial]"
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
alignSprite: 1 # Fixes FMV lines.
|
||||
getSkipCount: "GSC_NamcoGames"
|
||||
halfPixelOffset: 5 # Fixes alignment of shuffles and post processing.
|
||||
alignSprite: 1 # Fixes FMV lines.
|
||||
textureInsideRT: 1 # Fixes post shuffles.
|
||||
SLPM-60258:
|
||||
name: "THE TYPING OF THE DEAD ZOMBIE PANIC [体験版]"
|
||||
@@ -36950,7 +36957,7 @@ SLPM-62241:
|
||||
- EETimingHack # Fixes flickering textures.
|
||||
gsHWFixes:
|
||||
recommendedBlendingLevel: 4 # Fixes missing lighting effects.
|
||||
cpuFramebufferConversion: 1 # Fixes right side of the screen from garbage textures.
|
||||
textureInsideRT: 1 # Fixes right side shadow lookup.
|
||||
halfPixelOffset: 2 # Fixes misaligned lighting and other effects.
|
||||
SLPM-62242:
|
||||
name: "撞球 ビリヤードマスター2"
|
||||
@@ -38362,7 +38369,7 @@ SLPM-62513:
|
||||
- EETimingHack # Fixes flickering textures.
|
||||
gsHWFixes:
|
||||
recommendedBlendingLevel: 4 # Fixes missing lighting effects.
|
||||
cpuFramebufferConversion: 1 # Fixes right side of the screen from garbage textures.
|
||||
textureInsideRT: 1 # Fixes right side shadow lookup.
|
||||
halfPixelOffset: 2 # Fixes misaligned lighting and other effects.
|
||||
SLPM-62514:
|
||||
name: "シムピープル ~お茶の間劇場~ [EA BEST HITS]"
|
||||
@@ -39793,7 +39800,7 @@ SLPM-64528:
|
||||
- EETimingHack # Fixes flickering textures.
|
||||
gsHWFixes:
|
||||
recommendedBlendingLevel: 4 # Fixes missing lighting effects.
|
||||
cpuFramebufferConversion: 1 # Fixes right side of the screen from garbage textures.
|
||||
textureInsideRT: 1 # Fixes right side shadow lookup.
|
||||
halfPixelOffset: 2 # Fixes misaligned lighting and other effects.
|
||||
SLPM-64532:
|
||||
name: "Digital Holmes"
|
||||
@@ -43348,7 +43355,7 @@ SLPM-65612:
|
||||
name-en: "Harry Potter and the Prisoner of Azkaban"
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
cpuFramebufferConversion: 1 # Fixes right side of the screen from garbage textures.
|
||||
textureInsideRT: 1 # Fixes right side shadow lookup.
|
||||
halfPixelOffset: 2 # Aligns mirror reflections, removes some bloom-related ghosting.
|
||||
recommendedBlendingLevel: 4 # Improves lighting effects.
|
||||
SLPM-65613:
|
||||
@@ -52150,7 +52157,7 @@ SLPM-68005:
|
||||
- EETimingHack # Fixes flickering textures.
|
||||
gsHWFixes:
|
||||
recommendedBlendingLevel: 4 # Fixes missing lighting effects.
|
||||
cpuFramebufferConversion: 1 # Fixes right side of the screen from garbage textures.
|
||||
textureInsideRT: 1 # Fixes right side shadow lookup.
|
||||
halfPixelOffset: 2 # Fixes misaligned lighting and other effects.
|
||||
SLPM-68007:
|
||||
name: "カラオケレボリューション用マイク同梱お試しディスク"
|
||||
@@ -54301,7 +54308,7 @@ SLPS-20234:
|
||||
- EETimingHack # Fixes flickering textures.
|
||||
gsHWFixes:
|
||||
recommendedBlendingLevel: 4 # Fixes missing lighting effects.
|
||||
cpuFramebufferConversion: 1 # Fixes right side of the screen from garbage textures.
|
||||
textureInsideRT: 1 # Fixes right side shadow lookup.
|
||||
halfPixelOffset: 2 # Fixes misaligned lighting and other effects.
|
||||
SLPS-20237:
|
||||
name: "Saikyou Toudai Shougi 3"
|
||||
@@ -55561,7 +55568,8 @@ SLPS-20489:
|
||||
name-en: "Simple 2000 Series Vol. 114 - The Onna Okappichi Torimonochou - Oharu-chan GoGoGo!"
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
getSkipCount: "GSC_Simple2000Vol114"
|
||||
halfPixelOffset: 5 # Fixes DoF alignment.
|
||||
nativeScaling: 1 # Corrects DoF.
|
||||
SLPS-20490:
|
||||
name: "パチスロ倶楽部コレクション アイムジャグラーEX~ジャグラーセレクション~"
|
||||
name-sort: "ぱちすろくらぶこれくしょん あいむじゃぐらーEX じゃぐらーせれくしょん"
|
||||
@@ -55723,8 +55731,6 @@ SLPS-25007:
|
||||
name-sort: "あーまーどこあ 2"
|
||||
name-en: "Armored Core 2"
|
||||
region: "NTSC-J"
|
||||
gameFixes:
|
||||
- SoftwareRendererFMVHack # Fixes random corruption.
|
||||
clampModes:
|
||||
eeClampMode: 2 # Fixes Abnormal AI behavior.
|
||||
gsHWFixes:
|
||||
@@ -58221,8 +58227,9 @@ SLPS-25422:
|
||||
name-en: "Death by Degrees - Tekken - Nina Williams"
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
alignSprite: 1 # Fixes FMV lines.
|
||||
getSkipCount: "GSC_NamcoGames"
|
||||
halfPixelOffset: 5 # Fixes alignment of shuffles and post processing.
|
||||
alignSprite: 1 # Fixes FMV lines.
|
||||
textureInsideRT: 1 # Fixes post shuffles.
|
||||
SLPS-25423:
|
||||
name: "怪盗アプリコット 完全版 [限定版]"
|
||||
@@ -62368,8 +62375,6 @@ SLPS-73403:
|
||||
name-sort: "あーまーどこあ 2 [PlayStation2 the Best]"
|
||||
name-en: "Armored Core 2 [PlayStation2 the Best]"
|
||||
region: "NTSC-J"
|
||||
gameFixes:
|
||||
- SoftwareRendererFMVHack # Fixes random corruption.
|
||||
clampModes:
|
||||
eeClampMode: 2 # Fixes Abnormal AI behavior.
|
||||
gsHWFixes:
|
||||
@@ -62624,8 +62629,6 @@ SLUS-20014:
|
||||
name: "Armored Core 2"
|
||||
region: "NTSC-U"
|
||||
compat: 5
|
||||
gameFixes:
|
||||
- SoftwareRendererFMVHack # Fixes random corruption.
|
||||
clampModes:
|
||||
eeClampMode: 2 # Fixes Abnormal AI behavior.
|
||||
gsHWFixes:
|
||||
@@ -65270,7 +65273,7 @@ SLUS-20576:
|
||||
- EETimingHack # Fixes flickering textures.
|
||||
gsHWFixes:
|
||||
recommendedBlendingLevel: 4 # Fixes missing lighting effects.
|
||||
cpuFramebufferConversion: 1 # Fixes right side of the screen from garbage textures.
|
||||
textureInsideRT: 1 # Fixes right side shadow lookup.
|
||||
halfPixelOffset: 2 # Fixes misaligned lighting and other effects.
|
||||
SLUS-20577:
|
||||
name: "Drome Racers"
|
||||
@@ -67215,7 +67218,7 @@ SLUS-20926:
|
||||
name: "Harry Potter and the Prisoner of Azkaban"
|
||||
region: "NTSC-U"
|
||||
gsHWFixes:
|
||||
cpuFramebufferConversion: 1 # Fixes right side of the screen from garbage textures.
|
||||
textureInsideRT: 1 # Fixes right side shadow lookup.
|
||||
halfPixelOffset: 2 # Aligns mirror reflections, removes some bloom-related ghosting.
|
||||
recommendedBlendingLevel: 4 # Improves lighting effects.
|
||||
SLUS-20927:
|
||||
@@ -67260,8 +67263,9 @@ SLUS-20934:
|
||||
region: "NTSC-U"
|
||||
compat: 5
|
||||
gsHWFixes:
|
||||
alignSprite: 1 # Fixes FMV lines.
|
||||
getSkipCount: "GSC_NamcoGames"
|
||||
halfPixelOffset: 5 # Fixes alignment of shuffles and post processing.
|
||||
alignSprite: 1 # Fixes FMV lines.
|
||||
textureInsideRT: 1 # Fixes post shuffles.
|
||||
SLUS-20935:
|
||||
name: "IHRA Professional Drag Racing 2005"
|
||||
@@ -69943,6 +69947,8 @@ SLUS-21347:
|
||||
name: "AMF Xtreme Bowling"
|
||||
region: "NTSC-U"
|
||||
compat: 5
|
||||
gsHWFixes:
|
||||
textureInsideRT: 1 # Fixes half right.
|
||||
SLUS-21348:
|
||||
name: "Yakuza"
|
||||
region: "NTSC-U"
|
||||
|
||||
@@ -1283,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,
|
||||
@@ -1606,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,
|
||||
@@ -1802,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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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)")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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__
|
||||
|
||||
@@ -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];
|
||||
}}
|
||||
|
||||
@@ -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, ¶ms);
|
||||
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__
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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 = {};
|
||||
@@ -575,10 +581,10 @@ QIcon GameListModel::getIconForRegion(GameList::Region region)
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -138,16 +138,15 @@ namespace
|
||||
if (option.state & QStyle::State_Selected)
|
||||
{
|
||||
// See QItemDelegate::selectedPixmap()
|
||||
QString key = QString::fromStdString(fmt::format("{:016X}-{:d}", pix.cacheKey(), enabled));
|
||||
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);
|
||||
@@ -181,7 +180,7 @@ 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);
|
||||
@@ -559,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, {
|
||||
|
||||
@@ -93,6 +93,7 @@ public Q_SLOTS:
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent* event);
|
||||
bool event(QEvent* event) override;
|
||||
|
||||
private:
|
||||
void loadTableViewColumnVisibilitySettings();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -95,27 +95,27 @@
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Settings/AchievementLoginDialog.cpp" line="25"/>
|
||||
<location filename="../Settings/AchievementLoginDialog.cpp" line="26"/>
|
||||
<source><strong>Your RetroAchievements login token is no longer valid.</strong> You must re-enter your credentials for achievements to be tracked. Your password will not be saved in PCSX2, an access token will be generated and used instead.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Settings/AchievementLoginDialog.cpp" line="30"/>
|
||||
<location filename="../Settings/AchievementLoginDialog.cpp" line="31"/>
|
||||
<source>&Login</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Settings/AchievementLoginDialog.cpp" line="43"/>
|
||||
<location filename="../Settings/AchievementLoginDialog.cpp" line="44"/>
|
||||
<source>Logging in...</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Settings/AchievementLoginDialog.cpp" line="73"/>
|
||||
<location filename="../Settings/AchievementLoginDialog.cpp" line="74"/>
|
||||
<source>Login Error</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Settings/AchievementLoginDialog.cpp" line="74"/>
|
||||
<location filename="../Settings/AchievementLoginDialog.cpp" line="75"/>
|
||||
<source>Login failed.
|
||||
Error: %1
|
||||
|
||||
@@ -123,29 +123,29 @@ Please check your username and password, and try again.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Settings/AchievementLoginDialog.cpp" line="75"/>
|
||||
<location filename="../Settings/AchievementLoginDialog.cpp" line="76"/>
|
||||
<source>Login failed.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Settings/AchievementLoginDialog.cpp" line="83"/>
|
||||
<location filename="../Settings/AchievementLoginDialog.cpp" line="84"/>
|
||||
<source>Enable Achievements</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Settings/AchievementLoginDialog.cpp" line="84"/>
|
||||
<location filename="../Settings/AchievementLoginDialog.cpp" line="85"/>
|
||||
<source>Achievement tracking is not currently enabled. Your login will have no effect until after tracking is enabled.
|
||||
|
||||
Do you want to enable tracking now?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Settings/AchievementLoginDialog.cpp" line="95"/>
|
||||
<location filename="../Settings/AchievementLoginDialog.cpp" line="96"/>
|
||||
<source>Enable Hardcore Mode</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Settings/AchievementLoginDialog.cpp" line="96"/>
|
||||
<location filename="../Settings/AchievementLoginDialog.cpp" line="97"/>
|
||||
<source>Hardcore mode is not currently enabled. Enabling hardcore mode allows you to set times, scores, and participate in game-specific leaderboards.
|
||||
|
||||
However, hardcore mode also prevents the usage of save states, cheats and slowdown functionality.
|
||||
@@ -154,12 +154,12 @@ Do you want to enable hardcore mode?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Settings/AchievementLoginDialog.cpp" line="112"/>
|
||||
<location filename="../Settings/AchievementLoginDialog.cpp" line="113"/>
|
||||
<source>Reset System</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Settings/AchievementLoginDialog.cpp" line="113"/>
|
||||
<location filename="../Settings/AchievementLoginDialog.cpp" line="114"/>
|
||||
<source>Hardcore mode will not be enabled until the system is reset. Do you want to reset the system now?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
@@ -3539,7 +3539,7 @@ You cannot undo this action.</source>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../CoverDownloadDialog.ui" line="95"/>
|
||||
<location filename="../CoverDownloadDialog.cpp" line="86"/>
|
||||
<location filename="../CoverDownloadDialog.cpp" line="87"/>
|
||||
<source>Start</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
@@ -3549,12 +3549,12 @@ You cannot undo this action.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../CoverDownloadDialog.cpp" line="64"/>
|
||||
<location filename="../CoverDownloadDialog.cpp" line="65"/>
|
||||
<source>Download complete.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../CoverDownloadDialog.cpp" line="86"/>
|
||||
<location filename="../CoverDownloadDialog.cpp" line="87"/>
|
||||
<source>Stop</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
@@ -5469,52 +5469,52 @@ Do you want to overwrite?</source>
|
||||
<context>
|
||||
<name>DockTabBar</name>
|
||||
<message>
|
||||
<location filename="../Debugger/Docking/DockViews.cpp" line="175"/>
|
||||
<location filename="../Debugger/Docking/DockViews.cpp" line="177"/>
|
||||
<source>Rename</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Debugger/Docking/DockViews.cpp" line="186"/>
|
||||
<location filename="../Debugger/Docking/DockViews.cpp" line="188"/>
|
||||
<source>Rename Window</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Debugger/Docking/DockViews.cpp" line="186"/>
|
||||
<location filename="../Debugger/Docking/DockViews.cpp" line="188"/>
|
||||
<source>New name:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Debugger/Docking/DockViews.cpp" line="192"/>
|
||||
<location filename="../Debugger/Docking/DockViews.cpp" line="194"/>
|
||||
<source>Invalid Name</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Debugger/Docking/DockViews.cpp" line="192"/>
|
||||
<location filename="../Debugger/Docking/DockViews.cpp" line="194"/>
|
||||
<source>The specified name is too long.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Debugger/Docking/DockViews.cpp" line="199"/>
|
||||
<location filename="../Debugger/Docking/DockViews.cpp" line="201"/>
|
||||
<source>Reset Name</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Debugger/Docking/DockViews.cpp" line="213"/>
|
||||
<location filename="../Debugger/Docking/DockViews.cpp" line="215"/>
|
||||
<source>Primary</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Debugger/Docking/DockViews.cpp" line="228"/>
|
||||
<location filename="../Debugger/Docking/DockViews.cpp" line="230"/>
|
||||
<source>Set Target</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Debugger/Docking/DockViews.cpp" line="249"/>
|
||||
<location filename="../Debugger/Docking/DockViews.cpp" line="251"/>
|
||||
<source>Inherit From Layout</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Debugger/Docking/DockViews.cpp" line="257"/>
|
||||
<location filename="../Debugger/Docking/DockViews.cpp" line="259"/>
|
||||
<source>Close</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
@@ -9193,22 +9193,22 @@ Do you want to load this save and continue?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/ImGui/FullscreenUI.cpp" line="7903"/>
|
||||
<location filename="../../pcsx2/ImGui/FullscreenUI.cpp" line="7905"/>
|
||||
<source>Controller Settings</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/ImGui/FullscreenUI.cpp" line="7904"/>
|
||||
<location filename="../../pcsx2/ImGui/FullscreenUI.cpp" line="7906"/>
|
||||
<source>Hotkey Settings</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/ImGui/FullscreenUI.cpp" line="7905"/>
|
||||
<location filename="../../pcsx2/ImGui/FullscreenUI.cpp" line="7904"/>
|
||||
<source>Achievements Settings</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/ImGui/FullscreenUI.cpp" line="7906"/>
|
||||
<location filename="../../pcsx2/ImGui/FullscreenUI.cpp" line="7903"/>
|
||||
<source>Folder Settings</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
@@ -10856,7 +10856,7 @@ Do you want to load this save and continue?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GS/Renderers/HW/GSTextureReplacements.cpp" line="699"/>
|
||||
<location filename="../../pcsx2/GS/Renderers/HW/GSTextureReplacements.cpp" line="740"/>
|
||||
<source>Disabling autogenerated mipmaps on one or more compressed replacement textures. Please generate mipmaps when compressing your textures.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
@@ -11344,113 +11344,263 @@ graphical quality, but this will increase system requirements.</source>
|
||||
<context>
|
||||
<name>GameList</name>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="102"/>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="97"/>
|
||||
<source>PS2 Disc</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="102"/>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="98"/>
|
||||
<source>PS1 Disc</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="102"/>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="99"/>
|
||||
<source>ELF</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="109"/>
|
||||
<source>Other</source>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="100"/>
|
||||
<source>Invalid</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="113"/>
|
||||
<source>NTSC-B</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="114"/>
|
||||
<source>NTSC-C</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="115"/>
|
||||
<source>NTSC-HK</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="116"/>
|
||||
<source>NTSC-J</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="117"/>
|
||||
<source>NTSC-K</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="118"/>
|
||||
<source>NTSC-T</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="119"/>
|
||||
<source>NTSC-U</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="120"/>
|
||||
<source>Unknown</source>
|
||||
<source>Other</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="121"/>
|
||||
<source>Nothing</source>
|
||||
<source>PAL-A</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="122"/>
|
||||
<source>Intro</source>
|
||||
<source>PAL-AF</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="123"/>
|
||||
<source>Menu</source>
|
||||
<source>PAL-AU</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="124"/>
|
||||
<source>In-Game</source>
|
||||
<source>PAL-BE</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="125"/>
|
||||
<source>Playable</source>
|
||||
<source>PAL-E</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="126"/>
|
||||
<source>PAL-F</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="127"/>
|
||||
<source>PAL-FI</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="128"/>
|
||||
<source>PAL-G</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="129"/>
|
||||
<source>PAL-GR</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="130"/>
|
||||
<source>PAL-I</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="131"/>
|
||||
<source>PAL-IN</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="132"/>
|
||||
<source>PAL-M</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="133"/>
|
||||
<source>PAL-NL</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="134"/>
|
||||
<source>PAL-NO</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="135"/>
|
||||
<source>PAL-P</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="136"/>
|
||||
<source>PAL-PL</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="137"/>
|
||||
<source>PAL-R</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="138"/>
|
||||
<source>PAL-S</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="139"/>
|
||||
<source>PAL-SC</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="140"/>
|
||||
<source>PAL-SW</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="141"/>
|
||||
<source>PAL-SWI</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="142"/>
|
||||
<source>PAL-UK</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="158"/>
|
||||
<source>Unknown</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="161"/>
|
||||
<source>Nothing</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="164"/>
|
||||
<source>Intro</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="167"/>
|
||||
<source>Menu</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="170"/>
|
||||
<source>In-Game</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="173"/>
|
||||
<source>Playable</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="176"/>
|
||||
<source>Perfect</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="595"/>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="653"/>
|
||||
<source>Scanning directory {} (recursively)...</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="596"/>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="656"/>
|
||||
<source>Scanning directory {}...</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="625"/>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="684"/>
|
||||
<source>Scanning {}...</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="1083"/>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="1142"/>
|
||||
<source>Never</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="1100"/>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="1159"/>
|
||||
<source>Today</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="1105"/>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="1164"/>
|
||||
<source>Yesterday</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="1128"/>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="1187"/>
|
||||
<source>{}h {}m</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="1130"/>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="1189"/>
|
||||
<source>{}h {}m {}s</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="1132"/>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="1191"/>
|
||||
<source>{}m {}s</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="1134"/>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="1193"/>
|
||||
<source>{}s</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<location filename="../GameList/GameListModel.cpp" line="268"/>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="1141"/>
|
||||
<location filename="../GameList/GameListModel.cpp" line="274"/>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="1200"/>
|
||||
<source>%n hours</source>
|
||||
<translation type="unfinished">
|
||||
<numerusform></numerusform>
|
||||
@@ -11458,8 +11608,8 @@ graphical quality, but this will increase system requirements.</source>
|
||||
</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<location filename="../GameList/GameListModel.cpp" line="270"/>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="1143"/>
|
||||
<location filename="../GameList/GameListModel.cpp" line="276"/>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="1202"/>
|
||||
<source>%n minutes</source>
|
||||
<translation type="unfinished">
|
||||
<numerusform></numerusform>
|
||||
@@ -11467,7 +11617,7 @@ graphical quality, but this will increase system requirements.</source>
|
||||
</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="1303"/>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="1362"/>
|
||||
<source>Downloading cover for {0} [{1}]...</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
@@ -11475,52 +11625,52 @@ graphical quality, but this will increase system requirements.</source>
|
||||
<context>
|
||||
<name>GameListModel</name>
|
||||
<message>
|
||||
<location filename="../GameList/GameListModel.cpp" line="597"/>
|
||||
<location filename="../GameList/GameListModel.cpp" line="603"/>
|
||||
<source>Type</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../GameList/GameListModel.cpp" line="598"/>
|
||||
<location filename="../GameList/GameListModel.cpp" line="604"/>
|
||||
<source>Code</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../GameList/GameListModel.cpp" line="599"/>
|
||||
<location filename="../GameList/GameListModel.cpp" line="605"/>
|
||||
<source>Title</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../GameList/GameListModel.cpp" line="600"/>
|
||||
<location filename="../GameList/GameListModel.cpp" line="606"/>
|
||||
<source>File Title</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../GameList/GameListModel.cpp" line="601"/>
|
||||
<location filename="../GameList/GameListModel.cpp" line="607"/>
|
||||
<source>CRC</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../GameList/GameListModel.cpp" line="602"/>
|
||||
<location filename="../GameList/GameListModel.cpp" line="608"/>
|
||||
<source>Time Played</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../GameList/GameListModel.cpp" line="603"/>
|
||||
<location filename="../GameList/GameListModel.cpp" line="609"/>
|
||||
<source>Last Played</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../GameList/GameListModel.cpp" line="604"/>
|
||||
<location filename="../GameList/GameListModel.cpp" line="610"/>
|
||||
<source>Size</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../GameList/GameListModel.cpp" line="605"/>
|
||||
<location filename="../GameList/GameListModel.cpp" line="611"/>
|
||||
<source>Region</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../GameList/GameListModel.cpp" line="606"/>
|
||||
<location filename="../GameList/GameListModel.cpp" line="612"/>
|
||||
<source>Compatibility</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
@@ -12055,111 +12205,111 @@ Scanning recursively takes more time, but will identify files in subdirectories.
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="76"/>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="77"/>
|
||||
<source>%0%1</source>
|
||||
<extracomment>First arg is a GameList compat; second is a string with space followed by star rating OR empty if Unknown compat</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="84"/>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="85"/>
|
||||
<source> %0%1</source>
|
||||
<extracomment>First arg is filled-in stars for game compatibility; second is empty stars; should be swapped for RTL languages</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="173"/>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="174"/>
|
||||
<source>Select Disc Path</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="186"/>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="187"/>
|
||||
<source>Game is not a CD/DVD.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="193"/>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="194"/>
|
||||
<source>Track list unavailable while virtual machine is running.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="218"/>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="219"/>
|
||||
<source>#</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="221"/>
|
||||
<source>Mode</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="222"/>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="230"/>
|
||||
<source>Start</source>
|
||||
<source>Mode</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="223"/>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="231"/>
|
||||
<source>Sectors</source>
|
||||
<source>Start</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="224"/>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="232"/>
|
||||
<source>Size</source>
|
||||
<source>Sectors</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="225"/>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="233"/>
|
||||
<source>MD5</source>
|
||||
<source>Size</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="226"/>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="234"/>
|
||||
<source>MD5</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="227"/>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="235"/>
|
||||
<source>Status</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="242"/>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="247"/>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="243"/>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="248"/>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="249"/>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="255"/>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="250"/>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="256"/>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="257"/>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="258"/>
|
||||
<source>%1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="250"/>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="258"/>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="251"/>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="259"/>
|
||||
<source><not computed></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="274"/>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="275"/>
|
||||
<source>Error</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="274"/>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="275"/>
|
||||
<source>Cannot verify image while a game is running.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="304"/>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="305"/>
|
||||
<source>One or more tracks is missing.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="341"/>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="342"/>
|
||||
<source>Verified as %1 [%2] (Version %3).</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="348"/>
|
||||
<location filename="../Settings/GameSummaryWidget.cpp" line="349"/>
|
||||
<source>Verified as %1 [%2].</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
@@ -17437,10 +17587,10 @@ Savestates should not be used in place of in-game saves.</source>
|
||||
<name>MemoryCardCreateDialog</name>
|
||||
<message>
|
||||
<location filename="../Settings/MemoryCardCreateDialog.ui" line="14"/>
|
||||
<location filename="../Settings/MemoryCardCreateDialog.cpp" line="106"/>
|
||||
<location filename="../Settings/MemoryCardCreateDialog.cpp" line="113"/>
|
||||
<location filename="../Settings/MemoryCardCreateDialog.cpp" line="120"/>
|
||||
<location filename="../Settings/MemoryCardCreateDialog.cpp" line="133"/>
|
||||
<location filename="../Settings/MemoryCardCreateDialog.cpp" line="107"/>
|
||||
<location filename="../Settings/MemoryCardCreateDialog.cpp" line="114"/>
|
||||
<location filename="../Settings/MemoryCardCreateDialog.cpp" line="121"/>
|
||||
<location filename="../Settings/MemoryCardCreateDialog.cpp" line="134"/>
|
||||
<source>Create Memory Card</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
@@ -17521,22 +17671,22 @@ Savestates should not be used in place of in-game saves.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Settings/MemoryCardCreateDialog.cpp" line="107"/>
|
||||
<location filename="../Settings/MemoryCardCreateDialog.cpp" line="108"/>
|
||||
<source>Failed to create the Memory Card, because the name '%1' contains one or more invalid characters.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Settings/MemoryCardCreateDialog.cpp" line="114"/>
|
||||
<location filename="../Settings/MemoryCardCreateDialog.cpp" line="115"/>
|
||||
<source>Failed to create the Memory Card, because another card with the name '%1' already exists.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Settings/MemoryCardCreateDialog.cpp" line="121"/>
|
||||
<location filename="../Settings/MemoryCardCreateDialog.cpp" line="122"/>
|
||||
<source>Failed to create the Memory Card, the log may contain more information.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../Settings/MemoryCardCreateDialog.cpp" line="133"/>
|
||||
<location filename="../Settings/MemoryCardCreateDialog.cpp" line="134"/>
|
||||
<source>Memory Card '%1' created.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
@@ -20166,6 +20316,15 @@ Scanning recursively takes more time, but will identify files in subdirectories.
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>TextureReplacement</name>
|
||||
<message>
|
||||
<location filename="../../pcsx2/GS/Renderers/HW/GSTextureReplacements.cpp" line="409"/>
|
||||
<source>Texture replacement directory {} will not work on case sensitive filesystems.
|
||||
Rename it to {} to remove this warning.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ThreadModel</name>
|
||||
<message>
|
||||
|
||||
@@ -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})
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -44,6 +44,7 @@ enum class ShaderConvert
|
||||
DEPTH_COPY,
|
||||
DOWNSAMPLE_COPY,
|
||||
RGBA_TO_8I,
|
||||
RGB5A1_TO_8I,
|
||||
CLUT_4,
|
||||
CLUT_8,
|
||||
YUV,
|
||||
|
||||
@@ -1417,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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1599,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;
|
||||
@@ -1637,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()
|
||||
{
|
||||
@@ -1906,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
|
||||
@@ -2493,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))
|
||||
@@ -2927,12 +2934,12 @@ void GSRendererHW::Draw()
|
||||
}
|
||||
|
||||
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.
|
||||
@@ -3014,7 +3021,7 @@ 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.
|
||||
@@ -3045,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;
|
||||
@@ -3887,7 +3894,8 @@ 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)
|
||||
{
|
||||
@@ -3941,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;
|
||||
}
|
||||
@@ -4084,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));
|
||||
@@ -4170,6 +4183,7 @@ void GSRendererHW::Draw()
|
||||
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
|
||||
{
|
||||
@@ -4456,6 +4470,7 @@ void GSRendererHW::Draw()
|
||||
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)
|
||||
@@ -5197,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;
|
||||
@@ -5267,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;
|
||||
@@ -6997,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);
|
||||
@@ -7934,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)
|
||||
@@ -7997,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)))
|
||||
|
||||
@@ -129,6 +129,7 @@ private:
|
||||
bool ContinueSplitClear();
|
||||
void FinishSplitClear();
|
||||
|
||||
bool NeedsBlending();
|
||||
bool IsRTWritten();
|
||||
bool IsDepthAlwaysPassing();
|
||||
bool IsUsingCsInBlend();
|
||||
|
||||
@@ -1617,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;
|
||||
}
|
||||
@@ -1647,7 +1646,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
|
||||
// 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);
|
||||
@@ -1665,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;
|
||||
@@ -1773,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;
|
||||
@@ -1987,6 +1987,8 @@ 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
|
||||
@@ -2004,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, 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;
|
||||
}
|
||||
@@ -2070,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;
|
||||
}
|
||||
|
||||
@@ -2304,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];
|
||||
|
||||
@@ -2414,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;
|
||||
@@ -2678,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))
|
||||
{
|
||||
@@ -2687,28 +2740,54 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
const u32 mask = GSLocalMemory::m_psm[TEX0.PSM].fmsk;
|
||||
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);
|
||||
@@ -2820,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
|
||||
@@ -3031,7 +3110,7 @@ GSTextureCache::Target* GSTextureCache::CreateTarget(GIFRegTEX0 TEX0, const GSVe
|
||||
{
|
||||
// Not having this valid could make things explode, but I do enjoy watching the world burn (and this is actually more correct).
|
||||
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);
|
||||
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];
|
||||
@@ -3053,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;
|
||||
@@ -3077,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;
|
||||
}
|
||||
|
||||
@@ -3260,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)
|
||||
{
|
||||
@@ -3271,7 +3356,7 @@ 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);
|
||||
@@ -3314,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))
|
||||
@@ -3355,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)
|
||||
{
|
||||
@@ -3408,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)
|
||||
{
|
||||
@@ -3428,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(),
|
||||
@@ -3462,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;
|
||||
@@ -3941,7 +4056,7 @@ void GSTextureCache::InvalidateContainedTargets(u32 start_bp, u32 end_bp, u32 wr
|
||||
{
|
||||
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);
|
||||
@@ -3952,6 +4067,23 @@ void GSTextureCache::InvalidateContainedTargets(u32 start_bp, u32 end_bp, u32 wr
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
t->m_valid_alpha_low &= preserve_alpha;
|
||||
@@ -4674,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;
|
||||
|
||||
@@ -5047,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;
|
||||
@@ -5524,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
|
||||
@@ -5592,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;
|
||||
}
|
||||
@@ -5760,24 +5903,16 @@ 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);
|
||||
|
||||
@@ -6563,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
|
||||
@@ -6588,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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
|
||||
|
||||
@@ -64,6 +64,7 @@ struct GSMTLIndexedConvertPSUniform
|
||||
float scale;
|
||||
uint sbw;
|
||||
uint dbw;
|
||||
uint psm;
|
||||
};
|
||||
|
||||
struct GSMTLDownsamplePSUniform
|
||||
|
||||
@@ -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)]])
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user