Compare commits

...

265 Commits

Author SHA1 Message Date
TJnotJT
35624a12d9 GS/HW: Refactor StretchRect() to have single entry to renderer/reduce duplication. 2025-11-25 18:38:52 +01:00
JordanTheToaster
9b147cc57c GameDB: FFX-2 FMV FIxes 2025-11-25 17:50:06 +01:00
JordanTheToaster
10e13cfece GS/HW: Test double buffer offset from target base address on lookup 2025-11-25 17:50:06 +01:00
lightningterror
7b2eb7bc47 GS/DX11: Do Stencil date_one in a single pass if there's a barrier.
We currently have a barrier so let's do date_one in a single pass, plus this avoids any issues with copies and stencil issues.
On dx copies are slower so we can only use the optimization if we have barriers already present.
2025-11-25 16:58:27 +01:00
PCSX2 Bot
ab1cb802d8 [ci skip] Qt: Update Base Translation. 2025-11-25 01:01:42 +01:00
TheLastRar
366cdd8df0 Qt: Remove incorrect check in shouldMouseLock() 2025-11-24 18:34:43 -05:00
TheLastRar
bc3cfb1373 Qt: Improve mouse lock DPI handling 2025-11-24 18:34:43 -05:00
SternXD
db6792af2e Achievements/Qt: Show success feedback on RetroAchievements login 2025-11-24 18:19:08 -05:00
SternXD
a1485fb7cd FullscreenUI: Allow pause menu to wrap around 2025-11-24 18:14:44 -05:00
Ty
c72c309218 InputRecording: Use u32 for frames since that's what the core uses (warning fixes) 2025-11-24 18:07:25 -05:00
Ty
58899a9ed3 MTVU: uptr to u32 implicit cast fix 2025-11-24 18:07:25 -05:00
Ty
0823c70460 VTLB: uptr to u32 implicit cast warning fix 2025-11-24 18:07:25 -05:00
Ty
e5c29a3975 BiosTools: s64 to to u32 implicit cast warning fix 2025-11-24 18:07:25 -05:00
Ty
1174ae99c9 GS SW: size_t to int implicit cast warning fixes 2025-11-24 18:07:25 -05:00
Ty
e2d3680038 GSPerfMon: Use int rather than u64 for a frame count to match the rest of the core 2025-11-24 18:07:25 -05:00
Ty
8630893cb1 GIF: implicit cast to u32 warning fix 2025-11-24 18:07:25 -05:00
Ty
53598b970d IopBios: Use typedefs and explicit casting 2025-11-24 18:07:25 -05:00
Ty
89de00ac36 DEV9: Implicit size_t to int conversion warning fix 2025-11-24 18:07:25 -05:00
Ty
d5ddf07958 Counters: u32 implicit cast warning fix 2025-11-24 18:07:25 -05:00
PCSX2 Bot
30dcf4a14a [ci skip] PAD: Update to latest controller database. 2025-11-24 19:11:46 +01:00
JordanTheToaster
a87710e4bc GameDB: Simple 91/ All Star Fighters fixes 2025-11-24 19:11:33 +01:00
JordanTheToaster
a12f87fec2 Github: Use pip install yamllint 2025-11-24 16:57:40 +01:00
JordanTheToaster
8ba9bba094 ImGuiOverlays: Various text changes 2025-11-24 16:57:40 +01:00
JordanTheToaster
1363571c14 GameDB: Fixes of various kinds 2025-11-24 16:57:40 +01:00
chaoticgd
80de666fcc Debugger: Fix crash in getEEThreads 2025-11-24 16:52:15 +01:00
chaoticgd
ff0a2f84fa Config: Initialize AchievementsOptions bitset to zero 2025-11-24 16:12:27 +01:00
JordanTheToaster
0676f145bc Deps: Add additional fixes for Mac OS 11 compat
cheeseus
2025-11-24 16:11:40 +01:00
JordanTheToaster
e19ae2bf60 Deps: Update Qt to 6.10.1 2025-11-24 16:11:40 +01:00
lightningterror
7782d930d5 GS/HW: Always swap DATE with DATE_BARRIER if we have barriers on when alpha write is masked.
This is always enabled on vk/gl but not on dx11/12 as copies are
slow so we can selectively enable it like now.
2025-11-24 16:09:46 +01:00
lightningterror
d1a53fe29b GS/DX11/GL: Move vertices for stencil date in SetupDATE.
The vertices are used only in SetupDATE so let's move them there.
VK/DX12 already do this.
2025-11-24 16:09:46 +01:00
lightningterror
c484cf286c GS/DX11: Don't unbind shader resource if depth stencil view is read only.
We don't need to unbind conflicting srv with dsv if the dsv itself is read only, it is used for depth testing and both can be bind at the same time.

Avoids re binding srv using a read only dsv.
2025-11-24 16:08:50 +01:00
PCSX2 Bot
f8882c4da6 [ci skip] Qt: Update Base Translation. 2025-11-22 06:31:36 +01:00
Ty
52c17e67a5 MipsStackWalk: Fix s64 -> u32 implicit casting warning 2025-11-21 18:20:58 -05:00
Ty
615cd00147 DEV9 / ATA: Fix u64 to u32 implicit casting warning 2025-11-21 18:20:58 -05:00
Ty
cb0bf953d3 Interpreter: Fix warning from reading 64 bit GPR into u32 variable.
The PS2 has 32 bit addresses!
2025-11-21 18:20:58 -05:00
Ty
26a68ef76a GSDumpReplayer: Fix possible memcpy out of bounds read and fix different size implicit casting warnings 2025-11-21 18:20:58 -05:00
Ty
e4c1dc2359 CDVD / Common: Use u32 for PSXCLK instead of u64 (yeah, it fixes warnings) 2025-11-21 18:20:58 -05:00
Ty
40425e3bee Translations: Warning fix, use qsizetype instead of int 2025-11-21 18:20:58 -05:00
Ty
e51e4a35fe MemoryCardSettings: Warning fix, use qsizetype instead of int 2025-11-21 18:20:58 -05:00
Ty
bd1b9ea718 Debugger: size_t -> int warning fix. Switched to u32. 2025-11-21 18:20:58 -05:00
Ty
faaa376232 Console: size_t -> u32 when constructing smallstring warning fix 2025-11-21 18:20:58 -05:00
Ty Lamontagne
e9ca1a6ead Image.cpp: Cast between types instead of using offsetof 2025-11-21 18:20:58 -05:00
Ty Lamontagne
4d3149eacb Host / Cubeb: Use size_t to iterate cubeb_backend_names 2025-11-21 18:20:58 -05:00
Ty Lamontagne
78822c96fb GSVK: Remove unused variable 2025-11-21 18:20:58 -05:00
Ty Lamontagne
a78617b987 Config: Fix GSOptions bitset comparison 2025-11-21 18:20:58 -05:00
Ty Lamontagne
3059ab2b12 QT GameList: Replace deprecated invalidateRowsFilter usage 2025-11-21 18:20:58 -05:00
Ty Lamontagne
1d0f6cc5b7 3rdparty/soundtouch: Fix -Wdeprecated-ofast warning 2025-11-21 18:20:58 -05:00
Ty Lamontagne
38a35043a8 GS/Patches: Explicitly cast non-trivially copyable types during memset / memcpy
Warning fixes
2025-11-21 18:20:58 -05:00
Ty Lamontagne
7385cbe40a Qt: Fix nodiscard warning when opening HTML files for the about dialog 2025-11-21 18:20:58 -05:00
oltolm
9955e07470 Misc: use std::lock_guard 2025-11-20 21:41:45 -05:00
Ty
74db386144 misc: big changes (jk the release machine just broke, need a new release me thinks) 2025-11-19 20:23:42 -05:00
PCSX2 Bot
3f72efeb7a [ci skip] Qt: Update Base Translation. 2025-11-20 01:07:57 +01:00
lightningterror
d0f8905439 Qt: Check if main window exists before updating performance metrics. 2025-11-20 00:06:59 +01:00
TheTechnician27
5a60259ef5 GameListWidget: Overhaul serialization of table header state 2025-11-19 15:06:21 +01:00
SternXD
c8dffccaa7 Image: Support loading bmp files
Signed-off-by: SternXD <stern@sidestore.io>

f
2025-11-19 15:03:44 +01:00
SternXD
87a82b16ff BPM: Add custom background support to Big Picture Mode
Signed-off-by: SternXD <stern@sidestore.io>
2025-11-19 15:03:44 +01:00
KamFretoZ
5666902638 Qt: Improve custom background scaling option 2025-11-19 15:01:29 +01:00
TJnotJT
f1dc232f91 GSRunner: Use correct config name to disable shader cache. 2025-11-19 02:50:01 +01:00
TheLastRar
5476c5a17f Qt: Don't detach surface from container when deleting the surface 2025-11-19 01:46:30 +01:00
lightningterror
9aabb197e6 GS/DX12: Misc Fixes.
Properly unbind slot 0 if previous draw was tex is fb or tex is ds.
Mirrors vk behavior.

Don't recycle draw_rt_clone in colclip, it's null at this point anyway.

Don't bind rt on slot 2 if we have multidraw fb copy enabled.
Mirrors barrier behavior on vk.
2025-11-19 01:45:42 +01:00
PCSX2 Bot
c2488c9269 [ci skip] Qt: Update Base Translation. 2025-11-19 01:03:29 +01:00
TheTechnician27
7e40ab8e7e OSD: More relevant save state timestamps 2025-11-18 18:11:46 +01:00
lightningterror
902b3c5033 GS/HW: Enable barrier date on alpha masked blend case.
If alpha write is masked and barrier/copy enabled then we can switch to DATE_BARRIER since it uses the destination alpha for testing anyway.

By default this is enabled on vk/gl but not on dx11/12 as copies are slow so we can enable it now since rt alpha is read anyway.
2025-11-18 15:19:43 +01:00
TheLastRar
4d1afb9fdd Qt: Handle display surface Drag & Drop events 2025-11-18 15:03:57 +01:00
chaoticgd
4209900351 Debugger: Fix some infinite loops 2025-11-17 07:17:57 +01:00
PCSX2 Bot
780c599b49 [ci skip] Qt: Update Base Translation. 2025-11-17 05:06:56 +01:00
TheLastRar
908d35bf77 Qt: Round window sizes after applying DPI scaling 2025-11-16 22:29:57 +01:00
TheLastRar
cfea84b934 Qt: Use QWindow as display surface 2025-11-16 22:29:57 +01:00
TheTechnician27
e5d94e255b OSD: Eliminate performance overlay flickering via caching 2025-11-16 22:16:38 +01:00
refractionpcsx2
080858b97c GS/HW: Apply native scaling to exact GPU CLUTs 2025-11-16 22:10:22 +01:00
TheLastRar
d883076573 GSRunner: Move more of VM setup to CPU thread 2025-11-16 22:08:13 +01:00
chaoticgd
b80101fbd6 Debugger: Rewrite menu bar layout logic 2025-11-16 22:02:59 +01:00
refractionpcsx2
aca775f8b8 GS/HW: Reintroduce slightly more aggressive region offset for Align to Native 2025-11-16 21:36:14 +01:00
PCSX2 Bot
4f4a26769c [ci skip] Qt: Update Base Translation. 2025-11-16 21:29:11 +01:00
Wes Copeland
d19eaa1b8e Achievements: Support a custom host 2025-11-16 15:27:22 -05:00
TJnotJT
be1af0cd0f GS: Allow dumping draw/frames stats. 2025-11-16 21:26:10 +01:00
TheTechnician27
6ab02e76f1 Docs: Remove Debugger.md 2025-11-15 14:55:28 -05:00
TheTechnician27
f87bc7d72b UI: Make 'Start fullscreen' option work for BPM and improve variable name
Co-authored-by: Geraldi Kusuma Arnanto <981538+aldee@users.noreply.github.com>
2025-11-15 13:37:21 -05:00
chaoticgd
086f4f11e1 Qt: Prevent entering/exiting fullscreen while the VM is locked 2025-11-15 13:31:23 -05:00
TJnotJT
6f54da6234 GS/HW/TC: Do not force temporary source creation for PSMT8 sources that request outside a target valid area. 2025-11-15 16:56:57 +01:00
TJnotJT
44f47f11b8 GS/HW/TC: Force a temporary source creation in edges cases.
Case: When looking up a source, we find a perfect BP hit for a target.
However, the requested area is outside the target's valid area.
Don't use the target direct and instead load from memory in a temporary source.

Co-authored-by: refraction
2025-11-15 16:56:57 +01:00
TJnotJT
b5a2d04b2e GS/HW/TC: Remove outside target PSM check in LookupSource.
Co-autored-by: refraction
2025-11-15 16:56:57 +01:00
TJnotJT
8508ebb7d3 GS/HW: Remove legacy code for changing RT from depth to color in TC invalidation.
Co-authored-by: refraction
2025-11-15 16:56:57 +01:00
lightningterror
3234e45f33 GS/HW: Also check if blend will update the rt.
If it doesn't and there is no depth buffer we can abort the draw(s) safely.
2025-11-15 16:54:12 +01:00
lightningterror
53d1320d83 GS/HW: Re check if RT is written after we know the source alpha. 2025-11-15 16:54:12 +01:00
TheTechnician27
9b545809be Docs: Remove debugger.txt and Debugger.pdf 2025-11-14 20:46:06 -05:00
chaoticgd
79400acf2a Build: Add VectorIntrin.h to precompiled header 2025-11-14 08:03:27 -05:00
PCSX2 Bot
3107c4103a [ci skip] Qt: Update Base Translation. 2025-11-12 19:06:36 -05:00
chaoticgd
68c88f692e Qt: Fix use-after-free in GameSummaryWidget 2025-11-12 18:51:57 -05:00
chaoticgd
df19b37d6d Qt: Fix game list deadlock when changing discs 2025-11-12 18:51:57 -05:00
chaoticgd
1b5c352566 Debugger: Improve symbol tree enum editing 2025-11-12 18:46:10 -05:00
PCSX2 Bot
bed6a9e4d4 [ci skip] Qt: Update Base Translation. 2025-11-12 01:03:38 +01:00
lightningterror
d602ad1d3e GS/HW: Purge no longer needed/replaced crc hacks GSC_ZettaiZetsumeiToshi2, GSC_SteambotChronicles. 2025-11-11 22:36:46 +01:00
refractionpcsx2
51c31347df GS/HW: Remove clamp hack for edge garbage.
Not seemingly required anymore, might be because NS is a thing.
2025-11-11 22:36:46 +01:00
refractionpcsx2
00876e7076 GameDB: Fixes for later IRem games 2025-11-11 22:36:46 +01:00
refractionpcsx2
47eb499893 GS/HW: Add CRC hack to cover IRem games to deal with depth deswizzle shuffle 2025-11-11 22:36:46 +01:00
refractionpcsx2
b8680c3139 GS/HW: Mask channel offsets in channel shuffle heuristic 2025-11-11 22:36:46 +01:00
refractionpcsx2
9b4e3b8f74 GS/HW: Adjust target lookup to better check on source match 2025-11-11 22:36:46 +01:00
refractionpcsx2
3e858167bc Build: Update gamedb documentation and linter 2025-11-11 21:19:02 +01:00
refractionpcsx2
44d66555cc GS/HW: Add options to maintain upscale in Native Scaling
This will adjust how the box filter samples to simulate bilinear when upscaling
2025-11-11 21:19:02 +01:00
refractionpcsx2
c5438ceca3 GS/HW: Fix up invalidly selected Tex in RT targets 2025-11-11 20:58:35 +01:00
JordanTheToaster
3e1927ae44 GameDB: Ore no Shita de Agake fixes 2025-11-11 14:59:04 +01:00
TheTechnician27
b688117002 RTC: Automatically detect timezone 2025-11-10 20:14:36 -06:00
dependabot[bot]
e62e6fb6c3 Bump softprops/action-gh-release in the ci-deps group
Bumps the ci-deps group with 1 update: [softprops/action-gh-release](https://github.com/softprops/action-gh-release).


Updates `softprops/action-gh-release` from 2.4.1 to 2.4.2
- [Release notes](https://github.com/softprops/action-gh-release/releases)
- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
- [Commits](6da8fa9354...5be0e66d93)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-version: 2.4.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: ci-deps
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-10 19:28:32 -05:00
PCSX2 Bot
262e94e5d7 [ci skip] PAD: Update to latest controller database. 2025-11-10 20:01:35 +01:00
PCSX2 Bot
11a4b4e7ff [ci skip] Qt: Update Base Translation. 2025-11-10 01:07:34 +01:00
chaoticgd
a98cfcf28c CDVD: Synchronise access from different threads 2025-11-09 09:27:27 -05:00
PCSX2 Bot
d02f30ee62 [ci skip] Qt: Update Base Translation. 2025-11-08 18:23:15 -06:00
JordanTheToaster
c0bf01a646 MacOS: Patch Tahoe unclickable buttons 2025-11-08 08:31:46 -05:00
TheTechnician27
babb985e9e GS Hotkeys: Improve upscale and downscale hotkeys 2025-11-07 20:28:27 -05:00
PCSX2 Bot
e379c8317d [ci skip] Qt: Update Base Translation. 2025-11-07 03:49:56 +01:00
chaoticgd
5098277474 Debugger: Fix 1 pixel gap under layout tabs on non-Windows platforms 2025-11-06 09:32:29 -05:00
TheLastRar
4a94cb6cbd DEV9: Remove using namespace in TAPAdapter header 2025-11-05 16:44:10 -05:00
TheTechnician27
e245454b91 GameListWidget: Fix icon cell highlights and column widths 2025-11-05 16:43:28 -05:00
TheTechnician27
b003eadd2d VMManager: More helpful error message on no BIOS present 2025-11-03 12:52:28 -05:00
JordanTheToaster
a5984d8213 Flatpak: Update KDE runtime to 6.10 2025-11-03 12:49:35 -05:00
Stern
4dbd95b0bb labeler: Add OSD / ImGui labeler 2025-11-03 10:38:26 -05:00
JordanTheToaster
68803229da Core: Bump savestate version.
[SAVEVERSION+]
2025-11-02 20:19:38 -05:00
chaoticgd
b661a2a149 Deps: Update KDDockWidgets to 2.4.0 2025-11-02 15:02:25 -05:00
PCSX2 Bot
63cd355d7a [ci skip] Qt: Update Base Translation. 2025-11-02 14:43:04 -05:00
Ziemas
0f5ff68679 SPU: Remove KON delay/check key once per T 2025-11-02 14:36:54 -05:00
JordanTheToaster
bd74921926 Deps: Update SDL3 to v3.2.26 2025-11-01 11:32:32 -04:00
TheLastRar
78f83514f4 Deps: Force lib directory for libjpeg-turbo build 2025-11-01 11:31:20 -04:00
TheTechnician27
2c36259b88 DB: Remove some unsupported or nonexistent serials 2025-11-01 11:29:27 -04:00
TheTechnician27
32a0bed6af GameListModel: Touch up a couple functions 2025-11-01 11:28:59 -04:00
TheTechnician27
d415f8364c Snapshots: Clean up per-game snapshots folder code 2025-11-01 11:14:55 -04:00
Ty Lamontagne
7c768b6833 Qt: Mouse Lock: Add warning about mixed-resolution non 100% DPI configs 2025-11-01 10:21:54 -04:00
Ty Lamontagne
773f6968a4 Qt: Implement mouse locking when rendering to separate window 2025-11-01 10:21:54 -04:00
Ty Lamontagne
1021199512 Qt: Visually disable the mouse lock button on wayland 2025-11-01 10:21:54 -04:00
Ty Lamontagne
08ef9e2bd9 Qt: Make mouse screen locking DPI aware
Also removed some global mouse hook stuff. Don't ever want to use that anyways.
2025-11-01 10:21:54 -04:00
refractionpcsx2
6ba3f96f27 GS/MAD: Try to reconstruct centre line if previous version was weaved in motion 2025-11-01 09:29:09 -04:00
TheTechnician27
7d5b7bc3ce FUNDING.yml: Add Liberapay 2025-11-01 09:20:57 -04:00
TheTechnician27
0d43d30346 OSD: Fix uninitialized save state slot timestamp 2025-10-31 22:19:07 -04:00
PCSX2 Bot
da824b4e9e [ci skip] Qt: Update Base Translation. 2025-10-31 01:03:12 +01:00
chaoticgd
ed08b5f34e Memcard: Fix infinite loop caused by UB when YAML parsing fails 2025-10-30 14:02:55 -04:00
chaoticgd
0ce312c1c3 GameDB: Fix infinite loops caused by UB when YAML parsing fails 2025-10-30 14:02:55 -04:00
PCSX2 Bot
07bc2fa452 [ci skip] Qt: Update Base Translation. 2025-10-28 01:19:58 +01:00
TheTechnician27
bfd2775074 Qt: Add TextBrowserInteraction to labels where appropriate 2025-10-28 00:36:00 +01:00
TheTechnician27
94ccafd745 MainWindow: Make Settings > Video Capture checkbox toggle correctly 2025-10-27 19:25:25 -04:00
KamFretoZ
5c6049c4ae Qt: Add Create game shortcut functionality 2025-10-28 00:11:09 +01:00
PCSX2 Bot
090464c42d [ci skip] PAD: Update to latest controller database. 2025-10-27 18:43:11 +01:00
PCSX2 Bot
89a00db3d6 [ci skip] Qt: Update Base Translation. 2025-10-27 14:08:50 +01:00
dependabot[bot]
fc415dff93 GHActions: Update action scripts
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-27 08:36:36 -04:00
TheTechnician27
f648a9a438 QtHost: Fix locale crash on non-Windows machines 2025-10-25 17:58:00 -04:00
chaoticgd
cac6669423 Debugger: Respond to OS colour scheme changes 2025-10-25 09:48:18 -04:00
TheTechnician27
7db487a49b Game List: Improve dialog for Reset Play Time 2025-10-25 15:32:52 +02:00
TJnotJT
c96607fe37 GSRunner: Allow loading GS settings from INI. 2025-10-24 21:44:52 -04:00
SternXD
ba0dae5f57 ImGui: Rename BackgroundProgressDialog to ProgressDialog
Signed-off-by: SternXD <stern@sidestore.io>
2025-10-24 21:41:42 -04:00
SternXD
5fe5148e86 BPM: Add Network and HDD settings page
Signed-off-by: SternXD <stern@sidestore.io>
2025-10-24 21:41:42 -04:00
TJnotJT
5445cb516a GS: Handle zero clears separately in transfer dump. 2025-10-24 21:40:37 -04:00
PCSX2 Bot
84a29ffcca [ci skip] Qt: Update Base Translation. 2025-10-25 02:03:31 +02:00
PCSX2 Bot
e56075976f [ci skip] Qt: Update Base Translation. 2025-10-24 15:53:19 -04:00
SternXD
0f709735c0 FullscreenUI: Add footer for clear bindings
Signed-off-by: SternXD <stern@sidestore.io>
2025-10-24 15:53:05 -04:00
SternXD
ad3f0fd6cd ImGuiFullscreen: Add append and queue footer hints
Signed-off-by: SternXD <stern@sidestore.io>
2025-10-24 15:53:05 -04:00
TheTechnician27
fbfdacd589 Game Grid: Set Size Hint for List View 2025-10-24 15:50:44 -04:00
chaoticgd
aedc51e151 Qt: Set QStyleHints colorScheme property properly 2025-10-23 20:17:23 -04:00
TheTechnician27
fe95a697f4 GameListModel: Remove dead, nonsense switch case 2025-10-23 19:52:38 -04:00
TheTechnician27
f99cf28429 MainWindow: Add a separator between Achievements and Controllers 2025-10-23 10:38:43 -04:00
JordanTheToaster
bea1eb0cf9 MacOS: Fix for missing Metal Toolchain 2025-10-23 09:03:43 -04:00
Silent
3cf21e0ab6 usb_eyetoy: Clean up COM initialization, prevent a double-release
If the EyeToy camera was enabled in the settings, but not enabled
by the game, it would call CoUninitialize without having called
CoInitializeEx first.
2025-10-22 09:48:06 -04:00
Silent
521b32c253 USB: Initialize COM before using Cubeb on Windows 2025-10-22 09:48:06 -04:00
Silent
190b525ca6 CubebAudioStream: Call CoInitializeEx before creating cubeb 2025-10-22 09:48:06 -04:00
PCSX2 Bot
baa00e4d38 [ci skip] Qt: Update Base Translation. 2025-10-22 13:37:14 +02:00
TheTechnician27
51bc6c1465 pcsx2-qt: Add buddies and tabstops 2025-10-21 18:48:54 -04:00
JordanTheToaster
ea8492082a Windows: Optimize Qt build for size 2025-10-21 12:06:14 -04:00
dependabot[bot]
9d1fd23d78 Bump the ci-deps group with 2 updates
Bumps the ci-deps group with 2 updates: [flatpak/flatpak-github-actions](https://github.com/flatpak/flatpak-github-actions) and [actions/setup-node](https://github.com/actions/setup-node).


Updates `flatpak/flatpak-github-actions` from 6.5 to 6.6
- [Release notes](https://github.com/flatpak/flatpak-github-actions/releases)
- [Commits](https://github.com/flatpak/flatpak-github-actions/compare/v6.5...92ae9851ad316786193b1fd3f40c4b51eb5cb101)

Updates `actions/setup-node` from 5 to 6
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v5...v6)

---
updated-dependencies:
- dependency-name: flatpak/flatpak-github-actions
  dependency-version: '6.6'
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: ci-deps
- dependency-name: actions/setup-node
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: ci-deps
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-21 11:44:24 -04:00
TellowKrinkle
9ff575377d GHActions: Reduce ccache max size
Reduces the amount of GH cache quota they take up.

[ci skip]
2025-10-21 11:43:37 -04:00
JordanTheToaster
47250293cd Mac OS: Use Xcode 26.0.1 2025-10-20 19:01:03 -04:00
JordanTheToaster
f9b346521d Mac OS: Bump runner to Mac OS 26 2025-10-20 19:01:03 -04:00
TheTechnician27
b5a4c71eff RTC: Add option to choose between system locale and yyyy-MM-dd HH:mm:ss 2025-10-20 18:20:41 +02:00
JordanTheToaster
2604256424 GameDB: Various fixes 2025-10-20 17:49:22 +02:00
chaoticgd
906ac6a8ea Patch: Fix some incorrect format strings 2025-10-20 15:38:33 +02:00
TheTechnician27
3db21e0579 FSUI: Remove defunct option and move OSD position options 2025-10-19 22:29:31 -05:00
PCSX2 Bot
abd03884de [ci skip] Qt: Update Base Translation. 2025-10-19 20:02:42 -05:00
TheLastRar
a159256b07 FSUI: Allow specifying default button in message dialogs 2025-10-18 02:26:09 +02:00
Silent
d9a2618b7a Achievements: Use the actual image names for cache
Fixes an issue where PCSX2 cache was unaware of the game or achievement
icon getting updated in the backend, and it continued to show
the old image
2025-10-18 02:24:04 +02:00
PCSX2 Bot
dca0291cfb [ci skip] Qt: Update Base Translation. 2025-10-18 02:23:19 +02:00
JordanTheToaster
b261873471 GameDB: Wild Arms 5 SPS fix 2025-10-18 02:23:03 +02:00
TellowKrinkle
bfd01c913a macOS: Update to Qt 6.10.0 2025-10-17 18:11:04 -04:00
TellowKrinkle
818b3fe779 macOS: Update to Qt 6.9.3 2025-10-17 18:11:04 -04:00
TellowKrinkle
66a28e4488 macOS: Optimize Qt build for size
Saves a MB or so, there's no reason we need speed optimizations in our GUI toolkit
2025-10-17 18:11:04 -04:00
TellowKrinkle
28e2ecf920 macOS: Fix parallel kddockwidgets build for universal dependencies 2025-10-17 18:11:04 -04:00
TellowKrinkle
d3dbf53fa7 MacOS: Make universal build script executable 2025-10-17 18:11:04 -04:00
SternXD
8fb2940f25 Qt/BPM: Improve memory card shutdown warning clarity and safety emphasis 2025-10-15 23:48:35 -05:00
chaoticgd
55498762f9 Qt: Fix typo in Windows dependencies build script 2025-10-15 18:07:05 -04:00
JordanTheToaster
50baaf39d6 Deps: Update Mac OS ffmpeg to 8.0 2025-10-15 08:32:19 -04:00
lightningterror
4743ccac8c GS/HW/Shaders: Fix afail shader typo.
No dual source blend means PS_NO_COLOR1 is true, no output on target 1.
2025-10-15 00:57:33 +02:00
lightningterror
0dc3fc6228 GS/HW: Unify blend levels when barriers are and are not supported.
When barriers/multidraw fb copy is supported no change.
When barriers/multidraw fb copy isn't supported keep the same levels but only enable them if there's no overlap.
2025-10-14 02:19:43 +02:00
lightningterror
a7a4583c84 GS/HW: Enable any fbmask emulation if Blending is above minimum.
Since dx11/12 now supports sw blending and copies/draw area/quads optimizations have been made perf impact will be lower so we can use the same unified path if texture barriers are disabled.
2025-10-14 02:19:43 +02:00
lightningterror
6b52937262 GS/TC: Make sure target is created before setting any flags.
Xenosaga 3 fails to create a target because the rect is empty so we should avoid setting any flags since it's null.
2025-10-14 02:19:08 +02:00
PCSX2 Bot
df9caf6fb8 [ci skip] Qt: Update Base Translation. 2025-10-14 02:17:24 +02:00
Jordan
be5f8d2e60 GameDB: Various fixes for issues in Xenosaga 3 HyperSonic Xtreme Jeopardy and Jak 3. 2025-10-13 21:56:30 +02:00
Sean
b0e01ca518 Debugger: Floating point display in memory view (#13362) 2025-10-13 12:51:37 -04:00
TheTechnician27
4e85272393 Settings: Remove obsolete transitionary function 2025-10-13 12:47:20 -04:00
dependabot[bot]
bffed9a839 [ci skip] CI: Bump softprops/action-gh-release from 2.3.4 to 2.4.1 (#13383)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-13 08:49:29 -04:00
PCSX2 Bot
7ed0c0d031 [ci skip] Qt: Update Base Translation. 2025-10-13 10:38:02 +02:00
TheTechnician27
ae6e4c98f6 AudioSettingsWidget: Disambiguate overloaded volume name 2025-10-12 17:17:59 -05:00
TheTechnician27
38cfa9912d SPU2: Fix interface for output muting 2025-10-12 17:17:59 -05:00
JordanTheToaster
b02318e2b6 Misc: Qt 6.10.0 Build Fixes 2025-10-12 00:39:34 -05:00
JordanTheToaster
db45263221 Deps: Update Windows and Linux to Qt 6.10.0 2025-10-12 00:39:34 -05:00
JordanTheToaster
3fd9625f1c 3rdparty: Update rcheevos v12.1.0 2025-10-11 20:17:13 +02:00
JordanTheToaster
2978b6050d GameDB: Black adjust HPO 2025-10-11 20:05:44 +02:00
PCSX2 Bot
274acddcf1 [ci skip] Qt: Update Base Translation. 2025-10-11 20:04:52 +02:00
JordanTheToaster
8dffc85707 Misc: Qt 6.10.0 Build Fixes 2025-10-10 23:20:48 -05:00
lightningterror
f799631a70 Qt: Update dx12 info.
Allow texture barrier override, update blending tooltip.
2025-10-10 08:10:31 +02:00
lightningterror
d744f0dfeb GS/DX12: Backport multidraw fb copy from dx11 to dx12. 2025-10-10 08:10:31 +02:00
Ty
bdb8de6d3b translations: Syncing Crowdin translations (#13372) 2025-10-09 16:37:44 -04:00
TJnotJT
e751f367ca GS/SW: Rewrite vertex ST values if they might overflow the rasterizer fixed point format. 2025-10-09 19:42:27 +02:00
TJnotJT
13f2d87ba8 GS: Add ST NaN tracking to the vertex trace.
Add fields that will trace if any of the S/Q or T/Q values is NaN. Ignore any NaN ST values while calculating min/max.
2025-10-09 19:42:27 +02:00
TheLastRar
3cac2cf7c2 Qt: Add null check for gamelist icons 2025-10-09 16:57:55 +02:00
TheLastRar
a67409bfc6 Deps: Patch SDL3 to fix erroneous uninitialise 2025-10-09 12:53:40 +02:00
lightningterror
6a556c9968 GS/HW: Fix manual de swizzle -Wsign-compare warnings. 2025-10-09 03:28:00 +02:00
TJnotJT
4d93285ca2 GS: Use triangle quad detection in primitive overlap detection.
Coauthored-by: TellowKrinkle
2025-10-09 03:26:08 +02:00
Ty
98c35a308d ci: Set NPROCS in the linux gs runner dependency script 2025-10-08 16:24:15 -04:00
Ty Lamontagne
7da97e6d80 cmake/ci: Allow gs runner builds without building all of pcsx2-qt 2025-10-08 15:26:34 -04:00
Ty Lamontagne
edf686752a GSRunner: Support GNU/Linux 2025-10-08 15:26:34 -04:00
lightningterror
b7e17646a3 GS/DX12: Backport dx11 full rt copy optimizations.
We are copying the whole RT so just call CopyResource instead of CopyTextureRegion which will be faster.
2025-10-08 02:24:32 +02:00
JordanTheToaster
3f437e7496 3rdparty: Update fmt to 12.0.0 2025-10-07 15:12:49 +02:00
lightningterror
7ab6c62dee GS/HW: Update manual deswizzle detection.
Check if a draw is either a palette or 32bit depth format, also check for swizzle format is 16 or 32bit.

Expand/update quadrant detection, use the quadrant size of what it should be
from the page size and compare it to the actual quadrant size from the vertex positions.
2025-10-07 11:48:32 +02:00
lightningterror
2a7bf35f20 GS/TC: Point m_from_target to new target if it's also a source in CombineAlignedInsideTargets.
Issue is it was erasing the source which doesn't get passed back to the main draw function,
so we should be setting m_from_target to either new target or null because it is linked.
2025-10-07 11:48:32 +02:00
TJnotJT
08552a83cc GS/SW: Clamp colors, Z, fog to allowed ranges. 2025-10-07 07:34:26 +02:00
TJnotJT
ee417ee4c5 GS/SW: Silence code analyzer warnings. 2025-10-07 07:34:26 +02:00
TJnotJT
869837f5f0 GS/SW: Accurate rounding for points. 2025-10-07 07:34:26 +02:00
TJnotJT
7ab6bac39a GS/SW: Improve accuracy of line drawing and edge antialiasing. 2025-10-07 07:34:26 +02:00
dependabot[bot]
e388294004 [ci skip] GitHub Action updates
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-06 18:46:46 -04:00
JordanTheToaster
bba5ef67ff GameDB: Various fixes 2025-10-06 18:45:20 -04:00
JordanTheToaster
83dc2de6ca GameDB: Hot Wheels Beat That fixes 2025-10-05 14:55:22 -04:00
PCSX2 Bot
f47fbda0e6 [ci skip] Qt: Update Base Translation. 2025-10-04 02:02:43 +02:00
KamFretoZ
d34d2af849 OSD: Add ability to show the amount of activated patches 2025-10-04 01:59:41 +02:00
JordanTheToaster
db92af8ca4 Deps: Update SDL3 to v3.2.24 2025-10-04 01:56:36 +02:00
lightningterror
47897fc2be GS/TC: Add caching to block offset calculation for TranslateAlignedRectByPage and DirtyRectByPage. 2025-10-04 01:55:52 +02:00
lightningterror
b451602f69 GS/TC: Check for y offset when calculating block offset.
Make sure the rect starts at the beginning.
2025-10-04 01:55:52 +02:00
lightningterror
96ac35a4bd GS/TC: Optimize block offset calculations in ComputeSurfaceOffset.
Same deal as before, swap the fast linear loops to be second since they will be faster.
2025-10-04 01:55:52 +02:00
lightningterror
ddd33a0701 GS/HW: Make sure we use date barrier on sw fbmask instead of primid date.
Edge case when we have sw fbmask but no sw blending.
2025-10-04 01:55:07 +02:00
lightningterror
d5e4a1c896 GS/DX11: Add missing write_rg shader bit for date barrier on texture shuffle. 2025-10-03 07:39:52 +02:00
lightningterror
9674fd773c GS/DX12/VK: Fix CopyRect partial clear regression.
Partial clears texture type should be either render target or depth stencil.
2025-10-03 07:39:52 +02:00
lightningterror
01e916b132 GS/HW: Remove rebinding read only dsv after framebuffer optimizations on dx11.
The condition would never be triggered so useless.

Clean up SendHWDraw across renderers.

Recycle tex for rt copy if colclip tex fails.

Set flags for colclip CreateRenderTarget clear to false on dx11, consistent across renderers.
2025-10-03 07:39:52 +02:00
lightningterror
1867791653 GS/DX12: Backport depth test and sampling from dx11.
Unlike in dx11 where we create a separate dsv as read only we can transition the current dsv as read only which is cleaner.
2025-10-03 07:39:52 +02:00
JordanTheToaster
bc11ff0571 3rdparty: Update cubeb to e495bee 2025-10-01 19:11:12 -04:00
lightningterror
e550cf9b63 GS/TC: Optimize block offset calculation.
Swap offset x and y loops:
Having the y loop first allows for better optimizations/caching since it's bigger.
Also optimize start point loop conditions for target rect.
2025-09-30 22:01:46 +02:00
TJnotJT
bafd87694c GS/HW: Unify barrier handling in DX11/GL/VK/MTL.
Make sure the one_barrier/full_barrier flags are passed correctly depending on the pass (main or alpha second pass).

Check that the drawlist is non-empty when a full barrier is requested.
2025-09-30 05:53:59 +02:00
TJnotJT
caf6298ad1 GS/DX11: Unify batched/per-prim multidraw and do partial copies for barriers. 2025-09-30 05:53:59 +02:00
TJnotJT
7f0d287512 GS: Refactor drawlist saving for full barrier multidraw.
Get drawlist when needing full barriers and API doesn't support texture barrier.

Make drawlist computation lazy (only computed when will be used).

When API does not provide barriers get bbox of batched draws.
2025-09-30 05:53:59 +02:00
PCSX2 Bot
2177de2238 [ci skip] Qt: Update Base Translation. 2025-09-30 02:19:21 +02:00
Ty
32973c746e CI: Use pull_request_target for deps dispatch 2025-09-29 20:05:27 -04:00
xujibbs
3bf94330ef CDVD: Make IsoHasher string translatable 2025-09-29 11:50:12 -04:00
Ty
34ae3b1eb0 [ci skip] CI: Use GITHUB_TOKEN to dispatch actions onto windows deps 2025-09-29 11:47:49 -04:00
JordanTheToaster
b9288d4845 Deps: Update harfbuzz to 12.0.0 2025-09-29 11:40:11 -04:00
PCSX2 Bot
a13cb32eb1 [ci skip] Qt: Update Base Translation. 2025-09-29 04:42:45 +02:00
Ty
9d72ad785d CI: Automatically build windows deps when PRs change them 2025-09-28 18:58:56 -04:00
JordanTheToaster
0afde446c4 Deps: Update kddockwidgets to v2.3.0 2025-09-28 18:15:23 -04:00
SternXD
49111a6fbe Achievements: Harden RetroAchievements Hardcore Compliance
Fixes #13273
 - Totally block save state loading, frame advance, and disable cheats in hardcore
 - Disable load state UI buttons when hardcore mode active (leave message telling them if they want to load state if the user somehow gets past this)
 - Add consistent OSD messaging for all hardcore restrictions

Signed-off-by: SternXD <stern@sidestore.io>
2025-09-28 18:14:33 -04:00
sean
251140fd52 Debugger: Store validated copies of pasted instructions 2025-09-28 17:53:13 -04:00
sean
f57c7d216c Debugger: Validate new instructions before pasting 2025-09-28 17:53:13 -04:00
sean
36157402c8 Debugger: Move position of paste instruction text in context menu 2025-09-28 17:53:13 -04:00
recursean
51ee8f4015 Debugger: start context function for multi-instruction paste 2025-09-28 17:53:13 -04:00
JordanTheToaster
fb85bb9076 GameDB: Various fixes 2025-09-26 20:36:56 +02:00
PCSX2 Bot
0a391e2407 [ci skip] Qt: Update Base Translation. 2025-09-26 03:36:33 +02:00
chaoticgd
52f03c900f Debugger: Improve logic for applying custom font sizes 2025-09-25 18:26:20 -04:00
lightningterror
74be344ce6 GS/Debug: Add log for CopyRect if rect is empty. 2025-09-26 00:10:19 +02:00
Ty
14574ef4eb CI: Update Flatpak builder action version 2025-09-25 18:09:29 -04:00
TheTechnician27
fe565afff0 GameList: Prioritize file title over serial for covers 2025-09-25 18:08:50 -04:00
TellowKrinkle
5fcd0f94c2 GS:Capture: Allow selecting hardware device 2025-09-25 18:07:37 -04:00
TheTechnician27
e22609ea29 QtHost: Fix verbose status toggle 2025-09-25 18:00:22 -04:00
chaoticgd
1d068ffde9 Patch: Clear R5900 recompiler cache after updating dynamic patches 2025-09-25 11:08:01 -04:00
364 changed files with 274138 additions and 207001 deletions

1
.github/FUNDING.yml vendored
View File

@@ -1,3 +1,4 @@
# These are supported funding model platforms
github: [PCSX2]
liberapay: PCSX2

11
.github/labeler.yml vendored
View File

@@ -22,6 +22,10 @@
- '3rdparty/**/*'
- '**/3rdpartyDeps.props'
- '.gitmodules'
'requires-win-deps-build':
- changed-files:
- any-glob-to-any-file:
- '.github/workflows/scripts/windows/*'
'Documentation':
- changed-files:
- any-glob-to-any-file:
@@ -36,6 +40,13 @@
- 'pcsx2-qt/**/*'
- '3rdparty/Qt/*'
- '3rdparty/Qt/**/*'
'OSD / ImGui':
- changed-files:
- any-glob-to-any-file:
- 'pcsx2/ImGui/*'
- 'pcsx2/ImGui/**/*'
- '3rdparty/imgui/*'
- '3rdparty/imgui/**/*'
'GameDB':
- changed-files:
- any-glob-to-any-file:

View File

@@ -23,7 +23,7 @@ jobs:
- name: Install Packages
run: |
npm install -g ajv-cli prettier
sudo apt-get -y install yamllint
pip install yamllint
- name: Validate YAML
run: |

View File

@@ -45,7 +45,7 @@ jobs:
name: ${{ inputs.jobName }}
runs-on: ${{ inputs.os }}
container:
image: ghcr.io/flathub-infra/flatpak-github-actions:kde-6.7
image: ghcr.io/flathub-infra/flatpak-github-actions:kde-6.9
options: --privileged
timeout-minutes: 60
@@ -93,7 +93,7 @@ jobs:
- name: Build Flatpak (beta)
if: ${{ inputs.stableBuild == false || inputs.stableBuild == 'false' }}
uses: flatpak/flatpak-github-actions/flatpak-builder@10a3c29f0162516f0f68006be14c92f34bd4fa6c
uses: flatpak/flatpak-github-actions/flatpak-builder@92ae9851ad316786193b1fd3f40c4b51eb5cb101
with:
bundle: ${{ steps.artifact-metadata.outputs.artifact-name }}.flatpak
upload-artifact: false
@@ -109,7 +109,7 @@ jobs:
- name: Build Flatpak (stable)
if: ${{ inputs.stableBuild == true || inputs.stableBuild == 'true' }}
uses: flatpak/flatpak-github-actions/flatpak-builder@10a3c29f0162516f0f68006be14c92f34bd4fa6c
uses: flatpak/flatpak-github-actions/flatpak-builder@92ae9851ad316786193b1fd3f40c4b51eb5cb101
with:
bundle: ${{ steps.artifact-metadata.outputs.artifact-name }}.flatpak
upload-artifact: false
@@ -129,7 +129,7 @@ jobs:
- name: Push to Flathub (beta)
if: ${{ inputs.publish == true && (inputs.stableBuild == false || inputs.stableBuild == 'false') }}
uses: flatpak/flatpak-github-actions/flat-manager@10a3c29f0162516f0f68006be14c92f34bd4fa6c
uses: flatpak/flatpak-github-actions/flat-manager@92ae9851ad316786193b1fd3f40c4b51eb5cb101
with:
flat-manager-url: https://hub.flathub.org/
repository: beta
@@ -138,7 +138,7 @@ jobs:
- name: Push to Flathub (stable)
if: ${{ inputs.publish == true && (inputs.stableBuild == true || inputs.stableBuild == 'true') }}
uses: flatpak/flatpak-github-actions/flat-manager@10a3c29f0162516f0f68006be14c92f34bd4fa6c
uses: flatpak/flatpak-github-actions/flat-manager@92ae9851ad316786193b1fd3f40c4b51eb5cb101
with:
flat-manager-url: https://hub.flathub.org/
repository: stable
@@ -153,7 +153,7 @@ jobs:
mv "./${{ steps.artifact-metadata.outputs.artifact-name }}.flatpak" "$GITHUB_WORKSPACE"/ci-artifacts/
- name: Upload artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: ${{ steps.artifact-metadata.outputs.artifact-name }}
path: ci-artifacts

View File

@@ -55,7 +55,7 @@ jobs:
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_COMPRESS: true
CCACHE_COMPRESSLEVEL: 9
CCACHE_MAXSIZE: 500M
CCACHE_MAXSIZE: 100M
steps:
- name: Checkout Repository
@@ -174,7 +174,7 @@ jobs:
- name: Upload artifact
if: inputs.buildAppImage == true
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: ${{ steps.artifact-metadata.outputs.artifact-name }}
path: ci-artifacts

View File

@@ -12,7 +12,7 @@ on:
os:
required: false
type: string
default: macos-15
default: macos-26
patchesUrl:
required: false
type: string
@@ -42,7 +42,7 @@ jobs:
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_COMPRESS: true
CCACHE_COMPRESSLEVEL: 9
CCACHE_MAXSIZE: 500M
CCACHE_MAXSIZE: 100M
# Only way to use a secret in an if statement
SIGN_KEY: ${{ secrets.APPLE_SIGN_P12_B64 }}
@@ -62,8 +62,11 @@ jobs:
echo "#define DEFAULT_UPDATER_CHANNEL \"stable\"" > ./pcsx2-qt/DefaultUpdaterChannel.h
cat ./pcsx2-qt/DefaultUpdaterChannel.h
- name: Use Xcode 16.4
run: sudo xcode-select -s /Applications/Xcode_16.4.app
- name: Use Xcode 26.0.1
run: sudo xcode-select -s /Applications/Xcode_26.0.1.app
- name: Install Metal Toolchain
run: xcodebuild -downloadComponent MetalToolchain
- name: Prepare Artifact Metadata
id: artifact-metadata
@@ -91,7 +94,7 @@ jobs:
uses: actions/cache@v4
with:
path: ~/deps
key: ${{ inputs.os }} deps ${{ hashFiles('.github/workflows/scripts/macos/build-dependencies.sh', '.github/workflows/scripts/common/*.patch') }}
key: ${{ inputs.os }} deps ${{ hashFiles('.github/workflows/scripts/macos/*', '.github/workflows/scripts/common/*.patch') }}
- name: Build Dependencies
if: steps.cache-deps.outputs.cache-hit != 'true'
@@ -194,7 +197,7 @@ jobs:
cp "${{ steps.artifact-metadata.outputs.artifact-name }}.tar.xz" ci-artifacts/macOS.tar.xz
- name: Upload Artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: ${{ steps.artifact-metadata.outputs.artifact-name }}
path: "*.tar.xz"

View File

@@ -68,7 +68,7 @@ jobs:
mv ./release-notes.md ${GITHUB_WORKSPACE}/release-notes.md
- name: Create a GitHub Release (Manual)
uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836
uses: softprops/action-gh-release@5be0e66d93ac7ed76da52eca8bb058f665c3a5fe
if: steps.tag_version.outputs.new_tag && github.event_name == 'workflow_dispatch'
with:
body_path: ./release-notes.md
@@ -77,7 +77,7 @@ jobs:
tag_name: ${{ steps.tag_version.outputs.new_tag }}
- name: Create a GitHub Release (Push)
uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836
uses: softprops/action-gh-release@5be0e66d93ac7ed76da52eca8bb058f665c3a5fe
if: steps.tag_version.outputs.new_tag && github.event_name != 'workflow_dispatch'
with:
body_path: ./release-notes.md
@@ -168,7 +168,7 @@ jobs:
- name: Prepare Artifact Folder
run: mkdir ./ci-artifacts/
- uses: actions/download-artifact@v5
- uses: actions/download-artifact@v6
name: Download all Artifacts
with:
path: ./ci-artifacts/
@@ -203,7 +203,7 @@ jobs:
echo "TAG_VAL=${TAG_VAL}"
gh release edit ${TAG_VAL} --draft=false --repo PCSX2/pcsx2
- uses: actions/setup-node@v5
- uses: actions/setup-node@v6
with:
node-version: 22

View File

@@ -1,13 +0,0 @@
diff --git a/src/core/indicators/ClassicDropIndicatorOverlay.h b/src/core/indicators/ClassicDropIndicatorOverlay.h
index 2dfb9718a..9b01f002e 100644
--- a/src/core/indicators/ClassicDropIndicatorOverlay.h
+++ b/src/core/indicators/ClassicDropIndicatorOverlay.h
@@ -11,7 +11,7 @@
#pragma once
-#include "core/DropIndicatorOverlay.h"
+#include <kddockwidgets/core/DropIndicatorOverlay.h>
namespace KDDockWidgets {

View File

@@ -50,12 +50,6 @@ declare -a MANUAL_LIBS=(
"libfreetype.so.6"
)
declare -a REMOVE_LIBS=(
'libwayland-client.so*'
'libwayland-cursor.so*'
'libwayland-egl.so*'
)
set -e
LINUXDEPLOY=./linuxdeploy-x86_64.AppImage
@@ -130,7 +124,7 @@ echo "Running linuxdeploy to create AppDir..."
# Interestingly, specifying the module doesn't copy the module, only the required plugins for it
# https://github.com/linuxdeploy/linuxdeploy-plugin-qt/issues/160#issuecomment-2655543893
EXTRA_QT_MODULES="core;gui;svg;waylandclient;waylandcompositor;widgets;xcbqpa" \
EXTRA_PLATFORM_PLUGINS="libqwayland-egl.so;libqwayland-generic.so" \
EXTRA_PLATFORM_PLUGINS="libqwayland.so" \
DEPLOY_PLATFORM_THEMES="1" \
QMAKE="$DEPSDIR/bin/qmake" \
NO_STRIP="1" \
@@ -140,16 +134,6 @@ $LINUXDEPLOY --plugin qt --appdir="$OUTDIR" --executable="$BUILDDIR/bin/pcsx2-qt
echo "Copying resources into AppDir..."
cp -a "$BUILDDIR/bin/resources" "$OUTDIR/usr/bin"
# Why do we have to manually remove these libs? Because the linuxdeploy Qt plugin
# copies them, not the "main" linuxdeploy binary, and plugins don't inherit the
# include list...
for lib in "${REMOVE_LIBS[@]}"; do
for libpath in $(find "$OUTDIR/usr/lib" -name "$lib"); do
echo " Removing problematic library ${libpath}."
rm -f "$libpath"
done
done
# Restore unstripped deps (for cache).
rm -fr "$DEPSDIR"
mv "$DEPSDIR.bak" "$DEPSDIR"

View File

@@ -15,17 +15,17 @@ if [ "${INSTALLDIR:0:1}" != "/" ]; then
fi
FREETYPE=2.14.1
HARFBUZZ=11.5.0
HARFBUZZ=12.0.0
LIBBACKTRACE=ad106d5fdd5d960bd33fae1c48a351af567fd075
LIBJPEGTURBO=3.1.2
LIBPNG=1.6.50
LIBWEBP=1.6.0
SDL=SDL3-3.2.22
QT=6.9.2
SDL=SDL3-3.2.26
QT=6.10.1
QTAPNG=1.3.0
LZ4=1.10.0
ZSTD=1.5.7
KDDOCKWIDGETS=2.2.3
KDDOCKWIDGETS=2.4.0
PLUTOVG=1.3.1
PLUTOSVG=0.0.7
@@ -39,27 +39,27 @@ cd deps-build
cat > SHASUMS <<EOF
32427e8c471ac095853212a37aef816c60b42052d4d9e48230bab3bdf2936ccc freetype-$FREETYPE.tar.xz
119778e3a692806e45104b2cdfda807a8df2ccf5421c50a016aa4b7b82260205 harfbuzz-$HARFBUZZ.tar.gz
c4a398539c3e0fdc9a82dfe7824d0438cae78c1e2124e7c6ada3dfa600cdb6c8 harfbuzz-$HARFBUZZ.tar.gz
fd6f417fe9e3a071cf1424a5152d926a34c4a3c5070745470be6cf12a404ed79 $LIBBACKTRACE.zip
8f0012234b464ce50890c490f18194f913a7b1f4e6a03d6644179fa0f867d0cf libjpeg-turbo-$LIBJPEGTURBO.tar.gz
4df396518620a7aa3651443e87d1b2862e4e88cad135a8b93423e01706232307 libpng-$LIBPNG.tar.xz
e4ab7009bf0629fd11982d4c2aa83964cf244cffba7347ecd39019a9e38c4564 libwebp-$LIBWEBP.tar.gz
f29d00cbcee273c0a54f3f32f86bf5c595e8823a96b1d92a145aac40571ebfcc $SDL.tar.gz
dad488474a51a0b01d547cd2834893d6299328d2e30f479a3564088b5476bae2 $SDL.tar.gz
687ddc0c7cb128a3ea58e159b5129252537c27ede0c32a93f11f03127f0c0165 libpng-$LIBPNG-apng.patch.gz
537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b lz4-$LZ4.tar.gz
eb33e51f49a15e023950cd7825ca74a4a2b43db8354825ac24fc1b7ee09e6fa3 zstd-$ZSTD.tar.gz
44be9c9ecfe04129c4dea0a7e1b36ad476c9cc07c292016ac98e7b41514f2440 qtbase-everywhere-src-$QT.tar.xz
8a023f7e2f57dedc02e2ab10c975f7cb3cccac9b8f0823c12fd6824834549139 qtimageformats-everywhere-src-$QT.tar.xz
d984cab8f26334aa1c15e5b8f0cd9f1b7c0c1289fe0b68c1c84ab469b75605a5 qtsvg-everywhere-src-$QT.tar.xz
d8b7f7e8e970cc0b975205fd6d5832ea917ef3e751df69b97439c1cddd67a489 qttools-everywhere-src-$QT.tar.xz
c73bb6281ed365c0f954f4b1b6e1b13e1b3fefd94854f46fcd9a412f641f7ed6 qttranslations-everywhere-src-$QT.tar.xz
cad79806565568f12f9983fed69219416abcee9d5deef4abdfcf94aa2eef7781 qtwayland-everywhere-src-$QT.tar.xz
5a6226f7e23db51fdc3223121eba53f3f5447cf0cc4d6cb82a3a2df7a65d265d qtbase-everywhere-src-$QT.tar.xz
498eabdf2381db96f808942b3e3c765f6360fe6c0e9961f0a45ff7a4c68d7a72 qtimageformats-everywhere-src-$QT.tar.xz
c02f355a58f3bbcf404a628bf488b6aeb2d84a94c269afdb86f6e529343ab01f qtsvg-everywhere-src-$QT.tar.xz
8148408380ffea03101a26305c812b612ea30dbc07121e58707601522404d49b qttools-everywhere-src-$QT.tar.xz
8e49a2df88a12c376a479ae7bd272a91cf57ebb4e7c0cf7341b3565df99d2314 qttranslations-everywhere-src-$QT.tar.xz
49bf6db800227a6b2c971f4c5d03dd1e81297e7ffb296ce4a96437304f27cb13 qtwayland-everywhere-src-$QT.tar.xz
f1d3be3489f758efe1a8f12118a212febbe611aa670af32e0159fa3c1feab2a6 QtApng-$QTAPNG.tar.gz
a8e4a25e5c2686fd36981e527ed05e451fcfc226bddf350f4e76181371190937 shaderc-$SHADERC.tar.gz
9427deccbdf4bde6a269938df38c6bd75247493786a310d8d733a2c82065ef47 shaderc-glslang-$SHADERC_GLSLANG.tar.gz
c2225a49c3d7efa5c4f4ce4a6b42081e6ea3daca376f3353d9d7c2722d77a28a shaderc-spirv-headers-$SHADERC_SPIRVHEADERS.tar.gz
44d1005880c583fc00a0fb41c839214c68214b000ea8dcb54d352732fee600ff shaderc-spirv-tools-$SHADERC_SPIRVTOOLS.tar.gz
b8529755b2d54205341766ae168e83177c6120660539f9afba71af6bca4b81ec KDDockWidgets-$KDDOCKWIDGETS.tar.gz
51dbf24fe72e43dd7cb9a289d3cab47112010f1a2ed69b6fc8ac0dff31991ed2 KDDockWidgets-$KDDOCKWIDGETS.tar.gz
bea672eb96ee36c2cbeb911b9bac66dfe989b3ad9a9943101e00aeb2df2aefdb plutovg-$PLUTOVG.tar.gz
78561b571ac224030cdc450ca2986b4de915c2ba7616004a6d71a379bffd15f3 plutosvg-$PLUTOSVG.tar.gz
EOF
@@ -116,7 +116,9 @@ echo "Building libjpegturbo..."
rm -fr "libjpeg-turbo-$LIBJPEGTURBO"
tar xf "libjpeg-turbo-$LIBJPEGTURBO.tar.gz"
cd "libjpeg-turbo-$LIBJPEGTURBO"
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DENABLE_STATIC=OFF -DENABLE_SHARED=ON -B build -G Ninja
# On non debian or debian based Linux systems, libjpeg-turbo will set CMAKE_INSTALL_DEFAULT_LIBDIR "lib64" (or libx32)
# That will prevent CMake from finding the deps libjpeg later on. if we set CMAKE_INSTALL_DEFAULT_LIBDIR, libjpeg-turbo will leave it as is, so set it to "lib"
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DENABLE_STATIC=OFF -DENABLE_SHARED=ON -DCMAKE_INSTALL_DEFAULT_LIBDIR="lib" -B build -G Ninja
cmake --build build --parallel
ninja -C build install
cd ..
@@ -271,7 +273,6 @@ echo "Building KDDockWidgets..."
rm -fr "KDDockWidgets-$KDDOCKWIDGETS"
tar xf "KDDockWidgets-$KDDOCKWIDGETS.tar.gz"
cd "KDDockWidgets-$KDDOCKWIDGETS"
patch -p1 < "$SCRIPTDIR/../common/kddockwidgets-dodgy-include.patch"
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DKDDockWidgets_QT6=true -DKDDockWidgets_EXAMPLES=false -DKDDockWidgets_FRONTENDS=qtwidgets -B build -G Ninja
cmake --build build --parallel
ninja -C build install

View File

@@ -0,0 +1,196 @@
#!/usr/bin/env bash
set -e
if [ "$#" -ne 1 ]; then
echo "Syntax: $0 <output directory>"
exit 1
fi
SCRIPTDIR=$(realpath $(dirname "${BASH_SOURCE[0]}"))
NPROCS="$(getconf _NPROCESSORS_ONLN)"
INSTALLDIR="$1"
if [ "${INSTALLDIR:0:1}" != "/" ]; then
INSTALLDIR="$PWD/$INSTALLDIR"
fi
FREETYPE=2.14.1
HARFBUZZ=12.0.0
LIBBACKTRACE=ad106d5fdd5d960bd33fae1c48a351af567fd075
LIBPNG=1.6.50
LIBWEBP=1.6.0
SDL=SDL3-3.2.22
LZ4=1.10.0
ZSTD=1.5.7
PLUTOVG=1.3.1
PLUTOSVG=0.0.7
SHADERC=2025.3
SHADERC_GLSLANG=efd24d75bcbc55620e759f6bf42c45a32abac5f8
SHADERC_SPIRVHEADERS=2a611a970fdbc41ac2e3e328802aed9985352dca
SHADERC_SPIRVTOOLS=33e02568181e3312f49a3cf33df470bf96ef293a
mkdir -p deps-build
cd deps-build
cat > SHASUMS <<EOF
32427e8c471ac095853212a37aef816c60b42052d4d9e48230bab3bdf2936ccc freetype-$FREETYPE.tar.xz
c4a398539c3e0fdc9a82dfe7824d0438cae78c1e2124e7c6ada3dfa600cdb6c8 harfbuzz-$HARFBUZZ.tar.gz
fd6f417fe9e3a071cf1424a5152d926a34c4a3c5070745470be6cf12a404ed79 $LIBBACKTRACE.zip
4df396518620a7aa3651443e87d1b2862e4e88cad135a8b93423e01706232307 libpng-$LIBPNG.tar.xz
e4ab7009bf0629fd11982d4c2aa83964cf244cffba7347ecd39019a9e38c4564 libwebp-$LIBWEBP.tar.gz
f29d00cbcee273c0a54f3f32f86bf5c595e8823a96b1d92a145aac40571ebfcc $SDL.tar.gz
687ddc0c7cb128a3ea58e159b5129252537c27ede0c32a93f11f03127f0c0165 libpng-$LIBPNG-apng.patch.gz
537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b lz4-$LZ4.tar.gz
eb33e51f49a15e023950cd7825ca74a4a2b43db8354825ac24fc1b7ee09e6fa3 zstd-$ZSTD.tar.gz
a8e4a25e5c2686fd36981e527ed05e451fcfc226bddf350f4e76181371190937 shaderc-$SHADERC.tar.gz
9427deccbdf4bde6a269938df38c6bd75247493786a310d8d733a2c82065ef47 shaderc-glslang-$SHADERC_GLSLANG.tar.gz
c2225a49c3d7efa5c4f4ce4a6b42081e6ea3daca376f3353d9d7c2722d77a28a shaderc-spirv-headers-$SHADERC_SPIRVHEADERS.tar.gz
44d1005880c583fc00a0fb41c839214c68214b000ea8dcb54d352732fee600ff shaderc-spirv-tools-$SHADERC_SPIRVTOOLS.tar.gz
bea672eb96ee36c2cbeb911b9bac66dfe989b3ad9a9943101e00aeb2df2aefdb plutovg-$PLUTOVG.tar.gz
78561b571ac224030cdc450ca2986b4de915c2ba7616004a6d71a379bffd15f3 plutosvg-$PLUTOSVG.tar.gz
EOF
curl -L \
-o "freetype-$FREETYPE.tar.xz" "https://sourceforge.net/projects/freetype/files/freetype2/$FREETYPE/freetype-$FREETYPE.tar.xz/download" \
-o "harfbuzz-$HARFBUZZ.tar.gz" "https://github.com/harfbuzz/harfbuzz/archive/refs/tags/$HARFBUZZ.tar.gz" \
-O "https://github.com/ianlancetaylor/libbacktrace/archive/$LIBBACKTRACE.zip" \
-O "https://downloads.sourceforge.net/project/libpng/libpng16/$LIBPNG/libpng-$LIBPNG.tar.xz" \
-O "https://download.sourceforge.net/libpng-apng/libpng-$LIBPNG-apng.patch.gz" \
-O "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-$LIBWEBP.tar.gz" \
-O "https://github.com/lz4/lz4/releases/download/v$LZ4/lz4-$LZ4.tar.gz" \
-O "https://libsdl.org/release/$SDL.tar.gz" \
-O "https://github.com/facebook/zstd/releases/download/v$ZSTD/zstd-$ZSTD.tar.gz" \
-o "shaderc-$SHADERC.tar.gz" "https://github.com/google/shaderc/archive/refs/tags/v$SHADERC.tar.gz" \
-o "shaderc-glslang-$SHADERC_GLSLANG.tar.gz" "https://github.com/KhronosGroup/glslang/archive/$SHADERC_GLSLANG.tar.gz" \
-o "shaderc-spirv-headers-$SHADERC_SPIRVHEADERS.tar.gz" "https://github.com/KhronosGroup/SPIRV-Headers/archive/$SHADERC_SPIRVHEADERS.tar.gz" \
-o "shaderc-spirv-tools-$SHADERC_SPIRVTOOLS.tar.gz" "https://github.com/KhronosGroup/SPIRV-Tools/archive/$SHADERC_SPIRVTOOLS.tar.gz" \
-o "plutovg-$PLUTOVG.tar.gz" "https://github.com/sammycage/plutovg/archive/v$PLUTOVG.tar.gz" \
-o "plutosvg-$PLUTOSVG.tar.gz" "https://github.com/sammycage/plutosvg/archive/v$PLUTOSVG.tar.gz"
shasum -a 256 --check SHASUMS
echo "Building libbacktrace..."
rm -fr "libbacktrace-$LIBBACKTRACE"
unzip "$LIBBACKTRACE.zip"
cd "libbacktrace-$LIBBACKTRACE"
./configure --prefix="$INSTALLDIR"
make
make install
cd ..
echo "Building libpng..."
rm -fr "libpng-$LIBPNG"
tar xf "libpng-$LIBPNG.tar.xz"
gunzip -d -f "libpng-$LIBPNG-apng.patch.gz"
cd "libpng-$LIBPNG"
patch -p1 < "../libpng-$LIBPNG-apng.patch"
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DBUILD_SHARED_LIBS=ON -DBUILD_SHARED_LIBS=ON -DPNG_TESTS=OFF -DPNG_STATIC=OFF -DPNG_SHARED=ON -DPNG_TOOLS=OFF -B build -G Ninja
cmake --build build --parallel
ninja -C build install
cd ..
echo "Building LZ4..."
rm -fr "lz4-$LZ4"
tar xf "lz4-$LZ4.tar.gz"
cd "lz4-$LZ4"
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DBUILD_SHARED_LIBS=ON -DLZ4_BUILD_CLI=OFF -DLZ4_BUILD_LEGACY_LZ4C=OFF -B build-dir -G Ninja build/cmake
cmake --build build-dir --parallel
ninja -C build-dir install
cd ..
echo "Building Zstandard..."
rm -fr "zstd-$ZSTD"
tar xf "zstd-$ZSTD.tar.gz"
cd "zstd-$ZSTD"
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DBUILD_SHARED_LIBS=ON -DZSTD_BUILD_SHARED=ON -DZSTD_BUILD_STATIC=OFF -DZSTD_BUILD_PROGRAMS=OFF -B build -G Ninja build/cmake
cmake --build build --parallel
ninja -C build install
cd ..
echo "Building FreeType without HarfBuzz..."
rm -fr "freetype-$FREETYPE"
tar xf "freetype-$FREETYPE.tar.xz"
cd "freetype-$FREETYPE"
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DBUILD_SHARED_LIBS=ON -DFT_REQUIRE_ZLIB=ON -DFT_REQUIRE_PNG=ON -DFT_DISABLE_BZIP2=TRUE -DFT_DISABLE_BROTLI=TRUE -DFT_DISABLE_HARFBUZZ=TRUE -B build -G Ninja
cmake --build build --parallel
ninja -C build install
cd ..
echo "Building HarfBuzz..."
rm -fr "harfbuzz-$HARFBUZZ"
tar xf "harfbuzz-$HARFBUZZ.tar.gz"
cd "harfbuzz-$HARFBUZZ"
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DBUILD_SHARED_LIBS=ON -DHB_BUILD_UTILS=OFF -DHB_HAVE_FREETYPE=ON -B build -G Ninja
cmake --build build --parallel
ninja -C build install
cd ..
echo "Building WebP..."
rm -fr "libwebp-$LIBWEBP"
tar xf "libwebp-$LIBWEBP.tar.gz"
cd "libwebp-$LIBWEBP"
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -B build -G Ninja \
-DWEBP_BUILD_ANIM_UTILS=OFF -DWEBP_BUILD_CWEBP=OFF -DWEBP_BUILD_DWEBP=OFF -DWEBP_BUILD_GIF2WEBP=OFF -DWEBP_BUILD_IMG2WEBP=OFF \
-DWEBP_BUILD_VWEBP=OFF -DWEBP_BUILD_WEBPINFO=OFF -DWEBP_BUILD_WEBPMUX=OFF -DWEBP_BUILD_EXTRAS=OFF -DBUILD_SHARED_LIBS=ON
cmake --build build --parallel
ninja -C build install
cd ..
echo "Building FreeType with HarfBuzz..."
rm -fr "freetype-$FREETYPE"
tar xf "freetype-$FREETYPE.tar.xz"
cd "freetype-$FREETYPE"
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DBUILD_SHARED_LIBS=ON -DFT_REQUIRE_ZLIB=ON -DFT_REQUIRE_PNG=ON -DFT_DISABLE_BZIP2=TRUE -DFT_DISABLE_BROTLI=TRUE -DFT_REQUIRE_HARFBUZZ=TRUE -B build -G Ninja
cmake --build build --parallel
ninja -C build install
cd ..
echo "Building SDL..."
rm -fr "$SDL"
tar xf "$SDL.tar.gz"
cd "$SDL"
cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DBUILD_SHARED_LIBS=ON -DSDL_SHARED=ON -DSDL_STATIC=OFF -G Ninja
cmake --build build --parallel
ninja -C build install
cd ..
echo "Building PlutoVG..."
rm -fr "plutovg-$PLUTOVG"
tar xf "plutovg-$PLUTOVG.tar.gz"
cd "plutovg-$PLUTOVG"
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DBUILD_SHARED_LIBS=ON -DPLUTOVG_BUILD_EXAMPLES=OFF -B build -G Ninja
cmake --build build --parallel
ninja -C build install
cd ..
echo "Building PlutoSVG..."
rm -fr "plutosvg-$PLUTOSVG"
tar xf "plutosvg-$PLUTOSVG.tar.gz"
cd "plutosvg-$PLUTOSVG"
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DBUILD_SHARED_LIBS=ON -DPLUTOSVG_ENABLE_FREETYPE=ON -DPLUTOSVG_BUILD_EXAMPLES=OFF -B build -G Ninja
cmake --build build --parallel
ninja -C build install
cd ..
echo "Building shaderc..."
rm -fr "shaderc-$SHADERC"
tar xf "shaderc-$SHADERC.tar.gz"
cd "shaderc-$SHADERC"
cd third_party
tar xf "../../shaderc-glslang-$SHADERC_GLSLANG.tar.gz"
mv "glslang-$SHADERC_GLSLANG" "glslang"
tar xf "../../shaderc-spirv-headers-$SHADERC_SPIRVHEADERS.tar.gz"
mv "SPIRV-Headers-$SHADERC_SPIRVHEADERS" "spirv-headers"
tar xf "../../shaderc-spirv-tools-$SHADERC_SPIRVTOOLS.tar.gz"
mv "SPIRV-Tools-$SHADERC_SPIRVTOOLS" "spirv-tools"
cd ..
patch -p1 < "$SCRIPTDIR/../common/shaderc-changes.patch"
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DSHADERC_SKIP_TESTS=ON -DSHADERC_SKIP_EXAMPLES=ON -DSHADERC_SKIP_COPYRIGHT_CHECK=ON -B build -G Ninja
cmake --build build --parallel
ninja -C build install
cd ..
echo "Cleaning up..."
cd ..
rm -r deps-build

View File

@@ -14,8 +14,8 @@
"sources": [
{
"type": "archive",
"url": "https://libsdl.org/release/SDL3-3.2.22.tar.gz",
"sha256": "f29d00cbcee273c0a54f3f32f86bf5c595e8823a96b1d92a145aac40571ebfcc"
"url": "https://libsdl.org/release/SDL3-3.2.26.tar.gz",
"sha256": "dad488474a51a0b01d547cd2834893d6299328d2e30f479a3564088b5476bae2"
}
],
"cleanup": [

View File

@@ -14,13 +14,9 @@
{
"type": "git",
"url": "https://github.com/KDAB/KDDockWidgets.git",
"tag": "v2.2.3",
"commit": "28d16d0431d7cdc9f36cb619d22621146fdfab44",
"tag": "v2.3.0",
"commit": "c38711026e17e34916dd82c6fcbdcc0d2342f541",
"disable-submodules": true
},
{
"type": "patch",
"path": "../../../common/kddockwidgets-dodgy-include.patch"
}
],
"cleanup": [

View File

@@ -1,15 +1,15 @@
{
"app-id": "net.pcsx2.PCSX2",
"runtime": "org.kde.Platform",
"runtime-version": "6.9",
"runtime-version": "6.10",
"sdk": "org.kde.Sdk",
"sdk-extensions": [
"org.freedesktop.Sdk.Extension.llvm18"
"org.freedesktop.Sdk.Extension.llvm20"
],
"add-extensions": {
"org.freedesktop.Platform.ffmpeg-full": {
"directory": "lib/ffmpeg",
"version": "24.08",
"version": "25.08",
"add-ld-path": ".",
"autodownload": true
}
@@ -50,8 +50,8 @@
"-DCMAKE_PREFIX_PATH=\"${FLATPAK_DEST}\"",
"-DCMAKE_BUILD_TYPE=Release",
"-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON",
"-DCMAKE_C_COMPILER=/usr/lib/sdk/llvm18/bin/clang",
"-DCMAKE_CXX_COMPILER=/usr/lib/sdk/llvm18/bin/clang++",
"-DCMAKE_C_COMPILER=/usr/lib/sdk/llvm20/bin/clang",
"-DCMAKE_CXX_COMPILER=/usr/lib/sdk/llvm20/bin/clang++",
"-DCMAKE_EXE_LINKER_FLAGS_INIT=-fuse-ld=lld",
"-DCMAKE_MODULE_LINKER_FLAGS_INIT=-fuse-ld=lld",
"-DCMAKE_SHARED_LINKER_FLAGS_INIT=-fuse-ld=lld",

45
.github/workflows/scripts/macos/build-dependencies-universal.sh vendored Normal file → Executable file
View File

@@ -39,18 +39,18 @@ if [ "${INSTALLDIR:0:1}" != "/" ]; then
fi
FREETYPE=2.14.1
HARFBUZZ=11.5.0
SDL=SDL3-3.2.22
HARFBUZZ=12.0.0
SDL=SDL3-3.2.26
ZSTD=1.5.7
LZ4=1.10.0
LIBPNG=1.6.50
LIBJPEGTURBO=3.1.2
LIBWEBP=1.6.0
FFMPEG=6.0
FFMPEG=8.0
MOLTENVK=1.2.9
QT=6.7.3
QT=6.10.1
QTAPNG=1.3.0
KDDOCKWIDGETS=2.2.3
KDDOCKWIDGETS=2.4.0
PLUTOVG=1.3.1
PLUTOSVG=0.0.7
@@ -79,27 +79,27 @@ CMAKE_ARCH_UNIVERSAL=-DCMAKE_OSX_ARCHITECTURES="x86_64;arm64"
cat > SHASUMS <<EOF
32427e8c471ac095853212a37aef816c60b42052d4d9e48230bab3bdf2936ccc freetype-$FREETYPE.tar.xz
119778e3a692806e45104b2cdfda807a8df2ccf5421c50a016aa4b7b82260205 harfbuzz-$HARFBUZZ.tar.gz
f29d00cbcee273c0a54f3f32f86bf5c595e8823a96b1d92a145aac40571ebfcc $SDL.tar.gz
c4a398539c3e0fdc9a82dfe7824d0438cae78c1e2124e7c6ada3dfa600cdb6c8 harfbuzz-$HARFBUZZ.tar.gz
dad488474a51a0b01d547cd2834893d6299328d2e30f479a3564088b5476bae2 $SDL.tar.gz
eb33e51f49a15e023950cd7825ca74a4a2b43db8354825ac24fc1b7ee09e6fa3 zstd-$ZSTD.tar.gz
537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b lz4-$LZ4.tar.gz
4df396518620a7aa3651443e87d1b2862e4e88cad135a8b93423e01706232307 libpng-$LIBPNG.tar.xz
e4ab7009bf0629fd11982d4c2aa83964cf244cffba7347ecd39019a9e38c4564 libwebp-$LIBWEBP.tar.gz
687ddc0c7cb128a3ea58e159b5129252537c27ede0c32a93f11f03127f0c0165 libpng-$LIBPNG-apng.patch.gz
8f0012234b464ce50890c490f18194f913a7b1f4e6a03d6644179fa0f867d0cf libjpeg-turbo-$LIBJPEGTURBO.tar.gz
57be87c22d9b49c112b6d24bc67d42508660e6b718b3db89c44e47e289137082 ffmpeg-$FFMPEG.tar.xz
b2751fccb6cc4c77708113cd78b561059b6fa904b24162fa0be2d60273d27b8e ffmpeg-$FFMPEG.tar.xz
f415a09385030c6510a936155ce211f617c31506db5fbc563e804345f1ecf56e v$MOLTENVK.tar.gz
8ccbb9ab055205ac76632c9eeddd1ed6fc66936fc56afc2ed0fd5d9e23da3097 qtbase-everywhere-src-$QT.tar.xz
9fd58144081654c3373768dd96ead294023830927b14fe3d3c1ef641fb324753 qtimageformats-everywhere-src-$QT.tar.xz
40142cb71fb1e07ad612bc361b67f5d54cd9367f9979ae6b86124a064deda06b qtsvg-everywhere-src-$QT.tar.xz
f03bb7df619cd9ac9dba110e30b7bcab5dd88eb8bdc9cc752563b4367233203f qttools-everywhere-src-$QT.tar.xz
dcc762acac043b9bb5e4d369b6d6f53e0ecfcf76a408fe0db5f7ef071c9d6dc8 qttranslations-everywhere-src-$QT.tar.xz
5a6226f7e23db51fdc3223121eba53f3f5447cf0cc4d6cb82a3a2df7a65d265d qtbase-everywhere-src-$QT.tar.xz
498eabdf2381db96f808942b3e3c765f6360fe6c0e9961f0a45ff7a4c68d7a72 qtimageformats-everywhere-src-$QT.tar.xz
c02f355a58f3bbcf404a628bf488b6aeb2d84a94c269afdb86f6e529343ab01f qtsvg-everywhere-src-$QT.tar.xz
8148408380ffea03101a26305c812b612ea30dbc07121e58707601522404d49b qttools-everywhere-src-$QT.tar.xz
8e49a2df88a12c376a479ae7bd272a91cf57ebb4e7c0cf7341b3565df99d2314 qttranslations-everywhere-src-$QT.tar.xz
f1d3be3489f758efe1a8f12118a212febbe611aa670af32e0159fa3c1feab2a6 QtApng-$QTAPNG.tar.gz
a8e4a25e5c2686fd36981e527ed05e451fcfc226bddf350f4e76181371190937 shaderc-$SHADERC.tar.gz
9427deccbdf4bde6a269938df38c6bd75247493786a310d8d733a2c82065ef47 shaderc-glslang-$SHADERC_GLSLANG.tar.gz
c2225a49c3d7efa5c4f4ce4a6b42081e6ea3daca376f3353d9d7c2722d77a28a shaderc-spirv-headers-$SHADERC_SPIRVHEADERS.tar.gz
44d1005880c583fc00a0fb41c839214c68214b000ea8dcb54d352732fee600ff shaderc-spirv-tools-$SHADERC_SPIRVTOOLS.tar.gz
b8529755b2d54205341766ae168e83177c6120660539f9afba71af6bca4b81ec KDDockWidgets-$KDDOCKWIDGETS.tar.gz
51dbf24fe72e43dd7cb9a289d3cab47112010f1a2ed69b6fc8ac0dff31991ed2 KDDockWidgets-$KDDOCKWIDGETS.tar.gz
bea672eb96ee36c2cbeb911b9bac66dfe989b3ad9a9943101e00aeb2df2aefdb plutovg-$PLUTOVG.tar.gz
78561b571ac224030cdc450ca2986b4de915c2ba7616004a6d71a379bffd15f3 plutosvg-$PLUTOSVG.tar.gz
EOF
@@ -285,6 +285,10 @@ echo "Installing Qt Base..."
rm -fr "qtbase-everywhere-src-$QT"
tar xf "qtbase-everywhere-src-$QT.tar.xz"
cd "qtbase-everywhere-src-$QT"
# Patch Qt to support macOS 11
patch -p1 < "$SCRIPTDIR/qt-macos11compat.patch"
# since we don't have a direct reference to QtSvg, it doesn't deployed directly from the main binary
# (only indirectly from iconengines), and the libqsvg.dylib imageformat plugin does not get deployed.
# We could run macdeployqt twice, but that's even more janky than patching it.
@@ -320,7 +324,7 @@ tar xf "qtsvg-everywhere-src-$QT.tar.xz"
cd "qtsvg-everywhere-src-$QT"
mkdir build
cd build
"$INSTALLDIR/bin/qt-configure-module" .. -- "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_UNIVERSAL"
"$INSTALLDIR/bin/qt-configure-module" .. -- "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_UNIVERSAL" -DQT_GENERATE_SBOM=OFF
make "-j$NPROCS"
make install
cd ../..
@@ -331,7 +335,7 @@ tar xf "qtimageformats-everywhere-src-$QT.tar.xz"
cd "qtimageformats-everywhere-src-$QT"
mkdir build
cd build
"$INSTALLDIR/bin/qt-configure-module" .. -- "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_UNIVERSAL" -DFEATURE_system_webp=ON
"$INSTALLDIR/bin/qt-configure-module" .. -- "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_UNIVERSAL" -DQT_GENERATE_SBOM=OFF -DFEATURE_system_webp=ON
make "-j$NPROCS"
make install
cd ../..
@@ -342,7 +346,7 @@ tar xf "qttools-everywhere-src-$QT.tar.xz"
cd "qttools-everywhere-src-$QT"
mkdir build
cd build
"$INSTALLDIR/bin/qt-configure-module" .. -- "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_UNIVERSAL" -DFEATURE_assistant=OFF -DFEATURE_clang=OFF -DFEATURE_designer=OFF -DFEATURE_kmap2qmap=OFF -DFEATURE_pixeltool=OFF -DFEATURE_pkg_config=OFF -DFEATURE_qev=OFF -DFEATURE_qtattributionsscanner=OFF -DFEATURE_qtdiag=OFF -DFEATURE_qtplugininfo=OFF
"$INSTALLDIR/bin/qt-configure-module" .. -- "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_UNIVERSAL" -DQT_GENERATE_SBOM=OFF -DFEATURE_assistant=OFF -DFEATURE_clang=OFF -DFEATURE_designer=OFF -DFEATURE_kmap2qmap=OFF -DFEATURE_pixeltool=OFF -DFEATURE_pkg_config=OFF -DFEATURE_qev=OFF -DFEATURE_qtattributionsscanner=OFF -DFEATURE_qtdiag=OFF -DFEATURE_qtplugininfo=OFF
make "-j$NPROCS"
make install
cd ../..
@@ -371,7 +375,7 @@ tar xf "qttranslations-everywhere-src-$QT.tar.xz"
cd "qttranslations-everywhere-src-$QT"
mkdir build
cd build
"$INSTALLDIR/bin/qt-configure-module" .. -- "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_UNIVERSAL"
"$INSTALLDIR/bin/qt-configure-module" .. -- "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_UNIVERSAL" -DQT_GENERATE_SBOM=OFF
make "-j$NPROCS"
make install
cd ../..
@@ -390,10 +394,9 @@ echo "Building KDDockWidgets..."
rm -fr "KDDockWidgets-$KDDOCKWIDGETS"
tar xf "KDDockWidgets-$KDDOCKWIDGETS.tar.gz"
cd "KDDockWidgets-$KDDOCKWIDGETS"
patch -p1 < "$SCRIPTDIR/../common/kddockwidgets-dodgy-include.patch"
cmake "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_UNIVERSAL" -DKDDockWidgets_QT6=true -DKDDockWidgets_EXAMPLES=false -DKDDockWidgets_FRONTENDS=qtwidgets -B build
cmake --build build --parallel
cmake --install build
make -C build "-j$NPROCS"
make -C build install
cd ..
echo "Building PlutoVG..."

View File

@@ -21,18 +21,18 @@ if [ "${INSTALLDIR:0:1}" != "/" ]; then
fi
FREETYPE=2.14.1
HARFBUZZ=11.5.0
SDL=SDL3-3.2.22
HARFBUZZ=12.0.0
SDL=SDL3-3.2.26
ZSTD=1.5.7
LZ4=1.10.0
LIBPNG=1.6.50
LIBJPEGTURBO=3.1.2
LIBWEBP=1.6.0
FFMPEG=6.0
FFMPEG=8.0
MOLTENVK=1.2.9
QT=6.7.3
QT=6.10.1
QTAPNG=1.3.0
KDDOCKWIDGETS=2.2.3
KDDOCKWIDGETS=2.4.0
PLUTOVG=1.3.1
PLUTOSVG=0.0.7
@@ -54,32 +54,33 @@ CMAKE_COMMON=(
-DCMAKE_PREFIX_PATH="$INSTALLDIR"
-DCMAKE_INSTALL_PREFIX="$INSTALLDIR"
-DCMAKE_OSX_ARCHITECTURES="x86_64"
-DCMAKE_APPLE_SILICON_PROCESSOR="x86_64"
-DCMAKE_INSTALL_NAME_DIR='$<INSTALL_PREFIX>/lib'
)
cat > SHASUMS <<EOF
32427e8c471ac095853212a37aef816c60b42052d4d9e48230bab3bdf2936ccc freetype-$FREETYPE.tar.xz
119778e3a692806e45104b2cdfda807a8df2ccf5421c50a016aa4b7b82260205 harfbuzz-$HARFBUZZ.tar.gz
f29d00cbcee273c0a54f3f32f86bf5c595e8823a96b1d92a145aac40571ebfcc $SDL.tar.gz
c4a398539c3e0fdc9a82dfe7824d0438cae78c1e2124e7c6ada3dfa600cdb6c8 harfbuzz-$HARFBUZZ.tar.gz
dad488474a51a0b01d547cd2834893d6299328d2e30f479a3564088b5476bae2 $SDL.tar.gz
eb33e51f49a15e023950cd7825ca74a4a2b43db8354825ac24fc1b7ee09e6fa3 zstd-$ZSTD.tar.gz
537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b lz4-$LZ4.tar.gz
4df396518620a7aa3651443e87d1b2862e4e88cad135a8b93423e01706232307 libpng-$LIBPNG.tar.xz
e4ab7009bf0629fd11982d4c2aa83964cf244cffba7347ecd39019a9e38c4564 libwebp-$LIBWEBP.tar.gz
687ddc0c7cb128a3ea58e159b5129252537c27ede0c32a93f11f03127f0c0165 libpng-$LIBPNG-apng.patch.gz
8f0012234b464ce50890c490f18194f913a7b1f4e6a03d6644179fa0f867d0cf libjpeg-turbo-$LIBJPEGTURBO.tar.gz
57be87c22d9b49c112b6d24bc67d42508660e6b718b3db89c44e47e289137082 ffmpeg-$FFMPEG.tar.xz
b2751fccb6cc4c77708113cd78b561059b6fa904b24162fa0be2d60273d27b8e ffmpeg-$FFMPEG.tar.xz
f415a09385030c6510a936155ce211f617c31506db5fbc563e804345f1ecf56e v$MOLTENVK.tar.gz
8ccbb9ab055205ac76632c9eeddd1ed6fc66936fc56afc2ed0fd5d9e23da3097 qtbase-everywhere-src-$QT.tar.xz
9fd58144081654c3373768dd96ead294023830927b14fe3d3c1ef641fb324753 qtimageformats-everywhere-src-$QT.tar.xz
40142cb71fb1e07ad612bc361b67f5d54cd9367f9979ae6b86124a064deda06b qtsvg-everywhere-src-$QT.tar.xz
f03bb7df619cd9ac9dba110e30b7bcab5dd88eb8bdc9cc752563b4367233203f qttools-everywhere-src-$QT.tar.xz
dcc762acac043b9bb5e4d369b6d6f53e0ecfcf76a408fe0db5f7ef071c9d6dc8 qttranslations-everywhere-src-$QT.tar.xz
5a6226f7e23db51fdc3223121eba53f3f5447cf0cc4d6cb82a3a2df7a65d265d qtbase-everywhere-src-$QT.tar.xz
498eabdf2381db96f808942b3e3c765f6360fe6c0e9961f0a45ff7a4c68d7a72 qtimageformats-everywhere-src-$QT.tar.xz
c02f355a58f3bbcf404a628bf488b6aeb2d84a94c269afdb86f6e529343ab01f qtsvg-everywhere-src-$QT.tar.xz
8148408380ffea03101a26305c812b612ea30dbc07121e58707601522404d49b qttools-everywhere-src-$QT.tar.xz
8e49a2df88a12c376a479ae7bd272a91cf57ebb4e7c0cf7341b3565df99d2314 qttranslations-everywhere-src-$QT.tar.xz
f1d3be3489f758efe1a8f12118a212febbe611aa670af32e0159fa3c1feab2a6 QtApng-$QTAPNG.tar.gz
a8e4a25e5c2686fd36981e527ed05e451fcfc226bddf350f4e76181371190937 shaderc-$SHADERC.tar.gz
9427deccbdf4bde6a269938df38c6bd75247493786a310d8d733a2c82065ef47 shaderc-glslang-$SHADERC_GLSLANG.tar.gz
c2225a49c3d7efa5c4f4ce4a6b42081e6ea3daca376f3353d9d7c2722d77a28a shaderc-spirv-headers-$SHADERC_SPIRVHEADERS.tar.gz
44d1005880c583fc00a0fb41c839214c68214b000ea8dcb54d352732fee600ff shaderc-spirv-tools-$SHADERC_SPIRVTOOLS.tar.gz
b8529755b2d54205341766ae168e83177c6120660539f9afba71af6bca4b81ec KDDockWidgets-$KDDOCKWIDGETS.tar.gz
51dbf24fe72e43dd7cb9a289d3cab47112010f1a2ed69b6fc8ac0dff31991ed2 KDDockWidgets-$KDDOCKWIDGETS.tar.gz
bea672eb96ee36c2cbeb911b9bac66dfe989b3ad9a9943101e00aeb2df2aefdb plutovg-$PLUTOVG.tar.gz
78561b571ac224030cdc450ca2986b4de915c2ba7616004a6d71a379bffd15f3 plutosvg-$PLUTOSVG.tar.gz
EOF
@@ -233,26 +234,15 @@ rm -fr "qtbase-everywhere-src-$QT"
tar xf "qtbase-everywhere-src-$QT.tar.xz"
cd "qtbase-everywhere-src-$QT"
# Patch Qt to support macOS 11
patch -p1 < "$SCRIPTDIR/qt-macos11compat.patch"
# since we don't have a direct reference to QtSvg, it doesn't deployed directly from the main binary
# (only indirectly from iconengines), and the libqsvg.dylib imageformat plugin does not get deployed.
# We could run macdeployqt twice, but that's even more janky than patching it.
# https://github.com/qt/qtbase/commit/7b018629c3c3ab23665bf1da00c43c1546042035
# The QProcess default wait time of 30s may be too short in e.g. CI environments where processes may be blocked
# for a longer time waiting for CPU or IO.
patch -u src/tools/macdeployqt/shared/shared.cpp <<EOF
--- shared.cpp
+++ shared.cpp
@@ -152,7 +152,7 @@
LogDebug() << " inspecting" << binaryPath;
QProcess otool;
otool.start("otool", QStringList() << "-L" << binaryPath);
- otool.waitForFinished();
+ otool.waitForFinished(-1);
if (otool.exitStatus() != QProcess::NormalExit || otool.exitCode() != 0) {
LogError() << otool.readAllStandardError();
@@ -1122,14 +1122,8 @@
addPlugins(QStringLiteral("networkinformation"));
}
@@ -271,7 +261,7 @@ patch -u src/tools/macdeployqt/shared/shared.cpp <<EOF
// Platforminputcontext plugins if QtGui is in use
EOF
cmake -B build "${CMAKE_COMMON[@]}" -DFEATURE_dbus=OFF -DFEATURE_framework=OFF -DFEATURE_icu=OFF -DFEATURE_opengl=OFF -DFEATURE_sql=OFF -DFEATURE_gssapi=OFF -DFEATURE_system_png=ON -DFEATURE_system_jpeg=ON -DFEATURE_system_zlib=ON -DFEATURE_system_freetype=ON -DFEATURE_system_harfbuzz=ON
cmake -B build "${CMAKE_COMMON[@]}" -DCMAKE_BUILD_TYPE=MinSizeRel -DFEATURE_dbus=OFF -DFEATURE_framework=OFF -DFEATURE_icu=OFF -DFEATURE_opengl=OFF -DFEATURE_sql=OFF -DFEATURE_gssapi=OFF -DFEATURE_system_png=ON -DFEATURE_system_jpeg=ON -DFEATURE_system_zlib=ON -DFEATURE_system_freetype=ON -DFEATURE_system_harfbuzz=ON
make -C build "-j$NPROCS"
make -C build install
cd ..
@@ -282,7 +272,7 @@ tar xf "qtsvg-everywhere-src-$QT.tar.xz"
cd "qtsvg-everywhere-src-$QT"
mkdir build
cd build
"$INSTALLDIR/bin/qt-configure-module" .. -- "${CMAKE_COMMON[@]}"
"$INSTALLDIR/bin/qt-configure-module" .. -- "${CMAKE_COMMON[@]}" -DQT_GENERATE_SBOM=OFF
make "-j$NPROCS"
make install
cd ../..
@@ -293,7 +283,7 @@ tar xf "qtimageformats-everywhere-src-$QT.tar.xz"
cd "qtimageformats-everywhere-src-$QT"
mkdir build
cd build
"$INSTALLDIR/bin/qt-configure-module" .. -- "${CMAKE_COMMON[@]}" -DFEATURE_system_webp=ON
"$INSTALLDIR/bin/qt-configure-module" .. -- "${CMAKE_COMMON[@]}" -DQT_GENERATE_SBOM=OFF -DFEATURE_system_webp=ON
make "-j$NPROCS"
make install
cd ../..
@@ -304,7 +294,7 @@ tar xf "qttools-everywhere-src-$QT.tar.xz"
cd "qttools-everywhere-src-$QT"
mkdir build
cd build
"$INSTALLDIR/bin/qt-configure-module" .. -- "${CMAKE_COMMON[@]}" -DFEATURE_assistant=OFF -DFEATURE_clang=OFF -DFEATURE_designer=ON -DFEATURE_kmap2qmap=OFF -DFEATURE_pixeltool=OFF -DFEATURE_pkg_config=OFF -DFEATURE_qev=OFF -DFEATURE_qtattributionsscanner=OFF -DFEATURE_qtdiag=OFF -DFEATURE_qtplugininfo=OFF
"$INSTALLDIR/bin/qt-configure-module" .. -- "${CMAKE_COMMON[@]}" -DQT_GENERATE_SBOM=OFF -DFEATURE_assistant=OFF -DFEATURE_clang=OFF -DFEATURE_designer=ON -DFEATURE_kmap2qmap=OFF -DFEATURE_pixeltool=OFF -DFEATURE_pkg_config=OFF -DFEATURE_qev=OFF -DFEATURE_qtattributionsscanner=OFF -DFEATURE_qtdiag=OFF -DFEATURE_qtplugininfo=OFF
make "-j$NPROCS"
make install
cd ../..
@@ -333,7 +323,7 @@ tar xf "qttranslations-everywhere-src-$QT.tar.xz"
cd "qttranslations-everywhere-src-$QT"
mkdir build
cd build
"$INSTALLDIR/bin/qt-configure-module" .. -- "${CMAKE_COMMON[@]}"
"$INSTALLDIR/bin/qt-configure-module" .. -- "${CMAKE_COMMON[@]}" -DQT_GENERATE_SBOM=OFF
make "-j$NPROCS"
make install
cd ../..
@@ -352,7 +342,6 @@ echo "Building KDDockWidgets..."
rm -fr "KDDockWidgets-$KDDOCKWIDGETS"
tar xf "KDDockWidgets-$KDDOCKWIDGETS.tar.gz"
cd "KDDockWidgets-$KDDOCKWIDGETS"
patch -p1 < "$SCRIPTDIR/../common/kddockwidgets-dodgy-include.patch"
cmake "${CMAKE_COMMON[@]}" -DKDDockWidgets_QT6=true -DKDDockWidgets_EXAMPLES=false -DKDDockWidgets_FRONTENDS=qtwidgets -B build
make -C build "-j$NPROCS"
make -C build install

View File

@@ -0,0 +1,116 @@
diff --git a/.cmake.conf b/.cmake.conf
--- a/.cmake.conf
+++ b/.cmake.conf
@@ -51,7 +51,7 @@ set(QT_MAX_NEW_POLICY_CMAKE_VERSION_QT_APPLE "3.21")
set(QT_SUPPORTED_MIN_MACOS_SDK_VERSION "14")
set(QT_SUPPORTED_MAX_MACOS_SDK_VERSION "26")
set(QT_SUPPORTED_MIN_MACOS_XCODE_VERSION "15")
-set(QT_SUPPORTED_MIN_MACOS_VERSION "13")
+set(QT_SUPPORTED_MIN_MACOS_VERSION "11")
set(QT_SUPPORTED_MAX_MACOS_VERSION_TESTED "26")
set(QT_SUPPORTED_MIN_IOS_SDK_VERSION "17")
diff --git a/CMakeLists.txt b/CMakeLists.txt
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -12,6 +12,10 @@ cmake_minimum_required(VERSION 3.16)
# Get the repo version and CMake policy details
include(.cmake.conf)
+if(APPLE)
+ add_compile_options(-Werror=unguarded-availability-new)
+endif()
+
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/QtBaseHelpers.cmake)
qt_internal_check_if_path_has_symlinks("${CMAKE_BINARY_DIR}")
diff --git a/src/corelib/global/qsysinfo.cpp b/src/corelib/global/qsysinfo.cpp
--- a/src/corelib/global/qsysinfo.cpp
+++ b/src/corelib/global/qsysinfo.cpp
@@ -1027,7 +1027,7 @@ QByteArray QSysInfo::machineUniqueId()
{
#if defined(Q_OS_DARWIN) && __has_include(<IOKit/IOKitLib.h>)
char uuid[UuidStringLen + 1];
- io_service_t service = IOServiceGetMatchingService(kIOMainPortDefault, IOServiceMatching("IOPlatformExpertDevice"));
+ io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice"));
QCFString stringRef = (CFStringRef)IORegistryEntryCreateCFProperty(service, CFSTR(kIOPlatformUUIDKey), kCFAllocatorDefault, 0);
CFStringGetCString(stringRef, uuid, sizeof(uuid), kCFStringEncodingMacRoman);
return QByteArray(uuid);
diff --git a/src/corelib/kernel/qcore_mac.mm b/src/corelib/kernel/qcore_mac.mm
--- a/src/corelib/kernel/qcore_mac.mm
+++ b/src/corelib/kernel/qcore_mac.mm
@@ -367,7 +367,7 @@ bool qt_apple_runningWithLiquidGlass()
return config;
#endif
- QIOType<io_registry_entry_t> nvram = IORegistryEntryFromPath(kIOMainPortDefault, "IODeviceTree:/options");
+ QIOType<io_registry_entry_t> nvram = IORegistryEntryFromPath(kIOMasterPortDefault, "IODeviceTree:/options");
if (!nvram) {
qWarning("Failed to locate NVRAM entry in IO registry");
return {};
diff --git a/src/gui/platform/darwin/qappleiconengine.mm b/src/gui/platform/darwin/qappleiconengine.mm
--- a/src/gui/platform/darwin/qappleiconengine.mm
+++ b/src/gui/platform/darwin/qappleiconengine.mm
@@ -366,12 +366,16 @@
weight:NSFontWeightRegular
scale:NSImageSymbolScaleLarge];
+ auto *primaryColor = [NSColor colorWithSRGBRed:color.redF()
+ green:color.greenF()
+ blue:color.blueF()
+ alpha:color.alphaF()];
+
+ if (@available(macOS 13, *)) {
+
// Apply tint color first, which switches the configuration to palette mode
config = [config configurationByApplyingConfiguration:
- [NSImageSymbolConfiguration configurationWithPaletteColors:@[
- [NSColor colorWithSRGBRed:color.redF() green:color.greenF()
- blue:color.blueF() alpha:color.alphaF()]
- ]]];
+ [NSImageSymbolConfiguration configurationWithPaletteColors:@[primaryColor]]];
// Then switch back to monochrome, as palette mode gives a different look
// than monochrome, even with a single color.
@@ -379,6 +383,18 @@
[NSImageSymbolConfiguration configurationPreferringMonochrome]];
return [image imageWithSymbolConfiguration:config];
+
+ } else {
+ NSImage *configuredImage = [image imageWithSymbolConfiguration:config];
+ return [NSImage imageWithSize:configuredImage.size flipped:NO
+ drawingHandler:^BOOL(NSRect) {
+ [primaryColor set];
+ NSRect imageRect = {NSZeroPoint, configuredImage.size};
+ [configuredImage drawInRect:imageRect];
+ NSRectFillUsingOperation(imageRect, NSCompositingOperationSourceIn);
+ return YES;
+ }];
+ }
}
#elif defined(QT_PLATFORM_UIKIT)
auto *configuredImage(const UIImage *image, const QColor &color)
diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm
--- a/src/plugins/platforms/cocoa/qcocoawindow.mm
+++ b/src/plugins/platforms/cocoa/qcocoawindow.mm
@@ -323,6 +323,8 @@ a normal (not maximized or full screen) top-level window.
m_view.safeAreaInsets.bottom
};
+ if (@available(macOS 12, *)) {
+
// The screen's safe area insets represent the distances from the screen's
// edges at which content isn't obscured. The view's safe area margins do
// not include the screen's insets automatically, so we need to manually
@@ -355,6 +357,10 @@ a normal (not maximized or full screen) top-level window.
};
return (screenSafeAreaMargins | viewSafeAreaMargins).toMargins();
+
+ } else {
+ return viewSafeAreaMargins.toMargins();
+ }
}
void QCocoaWindow::updateSafeAreaMarginsIfNeeded()

View File

@@ -43,20 +43,20 @@ echo INSTALLDIR=%INSTALLDIR%
cd "%BUILDDIR%"
set FREETYPE=2.14.1
set HARFBUZZ=11.5.0
set HARFBUZZ=12.0.0
set LIBJPEGTURBO=3.1.2
set LIBPNG=1650
set SDL=SDL3-3.2.22
set QT=6.9.2
set LIBPNGLONG=1.6.50
set QTMINOR=6.9
set SDL=SDL3-3.2.26
set QT=6.10.1
set QTMINOR=6.10
set QTAPNG=1.3.0
set LZ4=1.10.0
set WEBP=1.6.0
set ZLIB=1.3.1
set ZLIBSHORT=131
set ZSTD=1.5.7
set KDDOCKWIDGETS=2.2.3
set KDDOCKWIDGETS=2.4.0
set PLUTOVG=1.3.1
set PLUTOSVG=0.0.7
@@ -66,22 +66,22 @@ set SHADERC_SPIRVHEADERS=2a611a970fdbc41ac2e3e328802aed9985352dca
set SHADERC_SPIRVTOOLS=33e02568181e3312f49a3cf33df470bf96ef293a
call :downloadfile "freetype-%FREETYPE%.tar.gz" https://sourceforge.net/projects/freetype/files/freetype2/%FREETYPE%/freetype-%FREETYPE%.tar.gz/download 174d9e53402e1bf9ec7277e22ec199ba3e55a6be2c0740cb18c0ee9850fc8c34 || goto error
call :downloadfile "harfbuzz-%HARFBUZZ%.zip" https://github.com/harfbuzz/harfbuzz/archive/refs/tags/%HARFBUZZ%.zip a7c2412e8fce884d98f67a0c79edf7f0b922adc9cbc177cb4fd4eeeca3fcdbe2 || goto error
call :downloadfile "harfbuzz-%HARFBUZZ%.zip" https://github.com/harfbuzz/harfbuzz/archive/refs/tags/%HARFBUZZ%.zip 0f3e036294974736982d8ec00f0bed763cd9f75ab9eacf8effe413af5d78ef06 || goto error
call :downloadfile "lpng%LIBPNG%.zip" https://download.sourceforge.net/libpng/lpng1650.zip 4be6938313b08d5921f9dede13f2789b653c96f4f8595d92ff3f09c9320e51c7 || goto error
call :downloadfile "lpng%LIBPNG%-apng.patch.gz" https://download.sourceforge.net/libpng-apng/libpng-%LIBPNGLONG%-apng.patch.gz 687ddc0c7cb128a3ea58e159b5129252537c27ede0c32a93f11f03127f0c0165 || goto error
call :downloadfile "libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" "https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/%LIBJPEGTURBO%/libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" 8f0012234b464ce50890c490f18194f913a7b1f4e6a03d6644179fa0f867d0cf || goto error
call :downloadfile "libwebp-%WEBP%.tar.gz" "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-%WEBP%.tar.gz" e4ab7009bf0629fd11982d4c2aa83964cf244cffba7347ecd39019a9e38c4564 || goto error
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" 3d60068b1e5c83c66bb14c325dfef46f8fcc380735b4591de6f5e7b9738929d1 || goto error
call :downloadfile "qtbase-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtbase-everywhere-src-%QT%.zip" 97d59c78e40b4ddd018738d285a12afc320b57f8265a3f760353739a3619ccdb || goto error
call :downloadfile "qtimageformats-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtimageformats-everywhere-src-%QT%.zip" f2fc6ff382c6f3af79493d0709dbd64847d0356313518f094f9096315f2fdb30 || goto error
call :downloadfile "qtsvg-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtsvg-everywhere-src-%QT%.zip" af80bb671ea0f66c0036ce7041a56b0e550fc94fb88d2c77b5b6a3e33e42139b || goto error
call :downloadfile "qttools-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttools-everywhere-src-%QT%.zip" d2f4c7a4a12630e879702353f944f96a5d8e764771b5a5f04163334ad61b39db || goto error
call :downloadfile "qttranslations-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttranslations-everywhere-src-%QT%.zip" 3e168d1b081ee3a2175fe1bd97ad03bb40fe7ce38a37e99923a19f0e7ec4d81c || goto error
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" 739356eef1192fff9d641c320a8f5ef4a10506b8927def4b9ceb764c7e947369 || goto error
call :downloadfile "qtbase-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtbase-everywhere-src-%QT%.zip" c43f471a808b07fc541528410e94ce89c6745bdc1d744492e19911d35fbf7d33 || goto error
call :downloadfile "qtimageformats-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtimageformats-everywhere-src-%QT%.zip" 2d828d8c999fdd18167937c071781c22321c643b04a106c714411c2356cdb26d || goto error
call :downloadfile "qtsvg-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtsvg-everywhere-src-%QT%.zip" ddd74a417d2397eb085d047a9b6ba52b76e748055817f728fe691f8456035d23 || goto error
call :downloadfile "qttools-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttools-everywhere-src-%QT%.zip" db8e49ed50912c3c064a4f9ada7791c09eccec5a8d53463a19608eaab17679f0 || goto error
call :downloadfile "qttranslations-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttranslations-everywhere-src-%QT%.zip" 868eb651e395d48ade5932ef2c386e606e054eb5888ebe5284fbd8cb63ed935a || goto error
call :downloadfile "QtApng-%QTAPNG%.zip" "https://github.com/jurplel/QtApng/archive/refs/tags/%QTAPNG%.zip" 5176082cdd468047a7eb1ec1f106b032f57df207aa318d559b29606b00d159ac || goto error
call :downloadfile "lz4-%LZ4%.zip" "https://github.com/lz4/lz4/archive/refs/tags/v%LZ4%.zip" 3224b4c80f351f194984526ef396f6079bd6332dd9825c72ac0d7a37b3cdc565 || goto error
call :downloadfile "zlib%ZLIBSHORT%.zip" "https://zlib.net/zlib%ZLIBSHORT%.zip" 72af66d44fcc14c22013b46b814d5d2514673dda3d115e64b690c1ad636e7b17 || goto error
call :downloadfile "zstd-%ZSTD%.zip" "https://github.com/facebook/zstd/archive/refs/tags/v%ZSTD%.zip" 7897bc5d620580d9b7cd3539c44b59d78f3657d33663fe97a145e07b4ebd69a4 || goto error
call :downloadfile "KDDockWidgets-%KDDOCKWIDGETS%.zip" "https://github.com/KDAB/KDDockWidgets/archive/v%KDDOCKWIDGETS%.zip" 1ba8e5b48f3b4d47d2de7121529d448532200fa36d9ed21f93909f6eb03f61cb || goto error
call :downloadfile "KDDockWidgets-%KDDOCKWIDGETS%.zip" "https://github.com/KDAB/KDDockWidgets/archive/v%KDDOCKWIDGETS%.zip" 47ddb48197872055f0adf8e90a7235f8a3b795ca1ee3a28ac2c504c673ae3806 || goto error
call :downloadfile "plutovg-%PLUTOVG%.zip" "https://github.com/sammycage/plutovg/archive/v%PLUTOVG%.zip" 615184f756d91ce416f2cf883bb67fd4262651417c2e40c4d681c8641a48263e || goto error
call :downloadfile "plutosvg-%PLUTOSVG%.zip" "https://github.com/sammycage/plutosvg/archive/v%PLUTOSVG%.zip" 82dee2c57ad712bdd6d6d81d3e76249d89caa4b5a4214353660fd5adff12201a || goto error
@@ -96,7 +96,7 @@ if %DEBUG%==1 (
echo Building release libraries...
)
set FORCEPDB=-DCMAKE_SHARED_LINKER_FLAGS_RELEASE="/DEBUG"
set FORCEPDB=-DCMAKE_SHARED_LINKER_FLAGS_RELEASE="/DEBUG" -DCMAKE_SHARED_LINKER_FLAGS_MINSIZEREL="/DEBUG"
set ARM64TOOLCHAIN=-DCMAKE_TOOLCHAIN_FILE="%SCRIPTDIR%\cmake-toolchain-windows-arm64.cmake"
echo Building Zlib...
@@ -197,7 +197,7 @@ cd .. || goto error
if %DEBUG%==1 (
set QTBUILDSPEC=-DCMAKE_CONFIGURATION_TYPES="Release;Debug" -G "Ninja Multi-Config"
) else (
set QTBUILDSPEC=-DCMAKE_BUILD_TYPE=Release -G Ninja
set QTBUILDSPEC=-DCMAKE_BUILD_TYPE=MinSizeRel -G Ninja
)
echo Building Qt base...
@@ -282,7 +282,6 @@ echo "Building KDDockWidgets..."
rmdir /S /Q "KDDockWidgets-%KDDOCKWIDGETS%"
%SEVENZIP% x "KDDockWidgets-%KDDOCKWIDGETS%.zip" || goto error
cd "KDDockWidgets-%KDDOCKWIDGETS%" || goto error
%PATCH% -p1 < "%SCRIPTDIR%\..\common\kddockwidgets-dodgy-include.patch" || goto error
cmake -B build %ARM64TOOLCHAIN% -DCMAKE_PREFIX_PATH="%INSTALLDIR%" -DCMAKE_INSTALL_PREFIX="%INSTALLDIR%" -DKDDockWidgets_QT6=true -DKDDockWidgets_EXAMPLES=false -DKDDockWidgets_FRONTENDS=qtwidgets %KDDOCKWIDGETSBUILDSPEC% || goto error
cmake --build build --parallel || goto error
ninja -C build install || goto error

View File

@@ -41,20 +41,20 @@ set "PATH=%PATH%;%INSTALLDIR%\bin"
cd "%BUILDDIR%"
set FREETYPE=2.14.1
set HARFBUZZ=11.5.0
set HARFBUZZ=12.0.0
set LIBJPEGTURBO=3.1.2
set LIBPNG=1650
set SDL=SDL3-3.2.22
set SDL=SDL3-3.2.26
set LIBPNGLONG=1.6.50
set QT=6.9.2
set QTMINOR=6.9
set QT=6.10.1
set QTMINOR=6.10
set QTAPNG=1.3.0
set LZ4=1.10.0
set WEBP=1.6.0
set ZLIB=1.3.1
set ZLIBSHORT=131
set ZSTD=1.5.7
set KDDOCKWIDGETS=2.2.3
set KDDOCKWIDGETS=2.4.0
set PLUTOVG=1.3.1
set PLUTOSVG=0.0.7
@@ -64,22 +64,22 @@ set SHADERC_SPIRVHEADERS=2a611a970fdbc41ac2e3e328802aed9985352dca
set SHADERC_SPIRVTOOLS=33e02568181e3312f49a3cf33df470bf96ef293a
call :downloadfile "freetype-%FREETYPE%.tar.gz" https://sourceforge.net/projects/freetype/files/freetype2/%FREETYPE%/freetype-%FREETYPE%.tar.gz/download 174d9e53402e1bf9ec7277e22ec199ba3e55a6be2c0740cb18c0ee9850fc8c34 || goto error
call :downloadfile "harfbuzz-%HARFBUZZ%.zip" https://github.com/harfbuzz/harfbuzz/archive/refs/tags/%HARFBUZZ%.zip a7c2412e8fce884d98f67a0c79edf7f0b922adc9cbc177cb4fd4eeeca3fcdbe2 || goto error
call :downloadfile "harfbuzz-%HARFBUZZ%.zip" https://github.com/harfbuzz/harfbuzz/archive/refs/tags/%HARFBUZZ%.zip 0f3e036294974736982d8ec00f0bed763cd9f75ab9eacf8effe413af5d78ef06 || goto error
call :downloadfile "lpng%LIBPNG%.zip" https://download.sourceforge.net/libpng/lpng1650.zip 4be6938313b08d5921f9dede13f2789b653c96f4f8595d92ff3f09c9320e51c7 || goto error
call :downloadfile "lpng%LIBPNG%-apng.patch.gz" https://download.sourceforge.net/libpng-apng/libpng-%LIBPNGLONG%-apng.patch.gz 687ddc0c7cb128a3ea58e159b5129252537c27ede0c32a93f11f03127f0c0165 || goto error
call :downloadfile "libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" "https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/%LIBJPEGTURBO%/libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" 8f0012234b464ce50890c490f18194f913a7b1f4e6a03d6644179fa0f867d0cf || goto error
call :downloadfile "libwebp-%WEBP%.tar.gz" "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-%WEBP%.tar.gz" e4ab7009bf0629fd11982d4c2aa83964cf244cffba7347ecd39019a9e38c4564 || goto error
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" 3d60068b1e5c83c66bb14c325dfef46f8fcc380735b4591de6f5e7b9738929d1 || goto error
call :downloadfile "qtbase-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtbase-everywhere-src-%QT%.zip" 97d59c78e40b4ddd018738d285a12afc320b57f8265a3f760353739a3619ccdb || goto error
call :downloadfile "qtimageformats-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtimageformats-everywhere-src-%QT%.zip" f2fc6ff382c6f3af79493d0709dbd64847d0356313518f094f9096315f2fdb30 || goto error
call :downloadfile "qtsvg-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtsvg-everywhere-src-%QT%.zip" af80bb671ea0f66c0036ce7041a56b0e550fc94fb88d2c77b5b6a3e33e42139b || goto error
call :downloadfile "qttools-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttools-everywhere-src-%QT%.zip" d2f4c7a4a12630e879702353f944f96a5d8e764771b5a5f04163334ad61b39db || goto error
call :downloadfile "qttranslations-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttranslations-everywhere-src-%QT%.zip" 3e168d1b081ee3a2175fe1bd97ad03bb40fe7ce38a37e99923a19f0e7ec4d81c || goto error
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" 739356eef1192fff9d641c320a8f5ef4a10506b8927def4b9ceb764c7e947369 || goto error
call :downloadfile "qtbase-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtbase-everywhere-src-%QT%.zip" c43f471a808b07fc541528410e94ce89c6745bdc1d744492e19911d35fbf7d33 || goto error
call :downloadfile "qtimageformats-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtimageformats-everywhere-src-%QT%.zip" 2d828d8c999fdd18167937c071781c22321c643b04a106c714411c2356cdb26d || goto error
call :downloadfile "qtsvg-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtsvg-everywhere-src-%QT%.zip" ddd74a417d2397eb085d047a9b6ba52b76e748055817f728fe691f8456035d23 || goto error
call :downloadfile "qttools-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttools-everywhere-src-%QT%.zip" db8e49ed50912c3c064a4f9ada7791c09eccec5a8d53463a19608eaab17679f0 || goto error
call :downloadfile "qttranslations-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttranslations-everywhere-src-%QT%.zip" 868eb651e395d48ade5932ef2c386e606e054eb5888ebe5284fbd8cb63ed935a || goto error
call :downloadfile "QtApng-%QTAPNG%.zip" "https://github.com/jurplel/QtApng/archive/refs/tags/%QTAPNG%.zip" 5176082cdd468047a7eb1ec1f106b032f57df207aa318d559b29606b00d159ac || goto error
call :downloadfile "lz4-%LZ4%.zip" "https://github.com/lz4/lz4/archive/refs/tags/v%LZ4%.zip" 3224b4c80f351f194984526ef396f6079bd6332dd9825c72ac0d7a37b3cdc565 || goto error
call :downloadfile "zlib%ZLIBSHORT%.zip" "https://zlib.net/zlib%ZLIBSHORT%.zip" 72af66d44fcc14c22013b46b814d5d2514673dda3d115e64b690c1ad636e7b17 || goto error
call :downloadfile "zstd-%ZSTD%.zip" "https://github.com/facebook/zstd/archive/refs/tags/v%ZSTD%.zip" 7897bc5d620580d9b7cd3539c44b59d78f3657d33663fe97a145e07b4ebd69a4 || goto error
call :downloadfile "KDDockWidgets-%KDDOCKWIDGETS%.zip" "https://github.com/KDAB/KDDockWidgets/archive/v%KDDOCKWIDGETS%.zip" 1ba8e5b48f3b4d47d2de7121529d448532200fa36d9ed21f93909f6eb03f61cb || goto error
call :downloadfile "KDDockWidgets-%KDDOCKWIDGETS%.zip" "https://github.com/KDAB/KDDockWidgets/archive/v%KDDOCKWIDGETS%.zip" 47ddb48197872055f0adf8e90a7235f8a3b795ca1ee3a28ac2c504c673ae3806 || goto error
call :downloadfile "plutovg-%PLUTOVG%.zip" "https://github.com/sammycage/plutovg/archive/v%PLUTOVG%.zip" 615184f756d91ce416f2cf883bb67fd4262651417c2e40c4d681c8641a48263e || goto error
call :downloadfile "plutosvg-%PLUTOSVG%.zip" "https://github.com/sammycage/plutosvg/archive/v%PLUTOSVG%.zip" 82dee2c57ad712bdd6d6d81d3e76249d89caa4b5a4214353660fd5adff12201a || goto error
@@ -94,7 +94,7 @@ if %DEBUG%==1 (
echo Building release libraries...
)
set FORCEPDB=-DCMAKE_SHARED_LINKER_FLAGS_RELEASE="/DEBUG" -DCMAKE_MODULE_LINKER_FLAGS_RELEASE="/DEBUG"
set FORCEPDB=-DCMAKE_SHARED_LINKER_FLAGS_RELEASE="/DEBUG" -DCMAKE_MODULE_LINKER_FLAGS_RELEASE="/DEBUG" -DCMAKE_SHARED_LINKER_FLAGS_MINSIZEREL="/DEBUG" -DCMAKE_MODULE_LINKER_FLAGS_MINSIZEREL="/DEBUG"
echo Building Zlib...
rmdir /S /Q "zlib-%ZLIB%"
@@ -194,7 +194,7 @@ cd .. || goto error
if %DEBUG%==1 (
set QTBUILDSPEC=-DCMAKE_CONFIGURATION_TYPES="Release;Debug" -G "Ninja Multi-Config"
) else (
set QTBUILDSPEC=-DCMAKE_BUILD_TYPE=Release -G Ninja
set QTBUILDSPEC=-DCMAKE_BUILD_TYPE=MinSizeRel -G Ninja
)
echo Building Qt base...
@@ -233,7 +233,7 @@ ninja install || goto error
cd ..\.. || goto error
echo Building Qt Tools...
rmdir /S /Q "qtimageformats-everywhere-src-%QT%"
rmdir /S /Q "qttools-everywhere-src-%QT%"
%SEVENZIP% x "qttools-everywhere-src-%QT%.zip" || goto error
cd "qttools-everywhere-src-%QT%" || goto error
mkdir build || goto error
@@ -283,7 +283,6 @@ echo "Building KDDockWidgets..."
rmdir /S /Q "KDDockWidgets-%KDDOCKWIDGETS%"
%SEVENZIP% x "KDDockWidgets-%KDDOCKWIDGETS%.zip" || goto error
cd "KDDockWidgets-%KDDOCKWIDGETS%" || goto error
%PATCH% -p1 < "%SCRIPTDIR%\..\common\kddockwidgets-dodgy-include.patch" || goto error
cmake -B build -DCMAKE_PREFIX_PATH="%INSTALLDIR%" -DCMAKE_INSTALL_PREFIX="%INSTALLDIR%" -DKDDockWidgets_QT6=true -DKDDockWidgets_EXAMPLES=false -DKDDockWidgets_FRONTENDS=qtwidgets %KDDOCKWIDGETSBUILDSPEC% || goto error
cmake --build build --parallel || goto error
ninja -C build install || goto error

View File

@@ -154,7 +154,7 @@ jobs:
cmake --build build --config Release --target unittests
- name: Upload artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: ${{ steps.artifact-metadata.outputs.artifact-name }}
path: |
@@ -186,7 +186,7 @@ jobs:
}
- name: Upload artifact - with symbols
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: ${{ steps.artifact-metadata.outputs.artifact-name }}-symbols
path: |

View File

@@ -0,0 +1,17 @@
name: 🖥️ 📦 Dispatch Windows Deps Build
on:
pull_request_target:
types: [closed]
jobs:
trigger:
if: github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'requires-win-deps-build')
runs-on: ubuntu-latest
steps:
- name: Dispatch to windows-dependencies repo
uses: peter-evans/repository-dispatch@v4
with:
token: ${{ secrets.DEPS_REPO_DISPATCH_ACCESS_TOKEN }}
repository: pcsx2/pcsx2-windows-dependencies
event-type: deps-update

View File

@@ -1,7 +1,117 @@
# libcubeb - Cross-platform Audio I/O Library
[![Build Status](https://github.com/mozilla/cubeb/actions/workflows/build.yml/badge.svg)](https://github.com/mozilla/cubeb/actions/workflows/build.yml)
See INSTALL.md for build instructions.
`libcubeb` is a cross-platform C library for high and low-latency audio input/output. It provides a simple, consistent API for audio playback and recording across multiple platforms and audio backends. It is written in C, C++ and Rust, with a C ABI and [Rust](https://github.com/mozilla/cubeb-rs) bindings. While originally written for use in the Firefox Web browser, a number of other software projects have adopted it.
See [Backend Support](https://github.com/mozilla/cubeb/wiki/Backend-Support) in the wiki for the support level of each backend.
## Features
Licensed under an ISC-style license. See LICENSE for details.
- **Cross-platform support**: Windows, macOS, Linux, Android, and other platforms
- **Versatile**: Optimized for low-latency real-time audio applications, or power efficient higher latency playback
- **A/V sync**: Latency compensated audio clock reporting for easy audio/video synchronization
- **Full-duplex support**: Simultaneous audio input and output, reclocked
- **Device enumeration**: Query available audio devices
- **Audio processing for speech**: Can use VoiceProcessing IO on recent macOS
## Supported Backends & status
| *Backend* | *Support Level* | *Platform version* | *Notes* |
|-------------------|-----------------|--------------------|--------------------------------------------------|
| PulseAudio (Rust) | Tier-1 | | Main Linux desktop backend |
| AudioUnit (Rust) | Tier-1 | | Main macOS backend |
| WASAPI | Tier-1 | Windows >= 7 | Main Windows backend |
| AAudio | Tier-1 | Android >= 8 | Main Android backend for most devices |
| OpenSL | Tier-1 | Android >= 2.3 | Android backend for older devices |
| OSS | Tier-2 | | |
| sndio | Tier-2 | | |
| Sun | Tier-2 | | |
| WinMM | Tier-3 | Windows XP | Was Tier-1, Firefox minimum Windows version 7. |
| AudioTrack | Tier-3 | Android < 2.3 | Was Tier-1, Firefox minimum Android version 4.1. |
| ALSA | Tier-3 | | |
| JACK | Tier-3 | | |
| KAI | Tier-3 | | |
| PulseAudio (C) | Tier-4 | | Was Tier-1, superseded by Rust |
| AudioUnit (C++) | Tier-4 | | Was Tier-1, superseded by Rust |
Tier-1: Actively maintained. Should have CI coverage. Critical for Firefox.
Tier-2: Actively maintained by contributors. CI coverage appreciated.
Tier-3: Maintainers/patches accepted. Status unclear.
Tier-4: Deprecated, obsolete. Scheduled to be removed.
Note that the support level is not a judgement of the relative merits
of a backend, only the current state of support, which is informed
by Firefox's needs, the responsiveness of a backend's
maintainer, and the level of contributions to that backend.
## Building
### Prerequisites
- CMake 3.15 or later
- Non-ancient MSVC, clang or gcc, for compiling both C and C++
- Platform-specific audio libraries (automatically detected)
- Optional but recommended: Rust compiler to compile and link more recent backends for macOS and PulseAudio
### Quick build
```bash
git clone https://github.com/mozilla/cubeb.git
cd cubeb
cmake -B build
cmake --build build
```
### Better build with Rust backends
```bash
git clone --recursive https://github.com/mozilla/cubeb.git
cd cubeb
cmake -B build -DBUILD_RUST_LIBS=ON
cmake --build build
```
### Platform-Specific Notes
**Windows**: Supports Visual Studio 2015+ and MinGW-w64. Use `-G "Visual Studio 16 2019"` or `-G "MinGW Makefiles"`.
**macOS**: Requires Xcode command line tools. Audio frameworks are automatically linked.
**Linux**: Development packages for desired backends:
```bash
# Ubuntu/Debian
sudo apt-get install libpulse-dev libasound2-dev libjack-dev
# Fedora/RHEL
sudo dnf install pulseaudio-libs-devel alsa-lib-devel jack-audio-connection-kit-devel
```
**Android**: Use with Android NDK. AAudio requires API level 26+.
## Testing
Run the test suite:
```bash
cd build
ctest
```
Use the interactive test tool:
```bash
./cubeb-test
```
## License
Licensed under an ISC-style license. See [LICENSE](LICENSE) for details.
## Contributing
Contributions are welcome! Please see the [contribution guidelines](CONTRIBUTING.md) and check the [issue tracker](https://github.com/mozilla/cubeb/issues).
## Links
- [GitHub Repository](https://github.com/mozilla/cubeb)
- [API Documentation](https://mozilla.github.io/cubeb/)

View File

@@ -49,6 +49,7 @@ extern "C" {
output_params.channels = 2;
output_params.layout = CUBEB_LAYOUT_UNDEFINED;
output_params.prefs = CUBEB_STREAM_PREF_NONE;
output_params.input_params = CUBEB_INPUT_PROCESSING_PARAM_NONE;
rv = cubeb_get_min_latency(app_ctx, &output_params, &latency_frames);
if (rv != CUBEB_OK) {
@@ -62,6 +63,7 @@ extern "C" {
input_params.channels = 1;
input_params.layout = CUBEB_LAYOUT_UNDEFINED;
input_params.prefs = CUBEB_STREAM_PREF_NONE;
input_params.input_params = CUBEB_INPUT_PROCESSING_PARAM_NONE;
cubeb_stream * stm;
rv = cubeb_stream_init(app_ctx, &stm, "Example Stream 1",
@@ -193,39 +195,39 @@ typedef uint32_t cubeb_channel_layout;
// Some common layout definitions.
enum {
CUBEB_LAYOUT_UNDEFINED = 0, // Indicate the speaker's layout is undefined.
CUBEB_LAYOUT_MONO = (uint32_t)CHANNEL_FRONT_CENTER,
CUBEB_LAYOUT_MONO_LFE = (uint32_t)CUBEB_LAYOUT_MONO | (uint32_t)CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_STEREO = (uint32_t)CHANNEL_FRONT_LEFT | (uint32_t)CHANNEL_FRONT_RIGHT,
CUBEB_LAYOUT_STEREO_LFE = (uint32_t)CUBEB_LAYOUT_STEREO | (uint32_t)CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_MONO = CHANNEL_FRONT_CENTER,
CUBEB_LAYOUT_MONO_LFE = CUBEB_LAYOUT_MONO | CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_STEREO = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT,
CUBEB_LAYOUT_STEREO_LFE = CUBEB_LAYOUT_STEREO | CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_3F =
(uint32_t)CHANNEL_FRONT_LEFT | (uint32_t)CHANNEL_FRONT_RIGHT | (uint32_t)CHANNEL_FRONT_CENTER,
CUBEB_LAYOUT_3F_LFE = (uint32_t)CUBEB_LAYOUT_3F | (uint32_t)CHANNEL_LOW_FREQUENCY,
CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT | CHANNEL_FRONT_CENTER,
CUBEB_LAYOUT_3F_LFE = CUBEB_LAYOUT_3F | CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_2F1 =
(uint32_t)CHANNEL_FRONT_LEFT | (uint32_t)CHANNEL_FRONT_RIGHT | (uint32_t)CHANNEL_BACK_CENTER,
CUBEB_LAYOUT_2F1_LFE = (uint32_t)CUBEB_LAYOUT_2F1 | (uint32_t)CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_3F1 = (uint32_t)CHANNEL_FRONT_LEFT | (uint32_t)CHANNEL_FRONT_RIGHT |
(uint32_t)CHANNEL_FRONT_CENTER | (uint32_t)CHANNEL_BACK_CENTER,
CUBEB_LAYOUT_3F1_LFE = (uint32_t)CUBEB_LAYOUT_3F1 | (uint32_t)CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_2F2 = (uint32_t)CHANNEL_FRONT_LEFT | (uint32_t)CHANNEL_FRONT_RIGHT |
(uint32_t)CHANNEL_SIDE_LEFT | (uint32_t)CHANNEL_SIDE_RIGHT,
CUBEB_LAYOUT_2F2_LFE = (uint32_t)CUBEB_LAYOUT_2F2 | (uint32_t)CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_QUAD = (uint32_t)CHANNEL_FRONT_LEFT | (uint32_t)CHANNEL_FRONT_RIGHT |
(uint32_t)CHANNEL_BACK_LEFT | (uint32_t)CHANNEL_BACK_RIGHT,
CUBEB_LAYOUT_QUAD_LFE = (uint32_t)CUBEB_LAYOUT_QUAD | (uint32_t)CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_3F2 = (uint32_t)CHANNEL_FRONT_LEFT | (uint32_t)CHANNEL_FRONT_RIGHT |
(uint32_t)CHANNEL_FRONT_CENTER | (uint32_t)CHANNEL_SIDE_LEFT |
(uint32_t)CHANNEL_SIDE_RIGHT,
CUBEB_LAYOUT_3F2_LFE = (uint32_t)CUBEB_LAYOUT_3F2 | (uint32_t)CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_3F2_BACK = (uint32_t)CUBEB_LAYOUT_QUAD | (uint32_t)CHANNEL_FRONT_CENTER,
CUBEB_LAYOUT_3F2_LFE_BACK = (uint32_t)CUBEB_LAYOUT_3F2_BACK | (uint32_t)CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_3F3R_LFE = (uint32_t)CHANNEL_FRONT_LEFT | (uint32_t)CHANNEL_FRONT_RIGHT |
(uint32_t)CHANNEL_FRONT_CENTER | (uint32_t)CHANNEL_LOW_FREQUENCY |
(uint32_t)CHANNEL_BACK_CENTER | (uint32_t)CHANNEL_SIDE_LEFT |
(uint32_t)CHANNEL_SIDE_RIGHT,
CUBEB_LAYOUT_3F4_LFE = (uint32_t)CHANNEL_FRONT_LEFT | (uint32_t)CHANNEL_FRONT_RIGHT |
(uint32_t)CHANNEL_FRONT_CENTER | (uint32_t)CHANNEL_LOW_FREQUENCY |
(uint32_t)CHANNEL_BACK_LEFT | (uint32_t)CHANNEL_BACK_RIGHT |
(uint32_t)CHANNEL_SIDE_LEFT | (uint32_t)CHANNEL_SIDE_RIGHT,
CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT | CHANNEL_BACK_CENTER,
CUBEB_LAYOUT_2F1_LFE = CUBEB_LAYOUT_2F1 | CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_3F1 = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
CHANNEL_FRONT_CENTER | CHANNEL_BACK_CENTER,
CUBEB_LAYOUT_3F1_LFE = CUBEB_LAYOUT_3F1 | CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_2F2 = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
CHANNEL_SIDE_LEFT | CHANNEL_SIDE_RIGHT,
CUBEB_LAYOUT_2F2_LFE = CUBEB_LAYOUT_2F2 | CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_QUAD = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
CHANNEL_BACK_LEFT | CHANNEL_BACK_RIGHT,
CUBEB_LAYOUT_QUAD_LFE = CUBEB_LAYOUT_QUAD | CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_3F2 = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
CHANNEL_FRONT_CENTER | CHANNEL_SIDE_LEFT |
CHANNEL_SIDE_RIGHT,
CUBEB_LAYOUT_3F2_LFE = CUBEB_LAYOUT_3F2 | CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_3F2_BACK = CUBEB_LAYOUT_QUAD | CHANNEL_FRONT_CENTER,
CUBEB_LAYOUT_3F2_LFE_BACK = CUBEB_LAYOUT_3F2_BACK | CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_3F3R_LFE = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
CHANNEL_FRONT_CENTER | CHANNEL_LOW_FREQUENCY |
CHANNEL_BACK_CENTER | CHANNEL_SIDE_LEFT |
CHANNEL_SIDE_RIGHT,
CUBEB_LAYOUT_3F4_LFE = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
CHANNEL_FRONT_CENTER | CHANNEL_LOW_FREQUENCY |
CHANNEL_BACK_LEFT | CHANNEL_BACK_RIGHT |
CHANNEL_SIDE_LEFT | CHANNEL_SIDE_RIGHT,
};
/** Miscellaneous stream preferences. */
@@ -279,7 +281,10 @@ typedef struct {
cubeb_channel_layout
layout; /**< Requested channel layout. This must be consistent with the
provided channels. CUBEB_LAYOUT_UNDEFINED if unknown */
cubeb_stream_prefs prefs; /**< Requested preferences. */
cubeb_stream_prefs prefs; /**< Requested preferences. */
cubeb_input_processing_params input_params; /**< Requested input processing
params. Ignored for output streams. At present, only supported on the
WASAPI backend; others should use cubeb_set_input_processing_params. */
} cubeb_stream_params;
/** Audio device description */
@@ -414,6 +419,13 @@ typedef struct {
size_t count; /**< Device count in collection. */
} cubeb_device_collection;
/** Array of compiled backends returned by `cubeb_get_backend_names`. */
typedef struct {
const char * const *
names; /**< Array of strings representing backend names. */
size_t count; /**< Length of the array. */
} cubeb_backend_names;
/** User supplied data callback.
- Calling other cubeb functions from this callback is unsafe.
- The code in the callback should be non-blocking.
@@ -454,6 +466,8 @@ typedef void (*cubeb_device_changed_callback)(void * user_ptr);
/**
* User supplied callback called when the underlying device collection changed.
* This callback will be called when devices are added or removed from the
* system, or when the default device changes for the specified device type.
* @param context A pointer to the cubeb context.
* @param user_ptr The pointer passed to
* cubeb_register_device_collection_changed. */
@@ -485,17 +499,18 @@ CUBEB_EXPORT int
cubeb_init(cubeb ** context, char const * context_name,
char const * backend_name);
/** Returns a list of backend names which can be supplid to cubeb_init().
Array is null-terminated. */
CUBEB_EXPORT const char**
cubeb_get_backend_names();
/** Get a read-only string identifying this context's current backend.
@param context A pointer to the cubeb context.
@retval Read-only string identifying current backend. */
CUBEB_EXPORT char const *
cubeb_get_backend_id(cubeb * context);
/** Get a read-only array of strings identifying available backends.
These can be passed as `backend_name` parameter to `cubeb_init`.
@retval Struct containing the array with backend names. */
CUBEB_EXPORT cubeb_backend_names
cubeb_get_backend_names();
/** Get the maximum possible number of channels.
@param context A pointer to the cubeb context.
@param max_channels The maximum number of channels.
@@ -674,7 +689,7 @@ cubeb_stream_get_current_device(cubeb_stream * stm,
application is accessing audio input. When all inputs are muted they can
prove to the user that the application is not actively capturing any input.
@param stream the stream for which to set input mute state
@param muted whether the input should mute or not
@param mute whether the input should mute or not
@retval CUBEB_OK
@retval CUBEB_ERROR_INVALID_PARAMETER if this stream does not have an input
device
@@ -745,14 +760,16 @@ cubeb_device_collection_destroy(cubeb * context,
cubeb_device_collection * collection);
/** Registers a callback which is called when the system detects
a new device or a device is removed.
a new device or a device is removed, or when the default device
changes for the specified device type.
@param context
@param devtype device type to include. Different callbacks and user pointers
can be registered for each devtype. The hybrid devtype
`CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT` is also valid
and will register the provided callback and user pointer in both
sides.
@param callback a function called whenever the system device list changes.
@param callback a function called whenever the system device list changes,
including when default devices change.
Passing NULL allow to unregister a function. You have to unregister
first before you register a new callback.
@param user_ptr pointer to user specified data which will be present in

View File

@@ -31,6 +31,10 @@ struct cubeb_stream {
int
pulse_init(cubeb ** context, char const * context_name);
#endif
#if defined(USE_PULSE_RUST)
int
pulse_rust_init(cubeb ** contet, char const * context_name);
#endif
#if defined(USE_JACK)
int
jack_init(cubeb ** context, char const * context_name);
@@ -43,6 +47,10 @@ alsa_init(cubeb ** context, char const * context_name);
int
audiounit_init(cubeb ** context, char const * context_name);
#endif
#if defined(USE_AUDIOUNIT_RUST)
int
audiounit_rust_init(cubeb ** contet, char const * context_name);
#endif
#if defined(USE_WINMM)
int
winmm_init(cubeb ** context, char const * context_name);
@@ -55,10 +63,30 @@ wasapi_init(cubeb ** context, char const * context_name);
int
sndio_init(cubeb ** context, char const * context_name);
#endif
#if defined(USE_SUN)
int
sun_init(cubeb ** context, char const * context_name);
#endif
#if defined(USE_OPENSL)
int
opensl_init(cubeb ** context, char const * context_name);
#endif
#if defined(USE_OSS)
int
oss_init(cubeb ** context, char const * context_name);
#endif
#if defined(USE_AAUDIO)
int
aaudio_init(cubeb ** context, char const * context_name);
#endif
#if defined(USE_AUDIOTRACK)
int
audiotrack_init(cubeb ** context, char const * context_name);
#endif
#if defined(USE_KAI)
int
kai_init(cubeb ** context, char const * context_name);
#endif
static int
validate_stream_params(cubeb_stream_params * input_stream_params,
@@ -123,6 +151,10 @@ cubeb_init(cubeb ** context, char const * context_name,
if (!strcmp(backend_name, "pulse")) {
#if defined(USE_PULSE)
init_oneshot = pulse_init;
#endif
} else if (!strcmp(backend_name, "pulse-rust")) {
#if defined(USE_PULSE_RUST)
init_oneshot = pulse_rust_init;
#endif
} else if (!strcmp(backend_name, "jack")) {
#if defined(USE_JACK)
@@ -135,6 +167,10 @@ cubeb_init(cubeb ** context, char const * context_name,
} else if (!strcmp(backend_name, "audiounit")) {
#if defined(USE_AUDIOUNIT)
init_oneshot = audiounit_init;
#endif
} else if (!strcmp(backend_name, "audiounit-rust")) {
#if defined(USE_AUDIOUNIT_RUST)
init_oneshot = audiounit_rust_init;
#endif
} else if (!strcmp(backend_name, "wasapi")) {
#if defined(USE_WASAPI)
@@ -147,10 +183,30 @@ cubeb_init(cubeb ** context, char const * context_name,
} else if (!strcmp(backend_name, "sndio")) {
#if defined(USE_SNDIO)
init_oneshot = sndio_init;
#endif
} else if (!strcmp(backend_name, "sun")) {
#if defined(USE_SUN)
init_oneshot = sun_init;
#endif
} else if (!strcmp(backend_name, "opensl")) {
#if defined(USE_OPENSL)
init_oneshot = opensl_init;
#endif
} else if (!strcmp(backend_name, "oss")) {
#if defined(USE_OSS)
init_oneshot = oss_init;
#endif
} else if (!strcmp(backend_name, "aaudio")) {
#if defined(USE_AAUDIO)
init_oneshot = aaudio_init;
#endif
} else if (!strcmp(backend_name, "audiotrack")) {
#if defined(USE_AUDIOTRACK)
init_oneshot = audiotrack_init;
#endif
} else if (!strcmp(backend_name, "kai")) {
#if defined(USE_KAI)
init_oneshot = kai_init;
#endif
} else {
/* Already set */
@@ -163,6 +219,9 @@ cubeb_init(cubeb ** context, char const * context_name,
* to override all other choices
*/
init_oneshot,
#if defined(USE_PULSE_RUST)
pulse_rust_init,
#endif
#if defined(USE_PULSE)
pulse_init,
#endif
@@ -178,6 +237,9 @@ cubeb_init(cubeb ** context, char const * context_name,
#if defined(USE_OSS)
oss_init,
#endif
#if defined(USE_AUDIOUNIT_RUST)
audiounit_rust_init,
#endif
#if defined(USE_AUDIOUNIT)
audiounit_init,
#endif
@@ -189,6 +251,18 @@ cubeb_init(cubeb ** context, char const * context_name,
#endif
#if defined(USE_SUN)
sun_init,
#endif
#if defined(USE_AAUDIO)
aaudio_init,
#endif
#if defined(USE_OPENSL)
opensl_init,
#endif
#if defined(USE_AUDIOTRACK)
audiotrack_init,
#endif
#if defined(USE_KAI)
kai_init,
#endif
};
int i;
@@ -214,13 +288,26 @@ cubeb_init(cubeb ** context, char const * context_name,
return CUBEB_ERROR;
}
const char**
char const *
cubeb_get_backend_id(cubeb * context)
{
if (!context) {
return NULL;
}
return context->ops->get_backend_id(context);
}
cubeb_backend_names
cubeb_get_backend_names()
{
static const char* backend_names[] = {
static const char * const backend_names[] = {
#if defined(USE_PULSE)
"pulse",
#endif
#if defined(USE_PULSE_RUST)
"pulse-rust",
#endif
#if defined(USE_JACK)
"jack",
#endif
@@ -230,6 +317,9 @@ cubeb_get_backend_names()
#if defined(USE_AUDIOUNIT)
"audiounit",
#endif
#if defined(USE_AUDIOUNIT_RUST)
"audiounit-rust",
#endif
#if defined(USE_WASAPI)
"wasapi",
#endif
@@ -239,23 +329,30 @@ cubeb_get_backend_names()
#if defined(USE_SNDIO)
"sndio",
#endif
#if defined(USE_SUN)
"sun",
#endif
#if defined(USE_OPENSL)
"opensl",
#endif
#if defined(USE_OSS)
"oss",
#endif
NULL,
#if defined(USE_AAUDIO)
"aaudio",
#endif
#if defined(USE_AUDIOTRACK)
"audiotrack",
#endif
#if defined(USE_KAI)
"kai",
#endif
};
return backend_names;
}
char const *
cubeb_get_backend_id(cubeb * context)
{
if (!context) {
return NULL;
}
return context->ops->get_backend_id(context);
return (cubeb_backend_names){
.names = backend_names,
.count = NELEMS(backend_names),
};
}
int

View File

@@ -213,12 +213,19 @@ struct cubeb_stream {
cubeb_device_changed_callback device_changed_callback = nullptr;
owned_critical_section device_changed_callback_lock;
/* Stream creation parameters */
cubeb_stream_params input_stream_params = {CUBEB_SAMPLE_FLOAT32NE, 0, 0,
cubeb_stream_params input_stream_params = {CUBEB_SAMPLE_FLOAT32NE,
0,
0,
CUBEB_LAYOUT_UNDEFINED,
CUBEB_STREAM_PREF_NONE};
cubeb_stream_params output_stream_params = {CUBEB_SAMPLE_FLOAT32NE, 0, 0,
CUBEB_LAYOUT_UNDEFINED,
CUBEB_STREAM_PREF_NONE};
CUBEB_STREAM_PREF_NONE,
CUBEB_INPUT_PROCESSING_PARAM_NONE};
cubeb_stream_params output_stream_params = {
CUBEB_SAMPLE_FLOAT32NE,
0,
0,
CUBEB_LAYOUT_UNDEFINED,
CUBEB_STREAM_PREF_NONE,
CUBEB_INPUT_PROCESSING_PARAM_NONE};
device_info input_device;
device_info output_device;
/* Format descriptions */

View File

@@ -16,8 +16,8 @@
#include <time.h>
#endif
static std::atomic<cubeb_log_level> g_cubeb_log_level;
static std::atomic<cubeb_log_callback> g_cubeb_log_callback;
std::atomic<cubeb_log_level> g_cubeb_log_level;
std::atomic<cubeb_log_callback> g_cubeb_log_callback;
/** The maximum size of a log message, after having been formatted. */
const size_t CUBEB_LOG_MESSAGE_MAX_SIZE = 256;
@@ -32,6 +32,133 @@ cubeb_noop_log_callback(char const * /* fmt */, ...)
{
}
/**
* This wraps an inline buffer, that represents a log message, that must be
* null-terminated.
* This class should not use system calls or other potentially blocking code.
*/
class cubeb_log_message {
public:
cubeb_log_message() { *storage = '\0'; }
cubeb_log_message(char const str[CUBEB_LOG_MESSAGE_MAX_SIZE])
{
size_t length = strlen(str);
/* paranoia against malformed message */
assert(length < CUBEB_LOG_MESSAGE_MAX_SIZE);
if (length > CUBEB_LOG_MESSAGE_MAX_SIZE - 1) {
return;
}
PodCopy(storage, str, length);
storage[length] = '\0';
}
char const * get() { return storage; }
private:
char storage[CUBEB_LOG_MESSAGE_MAX_SIZE]{};
};
/** Lock-free asynchronous logger, made so that logging from a
* real-time audio callback does not block the audio thread. */
class cubeb_async_logger {
public:
/* This is thread-safe since C++11 */
static cubeb_async_logger & get()
{
static cubeb_async_logger instance;
return instance;
}
void push(char const str[CUBEB_LOG_MESSAGE_MAX_SIZE])
{
cubeb_log_message msg(str);
auto * owned_queue = msg_queue.load();
// Check if the queue is being deallocated. If not, grab ownership. If yes,
// return, the message won't be logged.
if (!owned_queue ||
!msg_queue.compare_exchange_strong(owned_queue, nullptr)) {
return;
}
owned_queue->enqueue(msg);
// Return ownership.
msg_queue.store(owned_queue);
}
void run()
{
assert(logging_thread.get_id() == std::thread::id());
logging_thread = std::thread([this]() {
CUBEB_REGISTER_THREAD("cubeb_log");
while (!shutdown_thread) {
cubeb_log_message msg;
while (msg_queue_consumer.load()->dequeue(&msg, 1)) {
cubeb_log_internal_no_format(msg.get());
}
std::this_thread::sleep_for(
std::chrono::milliseconds(CUBEB_LOG_BATCH_PRINT_INTERVAL_MS));
}
CUBEB_UNREGISTER_THREAD();
});
}
// Tell the underlying queue the producer thread has changed, so it does not
// assert in debug. This should be called with the thread stopped.
void reset_producer_thread()
{
if (msg_queue) {
msg_queue.load()->reset_thread_ids();
}
}
void start()
{
auto * queue =
new lock_free_queue<cubeb_log_message>(CUBEB_LOG_MESSAGE_QUEUE_DEPTH);
msg_queue.store(queue);
msg_queue_consumer.store(queue);
shutdown_thread = false;
run();
}
void stop()
{
assert(((g_cubeb_log_callback == cubeb_noop_log_callback) ||
!g_cubeb_log_callback) &&
"Only call stop after logging has been disabled.");
shutdown_thread = true;
if (logging_thread.get_id() != std::thread::id()) {
logging_thread.join();
logging_thread = std::thread();
auto * owned_queue = msg_queue.load();
// Check if the queue is being used. If not, grab ownership. If yes,
// try again shortly. At this point, the logging thread has been joined,
// so nothing is going to dequeue.
// If there is a valid pointer here, then the real-time audio thread that
// logs won't attempt to write into the queue, and instead drop the
// message.
while (!msg_queue.compare_exchange_weak(owned_queue, nullptr)) {
}
delete owned_queue;
msg_queue_consumer.store(nullptr);
}
}
private:
cubeb_async_logger() {}
~cubeb_async_logger()
{
assert(logging_thread.get_id() == std::thread::id() &&
(g_cubeb_log_callback == cubeb_noop_log_callback ||
!g_cubeb_log_callback));
if (msg_queue.load()) {
delete msg_queue.load();
}
}
/** This is quite a big data structure, but is only instantiated if the
* asynchronous logger is used. The two pointers point to the same object, but
* the first one can be temporarily null when a message is being enqueued. */
std::atomic<lock_free_queue<cubeb_log_message> *> msg_queue = {nullptr};
std::atomic<lock_free_queue<cubeb_log_message> *> msg_queue_consumer = {
nullptr};
std::atomic<bool> shutdown_thread = {false};
std::thread logging_thread;
};
void
cubeb_log_internal(char const * file, uint32_t line, char const * fmt, ...)
{
@@ -49,6 +176,29 @@ cubeb_log_internal_no_format(const char * msg)
g_cubeb_log_callback.load()(msg);
}
void
cubeb_async_log(char const * fmt, ...)
{
// This is going to copy a 256 bytes array around, which is fine.
// We don't want to allocate memory here, because this is made to
// be called from a real-time callback.
va_list args;
va_start(args, fmt);
char msg[CUBEB_LOG_MESSAGE_MAX_SIZE];
vsnprintf(msg, CUBEB_LOG_MESSAGE_MAX_SIZE, fmt, args);
cubeb_async_logger::get().push(msg);
va_end(args);
}
void
cubeb_async_log_reset_threads(void)
{
if (!g_cubeb_log_callback) {
return;
}
cubeb_async_logger::get().reset_producer_thread();
}
void
cubeb_log_set(cubeb_log_level log_level, cubeb_log_callback log_callback)
{
@@ -57,8 +207,15 @@ cubeb_log_set(cubeb_log_level log_level, cubeb_log_callback log_callback)
// nullptr, to prevent a TOCTOU race between checking the pointer
if (log_callback && log_level != CUBEB_LOG_DISABLED) {
g_cubeb_log_callback = log_callback;
if (log_level == CUBEB_LOG_VERBOSE) {
cubeb_async_logger::get().start();
}
} else if (!log_callback || CUBEB_LOG_DISABLED) {
g_cubeb_log_callback = cubeb_noop_log_callback;
// This returns once the thread has joined.
// This is safe even if CUBEB_LOG_VERBOSE was not set; the thread will
// simply not be joinable.
cubeb_async_logger::get().stop();
} else {
assert(false && "Incorrect parameters passed to cubeb_log_set");
}

View File

@@ -39,7 +39,12 @@ cubeb_log_get_callback(void);
void
cubeb_log_internal_no_format(const char * msg);
void
cubeb_log_internal(const char * filename, uint32_t line, const char * fmt, ...);
cubeb_log_internal(const char * filename, uint32_t line, const char * fmt, ...)
PRINTF_FORMAT(3, 4);
void
cubeb_async_log(const char * fmt, ...) PRINTF_FORMAT(1, 2);
void
cubeb_async_log_reset_threads(void);
#ifdef __cplusplus
}
@@ -55,9 +60,16 @@ cubeb_log_internal(const char * filename, uint32_t line, const char * fmt, ...);
} \
} while (0)
#define ALOG_INTERNAL(level, fmt, ...) \
do { \
if (cubeb_log_get_level() >= level && cubeb_log_get_callback()) { \
cubeb_async_log(fmt, ##__VA_ARGS__); \
} \
} while (0)
/* Asynchronous logging macros to log in real-time callbacks. */
/* Should not be used on android due to the use of global/static variables. */
#define ALOGV(msg, ...) LOG_INTERNAL(CUBEB_LOG_VERBOSE, msg, ##__VA_ARGS__)
#define ALOG(msg, ...) LOG_INTERNAL(CUBEB_LOG_NORMAL, msg, ##__VA_ARGS__)
#define ALOGV(msg, ...) ALOG_INTERNAL(CUBEB_LOG_VERBOSE, msg, ##__VA_ARGS__)
#define ALOG(msg, ...) ALOG_INTERNAL(CUBEB_LOG_NORMAL, msg, ##__VA_ARGS__)
#endif // CUBEB_LOG

View File

@@ -371,3 +371,9 @@ cubeb_resampler_latency(cubeb_resampler * resampler)
{
return resampler->latency();
}
cubeb_resampler_stats
cubeb_resampler_stats_get(cubeb_resampler * resampler)
{
return resampler->stats();
}

View File

@@ -84,6 +84,20 @@ cubeb_resampler_destroy(cubeb_resampler * resampler);
long
cubeb_resampler_latency(cubeb_resampler * resampler);
/**
* Test-only introspection API to ensure that there is no buffering
* buildup when resampling.
*/
typedef struct {
size_t input_input_buffer_size;
size_t input_output_buffer_size;
size_t output_input_buffer_size;
size_t output_output_buffer_size;
} cubeb_resampler_stats;
cubeb_resampler_stats
cubeb_resampler_stats_get(cubeb_resampler * resampler);
#if defined(__cplusplus)
}
#endif

View File

@@ -56,6 +56,7 @@ struct cubeb_resampler {
virtual long fill(void * input_buffer, long * input_frames_count,
void * output_buffer, long frames_needed) = 0;
virtual long latency() = 0;
virtual cubeb_resampler_stats stats() = 0;
virtual ~cubeb_resampler() {}
};
@@ -86,6 +87,16 @@ public:
virtual long latency() { return 0; }
virtual cubeb_resampler_stats stats()
{
cubeb_resampler_stats stats;
stats.input_input_buffer_size = internal_input_buffer.length();
stats.input_output_buffer_size = 0;
stats.output_input_buffer_size = 0;
stats.output_output_buffer_size = 0;
return stats;
}
void drop_audio_if_needed()
{
uint32_t to_keep = min_buffered_audio_frame(sample_rate);
@@ -122,6 +133,20 @@ public:
virtual long fill(void * input_buffer, long * input_frames_count,
void * output_buffer, long output_frames_needed);
virtual cubeb_resampler_stats stats()
{
cubeb_resampler_stats stats = {};
if (input_processor) {
stats.input_input_buffer_size = input_processor->input_buffer_size();
stats.input_output_buffer_size = input_processor->output_buffer_size();
}
if (output_processor) {
stats.output_input_buffer_size = output_processor->input_buffer_size();
stats.output_output_buffer_size = output_processor->output_buffer_size();
}
return stats;
}
virtual long latency()
{
if (input_processor && output_processor) {
@@ -280,29 +305,28 @@ public:
}
/** Returns the number of frames to pass in the input of the resampler to have
* exactly `output_frame_count` resampled frames. This can return a number
* slightly bigger than what is strictly necessary, but it guaranteed that the
* number of output frames will be exactly equal. */
* at least `output_frame_count` resampled frames. */
uint32_t input_needed_for_output(int32_t output_frame_count) const
{
assert(output_frame_count >= 0); // Check overflow
int32_t unresampled_frames_left =
samples_to_frames(resampling_in_buffer.length());
int32_t resampled_frames_left =
samples_to_frames(resampling_out_buffer.length());
float input_frames_needed =
(output_frame_count - unresampled_frames_left) * resampling_ratio -
resampled_frames_left;
if (input_frames_needed < 0) {
return 0;
}
return (uint32_t)ceilf(input_frames_needed);
float input_frames_needed_frac =
static_cast<float>(output_frame_count) * resampling_ratio;
// speex_resample()` can be irregular in its consumption of input samples.
// Provide one more frame than the number that would be required with
// regular consumption, to make the speex resampler behave more regularly,
// and so predictably.
auto input_frame_needed =
1 + static_cast<int32_t>(ceilf(input_frames_needed_frac));
input_frame_needed -= std::min(unresampled_frames_left, input_frame_needed);
return input_frame_needed;
}
/** Returns a pointer to the input buffer, that contains empty space for at
* least `frame_count` elements. This is useful so that consumer can directly
* write into the input buffer of the resampler. The pointer returned is
* adjusted so that leftover data are not overwritten.
* least `frame_count` elements. This is useful so that consumer can
* directly write into the input buffer of the resampler. The pointer
* returned is adjusted so that leftover data are not overwritten.
*/
T * input_buffer(size_t frame_count)
{
@@ -312,8 +336,8 @@ public:
return resampling_in_buffer.data() + leftover_samples;
}
/** This method works with `input_buffer`, and allows to inform the processor
how much frames have been written in the provided buffer. */
/** This method works with `input_buffer`, and allows to inform the
processor how much frames have been written in the provided buffer. */
void written(size_t written_frames)
{
resampling_in_buffer.set_length(leftover_samples +
@@ -331,6 +355,9 @@ public:
}
}
size_t input_buffer_size() const { return resampling_in_buffer.length(); }
size_t output_buffer_size() const { return resampling_out_buffer.length(); }
private:
/** Wrapper for the speex resampling functions to have a typed
* interface. */
@@ -359,6 +386,7 @@ private:
output_frame_count);
assert(rv == RESAMPLER_ERR_SUCCESS);
}
/** The state for the speex resampler used internaly. */
SpeexResamplerState * speex_resampler;
/** Source rate / target rate. */
@@ -371,8 +399,8 @@ private:
auto_array<T> resampling_out_buffer;
/** Additional latency inserted into the pipeline for synchronisation. */
uint32_t additional_latency;
/** When `input_buffer` is called, this allows tracking the number of samples
that were in the buffer. */
/** When `input_buffer` is called, this allows tracking the number of
samples that were in the buffer. */
uint32_t leftover_samples;
};
@@ -417,8 +445,8 @@ public:
return delay_output_buffer.data();
}
/** Get a pointer to the first writable location in the input buffer>
* @parameter frames_needed the number of frames the user needs to write into
* the buffer.
* @parameter frames_needed the number of frames the user needs to write
* into the buffer.
* @returns a pointer to a location in the input buffer where #frames_needed
* can be writen. */
T * input_buffer(uint32_t frames_needed)
@@ -428,8 +456,8 @@ public:
frames_to_samples(frames_needed));
return delay_input_buffer.data() + leftover_samples;
}
/** This method works with `input_buffer`, and allows to inform the processor
how much frames have been written in the provided buffer. */
/** This method works with `input_buffer`, and allows to inform the
processor how much frames have been written in the provided buffer. */
void written(size_t frames_written)
{
delay_input_buffer.set_length(leftover_samples +
@@ -450,8 +478,8 @@ public:
return to_pop;
}
/** Returns the number of frames one needs to input into the delay line to get
* #frames_needed frames back.
/** Returns the number of frames one needs to input into the delay line to
* get #frames_needed frames back.
* @parameter frames_needed the number of frames one want to write into the
* delay_line
* @returns the number of frames one will get. */
@@ -469,19 +497,23 @@ public:
void drop_audio_if_needed()
{
size_t available = samples_to_frames(delay_input_buffer.length());
uint32_t available = samples_to_frames(delay_input_buffer.length());
uint32_t to_keep = min_buffered_audio_frame(sample_rate);
if (available > to_keep) {
ALOGV("Dropping %u frames", available - to_keep);
delay_input_buffer.pop(nullptr, frames_to_samples(available - to_keep));
}
}
size_t input_buffer_size() const { return delay_input_buffer.length(); }
size_t output_buffer_size() const { return delay_output_buffer.length(); }
private:
/** The length, in frames, of this delay line */
uint32_t length;
/** When `input_buffer` is called, this allows tracking the number of samples
that where in the buffer. */
/** When `input_buffer` is called, this allows tracking the number of
samples that where in the buffer. */
uint32_t leftover_samples;
/** The input buffer, where the delay is applied. */
auto_array<T> delay_input_buffer;
@@ -511,8 +543,8 @@ cubeb_resampler_create_internal(cubeb_stream * stream,
"need at least one valid parameter pointer.");
/* All the streams we have have a sample rate that matches the target
sample rate, use a no-op resampler, that simply forwards the buffers to the
callback. */
sample rate, use a no-op resampler, that simply forwards the buffers to
the callback. */
if (((input_params && input_params->rate == target_rate) &&
(output_params && output_params->rate == target_rate)) ||
(input_params && !output_params && (input_params->rate == target_rate)) ||

View File

@@ -4,8 +4,12 @@
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*/
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0603
#endif // !_WIN32_WINNT
#ifndef NOMINMAX
#define NOMINMAX
#endif // !NOMINMAX
#include <algorithm>
#include <atomic>
@@ -37,31 +41,6 @@
#include "cubeb_tracing.h"
#include "cubeb_utils.h"
// Some people have reported glitches with IAudioClient3 capture streams:
// http://blog.nirbheek.in/2018/03/low-latency-audio-on-windows-with.html
// https://bugzilla.mozilla.org/show_bug.cgi?id=1590902
#define ALLOW_AUDIO_CLIENT_3_FOR_INPUT 0
// IAudioClient3::GetSharedModeEnginePeriod() seem to return min latencies
// bigger than IAudioClient::GetDevicePeriod(), which is confusing (10ms vs
// 3ms), though the default latency is usually the same and we should use the
// IAudioClient3 function anyway, as it's more correct
#define USE_AUDIO_CLIENT_3_MIN_PERIOD 1
// If this is true, we allow IAudioClient3 the creation of sessions with a
// latency above the default one (usually 10ms).
// Whether we should default this to true or false depend on many things:
// -Does creating a shared IAudioClient3 session (not locked to a format)
// actually forces all the IAudioClient(1) sessions to have the same latency?
// I could find no proof of that.
// -Does creating a shared IAudioClient3 session with a latency >= the default
// one actually improve the latency (as in how late the audio is) at all?
// -Maybe we could expose this as cubeb stream pref
// (e.g. take priority over other apps)?
#define ALLOW_AUDIO_CLIENT_3_LATENCY_OVER_DEFAULT 1
// If this is true and the user specified a target latency >= the IAudioClient3
// max one, then we reject it and fall back to IAudioClient(1). There wouldn't
// be much point in having a low latency if that's not what the user wants.
#define REJECT_AUDIO_CLIENT_3_LATENCY_OVER_MAX 0
// Windows 10 exposes the IAudioClient3 interface to create low-latency streams.
// Copy the interface definition from audioclient.h here to make the code
// simpler and so that we can still access IAudioClient3 via COM if cubeb was
@@ -229,11 +208,6 @@ struct auto_stream_ref {
cubeb_stream * stm;
};
using set_mm_thread_characteristics_function =
decltype(&AvSetMmThreadCharacteristicsW);
using revert_mm_thread_characteristics_function =
decltype(&AvRevertMmThreadCharacteristics);
extern cubeb_ops const wasapi_ops;
static com_heap_ptr<wchar_t>
@@ -304,8 +278,8 @@ wasapi_enumerate_devices_internal(cubeb * context, cubeb_device_type type,
static int
wasapi_device_collection_destroy(cubeb * ctx,
cubeb_device_collection * collection);
static char const *
wstr_to_utf8(wchar_t const * str);
static std::unique_ptr<char const[]>
wstr_to_utf8(LPCWSTR str);
static std::unique_ptr<wchar_t const[]>
utf8_to_wstr(char const * str);
@@ -314,6 +288,15 @@ utf8_to_wstr(char const * str);
class wasapi_collection_notification_client;
class monitor_device_notifications;
typedef enum {
/* Clear options */
CUBEB_AUDIO_CLIENT2_NONE,
/* Use AUDCLNT_STREAMOPTIONS_RAW */
CUBEB_AUDIO_CLIENT2_RAW,
/* Use CUBEB_STREAM_PREF_COMMUNICATIONS */
CUBEB_AUDIO_CLIENT2_VOICE
} AudioClient2Option;
struct cubeb {
cubeb_ops const * ops = &wasapi_ops;
owned_critical_section lock;
@@ -331,13 +314,6 @@ struct cubeb {
nullptr;
void * output_collection_changed_user_ptr = nullptr;
UINT64 performance_counter_frequency;
/* Library dynamically opened to increase the render thread priority, and
the two function pointers we need. */
HMODULE mmcss_module = nullptr;
set_mm_thread_characteristics_function set_mm_thread_characteristics =
nullptr;
revert_mm_thread_characteristics_function revert_mm_thread_characteristics =
nullptr;
};
class wasapi_endpoint_notification_client;
@@ -360,20 +336,33 @@ struct cubeb_stream {
/* Mixer pameters. We need to convert the input stream to this
samplerate/channel layout, as WASAPI does not resample nor upmix
itself. */
cubeb_stream_params input_mix_params = {CUBEB_SAMPLE_FLOAT32NE, 0, 0,
cubeb_stream_params input_mix_params = {CUBEB_SAMPLE_FLOAT32NE,
0,
0,
CUBEB_LAYOUT_UNDEFINED,
CUBEB_STREAM_PREF_NONE};
cubeb_stream_params output_mix_params = {CUBEB_SAMPLE_FLOAT32NE, 0, 0,
CUBEB_STREAM_PREF_NONE,
CUBEB_INPUT_PROCESSING_PARAM_NONE};
cubeb_stream_params output_mix_params = {CUBEB_SAMPLE_FLOAT32NE,
0,
0,
CUBEB_LAYOUT_UNDEFINED,
CUBEB_STREAM_PREF_NONE};
CUBEB_STREAM_PREF_NONE,
CUBEB_INPUT_PROCESSING_PARAM_NONE};
/* Stream parameters. This is what the client requested,
* and what will be presented in the callback. */
cubeb_stream_params input_stream_params = {CUBEB_SAMPLE_FLOAT32NE, 0, 0,
cubeb_stream_params input_stream_params = {CUBEB_SAMPLE_FLOAT32NE,
0,
0,
CUBEB_LAYOUT_UNDEFINED,
CUBEB_STREAM_PREF_NONE};
cubeb_stream_params output_stream_params = {CUBEB_SAMPLE_FLOAT32NE, 0, 0,
CUBEB_LAYOUT_UNDEFINED,
CUBEB_STREAM_PREF_NONE};
CUBEB_STREAM_PREF_NONE,
CUBEB_INPUT_PROCESSING_PARAM_NONE};
cubeb_stream_params output_stream_params = {
CUBEB_SAMPLE_FLOAT32NE,
0,
0,
CUBEB_LAYOUT_UNDEFINED,
CUBEB_STREAM_PREF_NONE,
CUBEB_INPUT_PROCESSING_PARAM_NONE};
/* A MMDevice role for this stream: either communication or console here. */
ERole role;
/* True if this stream will transport voice-data. */
@@ -662,6 +651,10 @@ public:
LPCWSTR device_id)
{
LOG("collection: Audio device default changed, id = %S.", device_id);
/* Default device changes count as device collection changes */
monitor_notifications.notify(flow);
return S_OK;
}
@@ -772,7 +765,7 @@ public:
LPCWSTR device_id)
{
LOG("endpoint: Audio device default changed flow=%d role=%d "
"new_device_id=%ws.",
"new_device_id=%S.",
flow, role, device_id);
/* we only support a single stream type for now. */
@@ -783,11 +776,13 @@ public:
DWORD last_change_ms = timeGetTime() - last_device_change;
bool same_device = default_device_id && device_id &&
wcscmp(default_device_id.get(), device_id) == 0;
LOG("endpoint: Audio device default changed last_change=%u same_device=%d",
LOG("endpoint: Audio device default changed last_change=%lu same_device=%d",
last_change_ms, same_device);
if (last_change_ms > DEVICE_CHANGE_DEBOUNCE_MS || !same_device) {
if (device_id) {
default_device_id.reset(_wcsdup(device_id));
wchar_t * new_device_id = new wchar_t[wcslen(device_id) + 1];
wcscpy(new_device_id, device_id);
default_device_id.reset(new_device_id);
} else {
default_device_id.reset();
}
@@ -863,16 +858,12 @@ intern_device_id(cubeb * ctx, wchar_t const * id)
auto_lock lock(ctx->lock);
char const * tmp = wstr_to_utf8(id);
std::unique_ptr<char const[]> tmp = wstr_to_utf8(id);
if (!tmp) {
return nullptr;
}
char const * interned = cubeb_strings_intern(ctx->device_ids, tmp);
free((void *)tmp);
return interned;
return cubeb_strings_intern(ctx->device_ids, tmp.get());
}
bool
@@ -977,7 +968,7 @@ refill(cubeb_stream * stm, void * input_buffer, long input_frames_count,
cubeb_resampler_fill(stm->resampler.get(), input_buffer,
&input_frames_count, dest, output_frames_needed);
if (out_frames < 0) {
ALOGV("Callback refill error: %d", out_frames);
ALOGV("Callback refill error: %ld", out_frames);
wasapi_state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
return out_frames;
}
@@ -1263,8 +1254,8 @@ refill_callback_duplex(cubeb_stream * stm)
XASSERT(has_input(stm) && has_output(stm));
if (stm->input_stream_params.prefs & CUBEB_STREAM_PREF_LOOPBACK) {
HRESULT rv = get_input_buffer(stm);
if (FAILED(rv)) {
rv = get_input_buffer(stm);
if (!rv) {
return rv;
}
}
@@ -1274,7 +1265,6 @@ refill_callback_duplex(cubeb_stream * stm)
rv = get_output_buffer(stm, output_buffer, output_frames);
if (!rv) {
hr = stm->render_client->ReleaseBuffer(output_frames, 0);
return rv;
}
@@ -1291,9 +1281,11 @@ refill_callback_duplex(cubeb_stream * stm)
stm->total_output_frames += output_frames;
ALOGV("in: %zu, out: %zu, missing: %ld, ratio: %f", stm->total_input_frames,
stm->total_output_frames,
static_cast<long>(stm->total_output_frames) - stm->total_input_frames,
ALOGV("in: %llu, out: %llu, missing: %ld, ratio: %f",
(unsigned long long)stm->total_input_frames,
(unsigned long long)stm->total_output_frames,
static_cast<long long>(stm->total_output_frames) -
static_cast<long long>(stm->total_input_frames),
static_cast<float>(stm->total_output_frames) / stm->total_input_frames);
long got;
@@ -1438,8 +1430,7 @@ static unsigned int __stdcall wasapi_stream_render_loop(LPVOID stream)
/* We could consider using "Pro Audio" here for WebAudio and
maybe WebRTC. */
mmcss_handle =
stm->context->set_mm_thread_characteristics(L"Audio", &mmcss_task_index);
mmcss_handle = AvSetMmThreadCharacteristicsA("Audio", &mmcss_task_index);
if (!mmcss_handle) {
/* This is not fatal, but we might glitch under heavy load. */
LOG("Unable to use mmcss to bump the render thread priority: %lx",
@@ -1519,8 +1510,8 @@ static unsigned int __stdcall wasapi_stream_render_loop(LPVOID stream)
is_playing = stm->refill_callback(stm);
break;
case WAIT_OBJECT_0 + 3: { /* input available */
HRESULT rv = get_input_buffer(stm);
if (FAILED(rv)) {
bool rv = get_input_buffer(stm);
if (!rv) {
is_playing = false;
continue;
}
@@ -1532,8 +1523,11 @@ static unsigned int __stdcall wasapi_stream_render_loop(LPVOID stream)
break;
}
default:
LOG("case %lu not handled in render loop.", waitResult);
XASSERT(false);
LOG("render_loop: waitResult=%lu (lastError=%lu) unhandled, exiting",
waitResult, GetLastError());
is_playing = false;
hr = E_FAIL;
continue;
}
}
@@ -1547,7 +1541,7 @@ static unsigned int __stdcall wasapi_stream_render_loop(LPVOID stream)
}
if (mmcss_handle) {
stm->context->revert_mm_thread_characteristics(mmcss_handle);
AvRevertMmThreadCharacteristics(mmcss_handle);
}
if (FAILED(hr)) {
@@ -1560,18 +1554,6 @@ static unsigned int __stdcall wasapi_stream_render_loop(LPVOID stream)
void
wasapi_destroy(cubeb * context);
HANDLE WINAPI
set_mm_thread_characteristics_noop(LPCWSTR, LPDWORD mmcss_task_index)
{
return (HANDLE)1;
}
BOOL WINAPI
revert_mm_thread_characteristics_noop(HANDLE mmcss_handle)
{
return true;
}
HRESULT
register_notification_client(cubeb_stream * stm)
{
@@ -1807,31 +1789,6 @@ wasapi_init(cubeb ** context, char const * context_name)
ctx->performance_counter_frequency = 0;
}
ctx->mmcss_module = LoadLibraryW(L"Avrt.dll");
bool success = false;
if (ctx->mmcss_module) {
ctx->set_mm_thread_characteristics =
reinterpret_cast<set_mm_thread_characteristics_function>(
GetProcAddress(ctx->mmcss_module, "AvSetMmThreadCharacteristicsW"));
ctx->revert_mm_thread_characteristics =
reinterpret_cast<revert_mm_thread_characteristics_function>(
GetProcAddress(ctx->mmcss_module,
"AvRevertMmThreadCharacteristics"));
success = ctx->set_mm_thread_characteristics &&
ctx->revert_mm_thread_characteristics;
}
if (!success) {
// This is not a fatal error, but we might end up glitching when
// the system is under high load.
LOG("Could not load avrt.dll or fetch AvSetMmThreadCharacteristicsW "
"AvRevertMmThreadCharacteristics: %lx",
GetLastError());
ctx->set_mm_thread_characteristics = &set_mm_thread_characteristics_noop;
ctx->revert_mm_thread_characteristics =
&revert_mm_thread_characteristics_noop;
}
*context = ctx;
return CUBEB_OK;
@@ -1839,7 +1796,6 @@ wasapi_init(cubeb ** context, char const * context_name)
}
namespace {
enum ShutdownPhase { OnStop, OnDestroy };
bool
stop_and_join_render_thread(cubeb_stream * stm)
@@ -1855,16 +1811,7 @@ stop_and_join_render_thread(cubeb_stream * stm)
return false;
}
/* Wait five seconds for the rendering thread to return. It's supposed to
* check its event loop very often, five seconds is rather conservative.
* Note: 5*1s loop to work around timer sleep issues on pre-Windows 8. */
DWORD r;
for (int i = 0; i < 5; ++i) {
r = WaitForSingleObject(stm->thread, 1000);
if (r == WAIT_OBJECT_0) {
break;
}
}
DWORD r = WaitForSingleObject(stm->thread, INFINITE);
if (r != WAIT_OBJECT_0) {
LOG("stop_and_join_render_thread: WaitForSingleObject on thread failed: "
"%lx, %lx",
@@ -1888,10 +1835,6 @@ wasapi_destroy(cubeb * context)
}
}
if (context->mmcss_module) {
FreeLibrary(context->mmcss_module);
}
delete context;
}
@@ -1949,44 +1892,6 @@ wasapi_get_min_latency(cubeb * ctx, cubeb_stream_params params,
return CUBEB_ERROR;
}
#if USE_AUDIO_CLIENT_3_MIN_PERIOD
// This is unreliable as we can't know the actual mixer format cubeb will
// ask for later on (nor we can branch on ALLOW_AUDIO_CLIENT_3_FOR_INPUT),
// and the min latency can change based on that.
com_ptr<IAudioClient3> client3;
hr = device->Activate(__uuidof(IAudioClient3), CLSCTX_INPROC_SERVER, NULL,
client3.receive_vpp());
if (SUCCEEDED(hr)) {
WAVEFORMATEX * mix_format = nullptr;
hr = client3->GetMixFormat(&mix_format);
if (SUCCEEDED(hr)) {
uint32_t default_period = 0, fundamental_period = 0, min_period = 0,
max_period = 0;
hr = client3->GetSharedModeEnginePeriod(mix_format, &default_period,
&fundamental_period, &min_period,
&max_period);
auto sample_rate = mix_format->nSamplesPerSec;
CoTaskMemFree(mix_format);
if (SUCCEEDED(hr)) {
// Print values in the same format as IAudioDevice::GetDevicePeriod()
REFERENCE_TIME min_period_rt(frames_to_hns(sample_rate, min_period));
REFERENCE_TIME default_period_rt(
frames_to_hns(sample_rate, default_period));
LOG("default device period: %I64d, minimum device period: %I64d",
default_period_rt, min_period_rt);
*latency_frames = hns_to_frames(params.rate, min_period_rt);
LOG("Minimum latency in frames: %u", *latency_frames);
return CUBEB_OK;
}
}
}
#endif
com_ptr<IAudioClient> client;
hr = device->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL,
client.receive_vpp());
@@ -2006,8 +1911,18 @@ wasapi_get_min_latency(cubeb * ctx, cubeb_stream_params params,
LOG("default device period: %I64d, minimum device period: %I64d",
default_period, minimum_period);
// The minimum_period is only relevant in exclusive streams.
/* If we're on Windows 10, we can use IAudioClient3 to get minimal latency.
Otherwise, according to the docs, the best latency we can achieve is by
synchronizing the stream and the engine.
http://msdn.microsoft.com/en-us/library/windows/desktop/dd370871%28v=vs.85%29.aspx
*/
// #ifdef _WIN32_WINNT_WIN10
#if 0
*latency_frames = hns_to_frames(params.rate, minimum_period);
#else
*latency_frames = hns_to_frames(params.rate, default_period);
#endif
LOG("Minimum latency in frames: %u", *latency_frames);
@@ -2044,6 +1959,21 @@ wasapi_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
return CUBEB_OK;
}
int
wasapi_get_supported_input_processing_params(
cubeb * ctx, cubeb_input_processing_params * params)
{
// This is not entirely accurate -- windows doesn't document precisely what
// AudioCategory_Communications does -- but assume that we can set all or none
// of them.
*params = static_cast<cubeb_input_processing_params>(
CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION |
CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION |
CUBEB_INPUT_PROCESSING_PARAM_AUTOMATIC_GAIN_CONTROL |
CUBEB_INPUT_PROCESSING_PARAM_VOICE_ISOLATION);
return CUBEB_OK;
}
static void
waveformatex_update_derived_properties(WAVEFORMATEX * format)
{
@@ -2097,10 +2027,7 @@ handle_channel_layout(cubeb_stream * stm, EDataFlow direction,
if (hr == S_FALSE) {
/* Channel layout not supported, but WASAPI gives us a suggestion. Use it,
and handle the eventual upmix/downmix ourselves. Ignore the subformat of
the suggestion, since it seems to always be IEEE_FLOAT.
This fallback doesn't update the bit depth, so if a device
only supported bit depths cubeb doesn't support, so IAudioClient3
streams might fail */
the suggestion, since it seems to always be IEEE_FLOAT. */
LOG("Using WASAPI suggested format: channels: %d", closest->nChannels);
XASSERT(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE);
WAVEFORMATEXTENSIBLE * closest_pcm =
@@ -2122,7 +2049,8 @@ handle_channel_layout(cubeb_stream * stm, EDataFlow direction,
}
static int
initialize_iaudioclient2(com_ptr<IAudioClient> & audio_client)
initialize_iaudioclient2(com_ptr<IAudioClient> & audio_client,
AudioClient2Option option)
{
com_ptr<IAudioClient2> audio_client2;
audio_client->QueryInterface<IAudioClient2>(audio_client2.receive());
@@ -2131,10 +2059,14 @@ initialize_iaudioclient2(com_ptr<IAudioClient> & audio_client)
"AUDCLNT_STREAMOPTIONS_RAW.");
return CUBEB_OK;
}
AudioClientProperties properties = {0};
AudioClientProperties properties = {};
properties.cbSize = sizeof(AudioClientProperties);
#ifndef __MINGW32__
properties.Options |= AUDCLNT_STREAMOPTIONS_RAW;
if (option == CUBEB_AUDIO_CLIENT2_RAW) {
properties.Options |= AUDCLNT_STREAMOPTIONS_RAW;
} else if (option == CUBEB_AUDIO_CLIENT2_VOICE) {
properties.eCategory = AudioCategory_Communications;
}
#endif
HRESULT hr = audio_client2->SetClientProperties(&properties);
if (FAILED(hr)) {
@@ -2144,12 +2076,12 @@ initialize_iaudioclient2(com_ptr<IAudioClient> & audio_client)
return CUBEB_OK;
}
#if 0
bool
initialize_iaudioclient3(com_ptr<IAudioClient> & audio_client,
cubeb_stream * stm,
const com_heap_ptr<WAVEFORMATEX> & mix_format,
DWORD flags, EDataFlow direction,
REFERENCE_TIME latency_hns)
DWORD flags, EDataFlow direction)
{
com_ptr<IAudioClient3> audio_client3;
audio_client->QueryInterface<IAudioClient3>(audio_client3.receive());
@@ -2165,22 +2097,24 @@ initialize_iaudioclient3(com_ptr<IAudioClient> & audio_client,
return false;
}
// Some people have reported glitches with capture streams:
// http://blog.nirbheek.in/2018/03/low-latency-audio-on-windows-with.html
if (direction == eCapture) {
LOG("Audio stream is capture, not using IAudioClient3");
return false;
}
// Possibly initialize a shared-mode stream using IAudioClient3. Initializing
// a stream this way lets you request lower latencies, but also locks the
// global WASAPI engine at that latency.
// - If we request a shared-mode stream, streams created with IAudioClient
// might have their latency adjusted to match. When the shared-mode stream
// is closed, they'll go back to normal.
// - If there's already a shared-mode stream running, if it created with the
// AUDCLNT_STREAMOPTIONS_MATCH_FORMAT option, the audio engine would be
// locked to that format, so we have to match it (a custom one would fail).
// - We don't lock the WASAPI engine to a format, as it's antisocial towards
// other apps, especially if we locked to a latency >= than its default.
// - If the user requested latency is >= the default one, we might still
// accept it (without locking the format) depending on
// ALLOW_AUDIO_CLIENT_3_LATENCY_OVER_DEFAULT, as we might want to prioritize
// to lower our latency over other apps
// (there might still be latency advantages compared to IAudioDevice(1)).
// will
// have their latency adjusted to match. When the shared-mode stream is
// closed, they'll go back to normal.
// - If there's already a shared-mode stream running, then we cannot request
// the engine change to a different latency - we have to match it.
// - It's antisocial to lock the WASAPI engine at its default latency. If we
// would do this, then stop and use IAudioClient instead.
HRESULT hr;
uint32_t default_period = 0, fundamental_period = 0, min_period = 0,
@@ -2192,59 +2126,28 @@ initialize_iaudioclient3(com_ptr<IAudioClient> & audio_client,
LOG("Could not get shared mode engine period: error: %lx", hr);
return false;
}
uint32_t requested_latency =
hns_to_frames(mix_format->nSamplesPerSec, latency_hns);
#if !ALLOW_AUDIO_CLIENT_3_LATENCY_OVER_DEFAULT
uint32_t requested_latency = stm->latency;
if (requested_latency >= default_period) {
LOG("Requested latency %i equal or greater than default latency %i,"
" not using IAudioClient3",
LOG("Requested latency %i greater than default latency %i, not using "
"IAudioClient3",
requested_latency, default_period);
return false;
}
#elif REJECT_AUDIO_CLIENT_3_LATENCY_OVER_MAX
if (requested_latency > max_period) {
// Fallback to IAudioClient(1) as it's more accepting of large latencies
LOG("Requested latency %i greater than max latency %i,"
" not using IAudioClient3",
requested_latency, max_period);
return false;
}
#endif
LOG("Got shared mode engine period: default=%i fundamental=%i min=%i max=%i",
default_period, fundamental_period, min_period, max_period);
// Snap requested latency to a valid value
uint32_t old_requested_latency = requested_latency;
// The period is required to be a multiple of the fundamental period
// (and >= min and <= max, which should still be true)
requested_latency -= requested_latency % fundamental_period;
if (requested_latency < min_period) {
requested_latency = min_period;
}
// Likely unnecessary, but won't hurt
if (requested_latency > max_period) {
requested_latency = max_period;
}
requested_latency -= (requested_latency - min_period) % fundamental_period;
if (requested_latency != old_requested_latency) {
LOG("Requested latency %i was adjusted to %i", old_requested_latency,
requested_latency);
}
DWORD new_flags = flags;
// Always add these flags to IAudioClient3, they might help
// if the stream doesn't have the same format as the audio engine.
new_flags |= AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM;
new_flags |= AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY;
hr = audio_client3->InitializeSharedAudioStream(new_flags, requested_latency,
hr = audio_client3->InitializeSharedAudioStream(flags, requested_latency,
mix_format.get(), NULL);
// This error should be returned first even if
// the period was locked (AUDCLNT_E_ENGINE_PERIODICITY_LOCKED)
if (hr == AUDCLNT_E_INVALID_STREAM_FLAG) {
LOG("Got AUDCLNT_E_INVALID_STREAM_FLAG, removing some flags");
hr = audio_client3->InitializeSharedAudioStream(flags, requested_latency,
mix_format.get(), NULL);
}
if (SUCCEEDED(hr)) {
return true;
} else if (hr == AUDCLNT_E_ENGINE_PERIODICITY_LOCKED) {
@@ -2256,37 +2159,22 @@ initialize_iaudioclient3(com_ptr<IAudioClient> & audio_client,
}
uint32_t current_period = 0;
WAVEFORMATEX * current_format_ptr = nullptr;
WAVEFORMATEX * current_format = nullptr;
// We have to pass a valid WAVEFORMATEX** and not nullptr, otherwise
// GetCurrentSharedModeEnginePeriod will return E_POINTER
hr = audio_client3->GetCurrentSharedModeEnginePeriod(&current_format_ptr,
hr = audio_client3->GetCurrentSharedModeEnginePeriod(&current_format,
&current_period);
CoTaskMemFree(current_format);
if (FAILED(hr)) {
LOG("Could not get current shared mode engine period: error: %lx", hr);
return false;
}
com_heap_ptr<WAVEFORMATEX> current_format(current_format_ptr);
if (current_format->nSamplesPerSec != mix_format->nSamplesPerSec) {
// Unless some other external app locked the shared mode engine period
// within our audio initialization, this is unlikely to happen, though we
// can't respect the user selected latency, so we fallback on IAudioClient
LOG("IAudioClient3::GetCurrentSharedModeEnginePeriod() returned a "
"different mixer format (nSamplesPerSec) from "
"IAudioClient::GetMixFormat(); not using IAudioClient3");
return false;
}
#if REJECT_AUDIO_CLIENT_3_LATENCY_OVER_MAX
// Reject IAudioClient3 if we can't respect the user target latency.
// We don't need to check against default_latency anymore,
// as the current_period is already the best one we could get.
if (old_requested_latency > current_period) {
LOG("Requested latency %i greater than currently locked shared mode "
"latency %i, not using IAudioClient3",
old_requested_latency, current_period);
if (current_period >= default_period) {
LOG("Current shared mode engine period %i too high, not using IAudioClient",
current_period);
return false;
}
#endif
hr = audio_client3->InitializeSharedAudioStream(flags, current_period,
mix_format.get(), NULL);
@@ -2299,6 +2187,7 @@ initialize_iaudioclient3(com_ptr<IAudioClient> & audio_client,
LOG("Could not initialize shared stream with IAudioClient3: error: %lx", hr);
return false;
}
#endif
#define DIRECTION_NAME (direction == eCapture ? "capture" : "render")
@@ -2322,12 +2211,6 @@ setup_wasapi_stream_one_side(cubeb_stream * stm,
return CUBEB_ERROR;
}
#if ALLOW_AUDIO_CLIENT_3_FOR_INPUT
constexpr bool allow_audio_client_3 = true;
#else
const bool allow_audio_client_3 = direction == eRender;
#endif
stm->stream_reset_lock.assert_current_thread_owns();
// If user doesn't specify a particular device, we can choose another one when
// the given devid is unavailable.
@@ -2364,14 +2247,17 @@ setup_wasapi_stream_one_side(cubeb_stream * stm,
/* Get a client. We will get all other interfaces we need from
* this pointer. */
if (allow_audio_client_3) {
hr = device->Activate(__uuidof(IAudioClient3), CLSCTX_INPROC_SERVER, NULL,
audio_client.receive_vpp());
}
if (!allow_audio_client_3 || hr == E_NOINTERFACE) {
hr = device->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL,
audio_client.receive_vpp());
#if 0 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1590902
hr = device->Activate(__uuidof(IAudioClient3),
CLSCTX_INPROC_SERVER,
NULL, audio_client.receive_vpp());
if (hr == E_NOINTERFACE) {
#endif
hr = device->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL,
audio_client.receive_vpp());
#if 0
}
#endif
if (FAILED(hr)) {
LOG("Could not activate the device to get an audio"
@@ -2494,21 +2380,41 @@ setup_wasapi_stream_one_side(cubeb_stream * stm,
}
if (stream_params->prefs & CUBEB_STREAM_PREF_RAW) {
if (initialize_iaudioclient2(audio_client) != CUBEB_OK) {
if (initialize_iaudioclient2(audio_client, CUBEB_AUDIO_CLIENT2_RAW) !=
CUBEB_OK) {
LOG("Can't initialize an IAudioClient2, error: %lx", GetLastError());
// This is not fatal.
}
} else if (direction == eCapture &&
(stream_params->prefs & CUBEB_STREAM_PREF_VOICE) &&
stream_params->input_params != CUBEB_INPUT_PROCESSING_PARAM_NONE) {
if (stream_params->input_params ==
(CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION |
CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION |
CUBEB_INPUT_PROCESSING_PARAM_AUTOMATIC_GAIN_CONTROL |
CUBEB_INPUT_PROCESSING_PARAM_VOICE_ISOLATION)) {
if (initialize_iaudioclient2(audio_client, CUBEB_AUDIO_CLIENT2_VOICE) !=
CUBEB_OK) {
LOG("Can't initialize an IAudioClient2, error: %lx", GetLastError());
// This is not fatal.
}
} else {
LOG("Invalid combination of input processing params %#x",
stream_params->input_params);
return CUBEB_ERROR;
}
}
if (allow_audio_client_3 &&
initialize_iaudioclient3(audio_client, stm, mix_format, flags, direction,
latency_hns)) {
#if 0 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1590902
if (initialize_iaudioclient3(audio_client, stm, mix_format, flags, direction)) {
LOG("Initialized with IAudioClient3");
} else {
hr = audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED, flags, latency_hns,
0, mix_format.get(), NULL);
#endif
hr = audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED, flags, latency_hns, 0,
mix_format.get(), NULL);
#if 0
}
#endif
if (FAILED(hr)) {
LOG("Unable to initialize audio client for %s: %lx.", DIRECTION_NAME, hr);
return CUBEB_ERROR;
@@ -2970,6 +2876,7 @@ wasapi_stream_init(cubeb * context, cubeb_stream ** stream,
}
}
cubeb_async_log_reset_threads();
stm->thread =
(HANDLE)_beginthreadex(NULL, 512 * 1024, wasapi_stream_render_loop, stm,
STACK_SIZE_PARAM_IS_A_RESERVATION, NULL);
@@ -3031,7 +2938,7 @@ wasapi_stream_add_ref(cubeb_stream * stm)
{
XASSERT(stm);
LONG result = InterlockedIncrement(&stm->ref_count);
LOGV("Stream ref count incremented = %i (%p)", result, stm);
LOGV("Stream ref count incremented = %ld (%p)", result, stm);
return result;
}
@@ -3041,7 +2948,7 @@ wasapi_stream_release(cubeb_stream * stm)
XASSERT(stm);
LONG result = InterlockedDecrement(&stm->ref_count);
LOGV("Stream ref count decremented = %i (%p)", result, stm);
LOGV("Stream ref count decremented = %ld (%p)", result, stm);
if (result == 0) {
LOG("Stream ref count hit zero, destroying (%p)", stm);
@@ -3303,7 +3210,7 @@ wasapi_stream_set_volume(cubeb_stream * stm, float volume)
return CUBEB_OK;
}
static char const *
static std::unique_ptr<char const[]>
wstr_to_utf8(LPCWSTR str)
{
int size = ::WideCharToMultiByte(CP_UTF8, 0, str, -1, nullptr, 0, NULL, NULL);
@@ -3311,8 +3218,8 @@ wstr_to_utf8(LPCWSTR str)
return nullptr;
}
char * ret = static_cast<char *>(malloc(size));
::WideCharToMultiByte(CP_UTF8, 0, str, -1, ret, size, NULL, NULL);
std::unique_ptr<char[]> ret(new char[size]);
::WideCharToMultiByte(CP_UTF8, 0, str, -1, ret.get(), size, NULL, NULL);
return ret;
}
@@ -3440,7 +3347,7 @@ wasapi_create_device(cubeb * ctx, cubeb_device_info & ret,
prop_variant namevar;
hr = propstore->GetValue(PKEY_Device_FriendlyName, &namevar);
if (SUCCEEDED(hr) && namevar.vt == VT_LPWSTR) {
ret.friendly_name = wstr_to_utf8(namevar.pwszVal);
ret.friendly_name = wstr_to_utf8(namevar.pwszVal).release();
}
if (!ret.friendly_name) {
// This is not fatal, but a valid string is expected in all cases.
@@ -3461,7 +3368,7 @@ wasapi_create_device(cubeb * ctx, cubeb_device_info & ret,
prop_variant instancevar;
hr = ps->GetValue(PKEY_Device_InstanceId, &instancevar);
if (SUCCEEDED(hr) && instancevar.vt == VT_LPWSTR) {
ret.group_id = wstr_to_utf8(instancevar.pwszVal);
ret.group_id = wstr_to_utf8(instancevar.pwszVal).release();
}
}
@@ -3477,7 +3384,8 @@ wasapi_create_device(cubeb * ctx, cubeb_device_info & ret,
ret.preferred =
(cubeb_device_pref)(ret.preferred | CUBEB_DEVICE_PREF_MULTIMEDIA |
CUBEB_DEVICE_PREF_NOTIFICATION);
} else if (defaults->is_default(flow, eCommunications, device_id.get())) {
}
if (defaults->is_default(flow, eCommunications, device_id.get())) {
ret.preferred =
(cubeb_device_pref)(ret.preferred | CUBEB_DEVICE_PREF_VOICE);
}
@@ -3504,7 +3412,6 @@ wasapi_create_device(cubeb * ctx, cubeb_device_info & ret,
CUBEB_DEVICE_FMT_S16NE);
ret.default_format = CUBEB_DEVICE_FMT_F32NE;
prop_variant fmtvar;
WAVEFORMATEX * wfx = NULL;
hr = propstore->GetValue(PKEY_AudioEngine_DeviceFormat, &fmtvar);
if (SUCCEEDED(hr) && fmtvar.vt == VT_BLOB) {
if (fmtvar.blob.cbSize == sizeof(PCMWAVEFORMAT)) {
@@ -3514,7 +3421,8 @@ wasapi_create_device(cubeb * ctx, cubeb_device_info & ret,
ret.max_rate = ret.min_rate = ret.default_rate = pcm->wf.nSamplesPerSec;
ret.max_channels = pcm->wf.nChannels;
} else if (fmtvar.blob.cbSize >= sizeof(WAVEFORMATEX)) {
wfx = reinterpret_cast<WAVEFORMATEX *>(fmtvar.blob.pBlobData);
WAVEFORMATEX * wfx =
reinterpret_cast<WAVEFORMATEX *>(fmtvar.blob.pBlobData);
if (fmtvar.blob.cbSize >= sizeof(WAVEFORMATEX) + wfx->cbSize ||
wfx->wFormatTag == WAVE_FORMAT_PCM) {
@@ -3524,30 +3432,9 @@ wasapi_create_device(cubeb * ctx, cubeb_device_info & ret,
}
}
#if USE_AUDIO_CLIENT_3_MIN_PERIOD
// Here we assume an IAudioClient3 stream will successfully
// be initialized later (it might fail)
#if ALLOW_AUDIO_CLIENT_3_FOR_INPUT
constexpr bool allow_audio_client_3 = true;
#else
const bool allow_audio_client_3 = flow == eRender;
#endif
com_ptr<IAudioClient3> client3;
uint32_t def, fun, min, max;
if (allow_audio_client_3 && wfx &&
SUCCEEDED(dev->Activate(__uuidof(IAudioClient3), CLSCTX_INPROC_SERVER,
NULL, client3.receive_vpp())) &&
SUCCEEDED(
client3->GetSharedModeEnginePeriod(wfx, &def, &fun, &min, &max))) {
ret.latency_lo = min;
// This latency might actually be used as "default" and not "max" later on,
// so we return the default (we never really want to use the max anyway)
ret.latency_hi = def;
} else
#endif
if (SUCCEEDED(dev->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER,
NULL, client.receive_vpp())) &&
SUCCEEDED(client->GetDevicePeriod(&def_period, &min_period))) {
if (SUCCEEDED(dev->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER,
NULL, client.receive_vpp())) &&
SUCCEEDED(client->GetDevicePeriod(&def_period, &min_period))) {
ret.latency_lo = hns_to_frames(ret.default_rate, min_period);
ret.latency_hi = hns_to_frames(ret.default_rate, def_period);
} else {
@@ -3638,7 +3525,7 @@ wasapi_enumerate_devices(cubeb * context, cubeb_device_type type,
{
return wasapi_enumerate_devices_internal(
context, type, out,
DEVICE_STATE_ACTIVE /*| DEVICE_STATE_DISABLED | DEVICE_STATE_UNPLUGGED*/);
DEVICE_STATE_ACTIVE | DEVICE_STATE_DISABLED | DEVICE_STATE_UNPLUGGED);
}
static int
@@ -3656,6 +3543,14 @@ wasapi_device_collection_destroy(cubeb * /*ctx*/,
return CUBEB_OK;
}
int
wasapi_set_input_processing_params(cubeb_stream * stream,
cubeb_input_processing_params params)
{
LOG("Cannot set voice processing params after init. Use cubeb_stream_init.");
return CUBEB_ERROR_NOT_SUPPORTED;
}
static int
wasapi_register_device_collection_changed(
cubeb * context, cubeb_device_type devtype,
@@ -3736,7 +3631,8 @@ cubeb_ops const wasapi_ops = {
/*.get_max_channel_count =*/wasapi_get_max_channel_count,
/*.get_min_latency =*/wasapi_get_min_latency,
/*.get_preferred_sample_rate =*/wasapi_get_preferred_sample_rate,
/*.get_supported_input_processing_params =*/NULL,
/*.get_supported_input_processing_params =*/
wasapi_get_supported_input_processing_params,
/*.enumerate_devices =*/wasapi_enumerate_devices,
/*.device_collection_destroy =*/wasapi_device_collection_destroy,
/*.destroy =*/wasapi_destroy,
@@ -3751,7 +3647,7 @@ cubeb_ops const wasapi_ops = {
/*.stream_set_name =*/NULL,
/*.stream_get_current_device =*/NULL,
/*.stream_set_input_mute =*/NULL,
/*.stream_set_input_processing_params =*/NULL,
/*.stream_set_input_processing_params =*/wasapi_set_input_processing_params,
/*.stream_device_destroy =*/NULL,
/*.stream_register_device_changed_callback =*/NULL,
/*.register_device_collection_changed =*/

View File

@@ -41,10 +41,10 @@
#ifdef FLOATING_POINT
#error You cannot compile as floating point and fixed point at the same time
#endif
#ifdef _USE_SSE
#ifdef USE_SSE
#error SSE is only for floating-point
#endif
#if ((defined (ARM4_ASM)||defined (ARM4_ASM)) && defined(BFIN_ASM)) || (defined (ARM4_ASM)&&defined(ARM5E_ASM))
#if defined(ARM4_ASM) + defined(ARM5E_ASM) + defined(BFIN_ASM) > 1
#error Make up your mind. What CPU do you have?
#endif
#ifdef VORBIS_PSYCHO
@@ -56,10 +56,10 @@
#ifndef FLOATING_POINT
#error You now need to define either FIXED_POINT or FLOATING_POINT
#endif
#if defined (ARM4_ASM) || defined(ARM5E_ASM) || defined(BFIN_ASM)
#if defined(ARM4_ASM) || defined(ARM5E_ASM) || defined(BFIN_ASM)
#error I suppose you can have a [ARM4/ARM5E/Blackfin] that has float instructions?
#endif
#ifdef FIXED_POINT_DEBUG
#ifdef FIXED_DEBUG
#error "Don't you think enabling fixed-point is a good thing to do if you want to debug that?"
#endif
@@ -117,9 +117,9 @@ typedef spx_word32_t spx_sig_t;
#ifdef ARM5E_ASM
#include "fixed_arm5e.h"
#elif defined (ARM4_ASM)
#elif defined(ARM4_ASM)
#include "fixed_arm4.h"
#elif defined (BFIN_ASM)
#elif defined(BFIN_ASM)
#include "fixed_bfin.h"
#endif
@@ -177,16 +177,13 @@ typedef float spx_word32_t;
#define ADD32(a,b) ((a)+(b))
#define SUB32(a,b) ((a)-(b))
#define MULT16_16_16(a,b) ((a)*(b))
#define MULT16_32_32(a,b) ((a)*(b))
#define MULT16_16(a,b) ((spx_word32_t)(a)*(spx_word32_t)(b))
#define MAC16_16(c,a,b) ((c)+(spx_word32_t)(a)*(spx_word32_t)(b))
#define MULT16_32_Q11(a,b) ((a)*(b))
#define MULT16_32_Q13(a,b) ((a)*(b))
#define MULT16_32_Q14(a,b) ((a)*(b))
#define MULT16_32_Q15(a,b) ((a)*(b))
#define MULT16_32_P15(a,b) ((a)*(b))
#define MAC16_32_Q11(c,a,b) ((c)+(a)*(b))
#define MAC16_32_Q15(c,a,b) ((c)+(a)*(b))
#define MAC16_16_Q11(c,a,b) ((c)+(a)*(b))
@@ -210,7 +207,7 @@ typedef float spx_word32_t;
#endif
#if defined (CONFIG_TI_C54X) || defined (CONFIG_TI_C55X)
#if defined(CONFIG_TI_C54X) || defined(CONFIG_TI_C55X)
/* 2 on TI C5x DSP */
#define BYTES_PER_CHAR 2

View File

@@ -69,22 +69,18 @@
/* result fits in 16 bits */
#define MULT16_16_16(a,b) ((((spx_word16_t)(a))*((spx_word16_t)(b))))
#define MULT16_16_16(a,b) (((spx_word16_t)(a))*((spx_word16_t)(b)))
/* result fits in 32 bits */
#define MULT16_32_32(a,b) (((spx_word16_t)(a))*((spx_word32_t)(b)))
/* (spx_word32_t)(spx_word16_t) gives TI compiler a hint that it's 16x16->32 multiply */
#define MULT16_16(a,b) (((spx_word32_t)(spx_word16_t)(a))*((spx_word32_t)(spx_word16_t)(b)))
#define MAC16_16(c,a,b) (ADD32((c),MULT16_16((a),(b))))
#define MULT16_32_Q12(a,b) ADD32(MULT16_16((a),SHR((b),12)), SHR(MULT16_16((a),((b)&0x00000fff)),12))
#define MULT16_32_Q13(a,b) ADD32(MULT16_16((a),SHR((b),13)), SHR(MULT16_16((a),((b)&0x00001fff)),13))
#define MULT16_32_Q14(a,b) ADD32(MULT16_16((a),SHR((b),14)), SHR(MULT16_16((a),((b)&0x00003fff)),14))
#define MULT16_32_Q11(a,b) ADD32(MULT16_16((a),SHR((b),11)), SHR(MULT16_16((a),((b)&0x000007ff)),11))
#define MAC16_32_Q11(c,a,b) ADD32(c,ADD32(MULT16_16((a),SHR((b),11)), SHR(MULT16_16((a),((b)&0x000007ff)),11)))
#define MULT16_32_P15(a,b) ADD32(MULT16_16((a),SHR((b),15)), PSHR(MULT16_16((a),((b)&0x00007fff)),15))
#define MULT16_32_Q15(a,b) ADD32(MULT16_16((a),SHR((b),15)), SHR(MULT16_16((a),((b)&0x00007fff)),15))
#define MAC16_32_Q15(c,a,b) ADD32(c,ADD32(MULT16_16((a),SHR((b),15)), SHR(MULT16_16((a),((b)&0x00007fff)),15)))
#define MULT16_32_P15(a,b) ADD32(MULT16_32_32(a,SHR((b),15)), PSHR(MULT16_16((a),((b)&0x00007fff)),15))
#define MULT16_32_Q15(a,b) ADD32(MULT16_32_32(a,SHR((b),15)), SHR(MULT16_16((a),((b)&0x00007fff)),15))
#define MAC16_32_Q15(c,a,b) ADD32(c,MULT16_32_Q15(a,b))
#define MAC16_16_Q11(c,a,b) (ADD32((c),SHR(MULT16_16((a),(b)),11)))

View File

@@ -46,7 +46,7 @@
Smith, Julius O. Digital Audio Resampling Home Page
Center for Computer Research in Music and Acoustics (CCRMA),
Stanford University, 2007.
Web published at http://ccrma.stanford.edu/~jos/resample/.
Web published at https://ccrma.stanford.edu/~jos/resample/.
There is one main difference, though. This resampler uses cubic
interpolation instead of linear interpolation in the above paper. This
@@ -63,9 +63,12 @@
#ifdef OUTSIDE_SPEEX
#include <stdlib.h>
static void *speex_alloc (int size) {return calloc(size,1);}
static void *speex_realloc (void *ptr, int size) {return realloc(ptr, size);}
static void speex_free (void *ptr) {free(ptr);}
static void *speex_alloc(int size) {return calloc(size,1);}
static void *speex_realloc(void *ptr, int size) {return realloc(ptr, size);}
static void speex_free(void *ptr) {free(ptr);}
#ifndef EXPORT
#define EXPORT
#endif
#include "speex_resampler.h"
#include "arch.h"
#else /* OUTSIDE_SPEEX */
@@ -75,7 +78,6 @@ static void speex_free (void *ptr) {free(ptr);}
#include "os_support.h"
#endif /* OUTSIDE_SPEEX */
#include "stack_alloc.h"
#include <math.h>
#include <limits.h>
@@ -91,18 +93,18 @@ static void speex_free (void *ptr) {free(ptr);}
#endif
#ifndef UINT32_MAX
#define UINT32_MAX 4294967296U
#define UINT32_MAX 4294967295U
#endif
#ifdef _USE_SSE
#ifdef USE_SSE
#include "resample_sse.h"
#endif
#ifdef _USE_NEON
#ifdef USE_NEON
#include "resample_neon.h"
#endif
/* Numer of elements to allocate on the stack */
/* Number of elements to allocate on the stack */
#ifdef VAR_ARRAYS
#define FIXED_STACK_ALLOC 8192
#else
@@ -194,16 +196,14 @@ struct FuncDef {
int oversample;
};
static const struct FuncDef _KAISER12 = {kaiser12_table, 64};
#define KAISER12 (&_KAISER12)
/*static struct FuncDef _KAISER12 = {kaiser12_table, 32};
#define KAISER12 (&_KAISER12)*/
static const struct FuncDef _KAISER10 = {kaiser10_table, 32};
#define KAISER10 (&_KAISER10)
static const struct FuncDef _KAISER8 = {kaiser8_table, 32};
#define KAISER8 (&_KAISER8)
static const struct FuncDef _KAISER6 = {kaiser6_table, 32};
#define KAISER6 (&_KAISER6)
static const struct FuncDef kaiser12_funcdef = {kaiser12_table, 64};
#define KAISER12 (&kaiser12_funcdef)
static const struct FuncDef kaiser10_funcdef = {kaiser10_table, 32};
#define KAISER10 (&kaiser10_funcdef)
static const struct FuncDef kaiser8_funcdef = {kaiser8_table, 32};
#define KAISER8 (&kaiser8_funcdef)
static const struct FuncDef kaiser6_funcdef = {kaiser6_table, 32};
#define KAISER6 (&kaiser6_funcdef)
struct QualityMapping {
int base_length;
@@ -473,7 +473,7 @@ static int resampler_basic_interpolate_single(SpeexResamplerState *st, spx_uint3
}
cubic_coef(frac, interp);
sum = MULT16_32_Q15(interp[0],SHR32(accum[0], 1)) + MULT16_32_Q15(interp[1],SHR32(accum[1], 1)) + MULT16_32_Q15(interp[2],SHR32(accum[2], 1)) + MULT16_32_Q15(interp[3],SHR32(accum[3], 1));
sum = MULT16_32_Q15(interp[0],accum[0]) + MULT16_32_Q15(interp[1],accum[1]) + MULT16_32_Q15(interp[2],accum[2]) + MULT16_32_Q15(interp[3],accum[3]);
sum = SATURATE32PSHR(sum, 15, 32767);
#else
cubic_coef(frac, interp);
@@ -572,6 +572,7 @@ static int resampler_basic_zero(SpeexResamplerState *st, spx_uint32_t channel_in
const int frac_advance = st->frac_advance;
const spx_uint32_t den_rate = st->den_rate;
(void)in;
while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len))
{
out[out_stride * out_sample++] = 0;
@@ -589,16 +590,15 @@ static int resampler_basic_zero(SpeexResamplerState *st, spx_uint32_t channel_in
return out_sample;
}
static int _muldiv(spx_uint32_t *result, spx_uint32_t value, spx_uint32_t mul, spx_uint32_t div)
static int multiply_frac(spx_uint32_t *result, spx_uint32_t value, spx_uint32_t num, spx_uint32_t den)
{
speex_assert(result);
spx_uint32_t major = value / div;
spx_uint32_t remainder = value % div;
spx_uint32_t major = value / den;
spx_uint32_t remain = value % den;
/* TODO: Could use 64 bits operation to check for overflow. But only guaranteed in C99+ */
if (remainder > UINT32_MAX / mul || major > UINT32_MAX / mul
|| major * mul > UINT32_MAX - remainder * mul / div)
if (remain > UINT32_MAX / num || major > UINT32_MAX / num
|| major * num > UINT32_MAX - remain * num / den)
return RESAMPLER_ERR_OVERFLOW;
*result = remainder * mul / div + major * mul;
*result = remain * num / den + major * num;
return RESAMPLER_ERR_SUCCESS;
}
@@ -619,7 +619,7 @@ static int update_filter(SpeexResamplerState *st)
{
/* down-sampling */
st->cutoff = quality_map[st->quality].downsample_bandwidth * st->den_rate / st->num_rate;
if (_muldiv(&st->filt_len,st->filt_len,st->num_rate,st->den_rate) != RESAMPLER_ERR_SUCCESS)
if (multiply_frac(&st->filt_len,st->filt_len,st->num_rate,st->den_rate) != RESAMPLER_ERR_SUCCESS)
goto fail;
/* Round up to make sure we have a multiple of 8 for SSE */
st->filt_len = ((st->filt_len-1)&(~0x7))+8;
@@ -638,12 +638,12 @@ static int update_filter(SpeexResamplerState *st)
st->cutoff = quality_map[st->quality].upsample_bandwidth;
}
/* Choose the resampling type that requires the least amount of memory */
#ifdef RESAMPLE_FULL_SINC_TABLE
use_direct = 1;
if (INT_MAX/sizeof(spx_word16_t)/st->den_rate < st->filt_len)
goto fail;
#else
/* Choose the resampling type that requires the least amount of memory */
use_direct = st->filt_len*st->den_rate <= st->filt_len*st->oversample+8
&& INT_MAX/sizeof(spx_word16_t)/st->den_rate >= st->filt_len;
#endif
@@ -733,16 +733,18 @@ static int update_filter(SpeexResamplerState *st)
{
spx_uint32_t j;
spx_uint32_t olen = old_length;
spx_uint32_t start = i*st->mem_alloc_size;
spx_uint32_t magic_samples = st->magic_samples[i];
/*if (st->magic_samples[i])*/
{
/* Try and remove the magic samples as if nothing had happened */
/* FIXME: This is wrong but for now we need it to avoid going over the array bounds */
olen = old_length + 2*st->magic_samples[i];
for (j=old_length-1+st->magic_samples[i];j--;)
st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]] = st->mem[i*old_alloc_size+j];
for (j=0;j<st->magic_samples[i];j++)
st->mem[i*st->mem_alloc_size+j] = 0;
olen = old_length + 2*magic_samples;
for (j=old_length-1+magic_samples;j--;)
st->mem[start+j+magic_samples] = st->mem[i*old_alloc_size+j];
for (j=0;j<magic_samples;j++)
st->mem[start+j] = 0;
st->magic_samples[i] = 0;
}
if (st->filt_len > olen)
@@ -750,17 +752,18 @@ static int update_filter(SpeexResamplerState *st)
/* If the new filter length is still bigger than the "augmented" length */
/* Copy data going backward */
for (j=0;j<olen-1;j++)
st->mem[i*st->mem_alloc_size+(st->filt_len-2-j)] = st->mem[i*st->mem_alloc_size+(olen-2-j)];
st->mem[start+(st->filt_len-2-j)] = st->mem[start+(olen-2-j)];
/* Then put zeros for lack of anything better */
for (;j<st->filt_len-1;j++)
st->mem[i*st->mem_alloc_size+(st->filt_len-2-j)] = 0;
st->mem[start+(st->filt_len-2-j)] = 0;
/* Adjust last_sample */
st->last_sample[i] += (st->filt_len - olen)/2;
} else {
/* Put back some of the magic! */
st->magic_samples[i] = (olen - st->filt_len)/2;
for (j=0;j<st->filt_len-1+st->magic_samples[i];j++)
st->mem[i*st->mem_alloc_size+j] = st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]];
magic_samples = (olen - st->filt_len)/2;
for (j=0;j<st->filt_len-1+magic_samples;j++)
st->mem[start+j] = st->mem[start+j+magic_samples];
st->magic_samples[i] = magic_samples;
}
}
} else if (st->filt_len < old_length)
@@ -977,8 +980,7 @@ EXPORT int speex_resampler_process_int(SpeexResamplerState *st, spx_uint32_t cha
const spx_uint32_t xlen = st->mem_alloc_size - (st->filt_len - 1);
#ifdef VAR_ARRAYS
const unsigned int ylen = (olen < FIXED_STACK_ALLOC) ? olen : FIXED_STACK_ALLOC;
VARDECL(spx_word16_t *ystack);
ALLOC(ystack, ylen, spx_word16_t);
spx_word16_t ystack[ylen];
#else
const unsigned int ylen = FIXED_STACK_ALLOC;
spx_word16_t ystack[FIXED_STACK_ALLOC];
@@ -1093,7 +1095,7 @@ EXPORT void speex_resampler_get_rate(SpeexResamplerState *st, spx_uint32_t *in_r
*out_rate = st->out_rate;
}
static inline spx_uint32_t _gcd(spx_uint32_t a, spx_uint32_t b)
static inline spx_uint32_t compute_gcd(spx_uint32_t a, spx_uint32_t b)
{
while (b != 0)
{
@@ -1123,7 +1125,7 @@ EXPORT int speex_resampler_set_rate_frac(SpeexResamplerState *st, spx_uint32_t r
st->num_rate = ratio_num;
st->den_rate = ratio_den;
fact = _gcd (st->num_rate, st->den_rate);
fact = compute_gcd(st->num_rate, st->den_rate);
st->num_rate /= fact;
st->den_rate /= fact;
@@ -1132,7 +1134,7 @@ EXPORT int speex_resampler_set_rate_frac(SpeexResamplerState *st, spx_uint32_t r
{
for (i=0;i<st->nb_channels;i++)
{
if (_muldiv(&st->samp_frac_num[i],st->samp_frac_num[i],st->den_rate,old_den) != RESAMPLER_ERR_SUCCESS)
if (multiply_frac(&st->samp_frac_num[i],st->samp_frac_num[i],st->den_rate,old_den) != RESAMPLER_ERR_SUCCESS)
return RESAMPLER_ERR_OVERFLOW;
/* Safety net */
if (st->samp_frac_num[i] >= st->den_rate)

View File

@@ -36,14 +36,26 @@
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <arm_neon.h>
#include <stdint.h>
#ifdef FIXED_POINT
#ifdef __thumb2__
#if defined(__aarch64__)
static inline int32_t saturate_32bit_to_16bit(int32_t a) {
int32_t ret;
asm ("fmov s0, %w[a]\n"
"sqxtn h0, s0\n"
"sxtl v0.4s, v0.4h\n"
"fmov %w[ret], s0\n"
: [ret] "=r" (ret)
: [a] "r" (a)
: "v0" );
return ret;
}
#elif defined(__thumb2__)
static inline int32_t saturate_32bit_to_16bit(int32_t a) {
int32_t ret;
asm ("ssat %[ret], #16, %[a]"
: [ret] "=&r" (ret)
: [ret] "=r" (ret)
: [a] "r" (a)
: );
return ret;
@@ -54,7 +66,7 @@ static inline int32_t saturate_32bit_to_16bit(int32_t a) {
asm ("vmov.s32 d0[0], %[a]\n"
"vqmovn.s32 d0, q0\n"
"vmov.s16 %[ret], d0[0]\n"
: [ret] "=&r" (ret)
: [ret] "=r" (ret)
: [a] "r" (a)
: "q0");
return ret;
@@ -64,7 +76,63 @@ static inline int32_t saturate_32bit_to_16bit(int32_t a) {
#define WORD2INT(x) (saturate_32bit_to_16bit(x))
#define OVERRIDE_INNER_PRODUCT_SINGLE
/* Only works when len % 4 == 0 */
/* Only works when len % 4 == 0 and len >= 4 */
#if defined(__aarch64__)
static inline int32_t inner_product_single(const int16_t *a, const int16_t *b, unsigned int len)
{
int32_t ret;
uint32_t remainder = len % 16;
len = len - remainder;
asm volatile (" cmp %w[len], #0\n"
" b.ne 1f\n"
" ld1 {v16.4h}, [%[b]], #8\n"
" ld1 {v20.4h}, [%[a]], #8\n"
" subs %w[remainder], %w[remainder], #4\n"
" smull v0.4s, v16.4h, v20.4h\n"
" b.ne 4f\n"
" b 5f\n"
"1:"
" ld1 {v16.4h, v17.4h, v18.4h, v19.4h}, [%[b]], #32\n"
" ld1 {v20.4h, v21.4h, v22.4h, v23.4h}, [%[a]], #32\n"
" subs %w[len], %w[len], #16\n"
" smull v0.4s, v16.4h, v20.4h\n"
" smlal v0.4s, v17.4h, v21.4h\n"
" smlal v0.4s, v18.4h, v22.4h\n"
" smlal v0.4s, v19.4h, v23.4h\n"
" b.eq 3f\n"
"2:"
" ld1 {v16.4h, v17.4h, v18.4h, v19.4h}, [%[b]], #32\n"
" ld1 {v20.4h, v21.4h, v22.4h, v23.4h}, [%[a]], #32\n"
" subs %w[len], %w[len], #16\n"
" smlal v0.4s, v16.4h, v20.4h\n"
" smlal v0.4s, v17.4h, v21.4h\n"
" smlal v0.4s, v18.4h, v22.4h\n"
" smlal v0.4s, v19.4h, v23.4h\n"
" b.ne 2b\n"
"3:"
" cmp %w[remainder], #0\n"
" b.eq 5f\n"
"4:"
" ld1 {v18.4h}, [%[b]], #8\n"
" ld1 {v22.4h}, [%[a]], #8\n"
" subs %w[remainder], %w[remainder], #4\n"
" smlal v0.4s, v18.4h, v22.4h\n"
" b.ne 4b\n"
"5:"
" saddlv d0, v0.4s\n"
" sqxtn s0, d0\n"
" sqrshrn h0, s0, #15\n"
" sxtl v0.4s, v0.4h\n"
" fmov %w[ret], s0\n"
: [ret] "=r" (ret), [a] "+r" (a), [b] "+r" (b),
[len] "+r" (len), [remainder] "+r" (remainder)
:
: "cc", "v0",
"v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23");
return ret;
}
#else
static inline int32_t inner_product_single(const int16_t *a, const int16_t *b, unsigned int len)
{
int32_t ret;
@@ -112,33 +180,104 @@ static inline int32_t inner_product_single(const int16_t *a, const int16_t *b, u
" vqmovn.s64 d0, q0\n"
" vqrshrn.s32 d0, q0, #15\n"
" vmov.s16 %[ret], d0[0]\n"
: [ret] "=&r" (ret), [a] "+r" (a), [b] "+r" (b),
: [ret] "=r" (ret), [a] "+r" (a), [b] "+r" (b),
[len] "+r" (len), [remainder] "+r" (remainder)
:
: "cc", "q0",
"d16", "d17", "d18", "d19",
"d20", "d21", "d22", "d23");
"d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23");
return ret;
}
#elif defined(FLOATING_POINT)
#endif // !defined(__aarch64__)
#elif defined(FLOATING_POINT)
#if defined(__aarch64__)
static inline int32_t saturate_float_to_16bit(float a) {
int32_t ret;
asm ("fcvtas s1, %s[a]\n"
"sqxtn h1, s1\n"
"sxtl v1.4s, v1.4h\n"
"fmov %w[ret], s1\n"
: [ret] "=r" (ret)
: [a] "w" (a)
: "v1");
return ret;
}
#else
static inline int32_t saturate_float_to_16bit(float a) {
int32_t ret;
asm ("vmov.f32 d0[0], %[a]\n"
"vcvt.s32.f32 d0, d0, #15\n"
"vqrshrn.s32 d0, q0, #15\n"
"vmov.s16 %[ret], d0[0]\n"
: [ret] "=&r" (ret)
: [ret] "=r" (ret)
: [a] "r" (a)
: "q0");
return ret;
}
#endif
#undef WORD2INT
#define WORD2INT(x) (saturate_float_to_16bit(x))
#define OVERRIDE_INNER_PRODUCT_SINGLE
/* Only works when len % 4 == 0 */
/* Only works when len % 4 == 0 and len >= 4 */
#if defined(__aarch64__)
static inline float inner_product_single(const float *a, const float *b, unsigned int len)
{
float ret;
uint32_t remainder = len % 16;
len = len - remainder;
asm volatile (" cmp %w[len], #0\n"
" b.ne 1f\n"
" ld1 {v16.4s}, [%[b]], #16\n"
" ld1 {v20.4s}, [%[a]], #16\n"
" subs %w[remainder], %w[remainder], #4\n"
" fmul v1.4s, v16.4s, v20.4s\n"
" b.ne 4f\n"
" b 5f\n"
"1:"
" ld1 {v16.4s, v17.4s, v18.4s, v19.4s}, [%[b]], #64\n"
" ld1 {v20.4s, v21.4s, v22.4s, v23.4s}, [%[a]], #64\n"
" subs %w[len], %w[len], #16\n"
" fmul v1.4s, v16.4s, v20.4s\n"
" fmul v2.4s, v17.4s, v21.4s\n"
" fmul v3.4s, v18.4s, v22.4s\n"
" fmul v4.4s, v19.4s, v23.4s\n"
" b.eq 3f\n"
"2:"
" ld1 {v16.4s, v17.4s, v18.4s, v19.4s}, [%[b]], #64\n"
" ld1 {v20.4s, v21.4s, v22.4s, v23.4s}, [%[a]], #64\n"
" subs %w[len], %w[len], #16\n"
" fmla v1.4s, v16.4s, v20.4s\n"
" fmla v2.4s, v17.4s, v21.4s\n"
" fmla v3.4s, v18.4s, v22.4s\n"
" fmla v4.4s, v19.4s, v23.4s\n"
" b.ne 2b\n"
"3:"
" fadd v16.4s, v1.4s, v2.4s\n"
" fadd v17.4s, v3.4s, v4.4s\n"
" cmp %w[remainder], #0\n"
" fadd v1.4s, v16.4s, v17.4s\n"
" b.eq 5f\n"
"4:"
" ld1 {v18.4s}, [%[b]], #16\n"
" ld1 {v22.4s}, [%[a]], #16\n"
" subs %w[remainder], %w[remainder], #4\n"
" fmla v1.4s, v18.4s, v22.4s\n"
" b.ne 4b\n"
"5:"
" faddp v1.4s, v1.4s, v1.4s\n"
" faddp %[ret].4s, v1.4s, v1.4s\n"
: [ret] "=w" (ret), [a] "+r" (a), [b] "+r" (b),
[len] "+r" (len), [remainder] "+r" (remainder)
:
: "cc", "v1", "v2", "v3", "v4",
"v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23");
return ret;
}
#else
static inline float inner_product_single(const float *a, const float *b, unsigned int len)
{
float ret;
@@ -191,11 +330,12 @@ static inline float inner_product_single(const float *a, const float *b, unsigne
" vadd.f32 d0, d0, d1\n"
" vpadd.f32 d0, d0, d0\n"
" vmov.f32 %[ret], d0[0]\n"
: [ret] "=&r" (ret), [a] "+r" (a), [b] "+r" (b),
: [ret] "=r" (ret), [a] "+r" (a), [b] "+r" (b),
[len] "+l" (len), [remainder] "+l" (remainder)
:
: "cc", "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", "q8",
"q9", "q10", "q11");
: "cc", "q0", "q1", "q2", "q3",
"q4", "q5", "q6", "q7", "q8", "q9", "q10", "q11");
return ret;
}
#endif // defined(__aarch64__)
#endif

View File

@@ -71,7 +71,7 @@ static inline float interpolate_product_single(const float *a, const float *b, u
return ret;
}
#ifdef _USE_SSE2
#ifdef USE_SSE2
#include <emmintrin.h>
#define OVERRIDE_INNER_PRODUCT_DOUBLE

View File

@@ -1,3 +1,176 @@
# 12.0.0 - 2025-09-17
- Optimized the default floating point formatting
(https://github.com/fmtlib/fmt/issues/3675,
https://github.com/fmtlib/fmt/issues/4516). In particular, formatting a
`double` with format string compilation into a stack allocated buffer is
more than 60% faster in version 12.0 compared to 11.2 according to
[dtoa-benchmark](https://github.com/fmtlib/dtoa-benchmark):
```
Function Time (ns) Speedup
fmt11 34.471 1.00x
fmt12 21.000 1.64x
```
<img width="766" height="609" src="https://github.com/user-attachments/assets/d7d768ad-7543-468c-b0bb-449abf73b31b" />
- Added `constexpr` support to `fmt::format`. For example:
```c++
#include <fmt/compile.h>
using namespace fmt::literals;
std::string s = fmt::format(""_cf, 42);
```
now works at compile time provided that `std::string` supports `constexpr`
(https://github.com/fmtlib/fmt/issues/3403,
https://github.com/fmtlib/fmt/pull/4456). Thanks @msvetkin.
- Added `FMT_STATIC_FORMAT` that allows formatting into a string of the exact
required size at compile time.
For example:
```c++
#include <fmt/compile.h>
constexpr auto s = FMT_STATIC_FORMAT("{}", 42);
```
compiles to just
```s
__ZL1s:
.asciiz "42"
```
It can be accessed as a C string with `s.c_str()` or as a string view with
`s.str()`.
- Improved C++20 module support
(https://github.com/fmtlib/fmt/pull/4451,
https://github.com/fmtlib/fmt/pull/4459,
https://github.com/fmtlib/fmt/pull/4476,
https://github.com/fmtlib/fmt/pull/4488,
https://github.com/fmtlib/fmt/issues/4491,
https://github.com/fmtlib/fmt/pull/4495).
Thanks @arBmind, @tkhyn, @Mishura4, @anonymouspc and @autoantwort.
- Switched to using estimated display width in precision. For example:
```c++
fmt::print("|{:.4}|\n|1234|\n", "🐱🐱🐱");
```
prints
![](https://github.com/user-attachments/assets/6c4446b3-13eb-43b9-b74a-b4543540ad6a)
because `🐱` has an estimated width of 2
(https://github.com/fmtlib/fmt/issues/4272,
https://github.com/fmtlib/fmt/pull/4443,
https://github.com/fmtlib/fmt/pull/4475).
Thanks @nikhilreddydev and @localspook.
- Fix interaction between debug presentation, precision, and width for strings
(https://github.com/fmtlib/fmt/pull/4478). Thanks @localspook.
- Implemented allocator propagation on `basic_memory_buffer` move
(https://github.com/fmtlib/fmt/issues/4487,
https://github.com/fmtlib/fmt/pull/4490). Thanks @toprakmurat.
- Fixed an ambiguity between `std::reference_wrapper<T>` and `format_as`
formatters (https://github.com/fmtlib/fmt/issues/4424,
https://github.com/fmtlib/fmt/pull/4434). Thanks @jeremy-rifkin.
- Removed the following deprecated APIs:
- `has_formatter`: use `is_formattable` instead,
- `basic_format_args::parse_context_type`,
`basic_format_args::formatter_type` and similar aliases in context types,
- wide stream overload of `fmt::printf`,
- wide stream overloads of `fmt::print` that take text styles,
- `is_*char` traits,
- `fmt::localtime`.
- Deprecated wide overloads of `fmt::fprintf` and `fmt::sprintf`.
- Improved diagnostics for the incorrect usage of `fmt::ptr`
(https://github.com/fmtlib/fmt/pull/4453). Thanks @TobiSchluter.
- Made handling of ANSI escape sequences more efficient
(https://github.com/fmtlib/fmt/pull/4511,
https://github.com/fmtlib/fmt/pull/4528).
Thanks @localspook and @Anas-Hamdane.
- Fixed a buffer overflow on all emphasis flags set
(https://github.com/fmtlib/fmt/pull/4498). Thanks @dominicpoeschko.
- Fixed an integer overflow for precision close to the max `int` value.
- Fixed compatibility with WASI (https://github.com/fmtlib/fmt/issues/4496,
https://github.com/fmtlib/fmt/pull/4497). Thanks @whitequark.
- Fixed `back_insert_iterator` detection, preventing a fallback on slower path
that handles arbitrary iterators (https://github.com/fmtlib/fmt/issues/4454).
- Fixed handling of invalid glibc `FILE` buffers
(https://github.com/fmtlib/fmt/issues/4469).
- Added `wchar_t` support to the `std::byte` formatter
(https://github.com/fmtlib/fmt/issues/4479,
https://github.com/fmtlib/fmt/pull/4480). Thanks @phprus.
- Changed component prefix from `fmt-` to `fmt_` for compatibility with
NSIS/CPack on Windows, e.g. `fmt-doc` changed to `fmt_doc`
(https://github.com/fmtlib/fmt/issues/4441,
https://github.com/fmtlib/fmt/pull/4442). Thanks @n-stein.
- Added the `FMT_CUSTOM_ASSERT_FAIL` macro to simplify providing a custom
`fmt::assert_fail` implementation (https://github.com/fmtlib/fmt/pull/4505).
Thanks @HazardyKnusperkeks.
- Switched to `FMT_THROW` on reporting format errors so that it can be
overriden by users when exceptions are disabled
(https://github.com/fmtlib/fmt/pull/4521). Thanks @HazardyKnusperkeks.
- Improved master project detection and disabled install targets when using
{fmt} as a subproject by default (https://github.com/fmtlib/fmt/pull/4536).
Thanks @crueter.
- Made various code improvements
(https://github.com/fmtlib/fmt/pull/4445,
https://github.com/fmtlib/fmt/pull/4448,
https://github.com/fmtlib/fmt/pull/4473,
https://github.com/fmtlib/fmt/pull/4522).
Thanks @localspook, @tchaikov and @way4sahil.
- Added Conan instructions to the docs
(https://github.com/fmtlib/fmt/pull/4537). Thanks @uilianries.
- Removed Bazel files to avoid issues with downstream packaging
(https://github.com/fmtlib/fmt/pull/4530). Thanks @mering.
- Added more entries for generated files to `.gitignore`
(https://github.com/fmtlib/fmt/pull/4355,
https://github.com/fmtlib/fmt/pull/4512).
Thanks @dinomight and @localspook.
- Fixed various warnings and compilation issues
(https://github.com/fmtlib/fmt/pull/4447,
https://github.com/fmtlib/fmt/issues/4470,
https://github.com/fmtlib/fmt/pull/4474,
https://github.com/fmtlib/fmt/pull/4477,
https://github.com/fmtlib/fmt/pull/4471,
https://github.com/fmtlib/fmt/pull/4483,
https://github.com/fmtlib/fmt/pull/4515,
https://github.com/fmtlib/fmt/issues/4533,
https://github.com/fmtlib/fmt/pull/4534).
Thanks @dodomorandi, @localspook, @remyjette, @Tomek-Stolarczyk, @Mishura4,
@mattiasljungstrom and @FatihBAKIR.
# 11.2.0 - 2025-05-03
- Added the `s` specifier for `std::error_code`. It allows formatting an error
@@ -56,17 +229,18 @@
https://github.com/fmtlib/fmt/pull/4361). Thanks @dinomight.
- Added error reporting for duplicate named arguments
(https://github.com/fmtlib/fmt/pull/4367). Thanks @dinomight.
(https://github.com/fmtlib/fmt/issues/4282,
https://github.com/fmtlib/fmt/pull/4367). Thanks @dinomight.
- Fixed formatting of `long` with `FMT_BUILTIN_TYPES=0`
(https://github.com/fmtlib/fmt/issues/4375,
https://github.com/fmtlib/fmt/issues/4394).
- Optimized `text_style` using bit packing
(https://github.com/fmtlib/fmt/pull/4363). Thanks @LocalSpook.
(https://github.com/fmtlib/fmt/pull/4363). Thanks @localspook.
- Added support for incomplete types (https://github.com/fmtlib/fmt/issues/3180,
https://github.com/fmtlib/fmt/pull/4383). Thanks @LocalSpook.
https://github.com/fmtlib/fmt/pull/4383). Thanks @localspook.
- Fixed a flush issue in `fmt::print` when using libstdc++
(https://github.com/fmtlib/fmt/issues/4398).
@@ -107,13 +281,14 @@
`float` (https://github.com/fmtlib/fmt/issues/3649).
- Moved `is_compiled_string` to the public API
(https://github.com/fmtlib/fmt/issues/4342). Thanks @SwooshyCueb.
(https://github.com/fmtlib/fmt/issues/4335,
https://github.com/fmtlib/fmt/issues/4342). Thanks @SwooshyCueb.
- Simplified implementation of `operator""_cf`
(https://github.com/fmtlib/fmt/pull/4349). Thanks @LocalSpook.
(https://github.com/fmtlib/fmt/pull/4349). Thanks @localspook.
- Fixed `__builtin_strlen` detection (https://github.com/fmtlib/fmt/pull/4329).
Thanks @LocalSpook.
Thanks @localspook.
- Fixed handling of BMI paths with the Ninja generator
(https://github.com/fmtlib/fmt/pull/4344). Thanks @tkhyn.

View File

@@ -4,8 +4,9 @@
[![image](https://github.com/fmtlib/fmt/workflows/macos/badge.svg)](https://github.com/fmtlib/fmt/actions?query=workflow%3Amacos)
[![image](https://github.com/fmtlib/fmt/workflows/windows/badge.svg)](https://github.com/fmtlib/fmt/actions?query=workflow%3Awindows)
[![fmt is continuously fuzzed at oss-fuzz](https://oss-fuzz-build-logs.storage.googleapis.com/badges/fmt.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?\%0Acolspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20\%0ASummary&q=proj%3Dfmt&can=1)
[![Ask questions at StackOverflow with the tag fmt](https://img.shields.io/badge/stackoverflow-fmt-blue.svg)](https://stackoverflow.com/questions/tagged/fmt)
[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/8880/badge)](https://www.bestpractices.dev/projects/8880)
[![image](https://api.securityscorecards.dev/projects/github.com/fmtlib/fmt/badge)](https://securityscorecards.dev/viewer/?uri=github.com/fmtlib/fmt)
[![Ask questions at StackOverflow with the tag fmt](https://img.shields.io/badge/stackoverflow-fmt-blue.svg)](https://stackoverflow.com/questions/tagged/fmt)
**{fmt}** is an open-source formatting library providing a fast and safe
alternative to C stdio and C++ iostreams.

View File

@@ -71,7 +71,7 @@ class dynamic_arg_list {
* It can be implicitly converted into `fmt::basic_format_args` for passing
* into type-erased formatting functions such as `fmt::vformat`.
*/
template <typename Context> class dynamic_format_arg_store {
FMT_EXPORT template <typename Context> class dynamic_format_arg_store {
private:
using char_type = typename Context::char_type;
@@ -212,7 +212,7 @@ template <typename Context> class dynamic_format_arg_store {
}
/// Returns the number of elements in the store.
size_t size() const noexcept { return data_.size(); }
auto size() const noexcept -> size_t { return data_.size(); }
};
FMT_END_NAMESPACE

View File

@@ -21,7 +21,7 @@
#endif
// The fmt library version in the form major * 10000 + minor * 100 + patch.
#define FMT_VERSION 110200
#define FMT_VERSION 120000
// Detect compiler versions.
#if defined(__clang__) && !defined(__ibmxl__)
@@ -201,14 +201,6 @@
# define FMT_NODISCARD
#endif
#ifdef FMT_DEPRECATED
// Use the provided definition.
#elif FMT_HAS_CPP14_ATTRIBUTE(deprecated)
# define FMT_DEPRECATED [[deprecated]]
#else
# define FMT_DEPRECATED /* deprecated */
#endif
#if FMT_GCC_VERSION || FMT_CLANG_VERSION
# define FMT_VISIBILITY(value) __attribute__((visibility(value)))
#else
@@ -260,7 +252,7 @@ FMT_PRAGMA_CLANG(diagnostic push)
#ifndef FMT_BEGIN_NAMESPACE
# define FMT_BEGIN_NAMESPACE \
namespace fmt { \
inline namespace v11 {
inline namespace v12 {
# define FMT_END_NAMESPACE \
} \
}
@@ -356,6 +348,9 @@ template <typename T> constexpr auto max_of(T a, T b) -> T {
return a > b ? a : b;
}
FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
const char* message);
namespace detail {
// Suppresses "unused variable" warnings with the method described in
// https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/.
@@ -396,7 +391,7 @@ FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
# define FMT_ASSERT(condition, message) \
((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \
? (void)0 \
: fmt::detail::assert_fail(__FILE__, __LINE__, (message)))
: ::fmt::assert_fail(__FILE__, __LINE__, (message)))
#endif
#ifdef FMT_USE_INT128
@@ -463,12 +458,13 @@ enum { use_utf8 = !FMT_WIN32 || is_utf8_enabled };
static_assert(!FMT_UNICODE || use_utf8,
"Unicode support requires compiling with /utf-8");
template <typename T> constexpr const char* narrow(const T*) { return nullptr; }
constexpr FMT_ALWAYS_INLINE const char* narrow(const char* s) { return s; }
template <typename T> constexpr auto narrow(T*) -> char* { return nullptr; }
constexpr FMT_ALWAYS_INLINE auto narrow(const char* s) -> const char* {
return s;
}
template <typename Char>
FMT_CONSTEXPR auto compare(const Char* s1, const Char* s2, std::size_t n)
-> int {
FMT_CONSTEXPR auto compare(const Char* s1, const Char* s2, size_t n) -> int {
if (!is_constant_evaluated() && sizeof(Char) == 1) return memcmp(s1, s2, n);
for (; n != 0; ++s1, ++s2, --n) {
if (*s1 < *s2) return -1;
@@ -540,7 +536,7 @@ template <typename Char> class basic_string_view {
FMT_CONSTEXPR20 basic_string_view(const Char* s) : data_(s) {
#if FMT_HAS_BUILTIN(__builtin_strlen) || FMT_GCC_VERSION || FMT_CLANG_VERSION
if (std::is_same<Char, char>::value && !detail::is_constant_evaluated()) {
size_ = __builtin_strlen(detail::narrow(s)); // strlen is not costexpr.
size_ = __builtin_strlen(detail::narrow(s)); // strlen is not constexpr.
return;
}
#endif
@@ -616,19 +612,6 @@ template <typename Char> class basic_string_view {
using string_view = basic_string_view<char>;
// DEPRECATED! Will be merged with is_char and moved to detail.
template <typename T> struct is_xchar : std::false_type {};
template <> struct is_xchar<wchar_t> : std::true_type {};
template <> struct is_xchar<char16_t> : std::true_type {};
template <> struct is_xchar<char32_t> : std::true_type {};
#ifdef __cpp_char8_t
template <> struct is_xchar<char8_t> : std::true_type {};
#endif
// Specifies if `T` is a character (code unit) type.
template <typename T> struct is_char : is_xchar<T> {};
template <> struct is_char<char> : std::true_type {};
template <typename T> class basic_appender;
using appender = basic_appender<char>;
@@ -781,7 +764,7 @@ class basic_specs {
(static_cast<unsigned>(p) << precision_shift);
}
constexpr bool dynamic() const {
constexpr auto dynamic() const -> bool {
return (data_ & (width_mask | precision_mask)) != 0;
}
@@ -921,14 +904,47 @@ template <typename Char = char> class parse_context {
FMT_CONSTEXPR void check_dynamic_spec(int arg_id);
};
#ifndef FMT_USE_LOCALE
# define FMT_USE_LOCALE (FMT_OPTIMIZE_SIZE <= 1)
#endif
// A type-erased reference to std::locale to avoid the heavy <locale> include.
class locale_ref {
#if FMT_USE_LOCALE
private:
const void* locale_; // A type-erased pointer to std::locale.
public:
constexpr locale_ref() : locale_(nullptr) {}
template <typename Locale, FMT_ENABLE_IF(sizeof(Locale::collate) != 0)>
locale_ref(const Locale& loc);
inline explicit operator bool() const noexcept { return locale_ != nullptr; }
#endif // FMT_USE_LOCALE
public:
template <typename Locale> auto get() const -> Locale;
};
FMT_END_EXPORT
namespace detail {
// Specifies if `T` is a code unit type.
template <typename T> struct is_code_unit : std::false_type {};
template <> struct is_code_unit<char> : std::true_type {};
template <> struct is_code_unit<wchar_t> : std::true_type {};
template <> struct is_code_unit<char16_t> : std::true_type {};
template <> struct is_code_unit<char32_t> : std::true_type {};
#ifdef __cpp_char8_t
template <> struct is_code_unit<char8_t> : bool_constant<is_utf8_enabled> {};
#endif
// Constructs fmt::basic_string_view<Char> from types implicitly convertible
// to it, deducing Char. Explicitly convertible types such as the ones returned
// from FMT_STRING are intentionally excluded.
template <typename Char, FMT_ENABLE_IF(is_char<Char>::value)>
template <typename Char, FMT_ENABLE_IF(is_code_unit<Char>::value)>
constexpr auto to_string_view(const Char* s) -> basic_string_view<Char> {
return s;
}
@@ -1057,11 +1073,11 @@ template <bool B1, bool B2, bool... Tail> constexpr auto count() -> int {
return (B1 ? 1 : 0) + count<B2, Tail...>();
}
template <typename... Args> constexpr auto count_named_args() -> int {
return count<is_named_arg<Args>::value...>();
template <typename... T> constexpr auto count_named_args() -> int {
return count<is_named_arg<T>::value...>();
}
template <typename... Args> constexpr auto count_static_named_args() -> int {
return count<is_static_named_arg<Args>::value...>();
template <typename... T> constexpr auto count_static_named_args() -> int {
return count<is_static_named_arg<T>::value...>();
}
template <typename Char> struct named_arg_info {
@@ -1069,7 +1085,7 @@ template <typename Char> struct named_arg_info {
int id;
};
// named_args is non-const to suppress a bogus -Wmaybe-uninitalized in gcc 13.
// named_args is non-const to suppress a bogus -Wmaybe-uninitialized in gcc 13.
template <typename Char>
FMT_CONSTEXPR void check_for_duplicate(named_arg_info<Char>* named_args,
int named_arg_index,
@@ -1173,7 +1189,7 @@ template <typename Char> struct type_mapper {
static auto map(ubitint<N>)
-> conditional_t<N <= 64, unsigned long long, void>;
template <typename T, FMT_ENABLE_IF(is_char<T>::value)>
template <typename T, FMT_ENABLE_IF(is_code_unit<T>::value)>
static auto map(T) -> conditional_t<
std::is_same<T, char>::value || std::is_same<T, Char>::value, Char, void>;
@@ -1679,12 +1695,12 @@ template <typename... T> struct arg_pack {};
template <typename Char, int NUM_ARGS, int NUM_NAMED_ARGS, bool DYNAMIC_NAMES>
class format_string_checker {
private:
type types_[max_of(1, NUM_ARGS)];
named_arg_info<Char> named_args_[max_of(1, NUM_NAMED_ARGS)];
type types_[max_of<size_t>(1, NUM_ARGS)];
named_arg_info<Char> named_args_[max_of<size_t>(1, NUM_NAMED_ARGS)];
compile_parse_context<Char> context_;
using parse_func = auto (*)(parse_context<Char>&) -> const Char*;
parse_func parse_funcs_[max_of(1, NUM_ARGS)];
parse_func parse_funcs_[max_of<size_t>(1, NUM_ARGS)];
public:
template <typename... T>
@@ -2033,6 +2049,17 @@ struct has_back_insert_iterator_container_append<
.append(std::declval<InputIt>(),
std::declval<InputIt>()))>> : std::true_type {};
template <typename OutputIt, typename InputIt, typename = void>
struct has_back_insert_iterator_container_insert_at_end : std::false_type {};
template <typename OutputIt, typename InputIt>
struct has_back_insert_iterator_container_insert_at_end<
OutputIt, InputIt,
void_t<decltype(get_container(std::declval<OutputIt>())
.insert(get_container(std::declval<OutputIt>()).end(),
std::declval<InputIt>(),
std::declval<InputIt>()))>> : std::true_type {};
// An optimized version of std::copy with the output value type (T).
template <typename T, typename InputIt, typename OutputIt,
FMT_ENABLE_IF(is_back_insert_iterator<OutputIt>::value&&
@@ -2047,6 +2074,8 @@ FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out)
template <typename T, typename InputIt, typename OutputIt,
FMT_ENABLE_IF(is_back_insert_iterator<OutputIt>::value &&
!has_back_insert_iterator_container_append<
OutputIt, InputIt>::value &&
has_back_insert_iterator_container_insert_at_end<
OutputIt, InputIt>::value)>
FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out)
-> OutputIt {
@@ -2056,7 +2085,11 @@ FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out)
}
template <typename T, typename InputIt, typename OutputIt,
FMT_ENABLE_IF(!is_back_insert_iterator<OutputIt>::value)>
FMT_ENABLE_IF(!(is_back_insert_iterator<OutputIt>::value &&
(has_back_insert_iterator_container_append<
OutputIt, InputIt>::value ||
has_back_insert_iterator_container_insert_at_end<
OutputIt, InputIt>::value)))>
FMT_CONSTEXPR auto copy(InputIt begin, InputIt end, OutputIt out) -> OutputIt {
while (begin != end) *out++ = static_cast<T>(*begin++);
return out;
@@ -2176,7 +2209,7 @@ template <typename Context> class value {
static_assert(N <= 64, "unsupported _BitInt");
}
template <typename T, FMT_ENABLE_IF(is_char<T>::value)>
template <typename T, FMT_ENABLE_IF(is_code_unit<T>::value)>
constexpr FMT_INLINE value(T x FMT_BUILTIN) : char_value(x) {
static_assert(
std::is_same<T, char>::value || std::is_same<T, char_type>::value,
@@ -2252,7 +2285,7 @@ template <typename Context> class value {
custom.value = const_cast<value_type*>(&x);
#endif
}
custom.format = format_custom<value_type, formatter<value_type, char_type>>;
custom.format = format_custom<value_type>;
}
template <typename T, FMT_ENABLE_IF(!has_formatter<T, char_type>())>
@@ -2263,10 +2296,10 @@ template <typename Context> class value {
}
// Formats an argument of a custom type, such as a user-defined class.
template <typename T, typename Formatter>
template <typename T>
static void format_custom(void* arg, parse_context<char_type>& parse_ctx,
Context& ctx) {
auto f = Formatter();
auto f = formatter<T, char_type>();
parse_ctx.advance_to(f.parse(parse_ctx));
using qualified_type =
conditional_t<has_formatter<const T, char_type>(), const T, T>;
@@ -2293,35 +2326,14 @@ struct is_output_iterator<
enable_if_t<std::is_assignable<decltype(*std::declval<decay_t<It>&>()++),
T>::value>> : std::true_type {};
#ifndef FMT_USE_LOCALE
# define FMT_USE_LOCALE (FMT_OPTIMIZE_SIZE <= 1)
#endif
// A type-erased reference to an std::locale to avoid a heavy <locale> include.
class locale_ref {
#if FMT_USE_LOCALE
private:
const void* locale_; // A type-erased pointer to std::locale.
public:
constexpr locale_ref() : locale_(nullptr) {}
template <typename Locale> locale_ref(const Locale& loc);
inline explicit operator bool() const noexcept { return locale_ != nullptr; }
#endif // FMT_USE_LOCALE
public:
template <typename Locale> auto get() const -> Locale;
};
template <typename> constexpr auto encode_types() -> unsigned long long {
return 0;
}
template <typename Context, typename Arg, typename... Args>
template <typename Context, typename First, typename... T>
constexpr auto encode_types() -> unsigned long long {
return static_cast<unsigned>(stored_type_constant<Arg, Context>::value) |
(encode_types<Context, Args...>() << packed_arg_bits);
return static_cast<unsigned>(stored_type_constant<First, Context>::value) |
(encode_types<Context, T...>() << packed_arg_bits);
}
template <typename Context, typename... T, size_t NUM_ARGS = sizeof...(T)>
@@ -2338,8 +2350,9 @@ template <typename Context, int NUM_ARGS, int NUM_NAMED_ARGS,
unsigned long long DESC>
struct named_arg_store {
// args_[0].named_args points to named_args to avoid bloating format_args.
arg_t<Context, NUM_ARGS> args[1 + NUM_ARGS];
named_arg_info<typename Context::char_type> named_args[NUM_NAMED_ARGS];
arg_t<Context, NUM_ARGS> args[1u + NUM_ARGS];
named_arg_info<typename Context::char_type>
named_args[static_cast<size_t>(NUM_NAMED_ARGS)];
template <typename... T>
FMT_CONSTEXPR FMT_ALWAYS_INLINE named_arg_store(T&... values)
@@ -2358,8 +2371,8 @@ struct named_arg_store {
}
named_arg_store(const named_arg_store& rhs) = delete;
named_arg_store& operator=(const named_arg_store& rhs) = delete;
named_arg_store& operator=(named_arg_store&& rhs) = delete;
auto operator=(const named_arg_store& rhs) -> named_arg_store& = delete;
auto operator=(named_arg_store&& rhs) -> named_arg_store& = delete;
operator const arg_t<Context, NUM_ARGS>*() const { return args + 1; }
};
@@ -2372,7 +2385,7 @@ struct format_arg_store {
// +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
using type =
conditional_t<NUM_NAMED_ARGS == 0,
arg_t<Context, NUM_ARGS>[max_of(1, NUM_ARGS)],
arg_t<Context, NUM_ARGS>[max_of<size_t>(1, NUM_ARGS)],
named_arg_store<Context, NUM_ARGS, NUM_NAMED_ARGS, DESC>>;
type args;
};
@@ -2656,22 +2669,17 @@ class context {
private:
appender out_;
format_args args_;
FMT_NO_UNIQUE_ADDRESS detail::locale_ref loc_;
FMT_NO_UNIQUE_ADDRESS locale_ref loc_;
public:
/// The character type for the output.
using char_type = char;
using char_type = char; ///< The character type for the output.
using iterator = appender;
using format_arg = basic_format_arg<context>;
using parse_context_type FMT_DEPRECATED = parse_context<>;
template <typename T> using formatter_type FMT_DEPRECATED = formatter<T>;
enum { builtin_types = FMT_BUILTIN_TYPES };
/// Constructs a `context` object. References to the arguments are stored
/// in the object so make sure they have appropriate lifetimes.
FMT_CONSTEXPR context(iterator out, format_args args,
detail::locale_ref loc = {})
FMT_CONSTEXPR context(iterator out, format_args args, locale_ref loc = {})
: out_(out), args_(args), loc_(loc) {}
context(context&&) = default;
context(const context&) = delete;
@@ -2692,7 +2700,7 @@ class context {
// Advances the begin iterator to `it`.
FMT_CONSTEXPR void advance_to(iterator) {}
FMT_CONSTEXPR auto locale() const -> detail::locale_ref { return loc_; }
FMT_CONSTEXPR auto locale() const -> locale_ref { return loc_; }
};
template <typename Char = char> struct runtime_format_string {
@@ -2779,9 +2787,6 @@ template <typename T, typename Char = char>
concept formattable = is_formattable<remove_reference_t<T>, Char>::value;
#endif
template <typename T, typename Char>
using has_formatter FMT_DEPRECATED = std::is_constructible<formatter<T, Char>>;
// A formatter specialization for natively supported types.
template <typename T, typename Char>
struct formatter<T, Char,
@@ -2978,9 +2983,9 @@ FMT_INLINE void println(format_string<T...> fmt, T&&... args) {
return fmt::println(stdout, fmt, static_cast<T&&>(args)...);
}
FMT_END_EXPORT
FMT_PRAGMA_CLANG(diagnostic pop)
FMT_PRAGMA_GCC(pop_options)
FMT_END_EXPORT
FMT_END_NAMESPACE
#ifdef FMT_HEADER_ONLY

View File

@@ -38,6 +38,7 @@ FMT_BEGIN_NAMESPACE
// Copyright Paul Dreik 2019
namespace safe_duration_cast {
// DEPRECATED!
template <typename To, typename From,
FMT_ENABLE_IF(!std::is_same<From, To>::value &&
std::numeric_limits<From>::is_signed ==
@@ -161,17 +162,6 @@ auto safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
int& ec) -> To {
using From = std::chrono::duration<FromRep, FromPeriod>;
ec = 0;
if (std::isnan(from.count())) {
// nan in, gives nan out. easy.
return To{std::numeric_limits<typename To::rep>::quiet_NaN()};
}
// maybe we should also check if from is denormal, and decide what to do about
// it.
// +-inf should be preserved.
if (std::isinf(from.count())) {
return To{from.count()};
}
// the basic idea is that we need to convert from count() in the from type
// to count() in the To type, by multiplying it with this:
@@ -282,8 +272,6 @@ namespace detail {
#define FMT_NOMACRO
template <typename T = void> struct null {};
inline auto localtime_r FMT_NOMACRO(...) -> null<> { return null<>(); }
inline auto localtime_s(...) -> null<> { return null<>(); }
inline auto gmtime_r(...) -> null<> { return null<>(); }
inline auto gmtime_s(...) -> null<> { return null<>(); }
@@ -326,7 +314,7 @@ inline auto get_classic_locale() -> const std::locale& {
}
template <typename CodeUnit> struct codecvt_result {
static constexpr const size_t max_size = 32;
static constexpr size_t max_size = 32;
CodeUnit buf[max_size];
CodeUnit* end;
};
@@ -443,11 +431,7 @@ auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
using common_rep = typename std::common_type<FromRep, typename To::rep,
decltype(factor::num)>::type;
int ec = 0;
auto count = safe_duration_cast::lossless_integral_conversion<common_rep>(
from.count(), ec);
if (ec) throw_duration_error();
common_rep count = from.count(); // This conversion is lossless.
// Multiply from.count() by factor and check for overflow.
if (const_check(factor::num != 1)) {
@@ -458,6 +442,7 @@ auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
count *= factor::num;
}
if (const_check(factor::den != 1)) count /= factor::den;
int ec = 0;
auto to =
To(safe_duration_cast::lossless_integral_conversion<typename To::rep>(
count, ec));
@@ -471,6 +456,8 @@ template <typename To, typename FromRep, typename FromPeriod,
std::is_floating_point<typename To::rep>::value)>
auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
#if FMT_SAFE_DURATION_CAST
// Preserve infinity and NaN.
if (!isfinite(from.count())) return static_cast<To>(from.count());
// Throwing version of safe_duration_cast is only available for
// integer to integer or float to float casts.
int ec;
@@ -487,7 +474,7 @@ template <typename To, typename FromRep, typename FromPeriod,
FMT_ENABLE_IF(
!is_similar_arithmetic_type<FromRep, typename To::rep>::value)>
auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
// Mixed integer <-> float cast is not supported by safe_duration_cast.
// Mixed integer <-> float cast is not supported by safe duration_cast.
return std::chrono::duration_cast<To>(from);
}
@@ -501,86 +488,10 @@ auto to_time_t(sys_time<Duration> time_point) -> std::time_t {
.count();
}
namespace tz {
// DEPRECATED!
struct time_zone {
template <typename Duration, typename LocalTime>
auto to_sys(LocalTime) -> sys_time<Duration> {
return {};
}
};
template <typename... T> auto current_zone(T...) -> time_zone* {
return nullptr;
}
template <typename... T> void _tzset(T...) {}
} // namespace tz
// DEPRECATED!
inline void tzset_once() {
static bool init = []() {
using namespace tz;
_tzset();
return false;
}();
ignore_unused(init);
}
} // namespace detail
FMT_BEGIN_EXPORT
/**
* Converts given time since epoch as `std::time_t` value into calendar time,
* expressed in local time. Unlike `std::localtime`, this function is
* thread-safe on most platforms.
*/
FMT_DEPRECATED inline auto localtime(std::time_t time) -> std::tm {
struct dispatcher {
std::time_t time_;
std::tm tm_;
inline dispatcher(std::time_t t) : time_(t) {}
inline auto run() -> bool {
using namespace fmt::detail;
return handle(localtime_r(&time_, &tm_));
}
inline auto handle(std::tm* tm) -> bool { return tm != nullptr; }
inline auto handle(detail::null<>) -> bool {
using namespace fmt::detail;
return fallback(localtime_s(&tm_, &time_));
}
inline auto fallback(int res) -> bool { return res == 0; }
#if !FMT_MSC_VERSION
inline auto fallback(detail::null<>) -> bool {
using namespace fmt::detail;
std::tm* tm = std::localtime(&time_);
if (tm) tm_ = *tm;
return tm != nullptr;
}
#endif
};
dispatcher lt(time);
// Too big time values may be unsupported.
if (!lt.run()) FMT_THROW(format_error("time_t value out of range"));
return lt.tm_;
}
#if FMT_USE_LOCAL_TIME
template <typename Duration>
FMT_DEPRECATED auto localtime(std::chrono::local_time<Duration> time)
-> std::tm {
using namespace std::chrono;
using namespace detail::tz;
return localtime(detail::to_time_t(current_zone()->to_sys<Duration>(time)));
}
#endif
/**
* Converts given time since epoch as `std::time_t` value into calendar time,
* expressed in Coordinated Universal Time (UTC). Unlike `std::gmtime`, this
@@ -652,7 +563,7 @@ inline void write_digit2_separated(char* buf, unsigned a, unsigned b,
// Add ASCII '0' to each digit byte and insert separators.
digits |= 0x3030003030003030 | (usep << 16) | (usep << 40);
constexpr const size_t len = 8;
constexpr size_t len = 8;
if (const_check(is_big_endian())) {
char tmp[len];
std::memcpy(tmp, &digits, len);
@@ -1000,16 +911,16 @@ template <typename T>
struct has_tm_zone<T, void_t<decltype(T::tm_zone)>> : std::true_type {};
template <typename T, FMT_ENABLE_IF(has_tm_zone<T>::value)>
bool set_tm_zone(T& time, char* tz) {
auto set_tm_zone(T& time, char* tz) -> bool {
time.tm_zone = tz;
return true;
}
template <typename T, FMT_ENABLE_IF(!has_tm_zone<T>::value)>
bool set_tm_zone(T&, char*) {
auto set_tm_zone(T&, char*) -> bool {
return false;
}
inline char* utc() {
inline auto utc() -> char* {
static char tz[] = "UTC";
return tz;
}
@@ -2230,7 +2141,7 @@ template <typename Char> struct formatter<std::tm, Char> {
detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_,
ctx);
auto loc_ref = specs.localized() ? ctx.locale() : detail::locale_ref();
auto loc_ref = specs.localized() ? ctx.locale() : locale_ref();
detail::get_locale loc(static_cast<bool>(loc_ref), loc_ref);
auto w = detail::tm_writer<basic_appender<Char>, Char, Duration>(
loc, out, tm, subsecs);

View File

@@ -375,19 +375,17 @@ template <typename Char> struct ansi_color_escape {
// 10 more.
if (is_background) value += 10u;
size_t index = 0;
buffer[index++] = static_cast<Char>('\x1b');
buffer[index++] = static_cast<Char>('[');
buffer[size++] = static_cast<Char>('\x1b');
buffer[size++] = static_cast<Char>('[');
if (value >= 100u) {
buffer[index++] = static_cast<Char>('1');
buffer[size++] = static_cast<Char>('1');
value %= 100u;
}
buffer[index++] = static_cast<Char>('0' + value / 10u);
buffer[index++] = static_cast<Char>('0' + value % 10u);
buffer[size++] = static_cast<Char>('0' + value / 10u);
buffer[size++] = static_cast<Char>('0' + value % 10u);
buffer[index++] = static_cast<Char>('m');
buffer[index++] = static_cast<Char>('\0');
buffer[size++] = static_cast<Char>('m');
return;
}
@@ -398,7 +396,7 @@ template <typename Char> struct ansi_color_escape {
to_esc(color.r, buffer + 7, ';');
to_esc(color.g, buffer + 11, ';');
to_esc(color.b, buffer + 15, 'm');
buffer[19] = static_cast<Char>(0);
size = 19;
}
FMT_CONSTEXPR ansi_color_escape(emphasis em) noexcept {
uint8_t em_codes[num_emphases] = {};
@@ -411,26 +409,28 @@ template <typename Char> struct ansi_color_escape {
if (has_emphasis(em, emphasis::conceal)) em_codes[6] = 8;
if (has_emphasis(em, emphasis::strikethrough)) em_codes[7] = 9;
size_t index = 0;
buffer[size++] = static_cast<Char>('\x1b');
buffer[size++] = static_cast<Char>('[');
for (size_t i = 0; i < num_emphases; ++i) {
if (!em_codes[i]) continue;
buffer[index++] = static_cast<Char>('\x1b');
buffer[index++] = static_cast<Char>('[');
buffer[index++] = static_cast<Char>('0' + em_codes[i]);
buffer[index++] = static_cast<Char>('m');
buffer[size++] = static_cast<Char>('0' + em_codes[i]);
buffer[size++] = static_cast<Char>(';');
}
buffer[index++] = static_cast<Char>(0);
buffer[size - 1] = static_cast<Char>('m');
}
FMT_CONSTEXPR operator const Char*() const noexcept { return buffer; }
FMT_CONSTEXPR auto begin() const noexcept -> const Char* { return buffer; }
FMT_CONSTEXPR20 auto end() const noexcept -> const Char* {
return buffer + basic_string_view<Char>(buffer).size();
FMT_CONSTEXPR auto end() const noexcept -> const Char* {
return buffer + size;
}
private:
static constexpr size_t num_emphases = 8;
Char buffer[7u + 3u * num_emphases + 1u];
Char buffer[7u + 4u * num_emphases];
size_t size = 0;
static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out,
char delimiter) noexcept {

View File

@@ -22,8 +22,6 @@ FMT_EXPORT class compiled_string {};
template <typename S>
struct is_compiled_string : std::is_base_of<compiled_string, S> {};
namespace detail {
/**
* Converts a string literal `s` into a format string that will be parsed at
* compile time and converted into efficient formatting code. Requires C++17
@@ -41,18 +39,40 @@ namespace detail {
# define FMT_COMPILE(s) FMT_STRING(s)
#endif
/**
* Converts a string literal into a format string that will be parsed at
* compile time and converted into efficient formatting code. Requires support
* for class types in constant template parameters (a C++20 feature).
*
* **Example**:
*
* // Converts 42 into std::string using the most efficient method and no
* // runtime format string processing.
* using namespace fmt::literals;
* std::string s = fmt::format("{}"_cf, 42);
*/
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
inline namespace literals {
template <detail::fixed_string Str> constexpr auto operator""_cf() {
return FMT_COMPILE(Str.data);
}
} // namespace literals
#endif
namespace detail {
template <typename T, typename... Tail>
auto first(const T& value, const Tail&...) -> const T& {
constexpr auto first(const T& value, const Tail&...) -> const T& {
return value;
}
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
template <typename... Args> struct type_list {};
template <typename... T> struct type_list {};
// Returns a reference to the argument at index N from [first, rest...].
template <int N, typename T, typename... Args>
constexpr const auto& get([[maybe_unused]] const T& first,
[[maybe_unused]] const Args&... rest) {
constexpr auto get([[maybe_unused]] const T& first,
[[maybe_unused]] const Args&... rest) -> const auto& {
static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
if constexpr (N == 0)
return first;
@@ -84,8 +104,8 @@ FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
}
template <typename Char, typename... Args>
constexpr int get_arg_index_by_name(basic_string_view<Char> name,
type_list<Args...>) {
constexpr auto get_arg_index_by_name(basic_string_view<Char> name,
type_list<Args...>) -> int {
return get_arg_index_by_name<Args...>(name);
}
@@ -105,8 +125,8 @@ template <typename Char> struct text {
basic_string_view<Char> data;
using char_type = Char;
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&...) const {
template <typename OutputIt, typename... T>
constexpr auto format(OutputIt out, const T&...) const -> OutputIt {
return write<Char>(out, data);
}
};
@@ -115,8 +135,8 @@ template <typename Char>
struct is_compiled_format<text<Char>> : std::true_type {};
template <typename Char>
constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
size_t size) {
constexpr auto make_text(basic_string_view<Char> s, size_t pos, size_t size)
-> text<Char> {
return {{&s[pos], size}};
}
@@ -124,8 +144,8 @@ template <typename Char> struct code_unit {
Char value;
using char_type = Char;
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&...) const {
template <typename OutputIt, typename... T>
constexpr auto format(OutputIt out, const T&...) const -> OutputIt {
*out++ = value;
return out;
}
@@ -133,7 +153,7 @@ template <typename Char> struct code_unit {
// This ensures that the argument type is convertible to `const T&`.
template <typename T, int N, typename... Args>
constexpr const T& get_arg_checked(const Args&... args) {
constexpr auto get_arg_checked(const Args&... args) -> const T& {
const auto& arg = detail::get<N>(args...);
if constexpr (detail::is_named_arg<remove_cvref_t<decltype(arg)>>()) {
return arg.value;
@@ -146,13 +166,13 @@ template <typename Char>
struct is_compiled_format<code_unit<Char>> : std::true_type {};
// A replacement field that refers to argument N.
template <typename Char, typename T, int N> struct field {
template <typename Char, typename V, int N> struct field {
using char_type = Char;
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&... args) const {
const T& arg = get_arg_checked<T, N>(args...);
if constexpr (std::is_convertible<T, basic_string_view<Char>>::value) {
template <typename OutputIt, typename... T>
constexpr auto format(OutputIt out, const T&... args) const -> OutputIt {
const V& arg = get_arg_checked<V, N>(args...);
if constexpr (std::is_convertible<V, basic_string_view<Char>>::value) {
auto s = basic_string_view<Char>(arg);
return copy<Char>(s.begin(), s.end(), out);
} else {
@@ -170,10 +190,10 @@ template <typename Char> struct runtime_named_field {
basic_string_view<Char> name;
template <typename OutputIt, typename T>
constexpr static bool try_format_argument(
constexpr static auto try_format_argument(
OutputIt& out,
// [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9
[[maybe_unused]] basic_string_view<Char> arg_name, const T& arg) {
[[maybe_unused]] basic_string_view<Char> arg_name, const T& arg) -> bool {
if constexpr (is_named_arg<typename std::remove_cv<T>::type>::value) {
if (arg_name == arg.name) {
out = write<Char>(out, arg.value);
@@ -183,8 +203,8 @@ template <typename Char> struct runtime_named_field {
return false;
}
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&... args) const {
template <typename OutputIt, typename... T>
constexpr auto format(OutputIt out, const T&... args) const -> OutputIt {
bool found = (try_format_argument(out, name, args) || ...);
if (!found) {
FMT_THROW(format_error("argument with specified name is not found"));
@@ -197,17 +217,17 @@ template <typename Char>
struct is_compiled_format<runtime_named_field<Char>> : std::true_type {};
// A replacement field that refers to argument N and has format specifiers.
template <typename Char, typename T, int N> struct spec_field {
template <typename Char, typename V, int N> struct spec_field {
using char_type = Char;
formatter<T, Char> fmt;
formatter<V, Char> fmt;
template <typename OutputIt, typename... Args>
constexpr FMT_INLINE OutputIt format(OutputIt out,
const Args&... args) const {
template <typename OutputIt, typename... T>
constexpr FMT_INLINE auto format(OutputIt out, const T&... args) const
-> OutputIt {
const auto& vargs =
fmt::make_format_args<basic_format_context<OutputIt, Char>>(args...);
basic_format_context<OutputIt, Char> ctx(out, vargs);
return fmt.format(get_arg_checked<T, N>(args...), ctx);
return fmt.format(get_arg_checked<V, N>(args...), ctx);
}
};
@@ -219,8 +239,8 @@ template <typename L, typename R> struct concat {
R rhs;
using char_type = typename L::char_type;
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&... args) const {
template <typename OutputIt, typename... T>
constexpr auto format(OutputIt out, const T&... args) const -> OutputIt {
out = lhs.format(out, args...);
return rhs.format(out, args...);
}
@@ -230,14 +250,14 @@ template <typename L, typename R>
struct is_compiled_format<concat<L, R>> : std::true_type {};
template <typename L, typename R>
constexpr concat<L, R> make_concat(L lhs, R rhs) {
constexpr auto make_concat(L lhs, R rhs) -> concat<L, R> {
return {lhs, rhs};
}
struct unknown_format {};
template <typename Char>
constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) {
constexpr auto parse_text(basic_string_view<Char> str, size_t pos) -> size_t {
for (size_t size = str.size(); pos != size; ++pos) {
if (str[pos] == '{' || str[pos] == '}') break;
}
@@ -270,8 +290,8 @@ template <typename T, typename Char> struct parse_specs_result {
enum { manual_indexing_id = -1 };
template <typename T, typename Char>
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
size_t pos, int next_arg_id) {
constexpr auto parse_specs(basic_string_view<Char> str, size_t pos,
int next_arg_id) -> parse_specs_result<T, Char> {
str.remove_prefix(pos);
auto ctx =
compile_parse_context<Char>(str, max_value<int>(), nullptr, next_arg_id);
@@ -285,16 +305,16 @@ template <typename Char> struct arg_id_handler {
arg_id_kind kind;
arg_ref<Char> arg_id;
constexpr int on_auto() {
constexpr auto on_auto() -> int {
FMT_ASSERT(false, "handler cannot be used with automatic indexing");
return 0;
}
constexpr int on_index(int id) {
constexpr auto on_index(int id) -> int {
kind = arg_id_kind::index;
arg_id = arg_ref<Char>(id);
return 0;
}
constexpr int on_name(basic_string_view<Char> id) {
constexpr auto on_name(basic_string_view<Char> id) -> int {
kind = arg_id_kind::name;
arg_id = arg_ref<Char>(id);
return 0;
@@ -433,27 +453,28 @@ FMT_BEGIN_EXPORT
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
template <typename CompiledFormat, typename... Args,
template <typename CompiledFormat, typename... T,
typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
const Args&... args) {
FMT_INLINE FMT_CONSTEXPR_STRING auto format(const CompiledFormat& cf,
const T&... args)
-> std::basic_string<Char> {
auto s = std::basic_string<Char>();
cf.format(std::back_inserter(s), args...);
return s;
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
template <typename OutputIt, typename CompiledFormat, typename... T,
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) {
constexpr FMT_INLINE auto format_to(OutputIt out, const CompiledFormat& cf,
const T&... args) -> OutputIt {
return cf.format(out, args...);
}
template <typename S, typename... Args,
template <typename S, typename... T,
FMT_ENABLE_IF(is_compiled_string<S>::value)>
FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
Args&&... args) {
FMT_INLINE FMT_CONSTEXPR_STRING auto format(const S&, T&&... args)
-> std::basic_string<typename S::char_type> {
if constexpr (std::is_same<typename S::char_type, char>::value) {
constexpr auto str = basic_string_view<typename S::char_type>(S());
if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') {
@@ -466,72 +487,97 @@ FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
}
}
}
constexpr auto compiled = detail::compile<Args...>(S());
constexpr auto compiled = detail::compile<T...>(S());
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
detail::unknown_format>()) {
return fmt::format(
static_cast<basic_string_view<typename S::char_type>>(S()),
std::forward<Args>(args)...);
std::forward<T>(args)...);
} else {
return fmt::format(compiled, std::forward<Args>(args)...);
return fmt::format(compiled, std::forward<T>(args)...);
}
}
template <typename OutputIt, typename S, typename... Args,
template <typename OutputIt, typename S, typename... T,
FMT_ENABLE_IF(is_compiled_string<S>::value)>
FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
constexpr auto compiled = detail::compile<Args...>(S());
FMT_CONSTEXPR auto format_to(OutputIt out, const S&, T&&... args) -> OutputIt {
constexpr auto compiled = detail::compile<T...>(S());
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
detail::unknown_format>()) {
return fmt::format_to(
out, static_cast<basic_string_view<typename S::char_type>>(S()),
std::forward<Args>(args)...);
std::forward<T>(args)...);
} else {
return fmt::format_to(out, compiled, std::forward<Args>(args)...);
return fmt::format_to(out, compiled, std::forward<T>(args)...);
}
}
#endif
template <typename OutputIt, typename S, typename... Args,
template <typename OutputIt, typename S, typename... T,
FMT_ENABLE_IF(is_compiled_string<S>::value)>
auto format_to_n(OutputIt out, size_t n, const S& fmt, Args&&... args)
auto format_to_n(OutputIt out, size_t n, const S& fmt, T&&... args)
-> format_to_n_result<OutputIt> {
using traits = detail::fixed_buffer_traits;
auto buf = detail::iterator_buffer<OutputIt, char, traits>(out, n);
fmt::format_to(std::back_inserter(buf), fmt, std::forward<Args>(args)...);
fmt::format_to(std::back_inserter(buf), fmt, std::forward<T>(args)...);
return {buf.out(), buf.count()};
}
template <typename S, typename... Args,
template <typename S, typename... T,
FMT_ENABLE_IF(is_compiled_string<S>::value)>
FMT_CONSTEXPR20 auto formatted_size(const S& fmt, const Args&... args)
-> size_t {
FMT_CONSTEXPR20 auto formatted_size(const S& fmt, T&&... args) -> size_t {
auto buf = detail::counting_buffer<>();
fmt::format_to(appender(buf), fmt, args...);
fmt::format_to(appender(buf), fmt, std::forward<T>(args)...);
return buf.count();
}
template <typename S, typename... Args,
template <typename S, typename... T,
FMT_ENABLE_IF(is_compiled_string<S>::value)>
void print(std::FILE* f, const S& fmt, const Args&... args) {
void print(std::FILE* f, const S& fmt, T&&... args) {
auto buf = memory_buffer();
fmt::format_to(appender(buf), fmt, args...);
fmt::format_to(appender(buf), fmt, std::forward<T>(args)...);
detail::print(f, {buf.data(), buf.size()});
}
template <typename S, typename... Args,
template <typename S, typename... T,
FMT_ENABLE_IF(is_compiled_string<S>::value)>
void print(const S& fmt, const Args&... args) {
print(stdout, fmt, args...);
void print(const S& fmt, T&&... args) {
print(stdout, fmt, std::forward<T>(args)...);
}
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
inline namespace literals {
template <detail::fixed_string Str> constexpr auto operator""_cf() {
return FMT_COMPILE(Str.data);
}
} // namespace literals
#endif
template <size_t N> class static_format_result {
private:
char data[N];
public:
template <typename S, typename... T,
FMT_ENABLE_IF(is_compiled_string<S>::value)>
explicit FMT_CONSTEXPR static_format_result(const S& fmt, T&&... args) {
*fmt::format_to(data, fmt, std::forward<T>(args)...) = '\0';
}
auto str() const -> fmt::string_view { return {data, N - 1}; }
auto c_str() const -> const char* { return data; }
};
/**
* Formats arguments according to the format string `fmt_str` and produces
* a string of the exact required size at compile time. Both the format string
* and the arguments must be compile-time expressions.
*
* The resulting string can be accessed as a C string via `c_str()` or as
* a `fmt::string_view` via `str()`.
*
* **Example**:
*
* // Produces the static string "42" at compile time.
* static constexpr auto result = FMT_STATIC_FORMAT("{}", 42);
* const char* s = result.c_str();
*/
#define FMT_STATIC_FORMAT(fmt_str, ...) \
fmt::static_format_result< \
fmt::formatted_size(FMT_COMPILE(fmt_str), __VA_ARGS__) + 1>( \
FMT_COMPILE(fmt_str), __VA_ARGS__)
FMT_END_EXPORT
FMT_END_NAMESPACE

View File

@@ -22,7 +22,7 @@
#include "format.h"
#if FMT_USE_LOCALE
#if FMT_USE_LOCALE && !defined(FMT_MODULE)
# include <locale>
#endif
@@ -31,14 +31,49 @@
#endif
FMT_BEGIN_NAMESPACE
namespace detail {
#ifndef FMT_CUSTOM_ASSERT_FAIL
FMT_FUNC void assert_fail(const char* file, int line, const char* message) {
// Use unchecked std::fprintf to avoid triggering another assertion when
// writing to stderr fails.
fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message);
abort();
}
#endif
#if FMT_USE_LOCALE
namespace detail {
using std::locale;
using std::numpunct;
using std::use_facet;
} // namespace detail
template <typename Locale, enable_if_t<(sizeof(Locale::collate) != 0), int>>
locale_ref::locale_ref(const Locale& loc) : locale_(&loc) {
static_assert(std::is_same<Locale, std::locale>::value, "");
}
#else
namespace detail {
struct locale {};
template <typename Char> struct numpunct {
auto grouping() const -> std::string { return "\03"; }
auto thousands_sep() const -> Char { return ','; }
auto decimal_point() const -> Char { return '.'; }
};
template <typename Facet> Facet use_facet(locale) { return {}; }
} // namespace detail
#endif // FMT_USE_LOCALE
template <typename Locale> auto locale_ref::get() const -> Locale {
using namespace detail;
static_assert(std::is_same<Locale, locale>::value, "");
#if FMT_USE_LOCALE
if (locale_) return *static_cast<const locale*>(locale_);
#endif
return locale();
}
namespace detail {
FMT_FUNC void format_error_code(detail::buffer<char>& out, int error_code,
string_view message) noexcept {
@@ -79,33 +114,6 @@ inline void fwrite_all(const void* ptr, size_t count, FILE* stream) {
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
}
#if FMT_USE_LOCALE
using std::locale;
using std::numpunct;
using std::use_facet;
template <typename Locale>
locale_ref::locale_ref(const Locale& loc) : locale_(&loc) {
static_assert(std::is_same<Locale, locale>::value, "");
}
#else
struct locale {};
template <typename Char> struct numpunct {
auto grouping() const -> std::string { return "\03"; }
auto thousands_sep() const -> Char { return ','; }
auto decimal_point() const -> Char { return '.'; }
};
template <typename Facet> Facet use_facet(locale) { return {}; }
#endif // FMT_USE_LOCALE
template <typename Locale> auto locale_ref::get() const -> Locale {
static_assert(std::is_same<Locale, locale>::value, "");
#if FMT_USE_LOCALE
if (locale_) return *static_cast<const locale*>(locale_);
#endif
return locale();
}
template <typename Char>
FMT_FUNC auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result<Char> {
auto&& facet = use_facet<numpunct<Char>>(loc.get<locale>());
@@ -133,14 +141,13 @@ FMT_FUNC auto write_loc(appender out, loc_value value,
} // namespace detail
FMT_FUNC void report_error(const char* message) {
#if FMT_USE_EXCEPTIONS
// Use FMT_THROW instead of throw to avoid bogus unreachable code warnings
// from MSVC.
FMT_THROW(format_error(message));
#else
fputs(message, stderr);
abort();
#if FMT_MSC_VERSION || defined(__NVCC__)
// Silence unreachable code warnings in MSVC and NVCC because these
// are nearly impossible to fix in a generic code.
volatile bool b = true;
if (!b) return;
#endif
FMT_THROW(format_error(message));
}
template <typename Locale> typename Locale::id format_facet<Locale>::id;
@@ -174,11 +181,11 @@ inline auto operator==(basic_fp<F> x, basic_fp<F> y) -> bool {
}
// Compilers should be able to optimize this into the ror instruction.
FMT_CONSTEXPR inline auto rotr(uint32_t n, uint32_t r) noexcept -> uint32_t {
FMT_INLINE auto rotr(uint32_t n, uint32_t r) noexcept -> uint32_t {
r &= 31;
return (n >> r) | (n << (32 - r));
}
FMT_CONSTEXPR inline auto rotr(uint64_t n, uint32_t r) noexcept -> uint64_t {
FMT_INLINE auto rotr(uint64_t n, uint32_t r) noexcept -> uint64_t {
r &= 63;
return (n >> r) | (n << (64 - r));
}
@@ -275,7 +282,7 @@ template <> struct cache_accessor<float> {
static auto get_cached_power(int k) noexcept -> uint64_t {
FMT_ASSERT(k >= float_info<float>::min_k && k <= float_info<float>::max_k,
"k is out of range");
static constexpr const uint64_t pow10_significands[] = {
static constexpr uint64_t pow10_significands[] = {
0x81ceb32c4b43fcf5, 0xa2425ff75e14fc32, 0xcad2f7f5359a3b3f,
0xfd87b5f28300ca0e, 0x9e74d1b791e07e49, 0xc612062576589ddb,
0xf79687aed3eec552, 0x9abe14cd44753b53, 0xc16d9a0095928a28,
@@ -370,7 +377,7 @@ template <> struct cache_accessor<double> {
FMT_ASSERT(k >= float_info<double>::min_k && k <= float_info<double>::max_k,
"k is out of range");
static constexpr const uint128_fallback pow10_significands[] = {
static constexpr uint128_fallback pow10_significands[] = {
#if FMT_USE_FULL_CACHE_DRAGONBOX
{0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b},
{0x9faacf3df73609b1, 0x77b191618c54e9ad},
@@ -1037,7 +1044,7 @@ template <> struct cache_accessor<double> {
#if FMT_USE_FULL_CACHE_DRAGONBOX
return pow10_significands[k - float_info<double>::min_k];
#else
static constexpr const uint64_t powers_of_5_64[] = {
static constexpr uint64_t powers_of_5_64[] = {
0x0000000000000001, 0x0000000000000005, 0x0000000000000019,
0x000000000000007d, 0x0000000000000271, 0x0000000000000c35,
0x0000000000003d09, 0x000000000001312d, 0x000000000005f5e1,
@@ -1149,8 +1156,8 @@ auto is_left_endpoint_integer_shorter_interval(int exponent) noexcept -> bool {
exponent <= case_shorter_interval_left_endpoint_upper_threshold;
}
// Remove trailing zeros from n and return the number of zeros removed (float)
FMT_INLINE int remove_trailing_zeros(uint32_t& n, int s = 0) noexcept {
// Remove trailing zeros from n and return the number of zeros removed (float).
FMT_INLINE auto remove_trailing_zeros(uint32_t& n, int s = 0) noexcept -> int {
FMT_ASSERT(n != 0, "");
// Modular inverse of 5 (mod 2^32): (mod_inv_5 * 5) mod 2^32 = 1.
constexpr uint32_t mod_inv_5 = 0xcccccccd;
@@ -1170,22 +1177,19 @@ FMT_INLINE int remove_trailing_zeros(uint32_t& n, int s = 0) noexcept {
return s;
}
// Removes trailing zeros and returns the number of zeros removed (double)
FMT_INLINE int remove_trailing_zeros(uint64_t& n) noexcept {
// Removes trailing zeros and returns the number of zeros removed (double).
FMT_INLINE auto remove_trailing_zeros(uint64_t& n) noexcept -> int {
FMT_ASSERT(n != 0, "");
// This magic number is ceil(2^90 / 10^8).
constexpr uint64_t magic_number = 12379400392853802749ull;
auto nm = umul128(n, magic_number);
// Is n is divisible by 10^8?
if ((nm.high() & ((1ull << (90 - 64)) - 1)) == 0 && nm.low() < magic_number) {
constexpr uint32_t ten_pow_8 = 100000000u;
if ((n % ten_pow_8) == 0) {
// If yes, work with the quotient...
auto n32 = static_cast<uint32_t>(nm.high() >> (90 - 64));
auto n32 = static_cast<uint32_t>(n / ten_pow_8);
// ... and use the 32 bit variant of the function
int s = remove_trailing_zeros(n32, 8);
int num_zeros = remove_trailing_zeros(n32, 8);
n = n32;
return s;
return num_zeros;
}
// If n is not divisible by 10^8, work with n itself.
@@ -1210,7 +1214,7 @@ FMT_INLINE int remove_trailing_zeros(uint64_t& n) noexcept {
// The main algorithm for shorter interval case
template <typename T>
FMT_INLINE decimal_fp<T> shorter_interval_case(int exponent) noexcept {
FMT_INLINE auto shorter_interval_case(int exponent) noexcept -> decimal_fp<T> {
decimal_fp<T> ret_value;
// Compute k and beta
const int minus_k = floor_log10_pow2_minus_log10_4_over_3(exponent);
@@ -1454,8 +1458,8 @@ FMT_FUNC void vformat_to(buffer<char>& buf, string_view fmt, format_args args,
auto out = appender(buf);
if (fmt.size() == 2 && equal2(fmt.data(), "{}"))
return args.get(0).visit(default_arg_formatter<char>{out});
parse_format_string(
fmt, format_handler<char>{parse_context<char>(fmt), {out, args, loc}});
parse_format_string(fmt,
format_handler<>{parse_context<>(fmt), {out, args, loc}});
}
template <typename T> struct span {
@@ -1546,10 +1550,11 @@ template <typename F> class glibc_file : public file_base<F> {
void advance_write_buffer(size_t size) { this->file_->_IO_write_ptr += size; }
bool needs_flush() const {
auto needs_flush() const -> bool {
if ((this->file_->_flags & line_buffered) == 0) return false;
char* end = this->file_->_IO_write_end;
return memchr(end, '\n', to_unsigned(this->file_->_IO_write_ptr - end));
auto size = max_of<ptrdiff_t>(this->file_->_IO_write_ptr - end, 0);
return memchr(end, '\n', static_cast<size_t>(size));
}
void flush() { fflush_unlocked(this->file_); }
@@ -1573,7 +1578,7 @@ template <typename F> class apple_file : public file_base<F> {
void init_buffer() {
if (this->file_->_p) return;
// Force buffer initialization by placing and removing a char in a buffer.
putc_unlocked(0, this->file_);
if (!FMT_CLANG_ANALYZER) putc_unlocked(0, this->file_);
--this->file_->_p;
++this->file_->_w;
}
@@ -1594,7 +1599,7 @@ template <typename F> class apple_file : public file_base<F> {
this->file_->_w -= size;
}
bool needs_flush() const {
auto needs_flush() const -> bool {
if ((this->file_->_flags & line_buffered) == 0) return false;
return memchr(this->file_->_p + this->file_->_w, '\n',
to_unsigned(-this->file_->_w));

File diff suppressed because it is too large Load Diff

View File

@@ -29,7 +29,8 @@
# if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \
defined(__linux__)) && \
(!defined(WINAPI_FAMILY) || \
(WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
(WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) && \
!defined(__wasm__)
# include <fcntl.h> // for O_RDONLY
# define FMT_USE_FCNTL 1
# else

View File

@@ -33,8 +33,8 @@
FMT_BEGIN_NAMESPACE
namespace detail {
// Generate a unique explicit instantion in every translation unit using a tag
// type in an anonymous namespace.
// Generate a unique explicit instantiation in every translation unit using a
// tag type in an anonymous namespace.
namespace {
struct file_access_tag {};
} // namespace

View File

@@ -9,7 +9,7 @@
#define FMT_PRINTF_H_
#ifndef FMT_MODULE
# include <algorithm> // std::max
# include <algorithm> // std::find
# include <limits> // std::numeric_limits
#endif
@@ -18,10 +18,6 @@
FMT_BEGIN_NAMESPACE
FMT_BEGIN_EXPORT
template <typename T> struct printf_formatter {
printf_formatter() = delete;
};
template <typename Char> class basic_printf_context {
private:
basic_appender<Char> out_;
@@ -33,8 +29,6 @@ template <typename Char> class basic_printf_context {
public:
using char_type = Char;
using parse_context_type = parse_context<Char>;
template <typename T> using formatter_type = printf_formatter<T>;
enum { builtin_types = 1 };
/// Constructs a `printf_context` object. References to the arguments are
@@ -46,7 +40,7 @@ template <typename Char> class basic_printf_context {
auto out() -> basic_appender<Char> { return out_; }
void advance_to(basic_appender<Char>) {}
auto locale() -> detail::locale_ref { return {}; }
auto locale() -> locale_ref { return {}; }
auto arg(int id) const -> basic_format_arg<basic_printf_context> {
return args_.get(id);
@@ -74,10 +68,9 @@ inline auto find<false, char>(const char* first, const char* last, char value,
// Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers.
template <bool IsSigned> struct int_checker {
template <bool IS_SIGNED> struct int_checker {
template <typename T> static auto fits_in_int(T value) -> bool {
unsigned max = to_unsigned(max_value<int>());
return value <= max;
return value <= to_unsigned(max_value<int>());
}
inline static auto fits_in_int(bool) -> bool { return true; }
};
@@ -95,7 +88,7 @@ struct printf_precision_handler {
auto operator()(T value) -> int {
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
report_error("number is too big");
return (std::max)(static_cast<int>(value), 0);
return max_of(static_cast<int>(value), 0);
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
@@ -410,7 +403,9 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
arg_index = parse_ctx.next_arg_id();
else
parse_ctx.check_arg_id(--arg_index);
return detail::get_arg(context, arg_index);
auto arg = context.arg(arg_index);
if (!arg) report_error("argument not found");
return arg;
};
const Char* start = parse_ctx.begin();
@@ -571,15 +566,19 @@ inline auto vsprintf(basic_string_view<Char> fmt,
*
* std::string message = fmt::sprintf("The answer is %d", 42);
*/
template <typename S, typename... T, typename Char = detail::char_t<S>>
inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> {
return vsprintf(detail::to_string_view(fmt),
fmt::make_format_args<basic_printf_context<Char>>(args...));
template <typename... T>
inline auto sprintf(string_view fmt, const T&... args) -> std::string {
return vsprintf(fmt, make_printf_args(args...));
}
template <typename... T>
FMT_DEPRECATED auto sprintf(basic_string_view<wchar_t> fmt, const T&... args)
-> std::wstring {
return vsprintf(fmt, make_printf_args<wchar_t>(args...));
}
template <typename Char>
inline auto vfprintf(std::FILE* f, basic_string_view<Char> fmt,
typename vprintf_args<Char>::type args) -> int {
auto vfprintf(std::FILE* f, basic_string_view<Char> fmt,
typename vprintf_args<Char>::type args) -> int {
auto buf = basic_memory_buffer<Char>();
detail::vprintf(buf, fmt, args);
size_t size = buf.size();
@@ -596,17 +595,14 @@ inline auto vfprintf(std::FILE* f, basic_string_view<Char> fmt,
*
* fmt::fprintf(stderr, "Don't %s!", "panic");
*/
template <typename S, typename... T, typename Char = detail::char_t<S>>
inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int {
return vfprintf(f, detail::to_string_view(fmt),
make_printf_args<Char>(args...));
template <typename... T>
inline auto fprintf(std::FILE* f, string_view fmt, const T&... args) -> int {
return vfprintf(f, fmt, make_printf_args(args...));
}
template <typename Char>
FMT_DEPRECATED inline auto vprintf(basic_string_view<Char> fmt,
typename vprintf_args<Char>::type args)
-> int {
return vfprintf(stdout, fmt, args);
template <typename... T>
FMT_DEPRECATED auto fprintf(std::FILE* f, basic_string_view<wchar_t> fmt,
const T&... args) -> int {
return vfprintf(f, fmt, make_printf_args<wchar_t>(args...));
}
/**
@@ -621,11 +617,6 @@ template <typename... T>
inline auto printf(string_view fmt, const T&... args) -> int {
return vfprintf(stdout, fmt, make_printf_args(args...));
}
template <typename... T>
FMT_DEPRECATED inline auto printf(basic_string_view<wchar_t> fmt,
const T&... args) -> int {
return vfprintf(stdout, fmt, make_printf_args<wchar_t>(args...));
}
FMT_END_EXPORT
FMT_END_NAMESPACE

View File

@@ -11,7 +11,6 @@
#ifndef FMT_MODULE
# include <initializer_list>
# include <iterator>
# include <string>
# include <tuple>
# include <type_traits>
# include <utility>
@@ -31,7 +30,7 @@ template <typename T> class is_map {
template <typename> static void check(...);
public:
static constexpr const bool value =
static constexpr bool value =
!std::is_void<decltype(check<T>(nullptr))>::value;
};
@@ -40,17 +39,16 @@ template <typename T> class is_set {
template <typename> static void check(...);
public:
static constexpr const bool value =
static constexpr bool value =
!std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value;
};
// C array overload
template <typename T, std::size_t N>
template <typename T, size_t N>
auto range_begin(const T (&arr)[N]) -> const T* {
return arr;
}
template <typename T, std::size_t N>
auto range_end(const T (&arr)[N]) -> const T* {
template <typename T, size_t N> auto range_end(const T (&arr)[N]) -> const T* {
return arr + N;
}
@@ -120,7 +118,7 @@ template <typename T> class is_tuple_like_ {
template <typename> static void check(...);
public:
static constexpr const bool value =
static constexpr bool value =
!std::is_void<decltype(check<T>(nullptr))>::value;
};
@@ -154,7 +152,7 @@ using tuple_index_sequence = make_index_sequence<std::tuple_size<T>::value>;
template <typename T, typename C, bool = is_tuple_like_<T>::value>
class is_tuple_formattable_ {
public:
static constexpr const bool value = false;
static constexpr bool value = false;
};
template <typename T, typename C> class is_tuple_formattable_<T, C, true> {
template <size_t... Is>
@@ -170,7 +168,7 @@ template <typename T, typename C> class is_tuple_formattable_<T, C, true> {
C>::value)...>{}));
public:
static constexpr const bool value =
static constexpr bool value =
decltype(check(tuple_index_sequence<T>{}))::value;
};
@@ -208,7 +206,7 @@ template <typename Char, typename... T>
using result_t = std::tuple<formatter<remove_cvref_t<T>, Char>...>;
using std::get;
template <typename Tuple, typename Char, std::size_t... Is>
template <typename Tuple, typename Char, size_t... Is>
auto get_formatters(index_sequence<Is...>)
-> result_t<Char, decltype(get<Is>(std::declval<Tuple>()))...>;
} // namespace tuple
@@ -219,7 +217,7 @@ template <typename R> struct range_reference_type_impl {
using type = decltype(*detail::range_begin(std::declval<R&>()));
};
template <typename T, std::size_t N> struct range_reference_type_impl<T[N]> {
template <typename T, size_t N> struct range_reference_type_impl<T[N]> {
using type = T&;
};
@@ -281,14 +279,15 @@ template <typename FormatContext> struct format_tuple_element {
} // namespace detail
FMT_EXPORT
template <typename T> struct is_tuple_like {
static constexpr const bool value =
static constexpr bool value =
detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value;
};
FMT_EXPORT
template <typename T, typename C> struct is_tuple_formattable {
static constexpr const bool value =
detail::is_tuple_formattable_<T, C>::value;
static constexpr bool value = detail::is_tuple_formattable_<T, C>::value;
};
template <typename Tuple, typename Char>
@@ -343,8 +342,9 @@ struct formatter<Tuple, Char,
}
};
FMT_EXPORT
template <typename T, typename Char> struct is_range {
static constexpr const bool value =
static constexpr bool value =
detail::is_range_<T>::value && !detail::has_to_string_view<T>::value;
};
@@ -368,6 +368,7 @@ template <typename P1, typename... Pn>
struct conjunction<P1, Pn...>
: conditional_t<bool(P1::value), conjunction<Pn...>, P1> {};
FMT_EXPORT
template <typename T, typename Char, typename Enable = void>
struct range_formatter;
@@ -670,7 +671,8 @@ struct formatter<join_view<It, Sentinel, Char>, Char> {
}
};
template <typename Char, typename Tuple> struct tuple_join_view : detail::view {
FMT_EXPORT
template <typename Tuple, typename Char> struct tuple_join_view : detail::view {
const Tuple& tuple;
basic_string_view<Char> sep;
@@ -685,15 +687,15 @@ template <typename Char, typename Tuple> struct tuple_join_view : detail::view {
# define FMT_TUPLE_JOIN_SPECIFIERS 0
#endif
template <typename Char, typename Tuple>
struct formatter<tuple_join_view<Char, Tuple>, Char,
template <typename Tuple, typename Char>
struct formatter<tuple_join_view<Tuple, Char>, Char,
enable_if_t<is_tuple_like<Tuple>::value>> {
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return do_parse(ctx, std::tuple_size<Tuple>());
}
template <typename FormatContext>
auto format(const tuple_join_view<Char, Tuple>& value,
auto format(const tuple_join_view<Tuple, Char>& value,
FormatContext& ctx) const -> typename FormatContext::iterator {
return do_format(value, ctx, std::tuple_size<Tuple>());
}
@@ -725,14 +727,14 @@ struct formatter<tuple_join_view<Char, Tuple>, Char,
}
template <typename FormatContext>
auto do_format(const tuple_join_view<Char, Tuple>&, FormatContext& ctx,
auto do_format(const tuple_join_view<Tuple, Char>&, FormatContext& ctx,
std::integral_constant<size_t, 0>) const ->
typename FormatContext::iterator {
return ctx.out();
}
template <typename FormatContext, size_t N>
auto do_format(const tuple_join_view<Char, Tuple>& value, FormatContext& ctx,
auto do_format(const tuple_join_view<Tuple, Char>& value, FormatContext& ctx,
std::integral_constant<size_t, N>) const ->
typename FormatContext::iterator {
using std::get;
@@ -754,7 +756,7 @@ template <typename T> class is_container_adaptor_like {
template <typename> static void check(...);
public:
static constexpr const bool value =
static constexpr bool value =
!std::is_void<decltype(check<T>(nullptr))>::value;
};
@@ -825,7 +827,7 @@ auto join(Range&& r, string_view sep)
*/
template <typename Tuple, FMT_ENABLE_IF(is_tuple_like<Tuple>::value)>
FMT_CONSTEXPR auto join(const Tuple& tuple, string_view sep)
-> tuple_join_view<char, Tuple> {
-> tuple_join_view<Tuple, char> {
return {tuple, sep};
}

View File

@@ -15,15 +15,13 @@
# include <atomic>
# include <bitset>
# include <complex>
# include <cstdlib>
# include <exception>
# include <functional>
# include <functional> // std::reference_wrapper
# include <memory>
# include <thread>
# include <type_traits>
# include <typeinfo>
# include <utility>
# include <vector>
# include <typeinfo> // std::type_info
# include <utility> // std::make_index_sequence
// Check FMT_CPLUSPLUS to suppress a bogus warning in MSVC.
# if FMT_CPLUSPLUS >= 201703L
@@ -62,27 +60,26 @@
# endif
#endif
// For older Xcode versions, __cpp_lib_xxx flags are inaccurately defined.
#ifndef FMT_CPP_LIB_FILESYSTEM
# ifdef __cpp_lib_filesystem
# define FMT_CPP_LIB_FILESYSTEM __cpp_lib_filesystem
# else
# define FMT_CPP_LIB_FILESYSTEM 0
# endif
#ifdef FMT_CPP_LIB_FILESYSTEM
// Use the provided definition.
#elif defined(__cpp_lib_filesystem)
# define FMT_CPP_LIB_FILESYSTEM __cpp_lib_filesystem
#else
# define FMT_CPP_LIB_FILESYSTEM 0
#endif
#ifndef FMT_CPP_LIB_VARIANT
# ifdef __cpp_lib_variant
# define FMT_CPP_LIB_VARIANT __cpp_lib_variant
# else
# define FMT_CPP_LIB_VARIANT 0
# endif
#ifdef FMT_CPP_LIB_VARIANT
// Use the provided definition.
#elif defined(__cpp_lib_variant)
# define FMT_CPP_LIB_VARIANT __cpp_lib_variant
#else
# define FMT_CPP_LIB_VARIANT 0
#endif
FMT_BEGIN_NAMESPACE
namespace detail {
#if FMT_CPP_LIB_FILESYSTEM
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Char, typename PathChar>
auto get_path_string(const std::filesystem::path& p,
@@ -111,8 +108,168 @@ void write_escaped_path(basic_memory_buffer<Char>& quoted,
}
}
#endif // FMT_CPP_LIB_FILESYSTEM
#if defined(__cpp_lib_expected) || FMT_CPP_LIB_VARIANT
template <typename Char, typename OutputIt, typename T>
auto write_escaped_alternative(OutputIt out, const T& v) -> OutputIt {
if constexpr (has_to_string_view<T>::value)
return write_escaped_string<Char>(out, detail::to_string_view(v));
if constexpr (std::is_same_v<T, Char>) return write_escaped_char(out, v);
return write<Char>(out, v);
}
#endif
#if FMT_CPP_LIB_VARIANT
template <typename> struct is_variant_like_ : std::false_type {};
template <typename... Types>
struct is_variant_like_<std::variant<Types...>> : std::true_type {};
template <typename Variant, typename Char> class is_variant_formattable {
template <size_t... Is>
static auto check(std::index_sequence<Is...>) -> std::conjunction<
is_formattable<std::variant_alternative_t<Is, Variant>, Char>...>;
public:
static constexpr bool value = decltype(check(
std::make_index_sequence<std::variant_size<Variant>::value>()))::value;
};
#endif // FMT_CPP_LIB_VARIANT
#if FMT_USE_RTTI
template <typename OutputIt>
auto write_demangled_name(OutputIt out, const std::type_info& ti) -> OutputIt {
# ifdef FMT_HAS_ABI_CXA_DEMANGLE
int status = 0;
size_t size = 0;
std::unique_ptr<char, void (*)(void*)> demangled_name_ptr(
abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free);
string_view demangled_name_view;
if (demangled_name_ptr) {
demangled_name_view = demangled_name_ptr.get();
// Normalization of stdlib inline namespace names.
// libc++ inline namespaces.
// std::__1::* -> std::*
// std::__1::__fs::* -> std::*
// libstdc++ inline namespaces.
// std::__cxx11::* -> std::*
// std::filesystem::__cxx11::* -> std::filesystem::*
if (demangled_name_view.starts_with("std::")) {
char* begin = demangled_name_ptr.get();
char* to = begin + 5; // std::
for (char *from = to, *end = begin + demangled_name_view.size();
from < end;) {
// This is safe, because demangled_name is NUL-terminated.
if (from[0] == '_' && from[1] == '_') {
char* next = from + 1;
while (next < end && *next != ':') next++;
if (next[0] == ':' && next[1] == ':') {
from = next + 2;
continue;
}
}
*to++ = *from++;
}
demangled_name_view = {begin, detail::to_unsigned(to - begin)};
}
} else {
demangled_name_view = string_view(ti.name());
}
return detail::write_bytes<char>(out, demangled_name_view);
# elif FMT_MSC_VERSION
const string_view demangled_name(ti.name());
for (size_t i = 0; i < demangled_name.size(); ++i) {
auto sub = demangled_name;
sub.remove_prefix(i);
if (sub.starts_with("enum ")) {
i += 4;
continue;
}
if (sub.starts_with("class ") || sub.starts_with("union ")) {
i += 5;
continue;
}
if (sub.starts_with("struct ")) {
i += 6;
continue;
}
if (*sub.begin() != ' ') *out++ = *sub.begin();
}
return out;
# else
return detail::write_bytes<char>(out, string_view(ti.name()));
# endif
}
#endif // FMT_USE_RTTI
template <typename T, typename Enable = void>
struct has_flip : std::false_type {};
template <typename T>
struct has_flip<T, void_t<decltype(std::declval<T>().flip())>>
: std::true_type {};
template <typename T> struct is_bit_reference_like {
static constexpr bool value = std::is_convertible<T, bool>::value &&
std::is_nothrow_assignable<T, bool>::value &&
has_flip<T>::value;
};
// Workaround for libc++ incompatibility with C++ standard.
// According to the Standard, `bitset::operator[] const` returns bool.
#if defined(_LIBCPP_VERSION) && !defined(FMT_IMPORT_STD)
template <typename C>
struct is_bit_reference_like<std::__bit_const_reference<C>> {
static constexpr bool value = true;
};
#endif
template <typename T, typename Enable = void>
struct has_format_as : std::false_type {};
template <typename T>
struct has_format_as<T, void_t<decltype(format_as(std::declval<const T&>()))>>
: std::true_type {};
template <typename T, typename Enable = void>
struct has_format_as_member : std::false_type {};
template <typename T>
struct has_format_as_member<
T, void_t<decltype(formatter<T>::format_as(std::declval<const T&>()))>>
: std::true_type {};
} // namespace detail
template <typename T, typename Deleter>
auto ptr(const std::unique_ptr<T, Deleter>& p) -> const void* {
return p.get();
}
template <typename T> auto ptr(const std::shared_ptr<T>& p) -> const void* {
return p.get();
}
#if FMT_CPP_LIB_FILESYSTEM
class path : public std::filesystem::path {
public:
auto display_string() const -> std::string {
const std::filesystem::path& base = *this;
return fmt::format(FMT_STRING("{}"), base);
}
auto system_string() const -> std::string { return string(); }
auto generic_display_string() const -> std::string {
const std::filesystem::path& base = *this;
return fmt::format(FMT_STRING("{:g}"), base);
}
auto generic_system_string() const -> std::string { return generic_string(); }
};
template <typename Char> struct formatter<std::filesystem::path, Char> {
private:
format_specs specs_;
@@ -162,39 +319,20 @@ template <typename Char> struct formatter<std::filesystem::path, Char> {
}
};
class path : public std::filesystem::path {
public:
auto display_string() const -> std::string {
const std::filesystem::path& base = *this;
return fmt::format(FMT_STRING("{}"), base);
}
auto system_string() const -> std::string { return string(); }
auto generic_display_string() const -> std::string {
const std::filesystem::path& base = *this;
return fmt::format(FMT_STRING("{:g}"), base);
}
auto generic_system_string() const -> std::string { return generic_string(); }
};
FMT_END_NAMESPACE
#endif // FMT_CPP_LIB_FILESYSTEM
FMT_BEGIN_NAMESPACE
template <std::size_t N, typename Char>
template <size_t N, typename Char>
struct formatter<std::bitset<N>, Char>
: nested_formatter<basic_string_view<Char>, Char> {
private:
// Functor because C++11 doesn't support generic lambdas.
// This is a functor because C++11 doesn't support generic lambdas.
struct writer {
const std::bitset<N>& bs;
template <typename OutputIt>
FMT_CONSTEXPR auto operator()(OutputIt out) -> OutputIt {
for (auto pos = N; pos > 0; --pos) {
for (auto pos = N; pos > 0; --pos)
out = detail::write<Char>(out, bs[pos - 1] ? Char('1') : Char('0'));
}
return out;
}
};
@@ -209,10 +347,8 @@ struct formatter<std::bitset<N>, Char>
template <typename Char>
struct formatter<std::thread::id, Char> : basic_ostream_formatter<Char> {};
FMT_END_NAMESPACE
#ifdef __cpp_lib_optional
FMT_BEGIN_NAMESPACE
template <typename T, typename Char>
struct formatter<std::optional<T>, Char,
std::enable_if_t<is_formattable<T, Char>::value>> {
@@ -251,30 +387,9 @@ struct formatter<std::optional<T>, Char,
return detail::write(out, ')');
}
};
FMT_END_NAMESPACE
#endif // __cpp_lib_optional
#if defined(__cpp_lib_expected) || FMT_CPP_LIB_VARIANT
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Char, typename OutputIt, typename T>
auto write_escaped_alternative(OutputIt out, const T& v) -> OutputIt {
if constexpr (has_to_string_view<T>::value)
return write_escaped_string<Char>(out, detail::to_string_view(v));
if constexpr (std::is_same_v<T, Char>) return write_escaped_char(out, v);
return write<Char>(out, v);
}
} // namespace detail
FMT_END_NAMESPACE
#endif
#ifdef __cpp_lib_expected
FMT_BEGIN_NAMESPACE
template <typename T, typename E, typename Char>
struct formatter<std::expected<T, E>, Char,
std::enable_if_t<(std::is_void<T>::value ||
@@ -301,11 +416,9 @@ struct formatter<std::expected<T, E>, Char,
return out;
}
};
FMT_END_NAMESPACE
#endif // __cpp_lib_expected
#ifdef __cpp_lib_source_location
FMT_BEGIN_NAMESPACE
template <> struct formatter<std::source_location> {
FMT_CONSTEXPR auto parse(parse_context<>& ctx) { return ctx.begin(); }
@@ -323,42 +436,12 @@ template <> struct formatter<std::source_location> {
return out;
}
};
FMT_END_NAMESPACE
#endif
#if FMT_CPP_LIB_VARIANT
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename T>
using variant_index_sequence =
std::make_index_sequence<std::variant_size<T>::value>;
template <typename> struct is_variant_like_ : std::false_type {};
template <typename... Types>
struct is_variant_like_<std::variant<Types...>> : std::true_type {};
// formattable element check.
template <typename T, typename C> class is_variant_formattable_ {
template <std::size_t... Is>
static std::conjunction<
is_formattable<std::variant_alternative_t<Is, T>, C>...>
check(std::index_sequence<Is...>);
public:
static constexpr const bool value =
decltype(check(variant_index_sequence<T>{}))::value;
};
} // namespace detail
template <typename T> struct is_variant_like {
static constexpr const bool value = detail::is_variant_like_<T>::value;
};
template <typename T, typename C> struct is_variant_formattable {
static constexpr const bool value =
detail::is_variant_formattable_<T, C>::value;
static constexpr bool value = detail::is_variant_like_<T>::value;
};
template <typename Char> struct formatter<std::monostate, Char> {
@@ -374,10 +457,10 @@ template <typename Char> struct formatter<std::monostate, Char> {
};
template <typename Variant, typename Char>
struct formatter<
Variant, Char,
std::enable_if_t<std::conjunction_v<
is_variant_like<Variant>, is_variant_formattable<Variant, Char>>>> {
struct formatter<Variant, Char,
std::enable_if_t<std::conjunction_v<
is_variant_like<Variant>,
detail::is_variant_formattable<Variant, Char>>>> {
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return ctx.begin();
}
@@ -402,10 +485,9 @@ struct formatter<
return out;
}
};
FMT_END_NAMESPACE
#endif // FMT_CPP_LIB_VARIANT
FMT_BEGIN_NAMESPACE
template <> struct formatter<std::error_code> {
private:
format_specs specs_;
@@ -459,101 +541,29 @@ template <> struct formatter<std::error_code> {
};
#if FMT_USE_RTTI
namespace detail {
template <typename Char, typename OutputIt>
auto write_demangled_name(OutputIt out, const std::type_info& ti) -> OutputIt {
# ifdef FMT_HAS_ABI_CXA_DEMANGLE
int status = 0;
std::size_t size = 0;
std::unique_ptr<char, void (*)(void*)> demangled_name_ptr(
abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free);
string_view demangled_name_view;
if (demangled_name_ptr) {
demangled_name_view = demangled_name_ptr.get();
// Normalization of stdlib inline namespace names.
// libc++ inline namespaces.
// std::__1::* -> std::*
// std::__1::__fs::* -> std::*
// libstdc++ inline namespaces.
// std::__cxx11::* -> std::*
// std::filesystem::__cxx11::* -> std::filesystem::*
if (demangled_name_view.starts_with("std::")) {
char* begin = demangled_name_ptr.get();
char* to = begin + 5; // std::
for (char *from = to, *end = begin + demangled_name_view.size();
from < end;) {
// This is safe, because demangled_name is NUL-terminated.
if (from[0] == '_' && from[1] == '_') {
char* next = from + 1;
while (next < end && *next != ':') next++;
if (next[0] == ':' && next[1] == ':') {
from = next + 2;
continue;
}
}
*to++ = *from++;
}
demangled_name_view = {begin, detail::to_unsigned(to - begin)};
}
} else {
demangled_name_view = string_view(ti.name());
}
return detail::write_bytes<Char>(out, demangled_name_view);
# elif FMT_MSC_VERSION
const string_view demangled_name(ti.name());
for (std::size_t i = 0; i < demangled_name.size(); ++i) {
auto sub = demangled_name;
sub.remove_prefix(i);
if (sub.starts_with("enum ")) {
i += 4;
continue;
}
if (sub.starts_with("class ") || sub.starts_with("union ")) {
i += 5;
continue;
}
if (sub.starts_with("struct ")) {
i += 6;
continue;
}
if (*sub.begin() != ' ') *out++ = *sub.begin();
}
return out;
# else
return detail::write_bytes<Char>(out, string_view(ti.name()));
# endif
}
} // namespace detail
template <typename Char>
struct formatter<std::type_info, Char // DEPRECATED! Mixing code unit types.
> {
template <> struct formatter<std::type_info> {
public:
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* {
return ctx.begin();
}
template <typename Context>
auto format(const std::type_info& ti, Context& ctx) const
-> decltype(ctx.out()) {
return detail::write_demangled_name<Char>(ctx.out(), ti);
return detail::write_demangled_name(ctx.out(), ti);
}
};
#endif
#endif // FMT_USE_RTTI
template <typename T, typename Char>
template <typename T>
struct formatter<
T, Char, // DEPRECATED! Mixing code unit types.
T, char,
typename std::enable_if<std::is_base_of<std::exception, T>::value>::type> {
private:
bool with_typename_ = false;
public:
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* {
auto it = ctx.begin();
auto end = ctx.end();
if (it == end || *it == '}') return it;
@@ -570,43 +580,15 @@ struct formatter<
auto out = ctx.out();
#if FMT_USE_RTTI
if (with_typename_) {
out = detail::write_demangled_name<Char>(out, typeid(ex));
out = detail::write_demangled_name(out, typeid(ex));
*out++ = ':';
*out++ = ' ';
}
#endif
return detail::write_bytes<Char>(out, string_view(ex.what()));
return detail::write_bytes<char>(out, string_view(ex.what()));
}
};
namespace detail {
template <typename T, typename Enable = void>
struct has_flip : std::false_type {};
template <typename T>
struct has_flip<T, void_t<decltype(std::declval<T>().flip())>>
: std::true_type {};
template <typename T> struct is_bit_reference_like {
static constexpr const bool value =
std::is_convertible<T, bool>::value &&
std::is_nothrow_assignable<T, bool>::value && has_flip<T>::value;
};
#ifdef _LIBCPP_VERSION
// Workaround for libc++ incompatibility with C++ standard.
// According to the Standard, `bitset::operator[] const` returns bool.
template <typename C>
struct is_bit_reference_like<std::__bit_const_reference<C>> {
static constexpr const bool value = true;
};
#endif
} // namespace detail
// We can't use std::vector<bool, Allocator>::reference and
// std::bitset<N>::reference because the compiler can't deduce Allocator and N
// in partial specialization.
@@ -621,14 +603,6 @@ struct formatter<BitRef, Char,
}
};
template <typename T, typename Deleter>
auto ptr(const std::unique_ptr<T, Deleter>& p) -> const void* {
return p.get();
}
template <typename T> auto ptr(const std::shared_ptr<T>& p) -> const void* {
return p.get();
}
template <typename T, typename Char>
struct formatter<std::atomic<T>, Char,
enable_if_t<is_formattable<T, Char>::value>>
@@ -715,7 +689,11 @@ template <typename T, typename Char> struct formatter<std::complex<T>, Char> {
template <typename T, typename Char>
struct formatter<std::reference_wrapper<T>, Char,
enable_if_t<is_formattable<remove_cvref_t<T>, Char>::value>>
// Guard against format_as because reference_wrapper is
// implicitly convertible to T&.
enable_if_t<is_formattable<remove_cvref_t<T>, Char>::value &&
!detail::has_format_as<T>::value &&
!detail::has_format_as_member<T>::value>>
: formatter<remove_cvref_t<T>, Char> {
template <typename FormatContext>
auto format(std::reference_wrapper<T> ref, FormatContext& ctx) const
@@ -725,4 +703,5 @@ struct formatter<std::reference_wrapper<T>, Char,
};
FMT_END_NAMESPACE
#endif // FMT_STD_H_

View File

@@ -55,6 +55,16 @@ inline auto write_loc(basic_appender<wchar_t> out, loc_value value,
#endif
return false;
}
template <typename Char>
void vformat_to(buffer<Char>& buf, basic_string_view<Char> fmt,
basic_format_args<buffered_context<Char>> args,
locale_ref loc = {}) {
static_assert(!std::is_same<Char, char>::value, "");
auto out = basic_appender<Char>(buf);
parse_format_string(
fmt, format_handler<Char>{parse_context<Char>(fmt), {out, args, loc}});
}
} // namespace detail
FMT_BEGIN_EXPORT
@@ -112,10 +122,6 @@ inline auto runtime(wstring_view s) -> runtime_format_string<wchar_t> {
return {{s}};
}
#ifdef __cpp_char8_t
template <> struct is_char<char8_t> : bool_constant<detail::is_utf8_enabled> {};
#endif
template <typename... T>
constexpr auto make_wformat_args(T&... args)
-> decltype(fmt::make_format_args<wformat_context>(args...)) {
@@ -151,13 +157,13 @@ auto join(std::initializer_list<T> list, wstring_view sep)
template <typename Tuple, FMT_ENABLE_IF(is_tuple_like<Tuple>::value)>
auto join(const Tuple& tuple, basic_string_view<wchar_t> sep)
-> tuple_join_view<wchar_t, Tuple> {
-> tuple_join_view<Tuple, wchar_t> {
return {tuple, sep};
}
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
auto vformat(basic_string_view<Char> fmt,
typename detail::vformat_args<Char>::type args)
basic_format_args<buffered_context<Char>> args)
-> std::basic_string<Char> {
auto buf = basic_memory_buffer<Char>();
detail::vformat_to(buf, fmt, args);
@@ -187,24 +193,20 @@ auto format(const S& fmt, T&&... args) -> std::basic_string<Char> {
fmt::make_format_args<buffered_context<Char>>(args...));
}
template <typename Locale, typename S,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)>
inline auto vformat(const Locale& loc, const S& fmt,
typename detail::vformat_args<Char>::type args)
template <typename S, typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
inline auto vformat(locale_ref loc, const S& fmt,
basic_format_args<buffered_context<Char>> args)
-> std::basic_string<Char> {
auto buf = basic_memory_buffer<Char>();
detail::vformat_to(buf, detail::to_string_view(fmt), args,
detail::locale_ref(loc));
detail::vformat_to(buf, detail::to_string_view(fmt), args, loc);
return {buf.data(), buf.size()};
}
template <typename Locale, typename S, typename... T,
template <typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)>
inline auto format(const Locale& loc, const S& fmt, T&&... args)
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
inline auto format(locale_ref loc, const S& fmt, T&&... args)
-> std::basic_string<Char> {
return vformat(loc, detail::to_string_view(fmt),
fmt::make_format_args<buffered_context<Char>>(args...));
@@ -215,7 +217,7 @@ template <typename OutputIt, typename S,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)>
auto vformat_to(OutputIt out, const S& fmt,
typename detail::vformat_args<Char>::type args) -> OutputIt {
basic_format_args<buffered_context<Char>> args) -> OutputIt {
auto&& buf = detail::get_buffer<Char>(out);
detail::vformat_to(buf, detail::to_string_view(fmt), args);
return detail::get_iterator(buf, out);
@@ -231,27 +233,24 @@ inline auto format_to(OutputIt out, const S& fmt, T&&... args) -> OutputIt {
fmt::make_format_args<buffered_context<Char>>(args...));
}
template <typename Locale, typename S, typename OutputIt, typename... Args,
template <typename S, typename OutputIt, typename... Args,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)>
inline auto vformat_to(OutputIt out, const Locale& loc, const S& fmt,
typename detail::vformat_args<Char>::type args)
detail::is_exotic_char<Char>::value)>
inline auto vformat_to(OutputIt out, locale_ref loc, const S& fmt,
basic_format_args<buffered_context<Char>> args)
-> OutputIt {
auto&& buf = detail::get_buffer<Char>(out);
vformat_to(buf, detail::to_string_view(fmt), args, detail::locale_ref(loc));
vformat_to(buf, detail::to_string_view(fmt), args, loc);
return detail::get_iterator(buf, out);
}
template <typename Locale, typename OutputIt, typename S, typename... T,
template <typename OutputIt, typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
bool enable = detail::is_output_iterator<OutputIt, Char>::value &&
detail::is_locale<Locale>::value &&
detail::is_exotic_char<Char>::value>
inline auto format_to(OutputIt out, const Locale& loc, const S& fmt,
T&&... args) ->
typename std::enable_if<enable, OutputIt>::type {
inline auto format_to(OutputIt out, locale_ref loc, const S& fmt, T&&... args)
-> typename std::enable_if<enable, OutputIt>::type {
return vformat_to(out, loc, detail::to_string_view(fmt),
fmt::make_format_args<buffered_context<Char>>(args...));
}
@@ -260,7 +259,7 @@ template <typename OutputIt, typename Char, typename... Args,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)>
inline auto vformat_to_n(OutputIt out, size_t n, basic_string_view<Char> fmt,
typename detail::vformat_args<Char>::type args)
basic_format_args<buffered_context<Char>> args)
-> format_to_n_result<OutputIt> {
using traits = detail::fixed_buffer_traits;
auto buf = detail::iterator_buffer<OutputIt, Char, traits>(out, n);
@@ -331,18 +330,6 @@ inline auto format(text_style ts, wformat_string<T...> fmt, T&&... args)
return fmt::vformat(ts, fmt, fmt::make_wformat_args(args...));
}
template <typename... T>
FMT_DEPRECATED void print(std::FILE* f, text_style ts, wformat_string<T...> fmt,
const T&... args) {
vprint(f, ts, fmt, fmt::make_wformat_args(args...));
}
template <typename... T>
FMT_DEPRECATED void print(text_style ts, wformat_string<T...> fmt,
const T&... args) {
return print(stdout, ts, fmt, args...);
}
inline void vprint(std::wostream& os, wstring_view fmt, wformat_args args) {
auto buffer = basic_memory_buffer<wchar_t>();
detail::vformat_to(buffer, fmt, args);

View File

@@ -50,6 +50,8 @@ module;
# include <limits.h>
# include <stdint.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <time.h>
#endif
#include <cerrno>

View File

@@ -8,6 +8,12 @@
#include "fmt/format-inl.h"
FMT_BEGIN_NAMESPACE
#if FMT_USE_LOCALE
template FMT_API locale_ref::locale_ref(const std::locale& loc);
template FMT_API auto locale_ref::get<std::locale>() const -> std::locale;
#endif
namespace detail {
template FMT_API auto dragonbox::to_decimal(float x) noexcept
@@ -15,12 +21,6 @@ template FMT_API auto dragonbox::to_decimal(float x) noexcept
template FMT_API auto dragonbox::to_decimal(double x) noexcept
-> dragonbox::decimal_fp<double>;
#if FMT_USE_LOCALE
// DEPRECATED! locale_ref in the detail namespace
template FMT_API locale_ref::locale_ref(const std::locale& loc);
template FMT_API auto locale_ref::get<std::locale>() const -> std::locale;
#endif
// Explicit instantiations for char.
template FMT_API auto thousands_sep_impl(locale_ref)
@@ -30,16 +30,13 @@ template FMT_API auto decimal_point_impl(locale_ref) -> char;
// DEPRECATED!
template FMT_API void buffer<char>::append(const char*, const char*);
// DEPRECATED!
template FMT_API void vformat_to(buffer<char>&, string_view,
typename vformat_args<>::type, locale_ref);
// Explicit instantiations for wchar_t.
template FMT_API auto thousands_sep_impl(locale_ref)
-> thousands_sep_result<wchar_t>;
template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t;
// DEPRECATED!
template FMT_API void buffer<wchar_t>::append(const wchar_t*, const wchar_t*);
} // namespace detail

View File

@@ -66,14 +66,14 @@ using rwresult = int;
// On Windows the count argument to read and write is unsigned, so convert
// it from size_t preventing integer overflow.
inline unsigned convert_rwcount(std::size_t count) {
inline unsigned convert_rwcount(size_t count) {
return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX;
}
#elif FMT_USE_FCNTL
// Return type of read and write functions.
using rwresult = ssize_t;
inline std::size_t convert_rwcount(std::size_t count) { return count; }
inline auto convert_rwcount(size_t count) -> size_t { return count; }
#endif
} // namespace
@@ -185,7 +185,7 @@ void buffered_file::close() {
FMT_THROW(system_error(errno, FMT_STRING("cannot close file")));
}
int buffered_file::descriptor() const {
auto buffered_file::descriptor() const -> int {
#ifdef FMT_HAS_SYSTEM
// fileno is a macro on OpenBSD.
# ifdef fileno
@@ -240,7 +240,7 @@ void file::close() {
FMT_THROW(system_error(errno, FMT_STRING("cannot close file")));
}
long long file::size() const {
auto file::size() const -> long long {
# ifdef _WIN32
// Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT
// is less than 0x0500 as is the case with some default MinGW builds.
@@ -251,7 +251,7 @@ long long file::size() const {
if (size_lower == INVALID_FILE_SIZE) {
DWORD error = GetLastError();
if (error != NO_ERROR)
FMT_THROW(windows_error(GetLastError(), "cannot get file size"));
FMT_THROW(windows_error(error, "cannot get file size"));
}
unsigned long long long_size = size_upper;
return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower;
@@ -266,7 +266,7 @@ long long file::size() const {
# endif
}
std::size_t file::read(void* buffer, std::size_t count) {
auto file::read(void* buffer, size_t count) -> size_t {
rwresult result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
if (result < 0)
@@ -274,7 +274,7 @@ std::size_t file::read(void* buffer, std::size_t count) {
return detail::to_unsigned(result);
}
std::size_t file::write(const void* buffer, std::size_t count) {
auto file::write(const void* buffer, size_t count) -> size_t {
rwresult result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
if (result < 0)
@@ -282,7 +282,7 @@ std::size_t file::write(const void* buffer, std::size_t count) {
return detail::to_unsigned(result);
}
file file::dup(int fd) {
auto file::dup(int fd) -> file {
// Don't retry as dup doesn't return EINTR.
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html
int new_fd = FMT_POSIX_CALL(dup(fd));
@@ -308,7 +308,7 @@ void file::dup2(int fd, std::error_code& ec) noexcept {
if (result == -1) ec = std::error_code(errno, std::generic_category());
}
buffered_file file::fdopen(const char* mode) {
auto file::fdopen(const char* mode) -> buffered_file {
// Don't retry as fdopen doesn't return EINTR.
# if defined(__MINGW32__) && defined(_POSIX_)
FILE* f = ::fdopen(fd_, mode);
@@ -355,7 +355,7 @@ pipe::pipe() {
}
# if !defined(__MSDOS__)
long getpagesize() {
auto getpagesize() -> long {
# ifdef _WIN32
SYSTEM_INFO si;
GetSystemInfo(&si);

View File

@@ -1,3 +1,19 @@
# v12.1.0
* add rc_client_get_user_subset_summary
* add validation warning for using MeasuredIf without Measured
* add validation warning for using ResetIf without hit targets
* add rapi function for update_rich_presence
* add gap to RC_CONSOLE_WII memory map to make it easier to convert pointers
* improve range validation logic
* fix error Remembering float value
* fix MeasuredIf evaluation in rich presence
* fix parsing of code notes with addresses above 0x7FFFFFFF
* fix double evaluation of rich presence parameters
* fix validation of SubSource chain
* fix error if rc_client_allow_background_memory_reads called before calling rc_client_begin_load_raintegration
* fix memory corruption when mixing legacy and new-format macros in rich presence
* fix invalid pointer reference when iterator gets cloned
# v12.0.0
* rc_client changes
* add RC_CLIENT_EVENT_SUBSET_COMPLETED event

View File

@@ -5880,7 +5880,7 @@ void rc_client_do_frame(rc_client_t* client)
richpresence = client->game->runtime.richpresence;
if (richpresence && richpresence->richpresence)
rc_update_richpresence(richpresence->richpresence, client->state.legacy_peek, client, NULL);
rc_update_richpresence_internal(richpresence->richpresence, client->state.legacy_peek, client);
rc_mutex_unlock(&client->state.mutex);

View File

@@ -223,6 +223,8 @@ static void rc_client_init_raintegration(rc_client_t* client,
external_client->set_encore_mode_enabled(rc_client_get_encore_mode_enabled(client));
if (external_client->set_spectator_mode_enabled)
external_client->set_spectator_mode_enabled(rc_client_get_spectator_mode_enabled(client));
if (external_client->set_allow_background_memory_reads)
external_client->set_allow_background_memory_reads(client->state.allow_background_memory_reads);
/* attach the external client and call the callback */
client->state.external_client = external_client;

View File

@@ -8,7 +8,7 @@
RC_BEGIN_C_DECLS
#define RCHEEVOS_VERSION_MAJOR 12
#define RCHEEVOS_VERSION_MINOR 0
#define RCHEEVOS_VERSION_MINOR 1
#define RCHEEVOS_VERSION_PATCH 0
#define RCHEEVOS_MAKE_VERSION(major, minor, patch) (major * 1000000 + minor * 1000 + patch)

View File

@@ -607,8 +607,8 @@ static void rc_condset_evaluate_or_next(rc_condition_t* condition, rc_eval_state
eval_state->or_next = rc_condset_evaluate_condition_no_add_hits(condition, eval_state);
}
static void rc_test_condset_internal(rc_condition_t* condition, uint32_t num_conditions,
rc_eval_state_t* eval_state, int can_short_circuit) {
void rc_test_condset_internal(rc_condition_t* condition, uint32_t num_conditions,
rc_eval_state_t* eval_state, int can_short_circuit) {
const rc_condition_t* condition_end = condition + num_conditions;
for (; condition < condition_end; ++condition) {
switch (condition->type) {

View File

@@ -311,6 +311,7 @@ rc_condset_t* rc_parse_condset(const char** memaddr, rc_parse_state_t* parse);
int rc_test_condset(rc_condset_t* self, rc_eval_state_t* eval_state);
void rc_reset_condset(rc_condset_t* self);
rc_condition_t* rc_condset_get_conditions(rc_condset_t* self);
void rc_test_condset_internal(rc_condition_t* condition, uint32_t num_conditions, rc_eval_state_t* eval_state, int can_short_circuit);
enum {
RC_PROCESSING_COMPARE_DEFAULT = 0,
@@ -379,6 +380,7 @@ int rc_lboard_state_active(int state);
void rc_parse_richpresence_internal(rc_richpresence_t* self, const char* script, rc_parse_state_t* parse);
rc_memrefs_t* rc_richpresence_get_memrefs(rc_richpresence_t* self);
void rc_reset_richpresence_triggers(rc_richpresence_t* self);
void rc_update_richpresence_internal(rc_richpresence_t* richpresence, rc_peek_t peek, void* peek_ud);
int rc_validate_memrefs(const rc_memrefs_t* memrefs, char result[], const size_t result_size, uint32_t max_address);
int rc_validate_memrefs_for_console(const rc_memrefs_t* memrefs, char result[], const size_t result_size, uint32_t console_id);

View File

@@ -133,79 +133,93 @@ static uint32_t rc_max_value(const rc_operand_t* operand)
}
}
static uint32_t rc_scale_value(uint32_t value, uint8_t oper, const rc_operand_t* operand)
static void rc_combine_ranges(uint32_t* min_val, uint32_t* max_val, uint8_t oper, uint32_t oper_min_val, uint32_t oper_max_val)
{
switch (oper) {
case RC_OPERATOR_MULT:
{
unsigned long long scaled = ((unsigned long long)value) * rc_max_value(operand);
if (scaled > 0xFFFFFFFF)
return 0xFFFFFFFF;
unsigned long long scaled = ((unsigned long long)*min_val) * oper_min_val;
*min_val = (scaled > 0xFFFFFFFF) ? 0xFFFFFFFF : (uint32_t)scaled;
return (uint32_t)scaled;
scaled = ((unsigned long long)*max_val) * oper_max_val;
*max_val = (scaled > 0xFFFFFFFF) ? 0xFFFFFFFF : (uint32_t)scaled;
break;
}
case RC_OPERATOR_DIV:
{
const uint32_t min_val = (operand->type == RC_OPERAND_CONST) ? operand->value.num : 1;
return value / min_val;
}
*min_val = (oper_max_val == 0) ? *min_val : (*min_val / oper_max_val);
*max_val = (oper_min_val == 0) ? *max_val : (*max_val / oper_min_val);
break;
case RC_OPERATOR_AND:
return rc_max_value(operand);
*min_val = 0;
*max_val &= oper_max_val;
break;
case RC_OPERATOR_XOR:
return value | rc_max_value(operand);
*min_val = 0;
*max_val |= oper_max_val;
break;
case RC_OPERATOR_MOD:
{
const uint32_t divisor = (operand->type == RC_OPERAND_CONST) ? operand->value.num : 1;
return (divisor >= value) ? (divisor - 1) : value;
}
*min_val = 0;
*max_val = (*max_val >= oper_max_val) ? oper_max_val - 1 : *max_val;
break;
case RC_OPERATOR_ADD:
{
unsigned long scaled = ((unsigned long)value) + rc_max_value(operand);
if (scaled > 0xFFFFFFFF)
return 0xFFFFFFFF;
if (*min_val > *max_val) { /* underflow occurred */
*max_val += oper_max_val;
}
else {
unsigned long scaled = ((unsigned long)*max_val) + oper_max_val;
*max_val = (scaled > 0xFFFFFFFF) ? 0xFFFFFFFF : (uint32_t)scaled;
}
return (uint32_t)scaled;
}
*min_val += oper_min_val;
break;
case RC_OPERATOR_SUB:
{
const uint32_t op_max = (operand->type == RC_OPERAND_CONST) ? operand->value.num : 0;
if (value >= op_max)
return value - op_max;
return 0xFFFFFFFF;
}
*min_val -= oper_max_val;
*max_val -= oper_min_val;
break;
case RC_OPERATOR_SUB_PARENT:
{
const uint32_t op_max = (operand->type == RC_OPERAND_CONST) ? operand->value.num : rc_max_value(operand);
if (op_max > value)
return op_max - value;
return 0xFFFFFFFF;
uint32_t temp = oper_min_val - *max_val;
*max_val = oper_max_val - *min_val;
*min_val = temp;
break;
}
default:
return value;
break;
}
}
static uint32_t rc_max_chain_value(const rc_operand_t* operand)
static void rc_chain_get_value_range(const rc_operand_t* operand, uint32_t* min_val, uint32_t* max_val)
{
if (rc_operand_is_memref(operand) && operand->value.memref->value.memref_type == RC_MEMREF_TYPE_MODIFIED_MEMREF) {
const rc_modified_memref_t* modified_memref = (const rc_modified_memref_t*)operand->value.memref;
if (modified_memref->modifier_type != RC_OPERATOR_INDIRECT_READ) {
const uint32_t op_max = rc_max_chain_value(&modified_memref->parent);
return rc_scale_value(op_max, modified_memref->modifier_type, &modified_memref->modifier);
if (modified_memref->modifier_type == RC_OPERATOR_DIV &&
rc_operand_is_memref(&modified_memref->modifier) &&
rc_operands_are_equal(&modified_memref->modifier, &modified_memref->parent)) {
/* division by self can only return 0 or 1. */
*min_val = 0;
*max_val = 1;
}
else {
uint32_t modifier_min_val, modifier_max_val;
rc_chain_get_value_range(&modified_memref->parent, min_val, max_val);
rc_chain_get_value_range(&modified_memref->modifier, &modifier_min_val, &modifier_max_val);
rc_combine_ranges(min_val, max_val, modified_memref->modifier_type, modifier_min_val, modifier_max_val);
}
return;
}
}
return rc_max_value(operand);
*min_val = (operand->type == RC_OPERAND_CONST) ? operand->value.num : 0;
*max_val = rc_max_value(operand);
}
static int rc_validate_get_condition_index(const rc_condset_t* condset, const rc_condition_t* condition)
@@ -291,7 +305,7 @@ static int rc_validate_range(uint32_t min_val, uint32_t max_val, char oper, uint
return 1;
}
static int rc_validate_condset_internal(const rc_condset_t* condset, char result[], const size_t result_size, uint32_t console_id, uint32_t max_address)
static int rc_validate_condset_internal(const rc_condset_t* condset, char result[], const size_t result_size, uint32_t console_id, uint32_t max_address, int has_hits)
{
const rc_condition_t* cond;
char buffer[128];
@@ -299,6 +313,8 @@ static int rc_validate_condset_internal(const rc_condset_t* condset, char result
int in_add_hits = 0;
int in_add_address = 0;
int is_combining = 0;
int has_measured = 0;
int measuredif_index = -1;
if (!condset) {
*result = '\0';
@@ -383,6 +399,10 @@ static int rc_validate_condset_internal(const rc_condset_t* condset, char result
is_combining = 0;
break;
}
if (!has_hits) {
snprintf(result, result_size, "Condition %d: No captured hits to reset", index);
return 0;
}
if (cond->required_hits == 1) {
snprintf(result, result_size, "Condition %d: Hit target of 1 is redundant on ResetIf", index);
return 0;
@@ -398,6 +418,10 @@ static int rc_validate_condset_internal(const rc_condset_t* condset, char result
in_add_hits = 0;
}
has_measured |= (cond->type == RC_CONDITION_MEASURED);
if (cond->type == RC_CONDITION_MEASURED_IF && measuredif_index == -1)
measuredif_index = index;
is_combining = 0;
break;
}
@@ -424,10 +448,16 @@ static int rc_validate_condset_internal(const rc_condset_t* condset, char result
const size_t prefix_length = snprintf(result, result_size, "Condition %d: ", index);
const rc_operand_t* operand2 = &cond->operand2;
uint8_t oper = cond->oper;
uint32_t max = rc_max_chain_value(operand1);
uint32_t min, max;
uint32_t max_val = rc_max_value(operand2);
uint32_t min_val;
rc_chain_get_value_range(operand1, &min, &max);
if (min > max) { /* underflow */
min = 0;
max = 0xFFFFFFFF;
}
if (!is_memref1) {
/* pretend constant was on right side */
operand2 = operand1;
@@ -477,6 +507,7 @@ static int rc_validate_condset_internal(const rc_condset_t* condset, char result
break;
}
/* min_val and max_val are the range allowed by operand2. max is the upper value from operand1. */
if (!rc_validate_range(min_val, max_val, oper, max, result + prefix_length, result_size - prefix_length))
return 0;
}
@@ -487,19 +518,48 @@ static int rc_validate_condset_internal(const rc_condset_t* condset, char result
return 0;
}
if (measuredif_index != -1 && !has_measured) {
snprintf(result, result_size, "Condition %d: MeasuredIf without Measured", measuredif_index);
return 0;
}
*result = '\0';
return 1;
}
static int rc_condset_has_hittargets(const rc_condset_t* condset)
{
if (condset->num_hittarget_conditions > 0)
return 1;
/* pause and reset conditions may have hittargets and won't be classified as hittarget conditions.
* measured conditions may also have hittargets.
*/
if (condset->num_pause_conditions || condset->num_reset_conditions || condset->num_measured_conditions) {
const rc_condition_t* condition = rc_condset_get_conditions((rc_condset_t*)condset);
/* ASSERT: don't need to add num_hittarget_conditions because it must be 0 per earlier check */
const rc_condition_t* stop = condition + condset->num_pause_conditions
+ condset->num_reset_conditions + condset->num_measured_conditions;
for (; condition < stop; ++condition) {
if (condition->required_hits)
return 1;
}
}
return 0;
}
int rc_validate_condset(const rc_condset_t* condset, char result[], const size_t result_size, uint32_t max_address)
{
return rc_validate_condset_internal(condset, result, result_size, 0, max_address);
int has_hits = rc_condset_has_hittargets(condset);
return rc_validate_condset_internal(condset, result, result_size, 0, max_address, has_hits);
}
int rc_validate_condset_for_console(const rc_condset_t* condset, char result[], const size_t result_size, uint32_t console_id)
{
const uint32_t max_address = rc_console_max_address(console_id);
return rc_validate_condset_internal(condset, result, result_size, console_id, max_address);
int has_hits = rc_condset_has_hittargets(condset);
return rc_validate_condset_internal(condset, result, result_size, console_id, max_address, has_hits);
}
static int rc_validate_is_combining_condition(const rc_condition_t* condition)
@@ -845,7 +905,7 @@ static int rc_validate_conflicting_conditions(const rc_condset_t* conditions, co
switch (compare_condition->type)
{
case RC_CONDITION_PAUSE_IF:
if (conditions != compare_conditions)
if (conditions != compare_conditions) /* PauseIf only affects conditions in same group */
break;
/* fallthrough */
case RC_CONDITION_RESET_IF:
@@ -955,10 +1015,10 @@ static int rc_validate_trigger_internal(const rc_trigger_t* trigger, char result
{
const rc_condset_t* alt;
int index;
int has_hits = (trigger->requirement && trigger->requirement->num_hittarget_conditions > 0);
int has_hits = trigger->requirement && rc_condset_has_hittargets(trigger->requirement);
if (!has_hits) {
for (alt = trigger->alternative; alt; alt = alt->next) {
if (alt->num_hittarget_conditions > 0) {
if (rc_condset_has_hittargets(alt)) {
has_hits = 1;
break;
}
@@ -966,14 +1026,14 @@ static int rc_validate_trigger_internal(const rc_trigger_t* trigger, char result
}
if (!trigger->alternative) {
if (!rc_validate_condset_internal(trigger->requirement, result, result_size, console_id, max_address))
if (!rc_validate_condset_internal(trigger->requirement, result, result_size, console_id, max_address, has_hits))
return 0;
return rc_validate_conflicting_conditions(trigger->requirement, trigger->requirement, has_hits, "", "", result, result_size);
}
snprintf(result, result_size, "Core ");
if (!rc_validate_condset_internal(trigger->requirement, result + 5, result_size - 5, console_id, max_address))
if (!rc_validate_condset_internal(trigger->requirement, result + 5, result_size - 5, console_id, max_address, has_hits))
return 0;
/* compare core to itself */
@@ -984,7 +1044,7 @@ static int rc_validate_trigger_internal(const rc_trigger_t* trigger, char result
for (alt = trigger->alternative; alt; alt = alt->next, ++index) {
char altname[16];
const size_t prefix_length = snprintf(result, result_size, "Alt%d ", index);
if (!rc_validate_condset_internal(alt, result + prefix_length, result_size - prefix_length, console_id, max_address))
if (!rc_validate_condset_internal(alt, result + prefix_length, result_size - prefix_length, console_id, max_address, has_hits))
return 0;
/* compare alt to itself */

View File

@@ -723,14 +723,19 @@ rc_memrefs_t* rc_richpresence_get_memrefs(rc_richpresence_t* self) {
}
void rc_update_richpresence(rc_richpresence_t* richpresence, rc_peek_t peek, void* peek_ud, void* unused_L) {
rc_richpresence_display_t* display;
(void)unused_L;
rc_update_richpresence_memrefs(richpresence, peek, peek_ud);
rc_update_values(richpresence->values, peek, peek_ud);
rc_update_richpresence_internal(richpresence, peek, peek_ud);
}
void rc_update_richpresence_internal(rc_richpresence_t* richpresence, rc_peek_t peek, void* peek_ud) {
rc_richpresence_display_t* display;
for (display = richpresence->first_display; display; display = display->next) {
if (display->has_required_hits)
rc_test_trigger(&display->trigger, peek, peek_ud, unused_L);
rc_test_trigger(&display->trigger, peek, peek_ud, NULL);
}
}

View File

@@ -38,7 +38,7 @@ if(NOT "${CMAKE_BUILD_TYPE}" MATCHES "Debug")
if(MSVC)
target_compile_options(pcsx2-soundtouch PRIVATE /O2 /fp:fast)
else()
target_compile_options(pcsx2-soundtouch PRIVATE -Ofast)
target_compile_options(pcsx2-soundtouch PRIVATE -O3 -ffast-math)
endif()
endif()

View File

@@ -47,7 +47,10 @@ add_subdirectory(common)
# make pcsx2
add_subdirectory(pcsx2)
add_subdirectory(pcsx2-qt)
if(ENABLE_QT_UI)
add_subdirectory(pcsx2-qt)
endif()
# Updater is Windows only for now.
if (WIN32)
@@ -62,9 +65,6 @@ endif()
# gsrunner
if(ENABLE_GSRUNNER)
if (NOT WIN32 AND NOT APPLE)
message(WARNING "GSRunner is only supported on Windows and macOS and may not build on your system")
endif()
add_subdirectory(pcsx2-gsrunner)
else()
add_subdirectory(pcsx2-gsrunner EXCLUDE_FROM_ALL)

Binary file not shown.

View File

@@ -1,58 +0,0 @@
†---------------------†
Disassembly view:
†---------------------†
Ctrl + G Goto
Ctrl + E Edit Breakpoint
Ctrl + D Enable/disable breakpoint
Ctrl + B Add breakpoint
Left Go back one branch level/goto pc
Right Follow branch/position memory view to accessed address
Up Move cursor up one line
Down Move cursor down one line
Page Up Move visible area up one page
Page Down Move visible area down one page
F10 Step over
F11 Step into
Tab Toggle display symbols
Left Click Select line/toggle breakpoint if line is already highlighted
Right Click Open context menu
†---------------------†
Memory View:
†---------------------†
Ctrl + G Goto
Ctrl + B Add breakpoint
Left Move cursor back one byte/nibble
Right Move cursor ahead one byte/nibble
Up Move cursor up one line
Down Move cursor down one line
Page Up Move cursor up one page
Page Down Move cursor down one page
0-9,A-F Overwrite hex nibble
Any Overwrite ansi byte
Left Click Select byte/nibble
Right Click Open context menu
Ctrl+Wheel Zoom memory view
Esc Return to previous goto address
Ctrl+V Paste a hex string into memory
†---------------------†
Breakpoint List:
†---------------------†
Up Select previous item
Down Select next item
Delete Remove selected breakpoint
Return Edit selected breakpoint
Space Toggle enable state of selected breakpoint
†---------------------†

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
# Game Controller DB for SDL in 2.0.16 format
# Source: https://github.com/gabomdq/SDL_GameControllerDB
# Source: https://github.com/mdqinc/SDL_GameControllerDB
# Windows
03000000300f00000a01000000000000,3 In 1 Conversion Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b8,x:b3,y:b0,platform:Windows,
@@ -175,7 +175,6 @@
030000001a1c00000001000000000000,Datel Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
03000000451300000830000000000000,Defender Game Racer X7,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,
03000000791d00000103000000000000,Dual Box Wii,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
03000000c0160000e105000000000000,Dual Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,
030000004f040000070f000000000000,Dual Power,a:b8,b:b9,back:b4,dpdown:b1,dpleft:b2,dpright:b3,dpup:b0,leftshoulder:b13,leftstick:b6,lefttrigger:b14,leftx:a0,lefty:a1,rightshoulder:b12,rightstick:b7,righttrigger:b15,start:b5,x:b10,y:b11,platform:Windows,
030000004f04000012b3000000000000,Dual Power 3,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows,
030000004f04000020b3000000000000,Dual Trigger,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows,
@@ -253,7 +252,6 @@
03000000300f00000b01000000000000,GGE909 Recoil,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,
03000000f0250000c283000000000000,Gioteck PlayStation Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
03000000f025000021c1000000000000,Gioteck PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
03000000f025000021c1000010010000,Gioteck PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
03000000f025000031c1000000000000,Gioteck PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
03000000f0250000c383000000000000,Gioteck VX2 PlayStation Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
03000000f0250000c483000000000000,Gioteck VX2 PlayStation Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
@@ -840,6 +838,7 @@
03000000172700004431000000000000,Xiaomi Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a7,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,
03000000172700003350000000000000,Xiaomi XMGP01YM,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
03000000bc2000005060000000000000,Xiaomi XMGP01YM,+lefty:+a2,+righty:+a5,-lefty:-a1,-righty:-a4,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,start:b11,x:b3,y:b4,platform:Windows,
03000000c0160000e105000000000000,XinMo Dual Arcade,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,
xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
030000007d0400000340000000000000,Xterminator Digital Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:-a4,lefttrigger:+a4,leftx:a0,lefty:a1,paddle1:b7,paddle2:b6,rightshoulder:b5,rightstick:b9,righttrigger:b2,rightx:a3,righty:a5,start:b8,x:b3,y:b4,platform:Windows,
030000002c3600000100000000000000,Yawman Arrow,+rightx:h0.2,+righty:h0.4,-rightx:h0.8,-righty:h0.1,a:b4,b:b5,back:b6,dpdown:b15,dpleft:b14,dpright:b16,dpup:b13,leftshoulder:b10,leftstick:b0,lefttrigger:-a4,leftx:a0,lefty:a1,paddle1:b11,paddle2:b12,rightshoulder:b8,rightstick:b9,righttrigger:+a4,start:b3,x:b1,y:b2,platform:Windows,
@@ -1120,6 +1119,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000005f140000c501000000020000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,
03000000100800000100000000000000,Twin USB Joystick,a:b4,b:b2,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b12,leftstick:b20,lefttrigger:b8,leftx:a0,lefty:a2,rightshoulder:b14,rightstick:b22,righttrigger:b10,rightx:a6,righty:a4,start:b18,x:b6,y:b0,platform:Mac OS X,
03000000632500002605000000010000,Uberwith Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
03000000c0160000e105000000040000,Ultimate Atari Fight Stick,a:b2,b:b4,back:b18,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,rightshoulder:b8,righttrigger:b10,start:b16,x:b0,y:b6,platform:Mac OS X,
03000000151900005678000010010000,Uniplay U6,a:b3,b:b6,back:b25,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b17,leftstick:b31,lefttrigger:b21,leftx:a1,lefty:a3,rightshoulder:b19,rightstick:b33,righttrigger:b23,rightx:a4,righty:a5,start:b27,x:b11,y:b13,platform:Mac OS X,
030000006f0e00000302000025040000,Victrix PS4 Pro Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,
030000006f0e00000702000003060000,Victrix PS4 Pro Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,
@@ -1197,6 +1197,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
05000000c82d00000660000000010000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
03000000c82d00000020000000000000,8BitDo Pro 2 for Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
06000000c82d00000020000006010000,8BitDo Pro 2 for Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
03000000c82d00000960000011010000,8BitDo Pro 3,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b17,paddle2:b16,paddle3:b2,paddle4:b5,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
03000000c82d00000131000011010000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
03000000c82d00000231000011010000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
03000000c82d00000331000011010000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
@@ -1331,6 +1332,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000006f0e00000104000000010000,Gamestop Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000008f0e00000800000010010000,Gasia PlayStation Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
03000000451300000010000010010000,Genius Maxfire Grandias 12,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
03000000f025000021c1000010010000,Gioteck PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
03000000f0250000c283000010010000,Gioteck VX2 PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
190000004b4800000010000000010000,GO-Advance Controller,a:b1,b:b0,back:b10,dpdown:b7,dpleft:b8,dpright:b9,dpup:b6,leftshoulder:b4,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b13,start:b15,x:b2,y:b3,platform:Linux,
190000004b4800000010000001010000,GO-Advance Controller,a:b1,b:b0,back:b12,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,leftshoulder:b4,leftstick:b13,lefttrigger:b14,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b16,righttrigger:b15,start:b17,x:b2,y:b3,platform:Linux,
@@ -1773,6 +1775,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000005f140000c501000010010000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
06000000f51000000870000003010000,Turtle Beach Recon,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
03000000100800000100000010010000,Twin PS2 Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,
03000000c0160000e105000010010000,Ultimate Atari Fight Stick,a:b1,b:b2,back:b9,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b4,righttrigger:b5,start:b8,x:b0,y:b3,platform:Linux,
03000000151900005678000010010000,Uniplay U6,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000100800000300000010010000,USB Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,
03000000790000000600000007010000,USB gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Linux,

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 KiB

View File

@@ -80,6 +80,7 @@ PS_OUTPUT ps_downsample_copy(PS_INPUT input)
int DownsampleFactor = DOFFSET;
int2 ClampMin = int2(EMODA, EMODC);
float Weight = BGColor.x;
float step_multiplier = BGColor.y;
int2 coord = max(int2(input.p.xy) * DownsampleFactor, ClampMin);
@@ -88,7 +89,7 @@ PS_OUTPUT ps_downsample_copy(PS_INPUT input)
for (int yoff = 0; yoff < DownsampleFactor; yoff++)
{
for (int xoff = 0; xoff < DownsampleFactor; xoff++)
output.c += Texture.Load(int3(coord + int2(xoff, yoff), 0));
output.c += Texture.Load(int3(coord + int2(xoff * step_multiplier, yoff * step_multiplier), 0));
}
output.c /= Weight;
return output;

View File

@@ -175,8 +175,32 @@ float4 ps_main4(PS_INPUT input) : SV_Target0
// high motion -> interpolate pixels above and below
return (hn + ln) / 2.0f;
else
// low motion -> weave
return cn;
{
// Check if it's completely static first, we don't need to mess with any of that.
if((mh_max != -motion_thr.x) || (ml_max != -motion_thr.x) || (mc_max != -motion_thr.x))
{
// Check the diff with the above and below lines, if the difference is smaller between the new high and low lines
// compared to the new centre line and the high line (with some threshold of about 25 color steps), then reconstruct.
float3 mhln = hn.rgb - ln.rgb;
float3 mchn = hn.rgb - cn.rgb;
mhln = max(mhln, -mhln) - motion_thr;
mchn = max(mchn, -mchn) - motion_thr;
float mhln_max = max(max(mhln.x, mhln.y), mhln.z);
float mchn_max = max(max(mchn.x, mchn.y), mchn.z);
// The new centre line is a fair chunk different from those surrounding it, so quite likely incorrect.
if (mhln_max < 0.0f && mchn_max >= (mhln_max * 0.90f))
return (hn + ln) / 2.0f;
else
// low motion -> weave
return cn;
}
else
// low motion -> weave
return cn;
}
}
return float4(0.0f, 0.0f, 0.0f, 0.0f);

View File

@@ -49,6 +49,7 @@
#define PS_PROCESS_RG 0
#define PS_SHUFFLE_ACROSS 0
#define PS_READ16_SRC 0
#define PS_WRITE_RG 0
#define PS_DST_FMT 0
#define PS_DEPTH_FMT 0
#define PS_PAL_FMT 0
@@ -1196,7 +1197,7 @@ PS_OUTPUT ps_main(PS_INPUT input)
#if !PS_NO_COLOR1
output.c1 = alpha_blend;
#endif
#if PS_AFAIL == 3 && !PS_NO_COLOR1 // RGB_ONLY, no dual src blend
#if PS_AFAIL == 3 && PS_NO_COLOR1 // RGB_ONLY, no dual src blend
if (!atst_pass)
{
float RTa = NEEDS_RT_FOR_AFAIL ? RtTexture.Load(int3(input.p.xy, 0)).a : 0.0f;

View File

@@ -70,6 +70,7 @@ void ps_depth_copy()
uniform ivec2 ClampMin;
uniform int DownsampleFactor;
uniform float Weight;
uniform float StepMultiplier;
void ps_downsample_copy()
{
@@ -78,7 +79,7 @@ void ps_downsample_copy()
for (int yoff = 0; yoff < DownsampleFactor; yoff++)
{
for (int xoff = 0; xoff < DownsampleFactor; xoff++)
result += texelFetch(TextureSampler, coord + ivec2(xoff, yoff), 0);
result += texelFetch(TextureSampler, coord + ivec2(xoff * StepMultiplier, yoff * StepMultiplier), 0);
}
SV_Target0 = result / Weight;
}

View File

@@ -173,8 +173,32 @@ void ps_main4()
// high motion -> interpolate pixels above and below
SV_Target0 = (hn + ln) / 2.0f;
else
// low motion -> weave
SV_Target0 = cn;
{
// Check if it's completely static first, we don't need to mess with any of that.
if((mh_max != -motion_thr.x) || (ml_max != -motion_thr.x) || (mc_max != -motion_thr.x))
{
// Check the diff with the above and below lines, if the difference is smaller between the new high and low lines
// compared to the new centre line and the high line (with some threshold of about 25 color steps), then reconstruct.
vec3 mhln = hn.rgb - ln.rgb;
vec3 mchn = hn.rgb - cn.rgb;
mhln = max(mhln, -mhln) - motion_thr;
mchn = max(mchn, -mchn) - motion_thr;
float mhln_max = max(max(mhln.x, mhln.y), mhln.z);
float mchn_max = max(max(mchn.x, mchn.y), mchn.z);
// The new centre line is a fair chunk different from those surrounding it, so quite likely incorrect.
if (mhln_max < 0.0f && mchn_max >= (mhln_max * 0.90f))
SV_Target0 = (hn + ln) / 2.0f;
else
// low motion -> weave
SV_Target0 = cn;
}
else
// low motion -> weave
SV_Target0 = cn;
}
}
}

View File

@@ -1134,7 +1134,7 @@ void ps_main()
#else
SV_Target0.rgb = C.rgb / 255.0f;
#endif
#if PS_AFAIL == 3 && !PS_NO_COLOR1 // RGB_ONLY, no dual src blend
#if PS_AFAIL == 3 && PS_NO_COLOR1 // RGB_ONLY, no dual src blend
if (!atst_pass)
SV_Target0.a = sample_from_rt().a;
#endif

View File

@@ -66,7 +66,8 @@ layout(push_constant) uniform cb10
int DownsampleFactor;
int pad0;
float Weight;
vec3 pad1;
float step_multiplier;
vec2 pad1;
};
void ps_downsample_copy()
{
@@ -75,7 +76,9 @@ void ps_downsample_copy()
for (int yoff = 0; yoff < DownsampleFactor; yoff++)
{
for (int xoff = 0; xoff < DownsampleFactor; xoff++)
result += texelFetch(samp0, coord + ivec2(xoff, yoff), 0);
{
result += texelFetch(samp0, coord + ivec2(xoff * step_multiplier, yoff * step_multiplier), 0);
}
}
o_col0 = result / Weight;
}

View File

@@ -194,8 +194,32 @@ void ps_main4()
// high motion -> interpolate pixels above and below
o_col0 = (hn + ln) / 2.0f;
else
// low motion -> weave
o_col0 = cn;
{
// Check if it's completely static first, we don't need to mess with any of that.
if((mh_max != -motion_thr.x) || (ml_max != -motion_thr.x) || (mc_max != -motion_thr.x))
{
// Check the diff with the above and below lines, if the difference is smaller between the new high and low lines
// compared to the new centre line and the high line (with some threshold of about 25 color steps), then reconstruct.
vec3 mhln = hn.rgb - ln.rgb;
vec3 mchn = hn.rgb - cn.rgb;
mhln = max(mhln, -mhln) - motion_thr;
mchn = max(mchn, -mchn) - motion_thr;
float mhln_max = max(max(mhln.x, mhln.y), mhln.z);
float mchn_max = max(max(mchn.x, mchn.y), mchn.z);
// The new centre line is a fair chunk different from those surrounding it, so quite likely incorrect.
if (mhln_max < 0.0f && mchn_max >= (mhln_max * 0.90f))
o_col0 = (hn + ln) / 2.0f;
else
// low motion -> weave
o_col0 = cn;
}
else
// low motion -> weave
o_col0 = cn;
}
}
}
#endif

View File

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

View File

@@ -107,10 +107,19 @@ disable_compiler_warnings_for_target(cubeb)
disable_compiler_warnings_for_target(speex)
# Find the Qt components that we need.
find_package(Qt6 6.7.3 COMPONENTS CoreTools Core GuiTools Gui WidgetsTools Widgets LinguistTools REQUIRED)
if(ENABLE_QT_UI)
find_package(Qt6 6.10.0 COMPONENTS CoreTools Core GuiTools Gui WidgetsTools Widgets LinguistTools REQUIRED)
endif()
if (Qt6_VERSION VERSION_GREATER_EQUAL 6.10.0)
find_package(Qt6 COMPONENTS CorePrivate GuiPrivate WidgetsPrivate REQUIRED)
endif()
# The docking system for the debugger.
find_package(KDDockWidgets-qt6 2.3.0 REQUIRED)
if(WIN32)
add_subdirectory(3rdparty/rainterface EXCLUDE_FROM_ALL)
add_subdirectory(3rdparty/rainterface EXCLUDE_FROM_ALL)
endif()
# Demangler for the debugger.
@@ -119,15 +128,6 @@ add_subdirectory(3rdparty/demangler EXCLUDE_FROM_ALL)
# Symbol table parser.
add_subdirectory(3rdparty/ccc EXCLUDE_FROM_ALL)
# The docking system for the debugger.
find_package(KDDockWidgets-qt6 2.0.0 REQUIRED)
# Add an extra include path to work around a broken include directive.
# TODO: Remove this the next time we update KDDockWidgets.
get_target_property(KDDOCKWIDGETS_INCLUDE_DIRECTORY KDAB::kddockwidgets INTERFACE_INCLUDE_DIRECTORIES)
target_include_directories(KDAB::kddockwidgets INTERFACE
${KDDOCKWIDGETS_INCLUDE_DIRECTORY}/kddockwidgets
)
# Architecture-specific.
if(_M_X86)
add_subdirectory(3rdparty/zydis EXCLUDE_FROM_ALL)

View File

@@ -7,7 +7,6 @@ endif(NOT TOP_CMAKE_WAS_SOURCED)
add_library(common)
# x86emitter sources
target_sources(common PRIVATE
AlignedMalloc.cpp
Assertions.cpp
@@ -34,9 +33,9 @@ target_sources(common PRIVATE
Timer.cpp
WAVWriter.cpp
WindowInfo.cpp
YAML.cpp
)
# x86emitter headers
target_sources(common PRIVATE
AlignedMalloc.h
Assertions.h
@@ -81,6 +80,7 @@ target_sources(common PRIVATE
WAVWriter.h
WindowInfo.h
WrappedMemCopy.h
YAML.h
)
if(_M_X86)
@@ -208,6 +208,7 @@ target_link_libraries(common PRIVATE
target_link_libraries(common PUBLIC
fmt::fmt
fast_float
rapidyaml::rapidyaml
)
fixup_file_properties(common)

View File

@@ -15,10 +15,6 @@ namespace CocoaTools
void DestroyMetalLayer(WindowInfo* wi);
std::optional<float> GetViewRefreshRate(const WindowInfo& wi);
/// Add a handler to be run when macOS changes between dark and light themes
void AddThemeChangeHandler(void* ctx, void(handler)(void* ctx));
/// Remove a handler previously added using AddThemeChangeHandler with the given context
void RemoveThemeChangeHandler(void* ctx);
/// Mark an NSMenu as the help menu
void MarkHelpMenu(void* menu);
/// Returns the bundle path.
@@ -44,6 +40,6 @@ namespace CocoaTools
void RunCocoaEventLoop(bool wait_forever = false);
/// Posts an event to the main telling `RunCocoaEventLoop(true)` to exit
void StopMainThreadEventLoop();
}
} // namespace CocoaTools
#endif // __APPLE__

View File

@@ -85,63 +85,7 @@ std::optional<float> CocoaTools::GetViewRefreshRate(const WindowInfo& wi)
return ret;
}
// MARK: - Theme Change Handlers
@interface PCSX2KVOHelper : NSObject
- (void)addCallback:(void*)ctx run:(void(*)(void*))callback;
- (void)removeCallback:(void*)ctx;
@end
@implementation PCSX2KVOHelper
{
std::vector<std::pair<void*, void(*)(void*)>> _callbacks;
}
- (void)addCallback:(void*)ctx run:(void(*)(void*))callback
{
_callbacks.push_back(std::make_pair(ctx, callback));
}
- (void)removeCallback:(void*)ctx
{
auto new_end = std::remove_if(_callbacks.begin(), _callbacks.end(), [ctx](const auto& entry){
return ctx == entry.first;
});
_callbacks.erase(new_end, _callbacks.end());
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
for (const auto& callback : _callbacks)
callback.second(callback.first);
}
@end
static PCSX2KVOHelper* s_themeChangeHandler;
void CocoaTools::AddThemeChangeHandler(void* ctx, void(handler)(void* ctx))
{
assert([NSThread isMainThread]);
if (!s_themeChangeHandler)
{
s_themeChangeHandler = [[PCSX2KVOHelper alloc] init];
NSApplication* app = [NSApplication sharedApplication];
[app addObserver:s_themeChangeHandler
forKeyPath:@"effectiveAppearance"
options:0
context:nil];
}
[s_themeChangeHandler addCallback:ctx run:handler];
}
void CocoaTools::RemoveThemeChangeHandler(void* ctx)
{
assert([NSThread isMainThread]);
[s_themeChangeHandler removeCallback:ctx];
}
// MARK: - Help menu
void CocoaTools::MarkHelpMenu(void* menu)
{

View File

@@ -99,7 +99,7 @@ __ri void Log::WriteToConsole(LOGLEVEL level, ConsoleColors color, std::string_v
static constexpr size_t BUFFER_SIZE = 512;
SmallStackString<BUFFER_SIZE> buffer;
buffer.reserve(32 + message.length());
buffer.reserve(static_cast<u32>(32 + message.length()));
buffer.append(s_ansi_color_codes[color]);
if (s_log_timestamps)

View File

@@ -85,7 +85,7 @@ static inline bool FileSystemCharacterIsSane(char32_t c, bool strip_slashes)
if (c == '*')
return false;
// macos doesn't allow colons, apparently
// macos doesn't allow colons, apparently
#ifdef __APPLE__
if (c == U':')
return false;
@@ -2490,6 +2490,21 @@ bool FileSystem::DeleteDirectory(const char* path)
return (rmdir(path) == 0);
}
std::string FileSystem::GetPackagePath()
{
// NOTE: The reason this function is separated from FileSystem::GetProgramPath() is because
// This path check breaks other usages of FileSystem::GetProgramPath for the AppImage.
// Notably the CI-generated AppImage fails to start because PCSX2 can't find its resources
// since it tries to look for them relative to the .AppImage file instead of relative to the actual executable.
// Check if we are running inside appimage. If so, return the path to the appimage instead.
if (const char* appimage_path = getenv("APPIMAGE"))
return std::string(appimage_path);
// Otherwise, find the executable file directly
return GetProgramPath();
}
std::string FileSystem::GetProgramPath()
{
#if defined(__linux__)

View File

@@ -166,6 +166,9 @@ namespace FileSystem
/// Copies one file to another, optionally replacing it if it already exists.
bool CopyFilePath(const char* source, const char* destination, bool replace);
/// Returns the path to the current package (AppImage).
std::string GetPackagePath();
/// Returns the path to the current executable.
std::string GetProgramPath();

View File

@@ -14,9 +14,6 @@
#include <webp/decode.h>
#include <webp/encode.h>
// Compute the address of a base type given a field offset.
#define BASE_FROM_RECORD_FIELD(ptr, base_type, field) ((base_type*)(((char*)ptr) - offsetof(base_type, field)))
static bool PNGBufferLoader(RGBA8Image* image, const void* buffer, size_t buffer_size);
static bool PNGBufferSaver(const RGBA8Image& image, std::vector<u8>* buffer, u8 quality);
static bool PNGFileLoader(RGBA8Image* image, const char* filename, std::FILE* fp);
@@ -32,6 +29,11 @@ static bool WebPBufferSaver(const RGBA8Image& image, std::vector<u8>* buffer, u8
static bool WebPFileLoader(RGBA8Image* image, const char* filename, std::FILE* fp);
static bool WebPFileSaver(const RGBA8Image& image, const char* filename, std::FILE* fp, u8 quality);
static bool BMPBufferLoader(RGBA8Image* image, const void* buffer, size_t buffer_size);
static bool BMPBufferSaver(const RGBA8Image& image, std::vector<u8>* buffer, u8 quality);
static bool BMPFileLoader(RGBA8Image* image, const char* filename, std::FILE* fp);
static bool BMPFileSaver(const RGBA8Image& image, const char* filename, std::FILE* fp, u8 quality);
struct FormatHandler
{
const char* extension;
@@ -46,6 +48,7 @@ static constexpr FormatHandler s_format_handlers[] = {
{"jpg", JPEGBufferLoader, JPEGBufferSaver, JPEGFileLoader, JPEGFileSaver},
{"jpeg", JPEGBufferLoader, JPEGBufferSaver, JPEGFileLoader, JPEGFileSaver},
{"webp", WebPBufferLoader, WebPBufferSaver, WebPFileLoader, WebPFileSaver},
{"bmp", BMPBufferLoader, BMPBufferSaver, BMPFileLoader, BMPFileSaver},
};
static const FormatHandler* GetFormatHandler(const std::string_view extension)
@@ -485,6 +488,8 @@ bool JPEGFileLoader(RGBA8Image* image, const char* filename, std::FILE* fp)
struct FileCallback
{
// Must be the first member (&this == &mgr)
// We pass a pointer of mgr to libjpeg, and we need to be able to cast it back to FileCallback.
jpeg_source_mgr mgr;
std::FILE* fp;
@@ -496,7 +501,7 @@ bool JPEGFileLoader(RGBA8Image* image, const char* filename, std::FILE* fp)
.mgr = {
.init_source = [](j_decompress_ptr cinfo) {},
.fill_input_buffer = [](j_decompress_ptr cinfo) -> boolean {
FileCallback* cb = BASE_FROM_RECORD_FIELD(cinfo->src, FileCallback, mgr);
FileCallback* cb = reinterpret_cast<FileCallback*>(cinfo->src);
cb->mgr.next_input_byte = cb->buffer.get();
if (cb->end_of_file)
{
@@ -513,7 +518,7 @@ bool JPEGFileLoader(RGBA8Image* image, const char* filename, std::FILE* fp)
},
.skip_input_data =
[](j_decompress_ptr cinfo, long num_bytes) {
FileCallback* cb = BASE_FROM_RECORD_FIELD(cinfo->src, FileCallback, mgr);
FileCallback* cb = reinterpret_cast<FileCallback*>(cinfo->src);
const size_t skip_in_buffer = std::min<size_t>(cb->mgr.bytes_in_buffer, static_cast<size_t>(num_bytes));
cb->mgr.next_input_byte += skip_in_buffer;
cb->mgr.bytes_in_buffer -= skip_in_buffer;
@@ -650,12 +655,12 @@ bool JPEGFileSaver(const RGBA8Image& image, const char* filename, std::FILE* fp,
.mgr = {
.init_destination =
[](j_compress_ptr cinfo) {
FileCallback* cb = BASE_FROM_RECORD_FIELD(cinfo->dest, FileCallback, mgr);
FileCallback* cb = reinterpret_cast<FileCallback*>(cinfo->dest);
cb->mgr.next_output_byte = cb->buffer.get();
cb->mgr.free_in_buffer = BUFFER_SIZE;
},
.empty_output_buffer = [](j_compress_ptr cinfo) -> boolean {
FileCallback* cb = BASE_FROM_RECORD_FIELD(cinfo->dest, FileCallback, mgr);
FileCallback* cb = reinterpret_cast<FileCallback*>(cinfo->dest);
if (!cb->write_error)
cb->write_error |= (std::fwrite(cb->buffer.get(), 1, BUFFER_SIZE, cb->fp) != BUFFER_SIZE);
@@ -665,7 +670,7 @@ bool JPEGFileSaver(const RGBA8Image& image, const char* filename, std::FILE* fp,
},
.term_destination =
[](j_compress_ptr cinfo) {
FileCallback* cb = BASE_FROM_RECORD_FIELD(cinfo->dest, FileCallback, mgr);
FileCallback* cb = reinterpret_cast<FileCallback*>(cinfo->dest);
const size_t left = BUFFER_SIZE - cb->mgr.free_in_buffer;
if (left > 0 && !cb->write_error)
cb->write_error |= (std::fwrite(cb->buffer.get(), 1, left, cb->fp) != left);
@@ -734,3 +739,617 @@ bool WebPFileSaver(const RGBA8Image& image, const char* filename, std::FILE* fp,
return (std::fwrite(buffer.data(), buffer.size(), 1, fp) == 1);
}
// Some of this code is adapted from Qt's BMP handler (https://github.com/qt/qtbase/blob/dev/src/gui/image/qbmphandler.cpp)
#pragma pack(push, 1)
struct BMPFileHeader
{
u16 type;
u32 size;
u16 reserved1;
u16 reserved2;
u32 offset;
};
struct BMPInfoHeader
{
u32 size;
s32 width;
s32 height;
u16 planes;
u16 bit_count;
u32 compression;
u32 size_image;
s32 x_pels_per_meter;
s32 y_pels_per_meter;
u32 clr_used;
u32 clr_important;
};
#pragma pack(pop)
bool IsSupportedBMPFormat(u32 compression, u16 bit_count)
{
if (compression == 0)
return (bit_count == 1 || bit_count == 4 || bit_count == 8 || bit_count == 16 || bit_count == 24 || bit_count == 32);
if (compression == 1)
return (bit_count == 8);
if (compression == 2)
return (bit_count == 4);
if (compression == 3 || compression == 4) // BMP_BITFIELDS or BMP_ALPHABITFIELDS
return (bit_count == 16 || bit_count == 32);
return false;
}
bool LoadBMPPalette(std::vector<u32>& palette, const u8* data, u32 palette_offset, const BMPInfoHeader& info_header)
{
// 1 bit format doesn't use a palette in the traditional sense
if (info_header.bit_count == 1)
{
palette = {0xFFFFFFFF, 0xFF000000};
return true;
}
const u32 num_colors = (info_header.clr_used > 0) ? info_header.clr_used : (1u << info_header.bit_count);
// Make sure that we don't have an unreasonably large palette
if (num_colors > 256)
{
Console.Error("Invalid palette size: %u", num_colors);
return false;
}
palette.clear();
palette.reserve(num_colors);
const u8* palette_data = data + sizeof(BMPFileHeader) + info_header.size;
for (u32 i = 0; i < num_colors; i++)
{
const u8* color = palette_data + (i * 4);
const u8 b = color[0];
const u8 g = color[1];
const u8 r = color[2];
palette.push_back(r | (g << 8) | (b << 16) | 0xFF000000u);
}
return true;
}
bool LoadUncompressedBMP(u32* pixels, const u8* src, const u8* data, u32 width, u32 height, const BMPInfoHeader& info_header, const std::vector<u32>& palette, bool flip_vertical, u32 red_mask = 0, u32 green_mask = 0, u32 blue_mask = 0, u32 alpha_mask = 0, bool use_alpha = false)
{
const u32 row_size = ((width * info_header.bit_count + 31) / 32) * 4;
for (u32 y = 0; y < height; y++)
{
u32 dst_y = flip_vertical ? (height - 1 - y) : y;
const u8* row_src = src + (y * row_size);
u32* row_dst = pixels + (dst_y * width);
u32 bit_offset = 0;
for (u32 x = 0; x < width; x++)
{
u32 pixel_value = 0;
switch (info_header.bit_count)
{
case 1:
{
const u32 byte_index = bit_offset / 8;
const u32 bit_index = 7 - (bit_offset % 8);
pixel_value = (row_src[byte_index] >> bit_index) & 1;
bit_offset += 1;
break;
}
case 4:
{
const u32 byte_index = bit_offset / 8;
const u32 nibble_index = (bit_offset % 8) / 4;
pixel_value = (row_src[byte_index] >> (nibble_index * 4)) & 0xF;
bit_offset += 4;
break;
}
case 8:
{
pixel_value = row_src[bit_offset / 8];
bit_offset += 8;
break;
}
case 16:
{
const u32 byte_index = bit_offset / 8;
pixel_value = row_src[byte_index] | (row_src[byte_index + 1] << 8);
bit_offset += 16;
if (info_header.compression == 3)
{
const u8* bitfields = data + sizeof(BMPFileHeader) + info_header.size;
const u32 r_mask = *reinterpret_cast<const u32*>(bitfields);
const u32 g_mask = *reinterpret_cast<const u32*>(bitfields + 4);
const u32 b_mask = *reinterpret_cast<const u32*>(bitfields + 8);
u32 r_shift = 0, g_shift = 0, b_shift = 0;
u32 temp = r_mask;
while (temp >>= 1)
r_shift++;
temp = g_mask;
while (temp >>= 1)
g_shift++;
temp = b_mask;
while (temp >>= 1)
b_shift++;
const u8 r = static_cast<u8>((pixel_value & r_mask) >> r_shift);
const u8 g = static_cast<u8>((pixel_value & g_mask) >> g_shift);
const u8 b = static_cast<u8>((pixel_value & b_mask) >> b_shift);
const u8 r_max = static_cast<u8>(r_mask >> r_shift);
const u8 g_max = static_cast<u8>(g_mask >> g_shift);
const u8 b_max = static_cast<u8>(b_mask >> b_shift);
const u8 r_scaled = (r_max > 0) ? static_cast<u8>((r * 255) / r_max) : 0;
const u8 g_scaled = (g_max > 0) ? static_cast<u8>((g * 255) / g_max) : 0;
const u8 b_scaled = (b_max > 0) ? static_cast<u8>((b * 255) / b_max) : 0;
row_dst[x] = r_scaled | (g_scaled << 8) | (b_scaled << 16) | 0xFF000000u;
}
else
{
const u8 r = (pixel_value >> 10) & 0x1F;
const u8 g = (pixel_value >> 5) & 0x1F;
const u8 b = pixel_value & 0x1F;
row_dst[x] = (r << 3) | (g << 11) | (b << 19) | 0xFF000000u;
}
continue;
}
case 24:
{
const u32 byte_index = bit_offset / 8;
const u8 b = row_src[byte_index + 0];
const u8 g = row_src[byte_index + 1];
const u8 r = row_src[byte_index + 2];
row_dst[x] = r | (g << 8) | (b << 16) | 0xFF000000u;
bit_offset += 24;
continue;
}
case 32:
{
const u32 byte_index = bit_offset / 8;
u32 pixel_value = row_src[byte_index] | (row_src[byte_index + 1] << 8) | (row_src[byte_index + 2] << 16) | (row_src[byte_index + 3] << 24);
bit_offset += 32;
if (info_header.compression == 3 || info_header.compression == 4) // BITFIELDS or ALPHABITFIELDS
{
// Calculate shifts
auto calc_shift = [](u32 mask) -> u32 {
u32 result = 0;
while ((mask >= 0x100) || (!(mask & 1) && mask))
{
result++;
mask >>= 1;
}
return result;
};
// Calculate scales
auto calc_scale = [](u32 low_mask) -> u32 {
u32 result = 8;
while (low_mask && result)
{
result--;
low_mask >>= 1;
}
return result;
};
// Apply scale
auto apply_scale = [](u32 value, u32 scale) -> u8 {
if (!(scale & 0x07)) // scale == 8 or 0
return static_cast<u8>(value);
u32 filled = 8 - scale;
u32 result = value << scale;
do
{
result |= result >> filled;
filled <<= 1;
} while (filled < 8);
return static_cast<u8>(result);
};
const u32 r_shift = calc_shift(red_mask);
const u32 g_shift = calc_shift(green_mask);
const u32 b_shift = calc_shift(blue_mask);
const u32 a_shift = (alpha_mask != 0) ? calc_shift(alpha_mask) : 0;
const u32 r_scale = calc_scale(red_mask >> r_shift);
const u32 g_scale = calc_scale(green_mask >> g_shift);
const u32 b_scale = calc_scale(blue_mask >> b_shift);
const u32 a_scale = (alpha_mask != 0) ? calc_scale(alpha_mask >> a_shift) : 0;
const u8 r = apply_scale((pixel_value & red_mask) >> r_shift, r_scale);
const u8 g = apply_scale((pixel_value & green_mask) >> g_shift, g_scale);
const u8 b = apply_scale((pixel_value & blue_mask) >> b_shift, b_scale);
const u8 a = (use_alpha && alpha_mask != 0) ? apply_scale((pixel_value & alpha_mask) >> a_shift, a_scale) : 0xFF;
row_dst[x] = r | (g << 8) | (b << 16) | (a << 24);
}
else
{
// Uncompressed 32-bit BGRA order
const u8 b = row_src[byte_index + 0];
const u8 g = row_src[byte_index + 1];
const u8 r = row_src[byte_index + 2];
const u8 a = row_src[byte_index + 3];
row_dst[x] = r | (g << 8) | (b << 16) | (a << 24);
}
continue;
}
}
if (info_header.bit_count <= 8)
{
if (pixel_value < palette.size())
row_dst[x] = palette[pixel_value];
else
{
Console.Error("Invalid palette index: %u (palette size: %zu)", pixel_value, palette.size());
return false;
}
}
}
}
return true;
}
bool LoadCompressedBMP(u32* pixels, const u8* src, u32 src_size, u32 width, u32 height, const BMPInfoHeader& info_header, const std::vector<u32>& palette, bool flip_vertical)
{
u32 src_pos = 0;
const u32 pixel_size = (info_header.bit_count == 8) ? 1 : 2;
for (u32 y = 0; y < height; y++)
{
u32 dst_y = flip_vertical ? (height - 1 - y) : y;
u32* row_dst = pixels + (dst_y * width);
u32 x = 0;
while (x < width)
{
// Check bounds before reading
if (src_pos + 2 > src_size)
return false;
const u8 count = src[src_pos++];
const u8 value = src[src_pos++];
if (count == 0)
{
if (value == 0)
{
break;
}
else if (value == 1)
{
return true;
}
else if (value == 2)
{
// Delta (jump) need 2 more bytes
if (src_pos + 2 > src_size)
return false;
const u8 dx = src[src_pos++];
const u8 dy = src[src_pos++];
x += dx;
y += dy;
if (y >= height || x >= width)
return false;
const u32 new_dst_y = flip_vertical ? (height - 1 - y) : y;
row_dst = pixels + (new_dst_y * width);
}
else
{
// Absolute mode need "value" bytes of pixel data
const u32 run_length = value;
const u32 bytes_needed = run_length * pixel_size;
if (src_pos + bytes_needed > src_size)
return false;
for (u32 i = 0; i < run_length; i++)
{
if (x >= width)
break;
u8 pixel_value = 0;
if (info_header.bit_count == 8)
{
pixel_value = src[src_pos++];
}
else
{
const u8 byte_val = src[src_pos++];
pixel_value = (i % 2 == 0) ? (byte_val >> 4) : (byte_val & 0x0F);
}
row_dst[x++] = (pixel_value < palette.size()) ? palette[pixel_value] : 0;
}
if ((run_length * pixel_size) % 2 == 1)
src_pos++;
}
}
else
{
u8 pixel_value = value;
for (u32 i = 0; i < count; i++)
{
if (x >= width)
break;
row_dst[x++] = (pixel_value < palette.size()) ? palette[pixel_value] : 0;
}
}
}
}
return true;
}
bool BMPBufferLoader(RGBA8Image* image, const void* buffer, size_t buffer_size)
{
if (buffer_size < sizeof(BMPFileHeader) + sizeof(BMPInfoHeader))
{
Console.Error("BMP file too small");
return false;
}
const u8* data = static_cast<const u8*>(buffer);
BMPFileHeader file_header;
BMPInfoHeader info_header;
std::memcpy(&file_header, data, sizeof(BMPFileHeader));
std::memcpy(&info_header, data + sizeof(BMPFileHeader), sizeof(BMPInfoHeader));
if (file_header.type != 0x4D42)
{
Console.Error("Invalid BMP signature");
return false;
}
// Check for extended header versions (V4=108 bytes, V5=124 bytes)
// We read as BITMAPINFOHEADER (40 bytes) regardless, since extended headers just add fields at the end
if (info_header.size == 108)
{
Console.Warning("BITMAPV4HEADER detected, reading as BITMAPINFOHEADER");
}
else if (info_header.size == 124)
{
Console.Warning("BITMAPV5HEADER detected, reading as BITMAPINFOHEADER");
}
else if (info_header.size != 40)
{
Console.Warning("Unknown BMP header size: %u, attempting to read as BITMAPINFOHEADER", info_header.size);
}
if (!IsSupportedBMPFormat(info_header.compression, info_header.bit_count))
{
Console.Error("Unsupported BMP format: compression=%u, bit_count=%u", info_header.compression, info_header.bit_count);
return false;
}
const u32 width = static_cast<u32>(std::abs(info_header.width));
const u32 height = static_cast<u32>(std::abs(info_header.height));
const bool flip_vertical = (info_header.height > 0);
if (width == 0 || height == 0)
{
Console.Error("Invalid BMP dimensions: %ux%u", width, height);
return false;
}
if (width > 65536 || height > 65536)
{
Console.Error("BMP dimensions too large: %ux%u", width, height);
return false;
}
Console.WriteLn("BMP: %ux%u, %u-bit, compression=%u", width, height, info_header.bit_count, info_header.compression);
// Read color masks from header or bitfields
u32 red_mask = 0;
u32 green_mask = 0;
u32 blue_mask = 0;
u32 alpha_mask = 0;
const bool bitfields = (info_header.compression == 3 || info_header.compression == 4); // BMP_BITFIELDS or BMP_ALPHABITFIELDS
const u8* header_start = data + sizeof(BMPFileHeader);
const u32 header_base_offset = sizeof(BMPFileHeader) + 40; // Base header is 40 bytes
if (info_header.size >= 108) // BMP_WIN4 (108) or BMP_WIN5 (124)
{
// V4/V5 headers masks come right after the 40-byte base header
// Masks are at offsets from header_start: red=40, green=44, blue=48, alpha=52
if (buffer_size >= header_base_offset + 16) // Need space for 4 masks
{
red_mask = *reinterpret_cast<const u32*>(header_start + 40);
green_mask = *reinterpret_cast<const u32*>(header_start + 44);
blue_mask = *reinterpret_cast<const u32*>(header_start + 48);
alpha_mask = *reinterpret_cast<const u32*>(header_start + 52);
}
}
else if (bitfields && (info_header.bit_count == 16 || info_header.bit_count == 32))
{
const u32 bitfields_offset = sizeof(BMPFileHeader) + info_header.size;
if (buffer_size >= bitfields_offset + 12) // Need space for at least r/g/b masks
{
red_mask = *reinterpret_cast<const u32*>(data + bitfields_offset);
green_mask = *reinterpret_cast<const u32*>(data + bitfields_offset + 4);
blue_mask = *reinterpret_cast<const u32*>(data + bitfields_offset + 8);
if (info_header.compression == 4) // BMP_ALPHABITFIELDS
{
// Read alpha mask: r, g, b, a
if (buffer_size >= bitfields_offset + 16)
alpha_mask = *reinterpret_cast<const u32*>(data + bitfields_offset + 12);
}
// For BMP_BITFIELDS (3), alpha_mask stays 0
}
}
bool use_alpha = bitfields || (info_header.compression == 0 && info_header.bit_count == 32 && alpha_mask == 0xff000000);
use_alpha = use_alpha && (alpha_mask != 0);
const u32 bytes_per_pixel = info_header.bit_count / 8;
const u32 row_size = ((width * bytes_per_pixel + 3) / 4) * 4;
// For uncompressed BMPs, verify we have enough data
// For RLE-compressed BMPs, size is variable so we check differently
if (info_header.compression == 0)
{
if (file_header.offset + (row_size * height) > buffer_size)
{
Console.Error("BMP file data incomplete");
return false;
}
}
else
{
// For RLE-compressed BMPs, check that we have at least the offset and some data
// Use biSizeImage if available, otherwise just verify offset is valid
if (file_header.offset >= buffer_size)
{
Console.Error("BMP file data incomplete");
return false;
}
if (info_header.size_image > 0)
{
if (file_header.offset + info_header.size_image > buffer_size)
{
Console.Error("BMP file data incomplete");
return false;
}
}
}
std::vector<u32> pixels;
pixels.resize(width * height);
const u8* src = data + file_header.offset;
const u32 src_size = buffer_size - file_header.offset;
std::vector<u32> palette;
if (info_header.bit_count <= 8)
{
if (!LoadBMPPalette(palette, data, file_header.offset, info_header))
{
Console.Error("Failed to load BMP palette");
return false;
}
}
if (info_header.compression == 0 || info_header.compression == 3 || info_header.compression == 4)
{
if (!LoadUncompressedBMP(pixels.data(), src, data, width, height, info_header, palette, flip_vertical, red_mask, green_mask, blue_mask, alpha_mask, use_alpha))
{
Console.Error("Failed to load uncompressed BMP data");
return false;
}
}
else
{
if (!LoadCompressedBMP(pixels.data(), src, src_size, width, height, info_header, palette, flip_vertical))
{
Console.Error("Failed to load compressed BMP data");
return false;
}
}
// Handle alpha channel for 32-bit BMPs
// Only use alpha if alpha_mask is explicitly set in header/bitfields
if (info_header.bit_count == 32 && !use_alpha)
{
// Alpha mask not set or zero - set all pixels to fully opaque
for (u32& pixel : pixels)
pixel |= 0xFF000000u;
}
image->SetPixels(width, height, std::move(pixels));
return true;
}
bool BMPFileLoader(RGBA8Image* image, const char* filename, std::FILE* fp)
{
std::optional<std::vector<u8>> data = FileSystem::ReadBinaryFile(fp);
if (!data.has_value())
return false;
return BMPBufferLoader(image, data->data(), data->size());
}
bool BMPBufferSaver(const RGBA8Image& image, std::vector<u8>* buffer, u8 quality)
{
const u32 width = image.GetWidth();
const u32 height = image.GetHeight();
// Check dimensions
if (width == 0 || height == 0)
{
Console.Error("Invalid BMP dimensions: %ux%u", width, height);
return false;
}
const u32 row_size = ((width * 3 + 3) / 4) * 4;
const u32 image_size = row_size * height;
const u32 file_size = sizeof(BMPFileHeader) + sizeof(BMPInfoHeader) + image_size;
buffer->resize(file_size);
u8* data = buffer->data();
BMPFileHeader file_header = {};
file_header.type = 0x4D42;
file_header.size = file_size;
file_header.reserved1 = 0;
file_header.reserved2 = 0;
file_header.offset = sizeof(BMPFileHeader) + sizeof(BMPInfoHeader);
std::memcpy(data, &file_header, sizeof(BMPFileHeader));
BMPInfoHeader info_header = {};
info_header.size = sizeof(BMPInfoHeader);
info_header.width = static_cast<s32>(width);
info_header.height = static_cast<s32>(height);
info_header.planes = 1;
info_header.bit_count = 24;
info_header.compression = 0;
info_header.size_image = image_size;
info_header.x_pels_per_meter = 0;
info_header.y_pels_per_meter = 0;
info_header.clr_used = 0;
info_header.clr_important = 0;
std::memcpy(data + sizeof(BMPFileHeader), &info_header, sizeof(BMPInfoHeader));
u8* pixel_data = data + file_header.offset;
for (u32 y = 0; y < height; y++)
{
const u32 src_y = height - 1 - y;
const u32* row_src = image.GetRowPixels(src_y);
u8* row_dst = pixel_data + (y * row_size);
for (u32 x = 0; x < width; x++)
{
const u32 rgba = row_src[x];
row_dst[x * 3 + 0] = static_cast<u8>((rgba >> 16) & 0xFF);
row_dst[x * 3 + 1] = static_cast<u8>((rgba >> 8) & 0xFF);
row_dst[x * 3 + 2] = static_cast<u8>(rgba & 0xFF);
}
}
return true;
}
bool BMPFileSaver(const RGBA8Image& image, const char* filename, std::FILE* fp, u8 quality)
{
std::vector<u8> buffer;
if (!BMPBufferSaver(image, &buffer, quality))
return false;
return (std::fwrite(buffer.data(), buffer.size(), 1, fp) == 1);
}

View File

@@ -106,6 +106,11 @@ namespace Common
Reset();
}
Timer::Timer(Value start_value)
{
m_tvStartValue = start_value;
}
void Timer::Reset()
{
m_tvStartValue = GetCurrentValue();

View File

@@ -12,6 +12,7 @@ namespace Common
using Value = std::uint64_t;
Timer();
Timer (Value start_value);
static Value GetCurrentValue();
static double ConvertValueToSeconds(Value value);

View File

@@ -100,54 +100,16 @@ void Common::SetMousePosition(int x, int y)
SetCursorPos(x, y);
}
/*
static HHOOK mouseHook = nullptr;
static std::function<void(int, int)> fnMouseMoveCb;
LRESULT CALLBACK Mousecb(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode >= 0 && wParam == WM_MOUSEMOVE)
{
MSLLHOOKSTRUCT* mouse = (MSLLHOOKSTRUCT*)lParam;
fnMouseMoveCb(mouse->pt.x, mouse->pt.y);
}
return CallNextHookEx(mouseHook, nCode, wParam, lParam);
}
*/
// This (and the above) works, but is not recommended on Windows and is only here for consistency.
// Defer to using raw input instead.
bool Common::AttachMousePositionCb(std::function<void(int, int)> cb)
{
/*
if (mouseHook)
Common::DetachMousePositionCb();
fnMouseMoveCb = cb;
mouseHook = SetWindowsHookEx(WH_MOUSE_LL, Mousecb, GetModuleHandle(NULL), 0);
if (!mouseHook)
{
Console.Warning("Failed to set mouse hook: %d", GetLastError());
return false;
}
#if defined(PCSX2_DEBUG) || defined(PCSX2_DEVBUILD)
static bool warned = false;
if (!warned)
{
Console.Warning("Mouse hooks are enabled, and this isn't a release build! Using a debugger, or loading symbols, _will_ stall the hook and cause global mouse lag.");
warned = true;
}
#endif
*/
// We use raw input messages which are handled by the windows message loop.
// The alternative is to use a low-level mouse hook, but this passes Windows all mouse messages to PCSX2.
// If PCSX2 hangs, or you attach a debugger, the mouse will stop working system-wide.
return true;
}
void Common::DetachMousePositionCb()
{
/*
UnhookWindowsHookEx(mouseHook);
mouseHook = nullptr;
*/
}
bool Common::PlaySoundAsync(const char* path)

43
common/YAML.cpp Normal file
View File

@@ -0,0 +1,43 @@
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#include "YAML.h"
#include <csetjmp>
#include <cstdlib>
struct RapidYAMLContext
{
std::jmp_buf env;
Error* error = nullptr;
};
std::optional<ryml::Tree> ParseYAMLFromString(ryml::csubstr yaml, ryml::csubstr file_name, Error* error)
{
RapidYAMLContext context;
context.error = error;
ryml::Callbacks callbacks;
callbacks.m_user_data = static_cast<void*>(&context);
callbacks.m_error = [](const char* msg, size_t msg_len, ryml::Location location, void* user_data) {
RapidYAMLContext* context = static_cast<RapidYAMLContext*>(user_data);
Error::SetString(context->error, std::string(msg, msg_len));
std::longjmp(context->env, 1);
};
ryml::EventHandlerTree event_handler(callbacks);
ryml::Parser parser(&event_handler);
ryml::Tree tree;
// The only options RapidYAML provides for recovering from errors are
// throwing an exception or using setjmp/longjmp. Since we have exceptions
// disabled we have to use the latter option.
if (setjmp(context.env))
return std::nullopt;
ryml::parse_in_arena(&parser, file_name, yaml, &tree);
return tree;
}

18
common/YAML.h Normal file
View File

@@ -0,0 +1,18 @@
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#pragma once
#include "Error.h"
#include "ryml_std.hpp"
#include "ryml.hpp"
#include "ryml.hpp"
#include <optional>
/// Parse a YAML file with RapidYAML, and use setjmp/longjmp to recover from
/// parsing errors (as is recommended by the documentation for cases where
/// exceptions are disabled). The file_name parameter is only used for error
/// messages, which are returned via the error parameter.
std::optional<ryml::Tree> ParseYAMLFromString(ryml::csubstr yaml, ryml::csubstr file_name, Error* error);

View File

@@ -36,10 +36,12 @@
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\fast_float\include</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\fmt\include</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\jpgd</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\rapidyaml\include</AdditionalIncludeDirectories>
<PrecompiledHeader>Use</PrecompiledHeader>
<ForcedIncludeFiles>PrecompiledHeader.h</ForcedIncludeFiles>
<PrecompiledHeaderFile>PrecompiledHeader.h</PrecompiledHeaderFile>
<ObjectFileName>$(IntDir)%(RelativeDir)</ObjectFileName>
<PreprocessorDefinitions>C4_NO_DEBUG_BREAK;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
@@ -71,6 +73,7 @@
<ClCompile Include="Timer.cpp" />
<ClCompile Include="WAVWriter.cpp" />
<ClCompile Include="WindowInfo.cpp" />
<ClCompile Include="YAML.cpp" />
<ClCompile Include="Perf.cpp" />
<ClCompile Include="PrecompiledHeader.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
@@ -157,6 +160,7 @@
<ClInclude Include="Timer.h" />
<ClInclude Include="WAVWriter.h" />
<ClInclude Include="WindowInfo.h" />
<ClInclude Include="YAML.h" />
<ClInclude Include="Threading.h" />
<ClInclude Include="emitter\implement\avx.h" />
<ClInclude Include="emitter\implement\bmi.h" />

View File

@@ -127,6 +127,9 @@
<ClCompile Include="SmallString.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="YAML.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="AlignedMalloc.h">
@@ -335,6 +338,9 @@
</ClInclude>
<ClInclude Include="SingleRegisterTypes.h" />
<ClInclude Include="FPControl.h" />
<ClInclude Include="YAML.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="Source Files">
@@ -349,4 +355,4 @@
<Filter>Source Files</Filter>
</MASM>
</ItemGroup>
</Project>
</Project>

View File

@@ -47,10 +47,19 @@
#include "svnrev.h"
// Down here because X11 has a lot of defines that can conflict
#if defined(__linux__)
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <sys/select.h>
#include <unistd.h>
#endif
namespace GSRunner
{
static void InitializeConsole();
static bool InitializeConfig();
static void SettingsOverride();
static bool ParseCommandLineArgs(int argc, char* argv[], VMBootParameters& params);
static void DumpStats();
@@ -111,42 +120,6 @@ bool GSRunner::InitializeConfig()
VMManager::SetDefaultSettings(si, true, true, true, true, true);
// complete as quickly as possible
si.SetBoolValue("EmuCore/GS", "FrameLimitEnable", false);
si.SetIntValue("EmuCore/GS", "VsyncEnable", false);
// Force screenshot quality settings to something more performant, overriding any defaults good for users.
si.SetIntValue("EmuCore/GS", "ScreenshotFormat", static_cast<int>(GSScreenshotFormat::PNG));
si.SetIntValue("EmuCore/GS", "ScreenshotQuality", 10);
// ensure all input sources are disabled, we're not using them
si.SetBoolValue("InputSources", "SDL", false);
si.SetBoolValue("InputSources", "XInput", false);
// we don't need any sound output
si.SetStringValue("SPU2/Output", "OutputModule", "nullout");
// none of the bindings are going to resolve to anything
Pad::ClearPortBindings(si, 0);
si.ClearSection("Hotkeys");
// force logging
si.SetBoolValue("Logging", "EnableSystemConsole", !s_no_console);
si.SetBoolValue("Logging", "EnableTimestamps", true);
si.SetBoolValue("Logging", "EnableVerbose", true);
// and show some stats :)
si.SetBoolValue("EmuCore/GS", "OsdShowFPS", true);
si.SetBoolValue("EmuCore/GS", "OsdShowResolution", true);
si.SetBoolValue("EmuCore/GS", "OsdShowGSStats", true);
// remove memory cards, so we don't have sharing violations
for (u32 i = 0; i < 2; i++)
{
si.SetBoolValue("MemoryCards", fmt::format("Slot{}_Enable", i + 1).c_str(), false);
si.SetStringValue("MemoryCards", fmt::format("Slot{}_Filename", i + 1).c_str(), "");
}
VMManager::Internal::LoadStartupSettings();
return true;
}
@@ -473,8 +446,8 @@ static void PrintCommandLineHelp(const char* progname)
std::fprintf(stderr, " -help: Displays this information and exits.\n");
std::fprintf(stderr, " -version: Displays version information and exits.\n");
std::fprintf(stderr, " -dumpdir <dir>: Frame dump directory (will be dumped as filename_frameN.png).\n");
std::fprintf(stderr, " -dump [rt|tex|z|f|a|i|tr]: Enabling dumping of render target, texture, z buffer, frame, "
"alphas, and info (context, vertices, transfers (list)), transfers (images), respectively, per draw. Generates lots of data.\n");
std::fprintf(stderr, " -dump [rt|tex|z|f|a|i|tr|ds|fs]: Enabling dumping of render target, texture, z buffer, frame, "
"alphas, and info (context, vertices, list of transfers), transfers images, draw stats, frame stats, respectively, per draw. Generates lots of data.\n");
std::fprintf(stderr, " -dumprange N[,L,B]: Start dumping from draw N (base 0), stops after L draws, and only "
"those draws that are multiples of B (intersection of -dumprange and -dumprangef used)."
"Defaults to 0,-1,1 (all draws). Only used if -dump used.\n");
@@ -560,6 +533,10 @@ bool GSRunner::ParseCommandLineArgs(int argc, char* argv[], VMBootParameters& pa
s_settings_interface.SetBoolValue("EmuCore/GS", "SaveInfo", true);
if (str.find("tr") != std::string::npos)
s_settings_interface.SetBoolValue("EmuCore/GS", "SaveTransferImages", true);
if (str.find("ds") != std::string::npos)
s_settings_interface.SetBoolValue("EmuCore/GS", "SaveDrawStats", true);
if (str.find("fs") != std::string::npos)
s_settings_interface.SetBoolValue("EmuCore/GS", "SaveFrameStats", true);
continue;
}
else if (CHECK_ARG_PARAM("-dumprange"))
@@ -701,6 +678,28 @@ bool GSRunner::ParseCommandLineArgs(int argc, char* argv[], VMBootParameters& pa
continue;
}
else if (CHECK_ARG_PARAM("-ini"))
{
std::string path = std::string(StringUtil::StripWhitespace(argv[++i]));
if (!FileSystem::FileExists(path.c_str()))
{
Console.ErrorFmt("INI file {} does not exit.", path);
return false;
}
INISettingsInterface si_ini(path);
if (!si_ini.Load())
{
Console.ErrorFmt("Unable to load INI settings from {}.", path);
return false;
}
for (const auto& [key, value] : si_ini.GetKeyValueList("EmuCore/GS"))
s_settings_interface.SetStringValue("EmuCore/GS", key.c_str(), value.c_str());
continue;
}
else if (CHECK_ARG_PARAM("-upscale"))
{
const float upscale = StringUtil::FromChars<float>(argv[++i]).value_or(0.0f);
@@ -731,7 +730,7 @@ bool GSRunner::ParseCommandLineArgs(int argc, char* argv[], VMBootParameters& pa
else if (CHECK_ARG("-noshadercache"))
{
Console.WriteLn("Disabling shader cache");
s_settings_interface.SetBoolValue("EmuCore/GS", "disable_shader_cache", true);
s_settings_interface.SetBoolValue("EmuCore/GS", "DisableShaderCache", true);
continue;
}
else if (CHECK_ARG("-window"))
@@ -805,6 +804,45 @@ bool GSRunner::ParseCommandLineArgs(int argc, char* argv[], VMBootParameters& pa
return true;
}
void GSRunner::SettingsOverride()
{
// complete as quickly as possible
s_settings_interface.SetBoolValue("EmuCore/GS", "FrameLimitEnable", false);
s_settings_interface.SetIntValue("EmuCore/GS", "VsyncEnable", false);
// Force screenshot quality settings to something more performant, overriding any defaults good for users.
s_settings_interface.SetIntValue("EmuCore/GS", "ScreenshotFormat", static_cast<int>(GSScreenshotFormat::PNG));
s_settings_interface.SetIntValue("EmuCore/GS", "ScreenshotQuality", 10);
// ensure all input sources are disabled, we're not using them
s_settings_interface.SetBoolValue("InputSources", "SDL", false);
s_settings_interface.SetBoolValue("InputSources", "XInput", false);
// we don't need any sound output
s_settings_interface.SetStringValue("SPU2/Output", "OutputModule", "nullout");
// none of the bindings are going to resolve to anything
Pad::ClearPortBindings(s_settings_interface, 0);
s_settings_interface.ClearSection("Hotkeys");
// force logging
s_settings_interface.SetBoolValue("Logging", "EnableSystemConsole", !s_no_console);
s_settings_interface.SetBoolValue("Logging", "EnableTimestamps", true);
s_settings_interface.SetBoolValue("Logging", "EnableVerbose", true);
// and show some stats :)
s_settings_interface.SetBoolValue("EmuCore/GS", "OsdShowFPS", true);
s_settings_interface.SetBoolValue("EmuCore/GS", "OsdShowResolution", true);
s_settings_interface.SetBoolValue("EmuCore/GS", "OsdShowGSStats", true);
// remove memory cards, so we don't have sharing violations
for (u32 i = 0; i < 2; i++)
{
s_settings_interface.SetBoolValue("MemoryCards", fmt::format("Slot{}_Enable", i + 1).c_str(), false);
s_settings_interface.SetStringValue("MemoryCards", fmt::format("Slot{}_Filename", i + 1).c_str(), "");
}
}
void GSRunner::DumpStats()
{
std::atomic_thread_fence(std::memory_order_acquire);
@@ -823,16 +861,27 @@ void GSRunner::DumpStats()
#define main real_main
#endif
static void CPUThreadMain(VMBootParameters* params) {
if (VMManager::Initialize(*params))
static void CPUThreadMain(VMBootParameters* params, std::atomic<int>* ret)
{
ret->store(EXIT_FAILURE);
if (VMManager::Internal::CPUThreadInitialize())
{
// run until end
GSDumpReplayer::SetLoopCount(s_loop_count);
VMManager::SetState(VMState::Running);
while (VMManager::GetState() == VMState::Running)
VMManager::Execute();
VMManager::Shutdown(false);
GSRunner::DumpStats();
// apply new settings (e.g. pick up renderer change)
VMManager::ApplySettings();
GSDumpReplayer::SetIsDumpRunner(true);
if (VMManager::Initialize(*params))
{
// run until end
GSDumpReplayer::SetLoopCount(s_loop_count);
VMManager::SetState(VMState::Running);
while (VMManager::GetState() == VMState::Running)
VMManager::Execute();
VMManager::Shutdown(false);
GSRunner::DumpStats();
ret->store(EXIT_SUCCESS);
}
}
VMManager::Internal::CPUThreadShutdown();
@@ -854,27 +903,23 @@ int main(int argc, char* argv[])
if (!GSRunner::ParseCommandLineArgs(argc, argv, params))
return EXIT_FAILURE;
if (!VMManager::Internal::CPUThreadInitialize())
return EXIT_FAILURE;
if (s_use_window.value_or(true) && !GSRunner::CreatePlatformWindow())
{
Console.Error("Failed to create window.");
return EXIT_FAILURE;
}
// apply new settings (e.g. pick up renderer change)
VMManager::ApplySettings();
GSDumpReplayer::SetIsDumpRunner(true);
// Override settings that shouldn't be picked up from defaults or INIs.
GSRunner::SettingsOverride();
std::thread cputhread(CPUThreadMain, &params);
std::atomic<int> thread_ret;
std::thread cputhread(CPUThreadMain, &params, &thread_ret);
GSRunner::PumpPlatformMessages(/*forever=*/true);
cputhread.join();
VMManager::Internal::CPUThreadShutdown();
GSRunner::DestroyPlatformWindow();
return EXIT_SUCCESS;
return thread_ret.load();
}
void Host::PumpMessagesOnCPUThread()
@@ -1086,4 +1131,118 @@ void GSRunner::StopPlatformMessagePump()
CocoaTools::StopMainThreadEventLoop();
}
#elif defined(__linux__)
static Display* s_display = nullptr;
static Window s_window = None;
static WindowInfo s_wi;
static std::atomic<bool> s_shutdown_requested{false};
bool GSRunner::CreatePlatformWindow()
{
pxAssertRel(!s_display && s_window == None, "Tried to create window when there already was one!");
s_display = XOpenDisplay(nullptr);
if (!s_display)
{
Console.Error("Failed to open X11 display");
return false;
}
int screen = DefaultScreen(s_display);
Window root = RootWindow(s_display, screen);
s_window = XCreateSimpleWindow(s_display, root, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, 1,
BlackPixel(s_display, screen), WhitePixel(s_display, screen));
if (s_window == None)
{
Console.Error("Failed to create X11 window");
XCloseDisplay(s_display);
s_display = nullptr;
return false;
}
XStoreName(s_display, s_window, "PCSX2 GS Runner");
XSelectInput(s_display, s_window, StructureNotifyMask);
XMapWindow(s_display, s_window);
s_wi.type = WindowInfo::Type::X11;
s_wi.display_connection = s_display;
s_wi.window_handle = reinterpret_cast<void*>(s_window);
s_wi.surface_width = WINDOW_WIDTH;
s_wi.surface_height = WINDOW_HEIGHT;
s_wi.surface_scale = 1.0f;
XFlush(s_display);
PumpPlatformMessages();
return true;
}
void GSRunner::DestroyPlatformWindow()
{
if (s_display && s_window != None)
{
XDestroyWindow(s_display, s_window);
s_window = None;
}
if (s_display)
{
XCloseDisplay(s_display);
s_display = nullptr;
}
}
std::optional<WindowInfo> GSRunner::GetPlatformWindowInfo()
{
WindowInfo wi;
if (s_display && s_window != None)
wi = s_wi;
else
wi.type = WindowInfo::Type::Surfaceless;
return wi;
}
void GSRunner::PumpPlatformMessages(bool forever)
{
if (!s_display)
return;
do
{
while (XPending(s_display) > 0)
{
XEvent event;
XNextEvent(s_display, &event);
switch (event.type)
{
case ConfigureNotify:
{
const XConfigureEvent& configure = event.xconfigure;
s_wi.surface_width = static_cast<u32>(configure.width);
s_wi.surface_height = static_cast<u32>(configure.height);
break;
}
case DestroyNotify:
return;
default:
break;
}
}
if (s_shutdown_requested.load())
return;
if (forever)
{
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
} while (forever && !s_shutdown_requested.load());
}
void GSRunner::StopPlatformMessagePump()
{
s_shutdown_requested.store(true);
}
#endif // _WIN32 / __APPLE__

View File

@@ -73,7 +73,6 @@ def run_regression_test(runner, dumpdir, renderer, upscale, renderhacks, paralle
#print("Running '%s'" % (" ".join(args)))
subprocess.run(args, env=environ, stdin=subprocess.DEVNULL, stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL, creationflags=creationflags)
def run_regression_tests(runner, gsdir, dumpdir, renderer, upscale, renderhacks, parallel=1):
paths = glob.glob(gsdir + "/*.*", recursive=True)
gamepaths = list(filter(lambda x: get_gs_name(x) is not None, paths))
@@ -104,12 +103,12 @@ def run_regression_tests(runner, gsdir, dumpdir, renderer, upscale, renderhacks,
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Generate frame dump images for regression tests")
parser.add_argument("-runner", action="store", required=True, help="Path to PCSX2 GS runner")
parser.add_argument("-gsdir", action="store", required=True, help="Directory containing GS dumps")
parser.add_argument("-dumpdir", action="store", required=True, help="Base directory to dump frames to")
parser.add_argument("-renderer", action="store", required=False, help="Renderer to use")
parser.add_argument("-runner", action="store", required=True, type=str.strip, help="Path to PCSX2 GS runner")
parser.add_argument("-gsdir", action="store", required=True, type=str.strip, help="Directory containing GS dumps")
parser.add_argument("-dumpdir", action="store", required=True, type=str.strip, help="Base directory to dump frames to")
parser.add_argument("-renderer", action="store", required=False, type=str.strip, help="Renderer to use")
parser.add_argument("-upscale", action="store", type=float, default=1, help="Upscaling multiplier to use")
parser.add_argument("-renderhacks", action="store", required=False, help="Enable HW Rendering hacks")
parser.add_argument("-renderhacks", action="store", required=False, type=str.strip, help="Enable HW Rendering hacks")
parser.add_argument("-parallel", action="store", type=int, default=1, help="Number of processes to run")
args = parser.parse_args()

View File

@@ -12,6 +12,7 @@
#include "common/SmallString.h"
#include <QtCore/QFile>
#include <QtCore/QFileInfo>
#include <QtCore/QString>
#include <QtGui/QDesktopServices>
#include <QtWidgets/QDialog>
@@ -134,12 +135,16 @@ void AboutDialog::showHTMLDialog(QWidget* parent, const QString& title, const QS
tb->setOpenExternalLinks(true);
QFile file(path);
file.open(QIODevice::ReadOnly);
if (const QByteArray data = file.readAll(); !data.isEmpty())
tb->setText(QString::fromUtf8(data));
else
QFileInfo fi(path);
if (!fi.exists() || !fi.isReadable())
{
tb->setText(tr("File not found: %1").arg(path));
}
else
{
tb->setSource(QUrl::fromLocalFile(path));
}
layout->addWidget(tb, 1);
QDialogButtonBox* bb = new QDialogButtonBox(QDialogButtonBox::Close, &dialog);

View File

@@ -81,6 +81,9 @@
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::TextBrowserInteraction</set>
</property>
</widget>
</item>
<item>
@@ -115,6 +118,9 @@
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::TextBrowserInteraction</set>
</property>
</widget>
</item>
<item>

View File

@@ -3,7 +3,7 @@
<class>AutoUpdaterDialog</class>
<widget class="QDialog" name="AutoUpdaterDialog">
<property name="windowModality">
<enum>Qt::ApplicationModal</enum>
<enum>Qt::WindowModality::ApplicationModal</enum>
</property>
<property name="geometry">
<rect>
@@ -91,7 +91,7 @@
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
@@ -132,6 +132,12 @@
</item>
</layout>
</widget>
<tabstops>
<tabstop>updateNotes</tabstop>
<tabstop>downloadAndInstall</tabstop>
<tabstop>skipThisUpdate</tabstop>
<tabstop>remindMeLater</tabstop>
</tabstops>
<resources>
<include location="resources/resources.qrc"/>
</resources>

View File

@@ -264,6 +264,14 @@ target_sources(pcsx2-qt PRIVATE
resources/resources.qrc
)
if (NOT APPLE)
target_sources(pcsx2-qt PRIVATE
ShortcutCreationDialog.cpp
ShortcutCreationDialog.h
ShortcutCreationDialog.ui
)
endif()
file(GLOB TS_FILES ${CMAKE_CURRENT_SOURCE_DIR}/Translations/*.ts)
target_precompile_headers(pcsx2-qt PRIVATE PrecompiledHeader.h)
@@ -281,6 +289,7 @@ target_link_libraries(pcsx2-qt PRIVATE
PCSX2
Qt6::Core
Qt6::Gui
Qt6::GuiPrivate
Qt6::Widgets
KDAB::kddockwidgets
)

View File

@@ -28,7 +28,7 @@
<pixmap resource="resources/resources.qrc">:/icons/black/svg/artboard-2-line.svg</pixmap>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>
</property>
</widget>
</item>
@@ -40,6 +40,12 @@
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="buddy">
<cstring>label</cstring>
</property>
<property name="textInteractionFlags">
<set>Qt::TextBrowserInteraction</set>
</property>
</widget>
</item>
</layout>
@@ -52,6 +58,12 @@
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="buddy">
<cstring>urls</cstring>
</property>
<property name="textInteractionFlags">
<set>Qt::TextBrowserInteraction</set>
</property>
</widget>
</item>
<item>
@@ -65,6 +77,12 @@
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="buddy">
<cstring>useTitleFileNames</cstring>
</property>
<property name="textInteractionFlags">
<set>Qt::TextBrowserInteraction</set>
</property>
</widget>
</item>
<item>
@@ -110,6 +128,12 @@
</item>
</layout>
</widget>
<tabstops>
<tabstop>urls</tabstop>
<tabstop>useTitleFileNames</tabstop>
<tabstop>start</tabstop>
<tabstop>close</tabstop>
</tabstops>
<resources>
<include location="resources/resources.qrc"/>
</resources>

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