Compare commits

...

42 Commits

Author SHA1 Message Date
lightningterror
c4a8fc5b71 Qt: Fix Winconsistent-missing-override warning. 2025-06-11 23:17:53 +02:00
GovanifY
8eb46b5a4c IopBios: remove clang deprecated carveout
we use snprintf now, so this isn't useful anymore.
2025-06-10 20:59:25 +02:00
GovanifY
8be16d1039 IopBios: do not overflow snprintf tmp buffer
We could otherwise overflow as snprintf does not return the number of
written bytes but the number of written bytes assuming an infinite
buffer.
2025-06-10 20:59:25 +02:00
refractionpcsx2
76e6208d1b GS/TC: Fix region for tex in rt depth 2025-06-10 20:12:41 +02:00
refractionpcsx2
bc09080ba5 GS/TC: Remove old inside check from source lookup 2025-06-10 20:12:41 +02:00
refractionpcsx2
a6eb257a3a GS/HW: Allow RT in RT offset on Z buffer if FRAME is disabled 2025-06-10 20:11:32 +02:00
PCSX2 Bot
e62450d255 [ci skip] PAD: Update to latest controller database. 2025-06-10 20:10:22 +02:00
GovanifY
1aa922f700 IopBios: allow %u string formatting in IOP kprintf 2025-06-10 14:47:27 +02:00
GovanifY
4c9d2f99b1 IopBios: truncate printf output if bigger than our buffer
I also increased the buffer size while we are at it to avoid breakage,
despite the obvious unintended allowed situations that we allowed up
until now

Reported-By: Michael Lappas
2025-06-10 14:47:27 +02:00
Ty Lamontagne
f6e899b570 Debugger: Update search results when we are _not_ going to remove them 2025-06-08 15:44:56 -04:00
refractionpcsx2
695c39fba2 GS/HW: Remove channel shuffle override from Namco CRC hack 2025-06-08 16:05:24 +02:00
refractionpcsx2
e5616cff98 GS/HW: Improve shuffle detection robustness 2025-06-08 13:51:19 +02:00
refractionpcsx2
76d5994c1e GS/TC: Improve rect block offset calculation for translation and invalidation 2025-06-08 13:51:19 +02:00
refractionpcsx2
cbb40832a1 GS/HW: Correct block offset target usage 2025-06-08 13:51:19 +02:00
refractionpcsx2
e4bdcde1ca GS/HW: Remove CRC hacks for Kunoichi and Sakura Wars 2025-06-08 13:51:19 +02:00
refractionpcsx2
863e119ff4 GS/HW: Allow conversion of colour to Z formats during HW move 2025-06-08 13:51:19 +02:00
refractionpcsx2
2ccf6dc872 GS: Add IsPageAlignedMasked to return an alignment mask 2025-06-08 13:51:19 +02:00
refractionpcsx2
24ebf1b4f1 GS/HW: Fix bug in target preloading causing misaligned base addresses 2025-06-08 13:51:19 +02:00
refractionpcsx2
ed2832434c GS/HW: Don't look up block offset targets on Exact target lookup 2025-06-08 13:51:19 +02:00
TJnotJT
5e160fca8f Tools: Small fix to gsrunner args. 2025-06-06 18:05:37 -04:00
JordanTheToaster
a0ef82e221 AudioStream: Fix typo with default output latency 2025-06-06 18:03:29 -04:00
icup321
3e7ac3d66c GameDB: Add native scaling to Scooby-Doo! Unmasked 2025-06-06 23:09:37 +02:00
JordanTheToaster
730e6fa737 GameDB: Simple 2000 Vol 92 Fixes 2025-06-06 23:09:26 +02:00
PCSX2 Bot
cdfcd9fddd [ci skip] Qt: Update Base Translation. 2025-06-06 02:31:08 +02:00
refractionpcsx2
b6930c10b9 GS/HW: Clean up target download formats 2025-06-05 13:51:21 +02:00
refractionpcsx2
852734580d GameDB: Update Harry Potter fixes. 2025-06-05 13:51:21 +02:00
refractionpcsx2
d1dc6a9c1d GS/HW: Add 16bit to 8bit conversion shader 2025-06-05 13:51:21 +02:00
Ty
4fa6d3ed3f GSRunner: Add type to shutdown message code 2025-06-04 20:25:32 -04:00
TellowKrinkle
92e190ad6c GSRunner: Fix surfaceless run on macOS 2025-06-04 20:25:32 -04:00
TellowKrinkle
da4fcffef4 Input: Fix crash when shutting down without initializing input 2025-06-04 20:25:32 -04:00
TellowKrinkle
1a5731dd8e MacOS: Better handle directories of non-bundle applications 2025-06-04 20:25:32 -04:00
TellowKrinkle
e764c5cd4e GSRunner: macOS support 2025-06-04 20:25:32 -04:00
TellowKrinkle
e23b247947 GSRunner: Use separate CPU thread 2025-06-04 20:25:32 -04:00
PCSX2 Bot
3d7792436f [ci skip] Qt: Update Base Translation. 2025-06-04 20:03:38 -04:00
chaoticgd
d8187fbea4 Deps: Specify minimum version of KDDockWidgets 2025-06-04 20:02:02 -04:00
chaoticgd
02259ad0a5 Debugger: Add include required for older versions of KDDockWidgets 2025-06-04 20:02:02 -04:00
TellowKrinkle
220a68df9a GS: Warn on texture replacement folder with wrong case 2025-06-04 19:58:41 -04:00
TellowKrinkle
2ced24f69e GS: Create texture dump directory if it doesn't exist 2025-06-04 19:58:41 -04:00
TellowKrinkle
ec91d0dc74 GS: Formatting 2025-06-04 19:58:41 -04:00
TheLastRar
46874f4673 Qt: Add workaround for incorrectly tinted icons after theme switch 2025-06-04 19:39:18 -04:00
TheLastRar
9eac47dc6c Qt: Fix selected gamelist icons being wrong colour after theme switch 2025-06-04 19:39:18 -04:00
RedDevilus
9e3fd5c2e0 CI: Fix flatpak
Try to fix build failing
2025-06-04 20:19:46 +01:00
42 changed files with 1177 additions and 301 deletions

View File

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

View File

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

View File

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

View File

@@ -2557,8 +2557,6 @@ SCAJ-25012:
SCAJ-25026:
name: "Kunoichi Shinobi"
region: "NTSC-Unk"
gsHWFixes:
getSkipCount: "GSC_Kunoichi"
SCAJ-25034:
name: "Sakura Taisen Monogatari"
region: "NTSC-Unk"
@@ -12878,8 +12876,6 @@ SLAJ-25023:
SLAJ-25026:
name: "Kunoichi Shinobi"
region: "NTSC-Unk"
gsHWFixes:
getSkipCount: "GSC_Kunoichi"
SLAJ-25027:
name: "Sonic Heroes"
region: "NTSC-Unk"
@@ -12894,8 +12890,6 @@ SLAJ-25030:
SLAJ-25031:
name: "Kunoichi - Shinobu"
region: "NTSC-C-J"
gsHWFixes:
getSkipCount: "GSC_Kunoichi"
SLAJ-25033:
name: "Puyo Puyo Fever"
region: "NTSC-Unk"
@@ -12927,7 +12921,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:
@@ -16829,7 +16823,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:
@@ -16839,7 +16833,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:
@@ -16849,7 +16843,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:
@@ -16859,7 +16853,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:
@@ -16869,7 +16863,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:
@@ -16926,7 +16920,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:
@@ -16936,7 +16930,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:
@@ -16946,7 +16940,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:
@@ -16956,7 +16950,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:
@@ -16966,7 +16960,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:
@@ -16976,7 +16970,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:
@@ -19388,8 +19382,6 @@ SLES-52238:
name: "Nightshade"
region: "PAL-M5"
compat: 5
gsHWFixes:
getSkipCount: "GSC_Kunoichi"
SLES-52240:
name: "International Pool Championship"
region: "PAL-M5"
@@ -19896,7 +19888,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:
@@ -20123,7 +20115,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:
@@ -20434,7 +20426,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:
@@ -22086,6 +22078,8 @@ SLES-53099:
SLES-53100:
name: "Scooby-Doo! Unmasked"
region: "PAL-M4"
gsHWFixes:
nativeScaling: 2 # Fixes post processing position.
SLES-53104:
name: "Tom Clancy's Rainbow Six - Lockdown"
region: "PAL-M5"
@@ -30956,8 +30950,6 @@ SLKA-25135:
name: "Kunoichi"
region: "NTSC-K"
compat: 5
gsHWFixes:
getSkipCount: "GSC_Kunoichi"
SLKA-25136:
name: "Need for Speed - Underground"
region: "NTSC-K"
@@ -31108,7 +31100,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:
@@ -32522,8 +32514,6 @@ SLKA-35004:
name: "Sakura Wars 5 So Long My Love"
region: "NTSC-K"
compat: 5
gsHWFixes:
getSkipCount: "GSC_SakuraWarsSoLongMyLove"
SLKA-35005:
name: "Jin Samguk Mussang 5 - Special"
region: "NTSC-K"
@@ -35217,8 +35207,6 @@ SLPM-61059:
name-sort: "くのいち -しのび- [たいけんばん]"
name-en: "Kunoichi -Shinobi- [Trial]"
region: "NTSC-J"
gsHWFixes:
getSkipCount: "GSC_Kunoichi"
SLPM-61060:
name: "BUSIN 0 Wizardry Alternative NEO [体験版]"
name-sort: "ぶしん ぜろ ~うぃざーどりぃ おるたなてぃぶ ねお~ [たいけんばん]"
@@ -36957,7 +36945,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"
@@ -38369,7 +38357,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]"
@@ -39800,7 +39788,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"
@@ -42473,8 +42461,6 @@ SLPM-65447:
name-en: "Kunoichi -Shinobi-"
region: "NTSC-J"
compat: 5
gsHWFixes:
getSkipCount: "GSC_Kunoichi"
SLPM-65448:
name: "カンブリアンQTS ~化石になっても~"
name-sort: "かんぶりあんQTS ~かせきになっても~"
@@ -43355,7 +43341,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:
@@ -52157,7 +52143,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: "カラオケレボリューション用マイク同梱お試しディスク"
@@ -54308,7 +54294,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"
@@ -59136,6 +59122,8 @@ SLPS-25581:
name-sort: "しんぷる2000しりーず Vol. 92 THE のろいのげーむ"
name-en: "Simple 2000 Series Vol. 92 - The Game of a Curse"
region: "NTSC-J"
gsHWFixes:
cpuCLUTRender: 1 # Fixes corrupted and missing graphics.
SLPS-25582:
name: "アストロ球団 決戦!!ビクトリー球団編"
name-sort: "あすとろきゅうだん けっせん!!びくとりーきゅうだんへん"
@@ -65273,7 +65261,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"
@@ -66538,8 +66526,6 @@ SLUS-20810:
name: "Nightshade"
region: "NTSC-U"
compat: 5
gsHWFixes:
getSkipCount: "GSC_Kunoichi"
SLUS-20811:
name: "Need for Speed - Underground"
region: "NTSC-U"
@@ -67218,7 +67204,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:
@@ -68231,6 +68217,8 @@ SLUS-21091:
name: "Scooby-Doo! Unmasked"
region: "NTSC-U"
compat: 5
gsHWFixes:
nativeScaling: 2 # Fixes post processing position.
SLUS-21093:
name: "Worms Forts - Under Siege"
region: "NTSC-U"
@@ -73133,8 +73121,6 @@ SLUS-21927:
name: "Sakura Wars - So Long, My Love [English - Disc 1]"
region: "NTSC-U"
compat: 5
gsHWFixes:
getSkipCount: "GSC_SakuraWarsSoLongMyLove"
SLUS-21928:
name: "Scooby-Doo! and the Spooky Swamp"
region: "NTSC-U"
@@ -73151,8 +73137,6 @@ SLUS-21930:
name: "Sakura Wars - So Long, My Love [Japanese - Disc 2]"
region: "NTSC-U"
compat: 5
gsHWFixes:
getSkipCount: "GSC_SakuraWarsSoLongMyLove"
SLUS-21931:
name: "Disney/Pixar Toy Story 3"
region: "NTSC-U"

View File

@@ -1000,6 +1000,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000d62000007162000001000000,Moga Pro 2,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Mac OS X,
03000000c62400002a89000000010000,MOGA XP5A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,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:Mac OS X,
03000000c62400002b89000000010000,MOGA XP5A Plus,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:Mac OS X,
03000000853200008906000000010000,Nacon Revolution X Unlimited,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:Mac OS X,
03000000632500007505000000020000,NeoGeo mini PAD Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,x:b2,y:b3,platform:Mac OS X,
03000000921200004b46000003020000,NES 2-port Adapter,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b11,platform:Mac OS X,
030000001008000001e5000006010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,righttrigger:b6,start:b9,x:b3,y:b0,platform:Mac OS X,
@@ -1607,6 +1608,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
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,
030000009b2800006400000001010000,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,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -311,7 +311,7 @@ void searchWorker(DebugInterface* cpu, std::vector<SearchResult>& searchResults,
const auto readValue = readValueAtAddress<T>(cpu, addr);
const bool doesMatch = handleSearchComparison(searchComparison, addr, &searchResult, searchValue, readValue);
if (!doesMatch)
if (doesMatch)
searchResult = MemorySearchView::SearchResult(addr, QVariant::fromValue(readValue), searchType);
return !doesMatch;

View File

@@ -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);

View File

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

View File

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

View File

@@ -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>
@@ -10866,7 +10866,7 @@ Do you want to load this save and continue?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp" line="5060"/>
<location filename="../../pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp" line="5061"/>
<source>Spin GPU During Readbacks is enabled, but calibrated timestamps are unavailable. This might be really slow.</source>
<translation type="unfinished"></translation>
</message>
@@ -10959,7 +10959,7 @@ Please see our official documentation for more information.</source>
<context>
<name>GSDeviceOGL</name>
<message>
<location filename="../../pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp" line="641"/>
<location filename="../../pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp" line="642"/>
<source>OpenGL renderer is not supported. Only OpenGL {}.{}
was found</source>
<translation type="unfinished"></translation>
@@ -20316,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>

View File

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

View File

@@ -446,6 +446,13 @@ std::vector<GSVector2i>* GSLocalMemory::GetPage2TileMap(const GIFRegTEX0& TEX0)
return p2t;
}
u32 GSLocalMemory::IsPageAlignedMasked(u32 psm, const GSVector4i& rc)
{
const psm_t& psm_s = m_psm[psm];
const GSVector4i pgmsk = GSVector4i(psm_s.pgs).xyxy() - GSVector4i(1);
return ((rc & pgmsk) == GSVector4i::zero()).mask();
}
bool GSLocalMemory::IsPageAligned(u32 psm, const GSVector4i& rc)
{
const psm_t& psm_s = m_psm[psm];

View File

@@ -524,6 +524,7 @@ public:
GSPixelOffset4* GetPixelOffset4(const GIFRegFRAME& FRAME, const GIFRegZBUF& ZBUF);
std::vector<GSVector2i>* GetPage2TileMap(const GIFRegTEX0& TEX0);
static bool HasOverlap(u32 src_bp, u32 src_bw, u32 src_psm, GSVector4i src_rect, u32 dst_bp, u32 dst_bw, u32 dst_psm, GSVector4i dst_rect);
static u32 IsPageAlignedMasked(u32 psm, const GSVector4i& rc);
static bool IsPageAligned(u32 psm, const GSVector4i& rc);
static u32 GetStartBlockAddress(u32 bp, u32 bw, u32 psm, GSVector4i rect);
static u32 GetEndBlockAddress(u32 bp, u32 bw, u32 psm, GSVector4i rect);

View File

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

View File

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

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -184,40 +184,6 @@ bool GSHwHack::GSC_NamcoGames(GSRendererHW& r, int& skip)
{
if (skip == 0)
{
if (r.IsPossibleChannelShuffle() && !(RTBP0 & 31))
{
GSVertex* v = &r.m_vertex.buff[0];
// Make sure we're detecting the right effect.
if (((v[1].XYZ.X - v[0].XYZ.X) >> 4) != 8 || ((v[1].XYZ.Y - v[0].XYZ.Y) >> 4) != 14)
return false;
GSTextureCache::Target* rt = g_texture_cache->LookupTarget(GIFRegTEX0::Create(RTBP0, RFBW, RFPSM),
GSVector2i(1, 1), r.GetTextureScaleFactor(), GSTextureCache::RenderTarget);
if (!rt)
return false;
GL_INS("GSC_NamcoGames(): HLE channel shuffle");
// have to set up the palette ourselves too, since GSC executes before it does
r.m_mem.m_clut.Read32(RTEX0, r.m_draw_env->TEXA);
std::shared_ptr<GSTextureCache::Palette> palette =
g_texture_cache->LookupPaletteObject(r.m_mem.m_clut, GSLocalMemory::m_psm[RTEX0.PSM].pal, true);
if (!palette)
return false;
GSHWDrawConfig& conf = r.BeginHLEHardwareDraw(
rt->GetTexture(), nullptr, rt->GetScale(), rt->GetTexture(), rt->GetScale(), rt->GetUnscaledRect());
conf.pal = palette->GetPaletteGSTexture();
conf.ps.channel = ChannelFetch_RGB;
conf.colormask.wa = false;
r.EndHLEHardwareDraw(false);
// 12 pages: 2 calls by channel, 3 channels, 1 blit
skip = 12 * (3 + 3 + 1);
return true;
}
if (!s_nativeres && r.PRIM->PRIM == GS_SPRITE && RTME && RTEX0.TFX == 1 && RFPSM == RTPSM && RTPSM == PSMCT32 && RFBMSK == 0xFF000000 && r.m_index.tail > 2)
{
GSVertex* v = &r.m_vertex.buff[0];
@@ -431,31 +397,6 @@ bool GSHwHack::GSC_TalesOfLegendia(GSRendererHW& r, int& skip)
return true;
}
bool GSHwHack::GSC_Kunoichi(GSRendererHW& r, int& skip)
{
if (skip == 0)
{
if (!RTME && (RFBP == 0x0 || RFBP == 0x00700 || RFBP == 0x00800) && RFPSM == PSMCT32 && RFBMSK == 0x00FFFFFF)
{
// Removes depth effects(shadows) not rendered correctly on all renders.
skip = 3;
}
if (RTME && (RFBP == 0x0700 || RFBP == 0) && RTBP0 == 0x0e00 && RTPSM == 0 && RFBMSK == 0)
{
skip = 1; // Removes black screen (not needed anymore maybe)?
}
}
else
{
if (RTME && (RFBP == 0x0e00) && RFPSM == PSMCT32 && RFBMSK == 0xFF000000)
{
skip = 0;
}
}
return true;
}
bool GSHwHack::GSC_ZettaiZetsumeiToshi2(GSRendererHW& r, int& skip)
{
if (skip == 0)
@@ -505,27 +446,6 @@ bool GSHwHack::GSC_ZettaiZetsumeiToshi2(GSRendererHW& r, int& skip)
return true;
}
bool GSHwHack::GSC_SakuraWarsSoLongMyLove(GSRendererHW& r, int& skip)
{
if (skip == 0)
{
if (RTME == 0 && RFBP != RTBP0 && RTBP0 && RFBMSK == 0x00FFFFFF)
{
skip = 3; // Remove darkness
}
else if (RTME == 0 && RFBP == RTBP0 && (RTBP0 == 0x1200 || RTBP0 == 0x1180 || RTBP0 == 0) && RFBMSK == 0x00FFFFFF)
{
skip = 3; // Remove darkness
}
else if (RTME && (RFBP == 0 || RFBP == 0x1180) && RFPSM == PSMCT32 && RTBP0 == 0x3F3F && RTPSM == PSMT8)
{
skip = 1; // Floodlight
}
}
return true;
}
bool GSHwHack::GSC_UltramanFightingEvolution(GSRendererHW& r, int& skip)
{
if (skip == 0)
@@ -1393,12 +1313,10 @@ bool GSHwHack::MV_Ico(GSRendererHW& r)
#define CRC_F(name) { #name, &GSHwHack::name }
const GSHwHack::Entry<GSRendererHW::GSC_Ptr> GSHwHack::s_get_skip_count_functions[] = {
CRC_F(GSC_Kunoichi),
CRC_F(GSC_Manhunt2),
CRC_F(GSC_MidnightClub3),
CRC_F(GSC_SacredBlaze),
CRC_F(GSC_GuitarHero),
CRC_F(GSC_SakuraWarsSoLongMyLove),
CRC_F(GSC_SFEX3),
CRC_F(GSC_DTGames),
CRC_F(GSC_TalesOfLegendia),

View File

@@ -16,9 +16,7 @@ public:
static bool GSC_BlackAndBurnoutSky(GSRendererHW& r, int& skip);
static bool GSC_MidnightClub3(GSRendererHW& r, int& skip);
static bool GSC_TalesOfLegendia(GSRendererHW& r, int& skip);
static bool GSC_Kunoichi(GSRendererHW& r, int& skip);
static bool GSC_ZettaiZetsumeiToshi2(GSRendererHW& r, int& skip);
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_UrbanReign(GSRendererHW& r, int& skip);

View File

@@ -2862,7 +2862,9 @@ void GSRendererHW::Draw()
GIFRegTEX0 FRAME_TEX0;
bool shuffle_target = false;
if (!no_rt && GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].bpp == 16 && GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].bpp >= 16 &&
const u32 page_alignment = GSLocalMemory::IsPageAlignedMasked(m_cached_ctx.TEX0.PSM, m_r);
const bool page_aligned = (page_alignment & 0xF0F0) != 0; // Make sure Y is page aligned.
if (!no_rt && page_aligned && m_cached_ctx.ZBUF.ZMSK && GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].bpp == 16 && GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].bpp >= 16 &&
(m_vt.m_primclass == GS_SPRITE_CLASS || (m_vt.m_primclass == GS_TRIANGLE_CLASS && (m_index.tail % 6) == 0 && TrianglesAreQuads(true) && m_index.tail > 6)))
{
// Tail check is to make sure we have enough strips to go all the way across the page, or if it's using a region clamp could be used to draw strips.
@@ -3164,6 +3166,144 @@ void GSRendererHW::Draw()
}
}
}
if (no_rt && ds->m_TEX0.TBP0 != m_cached_ctx.ZBUF.Block())
{
const GSLocalMemory::psm_t& zbuf_psm = GSLocalMemory::m_psm[m_cached_ctx.ZBUF.PSM];
int 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)) * zbuf_psm.pgs.y; // I know I could just not shift it..
int texture_offset = 0;
int horizontal_offset = ((static_cast<int>((m_cached_ctx.ZBUF.Block() - ds->m_TEX0.TBP0)) / 32) % static_cast<int>(std::max(ds->m_TEX0.TBW, 1U))) * zbuf_psm.pgs.x;
// Used to reduce the offset made later in channel shuffles
m_target_offset = std::abs(static_cast<int>((m_cached_ctx.ZBUF.Block() - ds->m_TEX0.TBP0)) >> 5);
if (vertical_offset < 0)
{
ds->m_TEX0.TBP0 = m_cached_ctx.ZBUF.Block();
GSVector2i new_size = ds->m_unscaled_size;
// Make sure to use the original format for the offset.
const int new_offset = std::abs((vertical_offset / zbuf_psm.pgs.y) * GSLocalMemory::m_psm[ds->m_TEX0.PSM].pgs.y);
texture_offset = new_offset;
new_size.y += new_offset;
const GSVector4i new_drect = GSVector4i(0, new_offset * ds->m_scale, new_size.x * ds->m_scale, new_size.y * ds->m_scale);
ds->ResizeTexture(new_size.x, new_size.y, true, true, new_drect);
if (src && src->m_from_target && src->m_from_target == ds && src->m_target_direct)
{
src->m_texture = ds->m_texture;
// If we've moved it and the source is expecting to be inside this target, we need to update the region to point to it.
int max_region_y = src->m_region.GetMaxY() + new_offset;
if (max_region_y == new_offset)
max_region_y = new_size.y;
src->m_region.SetY(src->m_region.GetMinY() + new_offset, max_region_y);
}
ds->m_valid.y += new_offset;
ds->m_valid.w += new_offset;
ds->m_drawn_since_read.y += new_offset;
ds->m_drawn_since_read.w += new_offset;
g_texture_cache->CombineAlignedInsideTargets(ds, src);
if (ds->m_dirty.size())
{
for (int i = 0; i < static_cast<int>(ds->m_dirty.size()); i++)
{
ds->m_dirty[i].r.y += new_offset;
ds->m_dirty[i].r.w += new_offset;
}
}
t_size.y += std::abs(vertical_offset);
vertical_offset = 0;
}
if (horizontal_offset < 0)
{
// Thankfully this doesn't really happen, but catwoman moves the framebuffer backwards 1 page with a channel shuffle, which is really messy and not easy to deal with.
// Hopefully the quick channel shuffle will just guess this and run with it.
ds->m_TEX0.TBP0 += horizontal_offset;
horizontal_offset = 0;
}
if (vertical_offset || horizontal_offset)
{
GSVertex* v = &m_vertex.buff[0];
for (u32 i = 0; i < m_vertex.tail; i++)
{
v[i].XYZ.X += horizontal_offset << 4;
v[i].XYZ.Y += vertical_offset << 4;
}
if (texture_offset && src && src->m_from_target && src->m_target_direct && src->m_from_target == ds)
{
GSVector4i src_region = src->GetRegionRect();
if (src_region.rempty())
{
src_region = GSVector4i::loadh(ds->m_unscaled_size);
src_region.y += texture_offset;
}
else
{
src_region.y += texture_offset;
src_region.w += texture_offset;
}
src->m_region.SetX(src_region.x, src_region.z);
src->m_region.SetY(src_region.y, src_region.w);
}
m_context->scissor.in.x += horizontal_offset;
m_context->scissor.in.z += horizontal_offset;
m_context->scissor.in.y += vertical_offset;
m_context->scissor.in.w += vertical_offset;
m_r.y += vertical_offset;
m_r.w += vertical_offset;
m_r.x += horizontal_offset;
m_r.z += horizontal_offset;
m_in_target_draw = ds->m_TEX0.TBP0 != m_cached_ctx.ZBUF.Block();
m_vt.m_min.p.x += horizontal_offset;
m_vt.m_max.p.x += horizontal_offset;
m_vt.m_min.p.y += vertical_offset;
m_vt.m_max.p.y += vertical_offset;
t_size.y = ds->m_unscaled_size.y - vertical_offset;
t_size.x = ds->m_unscaled_size.x - horizontal_offset;
}
// Don't resize if the BPP don't match.
GSVector2i new_size = GetValidSize(src, possible_shuffle);
if (new_size.x > ds->m_unscaled_size.x || new_size.y > ds->m_unscaled_size.y)
{
const u32 new_width = std::max(new_size.x, ds->m_unscaled_size.x);
const u32 new_height = std::max(new_size.y, ds->m_unscaled_size.y);
//DevCon.Warning("HW: Resizing texture %d x %d draw %d", ds->m_unscaled_size.x, new_height, s_n);
ds->ResizeTexture(new_width, new_height);
}
else if ((IsPageCopy() || is_possible_mem_clear) && m_r.width() <= zbuf_psm.pgs.x && m_r.height() <= zbuf_psm.pgs.y)
{
const int get_next_ctx = m_env.PRIM.CTXT;
const GSDrawingContext& next_ctx = m_env.CTXT[get_next_ctx];
GSVector4i update_valid = GSVector4i::loadh(GSVector2i(horizontal_offset + GSLocalMemory::m_psm[m_cached_ctx.ZBUF.PSM].pgs.x, GSLocalMemory::m_psm[m_cached_ctx.ZBUF.PSM].pgs.y + vertical_offset));
ds->UpdateValidity(update_valid, true);
if (is_possible_mem_clear)
{
if ((horizontal_offset + GSLocalMemory::m_psm[m_cached_ctx.ZBUF.PSM].pgs.x) >= static_cast<int>(ds->m_TEX0.TBW * 64) && next_ctx.ZBUF.Block() == (m_cached_ctx.ZBUF.Block() + 0x20))
{
update_valid.x = 0;
update_valid.z = GSLocalMemory::m_psm[m_cached_ctx.ZBUF.PSM].pgs.x;
update_valid.y += GSLocalMemory::m_psm[m_cached_ctx.ZBUF.PSM].pgs.y;
update_valid.w += GSLocalMemory::m_psm[m_cached_ctx.ZBUF.PSM].pgs.y;
ds->UpdateValidity(update_valid, true);
}
}
}
}
}
if (!no_rt)

View File

@@ -228,8 +228,8 @@ bool GSTextureCache::CanTranslate(u32 bp, u32 bw, u32 spsm, GSVector4i r, u32 db
{
const GSVector2i src_page_size = GSLocalMemory::m_psm[spsm].pgs;
const GSVector2i dst_page_size = GSLocalMemory::m_psm[dpsm].pgs;
const bool bp_page_aligned_bp = ((bp & ~((1 << 5) - 1)) == bp) || bp == dbp;
const bool block_layout_match = GSLocalMemory::m_psm[spsm].bpp == GSLocalMemory::m_psm[dpsm].bpp;
const bool bp_page_aligned_bp = ((bp & ~((1 << 5) - 1)) == bp) || bp == dbp || (block_layout_match && bw == dbw);
const GSVector4i page_mask(GSVector4i((src_page_size.x - 1), (src_page_size.y - 1)).xyxy());
const GSVector4i masked_rect(r & ~page_mask);
const int src_pixel_width = static_cast<int>(bw * 64);
@@ -276,7 +276,7 @@ GSVector4i GSTextureCache::TranslateAlignedRectByPage(u32 tbp, u32 tebp, u32 tbw
int page_offset = (static_cast<int>(sbp) - static_cast<int>(tbp)) >> 5;
int block_offset = (static_cast<int>(sbp) - static_cast<int>(tbp)) & 0x1F;
if (!(s_psm.bpp == t_psm.bpp))
if (!(s_psm.bpp == t_psm.bpp) || block_offset)
{
if (block_offset)
in_rect = in_rect.ralign<Align_Outside>(s_psm.bs);
@@ -299,6 +299,33 @@ GSVector4i GSTextureCache::TranslateAlignedRectByPage(u32 tbp, u32 tebp, u32 tbw
DevCon.Warning("Error translating rect");
return GSVector4i::zero();
}
const bool block_matched_format = s_psm.bpp == t_psm.bpp && (sbw == tbw || sbw == 1);
// If there is block offset left over, try to adjust to that.
if (block_matched_format)
{
GSVector4i b2a_offset = GSVector4i::zero();
const GSVector4i target_rect = GSVector4i(0, 0, src_bw, 2048);
bool offset_found = false;
for (b2a_offset.x = target_rect.x; b2a_offset.x < target_rect.z; b2a_offset.x += s_psm.bs.x)
{
for (b2a_offset.y = target_rect.y; b2a_offset.y < target_rect.w; b2a_offset.y += s_psm.bs.y)
{
const u32 a_candidate_bp = s_psm.info.bn(b2a_offset.x, b2a_offset.y, tbp, sbw);
if (sbp == a_candidate_bp)
{
offset_found = true;
break;
}
}
if (offset_found)
break;
}
if (offset_found)
in_rect = (in_rect + b2a_offset.xyxy()).max_i32(GSVector4i(0));
}
}
GSVector4i new_rect = GSVector4i::zero();
@@ -618,7 +645,7 @@ void GSTextureCache::DirtyRectByPage(u32 sbp, u32 spsm, u32 sbw, Target* t, GSVe
int block_offset = static_cast<int>(sbp) - static_cast<int>(target_bp);
// Different format needs to be page aligned, unless the block layout matches, then we can block align
// Might be able to translate the original rect.
if (!(src_info->bpp == dst_info->bpp))
if (!(src_info->bpp == dst_info->bpp) || block_offset)
{
const int src_bpp = src_info->bpp;
const bool column_align = !block_offset && src_r.z <= src_info->cs.x && src_r.w <= src_info->cs.y && src_info->depth == dst_info->depth;
@@ -741,7 +768,7 @@ void GSTextureCache::DirtyRectByPage(u32 sbp, u32 spsm, u32 sbw, Target* t, GSVe
const int yblocks = in_rect.height() / src_info->bs.y;
// if !(block_offset & 0x7) is false, this is technically incorrect, but FFX hates it and starts making a mess, so it's better this way without adding complexity.
// TODO maybe: Add per block invalidation? ugh, would have to keep that to small blocks. 2 blocks in the case of FFX.
if ((xblocks <= 4 && yblocks <= 2) || req_depth_offset)
if ((xblocks <= (src_info->pgs.x / src_info->bs.x) && yblocks <= (src_info->pgs.y / src_info->bs.y)) || req_depth_offset)
{
GSVector4i b2a_offset = GSVector4i::zero();
const GSVector4i target_rect = GSVector4i(0, 0, src_width, 2048);
@@ -1165,8 +1192,8 @@ GSTextureCache::Source* GSTextureCache::LookupDepthSource(const bool is_depth, c
if (inside_target)
{
// Need to set it up as a region target.
src->m_region.SetX(block_boundary_rect.x, src->m_from_target->m_valid.z);
src->m_region.SetY(block_boundary_rect.y, src->m_from_target->m_valid.w);
src->m_region.SetX(block_boundary_rect.x, std::max(src->m_from_target->m_valid.z, block_boundary_rect.z));
src->m_region.SetY(block_boundary_rect.y, std::max(src->m_from_target->m_valid.w, block_boundary_rect.w));
}
if (GSRendererHW::GetInstance()->IsTBPFrameOrZ(dst->m_TEX0.TBP0))
@@ -1279,11 +1306,6 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
// Make sure it is page aligned, otherwise things get messy with the pixel order (Tomb Raider Legend).
if (t->m_used)
{
// If the BP is block offset it's unlikely to be a target, but a target can be made by a HW move, so we need to check for a match.
// Good for Baldurs Gate Dark Alliance (HW Move), bad for Tomb Raider Legends (just offset).
if (((bp & (BLOCKS_PER_PAGE - 1)) != (t->m_TEX0.TBP0 & (BLOCKS_PER_PAGE - 1))) && (bp & (BLOCKS_PER_PAGE - 1)))
continue;
//const bool overlaps = t->Inside(bp, bw, psm, block_boundary_rect);
const bool overlaps = t->Overlaps(bp, bw, psm, block_boundary_rect);
// Try to make sure the target has available what we need, be careful of self referencing frames with font in the alpha.
@@ -1293,6 +1315,12 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
if (!overlaps || (found_t && (GSState::s_n - dst->m_last_draw) < (GSState::s_n - t->m_last_draw)))
continue;
// If the BP is offset in to a page and the format does not match, trying to match up the correct position is very difficult since we don't swizzle.
// Tomb Raider Legends does a block level BP in PSMT8 over a C16 target, which is just a nightmare to get right.
// Baldurs Gate used to have this too, but now we can translate HW moves inside targets when the format matches.
if (((bp & (BLOCKS_PER_PAGE - 1)) != (t->m_TEX0.TBP0 & (BLOCKS_PER_PAGE - 1))) && (bp & (BLOCKS_PER_PAGE - 1)) && !CanTranslate(bp, bw, psm, block_boundary_rect, t->m_TEX0.TBP0, t->m_TEX0.PSM, t->m_TEX0.TBW))
continue;
const bool width_match = (std::max(64U, bw * 64U) >> GSLocalMemory::m_psm[psm].info.pageShiftX()) ==
(std::max(64U, t->m_TEX0.TBW * 64U) >> GSLocalMemory::m_psm[t->m_TEX0.PSM].info.pageShiftX());
@@ -1617,8 +1645,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 +1674,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);
@@ -1666,7 +1693,7 @@ 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 && (GSUtil::GetChannelMask(t->m_TEX0.PSM) != 0xF ||
(GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp == 32 && (!(block_boundary_rect.w <= GSLocalMemory::m_psm[psm].pgs.y &&
((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)))))
{
@@ -1773,10 +1800,6 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
if (!t->HasValidBitsForFormat(psm, req_color, req_alpha, rt_tbw == TEX0.TBW) && !(possible_shuffle && GSLocalMemory::m_psm[psm].bpp == 16 && GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp == 32))
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 && frame.Block() != TEX0.TBP0 && !t->Inside(bp, bw, psm, block_boundary_rect))
continue;
x_offset = rect.x;
y_offset = rect.y;
dst = t;
@@ -2007,7 +2030,6 @@ 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.
@@ -3545,11 +3567,11 @@ bool GSTextureCache::PreloadTarget(GIFRegTEX0 TEX0, const GSVector2i& size, cons
continue;
}
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;
const int height_adjust = ((((dst_end_block + 31) - 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_end_block;
t->m_TEX0.TBP0 = GSLocalMemory::GetStartBlockAddress(t->m_TEX0.TBP0, t->m_TEX0.TBW, t->m_TEX0.PSM, GSVector4i(0, height_adjust, t->m_valid.z, t->m_valid.w));
t->m_valid.w -= height_adjust;
t->ResizeValidity(t->m_valid);
@@ -4735,7 +4757,7 @@ bool GSTextureCache::Move(u32 SBP, u32 SBW, u32 SPSM, int sx, int sy, u32 DBP, u
// Not even going to go down the rabbit hole of palette formats on the GPU.. We shouldn't have any targets with P4/P8 anyway.
const GSLocalMemory::psm_t& spsm_s = GSLocalMemory::m_psm[SPSM];
const GSLocalMemory::psm_t& dpsm_s = GSLocalMemory::m_psm[DPSM];
if (SPSM != DPSM || ((spsm_s.pal + dpsm_s.pal) != 0 && !alpha_only))
if (GSLocalMemory::m_psm[SPSM].bpp != GSLocalMemory::m_psm[DPSM].bpp || ((spsm_s.pal + dpsm_s.pal) != 0 && !alpha_only))
{
GL_CACHE("TC: Skipping HW move from 0x%X to 0x%X with SPSM=%s DPSM=%s", SBP, DBP, psm_str(SPSM), psm_str(DPSM));
return false;
@@ -4751,6 +4773,7 @@ bool GSTextureCache::Move(u32 SBP, u32 SBW, u32 SPSM, int sx, int sy, u32 DBP, u
return false;
}
bool req_resize = false;
if (m_expected_src_bp == static_cast<int>(SBP) && m_expected_dst_bp == static_cast<int>(DBP))
{
// Get the new position so we can work out the offset.
@@ -4761,7 +4784,7 @@ bool GSTextureCache::Move(u32 SBP, u32 SBW, u32 SPSM, int sx, int sy, u32 DBP, u
sy += rect_offset.y;
dx += rect_offset.x;
dy += rect_offset.y;
req_resize = true;
GL_INS("TC: Detected striped move, realigning from SBP %x->%x DBP %x->%x", SBP, m_remembered_src_bp, DBP, m_remembered_dst_bp);
SBP = m_remembered_src_bp;
@@ -4806,15 +4829,18 @@ bool GSTextureCache::Move(u32 SBP, u32 SBW, u32 SPSM, int sx, int sy, u32 DBP, u
const GSVector2i target_size = GetTargetSize(DBP, DBW, DPSM, Common::AlignUpPow2(w, 64), h);
dst = LookupTarget(new_TEX0, target_size, src->m_scale, src->m_type);
if (!dst)
dst = CreateTarget(new_TEX0, target_size, target_size, src->m_scale, src->m_type);
{
dst = Target::Create(new_TEX0, target_size.x, target_size.y, src->m_scale, GSLocalMemory::m_psm[DPSM].depth, true);
if (!dst) [[unlikely]]
return false;
}
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));
req_resize = true;
// 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;
}
}
@@ -4829,6 +4855,24 @@ bool GSTextureCache::Move(u32 SBP, u32 SBW, u32 SPSM, int sx, int sy, u32 DBP, u
if (!src || !dst || src->m_scale != dst->m_scale)
return false;
// If we have an offset, adjust the base positions
if (src->m_TEX0.TBP0 != SBP)
{
GSVector4i offset = TranslateAlignedRectByPage(dst, SBP, SPSM, SBW, GSVector4i(0, 1).xxyy(), false);
sx += offset.x;
sy += offset.y;
}
if (dst->m_TEX0.TBP0 != DBP)
{
GSVector4i offset = TranslateAlignedRectByPage(dst, DBP, DPSM, DBW, GSVector4i(0, 1).xxyy(), false);
dx += offset.x;
dy += offset.y;
req_resize = true;
}
// Scale coordinates.
const float scale = src->m_scale;
const int scaled_sx = static_cast<int>(sx * scale);
@@ -4842,6 +4886,11 @@ bool GSTextureCache::Move(u32 SBP, u32 SBW, u32 SPSM, int sx, int sy, u32 DBP, u
if ((scaled_sx + scaled_w) > src->m_texture->GetWidth() || (scaled_sy + scaled_h) > src->m_texture->GetHeight())
return false;
if (req_resize)
{
const GSVector2i target_size = GetTargetSize(DBP, DBW, DPSM, Common::AlignUpPow2(dx + w, 64), dx + h);
dst->ResizeTexture(std::max(dst->m_unscaled_size.x, target_size.x), std::max(dst->m_unscaled_size.y, target_size.y));
}
// We don't want to copy "old" data that the game has overwritten with writes,
// so flush any overlapping dirty area.
src->UpdateIfDirtyIntersects(GSVector4i(sx, sy, sx + w, sy + h));
@@ -4850,6 +4899,9 @@ bool GSTextureCache::Move(u32 SBP, u32 SBW, u32 SPSM, int sx, int sy, u32 DBP, u
// we're not going to be able to use it as a source.
dst->Update();
// Invalidate any opposite targets.
g_texture_cache->InvalidateVideoMemType(GSTextureCache::DepthStencil - dst->m_type, dst->m_TEX0.TBP0);
// Expand the target when we used a more conservative size.
const int required_dh = scaled_dy + scaled_h;
if ((scaled_dx + scaled_w) <= dst->m_texture->GetWidth() && required_dh > dst->m_texture->GetHeight())
@@ -4938,9 +4990,30 @@ bool GSTextureCache::Move(u32 SBP, u32 SBW, u32 SPSM, int sx, int sy, u32 DBP, u
{
if (SPSM == PSMT8H && SPSM == DPSM)
{
ShaderConvert shader = ShaderConvert::COPY;
const GSVector4 src_rect = GSVector4(scaled_sx, scaled_sy, scaled_sx + scaled_w, scaled_sy + scaled_h) / (GSVector4(src->m_texture->GetSize()).xyxy());
const GSVector4 dst_rect = GSVector4(scaled_dx, scaled_dy, (scaled_dx + scaled_w), (scaled_dy + scaled_h));
g_gs_device->StretchRect(src->m_texture, src_rect, dst->m_texture, dst_rect, false, false, false, true);
g_gs_device->StretchRect(src->m_texture, src_rect, dst->m_texture, dst_rect, false, false, false, true, shader);
}
else if (src->m_type != dst->m_type)
{
ShaderConvert shader = dst->m_type ? ShaderConvert::RGBA8_TO_FLOAT32 : ShaderConvert::FLOAT32_TO_RGBA8;
switch (dpsm_s.trbpp)
{
case 24:
shader = dst->m_type ? ShaderConvert::RGBA8_TO_FLOAT24 : ShaderConvert::FLOAT32_TO_RGB8;
break;
case 16:
shader = dst->m_type ? ShaderConvert::RGB5A1_TO_FLOAT16 : ShaderConvert::FLOAT16_TO_RGB5A1;
break;
default:
break;
}
const GSVector4 src_rect = GSVector4(scaled_sx, scaled_sy, scaled_sx + scaled_w, scaled_sy + scaled_h) / (GSVector4(src->m_texture->GetSize()).xyxy());
g_gs_device->StretchRect(src->m_texture, src_rect, dst->m_texture, GSVector4(scaled_sx, scaled_sy, scaled_sx + scaled_w, scaled_sy + scaled_h), shader, false);
}
else
{
@@ -4968,7 +5041,8 @@ bool GSTextureCache::Move(u32 SBP, u32 SBW, u32 SPSM, int sx, int sy, u32 DBP, u
dst->m_alpha_range |= src->m_alpha_range;
}
if (GSLocalMemory::IsPageAligned(src->m_TEX0.PSM, GSVector4i(sx, sy, sx + w, sy + h)) && (w == GSLocalMemory::m_psm[src->m_TEX0.PSM].pgs.x || h == GSLocalMemory::m_psm[src->m_TEX0.PSM].pgs.y))
u32 page_mask = GSLocalMemory::IsPageAlignedMasked(src->m_TEX0.PSM, GSVector4i(sx, sy, sx + w, sy + h));
if (((page_mask & 0x0f0f) == 0x0f0f || (page_mask & 0xf0f0) == 0xf0f0) && (w == GSLocalMemory::m_psm[src->m_TEX0.PSM].pgs.x || h == GSLocalMemory::m_psm[src->m_TEX0.PSM].pgs.y))
{
// Vertical Strips
if (w == GSLocalMemory::m_psm[src->m_TEX0.PSM].pgs.x)
@@ -5003,7 +5077,7 @@ bool GSTextureCache::Move(u32 SBP, u32 SBW, u32 SPSM, int sx, int sy, u32 DBP, u
// Invalidate any sources that overlap with the target (since they're now stale).
InvalidateVideoMem(g_gs_renderer->m_mem.GetOffset(DBP, DBW, DPSM), GSVector4i(dx, dy, dx + w, dy + h), false);
CombineAlignedInsideTargets(dst);
return true;
}
@@ -5190,7 +5264,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 - t->m_TEX0.TBP0) >> 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 & 0x1f) && (((BP - t->m_TEX0.TBP0) >> 5) % tgt_bw) == 0)) && tgt_bw == BW && t->UnwrappedEndBlock() >= end_bp)
{
rts.MoveFront(it.Index());
return t;
@@ -5667,7 +5741,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
@@ -5737,7 +5811,11 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
{
// 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;
src->m_unscaled_size.y = dst->m_unscaled_size.y * 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;
}
@@ -5908,7 +5986,8 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
// Adjust to match a PSMT8 texture (coordinates are double C32, we shouldn't be converting from anything else).
x_offset *= 2;
y_offset *= 2;
if (GSLocalMemory::m_psm[dst->m_TEX0.PSM].bpp == 32)
y_offset *= 2;
src->m_region.SetX(x_offset, x_offset + tw);
src->m_region.SetY(y_offset, y_offset + th);
@@ -6695,6 +6774,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
@@ -6720,27 +6801,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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -350,10 +350,11 @@ bool GSDeviceOGL::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
return false;
m_convert.ps[i].SetFormattedName("Convert pipe %s", name);
if (static_cast<ShaderConvert>(i) == ShaderConvert::RGBA_TO_8I)
if (static_cast<ShaderConvert>(i) == ShaderConvert::RGBA_TO_8I || static_cast<ShaderConvert>(i) == ShaderConvert::RGB5A1_TO_8I)
{
m_convert.ps[i].RegisterUniform("SBW");
m_convert.ps[i].RegisterUniform("DBW");
m_convert.ps[i].RegisterUniform("PSM");
m_convert.ps[i].RegisterUniform("ScaleFactor");
}
else if (static_cast<ShaderConvert>(i) == ShaderConvert::YUV)
@@ -1594,12 +1595,13 @@ void GSDeviceOGL::ConvertToIndexedTexture(GSTexture* sTex, float sScale, u32 off
{
CommitClear(sTex, false);
const ShaderConvert shader = ShaderConvert::RGBA_TO_8I;
const ShaderConvert shader = ((SPSM & 0xE) == 0) ? ShaderConvert::RGBA_TO_8I : ShaderConvert::RGB5A1_TO_8I;
GLProgram& prog = m_convert.ps[static_cast<int>(shader)];
prog.Bind();
prog.Uniform1ui(0, SBW);
prog.Uniform1ui(1, DBW);
prog.Uniform1f(2, sScale);
prog.Uniform1ui(2, SPSM);
prog.Uniform1f(3, sScale);
OMSetDepthStencilState(m_convert.dss);
OMSetBlendState(false);

View File

@@ -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);

View File

@@ -857,7 +857,7 @@ void AudioStreamParameters::LoadSave(SettingsWrapper& wrap, const char* section)
wrap.EnumEntry(section, "ExpansionMode", expansion_mode, &AudioStream::ParseExpansionMode, &AudioStream::GetExpansionModeName, DEFAULT_EXPANSION_MODE);
minimal_output_latency = wrap.EntryBitBool(section, "OutputLatencyMinimal", DEFAULT_OUTPUT_LATENCY_MINIMAL);
buffer_ms = static_cast<u16>(std::clamp<int>(wrap.EntryBitfield(section, "BufferMS", buffer_ms, DEFAULT_BUFFER_MS), 0, std::numeric_limits<u16>::max()));
output_latency_ms = static_cast<u16>(std::clamp<int>(wrap.EntryBitfield(section, "OutputLatencyMS", buffer_ms, DEFAULT_OUTPUT_LATENCY_MS), 0, std::numeric_limits<u16>::max()));
output_latency_ms = static_cast<u16>(std::clamp<int>(wrap.EntryBitfield(section, "OutputLatencyMS", output_latency_ms, DEFAULT_OUTPUT_LATENCY_MS), 0, std::numeric_limits<u16>::max()));
stretch_sequence_length_ms = static_cast<u16>(std::clamp<int>(wrap.EntryBitfield(section, "StretchSequenceLengthMS", DEFAULT_STRETCH_SEQUENCE_LENGTH), 0, std::numeric_limits<u16>::max()));
stretch_seekwindow_ms = static_cast<u16>(std::clamp<int>(wrap.EntryBitfield(section, "StretchSeekWindowMS", DEFAULT_STRETCH_SEEKWINDOW), 0, std::numeric_limits<u16>::max()));

View File

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

View File

@@ -21,6 +21,7 @@
#include <cstring>
#include <fmt/format.h>
#include <sys/stat.h>
#include <algorithm>
#include <fcntl.h>
@@ -641,7 +642,7 @@ namespace R3000A
for (size_t i = 0; i < handles.size(); i++)
{
if (handles[i].fd_index == (u32) fd - firstfd)
if (handles[i].fd_index == (u32)fd - firstfd)
{
handles.erase(handles.begin() + i);
break;
@@ -928,12 +929,6 @@ namespace R3000A
{
int Kprintf_HLE()
{
// Using sprintf here is a bit nasty, but it has a large buffer..
// Don't feel like rewriting it.
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif
// Emulate the expected Kprintf functionality:
iopMemWrite32(sp, a0);
@@ -951,8 +946,12 @@ namespace R3000A
if (!ConsoleLogging.iopConsole.IsActive())
return 1;
char tmp[1024], tmp2[1024];
// maximum allowed size for our buffer before we truncate
const unsigned int max_len = 4096;
char tmp[max_len], tmp2[max_len];
char* ptmp = tmp;
unsigned int printed_bytes = 0;
int remaining_buf = max_len - 1;
int n = 1, i = 0, j = 0;
while (fmt[i])
@@ -963,21 +962,32 @@ namespace R3000A
j = 0;
tmp2[j++] = '%';
_start:
switch (fmt[++i])
// let's check whether this is our null terminator
// before allowing the parser to proceed
if (fmt[i + 1])
{
case '.':
case 'l':
tmp2[j++] = fmt[i];
goto _start;
default:
if (fmt[i] >= '0' && fmt[i] <= '9')
{
switch (fmt[++i])
{
case '.':
case 'l':
if (j >= max_len)
break;
tmp2[j++] = fmt[i];
goto _start;
}
break;
default:
if (fmt[i] >= '0' && fmt[i] <= '9')
{
if (j >= max_len)
break;
tmp2[j++] = fmt[i];
goto _start;
}
break;
}
}
if (j >= max_len)
break;
tmp2[j++] = fmt[i];
tmp2[j] = 0;
@@ -985,7 +995,9 @@ namespace R3000A
{
case 'f':
case 'F':
ptmp += sprintf(ptmp, tmp2, (float)iopMemRead32(sp + n * 4));
printed_bytes = std::min(remaining_buf, snprintf(ptmp, remaining_buf, tmp2, (float)iopMemRead32(sp + n * 4)));
remaining_buf -= printed_bytes;
ptmp += printed_bytes;
n++;
break;
@@ -995,7 +1007,9 @@ namespace R3000A
case 'E':
case 'g':
case 'G':
ptmp += sprintf(ptmp, tmp2, (double)iopMemRead32(sp + n * 4));
printed_bytes = std::min(remaining_buf, snprintf(ptmp, remaining_buf, tmp2, (double)iopMemRead32(sp + n * 4)));
remaining_buf -= printed_bytes;
ptmp += printed_bytes;
n++;
break;
@@ -1007,19 +1021,27 @@ namespace R3000A
case 'O':
case 'x':
case 'X':
ptmp += sprintf(ptmp, tmp2, (u32)iopMemRead32(sp + n * 4));
case 'u':
case 'U':
printed_bytes = std::min(remaining_buf, snprintf(ptmp, remaining_buf, tmp2, (u32)iopMemRead32(sp + n * 4)));
remaining_buf -= printed_bytes;
ptmp += printed_bytes;
n++;
break;
case 'c':
ptmp += sprintf(ptmp, tmp2, (u8)iopMemRead32(sp + n * 4));
printed_bytes = std::min(remaining_buf, snprintf(ptmp, remaining_buf, tmp2, (u8)iopMemRead32(sp + n * 4)));
remaining_buf -= printed_bytes;
ptmp += printed_bytes;
n++;
break;
case 's':
{
std::string s = iopMemReadString(iopMemRead32(sp + n * 4));
ptmp += sprintf(ptmp, tmp2, s.data());
printed_bytes = std::min(remaining_buf, snprintf(ptmp, remaining_buf, tmp2, s.data()));
remaining_buf -= printed_bytes;
ptmp += printed_bytes;
n++;
}
break;
@@ -1043,10 +1065,6 @@ namespace R3000A
iopConLog(ShiftJIS_ConvertString(tmp, 1023));
return 1;
#ifdef __clang__
#pragma clang diagnostic pop
#endif
}
} // namespace sysmem

View File

@@ -2084,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);
@@ -2111,8 +2110,10 @@ bool EmuFolders::SetResourcesDirectory()
#endif
#else
// On macOS, this is in the bundle resources directory.
const std::string program_path = FileSystem::GetProgramPath();
Resources = Path::Canonicalize(Path::Combine(Path::GetDirectory(program_path), "../Resources"));
if (auto resources = CocoaTools::GetResourcePath())
Resources = *resources;
else
Resources = Path::Combine(AppRoot, "resources");
#endif
Console.WriteLnFmt("Resources Directory: {}", Resources);

View File

@@ -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;