diff --git a/data/resources/gamedb.yaml b/data/resources/gamedb.yaml index b170ea4a5..3543ee7b7 100644 --- a/data/resources/gamedb.yaml +++ b/data/resources/gamedb.yaml @@ -748,12 +748,6 @@ SLPS-02348: controllers: - AnalogController - DigitalController - codes: - - SLPS-02348 - - SLPS-02356 - - SLPS-02359 - - SLPS-02373 - - SLPS-02378 metadata: publisher: "Ving" developer: "Ving" @@ -3626,12 +3620,6 @@ SLPS-02367: controllers: - AnalogController - DigitalController - codes: - - SLPS-02367 - - SLPS-02356 - - SLPS-02359 - - SLPS-02373 - - SLPS-02378 metadata: publisher: "Ving" developer: "Ving" @@ -19203,12 +19191,6 @@ SLPS-02375: controllers: - AnalogController - DigitalController - codes: - - SLPS-02375 - - SLPS-02356 - - SLPS-02359 - - SLPS-02373 - - SLPS-02378 metadata: publisher: "Ving" developer: "Ving" @@ -20380,7 +20362,6 @@ SLPS-00888: codes: - SLPS-00888 - SLPS-91103 - - SLPS-91108 metadata: publisher: "Axela" developer: "Break" @@ -26646,7 +26627,6 @@ SLPM-86241: codes: - SLPM-86241 - SLPM-86561 - - SLPM-86244 metadata: publisher: "Success" developer: "Success" @@ -26675,7 +26655,6 @@ SLPM-86242: codes: - SLPM-86242 - SLPM-86562 - - SLPM-86244 metadata: publisher: "Success" developer: "Success" @@ -26704,7 +26683,6 @@ SLPM-86243: codes: - SLPM-86243 - SLPM-86563 - - SLPM-86244 metadata: publisher: "Success" developer: "Success" @@ -26719,14 +26697,12 @@ SLPM-86243: vibration: false multitap: false linkCable: false -SLPM-86564: +SLPM-86244: name: "Cinema Eikaiwa Series Dai-6-dan - Ai no Hate ni (Japan) (Disc 4)" discSet: name: "Cinema Eikaiwa Series Dai-6-dan - Ai no Hate ni (Japan)" serials: - - SLPM-86241 - - SLPM-86242 - - SLPM-86243 + - SLPM-86244 - SLPM-86564 controllers: - DigitalController @@ -27268,12 +27244,6 @@ SLPS-02354: controllers: - AnalogController - DigitalController - codes: - - SLPS-02354 - - SLPS-02356 - - SLPS-02359 - - SLPS-02373 - - SLPS-02378 metadata: publisher: "Ving" developer: "Ving" @@ -27350,12 +27320,6 @@ SLPS-02353: controllers: - AnalogController - DigitalController - codes: - - SLPS-02353 - - SLPS-02356 - - SLPS-02359 - - SLPS-02373 - - SLPS-02378 metadata: publisher: "Ving" developer: "Ving" @@ -29742,7 +29706,6 @@ SLPS-00782: codes: - SLPS-00782 - SLPS-91104 - - SLPS-91108 metadata: publisher: "Human Entertaiment" developer: "Masterpiece / Access" @@ -30987,7 +30950,6 @@ LSP-905109: - LSP-905109 - LSP90510901 - LSP-90510901 - - LSP90510901 metadata: publisher: "Lightspan" developer: "Lightspan" @@ -36642,7 +36604,6 @@ SLPM-86378: codes: - SLPM-86378 - SLPM-86705 - - SLPM-86375 metadata: publisher: "Enix Corporation" developer: "tri-Ace" @@ -41310,9 +41271,6 @@ SLUS-01537: controllers: - AnalogController - DigitalController - codes: - - SLUS-01537 - - SLUS-07013 metadata: publisher: "Disney Interactive" developer: "Traveller's Tales / Eurocom Developments Ltd. / Doki Denki" @@ -41693,9 +41651,6 @@ SLUS-01538: controllers: - AnalogController - DigitalController - codes: - - SLUS-01538 - - SLUS-07013 metadata: publisher: "Disney Interactive" developer: "Traveller's Tales / Eurocom Developments Ltd. / Doki Denki" @@ -44122,12 +44077,6 @@ SLPS-02369: name: "Doraemon 2 - Sos! Otogi no Kuni [Reprint]" controllers: - DigitalController - codes: - - SLPS-02369 - - SLPS-02356 - - SLPS-02359 - - SLPS-02373 - - SLPS-02378 metadata: publisher: "Ving" developer: "Ving" @@ -44342,10 +44291,6 @@ SLPS-01385: codes: - SLPS-01385 - SLPS-02363 - - SLPS-02356 - - SLPS-02359 - - SLPS-02373 - - SLPS-02378 metadata: publisher: "Ving" developer: "Ving" @@ -55787,12 +55732,6 @@ SLPS-02371: controllers: - AnalogController - DigitalController - codes: - - SLPS-02371 - - SLPS-02356 - - SLPS-02359 - - SLPS-02373 - - SLPS-02378 metadata: publisher: "Ving" developer: "Ving" @@ -62032,8 +61971,6 @@ SLPM-87331: # Pick your poison here. Disabling true colour fixes the sprite backgrounds, # but if you're upscaling, leaves junk around the edges. - ForceSoftwareRendererForReadbacks - codes: - - SLPM-87331 metadata: publisher: "Squaresoft" developer: "Squaresoft" @@ -66848,6 +66785,8 @@ SLPS-00048: name: "Gokuu Densetsu - Magic Beast Warriors (Japan)" controllers: - DigitalController + codes: + - HASH-E2AD78E23425A0AF metadata: publisher: "Alyume System" developer: "Alyume System" @@ -68647,10 +68586,6 @@ SLPM-87333: - SLPM-87333 - SLPS-02380 - SLPS-03240 - - SLPS-02356 - - SLPS-02359 - - SLPS-02373 - - SLPS-02378 metadata: publisher: "Ving" developer: "Ving" @@ -71210,10 +71145,6 @@ SLPS-00578: codes: - SLPS-00578 - SLPS-02340 - - SLPS-02356 - - SLPS-02359 - - SLPS-02373 - - SLPS-02378 metadata: publisher: "Ving" developer: "Ving" @@ -71241,10 +71172,6 @@ SLPS-00579: codes: - SLPS-00579 - SLPS-02341 - - SLPS-02356 - - SLPS-02359 - - SLPS-02373 - - SLPS-02378 metadata: publisher: "Ving" developer: "Ving" @@ -71272,10 +71199,6 @@ SLPS-00580: codes: - SLPS-00580 - SLPS-02342 - - SLPS-02356 - - SLPS-02359 - - SLPS-02373 - - SLPS-02378 metadata: publisher: "Ving" developer: "Ving" @@ -71866,12 +71789,6 @@ SLPS-02374: name: "Heiwa Pachinko Graffiti Vol. 1 (Japan)" controllers: - DigitalController - codes: - - SLPS-02374 - - SLPS-02356 - - SLPS-02359 - - SLPS-02373 - - SLPS-02378 metadata: publisher: "Aqua Rouge" developer: "Aqua Rouge" @@ -72469,8 +72386,7 @@ SCPS-10012: controllers: - DigitalController codes: - - SCPS-10012 - - SCPS-91016 + - HASH-1438F7A2BB29BE8A metadata: publisher: "Sony" developer: "Yuke's / Sugar & Rockets" @@ -72849,12 +72765,6 @@ SLPS-02351: controllers: - AnalogController - DigitalController - codes: - - SLPS-02351 - - SLPS-02356 - - SLPS-02359 - - SLPS-02373 - - SLPS-02378 metadata: publisher: "MediaWorks" developer: "Japan Media Programming" @@ -73135,12 +73045,6 @@ SLPS-02355: controllers: - AnalogController - DigitalController - codes: - - SLPS-02355 - - SLPS-02356 - - SLPS-02359 - - SLPS-02373 - - SLPS-02378 metadata: publisher: "Sunsoft" developer: "Sunsoft" @@ -74569,7 +74473,6 @@ SLPM-86377: codes: - SLPM-86377 - SLPS-01547 - - SLPM-86375 metadata: publisher: "Enix Corporation" developer: "tri-Ace" @@ -75552,12 +75455,6 @@ SLPS-02358: controllers: - AnalogController - DigitalController - codes: - - SLPS-02358 - - SLPS-02356 - - SLPS-02359 - - SLPS-02373 - - SLPS-02378 metadata: publisher: "KSS" developer: "KSS" @@ -85098,12 +84995,6 @@ SLPS-02370: controllers: - AnalogController - DigitalController - codes: - - SLPS-02370 - - SLPS-02356 - - SLPS-02359 - - SLPS-02373 - - SLPS-02378 metadata: publisher: "Kodansha" developer: "Kodansha" @@ -90518,10 +90409,6 @@ SLPS-02344: codes: - SLPS-02344 - SLPS-02926 - - SLPS-02356 - - SLPS-02359 - - SLPS-02373 - - SLPS-02378 metadata: publisher: "Warashi" developer: "Same Creative Inc" @@ -90544,10 +90431,6 @@ SLPS-02343: codes: - SLPS-02343 - SLPS-02925 - - SLPS-02356 - - SLPS-02359 - - SLPS-02373 - - SLPS-02378 metadata: publisher: "Warashi" developer: "Same Creative Inc" @@ -92682,12 +92565,6 @@ SLPS-02376: settings: dmaMaxSliceTicks: 100 dmaHaltTicks: 200 - codes: - - SLPS-02376 - - SLPS-02356 - - SLPS-02359 - - SLPS-02373 - - SLPS-02378 metadata: publisher: "Nippon Ichi Software" developer: "Nippon Ichi Software" @@ -96983,6 +96860,8 @@ SLPS-00128: name: "Makeruna! Makendou 2 (Japan)" controllers: - DigitalController + codes: + - HASH-F267FA5D5A1CB78B metadata: publisher: "Datam Polystar" developer: "Fill In Cafe" @@ -97167,7 +97046,6 @@ SLPS-01136: codes: - SLPS-01136 - SLPS-91100 - - SLPS-91108 metadata: publisher: "Axela" developer: "Break" @@ -97195,7 +97073,6 @@ SLPS-01137: codes: - SLPS-01137 - SLPS-91101 - - SLPS-91108 metadata: publisher: "Axela" developer: "Break" @@ -97223,7 +97100,6 @@ SLPS-01138: codes: - SLPS-01138 - SLPS-91102 - - SLPS-91108 metadata: publisher: "Axela" developer: "Break" @@ -98137,12 +98013,6 @@ SLPS-02368: - DigitalController traits: - DisableAutoAnalogMode # Analog sticks do nothing. - codes: - - SLPS-02368 - - SLPS-02356 - - SLPS-02359 - - SLPS-02373 - - SLPS-02378 metadata: publisher: "Capcom" developer: "Capcom / Value Wave" @@ -101336,7 +101206,6 @@ SLPM-86114: codes: - SLPM-86114 - SLPM-87411 - - SLPM-87413 metadata: publisher: "Konami" developer: "KCE Japan" @@ -101366,7 +101235,6 @@ SLPM-86115: codes: - SLPM-86115 - SLPM-87412 - - SLPM-87413 metadata: publisher: "Konami" developer: "KCE Japan" @@ -101519,6 +101387,7 @@ SCPS-45414: - SLPM-86249 - SLPM-86472 - SLPM-87326 + - SLPM-87413 metadata: publisher: "Konami" developer: "KCE Japan" @@ -114866,6 +114735,8 @@ SLPS-00050: name: "Night Striker (Japan)" controllers: - DigitalController + codes: + - HASH-6AA77469D08E6515 metadata: publisher: "Xing Entertainment" developer: "Xing Entertainment/ Fill in Cafe" @@ -116147,9 +116018,6 @@ SLPM-86376: controllers: - AnalogController - DigitalController - codes: - - SLPM-86376 - - SLPM-86375 metadata: publisher: "Enix Corporation" developer: "tri-Ace" @@ -117806,9 +117674,7 @@ SLPS-00093: controllers: - DigitalController codes: - - SLPS-00093 - - SLPS-01568 - - SLPS-03116 + - HASH-AFE12D37382D032D metadata: publisher: "Sunsoft" developer: "Sunsoft" @@ -118510,6 +118376,8 @@ SLPS-00089: name: "Oni Taiji!!, The - Mezase! Nidaime Momotarou (Japan)" controllers: - DigitalController + codes: + - HASH-2084FC8A435830CD metadata: publisher: "Nippon Ichi Software" developer: "O-Two" @@ -118992,6 +118860,8 @@ SLPM-87047: controllers: - AnalogController - DigitalController + codes: + - HASH-782D7340E38E804E metadata: publisher: "Takumi" developer: "Takumi" @@ -120498,12 +120368,6 @@ SLPS-02345: controllers: - AnalogController - DigitalController - codes: - - SLPS-02345 - - SLPS-02356 - - SLPS-02359 - - SLPS-02373 - - SLPS-02378 metadata: publisher: "Namco Ltd" developer: "Namco Hometek" @@ -121641,6 +121505,8 @@ SLPS-00037: name: "Pachio-kun - Pachinko Land Adventure (Japan)" controllers: - DigitalController + codes: + - HASH-70E4238F26DC3BB4 metadata: publisher: "Coconuts Japan" developer: "Marionette" @@ -123228,12 +123094,6 @@ SLPS-02357: controllers: - AnalogController - DigitalController - codes: - - SLPS-02357 - - SLPS-02356 - - SLPS-02359 - - SLPS-02373 - - SLPS-02378 metadata: publisher: "Nihon Telenet" developer: "Nihon Telenet" @@ -128904,12 +128764,6 @@ SLPS-02360: controllers: - AnalogController - DigitalController - codes: - - SLPS-02360 - - SLPS-02356 - - SLPS-02359 - - SLPS-02373 - - SLPS-02378 metadata: publisher: "Arc System Works Co" developer: "Arc System Works Co" @@ -129195,10 +129049,6 @@ SLPS-02347: codes: - SLPS-02347 - SLPS-02938 - - SLPS-02356 - - SLPS-02359 - - SLPS-02373 - - SLPS-02378 metadata: publisher: "Athena" developer: "Athena" @@ -130253,12 +130103,6 @@ ESPM-70003: controllers: - DigitalController - GunCon - codes: - - ESPM-70003 - - ESPM-70004 - - ESPM-70005 - - ESPM-70006 - - ESPM-70007 metadata: publisher: "Sony Music Entertainment Incorporated" developer: "Sony Music Entertainment Incorporated" @@ -138663,10 +138507,6 @@ SLPM-87269: codes: - SLPM-87269 - SLPS-02379 - - SLPS-02356 - - SLPS-02359 - - SLPS-02373 - - SLPS-02378 metadata: publisher: "Capcom" developer: "Capcom/ OeRSTED" @@ -138745,7 +138585,6 @@ SLPS-91094: codes: - SLPS-91094 - SLPS-91107 - - SLPS-91108 metadata: publisher: "Capcom" developer: "Capcom" @@ -138837,7 +138676,6 @@ SLPM-87315: - SLPM-87315 - SLPS-00902 - SLPS-91106 - - SLPS-91108 metadata: publisher: "Capcom" developer: "Capcom" @@ -140977,10 +140815,6 @@ SLPS-01257: codes: - SLPS-01257 - SLPS-02372 - - SLPS-02356 - - SLPS-02359 - - SLPS-02373 - - SLPS-02378 metadata: publisher: "Visit" developer: "Break" @@ -153732,6 +153566,8 @@ SLPS-00012: name: "Space Griffon VF-9 (Japan)" controllers: - DigitalController + codes: + - HASH-A9253E2383B76F5A metadata: publisher: "Panther Software" developer: "Panther Software" @@ -158829,9 +158665,6 @@ SLPS-91105: name: "Street Fighter Zero 2' (Japan)" controllers: - DigitalController - codes: - - SLPS-91105 - - SLPS-91108 metadata: publisher: "Capcom" developer: "Capcom" @@ -166370,12 +166203,6 @@ SLPS-02366: controllers: - AnalogController - DigitalController - codes: - - SLPS-02366 - - SLPS-02356 - - SLPS-02359 - - SLPS-02373 - - SLPS-02378 metadata: publisher: "Techno Soleil" developer: "Hamster Co" @@ -170893,7 +170720,6 @@ LSP-907363: - LSP-907363 - LSP90736300 - LSP-90736300 - - LSP90736300 metadata: publisher: "Lightspan" developer: "Lightspan" @@ -171579,10 +171405,14 @@ SLPM-86053: multitap: false linkCable: false SLPS-00065: - name: "Tokimeki Memorial - Forever with You (Japan) (Rev 4)" + name: "Tokimeki Memorial - Forever with You (Japan)" controllers: - DigitalController - PlayStationMouse + codes: + - HASH-66FFEBF68485E094 # Rev 1 + - HASH-D3B543537941D0C9 # Rev 2 + - HASH-6A9968C8852447A8 # Rev 4 metadata: publisher: "Konami" developer: "Konami Computer Entertainment Tokyo (KCET)" @@ -171598,7 +171428,7 @@ SLPS-00065: multitap: false linkCable: false SLPS-00064: - name: "Tokimeki Memorial - Forever with You (Japan) (Shokai Genteiban) (Rev 1)" + name: "Tokimeki Memorial - Forever with You (Japan) (Shokai Genteiban)" controllers: - DigitalController - PlayStationMouse @@ -172504,7 +172334,6 @@ SLPM-86373: codes: - SLPM-86373 - SLPM-86374 - - SLPM-86375 metadata: publisher: "Enix Corporation" developer: "tri-Ace" @@ -174287,12 +174116,6 @@ SLPS-02350: controllers: - AnalogController - DigitalController - codes: - - SLPS-02350 - - SLPS-02356 - - SLPS-02359 - - SLPS-02373 - - SLPS-02378 metadata: publisher: "Whoopee Camp" developer: "Whoopee Camp" @@ -175437,12 +175260,6 @@ SLPS-02361: - NeGcon traits: - ForcePGXPVertexCache - codes: - - SLPS-02361 - - SLPS-02356 - - SLPS-02359 - - SLPS-02373 - - SLPS-02378 metadata: publisher: "Atlus Co" developer: "Cave" @@ -177107,6 +176924,8 @@ SLPS-00018: name: "Twin Goddesses (Japan)" controllers: - DigitalController + codes: + - HASH-7C5648B2D2C5AADF metadata: publisher: "Polygram Magic of Japan" developer: "Polygram Magic of Japan" @@ -178154,6 +177973,8 @@ SLPS-00032: name: "Uchuu Seibutsu Flopon-kun P! (Japan)" controllers: - DigitalController + codes: + - HASH-17C26DD8A7B6744A metadata: publisher: "Asmik Ace Entertainment, Inc" developer: "Warp" @@ -179155,12 +178976,6 @@ SLPS-02362: controllers: - AnalogController - DigitalController - codes: - - SLPS-02362 - - SLPS-02356 - - SLPS-02359 - - SLPS-02373 - - SLPS-02378 metadata: publisher: "Visit" developer: "Billiken Soft" @@ -179520,6 +179335,8 @@ SLPS-00103: name: "V-Tennis (Japan)" controllers: - DigitalController + codes: + - HASH-100C572E7CFFDE73 metadata: publisher: "Tonkin House" developer: "Tonkin House" @@ -179954,10 +179771,6 @@ SCPS-45486: - SCPS-45486 - SLPM-87393 - SLPS-02377 - - SLPS-02356 - - SLPS-02359 - - SLPS-02373 - - SLPS-02378 metadata: publisher: "Squaresoft" developer: "Squaresoft" @@ -180025,9 +179838,6 @@ SLPM-86379: - DigitalController traits: - ForcePGXPCPUMode # Fixes wobble in screen transitions/battle stages. - codes: - - SLPM-86379 - - SLPM-86375 metadata: publisher: "Enix Corporation" developer: "tri-Ace" @@ -180054,9 +179864,6 @@ SLPM-86371: - DigitalController traits: - ForcePGXPCPUMode # Fixes wobble in screen transitions/battle stages. - codes: - - SLPM-86371 - - SLPM-86375 metadata: publisher: "Enix Corporation" developer: "tri-Ace" @@ -180083,9 +179890,6 @@ SLPM-86380: - DigitalController traits: - ForcePGXPCPUMode # Fixes wobble in screen transitions/battle stages. - codes: - - SLPM-86380 - - SLPM-86375 metadata: publisher: "Enix Corporation" developer: "tri-Ace" @@ -180112,9 +179916,6 @@ SLPM-86372: - DigitalController traits: - ForcePGXPCPUMode # Fixes wobble in screen transitions/battle stages. - codes: - - SLPM-86372 - - SLPM-86375 metadata: publisher: "Enix Corporation" developer: "tri-Ace" @@ -183066,10 +182867,6 @@ SLPS-01695: codes: - SLPS-01695 - SLPS-02346 - - SLPS-02356 - - SLPS-02359 - - SLPS-02373 - - SLPS-02378 metadata: publisher: "Athena" developer: "Art Co" @@ -184396,12 +184193,6 @@ SLPS-02352: controllers: - AnalogController - DigitalController - codes: - - SLPS-02352 - - SLPS-02356 - - SLPS-02359 - - SLPS-02373 - - SLPS-02378 metadata: publisher: "Escot" developer: "Systemsoft / Dual" @@ -186815,12 +186606,6 @@ SLPS-02349: controllers: - AnalogController - DigitalController - codes: - - SLPS-02349 - - SLPS-02356 - - SLPS-02359 - - SLPS-02373 - - SLPS-02378 metadata: publisher: "Soliton" developer: "Thunder Stone Japan" @@ -192382,6 +192167,8 @@ SLPS-00083: name: "Zero Divide (Japan)" controllers: - DigitalController + codes: + - HASH-D967B8454178FF39 metadata: publisher: "Zoom Inc" developer: "Zoom Inc" diff --git a/scripts/packaging/arch/PKGBUILD b/scripts/packaging/arch/PKGBUILD index 35a47a5ac..b1db54fc2 100644 --- a/scripts/packaging/arch/PKGBUILD +++ b/scripts/packaging/arch/PKGBUILD @@ -120,8 +120,8 @@ package() { install -Dm755 scripts/packaging/duckstation-qt "${pkgdir}/usr/bin/duckstation-qt" # install desktop file and icon - install -Dm644 scripts/${_desktopname}.desktop "${pkgdir}/usr/share/applications/${_desktopname}.desktop" - install -Dm644 scripts/${_desktopname}.png "${pkgdir}/usr/share/icons/hicolor/512x512/apps/${_desktopname}.png" + install -Dm644 scripts/packaging/${_desktopname}.desktop "${pkgdir}/usr/share/applications/${_desktopname}.desktop" + install -Dm644 scripts/packaging/${_desktopname}.png "${pkgdir}/usr/share/icons/hicolor/512x512/apps/${_desktopname}.png" # install license install -Dm644 LICENSE "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE" diff --git a/scripts/packaging/fedora/duckstation.spec b/scripts/packaging/fedora/duckstation.spec index 0959cb9a1..a8f8ca4d9 100644 --- a/scripts/packaging/fedora/duckstation.spec +++ b/scripts/packaging/fedora/duckstation.spec @@ -71,8 +71,8 @@ ninja -C build %{?_smp_mflags} rm -fr %{buildroot} ninja -C build install install -Dm755 scripts/packaging/duckstation-qt %{buildroot}/usr/bin/duckstation-qt -install -Dm644 scripts/org.duckstation.DuckStation.png %{buildroot}/usr/share/icons/hicolor/512x512/apps/org.duckstation.DuckStation.png -install -Dm644 scripts/org.duckstation.DuckStation.desktop %{buildroot}/usr/share/applications/org.duckstation.DuckStation.desktop +install -Dm644 scripts/packaging/org.duckstation.DuckStation.png %{buildroot}/usr/share/icons/hicolor/512x512/apps/org.duckstation.DuckStation.png +install -Dm644 scripts/packaging/org.duckstation.DuckStation.desktop %{buildroot}/usr/share/applications/org.duckstation.DuckStation.desktop %files %license LICENSE diff --git a/src/common-tests/rectangle_tests.cpp b/src/common-tests/rectangle_tests.cpp index 803938684..3ccb7ea3f 100644 --- a/src/common-tests/rectangle_tests.cpp +++ b/src/common-tests/rectangle_tests.cpp @@ -78,3 +78,59 @@ TEST(Rectangle, RelationalOperators) ASSERT_FALSE(r1.eq(r2)); } +TEST(Rectangle, ValidRectangles) +{ + static constexpr GSVector4i cases[] = { + GSVector4i::cxpr(1, 2, 3, 4), + GSVector4i::cxpr(-5, -10, -1, -2), + GSVector4i::cxpr(0, 0, 1, 1), + GSVector4i::cxpr(100, 200, 300, 400), + GSVector4i::cxpr(-1000, -2000, 500, 600), + GSVector4i::cxpr(5, 10, 6, 12), + GSVector4i::cxpr(-10, -20, -5, -15), + GSVector4i::cxpr(-5, 0, 5, 10), + GSVector4i::cxpr(-100, -200, 100, 200), + GSVector4i::cxpr(-1, -2, 0, 1), + }; + + for (GSVector4i tcase : cases) + { + ASSERT_TRUE(tcase.rvalid()); + ASSERT_FALSE(tcase.rempty()); + } +} + +TEST(Rectangle, InvalidRectangles) +{ + static constexpr GSVector4i cases[] = { + // left < right but not top < bottom + GSVector4i::cxpr(1, 4, 3, 2), + GSVector4i::cxpr(-5, -2, -1, -10), + GSVector4i::cxpr(0, 1, 1, 0), + GSVector4i::cxpr(100, 400, 300, 200), + GSVector4i::cxpr(-1000, 600, 500, -2000), + GSVector4i::cxpr(5, 12, 6, 10), + GSVector4i::cxpr(-10, -15, -5, -20), + GSVector4i::cxpr(-5, 10, 5, 0), + GSVector4i::cxpr(-100, 200, 100, -200), + GSVector4i::cxpr(-1, 1, 0, -2), + + // not left < right but top < bottom + GSVector4i::cxpr(3, 2, 1, 4), + GSVector4i::cxpr(-1, -10, -5, -2), + GSVector4i::cxpr(1, 0, 0, 1), + GSVector4i::cxpr(300, 200, 100, 400), + GSVector4i::cxpr(500, -2000, -1000, 600), + GSVector4i::cxpr(6, 10, 5, 12), + GSVector4i::cxpr(-5, -20, -10, -15), + GSVector4i::cxpr(5, 0, -5, 10), + GSVector4i::cxpr(100, -200, -100, 200), + GSVector4i::cxpr(0, -2, -1, 1), + }; + + for (GSVector4i tcase : cases) + { + ASSERT_FALSE(tcase.rvalid()); + ASSERT_TRUE(tcase.rempty()); + } +} diff --git a/src/common/assert.cpp b/src/common/assert.cpp index 9c39c8ba9..6c116b469 100644 --- a/src/common/assert.cpp +++ b/src/common/assert.cpp @@ -3,15 +3,17 @@ #include "assert.h" #include "crash_handler.h" + #include #include -#include -#if defined(_WIN32) +#ifdef _WIN32 + #include "windows_headers.h" #include #include -#endif + +#include #ifdef __clang__ #pragma clang diagnostic ignored "-Winvalid-noreturn" @@ -19,9 +21,8 @@ static std::mutex s_AssertFailedMutex; -static inline void FreezeThreads(void** ppHandle) +static HANDLE FreezeThreads() { -#if defined(_WIN32) HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); if (hSnapshot != INVALID_HANDLE_VALUE) { @@ -43,17 +44,12 @@ static inline void FreezeThreads(void** ppHandle) } } - *ppHandle = (void*)hSnapshot; -#else - *ppHandle = nullptr; -#endif + return hSnapshot; } -static inline void ResumeThreads(void* pHandle) +static void ResumeThreads(HANDLE hSnapshot) { -#if defined(_WIN32) - HANDLE hSnapshot = (HANDLE)pHandle; - if (pHandle != INVALID_HANDLE_VALUE) + if (hSnapshot != INVALID_HANDLE_VALUE) { THREADENTRY32 threadEntry; if (Thread32First(hSnapshot, &threadEntry)) @@ -73,21 +69,42 @@ static inline void ResumeThreads(void* pHandle) } CloseHandle(hSnapshot); } +} + #else + +#ifdef __ANDROID__ +// Define as a weak symbol for ancient devices that don't have it. +extern "C" __attribute__((weak)) void android_set_abort_message(const char*); +#endif + +[[noreturn]] ALWAYS_INLINE static void AbortWithMessage(const char* szMsg) +{ +#ifndef __ANDROID__ + std::fputs(szMsg, stderr); + CrashHandler::WriteDumpForCaller(); + std::fputs("Aborting application.\n", stderr); + std::fflush(stderr); + std::abort(); +#else + if (&android_set_abort_message) + android_set_abort_message(szMsg); + + std::abort(); #endif } +#endif // _WIN32 + void Y_OnAssertFailed(const char* szMessage, const char* szFunction, const char* szFile, unsigned uLine) { - std::lock_guard guard(s_AssertFailedMutex); - - void* pHandle; - FreezeThreads(&pHandle); - char szMsg[512]; std::snprintf(szMsg, sizeof(szMsg), "%s in function %s (%s:%u)\n", szMessage, szFunction, szFile, uLine); #if defined(_WIN32) + std::unique_lock lock(s_AssertFailedMutex); + HANDLE pHandle = FreezeThreads(); + SetConsoleTextAttribute(GetStdHandle(STD_ERROR_HANDLE), FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY); WriteConsoleA(GetStdHandle(STD_ERROR_HANDLE), szMsg, static_cast(std::strlen(szMsg)), NULL, NULL); OutputDebugStringA(szMsg); @@ -107,28 +124,22 @@ void Y_OnAssertFailed(const char* szMessage, const char* szFunction, const char* CrashHandler::WriteDumpForCaller(); TerminateProcess(GetCurrentProcess(), 0xBAADC0DE); } -#else - std::fputs(szMsg, stderr); - CrashHandler::WriteDumpForCaller(); - std::fputs("Aborting application.\n", stderr); - std::fflush(stderr); - std::abort(); -#endif ResumeThreads(pHandle); +#else + AbortWithMessage(szMsg); +#endif } [[noreturn]] void Y_OnPanicReached(const char* szMessage, const char* szFunction, const char* szFile, unsigned uLine) { - std::lock_guard guard(s_AssertFailedMutex); - - void* pHandle; - FreezeThreads(&pHandle); - char szMsg[512]; std::snprintf(szMsg, sizeof(szMsg), "%s in function %s (%s:%u)\n", szMessage, szFunction, szFile, uLine); #if defined(_WIN32) + std::unique_lock guard(s_AssertFailedMutex); + HANDLE pHandle = FreezeThreads(); + SetConsoleTextAttribute(GetStdHandle(STD_ERROR_HANDLE), FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY); WriteConsoleA(GetStdHandle(STD_ERROR_HANDLE), szMsg, static_cast(std::strlen(szMsg)), NULL, NULL); OutputDebugStringA(szMsg); @@ -145,13 +156,9 @@ void Y_OnAssertFailed(const char* szMessage, const char* szFunction, const char* CrashHandler::WriteDumpForCaller(); TerminateProcess(GetCurrentProcess(), 0xBAADC0DE); -#else - std::fputs(szMsg, stderr); - CrashHandler::WriteDumpForCaller(); - std::fputs("Aborting application.\n", stderr); - std::fflush(stderr); - std::abort(); -#endif ResumeThreads(pHandle); +#else + AbortWithMessage(szMsg); +#endif } diff --git a/src/common/assert.h b/src/common/assert.h index fd7d4d884..2578f3fa1 100644 --- a/src/common/assert.h +++ b/src/common/assert.h @@ -9,27 +9,31 @@ void Y_OnAssertFailed(const char* szMessage, const char* szFunction, const char* [[noreturn]] void Y_OnPanicReached(const char* szMessage, const char* szFunction, const char* szFile, unsigned uLine); #define Assert(expr) \ - if (!(expr)) \ + do \ { \ - Y_OnAssertFailed("Assertion failed: '" #expr "'", __FUNCTION__, __FILE__, __LINE__); \ - } + if (!(expr)) \ + Y_OnAssertFailed("Assertion failed: '" #expr "'", __FUNCTION__, __FILE__, __LINE__); \ + } while (0) #define AssertMsg(expr, msg) \ - if (!(expr)) \ + do \ { \ - Y_OnAssertFailed("Assertion failed: '" msg "'", __FUNCTION__, __FILE__, __LINE__); \ - } + if (!(expr)) \ + Y_OnAssertFailed("Assertion failed: '" msg "'", __FUNCTION__, __FILE__, __LINE__); \ + } while (0) #if defined(_DEBUG) || defined(_DEVEL) #define DebugAssert(expr) \ - if (!(expr)) \ + do \ { \ - Y_OnAssertFailed("Debug assertion failed: '" #expr "'", __FUNCTION__, __FILE__, __LINE__); \ - } + if (!(expr)) \ + Y_OnAssertFailed("Debug assertion failed: '" #expr "'", __FUNCTION__, __FILE__, __LINE__); \ + } while (0) #define DebugAssertMsg(expr, msg) \ - if (!(expr)) \ + do \ { \ - Y_OnAssertFailed("Debug assertion failed: '" msg "'", __FUNCTION__, __FILE__, __LINE__); \ - } + if (!(expr)) \ + Y_OnAssertFailed("Debug assertion failed: '" msg "'", __FUNCTION__, __FILE__, __LINE__); \ + } while (0) #else #define DebugAssert(expr) #define DebugAssertMsg(expr, msg) diff --git a/src/common/crash_handler.cpp b/src/common/crash_handler.cpp index 724427446..a432791b5 100644 --- a/src/common/crash_handler.cpp +++ b/src/common/crash_handler.cpp @@ -367,7 +367,7 @@ void CrashHandler::WriteDumpForCaller() LogCallstack(0, nullptr); } -#else +#elif !defined(__ANDROID__) bool CrashHandler::Install(CleanupHandler cleanup_handler) { diff --git a/src/common/file_system.cpp b/src/common/file_system.cpp index 20e757cb1..be3065487 100644 --- a/src/common/file_system.cpp +++ b/src/common/file_system.cpp @@ -2788,6 +2788,10 @@ bool FileSystem::SetPathCompression(const char* path, bool enable) return false; } +#endif + +#ifdef HAS_POSIX_FILE_LOCK + static bool SetLock(int fd, bool lock, bool block, Error* error) { // We want to lock the whole file. @@ -2814,7 +2818,7 @@ static bool SetLock(int fd, bool lock, bool block, Error* error) bool res; for (;;) { - res = (lockf(fd, lock ? (block ? F_TLOCK : F_LOCK) : F_ULOCK, 0) == 0); + res = (lockf(fd, lock ? (block ? F_LOCK : F_TLOCK) : F_ULOCK, 0) == 0); if (!res && errno == EINTR) continue; else diff --git a/src/common/file_system.h b/src/common/file_system.h index c87c1d658..654fa28e2 100644 --- a/src/common/file_system.h +++ b/src/common/file_system.h @@ -155,7 +155,12 @@ bool CommitAtomicRenamedFile(AtomicRenamedFile& file, Error* error); void DiscardAtomicRenamedFile(AtomicRenamedFile& file); /// Abstracts a POSIX file lock. -#ifndef _WIN32 +#if !defined(_WIN32) && !defined(__ANDROID__) +#define HAS_POSIX_FILE_LOCK 1 +#endif + +#ifdef HAS_POSIX_FILE_LOCK + class POSIXLock { public: @@ -175,6 +180,7 @@ public: private: int m_fd; }; + #endif std::optional> ReadBinaryFile(const char* path, Error* error = nullptr); diff --git a/src/common/gsvector.cpp b/src/common/gsvector.cpp index 392b9da44..787fb7d66 100644 --- a/src/common/gsvector.cpp +++ b/src/common/gsvector.cpp @@ -65,3 +65,164 @@ void GSMatrix2x2::store(void* m) { std::memcpy(m, E, sizeof(E)); } + +GSMatrix4x4::GSMatrix4x4(float e00, float e01, float e02, float e03, float e10, float e11, float e12, float e13, + float e20, float e21, float e22, float e23, float e30, float e31, float e32, float e33) +{ + E[0][0] = e00; + E[0][1] = e01; + E[0][2] = e02; + E[0][3] = e03; + E[1][0] = e10; + E[1][1] = e11; + E[1][2] = e12; + E[1][3] = e13; + E[2][0] = e20; + E[2][1] = e21; + E[2][2] = e22; + E[2][3] = e23; + E[3][0] = e30; + E[3][1] = e31; + E[3][2] = e32; + E[3][3] = e33; +} + + GSMatrix4x4::GSMatrix4x4(const GSMatrix2x2& m) +{ + E[0][0] = m.E[0][0]; + E[0][1] = m.E[0][1]; + E[0][2] = 0.0f; + E[0][3] = 0.0f; + E[1][0] = m.E[1][0]; + E[1][1] = m.E[1][1]; + E[1][2] = 0.0f; + E[1][3] = 0.0f; + E[2][0] = 0.0f; + E[2][1] = 0.0f; + E[2][2] = 1.0f; + E[2][3] = 0.0f; + E[3][0] = 0.0f; + E[3][1] = 0.0f; + E[3][2] = 0.0f; + E[3][3] = 1.0f; + } + +GSMatrix4x4 GSMatrix4x4::operator*(const GSMatrix4x4& m) const +{ + // This isn't speedy by any means, but it's not hot code either. + GSMatrix4x4 res; + +#define MultRC(rw, cl) E[rw][0] * m.E[0][cl] + E[rw][1] * m.E[1][cl] + E[rw][2] * m.E[2][cl] + E[rw][3] * m.E[3][cl] + + res.E[0][0] = MultRC(0, 0); + res.E[0][1] = MultRC(0, 1); + res.E[0][2] = MultRC(0, 2); + res.E[0][3] = MultRC(0, 3); + res.E[1][0] = MultRC(1, 0); + res.E[1][1] = MultRC(1, 1); + res.E[1][2] = MultRC(1, 2); + res.E[1][3] = MultRC(1, 3); + res.E[2][0] = MultRC(2, 0); + res.E[2][1] = MultRC(2, 1); + res.E[2][2] = MultRC(2, 2); + res.E[2][3] = MultRC(2, 3); + res.E[3][0] = MultRC(3, 0); + res.E[3][1] = MultRC(3, 1); + res.E[3][2] = MultRC(3, 2); + res.E[3][3] = MultRC(3, 3); + +#undef MultRC + + return res; +} + +GSVector4 GSMatrix4x4::operator*(const GSVector4& v) const +{ + const GSVector4 r0 = row(0); + const GSVector4 r1 = row(1); + const GSVector4 r2 = row(2); + const GSVector4 r3 = row(4); + + return GSVector4(r0.dot(v), r1.dot(v), r2.dot(v), r3.dot(v)); +} + +GSMatrix4x4 GSMatrix4x4::Identity() +{ + GSMatrix4x4 res; + +#define MultRC(rw, cl) E[rw][0] * m.E[0][cl] + E[rw][1] * m.E[1][cl] + E[rw][2] * m.E[2][cl] + E[rw][3] * m.E[3][cl] + + res.E[0][0] = 1.0f; + res.E[0][1] = 0.0f; + res.E[0][2] = 0.0f; + res.E[0][3] = 0.0f; + res.E[1][0] = 0.0f; + res.E[1][1] = 1.0f; + res.E[1][2] = 0.0f; + res.E[1][3] = 0.0f; + res.E[2][0] = 0.0f; + res.E[2][1] = 0.0f; + res.E[2][2] = 1.0f; + res.E[2][3] = 0.0f; + res.E[3][0] = 0.0f; + res.E[3][1] = 0.0f; + res.E[3][2] = 0.0f; + res.E[3][3] = 1.0f; + + return res; +} + +GSMatrix4x4 GSMatrix4x4::RotationX(float angle_in_radians) +{ + const float sin_angle = std::sin(angle_in_radians); + const float cos_angle = std::cos(angle_in_radians); + + return GSMatrix4x4(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, cos_angle, -sin_angle, 0.0f, 0.0f, sin_angle, cos_angle, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f); +} + +GSMatrix4x4 GSMatrix4x4::RotationY(float angle_in_radians) +{ + const float sin_angle = std::sin(angle_in_radians); + const float cos_angle = std::cos(angle_in_radians); + + return GSMatrix4x4(cos_angle, 0.0f, sin_angle, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, -sin_angle, 0.0f, cos_angle, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f); +} + +GSMatrix4x4 GSMatrix4x4::RotationZ(float angle_in_radians) +{ + const float sin_angle = std::sin(angle_in_radians); + const float cos_angle = std::cos(angle_in_radians); + + return GSMatrix4x4(cos_angle, -sin_angle, 0.0f, 0.0f, sin_angle, cos_angle, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f); +} + +GSMatrix4x4 GSMatrix4x4::OffCenterOrthographicProjection(float left, float top, float right, float bottom, float zNear, + float zFar) +{ + return GSMatrix4x4(2.0f / (right - left), 0.0f, 0.0f, (left + right) / (left - right), 0.0f, 2.0f / (top - bottom), + 0.0f, (top + bottom) / (bottom - top), 0.0f, 0.0f, 1.0f / (zNear - zFar), zNear / (zNear - zFar), + 0.0f, 0.0f, 0.0f, 1.0f); +} + +GSMatrix4x4 GSMatrix4x4::OffCenterOrthographicProjection(float width, float height, float zNear, float zFar) +{ + return OffCenterOrthographicProjection(0.0f, 0.0f, width, height, zNear, zFar); +} + +GSVector4 GSMatrix4x4::row(size_t i) const +{ + return GSVector4::load(&E[i][0]); +} + +GSVector4 GSMatrix4x4::col(size_t i) const +{ + return GSVector4(E[0][i], E[1][i], E[2][i], E[3][i]); +} + +void GSMatrix4x4::store(void* m) +{ + std::memcpy(m, &E[0][0], sizeof(E)); +} diff --git a/src/common/gsvector.h b/src/common/gsvector.h index 3bccfa3ca..106e29b35 100644 --- a/src/common/gsvector.h +++ b/src/common/gsvector.h @@ -37,3 +37,33 @@ public: alignas(8) float E[2][2]; }; + +class alignas(VECTOR_ALIGNMENT) GSMatrix4x4 +{ +public: + GSMatrix4x4() = default; + GSMatrix4x4(float e00, float e01, float e02, float e03, float e10, float e11, float e12, float e13, float e20, + float e21, float e22, float e23, float e30, float e31, float e32, float e33); + GSMatrix4x4(const GSMatrix2x2& m); + + GSMatrix4x4 operator*(const GSMatrix4x4& m) const; + + GSVector4 operator*(const GSVector4& v) const; + + static GSMatrix4x4 Identity(); + + static GSMatrix4x4 RotationX(float angle_in_radians); + static GSMatrix4x4 RotationY(float angle_in_radians); + static GSMatrix4x4 RotationZ(float angle_in_radians); + + static GSMatrix4x4 OffCenterOrthographicProjection(float left, float top, float right, float bottom, float zNear, + float zFar); + static GSMatrix4x4 OffCenterOrthographicProjection(float width, float height, float zNear, float zFar); + + GSVector4 row(size_t i) const; + GSVector4 col(size_t i) const; + + void store(void* m); + + float E[4][4]; +}; diff --git a/src/common/gsvector_neon.h b/src/common/gsvector_neon.h index c8efec076..dd52f8fc9 100644 --- a/src/common/gsvector_neon.h +++ b/src/common/gsvector_neon.h @@ -1171,17 +1171,21 @@ public: ALWAYS_INLINE bool rempty() const { -#ifdef CPU_ARCH_ARM64 - return (vminv_u32(vreinterpret_u32_s32(vget_low_s32(lt32(zwzw())))) == 0); -#else - return (vget_lane_u64(vreinterpret_u64_u32(vreinterpret_u32_s32(vget_low_s32(lt32(zwzw())))), 0) == 0); -#endif + // !any((x, y) < (z, w)) i.e. !not_empty + return (vget_lane_u64(vreinterpret_u64_u32(vclt_s32(vget_low_s32(v4s), vget_high_s32(v4s))), 0) != + 0xFFFFFFFFFFFFFFFFULL); + } + + ALWAYS_INLINE bool rvalid() const + { + // !all((x, y) >= (z, w)) + return (vget_lane_u64(vreinterpret_u64_u32(vcge_s32(vget_low_s32(v4s), vget_high_s32(v4s))), 0) == 0); } ALWAYS_INLINE GSVector4i runion(const GSVector4i& a) const { return min_s32(a).upl64(max_s32(a).srl<8>()); } ALWAYS_INLINE GSVector4i rintersect(const GSVector4i& a) const { return sat_s32(a); } - ALWAYS_INLINE bool rintersects(const GSVector4i& v) const { return !rintersect(v).rempty(); } + ALWAYS_INLINE bool rintersects(const GSVector4i& v) const { return rintersect(v).rvalid(); } ALWAYS_INLINE bool rcontains(const GSVector4i& v) const { return rintersect(v).eq(v); } ALWAYS_INLINE u32 rgba32() const { return static_cast(ps32().pu16().extract32<0>()); } @@ -2574,6 +2578,17 @@ public: #endif + ALWAYS_INLINE float dot(const GSVector4& v) const + { +#ifdef CPU_ARCH_ARM64 + return vaddvq_f32(vmulq_f32(v4s, v.v4s)); +#else + const float32x4_t dp = vmulq_f32(v4s, v.v4s); + float32x2_t tmp = vadd_f32(vget_low_f32(dp), vget_high_f32(dp)); // (x+z, y+w) + return vget_lane_f32(vadd_f32(tmp, vdup_lane_f32(tmp, 1)), 0); +#endif + } + ALWAYS_INLINE GSVector4 sat(const GSVector4& a, const GSVector4& b) const { return max(a).min(b); } ALWAYS_INLINE GSVector4 sat(const GSVector4& a) const diff --git a/src/common/gsvector_nosimd.h b/src/common/gsvector_nosimd.h index 6d73592d9..71df7f28b 100644 --- a/src/common/gsvector_nosimd.h +++ b/src/common/gsvector_nosimd.h @@ -958,7 +958,8 @@ public: ALWAYS_INLINE s32 width() const { return right - left; } ALWAYS_INLINE s32 height() const { return bottom - top; } - ALWAYS_INLINE bool rempty() const { return lt32(zwzw()).mask() != 0x00ff; } + ALWAYS_INLINE bool rempty() const { return (lt32(zwzw()).mask() != 0x00ff); } + ALWAYS_INLINE bool rvalid() const { return ((ge32(zwzw()).mask() & 0xff) == 0); } GSVector4i runion(const GSVector4i& v) const { @@ -966,7 +967,7 @@ public: } ALWAYS_INLINE GSVector4i rintersect(const GSVector4i& v) const { return sat_s32(v); } - ALWAYS_INLINE bool rintersects(const GSVector4i& v) const { return !rintersect(v).rempty(); } + ALWAYS_INLINE bool rintersects(const GSVector4i& v) const { return rintersect(v).rvalid(); } ALWAYS_INLINE bool rcontains(const GSVector4i& v) const { return rintersect(v).eq(v); } ALWAYS_INLINE u32 rgba32() const { return static_cast(ps32().pu16().extract32<0>()); } @@ -1845,20 +1846,9 @@ public: GSVector4 hsub(const GSVector4& v) const { return GSVector4(x - y, z - w, v.x - v.y, v.z - v.w); } - template - GSVector4 dp(const GSVector4& v) const + ALWAYS_INLINE float dot(const GSVector4& v) const { - float res = 0.0f; - if constexpr (i & 0x10) - res += x * v.x; - if constexpr (i & 0x20) - res += y * v.y; - if constexpr (i & 0x40) - res += z * v.z; - if constexpr (i & 0x80) - res += w * v.w; - return GSVector4((i & 0x01) ? res : 0.0f, (i & 0x02) ? res : 0.0f, (i & 0x04) ? res : 0.0f, - (i & 0x08) ? res : 0.0f); + return (x * v.x) + (y * v.y) + (z * v.z) + (w * v.w); } GSVector4 sat(const GSVector4& min, const GSVector4& max) const diff --git a/src/common/gsvector_sse.h b/src/common/gsvector_sse.h index c37d50d2f..0b875c6fc 100644 --- a/src/common/gsvector_sse.h +++ b/src/common/gsvector_sse.h @@ -1071,12 +1071,13 @@ public: ALWAYS_INLINE s32 width() const { return right - left; } ALWAYS_INLINE s32 height() const { return bottom - top; } - ALWAYS_INLINE bool rempty() const { return lt32(zwzw()).mask() != 0x00ff; } + ALWAYS_INLINE bool rempty() const { return (lt32(zwzw()).mask() != 0x00ff); } + ALWAYS_INLINE bool rvalid() const { return ((ge32(zwzw()).mask() & 0xff) == 0); } ALWAYS_INLINE GSVector4i runion(const GSVector4i& v) const { return min_s32(v).blend32<0xc>(max_s32(v)); } ALWAYS_INLINE GSVector4i rintersect(const GSVector4i& v) const { return sat_s32(v); } - ALWAYS_INLINE bool rintersects(const GSVector4i& v) const { return !rintersect(v).rempty(); } + ALWAYS_INLINE bool rintersects(const GSVector4i& v) const { return rintersect(v).rvalid(); } ALWAYS_INLINE bool rcontains(const GSVector4i& v) const { return rintersect(v).eq(v); } ALWAYS_INLINE u32 rgba32() const { return static_cast(ps32().pu16().extract32<0>()); } @@ -2007,10 +2008,16 @@ public: ALWAYS_INLINE GSVector4 hsub(const GSVector4& v) const { return GSVector4(_mm_hsub_ps(m, v.m)); } - template - ALWAYS_INLINE GSVector4 dp(const GSVector4& v) const + ALWAYS_INLINE float dot(const GSVector4& v) const { - return GSVector4(_mm_dp_ps(m, v.m, i)); +#ifdef CPU_ARCH_SSE41 + return _mm_cvtss_f32(_mm_dp_ps(m, v.m, 0xf1)); +#else + __m128 tmp = _mm_mul_ps(m, v.m); + tmp = _mm_add_ps(tmp, _mm_unpackhi_ps(tmp, tmp)); // (x+z, y+w, ..., ...) + tmp = _mm_add_ss(tmp, _mm_shuffle_ps(tmp, tmp, _MM_SHUFFLE(3, 2, 1, 1))); + return _mm_cvtss_f32(tmp); +#endif } ALWAYS_INLINE GSVector4 sat(const GSVector4& min, const GSVector4& max) const @@ -2393,6 +2400,11 @@ public: ALWAYS_INLINE GSVector2 zw() const { return GSVector2(_mm_shuffle_ps(m, m, _MM_SHUFFLE(3, 2, 3, 2))); } + ALWAYS_INLINE static GSVector4 xyxy(const GSVector2& l, const GSVector2& h) + { + return GSVector4(_mm_movelh_ps(l.m, h.m)); + } + #define VECTOR4_SHUFFLE_4(xs, xn, ys, yn, zs, zn, ws, wn) \ ALWAYS_INLINE GSVector4 xs##ys##zs##ws() const \ { \ diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp index 18cc8110d..adaa4bc42 100644 --- a/src/common/string_util.cpp +++ b/src/common/string_util.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #ifndef __APPLE__ @@ -442,7 +443,7 @@ bool StringUtil::ParseAssignmentString(const std::string_view str, std::string_v void StringUtil::EncodeAndAppendUTF8(std::string& s, char32_t ch) { - if (ch <= 0x7F) + if (ch <= 0x7F) [[likely]] { s.push_back(static_cast(static_cast(ch))); } @@ -472,17 +473,84 @@ void StringUtil::EncodeAndAppendUTF8(std::string& s, char32_t ch) } } +size_t StringUtil::GetEncodedUTF8Length(char32_t ch) +{ + if (ch <= 0x7F) [[likely]] + return 1; + else if (ch <= 0x07FF) + return 2; + else if (ch <= 0xFFFF) + return 3; + else if (ch <= 0x10FFFF) + return 4; + else + return 3; +} + +size_t StringUtil::EncodeAndAppendUTF8(void* utf8, size_t pos, size_t size, char32_t ch) +{ + u8* utf8_bytes = static_cast(utf8) + pos; + if (ch <= 0x7F) [[likely]] + { + if (pos == size) [[unlikely]] + return 0; + + utf8_bytes[0] = static_cast(ch); + return 1; + } + else if (ch <= 0x07FF) + { + if ((pos + 1) >= size) [[unlikely]] + return 0; + + utf8_bytes[0] = static_cast(0xc0 | static_cast((ch >> 6) & 0x1f)); + utf8_bytes[1] = static_cast(0x80 | static_cast((ch & 0x3f))); + return 2; + } + else if (ch <= 0xFFFF) + { + if ((pos + 3) >= size) [[unlikely]] + return 0; + + utf8_bytes[0] = static_cast(0xe0 | static_cast(((ch >> 12) & 0x0f))); + utf8_bytes[1] = static_cast(0x80 | static_cast(((ch >> 6) & 0x3f))); + utf8_bytes[2] = static_cast(0x80 | static_cast((ch & 0x3f))); + return 3; + } + else if (ch <= 0x10FFFF) + { + if ((pos + 4) >= size) [[unlikely]] + return 0; + + utf8_bytes[0] = static_cast(0xf0 | static_cast(((ch >> 18) & 0x07))); + utf8_bytes[1] = static_cast(0x80 | static_cast(((ch >> 12) & 0x3f))); + utf8_bytes[2] = static_cast(0x80 | static_cast(((ch >> 6) & 0x3f))); + utf8_bytes[3] = static_cast(0x80 | static_cast((ch & 0x3f))); + return 4; + } + else + { + if ((pos + 3) >= size) [[unlikely]] + return 0; + + utf8_bytes[0] = 0xefu; + utf8_bytes[1] = 0xbfu; + utf8_bytes[2] = 0xbdu; + return 3; + } +} + size_t StringUtil::DecodeUTF8(const void* bytes, size_t length, char32_t* ch) { const u8* s = reinterpret_cast(bytes); - if (s[0] < 0x80) + if (s[0] < 0x80) [[likely]] { *ch = s[0]; return 1; } else if ((s[0] & 0xe0) == 0xc0) { - if (length < 2) + if (length < 2) [[unlikely]] goto invalid; *ch = static_cast((static_cast(s[0] & 0x1f) << 6) | (static_cast(s[1] & 0x3f) << 0)); @@ -490,7 +558,7 @@ size_t StringUtil::DecodeUTF8(const void* bytes, size_t length, char32_t* ch) } else if ((s[0] & 0xf0) == 0xe0) { - if (length < 3) + if (length < 3) [[unlikely]] goto invalid; *ch = static_cast((static_cast(s[0] & 0x0f) << 12) | (static_cast(s[1] & 0x3f) << 6) | @@ -499,7 +567,7 @@ size_t StringUtil::DecodeUTF8(const void* bytes, size_t length, char32_t* ch) } else if ((s[0] & 0xf8) == 0xf0 && (s[0] <= 0xf4)) { - if (length < 4) + if (length < 4) [[unlikely]] goto invalid; *ch = static_cast((static_cast(s[0] & 0x07) << 18) | (static_cast(s[1] & 0x3f) << 12) | @@ -512,6 +580,82 @@ invalid: return 1; } +size_t StringUtil::EncodeAndAppendUTF16(void* utf16, size_t pos, size_t size, char32_t codepoint) +{ + u8* const utf16_bytes = std::assume_aligned(static_cast(utf16)) + (pos * sizeof(u16)); + if (codepoint <= 0xFFFF) [[likely]] + { + if (pos == size) [[unlikely]] + return 0; + + // surrogates are invalid + const u16 codepoint16 = + static_cast((codepoint >= 0xD800 && codepoint <= 0xDFFF) ? UNICODE_REPLACEMENT_CHARACTER : codepoint); + std::memcpy(utf16_bytes, &codepoint16, sizeof(codepoint16)); + return 1; + } + else if (codepoint <= 0x10FFFF) + { + if ((pos + 1) >= size) [[unlikely]] + return 0; + + codepoint -= 0x010000; + + const u16 low = static_cast(((static_cast(codepoint) >> 10) & 0x3FFu) + 0xD800); + const u16 high = static_cast((static_cast(codepoint) & 0x3FFu) + 0xDC00); + std::memcpy(utf16_bytes, &low, sizeof(high)); + std::memcpy(utf16_bytes + sizeof(u16), &high, sizeof(high)); + return 2; + } + else + { + // unrepresentable + constexpr u16 value = static_cast(UNICODE_REPLACEMENT_CHARACTER); + std::memcpy(utf16_bytes, &value, sizeof(value)); + return 1; + } +} + +size_t StringUtil::DecodeUTF16(const void* bytes, size_t pos, size_t length, char32_t* ch) +{ + const u8* const utf16_bytes = std::assume_aligned(static_cast(bytes)) + pos * sizeof(u16); + + u16 high; + std::memcpy(&high, utf16_bytes, sizeof(high)); + + // High surrogate? + if (high >= 0xD800 && high <= 0xDBFF) [[unlikely]] + { + if (length < 2) [[unlikely]] + { + // Missing low surrogate. + *ch = UNICODE_REPLACEMENT_CHARACTER; + return 1; + } + + u16 low; + std::memcpy(&low, utf16_bytes + sizeof(u16), sizeof(low)); + if (low >= 0xDC00 && low <= 0xDFFF) [[likely]] + { + *ch = static_cast(((static_cast(high) - 0xD800u) << 10) + ((static_cast(low) - 0xDC00)) + + 0x10000u); + return 2; + } + else + { + // Invalid high surrogate. + *ch = UNICODE_REPLACEMENT_CHARACTER; + return 2; + } + } + else + { + // Single 16-bit value. + *ch = static_cast(high); + return 1; + } +} + std::string StringUtil::Ellipsise(const std::string_view str, u32 max_length, const char* ellipsis /*= "..."*/) { std::string ret; diff --git a/src/common/string_util.h b/src/common/string_util.h index 8c01ec17e..707632e21 100644 --- a/src/common/string_util.h +++ b/src/common/string_util.h @@ -361,13 +361,22 @@ static constexpr char32_t UNICODE_REPLACEMENT_CHARACTER = 0xFFFD; /// Appends a UTF-16/UTF-32 codepoint to a UTF-8 string. void EncodeAndAppendUTF8(std::string& s, char32_t ch); +size_t EncodeAndAppendUTF8(void* utf8, size_t pos, size_t size, char32_t ch); +size_t GetEncodedUTF8Length(char32_t ch); -/// Decodes UTF-8 to a single codepoint, updating the position parameter. +/// Decodes UTF-8 to a single unicode codepoint. /// Returns the number of bytes the codepoint took in the original string. size_t DecodeUTF8(const void* bytes, size_t length, char32_t* ch); size_t DecodeUTF8(const std::string_view str, size_t offset, char32_t* ch); size_t DecodeUTF8(const std::string& str, size_t offset, char32_t* ch); +/// Appends a unicode codepoint to a UTF-16 string. +size_t EncodeAndAppendUTF16(void* utf16, size_t pos, size_t size, char32_t codepoint); + +/// Decodes UTF-16 to a single unicode codepoint. +/// Returns the number of bytes the codepoint took in the original string. +size_t DecodeUTF16(const void* bytes, size_t pos, size_t size, char32_t* codepoint); + // Replaces the end of a string with ellipsis if it exceeds the specified length. std::string Ellipsise(const std::string_view str, u32 max_length, const char* ellipsis = "..."); void EllipsiseInPlace(std::string& str, u32 max_length, const char* ellipsis = "..."); diff --git a/src/common/threading.cpp b/src/common/threading.cpp index 2012fcb22..025e1b991 100644 --- a/src/common/threading.cpp +++ b/src/common/threading.cpp @@ -7,6 +7,7 @@ #include "log.h" #include +#include #if !defined(_WIN32) && !defined(__APPLE__) #ifndef _GNU_SOURCE @@ -164,8 +165,9 @@ Threading::ThreadHandle Threading::ThreadHandle::GetForCallingThread() { ThreadHandle ret; #ifdef _WIN32 + ret.m_native_id = GetCurrentThreadId(); ret.m_native_handle = - (void*)OpenThread(THREAD_QUERY_INFORMATION | THREAD_SET_LIMITED_INFORMATION, FALSE, GetCurrentThreadId()); + (void*)OpenThread(THREAD_QUERY_INFORMATION | THREAD_SET_LIMITED_INFORMATION, FALSE, ret.m_native_id); #else ret.m_native_handle = (void*)pthread_self(); #ifdef __linux__ @@ -181,7 +183,9 @@ Threading::ThreadHandle& Threading::ThreadHandle::operator=(ThreadHandle&& handl if (m_native_handle) CloseHandle((HANDLE)m_native_handle); m_native_handle = handle.m_native_handle; + m_native_id = handle.m_native_id; handle.m_native_handle = nullptr; + handle.m_native_id = 0; #else m_native_handle = handle.m_native_handle; handle.m_native_handle = nullptr; @@ -207,6 +211,12 @@ Threading::ThreadHandle& Threading::ThreadHandle::operator=(const ThreadHandle& THREAD_QUERY_INFORMATION | THREAD_SET_LIMITED_INFORMATION, FALSE, 0)) { m_native_handle = (void*)new_handle; + m_native_id = handle.m_native_id; + } + else + { + m_native_handle = nullptr; + m_native_id = 0; } #else m_native_handle = handle.m_native_handle; @@ -275,6 +285,15 @@ bool Threading::ThreadHandle::SetAffinity(u64 processor_mask) const #endif } +bool Threading::ThreadHandle::IsCallingThread() const +{ +#ifdef _WIN32 + return (GetCurrentThreadId() == m_native_id); +#else + return pthread_equal(pthread_self(), (pthread_t)m_native_handle); +#endif +} + #ifdef __APPLE__ bool Threading::ThreadHandle::SetTimeConstraints(bool enabled, u64 period, u64 typical_time, u64 maximum_time) @@ -317,9 +336,9 @@ bool Threading::ThreadHandle::SetTimeConstraints(bool enabled, u64 period, u64 t Threading::Thread::Thread() = default; -Threading::Thread::Thread(Thread&& thread) : ThreadHandle(thread), m_stack_size(thread.m_stack_size) +Threading::Thread::Thread(Thread&& thread) : ThreadHandle(thread) { - thread.m_stack_size = 0; + m_stack_size = std::exchange(thread.m_stack_size, 0); } Threading::Thread::Thread(EntryPoint func) : ThreadHandle() diff --git a/src/common/threading.h b/src/common/threading.h index 4b8934f40..82bd52e61 100644 --- a/src/common/threading.h +++ b/src/common/threading.h @@ -53,6 +53,9 @@ public: /// Obviously, only works up to 64 processors. bool SetAffinity(u64 processor_mask) const; + /// Returns true if the calling thread matches this handle. + bool IsCallingThread() const; + #ifdef __APPLE__ /// Only available on MacOS, sets a period/maximum time for the scheduler. bool SetTimeConstraints(bool enabled, u64 period, u64 typical_time, u64 maximum_time); @@ -62,8 +65,9 @@ protected: void* m_native_handle = nullptr; // We need the thread ID for affinity adjustments on Linux. -#if defined(__linux__) +#if defined(_WIN32) || defined(__linux__) unsigned int m_native_id = 0; + u32 m_stack_size = 0; #endif }; @@ -104,7 +108,10 @@ protected: static void* ThreadProc(void* param); #endif +#if !defined(_WIN32) && !defined(__linux__) + // Stored in ThreadHandle to save 8 bytes. u32 m_stack_size = 0; +#endif }; /// A semaphore that requires a system call to wake/sleep. diff --git a/src/core/achievements.cpp b/src/core/achievements.cpp index 9df8d7c82..7da0e4b6c 100644 --- a/src/core/achievements.cpp +++ b/src/core/achievements.cpp @@ -2153,8 +2153,8 @@ static float IndicatorOpacity(const T& i) void Achievements::DrawGameOverlays() { - using ImGuiFullscreen::g_medium_font; using ImGuiFullscreen::LayoutScale; + using ImGuiFullscreen::UIStyle; if (!HasActiveGame() || !g_settings.achievements_overlays) return; @@ -2210,7 +2210,8 @@ void Achievements::DrawGameOverlays() const char* text_start = s_state.active_progress_indicator->achievement->measured_progress; const char* text_end = text_start + std::strlen(text_start); - const ImVec2 text_size = g_medium_font->CalcTextSizeA(g_medium_font->FontSize, FLT_MAX, 0.0f, text_start, text_end); + const ImVec2 text_size = + UIStyle.MediumFont->CalcTextSizeA(UIStyle.MediumFont->FontSize, FLT_MAX, 0.0f, text_start, text_end); const ImVec2 box_min = ImVec2(position.x - image_size.x - text_size.x - spacing - padding * 2.0f, position.y - image_size.y - padding * 2.0f); @@ -2230,7 +2231,8 @@ void Achievements::DrawGameOverlays() const ImVec2 text_pos = box_min + ImVec2(padding + image_size.x + spacing, (box_max.y - box_min.y - text_size.y) * 0.5f); const ImVec4 text_clip_rect(text_pos.x, text_pos.y, box_max.x, box_max.y); - dl->AddText(g_medium_font, g_medium_font->FontSize, text_pos, col, text_start, text_end, 0.0f, &text_clip_rect); + dl->AddText(UIStyle.MediumFont, UIStyle.MediumFont->FontSize, text_pos, col, text_start, text_end, 0.0f, + &text_clip_rect); if (!indicator.active && opacity <= 0.01f) { @@ -2252,8 +2254,8 @@ void Achievements::DrawGameOverlays() width_string.append(ICON_FA_STOPWATCH); for (u32 i = 0; i < indicator.text.length(); i++) width_string.append('0'); - const ImVec2 size = ImGuiFullscreen::g_medium_font->CalcTextSizeA( - ImGuiFullscreen::g_medium_font->FontSize, FLT_MAX, 0.0f, width_string.c_str(), width_string.end_ptr()); + const ImVec2 size = ImGuiFullscreen::UIStyle.MediumFont->CalcTextSizeA( + ImGuiFullscreen::UIStyle.MediumFont->FontSize, FLT_MAX, 0.0f, width_string.c_str(), width_string.end_ptr()); const ImVec2 box_min = ImVec2(position.x - size.x - padding * 2.0f, position.y - size.y - padding * 2.0f); const ImVec2 box_max = position; @@ -2263,17 +2265,17 @@ void Achievements::DrawGameOverlays() dl->AddRect(box_min, box_max, ImGui::GetColorU32(ImVec4(0.8f, 0.8f, 0.8f, opacity)), box_rounding); const u32 text_col = ImGui::GetColorU32(ImVec4(1.0f, 1.0f, 1.0f, opacity)); - const ImVec2 text_size = ImGuiFullscreen::g_medium_font->CalcTextSizeA( - ImGuiFullscreen::g_medium_font->FontSize, FLT_MAX, 0.0f, indicator.text.c_str(), + const ImVec2 text_size = ImGuiFullscreen::UIStyle.MediumFont->CalcTextSizeA( + ImGuiFullscreen::UIStyle.MediumFont->FontSize, FLT_MAX, 0.0f, indicator.text.c_str(), indicator.text.c_str() + indicator.text.length()); const ImVec2 text_pos = ImVec2(box_max.x - padding - text_size.x, box_min.y + padding); const ImVec4 text_clip_rect(box_min.x, box_min.y, box_max.x, box_max.y); - dl->AddText(g_medium_font, g_medium_font->FontSize, text_pos, text_col, indicator.text.c_str(), + dl->AddText(UIStyle.MediumFont, UIStyle.MediumFont->FontSize, text_pos, text_col, indicator.text.c_str(), indicator.text.c_str() + indicator.text.length(), 0.0f, &text_clip_rect); const ImVec2 icon_pos = ImVec2(box_min.x + padding, box_min.y + padding); - dl->AddText(g_medium_font, g_medium_font->FontSize, icon_pos, text_col, ICON_FA_STOPWATCH, nullptr, 0.0f, - &text_clip_rect); + dl->AddText(UIStyle.MediumFont, UIStyle.MediumFont->FontSize, icon_pos, text_col, ICON_FA_STOPWATCH, nullptr, + 0.0f, &text_clip_rect); if (!indicator.active && opacity <= 0.01f) { @@ -2297,9 +2299,8 @@ void Achievements::DrawGameOverlays() void Achievements::DrawPauseMenuOverlays() { - using ImGuiFullscreen::g_large_font; - using ImGuiFullscreen::g_medium_font; using ImGuiFullscreen::LayoutScale; + using ImGuiFullscreen::UIStyle; if (!HasActiveGame()) return; @@ -2310,11 +2311,12 @@ void Achievements::DrawPauseMenuOverlays() return; const ImGuiIO& io = ImGui::GetIO(); - ImFont* font = g_medium_font; + ImFont* font = UIStyle.MediumFont; const ImVec2 image_size(LayoutScale(ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY)); - const float start_y = LayoutScale(10.0f + 4.0f + 4.0f) + g_large_font->FontSize + (g_medium_font->FontSize * 2.0f); + const float start_y = + LayoutScale(10.0f + 4.0f + 4.0f) + UIStyle.LargeFont->FontSize + (UIStyle.MediumFont->FontSize * 2.0f); const float margin = LayoutScale(10.0f); const float spacing = LayoutScale(10.0f); const float padding = LayoutScale(10.0f); @@ -2398,9 +2400,8 @@ bool Achievements::PrepareAchievementsWindow() void Achievements::DrawAchievementsWindow() { - using ImGuiFullscreen::g_large_font; - using ImGuiFullscreen::g_medium_font; using ImGuiFullscreen::LayoutScale; + using ImGuiFullscreen::UIStyle; if (!s_state.achievement_list) return; @@ -2411,10 +2412,10 @@ void Achievements::DrawAchievementsWindow() static constexpr float heading_alpha = 0.95f; static constexpr float heading_height_unscaled = 110.0f; - const ImVec4 background = ImGuiFullscreen::ModAlpha(ImGuiFullscreen::UIBackgroundColor, alpha); - const ImVec4 heading_background = ImGuiFullscreen::ModAlpha(ImGuiFullscreen::UIBackgroundColor, heading_alpha); + const ImVec4 background = ImGuiFullscreen::ModAlpha(UIStyle.BackgroundColor, alpha); + const ImVec4 heading_background = ImGuiFullscreen::ModAlpha(UIStyle.BackgroundColor, heading_alpha); const ImVec2 display_size = ImGui::GetIO().DisplaySize; - const float heading_height = ImGuiFullscreen::LayoutScale(heading_height_unscaled); + const float heading_height = LayoutScale(heading_height_unscaled); bool close_window = false; if (ImGuiFullscreen::BeginFullscreenWindow( @@ -2427,9 +2428,9 @@ void Achievements::DrawAchievementsWindow() &bb.Min, &bb.Max, 0, heading_alpha); if (visible) { - const float padding = ImGuiFullscreen::LayoutScale(10.0f); - const float spacing = ImGuiFullscreen::LayoutScale(10.0f); - const float image_height = ImGuiFullscreen::LayoutScale(85.0f); + const float padding = LayoutScale(10.0f); + const float spacing = LayoutScale(10.0f); + const float image_height = LayoutScale(85.0f); const ImVec2 icon_min(bb.Min + ImVec2(padding, padding)); const ImVec2 icon_max(icon_min + ImVec2(image_height, image_height)); @@ -2452,23 +2453,23 @@ void Achievements::DrawAchievementsWindow() ImVec2 text_size; close_window = (ImGuiFullscreen::FloatingButton(ICON_FA_WINDOW_CLOSE, 10.0f, 10.0f, -1.0f, -1.0f, 1.0f, 0.0f, - true, g_large_font) || + true, UIStyle.LargeFont) || ImGuiFullscreen::WantsToCloseMenu()); - const ImRect title_bb(ImVec2(left, top), ImVec2(right, top + g_large_font->FontSize)); + const ImRect title_bb(ImVec2(left, top), ImVec2(right, top + UIStyle.LargeFont->FontSize)); text.assign(s_state.game_title); if (s_state.hardcore_mode) text.append(TRANSLATE_SV("Achievements", " (Hardcore Mode)")); - top += g_large_font->FontSize + spacing; + top += UIStyle.LargeFont->FontSize + spacing; - ImGui::PushFont(g_large_font); + ImGui::PushFont(UIStyle.LargeFont); ImGui::RenderTextClipped(title_bb.Min, title_bb.Max, text.c_str(), text.end_ptr(), nullptr, ImVec2(0.0f, 0.0f), &title_bb); ImGui::PopFont(); - const ImRect summary_bb(ImVec2(left, top), ImVec2(right, top + g_medium_font->FontSize)); + const ImRect summary_bb(ImVec2(left, top), ImVec2(right, top + UIStyle.MediumFont->FontSize)); if (s_state.game_summary.num_core_achievements > 0) { if (s_state.game_summary.num_unlocked_achievements == s_state.game_summary.num_core_achievements) @@ -2489,31 +2490,31 @@ void Achievements::DrawAchievementsWindow() text.assign(TRANSLATE_SV("Achievements", "This game has no achievements.")); } - top += g_medium_font->FontSize + spacing; + top += UIStyle.MediumFont->FontSize + spacing; - ImGui::PushFont(g_medium_font); + ImGui::PushFont(UIStyle.MediumFont); ImGui::RenderTextClipped(summary_bb.Min, summary_bb.Max, text.c_str(), text.end_ptr(), nullptr, ImVec2(0.0f, 0.0f), &summary_bb); ImGui::PopFont(); if (s_state.game_summary.num_core_achievements > 0) { - const float progress_height = ImGuiFullscreen::LayoutScale(20.0f); + const float progress_height = LayoutScale(20.0f); const ImRect progress_bb(ImVec2(left, top), ImVec2(right, top + progress_height)); const float fraction = static_cast(s_state.game_summary.num_unlocked_achievements) / static_cast(s_state.game_summary.num_core_achievements); - dl->AddRectFilled(progress_bb.Min, progress_bb.Max, ImGui::GetColorU32(ImGuiFullscreen::UIPrimaryDarkColor)); + dl->AddRectFilled(progress_bb.Min, progress_bb.Max, ImGui::GetColorU32(UIStyle.PrimaryDarkColor)); dl->AddRectFilled(progress_bb.Min, ImVec2(progress_bb.Min.x + fraction * progress_bb.GetWidth(), progress_bb.Max.y), - ImGui::GetColorU32(ImGuiFullscreen::UISecondaryColor)); + ImGui::GetColorU32(UIStyle.SecondaryColor)); text.format("{}%", static_cast(std::round(fraction * 100.0f))); text_size = ImGui::CalcTextSize(text.c_str(), text.end_ptr()); const ImVec2 text_pos( progress_bb.Min.x + ((progress_bb.Max.x - progress_bb.Min.x) / 2.0f) - (text_size.x / 2.0f), progress_bb.Min.y + ((progress_bb.Max.y - progress_bb.Min.y) / 2.0f) - (text_size.y / 2.0f)); - dl->AddText(g_medium_font, g_medium_font->FontSize, text_pos, - ImGui::GetColorU32(ImGuiFullscreen::UIPrimaryTextColor), text.c_str(), text.end_ptr()); + dl->AddText(UIStyle.MediumFont, UIStyle.MediumFont->FontSize, text_pos, + ImGui::GetColorU32(UIStyle.PrimaryTextColor), text.c_str(), text.end_ptr()); top += progress_height + spacing; } } @@ -2587,10 +2588,9 @@ void Achievements::DrawAchievementsWindow() void Achievements::DrawAchievement(const rc_client_achievement_t* cheevo) { - using ImGuiFullscreen::g_large_font; - using ImGuiFullscreen::g_medium_font; using ImGuiFullscreen::LayoutScale; using ImGuiFullscreen::LayoutUnscale; + using ImGuiFullscreen::UIStyle; static constexpr float alpha = 0.8f; static constexpr float progress_height_unscaled = 20.0f; @@ -2602,18 +2602,19 @@ void Achievements::DrawAchievement(const rc_client_achievement_t* cheevo) const std::string_view measured_progress(cheevo->measured_progress); const bool is_measured = !is_unlocked && !measured_progress.empty(); const float unlock_size = is_unlocked ? (spacing + ImGuiFullscreen::LAYOUT_MEDIUM_FONT_SIZE) : 0.0f; - const ImVec2 points_template_size( - g_medium_font->CalcTextSizeA(g_medium_font->FontSize, FLT_MAX, 0.0f, TRANSLATE("Achievements", "XXX points"))); + const ImVec2 points_template_size(UIStyle.MediumFont->CalcTextSizeA(UIStyle.MediumFont->FontSize, FLT_MAX, 0.0f, + TRANSLATE("Achievements", "XXX points"))); const size_t summary_length = std::strlen(cheevo->description); const float summary_wrap_width = (ImGui::GetCurrentWindow()->WorkRect.GetWidth() - (ImGui::GetStyle().FramePadding.x * 2.0f) - LayoutScale(ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT + 30.0f) - points_template_size.x); - const ImVec2 summary_text_size(g_medium_font->CalcTextSizeA( - g_medium_font->FontSize, FLT_MAX, summary_wrap_width, cheevo->description, cheevo->description + summary_length)); + const ImVec2 summary_text_size(UIStyle.MediumFont->CalcTextSizeA(UIStyle.MediumFont->FontSize, FLT_MAX, + summary_wrap_width, cheevo->description, + cheevo->description + summary_length)); // Messy, but need to undo LayoutScale in MenuButtonFrame()... - const float extra_summary_height = LayoutUnscale(std::max(summary_text_size.y - g_medium_font->FontSize, 0.0f)); + const float extra_summary_height = LayoutUnscale(std::max(summary_text_size.y - UIStyle.MediumFont->FontSize, 0.0f)); ImRect bb; bool visible, hovered; @@ -2653,10 +2654,10 @@ void Achievements::DrawAchievement(const rc_client_achievement_t* cheevo) SmallString text; - const float midpoint = bb.Min.y + g_large_font->FontSize + spacing; + const float midpoint = bb.Min.y + UIStyle.LargeFont->FontSize + spacing; text = TRANSLATE_PLURAL_SSTR("Achievements", "%n points", "Achievement points", cheevo->points); const ImVec2 points_size( - g_medium_font->CalcTextSizeA(g_medium_font->FontSize, FLT_MAX, 0.0f, text.c_str(), text.end_ptr())); + UIStyle.MediumFont->CalcTextSizeA(UIStyle.MediumFont->FontSize, FLT_MAX, 0.0f, text.c_str(), text.end_ptr())); const float points_template_start = bb.Max.x - points_template_size.x; const float points_start = points_template_start + ((points_template_size.x - points_size.x) * 0.5f); @@ -2682,23 +2683,24 @@ void Achievements::DrawAchievement(const rc_client_achievement_t* cheevo) break; } - const ImVec2 right_icon_size(g_large_font->CalcTextSizeA(g_large_font->FontSize, FLT_MAX, 0.0f, right_icon_text)); + const ImVec2 right_icon_size( + UIStyle.LargeFont->CalcTextSizeA(UIStyle.LargeFont->FontSize, FLT_MAX, 0.0f, right_icon_text)); const float text_start_x = bb.Min.x + image_size.x + LayoutScale(15.0f); const ImRect title_bb(ImVec2(text_start_x, bb.Min.y), ImVec2(points_start, midpoint)); const ImRect summary_bb(ImVec2(text_start_x, midpoint), - ImVec2(points_start, midpoint + g_medium_font->FontSize + extra_summary_height)); + ImVec2(points_start, midpoint + UIStyle.MediumFont->FontSize + extra_summary_height)); const ImRect points_bb(ImVec2(points_start, midpoint), bb.Max); const ImRect lock_bb(ImVec2(points_template_start + ((points_template_size.x - right_icon_size.x) * 0.5f), bb.Min.y), ImVec2(bb.Max.x, midpoint)); - ImGui::PushFont(g_large_font); + ImGui::PushFont(UIStyle.LargeFont); ImGui::RenderTextClipped(title_bb.Min, title_bb.Max, cheevo->title, nullptr, nullptr, ImVec2(0.0f, 0.0f), &title_bb); ImGui::RenderTextClipped(lock_bb.Min, lock_bb.Max, right_icon_text, nullptr, &right_icon_size, ImVec2(0.0f, 0.0f), &lock_bb); ImGui::PopFont(); - ImGui::PushFont(g_medium_font); + ImGui::PushFont(UIStyle.MediumFont); if (cheevo->description && summary_length > 0) { ImGui::RenderTextWrapped(summary_bb.Min, cheevo->description, cheevo->description + summary_length, @@ -2722,19 +2724,19 @@ void Achievements::DrawAchievement(const rc_client_achievement_t* cheevo) ImDrawList* dl = ImGui::GetWindowDrawList(); const float progress_height = LayoutScale(progress_height_unscaled); const float progress_spacing = LayoutScale(progress_spacing_unscaled); - const float top = midpoint + g_medium_font->FontSize + progress_spacing; + const float top = midpoint + UIStyle.MediumFont->FontSize + progress_spacing; const ImRect progress_bb(ImVec2(text_start_x, top), ImVec2(bb.Max.x, top + progress_height)); const float fraction = cheevo->measured_percent * 0.01f; - dl->AddRectFilled(progress_bb.Min, progress_bb.Max, ImGui::GetColorU32(ImGuiFullscreen::UIPrimaryDarkColor)); + dl->AddRectFilled(progress_bb.Min, progress_bb.Max, ImGui::GetColorU32(ImGuiFullscreen::UIStyle.PrimaryDarkColor)); dl->AddRectFilled(progress_bb.Min, ImVec2(progress_bb.Min.x + fraction * progress_bb.GetWidth(), progress_bb.Max.y), - ImGui::GetColorU32(ImGuiFullscreen::UISecondaryColor)); + ImGui::GetColorU32(ImGuiFullscreen::UIStyle.SecondaryColor)); const ImVec2 text_size = ImGui::CalcTextSize(measured_progress.data(), measured_progress.data() + measured_progress.size()); const ImVec2 text_pos(progress_bb.Min.x + ((progress_bb.Max.x - progress_bb.Min.x) / 2.0f) - (text_size.x / 2.0f), progress_bb.Min.y + ((progress_bb.Max.y - progress_bb.Min.y) / 2.0f) - (text_size.y / 2.0f)); - dl->AddText(g_medium_font, g_medium_font->FontSize, text_pos, - ImGui::GetColorU32(ImGuiFullscreen::UIPrimaryTextColor), measured_progress.data(), + dl->AddText(UIStyle.MediumFont, UIStyle.MediumFont->FontSize, text_pos, + ImGui::GetColorU32(ImGuiFullscreen::UIStyle.PrimaryTextColor), measured_progress.data(), measured_progress.data() + measured_progress.size()); } @@ -2769,9 +2771,8 @@ bool Achievements::PrepareLeaderboardsWindow() void Achievements::DrawLeaderboardsWindow() { - using ImGuiFullscreen::g_large_font; - using ImGuiFullscreen::g_medium_font; using ImGuiFullscreen::LayoutScale; + using ImGuiFullscreen::UIStyle; static constexpr float alpha = 0.8f; static constexpr float heading_alpha = 0.95f; @@ -2785,8 +2786,8 @@ void Achievements::DrawLeaderboardsWindow() ImRect bb; - const ImVec4 background = ImGuiFullscreen::ModAlpha(ImGuiFullscreen::UIBackgroundColor, alpha); - const ImVec4 heading_background = ImGuiFullscreen::ModAlpha(ImGuiFullscreen::UIBackgroundColor, heading_alpha); + const ImVec4 background = ImGuiFullscreen::ModAlpha(ImGuiFullscreen::UIStyle.BackgroundColor, alpha); + const ImVec4 heading_background = ImGuiFullscreen::ModAlpha(ImGuiFullscreen::UIStyle.BackgroundColor, heading_alpha); const ImVec2 display_size = ImGui::GetIO().DisplaySize; const float padding = LayoutScale(10.0f); const float spacing = LayoutScale(10.0f); @@ -2802,13 +2803,15 @@ void Achievements::DrawLeaderboardsWindow() } const float rank_column_width = - g_large_font->CalcTextSizeA(g_large_font->FontSize, std::numeric_limits::max(), -1.0f, "99999").x; + UIStyle.LargeFont->CalcTextSizeA(UIStyle.LargeFont->FontSize, std::numeric_limits::max(), -1.0f, "99999").x; const float name_column_width = - g_large_font - ->CalcTextSizeA(g_large_font->FontSize, std::numeric_limits::max(), -1.0f, "WWWWWWWWWWWWWWWWWWWWWW") + UIStyle.LargeFont + ->CalcTextSizeA(UIStyle.LargeFont->FontSize, std::numeric_limits::max(), -1.0f, "WWWWWWWWWWWWWWWWWWWWWW") .x; const float time_column_width = - g_large_font->CalcTextSizeA(g_large_font->FontSize, std::numeric_limits::max(), -1.0f, "WWWWWWWWWWW").x; + UIStyle.LargeFont + ->CalcTextSizeA(UIStyle.LargeFont->FontSize, std::numeric_limits::max(), -1.0f, "WWWWWWWWWWW") + .x; const float column_spacing = spacing * 2.0f; if (ImGuiFullscreen::BeginFullscreenWindow( @@ -2845,7 +2848,7 @@ void Achievements::DrawLeaderboardsWindow() if (!is_leaderboard_open) { if (ImGuiFullscreen::FloatingButton(ICON_FA_WINDOW_CLOSE, 10.0f, 10.0f, -1.0f, -1.0f, 1.0f, 0.0f, true, - g_large_font) || + UIStyle.LargeFont) || ImGuiFullscreen::WantsToCloseMenu()) { FullscreenUI::ReturnToPreviousWindow(); @@ -2854,31 +2857,31 @@ void Achievements::DrawLeaderboardsWindow() else { if (ImGuiFullscreen::FloatingButton(ICON_FA_CARET_SQUARE_LEFT, 10.0f, 10.0f, -1.0f, -1.0f, 1.0f, 0.0f, true, - g_large_font) || + UIStyle.LargeFont) || ImGuiFullscreen::WantsToCloseMenu()) { close_leaderboard_on_exit = true; } } - const ImRect title_bb(ImVec2(left, top), ImVec2(right, top + g_large_font->FontSize)); + const ImRect title_bb(ImVec2(left, top), ImVec2(right, top + UIStyle.LargeFont->FontSize)); text.assign(Achievements::GetGameTitle()); - top += g_large_font->FontSize + spacing; + top += UIStyle.LargeFont->FontSize + spacing; - ImGui::PushFont(g_large_font); + ImGui::PushFont(UIStyle.LargeFont); ImGui::RenderTextClipped(title_bb.Min, title_bb.Max, text.c_str(), text.end_ptr(), nullptr, ImVec2(0.0f, 0.0f), &title_bb); ImGui::PopFont(); if (is_leaderboard_open) { - const ImRect subtitle_bb(ImVec2(left, top), ImVec2(right, top + g_large_font->FontSize)); + const ImRect subtitle_bb(ImVec2(left, top), ImVec2(right, top + UIStyle.LargeFont->FontSize)); text.assign(s_state.open_leaderboard->title); - top += g_large_font->FontSize + spacing_small; + top += UIStyle.LargeFont->FontSize + spacing_small; - ImGui::PushFont(g_large_font); + ImGui::PushFont(UIStyle.LargeFont); ImGui::RenderTextClipped(subtitle_bb.Min, subtitle_bb.Max, text.c_str(), text.end_ptr(), nullptr, ImVec2(0.0f, 0.0f), &subtitle_bb); ImGui::PopFont(); @@ -2893,17 +2896,17 @@ void Achievements::DrawLeaderboardsWindow() text = TRANSLATE_PLURAL_SSTR("Achievements", "This game has %n leaderboards.", "Leaderboard count", count); } - const ImRect summary_bb(ImVec2(left, top), ImVec2(right, top + g_medium_font->FontSize)); - top += g_medium_font->FontSize + spacing_small; + const ImRect summary_bb(ImVec2(left, top), ImVec2(right, top + UIStyle.MediumFont->FontSize)); + top += UIStyle.MediumFont->FontSize + spacing_small; - ImGui::PushFont(g_medium_font); + ImGui::PushFont(UIStyle.MediumFont); ImGui::RenderTextClipped(summary_bb.Min, summary_bb.Max, text.c_str(), text.end_ptr(), nullptr, ImVec2(0.0f, 0.0f), &summary_bb); if (!is_leaderboard_open && !Achievements::IsHardcoreModeActive()) { - const ImRect hardcore_warning_bb(ImVec2(left, top), ImVec2(right, top + g_medium_font->FontSize)); - top += g_medium_font->FontSize + spacing_small; + const ImRect hardcore_warning_bb(ImVec2(left, top), ImVec2(right, top + UIStyle.MediumFont->FontSize)); + top += UIStyle.MediumFont->FontSize + spacing_small; ImGui::RenderTextClipped( hardcore_warning_bb.Min, hardcore_warning_bb.Max, @@ -2916,7 +2919,7 @@ void Achievements::DrawLeaderboardsWindow() if (is_leaderboard_open) { - const float tab_width = (ImGui::GetWindowWidth() / ImGuiFullscreen::g_layout_scale) * 0.5f; + const float tab_width = (ImGui::GetWindowWidth() / ImGuiFullscreen::UIStyle.LayoutScale) * 0.5f; ImGui::SetCursorPos(ImVec2(0.0f, top + spacing_small)); if (ImGui::IsKeyPressed(ImGuiKey_GamepadDpadLeft, false) || @@ -2953,10 +2956,10 @@ void Achievements::DrawLeaderboardsWindow() &visible, &hovered, &bb.Min, &bb.Max, 0, alpha); UNREFERENCED_VARIABLE(pressed); - const float midpoint = bb.Min.y + g_large_font->FontSize + LayoutScale(4.0f); + const float midpoint = bb.Min.y + UIStyle.LargeFont->FontSize + LayoutScale(4.0f); float text_start_x = bb.Min.x + LayoutScale(15.0f) + padding; - ImGui::PushFont(g_large_font); + ImGui::PushFont(UIStyle.LargeFont); const ImRect rank_bb(ImVec2(text_start_x, bb.Min.y), ImVec2(bb.Max.x, midpoint)); ImGui::RenderTextClipped(rank_bb.Min, rank_bb.Max, TRANSLATE("Achievements", "Rank"), nullptr, nullptr, @@ -2991,7 +2994,7 @@ void Achievements::DrawLeaderboardsWindow() const float line_thickness = LayoutScale(1.0f); const float line_padding = LayoutScale(5.0f); - const ImVec2 line_start(bb.Min.x, bb.Min.y + g_large_font->FontSize + line_padding); + const ImVec2 line_start(bb.Min.x, bb.Min.y + UIStyle.LargeFont->FontSize + line_padding); const ImVec2 line_end(bb.Max.x, line_start.y); ImGui::GetWindowDrawList()->AddLine(line_start, line_end, ImGui::GetColorU32(ImGuiCol_TextDisabled), line_thickness); @@ -3063,7 +3066,7 @@ void Achievements::DrawLeaderboardsWindow() } else { - ImGui::PushFont(g_large_font); + ImGui::PushFont(UIStyle.LargeFont); const ImVec2 pos_min(0.0f, heading_height); const ImVec2 pos_max(display_size.x, display_size.y); @@ -3092,10 +3095,10 @@ void Achievements::DrawLeaderboardsWindow() &bb.Min, &bb.Max); if (visible) { - const float midpoint = bb.Min.y + g_large_font->FontSize + LayoutScale(4.0f); + const float midpoint = bb.Min.y + UIStyle.LargeFont->FontSize + LayoutScale(4.0f); const ImRect title_bb(bb.Min, ImVec2(bb.Max.x, midpoint)); - ImGui::PushFont(g_large_font); + ImGui::PushFont(UIStyle.LargeFont); ImGui::RenderTextClipped(title_bb.Min, title_bb.Max, TRANSLATE("Achievements", "Loading..."), nullptr, nullptr, ImVec2(0, 0), &title_bb); ImGui::PopFont(); @@ -3130,8 +3133,8 @@ void Achievements::DrawLeaderboardEntry(const rc_client_leaderboard_entry_t& ent float rank_column_width, float name_column_width, float time_column_width, float column_spacing) { - using ImGuiFullscreen::g_large_font; using ImGuiFullscreen::LayoutScale; + using ImGuiFullscreen::UIStyle; static constexpr float alpha = 0.8f; @@ -3143,13 +3146,13 @@ void Achievements::DrawLeaderboardEntry(const rc_client_leaderboard_entry_t& ent if (!visible) return; - const float midpoint = bb.Min.y + g_large_font->FontSize + LayoutScale(4.0f); + const float midpoint = bb.Min.y + UIStyle.LargeFont->FontSize + LayoutScale(4.0f); float text_start_x = bb.Min.x + LayoutScale(15.0f); SmallString text; text.format("{}", entry.rank); - ImGui::PushFont(g_large_font); + ImGui::PushFont(UIStyle.LargeFont); if (is_self) ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(255, 242, 0, 255)); @@ -3212,9 +3215,8 @@ void Achievements::DrawLeaderboardEntry(const rc_client_leaderboard_entry_t& ent } void Achievements::DrawLeaderboardListEntry(const rc_client_leaderboard_t* lboard) { - using ImGuiFullscreen::g_large_font; - using ImGuiFullscreen::g_medium_font; using ImGuiFullscreen::LayoutScale; + using ImGuiFullscreen::UIStyle; static constexpr float alpha = 0.8f; @@ -3228,18 +3230,18 @@ void Achievements::DrawLeaderboardListEntry(const rc_client_leaderboard_t* lboar if (!visible) return; - const float midpoint = bb.Min.y + g_large_font->FontSize + LayoutScale(4.0f); + const float midpoint = bb.Min.y + UIStyle.LargeFont->FontSize + LayoutScale(4.0f); const float text_start_x = bb.Min.x + LayoutScale(15.0f); const ImRect title_bb(ImVec2(text_start_x, bb.Min.y), ImVec2(bb.Max.x, midpoint)); const ImRect summary_bb(ImVec2(text_start_x, midpoint), bb.Max); - ImGui::PushFont(g_large_font); + ImGui::PushFont(UIStyle.LargeFont); ImGui::RenderTextClipped(title_bb.Min, title_bb.Max, lboard->title, nullptr, nullptr, ImVec2(0.0f, 0.0f), &title_bb); ImGui::PopFont(); if (lboard->description && lboard->description[0] != '\0') { - ImGui::PushFont(g_medium_font); + ImGui::PushFont(UIStyle.MediumFont); ImGui::RenderTextClipped(summary_bb.Min, summary_bb.Max, lboard->description, nullptr, nullptr, ImVec2(0.0f, 0.0f), &summary_bb); ImGui::PopFont(); diff --git a/src/core/cdrom_subq_replacement.cpp b/src/core/cdrom_subq_replacement.cpp index 0def0659e..fc3589846 100644 --- a/src/core/cdrom_subq_replacement.cpp +++ b/src/core/cdrom_subq_replacement.cpp @@ -123,8 +123,8 @@ std::unique_ptr CDROMSubQReplacement::LoadLSD(const std::s return ret; } -bool CDROMSubQReplacement::LoadForImage(std::unique_ptr* ret, CDImage* image, std::string_view serial, - std::string_view title, Error* error) +bool CDROMSubQReplacement::LoadForImage(std::unique_ptr* ret, CDImage* image, + std::string_view serial, std::string_view title, Error* error) { struct FileLoader { @@ -140,13 +140,19 @@ bool CDROMSubQReplacement::LoadForImage(std::unique_ptr* r std::string path; // Try sbi/lsd in the directory first. - for (const FileLoader& loader : loaders) + if (!CDImage::IsDeviceName(image_path.c_str())) { - path = Path::ReplaceExtension(image_path, loader.extension); - if (FileSystem::FileExists(path.c_str())) + for (const FileLoader& loader : loaders) { - *ret = loader.func(path, error); - return static_cast(*ret); + path = Path::ReplaceExtension(image_path, loader.extension); + if (FileSystem::FileExists(path.c_str())) + { + *ret = loader.func(path, error); + if (!static_cast(*ret)) + Error::AddPrefixFmt(error, "Failed to load subchannel data from {}: ", Path::GetFileName(path)); + + return static_cast(*ret); + } } } @@ -161,6 +167,9 @@ bool CDROMSubQReplacement::LoadForImage(std::unique_ptr* r if (FileSystem::FileExists(path.c_str())) { *ret = loader.func(path, error); + if (!static_cast(*ret)) + Error::AddPrefixFmt(error, "Failed to load subchannel data from {}: ", Path::GetFileName(path)); + return static_cast(*ret); } } @@ -175,6 +184,9 @@ bool CDROMSubQReplacement::LoadForImage(std::unique_ptr* r if (FileSystem::FileExists(path.c_str())) { *ret = loader.func(path, error); + if (!static_cast(*ret)) + Error::AddPrefixFmt(error, "Failed to load subchannel data from {}: ", Path::GetFileName(path)); + return static_cast(*ret); } } @@ -188,6 +200,9 @@ bool CDROMSubQReplacement::LoadForImage(std::unique_ptr* r if (FileSystem::FileExists(path.c_str())) { *ret = loader.func(path, error); + if (!static_cast(*ret)) + Error::AddPrefixFmt(error, "Failed to load subchannel data from {}: ", Path::GetFileName(path)); + return static_cast(*ret); } } diff --git a/src/core/cheats.cpp b/src/core/cheats.cpp index 10a59564a..1471a6fe3 100644 --- a/src/core/cheats.cpp +++ b/src/core/cheats.cpp @@ -107,11 +107,17 @@ public: ALWAYS_INLINE bool IsOpen() const { return static_cast(m_zip); } - bool Open(const char* name) + bool Open(bool cheats) { if (m_zip) return true; +#ifndef __ANDROID__ + const char* name = cheats ? "cheats.zip" : "patches.zip"; +#else + const char* name = cheats ? "patchcodes.zip" : "patches.zip"; +#endif + Error error; std::optional> data = Host::ReadResourceFile(name, false, &error); if (!data.has_value()) @@ -344,15 +350,16 @@ std::vector Cheats::FindChtFilesOnDisk(const std::string_view seria std::vector ret; FileSystem::FindResultsArray files; FileSystem::FindFiles(cheats ? EmuFolders::Cheats.c_str() : EmuFolders::Patches.c_str(), - GetChtTemplate(serial, hash, true).c_str(), + GetChtTemplate(serial, std::nullopt, true).c_str(), FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_HIDDEN_FILES, &files); ret.reserve(files.size()); + for (FILESYSTEM_FIND_DATA& fd : files) { // Skip mismatched hashes. if (hash.has_value()) { - if (const std::string_view filename = Path::GetFileTitle(fd.FileName); filename.length() >= serial.length() + 18) + if (const std::string_view filename = Path::GetFileTitle(fd.FileName); filename.length() >= serial.length() + 17) { const std::string_view filename_hash = filename.substr(serial.length() + 1, 16); const std::optional filename_parsed_hash = StringUtil::FromChars(filename_hash, 16); @@ -398,10 +405,7 @@ void Cheats::EnumerateChtFiles(const std::string_view serial, std::optional disk_patch_files; if (for_ui || !Achievements::IsHardcoreModeActive()) { - disk_patch_files = FindChtFilesOnDisk(serial, for_ui ? hash : std::nullopt, cheats); + disk_patch_files = FindChtFilesOnDisk(serial, for_ui ? std::nullopt : hash, cheats); if (cheats && disk_patch_files.empty()) { // Check if there's an old-format titled file. if (ImportOldChtFile(serial)) - disk_patch_files = FindChtFilesOnDisk(serial, for_ui ? hash : std::nullopt, cheats); + disk_patch_files = FindChtFilesOnDisk(serial, for_ui ? std::nullopt : hash, cheats); } } @@ -701,6 +705,36 @@ bool Cheats::SaveCodesToFile(const char* path, const CodeInfoList& codes, Error* return true; } +void Cheats::RemoveAllCodes(const std::string_view serial, const std::string_view title, std::optional hash) +{ + Error error; + std::string path = GetChtFilename(serial, hash, true); + if (FileSystem::FileExists(path.c_str())) + { + if (!FileSystem::DeleteFile(path.c_str(), &error)) + ERROR_LOG("Failed to remove cht file '{}': {}", Path::GetFileName(path), error.GetDescription()); + } + + // check for a non-hashed path and remove that too + path = GetChtFilename(serial, std::nullopt, true); + if (FileSystem::FileExists(path.c_str())) + { + if (!FileSystem::DeleteFile(path.c_str(), &error)) + ERROR_LOG("Failed to remove cht file '{}': {}", Path::GetFileName(path), error.GetDescription()); + } + + // and a legacy cht file with the game title + if (!title.empty()) + { + path = fmt::format("{}" FS_OSPATH_SEPARATOR_STR "{}.cht", EmuFolders::Cheats, Path::SanitizeFileName(title)); + if (FileSystem::FileExists(path.c_str())) + { + if (!FileSystem::DeleteFile(path.c_str(), &error)) + ERROR_LOG("Failed to remove cht file '{}': {}", Path::GetFileName(path), error.GetDescription()); + } + } +} + std::string Cheats::GetChtFilename(const std::string_view serial, std::optional hash, bool cheats) { return Path::Combine(cheats ? EmuFolders::Cheats : EmuFolders::Patches, GetChtTemplate(serial, hash, false)); diff --git a/src/core/cheats.h b/src/core/cheats.h index 5d7d8e307..893e77600 100644 --- a/src/core/cheats.h +++ b/src/core/cheats.h @@ -117,6 +117,9 @@ extern bool UpdateCodeInFile(const char* path, const std::string_view name, cons /// Updates or adds multiple codes to the file, rewriting it. extern bool SaveCodesToFile(const char* path, const CodeInfoList& codes, Error* error); +/// Removes any .cht files for the specified game. +extern void RemoveAllCodes(const std::string_view serial, const std::string_view title, std::optional hash); + /// Returns the path to a new cheat/patch cht for the specified serial and hash. extern std::string GetChtFilename(const std::string_view serial, std::optional hash, bool cheats); diff --git a/src/core/cpu_code_cache.cpp b/src/core/cpu_code_cache.cpp index 31c1ac411..fe0878eaf 100644 --- a/src/core/cpu_code_cache.cpp +++ b/src/core/cpu_code_cache.cpp @@ -130,10 +130,11 @@ static constexpr u32 RECOMPILER_CODE_CACHE_SIZE = 48 * 1024 * 1024; static constexpr u32 RECOMPILER_FAR_CODE_CACHE_SIZE = 16 * 1024 * 1024; #endif -// On Linux ARM32/ARM64, we use a dedicated section in the ELF for storing code. -// This is because without ASLR, or on certain ASLR offsets, the sbrk() heap ends up immediately following the text/data -// sections, which means there isn't a large enough gap to fit within range on ARM32. -#if defined(__linux__) && (defined(CPU_ARCH_ARM32) || defined(CPU_ARCH_ARM64)) +// On Linux ARM32/ARM64, we use a dedicated section in the ELF for storing code. This is because without +// ASLR, or on certain ASLR offsets, the sbrk() heap ends up immediately following the text/data sections, +// which means there isn't a large enough gap to fit within range on ARM32. Also enable it for Android, +// because MAP_FIXED_NOREPLACE may not exist on older kernels. +#if (defined(__linux__) && (defined(CPU_ARCH_ARM32) || defined(CPU_ARCH_ARM64))) || defined(__ANDROID__) #define USE_CODE_BUFFER_SECTION 1 #ifdef __clang__ #pragma clang section bss = ".jitstorage" diff --git a/src/core/cpu_recompiler_arm32.cpp b/src/core/cpu_recompiler_arm32.cpp index d480ce5e5..6338e701e 100644 --- a/src/core/cpu_recompiler_arm32.cpp +++ b/src/core/cpu_recompiler_arm32.cpp @@ -92,12 +92,12 @@ void armEmitMov(vixl::aarch32::Assembler* armAsm, const vixl::aarch32::Register& { if (vixl::IsUintN(16, imm)) { - armAsm->mov(al, rd, imm & 0xffff); + armAsm->mov(vixl::aarch32::al, rd, imm & 0xffff); return; } - armAsm->mov(al, rd, imm & 0xffff); - armAsm->movt(al, rd, imm >> 16); + armAsm->mov(vixl::aarch32::al, rd, imm & 0xffff); + armAsm->movt(vixl::aarch32::al, rd, imm >> 16); } void armMoveAddressToReg(vixl::aarch32::Assembler* armAsm, const vixl::aarch32::Register& reg, const void* addr) @@ -126,7 +126,7 @@ void armEmitJmp(vixl::aarch32::Assembler* armAsm, const void* ptr, bool force_in } else { - Label label(displacement + armAsm->GetCursorOffset()); + vixl::aarch32::Label label(displacement + armAsm->GetCursorOffset()); armAsm->b(&label); } } @@ -152,7 +152,7 @@ void armEmitCall(vixl::aarch32::Assembler* armAsm, const void* ptr, bool force_i } else { - Label label(displacement + armAsm->GetCursorOffset()); + vixl::aarch32::Label label(displacement + armAsm->GetCursorOffset()); armAsm->bl(&label); } } @@ -167,7 +167,7 @@ void armEmitCondBranch(vixl::aarch32::Assembler* armAsm, vixl::aarch32::Conditio } else { - Label label(displacement + armAsm->GetCursorOffset()); + vixl::aarch32::Label label(displacement + armAsm->GetCursorOffset()); armAsm->b(cond, &label); } } @@ -175,14 +175,14 @@ void armEmitCondBranch(vixl::aarch32::Assembler* armAsm, vixl::aarch32::Conditio void armEmitFarLoad(vixl::aarch32::Assembler* armAsm, const vixl::aarch32::Register& reg, const void* addr) { armMoveAddressToReg(armAsm, reg, addr); - armAsm->ldr(reg, MemOperand(reg)); + armAsm->ldr(reg, vixl::aarch32::MemOperand(reg)); } void armEmitFarStore(vixl::aarch32::Assembler* armAsm, const vixl::aarch32::Register& reg, const void* addr, const vixl::aarch32::Register& tempreg) { armMoveAddressToReg(armAsm, tempreg, addr); - armAsm->str(reg, MemOperand(tempreg)); + armAsm->str(reg, vixl::aarch32::MemOperand(tempreg)); } void CPU::CodeCache::DisassembleAndLogHostCode(const void* start, u32 size) @@ -204,7 +204,6 @@ u32 CPU::CodeCache::GetHostInstructionCount(const void* start, u32 size) u32 CPU::CodeCache::EmitJump(void* code, const void* dst, bool flush_icache) { using namespace vixl::aarch32; - using namespace CPU::Recompiler; const s32 disp = armGetPCDisplacement(code, dst); DebugAssert(armIsPCDisplacementInImmediateRange(disp)); @@ -222,7 +221,7 @@ u32 CPU::CodeCache::EmitJump(void* code, const void* dst, bool flush_icache) return kA32InstructionSizeInBytes; } -u8* CPU::Recompiler::armGetJumpTrampoline(const void* target) +u8* armGetJumpTrampoline(const void* target) { auto it = s_trampoline_targets.find(target); if (it != s_trampoline_targets.end()) @@ -239,7 +238,7 @@ u8* CPU::Recompiler::armGetJumpTrampoline(const void* target) } u8* start = s_trampoline_start_ptr + offset; - Assembler armAsm(start, TRAMPOLINE_AREA_SIZE - offset); + vixl::aarch32::Assembler armAsm(start, TRAMPOLINE_AREA_SIZE - offset); armMoveAddressToReg(&armAsm, RSCRATCH, target); armAsm.bx(RSCRATCH); @@ -255,7 +254,6 @@ u8* CPU::Recompiler::armGetJumpTrampoline(const void* target) u32 CPU::CodeCache::EmitASMFunctions(void* code, u32 code_size) { using namespace vixl::aarch32; - using namespace CPU::Recompiler; Assembler actual_asm(static_cast(code), code_size); Assembler* armAsm = &actual_asm; diff --git a/src/core/fullscreen_ui.cpp b/src/core/fullscreen_ui.cpp index ca8bf079b..c8a29879b 100644 --- a/src/core/fullscreen_ui.cpp +++ b/src/core/fullscreen_ui.cpp @@ -72,10 +72,6 @@ public: #define FSUI_NSTR(str) str using ImGuiFullscreen::FocusResetType; -using ImGuiFullscreen::g_large_font; -using ImGuiFullscreen::g_layout_padding_left; -using ImGuiFullscreen::g_layout_padding_top; -using ImGuiFullscreen::g_medium_font; using ImGuiFullscreen::LAYOUT_FOOTER_HEIGHT; using ImGuiFullscreen::LAYOUT_LARGE_FONT_SIZE; using ImGuiFullscreen::LAYOUT_MEDIUM_FONT_SIZE; @@ -85,21 +81,7 @@ using ImGuiFullscreen::LAYOUT_MENU_BUTTON_X_PADDING; using ImGuiFullscreen::LAYOUT_MENU_BUTTON_Y_PADDING; using ImGuiFullscreen::LAYOUT_SCREEN_HEIGHT; using ImGuiFullscreen::LAYOUT_SCREEN_WIDTH; -using ImGuiFullscreen::UIBackgroundColor; -using ImGuiFullscreen::UIBackgroundHighlightColor; -using ImGuiFullscreen::UIBackgroundLineColor; -using ImGuiFullscreen::UIBackgroundTextColor; -using ImGuiFullscreen::UIDisabledColor; -using ImGuiFullscreen::UIPrimaryColor; -using ImGuiFullscreen::UIPrimaryDarkColor; -using ImGuiFullscreen::UIPrimaryLightColor; -using ImGuiFullscreen::UIPrimaryLineColor; -using ImGuiFullscreen::UIPrimaryTextColor; -using ImGuiFullscreen::UISecondaryColor; -using ImGuiFullscreen::UISecondaryStrongColor; -using ImGuiFullscreen::UISecondaryTextColor; -using ImGuiFullscreen::UISecondaryWeakColor; -using ImGuiFullscreen::UITextHighlightColor; +using ImGuiFullscreen::UIStyle; using ImGuiFullscreen::ActiveButton; using ImGuiFullscreen::AddNotification; @@ -161,8 +143,10 @@ using ImGuiFullscreen::ToggleButton; using ImGuiFullscreen::WantsToCloseMenu; #ifndef __ANDROID__ + namespace FullscreenUI { -enum class MainWindowType + +enum class MainWindowType : u8 { None, Landing, @@ -176,14 +160,14 @@ enum class MainWindowType Leaderboards, }; -enum class PauseSubMenu +enum class PauseSubMenu : u8 { None, Exit, Achievements, }; -enum class SettingsPage +enum class SettingsPage : u8 { Summary, Interface, @@ -203,7 +187,7 @@ enum class SettingsPage Count }; -enum class GameListView +enum class GameListView : u8 { Grid, List, @@ -235,28 +219,12 @@ static void OpenAboutWindow(); static void FixStateIfPaused(); static void GetStandardSelectionFooterText(SmallStringBase& dest, bool back_instead_of_cancel); -static MainWindowType s_current_main_window = MainWindowType::None; -static PauseSubMenu s_current_pause_submenu = PauseSubMenu::None; -static std::string s_current_game_subtitle; -static bool s_initialized = false; -static bool s_tried_to_initialize = false; -static bool s_pause_menu_was_open = false; -static bool s_was_paused_on_quick_menu_open = false; -static bool s_about_window_open = false; -static bool s_achievements_login_window_open = false; - ////////////////////////////////////////////////////////////////////////// // Resources ////////////////////////////////////////////////////////////////////////// static bool LoadResources(); static void DestroyResources(); -static std::shared_ptr s_app_icon_texture; -static std::shared_ptr s_fallback_disc_texture; -static std::shared_ptr s_fallback_exe_texture; -static std::shared_ptr s_fallback_psf_texture; -static std::shared_ptr s_fallback_playlist_texture; - ////////////////////////////////////////////////////////////////////////// // Landing ////////////////////////////////////////////////////////////////////////// @@ -331,33 +299,34 @@ static void DoSaveInputProfile(const std::string& name); static bool DrawToggleSetting(SettingsInterface* bsi, const char* title, const char* summary, const char* section, const char* key, bool default_value, bool enabled = true, bool allow_tristate = true, - float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT, ImFont* font = g_large_font, - ImFont* summary_font = g_medium_font); + float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT, + ImFont* font = UIStyle.LargeFont, ImFont* summary_font = UIStyle.MediumFont); static void DrawIntListSetting(SettingsInterface* bsi, const char* title, const char* summary, const char* section, const char* key, int default_value, const char* const* options, size_t option_count, bool translate_options, int option_offset = 0, bool enabled = true, - float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT, ImFont* font = g_large_font, - ImFont* summary_font = g_medium_font, const char* tr_context = TR_CONTEXT); + float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT, + ImFont* font = UIStyle.LargeFont, ImFont* summary_font = UIStyle.MediumFont, + const char* tr_context = TR_CONTEXT); static void DrawIntRangeSetting(SettingsInterface* bsi, const char* title, const char* summary, const char* section, const char* key, int default_value, int min_value, int max_value, const char* format = "%d", bool enabled = true, - float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT, ImFont* font = g_large_font, - ImFont* summary_font = g_medium_font); + float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT, + ImFont* font = UIStyle.LargeFont, ImFont* summary_font = UIStyle.MediumFont); static void DrawIntSpinBoxSetting(SettingsInterface* bsi, const char* title, const char* summary, const char* section, const char* key, int default_value, int min_value, int max_value, int step_value, const char* format = "%d", bool enabled = true, float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT, - ImFont* font = g_large_font, ImFont* summary_font = g_medium_font); + ImFont* font = UIStyle.LargeFont, ImFont* summary_font = UIStyle.MediumFont); static void DrawFloatRangeSetting(SettingsInterface* bsi, const char* title, const char* summary, const char* section, const char* key, float default_value, float min_value, float max_value, const char* format = "%f", float multiplier = 1.0f, bool enabled = true, float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT, - ImFont* font = g_large_font, ImFont* summary_font = g_medium_font); + ImFont* font = UIStyle.LargeFont, ImFont* summary_font = UIStyle.MediumFont); static void DrawFloatSpinBoxSetting(SettingsInterface* bsi, const char* title, const char* summary, const char* section, const char* key, float default_value, float min_value, float max_value, float step_value, float multiplier, const char* format = "%f", bool enabled = true, float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT, - ImFont* font = g_large_font, ImFont* summary_font = g_medium_font); + ImFont* font = UIStyle.LargeFont, ImFont* summary_font = UIStyle.MediumFont); #if 0 static void DrawIntRectSetting(SettingsInterface* bsi, const char* title, const char* summary, const char* section, const char* left_key, int default_left, const char* top_key, int default_top, @@ -378,15 +347,15 @@ static void DrawEnumSetting(SettingsInterface* bsi, const char* title, const cha const char* (*to_string_function)(DataType value), const char* (*to_display_string_function)(DataType value), SizeType option_count, bool enabled = true, float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT, - ImFont* font = g_large_font, ImFont* summary_font = g_medium_font); + ImFont* font = UIStyle.LargeFont, ImFont* summary_font = UIStyle.MediumFont); static void DrawFloatListSetting(SettingsInterface* bsi, const char* title, const char* summary, const char* section, const char* key, float default_value, const char* const* options, const float* option_values, size_t option_count, bool translate_options, bool enabled = true, float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT, - ImFont* font = g_large_font, ImFont* summary_font = g_medium_font); + ImFont* font = UIStyle.LargeFont, ImFont* summary_font = UIStyle.MediumFont); static void DrawFolderSetting(SettingsInterface* bsi, const char* title, const char* section, const char* key, const std::string& runtime_var, float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT, - ImFont* font = g_large_font, ImFont* summary_font = g_medium_font); + ImFont* font = UIStyle.LargeFont, ImFont* summary_font = UIStyle.MediumFont); static void PopulateGraphicsAdapterList(); static void PopulateGameListDirectoryCache(SettingsInterface* si); @@ -401,31 +370,6 @@ static void DrawInputBindingButton(SettingsInterface* bsi, InputBindingInfo::Typ static void ClearInputBindingVariables(); static void StartAutomaticBinding(u32 port); -static SettingsPage s_settings_page = SettingsPage::Interface; -static std::unique_ptr s_game_settings_interface; -static std::unique_ptr s_game_settings_entry; -static std::vector> s_game_list_directories_cache; -static GPUDevice::AdapterInfoList s_graphics_adapter_list_cache; -static std::vector s_fullscreen_mode_list_cache; -static Cheats::CodeInfoList s_game_patch_list; -static std::vector s_enabled_game_patch_cache; -static Cheats::CodeInfoList s_game_cheats_list; -static std::vector s_enabled_game_cheat_cache; -static std::vector s_game_cheat_groups; -static std::vector s_postprocessing_stages; -static std::vector s_hotkey_list_cache; -static std::atomic_bool s_settings_changed{false}; -static std::atomic_bool s_game_settings_changed{false}; -static InputBindingInfo::Type s_input_binding_type = InputBindingInfo::Type::Unknown; -static std::string s_input_binding_section; -static std::string s_input_binding_key; -static std::string s_input_binding_display_name; -static std::vector s_input_binding_new_bindings; -static std::vector>> s_input_binding_value_ranges; -static Timer s_input_binding_timer; -static bool s_controller_macro_expanded[NUM_CONTROLLER_AND_CARD_PORTS][InputManager::NUM_MACRO_BUTTONS_PER_CONTROLLER] = - {}; - ////////////////////////////////////////////////////////////////////////// // Save State List ////////////////////////////////////////////////////////////////////////// @@ -456,13 +400,6 @@ static void DrawResumeStateSelector(); static void DoLoadState(std::string path); static void DoSaveState(s32 slot, bool global); -static std::vector s_save_state_selector_slots; -static std::string s_save_state_selector_game_path; -static s32 s_save_state_selector_submenu_index = -1; -static bool s_save_state_selector_open = false; -static bool s_save_state_selector_loading = true; -static bool s_save_state_selector_resuming = false; - ////////////////////////////////////////////////////////////////////////// // Game List ////////////////////////////////////////////////////////////////////////// @@ -479,10 +416,71 @@ static GPUTexture* GetTextureForGameListEntryType(GameList::EntryType type); static GPUTexture* GetGameListCover(const GameList::Entry* entry); static GPUTexture* GetCoverForCurrentGame(); -// Lazily populated cover images. -static std::unordered_map s_cover_image_map; -static std::vector s_game_list_sorted_entries; -static GameListView s_game_list_view = GameListView::Grid; +namespace { + +struct ALIGN_TO_CACHE_LINE UIState +{ + // Main + MainWindowType current_main_window = MainWindowType::None; + PauseSubMenu current_pause_submenu = PauseSubMenu::None; + bool initialized = false; + bool tried_to_initialize = false; + bool pause_menu_was_open = false; + bool was_paused_on_quick_menu_open = false; + bool about_window_open = false; + bool achievements_login_window_open = false; + std::string current_game_subtitle; + + // Resources + std::shared_ptr app_icon_texture; + std::shared_ptr fallback_disc_texture; + std::shared_ptr fallback_exe_texture; + std::shared_ptr fallback_psf_texture; + std::shared_ptr fallback_playlist_texture; + + // Settings + SettingsPage settings_page = SettingsPage::Interface; + std::unique_ptr game_settings_interface; + std::unique_ptr game_settings_entry; + std::vector> game_list_directories_cache; + GPUDevice::AdapterInfoList graphics_adapter_list_cache; + std::vector fullscreen_mode_list_cache; + Cheats::CodeInfoList game_patch_list; + std::vector enabled_game_patch_cache; + Cheats::CodeInfoList game_cheats_list; + std::vector enabled_game_cheat_cache; + std::vector game_cheat_groups; + std::vector postprocessing_stages; + std::vector hotkey_list_cache; + std::atomic_bool settings_changed{false}; + std::atomic_bool game_settings_changed{false}; + InputBindingInfo::Type input_binding_type = InputBindingInfo::Type::Unknown; + std::string input_binding_section; + std::string input_binding_key; + std::string input_binding_display_name; + std::vector input_binding_new_bindings; + std::vector>> input_binding_value_ranges; + Timer input_binding_timer; + bool controller_macro_expanded[NUM_CONTROLLER_AND_CARD_PORTS][InputManager::NUM_MACRO_BUTTONS_PER_CONTROLLER] = {}; + + // Save State List + std::vector save_state_selector_slots; + std::string save_state_selector_game_path; + s32 save_state_selector_submenu_index = -1; + bool save_state_selector_open = false; + bool save_state_selector_loading = true; + bool save_state_selector_resuming = false; + + // Lazily populated cover images. + std::unordered_map cover_image_map; + std::vector game_list_sorted_entries; + GameListView game_list_view = GameListView::Grid; +}; + +} // namespace + +static UIState s_state; + } // namespace FullscreenUI ////////////////////////////////////////////////////////////////////////// @@ -576,10 +574,10 @@ void ImGuiFullscreen::GetInputDialogHelpText(SmallStringBase& dest) bool FullscreenUI::Initialize() { - if (s_initialized) + if (s_state.initialized) return true; - if (s_tried_to_initialize) + if (s_state.tried_to_initialize) return false; ImGuiFullscreen::SetTheme(Host::GetBaseBoolSettingValue("Main", "UseLightFullscreenUITheme", false)); @@ -591,17 +589,17 @@ bool FullscreenUI::Initialize() { DestroyResources(); ImGuiFullscreen::Shutdown(); - s_tried_to_initialize = true; + s_state.tried_to_initialize = true; return false; } - s_initialized = true; - s_current_main_window = MainWindowType::None; - s_current_pause_submenu = PauseSubMenu::None; - s_pause_menu_was_open = false; - s_was_paused_on_quick_menu_open = false; - s_about_window_open = false; - s_hotkey_list_cache = InputManager::GetHotkeyList(); + s_state.initialized = true; + s_state.current_main_window = MainWindowType::None; + s_state.current_pause_submenu = PauseSubMenu::None; + s_state.pause_menu_was_open = false; + s_state.was_paused_on_quick_menu_open = false; + s_state.about_window_open = false; + s_state.hotkey_list_cache = InputManager::GetHotkeyList(); if (!System::IsValid()) SwitchToLanding(); @@ -615,18 +613,18 @@ bool FullscreenUI::Initialize() bool FullscreenUI::IsInitialized() { - return s_initialized; + return s_state.initialized; } bool FullscreenUI::HasActiveWindow() { - return s_initialized && (s_current_main_window != MainWindowType::None || AreAnyDialogsOpen()); + return s_state.initialized && (s_state.current_main_window != MainWindowType::None || AreAnyDialogsOpen()); } bool FullscreenUI::AreAnyDialogsOpen() { - return (s_save_state_selector_open || s_about_window_open || - s_input_binding_type != InputBindingInfo::Type::Unknown || ImGuiFullscreen::IsChoiceDialogOpen() || + return (s_state.save_state_selector_open || s_state.about_window_open || + s_state.input_binding_type != InputBindingInfo::Type::Unknown || ImGuiFullscreen::IsChoiceDialogOpen() || ImGuiFullscreen::IsFileSelectorOpen()); } @@ -639,7 +637,8 @@ void FullscreenUI::CheckForConfigChanges(const Settings& old_settings) // That means we're going to be reading achievement state. if (old_settings.achievements_enabled && !g_settings.achievements_enabled) { - if (s_current_main_window == MainWindowType::Achievements || s_current_main_window == MainWindowType::Leaderboards) + if (s_state.current_main_window == MainWindowType::Achievements || + s_state.current_main_window == MainWindowType::Leaderboards) ReturnToPreviousWindow(); } } @@ -649,7 +648,7 @@ void FullscreenUI::OnSystemStarted() if (!IsInitialized()) return; - s_current_main_window = MainWindowType::None; + s_state.current_main_window = MainWindowType::None; QueueResetFocus(FocusResetType::ViewChanged); } @@ -661,7 +660,7 @@ void FullscreenUI::OnSystemPaused() void FullscreenUI::OnSystemResumed() { // get rid of pause menu if we unpaused another way - if (s_current_main_window == MainWindowType::PauseMenu) + if (s_state.current_main_window == MainWindowType::PauseMenu) ClosePauseMenu(); } @@ -670,9 +669,9 @@ void FullscreenUI::OnSystemDestroyed() if (!IsInitialized()) return; - s_pause_menu_was_open = false; - s_was_paused_on_quick_menu_open = false; - s_current_pause_submenu = PauseSubMenu::None; + s_state.pause_menu_was_open = false; + s_state.was_paused_on_quick_menu_open = false; + s_state.current_pause_submenu = PauseSubMenu::None; SwitchToLanding(); } @@ -684,18 +683,18 @@ void FullscreenUI::OnRunningGameChanged() const std::string& path = System::GetDiscPath(); const std::string& serial = System::GetGameSerial(); if (!serial.empty()) - s_current_game_subtitle = fmt::format("{0} - {1}", serial, Path::GetFileName(path)); + s_state.current_game_subtitle = fmt::format("{0} - {1}", serial, Path::GetFileName(path)); else - s_current_game_subtitle = {}; + s_state.current_game_subtitle = {}; } void FullscreenUI::PauseForMenuOpen(bool set_pause_menu_open) { - s_was_paused_on_quick_menu_open = (System::GetState() == System::State::Paused); - if (!s_was_paused_on_quick_menu_open) + s_state.was_paused_on_quick_menu_open = (System::GetState() == System::State::Paused); + if (!s_state.was_paused_on_quick_menu_open) Host::RunOnCPUThread([]() { System::PauseSystem(true); }); - s_pause_menu_was_open |= set_pause_menu_open; + s_state.pause_menu_was_open |= set_pause_menu_open; } void FullscreenUI::OpenPauseMenu() @@ -703,12 +702,12 @@ void FullscreenUI::OpenPauseMenu() if (!System::IsValid()) return; - if (!Initialize() || s_current_main_window != MainWindowType::None) + if (!Initialize() || s_state.current_main_window != MainWindowType::None) return; PauseForMenuOpen(true); - s_current_main_window = MainWindowType::PauseMenu; - s_current_pause_submenu = PauseSubMenu::None; + s_state.current_main_window = MainWindowType::PauseMenu; + s_state.current_pause_submenu = PauseSubMenu::None; QueueResetFocus(FocusResetType::ViewChanged); ForceKeyNavEnabled(); FixStateIfPaused(); @@ -719,10 +718,10 @@ void FullscreenUI::OpenCheatsMenu() if (!System::IsValid()) return; - if (!Initialize() || s_current_main_window != MainWindowType::None || !SwitchToGameSettings()) + if (!Initialize() || s_state.current_main_window != MainWindowType::None || !SwitchToGameSettings()) return; - s_settings_page = SettingsPage::Cheats; + s_state.settings_page = SettingsPage::Cheats; PauseForMenuOpen(true); ForceKeyNavEnabled(); FixStateIfPaused(); @@ -752,20 +751,20 @@ void FullscreenUI::ClosePauseMenu() if (!IsInitialized() || !System::IsValid()) return; - if (System::GetState() == System::State::Paused && !s_was_paused_on_quick_menu_open) + if (System::GetState() == System::State::Paused && !s_state.was_paused_on_quick_menu_open) Host::RunOnCPUThread([]() { System::PauseSystem(false); }); - s_current_main_window = MainWindowType::None; - s_current_pause_submenu = PauseSubMenu::None; - s_pause_menu_was_open = false; + s_state.current_main_window = MainWindowType::None; + s_state.current_pause_submenu = PauseSubMenu::None; + s_state.pause_menu_was_open = false; QueueResetFocus(FocusResetType::ViewChanged); FixStateIfPaused(); } void FullscreenUI::OpenPauseSubMenu(PauseSubMenu submenu) { - s_current_main_window = MainWindowType::PauseMenu; - s_current_pause_submenu = submenu; + s_state.current_main_window = MainWindowType::PauseMenu; + s_state.current_pause_submenu = submenu; QueueResetFocus(FocusResetType::ViewChanged); } @@ -774,29 +773,29 @@ void FullscreenUI::Shutdown() Achievements::ClearUIState(); ClearInputBindingVariables(); CloseSaveStateSelector(); - s_cover_image_map.clear(); - std::memset(s_controller_macro_expanded, 0, sizeof(s_controller_macro_expanded)); - s_game_list_sorted_entries = {}; - s_game_list_directories_cache = {}; - s_game_patch_list = {}; - s_enabled_game_patch_cache = {}; - s_game_cheats_list = {}; - s_enabled_game_cheat_cache = {}; - s_game_cheat_groups = {}; - s_postprocessing_stages = {}; - s_fullscreen_mode_list_cache = {}; - s_graphics_adapter_list_cache = {}; - s_hotkey_list_cache = {}; - s_current_game_subtitle = {}; + s_state.cover_image_map.clear(); + std::memset(s_state.controller_macro_expanded, 0, sizeof(s_state.controller_macro_expanded)); + s_state.game_list_sorted_entries = {}; + s_state.game_list_directories_cache = {}; + s_state.game_patch_list = {}; + s_state.enabled_game_patch_cache = {}; + s_state.game_cheats_list = {}; + s_state.enabled_game_cheat_cache = {}; + s_state.game_cheat_groups = {}; + s_state.postprocessing_stages = {}; + s_state.fullscreen_mode_list_cache = {}; + s_state.graphics_adapter_list_cache = {}; + s_state.hotkey_list_cache = {}; + s_state.current_game_subtitle = {}; DestroyResources(); ImGuiFullscreen::Shutdown(); - s_initialized = false; - s_tried_to_initialize = false; + s_state.initialized = false; + s_state.tried_to_initialize = false; } void FullscreenUI::Render() { - if (!s_initialized) + if (!s_state.initialized) return; ImGuiFullscreen::UploadAsyncTextures(); @@ -804,10 +803,10 @@ void FullscreenUI::Render() ImGuiFullscreen::BeginLayout(); // Primed achievements must come first, because we don't want the pause screen to be behind them. - if (s_current_main_window == MainWindowType::None) + if (s_state.current_main_window == MainWindowType::None) Achievements::DrawGameOverlays(); - switch (s_current_main_window) + switch (s_state.current_main_window) { case MainWindowType::Landing: DrawLandingWindow(); @@ -840,39 +839,39 @@ void FullscreenUI::Render() break; } - if (s_save_state_selector_open) + if (s_state.save_state_selector_open) { - if (s_save_state_selector_resuming) + if (s_state.save_state_selector_resuming) DrawResumeStateSelector(); else - DrawSaveStateSelector(s_save_state_selector_loading); + DrawSaveStateSelector(s_state.save_state_selector_loading); } - if (s_about_window_open) + if (s_state.about_window_open) DrawAboutWindow(); - if (s_input_binding_type != InputBindingInfo::Type::Unknown) + if (s_state.input_binding_type != InputBindingInfo::Type::Unknown) DrawInputBindingWindow(); ImGuiFullscreen::EndLayout(); - if (s_settings_changed.load(std::memory_order_relaxed)) + if (s_state.settings_changed.load(std::memory_order_relaxed)) { Host::CommitBaseSettingChanges(); Host::RunOnCPUThread([]() { System::ApplySettings(false); }); - s_settings_changed.store(false, std::memory_order_release); + s_state.settings_changed.store(false, std::memory_order_release); } - if (s_game_settings_changed.load(std::memory_order_relaxed)) + if (s_state.game_settings_changed.load(std::memory_order_relaxed)) { - if (s_game_settings_interface) + if (s_state.game_settings_interface) { Error error; - s_game_settings_interface->RemoveEmptySections(); + s_state.game_settings_interface->RemoveEmptySections(); - if (s_game_settings_interface->IsEmpty()) + if (s_state.game_settings_interface->IsEmpty()) { - if (FileSystem::FileExists(s_game_settings_interface->GetFileName().c_str()) && - !FileSystem::DeleteFile(s_game_settings_interface->GetFileName().c_str(), &error)) + if (FileSystem::FileExists(s_state.game_settings_interface->GetFileName().c_str()) && + !FileSystem::DeleteFile(s_state.game_settings_interface->GetFileName().c_str(), &error)) { ImGuiFullscreen::OpenInfoMessageDialog( FSUI_STR("Error"), fmt::format(FSUI_FSTR("An error occurred while deleting empty game settings:\n{}"), @@ -881,7 +880,7 @@ void FullscreenUI::Render() } else { - if (!s_game_settings_interface->Save(&error)) + if (!s_state.game_settings_interface->Save(&error)) { ImGuiFullscreen::OpenInfoMessageDialog( FSUI_STR("Error"), @@ -892,7 +891,7 @@ void FullscreenUI::Render() if (System::IsValid()) Host::RunOnCPUThread([]() { System::ReloadGameSettings(false); }); } - s_game_settings_changed.store(false, std::memory_order_release); + s_state.game_settings_changed.store(false, std::memory_order_release); } ImGuiFullscreen::ResetCloseMenuIfNeeded(); @@ -903,14 +902,14 @@ void FullscreenUI::InvalidateCoverCache() if (!IsInitialized()) return; - Host::RunOnCPUThread([]() { s_cover_image_map.clear(); }); + Host::RunOnCPUThread([]() { s_state.cover_image_map.clear(); }); } void FullscreenUI::ReturnToPreviousWindow() { - if (System::IsValid() && s_pause_menu_was_open) + if (System::IsValid() && s_state.pause_menu_was_open) { - s_current_main_window = MainWindowType::PauseMenu; + s_state.current_main_window = MainWindowType::PauseMenu; QueueResetFocus(FocusResetType::ViewChanged); } else @@ -922,28 +921,28 @@ void FullscreenUI::ReturnToPreviousWindow() void FullscreenUI::ReturnToMainWindow() { ClosePauseMenu(); - s_current_main_window = System::IsValid() ? MainWindowType::None : MainWindowType::Landing; + s_state.current_main_window = System::IsValid() ? MainWindowType::None : MainWindowType::Landing; FixStateIfPaused(); } bool FullscreenUI::LoadResources() { - s_app_icon_texture = LoadTexture("images/duck.png"); + s_state.app_icon_texture = LoadTexture("images/duck.png"); - s_fallback_disc_texture = LoadTexture("fullscreenui/media-cdrom.png"); - s_fallback_exe_texture = LoadTexture("fullscreenui/applications-system.png"); - s_fallback_psf_texture = LoadTexture("fullscreenui/multimedia-player.png"); - s_fallback_playlist_texture = LoadTexture("fullscreenui/address-book-new.png"); + s_state.fallback_disc_texture = LoadTexture("fullscreenui/media-cdrom.png"); + s_state.fallback_exe_texture = LoadTexture("fullscreenui/applications-system.png"); + s_state.fallback_psf_texture = LoadTexture("fullscreenui/multimedia-player.png"); + s_state.fallback_playlist_texture = LoadTexture("fullscreenui/address-book-new.png"); return true; } void FullscreenUI::DestroyResources() { - s_app_icon_texture.reset(); - s_fallback_playlist_texture.reset(); - s_fallback_psf_texture.reset(); - s_fallback_exe_texture.reset(); - s_fallback_disc_texture.reset(); + s_state.app_icon_texture.reset(); + s_state.fallback_playlist_texture.reset(); + s_state.fallback_psf_texture.reset(); + s_state.fallback_exe_texture.reset(); + s_state.fallback_disc_texture.reset(); } ////////////////////////////////////////////////////////////////////////// @@ -992,11 +991,11 @@ void FullscreenUI::DoResume() return; CloseSaveStateSelector(); - s_save_state_selector_slots.push_back(std::move(slentry)); - s_save_state_selector_game_path = {}; - s_save_state_selector_loading = true; - s_save_state_selector_open = true; - s_save_state_selector_resuming = true; + s_state.save_state_selector_slots.push_back(std::move(slentry)); + s_state.save_state_selector_game_path = {}; + s_state.save_state_selector_loading = true; + s_state.save_state_selector_open = true; + s_state.save_state_selector_resuming = true; } void FullscreenUI::DoStartFile() @@ -1280,7 +1279,7 @@ void FullscreenUI::DoToggleFullscreen() void FullscreenUI::SwitchToLanding() { - s_current_main_window = MainWindowType::Landing; + s_state.current_main_window = MainWindowType::Landing; QueueResetFocus(FocusResetType::ViewChanged); } @@ -1293,20 +1292,20 @@ void FullscreenUI::DrawLandingTemplate(ImVec2* menu_pos, ImVec2* menu_size) *menu_pos = ImVec2(0.0f, heading_size.y); *menu_size = ImVec2(io.DisplaySize.x, io.DisplaySize.y - heading_size.y - LayoutScale(LAYOUT_FOOTER_HEIGHT)); - if (BeginFullscreenWindow(ImVec2(0.0f, 0.0f), heading_size, "landing_heading", UIPrimaryColor)) + if (BeginFullscreenWindow(ImVec2(0.0f, 0.0f), heading_size, "landing_heading", UIStyle.PrimaryColor)) { - ImFont* const heading_font = g_large_font; + ImFont* const heading_font = UIStyle.LargeFont; ImDrawList* const dl = ImGui::GetWindowDrawList(); SmallString heading_str; ImGui::PushFont(heading_font); - ImGui::PushStyleColor(ImGuiCol_Text, UIPrimaryTextColor); + ImGui::PushStyleColor(ImGuiCol_Text, UIStyle.PrimaryTextColor); // draw branding { const ImVec2 logo_pos = LayoutScale(LAYOUT_MENU_BUTTON_X_PADDING, LAYOUT_MENU_BUTTON_Y_PADDING); const ImVec2 logo_size = LayoutScale(LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY); - dl->AddImage(s_app_icon_texture.get(), logo_pos, logo_pos + logo_size); + dl->AddImage(s_state.app_icon_texture.get(), logo_pos, logo_pos + logo_size); dl->AddText(heading_font, heading_font->FontSize, ImVec2(logo_pos.x + logo_size.x + LayoutScale(LAYOUT_MENU_BUTTON_X_PADDING), logo_pos.y), ImGui::GetColorU32(ImGuiCol_Text), "DuckStation"); @@ -1361,7 +1360,7 @@ void FullscreenUI::DrawLandingWindow() ImVec2 menu_pos, menu_size; DrawLandingTemplate(&menu_pos, &menu_size); - ImGui::PushStyleColor(ImGuiCol_Text, UIBackgroundTextColor); + ImGui::PushStyleColor(ImGuiCol_Text, UIStyle.BackgroundTextColor); if (BeginHorizontalMenu("landing_window", menu_pos, menu_size, 4)) { @@ -1377,7 +1376,7 @@ void FullscreenUI::DrawLandingWindow() GetCachedTexture("fullscreenui/media-cdrom.png"), FSUI_CSTR("Start Game"), FSUI_CSTR("Launch a game from a file, disc, or starts the console without any disc inserted."))) { - s_current_main_window = MainWindowType::StartGame; + s_state.current_main_window = MainWindowType::StartGame; QueueResetFocus(FocusResetType::ViewChanged); } @@ -1391,7 +1390,7 @@ void FullscreenUI::DrawLandingWindow() FSUI_CSTR("Return to desktop mode, or exit the application.")) || (!AreAnyDialogsOpen() && WantsToCloseMenu())) { - s_current_main_window = MainWindowType::Exit; + s_state.current_main_window = MainWindowType::Exit; QueueResetFocus(FocusResetType::ViewChanged); } } @@ -1433,7 +1432,7 @@ void FullscreenUI::DrawStartGameWindow() ImVec2 menu_pos, menu_size; DrawLandingTemplate(&menu_pos, &menu_size); - ImGui::PushStyleColor(ImGuiCol_Text, UIBackgroundTextColor); + ImGui::PushStyleColor(ImGuiCol_Text, UIStyle.BackgroundTextColor); if (BeginHorizontalMenu("start_game_window", menu_pos, menu_size, 4)) { @@ -1462,7 +1461,7 @@ void FullscreenUI::DrawStartGameWindow() FSUI_CSTR("Return to the previous menu.")) || (!AreAnyDialogsOpen() && WantsToCloseMenu())) { - s_current_main_window = MainWindowType::Landing; + s_state.current_main_window = MainWindowType::Landing; QueueResetFocus(FocusResetType::ViewChanged); } } @@ -1497,7 +1496,7 @@ void FullscreenUI::DrawExitWindow() ImVec2 menu_pos, menu_size; DrawLandingTemplate(&menu_pos, &menu_size); - ImGui::PushStyleColor(ImGuiCol_Text, UIBackgroundTextColor); + ImGui::PushStyleColor(ImGuiCol_Text, UIStyle.BackgroundTextColor); if (BeginHorizontalMenu("exit_window", menu_pos, menu_size, 3)) { @@ -1508,7 +1507,7 @@ void FullscreenUI::DrawExitWindow() FSUI_CSTR("Return to the previous menu.")) || WantsToCloseMenu()) { - s_current_main_window = MainWindowType::Landing; + s_state.current_main_window = MainWindowType::Landing; QueueResetFocus(FocusResetType::ViewChanged); } @@ -1538,26 +1537,27 @@ bool FullscreenUI::ShouldShowAdvancedSettings() bool FullscreenUI::IsEditingGameSettings(SettingsInterface* bsi) { - return (bsi == s_game_settings_interface.get()); + return (bsi == s_state.game_settings_interface.get()); } SettingsInterface* FullscreenUI::GetEditingSettingsInterface() { - return s_game_settings_interface ? s_game_settings_interface.get() : Host::Internal::GetBaseSettingsLayer(); + return s_state.game_settings_interface ? s_state.game_settings_interface.get() : + Host::Internal::GetBaseSettingsLayer(); } SettingsInterface* FullscreenUI::GetEditingSettingsInterface(bool game_settings) { - return (game_settings && s_game_settings_interface) ? s_game_settings_interface.get() : - Host::Internal::GetBaseSettingsLayer(); + return (game_settings && s_state.game_settings_interface) ? s_state.game_settings_interface.get() : + Host::Internal::GetBaseSettingsLayer(); } void FullscreenUI::SetSettingsChanged(SettingsInterface* bsi) { - if (bsi && bsi == s_game_settings_interface.get()) - s_game_settings_changed.store(true, std::memory_order_release); + if (bsi && bsi == s_state.game_settings_interface.get()) + s_state.game_settings_changed.store(true, std::memory_order_release); else - s_settings_changed.store(true, std::memory_order_release); + s_state.settings_changed.store(true, std::memory_order_release); } bool FullscreenUI::GetEffectiveBoolSetting(SettingsInterface* bsi, const char* section, const char* key, @@ -1687,11 +1687,11 @@ void FullscreenUI::DrawInputBindingButton(SettingsInterface* bsi, InputBindingIn } } - const float midpoint = bb.Min.y + g_large_font->FontSize + LayoutScale(4.0f); + const float midpoint = bb.Min.y + UIStyle.LargeFont->FontSize + LayoutScale(4.0f); if (oneline) { - ImGui::PushFont(g_large_font); + ImGui::PushFont(UIStyle.LargeFont); const ImVec2 value_size(ImGui::CalcTextSize(value.empty() ? FSUI_CSTR("-") : value.c_str(), nullptr)); const float text_end = bb.Max.x - value_size.x; @@ -1708,12 +1708,12 @@ void FullscreenUI::DrawInputBindingButton(SettingsInterface* bsi, InputBindingIn const ImRect title_bb(bb.Min, ImVec2(bb.Max.x, midpoint)); const ImRect summary_bb(ImVec2(bb.Min.x, midpoint), bb.Max); - ImGui::PushFont(g_large_font); + ImGui::PushFont(UIStyle.LargeFont); ImGui::RenderTextClipped(title_bb.Min, title_bb.Max, show_type ? title.c_str() : display_name, nullptr, nullptr, ImVec2(0.0f, 0.0f), &title_bb); ImGui::PopFont(); - ImGui::PushFont(g_medium_font); + ImGui::PushFont(UIStyle.MediumFont); ImGui::RenderTextClipped(summary_bb.Min, summary_bb.Max, value.empty() ? FSUI_CSTR("No Binding") : value.c_str(), nullptr, nullptr, ImVec2(0.0f, 0.0f), &summary_bb); ImGui::PopFont(); @@ -1732,36 +1732,36 @@ void FullscreenUI::DrawInputBindingButton(SettingsInterface* bsi, InputBindingIn void FullscreenUI::ClearInputBindingVariables() { - s_input_binding_type = InputBindingInfo::Type::Unknown; - s_input_binding_section = {}; - s_input_binding_key = {}; - s_input_binding_display_name = {}; - s_input_binding_new_bindings = {}; - s_input_binding_value_ranges = {}; + s_state.input_binding_type = InputBindingInfo::Type::Unknown; + s_state.input_binding_section = {}; + s_state.input_binding_key = {}; + s_state.input_binding_display_name = {}; + s_state.input_binding_new_bindings = {}; + s_state.input_binding_value_ranges = {}; } void FullscreenUI::BeginInputBinding(SettingsInterface* bsi, InputBindingInfo::Type type, std::string_view section, std::string_view key, std::string_view display_name) { - if (s_input_binding_type != InputBindingInfo::Type::Unknown) + if (s_state.input_binding_type != InputBindingInfo::Type::Unknown) { InputManager::RemoveHook(); ClearInputBindingVariables(); } - s_input_binding_type = type; - s_input_binding_section = section; - s_input_binding_key = key; - s_input_binding_display_name = display_name; - s_input_binding_new_bindings = {}; - s_input_binding_value_ranges = {}; - s_input_binding_timer.Reset(); + s_state.input_binding_type = type; + s_state.input_binding_section = section; + s_state.input_binding_key = key; + s_state.input_binding_display_name = display_name; + s_state.input_binding_new_bindings = {}; + s_state.input_binding_value_ranges = {}; + s_state.input_binding_timer.Reset(); const bool game_settings = IsEditingGameSettings(bsi); InputManager::SetHook([game_settings](InputBindingKey key, float value) -> InputInterceptHook::CallbackResult { // shouldn't happen, just in case - if (s_input_binding_type == InputBindingInfo::Type::Unknown) + if (s_state.input_binding_type == InputBindingInfo::Type::Unknown) return InputInterceptHook::CallbackResult::RemoveHookAndContinueProcessingEvent; // holding the settings lock here will protect the input binding list @@ -1770,17 +1770,17 @@ void FullscreenUI::BeginInputBinding(SettingsInterface* bsi, InputBindingInfo::T float initial_value = value; float min_value = value; InputInterceptHook::CallbackResult default_action = InputInterceptHook::CallbackResult::StopProcessingEvent; - const auto it = std::find_if(s_input_binding_value_ranges.begin(), s_input_binding_value_ranges.end(), + const auto it = std::find_if(s_state.input_binding_value_ranges.begin(), s_state.input_binding_value_ranges.end(), [key](const auto& it) { return it.first.bits == key.bits; }); - if (it != s_input_binding_value_ranges.end()) + if (it != s_state.input_binding_value_ranges.end()) { initial_value = it->second.first; min_value = it->second.second = std::min(it->second.second, value); } else { - s_input_binding_value_ranges.emplace_back(key, std::make_pair(initial_value, min_value)); + s_state.input_binding_value_ranges.emplace_back(key, std::make_pair(initial_value, min_value)); // forward the event to imgui if it's a new key and a release, because this is what triggered the binding to start // if we don't do this, imgui thinks the activate button is held down @@ -1791,7 +1791,7 @@ void FullscreenUI::BeginInputBinding(SettingsInterface* bsi, InputBindingInfo::T const float abs_value = std::abs(value); const bool reverse_threshold = (key.source_subtype == InputSubclass::ControllerAxis && initial_value > 0.5f); - for (InputBindingKey& other_key : s_input_binding_new_bindings) + for (InputBindingKey& other_key : s_state.input_binding_new_bindings) { // if this key is in our new binding list, it's a "release", and we're done if (other_key.MaskDirection() == key.MaskDirection()) @@ -1805,8 +1805,10 @@ void FullscreenUI::BeginInputBinding(SettingsInterface* bsi, InputBindingInfo::T SettingsInterface* bsi = GetEditingSettingsInterface(game_settings); const std::string new_binding(InputManager::ConvertInputBindingKeysToString( - s_input_binding_type, s_input_binding_new_bindings.data(), s_input_binding_new_bindings.size())); - bsi->SetStringValue(s_input_binding_section.c_str(), s_input_binding_key.c_str(), new_binding.c_str()); + s_state.input_binding_type, s_state.input_binding_new_bindings.data(), + s_state.input_binding_new_bindings.size())); + bsi->SetStringValue(s_state.input_binding_section.c_str(), s_state.input_binding_key.c_str(), + new_binding.c_str()); SetSettingsChanged(bsi); ClearInputBindingVariables(); QueueResetFocus(FocusResetType::PopupClosed); @@ -1824,7 +1826,7 @@ void FullscreenUI::BeginInputBinding(SettingsInterface* bsi, InputBindingInfo::T InputBindingKey key_to_add = key; key_to_add.modifier = (value < 0.0f && !reverse_threshold) ? InputModifier::Negate : InputModifier::None; key_to_add.invert = reverse_threshold; - s_input_binding_new_bindings.push_back(key_to_add); + s_state.input_binding_new_bindings.push_back(key_to_add); } return default_action; @@ -1833,9 +1835,9 @@ void FullscreenUI::BeginInputBinding(SettingsInterface* bsi, InputBindingInfo::T void FullscreenUI::DrawInputBindingWindow() { - DebugAssert(s_input_binding_type != InputBindingInfo::Type::Unknown); + DebugAssert(s_state.input_binding_type != InputBindingInfo::Type::Unknown); - const double time_remaining = INPUT_BINDING_TIMEOUT_SECONDS - s_input_binding_timer.GetTimeSeconds(); + const double time_remaining = INPUT_BINDING_TIMEOUT_SECONDS - s_state.input_binding_timer.GetTimeSeconds(); if (time_remaining <= 0.0) { InputManager::RemoveHook(); @@ -1849,7 +1851,7 @@ void FullscreenUI::DrawInputBindingWindow() ImGui::SetNextWindowPos(ImGui::GetIO().DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f)); ImGui::OpenPopup(title); - ImGui::PushFont(g_large_font); + ImGui::PushFont(UIStyle.LargeFont); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, LayoutScale(10.0f)); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, LayoutScale(ImGuiFullscreen::LAYOUT_MENU_BUTTON_X_PADDING, ImGuiFullscreen::LAYOUT_MENU_BUTTON_Y_PADDING)); @@ -1860,8 +1862,8 @@ void FullscreenUI::DrawInputBindingWindow() ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoCollapse)) { - ImGui::TextWrapped("%s", SmallString::from_format(FSUI_FSTR("Setting {} binding {}."), s_input_binding_section, - s_input_binding_display_name) + ImGui::TextWrapped("%s", SmallString::from_format(FSUI_FSTR("Setting {} binding {}."), + s_state.input_binding_section, s_state.input_binding_display_name) .c_str()); ImGui::TextUnformatted(FSUI_CSTR("Push a controller button or axis now.")); ImGui::NewLine(); @@ -1983,7 +1985,7 @@ void FullscreenUI::DrawIntRangeSetting(SettingsInterface* bsi, const char* title ImGui::SetNextWindowSize(LayoutScale(500.0f, 194.0f)); ImGui::SetNextWindowPos(ImGui::GetIO().DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f)); - ImGui::PushFont(g_large_font); + ImGui::PushFont(UIStyle.LargeFont); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, LayoutScale(10.0f)); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, LayoutScale(ImGuiFullscreen::LAYOUT_MENU_BUTTON_X_PADDING, ImGuiFullscreen::LAYOUT_MENU_BUTTON_Y_PADDING)); @@ -2010,7 +2012,7 @@ void FullscreenUI::DrawIntRangeSetting(SettingsInterface* bsi, const char* title } ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(10.0f)); - if (MenuButtonWithoutSummary(FSUI_CSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, g_large_font, + if (MenuButtonWithoutSummary(FSUI_CSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, UIStyle.LargeFont, ImVec2(0.5f, 0.0f))) { ImGui::CloseCurrentPopup(); @@ -2041,7 +2043,7 @@ void FullscreenUI::DrawFloatRangeSetting(SettingsInterface* bsi, const char* tit ImGui::SetNextWindowSize(LayoutScale(500.0f, 194.0f)); ImGui::SetNextWindowPos(ImGui::GetIO().DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f)); - ImGui::PushFont(g_large_font); + ImGui::PushFont(UIStyle.LargeFont); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, LayoutScale(10.0f)); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, LayoutScale(ImGuiFullscreen::LAYOUT_MENU_BUTTON_X_PADDING, ImGuiFullscreen::LAYOUT_MENU_BUTTON_Y_PADDING)); @@ -2071,7 +2073,7 @@ void FullscreenUI::DrawFloatRangeSetting(SettingsInterface* bsi, const char* tit } ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(10.0f)); - if (MenuButtonWithoutSummary(FSUI_CSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, g_large_font, + if (MenuButtonWithoutSummary(FSUI_CSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, UIStyle.LargeFont, ImVec2(0.5f, 0.0f))) { ImGui::CloseCurrentPopup(); @@ -2107,7 +2109,7 @@ void FullscreenUI::DrawFloatSpinBoxSetting(SettingsInterface* bsi, const char* t ImGui::SetNextWindowSize(LayoutScale(500.0f, 194.0f)); ImGui::SetNextWindowPos(ImGui::GetIO().DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f)); - ImGui::PushFont(g_large_font); + ImGui::PushFont(UIStyle.LargeFont); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, LayoutScale(10.0f)); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, LayoutScale(ImGuiFullscreen::LAYOUT_MENU_BUTTON_X_PADDING, ImGuiFullscreen::LAYOUT_MENU_BUTTON_Y_PADDING)); @@ -2154,27 +2156,27 @@ void FullscreenUI::DrawFloatSpinBoxSetting(SettingsInterface* bsi, const char* t // Align value text in middle. ImGui::SetCursorPosY( button_pos.y + - ((LayoutScale(LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY) + padding.y * 2.0f) - g_large_font->FontSize) * 0.5f); + ((LayoutScale(LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY) + padding.y * 2.0f) - UIStyle.LargeFont->FontSize) * 0.5f); ImGui::TextUnformatted(str_value); float step = 0; - if (FloatingButton(ICON_FA_CHEVRON_UP, padding.x, button_pos.y, -1.0f, -1.0f, 1.0f, 0.0f, true, g_large_font, + if (FloatingButton(ICON_FA_CHEVRON_UP, padding.x, button_pos.y, -1.0f, -1.0f, 1.0f, 0.0f, true, UIStyle.LargeFont, &button_pos, true)) { step = step_value; } if (FloatingButton(ICON_FA_CHEVRON_DOWN, button_pos.x - padding.x, button_pos.y, -1.0f, -1.0f, -1.0f, 0.0f, true, - g_large_font, &button_pos, true)) + UIStyle.LargeFont, &button_pos, true)) { step = -step_value; } if (FloatingButton(ICON_FA_KEYBOARD, button_pos.x - padding.x, button_pos.y, -1.0f, -1.0f, -1.0f, 0.0f, true, - g_large_font, &button_pos)) + UIStyle.LargeFont, &button_pos)) { manual_input = true; } if (FloatingButton(ICON_FA_TRASH, button_pos.x - padding.x, button_pos.y, -1.0f, -1.0f, -1.0f, 0.0f, true, - g_large_font, &button_pos)) + UIStyle.LargeFont, &button_pos)) { dlg_value = default_value * multiplier; dlg_value_changed = true; @@ -2201,7 +2203,7 @@ void FullscreenUI::DrawFloatSpinBoxSetting(SettingsInterface* bsi, const char* t SetSettingsChanged(bsi); } - if (MenuButtonWithoutSummary(FSUI_CSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, g_large_font, + if (MenuButtonWithoutSummary(FSUI_CSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, UIStyle.LargeFont, ImVec2(0.5f, 0.0f))) { ImGui::CloseCurrentPopup(); @@ -2359,7 +2361,7 @@ void FullscreenUI::DrawIntSpinBoxSetting(SettingsInterface* bsi, const char* tit ImGui::SetNextWindowSize(LayoutScale(500.0f, 194.0f)); ImGui::SetNextWindowPos(ImGui::GetIO().DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f)); - ImGui::PushFont(g_large_font); + ImGui::PushFont(UIStyle.LargeFont); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, LayoutScale(10.0f)); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, LayoutScale(ImGuiFullscreen::LAYOUT_MENU_BUTTON_X_PADDING, ImGuiFullscreen::LAYOUT_MENU_BUTTON_Y_PADDING)); @@ -2400,27 +2402,27 @@ void FullscreenUI::DrawIntSpinBoxSetting(SettingsInterface* bsi, const char* tit // Align value text in middle. ImGui::SetCursorPosY( button_pos.y + - ((LayoutScale(LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY) + padding.y * 2.0f) - g_large_font->FontSize) * 0.5f); + ((LayoutScale(LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY) + padding.y * 2.0f) - UIStyle.LargeFont->FontSize) * 0.5f); ImGui::TextUnformatted(str_value); s32 step = 0; - if (FloatingButton(ICON_FA_CHEVRON_UP, padding.x, button_pos.y, -1.0f, -1.0f, 1.0f, 0.0f, true, g_large_font, + if (FloatingButton(ICON_FA_CHEVRON_UP, padding.x, button_pos.y, -1.0f, -1.0f, 1.0f, 0.0f, true, UIStyle.LargeFont, &button_pos, true)) { step = step_value; } if (FloatingButton(ICON_FA_CHEVRON_DOWN, button_pos.x - padding.x, button_pos.y, -1.0f, -1.0f, -1.0f, 0.0f, true, - g_large_font, &button_pos, true)) + UIStyle.LargeFont, &button_pos, true)) { step = -step_value; } if (FloatingButton(ICON_FA_KEYBOARD, button_pos.x - padding.x, button_pos.y, -1.0f, -1.0f, -1.0f, 0.0f, true, - g_large_font, &button_pos)) + UIStyle.LargeFont, &button_pos)) { manual_input = true; } if (FloatingButton(ICON_FA_TRASH, button_pos.x - padding.x, button_pos.y, -1.0f, -1.0f, -1.0f, 0.0f, true, - g_large_font, &button_pos)) + UIStyle.LargeFont, &button_pos)) { dlg_value = default_value; dlg_value_changed = true; @@ -2447,7 +2449,7 @@ void FullscreenUI::DrawIntSpinBoxSetting(SettingsInterface* bsi, const char* tit SetSettingsChanged(bsi); } - if (MenuButtonWithoutSummary(FSUI_CSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, g_large_font, + if (MenuButtonWithoutSummary(FSUI_CSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, UIStyle.LargeFont, ImVec2(0.5f, 0.0f))) { ImGui::CloseCurrentPopup(); @@ -2680,7 +2682,7 @@ void FullscreenUI::DrawFolderSetting(SettingsInterface* bsi, const char* title, SetSettingsChanged(bsi); Host::RunOnCPUThread(&EmuFolders::Update); - s_cover_image_map.clear(); + s_state.cover_image_map.clear(); CloseFileSelector(); }); @@ -2726,29 +2728,29 @@ void FullscreenUI::StartAutomaticBinding(u32 port) void FullscreenUI::SwitchToSettings() { - s_game_settings_entry.reset(); - s_game_settings_interface.reset(); - s_game_patch_list = {}; - s_enabled_game_patch_cache = {}; - s_game_cheats_list = {}; - s_enabled_game_cheat_cache = {}; - s_game_cheat_groups = {}; + s_state.game_settings_entry.reset(); + s_state.game_settings_interface.reset(); + s_state.game_patch_list = {}; + s_state.enabled_game_patch_cache = {}; + s_state.game_cheats_list = {}; + s_state.enabled_game_cheat_cache = {}; + s_state.game_cheat_groups = {}; PopulateGraphicsAdapterList(); PopulatePostProcessingChain(GetEditingSettingsInterface(), PostProcessing::Config::DISPLAY_CHAIN_SECTION); - s_current_main_window = MainWindowType::Settings; - s_settings_page = SettingsPage::Interface; + s_state.current_main_window = MainWindowType::Settings; + s_state.settings_page = SettingsPage::Interface; } void FullscreenUI::SwitchToGameSettingsForSerial(std::string_view serial) { - s_game_settings_entry.reset(); - s_game_settings_interface = std::make_unique(System::GetGameSettingsPath(serial)); - s_game_settings_interface->Load(); + s_state.game_settings_entry.reset(); + s_state.game_settings_interface = std::make_unique(System::GetGameSettingsPath(serial)); + s_state.game_settings_interface->Load(); PopulatePatchesAndCheatsList(serial); - s_current_main_window = MainWindowType::Settings; - s_settings_page = SettingsPage::Summary; + s_state.current_main_window = MainWindowType::Settings; + s_state.settings_page = SettingsPage::Summary; QueueResetFocus(FocusResetType::ViewChanged); } @@ -2785,7 +2787,7 @@ bool FullscreenUI::SwitchToGameSettingsForPath(const std::string& path) void FullscreenUI::SwitchToGameSettings(const GameList::Entry* entry) { SwitchToGameSettingsForSerial(entry->serial); - s_game_settings_entry = std::make_unique(*entry); + s_state.game_settings_entry = std::make_unique(*entry); } void FullscreenUI::PopulateGraphicsAdapterList() @@ -2796,58 +2798,58 @@ void FullscreenUI::PopulateGraphicsAdapterList() .c_str()) .value_or(Settings::DEFAULT_GPU_RENDERER); - s_graphics_adapter_list_cache = GPUDevice::GetAdapterListForAPI(Settings::GetRenderAPIForRenderer(renderer)); + s_state.graphics_adapter_list_cache = GPUDevice::GetAdapterListForAPI(Settings::GetRenderAPIForRenderer(renderer)); } void FullscreenUI::PopulateGameListDirectoryCache(SettingsInterface* si) { - s_game_list_directories_cache.clear(); + s_state.game_list_directories_cache.clear(); for (std::string& dir : si->GetStringList("GameList", "Paths")) - s_game_list_directories_cache.emplace_back(std::move(dir), false); + s_state.game_list_directories_cache.emplace_back(std::move(dir), false); for (std::string& dir : si->GetStringList("GameList", "RecursivePaths")) - s_game_list_directories_cache.emplace_back(std::move(dir), true); + s_state.game_list_directories_cache.emplace_back(std::move(dir), true); } void FullscreenUI::PopulatePatchesAndCheatsList(const std::string_view serial) { - s_game_patch_list = Cheats::GetCodeInfoList(serial, std::nullopt, false, true, true); - s_game_cheats_list = - Cheats::GetCodeInfoList(serial, std::nullopt, true, - s_game_settings_interface->GetBoolValue("Cheats", "LoadCheatsFromDatabase", true), true); - s_game_cheat_groups = Cheats::GetCodeListUniquePrefixes(s_game_cheats_list, true); - s_enabled_game_patch_cache = - s_game_settings_interface->GetStringList(Cheats::PATCHES_CONFIG_SECTION, Cheats::PATCH_ENABLE_CONFIG_KEY); - s_enabled_game_cheat_cache = - s_game_settings_interface->GetStringList(Cheats::CHEATS_CONFIG_SECTION, Cheats::PATCH_ENABLE_CONFIG_KEY); + s_state.game_patch_list = Cheats::GetCodeInfoList(serial, std::nullopt, false, true, true); + s_state.game_cheats_list = Cheats::GetCodeInfoList( + serial, std::nullopt, true, s_state.game_settings_interface->GetBoolValue("Cheats", "LoadCheatsFromDatabase", true), + true); + s_state.game_cheat_groups = Cheats::GetCodeListUniquePrefixes(s_state.game_cheats_list, true); + s_state.enabled_game_patch_cache = + s_state.game_settings_interface->GetStringList(Cheats::PATCHES_CONFIG_SECTION, Cheats::PATCH_ENABLE_CONFIG_KEY); + s_state.enabled_game_cheat_cache = + s_state.game_settings_interface->GetStringList(Cheats::CHEATS_CONFIG_SECTION, Cheats::PATCH_ENABLE_CONFIG_KEY); } void FullscreenUI::DoCopyGameSettings() { - if (!s_game_settings_interface) + if (!s_state.game_settings_interface) return; Settings temp_settings; temp_settings.Load(*GetEditingSettingsInterface(false), *GetEditingSettingsInterface(false)); - temp_settings.Save(*s_game_settings_interface, true); - SetSettingsChanged(s_game_settings_interface.get()); + temp_settings.Save(*s_state.game_settings_interface, true); + SetSettingsChanged(s_state.game_settings_interface.get()); ShowToast("Game Settings Copied", fmt::format(FSUI_FSTR("Game settings initialized with global settings for '{}'."), - Path::GetFileTitle(s_game_settings_interface->GetFileName()))); + Path::GetFileTitle(s_state.game_settings_interface->GetFileName()))); } void FullscreenUI::DoClearGameSettings() { - if (!s_game_settings_interface) + if (!s_state.game_settings_interface) return; - s_game_settings_interface->Clear(); - if (!s_game_settings_interface->GetFileName().empty()) - FileSystem::DeleteFile(s_game_settings_interface->GetFileName().c_str()); + s_state.game_settings_interface->Clear(); + if (!s_state.game_settings_interface->GetFileName().empty()) + FileSystem::DeleteFile(s_state.game_settings_interface->GetFileName().c_str()); - SetSettingsChanged(s_game_settings_interface.get()); + SetSettingsChanged(s_state.game_settings_interface.get()); ShowToast("Game Settings Cleared", fmt::format(FSUI_FSTR("Game settings have been cleared for '{}'."), - Path::GetFileTitle(s_game_settings_interface->GetFileName()))); + Path::GetFileTitle(s_state.game_settings_interface->GetFileName()))); } void FullscreenUI::DrawSettingsWindow() @@ -2857,10 +2859,11 @@ void FullscreenUI::DrawSettingsWindow() ImVec2(io.DisplaySize.x, LayoutScale(LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY) + (LayoutScale(LAYOUT_MENU_BUTTON_Y_PADDING) * 2.0f) + LayoutScale(2.0f)); - const float bg_alpha = System::IsValid() ? (s_settings_page == SettingsPage::PostProcessing ? 0.50f : 0.90f) : 1.0f; + const float bg_alpha = + System::IsValid() ? (s_state.settings_page == SettingsPage::PostProcessing ? 0.50f : 0.90f) : 1.0f; if (BeginFullscreenWindow(ImVec2(0.0f, 0.0f), heading_size, "settings_category", - ImVec4(UIPrimaryColor.x, UIPrimaryColor.y, UIPrimaryColor.z, bg_alpha))) + ImVec4(UIStyle.PrimaryColor.x, UIStyle.PrimaryColor.y, UIStyle.PrimaryColor.z, bg_alpha))) { static constexpr float ITEM_WIDTH = 25.0f; @@ -2897,7 +2900,7 @@ void FullscreenUI::DrawSettingsWindow() u32 index = 0; for (u32 i = 0; i < count; i++) { - if (pages[i] == s_settings_page) + if (pages[i] == s_state.settings_page) { index = i; break; @@ -2912,7 +2915,7 @@ void FullscreenUI::DrawSettingsWindow() ImGui::IsKeyPressed(ImGuiKey_NavGamepadTweakSlow, true) || ImGui::IsKeyPressed(ImGuiKey_LeftArrow, true)) { index = (index == 0) ? (count - 1) : (index - 1); - s_settings_page = pages[index]; + s_state.settings_page = pages[index]; QueueResetFocus(FocusResetType::Other); } else if (ImGui::IsKeyPressed(ImGuiKey_GamepadDpadRight, true) || @@ -2920,7 +2923,7 @@ void FullscreenUI::DrawSettingsWindow() ImGui::IsKeyPressed(ImGuiKey_RightArrow, true)) { index = (index + 1) % count; - s_settings_page = pages[index]; + s_state.settings_page = pages[index]; QueueResetFocus(FocusResetType::Other); } } @@ -2928,8 +2931,8 @@ void FullscreenUI::DrawSettingsWindow() if (NavButton(ICON_FA_BACKWARD, true, true)) ReturnToPreviousWindow(); - if (s_game_settings_entry) - NavTitle(s_game_settings_entry->title.c_str()); + if (s_state.game_settings_entry) + NavTitle(s_state.game_settings_entry->title.c_str()); else NavTitle(Host::TranslateToCString(TR_CONTEXT, titles[static_cast(pages[index])].first)); @@ -2940,7 +2943,7 @@ void FullscreenUI::DrawSettingsWindow() if (NavButton(titles[static_cast(pages[i])].second, i == index, true, ITEM_WIDTH, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY)) { - s_settings_page = pages[i]; + s_state.settings_page = pages[i]; QueueResetFocus(FocusResetType::Other); } } @@ -2958,8 +2961,8 @@ void FullscreenUI::DrawSettingsWindow() if (BeginFullscreenWindow( ImVec2(0.0f, heading_size.y), ImVec2(io.DisplaySize.x, io.DisplaySize.y - heading_size.y - LayoutScale(LAYOUT_FOOTER_HEIGHT)), - TinyString::from_format("settings_page_{}", static_cast(s_settings_page)).c_str(), - ImVec4(UIBackgroundColor.x, UIBackgroundColor.y, UIBackgroundColor.z, bg_alpha), 0.0f, + TinyString::from_format("settings_page_{}", static_cast(s_state.settings_page)).c_str(), + ImVec4(UIStyle.BackgroundColor.x, UIStyle.BackgroundColor.y, UIStyle.BackgroundColor.z, bg_alpha), 0.0f, ImVec2(ImGuiFullscreen::LAYOUT_MENU_WINDOW_X_PADDING, 0.0f))) { ResetFocusHere(); @@ -2969,7 +2972,7 @@ void FullscreenUI::DrawSettingsWindow() auto lock = Host::GetSettingsLock(); - switch (s_settings_page) + switch (s_state.settings_page) { case SettingsPage::Summary: DrawSummarySettingsPage(); @@ -3060,38 +3063,38 @@ void FullscreenUI::DrawSummarySettingsPage() MenuHeading(FSUI_CSTR("Details")); - if (s_game_settings_entry) + if (s_state.game_settings_entry) { - if (MenuButton(FSUI_ICONSTR(ICON_FA_WINDOW_MAXIMIZE, "Title"), s_game_settings_entry->title.c_str(), true)) - CopyTextToClipboard(FSUI_STR("Game title copied to clipboard."), s_game_settings_entry->title); - if (MenuButton(FSUI_ICONSTR(ICON_FA_PAGER, "Serial"), s_game_settings_entry->serial.c_str(), true)) - CopyTextToClipboard(FSUI_STR("Game serial copied to clipboard."), s_game_settings_entry->serial); + if (MenuButton(FSUI_ICONSTR(ICON_FA_WINDOW_MAXIMIZE, "Title"), s_state.game_settings_entry->title.c_str(), true)) + CopyTextToClipboard(FSUI_STR("Game title copied to clipboard."), s_state.game_settings_entry->title); + if (MenuButton(FSUI_ICONSTR(ICON_FA_PAGER, "Serial"), s_state.game_settings_entry->serial.c_str(), true)) + CopyTextToClipboard(FSUI_STR("Game serial copied to clipboard."), s_state.game_settings_entry->serial); if (MenuButton(FSUI_ICONSTR(ICON_FA_COMPACT_DISC, "Type"), - GameList::GetEntryTypeDisplayName(s_game_settings_entry->type), true)) + GameList::GetEntryTypeDisplayName(s_state.game_settings_entry->type), true)) { CopyTextToClipboard(FSUI_STR("Game type copied to clipboard."), - GameList::GetEntryTypeDisplayName(s_game_settings_entry->type)); + GameList::GetEntryTypeDisplayName(s_state.game_settings_entry->type)); } if (MenuButton(FSUI_ICONSTR(ICON_FA_BOX, "Region"), - Settings::GetDiscRegionDisplayName(s_game_settings_entry->region), true)) + Settings::GetDiscRegionDisplayName(s_state.game_settings_entry->region), true)) { CopyTextToClipboard(FSUI_STR("Game region copied to clipboard."), - Settings::GetDiscRegionDisplayName(s_game_settings_entry->region)); + Settings::GetDiscRegionDisplayName(s_state.game_settings_entry->region)); } if (MenuButton(FSUI_ICONSTR(ICON_FA_STAR, "Compatibility Rating"), - GameDatabase::GetCompatibilityRatingDisplayName(s_game_settings_entry->dbentry ? - s_game_settings_entry->dbentry->compatibility : - GameDatabase::CompatibilityRating::Unknown), + GameDatabase::GetCompatibilityRatingDisplayName( + s_state.game_settings_entry->dbentry ? s_state.game_settings_entry->dbentry->compatibility : + GameDatabase::CompatibilityRating::Unknown), true)) { CopyTextToClipboard(FSUI_STR("Game compatibility rating copied to clipboard."), GameDatabase::GetCompatibilityRatingDisplayName( - s_game_settings_entry->dbentry ? s_game_settings_entry->dbentry->compatibility : - GameDatabase::CompatibilityRating::Unknown)); + s_state.game_settings_entry->dbentry ? s_state.game_settings_entry->dbentry->compatibility : + GameDatabase::CompatibilityRating::Unknown)); } - if (MenuButton(FSUI_ICONSTR(ICON_FA_FOLDER_OPEN, "Path"), s_game_settings_entry->path.c_str(), true)) + if (MenuButton(FSUI_ICONSTR(ICON_FA_FOLDER_OPEN, "Path"), s_state.game_settings_entry->path.c_str(), true)) { - CopyTextToClipboard(FSUI_STR("Game path copied to clipboard."), s_game_settings_entry->path); + CopyTextToClipboard(FSUI_STR("Game path copied to clipboard."), s_state.game_settings_entry->path); } } else @@ -3597,7 +3600,7 @@ void FullscreenUI::DrawEmulationSettingsPage() "system requirements."); } - ActiveButton(rewind_summary, false, false, ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, g_large_font); + ActiveButton(rewind_summary, false, false, ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, UIStyle.LargeFont); EndMenuButtons(); } @@ -3862,10 +3865,10 @@ void FullscreenUI::DrawControllerSettingsPage() for (u32 macro_index = 0; macro_index < InputManager::NUM_MACRO_BUTTONS_PER_CONTROLLER; macro_index++) { - bool& expanded = s_controller_macro_expanded[global_slot][macro_index]; + bool& expanded = s_state.controller_macro_expanded[global_slot][macro_index]; expanded ^= MenuHeadingButton( SmallString::from_format(fmt::runtime(FSUI_ICONSTR(ICON_PF_EMPTY_KEYCAP, "Macro Button {}")), macro_index + 1), - s_controller_macro_expanded[global_slot][macro_index] ? ICON_FA_CHEVRON_UP : ICON_FA_CHEVRON_DOWN); + s_state.controller_macro_expanded[global_slot][macro_index] ? ICON_FA_CHEVRON_UP : ICON_FA_CHEVRON_DOWN); if (!expanded) continue; @@ -3986,7 +3989,7 @@ void FullscreenUI::DrawControllerSettingsPage() ImGui::SetNextWindowSize(LayoutScale(500.0f, 180.0f)); ImGui::SetNextWindowPos(ImGui::GetIO().DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f)); - ImGui::PushFont(g_large_font); + ImGui::PushFont(UIStyle.LargeFont); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, LayoutScale(10.0f)); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, LayoutScale(ImGuiFullscreen::LAYOUT_MENU_BUTTON_X_PADDING, ImGuiFullscreen::LAYOUT_MENU_BUTTON_Y_PADDING)); @@ -4039,8 +4042,8 @@ void FullscreenUI::DrawControllerSettingsPage() break; case SettingInfo::Type::IntegerList: DrawIntListSetting(bsi, title, description, section.c_str(), si.name, si.IntegerDefaultValue(), si.options, - 0, true, si.IntegerMinValue(), true, LAYOUT_MENU_BUTTON_HEIGHT, g_large_font, - g_medium_font, ci->name); + 0, true, si.IntegerMinValue(), true, LAYOUT_MENU_BUTTON_HEIGHT, UIStyle.LargeFont, + UIStyle.MediumFont, ci->name); break; case SettingInfo::Type::Float: DrawFloatSpinBoxSetting(bsi, title, description, section.c_str(), si.name, si.FloatDefaultValue(), @@ -4064,7 +4067,7 @@ void FullscreenUI::DrawHotkeySettingsPage() BeginMenuButtons(); const HotkeyInfo* last_category = nullptr; - for (const HotkeyInfo* hotkey : s_hotkey_list_cache) + for (const HotkeyInfo* hotkey : s_state.hotkey_list_cache) { if (!last_category || std::strcmp(hotkey->category, last_category->category) != 0) { @@ -4232,11 +4235,11 @@ void FullscreenUI::DrawGraphicsSettingsPage() FSUI_CSTR("Use Global Setting"))) { ImGuiFullscreen::ChoiceDialogOptions options; - options.reserve(s_graphics_adapter_list_cache.size() + 2); + options.reserve(s_state.graphics_adapter_list_cache.size() + 2); if (game_settings) options.emplace_back(FSUI_STR("Use Global Setting"), !current_adapter.has_value()); options.emplace_back(FSUI_STR("Default"), current_adapter.has_value() && current_adapter->empty()); - for (const GPUDevice::AdapterInfo& adapter : s_graphics_adapter_list_cache) + for (const GPUDevice::AdapterInfo& adapter : s_state.graphics_adapter_list_cache) { const bool checked = (current_adapter.has_value() && current_adapter.value() == adapter.name); options.emplace_back(adapter.name, checked); @@ -4388,7 +4391,7 @@ void FullscreenUI::DrawGraphicsSettingsPage() const GPUDevice::AdapterInfo* selected_adapter = nullptr; if (current_adapter.has_value()) { - for (const GPUDevice::AdapterInfo& ai : s_graphics_adapter_list_cache) + for (const GPUDevice::AdapterInfo& ai : s_state.graphics_adapter_list_cache) { if (ai.name == current_adapter->view()) { @@ -4399,8 +4402,8 @@ void FullscreenUI::DrawGraphicsSettingsPage() } else { - if (!s_graphics_adapter_list_cache.empty()) - selected_adapter = &s_graphics_adapter_list_cache.front(); + if (!s_state.graphics_adapter_list_cache.empty()) + selected_adapter = &s_state.graphics_adapter_list_cache.front(); } ImGuiFullscreen::ChoiceDialogOptions options; @@ -4583,7 +4586,7 @@ void FullscreenUI::DrawGraphicsSettingsPage() MenuHeading(FSUI_CSTR("Texture Replacements")); ActiveButton(FSUI_CSTR("The texture cache is currently experimental, and may cause rendering errors in some games."), - false, false, ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, g_large_font); + false, false, ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, UIStyle.LargeFont); DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_ID_BADGE, "Enable Texture Cache"), FSUI_CSTR("Enables caching of guest textures, required for texture replacement."), "GPU", @@ -4630,14 +4633,14 @@ void FullscreenUI::DrawGraphicsSettingsPage() void FullscreenUI::PopulatePostProcessingChain(SettingsInterface* si, const char* section) { const u32 stages = PostProcessing::Config::GetStageCount(*si, section); - s_postprocessing_stages.clear(); - s_postprocessing_stages.reserve(stages); + s_state.postprocessing_stages.clear(); + s_state.postprocessing_stages.reserve(stages); for (u32 i = 0; i < stages; i++) { PostProcessingStageInfo psi; psi.name = PostProcessing::Config::GetStageShaderName(*si, section, i); psi.options = PostProcessing::Config::GetStageOptions(*si, section, i); - s_postprocessing_stages.push_back(std::move(psi)); + s_state.postprocessing_stages.push_back(std::move(psi)); } } @@ -4728,9 +4731,9 @@ void FullscreenUI::DrawPostProcessingSettingsPage() SmallString str; SmallString tstr; - for (u32 stage_index = 0; stage_index < static_cast(s_postprocessing_stages.size()); stage_index++) + for (u32 stage_index = 0; stage_index < static_cast(s_state.postprocessing_stages.size()); stage_index++) { - PostProcessingStageInfo& si = s_postprocessing_stages[stage_index]; + PostProcessingStageInfo& si = s_state.postprocessing_stages[stage_index]; ImGui::PushID(stage_index); str.format(FSUI_FSTR("Stage {}: {}"), stage_index + 1, si.name); @@ -4751,7 +4754,7 @@ void FullscreenUI::DrawPostProcessingSettingsPage() if (MenuButton(FSUI_ICONSTR(ICON_FA_ARROW_DOWN, "Move Down"), FSUI_CSTR("Moves this shader lower in the chain, applying it later."), - (stage_index != (s_postprocessing_stages.size() - 1)))) + (stage_index != (s_state.postprocessing_stages.size() - 1)))) { postprocessing_action = POSTPROCESSING_ACTION_MOVE_DOWN; postprocessing_action_index = stage_index; @@ -4791,7 +4794,7 @@ void FullscreenUI::DrawPostProcessingSettingsPage() ImGui::SetNextWindowSize(LayoutScale(500.0f, 190.0f)); ImGui::SetNextWindowPos(ImGui::GetIO().DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f)); - ImGui::PushFont(g_large_font); + ImGui::PushFont(UIStyle.LargeFont); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, LayoutScale(10.0f)); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, LayoutScale(ImGuiFullscreen::LAYOUT_MENU_BUTTON_X_PADDING, ImGuiFullscreen::LAYOUT_MENU_BUTTON_Y_PADDING)); @@ -4864,7 +4867,7 @@ void FullscreenUI::DrawPostProcessingSettingsPage() #endif ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(10.0f)); - if (MenuButtonWithoutSummary(FSUI_CSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, g_large_font, + if (MenuButtonWithoutSummary(FSUI_CSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, UIStyle.LargeFont, ImVec2(0.5f, 0.0f))) { ImGui::CloseCurrentPopup(); @@ -4890,7 +4893,7 @@ void FullscreenUI::DrawPostProcessingSettingsPage() ImGui::SetNextWindowSize(LayoutScale(500.0f, 190.0f)); ImGui::SetNextWindowPos(ImGui::GetIO().DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f)); - ImGui::PushFont(g_large_font); + ImGui::PushFont(UIStyle.LargeFont); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, LayoutScale(10.0f)); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, LayoutScale(ImGuiFullscreen::LAYOUT_MENU_BUTTON_X_PADDING, ImGuiFullscreen::LAYOUT_MENU_BUTTON_Y_PADDING)); @@ -4962,7 +4965,7 @@ void FullscreenUI::DrawPostProcessingSettingsPage() #endif ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(10.0f)); - if (MenuButtonWithoutSummary(FSUI_CSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, g_large_font, + if (MenuButtonWithoutSummary(FSUI_CSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, UIStyle.LargeFont, ImVec2(0.5f, 0.0f))) { ImGui::CloseCurrentPopup(); @@ -4989,7 +4992,7 @@ void FullscreenUI::DrawPostProcessingSettingsPage() { case POSTPROCESSING_ACTION_REMOVE: { - const PostProcessingStageInfo& si = s_postprocessing_stages[postprocessing_action_index]; + const PostProcessingStageInfo& si = s_state.postprocessing_stages[postprocessing_action_index]; ShowToast(std::string(), fmt::format(FSUI_FSTR("Removed stage {} ({})."), postprocessing_action_index + 1, si.name)); PostProcessing::Config::RemoveStage(*bsi, section, postprocessing_action_index); @@ -5165,11 +5168,11 @@ void FullscreenUI::DrawAchievementsSettingsPage() if (MenuButton(FSUI_ICONSTR(ICON_FA_KEY, "Login"), FSUI_CSTR("Logs in to RetroAchievements."))) { - s_achievements_login_window_open = true; + s_state.achievements_login_window_open = true; QueueResetFocus(FocusResetType::PopupOpened); } - if (s_achievements_login_window_open) + if (s_state.achievements_login_window_open) DrawAchievementsLoginWindow(); } @@ -5217,7 +5220,7 @@ void FullscreenUI::DrawAchievementsLoginWindow() static constexpr auto actually_close_popup = []() { std::memset(username, 0, sizeof(username)); std::memset(password, 0, sizeof(password)); - s_achievements_login_window_open = false; + s_state.achievements_login_window_open = false; QueueResetFocus(FocusResetType::PopupClosed); }; @@ -5226,7 +5229,7 @@ void FullscreenUI::DrawAchievementsLoginWindow() ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, LayoutScale(10.0f)); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, LayoutScale(20.0f, 20.0f)); - ImGui::PushFont(g_large_font); + ImGui::PushFont(UIStyle.LargeFont); const char* popup_title = FSUI_CSTR("RetroAchievements Login"); bool popup_closed = false; @@ -5276,13 +5279,13 @@ void FullscreenUI::DrawAchievementsLoginWindow() // keep popup open on failure // because of the whole popup stack thing, we need to hide the dialog while this popup is visible - s_achievements_login_window_open = false; + s_state.achievements_login_window_open = false; ImGuiFullscreen::OpenInfoMessageDialog( FSUI_STR("Login Error"), fmt::format(FSUI_FSTR("Login Failed.\nError: {}\nPlease check your username and password, and try again."), error.GetDescription()), []() { - s_achievements_login_window_open = true; + s_state.achievements_login_window_open = true; QueueResetFocus(FocusResetType::PopupOpened); }, FSUI_ICONSTR(ICON_FA_TIMES, "Close")); @@ -5387,8 +5390,8 @@ void FullscreenUI::DrawPatchesOrCheatsSettingsPage(bool cheats) { SettingsInterface* bsi = GetEditingSettingsInterface(); - const Cheats::CodeInfoList& code_list = cheats ? s_game_cheats_list : s_game_patch_list; - std::vector& enable_list = cheats ? s_enabled_game_cheat_cache : s_enabled_game_patch_cache; + const Cheats::CodeInfoList& code_list = cheats ? s_state.game_cheats_list : s_state.game_patch_list; + std::vector& enable_list = cheats ? s_state.enabled_game_cheat_cache : s_state.enabled_game_patch_cache; const char* section = cheats ? Cheats::CHEATS_CONFIG_SECTION : Cheats::PATCHES_CONFIG_SECTION; BeginMenuButtons(); @@ -5429,13 +5432,13 @@ void FullscreenUI::DrawPatchesOrCheatsSettingsPage(bool cheats) [cheat_name = ci.name, cheats, section](s32 index, const std::string& title, bool checked) { if (index >= 0) { - const Cheats::CodeInfo* ci = - Cheats::FindCodeInInfoList(cheats ? s_game_cheats_list : s_game_patch_list, cheat_name); + const Cheats::CodeInfo* ci = Cheats::FindCodeInInfoList( + cheats ? s_state.game_cheats_list : s_state.game_patch_list, cheat_name); if (ci) { SettingsInterface* bsi = GetEditingSettingsInterface(); std::vector& enable_list = - cheats ? s_enabled_game_cheat_cache : s_enabled_game_patch_cache; + cheats ? s_state.enabled_game_cheat_cache : s_state.enabled_game_patch_cache; const auto it = std::find(enable_list.begin(), enable_list.end(), ci->name); if (index == 0) { @@ -5482,7 +5485,7 @@ void FullscreenUI::DrawPatchesOrCheatsSettingsPage(bool cheats) ImGui::SetNextWindowSize(LayoutScale(500.0f, 194.0f)); ImGui::SetNextWindowPos(ImGui::GetIO().DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f)); - ImGui::PushFont(g_large_font); + ImGui::PushFont(UIStyle.LargeFont); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, LayoutScale(10.0f)); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, LayoutScale(ImGuiFullscreen::LAYOUT_MENU_BUTTON_X_PADDING, ImGuiFullscreen::LAYOUT_MENU_BUTTON_Y_PADDING)); @@ -5501,24 +5504,24 @@ void FullscreenUI::DrawPatchesOrCheatsSettingsPage(bool cheats) ImVec2 button_pos(ImGui::GetCursorPos()); // Align value text in middle. - ImGui::SetCursorPosY( - button_pos.y + - ((LayoutScale(LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY) + padding.y * 2.0f) - g_large_font->FontSize) * 0.5f); + ImGui::SetCursorPosY(button_pos.y + ((LayoutScale(LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY) + padding.y * 2.0f) - + UIStyle.LargeFont->FontSize) * + 0.5f); ImGui::TextUnformatted(visible_value.c_str(), visible_value.end_ptr()); s32 step = 0; - if (FloatingButton(ICON_FA_CHEVRON_UP, padding.x, button_pos.y, -1.0f, -1.0f, 1.0f, 0.0f, true, g_large_font, - &button_pos, true)) + if (FloatingButton(ICON_FA_CHEVRON_UP, padding.x, button_pos.y, -1.0f, -1.0f, 1.0f, 0.0f, true, + UIStyle.LargeFont, &button_pos, true)) { step = step_value; } if (FloatingButton(ICON_FA_CHEVRON_DOWN, button_pos.x - padding.x, button_pos.y, -1.0f, -1.0f, -1.0f, 0.0f, - true, g_large_font, &button_pos, true)) + true, UIStyle.LargeFont, &button_pos, true)) { step = -step_value; } if (FloatingButton(ICON_FA_TRASH, button_pos.x - padding.x, button_pos.y, -1.0f, -1.0f, -1.0f, 0.0f, true, - g_large_font, &button_pos)) + UIStyle.LargeFont, &button_pos)) { range_value = ci.option_range_start - 1; range_value_changed = true; @@ -5555,7 +5558,7 @@ void FullscreenUI::DrawPatchesOrCheatsSettingsPage(bool cheats) SetSettingsChanged(bsi); } - if (MenuButtonWithoutSummary(FSUI_CSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, g_large_font, + if (MenuButtonWithoutSummary(FSUI_CSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, UIStyle.LargeFont, ImVec2(0.5f, 0.0f))) { ImGui::CloseCurrentPopup(); @@ -5620,8 +5623,8 @@ void FullscreenUI::DrawPatchesOrCheatsSettingsPage(bool cheats) else bsi->SetBoolValue("Cheats", "LoadCheatsFromDatabase", false); SetSettingsChanged(bsi); - if (s_game_settings_entry) - PopulatePatchesAndCheatsList(s_game_settings_entry->serial); + if (s_state.game_settings_entry) + PopulatePatchesAndCheatsList(s_state.game_settings_entry->serial); } if (code_list.empty()) @@ -5631,7 +5634,7 @@ void FullscreenUI::DrawPatchesOrCheatsSettingsPage(bool cheats) } else { - for (const std::string_view& group : s_game_cheat_groups) + for (const std::string_view& group : s_state.game_cheat_groups) { if (group.empty()) MenuHeading(FSUI_CSTR("Ungrouped")); @@ -5679,9 +5682,9 @@ void FullscreenUI::DrawPauseMenu() ImDrawList* dl = ImGui::GetBackgroundDrawList(); const ImVec2 display_size(ImGui::GetIO().DisplaySize); - const ImU32 text_color = ImGui::GetColorU32(UIBackgroundTextColor) | IM_COL32_A_MASK; + const ImU32 text_color = ImGui::GetColorU32(UIStyle.BackgroundTextColor) | IM_COL32_A_MASK; dl->AddRectFilled(ImVec2(0.0f, 0.0f), display_size, - (ImGui::GetColorU32(UIBackgroundColor) & ~IM_COL32_A_MASK) | (200 << IM_COL32_A_SHIFT)); + (ImGui::GetColorU32(UIStyle.BackgroundColor) & ~IM_COL32_A_MASK) | (200 << IM_COL32_A_SHIFT)); // title info { @@ -5695,15 +5698,15 @@ void FullscreenUI::DrawPauseMenu() const float image_width = 60.0f; const float image_height = 60.0f; - const ImVec2 title_size( - g_large_font->CalcTextSizeA(g_large_font->FontSize, std::numeric_limits::max(), -1.0f, title.c_str())); - const ImVec2 subtitle_size( - g_medium_font->CalcTextSizeA(g_medium_font->FontSize, std::numeric_limits::max(), -1.0f, buffer.c_str())); + const ImVec2 title_size(UIStyle.LargeFont->CalcTextSizeA(UIStyle.LargeFont->FontSize, + std::numeric_limits::max(), -1.0f, title.c_str())); + const ImVec2 subtitle_size(UIStyle.MediumFont->CalcTextSizeA( + UIStyle.MediumFont->FontSize, std::numeric_limits::max(), -1.0f, buffer.c_str())); ImVec2 title_pos(display_size.x - LayoutScale(10.0f + image_width + 20.0f) - title_size.x, display_size.y - LayoutScale(LAYOUT_FOOTER_HEIGHT) - LayoutScale(10.0f + image_height)); ImVec2 subtitle_pos(display_size.x - LayoutScale(10.0f + image_width + 20.0f) - subtitle_size.x, - title_pos.y + g_large_font->FontSize + LayoutScale(4.0f)); + title_pos.y + UIStyle.LargeFont->FontSize + LayoutScale(4.0f)); float rp_height = 0.0f; { @@ -5713,24 +5716,25 @@ void FullscreenUI::DrawPauseMenu() if (!rp.empty()) { const float wrap_width = LayoutScale(350.0f); - const ImVec2 rp_size = g_medium_font->CalcTextSizeA(g_medium_font->FontSize, std::numeric_limits::max(), - wrap_width, rp.data(), rp.data() + rp.length()); + const ImVec2 rp_size = + UIStyle.MediumFont->CalcTextSizeA(UIStyle.MediumFont->FontSize, std::numeric_limits::max(), wrap_width, + rp.data(), rp.data() + rp.length()); // Add a small extra gap if any Rich Presence is displayed - rp_height = rp_size.y - g_medium_font->FontSize + LayoutScale(2.0f); + rp_height = rp_size.y - UIStyle.MediumFont->FontSize + LayoutScale(2.0f); const ImVec2 rp_pos(display_size.x - LayoutScale(20.0f + 50.0f + 20.0f) - rp_size.x, - subtitle_pos.y + g_medium_font->FontSize + LayoutScale(4.0f) - rp_height); + subtitle_pos.y + UIStyle.MediumFont->FontSize + LayoutScale(4.0f) - rp_height); title_pos.y -= rp_height; subtitle_pos.y -= rp_height; - DrawShadowedText(dl, g_medium_font, rp_pos, text_color, rp.data(), rp.data() + rp.length(), wrap_width); + DrawShadowedText(dl, UIStyle.MediumFont, rp_pos, text_color, rp.data(), rp.data() + rp.length(), wrap_width); } } - DrawShadowedText(dl, g_large_font, title_pos, text_color, title.c_str()); - DrawShadowedText(dl, g_medium_font, subtitle_pos, text_color, buffer.c_str()); + DrawShadowedText(dl, UIStyle.LargeFont, title_pos, text_color, title.c_str()); + DrawShadowedText(dl, UIStyle.MediumFont, subtitle_pos, text_color, buffer.c_str()); GPUTexture* const cover = GetCoverForCurrentGame(); const ImVec2 image_min(display_size.x - LayoutScale(10.0f + image_width), @@ -5746,10 +5750,10 @@ void FullscreenUI::DrawPauseMenu() { buffer.format("{:%X}", fmt::localtime(std::time(nullptr))); - const ImVec2 time_size(g_large_font->CalcTextSizeA(g_large_font->FontSize, std::numeric_limits::max(), -1.0f, - buffer.c_str(), buffer.end_ptr())); + const ImVec2 time_size(UIStyle.LargeFont->CalcTextSizeA( + UIStyle.LargeFont->FontSize, std::numeric_limits::max(), -1.0f, buffer.c_str(), buffer.end_ptr())); const ImVec2 time_pos(display_size.x - LayoutScale(10.0f) - time_size.x, LayoutScale(10.0f)); - DrawShadowedText(dl, g_large_font, time_pos, text_color, buffer.c_str(), buffer.end_ptr()); + DrawShadowedText(dl, UIStyle.LargeFont, time_pos, text_color, buffer.c_str(), buffer.end_ptr()); const std::string& serial = System::GetGameSerial(); if (!serial.empty()) @@ -5758,18 +5762,18 @@ void FullscreenUI::DrawPauseMenu() const std::time_t session_time = static_cast(System::GetSessionPlayedTime()); buffer.format(FSUI_FSTR("Session: {}"), GameList::FormatTimespan(session_time, true)); - const ImVec2 session_size(g_medium_font->CalcTextSizeA(g_medium_font->FontSize, std::numeric_limits::max(), - -1.0f, buffer.c_str(), buffer.end_ptr())); + const ImVec2 session_size(UIStyle.MediumFont->CalcTextSizeA( + UIStyle.MediumFont->FontSize, std::numeric_limits::max(), -1.0f, buffer.c_str(), buffer.end_ptr())); const ImVec2 session_pos(display_size.x - LayoutScale(10.0f) - session_size.x, - time_pos.y + g_large_font->FontSize + LayoutScale(4.0f)); - DrawShadowedText(dl, g_medium_font, session_pos, text_color, buffer.c_str(), buffer.end_ptr()); + time_pos.y + UIStyle.LargeFont->FontSize + LayoutScale(4.0f)); + DrawShadowedText(dl, UIStyle.MediumFont, session_pos, text_color, buffer.c_str(), buffer.end_ptr()); buffer.format(FSUI_FSTR("All Time: {}"), GameList::FormatTimespan(cached_played_time + session_time, true)); - const ImVec2 total_size(g_medium_font->CalcTextSizeA(g_medium_font->FontSize, std::numeric_limits::max(), - -1.0f, buffer.c_str(), buffer.end_ptr())); + const ImVec2 total_size(UIStyle.MediumFont->CalcTextSizeA( + UIStyle.MediumFont->FontSize, std::numeric_limits::max(), -1.0f, buffer.c_str(), buffer.end_ptr())); const ImVec2 total_pos(display_size.x - LayoutScale(10.0f) - total_size.x, - session_pos.y + g_medium_font->FontSize + LayoutScale(4.0f)); - DrawShadowedText(dl, g_medium_font, total_pos, text_color, buffer.c_str(), buffer.end_ptr()); + session_pos.y + UIStyle.MediumFont->FontSize + LayoutScale(4.0f)); + DrawShadowedText(dl, UIStyle.MediumFont, total_pos, text_color, buffer.c_str(), buffer.end_ptr()); } } @@ -5786,11 +5790,11 @@ void FullscreenUI::DrawPauseMenu() }; ResetFocusHere(); - BeginMenuButtons(submenu_item_count[static_cast(s_current_pause_submenu)], 1.0f, + BeginMenuButtons(submenu_item_count[static_cast(s_state.current_pause_submenu)], 1.0f, ImGuiFullscreen::LAYOUT_MENU_BUTTON_X_PADDING, ImGuiFullscreen::LAYOUT_MENU_BUTTON_Y_PADDING, ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY); - switch (s_current_pause_submenu) + switch (s_state.current_pause_submenu) { case PauseSubMenu::None: { @@ -5809,13 +5813,13 @@ void FullscreenUI::DrawPauseMenu() if (ActiveButton(FSUI_ICONSTR(ICON_FA_UNDO, "Load State"), false, has_game)) { if (OpenSaveStateSelector(true)) - s_current_main_window = MainWindowType::None; + s_state.current_main_window = MainWindowType::None; } if (ActiveButton(FSUI_ICONSTR(ICON_FA_DOWNLOAD, "Save State"), false, has_game)) { if (OpenSaveStateSelector(false)) - s_current_main_window = MainWindowType::None; + s_state.current_main_window = MainWindowType::None; } if (ActiveButton(FSUI_ICONSTR(ICON_FA_GAMEPAD, "Toggle Analog"), false)) @@ -5847,7 +5851,7 @@ void FullscreenUI::DrawPauseMenu() if (ActiveButton(FSUI_ICONSTR(ICON_FA_COMPACT_DISC, "Change Disc"), false)) { - s_current_main_window = MainWindowType::None; + s_state.current_main_window = MainWindowType::None; DoChangeDisc(); } @@ -5976,19 +5980,19 @@ bool FullscreenUI::InitializeSaveStateListEntryFromPath(SaveStateListEntry* li, void FullscreenUI::ClearSaveStateEntryList() { - for (SaveStateListEntry& entry : s_save_state_selector_slots) + for (SaveStateListEntry& entry : s_state.save_state_selector_slots) { if (entry.preview_texture) g_gpu_device->RecycleTexture(std::move(entry.preview_texture)); } - s_save_state_selector_slots.clear(); + s_state.save_state_selector_slots.clear(); } u32 FullscreenUI::PopulateSaveStateListEntries(const std::string& title, const std::string& serial) { ClearSaveStateEntryList(); - if (s_save_state_selector_loading) + if (s_state.save_state_selector_loading) { std::optional ssi = System::GetUndoSaveStateInfo(); if (ssi) @@ -5998,7 +6002,7 @@ u32 FullscreenUI::PopulateSaveStateListEntries(const std::string& title, const s li.summary = FSUI_STR("Restores the state of the system prior to the last state loaded."); if (ssi->screenshot.IsValid()) li.preview_texture = g_gpu_device->FetchAndUploadTextureImage(ssi->screenshot); - s_save_state_selector_slots.push_back(std::move(li)); + s_state.save_state_selector_slots.push_back(std::move(li)); } } @@ -6007,19 +6011,19 @@ u32 FullscreenUI::PopulateSaveStateListEntries(const std::string& title, const s for (s32 i = 1; i <= System::PER_GAME_SAVE_STATE_SLOTS; i++) { SaveStateListEntry li; - if (InitializeSaveStateListEntryFromSerial(&li, serial, i, false) || !s_save_state_selector_loading) - s_save_state_selector_slots.push_back(std::move(li)); + if (InitializeSaveStateListEntryFromSerial(&li, serial, i, false) || !s_state.save_state_selector_loading) + s_state.save_state_selector_slots.push_back(std::move(li)); } } for (s32 i = 1; i <= System::GLOBAL_SAVE_STATE_SLOTS; i++) { SaveStateListEntry li; - if (InitializeSaveStateListEntryFromSerial(&li, serial, i, true) || !s_save_state_selector_loading) - s_save_state_selector_slots.push_back(std::move(li)); + if (InitializeSaveStateListEntryFromSerial(&li, serial, i, true) || !s_state.save_state_selector_loading) + s_state.save_state_selector_slots.push_back(std::move(li)); } - return static_cast(s_save_state_selector_slots.size()); + return static_cast(s_state.save_state_selector_slots.size()); } bool FullscreenUI::OpenLoadStateSelectorForGame(const std::string& game_path) @@ -6028,12 +6032,12 @@ bool FullscreenUI::OpenLoadStateSelectorForGame(const std::string& game_path) const GameList::Entry* entry = GameList::GetEntryForPath(game_path); if (entry) { - s_save_state_selector_loading = true; + s_state.save_state_selector_loading = true; if (PopulateSaveStateListEntries(entry->title, entry->serial) > 0) { - s_save_state_selector_open = true; - s_save_state_selector_resuming = false; - s_save_state_selector_game_path = game_path; + s_state.save_state_selector_open = true; + s_state.save_state_selector_resuming = false; + s_state.save_state_selector_game_path = game_path; return true; } } @@ -6044,12 +6048,12 @@ bool FullscreenUI::OpenLoadStateSelectorForGame(const std::string& game_path) bool FullscreenUI::OpenSaveStateSelector(bool is_loading) { - s_save_state_selector_game_path = {}; - s_save_state_selector_loading = is_loading; - s_save_state_selector_resuming = false; + s_state.save_state_selector_game_path = {}; + s_state.save_state_selector_loading = is_loading; + s_state.save_state_selector_resuming = false; if (PopulateSaveStateListEntries(System::GetGameTitle(), System::GetGameSerial()) > 0) { - s_save_state_selector_open = true; + s_state.save_state_selector_open = true; QueueResetFocus(FocusResetType::PopupOpened); return true; } @@ -6060,14 +6064,14 @@ bool FullscreenUI::OpenSaveStateSelector(bool is_loading) void FullscreenUI::CloseSaveStateSelector() { - if (s_save_state_selector_open) + if (s_state.save_state_selector_open) QueueResetFocus(FocusResetType::PopupClosed); ClearSaveStateEntryList(); - s_save_state_selector_open = false; - s_save_state_selector_loading = false; - s_save_state_selector_resuming = false; - s_save_state_selector_game_path = {}; + s_state.save_state_selector_open = false; + s_state.save_state_selector_loading = false; + s_state.save_state_selector_resuming = false; + s_state.save_state_selector_game_path = {}; } void FullscreenUI::DrawSaveStateSelector(bool is_loading) @@ -6109,7 +6113,7 @@ void FullscreenUI::DrawSaveStateSelector(bool is_loading) ImVec2(io.DisplaySize.x, LayoutScale(LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY) + (LayoutScale(LAYOUT_MENU_BUTTON_Y_PADDING) * 2.0f) + LayoutScale(2.0f)); - ImGui::PushStyleColor(ImGuiCol_ChildBg, ModAlpha(UIPrimaryColor, 0.9f)); + ImGui::PushStyleColor(ImGuiCol_ChildBg, ModAlpha(UIStyle.PrimaryColor, 0.9f)); bool closed = false; bool was_close_not_back = false; @@ -6126,7 +6130,7 @@ void FullscreenUI::DrawSaveStateSelector(bool is_loading) } ImGui::PopStyleColor(); - ImGui::PushStyleColor(ImGuiCol_ChildBg, ModAlpha(UIBackgroundColor, 0.9f)); + ImGui::PushStyleColor(ImGuiCol_ChildBg, ModAlpha(UIStyle.BackgroundColor, 0.9f)); ImGui::SetCursorPos(ImVec2(0.0f, heading_size.y)); if (IsFocusResetFromWindowChange()) @@ -6149,8 +6153,8 @@ void FullscreenUI::DrawSaveStateSelector(bool is_loading) const float image_width = item_width - (style.FramePadding.x * 2.0f); const float image_height = image_width / 1.33f; const ImVec2 image_size(image_width, image_height); - const float item_height = (style.FramePadding.y * 2.0f) + image_height + title_spacing + g_large_font->FontSize + - summary_spacing + g_medium_font->FontSize; + const float item_height = (style.FramePadding.y * 2.0f) + image_height + title_spacing + + UIStyle.LargeFont->FontSize + summary_spacing + UIStyle.MediumFont->FontSize; const ImVec2 item_size(item_width, item_height); const u32 grid_count_x = static_cast(std::floor(ImGui::GetWindowWidth() / item_width_with_spacing)); const float start_x = @@ -6159,25 +6163,25 @@ void FullscreenUI::DrawSaveStateSelector(bool is_loading) u32 grid_x = 0; ImGui::SetCursorPos(ImVec2(start_x, 0.0f)); - for (u32 i = 0; i < s_save_state_selector_slots.size();) + for (u32 i = 0; i < s_state.save_state_selector_slots.size();) { - SaveStateListEntry& entry = s_save_state_selector_slots[i]; - if (static_cast(i) == s_save_state_selector_submenu_index) + SaveStateListEntry& entry = s_state.save_state_selector_slots[i]; + if (static_cast(i) == s_state.save_state_selector_submenu_index) { // can't use a choice dialog here, because we're already in a modal... ImGuiFullscreen::PushResetLayout(); - ImGui::PushFont(g_large_font); + ImGui::PushFont(UIStyle.LargeFont); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, LayoutScale(10.0f)); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, LayoutScale(LAYOUT_MENU_BUTTON_X_PADDING, LAYOUT_MENU_BUTTON_Y_PADDING)); ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f); - ImGui::PushStyleColor(ImGuiCol_Text, UIPrimaryTextColor); - ImGui::PushStyleColor(ImGuiCol_TitleBg, UIPrimaryDarkColor); - ImGui::PushStyleColor(ImGuiCol_TitleBgActive, UIPrimaryColor); + ImGui::PushStyleColor(ImGuiCol_Text, UIStyle.PrimaryTextColor); + ImGui::PushStyleColor(ImGuiCol_TitleBg, UIStyle.PrimaryDarkColor); + ImGui::PushStyleColor(ImGuiCol_TitleBgActive, UIStyle.PrimaryColor); const float width = LayoutScale(600.0f); - const float title_height = - g_large_font->FontSize + ImGui::GetStyle().FramePadding.y * 2.0f + ImGui::GetStyle().WindowPadding.y * 2.0f; + const float title_height = UIStyle.LargeFont->FontSize + ImGui::GetStyle().FramePadding.y * 2.0f + + ImGui::GetStyle().WindowPadding.y * 2.0f; const float height = title_height + ((LayoutScale(LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY) + (LayoutScale(LAYOUT_MENU_BUTTON_Y_PADDING) * 2.0f)) * @@ -6190,7 +6194,7 @@ void FullscreenUI::DrawSaveStateSelector(bool is_loading) if (ImGui::BeginPopupModal(entry.title.c_str(), &is_open, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove)) { - ImGui::PushStyleColor(ImGuiCol_Text, UIBackgroundTextColor); + ImGui::PushStyleColor(ImGuiCol_Text, UIStyle.BackgroundTextColor); BeginMenuButtons(); @@ -6218,10 +6222,10 @@ void FullscreenUI::DrawSaveStateSelector(bool is_loading) else if (FileSystem::DeleteFile(entry.path.c_str())) { ShowToast({}, fmt::format(FSUI_FSTR("{} deleted."), ImGuiFullscreen::RemoveHash(entry.title))); - s_save_state_selector_slots.erase(s_save_state_selector_slots.begin() + i); + s_state.save_state_selector_slots.erase(s_state.save_state_selector_slots.begin() + i); removed = true; - if (s_save_state_selector_slots.empty()) + if (s_state.save_state_selector_slots.empty()) { closed = true; was_close_not_back = true; @@ -6253,7 +6257,7 @@ void FullscreenUI::DrawSaveStateSelector(bool is_loading) } if (!is_open) - s_save_state_selector_submenu_index = -1; + s_state.save_state_selector_submenu_index = -1; ImGui::PopStyleColor(3); ImGui::PopStyleVar(3); @@ -6305,17 +6309,17 @@ void FullscreenUI::DrawSaveStateSelector(bool is_loading) ImVec2(1.0f, 1.0f), IM_COL32(255, 255, 255, 255)); const ImVec2 title_pos(bb.Min.x, bb.Min.y + image_height + title_spacing); - const ImRect title_bb(title_pos, ImVec2(bb.Max.x, title_pos.y + g_large_font->FontSize)); - ImGui::PushFont(g_large_font); + const ImRect title_bb(title_pos, ImVec2(bb.Max.x, title_pos.y + UIStyle.LargeFont->FontSize)); + ImGui::PushFont(UIStyle.LargeFont); ImGui::RenderTextClipped(title_bb.Min, title_bb.Max, entry.title.c_str(), nullptr, nullptr, ImVec2(0.0f, 0.0f), &title_bb); ImGui::PopFont(); if (!entry.summary.empty()) { - const ImVec2 summary_pos(bb.Min.x, title_pos.y + g_large_font->FontSize + summary_spacing); - const ImRect summary_bb(summary_pos, ImVec2(bb.Max.x, summary_pos.y + g_medium_font->FontSize)); - ImGui::PushFont(g_medium_font); + const ImVec2 summary_pos(bb.Min.x, title_pos.y + UIStyle.LargeFont->FontSize + summary_spacing); + const ImRect summary_bb(summary_pos, ImVec2(bb.Max.x, summary_pos.y + UIStyle.MediumFont->FontSize)); + ImGui::PushFont(UIStyle.MediumFont); ImGui::RenderTextClipped(summary_bb.Min, summary_bb.Max, entry.summary.c_str(), nullptr, nullptr, ImVec2(0.0f, 0.0f), &summary_bb); ImGui::PopFont(); @@ -6335,7 +6339,7 @@ void FullscreenUI::DrawSaveStateSelector(bool is_loading) (ImGui::IsItemClicked(ImGuiMouseButton_Right) || ImGui::IsKeyPressed(ImGuiKey_NavGamepadInput, false) || ImGui::IsKeyPressed(ImGuiKey_F1, false))) { - s_save_state_selector_submenu_index = static_cast(i); + s_state.save_state_selector_submenu_index = static_cast(i); } } @@ -6386,7 +6390,7 @@ void FullscreenUI::DrawSaveStateSelector(bool is_loading) CloseSaveStateSelector(); if (was_close_not_back) ReturnToMainWindow(); - else if (s_current_main_window != MainWindowType::GameList) + else if (s_state.current_main_window != MainWindowType::GameList) ReturnToPreviousWindow(); } } @@ -6398,11 +6402,11 @@ bool FullscreenUI::OpenLoadStateSelectorForGameResume(const GameList::Entry* ent return false; CloseSaveStateSelector(); - s_save_state_selector_slots.push_back(std::move(slentry)); - s_save_state_selector_game_path = entry->path; - s_save_state_selector_loading = true; - s_save_state_selector_open = true; - s_save_state_selector_resuming = true; + s_state.save_state_selector_slots.push_back(std::move(slentry)); + s_state.save_state_selector_game_path = entry->path; + s_state.save_state_selector_loading = true; + s_state.save_state_selector_open = true; + s_state.save_state_selector_resuming = true; QueueResetFocus(FocusResetType::PopupOpened); return true; } @@ -6413,7 +6417,7 @@ void FullscreenUI::DrawResumeStateSelector() ImGui::SetNextWindowPos(ImGui::GetIO().DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f)); ImGui::OpenPopup(FSUI_CSTR("Load Resume State")); - ImGui::PushFont(g_large_font); + ImGui::PushFont(UIStyle.LargeFont); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, LayoutScale(10.0f)); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, LayoutScale(20.0f, 20.0f)); @@ -6421,7 +6425,7 @@ void FullscreenUI::DrawResumeStateSelector() if (ImGui::BeginPopupModal(FSUI_CSTR("Load Resume State"), &is_open, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize)) { - SaveStateListEntry& entry = s_save_state_selector_slots.front(); + SaveStateListEntry& entry = s_state.save_state_selector_slots.front(); SmallString time; TimeToPrintableString(&time, entry.timestamp); ImGui::TextWrapped( @@ -6445,13 +6449,13 @@ void FullscreenUI::DrawResumeStateSelector() if (ActiveButton(FSUI_ICONSTR(ICON_FA_PLAY, "Load State"), false)) { - DoStartPath(s_save_state_selector_game_path, std::move(entry.path)); + DoStartPath(s_state.save_state_selector_game_path, std::move(entry.path)); is_open = false; } if (ActiveButton(FSUI_ICONSTR(ICON_FA_LIGHTBULB, "Clean Boot"), false)) { - DoStartPath(s_save_state_selector_game_path); + DoStartPath(s_state.save_state_selector_game_path); is_open = false; } @@ -6459,7 +6463,7 @@ void FullscreenUI::DrawResumeStateSelector() { if (FileSystem::DeleteFile(entry.path.c_str())) { - DoStartPath(s_save_state_selector_game_path); + DoStartPath(s_state.save_state_selector_game_path); is_open = false; } else @@ -6484,10 +6488,10 @@ void FullscreenUI::DrawResumeStateSelector() if (!is_open) { ClearSaveStateEntryList(); - s_save_state_selector_open = false; - s_save_state_selector_loading = false; - s_save_state_selector_resuming = false; - s_save_state_selector_game_path = {}; + s_state.save_state_selector_open = false; + s_state.save_state_selector_loading = false; + s_state.save_state_selector_resuming = false; + s_state.save_state_selector_game_path = {}; } else { @@ -6497,7 +6501,7 @@ void FullscreenUI::DrawResumeStateSelector() void FullscreenUI::DoLoadState(std::string path) { - Host::RunOnCPUThread([boot_path = s_save_state_selector_game_path, path = std::move(path)]() { + Host::RunOnCPUThread([boot_path = s_state.save_state_selector_game_path, path = std::move(path)]() { CloseSaveStateSelector(); if (System::IsValid()) @@ -6532,10 +6536,10 @@ void FullscreenUI::DoSaveState(s32 slot, bool global) if (!System::IsValid()) return; - std::string filename(global ? System::GetGlobalSaveStateFileName(slot) : - System::GetGameSaveStateFileName(System::GetGameSerial(), slot)); + std::string path(global ? System::GetGlobalSaveStateFileName(slot) : + System::GetGameSaveStateFileName(System::GetGameSerial(), slot)); Error error; - if (!System::SaveState(filename.c_str(), &error, g_settings.create_save_state_backups)) + if (!System::SaveState(std::move(path), &error, g_settings.create_save_state_backups, false)) { ShowToast(std::string(), fmt::format(TRANSLATE_FS("System", "Failed to save state: {}"), error.GetDescription())); } @@ -6549,8 +6553,8 @@ void FullscreenUI::PopulateGameListEntryList() const bool merge_disc_sets = Host::GetBaseBoolSettingValue("Main", "FullscreenUIMergeDiscSets", true); const u32 count = GameList::GetEntryCount(); - s_game_list_sorted_entries.clear(); - s_game_list_sorted_entries.reserve(count); + s_state.game_list_sorted_entries.clear(); + s_state.game_list_sorted_entries.reserve(count); for (u32 i = 0; i < count; i++) { const GameList::Entry* entry = GameList::GetEntryByIndex(i); @@ -6565,10 +6569,10 @@ void FullscreenUI::PopulateGameListEntryList() continue; } - s_game_list_sorted_entries.push_back(entry); + s_state.game_list_sorted_entries.push_back(entry); } - std::sort(s_game_list_sorted_entries.begin(), s_game_list_sorted_entries.end(), + std::sort(s_state.game_list_sorted_entries.begin(), s_state.game_list_sorted_entries.end(), [sort, reverse](const GameList::Entry* lhs, const GameList::Entry* rhs) { switch (sort) { @@ -6660,7 +6664,8 @@ void FullscreenUI::DrawGameListWindow() const float bg_alpha = System::IsValid() ? 0.90f : 1.0f; - if (BeginFullscreenWindow(ImVec2(0.0f, 0.0f), heading_size, "gamelist_view", MulAlpha(UIPrimaryColor, bg_alpha))) + if (BeginFullscreenWindow(ImVec2(0.0f, 0.0f), heading_size, "gamelist_view", + MulAlpha(UIStyle.PrimaryColor, bg_alpha))) { static constexpr float ITEM_WIDTH = 25.0f; static constexpr const char* icons[] = {ICON_FA_BORDER_ALL, ICON_FA_LIST}; @@ -6672,15 +6677,15 @@ void FullscreenUI::DrawGameListWindow() if (NavButton(ICON_FA_BACKWARD, true, true)) ReturnToPreviousWindow(); - NavTitle(Host::TranslateToCString(TR_CONTEXT, titles[static_cast(s_game_list_view)])); + NavTitle(Host::TranslateToCString(TR_CONTEXT, titles[static_cast(s_state.game_list_view)])); RightAlignNavButtons(count, ITEM_WIDTH, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY); for (u32 i = 0; i < count; i++) { - if (NavButton(icons[i], static_cast(i) == s_game_list_view, true, ITEM_WIDTH, + if (NavButton(icons[i], static_cast(i) == s_state.game_list_view, true, ITEM_WIDTH, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY)) { - s_game_list_view = static_cast(i); + s_state.game_list_view = static_cast(i); } } @@ -6691,15 +6696,15 @@ void FullscreenUI::DrawGameListWindow() if (ImGui::IsKeyPressed(ImGuiKey_NavGamepadMenu, false) || ImGui::IsKeyPressed(ImGuiKey_F1, false)) { - s_game_list_view = (s_game_list_view == GameListView::Grid) ? GameListView::List : GameListView::Grid; + s_state.game_list_view = (s_state.game_list_view == GameListView::Grid) ? GameListView::List : GameListView::Grid; } else if (ImGui::IsKeyPressed(ImGuiKey_GamepadStart, false) || ImGui::IsKeyPressed(ImGuiKey_F2)) { - s_current_main_window = MainWindowType::GameListSettings; + s_state.current_main_window = MainWindowType::GameListSettings; QueueResetFocus(FocusResetType::ViewChanged); } - switch (s_game_list_view) + switch (s_state.game_list_view) { case GameListView::Grid: DrawGameGrid(heading_size); @@ -6759,7 +6764,7 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size) SmallString summary; - for (const GameList::Entry* entry : s_game_list_sorted_entries) + for (const GameList::Entry* entry : s_state.game_list_sorted_entries) { ImRect bb; bool visible, hovered; @@ -6784,19 +6789,19 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size) ImGui::GetWindowDrawList()->AddImage(cover_texture, image_rect.Min, image_rect.Max, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), IM_COL32(255, 255, 255, 255)); - const float midpoint = bb.Min.y + g_large_font->FontSize + LayoutScale(4.0f); + const float midpoint = bb.Min.y + UIStyle.LargeFont->FontSize + LayoutScale(4.0f); const float text_start_x = bb.Min.x + image_size.x + LayoutScale(15.0f); const ImRect title_bb(ImVec2(text_start_x, bb.Min.y), ImVec2(bb.Max.x, midpoint)); const ImRect summary_bb(ImVec2(text_start_x, midpoint), bb.Max); - ImGui::PushFont(g_large_font); + ImGui::PushFont(UIStyle.LargeFont); ImGui::RenderTextClipped(title_bb.Min, title_bb.Max, entry->title.c_str(), entry->title.c_str() + entry->title.size(), nullptr, ImVec2(0.0f, 0.0f), &title_bb); ImGui::PopFont(); if (!summary.empty()) { - ImGui::PushFont(g_medium_font); + ImGui::PushFont(UIStyle.MediumFont); ImGui::RenderTextClipped(summary_bb.Min, summary_bb.Max, summary.c_str(), summary.end_ptr(), nullptr, ImVec2(0.0f, 0.0f), &summary_bb); ImGui::PopFont(); @@ -6824,7 +6829,7 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size) } EndFullscreenColumnWindow(); - if (BeginFullscreenColumnWindow(-530.0f, 0.0f, "game_list_info", UIPrimaryDarkColor)) + if (BeginFullscreenColumnWindow(-530.0f, 0.0f, "game_list_info", UIStyle.PrimaryDarkColor)) { const GPUTexture* cover_texture = selected_entry ? GetGameListCover(selected_entry) : GetTextureForGameListEntryType(GameList::EntryType::Count); @@ -6854,13 +6859,13 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size) if (selected_entry) { // title - ImGui::PushFont(g_large_font); + ImGui::PushFont(UIStyle.LargeFont); text_width = ImGui::CalcTextSize(selected_entry->title.c_str(), nullptr, false, work_width).x; ImGui::SetCursorPosX((work_width - text_width) / 2.0f); ImGui::TextWrapped("%s", selected_entry->title.c_str()); ImGui::PopFont(); - ImGui::PushFont(g_medium_font); + ImGui::PushFont(UIStyle.MediumFont); // developer if (selected_entry->dbentry && !selected_entry->dbentry->developer.empty()) @@ -6936,7 +6941,7 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size) { // title const char* title = FSUI_CSTR("No Game Selected"); - ImGui::PushFont(g_large_font); + ImGui::PushFont(UIStyle.LargeFont); text_width = ImGui::CalcTextSize(title, nullptr, false, work_width).x; ImGui::SetCursorPosX((work_width - text_width) / 2.0f); ImGui::TextWrapped("%s", title); @@ -6961,7 +6966,7 @@ void FullscreenUI::DrawGameGrid(const ImVec2& heading_size) if (!BeginFullscreenWindow( ImVec2(0.0f, heading_size.y), ImVec2(io.DisplaySize.x, io.DisplaySize.y - heading_size.y - LayoutScale(LAYOUT_FOOTER_HEIGHT)), "game_grid", - UIBackgroundColor)) + UIStyle.BackgroundColor)) { EndFullscreenWindow(); return; @@ -6982,7 +6987,7 @@ void FullscreenUI::DrawGameGrid(const ImVec2& heading_size) const float image_width = item_width - (style.FramePadding.x * 2.0f); const float image_height = image_width; const ImVec2 image_size(image_width, image_height); - const float item_height = (style.FramePadding.y * 2.0f) + image_height + title_spacing + g_medium_font->FontSize; + const float item_height = (style.FramePadding.y * 2.0f) + image_height + title_spacing + UIStyle.MediumFont->FontSize; const ImVec2 item_size(item_width, item_height); const u32 grid_count_x = static_cast(std::floor(ImGui::GetWindowWidth() / item_width_with_spacing)); const float start_x = @@ -6992,7 +6997,7 @@ void FullscreenUI::DrawGameGrid(const ImVec2& heading_size) u32 grid_x = 0; ImGui::SetCursorPos(ImVec2(start_x, 0.0f)); - for (const GameList::Entry* entry : s_game_list_sorted_entries) + for (const GameList::Entry* entry : s_state.game_list_sorted_entries) { ImGuiWindow* window = ImGui::GetCurrentWindow(); if (window->SkipItems) @@ -7034,7 +7039,7 @@ void FullscreenUI::DrawGameGrid(const ImVec2& heading_size) const std::string_view title( std::string_view(entry->title).substr(0, (entry->title.length() > 31) ? 31 : std::string_view::npos)); draw_title.format("{}{}", title, (title.length() == entry->title.length()) ? "" : "..."); - ImGui::PushFont(g_medium_font); + ImGui::PushFont(UIStyle.MediumFont); ImGui::RenderTextClipped(title_bb.Min, title_bb.Max, draw_title.c_str(), draw_title.end_ptr(), nullptr, ImVec2(0.5f, 0.0f), &title_bb); ImGui::PopFont(); @@ -7209,13 +7214,14 @@ void FullscreenUI::DrawGameListSettingsWindow() const float bg_alpha = System::IsValid() ? 0.90f : 1.0f; - if (BeginFullscreenWindow(ImVec2(0.0f, 0.0f), heading_size, "gamelist_view", MulAlpha(UIPrimaryColor, bg_alpha))) + if (BeginFullscreenWindow(ImVec2(0.0f, 0.0f), heading_size, "gamelist_view", + MulAlpha(UIStyle.PrimaryColor, bg_alpha))) { BeginNavBar(); if (NavButton(ICON_FA_BACKWARD, true, true)) { - s_current_main_window = MainWindowType::GameList; + s_state.current_main_window = MainWindowType::GameList; QueueResetFocus(FocusResetType::Other); } @@ -7228,7 +7234,7 @@ void FullscreenUI::DrawGameListSettingsWindow() if (!BeginFullscreenWindow( ImVec2(0.0f, heading_size.y), ImVec2(io.DisplaySize.x, io.DisplaySize.y - heading_size.y - LayoutScale(LAYOUT_FOOTER_HEIGHT)), - "settings_parent", UIBackgroundColor, 0.0f, ImVec2(ImGuiFullscreen::LAYOUT_MENU_WINDOW_X_PADDING, 0.0f))) + "settings_parent", UIStyle.BackgroundColor, 0.0f, ImVec2(ImGuiFullscreen::LAYOUT_MENU_WINDOW_X_PADDING, 0.0f))) { EndFullscreenWindow(); return; @@ -7236,7 +7242,7 @@ void FullscreenUI::DrawGameListSettingsWindow() if (ImGui::IsWindowFocused() && WantsToCloseMenu()) { - s_current_main_window = MainWindowType::GameList; + s_state.current_main_window = MainWindowType::GameList; QueueResetFocus(FocusResetType::ViewChanged); } @@ -7266,7 +7272,7 @@ void FullscreenUI::DrawGameListSettingsWindow() }); } - for (const auto& it : s_game_list_directories_cache) + for (const auto& it : s_state.game_list_directories_cache) { if (MenuButton(SmallString::from_format(ICON_FA_FOLDER " {}", it.first), it.second ? FSUI_CSTR("Scanning Subdirectories") : FSUI_CSTR("Not Scanning Subdirectories"))) @@ -7385,8 +7391,9 @@ void FullscreenUI::DrawGameListSettingsWindow() void FullscreenUI::SwitchToGameList() { - s_current_main_window = MainWindowType::GameList; - s_game_list_view = static_cast(Host::GetBaseIntSettingValue("Main", "DefaultFullscreenUIGameView", 0)); + s_state.current_main_window = MainWindowType::GameList; + s_state.game_list_view = + static_cast(Host::GetBaseIntSettingValue("Main", "DefaultFullscreenUIGameView", 0)); { auto lock = Host::GetSettingsLock(); PopulateGameListDirectoryCache(Host::Internal::GetBaseSettingsLayer()); @@ -7397,11 +7404,11 @@ void FullscreenUI::SwitchToGameList() GPUTexture* FullscreenUI::GetGameListCover(const GameList::Entry* entry) { // lookup and grab cover image - auto cover_it = s_cover_image_map.find(entry->path); - if (cover_it == s_cover_image_map.end()) + auto cover_it = s_state.cover_image_map.find(entry->path); + if (cover_it == s_state.cover_image_map.end()) { std::string cover_path(GameList::GetCoverImagePathForEntry(entry)); - cover_it = s_cover_image_map.emplace(entry->path, std::move(cover_path)).first; + cover_it = s_state.cover_image_map.emplace(entry->path, std::move(cover_path)).first; } GPUTexture* tex = (!cover_it->second.empty()) ? GetCachedTextureAsync(cover_it->second.c_str()) : nullptr; @@ -7413,17 +7420,17 @@ GPUTexture* FullscreenUI::GetTextureForGameListEntryType(GameList::EntryType typ switch (type) { case GameList::EntryType::PSExe: - return s_fallback_exe_texture.get(); + return s_state.fallback_exe_texture.get(); case GameList::EntryType::Playlist: - return s_fallback_playlist_texture.get(); + return s_state.fallback_playlist_texture.get(); case GameList::EntryType::PSF: - return s_fallback_psf_texture.get(); + return s_state.fallback_psf_texture.get(); case GameList::EntryType::Disc: default: - return s_fallback_disc_texture.get(); + return s_state.fallback_disc_texture.get(); } } @@ -7433,7 +7440,7 @@ GPUTexture* FullscreenUI::GetCoverForCurrentGame() const GameList::Entry* entry = GameList::GetEntryForPath(System::GetDiscPath()); if (!entry) - return s_fallback_disc_texture.get(); + return s_state.fallback_disc_texture.get(); return GetGameListCover(entry); } @@ -7444,7 +7451,7 @@ GPUTexture* FullscreenUI::GetCoverForCurrentGame() void FullscreenUI::OpenAboutWindow() { - s_about_window_open = true; + s_state.about_window_open = true; } void FullscreenUI::ExitFullscreenAndOpenURL(std::string_view url) @@ -7471,11 +7478,11 @@ void FullscreenUI::DrawAboutWindow() ImGui::SetNextWindowPos(ImGui::GetIO().DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f)); ImGui::OpenPopup(FSUI_CSTR("About DuckStation")); - ImGui::PushFont(g_large_font); + ImGui::PushFont(UIStyle.LargeFont); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, LayoutScale(10.0f)); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, LayoutScale(30.0f, 30.0f)); - if (ImGui::BeginPopupModal(FSUI_CSTR("About DuckStation"), &s_about_window_open, + if (ImGui::BeginPopupModal(FSUI_CSTR("About DuckStation"), &s_state.about_window_open, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize)) { ImGui::TextWrapped("%s", FSUI_CSTR("DuckStation is a free simulator/emulator of the Sony PlayStation(TM) " @@ -7503,7 +7510,7 @@ void FullscreenUI::DrawAboutWindow() if (ActiveButton(FSUI_ICONSTR(ICON_FA_WINDOW_CLOSE, "Close"), false) || WantsToCloseMenu()) { ImGui::CloseCurrentPopup(); - s_about_window_open = false; + s_state.about_window_open = false; } else { @@ -7537,20 +7544,20 @@ void FullscreenUI::OpenAchievementsWindow() return; } - if (s_current_main_window != MainWindowType::PauseMenu) + if (s_state.current_main_window != MainWindowType::PauseMenu) { PauseForMenuOpen(false); ForceKeyNavEnabled(); } - s_current_main_window = MainWindowType::Achievements; + s_state.current_main_window = MainWindowType::Achievements; QueueResetFocus(FocusResetType::ViewChanged); FixStateIfPaused(); } bool FullscreenUI::IsAchievementsWindowOpen() { - return (s_current_main_window == MainWindowType::Achievements); + return (s_state.current_main_window == MainWindowType::Achievements); } void FullscreenUI::OpenLeaderboardsWindow() @@ -7571,20 +7578,20 @@ void FullscreenUI::OpenLeaderboardsWindow() return; } - if (s_current_main_window != MainWindowType::PauseMenu) + if (s_state.current_main_window != MainWindowType::PauseMenu) { PauseForMenuOpen(false); ForceKeyNavEnabled(); } - s_current_main_window = MainWindowType::Leaderboards; + s_state.current_main_window = MainWindowType::Leaderboards; QueueResetFocus(FocusResetType::ViewChanged); FixStateIfPaused(); } bool FullscreenUI::IsLeaderboardsWindowOpen() { - return (s_current_main_window == MainWindowType::Leaderboards); + return (s_state.current_main_window == MainWindowType::Leaderboards); } #endif // __ANDROID__ diff --git a/src/core/game_list.cpp b/src/core/game_list.cpp index 3f3520ed5..6b4246f54 100644 --- a/src/core/game_list.cpp +++ b/src/core/game_list.cpp @@ -776,7 +776,7 @@ void GameList::Refresh(bool invalidate_cache, bool only_cache, ProgressCallback* if (!cache_file) ERROR_LOG("Failed to open game list cache: {}", error.GetDescription()); -#ifndef _WIN32 +#ifdef HAS_POSIX_FILE_LOCK // Lock cache file for multi-instance on Linux. Implicitly done on Windows. std::optional cache_file_lock; if (cache_file) @@ -1122,7 +1122,7 @@ GameList::PlayedTimeMap GameList::LoadPlayedTimeMap(const std::string& path) return ret; } -#ifndef _WIN32 +#ifdef HAS_POSIX_FILE_LOCK FileSystem::POSIXLock flock(fp.get()); #endif @@ -1159,7 +1159,7 @@ GameList::PlayedTimeEntry GameList::UpdatePlayedTimeFile(const std::string& path return new_entry; } -#ifndef _WIN32 +#ifdef HAS_POSIX_FILE_LOCK FileSystem::POSIXLock flock(fp.get()); #endif @@ -1726,7 +1726,7 @@ void GameList::ReloadMemcardTimestampCache() if (!fp) return; -#ifndef _WIN32 +#ifdef HAS_POSIX_FILE_LOCK FileSystem::POSIXLock lock(fp.get()); #endif @@ -1856,7 +1856,7 @@ bool GameList::UpdateMemcardTimestampCache(const MemcardTimestampCacheEntry& ent if (!fp) return false; -#ifndef _WIN32 +#ifdef HAS_POSIX_FILE_LOCK FileSystem::POSIXLock lock(fp.get()); #endif diff --git a/src/core/gpu.cpp b/src/core/gpu.cpp index 6b0e03c14..606167bc2 100644 --- a/src/core/gpu.cpp +++ b/src/core/gpu.cpp @@ -1997,15 +1997,11 @@ GPUDevice::PresentResult GPU::RenderDisplay(GPUTexture* target, const GSVector4i // Now we can apply the post chain. GPUTexture* post_output_texture = PostProcessing::InternalChain.GetOutputTexture(); - if (const GPUDevice::PresentResult pres = PostProcessing::InternalChain.Apply( - display_texture, m_display_depth_buffer, post_output_texture, - GSVector4i(0, 0, display_texture_view_width, display_texture_view_height), display_texture_view_width, - display_texture_view_height, m_crtc_state.display_width, m_crtc_state.display_height); - pres != GPUDevice::PresentResult::OK) - { - return pres; - } - else + if (PostProcessing::InternalChain.Apply(display_texture, m_display_depth_buffer, post_output_texture, + GSVector4i(0, 0, display_texture_view_width, display_texture_view_height), + display_texture_view_width, display_texture_view_height, + m_crtc_state.display_width, + m_crtc_state.display_height) == GPUDevice::PresentResult::OK) { display_texture_view_x = 0; display_texture_view_y = 0; @@ -2020,8 +2016,13 @@ GPUDevice::PresentResult GPU::RenderDisplay(GPUTexture* target, const GSVector4i const bool really_postfx = (postfx && PostProcessing::DisplayChain.IsActive() && g_gpu_device->HasMainSwapChain() && hdformat != GPUTexture::Format::Unknown && target_width > 0 && target_height > 0 && PostProcessing::DisplayChain.CheckTargets(hdformat, target_width, target_height)); - const GSVector4i real_draw_rect = - g_gpu_device->UsesLowerLeftOrigin() ? GPUDevice::FlipToLowerLeft(draw_rect, target_height) : draw_rect; + GSVector4i real_draw_rect = target ? draw_rect : g_gpu_device->GetMainSwapChain()->PreRotateClipRect(draw_rect); + if (g_gpu_device->UsesLowerLeftOrigin()) + { + real_draw_rect = GPUDevice::FlipToLowerLeft( + real_draw_rect, + (target || really_postfx) ? target_height : g_gpu_device->GetMainSwapChain()->GetPostRotatedHeight()); + } if (really_postfx) { g_gpu_device->ClearRenderTarget(PostProcessing::DisplayChain.GetInputTexture(), GPUDevice::DEFAULT_CLEAR_COLOR); @@ -2106,16 +2107,22 @@ GPUDevice::PresentResult GPU::RenderDisplay(GPUTexture* target, const GSVector4i uniforms.src_size[2] = rcp_width; uniforms.src_size[3] = rcp_height; - if (g_settings.display_rotation != DisplayRotation::Normal) + const WindowInfo::PreRotation surface_prerotation = (target || really_postfx) ? + WindowInfo::PreRotation::Identity : + g_gpu_device->GetMainSwapChain()->GetPreRotation(); + if (g_settings.display_rotation != DisplayRotation::Normal || + surface_prerotation != WindowInfo::PreRotation::Identity) { - static constexpr const std::array(DisplayRotation::Count) - 1> rotation_radians = {{ + static constexpr const std::array(DisplayRotation::Count)> rotation_radians = {{ + 0.0f, // Disabled static_cast(std::numbers::pi * 1.5f), // Rotate90 static_cast(std::numbers::pi), // Rotate180 static_cast(std::numbers::pi / 2.0), // Rotate270 }}; - GSMatrix2x2::Rotation(rotation_radians[static_cast(g_settings.display_rotation) - 1]) - .store(uniforms.rotation_matrix); + const u32 rotation_idx = (static_cast(g_settings.display_rotation) + static_cast(surface_prerotation)) % + static_cast(rotation_radians.size()); + GSMatrix2x2::Rotation(rotation_radians[rotation_idx]).store(uniforms.rotation_matrix); } else { @@ -2340,8 +2347,6 @@ bool GPU::DeinterlaceExtractField(u32 dst_bufidx, GPUTexture* src, u32 x, u32 y, g_gpu_device->PushUniformBuffer(uniforms, sizeof(uniforms)); g_gpu_device->SetViewportAndScissor(0, 0, width, height); g_gpu_device->Draw(3, 0); - - GL_POP(); } dst->MakeReadyForSampling(); diff --git a/src/core/gpu_hw.cpp b/src/core/gpu_hw.cpp index 7b561383e..14fd6e2a8 100644 --- a/src/core/gpu_hw.cpp +++ b/src/core/gpu_hw.cpp @@ -4080,6 +4080,7 @@ void GPU_HW::DownsampleFramebufferAdaptive(GPUTexture* source, u32 left, u32 top if (!m_downsample_texture || !level_texture || !weight_texture) { ERROR_LOG("Failed to create {}x{} RTs for adaptive downsampling", width, height); + GL_POP(); return; } diff --git a/src/core/gpu_hw_texture_cache.cpp b/src/core/gpu_hw_texture_cache.cpp index 44de7df8e..a9b76131a 100644 --- a/src/core/gpu_hw_texture_cache.cpp +++ b/src/core/gpu_hw_texture_cache.cpp @@ -781,6 +781,8 @@ void GPUTextureCache::SetHashCacheTextureFormat() // Prefer 16-bit texture formats where possible. if (g_gpu_device->SupportsTextureFormat(GPUTexture::Format::RGB5A1)) s_state.hash_cache_texture_format = GPUTexture::Format::RGB5A1; + else if (g_gpu_device->SupportsTextureFormat(GPUTexture::Format::A1BGR5)) + s_state.hash_cache_texture_format = GPUTexture::Format::A1BGR5; else s_state.hash_cache_texture_format = GPUTexture::Format::RGBA8; @@ -1080,70 +1082,6 @@ ALWAYS_INLINE_RELEASE static const u16* VRAMPalettePointer(GPUTexturePaletteReg return &g_vram[VRAM_WIDTH * palette.GetYBase() + palette.GetXBase()]; } -template -ALWAYS_INLINE static void WriteDecodedTexel(u8*& dest, u16 c16) -{ - if constexpr (format == GPUTexture::Format::RGBA8) - { - const u32 c32 = VRAMRGBA5551ToRGBA8888(c16); - std::memcpy(std::assume_aligned(dest), &c32, sizeof(c32)); - dest += sizeof(c32); - } - else if constexpr (format == GPUTexture::Format::RGB5A1) - { - const u16 repacked = (c16 & 0x83E0) | ((c16 >> 10) & 0x1F) | ((c16 & 0x1F) << 10); - std::memcpy(std::assume_aligned(dest), &repacked, sizeof(repacked)); - dest += sizeof(repacked); - } -} - -#ifdef CPU_ARCH_SIMD - -ALWAYS_INLINE static GSVector4i VRAM5BitTo8Bit(GSVector4i val) -{ - return val.mul32l(GSVector4i::cxpr(527)).add32(GSVector4i::cxpr(23)).srl32<6>(); -} - -ALWAYS_INLINE static GSVector4i VRAMRGB5A1ToRGBA8888(GSVector4i val) -{ - static constexpr GSVector4i cmask = GSVector4i::cxpr(0x1F); - - const GSVector4i r = VRAM5BitTo8Bit(val & cmask); - const GSVector4i g = VRAM5BitTo8Bit((val.srl32<5>() & cmask)); - const GSVector4i b = VRAM5BitTo8Bit((val.srl32<10>() & cmask)); - const GSVector4i a = val.srl32<15>().sll32<31>().sra32<7>(); - - return r | g.sll32<8>() | b.sll32<16>() | b.sll32<24>() | a; -} - -template -ALWAYS_INLINE static void WriteDecodedTexels(u8*& dest, GSVector4i c16) -{ - if constexpr (format == GPUTexture::Format::RGBA8) - { - const GSVector4i low = VRAMRGB5A1ToRGBA8888(c16.upl16()); - const GSVector4i high = VRAMRGB5A1ToRGBA8888(c16.uph16()); - - GSVector4i::store(dest, low); - dest += sizeof(GSVector4i); - - GSVector4i::store(dest, high); - dest += sizeof(GSVector4i); - } - else if constexpr (format == GPUTexture::Format::RGB5A1) - { - static constexpr GSVector4i cmask = GSVector4i::cxpr16(0x1F); - - const GSVector4i repacked = - (c16 & GSVector4i::cxpr16(static_cast(0x83E0))) | (c16.srl16<10>() & cmask) | (c16 & cmask).sll16<10>(); - - GSVector4i::store(dest, repacked); - dest += sizeof(GSVector4i); - } -} - -#endif - template void GPUTextureCache::DecodeTexture4(const u16* page, const u16* palette, u32 width, u32 height, u8* dest, u32 dest_stride) @@ -1175,17 +1113,17 @@ void GPUTextureCache::DecodeTexture4(const u16* page, const u16* palette, u32 wi c16[5] = palette[(pp >> 4) & 0x0F]; c16[6] = palette[(pp >> 8) & 0x0F]; c16[7] = palette[pp >> 12]; - WriteDecodedTexels(dest_ptr, GSVector4i::load(c16)); + ConvertVRAMPixels(dest_ptr, GSVector4i::load(c16)); } #endif for (; x < vram_width; x++) { const u32 pp = *(page_ptr++); - WriteDecodedTexel(dest_ptr, palette[pp & 0x0F]); - WriteDecodedTexel(dest_ptr, palette[(pp >> 4) & 0x0F]); - WriteDecodedTexel(dest_ptr, palette[(pp >> 8) & 0x0F]); - WriteDecodedTexel(dest_ptr, palette[pp >> 12]); + ConvertVRAMPixel(dest_ptr, palette[pp & 0x0F]); + ConvertVRAMPixel(dest_ptr, palette[(pp >> 4) & 0x0F]); + ConvertVRAMPixel(dest_ptr, palette[(pp >> 8) & 0x0F]); + ConvertVRAMPixel(dest_ptr, palette[pp >> 12]); } page += VRAM_WIDTH; @@ -1206,7 +1144,7 @@ void GPUTextureCache::DecodeTexture4(const u16* page, const u16* palette, u32 wi if (offs == 0) texel = *(page_ptr++); - WriteDecodedTexel(dest_ptr, palette[texel & 0x0F]); + ConvertVRAMPixel(dest_ptr, palette[texel & 0x0F]); texel >>= 4; offs = (offs + 1) % 4; @@ -1251,15 +1189,15 @@ void GPUTextureCache::DecodeTexture8(const u16* page, const u16* palette, u32 wi pp = *(page_ptr++); c16[6] = palette[pp & 0xFF]; c16[7] = palette[(pp >> 8) & 0xFF]; - WriteDecodedTexels(dest_ptr, GSVector4i::load(c16)); + ConvertVRAMPixels(dest_ptr, GSVector4i::load(c16)); } #endif for (; x < vram_width; x++) { const u32 pp = *(page_ptr++); - WriteDecodedTexel(dest_ptr, palette[pp & 0xFF]); - WriteDecodedTexel(dest_ptr, palette[pp >> 8]); + ConvertVRAMPixel(dest_ptr, palette[pp & 0xFF]); + ConvertVRAMPixel(dest_ptr, palette[pp >> 8]); } page += VRAM_WIDTH; @@ -1280,7 +1218,7 @@ void GPUTextureCache::DecodeTexture8(const u16* page, const u16* palette, u32 wi if (offs == 0) texel = *(page_ptr++); - WriteDecodedTexel(dest_ptr, palette[texel & 0xFF]); + ConvertVRAMPixel(dest_ptr, palette[texel & 0xFF]); texel >>= 8; offs ^= 1; @@ -1307,13 +1245,13 @@ void GPUTextureCache::DecodeTexture16(const u16* page, u32 width, u32 height, u8 #ifdef CPU_ARCH_SIMD for (; x < aligned_width; x += pixels_per_vec) { - WriteDecodedTexels(dest_ptr, GSVector4i::load(page_ptr)); + ConvertVRAMPixels(dest_ptr, GSVector4i::load(page_ptr)); page_ptr += pixels_per_vec; } #endif for (; x < width; x++) - WriteDecodedTexel(dest_ptr, *(page_ptr++)); + ConvertVRAMPixel(dest_ptr, *(page_ptr++)); page += VRAM_WIDTH; dest += dest_stride; @@ -1359,6 +1297,24 @@ void GPUTextureCache::DecodeTexture(GPUTextureMode mode, const u16* page_ptr, co DefaultCaseIsUnreachable() } } + else if (dest_format == GPUTexture::Format::A1BGR5) + { + switch (mode) + { + case GPUTextureMode::Palette4Bit: + DecodeTexture4(page_ptr, palette, width, height, dest, dest_stride); + break; + case GPUTextureMode::Palette8Bit: + DecodeTexture8(page_ptr, palette, width, height, dest, dest_stride); + break; + case GPUTextureMode::Direct16Bit: + case GPUTextureMode::Reserved_Direct16Bit: + DecodeTexture16(page_ptr, width, height, dest, dest_stride); + break; + + DefaultCaseIsUnreachable() + } + } else { Panic("Unsupported texture format."); diff --git a/src/core/gpu_sw.cpp b/src/core/gpu_sw.cpp index 9159dbddc..3020246d6 100644 --- a/src/core/gpu_sw.cpp +++ b/src/core/gpu_sw.cpp @@ -41,10 +41,8 @@ bool GPU_SW::Initialize(Error* error) if (!GPU::Initialize(error) || !m_backend.Initialize(g_settings.gpu_use_thread)) return false; - static constexpr const std::array formats_for_16bit = {GPUTexture::Format::RGB565, GPUTexture::Format::RGB5A1, - GPUTexture::Format::RGBA8, GPUTexture::Format::BGRA8}; - static constexpr const std::array formats_for_24bit = {GPUTexture::Format::RGBA8, GPUTexture::Format::BGRA8, - GPUTexture::Format::RGB565, GPUTexture::Format::RGB5A1}; + static constexpr const std::array formats_for_16bit = {GPUTexture::Format::RGB5A1, GPUTexture::Format::A1BGR5, + GPUTexture::Format::RGB565, GPUTexture::Format::RGBA8}; for (const GPUTexture::Format format : formats_for_16bit) { if (g_gpu_device->SupportsTextureFormat(format)) @@ -53,15 +51,10 @@ bool GPU_SW::Initialize(Error* error) break; } } - for (const GPUTexture::Format format : formats_for_24bit) - { - if (g_gpu_device->SupportsTextureFormat(format)) - { - m_24bit_display_format = format; - break; - } - } + // RGBA8 will always be supported, hence we'll find one. + INFO_LOG("Using {} format for 16-bit display", GPUTexture::GetFormatName(m_16bit_display_format)); + Assert(m_16bit_display_format != GPUTexture::Format::Unknown); return true; } @@ -108,129 +101,43 @@ GPUTexture* GPU_SW::GetDisplayTexture(u32 width, u32 height, GPUTexture::Format return m_upload_texture.get(); } -template -static void CopyOutRow16(const u16* src_ptr, out_type* dst_ptr, u32 width); - -template -static out_type VRAM16ToOutput(u16 value); - -template<> -ALWAYS_INLINE u16 VRAM16ToOutput(u16 value) -{ - return (value & 0x3E0) | ((value >> 10) & 0x1F) | ((value & 0x1F) << 10); -} - -template<> -ALWAYS_INLINE u16 VRAM16ToOutput(u16 value) -{ - return ((value & 0x3E0) << 1) | ((value & 0x20) << 1) | ((value >> 10) & 0x1F) | ((value & 0x1F) << 11); -} - -template<> -ALWAYS_INLINE u32 VRAM16ToOutput(u16 value) -{ - const u32 value32 = ZeroExtend32(value); - const u32 r = (value32 & 31u) << 3; - const u32 g = ((value32 >> 5) & 31u) << 3; - const u32 b = ((value32 >> 10) & 31u) << 3; - const u32 a = ((value >> 15) != 0) ? 255 : 0; - return ZeroExtend32(r) | (ZeroExtend32(g) << 8) | (ZeroExtend32(b) << 16) | (ZeroExtend32(a) << 24); -} - -template<> -ALWAYS_INLINE u32 VRAM16ToOutput(u16 value) -{ - const u32 value32 = ZeroExtend32(value); - const u32 r = (value32 & 31u) << 3; - const u32 g = ((value32 >> 5) & 31u) << 3; - const u32 b = ((value32 >> 10) & 31u) << 3; - return ZeroExtend32(b) | (ZeroExtend32(g) << 8) | (ZeroExtend32(r) << 16) | (0xFF000000u); -} - -template<> -ALWAYS_INLINE void CopyOutRow16(const u16* src_ptr, u16* dst_ptr, u32 width) -{ - u32 col = 0; - - const u32 aligned_width = Common::AlignDownPow2(width, 8); - for (; col < aligned_width; col += 8) - { - constexpr GSVector4i single_mask = GSVector4i::cxpr16(0x1F); - GSVector4i value = GSVector4i::load(src_ptr); - src_ptr += 8; - GSVector4i a = value & GSVector4i::cxpr16(0x3E0); - GSVector4i b = value.srl16<10>() & single_mask; - GSVector4i c = (value & single_mask).sll16<10>(); - value = (a | b) | c; - GSVector4i::store(dst_ptr, value); - dst_ptr += 8; - } - - for (; col < width; col++) - *(dst_ptr++) = VRAM16ToOutput(*(src_ptr++)); -} - -template<> -ALWAYS_INLINE void CopyOutRow16(const u16* src_ptr, u16* dst_ptr, u32 width) -{ - u32 col = 0; - - const u32 aligned_width = Common::AlignDownPow2(width, 8); - for (; col < aligned_width; col += 8) - { - constexpr GSVector4i single_mask = GSVector4i::cxpr16(0x1F); - GSVector4i value = GSVector4i::load(src_ptr); - src_ptr += 8; - GSVector4i a = (value & GSVector4i::cxpr16(0x3E0)).sll16<1>(); // (value & 0x3E0) << 1 - GSVector4i b = (value & GSVector4i::cxpr16(0x20)).sll16<1>(); // (value & 0x20) << 1 - GSVector4i c = (value.srl16<10>() & single_mask); // ((value >> 10) & 0x1F) - GSVector4i d = (value & single_mask).sll16<11>(); // ((value & 0x1F) << 11) - value = (((a | b) | c) | d); - GSVector4i::store(dst_ptr, value); - dst_ptr += 8; - } - - for (; col < width; col++) - *(dst_ptr++) = VRAM16ToOutput(*(src_ptr++)); -} - -template<> -ALWAYS_INLINE void CopyOutRow16(const u16* src_ptr, u32* dst_ptr, u32 width) -{ - for (u32 col = 0; col < width; col++) - *(dst_ptr++) = VRAM16ToOutput(*(src_ptr++)); -} - -template<> -ALWAYS_INLINE void CopyOutRow16(const u16* src_ptr, u32* dst_ptr, u32 width) -{ - for (u32 col = 0; col < width; col++) - *(dst_ptr++) = VRAM16ToOutput(*(src_ptr++)); -} - template ALWAYS_INLINE_RELEASE bool GPU_SW::CopyOut15Bit(u32 src_x, u32 src_y, u32 width, u32 height, u32 line_skip) { - using OutputPixelType = - std::conditional_t; - GPUTexture* texture = GetDisplayTexture(width, height, display_format); if (!texture) [[unlikely]] return false; - u32 dst_stride = width * sizeof(OutputPixelType); + u32 dst_stride = Common::AlignUpPow2(width * texture->GetPixelSize(), 4); u8* dst_ptr = m_upload_buffer.data(); const bool mapped = texture->Map(reinterpret_cast(&dst_ptr), &dst_stride, 0, 0, width, height); // Fast path when not wrapping around. if ((src_x + width) <= VRAM_WIDTH && (src_y + height) <= VRAM_HEIGHT) { + [[maybe_unused]] constexpr u32 pixels_per_vec = 8; + [[maybe_unused]] const u32 aligned_width = Common::AlignDownPow2(width, pixels_per_vec); + const u16* src_ptr = &g_vram[src_y * VRAM_WIDTH + src_x]; const u32 src_step = VRAM_WIDTH << line_skip; + for (u32 row = 0; row < height; row++) { - CopyOutRow16(src_ptr, reinterpret_cast(dst_ptr), width); + const u16* src_row_ptr = src_ptr; + u8* dst_row_ptr = dst_ptr; + u32 x = 0; + +#ifdef CPU_ARCH_SIMD + for (; x < aligned_width; x += pixels_per_vec) + { + ConvertVRAMPixels(dst_row_ptr, GSVector4i::load(src_row_ptr)); + src_row_ptr += pixels_per_vec; + } +#endif + + for (; x < width; x++) + ConvertVRAMPixel(dst_row_ptr, *(src_row_ptr++)); + src_ptr += src_step; dst_ptr += dst_stride; } @@ -242,10 +149,10 @@ ALWAYS_INLINE_RELEASE bool GPU_SW::CopyOut15Bit(u32 src_x, u32 src_y, u32 width, for (u32 row = 0; row < height; row++) { const u16* src_row_ptr = &g_vram[(src_y % VRAM_HEIGHT) * VRAM_WIDTH]; - OutputPixelType* dst_row_ptr = reinterpret_cast(dst_ptr); + u8* dst_row_ptr = dst_ptr; for (u32 col = src_x; col < end_x; col++) - *(dst_row_ptr++) = VRAM16ToOutput(src_row_ptr[col % VRAM_WIDTH]); + ConvertVRAMPixel(dst_row_ptr, src_row_ptr[col % VRAM_WIDTH]); src_y += y_step; dst_ptr += dst_stride; @@ -260,18 +167,13 @@ ALWAYS_INLINE_RELEASE bool GPU_SW::CopyOut15Bit(u32 src_x, u32 src_y, u32 width, return true; } -template ALWAYS_INLINE_RELEASE bool GPU_SW::CopyOut24Bit(u32 src_x, u32 src_y, u32 skip_x, u32 width, u32 height, u32 line_skip) { - using OutputPixelType = - std::conditional_t; - - GPUTexture* texture = GetDisplayTexture(width, height, display_format); + GPUTexture* texture = GetDisplayTexture(width, height, FORMAT_FOR_24BIT); if (!texture) [[unlikely]] return false; - u32 dst_stride = Common::AlignUpPow2(width * sizeof(OutputPixelType), 4); + u32 dst_stride = width * sizeof(u32); u8* dst_ptr = m_upload_buffer.data(); const bool mapped = texture->Map(reinterpret_cast(&dst_ptr), &dst_stride, 0, 0, width, height); @@ -281,52 +183,14 @@ ALWAYS_INLINE_RELEASE bool GPU_SW::CopyOut24Bit(u32 src_x, u32 src_y, u32 skip_x const u32 src_stride = (VRAM_WIDTH << line_skip) * sizeof(u16); for (u32 row = 0; row < height; row++) { - if constexpr (display_format == GPUTexture::Format::RGBA8) + const u8* src_row_ptr = src_ptr; + u8* dst_row_ptr = reinterpret_cast(dst_ptr); + for (u32 col = 0; col < width; col++) { - const u8* src_row_ptr = src_ptr; - u8* dst_row_ptr = reinterpret_cast(dst_ptr); - for (u32 col = 0; col < width; col++) - { - *(dst_row_ptr++) = *(src_row_ptr++); - *(dst_row_ptr++) = *(src_row_ptr++); - *(dst_row_ptr++) = *(src_row_ptr++); - *(dst_row_ptr++) = 0xFF; - } - } - else if constexpr (display_format == GPUTexture::Format::BGRA8) - { - const u8* src_row_ptr = src_ptr; - u8* dst_row_ptr = reinterpret_cast(dst_ptr); - for (u32 col = 0; col < width; col++) - { - *(dst_row_ptr++) = src_row_ptr[2]; - *(dst_row_ptr++) = src_row_ptr[1]; - *(dst_row_ptr++) = src_row_ptr[0]; - *(dst_row_ptr++) = 0xFF; - src_row_ptr += 3; - } - } - else if constexpr (display_format == GPUTexture::Format::RGB565) - { - const u8* src_row_ptr = src_ptr; - u16* dst_row_ptr = reinterpret_cast(dst_ptr); - for (u32 col = 0; col < width; col++) - { - *(dst_row_ptr++) = ((static_cast(src_row_ptr[0]) >> 3) << 11) | - ((static_cast(src_row_ptr[1]) >> 2) << 5) | (static_cast(src_row_ptr[2]) >> 3); - src_row_ptr += 3; - } - } - else if constexpr (display_format == GPUTexture::Format::RGB5A1) - { - const u8* src_row_ptr = src_ptr; - u16* dst_row_ptr = reinterpret_cast(dst_ptr); - for (u32 col = 0; col < width; col++) - { - *(dst_row_ptr++) = ((static_cast(src_row_ptr[0]) >> 3) << 10) | - ((static_cast(src_row_ptr[1]) >> 3) << 5) | (static_cast(src_row_ptr[2]) >> 3); - src_row_ptr += 3; - } + *(dst_row_ptr++) = *(src_row_ptr++); + *(dst_row_ptr++) = *(src_row_ptr++); + *(dst_row_ptr++) = *(src_row_ptr++); + *(dst_row_ptr++) = 0xFF; } src_ptr += src_stride; @@ -340,7 +204,7 @@ ALWAYS_INLINE_RELEASE bool GPU_SW::CopyOut24Bit(u32 src_x, u32 src_y, u32 skip_x for (u32 row = 0; row < height; row++) { const u16* src_row_ptr = &g_vram[(src_y % VRAM_HEIGHT) * VRAM_WIDTH]; - OutputPixelType* dst_row_ptr = reinterpret_cast(dst_ptr); + u32* dst_row_ptr = reinterpret_cast(dst_ptr); for (u32 col = 0; col < width; col++) { @@ -350,22 +214,7 @@ ALWAYS_INLINE_RELEASE bool GPU_SW::CopyOut24Bit(u32 src_x, u32 src_y, u32 skip_x const u8 shift = static_cast(col & 1u) * 8; const u32 rgb = (((ZeroExtend32(s1) << 16) | ZeroExtend32(s0)) >> shift); - if constexpr (display_format == GPUTexture::Format::RGBA8) - { - *(dst_row_ptr++) = rgb | 0xFF000000u; - } - else if constexpr (display_format == GPUTexture::Format::BGRA8) - { - *(dst_row_ptr++) = (rgb & 0x00FF00) | ((rgb & 0xFF) << 16) | ((rgb >> 16) & 0xFF) | 0xFF000000u; - } - else if constexpr (display_format == GPUTexture::Format::RGB565) - { - *(dst_row_ptr++) = ((rgb >> 3) & 0x1F) | (((rgb >> 10) << 5) & 0x7E0) | (((rgb >> 19) << 11) & 0x3E0000); - } - else if constexpr (display_format == GPUTexture::Format::RGB5A1) - { - *(dst_row_ptr++) = ((rgb >> 3) & 0x1F) | (((rgb >> 11) << 5) & 0x3E0) | (((rgb >> 19) << 10) & 0x1F0000); - } + *(dst_row_ptr++) = rgb | 0xFF000000u; } src_y += y_step; @@ -392,6 +241,9 @@ bool GPU_SW::CopyOut(u32 src_x, u32 src_y, u32 skip_x, u32 width, u32 height, u3 case GPUTexture::Format::RGB5A1: return CopyOut15Bit(src_x, src_y, width, height, line_skip); + case GPUTexture::Format::A1BGR5: + return CopyOut15Bit(src_x, src_y, width, height, line_skip); + case GPUTexture::Format::RGB565: return CopyOut15Bit(src_x, src_y, width, height, line_skip); @@ -407,23 +259,7 @@ bool GPU_SW::CopyOut(u32 src_x, u32 src_y, u32 skip_x, u32 width, u32 height, u3 } else { - switch (m_24bit_display_format) - { - case GPUTexture::Format::RGB5A1: - return CopyOut24Bit(src_x, src_y, skip_x, width, height, line_skip); - - case GPUTexture::Format::RGB565: - return CopyOut24Bit(src_x, src_y, skip_x, width, height, line_skip); - - case GPUTexture::Format::RGBA8: - return CopyOut24Bit(src_x, src_y, skip_x, width, height, line_skip); - - case GPUTexture::Format::BGRA8: - return CopyOut24Bit(src_x, src_y, skip_x, width, height, line_skip); - - default: - UnreachableCode(); - } + return CopyOut24Bit(src_x, src_y, skip_x, width, height, line_skip); } } diff --git a/src/core/gpu_sw.h b/src/core/gpu_sw.h index 2251843aa..b99bdd168 100644 --- a/src/core/gpu_sw.h +++ b/src/core/gpu_sw.h @@ -45,7 +45,6 @@ protected: template bool CopyOut15Bit(u32 src_x, u32 src_y, u32 width, u32 height, u32 line_skip); - template bool CopyOut24Bit(u32 src_x, u32 src_y, u32 skip_x, u32 width, u32 height, u32 line_skip); bool CopyOut(u32 src_x, u32 src_y, u32 skip_x, u32 width, u32 height, u32 line_skip, bool is_24bit); @@ -57,11 +56,13 @@ protected: void FillBackendCommandParameters(GPUBackendCommand* cmd) const; void FillDrawCommand(GPUBackendDrawCommand* cmd, GPURenderCommand rc) const; +private: + static constexpr GPUTexture::Format FORMAT_FOR_24BIT = GPUTexture::Format::RGBA8; // RGBA8 always supported. + GPUTexture* GetDisplayTexture(u32 width, u32 height, GPUTexture::Format format); FixedHeapArray m_upload_buffer; - GPUTexture::Format m_16bit_display_format = GPUTexture::Format::RGB565; - GPUTexture::Format m_24bit_display_format = GPUTexture::Format::RGBA8; + GPUTexture::Format m_16bit_display_format = GPUTexture::Format::Unknown; std::unique_ptr m_upload_texture; GPU_SW_Backend m_backend; diff --git a/src/core/gpu_sw_rasterizer.inl b/src/core/gpu_sw_rasterizer.inl index 803dbfdef..d470a93a6 100644 --- a/src/core/gpu_sw_rasterizer.inl +++ b/src/core/gpu_sw_rasterizer.inl @@ -3,8 +3,10 @@ #ifdef __INTELLISENSE__ -#include "common/gsvector.h" #include "gpu.h" + +#include "common/gsvector.h" + #include #define USE_VECTOR 1 diff --git a/src/core/gpu_types.h b/src/core/gpu_types.h index 137264ec5..4782272b0 100644 --- a/src/core/gpu_types.h +++ b/src/core/gpu_types.h @@ -5,6 +5,8 @@ #include "types.h" +#include "util/gpu_texture.h" + #include "common/bitfield.h" #include "common/bitutils.h" #include "common/gsvector.h" @@ -249,6 +251,101 @@ ALWAYS_INLINE static constexpr u16 VRAMRGBA8888ToRGBA5551(u32 color) return Truncate16(r | (g << 5) | (b << 10) | (a << 15)); } +#ifdef CPU_ARCH_SIMD + +ALWAYS_INLINE static GSVector4i VRAM5BitTo8Bit(GSVector4i val) +{ + return val.mul32l(GSVector4i::cxpr(527)).add32(GSVector4i::cxpr(23)).srl32<6>(); +} + +ALWAYS_INLINE static GSVector4i VRAMRGB5A1ToRGBA8888(GSVector4i val) +{ + static constexpr GSVector4i cmask = GSVector4i::cxpr(0x1F); + + const GSVector4i r = VRAM5BitTo8Bit(val & cmask); + const GSVector4i g = VRAM5BitTo8Bit((val.srl32<5>() & cmask)); + const GSVector4i b = VRAM5BitTo8Bit((val.srl32<10>() & cmask)); + const GSVector4i a = val.srl32<15>().sll32<31>().sra32<7>(); + + return r | g.sll32<8>() | b.sll32<16>() | a; +} + +template +ALWAYS_INLINE static void ConvertVRAMPixels(u8*& dest, GSVector4i c16) +{ + if constexpr (format == GPUTexture::Format::RGBA8) + { + const GSVector4i low = VRAMRGB5A1ToRGBA8888(c16.upl16()); + const GSVector4i high = VRAMRGB5A1ToRGBA8888(c16.uph16()); + + GSVector4i::store(dest, low); + dest += sizeof(GSVector4i); + + GSVector4i::store(dest, high); + dest += sizeof(GSVector4i); + } + else if constexpr (format == GPUTexture::Format::RGB5A1) + { + static constexpr GSVector4i cmask = GSVector4i::cxpr16(0x1F); + + const GSVector4i repacked = + (c16 & GSVector4i::cxpr16(static_cast(0x83E0))) | (c16.srl16<10>() & cmask) | (c16 & cmask).sll16<10>(); + + GSVector4i::store(dest, repacked); + dest += sizeof(GSVector4i); + } + else if constexpr (format == GPUTexture::Format::A1BGR5) + { + const GSVector4i repacked = (c16 & GSVector4i::cxpr16(static_cast(0x3E0))).sll16<1>() | + (c16.srl16<9>() & GSVector4i::cxpr16(0x3E)) | + (c16 & GSVector4i::cxpr16(0x1F)).sll16<11>() | c16.srl16<15>(); + + GSVector4i::store(dest, repacked); + dest += sizeof(GSVector4i); + } + else if constexpr (format == GPUTexture::Format::RGB565) + { + constexpr GSVector4i single_mask = GSVector4i::cxpr16(0x1F); + const GSVector4i a = (c16 & GSVector4i::cxpr16(0x3E0)).sll16<1>(); // (value & 0x3E0) << 1 + const GSVector4i b = (c16 & GSVector4i::cxpr16(0x20)).sll16<1>(); // (value & 0x20) << 1 + const GSVector4i c = (c16.srl16<10>() & single_mask); // ((value >> 10) & 0x1F) + const GSVector4i d = (c16 & single_mask).sll16<11>(); // ((value & 0x1F) << 11) + GSVector4i::store(dest, (((a | b) | c) | d)); + dest += sizeof(GSVector4i); + } +} + +#endif + +template +ALWAYS_INLINE static void ConvertVRAMPixel(u8*& dest, u16 c16) +{ + if constexpr (format == GPUTexture::Format::RGBA8) + { + const u32 c32 = VRAMRGBA5551ToRGBA8888(c16); + std::memcpy(std::assume_aligned(dest), &c32, sizeof(c32)); + dest += sizeof(c32); + } + else if constexpr (format == GPUTexture::Format::RGB5A1) + { + const u16 repacked = (c16 & 0x83E0) | ((c16 >> 10) & 0x1F) | ((c16 & 0x1F) << 10); + std::memcpy(std::assume_aligned(dest), &repacked, sizeof(repacked)); + dest += sizeof(repacked); + } + else if constexpr (format == GPUTexture::Format::A1BGR5) + { + const u16 repacked = ((c16 & 0x3E0) << 1) | ((c16 >> 9) & 0x3E) | ((c16 & 0x1F) << 11) | (c16 >> 15); + std::memcpy(std::assume_aligned(dest), &repacked, sizeof(repacked)); + dest += sizeof(repacked); + } + else if constexpr (format == GPUTexture::Format::RGB565) + { + const u16 repacked = ((c16 & 0x3E0) << 1) | ((c16 & 0x20) << 1) | ((c16 >> 10) & 0x1F) | ((c16 & 0x1F) << 11); + std::memcpy(std::assume_aligned(dest), &repacked, sizeof(repacked)); + dest += sizeof(repacked); + } +} + union GPUVertexPosition { u32 bits; diff --git a/src/core/host.cpp b/src/core/host.cpp index 22a23be10..6083c6ee1 100644 --- a/src/core/host.cpp +++ b/src/core/host.cpp @@ -374,6 +374,10 @@ bool Host::CreateGPUDevice(RenderAPI api, bool fullscreen, Error* error) disabled_features |= GPUDevice::FEATURE_MASK_MEMORY_IMPORT; if (g_settings.gpu_disable_raster_order_views) disabled_features |= GPUDevice::FEATURE_MASK_RASTER_ORDER_VIEWS; + if (g_settings.gpu_disable_compute_shaders) + disabled_features |= GPUDevice::FEATURE_MASK_COMPUTE_SHADERS; + if (g_settings.gpu_disable_compressed_textures) + disabled_features |= GPUDevice::FEATURE_MASK_COMPRESSED_TEXTURES; // Don't dump shaders on debug builds for Android, users will complain about storage... #if !defined(__ANDROID__) || defined(_DEBUG) diff --git a/src/core/hotkeys.cpp b/src/core/hotkeys.cpp index 15b3bbaef..2d1857d20 100644 --- a/src/core/hotkeys.cpp +++ b/src/core/hotkeys.cpp @@ -111,7 +111,7 @@ static void HotkeySaveStateSlot(bool global, s32 slot) std::string path(global ? System::GetGlobalSaveStateFileName(slot) : System::GetGameSaveStateFileName(System::GetGameSerial(), slot)); Error error; - if (!System::SaveState(path.c_str(), &error, g_settings.create_save_state_backups)) + if (!System::SaveState(std::move(path), &error, g_settings.create_save_state_backups, false)) { Host::AddIconOSDMessage( "SaveState", ICON_FA_EXCLAMATION_TRIANGLE, diff --git a/src/core/imgui_overlays.cpp b/src/core/imgui_overlays.cpp index afd09ec3b..33f5479ac 100644 --- a/src/core/imgui_overlays.cpp +++ b/src/core/imgui_overlays.cpp @@ -389,7 +389,7 @@ void ImGuiManager::DrawPerformanceOverlay(float& position_y, float scale, float if (g_settings.display_show_resolution) { const u32 resolution_scale = g_gpu->GetResolutionScale(); - const auto [display_width, display_height] = g_gpu->GetFullDisplayResolution();// wrong + const auto [display_width, display_height] = g_gpu->GetFullDisplayResolution(); // wrong const bool interlaced = g_gpu->IsInterlacedDisplayEnabled(); const bool pal = g_gpu->IsInPALMode(); text.format("{}x{} {} {} [{}x]", display_width * resolution_scale, display_height * resolution_scale, @@ -827,66 +827,76 @@ static std::string GetCurrentSlotPath(); static constexpr const char* DATE_TIME_FORMAT = TRANSLATE_NOOP("SaveStateSelectorUI", "Saved at {0:%H:%M} on {0:%a} {0:%Y/%m/%d}."); -static std::shared_ptr s_placeholder_texture; +namespace { -static std::string s_load_legend; -static std::string s_save_legend; -static std::string s_prev_legend; -static std::string s_next_legend; +struct ALIGN_TO_CACHE_LINE State +{ + std::shared_ptr placeholder_texture; -static llvm::SmallVector s_slots; -static s32 s_current_slot = 0; -static bool s_current_slot_global = false; + std::string load_legend; + std::string save_legend; + std::string prev_legend; + std::string next_legend; -static float s_open_time = 0.0f; -static float s_close_time = 0.0f; + llvm::SmallVector slots; + s32 current_slot = 0; + bool current_slot_global = false; -static ImAnimatedFloat s_scroll_animated; -static ImAnimatedFloat s_background_animated; + float open_time = 0.0f; + float close_time = 0.0f; + + ImAnimatedFloat scroll_animated; + ImAnimatedFloat background_animated; + + bool is_open = false; +}; + +} // namespace + +static State s_state; -static bool s_open = false; } // namespace SaveStateSelectorUI bool SaveStateSelectorUI::IsOpen() { - return s_open; + return s_state.is_open; } void SaveStateSelectorUI::Open(float open_time /* = DEFAULT_OPEN_TIME */) { const std::string& serial = System::GetGameSerial(); - s_open_time = 0.0f; - s_close_time = open_time; + s_state.open_time = 0.0f; + s_state.close_time = open_time; - if (s_open) + if (s_state.is_open) return; - if (!s_placeholder_texture) - s_placeholder_texture = ImGuiFullscreen::LoadTexture("no-save.png"); + if (!s_state.placeholder_texture) + s_state.placeholder_texture = ImGuiFullscreen::LoadTexture("no-save.png"); - s_open = true; + s_state.is_open = true; RefreshList(serial); RefreshHotkeyLegend(); } void SaveStateSelectorUI::Close() { - s_open = false; - s_load_legend = {}; - s_save_legend = {}; - s_prev_legend = {}; - s_next_legend = {}; + s_state.is_open = false; + s_state.load_legend = {}; + s_state.save_legend = {}; + s_state.prev_legend = {}; + s_state.next_legend = {}; } void SaveStateSelectorUI::RefreshList(const std::string& serial) { - for (ListEntry& entry : s_slots) + for (ListEntry& entry : s_state.slots) { if (entry.preview_texture) g_gpu_device->RecycleTexture(std::move(entry.preview_texture)); } - s_slots.clear(); + s_state.slots.clear(); if (System::IsShutdown()) return; @@ -904,16 +914,16 @@ void SaveStateSelectorUI::RefreshList(const std::string& serial) else InitializePlaceholderListEntry(&li, std::move(path), i, false); - s_slots.push_back(std::move(li)); + s_state.slots.push_back(std::move(li)); } } else { // reset slot if it's not global - if (!s_current_slot_global) + if (!s_state.current_slot_global) { - s_current_slot = 0; - s_current_slot_global = true; + s_state.current_slot = 0; + s_state.current_slot_global = true; } } @@ -928,7 +938,7 @@ void SaveStateSelectorUI::RefreshList(const std::string& serial) else InitializePlaceholderListEntry(&li, std::move(path), i, true); - s_slots.push_back(std::move(li)); + s_state.slots.push_back(std::move(li)); } } @@ -938,33 +948,33 @@ void SaveStateSelectorUI::Clear() // big picture UI, in which case we have to delete them here... ClearList(); - s_current_slot = 0; - s_current_slot_global = false; - s_scroll_animated.Reset(0.0f); - s_background_animated.Reset(0.0f); + s_state.current_slot = 0; + s_state.current_slot_global = false; + s_state.scroll_animated.Reset(0.0f); + s_state.background_animated.Reset(0.0f); } void SaveStateSelectorUI::ClearList() { - for (ListEntry& li : s_slots) + for (ListEntry& li : s_state.slots) { if (li.preview_texture) g_gpu_device->RecycleTexture(std::move(li.preview_texture)); } - s_slots.clear(); + s_state.slots.clear(); } void SaveStateSelectorUI::DestroyTextures() { Close(); - for (ListEntry& entry : s_slots) + for (ListEntry& entry : s_state.slots) { if (entry.preview_texture) g_gpu_device->RecycleTexture(std::move(entry.preview_texture)); } - s_placeholder_texture.reset(); + s_state.placeholder_texture.reset(); } void SaveStateSelectorUI::RefreshHotkeyLegend() @@ -974,33 +984,34 @@ void SaveStateSelectorUI::RefreshHotkeyLegend() return fmt::format("{} - {}", binding, caption); }; - s_load_legend = format_legend_entry(Host::GetSmallStringSettingValue("Hotkeys", "LoadSelectedSaveState"), - TRANSLATE_SV("SaveStateSelectorUI", "Load")); - s_save_legend = format_legend_entry(Host::GetSmallStringSettingValue("Hotkeys", "SaveSelectedSaveState"), - TRANSLATE_SV("SaveStateSelectorUI", "Save")); - s_prev_legend = format_legend_entry(Host::GetSmallStringSettingValue("Hotkeys", "SelectPreviousSaveStateSlot"), - TRANSLATE_SV("SaveStateSelectorUI", "Select Previous")); - s_next_legend = format_legend_entry(Host::GetSmallStringSettingValue("Hotkeys", "SelectNextSaveStateSlot"), - TRANSLATE_SV("SaveStateSelectorUI", "Select Next")); + s_state.load_legend = format_legend_entry(Host::GetSmallStringSettingValue("Hotkeys", "LoadSelectedSaveState"), + TRANSLATE_SV("SaveStateSelectorUI", "Load")); + s_state.save_legend = format_legend_entry(Host::GetSmallStringSettingValue("Hotkeys", "SaveSelectedSaveState"), + TRANSLATE_SV("SaveStateSelectorUI", "Save")); + s_state.prev_legend = format_legend_entry(Host::GetSmallStringSettingValue("Hotkeys", "SelectPreviousSaveStateSlot"), + TRANSLATE_SV("SaveStateSelectorUI", "Select Previous")); + s_state.next_legend = format_legend_entry(Host::GetSmallStringSettingValue("Hotkeys", "SelectNextSaveStateSlot"), + TRANSLATE_SV("SaveStateSelectorUI", "Select Next")); } void SaveStateSelectorUI::SelectNextSlot(bool open_selector) { - const s32 total_slots = s_current_slot_global ? System::GLOBAL_SAVE_STATE_SLOTS : System::PER_GAME_SAVE_STATE_SLOTS; - s_current_slot++; - if (s_current_slot >= total_slots) + const s32 total_slots = + s_state.current_slot_global ? System::GLOBAL_SAVE_STATE_SLOTS : System::PER_GAME_SAVE_STATE_SLOTS; + s_state.current_slot++; + if (s_state.current_slot >= total_slots) { if (!System::GetGameSerial().empty()) - s_current_slot_global ^= true; - s_current_slot -= total_slots; + s_state.current_slot_global ^= true; + s_state.current_slot -= total_slots; } if (open_selector) { - if (!s_open) + if (!s_state.is_open) Open(); - s_open_time = 0.0f; + s_state.open_time = 0.0f; } else { @@ -1010,20 +1021,21 @@ void SaveStateSelectorUI::SelectNextSlot(bool open_selector) void SaveStateSelectorUI::SelectPreviousSlot(bool open_selector) { - s_current_slot--; - if (s_current_slot < 0) + s_state.current_slot--; + if (s_state.current_slot < 0) { if (!System::GetGameSerial().empty()) - s_current_slot_global ^= true; - s_current_slot += s_current_slot_global ? System::GLOBAL_SAVE_STATE_SLOTS : System::PER_GAME_SAVE_STATE_SLOTS; + s_state.current_slot_global ^= true; + s_state.current_slot += + s_state.current_slot_global ? System::GLOBAL_SAVE_STATE_SLOTS : System::PER_GAME_SAVE_STATE_SLOTS; } if (open_selector) { - if (!s_open) + if (!s_state.is_open) Open(); - s_open_time = 0.0f; + s_state.open_time = 0.0f; } else { @@ -1103,9 +1115,9 @@ void SaveStateSelectorUI::Draw() const float item_height = std::floor(image_size.y + padding * 2.0f); const float text_indent = image_size.x + padding + padding; - for (size_t i = 0; i < s_slots.size(); i++) + for (size_t i = 0; i < s_state.slots.size(); i++) { - const ListEntry& entry = s_slots[i]; + const ListEntry& entry = s_state.slots[i]; const float y_start = item_height * static_cast(i); if (entry.slot == current_slot && entry.global == current_slot_global) @@ -1124,19 +1136,20 @@ void SaveStateSelectorUI::Draw() else if (item_rect.Max.y > window_rect.Max.y) scroll_target = (ImGui::GetScrollY() + (item_rect.Max.y - window_rect.Max.y)); - if (scroll_target != s_scroll_animated.GetEndValue()) - s_scroll_animated.Start(ImGui::GetScrollY(), scroll_target, SCROLL_ANIMATION_TIME); + if (scroll_target != s_state.scroll_animated.GetEndValue()) + s_state.scroll_animated.Start(ImGui::GetScrollY(), scroll_target, SCROLL_ANIMATION_TIME); } - if (s_scroll_animated.IsActive()) - ImGui::SetScrollY(s_scroll_animated.UpdateAndGetValue()); + if (s_state.scroll_animated.IsActive()) + ImGui::SetScrollY(s_state.scroll_animated.UpdateAndGetValue()); - if (s_background_animated.GetEndValue() != p_start.y) - s_background_animated.Start(s_background_animated.UpdateAndGetValue(), p_start.y, BG_ANIMATION_TIME); + if (s_state.background_animated.GetEndValue() != p_start.y) + s_state.background_animated.Start(s_state.background_animated.UpdateAndGetValue(), p_start.y, + BG_ANIMATION_TIME); ImVec2 highlight_pos; - if (s_background_animated.IsActive()) - highlight_pos = ImVec2(p_start.x, s_background_animated.UpdateAndGetValue()); + if (s_state.background_animated.IsActive()) + highlight_pos = ImVec2(p_start.x, s_state.background_animated.UpdateAndGetValue()); else highlight_pos = p_start; @@ -1146,7 +1159,7 @@ void SaveStateSelectorUI::Draw() } if (GPUTexture* preview_texture = - entry.preview_texture ? entry.preview_texture.get() : s_placeholder_texture.get()) + entry.preview_texture ? entry.preview_texture.get() : s_state.placeholder_texture.get()) { ImGui::SetCursorPosY(y_start + padding); ImGui::SetCursorPosX(padding); @@ -1184,13 +1197,13 @@ void SaveStateSelectorUI::Draw() if (ImGui::BeginTable("table", 2)) { ImGui::TableNextColumn(); - ImGui::TextUnformatted(s_load_legend.c_str()); + ImGui::TextUnformatted(s_state.load_legend.c_str()); ImGui::TableNextColumn(); - ImGui::TextUnformatted(s_prev_legend.c_str()); + ImGui::TextUnformatted(s_state.prev_legend.c_str()); ImGui::TableNextColumn(); - ImGui::TextUnformatted(s_save_legend.c_str()); + ImGui::TextUnformatted(s_state.save_legend.c_str()); ImGui::TableNextColumn(); - ImGui::TextUnformatted(s_next_legend.c_str()); + ImGui::TextUnformatted(s_state.next_legend.c_str()); ImGui::EndTable(); } @@ -1204,8 +1217,8 @@ void SaveStateSelectorUI::Draw() ImGui::PopStyleColor(); // auto-close - s_open_time += io.DeltaTime; - if (s_open_time >= s_close_time) + s_state.open_time += io.DeltaTime; + if (s_state.open_time >= s_state.close_time) { Close(); } @@ -1219,25 +1232,25 @@ void SaveStateSelectorUI::Draw() s32 SaveStateSelectorUI::GetCurrentSlot() { - return s_current_slot + 1; + return s_state.current_slot + 1; } bool SaveStateSelectorUI::IsCurrentSlotGlobal() { - return s_current_slot_global; + return s_state.current_slot_global; } std::string SaveStateSelectorUI::GetCurrentSlotPath() { std::string filename; - if (!s_current_slot_global) + if (!s_state.current_slot_global) { if (const std::string& serial = System::GetGameSerial(); !serial.empty()) - filename = System::GetGameSaveStateFileName(serial, s_current_slot + 1); + filename = System::GetGameSaveStateFileName(serial, s_state.current_slot + 1); } else { - filename = System::GetGlobalSaveStateFileName(s_current_slot + 1); + filename = System::GetGlobalSaveStateFileName(s_state.current_slot + 1); } return filename; @@ -1277,7 +1290,7 @@ void SaveStateSelectorUI::SaveCurrentSlot() if (std::string path = GetCurrentSlotPath(); !path.empty()) { Error error; - if (!System::SaveState(path.c_str(), &error, g_settings.create_save_state_backups)) + if (!System::SaveState(std::move(path), &error, g_settings.create_save_state_backups, false)) { Host::AddIconOSDMessage("SaveState", ICON_EMOJI_WARNING, fmt::format(TRANSLATE_FS("OSDMessage", "Failed to save state to slot {0}:\n{1}"), @@ -1312,7 +1325,7 @@ void ImGuiManager::RenderOverlayWindows() const System::State state = System::GetState(); if (state != System::State::Shutdown) { - if (SaveStateSelectorUI::s_open) + if (SaveStateSelectorUI::s_state.is_open) SaveStateSelectorUI::Draw(); } } diff --git a/src/core/settings.cpp b/src/core/settings.cpp index feb4225fe..61733ff60 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -171,8 +171,8 @@ void Settings::Load(const SettingsInterface& si, const SettingsInterface& contro enable_discord_presence = si.GetBoolValue("Main", "EnableDiscordPresence", false); rewind_enable = si.GetBoolValue("Main", "RewindEnable", false); rewind_save_frequency = si.GetFloatValue("Main", "RewindFrequency", 10.0f); - rewind_save_slots = static_cast(si.GetUIntValue("Main", "RewindSaveSlots", 10u)); - runahead_frames = static_cast(si.GetUIntValue("Main", "RunaheadFrameCount", 0u)); + rewind_save_slots = static_cast(std::min(si.GetUIntValue("Main", "RewindSaveSlots", 10u), 65535u)); + runahead_frames = static_cast(std::min(si.GetUIntValue("Main", "RunaheadFrameCount", 0u), 255u)); cpu_execution_mode = ParseCPUExecutionMode( @@ -202,6 +202,8 @@ void Settings::Load(const SettingsInterface& si, const SettingsInterface& contro gpu_disable_texture_copy_to_self = si.GetBoolValue("GPU", "DisableTextureCopyToSelf", false); gpu_disable_memory_import = si.GetBoolValue("GPU", "DisableMemoryImport", false); gpu_disable_raster_order_views = si.GetBoolValue("GPU", "DisableRasterOrderViews", false); + gpu_disable_compute_shaders = si.GetBoolValue("GPU", "DisableComputeShaders", false); + gpu_disable_compressed_textures = si.GetBoolValue("GPU", "DisableCompressedTextures", false); gpu_per_sample_shading = si.GetBoolValue("GPU", "PerSampleShading", false); gpu_use_thread = si.GetBoolValue("GPU", "UseThread", true); gpu_use_software_renderer_for_readbacks = si.GetBoolValue("GPU", "UseSoftwareRendererForReadbacks", false); @@ -539,6 +541,8 @@ void Settings::Save(SettingsInterface& si, bool ignore_base) const si.SetBoolValue("GPU", "DisableTextureCopyToSelf", gpu_disable_texture_copy_to_self); si.SetBoolValue("GPU", "DisableMemoryImport", gpu_disable_memory_import); si.SetBoolValue("GPU", "DisableRasterOrderViews", gpu_disable_raster_order_views); + si.SetBoolValue("GPU", "DisableComputeShaders", gpu_disable_compute_shaders); + si.SetBoolValue("GPU", "DisableCompressedTextures", gpu_disable_compressed_textures); } si.SetBoolValue("GPU", "PerSampleShading", gpu_per_sample_shading); @@ -964,6 +968,8 @@ void Settings::FixIncompatibleSettings(bool display_osd_messages) g_settings.use_old_mdec_routines = false; g_settings.pcdrv_enable = false; g_settings.bios_patch_fast_boot = false; + g_settings.runahead_frames = 0; + g_settings.rewind_enable = false; } // fast forward boot requires fast boot diff --git a/src/core/settings.h b/src/core/settings.h index d63345e7b..cdb1e5f57 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -91,7 +91,7 @@ struct Settings bool rewind_enable : 1 = false; float rewind_save_frequency = 10.0f; - u8 rewind_save_slots = 10; + u16 rewind_save_slots = 10; u8 runahead_frames = 0; GPURenderer gpu_renderer = DEFAULT_GPU_RENDERER; @@ -108,6 +108,8 @@ struct Settings bool gpu_disable_texture_copy_to_self : 1 = false; bool gpu_disable_memory_import : 1 = false; bool gpu_disable_raster_order_views : 1 = false; + bool gpu_disable_compute_shaders : 1 = false; + bool gpu_disable_compressed_textures : 1 = false; bool gpu_per_sample_shading : 1 = false; bool gpu_true_color : 1 = true; bool gpu_scaled_dithering : 1 = true; @@ -168,7 +170,7 @@ struct Settings bool display_stretch_vertically : 1 = false; bool display_auto_resize_window : 1 = false; float display_pre_frame_sleep_buffer = DEFAULT_DISPLAY_PRE_FRAME_SLEEP_BUFFER; - float display_osd_scale = 100.0f; + float display_osd_scale = DEFAULT_OSD_SCALE; float display_osd_margin = 0.0f; float gpu_pgxp_tolerance = -1.0f; float gpu_pgxp_depth_clear_threshold = DEFAULT_GPU_PGXP_DEPTH_THRESHOLD / GPU_PGXP_DEPTH_THRESHOLD_SCALE; diff --git a/src/core/shader_cache_version.h b/src/core/shader_cache_version.h index 95a2b141b..f48ee7f87 100644 --- a/src/core/shader_cache_version.h +++ b/src/core/shader_cache_version.h @@ -5,4 +5,4 @@ #include "common/types.h" -static constexpr u32 SHADER_CACHE_VERSION = 22; +static constexpr u32 SHADER_CACHE_VERSION = 23; diff --git a/src/core/system.cpp b/src/core/system.cpp index 14e484e83..2e1123ebe 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -1663,8 +1663,8 @@ bool System::SaveResumeState(Error* error) return false; } - const std::string path(GetGameSaveStateFileName(s_state.running_game_serial, -1)); - return SaveState(path.c_str(), error, false); + std::string path(GetGameSaveStateFileName(s_state.running_game_serial, -1)); + return SaveState(std::move(path), error, false, true); } bool System::BootSystem(SystemBootParameters parameters, Error* error) @@ -2481,7 +2481,10 @@ bool System::DoState(StateWrapper& sw, GPUTexture** host_texture, bool update_di u32 state_taints = s_state.taints; sw.DoEx(&state_taints, 75, static_cast(0)); if (state_taints != s_state.taints) [[unlikely]] + { WarnAboutStateTaints(state_taints); + s_state.taints |= state_taints; + } sw.Do(&s_state.frame_number); sw.Do(&s_state.internal_frame_number); @@ -3010,14 +3013,14 @@ bool System::ReadAndDecompressStateData(std::FILE* fp, std::span dst, u32 fi } } -bool System::SaveState(const char* path, Error* error, bool backup_existing_save) +bool System::SaveState(std::string path, Error* error, bool backup_existing_save, bool ignore_memcard_busy) { if (!IsValid() || IsReplayingGPUDump()) { Error::SetStringView(error, TRANSLATE_SV("System", "System is not in correct state.")); return false; } - else if (IsSavingMemoryCards()) + else if (!ignore_memcard_busy && IsSavingMemoryCards()) { Error::SetStringView(error, TRANSLATE_SV("System", "Cannot save state while memory card is being saved.")); return false; @@ -3029,40 +3032,62 @@ bool System::SaveState(const char* path, Error* error, bool backup_existing_save if (!SaveStateToBuffer(&buffer, error, 256)) return false; - // TODO: Do this on a thread pool + VERBOSE_LOG("Preparing state save took {:.2f} msec", save_timer.GetTimeMilliseconds()); - if (backup_existing_save && FileSystem::FileExists(path)) - { - Error backup_error; - const std::string backup_filename = Path::ReplaceExtension(path, "bak"); - if (!FileSystem::RenamePath(path, backup_filename.c_str(), &backup_error)) + std::string osd_key = fmt::format("save_state_{}", path); + Host::AddIconOSDMessage(osd_key, ICON_EMOJI_FLOPPY_DISK, + fmt::format(TRANSLATE_FS("System", "Saving state to '{}'."), Path::GetFileName(path)), 60.0f); + + QueueTaskOnThread([path = std::move(path), buffer = std::move(buffer), osd_key = std::move(osd_key), + backup_existing_save, compression = g_settings.save_state_compression]() { + INFO_LOG("Saving state to '{}'...", path); + + Error lerror; + Timer lsave_timer; + + if (backup_existing_save && FileSystem::FileExists(path.c_str())) { - ERROR_LOG("Failed to rename save state backup '{}': {}", Path::GetFileName(backup_filename), - backup_error.GetDescription()); + const std::string backup_filename = Path::ReplaceExtension(path, "bak"); + if (!FileSystem::RenamePath(path.c_str(), backup_filename.c_str(), &lerror)) + { + ERROR_LOG("Failed to rename save state backup '{}': {}", Path::GetFileName(backup_filename), + lerror.GetDescription()); + } } - } - auto fp = FileSystem::CreateAtomicRenamedFile(path, error); - if (!fp) - { - Error::AddPrefixFmt(error, "Cannot open '{}': ", Path::GetFileName(path)); - return false; - } + auto fp = FileSystem::CreateAtomicRenamedFile(path, &lerror); + bool result = false; + if (fp) + { + if (SaveStateBufferToFile(buffer, fp.get(), &lerror, compression)) + result = FileSystem::CommitAtomicRenamedFile(fp, &lerror); + else + FileSystem::DiscardAtomicRenamedFile(fp); + } + else + { + lerror.AddPrefixFmt("Cannot open '{}': ", Path::GetFileName(path)); + } - INFO_LOG("Saving state to '{}'...", path); + VERBOSE_LOG("Saving state took {:.2f} msec", lsave_timer.GetTimeMilliseconds()); + if (result) + { + Host::AddIconOSDMessage(std::move(osd_key), ICON_EMOJI_FLOPPY_DISK, + fmt::format(TRANSLATE_FS("System", "State saved to '{}'."), Path::GetFileName(path)), + Host::OSD_QUICK_DURATION); + } + else + { + Host::AddIconOSDMessage(std::move(osd_key), ICON_EMOJI_WARNING, + fmt::format(TRANSLATE_FS("System", "Failed to save state to '{0}':\n{1}"), + Path::GetFileName(path), lerror.GetDescription()), + Host::OSD_ERROR_DURATION); + } - if (!SaveStateBufferToFile(buffer, fp.get(), error, g_settings.save_state_compression)) - { - FileSystem::DiscardAtomicRenamedFile(fp); - return false; - } + System::RemoveSelfFromTaskThreads(); + }); - Host::AddIconOSDMessage("save_state", ICON_EMOJI_FLOPPY_DISK, - fmt::format(TRANSLATE_FS("OSDMessage", "State saved to '{}'."), Path::GetFileName(path)), - 5.0f); - - VERBOSE_LOG("Saving state took {:.2f} msec", save_timer.GetTimeMilliseconds()); - return FileSystem::CommitAtomicRenamedFile(fp, error); + return true; } bool System::SaveStateToBuffer(SaveStateBuffer* buffer, Error* error, u32 screenshot_size /* = 256 */) @@ -3261,7 +3286,7 @@ u32 System::CompressAndWriteStateData(std::FILE* fp, std::span src, Sa buffer.resize(buffer_size); const int level = - ((method == SaveStateCompressionMode::ZstLow) ? 1 : ((method == SaveStateCompressionMode::ZstHigh) ? 19 : 0)); + ((method == SaveStateCompressionMode::ZstLow) ? 1 : ((method == SaveStateCompressionMode::ZstHigh) ? 18 : 0)); const size_t compressed_size = ZSTD_compress(buffer.data(), buffer_size, src.data(), src.size(), level); if (ZSTD_isError(compressed_size)) [[unlikely]] { @@ -4244,6 +4269,8 @@ void System::CheckForSettingsChanges(const Settings& old_settings) g_settings.gpu_disable_texture_copy_to_self != old_settings.gpu_disable_texture_copy_to_self || g_settings.gpu_disable_memory_import != old_settings.gpu_disable_memory_import || g_settings.gpu_disable_raster_order_views != old_settings.gpu_disable_raster_order_views || + g_settings.gpu_disable_compute_shaders != old_settings.gpu_disable_compute_shaders || + g_settings.gpu_disable_compressed_textures != old_settings.gpu_disable_compressed_textures || g_settings.display_exclusive_fullscreen_control != old_settings.display_exclusive_fullscreen_control)) { // if debug device/threaded presentation change, we need to recreate the whole display @@ -4256,6 +4283,8 @@ void System::CheckForSettingsChanges(const Settings& old_settings) g_settings.gpu_disable_texture_copy_to_self != old_settings.gpu_disable_texture_copy_to_self || g_settings.gpu_disable_memory_import != old_settings.gpu_disable_memory_import || g_settings.gpu_disable_raster_order_views != old_settings.gpu_disable_raster_order_views || + g_settings.gpu_disable_compute_shaders != old_settings.gpu_disable_compute_shaders || + g_settings.gpu_disable_compressed_textures != old_settings.gpu_disable_compressed_textures || g_settings.display_exclusive_fullscreen_control != old_settings.display_exclusive_fullscreen_control); Host::AddIconOSDMessage("RendererSwitch", ICON_FA_PAINT_ROLLER, @@ -4698,6 +4727,10 @@ void System::WarnAboutUnsafeSettings() APPEND_SUBMESSAGE(TRANSLATE_SV("System", "Video timings set to default.")); if (g_settings.gpu_widescreen_hack) APPEND_SUBMESSAGE(TRANSLATE_SV("System", "Widescreen rendering disabled.")); + if (g_settings.gpu_pgxp_enable) + APPEND_SUBMESSAGE(TRANSLATE_SV("System", "PGXP disabled.")); + if (g_settings.gpu_texture_cache) + APPEND_SUBMESSAGE(TRANSLATE_SV("System", "GPU texture cache disabled.")); if (g_settings.display_24bit_chroma_smoothing) APPEND_SUBMESSAGE(TRANSLATE_SV("System", "FMV chroma smoothing disabled.")); if (g_settings.cdrom_read_speedup != 1) @@ -5391,7 +5424,7 @@ std::string System::GetGlobalSaveStateFileName(s32 slot) return Path::Combine(EmuFolders::SaveStates, fmt::format("savestate_{}.sav", slot)); } -std::vector System::GetAvailableSaveStates(const char* serial) +std::vector System::GetAvailableSaveStates(std::string_view serial) { std::vector si; std::string path; @@ -5404,7 +5437,7 @@ std::vector System::GetAvailableSaveStates(const char* serial) si.push_back(SaveStateInfo{std::move(path), sd.ModificationTime, static_cast(slot), global}); }; - if (serial && std::strlen(serial) > 0) + if (!serial.empty()) { add_path(GetGameSaveStateFileName(serial, -1), -1, false); for (s32 i = 1; i <= PER_GAME_SAVE_STATE_SLOTS; i++) @@ -5417,9 +5450,9 @@ std::vector System::GetAvailableSaveStates(const char* serial) return si; } -std::optional System::GetSaveStateInfo(const char* serial, s32 slot) +std::optional System::GetSaveStateInfo(std::string_view serial, s32 slot) { - const bool global = (!serial || serial[0] == 0); + const bool global = serial.empty(); std::string path = global ? GetGlobalSaveStateFileName(slot) : GetGameSaveStateFileName(serial, slot); FILESYSTEM_STAT_DATA sd; @@ -5460,7 +5493,7 @@ std::optional System::GetExtendedSaveStateInfo(const char return ssi; } -void System::DeleteSaveStates(const char* serial, bool resume) +void System::DeleteSaveStates(std::string_view serial, bool resume) { const std::vector states(GetAvailableSaveStates(serial)); for (const SaveStateInfo& si : states) diff --git a/src/core/system.h b/src/core/system.h index c61a715c2..6fda00d7d 100644 --- a/src/core/system.h +++ b/src/core/system.h @@ -256,7 +256,7 @@ void ResetSystem(); /// Loads state from the specified path. bool LoadState(const char* path, Error* error, bool save_undo_state); -bool SaveState(const char* path, Error* error, bool backup_existing_save); +bool SaveState(std::string path, Error* error, bool backup_existing_save, bool ignore_memcard_busy); bool SaveResumeState(Error* error); /// Runs the VM until the CPU execution is canceled. @@ -358,16 +358,16 @@ std::optional GetUndoSaveStateInfo(); bool UndoLoadState(); /// Returns a list of save states for the specified game code. -std::vector GetAvailableSaveStates(const char* serial); +std::vector GetAvailableSaveStates(std::string_view serial); /// Returns save state info if present. If serial is null or empty, assumes global state. -std::optional GetSaveStateInfo(const char* serial, s32 slot); +std::optional GetSaveStateInfo(std::string_view serial, s32 slot); /// Returns save state info from opened save state stream. std::optional GetExtendedSaveStateInfo(const char* path); /// Deletes save states for the specified game code. If resume is set, the resume state is deleted too. -void DeleteSaveStates(const char* serial, bool resume); +void DeleteSaveStates(std::string_view serial, bool resume); /// Returns the path to the memory card for the specified game, considering game settings. std::string GetGameMemoryCardPath(std::string_view serial, std::string_view path, u32 slot, diff --git a/src/duckstation-qt/consolesettingswidget.cpp b/src/duckstation-qt/consolesettingswidget.cpp index 1c98360fd..fb4af157c 100644 --- a/src/duckstation-qt/consolesettingswidget.cpp +++ b/src/duckstation-qt/consolesettingswidget.cpp @@ -44,7 +44,6 @@ ConsoleSettingsWidget::ConsoleSettingsWidget(SettingsWindow* dialog, QWidget* pa SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.region, "Console", "Region", &Settings::ParseConsoleRegionName, &Settings::GetConsoleRegionName, Settings::DEFAULT_CONSOLE_REGION); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enable8MBRAM, "Console", "Enable8MBRAM", false); - SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableCheats, "Console", "EnableCheats", false); SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.cpuExecutionMode, "CPU", "ExecutionMode", &Settings::ParseCPUExecutionMode, &Settings::GetCPUExecutionModeName, Settings::DEFAULT_CPU_EXECUTION_MODE); diff --git a/src/duckstation-qt/consolesettingswidget.ui b/src/duckstation-qt/consolesettingswidget.ui index 0272b16d8..18c54b282 100644 --- a/src/duckstation-qt/consolesettingswidget.ui +++ b/src/duckstation-qt/consolesettingswidget.ui @@ -48,13 +48,6 @@ - - - - Enable Cheats - - - diff --git a/src/duckstation-qt/gamecheatsettingswidget.cpp b/src/duckstation-qt/gamecheatsettingswidget.cpp index eba4f7d43..dd365fb9b 100644 --- a/src/duckstation-qt/gamecheatsettingswidget.cpp +++ b/src/duckstation-qt/gamecheatsettingswidget.cpp @@ -626,34 +626,7 @@ void GameCheatSettingsWidget::onClearClicked() } disableAllCheats(); - - Error error; - std::string path = Cheats::GetChtFilename(m_dialog->getGameSerial(), m_dialog->getGameHash(), true); - if (FileSystem::FileExists(path.c_str())) - { - if (!FileSystem::DeleteFile(path.c_str(), &error)) - ERROR_LOG("Failed to remove cht file '{}': {}", Path::GetFileName(path), error.GetDescription()); - } - - // check for a non-hashed path and remove that too - path = Cheats::GetChtFilename(m_dialog->getGameSerial(), std::nullopt, true); - if (FileSystem::FileExists(path.c_str())) - { - if (!FileSystem::DeleteFile(path.c_str(), &error)) - ERROR_LOG("Failed to remove cht file '{}': {}", Path::GetFileName(path), error.GetDescription()); - } - - // and a legacy cht file with the game title - if (const std::string& title = m_dialog->getGameTitle(); !title.empty()) - { - path = fmt::format("{}" FS_OSPATH_SEPARATOR_STR "{}.cht", EmuFolders::Cheats, Path::SanitizeFileName(title)); - if (FileSystem::FileExists(path.c_str())) - { - if (!FileSystem::DeleteFile(path.c_str(), &error)) - ERROR_LOG("Failed to remove cht file '{}': {}", Path::GetFileName(path), error.GetDescription()); - } - } - + Cheats::RemoveAllCodes(m_dialog->getGameSerial(), m_dialog->getGameTitle(), m_dialog->getGameHash()); reloadList(); } diff --git a/src/duckstation-qt/graphicssettingswidget.cpp b/src/duckstation-qt/graphicssettingswidget.cpp index a3c540a50..bd9535f91 100644 --- a/src/duckstation-qt/graphicssettingswidget.cpp +++ b/src/duckstation-qt/graphicssettingswidget.cpp @@ -308,6 +308,12 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget* SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableTextureBuffers, "GPU", "DisableTextureBuffers", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableTextureCopyToSelf, "GPU", "DisableTextureCopyToSelf", false); + SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableMemoryImport, "GPU", "DisableMemoryImport", false); + SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableRasterOrderViews, "GPU", "DisableRasterOrderViews", + false); + SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableComputeShaders, "GPU", "DisableComputeShaders", false); + SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableCompressedTextures, "GPU", "DisableCompressedTextures", + false); // Init all dependent options. updateRendererDependentOptions(); @@ -632,6 +638,12 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget* dialog->registerWidgetHelp(m_ui.disableRasterOrderViews, tr("Disable Rasterizer Order Views"), tr("Unchecked"), tr("Disables the use of rasterizer order views. Useful for testing broken graphics " "drivers. Only for developer use.")); + dialog->registerWidgetHelp(m_ui.disableComputeShaders, tr("Disable Compute Shaders"), tr("Unchecked"), + tr("Disables the use of compute shaders. Useful for testing broken graphics drivers. " + "Only for developer use.")); + dialog->registerWidgetHelp(m_ui.disableCompressedTextures, tr("Disable Compressed Textures"), tr("Unchecked"), + tr("Disables the use of compressed textures. Useful for testing broken graphics drivers. " + "Only for developer use.")); } GraphicsSettingsWidget::~GraphicsSettingsWidget() = default; diff --git a/src/duckstation-qt/graphicssettingswidget.ui b/src/duckstation-qt/graphicssettingswidget.ui index 4ed1bace1..43f0ff2d8 100644 --- a/src/duckstation-qt/graphicssettingswidget.ui +++ b/src/duckstation-qt/graphicssettingswidget.ui @@ -1331,13 +1331,6 @@ - - - - Disable Texture Copy To Self - - - @@ -1345,20 +1338,6 @@ - - - - Disable Dual-Source Blending - - - - - - - Disable Framebuffer Fetch - - - @@ -1366,6 +1345,13 @@ + + + + Disable Dual-Source Blending + + + @@ -1373,6 +1359,20 @@ + + + + Disable Framebuffer Fetch + + + + + + + Disable Texture Copy To Self + + + @@ -1387,6 +1387,20 @@ + + + + Disable Compute Shaders + + + + + + + Disable Compressed Textures + + + diff --git a/src/duckstation-qt/inputbindingwidgets.cpp b/src/duckstation-qt/inputbindingwidgets.cpp index cc54a81ff..45bbd713c 100644 --- a/src/duckstation-qt/inputbindingwidgets.cpp +++ b/src/duckstation-qt/inputbindingwidgets.cpp @@ -10,6 +10,7 @@ #include "core/host.h" #include "common/bitutils.h" +#include "common/string_util.h" #include #include @@ -61,26 +62,22 @@ void InputBindingWidget::initialize(SettingsInterface* sif, InputBindingInfo::Ty void InputBindingWidget::updateText() { + static constexpr const char* help_text = + QT_TR_NOOP("Left-click to change binding.\nShift-click to set multiple bindings."); + static constexpr const char* help_clear_text = QT_TR_NOOP("Right-click to remove binding."); + if (m_bindings.empty()) { setText(QString()); + setToolTip(QStringLiteral("%1\n\n%2").arg(tr("No binding set.")).arg(tr(help_text))); } else if (m_bindings.size() > 1) { setText(tr("%n bindings", "", static_cast(m_bindings.size()))); // keep the full thing for the tooltip - std::stringstream ss; - bool first = true; - for (const std::string& binding : m_bindings) - { - if (first) - first = false; - else - ss << "\n"; - ss << binding; - } - setToolTip(QString::fromStdString(ss.str())); + const QString qss = QString::fromStdString(StringUtil::JoinString(m_bindings.begin(), m_bindings.end(), "\n")); + setToolTip(QStringLiteral("%1\n\n%2\n%3").arg(qss).arg(tr(help_text)).arg(help_clear_text)); } else { @@ -93,6 +90,7 @@ void InputBindingWidget::updateText() if (binding_text.length() > 35) binding_text = binding_text.left(35).append(QStringLiteral("...")); setText(binding_text); + setToolTip(QStringLiteral("%1\n\n%2\n%3").arg(binding_text).arg(tr(help_text)).arg(tr(help_clear_text))); } } diff --git a/src/duckstation-qt/isobrowserwindow.cpp b/src/duckstation-qt/isobrowserwindow.cpp index cb15c820c..82dc2f8ca 100644 --- a/src/duckstation-qt/isobrowserwindow.cpp +++ b/src/duckstation-qt/isobrowserwindow.cpp @@ -28,6 +28,7 @@ ISOBrowserWindow::ISOBrowserWindow(QWidget* parent) : QWidget(parent) setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); connect(m_ui.openFile, &QAbstractButton::clicked, this, &ISOBrowserWindow::onOpenFileClicked); + connect(m_ui.extract, &QAbstractButton::clicked, this, &ISOBrowserWindow::onExtractClicked); connect(m_ui.directoryView, &QTreeWidget::itemClicked, this, &ISOBrowserWindow::onDirectoryItemClicked); connect(m_ui.fileView, &QTreeWidget::itemActivated, this, &ISOBrowserWindow::onFileItemActivated); connect(m_ui.fileView, &QTreeWidget::itemSelectionChanged, this, &ISOBrowserWindow::onFileItemSelectionChanged); @@ -102,6 +103,16 @@ void ISOBrowserWindow::onOpenFileClicked() } } +void ISOBrowserWindow::onExtractClicked() +{ + const QList items = m_ui.fileView->selectedItems(); + if (items.isEmpty()) + return; + + const QString path = items.front()->data(0, Qt::UserRole).toString(); + extractFile(path); +} + void ISOBrowserWindow::onDirectoryItemClicked(QTreeWidgetItem* item, int column) { populateFiles(item->data(0, Qt::UserRole).toString()); @@ -295,8 +306,6 @@ void ISOBrowserWindow::populateFiles(const QString& path) } const auto add_entry = [this](const std::string& full_path, const IsoReader::ISODirectoryEntry& entry) { - const std::string_view filename = Path::GetFileName(full_path); - QTreeWidgetItem* item = new QTreeWidgetItem; item->setIcon( 0, QIcon::fromTheme(entry.IsDirectory() ? QStringLiteral("folder-open-line") : QStringLiteral("file-line"))); diff --git a/src/duckstation-qt/isobrowserwindow.h b/src/duckstation-qt/isobrowserwindow.h index 6e67c8c03..c13141bbb 100644 --- a/src/duckstation-qt/isobrowserwindow.h +++ b/src/duckstation-qt/isobrowserwindow.h @@ -25,6 +25,7 @@ protected: private Q_SLOTS: void onOpenFileClicked(); + void onExtractClicked(); void onDirectoryItemClicked(QTreeWidgetItem* item, int column); void onFileItemActivated(QTreeWidgetItem* item, int column); void onFileItemSelectionChanged(); diff --git a/src/duckstation-qt/mainwindow.cpp b/src/duckstation-qt/mainwindow.cpp index 02aab3b49..72a50df27 100644 --- a/src/duckstation-qt/mainwindow.cpp +++ b/src/duckstation-qt/mainwindow.cpp @@ -811,7 +811,7 @@ void MainWindow::populateGameListContextMenu(const GameList::Entry* entry, QWidg if (!entry->serial.empty()) { - std::vector available_states(System::GetAvailableSaveStates(entry->serial.c_str())); + std::vector available_states(System::GetAvailableSaveStates(entry->serial)); const QString timestamp_format = QLocale::system().dateTimeFormat(QLocale::ShortFormat); const bool challenge_mode = Achievements::IsHardcoreModeActive(); for (SaveStateInfo& ssi : available_states) @@ -869,7 +869,7 @@ void MainWindow::populateGameListContextMenu(const GameList::Entry* entry, QWidg return; } - System::DeleteSaveStates(entry->serial.c_str(), true); + System::DeleteSaveStates(entry->serial, true); }); } } @@ -881,10 +881,11 @@ static QString FormatTimestampForSaveStateMenu(u64 timestamp) return qtime.toString(QLocale::system().dateTimeFormat(QLocale::ShortFormat)); } -void MainWindow::populateLoadStateMenu(const char* game_serial, QMenu* menu) +void MainWindow::populateLoadStateMenu(std::string_view game_serial, QMenu* menu) { - auto add_slot = [this, game_serial, menu](const QString& title, const QString& empty_title, bool global, s32 slot) { - std::optional ssi = System::GetSaveStateInfo(global ? nullptr : game_serial, slot); + auto add_slot = [this, menu](const QString& title, const QString& empty_title, const std::string_view& serial, + s32 slot) { + std::optional ssi = System::GetSaveStateInfo(serial, slot); const QString menu_title = ssi.has_value() ? title.arg(slot).arg(FormatTimestampForSaveStateMenu(ssi->timestamp)) : empty_title.arg(slot); @@ -913,28 +914,30 @@ void MainWindow::populateLoadStateMenu(const char* game_serial, QMenu* menu) connect(load_from_state, &QAction::triggered, g_emu_thread, &EmuThread::undoLoadState); menu->addSeparator(); - if (game_serial && std::strlen(game_serial) > 0) + if (!game_serial.empty()) { for (u32 slot = 1; slot <= System::PER_GAME_SAVE_STATE_SLOTS; slot++) - add_slot(tr("Game Save %1 (%2)"), tr("Game Save %1 (Empty)"), false, static_cast(slot)); + add_slot(tr("Game Save %1 (%2)"), tr("Game Save %1 (Empty)"), game_serial, static_cast(slot)); menu->addSeparator(); } + std::string_view empty_serial; for (u32 slot = 1; slot <= System::GLOBAL_SAVE_STATE_SLOTS; slot++) - add_slot(tr("Global Save %1 (%2)"), tr("Global Save %1 (Empty)"), true, static_cast(slot)); + add_slot(tr("Global Save %1 (%2)"), tr("Global Save %1 (Empty)"), empty_serial, static_cast(slot)); } -void MainWindow::populateSaveStateMenu(const char* game_serial, QMenu* menu) +void MainWindow::populateSaveStateMenu(std::string_view game_serial, QMenu* menu) { - auto add_slot = [game_serial, menu](const QString& title, const QString& empty_title, bool global, s32 slot) { - std::optional ssi = System::GetSaveStateInfo(global ? nullptr : game_serial, slot); + auto add_slot = [menu](const QString& title, const QString& empty_title, const std::string_view& serial, s32 slot) { + std::optional ssi = System::GetSaveStateInfo(serial, slot); const QString menu_title = ssi.has_value() ? title.arg(slot).arg(FormatTimestampForSaveStateMenu(ssi->timestamp)) : empty_title.arg(slot); QAction* save_action = menu->addAction(menu_title); - connect(save_action, &QAction::triggered, [global, slot]() { g_emu_thread->saveState(global, slot); }); + connect(save_action, &QAction::triggered, + [global = serial.empty(), slot]() { g_emu_thread->saveState(global, slot); }); }; menu->clear(); @@ -952,16 +955,17 @@ void MainWindow::populateSaveStateMenu(const char* game_serial, QMenu* menu) }); menu->addSeparator(); - if (game_serial && std::strlen(game_serial) > 0) + if (!game_serial.empty()) { for (u32 slot = 1; slot <= System::PER_GAME_SAVE_STATE_SLOTS; slot++) - add_slot(tr("Game Save %1 (%2)"), tr("Game Save %1 (Empty)"), false, static_cast(slot)); + add_slot(tr("Game Save %1 (%2)"), tr("Game Save %1 (Empty)"), game_serial, static_cast(slot)); menu->addSeparator(); } + std::string_view empty_serial; for (u32 slot = 1; slot <= System::GLOBAL_SAVE_STATE_SLOTS; slot++) - add_slot(tr("Global Save %1 (%2)"), tr("Global Save %1 (Empty)"), true, static_cast(slot)); + add_slot(tr("Global Save %1 (%2)"), tr("Global Save %1 (Empty)"), empty_serial, static_cast(slot)); } void MainWindow::populateChangeDiscSubImageMenu(QMenu* menu, QActionGroup* action_group) @@ -1238,12 +1242,12 @@ void MainWindow::onChangeDiscMenuAboutToHide() void MainWindow::onLoadStateMenuAboutToShow() { - populateLoadStateMenu(s_current_game_serial.toUtf8().constData(), m_ui.menuLoadState); + populateLoadStateMenu(s_current_game_serial.toStdString(), m_ui.menuLoadState); } void MainWindow::onSaveStateMenuAboutToShow() { - populateSaveStateMenu(s_current_game_serial.toUtf8().constData(), m_ui.menuSaveState); + populateSaveStateMenu(s_current_game_serial.toStdString(), m_ui.menuSaveState); } void MainWindow::onStartFullscreenUITriggered() diff --git a/src/duckstation-qt/mainwindow.h b/src/duckstation-qt/mainwindow.h index 2d8d06faa..1f8e573c6 100644 --- a/src/duckstation-qt/mainwindow.h +++ b/src/duckstation-qt/mainwindow.h @@ -268,8 +268,8 @@ private: /// Fills menu with save state info and handlers. void populateGameListContextMenu(const GameList::Entry* entry, QWidget* parent_window, QMenu* menu); - void populateLoadStateMenu(const char* game_serial, QMenu* menu); - void populateSaveStateMenu(const char* game_serial, QMenu* menu); + void populateLoadStateMenu(std::string_view game_serial, QMenu* menu); + void populateSaveStateMenu(std::string_view game_serial, QMenu* menu); /// Fills menu with the current playlist entries. The disc index is marked as checked. void populateChangeDiscSubImageMenu(QMenu* menu, QActionGroup* action_group); diff --git a/src/duckstation-qt/qthost.cpp b/src/duckstation-qt/qthost.cpp index b9c183098..87847ba94 100644 --- a/src/duckstation-qt/qthost.cpp +++ b/src/duckstation-qt/qthost.cpp @@ -223,10 +223,16 @@ bool QtHost::SaveGameSettings(SettingsInterface* sif, bool delete_if_empty) INISettingsInterface* ini = static_cast(sif); Error error; + // if there's no keys, just toss the whole thing out if (delete_if_empty && ini->IsEmpty()) { INFO_LOG("Removing empty gamesettings ini {}", Path::GetFileName(ini->GetFileName())); + + // grab the settings lock while we're writing the file, that way the CPU thread doesn't try + // to read it at the same time. + const auto lock = Host::GetSettingsLock(); + if (FileSystem::FileExists(ini->GetFileName().c_str()) && !FileSystem::DeleteFile(ini->GetFileName().c_str(), &error)) { @@ -243,6 +249,9 @@ bool QtHost::SaveGameSettings(SettingsInterface* sif, bool delete_if_empty) // clean unused sections, stops the file being bloated sif->RemoveEmptySections(); + // see above + const auto lock = Host::GetSettingsLock(); + if (!sif->Save(&error)) { Host::ReportErrorAsync( @@ -1403,7 +1412,7 @@ void EmuThread::saveState(const QString& filename, bool block_until_done /* = fa return; Error error; - if (!System::SaveState(filename.toUtf8().data(), &error, g_settings.create_save_state_backups)) + if (!System::SaveState(filename.toStdString(), &error, g_settings.create_save_state_backups, false)) emit errorReported(tr("Error"), tr("Failed to save state: %1").arg(QString::fromStdString(error.GetDescription()))); } @@ -1423,7 +1432,7 @@ void EmuThread::saveState(bool global, qint32 slot, bool block_until_done /* = f if (!System::SaveState((global ? System::GetGlobalSaveStateFileName(slot) : System::GetGameSaveStateFileName(System::GetGameSerial(), slot)) .c_str(), - &error, g_settings.create_save_state_backups)) + &error, g_settings.create_save_state_backups, false)) { emit errorReported(tr("Error"), tr("Failed to save state: %1").arg(QString::fromStdString(error.GetDescription()))); } @@ -1908,6 +1917,18 @@ bool Host::ConfirmMessage(std::string_view title, std::string_view message) QString::fromUtf8(message.data(), message.size())); } +void Host::ConfirmMessageAsync(std::string_view title, std::string_view message, ConfirmMessageAsyncCallback callback) +{ + QtHost::RunOnUIThread([title = QtUtils::StringViewToQString(title), message = QtUtils::StringViewToQString(message), + callback = std::move(callback)]() mutable { + auto lock = g_main_window->pauseAndLockSystem(); + + const bool result = (QMessageBox::question(lock.getDialogParent(), title, message) != QMessageBox::No); + + callback(result); + }); +} + void Host::OpenURL(std::string_view url) { QtHost::RunOnUIThread([url = QtUtils::StringViewToQString(url)]() { QtUtils::OpenURL(g_main_window, QUrl(url)); }); diff --git a/src/duckstation-qt/translations/duckstation-qt_pt-BR.ts b/src/duckstation-qt/translations/duckstation-qt_pt-BR.ts index 33c56bbe0..95c9a82cd 100644 --- a/src/duckstation-qt/translations/duckstation-qt_pt-BR.ts +++ b/src/duckstation-qt/translations/duckstation-qt_pt-BR.ts @@ -5221,37 +5221,37 @@ This file can be several gigabytes, so be aware of SSD wear. EmuThread - - - - - - + + + + + + Error Erro - + Failed to boot system: %1 Falha ao iniciar o sistema: %1 - + Failed to load state: %1 Falha ao carregar o estado: %1 - + No resume save state found. Salvamento rápido não encontrado. - + Memory Card Busy Cartão de memória em uso - + WARNING: Your game is still saving to the memory card. Continuing to %1 may IRREVERSIBLY DESTROY YOUR MEMORY CARD. We recommend resuming your game and waiting 5 seconds for it to finish saving. Do you want to %1 anyway? @@ -5260,60 +5260,60 @@ Do you want to %1 anyway? Deseja continuar para %1 mesmo assim? - + shut down encerrar - + reset reiniciar - + change disc mudar disco - + Failed to switch to subimage %1 Falha ao alternar para a subimagem %1 - - + + Failed to save state: %1 Falha ao salvar o estado: %1 - + Game: %1 (%2) Jogo: %1 (%2) - + Rich presence inactive or unsupported. Presença rica do Discord inativa ou não suportada. - + Game not loaded or no RetroAchievements available. Jogo não carregado ou sem conquistas disponíveis. - + %1x%2 %1x%2 - + Game: %1 FPS Jogo: %1 FPS - + Video: %1 FPS (%2%) Vídeo: %1 FPS (%2%) @@ -9497,7 +9497,7 @@ Deseja fazer {0} mesmo assim? Automático - + Software Software @@ -11044,60 +11044,60 @@ Escanear o diretório desta forma demora mais tempo porém, identificará arquiv Compatibilidade: - - - - - - - - - + + + + + + + + + Unknown Desconhecido - + %1 (Published by %2) %1 (Publicado por %2) - + Published by %1 Publicado por %1 - + Released %1 Lançado %1 - + %1-%2 players %1-%2 Jogadores - + %1 players %1 Jogadores - + %1-%2 memory card blocks %1-%2 blocos de cartão de memória - + %1 memory card blocks %1 blocos de cartão de memória - + Use Global Settings Usar configuração global - + Game Specific Configuration Configuração específica do jogo @@ -11111,62 +11111,62 @@ Escanear o diretório desta forma demora mais tempo porém, identificará arquiv Mostrar bandeira padrão - + %1 tracks covering %2 MB (%3 MB on disk) %1 faixas cobrindo %2 MB (%3 MB no disco) - + Track %1 Faixa %1 - + <not computed> <Não calculado> - + Compatibility Report Relatório de compatibilidade - + Per-game controller configuration initialized with global settings. Configuração do controle por jogo carregada com as configurações globais. - + Error Erro - + Failed to open CD image for hashing. Falha ao abrir a imagem do CD. - + Verifying hashes... Calculando valores... - + Revision: %1 Revisão: %1 - + N/A - + Serial Mismatch: %1 vs %2 Número de série incompatível: %1 vs %2 {1 ?} - + Search on Redump.org Procurar em Redump.org @@ -13319,6 +13319,112 @@ Escanear o diretório desta forma demora mais tempo porém, identificará arquiv Volume - + + ISOBrowserWindow + + + Form + + + + + File: + Arquivo: + + + + ... + + + + + + Name + Nome + + + + Date + Data + + + + Size + Tamanho + + + + Extract + Extrair + + + + Close + Fechar + + + + + + Error + Erro + + + + + Failed to open %1: +%2 + Falha ao abrir %1: +%2 + + + + ISO Browser - %1 + Navegador de imagem - %1 + + + + Select File + Escolha o arquivo + + + + &Open + Abrir + + + + &Extract + Extrair + + + + Extract File + Extrair arquivo + + + + Extracting %1... + Extraindo %1... + + + + Failed to save %1: +%2 + Falha ao salvar %1: +%2 + + + + %1 KB + + + + + <Parent Directory> + <Diretório pai> + + InputBindingDialog @@ -14124,8 +14230,8 @@ Escanear o diretório desta forma demora mais tempo porém, identificará arquiv - - + + Change Disc Mudar disco @@ -14137,8 +14243,8 @@ Escanear o diretório desta forma demora mais tempo porém, identificará arquiv - - + + Load State Carregar estado @@ -14325,13 +14431,13 @@ Escanear o diretório desta forma demora mais tempo porém, identificará arquiv - + Start Big Picture Mode Iniciar em modo tela grande - + Big Picture Modo tela grande @@ -14534,7 +14640,7 @@ Escanear o diretório desta forma demora mais tempo porém, identificará arquiv - + Resume Continuar @@ -14661,23 +14767,23 @@ Escanear o diretório desta forma demora mais tempo porém, identificará arquiv Editor de cartão de memória - + Failed to get window info from widget Falha ao tentar obter informação da janela - - + + Select Disc Image Escolha uma imagem de disco - + Start Disc Iniciar disco - + Could not find any CD-ROM devices. Please ensure you have a CD-ROM drive connected and sufficient permissions to access it. Não foi possível encontrar nenhum dispositivo de CD-ROM. Certifique-se de ter uma unidade de CD-ROM conectada e permissões suficientes para acessá-la. @@ -14686,108 +14792,108 @@ Escanear o diretório desta forma demora mais tempo porém, identificará arquiv Todos os tipos de arquivo (*.bin *.img *.iso *.cue *.chd *.ecm *.mds *.pbp *.exe *.psexe *.psf *.minipsf *.m3u);;Imagens de faixa única (*.bin *.img *.iso);;Cue Sheets (*.cue);;MAME CHD Images (*.chd);;Error Code Modeler Images (*.ecm);;Media Descriptor Sidecar Images (*.mds);;PlayStation EBOOTs (*.pbp);;PlayStation Executables (*.exe *.psexe);;Arquivos de formato de som portátil (*.psf *.minipsf);;Playlists (*.m3u) - + All File Types (*.bin *.img *.iso *.cue *.chd *.cpe *.ecm *.mds *.pbp *.elf *.exe *.psexe *.ps-exe *.psx *.psf *.minipsf *.m3u *.psxgpu);;Single-Track Raw Images (*.bin *.img *.iso);;Cue Sheets (*.cue);;MAME CHD Images (*.chd);;Error Code Modeler Images (*.ecm);;Media Descriptor Sidecar Images (*.mds);;PlayStation EBOOTs (*.pbp *.PBP);;PlayStation Executables (*.cpe *.elf *.exe *.psexe *.ps-exe, *.psx);;Portable Sound Format Files (*.psf *.minipsf);;Playlists (*.m3u);;PSX GPU Dumps (*.psxgpu) Todos os tipos (*.bin *.img *.iso *.cue *.chd *.cpe *.ecm *.mds *.pbp *.elf *.exe *.psexe *.ps-exe *.psx *.psf *.minipsf *.m3u *.psxgpu);;Single-Track Raw Images (*.bin *.img *.iso);;Cue Sheets (*.cue);;MAME CHD Images (*.chd);;Error Code Modeler Images (*.ecm);;Media Descriptor Sidecar Images (*.mds);;PlayStation EBOOTs (*.pbp *.PBP);;PlayStation Executables (*.cpe *.elf *.exe *.psexe *.ps-exe, *.psx);;Portable Sound Format Files (*.psf *.minipsf);;Playlists (*.m3u);;PSX GPU Dumps (*.psxgpu) - - - + + + Error Erro - + Paused Pausado - + %1 (%2) %1 (%2) - + Select disc drive: Escolha a unidade de disco: - + Resume (%1) Continuar (%1) - - - + + + Game Save %1 (%2) Jogo salvo %1 (%2) - + Edit Memory Cards... Editar cartões de memória... - + Delete Save States... Apagar estados salvos... - + Confirm Save State Deletion Confirmar deleção de estado salvo - + Are you sure you want to delete all save states for %1? The saves will not be recoverable. Tem certeza de que deseja apagar os estados salvos %1? Não será possivel reverter esta ação. - + Load From File... Carregar do arquivo... - - + + Select Save State File Escolher arquivo de salvamento rápido - - + + Save States (*.sav) Salvamento rápido (*.sav) - + Undo Load State Desfazer estado carregado - - + + Game Save %1 (Empty) Jogo salvo %1 (Vazio) - - + + Global Save %1 (%2) Compartimento global %1 (%2) - - + + Global Save %1 (Empty) Compartimento global %1 (Vazio) - + Save To File... Salvar para arquivo... @@ -14816,12 +14922,12 @@ The saves will not be recoverable. &Aplicar trapaças - + Load Resume State Carregar estado salvo - + A resume save state was found for this game, saved at: %1. @@ -14834,52 +14940,52 @@ Do you want to load this state, or start from a fresh boot? Você deseja que este arquivo seja carregado ou que seja reiniciado novamente? - + Fresh Boot Inicialização limpa - + Delete And Boot Excluir e iniciar - + Failed to delete save state file '%1'. Falha ao apagar o arquivo de estado salvo '%1'. - + Confirm Disc Change Confirmar troca de disco - + Do you want to swap discs or boot the new image (via system reset)? Deseja trocar discos ou inicializar outro (via reinicialização do sistema)? - + Swap Disc Trocar disco - + Reset Redefinir - + Media Capture Mídia e captura - + <p>Sorry, you are trying to update a DuckStation version which is not an official GitHub release. To prevent incompatibilities, the auto-updater is only enabled on official builds.</p><p>Please download an official release from from <a href="https://www.duckstation.org/">duckstation.org</a>.</p> <p>Desculpe, você está tentando atualizar uma versão do DuckStation que não é uma versão oficial do GitHub. Para evitar incompatibilidades, o atualizador automático só é ativado em versões oficiais</p><p>Por favor baixe a versão oficial em <a href="https://www.duckstation.org/">duckstation.org</a>.</p> - + Cancel Cancelar @@ -14888,42 +14994,42 @@ Você deseja que este arquivo seja carregado ou que seja reiniciado novamente?Gerenciador de trapaças - + Stop Big Picture Mode Parar modo tela cheia - + Exit Big Picture Sair do modo tela cheia - + You must select a disc to change discs. Você deve escolher um disco para confirmar a troca - + All Cover Image Types (*.jpg *.jpeg *.png *.webp) Todos os tipos de imagem de capa (*.jpg *.jpeg *.png *.webp) - + You must select a different file to the current cover image. Você deve selecionar um arquivo diferente da imagem de capa atual. - + Failed to remove '%1' Falha ao remover '%1' - + Confirm Reset Confirmar redefinição - + Are you sure you want to reset the play time for '%1'? This action cannot be undone. @@ -14932,8 +15038,8 @@ This action cannot be undone. Esta ação não poderá ser desfeita. - - + + Properties... Propriedades... @@ -14942,92 +15048,97 @@ Esta ação não poderá ser desfeita. Todos os tipos de arquivo (*.bin *.img *.iso *.cue *.chd *.ecm *.mds *.pbp *.exe *.psexe *.psf *.minipsf *.m3u);;Imagens de faixa única (*.bin *.img *.iso);;Cue Sheets (*.cue);;MAME CHD Images (*.chd);;Error Code Modeler Images (*.ecm);;Media Descriptor Sidecar Images (*.mds);;PlayStation EBOOTs (*.pbp);;PlayStation Executables (*.exe *.psexe);;Arquivos de formato de som portátil (*.psf *.minipsf);;Playlists (*.m3u) - + Select Cheats... Escolher trapaças... - + Cheats are not enabled. As trapaças não estão ativadas. - + &Apply Cheat Aplicar trapaça - + Open Containing Directory... Abrir diretório... - - + + Browse ISO... + Navegar pela ISO... + + + + Set Cover Image... Definir imagem de capa... - + Default Boot Inicialização padrão - + Fast Boot Inicialização rápida - + Full Boot Inicialização completa - + Boot and Debug Iniciar jogo com depurador - - + + Exclude From List Excluir da lista - + Reset Play Time Redefinir tempo de jogo - + Select Disc Escolher disco - + Add Search Directory... Adicionar um novo diretório... - + Select Cover Image Escolher imagem de capa - + Cover Already Exists Capa já existe - + A cover image for this game already exists, do you wish to replace it? A capa para este jogo já existe, deseja substituí-lá? - - - - + + + + Copy Error Erro ao copiar @@ -15036,35 +15147,35 @@ Esta ação não poderá ser desfeita. Todos os tipos de arquivo (*.bin *.img *.iso *.cue *.chd *.ecm *.mds *.pbp *.exe *.psexe *.psf *.minipsf *.m3u);;Imagens de faixa única (*.bin *.img *.iso);;Cue Sheets (*.cue);;MAME CHD Images (*.chd);;Error Code Modeler Images (*.ecm);;Media Descriptor Sidecar Images (*.mds);;PlayStation EBOOTs (*.pbp);;PlayStation Executables (*.exe *.psexe);;Arquivos de formato de som portátil (*.psf *.minipsf);;Playlists (*.m3u) - + Failed to remove existing cover '%1' Falha ao remover capa existente '%1' - + Failed to copy '%1' to '%2' Falha ao copiar '%1' para '%2' - + %1x Scale Expandir para %1x - - - + + + Destination File Destino do arquivo - - + + Binary Files (*.bin) Arquivos binários (*.bin) - + Binary Files (*.bin);;PNG Images (*.png) Arquivos (*.bin);;Imagens (*.png) @@ -15119,36 +15230,36 @@ Esta ação não poderá ser desfeita. - + Confirm Shutdown Confirmar desligamento - + Are you sure you want to shut down the virtual machine? Tem certeza de que deseja desligar a máquina virtual? - + Save State For Resume Salvar estado e continuar - - - - + + + + Memory Card Not Found Cartão de memória não encontrado - + Memory card '%1' does not exist. Do you want to create an empty memory card? Cartão de memória '%1' não existe, você deseja criar um cartão de memória vazio? - - + + Memory card '%1' could not be found. Try starting the game and saving to create it. Cartão de memória '%1' não encontrado. Experimente iniciar o jogo e salvá-lo para que ele seja criado. @@ -15165,22 +15276,22 @@ As trapaças ficam guardadas no estado de salvamento rápido mesmo após serem d Tem certeza de que deseja continuar? - + Failed to create memory card '%1': %2 Erro ao criar cartão de memória '%1': %2 - + %1 Files (*.%2) %1 Arquivos (*.%2) - + Updater Error Erro na atualização - + Automatic updating is not supported on the current platform. Atualizações automáticas não são compatíveis com a plataforma atual. @@ -16464,7 +16575,7 @@ Você deve excluir o cartão de memória manualmente se quiser salvar.Pulo de quadros não é compatível com a versão de 32Bits. - + Rewind is disabled because runahead is enabled. Função de retrocesso desligada porque o avanço rápido está ligado. @@ -16474,12 +16585,12 @@ Você deve excluir o cartão de memória manualmente se quiser salvar.As opções do recompilador foram alteradas, limpando todos os blocos. - + Widescreen hack is now enabled, and aspect ratio is set to {}. Ajuste de tela panorâmica ligado, a proporção da imagem está definida para {}. - + Widescreen hack is now disabled, and aspect ratio is set to {}. Ajuste de tela panorâmica desligado, a proporção da imagem foi definida para {}. @@ -16720,14 +16831,14 @@ Deixando conectado {2}. Disco inserido '{}' ({}). - + Failed to save undo load state: {} Falha ao desfazer o carregamento: {} - + Switching to {} renderer... Alterando para renderizador {}... @@ -17000,17 +17111,17 @@ The URL was: %1 QtAsyncProgressThread - + Error Erro - + Question Pergunta - + Information Informação @@ -17018,32 +17129,32 @@ The URL was: %1 QtHost - - - - - - - - - - - - - - + + + + + + + + + + + + + + Error Erro - + An error occurred while deleting empty game settings: {} Ocorreu um erro ao apagar as configurações vazias: {} - + An error occurred while saving game settings: {} Ocorreu um erro ao salvar as configurações por jogo: @@ -17054,7 +17165,7 @@ The URL was: %1 Falha ao criar protocolo HTTP. - + Downloading %1... Baixando %1... @@ -17063,90 +17174,90 @@ The URL was: %1 O download falhou com o código HTTP %1. - + Failed to create HTTPDownloader: %1 - + Download failed with HTTP status code %1: %2 O download falhou com o código HTTP %1: %2 - + Download failed: Data is empty. O download falhou: Os dados estão vazios. - + Failed to write '%1'. Falha ao gravar '%1'. - + Failed to open downloaded zip file. Falha ao abrir o arquivo compactado baixado. - + Failed to locate '%1' in zip. Falha ao localizar '%1' no zip. - + Failed to open '%1': %2. Falha ao abrir '%1': %2. - + Failed to read '%1' from zip. Falha ao ler '%1' a partir do zip. - + Failed to write to '%1'. Falha ao gravar em '%1'. - + RA: Logged in as %1 (%2, %3 softcore). %4 unread messages. RA: conectado como %1 (%2, %3 normal). %4 mensagens não lidas. - + Controller {} connected. Controle {} conectado. - + System paused because controller {} was disconnected. O sistema foi pausado porque o controle {} foi desconectado. - + Controller {} disconnected. Controle {} desconectado. - + File '%1' does not exist. O arquivo '%1' não existe. - + The specified save state does not exist. O dado de salvamento não existe. - + Cannot use no-gui mode, because no boot filename was specified. Não é possível usar o modo no-gui, porque nenhum parâmetro de inicialização foi configurado. - + Cannot use batch mode, because no boot filename was specified. Não é possível usar este modo porque nenhum parâmetro de inicialização foi configurado. @@ -17179,17 +17290,17 @@ The URL was: %1 Cancelar - + Error Erro - + Question Pergunta - + Information Informação @@ -17436,121 +17547,121 @@ Deseja criar esse diretório? Zstandard (Alta) - + None LogLevel Nenhum - + Error LogLevel Erro - + Warning LogLevel Alerta - + Information LogLevel Informação - + Verbose LogLevel Detalhado - + Developer LogLevel Desenvolvedor - + Debug LogLevel Depurar - + Trace LogLevel Rastreio - + Auto-Detect ConsoleRegion Detectar automaticamente - + NTSC-J (Japan) ConsoleRegion NTSC-J (Japão) - + NTSC-U/C (US, Canada) ConsoleRegion NTSC-U/C (US, Canadá) - + PAL (Europe, Australia) ConsoleRegion PAL (Europeu, Austrália) - + NTSC-J (Japan) DiscRegion NTSC-J (Japão) - + NTSC-U/C (US, Canada) DiscRegion NTSC-U/C (US, Canadá) - + PAL (Europe, Australia) DiscRegion PAL (Europeu, Austrália) - + Other DiscRegion Outros - + Non-PS1 DiscRegion Não é de PS1 - + Interpreter (Slowest) CPUExecutionMode Interpretador (mais lento) - + Cached Interpreter (Faster) CPUExecutionMode Interpretador armazenado (rápido) - + Recompiler (Fastest) CPUExecutionMode Recompilador (mais rápido) @@ -17561,529 +17672,529 @@ Deseja criar esse diretório? Novo recompilador (experimental) - + Disabled (Slowest) CPUFastmemMode Desativado (Lento) - + MMap (Hardware, Fastest, 64-Bit Only) CPUFastmemMode MMap (hardware, mais rápido) - + LUT (Faster) CPUFastmemMode LUT (rápido) - + Automatic GPURenderer Automático - + Direct3D 11 GPURenderer - + Direct3D 12 GPURenderer - + Metal GPURenderer - + Vulkan GPURenderer - + OpenGL GPURenderer - + Software GPURenderer Software - + Nearest-Neighbor GPUTextureFilter Nearest-Neighbor - + Bilinear GPUTextureFilter Bi-linear - + Bilinear (No Edge Blending) GPUTextureFilter Bi-linear (sem AA) - + JINC2 (Slow) GPUTextureFilter JINC2 (lento) - + JINC2 (Slow, No Edge Blending) GPUTextureFilter JINC2 (lento, sem AA) - + xBR (Very Slow) GPUTextureFilter xBR (muito lento) - + xBR (Very Slow, No Edge Blending) GPUTextureFilter xBR (muito lento sem AA) - + Disabled GPULineDetectMode Desligado - + Quads GPULineDetectMode Quadrantes - + Triangles (Basic) GPULineDetectMode Triângulos (básico) - + Triangles (Aggressive) GPULineDetectMode Triângulos (agressivo) - + Disabled GPUDownsampleMode Desligado - + Box (Downsample 3D/Smooth All) GPUDownsampleMode Misto (Reduz 3D / Suaviza tudo) - + Adaptive (Preserve 3D/Smooth 2D) GPUDownsampleMode Adaptativo (Preserva o 3D / Suaviza 2D) - + Disabled GPUWireframeMode Desligado - + Overlay Wireframe GPUWireframeMode Sobreposição - + Only Wireframe GPUWireframeMode Esboço - + Disabled GPUDumpCompressionMode Desativado - + Zstandard (Low) GPUDumpCompressionMode Zstandard (baixo) - + Zstandard (Default) GPUDumpCompressionMode Zstandard (Padrão) - + Zstandard (High) GPUDumpCompressionMode Zstandard (Alta) - + XZ (Low) GPUDumpCompressionMode XZ (baixa) - + XZ (Default) GPUDumpCompressionMode XZ (padrão) - + XZ (High) GPUDumpCompressionMode XZ (alta) - + Disabled (Flickering) DisplayDeinterlacingMode Oscilação (desativada) - + Weave (Combing) DisplayDeinterlacingMode Ondulação - + Blend (Blur) DisplayDeinterlacingMode Desfoque - + Adaptive (FastMAD) DisplayDeinterlacingMode Adaptativo (rápido) - + Progressive (Optimal) DisplayDeinterlacingMode Progressivo (ideal) - + None DisplayCropMode Nenhum - + Only Overscan Area DisplayCropMode Somente área renderizada - + Only Overscan Area (Aspect Uncorrected) DisplayCropMode Somente área renderizada (Aspecto não corrigido) - + All Borders DisplayCropMode Todas as bordas - + All Borders (Aspect Uncorrected) DisplayCropMode Todas as bordas (Aspecto não corrigido) - + Auto (Game Native) DisplayAspectRatio Auto (resolução nativa) - + Stretch To Fill DisplayAspectRatio Esticar para preencher - + Custom DisplayAspectRatio Personalizado - + Left / Top DisplayAlignment Topo superior - + Center DisplayAlignment Centro - + Right / Bottom DisplayAlignment Esquerda inferior - + No Rotation DisplayRotation Sem rotação - + Rotate 90° (Clockwise) DisplayRotation Girar 90° (sentido horário) - + Rotate 180° (Vertical Flip) DisplayRotation Girar 180° (inversão vertical) - + Rotate 270° (Clockwise) DisplayRotation Girar 270° (sentido horário) - + Disabled ForceVideoTiming Desligado - + NTSC (60hz) ForceVideoTiming - + PAL (50hz) ForceVideoTiming - + Nearest-Neighbor DisplayScalingMode Nearest-Neighbor - + Nearest-Neighbor (Integer) DisplayScalingMode Nearest-Neighbor (integro) - + Bilinear (Smooth) DisplayScalingMode Bilinear (suave) - + Bilinear (Sharp) DisplayScalingMode Bilinear (forte) - + Bilinear (Integer) DisplayScalingMode Bilinear (inteiro) - + Automatic DisplayExclusiveFullscreenControl Automático - + Disallowed DisplayExclusiveFullscreenControl Não permitido - + Allowed DisplayExclusiveFullscreenControl Permitido - + Screen Resolution DisplayScreenshotMode Resolução da tela - + Internal Resolution DisplayScreenshotMode Resolução interna - + Internal Resolution (Aspect Uncorrected) DisplayScreenshotMode Resolução interna (aspecto não corrigido) - + PNG DisplayScreenshotFormat - + JPEG DisplayScreenshotFormat - + WebP DisplayScreenshotFormat - + No Memory Card MemoryCardType Sem cartão de memória - + Shared Between All Games MemoryCardType Compartilhada entre jogos - + Separate Card Per Game (Serial) MemoryCardType Cartão separado por jogo (Serial) - + Separate Card Per Game (Title) MemoryCardType Cartão separado por jogo (título) - + Separate Card Per Game (File Title) MemoryCardType Separar cartão por jogo (título do arquivo) - + Non-Persistent Card (Do Not Save) MemoryCardType Cartão não persistente (não salvar) - + Disabled MultitapMode Desligado - + Enable on Port 1 Only MultitapMode Ativar somente na porta 1 - + Enable on Port 2 Only MultitapMode Ativar somente na porta 2 - + Enable on Ports 1 and 2 MultitapMode Ativar nas portas 1 e 2 - + Uncompressed SaveStateCompressionMode Não comprimido - + Deflate (Low) SaveStateCompressionMode Compressão (baixa) - + Deflate (Default) SaveStateCompressionMode Diminuto (Padrão) - + Deflate (High) SaveStateCompressionMode Compressão (alta) - + Zstandard (Low) SaveStateCompressionMode Zstandard (baixo) - + Zstandard (Default) SaveStateCompressionMode Zstandard (Padrão) - + Zstandard (High) SaveStateCompressionMode Zstandard (Alta) @@ -18720,7 +18831,7 @@ Escanear o diretório desta forma demora mais tempo porém, identificará arquiv - + Error Erro @@ -18746,7 +18857,7 @@ Escanear o diretório desta forma demora mais tempo porém, identificará arquiv Falha ao salvar o estado: {} - + Compatibility settings are not enabled. Some games may not function correctly. As configurações de compatibilidade não estão ativadas. Alguns jogos podem não funcionar corretamente. @@ -18833,22 +18944,22 @@ O nome do arquivo SBI deve corresponder ao nome da imagem do disco.Alternado para a subimagem {} ({}) em '{}'. - + CPU clock speed is set to {}% ({} / {}). This may crash games. Velocidade do CPU configurada para {}% ({} / {}. pode causar falhas nos jogos. - + CD-ROM read speedup set to {}x (effective speed {}x). This may crash games. Velocidade de leitura do CD-ROM definida como {}x (velocidade efetiva {}x). Jogos podem travar. - + CD-ROM seek speedup set to {}. This may crash games. Velocidade de leitura do CD-ROM definida para {}. Jogos podem travar. - + Instant Instantâneo @@ -18857,17 +18968,17 @@ O nome do arquivo SBI deve corresponder ao nome da imagem do disco.A opção forçar temporizador no modo NTSC está ativada. Os jogos serão executados em uma velocidade incorreta. - + Multisample anti-aliasing is enabled, some games may not render correctly. O anti-serrilhamento múltiplo está ativado, alguns jogos podem não ser renderizados corretamente. - + Round upscaled texture coordinates is enabled. This may cause rendering errors. A opção de arredondamento das coordenadas de textura com escalas maiores está ativada. Isso pode causar erros de renderização. - + 8MB RAM is enabled, this may be incompatible with some games. Modo de 8 MB de RAM está ativado, o que pode ser incompatível com alguns jogos. @@ -18893,59 +19004,59 @@ O nome do arquivo SBI deve corresponder ao nome da imagem do disco.A inicialização foi cancelada. - + This save state was created with the following tainted options, and may be unstable. You will need to reset the system to clear any effects. Esse estado salvo foi criado com opções adulteradas e pode ser instável. Será necessário reiniciar o sistema para eliminar quaisquer efeitos. - + Force frame timings is enabled. Games may run at incorrect speeds. Forçar temporização de quadros está ativada. Jogos podem ser executados em velocidades incorretas. - + Texture cache is enabled. This feature is experimental, some games may not render correctly. O armazenamento de textura é atualmente experimental e pode causar erros de renderização em alguns jogos. - + Safe mode is enabled. Modo seguro está ativado. - + Overclock disabled. Overclock desligado. - + 8MB RAM disabled. 8MB RAM desligado. - + Cheats disabled. Trapaças desativada. - + Patches disabled. Modificações desativadas. - + Resolution scale set to 1x. Escala de resolução definida para 1x. - + Multisample anti-aliasing disabled. Suavização de serrilhado desligado. - + True color disabled. Cor real desativada. @@ -18954,107 +19065,117 @@ O nome do arquivo SBI deve corresponder ao nome da imagem do disco.Suavização de gradientes desligada. - + Texture filtering disabled. Filtro de textura desativado. - + Interlaced rendering enabled. Renderização entrelaçada ativada. - + Video timings set to default. Temporizadores de vídeo definidos para o padrão. - + Widescreen rendering disabled. Renderização em tela panorâmica desativada. - + + PGXP disabled. + PGXP disabled. + + + + GPU texture cache disabled. + Cache de textura da GPU desativado. + + + FMV chroma smoothing disabled. Suavização de croma do FMV desligado. - + CD-ROM read speedup disabled. Velocidade de leitura CD-ROM desligado. - + CD-ROM seek speedup disabled. Velocidade de busca CD-ROM desligado. - + Mute CD-ROM audio disabled. Silenciar o áudio do CD-ROM desativado. - + VRAM write texture replacements disabled. Substituições de textura na VRAM desativadas. - + Use old MDEC routines disabled. Usar rotinas MDEC antigas desativado. - + PCDrv disabled. PCDrv desativado. - + Fast boot disabled. Inicialização rápida desativada. - + CD-ROM SubQ Skew is enabled. This will break games. A derivação do CD-ROM no módulo SubQ está ativada. Erros poderão ocorrer. - + Failed to save resume state: {} Falha ao resumir o estado: {} - + capturing audio and video Capturando áudio e vídeo - + capturing video Capturando vídeo - + capturing audio Capturando áudio - + Failed to create media capture: {0} Falha ao criar a captura de mídia: {0} - + Starting {0} to '{1}'. Iniciando {0} a '{1}'. - + Stopped {0} to '{1}'. Terminado {0} a '{1}'. - + Stopped {0}: {1}. Terminado {0}: {1}. diff --git a/src/duckstation-regtest/regtest_host.cpp b/src/duckstation-regtest/regtest_host.cpp index c7923c9ac..9f96a69c8 100644 --- a/src/duckstation-regtest/regtest_host.cpp +++ b/src/duckstation-regtest/regtest_host.cpp @@ -150,6 +150,16 @@ bool Host::ConfirmMessage(std::string_view title, std::string_view message) return true; } +void Host::ConfirmMessageAsync(std::string_view title, std::string_view message, ConfirmMessageAsyncCallback callback) +{ + if (!title.empty() && !message.empty()) + ERROR_LOG("ConfirmMessage: {}: {}", title, message); + else if (!message.empty()) + ERROR_LOG("ConfirmMessage: {}", message); + + callback(true); +} + void Host::ReportDebuggerMessage(std::string_view message) { ERROR_LOG("ReportDebuggerMessage: {}", message); diff --git a/src/scmversion/gen_scmversion.bat b/src/scmversion/gen_scmversion.bat index 007a93887..52307a215 100644 --- a/src/scmversion/gen_scmversion.bat +++ b/src/scmversion/gen_scmversion.bat @@ -1,10 +1,12 @@ @echo off SET VERSIONFILE="scmversion.cpp" +PUSHD %~dp0 FOR /F "tokens=* USEBACKQ" %%g IN (`git rev-parse HEAD`) do (SET "HASH=%%g") FOR /F "tokens=* USEBACKQ" %%g IN (`git rev-parse --abbrev-ref HEAD`) do (SET "BRANCH=%%g") FOR /F "tokens=* USEBACKQ" %%g IN (`git describe --dirty`) do (SET "TAG=%%g") FOR /F "tokens=* USEBACKQ" %%g IN (`git log -1 --date=iso8601-strict "--format=%%cd"`) do (SET "CDATE=%%g") +POPD SET SIGNATURELINE=// %HASH% %BRANCH% %TAG% %CDATE% SET /P EXISTINGLINE=< %VERSIONFILE% diff --git a/src/util/d3d_common.cpp b/src/util/d3d_common.cpp index ce5ac6ea0..937c52895 100644 --- a/src/util/d3d_common.cpp +++ b/src/util/d3d_common.cpp @@ -475,8 +475,9 @@ std::optional> D3DCommon::CompileShaderWithFXC(u32 shader_m return {}; } - static constexpr UINT flags_non_debug = D3DCOMPILE_OPTIMIZATION_LEVEL3; - static constexpr UINT flags_debug = D3DCOMPILE_SKIP_OPTIMIZATION | D3DCOMPILE_DEBUG; + static constexpr UINT flags_non_debug = D3DCOMPILE_PACK_MATRIX_ROW_MAJOR | D3DCOMPILE_OPTIMIZATION_LEVEL3; + static constexpr UINT flags_debug = + D3DCOMPILE_PACK_MATRIX_ROW_MAJOR | D3DCOMPILE_SKIP_OPTIMIZATION | D3DCOMPILE_DEBUG; Microsoft::WRL::ComPtr blob; Microsoft::WRL::ComPtr error_blob; @@ -556,12 +557,14 @@ std::optional> D3DCommon::CompileShaderWithDXC(u32 shader_m static constexpr const wchar_t* nondebug_arguments[] = { L"-Qstrip_reflect", L"-Qstrip_debug", + DXC_ARG_PACK_MATRIX_ROW_MAJOR, DXC_ARG_OPTIMIZATION_LEVEL3, }; static constexpr const wchar_t* debug_arguments[] = { L"-Qstrip_reflect", DXC_ARG_DEBUG, L"-Qembed_debug", + DXC_ARG_PACK_MATRIX_ROW_MAJOR, DXC_ARG_SKIP_OPTIMIZATIONS, }; const wchar_t* const* arguments = debug_device ? debug_arguments : nondebug_arguments; @@ -630,6 +633,7 @@ static constexpr std::array(GPUTe {DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_UNKNOWN }, // BGRA8 {DXGI_FORMAT_B5G6R5_UNORM, DXGI_FORMAT_B5G6R5_UNORM, DXGI_FORMAT_B5G6R5_UNORM, DXGI_FORMAT_UNKNOWN }, // RGB565 {DXGI_FORMAT_B5G5R5A1_UNORM, DXGI_FORMAT_B5G5R5A1_UNORM, DXGI_FORMAT_B5G5R5A1_UNORM, DXGI_FORMAT_UNKNOWN }, // RGB5A1 + {DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN }, // A1BGR5 {DXGI_FORMAT_R8_UNORM, DXGI_FORMAT_R8_UNORM, DXGI_FORMAT_R8_UNORM, DXGI_FORMAT_UNKNOWN }, // R8 {DXGI_FORMAT_R16_TYPELESS, DXGI_FORMAT_R16_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_D16_UNORM }, // D16 {DXGI_FORMAT_R24G8_TYPELESS, DXGI_FORMAT_R24_UNORM_X8_TYPELESS, DXGI_FORMAT_R24_UNORM_X8_TYPELESS, DXGI_FORMAT_D24_UNORM_S8_UINT }, // D24S8 diff --git a/src/util/gpu_device.cpp b/src/util/gpu_device.cpp index de710f6e3..231283c2a 100644 --- a/src/util/gpu_device.cpp +++ b/src/util/gpu_device.cpp @@ -233,6 +233,47 @@ GPUSwapChain::GPUSwapChain(const WindowInfo& wi, GPUVSyncMode vsync_mode, bool a GPUSwapChain::~GPUSwapChain() = default; +GSVector4i GPUSwapChain::PreRotateClipRect(const GSVector4i& v) +{ + GSVector4i new_clip; + switch (m_window_info.surface_prerotation) + { + case WindowInfo::PreRotation::Identity: + new_clip = v; + break; + + case WindowInfo::PreRotation::Rotate90Clockwise: + { + const s32 height = (v.w - v.y); + const s32 y = m_window_info.surface_height - v.y - height; + new_clip = GSVector4i(y, v.x, y + height, v.z); + } + break; + + case WindowInfo::PreRotation::Rotate180Clockwise: + { + const s32 width = (v.z - v.x); + const s32 height = (v.w - v.y); + const s32 x = m_window_info.surface_width - v.x - width; + const s32 y = m_window_info.surface_height - v.y - height; + new_clip = GSVector4i(x, y, x + width, y + height); + } + break; + + case WindowInfo::PreRotation::Rotate270Clockwise: + { + const s32 width = (v.z - v.x); + const s32 x = m_window_info.surface_width - v.x - width; + new_clip = GSVector4i(v.y, x, v.w, x + width); + } + break; + + DefaultCaseIsUnreachable() + } + + return new_clip; +} + bool GPUSwapChain::ShouldSkipPresentingFrame() { // Only needed with FIFO. But since we're so fast, we allow it always. @@ -674,20 +715,18 @@ void GPUDevice::RenderImGui(GPUSwapChain* swap_chain) if (draw_data->CmdListsCount == 0 || !swap_chain) return; + const s32 post_rotated_height = swap_chain->GetPostRotatedHeight(); SetPipeline(m_imgui_pipeline.get()); - SetViewportAndScissor(0, 0, swap_chain->GetWidth(), swap_chain->GetHeight()); + SetViewport(0, 0, swap_chain->GetPostRotatedWidth(), post_rotated_height); - const float L = 0.0f; - const float R = static_cast(swap_chain->GetWidth()); - const float T = 0.0f; - const float B = static_cast(swap_chain->GetHeight()); - const float ortho_projection[4][4] = { - {2.0f / (R - L), 0.0f, 0.0f, 0.0f}, - {0.0f, 2.0f / (T - B), 0.0f, 0.0f}, - {0.0f, 0.0f, 0.5f, 0.0f}, - {(R + L) / (L - R), (T + B) / (B - T), 0.5f, 1.0f}, - }; - PushUniformBuffer(ortho_projection, sizeof(ortho_projection)); + GSMatrix4x4 mproj = GSMatrix4x4::OffCenterOrthographicProjection( + 0.0f, 0.0f, static_cast(swap_chain->GetWidth()), static_cast(swap_chain->GetHeight()), 0.0f, 1.0f); + if (swap_chain->GetPreRotation() != WindowInfo::PreRotation::Identity) + { + mproj = + GSMatrix4x4::RotationZ(WindowInfo::GetZRotationForPreRotation(swap_chain->GetPreRotation())) * mproj; + } + PushUniformBuffer(&mproj, sizeof(mproj)); // Render command lists const bool flip = UsesLowerLeftOrigin(); @@ -708,20 +747,12 @@ void GPUDevice::RenderImGui(GPUSwapChain* swap_chain) if (pcmd->ElemCount == 0 || pcmd->ClipRect.z <= pcmd->ClipRect.x || pcmd->ClipRect.w <= pcmd->ClipRect.y) continue; + GSVector4i clip = GSVector4i(GSVector4::load(&pcmd->ClipRect.x)); + clip = swap_chain->PreRotateClipRect(clip); if (flip) - { - const s32 height = static_cast(pcmd->ClipRect.w - pcmd->ClipRect.y); - const s32 flipped_y = static_cast(swap_chain->GetHeight()) - static_cast(pcmd->ClipRect.y) - height; - SetScissor(static_cast(pcmd->ClipRect.x), flipped_y, static_cast(pcmd->ClipRect.z - pcmd->ClipRect.x), - height); - } - else - { - SetScissor(static_cast(pcmd->ClipRect.x), static_cast(pcmd->ClipRect.y), - static_cast(pcmd->ClipRect.z - pcmd->ClipRect.x), - static_cast(pcmd->ClipRect.w - pcmd->ClipRect.y)); - } + clip = FlipToLowerLeft(clip, post_rotated_height); + SetScissor(clip); SetTextureSampler(0, reinterpret_cast(pcmd->TextureId), m_linear_sampler.get()); DrawIndexed(pcmd->ElemCount, base_index + pcmd->IdxOffset, base_vertex + pcmd->VtxOffset); } @@ -1041,7 +1072,7 @@ std::unique_ptr GPUDevice::FetchTexture(u32 width, u32 height, u32 l return ret; } -std::unique_ptr +GPUDevice::AutoRecycleTexture GPUDevice::FetchAutoRecycleTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, GPUTexture::Type type, GPUTexture::Format format, GPUTexture::Flags flags, const void* data /* = nullptr */, u32 data_stride /* = 0 */, Error* error /* = nullptr */) @@ -1396,7 +1427,7 @@ bool dyn_libs::OpenSpirvCross(Error* error) if (s_spirv_cross_library.IsOpen()) return true; -#ifdef _WIN32 +#if defined(_WIN32) || defined(__ANDROID__) // SPVC's build on Windows doesn't spit out a versioned DLL. const std::string libname = DynamicLibrary::GetVersionedFilename("spirv-cross-c-shared"); #else diff --git a/src/util/gpu_device.h b/src/util/gpu_device.h index 8a98cb4f5..ffcd7fd10 100644 --- a/src/util/gpu_device.h +++ b/src/util/gpu_device.h @@ -507,7 +507,10 @@ public: ALWAYS_INLINE const WindowInfo& GetWindowInfo() const { return m_window_info; } ALWAYS_INLINE u32 GetWidth() const { return m_window_info.surface_width; } ALWAYS_INLINE u32 GetHeight() const { return m_window_info.surface_height; } + ALWAYS_INLINE u32 GetPostRotatedWidth() const { return m_window_info.GetPostRotatedWidth(); } + ALWAYS_INLINE u32 GetPostRotatedHeight() const { return m_window_info.GetPostRotatedHeight(); } ALWAYS_INLINE float GetScale() const { return m_window_info.surface_scale; } + ALWAYS_INLINE WindowInfo::PreRotation GetPreRotation() const { return m_window_info.surface_prerotation; } ALWAYS_INLINE GPUTexture::Format GetFormat() const { return m_window_info.surface_format; } ALWAYS_INLINE GPUVSyncMode GetVSyncMode() const { return m_vsync_mode; } @@ -517,6 +520,8 @@ public: virtual bool ResizeBuffers(u32 new_width, u32 new_height, float new_scale, Error* error) = 0; virtual bool SetVSyncMode(GPUVSyncMode mode, bool allow_present_throttle, Error* error) = 0; + GSVector4i PreRotateClipRect(const GSVector4i& v); + bool ShouldSkipPresentingFrame(); void ThrottlePresentation(); @@ -630,6 +635,7 @@ public: { void operator()(GPUTexture* const tex); }; + using AutoRecycleTexture = std::unique_ptr; static constexpr u32 MAX_TEXTURE_SAMPLERS = 8; static constexpr u32 MIN_TEXEL_BUFFER_ELEMENTS = 4 * 1024 * 512; @@ -742,10 +748,9 @@ public: std::unique_ptr FetchTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, GPUTexture::Type type, GPUTexture::Format format, GPUTexture::Flags flags, const void* data = nullptr, u32 data_stride = 0, Error* error = nullptr); - std::unique_ptr - FetchAutoRecycleTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, GPUTexture::Type type, - GPUTexture::Format format, GPUTexture::Flags flags, const void* data = nullptr, - u32 data_stride = 0, Error* error = nullptr); + AutoRecycleTexture FetchAutoRecycleTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, + GPUTexture::Type type, GPUTexture::Format format, GPUTexture::Flags flags, + const void* data = nullptr, u32 data_stride = 0, Error* error = nullptr); std::unique_ptr FetchAndUploadTextureImage(const Image& image, GPUTexture::Flags flags = GPUTexture::Flags::None, Error* error = nullptr); diff --git a/src/util/gpu_texture.cpp b/src/util/gpu_texture.cpp index deeaec939..99f5623fc 100644 --- a/src/util/gpu_texture.cpp +++ b/src/util/gpu_texture.cpp @@ -31,6 +31,7 @@ const char* GPUTexture::GetFormatName(Format format) "BGRA8", // BGRA8 "RGB565", // RGB565 "RGB5A1", // RGB5A1 + "A1BGR5", // A1BGR5 "R8", // R8 "D16", // D16 "D24S8", // D24S8 @@ -152,6 +153,7 @@ GPUTexture::Format GPUTexture::GetTextureFormatForImageFormat(ImageFormat format Format::BGRA8, // BGRA8 Format::RGB565, // RGB565 Format::RGB5A1, // RGB5A1 + Format::A1BGR5, // A1BGR5 Format::Unknown, // BGR8 Format::BC1, // BC1 Format::BC2, // BC2 @@ -171,6 +173,7 @@ ImageFormat GPUTexture::GetImageFormatForTextureFormat(Format format) ImageFormat::BGRA8, // BGRA8 ImageFormat::RGB565, // RGB565 ImageFormat::RGB5A1, // RGB5A1 + ImageFormat::A1BGR5, // A1BGR5 ImageFormat::None, // R8 ImageFormat::None, // D16 ImageFormat::None, // D24S8 @@ -253,6 +256,7 @@ u32 GPUTexture::GetPixelSize(GPUTexture::Format format) 4, // BGRA8 2, // RGB565 2, // RGB5A1 + 2, // A1BGR5 1, // R8 2, // D16 4, // D24S8 diff --git a/src/util/gpu_texture.h b/src/util/gpu_texture.h index e243da148..40e4be872 100644 --- a/src/util/gpu_texture.h +++ b/src/util/gpu_texture.h @@ -44,6 +44,7 @@ public: BGRA8, RGB565, RGB5A1, + A1BGR5, R8, D16, D24S8, diff --git a/src/util/host.h b/src/util/host.h index 5afe611ab..d0258bfba 100644 --- a/src/util/host.h +++ b/src/util/host.h @@ -8,6 +8,7 @@ #include "common/types.h" #include +#include #include #include #include @@ -44,6 +45,11 @@ void ReportErrorAsync(std::string_view title, std::string_view message); /// Displays a synchronous confirmation on the UI thread, i.e. blocks the caller. bool ConfirmMessage(std::string_view title, std::string_view message); +/// Displays an asynchronous confirmation on the UI thread, but does not block the caller. +/// The callback may be executed on a different thread. Use RunOnCPUThread() in the callback to ensure safety. +using ConfirmMessageAsyncCallback = std::function; +void ConfirmMessageAsync(std::string_view title, std::string_view message, ConfirmMessageAsyncCallback callback); + /// Returns the user agent to use for HTTP requests. std::string GetHTTPUserAgent(); diff --git a/src/util/http_downloader.cpp b/src/util/http_downloader.cpp index 8a97eb8a2..b7fb69b5e 100644 --- a/src/util/http_downloader.cpp +++ b/src/util/http_downloader.cpp @@ -160,8 +160,11 @@ void HTTPDownloader::LockedPollRequests(std::unique_lock& lock) // run callback with lock unheld lock.unlock(); - if (req->status_code != HTTP_STATUS_OK) + if (req->status_code >= 0 && req->status_code != HTTP_STATUS_OK) req->error.SetStringFmt("Request failed with HTTP status code {}", req->status_code); + else if (req->status_code < 0) + DEV_LOG("Request failed with error {}", req->error.GetDescription()); + req->callback(req->status_code, req->error, req->content_type, std::move(req->data)); CloseRequest(req); lock.lock(); diff --git a/src/util/image.cpp b/src/util/image.cpp index 632e243b0..4b635a5bd 100644 --- a/src/util/image.cpp +++ b/src/util/image.cpp @@ -168,6 +168,7 @@ const char* Image::GetFormatName(ImageFormat format) "BGRA8", // BGRA8 "RGB565", // RGB565 "RGB5A1", // RGB5A1 + "A1BGR5", // A1BGR5 "BGR8", // BGR8 "BC1", // BC1 "BC2", // BC2 @@ -187,6 +188,7 @@ u32 Image::GetPixelSize(ImageFormat format) 4, // BGRA8 2, // RGB565 2, // RGB5A1 + 2, // A1BGR5 3, // BGR8 8, // BC1 - 16 pixels in 64 bits 16, // BC2 - 16 pixels in 128 bits diff --git a/src/util/image.h b/src/util/image.h index c57548e0f..bf02899d7 100644 --- a/src/util/image.h +++ b/src/util/image.h @@ -21,6 +21,7 @@ enum class ImageFormat : u8 BGRA8, RGB565, RGB5A1, + A1BGR5, BGR8, BC1, BC2, diff --git a/src/util/imgui_fullscreen.cpp b/src/util/imgui_fullscreen.cpp index 3ff62dbb5..2a4cd190a 100644 --- a/src/util/imgui_fullscreen.cpp +++ b/src/util/imgui_fullscreen.cpp @@ -60,83 +60,8 @@ static void PopulateFileSelectorItems(); static void SetFileSelectorDirectory(std::string dir); static ImGuiID GetBackgroundProgressID(const char* str_id); -ImFont* g_standard_font = nullptr; -ImFont* g_medium_font = nullptr; -ImFont* g_large_font = nullptr; -ImFont* g_icon_font = nullptr; - -float g_layout_scale = 1.0f; -float g_rcp_layout_scale = 1.0f; -float g_layout_padding_left = 0.0f; -float g_layout_padding_top = 0.0f; - -ImVec4 UIBackgroundColor; -ImVec4 UIBackgroundTextColor; -ImVec4 UIBackgroundLineColor; -ImVec4 UIBackgroundHighlightColor; -ImVec4 UIPopupBackgroundColor; -ImVec4 UIDisabledColor; -ImVec4 UIPrimaryColor; -ImVec4 UIPrimaryLightColor; -ImVec4 UIPrimaryDarkColor; -ImVec4 UIPrimaryTextColor; -ImVec4 UITextHighlightColor; -ImVec4 UIPrimaryLineColor; -ImVec4 UISecondaryColor; -ImVec4 UISecondaryWeakColor; -ImVec4 UISecondaryStrongColor; -ImVec4 UISecondaryTextColor; - -static u32 s_menu_button_index = 0; -static u32 s_close_button_state = 0; -static ImGuiDir s_has_pending_nav_move = ImGuiDir_None; -static FocusResetType s_focus_reset_queued = FocusResetType::None; -static bool s_light_theme = false; -static bool s_smooth_scrolling = false; - -static LRUCache> s_texture_cache(128, true); -static std::shared_ptr s_placeholder_texture; -static std::atomic_bool s_texture_load_thread_quit{false}; -static std::mutex s_texture_load_mutex; -static std::condition_variable s_texture_load_cv; -static std::deque s_texture_load_queue; -static std::deque> s_texture_upload_queue; -static std::thread s_texture_load_thread; - -static SmallString s_fullscreen_footer_text; -static SmallString s_last_fullscreen_footer_text; -static float s_fullscreen_text_change_time; - -static bool s_choice_dialog_open = false; -static bool s_choice_dialog_checkable = false; -static std::string s_choice_dialog_title; -static ChoiceDialogOptions s_choice_dialog_options; -static ChoiceDialogCallback s_choice_dialog_callback; -static ImGuiID s_enum_choice_button_id = 0; -static s32 s_enum_choice_button_value = 0; -static bool s_enum_choice_button_set = false; - -static bool s_input_dialog_open = false; -static std::string s_input_dialog_title; -static std::string s_input_dialog_message; -static std::string s_input_dialog_caption; -static std::string s_input_dialog_text; -static std::string s_input_dialog_ok_text; -static InputStringDialogCallback s_input_dialog_callback; - -static bool s_message_dialog_open = false; -static std::string s_message_dialog_title; -static std::string s_message_dialog_message; -static std::array s_message_dialog_buttons; -static MessageDialogCallbackVariant s_message_dialog_callback; - -static ImAnimatedVec2 s_menu_button_frame_min_animated; -static ImAnimatedVec2 s_menu_button_frame_max_animated; -static bool s_had_hovered_menu_item = false; -static bool s_has_hovered_menu_item = false; -static bool s_rendered_menu_item_border = false; - namespace { + struct FileSelectorItem { FileSelectorItem() = default; @@ -155,20 +80,7 @@ struct FileSelectorItem std::string full_path; bool is_file; }; -} // namespace -static bool s_file_selector_open = false; -static bool s_file_selector_directory = false; -static std::string s_file_selector_title; -static ImGuiFullscreen::FileSelectorCallback s_file_selector_callback; -static std::string s_file_selector_current_directory; -static std::vector s_file_selector_filters; -static std::vector s_file_selector_items; - -static constexpr float NOTIFICATION_FADE_IN_TIME = 0.2f; -static constexpr float NOTIFICATION_FADE_OUT_TIME = 0.8f; - -namespace { struct Notification { std::string key; @@ -181,16 +93,7 @@ struct Notification float target_y; float last_y; }; -} // namespace -static std::vector s_notifications; - -static std::string s_toast_title; -static std::string s_toast_message; -static Timer::Value s_toast_start_time; -static float s_toast_duration; - -namespace { struct BackgroundProgressDialogData { std::string message; @@ -199,93 +102,167 @@ struct BackgroundProgressDialogData s32 max; s32 value; }; + +struct ALIGN_TO_CACHE_LINE UIState +{ + u32 menu_button_index = 0; + u32 close_button_state = 0; + ImGuiDir has_pending_nav_move = ImGuiDir_None; + FocusResetType focus_reset_queued = FocusResetType::None; + bool light_theme = false; + bool smooth_scrolling = false; + + LRUCache> texture_cache{128, true}; + std::shared_ptr placeholder_texture; + std::atomic_bool texture_load_thread_quit{false}; + std::mutex texture_load_mutex; + std::condition_variable texture_load_cv; + std::deque texture_load_queue; + std::deque> texture_upload_queue; + std::thread texture_load_thread; + + SmallString fullscreen_footer_text; + SmallString last_fullscreen_footer_text; + float fullscreen_text_change_time; + + bool choice_dialog_open = false; + bool choice_dialog_checkable = false; + std::string choice_dialog_title; + ChoiceDialogOptions choice_dialog_options; + ChoiceDialogCallback choice_dialog_callback; + ImGuiID enum_choice_button_id = 0; + s32 enum_choice_button_value = 0; + bool enum_choice_button_set = false; + + bool input_dialog_open = false; + std::string input_dialog_title; + std::string input_dialog_message; + std::string input_dialog_caption; + std::string input_dialog_text; + std::string input_dialog_ok_text; + InputStringDialogCallback input_dialog_callback; + + bool message_dialog_open = false; + std::string message_dialog_title; + std::string message_dialog_message; + std::array message_dialog_buttons; + MessageDialogCallbackVariant message_dialog_callback; + + ImAnimatedVec2 menu_button_frame_min_animated; + ImAnimatedVec2 menu_button_frame_max_animated; + bool had_hovered_menu_item = false; + bool has_hovered_menu_item = false; + bool rendered_menu_item_border = false; + + bool file_selector_open = false; + bool file_selector_directory = false; + std::string file_selector_title; + ImGuiFullscreen::FileSelectorCallback file_selector_callback; + std::string file_selector_current_directory; + std::vector file_selector_filters; + std::vector file_selector_items; + + std::vector notifications; + + std::string toast_title; + std::string toast_message; + Timer::Value toast_start_time; + float toast_duration; + + std::vector background_progress_dialogs; + std::mutex background_progress_lock; +}; + } // namespace -static std::vector s_background_progress_dialogs; -static std::mutex s_background_progress_lock; +static constexpr float NOTIFICATION_FADE_IN_TIME = 0.2f; +static constexpr float NOTIFICATION_FADE_OUT_TIME = 0.8f; + +UIStyles UIStyle = {}; +static UIState s_state; + } // namespace ImGuiFullscreen void ImGuiFullscreen::SetFonts(ImFont* medium_font, ImFont* large_font) { - g_medium_font = medium_font; - g_large_font = large_font; + UIStyle.MediumFont = medium_font; + UIStyle.LargeFont = large_font; } bool ImGuiFullscreen::Initialize(const char* placeholder_image_path) { - s_focus_reset_queued = FocusResetType::ViewChanged; - s_close_button_state = 0; + s_state.focus_reset_queued = FocusResetType::ViewChanged; + s_state.close_button_state = 0; - s_placeholder_texture = LoadTexture(placeholder_image_path); - if (!s_placeholder_texture) + s_state.placeholder_texture = LoadTexture(placeholder_image_path); + if (!s_state.placeholder_texture) { ERROR_LOG("Missing placeholder texture '{}', cannot continue", placeholder_image_path); return false; } - s_texture_load_thread_quit.store(false, std::memory_order_release); - s_texture_load_thread = std::thread(TextureLoaderThread); + s_state.texture_load_thread_quit.store(false, std::memory_order_release); + s_state.texture_load_thread = std::thread(TextureLoaderThread); ResetMenuButtonFrame(); return true; } void ImGuiFullscreen::Shutdown() { - if (s_texture_load_thread.joinable()) + if (s_state.texture_load_thread.joinable()) { { - std::unique_lock lock(s_texture_load_mutex); - s_texture_load_thread_quit.store(true, std::memory_order_release); - s_texture_load_cv.notify_one(); + std::unique_lock lock(s_state.texture_load_mutex); + s_state.texture_load_thread_quit.store(true, std::memory_order_release); + s_state.texture_load_cv.notify_one(); } - s_texture_load_thread.join(); + s_state.texture_load_thread.join(); } - s_texture_upload_queue.clear(); - s_placeholder_texture.reset(); - g_standard_font = nullptr; - g_medium_font = nullptr; - g_large_font = nullptr; + s_state.texture_upload_queue.clear(); + s_state.placeholder_texture.reset(); + UIStyle.MediumFont = nullptr; + UIStyle.LargeFont = nullptr; - s_texture_cache.Clear(); + s_state.texture_cache.Clear(); - s_notifications.clear(); - s_background_progress_dialogs.clear(); - s_fullscreen_footer_text.clear(); - s_last_fullscreen_footer_text.clear(); - s_fullscreen_text_change_time = 0.0f; + s_state.notifications.clear(); + s_state.background_progress_dialogs.clear(); + s_state.fullscreen_footer_text.clear(); + s_state.last_fullscreen_footer_text.clear(); + s_state.fullscreen_text_change_time = 0.0f; CloseInputDialog(); CloseMessageDialog(); - s_choice_dialog_open = false; - s_choice_dialog_checkable = false; - s_choice_dialog_title = {}; - s_choice_dialog_options.clear(); - s_choice_dialog_callback = {}; - s_enum_choice_button_id = 0; - s_enum_choice_button_value = 0; - s_enum_choice_button_set = false; - s_file_selector_open = false; - s_file_selector_directory = false; - s_file_selector_title = {}; - s_file_selector_callback = {}; - s_file_selector_current_directory = {}; - s_file_selector_filters.clear(); - s_file_selector_items.clear(); - s_message_dialog_open = false; - s_message_dialog_title = {}; - s_message_dialog_message = {}; - s_message_dialog_buttons = {}; - s_message_dialog_callback = {}; + s_state.choice_dialog_open = false; + s_state.choice_dialog_checkable = false; + s_state.choice_dialog_title = {}; + s_state.choice_dialog_options.clear(); + s_state.choice_dialog_callback = {}; + s_state.enum_choice_button_id = 0; + s_state.enum_choice_button_value = 0; + s_state.enum_choice_button_set = false; + s_state.file_selector_open = false; + s_state.file_selector_directory = false; + s_state.file_selector_title = {}; + s_state.file_selector_callback = {}; + s_state.file_selector_current_directory = {}; + s_state.file_selector_filters.clear(); + s_state.file_selector_items.clear(); + s_state.message_dialog_open = false; + s_state.message_dialog_title = {}; + s_state.message_dialog_message = {}; + s_state.message_dialog_buttons = {}; + s_state.message_dialog_callback = {}; } void ImGuiFullscreen::SetSmoothScrolling(bool enabled) { - s_smooth_scrolling = enabled; + s_state.smooth_scrolling = enabled; } const std::shared_ptr& ImGuiFullscreen::GetPlaceholderTexture() { - return s_placeholder_texture; + return s_state.placeholder_texture; } std::optional ImGuiFullscreen::LoadTextureImage(std::string_view path, u32 svg_width, u32 svg_height) @@ -379,16 +356,16 @@ std::shared_ptr ImGuiFullscreen::LoadTexture(std::string_view path, return ret; } - return s_placeholder_texture; + return s_state.placeholder_texture; } GPUTexture* ImGuiFullscreen::GetCachedTexture(std::string_view name) { - std::shared_ptr* tex_ptr = s_texture_cache.Lookup(name); + std::shared_ptr* tex_ptr = s_state.texture_cache.Lookup(name); if (!tex_ptr) { std::shared_ptr tex = LoadTexture(name); - tex_ptr = s_texture_cache.Insert(std::string(name), std::move(tex)); + tex_ptr = s_state.texture_cache.Insert(std::string(name), std::move(tex)); } return tex_ptr->get(); @@ -400,11 +377,11 @@ GPUTexture* ImGuiFullscreen::GetCachedTexture(std::string_view name, u32 svg_wid svg_height = static_cast(std::ceil(LayoutScale(static_cast(svg_height)))); const SmallString wh_name = SmallString::from_format("{}#{}x{}", name, svg_width, svg_height); - std::shared_ptr* tex_ptr = s_texture_cache.Lookup(wh_name.view()); + std::shared_ptr* tex_ptr = s_state.texture_cache.Lookup(wh_name.view()); if (!tex_ptr) { std::shared_ptr tex = LoadTexture(name, svg_width, svg_height); - tex_ptr = s_texture_cache.Insert(std::string(wh_name.view()), std::move(tex)); + tex_ptr = s_state.texture_cache.Insert(std::string(wh_name.view()), std::move(tex)); } return tex_ptr->get(); @@ -412,16 +389,16 @@ GPUTexture* ImGuiFullscreen::GetCachedTexture(std::string_view name, u32 svg_wid GPUTexture* ImGuiFullscreen::GetCachedTextureAsync(std::string_view name) { - std::shared_ptr* tex_ptr = s_texture_cache.Lookup(name); + std::shared_ptr* tex_ptr = s_state.texture_cache.Lookup(name); if (!tex_ptr) { // insert the placeholder - tex_ptr = s_texture_cache.Insert(std::string(name), s_placeholder_texture); + tex_ptr = s_state.texture_cache.Insert(std::string(name), s_state.placeholder_texture); // queue the actual load - std::unique_lock lock(s_texture_load_mutex); - s_texture_load_queue.emplace_back(name); - s_texture_load_cv.notify_one(); + std::unique_lock lock(s_state.texture_load_mutex); + s_state.texture_load_queue.emplace_back(name); + s_state.texture_load_cv.notify_one(); } return tex_ptr->get(); @@ -429,21 +406,21 @@ GPUTexture* ImGuiFullscreen::GetCachedTextureAsync(std::string_view name) bool ImGuiFullscreen::InvalidateCachedTexture(const std::string& path) { - return s_texture_cache.Remove(path); + return s_state.texture_cache.Remove(path); } void ImGuiFullscreen::UploadAsyncTextures() { - std::unique_lock lock(s_texture_load_mutex); - while (!s_texture_upload_queue.empty()) + std::unique_lock lock(s_state.texture_load_mutex); + while (!s_state.texture_upload_queue.empty()) { - std::pair it(std::move(s_texture_upload_queue.front())); - s_texture_upload_queue.pop_front(); + std::pair it(std::move(s_state.texture_upload_queue.front())); + s_state.texture_upload_queue.pop_front(); lock.unlock(); std::shared_ptr tex = UploadTexture(it.first.c_str(), it.second); if (tex) - s_texture_cache.Insert(std::move(it.first), std::move(tex)); + s_state.texture_cache.Insert(std::move(it.first), std::move(tex)); lock.lock(); } @@ -453,21 +430,21 @@ void ImGuiFullscreen::TextureLoaderThread() { Threading::SetNameOfCurrentThread("ImGuiFullscreen Texture Loader"); - std::unique_lock lock(s_texture_load_mutex); + std::unique_lock lock(s_state.texture_load_mutex); for (;;) { - s_texture_load_cv.wait(lock, []() { - return (s_texture_load_thread_quit.load(std::memory_order_acquire) || !s_texture_load_queue.empty()); + s_state.texture_load_cv.wait(lock, []() { + return (s_state.texture_load_thread_quit.load(std::memory_order_acquire) || !s_state.texture_load_queue.empty()); }); - if (s_texture_load_thread_quit.load(std::memory_order_acquire)) + if (s_state.texture_load_thread_quit.load(std::memory_order_acquire)) break; - while (!s_texture_load_queue.empty()) + while (!s_state.texture_load_queue.empty()) { - std::string path(std::move(s_texture_load_queue.front())); - s_texture_load_queue.pop_front(); + std::string path(std::move(s_state.texture_load_queue.front())); + s_state.texture_load_queue.pop_front(); lock.unlock(); std::optional image(LoadTextureImage(path.c_str(), 0, 0)); @@ -475,11 +452,11 @@ void ImGuiFullscreen::TextureLoaderThread() // don't bother queuing back if it doesn't exist if (image) - s_texture_upload_queue.emplace_back(std::move(path), std::move(image.value())); + s_state.texture_upload_queue.emplace_back(std::move(path), std::move(image.value())); } } - s_texture_load_queue.clear(); + s_state.texture_load_queue.clear(); } bool ImGuiFullscreen::UpdateLayoutScale() @@ -490,26 +467,26 @@ bool ImGuiFullscreen::UpdateLayoutScale() const float screen_width = io.DisplaySize.x; const float screen_height = io.DisplaySize.y; const float screen_ratio = screen_width / screen_height; - const float old_scale = g_layout_scale; + const float old_scale = UIStyle.LayoutScale; if (screen_ratio > LAYOUT_RATIO) { // screen is wider, use height, pad width - g_layout_scale = std::max(screen_height / LAYOUT_SCREEN_HEIGHT, 0.1f); - g_layout_padding_top = 0.0f; - g_layout_padding_left = (screen_width - (LAYOUT_SCREEN_WIDTH * g_layout_scale)) / 2.0f; + UIStyle.LayoutScale = std::max(screen_height / LAYOUT_SCREEN_HEIGHT, 0.1f); + UIStyle.LayoutPaddingTop = 0.0f; + UIStyle.LayoutPaddingLeft = (screen_width - (LAYOUT_SCREEN_WIDTH * UIStyle.LayoutScale)) / 2.0f; } else { // screen is taller, use width, pad height - g_layout_scale = std::max(screen_width / LAYOUT_SCREEN_WIDTH, 0.1f); - g_layout_padding_top = (screen_height - (LAYOUT_SCREEN_HEIGHT * g_layout_scale)) / 2.0f; - g_layout_padding_left = 0.0f; + UIStyle.LayoutScale = std::max(screen_width / LAYOUT_SCREEN_WIDTH, 0.1f); + UIStyle.LayoutPaddingTop = (screen_height - (LAYOUT_SCREEN_HEIGHT * UIStyle.LayoutScale)) / 2.0f; + UIStyle.LayoutPaddingLeft = 0.0f; } - g_rcp_layout_scale = 1.0f / g_layout_scale; + UIStyle.RcpLayoutScale = 1.0f / UIStyle.LayoutScale; - return g_layout_scale != old_scale; + return UIStyle.LayoutScale != old_scale; } ImRect ImGuiFullscreen::CenterImage(const ImVec2& fit_size, const ImVec2& image_size) @@ -549,7 +526,7 @@ void ImGuiFullscreen::BeginLayout() { // we evict from the texture cache at the start of the frame, in case we go over mid-frame, // we need to keep all those textures alive until the end of the frame - s_texture_cache.ManualEvict(); + s_state.texture_cache.ManualEvict(); PushResetLayout(); } @@ -574,10 +551,10 @@ void ImGuiFullscreen::EndLayout() PopResetLayout(); - s_fullscreen_footer_text.clear(); + s_state.fullscreen_footer_text.clear(); - s_rendered_menu_item_border = false; - s_had_hovered_menu_item = std::exchange(s_has_hovered_menu_item, false); + s_state.rendered_menu_item_border = false; + s_state.had_hovered_menu_item = std::exchange(s_state.has_hovered_menu_item, false); } void ImGuiFullscreen::PushResetLayout() @@ -594,17 +571,17 @@ void ImGuiFullscreen::PushResetLayout() ImGui::PushStyleVar(ImGuiStyleVar_ScrollbarRounding, 0.0f); ImGui::PushStyleVar(ImGuiStyleVar_GrabMinSize, LayoutScale(10.0f)); ImGui::PushStyleVar(ImGuiStyleVar_TabRounding, LayoutScale(4.0f)); - ImGui::PushStyleColor(ImGuiCol_Text, UISecondaryTextColor); - ImGui::PushStyleColor(ImGuiCol_TextDisabled, UIDisabledColor); - ImGui::PushStyleColor(ImGuiCol_Button, UISecondaryColor); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, UIBackgroundColor); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, UIBackgroundHighlightColor); - ImGui::PushStyleColor(ImGuiCol_Border, UIBackgroundLineColor); - ImGui::PushStyleColor(ImGuiCol_ScrollbarBg, UIBackgroundColor); - ImGui::PushStyleColor(ImGuiCol_ScrollbarGrab, UIPrimaryColor); - ImGui::PushStyleColor(ImGuiCol_ScrollbarGrabHovered, UIPrimaryLightColor); - ImGui::PushStyleColor(ImGuiCol_ScrollbarGrabActive, UIPrimaryDarkColor); - ImGui::PushStyleColor(ImGuiCol_PopupBg, UIPopupBackgroundColor); + ImGui::PushStyleColor(ImGuiCol_Text, UIStyle.SecondaryTextColor); + ImGui::PushStyleColor(ImGuiCol_TextDisabled, UIStyle.DisabledColor); + ImGui::PushStyleColor(ImGuiCol_Button, UIStyle.SecondaryColor); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, UIStyle.BackgroundColor); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, UIStyle.BackgroundHighlight); + ImGui::PushStyleColor(ImGuiCol_Border, UIStyle.BackgroundLineColor); + ImGui::PushStyleColor(ImGuiCol_ScrollbarBg, UIStyle.BackgroundColor); + ImGui::PushStyleColor(ImGuiCol_ScrollbarGrab, UIStyle.PrimaryColor); + ImGui::PushStyleColor(ImGuiCol_ScrollbarGrabHovered, UIStyle.PrimaryLightColor); + ImGui::PushStyleColor(ImGuiCol_ScrollbarGrabActive, UIStyle.PrimaryDarkColor); + ImGui::PushStyleColor(ImGuiCol_PopupBg, UIStyle.PopupBackgroundColor); } void ImGuiFullscreen::PopResetLayout() @@ -615,13 +592,13 @@ void ImGuiFullscreen::PopResetLayout() void ImGuiFullscreen::QueueResetFocus(FocusResetType type) { - s_focus_reset_queued = type; - s_close_button_state = 0; + s_state.focus_reset_queued = type; + s_state.close_button_state = 0; } bool ImGuiFullscreen::ResetFocusHere() { - if (s_focus_reset_queued == FocusResetType::None) + if (s_state.focus_reset_queued == FocusResetType::None) return false; // don't take focus from dialogs @@ -635,16 +612,16 @@ bool ImGuiFullscreen::ResetFocusHere() if (!GImGui->NavDisableHighlight && GImGui->NavDisableMouseHover) { window->Appearing = true; - s_has_hovered_menu_item = s_had_hovered_menu_item; + s_state.has_hovered_menu_item = s_state.had_hovered_menu_item; } ImGui::SetWindowFocus(); // If this is a popup closing, we don't want to reset the current nav item, since we were presumably opened by one. - if (s_focus_reset_queued != FocusResetType::PopupClosed) + if (s_state.focus_reset_queued != FocusResetType::PopupClosed) ImGui::NavInitWindow(window, true); - s_focus_reset_queued = FocusResetType::None; + s_state.focus_reset_queued = FocusResetType::None; // only do the active selection magic when we're using keyboard/gamepad return (GImGui->NavInputSource == ImGuiInputSource_Keyboard || GImGui->NavInputSource == ImGuiInputSource_Gamepad); @@ -652,17 +629,18 @@ bool ImGuiFullscreen::ResetFocusHere() bool ImGuiFullscreen::IsFocusResetQueued() { - return (s_focus_reset_queued != FocusResetType::None); + return (s_state.focus_reset_queued != FocusResetType::None); } bool ImGuiFullscreen::IsFocusResetFromWindowChange() { - return (s_focus_reset_queued != FocusResetType::None && s_focus_reset_queued != FocusResetType::PopupClosed); + return (s_state.focus_reset_queued != FocusResetType::None && + s_state.focus_reset_queued != FocusResetType::PopupClosed); } ImGuiFullscreen::FocusResetType ImGuiFullscreen::GetQueuedFocusResetType() { - return s_focus_reset_queued; + return s_state.focus_reset_queued; } void ImGuiFullscreen::ForceKeyNavEnabled() @@ -683,37 +661,37 @@ bool ImGuiFullscreen::WantsToCloseMenu() ImGuiContext& g = *GImGui; // Wait for the Close button to be released, THEN pressed - if (s_close_button_state == 0) + if (s_state.close_button_state == 0) { if (ImGui::IsKeyPressed(ImGuiKey_Escape, false)) - s_close_button_state = 1; + s_state.close_button_state = 1; else if (ImGui::IsKeyPressed(ImGuiKey_NavGamepadCancel, false)) - s_close_button_state = 2; + s_state.close_button_state = 2; } - else if ((s_close_button_state == 1 && ImGui::IsKeyReleased(ImGuiKey_Escape)) || - (s_close_button_state == 2 && ImGui::IsKeyReleased(ImGuiKey_NavGamepadCancel))) + else if ((s_state.close_button_state == 1 && ImGui::IsKeyReleased(ImGuiKey_Escape)) || + (s_state.close_button_state == 2 && ImGui::IsKeyReleased(ImGuiKey_NavGamepadCancel))) { - s_close_button_state = 3; + s_state.close_button_state = 3; } - return s_close_button_state > 1; + return s_state.close_button_state > 1; } void ImGuiFullscreen::ResetCloseMenuIfNeeded() { // If s_close_button_state reached the "Released" state, reset it after the tick - if (s_close_button_state > 1) + if (s_state.close_button_state > 1) { - s_close_button_state = 0; + s_state.close_button_state = 0; } } void ImGuiFullscreen::PushPrimaryColor() { - ImGui::PushStyleColor(ImGuiCol_Text, UIPrimaryTextColor); - ImGui::PushStyleColor(ImGuiCol_Button, UIPrimaryDarkColor); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, UIPrimaryColor); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, UIPrimaryLightColor); - ImGui::PushStyleColor(ImGuiCol_Border, UIPrimaryLightColor); + ImGui::PushStyleColor(ImGuiCol_Text, UIStyle.PrimaryTextColor); + ImGui::PushStyleColor(ImGuiCol_Button, UIStyle.PrimaryDarkColor); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, UIStyle.PrimaryColor); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, UIStyle.PrimaryLightColor); + ImGui::PushStyleColor(ImGuiCol_Border, UIStyle.PrimaryLightColor); } void ImGuiFullscreen::PopPrimaryColor() @@ -723,7 +701,7 @@ void ImGuiFullscreen::PopPrimaryColor() bool ImGuiFullscreen::BeginFullscreenColumns(const char* title, float pos_y, bool expand_to_screen_width, bool footer) { - ImGui::SetNextWindowPos(ImVec2(expand_to_screen_width ? 0.0f : g_layout_padding_left, pos_y)); + ImGui::SetNextWindowPos(ImVec2(expand_to_screen_width ? 0.0f : UIStyle.LayoutPaddingLeft, pos_y)); ImGui::SetNextWindowSize( ImVec2(expand_to_screen_width ? ImGui::GetIO().DisplaySize.x : LayoutScale(LAYOUT_SCREEN_WIDTH), ImGui::GetIO().DisplaySize.y - pos_y - (footer ? LayoutScale(LAYOUT_FOOTER_HEIGHT) : 0.0f))); @@ -735,7 +713,7 @@ bool ImGuiFullscreen::BeginFullscreenColumns(const char* title, float pos_y, boo bool clipped; if (title) { - ImGui::PushFont(g_large_font); + ImGui::PushFont(UIStyle.LargeFont); clipped = ImGui::Begin(title, nullptr, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize); ImGui::PopFont(); } @@ -790,7 +768,7 @@ bool ImGuiFullscreen::BeginFullscreenWindow(float left, float top, float width, if (top < 0.0f) top = (LAYOUT_SCREEN_HEIGHT - height) * -top; - const ImVec2 pos(ImVec2(LayoutScale(left) + g_layout_padding_left, LayoutScale(top) + g_layout_padding_top)); + const ImVec2 pos(ImVec2(LayoutScale(left) + UIStyle.LayoutPaddingLeft, LayoutScale(top) + UIStyle.LayoutPaddingTop)); const ImVec2 size(LayoutScale(ImVec2(width, height))); return BeginFullscreenWindow(pos, size, name, background, rounding, padding, flags); } @@ -807,7 +785,7 @@ bool ImGuiFullscreen::BeginFullscreenWindow(const ImVec2& position, const ImVec2 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, LayoutScale(padding)); ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, LayoutScale(rounding)); - ImGui::PushStyleVar(ImGuiStyleVar_ScrollSmooth, s_smooth_scrolling ? SMOOTH_SCROLLING_SPEED : 1.0f); + ImGui::PushStyleVar(ImGuiStyleVar_ScrollSmooth, s_state.smooth_scrolling ? SMOOTH_SCROLLING_SPEED : 1.0f); return ImGui::Begin(name, nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | @@ -843,20 +821,20 @@ void ImGuiFullscreen::CreateFooterTextString(SmallStringBase& dest, void ImGuiFullscreen::SetFullscreenFooterText(std::string_view text) { - s_fullscreen_footer_text.assign(text); + s_state.fullscreen_footer_text.assign(text); } void ImGuiFullscreen::SetFullscreenFooterText(std::span> items) { - CreateFooterTextString(s_fullscreen_footer_text, items); + CreateFooterTextString(s_state.fullscreen_footer_text, items); } void ImGuiFullscreen::DrawFullscreenFooter() { const ImGuiIO& io = ImGui::GetIO(); - if (s_fullscreen_footer_text.empty()) + if (s_state.fullscreen_footer_text.empty()) { - s_last_fullscreen_footer_text.clear(); + s_state.last_fullscreen_footer_text.clear(); return; } @@ -864,60 +842,63 @@ void ImGuiFullscreen::DrawFullscreenFooter() const float height = LayoutScale(LAYOUT_FOOTER_HEIGHT); ImDrawList* dl = ImGui::GetForegroundDrawList(); - dl->AddRectFilled(ImVec2(0.0f, io.DisplaySize.y - height), io.DisplaySize, ImGui::GetColorU32(UIPrimaryColor), 0.0f); + dl->AddRectFilled(ImVec2(0.0f, io.DisplaySize.y - height), io.DisplaySize, ImGui::GetColorU32(UIStyle.PrimaryColor), + 0.0f); - ImFont* const font = g_medium_font; + ImFont* const font = UIStyle.MediumFont; const float max_width = io.DisplaySize.x - padding * 2.0f; float prev_opacity = 0.0f; - if (!s_last_fullscreen_footer_text.empty() && s_fullscreen_footer_text != s_last_fullscreen_footer_text) + if (!s_state.last_fullscreen_footer_text.empty() && + s_state.fullscreen_footer_text != s_state.last_fullscreen_footer_text) { - if (s_fullscreen_text_change_time == 0.0f) - s_fullscreen_text_change_time = 0.15f; + if (s_state.fullscreen_text_change_time == 0.0f) + s_state.fullscreen_text_change_time = 0.15f; else - s_fullscreen_text_change_time = std::max(s_fullscreen_text_change_time - io.DeltaTime, 0.0f); + s_state.fullscreen_text_change_time = std::max(s_state.fullscreen_text_change_time - io.DeltaTime, 0.0f); - if (s_fullscreen_text_change_time == 0.0f) - s_last_fullscreen_footer_text = s_fullscreen_footer_text; + if (s_state.fullscreen_text_change_time == 0.0f) + s_state.last_fullscreen_footer_text = s_state.fullscreen_footer_text; - prev_opacity = s_fullscreen_text_change_time * (1.0f / 0.15f); + prev_opacity = s_state.fullscreen_text_change_time * (1.0f / 0.15f); if (prev_opacity > 0.0f) { const ImVec2 text_size = - font->CalcTextSizeA(font->FontSize, max_width, 0.0f, s_last_fullscreen_footer_text.c_str(), - s_last_fullscreen_footer_text.end_ptr()); - dl->AddText( - font, font->FontSize, - ImVec2(io.DisplaySize.x - padding * 2.0f - text_size.x, io.DisplaySize.y - font->FontSize - padding), - ImGui::GetColorU32(ImVec4(UIPrimaryTextColor.x, UIPrimaryTextColor.y, UIPrimaryTextColor.z, prev_opacity)), - s_last_fullscreen_footer_text.c_str(), s_last_fullscreen_footer_text.end_ptr()); + font->CalcTextSizeA(font->FontSize, max_width, 0.0f, s_state.last_fullscreen_footer_text.c_str(), + s_state.last_fullscreen_footer_text.end_ptr()); + dl->AddText(font, font->FontSize, + ImVec2(io.DisplaySize.x - padding * 2.0f - text_size.x, io.DisplaySize.y - font->FontSize - padding), + ImGui::GetColorU32(ImVec4(UIStyle.PrimaryTextColor.x, UIStyle.PrimaryTextColor.y, + UIStyle.PrimaryTextColor.z, prev_opacity)), + s_state.last_fullscreen_footer_text.c_str(), s_state.last_fullscreen_footer_text.end_ptr()); } } - else if (s_last_fullscreen_footer_text.empty()) + else if (s_state.last_fullscreen_footer_text.empty()) { - s_last_fullscreen_footer_text = s_fullscreen_footer_text; + s_state.last_fullscreen_footer_text = s_state.fullscreen_footer_text; } if (prev_opacity < 1.0f) { - const ImVec2 text_size = font->CalcTextSizeA(font->FontSize, max_width, 0.0f, s_fullscreen_footer_text.c_str(), - s_fullscreen_footer_text.end_ptr()); - dl->AddText( - font, font->FontSize, - ImVec2(io.DisplaySize.x - padding * 2.0f - text_size.x, io.DisplaySize.y - font->FontSize - padding), - ImGui::GetColorU32(ImVec4(UIPrimaryTextColor.x, UIPrimaryTextColor.y, UIPrimaryTextColor.z, 1.0f - prev_opacity)), - s_fullscreen_footer_text.c_str(), s_fullscreen_footer_text.end_ptr()); + const ImVec2 text_size = + font->CalcTextSizeA(font->FontSize, max_width, 0.0f, s_state.fullscreen_footer_text.c_str(), + s_state.fullscreen_footer_text.end_ptr()); + dl->AddText(font, font->FontSize, + ImVec2(io.DisplaySize.x - padding * 2.0f - text_size.x, io.DisplaySize.y - font->FontSize - padding), + ImGui::GetColorU32(ImVec4(UIStyle.PrimaryTextColor.x, UIStyle.PrimaryTextColor.y, + UIStyle.PrimaryTextColor.z, 1.0f - prev_opacity)), + s_state.fullscreen_footer_text.c_str(), s_state.fullscreen_footer_text.end_ptr()); } } void ImGuiFullscreen::PrerenderMenuButtonBorder() { - if (!s_had_hovered_menu_item) + if (!s_state.had_hovered_menu_item) return; // updating might finish the animation - const ImVec2& min = s_menu_button_frame_min_animated.UpdateAndGetValue(); - const ImVec2& max = s_menu_button_frame_max_animated.UpdateAndGetValue(); + const ImVec2& min = s_state.menu_button_frame_min_animated.UpdateAndGetValue(); + const ImVec2& max = s_state.menu_button_frame_max_animated.UpdateAndGetValue(); const ImU32 col = ImGui::GetColorU32(ImGuiCol_ButtonHovered); const float t = static_cast(std::min(std::abs(std::sin(ImGui::GetTime() * 0.75) * 1.1), 1.0)); @@ -927,25 +908,25 @@ void ImGuiFullscreen::PrerenderMenuButtonBorder() ImGui::PopStyleColor(); - s_rendered_menu_item_border = true; + s_state.rendered_menu_item_border = true; } void ImGuiFullscreen::BeginMenuButtons(u32 num_items, float y_align, float x_padding, float y_padding, float item_height) { - s_menu_button_index = 0; + s_state.menu_button_index = 0; // If we're scrolling up and down, it's possible that the first menu item won't be enabled. // If so, track when the scroll happens, and if we moved to a new ID. If not, scroll the parent window. if (GImGui->NavMoveDir != ImGuiDir_None) { - s_has_pending_nav_move = GImGui->NavMoveDir; + s_state.has_pending_nav_move = GImGui->NavMoveDir; } - else if (s_has_pending_nav_move != ImGuiDir_None) + else if (s_state.has_pending_nav_move != ImGuiDir_None) { if (GImGui->NavJustMovedToId == 0) { - switch (s_has_pending_nav_move) + switch (s_state.has_pending_nav_move) { case ImGuiDir_Up: ImGui::SetScrollY(std::max(ImGui::GetScrollY() - item_height, 0.0f)); @@ -958,7 +939,7 @@ void ImGuiFullscreen::BeginMenuButtons(u32 num_items, float y_align, float x_pad } } - s_has_pending_nav_move = ImGuiDir_None; + s_state.has_pending_nav_move = ImGuiDir_None; } ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, LayoutScale(x_padding, y_padding)); @@ -988,18 +969,18 @@ void ImGuiFullscreen::DrawWindowTitle(const char* title) ImGuiWindow* window = ImGui::GetCurrentWindow(); const ImVec2 pos(window->DC.CursorPos + LayoutScale(LAYOUT_MENU_BUTTON_X_PADDING, LAYOUT_MENU_BUTTON_Y_PADDING)); const ImVec2 size(window->WorkRect.GetWidth() - (LayoutScale(LAYOUT_MENU_BUTTON_X_PADDING) * 2.0f), - g_large_font->FontSize + LayoutScale(LAYOUT_MENU_BUTTON_Y_PADDING) * 2.0f); + UIStyle.LargeFont->FontSize + LayoutScale(LAYOUT_MENU_BUTTON_Y_PADDING) * 2.0f); const ImRect rect(pos, pos + size); ImGui::ItemSize(size); if (!ImGui::ItemAdd(rect, window->GetID("window_title"))) return; - ImGui::PushFont(g_large_font); + ImGui::PushFont(UIStyle.LargeFont); ImGui::RenderTextClipped(rect.Min, rect.Max, title, nullptr, nullptr, ImVec2(0.0f, 0.0f), &rect); ImGui::PopFont(); - const ImVec2 line_start(pos.x, pos.y + g_large_font->FontSize + LayoutScale(LAYOUT_MENU_BUTTON_Y_PADDING)); + const ImVec2 line_start(pos.x, pos.y + UIStyle.LargeFont->FontSize + LayoutScale(LAYOUT_MENU_BUTTON_Y_PADDING)); const ImVec2 line_end(pos.x + size.x, line_start.y); const float line_thickness = LayoutScale(1.0f); ImDrawList* dl = ImGui::GetWindowDrawList(); @@ -1088,37 +1069,37 @@ void ImGuiFullscreen::DrawMenuButtonFrame(const ImVec2& p_min, const ImVec2& p_m ImVec2 frame_max = p_max; const ImGuiIO& io = ImGui::GetIO(); - if (s_smooth_scrolling && io.NavVisible) + if (s_state.smooth_scrolling && io.NavVisible) { - if (!s_had_hovered_menu_item || io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f) + if (!s_state.had_hovered_menu_item || io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f) { - s_menu_button_frame_min_animated.Reset(frame_min); - s_menu_button_frame_max_animated.Reset(frame_max); - s_has_hovered_menu_item = true; + s_state.menu_button_frame_min_animated.Reset(frame_min); + s_state.menu_button_frame_max_animated.Reset(frame_max); + s_state.has_hovered_menu_item = true; } else { - if (frame_min.x != s_menu_button_frame_min_animated.GetEndValue().x || - frame_min.y != s_menu_button_frame_min_animated.GetEndValue().y) + if (frame_min.x != s_state.menu_button_frame_min_animated.GetEndValue().x || + frame_min.y != s_state.menu_button_frame_min_animated.GetEndValue().y) { - s_menu_button_frame_min_animated.Start(s_menu_button_frame_min_animated.GetCurrentValue(), frame_min, - MENU_BACKGROUND_ANIMATION_TIME); + s_state.menu_button_frame_min_animated.Start(s_state.menu_button_frame_min_animated.GetCurrentValue(), + frame_min, MENU_BACKGROUND_ANIMATION_TIME); } - if (frame_max.x != s_menu_button_frame_max_animated.GetEndValue().x || - frame_max.y != s_menu_button_frame_max_animated.GetEndValue().y) + if (frame_max.x != s_state.menu_button_frame_max_animated.GetEndValue().x || + frame_max.y != s_state.menu_button_frame_max_animated.GetEndValue().y) { - s_menu_button_frame_max_animated.Start(s_menu_button_frame_max_animated.GetCurrentValue(), frame_max, - MENU_BACKGROUND_ANIMATION_TIME); + s_state.menu_button_frame_max_animated.Start(s_state.menu_button_frame_max_animated.GetCurrentValue(), + frame_max, MENU_BACKGROUND_ANIMATION_TIME); } - frame_min = s_menu_button_frame_min_animated.UpdateAndGetValue(); - frame_max = s_menu_button_frame_max_animated.UpdateAndGetValue(); - s_has_hovered_menu_item = true; + frame_min = s_state.menu_button_frame_min_animated.UpdateAndGetValue(); + frame_max = s_state.menu_button_frame_max_animated.UpdateAndGetValue(); + s_state.has_hovered_menu_item = true; } } - if (!s_rendered_menu_item_border) + if (!s_state.rendered_menu_item_border) { - s_rendered_menu_item_border = true; + s_state.rendered_menu_item_border = true; ImGui::RenderFrame(frame_min, frame_max, fill_col, border, rounding); } } @@ -1136,8 +1117,8 @@ bool ImGuiFullscreen::MenuButtonFrame(const char* str_id, bool enabled, float he void ImGuiFullscreen::ResetMenuButtonFrame() { - s_had_hovered_menu_item = false; - s_has_hovered_menu_item = false; + s_state.had_hovered_menu_item = false; + s_state.has_hovered_menu_item = false; } void ImGuiFullscreen::MenuHeading(const char* title, bool draw_line /*= true*/) @@ -1152,14 +1133,14 @@ void ImGuiFullscreen::MenuHeading(const char* title, bool draw_line /*= true*/) return; ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(ImGuiCol_TextDisabled)); - ImGui::PushFont(g_large_font); + ImGui::PushFont(UIStyle.LargeFont); ImGui::RenderTextClipped(bb.Min, bb.Max, title, nullptr, nullptr, ImVec2(0.0f, 0.0f), &bb); ImGui::PopFont(); ImGui::PopStyleColor(); if (draw_line) { - const ImVec2 line_start(bb.Min.x, bb.Min.y + g_large_font->FontSize + line_padding); + const ImVec2 line_start(bb.Min.x, bb.Min.y + UIStyle.LargeFont->FontSize + line_padding); const ImVec2 line_end(bb.Max.x, line_start.y); ImGui::GetWindowDrawList()->AddLine(line_start, line_end, ImGui::GetColorU32(ImGuiCol_TextDisabled), line_thickness); @@ -1180,13 +1161,13 @@ bool ImGuiFullscreen::MenuHeadingButton(const char* title, const char* value /*= if (!enabled) ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(ImGuiCol_TextDisabled)); - ImGui::PushFont(g_large_font); + ImGui::PushFont(UIStyle.LargeFont); ImGui::RenderTextClipped(bb.Min, bb.Max, title, nullptr, nullptr, ImVec2(0.0f, 0.0f), &bb); if (value) { const ImVec2 value_size( - g_large_font->CalcTextSizeA(g_large_font->FontSize, std::numeric_limits::max(), 0.0f, value)); + UIStyle.LargeFont->CalcTextSizeA(UIStyle.LargeFont->FontSize, std::numeric_limits::max(), 0.0f, value)); const ImRect value_bb(ImVec2(bb.Max.x - value_size.x, bb.Min.y), ImVec2(bb.Max.x, bb.Max.y)); ImGui::RenderTextClipped(value_bb.Min, value_bb.Max, value, nullptr, nullptr, ImVec2(0.0f, 0.0f), &value_bb); } @@ -1197,7 +1178,7 @@ bool ImGuiFullscreen::MenuHeadingButton(const char* title, const char* value /*= if (draw_line) { - const ImVec2 line_start(bb.Min.x, bb.Min.y + g_large_font->FontSize + line_padding); + const ImVec2 line_start(bb.Min.x, bb.Min.y + UIStyle.LargeFont->FontSize + line_padding); const ImVec2 line_end(bb.Max.x, line_start.y); ImGui::GetWindowDrawList()->AddLine(line_start, line_end, ImGui::GetColorU32(ImGuiCol_TextDisabled), line_thickness); @@ -1230,7 +1211,8 @@ bool ImGuiFullscreen::ActiveButtonWithRightText(const char* title, const char* r const ImVec2 border_size_v = ImVec2(border_size, border_size); ImVec2 pos, size; GetMenuButtonFrameBounds(height, &pos, &size); - ImGui::RenderFrame(pos + border_size_v, pos + size - border_size_v, ImGui::GetColorU32(UIPrimaryColor), false); + ImGui::RenderFrame(pos + border_size_v, pos + size - border_size_v, ImGui::GetColorU32(UIStyle.PrimaryColor), + false); } ImRect bb; @@ -1260,7 +1242,7 @@ bool ImGuiFullscreen::ActiveButtonWithRightText(const char* title, const char* r if (!enabled) ImGui::PopStyleColor(); - s_menu_button_index++; + s_state.menu_button_index++; return pressed; } @@ -1295,7 +1277,7 @@ bool ImGuiFullscreen::MenuButton(const char* title, const char* summary, bool en if (!enabled) ImGui::PopStyleColor(); - s_menu_button_index++; + s_state.menu_button_index++; return pressed; } @@ -1322,7 +1304,7 @@ bool ImGuiFullscreen::MenuButtonWithoutSummary(const char* title, bool enabled, if (!enabled) ImGui::PopStyleColor(); - s_menu_button_index++; + s_state.menu_button_index++; return pressed; } @@ -1363,7 +1345,7 @@ bool ImGuiFullscreen::MenuImageButton(const char* title, const char* summary, Im if (!enabled) ImGui::PopStyleColor(); - s_menu_button_index++; + s_state.menu_button_index++; return pressed; } @@ -1511,12 +1493,12 @@ bool ImGuiFullscreen::ToggleButton(const char* title, const char* summary, bool* ImU32 col_knob; if (!enabled) { - col_bg = ImGui::GetColorU32(UIDisabledColor); + col_bg = ImGui::GetColorU32(UIStyle.DisabledColor); col_knob = IM_COL32(200, 200, 200, 200); } else { - col_bg = ImGui::GetColorU32(ImLerp(HEX_TO_IMVEC4(0x8C8C8C, 0xff), UISecondaryStrongColor, t)); + col_bg = ImGui::GetColorU32(ImLerp(HEX_TO_IMVEC4(0x8C8C8C, 0xff), UIStyle.SecondaryStrongColor, t)); col_knob = IM_COL32(255, 255, 255, 255); } @@ -1526,7 +1508,7 @@ bool ImGuiFullscreen::ToggleButton(const char* title, const char* summary, bool* ImVec2(toggle_pos.x + toggle_radius + t * (toggle_width - toggle_radius * 2.0f), toggle_pos.y + toggle_radius), toggle_radius - 1.5f, col_knob, 32); - s_menu_button_index++; + s_state.menu_button_index++; return pressed; } @@ -1595,10 +1577,10 @@ bool ImGuiFullscreen::ThreeWayToggleButton(const char* title, const char* summar col_bg = IM_COL32(0x75, 0x75, 0x75, 0xff); else if (hovered) col_bg = ImGui::GetColorU32(ImLerp(v->has_value() ? HEX_TO_IMVEC4(0xf05100, 0xff) : HEX_TO_IMVEC4(0x9e9e9e, 0xff), - UISecondaryStrongColor, color_t)); + UIStyle.SecondaryStrongColor, color_t)); else col_bg = ImGui::GetColorU32(ImLerp(v->has_value() ? HEX_TO_IMVEC4(0xc45100, 0xff) : HEX_TO_IMVEC4(0x757575, 0xff), - UISecondaryStrongColor, color_t)); + UIStyle.SecondaryStrongColor, color_t)); dl->AddRectFilled(toggle_pos, ImVec2(toggle_pos.x + toggle_width, toggle_pos.y + toggle_height), col_bg, toggle_height * 0.5f); @@ -1606,7 +1588,7 @@ bool ImGuiFullscreen::ThreeWayToggleButton(const char* title, const char* summar ImVec2(toggle_pos.x + toggle_radius + t * (toggle_width - toggle_radius * 2.0f), toggle_pos.y + toggle_radius), toggle_radius - 1.5f, IM_COL32(255, 255, 255, 255), 32); - s_menu_button_index++; + s_state.menu_button_index++; return pressed; } @@ -1657,7 +1639,7 @@ bool ImGuiFullscreen::RangeButton(const char* title, const char* summary, s32* v ImGui::SetNextWindowPos((ImGui::GetIO().DisplaySize - LayoutScale(0.0f, LAYOUT_FOOTER_HEIGHT)) * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f)); - ImGui::PushFont(g_large_font); + ImGui::PushFont(UIStyle.LargeFont); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, LayoutScale(10.0f)); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, LayoutScale(ImGuiFullscreen::LAYOUT_MENU_BUTTON_X_PADDING, ImGuiFullscreen::LAYOUT_MENU_BUTTON_Y_PADDING)); @@ -1675,7 +1657,8 @@ bool ImGuiFullscreen::RangeButton(const char* title, const char* summary, s32* v changed = ImGui::SliderInt("##value", value, min, max, format, ImGuiSliderFlags_NoInput); ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(10.0f)); - if (MenuButtonWithoutSummary(ok_text, true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, g_large_font, ImVec2(0.5f, 0.0f))) + if (MenuButtonWithoutSummary(ok_text, true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, UIStyle.LargeFont, + ImVec2(0.5f, 0.0f))) ImGui::CloseCurrentPopup(); EndMenuButtons(); @@ -1735,7 +1718,7 @@ bool ImGuiFullscreen::RangeButton(const char* title, const char* summary, float* ImGui::SetNextWindowPos((ImGui::GetIO().DisplaySize - LayoutScale(0.0f, LAYOUT_FOOTER_HEIGHT)) * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f)); - ImGui::PushFont(g_large_font); + ImGui::PushFont(UIStyle.LargeFont); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, LayoutScale(10.0f)); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, LayoutScale(ImGuiFullscreen::LAYOUT_MENU_BUTTON_X_PADDING, ImGuiFullscreen::LAYOUT_MENU_BUTTON_Y_PADDING)); @@ -1752,7 +1735,8 @@ bool ImGuiFullscreen::RangeButton(const char* title, const char* summary, float* changed = ImGui::SliderFloat("##value", value, min, max, format, ImGuiSliderFlags_NoInput); - if (MenuButtonWithoutSummary(ok_text, true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, g_large_font, ImVec2(0.5f, 0.0f))) + if (MenuButtonWithoutSummary(ok_text, true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, UIStyle.LargeFont, + ImVec2(0.5f, 0.0f))) ImGui::CloseCurrentPopup(); EndMenuButtons(); @@ -1813,9 +1797,9 @@ bool ImGuiFullscreen::EnumChoiceButtonImpl(const char* title, const char* summar if (pressed) { - s_enum_choice_button_id = ImGui::GetID(title); - s_enum_choice_button_value = *value_pointer; - s_enum_choice_button_set = false; + s_state.enum_choice_button_id = ImGui::GetID(title); + s_state.enum_choice_button_value = *value_pointer; + s_state.enum_choice_button_set = false; ChoiceDialogOptions options; options.reserve(count); @@ -1824,23 +1808,23 @@ bool ImGuiFullscreen::EnumChoiceButtonImpl(const char* title, const char* summar static_cast(*value_pointer) == i); OpenChoiceDialog(title, false, std::move(options), [](s32 index, const std::string& title, bool checked) { if (index >= 0) - s_enum_choice_button_value = index; + s_state.enum_choice_button_value = index; - s_enum_choice_button_set = true; + s_state.enum_choice_button_set = true; CloseChoiceDialog(); }); } bool changed = false; - if (s_enum_choice_button_set && s_enum_choice_button_id == ImGui::GetID(title)) + if (s_state.enum_choice_button_set && s_state.enum_choice_button_id == ImGui::GetID(title)) { - changed = s_enum_choice_button_value != *value_pointer; + changed = s_state.enum_choice_button_value != *value_pointer; if (changed) - *value_pointer = s_enum_choice_button_value; + *value_pointer = s_state.enum_choice_button_value; - s_enum_choice_button_id = 0; - s_enum_choice_button_value = 0; - s_enum_choice_button_set = false; + s_state.enum_choice_button_id = 0; + s_state.enum_choice_button_value = 0; + s_state.enum_choice_button_set = false; } return changed; @@ -1850,14 +1834,14 @@ void ImGuiFullscreen::DrawShadowedText(ImDrawList* dl, ImFont* font, const ImVec const char* text_end /*= nullptr*/, float wrap_width /*= 0.0f*/) { dl->AddText(font, font->FontSize, pos + LayoutScale(1.0f, 1.0f), - s_light_theme ? IM_COL32(255, 255, 255, 100) : IM_COL32(0, 0, 0, 100), text, text_end, wrap_width); + s_state.light_theme ? IM_COL32(255, 255, 255, 100) : IM_COL32(0, 0, 0, 100), text, text_end, wrap_width); dl->AddText(font, font->FontSize, pos, col, text, text_end, wrap_width); } void ImGuiFullscreen::BeginNavBar(float x_padding /*= LAYOUT_MENU_BUTTON_X_PADDING*/, float y_padding /*= LAYOUT_MENU_BUTTON_Y_PADDING*/) { - s_menu_button_index = 0; + s_state.menu_button_index = 0; ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, LayoutScale(x_padding, y_padding)); ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 0.0f); @@ -1879,7 +1863,7 @@ void ImGuiFullscreen::NavTitle(const char* title, float height /*= LAYOUT_MENU_B if (window->SkipItems) return; - s_menu_button_index++; + s_state.menu_button_index++; const ImVec2 text_size(font->CalcTextSizeA(font->FontSize, std::numeric_limits::max(), 0.0f, title)); const ImVec2 pos(window->DC.CursorPos); @@ -1923,7 +1907,7 @@ bool ImGuiFullscreen::NavButton(const char* title, bool is_active, bool enabled if (window->SkipItems) return false; - s_menu_button_index++; + s_state.menu_button_index++; const ImVec2 text_size(font->CalcTextSizeA(font->FontSize, std::numeric_limits::max(), 0.0f, title)); const ImVec2 pos(window->DC.CursorPos); @@ -1992,7 +1976,7 @@ bool ImGuiFullscreen::NavTab(const char* title, bool is_active, bool enabled /* if (window->SkipItems) return false; - s_menu_button_index++; + s_state.menu_button_index++; const ImVec2 text_size(font->CalcTextSizeA(font->FontSize, std::numeric_limits::max(), 0.0f, title)); const ImVec2 pos(window->DC.CursorPos); @@ -2066,7 +2050,7 @@ bool ImGuiFullscreen::NavTab(const char* title, bool is_active, bool enabled /* bool ImGuiFullscreen::BeginHorizontalMenu(const char* name, const ImVec2& position, const ImVec2& size, u32 num_items) { - s_menu_button_index = 0; + s_state.menu_button_index = 0; const float item_padding = LayoutScale(LAYOUT_HORIZONTAL_MENU_PADDING); const float item_width = LayoutScale(LAYOUT_HORIZONTAL_MENU_ITEM_WIDTH); @@ -2079,7 +2063,7 @@ bool ImGuiFullscreen::BeginHorizontalMenu(const char* name, const ImVec2& positi ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, LayoutScale(1.0f)); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(item_spacing, 0.0f)); - if (!BeginFullscreenWindow(position, size, name, UIBackgroundColor, 0.0f, ImVec2())) + if (!BeginFullscreenWindow(position, size, name, UIStyle.BackgroundColor, 0.0f, ImVec2())) return false; ImGui::SetCursorPos(ImVec2((size.x - menu_width) * 0.5f, (size.y - menu_height) * 0.5f)); @@ -2135,7 +2119,7 @@ bool ImGuiFullscreen::HorizontalMenuItem(GPUTexture* icon, const char* title, co ImDrawList* dl = ImGui::GetWindowDrawList(); dl->AddImage(reinterpret_cast(icon), icon_pos, icon_pos + ImVec2(icon_size, icon_size)); - ImFont* title_font = g_large_font; + ImFont* title_font = UIStyle.LargeFont; const ImVec2 title_size = title_font->CalcTextSizeA(title_font->FontSize, std::numeric_limits::max(), avail_width, title); const ImVec2 title_pos = @@ -2145,7 +2129,7 @@ bool ImGuiFullscreen::HorizontalMenuItem(GPUTexture* icon, const char* title, co dl->AddText(title_font, title_font->FontSize, title_pos, ImGui::GetColorU32(ImGuiCol_Text), title, nullptr, 0.0f, &title_bb); - ImFont* desc_font = g_medium_font; + ImFont* desc_font = UIStyle.MediumFont; const ImVec2 desc_size = desc_font->CalcTextSizeA(desc_font->FontSize, std::numeric_limits::max(), avail_width, description); const ImVec2 desc_pos = ImVec2(bb.Min.x + (avail_width - desc_size.x) * 0.5f, title_bb.w + LayoutScale(10.0f)); @@ -2156,48 +2140,49 @@ bool ImGuiFullscreen::HorizontalMenuItem(GPUTexture* icon, const char* title, co ImGui::SameLine(); - s_menu_button_index++; + s_state.menu_button_index++; return pressed; } void ImGuiFullscreen::PopulateFileSelectorItems() { - s_file_selector_items.clear(); + s_state.file_selector_items.clear(); - if (s_file_selector_current_directory.empty()) + if (s_state.file_selector_current_directory.empty()) { for (std::string& root_path : FileSystem::GetRootDirectoryList()) - s_file_selector_items.emplace_back(fmt::format(ICON_FA_FOLDER " {}", root_path), std::move(root_path), false); + s_state.file_selector_items.emplace_back(fmt::format(ICON_FA_FOLDER " {}", root_path), std::move(root_path), + false); } else { FileSystem::FindResultsArray results; - FileSystem::FindFiles(s_file_selector_current_directory.c_str(), "*", + FileSystem::FindFiles(s_state.file_selector_current_directory.c_str(), "*", FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_FOLDERS | FILESYSTEM_FIND_HIDDEN_FILES | FILESYSTEM_FIND_RELATIVE_PATHS | FILESYSTEM_FIND_SORT_BY_NAME, &results); std::string parent_path; - std::string::size_type sep_pos = s_file_selector_current_directory.rfind(FS_OSPATH_SEPARATOR_CHARACTER); + std::string::size_type sep_pos = s_state.file_selector_current_directory.rfind(FS_OSPATH_SEPARATOR_CHARACTER); if (sep_pos != std::string::npos) - parent_path = Path::Canonicalize(s_file_selector_current_directory.substr(0, sep_pos)); + parent_path = Path::Canonicalize(s_state.file_selector_current_directory.substr(0, sep_pos)); - s_file_selector_items.emplace_back(ICON_FA_FOLDER_OPEN " ", std::move(parent_path), false); + s_state.file_selector_items.emplace_back(ICON_FA_FOLDER_OPEN " ", std::move(parent_path), false); for (const FILESYSTEM_FIND_DATA& fd : results) { std::string full_path = - fmt::format("{}" FS_OSPATH_SEPARATOR_STR "{}", s_file_selector_current_directory, fd.FileName); + fmt::format("{}" FS_OSPATH_SEPARATOR_STR "{}", s_state.file_selector_current_directory, fd.FileName); if (fd.Attributes & FILESYSTEM_FILE_ATTRIBUTE_DIRECTORY) { std::string title = fmt::format(ICON_FA_FOLDER " {}", fd.FileName); - s_file_selector_items.emplace_back(std::move(title), std::move(full_path), false); + s_state.file_selector_items.emplace_back(std::move(title), std::move(full_path), false); } else { - if (s_file_selector_filters.empty() || - std::none_of(s_file_selector_filters.begin(), s_file_selector_filters.end(), + if (s_state.file_selector_filters.empty() || + std::none_of(s_state.file_selector_filters.begin(), s_state.file_selector_filters.end(), [&fd](const std::string& filter) { return StringUtil::WildcardMatch(fd.FileName.c_str(), filter.c_str(), false); })) @@ -2206,7 +2191,7 @@ void ImGuiFullscreen::PopulateFileSelectorItems() } std::string title = fmt::format(ICON_FA_FILE " {}", fd.FileName); - s_file_selector_items.emplace_back(std::move(title), std::move(full_path), true); + s_state.file_selector_items.emplace_back(std::move(title), std::move(full_path), true); } } } @@ -2217,13 +2202,13 @@ void ImGuiFullscreen::SetFileSelectorDirectory(std::string dir) while (!dir.empty() && dir.back() == FS_OSPATH_SEPARATOR_CHARACTER) dir.erase(dir.size() - 1); - s_file_selector_current_directory = std::move(dir); + s_state.file_selector_current_directory = std::move(dir); PopulateFileSelectorItems(); } bool ImGuiFullscreen::IsFileSelectorOpen() { - return s_file_selector_open; + return s_state.file_selector_open; } void ImGuiFullscreen::OpenFileSelector(std::string_view title, bool select_directory, FileSelectorCallback callback, @@ -2239,14 +2224,14 @@ void ImGuiFullscreen::OpenFileSelector(std::string_view title, bool select_direc return; } - if (s_file_selector_open) + if (s_state.file_selector_open) CloseFileSelector(); - s_file_selector_open = true; - s_file_selector_directory = select_directory; - s_file_selector_title = fmt::format("{}##file_selector", title); - s_file_selector_callback = std::move(callback); - s_file_selector_filters = std::move(filters); + s_state.file_selector_open = true; + s_state.file_selector_directory = select_directory; + s_state.file_selector_title = fmt::format("{}##file_selector", title); + s_state.file_selector_callback = std::move(callback); + s_state.file_selector_filters = std::move(filters); SetFileSelectorDirectory(std::move(initial_directory)); QueueResetFocus(FocusResetType::PopupOpened); @@ -2254,67 +2239,67 @@ void ImGuiFullscreen::OpenFileSelector(std::string_view title, bool select_direc void ImGuiFullscreen::CloseFileSelector() { - if (!s_file_selector_open) + if (!s_state.file_selector_open) return; - if (ImGui::IsPopupOpen(s_file_selector_title.c_str(), 0)) + if (ImGui::IsPopupOpen(s_state.file_selector_title.c_str(), 0)) ImGui::ClosePopupToLevel(GImGui->OpenPopupStack.Size - 1, true); - s_file_selector_open = false; - s_file_selector_directory = false; - std::string().swap(s_file_selector_title); - FileSelectorCallback().swap(s_file_selector_callback); - FileSelectorFilters().swap(s_file_selector_filters); - std::string().swap(s_file_selector_current_directory); - s_file_selector_items.clear(); + s_state.file_selector_open = false; + s_state.file_selector_directory = false; + std::string().swap(s_state.file_selector_title); + FileSelectorCallback().swap(s_state.file_selector_callback); + FileSelectorFilters().swap(s_state.file_selector_filters); + std::string().swap(s_state.file_selector_current_directory); + s_state.file_selector_items.clear(); ImGui::CloseCurrentPopup(); QueueResetFocus(FocusResetType::PopupClosed); } void ImGuiFullscreen::DrawFileSelector() { - if (!s_file_selector_open) + if (!s_state.file_selector_open) return; ImGui::SetNextWindowSize(LayoutScale(1000.0f, 650.0f)); ImGui::SetNextWindowPos((ImGui::GetIO().DisplaySize - LayoutScale(0.0f, LAYOUT_FOOTER_HEIGHT)) * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f)); - ImGui::OpenPopup(s_file_selector_title.c_str()); + ImGui::OpenPopup(s_state.file_selector_title.c_str()); FileSelectorItem* selected = nullptr; - ImGui::PushFont(g_large_font); + ImGui::PushFont(UIStyle.LargeFont); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, LayoutScale(10.0f)); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, LayoutScale(LAYOUT_MENU_BUTTON_X_PADDING, LAYOUT_MENU_BUTTON_Y_PADDING)); ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f); - ImGui::PushStyleColor(ImGuiCol_Text, UIPrimaryTextColor); - ImGui::PushStyleColor(ImGuiCol_TitleBg, UIPrimaryDarkColor); - ImGui::PushStyleColor(ImGuiCol_TitleBgActive, UIPrimaryColor); + ImGui::PushStyleColor(ImGuiCol_Text, UIStyle.PrimaryTextColor); + ImGui::PushStyleColor(ImGuiCol_TitleBg, UIStyle.PrimaryDarkColor); + ImGui::PushStyleColor(ImGuiCol_TitleBgActive, UIStyle.PrimaryColor); bool is_open = !WantsToCloseMenu(); bool directory_selected = false; - if (ImGui::BeginPopupModal(s_file_selector_title.c_str(), &is_open, + if (ImGui::BeginPopupModal(s_state.file_selector_title.c_str(), &is_open, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove)) { - ImGui::PushStyleColor(ImGuiCol_Text, UIBackgroundTextColor); + ImGui::PushStyleColor(ImGuiCol_Text, UIStyle.BackgroundTextColor); ResetFocusHere(); BeginMenuButtons(); - if (!s_file_selector_current_directory.empty()) + if (!s_state.file_selector_current_directory.empty()) { - MenuButton(SmallString::from_format(ICON_FA_FOLDER_OPEN " {}", s_file_selector_current_directory).c_str(), + MenuButton(SmallString::from_format(ICON_FA_FOLDER_OPEN " {}", s_state.file_selector_current_directory).c_str(), nullptr, false, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY); } - if (s_file_selector_directory && !s_file_selector_current_directory.empty()) + if (s_state.file_selector_directory && !s_state.file_selector_current_directory.empty()) { if (MenuButton(ICON_FA_FOLDER_PLUS " ", nullptr, true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY)) directory_selected = true; } - for (FileSelectorItem& item : s_file_selector_items) + for (FileSelectorItem& item : s_state.file_selector_items) { if (MenuButton(item.display_name.c_str(), nullptr, true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY)) selected = &item; @@ -2336,13 +2321,13 @@ void ImGuiFullscreen::DrawFileSelector() ImGui::PopFont(); if (is_open) - GetFileSelectorHelpText(s_fullscreen_footer_text); + GetFileSelectorHelpText(s_state.fullscreen_footer_text); if (selected) { if (selected->is_file) { - s_file_selector_callback(selected->full_path); + s_state.file_selector_callback(selected->full_path); } else { @@ -2352,22 +2337,22 @@ void ImGuiFullscreen::DrawFileSelector() } else if (directory_selected) { - s_file_selector_callback(s_file_selector_current_directory); + s_state.file_selector_callback(s_state.file_selector_current_directory); } else if (!is_open) { std::string no_path; - s_file_selector_callback(no_path); + s_state.file_selector_callback(no_path); CloseFileSelector(); } else { if (ImGui::IsKeyPressed(ImGuiKey_Backspace, false) || ImGui::IsKeyPressed(ImGuiKey_NavGamepadMenu, false)) { - if (!s_file_selector_items.empty() && s_file_selector_items.front().display_name == ICON_FA_FOLDER_OPEN - " ") + if (!s_state.file_selector_items.empty() && + s_state.file_selector_items.front().display_name == ICON_FA_FOLDER_OPEN " ") { - SetFileSelectorDirectory(std::move(s_file_selector_items.front().full_path)); + SetFileSelectorDirectory(std::move(s_state.file_selector_items.front().full_path)); QueueResetFocus(FocusResetType::Other); } } @@ -2376,81 +2361,81 @@ void ImGuiFullscreen::DrawFileSelector() bool ImGuiFullscreen::IsChoiceDialogOpen() { - return s_choice_dialog_open; + return s_state.choice_dialog_open; } void ImGuiFullscreen::OpenChoiceDialog(std::string_view title, bool checkable, ChoiceDialogOptions options, ChoiceDialogCallback callback) { - if (s_choice_dialog_open) + if (s_state.choice_dialog_open) CloseChoiceDialog(); - s_choice_dialog_open = true; - s_choice_dialog_checkable = checkable; - s_choice_dialog_title = fmt::format("{}##choice_dialog", title); - s_choice_dialog_options = std::move(options); - s_choice_dialog_callback = std::move(callback); + s_state.choice_dialog_open = true; + s_state.choice_dialog_checkable = checkable; + s_state.choice_dialog_title = fmt::format("{}##choice_dialog", title); + s_state.choice_dialog_options = std::move(options); + s_state.choice_dialog_callback = std::move(callback); QueueResetFocus(FocusResetType::PopupOpened); } void ImGuiFullscreen::CloseChoiceDialog() { - if (!s_choice_dialog_open) + if (!s_state.choice_dialog_open) return; - if (ImGui::IsPopupOpen(s_choice_dialog_title.c_str(), 0)) + if (ImGui::IsPopupOpen(s_state.choice_dialog_title.c_str(), 0)) ImGui::ClosePopupToLevel(GImGui->OpenPopupStack.Size - 1, true); - s_choice_dialog_open = false; - s_choice_dialog_checkable = false; - std::string().swap(s_choice_dialog_title); - ChoiceDialogOptions().swap(s_choice_dialog_options); - ChoiceDialogCallback().swap(s_choice_dialog_callback); + s_state.choice_dialog_open = false; + s_state.choice_dialog_checkable = false; + std::string().swap(s_state.choice_dialog_title); + ChoiceDialogOptions().swap(s_state.choice_dialog_options); + ChoiceDialogCallback().swap(s_state.choice_dialog_callback); QueueResetFocus(FocusResetType::PopupClosed); } void ImGuiFullscreen::DrawChoiceDialog() { - if (!s_choice_dialog_open) + if (!s_state.choice_dialog_open) return; - ImGui::PushFont(g_large_font); + ImGui::PushFont(UIStyle.LargeFont); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, LayoutScale(10.0f)); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, LayoutScale(LAYOUT_MENU_BUTTON_X_PADDING, LAYOUT_MENU_BUTTON_Y_PADDING)); ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f); - ImGui::PushStyleColor(ImGuiCol_Text, UIPrimaryTextColor); - ImGui::PushStyleColor(ImGuiCol_TitleBg, UIPrimaryDarkColor); - ImGui::PushStyleColor(ImGuiCol_TitleBgActive, UIPrimaryColor); + ImGui::PushStyleColor(ImGuiCol_Text, UIStyle.PrimaryTextColor); + ImGui::PushStyleColor(ImGuiCol_TitleBg, UIStyle.PrimaryDarkColor); + ImGui::PushStyleColor(ImGuiCol_TitleBgActive, UIStyle.PrimaryColor); const float width = LayoutScale(600.0f); const float title_height = - g_large_font->FontSize + ImGui::GetStyle().FramePadding.y * 2.0f + ImGui::GetStyle().WindowPadding.y * 2.0f; + UIStyle.LargeFont->FontSize + ImGui::GetStyle().FramePadding.y * 2.0f + ImGui::GetStyle().WindowPadding.y * 2.0f; const float height = std::min(LayoutScale(480.0f), title_height + (LayoutScale(LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY) + LayoutScale(LAYOUT_MENU_BUTTON_Y_PADDING) * 2.0f) * - static_cast(s_choice_dialog_options.size())); + static_cast(s_state.choice_dialog_options.size())); ImGui::SetNextWindowSize(ImVec2(width, height)); ImGui::SetNextWindowPos((ImGui::GetIO().DisplaySize - LayoutScale(0.0f, LAYOUT_FOOTER_HEIGHT)) * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f)); - ImGui::OpenPopup(s_choice_dialog_title.c_str()); + ImGui::OpenPopup(s_state.choice_dialog_title.c_str()); bool is_open = true; s32 choice = -1; - if (ImGui::BeginPopupModal(s_choice_dialog_title.c_str(), &is_open, + if (ImGui::BeginPopupModal(s_state.choice_dialog_title.c_str(), &is_open, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove)) { - ImGui::PushStyleColor(ImGuiCol_Text, UIBackgroundTextColor); + ImGui::PushStyleColor(ImGuiCol_Text, UIStyle.BackgroundTextColor); ResetFocusHere(); BeginMenuButtons(); - if (s_choice_dialog_checkable) + if (s_state.choice_dialog_checkable) { - for (s32 i = 0; i < static_cast(s_choice_dialog_options.size()); i++) + for (s32 i = 0; i < static_cast(s_state.choice_dialog_options.size()); i++) { - auto& option = s_choice_dialog_options[i]; + auto& option = s_state.choice_dialog_options[i]; const SmallString title = SmallString::from_format("{0} {1}", option.second ? ICON_FA_CHECK_SQUARE : ICON_FA_SQUARE, option.first); @@ -2463,15 +2448,15 @@ void ImGuiFullscreen::DrawChoiceDialog() } else { - for (s32 i = 0; i < static_cast(s_choice_dialog_options.size()); i++) + for (s32 i = 0; i < static_cast(s_state.choice_dialog_options.size()); i++) { - auto& option = s_choice_dialog_options[i]; + auto& option = s_state.choice_dialog_options[i]; if (ActiveButtonWithRightText(option.first.c_str(), option.second ? ICON_FA_CHECK : nullptr, option.second, true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY)) { choice = i; - for (s32 j = 0; j < static_cast(s_choice_dialog_options.size()); j++) - s_choice_dialog_options[j].second = (j == i); + for (s32 j = 0; j < static_cast(s_state.choice_dialog_options.size()); j++) + s_state.choice_dialog_options[j].second = (j == i); } } } @@ -2491,90 +2476,90 @@ void ImGuiFullscreen::DrawChoiceDialog() if (choice >= 0) { - const auto& option = s_choice_dialog_options[choice]; - s_choice_dialog_callback(choice, option.first, option.second); + const auto& option = s_state.choice_dialog_options[choice]; + s_state.choice_dialog_callback(choice, option.first, option.second); } else if (!is_open) { std::string no_string; - s_choice_dialog_callback(-1, no_string, false); + s_state.choice_dialog_callback(-1, no_string, false); CloseChoiceDialog(); } else { - GetChoiceDialogHelpText(s_fullscreen_footer_text); + GetChoiceDialogHelpText(s_state.fullscreen_footer_text); } } bool ImGuiFullscreen::IsInputDialogOpen() { - return s_input_dialog_open; + return s_state.input_dialog_open; } void ImGuiFullscreen::OpenInputStringDialog(std::string title, std::string message, std::string caption, std::string ok_button_text, InputStringDialogCallback callback) { - s_input_dialog_open = true; - s_input_dialog_title = std::move(title); - s_input_dialog_message = std::move(message); - s_input_dialog_caption = std::move(caption); - s_input_dialog_ok_text = std::move(ok_button_text); - s_input_dialog_callback = std::move(callback); + s_state.input_dialog_open = true; + s_state.input_dialog_title = std::move(title); + s_state.input_dialog_message = std::move(message); + s_state.input_dialog_caption = std::move(caption); + s_state.input_dialog_ok_text = std::move(ok_button_text); + s_state.input_dialog_callback = std::move(callback); QueueResetFocus(FocusResetType::PopupOpened); } void ImGuiFullscreen::DrawInputDialog() { - if (!s_input_dialog_open) + if (!s_state.input_dialog_open) return; ImGui::SetNextWindowSize(LayoutScale(700.0f, 0.0f)); ImGui::SetNextWindowPos((ImGui::GetIO().DisplaySize - LayoutScale(0.0f, LAYOUT_FOOTER_HEIGHT)) * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f)); - ImGui::OpenPopup(s_input_dialog_title.c_str()); + ImGui::OpenPopup(s_state.input_dialog_title.c_str()); - ImGui::PushFont(g_large_font); + ImGui::PushFont(UIStyle.LargeFont); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, LayoutScale(10.0f)); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, LayoutScale(LAYOUT_MENU_BUTTON_X_PADDING, LAYOUT_MENU_BUTTON_Y_PADDING)); ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f); - ImGui::PushStyleColor(ImGuiCol_Text, UIPrimaryTextColor); - ImGui::PushStyleColor(ImGuiCol_TitleBg, UIPrimaryDarkColor); - ImGui::PushStyleColor(ImGuiCol_TitleBgActive, UIPrimaryColor); + ImGui::PushStyleColor(ImGuiCol_Text, UIStyle.PrimaryTextColor); + ImGui::PushStyleColor(ImGuiCol_TitleBg, UIStyle.PrimaryDarkColor); + ImGui::PushStyleColor(ImGuiCol_TitleBgActive, UIStyle.PrimaryColor); bool is_open = true; - if (ImGui::BeginPopupModal(s_input_dialog_title.c_str(), &is_open, + if (ImGui::BeginPopupModal(s_state.input_dialog_title.c_str(), &is_open, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove)) { ResetFocusHere(); - ImGui::TextWrapped("%s", s_input_dialog_message.c_str()); + ImGui::TextWrapped("%s", s_state.input_dialog_message.c_str()); BeginMenuButtons(); ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(10.0f)); - if (!s_input_dialog_caption.empty()) + if (!s_state.input_dialog_caption.empty()) { const float prev = ImGui::GetCursorPosX(); - ImGui::TextUnformatted(s_input_dialog_caption.c_str()); + ImGui::TextUnformatted(s_state.input_dialog_caption.c_str()); ImGui::SetNextItemWidth(ImGui::GetCursorPosX() - prev); } else { ImGui::SetNextItemWidth(ImGui::GetCurrentWindow()->WorkRect.GetWidth()); } - ImGui::InputText("##input", &s_input_dialog_text); + ImGui::InputText("##input", &s_state.input_dialog_text); ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(10.0f)); - const bool ok_enabled = !s_input_dialog_text.empty(); + const bool ok_enabled = !s_state.input_dialog_text.empty(); - if (ActiveButton(s_input_dialog_ok_text.c_str(), false, ok_enabled) && ok_enabled) + if (ActiveButton(s_state.input_dialog_ok_text.c_str(), false, ok_enabled) && ok_enabled) { // have to move out in case they open another dialog in the callback - InputStringDialogCallback cb(std::move(s_input_dialog_callback)); - std::string text(std::move(s_input_dialog_text)); + InputStringDialogCallback cb(std::move(s_state.input_dialog_callback)); + std::string text(std::move(s_state.input_dialog_text)); CloseInputDialog(); ImGui::CloseCurrentPopup(); cb(std::move(text)); @@ -2594,7 +2579,7 @@ void ImGuiFullscreen::DrawInputDialog() if (!is_open) CloseInputDialog(); else - GetInputDialogHelpText(s_fullscreen_footer_text); + GetInputDialogHelpText(s_state.fullscreen_footer_text); ImGui::PopStyleColor(3); ImGui::PopStyleVar(3); @@ -2603,24 +2588,24 @@ void ImGuiFullscreen::DrawInputDialog() void ImGuiFullscreen::CloseInputDialog() { - if (!s_input_dialog_open) + if (!s_state.input_dialog_open) return; - if (ImGui::IsPopupOpen(s_input_dialog_title.c_str(), 0)) + if (ImGui::IsPopupOpen(s_state.input_dialog_title.c_str(), 0)) ImGui::ClosePopupToLevel(GImGui->OpenPopupStack.Size - 1, true); - s_input_dialog_open = false; - s_input_dialog_title = {}; - s_input_dialog_message = {}; - s_input_dialog_caption = {}; - s_input_dialog_ok_text = {}; - s_input_dialog_text = {}; - s_input_dialog_callback = {}; + s_state.input_dialog_open = false; + s_state.input_dialog_title = {}; + s_state.input_dialog_message = {}; + s_state.input_dialog_caption = {}; + s_state.input_dialog_ok_text = {}; + s_state.input_dialog_text = {}; + s_state.input_dialog_callback = {}; } bool ImGuiFullscreen::IsMessageBoxDialogOpen() { - return s_message_dialog_open; + return s_state.message_dialog_open; } void ImGuiFullscreen::OpenConfirmMessageDialog(std::string title, std::string message, @@ -2629,12 +2614,12 @@ void ImGuiFullscreen::OpenConfirmMessageDialog(std::string title, std::string me { CloseMessageDialog(); - s_message_dialog_open = true; - s_message_dialog_title = std::move(title); - s_message_dialog_message = std::move(message); - s_message_dialog_callback = std::move(callback); - s_message_dialog_buttons[0] = std::move(yes_button_text); - s_message_dialog_buttons[1] = std::move(no_button_text); + s_state.message_dialog_open = true; + s_state.message_dialog_title = std::move(title); + s_state.message_dialog_message = std::move(message); + s_state.message_dialog_callback = std::move(callback); + s_state.message_dialog_buttons[0] = std::move(yes_button_text); + s_state.message_dialog_buttons[1] = std::move(no_button_text); QueueResetFocus(FocusResetType::PopupOpened); } @@ -2643,11 +2628,11 @@ void ImGuiFullscreen::OpenInfoMessageDialog(std::string title, std::string messa { CloseMessageDialog(); - s_message_dialog_open = true; - s_message_dialog_title = std::move(title); - s_message_dialog_message = std::move(message); - s_message_dialog_callback = std::move(callback); - s_message_dialog_buttons[0] = std::move(button_text); + s_state.message_dialog_open = true; + s_state.message_dialog_title = std::move(title); + s_state.message_dialog_message = std::move(message); + s_state.message_dialog_callback = std::move(callback); + s_state.message_dialog_buttons[0] = std::move(button_text); QueueResetFocus(FocusResetType::PopupOpened); } @@ -2657,57 +2642,57 @@ void ImGuiFullscreen::OpenMessageDialog(std::string title, std::string message, { CloseMessageDialog(); - s_message_dialog_open = true; - s_message_dialog_title = std::move(title); - s_message_dialog_message = std::move(message); - s_message_dialog_callback = std::move(callback); - s_message_dialog_buttons[0] = std::move(first_button_text); - s_message_dialog_buttons[1] = std::move(second_button_text); - s_message_dialog_buttons[2] = std::move(third_button_text); + s_state.message_dialog_open = true; + s_state.message_dialog_title = std::move(title); + s_state.message_dialog_message = std::move(message); + s_state.message_dialog_callback = std::move(callback); + s_state.message_dialog_buttons[0] = std::move(first_button_text); + s_state.message_dialog_buttons[1] = std::move(second_button_text); + s_state.message_dialog_buttons[2] = std::move(third_button_text); QueueResetFocus(FocusResetType::PopupOpened); } void ImGuiFullscreen::CloseMessageDialog() { - if (!s_message_dialog_open) + if (!s_state.message_dialog_open) return; - if (ImGui::IsPopupOpen(s_message_dialog_title.c_str(), 0)) + if (ImGui::IsPopupOpen(s_state.message_dialog_title.c_str(), 0)) ImGui::ClosePopupToLevel(GImGui->OpenPopupStack.Size - 1, true); - s_message_dialog_open = false; - s_message_dialog_title = {}; - s_message_dialog_message = {}; - s_message_dialog_buttons = {}; - s_message_dialog_callback = {}; + s_state.message_dialog_open = false; + s_state.message_dialog_title = {}; + s_state.message_dialog_message = {}; + s_state.message_dialog_buttons = {}; + s_state.message_dialog_callback = {}; QueueResetFocus(FocusResetType::PopupClosed); } void ImGuiFullscreen::DrawMessageDialog() { - if (!s_message_dialog_open) + if (!s_state.message_dialog_open) return; - const char* win_id = s_message_dialog_title.empty() ? "##messagedialog" : s_message_dialog_title.c_str(); + const char* win_id = s_state.message_dialog_title.empty() ? "##messagedialog" : s_state.message_dialog_title.c_str(); ImGui::SetNextWindowSize(LayoutScale(700.0f, 0.0f)); ImGui::SetNextWindowPos((ImGui::GetIO().DisplaySize - LayoutScale(0.0f, LAYOUT_FOOTER_HEIGHT)) * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f)); ImGui::OpenPopup(win_id); - ImGui::PushFont(g_large_font); + ImGui::PushFont(UIStyle.LargeFont); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, LayoutScale(20.0f, 20.0f)); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, LayoutScale(10.0f)); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, LayoutScale(LAYOUT_MENU_BUTTON_X_PADDING, LAYOUT_MENU_BUTTON_Y_PADDING)); ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f); - ImGui::PushStyleColor(ImGuiCol_Text, UIPrimaryTextColor); - ImGui::PushStyleColor(ImGuiCol_TitleBg, UIPrimaryDarkColor); - ImGui::PushStyleColor(ImGuiCol_TitleBgActive, UIPrimaryColor); + ImGui::PushStyleColor(ImGuiCol_Text, UIStyle.PrimaryTextColor); + ImGui::PushStyleColor(ImGuiCol_TitleBg, UIStyle.PrimaryDarkColor); + ImGui::PushStyleColor(ImGuiCol_TitleBgActive, UIStyle.PrimaryColor); bool is_open = true; const u32 flags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | - (s_message_dialog_title.empty() ? ImGuiWindowFlags_NoTitleBar : 0); + (s_state.message_dialog_title.empty() ? ImGuiWindowFlags_NoTitleBar : 0); std::optional result; if (ImGui::BeginPopupModal(win_id, &is_open, flags)) @@ -2715,13 +2700,13 @@ void ImGuiFullscreen::DrawMessageDialog() ResetFocusHere(); BeginMenuButtons(); - ImGui::TextWrapped("%s", s_message_dialog_message.c_str()); + ImGui::TextWrapped("%s", s_state.message_dialog_message.c_str()); ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(20.0f)); - for (s32 button_index = 0; button_index < static_cast(s_message_dialog_buttons.size()); button_index++) + for (s32 button_index = 0; button_index < static_cast(s_state.message_dialog_buttons.size()); button_index++) { - if (!s_message_dialog_buttons[button_index].empty() && - ActiveButton(s_message_dialog_buttons[button_index].c_str(), false)) + if (!s_state.message_dialog_buttons[button_index].empty() && + ActiveButton(s_state.message_dialog_buttons[button_index].c_str(), false)) { result = button_index; ImGui::CloseCurrentPopup(); @@ -2740,7 +2725,7 @@ void ImGuiFullscreen::DrawMessageDialog() if (!is_open || result.has_value()) { // have to move out in case they open another dialog in the callback - auto cb = (std::move(s_message_dialog_callback)); + auto cb = (std::move(s_state.message_dialog_callback)); CloseMessageDialog(); if (std::holds_alternative(cb)) @@ -2758,7 +2743,7 @@ void ImGuiFullscreen::DrawMessageDialog() } else { - GetChoiceDialogHelpText(s_fullscreen_footer_text); + GetChoiceDialogHelpText(s_state.fullscreen_footer_text); } } @@ -2790,10 +2775,10 @@ void ImGuiFullscreen::OpenBackgroundProgressDialog(const char* str_id, std::stri { const ImGuiID id = GetBackgroundProgressID(str_id); - std::unique_lock lock(s_background_progress_lock); + std::unique_lock lock(s_state.background_progress_lock); #if defined(_DEBUG) || defined(_DEVEL) - for (const BackgroundProgressDialogData& data : s_background_progress_dialogs) + for (const BackgroundProgressDialogData& data : s_state.background_progress_dialogs) { DebugAssert(data.id != id); } @@ -2805,7 +2790,7 @@ void ImGuiFullscreen::OpenBackgroundProgressDialog(const char* str_id, std::stri data.min = min; data.max = max; data.value = value; - s_background_progress_dialogs.push_back(std::move(data)); + s_state.background_progress_dialogs.push_back(std::move(data)); } void ImGuiFullscreen::UpdateBackgroundProgressDialog(const char* str_id, std::string message, s32 min, s32 max, @@ -2813,9 +2798,9 @@ void ImGuiFullscreen::UpdateBackgroundProgressDialog(const char* str_id, std::st { const ImGuiID id = GetBackgroundProgressID(str_id); - std::unique_lock lock(s_background_progress_lock); + std::unique_lock lock(s_state.background_progress_lock); - for (BackgroundProgressDialogData& data : s_background_progress_dialogs) + for (BackgroundProgressDialogData& data : s_state.background_progress_dialogs) { if (data.id == id) { @@ -2834,13 +2819,13 @@ void ImGuiFullscreen::CloseBackgroundProgressDialog(const char* str_id) { const ImGuiID id = GetBackgroundProgressID(str_id); - std::unique_lock lock(s_background_progress_lock); + std::unique_lock lock(s_state.background_progress_lock); - for (auto it = s_background_progress_dialogs.begin(); it != s_background_progress_dialogs.end(); ++it) + for (auto it = s_state.background_progress_dialogs.begin(); it != s_state.background_progress_dialogs.end(); ++it) { if (it->id == id) { - s_background_progress_dialogs.erase(it); + s_state.background_progress_dialogs.erase(it); return; } } @@ -2852,9 +2837,9 @@ bool ImGuiFullscreen::IsBackgroundProgressDialogOpen(const char* str_id) { const ImGuiID id = GetBackgroundProgressID(str_id); - std::unique_lock lock(s_background_progress_lock); + std::unique_lock lock(s_state.background_progress_lock); - for (auto it = s_background_progress_dialogs.begin(); it != s_background_progress_dialogs.end(); ++it) + for (auto it = s_state.background_progress_dialogs.begin(); it != s_state.background_progress_dialogs.end(); ++it) { if (it->id == id) return true; @@ -2865,24 +2850,24 @@ bool ImGuiFullscreen::IsBackgroundProgressDialogOpen(const char* str_id) void ImGuiFullscreen::DrawBackgroundProgressDialogs(ImVec2& position, float spacing) { - std::unique_lock lock(s_background_progress_lock); - if (s_background_progress_dialogs.empty()) + std::unique_lock lock(s_state.background_progress_lock); + if (s_state.background_progress_dialogs.empty()) return; const float window_width = LayoutScale(500.0f); const float window_height = LayoutScale(75.0f); - ImGui::PushStyleColor(ImGuiCol_WindowBg, UIPrimaryDarkColor); - ImGui::PushStyleColor(ImGuiCol_PlotHistogram, UISecondaryStrongColor); + ImGui::PushStyleColor(ImGuiCol_WindowBg, UIStyle.PrimaryDarkColor); + ImGui::PushStyleColor(ImGuiCol_PlotHistogram, UIStyle.SecondaryStrongColor); ImGui::PushStyleVar(ImGuiStyleVar_PopupRounding, LayoutScale(4.0f)); ImGui::PushStyleVar(ImGuiStyleVar_PopupBorderSize, LayoutScale(1.0f)); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, LayoutScale(10.0f, 10.0f)); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, LayoutScale(10.0f, 10.0f)); - ImGui::PushFont(g_medium_font); + ImGui::PushFont(UIStyle.MediumFont); ImDrawList* dl = ImGui::GetForegroundDrawList(); - for (const BackgroundProgressDialogData& data : s_background_progress_dialogs) + for (const BackgroundProgressDialogData& data : s_state.background_progress_dialogs) { const float window_pos_x = position.x; const float window_pos_y = position.y - ((s_notification_vertical_direction < 0.0f) ? window_height : 0.0f); @@ -2892,25 +2877,25 @@ void ImGuiFullscreen::DrawBackgroundProgressDialogs(ImVec2& position, float spac IM_COL32(0x11, 0x11, 0x11, 200), LayoutScale(10.0f)); ImVec2 pos(window_pos_x + LayoutScale(10.0f), window_pos_y + LayoutScale(10.0f)); - dl->AddText(g_medium_font, g_medium_font->FontSize, pos, IM_COL32(255, 255, 255, 255), data.message.c_str(), - nullptr, 0.0f); - pos.y += g_medium_font->FontSize + LayoutScale(10.0f); + dl->AddText(UIStyle.MediumFont, UIStyle.MediumFont->FontSize, pos, IM_COL32(255, 255, 255, 255), + data.message.c_str(), nullptr, 0.0f); + pos.y += UIStyle.MediumFont->FontSize + LayoutScale(10.0f); const ImVec2 box_end(pos.x + window_width - LayoutScale(10.0f * 2.0f), pos.y + LayoutScale(25.0f)); - dl->AddRectFilled(pos, box_end, ImGui::GetColorU32(UIPrimaryDarkColor)); + dl->AddRectFilled(pos, box_end, ImGui::GetColorU32(UIStyle.PrimaryDarkColor)); if (data.min != data.max) { const float fraction = static_cast(data.value - data.min) / static_cast(data.max - data.min); dl->AddRectFilled(pos, ImVec2(pos.x + fraction * (box_end.x - pos.x), box_end.y), - ImGui::GetColorU32(UISecondaryColor)); + ImGui::GetColorU32(UIStyle.SecondaryColor)); const auto text = TinyString::from_format("{}%", static_cast(std::round(fraction * 100.0f))); const ImVec2 text_size(ImGui::CalcTextSize(text)); const ImVec2 text_pos(pos.x + ((box_end.x - pos.x) / 2.0f) - (text_size.x / 2.0f), pos.y + ((box_end.y - pos.y) / 2.0f) - (text_size.y / 2.0f)); - dl->AddText(g_medium_font, g_medium_font->FontSize, text_pos, ImGui::GetColorU32(UIPrimaryTextColor), - text.c_str(), text.end_ptr()); + dl->AddText(UIStyle.MediumFont, UIStyle.MediumFont->FontSize, text_pos, + ImGui::GetColorU32(UIStyle.PrimaryTextColor), text.c_str(), text.end_ptr()); } else { @@ -2920,7 +2905,7 @@ void ImGuiFullscreen::DrawBackgroundProgressDialogs(ImVec2& position, float spac const ImVec2 bar_start(pos.x + ImLerp(0.0f, box_end.x, fraction) - bar_width, pos.y); const ImVec2 bar_end(std::min(bar_start.x + bar_width, box_end.x), pos.y + LayoutScale(25.0f)); dl->AddRectFilled(ImClamp(bar_start, pos, box_end), ImClamp(bar_end, pos, box_end), - ImGui::GetColorU32(UISecondaryColor)); + ImGui::GetColorU32(UIStyle.SecondaryColor)); } position.y += s_notification_vertical_direction * (window_height + spacing); @@ -2942,7 +2927,7 @@ void ImGuiFullscreen::AddNotification(std::string key, float duration, std::stri if (!key.empty()) { - for (auto it = s_notifications.begin(); it != s_notifications.end(); ++it) + for (auto it = s_state.notifications.begin(); it != s_state.notifications.end(); ++it) { if (it->key == key) { @@ -2969,17 +2954,17 @@ void ImGuiFullscreen::AddNotification(std::string key, float duration, std::stri notif.move_time = current_time; notif.target_y = -1.0f; notif.last_y = -1.0f; - s_notifications.push_back(std::move(notif)); + s_state.notifications.push_back(std::move(notif)); } void ImGuiFullscreen::ClearNotifications() { - s_notifications.clear(); + s_state.notifications.clear(); } void ImGuiFullscreen::DrawNotifications(ImVec2& position, float spacing) { - if (s_notifications.empty()) + if (s_state.notifications.empty()) return; static constexpr float MOVE_DURATION = 0.5f; @@ -2997,21 +2982,23 @@ void ImGuiFullscreen::DrawNotifications(ImVec2& position, float spacing) const float shadow_size = ImGuiFullscreen::LayoutScale(4.0f); const float rounding = ImGuiFullscreen::LayoutScale(4.0f); - ImFont* const title_font = ImGuiFullscreen::g_large_font; - ImFont* const text_font = ImGuiFullscreen::g_medium_font; + ImFont* const title_font = ImGuiFullscreen::UIStyle.LargeFont; + ImFont* const text_font = ImGuiFullscreen::UIStyle.MediumFont; - const u32 toast_background_color = s_light_theme ? IM_COL32(241, 241, 241, 255) : IM_COL32(0x21, 0x21, 0x21, 255); - const u32 toast_border_color = s_light_theme ? IM_COL32(0x88, 0x88, 0x88, 255) : IM_COL32(0x48, 0x48, 0x48, 255); - const u32 toast_title_color = s_light_theme ? IM_COL32(1, 1, 1, 255) : IM_COL32(0xff, 0xff, 0xff, 255); - const u32 toast_text_color = s_light_theme ? IM_COL32(0, 0, 0, 255) : IM_COL32(0xff, 0xff, 0xff, 255); + const u32 toast_background_color = + s_state.light_theme ? IM_COL32(241, 241, 241, 255) : IM_COL32(0x21, 0x21, 0x21, 255); + const u32 toast_border_color = + s_state.light_theme ? IM_COL32(0x88, 0x88, 0x88, 255) : IM_COL32(0x48, 0x48, 0x48, 255); + const u32 toast_title_color = s_state.light_theme ? IM_COL32(1, 1, 1, 255) : IM_COL32(0xff, 0xff, 0xff, 255); + const u32 toast_text_color = s_state.light_theme ? IM_COL32(0, 0, 0, 255) : IM_COL32(0xff, 0xff, 0xff, 255); - for (u32 index = 0; index < static_cast(s_notifications.size());) + for (u32 index = 0; index < static_cast(s_state.notifications.size());) { - Notification& notif = s_notifications[index]; + Notification& notif = s_state.notifications[index]; const float time_passed = static_cast(Timer::ConvertValueToSeconds(current_time - notif.start_time)); if (time_passed >= notif.duration) { - s_notifications.erase(s_notifications.begin() + index); + s_state.notifications.erase(s_state.notifications.begin() + index); continue; } @@ -3104,119 +3091,121 @@ void ImGuiFullscreen::DrawNotifications(ImVec2& position, float spacing) void ImGuiFullscreen::ShowToast(std::string title, std::string message, float duration) { - s_toast_title = std::move(title); - s_toast_message = std::move(message); - s_toast_start_time = Timer::GetCurrentValue(); - s_toast_duration = duration; + s_state.toast_title = std::move(title); + s_state.toast_message = std::move(message); + s_state.toast_start_time = Timer::GetCurrentValue(); + s_state.toast_duration = duration; } void ImGuiFullscreen::ClearToast() { - s_toast_message = {}; - s_toast_title = {}; - s_toast_start_time = 0; - s_toast_duration = 0.0f; + s_state.toast_message = {}; + s_state.toast_title = {}; + s_state.toast_start_time = 0; + s_state.toast_duration = 0.0f; } void ImGuiFullscreen::DrawToast() { - if (s_toast_title.empty() && s_toast_message.empty()) + if (s_state.toast_title.empty() && s_state.toast_message.empty()) return; - const float elapsed = static_cast(Timer::ConvertValueToSeconds(Timer::GetCurrentValue() - s_toast_start_time)); - if (elapsed >= s_toast_duration) + const float elapsed = + static_cast(Timer::ConvertValueToSeconds(Timer::GetCurrentValue() - s_state.toast_start_time)); + if (elapsed >= s_state.toast_duration) { ClearToast(); return; } // fade out the last second - const float alpha = std::min(std::min(elapsed * 4.0f, s_toast_duration - elapsed), 1.0f); + const float alpha = std::min(std::min(elapsed * 4.0f, s_state.toast_duration - elapsed), 1.0f); const float max_width = LayoutScale(600.0f); - ImFont* title_font = g_large_font; - ImFont* message_font = g_medium_font; + ImFont* title_font = UIStyle.LargeFont; + ImFont* message_font = UIStyle.MediumFont; const float padding = LayoutScale(20.0f); const float total_padding = padding * 2.0f; - const float margin = LayoutScale(20.0f + (s_fullscreen_footer_text.empty() ? 0.0f : LAYOUT_FOOTER_HEIGHT)); - const float spacing = s_toast_title.empty() ? 0.0f : LayoutScale(10.0f); + const float margin = LayoutScale(20.0f + (s_state.fullscreen_footer_text.empty() ? 0.0f : LAYOUT_FOOTER_HEIGHT)); + const float spacing = s_state.toast_title.empty() ? 0.0f : LayoutScale(10.0f); const ImVec2 display_size(ImGui::GetIO().DisplaySize); - const ImVec2 title_size(s_toast_title.empty() ? + const ImVec2 title_size(s_state.toast_title.empty() ? ImVec2(0.0f, 0.0f) : - title_font->CalcTextSizeA(title_font->FontSize, FLT_MAX, max_width, s_toast_title.c_str(), - s_toast_title.c_str() + s_toast_title.length())); - const ImVec2 message_size(s_toast_message.empty() ? - ImVec2(0.0f, 0.0f) : - message_font->CalcTextSizeA(message_font->FontSize, FLT_MAX, max_width, - s_toast_message.c_str(), - s_toast_message.c_str() + s_toast_message.length())); + title_font->CalcTextSizeA(title_font->FontSize, FLT_MAX, max_width, + s_state.toast_title.c_str(), + s_state.toast_title.c_str() + s_state.toast_title.length())); + const ImVec2 message_size( + s_state.toast_message.empty() ? + ImVec2(0.0f, 0.0f) : + message_font->CalcTextSizeA(message_font->FontSize, FLT_MAX, max_width, s_state.toast_message.c_str(), + s_state.toast_message.c_str() + s_state.toast_message.length())); const ImVec2 comb_size(std::max(title_size.x, message_size.x), title_size.y + spacing + message_size.y); const ImVec2 box_size(comb_size.x + total_padding, comb_size.y + total_padding); const ImVec2 box_pos((display_size.x - box_size.x) * 0.5f, (display_size.y - margin - box_size.y)); ImDrawList* dl = ImGui::GetForegroundDrawList(); - dl->AddRectFilled(box_pos, box_pos + box_size, ImGui::GetColorU32(ModAlpha(UIPrimaryColor, alpha)), padding); - if (!s_toast_title.empty()) + dl->AddRectFilled(box_pos, box_pos + box_size, ImGui::GetColorU32(ModAlpha(UIStyle.PrimaryColor, alpha)), padding); + if (!s_state.toast_title.empty()) { const float offset = (comb_size.x - title_size.x) * 0.5f; dl->AddText(title_font, title_font->FontSize, box_pos + ImVec2(offset + padding, padding), - ImGui::GetColorU32(ModAlpha(UIPrimaryTextColor, alpha)), s_toast_title.c_str(), - s_toast_title.c_str() + s_toast_title.length(), max_width); + ImGui::GetColorU32(ModAlpha(UIStyle.PrimaryTextColor, alpha)), s_state.toast_title.c_str(), + s_state.toast_title.c_str() + s_state.toast_title.length(), max_width); } - if (!s_toast_message.empty()) + if (!s_state.toast_message.empty()) { const float offset = (comb_size.x - message_size.x) * 0.5f; dl->AddText(message_font, message_font->FontSize, box_pos + ImVec2(offset + padding, padding + spacing + title_size.y), - ImGui::GetColorU32(ModAlpha(UIPrimaryTextColor, alpha)), s_toast_message.c_str(), - s_toast_message.c_str() + s_toast_message.length(), max_width); + ImGui::GetColorU32(ModAlpha(UIStyle.PrimaryTextColor, alpha)), s_state.toast_message.c_str(), + s_state.toast_message.c_str() + s_state.toast_message.length(), max_width); } } void ImGuiFullscreen::SetTheme(bool light) { - s_light_theme = light; + s_state.light_theme = light; if (!light) { // dark - UIBackgroundColor = HEX_TO_IMVEC4(0x212121, 0xff); - UIBackgroundTextColor = HEX_TO_IMVEC4(0xffffff, 0xff); - UIBackgroundLineColor = HEX_TO_IMVEC4(0xf0f0f0, 0xff); - UIBackgroundHighlightColor = HEX_TO_IMVEC4(0x4b4b4b, 0xff); - UIPopupBackgroundColor = HEX_TO_IMVEC4(0x212121, 0xf2); - UIPrimaryColor = HEX_TO_IMVEC4(0x2e2e2e, 0xff); - UIPrimaryLightColor = HEX_TO_IMVEC4(0x484848, 0xff); - UIPrimaryDarkColor = HEX_TO_IMVEC4(0x000000, 0xff); - UIPrimaryTextColor = HEX_TO_IMVEC4(0xffffff, 0xff); - UIDisabledColor = HEX_TO_IMVEC4(0xaaaaaa, 0xff); - UITextHighlightColor = HEX_TO_IMVEC4(0x90caf9, 0xff); - UIPrimaryLineColor = HEX_TO_IMVEC4(0xffffff, 0xff); - UISecondaryColor = HEX_TO_IMVEC4(0x0d47a1, 0xff); - UISecondaryStrongColor = HEX_TO_IMVEC4(0x63a4ff, 0xff); - UISecondaryWeakColor = HEX_TO_IMVEC4(0x002171, 0xff); - UISecondaryTextColor = HEX_TO_IMVEC4(0xffffff, 0xff); + UIStyle.BackgroundColor = HEX_TO_IMVEC4(0x212121, 0xff); + UIStyle.BackgroundTextColor = HEX_TO_IMVEC4(0xffffff, 0xff); + UIStyle.BackgroundLineColor = HEX_TO_IMVEC4(0xf0f0f0, 0xff); + UIStyle.BackgroundHighlight = HEX_TO_IMVEC4(0x4b4b4b, 0xff); + UIStyle.PopupBackgroundColor = HEX_TO_IMVEC4(0x212121, 0xf2); + UIStyle.PrimaryColor = HEX_TO_IMVEC4(0x2e2e2e, 0xff); + UIStyle.PrimaryLightColor = HEX_TO_IMVEC4(0x484848, 0xff); + UIStyle.PrimaryDarkColor = HEX_TO_IMVEC4(0x000000, 0xff); + UIStyle.PrimaryTextColor = HEX_TO_IMVEC4(0xffffff, 0xff); + UIStyle.DisabledColor = HEX_TO_IMVEC4(0xaaaaaa, 0xff); + UIStyle.TextHighlightColor = HEX_TO_IMVEC4(0x90caf9, 0xff); + UIStyle.PrimaryLineColor = HEX_TO_IMVEC4(0xffffff, 0xff); + UIStyle.SecondaryColor = HEX_TO_IMVEC4(0x0d47a1, 0xff); + UIStyle.SecondaryStrongColor = HEX_TO_IMVEC4(0x63a4ff, 0xff); + UIStyle.SecondaryWeakColor = HEX_TO_IMVEC4(0x002171, 0xff); + UIStyle.SecondaryTextColor = HEX_TO_IMVEC4(0xffffff, 0xff); } else { // light - UIBackgroundColor = HEX_TO_IMVEC4(0xc8c8c8, 0xff); - UIBackgroundTextColor = HEX_TO_IMVEC4(0x000000, 0xff); - UIBackgroundLineColor = HEX_TO_IMVEC4(0xe1e2e1, 0xff); - UIBackgroundHighlightColor = HEX_TO_IMVEC4(0xe1e2e1, 0xff); - UIPopupBackgroundColor = HEX_TO_IMVEC4(0xd8d8d8, 0xf2); - UIPrimaryColor = HEX_TO_IMVEC4(0x2a3e78, 0xff); - UIPrimaryLightColor = HEX_TO_IMVEC4(0x235cd9, 0xff); - UIPrimaryDarkColor = HEX_TO_IMVEC4(0x1d2953, 0xff); - UIPrimaryTextColor = HEX_TO_IMVEC4(0xffffff, 0xff); - UIDisabledColor = HEX_TO_IMVEC4(0x999999, 0xff); - UITextHighlightColor = HEX_TO_IMVEC4(0x8e8e8e, 0xff); - UIPrimaryLineColor = HEX_TO_IMVEC4(0x000000, 0xff); - UISecondaryColor = HEX_TO_IMVEC4(0x2a3e78, 0xff); - UISecondaryStrongColor = HEX_TO_IMVEC4(0x464db1, 0xff); - UISecondaryWeakColor = HEX_TO_IMVEC4(0xc0cfff, 0xff); - UISecondaryTextColor = HEX_TO_IMVEC4(0x000000, 0xff); + UIStyle.BackgroundColor = HEX_TO_IMVEC4(0xc8c8c8, 0xff); + UIStyle.BackgroundTextColor = HEX_TO_IMVEC4(0x000000, 0xff); + UIStyle.BackgroundLineColor = HEX_TO_IMVEC4(0xe1e2e1, 0xff); + UIStyle.BackgroundHighlight = HEX_TO_IMVEC4(0xe1e2e1, 0xff); + UIStyle.PopupBackgroundColor = HEX_TO_IMVEC4(0xd8d8d8, 0xf2); + UIStyle.PrimaryColor = HEX_TO_IMVEC4(0x2a3e78, 0xff); + UIStyle.PrimaryLightColor = HEX_TO_IMVEC4(0x235cd9, 0xff); + UIStyle.PrimaryDarkColor = HEX_TO_IMVEC4(0x1d2953, 0xff); + UIStyle.PrimaryTextColor = HEX_TO_IMVEC4(0xffffff, 0xff); + UIStyle.DisabledColor = HEX_TO_IMVEC4(0x999999, 0xff); + UIStyle.TextHighlightColor = HEX_TO_IMVEC4(0x8e8e8e, 0xff); + UIStyle.PrimaryLineColor = HEX_TO_IMVEC4(0x000000, 0xff); + UIStyle.SecondaryColor = HEX_TO_IMVEC4(0x2a3e78, 0xff); + UIStyle.SecondaryStrongColor = HEX_TO_IMVEC4(0x464db1, 0xff); + UIStyle.SecondaryWeakColor = HEX_TO_IMVEC4(0xc0cfff, 0xff); + UIStyle.SecondaryTextColor = HEX_TO_IMVEC4(0x000000, 0xff); } } \ No newline at end of file diff --git a/src/util/imgui_fullscreen.h b/src/util/imgui_fullscreen.h index 8f1fecbfa..a9bbba568 100644 --- a/src/util/imgui_fullscreen.h +++ b/src/util/imgui_fullscreen.h @@ -43,57 +43,62 @@ static constexpr float LAYOUT_HORIZONTAL_MENU_HEIGHT = 320.0f; static constexpr float LAYOUT_HORIZONTAL_MENU_PADDING = 30.0f; static constexpr float LAYOUT_HORIZONTAL_MENU_ITEM_WIDTH = 250.0f; -extern ImFont* g_medium_font; -extern ImFont* g_large_font; +struct ALIGN_TO_CACHE_LINE UIStyles +{ + ImVec4 BackgroundColor; + ImVec4 BackgroundTextColor; + ImVec4 BackgroundLineColor; + ImVec4 BackgroundHighlight; + ImVec4 PopupBackgroundColor; + ImVec4 DisabledColor; + ImVec4 PrimaryColor; + ImVec4 PrimaryLightColor; + ImVec4 PrimaryDarkColor; + ImVec4 PrimaryTextColor; + ImVec4 TextHighlightColor; + ImVec4 PrimaryLineColor; + ImVec4 SecondaryColor; + ImVec4 SecondaryWeakColor; // Not currently used. + ImVec4 SecondaryStrongColor; + ImVec4 SecondaryTextColor; -extern float g_layout_scale; -extern float g_rcp_layout_scale; -extern float g_layout_padding_left; -extern float g_layout_padding_top; + ImFont* MediumFont; + ImFont* LargeFont; -extern ImVec4 UIBackgroundColor; -extern ImVec4 UIBackgroundTextColor; -extern ImVec4 UIBackgroundLineColor; -extern ImVec4 UIBackgroundHighlightColor; -extern ImVec4 UIPopupBackgroundColor; -extern ImVec4 UIDisabledColor; -extern ImVec4 UIPrimaryColor; -extern ImVec4 UIPrimaryLightColor; -extern ImVec4 UIPrimaryDarkColor; -extern ImVec4 UIPrimaryTextColor; -extern ImVec4 UITextHighlightColor; -extern ImVec4 UIPrimaryLineColor; -extern ImVec4 UISecondaryColor; -extern ImVec4 UISecondaryWeakColor; // Not currently used. -extern ImVec4 UISecondaryStrongColor; -extern ImVec4 UISecondaryTextColor; + float LayoutScale; + float RcpLayoutScale; + float LayoutPaddingLeft; + float LayoutPaddingTop; +}; + +extern UIStyles UIStyle; ALWAYS_INLINE static float LayoutScale(float v) { - return ImCeil(g_layout_scale * v); + return ImCeil(UIStyle.LayoutScale * v); } ALWAYS_INLINE static ImVec2 LayoutScale(const ImVec2& v) { - return ImVec2(ImCeil(v.x * g_layout_scale), ImCeil(v.y * g_layout_scale)); + return ImVec2(ImCeil(v.x * UIStyle.LayoutScale), ImCeil(v.y * UIStyle.LayoutScale)); } ALWAYS_INLINE static ImVec2 LayoutScale(float x, float y) { - return ImVec2(ImCeil(x * g_layout_scale), ImCeil(y * g_layout_scale)); + return ImVec2(ImCeil(x * UIStyle.LayoutScale), ImCeil(y * UIStyle.LayoutScale)); } ALWAYS_INLINE static float LayoutUnscale(float v) { - return ImCeil(g_rcp_layout_scale * v); + return ImCeil(UIStyle.RcpLayoutScale * v); } ALWAYS_INLINE static ImVec2 LayoutUnscale(const ImVec2& v) { - return ImVec2(ImCeil(v.x * g_rcp_layout_scale), ImCeil(v.y * g_rcp_layout_scale)); + return ImVec2(ImCeil(v.x * UIStyle.RcpLayoutScale), ImCeil(v.y * UIStyle.RcpLayoutScale)); } ALWAYS_INLINE static ImVec2 LayoutUnscale(float x, float y) { - return ImVec2(ImCeil(x * g_rcp_layout_scale), ImCeil(y * g_rcp_layout_scale)); + return ImVec2(ImCeil(x * UIStyle.RcpLayoutScale), ImCeil(y * UIStyle.RcpLayoutScale)); } ALWAYS_INLINE static ImVec4 ModAlpha(const ImVec4& v, float a) @@ -170,7 +175,7 @@ bool BeginFullscreenColumns(const char* title = nullptr, float pos_y = 0.0f, boo void EndFullscreenColumns(); bool BeginFullscreenColumnWindow(float start, float end, const char* name, - const ImVec4& background = UIBackgroundColor); + const ImVec4& background = UIStyle.BackgroundColor); void EndFullscreenColumnWindow(); bool BeginFullscreenWindow(float left, float top, float width, float height, const char* name, @@ -200,39 +205,41 @@ void ResetMenuButtonFrame(); void MenuHeading(const char* title, bool draw_line = true); bool MenuHeadingButton(const char* title, const char* value = nullptr, bool enabled = true, bool draw_line = true); bool ActiveButton(const char* title, bool is_active, bool enabled = true, - float height = LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, ImFont* font = g_large_font); + float height = LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, ImFont* font = UIStyle.LargeFont); bool DefaultActiveButton(const char* title, bool is_active, bool enabled = true, - float height = LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, ImFont* font = g_large_font); + float height = LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, ImFont* font = UIStyle.LargeFont); bool ActiveButtonWithRightText(const char* title, const char* right_title, bool is_active, bool enabled = true, - float height = LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, ImFont* font = g_large_font); + float height = LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, ImFont* font = UIStyle.LargeFont); bool MenuButton(const char* title, const char* summary, bool enabled = true, float height = LAYOUT_MENU_BUTTON_HEIGHT, - ImFont* font = g_large_font, ImFont* summary_font = g_medium_font); + ImFont* font = UIStyle.LargeFont, ImFont* summary_font = UIStyle.MediumFont); bool MenuButtonWithoutSummary(const char* title, bool enabled = true, - float height = LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, ImFont* font = g_large_font, + float height = LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, ImFont* font = UIStyle.LargeFont, const ImVec2& text_align = ImVec2(0.0f, 0.0f)); bool MenuButtonWithValue(const char* title, const char* summary, const char* value, bool enabled = true, - float height = LAYOUT_MENU_BUTTON_HEIGHT, ImFont* font = g_large_font, - ImFont* summary_font = g_medium_font); + float height = LAYOUT_MENU_BUTTON_HEIGHT, ImFont* font = UIStyle.LargeFont, + ImFont* summary_font = UIStyle.MediumFont); bool MenuImageButton(const char* title, const char* summary, ImTextureID user_texture_id, const ImVec2& image_size, bool enabled = true, float height = LAYOUT_MENU_BUTTON_HEIGHT, const ImVec2& uv0 = ImVec2(0.0f, 0.0f), const ImVec2& uv1 = ImVec2(1.0f, 1.0f), - ImFont* font = g_large_font, ImFont* summary_font = g_medium_font); + ImFont* font = UIStyle.LargeFont, ImFont* summary_font = UIStyle.MediumFont); bool FloatingButton(const char* text, float x, float y, float width = -1.0f, float height = LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, float anchor_x = 0.0f, float anchor_y = 0.0f, - bool enabled = true, ImFont* font = g_large_font, ImVec2* out_position = nullptr, + bool enabled = true, ImFont* font = UIStyle.LargeFont, ImVec2* out_position = nullptr, bool repeat_button = false); bool ToggleButton(const char* title, const char* summary, bool* v, bool enabled = true, - float height = LAYOUT_MENU_BUTTON_HEIGHT, ImFont* font = g_large_font, - ImFont* summary_font = g_medium_font); + float height = LAYOUT_MENU_BUTTON_HEIGHT, ImFont* font = UIStyle.LargeFont, + ImFont* summary_font = UIStyle.MediumFont); bool ThreeWayToggleButton(const char* title, const char* summary, std::optional* v, bool enabled = true, - float height = LAYOUT_MENU_BUTTON_HEIGHT, ImFont* font = g_large_font, - ImFont* summary_font = g_medium_font); + float height = LAYOUT_MENU_BUTTON_HEIGHT, ImFont* font = UIStyle.LargeFont, + ImFont* summary_font = UIStyle.MediumFont); bool RangeButton(const char* title, const char* summary, s32* value, s32 min, s32 max, s32 increment, const char* format = "%d", bool enabled = true, float height = LAYOUT_MENU_BUTTON_HEIGHT, - ImFont* font = g_large_font, ImFont* summary_font = g_medium_font, const char* ok_text = "OK"); + ImFont* font = UIStyle.LargeFont, ImFont* summary_font = UIStyle.MediumFont, + const char* ok_text = "OK"); bool RangeButton(const char* title, const char* summary, float* value, float min, float max, float increment, const char* format = "%f", bool enabled = true, float height = LAYOUT_MENU_BUTTON_HEIGHT, - ImFont* font = g_large_font, ImFont* summary_font = g_medium_font, const char* ok_text = "OK"); + ImFont* font = UIStyle.LargeFont, ImFont* summary_font = UIStyle.MediumFont, + const char* ok_text = "OK"); bool EnumChoiceButtonImpl(const char* title, const char* summary, s32* value_pointer, const char* (*to_display_name_function)(s32 value, void* opaque), void* opaque, u32 count, bool enabled, float height, ImFont* font, ImFont* summary_font); @@ -241,7 +248,7 @@ template ALWAYS_INLINE static bool EnumChoiceButton(const char* title, const char* summary, DataType* value_pointer, const char* (*to_display_name_function)(DataType value), CountType count, bool enabled = true, float height = LAYOUT_MENU_BUTTON_HEIGHT, - ImFont* font = g_large_font, ImFont* summary_font = g_medium_font) + ImFont* font = UIStyle.LargeFont, ImFont* summary_font = UIStyle.MediumFont) { s32 value = static_cast(*value_pointer); auto to_display_name_wrapper = [](s32 value, void* opaque) -> const char* { @@ -265,13 +272,13 @@ void DrawShadowedText(ImDrawList* dl, ImFont* font, const ImVec2& pos, u32 col, void BeginNavBar(float x_padding = LAYOUT_MENU_BUTTON_X_PADDING, float y_padding = LAYOUT_MENU_BUTTON_Y_PADDING); void EndNavBar(); -void NavTitle(const char* title, float height = LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, ImFont* font = g_large_font); +void NavTitle(const char* title, float height = LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, ImFont* font = UIStyle.LargeFont); void RightAlignNavButtons(u32 num_items = 0, float item_width = LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, float item_height = LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY); bool NavButton(const char* title, bool is_active, bool enabled = true, float width = -1.0f, - float height = LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, ImFont* font = g_large_font); + float height = LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, ImFont* font = UIStyle.LargeFont); bool NavTab(const char* title, bool is_active, bool enabled, float width, float height, const ImVec4& background, - ImFont* font = g_large_font); + ImFont* font = UIStyle.LargeFont); bool BeginHorizontalMenu(const char* name, const ImVec2& position, const ImVec2& size, u32 num_items); void EndHorizontalMenu(); diff --git a/src/util/imgui_manager.h b/src/util/imgui_manager.h index 71f97818e..e3998a811 100644 --- a/src/util/imgui_manager.h +++ b/src/util/imgui_manager.h @@ -54,7 +54,11 @@ namespace ImGuiManager { using WCharType = u32; /// Default size for screen margins. +#ifndef __ANDROID__ static constexpr float DEFAULT_SCREEN_MARGIN = 10.0f; +#else +static constexpr float DEFAULT_SCREEN_MARGIN = 16.0f; +#endif /// Sets the path to the font to use. Empty string means to use the default. void SetFontPathAndRange(std::string path, std::vector range); diff --git a/src/util/metal_device.mm b/src/util/metal_device.mm index 745606ba6..825476aae 100644 --- a/src/util/metal_device.mm +++ b/src/util/metal_device.mm @@ -51,6 +51,7 @@ static constexpr std::array(GPUTexture::Format: MTLPixelFormatBGRA8Unorm, // BGRA8 MTLPixelFormatB5G6R5Unorm, // RGB565 MTLPixelFormatBGR5A1Unorm, // RGB5A1 + MTLPixelFormatInvalid, // A1BGR5 MTLPixelFormatR8Unorm, // R8 MTLPixelFormatDepth16Unorm, // D16 MTLPixelFormatDepth24Unorm_Stencil8, // D24S8 diff --git a/src/util/opengl_context_egl.cpp b/src/util/opengl_context_egl.cpp index 43f6d8ab8..a2ddc9061 100644 --- a/src/util/opengl_context_egl.cpp +++ b/src/util/opengl_context_egl.cpp @@ -443,6 +443,9 @@ void OpenGLContextEGL::UpdateWindowInfoSize(WindowInfo& wi, EGLSurface surface) { wi.surface_width = static_cast(surface_width); wi.surface_height = static_cast(surface_height); + + if (WindowInfo::ShouldSwapDimensionsForPreRotation(wi.surface_prerotation)) + std::swap(wi.surface_width, wi.surface_height); } else { diff --git a/src/util/opengl_device.h b/src/util/opengl_device.h index fa1cae9c9..4cf1be950 100644 --- a/src/util/opengl_device.h +++ b/src/util/opengl_device.h @@ -17,12 +17,6 @@ #include #include -// Unix doesn't prevent concurrent write access, need to explicitly lock the pipeline cache. -// Don't worry about Android, it's not like you can run one more than one instance of the app there... -#if !defined(_WIN32) && !defined(__ANDROID__) -#define OPENGL_PIPELINE_CACHE_NEEDS_LOCK 1 -#endif - class OpenGLPipeline; class OpenGLStreamBuffer; class OpenGLTexture; @@ -239,7 +233,7 @@ private: bool m_timestamp_query_started = false; std::FILE* m_pipeline_disk_cache_file = nullptr; -#ifdef OPENGL_PIPELINE_CACHE_NEEDS_LOCK +#ifdef HAS_POSIX_FILE_LOCK FileSystem::POSIXLock m_pipeline_disk_cache_file_lock; #endif u32 m_pipeline_disk_cache_data_end = 0; diff --git a/src/util/opengl_pipeline.cpp b/src/util/opengl_pipeline.cpp index ec31f4147..0d9067e2f 100644 --- a/src/util/opengl_pipeline.cpp +++ b/src/util/opengl_pipeline.cpp @@ -765,9 +765,9 @@ bool OpenGLDevice::OpenPipelineCache(const std::string& path, Error* error) if (!fp) return false; -#ifdef OPENGL_PIPELINE_CACHE_NEEDS_LOCK +#ifdef HAS_POSIX_FILE_LOCK // Unix doesn't prevent concurrent write access, need to explicitly lock it. - FileSystem::POSIXLock fp_lock(fp.get(), true, error); + FileSystem::POSIXLock fp_lock(fp.get(), false, error); if (!fp_lock.IsLocked()) { Error::AddPrefix(error, "Failed to lock cache file: "); @@ -847,7 +847,7 @@ bool OpenGLDevice::OpenPipelineCache(const std::string& path, Error* error) VERBOSE_LOG("Read {} programs from disk cache.", m_program_cache.size()); m_pipeline_disk_cache_file = fp.release(); -#ifdef OPENGL_PIPELINE_CACHE_NEEDS_LOCK +#ifdef HAS_POSIX_FILE_LOCK m_pipeline_disk_cache_file_lock = std::move(fp_lock); #endif return true; @@ -855,7 +855,7 @@ bool OpenGLDevice::OpenPipelineCache(const std::string& path, Error* error) bool OpenGLDevice::CreatePipelineCache(const std::string& path, Error* error) { -#ifndef OPENGL_PIPELINE_CACHE_NEEDS_LOCK +#ifndef HAS_POSIX_FILE_LOCK m_pipeline_disk_cache_file = FileSystem::OpenCFile(path.c_str(), "w+b", error); if (!m_pipeline_disk_cache_file) return false; @@ -865,7 +865,7 @@ bool OpenGLDevice::CreatePipelineCache(const std::string& path, Error* error) if (!m_pipeline_disk_cache_file || !FileSystem::FSeek64(m_pipeline_disk_cache_file, 0, SEEK_SET, error)) return false; - m_pipeline_disk_cache_file_lock = FileSystem::POSIXLock(m_pipeline_disk_cache_file, true, error); + m_pipeline_disk_cache_file_lock = FileSystem::POSIXLock(m_pipeline_disk_cache_file, false, error); if (!m_pipeline_disk_cache_file_lock.IsLocked()) { Error::AddPrefix(error, "Failed to lock cache file: "); @@ -1015,7 +1015,7 @@ bool OpenGLDevice::DiscardPipelineCache() if (!FileSystem::FTruncate64(m_pipeline_disk_cache_file, 0, &error)) { ERROR_LOG("Failed to truncate pipeline cache: {}", error.GetDescription()); -#ifdef OPENGL_PIPELINE_CACHE_NEEDS_LOCK +#ifdef HAS_POSIX_FILE_LOCK m_pipeline_disk_cache_file_lock.Unlock(); #endif std::fclose(m_pipeline_disk_cache_file); @@ -1031,7 +1031,7 @@ bool OpenGLDevice::DiscardPipelineCache() bool OpenGLDevice::ClosePipelineCache(const std::string& filename, Error* error) { const auto close_cache = [this]() { -#ifdef OPENGL_PIPELINE_CACHE_NEEDS_LOCK +#ifdef HAS_POSIX_FILE_LOCK m_pipeline_disk_cache_file_lock.Unlock(); #endif std::fclose(m_pipeline_disk_cache_file); diff --git a/src/util/opengl_texture.cpp b/src/util/opengl_texture.cpp index 1b04fb5f7..285466b16 100644 --- a/src/util/opengl_texture.cpp +++ b/src/util/opengl_texture.cpp @@ -37,7 +37,8 @@ const std::tuple& OpenGLTexture::GetPixelFormatMapping(G {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE}, // RGBA8 {GL_RGBA8, GL_BGRA, GL_UNSIGNED_BYTE}, // BGRA8 {GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, // RGB565 - {GL_RGB5_A1, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, // RGB5A1 + {}, // RGB5A1 + {GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1}, // A1BGR5 {GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // R8 {GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_SHORT}, // D16 {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT}, // D24S8 @@ -71,7 +72,8 @@ const std::tuple& OpenGLTexture::GetPixelFormatMapping(G {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE}, // RGBA8 {GL_RGBA8, GL_BGRA, GL_UNSIGNED_BYTE}, // BGRA8 {GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, // RGB565 - {GL_RGB5_A1, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, // RGB5A1 + {}, // RGB5A1 + {GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1}, // A1BGR5 {GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // R8 {GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_SHORT}, // D16 {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT}, // D24S8 diff --git a/src/util/page_fault_handler.cpp b/src/util/page_fault_handler.cpp index f235f882d..5b4a7fc5c 100644 --- a/src/util/page_fault_handler.cpp +++ b/src/util/page_fault_handler.cpp @@ -15,7 +15,7 @@ #if defined(_WIN32) #include "common/windows_headers.h" -#elif defined(__linux__) +#elif defined(__linux__) && !defined(__ANDROID__) #include #include #include @@ -136,7 +136,7 @@ bool PageFaultHandler::Install(Error* error) return true; } -#else +#elif !defined(__ANDROID__) namespace PageFaultHandler { static void SignalHandler(int sig, siginfo_t* info, void* ctx); diff --git a/src/util/postprocessing.cpp b/src/util/postprocessing.cpp index 030a77f40..81405ca86 100644 --- a/src/util/postprocessing.cpp +++ b/src/util/postprocessing.cpp @@ -8,6 +8,7 @@ #include "postprocessing_shader.h" #include "postprocessing_shader_fx.h" #include "postprocessing_shader_glsl.h" +#include "shadergen.h" // TODO: Remove me #include "core/host.h" @@ -28,9 +29,6 @@ LOG_CHANNEL(PostProcessing); -// TODO: ProgressCallbacks for shader compiling, it can be a bit slow. -// TODO: buffer width/height is wrong on resize, need to change it somehow. - namespace PostProcessing { template static u32 ParseVector(std::string_view line, ShaderOption::ValueVector* values); @@ -369,6 +367,11 @@ PostProcessing::Chain::Chain(const char* section) : m_section(section) PostProcessing::Chain::~Chain() = default; +GPUTexture* PostProcessing::Chain::GetTextureUnusedAtEndOfChain() const +{ + return (m_stages.size() % 2) ? m_output_texture.get() : m_input_texture.get(); +} + bool PostProcessing::Chain::IsActive() const { return m_enabled && !m_stages.empty(); @@ -561,16 +564,58 @@ bool PostProcessing::Chain::CheckTargets(GPUTexture::Format target_format, u32 t if (m_target_format == target_format && m_target_width == target_width && m_target_height == target_height) return true; + Error error; + + if (!IsInternalChain() && (!m_rotated_copy_pipeline || m_target_format != target_format)) + { + const RenderAPI rapi = g_gpu_device->GetRenderAPI(); + const ShaderGen shadergen(rapi, ShaderGen::GetShaderLanguageForAPI(rapi), false, false); + const std::unique_ptr vso = g_gpu_device->CreateShader(GPUShaderStage::Vertex, shadergen.GetLanguage(), + shadergen.GenerateRotateVertexShader(), &error); + const std::unique_ptr fso = g_gpu_device->CreateShader(GPUShaderStage::Fragment, shadergen.GetLanguage(), + shadergen.GenerateRotateFragmentShader(), &error); + if (!vso || !fso) + { + ERROR_LOG("Failed to compile post-processing rotate shaders: {}", error.GetDescription()); + return false; + } + GL_OBJECT_NAME(vso, "Post-processing rotate blit VS"); + GL_OBJECT_NAME(vso, "Post-processing rotate blit FS"); + + const GPUPipeline::GraphicsConfig config = {.layout = GPUPipeline::Layout::SingleTextureAndPushConstants, + .primitive = GPUPipeline::Primitive::Triangles, + .input_layout = {}, + .rasterization = GPUPipeline::RasterizationState::GetNoCullState(), + .depth = GPUPipeline::DepthState::GetNoTestsState(), + .blend = GPUPipeline::BlendState::GetNoBlendingState(), + .vertex_shader = vso.get(), + .geometry_shader = nullptr, + .fragment_shader = fso.get(), + .color_formats = {target_format}, + .depth_format = GPUTexture::Format::Unknown, + .samples = 1, + .per_sample_shading = false, + .render_pass_flags = GPUPipeline::NoRenderPassFlags}; + m_rotated_copy_pipeline = g_gpu_device->CreatePipeline(config, &error); + if (!m_rotated_copy_pipeline) + { + ERROR_LOG("Failed to compile post-processing rotate pipeline: {}", error.GetDescription()); + return false; + } + GL_OBJECT_NAME(m_rotated_copy_pipeline, "Post-processing rotate pipeline"); + } + // In case any allocs fail. DestroyTextures(); if (!(m_input_texture = g_gpu_device->FetchTexture(target_width, target_height, 1, 1, 1, GPUTexture::Type::RenderTarget, - target_format, GPUTexture::Flags::None)) || + target_format, GPUTexture::Flags::None, nullptr, 0, &error)) || !(m_output_texture = g_gpu_device->FetchTexture(target_width, target_height, 1, 1, 1, GPUTexture::Type::RenderTarget, - target_format, GPUTexture::Flags::None))) + target_format, GPUTexture::Flags::None, nullptr, 0, &error))) { + ERROR_LOG("Failed to create input/output textures: {}", error.GetDescription()); DestroyTextures(); return false; } @@ -589,10 +634,10 @@ bool PostProcessing::Chain::CheckTargets(GPUTexture::Format target_format, u32 t progress->FormatStatusText("Compiling {}...", shader->GetName()); - if (!shader->CompilePipeline(target_format, target_width, target_height, progress) || - !shader->ResizeOutput(target_format, target_width, target_height)) + if (!shader->CompilePipeline(target_format, target_width, target_height, &error, progress) || + !shader->ResizeOutput(target_format, target_width, target_height, &error)) { - ERROR_LOG("Failed to compile one or more post-processing shaders, disabling."); + ERROR_LOG("Failed to compile post-processing shader '{}':\n{}", shader->GetName(), error.GetDescription()); Host::AddIconOSDMessage( "PostProcessLoadFail", ICON_FA_EXCLAMATION_TRIANGLE, fmt::format("Failed to compile post-processing shader '{}'. Disabling post-processing.", shader->GetName())); @@ -621,6 +666,11 @@ void PostProcessing::Chain::DestroyTextures() g_gpu_device->RecycleTexture(std::move(m_input_texture)); } +void PostProcessing::Chain::DestroyPipelines() +{ + m_rotated_copy_pipeline.reset(); +} + GPUDevice::PresentResult PostProcessing::Chain::Apply(GPUTexture* input_color, GPUTexture* input_depth, GPUTexture* final_target, GSVector4i final_rect, s32 orig_width, s32 orig_height, s32 native_width, s32 native_height) @@ -632,13 +682,24 @@ GPUDevice::PresentResult PostProcessing::Chain::Apply(GPUTexture* input_color, G if (input_depth) input_depth->MakeReadyForSampling(); + GPUTexture* draw_final_target = final_target; + const WindowInfo::PreRotation prerotation = + final_target ? WindowInfo::PreRotation::Identity : g_gpu_device->GetMainSwapChain()->GetPreRotation(); + if (prerotation != WindowInfo::PreRotation::Identity) + { + // We have prerotation and post processing. This is messy, since we need to run the shader on the "real" size, + // then copy it across to the rotated image. We can use the input or output texture from the chain, whichever + // was not the last that was drawn to. + draw_final_target = GetTextureUnusedAtEndOfChain(); + } + for (const std::unique_ptr& stage : m_stages) { const bool is_final = (stage.get() == m_stages.back().get()); if (const GPUDevice::PresentResult pres = - stage->Apply(input_color, input_depth, is_final ? final_target : output, final_rect, orig_width, orig_height, - native_width, native_height, m_target_width, m_target_height); + stage->Apply(input_color, input_depth, is_final ? draw_final_target : output, final_rect, orig_width, + orig_height, native_width, native_height, m_target_width, m_target_height); pres != GPUDevice::PresentResult::OK) { return pres; @@ -652,6 +713,30 @@ GPUDevice::PresentResult PostProcessing::Chain::Apply(GPUTexture* input_color, G } } + if (prerotation != WindowInfo::PreRotation::Identity) + { + draw_final_target->MakeReadyForSampling(); + + // Rotate and blit to final swap chain. + GPUSwapChain* const swap_chain = g_gpu_device->GetMainSwapChain(); + if (const GPUDevice::PresentResult pres = g_gpu_device->BeginPresent(swap_chain); + pres != GPUDevice::PresentResult::OK) + { + return pres; + } + + GL_PUSH_FMT("Apply swap chain pre-rotation"); + + const GSMatrix2x2 rotmat = GSMatrix2x2::Rotation(WindowInfo::GetZRotationForPreRotation(prerotation)); + g_gpu_device->SetPipeline(m_rotated_copy_pipeline.get()); + g_gpu_device->PushUniformBuffer(&rotmat, sizeof(rotmat)); + g_gpu_device->SetTextureSampler(0, draw_final_target, g_gpu_device->GetNearestSampler()); + g_gpu_device->SetViewportAndScissor(0, 0, swap_chain->GetPostRotatedWidth(), swap_chain->GetPostRotatedHeight()); + g_gpu_device->Draw(3, 0); + + GL_POP(); + } + return GPUDevice::PresentResult::OK; } @@ -674,6 +759,7 @@ void PostProcessing::Shutdown() s_samplers.clear(); ForAllChains([](Chain& chain) { chain.ClearStages(); + chain.DestroyPipelines(); chain.DestroyTextures(); }); } @@ -690,6 +776,7 @@ bool PostProcessing::ReloadShaders() ForAllChains([](Chain& chain) { chain.ClearStages(); + chain.DestroyPipelines(); chain.DestroyTextures(); chain.LoadStages(); }); diff --git a/src/util/postprocessing.h b/src/util/postprocessing.h index 11c9d9444..6c78cb12f 100644 --- a/src/util/postprocessing.h +++ b/src/util/postprocessing.h @@ -13,6 +13,7 @@ class Timer; +class GPUPipeline; class GPUSampler; class GPUTexture; @@ -117,6 +118,9 @@ public: ALWAYS_INLINE GPUTexture* GetInputTexture() const { return m_input_texture.get(); } ALWAYS_INLINE GPUTexture* GetOutputTexture() const { return m_output_texture.get(); } + /// Returns either the input or output texture, whichever isn't the destination after the final pass. + GPUTexture* GetTextureUnusedAtEndOfChain() const; + bool IsActive() const; bool IsInternalChain() const; @@ -125,6 +129,7 @@ public: void LoadStages(); void ClearStages(); void DestroyTextures(); + void DestroyPipelines(); /// Temporarily toggles post-processing on/off. void Toggle(); @@ -151,6 +156,7 @@ private: std::vector> m_stages; std::unique_ptr m_input_texture; std::unique_ptr m_output_texture; + std::unique_ptr m_rotated_copy_pipeline; }; // [display_name, filename] diff --git a/src/util/postprocessing_shader.h b/src/util/postprocessing_shader.h index 8d2c73f5d..b9137b71a 100644 --- a/src/util/postprocessing_shader.h +++ b/src/util/postprocessing_shader.h @@ -44,9 +44,10 @@ public: const ShaderOption* GetOptionByName(std::string_view name) const; ShaderOption* GetOptionByName(std::string_view name); - virtual bool ResizeOutput(GPUTexture::Format format, u32 width, u32 height) = 0; + virtual bool ResizeOutput(GPUTexture::Format format, u32 width, u32 height, Error* error) = 0; - virtual bool CompilePipeline(GPUTexture::Format format, u32 width, u32 height, ProgressCallback* progress) = 0; + virtual bool CompilePipeline(GPUTexture::Format format, u32 width, u32 height, Error* error, + ProgressCallback* progress) = 0; virtual GPUDevice::PresentResult Apply(GPUTexture* input_color, GPUTexture* input_depth, GPUTexture* final_target, GSVector4i final_rect, s32 orig_width, s32 orig_height, s32 native_width, diff --git a/src/util/postprocessing_shader_fx.cpp b/src/util/postprocessing_shader_fx.cpp index 301170e69..1abe6ad47 100644 --- a/src/util/postprocessing_shader_fx.cpp +++ b/src/util/postprocessing_shader_fx.cpp @@ -1321,7 +1321,7 @@ GPUTexture* PostProcessing::ReShadeFXShader::GetTextureByID(TextureID id, GPUTex return m_textures[static_cast(id)].texture.get(); } -bool PostProcessing::ReShadeFXShader::CompilePipeline(GPUTexture::Format format, u32 width, u32 height, +bool PostProcessing::ReShadeFXShader::CompilePipeline(GPUTexture::Format format, u32 width, u32 height, Error* error, ProgressCallback* progress) { m_valid = false; @@ -1342,31 +1342,29 @@ bool PostProcessing::ReShadeFXShader::CompilePipeline(GPUTexture::Format format, const auto& [cg, cg_language] = CreateRFXCodegen(false); - Error error; - if (!CreateModule(width, height, cg.get(), cg_language, std::move(fxcode), &error)) + if (!CreateModule(width, height, cg.get(), cg_language, std::move(fxcode), error)) { - ERROR_LOG("Failed to create module for '{}': {}", m_name, error.GetDescription()); + Error::AddPrefix(error, "Failed to create module: "); return false; } const reshadefx::effect_module& mod = cg->module(); DebugAssert(!mod.techniques.empty()); - if (!CreatePasses(format, mod, &error)) + if (!CreatePasses(format, mod, error)) { - ERROR_LOG("Failed to create passes for '{}': {}", m_name, error.GetDescription()); + Error::AddPrefix(error, "Failed to create passes: "); return false; } - auto get_shader = [cg_language, &cg](const std::string& name, const std::span samplers, - GPUShaderStage stage) { + auto get_shader = [cg_language, &cg, error](const std::string& name, const std::span samplers, + GPUShaderStage stage) { const std::string real_code = cg->finalize_code_for_entry_point(name); const char* entry_point = (cg_language == GPUShaderLanguage::HLSL) ? name.c_str() : "main"; - Error error; - std::unique_ptr sshader = g_gpu_device->CreateShader(stage, cg_language, real_code, &error, entry_point); + std::unique_ptr sshader = g_gpu_device->CreateShader(stage, cg_language, real_code, error, entry_point); if (!sshader) - ERROR_LOG("Failed to compile function '{}': {}", name, error.GetDescription()); + Error::AddPrefixFmt(error, "Failed to compile function '{}': ", name); return sshader; }; @@ -1426,10 +1424,10 @@ bool PostProcessing::ReShadeFXShader::CompilePipeline(GPUTexture::Format format, return false; } - pass.pipeline = g_gpu_device->CreatePipeline(plconfig, &error); + pass.pipeline = g_gpu_device->CreatePipeline(plconfig, error); if (!pass.pipeline) { - ERROR_LOG("Failed to create pipeline for pass '{}': {}", info.name, error.GetDescription()); + Error::AddPrefixFmt(error, "Failed to create pipeline for pass '{}': ", info.name); progress->PopState(); return false; } @@ -1444,7 +1442,7 @@ bool PostProcessing::ReShadeFXShader::CompilePipeline(GPUTexture::Format format, return true; } -bool PostProcessing::ReShadeFXShader::ResizeOutput(GPUTexture::Format format, u32 width, u32 height) +bool PostProcessing::ReShadeFXShader::ResizeOutput(GPUTexture::Format format, u32 width, u32 height, Error* error) { m_valid = false; @@ -1458,7 +1456,7 @@ bool PostProcessing::ReShadeFXShader::ResizeOutput(GPUTexture::Format format, u3 const u32 t_width = std::max(static_cast(static_cast(width) * tex.rt_scale), 1u); const u32 t_height = std::max(static_cast(static_cast(height) * tex.rt_scale), 1u); tex.texture = g_gpu_device->FetchTexture(t_width, t_height, 1, 1, 1, GPUTexture::Type::RenderTarget, tex.format, - GPUTexture::Flags::None); + GPUTexture::Flags::None, nullptr, 0, error); if (!tex.texture) return {}; } diff --git a/src/util/postprocessing_shader_fx.h b/src/util/postprocessing_shader_fx.h index 286026d14..2f20e2147 100644 --- a/src/util/postprocessing_shader_fx.h +++ b/src/util/postprocessing_shader_fx.h @@ -33,8 +33,9 @@ public: bool LoadFromFile(std::string name, std::string filename, bool only_config, Error* error); bool LoadFromString(std::string name, std::string filename, std::string code, bool only_config, Error* error); - bool ResizeOutput(GPUTexture::Format format, u32 width, u32 height) override; - bool CompilePipeline(GPUTexture::Format format, u32 width, u32 height, ProgressCallback* progress) override; + bool ResizeOutput(GPUTexture::Format format, u32 width, u32 height, Error* error) override; + bool CompilePipeline(GPUTexture::Format format, u32 width, u32 height, Error* error, + ProgressCallback* progress) override; GPUDevice::PresentResult Apply(GPUTexture* input_color, GPUTexture* input_depth, GPUTexture* final_target, GSVector4i final_rect, s32 orig_width, s32 orig_height, s32 native_width, s32 native_height, u32 target_width, u32 target_height) override; diff --git a/src/util/postprocessing_shader_glsl.cpp b/src/util/postprocessing_shader_glsl.cpp index 95ecb5423..c699b51a5 100644 --- a/src/util/postprocessing_shader_glsl.cpp +++ b/src/util/postprocessing_shader_glsl.cpp @@ -121,7 +121,7 @@ void PostProcessing::GLSLShader::FillUniformBuffer(void* buffer, s32 viewport_x, } } -bool PostProcessing::GLSLShader::CompilePipeline(GPUTexture::Format format, u32 width, u32 height, +bool PostProcessing::GLSLShader::CompilePipeline(GPUTexture::Format format, u32 width, u32 height, Error* error, ProgressCallback* progress) { if (m_pipeline) @@ -130,10 +130,10 @@ bool PostProcessing::GLSLShader::CompilePipeline(GPUTexture::Format format, u32 PostProcessingGLSLShaderGen shadergen(g_gpu_device->GetRenderAPI(), g_gpu_device->GetFeatures().dual_source_blend, g_gpu_device->GetFeatures().framebuffer_fetch); - std::unique_ptr vs = g_gpu_device->CreateShader(GPUShaderStage::Vertex, shadergen.GetLanguage(), - shadergen.GeneratePostProcessingVertexShader(*this)); - std::unique_ptr fs = g_gpu_device->CreateShader(GPUShaderStage::Fragment, shadergen.GetLanguage(), - shadergen.GeneratePostProcessingFragmentShader(*this)); + std::unique_ptr vs = g_gpu_device->CreateShader( + GPUShaderStage::Vertex, shadergen.GetLanguage(), shadergen.GeneratePostProcessingVertexShader(*this), error); + std::unique_ptr fs = g_gpu_device->CreateShader( + GPUShaderStage::Fragment, shadergen.GetLanguage(), shadergen.GeneratePostProcessingFragmentShader(*this), error); if (!vs || !fs) return false; @@ -151,7 +151,7 @@ bool PostProcessing::GLSLShader::CompilePipeline(GPUTexture::Format format, u32 plconfig.fragment_shader = fs.get(); plconfig.geometry_shader = nullptr; - if (!(m_pipeline = g_gpu_device->CreatePipeline(plconfig))) + if (!(m_pipeline = g_gpu_device->CreatePipeline(plconfig, error))) return false; if (!m_sampler) @@ -160,7 +160,7 @@ bool PostProcessing::GLSLShader::CompilePipeline(GPUTexture::Format format, u32 config.address_u = GPUSampler::AddressMode::ClampToBorder; config.address_v = GPUSampler::AddressMode::ClampToBorder; config.border_color = 0xFF000000u; - if (!(m_sampler = g_gpu_device->CreateSampler(config))) + if (!(m_sampler = g_gpu_device->CreateSampler(config, error))) return false; } @@ -201,7 +201,7 @@ GPUDevice::PresentResult PostProcessing::GLSLShader::Apply(GPUTexture* input_col return GPUDevice::PresentResult::OK; } -bool PostProcessing::GLSLShader::ResizeOutput(GPUTexture::Format format, u32 width, u32 height) +bool PostProcessing::GLSLShader::ResizeOutput(GPUTexture::Format format, u32 width, u32 height, Error* error) { return true; } diff --git a/src/util/postprocessing_shader_glsl.h b/src/util/postprocessing_shader_glsl.h index 7fc617dad..c983ffdfb 100644 --- a/src/util/postprocessing_shader_glsl.h +++ b/src/util/postprocessing_shader_glsl.h @@ -22,8 +22,9 @@ public: bool LoadFromFile(std::string name, const char* filename, Error* error); bool LoadFromString(std::string name, std::string code, Error* error); - bool ResizeOutput(GPUTexture::Format format, u32 width, u32 height) override; - bool CompilePipeline(GPUTexture::Format format, u32 width, u32 height, ProgressCallback* progress) override; + bool ResizeOutput(GPUTexture::Format format, u32 width, u32 height, Error* error) override; + bool CompilePipeline(GPUTexture::Format format, u32 width, u32 height, Error* error, + ProgressCallback* progress) override; GPUDevice::PresentResult Apply(GPUTexture* input_color, GPUTexture* input_depth, GPUTexture* final_target, GSVector4i final_rect, s32 orig_width, s32 orig_height, s32 native_width, s32 native_height, u32 target_width, u32 target_height) override; diff --git a/src/util/shadergen.cpp b/src/util/shadergen.cpp index 3bd263484..98907cbb1 100644 --- a/src/util/shadergen.cpp +++ b/src/util/shadergen.cpp @@ -357,20 +357,20 @@ void ShaderGen::WriteUniformBufferDeclaration(std::stringstream& ss, bool push_c { if (m_render_api == RenderAPI::Vulkan && push_constant_on_vulkan) { - ss << "layout(push_constant) uniform PushConstants\n"; + ss << "layout(push_constant, row_major) uniform PushConstants\n"; } else { - ss << "layout(std140, set = 0, binding = 0) uniform UBOBlock\n"; + ss << "layout(std140, row_major, set = 0, binding = 0) uniform UBOBlock\n"; m_has_uniform_buffer = true; } } else if (m_glsl) { if (m_use_glsl_binding_layout) - ss << "layout(std140, binding = 0) uniform UBOBlock\n"; + ss << "layout(std140, row_major, binding = 0) uniform UBOBlock\n"; else - ss << "layout(std140) uniform UBOBlock\n"; + ss << "layout(std140, row_major) uniform UBOBlock\n"; m_has_uniform_buffer = true; } @@ -791,6 +791,40 @@ void ShaderGen::DeclareFragmentEntryPoint( } } +std::string ShaderGen::GenerateRotateVertexShader() const +{ + std::stringstream ss; + WriteHeader(ss); + DeclareUniformBuffer(ss, { "float2 u_rotation_matrix0", "float2 u_rotation_matrix1" }, true); + DeclareVertexEntryPoint(ss, {}, 0, 1, {}, true); + ss << "{\n"; + ss << " v_tex0 = float2(float((v_id << 1) & 2u), float(v_id & 2u));\n"; + ss << " v_pos = float4(v_tex0 * float2(2.0f, -2.0f) + float2(-1.0f, 1.0f), 0.0f, 1.0f);\n"; + ss << " v_pos.xy = float2(dot(u_rotation_matrix0, v_pos.xy), dot(u_rotation_matrix1, v_pos.xy));\n"; + ss << " #if API_OPENGL || API_OPENGL_ES || API_VULKAN\n"; + ss << " v_pos.y = -v_pos.y;\n"; + ss << " #endif\n"; + ss << "}\n"; + + return ss.str(); +} + +std::string ShaderGen::GenerateRotateFragmentShader() const +{ + std::stringstream ss; + WriteHeader(ss); + DeclareTexture(ss, "samp0", 0); + DeclareFragmentEntryPoint(ss, 0, 1); + + ss << R"( +{ + o_col0 = SAMPLE_TEXTURE(samp0, v_tex0); +} +)"; + + return ss.str(); +} + std::string ShaderGen::GenerateScreenQuadVertexShader(float z /* = 0.0f */) const { std::stringstream ss; diff --git a/src/util/shadergen.h b/src/util/shadergen.h index 714acbb2b..8685ec728 100644 --- a/src/util/shadergen.h +++ b/src/util/shadergen.h @@ -25,6 +25,9 @@ public: ALWAYS_INLINE GPUShaderLanguage GetLanguage() const { return m_shader_language; } + std::string GenerateRotateVertexShader() const; + std::string GenerateRotateFragmentShader() const; + std::string GenerateScreenQuadVertexShader(float z = 0.0f) const; std::string GenerateUVQuadVertexShader() const; std::string GenerateFillFragmentShader() const; diff --git a/src/util/vulkan_device.cpp b/src/util/vulkan_device.cpp index f61fb69a7..b10eaaab8 100644 --- a/src/util/vulkan_device.cpp +++ b/src/util/vulkan_device.cpp @@ -76,6 +76,7 @@ const std::array(GPUTexture::Format::MaxCount)> Vulka VK_FORMAT_B8G8R8A8_UNORM, // BGRA8 VK_FORMAT_R5G6B5_UNORM_PACK16, // RGB565 VK_FORMAT_A1R5G5B5_UNORM_PACK16, // RGB5A1 + VK_FORMAT_B5G5R5A1_UNORM_PACK16, // A1BGR5 VK_FORMAT_R8_UNORM, // R8 VK_FORMAT_D16_UNORM, // D16 VK_FORMAT_D24_UNORM_S8_UINT, // D24S8 @@ -510,9 +511,12 @@ bool VulkanDevice::SelectDeviceExtensions(ExtensionList* extension_list, bool en { // VK_KHR_dynamic_rendering_local_read appears to be broken on RDNA3, like everything else... // Just causes GPU resets when you actually use a feedback loop. Assume Mesa is fine. + // VK_EXT_fragment_shader_interlock is similar, random GPU hangs. #if defined(_WIN32) || defined(__ANDROID__) + m_optional_extensions.vk_ext_fragment_shader_interlock = false; m_optional_extensions.vk_khr_dynamic_rendering_local_read = false; - WARNING_LOG("Disabling VK_KHR_dynamic_rendering_local_read on broken AMD driver."); + WARNING_LOG( + "Disabling VK_EXT_fragment_shader_interlock and VK_KHR_dynamic_rendering_local_read on broken AMD driver."); #endif } @@ -3406,7 +3410,7 @@ void VulkanDevice::BeginSwapChainRenderPass(VulkanSwapChain* swap_chain, u32 cle const VkRenderingInfoKHR ri = {VK_STRUCTURE_TYPE_RENDERING_INFO_KHR, nullptr, 0u, - {{}, {swap_chain->GetWidth(), swap_chain->GetHeight()}}, + {{}, {swap_chain->GetPostRotatedWidth(), swap_chain->GetPostRotatedHeight()}}, 1u, 0u, 1u, @@ -3427,7 +3431,7 @@ void VulkanDevice::BeginSwapChainRenderPass(VulkanSwapChain* swap_chain, u32 cle nullptr, m_current_render_pass, swap_chain->GetCurrentFramebuffer(), - {{0, 0}, {swap_chain->GetWidth(), swap_chain->GetHeight()}}, + {{0, 0}, {swap_chain->GetPostRotatedWidth(), swap_chain->GetPostRotatedHeight()}}, 1u, &clear_value}; vkCmdBeginRenderPass(GetCurrentCommandBuffer(), &rp, VK_SUBPASS_CONTENTS_INLINE); diff --git a/src/util/vulkan_swap_chain.cpp b/src/util/vulkan_swap_chain.cpp index 34bceaf26..334e74b62 100644 --- a/src/util/vulkan_swap_chain.cpp +++ b/src/util/vulkan_swap_chain.cpp @@ -387,9 +387,50 @@ bool VulkanSwapChain::CreateSwapChain(VulkanDevice& dev, Error* error) surface_caps.surfaceCapabilities.maxImageExtent.height); // Prefer identity transform if possible + VkExtent2D window_size = size; + WindowInfo::PreRotation window_prerotation = WindowInfo::PreRotation::Identity; VkSurfaceTransformFlagBitsKHR transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; - if (!(surface_caps.surfaceCapabilities.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)) - transform = surface_caps.surfaceCapabilities.currentTransform; + switch (surface_caps.surfaceCapabilities.currentTransform) + { + case VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR: + break; + + case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR: + transform = VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR; + window_prerotation = WindowInfo::PreRotation::Rotate90Clockwise; + std::swap(size.width, size.height); + DEV_LOG("Using VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR pretransform."); + break; + + case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR: + transform = VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR; + window_prerotation = WindowInfo::PreRotation::Rotate180Clockwise; + DEV_LOG("Using VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR pretransform."); + break; + + case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR: + transform = VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR; + window_prerotation = WindowInfo::PreRotation::Rotate270Clockwise; + std::swap(size.width, size.height); + DEV_LOG("Using VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR pretransform."); + break; + + default: + { + if (!(surface_caps.surfaceCapabilities.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)) + { + WARNING_LOG("Unhandled surface transform 0x{:X}, identity unsupported.", + static_cast(surface_caps.surfaceCapabilities.supportedTransforms)); + transform = surface_caps.surfaceCapabilities.currentTransform; + } + else + { + WARNING_LOG("Unhandled surface transform 0x{:X}", + static_cast(surface_caps.surfaceCapabilities.supportedTransforms)); + } + } + break; + } VkCompositeAlphaFlagBitsKHR alpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; if (!(surface_caps.surfaceCapabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR)) @@ -486,17 +527,18 @@ bool VulkanSwapChain::CreateSwapChain(VulkanDevice& dev, Error* error) if (old_swap_chain != VK_NULL_HANDLE) vkDestroySwapchainKHR(vkdev, old_swap_chain, nullptr); - if (size.width == 0 || size.width > std::numeric_limits::max() || size.height == 0 || - size.height > std::numeric_limits::max()) + if (window_size.width == 0 || window_size.width > std::numeric_limits::max() || window_size.height == 0 || + window_size.height > std::numeric_limits::max()) { - Error::SetStringFmt(error, "Invalid swap chain dimensions: {}x{}", size.width, size.height); + Error::SetStringFmt(error, "Invalid swap chain dimensions: {}x{}", window_size.width, window_size.height); return false; } m_present_mode = present_mode.value(); - m_window_info.surface_width = static_cast(size.width); - m_window_info.surface_height = static_cast(size.height); + m_window_info.surface_width = static_cast(window_size.width); + m_window_info.surface_height = static_cast(window_size.height); m_window_info.surface_format = VulkanDevice::GetFormatForVkFormat(surface_format->format); + m_window_info.surface_prerotation = window_prerotation; if (m_window_info.surface_format == GPUTexture::Format::Unknown) { Error::SetStringFmt(error, "Unknown surface format {}", static_cast(surface_format->format)); @@ -534,6 +576,8 @@ bool VulkanSwapChain::CreateSwapChainImages(VulkanDevice& dev, Error* error) return false; } + const u32 fb_width = GetPostRotatedWidth(); + const u32 fb_height = GetPostRotatedHeight(); m_images.reserve(image_count); m_current_image = 0; for (u32 i = 0; i < image_count; i++) @@ -565,7 +609,7 @@ bool VulkanSwapChain::CreateSwapChainImages(VulkanDevice& dev, Error* error) Vulkan::FramebufferBuilder fbb; fbb.AddAttachment(image.view); fbb.SetRenderPass(render_pass); - fbb.SetSize(m_window_info.surface_width, m_window_info.surface_height, 1); + fbb.SetSize(fb_width, fb_height, 1); if ((image.framebuffer = fbb.Create(vkdev)) == VK_NULL_HANDLE) { Error::SetStringView(error, "Failed to create swap chain image framebuffer."); diff --git a/src/util/window_info.cpp b/src/util/window_info.cpp index 543bfedf6..0fc19ef24 100644 --- a/src/util/window_info.cpp +++ b/src/util/window_info.cpp @@ -9,8 +9,31 @@ #include "common/log.h" #include "common/scoped_guard.h" +#include +#include + LOG_CHANNEL(WindowInfo); +void WindowInfo::SetPreRotated(PreRotation prerotation) +{ + if (ShouldSwapDimensionsForPreRotation(prerotation) != ShouldSwapDimensionsForPreRotation(surface_prerotation)) + std::swap(surface_width, surface_height); + + surface_prerotation = prerotation; +} + +float WindowInfo::GetZRotationForPreRotation(PreRotation prerotation) +{ + static constexpr const std::array rotation_radians = {{ + 0.0f, // Identity + static_cast(std::numbers::pi * 1.5f), // Rotate90Clockwise + static_cast(std::numbers::pi), // Rotate180Clockwise + static_cast(std::numbers::pi / 2.0), // Rotate270Clockwise + }}; + + return rotation_radians[static_cast(prerotation)]; +} + #if defined(_WIN32) #include "common/windows_headers.h" diff --git a/src/util/window_info.h b/src/util/window_info.h index 4bb672b46..2584a30fd 100644 --- a/src/util/window_info.h +++ b/src/util/window_info.h @@ -24,8 +24,17 @@ struct WindowInfo Android, }; + enum class PreRotation : u8 + { + Identity, + Rotate90Clockwise, + Rotate180Clockwise, + Rotate270Clockwise, + }; + Type type = Type::Surfaceless; GPUTexture::Format surface_format = GPUTexture::Format::Unknown; + PreRotation surface_prerotation = PreRotation::Identity; u16 surface_width = 0; u16 surface_height = 0; float surface_refresh_rate = 0.0f; @@ -35,5 +44,24 @@ struct WindowInfo ALWAYS_INLINE bool IsSurfaceless() const { return type == Type::Surfaceless; } + ALWAYS_INLINE u32 GetPostRotatedWidth() const + { + return ShouldSwapDimensionsForPreRotation(surface_prerotation) ? surface_height : surface_width; + } + ALWAYS_INLINE u32 GetPostRotatedHeight() const + { + return ShouldSwapDimensionsForPreRotation(surface_prerotation) ? surface_width : surface_height; + } + + ALWAYS_INLINE static bool ShouldSwapDimensionsForPreRotation(PreRotation prerotation) + { + return (prerotation == PreRotation::Rotate90Clockwise || prerotation == PreRotation::Rotate270Clockwise); + } + + /// Sets a new pre-rotation, adjusting the virtual width/height to suit. + void SetPreRotated(PreRotation prerotation); + + static float GetZRotationForPreRotation(PreRotation prerotation); + static std::optional QueryRefreshRateForWindow(const WindowInfo& wi, Error* error = nullptr); };