Merge remote-tracking branch 'upstream/master'

This commit is contained in:
987123879113 2022-12-16 21:53:24 +09:00
commit 6d40d9fb94
84 changed files with 1522 additions and 1721 deletions

View File

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

View File

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

View File

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

View File

@ -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);}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//_____________________________________________________________/\_______________________________________________________________

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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] =

View File

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

View File

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

View File

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

View File

@ -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.",

View File

@ -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, &region) || !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, &region) || !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();
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -23,6 +23,7 @@ namespace GLState
GSVector4i scissor;
bool point_size = false;
float line_width = 1.0f;
bool blend;
u16 eq_RGB;

View File

@ -25,6 +25,7 @@ namespace GLState
extern GSVector4i scissor;
extern bool point_size;
extern float line_width;
extern bool blend;
extern u16 eq_RGB;

View File

@ -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, ...)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -111,7 +111,7 @@ void SysCoreThread::OnSuspendInThread()
void SysCoreThread::Start()
{
SPU2init(false);
SPU2init();
PADinit();
DEV9init();
USBinit();

View File

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

View File

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

View File

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

View File

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

View File

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