Compare commits

...
Sign in to create a new pull request.

579 commits

Author SHA1 Message Date
Henrik Rydgård
8c0b0ff0a1
Merge pull request #18466 from hrydgard/reverse-replacement-priorities
Some checks failed
Generate Docker Layer / build (push) Has been cancelled
Texture replacement: Prioritize ini file [hashes] section over just files in the "root" folder.
2023-12-03 01:47:46 +01:00
Henrik Rydgård
7f67a10543 Texture replacement: Prioritize ini file lines over files in the "root".
This reverts back to the old behavior, as mentioned in #18465
2023-12-03 00:18:39 +01:00
Henrik Rydgård
5a972be7ef Fix the display of uncompressed size of things that aren't ISO and CSO 2023-12-02 23:41:20 +01:00
Henrik Rydgård
d584162e06
Merge pull request #18462 from hrydgard/framebuffer-listing-overlay
Framebuffer listing overlay
2023-12-02 18:51:33 +01:00
Henrik Rydgård
3621529df9 Debug: Add quick "Framebuffer List" overlay. 2023-12-02 14:07:31 +01:00
Henrik Rydgård
6d977b4a12 Remove unnecessary struct FramebufferInfo 2023-12-02 13:56:18 +01:00
Henrik Rydgård
5bd9437403
Merge pull request #18461 from hrydgard/compat-allow-delayed-readback
Add a compat.ini setting to allow delayed GPU readbacks
2023-12-02 11:59:34 +01:00
Henrik Rydgård
4ef54169af Add a compat.ini setting to allow delayed GPU readbacks, for experimentation. 2023-12-02 11:34:59 +01:00
Henrik Rydgård
a981ea2ea3
Merge pull request #18457 from Narugakuruga/patch-30
Update Chinese translation
2023-12-01 23:35:22 +01:00
Henrik Rydgård
e2b1c7c8b7
Merge pull request #18454 from hrydgard/naruto-video-flicker-heuristic-take-3
Naruto Ultimate Ninja Heroes 2 video flicker fix: Take 3
2023-12-01 23:35:06 +01:00
Henrik Rydgård
b636356f36 copy: Reverse the order of the y and seq heuristics 2023-12-01 20:40:12 +01:00
Henrik Rydgård
facc82cc01
Merge pull request #18455 from DDinghoya/patch-18
Update ko_KR.ini
2023-12-01 17:30:19 +01:00
Narugakuruga
7152ba7262
Update zh_CN.ini 2023-12-01 22:44:40 +08:00
Narugakuruga
651645d2ac
Update zh_CN.ini 2023-12-01 22:42:52 +08:00
DDinghoya
82e00dc564
Update ko_KR.ini 2023-12-01 22:03:07 +09:00
DDinghoya
6a075be663
Update ko_KR.ini 2023-12-01 21:57:40 +09:00
DDinghoya
08e1a9f56b
Update ko_KR.ini 2023-12-01 09:48:30 +09:00
Henrik Rydgård
cef17589d2 Move the oversize copy detection to a better location (less false positive) 2023-12-01 00:30:06 +01:00
Henrik Rydgård
d9365a6df1 FramebufferCopy: New framebuffer candidate sorting, similar to block transfer detection.
The previous attempt was simply flawed.
2023-12-01 00:10:16 +01:00
Henrik Rydgård
7920e86098 Add heuristic, fixing video flicker in Naruto UNH 2 caused by copy to wrong target. 2023-11-30 22:19:52 +01:00
Henrik Rydgård
c3f881b7af
Merge pull request #18453 from hrydgard/retroachievements-updates
Retroachievements: Rename Challenge Mode, default to on
2023-11-30 22:19:31 +01:00
Henrik Rydgård
b629c99dbf Change "Challenge Mode" to "Hardcore Mode", according to RetroAchievements guidelines 2023-11-30 18:11:03 +01:00
Henrik Rydgård
f6dcf6e834 RetroAchievements: Default ChallengeMode to true 2023-11-30 17:43:36 +01:00
Henrik Rydgård
75a59d100a langtool: When renaming the key of untranslated strings, also change the value. 2023-11-30 17:43:33 +01:00
Henrik Rydgård
737ec3e90b NEON buildfix 2023-11-28 18:40:10 +01:00
Henrik Rydgård
8ad0ef6901
Merge pull request #18450 from hrydgard/neon-arm32
Enable some NEON optimizations on ARM32 that we only had on ARM64 before
2023-11-28 00:28:39 +01:00
Henrik Rydgård
4ec2d76bc9 NEON-optimize matrix tranposes 2023-11-27 23:57:26 +01:00
Henrik Rydgård
45aae7b9da ARM32: Backport a lot of previously 64-bit-only NEON optimizations to ARM32. 2023-11-27 23:51:10 +01:00
Henrik Rydgård
d58f826c8d
Merge pull request #18430 from Kethen/writedata
Handle SCE_UTILITY_SAVEDATA_TYPE_SAVE then SCE_UTILITY_SAVEDATA_TYPE_WRITEDATA
2023-11-27 22:15:21 +01:00
Henrik Rydgård
04c6b1ac99
Merge pull request #18442 from hrydgard/time-tracking
Track time-played per game
2023-11-27 22:12:45 +01:00
Henrik Rydgård
a9c1bc2ce7 Add translation string, show uncompressed size if different from disk size 2023-11-27 01:11:43 +01:00
Henrik Rydgård
55fdeb93ff Change GameScreen layout so that the game time can fit 2023-11-26 21:58:21 +01:00
Henrik Rydgård
707670cfcf Fix time tracking during pause 2023-11-26 19:49:21 +01:00
Henrik Rydgård
4d94b7006a
Merge pull request #18448 from hrydgard/chd-path-pattern-android
Android: Add *.chd and *.CHD to allowed path patterns for shortcut/intents
2023-11-26 19:37:01 +01:00
Henrik Rydgård
b1bc6caaf6 Add a screen focus-tracking mechanism 2023-11-26 19:30:37 +01:00
Henrik Rydgård
31c85ae0a5 Add the basics of a played-time tracker. 2023-11-26 19:15:38 +01:00
Henrik Rydgård
7558544183 Android: Add *.chd and *.CHD to allowed path patterns for shortcut/intents 2023-11-26 19:08:09 +01:00
Henrik Rydgård
aa4ddbe9ae Update build.gradle yet again 2023-11-26 19:05:46 +01:00
Henrik Rydgård
68acd3d96a
Merge pull request #18445 from sum2012/compat
Make 3 games into compat
2023-11-26 16:47:44 +01:00
Henrik Rydgård
77e3f1185f
Merge pull request #18446 from hrydgard/minor-bbox-opt
Minor bbox optimizations, assorted bugfixes
2023-11-26 16:44:29 +01:00
Henrik Rydgård
dae758e5f4 Fix some bugs and mistakes found by Nemoumbra through static analysis 2023-11-26 13:43:11 +01:00
Henrik Rydgård
aec0606ba4 Optimize the bounding box code for more vertex formats 2023-11-26 13:40:37 +01:00
Henrik Rydgård
b76d519682 Minor langtool update 2023-11-26 11:43:40 +01:00
sum2012
7b3596b906 Add infomation for qix 2023-11-26 14:29:23 +08:00
sum2012
6a93a18873 Make 3 games into compat
fix#6557 fix #12434 fix #5545
2023-11-26 14:24:47 +08:00
Henrik Rydgård
d6324d10a6
Merge pull request #18438 from lvonasek/hotfix_quest3
OpenXR - Projection matrix on Quest 3 fixed
2023-11-23 22:39:24 +01:00
Lubos
4b8ed69a37 OpenXR - Texts cleanup 2023-11-23 21:57:46 +01:00
Henrik Rydgård
69e22fb2f5
Merge pull request #18439 from sum2012/djmax
Update Directory for DJmax
2023-11-23 09:24:22 +01:00
sum2012
d0fced8ef1 Update Directory for DJmax 2023-11-22 21:33:18 +08:00
Henrik Rydgård
4d7f00f968
Merge pull request #18436 from sum2012/compat
Add Doko Demo Issho ,Driver 76, 	Harukanaru Toki no Naka de 3 with Izayoiki Aizouban into ForceUMDReadSpeed
2023-11-22 09:12:43 +01:00
Henrik Rydgård
3d59db02bc
Merge pull request #18433 from joolswills/gles2_astc_fix
Fix building on OpenGL ES 2.0 (RPI VideoCore IV)
2023-11-22 00:27:37 +01:00
Lubos
4d61896403 OpenXR - Projection matrix on Quest 3 fixed 2023-11-21 23:07:58 +01:00
Jools Wills
82ce83185e Fix building on OpenGL ES 2.0 (RPI VideoCore IV)
GL_COMPRESSED_RGBA_ASTC_4x4_KHR is not defined and looks to be an OpenGL ES 3.0 extension.

This fixes building on the Raspberry Pi 1-3 using VideoCore IV OpenGL ES 2.0 headers.
2023-11-21 20:16:09 +00:00
sum2012
006d8ecb66 Add Harukanaru Toki no Naka de 3 with Izayoiki Aizouban
fix #6127
2023-11-22 01:27:28 +08:00
sum2012
2353fdd414 oop 2023-11-21 19:42:12 +08:00
sum2012
933a51f2ff Add Doko Demo Issho and Driver '76 into ForceUMDReadSpeed
Fix #18420 Fix #12054
2023-11-20 21:48:09 +08:00
Henrik Rydgård
3d508ef282
Merge pull request #18435 from anr2me/adhocctl
An attempt to fix Tekken 6 stuck issue when exiting Lobby
2023-11-20 14:41:40 +01:00
AdamN
c6628ee55c
An attempt to fix Tekken 6 stuck issue when exiting Lobby 2023-11-20 19:36:26 +07:00
Henrik Rydgård
ecab503461 compat: Enable ForceUMDReadSpeed for Bejeweled 2, as a temp workaround. See issue #15304
See issue #15304
2023-11-19 18:53:28 +01:00
Henrik Rydgård
6b86cabd0c
Merge pull request #18432 from danssmnt/master
Updated pt-PT translation
2023-11-19 18:48:11 +01:00
danssmnt
b2a759c711
Updated pt-PT translation 2023-11-19 16:27:53 +00:00
Katharine Chui
652498e995 Handle SCE_UTILITY_SAVEDATA_TYPE_SAVE then SCE_UTILITY_SAVEDATA_TYPE_WRITEDATA
Modnation racers does a normal save for global profile, then
writes an insecure file for keeping track of selected language.
During SCE_UTILITY_SAVEDATA_TYPE_WRITEDATA it also provides no
data for param.sfo, so I'm assuming that utility.prx does not
update param.sfo data in that mode since the PSP save provided on
the issue tracker has information.
2023-11-18 03:48:01 +08:00
Henrik Rydgård
74a13b0ca4
Merge pull request #18428 from hrydgard/savestate-challenge-mode-fixes
Forgot some cases where I need to enable save (but not load) state in challenge mode, if the option is set
2023-11-16 20:39:21 +01:00
Henrik Rydgård
bd08fdd566 Forgot some cases where I need to enable save (but not load) state in challenge mode, if the option is set 2023-11-16 20:13:47 +01:00
Henrik Rydgård
07e0b963f9 Doko Demo Issho: Add to compat.ini under [ReportSmallMemstick]
Reported by IrfanH495 in issue #18420
2023-11-16 09:22:53 +01:00
Henrik Rydgård
211c23e004 Remove the axis event filtering from Screen.cpp again. See #18368 2023-11-16 09:20:40 +01:00
Henrik Rydgård
4be1706876 SDL: Add axis event deduplication
We now do this in the backedns instead of centrally since on some backends this is more efficient.
2023-11-16 09:20:40 +01:00
Henrik Rydgård
931fdfbd55
Merge pull request #18425 from hrydgard/save-state-challenge-mode
RetroAchievements: Add option to allow saving, but not loading, in challenge / hardcore mode.
2023-11-15 00:16:14 +01:00
Henrik Rydgård
4c315bacdf RetroAchievements: Add option to allow saving, but not loading, in challenge / hardcore mode.
This has been requested many times and is useful for development, so
let's simply allow it. It doesn't enable cheating for achievemenst since
you still can't load these states in challenge mode.
2023-11-14 22:54:39 +01:00
Henrik Rydgård
50d232d4aa
Merge pull request #18421 from hrydgard/string-view-opts
String view optimizations (code cleanup)
2023-11-14 22:34:34 +01:00
Henrik Rydgård
1dea3f80eb
Merge pull request #18424 from Narugakuruga/patch-29
Update zh_CN.ini
2023-11-14 16:25:56 +01:00
Narugakuruga
d7323c88b8
Update zh_CN.ini
new strings and optimization
2023-11-14 23:13:43 +08:00
Henrik Rydgård
1da6da446b More std::string conversion 2023-11-13 23:43:57 +01:00
Henrik Rydgård
19eeaef2ea More uses of string_view 2023-11-13 23:36:34 +01:00
Henrik Rydgård
d0ee5fc308 Path: Use string_view more to avoid allocations 2023-11-13 23:24:42 +01:00
Henrik Rydgård
70c7bb3b0d
Merge pull request #18419 from hrydgard/gamelist-triangle-button-cleanup
Clean up the checks for triangle-button-for-info on the main screen
2023-11-13 14:13:51 +01:00
Henrik Rydgård
cb9c6dc661
Merge pull request #18418 from hrydgard/simplify-input-layout
thin3d/backends: Remove code that pretended that we supported multiple vertex streams
2023-11-13 12:51:09 +01:00
Henrik Rydgård
2910303d20 Clean up the checks for triangle-button-for-info on the main screen 2023-11-13 12:49:08 +01:00
Henrik Rydgård
802bae6ee8 Fix minor perf problem in debug mode, navigating long game lists.
InputMappingToPspButton is very slow and was called once per item.
2023-11-13 12:47:28 +01:00
Henrik Rydgård
19205145e5
Merge pull request #18414 from sum2012/atrac_steam_loop_full
Fix atrac sound loop problem
2023-11-13 12:04:02 +01:00
Henrik Rydgård
d891aaf9cd Remove code that pretended that we supported multiple vertex streams
Don't really see that we'll have much use for this feature, so simplify
it away. Only single vertex stream data is now supported by the thin3d
API.
2023-11-13 01:15:28 +01:00
Henrik Rydgård
bfd11153db
Merge pull request #18416 from hrydgard/drawpixels-16-bit-opt
Optimize DrawPixels for 16-bit RGB565 copies
2023-11-13 00:44:46 +01:00
Henrik Rydgård
77825484a0 If available, use 16-bit texture formats for MakePixelTexture when appropriate.
Optimization for God of War on low-end platforms. Avoids calling a color
conversion function that's currently only SIMD-optimized on x86, so will
also benefit ARM a little bit.
2023-11-12 15:58:03 +01:00
Henrik Rydgård
49f5da370a Simplify the logic in MakePixelTexture a bit 2023-11-12 11:19:45 +01:00
sum2012
dfec953f1d Fix atrac sound loop problem
Fix #14058 fix #14352
2023-11-12 12:18:00 +08:00
Henrik Rydgård
cc6f9a73ca Oops, fix for previous commit. And minor optimization. 2023-11-12 01:32:02 +01:00
Henrik Rydgård
2530bc88af
Merge pull request #18411 from Kethen/allow_plugin_blacklist
allow `ALL = true` then `gameID = false` blacklisting
2023-11-11 23:49:27 +01:00
Henrik Rydgård
b87510115e
Merge pull request #18413 from hrydgard/cache-drawpixels
Cache and hash data for DrawPixels
2023-11-11 20:45:56 +01:00
Henrik Rydgård
632fa1c9d6 Cache and hash data for DrawPixels.
We already had a cache to reuse texture objects so just
opportunistically reuse them when easy to do so.
2023-11-11 19:58:12 +01:00
Katharine Chui
030a031943 allow ALL = true then gameID = false blacklisting 2023-11-11 20:43:46 +08:00
Henrik Rydgård
3287c268ff
Merge pull request #18408 from hrydgard/ui-refactoring-1
UI: Remove unnecessary mutexes, assorted cleanup
2023-11-11 12:08:39 +01:00
Henrik Rydgård
d583da9d81
Merge pull request #18409 from DDinghoya/patch-17
Update ko_KR.ini
2023-11-11 12:07:56 +01:00
DDinghoya
80f2d72817
Update ko_KR.ini 2023-11-11 19:58:08 +09:00
Henrik Rydgård
5146a2a967 Remove modifyLock_ from ViewGroup 2023-11-11 11:25:41 +01:00
Henrik Rydgård
d4833b72b1 Fix the last place where we were processing UI input events on the wrong thread 2023-11-11 11:21:36 +01:00
Henrik Rydgård
96cfdbaa96 Some more XInput cleanup 2023-11-11 11:08:22 +01:00
Henrik Rydgård
dd032dc533 Delete two unused structs 2023-11-11 10:55:54 +01:00
Henrik Rydgård
0c7b42b079 UI: Remove locks in event dispatching 2023-11-11 10:55:45 +01:00
Henrik Rydgård
e0c0cb9a6d Change loops to the shorter form. 2023-11-11 10:55:45 +01:00
Henrik Rydgård
57b96250ef Merge branch 'xinput_trigger_deadzone' 2023-11-11 10:51:54 +01:00
Henrik Rydgård
004d8fc20e Remove unused code 2023-11-11 10:51:41 +01:00
Katharine Chui
cdd6f2f5c9 skip xinput trigger threshold check 2023-11-11 16:03:08 +08:00
Henrik Rydgård
2036f1c36a
Merge pull request #18403 from hrydgard/tilt-fixes
Fixes for tilt control bugs
2023-11-09 21:24:44 +01:00
Henrik Rydgård
4f2f1c4392 Tilt: Fix some edge cases leading to division by zero and similar. 2023-11-09 19:14:31 +01:00
Henrik Rydgård
25ab1206b5 Fix stepping of tilt low end radius setting. Add some asserts. 2023-11-06 18:33:05 -06:00
Henrik Rydgård
73932603e3 IniFile: Remove redundant function, add a debug assert 2023-11-06 18:33:05 -06:00
Henrik Rydgård
c5469c409a Add specific sysprop for accelerometer 2023-11-06 18:33:01 -06:00
Henrik Rydgård
5a72e2d6d2
Merge pull request #18395 from hrydgard/bump-rcheevos-again
Update the rcheevos submodule to the new v11.0.0
2023-11-06 18:27:12 -06:00
Henrik Rydgård
949dc97e3a
Merge pull request #18397 from sum2012/lang
Add Simulate UMD slow reading speed translate
2023-11-05 07:27:53 -06:00
sum2012
56163469dc
Update Japanese by @nishinji
Co-authored-by: nishinji <107111782+nishinji@users.noreply.github.com>
2023-11-05 20:55:50 +08:00
sum2012
4dcf203b15 Add Simulate UMD slow reading speed transate
Chinese is my natural language.
For Japanese,I use ChatGPT
2023-11-05 13:24:37 +08:00
Henrik Rydgård
6d063ce3ea
Merge pull request #18372 from iota97/empty-shape
Add empty shape to custom keys
2023-11-03 12:33:42 -06:00
iota97
9fe618840a add empty shape to custom keys 2023-11-03 16:44:08 +01:00
Henrik Rydgård
70999dc8f2
Merge pull request #18369 from sum2012/UmdReadSpeed
Add Simulate UMD slow reading speed in UI
2023-11-03 08:33:50 -06:00
Henrik Rydgård
ede0f093e1
Merge pull request #18392 from sum2012/AtracInfo
Record Atrac library version
2023-11-03 08:31:00 -06:00
Henrik Rydgård
d0d9ce33a4 Update the rcheevos submodule to the new v11.0.0 2023-11-03 08:17:27 -06:00
sum2012
fad262e810 oop 2023-11-03 03:52:18 +08:00
sum2012
a41a1dcfff Fix indentation 2023-11-02 20:42:03 +08:00
sum2012
b3527fe1be Move to _AtracSetData
Fix before LoadModule
2023-11-02 20:37:20 +08:00
Henrik Rydgård
157fabf91b
Merge pull request #18394 from hrydgard/driver76-fix-new-crash
Ignore triangle strips with less than 3 vertices.
2023-11-01 22:13:14 -06:00
Henrik Rydgård
48a1348352 Move a var for clarity 2023-11-01 21:30:04 -06:00
Henrik Rydgård
ee6ffac28e Ignore triangle strips with less than 3 vertices.
Should fix the new issue reported in #18273
2023-11-01 21:28:37 -06:00
sum2012
9869267160 oop 2023-11-01 06:33:08 +08:00
Henrik Rydgård
a4d667caf8
Merge pull request #18391 from kotcrab/cmake-winhttp
Add winhttp for cmake Windows builds
2023-10-31 16:30:36 -06:00
sum2012
199f0dd704 Record Atrac libersion
Gripshift library version 0x101 ,crc 3dd5e32f
2023-11-01 06:29:55 +08:00
kotcrab
e07495502e Add winhttp for cmake Windows builds 2023-10-31 22:58:59 +01:00
Henrik Rydgård
b01ec488d6
Merge pull request #18389 from hrydgard/disable-negative-messages
Redump checks: Disable negative messages
2023-10-30 07:54:58 -06:00
Henrik Rydgård
9745995e4b Redump checks: Disable negativem messages
The current database has issues as mentioned in #18387. Until this is
resolved and until we have confidence in the DB, don't be so loud
about ISOs possibly being bad.
2023-10-30 00:22:49 -06:00
Henrik Rydgård
8ce639fddf
Merge pull request #18386 from hrydgard/crc-progress-bar
CRC calculation on game screen: Show progress bar
2023-10-30 00:15:26 -06:00
Henrik Rydgård
ce83fec206 Linux buildfix attempt 2023-10-29 23:39:25 -06:00
sum2012
7092393ed7 Space bug 2023-10-29 12:40:20 +08:00
sum2012
8613c685eb Change timing in __IoRead
Test in Aces of War (Europe) and Sengoku Musou 3 Z Special
2023-10-29 12:38:52 +08:00
Henrik Rydgård
391d45cc03 Typo fix, see #18384 2023-10-28 06:51:27 -05:00
Henrik Rydgård
93c0ef68b6 Add progress bar to CRC calculation 2023-10-28 06:50:57 -05:00
Henrik Rydgård
8e43a9a204
Merge pull request #18384 from Felipefpl/master
Updated brazilian portuguese translation
2023-10-27 19:49:15 -05:00
Felipe
bb3934f2fb
Add files via upload 2023-10-27 21:01:29 -03:00
Felipe
07799ad8a3
Updated brazilian portuguese translation
Updated translation with the latest strings.
2023-10-27 20:55:28 -03:00
Henrik Rydgård
a9d2ff29ea
Merge pull request #18377 from hrydgard/redump-verification
Game info screen: Add checks against the Redump database
2023-10-26 13:12:24 -05:00
Henrik Rydgård
f3c2f22199 Add the new strings to all the ini files 2023-10-26 00:19:05 -05:00
Henrik Rydgård
e0ada6e5ba Cleanup, add the new strings to en_US.ini 2023-10-26 00:18:05 -05:00
sum2012
aa5a3edda8 Japanese version need
Last time I test wrong
2023-10-25 05:39:51 +08:00
Henrik Rydgård
b71e44ca40 More use of string_view in StringUtils 2023-10-24 08:08:20 -05:00
sum2012
84d3bff507 Delay smaller 2023-10-24 20:06:30 +08:00
Henrik Rydgård
23ce34ef78 Add redump game database 2023-10-23 19:11:04 -05:00
Henrik Rydgård
87ddb3f3b1 Add easy way to verify games against the Redump database, supplied as CSV. 2023-10-23 19:11:01 -05:00
Henrik Rydgård
42dd37352f Add missing line to save the sticky d-pad settting. Oops. 2023-10-23 15:50:27 -05:00
Henrik Rydgård
99f3d21e31
Merge pull request #18376 from Nemoumbra/encoding-fix
DNS resolver warning logs fix
2023-10-23 08:24:58 -05:00
sum2012
6a844dd985 Add more delay as ForceUMDDelay
Also add setting in libretro
2023-10-23 20:03:42 +08:00
sum2012
92c1eb4efa Add it to option
Also fix Sengoku Musou 3Z Special DLC
2023-10-23 19:38:09 +08:00
Nemoumbra
7b91e0d626 Fixed the encoding of the culture-dependent DNS resolver error messages (Windows) 2023-10-23 12:04:49 +03:00
sum2012
079452001e Japanese version no need 2023-10-19 20:51:29 +08:00
sum2012
60ddac0048 Add UMDReadSpeed in compat.ini
Fix #11062
2023-10-19 06:21:05 +08:00
Henrik Rydgård
e93f9f64cc
Merge pull request #18368 from hrydgard/revert-joystick-change
Revert "Control: Remove the axis event dupe filtering from ScreenManager"
2023-10-18 23:17:11 +02:00
Henrik Rydgård
0fb318c8e9 Revert "Control: Remove the axis event dupe filtering from ScreenManager"
This reverts commit 265a9021fd.
2023-10-18 22:48:23 +02:00
Henrik Rydgård
e2f6b36a19 Add compat.ini entry for Qix under ForceUMDDelay.
See issue #13724
2023-10-17 15:25:50 +02:00
Henrik Rydgård
fd55f1f4a8 compat.ini: Add some missing game IDs for Burnout Dominator 2023-10-17 10:31:05 +02:00
Henrik Rydgård
53df5093d7 Update Spanish translations for "Screen layout and effects"
Got advice on Discord, heh.
2023-10-16 15:23:24 +02:00
Henrik Rydgård
3611b4d94b
Merge pull request #18363 from unknownbrackets/gamedata-install
Savedata: Fix gamedata install shutdown status
2023-10-15 22:54:20 +02:00
Henrik Rydgård
533490848f
Merge pull request #18364 from warmenhoven/pr/tvos
libretro: build tvos with ffmpeg
2023-10-15 22:53:50 +02:00
Henrik Rydgård
618589abd8
Merge pull request #18362 from unknownbrackets/softgpu-zmirror
softgpu: Point depthbuf at the first VRAM mirror
2023-10-15 22:53:00 +02:00
Eric Warmenhoven
39e2d2accd libretro: build tvos with ffmpeg 2023-10-15 14:31:09 -04:00
Unknown W. Brackets
624d5a38e7 Savedata: Fix gamedata install shutdown status.
This is the only one that returns non-errors when the dialog is disabled.
2023-10-15 10:36:27 -07:00
Unknown W. Brackets
f7f05072fe softgpu: Point depthbuf at the first VRAM mirror. 2023-10-15 10:33:05 -07:00
Henrik Rydgård
5580d47d87 Bump ffmpeg submodule 2023-10-15 07:58:30 +02:00
Henrik Rydgård
8ff38d0a9b
Merge pull request #18360 from unknownbrackets/arm64jit-fixes
Fix some arm64jit issues
2023-10-15 07:34:03 +02:00
Unknown W. Brackets
5e813e6bd6 irjit: Correct bad Vec4 overlap handling. 2023-10-14 20:54:40 -07:00
Unknown W. Brackets
2a24c99441 arm64jit: Correct FlushBeforeCall pairing.
Oops, some silly mistakes here.
2023-10-14 20:54:40 -07:00
Unknown W. Brackets
b85b0476b9 arm64jit: Correct vdot vec4 mapping. 2023-10-14 20:54:40 -07:00
Henrik Rydgård
54ad7d0bb8
Merge pull request #18358 from hrydgard/upgrade-rcheevos
Update rcheevos again
2023-10-14 23:54:21 +02:00
Henrik Rydgård
2c795e701f Update rcheevos again. This time there are some files moving around. 2023-10-14 23:23:48 +02:00
Henrik Rydgård
1e6142d99b
Merge pull request #18355 from hrydgard/merge-1.16.6
Merge the 1.16.6 branch into master
2023-10-13 13:55:03 +02:00
Henrik Rydgård
ac93419331 Merge the 1.16.6 branch into master
Also deletes some unused code.
2023-10-13 11:00:11 +02:00
Henrik Rydgård
a196c5e7d4
Merge pull request #18351 from hrydgard/shutdown-fixes-better
Better fix for shutdown crash
2023-10-13 02:31:00 +02:00
Henrik Rydgård
88451f0476
Merge pull request #18352 from LeSaucee/patch-1
Update fr_FR.ini
2023-10-13 02:18:19 +02:00
LeSaucee
4930da5568
Update fr_FR.ini
Added some french translation to the file :D
2023-10-12 17:44:33 -04:00
Henrik Rydgård
6357b95ff5 Better version. 2023-10-12 23:33:54 +02:00
Henrik Rydgård
f301035ba0 Step 1 2023-10-12 23:28:44 +02:00
Henrik Rydgård
6f28c5d660
Merge pull request #18346 from hrydgard/assorted-cleanups-15
Assorted cleanups
2023-10-12 17:14:47 +02:00
Henrik Rydgård
ba0ce34493 v1.16.6 2023-10-12 12:23:28 +02:00
Henrik Rydgård
1dd98c5a55 Update UWP version 2023-10-12 12:22:38 +02:00
Henrik Rydgård
f842f16fbe Inline "DecodeVertexToPushPool" for ease of change. 2023-10-12 11:58:49 +02:00
Henrik Rydgård
12a98baf59 Cleanups, make the various SubmitPrim implementations more similar 2023-10-12 11:58:48 +02:00
Henrik Rydgård
6dbe49775c Add a sysprop for IsDebuggerPresent. 2023-10-12 11:58:04 +02:00
Henrik Rydgård
f1bc547e94 IconCache: Make valgrind happy (zero some struct padding bytes before writing to disk) 2023-10-12 10:51:14 +02:00
Henrik Rydgård
fc9a057083 Update README.md 2023-10-12 10:22:41 +02:00
Henrik Rydgård
a507563708 Remove the exit-game sync again.
It's not actually needed until some of the additions made on master, and
in that case, I'm going to change the solution again.
2023-10-12 10:14:19 +02:00
Henrik Rydgård
ae4f48ced3
Merge pull request #18344 from hrydgard/shutdown-fixes
Vulkan: Fix synchronization when shutting the app down in-game.
2023-10-12 09:49:14 +02:00
Henrik Rydgård
2c751d39f8 Vulkan: Add simple delete count to GPU profiler, to make sure we don't have more resource churn
Also fix the render pass type name lookup table.
2023-10-12 09:02:45 +02:00
Henrik Rydgård
028c7c3ea8 Android: Fix crashes on screen rotation 2023-10-11 16:44:30 +02:00
Henrik Rydgård
d60c9a015f Vulkan: Fix synchronization when shutting the GPU down in-game. 2023-10-11 12:49:33 +02:00
Henrik Rydgård
f769b2c8a3 Remove unused functionality from descpool 2023-10-11 12:29:57 +02:00
Henrik Rydgård
0ad2827e14 Vulkan: Fix synchronization when shutting the GPU down in-game. 2023-10-11 12:27:39 +02:00
Henrik Rydgård
e26bf611e0
Merge pull request #18343 from hrydgard/minor-descriptor-opt
Vulkan: Avoid copying descriptors unnecessarily
2023-10-11 12:27:00 +02:00
Henrik Rydgård
183d49329a Allow writing directly into the packed descriptor buffer, saving a memcpy. 2023-10-11 11:02:17 +02:00
Henrik Rydgård
f931f85d57 Fix outdated renderpass name lookup table (debug info) 2023-10-11 10:34:20 +02:00
Henrik Rydgård
bdff93374b
Merge pull request #18339 from hrydgard/delete-vertex-cache
Remove the vertex cache option
2023-10-11 10:02:30 +02:00
Henrik Rydgård
2ac14f555d Remove VulkanPushBuffer (keeping our newer replacement VulkanPushPool) 2023-10-11 09:06:24 +02:00
Henrik Rydgård
52dae5c9a7 README.md update for 1.16.6 2023-10-10 23:44:23 +02:00
Henrik Rydgård
a3c2d058e0 Fix main-thread stalls due to decimate during replacement texture loading 2023-10-10 19:33:04 +02:00
Henrik Rydgård
51fd502d7d Update rcheevos 2023-10-10 19:15:59 +02:00
Henrik Rydgård
c362c159d7 VulkanDescSetPool: Don't forget to increment usage 2023-10-10 19:14:19 +02:00
Henrik Rydgård
31f28a3eaf Fix the CMake expression, thanks Halo-Michael 2023-10-10 19:12:56 +02:00
Henrik Rydgård
a9987c6885 Turn off HTTPS support for iOS.
Should fix #18322
2023-10-10 19:12:56 +02:00
Henrik Rydgård
8d59e30b53 Gradle: Fix some deprecation warnings 2023-10-10 19:12:24 +02:00
Henrik Rydgård
37de5b9d0d Move the menu frame-rate throttling to NativeFrame
Now needed on Android since we added the ability to turn off vsync,
which caused the menu to burn battery by rendering too fast.
2023-10-10 19:11:48 +02:00
Henrik Rydgård
d00928e1db Guaranteed gap-free rendering of waves background 2023-10-10 19:11:35 +02:00
Henrik Rydgård
89effbf498 Revert "UI: Round wave coords to prevent gaps."
This reverts commit ebf9de7864.
2023-10-10 19:11:35 +02:00
aeiouaeiouaeiouaeiouaeiouaeiou
15b6a48070 Update Russian translation 2023-10-10 19:11:28 +02:00
Henrik Rydgård
833fc35462 Reduce refresh rate checks.
These turned out to be unexpectedly expensive, so cache the value and
also try to check it a bit less.
2023-10-10 19:11:28 +02:00
Unknown W. Brackets
3236f54693 x86: Fix 32-bit IR jit block entry. 2023-10-10 19:11:28 +02:00
Henrik Rydgård
358cfd4bc6 Prevent duplicate alternate-speed status messages 2023-10-10 19:11:28 +02:00
Unknown W. Brackets
c53c33c0c2 irjit: Fix vhtfm instruction. 2023-10-10 19:11:27 +02:00
Henrik Rydgård
a2129856a9 Remove vertex-cache related translation strings 2023-10-10 15:43:45 +02:00
Henrik Rydgård
e4ea4831e9 Delete the vertex cache option from the code. 2023-10-10 15:43:43 +02:00
Henrik Rydgård
7d43a4940d
Merge pull request #18340 from hrydgard/cullmode-cleanup
Cullmode cleanup (for mixed-culling draw calls)
2023-10-10 15:41:58 +02:00
Henrik Rydgård
078018a943 Move the clockwise calculation out of DrawEngineCommon 2023-10-10 13:16:34 +02:00
Henrik Rydgård
82606b6eb2 Move the clockwise calculation out of the AddPrim loop 2023-10-10 13:00:57 +02:00
Henrik Rydgård
4eecf5ab1d
Merge pull request #18338 from hrydgard/allocate-descs-in-blocks
Vulkan: Allocate descriptors in blocks of 8 instead of individually
2023-10-10 10:33:47 +02:00
Henrik Rydgård
8ebc08185b Allocate descriptors in blocks of 8 instead of individually, to reduce overhead. 2023-10-10 10:17:56 +02:00
Henrik Rydgård
6319dc2a90
Merge pull request #18337 from hrydgard/fix-vulkan-32-bit
Fix for Vulkan on 32-bit devices, oops.
2023-10-10 10:17:15 +02:00
Henrik Rydgård
3b22d1248e Fix for Vulkan on 32-bit devices, oops. 2023-10-10 10:10:12 +02:00
Henrik Rydgård
9767f43cbe
Merge pull request #18328 from hrydgard/desc-on-render-thread-2
Vulkan: Write descriptor sets on the render thread
2023-10-10 09:49:26 +02:00
Henrik Rydgård
3d949b080d Prepare VulkanDescSetPool for block allocation 2023-10-10 09:14:10 +02:00
Henrik Rydgård
9fdc7e2372 Address feedback 2023-10-10 09:02:35 +02:00
Henrik Rydgård
b38b46df0f Update comments 2023-10-10 09:02:35 +02:00
Henrik Rydgård
2b0192d818 Have FrameData structs for each pipeline layout, instead of multiple arrays 2023-10-10 09:02:35 +02:00
Henrik Rydgård
397745ce14 Remove unused code 2023-10-10 09:02:35 +02:00
Henrik Rydgård
9c1c09ff5c Remove commented out code 2023-10-10 09:02:35 +02:00
Henrik Rydgård
ba4d1668ce Don't forget to update descCount in tess mode 2023-10-10 09:02:35 +02:00
Henrik Rydgård
8a4d84d82b Simplest possible de-duplication of descriptor set writes 2023-10-10 09:02:35 +02:00
Henrik Rydgård
018e4ee1d6 Show the desc set write time even in the limited GPU profiler. 2023-10-10 09:02:34 +02:00
Henrik Rydgård
84f9c1694f Better logging in descpool 2023-10-10 09:02:21 +02:00
Henrik Rydgård
af47ad035d Also use the new descriptor mechanism for in-game 2023-10-10 09:00:29 +02:00
Henrik Rydgård
f0ee3b8daa Fill in descriptors on the render thread in the PPSSPP UI. 2023-10-10 09:00:29 +02:00
Henrik Rydgård
96be932003
Merge pull request #18336 from Narugakuruga/patch-28
Trivial fix in zh_CN.ini
2023-10-10 08:32:57 +02:00
Narugakuruga
ddafcce339
revert blanket, optimize with context, new string
As they don't look very good in UI
2023-10-10 11:28:15 +08:00
Henrik Rydgård
35fcec1e4b Another small fix, helps Toca series games. 2023-10-10 02:13:25 +02:00
Henrik Rydgård
9ff5ba7c8d
Merge pull request #18335 from hrydgard/crazy-taxi-fix
Crazy Taxi rendering fix
2023-10-09 21:50:44 +02:00
Henrik Rydgård
24409f6f94 Additional check fix 2023-10-09 21:15:17 +02:00
Henrik Rydgård
f6ba4ee4de Only support extending triangle-based draw calls. Fixes Crazy Taxi. 2023-10-09 21:14:00 +02:00
Henrik Rydgård
10bc6b4cd8 Safety check that doesn't fix crazy taxi 2023-10-09 21:10:53 +02:00
Henrik Rydgård
ced821169e Bump shader cache versions 2023-10-09 19:39:25 +02:00
Henrik Rydgård
9b7df47de3
Merge pull request #18334 from hrydgard/fastforward-mode
Add UI to allow setting fast-forward mode (in developer tools)
2023-10-09 19:18:01 +02:00
Henrik Rydgård
19ee0f571c
Merge pull request #18333 from hrydgard/rotate-crash-fix
Android: Fix crashes on screen rotation
2023-10-09 19:17:24 +02:00
Henrik Rydgård
35a2f46ad0 Add UI to set fast-forward mode to "continuous" (or now, "Render all frames") if vsync is off 2023-10-09 18:34:14 +02:00
Henrik Rydgård
8c821893f1 Android: Fix crashes on screen rotation 2023-10-09 18:24:43 +02:00
Henrik Rydgård
ef35cbbedb
Merge pull request #18332 from hrydgard/desc-set-fix
We somehow lost the usage_ counter increment in VulkanDescSetPool, fix that
2023-10-09 17:34:45 +02:00
Henrik Rydgård
bb38210cfb We somehow lost the usage_ counter increment in VulkanDescSetPool, fix that 2023-10-09 17:01:35 +02:00
Henrik Rydgård
34710ec750
Merge pull request #18329 from hrydgard/more-triangle-fixes
Some drawcall submission bug fixes
2023-10-09 14:31:46 +02:00
Henrik Rydgård
a8b8580756 Don't forget to check the stall address, even in the optimized primitive loop 2023-10-09 14:08:11 +02:00
Henrik Rydgård
7fd7015987 Fix bug in vertex cache using uninitialized data 2023-10-09 14:03:41 +02:00
Henrik Rydgård
a780d02c07 Minor reordering 2023-10-09 11:54:15 +02:00
Henrik Rydgård
316bc03ac9 Move the destroy function for VKRPipelineLayout to VulkanRenderManager 2023-10-09 11:54:13 +02:00
Henrik Rydgård
5a29ebfe61
Merge pull request #18323 from hrydgard/turn-off-https-ios
Turn off HTTPS support for iOS.
2023-10-08 23:43:02 +02:00
Henrik Rydgård
bc4f6a8341 Translation string fix, thanks [Unknown] 2023-10-08 23:26:27 +02:00
Henrik Rydgård
89e846dc10
Merge pull request #18325 from GABO1423/master
(UWP) Update Manifest Files
2023-10-08 22:08:41 +02:00
Gabriel Morazán
18b62f3bda (UWP) Update Manifest Files 2023-10-08 15:51:36 -04:00
Henrik Rydgård
b43ded8e78 Fix the CMake expression, thanks Halo-Michael 2023-10-08 21:16:57 +02:00
Henrik Rydgård
d280495e63
Merge pull request #18321 from hrydgard/fix-triangle-errors
In GL and Vulkan soft-skin, we might not be fully done decoding when we reach flush. Take that into account.
2023-10-08 19:11:56 +02:00
Henrik Rydgård
87d0461fe0 Turn off HTTPS support for iOS.
Should fix #18322
2023-10-08 19:07:29 +02:00
Henrik Rydgård
ae58fe3828 In GL and Vulkan soft-skin, we might not be fully done decoding when we reach flush. Take that into account. 2023-10-08 16:51:58 +02:00
Henrik Rydgård
43cdb88ace
Merge pull request #18320 from hrydgard/simplify-desc-set-layout
Vulkan: Simplify pipeline and descriptor set layout, pool creation
2023-10-08 14:12:52 +02:00
Henrik Rydgård
c73e2351de Add checks for unused topology values when loading pipeline caches. 2023-10-08 13:39:04 +02:00
Henrik Rydgård
03328638b1 Typo fix, reorder 2023-10-08 13:39:04 +02:00
Henrik Rydgård
28ed12aa93 Simplify descriptor pool creation 2023-10-08 12:39:19 +02:00
Henrik Rydgård
b82a34539d Same as last commit, but in DrawEngineVulkan. 2023-10-08 12:39:19 +02:00
Henrik Rydgård
88a50575c7 Wrap pipeline layout creation, use in thin3d
Two more types
2023-10-08 12:39:18 +02:00
Henrik Rydgård
dbe395dd00 Add a wrapper around VKRPipelineLayout / descsetlayout 2023-10-08 12:39:18 +02:00
Henrik Rydgård
5bb1db557b
Merge pull request #18319 from hrydgard/break-out-desc-sets
Split out the descriptorset pool from VulkanMemory.cpp/h
2023-10-08 12:14:35 +02:00
Henrik Rydgård
34fbbf2c2a Split out the descriptorset pool from VulkanMemory.cpp/h 2023-10-08 11:45:00 +02:00
Henrik Rydgård
3e63fe8655
Merge pull request #18316 from hrydgard/strip-opt
Micro-optimize draw calls a bit more.
2023-10-07 12:04:13 +02:00
Henrik Rydgård
c7a3e7bc32 Remove a redundant variable 2023-10-06 16:32:59 +02:00
Henrik Rydgård
cd35252400 DrawEngine; Convert strip sequences in a tight loop 2023-10-06 16:25:13 +02:00
Henrik Rydgård
15df71c02a
Merge pull request #18315 from hrydgard/assorted-cleanup
Assorted code cleanup
2023-10-06 16:25:05 +02:00
Henrik Rydgård
5711259b86 Declare the back depth buffer as "transient". Allows allocating no memory for it on tiled GPUs.
We can't do the same for other depth buffers as we often need to
preserve them between passes.
2023-10-06 15:40:13 +02:00
Henrik Rydgård
42164b37d6 Gradle: Fix some deprecation warnings 2023-10-06 15:39:59 +02:00
Henrik Rydgård
10ccbfd68c Unify the clearing of variables after a draw call 2023-10-06 15:39:59 +02:00
Henrik Rydgård
64ee5675b8 Minor unrelated cleanup 2023-10-06 15:39:59 +02:00
Henrik Rydgård
d4703e9534 Decoded position format is always the same 2023-10-06 15:39:58 +02:00
Henrik Rydgård
0cd02ab58e
Merge pull request #18314 from hrydgard/read-write-vector-opt
Interpreter: Optimize ReadVector/WriteVector by removing voffset lookups
2023-10-05 21:59:13 +02:00
Henrik Rydgård
0d06af87b6 Interpreter: Optimize ReadVector/WriteVector by removing voffset lookups
Drops these functions down the ranking of top functions by quite a bit in GTA,
speedup at most 0.5% though. But enough of these small ones and they
start adding up.

Not sure why GTA falls back to the interpreter for these so much though.
I guess some "uneaten" prefix..
2023-10-05 19:11:34 +02:00
Henrik Rydgård
60a304f29b Turn the ifs inside out 2023-10-05 18:59:56 +02:00
Henrik Rydgård
f21523ff74 WriteVector: Pluck transpose out of the loop 2023-10-05 18:56:15 +02:00
Henrik Rydgård
e852771480 Integrate the voffset shuffle in ReadVector 2023-10-05 18:52:50 +02:00
Henrik Rydgård
ba1688bd44
Merge pull request #18310 from hrydgard/background-fix
Fix waves background
2023-10-05 09:30:41 +02:00
Henrik Rydgård
14c7eda7f9
Merge pull request #18309 from hrydgard/menu-throttle
Move the menu frame-rate throttling to NativeFrame
2023-10-04 18:59:37 +02:00
Henrik Rydgård
5b14cb61a7
Merge pull request #18307 from hrydgard/exit-hotkey
Add bindable hotkey to exit the app from within gameplay
2023-10-04 17:22:30 +02:00
Henrik Rydgård
931ff4eb1e Guaranteed gap-free rendering of waves background 2023-10-04 17:07:55 +02:00
Henrik Rydgård
4e2759713f Revert "UI: Round wave coords to prevent gaps."
This reverts commit ebf9de7864.
2023-10-04 17:02:23 +02:00
Henrik Rydgård
ae0c1e88c3 Move the menu frame-rate throttling to NativeFrame
Now needed on Android since we added the ability to turn off vsync,
which caused the menu to burn battery by rendering too fast.
2023-10-04 16:57:06 +02:00
Henrik Rydgård
f9403efaf0
Merge pull request #18306 from nishinji/master
Update ja_JP.ini
2023-10-04 16:36:13 +02:00
nishinji
0e86ac6bf0 small fix 2023-10-04 23:25:55 +09:00
nishinji
34189174d6 Update ja_JP.ini 2023-10-04 23:17:18 +09:00
Henrik Rydgård
2c4fda0ecf
Merge pull request #18308 from hrydgard/fix-translation-string-order
Add support for using %1 and %2 in some more translation strings.
2023-10-04 15:04:52 +02:00
Henrik Rydgård
b2f97d5934 Add support for using %1 and %2 in some more translation strings.
Requested in #18306
2023-10-04 14:36:42 +02:00
Henrik Rydgård
06882d6e55 Add new string 2023-10-04 14:12:19 +02:00
Henrik Rydgård
12de5bdead Add hotkey to exit the app from within gameplay
Requested by bluemonkeyinsuit07 and trivial to implement, so..
2023-10-04 14:10:14 +02:00
Henrik Rydgård
166ea2b2ba
Merge pull request #18298 from hrydgard/upgrade-rcheevos
Bump rcheevos submodule to latest.
2023-10-04 09:29:22 +02:00
Henrik Rydgård
1cf5c1bb34
Merge pull request #18304 from hrydgard/manhunt-color-ramp-fix
Extend the Test Drive color ramp smoother to detect up to 3 ramps in a texture
2023-10-04 08:33:31 +02:00
Henrik Rydgård
76f0c6cab4
Merge pull request #18305 from unknownbrackets/x86-ir-vcmp
x86jit: Fix IR vcmp all bit
2023-10-04 07:48:42 +02:00
Unknown W. Brackets
f1a9e39ce9 x86jit: Fix IR vcmp all bit. 2023-10-03 17:46:29 -07:00
Henrik Rydgård
69b43ab734 Extend the Test Drive color ramp smoother to detect up to 3 ramps in a texture.
Note that we also offset the lookup slightly to miss the wrap-around
points. The existing 31 scale factor instead of 32, together with that
half-texel, are enough to avoid that problem.

Fixes #18300
2023-10-03 23:30:18 +02:00
Henrik Rydgård
4d5a671b0d
Merge pull request #18303 from aeiouaeiouaeiouaeiouaeiouaeiou/ru_RU-effects
Update Russian translation
2023-10-03 22:12:01 +02:00
aeiouaeiouaeiouaeiouaeiouaeiou
927fa6bfcb
Update Russian translation 2023-10-03 23:01:53 +03:00
Henrik Rydgård
ca7480fa55 Bump rcheevos submodule to latest. 2023-10-03 14:57:27 +02:00
Henrik Rydgård
f94442d1b3
Merge pull request #18297 from hrydgard/block-transfer-stat
Add a block transfer GPU stat, remove a redundant one
2023-10-03 14:48:41 +02:00
Henrik Rydgård
226d25721a Add a block transfer GPU stat, remove a redundant one 2023-10-03 13:15:55 +02:00
Henrik Rydgård
7a2f30803d
Merge pull request #18296 from hrydgard/startup-perf
Startup and tex-replacement performance fixes
2023-10-03 13:06:41 +02:00
Henrik Rydgård
d07c3c5148 Fix main-thread stalls due to decimate during replacement texture loading 2023-10-03 12:17:43 +02:00
Henrik Rydgård
20c13f3b4c Do the Vulkan check as early as possible 2023-10-03 12:17:43 +02:00
Henrik Rydgård
c4e2ad37ff Windows: Avoid loading shell libraries during startup. 2023-10-03 12:17:43 +02:00
Henrik Rydgård
4f43dac04d
Merge pull request #18286 from hrydgard/drawengine-refactor
DrawEngine refactor, quickly merge non-indexed consecutive draws
2023-10-03 12:17:34 +02:00
Henrik Rydgård
af7efe4b5d Fix. Need to flush soft-skinned vertices when changing vertex format. 2023-10-03 11:01:37 +02:00
Henrik Rydgård
200575b2bc Allow the new optimization through redundant VADDR instructions, very common 2023-10-03 11:01:37 +02:00
Henrik Rydgård
3aa0f5b543 A bit more 2023-10-03 11:01:37 +02:00
Henrik Rydgård
4d95250052 Optimize further 2023-10-03 11:01:37 +02:00
Henrik Rydgård
0260aebc26 Implement fast-path for merging non-indexed draws quickly. 2023-10-03 11:01:37 +02:00
Henrik Rydgård
e63bb0459c Add a new stat, so we can see per game if the optimization has an effect 2023-10-03 11:01:37 +02:00
Henrik Rydgård
1c49d5718c Add an offset field that we'll need later 2023-10-03 11:01:37 +02:00
Henrik Rydgård
92ffef2626 Remove some state from IndexGenerator, fix bugs. Mostly works except vertex cache. 2023-10-03 11:01:37 +02:00
Henrik Rydgård
9b411af1f5 It's running. 2023-10-03 11:01:37 +02:00
Henrik Rydgård
6a2e5dd7f7
Merge pull request #18291 from hrydgard/cache-refresh-rate
Reduce refresh rate checks on Windows
2023-10-03 11:01:11 +02:00
Henrik Rydgård
e39980fc73 Reduce refresh rate checks.
These turned out to be unexpectedly expensive, so cache the value and
also try to check it a bit less.
2023-10-03 11:00:47 +02:00
Henrik Rydgård
bd760b9115
Merge pull request #18217 from hrydgard/gles-simplify-disk-cache
Simplify disk-cache-load on GLES as well, for the same reasons as #18216
2023-10-03 10:39:27 +02:00
Henrik Rydgård
7c184a7e1c
Merge pull request #18289 from fp64/sse2-vfpu-dot
Add SSE2 version of vfpu_dot
2023-10-03 10:39:10 +02:00
Henrik Rydgård
cd0b4fce48
Merge pull request #18293 from unknownbrackets/x86-ir
x86: Fix 32-bit IR jit block entry
2023-10-03 07:24:30 +02:00
Unknown W. Brackets
521335cb2a x86: Fix 32-bit IR jit block entry. 2023-10-02 20:26:07 -07:00
fp64
49ac4c6774 Clarify 2023-10-02 14:05:49 -04:00
fp64
23e2d0f797 Add SSE2 version of vfpu_dot
See #18249. Speedup for this function ranges 10%..100%,
depending on system. Updated verification and speed measurements:
https://godbolt.org/z/W1z3sj6hz
2023-10-02 12:53:30 -04:00
Henrik Rydgård
008055d242 Prevent duplicate alternate-speed status messages 2023-10-01 18:00:04 +02:00
Henrik Rydgård
b85f7e28a9
Merge pull request #18284 from hrydgard/prim-flush-fix
Execute_Prim: Minor fixes and cleanups
2023-10-01 17:50:21 +02:00
Henrik Rydgård
76ad3dec4d Revert unclear optimization 2023-10-01 16:43:33 +02:00
Henrik Rydgård
bd931f9cbe Additional minor cleanups 2023-10-01 14:31:46 +02:00
Henrik Rydgård
3cef04f885 Fix incorrect flushing behavior in the prim sequencer, small optimization 2023-10-01 14:23:34 +02:00
Henrik Rydgård
a2fe906534 Micro-optimization: Don't need to check drawcalls for 0. Extract shared expression. Yes I checked assembly. 2023-10-01 14:10:19 +02:00
Henrik Rydgård
52ad0d0335 Minor cleanup in Prim() 2023-10-01 13:57:41 +02:00
Henrik Rydgård
db805cc4cc
Merge pull request #18282 from unknownbrackets/ir-compiling
Improve IR compilation performance
2023-10-01 11:34:27 +02:00
Henrik Rydgård
7bb7c2f28a
Merge pull request #18279 from unknownbrackets/arm64-ir-transfer
arm64jit: Implement reg lane transfers in IR
2023-10-01 11:31:19 +02:00
Henrik Rydgård
74430ae9d7
Merge pull request #18283 from unknownbrackets/hle-helper
Thread: Reduce thread stop freeing on shutdown
2023-10-01 11:11:35 +02:00
Henrik Rydgård
8bdcd89b77
Merge pull request #18281 from unknownbrackets/irjit-regcache
irjit: Fix regcache disable for FPRs
2023-10-01 11:10:50 +02:00
Henrik Rydgård
4d62b4c50d
Merge pull request #18280 from unknownbrackets/debugger-state-load
Debugger: Improve savestate load performance
2023-10-01 11:10:04 +02:00
Henrik Rydgård
9389456e56
Merge pull request #18277 from unknownbrackets/symbolmap-skip-zz
Debugger: Ignore func imports in ppmap files
2023-10-01 07:24:02 +02:00
Henrik Rydgård
86f3c1ca9e
Merge pull request #18278 from unknownbrackets/arm64-vertexjit
arm64jit: Skip unnecessary const load w/4 weights
2023-10-01 07:23:16 +02:00
Unknown W. Brackets
0a4f1dc49b Thread: Reduce thread stop freeing on shutdown.
Seeing errors for helper threads not existing, so let's try skipping
delete on shutdown.  They already get freed anyway.
2023-09-30 16:52:01 -07:00
Henrik Rydgård
4bde384aaf
Merge pull request #18276 from unknownbrackets/ui-axis-crash
UI: Fix crash on input with no screens
2023-10-01 01:01:17 +02:00
Unknown W. Brackets
cd46f0b4cb irjit: Cache IR metadata lookups.
This improves compilation performance, because all those lookups were
adding up.
2023-09-30 15:56:53 -07:00
Unknown W. Brackets
00c80cea6e irjit: Optimize offset logging during compile.
As I guessed, this was expensive.  using a vector and reserve isn't very.
It's nice to keep this before logBlocks_ is > 0, in case it's set mid
block.
2023-09-30 15:56:18 -07:00
Unknown W. Brackets
4e0761b104 irjit: Fix regcache disable for FPRs. 2023-09-30 15:54:54 -07:00
Unknown W. Brackets
0668a60406 Debugger: Improve savestate load performance. 2023-09-30 15:53:41 -07:00
Unknown W. Brackets
4380bf9787 arm64jit: Optimize transfers to vec4 better. 2023-09-30 15:44:53 -07:00
Unknown W. Brackets
cb835295c8 arm64jit: Implement reg lane transfers. 2023-09-30 15:44:41 -07:00
Unknown W. Brackets
e79e0e21ad arm64jit: Skip unnecessary const load w/4 weights. 2023-09-30 15:41:56 -07:00
Unknown W. Brackets
847a87f164 UI: Fix crash on input with no screens. 2023-09-30 15:31:00 -07:00
Unknown W. Brackets
9844422fc8 Debugger: Ignore func imports in ppmap files. 2023-09-30 15:25:48 -07:00
Henrik Rydgård
fb4a1fb7dd Simplify disk-cache-load on GLES as well, for the same reasons as #18216 2023-09-30 13:45:13 +02:00
Henrik Rydgård
aedd51f2f6
Merge pull request #18272 from hrydgard/ui-event-enum
Change global UI messages to use an enum instead of strings.
2023-09-30 13:43:32 +02:00
Henrik Rydgård
c0e5da02ff Buildfixes 2023-09-30 12:06:07 +02:00
Henrik Rydgård
19e4de5088 Change global UI messages to use an enum instead of strings.
Makes it easier to add new ones and delete outdated ones without missing
any uses.
2023-09-30 11:37:02 +02:00
Henrik Rydgård
2a4d21e53b
Merge pull request #18241 from hrydgard/ini-rewrite
Optimize IniFile for faster save/load of config
2023-09-30 11:26:04 +02:00
Henrik Rydgård
526d3047c7
Merge pull request #18268 from hrydgard/sdl-input-latency
SDL: Use an "EmuThread" in Vulkan mode
2023-09-29 20:16:15 +02:00
Henrik Rydgård
0b15d7d153 Add remaining functionality to the waiting SDL mainloop 2023-09-29 19:35:16 +02:00
Henrik Rydgård
b8baff712b Move the menu frame timing to the emuthread, fix hang on change backend 2023-09-29 19:18:48 +02:00
Henrik Rydgård
da801033f5 SDL: Use an "EmuThread" for Vulkan, send input event asynchonously from main thread 2023-09-29 19:10:51 +02:00
Henrik Rydgård
abdfe74c94 Extract UpdateSDLCursor() 2023-09-29 19:02:34 +02:00
Henrik Rydgård
66fdb86eff remove g_frameCount, unnecessary GetKeyboardState call 2023-09-29 19:02:08 +02:00
Henrik Rydgård
aa411c2f09
Merge pull request #18267 from hrydgard/build-fix-attempt
Buildfix on CI ?
2023-09-29 15:51:00 +02:00
Henrik Rydgård
5f3f2199c9 Buildfix on CI ? 2023-09-29 13:45:48 +02:00
Henrik Rydgård
f0d3a8f88e
Merge pull request #18236 from hrydgard/c-emuthread
Manage the Vulkan "EmuThread" from C++.
2023-09-29 13:35:59 +02:00
Henrik Rydgård
8eefb9f935
Merge pull request #18251 from hrydgard/remove-extra-event-filtering
Control: Remove the axis event dupe filtering, batch events deeper
2023-09-29 13:21:12 +02:00
Henrik Rydgård
dea038a91b
Merge pull request #18193 from Croden1999/Croden1999-patch-lang
assets/lang: Update zh_CN.ini
2023-09-29 11:38:42 +02:00
Henrik Rydgård
fea88b62ec
Merge branch 'master' into Croden1999-patch-lang 2023-09-29 11:38:34 +02:00
Henrik Rydgård
70edf4f234
Merge pull request #18233 from unknownbrackets/meminfo-defer
Use a thread for meminfo and defer tag lookup for copies
2023-09-29 11:37:47 +02:00
Henrik Rydgård
1a19884769 Manage the Vulkan "EmuThread" from C++.
Preparation for a larger EmuThread refactoring.
2023-09-29 11:35:13 +02:00
Henrik Rydgård
80ae562b18
Merge pull request #18198 from hrydgard/libchdr-support
CHD support through libchdr
2023-09-29 11:31:51 +02:00
Henrik Rydgård
cf48532ef5
Merge pull request #18219 from hrydgard/get-index-bounds-autovec
Make GetIndexBounds friendlier to autovectorization. Works on x86 at least.
2023-09-29 11:31:34 +02:00
Henrik Rydgård
ee93e4a2ca Batch axis events all the way into ControlMapper 2023-09-29 11:14:19 +02:00
Henrik Rydgård
b3a2b7a35c Batch axis events coming into the ScreenManager 2023-09-29 11:10:32 +02:00
Henrik Rydgård
1a5d5452fe Batch axis events to the VR code 2023-09-29 11:10:32 +02:00
Henrik Rydgård
b3be6db3ae Remove (probably) unnecessary special case for axis value = 0. 2023-09-29 11:10:32 +02:00
Henrik Rydgård
265a9021fd Control: Remove the axis event dupe filtering from ScreenManager
It's better to have the event sources pre-filter, and most of them now
do that.
2023-09-29 11:10:32 +02:00
Henrik Rydgård
bb33a43d54 Remove superfluous chd_read_header (until we add parent/child chd support) 2023-09-29 11:05:48 +02:00
Henrik Rydgård
2f5f9df620 Customize the cmake build. Android now builds that way too. Fixes.
Hack around build error

port the last fix to Android.mk

One more attempt

Warning fixes

Oops, forgot a slash
2023-09-29 10:16:47 +02:00
GABO1423
9e151d0794 Cleanup UWP libchdr files 2023-09-29 10:16:47 +02:00
Henrik Rydgård
e89396b652 Add UWP project 2023-09-29 10:16:47 +02:00
Henrik Rydgård
1f53d8a9a2 Hook up libchdr to CMakeLists.txt, Android.mk fix 2023-09-29 10:16:47 +02:00
Henrik Rydgård
64d92c9aa0 Use OpenCFile, for future Android compatibility for chd 2023-09-29 10:16:45 +02:00
Henrik Rydgård
ade64171ce Proof-of-concept: libchdr works on Windows 2023-09-29 10:05:19 +02:00
Henrik Rydgård
b8fa3a2071
Merge pull request #18125 from unknownbrackets/arm64-vertexjit
arm64jit: Optimize weight loading a bit
2023-09-29 09:52:56 +02:00
Henrik Rydgård
d1fca2ac33
Merge pull request #18176 from hrydgard/renderpass-dependency-fix
Vulkan: Renderpass dependency fix
2023-09-29 09:52:31 +02:00
Henrik Rydgård
db421165c0
Merge pull request #18172 from hrydgard/more-lenient-clear-detection
Make clear detection a bit more lenient
2023-09-29 09:52:08 +02:00
Henrik Rydgård
1d0114df4d
Merge pull request #18229 from unknownbrackets/arm64jit-ir
arm64jit: Enable in UI
2023-09-29 09:51:47 +02:00
Henrik Rydgård
5d8a0b3ac7
Merge pull request #18266 from unknownbrackets/ir-vtfm
irjit: Fix vhtfm instruction
2023-09-29 09:43:06 +02:00
Unknown W. Brackets
c92148ee2c irjit: Fix vhtfm instruction. 2023-09-28 21:16:54 -07:00
Henrik Rydgård
9dcaf4e761 Add a little utility shell script 2023-09-28 11:53:44 +02:00
Henrik Rydgård
bc98cf6bcb
Merge pull request #18245 from hrydgard/readme-1.16.5
Update README.md for 1.16.5
2023-09-28 11:29:20 +02:00
Henrik Rydgård
86a287f512 linkify readme 2023-09-27 22:15:36 +02:00
Henrik Rydgård
6ec4d4f573 Update README for 1.16.5 2023-09-27 22:15:33 +02:00
Henrik Rydgård
fde10ed4bf
Merge pull request #18261 from hrydgard/revert-expand-lines-fix
Revert "Merge pull request #18184 from hrydgard/expand-lines-mem-fix"
2023-09-27 21:09:43 +02:00
Henrik Rydgård
abbd1c83bd Revert "Merge pull request #18184 from hrydgard/expand-lines-mem-fix"
This reverts commit 65b995ac6c, reversing
changes made to 01c3c3638f.
2023-09-27 20:04:37 +02:00
Henrik Rydgård
ed99e64b7c
Merge pull request #18256 from hrydgard/control-mapper-fix-buffer-overflow
ControlMapper: Fix a range check and array size. In reality, probably not a danger.
2023-09-27 19:20:17 +02:00
Henrik Rydgård
48d3efc473 Bump shader cache versions again, just because. 2023-09-27 17:38:15 +02:00
Henrik Rydgård
65a787026d Fix a range check and array size. In reality, probably not a danger. 2023-09-27 17:36:51 +02:00
Henrik Rydgård
fdb1d69d11
Merge pull request #18255 from hrydgard/opengl-remove-stride-adjustment
Fix issue uploading narrow textures in OpenGL.
2023-09-27 17:14:06 +02:00
Henrik Rydgård
6063e30fd4 Correct the subpass dependencies for the backbuffer to pass validation. 2023-09-27 16:56:59 +02:00
Henrik Rydgård
ca4ee83038 Vulkan: Add debug names to renderpasses 2023-09-27 16:56:57 +02:00
Henrik Rydgård
038bc7fc49 Fix issue uploading narrow textures in OpenGL.
We had some stride adjustment that is not needed - and we're not passing
the stride along, so it can't do the "right thing".

Fixes #18254
2023-09-27 16:43:06 +02:00
Henrik Rydgård
43ae1e3b07
Merge pull request #18248 from hrydgard/assorted-fixes-12
More assorted fixes
2023-09-27 13:01:17 +02:00
Henrik Rydgård
84d0236bf4 Comment fixes 2023-09-27 12:31:17 +02:00
Henrik Rydgård
b5c4b2c3f7 Add a missing comment 2023-09-27 12:31:17 +02:00
Henrik Rydgård
3d14cd16eb Add a null check in PopupMultiChoice::UpdateText 2023-09-27 12:31:17 +02:00
Henrik Rydgård
cbbaf148f4 Don't crash from incomplete wav files 2023-09-27 12:31:17 +02:00
Henrik Rydgård
4c0077fd84 Protect against weirdness in UnlinkBlocks (hopefully not needed after prev fix) 2023-09-27 12:31:17 +02:00
Henrik Rydgård
1fff976e48
Merge pull request #18250 from hrydgard/separate-accelerometer-events
Separate out accelerometer events from joystick axis events
2023-09-27 12:11:02 +02:00
Henrik Rydgård
c8d7226a5f DInput: Seems sometimes we never check for pads on startup? Fix that. 2023-09-27 12:07:21 +02:00
Henrik Rydgård
c28dc9e4f2 Pass in accelerometer readings using NativeAccelerometer instead of NativeAxis 2023-09-27 11:34:31 +02:00
Henrik Rydgård
3b004e7a4b Remove the last use of accelerometer axis events (calibration) 2023-09-27 11:33:40 +02:00
Henrik Rydgård
d6a8bfdf3e
Merge pull request #18249 from unknownbrackets/arm64jit-vcrsp
arm64jit: Avoid fused multiplies in vcrsp.t
2023-09-27 08:49:01 +02:00
Unknown W. Brackets
ded18ff237 arm64jit: Avoid fused multiplies in vcrsp.t.
With this change, issues in Harvest Moon with teleporting animals seem to
disappear.  It was causing some differences in signs of zeros in results,
and slightly different result values.
2023-09-26 20:09:02 -07:00
Henrik Rydgård
8baae83136
Merge pull request #18247 from hrydgard/jit-exit-fixes
Jit: Assert on bad exit numbers, allow two more exits per block
2023-09-26 20:29:31 +02:00
Henrik Rydgård
dd2b1ace88 BlockCache on ARM/ARM64: Allow two more exits 2023-09-26 19:44:05 +02:00
Henrik Rydgård
51d5026792 WriteExit: Assert on bad exit numbers 2023-09-26 19:39:48 +02:00
Henrik Rydgård
c0ee711cb9 In the FinalizeBlock assert, extract some more info 2023-09-26 13:37:40 +02:00
Henrik Rydgård
2bd2292bac With string_view, stripping whitespace can be done without allocs. 2023-09-26 13:19:19 +02:00
Henrik Rydgård
20e703db83 Simplify string-view-equal-case-insensitive comparisons 2023-09-26 10:11:52 +02:00
Henrik Rydgård
41f4c08d17 Replace the internal representation of IniFile with ParsedIniLine. 2023-09-26 10:07:14 +02:00
Henrik Rydgård
daa1bc3c6e Introduce "ParsedIniLine" 2023-09-26 10:07:10 +02:00
Henrik Rydgård
53c33f344c Benchmark loading and saving config 2023-09-26 10:05:27 +02:00
Henrik Rydgård
9fffa33eee
Merge pull request #18234 from unknownbrackets/x86-ir-transfer
x86jit: Perform vector transfers instead of flushing to memory
2023-09-26 09:28:05 +02:00
Henrik Rydgård
d3cd065ccb
Merge pull request #18243 from hrydgard/assorted-fixes-11
More crashfix/leakfix attempts
2023-09-26 09:24:39 +02:00
Henrik Rydgård
cc616547bb
Merge pull request #18244 from unknownbrackets/fileloader-leak
Core: Stop leaking file loaders
2023-09-26 09:23:53 +02:00
Unknown W. Brackets
38e5b33a53 x86jit: Prefer BLENDPS to INSERTPS.
It's faster, this performs better.
2023-09-25 22:12:48 -07:00
Unknown W. Brackets
b0dd2a52b1 Core: Stop leaking file loaders.
Also fixes remote disc streaming caching getting invalidated constantly.
2023-09-25 22:09:28 -07:00
Henrik Rydgård
42fcd399f4 Restore the ReadyForFence flag. Some paranoia probably 2023-09-26 02:00:22 +02:00
Henrik Rydgård
f2cfbe1bcf Vulkan: Add the same shutdown logic to stop async shader compiles to DeviceLost 2023-09-26 01:28:59 +02:00
Henrik Rydgård
91119c7052 Remove useless wait loop since we join the thread anyway 2023-09-26 00:50:36 +02:00
Henrik Rydgård
4d465678cd GL: Add resilience to delete-null bugs (but assert in debug builds) 2023-09-26 00:49:59 +02:00
Henrik Rydgård
0198f7c55d Java/GL: Add null check for surface 2023-09-26 00:38:29 +02:00
Henrik Rydgård
db245e1b34 Fix old texture leak in GLES hardware tessellation 2023-09-26 00:38:11 +02:00
Henrik Rydgård
01035f48a4 Fix for crash when changing backends in-game 2023-09-26 00:13:53 +02:00
Henrik Rydgård
fae186bba6
Merge pull request #18240 from Valtekken/patch-1
UI/Localization: Partial rework of the Italian translation
2023-09-25 19:52:29 +02:00
Henrik Rydgård
308cbfd346 Apply the WWE 2006 workaround to WWE 2007 as well. 2023-09-25 17:49:09 +02:00
Valtekken
2173a8ffd4
UI/Localization: Partial rework of the Italian translation, missing lines added 2023-09-25 17:40:37 +02:00
Henrik Rydgård
80e429f2a1
Merge pull request #18239 from Valtekken/patch-1
UI/Localization: another small fix for the Italian translation
2023-09-25 17:01:12 +02:00
Valtekken
e503242260
UI/Localization: another small fix for the Italian translation 2023-09-25 16:59:42 +02:00
Henrik Rydgård
797db387e4 v1.16.4 2023-09-25 14:17:56 +02:00
Henrik Rydgård
1bc6a1a23f
Merge pull request #18238 from hrydgard/readme-1.16.4
Update README.md for 1.16.4
2023-09-25 14:14:02 +02:00
Henrik Rydgård
f0361db13a Linkify readme 2023-09-25 14:12:56 +02:00
Henrik Rydgård
211d30d589 Update README.md for 1.16.4 2023-09-25 14:11:52 +02:00
Henrik Rydgård
58c4039d48
Merge pull request #18237 from Valtekken/patch-1
UI/localization: Italian translation update
2023-09-25 13:52:50 +02:00
Valtekken
0d78d2e87e
Small fix 2023-09-25 13:24:32 +02:00
Valtekken
e8e646c568
Italian translation update 2023-09-25 13:21:15 +02:00
Henrik Rydgård
165225dc18
Merge pull request #18230 from hrydgard/minor-java-lifecycle-stuff
Android: Minor activity lifecycle stuff
2023-09-25 10:12:42 +02:00
Henrik Rydgård
0fd22ea3bb Comment clarifications. Slightly extend renderlock use in shutdown. 2023-09-25 09:40:14 +02:00
Henrik Rydgård
9f62a3f750
Merge pull request #18235 from unknownbrackets/ir-vdet
irjit: Handle VDet
2023-09-25 09:06:20 +02:00
Henrik Rydgård
51456980db
Merge pull request #18121 from unknownbrackets/jit-ir-profiler
IR: Add mini native jit MIPS block profiler
2023-09-25 09:04:55 +02:00
Unknown W. Brackets
9b2fa46861 IR: Add mini native jit MIPS block profiler. 2023-09-24 23:04:29 -07:00
Unknown W. Brackets
e104a28b71 irjit: Handle VDet. 2023-09-24 23:03:25 -07:00
Henrik Rydgård
4164f363da
Merge pull request #18232 from unknownbrackets/x86-ir-extract
x86jit: Correct spill on IR lane extract
2023-09-25 07:40:35 +02:00
Henrik Rydgård
279b046b8f
Merge pull request #18231 from hch12907/master
SDL: fallback to atlas if SDL2_ttf is <2.0.18
2023-09-25 07:40:05 +02:00
Unknown W. Brackets
fc133f4994 Debugger: Use a thread to flush meminfo. 2023-09-24 20:03:27 -07:00
Unknown W. Brackets
b0da32f41f Debugger: Defer copy src tag lookups. 2023-09-24 19:07:45 -07:00
Unknown W. Brackets
a416478780 Debugger: Skip tag copy on READ notify. 2023-09-24 19:07:45 -07:00
Unknown W. Brackets
810d8c0890 Debugger: Use dedicated func to notify mem copy. 2023-09-24 19:07:36 -07:00
Hoe Hao Cheng
28edae016e SDL: fallback to atlas if SDL2_ttf is <2.0.18 2023-09-25 10:07:30 +08:00
Unknown W. Brackets
05786f5719 x86jit: Correct spill on IR lane extract. 2023-09-24 19:06:06 -07:00
Unknown W. Brackets
685d2acffe x86jit: Retain old lanes when there's space. 2023-09-24 17:31:25 -07:00
Unknown W. Brackets
46e704f879 x86jit: Cleanup and refactor transfer. 2023-09-24 16:58:41 -07:00
Unknown W. Brackets
d9f6bae1ff x64jit: Initial reg transfer. 2023-09-24 16:28:29 -07:00
Unknown W. Brackets
88b6442527 irjit: Add facility for native reg transfer. 2023-09-24 16:28:29 -07:00
Henrik Rydgård
06a1f0b72c
Merge pull request #18226 from unknownbrackets/x86-ir-breakpoints
x86jit: Improve memory breakpoint speed
2023-09-25 00:47:22 +02:00
Henrik Rydgård
8b9836afd3 SizeManager: Don't send notifySurface if paused. Cleaner exits / task switches in the log. 2023-09-25 00:32:37 +02:00
Henrik Rydgård
1b8b441cfd Tighten up some renderloop logic in app-android.cpp 2023-09-25 00:03:58 +02:00
Henrik Rydgård
0013c6fede Rename ensureRenderLoop -> startRenderLoopThread 2023-09-25 00:03:39 +02:00
Henrik Rydgård
2b0bbb1e0c Remove isFinishing check in onDestroy - not relevant. 2023-09-25 00:03:39 +02:00
Henrik Rydgård
2291855a1f Remove dead code path 2023-09-25 00:03:38 +02:00
Henrik Rydgård
7b2657f3ff
Merge pull request #18228 from unknownbrackets/ir-jit-unittest
unittest: Add jit compare for jit IR
2023-09-24 21:36:28 +02:00
Unknown W. Brackets
da013ee105 x86jit: Fix asm jitbase displacement check. 2023-09-24 12:11:00 -07:00
Unknown W. Brackets
7d0f2e43b6 irjit: Fix safety of kernel bit memory addresses. 2023-09-24 10:18:55 -07:00
Unknown W. Brackets
e5df318990 unittest: Add jit compare for jit IR. 2023-09-24 10:17:34 -07:00
Henrik Rydgård
2ba63c65f2
Merge pull request #18227 from unknownbrackets/x86-ir-flush
x86jit: Flush floats together if possible
2023-09-24 17:27:38 +02:00
Unknown W. Brackets
272c162aae arm64jit: Enable in UI. 2023-09-24 08:06:50 -07:00
Henrik Rydgård
e7aec4dae1
Merge pull request #18225 from TotalCaesar659/patch-1
Update Russian translation
2023-09-24 17:02:12 +02:00
Unknown W. Brackets
d36728e532 x86jit: Load common float vals from constants. 2023-09-24 08:01:08 -07:00
Unknown W. Brackets
decccf199a x86jit: Flush floats together if possible. 2023-09-24 08:01:05 -07:00
Unknown W. Brackets
9742aaaffe x86jit: Use MOVAPS directly when we can.
May help older processors or reduce total bytes.
2023-09-24 08:01:02 -07:00
TotalCaesar659
ec7f163a32
Update Russian translation 2023-09-24 17:43:30 +03:00
Unknown W. Brackets
772b3ff7b8 arm64jit: Tweak memchecks. 2023-09-24 07:42:11 -07:00
Unknown W. Brackets
e433a8be4a arm64jit: Speed up memchecks, add validation. 2023-09-24 07:42:11 -07:00
Henrik Rydgård
fcb5042eaa
Merge pull request #18224 from unknownbrackets/x86-ir-dis
irjit: Describe native offsets better
2023-09-24 16:29:34 +02:00
Henrik Rydgård
86f98bf904
Merge pull request #18223 from Valtekken/patch-1
RA popup: different choice of words
2023-09-24 16:16:49 +02:00
Unknown W. Brackets
5929aaae85 x86jit: Speed up safe memory checks. 2023-09-24 07:06:57 -07:00
Unknown W. Brackets
017d0d4b17 x86jit: Improve memory breakpoint speed.
This helps a lot compared to before.
2023-09-24 07:06:57 -07:00
Unknown W. Brackets
34ff24a4f3 irjit: Describe native offsets better.
Also check in case of non-linear blocks, can happen with preload.
2023-09-24 07:05:30 -07:00
Unknown W. Brackets
3e20a5802f x86jit: Describe constants better. 2023-09-24 06:46:42 -07:00
Valtekken
cf8c5f3b6b
RA popup: different choice of words 2023-09-24 15:16:06 +02:00
Henrik Rydgård
638192b024
Merge pull request #18220 from hrydgard/keymap-lock-fix
Add some missing locking in KeyMap.cpp.
2023-09-24 14:47:58 +02:00
Henrik Rydgård
fa2b9f8fdd
Merge pull request #18221 from hrydgard/cleanups-fixes
Some cleanups and fixes to obscure crashes
2023-09-24 14:47:41 +02:00
Henrik Rydgård
546f9d7743 Some cleanups and fixes to obscure crashes 2023-09-24 13:05:25 +02:00
Henrik Rydgård
3264209772 Add some missing locking in KeyMap.cpp.
Plus minor assert change and java null check.
2023-09-24 12:33:01 +02:00
Henrik Rydgård
acf530e996
Merge pull request #18218 from hrydgard/simplify-getshaders
Vulkan: Simplify GetShaders and DirtyLastShader, making them internally consistent.
2023-09-24 12:19:50 +02:00
Henrik Rydgård
45bc4d8750 Make GetIndexBounds friendlier to autovectorization. Works on x86 at least. 2023-09-24 12:15:04 +02:00
Henrik Rydgård
6e303e8f1d Vulkan: Simplify GetShaders and DirtyLastShader, making them internally consistent. 2023-09-24 11:55:15 +02:00
Henrik Rydgård
559cc60a66
Merge pull request #18216 from hrydgard/remove-shader-cache-load-thread
Don't load the shader cache on a separate thread - all it does is already async
2023-09-24 11:18:07 +02:00
Henrik Rydgård
d31ba393af Don't load the shader cache on a separate thread - all it does is already async 2023-09-24 10:53:23 +02:00
Henrik Rydgård
1c58617392
Merge pull request #18208 from unknownbrackets/x86-ir-float
x86jit: Speed up float to int conversions
2023-09-24 09:30:00 +02:00
Henrik Rydgård
ac3139b8ee
Merge pull request #18213 from unknownbrackets/x86-ir-fcmp
IR: Improve fcmp/vfpu compare jit
2023-09-24 09:29:14 +02:00
Henrik Rydgård
87feeeb7e0
Merge pull request #18214 from unknownbrackets/x86-ir-leftover
Implement a few leftover x64 ops
2023-09-24 09:28:39 +02:00
Henrik Rydgård
fb5ba16ef3
Merge pull request #18211 from hrydgard/more-crash-fix-attempts
More crash fix attempts
2023-09-24 09:24:48 +02:00
Unknown W. Brackets
6d41f15f0d x86jit: Implement FSign. 2023-09-23 22:08:17 -07:00
Unknown W. Brackets
06ec41d1de x86jit: Implement fcr31/break related ops. 2023-09-23 22:01:22 -07:00
Unknown W. Brackets
3a705d9470 x86jit: Implement BSwap16. 2023-09-23 22:01:09 -07:00
Unknown W. Brackets
580c9a634b x86jit: Implement ReverseBits. 2023-09-23 22:00:58 -07:00
Unknown W. Brackets
24da5a3ba2 irjit: Small simplification to regcache. 2023-09-23 22:00:49 -07:00
Unknown W. Brackets
15f01b13a2 x86jit: Small tweak for SltU zero, x. 2023-09-23 22:00:38 -07:00
Henrik Rydgård
964f606a9c Fix some issues around geometry shaders - like, loading them from shader cache while disabled 2023-09-24 01:29:38 +02:00
Henrik Rydgård
dbd3045f87 Join the shader cache load thread on exit 2023-09-24 01:07:08 +02:00
Henrik Rydgård
9a515c851f Vulkan: Extend the cacheLock usage in GetShaders (was unsafe, though mildly) 2023-09-24 00:58:45 +02:00
Henrik Rydgård
c4ad32420a
Merge pull request #18210 from unknownbrackets/ui-waves
UI: Round wave coords to prevent gaps
2023-09-23 23:34:35 +02:00
Unknown W. Brackets
14e2e1ed62 x64jit: Optimize FCmpVfpuAggregate. 2023-09-23 14:31:46 -07:00
Unknown W. Brackets
c5d896a9d7 x86jit: Speed up c.eq.s. 2023-09-23 14:31:18 -07:00
Henrik Rydgård
ae0305d974
Merge pull request #18207 from unknownbrackets/irjit-safe-mem
irjit: Correct alignment checks against SP
2023-09-23 23:25:12 +02:00
Henrik Rydgård
946b16dbc5
Merge pull request #18206 from unknownbrackets/gpu-blend-cleanup
GPU: Handle invalid blendeq more accurately
2023-09-23 23:23:13 +02:00
Henrik Rydgård
a8295781bb
Merge pull request #18205 from unknownbrackets/http-error
http: Fix errors on connect
2023-09-23 23:15:55 +02:00
Unknown W. Brackets
ebf9de7864 UI: Round wave coords to prevent gaps. 2023-09-23 14:03:06 -07:00
Unknown W. Brackets
1c81d47dd4 x86jit: Speed up float to int conversions. 2023-09-23 13:28:58 -07:00
Unknown W. Brackets
69b8fb9bc2 irjit: Correct alignment checks against SP. 2023-09-23 13:15:06 -07:00
Unknown W. Brackets
b610e2f314 GPU: Handle invalid blendeq more accurately. 2023-09-23 13:08:25 -07:00
Unknown W. Brackets
4a2cd1bb7b http: Fix errors on connect. 2023-09-23 13:05:01 -07:00
Henrik Rydgård
7dc18a94af
Merge pull request #18201 from hrydgard/asserts-and-checks
Asserts and checks
2023-09-23 19:59:41 +02:00
Henrik Rydgård
e64d1e94fe add reporting to the invalid replacement op 2023-09-23 11:39:20 +02:00
Henrik Rydgård
8fc01e37d9 Check for bad indices in GetReplacementFunc to avoid crashes 2023-09-23 10:09:55 +02:00
Henrik Rydgård
6a8f65b566 Some assert paranoia, remove unused "failed_" variable 2023-09-23 10:09:32 +02:00
Henrik Rydgård
c1529b2704
Merge pull request #18200 from hrydgard/assert-time
Add time-elapsed to assert messages
2023-09-23 09:29:42 +02:00
Henrik Rydgård
949f746f96 Add time-elapsed to assert messages
Just a crude thing to easily see if a crash is from just starting a game
or later.
2023-09-23 08:53:59 +02:00
Croden1999
0b58f0917d
assets/lang: Update zh_CN.ini 2023-09-23 00:40:43 +08:00
Henrik Rydgård
a9b6421dfd v1.16.3 2023-09-22 15:27:23 +02:00
Henrik Rydgård
90ac4d5127 Add one string to zh_CN.ini from #18193 2023-09-22 15:16:45 +02:00
Henrik Rydgård
d0d41262d1
Merge pull request #18195 from hrydgard/readme-1.16.3
Update README.md for 1.16.3
2023-09-22 11:26:36 +02:00
Henrik Rydgård
455b58487d Linkify readme 2023-09-22 11:03:29 +02:00
Henrik Rydgård
8fd0426c29 Update README.md for 1.16.3 2023-09-22 11:03:04 +02:00
Henrik Rydgård
c2406aeb45
Merge pull request #18194 from hrydgard/minor-stuff-again
Cleanups and comment clarifications
2023-09-22 10:59:30 +02:00
Henrik Rydgård
e7c0b41867 Improve an assert 2023-09-22 10:58:43 +02:00
Henrik Rydgård
81f47caf2f Clarify the primitive expansion, add reporting 2023-09-22 10:27:02 +02:00
Henrik Rydgård
755648c997 Don't use std::move in dirlisting, see #18192 2023-09-22 10:09:10 +02:00
Croden1999
567ba61057
Update zh_CN.ini 2023-09-22 06:23:04 +08:00
Henrik Rydgård
f83fb2325e NativeActivity: null surface on exit, renderloopthread on exception 2023-09-21 16:42:00 +02:00
Henrik Rydgård
602407fcf2 Warning and comment fixes, logic precedence fixes in PPGeDraw 2023-09-21 16:41:42 +02:00
Henrik Rydgård
3aed81d51b
Merge pull request #18170 from hch12907/sdl-ttf
ci: use pkgconfig as a fallback for finding SDL2_ttf
2023-09-21 13:45:52 +02:00
Henrik Rydgård
50da8a91da
Merge pull request #18190 from hrydgard/original-address-asserts
Add an assert to try to track down a mysterious reported crash.
2023-09-21 12:26:23 +02:00
Henrik Rydgård
8a90e94e74 Add an assert to try to track down a mysterious reported crash. 2023-09-21 12:08:16 +02:00
Henrik Rydgård
1cb8bf38f9
Merge pull request #18189 from hrydgard/vulkan-shader-cache-load
Be a bit smarter when loading the shader cache, avoid duplicating work
2023-09-21 11:48:27 +02:00
Henrik Rydgård
1aab1c4b09 Be a bit smarter when loading the shader cache, avoid duplicating work 2023-09-21 10:44:04 +02:00
Henrik Rydgård
cdffce8ce0
Merge pull request #18186 from hrydgard/remove-unhelpful-assert
Vulkan: Remove an assert that didn't give much actionable information
2023-09-21 09:15:00 +02:00
Henrik Rydgård
2e171b22ec Vulkan: Remove an assert that didn't give much actionable information. Replace with reporting. 2023-09-20 22:50:38 +02:00
Henrik Rydgård
f8320e4764
Merge pull request #18185 from hrydgard/http-image-file-view-race-fix
Store: Fix race condition causing crashes if looking at another game before an icon finishes downloading
2023-09-20 22:22:47 +02:00
Henrik Rydgård
2648cd0eee Store: Fix race condition causing crashes if looking at another game before an icon finishes downloading 2023-09-20 21:49:17 +02:00
Henrik Rydgård
65b995ac6c
Merge pull request #18184 from hrydgard/expand-lines-mem-fix
Add memory bounds-check when expanding points, rects and lines to triangles
2023-09-20 20:39:16 +02:00
Henrik Rydgård
01c3c3638f
Merge pull request #18183 from hrydgard/shader-race-condition-fix
Pipeline/shader race-condition-during-shutdown crash fix
2023-09-20 19:33:47 +02:00
Henrik Rydgård
dee84e6810
Merge pull request #18182 from hrydgard/change-backend-exit-fix
Backend change from Win32 menu: Add quick workaround for instance counter misbehavior
2023-09-20 19:32:47 +02:00
Henrik Rydgård
966144fa64 Bounds check writing to the index buffer when expanding lines/rects/points 2023-09-20 19:26:36 +02:00
Henrik Rydgård
3f2ef508c9 Make it easier to reason about space in the inds buffer by moving an offset instead of the pointer. 2023-09-20 19:23:24 +02:00
Henrik Rydgård
0f5fdf7840
Merge pull request #18174 from bentley/wayland-cmake
Disable Wayland if USE_WAYLAND_WSI is off
2023-09-20 18:54:19 +02:00
Henrik Rydgård
3783afd855 Fix a really bad race condition during game shutdown. 2023-09-20 18:47:32 +02:00
Henrik Rydgård
b8353c6273 Add a commented-out torture test for a shader race condition 2023-09-20 18:47:32 +02:00
Henrik Rydgård
5c94b41dde Vulkan: If a createimageview failed, don't leak the image. Probably very rare. 2023-09-20 18:47:32 +02:00
Henrik Rydgård
c2c7933393 Backend change from Win32 menu: Add quick workaround for instance counter misbehavior.
Should really do this some better way.

See #18148
2023-09-20 18:46:41 +02:00
Henrik Rydgård
ad029e86d2
Merge pull request #18180 from DDinghoya/patch-16
Update ko_KR.ini
2023-09-20 16:07:28 +02:00
DDinghoya
07e0cf9a22
Update ko_KR.ini 2023-09-20 22:26:49 +09:00
Henrik Rydgård
4eec2e0444
Merge pull request #18179 from hrydgard/update-rcheevos
Update the rcheevos submodule
2023-09-20 14:26:17 +02:00
Anthony J. Bentley
cbab677075 Enable USE_WAYLAND_WSI by default. 2023-09-19 23:17:54 -06:00
Henrik Rydgård
be658f8f4e Update the rcheevos submodule
This is the RetroAchievements code, which has recently received some
fixes we want.
2023-09-19 17:38:47 +02:00
Anthony J. Bentley
25a7334b5f Only build with Wayland if USE_WAYLAND_WSI is on. 2023-09-19 02:07:46 -06:00
Henrik Rydgård
e6a864ee04 Make clear detection a bit more lenient. Allows using clears in Assassin's Creed and likely more. 2023-09-18 23:57:20 +02:00
Hoe Hao Cheng
b768210797 ci: use pkgconfig as a fallback for finding SDL2_ttf 2023-09-19 00:51:19 +08:00
Unknown W. Brackets
5c4e08fe19 arm64jit: Use FMLA for TC precale. 2023-09-10 23:04:15 -07:00
Unknown W. Brackets
646e3b269d arm64jit: Skip vertexjit prolog/epilog if possible. 2023-09-10 23:04:15 -07:00
Unknown W. Brackets
00e691d633 arm64jit: Try shifted MOVI in MOVI2FDUP().
Any penalty from int/float or size change should be less than GPR load.
2023-09-10 23:04:15 -07:00
Unknown W. Brackets
a8493c0e19 arm64jit: Optimize weight loading a bit. 2023-09-10 23:04:15 -07:00
Unknown W. Brackets
f1f3e6fba2 arm64jit: Optimize vertex full alpha tracking. 2023-09-10 13:08:33 -07:00
393 changed files with 12877 additions and 6722 deletions

View file

@ -333,7 +333,7 @@ jobs:
run: |
sudo add-apt-repository -y "deb http://archive.ubuntu.com/ubuntu `lsb_release -sc` main universe restricted multiverse"
sudo apt-get update -y -qq
sudo apt-get install libsdl2-dev libgl1-mesa-dev libglu1-mesa-dev
sudo apt-get install libsdl2-dev libgl1-mesa-dev libglu1-mesa-dev libsdl2-ttf-dev libfontconfig1-dev
- name: Install macOS dependencies
if: runner.os == 'macOS'

View file

@ -158,4 +158,4 @@ libretro-build-tvos-arm64:
- .core-defs
- .cmake-defs
variables:
CORE_ARGS: -DIOS_PLATFORM=TVOS -DUSE_FFMPEG=NO -DCMAKE_TOOLCHAIN_FILE=cmake/Toolchains/ios.cmake -DLIBRETRO=ON
CORE_ARGS: -DIOS_PLATFORM=TVOS -DCMAKE_TOOLCHAIN_FILE=cmake/Toolchains/ios.cmake -DLIBRETRO=ON

3
.gitmodules vendored
View file

@ -50,3 +50,6 @@
[submodule "ext/naett"]
path = ext/naett
url = https://github.com/erkkah/naett.git
[submodule "ext/libchdr"]
path = ext/libchdr
url = https://github.com/rtissera/libchdr.git

View file

@ -149,7 +149,7 @@ option(USING_EGL "Set to ON if target environment uses EGL" ${USING_EGL})
option(USING_FBDEV "Set to ON if target environment uses fbdev (eg. Pandora)" ${USING_FBDEV})
option(USING_GLES2 "Set to ON if target device uses OpenGL ES 2.0" ${USING_GLES2})
option(USING_X11_VULKAN "Set to OFF if target environment doesn't use X11 for Vulkan" ON)
option(USE_WAYLAND_WSI "Enable or disable Wayland WSI support for Vulkan" ${USE_WAYLAND_WSI})
option(USE_WAYLAND_WSI "Enable or disable Wayland WSI support for Vulkan" ON)
option(USE_VULKAN_DISPLAY_KHR "Enable or disable full screen display of Vulkan" ${USE_VULKAN_DISPLAY_KHR})
# :: Frontends
option(USING_QT_UI "Set to ON if you wish to use the Qt frontend wrapper" ${USING_QT_UI})
@ -188,10 +188,9 @@ if(UNIX AND NOT (APPLE OR ANDROID) AND VULKAN)
find_package(Wayland)
if(NOT WAYLAND_FOUND)
message(STATUS "Could not find Wayland libraries, disabling Wayland WSI support for Vulkan.")
else()
elseif(USE_WAYLAND_WSI)
include_directories(${WAYLAND_INCLUDE_DIR})
add_definitions(-DVK_USE_PLATFORM_WAYLAND_KHR)
add_definitions(-DUSE_WAYLAND_WSI=ON)
endif()
if(USE_VULKAN_DISPLAY_KHR)
@ -221,7 +220,7 @@ else()
set(CoreLinkType STATIC)
endif()
if(NOT ANDROID AND NOT WIN32 AND NOT APPLE)
if(NOT ANDROID AND NOT WIN32 AND (NOT APPLE OR IOS))
set(HTTPS_NOT_AVAILABLE ON)
endif()
@ -262,6 +261,14 @@ if(NOT LIBRETRO AND NOT IOS AND NOT MACOSX)
find_package(SDL2)
find_package(SDL2_ttf)
find_package(Fontconfig)
# TODO: this can be removed once CI supports newer SDL2_ttf
if (NOT SDL2_ttf_FOUND)
find_package(PkgConfig)
if(PkgConfig_FOUND)
pkg_check_modules(SDL2_ttf_PKGCONFIG IMPORTED_TARGET SDL2_ttf)
endif()
endif()
endif()
if(MACOSX AND NOT IOS)
@ -705,6 +712,8 @@ add_library(Common STATIC
Common/GPU/Vulkan/VulkanDebug.h
Common/GPU/Vulkan/VulkanContext.cpp
Common/GPU/Vulkan/VulkanContext.h
Common/GPU/Vulkan/VulkanDescSet.cpp
Common/GPU/Vulkan/VulkanDescSet.h
Common/GPU/Vulkan/VulkanFramebuffer.cpp
Common/GPU/Vulkan/VulkanFramebuffer.h
Common/GPU/Vulkan/VulkanImage.cpp
@ -892,7 +901,11 @@ if(USE_FFMPEG)
set(PLATFORM_ARCH "android/x86")
endif()
elseif(IOS)
set(PLATFORM_ARCH "ios/universal")
if(IOS_PLATFORM STREQUAL "TVOS")
set(PLATFORM_ARCH "tvos/arm64")
else()
set(PLATFORM_ARCH "ios/universal")
endif()
elseif(MACOSX)
set(PLATFORM_ARCH "macosx/universal")
elseif(LINUX)
@ -1336,14 +1349,21 @@ else()
SDL/SDLVulkanGraphicsContext.cpp
)
endif()
if(SDL2_ttf_FOUND)
if(SDL2_ttf_FOUND OR
(SDL2_ttf_PKGCONFIG_FOUND AND
SDL2_ttf_PKGCONFIG_VERSION VERSION_GREATER_EQUAL "2.0.18"))
add_definitions(-DUSE_SDL2_TTF)
set(nativeExtraLibs ${nativeExtraLibs} SDL2_ttf::SDL2_ttf)
if(FONTCONFIG_FOUND)
add_definitions(-DUSE_SDL2_TTF_FONTCONFIG)
set(nativeExtraLibs ${nativeExtraLibs} Fontconfig::Fontconfig)
endif()
elseif(SDL2_ttf_PKGCONFIG_FOUND)
message(WARNING "Found SDL2_ttf <2.0.18 - this is too old, falling back to atlas")
endif()
if(SDL2_ttf_FOUND)
set(nativeExtraLibs ${nativeExtraLibs} SDL2_ttf::SDL2_ttf)
elseif(SDL2_ttf_PKGCONFIG_FOUND)
set(nativeExtraLibs ${nativeExtraLibs} PkgConfig::SDL2_ttf_PKGCONFIG)
endif()
if(APPLE)
set(nativeExtra ${nativeExtra}
@ -2245,6 +2265,8 @@ add_library(${CoreLibName} ${CoreLinkType}
Core/Util/AudioFormat.h
Core/Util/GameManager.cpp
Core/Util/GameManager.h
Core/Util/GameDB.cpp
Core/Util/GameDB.h
Core/Util/PortManager.cpp
Core/Util/PortManager.h
Core/Util/BlockAllocator.cpp
@ -2303,11 +2325,16 @@ else()
include_directories(ext/zstd/lib)
endif()
target_link_libraries(${CoreLibName} Common native kirk cityhash sfmt19937 xbrz xxhash rcheevos ${GlslangLibs}
include_directories(ext/libchdr/include)
target_link_libraries(${CoreLibName} Common native chdr kirk cityhash sfmt19937 xbrz xxhash rcheevos ${GlslangLibs}
${CoreExtraLibs} ${OPENGL_LIBRARIES} ${X11_LIBRARIES} ${CMAKE_DL_LIBS})
if(NOT HTTPS_NOT_AVAILABLE)
target_link_libraries(${CoreLibName} naett)
if(WIN32)
target_link_libraries(${CoreLibName} winhttp)
endif()
endif()
target_compile_features(${CoreLibName} PUBLIC cxx_std_17)

View file

@ -4204,6 +4204,14 @@ void ARM64FloatEmitter::MOVI2FDUP(ARM64Reg Rd, float value, ARM64Reg scratch, bo
if (negate) {
FNEG(32, Rd, Rd);
}
} else if (TryAnyMOVI(32, Rd, ival)) {
if (negate) {
FNEG(32, Rd, Rd);
}
} else if (TryAnyMOVI(32, Rd, ival ^ 0x80000000)) {
if (!negate) {
FNEG(32, Rd, Rd);
}
} else {
_assert_msg_(scratch != INVALID_REG, "Failed to find a way to generate FP immediate %f without scratch", value);
if (negate) {
@ -4214,6 +4222,96 @@ void ARM64FloatEmitter::MOVI2FDUP(ARM64Reg Rd, float value, ARM64Reg scratch, bo
}
}
bool ARM64FloatEmitter::TryMOVI(u8 size, ARM64Reg Rd, uint64_t elementValue) {
if (size == 8) {
// Can always do 8.
MOVI(size, Rd, elementValue & 0xFF);
return true;
} else if (size == 16) {
if ((elementValue & 0xFF00) == 0) {
MOVI(size, Rd, elementValue & 0xFF, 0);
return true;
} else if ((elementValue & 0x00FF) == 0) {
MOVI(size, Rd, (elementValue >> 8) & 0xFF, 8);
return true;
} else if ((elementValue & 0xFF00) == 0xFF00) {
MVNI(size, Rd, ~elementValue & 0xFF, 0);
return true;
} else if ((elementValue & 0x00FF) == 0x00FF) {
MVNI(size, Rd, (~elementValue >> 8) & 0xFF, 8);
return true;
}
return false;
} else if (size == 32) {
for (int shift = 0; shift < 32; shift += 8) {
uint32_t mask = 0xFFFFFFFF &~ (0xFF << shift);
if ((elementValue & mask) == 0) {
MOVI(size, Rd, (elementValue >> shift) & 0xFF, shift);
return true;
} else if ((elementValue & mask) == mask) {
MVNI(size, Rd, (~elementValue >> shift) & 0xFF, shift);
return true;
}
}
// Maybe an MSL shift will work?
for (int shift = 8; shift <= 16; shift += 8) {
uint32_t mask = 0xFFFFFFFF & ~(0xFF << shift);
uint32_t ones = (1 << shift) - 1;
uint32_t notOnes = 0xFFFFFF00 << shift;
if ((elementValue & mask) == ones) {
MOVI(size, Rd, (elementValue >> shift) & 0xFF, shift, true);
return true;
} else if ((elementValue & mask) == notOnes) {
MVNI(size, Rd, (elementValue >> shift) & 0xFF, shift, true);
return true;
}
}
return false;
} else if (size == 64) {
uint8_t imm8 = 0;
for (int i = 0; i < 8; ++i) {
uint8_t byte = (elementValue >> (i * 8)) & 0xFF;
if (byte != 0 && byte != 0xFF)
return false;
if (byte == 0xFF)
imm8 |= 1 << i;
}
// Didn't run into any partial bytes, so size 64 is doable.
MOVI(size, Rd, imm8);
return true;
}
return false;
}
bool ARM64FloatEmitter::TryAnyMOVI(u8 size, ARM64Reg Rd, uint64_t elementValue) {
// Try the original size first in case that's more optimal.
if (TryMOVI(size, Rd, elementValue))
return true;
uint64_t value = elementValue;
if (size != 64) {
uint64_t masked = elementValue & ((1 << size) - 1);
for (int i = size; i < 64; ++i) {
value |= masked << i;
}
}
for (int attempt = 8; attempt <= 64; attempt += attempt) {
// Original size was already attempted above.
if (attempt != size) {
if (TryMOVI(attempt, Rd, value))
return true;
}
}
return false;
}
void ARM64XEmitter::SUBSI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm, ARM64Reg scratch) {
u32 val;
bool shift;

View file

@ -925,6 +925,10 @@ public:
void ORR(u8 size, ARM64Reg Rd, u8 imm8, u8 shift = 0);
void BIC(u8 size, ARM64Reg Rd, u8 imm8, u8 shift = 0);
bool TryMOVI(u8 size, ARM64Reg Rd, uint64_t value);
// Allow using a different size. Unclear if there's a penalty.
bool TryAnyMOVI(u8 size, ARM64Reg Rd, uint64_t value);
// One source
void FCVT(u8 size_to, u8 size_from, ARM64Reg Rd, ARM64Reg Rn);

View file

@ -472,6 +472,7 @@
<ClInclude Include="GPU\Vulkan\VulkanBarrier.h" />
<ClInclude Include="GPU\Vulkan\VulkanContext.h" />
<ClInclude Include="GPU\Vulkan\VulkanDebug.h" />
<ClInclude Include="GPU\Vulkan\VulkanDescSet.h" />
<ClInclude Include="GPU\Vulkan\VulkanFramebuffer.h" />
<ClInclude Include="GPU\Vulkan\VulkanFrameData.h" />
<ClInclude Include="GPU\Vulkan\VulkanImage.h" />
@ -900,6 +901,7 @@
<ClCompile Include="GPU\Vulkan\VulkanBarrier.cpp" />
<ClCompile Include="GPU\Vulkan\VulkanContext.cpp" />
<ClCompile Include="GPU\Vulkan\VulkanDebug.cpp" />
<ClCompile Include="GPU\Vulkan\VulkanDescSet.cpp" />
<ClCompile Include="GPU\Vulkan\VulkanFramebuffer.cpp" />
<ClCompile Include="GPU\Vulkan\VulkanFrameData.cpp" />
<ClCompile Include="GPU\Vulkan\VulkanImage.cpp" />

View file

@ -515,6 +515,9 @@
<ClInclude Include="Render\Text\draw_text_sdl.h">
<Filter>Render\Text</Filter>
</ClInclude>
<ClInclude Include="GPU\Vulkan\VulkanDescSet.h">
<Filter>GPU\Vulkan</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="ABI.cpp" />
@ -966,6 +969,9 @@
<ClCompile Include="Render\Text\draw_text_sdl.cpp">
<Filter>Render\Text</Filter>
</ClCompile>
<ClCompile Include="GPU\Vulkan\VulkanDescSet.cpp">
<Filter>GPU\Vulkan</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Filter Include="Crypto">
@ -1073,6 +1079,9 @@
<Filter Include="ext\naett">
<UniqueIdentifier>{34f45db9-5c08-49cb-b349-b9e760ce3213}</UniqueIdentifier>
</Filter>
<Filter Include="ext\libchdr">
<UniqueIdentifier>{b681797d-7747-487f-b448-5ef5b2d2805b}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<Text Include="..\ext\libpng17\CMakeLists.txt">

View file

@ -69,6 +69,7 @@ public:
void clear() { size_ = 0; }
bool empty() const { return size_ == 0; }
const T *data() { return data_; }
T *begin() { return data_; }
T *end() { return data_ + size_; }
const T *begin() const { return data_; }
@ -117,6 +118,29 @@ public:
IncreaseCapacityTo(newCapacity);
}
void extend(const T *newData, size_t count) {
IncreaseCapacityTo(size_ + count);
memcpy(data_ + size_, newData, count * sizeof(T));
size_ += count;
}
T *extend_uninitialized(size_t count) {
size_t sz = size_;
if (size_ + count <= capacity_) {
size_ += count;
return &data_[sz];
} else {
size_t newCapacity = size_ + count * 2; // Leave some extra room when growing in all cases
if (newCapacity < capacity_ * 2) {
// Standard amortized O(1).
newCapacity = capacity_ * 2;
}
IncreaseCapacityTo(newCapacity);
size_ += count;
return &data_[sz];
}
}
void LockCapacity() {
#ifdef _DEBUG
capacityLocked_ = true;

View file

@ -72,7 +72,7 @@ public:
}
bool ContainsKey(const Key &key) const {
// Slightly wasteful.
// Slightly wasteful, though compiler might optimize it.
Value value;
return Get(key, &value);
}
@ -135,6 +135,7 @@ public:
return false;
}
// This will never crash if you call it without locking - but, the value might not be right.
size_t size() const {
return count_;
}

View file

@ -617,6 +617,7 @@ void ConvertRGB565ToBGR565(u16 *dst, const u16 *src, u32 numPixels) {
u32 i = 0;
#endif
// TODO: Add a 64-bit loop too.
const u32 *src32 = (const u32 *)src;
u32 *dst32 = (u32 *)dst;
for (; i < numPixels / 2; i++) {

View file

@ -7,6 +7,7 @@
#include <inttypes.h>
// Hm, what's this for?
#ifndef _MSC_VER
#include <strings.h>
#endif
@ -19,17 +20,17 @@
#include <vector>
#include "Common/Data/Format/IniFile.h"
#include "Common/Data/Text/Parsers.h"
#include "Common/File/VFS/VFS.h"
#include "Common/File/FileUtil.h"
#include "Common/Data/Text/Parsers.h"
#ifdef _WIN32
#include "Common/Data/Encoding/Utf8.h"
#endif
#include "Common/Log.h"
#include "Common/Math/math_util.h"
#include "Common/StringUtils.h"
static bool ParseLineKey(const std::string &line, size_t &pos, std::string *keyOut) {
// This unescapes # signs.
// NOTE: These parse functions can make better use of the string_view - the pos argument should not be needed, for example.
static bool ParseLineKey(std::string_view line, size_t &pos, std::string *keyOut) {
std::string key = "";
while (pos < line.size()) {
@ -44,7 +45,8 @@ static bool ParseLineKey(const std::string &line, size_t &pos, std::string *keyO
}
// Escaped.
key += line.substr(pos, next - pos - 1) + "#";
key += line.substr(pos, next - pos - 1);
key.push_back('#');
pos = next + 1;
} else if (line[next] == '=') {
// Hurray, done.
@ -60,11 +62,11 @@ static bool ParseLineKey(const std::string &line, size_t &pos, std::string *keyO
return true;
}
static bool ParseLineValue(const std::string &line, size_t &pos, std::string *valueOut) {
static bool ParseLineValue(std::string_view line, size_t &pos, std::string *valueOut) {
std::string value = "";
std::string strippedLine = StripSpaces(line.substr(pos));
if (strippedLine[0] == '"' && strippedLine[strippedLine.size()-1] == '"') {
std::string_view strippedLine = StripSpaces(line.substr(pos));
if (strippedLine.size() >= 2 && strippedLine[0] == '"' && strippedLine[strippedLine.size() - 1] == '"') {
// Don't remove comment if is surrounded by " "
value += line.substr(pos);
pos = line.npos; // Won't enter the while below
@ -84,7 +86,8 @@ static bool ParseLineValue(const std::string &line, size_t &pos, std::string *va
break;
} else {
// Escaped.
value += line.substr(pos, next - pos - 1) + "#";
value += line.substr(pos, next - pos - 1);
value.push_back('#');
pos = next + 1;
}
}
@ -96,7 +99,7 @@ static bool ParseLineValue(const std::string &line, size_t &pos, std::string *va
return true;
}
static bool ParseLineComment(const std::string& line, size_t &pos, std::string *commentOut) {
static bool ParseLineComment(std::string_view line, size_t &pos, std::string *commentOut) {
// Don't bother with anything if we don't need the comment data.
if (commentOut) {
// Include any whitespace/formatting in the comment.
@ -117,8 +120,7 @@ static bool ParseLineComment(const std::string& line, size_t &pos, std::string *
return true;
}
// Ugh, this is ugly.
static bool ParseLine(const std::string& line, std::string* keyOut, std::string* valueOut, std::string* commentOut)
static bool ParseLine(std::string_view line, std::string* keyOut, std::string* valueOut, std::string* commentOut)
{
// Rules:
// 1. A line starting with ; is commented out.
@ -142,7 +144,7 @@ static bool ParseLine(const std::string& line, std::string* keyOut, std::string*
return true;
}
static std::string EscapeComments(const std::string &value) {
static std::string EscapeHash(std::string_view value) {
std::string result = "";
for (size_t pos = 0; pos < value.size(); ) {
@ -151,7 +153,8 @@ static std::string EscapeComments(const std::string &value) {
result += value.substr(pos);
pos = value.npos;
} else {
result += value.substr(pos, next - pos) + "\\#";
result += value.substr(pos, next - pos);
result += "\\#";
pos = next + 1;
}
}
@ -159,34 +162,56 @@ static std::string EscapeComments(const std::string &value) {
return result;
}
void ParsedIniLine::ParseFrom(std::string_view line) {
line = StripSpaces(line);
if (line.empty()) {
key.clear();
value.clear();
comment.clear();
} else if (line[0] == '#') {
key.clear();
value.clear();
comment = line;
} else {
ParseLine(line, &key, &value, &comment);
}
}
void ParsedIniLine::Reconstruct(std::string *output) const {
if (!key.empty()) {
*output = EscapeHash(key) + " = " + EscapeHash(value) + comment;
} else {
*output = comment;
}
}
void Section::Clear() {
lines.clear();
lines_.clear();
}
std::string* Section::GetLine(const char* key, std::string* valueOut, std::string* commentOut)
{
for (std::vector<std::string>::iterator iter = lines.begin(); iter != lines.end(); ++iter)
{
std::string& line = *iter;
std::string lineKey;
ParseLine(line, &lineKey, valueOut, commentOut);
if (!strcasecmp(lineKey.c_str(), key))
return &line;
bool Section::GetKeys(std::vector<std::string> &keys) const {
keys.clear();
for (auto liter = lines_.begin(); liter != lines_.end(); ++liter) {
if (!liter->Key().empty())
keys.push_back(std::string(liter->Key()));
}
return 0;
return true;
}
const std::string* Section::GetLine(const char* key, std::string* valueOut, std::string* commentOut) const
{
for (std::vector<std::string>::const_iterator iter = lines.begin(); iter != lines.end(); ++iter)
{
const std::string& line = *iter;
std::string lineKey;
ParseLine(line, &lineKey, valueOut, commentOut);
if (!strcasecmp(lineKey.c_str(), key))
ParsedIniLine *Section::GetLine(const char *key) {
for (auto &line : lines_) {
if (equalsNoCase(line.Key(), key))
return &line;
}
return 0;
return nullptr;
}
const ParsedIniLine *Section::GetLine(const char* key) const {
for (auto &line : lines_) {
if (equalsNoCase(line.Key(), key))
return &line;
}
return nullptr;
}
void Section::Set(const char* key, uint32_t newValue) {
@ -198,6 +223,7 @@ void Section::Set(const char* key, uint64_t newValue) {
}
void Section::Set(const char* key, float newValue) {
_dbg_assert_(!my_isnanorinf(newValue));
Set(key, StringFromFormat("%f", newValue).c_str());
}
@ -209,19 +235,13 @@ void Section::Set(const char* key, int newValue) {
Set(key, StringFromInt(newValue).c_str());
}
void Section::Set(const char* key, const char* newValue)
{
std::string value, commented;
std::string* line = GetLine(key, &value, &commented);
if (line)
{
// Change the value - keep the key and comment
*line = StripSpaces(key) + " = " + EscapeComments(newValue) + commented;
}
else
{
void Section::Set(const char* key, const char* newValue) {
ParsedIniLine *line = GetLine(key);
if (line) {
line->SetValue(newValue);
} else {
// The key did not already exist in this section - let's add it.
lines.emplace_back(std::string(key) + " = " + EscapeComments(newValue));
lines_.emplace_back(ParsedIniLine(key, newValue));
}
}
@ -233,16 +253,15 @@ void Section::Set(const char* key, const std::string& newValue, const std::strin
Delete(key);
}
bool Section::Get(const char* key, std::string* value, const char* defaultValue) const
{
const std::string* line = GetLine(key, value, 0);
if (!line)
{
if (defaultValue)
{
bool Section::Get(const char* key, std::string* value, const char* defaultValue) const {
const ParsedIniLine *line = GetLine(key);
if (!line) {
if (defaultValue) {
*value = defaultValue;
}
return false;
} else {
*value = line->Value();
}
return true;
}
@ -287,7 +306,7 @@ void Section::Set(const char* key, const std::vector<std::string>& newValues)
}
void Section::AddComment(const std::string &comment) {
lines.emplace_back("# " + comment);
lines_.emplace_back(ParsedIniLine::CommentOnly("# " + comment));
}
bool Section::Get(const char* key, std::vector<std::string>& values) const
@ -378,39 +397,29 @@ bool Section::Get(const char* key, double* value, double defaultValue) const
return false;
}
bool Section::Exists(const char *key) const
{
for (std::vector<std::string>::const_iterator iter = lines.begin(); iter != lines.end(); ++iter)
{
std::string lineKey;
ParseLine(*iter, &lineKey, NULL, NULL);
if (!strcasecmp(lineKey.c_str(), key))
bool Section::Exists(const char *key) const {
for (auto &line : lines_) {
if (equalsNoCase(key, line.Key()))
return true;
}
return false;
}
std::map<std::string, std::string> Section::ToMap() const
{
std::map<std::string, std::string> Section::ToMap() const {
std::map<std::string, std::string> outMap;
for (std::vector<std::string>::const_iterator iter = lines.begin(); iter != lines.end(); ++iter)
{
std::string lineKey, lineValue;
if (ParseLine(*iter, &lineKey, &lineValue, NULL)) {
outMap[lineKey] = lineValue;
for (auto &line : lines_) {
if (!line.Key().empty()) {
outMap[std::string(line.Key())] = line.Value();
}
}
return outMap;
}
bool Section::Delete(const char *key)
{
std::string* line = GetLine(key, 0, 0);
for (std::vector<std::string>::iterator liter = lines.begin(); liter != lines.end(); ++liter)
{
if (line == &*liter)
{
lines.erase(liter);
bool Section::Delete(const char *key) {
ParsedIniLine *line = GetLine(key);
for (auto liter = lines_.begin(); liter != lines_.end(); ++liter) {
if (line == &*liter) {
lines_.erase(liter);
return true;
}
}
@ -423,14 +432,14 @@ const Section* IniFile::GetSection(const char* sectionName) const {
for (const auto &iter : sections)
if (!strcasecmp(iter->name().c_str(), sectionName))
return iter.get();
return nullptr ;
return nullptr;
}
Section* IniFile::GetSection(const char* sectionName) {
for (const auto &iter : sections)
if (!strcasecmp(iter->name().c_str(), sectionName))
return iter.get();
return 0;
return nullptr;
}
Section* IniFile::GetOrCreateSection(const char* sectionName) {
@ -463,27 +472,14 @@ bool IniFile::Exists(const char* sectionName, const char* key) const {
return section->Exists(key);
}
void IniFile::SetLines(const char* sectionName, const std::vector<std::string> &lines)
{
Section* section = GetOrCreateSection(sectionName);
section->lines.clear();
for (std::vector<std::string>::const_iterator iter = lines.begin(); iter != lines.end(); ++iter)
{
section->lines.push_back(*iter);
}
}
bool IniFile::DeleteKey(const char* sectionName, const char* key)
{
bool IniFile::DeleteKey(const char* sectionName, const char* key) {
Section* section = GetSection(sectionName);
if (!section)
return false;
std::string* line = section->GetLine(key, 0, 0);
for (std::vector<std::string>::iterator liter = section->lines.begin(); liter != section->lines.end(); ++liter)
{
if (line == &(*liter))
{
section->lines.erase(liter);
ParsedIniLine *line = section->GetLine(key);
for (auto liter = section->lines_.begin(); liter != section->lines_.end(); ++liter) {
if (line == &(*liter)) {
section->lines_.erase(liter);
return true;
}
}
@ -491,55 +487,13 @@ bool IniFile::DeleteKey(const char* sectionName, const char* key)
}
// Return a list of all keys in a section
bool IniFile::GetKeys(const char* sectionName, std::vector<std::string>& keys) const
{
const Section* section = GetSection(sectionName);
bool IniFile::GetKeys(const char* sectionName, std::vector<std::string>& keys) const {
const Section *section = GetSection(sectionName);
if (!section)
return false;
keys.clear();
for (std::vector<std::string>::const_iterator liter = section->lines.begin(); liter != section->lines.end(); ++liter)
{
std::string key;
ParseLine(*liter, &key, 0, 0);
if (!key.empty())
keys.push_back(key);
}
return true;
return section->GetKeys(keys);
}
// Return a list of all lines in a section
bool IniFile::GetLines(const char* sectionName, std::vector<std::string>& lines, const bool remove_comments) const
{
const Section* section = GetSection(sectionName);
if (!section)
return false;
lines.clear();
for (std::vector<std::string>::const_iterator iter = section->lines.begin(); iter != section->lines.end(); ++iter)
{
std::string line = StripSpaces(*iter);
if (remove_comments)
{
int commentPos = (int)line.find('#');
if (commentPos == 0)
{
continue;
}
if (commentPos != (int)std::string::npos)
{
line = StripSpaces(line.substr(0, commentPos));
}
}
lines.push_back(line);
}
return true;
}
void IniFile::SortSections()
{
std::sort(sections.begin(), sections.end());
@ -613,7 +567,9 @@ bool IniFile::Load(std::istream &in) {
if (sections.empty()) {
sections.push_back(std::unique_ptr<Section>(new Section("")));
}
sections.back()->lines.push_back(line);
ParsedIniLine parsedLine;
parsedLine.ParseFrom(line);
sections.back()->lines_.push_back(parsedLine);
}
}
}
@ -634,12 +590,13 @@ bool IniFile::Save(const Path &filename)
fprintf(file, "\xEF\xBB\xBF");
for (const auto &section : sections) {
if (!section->name().empty() && (!section->lines.empty() || !section->comment.empty())) {
if (!section->name().empty() && (!section->lines_.empty() || !section->comment.empty())) {
fprintf(file, "[%s]%s\n", section->name().c_str(), section->comment.c_str());
}
for (const std::string &s : section->lines) {
fprintf(file, "%s\n", s.c_str());
for (const auto &line : section->lines_) {
std::string buffer;
line.Reconstruct(&buffer);
fprintf(file, "%s\n", buffer.c_str());
}
}

View file

@ -8,6 +8,7 @@
#include <memory>
#include <map>
#include <string>
#include <string_view>
#include <vector>
#include <cstdint>
@ -15,6 +16,39 @@
class VFSInterface;
class ParsedIniLine {
public:
ParsedIniLine() {}
ParsedIniLine(std::string_view key, std::string_view value) {
this->key = key;
this->value = value;
}
ParsedIniLine(std::string_view key, std::string_view value, std::string_view comment) {
this->key = key;
this->value = value;
this->comment = comment;
}
static ParsedIniLine CommentOnly(std::string_view comment) {
return ParsedIniLine(std::string_view(), std::string_view(), comment);
}
// Comments only come from "ParseFrom".
void ParseFrom(std::string_view line);
void Reconstruct(std::string *output) const;
// Having these as views allows a more efficient internal representation, like one joint string.
std::string_view Key() const { return key; }
std::string_view Value() const { return value; }
std::string_view Comment() const { return comment; }
void SetValue(std::string_view newValue) { value = newValue; }
private:
std::string key;
std::string value;
std::string comment;
};
class Section {
friend class IniFile;
@ -29,8 +63,8 @@ public:
std::map<std::string, std::string> ToMap() const;
std::string *GetLine(const char* key, std::string* valueOut, std::string* commentOut);
const std::string *GetLine(const char* key, std::string* valueOut, std::string* commentOut) const;
ParsedIniLine *GetLine(const char *key);
const ParsedIniLine *GetLine(const char *key) const;
void Set(const char* key, const char* newValue);
void Set(const char* key, const std::string& newValue, const std::string& defaultValue);
@ -71,6 +105,9 @@ public:
bool Get(const char* key, double* value, double defaultValue = false) const;
bool Get(const char* key, std::vector<std::string>& values) const;
// Return a list of all keys in this section
bool GetKeys(std::vector<std::string> &keys) const;
bool operator < (const Section& other) const {
return name_ < other.name_;
}
@ -80,7 +117,7 @@ public:
}
protected:
std::vector<std::string> lines;
std::vector<ParsedIniLine> lines_;
std::string name_;
std::string comment;
};
@ -88,12 +125,10 @@ protected:
class IniFile {
public:
bool Load(const Path &path);
bool Load(const std::string &filename) { return Load(Path(filename)); }
bool Load(std::istream &istream);
bool LoadFromVFS(VFSInterface &vfs, const std::string &filename);
bool Save(const Path &path);
bool Save(const std::string &filename) { return Save(Path(filename)); }
// Returns true if key exists in section
bool Exists(const char* sectionName, const char* key) const;
@ -138,9 +173,6 @@ public:
bool GetKeys(const char* sectionName, std::vector<std::string>& keys) const;
void SetLines(const char* sectionName, const std::vector<std::string> &lines);
bool GetLines(const char* sectionName, std::vector<std::string>& lines, const bool remove_comments = true) const;
bool DeleteKey(const char* sectionName, const char* key);
bool DeleteSection(const char* sectionName);

View file

@ -116,8 +116,9 @@ public:
std::string LanguageID();
std::shared_ptr<I18NCategory> GetCategory(I18NCat category);
std::shared_ptr<I18NCategory> GetCategoryByName(const char *name);
// Translate the string, by looking up "key" in the file, and falling back to either def or key, in that order, if the lookup fails.
// def can (and usually is) set to nullptr.
const char *T(I18NCat category, const char *key, const char *def = nullptr) {
if (category == I18NCat::NONE)
return def ? def : key;

View file

@ -19,7 +19,7 @@ void NiceSizeFormat(uint64_t size, char *out, size_t bufSize) {
if (s == 0)
snprintf(out, bufSize, "%d B", (int)size);
else
snprintf(out, bufSize, "%3.1f %s", f, sizes[s]);
snprintf(out, bufSize, "%3.2f %s", f, sizes[s]);
}
std::string NiceSizeFormat(uint64_t size) {

View file

@ -1,14 +1,14 @@
#include "Common/File/AndroidContentURI.h"
bool AndroidContentURI::Parse(const std::string &path) {
bool AndroidContentURI::Parse(std::string_view path) {
const char *prefix = "content://";
if (!startsWith(path, prefix)) {
return false;
}
std::string components = path.substr(strlen(prefix));
std::string_view components = path.substr(strlen(prefix));
std::vector<std::string> parts;
std::vector<std::string_view> parts;
SplitString(components, '/', parts);
if (parts.size() == 3) {
// Single file URI.
@ -60,7 +60,7 @@ AndroidContentURI AndroidContentURI::WithRootFilePath(const std::string &filePat
return uri;
}
AndroidContentURI AndroidContentURI::WithComponent(const std::string &filePath) {
AndroidContentURI AndroidContentURI::WithComponent(std::string_view filePath) {
AndroidContentURI uri = *this;
if (uri.file.empty()) {
// Not sure what to do.
@ -68,16 +68,17 @@ AndroidContentURI AndroidContentURI::WithComponent(const std::string &filePath)
}
if (uri.file.back() == ':') {
// Special case handling for Document URIs: Treat the ':' as a directory separator too (but preserved in the filename).
uri.file = uri.file + filePath;
uri.file.append(filePath);
} else {
uri.file = uri.file + "/" + filePath;
uri.file.push_back('/');
uri.file.append(filePath);
}
return uri;
}
AndroidContentURI AndroidContentURI::WithExtraExtension(const std::string &extension) {
AndroidContentURI AndroidContentURI::WithExtraExtension(std::string_view extension) {
AndroidContentURI uri = *this;
uri.file = uri.file + extension;
uri.file.append(extension);
return uri;
}

View file

@ -23,15 +23,15 @@ private:
std::string file;
public:
AndroidContentURI() {}
explicit AndroidContentURI(const std::string &path) {
explicit AndroidContentURI(std::string_view path) {
Parse(path);
}
bool Parse(const std::string &path);
bool Parse(std::string_view path);
AndroidContentURI WithRootFilePath(const std::string &filePath);
AndroidContentURI WithComponent(const std::string &filePath);
AndroidContentURI WithExtraExtension(const std::string &extension);
AndroidContentURI WithComponent(std::string_view filePath);
AndroidContentURI WithExtraExtension(std::string_view extension); // The ext string contains the dot.
AndroidContentURI WithReplacedExtension(const std::string &oldExtension, const std::string &newExtension) const;
AndroidContentURI WithReplacedExtension(const std::string &newExtension) const;

View file

@ -61,16 +61,16 @@ void Android_RegisterStorageCallbacks(JNIEnv * env, jobject obj) {
_dbg_assert_(computeRecursiveDirectorySize);
}
bool Android_IsContentUri(const std::string &filename) {
bool Android_IsContentUri(std::string_view filename) {
return startsWith(filename, "content://");
}
int Android_OpenContentUriFd(const std::string &filename, Android_OpenContentUriMode mode) {
int Android_OpenContentUriFd(std::string_view filename, Android_OpenContentUriMode mode) {
if (!g_nativeActivity) {
return -1;
}
std::string fname = filename;
std::string fname(filename);
// PPSSPP adds an ending slash to directories before looking them up.
// TODO: Fix that in the caller (or don't call this for directories).
if (fname.back() == '/')

View file

@ -2,6 +2,7 @@
#include <vector>
#include <string>
#include <string_view>
#include "Common/File/DirListing.h"
@ -39,8 +40,8 @@ extern std::string g_externalDir;
void Android_StorageSetNativeActivity(jobject nativeActivity);
bool Android_IsContentUri(const std::string &uri);
int Android_OpenContentUriFd(const std::string &uri, const Android_OpenContentUriMode mode);
bool Android_IsContentUri(std::string_view uri);
int Android_OpenContentUriFd(std::string_view uri, const Android_OpenContentUriMode mode);
StorageError Android_CreateDirectory(const std::string &parentTreeUri, const std::string &dirName);
StorageError Android_CreateFile(const std::string &parentTreeUri, const std::string &fileName);
StorageError Android_MoveFile(const std::string &fileUri, const std::string &srcParentUri, const std::string &destParentUri);
@ -63,8 +64,8 @@ void Android_RegisterStorageCallbacks(JNIEnv * env, jobject obj);
// Stub out the Android Storage wrappers, so that we can avoid ifdefs everywhere.
inline bool Android_IsContentUri(const std::string &uri) { return false; }
inline int Android_OpenContentUriFd(const std::string &uri, const Android_OpenContentUriMode mode) { return -1; }
inline bool Android_IsContentUri(std::string_view uri) { return false; }
inline int Android_OpenContentUriFd(std::string_view uri, const Android_OpenContentUriMode mode) { return -1; }
inline StorageError Android_CreateDirectory(const std::string &parentTreeUri, const std::string &dirName) { return StorageError::UNKNOWN; }
inline StorageError Android_CreateFile(const std::string &parentTreeUri, const std::string &fileName) { return StorageError::UNKNOWN; }
inline StorageError Android_MoveFile(const std::string &fileUri, const std::string &srcParentUri, const std::string &destParentUri) { return StorageError::UNKNOWN; }

View file

@ -184,7 +184,7 @@ bool GetFilesInDir(const Path &directory, std::vector<FileInfo> *files, const ch
std::string tmp;
while (*filter) {
if (*filter == ':') {
filters.insert(std::move(tmp));
filters.insert(tmp);
tmp.clear();
} else {
tmp.push_back(*filter);
@ -192,7 +192,7 @@ bool GetFilesInDir(const Path &directory, std::vector<FileInfo> *files, const ch
filter++;
}
if (!tmp.empty())
filters.insert(std::move(tmp));
filters.insert(tmp);
}
#if PPSSPP_PLATFORM(WINDOWS)

View file

@ -592,7 +592,7 @@ bool CreateFullPath(const Path &path) {
return false;
}
std::vector<std::string> parts;
std::vector<std::string_view> parts;
SplitString(diff, '/', parts);
// Probably not necessary sanity check, ported from the old code.
@ -602,7 +602,7 @@ bool CreateFullPath(const Path &path) {
}
Path curPath = root;
for (auto &part : parts) {
for (auto part : parts) {
curPath /= part;
if (!File::Exists(curPath)) {
File::CreateDir(curPath);
@ -1181,6 +1181,7 @@ uint8_t *ReadLocalFile(const Path &filename, size_t *size) {
return nullptr;
}
fseek(file, 0, SEEK_SET);
// NOTE: If you find ~10 memory leaks from here, with very varying sizes, it might be the VFPU LUTs.
uint8_t *contents = new uint8_t[f_size + 1];
if (fread(contents, 1, f_size, file) != f_size) {
delete[] contents;

View file

@ -22,7 +22,7 @@
#include <sys/stat.h>
#endif
Path::Path(const std::string &str) {
Path::Path(std::string_view str) {
Init(str);
}
@ -33,7 +33,7 @@ Path::Path(const std::wstring &str) {
}
#endif
void Path::Init(const std::string &str) {
void Path::Init(std::string_view str) {
if (str.empty()) {
type_ = PathType::UNDEFINED;
path_.clear();
@ -81,7 +81,7 @@ void Path::Init(const std::string &str) {
// We always use forward slashes internally, we convert to backslash only when
// converted to a wstring.
Path Path::operator /(const std::string &subdir) const {
Path Path::operator /(std::string_view subdir) const {
if (type_ == PathType::CONTENT_URI) {
AndroidContentURI uri(path_);
return Path(uri.WithComponent(subdir).ToString());
@ -104,18 +104,18 @@ Path Path::operator /(const std::string &subdir) const {
return Path(fullPath);
}
void Path::operator /=(const std::string &subdir) {
void Path::operator /=(std::string_view subdir) {
*this = *this / subdir;
}
Path Path::WithExtraExtension(const std::string &ext) const {
Path Path::WithExtraExtension(std::string_view ext) const {
if (type_ == PathType::CONTENT_URI) {
AndroidContentURI uri(path_);
return Path(uri.WithExtraExtension(ext).ToString());
}
_dbg_assert_(!ext.empty() && ext[0] == '.');
return Path(path_ + ext);
return Path(path_ + std::string(ext));
}
Path Path::WithReplacedExtension(const std::string &oldExtension, const std::string &newExtension) const {
@ -161,7 +161,7 @@ std::string Path::GetFilename() const {
return path_;
}
std::string GetExtFromString(const std::string &str) {
std::string GetExtFromString(std::string_view str) {
size_t pos = str.rfind(".");
if (pos == std::string::npos) {
return "";
@ -171,7 +171,7 @@ std::string GetExtFromString(const std::string &str) {
// Don't want to detect "df/file" from "/as.df/file"
return "";
}
std::string ext = str.substr(pos);
std::string ext(str.substr(pos));
for (size_t i = 0; i < ext.size(); i++) {
ext[i] = tolower(ext[i]);
}

View file

@ -3,6 +3,7 @@
#include "ppsspp_config.h"
#include <string>
#include <string_view>
#if defined(__APPLE__)
@ -36,11 +37,11 @@ enum class PathType {
class Path {
private:
void Init(const std::string &str);
void Init(std::string_view str);
public:
Path() : type_(PathType::UNDEFINED) {}
explicit Path(const std::string &str);
explicit Path(std::string_view str);
#if PPSSPP_PLATFORM(WINDOWS)
explicit Path(const std::wstring &str);
@ -71,13 +72,13 @@ public:
bool IsAbsolute() const;
// Returns a path extended with a subdirectory.
Path operator /(const std::string &subdir) const;
Path operator /(std::string_view subdir) const;
// Navigates down into a subdir.
void operator /=(const std::string &subdir);
void operator /=(std::string_view subdir);
// File extension manipulation.
Path WithExtraExtension(const std::string &ext) const;
Path WithExtraExtension(std::string_view ext) const;
Path WithReplacedExtension(const std::string &oldExtension, const std::string &newExtension) const;
Path WithReplacedExtension(const std::string &newExtension) const;
@ -139,7 +140,7 @@ private:
};
// Utility function for parsing out file extensions.
std::string GetExtFromString(const std::string &str);
std::string GetExtFromString(std::string_view str);
// Utility function for fixing the case of paths. Only present on Unix-like systems.

View file

@ -78,7 +78,7 @@ bool LoadRemoteFileList(const Path &url, const std::string &userAgent, bool *can
return false;
}
for (std::string item : items) {
for (auto &item : items) {
// Apply some workarounds.
if (item.empty())
continue;

View file

@ -106,7 +106,7 @@ public:
void BindTextures(int start, int count, Texture **textures, TextureBindFlags flags) override;
void BindNativeTexture(int index, void *nativeTexture) override;
void BindSamplerStates(int start, int count, SamplerState **states) override;
void BindVertexBuffers(int start, int count, Buffer **buffers, const int *offsets) override;
void BindVertexBuffer(Buffer *buffers, int offset) override;
void BindIndexBuffer(Buffer *indexBuffer, int offset) override;
void BindPipeline(Pipeline *pipeline) override;
@ -214,12 +214,12 @@ private:
ID3D11GeometryShader *curGS_ = nullptr;
D3D11_PRIMITIVE_TOPOLOGY curTopology_ = D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED;
ID3D11Buffer *nextVertexBuffers_[4]{};
int nextVertexBufferOffsets_[4]{};
ID3D11Buffer *nextVertexBuffer_ = nullptr;
UINT nextVertexBufferOffset_ = 0;
bool dirtyIndexBuffer_ = false;
ID3D11Buffer *nextIndexBuffer_ = nullptr;
int nextIndexBufferOffset_ = 0;
UINT nextIndexBufferOffset_ = 0;
InvalidationCallback invalidationCallback_;
int frameCount_ = FRAME_TIME_HISTORY_LENGTH;
@ -725,7 +725,7 @@ public:
D3D11InputLayout() {}
InputLayoutDesc desc;
std::vector<D3D11_INPUT_ELEMENT_DESC> elements;
std::vector<int> strides;
UINT stride; // type to match function parameter
};
const char *semanticToD3D11(int semantic, UINT *index) {
@ -752,15 +752,13 @@ InputLayout *D3D11DrawContext::CreateInputLayout(const InputLayoutDesc &desc) {
D3D11_INPUT_ELEMENT_DESC el;
el.AlignedByteOffset = desc.attributes[i].offset;
el.Format = dataFormatToD3D11(desc.attributes[i].format);
el.InstanceDataStepRate = desc.bindings[desc.attributes[i].binding].instanceRate ? 1 : 0;
el.InputSlot = desc.attributes[i].binding;
el.InstanceDataStepRate = 0;
el.InputSlot = 0;
el.SemanticName = semanticToD3D11(desc.attributes[i].location, &el.SemanticIndex);
el.InputSlotClass = desc.bindings[desc.attributes[i].binding].instanceRate ? D3D11_INPUT_PER_INSTANCE_DATA : D3D11_INPUT_PER_VERTEX_DATA;
el.InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
inputLayout->elements.push_back(el);
}
for (size_t i = 0; i < desc.bindings.size(); i++) {
inputLayout->strides.push_back(desc.bindings[i].stride);
}
inputLayout->stride = desc.stride;
return inputLayout;
}
@ -1253,8 +1251,7 @@ void D3D11DrawContext::ApplyCurrentState() {
}
if (curPipeline_->input != nullptr) {
int numVBs = (int)curPipeline_->input->strides.size();
context_->IASetVertexBuffers(0, numVBs, nextVertexBuffers_, (UINT *)curPipeline_->input->strides.data(), (UINT *)nextVertexBufferOffsets_);
context_->IASetVertexBuffers(0, 1, &nextVertexBuffer_, &curPipeline_->input->stride, &nextVertexBufferOffset_);
}
if (dirtyIndexBuffer_) {
context_->IASetIndexBuffer(nextIndexBuffer_, DXGI_FORMAT_R16_UINT, nextIndexBufferOffset_);
@ -1323,14 +1320,11 @@ void D3D11DrawContext::UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t
context_->UpdateSubresource(buf->buf, 0, &box, data, 0, 0);
}
void D3D11DrawContext::BindVertexBuffers(int start, int count, Buffer **buffers, const int *offsets) {
_assert_(start + count <= ARRAY_SIZE(nextVertexBuffers_));
void D3D11DrawContext::BindVertexBuffer(Buffer *buffer, int offset) {
// Lazy application
for (int i = 0; i < count; i++) {
D3D11Buffer *buf = (D3D11Buffer *)buffers[i];
nextVertexBuffers_[start + i] = buf->buf;
nextVertexBufferOffsets_[start + i] = offsets ? offsets[i] : 0;
}
D3D11Buffer *buf = (D3D11Buffer *)buffer;
nextVertexBuffer_ = buf->buf;
nextVertexBufferOffset_ = offset;
}
void D3D11DrawContext::BindIndexBuffer(Buffer *indexBuffer, int offset) {
@ -1354,10 +1348,10 @@ void D3D11DrawContext::DrawIndexed(int indexCount, int offset) {
void D3D11DrawContext::DrawUP(const void *vdata, int vertexCount) {
ApplyCurrentState();
int byteSize = vertexCount * curPipeline_->input->strides[0];
int byteSize = vertexCount * curPipeline_->input->stride;
UpdateBuffer(upBuffer_, (const uint8_t *)vdata, 0, byteSize, Draw::UPDATE_DISCARD);
BindVertexBuffers(0, 1, &upBuffer_, nullptr);
BindVertexBuffer(upBuffer_, 0);
int offset = 0;
Draw(vertexCount, offset);
}
@ -1565,7 +1559,7 @@ void D3D11DrawContext::BeginFrame(DebugFlags debugFlags) {
context_->IASetPrimitiveTopology(curTopology_);
}
if (curPipeline_ != nullptr) {
context_->IASetVertexBuffers(0, 1, nextVertexBuffers_, (UINT *)curPipeline_->input->strides.data(), (UINT *)nextVertexBufferOffsets_);
context_->IASetVertexBuffers(0, 1, &nextVertexBuffer_, &curPipeline_->input->stride, &nextVertexBufferOffset_);
context_->IASetIndexBuffer(nextIndexBuffer_, DXGI_FORMAT_R16_UINT, nextIndexBufferOffset_);
if (curPipeline_->dynamicUniforms) {
context_->VSSetConstantBuffers(0, 1, &curPipeline_->dynamicUniforms);

View file

@ -231,14 +231,14 @@ public:
decl_->Release();
}
}
int GetStride(int binding) const { return stride_[binding]; }
int GetStride() const { return stride_; }
void Apply(LPDIRECT3DDEVICE9 device) {
device->SetVertexDeclaration(decl_);
}
private:
LPDIRECT3DVERTEXDECLARATION9 decl_;
int stride_[4];
int stride_;
};
class D3D9ShaderModule : public ShaderModule {
@ -560,12 +560,9 @@ public:
s->Apply(device_, start + i);
}
}
void BindVertexBuffers(int start, int count, Buffer **buffers, const int *offsets) override {
_assert_(start + count <= ARRAY_SIZE(curVBuffers_));
for (int i = 0; i < count; i++) {
curVBuffers_[i + start] = (D3D9Buffer *)buffers[i];
curVBufferOffsets_[i + start] = offsets ? offsets[i] : 0;
}
void BindVertexBuffer(Buffer *vertexBuffer, int offset) override {
curVBuffer_ = (D3D9Buffer *)vertexBuffer;
curVBufferOffset_ = offset;
}
void BindIndexBuffer(Buffer *indexBuffer, int offset) override {
curIBuffer_ = (D3D9Buffer *)indexBuffer;
@ -645,8 +642,8 @@ private:
// Bound state
AutoRef<D3D9Pipeline> curPipeline_;
AutoRef<D3D9Buffer> curVBuffers_[4];
int curVBufferOffsets_[4]{};
AutoRef<D3D9Buffer> curVBuffer_;
int curVBufferOffset_ = 0;
AutoRef<D3D9Buffer> curIBuffer_;
int curIBufferOffset_ = 0;
AutoRef<Framebuffer> curRenderTarget_;
@ -1028,7 +1025,7 @@ D3D9InputLayout::D3D9InputLayout(LPDIRECT3DDEVICE9 device, const InputLayoutDesc
D3DVERTEXELEMENT9 *elements = new D3DVERTEXELEMENT9[desc.attributes.size() + 1];
size_t i;
for (i = 0; i < desc.attributes.size(); i++) {
elements[i].Stream = desc.attributes[i].binding;
elements[i].Stream = 0;
elements[i].Offset = desc.attributes[i].offset;
elements[i].Method = D3DDECLMETHOD_DEFAULT;
SemanticToD3D9UsageAndIndex(desc.attributes[i].location, &elements[i].Usage, &elements[i].UsageIndex);
@ -1038,9 +1035,7 @@ D3D9InputLayout::D3D9InputLayout(LPDIRECT3DDEVICE9 device, const InputLayoutDesc
// Zero the last one.
memcpy(&elements[i], &end, sizeof(elements[i]));
for (i = 0; i < desc.bindings.size(); i++) {
stride_[i] = desc.bindings[i].stride;
}
stride_ = desc.stride;
HRESULT hr = device->CreateVertexDeclaration(elements, &decl_);
if (FAILED(hr)) {
@ -1174,7 +1169,7 @@ inline int D3DPrimCount(D3DPRIMITIVETYPE prim, int size) {
}
void D3D9Context::Draw(int vertexCount, int offset) {
device_->SetStreamSource(0, curVBuffers_[0]->vbuffer_, curVBufferOffsets_[0], curPipeline_->inputLayout->GetStride(0));
device_->SetStreamSource(0, curVBuffer_->vbuffer_, curVBufferOffset_, curPipeline_->inputLayout->GetStride());
curPipeline_->inputLayout->Apply(device_);
curPipeline_->Apply(device_, stencilRef_, stencilWriteMask_, stencilCompareMask_);
ApplyDynamicState();
@ -1185,7 +1180,7 @@ void D3D9Context::DrawIndexed(int vertexCount, int offset) {
curPipeline_->inputLayout->Apply(device_);
curPipeline_->Apply(device_, stencilRef_, stencilWriteMask_, stencilCompareMask_);
ApplyDynamicState();
device_->SetStreamSource(0, curVBuffers_[0]->vbuffer_, curVBufferOffsets_[0], curPipeline_->inputLayout->GetStride(0));
device_->SetStreamSource(0, curVBuffer_->vbuffer_, curVBufferOffset_, curPipeline_->inputLayout->GetStride());
device_->SetIndices(curIBuffer_->ibuffer_);
device_->DrawIndexedPrimitive(curPipeline_->prim, 0, 0, vertexCount, offset, D3DPrimCount(curPipeline_->prim, vertexCount));
}
@ -1195,7 +1190,7 @@ void D3D9Context::DrawUP(const void *vdata, int vertexCount) {
curPipeline_->Apply(device_, stencilRef_, stencilWriteMask_, stencilCompareMask_);
ApplyDynamicState();
device_->DrawPrimitiveUP(curPipeline_->prim, D3DPrimCount(curPipeline_->prim, vertexCount), vdata, curPipeline_->inputLayout->GetStride(0));
device_->DrawPrimitiveUP(curPipeline_->prim, D3DPrimCount(curPipeline_->prim, vertexCount), vdata, curPipeline_->inputLayout->GetStride());
}
static uint32_t SwapRB(uint32_t c) {

View file

@ -146,12 +146,14 @@ bool Thin3DFormatToGLFormatAndType(DataFormat fmt, GLuint &internalFormat, GLuin
alignment = 16;
break;
#ifdef GL_COMPRESSED_RGBA_ASTC_4x4_KHR
case DataFormat::ASTC_4x4_UNORM_BLOCK:
internalFormat = GL_COMPRESSED_RGBA_ASTC_4x4_KHR;
format = GL_RGBA;
type = GL_FLOAT;
alignment = 16;
break;
#endif
default:
return false;

View file

@ -592,7 +592,9 @@ bool CheckGLExtensions() {
for (int i = 0; i < numCompressedFormats; i++) {
switch (compressedFormats[i]) {
case GL_COMPRESSED_RGB8_ETC2: gl_extensions.supportsETC2 = true; break;
#ifdef GL_COMPRESSED_RGBA_ASTC_4x4_KHR
case GL_COMPRESSED_RGBA_ASTC_4x4_KHR: gl_extensions.supportsASTC = true; break;
#endif
#ifndef USING_GLES2
case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: gl_extensions.supportsBC123 = true; break;
case GL_COMPRESSED_RGBA_BPTC_UNORM: gl_extensions.supportsBC7 = true; break;

View file

@ -32,25 +32,25 @@ void GLDeleter::Perform(GLRenderManager *renderManager, bool skipGLCalls) {
}
pushBuffers.clear();
for (auto shader : shaders) {
if (skipGLCalls)
if (skipGLCalls && shader)
shader->shader = 0; // prevent the glDeleteShader
delete shader;
}
shaders.clear();
for (auto program : programs) {
if (skipGLCalls)
if (skipGLCalls && program)
program->program = 0; // prevent the glDeleteProgram
delete program;
}
programs.clear();
for (auto buffer : buffers) {
if (skipGLCalls)
if (skipGLCalls && buffer)
buffer->buffer_ = 0;
delete buffer;
}
buffers.clear();
for (auto texture : textures) {
if (skipGLCalls)
if (skipGLCalls && texture)
texture->texture = 0;
delete texture;
}

View file

@ -334,10 +334,10 @@ void GLQueueRunner::RunInitSteps(const FastVec<GLRInitStep> &steps, bool skipGLC
step.create_shader.shader->desc.c_str(),
infoLog.c_str(),
LineNumberString(code).c_str());
std::vector<std::string> lines;
std::vector<std::string_view> lines;
SplitString(errorString, '\n', lines);
for (auto &line : lines) {
ERROR_LOG(G3D, "%s", line.c_str());
for (auto line : lines) {
ERROR_LOG(G3D, "%.*s", (int)line.size(), line.data());
}
if (errorCallback_) {
std::string desc = StringFromFormat("Shader compilation failed: %s", step.create_shader.stage == GL_VERTEX_SHADER ? "vertex" : "fragment");
@ -1251,7 +1251,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step, bool first, bool last
}
for (size_t i = 0; i < layout->entries.size(); i++) {
auto &entry = layout->entries[i];
glVertexAttribPointer(entry.location, entry.count, entry.type, entry.normalized, entry.stride, (const void *)(c.draw.vertexOffset + entry.offset));
glVertexAttribPointer(entry.location, entry.count, entry.type, entry.normalized, layout->stride, (const void *)(c.draw.vertexOffset + entry.offset));
}
if (c.draw.indexBuffer) {
GLuint buf = c.draw.indexBuffer->buffer_;

View file

@ -190,10 +190,10 @@ public:
int count;
GLenum type;
GLboolean normalized;
int stride;
intptr_t offset;
};
std::vector<Entry> entries;
int stride;
int semanticsMask_ = 0;
};
@ -331,11 +331,12 @@ public:
return step.create_program.program;
}
GLRInputLayout *CreateInputLayout(const std::vector<GLRInputLayout::Entry> &entries) {
GLRInputLayout *CreateInputLayout(const std::vector<GLRInputLayout::Entry> &entries, int stride) {
GLRInitStep &step = initSteps_.push_uninitialized();
step.stepType = GLRInitStepType::CREATE_INPUT_LAYOUT;
step.create_input_layout.inputLayout = new GLRInputLayout();
step.create_input_layout.inputLayout->entries = entries;
step.create_input_layout.inputLayout->stride = stride;
for (auto &iter : step.create_input_layout.inputLayout->entries) {
step.create_input_layout.inputLayout->semanticsMask_ |= 1 << iter.location;
}
@ -349,24 +350,31 @@ public:
}
void DeleteShader(GLRShader *shader) {
_dbg_assert_(shader != nullptr);
deleter_.shaders.push_back(shader);
}
void DeleteProgram(GLRProgram *program) {
_dbg_assert_(program != nullptr);
deleter_.programs.push_back(program);
}
void DeleteBuffer(GLRBuffer *buffer) {
_dbg_assert_(buffer != nullptr);
deleter_.buffers.push_back(buffer);
}
void DeleteTexture(GLRTexture *texture) {
_dbg_assert_(texture != nullptr);
deleter_.textures.push_back(texture);
}
void DeleteInputLayout(GLRInputLayout *inputLayout) {
_dbg_assert_(inputLayout != nullptr);
deleter_.inputLayouts.push_back(inputLayout);
}
void DeleteFramebuffer(GLRFramebuffer *framebuffer) {
_dbg_assert_(framebuffer != nullptr);
deleter_.framebuffers.push_back(framebuffer);
}
void DeletePushBuffer(GLPushBuffer *pushbuffer) {
_dbg_assert_(pushbuffer != nullptr);
deleter_.pushBuffers.push_back(pushbuffer);
}

View file

@ -421,12 +421,9 @@ public:
void BindNativeTexture(int sampler, void *nativeTexture) override;
void BindPipeline(Pipeline *pipeline) override;
void BindVertexBuffers(int start, int count, Buffer **buffers, const int *offsets) override {
_assert_(start + count <= ARRAY_SIZE(curVBuffers_));
for (int i = 0; i < count; i++) {
curVBuffers_[i + start] = (OpenGLBuffer *)buffers[i];
curVBufferOffsets_[i + start] = offsets ? offsets[i] : 0;
}
void BindVertexBuffer(Buffer *buffer, int offset) override {
curVBuffer_ = (OpenGLBuffer *)buffer;
curVBufferOffset_ = offset;
}
void BindIndexBuffer(Buffer *indexBuffer, int offset) override {
curIBuffer_ = (OpenGLBuffer *)indexBuffer;
@ -505,9 +502,9 @@ private:
const GLRTexture *boundTextures_[MAX_TEXTURE_SLOTS]{};
AutoRef<OpenGLPipeline> curPipeline_;
AutoRef<OpenGLBuffer> curVBuffers_[4]{};
int curVBufferOffsets_[4]{};
AutoRef<OpenGLBuffer> curVBuffer_;
AutoRef<OpenGLBuffer> curIBuffer_;
int curVBufferOffset_ = 0;
int curIBufferOffset_ = 0;
AutoRef<Framebuffer> curRenderTarget_;
@ -934,7 +931,7 @@ void OpenGLTexture::UpdateTextureLevels(GLRenderManager *render, const uint8_t *
OpenGLTexture::~OpenGLTexture() {
if (tex_) {
render_->DeleteTexture(tex_);
tex_ = 0;
tex_ = nullptr;
generatedMips_ = false;
}
}
@ -1370,20 +1367,20 @@ void OpenGLContext::UpdateDynamicUniformBuffer(const void *ub, size_t size) {
}
void OpenGLContext::Draw(int vertexCount, int offset) {
_dbg_assert_msg_(curVBuffers_[0] != nullptr, "Can't call Draw without a vertex buffer");
_dbg_assert_msg_(curVBuffer_ != nullptr, "Can't call Draw without a vertex buffer");
ApplySamplers();
_assert_(curPipeline_->inputLayout);
renderManager_.Draw(curPipeline_->inputLayout->inputLayout_, curVBuffers_[0]->buffer_, curVBufferOffsets_[0], curPipeline_->prim, offset, vertexCount);
renderManager_.Draw(curPipeline_->inputLayout->inputLayout_, curVBuffer_->buffer_, curVBufferOffset_, curPipeline_->prim, offset, vertexCount);
}
void OpenGLContext::DrawIndexed(int vertexCount, int offset) {
_dbg_assert_msg_(curVBuffers_[0] != nullptr, "Can't call DrawIndexed without a vertex buffer");
_dbg_assert_msg_(curVBuffer_ != nullptr, "Can't call DrawIndexed without a vertex buffer");
_dbg_assert_msg_(curIBuffer_ != nullptr, "Can't call DrawIndexed without an index buffer");
ApplySamplers();
_assert_(curPipeline_->inputLayout);
renderManager_.DrawIndexed(
curPipeline_->inputLayout->inputLayout_,
curVBuffers_[0]->buffer_, curVBufferOffsets_[0],
curVBuffer_->buffer_, curVBufferOffset_,
curIBuffer_->buffer_, curIBufferOffset_ + offset * sizeof(uint32_t),
curPipeline_->prim, vertexCount, GL_UNSIGNED_SHORT);
}
@ -1432,13 +1429,12 @@ OpenGLInputLayout::~OpenGLInputLayout() {
void OpenGLInputLayout::Compile(const InputLayoutDesc &desc) {
// TODO: This is only accurate if there's only one stream. But whatever, for now we
// never use multiple streams anyway.
stride = desc.bindings.empty() ? 0 : (GLsizei)desc.bindings[0].stride;
stride = desc.stride;
std::vector<GLRInputLayout::Entry> entries;
for (auto &attr : desc.attributes) {
GLRInputLayout::Entry entry;
entry.location = attr.location;
entry.stride = (GLsizei)desc.bindings[attr.binding].stride;
entry.offset = attr.offset;
switch (attr.format) {
case DataFormat::R32G32_FLOAT:
@ -1470,7 +1466,7 @@ void OpenGLInputLayout::Compile(const InputLayoutDesc &desc) {
entries.push_back(entry);
}
if (!entries.empty()) {
inputLayout_ = render_->CreateInputLayout(entries);
inputLayout_ = render_->CreateInputLayout(entries, stride);
} else {
inputLayout_ = nullptr;
}

View file

@ -1666,80 +1666,101 @@ void VulkanDeleteList::Take(VulkanDeleteList &del) {
}
void VulkanDeleteList::PerformDeletes(VulkanContext *vulkan, VmaAllocator allocator) {
int deleteCount = 0;
for (auto &callback : callbacks_) {
callback.func(vulkan, callback.userdata);
deleteCount++;
}
callbacks_.clear();
VkDevice device = vulkan->GetDevice();
for (auto &cmdPool : cmdPools_) {
vkDestroyCommandPool(device, cmdPool, nullptr);
deleteCount++;
}
cmdPools_.clear();
for (auto &descPool : descPools_) {
vkDestroyDescriptorPool(device, descPool, nullptr);
deleteCount++;
}
descPools_.clear();
for (auto &module : modules_) {
vkDestroyShaderModule(device, module, nullptr);
deleteCount++;
}
modules_.clear();
for (auto &buf : buffers_) {
vkDestroyBuffer(device, buf, nullptr);
deleteCount++;
}
buffers_.clear();
for (auto &buf : buffersWithAllocs_) {
vmaDestroyBuffer(allocator, buf.buffer, buf.alloc);
deleteCount++;
}
buffersWithAllocs_.clear();
for (auto &bufView : bufferViews_) {
vkDestroyBufferView(device, bufView, nullptr);
deleteCount++;
}
bufferViews_.clear();
for (auto &imageWithAlloc : imagesWithAllocs_) {
vmaDestroyImage(allocator, imageWithAlloc.image, imageWithAlloc.alloc);
deleteCount++;
}
imagesWithAllocs_.clear();
for (auto &imageView : imageViews_) {
vkDestroyImageView(device, imageView, nullptr);
deleteCount++;
}
imageViews_.clear();
for (auto &mem : deviceMemory_) {
vkFreeMemory(device, mem, nullptr);
deleteCount++;
}
deviceMemory_.clear();
for (auto &sampler : samplers_) {
vkDestroySampler(device, sampler, nullptr);
deleteCount++;
}
samplers_.clear();
for (auto &pipeline : pipelines_) {
vkDestroyPipeline(device, pipeline, nullptr);
deleteCount++;
}
pipelines_.clear();
for (auto &pcache : pipelineCaches_) {
vkDestroyPipelineCache(device, pcache, nullptr);
deleteCount++;
}
pipelineCaches_.clear();
for (auto &renderPass : renderPasses_) {
vkDestroyRenderPass(device, renderPass, nullptr);
deleteCount++;
}
renderPasses_.clear();
for (auto &framebuffer : framebuffers_) {
vkDestroyFramebuffer(device, framebuffer, nullptr);
deleteCount++;
}
framebuffers_.clear();
for (auto &pipeLayout : pipelineLayouts_) {
vkDestroyPipelineLayout(device, pipeLayout, nullptr);
deleteCount++;
}
pipelineLayouts_.clear();
for (auto &descSetLayout : descSetLayouts_) {
vkDestroyDescriptorSetLayout(device, descSetLayout, nullptr);
deleteCount++;
}
descSetLayouts_.clear();
for (auto &queryPool : queryPools_) {
vkDestroyQueryPool(device, queryPool, nullptr);
deleteCount++;
}
queryPools_.clear();
deleteCount_ = deleteCount;
}
void VulkanContext::GetImageMemoryRequirements(VkImage image, VkMemoryRequirements *mem_reqs, bool *dedicatedAllocation) {

View file

@ -138,6 +138,10 @@ public:
void Take(VulkanDeleteList &del);
void PerformDeletes(VulkanContext *vulkan, VmaAllocator allocator);
int GetLastDeleteCount() const {
return deleteCount_;
}
private:
std::vector<VkCommandPool> cmdPools_;
std::vector<VkDescriptorPool> descPools_;
@ -157,6 +161,7 @@ private:
std::vector<VkDescriptorSetLayout> descSetLayouts_;
std::vector<VkQueryPool> queryPools_;
std::vector<Callback> callbacks_;
int deleteCount_ = 0;
};
// VulkanContext manages the device and swapchain, and deferred deletion of objects.
@ -392,6 +397,10 @@ public:
return availablePresentModes_;
}
int GetLastDeleteCount() const {
return frame_[curFrame_].deleteList.GetLastDeleteCount();
}
private:
bool ChooseQueue();

View file

@ -21,6 +21,7 @@
#include <mutex>
#include "Common/Log.h"
#include "Common/System/System.h"
#include "Common/GPU/Vulkan/VulkanContext.h"
#include "Common/GPU/Vulkan/VulkanDebug.h"
@ -90,6 +91,19 @@ VKAPI_ATTR VkBool32 VKAPI_CALL VulkanDebugUtilsCallback(
break;
}
/*
// Can be used to temporarily turn errors into info for easier debugging.
switch (messageCode) {
case 1544472022:
if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
messageSeverity = (VkDebugUtilsMessageSeverityFlagBitsEXT)((messageSeverity & ~VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) | VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT);
}
break;
default:
break;
}
*/
int count;
{
std::lock_guard<std::mutex> lock(g_errorCountMutex);
@ -126,7 +140,7 @@ VKAPI_ATTR VkBool32 VKAPI_CALL VulkanDebugUtilsCallback(
#ifdef _WIN32
OutputDebugStringA(msg.c_str());
if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
if (options->breakOnError && IsDebuggerPresent()) {
if (options->breakOnError && System_GetPropertyBool(SYSPROP_DEBUGGER_PRESENT)) {
DebugBreak();
}
if (options->msgBoxOnError) {
@ -134,7 +148,7 @@ VKAPI_ATTR VkBool32 VKAPI_CALL VulkanDebugUtilsCallback(
}
} else if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
// Don't break on perf warnings for now, even with a debugger. We log them at least.
if (options->breakOnWarning && IsDebuggerPresent() && 0 == (messageType & VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT)) {
if (options->breakOnWarning && System_GetPropertyBool(SYSPROP_DEBUGGER_PRESENT) && 0 == (messageType & VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT)) {
DebugBreak();
}
}

View file

@ -0,0 +1,129 @@
#include "Common/GPU/Vulkan/VulkanDescSet.h"
VulkanDescSetPool::~VulkanDescSetPool() {
_assert_msg_(descPool_ == VK_NULL_HANDLE, "VulkanDescSetPool %s never destroyed", tag_);
}
void VulkanDescSetPool::Create(VulkanContext *vulkan, const BindingType *bindingTypes, uint32_t bindingTypesCount, uint32_t descriptorCount) {
_assert_msg_(descPool_ == VK_NULL_HANDLE, "VulkanDescSetPool::Create when already exists");
vulkan_ = vulkan;
info_ = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO };
info_.maxSets = descriptorCount;
_dbg_assert_(sizes_.empty());
uint32_t storageImageCount = 0;
uint32_t storageBufferCount = 0;
uint32_t combinedImageSamplerCount = 0;
uint32_t uniformBufferDynamicCount = 0;
for (uint32_t i = 0; i < bindingTypesCount; i++) {
switch (bindingTypes[i]) {
case BindingType::COMBINED_IMAGE_SAMPLER: combinedImageSamplerCount++; break;
case BindingType::UNIFORM_BUFFER_DYNAMIC_VERTEX:
case BindingType::UNIFORM_BUFFER_DYNAMIC_ALL: uniformBufferDynamicCount++; break;
case BindingType::STORAGE_BUFFER_VERTEX:
case BindingType::STORAGE_BUFFER_COMPUTE: storageBufferCount++; break;
case BindingType::STORAGE_IMAGE_COMPUTE: storageImageCount++; break;
}
}
if (combinedImageSamplerCount) {
sizes_.push_back(VkDescriptorPoolSize{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, combinedImageSamplerCount * descriptorCount });
}
if (uniformBufferDynamicCount) {
sizes_.push_back(VkDescriptorPoolSize{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, uniformBufferDynamicCount * descriptorCount });
}
if (storageBufferCount) {
sizes_.push_back(VkDescriptorPoolSize{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, storageBufferCount * descriptorCount });
}
if (storageImageCount) {
sizes_.push_back(VkDescriptorPoolSize{ VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, storageImageCount * descriptorCount });
}
VkResult res = Recreate(false);
_assert_msg_(res == VK_SUCCESS, "Could not create VulkanDescSetPool %s", tag_);
}
bool VulkanDescSetPool::Allocate(VkDescriptorSet *descriptorSets, int count, const VkDescriptorSetLayout *layouts) {
if (descPool_ == VK_NULL_HANDLE || usage_ + count >= info_.maxSets) {
// Missing or out of space, need to recreate.
VkResult res = Recreate(grow_);
_assert_msg_(res == VK_SUCCESS, "Could not grow VulkanDescSetPool %s on usage %d", tag_, (int)usage_);
}
VkDescriptorSetAllocateInfo descAlloc{ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO };
descAlloc.descriptorPool = descPool_;
descAlloc.descriptorSetCount = count;
descAlloc.pSetLayouts = layouts;
VkResult result = vkAllocateDescriptorSets(vulkan_->GetDevice(), &descAlloc, descriptorSets);
if (result == VK_ERROR_FRAGMENTED_POOL || result < 0) {
WARN_LOG(G3D, "Pool %s %s - recreating", tag_, result == VK_ERROR_FRAGMENTED_POOL ? "fragmented" : "full");
// There seems to have been a spec revision. Here we should apparently recreate the descriptor pool,
// so let's do that. See https://www.khronos.org/registry/vulkan/specs/1.0/man/html/vkAllocateDescriptorSets.html
// Fragmentation shouldn't really happen though since we wipe the pool every frame.
VkResult res = Recreate(false);
_assert_msg_(res == VK_SUCCESS, "Ran out of descriptor space (frag?) and failed to recreate a descriptor pool. sz=%d res=%d", usage_, (int)res);
// Need to update this pointer since we have allocated a new one.
descAlloc.descriptorPool = descPool_;
result = vkAllocateDescriptorSets(vulkan_->GetDevice(), &descAlloc, descriptorSets);
_assert_msg_(result == VK_SUCCESS, "Ran out of descriptor space (frag?) and failed to allocate after recreating a descriptor pool. res=%d", (int)result);
}
if (result != VK_SUCCESS) {
return false;
}
usage_ += count;
return true;
}
void VulkanDescSetPool::Reset() {
_assert_msg_(descPool_ != VK_NULL_HANDLE, "VulkanDescSetPool::Reset without valid pool");
vkResetDescriptorPool(vulkan_->GetDevice(), descPool_, 0);
usage_ = 0;
}
void VulkanDescSetPool::Destroy() {
if (descPool_ != VK_NULL_HANDLE) {
vulkan_->Delete().QueueDeleteDescriptorPool(descPool_);
usage_ = 0;
}
sizes_.clear();
}
void VulkanDescSetPool::DestroyImmediately() {
if (descPool_ != VK_NULL_HANDLE) {
vkDestroyDescriptorPool(vulkan_->GetDevice(), descPool_, nullptr);
descPool_ = VK_NULL_HANDLE;
usage_ = 0;
}
sizes_.clear();
}
VkResult VulkanDescSetPool::Recreate(bool grow) {
_assert_msg_(vulkan_ != nullptr, "VulkanDescSetPool::Recreate without VulkanContext");
uint32_t prevSize = info_.maxSets;
if (grow) {
info_.maxSets *= 2;
for (auto &size : sizes_)
size.descriptorCount *= 2;
}
// Delete the pool if it already exists.
if (descPool_ != VK_NULL_HANDLE) {
INFO_LOG(G3D, "Reallocating %s desc pool from %d to %d", tag_, prevSize, info_.maxSets);
vulkan_->Delete().QueueDeleteDescriptorPool(descPool_);
usage_ = 0;
}
info_.pPoolSizes = &sizes_[0];
info_.poolSizeCount = (uint32_t)sizes_.size();
VkResult result = vkCreateDescriptorPool(vulkan_->GetDevice(), &info_, nullptr, &descPool_);
if (result == VK_SUCCESS) {
vulkan_->SetDebugName(descPool_, VK_OBJECT_TYPE_DESCRIPTOR_POOL, tag_);
}
return result;
}

View file

@ -0,0 +1,48 @@
#pragma once
#include "Common/Data/Collections/FastVec.h"
#include "Common/GPU/Vulkan/VulkanContext.h"
#include <vector>
enum class BindingType {
COMBINED_IMAGE_SAMPLER,
UNIFORM_BUFFER_DYNAMIC_VERTEX,
UNIFORM_BUFFER_DYNAMIC_ALL,
STORAGE_BUFFER_VERTEX,
STORAGE_BUFFER_COMPUTE,
STORAGE_IMAGE_COMPUTE,
};
// Only appropriate for use in a per-frame pool.
class VulkanDescSetPool {
public:
VulkanDescSetPool(const char *tag, bool grow = true) : tag_(tag), grow_(grow) {}
~VulkanDescSetPool();
void Create(VulkanContext *vulkan, const BindingType *bindingTypes, uint32_t bindingTypesCount, uint32_t descriptorCount);
// Allocate a new set, which may resize and empty the current sets.
// Use only for the current frame.
bool Allocate(VkDescriptorSet *descriptorSets, int count, const VkDescriptorSetLayout *layouts);
void Reset();
// This queues up destruction.
void Destroy();
// This actually destroys immediately.
void DestroyImmediately();
bool IsDestroyed() const {
return !descPool_;
}
private:
VkResult Recreate(bool grow);
const char *tag_;
VulkanContext *vulkan_ = nullptr;
VkDescriptorPool descPool_ = VK_NULL_HANDLE;
VkDescriptorPoolCreateInfo info_{};
std::vector<VkDescriptorPoolSize> sizes_;
uint32_t usage_ = 0;
bool grow_;
};

View file

@ -90,6 +90,10 @@ void FrameData::AcquireNextImage(VulkanContext *vulkan, FrameDataShared &shared)
WARN_LOG(G3D, "%s returned from AcquireNextImage - processing the frame, but not presenting", VulkanResultToString(res));
skipSwap = true;
break;
case VK_ERROR_SURFACE_LOST_KHR:
ERROR_LOG(G3D, "%s returned from AcquireNextImage - ignoring, but this better be during shutdown", VulkanResultToString(res));
skipSwap = true;
break;
default:
// Weird, shouldn't get any other values. Maybe lost device?
_assert_msg_(false, "vkAcquireNextImageKHR failed! result=%s", VulkanResultToString(res));

View file

@ -28,6 +28,7 @@ struct QueueProfileContext {
double cpuStartTime;
double cpuEndTime;
double descWriteTime;
int descriptorsWritten;
};
class VKRFramebuffer;

View file

@ -2,6 +2,27 @@
#include "Common/GPU/Vulkan/VulkanFramebuffer.h"
#include "Common/GPU/Vulkan/VulkanQueueRunner.h"
static const char *rpTypeDebugNames[] = {
"RENDER",
"RENDER_DEPTH",
"MV_RENDER",
"MV_RENDER_DEPTH",
"MS_RENDER",
"MS_RENDER_DEPTH",
"MS_MV_RENDER",
"MS_MV_RENDER_DEPTH",
"BACKBUF",
};
const char *GetRPTypeName(RenderPassType rpType) {
uint32_t index = (uint32_t)rpType;
if (index < ARRAY_SIZE(rpTypeDebugNames)) {
return rpTypeDebugNames[index];
} else {
return "N/A";
}
}
VkSampleCountFlagBits MultiSampleLevelToFlagBits(int count) {
// TODO: Check hardware support here, or elsewhere?
// Some hardware only supports 4x.
@ -387,12 +408,25 @@ VkRenderPass CreateRenderPass(VulkanContext *vulkan, const RPKey &key, RenderPas
}
if (isBackbuffer) {
// We don't specify any explicit transitions for these, so let's use subpass dependencies.
// This makes sure that writes to the depth image are done before we try to write to it again.
// From Sascha's examples.
deps[numDeps].srcSubpass = VK_SUBPASS_EXTERNAL;
deps[numDeps].dstSubpass = 0;
deps[numDeps].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
deps[numDeps].srcStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
deps[numDeps].dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
deps[numDeps].srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
deps[numDeps].dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
deps[numDeps].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
numDeps++;
// Dependencies for the color image.
deps[numDeps].srcSubpass = VK_SUBPASS_EXTERNAL;
deps[numDeps].dstSubpass = 0;
deps[numDeps].srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
deps[numDeps].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
deps[numDeps].srcAccessMask = 0;
deps[numDeps].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT;
deps[numDeps].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
deps[numDeps].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
numDeps++;
}
@ -494,6 +528,10 @@ VkRenderPass CreateRenderPass(VulkanContext *vulkan, const RPKey &key, RenderPas
res = vkCreateRenderPass(vulkan->GetDevice(), &rp, nullptr, &pass);
}
if (pass) {
vulkan->SetDebugName(pass, VK_OBJECT_TYPE_RENDER_PASS, GetRPTypeName(rpType));
}
_assert_(res == VK_SUCCESS);
_assert_(pass != VK_NULL_HANDLE);
return pass;

View file

@ -157,3 +157,5 @@ private:
VkSampleCountFlagBits sampleCounts[(size_t)RenderPassType::TYPE_COUNT];
RPKey key_;
};
const char *GetRPTypeName(RenderPassType rpType);

View file

@ -130,9 +130,12 @@ bool VulkanTexture::CreateDirect(VkCommandBuffer cmd, int w, int h, int depth, i
res = vkCreateImageView(vulkan_->GetDevice(), &view_info, NULL, &view_);
if (res != VK_SUCCESS) {
ERROR_LOG(G3D, "vkCreateImageView failed: %s", VulkanResultToString(res));
// This leaks the image.
ERROR_LOG(G3D, "vkCreateImageView failed: %s. Destroying image.", VulkanResultToString(res));
_assert_(res == VK_ERROR_OUT_OF_HOST_MEMORY || res == VK_ERROR_OUT_OF_DEVICE_MEMORY || res == VK_ERROR_TOO_MANY_OBJECTS);
vmaDestroyImage(vulkan_->Allocator(), image_, allocation_);
view_ = VK_NULL_HANDLE;
image_ = VK_NULL_HANDLE;
allocation_ = VK_NULL_HANDLE;
return false;
}
vulkan_->SetDebugName(view_, VK_OBJECT_TYPE_IMAGE_VIEW, tag_);
@ -141,6 +144,7 @@ bool VulkanTexture::CreateDirect(VkCommandBuffer cmd, int w, int h, int depth, i
if (view_info.viewType == VK_IMAGE_VIEW_TYPE_2D) {
view_info.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
res = vkCreateImageView(vulkan_->GetDevice(), &view_info, NULL, &arrayView_);
// Assume that if the above view creation succeeded, so will this.
_assert_(res == VK_SUCCESS);
vulkan_->SetDebugName(arrayView_, VK_OBJECT_TYPE_IMAGE_VIEW, tag_);
}

View file

@ -314,7 +314,7 @@ static void VulkanFreeLibrary(VulkanLibraryHandle &h) {
}
void VulkanSetAvailable(bool available) {
INFO_LOG(G3D, "Forcing Vulkan availability to true");
INFO_LOG(G3D, "Setting Vulkan availability to true");
g_vulkanAvailabilityChecked = true;
g_vulkanMayBeAvailable = available;
}

View file

@ -35,223 +35,6 @@ using namespace PPSSPP_VK;
// Always keep around push buffers at least this long (seconds).
static const double PUSH_GARBAGE_COLLECTION_DELAY = 10.0;
VulkanPushBuffer::VulkanPushBuffer(VulkanContext *vulkan, const char *name, size_t size, VkBufferUsageFlags usage)
: vulkan_(vulkan), name_(name), size_(size), usage_(usage) {
RegisterGPUMemoryManager(this);
bool res = AddBuffer();
_assert_(res);
}
VulkanPushBuffer::~VulkanPushBuffer() {
UnregisterGPUMemoryManager(this);
_dbg_assert_(!writePtr_);
_assert_(buffers_.empty());
}
bool VulkanPushBuffer::AddBuffer() {
BufInfo info;
VkDevice device = vulkan_->GetDevice();
VkBufferCreateInfo b{ VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
b.size = size_;
b.flags = 0;
b.usage = usage_;
b.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
b.queueFamilyIndexCount = 0;
b.pQueueFamilyIndices = nullptr;
VmaAllocationCreateInfo allocCreateInfo{};
allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
VmaAllocationInfo allocInfo{};
VkResult res = vmaCreateBuffer(vulkan_->Allocator(), &b, &allocCreateInfo, &info.buffer, &info.allocation, &allocInfo);
if (VK_SUCCESS != res) {
_assert_msg_(false, "vkCreateBuffer failed! result=%d", (int)res);
return false;
}
vulkan_->SetDebugName(info.buffer, VK_OBJECT_TYPE_BUFFER, name_);
buffers_.push_back(info);
buf_ = buffers_.size() - 1;
return true;
}
void VulkanPushBuffer::Destroy(VulkanContext *vulkan) {
_dbg_assert_(!writePtr_);
for (BufInfo &info : buffers_) {
vulkan->Delete().QueueDeleteBufferAllocation(info.buffer, info.allocation);
}
buffers_.clear();
}
void VulkanPushBuffer::NextBuffer(size_t minSize) {
// First, unmap the current memory.
Unmap();
buf_++;
if (buf_ >= buffers_.size() || minSize > size_) {
// Before creating the buffer, adjust to the new size_ if necessary.
while (size_ < minSize) {
size_ <<= 1;
}
bool res = AddBuffer();
_assert_(res);
if (!res) {
// Let's try not to crash at least?
buf_ = 0;
}
}
// Now, move to the next buffer and map it.
offset_ = 0;
Map();
}
void VulkanPushBuffer::Defragment(VulkanContext *vulkan) {
if (buffers_.size() <= 1) {
return;
}
// Okay, we have more than one. Destroy them all and start over with a larger one.
size_t newSize = size_ * buffers_.size();
Destroy(vulkan);
size_ = newSize;
bool res = AddBuffer();
_assert_(res);
}
size_t VulkanPushBuffer::GetTotalSize() const {
size_t sum = 0;
if (buffers_.size() > 1)
sum += size_ * (buffers_.size() - 1);
sum += offset_;
return sum;
}
void VulkanPushBuffer::GetDebugString(char *buffer, size_t bufSize) const {
size_t sum = 0;
if (buffers_.size() > 1)
sum += size_ * (buffers_.size() - 1);
sum += offset_;
size_t capacity = size_ * buffers_.size();
snprintf(buffer, bufSize, "Push %s: %s / %s", name_, NiceSizeFormat(sum).c_str(), NiceSizeFormat(capacity).c_str());
}
void VulkanPushBuffer::Map() {
_dbg_assert_(!writePtr_);
VkResult res = vmaMapMemory(vulkan_->Allocator(), buffers_[buf_].allocation, (void **)(&writePtr_));
_dbg_assert_(writePtr_);
_assert_(VK_SUCCESS == res);
}
void VulkanPushBuffer::Unmap() {
_dbg_assert_msg_(writePtr_ != nullptr, "VulkanPushBuffer::Unmap: writePtr_ null here means we have a bug (map/unmap mismatch)");
if (!writePtr_)
return;
vmaUnmapMemory(vulkan_->Allocator(), buffers_[buf_].allocation);
writePtr_ = nullptr;
}
VulkanDescSetPool::~VulkanDescSetPool() {
_assert_msg_(descPool_ == VK_NULL_HANDLE, "VulkanDescSetPool %s never destroyed", tag_);
}
void VulkanDescSetPool::Create(VulkanContext *vulkan, const VkDescriptorPoolCreateInfo &info, const std::vector<VkDescriptorPoolSize> &sizes) {
_assert_msg_(descPool_ == VK_NULL_HANDLE, "VulkanDescSetPool::Create when already exists");
vulkan_ = vulkan;
info_ = info;
sizes_ = sizes;
VkResult res = Recreate(false);
_assert_msg_(res == VK_SUCCESS, "Could not create VulkanDescSetPool %s", tag_);
}
VkDescriptorSet VulkanDescSetPool::Allocate(int n, const VkDescriptorSetLayout *layouts, const char *tag) {
if (descPool_ == VK_NULL_HANDLE || usage_ + n >= info_.maxSets) {
// Missing or out of space, need to recreate.
VkResult res = Recreate(grow_);
_assert_msg_(res == VK_SUCCESS, "Could not grow VulkanDescSetPool %s on usage %d", tag_, (int)usage_);
}
VkDescriptorSet desc;
VkDescriptorSetAllocateInfo descAlloc{ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO };
descAlloc.descriptorPool = descPool_;
descAlloc.descriptorSetCount = n;
descAlloc.pSetLayouts = layouts;
VkResult result = vkAllocateDescriptorSets(vulkan_->GetDevice(), &descAlloc, &desc);
if (result == VK_ERROR_FRAGMENTED_POOL || result < 0) {
// There seems to have been a spec revision. Here we should apparently recreate the descriptor pool,
// so let's do that. See https://www.khronos.org/registry/vulkan/specs/1.0/man/html/vkAllocateDescriptorSets.html
// Fragmentation shouldn't really happen though since we wipe the pool every frame.
VkResult res = Recreate(false);
_assert_msg_(res == VK_SUCCESS, "Ran out of descriptor space (frag?) and failed to recreate a descriptor pool. sz=%d res=%d", usage_, (int)res);
// Need to update this pointer since we have allocated a new one.
descAlloc.descriptorPool = descPool_;
result = vkAllocateDescriptorSets(vulkan_->GetDevice(), &descAlloc, &desc);
_assert_msg_(result == VK_SUCCESS, "Ran out of descriptor space (frag?) and failed to allocate after recreating a descriptor pool. res=%d", (int)result);
}
if (result != VK_SUCCESS) {
return VK_NULL_HANDLE;
}
vulkan_->SetDebugName(desc, VK_OBJECT_TYPE_DESCRIPTOR_SET, tag);
return desc;
}
void VulkanDescSetPool::Reset() {
_assert_msg_(descPool_ != VK_NULL_HANDLE, "VulkanDescSetPool::Reset without valid pool");
vkResetDescriptorPool(vulkan_->GetDevice(), descPool_, 0);
clear_();
usage_ = 0;
}
void VulkanDescSetPool::Destroy() {
_assert_msg_(vulkan_ != nullptr, "VulkanDescSetPool::Destroy without VulkanContext");
if (descPool_ != VK_NULL_HANDLE) {
vulkan_->Delete().QueueDeleteDescriptorPool(descPool_);
clear_();
usage_ = 0;
}
}
VkResult VulkanDescSetPool::Recreate(bool grow) {
_assert_msg_(vulkan_ != nullptr, "VulkanDescSetPool::Recreate without VulkanContext");
uint32_t prevSize = info_.maxSets;
if (grow) {
info_.maxSets *= 2;
for (auto &size : sizes_)
size.descriptorCount *= 2;
}
// Delete the pool if it already exists.
if (descPool_ != VK_NULL_HANDLE) {
DEBUG_LOG(G3D, "Reallocating %s desc pool from %d to %d", tag_, prevSize, info_.maxSets);
vulkan_->Delete().QueueDeleteDescriptorPool(descPool_);
clear_();
usage_ = 0;
}
info_.pPoolSizes = &sizes_[0];
info_.poolSizeCount = (uint32_t)sizes_.size();
VkResult result = vkCreateDescriptorPool(vulkan_->GetDevice(), &info_, nullptr, &descPool_);
if (result == VK_SUCCESS) {
vulkan_->SetDebugName(descPool_, VK_OBJECT_TYPE_DESCRIPTOR_POOL, tag_);
}
return result;
}
VulkanPushPool::VulkanPushPool(VulkanContext *vulkan, const char *name, size_t originalBlockSize, VkBufferUsageFlags usage)
: vulkan_(vulkan), name_(name), originalBlockSize_(originalBlockSize), usage_(usage) {
RegisterGPUMemoryManager(this);
@ -291,7 +74,7 @@ VulkanPushPool::Block VulkanPushPool::CreateBlock(size_t size) {
_assert_(result == VK_SUCCESS);
result = vmaMapMemory(vulkan_->Allocator(), block.allocation, (void **)(&block.writePtr));
_assert_msg_(result == VK_SUCCESS, "VulkanPushPool: Failed to map memory (result = %08x)", result);
_assert_msg_(result == VK_SUCCESS, "VulkanPushPool: Failed to map memory (result = %s)", VulkanResultToString(result));
_assert_msg_(block.writePtr != nullptr, "VulkanPushPool: Failed to map memory on block of size %d", (int)block.size);
return block;

View file

@ -16,88 +16,6 @@ VK_DEFINE_HANDLE(VmaAllocation);
//
// Vulkan memory management utils.
// VulkanPushBuffer
// Simple incrementing allocator.
// Use these to push vertex, index and uniform data. Generally you'll have two or three of these
// and alternate on each frame. Make sure not to reset until the fence from the last time you used it
// has completed.
// NOTE: This has now been replaced with VulkanPushPool for all uses except the vertex cache.
class VulkanPushBuffer : public GPUMemoryManager {
struct BufInfo {
VkBuffer buffer;
VmaAllocation allocation;
};
public:
// NOTE: If you create a push buffer with PushBufferType::GPU_ONLY,
// then you can't use any of the push functions as pointers will not be reachable from the CPU.
// You must in this case use Allocate() only, and pass the returned offset and the VkBuffer to Vulkan APIs.
VulkanPushBuffer(VulkanContext *vulkan, const char *name, size_t size, VkBufferUsageFlags usage);
~VulkanPushBuffer();
void Destroy(VulkanContext *vulkan);
void Reset() { offset_ = 0; }
void GetDebugString(char *buffer, size_t bufSize) const override;
const char *Name() const override {
return name_;
}
// Needs context in case of defragment.
void Begin(VulkanContext *vulkan) {
buf_ = 0;
offset_ = 0;
// Note: we must defrag because some buffers may be smaller than size_.
Defragment(vulkan);
Map();
}
void BeginNoReset() { Map(); }
void End() { Unmap(); }
void Map();
void Unmap();
// When using the returned memory, make sure to bind the returned vkbuf.
uint8_t *Allocate(VkDeviceSize numBytes, VkDeviceSize alignment, VkBuffer *vkbuf, uint32_t *bindOffset) {
size_t offset = (offset_ + alignment - 1) & ~(alignment - 1);
if (offset + numBytes > size_) {
NextBuffer(numBytes);
offset = offset_;
}
offset_ = offset + numBytes;
*bindOffset = (uint32_t)offset;
*vkbuf = buffers_[buf_].buffer;
return writePtr_ + offset;
}
VkDeviceSize Push(const void *data, VkDeviceSize numBytes, int alignment, VkBuffer *vkbuf) {
uint32_t bindOffset;
uint8_t *ptr = Allocate(numBytes, alignment, vkbuf, &bindOffset);
memcpy(ptr, data, numBytes);
return bindOffset;
}
size_t GetOffset() const { return offset_; }
size_t GetTotalSize() const;
private:
bool AddBuffer();
void NextBuffer(size_t minSize);
void Defragment(VulkanContext *vulkan);
VulkanContext *vulkan_;
std::vector<BufInfo> buffers_;
size_t buf_ = 0;
size_t offset_ = 0;
size_t size_ = 0;
uint8_t *writePtr_ = nullptr;
VkBufferUsageFlags usage_;
const char *name_;
};
// Simple memory pushbuffer pool that can share blocks between the "frames", to reduce the impact of push memory spikes -
// a later frame can gobble up redundant buffers from an earlier frame even if they don't share frame index.
// NOT thread safe! Can only be used from one thread (our main thread).
@ -176,33 +94,3 @@ private:
int curBlockIndex_ = -1;
const char *name_;
};
// Only appropriate for use in a per-frame pool.
class VulkanDescSetPool {
public:
VulkanDescSetPool(const char *tag, bool grow) : tag_(tag), grow_(grow) {}
~VulkanDescSetPool();
// Must call this before use: defines how to clear cache of ANY returned values from Allocate().
void Setup(const std::function<void()> &clear) {
clear_ = clear;
}
void Create(VulkanContext *vulkan, const VkDescriptorPoolCreateInfo &info, const std::vector<VkDescriptorPoolSize> &sizes);
// Allocate a new set, which may resize and empty the current sets.
// Use only for the current frame, unless in a cache cleared by clear_.
VkDescriptorSet Allocate(int n, const VkDescriptorSetLayout *layouts, const char *tag);
void Reset();
void Destroy();
private:
VkResult Recreate(bool grow);
const char *tag_;
VulkanContext *vulkan_ = nullptr;
VkDescriptorPool descPool_ = VK_NULL_HANDLE;
VkDescriptorPoolCreateInfo info_{};
std::vector<VkDescriptorPoolSize> sizes_;
std::function<void()> clear_;
uint32_t usage_ = 0;
bool grow_;
};

View file

@ -129,7 +129,6 @@ bool VulkanQueueRunner::CreateSwapchain(VkCommandBuffer cmdInit) {
return true;
}
bool VulkanQueueRunner::InitBackbufferFramebuffers(int width, int height) {
VkResult res;
// We share the same depth buffer but have multiple color buffers, see the loop below.
@ -173,7 +172,7 @@ bool VulkanQueueRunner::InitDepthStencilBuffer(VkCommandBuffer cmd) {
image_info.queueFamilyIndexCount = 0;
image_info.pQueueFamilyIndices = nullptr;
image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
image_info.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
image_info.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT;
image_info.flags = 0;
depth_.format = depth_format;
@ -338,7 +337,7 @@ void VulkanQueueRunner::PreprocessSteps(std::vector<VKRStep *> &steps) {
}
}
void VulkanQueueRunner::RunSteps(std::vector<VKRStep *> &steps, FrameData &frameData, FrameDataShared &frameDataShared, bool keepSteps) {
void VulkanQueueRunner::RunSteps(std::vector<VKRStep *> &steps, int curFrame, FrameData &frameData, FrameDataShared &frameDataShared, bool keepSteps) {
QueueProfileContext *profile = frameData.profile.enabled ? &frameData.profile : nullptr;
if (profile)
@ -394,7 +393,7 @@ void VulkanQueueRunner::RunSteps(std::vector<VKRStep *> &steps, FrameData &frame
vkCmdBeginDebugUtilsLabelEXT(cmd, &labelInfo);
}
}
PerformRenderPass(step, cmd);
PerformRenderPass(step, cmd, curFrame);
break;
case VKRStepType::COPY:
PerformCopy(step, cmd);
@ -414,7 +413,7 @@ void VulkanQueueRunner::RunSteps(std::vector<VKRStep *> &steps, FrameData &frame
if (profile && profile->timestampsEnabled && profile->timestampDescriptions.size() + 1 < MAX_TIMESTAMP_QUERIES) {
vkCmdWriteTimestamp(cmd, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, profile->queryPool, (uint32_t)profile->timestampDescriptions.size());
profile->timestampDescriptions.push_back(StepToString(step));
profile->timestampDescriptions.push_back(StepToString(vulkan_, step));
}
if (emitLabels) {
@ -674,36 +673,16 @@ const char *AspectToString(VkImageAspectFlags aspect) {
}
}
static const char *rpTypeDebugNames[] = {
"RENDER",
"RENDER_DEPTH",
"RENDER_INPUT",
"RENDER_DEPTH_INPUT",
"MV_RENDER",
"MV_RENDER_DEPTH",
"MV_RENDER_INPUT",
"MV_RENDER_DEPTH_INPUT",
"MS_RENDER",
"MS_RENDER_DEPTH",
"MS_RENDER_INPUT",
"MS_RENDER_DEPTH_INPUT",
"MS_MV_RENDER",
"MS_MV_RENDER_DEPTH",
"MS_MV_RENDER_INPUT",
"MS_MV_RENDER_DEPTH_INPUT",
"BACKBUF",
};
std::string VulkanQueueRunner::StepToString(const VKRStep &step) const {
std::string VulkanQueueRunner::StepToString(VulkanContext *vulkan, const VKRStep &step) {
char buffer[256];
switch (step.stepType) {
case VKRStepType::RENDER:
{
int w = step.render.framebuffer ? step.render.framebuffer->width : vulkan_->GetBackbufferWidth();
int h = step.render.framebuffer ? step.render.framebuffer->height : vulkan_->GetBackbufferHeight();
int w = step.render.framebuffer ? step.render.framebuffer->width : vulkan->GetBackbufferWidth();
int h = step.render.framebuffer ? step.render.framebuffer->height : vulkan->GetBackbufferHeight();
int actual_w = step.render.renderArea.extent.width;
int actual_h = step.render.renderArea.extent.height;
const char *renderCmd = rpTypeDebugNames[(size_t)step.render.renderPassType];
const char *renderCmd = GetRPTypeName(step.render.renderPassType);
snprintf(buffer, sizeof(buffer), "%s %s %s (draws: %d, %dx%d/%dx%d)", renderCmd, step.tag, step.render.framebuffer ? step.render.framebuffer->Tag() : "", step.render.numDraws, actual_w, actual_h, w, h);
break;
}
@ -896,9 +875,6 @@ void VulkanQueueRunner::LogRenderPass(const VKRStep &pass, bool verbose) {
case VKRRenderCommand::BIND_GRAPHICS_PIPELINE:
INFO_LOG(G3D, " BindGraphicsPipeline(%x)", (int)(intptr_t)cmd.graphics_pipeline.pipeline);
break;
case VKRRenderCommand::BIND_COMPUTE_PIPELINE:
INFO_LOG(G3D, " BindComputePipeline(%x)", (int)(intptr_t)cmd.compute_pipeline.pipeline);
break;
case VKRRenderCommand::BLEND:
INFO_LOG(G3D, " BlendColor(%08x)", cmd.blendColor.color);
break;
@ -938,19 +914,19 @@ void VulkanQueueRunner::LogRenderPass(const VKRStep &pass, bool verbose) {
}
void VulkanQueueRunner::LogCopy(const VKRStep &step) {
INFO_LOG(G3D, "%s", StepToString(step).c_str());
INFO_LOG(G3D, "%s", StepToString(vulkan_, step).c_str());
}
void VulkanQueueRunner::LogBlit(const VKRStep &step) {
INFO_LOG(G3D, "%s", StepToString(step).c_str());
INFO_LOG(G3D, "%s", StepToString(vulkan_, step).c_str());
}
void VulkanQueueRunner::LogReadback(const VKRStep &step) {
INFO_LOG(G3D, "%s", StepToString(step).c_str());
INFO_LOG(G3D, "%s", StepToString(vulkan_, step).c_str());
}
void VulkanQueueRunner::LogReadbackImage(const VKRStep &step) {
INFO_LOG(G3D, "%s", StepToString(step).c_str());
INFO_LOG(G3D, "%s", StepToString(vulkan_, step).c_str());
}
void TransitionToOptimal(VkCommandBuffer cmd, VkImage colorImage, VkImageLayout colorLayout, VkImage depthStencilImage, VkImageLayout depthStencilLayout, int numLayers, VulkanBarrier *recordBarrier) {
@ -1123,7 +1099,7 @@ void TransitionFromOptimal(VkCommandBuffer cmd, VkImage colorImage, VkImageLayou
}
}
void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer cmd) {
void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer cmd, int curFrame) {
for (size_t i = 0; i < step.preTransitions.size(); i++) {
const TransitionRequest &iter = step.preTransitions[i];
if (iter.aspect == VK_IMAGE_ASPECT_COLOR_BIT && iter.fb->color.layout != iter.targetLayout) {
@ -1219,6 +1195,7 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
// The stencil ones are very commonly mostly redundant so let's eliminate them where possible.
// Might also want to consider scissor and viewport.
VkPipeline lastPipeline = VK_NULL_HANDLE;
FastVec<PendingDescSet> *descSets = nullptr;
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
bool pipelineOK = false;
@ -1261,7 +1238,9 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
if (pipeline != VK_NULL_HANDLE) {
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
pipelineLayout = c.pipeline.pipelineLayout;
descSets = &c.graphics_pipeline.pipelineLayout->frameData[curFrame].descSets_;
pipelineLayout = c.graphics_pipeline.pipelineLayout->pipelineLayout;
_dbg_assert_(pipelineLayout != VK_NULL_HANDLE);
lastGraphicsPipeline = graphicsPipeline;
pipelineOK = true;
} else {
@ -1276,20 +1255,6 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
break;
}
case VKRRenderCommand::BIND_COMPUTE_PIPELINE:
{
VKRComputePipeline *computePipeline = c.compute_pipeline.pipeline;
if (computePipeline != lastComputePipeline) {
VkPipeline pipeline = computePipeline->pipeline->BlockUntilReady();
if (pipeline != VK_NULL_HANDLE) {
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline);
pipelineLayout = c.pipeline.pipelineLayout;
lastComputePipeline = computePipeline;
}
}
break;
}
case VKRRenderCommand::VIEWPORT:
if (fb != nullptr) {
vkCmdSetViewport(cmd, 0, 1, &c.viewport.vp);
@ -1356,7 +1321,9 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
case VKRRenderCommand::DRAW_INDEXED:
if (pipelineOK) {
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &c.drawIndexed.ds, c.drawIndexed.numUboOffsets, c.drawIndexed.uboOffsets);
VkDescriptorSet set = (*descSets)[c.drawIndexed.descSetIndex].set;
_dbg_assert_(set != VK_NULL_HANDLE);
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &set, c.drawIndexed.numUboOffsets, c.drawIndexed.uboOffsets);
vkCmdBindIndexBuffer(cmd, c.drawIndexed.ibuffer, c.drawIndexed.ioffset, VK_INDEX_TYPE_UINT16);
VkDeviceSize voffset = c.drawIndexed.voffset;
vkCmdBindVertexBuffers(cmd, 0, 1, &c.drawIndexed.vbuffer, &voffset);
@ -1366,7 +1333,9 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
case VKRRenderCommand::DRAW:
if (pipelineOK) {
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &c.draw.ds, c.draw.numUboOffsets, c.draw.uboOffsets);
VkDescriptorSet set = (*descSets)[c.drawIndexed.descSetIndex].set;
_dbg_assert_(set != VK_NULL_HANDLE);
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &set, c.draw.numUboOffsets, c.draw.uboOffsets);
if (c.draw.vbuffer) {
vkCmdBindVertexBuffers(cmd, 0, 1, &c.draw.vbuffer, &c.draw.voffset);
}

View file

@ -19,6 +19,7 @@ class VKRFramebuffer;
struct VKRGraphicsPipeline;
struct VKRComputePipeline;
struct VKRImage;
struct VKRPipelineLayout;
struct FrameData;
enum {
@ -30,7 +31,6 @@ enum {
enum class VKRRenderCommand : uint8_t {
REMOVED,
BIND_GRAPHICS_PIPELINE, // async
BIND_COMPUTE_PIPELINE, // async
STENCIL,
BLEND,
VIEWPORT,
@ -56,20 +56,12 @@ ENUM_CLASS_BITOPS(PipelineFlags);
struct VkRenderData {
VKRRenderCommand cmd;
union {
struct {
VkPipeline pipeline;
VkPipelineLayout pipelineLayout;
} pipeline;
struct {
VKRGraphicsPipeline *pipeline;
VkPipelineLayout pipelineLayout;
VKRPipelineLayout *pipelineLayout;
} graphics_pipeline;
struct {
VKRComputePipeline *pipeline;
VkPipelineLayout pipelineLayout;
} compute_pipeline;
struct {
VkDescriptorSet ds;
uint32_t descSetIndex;
int numUboOffsets;
uint32_t uboOffsets[3];
VkBuffer vbuffer;
@ -78,7 +70,7 @@ struct VkRenderData {
uint32_t offset;
} draw;
struct {
VkDescriptorSet ds;
uint32_t descSetIndex;
uint32_t uboOffsets[3];
uint16_t numUboOffsets;
uint16_t instances;
@ -118,9 +110,7 @@ struct VkRenderData {
const char *annotation;
} debugAnnotation;
struct {
int setNumber;
VkDescriptorSet set;
VkPipelineLayout pipelineLayout;
int setIndex;
} bindDescSet;
};
};
@ -230,10 +220,10 @@ public:
}
void PreprocessSteps(std::vector<VKRStep *> &steps);
void RunSteps(std::vector<VKRStep *> &steps, FrameData &frameData, FrameDataShared &frameDataShared, bool keepSteps = false);
void RunSteps(std::vector<VKRStep *> &steps, int curFrame, FrameData &frameData, FrameDataShared &frameDataShared, bool keepSteps = false);
void LogSteps(const std::vector<VKRStep *> &steps, bool verbose);
std::string StepToString(const VKRStep &step) const;
static std::string StepToString(VulkanContext *vulkan, const VKRStep &step);
void CreateDeviceObjects();
void DestroyDeviceObjects();
@ -290,7 +280,7 @@ private:
bool InitDepthStencilBuffer(VkCommandBuffer cmd); // Used for non-buffered rendering.
VKRRenderPass *PerformBindFramebufferAsRenderTarget(const VKRStep &pass, VkCommandBuffer cmd);
void PerformRenderPass(const VKRStep &pass, VkCommandBuffer cmd);
void PerformRenderPass(const VKRStep &pass, VkCommandBuffer cmd, int curFrame);
void PerformCopy(const VKRStep &pass, VkCommandBuffer cmd);
void PerformBlit(const VKRStep &pass, VkCommandBuffer cmd);
void PerformReadback(const VKRStep &pass, VkCommandBuffer cmd, FrameData &frameData);

View file

@ -12,6 +12,7 @@
#include "Common/GPU/Vulkan/VulkanContext.h"
#include "Common/GPU/Vulkan/VulkanRenderManager.h"
#include "Common/LogReporting.h"
#include "Common/Thread/ThreadUtil.h"
#include "Common/VR/PPSSPPVR.h"
@ -29,6 +30,10 @@ using namespace PPSSPP_VK;
// renderPass is an example of the "compatibility class" or RenderPassType type.
bool VKRGraphicsPipeline::Create(VulkanContext *vulkan, VkRenderPass compatibleRenderPass, RenderPassType rpType, VkSampleCountFlagBits sampleCount, double scheduleTime, int countToCompile) {
// Good torture test to test the shutdown-while-precompiling-shaders issue on PC where it's normally
// hard to catch because shaders compile so fast.
// sleep_ms(200);
bool multisample = RenderPassTypeHasMultisample(rpType);
if (multisample) {
if (sampleCount_ != VK_SAMPLE_COUNT_FLAG_BITS_MAX_ENUM) {
@ -111,7 +116,7 @@ bool VKRGraphicsPipeline::Create(VulkanContext *vulkan, VkRenderPass compatibleR
pipe.pDynamicState = &desc->ds;
pipe.pInputAssemblyState = &inputAssembly;
pipe.pMultisampleState = &ms;
pipe.layout = desc->pipelineLayout;
pipe.layout = desc->pipelineLayout->pipelineLayout;
pipe.basePipelineHandle = VK_NULL_HANDLE;
pipe.basePipelineIndex = 0;
pipe.subpass = 0;
@ -187,7 +192,7 @@ void VKRGraphicsPipeline::DestroyVariantsInstant(VkDevice device) {
VKRGraphicsPipeline::~VKRGraphicsPipeline() {
// This is called from the callbacked queued in QueueForDeletion.
// Here we are free to directly delete stuff, don't need to queue.
// When we reach here, we should already be empty, so let's assert on that.
for (size_t i = 0; i < (size_t)RenderPassType::TYPE_COUNT; i++) {
_assert_(!pipeline[i]);
}
@ -195,6 +200,14 @@ VKRGraphicsPipeline::~VKRGraphicsPipeline() {
desc->Release();
}
void VKRGraphicsPipeline::BlockUntilCompiled() {
for (size_t i = 0; i < (size_t)RenderPassType::TYPE_COUNT; i++) {
if (pipeline[i]) {
pipeline[i]->BlockUntilReady();
}
}
}
void VKRGraphicsPipeline::QueueForDeletion(VulkanContext *vulkan) {
// Can't destroy variants here, the pipeline still lives for a while.
vulkan->Delete().QueueCallback([](VulkanContext *vulkan, void *p) {
@ -252,6 +265,7 @@ VulkanRenderManager::VulkanRenderManager(VulkanContext *vulkan, bool useThread,
initTimeMs_("initTimeMs"),
totalGPUTimeMs_("totalGPUTimeMs"),
renderCPUTimeMs_("renderCPUTimeMs"),
descUpdateTimeMs_("descUpdateCPUTimeMs"),
useRenderThread_(useThread),
frameTimeHistory_(frameTimeHistory)
{
@ -275,7 +289,6 @@ bool VulkanRenderManager::CreateBackbuffers() {
return false;
}
VkCommandBuffer cmdInit = GetInitCmd();
if (!queueRunner_.CreateSwapchain(cmdInit)) {
@ -297,6 +310,11 @@ bool VulkanRenderManager::CreateBackbuffers() {
outOfDateFrames_ = 0;
for (int i = 0; i < vulkan_->GetInflightFrames(); i++) {
auto &frameData = frameData_[i];
frameData.readyForFence = true; // Just in case.
}
// Start the thread(s).
if (HasBackbuffers()) {
run_ = true; // For controlling the compiler thread's exit
@ -395,6 +413,8 @@ VulkanRenderManager::~VulkanRenderManager() {
vulkan_->WaitUntilQueueIdle();
_dbg_assert_(pipelineLayouts_.empty());
VkDevice device = vulkan_->GetDevice();
frameDataShared_.Destroy(vulkan_);
for (int i = 0; i < inflightFramesAtStart_; i++) {
@ -495,7 +515,7 @@ void VulkanRenderManager::CompileThreadFunc() {
Task *task = new CreateMultiPipelinesTask(vulkan_, entries);
g_threadManager.EnqueueTask(task);
}
queueRunner_.NotifyCompileDone();
}
}
@ -628,6 +648,8 @@ void VulkanRenderManager::BeginFrame(bool enableProfiling, bool enableLogProfile
PollPresentTiming();
ResetDescriptorLists(curFrame);
int validBits = vulkan_->GetQueueFamilyProperties(vulkan_->GetGraphicsQueueFamilyIndex()).timestampValidBits;
FrameTimeData &frameTimeData = frameTimeHistory_.Add(frameId);
@ -662,6 +684,13 @@ void VulkanRenderManager::BeginFrame(bool enableProfiling, bool enableLogProfile
renderCPUTimeMs_.Update((frameData.profile.cpuEndTime - frameData.profile.cpuStartTime) * 1000.0);
renderCPUTimeMs_.Format(line, sizeof(line));
str << line;
descUpdateTimeMs_.Update(frameData.profile.descWriteTime * 1000.0);
descUpdateTimeMs_.Format(line, sizeof(line));
str << line;
snprintf(line, sizeof(line), "Descriptors written: %d\n", frameData.profile.descriptorsWritten);
str << line;
snprintf(line, sizeof(line), "Resource deletions: %d\n", vulkan_->GetLastDeleteCount());
str << line;
for (int i = 0; i < numQueries - 1; i++) {
uint64_t diff = (queryResults[i + 1] - queryResults[i]) & timestampDiffMask;
double milliseconds = (double)diff * timestampConversionFactor;
@ -687,10 +716,17 @@ void VulkanRenderManager::BeginFrame(bool enableProfiling, bool enableLogProfile
renderCPUTimeMs_.Update((frameData.profile.cpuEndTime - frameData.profile.cpuStartTime) * 1000.0);
renderCPUTimeMs_.Format(line, sizeof(line));
str << line;
descUpdateTimeMs_.Update(frameData.profile.descWriteTime * 1000.0);
descUpdateTimeMs_.Format(line, sizeof(line));
str << line;
snprintf(line, sizeof(line), "Descriptors written: %d\n", frameData.profile.descriptorsWritten);
str << line;
frameData.profile.profileSummary = str.str();
}
}
frameData.profile.descriptorsWritten = 0;
// Must be after the fence - this performs deletes.
VLOG("PUSH: BeginFrame %d", curFrame);
@ -714,6 +750,21 @@ VkCommandBuffer VulkanRenderManager::GetInitCmd() {
return frameData_[curFrame].GetInitCmd(vulkan_);
}
void VulkanRenderManager::ReportBadStateForDraw() {
const char *cause1 = "";
char cause2[256];
cause2[0] = '\0';
if (!curRenderStep_) {
cause1 = "No current render step";
}
if (curRenderStep_ && curRenderStep_->stepType != VKRStepType::RENDER) {
cause1 = "Not a render step: ";
std::string str = VulkanQueueRunner::StepToString(vulkan_, *curRenderStep_);
truncate_cpy(cause2, str.c_str());
}
ERROR_LOG_REPORT_ONCE(baddraw, G3D, "Can't draw: %s%s. Step count: %d", cause1, cause2, (int)steps_.size());
}
VKRGraphicsPipeline *VulkanRenderManager::CreateGraphicsPipeline(VKRGraphicsPipelineDesc *desc, PipelineFlags pipelineFlags, uint32_t variantBitmask, VkSampleCountFlagBits sampleCount, bool cacheLoad, const char *tag) {
if (!desc->vertexShader || !desc->fragmentShader) {
ERROR_LOG(G3D, "Can't create graphics pipeline with missing vs/ps: %p %p", desc->vertexShader, desc->fragmentShader);
@ -1430,6 +1481,11 @@ void VulkanRenderManager::Run(VKRRenderThreadTask &task) {
}
frameData.SubmitPending(vulkan_, FrameSubmitType::Pending, frameDataShared_);
// Flush descriptors.
double descStart = time_now_d();
FlushDescriptors(task.frame);
frameData.profile.descWriteTime = time_now_d() - descStart;
if (!frameData.hasMainCommands) {
// Effectively resets both main and present command buffers, since they both live in this pool.
// We always record main commands first, so we don't need to reset the present command buffer separately.
@ -1451,11 +1507,11 @@ void VulkanRenderManager::Run(VKRRenderThreadTask &task) {
int passes = GetVRPassesCount();
for (int i = 0; i < passes; i++) {
PreVRFrameRender(i);
queueRunner_.RunSteps(task.steps, frameData, frameDataShared_, i < passes - 1);
queueRunner_.RunSteps(task.steps, task.frame, frameData, frameDataShared_, i < passes - 1);
PostVRFrameRender();
}
} else {
queueRunner_.RunSteps(task.steps, frameData, frameDataShared_);
queueRunner_.RunSteps(task.steps, task.frame, frameData, frameDataShared_);
}
switch (task.runType) {
@ -1486,6 +1542,8 @@ void VulkanRenderManager::Run(VKRRenderThreadTask &task) {
// Called from main thread.
void VulkanRenderManager::FlushSync() {
_dbg_assert_(!curRenderStep_);
if (invalidationCallback_) {
invalidationCallback_(InvalidationCallbackFlags::COMMAND_BUFFER_STATE);
}
@ -1529,3 +1587,238 @@ void VulkanRenderManager::ResetStats() {
totalGPUTimeMs_.Reset();
renderCPUTimeMs_.Reset();
}
VKRPipelineLayout *VulkanRenderManager::CreatePipelineLayout(BindingType *bindingTypes, size_t bindingTypesCount, bool geoShadersEnabled, const char *tag) {
VKRPipelineLayout *layout = new VKRPipelineLayout();
layout->tag = tag;
layout->bindingTypesCount = (uint32_t)bindingTypesCount;
_dbg_assert_(bindingTypesCount <= ARRAY_SIZE(layout->bindingTypes));
memcpy(layout->bindingTypes, bindingTypes, sizeof(BindingType) * bindingTypesCount);
VkDescriptorSetLayoutBinding bindings[VKRPipelineLayout::MAX_DESC_SET_BINDINGS];
for (int i = 0; i < bindingTypesCount; i++) {
bindings[i].binding = i;
bindings[i].descriptorCount = 1;
bindings[i].pImmutableSamplers = nullptr;
switch (bindingTypes[i]) {
case BindingType::COMBINED_IMAGE_SAMPLER:
bindings[i].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
bindings[i].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
break;
case BindingType::UNIFORM_BUFFER_DYNAMIC_VERTEX:
bindings[i].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
bindings[i].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
break;
case BindingType::UNIFORM_BUFFER_DYNAMIC_ALL:
bindings[i].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
bindings[i].stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
if (geoShadersEnabled) {
bindings[i].stageFlags |= VK_SHADER_STAGE_GEOMETRY_BIT;
}
break;
case BindingType::STORAGE_BUFFER_VERTEX:
bindings[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
bindings[i].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
break;
case BindingType::STORAGE_BUFFER_COMPUTE:
bindings[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
bindings[i].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
break;
case BindingType::STORAGE_IMAGE_COMPUTE:
bindings[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
bindings[i].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
break;
default:
_dbg_assert_(false);
break;
}
}
VkDescriptorSetLayoutCreateInfo dsl = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO };
dsl.bindingCount = (uint32_t)bindingTypesCount;
dsl.pBindings = bindings;
VkResult res = vkCreateDescriptorSetLayout(vulkan_->GetDevice(), &dsl, nullptr, &layout->descriptorSetLayout);
_assert_(VK_SUCCESS == res && layout->descriptorSetLayout);
VkPipelineLayoutCreateInfo pl = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
VkDescriptorSetLayout setLayouts[1] = { layout->descriptorSetLayout };
pl.setLayoutCount = ARRAY_SIZE(setLayouts);
pl.pSetLayouts = setLayouts;
res = vkCreatePipelineLayout(vulkan_->GetDevice(), &pl, nullptr, &layout->pipelineLayout);
_assert_(VK_SUCCESS == res && layout->pipelineLayout);
vulkan_->SetDebugName(layout->descriptorSetLayout, VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT, tag);
vulkan_->SetDebugName(layout->pipelineLayout, VK_OBJECT_TYPE_PIPELINE_LAYOUT, tag);
for (int i = 0; i < VulkanContext::MAX_INFLIGHT_FRAMES; i++) {
// Some games go beyond 1024 and end up having to resize like GTA, but most stay below so we start there.
layout->frameData[i].pool.Create(vulkan_, bindingTypes, (uint32_t)bindingTypesCount, 1024);
}
pipelineLayouts_.push_back(layout);
return layout;
}
void VulkanRenderManager::DestroyPipelineLayout(VKRPipelineLayout *layout) {
for (auto iter = pipelineLayouts_.begin(); iter != pipelineLayouts_.end(); iter++) {
if (*iter == layout) {
pipelineLayouts_.erase(iter);
break;
}
}
vulkan_->Delete().QueueCallback([](VulkanContext *vulkan, void *userdata) {
VKRPipelineLayout *layout = (VKRPipelineLayout *)userdata;
for (int i = 0; i < VulkanContext::MAX_INFLIGHT_FRAMES; i++) {
layout->frameData[i].pool.DestroyImmediately();
}
vkDestroyPipelineLayout(vulkan->GetDevice(), layout->pipelineLayout, nullptr);
vkDestroyDescriptorSetLayout(vulkan->GetDevice(), layout->descriptorSetLayout, nullptr);
delete layout;
}, layout);
}
void VulkanRenderManager::FlushDescriptors(int frame) {
for (auto iter : pipelineLayouts_) {
iter->FlushDescSets(vulkan_, frame, &frameData_[frame].profile);
}
}
void VulkanRenderManager::ResetDescriptorLists(int frame) {
for (auto iter : pipelineLayouts_) {
VKRPipelineLayout::FrameData &data = iter->frameData[frame];
data.flushedDescriptors_ = 0;
data.descSets_.clear();
data.descData_.clear();
}
}
VKRPipelineLayout::~VKRPipelineLayout() {
_assert_(frameData[0].pool.IsDestroyed());
}
void VKRPipelineLayout::FlushDescSets(VulkanContext *vulkan, int frame, QueueProfileContext *profile) {
_dbg_assert_(frame < VulkanContext::MAX_INFLIGHT_FRAMES);
FrameData &data = frameData[frame];
VulkanDescSetPool &pool = data.pool;
FastVec<PackedDescriptor> &descData = data.descData_;
FastVec<PendingDescSet> &descSets = data.descSets_;
pool.Reset();
VkDescriptorSet setCache[8];
VkDescriptorSetLayout layoutsForAlloc[ARRAY_SIZE(setCache)];
for (int i = 0; i < ARRAY_SIZE(setCache); i++) {
layoutsForAlloc[i] = descriptorSetLayout;
}
int setsUsed = ARRAY_SIZE(setCache); // To allocate immediately.
// This will write all descriptors.
// Initially, we just do a simple look-back comparing to the previous descriptor to avoid sequential dupes.
// Initially, let's do naive single desc set writes.
VkWriteDescriptorSet writes[MAX_DESC_SET_BINDINGS];
VkDescriptorImageInfo imageInfo[MAX_DESC_SET_BINDINGS]; // just picked a practical number
VkDescriptorBufferInfo bufferInfo[MAX_DESC_SET_BINDINGS];
size_t start = data.flushedDescriptors_;
int writeCount = 0;
for (size_t index = start; index < descSets.size(); index++) {
auto &d = descSets[index];
// This is where we look up to see if we already have an identical descriptor previously in the array.
// We could do a simple custom hash map here that doesn't handle collisions, since those won't matter.
// Instead, for now we just check history one item backwards. Good enough, it seems.
if (index > start + 1) {
if (descSets[index - 1].count == d.count) {
if (!memcmp(descData.data() + d.offset, descData.data() + descSets[index - 1].offset, d.count * sizeof(PackedDescriptor))) {
d.set = descSets[index - 1].set;
continue;
}
}
}
if (setsUsed < ARRAY_SIZE(setCache)) {
d.set = setCache[setsUsed++];
} else {
// Allocate in small batches.
bool success = pool.Allocate(setCache, ARRAY_SIZE(setCache), layoutsForAlloc);
_dbg_assert_(success);
d.set = setCache[0];
setsUsed = 1;
}
// TODO: Build up bigger batches of writes.
const PackedDescriptor *data = descData.begin() + d.offset;
int numWrites = 0;
int numBuffers = 0;
int numImages = 0;
for (int i = 0; i < d.count; i++) {
if (!data[i].image.view) { // This automatically also checks for an null buffer due to the union.
continue;
}
switch (this->bindingTypes[i]) {
case BindingType::COMBINED_IMAGE_SAMPLER:
_dbg_assert_(data[i].image.sampler != VK_NULL_HANDLE);
imageInfo[numImages].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
imageInfo[numImages].imageView = data[i].image.view;
imageInfo[numImages].sampler = data[i].image.sampler;
writes[numWrites].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
writes[numWrites].pImageInfo = &imageInfo[numImages];
writes[numWrites].pBufferInfo = nullptr;
numImages++;
break;
case BindingType::STORAGE_IMAGE_COMPUTE:
imageInfo[numImages].imageLayout = VK_IMAGE_LAYOUT_GENERAL;
imageInfo[numImages].imageView = data[i].image.view;
imageInfo[numImages].sampler = VK_NULL_HANDLE;
writes[numWrites].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
writes[numWrites].pImageInfo = &imageInfo[numImages];
writes[numWrites].pBufferInfo = nullptr;
numImages++;
break;
case BindingType::STORAGE_BUFFER_VERTEX:
case BindingType::STORAGE_BUFFER_COMPUTE:
bufferInfo[numBuffers].buffer = data[i].buffer.buffer;
bufferInfo[numBuffers].offset = data[i].buffer.offset;
bufferInfo[numBuffers].range = data[i].buffer.range;
writes[numWrites].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
writes[numWrites].pBufferInfo = &bufferInfo[numBuffers];
writes[numWrites].pImageInfo = nullptr;
numBuffers++;
break;
case BindingType::UNIFORM_BUFFER_DYNAMIC_ALL:
case BindingType::UNIFORM_BUFFER_DYNAMIC_VERTEX:
bufferInfo[numBuffers].buffer = data[i].buffer.buffer;
bufferInfo[numBuffers].offset = 0;
bufferInfo[numBuffers].range = data[i].buffer.range;
writes[numWrites].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
writes[numWrites].pBufferInfo = &bufferInfo[numBuffers];
writes[numWrites].pImageInfo = nullptr;
numBuffers++;
break;
}
writes[numWrites].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writes[numWrites].pNext = nullptr;
writes[numWrites].descriptorCount = 1;
writes[numWrites].dstArrayElement = 0;
writes[numWrites].dstBinding = i;
writes[numWrites].dstSet = d.set;
writes[numWrites].pTexelBufferView = nullptr;
numWrites++;
}
vkUpdateDescriptorSets(vulkan->GetDevice(), numWrites, writes, 0, nullptr);
writeCount++;
}
data.flushedDescriptors_ = (int)descSets.size();
profile->descriptorsWritten += writeCount;
}

View file

@ -23,6 +23,7 @@
#include "Common/GPU/MiscTypes.h"
#include "Common/GPU/Vulkan/VulkanQueueRunner.h"
#include "Common/GPU/Vulkan/VulkanFramebuffer.h"
#include "Common/GPU/Vulkan/VulkanDescSet.h"
#include "Common/GPU/thin3d.h"
// Forward declaration
@ -106,7 +107,7 @@ public:
VkPipelineVertexInputStateCreateInfo vis{ VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO };
VkPipelineViewportStateCreateInfo views{ VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO };
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
VKRPipelineLayout *pipelineLayout = nullptr;
// Does not include the render pass type, it's passed in separately since the
// desc is persistent.
@ -131,6 +132,10 @@ struct VKRGraphicsPipeline {
// This deletes the whole VKRGraphicsPipeline, you must remove your last pointer to it when doing this.
void QueueForDeletion(VulkanContext *vulkan);
// This blocks until any background compiles are finished.
// Used during game shutdown before we clear out shaders that these compiles depend on.
void BlockUntilCompiled();
u32 GetVariantsBitmask() const;
void LogCreationFailure() const;
@ -180,6 +185,56 @@ struct CompileQueueEntry {
VkSampleCountFlagBits sampleCount;
};
// Pending descriptor sets.
// TODO: Sort these by VKRPipelineLayout to avoid storing it for each element.
struct PendingDescSet {
int offset; // probably enough with a u16.
u8 count;
VkDescriptorSet set;
};
struct PackedDescriptor {
union {
struct {
VkImageView view;
VkSampler sampler;
} image;
struct {
VkBuffer buffer;
uint32_t offset;
uint32_t range;
} buffer;
};
};
// Note that we only support a single descriptor set due to compatibility with some ancient devices.
// We should probably eventually give that up.
struct VKRPipelineLayout {
~VKRPipelineLayout();
enum { MAX_DESC_SET_BINDINGS = 10 };
BindingType bindingTypes[MAX_DESC_SET_BINDINGS];
uint32_t bindingTypesCount = 0;
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE; // only support 1 for now.
int pushConstSize = 0;
const char *tag = nullptr;
struct FrameData {
FrameData() : pool("GameDescPool", true) {}
VulkanDescSetPool pool;
FastVec<PackedDescriptor> descData_;
FastVec<PendingDescSet> descSets_;
// TODO: We should be able to get away with a single descData_/descSets_ and then send it along,
// but it's easier to just segregate by frame id.
int flushedDescriptors_ = 0;
};
FrameData frameData[VulkanContext::MAX_INFLIGHT_FRAMES];
void FlushDescSets(VulkanContext *vulkan, int frame, QueueProfileContext *profile);
};
class VulkanRenderManager {
public:
VulkanRenderManager(VulkanContext *vulkan, bool useThread, HistoryBuffer<FrameTimeData, FRAME_TIME_HISTORY_LENGTH> &frameTimeHistory);
@ -234,14 +289,25 @@ public:
VKRGraphicsPipeline *CreateGraphicsPipeline(VKRGraphicsPipelineDesc *desc, PipelineFlags pipelineFlags, uint32_t variantBitmask, VkSampleCountFlagBits sampleCount, bool cacheLoad, const char *tag);
VKRComputePipeline *CreateComputePipeline(VKRComputePipelineDesc *desc);
VKRPipelineLayout *CreatePipelineLayout(BindingType *bindingTypes, size_t bindingCount, bool geoShadersEnabled, const char *tag);
void DestroyPipelineLayout(VKRPipelineLayout *pipelineLayout);
void ReportBadStateForDraw();
void NudgeCompilerThread() {
compileMutex_.lock();
compileCond_.notify_one();
compileMutex_.unlock();
}
void BindPipeline(VKRGraphicsPipeline *pipeline, PipelineFlags flags, VkPipelineLayout pipelineLayout) {
_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER && pipeline != nullptr);
// This is the first call in a draw operation. Instead of asserting like we used to, you can now check the
// return value and skip the draw if we're in a bad state. In that case, call ReportBadState.
// The old assert wasn't very helpful in figuring out what caused it anyway...
bool BindPipeline(VKRGraphicsPipeline *pipeline, PipelineFlags flags, VKRPipelineLayout *pipelineLayout) {
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER && pipeline != nullptr);
if (!curRenderStep_ || curRenderStep_->stepType != VKRStepType::RENDER) {
return false;
}
VkRenderData &data = curRenderStep_->commands.push_uninitialized();
data.cmd = VKRRenderCommand::BIND_GRAPHICS_PIPELINE;
pipelinesToCheck_.push_back(pipeline);
@ -252,16 +318,8 @@ public:
// DebugBreak();
// }
curPipelineFlags_ |= flags;
}
void BindPipeline(VKRComputePipeline *pipeline, PipelineFlags flags, VkPipelineLayout pipelineLayout) {
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);
_dbg_assert_(pipeline != nullptr);
VkRenderData &data = curRenderStep_->commands.push_uninitialized();
data.cmd = VKRRenderCommand::BIND_COMPUTE_PIPELINE;
data.compute_pipeline.pipeline = pipeline;
data.compute_pipeline.pipelineLayout = pipelineLayout;
curPipelineFlags_ |= flags;
curPipelineLayout_ = pipelineLayout;
return true;
}
void SetViewport(const VkViewport &vp) {
@ -380,13 +438,36 @@ public:
curRenderStep_->render.stencilStore = VKRRenderPassStoreAction::DONT_CARE;
}
void Draw(VkDescriptorSet descSet, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, int count, int offset = 0) {
// Descriptors will match the current pipeline layout, set by the last call to BindPipeline.
// Count is the count of void*s. Two are needed for COMBINED_IMAGE_SAMPLER, everything else is a single one.
// The goal is to keep this function very small and fast, and do the expensive work on the render thread or
// another thread.
PackedDescriptor *PushDescriptorSet(int count, int *descSetIndex) {
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);
int curFrame = vulkan_->GetCurFrame();
VKRPipelineLayout::FrameData &data = curPipelineLayout_->frameData[curFrame];
size_t offset = data.descData_.size();
PackedDescriptor *retval = data.descData_.extend_uninitialized(count);
int setIndex = (int)data.descSets_.size();
PendingDescSet &descSet = data.descSets_.push_uninitialized();
descSet.offset = (uint32_t)offset;
descSet.count = count;
// descSet.set = VK_NULL_HANDLE; // to be filled in
*descSetIndex = setIndex;
return retval;
}
void Draw(int descSetIndex, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, int count, int offset = 0) {
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER && curStepHasViewport_ && curStepHasScissor_);
VkRenderData &data = curRenderStep_->commands.push_uninitialized();
data.cmd = VKRRenderCommand::DRAW;
data.draw.count = count;
data.draw.offset = offset;
data.draw.ds = descSet;
data.draw.descSetIndex = descSetIndex;
data.draw.vbuffer = vbuffer;
data.draw.voffset = voffset;
data.draw.numUboOffsets = numUboOffsets;
@ -396,13 +477,13 @@ public:
curRenderStep_->render.numDraws++;
}
void DrawIndexed(VkDescriptorSet descSet, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, VkBuffer ibuffer, int ioffset, int count, int numInstances) {
void DrawIndexed(int descSetIndex, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, VkBuffer ibuffer, int ioffset, int count, int numInstances) {
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER && curStepHasViewport_ && curStepHasScissor_);
VkRenderData &data = curRenderStep_->commands.push_uninitialized();
data.cmd = VKRRenderCommand::DRAW_INDEXED;
data.drawIndexed.count = count;
data.drawIndexed.instances = numInstances;
data.drawIndexed.ds = descSet;
data.drawIndexed.descSetIndex = descSetIndex;
data.drawIndexed.vbuffer = vbuffer;
data.drawIndexed.voffset = voffset;
data.drawIndexed.ibuffer = ibuffer;
@ -473,6 +554,9 @@ private:
void PresentWaitThreadFunc();
void PollPresentTiming();
void ResetDescriptorLists(int frame);
void FlushDescriptors(int frame);
FrameDataShared frameDataShared_;
FrameData frameData_[VulkanContext::MAX_INFLIGHT_FRAMES];
@ -540,9 +624,13 @@ private:
SimpleStat initTimeMs_;
SimpleStat totalGPUTimeMs_;
SimpleStat renderCPUTimeMs_;
SimpleStat descUpdateTimeMs_;
std::function<void(InvalidationCallbackFlags)> invalidationCallback_;
uint64_t frameIdGen_ = FRAME_TIME_HISTORY_LENGTH;
HistoryBuffer<FrameTimeData, FRAME_TIME_HISTORY_LENGTH> &frameTimeHistory_;
VKRPipelineLayout *curPipelineLayout_ = nullptr;
std::vector<VKRPipelineLayout *> pipelineLayouts_;
};

View file

@ -20,27 +20,20 @@
#include <string>
#include <map>
#include "Common/Log.h"
#include "Common/StringUtils.h"
#include "Common/System/Display.h"
#include "Common/Math/lin/matrix4x4.h"
#include "Common/Data/Convert/SmallDataConvert.h"
#include "Common/GPU/thin3d.h"
#include "Common/GPU/Vulkan/VulkanRenderManager.h"
#include "Common/Log.h"
#include "Common/StringUtils.h"
#include "Common/GPU/Vulkan/VulkanContext.h"
#include "Common/GPU/Vulkan/VulkanImage.h"
#include "Common/GPU/Vulkan/VulkanMemory.h"
#include "Common/GPU/Vulkan/VulkanLoader.h"
#include "Common/Thread/Promise.h"
#include "Common/GPU/Vulkan/VulkanLoader.h"
// We support a frame-global descriptor set, which can be optionally used by other code,
// but is not directly used by thin3d. It has to be defined here though, be in set 0
// and specified in every pipeline layout, otherwise it can't sit undisturbed when other
// descriptor sets are bound on top.
// For descriptor set 1, we use a simple descriptor set for all thin3d rendering: 1 UBO binding point, 3 combined texture/samples.
// For descriptor set 0 (the only one), we use a simple descriptor set for all thin3d rendering: 1 UBO binding point, 3 combined texture/samples.
//
// binding 0 - uniform buffer
// binding 1 - texture/sampler
@ -254,7 +247,7 @@ bool VKShaderModule::Compile(VulkanContext *vulkan, ShaderLanguage language, con
class VKInputLayout : public InputLayout {
public:
std::vector<VkVertexInputBindingDescription> bindings;
VkVertexInputBindingDescription binding;
std::vector<VkVertexInputAttributeDescription> attributes;
VkPipelineVertexInputStateCreateInfo visc;
};
@ -297,7 +290,7 @@ public:
std::vector<VKShaderModule *> deps;
int stride[4]{};
int stride = 0;
int dynamicUniformSize = 0;
bool usesStencil = false;
@ -453,13 +446,9 @@ public:
curPipeline_ = (VKPipeline *)pipeline;
}
// TODO: Make VKBuffers proper buffers, and do a proper binding model. This is just silly.
void BindVertexBuffers(int start, int count, Buffer **buffers, const int *offsets) override {
_assert_(start + count <= ARRAY_SIZE(curVBuffers_));
for (int i = 0; i < count; i++) {
curVBuffers_[i + start] = (VKBuffer *)buffers[i];
curVBufferOffsets_[i + start] = offsets ? offsets[i] : 0;
}
void BindVertexBuffer(Buffer *vertexBuffer, int offset) override {
curVBuffer_ = (VKBuffer *)vertexBuffer;
curVBufferOffset_ = offset;
}
void BindIndexBuffer(Buffer *indexBuffer, int offset) override {
curIBuffer_ = (VKBuffer *)indexBuffer;
@ -511,7 +500,7 @@ public:
}
}
VkDescriptorSet GetOrCreateDescriptorSet(VkBuffer buffer);
void BindDescriptors(VkBuffer buffer, PackedDescriptor descriptors[4]);
std::vector<std::string> GetFeatureList() const override;
std::vector<std::string> GetExtensionList(bool device, bool enabledOnly) const override;
@ -542,13 +531,12 @@ private:
VulkanTexture *nullTexture_ = nullptr;
AutoRef<VKPipeline> curPipeline_;
AutoRef<VKBuffer> curVBuffers_[4];
int curVBufferOffsets_[4]{};
AutoRef<VKBuffer> curVBuffer_;
int curVBufferOffset_ = 0;
AutoRef<VKBuffer> curIBuffer_;
int curIBufferOffset_ = 0;
VkDescriptorSetLayout descriptorSetLayout_ = VK_NULL_HANDLE;
VkPipelineLayout pipelineLayout_ = VK_NULL_HANDLE;
VKRPipelineLayout *pipelineLayout_ = nullptr;
VkPipelineCache pipelineCache_ = VK_NULL_HANDLE;
AutoRef<VKFramebuffer> curFramebuffer_;
@ -564,19 +552,6 @@ private:
VulkanPushPool *push_ = nullptr;
struct FrameData {
FrameData() : descriptorPool("VKContext", false) {
descriptorPool.Setup([this] { descSets_.clear(); });
}
// Per-frame descriptor set cache. As it's per frame and reset every frame, we don't need to
// worry about invalidating descriptors pointing to deleted textures.
// However! ARM is not a fan of doing it this way.
std::map<DescriptorSetKey, VkDescriptorSet> descSets_;
VulkanDescSetPool descriptorPool;
};
FrameData frame_[VulkanContext::MAX_INFLIGHT_FRAMES];
DeviceCaps caps_{};
uint8_t stencilRef_ = 0;
@ -874,8 +849,11 @@ VKContext::VKContext(VulkanContext *vulkan, bool useRenderThread)
caps_.tesselationShaderSupported = vulkan->GetDeviceFeatures().enabled.standard.tessellationShader != 0;
caps_.dualSourceBlend = vulkan->GetDeviceFeatures().enabled.standard.dualSrcBlend != 0;
caps_.depthClampSupported = vulkan->GetDeviceFeatures().enabled.standard.depthClamp != 0;
// Comment out these two to test geometry shader culling on any geometry shader-supporting hardware.
caps_.clipDistanceSupported = vulkan->GetDeviceFeatures().enabled.standard.shaderClipDistance != 0;
caps_.cullDistanceSupported = vulkan->GetDeviceFeatures().enabled.standard.shaderCullDistance != 0;
caps_.framebufferBlitSupported = true;
caps_.framebufferCopySupported = true;
caps_.framebufferDepthBlitSupported = vulkan->GetDeviceInfo().canBlitToPreferredDepthStencilFormat;
@ -1038,64 +1016,22 @@ VKContext::VKContext(VulkanContext *vulkan, bool useRenderThread)
caps_.deviceID = deviceProps.deviceID;
device_ = vulkan->GetDevice();
std::vector<VkDescriptorPoolSize> dpTypes;
dpTypes.resize(2);
dpTypes[0].descriptorCount = 200;
dpTypes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
dpTypes[1].descriptorCount = 200 * MAX_BOUND_TEXTURES;
dpTypes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
VkDescriptorPoolCreateInfo dp{ VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO };
// Don't want to mess around with individually freeing these, let's go dynamic each frame.
dp.flags = 0;
// 200 textures per frame was not enough for the UI.
dp.maxSets = 4096;
VkBufferUsageFlags usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
push_ = new VulkanPushPool(vulkan_, "pushBuffer", 4 * 1024 * 1024, usage);
for (int i = 0; i < VulkanContext::MAX_INFLIGHT_FRAMES; i++) {
frame_[i].descriptorPool.Create(vulkan_, dp, dpTypes);
}
// binding 0 - uniform data
// binding 1 - combined sampler/image 0
// binding 2 - combined sampler/image 1
VkDescriptorSetLayoutBinding bindings[MAX_BOUND_TEXTURES + 1];
bindings[0].descriptorCount = 1;
bindings[0].pImmutableSamplers = nullptr;
bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
bindings[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[0].binding = 0;
// ...etc
BindingType bindings[MAX_BOUND_TEXTURES + 1];
bindings[0] = BindingType::UNIFORM_BUFFER_DYNAMIC_ALL;
for (int i = 0; i < MAX_BOUND_TEXTURES; ++i) {
bindings[i + 1].descriptorCount = 1;
bindings[i + 1].pImmutableSamplers = nullptr;
bindings[i + 1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
bindings[i + 1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[i + 1].binding = i + 1;
bindings[1 + i] = BindingType::COMBINED_IMAGE_SAMPLER;
}
VkDescriptorSetLayoutCreateInfo dsl = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO };
dsl.bindingCount = ARRAY_SIZE(bindings);
dsl.pBindings = bindings;
VkResult res = vkCreateDescriptorSetLayout(device_, &dsl, nullptr, &descriptorSetLayout_);
_assert_(VK_SUCCESS == res);
vulkan_->SetDebugName(descriptorSetLayout_, VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT, "thin3d_d_layout");
VkPipelineLayoutCreateInfo pl = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
pl.pPushConstantRanges = nullptr;
pl.pushConstantRangeCount = 0;
VkDescriptorSetLayout setLayouts[1] = { descriptorSetLayout_ };
pl.setLayoutCount = ARRAY_SIZE(setLayouts);
pl.pSetLayouts = setLayouts;
res = vkCreatePipelineLayout(device_, &pl, nullptr, &pipelineLayout_);
_assert_(VK_SUCCESS == res);
vulkan_->SetDebugName(pipelineLayout_, VK_OBJECT_TYPE_PIPELINE_LAYOUT, "thin3d_p_layout");
pipelineLayout_ = renderManager_.CreatePipelineLayout(bindings, ARRAY_SIZE(bindings), caps_.geometryShaderSupported, "thin3d_layout");
VkPipelineCacheCreateInfo pc{ VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO };
res = vkCreatePipelineCache(vulkan_->GetDevice(), &pc, nullptr, &pipelineCache_);
VkResult res = vkCreatePipelineCache(vulkan_->GetDevice(), &pc, nullptr, &pipelineCache_);
_assert_(VK_SUCCESS == res);
}
@ -1103,25 +1039,15 @@ VKContext::~VKContext() {
DestroyPresets();
delete nullTexture_;
// This also destroys all descriptor sets.
for (int i = 0; i < VulkanContext::MAX_INFLIGHT_FRAMES; i++) {
frame_[i].descriptorPool.Destroy();
}
push_->Destroy();
delete push_;
vulkan_->Delete().QueueDeleteDescriptorSetLayout(descriptorSetLayout_);
vulkan_->Delete().QueueDeletePipelineLayout(pipelineLayout_);
renderManager_.DestroyPipelineLayout(pipelineLayout_);
vulkan_->Delete().QueueDeletePipelineCache(pipelineCache_);
}
void VKContext::BeginFrame(DebugFlags debugFlags) {
renderManager_.BeginFrame(debugFlags & DebugFlags::PROFILE_TIMESTAMPS, debugFlags & DebugFlags::PROFILE_SCOPES);
FrameData &frame = frame_[vulkan_->GetCurFrame()];
push_->BeginFrame();
frame.descriptorPool.Reset();
}
void VKContext::EndFrame() {
@ -1159,81 +1085,30 @@ void VKContext::WipeQueue() {
renderManager_.Wipe();
}
VkDescriptorSet VKContext::GetOrCreateDescriptorSet(VkBuffer buf) {
DescriptorSetKey key{};
FrameData *frame = &frame_[vulkan_->GetCurFrame()];
void VKContext::BindDescriptors(VkBuffer buf, PackedDescriptor descriptors[4]) {
descriptors[0].buffer.buffer = buf;
descriptors[0].buffer.offset = 0; // dynamic
descriptors[0].buffer.range = curPipeline_->GetUBOSize();
int numDescs = 1;
for (int i = 0; i < MAX_BOUND_TEXTURES; ++i) {
VkImageView view;
VkSampler sampler;
if (boundTextures_[i]) {
key.imageViews_[i] = (boundTextureFlags_[i] & TextureBindFlags::VULKAN_BIND_ARRAY) ? boundTextures_[i]->GetImageArrayView() : boundTextures_[i]->GetImageView();
view = (boundTextureFlags_[i] & TextureBindFlags::VULKAN_BIND_ARRAY) ? boundTextures_[i]->GetImageArrayView() : boundTextures_[i]->GetImageView();
} else {
key.imageViews_[i] = boundImageView_[i];
view = boundImageView_[i];
}
key.samplers_[i] = boundSamplers_[i];
}
key.buffer_ = buf;
sampler = boundSamplers_[i] ? boundSamplers_[i]->GetSampler() : VK_NULL_HANDLE;
auto iter = frame->descSets_.find(key);
if (iter != frame->descSets_.end()) {
return iter->second;
}
VkDescriptorSet descSet = frame->descriptorPool.Allocate(1, &descriptorSetLayout_, "thin3d_descset");
if (descSet == VK_NULL_HANDLE) {
ERROR_LOG(G3D, "GetOrCreateDescriptorSet failed");
return VK_NULL_HANDLE;
}
vulkan_->SetDebugName(descSet, VK_OBJECT_TYPE_DESCRIPTOR_SET, "(thin3d desc set)");
VkDescriptorBufferInfo bufferDesc;
bufferDesc.buffer = buf;
bufferDesc.offset = 0;
bufferDesc.range = curPipeline_->GetUBOSize();
VkDescriptorImageInfo imageDesc[MAX_BOUND_TEXTURES]{};
VkWriteDescriptorSet writes[1 + MAX_BOUND_TEXTURES]{};
// If handles are NULL for whatever buggy reason, it's best to leave the descriptors
// unwritten instead of trying to write a zero, which is not legal.
int numWrites = 0;
if (buf) {
writes[numWrites].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writes[numWrites].dstSet = descSet;
writes[numWrites].dstArrayElement = 0;
writes[numWrites].dstBinding = 0;
writes[numWrites].pBufferInfo = &bufferDesc;
writes[numWrites].pImageInfo = nullptr;
writes[numWrites].pTexelBufferView = nullptr;
writes[numWrites].descriptorCount = 1;
writes[numWrites].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
numWrites++;
}
for (int i = 0; i < MAX_BOUND_TEXTURES; ++i) {
if (key.imageViews_[i] && key.samplers_[i] && key.samplers_[i]->GetSampler()) {
imageDesc[i].imageView = key.imageViews_[i];
imageDesc[i].sampler = key.samplers_[i]->GetSampler();
imageDesc[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
writes[numWrites].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writes[numWrites].dstSet = descSet;
writes[numWrites].dstArrayElement = 0;
writes[numWrites].dstBinding = i + 1;
writes[numWrites].pBufferInfo = nullptr;
writes[numWrites].pImageInfo = &imageDesc[i];
writes[numWrites].pTexelBufferView = nullptr;
writes[numWrites].descriptorCount = 1;
writes[numWrites].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
numWrites++;
if (view && sampler) {
descriptors[i + 1].image.view = view;
descriptors[i + 1].image.sampler = sampler;
} else {
descriptors[i + 1].image.view = VK_NULL_HANDLE;
descriptors[i + 1].image.sampler = VK_NULL_HANDLE;
}
}
vkUpdateDescriptorSets(device_, numWrites, writes, 0, nullptr);
frame->descSets_[key] = descSet;
return descSet;
}
Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc, const char *tag) {
@ -1270,13 +1145,11 @@ Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc, const char
}
}
_dbg_assert_(input && input->bindings.size() == 1);
_dbg_assert_(input);
_dbg_assert_((int)input->attributes.size() == (int)input->visc.vertexAttributeDescriptionCount);
for (int i = 0; i < (int)input->bindings.size(); i++) {
pipeline->stride[i] = input->bindings[i].stride;
}
gDesc.ibd = input->bindings[0];
pipeline->stride = input->binding.stride;
gDesc.ibd = input->binding;
for (size_t i = 0; i < input->attributes.size(); i++) {
gDesc.attrs[i] = input->attributes[i];
}
@ -1358,23 +1231,20 @@ InputLayout *VKContext::CreateInputLayout(const InputLayoutDesc &desc) {
VKInputLayout *vl = new VKInputLayout();
vl->visc = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO };
vl->visc.flags = 0;
vl->visc.vertexBindingDescriptionCount = 1;
vl->visc.vertexAttributeDescriptionCount = (uint32_t)desc.attributes.size();
vl->visc.vertexBindingDescriptionCount = (uint32_t)desc.bindings.size();
vl->bindings.resize(vl->visc.vertexBindingDescriptionCount);
vl->attributes.resize(vl->visc.vertexAttributeDescriptionCount);
vl->visc.pVertexBindingDescriptions = vl->bindings.data();
vl->visc.pVertexBindingDescriptions = &vl->binding;
vl->visc.pVertexAttributeDescriptions = vl->attributes.data();
for (size_t i = 0; i < desc.attributes.size(); i++) {
vl->attributes[i].binding = (uint32_t)desc.attributes[i].binding;
vl->attributes[i].binding = 0;
vl->attributes[i].format = DataFormatToVulkan(desc.attributes[i].format);
vl->attributes[i].location = desc.attributes[i].location;
vl->attributes[i].offset = desc.attributes[i].offset;
}
for (size_t i = 0; i < desc.bindings.size(); i++) {
vl->bindings[i].inputRate = desc.bindings[i].instanceRate ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX;
vl->bindings[i].binding = (uint32_t)i;
vl->bindings[i].stride = desc.bindings[i].stride;
}
vl->binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
vl->binding.binding = 0;
vl->binding.stride = desc.stride;
return vl;
}
@ -1483,7 +1353,7 @@ void VKContext::BindTextures(int start, int count, Texture **textures, TextureBi
boundTextures_[i] = static_cast<VKTexture *>(textures[i - start]);
boundTextureFlags_[i] = flags;
if (boundTextures_[i]) {
// If a texture is bound, we set these up in GetOrCreateDescriptorSet too.
// If a texture is bound, we set these up in BindDescriptors too.
// But we might need to set the view here anyway so it can be queried using GetNativeObject.
if (flags & TextureBindFlags::VULKAN_BIND_ARRAY) {
boundImageView_[i] = boundTextures_[i]->GetImageArrayView();
@ -1529,42 +1399,36 @@ void VKContext::ApplyDynamicState() {
}
void VKContext::Draw(int vertexCount, int offset) {
VKBuffer *vbuf = curVBuffers_[0];
VKBuffer *vbuf = curVBuffer_;
VkBuffer vulkanVbuf;
VkBuffer vulkanUBObuf;
uint32_t ubo_offset = (uint32_t)curPipeline_->PushUBO(push_, vulkan_, &vulkanUBObuf);
size_t vbBindOffset = push_->Push(vbuf->GetData(), vbuf->GetSize(), 4, &vulkanVbuf);
VkDescriptorSet descSet = GetOrCreateDescriptorSet(vulkanUBObuf);
if (descSet == VK_NULL_HANDLE) {
ERROR_LOG(G3D, "GetOrCreateDescriptorSet failed, skipping %s", __FUNCTION__);
return;
}
BindCurrentPipeline();
ApplyDynamicState();
renderManager_.Draw(descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffsets_[0], vertexCount, offset);
int descSetIndex;
PackedDescriptor *descriptors = renderManager_.PushDescriptorSet(4, &descSetIndex);
BindDescriptors(vulkanUBObuf, descriptors);
renderManager_.Draw(descSetIndex, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffset_, vertexCount, offset);
}
void VKContext::DrawIndexed(int vertexCount, int offset) {
VKBuffer *ibuf = curIBuffer_;
VKBuffer *vbuf = curVBuffers_[0];
VKBuffer *vbuf = curVBuffer_;
VkBuffer vulkanVbuf, vulkanIbuf, vulkanUBObuf;
uint32_t ubo_offset = (uint32_t)curPipeline_->PushUBO(push_, vulkan_, &vulkanUBObuf);
size_t vbBindOffset = push_->Push(vbuf->GetData(), vbuf->GetSize(), 4, &vulkanVbuf);
size_t ibBindOffset = push_->Push(ibuf->GetData(), ibuf->GetSize(), 4, &vulkanIbuf);
VkDescriptorSet descSet = GetOrCreateDescriptorSet(vulkanUBObuf);
if (descSet == VK_NULL_HANDLE) {
ERROR_LOG(G3D, "GetOrCreateDescriptorSet failed, skipping %s", __FUNCTION__);
return;
}
BindCurrentPipeline();
ApplyDynamicState();
renderManager_.DrawIndexed(descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffsets_[0], vulkanIbuf, (int)ibBindOffset + offset * sizeof(uint32_t), vertexCount, 1);
int descSetIndex;
PackedDescriptor *descriptors = renderManager_.PushDescriptorSet(4, &descSetIndex);
BindDescriptors(vulkanUBObuf, descriptors);
renderManager_.DrawIndexed(descSetIndex, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffset_, vulkanIbuf, (int)ibBindOffset + offset * sizeof(uint32_t), vertexCount, 1);
}
void VKContext::DrawUP(const void *vdata, int vertexCount) {
@ -1574,7 +1438,7 @@ void VKContext::DrawUP(const void *vdata, int vertexCount) {
}
VkBuffer vulkanVbuf, vulkanUBObuf;
size_t dataSize = vertexCount * curPipeline_->stride[0];
size_t dataSize = vertexCount * curPipeline_->stride;
uint32_t vbBindOffset;
uint8_t *dataPtr = push_->Allocate(dataSize, 4, &vulkanVbuf, &vbBindOffset);
_assert_(dataPtr != nullptr);
@ -1582,15 +1446,12 @@ void VKContext::DrawUP(const void *vdata, int vertexCount) {
uint32_t ubo_offset = (uint32_t)curPipeline_->PushUBO(push_, vulkan_, &vulkanUBObuf);
VkDescriptorSet descSet = GetOrCreateDescriptorSet(vulkanUBObuf);
if (descSet == VK_NULL_HANDLE) {
ERROR_LOG(G3D, "GetOrCreateDescriptorSet failed, skipping %s", __FUNCTION__);
return;
}
BindCurrentPipeline();
ApplyDynamicState();
renderManager_.Draw(descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffsets_[0], vertexCount);
int descSetIndex;
PackedDescriptor *descriptors = renderManager_.PushDescriptorSet(4, &descSetIndex);
BindDescriptors(vulkanUBObuf, descriptors);
renderManager_.Draw(descSetIndex, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffset_, vertexCount);
}
void VKContext::BindCurrentPipeline() {

View file

@ -475,20 +475,14 @@ protected:
DataFormat format_ = DataFormat::UNDEFINED;
};
struct BindingDesc {
int stride;
bool instanceRate;
};
struct AttributeDesc {
int binding;
int location; // corresponds to semantic
DataFormat format;
int offset;
};
struct InputLayoutDesc {
std::vector<BindingDesc> bindings;
int stride;
std::vector<AttributeDesc> attributes;
};
@ -787,7 +781,7 @@ public:
virtual void BindSamplerStates(int start, int count, SamplerState **state) = 0;
virtual void BindTextures(int start, int count, Texture **textures, TextureBindFlags flags = TextureBindFlags::NONE) = 0;
virtual void BindVertexBuffers(int start, int count, Buffer **buffers, const int *offsets) = 0;
virtual void BindVertexBuffer(Buffer *vertexBuffer, int offset) = 0;
virtual void BindIndexBuffer(Buffer *indexBuffer, int offset) = 0;
// Sometimes it's necessary to bind a texture not created by thin3d, and use with a thin3d pipeline.

View file

@ -37,6 +37,7 @@ const char *GetDeviceName(int deviceId) {
std::vector<InputMapping> dpadKeys;
std::vector<InputMapping> confirmKeys;
std::vector<InputMapping> cancelKeys;
std::vector<InputMapping> infoKeys;
std::vector<InputMapping> tabLeftKeys;
std::vector<InputMapping> tabRightKeys;
static std::unordered_map<InputDeviceID, int> uiFlipAnalogY;
@ -69,6 +70,10 @@ void SetTabLeftRightKeys(const std::vector<InputMapping> &tabLeft, const std::ve
tabRightKeys = tabRight;
}
void SetInfoKeys(const std::vector<InputMapping> &info) {
infoKeys = info;
}
void SetAnalogFlipY(std::unordered_map<InputDeviceID, int> flipYByDeviceId) {
uiFlipAnalogY = flipYByDeviceId;
}
@ -81,11 +86,12 @@ int GetAnalogYDirection(InputDeviceID deviceId) {
}
// NOTE: Changing the format of FromConfigString/ToConfigString breaks controls.ini backwards compatibility.
InputMapping InputMapping::FromConfigString(const std::string &str) {
std::vector<std::string> parts;
InputMapping InputMapping::FromConfigString(const std::string_view str) {
std::vector<std::string_view> parts;
SplitString(str, '-', parts);
InputDeviceID deviceId = (InputDeviceID)(atoi(parts[0].c_str()));
InputKeyCode keyCode = (InputKeyCode)atoi(parts[1].c_str());
// We only convert to std::string here to add null terminators for atoi.
InputDeviceID deviceId = (InputDeviceID)(atoi(std::string(parts[0]).c_str()));
InputKeyCode keyCode = (InputKeyCode)atoi(std::string(parts[1]).c_str());
InputMapping mapping;
mapping.deviceId = deviceId;

View file

@ -31,7 +31,7 @@ enum InputDeviceID {
DEVICE_ID_XINPUT_1 = 21,
DEVICE_ID_XINPUT_2 = 22,
DEVICE_ID_XINPUT_3 = 23,
DEVICE_ID_ACCELEROMETER = 30,
DEVICE_ID_ACCELEROMETER = 30, // no longer used
DEVICE_ID_XR_HMD = 39,
DEVICE_ID_XR_CONTROLLER_LEFT = 40,
DEVICE_ID_XR_CONTROLLER_RIGHT = 41,
@ -79,7 +79,7 @@ public:
_dbg_assert_(direction != 0);
}
static InputMapping FromConfigString(const std::string &str);
static InputMapping FromConfigString(std::string_view str);
std::string ToConfigString() const;
InputDeviceID deviceId;
@ -191,12 +191,14 @@ struct AxisInput {
extern std::vector<InputMapping> dpadKeys;
extern std::vector<InputMapping> confirmKeys;
extern std::vector<InputMapping> cancelKeys;
extern std::vector<InputMapping> infoKeys;
extern std::vector<InputMapping> tabLeftKeys;
extern std::vector<InputMapping> tabRightKeys;
void SetDPadKeys(const std::vector<InputMapping> &leftKey, const std::vector<InputMapping> &rightKey,
const std::vector<InputMapping> &upKey, const std::vector<InputMapping> &downKey);
void SetConfirmCancelKeys(const std::vector<InputMapping> &confirm, const std::vector<InputMapping> &cancel);
void SetTabLeftRightKeys(const std::vector<InputMapping> &tabLeft, const std::vector<InputMapping> &tabRight);
void SetInfoKeys(const std::vector<InputMapping> &info);
// 0 means unknown (attempt autodetect), -1 means flip, 1 means original direction.
void SetAnalogFlipY(std::unordered_map<InputDeviceID, int> flipYByDeviceId);

View file

@ -305,7 +305,7 @@ enum InputAxis {
JOYSTICK_AXIS_MOUSE_REL_X = 26,
JOYSTICK_AXIS_MOUSE_REL_Y = 27,
// Mobile device accelerometer/gyro
// Mobile device accelerometer/gyro. NOTE: These are no longer passed around internally, only used for the plugin API.
JOYSTICK_AXIS_ACCELEROMETER_X = 40,
JOYSTICK_AXIS_ACCELEROMETER_Y = 41,
JOYSTICK_AXIS_ACCELEROMETER_Z = 42,

View file

@ -25,6 +25,7 @@
#include "StringUtils.h"
#include "Common/Data/Encoding/Utf8.h"
#include "Common/Thread/ThreadUtil.h"
#include "Common/TimeUtil.h"
#if PPSSPP_PLATFORM(ANDROID)
#include <android/log.h>
@ -38,10 +39,12 @@ static bool hitAnyAsserts = false;
std::mutex g_extraAssertInfoMutex;
std::string g_extraAssertInfo = "menu";
double g_assertInfoTime = 0.0;
void SetExtraAssertInfo(const char *info) {
std::lock_guard<std::mutex> guard(g_extraAssertInfoMutex);
g_extraAssertInfo = info ? info : "menu";
g_assertInfoTime = time_now_d();
}
bool HandleAssert(const char *function, const char *file, int line, const char *expression, const char* format, ...) {
@ -57,7 +60,8 @@ bool HandleAssert(const char *function, const char *file, int line, const char *
char formatted[LOG_BUF_SIZE + 128];
{
std::lock_guard<std::mutex> guard(g_extraAssertInfoMutex);
snprintf(formatted, sizeof(formatted), "(%s:%s:%d): [%s] (%s) %s", file, function, line, expression, g_extraAssertInfo.c_str(), text);
double delta = time_now_d() - g_assertInfoTime;
snprintf(formatted, sizeof(formatted), "(%s:%s:%d): [%s] (%s, %0.1fs) %s", file, function, line, expression, g_extraAssertInfo.c_str(), delta, text);
}
// Normal logging (will also log to Android log)

View file

@ -30,6 +30,7 @@
#include "Common/Net/URL.h"
#include "Common/File/FileDescriptor.h"
#include "Common/SysError.h"
#include "Common/Thread/ThreadUtil.h"
#include "Common/Data/Encoding/Compression.h"
#include "Common/Net/NetBuffer.h"
@ -97,7 +98,7 @@ static void FormatAddr(char *addrbuf, size_t bufsize, const addrinfo *info) {
switch (info->ai_family) {
case AF_INET:
case AF_INET6:
inet_ntop(info->ai_family, info->ai_addr, addrbuf, bufsize);
inet_ntop(info->ai_family, &((sockaddr_in *)info->ai_addr)->sin_addr, addrbuf, bufsize);
break;
default:
snprintf(addrbuf, bufsize, "(Unknown AF %d)", info->ai_family);
@ -131,11 +132,22 @@ bool Connection::Connect(int maxTries, double timeout, bool *cancelConnect) {
// Start trying to connect (async with timeout.)
errno = 0;
if (connect(sock, possible->ai_addr, (int)possible->ai_addrlen) < 0) {
if (errno != 0 && errno != EINPROGRESS) {
char addrStr[128];
#if PPSSPP_PLATFORM(WINDOWS)
int errorCode = WSAGetLastError();
std::string errorString = GetStringErrorMsg(errorCode);
bool unreachable = errorCode == WSAENETUNREACH;
bool inProgress = errorCode == WSAEINPROGRESS || errorCode == WSAEWOULDBLOCK;
#else
int errorCode = errno;
std::string errorString = strerror(errno);
bool unreachable = errorCode == ENETUNREACH;
bool inProgress = errorCode == EINPROGRESS || errorCode == EWOULDBLOCK;
#endif
if (!inProgress) {
char addrStr[128]{};
FormatAddr(addrStr, sizeof(addrStr), possible);
if (errno != ENETUNREACH) {
ERROR_LOG(HTTP, "connect(%d) call to %s failed (%d: %s)", sock, addrStr, errno, strerror(errno));
if (!unreachable) {
ERROR_LOG(HTTP, "connect(%d) call to %s failed (%d: %s)", sock, addrStr, errorCode, errorString.c_str());
} else {
INFO_LOG(HTTP, "connect(%d): Ignoring unreachable resolved address %s", sock, addrStr);
}
@ -207,9 +219,9 @@ namespace http {
// TODO: do something sane here
constexpr const char *DEFAULT_USERAGENT = "PPSSPP";
constexpr const char *HTTP_VERSION = "1.1";
Client::Client() {
httpVersion_ = "1.1";
userAgent_ = DEFAULT_USERAGENT;
}
@ -341,7 +353,7 @@ int Client::SendRequestWithData(const char *method, const RequestParams &req, co
"\r\n";
buffer.Printf(tpl,
method, req.resource.c_str(), httpVersion_,
method, req.resource.c_str(), HTTP_VERSION,
host_.c_str(),
userAgent_.c_str(),
req.acceptMime,

View file

@ -86,7 +86,6 @@ public:
protected:
std::string userAgent_;
const char *httpVersion_;
double dataTimeout_ = 900.0;
};

View file

@ -26,12 +26,12 @@ bool RequestHeader::GetParamValue(const char *param_name, std::string *value) co
if (!params)
return false;
std::string p(params);
std::vector<std::string> v;
std::vector<std::string_view> v;
SplitString(p, '&', v);
for (size_t i = 0; i < v.size(); i++) {
std::vector<std::string> parts;
std::vector<std::string_view> parts;
SplitString(v[i], '=', parts);
DEBUG_LOG(IO, "Param: %s Value: %s", parts[0].c_str(), parts[1].c_str());
DEBUG_LOG(IO, "Param: %.*s Value: %.*s", (int)parts[0].size(), parts[0].data(), (int)parts[1].size(), parts[1].data());
if (parts[0] == param_name) {
*value = parts[1];
return true;

View file

@ -29,6 +29,7 @@
#include "Common/Log.h"
#include "Common/TimeUtil.h"
#include "Common/Data/Encoding/Utf8.h"
#ifndef HTTPS_NOT_AVAILABLE
#include "ext/naett/naett.h"
@ -109,7 +110,7 @@ bool DNSResolve(const std::string &host, const std::string &service, addrinfo **
if (result != 0) {
#ifdef _WIN32
error = gai_strerrorA(result);
error = ConvertWStringToUTF8(gai_strerror(result));
#else
error = gai_strerror(result);
#endif

View file

@ -115,13 +115,13 @@ const char HEX2DEC[256] =
/* F */ N1,N1,N1,N1, N1,N1,N1,N1, N1,N1,N1,N1, N1,N1,N1,N1
};
std::string UriDecode(const std::string & sSrc)
std::string UriDecode(std::string_view sSrc)
{
// Note from RFC1630: "Sequences which start with a percent sign
// but are not followed by two hexadecimal characters (0-9, A-F) are reserved
// for future extension"
const unsigned char * pSrc = (const unsigned char *)sSrc.c_str();
const unsigned char * pSrc = (const unsigned char *)sSrc.data();
const size_t SRC_LEN = sSrc.length();
const unsigned char * const SRC_END = pSrc + SRC_LEN;
const unsigned char * const SRC_LAST_DEC = SRC_END - 2; // last decodable '%'
@ -129,14 +129,10 @@ std::string UriDecode(const std::string & sSrc)
char * const pStart = new char[SRC_LEN]; // Output will be shorter.
char * pEnd = pStart;
while (pSrc < SRC_LAST_DEC)
{
if (*pSrc == '%')
{
while (pSrc < SRC_LAST_DEC) {
if (*pSrc == '%') {
char dec1, dec2;
if (N1 != (dec1 = HEX2DEC[*(pSrc + 1)])
&& N1 != (dec2 = HEX2DEC[*(pSrc + 2)]))
{
if (N1 != (dec1 = HEX2DEC[*(pSrc + 1)]) && N1 != (dec2 = HEX2DEC[*(pSrc + 2)])) {
*pEnd++ = (dec1 << 4) + dec2;
pSrc += 3;
continue;
@ -156,8 +152,7 @@ std::string UriDecode(const std::string & sSrc)
}
// Only alphanum and underscore is safe.
const char SAFE[256] =
{
static const char SAFE[256] = {
/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
/* 0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
/* 1 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
@ -180,21 +175,18 @@ const char SAFE[256] =
/* F */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0
};
std::string UriEncode(const std::string & sSrc)
{
std::string UriEncode(std::string_view sSrc) {
const char DEC2HEX[16 + 1] = "0123456789ABCDEF";
const unsigned char * pSrc = (const unsigned char *)sSrc.c_str();
const unsigned char * pSrc = (const unsigned char *)sSrc.data();
const size_t SRC_LEN = sSrc.length();
unsigned char * const pStart = new unsigned char[SRC_LEN * 3];
unsigned char * pEnd = pStart;
const unsigned char * const SRC_END = pSrc + SRC_LEN;
for (; pSrc < SRC_END; ++pSrc)
{
if (SAFE[*pSrc])
for (; pSrc < SRC_END; ++pSrc) {
if (SAFE[*pSrc]) {
*pEnd++ = *pSrc;
else
{
} else {
// escape this char
*pEnd++ = '%';
*pEnd++ = DEC2HEX[*pSrc >> 4];

View file

@ -203,5 +203,5 @@ private:
};
std::string UriDecode(const std::string & sSrc);
std::string UriEncode(const std::string & sSrc);
std::string UriDecode(std::string_view sSrc);
std::string UriEncode(std::string_view sSrc);

View file

@ -39,13 +39,11 @@ void DrawBuffer::Init(Draw::DrawContext *t3d, Draw::Pipeline *pipeline) {
Draw::InputLayout *DrawBuffer::CreateInputLayout(Draw::DrawContext *t3d) {
using namespace Draw;
InputLayoutDesc desc = {
sizeof(Vertex),
{
{ sizeof(Vertex), false },
},
{
{ 0, SEM_POSITION, DataFormat::R32G32B32_FLOAT, 0 },
{ 0, SEM_TEXCOORD0, DataFormat::R32G32_FLOAT, 12 },
{ 0, SEM_COLOR0, DataFormat::R8G8B8A8_UNORM, 20 },
{ SEM_POSITION, DataFormat::R32G32B32_FLOAT, 0 },
{ SEM_TEXCOORD0, DataFormat::R32G32_FLOAT, 12 },
{ SEM_COLOR0, DataFormat::R8G8B8A8_UNORM, 20 },
},
};
@ -106,7 +104,7 @@ void DrawBuffer::V(float x, float y, float z, uint32_t color, float u, float v)
void DrawBuffer::Rect(float x, float y, float w, float h, uint32_t color, int align) {
DoAlign(align, &x, &y, &w, &h);
RectVGradient(x, y, w, h, color, color);
RectVGradient(x, y, x + w, y + h, color, color);
}
void DrawBuffer::hLine(float x1, float y, float x2, uint32_t color) {
@ -121,13 +119,13 @@ void DrawBuffer::vLine(float x, float y1, float y2, uint32_t color) {
Rect(x, y1, g_display.pixel_in_dps_x, y2 - y1, color);
}
void DrawBuffer::RectVGradient(float x, float y, float w, float h, uint32_t colorTop, uint32_t colorBottom) {
V(x, y, 0, colorTop, 0, 0);
V(x + w, y, 0, colorTop, 1, 0);
V(x + w, y + h, 0, colorBottom, 1, 1);
V(x, y, 0, colorTop, 0, 0);
V(x + w, y + h, 0, colorBottom, 1, 1);
V(x, y + h, 0, colorBottom, 0, 1);
void DrawBuffer::RectVGradient(float x1, float y1, float x2, float y2, uint32_t colorTop, uint32_t colorBottom) {
V(x1, y1, 0, colorTop, 0, 0);
V(x2, y1, 0, colorTop, 1, 0);
V(x2, y2, 0, colorBottom, 1, 1);
V(x1, y1, 0, colorTop, 0, 0);
V(x2, y2, 0, colorBottom, 1, 1);
V(x1, y2, 0, colorBottom, 0, 1);
}
void DrawBuffer::RectOutline(float x, float y, float w, float h, uint32_t color, int align) {
@ -142,7 +140,7 @@ void DrawBuffer::MultiVGradient(float x, float y, float w, float h, const Gradie
for (int i = 0; i < numStops - 1; i++) {
float t0 = stops[i].t, t1 = stops[i+1].t;
uint32_t c0 = stops[i].color, c1 = stops[i+1].color;
RectVGradient(x, y + h * t0, w, h * (t1 - t0), c0, c1);
RectVGradient(x, y + h * t0, x + w, y + h * (t1 - t0), c0, c1);
}
}

View file

@ -83,9 +83,10 @@ public:
void RectOutline(float x, float y, float w, float h, uint32_t color, int align = ALIGN_TOPLEFT);
void RectVGradient(float x, float y, float w, float h, uint32_t colorTop, uint32_t colorBottom);
// NOTE: This one takes x2/y2 instead of w/h, better for gap-free graphics.
void RectVGradient(float x1, float y1, float x2, float y2, uint32_t colorTop, uint32_t colorBottom);
void RectVDarkFaded(float x, float y, float w, float h, uint32_t colorTop) {
RectVGradient(x, y, w, h, colorTop, darkenColor(colorTop));
RectVGradient(x, y, x + w, y + h, colorTop, darkenColor(colorTop));
}
void MultiVGradient(float x, float y, float w, float h, const GradientStop *stops, int numStops);

View file

@ -186,8 +186,15 @@ void ManagedTexture::DeviceLost() {
void ManagedTexture::DeviceRestored(Draw::DrawContext *draw) {
INFO_LOG(G3D, "ManagedTexture::DeviceRestored(%s)", filename_.c_str());
_assert_(!texture_);
draw_ = draw;
_dbg_assert_(!texture_);
if (texture_) {
ERROR_LOG(G3D, "ManagedTexture: Unexpected - texture already present: %s", filename_.c_str());
return;
}
// Vulkan: Can't load textures before the first frame has started.
// Should probably try to lift that restriction again someday..
loadPending_ = true;

View file

@ -378,12 +378,14 @@ void TextDrawerSDL::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStrin
font = fallbackFonts_[0];
}
#if SDL_TTF_VERSION_ATLEAST(2, 20, 0)
if (align & ALIGN_HCENTER)
TTF_SetFontWrappedAlign(font, TTF_WRAPPED_ALIGN_CENTER);
else if (align & ALIGN_RIGHT)
TTF_SetFontWrappedAlign(font, TTF_WRAPPED_ALIGN_RIGHT);
else
TTF_SetFontWrappedAlign(font, TTF_WRAPPED_ALIGN_LEFT);
#endif
SDL_Color fgColor = { 0xFF, 0xFF, 0xFF, 0xFF };
SDL_Surface *text = TTF_RenderUTF8_Blended_Wrapped(font, processedStr.c_str(), fgColor, 0);

View file

@ -239,18 +239,15 @@ std::string StringFromFormat(const char* format, ...)
return temp;
}
std::string StringFromInt(int value)
{
std::string StringFromInt(int value) {
char temp[16];
snprintf(temp, sizeof(temp), "%d", value);
return temp;
}
// Turns " hej " into "hej". Also handles tabs.
std::string StripSpaces(const std::string &str)
{
std::string StripSpaces(const std::string &str) {
const size_t s = str.find_first_not_of(" \t\r\n");
if (str.npos != s)
return str.substr(s, str.find_last_not_of(" \t\r\n") - s + 1);
else
@ -268,8 +265,26 @@ std::string StripQuotes(const std::string& s)
return s;
}
void SplitString(const std::string& str, const char delim, std::vector<std::string>& output)
{
// Turns " hej " into "hej". Also handles tabs.
std::string_view StripSpaces(std::string_view str) {
const size_t s = str.find_first_not_of(" \t\r\n");
if (str.npos != s)
return str.substr(s, str.find_last_not_of(" \t\r\n") - s + 1);
else
return "";
}
// "\"hello\"" is turned to "hello"
// This one assumes that the string has already been space stripped in both
// ends, as done by StripSpaces above, for example.
std::string_view StripQuotes(std::string_view s) {
if (s.size() && '\"' == s[0] && '\"' == *s.rbegin())
return s.substr(1, s.size() - 2);
else
return s;
}
void SplitString(std::string_view str, const char delim, std::vector<std::string_view> &output) {
size_t next = 0;
for (size_t pos = 0, len = str.length(); pos < len; ++pos) {
if (str[pos] == delim) {
@ -286,6 +301,23 @@ void SplitString(const std::string& str, const char delim, std::vector<std::stri
}
}
void SplitString(std::string_view str, const char delim, std::vector<std::string> &output) {
size_t next = 0;
for (size_t pos = 0, len = str.length(); pos < len; ++pos) {
if (str[pos] == delim) {
output.emplace_back(str.substr(next, pos - next));
// Skip the delimiter itself.
next = pos + 1;
}
}
if (next == 0) {
output.emplace_back(str);
} else if (next < str.length()) {
output.emplace_back(str.substr(next));
}
}
static std::string ApplyHtmlEscapes(std::string str) {
struct Repl {
const char *a;
@ -305,8 +337,7 @@ static std::string ApplyHtmlEscapes(std::string str) {
}
// Meant for HTML listings and similar, so supports some HTML escapes.
void GetQuotedStrings(const std::string& str, std::vector<std::string>& output)
{
void GetQuotedStrings(const std::string& str, std::vector<std::string> &output) {
size_t next = 0;
bool even = 0;
for (size_t pos = 0, len = str.length(); pos < len; ++pos) {
@ -325,15 +356,13 @@ void GetQuotedStrings(const std::string& str, std::vector<std::string>& output)
}
}
std::string ReplaceAll(std::string result, const std::string& src, const std::string& dest)
{
std::string ReplaceAll(std::string result, const std::string& src, const std::string& dest) {
size_t pos = 0;
if (src == dest)
return result;
while (1)
{
while (true) {
pos = result.find(src, pos);
if (pos == result.npos)
break;
@ -369,7 +398,7 @@ std::string UnescapeMenuString(const char *input, char *shortcutChar) {
return output;
}
std::string ApplySafeSubstitutions(const char *format, const std::string &string1, const std::string &string2, const std::string &string3) {
std::string ApplySafeSubstitutions(const char *format, std::string_view string1, std::string_view string2, std::string_view string3, std::string_view string4) {
size_t formatLen = strlen(format);
std::string output;
output.reserve(formatLen + 20);
@ -392,6 +421,40 @@ std::string ApplySafeSubstitutions(const char *format, const std::string &string
case '3':
output += string3; i++;
break;
case '4':
output += string4; i++;
break;
}
}
return output;
}
std::string ApplySafeSubstitutions(const char *format, int i1, int i2, int i3, int i4) {
size_t formatLen = strlen(format);
std::string output;
output.reserve(formatLen + 20);
for (size_t i = 0; i < formatLen; i++) {
char c = format[i];
if (c != '%') {
output.push_back(c);
continue;
}
if (i >= formatLen - 1) {
break;
}
switch (format[i + 1]) {
case '1':
output += StringFromInt(i1); i++;
break;
case '2':
output += StringFromInt(i2); i++;
break;
case '3':
output += StringFromInt(i3); i++;
break;
case '4':
output += StringFromInt(i4); i++;
break;
}
}
return output;

View file

@ -21,6 +21,7 @@
#include <cstdint>
#include <string>
#include <cstring>
#include <string_view>
#include <vector>
#ifdef _MSC_VER
@ -36,42 +37,36 @@ std::string IndentString(const std::string &str, const std::string &sep, bool sk
// Other simple string utilities.
// Optimized for string constants.
inline bool startsWith(const std::string &str, const char *key) {
size_t keyLen = strlen(key);
if (str.size() < keyLen)
inline bool startsWith(std::string_view str, std::string_view key) {
if (str.size() < key.size())
return false;
return !memcmp(str.data(), key, keyLen);
return !memcmp(str.data(), key.data(), key.size());
}
inline bool startsWith(const std::string &str, const std::string &what) {
if (str.size() < what.size())
return false;
return str.substr(0, what.size()) == what;
}
inline bool endsWith(const std::string &str, const std::string &what) {
inline bool endsWith(std::string_view str, std::string_view what) {
if (str.size() < what.size())
return false;
return str.substr(str.size() - what.size()) == what;
}
// Only use on strings where you're only concerned about ASCII.
inline bool startsWithNoCase(const std::string &str, const std::string &what) {
if (str.size() < what.size())
inline bool startsWithNoCase(std::string_view str, std::string_view key) {
if (str.size() < key.size())
return false;
return strncasecmp(str.c_str(), what.c_str(), what.size()) == 0;
return strncasecmp(str.data(), key.data(), key.size()) == 0;
}
inline bool endsWithNoCase(const std::string &str, const std::string &what) {
if (str.size() < what.size())
inline bool endsWithNoCase(std::string_view str, std::string_view key) {
if (str.size() < key.size())
return false;
const size_t offset = str.size() - what.size();
return strncasecmp(str.c_str() + offset, what.c_str(), what.size()) == 0;
const size_t offset = str.size() - key.size();
return strncasecmp(str.data() + offset, key.data(), key.size()) == 0;
}
inline bool equalsNoCase(const std::string &str, const char *what) {
return strcasecmp(str.c_str(), what) == 0;
inline bool equalsNoCase(std::string_view str, std::string_view key) {
if (str.size() != key.size())
return false;
return strncasecmp(str.data(), key.data(), key.size()) == 0;
}
void DataToHexString(const uint8_t *data, size_t size, std::string *output);
@ -83,7 +78,12 @@ std::string StringFromInt(int value);
std::string StripSpaces(const std::string &s);
std::string StripQuotes(const std::string &s);
void SplitString(const std::string& str, const char delim, std::vector<std::string>& output);
std::string_view StripSpaces(std::string_view s);
std::string_view StripQuotes(std::string_view s);
// TODO: Make this a lot more efficient by outputting string_views.
void SplitString(std::string_view str, const char delim, std::vector<std::string_view> &output);
void SplitString(std::string_view str, const char delim, std::vector<std::string> &output);
void GetQuotedStrings(const std::string& str, std::vector<std::string>& output);
@ -123,4 +123,6 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _
// Replaces %1, %2, %3 in format with arg1, arg2, arg3.
// Much safer than snprintf and friends.
std::string ApplySafeSubstitutions(const char *format, const std::string &string1, const std::string &string2 = "", const std::string &string3 = "");
// For mixes of strings and ints, manually convert the ints to strings.
std::string ApplySafeSubstitutions(const char *format, std::string_view string1, std::string_view string2 = "", std::string_view string3 = "", std::string_view string4 = "");
std::string ApplySafeSubstitutions(const char *format, int i1, int i2 = 0, int i3 = 0, int i4 = 0);

View file

@ -55,6 +55,7 @@ bool NativeIsRestarting();
void NativeTouch(const TouchInput &touch);
bool NativeKey(const KeyInput &key);
void NativeAxis(const AxisInput *axis, size_t count);
void NativeAccelerometer(float tiltX, float tiltY, float tiltZ);
// Called when it's process a frame, including rendering. If the device can keep up, this
// will be called sixty times per second. Main thread.

View file

@ -130,6 +130,7 @@ enum SystemProperty {
SYSPROP_HAS_IMAGE_BROWSER,
SYSPROP_HAS_BACK_BUTTON,
SYSPROP_HAS_KEYBOARD,
SYSPROP_HAS_ACCELEROMETER, // Used to enable/disable tilt input settings
SYSPROP_HAS_OPEN_DIRECTORY,
SYSPROP_HAS_LOGIN_DIALOG,
SYSPROP_HAS_TEXT_INPUT_DIALOG, // Indicates that System_InputBoxGetString is available.
@ -139,6 +140,8 @@ enum SystemProperty {
SYSPROP_SUPPORTS_HTTPS,
SYSPROP_DEBUGGER_PRESENT,
// Available as Int:
SYSPROP_SYSTEMVERSION,
SYSPROP_DISPLAY_XRES,
@ -207,6 +210,40 @@ enum class SystemNotification {
ACTIVITY,
};
// I guess it's not super great architecturally to centralize this, since it's not general - but same with a lot of
// the other stuff, and this is only used by PPSSPP, so... better this than ugly strings.
enum class UIMessage {
PERMISSION_GRANTED,
POWER_SAVING,
RECREATE_VIEWS,
CONFIG_LOADED,
REQUEST_GAME_BOOT,
REQUEST_GAME_RUN, // or continue?
REQUEST_GAME_PAUSE,
REQUEST_GAME_RESET,
REQUEST_GAME_STOP,
SHOW_CONTROL_MAPPING,
SHOW_CHAT_SCREEN,
SHOW_DISPLAY_LAYOUT_EDITOR,
SHOW_SETTINGS,
SHOW_LANGUAGE_SCREEN,
REQUEST_GPU_DUMP_NEXT_FRAME,
REQUEST_CLEAR_JIT,
APP_RESUMED,
REQUEST_PLAY_SOUND,
WINDOW_MINIMIZED,
LOST_FOCUS,
GOT_FOCUS,
GPU_CONFIG_CHANGED,
GPU_RENDER_RESIZED,
GPU_DISPLAY_RESIZED,
POSTSHADER_UPDATED,
ACHIEVEMENT_LOGIN_STATE_CHANGE,
SAVESTATE_DISPLAY_SLOT,
GAMESETTINGS_SEARCH,
SAVEDATA_SEARCH,
};
std::string System_GetProperty(SystemProperty prop);
std::vector<std::string> System_GetPropertyStringVec(SystemProperty prop);
int System_GetPropertyInt(SystemProperty prop);
@ -221,7 +258,7 @@ bool System_AudioRecordingIsAvailable();
bool System_AudioRecordingState();
// This will be changed to take an enum. Replacement for the old NativeMessageReceived.
void System_PostUIMessage(const std::string &message, const std::string &param);
void System_PostUIMessage(UIMessage message, const std::string &param = "");
// For these functions, most platforms will use the implementation provided in UI/AudioCommon.cpp,
// no need to implement separately.

View file

@ -45,6 +45,7 @@ public:
template<class T>
class Promise {
public:
// Never fails.
static Promise<T> *Spawn(ThreadManager *threadman, std::function<T()> fun, TaskType taskType, TaskPriority taskPriority = TaskPriority::NORMAL) {
Mailbox<T> *mailbox = new Mailbox<T>();

View file

@ -68,6 +68,20 @@ double from_time_raw_relative(uint64_t raw_time) {
return from_time_raw(raw_time);
}
double time_now_unix_utc() {
const int64_t UNIX_TIME_START = 0x019DB1DED53E8000; //January 1, 1970 (start of Unix epoch) in "ticks"
const double TICKS_PER_SECOND = 10000000; //a tick is 100ns
FILETIME ft;
GetSystemTimeAsFileTime(&ft); //returns ticks in UTC
// Copy the low and high parts of FILETIME into a LARGE_INTEGER
LARGE_INTEGER li;
li.LowPart = ft.dwLowDateTime;
li.HighPart = ft.dwHighDateTime;
//Convert ticks since 1/1/1970 into seconds
return (double)(li.QuadPart - UNIX_TIME_START) / TICKS_PER_SECOND;
}
void yield() {
YieldProcessor();
}
@ -99,6 +113,12 @@ double from_time_raw_relative(uint64_t raw_time) {
return (double)raw_time * (1.0 / nanos);
}
double time_now_unix_utc() {
struct timespec tp;
clock_gettime(CLOCK_REALTIME, &tp);
return tp.tv_sec * 1000000000ULL + tp.tv_nsec;
}
void yield() {
#if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
_mm_pause();
@ -120,9 +140,14 @@ double time_now_d() {
return (double)(tv.tv_sec - start) + (double)tv.tv_usec * (1.0 / micros);
}
// Fake, but usable in a pinch. Don't, though.
uint64_t time_now_raw() {
return (uint64_t)(time_now_d() * nanos);
static time_t start;
struct timeval tv;
gettimeofday(&tv, nullptr);
if (start == 0) {
start = tv.tv_sec;
}
return (double)tv.tv_sec + (double)tv.tv_usec * (1.0 / micros);
}
double from_time_raw(uint64_t raw_time) {
@ -135,6 +160,10 @@ double from_time_raw_relative(uint64_t raw_time) {
void yield() {}
double time_now_unix_utc() {
return time_now_raw();
}
#endif
void sleep_ms(int ms) {

View file

@ -13,6 +13,9 @@ uint64_t time_now_raw();
double from_time_raw(uint64_t raw_time);
double from_time_raw_relative(uint64_t raw_time);
// Seconds, Unix UTC time
double time_now_unix_utc();
// Sleep. Does not necessarily have millisecond granularity, especially on Windows.
void sleep_ms(int ms);

View file

@ -44,6 +44,7 @@ void IconCache::SaveToFile(FILE *file) {
for (auto &iter : cache_) {
DiskCacheEntry entryHeader;
memset(&entryHeader, 0, sizeof(entryHeader)); // valgrind complains about padding bytes
entryHeader.keyLen = (uint32_t)iter.first.size();
entryHeader.dataLen = (uint32_t)iter.second.data.size();
entryHeader.format = iter.second.format;
@ -228,7 +229,7 @@ bool IconCache::MarkPending(const std::string &key) {
return true;
}
void IconCache::Cancel(const std::string &key) {
void IconCache::CancelPending(const std::string &key) {
std::unique_lock<std::mutex> lock(lock_);
pending_.erase(key);
}
@ -266,6 +267,7 @@ Draw::Texture *IconCache::BindIconTexture(UIContext *context, const std::string
return nullptr;
}
// TODO: Cut down on how long we're holding this lock here.
std::unique_lock<std::mutex> lock(lock_);
auto iter = cache_.find(key);
if (iter == cache_.end()) {

View file

@ -36,7 +36,7 @@ public:
// It's okay to call these from any thread.
bool MarkPending(const std::string &key); // returns false if already pending or loaded
void Cancel(const std::string &key);
void CancelPending(const std::string &key);
bool InsertIcon(const std::string &key, IconFormat format, std::string &&pngData);
bool GetDimensions(const std::string &key, int *width, int *height);
bool Contains(const std::string &key);

View file

@ -17,9 +17,9 @@ void MessagePopupScreen::CreatePopupContents(UI::ViewGroup *parent) {
using namespace UI;
UIContext &dc = *screenManager()->getUIContext();
std::vector<std::string> messageLines;
std::vector<std::string_view> messageLines;
SplitString(message_, '\n', messageLines);
for (const auto &lineOfText : messageLines)
for (auto lineOfText : messageLines)
parent->Add(new UI::TextView(lineOfText, ALIGN_LEFT | ALIGN_VCENTER, false))->SetTextColor(dc.theme->popupStyle.fgColor);
}
@ -122,7 +122,11 @@ void PopupMultiChoice::UpdateText() {
if (index < 0 || index >= numChoices_) {
valueText_ = "(invalid choice)"; // Shouldn't happen. Should be no need to translate this.
} else {
valueText_ = T(category_, choices_[index]);
if (choices_[index]) {
valueText_ = T(category_, choices_[index]);
} else {
valueText_ = "";
}
}
}
@ -163,12 +167,15 @@ PopupSliderChoice::PopupSliderChoice(int *value, int minValue, int maxValue, int
PopupSliderChoiceFloat::PopupSliderChoiceFloat(float *value, float minValue, float maxValue, float defaultValue, const std::string &text, ScreenManager *screenManager, const std::string &units, LayoutParams *layoutParams)
: AbstractChoiceWithValueDisplay(text, layoutParams), value_(value), minValue_(minValue), maxValue_(maxValue), defaultValue_(defaultValue), step_(1.0f), units_(units), screenManager_(screenManager) {
_dbg_assert_(maxValue > minValue);
fmt_ = "%2.2f";
OnClick.Handle(this, &PopupSliderChoiceFloat::HandleClick);
}
PopupSliderChoiceFloat::PopupSliderChoiceFloat(float *value, float minValue, float maxValue, float defaultValue, const std::string &text, float step, ScreenManager *screenManager, const std::string &units, LayoutParams *layoutParams)
: AbstractChoiceWithValueDisplay(text, layoutParams), value_(value), minValue_(minValue), maxValue_(maxValue), defaultValue_(defaultValue), step_(step), units_(units), screenManager_(screenManager) {
_dbg_assert_(step > 0.0f);
_dbg_assert_(maxValue > minValue);
fmt_ = "%2.2f";
OnClick.Handle(this, &PopupSliderChoiceFloat::HandleClick);
}

View file

@ -1,5 +1,3 @@
#include <atomic>
#include <mutex>
#include <deque>
#include "ppsspp_config.h"
@ -12,14 +10,12 @@
namespace UI {
static std::mutex focusLock;
static std::vector<int> focusMoves;
extern bool focusForced;
static View *focusedView;
static bool focusMovementEnabled;
bool focusForced;
static std::mutex eventMutex_;
static std::function<void(UISound, float)> soundCallback;
static bool soundEnabled = true;
@ -29,29 +25,20 @@ struct DispatchQueueItem {
EventParams params;
};
std::atomic<bool> hasDispatchQueue;
std::deque<DispatchQueueItem> g_dispatchQueue;
void EventTriggered(Event *e, EventParams params) {
DispatchQueueItem item{ e, params };
std::unique_lock<std::mutex> guard(eventMutex_);
// Set before adding so we lock and check the added value.
hasDispatchQueue = true;
g_dispatchQueue.push_front(item);
}
void DispatchEvents() {
while (hasDispatchQueue) {
while (!g_dispatchQueue.empty()) {
DispatchQueueItem item;
{
std::unique_lock<std::mutex> guard(eventMutex_);
if (g_dispatchQueue.empty())
break;
item = g_dispatchQueue.back();
g_dispatchQueue.pop_back();
hasDispatchQueue = !g_dispatchQueue.empty();
}
if (g_dispatchQueue.empty())
break;
item = g_dispatchQueue.back();
g_dispatchQueue.pop_back();
if (item.e) {
item.e->Dispatch(item.params);
}
@ -59,9 +46,6 @@ void DispatchEvents() {
}
void RemoveQueuedEventsByView(View *view) {
if (!hasDispatchQueue)
return;
std::unique_lock<std::mutex> guard(eventMutex_);
for (auto it = g_dispatchQueue.begin(); it != g_dispatchQueue.end(); ) {
if (it->params.v == view) {
it = g_dispatchQueue.erase(it);
@ -72,9 +56,6 @@ void RemoveQueuedEventsByView(View *view) {
}
void RemoveQueuedEventsByEvent(Event *event) {
if (!hasDispatchQueue)
return;
std::unique_lock<std::mutex> guard(eventMutex_);
for (auto it = g_dispatchQueue.begin(); it != g_dispatchQueue.end(); ) {
if (it->e == event) {
it = g_dispatchQueue.erase(it);
@ -214,7 +195,6 @@ static KeyEventResult KeyEventToFocusMoves(const KeyInput &key) {
hk.deviceId = key.deviceId;
hk.triggerTime = time_now_d() + repeatDelay;
std::lock_guard<std::mutex> lock(focusLock);
// Check if the key is already held. If it is, ignore it. This is to avoid
// multiple key repeat mechanisms colliding.
if (heldKeys.find(hk) != heldKeys.end()) {
@ -381,7 +361,6 @@ restart:
key.flags = KEY_DOWN;
KeyEvent(key, root);
std::lock_guard<std::mutex> lock(focusLock);
focusMoves.push_back(key.keyCode);
// Cannot modify the current item when looping over a set, so let's do this instead.
@ -404,7 +383,6 @@ void UpdateViewHierarchy(ViewGroup *root) {
}
if (focusMoves.size()) {
std::lock_guard<std::mutex> lock(focusLock);
EnableFocusMovement(true);
if (!GetFocusedView()) {
// Find a view to focus.

View file

@ -13,6 +13,15 @@
#include "Core/KeyMap.h"
void Screen::focusChanged(ScreenFocusChange focusChange) {
char *eventName = "";
switch (focusChange) {
case ScreenFocusChange::FOCUS_LOST_TOP: eventName = "FOCUS_LOST_TOP"; break;
case ScreenFocusChange::FOCUS_BECAME_TOP: eventName = "FOCUS_BECAME_TOP"; break;
}
DEBUG_LOG(SYSTEM, "Screen %s got %s", this->tag(), eventName);
}
ScreenManager::~ScreenManager() {
shutdown();
}
@ -68,14 +77,17 @@ void ScreenManager::switchToNext() {
Layer temp = {nullptr, 0};
if (!stack_.empty()) {
temp = stack_.back();
temp.screen->focusChanged(ScreenFocusChange::FOCUS_LOST_TOP);
stack_.pop_back();
}
stack_.push_back(nextStack_.front());
nextStack_.front().screen->focusChanged(ScreenFocusChange::FOCUS_BECAME_TOP);
if (temp.screen) {
delete temp.screen;
}
UI::SetFocusedView(nullptr);
// When will this ever happen? Should handle focus here too?
for (size_t i = 1; i < nextStack_.size(); ++i) {
stack_.push_back(nextStack_[i]);
}
@ -117,26 +129,10 @@ bool ScreenManager::key(const KeyInput &key) {
return result;
}
void ScreenManager::axis(const AxisInput &axis) {
void ScreenManager::axis(const AxisInput *axes, size_t count) {
std::lock_guard<std::recursive_mutex> guard(inputLock_);
// Ignore duplicate values to prevent axis values overwriting each other.
uint64_t key = ((uint64_t)axis.axisId << 32) | axis.deviceId;
// Center value far from zero just to ensure we send the first zero.
// PSP games can't see higher resolution than this.
int value = 128 + ceilf(axis.value * 127.5f + 127.5f);
if (lastAxis_[key] == value) {
return;
}
lastAxis_[key] = value;
// Send center axis to every screen layer.
if (axis.value == 0) {
for (auto &layer : stack_) {
layer.screen->UnsyncAxis(axis);
}
} else if (!stack_.empty()) {
stack_.back().screen->UnsyncAxis(axis);
if (!stack_.empty()) {
stack_.back().screen->UnsyncAxis(axes, count);
}
}
@ -226,10 +222,10 @@ void ScreenManager::getFocusPosition(float &x, float &y, float &z) {
z = stack_.size();
}
void ScreenManager::sendMessage(const char *msg, const char *value) {
if (!strcmp(msg, "recreateviews"))
void ScreenManager::sendMessage(UIMessage message, const char *value) {
if (message == UIMessage::RECREATE_VIEWS) {
RecreateAllViews();
if (!strcmp(msg, "lost_focus")) {
} else if (message == UIMessage::LOST_FOCUS) {
TouchInput input{};
input.x = -50000.0f;
input.y = -50000.0f;
@ -238,8 +234,9 @@ void ScreenManager::sendMessage(const char *msg, const char *value) {
input.id = 0;
touch(input);
}
if (!stack_.empty())
stack_.back().screen->sendMessage(msg, value);
stack_.back().screen->sendMessage(message, value);
}
Screen *ScreenManager::topScreen() const {
@ -279,17 +276,30 @@ void ScreenManager::push(Screen *screen, int layerFlags) {
touch(input);
Layer layer = {screen, layerFlags};
if (nextStack_.empty())
if (!stack_.empty()) {
stack_.back().screen->focusChanged(ScreenFocusChange::FOCUS_LOST_TOP);
}
if (nextStack_.empty()) {
layer.screen->focusChanged(ScreenFocusChange::FOCUS_BECAME_TOP);
stack_.push_back(layer);
else
} else {
nextStack_.push_back(layer);
}
}
void ScreenManager::pop() {
std::lock_guard<std::recursive_mutex> guard(inputLock_);
if (stack_.size()) {
if (!stack_.empty()) {
stack_.back().screen->focusChanged(ScreenFocusChange::FOCUS_LOST_TOP);
delete stack_.back().screen;
stack_.pop_back();
if (!stack_.empty()) {
stack_.back().screen->focusChanged(ScreenFocusChange::FOCUS_LOST_TOP);
}
} else {
ERROR_LOG(SYSTEM, "Can't pop when stack empty");
}
@ -333,12 +343,19 @@ void ScreenManager::processFinishDialog() {
std::lock_guard<std::recursive_mutex> guard(inputLock_);
// Another dialog may have been pushed before the render, so search for it.
Screen *caller = dialogParent(dialogFinished_);
bool erased = false;
for (size_t i = 0; i < stack_.size(); ++i) {
if (stack_[i].screen == dialogFinished_) {
stack_[i].screen->focusChanged(ScreenFocusChange::FOCUS_LOST_TOP);
stack_.erase(stack_.begin() + i);
erased = true;
}
}
if (erased && !stack_.empty()) {
stack_.back().screen->focusChanged(ScreenFocusChange::FOCUS_BECAME_TOP);
}
if (!caller) {
ERROR_LOG(SYSTEM, "ERROR: no top screen when finishing dialog");
} else if (caller != topScreen()) {

View file

@ -21,6 +21,7 @@
#include "Common/Common.h"
#include "Common/Input/InputState.h"
#include "Common/System/System.h"
namespace UI {
class View;
@ -41,6 +42,11 @@ namespace Draw {
class DrawContext;
}
enum class ScreenFocusChange {
FOCUS_LOST_TOP, // Another screen was pushed on top
FOCUS_BECAME_TOP, // Became the top screen again
};
class Screen {
public:
Screen() : screenManager_(nullptr) { }
@ -55,15 +61,17 @@ public:
virtual void postRender() {}
virtual void resized() {}
virtual void dialogFinished(const Screen *dialog, DialogResult result) {}
virtual void sendMessage(const char *msg, const char *value) {}
virtual void sendMessage(UIMessage message, const char *value) {}
virtual void deviceLost() {}
virtual void deviceRestored() {}
virtual void focusChanged(ScreenFocusChange focusChange);
// Return value of UnsyncTouch is only used to let the overlay screen block touches.
virtual bool UnsyncTouch(const TouchInput &touch) = 0;
// Return value of UnsyncKey is used to not block certain system keys like volume when unhandled, on Android.
virtual bool UnsyncKey(const KeyInput &touch) = 0;
virtual void UnsyncAxis(const AxisInput &touch) = 0;
virtual void UnsyncAxis(const AxisInput *axes, size_t count) = 0;
virtual void RecreateViews() {}
@ -135,10 +143,10 @@ public:
// Instant touch, separate from the update() mechanism.
void touch(const TouchInput &touch);
bool key(const KeyInput &key);
void axis(const AxisInput &touch);
void axis(const AxisInput *axes, size_t count);
// Generic facility for gross hacks :P
void sendMessage(const char *msg, const char *value);
void sendMessage(UIMessage message, const char *value);
Screen *topScreen() const;

View file

@ -99,12 +99,14 @@ bool UIScreen::UnsyncTouch(const TouchInput &touch) {
return false;
}
void UIScreen::UnsyncAxis(const AxisInput &axis) {
void UIScreen::UnsyncAxis(const AxisInput *axes, size_t count) {
QueuedEvent ev{};
ev.type = QueuedEventType::AXIS;
ev.axis = axis;
std::lock_guard<std::mutex> guard(eventQueueLock_);
eventQueue_.push_back(ev);
for (size_t i = 0; i < count; i++) {
ev.axis = axes[i];
eventQueue_.push_back(ev);
}
}
bool UIScreen::UnsyncKey(const KeyInput &key) {
@ -267,10 +269,10 @@ bool UIDialogScreen::key(const KeyInput &key) {
return retval;
}
void UIDialogScreen::sendMessage(const char *msg, const char *value) {
void UIDialogScreen::sendMessage(UIMessage message, const char *value) {
Screen *screen = screenManager()->dialogParent(this);
if (screen) {
screen->sendMessage(msg, value);
screen->sendMessage(message, value);
}
}

View file

@ -48,7 +48,7 @@ public:
bool UnsyncTouch(const TouchInput &touch) override;
bool UnsyncKey(const KeyInput &key) override;
void UnsyncAxis(const AxisInput &axis) override;
void UnsyncAxis(const AxisInput *axes, size_t count) override;
TouchInput transformTouch(const TouchInput &touch) override;
@ -88,7 +88,7 @@ class UIDialogScreen : public UIScreen {
public:
UIDialogScreen() : UIScreen(), finished_(false) {}
bool key(const KeyInput &key) override;
void sendMessage(const char *msg, const char *value) override;
void sendMessage(UIMessage message, const char *value) override;
private:
bool finished_;

View file

@ -307,6 +307,21 @@ bool IsEscapeKey(const KeyInput &key) {
}
}
// Corresponds to Triangle
bool IsInfoKey(const KeyInput &key) {
if (infoKeys.empty()) {
// This path is pretty much not used, infoKeys should be set.
// TODO: Get rid of this stuff?
if (key.deviceId == DEVICE_ID_KEYBOARD) {
return key.keyCode == NKCODE_S || key.keyCode == NKCODE_NUMPAD_ADD;
} else {
return key.keyCode == NKCODE_BUTTON_Y || key.keyCode == NKCODE_BUTTON_3;
}
} else {
return MatchesKeyDef(infoKeys, key);
}
}
bool IsTabLeftKey(const KeyInput &key) {
if (tabLeftKeys.empty()) {
// This path is pretty much not used, tabLeftKeys should be set.
@ -1557,6 +1572,9 @@ bool SliderFloat::ApplyKey(InputKeyCode keyCode) {
default:
return false;
}
_dbg_assert_(!my_isnanorinf(*value_));
EventParams params{};
params.v = this;
params.a = (uint32_t)(*value_);
@ -1584,6 +1602,7 @@ bool SliderFloat::Touch(const TouchInput &input) {
}
void SliderFloat::Clamp() {
_dbg_assert_(!my_isnanorinf(*value_));
if (*value_ < minValue_)
*value_ = minValue_;
else if (*value_ > maxValue_)

View file

@ -927,10 +927,10 @@ private:
class TextView : public InertView {
public:
TextView(const std::string &text, LayoutParams *layoutParams = 0)
TextView(std::string_view text, LayoutParams *layoutParams = 0)
: InertView(layoutParams), text_(text), textAlign_(0), textColor_(0xFFFFFFFF), small_(false), shadow_(false), focusable_(false), clip_(true) {}
TextView(const std::string &text, int textAlign, bool small, LayoutParams *layoutParams = 0)
TextView(std::string_view text, int textAlign, bool small, LayoutParams *layoutParams = 0)
: InertView(layoutParams), text_(text), textAlign_(textAlign), textColor_(0xFFFFFFFF), small_(small), shadow_(false), focusable_(false), clip_(true) {}
void GetContentDimensionsBySpec(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert, float &w, float &h) const override;
@ -1066,6 +1066,7 @@ void ApplyBoundsBySpec(Bounds &bounds, MeasureSpec horiz, MeasureSpec vert);
bool IsDPadKey(const KeyInput &key);
bool IsAcceptKey(const KeyInput &key);
bool IsEscapeKey(const KeyInput &key);
bool IsInfoKey(const KeyInput &key);
bool IsTabLeftKey(const KeyInput &key);
bool IsTabRightKey(const KeyInput &key);

View file

@ -47,12 +47,12 @@ ViewGroup::~ViewGroup() {
Clear();
}
void ViewGroup::RemoveSubview(View *view) {
std::lock_guard<std::mutex> guard(modifyLock_);
void ViewGroup::RemoveSubview(View *subView) {
// loop counter needed, so can't convert loop.
for (size_t i = 0; i < views_.size(); i++) {
if (views_[i] == view) {
if (views_[i] == subView) {
views_.erase(views_.begin() + i);
delete view;
delete subView;
return;
}
}
@ -67,17 +67,13 @@ bool ViewGroup::ContainsSubview(const View *view) const {
}
void ViewGroup::Clear() {
std::lock_guard<std::mutex> guard(modifyLock_);
for (size_t i = 0; i < views_.size(); i++) {
delete views_[i];
views_[i] = nullptr;
for (View *view : views_) {
delete view;
}
views_.clear();
}
void ViewGroup::PersistData(PersistStatus status, std::string anonId, PersistMap &storage) {
std::lock_guard<std::mutex> guard(modifyLock_);
std::string tag = Tag();
if (tag.empty()) {
tag = anonId;
@ -89,12 +85,11 @@ void ViewGroup::PersistData(PersistStatus status, std::string anonId, PersistMap
}
bool ViewGroup::Touch(const TouchInput &input) {
std::lock_guard<std::mutex> guard(modifyLock_);
bool any = false;
for (auto iter = views_.begin(); iter != views_.end(); ++iter) {
for (View *view : views_) {
// TODO: If there is a transformation active, transform input coordinates accordingly.
if ((*iter)->GetVisibility() == V_VISIBLE) {
bool touch = (*iter)->Touch(input);
if (view->GetVisibility() == V_VISIBLE) {
bool touch = view->Touch(input);
any = any || touch;
if (exclusiveTouch_ && touch && (input.flags & TOUCH_DOWN)) {
break;
@ -111,43 +106,39 @@ bool ViewGroup::Touch(const TouchInput &input) {
void ViewGroup::Query(float x, float y, std::vector<View *> &list) {
if (bounds_.Contains(x, y)) {
list.push_back(this);
for (auto iter = views_.begin(); iter != views_.end(); ++iter) {
(*iter)->Query(x, y, list);
for (View *view : views_) {
view->Query(x, y, list);
}
}
}
bool ViewGroup::Key(const KeyInput &input) {
std::lock_guard<std::mutex> guard(modifyLock_);
bool ret = false;
for (auto iter = views_.begin(); iter != views_.end(); ++iter) {
for (View *view : views_) {
// TODO: If there is a transformation active, transform input coordinates accordingly.
if ((*iter)->GetVisibility() == V_VISIBLE)
ret = ret || (*iter)->Key(input);
if (view->GetVisibility() == V_VISIBLE)
ret = ret || view->Key(input);
}
return ret;
}
void ViewGroup::Axis(const AxisInput &input) {
std::lock_guard<std::mutex> guard(modifyLock_);
for (auto iter = views_.begin(); iter != views_.end(); ++iter) {
for (View *view : views_) {
// TODO: If there is a transformation active, transform input coordinates accordingly.
if ((*iter)->GetVisibility() == V_VISIBLE)
(*iter)->Axis(input);
if (view->GetVisibility() == V_VISIBLE)
view->Axis(input);
}
}
void ViewGroup::DeviceLost() {
std::lock_guard<std::mutex> guard(modifyLock_);
for (auto iter = views_.begin(); iter != views_.end(); ++iter) {
(*iter)->DeviceLost();
for (View *view : views_) {
view->DeviceLost();
}
}
void ViewGroup::DeviceRestored(Draw::DrawContext *draw) {
std::lock_guard<std::mutex> guard(modifyLock_);
for (auto iter = views_.begin(); iter != views_.end(); ++iter) {
(*iter)->DeviceRestored(draw);
for (View *view : views_) {
view->DeviceRestored(draw);
}
}
@ -245,21 +236,18 @@ void ViewGroup::Update() {
}
bool ViewGroup::SetFocus() {
std::lock_guard<std::mutex> guard(modifyLock_);
if (!CanBeFocused() && !views_.empty()) {
for (size_t i = 0; i < views_.size(); i++) {
if (views_[i]->SetFocus())
for (View *view : views_) {
if (view->SetFocus())
return true;
}
}
return false;
}
bool ViewGroup::SubviewFocused(View *view) {
for (size_t i = 0; i < views_.size(); i++) {
if (views_[i] == view)
return true;
if (views_[i]->SubviewFocused(view))
bool ViewGroup::SubviewFocused(View *queryView) {
for (View *view : views_) {
if (view == queryView || view->SubviewFocused(queryView))
return true;
}
return false;

View file

@ -45,7 +45,6 @@ public:
// Takes ownership! DO NOT add a view to multiple parents!
template <class T>
T *Add(T *view) {
std::lock_guard<std::mutex> guard(modifyLock_);
views_.push_back(view);
return view;
}
@ -76,9 +75,6 @@ public:
void SetExclusiveTouch(bool exclusive) { exclusiveTouch_ = exclusive; }
void SetClickableBackground(bool clickableBackground) { clickableBackground_ = clickableBackground; }
void Lock() { modifyLock_.lock(); }
void Unlock() { modifyLock_.unlock(); }
void SetClip(bool clip) { clip_ = clip; }
std::string DescribeLog() const override { return "ViewGroup: " + View::DescribeLog(); }
std::string DescribeText() const override;
@ -87,7 +83,6 @@ protected:
std::string DescribeListUnordered(const char *heading) const;
std::string DescribeListOrdered(const char *heading) const;
std::mutex modifyLock_; // Hold this when changing the subviews.
std::vector<View *> views_;
View *defaultFocusView_ = nullptr;
Drawable bg_;

View file

@ -26,13 +26,6 @@
#include "Core/KeyMap.h"
#include "Core/System.h"
enum VRMatrix {
VR_PROJECTION_MATRIX,
VR_VIEW_MATRIX_LEFT_EYE,
VR_VIEW_MATRIX_RIGHT_EYE,
VR_MATRIX_COUNT
};
enum VRMirroring {
VR_MIRRORING_AXIS_X,
VR_MIRRORING_AXIS_Y,
@ -51,9 +44,10 @@ static int vr3DGeometryCount = 0;
static long vrCompat[VR_COMPAT_MAX];
static bool vrFlatForced = false;
static bool vrFlatGame = false;
static float vrMatrix[VR_MATRIX_COUNT][16];
static double vrFov[2] = {};
static bool vrMirroring[VR_MIRRORING_COUNT];
static int vrMirroringVariant = 0;
static float vrViewMatrix[2][16];
static XrView vrView[2];
static void (*cbNativeAxis)(const AxisInput *axis, size_t count);
@ -473,11 +467,14 @@ void UpdateVRInput(bool haptics, float dp_xscale, float dp_yscale) {
}
}
bool UpdateVRAxis(const AxisInput &axis) {
if (pspAxis.find(axis.deviceId) == pspAxis.end()) {
pspAxis[axis.deviceId] = std::map<int, float>();
bool UpdateVRAxis(const AxisInput *axes, size_t count) {
for (size_t i = 0; i < count; i++) {
const AxisInput &axis = axes[i];
if (pspAxis.find(axis.deviceId) == pspAxis.end()) {
pspAxis[axis.deviceId] = std::map<int, float>();
}
pspAxis[axis.deviceId][axis.axisId] = axis.value;
}
pspAxis[axis.deviceId][axis.axisId] = axis.value;
return !pspKeys[VIRTKEY_VR_CAMERA_ADJUST];
}
@ -639,28 +636,16 @@ bool StartVRRender() {
}
UpdateVRViewMatrices();
// Update projection matrix
// Calculate field of view
XrFovf fov = {};
for (int eye = 0; eye < ovrMaxNumEyes; eye++) {
fov.angleLeft += vrView[eye].fov.angleLeft / 2.0f;
fov.angleRight += vrView[eye].fov.angleRight / 2.0f;
fov.angleUp += vrView[eye].fov.angleUp / 2.0f;
fov.angleDown += vrView[eye].fov.angleDown / 2.0f;
for (auto & eye : vrView) {
fov.angleLeft += eye.fov.angleLeft / 2.0f;
fov.angleRight += eye.fov.angleRight / 2.0f;
fov.angleUp += eye.fov.angleUp / 2.0f;
fov.angleDown += eye.fov.angleDown / 2.0f;
}
float nearZ = g_Config.fFieldOfViewPercentage / 200.0f;
float tanAngleLeft = tanf(fov.angleLeft);
float tanAngleRight = tanf(fov.angleRight);
float tanAngleDown = tanf(fov.angleDown);
float tanAngleUp = tanf(fov.angleUp);
float M[16] = {};
M[0] = 2 / (tanAngleRight - tanAngleLeft);
M[2] = (tanAngleRight + tanAngleLeft) / (tanAngleRight - tanAngleLeft);
M[5] = 2 / (tanAngleUp - tanAngleDown);
M[6] = (tanAngleUp + tanAngleDown) / (tanAngleUp - tanAngleDown);
M[10] = -1;
M[11] = -(nearZ + nearZ);
M[14] = -1;
memcpy(vrMatrix[VR_PROJECTION_MATRIX], M, sizeof(float) * 16);
vrFov[0] = 2.0 / (tan(fov.angleRight) - tan(fov.angleLeft));
vrFov[1] = 2.0 / (tan(fov.angleUp) - tan(fov.angleDown));
// Decide if the scene is 3D or not
VR_SetConfigFloat(VR_CONFIG_CANVAS_ASPECT, 480.0f / 272.0f);
@ -805,19 +790,16 @@ void UpdateVRParams(float* projMatrix) {
}
void UpdateVRProjection(float* projMatrix, float* leftEye, float* rightEye) {
float* hmdProjection = vrMatrix[VR_PROJECTION_MATRIX];
for (int i = 0; i < 16; i++) {
if ((hmdProjection[i] > 0) != (projMatrix[i] > 0)) {
hmdProjection[i] *= -1.0f;
}
}
float hmdProjection[16];
memcpy(hmdProjection, projMatrix, 16 * sizeof(float));
hmdProjection[0] = vrFov[0];
hmdProjection[5] = vrFov[1];
memcpy(leftEye, hmdProjection, 16 * sizeof(float));
memcpy(rightEye, hmdProjection, 16 * sizeof(float));
}
void UpdateVRView(float* leftEye, float* rightEye) {
float* dst[] = {leftEye, rightEye};
float* matrix[] = {vrMatrix[VR_VIEW_MATRIX_LEFT_EYE], vrMatrix[VR_VIEW_MATRIX_RIGHT_EYE]};
for (int index = 0; index < 2; index++) {
// Validate the view matrix
@ -831,7 +813,7 @@ void UpdateVRView(float* leftEye, float* rightEye) {
// Get view matrix from the headset
Lin::Matrix4x4 hmdView = {};
memcpy(hmdView.m, matrix[index], 16 * sizeof(float));
memcpy(hmdView.m, vrViewMatrix[index], 16 * sizeof(float));
// Combine the matrices
Lin::Matrix4x4 renderView = hmdView * gameView;
@ -940,12 +922,12 @@ void UpdateVRViewMatrices() {
M[11] += side.z;
}
for (int matrix = VR_VIEW_MATRIX_LEFT_EYE; matrix <= VR_VIEW_MATRIX_RIGHT_EYE; matrix++) {
for (int eye = 0; eye < ovrMaxNumEyes; eye++) {
// Stereoscopy
bool vrStereo = !PSP_CoreParameter().compat.vrCompat().ForceMono && g_Config.bEnableStereo;
if (vrStereo) {
bool mirrored = vrMirroring[VR_MIRRORING_AXIS_Z] ^ (matrix == VR_VIEW_MATRIX_RIGHT_EYE);
bool mirrored = vrMirroring[VR_MIRRORING_AXIS_Z] ^ (eye == 1);
float dx = fabs(vrView[1].pose.position.x - vrView[0].pose.position.x);
float dy = fabs(vrView[1].pose.position.y - vrView[0].pose.position.y);
float dz = fabs(vrView[1].pose.position.z - vrView[0].pose.position.z);
@ -958,6 +940,6 @@ void UpdateVRViewMatrices() {
M[11] += separation.z;
}
memcpy(vrMatrix[matrix], M, sizeof(float) * 16);
memcpy(vrViewMatrix[eye], M, sizeof(float) * 16);
}
}

View file

@ -37,7 +37,7 @@ void SetVRCallbacks(void(*axis)(const AxisInput *axis, size_t count), bool(*key)
// VR input integration
void SetVRAppMode(VRAppMode mode);
void UpdateVRInput(bool haptics, float dp_xscale, float dp_yscale);
bool UpdateVRAxis(const AxisInput &axis);
bool UpdateVRAxis(const AxisInput *axes, size_t count);
bool UpdateVRKeys(const KeyInput &key);
// VR games compatibility

View file

@ -109,8 +109,8 @@ static bool ovrFramebuffer_CreateGLES(XrSession session, ovrFramebuffer* frameBu
swapChainCreateInfo.arraySize = multiview ? 2 : 1;
#ifdef ANDROID
XrSwapchainCreateInfoFoveationFB swapChainFoveationCreateInfo;
if (VR_GetPlatformFlag(VR_PLATFORM_EXTENSION_FOVEATION)) {
XrSwapchainCreateInfoFoveationFB swapChainFoveationCreateInfo;
memset(&swapChainFoveationCreateInfo, 0, sizeof(swapChainFoveationCreateInfo));
swapChainFoveationCreateInfo.type = XR_TYPE_SWAPCHAIN_CREATE_INFO_FOVEATION_FB;
swapChainCreateInfo.next = &swapChainFoveationCreateInfo;
@ -203,8 +203,8 @@ static bool ovrFramebuffer_CreateVK(XrSession session, ovrFramebuffer* frameBuff
swapChainCreateInfo.arraySize = multiview ? 2 : 1;
#ifdef ANDROID
XrSwapchainCreateInfoFoveationFB swapChainFoveationCreateInfo;
if (VR_GetPlatformFlag(VR_PLATFORM_EXTENSION_FOVEATION)) {
XrSwapchainCreateInfoFoveationFB swapChainFoveationCreateInfo;
memset(&swapChainFoveationCreateInfo, 0, sizeof(swapChainFoveationCreateInfo));
swapChainFoveationCreateInfo.type = XR_TYPE_SWAPCHAIN_CREATE_INFO_FOVEATION_FB;
swapChainCreateInfo.next = &swapChainFoveationCreateInfo;

View file

@ -1697,7 +1697,6 @@ void XEmitter::MOVMSKPD(X64Reg dest, OpArg arg) {WriteSSEOp(0x66, 0x50, dest, ar
void XEmitter::LDDQU(X64Reg dest, OpArg arg) {WriteSSEOp(0xF2, sseLDDQU, dest, arg);} // For integer data only
// THESE TWO ARE UNTESTED.
void XEmitter::UNPCKLPS(X64Reg dest, OpArg arg) {WriteSSEOp(0x00, 0x14, dest, arg);}
void XEmitter::UNPCKHPS(X64Reg dest, OpArg arg) {WriteSSEOp(0x00, 0x15, dest, arg);}
@ -1892,6 +1891,9 @@ void XEmitter::PTEST(X64Reg dest, OpArg arg) {WriteSSE41Op(0x66, 0x3817, dest
void XEmitter::PACKUSDW(X64Reg dest, OpArg arg) {WriteSSE41Op(0x66, 0x382b, dest, arg);}
void XEmitter::DPPS(X64Reg dest, OpArg arg, u8 mask) {WriteSSE41Op(0x66, 0x3A40, dest, arg, 1); Write8(mask);}
void XEmitter::INSERTPS(X64Reg dest, OpArg arg, u8 dstsubreg, u8 srcsubreg, u8 zmask) { WriteSSE41Op(0x66, 0x3A21, dest, arg, 1); Write8((srcsubreg << 6) | (dstsubreg << 4) | zmask); }
void XEmitter::EXTRACTPS(OpArg dest, X64Reg arg, u8 subreg) { WriteSSE41Op(0x66, 0x3A17, arg, dest, 1); Write8(subreg); }
void XEmitter::PMINSB(X64Reg dest, OpArg arg) {WriteSSE41Op(0x66, 0x3838, dest, arg);}
void XEmitter::PMINSD(X64Reg dest, OpArg arg) {WriteSSE41Op(0x66, 0x3839, dest, arg);}
void XEmitter::PMINUW(X64Reg dest, OpArg arg) {WriteSSE41Op(0x66, 0x383a, dest, arg);}
@ -2084,7 +2086,7 @@ void XEmitter::VCVTTPD2DQ(int bits, X64Reg regOp1, OpArg arg) { WriteAVXOp(bits,
void XEmitter::VCVTTSS2SI(int bits, X64Reg regOp1, OpArg arg) { WriteAVXOp(0, 0xF3, 0x2C, regOp1, arg, 0, bits == 64 ? 1 : 0); }
void XEmitter::VCVTTSD2SI(int bits, X64Reg regOp1, OpArg arg) { WriteAVXOp(0, 0xF2, 0x2C, regOp1, arg, 0, bits == 64 ? 1 : 0); }
void XEmitter::VEXTRACTPS(OpArg arg, X64Reg regOp1, u8 subreg) { WriteAVXOp(0, 0x66, 0x3A17, regOp1, arg, 1); Write8(subreg); }
void XEmitter::VINSERTPS(X64Reg regOp1, X64Reg regOp2, OpArg arg, u8 subreg) { WriteAVXOp(0, 0x66, 0x3A21, regOp1, regOp2, arg, 1); Write8(subreg); }
void XEmitter::VINSERTPS(X64Reg regOp1, X64Reg regOp2, OpArg arg, u8 dstsubreg, u8 srcsubreg, u8 zmask) { WriteAVXOp(0, 0x66, 0x3A21, regOp1, regOp2, arg, 1); Write8((srcsubreg << 6) | (dstsubreg << 4) | zmask); }
void XEmitter::VLDDQU(int bits, X64Reg regOp1, OpArg arg) { WriteAVXOp(bits, 0xF2, sseLDDQU, regOp1, arg); }
void XEmitter::VMOVAPS(int bits, X64Reg regOp1, OpArg arg) { WriteAVXOp(bits, 0x00, sseMOVAPfromRM, regOp1, arg); }
void XEmitter::VMOVAPD(int bits, X64Reg regOp1, OpArg arg) { WriteAVXOp(bits, 0x66, sseMOVAPfromRM, regOp1, arg); }

View file

@ -684,12 +684,14 @@ public:
// SSE4: Further horizontal operations - dot products. These are weirdly flexible, the arg contains both a read mask and a write "mask".
void DPPD(X64Reg dest, OpArg src, u8 arg);
// These are probably useful for VFPU emulation.
void INSERTPS(X64Reg dest, OpArg src, u8 arg);
void EXTRACTPS(OpArg dest, X64Reg src, u8 arg);
#endif
// SSE4: Insert and extract for floats.
// Note: insert from memory or an XMM.
void INSERTPS(X64Reg dest, OpArg arg, u8 dstsubreg, u8 srcsubreg = 0, u8 zmask = 0);
// Extract to memory or GPR.
void EXTRACTPS(OpArg dest, X64Reg arg, u8 subreg);
// SSE3: Horizontal operations in SIMD registers. Very slow! shufps-based code beats it handily on Ivy.
void HADDPS(X64Reg dest, OpArg src);
@ -1040,7 +1042,7 @@ public:
// Can only extract from the low 128 bits.
void VEXTRACTPS(OpArg arg, X64Reg regOp1, u8 subreg);
// Can only insert into the low 128 bits, zeros upper bits. Inserts from XMM.
void VINSERTPS(X64Reg regOp1, X64Reg regOp2, OpArg arg, u8 subreg);
void VINSERTPS(X64Reg regOp1, X64Reg regOp2, OpArg arg, u8 dstsubreg, u8 srcsubreg = 0, u8 zmask = 0);
void VLDDQU(int bits, X64Reg regOp1, OpArg arg);
void VMOVAPS(int bits, X64Reg regOp1, OpArg arg);
void VMOVAPD(int bits, X64Reg regOp1, OpArg arg);

View file

@ -133,6 +133,8 @@ void Compatibility::CheckSettings(IniFile &iniFile, const std::string &gameID) {
CheckSetting(iniFile, gameID, "SOCOMClut8Replacement", &flags_.SOCOMClut8Replacement);
CheckSetting(iniFile, gameID, "Fontltn12Hack", &flags_.Fontltn12Hack);
CheckSetting(iniFile, gameID, "LoadCLUTFromCurrentFrameOnly", &flags_.LoadCLUTFromCurrentFrameOnly);
CheckSetting(iniFile, gameID, "ForceUMDReadSpeed", &flags_.ForceUMDReadSpeed);
CheckSetting(iniFile, gameID, "AllowDelayedReadbacks", &flags_.AllowDelayedReadbacks);
}
void Compatibility::CheckVRSettings(IniFile &iniFile, const std::string &gameID) {

View file

@ -103,6 +103,8 @@ struct CompatFlags {
bool SOCOMClut8Replacement;
bool Fontltn12Hack;
bool LoadCLUTFromCurrentFrameOnly;
bool ForceUMDReadSpeed;
bool AllowDelayedReadbacks;
};
struct VRCompat {

View file

@ -81,6 +81,33 @@ static const char *logSectionName = "LogDebug";
static const char *logSectionName = "Log";
#endif
std::string GPUBackendToString(GPUBackend backend) {
switch (backend) {
case GPUBackend::OPENGL:
return "OPENGL";
case GPUBackend::DIRECT3D9:
return "DIRECT3D9";
case GPUBackend::DIRECT3D11:
return "DIRECT3D11";
case GPUBackend::VULKAN:
return "VULKAN";
}
// Intentionally not a default so we get a warning.
return "INVALID";
}
GPUBackend GPUBackendFromString(std::string_view backend) {
if (!equalsNoCase(backend, "OPENGL") || backend == "0")
return GPUBackend::OPENGL;
if (!equalsNoCase(backend, "DIRECT3D9") || backend == "1")
return GPUBackend::DIRECT3D9;
if (!equalsNoCase(backend, "DIRECT3D11") || backend == "2")
return GPUBackend::DIRECT3D11;
if (!equalsNoCase(backend, "VULKAN") || backend == "3")
return GPUBackend::VULKAN;
return GPUBackend::OPENGL;
}
const char *DefaultLangRegion() {
// Unfortunate default. There's no need to use bFirstRun, since this is only a default.
static std::string defaultLangRegion = "en_US";
@ -279,10 +306,11 @@ static bool DefaultSasThread() {
static const ConfigSetting achievementSettings[] = {
// Core settings
ConfigSetting("AchievementsEnable", &g_Config.bAchievementsEnable, true, CfgFlag::DEFAULT),
ConfigSetting("AchievementsChallengeMode", &g_Config.bAchievementsChallengeMode, false, CfgFlag::DEFAULT),
ConfigSetting("AchievementsChallengeMode", &g_Config.bAchievementsChallengeMode, true, CfgFlag::DEFAULT),
ConfigSetting("AchievementsEncoreMode", &g_Config.bAchievementsEncoreMode, false, CfgFlag::DEFAULT),
ConfigSetting("AchievementsUnofficial", &g_Config.bAchievementsUnofficial, false, CfgFlag::DEFAULT),
ConfigSetting("AchievementsLogBadMemReads", &g_Config.bAchievementsLogBadMemReads, false, CfgFlag::DEFAULT),
ConfigSetting("bAchievementsSaveStateInChallengeMode", &g_Config.bAchievementsSaveStateInChallengeMode, false, CfgFlag::DEFAULT),
// Achievements login info. Note that password is NOT stored, only a login token.
// And that login token is stored separately from the ini, see NativeSaveSecret, but it can also be loaded
@ -513,7 +541,7 @@ bool Config::IsBackendEnabled(GPUBackend backend, bool validate) {
return true;
}
template <typename T, std::string (*FTo)(T), T (*FFrom)(const std::string &)>
template <typename T, std::string (*FTo)(T), T (*FFrom)(std::string_view)>
struct ConfigTranslator {
static std::string To(int v) {
return StringFromInt(v) + " (" + FTo(T(v)) + ")";
@ -590,7 +618,6 @@ static const ConfigSetting graphicsSettings[] = {
ConfigSetting("AnisotropyLevel", &g_Config.iAnisotropyLevel, 4, CfgFlag::PER_GAME),
ConfigSetting("MultiSampleLevel", &g_Config.iMultiSampleLevel, 0, CfgFlag::PER_GAME), // Number of samples is 1 << iMultiSampleLevel
ConfigSetting("VertexDecCache", &g_Config.bVertexCache, false, CfgFlag::PER_GAME | CfgFlag::REPORT),
ConfigSetting("TextureBackoffCache", &g_Config.bTextureBackoffCache, false, CfgFlag::PER_GAME | CfgFlag::REPORT),
ConfigSetting("VertexDecJit", &g_Config.bVertexDecoderJit, &DefaultCodeGen, CfgFlag::DONT_SAVE | CfgFlag::REPORT),
@ -762,6 +789,7 @@ static const ConfigSetting controlSettings[] = {
ConfigSetting("TouchButtonOpacity", &g_Config.iTouchButtonOpacity, 65, CfgFlag::PER_GAME),
ConfigSetting("TouchButtonHideSeconds", &g_Config.iTouchButtonHideSeconds, 20, CfgFlag::PER_GAME),
ConfigSetting("AutoCenterTouchAnalog", &g_Config.bAutoCenterTouchAnalog, false, CfgFlag::PER_GAME),
ConfigSetting("StickyTouchDPad", &g_Config.bStickyTouchDPad, false, CfgFlag::PER_GAME),
// Snap touch control position
ConfigSetting("TouchSnapToGrid", &g_Config.bTouchSnapToGrid, false, CfgFlag::PER_GAME),
@ -907,7 +935,6 @@ static const ConfigSetting vrSettings[] = {
ConfigSetting("VRCameraPitch", &g_Config.iCameraPitch, 0, CfgFlag::PER_GAME),
ConfigSetting("VRCanvasDistance", &g_Config.fCanvasDistance, 12.0f, CfgFlag::DEFAULT),
ConfigSetting("VRCanvas3DDistance", &g_Config.fCanvas3DDistance, 3.0f, CfgFlag::DEFAULT),
ConfigSetting("VRFieldOfView", &g_Config.fFieldOfViewPercentage, 100.0f, CfgFlag::PER_GAME),
ConfigSetting("VRHeadUpDisplayScale", &g_Config.fHeadUpDisplayScale, 0.3f, CfgFlag::PER_GAME),
ConfigSetting("VRMotionLength", &g_Config.fMotionLength, 0.5f, CfgFlag::DEFAULT),
ConfigSetting("VRHeadRotationScale", &g_Config.fHeadRotationScale, 5.0f, CfgFlag::PER_GAME),
@ -1068,6 +1095,8 @@ void Config::UpdateAfterSettingAutoFrameSkip() {
}
void Config::Load(const char *iniFileName, const char *controllerIniFilename) {
double startTime = time_now_d();
if (!bUpdatedInstanceCounter) {
InitInstanceCounter();
bUpdatedInstanceCounter = true;
@ -1128,6 +1157,10 @@ void Config::Load(const char *iniFileName, const char *controllerIniFilename) {
}
}
// Time tracking
Section *playTime = iniFile.GetOrCreateSection("PlayTime");
playTimeTracker_.Load(playTime);
auto pinnedPaths = iniFile.GetOrCreateSection("PinnedPaths")->ToMap();
vPinnedPaths.clear();
for (auto it = pinnedPaths.begin(), end = pinnedPaths.end(); it != end; ++it) {
@ -1209,10 +1242,11 @@ void Config::Load(const char *iniFileName, const char *controllerIniFilename) {
PostLoadCleanup(false);
INFO_LOG(LOADER, "Config loaded: '%s'", iniFilename_.c_str());
INFO_LOG(LOADER, "Config loaded: '%s' (%0.1f ms)", iniFilename_.c_str(), (time_now_d() - startTime) * 1000.0);
}
bool Config::Save(const char *saveReason) {
double startTime = time_now_d();
if (!IsFirstInstance()) {
// TODO: Should we allow saving config if started from a different directory?
// How do we tell?
@ -1287,11 +1321,15 @@ bool Config::Save(const char *saveReason) {
if (LogManager::GetInstance())
LogManager::GetInstance()->SaveConfig(log);
// Time tracking
Section *playTime = iniFile.GetOrCreateSection("PlayTime");
playTimeTracker_.Save(playTime);
if (!iniFile.Save(iniFilename_)) {
ERROR_LOG(LOADER, "Error saving config (%s)- can't write ini '%s'", saveReason, iniFilename_.c_str());
return false;
}
INFO_LOG(LOADER, "Config saved (%s): '%s'", saveReason, iniFilename_.c_str());
INFO_LOG(LOADER, "Config saved (%s): '%s' (%0.1f ms)", saveReason, iniFilename_.c_str(), (time_now_d() - startTime) * 1000.0);
if (!bGameSpecific) //otherwise we already did this in saveGameConfig()
{
@ -1797,3 +1835,89 @@ int Config::GetPSPLanguage() {
return g_Config.iLanguage;
}
}
void PlayTimeTracker::Start(std::string gameId) {
INFO_LOG(SYSTEM, "GameTimeTracker::Start(%s)", gameId.c_str());
if (gameId.empty()) {
return;
}
auto iter = tracker_.find(std::string(gameId));
if (iter != tracker_.end()) {
if (iter->second.startTime == 0.0) {
iter->second.lastTimePlayed = time_now_unix_utc();
iter->second.startTime = time_now_d();
}
return;
}
PlayTime playTime;
playTime.lastTimePlayed = time_now_unix_utc();
playTime.totalTimePlayed = 0.0;
playTime.startTime = time_now_d();
tracker_[gameId] = playTime;
}
void PlayTimeTracker::Stop(std::string gameId) {
INFO_LOG(SYSTEM, "GameTimeTracker::Stop(%s)", gameId.c_str());
_dbg_assert_(!gameId.empty());
auto iter = tracker_.find(std::string(gameId));
if (iter != tracker_.end()) {
if (iter->second.startTime != 0.0) {
iter->second.totalTimePlayed += time_now_d() - iter->second.startTime;
iter->second.startTime = 0.0;
}
iter->second.lastTimePlayed = time_now_unix_utc();
return;
}
// Shouldn't happen, ignore this case.
WARN_LOG(SYSTEM, "GameTimeTracker::Stop called without corresponding GameTimeTracker::Start");
}
void PlayTimeTracker::Load(const Section *section) {
tracker_.clear();
std::vector<std::string> keys;
section->GetKeys(keys);
for (auto key : keys) {
std::string value;
if (!section->Get(key.c_str(), &value, nullptr)) {
continue;
}
// Parse the string.
PlayTime gameTime{};
if (2 == sscanf(value.c_str(), "%d,%llu", &gameTime.totalTimePlayed, &gameTime.lastTimePlayed)) {
tracker_[key] = gameTime;
}
}
}
void PlayTimeTracker::Save(Section *section) {
for (auto iter : tracker_) {
std::string formatted = StringFromFormat("%d,%llu", iter.second.totalTimePlayed, iter.second.lastTimePlayed);
section->Set(iter.first.c_str(), formatted);
}
}
bool PlayTimeTracker::GetPlayedTimeString(const std::string &gameId, std::string *str) const {
auto ga = GetI18NCategory(I18NCat::GAME);
auto iter = tracker_.find(gameId);
if (iter == tracker_.end()) {
return false;
}
int totalSeconds = iter->second.totalTimePlayed;
int seconds = totalSeconds % 60;
totalSeconds /= 60;
int minutes = totalSeconds % 60;
totalSeconds /= 60;
int hours = totalSeconds;
*str = ApplySafeSubstitutions(ga->T("Time Played: %1h %2m %3s"), hours, minutes, seconds);
return true;
}

View file

@ -37,6 +37,29 @@ namespace http {
struct UrlEncoder;
struct ConfigPrivate;
class Section;
class PlayTimeTracker {
public:
struct PlayTime {
int totalTimePlayed;
double startTime; // time_now_d() time
uint64_t lastTimePlayed; // UTC Unix time for portability.
};
// It's OK to call these redundantly.
void Start(std::string gameId);
void Stop(std::string gameId);
void Load(const Section *section);
void Save(Section *section);
bool GetPlayedTimeString(const std::string &gameId, std::string *str) const;
private:
std::map<std::string, PlayTime> tracker_;
};
struct Config {
public:
Config();
@ -176,7 +199,6 @@ public:
float fUITint;
float fUISaturation;
bool bVertexCache;
bool bTextureBackoffCache;
bool bVertexDecoderJit;
bool bFullScreen;
@ -443,7 +465,6 @@ public:
float fCameraSide;
float fCanvasDistance;
float fCanvas3DDistance;
float fFieldOfViewPercentage;
float fHeadUpDisplayScale;
float fMotionLength;
float fHeadRotationScale;
@ -492,6 +513,7 @@ public:
bool bAchievementsUnofficial;
bool bAchievementsSoundEffects;
bool bAchievementsLogBadMemReads;
bool bAchievementsSaveStateInChallengeMode;
// Positioning of the various notifications
int iAchievementsLeaderboardTrackerPos;
@ -579,6 +601,8 @@ public:
// Applies the Auto setting if set. Returns an enum value from PSP_SYSTEMPARAM_LANGUAGE_*.
int GetPSPLanguage();
PlayTimeTracker &TimeTracker() { return playTimeTracker_; }
protected:
void LoadStandardControllerIni();
void LoadLangValuesMapping();
@ -593,6 +617,7 @@ private:
std::string gameIdTitle_;
std::vector<std::string> recentIsos;
std::map<std::string, std::pair<std::string, int>> langValuesMapping_;
PlayTimeTracker playTimeTracker_;
Path iniFilename_;
Path controllerIniFilename_;
Path searchPath_;

View file

@ -90,32 +90,8 @@ enum class RestoreSettingsBits : int {
};
ENUM_CLASS_BITOPS(RestoreSettingsBits);
inline std::string GPUBackendToString(GPUBackend backend) {
switch (backend) {
case GPUBackend::OPENGL:
return "OPENGL";
case GPUBackend::DIRECT3D9:
return "DIRECT3D9";
case GPUBackend::DIRECT3D11:
return "DIRECT3D11";
case GPUBackend::VULKAN:
return "VULKAN";
}
// Intentionally not a default so we get a warning.
return "INVALID";
}
inline GPUBackend GPUBackendFromString(const std::string &backend) {
if (!strcasecmp(backend.c_str(), "OPENGL") || backend == "0")
return GPUBackend::OPENGL;
if (!strcasecmp(backend.c_str(), "DIRECT3D9") || backend == "1")
return GPUBackend::DIRECT3D9;
if (!strcasecmp(backend.c_str(), "DIRECT3D11") || backend == "2")
return GPUBackend::DIRECT3D11;
if (!strcasecmp(backend.c_str(), "VULKAN") || backend == "3")
return GPUBackend::VULKAN;
return GPUBackend::OPENGL;
}
std::string GPUBackendToString(GPUBackend backend);
GPUBackend GPUBackendFromString(std::string_view backend);
enum AudioBackendType {
AUDIO_BACKEND_AUTO,
@ -128,6 +104,7 @@ enum IOTimingMethods {
IOTIMING_FAST = 0,
IOTIMING_HOST = 1,
IOTIMING_REALISTIC = 2,
IOTIMING_UMDSLOWREALISTIC = 3,
};
enum class AutoLoadSaveState {
@ -189,4 +166,5 @@ enum class DebugOverlay : int {
AUDIO,
GPU_PROFILE,
GPU_ALLOCATOR,
FRAMEBUFFER_LIST,
};

View file

@ -476,27 +476,31 @@ void ControlMapper::ToggleSwapAxes() {
UpdateAnalogOutput(1);
}
void ControlMapper::Axis(const AxisInput &axis) {
void ControlMapper::Axis(const AxisInput *axes, size_t count) {
double now = time_now_d();
std::lock_guard<std::mutex> guard(mutex_);
if (axis.deviceId < DEVICE_ID_COUNT) {
deviceTimestamps_[(int)axis.deviceId] = now;
}
if (axis.value >= 0.0f) {
InputMapping mapping(axis.deviceId, axis.axisId, 1);
InputMapping opposite(axis.deviceId, axis.axisId, -1);
curInput_[mapping] = { axis.value, now };
curInput_[opposite] = { 0.0f, now };
UpdatePSPState(mapping, now);
UpdatePSPState(opposite, now);
} else if (axis.value < 0.0f) {
InputMapping mapping(axis.deviceId, axis.axisId, -1);
InputMapping opposite(axis.deviceId, axis.axisId, 1);
curInput_[mapping] = { -axis.value, now };
curInput_[opposite] = { 0.0f, now };
UpdatePSPState(mapping, now);
UpdatePSPState(opposite, now);
for (size_t i = 0; i < count; i++) {
const AxisInput &axis = axes[i];
size_t deviceIndex = (size_t)axis.deviceId; // this wraps -1 up high, so will get rejected on the next line.
if (deviceIndex < (size_t)DEVICE_ID_COUNT) {
deviceTimestamps_[deviceIndex] = now;
}
if (axis.value >= 0.0f) {
InputMapping mapping(axis.deviceId, axis.axisId, 1);
InputMapping opposite(axis.deviceId, axis.axisId, -1);
curInput_[mapping] = { axis.value, now };
curInput_[opposite] = { 0.0f, now };
UpdatePSPState(mapping, now);
UpdatePSPState(opposite, now);
} else if (axis.value < 0.0f) {
InputMapping mapping(axis.deviceId, axis.axisId, -1);
InputMapping opposite(axis.deviceId, axis.axisId, 1);
curInput_[mapping] = { -axis.value, now };
curInput_[opposite] = { 0.0f, now };
UpdatePSPState(mapping, now);
UpdatePSPState(opposite, now);
}
}
}

View file

@ -16,7 +16,7 @@ public:
// Inputs to the table-based mapping
// These functions are free-threaded.
bool Key(const KeyInput &key, bool *pauseTrigger);
void Axis(const AxisInput &axis);
void Axis(const AxisInput *axes, size_t count);
// Required callbacks.
// TODO: These are so many now that a virtual interface might be more appropriate..
@ -62,7 +62,7 @@ private:
float virtKeys_[VIRTKEY_COUNT]{};
bool virtKeyOn_[VIRTKEY_COUNT]{}; // Track boolean output separaately since thresholds may differ.
double deviceTimestamps_[42]{};
double deviceTimestamps_[(size_t)DEVICE_ID_COUNT]{};
int lastNonDeadzoneDeviceID_[2]{};
@ -76,6 +76,8 @@ private:
bool swapAxes_ = false;
// Protects basically all the state.
// TODO: Maybe we should piggyback on the screenmanager mutex - it's always locked
// when events come in here.
std::mutex mutex_;
std::map<InputMapping, InputSample> curInput_;

View file

@ -207,30 +207,12 @@ void UpdateRunLoop(GraphicsContext *ctx) {
// Note: not used on Android.
void Core_RunLoop(GraphicsContext *ctx) {
float refreshRate = System_GetPropertyFloat(SYSPROP_DISPLAY_REFRESH_RATE);
if (windowHidden && g_Config.bPauseWhenMinimized) {
sleep_ms(16);
return;
}
bool menuThrottle = (GetUIState() != UISTATE_INGAME || !PSP_IsInited()) && GetUIState() != UISTATE_EXIT;
double startTime;
if (menuThrottle) {
startTime = time_now_d();
}
NativeFrame(ctx);
if (menuThrottle) {
// Simple throttling to not burn the GPU in the menu.
// TODO: This should move into NativeFrame. Also, it's only necessary in MAILBOX or IMMEDIATE presentation modes.
double diffTime = time_now_d() - startTime;
int sleepTime = (int)(1000.0 / refreshRate) - (int)(diffTime * 1000.0);
if (sleepTime > 0)
sleep_ms(sleepTime);
}
}
void Core_DoSingleStep() {

View file

@ -138,7 +138,7 @@
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<AdditionalIncludeDirectories>..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext;../ext/zstd/lib</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>..\ffmpeg\WindowsInclude;..\ext\libchdr\include;..\ffmpeg\Windows\x86\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext;../ext/zstd/lib</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_32=1;_M_IX86=1;_DEBUG;_LIB;_UNICODE;UNICODE;MINIUPNP_STATICLIB;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
<FloatingPointModel>Precise</FloatingPointModel>
@ -165,7 +165,7 @@
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<AdditionalIncludeDirectories>..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext;../ext/zstd/lib;../ext/zstd/lib</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>..\ffmpeg\WindowsInclude;..\ext\libchdr\include;..\ffmpeg\Windows\x86_64\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext;../ext/zstd/lib;../ext/zstd/lib</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_64=1;_M_X64=1;_DEBUG;_LIB;_UNICODE;UNICODE;MINIUPNP_STATICLIB;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
<FloatingPointModel>Precise</FloatingPointModel>
@ -193,7 +193,7 @@
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<AdditionalIncludeDirectories>..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\aarch64\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext;../ext/zstd/lib</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>..\ffmpeg\WindowsInclude;..\ext\libchdr\include;..\ffmpeg\Windows\aarch64\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext;../ext/zstd/lib</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_64=1;_DEBUG;_LIB;_UNICODE;UNICODE;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
<FloatingPointModel>Precise</FloatingPointModel>
@ -221,7 +221,7 @@
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<AdditionalIncludeDirectories>..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\arm\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext;../ext/zstd/lib</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>..\ffmpeg\WindowsInclude;..\ext\libchdr\include;..\ffmpeg\Windows\arm\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext;../ext/zstd/lib</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;USE_FFMPEG;WITH_UPNP;WIN32;_ARCH_32=1;_DEBUG;_LIB;_UNICODE;UNICODE;ARMIPS_USE_STD_FILESYSTEM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
<FloatingPointModel>Precise</FloatingPointModel>
@ -253,7 +253,7 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<AdditionalIncludeDirectories>..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext;../ext/zstd/lib</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>..\ffmpeg\WindowsInclude;..\ext\libchdr\include;..\ffmpeg\Windows\x86\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext;../ext/zstd/lib</AdditionalIncludeDirectories>
<BufferSecurityCheck>false</BufferSecurityCheck>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
<FloatingPointModel>Precise</FloatingPointModel>
@ -286,7 +286,7 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<AdditionalIncludeDirectories>..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext;../ext/zstd/lib;../ext/zstd/lib</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>..\ffmpeg\WindowsInclude;..\ext\libchdr\include;..\ffmpeg\Windows\x86_64\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext;../ext/zstd/lib;../ext/zstd/lib</AdditionalIncludeDirectories>
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
<FloatingPointModel>Precise</FloatingPointModel>
<BufferSecurityCheck>false</BufferSecurityCheck>
@ -321,7 +321,7 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<AdditionalIncludeDirectories>..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\aarch64\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext;../ext/zstd/lib</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>..\ffmpeg\WindowsInclude;..\ext\libchdr\include;..\ffmpeg\Windows\aarch64\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext;../ext/zstd/lib</AdditionalIncludeDirectories>
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
<FloatingPointModel>Precise</FloatingPointModel>
<BufferSecurityCheck>false</BufferSecurityCheck>
@ -356,7 +356,7 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<AdditionalIncludeDirectories>..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\arm\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext;../ext/zstd/lib</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>..\ffmpeg\WindowsInclude;..\ext\libchdr\include;..\ffmpeg\Windows\arm\include;../common;..;../ext/glew;../ext/snappy;../ext/libpng17;../ext/zlib;../ext;../ext/zstd/lib</AdditionalIncludeDirectories>
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
<FloatingPointModel>Precise</FloatingPointModel>
<BufferSecurityCheck>false</BufferSecurityCheck>
@ -1076,6 +1076,7 @@
<ClCompile Include="Util\AudioFormat.cpp" />
<ClCompile Include="Util\BlockAllocator.cpp" />
<ClCompile Include="Util\DisArm64.cpp" />
<ClCompile Include="Util\GameDB.cpp" />
<ClCompile Include="Util\GameManager.cpp" />
<ClCompile Include="Util\PortManager.cpp" />
<ClCompile Include="Util\PPGeDraw.cpp" />
@ -1444,6 +1445,7 @@
<ClInclude Include="Util\AudioFormat.h" />
<ClInclude Include="Util\BlockAllocator.h" />
<ClInclude Include="Util\DisArm64.h" />
<ClInclude Include="Util\GameDB.h" />
<ClInclude Include="Util\GameManager.h" />
<ClInclude Include="Util\PortManager.h" />
<ClInclude Include="Util\PPGeDraw.h" />
@ -1466,6 +1468,9 @@
<ProjectReference Include="..\ext\libarmips.vcxproj">
<Project>{129e5e2b-39c1-4d84-96fe-dfd22dbb4a25}</Project>
</ProjectReference>
<ProjectReference Include="..\ext\libchdr.vcxproj">
<Project>{956f1f48-b612-46d8-89ee-96996dcd9383}</Project>
</ProjectReference>
<ProjectReference Include="..\ext\miniupnpc.vcxproj">
<Project>{d8a71225-178b-424e-96c1-cc3be2c1b047}</Project>
</ProjectReference>

View file

@ -1297,6 +1297,9 @@
<ClCompile Include="MIPS\ARM64\Arm64IRCompFPU.cpp">
<Filter>MIPS\ARM64</Filter>
</ClCompile>
<ClCompile Include="Util\GameDB.cpp">
<Filter>Util</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="ELF\ElfReader.h">
@ -2070,6 +2073,9 @@
<ClInclude Include="MIPS\ARM64\Arm64IRRegCache.h">
<Filter>MIPS\ARM64</Filter>
</ClInclude>
<ClInclude Include="Util\GameDB.h">
<Filter>Util</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="..\LICENSE.TXT" />

View file

@ -17,8 +17,10 @@
#include <algorithm>
#include <atomic>
#include <condition_variable>
#include <cstring>
#include <mutex>
#include <thread>
#include "Common/Log.h"
#include "Common/Serialize/Serializer.h"
@ -49,6 +51,8 @@ private:
uint64_t ticks = 0;
uint32_t pc = 0;
bool allocated = false;
// Intentionally not save stated.
bool bulkStorage = false;
char tag[128]{};
Slab *prev = nullptr;
Slab *next = nullptr;
@ -72,18 +76,22 @@ private:
Slab *first_ = nullptr;
Slab *lastFind_ = nullptr;
std::vector<Slab *> heads_;
Slab *bulkStorage_ = nullptr;
};
struct PendingNotifyMem {
MemBlockFlags flags;
uint32_t start;
uint32_t size;
uint32_t copySrc;
uint64_t ticks;
uint32_t pc;
char tag[128];
};
static constexpr size_t MAX_PENDING_NOTIFIES = 512;
// 160 KB.
static constexpr size_t MAX_PENDING_NOTIFIES = 1024;
static constexpr size_t MAX_PENDING_NOTIFIES_THREAD = 1000;
static MemSlabMap allocMap;
static MemSlabMap suballocMap;
static MemSlabMap writeMap;
@ -93,9 +101,17 @@ static std::atomic<uint32_t> pendingNotifyMinAddr1;
static std::atomic<uint32_t> pendingNotifyMaxAddr1;
static std::atomic<uint32_t> pendingNotifyMinAddr2;
static std::atomic<uint32_t> pendingNotifyMaxAddr2;
static std::mutex pendingMutex;
// To prevent deadlocks, acquire Read before Write if you're going to acquire both.
static std::mutex pendingWriteMutex;
static std::mutex pendingReadMutex;
static int detailedOverride;
static std::thread flushThread;
static std::atomic<bool> flushThreadRunning;
static std::atomic<bool> flushThreadPending;
static std::mutex flushLock;
static std::condition_variable flushCond;
MemSlabMap::MemSlabMap() {
Reset();
}
@ -184,6 +200,7 @@ void MemSlabMap::DoState(PointerWrap &p) {
// Since heads_ is a static size, let's avoid clearing it.
// This helps in case a debugger call happens concurrently.
Slab *old = first_;
Slab *oldBulk = bulkStorage_;
Do(p, count);
first_ = new Slab();
@ -193,9 +210,12 @@ void MemSlabMap::DoState(PointerWrap &p) {
FillHeads(first_);
bulkStorage_ = new Slab[count];
Slab *slab = first_;
for (int i = 0; i < count; ++i) {
slab->next = new Slab();
slab->next = &bulkStorage_[i];
slab->next->bulkStorage = true;
slab->next->DoState(p);
slab->next->prev = slab;
@ -207,9 +227,11 @@ void MemSlabMap::DoState(PointerWrap &p) {
// Now that it's entirely disconnected, delete the old slabs.
while (old != nullptr) {
Slab *next = old->next;
delete old;
if (!old->bulkStorage)
delete old;
old = next;
}
delete [] oldBulk;
} else {
for (Slab *slab = first_; slab != nullptr; slab = slab->next)
++count;
@ -253,9 +275,12 @@ void MemSlabMap::Clear() {
Slab *s = first_;
while (s != nullptr) {
Slab *next = s->next;
delete s;
if (!s->bulkStorage)
delete s;
s = next;
}
delete bulkStorage_;
bulkStorage_ = nullptr;
first_ = nullptr;
lastFind_ = nullptr;
heads_.clear();
@ -348,7 +373,8 @@ void MemSlabMap::Merge(Slab *a, Slab *b) {
}
if (lastFind_ == b)
lastFind_ = a;
delete b;
if (!b->bulkStorage)
delete b;
}
void MemSlabMap::FillHeads(Slab *slab) {
@ -369,9 +395,32 @@ void MemSlabMap::FillHeads(Slab *slab) {
}
}
size_t FormatMemWriteTagAtNoFlush(char *buf, size_t sz, const char *prefix, uint32_t start, uint32_t size);
void FlushPendingMemInfo() {
std::lock_guard<std::mutex> guard(pendingMutex);
for (const auto &info : pendingNotifies) {
// This lock prevents us from another thread reading while we're busy flushing.
std::lock_guard<std::mutex> guard(pendingReadMutex);
std::vector<PendingNotifyMem> thisBatch;
{
std::lock_guard<std::mutex> guard(pendingWriteMutex);
thisBatch = std::move(pendingNotifies);
pendingNotifies.clear();
pendingNotifies.reserve(MAX_PENDING_NOTIFIES);
pendingNotifyMinAddr1 = 0xFFFFFFFF;
pendingNotifyMaxAddr1 = 0;
pendingNotifyMinAddr2 = 0xFFFFFFFF;
pendingNotifyMaxAddr2 = 0;
}
for (const auto &info : thisBatch) {
if (info.copySrc != 0) {
char tagData[128];
size_t tagSize = FormatMemWriteTagAtNoFlush(tagData, sizeof(tagData), info.tag, info.copySrc, info.size);
writeMap.Mark(info.start, info.size, info.ticks, info.pc, true, tagData);
continue;
}
if (info.flags & MemBlockFlags::ALLOC) {
allocMap.Mark(info.start, info.size, info.ticks, info.pc, true, info.tag);
} else if (info.flags & MemBlockFlags::FREE) {
@ -392,11 +441,6 @@ void FlushPendingMemInfo() {
writeMap.Mark(info.start, info.size, info.ticks, info.pc, true, info.tag);
}
}
pendingNotifies.clear();
pendingNotifyMinAddr1 = 0xFFFFFFFF;
pendingNotifyMaxAddr1 = 0;
pendingNotifyMinAddr2 = 0xFFFFFFFF;
pendingNotifyMaxAddr2 = 0;
}
static inline uint32_t NormalizeAddress(uint32_t addr) {
@ -411,6 +455,9 @@ static inline bool MergeRecentMemInfo(const PendingNotifyMem &info, size_t copyL
for (size_t i = 1; i <= 4; ++i) {
auto &prev = pendingNotifies[pendingNotifies.size() - i];
if (prev.copySrc != 0)
return false;
if (prev.flags != info.flags)
continue;
@ -440,7 +487,7 @@ void NotifyMemInfoPC(MemBlockFlags flags, uint32_t start, uint32_t size, uint32_
bool needFlush = false;
// When the setting is off, we skip smaller info to keep things fast.
if (MemBlockInfoDetailed(size)) {
if (MemBlockInfoDetailed(size) && flags != MemBlockFlags::READ) {
PendingNotifyMem info{ flags, start, size };
info.ticks = CoreTiming::GetTicks();
info.pc = pc;
@ -452,7 +499,7 @@ void NotifyMemInfoPC(MemBlockFlags flags, uint32_t start, uint32_t size, uint32_
memcpy(info.tag, tagStr, copyLength);
info.tag[copyLength] = 0;
std::lock_guard<std::mutex> guard(pendingMutex);
std::lock_guard<std::mutex> guard(pendingWriteMutex);
// Sometimes we get duplicates, quickly check.
if (!MergeRecentMemInfo(info, copyLength)) {
if (start < 0x08000000) {
@ -464,11 +511,15 @@ void NotifyMemInfoPC(MemBlockFlags flags, uint32_t start, uint32_t size, uint32_
}
pendingNotifies.push_back(info);
}
needFlush = pendingNotifies.size() > MAX_PENDING_NOTIFIES;
needFlush = pendingNotifies.size() > MAX_PENDING_NOTIFIES_THREAD;
}
if (needFlush) {
FlushPendingMemInfo();
{
std::lock_guard<std::mutex> guard(flushLock);
flushThreadPending = true;
}
flushCond.notify_one();
}
if (!(flags & MemBlockFlags::SKIP_MEMCHECK)) {
@ -484,6 +535,50 @@ void NotifyMemInfo(MemBlockFlags flags, uint32_t start, uint32_t size, const cha
NotifyMemInfoPC(flags, start, size, currentMIPS->pc, str, strLength);
}
void NotifyMemInfoCopy(uint32_t destPtr, uint32_t srcPtr, uint32_t size, const char *prefix) {
if (size == 0)
return;
bool needsFlush = false;
if (CBreakPoints::HasMemChecks()) {
// This will cause a flush, but it's needed to trigger memchecks with proper data.
char tagData[128];
size_t tagSize = FormatMemWriteTagAt(tagData, sizeof(tagData), prefix, srcPtr, size);
NotifyMemInfo(MemBlockFlags::READ, srcPtr, size, tagData, tagSize);
NotifyMemInfo(MemBlockFlags::WRITE, destPtr, size, tagData, tagSize);
} else if (MemBlockInfoDetailed(size)) {
srcPtr = NormalizeAddress(srcPtr);
destPtr = NormalizeAddress(destPtr);
PendingNotifyMem info{ MemBlockFlags::WRITE, destPtr, size };
info.copySrc = srcPtr;
info.ticks = CoreTiming::GetTicks();
info.pc = currentMIPS->pc;
// Store the prefix for now. The correct tag will be calculated on flush.
truncate_cpy(info.tag, prefix);
std::lock_guard<std::mutex> guard(pendingWriteMutex);
if (destPtr < 0x08000000) {
pendingNotifyMinAddr1 = std::min(pendingNotifyMinAddr1.load(), destPtr);
pendingNotifyMaxAddr1 = std::max(pendingNotifyMaxAddr1.load(), destPtr + size);
} else {
pendingNotifyMinAddr2 = std::min(pendingNotifyMinAddr2.load(), destPtr);
pendingNotifyMaxAddr2 = std::max(pendingNotifyMaxAddr2.load(), destPtr + size);
}
pendingNotifies.push_back(info);
needsFlush = pendingNotifies.size() > MAX_PENDING_NOTIFIES_THREAD;
}
if (needsFlush) {
{
std::lock_guard<std::mutex> guard(flushLock);
flushThreadPending = true;
}
flushCond.notify_one();
}
}
std::vector<MemBlockInfo> FindMemInfo(uint32_t start, uint32_t size) {
start = NormalizeAddress(start);
@ -520,13 +615,15 @@ std::vector<MemBlockInfo> FindMemInfoByFlag(MemBlockFlags flags, uint32_t start,
return results;
}
static const char *FindWriteTagByFlag(MemBlockFlags flags, uint32_t start, uint32_t size) {
static const char *FindWriteTagByFlag(MemBlockFlags flags, uint32_t start, uint32_t size, bool flush = true) {
start = NormalizeAddress(start);
if (pendingNotifyMinAddr1 < start + size && pendingNotifyMaxAddr1 >= start)
FlushPendingMemInfo();
if (pendingNotifyMinAddr2 < start + size && pendingNotifyMaxAddr2 >= start)
FlushPendingMemInfo();
if (flush) {
if (pendingNotifyMinAddr1 < start + size && pendingNotifyMaxAddr1 >= start)
FlushPendingMemInfo();
if (pendingNotifyMinAddr2 < start + size && pendingNotifyMaxAddr2 >= start)
FlushPendingMemInfo();
}
if (flags & MemBlockFlags::ALLOC) {
const char *tag = allocMap.FastFindWriteTag(MemBlockFlags::ALLOC, start, size);
@ -564,22 +661,63 @@ size_t FormatMemWriteTagAt(char *buf, size_t sz, const char *prefix, uint32_t st
return snprintf(buf, sz, "%s%08x_size_%08x", prefix, start, size);
}
size_t FormatMemWriteTagAtNoFlush(char *buf, size_t sz, const char *prefix, uint32_t start, uint32_t size) {
const char *tag = FindWriteTagByFlag(MemBlockFlags::WRITE, start, size, false);
if (tag && strcmp(tag, "MemInit") != 0) {
return snprintf(buf, sz, "%s%s", prefix, tag);
}
// Fall back to alloc and texture, especially for VRAM. We prefer write above.
tag = FindWriteTagByFlag(MemBlockFlags::ALLOC | MemBlockFlags::TEXTURE, start, size, false);
if (tag) {
return snprintf(buf, sz, "%s%s", prefix, tag);
}
return snprintf(buf, sz, "%s%08x_size_%08x", prefix, start, size);
}
static void FlushMemInfoThread() {
while (flushThreadRunning.load()) {
flushThreadPending = false;
FlushPendingMemInfo();
std::unique_lock<std::mutex> guard(flushLock);
flushCond.wait(guard, [] {
return flushThreadPending.load();
});
}
}
void MemBlockInfoInit() {
std::lock_guard<std::mutex> guard(pendingMutex);
std::lock_guard<std::mutex> guard(pendingReadMutex);
std::lock_guard<std::mutex> guardW(pendingWriteMutex);
pendingNotifies.reserve(MAX_PENDING_NOTIFIES);
pendingNotifyMinAddr1 = 0xFFFFFFFF;
pendingNotifyMaxAddr1 = 0;
pendingNotifyMinAddr2 = 0xFFFFFFFF;
pendingNotifyMaxAddr2 = 0;
flushThreadRunning = true;
flushThreadPending = false;
flushThread = std::thread(&FlushMemInfoThread);
}
void MemBlockInfoShutdown() {
std::lock_guard<std::mutex> guard(pendingMutex);
allocMap.Reset();
suballocMap.Reset();
writeMap.Reset();
textureMap.Reset();
pendingNotifies.clear();
{
std::lock_guard<std::mutex> guard(pendingReadMutex);
std::lock_guard<std::mutex> guardW(pendingWriteMutex);
allocMap.Reset();
suballocMap.Reset();
writeMap.Reset();
textureMap.Reset();
pendingNotifies.clear();
}
if (flushThreadRunning.load()) {
std::lock_guard<std::mutex> guard(flushLock);
flushThreadRunning = false;
flushThreadPending = true;
}
flushCond.notify_one();
flushThread.join();
}
void MemBlockInfoDoState(PointerWrap &p) {

Some files were not shown because too many files have changed in this diff Show more