Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
6d40d9fb94
|
@ -38,11 +38,6 @@ States_DefrostCurrentSlotBackup = Shift-F3
|
|||
States_CycleSlotForward = F2
|
||||
States_CycleSlotBackward = Shift-F2
|
||||
|
||||
Frameskip_Toggle = Shift-F4
|
||||
Framelimiter_TurboToggle = TAB
|
||||
Framelimiter_SlomoToggle = Shift-TAB
|
||||
Framelimiter_MasterToggle = F4
|
||||
|
||||
FullscreenToggle = Alt-ENTER
|
||||
|
||||
# Note: toggles suspend, but can resume only if the GS window is not set to hide
|
||||
|
@ -61,23 +56,11 @@ Sys_RecordingToggle = F12
|
|||
|
||||
GSwindow_CycleAspectRatio = F6
|
||||
|
||||
# Whole picture zoom in/out
|
||||
GSwindow_ZoomIn = Ctrl-KP_ADD
|
||||
GSwindow_ZoomOut = Ctrl-KP_SUBTRACT
|
||||
GSwindow_ZoomToggle = Ctrl-KP_MULTIPLY
|
||||
|
||||
# Vertical stretch/squash
|
||||
GSwindow_ZoomInY = Alt-Ctrl-KP_ADD
|
||||
GSwindow_ZoomOutY = Alt-Ctrl-KP_SUBTRACT
|
||||
GSwindow_ZoomResetY = Alt-Ctrl-KP_MULTIPLY
|
||||
|
||||
# Move the whole image
|
||||
GSwindow_OffsetYminus = Alt-Ctrl-UP
|
||||
GSwindow_OffsetYplus = Alt-Ctrl-DOWN
|
||||
GSwindow_OffsetXminus = Alt-Ctrl-LEFT
|
||||
GSwindow_OffsetXplus = Alt-Ctrl-RIGHT
|
||||
GSwindow_OffsetReset = Alt-Ctrl-KP_DIVIDE
|
||||
|
||||
# Recording Bindings
|
||||
# Note - These are disabled if 'System > Enable Recording Tools' is disabled
|
||||
FrameAdvance = SPACE
|
||||
|
|
|
@ -581,6 +581,7 @@ SCAJ-20020:
|
|||
eeClampMode: 3 # Characters are visible in-game.
|
||||
gsHWFixes:
|
||||
texturePreloading: 1 # Performs better with partial preload because it is slow on locations outside gameplay foremost.
|
||||
mergeSprite: 1 # Fixes misaligned white lines.
|
||||
SCAJ-20021:
|
||||
name: "Metal Slug 3"
|
||||
region: "NTSC-Unk"
|
||||
|
@ -1584,7 +1585,6 @@ SCAJ-30010:
|
|||
gsHWFixes:
|
||||
alignSprite: 1 # Fixes water vertical lines.
|
||||
roundSprite: 1 # Fixes vertical lines and minor ghosting.
|
||||
wrapGSMem: 1 # Fixes FMV when in progressive mode.
|
||||
autoFlush: 1 # Fixes sun going through walls.
|
||||
SCAJ-30011:
|
||||
name: "God of War II"
|
||||
|
@ -2986,7 +2986,7 @@ SCES-50602:
|
|||
name: "Monsters Inc."
|
||||
region: "PAL-Unk"
|
||||
SCES-50603:
|
||||
name: "Monstruos, S.A. - La Isla de los Sustos"
|
||||
name: "Monstruos, S.A. - Isla de los sustos"
|
||||
region: "PAL-S"
|
||||
SCES-50604:
|
||||
name: "Monsters, Inc - Skrämmarön"
|
||||
|
@ -3272,7 +3272,6 @@ SCES-51533:
|
|||
gsHWFixes:
|
||||
alignSprite: 1 # Fixes water vertical lines.
|
||||
roundSprite: 1 # Fixes vertical lines and minor ghosting.
|
||||
wrapGSMem: 1 # Fixes FMV when in progressive mode.
|
||||
autoFlush: 1 # Fixes sun going through walls.
|
||||
SCES-51578:
|
||||
name: "Network Access Disc [v1.03 - v6.00]"
|
||||
|
@ -3739,7 +3738,6 @@ SCES-53133:
|
|||
gsHWFixes:
|
||||
alignSprite: 1 # Fixes water vertical lines.
|
||||
roundSprite: 1 # Fixes vertical lines and minor ghosting.
|
||||
wrapGSMem: 1 # Fixes FMV when in progressive mode.
|
||||
autoFlush: 1 # Fixes sun going through walls.
|
||||
SCES-53178:
|
||||
name: "SingStar Pop"
|
||||
|
@ -4013,7 +4011,7 @@ SCES-53883:
|
|||
gameFixes:
|
||||
- EETimingHack # Fixes freezes.
|
||||
SCES-53884:
|
||||
name: "Buzz! The Big Quiz"
|
||||
name: "Buzz! El GRAN Reto"
|
||||
region: "PAL-S"
|
||||
compat: 2
|
||||
gameFixes:
|
||||
|
@ -4167,7 +4165,7 @@ SCES-54261:
|
|||
name: "Buzz! The Sports Quiz"
|
||||
region: "PAL-I"
|
||||
SCES-54262:
|
||||
name: "Buzz! The Sports Quiz"
|
||||
name: "Buzz! - El Gran Concurso de Deportes"
|
||||
region: "PAL-S"
|
||||
SCES-54263:
|
||||
name: "Buzz! The Sports Quiz"
|
||||
|
@ -4504,7 +4502,7 @@ SCES-55255:
|
|||
name: "SingStar Singalong with Disney"
|
||||
region: "PAL-E"
|
||||
SCES-55256:
|
||||
name: "SingStar Canciones Disney"
|
||||
name: "SingStar canciones Disney"
|
||||
region: "PAL-S"
|
||||
SCES-55257:
|
||||
name: "SingStar - Chansons Magiques de Disney"
|
||||
|
@ -4603,7 +4601,7 @@ SCES-55521:
|
|||
name: "SingStar SuomiPop"
|
||||
region: "PAL-FI"
|
||||
SCES-55527:
|
||||
name: "SingStar 2009"
|
||||
name: "SingStar Pop 2009"
|
||||
region: "PAL-S"
|
||||
SCES-55535:
|
||||
name: "Desi Adda - Games of India"
|
||||
|
@ -5272,7 +5270,6 @@ SCKA-30002:
|
|||
gsHWFixes:
|
||||
alignSprite: 1 # Fixes water vertical lines.
|
||||
roundSprite: 1 # Fixes vertical lines and minor ghosting.
|
||||
wrapGSMem: 1 # Fixes FMV when in progressive mode.
|
||||
autoFlush: 1 # Fixes sun going through walls.
|
||||
SCKA-30006:
|
||||
name: "God of War 2"
|
||||
|
@ -7857,7 +7854,6 @@ SCUS-97399:
|
|||
gsHWFixes:
|
||||
alignSprite: 1 # Fixes water vertical lines.
|
||||
roundSprite: 1 # Fixes vertical lines and minor ghosting.
|
||||
wrapGSMem: 1 # Fixes FMV when in progressive mode.
|
||||
autoFlush: 1 # Fixes sun going through walls.
|
||||
SCUS-97400:
|
||||
name: "EyeToy - Groove"
|
||||
|
@ -8147,7 +8143,6 @@ SCUS-97467:
|
|||
gsHWFixes:
|
||||
alignSprite: 1 # Fixes water vertical lines.
|
||||
roundSprite: 1 # Fixes vertical lines and minor ghosting.
|
||||
wrapGSMem: 1 # Fixes FMV when in progressive mode.
|
||||
autoFlush: 1 # Fixes sun going through walls.
|
||||
SCUS-97468:
|
||||
name: "EyeToy - Play 2"
|
||||
|
@ -9853,17 +9848,17 @@ SLES-50225:
|
|||
name: "Escape from Monkey Island"
|
||||
region: "PAL-E"
|
||||
SLES-50226:
|
||||
name: "Escape from Monkey Island 4"
|
||||
name: "Escape from Monkey Island"
|
||||
region: "PAL-F"
|
||||
SLES-50227:
|
||||
name: "Flucht von Monkey Island"
|
||||
region: "PAL-G"
|
||||
compat: 5
|
||||
SLES-50228:
|
||||
name: "Escape from Monkey Island 4"
|
||||
name: "Fuga da Monkey Island"
|
||||
region: "PAL-I"
|
||||
SLES-50229:
|
||||
name: "Monkey Island 4"
|
||||
name: "Fuga de Monkey Island, La"
|
||||
region: "PAL-S"
|
||||
SLES-50230:
|
||||
name: "Lotus Challenge"
|
||||
|
@ -10901,11 +10896,15 @@ SLES-50771:
|
|||
name: "Blood Omen 2 - Legend of Kain"
|
||||
region: "PAL-E"
|
||||
compat: 5
|
||||
roundModes:
|
||||
eeRoundMode: 0 # Fixes pathing.
|
||||
gsHWFixes:
|
||||
mipmap: 1 # Fixes glitching textures.
|
||||
SLES-50772:
|
||||
name: "Blood Omen 2 - Legend of Kain"
|
||||
region: "PAL-M3"
|
||||
roundModes:
|
||||
eeRoundMode: 0 # Fixes pathing.
|
||||
gsHWFixes:
|
||||
mipmap: 1 # Fixes glitching textures.
|
||||
SLES-50773:
|
||||
|
@ -11044,6 +11043,8 @@ SLES-50814:
|
|||
SLES-50815:
|
||||
name: "Blood Omen 2 - Legend of Kain"
|
||||
region: "PAL-G"
|
||||
roundModes:
|
||||
eeRoundMode: 0 # Fixes pathing.
|
||||
gsHWFixes:
|
||||
mipmap: 1 # Fixes glitching textures.
|
||||
SLES-50816:
|
||||
|
@ -11136,14 +11137,14 @@ SLES-50836:
|
|||
gsHWFixes:
|
||||
textureInsideRT: 1
|
||||
SLES-50837:
|
||||
name: "Indiana Jones and The Emperor's Tomb"
|
||||
name: "Indiana Jones et le Tombeau de L'Empereur"
|
||||
region: "PAL-F"
|
||||
gameFixes:
|
||||
- EETimingHack # For texture flicker.
|
||||
gsHWFixes:
|
||||
textureInsideRT: 1
|
||||
SLES-50838:
|
||||
name: "Indiana Jones and The Emperor's Tomb"
|
||||
name: "Indiana Jones und die Legende der Kaisergruft"
|
||||
region: "PAL-G"
|
||||
compat: 5
|
||||
gameFixes:
|
||||
|
@ -11151,14 +11152,14 @@ SLES-50838:
|
|||
gsHWFixes:
|
||||
textureInsideRT: 1
|
||||
SLES-50839:
|
||||
name: "Indiana Jones and The Emperor's Tomb"
|
||||
name: "Indiana Jones e la Tomba dell'Imperatore"
|
||||
region: "PAL-I"
|
||||
gameFixes:
|
||||
- EETimingHack # For texture flicker.
|
||||
gsHWFixes:
|
||||
textureInsideRT: 1
|
||||
SLES-50840:
|
||||
name: "Indiana Jones and The Emperor's Tomb"
|
||||
name: "Indiana Jones y la Tumba del Emperador"
|
||||
region: "PAL-S"
|
||||
gameFixes:
|
||||
- EETimingHack # For texture flicker.
|
||||
|
@ -11514,7 +11515,7 @@ SLES-51026:
|
|||
name: "Football Manager Campionato 2003"
|
||||
region: "PAL-I"
|
||||
SLES-51027:
|
||||
name: "Mánager de Liga 2003"
|
||||
name: "Manager de Liga 2003"
|
||||
region: "PAL-S"
|
||||
SLES-51038:
|
||||
name: "MX Superfly featuring Ricky Carmichael"
|
||||
|
@ -11873,7 +11874,7 @@ SLES-51194:
|
|||
mipmap: 1
|
||||
cpuFramebufferConversion: 1 # Fixes right side of the screen from garbage textures.
|
||||
SLES-51195:
|
||||
name: "Harry Potter y La Camara Secreta"
|
||||
name: "Harry Potter y la Cámara Secreta"
|
||||
region: "PAL-S"
|
||||
gameFixes:
|
||||
- EETimingHack # Fixes flickering textures.
|
||||
|
@ -12081,17 +12082,17 @@ SLES-51253:
|
|||
clampModes:
|
||||
vuClampMode: 3 # Fix white shiny weapons.
|
||||
SLES-51254:
|
||||
name: "Der Herr der Ringe - Die Zwei Tuerme"
|
||||
name: "Herr der Ringe, Der - Die Zwei Türme"
|
||||
region: "PAL-G"
|
||||
clampModes:
|
||||
vuClampMode: 3 # Fix white shiny weapons.
|
||||
SLES-51255:
|
||||
name: "Lord of the Rings, The - The Two Towers"
|
||||
name: "Signore Degli Anelli, Il - Le Due Torri"
|
||||
region: "PAL-I"
|
||||
clampModes:
|
||||
vuClampMode: 3 # Fix white shiny weapons.
|
||||
SLES-51256:
|
||||
name: "El Senor de Los Anillos - Las Dos Torres"
|
||||
name: "Señor de Los Anillos, El - Las Dos Torres"
|
||||
region: "PAL-S"
|
||||
compat: 5
|
||||
clampModes:
|
||||
|
@ -12328,13 +12329,13 @@ SLES-51358:
|
|||
region: "PAL-E"
|
||||
compat: 5
|
||||
SLES-51360:
|
||||
name: "Simpsons Skateboarding"
|
||||
name: "Simpsons Skateboarding, The"
|
||||
region: "PAL-I"
|
||||
SLES-51361:
|
||||
name: "Simpsons Skateboarding"
|
||||
name: "Simpsons Skateboarding, The"
|
||||
region: "PAL-S"
|
||||
SLES-51362:
|
||||
name: "Simpsons Skateboarding"
|
||||
name: "Simpsons Skateboarding, The"
|
||||
region: "PAL-G"
|
||||
SLES-51363:
|
||||
name: "Music 3000"
|
||||
|
@ -12532,7 +12533,7 @@ SLES-51460:
|
|||
name: "Football Manager 2004"
|
||||
region: "PAL-I"
|
||||
SLES-51461:
|
||||
name: "Football Manager 2004"
|
||||
name: "Manager de Liga 2004"
|
||||
region: "PAL-S"
|
||||
SLES-51462:
|
||||
name: "Shrek Super Party"
|
||||
|
@ -12840,7 +12841,7 @@ SLES-51665:
|
|||
name: "Dynasty Warriors 4"
|
||||
region: "PAL-S"
|
||||
SLES-51666:
|
||||
name: "Piglet's Big Game"
|
||||
name: "Piglet - El Gran Juego de Disney"
|
||||
region: "PAL-S"
|
||||
SLES-51667:
|
||||
name: "Piglet's Big Game"
|
||||
|
@ -13270,7 +13271,7 @@ SLES-51853:
|
|||
roundModes:
|
||||
vuRoundMode: 0 # Crashes without.
|
||||
SLES-51854:
|
||||
name: "Tony Hawk Underground"
|
||||
name: "Tony Hawk's Underground"
|
||||
region: "PAL-S"
|
||||
roundModes:
|
||||
vuRoundMode: 0 # Crashes without.
|
||||
|
@ -13280,6 +13281,9 @@ SLES-51855:
|
|||
SLES-51856:
|
||||
name: "Monster Attack"
|
||||
region: "PAL-E"
|
||||
gsHWFixes:
|
||||
mipmap: 2 # Improves distant textures.
|
||||
trilinearFiltering: 1 # Smooths high frequency textures on distant ground.
|
||||
SLES-51859:
|
||||
name: "Billiards Xciting"
|
||||
region: "PAL-E"
|
||||
|
@ -13675,16 +13679,16 @@ SLES-52017:
|
|||
region: "PAL-M5"
|
||||
compat: 5
|
||||
SLES-52018:
|
||||
name: "Der Herr der Ringe, Die Rueckkehr des Koenigs"
|
||||
name: "Herr der Ringe, Der - Die Rückkehr des Königs"
|
||||
region: "PAL-G"
|
||||
SLES-52019:
|
||||
name: "Lord of the Rings, The - Return of the King"
|
||||
name: "Seigneur des Anneaux, Le - Le Retour du roi"
|
||||
region: "PAL-F"
|
||||
SLES-52020:
|
||||
name: "Lord of the Rings, The - The Return of the King"
|
||||
name: "Señor de Los Anillos, El - El Retorno del Rey"
|
||||
region: "PAL-S"
|
||||
SLES-52021:
|
||||
name: "Lord of the Rings, The - The Return of the King"
|
||||
name: "Signore degli Anelli, Il - Il Ritorno del Re"
|
||||
region: "PAL-I"
|
||||
SLES-52022:
|
||||
name: "Total Club Manager 2004"
|
||||
|
@ -13716,11 +13720,11 @@ SLES-52036:
|
|||
region: "PAL-E"
|
||||
compat: 5
|
||||
SLES-52038:
|
||||
name: "Terminator 3 - Rise of the Machines"
|
||||
name: "Terminator 3 - Rebellion der Maschinen"
|
||||
region: "PAL-G"
|
||||
SLES-52039:
|
||||
name: "Terminator 3 - La Rebelión de las Máquinas"
|
||||
region: "PAL-S"
|
||||
name: "Terminator 3 - Rise Of The Machines"
|
||||
region: "PAL-IS"
|
||||
SLES-52041:
|
||||
name: "Detonator"
|
||||
region: "PAL-E"
|
||||
|
@ -14159,7 +14163,7 @@ SLES-52284:
|
|||
name: "Deadly Skies III"
|
||||
region: "PAL-M5"
|
||||
SLES-52286:
|
||||
name: "Tak and The Power of JuJu"
|
||||
name: "Tak y el Poder Juju"
|
||||
region: "PAL-S"
|
||||
compat: 5
|
||||
SLES-52287:
|
||||
|
@ -14224,6 +14228,7 @@ SLES-52322:
|
|||
eeClampMode: 3 # Characters are visible in-game.
|
||||
gsHWFixes:
|
||||
texturePreloading: 1 # Performs better with partial preload because it is slow on locations outside gameplay foremost.
|
||||
mergeSprite: 1 # Fixes misaligned white lines.
|
||||
SLES-52323:
|
||||
name: "Richard Burns Rally"
|
||||
region: "PAL-M5"
|
||||
|
@ -15141,7 +15146,7 @@ SLES-52696:
|
|||
name: "Football Manager Campionato 2005"
|
||||
region: "PAL-I"
|
||||
SLES-52697:
|
||||
name: "Manager de la Liga 2005"
|
||||
name: "Manager de Liga 2005"
|
||||
region: "PAL-S"
|
||||
SLES-52700:
|
||||
name: "Adventures of Jimmy Neutron, The - Boy Genius - Attack of the Twonkies"
|
||||
|
@ -15370,20 +15375,20 @@ SLES-52801:
|
|||
gsHWFixes:
|
||||
halfPixelOffset: 1 # Fixes ghosting in cutscenes.
|
||||
SLES-52802:
|
||||
name: "Lord of the Rings, The - The Third Age"
|
||||
name: "Seigneur des anneaux, Le - Le Tiers Âge"
|
||||
region: "PAL-F"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 1 # Fixes ghosting in cutscenes.
|
||||
SLES-52803:
|
||||
name: "Der Herr der Ringe - Das dritte Zeitalter"
|
||||
name: "Herr der Ringe, Der - Das dritte Zeitalter"
|
||||
region: "PAL-G"
|
||||
SLES-52804:
|
||||
name: "Lord of the Rings, The - The Third Age"
|
||||
name: "Signore degli Anelli, Il - La Terza Era"
|
||||
region: "PAL-I"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 1 # Fixes ghosting in cutscenes.
|
||||
SLES-52805:
|
||||
name: "Lord of the Rings, The - The Third Age"
|
||||
name: "Señor de Los Anillos, El - La Tercera Edad"
|
||||
region: "PAL-S"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 1 # Fixes ghosting in cutscenes.
|
||||
|
@ -15395,7 +15400,7 @@ SLES-52807:
|
|||
gsHWFixes:
|
||||
halfPixelOffset: 1 # Reduces lighting misalignment but doesn't fully fix it.
|
||||
SLES-52808:
|
||||
name: "Lemony Snicket's A Series of Unfortunate Events"
|
||||
name: "Désastreuses Aventures des orphelins Baudelaire, Les"
|
||||
region: "PAL-F"
|
||||
clampModes:
|
||||
eeClampMode: 3 # Fixes the inability to collect items.
|
||||
|
@ -15409,7 +15414,7 @@ SLES-52809:
|
|||
gsHWFixes:
|
||||
halfPixelOffset: 1 # Reduces lighting misalignment but doesn't fully fix it.
|
||||
SLES-52810:
|
||||
name: "Lemony Snicket's Una Serie Disfortunati Eventi"
|
||||
name: "Lemony Snicket Una Serie Di Sfortunati Eventi"
|
||||
region: "PAL-I"
|
||||
clampModes:
|
||||
eeClampMode: 3 # Fixes the inability to collect items.
|
||||
|
@ -15433,7 +15438,7 @@ SLES-52815:
|
|||
name: "Disney-Pixar's The Incredibles"
|
||||
region: "PAL-G"
|
||||
SLES-52816:
|
||||
name: "Incredibles, The"
|
||||
name: "Increíbles, Los"
|
||||
region: "PAL-S"
|
||||
SLES-52820:
|
||||
name: "Incredibles, The"
|
||||
|
@ -15451,7 +15456,7 @@ SLES-52824:
|
|||
name: "Furry Tales"
|
||||
region: "PAL-E-F"
|
||||
SLES-52825:
|
||||
name: "Lemony Snicket's A Series of Unfortunate Events"
|
||||
name: "Serie de Catastróficas Desdichas de Lemony Snicket, Una"
|
||||
region: "PAL-S"
|
||||
clampModes:
|
||||
eeClampMode: 3 # Fixes the inability to collect items.
|
||||
|
@ -15821,7 +15826,7 @@ SLES-52985:
|
|||
name: "Spongebob SquarePants - The Movie"
|
||||
region: "PAL-G"
|
||||
SLES-52986:
|
||||
name: "Spongebob SquarePants - The Movie"
|
||||
name: "Bob Esponja La Película"
|
||||
region: "PAL-S"
|
||||
SLES-52988:
|
||||
name: "Mega Man X8"
|
||||
|
@ -16262,7 +16267,7 @@ SLES-53143:
|
|||
name: "Fantastic Four"
|
||||
region: "PAL-E"
|
||||
SLES-53144:
|
||||
name: "Four Fantastiques, Les"
|
||||
name: "4 Fantastiques, Les"
|
||||
region: "PAL-F"
|
||||
SLES-53145:
|
||||
name: "Fantastic Four"
|
||||
|
@ -16271,7 +16276,7 @@ SLES-53146:
|
|||
name: "I Fantastici Quattro"
|
||||
region: "PAL-I"
|
||||
SLES-53147:
|
||||
name: "Four Fantasticos, Los"
|
||||
name: "4 Fantásticos, Los"
|
||||
region: "PAL-S"
|
||||
SLES-53148:
|
||||
name: "Fruitfall"
|
||||
|
@ -16466,7 +16471,7 @@ SLES-53242:
|
|||
roundSprite: 2 # Improves bloom alignment and clarity.
|
||||
halfPixelOffset: 2 # Improves bloom alignment and clarity.
|
||||
SLES-53246:
|
||||
name: "Dreamworks Madagascar"
|
||||
name: "Madagascar"
|
||||
region: "PAL-S"
|
||||
gsHWFixes:
|
||||
roundSprite: 2 # Improves bloom alignment and clarity.
|
||||
|
@ -17346,7 +17351,7 @@ SLES-53580:
|
|||
name: "NBA Live '06"
|
||||
region: "PAL-F"
|
||||
SLES-53581:
|
||||
name: "NBA Live '06"
|
||||
name: "NBA Live 06"
|
||||
region: "PAL-S"
|
||||
SLES-53582:
|
||||
name: "Bratz - Rock Angelz"
|
||||
|
@ -17652,12 +17657,12 @@ SLES-53706:
|
|||
gsHWFixes:
|
||||
halfPixelOffset: 2 # Fixes lighting misalignment.
|
||||
SLES-53707:
|
||||
name: "Chronicles of Narnia, The - The Lion, The Witch and The Wardrobe"
|
||||
name: "Chroniken von Narnia, Die - Der König von Narnia"
|
||||
region: "PAL-G"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 2 # Fixes lighting misalignment.
|
||||
SLES-53708:
|
||||
name: "Chronicles of Narnia, The - The Lion, The Witch and The Wardrobe"
|
||||
name: "Cronache di Narnia, Le - Il Leone, La Strega e L'Armadio"
|
||||
region: "PAL-I"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 2 # Fixes lighting misalignment.
|
||||
|
@ -17667,7 +17672,7 @@ SLES-53709:
|
|||
gsHWFixes:
|
||||
halfPixelOffset: 2 # Fixes lighting misalignment.
|
||||
SLES-53710:
|
||||
name: "Chronicles of Narnia, The - The Lion, The Witch and The Wardrobe"
|
||||
name: "Crónicas de Narnia, Las - El León, La Bruja y El Armario"
|
||||
region: "PAL-S"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 2 # Fixes lighting misalignment.
|
||||
|
@ -18137,9 +18142,6 @@ SLES-53886:
|
|||
patch=1,EE,0037EBD8,word,4af103bc
|
||||
patch=1,EE,0037EBF0,word,4a800460
|
||||
patch=1,EE,0037EBF4,word,4b7103bc
|
||||
SLES-53897:
|
||||
name: "Dreamworks Vecinos Invasores"
|
||||
region: "PAL-S"
|
||||
SLES-53899:
|
||||
name: "Pro Evolution Soccer Management"
|
||||
region: "PAL-M5"
|
||||
|
@ -18200,7 +18202,7 @@ SLES-53913:
|
|||
name: "Plan, The"
|
||||
region: "PAL-F-I"
|
||||
SLES-53914:
|
||||
name: "Plan, The"
|
||||
name: "Plan, Th3"
|
||||
region: "PAL-S"
|
||||
SLES-53915:
|
||||
name: "Space War Attack"
|
||||
|
@ -18343,12 +18345,12 @@ SLES-53984:
|
|||
patch=1,EE,002FF3F0,word,27BDFEE0
|
||||
patch=1,EE,002FF42C,word,27BD0120
|
||||
SLES-53986:
|
||||
name: "Over the Hedge"
|
||||
name: "gang del bosco, La"
|
||||
region: "PAL-I"
|
||||
speedHacks:
|
||||
MTVUSpeedHack: 0 # Fixes bad graphics due to bad T-Bit handling.
|
||||
SLES-53987:
|
||||
name: "Over the Hedge"
|
||||
name: "Vecinos Invasores"
|
||||
region: "PAL-S"
|
||||
speedHacks:
|
||||
MTVUSpeedHack: 0 # Fixes bad graphics due to bad T-Bit handling.
|
||||
|
@ -18993,7 +18995,7 @@ SLES-54250:
|
|||
name: "NBA Live '07"
|
||||
region: "PAL-F"
|
||||
SLES-54251:
|
||||
name: "NBA Live '07"
|
||||
name: "NBA Live 07"
|
||||
region: "PAL-S"
|
||||
SLES-54252:
|
||||
name: "NBA Live '07"
|
||||
|
@ -19427,7 +19429,7 @@ SLES-54451:
|
|||
name: "Himmel und Huhn - Ace in Action"
|
||||
region: "PAL-G"
|
||||
SLES-54452:
|
||||
name: "Chicken Little - 'As' en Accion"
|
||||
name: "Disney Chicken Little - As en Acción"
|
||||
region: "PAL-S"
|
||||
SLES-54453:
|
||||
name: "Chicken Little - Asso Spaziale!"
|
||||
|
@ -19443,7 +19445,6 @@ SLES-54455:
|
|||
gsHWFixes:
|
||||
halfPixelOffset: 1 # Fixes misaligned bloom effects.
|
||||
mergeSprite: 1 # Fixes blurriness but removes bloom + Recommended to use Shadeboost brightness 80.
|
||||
wrapGSMem: 1 # Fixes FMVs missing video pieces.
|
||||
deinterlace: 8 # Game requires adaptive (or blend) tff deinterlacing instead of auto for the whole game.
|
||||
SLES-54456:
|
||||
name: "Beverly Hills Cop"
|
||||
|
@ -19609,7 +19610,6 @@ SLES-54516:
|
|||
gsHWFixes:
|
||||
halfPixelOffset: 1 # Fixes misaligned bloom effects.
|
||||
mergeSprite: 1 # Fixes blurriness but removes bloom + Recommended to use Shadeboost brightness 80.
|
||||
wrapGSMem: 1 # Fixes FMVs missing video pieces.
|
||||
deinterlace: 8 # Game requires adaptive (or blend) tff deinterlacing instead of auto for the whole game.
|
||||
SLES-54517:
|
||||
name: "Thrillville"
|
||||
|
@ -19617,7 +19617,6 @@ SLES-54517:
|
|||
gsHWFixes:
|
||||
halfPixelOffset: 1 # Fixes misaligned bloom effects.
|
||||
mergeSprite: 1 # Fixes blurriness but removes bloom + Recommended to use Shadeboost brightness 80.
|
||||
wrapGSMem: 1 # Fixes FMVs missing video pieces.
|
||||
deinterlace: 8 # Game requires adaptive (or blend) tff deinterlacing instead of auto for the whole game.
|
||||
SLES-54518:
|
||||
name: "Clumsy Shumsy"
|
||||
|
@ -19799,7 +19798,7 @@ SLES-54596:
|
|||
name: "Heatseeker"
|
||||
region: "PAL-M3"
|
||||
SLES-54604:
|
||||
name: "¡Qué Pasa Neng! El Videojuego"
|
||||
name: "¡Qué pasa Neng! El videojuego"
|
||||
region: "PAL-S"
|
||||
SLES-54606:
|
||||
name: "Harley-Davidson - Race to the Rally"
|
||||
|
@ -20345,14 +20344,12 @@ SLES-54806:
|
|||
region: "PAL-E"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 2 # Fixes misaligned bloom effects, 1 is technically more correct compared to software but looks worse.
|
||||
wrapGSMem: 1 # Fixes FMVs missing video pieces.
|
||||
deinterlace: 8 # Game requires adaptive (or blend) tff deinterlacing instead of auto for the whole game.
|
||||
SLES-54807:
|
||||
name: "Thrillville - Off the Rails"
|
||||
region: "PAL-F"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 2 # Fixes misaligned bloom effects, 1 is technically more correct compared to software but looks worse.
|
||||
wrapGSMem: 1 # Fixes FMVs missing video pieces.
|
||||
deinterlace: 8 # Game requires adaptive (or blend) tff deinterlacing instead of auto for the whole game.
|
||||
SLES-54809:
|
||||
name: "Charlotte's Web"
|
||||
|
@ -20565,7 +20562,6 @@ SLES-54887:
|
|||
region: "PAL-G"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 2 # Fixes misaligned bloom effects, 1 is technically more correct compared to software but looks worse.
|
||||
wrapGSMem: 1 # Fixes FMVs missing video pieces.
|
||||
deinterlace: 8 # Game requires adaptive (or blend) tff deinterlacing instead of auto for the whole game.
|
||||
SLES-54888:
|
||||
name: "Pro Biker 2"
|
||||
|
@ -20921,14 +20917,12 @@ SLES-55010:
|
|||
region: "PAL-I"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 2 # Fixes misaligned bloom effects, 1 is technically more correct compared to software but looks worse.
|
||||
wrapGSMem: 1 # Fixes FMVs missing video pieces.
|
||||
deinterlace: 8 # Game requires adaptive (or blend) tff deinterlacing instead of auto for the whole game.
|
||||
SLES-55011:
|
||||
name: "Thrillville - Fuera de Control"
|
||||
region: "PAL-S"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 2 # Fixes misaligned bloom effects, 1 is technically more correct compared to software but looks worse.
|
||||
wrapGSMem: 1 # Fixes FMVs missing video pieces.
|
||||
deinterlace: 8 # Game requires adaptive (or blend) tff deinterlacing instead of auto for the whole game.
|
||||
SLES-55012:
|
||||
name: "Golden Compass, The"
|
||||
|
@ -23882,6 +23876,7 @@ SLPM-55080:
|
|||
eeClampMode: 3 # Characters are visible in-game.
|
||||
gsHWFixes:
|
||||
texturePreloading: 1 # Performs better with partial preload because it is slow on locations outside gameplay foremost.
|
||||
mergeSprite: 1 # Fixes misaligned white lines.
|
||||
SLPM-55081:
|
||||
name: "Drag-on Dragoon 2 - Fuuin no Aka, Haitoku no Kuro [Ultimate Hits]"
|
||||
region: "NTSC-J"
|
||||
|
@ -25450,6 +25445,9 @@ SLPM-62343:
|
|||
SLPM-62344:
|
||||
name: "Simple 2000 Series Vol. 31 - The Chikyuu Boueigun"
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
mipmap: 2 # Improves distant textures.
|
||||
trilinearFiltering: 1 # Smooths high frequency textures on distant ground.
|
||||
SLPM-62345:
|
||||
name: "Simple 2000 Series Vol. 32 - The Sensha"
|
||||
region: "NTSC-J"
|
||||
|
@ -27727,6 +27725,7 @@ SLPM-65266:
|
|||
eeClampMode: 3 # Characters are visible in-game.
|
||||
gsHWFixes:
|
||||
texturePreloading: 1 # Performs better with partial preload because it is slow on locations outside gameplay foremost.
|
||||
mergeSprite: 1 # Fixes misaligned white lines.
|
||||
SLPM-65267:
|
||||
name: "Kurogane no Houkou 2 - Warship Gunner"
|
||||
region: "NTSC-J"
|
||||
|
@ -27888,6 +27887,8 @@ SLPM-65310:
|
|||
SLPM-65311:
|
||||
name: "Violet no Atelier - Gramnad no Renkinjutsushi"
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
roundSprite: 1 # Aligns text boxes.
|
||||
SLPM-65313:
|
||||
name: "First Kiss Story 1&2"
|
||||
region: "NTSC-J"
|
||||
|
@ -29056,6 +29057,8 @@ SLPM-65682:
|
|||
SLPM-65683:
|
||||
name: "Violet no Atelier - Gramnad no Renkinjutsushi [Gust Best Price]"
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
roundSprite: 1 # Aligns text boxes.
|
||||
SLPM-65684:
|
||||
name: "Meine Liebe - Yuubinaru Kioku"
|
||||
region: "NTSC-J"
|
||||
|
@ -30772,7 +30775,6 @@ SLPM-66167:
|
|||
gsHWFixes:
|
||||
alignSprite: 1 # Fixes water vertical lines.
|
||||
roundSprite: 1 # Fixes vertical lines and minor ghosting.
|
||||
wrapGSMem: 1 # Fixes FMV when in progressive mode.
|
||||
autoFlush: 1 # Fixes sun going through walls.
|
||||
SLPM-66168:
|
||||
name: "Ryu Ga Gotoku"
|
||||
|
@ -33839,7 +33841,6 @@ SLPM-67010:
|
|||
gsHWFixes:
|
||||
alignSprite: 1 # Fixes water vertical lines.
|
||||
roundSprite: 1 # Fixes vertical lines and minor ghosting.
|
||||
wrapGSMem: 1 # Fixes FMV when in progressive mode.
|
||||
autoFlush: 1 # Fixes sun going through walls.
|
||||
SLPM-67011:
|
||||
name: "God of War [CapKore]"
|
||||
|
@ -33847,7 +33848,6 @@ SLPM-67011:
|
|||
gsHWFixes:
|
||||
alignSprite: 1 # Fixes water vertical lines.
|
||||
roundSprite: 1 # Fixes vertical lines and minor ghosting.
|
||||
wrapGSMem: 1 # Fixes FMV when in progressive mode.
|
||||
autoFlush: 1 # Fixes sun going through walls.
|
||||
SLPM-67012:
|
||||
name: "God of War [Best Price]"
|
||||
|
@ -33855,7 +33855,6 @@ SLPM-67012:
|
|||
gsHWFixes:
|
||||
alignSprite: 1 # Fixes water vertical lines.
|
||||
roundSprite: 1 # Fixes vertical lines and minor ghosting.
|
||||
wrapGSMem: 1 # Fixes FMV when in progressive mode.
|
||||
autoFlush: 1 # Fixes sun going through walls.
|
||||
SLPM-67013:
|
||||
name: "God of War II - The End Begins"
|
||||
|
@ -39810,6 +39809,8 @@ SLUS-20024:
|
|||
name: "Legacy of Kain - Blood Omen 2"
|
||||
region: "NTSC-U"
|
||||
compat: 5
|
||||
roundModes:
|
||||
eeRoundMode: 0 # Fixes pathing.
|
||||
gsHWFixes:
|
||||
mipmap: 1 # Fixes glitching textures.
|
||||
SLUS-20028:
|
||||
|
@ -42669,6 +42670,8 @@ SLUS-20682:
|
|||
name: "K-1 World Grand Prix"
|
||||
region: "NTSC-U"
|
||||
compat: 5
|
||||
gsHWFixes:
|
||||
alignSprite: 1 # Fixes black vertical lines.
|
||||
SLUS-20683:
|
||||
name: "Lupin the 3rd - Treasure of the Sorcerer King"
|
||||
region: "NTSC-U"
|
||||
|
@ -42906,6 +42909,7 @@ SLUS-20732:
|
|||
eeClampMode: 3 # Characters are visible in-game.
|
||||
gsHWFixes:
|
||||
texturePreloading: 1 # Performs better with partial preload because it is slow on locations outside gameplay foremost.
|
||||
mergeSprite: 1 # Fixes misaligned white lines.
|
||||
SLUS-20733:
|
||||
name: "Castlevania - Lament of Innocence"
|
||||
region: "NTSC-U"
|
||||
|
@ -46480,7 +46484,6 @@ SLUS-21413:
|
|||
gsHWFixes:
|
||||
halfPixelOffset: 1 # Fixes misaligned bloom effects.
|
||||
mergeSprite: 1 # Fixes blurriness but removes bloom + Recommended to use Shadeboost brightness 80.
|
||||
wrapGSMem: 1 # Fixes FMVs missing video pieces.
|
||||
deinterlace: 8 # Game requires adaptive (or blend) tff deinterlacing instead of auto for the whole game.
|
||||
SLUS-21414:
|
||||
name: "Delta Force - Black Hawk Down - Team Sabre"
|
||||
|
@ -47346,7 +47349,6 @@ SLUS-21611:
|
|||
compat: 5
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 2 # Fixes misaligned bloom effects, 1 is technically more correct compared to software but looks worse.
|
||||
wrapGSMem: 1 # Fixes FMVs missing video pieces.
|
||||
deinterlace: 8 # Game requires adaptive (or blend) tff deinterlacing instead of auto for the whole game.
|
||||
SLUS-21612:
|
||||
name: "Legend of the Dragon"
|
||||
|
|
|
@ -203,6 +203,7 @@
|
|||
03000000ac0500005b05000000000000,GameSir G3w,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
|
||||
03000000ac0500002d02000000000000,GameSir G4,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,
|
||||
03000000ac0500004d04000000000000,GameSir G4,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
|
||||
03000000ac0500001a06000000000000,GameSir-T3 2.02,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
|
||||
030000004c0e00001035000000000000,Gamester,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows,
|
||||
030000000d0f00001110000000000000,GameStick Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,
|
||||
0300000047530000616d000000000000,GameStop,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,
|
||||
|
@ -560,6 +561,7 @@
|
|||
03000000830500006020000000000000,Retro Controller,a:b0,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b8,righttrigger:b9,start:b7,x:b2,y:b3,platform:Windows,
|
||||
03000000bd12000013d0000000000000,Retrolink Sega Saturn Classic Controller,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b5,lefttrigger:b6,rightshoulder:b2,righttrigger:b7,start:b8,x:b3,y:b4,platform:Windows,
|
||||
03000000bd12000015d0000000000000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows,
|
||||
03000000341200000400000000000000,RetroUSB N64 RetroPort,+rightx:b8,+righty:b10,-rightx:b9,-righty:b11,a:b7,b:b6,dpdown:b2,dpleft:b1,dpright:b0,dpup:b3,leftshoulder:b13,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b12,start:b4,platform:Windows,
|
||||
0300000000f000000300000000000000,RetroUSB RetroPad,a:b1,b:b5,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows,
|
||||
0300000000f00000f100000000000000,RetroUSB Super RetroPort,a:b1,b:b5,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows,
|
||||
03000000830500000960000000000000,Revenger,a:b0,b:b1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b3,x:b4,y:b5,platform:Windows,
|
||||
|
@ -749,7 +751,6 @@
|
|||
03000000172700004431000000000000,Xiaomi Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a7,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,
|
||||
03000000172700003350000000000000,Xiaomi XMGP01YM,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
|
||||
03000000bc2000005060000000000000,Xiaomi XMGP01YM,+lefty:+a2,+righty:+a5,-lefty:-a1,-righty:-a4,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:b8,leftx:a0,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,start:b11,x:b3,y:b4,platform:Windows,
|
||||
03000000786901006e70000000000000,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
|
||||
xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
|
||||
030000007d0400000340000000000000,Xterminator Digital Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:-a4,lefttrigger:+a4,leftx:a0,lefty:a1,paddle1:b7,paddle2:b6,rightshoulder:b5,rightstick:b9,righttrigger:b2,rightx:a3,righty:a5,start:b8,x:b3,y:b4,platform:Windows,
|
||||
03000000790000004f18000000000000,ZDT Android Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
|
||||
|
@ -799,6 +800,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
|||
03000000c82d00000261000000010000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
|
||||
03000000c82d00001130000000020000,8BitDo Ultimate Wired,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,
|
||||
03000000c82d00001330000001000000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b23,paddle2:b19,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
|
||||
03000000c82d00001330000000020000,8BitDo Ultimate Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b23,paddle2:b19,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
|
||||
03000000a00500003232000008010000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X,
|
||||
03000000a00500003232000009010000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X,
|
||||
03000000c82d00001890000001000000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,
|
||||
|
@ -825,10 +827,12 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
|||
03000000151900004000000001000000,Flydigi Vader 2,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,
|
||||
03000000b40400001124000000000000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b4,paddle2:b5,paddle3:b17,rightshoulder:b7,rightstick:b13,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b2,y:b3,platform:Mac OS X,
|
||||
03000000790000004618000000010000,GameCube Controller Adapter,a:b4,b:b0,dpdown:b56,dpleft:b60,dpright:b52,dpup:b48,lefttrigger:a12,leftx:a0,lefty:a4,rightshoulder:b28,righttrigger:a16,rightx:a20,righty:a8,start:b36,x:b8,y:b12,platform:Mac OS X,
|
||||
03000000ac0500001a06000002020000,GameSir-T3 2.02,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,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,
|
||||
03000000ad1b000001f9000000000000,Gamestop BB070 X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
|
||||
0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,
|
||||
03000000c01100000140000000010000,GameStop PS4 Fun Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
|
||||
030000006f0e00000102000000000000,GameStop Xbox 360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
|
||||
03000000ff1100003133000007010000,GameWare PC Control Pad,a:b2,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b0,platform:Mac OS X,
|
||||
030000007d0400000540000001010000,Gravis Eliminator Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,
|
||||
03000000280400000140000000020000,Gravis GamePad Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,
|
||||
030000008f0e00000300000007010000,GreenAsia Joystick,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Mac OS X,
|
||||
|
@ -844,6 +848,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
|||
030000000d0f00004d00000000000000,Hori Gem Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
|
||||
030000000d0f00003801000008010000,Hori PC Engine Mini Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,platform:Mac OS X,
|
||||
030000000d0f00009200000000010000,Hori Pokken Tournament DX Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,
|
||||
030000000d0f0000aa00000072050000,Hori Real Arcade Pro,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,
|
||||
030000000d0f00006e00000000010000,Horipad 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
|
||||
030000000d0f00006600000000010000,Horipad 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
|
||||
030000000d0f00006600000000000000,Horipad FPS Plus 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
|
||||
|
@ -895,6 +900,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
|||
030000007e0500001920000001000000,NSO N64 Controller,+rightx:b8,+righty:b7,-rightx:b3,-righty:b2,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,righttrigger:b10,start:b9,platform:Mac OS X,
|
||||
030000007e0500001720000001000000,NSO SNES Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b15,start:b9,x:b2,y:b3,platform:Mac OS X,
|
||||
03000000550900001472000025050000,NVIDIA Controller,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Mac OS X,
|
||||
030000004b120000014d000000010000,Nyko Airflo EX,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Mac OS X,
|
||||
030000006f0e00000901000002010000,PDP Versus Fighting,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,
|
||||
030000008f0e00000300000000000000,Piranha Xtreme PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Mac OS X,
|
||||
03000000666600006706000088020000,PlayStation Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Mac OS X,
|
||||
|
@ -931,6 +937,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
|||
03000000790000001100000005010000,Retro Controller,a:b1,b:b2,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b4,start:b9,x:b0,y:b3,platform:Mac OS X,
|
||||
03000000830500006020000000010000,Retro Controller,a:b0,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b8,righttrigger:b9,start:b7,x:b2,y:b3,platform:Mac OS X,
|
||||
03000000790000001100000006010000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,
|
||||
03000000341200000400000000000000,RetroUSB N64 RetroPort,+rightx:b8,+righty:b10,-rightx:b9,-righty:b11,a:b7,b:b6,dpdown:b2,dpleft:b1,dpright:b0,dpup:b3,leftshoulder:b13,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b12,start:b4,platform:Mac OS X,
|
||||
030000006b140000010d000000010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
|
||||
030000006b140000130d000000010000,Revolution Pro Controller 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
|
||||
03000000c6240000fefa000000000000,Rock Candy PS3,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
|
||||
|
@ -977,6 +984,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
|||
030000005e040000050b000003090000,Xbox Elite Controller Series 2,a:b0,b:b1,back:b31,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b53,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
|
||||
030000005e040000130b000011050000,Xbox One Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
|
||||
030000005e040000200b000011050000,Xbox One Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
|
||||
030000005e040000200b000013050000,Xbox One Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
|
||||
030000005e040000d102000000000000,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
|
||||
030000005e040000dd02000000000000,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
|
||||
030000005e040000e002000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X,
|
||||
|
@ -984,7 +992,6 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
|||
030000005e040000e302000000000000,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
|
||||
030000005e040000ea02000000000000,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
|
||||
030000005e040000fd02000003090000,Xbox One Controller,a:b0,b:b1,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,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,
|
||||
030000005e040000200b000013050000,Xbox One Controller,a:b0,b:b1,x:b3,y:b4,back:b10,guide:b12,start:b11,leftstick:b13,rightstick:b14,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a5,righttrigger:a4,platform:Mac OS X,
|
||||
03000000c62400003a54000000000000,Xbox One PowerA Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
|
||||
030000005e040000130b000001050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
|
||||
030000005e040000130b000005050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
|
||||
|
@ -994,7 +1001,6 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
|||
03000000172700004431000029010000,XiaoMi Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Mac OS X,
|
||||
03000000120c0000100e000000010000,Zeroplus P4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
|
||||
03000000120c0000101e000000010000,Zeroplus P4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
|
||||
03000000ff1100003133000007010000,GameWare PC Control Pad,a:b2,b:b1,x:b3,y:b0,back:b10,start:b11,leftstick:b8,rightstick:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:b6,righttrigger:b7,platform:Mac OS X,
|
||||
|
||||
# Linux
|
||||
030000005e0400008e02000020010000,8BitDo Adapter,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||
|
@ -1023,6 +1029,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
|||
03000000c82d00000751000000010000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:a8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
05000000c82d00000851000000010000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:a8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
03000000c82d00000660000011010000,8BitDo Pro 2,a:b1,b:b0,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:b4,y:b3,platform:Linux,
|
||||
03000000c82d00001030000011010000,8BitDo Pro 2,a:b1,b:b0,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:b4,y:b3,platform:Linux,
|
||||
05000000c82d00000660000000010000,8BitDo Pro 2,a:b1,b:b0,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:b4,y:b3,platform:Linux,
|
||||
03000000c82d00000131000011010000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
03000000c82d00000231000011010000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
|
@ -1045,10 +1052,9 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
|||
05000000c82d00006228000000010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
03000000c82d00000260000011010000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
05000000c82d00000261000000010000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
03000000c82d00001030000011010000,8BitDo 8BitDo Pro 2,a:b1,b:b0,x:b4,y:b3,back:b10,guide:b12,start:b11,leftstick:b13,rightstick:b14,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a5,righttrigger:a4,platform:Linux,
|
||||
05000000202800000900000000010000,8BitDo SNES30,a:b1,b:b0,back:b10,dpdown:b122,dpleft:b119,dpright:b120,dpup:b117,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,
|
||||
03000000c82d00001130000011010000,8BitDo Ultimate Wired,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,
|
||||
03000000c82d00000760000011010000,8BitDo Ultimate Wireless,a:b1,b:b0,x:b4,y:b3,back:b10,start:b11,leftstick:b13,rightstick:b14,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a5,righttrigger:a4,platform:Linux,
|
||||
03000000c82d00000760000011010000,8BitDo Ultimate Wireless,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
03000000c82d00001230000011010000,8BitDo Ultimate Wireless,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,
|
||||
03000000c82d00001330000011010000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b23,paddle2:b19,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
05000000a00500003232000001000000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux,
|
||||
|
@ -1137,6 +1143,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
|||
03000000632500002605000010010000,HJDX,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:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
030000000d0f00000d00000000010000,Hori,a:b0,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,rightshoulder:b7,start:b9,x:b1,y:b2,platform:Linux,
|
||||
030000000d0f00006d00000020010000,Hori EDGE 301,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:+a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||
030000000d0f00008400000011010000,Hori Fighting Commander,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
|
||||
030000000d0f00005f00000011010000,Hori Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
|
||||
030000000d0f00005e00000011010000,Hori Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
|
||||
030000000d0f00005001000009040000,Hori Fighting Commander OCTA Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||
|
@ -1388,6 +1395,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
|||
0300000081170000990a000001010000,Retronic Adapter,a:b0,leftx:a0,lefty:a1,platform:Linux,
|
||||
0300000000f000000300000000010000,RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux,
|
||||
00000000526574726f53746f6e653200,RetroStone 2 Controller,a:b1,b:b0,back:b10,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,leftshoulder:b6,lefttrigger:b8,rightshoulder:b7,righttrigger:b9,start:b11,x:b4,y:b3,platform:Linux,
|
||||
03000000341200000400000000010000,RetroUSB N64 RetroPort,+rightx:b8,+righty:b10,-rightx:b9,-righty:b11,a:b7,b:b6,dpdown:b2,dpleft:b1,dpright:b0,dpup:b3,leftshoulder:b13,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b12,start:b4,platform:Linux,
|
||||
030000006b140000010d000011010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
|
||||
030000006b140000130d000011010000,Revolution Pro Controller 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
|
||||
030000006f0e00001f01000000010000,Rock Candy,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||
|
@ -1527,4 +1535,5 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
|||
xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||
03000000120c0000100e000011010000,Zeroplus P4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
|
||||
03000000120c0000101e000011010000,Zeroplus P4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
|
||||
03000000f00300008d03000011010000,HyperX Clutch,a:b0,b:b1,x:b3,y:b4,back:b10,guide:b12,start:b11,leftshoulder:b6,rightshoulder:b7,leftstick:b13,rightstick:b14,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a5,righttrigger:a4,platform:Linux,
|
||||
|
||||
|
|
|
@ -393,7 +393,7 @@ A_STATIC void CasSetup(
|
|||
const1[0]=AU1_AF1(sharp);
|
||||
const1[1]=AU1_AH2_AF2(hSharp);
|
||||
const1[2]=AU1_AF1(AF1_(8.0)*inputSizeInPixelsX*ARcpF1(outputSizeInPixelsX));
|
||||
const1[3]=0;}
|
||||
const1[3]=AU1(0);}
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//_____________________________________________________________/\_______________________________________________________________
|
||||
|
|
|
@ -1,5 +1,14 @@
|
|||
#if defined(SHADER_MODEL) || defined(FXAA_GLSL_130) || defined(FXAA_GLSL_VK) || defined(__METAL_VERSION__)
|
||||
|
||||
#ifndef SHADER_MODEL
|
||||
#define SHADER_MODEL 0
|
||||
#endif
|
||||
#ifndef FXAA_HLSL_4
|
||||
#define FXAA_HLSL_4 0
|
||||
#endif
|
||||
#ifndef FXAA_HLSL_5
|
||||
#define FXAA_HLSL_5 0
|
||||
#endif
|
||||
#ifndef FXAA_GLSL_130
|
||||
#define FXAA_GLSL_130 0
|
||||
#endif
|
||||
|
@ -55,19 +64,22 @@ static constexpr sampler MAIN_SAMPLER(coord::normalized, address::clamp_to_edge,
|
|||
[FXAA CODE SECTION]
|
||||
------------------------------------------------------------------------------*/
|
||||
|
||||
// We don't use gather4 for alpha/luminance because it would require an additional
|
||||
// pass to compute the values, which would be slower than the extra shader loads.
|
||||
|
||||
#if (SHADER_MODEL >= 0x500)
|
||||
#define FXAA_HLSL_5 1
|
||||
#define FXAA_GATHER4_ALPHA 1
|
||||
#define FXAA_GATHER4_ALPHA 0
|
||||
|
||||
#elif (SHADER_MODEL >= 0x400)
|
||||
#define FXAA_HLSL_4 1
|
||||
#define FXAA_GATHER4_ALPHA 0
|
||||
|
||||
#elif (FXAA_GLSL_130 == 1 || FXAA_GLSL_VK == 1)
|
||||
#define FXAA_GATHER4_ALPHA 1
|
||||
#define FXAA_GATHER4_ALPHA 0
|
||||
|
||||
#elif defined(__METAL_VERSION__)
|
||||
#define FXAA_GATHER4_ALPHA 1
|
||||
#define FXAA_GATHER4_ALPHA 0
|
||||
#endif
|
||||
|
||||
#if (FXAA_HLSL_5 == 1)
|
||||
|
@ -505,7 +517,7 @@ float4 FxaaPass(float4 FxaaColor, float2 uv0, texture2d<float> tex)
|
|||
FxaaColor = FxaaPixelShader(uv0, tex, 1.0/PixelSize.xy, FxaaSubpixMax, FxaaEdgeThreshold, FxaaEdgeThresholdMin);
|
||||
|
||||
#elif (FXAA_GLSL_130 == 1 || FXAA_GLSL_VK == 1)
|
||||
vec2 PixelSize = textureSize(TextureSampler, 0);
|
||||
vec2 PixelSize = vec2(textureSize(TextureSampler, 0));
|
||||
FxaaColor = FxaaPixelShader(uv0, TextureSampler, 1.0/PixelSize.xy, FxaaSubpixMax, FxaaEdgeThreshold, FxaaEdgeThresholdMin);
|
||||
#elif defined(__METAL_VERSION__)
|
||||
float2 PixelSize = float2(tex.get_width(), tex.get_height());
|
||||
|
@ -526,7 +538,7 @@ void main()
|
|||
color = PreGammaPass(color);
|
||||
color = FxaaPass(color, PSin_t);
|
||||
|
||||
SV_Target0 = color;
|
||||
SV_Target0 = float4(color.rgb, 1.0);
|
||||
}
|
||||
|
||||
#elif (SHADER_MODEL >= 0x400)
|
||||
|
@ -539,7 +551,7 @@ PS_OUTPUT ps_main(VS_OUTPUT input)
|
|||
color = PreGammaPass(color);
|
||||
color = FxaaPass(color, input.t);
|
||||
|
||||
output.c = color;
|
||||
output.c = float4(color.rgb, 1.0);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#ifdef VERTEX_SHADER
|
||||
|
||||
#if !pGL_ES
|
||||
out gl_PerVertex {
|
||||
vec4 gl_Position;
|
||||
float gl_PointSize;
|
||||
|
@ -13,6 +14,7 @@ out gl_PerVertex {
|
|||
float gl_ClipDistance[1];
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -20,6 +22,7 @@ out gl_PerVertex {
|
|||
|
||||
#ifdef GEOMETRY_SHADER
|
||||
|
||||
#if !pGL_ES
|
||||
in gl_PerVertex {
|
||||
vec4 gl_Position;
|
||||
float gl_PointSize;
|
||||
|
@ -35,6 +38,7 @@ out gl_PerVertex {
|
|||
float gl_ClipDistance[1];
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -75,7 +75,11 @@ void ps_convert_rgba8_16bits()
|
|||
void ps_convert_float32_32bits()
|
||||
{
|
||||
// Convert a GL_FLOAT32 depth texture into a 32 bits UINT texture
|
||||
#if HAS_CLIP_CONTROL
|
||||
SV_Target1 = uint(exp2(32.0f) * sample_c().r);
|
||||
#else
|
||||
SV_Target1 = uint(exp2(24.0f) * sample_c().r);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -83,7 +87,11 @@ void ps_convert_float32_32bits()
|
|||
void ps_convert_float32_rgba8()
|
||||
{
|
||||
// Convert a GL_FLOAT32 depth texture into a RGBA color texture
|
||||
#if HAS_CLIP_CONTROL
|
||||
uint d = uint(sample_c().r * exp2(32.0f));
|
||||
#else
|
||||
uint d = uint(sample_c().r * exp2(24.0f));
|
||||
#endif
|
||||
SV_Target0 = vec4(uvec4((d & 0xFFu), ((d >> 8) & 0xFFu), ((d >> 16) & 0xFFu), (d >> 24))) / vec4(255.0);
|
||||
}
|
||||
#endif
|
||||
|
@ -92,7 +100,11 @@ void ps_convert_float32_rgba8()
|
|||
void ps_convert_float16_rgb5a1()
|
||||
{
|
||||
// Convert a GL_FLOAT32 (only 16 lsb) depth into a RGB5A1 color texture
|
||||
#if HAS_CLIP_CONTROL
|
||||
uint d = uint(sample_c().r * exp2(32.0f));
|
||||
#else
|
||||
uint d = uint(sample_c().r * exp2(24.0f));
|
||||
#endif
|
||||
SV_Target0 = vec4(uvec4((d & 0x1Fu), ((d >> 5) & 0x1Fu), ((d >> 10) & 0x1Fu), (d >> 15) & 0x01u)) / vec4(32.0f, 32.0f, 32.0f, 1.0f);
|
||||
}
|
||||
#endif
|
||||
|
@ -100,25 +112,41 @@ void ps_convert_float16_rgb5a1()
|
|||
float rgba8_to_depth32(vec4 unorm)
|
||||
{
|
||||
uvec4 c = uvec4(unorm * vec4(255.5f));
|
||||
#if HAS_CLIP_CONTROL
|
||||
return float(c.r | (c.g << 8) | (c.b << 16) | (c.a << 24)) * exp2(-32.0f);
|
||||
#else
|
||||
return float(c.r | (c.g << 8) | (c.b << 16) | (c.a << 24)) * exp2(-24.0f);
|
||||
#endif
|
||||
}
|
||||
|
||||
float rgba8_to_depth24(vec4 unorm)
|
||||
{
|
||||
uvec3 c = uvec3(unorm.rgb * vec3(255.5f));
|
||||
#if HAS_CLIP_CONTROL
|
||||
return float(c.r | (c.g << 8) | (c.b << 16)) * exp2(-32.0f);
|
||||
#else
|
||||
return float(c.r | (c.g << 8) | (c.b << 16)) * exp2(-24.0f);
|
||||
#endif
|
||||
}
|
||||
|
||||
float rgba8_to_depth16(vec4 unorm)
|
||||
{
|
||||
uvec2 c = uvec2(unorm.rg * vec2(255.5f));
|
||||
#if HAS_CLIP_CONTROL
|
||||
return float(c.r | (c.g << 8)) * exp2(-32.0f);
|
||||
#else
|
||||
return float(c.r | (c.g << 8)) * exp2(-24.0f);
|
||||
#endif
|
||||
}
|
||||
|
||||
float rgb5a1_to_depth16(vec4 unorm)
|
||||
{
|
||||
uvec4 c = uvec4(unorm * vec4(255.5f));
|
||||
#if HAS_CLIP_CONTROL
|
||||
return float(((c.r & 0xF8u) >> 3) | ((c.g & 0xF8u) << 2) | ((c.b & 0xF8u) << 7) | ((c.a & 0x80u) << 8)) * exp2(-32.0f);
|
||||
#else
|
||||
return float(((c.r & 0xF8u) >> 3) | ((c.g & 0xF8u) << 2) | ((c.b & 0xF8u) << 7) | ((c.a & 0x80u) << 8)) * exp2(-24.0f);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ps_convert_rgba8_float32
|
||||
|
|
|
@ -14,9 +14,9 @@ layout(location = 0) out vec4 SV_Target0;
|
|||
// Weave shader
|
||||
void ps_main0()
|
||||
{
|
||||
const int idx = int(ZrH.x); // buffer index passed from CPU
|
||||
const int field = idx & 1; // current field
|
||||
const int vpos = int(gl_FragCoord.y); // vertical position of destination texture
|
||||
int idx = int(ZrH.x); // buffer index passed from CPU
|
||||
int field = idx & 1; // current field
|
||||
int vpos = int(gl_FragCoord.y); // vertical position of destination texture
|
||||
|
||||
if ((vpos & 1) == field)
|
||||
SV_Target0 = texture(TextureSampler, PSin_t);
|
||||
|
@ -54,16 +54,16 @@ void ps_main3()
|
|||
// causing the wrong lines to be discarded, so a vertical offset (lofs) is added to the vertical
|
||||
// position of the destination texture to force the proper field alignment
|
||||
|
||||
const int idx = int(ZrH.x); // buffer index passed from CPU
|
||||
const int bank = idx >> 1; // current bank
|
||||
const int field = idx & 1; // current field
|
||||
const int vres = int(ZrH.z) >> 1; // vertical resolution of source texture
|
||||
const int lofs = ((((vres + 1) >> 1) << 1) - vres) & bank; // line alignment offset for bank 1
|
||||
const int vpos = int(gl_FragCoord.y) + lofs; // vertical position of destination texture
|
||||
const vec2 bofs = vec2(0.0f, 0.5f * bank); // vertical offset of the current bank relative to source texture size
|
||||
const vec2 vscale = vec2(1.0f, 2.0f); // scaling factor from source to destination texture
|
||||
const vec2 optr = PSin_t - bofs; // used to check if the current destination line is within the current bank
|
||||
const vec2 iptr = optr * vscale; // pointer to the current pixel in the source texture
|
||||
int idx = int(ZrH.x); // buffer index passed from CPU
|
||||
int bank = idx >> 1; // current bank
|
||||
int field = idx & 1; // current field
|
||||
int vres = int(ZrH.z) >> 1; // vertical resolution of source texture
|
||||
int lofs = ((((vres + 1) >> 1) << 1) - vres) & bank; // line alignment offset for bank 1
|
||||
int vpos = int(gl_FragCoord.y) + lofs; // vertical position of destination texture
|
||||
vec2 bofs = vec2(0.0f, 0.5f * float(bank)); // vertical offset of the current bank relative to source texture size
|
||||
vec2 vscale = vec2(1.0f, 2.0f); // scaling factor from source to destination texture
|
||||
vec2 optr = PSin_t - bofs; // used to check if the current destination line is within the current bank
|
||||
vec2 iptr = optr * vscale; // pointer to the current pixel in the source texture
|
||||
|
||||
// if the index of current destination line belongs to the current fiels we update it, otherwise
|
||||
// we leave the old line in the destination buffer
|
||||
|
@ -79,15 +79,15 @@ void ps_main4()
|
|||
{
|
||||
// we use the contents of the MAD frame buffer to reconstruct the missing lines from the current field.
|
||||
|
||||
const int idx = int(ZrH.x); // buffer index passed from CPU
|
||||
const int field = idx & 1; // current field
|
||||
const int vpos = int(gl_FragCoord.y); // vertical position of destination texture
|
||||
const float sensitivity = ZrH.w; // passed from CPU, higher values mean more likely to use weave
|
||||
const vec3 motion_thr = vec3(1.0, 1.0, 1.0) * sensitivity; //
|
||||
const vec2 bofs = vec2(0.0f, 0.5f); // position of the bank 1 relative to source texture size
|
||||
const vec2 vscale = vec2(1.0f, 0.5f); // scaling factor from source to destination texture
|
||||
const vec2 lofs = vec2(0.0f, ZrH.y) * vscale; // distance between two adjacent lines relative to source texture size
|
||||
const vec2 iptr = PSin_t * vscale; // pointer to the current pixel in the source texture
|
||||
int idx = int(ZrH.x); // buffer index passed from CPU
|
||||
int field = idx & 1; // current field
|
||||
int vpos = int(gl_FragCoord.y); // vertical position of destination texture
|
||||
float sensitivity = ZrH.w; // passed from CPU, higher values mean more likely to use weave
|
||||
vec3 motion_thr = vec3(1.0, 1.0, 1.0) * sensitivity; //
|
||||
vec2 bofs = vec2(0.0f, 0.5f); // position of the bank 1 relative to source texture size
|
||||
vec2 vscale = vec2(1.0f, 0.5f); // scaling factor from source to destination texture
|
||||
vec2 lofs = vec2(0.0f, ZrH.y) * vscale; // distance between two adjacent lines relative to source texture size
|
||||
vec2 iptr = PSin_t * vscale; // pointer to the current pixel in the source texture
|
||||
|
||||
vec2 p_t0; // pointer to current pixel (missing or not) from most recent frame
|
||||
vec2 p_t1; // pointer to current pixel (missing or not) from one frame back
|
||||
|
|
|
@ -73,10 +73,11 @@ void ps_copy()
|
|||
#ifdef ps_filter_scanlines
|
||||
vec4 ps_scanlines(uint i)
|
||||
{
|
||||
vec4 mask[2] =
|
||||
{
|
||||
vec4(1, 1, 1, 0),
|
||||
vec4(0, 0, 0, 0)};
|
||||
vec4 mask[2] = vec4[2]
|
||||
(
|
||||
vec4(1, 1, 1, 0),
|
||||
vec4(0, 0, 0, 0)
|
||||
);
|
||||
|
||||
return sample_c() * clamp((mask[i] + 0.5f), 0.0f, 1.0f);
|
||||
}
|
||||
|
@ -360,7 +361,7 @@ vec4 LottesCRTPass()
|
|||
//flipped y axis in opengl
|
||||
vec2 fragcoord = vec2(gl_FragCoord.x, u_target_resolution.y - gl_FragCoord.y) - u_target_rect.xy;
|
||||
vec4 color;
|
||||
vec2 inSize = u_target_resolution - (2 * u_target_rect.xy);
|
||||
vec2 inSize = u_target_resolution - (2.0 * u_target_rect.xy);
|
||||
|
||||
vec2 pos = Warp(fragcoord.xy / inSize);
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
|
||||
#ifdef FRAGMENT_SHADER
|
||||
|
||||
#if !defined(BROKEN_DRIVER) && defined(GL_ARB_enhanced_layouts) && GL_ARB_enhanced_layouts
|
||||
#if !defined(BROKEN_DRIVER) && (pGL_ES || defined(GL_ARB_enhanced_layouts) && GL_ARB_enhanced_layouts)
|
||||
layout(location = 0)
|
||||
#endif
|
||||
in SHADER
|
||||
|
@ -55,6 +55,8 @@ in SHADER
|
|||
#undef TARGET_0_QUALIFIER
|
||||
#define TARGET_0_QUALIFIER inout
|
||||
#define LAST_FRAG_COLOR SV_Target0
|
||||
#elif defined(GL_ARM_shader_framebuffer_fetch)
|
||||
#define LAST_FRAG_COLOR gl_LastFragColorARM
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
@ -125,7 +127,7 @@ vec4 sample_c(vec2 uv)
|
|||
|
||||
return textureLod(TextureSampler, uv, lod);
|
||||
#else
|
||||
return textureLod(TextureSampler, uv, 0); // No lod
|
||||
return textureLod(TextureSampler, uv, 0.0f); // No lod
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -249,10 +251,16 @@ mat4 sample_4p(vec4 u)
|
|||
|
||||
int fetch_raw_depth()
|
||||
{
|
||||
#if PS_TEX_IS_FB == 1
|
||||
return int(fetch_rt().r * exp2(32.0f));
|
||||
#if HAS_CLIP_CONTROL
|
||||
float multiplier = exp2(32.0f);
|
||||
#else
|
||||
return int(texelFetch(TextureSampler, ivec2(gl_FragCoord.xy), 0).r * exp2(32.0f));
|
||||
float multiplier = exp2(24.0f);
|
||||
#endif
|
||||
|
||||
#if PS_TEX_IS_FB == 1
|
||||
return int(fetch_rt().r * multiplier);
|
||||
#else
|
||||
return int(texelFetch(TextureSampler, ivec2(gl_FragCoord.xy), 0).r * multiplier);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -344,13 +352,21 @@ vec4 sample_depth(vec2 st)
|
|||
#elif PS_DEPTH_FMT == 1
|
||||
// Based on ps_convert_float32_rgba8 of convert
|
||||
// Convert a GL_FLOAT32 depth texture into a RGBA color texture
|
||||
uint d = uint(fetch_c(uv).r * exp2(32.0f));
|
||||
#if HAS_CLIP_CONTROL
|
||||
uint d = uint(fetch_c(uv).r * exp2(32.0f));
|
||||
#else
|
||||
uint d = uint(fetch_c(uv).r * exp2(24.0f));
|
||||
#endif
|
||||
t = vec4(uvec4((d & 0xFFu), ((d >> 8) & 0xFFu), ((d >> 16) & 0xFFu), (d >> 24)));
|
||||
|
||||
#elif PS_DEPTH_FMT == 2
|
||||
// Based on ps_convert_float16_rgb5a1 of convert
|
||||
// Convert a GL_FLOAT32 (only 16 lsb) depth into a RGB5A1 color texture
|
||||
uint d = uint(fetch_c(uv).r * exp2(32.0f));
|
||||
#if HAS_CLIP_CONTROL
|
||||
uint d = uint(fetch_c(uv).r * exp2(32.0f));
|
||||
#else
|
||||
uint d = uint(fetch_c(uv).r * exp2(24.0f));
|
||||
#endif
|
||||
t = vec4(uvec4((d & 0x1Fu), ((d >> 5) & 0x1Fu), ((d >> 10) & 0x1Fu), (d >> 15) & 0x01u)) * vec4(8.0f, 8.0f, 8.0f, 128.0f);
|
||||
|
||||
#elif PS_DEPTH_FMT == 3
|
||||
|
@ -834,16 +850,16 @@ void ps_main()
|
|||
|
||||
vec4 C = ps_color();
|
||||
#if (APITRACE_DEBUG & 1) == 1
|
||||
C.r = 255f;
|
||||
C.r = 255.0f;
|
||||
#endif
|
||||
#if (APITRACE_DEBUG & 2) == 2
|
||||
C.g = 255f;
|
||||
C.g = 255.0f;
|
||||
#endif
|
||||
#if (APITRACE_DEBUG & 4) == 4
|
||||
C.b = 255f;
|
||||
C.b = 255.0f;
|
||||
#endif
|
||||
#if (APITRACE_DEBUG & 8) == 8
|
||||
C.a = 128f;
|
||||
C.a = 128.0f;
|
||||
#endif
|
||||
|
||||
#if PS_SHUFFLE
|
||||
|
|
|
@ -9,7 +9,7 @@ layout(location = 5) in uint i_z;
|
|||
layout(location = 6) in uvec2 i_uv;
|
||||
layout(location = 7) in vec4 i_f;
|
||||
|
||||
#if !defined(BROKEN_DRIVER) && defined(GL_ARB_enhanced_layouts) && GL_ARB_enhanced_layouts
|
||||
#if !defined(BROKEN_DRIVER) && (pGL_ES || defined(GL_ARB_enhanced_layouts) && GL_ARB_enhanced_layouts)
|
||||
layout(location = 0)
|
||||
#endif
|
||||
out SHADER
|
||||
|
@ -59,7 +59,15 @@ void vs_main()
|
|||
p.xy = vec2(i_p) - vec2(0.05f, 0.05f);
|
||||
p.xy = p.xy * VertexScale - VertexOffset;
|
||||
p.w = 1.0f;
|
||||
|
||||
#if HAS_CLIP_CONTROL
|
||||
p.z = float(z) * exp_min32;
|
||||
#else
|
||||
// GLES doesn't support ARB_clip_control, so remap it to -1..1. We also reduce the range from 32 bits
|
||||
// to 24 bits, which means some games with very large depth ranges will not render correctly. But,
|
||||
// for most, it's okay, and really, the best we can do.
|
||||
p.z = min(float(z) * exp2(-23.0f), 2.0f) - 1.0f;
|
||||
#endif
|
||||
|
||||
gl_Position = p;
|
||||
|
||||
|
@ -77,7 +85,7 @@ void vs_main()
|
|||
|
||||
#ifdef GEOMETRY_SHADER
|
||||
|
||||
#if !defined(BROKEN_DRIVER) && defined(GL_ARB_enhanced_layouts) && GL_ARB_enhanced_layouts
|
||||
#if !defined(BROKEN_DRIVER) && (pGL_ES || defined(GL_ARB_enhanced_layouts) && GL_ARB_enhanced_layouts)
|
||||
layout(location = 0)
|
||||
#endif
|
||||
in SHADER
|
||||
|
@ -91,7 +99,7 @@ in SHADER
|
|||
#endif
|
||||
} GSin[];
|
||||
|
||||
#if !defined(BROKEN_DRIVER) && defined(GL_ARB_enhanced_layouts) && GL_ARB_enhanced_layouts
|
||||
#if !defined(BROKEN_DRIVER) && (pGL_ES || defined(GL_ARB_enhanced_layouts) && GL_ARB_enhanced_layouts)
|
||||
layout(location = 0)
|
||||
#endif
|
||||
out SHADER
|
||||
|
@ -173,7 +181,7 @@ void gs_main()
|
|||
// Potentially there is faster math
|
||||
vec2 line_vector = normalize(rt_p.xy - lt_p.xy);
|
||||
vec2 line_normal = vec2(line_vector.y, -line_vector.x);
|
||||
vec2 line_width = (line_normal * PointSize) / 2;
|
||||
vec2 line_width = (line_normal * PointSize) / 2.0f;
|
||||
|
||||
lt_p.xy -= line_width;
|
||||
rt_p.xy -= line_width;
|
||||
|
|
|
@ -209,7 +209,6 @@ bool Context::CreateDevice(IDXGIFactory* dxgi_factory, u32 adapter_index, bool e
|
|||
|
||||
// Create the actual device.
|
||||
hr = s_d3d12_create_device(adapter.get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_device));
|
||||
pxAssertRel(SUCCEEDED(hr), "Create D3D12 device");
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
|
@ -548,7 +547,11 @@ void Context::DestroyPendingResources(CommandListResources& cmdlist)
|
|||
|
||||
void Context::DestroyResources()
|
||||
{
|
||||
ExecuteCommandList(WaitType::Sleep);
|
||||
if (m_command_queue)
|
||||
{
|
||||
ExecuteCommandList(WaitType::Sleep);
|
||||
WaitForGPUIdle();
|
||||
}
|
||||
|
||||
m_texture_stream_buffer.Destroy(false);
|
||||
m_descriptor_heap_manager.Free(&m_null_srv_descriptor);
|
||||
|
|
|
@ -65,6 +65,11 @@ namespace GL
|
|||
glBufferSubData(m_target, 0, used_size, m_cpu_buffer.data());
|
||||
}
|
||||
|
||||
u32 GetChunkSize() const override
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
static std::unique_ptr<StreamBuffer> Create(GLenum target, u32 size)
|
||||
{
|
||||
glGetError();
|
||||
|
@ -115,6 +120,11 @@ namespace GL
|
|||
glBufferData(m_target, used_size, m_cpu_buffer.data(), GL_STREAM_DRAW);
|
||||
}
|
||||
|
||||
u32 GetChunkSize() const override
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
static std::unique_ptr<StreamBuffer> Create(GLenum target, u32 size)
|
||||
{
|
||||
glGetError();
|
||||
|
@ -226,6 +236,11 @@ namespace GL
|
|||
}
|
||||
}
|
||||
|
||||
u32 GetChunkSize() const override
|
||||
{
|
||||
return m_size / NUM_SYNC_POINTS;
|
||||
}
|
||||
|
||||
u32 m_position = 0;
|
||||
u32 m_used_block_index = 0;
|
||||
u32 m_available_block_index = NUM_SYNC_POINTS;
|
||||
|
|
|
@ -46,6 +46,9 @@ namespace GL
|
|||
virtual MappingResult Map(u32 alignment, u32 min_size) = 0;
|
||||
virtual void Unmap(u32 used_size) = 0;
|
||||
|
||||
/// Returns the minimum granularity of blocks which sync objects will be created around.
|
||||
virtual u32 GetChunkSize() const = 0;
|
||||
|
||||
static std::unique_ptr<StreamBuffer> Create(GLenum target, u32 size);
|
||||
|
||||
protected:
|
||||
|
|
|
@ -77,6 +77,9 @@ static std::string s_output_prefix;
|
|||
static s32 s_loop_count = 1;
|
||||
static std::optional<bool> s_use_window;
|
||||
|
||||
// Owned by the GS thread.
|
||||
static u32 s_dump_frame_number = 0;
|
||||
|
||||
bool GSRunner::SetCriticalFolders()
|
||||
{
|
||||
EmuFolders::AppRoot = Path::Canonicalize(Path::GetDirectory(FileSystem::GetProgramPath()));
|
||||
|
@ -286,6 +289,13 @@ void Host::ReleaseHostDisplay(bool clear_state)
|
|||
|
||||
bool Host::BeginPresentFrame(bool frame_skip)
|
||||
{
|
||||
// when we wrap around, don't race other files
|
||||
GSJoinSnapshotThreads();
|
||||
|
||||
// queue dumping of this frame
|
||||
std::string dump_path(fmt::format("{}_frame{}.png", s_output_prefix, s_dump_frame_number));
|
||||
GSQueueSnapshot(dump_path);
|
||||
|
||||
if (g_host_display->BeginPresent(frame_skip))
|
||||
return true;
|
||||
|
||||
|
@ -336,7 +346,8 @@ void Host::OnVMResumed()
|
|||
{
|
||||
}
|
||||
|
||||
void Host::OnGameChanged(const std::string& disc_path, const std::string& game_serial, const std::string& game_name, u32 game_crc)
|
||||
void Host::OnGameChanged(const std::string& disc_path, const std::string& elf_override, const std::string& game_serial,
|
||||
const std::string& game_name, u32 game_crc)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -657,15 +668,8 @@ int main(int argc, char* argv[])
|
|||
|
||||
void Host::CPUThreadVSync()
|
||||
{
|
||||
if (!s_output_prefix.empty())
|
||||
{
|
||||
// wait for the previous frame to complete (and thus dump)
|
||||
GetMTGS().WaitGS(false, false, false);
|
||||
|
||||
// queue dumping of this frame
|
||||
std::string dump_path(fmt::format("{}_frame{}.png", s_output_prefix, GSDumpReplayer::GetFrameNumber()));
|
||||
GetMTGS().RunOnGSThread([dump_path = std::move(dump_path)]() { GSQueueSnapshot(dump_path); });
|
||||
}
|
||||
// update GS thread copy of frame number
|
||||
GetMTGS().RunOnGSThread([frame_number = GSDumpReplayer::GetFrameNumber()]() { s_dump_frame_number = frame_number; });
|
||||
|
||||
// process any window messages (but we shouldn't really have any)
|
||||
GSRunner::PumpPlatformMessages();
|
||||
|
|
|
@ -35,6 +35,8 @@
|
|||
#include "QtHost.h"
|
||||
#include "QtUtils.h"
|
||||
|
||||
#include "fmt/format.h"
|
||||
|
||||
static const char* SUPPORTED_FORMATS_STRING = QT_TRANSLATE_NOOP(GameListWidget,
|
||||
".bin/.iso (ISO Disc Images)\n"
|
||||
".chd (Compressed Hunks of Data)\n"
|
||||
|
@ -596,6 +598,19 @@ const GameList::Entry* GameListWidget::getSelectedEntry() const
|
|||
}
|
||||
}
|
||||
|
||||
void GameListWidget::rescanFile(const std::string& path)
|
||||
{
|
||||
// We can't do this while there's a VM running, because of CDVD state... ugh.
|
||||
if (QtHost::IsVMValid())
|
||||
{
|
||||
Console.Error(fmt::format("Can't re-scan ELF at '{}' because we have a VM running.", path));
|
||||
return;
|
||||
}
|
||||
|
||||
GameList::RescanPath(path);
|
||||
m_model->refresh();
|
||||
}
|
||||
|
||||
GameListGridListView::GameListGridListView(QWidget* parent /*= nullptr*/)
|
||||
: QListView(parent)
|
||||
{
|
||||
|
|
|
@ -64,6 +64,9 @@ public:
|
|||
|
||||
const GameList::Entry* getSelectedEntry() const;
|
||||
|
||||
/// Rescans a single file. NOTE: Happens on UI thread.
|
||||
void rescanFile(const std::string& path);
|
||||
|
||||
Q_SIGNALS:
|
||||
void refreshProgress(const QString& status, int current, int total);
|
||||
void refreshComplete();
|
||||
|
|
|
@ -68,7 +68,7 @@
|
|||
#endif
|
||||
|
||||
|
||||
static constexpr char OPEN_FILE_FILTER[] =
|
||||
const char* MainWindow::OPEN_FILE_FILTER =
|
||||
QT_TRANSLATE_NOOP("MainWindow", "All File Types (*.bin *.iso *.cue *.chd *.cso *.gz *.elf *.irx *.gs *.gs.xz *.gs.zst *.dump);;"
|
||||
"Single-Track Raw Images (*.bin *.iso);;"
|
||||
"Cue Sheets (*.cue);;"
|
||||
|
@ -80,14 +80,13 @@ static constexpr char OPEN_FILE_FILTER[] =
|
|||
"GS Dumps (*.gs *.gs.xz *.gs.zst);;"
|
||||
"Block Dumps (*.dump)");
|
||||
|
||||
static constexpr char DISC_IMAGE_FILTER[] =
|
||||
QT_TRANSLATE_NOOP("MainWindow", "All File Types (*.bin *.iso *.cue *.chd *.cso *.gz *.dump);;"
|
||||
"Single-Track Raw Images (*.bin *.iso);;"
|
||||
"Cue Sheets (*.cue);;"
|
||||
"MAME CHD Images (*.chd);;"
|
||||
"CSO Images (*.cso);;"
|
||||
"GZ Images (*.gz);;"
|
||||
"Block Dumps (*.dump)");
|
||||
const char* MainWindow::DISC_IMAGE_FILTER = QT_TRANSLATE_NOOP("MainWindow", "All File Types (*.bin *.iso *.cue *.chd *.cso *.gz *.dump);;"
|
||||
"Single-Track Raw Images (*.bin *.iso);;"
|
||||
"Cue Sheets (*.cue);;"
|
||||
"MAME CHD Images (*.chd);;"
|
||||
"CSO Images (*.cso);;"
|
||||
"GZ Images (*.gz);;"
|
||||
"Block Dumps (*.dump)");
|
||||
|
||||
#ifdef __APPLE__
|
||||
const char* MainWindow::DEFAULT_THEME_NAME = "";
|
||||
|
@ -285,9 +284,8 @@ void MainWindow::setupAdditionalUi()
|
|||
raAction->setChecked(checked);
|
||||
}
|
||||
|
||||
connect(raAction, &QAction::triggered, this, [id = id]() {
|
||||
Host::RunOnCPUThread([id]() { Achievements::RAIntegration::ActivateMenuItem(id); }, false);
|
||||
});
|
||||
connect(raAction, &QAction::triggered, this,
|
||||
[id = id]() { Host::RunOnCPUThread([id]() { Achievements::RAIntegration::ActivateMenuItem(id); }, false); });
|
||||
}
|
||||
});
|
||||
m_ui.menu_Tools->insertMenu(m_ui.menuInput_Recording->menuAction(), raMenu);
|
||||
|
@ -326,11 +324,12 @@ void MainWindow::connectSignals()
|
|||
connect(m_ui.actionDEV9Settings, &QAction::triggered, [this]() { doSettings("Network & HDD"); });
|
||||
connect(m_ui.actionFolderSettings, &QAction::triggered, [this]() { doSettings("Folders"); });
|
||||
connect(m_ui.actionAchievementSettings, &QAction::triggered, [this]() { doSettings("Achievements"); });
|
||||
connect(
|
||||
m_ui.actionControllerSettings, &QAction::triggered, [this]() { doControllerSettings(ControllerSettingsDialog::Category::GlobalSettings); });
|
||||
connect(m_ui.actionHotkeySettings, &QAction::triggered, [this]() { doControllerSettings(ControllerSettingsDialog::Category::HotkeySettings); });
|
||||
connect(
|
||||
m_ui.actionAddGameDirectory, &QAction::triggered, [this]() { getSettingsDialog()->getGameListSettingsWidget()->addSearchDirectory(this); });
|
||||
connect(m_ui.actionControllerSettings, &QAction::triggered,
|
||||
[this]() { doControllerSettings(ControllerSettingsDialog::Category::GlobalSettings); });
|
||||
connect(m_ui.actionHotkeySettings, &QAction::triggered,
|
||||
[this]() { doControllerSettings(ControllerSettingsDialog::Category::HotkeySettings); });
|
||||
connect(m_ui.actionAddGameDirectory, &QAction::triggered,
|
||||
[this]() { getSettingsDialog()->getGameListSettingsWidget()->addSearchDirectory(this); });
|
||||
connect(m_ui.actionScanForNewGames, &QAction::triggered, [this]() { refreshGameList(false); });
|
||||
connect(m_ui.actionRescanAllGames, &QAction::triggered, [this]() { refreshGameList(true); });
|
||||
connect(m_ui.actionViewToolbar, &QAction::toggled, this, &MainWindow::onViewToolbarActionToggled);
|
||||
|
@ -397,8 +396,8 @@ void MainWindow::connectSignals()
|
|||
connect(m_game_list_widget, &GameListWidget::refreshComplete, this, &MainWindow::onGameListRefreshComplete);
|
||||
connect(m_game_list_widget, &GameListWidget::selectionChanged, this, &MainWindow::onGameListSelectionChanged, Qt::QueuedConnection);
|
||||
connect(m_game_list_widget, &GameListWidget::entryActivated, this, &MainWindow::onGameListEntryActivated, Qt::QueuedConnection);
|
||||
connect(
|
||||
m_game_list_widget, &GameListWidget::entryContextMenuRequested, this, &MainWindow::onGameListEntryContextMenuRequested, Qt::QueuedConnection);
|
||||
connect(m_game_list_widget, &GameListWidget::entryContextMenuRequested, this, &MainWindow::onGameListEntryContextMenuRequested,
|
||||
Qt::QueuedConnection);
|
||||
connect(m_game_list_widget, &GameListWidget::addGameDirectoryRequested, this,
|
||||
[this]() { getSettingsDialog()->getGameListSettingsWidget()->addSearchDirectory(this); });
|
||||
}
|
||||
|
@ -432,8 +431,8 @@ void MainWindow::connectVMThreadSignals(EmuThread* thread)
|
|||
GSRendererType::OGL, GSRendererType::VK, GSRendererType::SW, GSRendererType::Null};
|
||||
for (GSRendererType renderer : renderers)
|
||||
{
|
||||
connect(m_ui.menuDebugSwitchRenderer->addAction(QString::fromUtf8(Pcsx2Config::GSOptions::GetRendererName(renderer))), &QAction::triggered,
|
||||
[renderer] { g_emu_thread->switchRenderer(renderer); });
|
||||
connect(m_ui.menuDebugSwitchRenderer->addAction(QString::fromUtf8(Pcsx2Config::GSOptions::GetRendererName(renderer))),
|
||||
&QAction::triggered, [renderer] { g_emu_thread->switchRenderer(renderer); });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -819,8 +818,7 @@ void MainWindow::onBlockDumpActionToggled(bool checked)
|
|||
|
||||
// prompt for a location to save
|
||||
const QString new_dir(
|
||||
QFileDialog::getExistingDirectory(this, tr("Select location to save block dump:"),
|
||||
QString::fromStdString(old_directory)));
|
||||
QFileDialog::getExistingDirectory(this, tr("Select location to save block dump:"), QString::fromStdString(old_directory)));
|
||||
if (new_dir.isEmpty())
|
||||
{
|
||||
// disable it again
|
||||
|
@ -841,11 +839,12 @@ void MainWindow::onShowAdvancedSettingsToggled(bool checked)
|
|||
QCheckBox* cb = new QCheckBox(tr("Do not show again"));
|
||||
QMessageBox mb(this);
|
||||
mb.setWindowTitle(tr("Show Advanced Settings"));
|
||||
mb.setText(
|
||||
tr("Changing advanced settings can have unpredictable effects on games, including graphical glitches, lock-ups, and even corrupted save files. "
|
||||
"We do not recommend changing advanced settings unless you know what you are doing, and the implications of changing each setting.\n\n"
|
||||
"The PCSX2 team will not provide any support for configurations that modify these settings, you are on your own.\n\n"
|
||||
"Are you sure you want to continue?"));
|
||||
mb.setText(tr("Changing advanced settings can have unpredictable effects on games, including graphical glitches, lock-ups, and "
|
||||
"even corrupted save files. "
|
||||
"We do not recommend changing advanced settings unless you know what you are doing, and the implications of changing "
|
||||
"each setting.\n\n"
|
||||
"The PCSX2 team will not provide any support for configurations that modify these settings, you are on your own.\n\n"
|
||||
"Are you sure you want to continue?"));
|
||||
mb.setIcon(QMessageBox::Warning);
|
||||
mb.addButton(QMessageBox::Yes);
|
||||
mb.addButton(QMessageBox::No);
|
||||
|
@ -1099,8 +1098,7 @@ bool MainWindow::shouldHideMainWindow() const
|
|||
{
|
||||
// NOTE: We can't use isRenderingToMain() here, because this happens post-fullscreen-switch.
|
||||
return Host::GetBoolSettingValue("UI", "HideMainWindowWhenRunning", false) ||
|
||||
(g_emu_thread->shouldRenderToMain() && isRenderingFullscreen()) ||
|
||||
QtHost::InNoGUIMode();
|
||||
(g_emu_thread->shouldRenderToMain() && isRenderingFullscreen()) || QtHost::InNoGUIMode();
|
||||
}
|
||||
|
||||
void MainWindow::switchToGameListView()
|
||||
|
@ -1175,7 +1173,8 @@ void MainWindow::runOnUIThread(const std::function<void()>& func)
|
|||
func();
|
||||
}
|
||||
|
||||
bool MainWindow::requestShutdown(bool allow_confirm /* = true */, bool allow_save_to_state /* = true */, bool default_save_to_state /* = true */, bool block_until_done /* = false */)
|
||||
bool MainWindow::requestShutdown(bool allow_confirm /* = true */, bool allow_save_to_state /* = true */,
|
||||
bool default_save_to_state /* = true */, bool block_until_done /* = false */)
|
||||
{
|
||||
if (!s_vm_valid)
|
||||
return true;
|
||||
|
@ -1312,8 +1311,8 @@ void MainWindow::onGameListEntryActivated()
|
|||
// we might still be saving a resume state...
|
||||
VMManager::WaitForSaveStateFlush();
|
||||
|
||||
const std::optional<bool> resume = promptForResumeState(
|
||||
QString::fromStdString(VMManager::GetSaveStateFileName(entry->serial.c_str(), entry->crc, -1)));
|
||||
const std::optional<bool> resume =
|
||||
promptForResumeState(QString::fromStdString(VMManager::GetSaveStateFileName(entry->serial.c_str(), entry->crc, -1)));
|
||||
if (!resume.has_value())
|
||||
{
|
||||
// cancelled
|
||||
|
@ -1334,9 +1333,14 @@ void MainWindow::onGameListEntryContextMenuRequested(const QPoint& point)
|
|||
if (entry)
|
||||
{
|
||||
QAction* action = menu.addAction(tr("Properties..."));
|
||||
action->setEnabled(!entry->serial.empty());
|
||||
action->setEnabled(!entry->serial.empty() || entry->type == GameList::EntryType::ELF);
|
||||
if (action->isEnabled())
|
||||
connect(action, &QAction::triggered, [entry]() { SettingsDialog::openGamePropertiesDialog(entry, entry->serial, entry->crc); });
|
||||
{
|
||||
connect(action, &QAction::triggered, [entry]() {
|
||||
SettingsDialog::openGamePropertiesDialog(
|
||||
entry, (entry->type != GameList::EntryType::ELF) ? std::string_view(entry->serial) : std::string_view(), entry->crc);
|
||||
});
|
||||
}
|
||||
|
||||
action = menu.addAction(tr("Open Containing Directory..."));
|
||||
connect(action, &QAction::triggered, [this, entry]() {
|
||||
|
@ -1350,8 +1354,7 @@ void MainWindow::onGameListEntryContextMenuRequested(const QPoint& point)
|
|||
connect(menu.addAction(tr("Exclude From List")), &QAction::triggered,
|
||||
[this, entry]() { getSettingsDialog()->getGameListSettingsWidget()->addExcludedPath(entry->path); });
|
||||
|
||||
connect(menu.addAction(tr("Reset Play Time")), &QAction::triggered,
|
||||
[this, entry]() { clearGameListEntryPlayTime(entry); });
|
||||
connect(menu.addAction(tr("Reset Play Time")), &QAction::triggered, [this, entry]() { clearGameListEntryPlayTime(entry); });
|
||||
|
||||
menu.addSeparator();
|
||||
|
||||
|
@ -1400,7 +1403,8 @@ void MainWindow::onGameListEntryContextMenuRequested(const QPoint& point)
|
|||
|
||||
void MainWindow::onStartFileActionTriggered()
|
||||
{
|
||||
const QString path(QDir::toNativeSeparators(QFileDialog::getOpenFileName(this, tr("Start File"), QString(), tr(OPEN_FILE_FILTER), nullptr)));
|
||||
const QString path(
|
||||
QDir::toNativeSeparators(QFileDialog::getOpenFileName(this, tr("Start File"), QString(), tr(OPEN_FILE_FILTER), nullptr)));
|
||||
if (path.isEmpty())
|
||||
return;
|
||||
|
||||
|
@ -1425,7 +1429,8 @@ void MainWindow::onStartBIOSActionTriggered()
|
|||
void MainWindow::onChangeDiscFromFileActionTriggered()
|
||||
{
|
||||
VMLock lock(pauseAndLockVM());
|
||||
QString filename = QFileDialog::getOpenFileName(lock.getDialogParent(), tr("Select Disc Image"), QString(), tr(DISC_IMAGE_FILTER), nullptr);
|
||||
QString filename =
|
||||
QFileDialog::getOpenFileName(lock.getDialogParent(), tr("Select Disc Image"), QString(), tr(DISC_IMAGE_FILTER), nullptr);
|
||||
if (filename.isEmpty())
|
||||
return;
|
||||
|
||||
|
@ -1457,7 +1462,9 @@ void MainWindow::onChangeDiscMenuAboutToShow()
|
|||
// TODO: This is where we would populate the playlist if there is one.
|
||||
}
|
||||
|
||||
void MainWindow::onChangeDiscMenuAboutToHide() {}
|
||||
void MainWindow::onChangeDiscMenuAboutToHide()
|
||||
{
|
||||
}
|
||||
|
||||
void MainWindow::onLoadStateMenuAboutToShow()
|
||||
{
|
||||
|
@ -1516,13 +1523,16 @@ void MainWindow::onViewGamePropertiesActionTriggered()
|
|||
return;
|
||||
|
||||
// prefer to use a game list entry, if we have one, that way the summary is populated
|
||||
if (!m_current_disc_path.isEmpty())
|
||||
if (!m_current_disc_path.isEmpty() || !m_current_elf_override.isEmpty())
|
||||
{
|
||||
auto lock = GameList::GetLock();
|
||||
const GameList::Entry* entry = GameList::GetEntryForPath(m_current_disc_path.toUtf8().constData());
|
||||
const GameList::Entry* entry = m_current_elf_override.isEmpty() ?
|
||||
GameList::GetEntryForPath(m_current_disc_path.toUtf8().constData()) :
|
||||
GameList::GetEntryForPath(m_current_elf_override.toUtf8().constData());
|
||||
if (entry)
|
||||
{
|
||||
SettingsDialog::openGamePropertiesDialog(entry, entry->serial, entry->crc);
|
||||
SettingsDialog::openGamePropertiesDialog(
|
||||
entry, m_current_elf_override.isEmpty() ? std::string_view(entry->serial) : std::string_view(), entry->crc);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -1573,11 +1583,10 @@ void MainWindow::checkForUpdates(bool display_message)
|
|||
|
||||
QString message;
|
||||
#ifdef _WIN32
|
||||
message =
|
||||
tr("<p>Sorry, you are trying to update a PCSX2 version which is not an official GitHub release. To "
|
||||
"prevent incompatibilities, the auto-updater is only enabled on official builds.</p>"
|
||||
"<p>To obtain an official build, please download from the link below:</p>"
|
||||
"<p><a href=\"https://pcsx2.net/downloads/\">https://pcsx2.net/downloads/</a></p>");
|
||||
message = tr("<p>Sorry, you are trying to update a PCSX2 version which is not an official GitHub release. To "
|
||||
"prevent incompatibilities, the auto-updater is only enabled on official builds.</p>"
|
||||
"<p>To obtain an official build, please download from the link below:</p>"
|
||||
"<p><a href=\"https://pcsx2.net/downloads/\">https://pcsx2.net/downloads/</a></p>");
|
||||
#else
|
||||
message = tr("Automatic updating is not supported on the current platform.");
|
||||
#endif
|
||||
|
@ -1648,21 +1657,18 @@ void MainWindow::onInputRecNewActionTriggered()
|
|||
|
||||
if (result == QDialog::Accepted)
|
||||
{
|
||||
Host::RunOnCPUThread([&, filePath = dlg.getFilePath(),
|
||||
fromSavestate = dlg.getInputRecType() == InputRecording::Type::FROM_SAVESTATE,
|
||||
authorName = dlg.getAuthorName()]() {
|
||||
if (g_InputRecording.create(
|
||||
filePath,
|
||||
fromSavestate,
|
||||
authorName))
|
||||
{
|
||||
QtHost::RunOnUIThread([&]() {
|
||||
m_ui.actionInputRecNew->setEnabled(false);
|
||||
m_ui.actionInputRecStop->setEnabled(true);
|
||||
m_ui.actionReset->setEnabled(!g_InputRecording.isTypeSavestate());
|
||||
});
|
||||
}
|
||||
});
|
||||
Host::RunOnCPUThread(
|
||||
[&, filePath = dlg.getFilePath(), fromSavestate = dlg.getInputRecType() == InputRecording::Type::FROM_SAVESTATE,
|
||||
authorName = dlg.getAuthorName()]() {
|
||||
if (g_InputRecording.create(filePath, fromSavestate, authorName))
|
||||
{
|
||||
QtHost::RunOnUIThread([&]() {
|
||||
m_ui.actionInputRecNew->setEnabled(false);
|
||||
m_ui.actionInputRecStop->setEnabled(true);
|
||||
m_ui.actionReset->setEnabled(!g_InputRecording.isTypeSavestate());
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (wasRunning && !wasPaused)
|
||||
|
@ -1702,9 +1708,7 @@ void MainWindow::onInputRecPlayActionTriggered()
|
|||
{
|
||||
if (g_InputRecording.isActive())
|
||||
{
|
||||
Host::RunOnCPUThread([]() {
|
||||
g_InputRecording.stop();
|
||||
});
|
||||
Host::RunOnCPUThread([]() { g_InputRecording.stop(); });
|
||||
m_ui.actionInputRecStop->setEnabled(false);
|
||||
}
|
||||
Host::RunOnCPUThread([&, filename = fileNames.first().toStdString()]() {
|
||||
|
@ -1854,9 +1858,10 @@ void MainWindow::onVMStopped()
|
|||
m_game_list_widget->refresh(false);
|
||||
}
|
||||
|
||||
void MainWindow::onGameChanged(const QString& path, const QString& serial, const QString& name, quint32 crc)
|
||||
void MainWindow::onGameChanged(const QString& path, const QString& elf_override, const QString& serial, const QString& name, quint32 crc)
|
||||
{
|
||||
m_current_disc_path = path;
|
||||
m_current_elf_override = elf_override;
|
||||
m_current_game_serial = serial;
|
||||
m_current_game_name = name;
|
||||
m_current_game_crc = crc;
|
||||
|
@ -1952,8 +1957,8 @@ void MainWindow::registerForDeviceNotifications()
|
|||
#ifdef _WIN32
|
||||
// We use these notifications to detect when a controller is connected or disconnected.
|
||||
DEV_BROADCAST_DEVICEINTERFACE_W filter = {sizeof(DEV_BROADCAST_DEVICEINTERFACE_W), DBT_DEVTYP_DEVICEINTERFACE};
|
||||
m_device_notification_handle = RegisterDeviceNotificationW((HANDLE)winId(), &filter,
|
||||
DEVICE_NOTIFY_WINDOW_HANDLE | DEVICE_NOTIFY_ALL_INTERFACE_CLASSES);
|
||||
m_device_notification_handle =
|
||||
RegisterDeviceNotificationW((HANDLE)winId(), &filter, DEVICE_NOTIFY_WINDOW_HANDLE | DEVICE_NOTIFY_ALL_INTERFACE_CLASSES);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -2043,8 +2048,8 @@ DisplayWidget* MainWindow::createDisplay(bool fullscreen, bool render_to_main)
|
|||
|
||||
DisplayWidget* MainWindow::updateDisplay(bool fullscreen, bool render_to_main, bool surfaceless)
|
||||
{
|
||||
DevCon.WriteLn("updateDisplay() fullscreen=%s render_to_main=%s surfaceless=%s",
|
||||
fullscreen ? "true" : "false", render_to_main ? "true" : "false", surfaceless ? "true" : "false");
|
||||
DevCon.WriteLn("updateDisplay() fullscreen=%s render_to_main=%s surfaceless=%s", fullscreen ? "true" : "false",
|
||||
render_to_main ? "true" : "false", surfaceless ? "true" : "false");
|
||||
|
||||
QWidget* container = m_display_container ? static_cast<QWidget*>(m_display_container) : static_cast<QWidget*>(m_display_widget);
|
||||
const bool is_fullscreen = isRenderingFullscreen();
|
||||
|
@ -2059,7 +2064,8 @@ DisplayWidget* MainWindow::updateDisplay(bool fullscreen, bool render_to_main, b
|
|||
// .. except on Wayland, where everything tends to break if you don't recreate.
|
||||
const bool has_container = (m_display_container != nullptr);
|
||||
const bool needs_container = DisplayContainer::isNeeded(fullscreen, render_to_main);
|
||||
if (!is_rendering_to_main && !render_to_main && !is_exclusive_fullscreen && has_container == needs_container && !needs_container && !changing_surfaceless)
|
||||
if (!is_rendering_to_main && !render_to_main && !is_exclusive_fullscreen && has_container == needs_container && !needs_container &&
|
||||
!changing_surfaceless)
|
||||
{
|
||||
DevCon.WriteLn("Toggling to %s without recreating surface", (fullscreen ? "fullscreen" : "windowed"));
|
||||
if (g_host_display->IsFullscreen())
|
||||
|
@ -2353,8 +2359,7 @@ SettingsDialog* MainWindow::getSettingsDialog()
|
|||
if (!m_settings_dialog)
|
||||
{
|
||||
m_settings_dialog = new SettingsDialog(this);
|
||||
connect(
|
||||
m_settings_dialog->getInterfaceSettingsWidget(), &InterfaceSettingsWidget::themeChanged, this, &MainWindow::updateTheme);
|
||||
connect(m_settings_dialog->getInterfaceSettingsWidget(), &InterfaceSettingsWidget::themeChanged, this, &MainWindow::updateTheme);
|
||||
}
|
||||
|
||||
return m_settings_dialog;
|
||||
|
@ -2461,14 +2466,16 @@ void MainWindow::startGameListEntry(const GameList::Entry* entry, std::optional<
|
|||
|
||||
void MainWindow::setGameListEntryCoverImage(const GameList::Entry* entry)
|
||||
{
|
||||
const QString filename(QFileDialog::getOpenFileName(this, tr("Select Cover Image"), QString(), tr("All Cover Image Types (*.jpg *.jpeg *.png)")));
|
||||
const QString filename(
|
||||
QFileDialog::getOpenFileName(this, tr("Select Cover Image"), QString(), tr("All Cover Image Types (*.jpg *.jpeg *.png)")));
|
||||
if (filename.isEmpty())
|
||||
return;
|
||||
|
||||
if (!GameList::GetCoverImagePathForEntry(entry).empty())
|
||||
{
|
||||
if (QMessageBox::question(this, tr("Cover Already Exists"), tr("A cover image for this game already exists, do you wish to replace it?"),
|
||||
QMessageBox::Yes, QMessageBox::No) != QMessageBox::Yes)
|
||||
if (QMessageBox::question(this, tr("Cover Already Exists"),
|
||||
tr("A cover image for this game already exists, do you wish to replace it?"), QMessageBox::Yes,
|
||||
QMessageBox::No) != QMessageBox::Yes)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -2652,10 +2659,8 @@ void MainWindow::populateLoadStateMenu(QMenu* menu, const QString& filename, con
|
|||
if (has_any_states)
|
||||
{
|
||||
connect(delete_save_states_action, &QAction::triggered, this, [this, serial, crc] {
|
||||
if (QMessageBox::warning(
|
||||
this, tr("Delete Save States"),
|
||||
tr("Are you sure you want to delete all save states for %1?\n\nThe saves will not be recoverable.")
|
||||
.arg(serial),
|
||||
if (QMessageBox::warning(this, tr("Delete Save States"),
|
||||
tr("Are you sure you want to delete all save states for %1?\n\nThe saves will not be recoverable.").arg(serial),
|
||||
QMessageBox::Yes, QMessageBox::No) != QMessageBox::Yes)
|
||||
{
|
||||
return;
|
||||
|
@ -2728,8 +2733,7 @@ void MainWindow::doStartFile(std::optional<CDVD_SourceType> source, const QStrin
|
|||
VMManager::WaitForSaveStateFlush();
|
||||
|
||||
const std::optional<bool> resume(
|
||||
promptForResumeState(
|
||||
QString::fromStdString(VMManager::GetSaveStateFileName(params->filename.c_str(), -1))));
|
||||
promptForResumeState(QString::fromStdString(VMManager::GetSaveStateFileName(params->filename.c_str(), -1))));
|
||||
if (!resume.has_value())
|
||||
return;
|
||||
else if (resume.value())
|
||||
|
@ -2743,8 +2747,8 @@ void MainWindow::doDiscChange(CDVD_SourceType source, const QString& path)
|
|||
bool reset_system = false;
|
||||
if (!m_was_disc_change_request)
|
||||
{
|
||||
const int choice = QMessageBox::question(this, tr("Confirm Disc Change"), tr("Do you want to swap discs or boot the new image (via system reset)?"),
|
||||
tr("Swap Disc"), tr("Reset"), tr("Cancel"), 0, 2);
|
||||
const int choice = QMessageBox::question(this, tr("Confirm Disc Change"),
|
||||
tr("Do you want to swap discs or boot the new image (via system reset)?"), tr("Swap Disc"), tr("Reset"), tr("Cancel"), 0, 2);
|
||||
if (choice == 2)
|
||||
return;
|
||||
reset_system = (choice != 0);
|
||||
|
@ -2776,6 +2780,11 @@ MainWindow::VMLock MainWindow::pauseAndLockVM()
|
|||
return VMLock(dialog_parent, was_paused, was_fullscreen);
|
||||
}
|
||||
|
||||
void MainWindow::rescanFile(const std::string& path)
|
||||
{
|
||||
m_game_list_widget->rescanFile(path);
|
||||
}
|
||||
|
||||
MainWindow::VMLock::VMLock(QWidget* dialog_parent, bool was_paused, bool was_fullscreen)
|
||||
: m_dialog_parent(dialog_parent)
|
||||
, m_was_paused(was_paused)
|
||||
|
|
|
@ -78,6 +78,12 @@ public:
|
|||
/// Default theme name for the platform.
|
||||
static const char* DEFAULT_THEME_NAME;
|
||||
|
||||
/// Default filter for opening a file.
|
||||
static const char* OPEN_FILE_FILTER;
|
||||
|
||||
/// Default filter for opening a disc image.
|
||||
static const char* DISC_IMAGE_FILTER;
|
||||
|
||||
public:
|
||||
MainWindow();
|
||||
~MainWindow();
|
||||
|
@ -100,6 +106,9 @@ public:
|
|||
__fi QLabel* getStatusFPSWidget() const { return m_status_fps_widget; }
|
||||
__fi QLabel* getStatusVPSWidget() const { return m_status_vps_widget; }
|
||||
|
||||
/// Rescans a single file. NOTE: Happens on UI thread.
|
||||
void rescanFile(const std::string& path);
|
||||
|
||||
public Q_SLOTS:
|
||||
void checkForUpdates(bool display_message);
|
||||
void refreshGameList(bool invalidate_cache);
|
||||
|
@ -108,7 +117,8 @@ public Q_SLOTS:
|
|||
void reportError(const QString& title, const QString& message);
|
||||
bool confirmMessage(const QString& title, const QString& message);
|
||||
void runOnUIThread(const std::function<void()>& func);
|
||||
bool requestShutdown(bool allow_confirm = true, bool allow_save_to_state = true, bool default_save_to_state = true, bool block_until_done = false);
|
||||
bool requestShutdown(
|
||||
bool allow_confirm = true, bool allow_save_to_state = true, bool default_save_to_state = true, bool block_until_done = false);
|
||||
void requestExit();
|
||||
void checkForSettingChanges();
|
||||
std::optional<WindowInfo> getWindowInfo();
|
||||
|
@ -172,7 +182,7 @@ private Q_SLOTS:
|
|||
void onVMResumed();
|
||||
void onVMStopped();
|
||||
|
||||
void onGameChanged(const QString& path, const QString& serial, const QString& name, quint32 crc);
|
||||
void onGameChanged(const QString& path, const QString& elf_override, const QString& serial, const QString& name, quint32 crc);
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent* event) override;
|
||||
|
@ -234,8 +244,8 @@ private:
|
|||
|
||||
QString getDiscDevicePath(const QString& title);
|
||||
|
||||
void startGameListEntry(const GameList::Entry* entry, std::optional<s32> save_slot = std::nullopt,
|
||||
std::optional<bool> fast_boot = std::nullopt);
|
||||
void startGameListEntry(
|
||||
const GameList::Entry* entry, std::optional<s32> save_slot = std::nullopt, std::optional<bool> fast_boot = std::nullopt);
|
||||
void setGameListEntryCoverImage(const GameList::Entry* entry);
|
||||
void clearGameListEntryPlayTime(const GameList::Entry* entry);
|
||||
|
||||
|
@ -268,6 +278,7 @@ private:
|
|||
QLabel* m_status_resolution_widget = nullptr;
|
||||
|
||||
QString m_current_disc_path;
|
||||
QString m_current_elf_override;
|
||||
QString m_current_game_serial;
|
||||
QString m_current_game_name;
|
||||
quint32 m_current_game_crc;
|
||||
|
|
|
@ -74,15 +74,16 @@ EmuThread* g_emu_thread = nullptr;
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
// Local function declarations
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
namespace QtHost {
|
||||
static void PrintCommandLineVersion();
|
||||
static void PrintCommandLineHelp(const std::string_view& progname);
|
||||
static std::shared_ptr<VMBootParameters>& AutoBoot(std::shared_ptr<VMBootParameters>& autoboot);
|
||||
static bool ParseCommandLineOptions(const QStringList& args, std::shared_ptr<VMBootParameters>& autoboot);
|
||||
static bool InitializeConfig();
|
||||
static void SaveSettings();
|
||||
static void HookSignals();
|
||||
}
|
||||
namespace QtHost
|
||||
{
|
||||
static void PrintCommandLineVersion();
|
||||
static void PrintCommandLineHelp(const std::string_view& progname);
|
||||
static std::shared_ptr<VMBootParameters>& AutoBoot(std::shared_ptr<VMBootParameters>& autoboot);
|
||||
static bool ParseCommandLineOptions(const QStringList& args, std::shared_ptr<VMBootParameters>& autoboot);
|
||||
static bool InitializeConfig();
|
||||
static void SaveSettings();
|
||||
static void HookSignals();
|
||||
} // namespace QtHost
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Local variable declarations
|
||||
|
@ -151,8 +152,8 @@ bool EmuThread::confirmMessage(const QString& title, const QString& message)
|
|||
{
|
||||
// This is definitely deadlock risky, but unlikely to happen (why would GS be confirming?).
|
||||
bool result = false;
|
||||
QMetaObject::invokeMethod(g_emu_thread, "confirmMessage", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(bool, result), Q_ARG(const QString&, title), Q_ARG(const QString&, message));
|
||||
QMetaObject::invokeMethod(g_emu_thread, "confirmMessage", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, result),
|
||||
Q_ARG(const QString&, title), Q_ARG(const QString&, message));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -231,8 +232,7 @@ void EmuThread::startVM(std::shared_ptr<VMBootParameters> boot_params)
|
|||
{
|
||||
if (!isOnEmuThread())
|
||||
{
|
||||
QMetaObject::invokeMethod(this, "startVM", Qt::QueuedConnection,
|
||||
Q_ARG(std::shared_ptr<VMBootParameters>, boot_params));
|
||||
QMetaObject::invokeMethod(this, "startVM", Qt::QueuedConnection, Q_ARG(std::shared_ptr<VMBootParameters>, boot_params));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -460,9 +460,8 @@ void EmuThread::startBackgroundControllerPollTimer()
|
|||
if (m_background_controller_polling_timer->isActive())
|
||||
return;
|
||||
|
||||
m_background_controller_polling_timer->start(FullscreenUI::IsInitialized() ?
|
||||
FULLSCREEN_UI_CONTROLLER_POLLING_INTERVAL :
|
||||
BACKGROUND_CONTROLLER_POLLING_INTERVAL);
|
||||
m_background_controller_polling_timer->start(
|
||||
FullscreenUI::IsInitialized() ? FULLSCREEN_UI_CONTROLLER_POLLING_INTERVAL : BACKGROUND_CONTROLLER_POLLING_INTERVAL);
|
||||
}
|
||||
|
||||
void EmuThread::stopBackgroundControllerPollTimer()
|
||||
|
@ -845,9 +844,7 @@ void EmuThread::queueSnapshot(quint32 gsdump_frames)
|
|||
if (!VMManager::HasValidVM())
|
||||
return;
|
||||
|
||||
GetMTGS().RunOnGSThread([gsdump_frames]() {
|
||||
GSQueueSnapshot(std::string(), gsdump_frames);
|
||||
});
|
||||
GetMTGS().RunOnGSThread([gsdump_frames]() { GSQueueSnapshot(std::string(), gsdump_frames); });
|
||||
}
|
||||
|
||||
void EmuThread::updateDisplay()
|
||||
|
@ -1014,10 +1011,12 @@ void Host::OnVMResumed()
|
|||
emit g_emu_thread->onVMResumed();
|
||||
}
|
||||
|
||||
void Host::OnGameChanged(const std::string& disc_path, const std::string& game_serial, const std::string& game_name, u32 game_crc)
|
||||
void Host::OnGameChanged(const std::string& disc_path, const std::string& elf_override, const std::string& game_serial,
|
||||
const std::string& game_name, u32 game_crc)
|
||||
{
|
||||
CommonHost::OnGameChanged(disc_path, game_serial, game_name, game_crc);
|
||||
emit g_emu_thread->onGameChanged(QString::fromStdString(disc_path), QString::fromStdString(game_serial), QString::fromStdString(game_name), game_crc);
|
||||
CommonHost::OnGameChanged(disc_path, elf_override, game_serial, game_name, game_crc);
|
||||
emit g_emu_thread->onGameChanged(QString::fromStdString(disc_path), QString::fromStdString(elf_override),
|
||||
QString::fromStdString(game_serial), QString::fromStdString(game_name), game_crc);
|
||||
}
|
||||
|
||||
void EmuThread::updatePerformanceMetrics(bool force)
|
||||
|
@ -1030,12 +1029,11 @@ void EmuThread::updatePerformanceMetrics(bool force)
|
|||
QString gs_stat;
|
||||
if (THREAD_VU1)
|
||||
{
|
||||
gs_stat =
|
||||
QStringLiteral("%1 | EE: %2% | VU: %3% | GS: %4%")
|
||||
.arg(gs_stat_str.c_str())
|
||||
.arg(PerformanceMetrics::GetCPUThreadUsage(), 0, 'f', 0)
|
||||
.arg(PerformanceMetrics::GetVUThreadUsage(), 0, 'f', 0)
|
||||
.arg(PerformanceMetrics::GetGSThreadUsage(), 0, 'f', 0);
|
||||
gs_stat = QStringLiteral("%1 | EE: %2% | VU: %3% | GS: %4%")
|
||||
.arg(gs_stat_str.c_str())
|
||||
.arg(PerformanceMetrics::GetCPUThreadUsage(), 0, 'f', 0)
|
||||
.arg(PerformanceMetrics::GetVUThreadUsage(), 0, 'f', 0)
|
||||
.arg(PerformanceMetrics::GetGSThreadUsage(), 0, 'f', 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1055,17 +1053,18 @@ void EmuThread::updatePerformanceMetrics(bool force)
|
|||
int iwidth, iheight;
|
||||
GSgetInternalResolution(&iwidth, &iheight);
|
||||
|
||||
if (iwidth != m_last_internal_width || iheight != m_last_internal_height ||
|
||||
speed != m_last_speed || gfps != m_last_game_fps || vfps != m_last_video_fps ||
|
||||
renderer != m_last_renderer || force)
|
||||
if (iwidth != m_last_internal_width || iheight != m_last_internal_height || speed != m_last_speed || gfps != m_last_game_fps ||
|
||||
vfps != m_last_video_fps || renderer != m_last_renderer || force)
|
||||
{
|
||||
if (iwidth == 0 && iheight == 0)
|
||||
{
|
||||
// if we don't have width/height yet, we're not going to have fps either.
|
||||
// and we'll probably be <100% due to compiling. so just leave it blank for now.
|
||||
QString blank;
|
||||
QMetaObject::invokeMethod(g_main_window->getStatusRendererWidget(), "setText", Qt::QueuedConnection, Q_ARG(const QString&, blank));
|
||||
QMetaObject::invokeMethod(g_main_window->getStatusResolutionWidget(), "setText", Qt::QueuedConnection, Q_ARG(const QString&, blank));
|
||||
QMetaObject::invokeMethod(
|
||||
g_main_window->getStatusRendererWidget(), "setText", Qt::QueuedConnection, Q_ARG(const QString&, blank));
|
||||
QMetaObject::invokeMethod(
|
||||
g_main_window->getStatusResolutionWidget(), "setText", Qt::QueuedConnection, Q_ARG(const QString&, blank));
|
||||
QMetaObject::invokeMethod(g_main_window->getStatusFPSWidget(), "setText", Qt::QueuedConnection, Q_ARG(const QString&, blank));
|
||||
QMetaObject::invokeMethod(g_main_window->getStatusVPSWidget(), "setText", Qt::QueuedConnection, Q_ARG(const QString&, blank));
|
||||
return;
|
||||
|
@ -1081,9 +1080,7 @@ void EmuThread::updatePerformanceMetrics(bool force)
|
|||
if (iwidth != m_last_internal_width || iheight != m_last_internal_height || force)
|
||||
{
|
||||
QMetaObject::invokeMethod(g_main_window->getStatusResolutionWidget(), "setText", Qt::QueuedConnection,
|
||||
Q_ARG(const QString&, tr("%1x%2")
|
||||
.arg(iwidth)
|
||||
.arg(iheight)));
|
||||
Q_ARG(const QString&, tr("%1x%2").arg(iwidth).arg(iheight)));
|
||||
m_last_internal_width = iwidth;
|
||||
m_last_internal_height = iheight;
|
||||
}
|
||||
|
@ -1091,17 +1088,14 @@ void EmuThread::updatePerformanceMetrics(bool force)
|
|||
if (gfps != m_last_game_fps || force)
|
||||
{
|
||||
QMetaObject::invokeMethod(g_main_window->getStatusFPSWidget(), "setText", Qt::QueuedConnection,
|
||||
Q_ARG(const QString&, tr("Game: %1 FPS")
|
||||
.arg(gfps, 0, 'f', 0)));
|
||||
Q_ARG(const QString&, tr("Game: %1 FPS").arg(gfps, 0, 'f', 0)));
|
||||
m_last_game_fps = gfps;
|
||||
}
|
||||
|
||||
if (speed != m_last_speed || vfps != m_last_video_fps || force)
|
||||
{
|
||||
QMetaObject::invokeMethod(g_main_window->getStatusVPSWidget(), "setText", Qt::QueuedConnection,
|
||||
Q_ARG(const QString&, tr("Video: %1 FPS (%2%)")
|
||||
.arg(vfps, 0, 'f', 0)
|
||||
.arg(speed, 0, 'f', 0)));
|
||||
Q_ARG(const QString&, tr("Video: %1 FPS (%2%)").arg(vfps, 0, 'f', 0).arg(speed, 0, 'f', 0)));
|
||||
m_last_speed = speed;
|
||||
m_last_video_fps = vfps;
|
||||
}
|
||||
|
@ -1144,10 +1138,9 @@ void Host::OnAchievementsRefreshed()
|
|||
achievement_count = Achievements::GetAchievementCount();
|
||||
max_points = Achievements::GetMaximumPointsForGame();
|
||||
|
||||
game_info = qApp->translate("EmuThread",
|
||||
"Game ID: %1\n"
|
||||
"Game Title: %2\n"
|
||||
"Achievements: %5 (%6)\n\n")
|
||||
game_info = qApp->translate("EmuThread", "Game ID: %1\n"
|
||||
"Game Title: %2\n"
|
||||
"Achievements: %5 (%6)\n\n")
|
||||
.arg(game_id)
|
||||
.arg(QString::fromStdString(Achievements::GetGameTitle()))
|
||||
.arg(achievement_count)
|
||||
|
@ -1183,15 +1176,13 @@ void Host::RunOnCPUThread(std::function<void()> function, bool block /* = false
|
|||
return;
|
||||
}
|
||||
|
||||
QMetaObject::invokeMethod(g_emu_thread, "runOnCPUThread",
|
||||
block ? Qt::BlockingQueuedConnection : Qt::QueuedConnection,
|
||||
QMetaObject::invokeMethod(g_emu_thread, "runOnCPUThread", block ? Qt::BlockingQueuedConnection : Qt::QueuedConnection,
|
||||
Q_ARG(const std::function<void()>&, std::move(function)));
|
||||
}
|
||||
|
||||
void Host::RefreshGameListAsync(bool invalidate_cache)
|
||||
{
|
||||
QMetaObject::invokeMethod(g_main_window, "refreshGameList", Qt::QueuedConnection,
|
||||
Q_ARG(bool, invalidate_cache));
|
||||
QMetaObject::invokeMethod(g_main_window, "refreshGameList", Qt::QueuedConnection, Q_ARG(bool, invalidate_cache));
|
||||
}
|
||||
|
||||
void Host::CancelGameListRefresh()
|
||||
|
@ -1213,9 +1204,8 @@ void Host::RequestVMShutdown(bool allow_confirm, bool allow_save_state, bool def
|
|||
return;
|
||||
|
||||
// Run it on the host thread, that way we get the confirm prompt (if enabled).
|
||||
QMetaObject::invokeMethod(g_main_window, "requestShutdown", Qt::QueuedConnection,
|
||||
Q_ARG(bool, allow_confirm), Q_ARG(bool, allow_save_state),
|
||||
Q_ARG(bool, default_save_state), Q_ARG(bool, false));
|
||||
QMetaObject::invokeMethod(g_main_window, "requestShutdown", Qt::QueuedConnection, Q_ARG(bool, allow_confirm),
|
||||
Q_ARG(bool, allow_save_state), Q_ARG(bool, default_save_state), Q_ARG(bool, false));
|
||||
}
|
||||
|
||||
bool Host::IsFullscreen()
|
||||
|
@ -1254,7 +1244,8 @@ bool QtHost::InitializeConfig()
|
|||
// If the config file doesn't exist, assume this is a new install and don't prompt to overwrite.
|
||||
if (FileSystem::FileExists(s_base_settings_interface->GetFileName().c_str()) &&
|
||||
QMessageBox::question(nullptr, QStringLiteral("PCSX2"),
|
||||
QStringLiteral("Settings failed to load, or are the incorrect version. Clicking Yes will reset all settings to defaults. Do you want to continue?")) != QMessageBox::Yes)
|
||||
QStringLiteral("Settings failed to load, or are the incorrect version. Clicking Yes will reset all settings to defaults. "
|
||||
"Do you want to continue?")) != QMessageBox::Yes)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -1339,8 +1330,7 @@ bool QtHost::ShouldShowAdvancedSettings()
|
|||
void QtHost::RunOnUIThread(const std::function<void()>& func, bool block /*= false*/)
|
||||
{
|
||||
// main window always exists, so it's fine to attach it to that.
|
||||
QMetaObject::invokeMethod(g_main_window, "runOnUIThread",
|
||||
block ? Qt::BlockingQueuedConnection : Qt::QueuedConnection,
|
||||
QMetaObject::invokeMethod(g_main_window, "runOnUIThread", block ? Qt::BlockingQueuedConnection : Qt::QueuedConnection,
|
||||
Q_ARG(const std::function<void()>&, func));
|
||||
}
|
||||
|
||||
|
@ -1369,10 +1359,8 @@ QString QtHost::GetAppNameAndVersion()
|
|||
else if constexpr (PCSX2_isReleaseVersion)
|
||||
{
|
||||
#define APPNAME_STRINGIZE(x) #x
|
||||
ret = QStringLiteral("PCSX2 "
|
||||
APPNAME_STRINGIZE(PCSX2_VersionHi) "."
|
||||
APPNAME_STRINGIZE(PCSX2_VersionMid) "."
|
||||
APPNAME_STRINGIZE(PCSX2_VersionLo));
|
||||
ret = QStringLiteral(
|
||||
"PCSX2 " APPNAME_STRINGIZE(PCSX2_VersionHi) "." APPNAME_STRINGIZE(PCSX2_VersionMid) "." APPNAME_STRINGIZE(PCSX2_VersionLo));
|
||||
#undef APPNAME_STRINGIZE
|
||||
}
|
||||
else
|
||||
|
@ -1431,14 +1419,12 @@ void Host::ReportErrorAsync(const std::string_view& title, const std::string_vie
|
|||
{
|
||||
if (!title.empty() && !message.empty())
|
||||
{
|
||||
Console.Error("ReportErrorAsync: %.*s: %.*s",
|
||||
static_cast<int>(title.size()), title.data(),
|
||||
static_cast<int>(message.size()), message.data());
|
||||
Console.Error(
|
||||
"ReportErrorAsync: %.*s: %.*s", static_cast<int>(title.size()), title.data(), static_cast<int>(message.size()), message.data());
|
||||
}
|
||||
else if (!message.empty())
|
||||
{
|
||||
Console.Error("ReportErrorAsync: %.*s",
|
||||
static_cast<int>(message.size()), message.data());
|
||||
Console.Error("ReportErrorAsync: %.*s", static_cast<int>(message.size()), message.data());
|
||||
}
|
||||
|
||||
QMetaObject::invokeMethod(g_main_window, "reportError", Qt::QueuedConnection,
|
||||
|
@ -1455,9 +1441,7 @@ bool Host::ConfirmMessage(const std::string_view& title, const std::string_view&
|
|||
|
||||
void Host::OpenURL(const std::string_view& url)
|
||||
{
|
||||
QtHost::RunOnUIThread([url = QtUtils::StringViewToQString(url)]() {
|
||||
QtUtils::OpenURL(g_main_window, QUrl(url));
|
||||
});
|
||||
QtHost::RunOnUIThread([url = QtUtils::StringViewToQString(url)]() { QtUtils::OpenURL(g_main_window, QUrl(url)); });
|
||||
}
|
||||
|
||||
bool Host::CopyTextToClipboard(const std::string_view& text)
|
||||
|
@ -1493,15 +1477,13 @@ std::optional<WindowInfo> Host::GetTopLevelWindowInfo()
|
|||
|
||||
void Host::OnInputDeviceConnected(const std::string_view& identifier, const std::string_view& device_name)
|
||||
{
|
||||
emit g_emu_thread->onInputDeviceConnected(
|
||||
identifier.empty() ? QString() : QString::fromUtf8(identifier.data(), identifier.size()),
|
||||
emit g_emu_thread->onInputDeviceConnected(identifier.empty() ? QString() : QString::fromUtf8(identifier.data(), identifier.size()),
|
||||
device_name.empty() ? QString() : QString::fromUtf8(device_name.data(), device_name.size()));
|
||||
}
|
||||
|
||||
void Host::OnInputDeviceDisconnected(const std::string_view& identifier)
|
||||
{
|
||||
emit g_emu_thread->onInputDeviceDisconnected(
|
||||
identifier.empty() ? QString() : QString::fromUtf8(identifier.data(), identifier.size()));
|
||||
emit g_emu_thread->onInputDeviceDisconnected(identifier.empty() ? QString() : QString::fromUtf8(identifier.data(), identifier.size()));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
@ -1746,9 +1728,9 @@ bool QtHost::ParseCommandLineOptions(const QStringList& args, std::shared_ptr<VM
|
|||
// scanning the game list).
|
||||
if (s_batch_mode && !s_start_fullscreen_ui && !autoboot)
|
||||
{
|
||||
QMessageBox::critical(nullptr, QStringLiteral("Error"), s_nogui_mode ?
|
||||
QStringLiteral("Cannot use no-gui mode, because no boot filename was specified.") :
|
||||
QStringLiteral("Cannot use batch mode, because no boot filename was specified."));
|
||||
QMessageBox::critical(nullptr, QStringLiteral("Error"),
|
||||
s_nogui_mode ? QStringLiteral("Cannot use no-gui mode, because no boot filename was specified.") :
|
||||
QStringLiteral("Cannot use batch mode, because no boot filename was specified."));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -137,7 +137,7 @@ Q_SIGNALS:
|
|||
void onVMStopped();
|
||||
|
||||
/// Provided by the host; called when the running executable changes.
|
||||
void onGameChanged(const QString& path, const QString& serial, const QString& name, quint32 crc);
|
||||
void onGameChanged(const QString& path, const QString& elf_override, const QString& serial, const QString& name, quint32 crc);
|
||||
|
||||
void onInputDevicesEnumerated(const QList<QPair<QString, QString>>& devices);
|
||||
void onInputDeviceConnected(const QString& identifier, const QString& device_name);
|
||||
|
|
|
@ -22,7 +22,14 @@
|
|||
|
||||
#include "GameSummaryWidget.h"
|
||||
#include "SettingsDialog.h"
|
||||
#include "MainWindow.h"
|
||||
#include "QtHost.h"
|
||||
#include "QtUtils.h"
|
||||
|
||||
#include <QtCore/QDir>
|
||||
#include <QtWidgets/QFileDialog>
|
||||
|
||||
#include "fmt/format.h"
|
||||
|
||||
GameSummaryWidget::GameSummaryWidget(const GameList::Entry* entry, SettingsDialog* dialog, QWidget* parent)
|
||||
: m_dialog(dialog)
|
||||
|
@ -32,35 +39,40 @@ GameSummaryWidget::GameSummaryWidget(const GameList::Entry* entry, SettingsDialo
|
|||
const QString base_path(QtHost::GetResourcesBasePath());
|
||||
for (int i = 0; i < m_ui.region->count(); i++)
|
||||
{
|
||||
m_ui.region->setItemIcon(i, QIcon(
|
||||
QStringLiteral("%1/icons/flags/%2.png").arg(base_path).arg(GameList::RegionToString(static_cast<GameList::Region>(i)))));
|
||||
m_ui.region->setItemIcon(i,
|
||||
QIcon(QStringLiteral("%1/icons/flags/%2.png").arg(base_path).arg(GameList::RegionToString(static_cast<GameList::Region>(i)))));
|
||||
}
|
||||
for (int i = 1; i < m_ui.compatibility->count(); i++)
|
||||
{
|
||||
m_ui.compatibility->setItemIcon(i, QIcon(
|
||||
QStringLiteral("%1/icons/star-%2.png").arg(base_path).arg(i)));
|
||||
m_ui.compatibility->setItemIcon(i, QIcon(QStringLiteral("%1/icons/star-%2.png").arg(base_path).arg(i)));
|
||||
}
|
||||
|
||||
populateUi(entry);
|
||||
m_entry_path = entry->path;
|
||||
populateInputProfiles();
|
||||
populateDetails(entry);
|
||||
populateDiscPath(entry);
|
||||
|
||||
connect(m_ui.inputProfile, &QComboBox::currentIndexChanged, this, &GameSummaryWidget::onInputProfileChanged);
|
||||
}
|
||||
|
||||
GameSummaryWidget::~GameSummaryWidget() = default;
|
||||
|
||||
void GameSummaryWidget::populateUi(const GameList::Entry* entry)
|
||||
void GameSummaryWidget::populateInputProfiles()
|
||||
{
|
||||
for (const std::string& name : PAD::GetInputProfileNames())
|
||||
m_ui.inputProfile->addItem(QString::fromStdString(name));
|
||||
}
|
||||
|
||||
void GameSummaryWidget::populateDetails(const GameList::Entry* entry)
|
||||
{
|
||||
m_ui.title->setText(QString::fromStdString(entry->title));
|
||||
m_ui.path->setText(QString::fromStdString(entry->path));
|
||||
m_ui.serial->setText(QString::fromStdString(entry->serial));
|
||||
m_ui.crc->setText(QString::fromStdString(StringUtil::StdStringFromFormat("%08X", entry->crc)));
|
||||
m_ui.crc->setText(QString::fromStdString(fmt::format("{:08X}", entry->crc)));
|
||||
m_ui.type->setCurrentIndex(static_cast<int>(entry->type));
|
||||
m_ui.region->setCurrentIndex(static_cast<int>(entry->region));
|
||||
m_ui.compatibility->setCurrentIndex(static_cast<int>(entry->compatibility_rating));
|
||||
|
||||
for (const std::string& name : PAD::GetInputProfileNames())
|
||||
m_ui.inputProfile->addItem(QString::fromStdString(name));
|
||||
|
||||
std::optional<std::string> profile(m_dialog->getStringValue("EmuCore", "InputProfileName", std::nullopt));
|
||||
if (profile.has_value())
|
||||
m_ui.inputProfile->setCurrentIndex(m_ui.inputProfile->findText(QString::fromStdString(profile.value())));
|
||||
|
@ -68,6 +80,28 @@ void GameSummaryWidget::populateUi(const GameList::Entry* entry)
|
|||
m_ui.inputProfile->setCurrentIndex(0);
|
||||
}
|
||||
|
||||
void GameSummaryWidget::populateDiscPath(const GameList::Entry* entry)
|
||||
{
|
||||
if (entry->type == GameList::EntryType::ELF)
|
||||
{
|
||||
std::optional<std::string> iso_path(m_dialog->getStringValue("EmuCore", "DiscPath", std::nullopt));
|
||||
if (iso_path.has_value() && !iso_path->empty())
|
||||
m_ui.discPath->setText(QString::fromStdString(iso_path.value()));
|
||||
|
||||
connect(m_ui.discPath, &QLineEdit::textChanged, this, &GameSummaryWidget::onDiscPathChanged);
|
||||
connect(m_ui.discPathBrowse, &QPushButton::clicked, this, &GameSummaryWidget::onDiscPathBrowseClicked);
|
||||
connect(m_ui.discPathClear, &QPushButton::clicked, m_ui.discPath, &QLineEdit::clear);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Makes no sense to have disc override for a disc.
|
||||
m_ui.detailsFormLayout->removeRow(8);
|
||||
m_ui.discPath = nullptr;
|
||||
m_ui.discPathBrowse = nullptr;
|
||||
m_ui.discPathClear = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void GameSummaryWidget::onInputProfileChanged(int index)
|
||||
{
|
||||
if (index == 0)
|
||||
|
@ -75,3 +109,31 @@ void GameSummaryWidget::onInputProfileChanged(int index)
|
|||
else
|
||||
m_dialog->setStringSettingValue("EmuCore", "InputProfileName", m_ui.inputProfile->itemText(index).toUtf8());
|
||||
}
|
||||
|
||||
void GameSummaryWidget::onDiscPathChanged(const QString& value)
|
||||
{
|
||||
if (value.isEmpty())
|
||||
m_dialog->removeSettingValue("EmuCore", "DiscPath");
|
||||
else
|
||||
m_dialog->setStringSettingValue("EmuCore", "DiscPath", value.toStdString().c_str());
|
||||
|
||||
// force rescan of elf to update the serial
|
||||
g_main_window->rescanFile(m_entry_path);
|
||||
|
||||
// and re-fill our details (mainly the serial)
|
||||
auto lock = GameList::GetLock();
|
||||
const GameList::Entry* entry = GameList::GetEntryForPath(m_entry_path.c_str());
|
||||
if (entry)
|
||||
populateDetails(entry);
|
||||
}
|
||||
|
||||
void GameSummaryWidget::onDiscPathBrowseClicked()
|
||||
{
|
||||
const QString filename(QFileDialog::getOpenFileName(
|
||||
QtUtils::GetRootWidget(this), tr("Select Disc Path"), QString(), qApp->translate("MainWindow", MainWindow::DISC_IMAGE_FILTER)));
|
||||
if (filename.isEmpty())
|
||||
return;
|
||||
|
||||
// let the signal take care of it
|
||||
m_ui.discPath->setText(QDir::toNativeSeparators(filename));
|
||||
}
|
||||
|
|
|
@ -35,10 +35,15 @@ public:
|
|||
~GameSummaryWidget();
|
||||
|
||||
private:
|
||||
void populateUi(const GameList::Entry* entry);
|
||||
void populateInputProfiles();
|
||||
void populateDetails(const GameList::Entry* entry);
|
||||
void populateDiscPath(const GameList::Entry* entry);
|
||||
|
||||
void onInputProfileChanged(int index);
|
||||
void onDiscPathChanged(const QString& value);
|
||||
void onDiscPathBrowseClicked();
|
||||
|
||||
Ui::GameSummaryWidget m_ui;
|
||||
SettingsDialog* m_dialog;
|
||||
std::string m_entry_path;
|
||||
};
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<height>562</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<layout class="QFormLayout" name="detailsFormLayout">
|
||||
<property name="fieldGrowthPolicy">
|
||||
<enum>QFormLayout::ExpandingFieldsGrow</enum>
|
||||
</property>
|
||||
|
@ -353,18 +353,12 @@
|
|||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="1">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Input Profile:</string>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<widget class="QComboBox" name="inputProfile">
|
||||
|
@ -381,13 +375,47 @@
|
|||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<item row="8" column="0">
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="text">
|
||||
<string>Input Profile:</string>
|
||||
<string>Disc Path:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="discPath"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="discPathBrowse">
|
||||
<property name="text">
|
||||
<string>Browse...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="discPathClear">
|
||||
<property name="text">
|
||||
<string>Clear</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="9" column="1">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources>
|
||||
|
|
|
@ -226,7 +226,6 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
|
|||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.hwAutoFlush, "EmuCore/GS", "UserHacks_AutoFlush", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.frameBufferConversion, "EmuCore/GS", "UserHacks_CPU_FB_Conversion", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableDepthEmulation, "EmuCore/GS", "UserHacks_DisableDepthSupport", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.memoryWrapping, "EmuCore/GS", "wrap_gs_mem", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableSafeFeatures, "EmuCore/GS", "UserHacks_Disable_Safe_Features", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.preloadFrameData, "EmuCore/GS", "preload_frame_with_gs_data", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disablePartialInvalidation, "EmuCore/GS", "UserHacks_DisablePartialInvalidation", false);
|
||||
|
@ -481,11 +480,6 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
|
|||
tr("Convert 4-bit and 8-bit frame buffer on the CPU instead of the GPU. "
|
||||
"Helps Harry Potter and Stuntman games. It has a big impact on performance."));
|
||||
|
||||
dialog->registerWidgetHelp(m_ui.memoryWrapping, tr("Memory Wrapping"), tr("Unchecked"),
|
||||
tr("Emulates GS memory wrapping accurately. "
|
||||
"This fixes issues where part of the image is cut-off by block shaped sections such as the FMVs in "
|
||||
"Wallace & Gromit: The Curse of the Were-Rabbit and Thrillville."));
|
||||
|
||||
dialog->registerWidgetHelp(m_ui.preloadFrameData, tr("Preload Frame Data"), tr("Unchecked"),
|
||||
tr("Uploads GS data when rendering a new frame to reproduce some effects accurately. "
|
||||
"Fixes black screen issues in games like Armored Core: Last Raven."));
|
||||
|
|
|
@ -968,13 +968,6 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QCheckBox" name="memoryWrapping">
|
||||
<property name="text">
|
||||
<string>Memory Wrapping</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="disableSafeFeatures">
|
||||
<property name="text">
|
||||
|
@ -982,13 +975,6 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QCheckBox" name="preloadFrameData">
|
||||
<property name="text">
|
||||
<string>Preload Frame Data</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QCheckBox" name="disablePartialInvalidation">
|
||||
<property name="text">
|
||||
|
@ -996,7 +982,14 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<item row="1" column="1">
|
||||
<widget class="QCheckBox" name="preloadFrameData">
|
||||
<property name="text">
|
||||
<string>Preload Frame Data</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QCheckBox" name="textureInsideRt">
|
||||
<property name="text">
|
||||
<string>Texture Inside RT</string>
|
||||
|
|
|
@ -476,6 +476,22 @@ void SettingsDialog::setStringSettingValue(const char* section, const char* key,
|
|||
}
|
||||
}
|
||||
|
||||
void SettingsDialog::removeSettingValue(const char* section, const char* key)
|
||||
{
|
||||
if (m_sif)
|
||||
{
|
||||
m_sif->DeleteValue(section, key);
|
||||
m_sif->Save();
|
||||
g_emu_thread->reloadGameSettings();
|
||||
}
|
||||
else
|
||||
{
|
||||
Host::RemoveBaseSettingValue(section, key);
|
||||
Host::CommitBaseSettingChanges();
|
||||
g_emu_thread->applySettings();
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsDialog::openGamePropertiesDialog(const GameList::Entry* game, const std::string_view& serial, u32 crc)
|
||||
{
|
||||
// check for an existing dialog with this crc
|
||||
|
|
|
@ -92,6 +92,7 @@ public:
|
|||
void setIntSettingValue(const char* section, const char* key, std::optional<int> value);
|
||||
void setFloatSettingValue(const char* section, const char* key, std::optional<float> value);
|
||||
void setStringSettingValue(const char* section, const char* key, std::optional<const char*> value);
|
||||
void removeSettingValue(const char* section, const char* key);
|
||||
|
||||
Q_SIGNALS:
|
||||
void settingsResetToDefaults();
|
||||
|
|
|
@ -263,12 +263,27 @@ static void cdvdCreateNewNVM(std::FILE* fp)
|
|||
|
||||
// Write NVM ILink area with dummy data (Age of Empires 2)
|
||||
// Also write language data defaulting to English (Guitar Hero 2)
|
||||
// Also write PStwo region defaults
|
||||
|
||||
NVMLayout* nvmLayout = getNvmLayout();
|
||||
u8 ILinkID_Data[8] = {0x00, 0xAC, 0xFF, 0xFF, 0xFF, 0xFF, 0xB9, 0x86};
|
||||
|
||||
if (((BiosVersion >> 8) == 2) && ((BiosVersion & 0xff) != 10)) // bios >= 200, except of 0x210 for PSX2 DESR
|
||||
{
|
||||
u8 RegParams[12];
|
||||
memcpy(RegParams, &PStwoRegionDefaults[BiosRegion][0], 12);
|
||||
std::fseek(fp, *(s32*)(((u8*)nvmLayout) + offsetof(NVMLayout, regparams)), SEEK_SET);
|
||||
std::fwrite(RegParams, sizeof(RegParams), 1, fp);
|
||||
}
|
||||
|
||||
u8 ILinkID_Data[8] = {0x00, 0xAC, 0xFF, 0xFF, 0xFF, 0xFF, 0xB9, 0x86};
|
||||
std::fseek(fp, *(s32*)(((u8*)nvmLayout) + offsetof(NVMLayout, ilinkId)), SEEK_SET);
|
||||
std::fwrite(ILinkID_Data, sizeof(ILinkID_Data), 1, fp);
|
||||
if (nvmlayouts[1].biosVer <= BiosVersion)
|
||||
{
|
||||
u8 ILinkID_checksum[2] = {0x00, 0x18};
|
||||
std::fseek(fp, *(s32*)(((u8*)nvmLayout) + offsetof(NVMLayout, ilinkId)) + 0x08, SEEK_SET);
|
||||
std::fwrite(ILinkID_checksum, sizeof(ILinkID_checksum), 1, fp);
|
||||
}
|
||||
|
||||
u8 biosLanguage[16];
|
||||
memcpy(biosLanguage, &biosLangDefaults[BiosRegion][0], 16);
|
||||
|
@ -318,12 +333,12 @@ static void cdvdNVM(u8* buffer, int offset, size_t bytes, bool read)
|
|||
if (std::fseek(fp.get(), *(s32*)(((u8*)nvmLayout) + offsetof(NVMLayout, config1)) + 0x10, SEEK_SET) != 0 ||
|
||||
std::fread(LanguageParams, 16, 1, fp.get()) != 1 ||
|
||||
std::memcmp(LanguageParams, zero, sizeof(LanguageParams)) == 0 ||
|
||||
(BiosVersion >= 0x200 &&
|
||||
(((BiosVersion >> 8) == 2) && ((BiosVersion & 0xff) != 10) &&
|
||||
(std::fseek(fp.get(), *(s32*)(((u8*)nvmLayout) + offsetof(NVMLayout, regparams)), SEEK_SET) != 0 ||
|
||||
std::fread(RegParams, 12, 1, fp.get()) != 1 ||
|
||||
std::memcmp(RegParams, zero, sizeof(RegParams)) == 0)))
|
||||
{
|
||||
Console.Warning("Language Parameters missing, filling in defaults");
|
||||
Console.Warning("Language or Region Parameters missing, filling in defaults");
|
||||
|
||||
FileSystem::FSeek64(fp.get(), 0, SEEK_SET);
|
||||
cdvdCreateNewNVM(fp.get());
|
||||
|
@ -565,6 +580,25 @@ static __fi void _reloadElfInfo(std::string elfpath)
|
|||
// binary).
|
||||
}
|
||||
|
||||
u32 cdvdGetElfCRC(const std::string& path)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Yay for write-after-read here. Isn't our ELF parser great....
|
||||
const s64 host_size = FileSystem::GetPathFileSize(path.c_str());
|
||||
if (host_size <= 0)
|
||||
return 0;
|
||||
|
||||
std::unique_ptr<ElfObject> elfptr(std::make_unique<ElfObject>(path, static_cast<u32>(std::max<s64>(host_size, 0)), false));
|
||||
elfptr->loadHeaders();
|
||||
return elfptr->getCRC();
|
||||
}
|
||||
catch ([[maybe_unused]] Exception::FileNotFound& e)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static std::string ExecutablePathToSerial(const std::string& path)
|
||||
{
|
||||
// cdrom:\SCES_123.45;1
|
||||
|
@ -622,16 +656,17 @@ void cdvdReloadElfInfo(std::string elfoverride)
|
|||
DevCon.WriteLn(Color_Green, "Reload ELF");
|
||||
try
|
||||
{
|
||||
std::string elfpath;
|
||||
u32 discType = GetPS2ElfName(elfpath);
|
||||
DiscSerial = ExecutablePathToSerial(elfpath);
|
||||
|
||||
// Use the serial from the disc (if any), and the ELF CRC of the override.
|
||||
if (!elfoverride.empty())
|
||||
{
|
||||
_reloadElfInfo(std::move(elfoverride));
|
||||
return;
|
||||
}
|
||||
|
||||
std::string elfpath;
|
||||
u32 discType = GetPS2ElfName(elfpath);
|
||||
DiscSerial = ExecutablePathToSerial(elfpath);
|
||||
|
||||
if (discType == 1)
|
||||
{
|
||||
// PCSX2 currently only recognizes *.elf executables in proper PS2 format.
|
||||
|
|
|
@ -304,6 +304,7 @@ extern u8 cdvdRead(u8 key);
|
|||
extern void cdvdWrite(u8 key, u8 rt);
|
||||
|
||||
extern void cdvdReloadElfInfo(std::string elfoverride = std::string());
|
||||
extern u32 cdvdGetElfCRC(const std::string& path);
|
||||
extern s32 cdvdCtrlTrayOpen();
|
||||
extern s32 cdvdCtrlTrayClose();
|
||||
|
||||
|
|
|
@ -277,6 +277,23 @@ static NVMLayout nvmlayouts[NVM_FORMAT_MAX] =
|
|||
{0x146, 0x270, 0x2B0, 0x200, 0x1C8, 0x1E0, 0x1B0, 0x180, 0x198}, // eeproms from bios v1.70 and up
|
||||
};
|
||||
|
||||
static u8 PStwoRegionDefaults[13][12] =
|
||||
{
|
||||
{0x4a, 0x4a, 0x6a, 0x70, 0x6e, 0x4a, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00}, // JJjpnJJ - Japan
|
||||
{0x41, 0x41, 0x65, 0x6e, 0x67, 0x41, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00}, // AAengAU - USA
|
||||
{0x45, 0x45, 0x65, 0x6e, 0x67, 0x45, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00}, // EEengEE - Europe
|
||||
{0x45, 0x45, 0x65, 0x6e, 0x67, 0x45, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00}, // EEengEO - Oceania
|
||||
{0x48, 0x48, 0x65, 0x6e, 0x67, 0x4a, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00}, // HHengJA - Asia
|
||||
{0x45, 0x52, 0x65, 0x6e, 0x67, 0x45, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00}, // ERengER - Russia
|
||||
{0x43, 0x43, 0x73, 0x63, 0x68, 0x4A, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00}, // CCschJC - China
|
||||
{0x41, 0x41, 0x73, 0x70, 0x61, 0x41, 0x4D, 0x00, 0x00, 0x00, 0x00, 0x00}, // AAspaAM - Mexico
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // T10K (does not exist)
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // Test (does not exist)
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // Free (does not exist)
|
||||
{0x48, 0x4b, 0x6b, 0x6f, 0x72, 0x4a, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00}, // HKkorJA - Korea
|
||||
{0x48, 0x48, 0x74, 0x63, 0x68, 0x4a, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00}, // HHtchJA - Taiwan
|
||||
};
|
||||
|
||||
static u8 biosLangDefaults[11][16] =
|
||||
{
|
||||
{0x20, 0x20, 0x80, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30}, // Japan (Japanese)
|
||||
|
@ -289,7 +306,7 @@ static u8 biosLangDefaults[11][16] =
|
|||
{0x30, 0x21, 0x80, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41}, // Mexico (English)
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // T10K (Japanese, generally gets overridden)
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // Test (Japanese, as above)
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // Free (Japanese, no examples to use)
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // Free (Japanese, no examples to use)
|
||||
};
|
||||
|
||||
static u8 MRenewalDate[16][6] =
|
||||
|
|
|
@ -881,7 +881,6 @@ if(USE_OPENGL)
|
|||
GS/Renderers/OpenGL/GLState.h
|
||||
GS/Renderers/OpenGL/GSDeviceOGL.h
|
||||
GS/Renderers/OpenGL/GSTextureOGL.h
|
||||
GS/Renderers/OpenGL/GSUniformBufferOGL.h
|
||||
)
|
||||
target_link_libraries(PCSX2_FLAGS INTERFACE glad)
|
||||
endif()
|
||||
|
@ -913,7 +912,6 @@ if(PCSX2_CORE)
|
|||
SPU2/Host/Config.cpp
|
||||
SPU2/Host/ConfigDebug.cpp
|
||||
SPU2/Host/ConfigSoundTouch.cpp
|
||||
SPU2/Host/Dialogs.cpp
|
||||
)
|
||||
list(APPEND pcsx2SPU2Headers
|
||||
SPU2/Host/Config.h
|
||||
|
|
|
@ -363,7 +363,8 @@ void CommonHost::OnVMResumed()
|
|||
UpdateInhibitScreensaver(EmuConfig.InhibitScreensaver);
|
||||
}
|
||||
|
||||
void CommonHost::OnGameChanged(const std::string& disc_path, const std::string& game_serial, const std::string& game_name, u32 game_crc)
|
||||
void CommonHost::OnGameChanged(const std::string& disc_path, const std::string& elf_override, const std::string& game_serial,
|
||||
const std::string& game_name, u32 game_crc)
|
||||
{
|
||||
UpdateSessionTime(game_serial);
|
||||
|
||||
|
|
|
@ -71,7 +71,8 @@ namespace CommonHost
|
|||
void OnVMResumed();
|
||||
|
||||
/// Called when the running executable changes.
|
||||
void OnGameChanged(const std::string& disc_path, const std::string& game_serial, const std::string& game_name, u32 game_crc);
|
||||
void OnGameChanged(const std::string& disc_path, const std::string& elf_override, const std::string& game_serial,
|
||||
const std::string& game_name, u32 game_crc);
|
||||
|
||||
/// Provided by the host; called once per frame at guest vsync.
|
||||
void CPUThreadVSync();
|
||||
|
|
|
@ -252,6 +252,7 @@ namespace FullscreenUI
|
|||
// Landing
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
static void SwitchToLanding();
|
||||
static ImGuiFullscreen::FileSelectorFilters GetOpenFileFilters();
|
||||
static ImGuiFullscreen::FileSelectorFilters GetDiscImageFilters();
|
||||
static void DoStartPath(
|
||||
const std::string& path, std::optional<s32> state_index = std::nullopt, std::optional<bool> fast_boot = std::nullopt);
|
||||
|
@ -877,11 +878,16 @@ void FullscreenUI::DestroyResources()
|
|||
// Utility
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ImGuiFullscreen::FileSelectorFilters FullscreenUI::GetDiscImageFilters()
|
||||
ImGuiFullscreen::FileSelectorFilters FullscreenUI::GetOpenFileFilters()
|
||||
{
|
||||
return {"*.bin", "*.iso", "*.cue", "*.chd", "*.cso", "*.gz", "*.elf", "*.irx", "*.gs", "*.gs.xz", "*.gs.zst", "*.dump"};
|
||||
}
|
||||
|
||||
ImGuiFullscreen::FileSelectorFilters FullscreenUI::GetDiscImageFilters()
|
||||
{
|
||||
return {"*.bin", "*.iso", "*.cue", "*.chd", "*.cso", "*.gz"};
|
||||
}
|
||||
|
||||
void FullscreenUI::DoStartPath(const std::string& path, std::optional<s32> state_index, std::optional<bool> fast_boot)
|
||||
{
|
||||
VMBootParameters params;
|
||||
|
@ -914,7 +920,7 @@ void FullscreenUI::DoStartFile()
|
|||
CloseFileSelector();
|
||||
};
|
||||
|
||||
OpenFileSelector(ICON_FA_FOLDER_OPEN " Select Disc Image", false, std::move(callback), GetDiscImageFilters());
|
||||
OpenFileSelector(ICON_FA_FOLDER_OPEN " Select Disc Image", false, std::move(callback), GetOpenFileFilters());
|
||||
}
|
||||
|
||||
void FullscreenUI::DoStartBIOS()
|
||||
|
@ -2129,16 +2135,10 @@ void FullscreenUI::SwitchToGameSettings()
|
|||
auto lock = GameList::GetLock();
|
||||
const GameList::Entry* entry = GameList::GetEntryForPath(s_current_game_path.c_str());
|
||||
if (!entry)
|
||||
{
|
||||
entry = GameList::GetEntryBySerialAndCRC(s_current_game_serial.c_str(), s_current_game_crc);
|
||||
if (!entry)
|
||||
{
|
||||
SwitchToGameSettings(s_current_game_serial.c_str(), s_current_game_crc);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SwitchToGameSettings(entry);
|
||||
if (entry)
|
||||
SwitchToGameSettings(entry);
|
||||
}
|
||||
|
||||
void FullscreenUI::SwitchToGameSettings(const std::string& path)
|
||||
|
@ -2151,7 +2151,7 @@ void FullscreenUI::SwitchToGameSettings(const std::string& path)
|
|||
|
||||
void FullscreenUI::SwitchToGameSettings(const GameList::Entry* entry)
|
||||
{
|
||||
SwitchToGameSettings(entry->serial.c_str(), entry->crc);
|
||||
SwitchToGameSettings((entry->type != GameList::EntryType::ELF) ? std::string_view(entry->serial) : std::string_view(), entry->crc);
|
||||
s_game_settings_entry = std::make_unique<GameList::Entry>(*entry);
|
||||
}
|
||||
|
||||
|
@ -2367,6 +2367,8 @@ void FullscreenUI::DrawSettingsWindow()
|
|||
|
||||
void FullscreenUI::DrawSummarySettingsPage()
|
||||
{
|
||||
SettingsInterface* bsi = GetEditingSettingsInterface();
|
||||
|
||||
BeginMenuButtons();
|
||||
|
||||
MenuHeading("Details");
|
||||
|
@ -2379,7 +2381,7 @@ void FullscreenUI::DrawSummarySettingsPage()
|
|||
CopyTextToClipboard("Game serial copied to clipboard.", s_game_settings_entry->serial);
|
||||
if (MenuButton(ICON_FA_CODE " CRC", fmt::format("{:08X}", s_game_settings_entry->crc).c_str(), true))
|
||||
CopyTextToClipboard("Game CRC copied to clipboard.", fmt::format("{:08X}", s_game_settings_entry->crc));
|
||||
if (MenuButton(ICON_FA_COMPACT_DISC " Type", GameList::EntryTypeToString(s_game_settings_entry->type), true))
|
||||
if (MenuButton(ICON_FA_LIST " Type", GameList::EntryTypeToString(s_game_settings_entry->type), true))
|
||||
CopyTextToClipboard("Game type copied to clipboard.", GameList::EntryTypeToString(s_game_settings_entry->type));
|
||||
if (MenuButton(ICON_FA_BOX " Region", GameList::RegionToString(s_game_settings_entry->region), true))
|
||||
CopyTextToClipboard("Game region copied to clipboard.", GameList::RegionToString(s_game_settings_entry->region));
|
||||
|
@ -2391,6 +2393,44 @@ void FullscreenUI::DrawSummarySettingsPage()
|
|||
}
|
||||
if (MenuButton(ICON_FA_FOLDER_OPEN " Path", s_game_settings_entry->path.c_str(), true))
|
||||
CopyTextToClipboard("Game path copied to clipboard.", s_game_settings_entry->path);
|
||||
|
||||
if (s_game_settings_entry->type == GameList::EntryType::ELF)
|
||||
{
|
||||
const std::string iso_path(bsi->GetStringValue("EmuCore", "DiscPath"));
|
||||
if (MenuButton(ICON_FA_COMPACT_DISC " Disc Path", iso_path.empty() ? "No Disc" : iso_path.c_str()))
|
||||
{
|
||||
auto callback = [](const std::string& path) {
|
||||
if (!path.empty())
|
||||
{
|
||||
{
|
||||
auto lock = Host::GetSettingsLock();
|
||||
if (s_game_settings_interface)
|
||||
{
|
||||
s_game_settings_interface->SetStringValue("EmuCore", "DiscPath", path.c_str());
|
||||
s_game_settings_interface->Save();
|
||||
}
|
||||
}
|
||||
|
||||
if (s_game_settings_entry)
|
||||
{
|
||||
// re-scan the entry to update its serial.
|
||||
if (GameList::RescanPath(s_game_settings_entry->path))
|
||||
{
|
||||
auto lock = GameList::GetLock();
|
||||
const GameList::Entry* entry = GameList::GetEntryForPath(s_game_settings_entry->path.c_str());
|
||||
if (entry)
|
||||
*s_game_settings_entry = *entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QueueResetFocus();
|
||||
CloseFileSelector();
|
||||
};
|
||||
|
||||
OpenFileSelector(ICON_FA_COMPACT_DISC " Select Disc Path", false, std::move(callback), GetDiscImageFilters());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -2938,8 +2978,6 @@ void FullscreenUI::DrawGraphicsSettingsPage()
|
|||
"EmuCore/GS", "UserHacks_CPU_FB_Conversion", false, manual_hw_fixes);
|
||||
DrawToggleSetting(bsi, "Disable Depth Support", "Disable the support of depth buffer in the texture cache.", "EmuCore/GS",
|
||||
"UserHacks_DisableDepthSupport", false, manual_hw_fixes);
|
||||
DrawToggleSetting(
|
||||
bsi, "Wrap GS Memory", "Emulates GS memory wrapping accurately.", "EmuCore/GS", "wrap_gs_mem", false, manual_hw_fixes);
|
||||
DrawToggleSetting(bsi, "Disable Safe Features", "This option disables multiple safe features.", "EmuCore/GS",
|
||||
"UserHacks_Disable_Safe_Features", false, manual_hw_fixes);
|
||||
DrawToggleSetting(bsi, "Preload Frame", "Uploads GS data when rendering a new frame to reproduce some effects accurately.",
|
||||
|
|
|
@ -71,6 +71,8 @@ namespace GameList
|
|||
|
||||
static Entry* GetMutableEntryForPath(const char* path);
|
||||
|
||||
static bool GetIsoSerialAndCRC(const std::string& path, s32* disc_type, std::string* serial, u32* crc);
|
||||
static Region ParseDatabaseRegion(const std::string_view& db_region);
|
||||
static bool GetElfListEntry(const std::string& path, GameList::Entry* entry);
|
||||
static bool GetIsoListEntry(const std::string& path, GameList::Entry* entry);
|
||||
static bool GetPython2ListEntry(const std::string& path, GameList::Entry* entry);
|
||||
|
@ -88,6 +90,7 @@ namespace GameList
|
|||
static bool WriteEntryToCache(const GameList::Entry* entry);
|
||||
static void CloseCacheFileStream();
|
||||
static void DeleteCacheFile();
|
||||
static void RewriteCacheFile();
|
||||
|
||||
static std::string GetPlayedTimeFile();
|
||||
static bool ParsePlayedTimeLine(char* line, std::string& serial, PlayedTimeEntry& entry);
|
||||
|
@ -160,8 +163,8 @@ void GameList::FillBootParametersForEntry(VMBootParameters* params, const Entry*
|
|||
}
|
||||
else if (entry->type == GameList::EntryType::ELF)
|
||||
{
|
||||
params->filename.clear();
|
||||
params->source_type = CDVD_SourceType::NoDisc;
|
||||
params->filename = VMManager::GetDiscOverrideFromGameSettings(entry->path);
|
||||
params->source_type = params->filename.empty() ? CDVD_SourceType::NoDisc : CDVD_SourceType::Iso;
|
||||
params->elf_override = entry->path;
|
||||
}
|
||||
else if (entry->type == GameList::EntryType::Python2)
|
||||
|
@ -200,6 +203,29 @@ void GameList::FillBootParametersForEntry(VMBootParameters* params, const Entry*
|
|||
}
|
||||
}
|
||||
|
||||
bool GameList::GetIsoSerialAndCRC(const std::string& path, s32* disc_type, std::string* serial, u32* crc)
|
||||
{
|
||||
// This isn't great, we really want to make it all thread-local...
|
||||
CDVD = &CDVDapi_Iso;
|
||||
if (CDVD->open(path.c_str()) != 0)
|
||||
return false;
|
||||
|
||||
*disc_type = DoCDVDdetectDiskType();
|
||||
cdvdReloadElfInfo();
|
||||
|
||||
*serial = DiscSerial;
|
||||
*crc = ElfCRC;
|
||||
|
||||
DoCDVDclose();
|
||||
|
||||
// TODO(Stenzek): These globals are **awful**. Clean it up.
|
||||
DiscSerial.clear();
|
||||
ElfCRC = 0;
|
||||
ElfEntry = -1;
|
||||
LastELF.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GameList::GetElfListEntry(const std::string& path, GameList::Entry* entry)
|
||||
{
|
||||
const s64 file_size = FileSystem::GetPathFileSize(path.c_str());
|
||||
|
@ -217,17 +243,101 @@ bool GameList::GetElfListEntry(const std::string& path, GameList::Entry* entry)
|
|||
return false;
|
||||
}
|
||||
|
||||
const std::string display_name(FileSystem::GetDisplayNameFromPath(path));
|
||||
entry->path = path;
|
||||
entry->serial.clear();
|
||||
entry->title = Path::GetFileTitle(display_name);
|
||||
entry->title = Path::GetFileTitle(FileSystem::GetDisplayNameFromPath(path));
|
||||
entry->region = Region::Other;
|
||||
entry->total_size = static_cast<u64>(file_size);
|
||||
entry->type = EntryType::ELF;
|
||||
entry->compatibility_rating = CompatibilityRating::Unknown;
|
||||
|
||||
std::string disc_path(VMManager::GetDiscOverrideFromGameSettings(path));
|
||||
if (!disc_path.empty())
|
||||
{
|
||||
s32 disc_type;
|
||||
u32 disc_crc;
|
||||
if (GetIsoSerialAndCRC(disc_path, &disc_type, &entry->serial, &disc_crc))
|
||||
{
|
||||
// use serial/region/compat info from the db
|
||||
if (const GameDatabaseSchema::GameEntry* db_entry = GameDatabase::findGame(entry->serial))
|
||||
{
|
||||
entry->compatibility_rating = db_entry->compat;
|
||||
entry->region = ParseDatabaseRegion(db_entry->region);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
// clang-format off
|
||||
|
||||
GameList::Region GameList::ParseDatabaseRegion(const std::string_view& db_region)
|
||||
{
|
||||
// clang-format off
|
||||
////// NTSC //////
|
||||
//////////////////
|
||||
if (StringUtil::StartsWith(db_region, "NTSC-B"))
|
||||
return Region::NTSC_B;
|
||||
else if (StringUtil::StartsWith(db_region, "NTSC-C"))
|
||||
return Region::NTSC_C;
|
||||
else if (StringUtil::StartsWith(db_region, "NTSC-HK"))
|
||||
return Region::NTSC_HK;
|
||||
else if (StringUtil::StartsWith(db_region, "NTSC-J"))
|
||||
return Region::NTSC_J;
|
||||
else if (StringUtil::StartsWith(db_region, "NTSC-K"))
|
||||
return Region::NTSC_K;
|
||||
else if (StringUtil::StartsWith(db_region, "NTSC-T"))
|
||||
return Region::NTSC_T;
|
||||
else if (StringUtil::StartsWith(db_region, "NTSC-U"))
|
||||
return Region::NTSC_U;
|
||||
////// PAL //////
|
||||
//////////////////
|
||||
else if (StringUtil::StartsWith(db_region, "PAL-AF"))
|
||||
return Region::PAL_AF;
|
||||
else if (StringUtil::StartsWith(db_region, "PAL-AU"))
|
||||
return Region::PAL_AU;
|
||||
else if (StringUtil::StartsWith(db_region, "PAL-A"))
|
||||
return Region::PAL_A;
|
||||
else if (StringUtil::StartsWith(db_region, "PAL-BE"))
|
||||
return Region::PAL_BE;
|
||||
else if (StringUtil::StartsWith(db_region, "PAL-E"))
|
||||
return Region::PAL_E;
|
||||
else if (StringUtil::StartsWith(db_region, "PAL-FI"))
|
||||
return Region::PAL_FI;
|
||||
else if (StringUtil::StartsWith(db_region, "PAL-F"))
|
||||
return Region::PAL_F;
|
||||
else if (StringUtil::StartsWith(db_region, "PAL-GR"))
|
||||
return Region::PAL_GR;
|
||||
else if (StringUtil::StartsWith(db_region, "PAL-G"))
|
||||
return Region::PAL_G;
|
||||
else if (StringUtil::StartsWith(db_region, "PAL-IN"))
|
||||
return Region::PAL_IN;
|
||||
else if (StringUtil::StartsWith(db_region, "PAL-I"))
|
||||
return Region::PAL_I;
|
||||
else if (StringUtil::StartsWith(db_region, "PAL-M"))
|
||||
return Region::PAL_M;
|
||||
else if (StringUtil::StartsWith(db_region, "PAL-NL"))
|
||||
return Region::PAL_NL;
|
||||
else if (StringUtil::StartsWith(db_region, "PAL-NO"))
|
||||
return Region::PAL_NO;
|
||||
else if (StringUtil::StartsWith(db_region, "PAL-P"))
|
||||
return Region::PAL_P;
|
||||
else if (StringUtil::StartsWith(db_region, "PAL-R"))
|
||||
return Region::PAL_R;
|
||||
else if (StringUtil::StartsWith(db_region, "PAL-SC"))
|
||||
return Region::PAL_SC;
|
||||
else if (StringUtil::StartsWith(db_region, "PAL-SWI"))
|
||||
return Region::PAL_SWI;
|
||||
else if (StringUtil::StartsWith(db_region, "PAL-SW"))
|
||||
return Region::PAL_SW;
|
||||
else if (StringUtil::StartsWith(db_region, "PAL-S"))
|
||||
return Region::PAL_S;
|
||||
else if (StringUtil::StartsWith(db_region, "PAL-UK"))
|
||||
return Region::PAL_UK;
|
||||
else
|
||||
return Region::Other;
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
bool GameList::GetIsoListEntry(const std::string& path, GameList::Entry* entry)
|
||||
{
|
||||
FILESYSTEM_STAT_DATA sd;
|
||||
|
@ -239,8 +349,11 @@ bool GameList::GetIsoListEntry(const std::string& path, GameList::Entry* entry)
|
|||
if (CDVD->open(path.c_str()) != 0)
|
||||
return false;
|
||||
|
||||
const s32 type = DoCDVDdetectDiskType();
|
||||
switch (type)
|
||||
s32 disc_type;
|
||||
if (!GetIsoSerialAndCRC(path, &disc_type, &entry->serial, &entry->crc))
|
||||
return false;
|
||||
|
||||
switch (disc_type)
|
||||
{
|
||||
case CDVD_TYPE_PSCD:
|
||||
case CDVD_TYPE_PSCDDA:
|
||||
|
@ -255,92 +368,18 @@ bool GameList::GetIsoListEntry(const std::string& path, GameList::Entry* entry)
|
|||
|
||||
case CDVD_TYPE_ILLEGAL:
|
||||
default:
|
||||
DoCDVDclose();
|
||||
return false;
|
||||
}
|
||||
|
||||
cdvdReloadElfInfo();
|
||||
|
||||
entry->path = path;
|
||||
entry->serial = DiscSerial;
|
||||
entry->crc = ElfCRC;
|
||||
entry->total_size = sd.Size;
|
||||
entry->compatibility_rating = CompatibilityRating::Unknown;
|
||||
|
||||
DoCDVDclose();
|
||||
|
||||
// TODO(Stenzek): These globals are **awful**. Clean it up.
|
||||
DiscSerial.clear();
|
||||
ElfCRC = 0;
|
||||
ElfEntry = -1;
|
||||
LastELF.clear();
|
||||
|
||||
if (const GameDatabaseSchema::GameEntry* db_entry = GameDatabase::findGame(entry->serial))
|
||||
{
|
||||
entry->title = std::move(db_entry->name);
|
||||
entry->compatibility_rating = db_entry->compat;
|
||||
////// NTSC //////
|
||||
//////////////////
|
||||
if (StringUtil::StartsWith(db_entry->region, "NTSC-B"))
|
||||
entry->region = Region::NTSC_B;
|
||||
else if (StringUtil::StartsWith(db_entry->region, "NTSC-C"))
|
||||
entry->region = Region::NTSC_C;
|
||||
else if (StringUtil::StartsWith(db_entry->region, "NTSC-HK"))
|
||||
entry->region = Region::NTSC_HK;
|
||||
else if (StringUtil::StartsWith(db_entry->region, "NTSC-J"))
|
||||
entry->region = Region::NTSC_J;
|
||||
else if (StringUtil::StartsWith(db_entry->region, "NTSC-K"))
|
||||
entry->region = Region::NTSC_K;
|
||||
else if (StringUtil::StartsWith(db_entry->region, "NTSC-T"))
|
||||
entry->region = Region::NTSC_T;
|
||||
else if (StringUtil::StartsWith(db_entry->region, "NTSC-U"))
|
||||
entry->region = Region::NTSC_U;
|
||||
////// PAL //////
|
||||
//////////////////
|
||||
else if (StringUtil::StartsWith(db_entry->region, "PAL-AF"))
|
||||
entry->region = Region::PAL_AF;
|
||||
else if (StringUtil::StartsWith(db_entry->region, "PAL-AU"))
|
||||
entry->region = Region::PAL_AU;
|
||||
else if (StringUtil::StartsWith(db_entry->region, "PAL-A"))
|
||||
entry->region = Region::PAL_A;
|
||||
else if (StringUtil::StartsWith(db_entry->region, "PAL-BE"))
|
||||
entry->region = Region::PAL_BE;
|
||||
else if (StringUtil::StartsWith(db_entry->region, "PAL-E"))
|
||||
entry->region = Region::PAL_E;
|
||||
else if (StringUtil::StartsWith(db_entry->region, "PAL-FI"))
|
||||
entry->region = Region::PAL_FI;
|
||||
else if (StringUtil::StartsWith(db_entry->region, "PAL-F"))
|
||||
entry->region = Region::PAL_F;
|
||||
else if (StringUtil::StartsWith(db_entry->region, "PAL-GR"))
|
||||
entry->region = Region::PAL_GR;
|
||||
else if (StringUtil::StartsWith(db_entry->region, "PAL-G"))
|
||||
entry->region = Region::PAL_G;
|
||||
else if (StringUtil::StartsWith(db_entry->region, "PAL-IN"))
|
||||
entry->region = Region::PAL_IN;
|
||||
else if (StringUtil::StartsWith(db_entry->region, "PAL-I"))
|
||||
entry->region = Region::PAL_I;
|
||||
else if (StringUtil::StartsWith(db_entry->region, "PAL-M"))
|
||||
entry->region = Region::PAL_M;
|
||||
else if (StringUtil::StartsWith(db_entry->region, "PAL-NL"))
|
||||
entry->region = Region::PAL_NL;
|
||||
else if (StringUtil::StartsWith(db_entry->region, "PAL-NO"))
|
||||
entry->region = Region::PAL_NO;
|
||||
else if (StringUtil::StartsWith(db_entry->region, "PAL-P"))
|
||||
entry->region = Region::PAL_P;
|
||||
else if (StringUtil::StartsWith(db_entry->region, "PAL-R"))
|
||||
entry->region = Region::PAL_R;
|
||||
else if (StringUtil::StartsWith(db_entry->region, "PAL-SC"))
|
||||
entry->region = Region::PAL_SC;
|
||||
else if (StringUtil::StartsWith(db_entry->region, "PAL-SWI"))
|
||||
entry->region = Region::PAL_SWI;
|
||||
else if (StringUtil::StartsWith(db_entry->region, "PAL-SW"))
|
||||
entry->region = Region::PAL_SW;
|
||||
else if (StringUtil::StartsWith(db_entry->region, "PAL-S"))
|
||||
entry->region = Region::PAL_S;
|
||||
else if (StringUtil::StartsWith(db_entry->region, "PAL-UK"))
|
||||
entry->region = Region::PAL_UK;
|
||||
else
|
||||
entry->region = Region::Other;
|
||||
entry->region = ParseDatabaseRegion(db_entry->region);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -350,6 +389,7 @@ bool GameList::GetIsoListEntry(const std::string& path, GameList::Entry* entry)
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
bool GameList::GetPython2ListEntry(const std::string& path, GameList::Entry* entry)
|
||||
{
|
||||
|
@ -391,69 +431,7 @@ bool GameList::GetPython2ListEntry(const std::string& path, GameList::Entry* ent
|
|||
entry->title = game_title;
|
||||
entry->type = EntryType::Python2;
|
||||
entry->compatibility_rating = CompatibilityRating::Unknown;
|
||||
|
||||
////// NTSC //////
|
||||
//////////////////
|
||||
if (StringUtil::StartsWith(region, "NTSC-B"))
|
||||
entry->region = Region::NTSC_B;
|
||||
else if (StringUtil::StartsWith(region, "NTSC-C"))
|
||||
entry->region = Region::NTSC_C;
|
||||
else if (StringUtil::StartsWith(region, "NTSC-HK"))
|
||||
entry->region = Region::NTSC_HK;
|
||||
else if (StringUtil::StartsWith(region, "NTSC-J"))
|
||||
entry->region = Region::NTSC_J;
|
||||
else if (StringUtil::StartsWith(region, "NTSC-K"))
|
||||
entry->region = Region::NTSC_K;
|
||||
else if (StringUtil::StartsWith(region, "NTSC-T"))
|
||||
entry->region = Region::NTSC_T;
|
||||
else if (StringUtil::StartsWith(region, "NTSC-U"))
|
||||
entry->region = Region::NTSC_U;
|
||||
////// PAL //////
|
||||
//////////////////
|
||||
else if (StringUtil::StartsWith(region, "PAL-AF"))
|
||||
entry->region = Region::PAL_AF;
|
||||
else if (StringUtil::StartsWith(region, "PAL-AU"))
|
||||
entry->region = Region::PAL_AU;
|
||||
else if (StringUtil::StartsWith(region, "PAL-A"))
|
||||
entry->region = Region::PAL_A;
|
||||
else if (StringUtil::StartsWith(region, "PAL-BE"))
|
||||
entry->region = Region::PAL_BE;
|
||||
else if (StringUtil::StartsWith(region, "PAL-E"))
|
||||
entry->region = Region::PAL_E;
|
||||
else if (StringUtil::StartsWith(region, "PAL-FI"))
|
||||
entry->region = Region::PAL_FI;
|
||||
else if (StringUtil::StartsWith(region, "PAL-F"))
|
||||
entry->region = Region::PAL_F;
|
||||
else if (StringUtil::StartsWith(region, "PAL-GR"))
|
||||
entry->region = Region::PAL_GR;
|
||||
else if (StringUtil::StartsWith(region, "PAL-G"))
|
||||
entry->region = Region::PAL_G;
|
||||
else if (StringUtil::StartsWith(region, "PAL-IN"))
|
||||
entry->region = Region::PAL_IN;
|
||||
else if (StringUtil::StartsWith(region, "PAL-I"))
|
||||
entry->region = Region::PAL_I;
|
||||
else if (StringUtil::StartsWith(region, "PAL-M"))
|
||||
entry->region = Region::PAL_M;
|
||||
else if (StringUtil::StartsWith(region, "PAL-NL"))
|
||||
entry->region = Region::PAL_NL;
|
||||
else if (StringUtil::StartsWith(region, "PAL-NO"))
|
||||
entry->region = Region::PAL_NO;
|
||||
else if (StringUtil::StartsWith(region, "PAL-P"))
|
||||
entry->region = Region::PAL_P;
|
||||
else if (StringUtil::StartsWith(region, "PAL-R"))
|
||||
entry->region = Region::PAL_R;
|
||||
else if (StringUtil::StartsWith(region, "PAL-SC"))
|
||||
entry->region = Region::PAL_SC;
|
||||
else if (StringUtil::StartsWith(region, "PAL-SWI"))
|
||||
entry->region = Region::PAL_SWI;
|
||||
else if (StringUtil::StartsWith(region, "PAL-SW"))
|
||||
entry->region = Region::PAL_SW;
|
||||
else if (StringUtil::StartsWith(region, "PAL-S"))
|
||||
entry->region = Region::PAL_S;
|
||||
else if (StringUtil::StartsWith(region, "PAL-UK"))
|
||||
entry->region = Region::PAL_UK;
|
||||
else
|
||||
entry->region = Region::Other;
|
||||
entry->region = ParseDatabaseRegion(region);
|
||||
|
||||
std::string filename(VMManager::GetGameSettingsPath(entry->serial, entry->crc));
|
||||
std::unique_ptr<INISettingsInterface> sif = std::make_unique<INISettingsInterface>(std::move(filename));
|
||||
|
@ -563,7 +541,7 @@ bool GameList::GetPython2ListEntry(const std::string& path, GameList::Entry* ent
|
|||
|
||||
return true;
|
||||
}
|
||||
// clang-format off
|
||||
|
||||
bool GameList::PopulateEntryFromPath(const std::string& path, GameList::Entry* entry)
|
||||
{
|
||||
if (VMManager::IsElfFileName(path.c_str()))
|
||||
|
@ -616,8 +594,7 @@ static bool ReadU64(std::FILE* stream, u64* dest)
|
|||
static bool WriteString(std::FILE* stream, const std::string& str)
|
||||
{
|
||||
const u32 size = static_cast<u32>(str.size());
|
||||
return (std::fwrite(&size, sizeof(size), 1, stream) > 0 &&
|
||||
(size == 0 || std::fwrite(str.data(), size, 1, stream) > 0));
|
||||
return (std::fwrite(&size, sizeof(size), 1, stream) > 0 && (size == 0 || std::fwrite(str.data(), size, 1, stream) > 0));
|
||||
}
|
||||
|
||||
static bool WriteU8(std::FILE* stream, u8 dest)
|
||||
|
@ -639,10 +616,10 @@ bool GameList::LoadEntriesFromCache(std::FILE* stream)
|
|||
{
|
||||
u32 file_signature, file_version;
|
||||
s64 start_pos, file_size;
|
||||
if (!ReadU32(stream, &file_signature) || !ReadU32(stream, &file_version) ||
|
||||
file_signature != GAME_LIST_CACHE_SIGNATURE || file_version != GAME_LIST_CACHE_VERSION ||
|
||||
(start_pos = FileSystem::FTell64(stream)) < 0 || FileSystem::FSeek64(stream, 0, SEEK_END) != 0 ||
|
||||
(file_size = FileSystem::FTell64(stream)) < 0 || FileSystem::FSeek64(stream, start_pos, SEEK_SET) != 0)
|
||||
if (!ReadU32(stream, &file_signature) || !ReadU32(stream, &file_version) || file_signature != GAME_LIST_CACHE_SIGNATURE ||
|
||||
file_version != GAME_LIST_CACHE_VERSION || (start_pos = FileSystem::FTell64(stream)) < 0 ||
|
||||
FileSystem::FSeek64(stream, 0, SEEK_END) != 0 || (file_size = FileSystem::FTell64(stream)) < 0 ||
|
||||
FileSystem::FSeek64(stream, start_pos, SEEK_SET) != 0)
|
||||
{
|
||||
Console.Warning("Game list cache is corrupted");
|
||||
return false;
|
||||
|
@ -658,11 +635,10 @@ bool GameList::LoadEntriesFromCache(std::FILE* stream)
|
|||
u8 compatibility_rating;
|
||||
u64 last_modified_time;
|
||||
|
||||
if (!ReadString(stream, &path) || !ReadString(stream, &ge.serial) || !ReadString(stream, &ge.title) ||
|
||||
!ReadU8(stream, &type) || !ReadU8(stream, ®ion) || !ReadU64(stream, &ge.total_size) ||
|
||||
!ReadU64(stream, &last_modified_time) || !ReadU32(stream, &ge.crc) || !ReadU8(stream, &compatibility_rating) ||
|
||||
region >= static_cast<u8>(Region::Count) || type >= static_cast<u8>(EntryType::Count) ||
|
||||
compatibility_rating > static_cast<u8>(CompatibilityRating::Perfect))
|
||||
if (!ReadString(stream, &path) || !ReadString(stream, &ge.serial) || !ReadString(stream, &ge.title) || !ReadU8(stream, &type) ||
|
||||
!ReadU8(stream, ®ion) || !ReadU64(stream, &ge.total_size) || !ReadU64(stream, &last_modified_time) ||
|
||||
!ReadU32(stream, &ge.crc) || !ReadU8(stream, &compatibility_rating) || region >= static_cast<u8>(Region::Count) ||
|
||||
type >= static_cast<u8>(EntryType::Count) || compatibility_rating > static_cast<u8>(CompatibilityRating::Perfect))
|
||||
{
|
||||
Console.Warning("Game list cache entry is corrupted");
|
||||
return false;
|
||||
|
@ -736,8 +712,7 @@ bool GameList::OpenCacheForWriting()
|
|||
|
||||
|
||||
// new cache file, write header
|
||||
if (!WriteU32(s_cache_write_stream, GAME_LIST_CACHE_SIGNATURE) ||
|
||||
!WriteU32(s_cache_write_stream, GAME_LIST_CACHE_VERSION))
|
||||
if (!WriteU32(s_cache_write_stream, GAME_LIST_CACHE_SIGNATURE) || !WriteU32(s_cache_write_stream, GAME_LIST_CACHE_VERSION))
|
||||
{
|
||||
Console.Error("Failed to write game list cache header");
|
||||
std::fclose(s_cache_write_stream);
|
||||
|
@ -792,13 +767,27 @@ void GameList::DeleteCacheFile()
|
|||
Console.Warning("Failed to delete game list cache '%s'", cache_filename.c_str());
|
||||
}
|
||||
|
||||
void GameList::RewriteCacheFile()
|
||||
{
|
||||
CloseCacheFileStream();
|
||||
DeleteCacheFile();
|
||||
|
||||
if (OpenCacheForWriting())
|
||||
{
|
||||
for (const GameList::Entry& entry : s_entries)
|
||||
WriteEntryToCache(&entry);
|
||||
|
||||
CloseCacheFileStream();
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsPathExcluded(const std::vector<std::string>& excluded_paths, const std::string& path)
|
||||
{
|
||||
return (std::find(excluded_paths.begin(), excluded_paths.end(), path) != excluded_paths.end());
|
||||
}
|
||||
|
||||
void GameList::ScanDirectory(const char* path, bool recursive, bool only_cache, const std::vector<std::string>& excluded_paths, const PlayedTimeMap& played_time_map,
|
||||
ProgressCallback* progress)
|
||||
void GameList::ScanDirectory(const char* path, bool recursive, bool only_cache, const std::vector<std::string>& excluded_paths,
|
||||
const PlayedTimeMap& played_time_map, ProgressCallback* progress)
|
||||
{
|
||||
Console.WriteLn("Scanning %s%s", path, recursive ? " (recursively)" : "");
|
||||
|
||||
|
@ -819,15 +808,13 @@ void GameList::ScanDirectory(const char* path, bool recursive, bool only_cache,
|
|||
{
|
||||
files_scanned++;
|
||||
|
||||
if (progress->IsCancelled() || !GameList::IsScannableFilename(ffd.FileName) ||
|
||||
IsPathExcluded(excluded_paths, ffd.FileName))
|
||||
if (progress->IsCancelled() || !GameList::IsScannableFilename(ffd.FileName) || IsPathExcluded(excluded_paths, ffd.FileName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
std::unique_lock lock(s_mutex);
|
||||
if (GetEntryForPath(ffd.FileName.c_str()) ||
|
||||
AddFileFromCache(ffd.FileName, ffd.ModificationTime, played_time_map) || only_cache)
|
||||
if (GetEntryForPath(ffd.FileName.c_str()) || AddFileFromCache(ffd.FileName, ffd.ModificationTime, played_time_map) || only_cache)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
@ -858,8 +845,8 @@ bool GameList::AddFileFromCache(const std::string& path, std::time_t timestamp,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GameList::ScanFile(std::string path, std::time_t timestamp, std::unique_lock<std::recursive_mutex>& lock,
|
||||
const PlayedTimeMap& played_time_map)
|
||||
bool GameList::ScanFile(
|
||||
std::string path, std::time_t timestamp, std::unique_lock<std::recursive_mutex>& lock, const PlayedTimeMap& played_time_map)
|
||||
{
|
||||
// don't block UI while scanning
|
||||
lock.unlock();
|
||||
|
@ -887,6 +874,13 @@ bool GameList::ScanFile(std::string path, std::time_t timestamp, std::unique_loc
|
|||
}
|
||||
|
||||
lock.lock();
|
||||
|
||||
// remove if present
|
||||
auto it = std::find_if(
|
||||
s_entries.begin(), s_entries.end(), [&entry](const Entry& existing_entry) { return (existing_entry.path == entry.path); });
|
||||
if (it != s_entries.end())
|
||||
s_entries.erase(it);
|
||||
|
||||
s_entries.push_back(std::move(entry));
|
||||
return true;
|
||||
}
|
||||
|
@ -1004,6 +998,32 @@ void GameList::Refresh(bool invalidate_cache, bool only_cache, ProgressCallback*
|
|||
s_cache_map.clear();
|
||||
}
|
||||
|
||||
bool GameList::RescanPath(const std::string& path)
|
||||
{
|
||||
FILESYSTEM_STAT_DATA sd;
|
||||
if (!FileSystem::StatFile(path.c_str(), &sd))
|
||||
return false;
|
||||
|
||||
std::unique_lock lock(s_mutex);
|
||||
|
||||
const PlayedTimeMap played_time(LoadPlayedTimeMap(GetPlayedTimeFile()));
|
||||
|
||||
{
|
||||
// cancel if excluded
|
||||
const std::vector<std::string> excluded_paths(Host::GetBaseStringListSetting("GameList", "ExcludedPaths"));
|
||||
if (std::find(excluded_paths.begin(), excluded_paths.end(), path) != excluded_paths.end())
|
||||
return false;
|
||||
}
|
||||
|
||||
// re-scan!
|
||||
if (!ScanFile(path, sd.ModificationTime, lock, played_time))
|
||||
return true;
|
||||
|
||||
// update cache.. this is far from ideal, but since everything's variable length, all we can do.
|
||||
RewriteCacheFile();
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string GameList::GetPlayedTimeFile()
|
||||
{
|
||||
return Path::Combine(EmuFolders::Settings, "playtime.dat");
|
||||
|
@ -1021,8 +1041,8 @@ bool GameList::ParsePlayedTimeLine(char* line, std::string& serial, PlayedTimeEn
|
|||
const std::string_view serial_tok(StringUtil::StripWhitespace(std::string_view(line, PLAYED_TIME_SERIAL_LENGTH)));
|
||||
const std::string_view total_played_time_tok(
|
||||
StringUtil::StripWhitespace(std::string_view(line + PLAYED_TIME_SERIAL_LENGTH + 1, PLAYED_TIME_LAST_TIME_LENGTH)));
|
||||
const std::string_view last_played_time_tok(StringUtil::StripWhitespace(std::string_view(
|
||||
line + PLAYED_TIME_SERIAL_LENGTH + 1 + PLAYED_TIME_LAST_TIME_LENGTH + 1, PLAYED_TIME_TOTAL_TIME_LENGTH)));
|
||||
const std::string_view last_played_time_tok(StringUtil::StripWhitespace(
|
||||
std::string_view(line + PLAYED_TIME_SERIAL_LENGTH + 1 + PLAYED_TIME_LAST_TIME_LENGTH + 1, PLAYED_TIME_TOTAL_TIME_LENGTH)));
|
||||
|
||||
const std::optional<u64> total_played_time(StringUtil::FromChars<u64>(total_played_time_tok));
|
||||
const std::optional<u64> last_played_time(StringUtil::FromChars<u64>(last_played_time_tok));
|
||||
|
@ -1040,9 +1060,8 @@ bool GameList::ParsePlayedTimeLine(char* line, std::string& serial, PlayedTimeEn
|
|||
|
||||
std::string GameList::MakePlayedTimeLine(const std::string& serial, const PlayedTimeEntry& entry)
|
||||
{
|
||||
return fmt::format("{:<{}} {:<{}} {:<{}}\n", serial, static_cast<unsigned>(PLAYED_TIME_SERIAL_LENGTH),
|
||||
entry.total_played_time, static_cast<unsigned>(PLAYED_TIME_TOTAL_TIME_LENGTH),
|
||||
entry.last_played_time, static_cast<unsigned>(PLAYED_TIME_LAST_TIME_LENGTH));
|
||||
return fmt::format("{:<{}} {:<{}} {:<{}}\n", serial, static_cast<unsigned>(PLAYED_TIME_SERIAL_LENGTH), entry.total_played_time,
|
||||
static_cast<unsigned>(PLAYED_TIME_TOTAL_TIME_LENGTH), entry.last_played_time, static_cast<unsigned>(PLAYED_TIME_LAST_TIME_LENGTH));
|
||||
}
|
||||
|
||||
GameList::PlayedTimeMap GameList::LoadPlayedTimeMap(const std::string& path)
|
||||
|
@ -1088,10 +1107,10 @@ GameList::PlayedTimeMap GameList::LoadPlayedTimeMap(const std::string& path)
|
|||
return ret;
|
||||
}
|
||||
|
||||
GameList::PlayedTimeEntry GameList::UpdatePlayedTimeFile(const std::string& path, const std::string& serial,
|
||||
std::time_t last_time, std::time_t add_time)
|
||||
GameList::PlayedTimeEntry GameList::UpdatePlayedTimeFile(
|
||||
const std::string& path, const std::string& serial, std::time_t last_time, std::time_t add_time)
|
||||
{
|
||||
const PlayedTimeEntry new_entry{ last_time, add_time };
|
||||
const PlayedTimeEntry new_entry{last_time, add_time};
|
||||
|
||||
auto fp = FileSystem::OpenManagedCFile(path.c_str(), "r+b");
|
||||
|
||||
|
@ -1138,8 +1157,7 @@ GameList::PlayedTimeEntry GameList::UpdatePlayedTimeFile(const std::string& path
|
|||
line_entry.total_played_time = (last_time != 0) ? (line_entry.total_played_time + add_time) : 0;
|
||||
|
||||
std::string new_line(MakePlayedTimeLine(serial, line_entry));
|
||||
if (FileSystem::FSeek64(fp.get(), line_pos, SEEK_SET) != 0 ||
|
||||
std::fwrite(new_line.data(), new_line.length(), 1, fp.get()) != 1 ||
|
||||
if (FileSystem::FSeek64(fp.get(), line_pos, SEEK_SET) != 0 || std::fwrite(new_line.data(), new_line.length(), 1, fp.get()) != 1 ||
|
||||
std::fflush(fp.get()) != 0)
|
||||
{
|
||||
Console.Error("Failed to update '%s'.", path.c_str());
|
||||
|
@ -1152,8 +1170,7 @@ GameList::PlayedTimeEntry GameList::UpdatePlayedTimeFile(const std::string& path
|
|||
{
|
||||
// new entry.
|
||||
std::string new_line(MakePlayedTimeLine(serial, new_entry));
|
||||
if (FileSystem::FSeek64(fp.get(), 0, SEEK_END) != 0 ||
|
||||
std::fwrite(new_line.data(), new_line.length(), 1, fp.get()) != 1)
|
||||
if (FileSystem::FSeek64(fp.get(), 0, SEEK_END) != 0 || std::fwrite(new_line.data(), new_line.length(), 1, fp.get()) != 1)
|
||||
{
|
||||
Console.Error("Failed to write '%s'.", path.c_str());
|
||||
}
|
||||
|
@ -1243,7 +1260,7 @@ std::string GameList::FormatTimestamp(std::time_t timestamp)
|
|||
ret = "Today";
|
||||
}
|
||||
else if ((ctime.tm_year == ttime.tm_year && ctime.tm_yday == (ttime.tm_yday + 1)) ||
|
||||
(ctime.tm_yday == 0 && (ctime.tm_year - 1) == ttime.tm_year))
|
||||
(ctime.tm_yday == 0 && (ctime.tm_year - 1) == ttime.tm_year))
|
||||
{
|
||||
ret = "Yesterday";
|
||||
}
|
||||
|
@ -1445,38 +1462,39 @@ bool GameList::DownloadCovers(const std::vector<std::string>& url_templates, boo
|
|||
|
||||
// we could actually do a few in parallel here...
|
||||
std::string filename(Common::HTTPDownloader::URLDecode(url));
|
||||
downloader->CreateRequest(std::move(url), [use_serial, &save_callback, entry_path = std::move(entry_path),
|
||||
filename = std::move(filename)](s32 status_code, const std::string& content_type, Common::HTTPDownloader::Request::Data data) {
|
||||
if (status_code != Common::HTTPDownloader::HTTP_OK || data.empty())
|
||||
return;
|
||||
downloader->CreateRequest(
|
||||
std::move(url), [use_serial, &save_callback, entry_path = std::move(entry_path), filename = std::move(filename)](
|
||||
s32 status_code, const std::string& content_type, Common::HTTPDownloader::Request::Data data) {
|
||||
if (status_code != Common::HTTPDownloader::HTTP_OK || data.empty())
|
||||
return;
|
||||
|
||||
std::unique_lock lock(s_mutex);
|
||||
const GameList::Entry* entry = GetEntryForPath(entry_path.c_str());
|
||||
if (!entry || !GetCoverImagePathForEntry(entry).empty())
|
||||
return;
|
||||
std::unique_lock lock(s_mutex);
|
||||
const GameList::Entry* entry = GetEntryForPath(entry_path.c_str());
|
||||
if (!entry || !GetCoverImagePathForEntry(entry).empty())
|
||||
return;
|
||||
|
||||
// prefer the content type from the response for the extension
|
||||
// otherwise, if it's missing, and the request didn't have an extension.. fall back to jpegs.
|
||||
std::string template_filename;
|
||||
std::string content_type_extension(Common::HTTPDownloader::GetExtensionForContentType(content_type));
|
||||
// prefer the content type from the response for the extension
|
||||
// otherwise, if it's missing, and the request didn't have an extension.. fall back to jpegs.
|
||||
std::string template_filename;
|
||||
std::string content_type_extension(Common::HTTPDownloader::GetExtensionForContentType(content_type));
|
||||
|
||||
// don't treat the domain name as an extension..
|
||||
const std::string::size_type last_slash = filename.find('/');
|
||||
const std::string::size_type last_dot = filename.find('.');
|
||||
if (!content_type_extension.empty())
|
||||
template_filename = fmt::format("cover.{}", content_type_extension);
|
||||
else if (last_slash != std::string::npos && last_dot != std::string::npos && last_dot > last_slash)
|
||||
template_filename = Path::GetFileName(filename);
|
||||
else
|
||||
template_filename = "cover.jpg";
|
||||
// don't treat the domain name as an extension..
|
||||
const std::string::size_type last_slash = filename.find('/');
|
||||
const std::string::size_type last_dot = filename.find('.');
|
||||
if (!content_type_extension.empty())
|
||||
template_filename = fmt::format("cover.{}", content_type_extension);
|
||||
else if (last_slash != std::string::npos && last_dot != std::string::npos && last_dot > last_slash)
|
||||
template_filename = Path::GetFileName(filename);
|
||||
else
|
||||
template_filename = "cover.jpg";
|
||||
|
||||
std::string write_path(GetNewCoverImagePathForEntry(entry, template_filename.c_str(), use_serial));
|
||||
if (write_path.empty())
|
||||
return;
|
||||
std::string write_path(GetNewCoverImagePathForEntry(entry, template_filename.c_str(), use_serial));
|
||||
if (write_path.empty())
|
||||
return;
|
||||
|
||||
if (FileSystem::WriteBinaryFile(write_path.c_str(), data.data(), data.size()) && save_callback)
|
||||
save_callback(entry, std::move(write_path));
|
||||
});
|
||||
if (FileSystem::WriteBinaryFile(write_path.c_str(), data.data(), data.size()) && save_callback)
|
||||
save_callback(entry, std::move(write_path));
|
||||
});
|
||||
downloader->WaitForAllRequests();
|
||||
progress->IncrementProgressValue();
|
||||
}
|
||||
|
|
|
@ -123,6 +123,9 @@ namespace GameList
|
|||
/// If only_cache is set, no new files will be scanned, only those present in the cache.
|
||||
void Refresh(bool invalidate_cache, bool only_cache = false, ProgressCallback* progress = nullptr);
|
||||
|
||||
/// Re-scans a single entry in the game list.
|
||||
bool RescanPath(const std::string& path);
|
||||
|
||||
/// Add played time for the specified serial.
|
||||
void AddPlayedTimeForSerial(const std::string& serial, std::time_t last_time, std::time_t add_time);
|
||||
void ClearPlayedTimeForSerial(const std::string& serial);
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include "HostDisplay.h"
|
||||
#include "IconsFontAwesome5.h"
|
||||
#include "PerformanceMetrics.h"
|
||||
#include "Recording/InputRecording.h"
|
||||
|
||||
#ifdef PCSX2_CORE
|
||||
#include "Frontend/FullscreenUI.h"
|
||||
|
@ -48,7 +49,6 @@
|
|||
#include "Frontend/InputManager.h"
|
||||
#include "VMManager.h"
|
||||
#endif
|
||||
#include <pcsx2/Recording/InputRecording.h>
|
||||
|
||||
namespace ImGuiManager
|
||||
{
|
||||
|
@ -540,6 +540,11 @@ void Host::AddOSDMessage(std::string message, float duration /*= 2.0f*/)
|
|||
|
||||
void Host::AddKeyedOSDMessage(std::string key, std::string message, float duration /* = 2.0f */)
|
||||
{
|
||||
if (!key.empty())
|
||||
Console.WriteLn(Color_StrongGreen, fmt::format("OSD [{}]: {}", key, message));
|
||||
else
|
||||
Console.WriteLn(Color_StrongGreen, fmt::format("OSD: {}", message));
|
||||
|
||||
OSDMessage msg;
|
||||
msg.key = std::move(key);
|
||||
msg.text = std::move(message);
|
||||
|
@ -552,6 +557,11 @@ void Host::AddKeyedOSDMessage(std::string key, std::string message, float durati
|
|||
|
||||
void Host::AddIconOSDMessage(std::string key, const char* icon, const std::string_view& message, float duration /* = 2.0f */)
|
||||
{
|
||||
if (!key.empty())
|
||||
Console.WriteLn(Color_StrongGreen, fmt::format("OSD [{}]: {}", key, message));
|
||||
else
|
||||
Console.WriteLn(Color_StrongGreen, fmt::format("OSD: {}", message));
|
||||
|
||||
OSDMessage msg;
|
||||
msg.key = std::move(key);
|
||||
msg.text = fmt::format("{} {}", icon, message);
|
||||
|
|
166
pcsx2/GS/GS.cpp
166
pcsx2/GS/GS.cpp
|
@ -14,10 +14,15 @@
|
|||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#ifndef PCSX2_CORE
|
||||
// NOTE: The include order matters - GS.h includes windows.h
|
||||
#ifdef _WIN32
|
||||
// Need to ensure we include windows.h and set _WIN32_WINNT before wx does..
|
||||
#include "common/RedtapeWindows.h"
|
||||
#endif
|
||||
#include "GS/Window/GSwxDialog.h"
|
||||
#endif
|
||||
|
||||
#include "GS.h"
|
||||
#include "GSGL.h"
|
||||
#include "GSUtil.h"
|
||||
|
@ -116,20 +121,7 @@ void GSinitConfig()
|
|||
|
||||
void GSshutdown()
|
||||
{
|
||||
#ifndef PCSX2_CORE
|
||||
if (g_gs_renderer)
|
||||
{
|
||||
g_gs_renderer->Destroy();
|
||||
g_gs_renderer.reset();
|
||||
}
|
||||
if (g_gs_device)
|
||||
{
|
||||
g_gs_device->Destroy();
|
||||
g_gs_device.reset();
|
||||
}
|
||||
|
||||
Host::ReleaseHostDisplay(true);
|
||||
#endif
|
||||
GSclose();
|
||||
|
||||
#ifdef _WIN32
|
||||
if (SUCCEEDED(s_hr))
|
||||
|
@ -139,6 +131,9 @@ void GSshutdown()
|
|||
s_hr = E_FAIL;
|
||||
}
|
||||
#endif
|
||||
|
||||
// ensure all screenshots have been saved
|
||||
GSJoinSnapshotThreads();
|
||||
}
|
||||
|
||||
void GSclose()
|
||||
|
@ -963,25 +958,9 @@ const std::string root_hw("/tmp/GS_HW_dump32/");
|
|||
|
||||
#ifdef _WIN32
|
||||
|
||||
void* vmalloc(size_t size, bool code)
|
||||
{
|
||||
void* ptr = VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, code ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE);
|
||||
if (!ptr)
|
||||
throw std::bad_alloc();
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void vmfree(void* ptr, size_t size)
|
||||
{
|
||||
VirtualFree(ptr, 0, MEM_RELEASE);
|
||||
}
|
||||
|
||||
#ifdef PCSX2_CORE
|
||||
// Safe, placeholder-based mapping for Win10+ and Qt.
|
||||
|
||||
static HANDLE s_fh = NULL;
|
||||
|
||||
void* fifo_alloc(size_t size, size_t repeat)
|
||||
void* GSAllocateWrappedMemory(size_t size, size_t repeat)
|
||||
{
|
||||
pxAssertRel(!s_fh, "Has no file mapping");
|
||||
|
||||
|
@ -1030,7 +1009,7 @@ void* fifo_alloc(size_t size, size_t repeat)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
void fifo_free(void* ptr, size_t size, size_t repeat)
|
||||
void GSFreeWrappedMemory(void* ptr, size_t size, size_t repeat)
|
||||
{
|
||||
pxAssertRel(s_fh, "Has a file mapping");
|
||||
|
||||
|
@ -1046,130 +1025,14 @@ void fifo_free(void* ptr, size_t size, size_t repeat)
|
|||
|
||||
#else
|
||||
|
||||
// "Best effort" mapping for <Win10 and wx build.
|
||||
// This can be removed once the wx UI is dropped.
|
||||
|
||||
static HANDLE s_fh = NULL;
|
||||
static u8* s_Next[8];
|
||||
|
||||
void* fifo_alloc(size_t size, size_t repeat)
|
||||
{
|
||||
ASSERT(s_fh == NULL);
|
||||
|
||||
if (repeat >= std::size(s_Next))
|
||||
{
|
||||
fprintf(stderr, "Memory mapping overflow (%zu >= %u)\n", repeat, static_cast<unsigned>(std::size(s_Next)));
|
||||
return nullptr; // Fallback to default vmalloc
|
||||
}
|
||||
|
||||
s_fh = CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, size, nullptr);
|
||||
DWORD errorID = ::GetLastError();
|
||||
if (s_fh == NULL)
|
||||
{
|
||||
fprintf(stderr, "Failed to reserve memory. WIN API ERROR:%u\n", errorID);
|
||||
return nullptr; // Fallback to default vmalloc
|
||||
}
|
||||
|
||||
int mmap_segment_failed = 0;
|
||||
void* fifo = MapViewOfFile(s_fh, FILE_MAP_ALL_ACCESS, 0, 0, size);
|
||||
for (size_t i = 1; i < repeat; i++)
|
||||
{
|
||||
void* base = (u8*)fifo + size * i;
|
||||
s_Next[i] = (u8*)MapViewOfFileEx(s_fh, FILE_MAP_ALL_ACCESS, 0, 0, size, base);
|
||||
errorID = ::GetLastError();
|
||||
if (s_Next[i] != base)
|
||||
{
|
||||
mmap_segment_failed++;
|
||||
if (mmap_segment_failed > 4)
|
||||
{
|
||||
fprintf(stderr, "Memory mapping failed after %d attempts, aborting. WIN API ERROR:%u\n", mmap_segment_failed, errorID);
|
||||
fifo_free(fifo, size, repeat);
|
||||
return nullptr; // Fallback to default vmalloc
|
||||
}
|
||||
do
|
||||
{
|
||||
UnmapViewOfFile(s_Next[i]);
|
||||
s_Next[i] = 0;
|
||||
} while (--i > 0);
|
||||
|
||||
fifo = MapViewOfFile(s_fh, FILE_MAP_ALL_ACCESS, 0, 0, size);
|
||||
}
|
||||
}
|
||||
|
||||
return fifo;
|
||||
}
|
||||
|
||||
void fifo_free(void* ptr, size_t size, size_t repeat)
|
||||
{
|
||||
ASSERT(s_fh != NULL);
|
||||
|
||||
if (s_fh == NULL)
|
||||
{
|
||||
if (ptr != NULL)
|
||||
vmfree(ptr, size);
|
||||
return;
|
||||
}
|
||||
|
||||
UnmapViewOfFile(ptr);
|
||||
|
||||
for (size_t i = 1; i < std::size(s_Next); i++)
|
||||
{
|
||||
if (s_Next[i] != 0)
|
||||
{
|
||||
UnmapViewOfFile(s_Next[i]);
|
||||
s_Next[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
CloseHandle(s_fh);
|
||||
s_fh = NULL;
|
||||
}
|
||||
|
||||
#endif // PCSX2_CORE
|
||||
|
||||
#else
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void* vmalloc(size_t size, bool code)
|
||||
{
|
||||
size_t mask = getpagesize() - 1;
|
||||
|
||||
size = (size + mask) & ~mask;
|
||||
|
||||
int prot = PROT_READ | PROT_WRITE;
|
||||
int flags = MAP_PRIVATE | MAP_ANONYMOUS;
|
||||
|
||||
if (code)
|
||||
{
|
||||
prot |= PROT_EXEC;
|
||||
#if defined(_M_AMD64) && !defined(__APPLE__)
|
||||
// macOS doesn't allow any mappings in the first 4GB of address space
|
||||
flags |= MAP_32BIT;
|
||||
#endif
|
||||
}
|
||||
|
||||
void* ptr = mmap(NULL, size, prot, flags, -1, 0);
|
||||
if (ptr == MAP_FAILED)
|
||||
throw std::bad_alloc();
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void vmfree(void* ptr, size_t size)
|
||||
{
|
||||
size_t mask = getpagesize() - 1;
|
||||
|
||||
size = (size + mask) & ~mask;
|
||||
|
||||
munmap(ptr, size);
|
||||
}
|
||||
|
||||
static int s_shm_fd = -1;
|
||||
|
||||
void* fifo_alloc(size_t size, size_t repeat)
|
||||
void* GSAllocateWrappedMemory(size_t size, size_t repeat)
|
||||
{
|
||||
ASSERT(s_shm_fd == -1);
|
||||
|
||||
|
@ -1201,7 +1064,7 @@ void* fifo_alloc(size_t size, size_t repeat)
|
|||
return fifo;
|
||||
}
|
||||
|
||||
void fifo_free(void* ptr, size_t size, size_t repeat)
|
||||
void GSFreeWrappedMemory(void* ptr, size_t size, size_t repeat)
|
||||
{
|
||||
ASSERT(s_shm_fd >= 0);
|
||||
|
||||
|
@ -1556,7 +1419,6 @@ void GSApp::Init()
|
|||
m_default_configuration["UserHacks_TCOffsetY"] = "0";
|
||||
m_default_configuration["UserHacks_TextureInsideRt"] = "0";
|
||||
m_default_configuration["UserHacks_WildHack"] = "0";
|
||||
m_default_configuration["wrap_gs_mem"] = "0";
|
||||
m_default_configuration["vsync"] = "0";
|
||||
// clang-format on
|
||||
}
|
||||
|
|
|
@ -93,6 +93,7 @@ void GSResetAPIState();
|
|||
void GSRestoreAPIState();
|
||||
bool GSSaveSnapshotToMemory(u32 window_width, u32 window_height, bool apply_aspect, bool crop_borders,
|
||||
u32* width, u32* height, std::vector<u32>* pixels);
|
||||
void GSJoinSnapshotThreads();
|
||||
|
||||
class GSApp
|
||||
{
|
||||
|
|
|
@ -17,17 +17,20 @@
|
|||
#include "GSClut.h"
|
||||
#include "GSLocalMemory.h"
|
||||
#include "GSGL.h"
|
||||
|
||||
#define CLUT_ALLOC_SIZE (2 * 4096)
|
||||
#include "common/AlignedMalloc.h"
|
||||
|
||||
GSClut::GSClut(GSLocalMemory* mem)
|
||||
: m_mem(mem)
|
||||
{
|
||||
u8* p = (u8*)vmalloc(CLUT_ALLOC_SIZE, false);
|
||||
static constexpr u32 CLUT_ALLOC_SIZE = 4096 * 2;
|
||||
|
||||
m_clut = (u16*)&p[0]; // 1k + 1k for mirrored area simulating wrapping memory
|
||||
m_buff32 = (u32*)&p[2048]; // 1k
|
||||
m_buff64 = (u64*)&p[4096]; // 2k
|
||||
// 1k + 1k for mirrored area simulating wrapping memory
|
||||
m_clut = static_cast<u16*>(_aligned_malloc(CLUT_ALLOC_SIZE, 32));
|
||||
if (!m_clut)
|
||||
throw std::bad_alloc();
|
||||
|
||||
m_buff32 = reinterpret_cast<u32*>(reinterpret_cast<u8*>(m_clut) + 2048); // 1k
|
||||
m_buff64 = reinterpret_cast<u64*>(reinterpret_cast<u8*>(m_clut) + 4096); // 2k
|
||||
m_write.dirty = 1;
|
||||
m_read.dirty = true;
|
||||
|
||||
|
@ -100,7 +103,7 @@ GSClut::GSClut(GSLocalMemory* mem)
|
|||
|
||||
GSClut::~GSClut()
|
||||
{
|
||||
vmfree(m_clut, CLUT_ALLOC_SIZE);
|
||||
_aligned_free(m_clut);
|
||||
}
|
||||
|
||||
u8 GSClut::IsInvalid()
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "PrecompiledHeader.h"
|
||||
#include "GSCodeBuffer.h"
|
||||
#include "GSExtra.h"
|
||||
#include "common/General.h"
|
||||
|
||||
GSCodeBuffer::GSCodeBuffer(size_t blocksize)
|
||||
: m_blocksize(blocksize)
|
||||
|
@ -29,7 +30,7 @@ GSCodeBuffer::~GSCodeBuffer()
|
|||
{
|
||||
for (auto buffer : m_buffers)
|
||||
{
|
||||
vmfree(buffer, m_blocksize);
|
||||
HostSys::Munmap(buffer, m_blocksize);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,7 +43,9 @@ void* GSCodeBuffer::GetBuffer(size_t size)
|
|||
|
||||
if (m_ptr == NULL || m_pos + size > m_blocksize)
|
||||
{
|
||||
m_ptr = (u8*)vmalloc(m_blocksize, true);
|
||||
m_ptr = (u8*)HostSys::Mmap(nullptr, m_blocksize, PageProtectionMode().All());
|
||||
if (!m_ptr)
|
||||
pxFailRel("Failed to allocate GS code buffer");
|
||||
|
||||
m_pos = 0;
|
||||
|
||||
|
|
|
@ -118,11 +118,8 @@ static constexpr u32 MAX_SKIPPED_DUPLICATE_FRAMES = 3;
|
|||
extern const std::string root_sw;
|
||||
extern const std::string root_hw;
|
||||
|
||||
extern void* vmalloc(size_t size, bool code);
|
||||
extern void vmfree(void* ptr, size_t size);
|
||||
|
||||
extern void* fifo_alloc(size_t size, size_t repeat);
|
||||
extern void fifo_free(void* ptr, size_t size, size_t repeat);
|
||||
extern void* GSAllocateWrappedMemory(size_t size, size_t repeat);
|
||||
extern void GSFreeWrappedMemory(void* ptr, size_t size, size_t repeat);
|
||||
|
||||
// clang-format off
|
||||
|
||||
|
|
|
@ -20,9 +20,7 @@
|
|||
|
||||
#if !defined(NDEBUG) || defined(_DEBUG) || defined(_DEVEL)
|
||||
#define ENABLE_OGL_DEBUG // Create a debug context and check opengl command status. Allow also to dump various textures/states.
|
||||
//#define ENABLE_OGL_DEBUG_FENCE
|
||||
//#define ENABLE_OGL_DEBUG_MEM_BW // compute the quantity of data transfered (debug purpose)
|
||||
//#define ENABLE_TRACE_REG // print GS reg write
|
||||
//#define ENABLE_TRACE_REG // print GS reg write
|
||||
//#define ENABLE_EXTRA_LOG // print extra log
|
||||
#endif
|
||||
|
||||
|
|
|
@ -62,22 +62,9 @@ GSLocalMemory::readImage GSLocalMemory::m_readImageX;
|
|||
GSLocalMemory::GSLocalMemory()
|
||||
: m_clut(this)
|
||||
{
|
||||
m_use_fifo_alloc = theApp.GetConfigB("UserHacks") && theApp.GetConfigB("wrap_gs_mem");
|
||||
|
||||
if (!GSConfig.UseHardwareRenderer())
|
||||
m_use_fifo_alloc = true;
|
||||
|
||||
if (m_use_fifo_alloc)
|
||||
m_vm8 = (u8*)fifo_alloc(m_vmsize, 4);
|
||||
else
|
||||
m_vm8 = nullptr;
|
||||
|
||||
// Either we don't use fifo alloc or we get an error.
|
||||
if (m_vm8 == nullptr)
|
||||
{
|
||||
m_vm8 = (u8*)vmalloc(m_vmsize * 4, false);
|
||||
m_use_fifo_alloc = false;
|
||||
}
|
||||
m_vm8 = (u8*)GSAllocateWrappedMemory(m_vmsize, 4);
|
||||
if (!m_vm8)
|
||||
throw std::bad_alloc();
|
||||
|
||||
memset(m_vm8, 0, m_vmsize);
|
||||
|
||||
|
@ -260,10 +247,8 @@ GSLocalMemory::GSLocalMemory()
|
|||
|
||||
GSLocalMemory::~GSLocalMemory()
|
||||
{
|
||||
if (m_use_fifo_alloc)
|
||||
fifo_free(m_vm8, m_vmsize, 4);
|
||||
else
|
||||
vmfree(m_vm8, m_vmsize * 4);
|
||||
if (m_vm8)
|
||||
GSFreeWrappedMemory(m_vm8, m_vmsize, 4);
|
||||
|
||||
for (auto& i : m_pomap)
|
||||
_aligned_free(i.second);
|
||||
|
|
|
@ -487,9 +487,6 @@ public:
|
|||
|
||||
GSClut m_clut;
|
||||
|
||||
protected:
|
||||
bool m_use_fifo_alloc;
|
||||
|
||||
public:
|
||||
static constexpr GSSwizzleInfo swizzle32 {swizzleTables32};
|
||||
static constexpr GSSwizzleInfo swizzle32Z {swizzleTables32Z};
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "GSRingHeap.h"
|
||||
#include "GS.h"
|
||||
#include "GSExtra.h"
|
||||
#include "common/AlignedMalloc.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
@ -120,7 +121,7 @@ struct GSRingHeap::Buffer
|
|||
if (unlikely(m_amt_allocated.fetch_sub(amt, std::memory_order_release) == amt))
|
||||
{
|
||||
std::atomic_thread_fence(std::memory_order_acquire);
|
||||
vmfree(this, m_size);
|
||||
_aligned_free(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -167,7 +168,7 @@ struct GSRingHeap::Buffer
|
|||
static Buffer* make(int quadrant_shift)
|
||||
{
|
||||
size_t size = 4ull << quadrant_shift;
|
||||
Buffer* buffer = reinterpret_cast<Buffer*>(vmalloc(size, false));
|
||||
Buffer* buffer = reinterpret_cast<Buffer*>(_aligned_malloc(size, 32));
|
||||
buffer->m_size = size;
|
||||
buffer->m_quadrant_shift = quadrant_shift;
|
||||
buffer->m_amt_allocated.store(1, std::memory_order_relaxed);
|
||||
|
|
|
@ -387,38 +387,25 @@ void GSDevice::Interlace(const GSVector2i& ds, int field, int mode, float yoffse
|
|||
default:
|
||||
m_current = m_merge;
|
||||
}
|
||||
|
||||
if ((GSConfig.FXAA || GSConfig.ShadeBoost) && m_current != m_merge)
|
||||
{
|
||||
const GSVector2i s = m_current->GetSize();
|
||||
ResizeTexture(&m_merge, GSTexture::Type::RenderTarget, s.x, s.y, false);
|
||||
StretchRect(m_current, GSVector4(0, 0, 1, 1), m_merge, GSVector4(0, 0, ds.x, ds.y), ShaderConvert::COPY, false);
|
||||
m_current = m_merge;
|
||||
}
|
||||
}
|
||||
|
||||
void GSDevice::FXAA()
|
||||
{
|
||||
const GSVector2i s = m_current->GetSize();
|
||||
|
||||
if (ResizeTexture(&m_target_tmp, GSTexture::Type::RenderTarget, s.x, s.y, false))
|
||||
// Combining FXAA+ShadeBoost can't share the same target.
|
||||
GSTexture*& dTex = (m_current == m_target_tmp) ? m_merge : m_target_tmp;
|
||||
if (ResizeTexture(&dTex, GSTexture::Type::RenderTarget, m_current->GetWidth(), m_current->GetHeight(), false, true))
|
||||
{
|
||||
const GSVector4 sRect(0, 0, 1, 1);
|
||||
const GSVector4 dRect(0, 0, s.x, s.y);
|
||||
|
||||
StretchRect(m_current, sRect, m_target_tmp, dRect, ShaderConvert::TRANSPARENCY_FILTER, false);
|
||||
DoFXAA(m_target_tmp, m_current);
|
||||
InvalidateRenderTarget(dTex);
|
||||
DoFXAA(m_current, dTex);
|
||||
m_current = dTex;
|
||||
}
|
||||
}
|
||||
|
||||
void GSDevice::ShadeBoost()
|
||||
{
|
||||
const GSVector2i s = m_current->GetSize();
|
||||
|
||||
if (ResizeTexture(&m_target_tmp, GSTexture::Type::RenderTarget, s.x, s.y, false))
|
||||
if (ResizeTexture(&m_target_tmp, GSTexture::Type::RenderTarget, m_current->GetWidth(), m_current->GetHeight(), false, true))
|
||||
{
|
||||
const GSVector4 sRect(0, 0, 1, 1);
|
||||
const GSVector4 dRect(0, 0, s.x, s.y);
|
||||
InvalidateRenderTarget(m_target_tmp);
|
||||
|
||||
// predivide to avoid the divide (multiply) in the shader
|
||||
const float params[4] = {
|
||||
|
@ -427,13 +414,15 @@ void GSDevice::ShadeBoost()
|
|||
static_cast<float>(GSConfig.ShadeBoost_Saturation) * (1.0f / 50.0f),
|
||||
};
|
||||
|
||||
StretchRect(m_current, sRect, m_target_tmp, dRect, ShaderConvert::COPY, false);
|
||||
DoShadeBoost(m_target_tmp, m_current, params);
|
||||
DoShadeBoost(m_current, m_target_tmp, params);
|
||||
|
||||
m_current = m_target_tmp;
|
||||
}
|
||||
}
|
||||
|
||||
void GSDevice::Resize(int width, int height)
|
||||
{
|
||||
GSTexture*& dTex = (m_current == m_target_tmp) ? m_merge : m_target_tmp;
|
||||
GSVector2i s = m_current->GetSize();
|
||||
int multiplier = 1;
|
||||
|
||||
|
@ -442,12 +431,12 @@ void GSDevice::Resize(int width, int height)
|
|||
s = m_current->GetSize() * GSVector2i(++multiplier);
|
||||
}
|
||||
|
||||
if (ResizeTexture(&m_target_tmp, GSTexture::Type::RenderTarget, s.x, s.y, false))
|
||||
if (ResizeTexture(&dTex, GSTexture::Type::RenderTarget, s.x, s.y, false))
|
||||
{
|
||||
const GSVector4 sRect(0, 0, 1, 1);
|
||||
const GSVector4 dRect(0, 0, s.x, s.y);
|
||||
StretchRect(m_current, sRect, m_target_tmp, dRect, ShaderConvert::COPY, false);
|
||||
m_current = m_target_tmp;
|
||||
StretchRect(m_current, sRect, dTex, dRect, ShaderConvert::COPY, false);
|
||||
m_current = dTex;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -699,6 +699,7 @@ public:
|
|||
bool bptc_textures : 1; ///< Supports BC6/7 texture compression.
|
||||
bool framebuffer_fetch : 1; ///< Can sample from the framebuffer without texture barriers.
|
||||
bool dual_source_blend : 1; ///< Can use alpha output as a blend factor.
|
||||
bool clip_control : 1; ///< Can use 0..1 depth range instead of -1..1.
|
||||
bool stencil_buffer : 1; ///< Supports stencil buffer, and can use for DATE.
|
||||
bool cas_sharpening : 1; ///< Supports sufficient functionality for contrast adaptive sharpening.
|
||||
FeatureSupport()
|
||||
|
|
|
@ -28,6 +28,9 @@
|
|||
#include "common/Timer.h"
|
||||
#include "fmt/core.h"
|
||||
#include <array>
|
||||
#include <deque>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
|
||||
#ifndef PCSX2_CORE
|
||||
#include "gui/AppCoreThread.h"
|
||||
|
@ -63,6 +66,9 @@ static constexpr std::array<PresentShader, 6> s_tv_shader_indices = {
|
|||
PresentShader::DIAGONAL_FILTER, PresentShader::TRIANGULAR_FILTER,
|
||||
PresentShader::COMPLEX_FILTER, PresentShader::LOTTES_FILTER};
|
||||
|
||||
static std::deque<std::thread> s_screenshot_threads;
|
||||
static std::mutex s_screenshot_threads_mutex;
|
||||
|
||||
std::unique_ptr<GSRenderer> g_gs_renderer;
|
||||
|
||||
GSRenderer::GSRenderer()
|
||||
|
@ -570,7 +576,8 @@ static void CompressAndWriteScreenshot(std::string filename, u32 width, u32 heig
|
|||
Host::AddIconOSDMessage(key, ICON_FA_CAMERA, fmt::format("Saving screenshot to '{}'.", Path::GetFileName(filename)), 60.0f);
|
||||
|
||||
// maybe std::async would be better here.. but it's definitely worth threading, large screenshots take a while to compress.
|
||||
std::thread compress_thread([key = std::move(key), filename = std::move(filename), image = std::move(image), quality = GSConfig.ScreenshotQuality]() {
|
||||
std::unique_lock lock(s_screenshot_threads_mutex);
|
||||
s_screenshot_threads.emplace_back([key = std::move(key), filename = std::move(filename), image = std::move(image), quality = GSConfig.ScreenshotQuality]() {
|
||||
if (image.SaveToFile(filename.c_str(), quality))
|
||||
{
|
||||
Host::AddIconOSDMessage(std::move(key), ICON_FA_CAMERA,
|
||||
|
@ -581,8 +588,33 @@ static void CompressAndWriteScreenshot(std::string filename, u32 width, u32 heig
|
|||
Host::AddIconOSDMessage(std::move(key), ICON_FA_CAMERA,
|
||||
fmt::format("Failed to save screenshot to '{}'.", Path::GetFileName(filename), Host::OSD_ERROR_DURATION));
|
||||
}
|
||||
|
||||
// remove ourselves from the list, if the GS thread is waiting for us, we won't be in there
|
||||
const auto this_id = std::this_thread::get_id();
|
||||
std::unique_lock lock(s_screenshot_threads_mutex);
|
||||
for (auto it = s_screenshot_threads.begin(); it != s_screenshot_threads.end(); ++it)
|
||||
{
|
||||
if (it->get_id() == this_id)
|
||||
{
|
||||
it->detach();
|
||||
s_screenshot_threads.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
compress_thread.detach();
|
||||
}
|
||||
|
||||
void GSJoinSnapshotThreads()
|
||||
{
|
||||
std::unique_lock lock(s_screenshot_threads_mutex);
|
||||
while (!s_screenshot_threads.empty())
|
||||
{
|
||||
std::thread save_thread(std::move(s_screenshot_threads.front()));
|
||||
s_screenshot_threads.pop_front();
|
||||
lock.unlock();
|
||||
save_thread.join();
|
||||
lock.lock();
|
||||
}
|
||||
}
|
||||
|
||||
void GSRenderer::VSync(u32 field, bool registers_written)
|
||||
|
|
|
@ -58,6 +58,7 @@ GSDevice11::GSDevice11()
|
|||
m_features.framebuffer_fetch = false;
|
||||
m_features.dual_source_blend = true;
|
||||
m_features.stencil_buffer = true;
|
||||
m_features.clip_control = true;
|
||||
}
|
||||
|
||||
GSDevice11::~GSDevice11()
|
||||
|
@ -871,7 +872,7 @@ void GSDevice11::DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float para
|
|||
|
||||
m_ctx->UpdateSubresource(m_shadeboost.cb.get(), 0, nullptr, params, 0, 0);
|
||||
|
||||
StretchRect(sTex, sRect, dTex, dRect, m_shadeboost.ps.get(), m_shadeboost.cb.get(), true);
|
||||
StretchRect(sTex, sRect, dTex, dRect, m_shadeboost.ps.get(), m_shadeboost.cb.get(), false);
|
||||
}
|
||||
|
||||
bool GSDevice11::CreateCASShaders()
|
||||
|
|
|
@ -770,6 +770,7 @@ void GSDevice12::DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float para
|
|||
const GSVector4i dRect(0, 0, dTex->GetWidth(), dTex->GetHeight());
|
||||
EndRenderPass();
|
||||
OMSetRenderTargets(dTex, nullptr, dRect);
|
||||
SetUtilityRootSignature();
|
||||
SetUtilityTexture(sTex, m_point_sampler_cpu);
|
||||
BeginRenderPass(D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_DISCARD, D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE,
|
||||
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_NO_ACCESS, D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS);
|
||||
|
@ -787,6 +788,7 @@ void GSDevice12::DoFXAA(GSTexture* sTex, GSTexture* dTex)
|
|||
const GSVector4i dRect(0, 0, dTex->GetWidth(), dTex->GetHeight());
|
||||
EndRenderPass();
|
||||
OMSetRenderTargets(dTex, nullptr, dRect);
|
||||
SetUtilityRootSignature();
|
||||
SetUtilityTexture(sTex, m_linear_sampler_cpu);
|
||||
BeginRenderPass(D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_DISCARD, D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE,
|
||||
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_NO_ACCESS, D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS);
|
||||
|
@ -1852,7 +1854,7 @@ void GSDevice12::ExecuteCommandList(bool wait_for_completion, const char* reason
|
|||
|
||||
void GSDevice12::ExecuteCommandListAndRestartRenderPass(bool wait_for_completion, const char* reason)
|
||||
{
|
||||
Console.Warning("Vulkan: Executing command buffer due to '%s'", reason);
|
||||
Console.Warning("D3D12: Executing command buffer due to '%s'", reason);
|
||||
|
||||
const bool was_in_render_pass = m_in_render_pass;
|
||||
EndRenderPass();
|
||||
|
|
|
@ -2179,7 +2179,7 @@ void GSRendererHW::EmulateZbuffer()
|
|||
}
|
||||
else if (!m_context->ZBUF.ZMSK)
|
||||
{
|
||||
m_conf.cb_ps.TA_MaxDepth_Af.z = static_cast<float>(max_z) * 0x1p-32f;
|
||||
m_conf.cb_ps.TA_MaxDepth_Af.z = static_cast<float>(max_z) * (g_gs_device->Features().clip_control ? 0x1p-32f : 0x1p-24f);
|
||||
m_conf.ps.zclamp = 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,28 +16,8 @@
|
|||
#include "PrecompiledHeader.h"
|
||||
#include "GLLoader.h"
|
||||
#include "GS/GS.h"
|
||||
#include <unordered_set>
|
||||
#include "Host.h"
|
||||
|
||||
namespace GLExtension
|
||||
{
|
||||
|
||||
static std::unordered_set<std::string> s_extensions;
|
||||
|
||||
bool Has(const std::string& ext)
|
||||
{
|
||||
return !!s_extensions.count(ext);
|
||||
}
|
||||
|
||||
void Set(const std::string& ext, bool v)
|
||||
{
|
||||
if (v)
|
||||
s_extensions.insert(ext);
|
||||
else
|
||||
s_extensions.erase(ext);
|
||||
}
|
||||
} // namespace GLExtension
|
||||
|
||||
namespace ReplaceGL
|
||||
{
|
||||
void APIENTRY ScissorIndexed(GLuint index, GLint left, GLint bottom, GLsizei width, GLsizei height)
|
||||
|
@ -56,7 +36,6 @@ namespace ReplaceGL
|
|||
|
||||
} // namespace ReplaceGL
|
||||
|
||||
#ifdef _WIN32
|
||||
namespace Emulate_DSA
|
||||
{
|
||||
// Texture entry point
|
||||
|
@ -108,12 +87,6 @@ namespace Emulate_DSA
|
|||
}
|
||||
|
||||
// Misc entry point
|
||||
// (only purpose is to have a consistent API otherwise it is useless)
|
||||
void APIENTRY CreateProgramPipelines(GLsizei n, GLuint* pipelines)
|
||||
{
|
||||
glGenProgramPipelines(n, pipelines);
|
||||
}
|
||||
|
||||
void APIENTRY CreateSamplers(GLsizei n, GLuint* samplers)
|
||||
{
|
||||
glGenSamplers(n, samplers);
|
||||
|
@ -130,12 +103,10 @@ namespace Emulate_DSA
|
|||
glCompressedTextureSubImage2D = CompressedTextureSubImage;
|
||||
glGetTextureImage = GetTexureImage;
|
||||
glTextureParameteri = TextureParameteri;
|
||||
|
||||
glCreateProgramPipelines = CreateProgramPipelines;
|
||||
glGenerateTextureMipmap = GenerateTextureMipmap;
|
||||
glCreateSamplers = CreateSamplers;
|
||||
}
|
||||
} // namespace Emulate_DSA
|
||||
#endif
|
||||
|
||||
namespace GLLoader
|
||||
{
|
||||
|
@ -143,53 +114,18 @@ namespace GLLoader
|
|||
bool vendor_id_nvidia = false;
|
||||
bool vendor_id_intel = false;
|
||||
bool mesa_driver = false;
|
||||
bool in_replayer = false;
|
||||
bool buggy_pbo = false;
|
||||
|
||||
bool is_gles = false;
|
||||
bool has_dual_source_blend = false;
|
||||
bool has_clip_control = true;
|
||||
bool found_framebuffer_fetch = false;
|
||||
bool found_geometry_shader = true; // we require GL3.3 so geometry must be supported by default
|
||||
// DX11 GPU
|
||||
bool found_GL_ARB_gpu_shader5 = false; // Require IvyBridge
|
||||
bool found_GL_ARB_gpu_shader5 = false; // Require IvyBridge
|
||||
bool found_GL_ARB_texture_barrier = false;
|
||||
|
||||
static bool mandatory(const std::string& ext)
|
||||
{
|
||||
if (!GLExtension::Has(ext))
|
||||
{
|
||||
Host::ReportFormattedErrorAsync("GS", "ERROR: %s is NOT SUPPORTED\n", ext.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool optional(const std::string& name)
|
||||
{
|
||||
bool found = GLExtension::Has(name);
|
||||
|
||||
if (!found)
|
||||
{
|
||||
DevCon.Warning("INFO: %s is NOT SUPPORTED", name.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
DevCon.WriteLn("INFO: %s is available", name.c_str());
|
||||
}
|
||||
|
||||
std::string opt("override_");
|
||||
opt += name;
|
||||
|
||||
if (theApp.GetConfigI(opt.c_str()) != -1)
|
||||
{
|
||||
found = theApp.GetConfigB(opt.c_str());
|
||||
fprintf(stderr, "Override %s detection (%s)\n", name.c_str(), found ? "Enabled" : "Disabled");
|
||||
GLExtension::Set(name, found);
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
bool check_gl_version(int major, int minor)
|
||||
static bool check_gl_version()
|
||||
{
|
||||
const char* vendor = (const char*)glGetString(GL_VENDOR);
|
||||
if (strstr(vendor, "Advanced Micro Devices") || strstr(vendor, "ATI Technologies Inc.") || strstr(vendor, "ATI"))
|
||||
|
@ -209,103 +145,97 @@ namespace GLLoader
|
|||
{
|
||||
found_geometry_shader = GSConfig.OverrideGeometryShaders != 0 &&
|
||||
(GLAD_GL_VERSION_3_2 || GL_ARB_geometry_shader4 || GSConfig.OverrideGeometryShaders == 1);
|
||||
GLExtension::Set("GL_ARB_geometry_shader4", found_geometry_shader);
|
||||
fprintf(stderr, "Overriding geometry shaders detection\n");
|
||||
Console.Warning("Overriding geometry shaders detection to %s", found_geometry_shader ? "true" : "false");
|
||||
}
|
||||
|
||||
GLint major_gl = 0;
|
||||
GLint minor_gl = 0;
|
||||
glGetIntegerv(GL_MAJOR_VERSION, &major_gl);
|
||||
glGetIntegerv(GL_MINOR_VERSION, &minor_gl);
|
||||
if ((major_gl < major) || (major_gl == major && minor_gl < minor))
|
||||
if (!GLAD_GL_VERSION_3_3 && !GLAD_GL_ES_VERSION_3_1)
|
||||
{
|
||||
Host::ReportFormattedErrorAsync("GS", "OpenGL %d.%d is not supported. Only OpenGL %d.%d\n was found", major, minor, major_gl, minor_gl);
|
||||
Host::ReportFormattedErrorAsync("GS", "OpenGL is not supported. Only OpenGL %d.%d\n was found", major_gl, minor_gl);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool check_gl_supported_extension()
|
||||
static bool check_gl_supported_extension()
|
||||
{
|
||||
int max_ext = 0;
|
||||
glGetIntegerv(GL_NUM_EXTENSIONS, &max_ext);
|
||||
for (GLint i = 0; i < max_ext; i++)
|
||||
if (GLAD_GL_VERSION_3_3 && !GLAD_GL_ARB_shading_language_420pack)
|
||||
{
|
||||
std::string ext{(const char*)glGetStringi(GL_EXTENSIONS, i)};
|
||||
GLExtension::Set(ext);
|
||||
//fprintf(stderr, "DEBUG ext: %s\n", ext.c_str());
|
||||
}
|
||||
|
||||
// Mandatory for both renderer
|
||||
bool ok = true;
|
||||
{
|
||||
// GL4.1
|
||||
ok = ok && mandatory("GL_ARB_separate_shader_objects");
|
||||
// GL4.2
|
||||
ok = ok && mandatory("GL_ARB_shading_language_420pack");
|
||||
ok = ok && mandatory("GL_ARB_texture_storage");
|
||||
// GL4.3
|
||||
ok = ok && mandatory("GL_KHR_debug");
|
||||
// GL4.4
|
||||
ok = ok && mandatory("GL_ARB_buffer_storage");
|
||||
}
|
||||
|
||||
// Only for HW renderer
|
||||
if (GSConfig.UseHardwareRenderer())
|
||||
{
|
||||
ok = ok && mandatory("GL_ARB_copy_image");
|
||||
ok = ok && mandatory("GL_ARB_clip_control");
|
||||
}
|
||||
if (!ok)
|
||||
Host::ReportFormattedErrorAsync("GS",
|
||||
"GL_ARB_shading_language_420pack is not supported, this is required for the OpenGL renderer.");
|
||||
return false;
|
||||
|
||||
// Extra
|
||||
{
|
||||
// GL4.0
|
||||
found_GL_ARB_gpu_shader5 = optional("GL_ARB_gpu_shader5");
|
||||
// GL4.5
|
||||
optional("GL_ARB_direct_state_access");
|
||||
// Mandatory for the advance HW renderer effect. Unfortunately Mesa LLVMPIPE/SWR renderers doesn't support this extension.
|
||||
// Rendering might be corrupted but it could be good enough for test/virtual machine.
|
||||
found_GL_ARB_texture_barrier = optional("GL_ARB_texture_barrier");
|
||||
|
||||
has_dual_source_blend = GLAD_GL_VERSION_3_2 || GLAD_GL_ARB_blend_func_extended;
|
||||
found_framebuffer_fetch = GLAD_GL_EXT_shader_framebuffer_fetch || GLAD_GL_ARM_shader_framebuffer_fetch;
|
||||
if (found_framebuffer_fetch && GSConfig.DisableFramebufferFetch)
|
||||
{
|
||||
Console.Warning("Framebuffer fetch was found but is disabled. This will reduce performance.");
|
||||
found_framebuffer_fetch = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!GLExtension::Has("GL_ARB_viewport_array"))
|
||||
// GLES doesn't have ARB_clip_control.
|
||||
has_clip_control = GLAD_GL_ARB_clip_control;
|
||||
if (!has_clip_control && !is_gles)
|
||||
{
|
||||
Host::AddOSDMessage("GL_ARB_clip_control is not supported, this will cause rendering issues.",
|
||||
Host::OSD_ERROR_DURATION);
|
||||
}
|
||||
|
||||
found_GL_ARB_gpu_shader5 = GLAD_GL_ARB_gpu_shader5;
|
||||
found_GL_ARB_texture_barrier = GLAD_GL_ARB_texture_barrier;
|
||||
|
||||
has_dual_source_blend = GLAD_GL_VERSION_3_2 || GLAD_GL_ARB_blend_func_extended;
|
||||
found_framebuffer_fetch = GLAD_GL_EXT_shader_framebuffer_fetch || GLAD_GL_ARM_shader_framebuffer_fetch;
|
||||
if (found_framebuffer_fetch && GSConfig.DisableFramebufferFetch)
|
||||
{
|
||||
Console.Warning("Framebuffer fetch was found but is disabled. This will reduce performance.");
|
||||
found_framebuffer_fetch = false;
|
||||
}
|
||||
|
||||
if (!GLAD_GL_ARB_viewport_array)
|
||||
{
|
||||
glScissorIndexed = ReplaceGL::ScissorIndexed;
|
||||
glViewportIndexedf = ReplaceGL::ViewportIndexedf;
|
||||
Console.Warning("GL_ARB_viewport_array is not supported! Function pointer will be replaced");
|
||||
}
|
||||
|
||||
if (!GLExtension::Has("GL_ARB_texture_barrier"))
|
||||
if (!GLAD_GL_ARB_texture_barrier)
|
||||
{
|
||||
glTextureBarrier = ReplaceGL::TextureBarrier;
|
||||
Console.Warning("GL_ARB_texture_barrier is not supported! Blending emulation will not be supported");
|
||||
Host::AddOSDMessage("GL_ARB_texture_barrier is not supported, blending will not be accurate.",
|
||||
Host::OSD_ERROR_DURATION);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// Thank you Intel for not providing support of basic features on your IGPUs.
|
||||
if (!GLExtension::Has("GL_ARB_direct_state_access"))
|
||||
if (!GLAD_GL_ARB_direct_state_access)
|
||||
{
|
||||
Console.Warning("GL_ARB_direct_state_access is not supported, this will reduce performance.");
|
||||
Emulate_DSA::Init();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (is_gles)
|
||||
{
|
||||
has_dual_source_blend = GLAD_GL_EXT_blend_func_extended || GLAD_GL_ARB_blend_func_extended;
|
||||
if (!has_dual_source_blend && !found_framebuffer_fetch)
|
||||
{
|
||||
Host::AddOSDMessage("Both dual source blending and framebuffer fetch are missing, things will be broken.",
|
||||
Host::OSD_ERROR_DURATION);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Core in GL3.2, so everything supports it.
|
||||
has_dual_source_blend = true;
|
||||
}
|
||||
|
||||
// Don't use PBOs when we don't have ARB_buffer_storage, orphaning buffers probably ends up worse than just
|
||||
// using the normal texture update routines and letting the driver take care of it.
|
||||
GLLoader::buggy_pbo = !GLAD_GL_VERSION_4_4 && !GLAD_GL_ARB_buffer_storage && !GLAD_GL_EXT_buffer_storage;
|
||||
if (GLLoader::buggy_pbo)
|
||||
Console.Warning("Not using PBOs for texture uploads because buffer_storage is unavailable.");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool check_gl_requirements()
|
||||
{
|
||||
if (!check_gl_version(3, 3))
|
||||
if (!check_gl_version())
|
||||
return false;
|
||||
|
||||
if (!check_gl_supported_extension())
|
||||
|
|
|
@ -17,17 +17,11 @@
|
|||
|
||||
#define GL_TEX_LEVEL_0 (0)
|
||||
#define GL_TEX_LEVEL_1 (1)
|
||||
#define GL_FB_DEFAULT (0)
|
||||
#define GL_BUFFER_0 (0)
|
||||
#define GL_FB_DEFAULT (0)
|
||||
#define GL_BUFFER_0 (0)
|
||||
|
||||
#include "glad.h"
|
||||
|
||||
namespace GLExtension
|
||||
{
|
||||
extern bool Has(const std::string& ext);
|
||||
extern void Set(const std::string& ext, bool v = true);
|
||||
} // namespace GLExtension
|
||||
|
||||
namespace GLLoader
|
||||
{
|
||||
bool check_gl_requirements();
|
||||
|
@ -36,9 +30,12 @@ namespace GLLoader
|
|||
extern bool vendor_id_nvidia;
|
||||
extern bool vendor_id_intel;
|
||||
extern bool mesa_driver;
|
||||
extern bool buggy_pbo;
|
||||
extern bool in_replayer;
|
||||
|
||||
// GL
|
||||
extern bool is_gles;
|
||||
extern bool has_clip_control;
|
||||
extern bool has_dual_source_blend;
|
||||
extern bool found_framebuffer_fetch;
|
||||
extern bool found_geometry_shader;
|
||||
|
|
|
@ -23,6 +23,7 @@ namespace GLState
|
|||
GSVector4i scissor;
|
||||
|
||||
bool point_size = false;
|
||||
float line_width = 1.0f;
|
||||
|
||||
bool blend;
|
||||
u16 eq_RGB;
|
||||
|
|
|
@ -25,6 +25,7 @@ namespace GLState
|
|||
extern GSVector4i scissor;
|
||||
|
||||
extern bool point_size;
|
||||
extern float line_width;
|
||||
|
||||
extern bool blend;
|
||||
extern u16 eq_RGB;
|
||||
|
|
|
@ -30,13 +30,6 @@
|
|||
|
||||
//#define ONLY_LINES
|
||||
|
||||
// TODO port those value into PerfMon API
|
||||
#ifdef ENABLE_OGL_DEBUG_MEM_BW
|
||||
u64 g_real_texture_upload_byte = 0;
|
||||
u64 g_vertex_upload_byte = 0;
|
||||
u64 g_uniform_upload_byte = 0;
|
||||
#endif
|
||||
|
||||
static constexpr u32 g_vs_cb_index = 1;
|
||||
static constexpr u32 g_ps_cb_index = 0;
|
||||
|
||||
|
@ -44,33 +37,15 @@ static constexpr u32 VERTEX_BUFFER_SIZE = 32 * 1024 * 1024;
|
|||
static constexpr u32 INDEX_BUFFER_SIZE = 16 * 1024 * 1024;
|
||||
static constexpr u32 VERTEX_UNIFORM_BUFFER_SIZE = 8 * 1024 * 1024;
|
||||
static constexpr u32 FRAGMENT_UNIFORM_BUFFER_SIZE = 8 * 1024 * 1024;
|
||||
static constexpr u32 TEXTURE_UPLOAD_BUFFER_SIZE = 128 * 1024 * 1024;
|
||||
|
||||
int GSDeviceOGL::m_shader_inst = 0;
|
||||
int GSDeviceOGL::m_shader_reg = 0;
|
||||
FILE* GSDeviceOGL::m_debug_gl_file = NULL;
|
||||
static std::unique_ptr<GL::StreamBuffer> s_texture_upload_buffer;
|
||||
|
||||
GSDeviceOGL::GSDeviceOGL()
|
||||
: m_fbo(0)
|
||||
, m_fbo_read(0)
|
||||
, m_palette_ss(0)
|
||||
{
|
||||
// Reset the debug file
|
||||
#ifdef ENABLE_OGL_DEBUG
|
||||
m_debug_gl_file = fopen("GS_opengl_debug.txt", "w");
|
||||
#endif
|
||||
}
|
||||
GSDeviceOGL::GSDeviceOGL() = default;
|
||||
|
||||
GSDeviceOGL::~GSDeviceOGL()
|
||||
{
|
||||
#ifdef ENABLE_OGL_DEBUG
|
||||
if (m_debug_gl_file)
|
||||
{
|
||||
fclose(m_debug_gl_file);
|
||||
m_debug_gl_file = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Clean vertex buffer state
|
||||
s_texture_upload_buffer.reset();
|
||||
if (m_vertex_array_object)
|
||||
glDeleteVertexArrays(1, &m_vertex_array_object);
|
||||
m_vertex_stream_buffer.reset();
|
||||
|
@ -86,6 +61,7 @@ GSDeviceOGL::~GSDeviceOGL()
|
|||
// Clean various opengl allocation
|
||||
glDeleteFramebuffers(1, &m_fbo);
|
||||
glDeleteFramebuffers(1, &m_fbo_read);
|
||||
glDeleteFramebuffers(1, &m_fbo_write);
|
||||
|
||||
// Delete HW FX
|
||||
m_vertex_uniform_stream_buffer.reset();
|
||||
|
@ -98,91 +74,6 @@ GSDeviceOGL::~GSDeviceOGL()
|
|||
|
||||
for (GSDepthStencilOGL* ds : m_om_dss)
|
||||
delete ds;
|
||||
|
||||
PboPool::Destroy();
|
||||
}
|
||||
|
||||
void GSDeviceOGL::GenerateProfilerData()
|
||||
{
|
||||
if (m_profiler.last_query < 3)
|
||||
{
|
||||
glDeleteQueries(1 << 16, m_profiler.timer_query);
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait latest quey to get valid result
|
||||
GLuint available = 0;
|
||||
while (!available)
|
||||
{
|
||||
glGetQueryObjectuiv(m_profiler.timer(), GL_QUERY_RESULT_AVAILABLE, &available);
|
||||
}
|
||||
|
||||
GLuint64 time_start = 0;
|
||||
GLuint64 time_end = 0;
|
||||
std::vector<double> times;
|
||||
constexpr double ms = 0.000001;
|
||||
|
||||
const int replay = theApp.GetConfigI("linux_replay");
|
||||
const int first_query = replay > 1 ? m_profiler.last_query / replay : 0;
|
||||
|
||||
glGetQueryObjectui64v(m_profiler.timer_query[first_query], GL_QUERY_RESULT, &time_start);
|
||||
for (u32 q = first_query + 1; q < m_profiler.last_query; q++)
|
||||
{
|
||||
glGetQueryObjectui64v(m_profiler.timer_query[q], GL_QUERY_RESULT, &time_end);
|
||||
u64 t = time_end - time_start;
|
||||
times.push_back((double)t * ms);
|
||||
|
||||
time_start = time_end;
|
||||
}
|
||||
|
||||
// Latest value is often silly, just drop it
|
||||
times.pop_back();
|
||||
|
||||
glDeleteQueries(1 << 16, m_profiler.timer_query);
|
||||
|
||||
const double frames = times.size();
|
||||
double mean = 0.0;
|
||||
double sd = 0.0;
|
||||
|
||||
auto minmax_time = std::minmax_element(times.begin(), times.end());
|
||||
|
||||
for (auto t : times)
|
||||
mean += t;
|
||||
mean = mean / frames;
|
||||
|
||||
for (auto t : times)
|
||||
sd += pow(t - mean, 2);
|
||||
sd = sqrt(sd / frames);
|
||||
|
||||
u32 time_repartition[16] = {0};
|
||||
for (auto t : times)
|
||||
{
|
||||
size_t slot = std::min<size_t>(t / 2.0, std::size(time_repartition) - 1);
|
||||
time_repartition[slot]++;
|
||||
}
|
||||
|
||||
fprintf(stderr, "\nPerformance Profile for %.0f frames:\n", frames);
|
||||
fprintf(stderr, "Min %4.2f ms\t(%4.2f fps)\n", *minmax_time.first, 1000.0 / *minmax_time.first);
|
||||
fprintf(stderr, "Mean %4.2f ms\t(%4.2f fps)\n", mean, 1000.0 / mean);
|
||||
fprintf(stderr, "Max %4.2f ms\t(%4.2f fps)\n", *minmax_time.second, 1000.0 / *minmax_time.second);
|
||||
fprintf(stderr, "SD %4.2f ms\n", sd);
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "Frame Repartition\n");
|
||||
for (u32 i = 0; i < std::size(time_repartition); i++)
|
||||
{
|
||||
fprintf(stderr, "%3u ms => %3u ms\t%4u\n", 2 * i, 2 * (i + 1), time_repartition[i]);
|
||||
}
|
||||
|
||||
FILE* csv = fopen("GS_profile.csv", "w");
|
||||
if (csv)
|
||||
{
|
||||
for (size_t i = 0; i < times.size(); i++)
|
||||
{
|
||||
fprintf(csv, "%zu,%lf\n", i, times[i]);
|
||||
}
|
||||
|
||||
fclose(csv);
|
||||
}
|
||||
}
|
||||
|
||||
GSTexture* GSDeviceOGL::CreateSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format)
|
||||
|
@ -196,11 +87,13 @@ bool GSDeviceOGL::Create()
|
|||
if (!GSDevice::Create())
|
||||
return false;
|
||||
|
||||
if (g_host_display->GetRenderAPI() != RenderAPI::OpenGL)
|
||||
const RenderAPI render_api = g_host_display->GetRenderAPI();
|
||||
if (render_api != RenderAPI::OpenGL && render_api != RenderAPI::OpenGLES)
|
||||
return false;
|
||||
|
||||
// Check openGL requirement as soon as possible so we can switch to another
|
||||
// renderer/device
|
||||
GLLoader::is_gles = (render_api == RenderAPI::OpenGLES);
|
||||
if (!GLLoader::check_gl_requirements())
|
||||
return false;
|
||||
|
||||
|
@ -227,18 +120,29 @@ bool GSDeviceOGL::Create()
|
|||
m_features.provoking_vertex_last = true;
|
||||
m_features.dxt_textures = GLAD_GL_EXT_texture_compression_s3tc;
|
||||
m_features.bptc_textures = GLAD_GL_VERSION_4_2 || GLAD_GL_ARB_texture_compression_bptc || GLAD_GL_EXT_texture_compression_bptc;
|
||||
m_features.prefer_new_textures = false;
|
||||
m_features.prefer_new_textures = GLLoader::is_gles;
|
||||
m_features.framebuffer_fetch = GLLoader::found_framebuffer_fetch;
|
||||
m_features.dual_source_blend = GLLoader::has_dual_source_blend && !GSConfig.DisableDualSourceBlend;
|
||||
m_features.clip_control = GLLoader::has_clip_control;
|
||||
m_features.stencil_buffer = true;
|
||||
// Wide line support in GL is deprecated as of 3.1, so we will just do it in the Geometry Shader.
|
||||
m_features.line_expand = false;
|
||||
|
||||
GLint point_range[2] = {};
|
||||
glGetIntegerv(GL_ALIASED_POINT_SIZE_RANGE, point_range);
|
||||
m_features.point_expand = (point_range[0] <= GSConfig.UpscaleMultiplier && point_range[1] >= GSConfig.UpscaleMultiplier);
|
||||
|
||||
Console.WriteLn("Using %s for point expansion.", m_features.point_expand ? "hardware" : "geometry shaders");
|
||||
if (GLLoader::is_gles)
|
||||
{
|
||||
GLint line_range[2] = {};
|
||||
glGetIntegerv(GL_ALIASED_LINE_WIDTH_RANGE, line_range);
|
||||
m_features.line_expand = (line_range[0] <= static_cast<GLint>(GSConfig.UpscaleMultiplier) && line_range[1] >= static_cast<GLint>(GSConfig.UpscaleMultiplier));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_features.line_expand = false;
|
||||
}
|
||||
|
||||
Console.WriteLn("Using %s for point expansion and %s for line expansion.",
|
||||
m_features.point_expand ? "hardware" : "geometry shaders", m_features.line_expand ? "hardware" : "geometry shaders");
|
||||
|
||||
{
|
||||
auto shader = Host::ReadResourceFileToString("shaders/opengl/common_header.glsl");
|
||||
|
@ -257,18 +161,27 @@ bool GSDeviceOGL::Create()
|
|||
// ****************************************************************
|
||||
// Debug helper
|
||||
// ****************************************************************
|
||||
#ifdef ENABLE_OGL_DEBUG
|
||||
if (GSConfig.UseDebugDevice)
|
||||
{
|
||||
glDebugMessageCallback((GLDEBUGPROC)DebugOutputToFile, NULL);
|
||||
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
|
||||
if (!GLLoader::is_gles)
|
||||
{
|
||||
glDebugMessageCallback((GLDEBUGPROC)DebugOutputToFile, NULL);
|
||||
|
||||
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, true);
|
||||
// Useless info message on Nvidia driver
|
||||
GLuint ids[] = {0x20004};
|
||||
glDebugMessageControl(GL_DEBUG_SOURCE_API_ARB, GL_DEBUG_TYPE_OTHER_ARB, GL_DONT_CARE, std::size(ids), ids, false);
|
||||
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, true);
|
||||
// Useless info message on Nvidia driver
|
||||
GLuint ids[] = { 0x20004 };
|
||||
glDebugMessageControl(GL_DEBUG_SOURCE_API_ARB, GL_DEBUG_TYPE_OTHER_ARB, GL_DONT_CARE, std::size(ids), ids, false);
|
||||
}
|
||||
else if (GLAD_GL_KHR_debug)
|
||||
{
|
||||
glDebugMessageCallbackKHR((GLDEBUGPROC)DebugOutputToFile, NULL);
|
||||
glDebugMessageControlKHR(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, true);
|
||||
}
|
||||
|
||||
// Uncomment synchronous if you want callstacks which match where the error occurred.
|
||||
glEnable(GL_DEBUG_OUTPUT);
|
||||
//glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
|
||||
}
|
||||
#endif
|
||||
|
||||
// WARNING it must be done after the control setup (at least on MESA)
|
||||
GL_PUSH("GSDeviceOGL::Create");
|
||||
|
@ -287,16 +200,11 @@ bool GSDeviceOGL::Create()
|
|||
OMSetFBO(0);
|
||||
|
||||
glGenFramebuffers(1, &m_fbo_read);
|
||||
glGenFramebuffers(1, &m_fbo_write);
|
||||
// Always read from the first buffer
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo_read);
|
||||
glReadBuffer(GL_COLOR_ATTACHMENT0);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
|
||||
// Some timers to help profiling
|
||||
if (GLLoader::in_replayer)
|
||||
{
|
||||
glCreateQueries(GL_TIMESTAMP, 1 << 16, m_profiler.timer_query);
|
||||
}
|
||||
}
|
||||
|
||||
// ****************************************************************
|
||||
|
@ -379,7 +287,7 @@ bool GSDeviceOGL::Create()
|
|||
{
|
||||
const char* name = shaderName(static_cast<ShaderConvert>(i));
|
||||
const std::string macro_sel = (static_cast<ShaderConvert>(i) == ShaderConvert::RGBA_TO_8I) ?
|
||||
fmt::format("#define PS_SCALE_FACTOR {}\n", GSConfig.UpscaleMultiplier) :
|
||||
fmt::format("#define PS_SCALE_FACTOR {:.8f}f\n", GSConfig.UpscaleMultiplier) :
|
||||
std::string();
|
||||
const std::string ps(GetShaderSource(name, GL_FRAGMENT_SHADER, m_shader_common_header, *convert_glsl, macro_sel));
|
||||
if (!m_shader_cache.GetProgram(&m_convert.ps[i], m_convert.vs, {}, ps))
|
||||
|
@ -518,15 +426,13 @@ bool GSDeviceOGL::Create()
|
|||
{
|
||||
GL_PUSH("GSDeviceOGL::Rasterization");
|
||||
|
||||
#ifdef ONLY_LINES
|
||||
glLineWidth(5.0);
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
||||
#else
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||||
#endif
|
||||
if (!GLLoader::is_gles)
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
glDisable(GL_MULTISAMPLE);
|
||||
if (!GLLoader::is_gles)
|
||||
glDisable(GL_MULTISAMPLE);
|
||||
|
||||
glDisable(GL_DITHER); // Honestly I don't know!
|
||||
}
|
||||
|
||||
|
@ -560,7 +466,7 @@ bool GSDeviceOGL::Create()
|
|||
// This extension allow FS depth to range from -1 to 1. So
|
||||
// gl_position.z could range from [0, 1]
|
||||
// Change depth convention
|
||||
if (GLExtension::Has("GL_ARB_clip_control"))
|
||||
if (GLLoader::has_clip_control)
|
||||
glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
|
||||
|
||||
// ****************************************************************
|
||||
|
@ -572,15 +478,19 @@ bool GSDeviceOGL::Create()
|
|||
// ****************************************************************
|
||||
// Pbo Pool allocation
|
||||
// ****************************************************************
|
||||
if (!GLLoader::buggy_pbo)
|
||||
{
|
||||
GL_PUSH("GSDeviceOGL::PBO");
|
||||
|
||||
// Mesa seems to use it to compute the row length. In our case, we are
|
||||
// tightly packed so don't bother with this parameter and set it to the
|
||||
// minimum alignment (1 byte)
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
|
||||
PboPool::Init();
|
||||
s_texture_upload_buffer = GL::StreamBuffer::Create(GL_PIXEL_UNPACK_BUFFER, TEXTURE_UPLOAD_BUFFER_SIZE);
|
||||
if (!s_texture_upload_buffer)
|
||||
{
|
||||
Console.Error("Failed to create texture upload buffer. Using slow path.");
|
||||
GLLoader::buggy_pbo = true;
|
||||
}
|
||||
}
|
||||
|
||||
// ****************************************************************
|
||||
|
@ -592,7 +502,7 @@ bool GSDeviceOGL::Create()
|
|||
// Full vram, remove a small margin for others buffer
|
||||
glGetIntegerv(GL_TEXTURE_FREE_MEMORY_ATI, vram);
|
||||
}
|
||||
else if (GLExtension::Has("GL_NVX_gpu_memory_info"))
|
||||
else if (GLAD_GL_NVX_gpu_memory_info)
|
||||
{
|
||||
// GL_GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX <= give full memory
|
||||
// Available vram
|
||||
|
@ -649,12 +559,6 @@ bool GSDeviceOGL::CreateTextureFX()
|
|||
m_om_dss[key] = CreateDepthStencil(OMDepthStencilSelector(key));
|
||||
}
|
||||
|
||||
if (GLLoader::in_replayer)
|
||||
{
|
||||
glQueryCounter(m_profiler.timer(), GL_TIMESTAMP);
|
||||
m_profiler.last_query++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -662,6 +566,12 @@ void GSDeviceOGL::ResetAPIState()
|
|||
{
|
||||
if (GLState::point_size)
|
||||
glDisable(GL_PROGRAM_POINT_SIZE);
|
||||
if (GLState::line_width != 1.0f)
|
||||
glLineWidth(1.0f);
|
||||
|
||||
// clear out DSB
|
||||
glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ONE, GL_ZERO);
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
void GSDeviceOGL::RestoreAPIState()
|
||||
|
@ -714,6 +624,8 @@ void GSDeviceOGL::RestoreAPIState()
|
|||
|
||||
if (GLState::point_size)
|
||||
glEnable(GL_PROGRAM_POINT_SIZE);
|
||||
if (GLState::line_width != 1.0f)
|
||||
glLineWidth(GLState::line_width);
|
||||
|
||||
// Force UBOs to be reuploaded, we don't know what else was bound there.
|
||||
std::memset(&m_vs_cb_cache, 0xFF, sizeof(m_vs_cb_cache));
|
||||
|
@ -770,7 +682,17 @@ void GSDeviceOGL::ClearRenderTarget(GSTexture* t, const GSVector4& c)
|
|||
OMSetFBO(m_fbo);
|
||||
OMAttachRt(T);
|
||||
|
||||
glClearBufferfv(GL_COLOR, 0, c.v);
|
||||
if (T->IsIntegerFormat())
|
||||
{
|
||||
if (T->IsUnsignedFormat())
|
||||
glClearBufferuiv(GL_COLOR, 0, c.U32);
|
||||
else
|
||||
glClearBufferiv(GL_COLOR, 0, c.I32);
|
||||
}
|
||||
else
|
||||
{
|
||||
glClearBufferfv(GL_COLOR, 0, c.v);
|
||||
}
|
||||
|
||||
OMSetColorMaskState(OMColorMaskSelector(old_color_mask));
|
||||
|
||||
|
@ -899,10 +821,10 @@ GLuint GSDeviceOGL::CreateSampler(PSSamplerSelector sel)
|
|||
const int anisotropy = GSConfig.MaxAnisotropy;
|
||||
if (anisotropy > 1 && sel.aniso)
|
||||
{
|
||||
if (GLExtension::Has("GL_ARB_texture_filter_anisotropic"))
|
||||
glSamplerParameterf(sampler, GL_TEXTURE_MAX_ANISOTROPY, (float)anisotropy);
|
||||
else if (GLExtension::Has("GL_EXT_texture_filter_anisotropic"))
|
||||
glSamplerParameterf(sampler, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)anisotropy);
|
||||
if (GLAD_GL_ARB_texture_filter_anisotropic)
|
||||
glSamplerParameterf(sampler, GL_TEXTURE_MAX_ANISOTROPY, static_cast<float>(anisotropy));
|
||||
else if (GLAD_GL_EXT_texture_filter_anisotropic)
|
||||
glSamplerParameterf(sampler, GL_TEXTURE_MAX_ANISOTROPY_EXT, static_cast<float>(anisotropy));
|
||||
}
|
||||
|
||||
return sampler;
|
||||
|
@ -963,28 +885,64 @@ std::string GSDeviceOGL::GetShaderSource(const std::string_view& entry, GLenum t
|
|||
|
||||
std::string GSDeviceOGL::GenGlslHeader(const std::string_view& entry, GLenum type, const std::string_view& macro)
|
||||
{
|
||||
std::string header = "#version 330 core\n";
|
||||
std::string header;
|
||||
|
||||
// Need GL version 420
|
||||
header += "#extension GL_ARB_shading_language_420pack: require\n";
|
||||
// Need GL version 410
|
||||
header += "#extension GL_ARB_separate_shader_objects: require\n";
|
||||
if (m_features.framebuffer_fetch)
|
||||
if (GLLoader::is_gles)
|
||||
{
|
||||
if (GLAD_GL_EXT_shader_framebuffer_fetch)
|
||||
header += "#extension GL_EXT_shader_framebuffer_fetch : require\n";
|
||||
else if (GLAD_GL_ARM_shader_framebuffer_fetch)
|
||||
header += "#extension GL_ARM_shader_framebuffer_fetch : require\n";
|
||||
}
|
||||
if (GLAD_GL_ES_VERSION_3_2)
|
||||
header = "#version 320 es\n";
|
||||
else if (GLAD_GL_ES_VERSION_3_1)
|
||||
header = "#version 310 es\n";
|
||||
|
||||
if (GLLoader::found_GL_ARB_gpu_shader5)
|
||||
header += "#extension GL_ARB_gpu_shader5 : enable\n";
|
||||
if (GLAD_GL_EXT_blend_func_extended)
|
||||
header += "#extension GL_EXT_blend_func_extended : require\n";
|
||||
if (GLAD_GL_ARB_blend_func_extended)
|
||||
header += "#extension GL_ARB_blend_func_extended : require\n";
|
||||
if (m_features.framebuffer_fetch)
|
||||
{
|
||||
if (GLAD_GL_EXT_shader_framebuffer_fetch)
|
||||
header += "#extension GL_EXT_shader_framebuffer_fetch : require\n";
|
||||
else if (GLAD_GL_ARM_shader_framebuffer_fetch)
|
||||
header += "#extension GL_ARM_shader_framebuffer_fetch : require\n";
|
||||
}
|
||||
|
||||
header += "precision highp float;\n";
|
||||
header += "precision highp int;\n";
|
||||
header += "precision highp sampler2D;\n";
|
||||
if (GLAD_GL_ES_VERSION_3_1)
|
||||
header += "precision highp sampler2DMS;\n";
|
||||
if (GLAD_GL_ES_VERSION_3_2)
|
||||
header += "precision highp usamplerBuffer;\n";
|
||||
|
||||
if (!GLAD_GL_EXT_blend_func_extended && !GLAD_GL_ARB_blend_func_extended)
|
||||
header += "#define DISABLE_DUAL_SOURCE\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
header = "#version 330 core\n";
|
||||
|
||||
// Need GL version 420
|
||||
header += "#extension GL_ARB_shading_language_420pack: require\n";
|
||||
// Need GL version 410
|
||||
header += "#extension GL_ARB_separate_shader_objects: require\n";
|
||||
|
||||
if (m_features.framebuffer_fetch && GLAD_GL_EXT_shader_framebuffer_fetch)
|
||||
header += "#extension GL_EXT_shader_framebuffer_fetch : require\n";
|
||||
|
||||
if (GLLoader::found_GL_ARB_gpu_shader5)
|
||||
header += "#extension GL_ARB_gpu_shader5 : enable\n";
|
||||
}
|
||||
|
||||
if (m_features.framebuffer_fetch)
|
||||
header += "#define HAS_FRAMEBUFFER_FETCH 1\n";
|
||||
else
|
||||
header += "#define HAS_FRAMEBUFFER_FETCH 0\n";
|
||||
|
||||
if (GLLoader::has_clip_control)
|
||||
header += "#define HAS_CLIP_CONTROL 1\n";
|
||||
else
|
||||
header += "#define HAS_CLIP_CONTROL 0\n";
|
||||
|
||||
if (GLLoader::vendor_id_amd || GLLoader::vendor_id_intel)
|
||||
header += "#define BROKEN_DRIVER as_usual\n";
|
||||
|
||||
|
@ -992,7 +950,10 @@ std::string GSDeviceOGL::GenGlslHeader(const std::string_view& entry, GLenum typ
|
|||
// AMD/nvidia define it to 0
|
||||
// intel window don't define it
|
||||
// intel linux refuse to define it
|
||||
header += "#define pGL_ES 0\n";
|
||||
if (GLLoader::is_gles)
|
||||
header += "#define pGL_ES 1\n";
|
||||
else
|
||||
header += "#define pGL_ES 0\n";
|
||||
|
||||
// Allow to puts several shader in 1 files
|
||||
switch (type)
|
||||
|
@ -1030,7 +991,7 @@ std::string GSDeviceOGL::GetVSSource(VSSelector sel)
|
|||
+ fmt::format("#define VS_IIP {}\n", static_cast<u32>(sel.iip))
|
||||
+ fmt::format("#define VS_POINT_SIZE {}\n", static_cast<u32>(sel.point_size));
|
||||
if (sel.point_size)
|
||||
macro += fmt::format("#define VS_POINT_SIZE_VALUE {}\n", GSConfig.UpscaleMultiplier);
|
||||
macro += fmt::format("#define VS_POINT_SIZE_VALUE {:.8f}f\n", GSConfig.UpscaleMultiplier);
|
||||
|
||||
std::string src = GenGlslHeader("vs_main", GL_VERTEX_SHADER, macro);
|
||||
src += m_shader_common_header;
|
||||
|
@ -1102,7 +1063,7 @@ std::string GSDeviceOGL::GetPSSource(const PSSelector& sel)
|
|||
+ fmt::format("#define PS_FIXED_ONE_A {}\n", sel.fixed_one_a)
|
||||
+ fmt::format("#define PS_PABE {}\n", sel.pabe)
|
||||
+ fmt::format("#define PS_SCANMSK {}\n", sel.scanmsk)
|
||||
+ fmt::format("#define PS_SCALE_FACTOR {}\n", GSConfig.UpscaleMultiplier)
|
||||
+ fmt::format("#define PS_SCALE_FACTOR {:.8f}f\n", GSConfig.UpscaleMultiplier)
|
||||
+ fmt::format("#define PS_NO_COLOR {}\n", sel.no_color)
|
||||
+ fmt::format("#define PS_NO_COLOR1 {}\n", sel.no_color1)
|
||||
+ fmt::format("#define PS_NO_ABLEND {}\n", sel.no_ablend)
|
||||
|
@ -1169,12 +1130,36 @@ void GSDeviceOGL::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r
|
|||
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
|
||||
ASSERT(GLExtension::Has("GL_ARB_copy_image") && glCopyImageSubData);
|
||||
glCopyImageSubData(sid, GL_TEXTURE_2D,
|
||||
0, r.x, r.y, 0,
|
||||
did, GL_TEXTURE_2D,
|
||||
0, destX, destY, 0,
|
||||
r.width(), r.height(), 1);
|
||||
if (GLAD_GL_VERSION_4_3 || GLAD_GL_ARB_copy_image)
|
||||
{
|
||||
glCopyImageSubData(sid, GL_TEXTURE_2D, 0, r.x, r.y, 0, did, GL_TEXTURE_2D,
|
||||
0, destX, destY, 0, r.width(), r.height(), 1);
|
||||
}
|
||||
else if (GLAD_GL_EXT_copy_image)
|
||||
{
|
||||
glCopyImageSubDataEXT(sid, GL_TEXTURE_2D, 0, r.x, r.y, 0, did, GL_TEXTURE_2D,
|
||||
0, destX, destY, 0, r.width(), r.height(), 1);
|
||||
}
|
||||
else if (GLAD_GL_OES_copy_image)
|
||||
{
|
||||
glCopyImageSubDataOES(sid, GL_TEXTURE_2D, 0, r.x, r.y, 0, did, GL_TEXTURE_2D,
|
||||
0, destX, destY, 0, r.width(), r.height(), 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo_read);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fbo_write);
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, sid, 0);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, did, 0);
|
||||
|
||||
const int w = r.width(), h = r.height();
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
glBlitFramebuffer(r.x, r.y, r.x + w, r.y + h, destX + r.x, destY + r.y, destX + r.x + w, destY + r.y + h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, GLState::fbo);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void GSDeviceOGL::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ShaderConvert shader, bool linear)
|
||||
|
@ -1407,7 +1392,7 @@ void GSDeviceOGL::DoFXAA(GSTexture* sTex, GSTexture* dTex)
|
|||
if (!m_fxaa.ps.IsValid())
|
||||
{
|
||||
// Needs ARB_gpu_shader5 for gather.
|
||||
if (!GLLoader::found_GL_ARB_gpu_shader5)
|
||||
if (!GLLoader::is_gles && !GLLoader::found_GL_ARB_gpu_shader5)
|
||||
return;
|
||||
|
||||
std::string fxaa_macro = "#define FXAA_GLSL_130 1\n";
|
||||
|
@ -1446,7 +1431,7 @@ void GSDeviceOGL::DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float par
|
|||
const GSVector4 sRect(0, 0, 1, 1);
|
||||
const GSVector4 dRect(0, 0, s.x, s.y);
|
||||
|
||||
StretchRect(sTex, sRect, dTex, dRect, m_shadeboost.ps, true);
|
||||
StretchRect(sTex, sRect, dTex, dRect, m_shadeboost.ps, false);
|
||||
}
|
||||
|
||||
void GSDeviceOGL::SetupDATE(GSTexture* rt, GSTexture* ds, const GSVertexPT1* vertices, bool datm)
|
||||
|
@ -1559,7 +1544,7 @@ void GSDeviceOGL::ClearSamplerCache()
|
|||
bool GSDeviceOGL::CreateCASPrograms()
|
||||
{
|
||||
// Image load store and GLSL 420pack is core in GL4.2, no need to check.
|
||||
m_features.cas_sharpening = GLAD_GL_VERSION_4_2 && GLAD_GL_ARB_compute_shader;
|
||||
m_features.cas_sharpening = (GLAD_GL_VERSION_4_2 && GLAD_GL_ARB_compute_shader) || GLAD_GL_ES_VERSION_3_2;
|
||||
if (!m_features.cas_sharpening)
|
||||
{
|
||||
Console.Warning("Compute shaders not supported, CAS is unavailable.");
|
||||
|
@ -1574,6 +1559,12 @@ bool GSDeviceOGL::CreateCASPrograms()
|
|||
}
|
||||
|
||||
const char* header =
|
||||
GLLoader::is_gles ?
|
||||
"#version 320 es\n"
|
||||
"precision highp float;\n"
|
||||
"precision highp int;\n"
|
||||
"precision highp sampler2D;\n"
|
||||
"precision highp image2D;\n" :
|
||||
"#version 420\n"
|
||||
"#extension GL_ARB_compute_shader : require\n";
|
||||
const char* sharpen_params[2] = {
|
||||
|
@ -1710,6 +1701,15 @@ void GSDeviceOGL::OMSetBlendState(bool enable, GLenum src_factor, GLenum dst_fac
|
|||
{
|
||||
if (GLState::blend)
|
||||
{
|
||||
// make sure we're not using dual source
|
||||
if (GLState::f_sRGB == GL_SRC1_ALPHA || GLState::f_sRGB == GL_ONE_MINUS_SRC1_ALPHA ||
|
||||
GLState::f_dRGB == GL_SRC1_ALPHA || GLState::f_dRGB == GL_ONE_MINUS_SRC1_ALPHA)
|
||||
{
|
||||
glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ONE, GL_ZERO);
|
||||
GLState::f_sRGB = GL_ONE;
|
||||
GLState::f_dRGB = GL_ZERO;
|
||||
}
|
||||
|
||||
GLState::blend = false;
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
|
@ -1798,12 +1798,13 @@ void GSDeviceOGL::SetupOM(OMDepthStencilSelector dssel)
|
|||
OMSetDepthStencilState(m_om_dss[dssel.key]);
|
||||
}
|
||||
|
||||
static GSDeviceOGL::VSSelector convertSel(const GSHWDrawConfig::VSSelector sel)
|
||||
static GSDeviceOGL::VSSelector convertSel(const GSHWDrawConfig::VSSelector sel, const GSHWDrawConfig::Topology topology)
|
||||
{
|
||||
// Mali requires gl_PointSize written when rasterizing points. The spec seems to suggest this is okay.
|
||||
GSDeviceOGL::VSSelector out;
|
||||
out.int_fst = !sel.fst;
|
||||
out.iip = sel.iip;
|
||||
out.point_size = sel.point_size;
|
||||
out.point_size = sel.point_size || (GLLoader::is_gles && topology == GSHWDrawConfig::Topology::Point);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
@ -1916,7 +1917,7 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
|
|||
}
|
||||
|
||||
ProgramSelector psel;
|
||||
psel.vs = convertSel(config.vs);
|
||||
psel.vs = convertSel(config.vs, config.topology);
|
||||
psel.ps.key_hi = config.ps.key_hi;
|
||||
psel.ps.key_lo = config.ps.key_lo;
|
||||
psel.gs.key = 0;
|
||||
|
@ -1936,7 +1937,7 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
|
|||
SetupPipeline(psel);
|
||||
|
||||
// additional non-pipeline config stuff
|
||||
const bool point_size_enabled = config.vs.point_size;
|
||||
const bool point_size_enabled = config.vs.point_size && !GLLoader::is_gles;
|
||||
if (GLState::point_size != point_size_enabled)
|
||||
{
|
||||
if (point_size_enabled)
|
||||
|
@ -1945,6 +1946,12 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
|
|||
glDisable(GL_PROGRAM_POINT_SIZE);
|
||||
GLState::point_size = point_size_enabled;
|
||||
}
|
||||
const float line_width = config.line_expand ? static_cast<float>(GSConfig.UpscaleMultiplier) : 1.0f;
|
||||
if (GLState::line_width != line_width)
|
||||
{
|
||||
GLState::line_width = line_width;
|
||||
glLineWidth(line_width);
|
||||
}
|
||||
|
||||
if (config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::PrimIDTracking)
|
||||
{
|
||||
|
@ -2115,7 +2122,6 @@ void GSDeviceOGL::DebugOutputToFile(GLenum gl_source, GLenum gl_type, GLuint id,
|
|||
{
|
||||
std::string message(gl_message, gl_length >= 0 ? gl_length : strlen(gl_message));
|
||||
std::string type, severity, source;
|
||||
static int sev_counter = 0;
|
||||
switch (gl_type)
|
||||
{
|
||||
case GL_DEBUG_TYPE_ERROR_ARB : type = "Error"; break;
|
||||
|
@ -2130,7 +2136,7 @@ void GSDeviceOGL::DebugOutputToFile(GLenum gl_source, GLenum gl_type, GLuint id,
|
|||
}
|
||||
switch (gl_severity)
|
||||
{
|
||||
case GL_DEBUG_SEVERITY_HIGH_ARB : severity = "High"; sev_counter++; break;
|
||||
case GL_DEBUG_SEVERITY_HIGH_ARB : severity = "High"; break;
|
||||
case GL_DEBUG_SEVERITY_MEDIUM_ARB : severity = "Mid"; break;
|
||||
case GL_DEBUG_SEVERITY_LOW_ARB : severity = "Low"; break;
|
||||
default:
|
||||
|
@ -2153,43 +2159,16 @@ void GSDeviceOGL::DebugOutputToFile(GLenum gl_source, GLenum gl_type, GLuint id,
|
|||
default : source = "???"; break;
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
// Don't spam noisy information on the terminal
|
||||
if (gl_severity != GL_DEBUG_SEVERITY_NOTIFICATION)
|
||||
if (gl_severity != GL_DEBUG_SEVERITY_NOTIFICATION && gl_source != GL_DEBUG_SOURCE_APPLICATION)
|
||||
{
|
||||
Console.Error("T:%s\tID:%d\tS:%s\t=> %s", type.c_str(), GSState::s_n, severity.c_str(), message.c_str());
|
||||
}
|
||||
#else
|
||||
// Print nouveau shader compiler info
|
||||
if (GSState::s_n == 0)
|
||||
{
|
||||
int t, local, gpr, inst, byte;
|
||||
const int status = sscanf(message.c_str(), "type: %d, local: %d, gpr: %d, inst: %d, bytes: %d",
|
||||
&t, &local, &gpr, &inst, &byte);
|
||||
if (status == 5)
|
||||
{
|
||||
m_shader_inst += inst;
|
||||
m_shader_reg += gpr;
|
||||
fprintf(stderr, "T:%s\t\tS:%s\t=> %s\n", type.c_str(), severity.c_str(), message.c_str());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ENABLE_OGL_DEBUG
|
||||
if (m_debug_gl_file)
|
||||
fprintf(m_debug_gl_file, "T:%s\tID:%d\tS:%s\t=> %s\n", type.c_str(), GSState::s_n, severity.c_str(), message.c_str());
|
||||
|
||||
if (sev_counter >= 5)
|
||||
{
|
||||
// Close the file to flush the content on disk before exiting.
|
||||
if (m_debug_gl_file)
|
||||
{
|
||||
fclose(m_debug_gl_file);
|
||||
m_debug_gl_file = NULL;
|
||||
}
|
||||
ASSERT(0);
|
||||
}
|
||||
#endif
|
||||
GL::StreamBuffer* GSDeviceOGL::GetTextureUploadBuffer()
|
||||
{
|
||||
return s_texture_upload_buffer.get();
|
||||
}
|
||||
|
||||
void GSDeviceOGL::PushDebugGroup(const char* fmt, ...)
|
||||
|
|
|
@ -22,16 +22,10 @@
|
|||
#include "common/HashCombine.h"
|
||||
#include "GS/Renderers/Common/GSDevice.h"
|
||||
#include "GSTextureOGL.h"
|
||||
#include "GSUniformBufferOGL.h"
|
||||
#include "GLState.h"
|
||||
#include "GLLoader.h"
|
||||
#include "GS/GS.h"
|
||||
|
||||
#ifdef ENABLE_OGL_DEBUG_MEM_BW
|
||||
extern u64 g_real_texture_upload_byte;
|
||||
extern u64 g_vertex_upload_byte;
|
||||
#endif
|
||||
|
||||
class GSDepthStencilOGL
|
||||
{
|
||||
bool m_depth_enable;
|
||||
|
@ -207,19 +201,15 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
static int m_shader_inst;
|
||||
static int m_shader_reg;
|
||||
|
||||
private:
|
||||
static FILE* m_debug_gl_file;
|
||||
|
||||
// Place holder for the GLSL shader code (to avoid useless reload)
|
||||
std::string m_shader_common_header;
|
||||
std::string m_shader_tfx_vgs;
|
||||
std::string m_shader_tfx_fs;
|
||||
|
||||
GLuint m_fbo; // frame buffer container
|
||||
GLuint m_fbo_read; // frame buffer container only for reading
|
||||
GLuint m_fbo = 0; // frame buffer container
|
||||
GLuint m_fbo_read = 0; // frame buffer container only for reading
|
||||
GLuint m_fbo_write = 0; // frame buffer container only for writing
|
||||
|
||||
std::unique_ptr<GL::StreamBuffer> m_vertex_stream_buffer;
|
||||
std::unique_ptr<GL::StreamBuffer> m_index_stream_buffer;
|
||||
|
@ -274,20 +264,12 @@ private:
|
|||
GL::Program sharpen_ps;
|
||||
} m_cas;
|
||||
|
||||
struct
|
||||
{
|
||||
u16 last_query = 0;
|
||||
GLuint timer_query[1 << 16] = {};
|
||||
|
||||
GLuint timer() { return timer_query[last_query]; }
|
||||
} m_profiler;
|
||||
|
||||
GLuint m_ps_ss[1 << 8];
|
||||
GSDepthStencilOGL* m_om_dss[1 << 5] = {};
|
||||
std::unordered_map<ProgramSelector, GL::Program, ProgramSelectorHash> m_programs;
|
||||
GL::ShaderCache m_shader_cache;
|
||||
|
||||
GLuint m_palette_ss;
|
||||
GLuint m_palette_ss = 0;
|
||||
|
||||
GSHWDrawConfig::VSConstantBuffer m_vs_cb_cache;
|
||||
GSHWDrawConfig::PSConstantBuffer m_ps_cb_cache;
|
||||
|
@ -314,11 +296,11 @@ public:
|
|||
GSDeviceOGL();
|
||||
virtual ~GSDeviceOGL();
|
||||
|
||||
void GenerateProfilerData();
|
||||
|
||||
// Used by OpenGL, so the same calling convention is required.
|
||||
static void APIENTRY DebugOutputToFile(GLenum gl_source, GLenum gl_type, GLuint id, GLenum gl_severity, GLsizei gl_length, const GLchar* gl_message, const void* userParam);
|
||||
|
||||
static GL::StreamBuffer* GetTextureUploadBuffer();
|
||||
|
||||
bool Create() override;
|
||||
|
||||
void ResetAPIState() override;
|
||||
|
|
|
@ -15,170 +15,23 @@
|
|||
|
||||
#include "PrecompiledHeader.h"
|
||||
#include <limits.h>
|
||||
#include "GSTextureOGL.h"
|
||||
#include "GLState.h"
|
||||
#include "GS/Renderers/OpenGL/GSDeviceOGL.h"
|
||||
#include "GS/Renderers/OpenGL/GSTextureOGL.h"
|
||||
#include "GS/Renderers/OpenGL/GLState.h"
|
||||
#include "GS/GSPerfMon.h"
|
||||
#include "GS/GSPng.h"
|
||||
#include "GS/GSGL.h"
|
||||
#include "common/StringUtil.h"
|
||||
|
||||
#ifdef ENABLE_OGL_DEBUG_MEM_BW
|
||||
extern u64 g_real_texture_upload_byte;
|
||||
#endif
|
||||
|
||||
// FIXME OGL4: investigate, only 1 unpack buffer always bound
|
||||
namespace PboPool
|
||||
{
|
||||
|
||||
const u32 m_pbo_size = 64 * 1024 * 1024;
|
||||
const u32 m_seg_size = 16 * 1024 * 1024;
|
||||
|
||||
GLuint m_buffer;
|
||||
uptr m_offset;
|
||||
char* m_map;
|
||||
u32 m_size;
|
||||
GLsync m_fence[m_pbo_size / m_seg_size];
|
||||
|
||||
// Option for buffer storage
|
||||
// XXX: actually does I really need coherent and barrier???
|
||||
// As far as I understand glTexSubImage2D is a client-server transfer so no need to make
|
||||
// the value visible to the server
|
||||
const GLbitfield common_flags = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT;
|
||||
const GLbitfield map_flags = common_flags | GL_MAP_FLUSH_EXPLICIT_BIT;
|
||||
const GLbitfield create_flags = common_flags | GL_CLIENT_STORAGE_BIT;
|
||||
|
||||
void Init()
|
||||
{
|
||||
glGenBuffers(1, &m_buffer);
|
||||
|
||||
BindPbo();
|
||||
|
||||
glObjectLabel(GL_BUFFER, m_buffer, -1, "PBO");
|
||||
|
||||
glBufferStorage(GL_PIXEL_UNPACK_BUFFER, m_pbo_size, NULL, create_flags);
|
||||
m_map = (char*)glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, m_pbo_size, map_flags);
|
||||
m_offset = 0;
|
||||
|
||||
std::fill(std::begin(m_fence), std::end(m_fence), nullptr);
|
||||
|
||||
UnbindPbo();
|
||||
}
|
||||
|
||||
char* Map(u32 size)
|
||||
{
|
||||
char* map;
|
||||
// Note: keep offset aligned for SSE/AVX
|
||||
m_size = (size + 63) & ~0x3F;
|
||||
|
||||
if (m_size > m_pbo_size)
|
||||
{
|
||||
fprintf(stderr, "BUG: PBO too small %u but need %u\n", m_pbo_size, m_size);
|
||||
}
|
||||
|
||||
// Note: texsubimage will access currently bound buffer
|
||||
// Pbo ready let's get a pointer
|
||||
BindPbo();
|
||||
|
||||
Sync();
|
||||
|
||||
map = m_map + m_offset;
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
void Unmap()
|
||||
{
|
||||
glFlushMappedBufferRange(GL_PIXEL_UNPACK_BUFFER, m_offset, m_size);
|
||||
}
|
||||
|
||||
uptr Offset()
|
||||
{
|
||||
return m_offset;
|
||||
}
|
||||
|
||||
void Destroy()
|
||||
{
|
||||
m_map = NULL;
|
||||
m_offset = 0;
|
||||
|
||||
for (GLsync& fence : m_fence)
|
||||
{
|
||||
if (fence != 0)
|
||||
{
|
||||
glDeleteSync(fence);
|
||||
fence = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_buffer != 0)
|
||||
{
|
||||
glDeleteBuffers(1, &m_buffer);
|
||||
m_buffer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void BindPbo()
|
||||
{
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, m_buffer);
|
||||
}
|
||||
|
||||
void Sync()
|
||||
{
|
||||
u32 segment_current = m_offset / m_seg_size;
|
||||
u32 segment_next = (m_offset + m_size) / m_seg_size;
|
||||
|
||||
if (segment_current != segment_next)
|
||||
{
|
||||
if (segment_next >= std::size(m_fence))
|
||||
{
|
||||
segment_next = 0;
|
||||
}
|
||||
// Align current transfer on the start of the segment
|
||||
m_offset = m_seg_size * segment_next;
|
||||
|
||||
if (m_size > m_seg_size)
|
||||
{
|
||||
fprintf(stderr, "BUG: PBO Map size %u is bigger than a single segment %u. Crossing more than one fence is not supported yet, texture data may be corrupted.\n", m_size, m_seg_size);
|
||||
// TODO Synchronize all crossed fences
|
||||
}
|
||||
|
||||
// protect the left segment
|
||||
m_fence[segment_current] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
|
||||
// Check next segment is free
|
||||
if (m_fence[segment_next])
|
||||
{
|
||||
GLenum status = glClientWaitSync(m_fence[segment_next], GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED);
|
||||
// Potentially it doesn't work on AMD driver which might always return GL_CONDITION_SATISFIED
|
||||
if (status != GL_ALREADY_SIGNALED)
|
||||
{
|
||||
GL_PERF("GL_PIXEL_UNPACK_BUFFER: Sync Sync (%x)! Buffer too small ?", status);
|
||||
}
|
||||
|
||||
glDeleteSync(m_fence[segment_next]);
|
||||
m_fence[segment_next] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UnbindPbo()
|
||||
{
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
}
|
||||
|
||||
void EndTransfer()
|
||||
{
|
||||
m_offset += m_size;
|
||||
}
|
||||
} // namespace PboPool
|
||||
static constexpr u32 TEXTURE_UPLOAD_ALIGNMENT = 256;
|
||||
|
||||
GSTextureOGL::GSTextureOGL(Type type, int width, int height, int levels, Format format, GLuint fbo_read)
|
||||
: m_clean(false), m_r_x(0), m_r_y(0), m_r_w(0), m_r_h(0), m_layer(0)
|
||||
{
|
||||
// OpenGL didn't like dimensions of size 0
|
||||
m_size.x = std::max(1, width);
|
||||
m_size.y = std::max(1, height);
|
||||
m_format = format;
|
||||
m_type = type;
|
||||
m_type = type;
|
||||
m_fbo_read = fbo_read;
|
||||
m_texture_id = 0;
|
||||
m_mipmap_levels = 1;
|
||||
|
@ -189,46 +42,46 @@ GSTextureOGL::GSTextureOGL(Type type, int width, int height, int levels, Format
|
|||
{
|
||||
// 1 Channel integer
|
||||
case Format::PrimID:
|
||||
gl_fmt = GL_R32F;
|
||||
m_int_format = GL_RED;
|
||||
m_int_type = GL_INT;
|
||||
m_int_shift = 2;
|
||||
gl_fmt = GL_R32F;
|
||||
m_int_format = GL_RED;
|
||||
m_int_type = GL_INT;
|
||||
m_int_shift = 2;
|
||||
break;
|
||||
case Format::UInt32:
|
||||
gl_fmt = GL_R32UI;
|
||||
m_int_format = GL_RED_INTEGER;
|
||||
m_int_type = GL_UNSIGNED_INT;
|
||||
m_int_shift = 2;
|
||||
gl_fmt = GL_R32UI;
|
||||
m_int_format = GL_RED_INTEGER;
|
||||
m_int_type = GL_UNSIGNED_INT;
|
||||
m_int_shift = 2;
|
||||
break;
|
||||
case Format::UInt16:
|
||||
gl_fmt = GL_R16UI;
|
||||
m_int_format = GL_RED_INTEGER;
|
||||
m_int_type = GL_UNSIGNED_SHORT;
|
||||
m_int_shift = 1;
|
||||
gl_fmt = GL_R16UI;
|
||||
m_int_format = GL_RED_INTEGER;
|
||||
m_int_type = GL_UNSIGNED_SHORT;
|
||||
m_int_shift = 1;
|
||||
break;
|
||||
|
||||
// 1 Channel normalized
|
||||
case Format::UNorm8:
|
||||
gl_fmt = GL_R8;
|
||||
m_int_format = GL_RED;
|
||||
m_int_type = GL_UNSIGNED_BYTE;
|
||||
m_int_shift = 0;
|
||||
gl_fmt = GL_R8;
|
||||
m_int_format = GL_RED;
|
||||
m_int_type = GL_UNSIGNED_BYTE;
|
||||
m_int_shift = 0;
|
||||
break;
|
||||
|
||||
// 4 channel normalized
|
||||
case Format::Color:
|
||||
gl_fmt = GL_RGBA8;
|
||||
m_int_format = GL_RGBA;
|
||||
m_int_type = GL_UNSIGNED_BYTE;
|
||||
m_int_shift = 2;
|
||||
gl_fmt = GL_RGBA8;
|
||||
m_int_format = GL_RGBA;
|
||||
m_int_type = GL_UNSIGNED_BYTE;
|
||||
m_int_shift = 2;
|
||||
break;
|
||||
|
||||
// 4 channel float
|
||||
case Format::HDRColor:
|
||||
gl_fmt = GL_RGBA16;
|
||||
m_int_format = GL_RGBA;
|
||||
m_int_type = GL_UNSIGNED_SHORT;
|
||||
m_int_shift = 3;
|
||||
gl_fmt = GL_RGBA16;
|
||||
m_int_format = GL_RGBA;
|
||||
m_int_type = GL_UNSIGNED_SHORT;
|
||||
m_int_shift = 3;
|
||||
break;
|
||||
|
||||
// Depth buffer
|
||||
|
@ -252,37 +105,37 @@ GSTextureOGL::GSTextureOGL(Type type, int width, int height, int levels, Format
|
|||
break;
|
||||
|
||||
case Format::BC1:
|
||||
gl_fmt = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
|
||||
m_int_format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
|
||||
m_int_type = GL_UNSIGNED_BYTE;
|
||||
m_int_shift = 1;
|
||||
gl_fmt = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
|
||||
m_int_format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
|
||||
m_int_type = GL_UNSIGNED_BYTE;
|
||||
m_int_shift = 1;
|
||||
break;
|
||||
|
||||
case Format::BC2:
|
||||
gl_fmt = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
|
||||
m_int_format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
|
||||
m_int_type = GL_UNSIGNED_BYTE;
|
||||
m_int_shift = 1;
|
||||
gl_fmt = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
|
||||
m_int_format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
|
||||
m_int_type = GL_UNSIGNED_BYTE;
|
||||
m_int_shift = 1;
|
||||
break;
|
||||
|
||||
case Format::BC3:
|
||||
gl_fmt = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
|
||||
m_int_format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
|
||||
m_int_type = GL_UNSIGNED_BYTE;
|
||||
m_int_shift = 1;
|
||||
gl_fmt = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
|
||||
m_int_format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
|
||||
m_int_type = GL_UNSIGNED_BYTE;
|
||||
m_int_shift = 1;
|
||||
break;
|
||||
|
||||
case Format::BC7:
|
||||
gl_fmt = GL_COMPRESSED_RGBA_BPTC_UNORM_ARB;
|
||||
m_int_format = GL_COMPRESSED_RGBA_BPTC_UNORM_ARB;
|
||||
m_int_type = GL_UNSIGNED_BYTE;
|
||||
m_int_shift = 1;
|
||||
gl_fmt = GL_COMPRESSED_RGBA_BPTC_UNORM_ARB;
|
||||
m_int_format = GL_COMPRESSED_RGBA_BPTC_UNORM_ARB;
|
||||
m_int_type = GL_UNSIGNED_BYTE;
|
||||
m_int_shift = 1;
|
||||
break;
|
||||
|
||||
case Format::Invalid:
|
||||
m_int_format = 0;
|
||||
m_int_type = 0;
|
||||
m_int_shift = 0;
|
||||
m_int_format = 0;
|
||||
m_int_type = 0;
|
||||
m_int_shift = 0;
|
||||
ASSERT(0);
|
||||
}
|
||||
|
||||
|
@ -363,9 +216,6 @@ bool GSTextureOGL::Update(const GSVector4i& r, const void* data, int pitch, int
|
|||
|
||||
u32 row_byte = r.width() << m_int_shift;
|
||||
u32 map_size = r.height() * row_byte;
|
||||
#ifdef ENABLE_OGL_DEBUG_MEM_BW
|
||||
g_real_texture_upload_byte += map_size;
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
if (r.height() == 1) {
|
||||
|
@ -389,7 +239,7 @@ bool GSTextureOGL::Update(const GSVector4i& r, const void* data, int pitch, int
|
|||
glCompressedTextureSubImage2D(m_texture_id, layer, r.x, r.y, r.width(), r.height(), m_int_format, upload_size, data);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
}
|
||||
else if (map_size >= PboPool::m_seg_size)
|
||||
else if (GLLoader::buggy_pbo || map_size > GSDeviceOGL::GetTextureUploadBuffer()->GetChunkSize())
|
||||
{
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, pitch >> m_int_shift);
|
||||
glTextureSubImage2D(m_texture_id, layer, r.x, r.y, r.width(), r.height(), m_int_format, m_int_type, data);
|
||||
|
@ -397,27 +247,15 @@ bool GSTextureOGL::Update(const GSVector4i& r, const void* data, int pitch, int
|
|||
}
|
||||
else
|
||||
{
|
||||
// The complex solution with PBO
|
||||
char* src = (char*)data;
|
||||
char* map = PboPool::Map(map_size);
|
||||
GL::StreamBuffer* const sb = GSDeviceOGL::GetTextureUploadBuffer();
|
||||
|
||||
// PERF: slow path of the texture upload. Dunno if we could do better maybe check if TC can keep row_byte == pitch
|
||||
// Note: row_byte != pitch
|
||||
for (int h = 0; h < r.height(); h++)
|
||||
{
|
||||
memcpy(map, src, row_byte);
|
||||
map += row_byte;
|
||||
src += pitch;
|
||||
}
|
||||
const auto map = sb->Map(TEXTURE_UPLOAD_ALIGNMENT, map_size);
|
||||
StringUtil::StrideMemCpy(map.pointer, row_byte, data, pitch, row_byte, r.height());
|
||||
sb->Unmap(map_size);
|
||||
sb->Bind();
|
||||
|
||||
PboPool::Unmap();
|
||||
|
||||
glTextureSubImage2D(m_texture_id, layer, r.x, r.y, r.width(), r.height(), m_int_format, m_int_type, (const void*)PboPool::Offset());
|
||||
|
||||
// FIXME OGL4: investigate, only 1 unpack buffer always bound
|
||||
PboPool::UnbindPbo();
|
||||
|
||||
PboPool::EndTransfer();
|
||||
glTextureSubImage2D(m_texture_id, layer, r.x, r.y, r.width(), r.height(), m_int_format, m_int_type,
|
||||
reinterpret_cast<void*>(static_cast<uintptr_t>(map.buffer_offset)));
|
||||
}
|
||||
|
||||
m_needs_mipmaps_generated = true;
|
||||
|
@ -441,7 +279,7 @@ bool GSTextureOGL::Map(GSMap& m, const GSVector4i* _r, int layer)
|
|||
if (m_type == Type::Texture || m_type == Type::RenderTarget)
|
||||
{
|
||||
const u32 map_size = r.height() * row_byte;
|
||||
if (map_size > PboPool::m_seg_size)
|
||||
if (GLLoader::buggy_pbo || map_size > GSDeviceOGL::GetTextureUploadBuffer()->GetChunkSize())
|
||||
return false;
|
||||
|
||||
GL_PUSH_("Upload Texture %d", m_texture_id); // POP is in Unmap
|
||||
|
@ -449,11 +287,8 @@ bool GSTextureOGL::Map(GSMap& m, const GSVector4i* _r, int layer)
|
|||
|
||||
m_clean = false;
|
||||
|
||||
m.bits = (u8*)PboPool::Map(map_size);
|
||||
|
||||
#ifdef ENABLE_OGL_DEBUG_MEM_BW
|
||||
g_real_texture_upload_byte += map_size;
|
||||
#endif
|
||||
const auto map = GSDeviceOGL::GetTextureUploadBuffer()->Map(TEXTURE_UPLOAD_ALIGNMENT, map_size);
|
||||
m.bits = static_cast<u8*>(map.pointer);
|
||||
|
||||
// Save the area for the unmap
|
||||
m_r_x = r.x;
|
||||
|
@ -461,6 +296,7 @@ bool GSTextureOGL::Map(GSMap& m, const GSVector4i* _r, int layer)
|
|||
m_r_w = r.width();
|
||||
m_r_h = r.height();
|
||||
m_layer = layer;
|
||||
m_map_offset = map.buffer_offset;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -472,15 +308,13 @@ void GSTextureOGL::Unmap()
|
|||
{
|
||||
if (m_type == Type::Texture || m_type == Type::RenderTarget)
|
||||
{
|
||||
const u32 map_size = (m_r_w << m_int_shift) * m_r_h;
|
||||
GL::StreamBuffer* sb = GSDeviceOGL::GetTextureUploadBuffer();
|
||||
sb->Unmap(map_size);
|
||||
sb->Bind();
|
||||
|
||||
PboPool::Unmap();
|
||||
|
||||
glTextureSubImage2D(m_texture_id, m_layer, m_r_x, m_r_y, m_r_w, m_r_h, m_int_format, m_int_type, (const void*)PboPool::Offset());
|
||||
|
||||
// FIXME OGL4: investigate, only 1 unpack buffer always bound
|
||||
PboPool::UnbindPbo();
|
||||
|
||||
PboPool::EndTransfer();
|
||||
glTextureSubImage2D(m_texture_id, m_layer, m_r_x, m_r_y, m_r_w, m_r_h, m_int_format, m_int_type,
|
||||
reinterpret_cast<void*>(static_cast<uintptr_t>(m_map_offset)));
|
||||
|
||||
m_needs_mipmaps_generated = true;
|
||||
|
||||
|
|
|
@ -19,40 +19,26 @@
|
|||
#include "GS/Renderers/OpenGL/GLLoader.h"
|
||||
#include "common/AlignedMalloc.h"
|
||||
|
||||
namespace PboPool
|
||||
{
|
||||
inline void BindPbo();
|
||||
inline void UnbindPbo();
|
||||
inline void Sync();
|
||||
|
||||
inline char* Map(u32 size);
|
||||
inline void Unmap();
|
||||
inline uptr Offset();
|
||||
inline void EndTransfer();
|
||||
|
||||
void Init();
|
||||
void Destroy();
|
||||
} // namespace PboPool
|
||||
|
||||
class GSTextureOGL final : public GSTexture
|
||||
{
|
||||
private:
|
||||
GLuint m_texture_id; // the texture id
|
||||
GLuint m_fbo_read;
|
||||
bool m_clean;
|
||||
GLuint m_texture_id = 0; // the texture id
|
||||
GLuint m_fbo_read = 0;
|
||||
bool m_clean = false;
|
||||
|
||||
// Avoid alignment constrain
|
||||
//GSVector4i m_r;
|
||||
int m_r_x;
|
||||
int m_r_y;
|
||||
int m_r_w;
|
||||
int m_r_h;
|
||||
int m_layer;
|
||||
int m_r_x = 0;
|
||||
int m_r_y = 0;
|
||||
int m_r_w = 0;
|
||||
int m_r_h = 0;
|
||||
int m_layer = 0;
|
||||
u32 m_map_offset = 0;
|
||||
|
||||
// internal opengl format/type/alignment
|
||||
GLenum m_int_format;
|
||||
GLenum m_int_type;
|
||||
u32 m_int_shift;
|
||||
GLenum m_int_format = 0;
|
||||
GLenum m_int_type = 0;
|
||||
u32 m_int_shift = 0;
|
||||
|
||||
public:
|
||||
explicit GSTextureOGL(Type type, int width, int height, int levels, Format format, GLuint fbo_read);
|
||||
|
|
|
@ -1,88 +0,0 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GLState.h"
|
||||
|
||||
#ifdef ENABLE_OGL_DEBUG_MEM_BW
|
||||
extern u64 g_uniform_upload_byte;
|
||||
#endif
|
||||
|
||||
|
||||
class GSUniformBufferOGL
|
||||
{
|
||||
GLuint m_buffer; // data object
|
||||
GLuint m_index; // GLSL slot
|
||||
u32 m_size; // size of the data
|
||||
u8* m_cache; // content of the previous upload
|
||||
|
||||
public:
|
||||
GSUniformBufferOGL(const std::string& pretty_name, GLuint index, u32 size)
|
||||
: m_index(index), m_size(size)
|
||||
{
|
||||
glGenBuffers(1, &m_buffer);
|
||||
bind();
|
||||
glObjectLabel(GL_BUFFER, m_buffer, pretty_name.size(), pretty_name.c_str());
|
||||
allocate();
|
||||
attach();
|
||||
m_cache = (u8*)_aligned_malloc(m_size, 32);
|
||||
memset(m_cache, 0, m_size);
|
||||
}
|
||||
|
||||
void bind()
|
||||
{
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, m_buffer);
|
||||
}
|
||||
|
||||
void allocate()
|
||||
{
|
||||
glBufferData(GL_UNIFORM_BUFFER, m_size, NULL, GL_DYNAMIC_DRAW);
|
||||
}
|
||||
|
||||
void attach()
|
||||
{
|
||||
// From the opengl manpage:
|
||||
// glBindBufferBase also binds buffer to the generic buffer binding point specified by target
|
||||
glBindBufferBase(GL_UNIFORM_BUFFER, m_index, m_buffer);
|
||||
}
|
||||
|
||||
void upload(const void* src)
|
||||
{
|
||||
bind();
|
||||
// glMapBufferRange allow to set various parameter but the call is
|
||||
// synchronous whereas glBufferSubData could be asynchronous.
|
||||
// TODO: investigate the extension ARB_invalidate_subdata
|
||||
glBufferSubData(GL_UNIFORM_BUFFER, 0, m_size, src);
|
||||
#ifdef ENABLE_OGL_DEBUG_MEM_BW
|
||||
g_uniform_upload_byte += m_size;
|
||||
#endif
|
||||
}
|
||||
|
||||
void cache_upload(const void* src)
|
||||
{
|
||||
if (memcmp(m_cache, src, m_size) != 0)
|
||||
{
|
||||
memcpy(m_cache, src, m_size);
|
||||
upload(src);
|
||||
}
|
||||
}
|
||||
|
||||
~GSUniformBufferOGL()
|
||||
{
|
||||
glDeleteBuffers(1, &m_buffer);
|
||||
_aligned_free(m_cache);
|
||||
}
|
||||
};
|
|
@ -19,6 +19,7 @@
|
|||
#include "GSRasterizer.h"
|
||||
#include "GS/GSExtra.h"
|
||||
#include "PerformanceMetrics.h"
|
||||
#include "common/AlignedMalloc.h"
|
||||
#include "common/StringUtil.h"
|
||||
|
||||
#ifdef PCSX2_CORE
|
||||
|
@ -56,8 +57,10 @@ GSRasterizer::GSRasterizer(IDrawScanline* ds, int id, int threads)
|
|||
|
||||
m_thread_height = compute_best_thread_height(threads);
|
||||
|
||||
m_edge.buff = (GSVertexSW*)vmalloc(sizeof(GSVertexSW) * 2048, false);
|
||||
m_edge.buff = static_cast<GSVertexSW*>(_aligned_malloc(sizeof(GSVertexSW) * 2048, 32));
|
||||
m_edge.count = 0;
|
||||
if (!m_edge.buff)
|
||||
throw std::bad_alloc();
|
||||
|
||||
int rows = (2048 >> m_thread_height) + 16;
|
||||
m_scanline = (u8*)_aligned_malloc(rows, 64);
|
||||
|
@ -71,10 +74,7 @@ GSRasterizer::GSRasterizer(IDrawScanline* ds, int id, int threads)
|
|||
GSRasterizer::~GSRasterizer()
|
||||
{
|
||||
_aligned_free(m_scanline);
|
||||
|
||||
if (m_edge.buff != NULL)
|
||||
vmfree(m_edge.buff, sizeof(GSVertexSW) * 2048);
|
||||
|
||||
_aligned_free(m_edge.buff);
|
||||
delete m_ds;
|
||||
}
|
||||
|
||||
|
|
|
@ -243,6 +243,7 @@ bool GSDeviceVK::CheckFeatures()
|
|||
m_features.prefer_new_textures = true;
|
||||
m_features.provoking_vertex_last = g_vulkan_context->GetOptionalExtensions().vk_ext_provoking_vertex;
|
||||
m_features.dual_source_blend = features.dualSrcBlend && !GSConfig.DisableDualSourceBlend;
|
||||
m_features.clip_control = true;
|
||||
|
||||
if (!m_features.dual_source_blend)
|
||||
Console.Warning("Vulkan driver is missing dual-source blending. This will have an impact on performance.");
|
||||
|
|
|
@ -154,9 +154,6 @@ const char* dialog_message(int ID, bool* updateText)
|
|||
"It can help Xenosaga games.\n\n"
|
||||
"Disables accurate GS Memory Clearing to be done on the CPU, and let only the GPU handle it.\n"
|
||||
"It can help Kingdom Hearts games.");
|
||||
case IDC_MEMORY_WRAPPING:
|
||||
return cvtString("Emulates GS memory wrapping accurately. This fixes issues where part of the image is cut-off by block shaped sections such as the FMVs in Wallace & Gromit: The Curse of the Were-Rabbit and Thrillville.\n\n"
|
||||
"Note: This hack can have a small impact on performance.");
|
||||
case IDC_MERGE_PP_SPRITE:
|
||||
return cvtString("Replaces post-processing multiple paving sprites by a single fat sprite.\n"
|
||||
"It reduces various upscaling lines.\n\n"
|
||||
|
|
|
@ -61,7 +61,6 @@ enum
|
|||
IDC_SAFE_FEATURES,
|
||||
IDC_DISABLE_PARTIAL_TC_INV,
|
||||
IDC_CPU_FB_CONVERSION,
|
||||
IDC_MEMORY_WRAPPING,
|
||||
IDC_PRELOAD_GS,
|
||||
IDC_HALF_SCREEN_TS,
|
||||
IDC_SKIPDRAWEND,
|
||||
|
|
|
@ -356,7 +356,6 @@ HacksTab::HacksTab(wxWindow* parent)
|
|||
m_ui.addCheckBox(rend_hacks_grid, "Auto Flush", "UserHacks_AutoFlush", IDC_AUTO_FLUSH_HW, hacks_prereq);
|
||||
m_ui.addCheckBox(rend_hacks_grid, "Frame Buffer Conversion", "UserHacks_CPU_FB_Conversion", IDC_CPU_FB_CONVERSION, hacks_prereq);
|
||||
m_ui.addCheckBox(rend_hacks_grid, "Disable Depth Emulation", "UserHacks_DisableDepthSupport", IDC_TC_DEPTH, hacks_prereq);
|
||||
m_ui.addCheckBox(rend_hacks_grid, "Memory Wrapping", "wrap_gs_mem", IDC_MEMORY_WRAPPING, hacks_prereq);
|
||||
m_ui.addCheckBox(rend_hacks_grid, "Disable Safe Features", "UserHacks_Disable_Safe_Features", IDC_SAFE_FEATURES, hacks_prereq);
|
||||
m_ui.addCheckBox(rend_hacks_grid, "Preload Frame Data", "preload_frame_with_gs_data", IDC_PRELOAD_GS, hacks_prereq);
|
||||
m_ui.addCheckBox(rend_hacks_grid, "Disable Partial Invalidation", "UserHacks_DisablePartialInvalidation", IDC_DISABLE_PARTIAL_TC_INV, hacks_prereq);
|
||||
|
|
|
@ -284,11 +284,11 @@ void GSDumpReplayerCpuStep()
|
|||
case GSDumpTypes::GSType::VSync:
|
||||
{
|
||||
s_dump_frame_number++;
|
||||
GSDumpReplayerCpuCheckExecutionState();
|
||||
GSDumpReplayerUpdateFrameLimit();
|
||||
GSDumpReplayerFrameLimit();
|
||||
GetMTGS().PostVsyncStart(false);
|
||||
VMManager::Internal::VSyncOnCPUThread();
|
||||
GSDumpReplayerCpuCheckExecutionState();
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
|
@ -553,7 +553,6 @@ void Pcsx2Config::GSOptions::ReloadIniSettings()
|
|||
GSSettingBoolEx(GPUPaletteConversion, "paltex");
|
||||
GSSettingBoolEx(AutoFlushSW, "autoflush_sw");
|
||||
GSSettingBoolEx(PreloadFrameWithGSData, "preload_frame_with_gs_data");
|
||||
GSSettingBoolEx(WrapGSMem, "wrap_gs_mem");
|
||||
GSSettingBoolEx(Mipmap, "mipmap");
|
||||
GSSettingBoolEx(ManualUserHacks, "UserHacks");
|
||||
GSSettingBoolEx(UserHacks_AlignSpriteX, "UserHacks_align_sprite_X");
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#define NOMINMAX
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
extern bool psxmode;
|
||||
|
||||
|
@ -30,15 +30,6 @@ namespace soundtouch
|
|||
class SoundTouch;
|
||||
}
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Override Win32 min/max macros with the STL's type safe and macro
|
||||
// free varieties (much safer!)
|
||||
|
||||
#undef min
|
||||
#undef max
|
||||
|
||||
template <typename T>
|
||||
static __forceinline void Clampify(T& src, T min, T max)
|
||||
{
|
||||
|
@ -51,8 +42,6 @@ static __forceinline T GetClamped(T src, T min, T max)
|
|||
return std::min(std::max(src, min), max);
|
||||
}
|
||||
|
||||
extern void SysMessage(const char* fmt, ...);
|
||||
extern void SysMessage(const wchar_t* fmt, ...);
|
||||
|
||||
// Uncomment to enable debug keys on numpad (0 to 5)
|
||||
//#define DEBUG_KEYS
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2020 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// To be continued...
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
#include "Dialogs.h"
|
||||
#include <cstring>
|
||||
#include <cstdarg>
|
||||
|
||||
void SysMessage(const char* fmt, ...)
|
||||
{
|
||||
va_list list;
|
||||
va_start(list, fmt);
|
||||
vfprintf(stderr, fmt, list);
|
||||
va_end(list);
|
||||
}
|
||||
|
||||
void DspUpdate()
|
||||
{
|
||||
}
|
||||
|
||||
s32 DspLoadLibrary(wchar_t* fileName, int modnum)
|
||||
{
|
||||
return 0;
|
||||
}
|
|
@ -676,7 +676,9 @@ extern bool RecordStart(const std::string* filename);
|
|||
extern void RecordStop();
|
||||
extern void RecordWrite(const StereoOut16& sample);
|
||||
|
||||
#ifndef PCSX2_CORE
|
||||
extern s32 DspLoadLibrary(wchar_t* fileName, int modNum);
|
||||
extern void DspCloseLibrary();
|
||||
extern int DspProcess(s16* buffer, int samples);
|
||||
extern void DspUpdate(); // to let the Dsp process window messages
|
||||
#endif
|
||||
|
|
|
@ -134,9 +134,9 @@ bool RecordStart(const std::string* filename)
|
|||
{
|
||||
m_wavrecord = nullptr; // not needed, but what the heck. :)
|
||||
if (filename)
|
||||
SysMessage("SPU2 couldn't open file for recording: %s.\nWavfile capture disabled.", filename->c_str());
|
||||
Console.Error("SPU2 couldn't open file for recording: %s.\nWavfile capture disabled.", filename->c_str());
|
||||
else
|
||||
SysMessage("SPU2 couldn't open file for recording: audio_recording.wav.\nWavfile capture disabled.");
|
||||
Console.Error("SPU2 couldn't open file for recording: audio_recording.wav.\nWavfile capture disabled.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,21 +31,19 @@
|
|||
|
||||
using namespace Threading;
|
||||
|
||||
std::recursive_mutex mtx_SPU2Status;
|
||||
|
||||
static int ConsoleSampleRate = 48000;
|
||||
int SampleRate = 48000;
|
||||
|
||||
static double DeviceSampleRateMultiplier = 1.0;
|
||||
|
||||
static bool IsOpened = false;
|
||||
static bool IsInitialized = false;
|
||||
|
||||
u32 lClocks = 0;
|
||||
|
||||
#ifndef PCSX2_CORE
|
||||
#include "gui/AppCoreThread.h"
|
||||
|
||||
static bool IsOpened = false;
|
||||
std::recursive_mutex mtx_SPU2Status;
|
||||
|
||||
void SPU2configure()
|
||||
{
|
||||
ScopedCoreThreadPause paused_core(SystemsMask::System_SPU2);
|
||||
|
@ -176,11 +174,10 @@ static void SPU2InternalReset(PS2Modes isRunningPSXMode)
|
|||
}
|
||||
}
|
||||
|
||||
s32 SPU2reset(PS2Modes isRunningPSXMode)
|
||||
void SPU2reset(PS2Modes isRunningPSXMode)
|
||||
{
|
||||
SPU2InternalReset(isRunningPSXMode);
|
||||
SPU2UpdateSampleRate();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SPU2SetDeviceSampleRateMultiplier(double multiplier)
|
||||
|
@ -192,27 +189,9 @@ void SPU2SetDeviceSampleRateMultiplier(double multiplier)
|
|||
SPU2UpdateSampleRate();
|
||||
}
|
||||
|
||||
s32 SPU2init(bool KeepMode)
|
||||
bool SPU2init()
|
||||
{
|
||||
assert(regtable[0x400] == nullptr);
|
||||
|
||||
if (IsInitialized)
|
||||
return 0;
|
||||
|
||||
IsInitialized = true;
|
||||
|
||||
ReadSettings();
|
||||
|
||||
#ifdef SPU2_LOG
|
||||
if (AccessLog())
|
||||
{
|
||||
spu2Log = OpenLog(AccessLogFileName.c_str());
|
||||
setvbuf(spu2Log, nullptr, _IONBF, 0);
|
||||
FileLog("SPU2init\n");
|
||||
}
|
||||
#endif
|
||||
srand((unsigned)time(nullptr));
|
||||
|
||||
pxAssert(regtable[0x400] == nullptr);
|
||||
spu2regs = (s16*)malloc(0x010000);
|
||||
_spu2mem = (s16*)malloc(0x200000);
|
||||
|
||||
|
@ -225,10 +204,10 @@ s32 SPU2init(bool KeepMode)
|
|||
|
||||
pcm_cache_data = (PcmCacheEntry*)calloc(pcm_BlockCount, sizeof(PcmCacheEntry));
|
||||
|
||||
if ((spu2regs == nullptr) || (_spu2mem == nullptr) || (pcm_cache_data == nullptr))
|
||||
if (!spu2regs || !_spu2mem || !pcm_cache_data)
|
||||
{
|
||||
SysMessage("SPU2: Error allocating Memory\n");
|
||||
return -1;
|
||||
Console.Error("SPU2: Error allocating Memory");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Patch up a copy of regtable that directly maps "nullptrs" to SPU2 memory.
|
||||
|
@ -244,12 +223,8 @@ s32 SPU2init(bool KeepMode)
|
|||
}
|
||||
}
|
||||
|
||||
SPU2InternalReset((ConsoleSampleRate == 44100 && KeepMode) ? PS2Modes::PSX : PS2Modes::PS2);
|
||||
|
||||
DMALogOpen();
|
||||
InitADSR();
|
||||
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(_MSC_VER) && !defined(PCSX2_CORE)
|
||||
|
@ -292,18 +267,17 @@ static INT_PTR CALLBACK DebugProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lP
|
|||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
uptr gsWindowHandle = 0;
|
||||
|
||||
s32 SPU2open()
|
||||
bool SPU2open(PS2Modes isRunningPSXMode)
|
||||
{
|
||||
#ifndef PCSX2_CORE
|
||||
std::unique_lock lock(mtx_SPU2Status);
|
||||
if (IsOpened)
|
||||
return 0;
|
||||
return true;
|
||||
|
||||
FileLog("[%10d] SPU2 Open\n", Cycles);
|
||||
#if defined(_MSC_VER) && defined(PCSX2_DEVBUILD) // Define may not be needed but not tested yet. Better make sure.
|
||||
IsOpened = true;
|
||||
|
||||
#if defined(_MSC_VER) && !defined(PCSX2_CORE)
|
||||
#ifdef PCSX2_DEVBUILD // Define may not be needed but not tested yet. Better make sure.
|
||||
if (IsDevBuild && VisualDebug())
|
||||
{
|
||||
if (debugDialogOpen == 0)
|
||||
|
@ -321,9 +295,25 @@ s32 SPU2open()
|
|||
#endif
|
||||
#endif
|
||||
|
||||
IsOpened = true;
|
||||
ReadSettings();
|
||||
|
||||
#ifdef SPU2_LOG
|
||||
if (AccessLog())
|
||||
{
|
||||
spu2Log = OpenLog(AccessLogFileName.c_str());
|
||||
setvbuf(spu2Log, nullptr, _IONBF, 0);
|
||||
FileLog("SPU2init\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
DMALogOpen();
|
||||
|
||||
FileLog("[%10d] SPU2 Open\n", Cycles);
|
||||
|
||||
lClocks = psxRegs.cycle;
|
||||
|
||||
SPU2InternalReset(isRunningPSXMode);
|
||||
|
||||
try
|
||||
{
|
||||
SampleRate = static_cast<int>(std::round(static_cast<double>(ConsoleSampleRate) * DeviceSampleRateMultiplier));
|
||||
|
@ -336,19 +326,22 @@ s32 SPU2open()
|
|||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
fprintf(stderr, "SPU2 Error: Could not initialize device, or something.\nReason: %s", ex.what());
|
||||
Console.Error("SPU2 Error: Could not initialize device, or something.\nReason: %s", ex.what());
|
||||
SPU2close();
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
return 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SPU2close()
|
||||
{
|
||||
#ifndef PCSX2_CORE
|
||||
std::unique_lock lock(mtx_SPU2Status);
|
||||
if (!IsOpened)
|
||||
return;
|
||||
IsOpened = false;
|
||||
#endif
|
||||
|
||||
FileLog("[%10d] SPU2 Close\n", Cycles);
|
||||
|
||||
|
@ -361,10 +354,6 @@ void SPU2close()
|
|||
|
||||
void SPU2shutdown()
|
||||
{
|
||||
if (!IsInitialized)
|
||||
return;
|
||||
IsInitialized = false;
|
||||
|
||||
ConLog("* SPU2: Shutting down.\n");
|
||||
|
||||
SPU2close();
|
||||
|
@ -396,6 +385,11 @@ void SPU2shutdown()
|
|||
#endif
|
||||
}
|
||||
|
||||
bool SPU2IsRunningPSXMode()
|
||||
{
|
||||
return (ConsoleSampleRate == 44100);
|
||||
}
|
||||
|
||||
void SPU2SetOutputPaused(bool paused)
|
||||
{
|
||||
SndBuffer::SetPaused(paused);
|
||||
|
@ -408,7 +402,9 @@ static bool lState[6];
|
|||
|
||||
void SPU2async(u32 cycles)
|
||||
{
|
||||
#ifndef PCSX2_CORE
|
||||
DspUpdate();
|
||||
#endif
|
||||
|
||||
TimeUpdate(psxRegs.cycle);
|
||||
|
||||
|
|
|
@ -19,7 +19,9 @@
|
|||
#include "IopCounters.h"
|
||||
#include <mutex>
|
||||
|
||||
#ifndef PCSX2_CORE
|
||||
extern std::recursive_mutex mtx_SPU2Status;
|
||||
#endif
|
||||
|
||||
enum class PS2Modes
|
||||
{
|
||||
|
@ -27,11 +29,12 @@ enum class PS2Modes
|
|||
PSX,
|
||||
};
|
||||
|
||||
s32 SPU2init(bool KeepMode);
|
||||
s32 SPU2reset(PS2Modes isRunningPSXMode);
|
||||
s32 SPU2open();
|
||||
bool SPU2init();
|
||||
void SPU2reset(PS2Modes isRunningPSXMode = PS2Modes::PS2);
|
||||
bool SPU2open(PS2Modes isRunningPSXMode = PS2Modes::PS2);
|
||||
void SPU2close();
|
||||
void SPU2shutdown();
|
||||
bool SPU2IsRunningPSXMode();
|
||||
void SPU2SetOutputPaused(bool paused);
|
||||
void SPU2SetDeviceSampleRateMultiplier(double multiplier);
|
||||
void SPU2write(u32 mem, u16 value);
|
||||
|
|
|
@ -279,11 +279,20 @@ bool VMManager::Internal::InitializeGlobals()
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!SPU2init())
|
||||
{
|
||||
Host::ReportErrorAsync("Error", "Failed to initialize SPU2 (SPU2init()).");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void VMManager::Internal::ReleaseGlobals()
|
||||
{
|
||||
SPU2shutdown();
|
||||
GSshutdown();
|
||||
|
||||
#ifdef _WIN32
|
||||
CoUninitialize();
|
||||
#endif
|
||||
|
@ -384,6 +393,23 @@ std::string VMManager::GetGameSettingsPath(const std::string_view& game_serial,
|
|||
Path::Combine(EmuFolders::GameSettings, fmt::format("{}_{:08X}.ini", sanitized_serial, game_crc));
|
||||
}
|
||||
|
||||
std::string VMManager::GetDiscOverrideFromGameSettings(const std::string& elf_path)
|
||||
{
|
||||
std::string iso_path;
|
||||
if (const u32 crc = cdvdGetElfCRC(elf_path); crc != 0)
|
||||
{
|
||||
INISettingsInterface si(GetGameSettingsPath(std::string_view(), crc));
|
||||
if (si.Load())
|
||||
{
|
||||
iso_path = si.GetStringValue("EmuCore", "DiscPath");
|
||||
if (!iso_path.empty())
|
||||
Console.WriteLn(fmt::format("Disc override for ELF at '{}' is '{}'", elf_path, iso_path));
|
||||
}
|
||||
}
|
||||
|
||||
return iso_path;
|
||||
}
|
||||
|
||||
std::string VMManager::GetInputProfilePath(const std::string_view& name)
|
||||
{
|
||||
return Path::Combine(EmuFolders::InputProfiles, fmt::format("{}.ini", name));
|
||||
|
@ -435,12 +461,20 @@ void VMManager::RequestDisplaySize(float scale /*= 0.0f*/)
|
|||
Host::RequestResizeHostDisplay(iwidth, iheight);
|
||||
}
|
||||
|
||||
std::string VMManager::GetSerialForGameSettings()
|
||||
{
|
||||
// If we're running an ELF, we don't want to use the serial for any ISO override
|
||||
// for game settings, since the game settings is where we define the override.
|
||||
std::unique_lock lock(s_info_mutex);
|
||||
return s_elf_override.empty() ? std::string(s_game_serial) : std::string();
|
||||
}
|
||||
|
||||
bool VMManager::UpdateGameSettingsLayer()
|
||||
{
|
||||
std::unique_ptr<INISettingsInterface> new_interface;
|
||||
if (s_game_crc != 0 && Host::GetBaseBoolSettingValue("EmuCore", "EnablePerGameSettings", true))
|
||||
{
|
||||
std::string filename(GetGameSettingsPath(s_game_serial.c_str(), s_game_crc));
|
||||
std::string filename(GetGameSettingsPath(GetSerialForGameSettings(), s_game_crc));
|
||||
if (!FileSystem::FileExists(filename.c_str()))
|
||||
{
|
||||
// try the legacy format (crc.ini)
|
||||
|
@ -702,7 +736,11 @@ void VMManager::UpdateRunningGame(bool resetting, bool game_starting)
|
|||
|
||||
if (const GameDatabaseSchema::GameEntry* game = GameDatabase::findGame(s_game_serial))
|
||||
{
|
||||
s_game_name = game->name;
|
||||
if (!s_elf_override.empty())
|
||||
s_game_name = Path::GetFileTitle(FileSystem::GetDisplayNameFromPath(s_elf_override));
|
||||
else
|
||||
s_game_name = game->name;
|
||||
|
||||
memcardFilters = game->memcardFiltersAsString();
|
||||
}
|
||||
else
|
||||
|
@ -739,7 +777,7 @@ void VMManager::UpdateRunningGame(bool resetting, bool game_starting)
|
|||
|
||||
GetMTGS().SendGameCRC(new_crc);
|
||||
|
||||
Host::OnGameChanged(s_disc_path, s_game_serial, s_game_name, s_game_crc);
|
||||
Host::OnGameChanged(s_disc_path, s_elf_override, s_game_serial, s_game_name, s_game_crc);
|
||||
|
||||
#if 0
|
||||
// TODO: Enable this when the debugger is added to Qt, and it's active. Otherwise, this is just a waste of time.
|
||||
|
@ -778,8 +816,20 @@ bool VMManager::AutoDetectSource(const std::string& filename)
|
|||
}
|
||||
else if (IsElfFileName(display_name))
|
||||
{
|
||||
// alternative way of booting an elf, change the elf override, and use no disc.
|
||||
CDVDsys_ChangeSource(CDVD_SourceType::NoDisc);
|
||||
// alternative way of booting an elf, change the elf override, and (optionally) use the disc
|
||||
// specified in the game settings.
|
||||
std::string disc_path(GetDiscOverrideFromGameSettings(filename));
|
||||
if (!disc_path.empty())
|
||||
{
|
||||
CDVDsys_SetFile(CDVD_SourceType::Iso, disc_path);
|
||||
CDVDsys_ChangeSource(CDVD_SourceType::Iso);
|
||||
s_disc_path = std::move(disc_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
CDVDsys_ChangeSource(CDVD_SourceType::NoDisc);
|
||||
}
|
||||
|
||||
s_elf_override = filename;
|
||||
return true;
|
||||
}
|
||||
|
@ -963,16 +1013,12 @@ bool VMManager::Initialize(VMBootParameters boot_params)
|
|||
};
|
||||
|
||||
Console.WriteLn("Opening SPU2...");
|
||||
if (SPU2init(false) != 0 || SPU2open() != 0)
|
||||
if (!SPU2open())
|
||||
{
|
||||
Host::ReportErrorAsync("Startup Error", "Failed to initialize SPU2.");
|
||||
SPU2shutdown();
|
||||
return false;
|
||||
}
|
||||
ScopedGuard close_spu2 = []() {
|
||||
SPU2close();
|
||||
SPU2shutdown();
|
||||
};
|
||||
ScopedGuard close_spu2(&SPU2close);
|
||||
|
||||
Console.WriteLn("Opening PAD...");
|
||||
if (PADinit() != 0 || PADopen(g_host_display->GetWindowInfo()) != 0)
|
||||
|
@ -1100,11 +1146,12 @@ void VMManager::Shutdown(bool save_resume_state)
|
|||
|
||||
std::unique_lock lock(s_info_mutex);
|
||||
s_disc_path.clear();
|
||||
s_elf_override.clear();
|
||||
s_game_crc = 0;
|
||||
s_patches_crc = 0;
|
||||
s_game_serial.clear();
|
||||
s_game_name.clear();
|
||||
Host::OnGameChanged(s_disc_path, s_game_serial, s_game_name, 0);
|
||||
Host::OnGameChanged(s_disc_path, s_elf_override, s_game_serial, s_game_name, 0);
|
||||
}
|
||||
s_active_game_fixes = 0;
|
||||
s_active_widescreen_patches = 0;
|
||||
|
@ -1144,10 +1191,8 @@ void VMManager::Shutdown(bool save_resume_state)
|
|||
}
|
||||
|
||||
USBshutdown();
|
||||
SPU2shutdown();
|
||||
PADshutdown();
|
||||
DEV9shutdown();
|
||||
GSshutdown();
|
||||
|
||||
s_state.store(VMState::Shutdown, std::memory_order_release);
|
||||
Host::OnVMDestroyed();
|
||||
|
@ -1723,9 +1768,10 @@ void VMManager::CheckForSPU2ConfigChanges(const Pcsx2Config& old_config)
|
|||
return;
|
||||
}
|
||||
|
||||
const bool psxmode = SPU2IsRunningPSXMode();
|
||||
|
||||
SPU2close();
|
||||
SPU2shutdown();
|
||||
if (SPU2init(true) != 0 || SPU2open() != 0)
|
||||
if (!SPU2open(psxmode ? PS2Modes::PSX : PS2Modes::PS2))
|
||||
{
|
||||
Console.Error("(CheckForSPU2ConfigChanges) Failed to reopen SPU2, we'll probably crash :(");
|
||||
return;
|
||||
|
|
|
@ -173,9 +173,15 @@ namespace VMManager
|
|||
/// Returns true if the specified path is a disc/elf/etc.
|
||||
bool IsLoadableFileName(const std::string_view& path);
|
||||
|
||||
/// Returns the serial to use when computing the game settings path for the current game.
|
||||
std::string GetSerialForGameSettings();
|
||||
|
||||
/// Returns the path for the game settings ini file for the specified CRC.
|
||||
std::string GetGameSettingsPath(const std::string_view& game_serial, u32 game_crc);
|
||||
|
||||
/// Returns the ISO override for an ELF via gamesettings.
|
||||
std::string GetDiscOverrideFromGameSettings(const std::string& elf_path);
|
||||
|
||||
/// Returns the path for the input profile ini file with the specified name (may not exist).
|
||||
std::string GetInputProfilePath(const std::string_view& name);
|
||||
|
||||
|
@ -210,7 +216,7 @@ namespace VMManager
|
|||
void EntryPointCompilingOnCPUThread();
|
||||
void GameStartingOnCPUThread();
|
||||
void VSyncOnCPUThread();
|
||||
}
|
||||
} // namespace Internal
|
||||
} // namespace VMManager
|
||||
|
||||
|
||||
|
@ -255,11 +261,12 @@ namespace Host
|
|||
void OnSaveStateSaved(const std::string_view& filename);
|
||||
|
||||
/// Provided by the host; called when the running executable changes.
|
||||
void OnGameChanged(const std::string& disc_path, const std::string& game_serial, const std::string& game_name, u32 game_crc);
|
||||
void OnGameChanged(const std::string& disc_path, const std::string& elf_override, const std::string& game_serial,
|
||||
const std::string& game_name, u32 game_crc);
|
||||
|
||||
/// Provided by the host; called once per frame at guest vsync.
|
||||
void CPUThreadVSync();
|
||||
|
||||
/// Provided by the host; called when a state is saved, and the frontend should invalidate its save state cache.
|
||||
void InvalidateSaveStateCache();
|
||||
}
|
||||
} // namespace Host
|
||||
|
|
|
@ -82,20 +82,10 @@ void GSPanel::InitDefaultAccelerators()
|
|||
|
||||
m_Accels->Map( AAC( WXK_F6 ), "GSwindow_CycleAspectRatio" );
|
||||
|
||||
m_Accels->Map( AAC( WXK_NUMPAD_ADD ).Cmd(), "GSwindow_ZoomIn" ); //CTRL on Windows/linux, CMD on OSX
|
||||
m_Accels->Map( AAC( WXK_NUMPAD_SUBTRACT ).Cmd(), "GSwindow_ZoomOut" );
|
||||
m_Accels->Map( AAC( WXK_NUMPAD_MULTIPLY ).Cmd(), "GSwindow_ZoomToggle" );
|
||||
|
||||
m_Accels->Map( AAC( WXK_NUMPAD_ADD ).Cmd().Alt(), "GSwindow_ZoomInY" ); //CTRL on Windows/linux, CMD on OSX
|
||||
m_Accels->Map( AAC( WXK_NUMPAD_SUBTRACT ).Cmd().Alt(), "GSwindow_ZoomOutY" );
|
||||
m_Accels->Map( AAC( WXK_NUMPAD_MULTIPLY ).Cmd().Alt(), "GSwindow_ZoomResetY" );
|
||||
|
||||
m_Accels->Map( AAC( WXK_UP ).Cmd().Alt(), "GSwindow_OffsetYminus" );
|
||||
m_Accels->Map( AAC( WXK_DOWN ).Cmd().Alt(), "GSwindow_OffsetYplus" );
|
||||
m_Accels->Map( AAC( WXK_LEFT ).Cmd().Alt(), "GSwindow_OffsetXminus" );
|
||||
m_Accels->Map( AAC( WXK_RIGHT ).Cmd().Alt(), "GSwindow_OffsetXplus" );
|
||||
m_Accels->Map( AAC( WXK_NUMPAD_DIVIDE ).Cmd().Alt(), "GSwindow_OffsetReset" );
|
||||
|
||||
m_Accels->Map( AAC( WXK_ESCAPE ), "Sys_SuspendResume" );
|
||||
m_Accels->Map( AAC( WXK_F8 ), "Sys_TakeSnapshot" ); // also shift and ctrl-shift will be added automatically
|
||||
m_Accels->Map( AAC( WXK_F9 ), "Sys_RenderswitchToggle");
|
||||
|
|
|
@ -111,7 +111,7 @@ void SysCoreThread::OnSuspendInThread()
|
|||
|
||||
void SysCoreThread::Start()
|
||||
{
|
||||
SPU2init(false);
|
||||
SPU2init();
|
||||
PADinit();
|
||||
DEV9init();
|
||||
USBinit();
|
||||
|
|
|
@ -944,7 +944,6 @@
|
|||
<ClInclude Include="GS\Renderers\SW\GSTextureSW.h" />
|
||||
<ClInclude Include="GS\GSThread.h" />
|
||||
<ClInclude Include="GS\GSThread_CXX11.h" />
|
||||
<ClInclude Include="GS\Renderers\OpenGL\GSUniformBufferOGL.h" />
|
||||
<ClInclude Include="GS\GSUtil.h" />
|
||||
<ClInclude Include="GS\GSVector.h" />
|
||||
<ClInclude Include="GS\GSVector4i.h" />
|
||||
|
@ -1267,4 +1266,4 @@
|
|||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets" />
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
|
@ -2881,9 +2881,6 @@
|
|||
<ClInclude Include="GS\Renderers\HW\GSVertexHW.h">
|
||||
<Filter>System\Ps2\GS\Renderers\Hardware</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GS\Renderers\OpenGL\GSUniformBufferOGL.h">
|
||||
<Filter>System\Ps2\GS\Renderers\OpenGL</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GS\Renderers\Common\GSRenderer.h">
|
||||
<Filter>System\Ps2\GS\Renderers\Common</Filter>
|
||||
</ClInclude>
|
||||
|
@ -3185,4 +3182,4 @@
|
|||
<Filter>AppHost\Resources</Filter>
|
||||
</Manifest>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
|
@ -250,7 +250,6 @@
|
|||
<ClCompile Include="SPU2\Host\Config.cpp" />
|
||||
<ClCompile Include="SPU2\Host\ConfigDebug.cpp" />
|
||||
<ClCompile Include="SPU2\Host\ConfigSoundTouch.cpp" />
|
||||
<ClCompile Include="SPU2\Host\Dialogs.cpp" />
|
||||
<ClCompile Include="SPU2\RegLog.cpp" />
|
||||
<ClCompile Include="SPU2\SndOut_Cubeb.cpp" />
|
||||
<ClCompile Include="SPU2\wavedump_wav.cpp" />
|
||||
|
@ -675,7 +674,6 @@
|
|||
<ClInclude Include="GS\Renderers\SW\GSTextureSW.h" />
|
||||
<ClInclude Include="GS\GSThread.h" />
|
||||
<ClInclude Include="GS\GSThread_CXX11.h" />
|
||||
<ClInclude Include="GS\Renderers\OpenGL\GSUniformBufferOGL.h" />
|
||||
<ClInclude Include="GS\GSUtil.h" />
|
||||
<ClInclude Include="GS\GSVector.h" />
|
||||
<ClInclude Include="GS\GSVector4i.h" />
|
||||
|
@ -915,4 +913,4 @@
|
|||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets" />
|
||||
</Project>
|
||||
</Project>
|
|
@ -1223,9 +1223,6 @@
|
|||
<ClCompile Include="SPU2\Host\ConfigSoundTouch.cpp">
|
||||
<Filter>System\Ps2\SPU2</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SPU2\Host\Dialogs.cpp">
|
||||
<Filter>System\Ps2\SPU2</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SPU2\Host\Config.cpp">
|
||||
<Filter>System\Ps2\SPU2</Filter>
|
||||
</ClCompile>
|
||||
|
@ -2058,9 +2055,6 @@
|
|||
<ClInclude Include="GS\Renderers\HW\GSVertexHW.h">
|
||||
<Filter>System\Ps2\GS\Renderers\Hardware</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GS\Renderers\OpenGL\GSUniformBufferOGL.h">
|
||||
<Filter>System\Ps2\GS\Renderers\OpenGL</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GS\Renderers\Common\GSRenderer.h">
|
||||
<Filter>System\Ps2\GS\Renderers\Common</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
@ -587,7 +587,7 @@ void mVUcustomSearch()
|
|||
xVMOVUPS(ymm0, ptr[arg1reg]);
|
||||
xVPCMP.EQD(ymm0, ymm0, ptr[arg2reg]);
|
||||
xVPMOVMSKB(eax, ymm0);
|
||||
xNOT(eax);
|
||||
xXOR(eax, 0xffffffff);
|
||||
xForwardJNZ8 exitPoint;
|
||||
|
||||
xVMOVUPS(ymm0, ptr[arg1reg + 0x20]);
|
||||
|
|
Loading…
Reference in New Issue